<commit_before>// Copyright Daniel Wallin, Arvid Norberg 2006. Use, modification and distribution is
// subject to the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#include <boost/python.hpp>
#include <libtorrent/session.hpp>
#include <libtorrent/torrent.hpp>
#include <libtorrent/storage.hpp>
#include <libtorrent/ip_filter.hpp>
#include "gil.hpp"

using namespace boost::python;
using namespace libtorrent;

namespace
{
    bool listen_on(session& s, int min_, int max_, char const* interface)
    {
        allow_threading_guard guard;
        return s.listen_on(std::make_pair(min_, max_), interface);
    }

    void outgoing_ports(session& s, int _min, int _max)
    {
        allow_threading_guard guard;
        session_settings settings = s.settings();
        settings.outgoing_ports = std::make_pair(_min, _max);
        s.set_settings(settings);
        return;
    }
#ifndef TORRENT_DISABLE_DHT
    void add_dht_router(session& s, std::string router_, int port_)
    {
        allow_threading_guard guard;
        return s.add_dht_router(std::make_pair(router_, port_));
    }
#endif

    struct invoke_extension_factory
    {
        invoke_extension_factory(object const& callback)
            : cb(callback)
        {}

        boost::shared_ptr<torrent_plugin> operator()(torrent* t, void*)
        {
           lock_gil lock;
           return extract<boost::shared_ptr<torrent_plugin> >(cb(ptr(t)))();
        }

        object cb;
    };

    void add_extension(session& s, object const& e)
    {
        allow_threading_guard guard;
        s.add_extension(invoke_extension_factory(e));
    }

#ifndef TORRENT_NO_DEPRECATE
    torrent_handle add_torrent_depr(session& s, torrent_info const& ti
        , boost::filesystem::path const& save, entry const& resume
        , storage_mode_t storage_mode, bool paused)
    {
        allow_threading_guard guard;
        return s.add_torrent(ti, save, resume, storage_mode, paused, default_storage_constructor);
    }
#endif

    torrent_handle add_torrent(session& s, dict params)
    {
        add_torrent_params p;

        if (params.has_key("ti"))
            p.ti = new torrent_info(extract<torrent_info const&>(params["ti"]));

        std::string url;
        if (params.has_key("tracker_url"))
        {
            url = extract<std::string>(params["tracker_url"]);
            p.tracker_url = url.c_str();
        }
        if (params.has_key("info_hash"))
            p.info_hash = extract<sha1_hash>(params["info_hash"]);
        std::string name;
        if (params.has_key("name"))
        {
            name = extract<std::string>(params["name"]);
            p.name = name.c_str();
        }
        p.save_path = fs::path(extract<std::string>(params["save_path"]));

        std::vector<char> resume_buf;
        if (params.has_key("resume_data"))
        {
            std::string resume = extract<std::string>(params["resume_data"]);
            resume_buf.resize(resume.size());
            std::memcpy(&resume_buf[0], &resume[0], resume.size());
            p.resume_data = &resume_buf;
        }
        if (params.has_key("storage_mode"))
            p.storage_mode = extract<storage_mode_t>(params["storage_mode"]);
        if (params.has_key("paused"))
            p.paused = params["paused"];
        if (params.has_key("auto_managed"))
            p.auto_managed = params["auto_managed"];
        if (params.has_key("duplicate_is_error"))
            p.duplicate_is_error = params["duplicate_is_error"];
        if (params.has_key("seed_mode"))
            p.seed_mode = params["seed_mode"];
        if (params.has_key("override_resume_data"))
            p.override_resume_data = params["override_resume_data"];

        return s.add_torrent(p);
    }

    void start_natpmp(session& s)
    {
        allow_threading_guard guard;
        s.start_natpmp();
        return;
    }

    void start_upnp(session& s)
    {
        allow_threading_guard guard;
        s.start_upnp();
        return;
    }

    list get_torrents(session& s)
    {
        list ret;
        std::vector<torrent_handle> torrents = s.get_torrents();

        for (std::vector<torrent_handle>::iterator i = torrents.begin(); i != torrents.end(); ++i)
        {
            ret.append(*i);
        }
        return ret;
    }

#ifndef TORRENT_DISABLE_GEO_IP
    bool load_asnum_db(session& s, std::string file)
    {
        allow_threading_guard guard;
        return s.load_asnum_db(file.c_str());
    }

    bool load_country_db(session& s, std::string file)
    {
        allow_threading_guard guard;
        return s.load_country_db(file.c_str());
    }
#endif
} // namespace unnamed

