transmission/gtk/MainWindow.cc

712 lines
24 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/******************************************************************************
* Copyright (c) Transmission authors and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*****************************************************************************/
#include <string.h> /* strlen() */
#include <glibmm/i18n.h>
#include <libtransmission/transmission.h>
#include <libtransmission/utils.h> /* tr_formatter_speed_KBps() */
#include "Actions.h"
#include "FilterBar.h"
#include "HigWorkarea.h"
#include "MainWindow.h"
#include "Prefs.h"
#include "PrefsDialog.h"
#include "Session.h"
#include "TorrentCellRenderer.h"
#include "Utils.h"
class MainWindow::Impl
{
public:
Impl(MainWindow& window, Glib::RefPtr<Gio::ActionGroup> const& actions, Glib::RefPtr<Session> const& core);
~Impl();
Glib::RefPtr<Gtk::TreeSelection> get_selection() const;
void refresh();
void prefsChanged(tr_quark key);
private:
Gtk::TreeView* makeview(Glib::RefPtr<Gtk::TreeModel> const& model);
Gtk::Menu* createOptionsMenu();
Gtk::Menu* createSpeedMenu(tr_direction dir);
Gtk::Menu* createRatioMenu();
void on_popup_menu(GdkEventButton* event);
void onSpeedToggled(Gtk::CheckMenuItem* check, tr_direction dir, bool enabled);
void onSpeedSet(tr_direction dir, int KBps);
void onRatioToggled(Gtk::CheckMenuItem* check, bool enabled);
void onRatioSet(double ratio);
void updateStats();
void updateSpeeds();
void syncAltSpeedButton();
void status_menu_toggled_cb(Gtk::CheckMenuItem* menu_item, std::string const& val);
void onOptionsClicked(Gtk::Button* button);
void onYinYangClicked(Gtk::Button* button);
void alt_speed_toggled_cb();
void onAltSpeedToggledIdle();
private:
MainWindow& window_;
Gtk::RadioMenuItem* speedlimit_on_item_[2] = { nullptr, nullptr };
Gtk::RadioMenuItem* speedlimit_off_item_[2] = { nullptr, nullptr };
Gtk::RadioMenuItem* ratio_on_item_ = nullptr;
Gtk::RadioMenuItem* ratio_off_item_ = nullptr;
Gtk::ScrolledWindow* scroll_ = nullptr;
Gtk::TreeView* view_ = nullptr;
Gtk::Toolbar* toolbar_ = nullptr;
FilterBar* filter_ = nullptr;
Gtk::Grid* status_ = nullptr;
Gtk::Menu* status_menu_;
Gtk::Label* ul_lb_ = nullptr;
Gtk::Label* dl_lb_ = nullptr;
Gtk::Label* stats_lb_ = nullptr;
Gtk::Image* alt_speed_image_ = nullptr;
Gtk::ToggleButton* alt_speed_button_ = nullptr;
Gtk::Menu* options_menu_ = nullptr;
Glib::RefPtr<Gtk::TreeSelection> selection_;
TorrentCellRenderer* renderer_ = nullptr;
Gtk::TreeViewColumn* column_ = nullptr;
Glib::RefPtr<Session> const core_;
sigc::connection pref_handler_id_;
Gtk::Menu* popup_menu_ = nullptr;
};
/***
****
***/
void MainWindow::Impl::on_popup_menu(GdkEventButton* event)
{
if (popup_menu_ == nullptr)
{
popup_menu_ = Gtk::make_managed<Gtk::Menu>(gtr_action_get_object<Gio::Menu>("main-window-popup"));
popup_menu_->attach_to_widget(window_);
}
popup_menu_->popup_at_pointer(reinterpret_cast<GdkEvent*>(event));
}
namespace
{
bool tree_view_search_equal_func(
Glib::RefPtr<Gtk::TreeModel> const& /*model*/,
int /*column*/,
Glib::ustring const& key,
Gtk::TreeModel::iterator const& iter)
{
auto const name = iter->get_value(torrent_cols.name_collated);
return name.find(key.lowercase()) == Glib::ustring::npos;
}
} // namespace
Gtk::TreeView* MainWindow::Impl::makeview(Glib::RefPtr<Gtk::TreeModel> const& model)
{
auto* view = Gtk::make_managed<Gtk::TreeView>();
view->set_search_column(torrent_cols.name_collated);
view->set_search_equal_func(&tree_view_search_equal_func);
view->set_headers_visible(false);
view->set_fixed_height_mode(true);
selection_ = view->get_selection();
column_ = new Gtk::TreeViewColumn();
column_->set_title(_("Torrent"));
column_->set_resizable(true);
column_->set_sizing(Gtk::TREE_VIEW_COLUMN_FIXED);
renderer_ = Gtk::make_managed<TorrentCellRenderer>();
column_->pack_start(*renderer_, false);
column_->add_attribute(renderer_->property_torrent(), torrent_cols.torrent);
column_->add_attribute(renderer_->property_piece_upload_speed(), torrent_cols.speed_up);
column_->add_attribute(renderer_->property_piece_download_speed(), torrent_cols.speed_down);
view->append_column(*column_);
renderer_->property_xpad() = GUI_PAD_SMALL;
renderer_->property_ypad() = GUI_PAD_SMALL;
selection_->set_mode(Gtk::SELECTION_MULTIPLE);
view->signal_popup_menu().connect_notify([this]() { on_popup_menu(nullptr); });
view->signal_button_press_event().connect(
[this, view](GdkEventButton* event)
{ return on_tree_view_button_pressed(view, event, sigc::mem_fun(*this, &Impl::on_popup_menu)); },
false);
view->signal_button_release_event().connect([view](GdkEventButton* event)
{ return on_tree_view_button_released(view, event); });
view->signal_row_activated().connect([](auto const& /*path*/, auto* /*column*/)
{ gtr_action_activate("show-torrent-properties"); });
view->set_model(model);
return view;
}
void MainWindow::Impl::prefsChanged(tr_quark const key)
{
switch (key)
{
case TR_KEY_compact_view:
renderer_->property_compact() = gtr_pref_flag_get(key);
/* since the cell size has changed, we need gtktreeview to revalidate
* its fixed-height mode values. Unfortunately there's not an API call
* for that, but it *does* revalidate when it thinks the style's been tweaked */
g_signal_emit_by_name(Glib::unwrap(view_), "style-updated", nullptr, nullptr);
break;
case TR_KEY_show_statusbar:
status_->set_visible(gtr_pref_flag_get(key));
break;
case TR_KEY_show_filterbar:
filter_->set_visible(gtr_pref_flag_get(key));
break;
case TR_KEY_show_toolbar:
toolbar_->set_visible(gtr_pref_flag_get(key));
break;
case TR_KEY_statusbar_stats:
refresh();
break;
case TR_KEY_alt_speed_enabled:
case TR_KEY_alt_speed_up:
case TR_KEY_alt_speed_down:
syncAltSpeedButton();
break;
default:
break;
}
}
MainWindow::Impl::~Impl()
{
pref_handler_id_.disconnect();
}
void MainWindow::Impl::onYinYangClicked(Gtk::Button* button)
{
status_menu_->popup_at_widget(button, Gdk::GRAVITY_NORTH_EAST, Gdk::GRAVITY_SOUTH_EAST, nullptr);
}
void MainWindow::Impl::status_menu_toggled_cb(Gtk::CheckMenuItem* menu_item, std::string const& val)
{
if (menu_item->get_active())
{
core_->set_pref(TR_KEY_statusbar_stats, val);
}
}
void MainWindow::Impl::syncAltSpeedButton()
{
bool const b = gtr_pref_flag_get(TR_KEY_alt_speed_enabled);
char const* const stock = b ? "alt-speed-on" : "alt-speed-off";
char u[32];
tr_formatter_speed_KBps(u, gtr_pref_int_get(TR_KEY_alt_speed_up), sizeof(u));
char d[32];
tr_formatter_speed_KBps(d, gtr_pref_int_get(TR_KEY_alt_speed_down), sizeof(d));
auto const str = b ? gtr_sprintf(_("Click to disable Alternative Speed Limits\n (%1$s down, %2$s up)"), d, u) :
gtr_sprintf(_("Click to enable Alternative Speed Limits\n (%1$s down, %2$s up)"), d, u);
alt_speed_button_->set_active(b);
alt_speed_image_->set_from_icon_name(stock, Gtk::BuiltinIconSize::ICON_SIZE_MENU);
alt_speed_button_->set_halign(Gtk::ALIGN_CENTER);
alt_speed_button_->set_valign(Gtk::ALIGN_CENTER);
alt_speed_button_->set_tooltip_text(str);
}
void MainWindow::Impl::alt_speed_toggled_cb()
{
core_->set_pref(TR_KEY_alt_speed_enabled, alt_speed_button_->get_active());
}
/***
**** FILTER
***/
void MainWindow::Impl::onAltSpeedToggledIdle()
{
core_->set_pref(TR_KEY_alt_speed_enabled, tr_sessionUsesAltSpeed(core_->get_session()));
}
/***
**** Speed limit menu
***/
void MainWindow::Impl::onSpeedToggled(Gtk::CheckMenuItem* check, tr_direction dir, bool enabled)
{
if (check->get_active())
{
core_->set_pref(dir == TR_UP ? TR_KEY_speed_limit_up_enabled : TR_KEY_speed_limit_down_enabled, enabled);
}
}
void MainWindow::Impl::onSpeedSet(tr_direction dir, int KBps)
{
core_->set_pref(dir == TR_UP ? TR_KEY_speed_limit_up : TR_KEY_speed_limit_down, KBps);
core_->set_pref(dir == TR_UP ? TR_KEY_speed_limit_up_enabled : TR_KEY_speed_limit_down_enabled, true);
}
Gtk::Menu* MainWindow::Impl::createSpeedMenu(tr_direction dir)
{
static int const speeds_KBps[] = { 5, 10, 20, 30, 40, 50, 75, 100, 150, 200, 250, 500, 750 };
auto* m = Gtk::make_managed<Gtk::Menu>();
Gtk::RadioButtonGroup group;
speedlimit_off_item_[dir] = Gtk::make_managed<Gtk::RadioMenuItem>(group, _("Unlimited"));
speedlimit_off_item_[dir]->signal_toggled().connect([this, dir]()
{ onSpeedToggled(speedlimit_off_item_[dir], dir, false); });
m->append(*speedlimit_off_item_[dir]);
speedlimit_on_item_[dir] = Gtk::make_managed<Gtk::RadioMenuItem>(group, "");
speedlimit_on_item_[dir]->signal_toggled().connect([this, dir]() { onSpeedToggled(speedlimit_on_item_[dir], dir, true); });
m->append(*speedlimit_on_item_[dir]);
m->append(*Gtk::make_managed<Gtk::SeparatorMenuItem>());
for (auto const speed : speeds_KBps)
{
char buf[128];
tr_formatter_speed_KBps(buf, speed, sizeof(buf));
auto* w = Gtk::make_managed<Gtk::MenuItem>(buf);
w->signal_activate().connect([this, dir, speed]() { onSpeedSet(dir, speed); });
m->append(*w);
}
return m;
}
/***
**** Speed limit menu
***/
void MainWindow::Impl::onRatioToggled(Gtk::CheckMenuItem* check, bool enabled)
{
if (check->get_active())
{
core_->set_pref(TR_KEY_ratio_limit_enabled, enabled);
}
}
void MainWindow::Impl::onRatioSet(double ratio)
{
core_->set_pref(TR_KEY_ratio_limit, ratio);
core_->set_pref(TR_KEY_ratio_limit_enabled, true);
}
Gtk::Menu* MainWindow::Impl::createRatioMenu()
{
static double const stockRatios[] = { 0.25, 0.5, 0.75, 1, 1.5, 2, 3 };
auto* m = Gtk::make_managed<Gtk::Menu>();
Gtk::RadioButtonGroup group;
ratio_off_item_ = Gtk::make_managed<Gtk::RadioMenuItem>(group, _("Seed Forever"));
ratio_off_item_->signal_toggled().connect([this]() { onRatioToggled(ratio_off_item_, false); });
m->append(*ratio_off_item_);
ratio_on_item_ = Gtk::make_managed<Gtk::RadioMenuItem>(group, "");
ratio_on_item_->signal_toggled().connect([this]() { onRatioToggled(ratio_on_item_, true); });
m->append(*ratio_on_item_);
m->append(*Gtk::make_managed<Gtk::SeparatorMenuItem>());
for (auto const ratio : stockRatios)
{
auto* w = Gtk::make_managed<Gtk::MenuItem>(tr_strlratio(ratio));
w->signal_activate().connect([this, ratio]() { onRatioSet(ratio); });
m->append(*w);
}
return m;
}
/***
**** Option menu
***/
Gtk::Menu* MainWindow::Impl::createOptionsMenu()
{
Gtk::MenuItem* m;
auto* top = Gtk::make_managed<Gtk::Menu>();
m = Gtk::make_managed<Gtk::MenuItem>(_("Limit Download Speed"));
m->set_submenu(*createSpeedMenu(TR_DOWN));
top->append(*m);
m = Gtk::make_managed<Gtk::MenuItem>(_("Limit Upload Speed"));
m->set_submenu(*createSpeedMenu(TR_UP));
top->append(*m);
top->append(*Gtk::make_managed<Gtk::SeparatorMenuItem>());
m = Gtk::make_managed<Gtk::MenuItem>(_("Stop Seeding at Ratio"));
m->set_submenu(*createRatioMenu());
top->append(*m);
top->show_all();
return top;
}
void MainWindow::Impl::onOptionsClicked(Gtk::Button* button)
{
char buf1[512];
tr_formatter_speed_KBps(buf1, gtr_pref_int_get(TR_KEY_speed_limit_down), sizeof(buf1));
gtr_label_set_text(*static_cast<Gtk::Label*>(speedlimit_on_item_[TR_DOWN]->get_child()), buf1);
(gtr_pref_flag_get(TR_KEY_speed_limit_down_enabled) ? speedlimit_on_item_[TR_DOWN] : speedlimit_off_item_[TR_DOWN])
->set_active(true);
tr_formatter_speed_KBps(buf1, gtr_pref_int_get(TR_KEY_speed_limit_up), sizeof(buf1));
gtr_label_set_text(*static_cast<Gtk::Label*>(speedlimit_on_item_[TR_UP]->get_child()), buf1);
(gtr_pref_flag_get(TR_KEY_speed_limit_up_enabled) ? speedlimit_on_item_[TR_UP] : speedlimit_off_item_[TR_UP])
->set_active(true);
gtr_label_set_text(
*static_cast<Gtk::Label*>(ratio_on_item_->get_child()),
gtr_sprintf(_("Stop at Ratio (%s)"), tr_strlratio(gtr_pref_double_get(TR_KEY_ratio_limit))));
(gtr_pref_flag_get(TR_KEY_ratio_limit_enabled) ? ratio_on_item_ : ratio_off_item_)->set_active(true);
options_menu_->popup_at_widget(button, Gdk::GRAVITY_NORTH_WEST, Gdk::GRAVITY_SOUTH_WEST, nullptr);
}
/***
**** PUBLIC
***/
std::unique_ptr<MainWindow> MainWindow::create(
Gtk::Application& app,
Glib::RefPtr<Gio::ActionGroup> const& actions,
Glib::RefPtr<Session> const& core)
{
return std::unique_ptr<MainWindow>(new MainWindow(app, actions, core));
}
MainWindow::MainWindow(Gtk::Application& app, Glib::RefPtr<Gio::ActionGroup> const& actions, Glib::RefPtr<Session> const& core)
: Gtk::ApplicationWindow()
, impl_(std::make_unique<Impl>(*this, actions, core))
{
app.add_window(*this);
}
MainWindow::~MainWindow() = default;
MainWindow::Impl::Impl(MainWindow& window, Glib::RefPtr<Gio::ActionGroup> const& actions, Glib::RefPtr<Session> const& core)
: window_(window)
, core_(core)
{
static struct
{
char const* val;
char const* i18n;
} const stats_modes[] = {
{ "total-ratio", N_("Total Ratio") },
{ "session-ratio", N_("Session Ratio") },
{ "total-transfer", N_("Total Transfer") },
{ "session-transfer", N_("Session Transfer") },
};
/* make the window */
window.set_title(Glib::get_application_name());
window.set_role("tr-main");
window.set_default_size(gtr_pref_int_get(TR_KEY_main_window_width), gtr_pref_int_get(TR_KEY_main_window_height));
window.move(gtr_pref_int_get(TR_KEY_main_window_x), gtr_pref_int_get(TR_KEY_main_window_y));
if (gtr_pref_flag_get(TR_KEY_main_window_is_maximized))
{
window.maximize();
}
window.insert_action_group("win", actions);
/* Add style provider to the window. */
/* Please move it to separate .css file if youre adding more styles here. */
auto const* style = ".tr-workarea.frame {border-left-width: 0; border-right-width: 0; border-radius: 0;}";
auto css_provider = Gtk::CssProvider::create();
css_provider->load_from_data(style);
Gtk::StyleContext::add_provider_for_screen(
Gdk::Screen::get_default(),
css_provider,
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
/* window's main container */
auto* vbox = Gtk::make_managed<Gtk::Box>(Gtk::ORIENTATION_VERTICAL, 0);
window.add(*vbox);
/* toolbar */
toolbar_ = gtr_action_get_widget<Gtk::Toolbar>("main-window-toolbar");
toolbar_->get_style_context()->add_class(GTK_STYLE_CLASS_PRIMARY_TOOLBAR);
/* filter */
filter_ = Gtk::make_managed<FilterBar>(core_->get_session(), core_->get_model());
filter_->set_border_width(GUI_PAD_SMALL);
/* status menu */
status_menu_ = Gtk::make_managed<Gtk::Menu>();
Gtk::RadioButtonGroup stats_modes_group;
auto const pch = gtr_pref_string_get(TR_KEY_statusbar_stats);
for (auto const& mode : stats_modes)
{
auto* w = Gtk::make_managed<Gtk::RadioMenuItem>(stats_modes_group, _(mode.i18n));
w->set_active(mode.val == pch);
w->signal_toggled().connect([this, w, val = mode.val]() { status_menu_toggled_cb(w, val); });
status_menu_->append(*w);
w->show();
}
/**
*** Statusbar
**/
status_ = Gtk::make_managed<Gtk::Grid>();
status_->set_orientation(Gtk::ORIENTATION_HORIZONTAL);
status_->set_border_width(GUI_PAD_SMALL);
/* gear */
auto* gear_button = Gtk::make_managed<Gtk::Button>();
gear_button->add(*Gtk::make_managed<Gtk::Image>("preferences-other", Gtk::ICON_SIZE_MENU));
gear_button->set_tooltip_text(_("Options"));
gear_button->set_relief(Gtk::RELIEF_NONE);
options_menu_ = createOptionsMenu();
gear_button->signal_clicked().connect([this, gear_button]() { onOptionsClicked(gear_button); });
status_->add(*gear_button);
/* turtle */
alt_speed_image_ = Gtk::make_managed<Gtk::Image>();
alt_speed_button_ = Gtk::make_managed<Gtk::ToggleButton>();
alt_speed_button_->set_image(*alt_speed_image_);
alt_speed_button_->set_relief(Gtk::RELIEF_NONE);
alt_speed_button_->signal_toggled().connect(sigc::mem_fun(*this, &Impl::alt_speed_toggled_cb));
status_->add(*alt_speed_button_);
/* spacer */
auto* w = Gtk::make_managed<Gtk::Fixed>();
w->set_hexpand(true);
status_->add(*w);
/* download */
dl_lb_ = Gtk::make_managed<Gtk::Label>();
dl_lb_->set_single_line_mode(true);
status_->add(*dl_lb_);
/* upload */
ul_lb_ = Gtk::make_managed<Gtk::Label>();
ul_lb_->set_margin_start(GUI_PAD);
ul_lb_->set_single_line_mode(true);
status_->add(*ul_lb_);
/* ratio */
stats_lb_ = Gtk::make_managed<Gtk::Label>();
stats_lb_->set_margin_start(GUI_PAD_BIG);
stats_lb_->set_single_line_mode(true);
status_->add(*stats_lb_);
/* ratio selector */
auto* ratio_button = Gtk::make_managed<Gtk::Button>();
ratio_button->set_tooltip_text(_("Statistics"));
ratio_button->add(*Gtk::make_managed<Gtk::Image>("view-statistics", Gtk::ICON_SIZE_MENU));
ratio_button->set_relief(Gtk::RELIEF_NONE);
ratio_button->signal_clicked().connect([this, ratio_button]() { onYinYangClicked(ratio_button); });
status_->add(*ratio_button);
/**
*** Workarea
**/
view_ = makeview(filter_->get_filter_model());
scroll_ = Gtk::make_managed<Gtk::ScrolledWindow>();
scroll_->set_policy(Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
scroll_->set_shadow_type(Gtk::SHADOW_OUT);
scroll_->get_style_context()->add_class("tr-workarea");
scroll_->add(*view_);
/* lay out the widgets */
vbox->pack_start(*toolbar_, false, false);
vbox->pack_start(*filter_, false, false);
vbox->pack_start(*scroll_, true, true);
vbox->pack_start(*status_, false, false);
{
/* this is to determine the maximum width/height for the label */
int width = 0;
int height = 0;
auto const pango_layout = ul_lb_->create_pango_layout("999.99 kB/s");
pango_layout->get_pixel_size(width, height);
ul_lb_->set_size_request(width, height);
dl_lb_->set_size_request(width, height);
ul_lb_->set_halign(Gtk::ALIGN_END);
ul_lb_->set_valign(Gtk::ALIGN_CENTER);
dl_lb_->set_halign(Gtk::ALIGN_END);
dl_lb_->set_valign(Gtk::ALIGN_CENTER);
}
/* show all but the window */
vbox->show_all();
/* listen for prefs changes that affect the window */
prefsChanged(TR_KEY_compact_view);
prefsChanged(TR_KEY_show_filterbar);
prefsChanged(TR_KEY_show_statusbar);
prefsChanged(TR_KEY_statusbar_stats);
prefsChanged(TR_KEY_show_toolbar);
prefsChanged(TR_KEY_alt_speed_enabled);
pref_handler_id_ = core_->signal_prefs_changed().connect(sigc::mem_fun(*this, &Impl::prefsChanged));
tr_sessionSetAltSpeedFunc(
core_->get_session(),
[](tr_session* /*s*/, bool /*isEnabled*/, bool /*byUser*/, void* p)
{ Glib::signal_idle().connect_once([p]() { static_cast<Impl*>(p)->onAltSpeedToggledIdle(); }); },
this);
refresh();
}
void MainWindow::Impl::updateStats()
{
Glib::ustring buf;
tr_session_stats stats;
auto const* const session = core_->get_session();
/* update the stats */
auto const pch = gtr_pref_string_get(TR_KEY_statusbar_stats);
if (pch == "session-ratio")
{
tr_sessionGetStats(session, &stats);
buf = gtr_sprintf(_("Ratio: %s"), tr_strlratio(stats.ratio));
}
else if (pch == "session-transfer")
{
tr_sessionGetStats(session, &stats);
/* Translators: "size|" is here for disambiguation. Please remove it from your translation.
%1$s is the size of the data we've downloaded
%2$s is the size of the data we've uploaded */
buf = gtr_sprintf(Q_("Down: %1$s, Up: %2$s"), tr_strlsize(stats.downloadedBytes), tr_strlsize(stats.uploadedBytes));
}
else if (pch == "total-transfer")
{
tr_sessionGetCumulativeStats(session, &stats);
/* Translators: "size|" is here for disambiguation. Please remove it from your translation.
%1$s is the size of the data we've downloaded
%2$s is the size of the data we've uploaded */
buf = gtr_sprintf(
Q_("size|Down: %1$s, Up: %2$s"),
tr_strlsize(stats.downloadedBytes),
tr_strlsize(stats.uploadedBytes));
}
else /* default is total-ratio */
{
tr_sessionGetCumulativeStats(session, &stats);
buf = gtr_sprintf(_("Ratio: %s"), tr_strlratio(stats.ratio));
}
stats_lb_->set_text(buf);
}
void MainWindow::Impl::updateSpeeds()
{
auto const* const session = core_->get_session();
if (session != nullptr)
{
char speed_str[128];
double upSpeed = 0;
double downSpeed = 0;
int upCount = 0;
int downCount = 0;
auto const model = core_->get_model();
for (auto const& row : model->children())
{
upSpeed += row.get_value(torrent_cols.speed_up);
upCount += row.get_value(torrent_cols.active_peers_up);
downSpeed += row.get_value(torrent_cols.speed_down);
downCount += row.get_value(torrent_cols.active_peers_down);
}
tr_formatter_speed_KBps(speed_str, downSpeed, sizeof(speed_str));
dl_lb_->set_text(gtr_sprintf("%s %s", speed_str, gtr_get_unicode_string(GTR_UNICODE_DOWN)));
dl_lb_->set_visible(downCount > 0);
tr_formatter_speed_KBps(speed_str, upSpeed, sizeof(speed_str));
ul_lb_->set_text(gtr_sprintf("%s %s", speed_str, gtr_get_unicode_string(GTR_UNICODE_UP)));
ul_lb_->set_visible(downCount > 0 || upCount > 0);
}
}
void MainWindow::refresh()
{
impl_->refresh();
}
void MainWindow::Impl::refresh()
{
if (core_ != nullptr && core_->get_session() != nullptr)
{
updateSpeeds();
updateStats();
}
}
Glib::RefPtr<Gtk::TreeSelection> MainWindow::get_selection() const
{
return impl_->get_selection();
}
Glib::RefPtr<Gtk::TreeSelection> MainWindow::Impl::get_selection() const
{
return selection_;
}
void MainWindow::set_busy(bool isBusy)
{
if (get_realized())
{
auto const display = get_display();
auto const cursor = isBusy ? Gdk::Cursor::create(display, Gdk::WATCH) : Glib::RefPtr<Gdk::Cursor>();
get_window()->set_cursor(cursor);
display->flush();
}
}