void bind_session()
{
    class_<session_status>("session_status")
        .def_readonly("has_incoming_connections", &session_status::has_incoming_connections)

        .def_readonly("upload_rate", &session_status::upload_rate)
        .def_readonly("download_rate", &session_status::download_rate)
        .def_readonly("total_download", &session_status::total_download)
        .def_readonly("total_upload", &session_status::total_upload)

        .def_readonly("payload_upload_rate", &session_status::payload_upload_rate)
        .def_readonly("payload_download_rate", &session_status::payload_download_rate)
        .def_readonly("total_payload_download", &session_status::total_payload_download)
        .def_readonly("total_payload_upload", &session_status::total_payload_upload)

        .def_readonly("ip_overhead_upload_rate", &session_status::ip_overhead_upload_rate)
        .def_readonly("ip_overhead_download_rate", &session_status::ip_overhead_download_rate)
        .def_readonly("total_ip_overhead_download", &session_status::total_ip_overhead_download)
        .def_readonly("total_ip_overhead_upload", &session_status::total_ip_overhead_upload)

        .def_readonly("dht_upload_rate", &session_status::dht_upload_rate)
        .def_readonly("dht_download_rate", &session_status::dht_download_rate)
        .def_readonly("total_dht_download", &session_status::total_dht_download)
        .def_readonly("total_dht_upload", &session_status::total_dht_upload)

        .def_readonly("tracker_upload_rate", &session_status::tracker_upload_rate)
        .def_readonly("tracker_download_rate", &session_status::tracker_download_rate)
        .def_readonly("total_tracker_download", &session_status::total_tracker_download)
        .def_readonly("total_tracker_upload", &session_status::total_tracker_upload)

        .def_readonly("total_redundant_bytes", &session_status::total_redundant_bytes)
        .def_readonly("total_failed_bytes", &session_status::total_failed_bytes)

        .def_readonly("num_peers", &session_status::num_peers)
        .def_readonly("num_unchoked", &session_status::num_unchoked)
        .def_readonly("allowed_upload_slots", &session_status::allowed_upload_slots)

        .def_readonly("up_bandwidth_queue", &session_status::up_bandwidth_queue)
        .def_readonly("down_bandwidth_queue", &session_status::down_bandwidth_queue)

        .def_readonly("up_bandwidth_bytes_queue", &session_status::up_bandwidth_bytes_queue)
        .def_readonly("down_bandwidth_bytes_queue", &session_status::down_bandwidth_bytes_queue)

        .def_readonly("optimistic_unchoke_counter", &session_status::optimistic_unchoke_counter)
        .def_readonly("unchoke_counter", &session_status::unchoke_counter)

#ifndef TORRENT_DISABLE_DHT
        .def_readonly("dht_nodes", &session_status::dht_nodes)
        .def_readonly("dht_cache_nodes", &session_status::dht_node_cache)
        .def_readonly("dht_torrents", &session_status::dht_torrents)
        .def_readonly("dht_global_nodes", &session_status::dht_global_nodes)
        .def_readonly("active_requests", &session_status::active_requests)
#endif
        ;

    class_<dht_lookup>("dht_lookup")
        .def_readonly("type", &dht_lookup::type)
        .def_readonly("outstanding_requests", &dht_lookup::outstanding_requests)
        .def_readonly("timeouts", &dht_lookup::timeouts)
        .def_readonly("response", &dht_lookup::responses)
        .def_readonly("branch_factor", &dht_lookup::branch_factor)
    ;

    enum_<storage_mode_t>("storage_mode_t")
        .value("storage_mode_allocate", storage_mode_allocate)
        .value("storage_mode_sparse", storage_mode_sparse)
        .value("storage_mode_compact", storage_mode_compact)
    ;

    enum_<session::options_t>("options_t")
        .value("none", session::none)
        .value("delete_files", session::delete_files)
    ;

    enum_<session::session_flags_t>("session_flags_t")
        .value("add_default_plugins", session::add_default_plugins)
        .value("start_default_features", session::start_default_features)
    ;

    class_<session, boost::noncopyable>("session", no_init)
        .def(
            init<fingerprint, int>((
                arg("fingerprint")=fingerprint("LT",0,1,0,0)
                , arg("flags")=session::start_default_features | session::add_default_plugins))
        )
        .def(
            "listen_on", &listen_on
          , (arg("min"), "max", arg("interface") = (char const*)0)
        )
        .def("outgoing_ports", &outgoing_ports)
        .def("is_listening", allow_threads(&session::is_listening))
        .def("listen_port", allow_threads(&session::listen_port))
        .def("status", allow_threads(&session::status))
#ifndef TORRENT_DISABLE_DHT
        .def(
            "add_dht_router", &add_dht_router
          , (arg("router"), "port")
        )
        .def("start_dht", allow_threads(&session::start_dht))
        .def("stop_dht", allow_threads(&session::stop_dht))
        .def("dht_state", allow_threads(&session::dht_state))
        .def("set_dht_proxy", allow_threads(&session::set_dht_proxy))
        .def("dht_proxy", allow_threads(&session::dht_proxy), return_value_policy<copy_const_reference>())
#endif
        .def("add_torrent", &add_torrent)
#ifndef TORRENT_NO_DEPRECATE
        .def(
            "add_torrent", &add_torrent_depr
          , (
                arg("resume_data") = entry(),
					 arg("storage_mode") = storage_mode_sparse,
                arg("paused") = false
            )
        )
#endif
        .def("remove_torrent", allow_threads(&session::remove_torrent), arg("option") = session::none
)
        .def("set_local_download_rate_limit", allow_threads(&session::set_local_download_rate_limit))
        .def("local_download_rate_limit", allow_threads(&session::local_download_rate_limit))

        .def("set_local_upload_rate_limit", allow_threads(&session::set_local_upload_rate_limit))
        .def("local_upload_rate_limit", allow_threads(&session::local_upload_rate_limit))

        .def("set_download_rate_limit", allow_threads(&session::set_download_rate_limit))
        .def("download_rate_limit", allow_threads(&session::download_rate_limit))

        .def("set_upload_rate_limit", allow_threads(&session::set_upload_rate_limit))
        .def("upload_rate_limit", allow_threads(&session::upload_rate_limit))

        .def("set_max_uploads", allow_threads(&session::set_max_uploads))
        .def("set_max_connections", allow_threads(&session::set_max_connections))
        .def("set_max_half_open_connections", allow_threads(&session::set_max_half_open_connections))
        .def("num_connections", allow_threads(&session::num_connections))
        .def("set_settings", allow_threads(&session::set_settings))
        .def("settings", allow_threads(&session::settings), return_value_policy<copy_const_reference>())
#ifndef TORRENT_DISABLE_ENCRYPTION
        .def("set_pe_settings", allow_threads(&session::set_pe_settings))
        .def("get_pe_settings", allow_threads(&session::get_pe_settings), return_value_policy<copy_const_reference>())
#endif
#ifndef TORRENT_DISABLE_GEO_IP
        .def("load_asnum_db", &load_asnum_db)
        .def("load_country_db", &load_country_db)
#endif
        .def("load_state", allow_threads(&session::load_state))
        .def("state", allow_threads(&session::state))
#ifndef TORRENT_NO_DEPRECATE
        .def("set_severity_level", allow_threads(&session::set_severity_level))
#endif
        .def("set_alert_mask", allow_threads(&session::set_alert_mask))
        .def("pop_alert", allow_threads(&session::pop_alert))
        .def("add_extension", &add_extension)
        .def("set_peer_proxy", allow_threads(&session::set_peer_proxy))
        .def("set_tracker_proxy", allow_threads(&session::set_tracker_proxy))
        .def("set_web_seed_proxy", allow_threads(&session::set_web_seed_proxy))
        .def("peer_proxy", allow_threads(&session::peer_proxy), return_value_policy<copy_const_reference>())
        .def("tracker_proxy", allow_threads(&session::tracker_proxy), return_value_policy<copy_const_reference>())
        .def("web_seed_proxy", allow_threads(&session::web_seed_proxy), return_value_policy<copy_const_reference>())
        .def("start_upnp", &start_upnp)
        .def("stop_upnp", allow_threads(&session::stop_upnp))
        .def("start_lsd", allow_threads(&session::start_lsd))
        .def("stop_lsd", allow_threads(&session::stop_lsd))
        .def("start_natpmp", &start_natpmp)
        .def("stop_natpmp", allow_threads(&session::stop_natpmp))
        .def("set_ip_filter", allow_threads(&session::set_ip_filter))
        .def("find_torrent", allow_threads(&session::find_torrent))
        .def("get_torrents", &get_torrents)
        .def("pause", allow_threads(&session::pause))
        .def("resume", allow_threads(&session::resume))
        .def("is_paused", allow_threads(&session::is_paused))
        .def("id", allow_threads(&session::id))
        ;

    register_ptr_to_python<std::auto_ptr<alert> >();
}
<commit_msg>Add session.get_cache_status() to the python bindings<commit_after>// Copyright Daniel Wallin, Arvid Norberg 2006. Use, modification and distribution is
// subject to the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#include <boost/python.hpp>
#include <libtorrent/session.hpp>
#include <libtorrent/torrent.hpp>
#include <libtorrent/storage.hpp>
#include <libtorrent/ip_filter.hpp>
#include <libtorrent/disk_io_thread.hpp>
#include "gil.hpp"

using namespace boost::python;
using namespace libtorrent;

namespace
{
    bool listen_on(session& s, int min_, int max_, char const* interface)
    {
        allow_threading_guard guard;
        return s.listen_on(std::make_pair(min_, max_), interface);
    }

    void outgoing_ports(session& s, int _min, int _max)
    {
        allow_threading_guard guard;
        session_settings settings = s.settings();
        settings.outgoing_ports = std::make_pair(_min, _max);
        s.set_settings(settings);
        return;
    }
#ifndef TORRENT_DISABLE_DHT
    void add_dht_router(session& s, std::string router_, int port_)
    {
        allow_threading_guard guard;
        return s.add_dht_router(std::make_pair(router_, port_));
    }
#endif

    struct invoke_extension_factory
    {
        invoke_extension_factory(object const& callback)
            : cb(callback)
        {}

        boost::shared_ptr<torrent_plugin> operator()(torrent* t, void*)
        {
           lock_gil lock;
           return extract<boost::shared_ptr<torrent_plugin> >(cb(ptr(t)))();
        }

        object cb;
    };

    void add_extension(session& s, object const& e)
    {
        allow_threading_guard guard;
        s.add_extension(invoke_extension_factory(e));
    }

#ifndef TORRENT_NO_DEPRECATE
    torrent_handle add_torrent_depr(session& s, torrent_info const& ti
        , boost::filesystem::path const& save, entry const& resume
        , storage_mode_t storage_mode, bool paused)
    {
        allow_threading_guard guard;
        return s.add_torrent(ti, save, resume, storage_mode, paused, default_storage_constructor);
    }
#endif

    torrent_handle add_torrent(session& s, dict params)
    {
        add_torrent_params p;

        if (params.has_key("ti"))
            p.ti = new torrent_info(extract<torrent_info const&>(params["ti"]));

        std::string url;
        if (params.has_key("tracker_url"))
        {
            url = extract<std::string>(params["tracker_url"]);
            p.tracker_url = url.c_str();
        }
        if (params.has_key("info_hash"))
            p.info_hash = extract<sha1_hash>(params["info_hash"]);
        std::string name;
        if (params.has_key("name"))
        {
            name = extract<std::string>(params["name"]);
            p.name = name.c_str();
        }
        p.save_path = fs::path(extract<std::string>(params["save_path"]));

        std::vector<char> resume_buf;
        if (params.has_key("resume_data"))
        {
            std::string resume = extract<std::string>(params["resume_data"]);
            resume_buf.resize(resume.size());
            std::memcpy(&resume_buf[0], &resume[0], resume.size());
            p.resume_data = &resume_buf;
        }
        if (params.has_key("storage_mode"))
            p.storage_mode = extract<storage_mode_t>(params["storage_mode"]);
        if (params.has_key("paused"))
            p.paused = params["paused"];
        if (params.has_key("auto_managed"))
            p.auto_managed = params["auto_managed"];
        if (params.has_key("duplicate_is_error"))
            p.duplicate_is_error = params["duplicate_is_error"];
        if (params.has_key("seed_mode"))
            p.seed_mode = params["seed_mode"];
        if (params.has_key("override_resume_data"))
            p.override_resume_data = params["override_resume_data"];

        return s.add_torrent(p);
    }

    void start_natpmp(session& s)
    {
        allow_threading_guard guard;
        s.start_natpmp();
        return;
    }

    void start_upnp(session& s)
    {
        allow_threading_guard guard;
        s.start_upnp();
        return;
    }

    list get_torrents(session& s)
    {
        list ret;
        std::vector<torrent_handle> torrents = s.get_torrents();

        for (std::vector<torrent_handle>::iterator i = torrents.begin(); i != torrents.end(); ++i)
        {
            ret.append(*i);
        }
        return ret;
    }

#ifndef TORRENT_DISABLE_GEO_IP
    bool load_asnum_db(session& s, std::string file)
    {
        allow_threading_guard guard;
        return s.load_asnum_db(file.c_str());
    }

    bool load_country_db(session& s, std::string file)
    {
        allow_threading_guard guard;
        return s.load_country_db(file.c_str());
    }
#endif
} // namespace unnamed

void bind_session()
{
    class_<session_status>("session_status")
        .def_readonly("has_incoming_connections", &session_status::has_incoming_connections)

        .def_readonly("upload_rate", &session_status::upload_rate)
        .def_readonly("download_rate", &session_status::download_rate)
        .def_readonly("total_download", &session_status::total_download)
        .def_readonly("total_upload", &session_status::total_upload)

        .def_readonly("payload_upload_rate", &session_status::payload_upload_rate)
        .def_readonly("payload_download_rate", &session_status::payload_download_rate)
        .def_readonly("total_payload_download", &session_status::total_payload_download)
        .def_readonly("total_payload_upload", &session_status::total_payload_upload)

        .def_readonly("ip_overhead_upload_rate", &session_status::ip_overhead_upload_rate)
        .def_readonly("ip_overhead_download_rate", &session_status::ip_overhead_download_rate)
        .def_readonly("total_ip_overhead_download", &session_status::total_ip_overhead_download)
        .def_readonly("total_ip_overhead_upload", &session_status::total_ip_overhead_upload)

        .def_readonly("dht_upload_rate", &session_status::dht_upload_rate)
        .def_readonly("dht_download_rate", &session_status::dht_download_rate)
        .def_readonly("total_dht_download", &session_status::total_dht_download)
        .def_readonly("total_dht_upload", &session_status::total_dht_upload)

        .def_readonly("tracker_upload_rate", &session_status::tracker_upload_rate)
        .def_readonly("tracker_download_rate", &session_status::tracker_download_rate)
        .def_readonly("total_tracker_download", &session_status::total_tracker_download)
        .def_readonly("total_tracker_upload", &session_status::total_tracker_upload)

        .def_readonly("total_redundant_bytes", &session_status::total_redundant_bytes)
        .def_readonly("total_failed_bytes", &session_status::total_failed_bytes)

        .def_readonly("num_peers", &session_status::num_peers)
        .def_readonly("num_unchoked", &session_status::num_unchoked)
        .def_readonly("allowed_upload_slots", &session_status::allowed_upload_slots)

        .def_readonly("up_bandwidth_queue", &session_status::up_bandwidth_queue)
        .def_readonly("down_bandwidth_queue", &session_status::down_bandwidth_queue)

        .def_readonly("up_bandwidth_bytes_queue", &session_status::up_bandwidth_bytes_queue)
        .def_readonly("down_bandwidth_bytes_queue", &session_status::down_bandwidth_bytes_queue)

        .def_readonly("optimistic_unchoke_counter", &session_status::optimistic_unchoke_counter)
        .def_readonly("unchoke_counter", &session_status::unchoke_counter)

#ifndef TORRENT_DISABLE_DHT
        .def_readonly("dht_nodes", &session_status::dht_nodes)
        .def_readonly("dht_cache_nodes", &session_status::dht_node_cache)
        .def_readonly("dht_torrents", &session_status::dht_torrents)
        .def_readonly("dht_global_nodes", &session_status::dht_global_nodes)
        .def_readonly("active_requests", &session_status::active_requests)
#endif
        ;

    class_<dht_lookup>("dht_lookup")
        .def_readonly("type", &dht_lookup::type)
        .def_readonly("outstanding_requests", &dht_lookup::outstanding_requests)
        .def_readonly("timeouts", &dht_lookup::timeouts)
        .def_readonly("response", &dht_lookup::responses)
        .def_readonly("branch_factor", &dht_lookup::branch_factor)
    ;

    enum_<storage_mode_t>("storage_mode_t")
        .value("storage_mode_allocate", storage_mode_allocate)
        .value("storage_mode_sparse", storage_mode_sparse)
        .value("storage_mode_compact", storage_mode_compact)
    ;

    enum_<session::options_t>("options_t")
        .value("none", session::none)
        .value("delete_files", session::delete_files)
    ;

    enum_<session::session_flags_t>("session_flags_t")
        .value("add_default_plugins", session::add_default_plugins)
        .value("start_default_features", session::start_default_features)
    ;

    class_<cache_status>("cache_status")
        .def_readonly("blocks_written", &cache_status::blocks_written)
        .def_readonly("writes", &cache_status::writes)
        .def_readonly("blocks_read", &cache_status::blocks_read)
        .def_readonly("blocks_read_hit", &cache_status::blocks_read_hit)
        .def_readonly("reads", &cache_status::reads)
        .def_readonly("cache_size", &cache_status::cache_size)
        .def_readonly("read_cache_size", &cache_status::read_cache_size)
        .def_readonly("total_used_buffers", &cache_status::total_used_buffers)
    ;

    class_<session, boost::noncopyable>("session", no_init)
        .def(
            init<fingerprint, int>((
                arg("fingerprint")=fingerprint("LT",0,1,0,0)
                , arg("flags")=session::start_default_features | session::add_default_plugins))
        )
        .def(
            "listen_on", &listen_on
          , (arg("min"), "max", arg("interface") = (char const*)0)
        )
        .def("outgoing_ports", &outgoing_ports)
        .def("is_listening", allow_threads(&session::is_listening))
        .def("listen_port", allow_threads(&session::listen_port))
        .def("status", allow_threads(&session::status))
#ifndef TORRENT_DISABLE_DHT
        .def(
            "add_dht_router", &add_dht_router
          , (arg("router"), "port")
        )
        .def("start_dht", allow_threads(&session::start_dht))
        .def("stop_dht", allow_threads(&session::stop_dht))
        .def("dht_state", allow_threads(&session::dht_state))
        .def("set_dht_proxy", allow_threads(&session::set_dht_proxy))
        .def("dht_proxy", allow_threads(&session::dht_proxy), return_value_policy<copy_const_reference>())
#endif
        .def("add_torrent", &add_torrent)
#ifndef TORRENT_NO_DEPRECATE
        .def(
            "add_torrent", &add_torrent_depr
          , (
                arg("resume_data") = entry(),
					 arg("storage_mode") = storage_mode_sparse,
                arg("paused") = false
            )
        )
#endif
        .def("remove_torrent", allow_threads(&session::remove_torrent), arg("option") = session::none
)
        .def("set_local_download_rate_limit", allow_threads(&session::set_local_download_rate_limit))
        .def("local_download_rate_limit", allow_threads(&session::local_download_rate_limit))

        .def("set_local_upload_rate_limit", allow_threads(&session::set_local_upload_rate_limit))
        .def("local_upload_rate_limit", allow_threads(&session::local_upload_rate_limit))

        .def("set_download_rate_limit", allow_threads(&session::set_download_rate_limit))
        .def("download_rate_limit", allow_threads(&session::download_rate_limit))

        .def("set_upload_rate_limit", allow_threads(&session::set_upload_rate_limit))
        .def("upload_rate_limit", allow_threads(&session::upload_rate_limit))

        .def("set_max_uploads", allow_threads(&session::set_max_uploads))
        .def("set_max_connections", allow_threads(&session::set_max_connections))
        .def("set_max_half_open_connections", allow_threads(&session::set_max_half_open_connections))
        .def("num_connections", allow_threads(&session::num_connections))
        .def("set_settings", allow_threads(&session::set_settings))
        .def("settings", allow_threads(&session::settings), return_value_policy<copy_const_reference>())
#ifndef TORRENT_DISABLE_ENCRYPTION
        .def("set_pe_settings", allow_threads(&session::set_pe_settings))
        .def("get_pe_settings", allow_threads(&session::get_pe_settings), return_value_policy<copy_const_reference>())
#endif
#ifndef TORRENT_DISABLE_GEO_IP
        .def("load_asnum_db", &load_asnum_db)
        .def("load_country_db", &load_country_db)
#endif
        .def("load_state", allow_threads(&session::load_state))
        .def("state", allow_threads(&session::state))
#ifndef TORRENT_NO_DEPRECATE
        .def("set_severity_level", allow_threads(&session::set_severity_level))
#endif
        .def("set_alert_mask", allow_threads(&session::set_alert_mask))
        .def("pop_alert", allow_threads(&session::pop_alert))
        .def("add_extension", &add_extension)
        .def("set_peer_proxy", allow_threads(&session::set_peer_proxy))
        .def("set_tracker_proxy", allow_threads(&session::set_tracker_proxy))
        .def("set_web_seed_proxy", allow_threads(&session::set_web_seed_proxy))
        .def("peer_proxy", allow_threads(&session::peer_proxy), return_value_policy<copy_const_reference>())
        .def("tracker_proxy", allow_threads(&session::tracker_proxy), return_value_policy<copy_const_reference>())
        .def("web_seed_proxy", allow_threads(&session::web_seed_proxy), return_value_policy<copy_const_reference>())
        .def("start_upnp", &start_upnp)
        .def("stop_upnp", allow_threads(&session::stop_upnp))
        .def("start_lsd", allow_threads(&session::start_lsd))
        .def("stop_lsd", allow_threads(&session::stop_lsd))
        .def("start_natpmp", &start_natpmp)
        .def("stop_natpmp", allow_threads(&session::stop_natpmp))
        .def("set_ip_filter", allow_threads(&session::set_ip_filter))
        .def("find_torrent", allow_threads(&session::find_torrent))
        .def("get_torrents", &get_torrents)
        .def("pause", allow_threads(&session::pause))
        .def("resume", allow_threads(&session::resume))
        .def("is_paused", allow_threads(&session::is_paused))
        .def("id", allow_threads(&session::id))
        .def("get_cache_status", allow_threads(&session::get_cache_status))
        ;

    register_ptr_to_python<std::auto_ptr<alert> >();
}
<|endoftext|>


<commit_before>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "config.h"

#include "CachedCSSStyleSheet.h"
#include "CachedResource.h"
#include "CachedScript.h"
#include "CachedXSLStyleSheet.h"
#include "DocLoader.h"
#include "Document.h"
#include "DocumentLoader.h"
#include "FrameLoader.h"
#include "PlatformString.h"
#include "ResourceError.h"
#include "ResourceRequest.h"
#include "ResourceResponse.h"
#include "ScriptString.h"
#include "TextEncoding.h"
#include <wtf/CurrentTime.h>
#undef LOG

#include "base/basictypes.h"
#include "base/values.h"
#include "googleurl/src/gurl.h"
#include "webkit/glue/devtools/net_agent_impl.h"
#include "webkit/glue/glue_util.h"

using namespace WebCore;

NetAgentImpl::NetAgentImpl(NetAgentDelegate* delegate)
    : delegate_(delegate),
      document_(NULL),
      main_loader_(NULL),
      last_cached_identifier_(-2),
      attached_(false)  {
}

NetAgentImpl::~NetAgentImpl() {
  SetDocument(NULL);
  DidCommitMainResourceLoad();
  deleteAllValues(pending_resources_);
  pending_resources_.clear();
}

void NetAgentImpl::SetDocument(Document* doc) {
  document_ = doc;
}

void NetAgentImpl::Attach() {
  for (FinishedResources::iterator it = finished_resources_.begin();
       it != finished_resources_.end(); ++it) {
    DictionaryValue value;
    Serialize(*it->second, &value);
    delegate_->DidFinishLoading(it->first, value);
  }
  attached_ = true;
}

void NetAgentImpl::Detach() {
  attached_ = false;
}

void NetAgentImpl::DidCommitMainResourceLoad() {
  for (FinishedResources::iterator it = finished_resources_.begin();
       it != finished_resources_.end(); ++it) {
    delete it->second;
  }
  finished_resources_.clear();
  main_loader_ = NULL;
}

void NetAgentImpl::AssignIdentifierToRequest(
    DocumentLoader* loader,
    int identifier,
    const ResourceRequest& request) {
}

void NetAgentImpl::WillSendRequest(
    DocumentLoader* loader,
    int identifier,
    const ResourceRequest& request) {
  if (pending_resources_.contains(identifier)) {
    // We are going through redirect, nuke old resource.
    delete pending_resources_.get(identifier);
  }

  Resource* resource = new Resource();
  KURL url = request.url();
  resource->start_time = WTF::currentTime();
  resource->url = request.url();
  resource->request_headers = request.httpHeaderFields();

  if (attached_) {
    DictionaryValue value;
    Serialize(*resource, &value);
    delegate_->WillSendRequest(identifier, value);
  }
}

void NetAgentImpl::DidReceiveResponse(
    DocumentLoader* loader,
    int identifier,
    const ResourceResponse &response) {
  if (!pending_resources_.contains(identifier)) {
    return;
  }

  KURL url = response.url();
  Resource* resource = pending_resources_.get(identifier);
  resource->response_received_time = WTF::currentTime();
  resource->expected_content_length =
      static_cast<int>(response.expectedContentLength());
  resource->http_status_code = response.httpStatusCode();
  resource->mime_type = response.mimeType();
  resource->suggested_filename = response.suggestedFilename();
  resource->response_headers = response.httpHeaderFields();

  if (attached_) {
    DictionaryValue value;
    Serialize(*resource, &value);
    delegate_->DidReceiveResponse(identifier, value);
  }
}

void NetAgentImpl::DidReceiveContentLength(
    DocumentLoader* loader,
    int identifier,
    int length) {
}

void NetAgentImpl::DidFinishLoading(
    DocumentLoader* loader,
    int identifier) {
  if (!pending_resources_.contains(identifier)) {
    return;
  }

  // This is the first command being dispatched after
  // DidCommitMainResourceLoad, we know that the first resource to be reported
  // as loaded is main resource.
  if (!main_loader_.get()) {
    main_loader_ = loader;
  }

  Resource* resource = pending_resources_.get(identifier);
  resource->end_time = WTF::currentTime();

  pending_resources_.remove(identifier);
  finished_resources_.append(std::make_pair(identifier, resource));

  // Start removing resources from the cache once there are too many of them.
  if (finished_resources_.size() > 200) {
    for (int i = 0; i < 50; ++i) {
      xml_http_sources_.remove(finished_resources_[i].first);
      delete finished_resources_[i].second;
    }
    finished_resources_.remove(0, 50);
  }

  if (attached_) {
    DictionaryValue value;
    Serialize(*resource, &value);
    delegate_->DidFinishLoading(identifier, value);
  }
}

void NetAgentImpl::DidFailLoading(
    DocumentLoader* loader,
    int identifier,
    const ResourceError& error) {
  if (!pending_resources_.contains(identifier)) {
    return;
  }
  Resource* resource = pending_resources_.get(identifier);
  resource->error_code = error.errorCode();
  resource->error_description = error.localizedDescription();
  DidFinishLoading(loader, identifier);
}

void NetAgentImpl::DidLoadResourceFromMemoryCache(
    DocumentLoader* loader,
    const ResourceRequest& request,
    const ResourceResponse& response,
    int length) {
  int identifier = last_cached_identifier_--;
}

void NetAgentImpl::DidLoadResourceByXMLHttpRequest(
    int identifier,
    const WebCore::ScriptString& source) {
  xml_http_sources_.set(identifier, source);
}

void NetAgentImpl::GetResourceContent(
    int call_id,
    int identifier,
    const String& url) {
  if (!document_) {
    return;
  }

  String source;

  WebCore::ScriptString script = xml_http_sources_.get(identifier);
  if (!script.isNull()) {
    source = String(script);
  } else if (main_loader_.get() && main_loader_->requestURL() == url) {
    RefPtr<SharedBuffer> buffer = main_loader_->mainResourceData();
    String text_encoding_name = document_->inputEncoding();
    if (buffer) {
      WebCore::TextEncoding encoding(text_encoding_name);
      if (!encoding.isValid())
        encoding = WindowsLatin1Encoding();
      source = encoding.decode(buffer->data(), buffer->size());
    }
  } else {
    CachedResource* cached_resource = document_->
        docLoader()->cachedResource(url);
    if (!cached_resource) {
      delegate_->GetResourceContentResult(call_id, "");
      return;
    }
    if (cached_resource->isPurgeable()) {
      // If the resource is purgeable then make it unpurgeable to get its data.
      // This might fail, in which case we return an empty string.
      if (!cached_resource->makePurgeable(false)) {
        delegate_->GetResourceContentResult(call_id, "");
        return;
      }
    }

    // Try to get the decoded source.  Only applies to some CachedResource
    // types.
    switch (cached_resource->type()) {
      case CachedResource::CSSStyleSheet: {
        CachedCSSStyleSheet *sheet =
            reinterpret_cast<CachedCSSStyleSheet*>(cached_resource);
        source = sheet->sheetText();
        break;
      }
      case CachedResource::Script: {
        CachedScript *script =
            reinterpret_cast<CachedScript*>(cached_resource);
        source = script->script();
        break;
      }
#if ENABLE(XSLT)
      case CachedResource::XSLStyleSheet: {
        CachedXSLStyleSheet *sheet =
            reinterpret_cast<CachedXSLStyleSheet*>(cached_resource);
        source = sheet->sheet();
        break;
      }
#endif
      default:
        break;
    }
  }
  delegate_->GetResourceContentResult(call_id,
      webkit_glue::StringToStdString(source));
}

// static
Value* NetAgentImpl::BuildValueForHeaders(const HTTPHeaderMap& headers) {
  OwnPtr<DictionaryValue> value(new DictionaryValue());
  HTTPHeaderMap::const_iterator end = headers.end();
  for (HTTPHeaderMap::const_iterator it = headers.begin(); it != end; ++it) {
    value->SetString(webkit_glue::StringToStdWString(it->first),
        webkit_glue::StringToStdString(it->second));
  }
  return value.release();
}

// static
void NetAgentImpl::Serialize(const Resource& resource,
                             DictionaryValue* value) {
  value->SetReal(L"startTime", resource.start_time);
  value->SetReal(L"responseReceivedTime", resource.response_received_time);
  value->SetReal(L"endTime", resource.end_time);

  value->SetString(L"url",
                   webkit_glue::StringToStdString(resource.url.string()));
  value->SetString(L"domain",
                   webkit_glue::StringToStdString(resource.url.host()));
  value->SetString(L"path",
                   webkit_glue::StringToStdString(resource.url.path()));
  value->SetString(
      L"lastPathComponent",
      webkit_glue::StringToStdString(resource.url.lastPathComponent()));

  value->SetString(L"mimeType",
      webkit_glue::StringToStdWString(resource.mime_type));
  value->SetString(L"suggestedFilename",
      webkit_glue::StringToStdWString(resource.suggested_filename));

  value->SetInteger(L"expectedContentLength",
                    resource.expected_content_length);
  value->SetInteger(L"responseStatusCode", resource.http_status_code);

  value->Set(L"requestHeaders",
             BuildValueForHeaders(resource.request_headers));
  value->Set(L"responseHeaders",
             BuildValueForHeaders(resource.response_headers));

  if (resource.error_code) {
    value->SetInteger(L"errorCode", resource.error_code);
    value->SetString(L"localizedDescription",
        webkit_glue::StringToStdString(resource.error_description));
  }
}
<commit_msg>DevTools: fix memory leak. TBR=yurys<commit_after>// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "config.h"

#include "CachedCSSStyleSheet.h"
#include "CachedResource.h"
#include "CachedScript.h"
#include "CachedXSLStyleSheet.h"
#include "DocLoader.h"
#include "Document.h"
#include "DocumentLoader.h"
#include "FrameLoader.h"
#include "PlatformString.h"
#include "ResourceError.h"
#include "ResourceRequest.h"
#include "ResourceResponse.h"
#include "ScriptString.h"
#include "TextEncoding.h"
#include <wtf/CurrentTime.h>
#undef LOG

#include "base/basictypes.h"
#include "base/values.h"
#include "googleurl/src/gurl.h"
#include "webkit/glue/devtools/net_agent_impl.h"
#include "webkit/glue/glue_util.h"

using namespace WebCore;

NetAgentImpl::NetAgentImpl(NetAgentDelegate* delegate)
    : delegate_(delegate),
      document_(NULL),
      main_loader_(NULL),
      last_cached_identifier_(-2),
      attached_(false)  {
}

NetAgentImpl::~NetAgentImpl() {
  SetDocument(NULL);
  DidCommitMainResourceLoad();
  deleteAllValues(pending_resources_);
  pending_resources_.clear();
}

void NetAgentImpl::SetDocument(Document* doc) {
  document_ = doc;
}

void NetAgentImpl::Attach() {
  for (FinishedResources::iterator it = finished_resources_.begin();
       it != finished_resources_.end(); ++it) {
    DictionaryValue value;
    Serialize(*it->second, &value);
    delegate_->DidFinishLoading(it->first, value);
  }
  attached_ = true;
}

void NetAgentImpl::Detach() {
  attached_ = false;
}

void NetAgentImpl::DidCommitMainResourceLoad() {
  for (FinishedResources::iterator it = finished_resources_.begin();
       it != finished_resources_.end(); ++it) {
    delete it->second;
  }
  finished_resources_.clear();
  main_loader_ = NULL;
}

void NetAgentImpl::AssignIdentifierToRequest(
    DocumentLoader* loader,
    int identifier,
    const ResourceRequest& request) {
}

void NetAgentImpl::WillSendRequest(
    DocumentLoader* loader,
    int identifier,
    const ResourceRequest& request) {
  if (pending_resources_.contains(identifier)) {
    // We are going through redirect, nuke old resource.
    delete pending_resources_.get(identifier);
  }

  Resource* resource = new Resource();
  pending_resources_.set(identifier, resource);
  KURL url = request.url();
  resource->start_time = WTF::currentTime();
  resource->url = request.url();
  resource->request_headers = request.httpHeaderFields();

  if (attached_) {
    DictionaryValue value;
    Serialize(*resource, &value);
    delegate_->WillSendRequest(identifier, value);
  }
}

void NetAgentImpl::DidReceiveResponse(
    DocumentLoader* loader,
    int identifier,
    const ResourceResponse &response) {
  if (!pending_resources_.contains(identifier)) {
    return;
  }

  KURL url = response.url();
  Resource* resource = pending_resources_.get(identifier);
  resource->response_received_time = WTF::currentTime();
  resource->expected_content_length =
      static_cast<int>(response.expectedContentLength());
  resource->http_status_code = response.httpStatusCode();
  resource->mime_type = response.mimeType();
  resource->suggested_filename = response.suggestedFilename();
  resource->response_headers = response.httpHeaderFields();

  if (attached_) {
    DictionaryValue value;
    Serialize(*resource, &value);
    delegate_->DidReceiveResponse(identifier, value);
  }
}

void NetAgentImpl::DidReceiveContentLength(
    DocumentLoader* loader,
    int identifier,
    int length) {
}

void NetAgentImpl::DidFinishLoading(
    DocumentLoader* loader,
    int identifier) {
  if (!pending_resources_.contains(identifier)) {
    return;
  }

  // This is the first command being dispatched after
  // DidCommitMainResourceLoad, we know that the first resource to be reported
  // as loaded is main resource.
  if (!main_loader_.get()) {
    main_loader_ = loader;
  }

  Resource* resource = pending_resources_.get(identifier);
  resource->end_time = WTF::currentTime();

  pending_resources_.remove(identifier);
  finished_resources_.append(std::make_pair(identifier, resource));

  // Start removing resources from the cache once there are too many of them.
  if (finished_resources_.size() > 200) {
    for (int i = 0; i < 50; ++i) {
      xml_http_sources_.remove(finished_resources_[i].first);
      delete finished_resources_[i].second;
    }
    finished_resources_.remove(0, 50);
  }

  if (attached_) {
    DictionaryValue value;
    Serialize(*resource, &value);
    delegate_->DidFinishLoading(identifier, value);
  }
}

void NetAgentImpl::DidFailLoading(
    DocumentLoader* loader,
    int identifier,
    const ResourceError& error) {
  if (!pending_resources_.contains(identifier)) {
    return;
  }
  Resource* resource = pending_resources_.get(identifier);
  resource->error_code = error.errorCode();
  resource->error_description = error.localizedDescription();
  DidFinishLoading(loader, identifier);
}

void NetAgentImpl::DidLoadResourceFromMemoryCache(
    DocumentLoader* loader,
    const ResourceRequest& request,
    const ResourceResponse& response,
    int length) {
  int identifier = last_cached_identifier_--;
}

void NetAgentImpl::DidLoadResourceByXMLHttpRequest(
    int identifier,
    const WebCore::ScriptString& source) {
  xml_http_sources_.set(identifier, source);
}

void NetAgentImpl::GetResourceContent(
    int call_id,
    int identifier,
    const String& url) {
  if (!document_) {
    return;
  }

  String source;

  WebCore::ScriptString script = xml_http_sources_.get(identifier);
  if (!script.isNull()) {
    source = String(script);
  } else if (main_loader_.get() && main_loader_->requestURL() == url) {
    RefPtr<SharedBuffer> buffer = main_loader_->mainResourceData();
    String text_encoding_name = document_->inputEncoding();
    if (buffer) {
      WebCore::TextEncoding encoding(text_encoding_name);
      if (!encoding.isValid())
        encoding = WindowsLatin1Encoding();
      source = encoding.decode(buffer->data(), buffer->size());
    }
  } else {
    CachedResource* cached_resource = document_->
        docLoader()->cachedResource(url);
    if (!cached_resource) {
      delegate_->GetResourceContentResult(call_id, "");
      return;
    }
    if (cached_resource->isPurgeable()) {
      // If the resource is purgeable then make it unpurgeable to get its data.
      // This might fail, in which case we return an empty string.
      if (!cached_resource->makePurgeable(false)) {
        delegate_->GetResourceContentResult(call_id, "");
        return;
      }
    }

    // Try to get the decoded source.  Only applies to some CachedResource
    // types.
    switch (cached_resource->type()) {
      case CachedResource::CSSStyleSheet: {
        CachedCSSStyleSheet *sheet =
            reinterpret_cast<CachedCSSStyleSheet*>(cached_resource);
        source = sheet->sheetText();
        break;
      }
      case CachedResource::Script: {
        CachedScript *script =
            reinterpret_cast<CachedScript*>(cached_resource);
        source = script->script();
        break;
      }
#if ENABLE(XSLT)
      case CachedResource::XSLStyleSheet: {
        CachedXSLStyleSheet *sheet =
            reinterpret_cast<CachedXSLStyleSheet*>(cached_resource);
        source = sheet->sheet();
        break;
      }
#endif
      default:
        break;
    }
  }
  delegate_->GetResourceContentResult(call_id,
      webkit_glue::StringToStdString(source));
}

// static
Value* NetAgentImpl::BuildValueForHeaders(const HTTPHeaderMap& headers) {
  OwnPtr<DictionaryValue> value(new DictionaryValue());
  HTTPHeaderMap::const_iterator end = headers.end();
  for (HTTPHeaderMap::const_iterator it = headers.begin(); it != end; ++it) {
    value->SetString(webkit_glue::StringToStdWString(it->first),
        webkit_glue::StringToStdString(it->second));
  }
  return value.release();
}

// static
void NetAgentImpl::Serialize(const Resource& resource,
                             DictionaryValue* value) {
  value->SetReal(L"startTime", resource.start_time);
  value->SetReal(L"responseReceivedTime", resource.response_received_time);
  value->SetReal(L"endTime", resource.end_time);

  value->SetString(L"url",
                   webkit_glue::StringToStdString(resource.url.string()));
  value->SetString(L"domain",
                   webkit_glue::StringToStdString(resource.url.host()));
  value->SetString(L"path",
                   webkit_glue::StringToStdString(resource.url.path()));
  value->SetString(
      L"lastPathComponent",
      webkit_glue::StringToStdString(resource.url.lastPathComponent()));

  value->SetString(L"mimeType",
      webkit_glue::StringToStdWString(resource.mime_type));
  value->SetString(L"suggestedFilename",
      webkit_glue::StringToStdWString(resource.suggested_filename));

  value->SetInteger(L"expectedContentLength",
                    resource.expected_content_length);
  value->SetInteger(L"responseStatusCode", resource.http_status_code);

  value->Set(L"requestHeaders",
             BuildValueForHeaders(resource.request_headers));
  value->Set(L"responseHeaders",
             BuildValueForHeaders(resource.response_headers));

  if (resource.error_code) {
    value->SetInteger(L"errorCode", resource.error_code);
    value->SetString(L"localizedDescription",
        webkit_glue::StringToStdString(resource.error_description));
  }
}
<|endoftext|>


<commit_before>#include "messagelayoutcontainer.hpp"

#include "messagelayoutelement.hpp"
#include "messages/selection.hpp"
#include "singletons/settingsmanager.hpp"

#include <QDebug>
#include <QPainter>

#define COMPACT_EMOTES_OFFSET 6

namespace chatterino {
namespace messages {
namespace layouts {

int MessageLayoutContainer::getHeight() const
{
    return this->height;
}

int MessageLayoutContainer::getWidth() const
{
    return this->width;
}

float MessageLayoutContainer::getScale() const
{
    return this->scale;
}

// methods
void MessageLayoutContainer::begin(int _width, float _scale, Message::MessageFlags _flags)
{
    this->clear();
    this->width = _width;
    this->scale = _scale;
    this->flags = _flags;
}

void MessageLayoutContainer::clear()
{
    this->elements.clear();
    this->lines.clear();

    this->height = 0;
    this->line = 0;
    this->currentX = 0;
    this->currentY = 0;
    this->lineStart = 0;
    this->lineHeight = 0;
    this->charIndex = 0;
}

void MessageLayoutContainer::addElement(MessageLayoutElement *element)
{
    if (!this->fitsInLine(element->getRect().width())) {
        this->breakLine();
    }

    this->_addElement(element);
}

void MessageLayoutContainer::addElementNoLineBreak(MessageLayoutElement *element)
{
    this->_addElement(element);
}

void MessageLayoutContainer::_addElement(MessageLayoutElement *element)
{
    // top margin
    if (this->elements.size() == 0) {
        this->currentY = this->margin.top * this->scale;
    }

    int newLineHeight = element->getRect().height();

    // compact emote offset
    bool isCompactEmote = !(this->flags & Message::DisableCompactEmotes) &&
                          element->getCreator().getFlags() & MessageElement::EmoteImages;

    if (isCompactEmote) {
        newLineHeight -= COMPACT_EMOTES_OFFSET * this->scale;
    }

    // update line height
    this->lineHeight = std::max(this->lineHeight, newLineHeight);

    // set move element
    element->setPosition(QPoint(this->currentX, this->currentY - element->getRect().height()));

    // add element
    this->elements.push_back(std::unique_ptr<MessageLayoutElement>(element));

    // set current x
    this->currentX += element->getRect().width();

    if (element->hasTrailingSpace()) {
        this->currentX += this->spaceWidth;
    }
}

void MessageLayoutContainer::breakLine()
{
    int xOffset = 0;

    if (this->flags & Message::Centered && this->elements.size() > 0) {
        xOffset = (width - this->elements.at(this->elements.size() - 1)->getRect().right()) / 2;
    }

    for (size_t i = lineStart; i < this->elements.size(); i++) {
        MessageLayoutElement *element = this->elements.at(i).get();

        bool isCompactEmote =
            !(this->flags & Message::DisableCompactEmotes) &&
            (this->flags & element->getCreator().getFlags()) & MessageElement::EmoteImages;

        int yExtra = 0;
        if (isCompactEmote) {
            yExtra = (COMPACT_EMOTES_OFFSET / 2) * this->scale;
        }

        element->setPosition(QPoint(element->getRect().x() + xOffset + this->margin.left,
                                    element->getRect().y() + this->lineHeight + yExtra));
    }

    if (this->lines.size() != 0) {
        this->lines.back().endIndex = this->lineStart;
        this->lines.back().endCharIndex = this->charIndex;
    }
    this->lines.push_back({(int)lineStart, 0, this->charIndex, 0,
                           QRect(-100000, this->currentY, 200000, lineHeight)});

    for (int i = this->lineStart; i < this->elements.size(); i++) {
        this->charIndex += this->elements[i]->getSelectionIndexCount();
    }

    this->lineStart = this->elements.size();
    this->currentX = (int)(this->scale * 8);
    this->currentY += this->lineHeight;
    this->height = this->currentY + (this->margin.bottom * this->scale);
    this->lineHeight = 0;
}

bool MessageLayoutContainer::atStartOfLine()
{
    return this->lineStart == this->elements.size();
}

bool MessageLayoutContainer::fitsInLine(int _width)
{
    return this->currentX + _width <= this->width - this->margin.left - this->margin.right;
}

void MessageLayoutContainer::end()
{
    if (!this->atStartOfLine()) {
        this->breakLine();
    }

    if (this->lines.size() != 0) {
        this->lines[0].rect.setTop(-100000);
        this->lines.back().rect.setBottom(100000);
        this->lines.back().endIndex = this->elements.size();
        this->lines.back().endCharIndex = this->charIndex;
    }
}

MessageLayoutElement *MessageLayoutContainer::getElementAt(QPoint point)
{
    for (std::unique_ptr<MessageLayoutElement> &element : this->elements) {
        if (element->getRect().contains(point)) {
            return element.get();
        }
    }

    return nullptr;
}

// painting
void MessageLayoutContainer::paintElements(QPainter &painter)
{
    for (const std::unique_ptr<MessageLayoutElement> &element : this->elements) {
        element->paint(painter);
    }
}

void MessageLayoutContainer::paintAnimatedElements(QPainter &painter, int yOffset)
{
    for (const std::unique_ptr<MessageLayoutElement> &element : this->elements) {
        element->paintAnimated(painter, yOffset);
    }
}

void MessageLayoutContainer::paintSelection(QPainter &painter, int messageIndex,
                                            Selection &selection)
{
    singletons::ThemeManager &themeManager = singletons::ThemeManager::getInstance();
    QColor selectionColor = themeManager.messages.selection;

    // don't draw anything
    if (selection.min.messageIndex > messageIndex || selection.max.messageIndex < messageIndex) {
        return;
    }

    // fully selected
    if (selection.min.messageIndex < messageIndex && selection.max.messageIndex > messageIndex) {
        for (Line &line : this->lines) {
            QRect rect = line.rect;

            rect.setTop(std::max(0, rect.top()));
            rect.setBottom(std::min(this->height, rect.bottom()));
            rect.setLeft(this->elements[line.startIndex]->getRect().left());
            rect.setRight(this->elements[line.endIndex - 1]->getRect().right());

            painter.fillRect(rect, selectionColor);
        }
        return;
    }

    int lineIndex = 0;
    int index = 0;

    // start in this message
    if (selection.min.messageIndex == messageIndex) {
        for (; lineIndex < this->lines.size(); lineIndex++) {
            Line &line = this->lines[lineIndex];
            index = line.startCharIndex;

            bool returnAfter = false;
            bool breakAfter = false;
            int x = this->elements[line.startIndex]->getRect().left();
            int r = this->elements[line.endIndex - 1]->getRect().right();

            if (line.endCharIndex < selection.min.charIndex) {
                continue;
            }

            for (int i = line.startIndex; i < line.endIndex; i++) {
                int c = this->elements[i]->getSelectionIndexCount();

                if (index + c > selection.min.charIndex) {
                    x = this->elements[i]->getXFromIndex(selection.min.charIndex - index);

                    // ends in same line
                    if (selection.max.messageIndex == messageIndex &&
                        line.endCharIndex > /*=*/selection.max.charIndex)  //
                    {
                        returnAfter = true;
                        index = line.startCharIndex;
                        for (int i = line.startIndex; i < line.endIndex; i++) {
                            int c = this->elements[i]->getSelectionIndexCount();

                            if (index + c > selection.max.charIndex) {
                                r = this->elements[i]->getXFromIndex(selection.max.charIndex -
                                                                     index);
                                break;
                            }
                            index += c;
                        }
                    }
                    // ends in same line end

                    if (selection.max.messageIndex != messageIndex) {
                        int lineIndex2 = lineIndex + 1;
                        for (; lineIndex2 < this->lines.size(); lineIndex2++) {
                            Line &line = this->lines[lineIndex2];
                            QRect rect = line.rect;

                            rect.setTop(std::max(0, rect.top()));
                            rect.setBottom(std::min(this->height, rect.bottom()));
                            rect.setLeft(this->elements[line.startIndex]->getRect().left());
                            rect.setRight(this->elements[line.endIndex - 1]->getRect().right());

                            painter.fillRect(rect, selectionColor);
                        }
                        returnAfter = true;
                    } else {
                        lineIndex++;
                        breakAfter = true;
                    }

                    break;
                }
                index += c;
            }

            QRect rect = line.rect;

            rect.setTop(std::max(0, rect.top()));
            rect.setBottom(std::min(this->height, rect.bottom()));
            rect.setLeft(x);
            rect.setRight(r);

            painter.fillRect(rect, selectionColor);

            if (returnAfter) {
                return;
            }

            if (breakAfter) {
                break;
            }
        }
    }

    // start in this message
    for (; lineIndex < this->lines.size(); lineIndex++) {
        Line &line = this->lines[lineIndex];
        index = line.startCharIndex;

        // just draw the garbage
        if (line.endCharIndex < /*=*/selection.max.charIndex) {
            QRect rect = line.rect;

            rect.setTop(std::max(0, rect.top()));
            rect.setBottom(std::min(this->height, rect.bottom()));
            rect.setLeft(this->elements[line.startIndex]->getRect().left());
            rect.setRight(this->elements[line.endIndex - 1]->getRect().right());

            painter.fillRect(rect, selectionColor);
            continue;
        }

        int r = this->elements[line.endIndex - 1]->getRect().right();

        for (int i = line.startIndex; i < line.endIndex; i++) {
            int c = this->elements[i]->getSelectionIndexCount();

            if (index + c > selection.max.charIndex) {
                r = this->elements[i]->getXFromIndex(selection.max.charIndex - index);
                break;
            }

            index += c;
        }

        QRect rect = line.rect;

        rect.setTop(std::max(0, rect.top()));
        rect.setBottom(std::min(this->height, rect.bottom()));
        rect.setLeft(this->elements[line.startIndex]->getRect().left());
        rect.setRight(r);

        painter.fillRect(rect, selectionColor);
        break;
    }
}

// selection
int MessageLayoutContainer::getSelectionIndex(QPoint point)
{
    if (this->elements.size() == 0) {
        return 0;
    }

    auto line = this->lines.begin();

    for (; line != this->lines.end(); line++) {
        if (line->rect.contains(point)) {
            break;
        }
    }

    int lineStart = line == this->lines.end() ? this->lines.back().startIndex : line->startIndex;
    if (line != this->lines.end()) {
        line++;
    }
    int lineEnd = line == this->lines.end() ? this->elements.size() : line->startIndex;

    int index = 0;

    for (int i = 0; i < lineEnd; i++) {
        // end of line
        if (i == lineEnd) {
            break;
        }

        // before line
        if (i < lineStart) {
            index += this->elements[i]->getSelectionIndexCount();
            continue;
        }

        // this is the word
        if (point.x() < this->elements[i]->getRect().right()) {
            index += this->elements[i]->getMouseOverIndex(point);
            break;
        }

        index += this->elements[i]->getSelectionIndexCount();
    }

    return index;
}

// fourtf: no idea if this is acurate LOL
int MessageLayoutContainer::getLastCharacterIndex() const
{
    if (this->lines.size() == 0) {
        return 0;
    }
    return this->lines.back().endCharIndex;
}

void MessageLayoutContainer::addSelectionText(QString &str, int from, int to)
{
    int index = 0;
    bool xd = true;

    for (std::unique_ptr<MessageLayoutElement> &ele : this->elements) {
        int c = ele->getSelectionIndexCount();

        if (xd) {
            if (index + c > from) {
                ele->addCopyTextToString(str, index - from, to - from);
                xd = false;
            }
        } else {
            if (index + c > from) {
                ele->addCopyTextToString(str, 0, index - to);
                break;
            } else {
                ele->addCopyTextToString(str);
            }
        }

        index += c;
    }
}

}  // namespace layouts
}  // namespace messages
}  // namespace chatterino
<commit_msg>fixes #307<commit_after>#include "messagelayoutcontainer.hpp"

#include "messagelayoutelement.hpp"
#include "messages/selection.hpp"
#include "singletons/settingsmanager.hpp"

#include <QDebug>
#include <QPainter>

#define COMPACT_EMOTES_OFFSET 6

namespace chatterino {
namespace messages {
namespace layouts {

int MessageLayoutContainer::getHeight() const
{
    return this->height;
}

int MessageLayoutContainer::getWidth() const
{
    return this->width;
}

float MessageLayoutContainer::getScale() const
{
    return this->scale;
}

// methods
void MessageLayoutContainer::begin(int _width, float _scale, Message::MessageFlags _flags)
{
    this->clear();
    this->width = _width;
    this->scale = _scale;
    this->flags = _flags;
}

void MessageLayoutContainer::clear()
{
    this->elements.clear();
    this->lines.clear();

    this->height = 0;
    this->line = 0;
    this->currentX = 0;
    this->currentY = 0;
    this->lineStart = 0;
    this->lineHeight = 0;
    this->charIndex = 0;
}

void MessageLayoutContainer::addElement(MessageLayoutElement *element)
{
    if (!this->fitsInLine(element->getRect().width())) {
        this->breakLine();
    }

    this->_addElement(element);
}

void MessageLayoutContainer::addElementNoLineBreak(MessageLayoutElement *element)
{
    this->_addElement(element);
}

void MessageLayoutContainer::_addElement(MessageLayoutElement *element)
{
    // top margin
    if (this->elements.size() == 0) {
        this->currentY = this->margin.top * this->scale;
    }

    int newLineHeight = element->getRect().height();

    // compact emote offset
    bool isCompactEmote = !(this->flags & Message::DisableCompactEmotes) &&
                          element->getCreator().getFlags() & MessageElement::EmoteImages;

    if (isCompactEmote) {
        newLineHeight -= COMPACT_EMOTES_OFFSET * this->scale;
    }

    // update line height
    this->lineHeight = std::max(this->lineHeight, newLineHeight);

    // set move element
    element->setPosition(QPoint(this->currentX, this->currentY - element->getRect().height()));

    // add element
    this->elements.push_back(std::unique_ptr<MessageLayoutElement>(element));

    // set current x
    this->currentX += element->getRect().width();

    if (element->hasTrailingSpace()) {
        this->currentX += this->spaceWidth;
    }
}

void MessageLayoutContainer::breakLine()
{
    int xOffset = 0;

    if (this->flags & Message::Centered && this->elements.size() > 0) {
        xOffset = (width - this->elements.at(this->elements.size() - 1)->getRect().right()) / 2;
    }

    for (size_t i = lineStart; i < this->elements.size(); i++) {
        MessageLayoutElement *element = this->elements.at(i).get();

        bool isCompactEmote = !(this->flags & Message::DisableCompactEmotes) &&
                              element->getCreator().getFlags() & MessageElement::EmoteImages;

        int yExtra = 0;
        if (isCompactEmote) {
            yExtra = (COMPACT_EMOTES_OFFSET / 2) * this->scale;
        }

        element->setPosition(QPoint(element->getRect().x() + xOffset + this->margin.left,
                                    element->getRect().y() + this->lineHeight + yExtra));
    }

    if (this->lines.size() != 0) {
        this->lines.back().endIndex = this->lineStart;
        this->lines.back().endCharIndex = this->charIndex;
    }
    this->lines.push_back({(int)lineStart, 0, this->charIndex, 0,
                           QRect(-100000, this->currentY, 200000, lineHeight)});

    for (int i = this->lineStart; i < this->elements.size(); i++) {
        this->charIndex += this->elements[i]->getSelectionIndexCount();
    }

    this->lineStart = this->elements.size();
    this->currentX = (int)(this->scale * 8);
    this->currentY += this->lineHeight;
    this->height = this->currentY + (this->margin.bottom * this->scale);
    this->lineHeight = 0;
}

bool MessageLayoutContainer::atStartOfLine()
{
    return this->lineStart == this->elements.size();
}

bool MessageLayoutContainer::fitsInLine(int _width)
{
    return this->currentX + _width <= this->width - this->margin.left - this->margin.right;
}

void MessageLayoutContainer::end()
{
    if (!this->atStartOfLine()) {
        this->breakLine();
    }

    if (this->lines.size() != 0) {
        this->lines[0].rect.setTop(-100000);
        this->lines.back().rect.setBottom(100000);
        this->lines.back().endIndex = this->elements.size();
        this->lines.back().endCharIndex = this->charIndex;
    }
}

MessageLayoutElement *MessageLayoutContainer::getElementAt(QPoint point)
{
    for (std::unique_ptr<MessageLayoutElement> &element : this->elements) {
        if (element->getRect().contains(point)) {
            return element.get();
        }
    }

    return nullptr;
}

// painting
void MessageLayoutContainer::paintElements(QPainter &painter)
{
    for (const std::unique_ptr<MessageLayoutElement> &element : this->elements) {
        element->paint(painter);
    }
}

void MessageLayoutContainer::paintAnimatedElements(QPainter &painter, int yOffset)
{
    for (const std::unique_ptr<MessageLayoutElement> &element : this->elements) {
        element->paintAnimated(painter, yOffset);
    }
}

void MessageLayoutContainer::paintSelection(QPainter &painter, int messageIndex,
                                            Selection &selection)
{
    singletons::ThemeManager &themeManager = singletons::ThemeManager::getInstance();
    QColor selectionColor = themeManager.messages.selection;

    // don't draw anything
    if (selection.min.messageIndex > messageIndex || selection.max.messageIndex < messageIndex) {
        return;
    }

    // fully selected
    if (selection.min.messageIndex < messageIndex && selection.max.messageIndex > messageIndex) {
        for (Line &line : this->lines) {
            QRect rect = line.rect;

            rect.setTop(std::max(0, rect.top()));
            rect.setBottom(std::min(this->height, rect.bottom()));
            rect.setLeft(this->elements[line.startIndex]->getRect().left());
            rect.setRight(this->elements[line.endIndex - 1]->getRect().right());

            painter.fillRect(rect, selectionColor);
        }
        return;
    }

    int lineIndex = 0;
    int index = 0;

    // start in this message
    if (selection.min.messageIndex == messageIndex) {
        for (; lineIndex < this->lines.size(); lineIndex++) {
            Line &line = this->lines[lineIndex];
            index = line.startCharIndex;

            bool returnAfter = false;
            bool breakAfter = false;
            int x = this->elements[line.startIndex]->getRect().left();
            int r = this->elements[line.endIndex - 1]->getRect().right();

            if (line.endCharIndex < selection.min.charIndex) {
                continue;
            }

            for (int i = line.startIndex; i < line.endIndex; i++) {
                int c = this->elements[i]->getSelectionIndexCount();

                if (index + c > selection.min.charIndex) {
                    x = this->elements[i]->getXFromIndex(selection.min.charIndex - index);

                    // ends in same line
                    if (selection.max.messageIndex == messageIndex &&
                        line.endCharIndex > /*=*/selection.max.charIndex)  //
                    {
                        returnAfter = true;
                        index = line.startCharIndex;
                        for (int i = line.startIndex; i < line.endIndex; i++) {
                            int c = this->elements[i]->getSelectionIndexCount();

                            if (index + c > selection.max.charIndex) {
                                r = this->elements[i]->getXFromIndex(selection.max.charIndex -
                                                                     index);
                                break;
                            }
                            index += c;
                        }
                    }
                    // ends in same line end

                    if (selection.max.messageIndex != messageIndex) {
                        int lineIndex2 = lineIndex + 1;
                        for (; lineIndex2 < this->lines.size(); lineIndex2++) {
                            Line &line = this->lines[lineIndex2];
                            QRect rect = line.rect;

                            rect.setTop(std::max(0, rect.top()));
                            rect.setBottom(std::min(this->height, rect.bottom()));
                            rect.setLeft(this->elements[line.startIndex]->getRect().left());
                            rect.setRight(this->elements[line.endIndex - 1]->getRect().right());

                            painter.fillRect(rect, selectionColor);
                        }
                        returnAfter = true;
                    } else {
                        lineIndex++;
                        breakAfter = true;
                    }

                    break;
                }
                index += c;
            }

            QRect rect = line.rect;

            rect.setTop(std::max(0, rect.top()));
            rect.setBottom(std::min(this->height, rect.bottom()));
            rect.setLeft(x);
            rect.setRight(r);

            painter.fillRect(rect, selectionColor);

            if (returnAfter) {
                return;
            }

            if (breakAfter) {
                break;
            }
        }
    }

    // start in this message
    for (; lineIndex < this->lines.size(); lineIndex++) {
        Line &line = this->lines[lineIndex];
        index = line.startCharIndex;

        // just draw the garbage
        if (line.endCharIndex < /*=*/selection.max.charIndex) {
            QRect rect = line.rect;

            rect.setTop(std::max(0, rect.top()));
            rect.setBottom(std::min(this->height, rect.bottom()));
            rect.setLeft(this->elements[line.startIndex]->getRect().left());
            rect.setRight(this->elements[line.endIndex - 1]->getRect().right());

            painter.fillRect(rect, selectionColor);
            continue;
        }

        int r = this->elements[line.endIndex - 1]->getRect().right();

        for (int i = line.startIndex; i < line.endIndex; i++) {
            int c = this->elements[i]->getSelectionIndexCount();

            if (index + c > selection.max.charIndex) {
                r = this->elements[i]->getXFromIndex(selection.max.charIndex - index);
                break;
            }

            index += c;
        }

        QRect rect = line.rect;

        rect.setTop(std::max(0, rect.top()));
        rect.setBottom(std::min(this->height, rect.bottom()));
        rect.setLeft(this->elements[line.startIndex]->getRect().left());
        rect.setRight(r);

        painter.fillRect(rect, selectionColor);
        break;
    }
}

// selection
int MessageLayoutContainer::getSelectionIndex(QPoint point)
{
    if (this->elements.size() == 0) {
        return 0;
    }

    auto line = this->lines.begin();

    for (; line != this->lines.end(); line++) {
        if (line->rect.contains(point)) {
            break;
        }
    }

    int lineStart = line == this->lines.end() ? this->lines.back().startIndex : line->startIndex;
    if (line != this->lines.end()) {
        line++;
    }
    int lineEnd = line == this->lines.end() ? this->elements.size() : line->startIndex;

    int index = 0;

    for (int i = 0; i < lineEnd; i++) {
        // end of line
        if (i == lineEnd) {
            break;
        }

        // before line
        if (i < lineStart) {
            index += this->elements[i]->getSelectionIndexCount();
            continue;
        }

        // this is the word
        if (point.x() < this->elements[i]->getRect().right()) {
            index += this->elements[i]->getMouseOverIndex(point);
            break;
        }

        index += this->elements[i]->getSelectionIndexCount();
    }

    return index;
}

// fourtf: no idea if this is acurate LOL
int MessageLayoutContainer::getLastCharacterIndex() const
{
    if (this->lines.size() == 0) {
        return 0;
    }
    return this->lines.back().endCharIndex;
}

void MessageLayoutContainer::addSelectionText(QString &str, int from, int to)
{
    int index = 0;
    bool xd = true;

    for (std::unique_ptr<MessageLayoutElement> &ele : this->elements) {
        int c = ele->getSelectionIndexCount();

        if (xd) {
            if (index + c > from) {
                ele->addCopyTextToString(str, index - from, to - from);
                xd = false;
            }
        } else {
            if (index + c > from) {
                ele->addCopyTextToString(str, 0, index - to);
                break;
            } else {
                ele->addCopyTextToString(str);
            }
        }

        index += c;
    }
}

}  // namespace layouts
}  // namespace messages
}  // namespace chatterino
<|endoftext|>