Switch to Gtk::Builder for all UI in GTK client (#3781)

* Move CSS definitions to resources

* Add Gtk::Builder helpers

* Switch StatsDialog to Gtk::Builder

* Switch RelocateDialog to Gtk::Builder

* Switch OptionsDialog to Gtk::Builder

* Switch MakeDialog to Gtk::Builder

* Switch FilterBar to Gtk::Builder

* Switch MainWindow to Gtk::Builder

* Switch MessageLogWindow to Gtk::Builder

* Switch DetailsDialog to Gtk::Builder

* Switch PrefsDialog to Gtk::Builder

* Fixup translatable strings

Since this branch was brewing for a while, changes happened in the meantime.
This commit is contained in:
Mike Gelfand 2022-09-08 01:25:04 +03:00 committed by GitHub
parent c000311b80
commit b41501eeb8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 7058 additions and 1691 deletions

130
gtk/AddTrackerDialog.ui Normal file
View File

@ -0,0 +1,130 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface>
<requires lib="gtk+" version="3.24"/>
<object class="GtkDialog" id="AddTrackerDialog">
<property name="can-focus">False</property>
<property name="border-width">6</property>
<property name="modal">True</property>
<property name="type-hint">dialog</property>
<child internal-child="vbox">
<object class="GtkBox" id="dialog_layout">
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child internal-child="action_area">
<object class="GtkButtonBox" id="dialog_buttons">
<property name="can-focus">False</property>
<property name="layout-style">end</property>
<child>
<object class="GtkButton" id="cancel_button">
<property name="label" translatable="yes">_Cancel</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="use-underline">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="open_button">
<property name="label" translatable="yes">_Add</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="use-underline">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox" id="dialog_content_layout">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel" id="url_section_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Tracker</property>
<property name="xalign">0</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<!-- n-columns=2 n-rows=1 -->
<object class="GtkGrid" id="url_section_layout">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="margin-start">18</property>
<property name="row-spacing">6</property>
<property name="column-spacing">12</property>
<child>
<object class="GtkLabel" id="url_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">_Announce URL:</property>
<property name="use-underline">True</property>
<property name="mnemonic-widget">url_entry</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="url_entry">
<property name="width-request">400</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="hexpand">True</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">0</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
<action-widgets>
<action-widget response="-6">cancel_button</action-widget>
<action-widget response="-3">open_button</action-widget>
</action-widgets>
</object>
</interface>

View File

@ -5,12 +5,13 @@
#include <algorithm>
#include <cstdlib> // exit()
#include <ctime>
#include <iterator> // for std::back_inserter
#include <iterator> // std::back_inserter
#include <map>
#include <memory>
#include <sstream>
#include <string>
#include <thread>
#include <tuple>
#include <utility>
#include <vector>
@ -34,7 +35,8 @@
#include "Application.h"
#include "DetailsDialog.h"
#include "Dialogs.h"
#include "HigWorkarea.h"
#include "FilterBar.h"
#include "HigWorkarea.h" // GUI_PAD, GUI_PAD_BIG
#include "MainWindow.h"
#include "MakeDialog.h"
#include "MessageLogWindow.h"
@ -535,6 +537,16 @@ void Application::Impl::on_startup()
Gtk::IconTheme::get_default()->add_resource_path(gtr_get_full_resource_path("icons"s));
Gtk::Window::set_default_icon_name(AppIconName);
/* Add style provider to the window. */
auto css_provider = Gtk::CssProvider::create();
css_provider->load_from_resource(gtr_get_full_resource_path("transmission-ui.css"));
Gtk::StyleContext::add_provider_for_screen(
Gdk::Screen::get_default(),
css_provider,
GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
std::ignore = FilterBar();
tr_session* session;
::signal(SIGINT, signal_handler);

View File

@ -20,6 +20,22 @@ endif()
find_program(APPSTREAM appstreamcli)
set(${PROJECT_NAME}_UI_FILES
AddTrackerDialog.ui
DetailsDialog.ui
EditTrackersDialog.ui
FilterBar.ui
MainWindow.ui
MakeDialog.ui
MakeProgressDialog.ui
MessageLogWindow.ui
OptionsDialog.ui
PrefsDialog.ui
RelocateDialog.ui
StatsDialog.ui
TorrentUrlChooserDialog.ui
)
add_custom_command(
OUTPUT
${CMAKE_CURRENT_BINARY_DIR}/transmission-resources.c
@ -46,6 +62,7 @@ add_custom_command(
icons/turtle-symbolic.svg
transmission-ui.xml
transmission.gresource.xml
${${PROJECT_NAME}_UI_FILES}
WORKING_DIRECTORY
${CMAKE_CURRENT_SOURCE_DIR}
)

View File

@ -5,13 +5,13 @@
#include <algorithm>
#include <array>
#include <limits.h> /* INT_MAX */
#include <limits.h> // INT_MAX
#include <memory>
#include <numeric>
#include <sstream>
#include <stddef.h>
#include <stdio.h> /* sscanf() */
#include <stdlib.h> /* abort() */
#include <stdio.h> // sscanf()
#include <stdlib.h> // abort()
#include <string>
#include <string_view>
#include <unordered_map>
@ -28,9 +28,9 @@
#include "Actions.h"
#include "DetailsDialog.h"
#include "FaviconCache.h" /* gtr_get_favicon() */
#include "FaviconCache.h" // gtr_get_favicon()
#include "FileList.h"
#include "HigWorkarea.h"
#include "HigWorkarea.h" // GUI_PAD, GUI_PAD_BIG, GUI_PAD_SMALL
#include "Prefs.h"
#include "PrefsDialog.h"
#include "Session.h"
@ -41,18 +41,19 @@ using namespace std::literals;
class DetailsDialog::Impl
{
public:
Impl(DetailsDialog& dialog, Glib::RefPtr<Session> const& core);
Impl(DetailsDialog& dialog, Glib::RefPtr<Gtk::Builder> const& builder, Glib::RefPtr<Session> const& core);
~Impl();
TR_DISABLE_COPY_MOVE(Impl)
void set_torrents(std::vector<tr_torrent_id_t> const& torrent_ids);
void refresh();
private:
Gtk::Widget* info_page_new();
Gtk::Widget* peer_page_new();
Gtk::Widget* tracker_page_new();
Gtk::Widget* options_page_new();
void info_page_init(Glib::RefPtr<Gtk::Builder> const& builder);
void peer_page_init(Glib::RefPtr<Gtk::Builder> const& builder);
void tracker_page_init(Glib::RefPtr<Gtk::Builder> const& builder);
void options_page_init(Glib::RefPtr<Gtk::Builder> const& builder);
void on_details_window_size_allocated(Gtk::Allocation& alloc);
@ -68,15 +69,10 @@ private:
void onScrapeToggled();
void onBackupToggled();
void on_add_tracker_response(int response, std::shared_ptr<Gtk::Dialog>& dialog);
void on_edit_trackers_response(int response, std::shared_ptr<Gtk::Dialog>& dialog);
void torrent_set_bool(tr_quark key, bool value);
void torrent_set_int(tr_quark key, int value);
void torrent_set_real(tr_quark key, double value);
void refresh();
void refreshInfo(std::vector<tr_torrent*> const& torrents);
void refreshPeers(std::vector<tr_torrent*> const& torrents);
void refreshTracker(std::vector<tr_torrent*> const& torrents);
@ -92,6 +88,7 @@ private:
private:
DetailsDialog& dialog_;
Glib::RefPtr<Session> const core_;
Gtk::CheckButton* honor_limits_check_ = nullptr;
Gtk::CheckButton* up_limited_check_ = nullptr;
@ -157,7 +154,6 @@ private:
Gtk::Label* file_label_ = nullptr;
std::vector<tr_torrent_id_t> ids_;
Glib::RefPtr<Session> const core_;
sigc::connection periodic_refresh_tag_;
Glib::Quark const TORRENT_ID_KEY = Glib::Quark("tr-torrent-id-key");
@ -457,97 +453,69 @@ void DetailsDialog::Impl::torrent_set_real(tr_quark key, double value)
tr_variantClear(&top);
}
Gtk::Widget* DetailsDialog::Impl::options_page_new()
void DetailsDialog::Impl::options_page_init(Glib::RefPtr<Gtk::Builder> const& /*builder*/)
{
guint row;
row = 0;
auto* t = Gtk::make_managed<HigWorkarea>();
t->add_section_title(row, _("Speed"));
honor_limits_check_ = t->add_wide_checkbutton(row, _("Honor global _limits"), false);
honor_limits_check_tag_ = honor_limits_check_->signal_toggled().connect(
[this]() { torrent_set_bool(TR_KEY_honorsSessionLimits, honor_limits_check_->get_active()); });
down_limited_check_ = Gtk::make_managed<Gtk::CheckButton>(
fmt::format(_("Limit _download speed ({speed_units}):"), fmt::arg("speed_units", speed_K_str)),
true);
down_limited_check_->set_active(false);
down_limited_check_->set_label(fmt::format(down_limited_check_->get_label().raw(), fmt::arg("speed_units", speed_K_str)));
down_limited_check_tag_ = down_limited_check_->signal_toggled().connect(
[this]() { torrent_set_bool(TR_KEY_downloadLimited, down_limited_check_->get_active()); });
down_limit_spin_ = Gtk::make_managed<Gtk::SpinButton>(Gtk::Adjustment::create(0, 0, INT_MAX, 5));
down_limit_spin_->set_adjustment(Gtk::Adjustment::create(0, 0, INT_MAX, 5));
down_limit_spin_tag_ = down_limit_spin_->signal_value_changed().connect(
[this]() { torrent_set_int(TR_KEY_downloadLimit, down_limit_spin_->get_value_as_int()); });
t->add_row_w(row, *down_limited_check_, *down_limit_spin_);
up_limited_check_ = Gtk::make_managed<Gtk::CheckButton>(
fmt::format(_("Limit _upload speed ({speed_units}):"), fmt::arg("speed_units", speed_K_str)),
true);
up_limited_check_->set_label(fmt::format(up_limited_check_->get_label().raw(), fmt::arg("speed_units", speed_K_str)));
up_limited_check_tag_ = up_limited_check_->signal_toggled().connect(
[this]() { torrent_set_bool(TR_KEY_uploadLimited, up_limited_check_->get_active()); });
up_limit_sping_ = Gtk::make_managed<Gtk::SpinButton>(Gtk::Adjustment::create(0, 0, INT_MAX, 5));
up_limit_sping_->set_adjustment(Gtk::Adjustment::create(0, 0, INT_MAX, 5));
up_limit_spin_tag_ = up_limit_sping_->signal_value_changed().connect(
[this]() { torrent_set_int(TR_KEY_uploadLimit, up_limit_sping_->get_value_as_int()); });
t->add_row_w(row, *up_limited_check_, *up_limit_sping_);
bandwidth_combo_ = gtr_priority_combo_new();
gtr_priority_combo_init(*bandwidth_combo_);
bandwidth_combo_tag_ = bandwidth_combo_->signal_changed().connect(
[this]() { torrent_set_int(TR_KEY_bandwidthPriority, gtr_priority_combo_get_value(*bandwidth_combo_)); });
t->add_row(row, _("Torrent _priority:"), *bandwidth_combo_);
t->add_section_divider(row);
t->add_section_title(row, _("Seeding Limits"));
auto* h1 = Gtk::make_managed<Gtk::Box>(Gtk::ORIENTATION_HORIZONTAL, GUI_PAD);
ratio_combo_ = gtr_combo_box_new_enum({
{ _("Use global settings"), TR_RATIOLIMIT_GLOBAL },
{ _("Seed regardless of ratio"), TR_RATIOLIMIT_UNLIMITED },
{ _("Stop seeding at ratio:"), TR_RATIOLIMIT_SINGLE },
});
gtr_combo_box_set_enum(
*ratio_combo_,
{
{ _("Use global settings"), TR_RATIOLIMIT_GLOBAL },
{ _("Seed regardless of ratio"), TR_RATIOLIMIT_UNLIMITED },
{ _("Stop seeding at ratio:"), TR_RATIOLIMIT_SINGLE },
});
ratio_combo_tag_ = ratio_combo_->signal_changed().connect(
[this]()
{
torrent_set_int(TR_KEY_seedRatioMode, gtr_combo_box_get_active_enum(*ratio_combo_));
refresh();
});
h1->pack_start(*ratio_combo_, true, true, 0);
ratio_spin_ = Gtk::make_managed<Gtk::SpinButton>(Gtk::Adjustment::create(0, 0, 1000, .05));
ratio_spin_->set_adjustment(Gtk::Adjustment::create(0, 0, 1000, .05));
ratio_spin_->set_width_chars(7);
ratio_spin_tag_ = ratio_spin_->signal_value_changed().connect(
[this]() { torrent_set_real(TR_KEY_seedRatioLimit, ratio_spin_->get_value()); });
h1->pack_start(*ratio_spin_, false, false, 0);
t->add_row(row, _("_Ratio:"), *h1);
auto* h2 = Gtk::make_managed<Gtk::Box>(Gtk::ORIENTATION_HORIZONTAL, GUI_PAD);
idle_combo_ = gtr_combo_box_new_enum({
{ _("Use global settings"), TR_IDLELIMIT_GLOBAL },
{ _("Seed regardless of activity"), TR_IDLELIMIT_UNLIMITED },
{ _("Stop seeding if idle for N minutes:"), TR_IDLELIMIT_SINGLE },
});
gtr_combo_box_set_enum(
*idle_combo_,
{
{ _("Use global settings"), TR_IDLELIMIT_GLOBAL },
{ _("Seed regardless of activity"), TR_IDLELIMIT_UNLIMITED },
{ _("Stop seeding if idle for N minutes:"), TR_IDLELIMIT_SINGLE },
});
idle_combo_tag_ = idle_combo_->signal_changed().connect(
[this]()
{
torrent_set_int(TR_KEY_seedIdleMode, gtr_combo_box_get_active_enum(*idle_combo_));
refresh();
});
h2->pack_start(*idle_combo_, true, true, 0);
idle_spin_ = Gtk::make_managed<Gtk::SpinButton>(Gtk::Adjustment::create(1, 1, 40320, 5));
idle_spin_->set_adjustment(Gtk::Adjustment::create(1, 1, 40320, 5));
idle_spin_tag_ = idle_spin_->signal_value_changed().connect(
[this]() { torrent_set_int(TR_KEY_seedIdleLimit, idle_spin_->get_value_as_int()); });
h2->pack_start(*idle_spin_, false, false, 0);
t->add_row(row, _("_Idle:"), *h2);
t->add_section_divider(row);
t->add_section_title(row, _("Peer Connections"));
max_peers_spin_ = Gtk::make_managed<Gtk::SpinButton>(Gtk::Adjustment::create(1, 1, 3000, 5));
t->add_row(row, _("_Maximum peers:"), *max_peers_spin_, max_peers_spin_);
max_peers_spin_->set_adjustment(Gtk::Adjustment::create(1, 1, 3000, 5));
max_peers_spin_tag_ = max_peers_spin_->signal_value_changed().connect(
[this]() { torrent_set_int(TR_KEY_peer_limit, max_peers_spin_->get_value_as_int()); });
return t;
}
/****
@ -1084,111 +1052,11 @@ void DetailsDialog::Impl::refreshInfo(std::vector<tr_torrent*> const& torrents)
last_activity_lb_->set_text(str);
}
Gtk::Widget* DetailsDialog::Impl::info_page_new()
void DetailsDialog::Impl::info_page_init(Glib::RefPtr<Gtk::Builder> const& builder)
{
guint row = 0;
auto* t = Gtk::make_managed<HigWorkarea>();
t->add_section_title(row, _("Activity"));
/* size */
size_lb_ = Gtk::make_managed<Gtk::Label>();
size_lb_->set_single_line_mode(true);
t->add_row(row, _("Torrent size:"), *size_lb_);
/* have */
have_lb_ = Gtk::make_managed<Gtk::Label>();
have_lb_->set_single_line_mode(true);
t->add_row(row, _("Have:"), *have_lb_);
/* uploaded */
ul_lb_ = Gtk::make_managed<Gtk::Label>();
ul_lb_->set_single_line_mode(true);
t->add_row(row, _("Uploaded:"), *ul_lb_);
/* downloaded */
dl_lb_ = Gtk::make_managed<Gtk::Label>();
dl_lb_->set_single_line_mode(true);
t->add_row(row, _("Downloaded:"), *dl_lb_);
/* state */
state_lb_ = Gtk::make_managed<Gtk::Label>();
state_lb_->set_single_line_mode(true);
t->add_row(row, _("State:"), *state_lb_);
/* running for */
date_started_lb_ = Gtk::make_managed<Gtk::Label>();
date_started_lb_->set_single_line_mode(true);
t->add_row(row, _("Running time:"), *date_started_lb_);
/* eta */
eta_lb_ = Gtk::make_managed<Gtk::Label>();
eta_lb_->set_single_line_mode(true);
t->add_row(row, _("Remaining time:"), *eta_lb_);
/* last activity */
last_activity_lb_ = Gtk::make_managed<Gtk::Label>();
last_activity_lb_->set_single_line_mode(true);
t->add_row(row, _("Last activity:"), *last_activity_lb_);
/* error */
error_lb_ = Gtk::make_managed<Gtk::Label>();
error_lb_->set_selectable(true);
error_lb_->set_ellipsize(Pango::ELLIPSIZE_END);
error_lb_->set_line_wrap(true);
error_lb_->set_lines(10);
t->add_row(row, _("Error:"), *error_lb_);
/* details */
t->add_section_divider(row);
t->add_section_title(row, _("Details"));
/* destination */
destination_lb_ = Gtk::make_managed<Gtk::Label>();
destination_lb_->set_selectable(true);
destination_lb_->set_ellipsize(Pango::ELLIPSIZE_END);
t->add_row(row, _("Location:"), *destination_lb_);
/* hash */
hash_lb_ = Gtk::make_managed<Gtk::Label>();
hash_lb_->set_selectable(true);
hash_lb_->set_ellipsize(Pango::ELLIPSIZE_END);
t->add_row(row, _("Hash:"), *hash_lb_);
/* privacy */
privacy_lb_ = Gtk::make_managed<Gtk::Label>();
privacy_lb_->set_single_line_mode(true);
t->add_row(row, _("Privacy:"), *privacy_lb_);
/* origins */
origin_lb_ = Gtk::make_managed<Gtk::Label>();
origin_lb_->set_selectable(true);
origin_lb_->set_ellipsize(Pango::ELLIPSIZE_END);
t->add_row(row, _("Origin:"), *origin_lb_);
/* added */
added_lb_ = Gtk::make_managed<Gtk::Label>();
added_lb_->set_single_line_mode(true);
t->add_row(row, _("Added:"), *added_lb_);
/* comment */
comment_buffer_ = Gtk::TextBuffer::create();
auto* tw = Gtk::make_managed<Gtk::TextView>(comment_buffer_);
tw->set_wrap_mode(Gtk::WRAP_WORD);
tw->set_editable(false);
auto* sw = Gtk::make_managed<Gtk::ScrolledWindow>();
sw->set_size_request(350, 36);
sw->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
sw->add(*tw);
auto* fr = Gtk::make_managed<Gtk::Frame>();
fr->set_shadow_type(Gtk::SHADOW_IN);
fr->add(*sw);
auto* w = t->add_tall_row(row, _("Comment:"), *fr);
w->set_halign(Gtk::ALIGN_START);
w->set_valign(Gtk::ALIGN_START);
t->add_section_divider(row);
return t;
auto* tw = gtr_get_widget<Gtk::TextView>(builder, "comment_value_view");
tw->set_buffer(comment_buffer_);
}
/****
@ -1823,12 +1691,13 @@ void DetailsDialog::Impl::onMorePeerInfoToggled()
setPeerViewColumns(peer_view_);
}
Gtk::Widget* DetailsDialog::Impl::peer_page_new()
void DetailsDialog::Impl::peer_page_init(Glib::RefPtr<Gtk::Builder> const& builder)
{
/* webseeds */
webseed_store_ = Gtk::ListStore::create(webseed_cols);
auto* v = Gtk::make_managed<Gtk::TreeView>(webseed_store_);
auto* v = gtr_get_widget<Gtk::TreeView>(builder, "webseeds_view");
v->set_model(webseed_store_);
v->signal_button_release_event().connect([v](GdkEventButton* event) { return on_tree_view_button_released(v, event); });
{
@ -1849,44 +1718,22 @@ Gtk::Widget* DetailsDialog::Impl::peer_page_new()
v->append_column(*c);
}
webseed_view_ = Gtk::make_managed<Gtk::ScrolledWindow>();
webseed_view_->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
webseed_view_->set_shadow_type(Gtk::SHADOW_IN);
webseed_view_->add(*v);
/* peers */
peer_store_ = Gtk::ListStore::create(peer_cols);
auto m = Gtk::TreeModelSort::create(peer_store_);
m->set_sort_column(peer_cols.progress, Gtk::SORT_DESCENDING);
peer_view_ = Gtk::make_managed<Gtk::TreeView>(m);
peer_view_->set_has_tooltip(true);
peer_view_->set_model(m);
peer_view_->set_has_tooltip(true);
peer_view_->signal_query_tooltip().connect(sigc::mem_fun(*this, &Impl::onPeerViewQueryTooltip));
peer_view_->signal_button_release_event().connect([this](GdkEventButton* event)
{ return on_tree_view_button_released(peer_view_, event); });
setPeerViewColumns(peer_view_);
auto* sw = Gtk::make_managed<Gtk::ScrolledWindow>();
sw->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
sw->set_shadow_type(Gtk::SHADOW_IN);
sw->add(*peer_view_);
auto* vbox = Gtk::make_managed<Gtk::Box>(Gtk::ORIENTATION_VERTICAL, GUI_PAD);
vbox->set_border_width(GUI_PAD_BIG);
auto* v2 = Gtk::make_managed<Gtk::Paned>(Gtk::ORIENTATION_VERTICAL);
v2->add(*webseed_view_);
v2->add(*sw);
vbox->pack_start(*v2, true, true);
more_peer_details_check_ = Gtk::make_managed<Gtk::CheckButton>(_("Show _more details"), true);
more_peer_details_check_->set_active(gtr_pref_flag_get(TR_KEY_show_extra_peer_details));
more_peer_details_check_->signal_toggled().connect(sigc::mem_fun(*this, &Impl::onMorePeerInfoToggled));
vbox->pack_start(*more_peer_details_check_, false, false);
return vbox;
}
/****
@ -2267,25 +2114,81 @@ void DetailsDialog::Impl::onBackupToggled()
refresh();
}
void DetailsDialog::Impl::on_edit_trackers_response(int response, std::shared_ptr<Gtk::Dialog>& dialog)
namespace
{
class EditTrackersDialog : public Gtk::Dialog
{
public:
EditTrackersDialog(
BaseObjectType* cast_item,
Glib::RefPtr<Gtk::Builder> const& builder,
DetailsDialog& parent,
Glib::RefPtr<Session> core,
tr_torrent const* torrent);
TR_DISABLE_COPY_MOVE(EditTrackersDialog)
static std::unique_ptr<EditTrackersDialog> create(DetailsDialog& parent, Glib::RefPtr<Session> core, tr_torrent const* tor);
private:
void on_response(int response);
private:
DetailsDialog& parent_;
Glib::RefPtr<Session> const core_;
tr_torrent_id_t const torrent_id_;
Gtk::TextView* const urls_view_;
};
EditTrackersDialog::EditTrackersDialog(
BaseObjectType* cast_item,
Glib::RefPtr<Gtk::Builder> const& builder,
DetailsDialog& parent,
Glib::RefPtr<Session> core,
tr_torrent const* torrent)
: Gtk::Dialog(cast_item)
, parent_(parent)
, core_(core)
, torrent_id_(tr_torrentId(torrent))
, urls_view_(gtr_get_widget<Gtk::TextView>(builder, "urls_view"))
{
set_title(fmt::format(_("{torrent_name} - Edit Trackers"), fmt::arg("torrent_name", tr_torrentName(torrent))));
set_transient_for(parent);
urls_view_->get_buffer()->set_text(tr_torrentGetTrackerList(torrent));
signal_response().connect([this](int response) { on_response(response); });
}
std::unique_ptr<EditTrackersDialog> EditTrackersDialog::create(
DetailsDialog& parent,
Glib::RefPtr<Session> core,
tr_torrent const* torrent)
{
auto const builder = Gtk::Builder::create_from_resource(gtr_get_full_resource_path("EditTrackersDialog.ui"));
return std::unique_ptr<EditTrackersDialog>(
gtr_get_widget_derived<EditTrackersDialog>(builder, "EditTrackersDialog", parent, core, torrent));
}
void EditTrackersDialog::on_response(int response)
{
bool do_destroy = true;
if (response == Gtk::RESPONSE_ACCEPT)
{
auto const torrent_id = GPOINTER_TO_INT(dialog->get_data(TORRENT_ID_KEY));
auto const* const text_buffer = static_cast<Gtk::TextBuffer*>(dialog->get_data(TEXT_BUFFER_KEY));
auto const text_buffer = urls_view_->get_buffer();
if (auto* const tor = core_->find_torrent(torrent_id); tor != nullptr)
if (auto* const tor = core_->find_torrent(torrent_id_); tor != nullptr)
{
if (tr_torrentSetTrackerList(tor, text_buffer->get_text(false).c_str()))
{
refresh();
parent_.refresh();
}
else
{
Gtk::MessageDialog
w(*dialog, _("List contains invalid URLs"), false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
w(*this, _("List contains invalid URLs"), false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_CLOSE, true);
w.set_secondary_text(_("Please correct the errors and try again."));
w.run();
@ -2296,63 +2199,18 @@ void DetailsDialog::Impl::on_edit_trackers_response(int response, std::shared_pt
if (do_destroy)
{
dialog.reset();
hide();
}
}
} // namespace
void DetailsDialog::Impl::on_edit_trackers()
{
tr_torrent const* tor = tracker_list_get_current_torrent();
if (tor != nullptr)
if (auto const* const tor = tracker_list_get_current_torrent(); tor != nullptr)
{
guint row;
auto const torrent_id = tr_torrentId(tor);
auto d = std::make_shared<Gtk::Dialog>(
fmt::format(_("{torrent_name} - Edit Trackers"), fmt::arg("torrent_name", tr_torrentName(tor))),
dialog_,
Gtk::DIALOG_MODAL | Gtk::DIALOG_DESTROY_WITH_PARENT);
d->add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
d->add_button(_("_Save"), Gtk::RESPONSE_ACCEPT);
d->signal_response().connect([this, d](int response) mutable { on_edit_trackers_response(response, d); });
row = 0;
auto* t = Gtk::make_managed<HigWorkarea>();
t->add_section_title(row, _("Tracker Announce URLs"));
auto* l = Gtk::make_managed<Gtk::Label>();
l->set_markup(
_("To add a backup URL, add it on the next line after a primary URL.\n"
"To add a new primary URL, add it after a blank line."));
l->set_justify(Gtk::JUSTIFY_LEFT);
l->set_halign(Gtk::ALIGN_START);
l->set_valign(Gtk::ALIGN_CENTER);
t->add_wide_control(row, *l);
auto* w = Gtk::make_managed<Gtk::TextView>();
w->get_buffer()->set_text(tr_torrentGetTrackerList(tor));
auto* fr = Gtk::make_managed<Gtk::Frame>();
fr->set_shadow_type(Gtk::SHADOW_IN);
auto* sw = Gtk::make_managed<Gtk::ScrolledWindow>();
sw->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
sw->add(*w);
fr->add(*sw);
fr->set_size_request(500U, 166U);
t->add_wide_tall_control(row, *fr);
l = Gtk::make_managed<Gtk::Label>();
l->set_markup(_("Also see Default Public Trackers in Edit > Preferences > Network"));
l->set_justify(Gtk::JUSTIFY_LEFT);
l->set_halign(Gtk::ALIGN_START);
l->set_valign(Gtk::ALIGN_CENTER);
t->add_wide_control(row, *l);
gtr_dialog_set_content(*d, *t);
d->set_data(TORRENT_ID_KEY, GINT_TO_POINTER(torrent_id));
d->set_data(TEXT_BUFFER_KEY, gtr_get_ptr(w->get_buffer()));
auto d = std::shared_ptr<EditTrackersDialog>(EditTrackersDialog::create(dialog_, core_, tor));
d->signal_hide().connect([d]() mutable { d.reset(); });
d->show();
}
}
@ -2367,15 +2225,70 @@ void DetailsDialog::Impl::on_tracker_list_selection_changed()
edit_trackers_button_->set_sensitive(tor != nullptr);
}
void DetailsDialog::Impl::on_add_tracker_response(int response, std::shared_ptr<Gtk::Dialog>& dialog)
namespace
{
class AddTrackerDialog : public Gtk::Dialog
{
public:
AddTrackerDialog(
BaseObjectType* cast_item,
Glib::RefPtr<Gtk::Builder> const& builder,
DetailsDialog& parent,
Glib::RefPtr<Session> core,
tr_torrent const* torrent);
TR_DISABLE_COPY_MOVE(AddTrackerDialog)
static std::unique_ptr<AddTrackerDialog> create(DetailsDialog& parent, Glib::RefPtr<Session> core, tr_torrent const* tor);
private:
void on_response(int response);
private:
DetailsDialog& parent_;
Glib::RefPtr<Session> const core_;
tr_torrent_id_t const torrent_id_;
Gtk::Entry* const url_entry_;
};
AddTrackerDialog::AddTrackerDialog(
BaseObjectType* cast_item,
Glib::RefPtr<Gtk::Builder> const& builder,
DetailsDialog& parent,
Glib::RefPtr<Session> core,
tr_torrent const* torrent)
: Gtk::Dialog(cast_item)
, parent_(parent)
, core_(core)
, torrent_id_(tr_torrentId(torrent))
, url_entry_(gtr_get_widget<Gtk::Entry>(builder, "url_entry"))
{
set_title(fmt::format(_("{torrent_name} - Add Tracker"), fmt::arg("torrent_name", tr_torrentName(torrent))));
set_transient_for(parent);
gtr_paste_clipboard_url_into_entry(*url_entry_);
signal_response().connect([this](int response) { on_response(response); });
}
std::unique_ptr<AddTrackerDialog> AddTrackerDialog::create(
DetailsDialog& parent,
Glib::RefPtr<Session> core,
tr_torrent const* torrent)
{
auto const builder = Gtk::Builder::create_from_resource(gtr_get_full_resource_path("AddTrackerDialog.ui"));
return std::unique_ptr<AddTrackerDialog>(
gtr_get_widget_derived<AddTrackerDialog>(builder, "AddTrackerDialog", parent, core, torrent));
}
void AddTrackerDialog::on_response(int response)
{
bool destroy = true;
if (response == Gtk::RESPONSE_ACCEPT)
{
auto const* const e = static_cast<Gtk::Entry*>(dialog->get_data(URL_ENTRY_KEY));
auto const torrent_id = GPOINTER_TO_INT(dialog->get_data(TORRENT_ID_KEY));
auto const url = gtr_str_strip(e->get_text());
auto const url = gtr_str_strip(url_entry_->get_text());
if (!url.empty())
{
@ -2388,18 +2301,18 @@ void DetailsDialog::Impl::on_add_tracker_response(int response, std::shared_ptr<
tr_variantInitDict(&top, 2);
tr_variantDictAddStrView(&top, TR_KEY_method, "torrent-set"sv);
args = tr_variantDictAddDict(&top, TR_KEY_arguments, 2);
tr_variantDictAddInt(args, TR_KEY_id, torrent_id);
tr_variantDictAddInt(args, TR_KEY_id, torrent_id_);
trackers = tr_variantDictAddList(args, TR_KEY_trackerAdd, 1);
tr_variantListAddStr(trackers, url.raw());
core_->exec(&top);
refresh();
parent_.refresh();
tr_variantClear(&top);
}
else
{
gtr_unrecognized_url_dialog(*dialog, url);
gtr_unrecognized_url_dialog(*this, url);
destroy = false;
}
}
@ -2407,38 +2320,19 @@ void DetailsDialog::Impl::on_add_tracker_response(int response, std::shared_ptr<
if (destroy)
{
dialog.reset();
hide();
}
}
} // namespace
void DetailsDialog::Impl::on_tracker_list_add_button_clicked()
{
tr_torrent const* tor = tracker_list_get_current_torrent();
if (tor != nullptr)
if (auto const* const tor = tracker_list_get_current_torrent(); tor != nullptr)
{
guint row;
auto w = std::make_shared<Gtk::Dialog>(
fmt::format(_("{torrent_name} - Add Tracker"), fmt::arg("torrent_name", tr_torrentName(tor))),
dialog_,
Gtk::DIALOG_DESTROY_WITH_PARENT);
w->add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
w->add_button(_("_Add"), Gtk::RESPONSE_ACCEPT);
w->signal_response().connect([this, w](int response) mutable { on_add_tracker_response(response, w); });
row = 0;
auto* t = Gtk::make_managed<HigWorkarea>();
t->add_section_title(row, _("Tracker"));
auto* e = Gtk::make_managed<Gtk::Entry>();
e->set_size_request(400, -1);
gtr_paste_clipboard_url_into_entry(*e);
w->set_data(URL_ENTRY_KEY, e);
w->set_data(TORRENT_ID_KEY, GINT_TO_POINTER(tr_torrentId(tor)));
t->add_row(row, _("_Announce URL:"), *e);
gtr_dialog_set_content(*w, *t);
w->show_all();
auto d = std::shared_ptr<AddTrackerDialog>(AddTrackerDialog::create(dialog_, core_, tor));
d->signal_hide().connect([d]() mutable { d.reset(); });
d->show();
}
}
@ -2469,22 +2363,16 @@ void DetailsDialog::Impl::on_tracker_list_remove_button_clicked()
}
}
Gtk::Widget* DetailsDialog::Impl::tracker_page_new()
void DetailsDialog::Impl::tracker_page_init(Glib::RefPtr<Gtk::Builder> const& /*builder*/)
{
int const pad = (GUI_PAD + GUI_PAD_BIG) / 2;
auto* vbox = Gtk::make_managed<Gtk::Box>(Gtk::ORIENTATION_VERTICAL, GUI_PAD);
vbox->set_border_width(GUI_PAD_BIG);
tracker_store_ = Gtk::ListStore::create(tracker_cols);
trackers_filtered_ = Gtk::TreeModelFilter::create(tracker_store_);
trackers_filtered_->set_visible_func(sigc::mem_fun(*this, &Impl::trackerVisibleFunc));
auto* hbox = Gtk::make_managed<Gtk::Box>(Gtk::ORIENTATION_HORIZONTAL, GUI_PAD_BIG);
tracker_view_ = Gtk::make_managed<Gtk::TreeView>(trackers_filtered_);
tracker_view_->set_headers_visible(false);
tracker_view_->set_model(trackers_filtered_);
tracker_view_->signal_button_press_event().connect([this](GdkEventButton* event)
{ return on_tree_view_button_pressed(tracker_view_, event); });
tracker_view_->signal_button_release_event().connect([this](GdkEventButton* event)
@ -2516,44 +2404,15 @@ Gtk::Widget* DetailsDialog::Impl::tracker_page_new()
c->add_attribute(r->property_markup(), tracker_cols.text);
}
auto* sw = Gtk::make_managed<Gtk::ScrolledWindow>();
sw->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
sw->add(*tracker_view_);
auto* w = Gtk::make_managed<Gtk::Frame>();
w->set_shadow_type(Gtk::SHADOW_IN);
w->add(*sw);
hbox->pack_start(*w, true, true);
auto* v = Gtk::make_managed<Gtk::Box>(Gtk::ORIENTATION_VERTICAL, GUI_PAD);
add_tracker_button_ = Gtk::make_managed<Gtk::Button>(_("_Add"), true);
add_tracker_button_->signal_clicked().connect(sigc::mem_fun(*this, &Impl::on_tracker_list_add_button_clicked));
v->pack_start(*add_tracker_button_, false, false);
edit_trackers_button_ = Gtk::make_managed<Gtk::Button>(_("_Edit"), true);
edit_trackers_button_->signal_clicked().connect(sigc::mem_fun(*this, &Impl::on_edit_trackers));
v->pack_start(*edit_trackers_button_, false, false);
remove_tracker_button_ = Gtk::make_managed<Gtk::Button>(_("_Remove"), true);
remove_tracker_button_->signal_clicked().connect(sigc::mem_fun(*this, &Impl::on_tracker_list_remove_button_clicked));
v->pack_start(*remove_tracker_button_, false, false);
hbox->pack_start(*v, false, false);
vbox->pack_start(*hbox, true, true);
scrape_check_ = Gtk::make_managed<Gtk::CheckButton>(_("Show _more details"), true);
scrape_check_->set_active(gtr_pref_flag_get(TR_KEY_show_tracker_scrapes));
scrape_check_->signal_toggled().connect(sigc::mem_fun(*this, &Impl::onScrapeToggled));
vbox->pack_start(*scrape_check_, false, false);
all_check_ = Gtk::make_managed<Gtk::CheckButton>(_("Show _backup trackers"), true);
all_check_->set_active(gtr_pref_flag_get(TR_KEY_show_backup_trackers));
all_check_->signal_toggled().connect(sigc::mem_fun(*this, &Impl::onBackupToggled));
vbox->pack_start(*all_check_, false, false);
return vbox;
}
/****
@ -2592,54 +2451,79 @@ DetailsDialog::Impl::~Impl()
std::unique_ptr<DetailsDialog> DetailsDialog::create(Gtk::Window& parent, Glib::RefPtr<Session> const& core)
{
return std::unique_ptr<DetailsDialog>(new DetailsDialog(parent, core));
auto const builder = Gtk::Builder::create_from_resource(gtr_get_full_resource_path("DetailsDialog.ui"));
return std::unique_ptr<DetailsDialog>(gtr_get_widget_derived<DetailsDialog>(builder, "DetailsDialog", parent, core));
}
DetailsDialog::DetailsDialog(Gtk::Window& parent, Glib::RefPtr<Session> const& core)
: Gtk::Dialog({}, parent)
, impl_(std::make_unique<Impl>(*this, core))
DetailsDialog::DetailsDialog(
BaseObjectType* cast_item,
Glib::RefPtr<Gtk::Builder> const& builder,
Gtk::Window& parent,
Glib::RefPtr<Session> const& core)
: Gtk::Dialog(cast_item)
, impl_(std::make_unique<Impl>(*this, builder, core))
{
set_transient_for(parent);
}
DetailsDialog::~DetailsDialog() = default;
DetailsDialog::Impl::Impl(DetailsDialog& dialog, Glib::RefPtr<Session> const& core)
DetailsDialog::Impl::Impl(DetailsDialog& dialog, Glib::RefPtr<Gtk::Builder> const& builder, Glib::RefPtr<Session> const& core)
: dialog_(dialog)
, core_(core)
, honor_limits_check_(gtr_get_widget<Gtk::CheckButton>(builder, "honor_limits_check"))
, up_limited_check_(gtr_get_widget<Gtk::CheckButton>(builder, "upload_limit_check"))
, up_limit_sping_(gtr_get_widget<Gtk::SpinButton>(builder, "upload_limit_spin"))
, down_limited_check_(gtr_get_widget<Gtk::CheckButton>(builder, "download_limit_check"))
, down_limit_spin_(gtr_get_widget<Gtk::SpinButton>(builder, "download_limit_spin"))
, bandwidth_combo_(gtr_get_widget<Gtk::ComboBox>(builder, "priority_combo"))
, ratio_combo_(gtr_get_widget<Gtk::ComboBox>(builder, "ratio_limit_combo"))
, ratio_spin_(gtr_get_widget<Gtk::SpinButton>(builder, "ratio_limit_spin"))
, idle_combo_(gtr_get_widget<Gtk::ComboBox>(builder, "idle_limit_combo"))
, idle_spin_(gtr_get_widget<Gtk::SpinButton>(builder, "idle_limit_spin"))
, max_peers_spin_(gtr_get_widget<Gtk::SpinButton>(builder, "max_peers_spin"))
, added_lb_(gtr_get_widget<Gtk::Label>(builder, "added_value_label"))
, size_lb_(gtr_get_widget<Gtk::Label>(builder, "torrent_size_value_label"))
, state_lb_(gtr_get_widget<Gtk::Label>(builder, "state_value_label"))
, have_lb_(gtr_get_widget<Gtk::Label>(builder, "have_value_label"))
, dl_lb_(gtr_get_widget<Gtk::Label>(builder, "downloaded_value_label"))
, ul_lb_(gtr_get_widget<Gtk::Label>(builder, "uploaded_value_label"))
, error_lb_(gtr_get_widget<Gtk::Label>(builder, "error_value_label"))
, date_started_lb_(gtr_get_widget<Gtk::Label>(builder, "running_time_value_label"))
, eta_lb_(gtr_get_widget<Gtk::Label>(builder, "remaining_time_value_label"))
, last_activity_lb_(gtr_get_widget<Gtk::Label>(builder, "last_activity_value_label"))
, hash_lb_(gtr_get_widget<Gtk::Label>(builder, "hash_value_label"))
, privacy_lb_(gtr_get_widget<Gtk::Label>(builder, "privacy_value_label"))
, origin_lb_(gtr_get_widget<Gtk::Label>(builder, "origin_value_label"))
, destination_lb_(gtr_get_widget<Gtk::Label>(builder, "location_value_label"))
, webseed_view_(gtr_get_widget<Gtk::ScrolledWindow>(builder, "webseeds_view_scroll"))
, peer_view_(gtr_get_widget<Gtk::TreeView>(builder, "peers_view"))
, more_peer_details_check_(gtr_get_widget<Gtk::CheckButton>(builder, "more_peer_details_check"))
, add_tracker_button_(gtr_get_widget<Gtk::Button>(builder, "add_tracker_button"))
, edit_trackers_button_(gtr_get_widget<Gtk::Button>(builder, "edit_tracker_button"))
, remove_tracker_button_(gtr_get_widget<Gtk::Button>(builder, "remove_tracker_button"))
, tracker_view_(gtr_get_widget<Gtk::TreeView>(builder, "trackers_view"))
, scrape_check_(gtr_get_widget<Gtk::CheckButton>(builder, "more_tracker_details_check"))
, all_check_(gtr_get_widget<Gtk::CheckButton>(builder, "backup_trackers_check"))
, file_list_(gtr_get_widget_derived<FileList>(builder, "files_view_scroll", "files_view", core, 0))
, file_label_(gtr_get_widget<Gtk::Label>(builder, "files_label"))
{
/* create the dialog */
dialog_.add_button(_("_Close"), Gtk::RESPONSE_CLOSE);
dialog_.set_role("tr-info");
/* return saved window size */
dialog_.resize((int)gtr_pref_int_get(TR_KEY_details_window_width), (int)gtr_pref_int_get(TR_KEY_details_window_height));
dialog_.signal_size_allocate().connect(sigc::mem_fun(*this, &Impl::on_details_window_size_allocated));
dialog_.signal_response().connect(sigc::hide<0>(sigc::mem_fun(dialog_, &DetailsDialog::hide)));
dialog_.set_border_width(GUI_PAD);
auto* n = Gtk::make_managed<Gtk::Notebook>();
n->set_border_width(GUI_PAD);
info_page_init(builder);
peer_page_init(builder);
tracker_page_init(builder);
options_page_init(builder);
n->append_page(*info_page_new(), _("Information"));
n->append_page(*peer_page_new(), _("Peers"));
n->append_page(*tracker_page_new(), _("Trackers"));
auto* v = Gtk::make_managed<Gtk::Box>(Gtk::ORIENTATION_VERTICAL);
file_list_ = Gtk::make_managed<FileList>(core, 0);
file_label_ = Gtk::make_managed<Gtk::Label>(_("File listing not available for combined torrent properties"));
v->pack_start(*file_list_, true, true, 0);
v->pack_start(*file_label_, true, true, 0);
v->set_border_width(GUI_PAD_BIG);
n->append_page(*v, _("Files"));
n->append_page(*options_page_new(), _("Options"));
gtr_dialog_set_content(dialog_, *n);
periodic_refresh_tag_ = Glib::signal_timeout().connect_seconds(
[this]() { return refresh(), true; },
SECONDARY_WINDOW_REFRESH_INTERVAL_SECONDS);
auto* const n = gtr_get_widget<Gtk::Notebook>(builder, "dialog_pages");
n->set_current_page(last_page_);
last_page_tag_ = n->signal_switch_page().connect([](Widget*, guint page) { DetailsDialog::Impl::last_page_ = page; });
}
@ -2649,6 +2533,11 @@ void DetailsDialog::set_torrents(std::vector<tr_torrent_id_t> const& ids)
impl_->set_torrents(ids);
}
void DetailsDialog::refresh()
{
impl_->refresh();
}
void DetailsDialog::Impl::set_torrents(std::vector<tr_torrent_id_t> const& ids)
{
Glib::ustring title;

View File

@ -18,6 +18,11 @@ class Session;
class DetailsDialog : public Gtk::Dialog
{
public:
DetailsDialog(
BaseObjectType* cast_item,
Glib::RefPtr<Gtk::Builder> const& builder,
Gtk::Window& parent,
Glib::RefPtr<Session> const& core);
~DetailsDialog() override;
TR_DISABLE_COPY_MOVE(DetailsDialog)
@ -25,9 +30,7 @@ public:
static std::unique_ptr<DetailsDialog> create(Gtk::Window& parent, Glib::RefPtr<Session> const& core);
void set_torrents(std::vector<tr_torrent_id_t> const& torrent_ids);
protected:
DetailsDialog(Gtk::Window& parent, Glib::RefPtr<Session> const& core);
void refresh();
private:
class Impl;

1224
gtk/DetailsDialog.ui Normal file

File diff suppressed because it is too large Load Diff

153
gtk/EditTrackersDialog.ui Normal file
View File

@ -0,0 +1,153 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface domain="transmission-gtk">
<requires lib="gtk+" version="3.24"/>
<object class="GtkDialog" id="EditTrackersDialog">
<property name="can-focus">False</property>
<property name="border-width">6</property>
<property name="modal">True</property>
<property name="type-hint">dialog</property>
<child internal-child="vbox">
<object class="GtkBox" id="dialog_layout">
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child internal-child="action_area">
<object class="GtkButtonBox" id="dialog_buttons">
<property name="can-focus">False</property>
<property name="layout-style">end</property>
<child>
<object class="GtkButton" id="cancel_button">
<property name="label" translatable="yes">_Cancel</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="use-underline">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="save_button">
<property name="label" translatable="yes">_Save</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="use-underline">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox" id="dialog_content_layout">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel" id="urls_section_title">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Tracker Announce URLs</property>
<property name="xalign">0</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<!-- n-columns=2 n-rows=3 -->
<object class="GtkGrid" id="urls_section_layout">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="margin-start">18</property>
<property name="row-spacing">6</property>
<property name="column-spacing">12</property>
<child>
<object class="GtkLabel" id="urls_section_top_comment_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">To add a backup URL, add it on the next line after a primary URL.
To add a new primary URL, add it after a blank line.</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">0</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow" id="urls_view_scroll">
<property name="width-request">500</property>
<property name="height-request">166</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="shadow-type">in</property>
<child>
<object class="GtkTextView" id="urls_view">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="accepts-tab">False</property>
</object>
</child>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">1</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="urls_section_bottom_comment_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Also see Default Public Trackers in Edit &gt; Preferences &gt; Network</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">2</property>
<property name="width">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
<action-widgets>
<action-widget response="-6">cancel_button</action-widget>
<action-widget response="-3">save_button</action-widget>
</action-widgets>
</object>
</interface>

View File

@ -4,7 +4,7 @@
// License text can be found in the licenses/ folder.
#include <algorithm>
#include <climits> /* INT_MAX */
#include <climits> // INT_MAX
#include <cstddef>
#include <list>
#include <memory>
@ -22,7 +22,7 @@
#include <libtransmission/utils.h>
#include "FileList.h"
#include "HigWorkarea.h"
#include "HigWorkarea.h" // GUI_PAD, GUI_PAD_BIG
#include "IconCache.h"
#include "PrefsDialog.h"
#include "Session.h"
@ -81,7 +81,14 @@ FileModelColumns const file_cols;
class FileList::Impl
{
public:
Impl(FileList& widget, Glib::RefPtr<Session> const& core, tr_torrent_id_t tor_id);
Impl(FileList& widget, Gtk::TreeView* view, Glib::RefPtr<Session> const& core, tr_torrent_id_t torrent_id);
Impl(FileList& widget, Glib::RefPtr<Session> const& core, tr_torrent_id_t torrent_id);
Impl(
FileList& widget,
Glib::RefPtr<Gtk::Builder> const& builder,
Glib::ustring const& view_name,
Glib::RefPtr<Session> const& core,
tr_torrent_id_t torrent_id);
~Impl();
TR_DISABLE_COPY_MOVE(Impl)
@ -838,13 +845,23 @@ FileList::FileList(Glib::RefPtr<Session> const& core, tr_torrent_id_t tor_id)
{
}
FileList::Impl::Impl(FileList& widget, Glib::RefPtr<Session> const& core, tr_torrent_id_t tor_id)
FileList::FileList(
BaseObjectType* cast_item,
Glib::RefPtr<Gtk::Builder> const& builder,
Glib::ustring const& view_name,
Glib::RefPtr<Session> const& core,
tr_torrent_id_t torrent_id)
: Gtk::ScrolledWindow(cast_item)
, impl_(std::make_unique<Impl>(*this, builder, view_name, core, torrent_id))
{
}
FileList::Impl::Impl(FileList& widget, Gtk::TreeView* view, Glib::RefPtr<Session> const& core, tr_torrent_id_t torrent_id)
: widget_(widget)
, core_(core)
, view_(view)
{
/* create the view */
view_ = Gtk::make_managed<Gtk::TreeView>();
view_->set_border_width(GUI_PAD_BIG);
view_->signal_button_press_event().connect(sigc::mem_fun(*this, &Impl::onViewButtonPressed), false);
view_->signal_row_activated().connect(sigc::mem_fun(*this, &Impl::onRowActivated));
view_->signal_button_release_event().connect([this](GdkEventButton* event)
@ -951,13 +968,29 @@ FileList::Impl::Impl(FileList& widget, Glib::RefPtr<Session> const& core, tr_tor
/* add tooltip to tree */
view_->set_tooltip_column(file_cols.label_esc.index());
set_torrent(torrent_id);
}
FileList::Impl::Impl(FileList& widget, Glib::RefPtr<Session> const& core, tr_torrent_id_t torrent_id)
: Impl(widget, Gtk::make_managed<Gtk::TreeView>(), core, torrent_id)
{
view_->set_border_width(GUI_PAD_BIG);
/* create the scrolled window and stick the view in it */
widget_.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
widget_.set_shadow_type(Gtk::SHADOW_IN);
widget_.add(*view_);
widget_.set_size_request(-1, 200);
}
set_torrent(tor_id);
FileList::Impl::Impl(
FileList& widget,
Glib::RefPtr<Gtk::Builder> const& builder,
Glib::ustring const& view_name,
Glib::RefPtr<Session> const& core,
tr_torrent_id_t torrent_id)
: Impl(widget, gtr_get_widget<Gtk::TreeView>(builder, view_name), core, torrent_id)
{
}
FileList::~FileList() = default;

View File

@ -17,6 +17,12 @@ class FileList : public Gtk::ScrolledWindow
{
public:
FileList(Glib::RefPtr<Session> const& core, tr_torrent_id_t torrent_id);
FileList(
BaseObjectType* cast_item,
Glib::RefPtr<Gtk::Builder> const& builder,
Glib::ustring const& view_name,
Glib::RefPtr<Session> const& core,
tr_torrent_id_t torrent_id);
~FileList() override;
TR_DISABLE_COPY_MOVE(FileList)

View File

@ -12,10 +12,10 @@
#include <glibmm.h>
#include <glibmm/i18n.h>
#include "FaviconCache.h" /* gtr_get_favicon() */
#include "FaviconCache.h" // gtr_get_favicon()
#include "FilterBar.h"
#include "HigWorkarea.h" /* GUI_PAD */
#include "Session.h" /* MC_TORRENT */
#include "HigWorkarea.h" // GUI_PAD
#include "Session.h" // torrent_cols
#include "Utils.h"
namespace
@ -38,8 +38,11 @@ public:
Glib::RefPtr<Gtk::TreeModel> get_filter_model() const;
private:
Gtk::ComboBox* activity_combo_box_new(Glib::RefPtr<Gtk::TreeModel> const& tmodel);
Gtk::ComboBox* tracker_combo_box_new(Glib::RefPtr<Gtk::TreeModel> const& tmodel);
template<typename T>
T* get_template_child(char const* name) const;
void activity_combo_box_init(Gtk::ComboBox* combo, Glib::RefPtr<Gtk::TreeModel> const& tmodel);
void tracker_combo_box_init(Gtk::ComboBox* combo, Glib::RefPtr<Gtk::TreeModel> const& tmodel);
void update_count_label_idle();
@ -332,31 +335,32 @@ Gtk::CellRendererText* number_renderer_new()
} // namespace
Gtk::ComboBox* FilterBar::Impl::tracker_combo_box_new(Glib::RefPtr<Gtk::TreeModel> const& tmodel)
void FilterBar::Impl::tracker_combo_box_init(Gtk::ComboBox* combo, Glib::RefPtr<Gtk::TreeModel> const& tmodel)
{
/* create the tracker combobox */
auto const cat_model = tracker_filter_model_new(tmodel);
auto* c = Gtk::make_managed<Gtk::ComboBox>(static_cast<Glib::RefPtr<Gtk::TreeModel> const&>(cat_model));
c->set_row_separator_func(&is_it_a_separator);
c->set_active(0);
combo->set_model(cat_model);
combo->set_row_separator_func(&is_it_a_separator);
combo->set_active(0);
{
auto* r = Gtk::make_managed<Gtk::CellRendererPixbuf>();
c->pack_start(*r, false);
c->set_cell_data_func(*r, [r](auto const& iter) { render_pixbuf_func(r, iter); });
c->add_attribute(r->property_pixbuf(), tracker_filter_cols.pixbuf);
combo->pack_start(*r, false);
combo->set_cell_data_func(*r, [r](auto const& iter) { render_pixbuf_func(r, iter); });
combo->add_attribute(r->property_pixbuf(), tracker_filter_cols.pixbuf);
}
{
auto* r = Gtk::make_managed<Gtk::CellRendererText>();
c->pack_start(*r, false);
c->add_attribute(r->property_text(), tracker_filter_cols.displayname);
combo->pack_start(*r, false);
combo->add_attribute(r->property_text(), tracker_filter_cols.displayname);
}
{
auto* r = number_renderer_new();
c->pack_end(*r, true);
c->set_cell_data_func(*r, [r](auto const& iter) { render_number_func(r, iter); });
combo->pack_end(*r, true);
combo->set_cell_data_func(*r, [r](auto const& iter) { render_number_func(r, iter); });
}
torrent_model_row_changed_tag_ = tmodel->signal_row_changed().connect(
@ -365,8 +369,6 @@ Gtk::ComboBox* FilterBar::Impl::tracker_combo_box_new(Glib::RefPtr<Gtk::TreeMode
[cat_model](auto const& /*path*/, auto const& /*iter*/) { tracker_model_update_idle(cat_model); });
torrent_model_row_deleted_cb_tag_ = tmodel->signal_row_deleted().connect( //
[cat_model](auto const& /*path*/) { tracker_model_update_idle(cat_model); });
return c;
}
namespace
@ -557,30 +559,31 @@ void activity_model_update_idle(Glib::RefPtr<Gtk::ListStore> const& activity_mod
} // namespace
Gtk::ComboBox* FilterBar::Impl::activity_combo_box_new(Glib::RefPtr<Gtk::TreeModel> const& tmodel)
void FilterBar::Impl::activity_combo_box_init(Gtk::ComboBox* combo, Glib::RefPtr<Gtk::TreeModel> const& tmodel)
{
auto const activity_model = activity_filter_model_new(tmodel);
auto* c = Gtk::make_managed<Gtk::ComboBox>(static_cast<Glib::RefPtr<Gtk::TreeModel> const&>(activity_model));
c->set_row_separator_func(&activity_is_it_a_separator);
c->set_active(0);
combo->set_model(activity_model);
combo->set_row_separator_func(&activity_is_it_a_separator);
combo->set_active(0);
{
auto* r = Gtk::make_managed<Gtk::CellRendererPixbuf>();
c->pack_start(*r, false);
c->add_attribute(r->property_icon_name(), activity_filter_cols.icon_name);
c->set_cell_data_func(*r, [r](auto const& iter) { render_activity_pixbuf_func(r, iter); });
combo->pack_start(*r, false);
combo->add_attribute(r->property_icon_name(), activity_filter_cols.icon_name);
combo->set_cell_data_func(*r, [r](auto const& iter) { render_activity_pixbuf_func(r, iter); });
}
{
auto* r = Gtk::make_managed<Gtk::CellRendererText>();
c->pack_start(*r, true);
c->add_attribute(r->property_text(), activity_filter_cols.name);
combo->pack_start(*r, true);
combo->add_attribute(r->property_text(), activity_filter_cols.name);
}
{
auto* r = number_renderer_new();
c->pack_end(*r, true);
c->set_cell_data_func(*r, [r](auto const& iter) { render_number_func(r, iter); });
combo->pack_end(*r, true);
combo->set_cell_data_func(*r, [r](auto const& iter) { render_number_func(r, iter); });
}
activity_model_row_changed_tag_ = tmodel->signal_row_changed().connect(
@ -589,8 +592,6 @@ Gtk::ComboBox* FilterBar::Impl::activity_combo_box_new(Glib::RefPtr<Gtk::TreeMod
[activity_model](auto const& /*path*/, auto const& /*iter*/) { activity_model_update_idle(activity_model); });
activity_model_row_deleted_cb_tag_ = tmodel->signal_row_deleted().connect( //
[activity_model](auto const& /*path*/) { activity_model_update_idle(activity_model); });
return c;
}
/****
@ -731,24 +732,65 @@ void FilterBar::Impl::update_count_label_idle()
****
***/
FilterBar::FilterBar(tr_session* session, Glib::RefPtr<Gtk::TreeModel> const& tmodel)
: Gtk::Box(Gtk::ORIENTATION_HORIZONTAL, GUI_PAD_SMALL)
FilterBarExtraInit::FilterBarExtraInit()
: ExtraClassInit(&FilterBarExtraInit::class_init, nullptr, &FilterBarExtraInit::instance_init)
{
}
void FilterBarExtraInit::class_init(void* klass, void* /*user_data*/)
{
auto* const widget_klass = GTK_WIDGET_CLASS(klass);
gtk_widget_class_set_template_from_resource(widget_klass, gtr_get_full_resource_path("FilterBar.ui").c_str());
gtk_widget_class_bind_template_child_full(widget_klass, "activity_combo", FALSE, 0);
gtk_widget_class_bind_template_child_full(widget_klass, "tracker_combo", FALSE, 0);
gtk_widget_class_bind_template_child_full(widget_klass, "text_entry", FALSE, 0);
gtk_widget_class_bind_template_child_full(widget_klass, "show_label", FALSE, 0);
}
void FilterBarExtraInit::instance_init(GTypeInstance* instance, void* /*klass*/)
{
gtk_widget_init_template(GTK_WIDGET(instance));
}
/***
****
***/
FilterBar::FilterBar()
: Glib::ObjectBase(typeid(FilterBar))
{
}
FilterBar::FilterBar(
BaseObjectType* cast_item,
Glib::RefPtr<Gtk::Builder> const& /*builder*/,
tr_session* session,
Glib::RefPtr<Gtk::TreeModel> const& tmodel)
: Glib::ObjectBase(typeid(FilterBar))
, Gtk::Box(cast_item)
, impl_(std::make_unique<Impl>(*this, session, tmodel))
{
}
FilterBar::~FilterBar() = default;
FilterBar::Impl::Impl(FilterBar& widget, tr_session* session, Glib::RefPtr<Gtk::TreeModel> const& tmodel)
: widget_(widget)
, activity_(get_template_child<Gtk::ComboBox>("activity_combo"))
, tracker_(get_template_child<Gtk::ComboBox>("tracker_combo"))
, entry_(get_template_child<Gtk::Entry>("text_entry"))
, show_lb_(get_template_child<Gtk::Label>("show_label"))
{
show_lb_ = Gtk::make_managed<Gtk::Label>();
activity_ = activity_combo_box_new(tmodel);
tracker_ = tracker_combo_box_new(tmodel);
activity_combo_box_init(activity_, tmodel);
tracker_combo_box_init(tracker_, tmodel);
filter_model_ = Gtk::TreeModelFilter::create(tmodel);
filter_model_->signal_row_deleted().connect([this](auto const& /*path*/) { update_count_label_idle(); });
filter_model_->signal_row_inserted().connect([this](auto const& /*path*/, auto const& /*iter*/)
{ update_count_label_idle(); });
tracker_->property_width_request() = 170;
static_cast<Gtk::TreeStore*>(gtr_get_ptr(tracker_->get_model()))->set_data(SESSION_KEY, session);
filter_model_->set_visible_func(sigc::mem_fun(*this, &Impl::is_row_visible));
@ -756,30 +798,13 @@ FilterBar::Impl::Impl(FilterBar& widget, tr_session* session, Glib::RefPtr<Gtk::
tracker_->signal_changed().connect(sigc::mem_fun(*this, &Impl::selection_changed_cb));
activity_->signal_changed().connect(sigc::mem_fun(*this, &Impl::selection_changed_cb));
/* add the activity combobox */
show_lb_->set_mnemonic_widget(*activity_);
widget_.pack_start(*show_lb_, false, false, 0);
widget_.pack_start(*activity_, true, true, 0);
activity_->set_margin_end(GUI_PAD);
/* add the tracker combobox */
widget_.pack_start(*tracker_, true, true, 0);
tracker_->set_margin_end(GUI_PAD);
/* add the entry field */
entry_ = Gtk::make_managed<Gtk::Entry>();
entry_->set_icon_from_icon_name("edit-clear", Gtk::ENTRY_ICON_SECONDARY);
entry_->signal_icon_release().connect([this](auto /*icon_position*/, auto const* /*event*/) { entry_->set_text({}); });
widget_.pack_start(*entry_, true, true, 0);
entry_->signal_changed().connect(sigc::mem_fun(*this, &Impl::filter_entry_changed));
selection_changed_cb();
selection_changed_cb();
update_count_label();
}
FilterBar::~FilterBar() = default;
FilterBar::Impl::~Impl()
{
torrent_model_row_deleted_cb_tag_.disconnect();
@ -800,3 +825,13 @@ Glib::RefPtr<Gtk::TreeModel> FilterBar::Impl::get_filter_model() const
{
return filter_model_;
}
template<typename T>
T* FilterBar::Impl::get_template_child(char const* name) const
{
auto full_type_name = std::string("gtkmm__CustomObject_");
Glib::append_canonical_typename(full_type_name, typeid(FilterBar).name());
return Glib::wrap(reinterpret_cast<typename T::BaseObjectType*>(
gtk_widget_get_template_child(GTK_WIDGET(widget_.gobj()), g_type_from_name(full_type_name.c_str()), name)));
}

View File

@ -7,16 +7,34 @@
#include <memory>
#include <glibmm/extraclassinit.h>
#include <gtkmm.h>
#include <libtransmission/tr-macros.h>
typedef struct tr_session tr_session;
class FilterBar : public Gtk::Box
class FilterBarExtraInit : public Glib::ExtraClassInit
{
public:
FilterBar(tr_session* session, Glib::RefPtr<Gtk::TreeModel> const& torrent_model);
FilterBarExtraInit();
private:
static void class_init(void* klass, void* user_data);
static void instance_init(GTypeInstance* instance, void* klass);
};
class FilterBar
: public FilterBarExtraInit
, public Gtk::Box
{
public:
FilterBar();
FilterBar(
BaseObjectType* cast_item,
Glib::RefPtr<Gtk::Builder> const& builder,
tr_session* session,
Glib::RefPtr<Gtk::TreeModel> const& torrent_model);
~FilterBar() override;
TR_DISABLE_COPY_MOVE(FilterBar)

62
gtk/FilterBar.ui Normal file
View File

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface domain="transmission-gtk">
<requires lib="gtk+" version="3.24"/>
<template class="gtkmm__CustomObject_9FilterBar" parent="GtkBox">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="border-width">3</property>
<property name="spacing">3</property>
<child>
<object class="GtkLabel" id="show_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">_Show:</property>
<property name="use-underline">True</property>
<property name="mnemonic-widget">activity_combo</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="activity_combo">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="margin-end">6</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="tracker_combo">
<property name="width-request">170</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="margin-end">6</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="text_entry">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="secondary-icon-name">edit-clear</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
</template>
</interface>

View File

@ -61,6 +61,16 @@ FreeSpaceLabel::FreeSpaceLabel(Glib::RefPtr<Session> const& core, std::string_vi
{
}
FreeSpaceLabel::FreeSpaceLabel(
BaseObjectType* cast_item,
Glib::RefPtr<Gtk::Builder> const& /*builder*/,
Glib::RefPtr<Session> const& core,
std::string_view dir)
: Gtk::Label(cast_item)
, impl_(std::make_unique<Impl>(*this, core, dir))
{
}
FreeSpaceLabel::~FreeSpaceLabel() = default;
FreeSpaceLabel::Impl::Impl(FreeSpaceLabel& label, Glib::RefPtr<Session> const& core, std::string_view dir)

View File

@ -17,6 +17,11 @@ class FreeSpaceLabel : public Gtk::Label
{
public:
FreeSpaceLabel(Glib::RefPtr<Session> const& core, std::string_view dir = {});
FreeSpaceLabel(
BaseObjectType* cast_item,
Glib::RefPtr<Gtk::Builder> const& builder,
Glib::RefPtr<Session> const& core,
std::string_view dir = {});
~FreeSpaceLabel() override;
TR_DISABLE_COPY_MOVE(FreeSpaceLabel)

View File

@ -9,11 +9,11 @@
#include <glibmm/i18n.h>
#include <libtransmission/transmission.h>
#include <libtransmission/utils.h> /* tr_formatter_speed_KBps() */
#include <libtransmission/utils.h> // tr_formatter_speed_KBps()
#include "Actions.h"
#include "FilterBar.h"
#include "HigWorkarea.h"
#include "HigWorkarea.h" // GUI_PAD_SMALL
#include "MainWindow.h"
#include "Prefs.h"
#include "PrefsDialog.h"
@ -24,7 +24,11 @@
class MainWindow::Impl
{
public:
Impl(MainWindow& window, Glib::RefPtr<Gio::ActionGroup> const& actions, Glib::RefPtr<Session> const& core);
Impl(
MainWindow& window,
Glib::RefPtr<Gtk::Builder> const& builder,
Glib::RefPtr<Gio::ActionGroup> const& actions,
Glib::RefPtr<Session> const& core);
~Impl();
TR_DISABLE_COPY_MOVE(Impl)
@ -36,7 +40,7 @@ public:
void prefsChanged(tr_quark key);
private:
Gtk::TreeView* makeview(Glib::RefPtr<Gtk::TreeModel> const& model);
void init_view(Gtk::TreeView* view, Glib::RefPtr<Gtk::TreeModel> const& model);
Gtk::Menu* createOptionsMenu();
Gtk::Menu* createSpeedMenu(tr_direction dir);
@ -63,6 +67,7 @@ private:
private:
MainWindow& window_;
Glib::RefPtr<Session> const core_;
std::array<Gtk::RadioMenuItem*, 2> speedlimit_on_item_;
std::array<Gtk::RadioMenuItem*, 2> speedlimit_off_item_;
@ -70,9 +75,9 @@ private:
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::Widget* toolbar_ = nullptr;
FilterBar* filter_;
Gtk::Widget* status_ = nullptr;
Gtk::Menu* status_menu_;
Gtk::Label* ul_lb_ = nullptr;
Gtk::Label* dl_lb_ = nullptr;
@ -80,10 +85,8 @@ private:
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;
};
@ -118,20 +121,12 @@ bool tree_view_search_equal_func(
} // namespace
Gtk::TreeView* MainWindow::Impl::makeview(Glib::RefPtr<Gtk::TreeModel> const& model)
void MainWindow::Impl::init_view(Gtk::TreeView* view, 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_ = Gtk::make_managed<Gtk::TreeViewColumn>();
column_->set_title(_("Torrent"));
column_->set_resizable(true);
column_->set_sizing(Gtk::TREE_VIEW_COLUMN_FIXED);
column_ = view->get_column(0);
renderer_ = Gtk::make_managed<TorrentCellRenderer>();
column_->pack_start(*renderer_, false);
@ -139,12 +134,9 @@ Gtk::TreeView* MainWindow::Impl::makeview(Glib::RefPtr<Gtk::TreeModel> const& mo
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)
@ -156,8 +148,6 @@ Gtk::TreeView* MainWindow::Impl::makeview(Glib::RefPtr<Gtk::TreeModel> const& mo
{ gtr_action_activate("show-torrent-properties"); });
view->set_model(model);
return view;
}
void MainWindow::Impl::prefsChanged(tr_quark const key)
@ -395,21 +385,41 @@ std::unique_ptr<MainWindow> MainWindow::create(
Glib::RefPtr<Gio::ActionGroup> const& actions,
Glib::RefPtr<Session> const& core)
{
return std::unique_ptr<MainWindow>(new MainWindow(app, actions, core));
auto const builder = Gtk::Builder::create_from_resource(gtr_get_full_resource_path("MainWindow.ui"));
return std::unique_ptr<MainWindow>(gtr_get_widget_derived<MainWindow>(builder, "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))
MainWindow::MainWindow(
BaseObjectType* cast_item,
Glib::RefPtr<Gtk::Builder> const& builder,
Gtk::Application& app,
Glib::RefPtr<Gio::ActionGroup> const& actions,
Glib::RefPtr<Session> const& core)
: Gtk::ApplicationWindow(cast_item)
, impl_(std::make_unique<Impl>(*this, builder, 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)
MainWindow::Impl::Impl(
MainWindow& window,
Glib::RefPtr<Gtk::Builder> const& builder,
Glib::RefPtr<Gio::ActionGroup> const& actions,
Glib::RefPtr<Session> const& core)
: window_(window)
, core_(core)
, scroll_(gtr_get_widget<Gtk::ScrolledWindow>(builder, "torrents_view_scroll"))
, view_(gtr_get_widget<Gtk::TreeView>(builder, "torrents_view"))
, toolbar_(gtr_get_widget<Gtk::Widget>(builder, "toolbar"))
, filter_(gtr_get_widget_derived<FilterBar>(builder, "filterbar", core_->get_session(), core_->get_model()))
, status_(gtr_get_widget<Gtk::Widget>(builder, "statusbar"))
, ul_lb_(gtr_get_widget<Gtk::Label>(builder, "upload_speed_label"))
, dl_lb_(gtr_get_widget<Gtk::Label>(builder, "download_speed_label"))
, stats_lb_(gtr_get_widget<Gtk::Label>(builder, "statistics_label"))
, alt_speed_image_(gtr_get_widget<Gtk::Image>(builder, "alt_speed_button_image"))
, alt_speed_button_(gtr_get_widget<Gtk::ToggleButton>(builder, "alt_speed_button"))
{
static struct
{
@ -424,7 +434,6 @@ MainWindow::Impl::Impl(MainWindow& window, Glib::RefPtr<Gio::ActionGroup> const&
/* 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));
@ -434,27 +443,6 @@ MainWindow::Impl::Impl(MainWindow& window, Glib::RefPtr<Gio::ActionGroup> const&
}
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>();
@ -474,73 +462,23 @@ MainWindow::Impl::Impl(MainWindow& window, Glib::RefPtr<Gio::ActionGroup> const&
*** 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>("options-symbolic", Gtk::ICON_SIZE_MENU));
gear_button->set_tooltip_text(_("Options"));
gear_button->set_relief(Gtk::RELIEF_NONE);
auto* gear_button = gtr_get_widget<Gtk::Button>(builder, "gear_button");
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>("ratio-symbolic", Gtk::ICON_SIZE_MENU));
ratio_button->set_relief(Gtk::RELIEF_NONE);
auto* ratio_button = gtr_get_widget<Gtk::Button>(builder, "ratio_button");
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);
init_view(view_, filter_->get_filter_model());
{
/* this is to determine the maximum width/height for the label */
@ -550,15 +488,8 @@ MainWindow::Impl::Impl(MainWindow& window, Glib::RefPtr<Gio::ActionGroup> const&
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);
@ -670,7 +601,7 @@ Glib::RefPtr<Gtk::TreeSelection> MainWindow::get_selection() const
Glib::RefPtr<Gtk::TreeSelection> MainWindow::Impl::get_selection() const
{
return selection_;
return view_->get_selection();
}
void MainWindow::set_busy(bool isBusy)

View File

@ -16,6 +16,12 @@ class Session;
class MainWindow : public Gtk::ApplicationWindow
{
public:
MainWindow(
BaseObjectType* cast_item,
Glib::RefPtr<Gtk::Builder> const& builder,
Gtk::Application& app,
Glib::RefPtr<Gio::ActionGroup> const& actions,
Glib::RefPtr<Session> const& core);
~MainWindow() override;
TR_DISABLE_COPY_MOVE(MainWindow)
@ -30,9 +36,6 @@ public:
void set_busy(bool isBusy);
void refresh();
protected:
MainWindow(Gtk::Application& app, Glib::RefPtr<Gio::ActionGroup> const& actions, Glib::RefPtr<Session> const& core);
private:
class Impl;
std::unique_ptr<Impl> const impl_;

293
gtk/MainWindow.ui Normal file
View File

@ -0,0 +1,293 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface domain="transmission-gtk">
<requires lib="gtk+" version="3.24"/>
<object class="GtkApplicationWindow" id="MainWindow">
<property name="can-focus">False</property>
<property name="role">tr-main</property>
<child>
<object class="GtkBox" id="window_layout">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkToolbar" id="toolbar">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkToolButton" id="open_file_button">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">Open a torrent</property>
<property name="is-important">True</property>
<property name="action-name">win.open-torrent</property>
<property name="label" translatable="yes">_Open</property>
<property name="use-underline">True</property>
<property name="icon-name">document-open</property>
</object>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
</packing>
</child>
<child>
<object class="GtkToolButton" id="start_torrent_button">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">Start torrent</property>
<property name="action-name">win.torrent-start</property>
<property name="label" translatable="yes">_Start</property>
<property name="use-underline">True</property>
<property name="icon-name">media-playback-start</property>
</object>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
</packing>
</child>
<child>
<object class="GtkToolButton" id="pause_torrent_button">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">Pause torrent</property>
<property name="action-name">win.torrent-stop</property>
<property name="label" translatable="yes">_Pause</property>
<property name="use-underline">True</property>
<property name="icon-name">media-playback-pause</property>
</object>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
</packing>
</child>
<child>
<object class="GtkToolButton" id="remove_torrent_button">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="action-name">win.remove-torrent</property>
<property name="label" translatable="yes">Remove torrent</property>
<property name="use-underline">True</property>
<property name="icon-name">list-remove</property>
</object>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
</packing>
</child>
<child>
<object class="GtkSeparatorToolItem">
<property name="visible">True</property>
<property name="can-focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
</packing>
</child>
<child>
<object class="GtkToolButton" id="torrent_properties_button">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">Torrent properties</property>
<property name="is-important">True</property>
<property name="action-name">win.show-torrent-properties</property>
<property name="label" translatable="yes">_Properties</property>
<property name="use-underline">True</property>
<property name="icon-name">document-properties</property>
</object>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="gtkmm__CustomObject_9FilterBar" id="filterbar">
<property name="visible">True</property>
<property name="can-focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow" id="torrents_view_scroll">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="hscrollbar-policy">never</property>
<property name="shadow-type">out</property>
<child>
<object class="GtkTreeView" id="torrents_view">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="headers-visible">False</property>
<property name="fixed-height-mode">True</property>
<child internal-child="selection">
<object class="GtkTreeSelection" id="torrents_view_selection">
<property name="mode">multiple</property>
</object>
</child>
<child>
<object class="GtkTreeViewColumn" id="torrent_column">
<property name="resizable">True</property>
<property name="sizing">fixed</property>
<property name="title" translatable="yes">Torrent</property>
</object>
</child>
</object>
</child>
<style>
<class name="tr-workarea"/>
</style>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkBox" id="statusbar">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="border-width">3</property>
<property name="spacing">3</property>
<child>
<object class="GtkButton" id="gear_button">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="tooltip-text" translatable="yes">Options</property>
<property name="relief">none</property>
<child>
<object class="GtkImage" id="gear_button_image">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">options-symbolic</property>
<property name="icon_size">1</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkToggleButton" id="alt_speed_button">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="relief">none</property>
<child>
<object class="GtkImage" id="alt_speed_button_image">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">turtle-symbolic</property>
<property name="icon_size">1</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkFixed">
<property name="visible">True</property>
<property name="can-focus">False</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="download_speed_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="margin-start">3</property>
<property name="label">...</property>
<property name="single-line-mode">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="upload_speed_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="margin-start">3</property>
<property name="label">...</property>
<property name="single-line-mode">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
<child>
<object class="GtkButton" id="ratio_button">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="tooltip-text" translatable="yes">Statistics</property>
<property name="relief">none</property>
<child>
<object class="GtkImage" id="ratio_button_image">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="icon-name">ratio-symbolic</property>
<property name="icon_size">1</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack-type">end</property>
<property name="position">5</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="statistics_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="margin-start">9</property>
<property name="margin-end">3</property>
<property name="label">...</property>
<property name="single-line-mode">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">6</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
</object>
</child>
</object>
</interface>

View File

@ -21,7 +21,6 @@
#include <libtransmission/makemeta.h>
#include <libtransmission/utils.h> /* tr_formatter_mem_B() */
#include "HigWorkarea.h"
#include "MakeDialog.h"
#include "PrefsDialog.h"
#include "Session.h"
@ -38,10 +37,12 @@ class MakeProgressDialog : public Gtk::Dialog
{
public:
MakeProgressDialog(
BaseObjectType* cast_item,
Glib::RefPtr<Gtk::Builder> const& builder,
Gtk::Window& parent,
tr_metainfo_builder& builder,
tr_metainfo_builder& metainfo_builder,
std::future<tr_error*> future,
std::string_view target,
std::string_view const& target,
Glib::RefPtr<Session> const& core);
~MakeProgressDialog() override;
@ -75,7 +76,7 @@ private:
class MakeDialog::Impl
{
public:
Impl(MakeDialog& dialog, Glib::RefPtr<Session> const& core);
Impl(MakeDialog& dialog, Glib::RefPtr<Gtk::Builder> const& builder, Glib::RefPtr<Session> const& core);
TR_DISABLE_COPY_MOVE(Impl)
@ -237,48 +238,41 @@ void MakeProgressDialog::onProgressDialogResponse(int response)
}
MakeProgressDialog::MakeProgressDialog(
BaseObjectType* cast_item,
Glib::RefPtr<Gtk::Builder> const& builder,
Gtk::Window& parent,
tr_metainfo_builder& builder,
tr_metainfo_builder& metainfo_builder,
std::future<tr_error*> future,
std::string_view target,
std::string_view const& target,
Glib::RefPtr<Session> const& core)
: Gtk::Dialog(_("New Torrent"), parent, true)
, builder_{ builder }
: Gtk::Dialog(cast_item)
, builder_(metainfo_builder)
, future_{ std::move(future) }
, target_{ target }
, core_{ core }
, target_(target)
, core_(core)
, progress_label_(gtr_get_widget<Gtk::Label>(builder, "progress_label"))
, progress_bar_(gtr_get_widget<Gtk::ProgressBar>(builder, "progress_bar"))
{
add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
add_button(_("_Close"), Gtk::RESPONSE_CLOSE);
add_button(_("_Add"), Gtk::RESPONSE_ACCEPT);
set_transient_for(parent);
signal_response().connect(sigc::mem_fun(*this, &MakeProgressDialog::onProgressDialogResponse));
auto* fr = Gtk::make_managed<Gtk::Frame>();
fr->set_border_width(GUI_PAD_BIG);
fr->set_shadow_type(Gtk::SHADOW_NONE);
auto* v = Gtk::make_managed<Gtk::Box>(Gtk::ORIENTATION_VERTICAL, GUI_PAD);
fr->add(*v);
progress_label_ = Gtk::make_managed<Gtk::Label>(_("Creating torrent…"));
progress_label_->set_halign(Gtk::ALIGN_START);
progress_label_->set_valign(Gtk::ALIGN_CENTER);
progress_label_->set_justify(Gtk::JUSTIFY_LEFT);
v->pack_start(*progress_label_, false, false, 0);
progress_bar_ = Gtk::make_managed<Gtk::ProgressBar>();
v->pack_start(*progress_bar_, false, false, 0);
progress_tag_ = Glib::signal_timeout().connect_seconds(
sigc::mem_fun(*this, &MakeProgressDialog::onProgressDialogRefresh),
SECONDARY_WINDOW_REFRESH_INTERVAL_SECONDS);
onProgressDialogRefresh();
gtr_dialog_set_content(*this, *fr);
}
void MakeDialog::Impl::makeProgressDialog(std::string_view target, std::future<tr_error*> future)
{
progress_dialog_ = std::make_unique<MakeProgressDialog>(dialog_, *builder_, std::move(future), target, core_);
auto const builder = Gtk::Builder::create_from_resource(gtr_get_full_resource_path("MakeProgressDialog.ui"));
progress_dialog_ = std::unique_ptr<MakeProgressDialog>(gtr_get_widget_derived<MakeProgressDialog>(
builder,
"MakeProgressDialog",
dialog_,
*builder_,
std::move(future),
target,
core_));
progress_dialog_->signal_hide().connect(
[this]()
{
@ -350,7 +344,7 @@ void MakeDialog::Impl::updatePiecesLabel()
{
auto const filename = builder_ ? builder_->top() : ""sv;
auto gstr = Glib::ustring{ "<i>" };
auto gstr = Glib::ustring();
if (std::empty(filename))
{
@ -373,8 +367,7 @@ void MakeDialog::Impl::updatePiecesLabel()
fmt::arg("piece_size", tr_formatter_mem_B(builder_->pieceSize())));
}
gstr += "</i>";
pieces_lb_->set_markup(gstr);
pieces_lb_->set_text(gstr);
}
void MakeDialog::Impl::configurePieceSizeScale()
@ -453,106 +446,69 @@ void MakeDialog::Impl::on_drag_data_received(
drag_context->drag_finish(success, false, time_);
}
std::unique_ptr<MakeDialog> MakeDialog::create(Gtk::Window& parent, Glib::RefPtr<Session> const& core)
{
return std::unique_ptr<MakeDialog>(new MakeDialog(parent, core));
}
MakeDialog::MakeDialog(Gtk::Window& parent, Glib::RefPtr<Session> const& core)
: Gtk::Dialog(_("New Torrent"), parent)
, impl_(std::make_unique<Impl>(*this, core))
MakeDialog::MakeDialog(
BaseObjectType* cast_item,
Glib::RefPtr<Gtk::Builder> const& builder,
Gtk::Window& parent,
Glib::RefPtr<Session> const& core)
: Gtk::Dialog(cast_item)
, impl_(std::make_unique<Impl>(*this, builder, core))
{
set_transient_for(parent);
}
MakeDialog::~MakeDialog() = default;
MakeDialog::Impl::Impl(MakeDialog& dialog, Glib::RefPtr<Session> const& core)
std::unique_ptr<MakeDialog> MakeDialog::create(Gtk::Window& parent, Glib::RefPtr<Session> const& core)
{
auto const builder = Gtk::Builder::create_from_resource(gtr_get_full_resource_path("MakeDialog.ui"));
return std::unique_ptr<MakeDialog>(gtr_get_widget_derived<MakeDialog>(builder, "MakeDialog", parent, core));
}
MakeDialog::Impl::Impl(MakeDialog& dialog, Glib::RefPtr<Gtk::Builder> const& builder, Glib::RefPtr<Session> const& core)
: dialog_(dialog)
, core_(core)
, file_radio_(gtr_get_widget<Gtk::RadioButton>(builder, "source_file_radio"))
, file_chooser_(gtr_get_widget<Gtk::FileChooserButton>(builder, "source_file_button"))
, folder_radio_(gtr_get_widget<Gtk::RadioButton>(builder, "source_folder_radio"))
, folder_chooser_(gtr_get_widget<Gtk::FileChooserButton>(builder, "source_folder_button"))
, pieces_lb_(gtr_get_widget<Gtk::Label>(builder, "source_size_label"))
, piece_size_scale_(gtr_get_widget<Gtk::Scale>(builder, "piece_size_scale"))
, destination_chooser_(gtr_get_widget<Gtk::FileChooserButton>(builder, "destination_button"))
, comment_check_(gtr_get_widget<Gtk::CheckButton>(builder, "comment_check"))
, comment_entry_(gtr_get_widget<Gtk::Entry>(builder, "comment_entry"))
, private_check_(gtr_get_widget<Gtk::CheckButton>(builder, "private_check"))
, source_check_(gtr_get_widget<Gtk::CheckButton>(builder, "source_check"))
, source_entry_(gtr_get_widget<Gtk::Entry>(builder, "source_entry"))
, announce_text_buffer_(gtr_get_widget<Gtk::TextView>(builder, "trackers_view")->get_buffer())
{
guint row = 0;
dialog_.add_button(_("_Close"), Gtk::RESPONSE_CLOSE);
dialog_.add_button(_("_New"), Gtk::RESPONSE_ACCEPT);
dialog_.signal_response().connect(sigc::mem_fun(*this, &Impl::onResponse));
auto* t = Gtk::make_managed<HigWorkarea>();
t->add_section_title(row, _("Files"));
destination_chooser_ = Gtk::make_managed<Gtk::FileChooserButton>(Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER);
destination_chooser_->set_current_folder(Glib::get_user_special_dir(Glib::USER_DIRECTORY_DESKTOP));
t->add_row(row, _("Sa_ve to:"), *destination_chooser_);
Gtk::RadioButton::Group slist;
folder_radio_ = Gtk::make_managed<Gtk::RadioButton>(slist, _("Source F_older:"), true);
folder_radio_->set_active(false);
folder_chooser_ = Gtk::make_managed<Gtk::FileChooserButton>(Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER);
folder_radio_->signal_toggled().connect([this]() { onSourceToggled2(folder_radio_, folder_chooser_); });
folder_radio_->signal_toggled().connect([this]() { onSourceToggled(folder_radio_, folder_chooser_); });
folder_chooser_->signal_selection_changed().connect([this]() { onChooserChosen(folder_chooser_); });
folder_chooser_->set_sensitive(false);
t->add_row_w(row, *folder_radio_, *folder_chooser_);
file_radio_ = Gtk::make_managed<Gtk::RadioButton>(slist, _("Source _File:"), true);
file_radio_->set_active(true);
file_chooser_ = Gtk::make_managed<Gtk::FileChooserButton>(Gtk::FILE_CHOOSER_ACTION_OPEN);
file_radio_->signal_toggled().connect([this]() { onSourceToggled2(file_radio_, file_chooser_); });
file_radio_->signal_toggled().connect([this]() { onSourceToggled(file_radio_, file_chooser_); });
file_chooser_->signal_selection_changed().connect([this]() { onChooserChosen(file_chooser_); });
t->add_row_w(row, *file_radio_, *file_chooser_);
pieces_lb_ = Gtk::make_managed<Gtk::Label>();
pieces_lb_->set_markup(fmt::format(FMT_STRING("<i>{:s}</i>"), _("No source selected")));
t->add_row(row, {}, *pieces_lb_);
piece_size_scale_ = Gtk::make_managed<Gtk::Scale>();
piece_size_scale_->set_draw_value(false);
piece_size_scale_->set_visible(false);
piece_size_scale_->signal_value_changed().connect([this]() { onPieceSizeUpdated(); });
t->add_row(row, _("Piece size:"), *piece_size_scale_);
t->add_section_divider(row);
t->add_section_title(row, _("Properties"));
auto* v = Gtk::make_managed<Gtk::Box>(Gtk::ORIENTATION_VERTICAL, GUI_PAD_SMALL);
announce_text_buffer_ = Gtk::TextBuffer::create();
auto* w = Gtk::make_managed<Gtk::TextView>(announce_text_buffer_);
w->set_size_request(-1, 80);
auto* sw = Gtk::make_managed<Gtk::ScrolledWindow>();
sw->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
sw->add(*w);
auto* fr = Gtk::make_managed<Gtk::Frame>();
fr->set_shadow_type(Gtk::SHADOW_IN);
fr->add(*sw);
v->pack_start(*fr, true, true, 0);
auto* l = Gtk::make_managed<Gtk::Label>();
l->set_markup(_(
"To add a backup URL, add it on the next line after a primary URL.\nTo add a new primary URL, add it after a blank line."));
l->set_justify(Gtk::JUSTIFY_LEFT);
l->set_halign(Gtk::ALIGN_START);
l->set_valign(Gtk::ALIGN_CENTER);
v->pack_start(*l, false, false, 0);
t->add_tall_row(row, _("_Trackers:"), *v);
comment_check_ = Gtk::make_managed<Gtk::CheckButton>(_("Co_mment:"), true);
comment_check_->set_active(false);
comment_entry_ = Gtk::make_managed<Gtk::Entry>();
comment_entry_->set_sensitive(false);
comment_check_->signal_toggled().connect([this]() { onSourceToggled(comment_check_, comment_entry_); });
t->add_row_w(row, *comment_check_, *comment_entry_);
source_check_ = Gtk::make_managed<Gtk::CheckButton>(_("_Source:"), true);
source_check_->set_active(false);
source_entry_ = Gtk::make_managed<Gtk::Entry>();
source_entry_->set_sensitive(false);
source_check_->signal_toggled().connect([this]() { onSourceToggled(source_check_, source_entry_); });
t->add_row_w(row, *source_check_, *source_entry_);
private_check_ = t->add_wide_checkbutton(row, _("_Private torrent"), false);
gtr_dialog_set_content(dialog_, *t);
dialog_.drag_dest_set(Gtk::DEST_DEFAULT_ALL, Gdk::ACTION_COPY);
dialog_.drag_dest_add_uri_targets();

View File

@ -16,15 +16,17 @@ class Session;
class MakeDialog : public Gtk::Dialog
{
public:
MakeDialog(
BaseObjectType* cast_item,
Glib::RefPtr<Gtk::Builder> const& builder,
Gtk::Window& parent,
Glib::RefPtr<Session> const& core);
~MakeDialog() override;
TR_DISABLE_COPY_MOVE(MakeDialog)
static std::unique_ptr<MakeDialog> create(Gtk::Window& parent, Glib::RefPtr<Session> const& core);
protected:
MakeDialog(Gtk::Window& parent, Glib::RefPtr<Session> const& core);
private:
class Impl;
std::unique_ptr<Impl> const impl_;

429
gtk/MakeDialog.ui Normal file
View File

@ -0,0 +1,429 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface domain="transmission-gtk">
<requires lib="gtk+" version="3.24"/>
<object class="GtkDialog" id="MakeDialog">
<property name="can-focus">False</property>
<property name="border-width">6</property>
<property name="title" translatable="yes">New Torrent</property>
<property name="type-hint">dialog</property>
<child internal-child="vbox">
<object class="GtkBox" id="dialog_layout">
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child internal-child="action_area">
<object class="GtkButtonBox" id="dialog_buttons">
<property name="can-focus">False</property>
<property name="layout-style">end</property>
<child>
<object class="GtkButton" id="close_button">
<property name="label" translatable="yes">_Close</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="can-default">True</property>
<property name="receives-default">True</property>
<property name="use-underline">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="new_button">
<property name="label" translatable="yes">_New</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="can-default">True</property>
<property name="receives-default">True</property>
<property name="use-underline">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox" id="dialog_content_layout">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel" id="files_section_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="hexpand">True</property>
<property name="label" translatable="yes">Files</property>
<property name="xalign">0</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<!-- n-columns=2 n-rows=5 -->
<object class="GtkGrid" id="files_section_layout">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="margin-start">18</property>
<property name="row-spacing">6</property>
<property name="column-spacing">12</property>
<child>
<object class="GtkLabel" id="destination_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Sa_ve to:</property>
<property name="use-underline">True</property>
<property name="mnemonic-widget">destination_button</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object class="GtkFileChooserButton" id="destination_button">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="hexpand">True</property>
<property name="action">select-folder</property>
<property name="title" translatable="yes"/>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object class="GtkRadioButton" id="source_folder_radio">
<property name="label" translatable="yes">Source F_older:</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="valign">center</property>
<property name="use-underline">True</property>
<property name="draw-indicator">True</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkFileChooserButton" id="source_folder_button">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="hexpand">True</property>
<property name="action">select-folder</property>
<property name="title" translatable="yes"/>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkRadioButton" id="source_file_radio">
<property name="label" translatable="yes">Source _File:</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="valign">center</property>
<property name="use-underline">True</property>
<property name="active">True</property>
<property name="draw-indicator">True</property>
<property name="group">source_folder_radio</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">2</property>
</packing>
</child>
<child>
<object class="GtkFileChooserButton" id="source_file_button">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="hexpand">True</property>
<property name="title" translatable="yes"/>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="source_size_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="hexpand">True</property>
<property name="label" translatable="yes">No source selected</property>
<property name="xalign">0</property>
<attributes>
<attribute name="style" value="italic"/>
</attributes>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">3</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="piece_size_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Piece size:</property>
<property name="use-underline">True</property>
<property name="mnemonic-widget">piece_size_scale</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">4</property>
</packing>
</child>
<child>
<object class="GtkScale" id="piece_size_scale">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="hexpand">True</property>
<property name="digits">0</property>
<property name="draw-value">False</property>
<property name="value-pos">left</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">4</property>
</packing>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkFixed" id="properties_section_spacer">
<property name="height-request">6</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="properties_section_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="hexpand">True</property>
<property name="label" translatable="yes">Properties</property>
<property name="xalign">0</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
<child>
<!-- n-columns=2 n-rows=4 -->
<object class="GtkGrid" id="properties_section_layout">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="margin-start">18</property>
<property name="row-spacing">6</property>
<property name="column-spacing">12</property>
<child>
<object class="GtkLabel" id="trackers_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="valign">start</property>
<property name="ypad">6</property>
<property name="label" translatable="yes">_Trackers:</property>
<property name="use-underline">True</property>
<property name="mnemonic-widget">trackers_view</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object class="GtkBox" id="trackers_layout">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="orientation">vertical</property>
<property name="spacing">3</property>
<child>
<object class="GtkScrolledWindow" id="trackers_view_scroll">
<property name="height-request">80</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<property name="shadow-type">in</property>
<child>
<object class="GtkTextView" id="trackers_view">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="accepts-tab">False</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="trackers_description_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="hexpand">True</property>
<property name="label" translatable="yes">To add a backup URL, add it on the next line after a primary URL.
To add a new primary URL, add it after a blank line.</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="comment_check">
<property name="label" translatable="yes">Co_mment:</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="valign">center</property>
<property name="use-underline">True</property>
<property name="draw-indicator">True</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="comment_entry">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="hexpand">True</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="source_check">
<property name="label" translatable="yes">_Source:</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="valign">center</property>
<property name="use-underline">True</property>
<property name="draw-indicator">True</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">2</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="source_entry">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="hexpand">True</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">2</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="private_check">
<property name="label" translatable="yes">_Private torrent</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="valign">center</property>
<property name="use-underline">True</property>
<property name="draw-indicator">True</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">3</property>
<property name="width">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
</object>
</child>
<action-widgets>
<action-widget response="-7">close_button</action-widget>
<action-widget response="-3">new_button</action-widget>
</action-widgets>
</object>
<object class="GtkSizeGroup" id="labels_width_group">
<widgets>
<widget name="destination_label"/>
<widget name="source_folder_radio"/>
<widget name="source_file_radio"/>
<widget name="piece_size_label"/>
<widget name="trackers_label"/>
<widget name="comment_check"/>
<widget name="source_check"/>
</widgets>
</object>
</interface>

117
gtk/MakeProgressDialog.ui Normal file
View File

@ -0,0 +1,117 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface domain="transmission-gtk">
<requires lib="gtk+" version="3.24"/>
<object class="GtkDialog" id="MakeProgressDialog">
<property name="can-focus">False</property>
<property name="border-width">6</property>
<property name="title" translatable="yes">New Torrent</property>
<property name="modal">True</property>
<property name="type-hint">dialog</property>
<child internal-child="vbox">
<object class="GtkBox" id="dialog_layout">
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child internal-child="action_area">
<object class="GtkButtonBox" id="dialog_button">
<property name="can-focus">False</property>
<property name="layout-style">end</property>
<child>
<object class="GtkButton" id="cancel_button">
<property name="label" translatable="yes">_Cancel</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="can-default">True</property>
<property name="receives-default">True</property>
<property name="use-underline">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="close_button">
<property name="label" translatable="yes">_Close</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="can-default">True</property>
<property name="receives-default">True</property>
<property name="use-underline">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="add_button">
<property name="label" translatable="yes">_Add</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="can-default">True</property>
<property name="receives-default">True</property>
<property name="use-underline">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox" id="dialog_content_layout">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel" id="progress_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Creating torrent…</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkProgressBar" id="progress_bar">
<property name="visible">True</property>
<property name="can-focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
<action-widgets>
<action-widget response="-6">cancel_button</action-widget>
<action-widget response="-7">close_button</action-widget>
<action-widget response="-3">add_button</action-widget>
</action-widgets>
</object>
</interface>

View File

@ -17,7 +17,6 @@
#include <libtransmission/log.h>
#include "Actions.h"
#include "HigWorkarea.h"
#include "MessageLogWindow.h"
#include "Prefs.h"
#include "PrefsDialog.h"
@ -46,7 +45,7 @@ MessageLogColumnsModel const message_log_cols;
class MessageLogWindow::Impl
{
public:
Impl(MessageLogWindow& window, Glib::RefPtr<Session> const& core);
Impl(MessageLogWindow& window, Glib::RefPtr<Gtk::Builder> const& builder, Glib::RefPtr<Session> const& core);
~Impl();
TR_DISABLE_COPY_MOVE(Impl)
@ -59,19 +58,19 @@ private:
void doSave(Gtk::Window& parent, Glib::ustring const& filename);
void onClearRequest();
void onPauseToggled(Gtk::ToggleToolButton* w);
void onPauseToggled(Gio::SimpleAction& action);
void scroll_to_bottom();
void level_combo_changed_cb(Gtk::ComboBox* combo_box);
Gtk::ComboBox* level_combo_new() const;
void level_combo_init(Gtk::ComboBox* level_combo) const;
bool is_pinned_to_new() const;
bool isRowVisible(Gtk::TreeModel::const_iterator const& iter) const;
private:
MessageLogWindow& window_;
Glib::RefPtr<Session> const core_;
Gtk::TreeView* view_ = nullptr;
Glib::RefPtr<Gtk::ListStore> store_;
Glib::RefPtr<Gtk::TreeModelFilter> filter_;
@ -139,16 +138,15 @@ void MessageLogWindow::Impl::scroll_to_bottom()
*****
****/
Gtk::ComboBox* MessageLogWindow::Impl::level_combo_new() const
void MessageLogWindow::Impl::level_combo_init(Gtk::ComboBox* level_combo) const
{
auto items = std::vector<std::pair<Glib::ustring, int>>{};
for (auto const& [level, name] : level_names_)
{
items.emplace_back(name, level);
}
auto* w = gtr_combo_box_new_enum(items);
gtr_combo_box_set_active_enum(*w, gtr_pref_int_get(TR_KEY_message_level));
return w;
gtr_combo_box_set_enum(*level_combo, items);
gtr_combo_box_set_active_enum(*level_combo, gtr_pref_int_get(TR_KEY_message_level));
}
void MessageLogWindow::Impl::level_combo_changed_cb(Gtk::ComboBox* combo_box)
@ -243,9 +241,14 @@ void MessageLogWindow::Impl::onClearRequest()
myHead = myTail = nullptr;
}
void MessageLogWindow::Impl::onPauseToggled(Gtk::ToggleToolButton* w)
void MessageLogWindow::Impl::onPauseToggled(Gio::SimpleAction& action)
{
isPaused_ = w->get_active();
bool value = false;
action.get_state(value);
action.set_state(Glib::Variant<bool>::create(!value));
isPaused_ = !value;
}
namespace
@ -417,21 +420,31 @@ bool MessageLogWindow::Impl::onRefresh()
std::unique_ptr<MessageLogWindow> MessageLogWindow::create(Gtk::Window& parent, Glib::RefPtr<Session> const& core)
{
return std::unique_ptr<MessageLogWindow>(new MessageLogWindow(parent, core));
auto const builder = Gtk::Builder::create_from_resource(gtr_get_full_resource_path("MessageLogWindow.ui"));
return std::unique_ptr<MessageLogWindow>(
gtr_get_widget_derived<MessageLogWindow>(builder, "MessageLogWindow", parent, core));
}
MessageLogWindow::MessageLogWindow(Gtk::Window& parent, Glib::RefPtr<Session> const& core)
: Gtk::Window(Gtk::WINDOW_TOPLEVEL)
, impl_(std::make_unique<Impl>(*this, core))
MessageLogWindow::MessageLogWindow(
BaseObjectType* cast_item,
Glib::RefPtr<Gtk::Builder> const& builder,
Gtk::Window& parent,
Glib::RefPtr<Session> const& core)
: Gtk::Window(cast_item)
, impl_(std::make_unique<Impl>(*this, builder, core))
{
set_transient_for(parent);
}
MessageLogWindow::~MessageLogWindow() = default;
MessageLogWindow::Impl::Impl(MessageLogWindow& window, Glib::RefPtr<Session> const& core)
MessageLogWindow::Impl::Impl(
MessageLogWindow& window,
Glib::RefPtr<Gtk::Builder> const& builder,
Glib::RefPtr<Session> const& core)
: window_(window)
, core_(core)
, view_(gtr_get_widget<Gtk::TreeView>(builder, "messages_view"))
, level_names_{ { { TR_LOG_CRITICAL, _("Critical") },
{ TR_LOG_ERROR, _("Error") },
{ TR_LOG_WARN, _("Warning") },
@ -439,73 +452,30 @@ MessageLogWindow::Impl::Impl(MessageLogWindow& window, Glib::RefPtr<Session> con
{ TR_LOG_DEBUG, _("Debug") },
{ TR_LOG_TRACE, _("Trace") } } }
{
window_.set_title(_("Message Log"));
window_.set_default_size(560, 350);
window_.set_role("message-log");
auto* vbox = Gtk::make_managed<Gtk::Box>(Gtk::ORIENTATION_VERTICAL, 0);
/**
*** toolbar
**/
auto* toolbar = Gtk::make_managed<Gtk::Toolbar>();
toolbar->set_toolbar_style(Gtk::TOOLBAR_BOTH_HORIZ);
toolbar->get_style_context()->add_class(GTK_STYLE_CLASS_PRIMARY_TOOLBAR);
auto const action_group = Gio::SimpleActionGroup::create();
{
auto* icon = Gtk::make_managed<Gtk::Image>();
icon->set_from_icon_name("document-save-as", Gtk::BuiltinIconSize::ICON_SIZE_SMALL_TOOLBAR);
auto* item = Gtk::make_managed<Gtk::ToolButton>(*icon);
item->set_is_important(true);
item->set_label(_("Save _As"));
item->set_use_underline(true);
item->signal_clicked().connect(sigc::mem_fun(*this, &Impl::onSaveRequest));
toolbar->insert(*item, -1);
}
auto const save_action = Gio::SimpleAction::create("save-message-log");
save_action->signal_activate().connect([this](auto const& /*value*/) { onSaveRequest(); });
action_group->add_action(save_action);
{
auto* icon = Gtk::make_managed<Gtk::Image>();
icon->set_from_icon_name("edit-clear", Gtk::BuiltinIconSize::ICON_SIZE_SMALL_TOOLBAR);
auto* item = Gtk::make_managed<Gtk::ToolButton>(*icon);
item->set_is_important(true);
item->set_label(_("Clear"));
item->set_use_underline(true);
item->signal_clicked().connect(sigc::mem_fun(*this, &Impl::onClearRequest));
toolbar->insert(*item, -1);
}
auto const clear_action = Gio::SimpleAction::create("clear-message-log");
clear_action->signal_activate().connect([this](auto const& /*value*/) { onClearRequest(); });
action_group->add_action(clear_action);
toolbar->insert(*Gtk::make_managed<Gtk::SeparatorToolItem>(), -1);
auto const pause_action = Gio::SimpleAction::create_bool("pause-message-log");
pause_action->signal_activate().connect([this, &action = *gtr_get_ptr(pause_action)](auto const& /*value*/)
{ onPauseToggled(action); });
action_group->add_action(pause_action);
{
auto* icon = Gtk::make_managed<Gtk::Image>();
icon->set_from_icon_name("media-playback-pause", Gtk::BuiltinIconSize::ICON_SIZE_SMALL_TOOLBAR);
auto* item = Gtk::make_managed<Gtk::ToggleToolButton>(*icon);
item->set_is_important(true);
item->set_label(_("P_ause"));
item->set_use_underline(true);
item->signal_toggled().connect([this, item]() { onPauseToggled(item); });
toolbar->insert(*item, -1);
}
auto* const level_combo = gtr_get_widget<Gtk::ComboBox>(builder, "level_combo");
level_combo_init(level_combo);
level_combo->signal_changed().connect([this, level_combo]() { level_combo_changed_cb(level_combo); });
toolbar->insert(*Gtk::make_managed<Gtk::SeparatorToolItem>(), -1);
{
auto* w = Gtk::make_managed<Gtk::Label>(_("Level"));
w->property_margin() = GUI_PAD;
auto* item = Gtk::make_managed<Gtk::ToolItem>();
item->add(*w);
toolbar->insert(*item, -1);
}
{
auto* w = level_combo_new();
w->signal_changed().connect([this, w]() { level_combo_changed_cb(w); });
auto* item = Gtk::make_managed<Gtk::ToolItem>();
item->add(*w);
toolbar->insert(*item, -1);
}
vbox->pack_start(*toolbar, false, false, 0);
window_.insert_action_group("win", action_group);
/**
*** messages
@ -522,18 +492,12 @@ MessageLogWindow::Impl::Impl(MessageLogWindow& window, Glib::RefPtr<Session> con
maxLevel_ = static_cast<tr_log_level>(gtr_pref_int_get(TR_KEY_message_level));
filter_->set_visible_func(sigc::mem_fun(*this, &Impl::isRowVisible));
view_ = Gtk::make_managed<Gtk::TreeView>(sort_);
view_->set_model(sort_);
view_->signal_button_release_event().connect([this](GdkEventButton* event)
{ return on_tree_view_button_released(view_, event); });
appendColumn(view_, message_log_cols.sequence);
appendColumn(view_, message_log_cols.name);
appendColumn(view_, message_log_cols.message);
auto* w = Gtk::make_managed<Gtk::ScrolledWindow>();
w->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
w->set_shadow_type(Gtk::SHADOW_IN);
w->add(*view_);
vbox->pack_start(*w, true, true, 0);
window_.add(*vbox);
refresh_tag_ = Glib::signal_timeout().connect_seconds(
sigc::mem_fun(*this, &Impl::onRefresh),

View File

@ -16,6 +16,11 @@ class Session;
class MessageLogWindow : public Gtk::Window
{
public:
MessageLogWindow(
BaseObjectType* cast_item,
Glib::RefPtr<Gtk::Builder> const& builder,
Gtk::Window& parent,
Glib::RefPtr<Session> const& core);
~MessageLogWindow() override;
TR_DISABLE_COPY_MOVE(MessageLogWindow)
@ -23,8 +28,6 @@ public:
static std::unique_ptr<MessageLogWindow> create(Gtk::Window& parent, Glib::RefPtr<Session> const& core);
protected:
MessageLogWindow(Gtk::Window& parent, Glib::RefPtr<Session> const& core);
void on_show() override;
void on_hide() override;

163
gtk/MessageLogWindow.ui Normal file
View File

@ -0,0 +1,163 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface domain="transmission-gtk">
<requires lib="gtk+" version="3.24"/>
<object class="GtkWindow" id="MessageLogWindow">
<property name="can-focus">False</property>
<property name="title" translatable="yes">Message Log</property>
<property name="role">message-log</property>
<property name="default-width">560</property>
<property name="default-height">350</property>
<child>
<object class="GtkBox" id="window_layout">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkToolbar" id="toolbar">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkToolButton" id="save_as_button">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="is-important">True</property>
<property name="action-name">win.save-message-log</property>
<property name="label" translatable="yes">Save _As</property>
<property name="use-underline">True</property>
<property name="icon-name">document-save-as</property>
</object>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
</packing>
</child>
<child>
<object class="GtkToolButton" id="clear_button">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="is-important">True</property>
<property name="action-name">win.clear-message-log</property>
<property name="label" translatable="yes">Clear</property>
<property name="use-underline">True</property>
<property name="icon-name">edit-clear</property>
</object>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
</packing>
</child>
<child>
<object class="GtkSeparatorToolItem">
<property name="visible">True</property>
<property name="can-focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
</packing>
</child>
<child>
<object class="GtkToggleToolButton" id="pause_button">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="is-important">True</property>
<property name="action-name">win.pause-message-log</property>
<property name="label" translatable="yes">P_ause</property>
<property name="use-underline">True</property>
<property name="icon-name">media-playback-pause</property>
</object>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
</packing>
</child>
<child>
<object class="GtkSeparatorToolItem">
<property name="visible">True</property>
<property name="can-focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
</packing>
</child>
<child>
<object class="GtkToolItem" id="level_label_item">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkLabel" id="level_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="margin-start">6</property>
<property name="margin-end">6</property>
<property name="xpad">6</property>
<property name="label" translatable="yes">Level</property>
<property name="use-underline">True</property>
<property name="mnemonic-widget">level_combo</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
</packing>
</child>
<child>
<object class="GtkToolItem" id="level_combo_item">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkComboBox" id="level_combo">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkCellRendererText" id="level_combo_renderer"/>
<attributes>
<attribute name="text">1</attribute>
</attributes>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
</packing>
</child>
<style>
<class name="primary-toolbar"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow" id="messages_view_scroll">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="shadow-type">in</property>
<child>
<object class="GtkTreeView" id="messages_view">
<property name="visible">True</property>
<property name="can-focus">True</property>
<child internal-child="selection">
<object class="GtkTreeSelection" id="messages_view_selection"/>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
</interface>

View File

@ -14,7 +14,6 @@
#include "FileList.h"
#include "FreeSpaceLabel.h"
#include "HigWorkarea.h"
#include "OptionsDialog.h"
#include "Prefs.h"
#include "PrefsDialog.h"
@ -25,10 +24,45 @@
*****
****/
namespace
{
std::string get_source_file(tr_ctor& ctor)
{
if (char const* source_file = tr_ctorGetSourceFile(&ctor); source_file != nullptr)
{
return source_file;
}
return "";
}
std::string get_download_dir(tr_ctor& ctor)
{
char const* str = nullptr;
if (!tr_ctorGetDownloadDir(&ctor, TR_FORCE, &str))
{
g_assert_not_reached();
}
g_assert(str != nullptr);
return str;
}
} // namespace
/****
*****
****/
class OptionsDialog::Impl
{
public:
Impl(OptionsDialog& dialog, Glib::RefPtr<Session> const& core, std::unique_ptr<tr_ctor, void (*)(tr_ctor*)> ctor);
Impl(
OptionsDialog& dialog,
Glib::RefPtr<Gtk::Builder> const& builder,
Glib::RefPtr<Session> const& core,
std::unique_ptr<tr_ctor, void (*)(tr_ctor*)> ctor);
TR_DISABLE_COPY_MOVE(Impl)
@ -43,17 +77,18 @@ private:
private:
OptionsDialog& dialog_;
Glib::RefPtr<Session> const core_;
std::unique_ptr<tr_ctor, void (*)(tr_ctor*)> ctor_;
std::string filename_;
std::string downloadDir_;
tr_torrent* tor_ = nullptr;
FileList* file_list_ = nullptr;
Gtk::CheckButton* run_check_ = nullptr;
Gtk::CheckButton* trash_check_ = nullptr;
Gtk::ComboBox* priority_combo_ = nullptr;
FreeSpaceLabel* freespace_label_ = nullptr;
std::string filename_;
std::string downloadDir_;
tr_torrent* tor_ = nullptr;
std::unique_ptr<tr_ctor, void (*)(tr_ctor*)> ctor_;
};
void OptionsDialog::Impl::removeOldTorrent()
@ -193,85 +228,57 @@ void addTorrentFilters(Gtk::FileChooser* chooser)
*****
****/
OptionsDialog::OptionsDialog(
BaseObjectType* cast_item,
Glib::RefPtr<Gtk::Builder> const& builder,
Gtk::Window& parent,
Glib::RefPtr<Session> const& core,
std::unique_ptr<tr_ctor, void (*)(tr_ctor*)> ctor)
: Gtk::Dialog(cast_item)
, impl_(std::make_unique<Impl>(*this, builder, core, std::move(ctor)))
{
set_transient_for(parent);
}
OptionsDialog::~OptionsDialog() = default;
std::unique_ptr<OptionsDialog> OptionsDialog::create(
Gtk::Window& parent,
Glib::RefPtr<Session> const& core,
std::unique_ptr<tr_ctor, void (*)(tr_ctor*)> ctor)
{
return std::unique_ptr<OptionsDialog>(new OptionsDialog(parent, core, std::move(ctor)));
auto const builder = Gtk::Builder::create_from_resource(gtr_get_full_resource_path("OptionsDialog.ui"));
return std::unique_ptr<OptionsDialog>(
gtr_get_widget_derived<OptionsDialog>(builder, "OptionsDialog", parent, core, std::move(ctor)));
}
OptionsDialog::OptionsDialog(
Gtk::Window& parent,
Glib::RefPtr<Session> const& core,
std::unique_ptr<tr_ctor, void (*)(tr_ctor*)> ctor)
: Gtk::Dialog(_("Torrent Options"), parent, true /* modal */)
, impl_(std::make_unique<Impl>(*this, core, std::move(ctor)))
{
}
OptionsDialog::~OptionsDialog() = default;
OptionsDialog::Impl::Impl(
OptionsDialog& dialog,
Glib::RefPtr<Gtk::Builder> const& builder,
Glib::RefPtr<Session> const& core,
std::unique_ptr<tr_ctor, void (*)(tr_ctor*)> ctor)
: dialog_(dialog)
, core_(core)
, ctor_(std::move(ctor))
, filename_(get_source_file(*ctor_))
, downloadDir_(get_download_dir(*ctor_))
, file_list_(gtr_get_widget_derived<FileList>(builder, "files_view_scroll", "files_view", core_, 0))
, run_check_(gtr_get_widget<Gtk::CheckButton>(builder, "start_check"))
, trash_check_(gtr_get_widget<Gtk::CheckButton>(builder, "trash_check"))
, priority_combo_(gtr_get_widget<Gtk::ComboBox>(builder, "priority_combo"))
, freespace_label_(gtr_get_widget_derived<FreeSpaceLabel>(builder, "free_space_label", core_, downloadDir_))
{
int row = 0;
/* make the dialog */
dialog_.add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
dialog_.add_button(_("_Open"), Gtk::RESPONSE_ACCEPT);
dialog_.set_default_response(Gtk::RESPONSE_ACCEPT);
char const* str = nullptr;
if (!tr_ctorGetDownloadDir(ctor_.get(), TR_FORCE, &str))
{
g_assert_not_reached();
}
g_assert(str != nullptr);
filename_ = tr_ctorGetSourceFile(ctor_.get()) != nullptr ? tr_ctorGetSourceFile(ctor_.get()) : "";
downloadDir_ = str;
file_list_ = Gtk::make_managed<FileList>(core_, 0);
trash_check_ = Gtk::make_managed<Gtk::CheckButton>(_("Mo_ve torrent file to the trash"), true);
run_check_ = Gtk::make_managed<Gtk::CheckButton>(_("_Start when added"), true);
priority_combo_ = gtr_priority_combo_new();
gtr_priority_combo_set_value(*priority_combo_, TR_PRI_NORMAL);
dialog.signal_response().connect(sigc::mem_fun(*this, &Impl::addResponseCB));
auto* grid = Gtk::make_managed<Gtk::Grid>();
grid->set_border_width(GUI_PAD_BIG);
grid->set_row_spacing(GUI_PAD);
grid->set_column_spacing(GUI_PAD_BIG);
gtr_priority_combo_init(*priority_combo_);
gtr_priority_combo_set_value(*priority_combo_, TR_PRI_NORMAL);
/* "torrent file" row */
auto* source_label = Gtk::make_managed<Gtk::Label>(_("_Torrent file:"), true);
source_label->set_halign(Gtk::ALIGN_START);
source_label->set_halign(Gtk::ALIGN_CENTER);
grid->attach(*source_label, 0, row, 1, 1);
auto* source_chooser = Gtk::make_managed<Gtk::FileChooserButton>(_("Select Source File"), Gtk::FILE_CHOOSER_ACTION_OPEN);
source_chooser->set_hexpand(true);
grid->attach_next_to(*source_chooser, *source_label, Gtk::POS_RIGHT);
source_label->set_mnemonic_widget(*source_chooser);
auto* source_chooser = gtr_get_widget<Gtk::FileChooserButton>(builder, "source_button");
addTorrentFilters(source_chooser);
source_chooser->signal_selection_changed().connect([this, source_chooser]() { sourceChanged(source_chooser); });
/* "destination folder" row */
row++;
auto* destination_label = Gtk::make_managed<Gtk::Label>(_("_Destination folder:"), true);
destination_label->set_halign(Gtk::ALIGN_START);
destination_label->set_valign(Gtk::ALIGN_CENTER);
grid->attach(*destination_label, 0, row, 1, 1);
auto* destination_chooser = Gtk::make_managed<Gtk::FileChooserButton>(
_("Select Destination Folder"),
Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER);
auto* destination_chooser = gtr_get_widget<Gtk::FileChooserButton>(builder, "destination_button");
if (!destination_chooser->set_current_folder(downloadDir_))
{
@ -284,36 +291,9 @@ OptionsDialog::Impl::Impl(
destination_chooser->add_shortcut_folder(folder);
}
grid->attach_next_to(*destination_chooser, *destination_label, Gtk::POS_RIGHT);
destination_label->set_mnemonic_widget(*destination_chooser);
destination_chooser->signal_selection_changed().connect([this, destination_chooser]()
{ downloadDirChanged(destination_chooser); });
row++;
freespace_label_ = Gtk::make_managed<FreeSpaceLabel>(core_, downloadDir_);
freespace_label_->set_margin_bottom(GUI_PAD_BIG);
freespace_label_->set_halign(Gtk::ALIGN_END);
freespace_label_->set_valign(Gtk::ALIGN_CENTER);
grid->attach(*freespace_label_, 0, row, 2, 1);
/* file list row */
row++;
file_list_->set_vexpand(true);
file_list_->set_size_request(466U, 300U);
grid->attach(*file_list_, 0, row, 2, 1);
/* torrent priority row */
row++;
auto* priority_label = Gtk::make_managed<Gtk::Label>(_("Torrent _priority:"), true);
priority_label->set_halign(Gtk::ALIGN_START);
priority_label->set_valign(Gtk::ALIGN_CENTER);
grid->attach(*priority_label, 0, row, 1, 1);
priority_label->set_mnemonic_widget(*priority_combo_);
grid->attach_next_to(*priority_combo_, *priority_label, Gtk::POS_RIGHT);
/* torrent priority row */
row++;
bool flag;
if (!tr_ctorGetPaused(ctor_.get(), TR_FORCE, &flag))
{
@ -321,10 +301,6 @@ OptionsDialog::Impl::Impl(
}
run_check_->set_active(!flag);
grid->attach(*run_check_, 0, row, 2, 1);
/* "trash torrent file" row */
row++;
if (!tr_ctorGetDeleteSource(ctor_.get(), &flag))
{
@ -332,7 +308,6 @@ OptionsDialog::Impl::Impl(
}
trash_check_->set_active(flag);
grid->attach(*trash_check_, 0, row, 2, 1);
/* trigger sourceChanged, either directly or indirectly,
* so that it creates the tor/gtor objects */
@ -345,7 +320,6 @@ OptionsDialog::Impl::Impl(
sourceChanged(source_chooser);
}
gtr_dialog_set_content(dialog_, *grid);
dialog_.get_widget_for_response(Gtk::RESPONSE_ACCEPT)->grab_focus();
}
@ -406,7 +380,7 @@ TorrentFileChooserDialog::TorrentFileChooserDialog(Gtk::Window& parent, Glib::Re
****
***/
void TorrentUrlChooserDialog::onOpenURLResponse(int response, Glib::RefPtr<Session> const& core)
void TorrentUrlChooserDialog::onOpenURLResponse(int response, Gtk::Entry const& entry, Glib::RefPtr<Session> const& core)
{
if (response == Gtk::RESPONSE_CANCEL)
@ -415,8 +389,7 @@ void TorrentUrlChooserDialog::onOpenURLResponse(int response, Glib::RefPtr<Sessi
}
else if (response == Gtk::RESPONSE_ACCEPT)
{
auto const* const e = static_cast<Gtk::Entry*>(get_data("url-entry"));
auto const url = gtr_str_strip(e->get_text());
auto const url = gtr_str_strip(entry.get_text());
if (url.empty())
{
@ -436,28 +409,24 @@ void TorrentUrlChooserDialog::onOpenURLResponse(int response, Glib::RefPtr<Sessi
std::unique_ptr<TorrentUrlChooserDialog> TorrentUrlChooserDialog::create(Gtk::Window& parent, Glib::RefPtr<Session> const& core)
{
return std::unique_ptr<TorrentUrlChooserDialog>(new TorrentUrlChooserDialog(parent, core));
auto const builder = Gtk::Builder::create_from_resource(gtr_get_full_resource_path("TorrentUrlChooserDialog.ui"));
return std::unique_ptr<TorrentUrlChooserDialog>(
gtr_get_widget_derived<TorrentUrlChooserDialog>(builder, "TorrentUrlChooserDialog", parent, core));
}
TorrentUrlChooserDialog::TorrentUrlChooserDialog(Gtk::Window& parent, Glib::RefPtr<Session> const& core)
: Gtk::Dialog(_("Open URL"), parent, true /* modal */)
TorrentUrlChooserDialog::TorrentUrlChooserDialog(
BaseObjectType* cast_item,
Glib::RefPtr<Gtk::Builder> const& builder,
Gtk::Window& parent,
Glib::RefPtr<Session> const& core)
: Gtk::Dialog(cast_item)
{
guint row;
set_transient_for(parent);
add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
add_button(_("_Open"), Gtk::RESPONSE_ACCEPT);
signal_response().connect([this, core](int response) { onOpenURLResponse(response, core); });
row = 0;
auto* t = Gtk::make_managed<HigWorkarea>();
t->add_section_title(row, _("Open torrent from URL"));
auto* e = Gtk::make_managed<Gtk::Entry>();
e->set_size_request(400, -1);
auto* const e = gtr_get_widget<Gtk::Entry>(builder, "url_entry");
gtr_paste_clipboard_url_into_entry(*e);
set_data("url-entry", e);
t->add_row(row, _("_URL"), *e);
gtr_dialog_set_content(*this, *t);
signal_response().connect([this, e, core](int response) { onOpenURLResponse(response, *e, core); });
if (e->get_text_length() == 0)
{

View File

@ -17,15 +17,18 @@ typedef struct tr_ctor tr_ctor;
class TorrentUrlChooserDialog : public Gtk::Dialog
{
public:
TorrentUrlChooserDialog(
BaseObjectType* cast_item,
Glib::RefPtr<Gtk::Builder> const& builder,
Gtk::Window& parent,
Glib::RefPtr<Session> const& core);
TR_DISABLE_COPY_MOVE(TorrentUrlChooserDialog)
static std::unique_ptr<TorrentUrlChooserDialog> create(Gtk::Window& parent, Glib::RefPtr<Session> const& core);
protected:
TorrentUrlChooserDialog(Gtk::Window& parent, Glib::RefPtr<Session> const& core);
private:
void onOpenURLResponse(int response, Glib::RefPtr<Session> const& core);
void onOpenURLResponse(int response, Gtk::Entry const& entry, Glib::RefPtr<Session> const& core);
};
class TorrentFileChooserDialog : public Gtk::FileChooserDialog
@ -45,6 +48,12 @@ private:
class OptionsDialog : public Gtk::Dialog
{
public:
OptionsDialog(
BaseObjectType* cast_item,
Glib::RefPtr<Gtk::Builder> const& builder,
Gtk::Window& parent,
Glib::RefPtr<Session> const& core,
std::unique_ptr<tr_ctor, void (*)(tr_ctor*)> ctor);
~OptionsDialog() override;
TR_DISABLE_COPY_MOVE(OptionsDialog)
@ -54,9 +63,6 @@ public:
Glib::RefPtr<Session> const& core,
std::unique_ptr<tr_ctor, void (*)(tr_ctor*)> ctor);
protected:
OptionsDialog(Gtk::Window& parent, Glib::RefPtr<Session> const& core, std::unique_ptr<tr_ctor, void (*)(tr_ctor*)> ctor);
private:
class Impl;
std::unique_ptr<Impl> const impl_;

252
gtk/OptionsDialog.ui Normal file
View File

@ -0,0 +1,252 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface domain="transmission-gtk">
<requires lib="gtk+" version="3.24"/>
<object class="GtkDialog" id="OptionsDialog">
<property name="can-focus">False</property>
<property name="border-width">6</property>
<property name="title" translatable="yes">Torrent Options</property>
<property name="type-hint">dialog</property>
<child internal-child="vbox">
<object class="GtkBox" id="dialog_layout">
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child internal-child="action_area">
<object class="GtkButtonBox" id="dialog_buttons">
<property name="can-focus">False</property>
<property name="layout-style">end</property>
<child>
<object class="GtkButton" id="cancel_button">
<property name="label" translatable="yes">_Cancel</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="can-default">True</property>
<property name="receives-default">True</property>
<property name="use-underline">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="open_button">
<property name="label" translatable="yes">_Open</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="can-default">True</property>
<property name="receives-default">True</property>
<property name="use-underline">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<!-- n-columns=2 n-rows=8 -->
<object class="GtkGrid" id="dialog_content_layout">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="row-spacing">6</property>
<property name="column-spacing">12</property>
<child>
<object class="GtkLabel" id="source_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">_Torrent file:</property>
<property name="use-underline">True</property>
<property name="mnemonic-widget">source_button</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object class="GtkFileChooserButton" id="source_button">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="hexpand">True</property>
<property name="title" translatable="yes">Select Source File</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="destination_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">_Destination folder:</property>
<property name="use-underline">True</property>
<property name="mnemonic-widget">destination_button</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkFileChooserButton" id="destination_button">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="hexpand">True</property>
<property name="action">select-folder</property>
<property name="title" translatable="yes">Select Destination Folder</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow" id="files_view_scroll">
<property name="width-request">466</property>
<property name="height-request">300</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="shadow-type">in</property>
<child>
<object class="GtkTreeView" id="files_view">
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="hexpand">True</property>
<property name="vexpand">True</property>
<child internal-child="selection">
<object class="GtkTreeSelection" id="files_view_selection"/>
</child>
</object>
</child>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">4</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="priority_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Torrent _priority:</property>
<property name="use-underline">True</property>
<property name="mnemonic-widget">priority_combo</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">5</property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="priority_combo">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="hexpand">True</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">5</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="start_check">
<property name="label" translatable="yes">_Start when added</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="valign">center</property>
<property name="use-underline">True</property>
<property name="draw-indicator">True</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">6</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="trash_check">
<property name="label" translatable="yes">Mo_ve torrent file to the trash</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="valign">center</property>
<property name="use-underline">True</property>
<property name="draw-indicator">True</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">7</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="free_space_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="hexpand">True</property>
<property name="label">...</property>
<property name="xalign">0</property>
<attributes>
<attribute name="style" value="italic"/>
</attributes>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">2</property>
</packing>
</child>
<child>
<object class="GtkFixed">
<property name="width-request">0</property>
<property name="height-request">0</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">2</property>
</packing>
</child>
<child>
<object class="GtkFixed">
<property name="height-request">6</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">3</property>
<property name="width">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
<action-widgets>
<action-widget response="-6">cancel_button</action-widget>
<action-widget response="-3">open_button</action-widget>
</action-widgets>
</object>
</interface>

File diff suppressed because it is too large Load Diff

View File

@ -16,15 +16,17 @@ class Session;
class PrefsDialog : public Gtk::Dialog
{
public:
PrefsDialog(
BaseObjectType* cast_item,
Glib::RefPtr<Gtk::Builder> const& builder,
Gtk::Window& parent,
Glib::RefPtr<Session> const& core);
~PrefsDialog() override;
TR_DISABLE_COPY_MOVE(PrefsDialog)
static std::unique_ptr<PrefsDialog> create(Gtk::Window& parent, Glib::RefPtr<Session> const& core);
protected:
PrefsDialog(Gtk::Window& parent, Glib::RefPtr<Session> const& core);
private:
class Impl;
std::unique_ptr<Impl> const impl_;

2114
gtk/PrefsDialog.ui Normal file

File diff suppressed because it is too large Load Diff

View File

@ -13,7 +13,6 @@
#include <libtransmission/transmission.h>
#include "HigWorkarea.h"
#include "Prefs.h" /* gtr_pref_string_get */
#include "RelocateDialog.h"
#include "Session.h"
@ -29,7 +28,11 @@ std::string targetLocation;
class RelocateDialog::Impl
{
public:
Impl(RelocateDialog& dialog, Glib::RefPtr<Session> const& core, std::vector<tr_torrent_id_t> const& torrent_ids);
Impl(
RelocateDialog& dialog,
Glib::RefPtr<Gtk::Builder> const& builder,
Glib::RefPtr<Session> const& core,
std::vector<tr_torrent_id_t> const& torrent_ids);
~Impl();
TR_DISABLE_COPY_MOVE(Impl)
@ -140,46 +143,44 @@ void RelocateDialog::Impl::onResponse(int response)
}
}
RelocateDialog::RelocateDialog(
BaseObjectType* cast_item,
Glib::RefPtr<Gtk::Builder> const& builder,
Gtk::Window& parent,
Glib::RefPtr<Session> const& core,
std::vector<int> const& torrent_ids)
: Gtk::Dialog(cast_item)
, impl_(std::make_unique<Impl>(*this, builder, core, torrent_ids))
{
set_transient_for(parent);
}
RelocateDialog::~RelocateDialog() = default;
std::unique_ptr<RelocateDialog> RelocateDialog::create(
Gtk::Window& parent,
Glib::RefPtr<Session> const& core,
std::vector<tr_torrent_id_t> const& torrent_ids)
{
return std::unique_ptr<RelocateDialog>(new RelocateDialog(parent, core, torrent_ids));
auto const builder = Gtk::Builder::create_from_resource(gtr_get_full_resource_path("RelocateDialog.ui"));
return std::unique_ptr<RelocateDialog>(
gtr_get_widget_derived<RelocateDialog>(builder, "RelocateDialog", parent, core, torrent_ids));
}
RelocateDialog::RelocateDialog(
Gtk::Window& parent,
Glib::RefPtr<Session> const& core,
std::vector<tr_torrent_id_t> const& torrent_ids)
: Gtk::Dialog(_("Set Torrent Location"), parent, true)
, impl_(std::make_unique<Impl>(*this, core, torrent_ids))
{
}
RelocateDialog::~RelocateDialog() = default;
RelocateDialog::Impl::Impl(
RelocateDialog& dialog,
Glib::RefPtr<Gtk::Builder> const& builder,
Glib::RefPtr<Session> const& core,
std::vector<tr_torrent_id_t> const& torrent_ids)
: dialog_(dialog)
, core_(core)
, torrent_ids_(torrent_ids)
, chooser_(gtr_get_widget<Gtk::FileChooserButton>(builder, "new_location_button"))
, move_tb_(gtr_get_widget<Gtk::RadioButton>(builder, "move_data_radio"))
{
guint row;
dialog_.add_button(_("_Cancel"), Gtk::RESPONSE_CANCEL);
dialog_.add_button(_("_Apply"), Gtk::RESPONSE_APPLY);
dialog_.set_default_response(Gtk::RESPONSE_CANCEL);
dialog_.signal_response().connect(sigc::mem_fun(*this, &Impl::onResponse));
row = 0;
auto* t = Gtk::make_managed<HigWorkarea>();
t->add_section_title(row, _("Location"));
chooser_ = Gtk::make_managed<Gtk::FileChooserButton>(_("Set Torrent Location"), Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER);
auto recent_dirs = gtr_get_recent_dirs("relocate");
if (recent_dirs.empty())
{
@ -199,15 +200,4 @@ RelocateDialog::Impl::Impl(
chooser_->add_shortcut_folder(folder);
}
}
t->add_row(row, _("Torrent _location:"), *chooser_);
Gtk::RadioButton::Group group;
move_tb_ = Gtk::make_managed<Gtk::RadioButton>(group, _("_Move from the current folder"), true);
t->add_wide_control(row, *move_tb_);
t->add_wide_control(row, *Gtk::make_managed<Gtk::RadioButton>(group, _("Local data is _already there"), true));
gtr_dialog_set_content(dialog_, *t);
}

View File

@ -17,6 +17,12 @@ class Session;
class RelocateDialog : public Gtk::Dialog
{
public:
RelocateDialog(
BaseObjectType* cast_item,
Glib::RefPtr<Gtk::Builder> const& builder,
Gtk::Window& parent,
Glib::RefPtr<Session> const& core,
std::vector<tr_torrent_id_t> const& torrent_ids);
~RelocateDialog() override;
TR_DISABLE_COPY_MOVE(RelocateDialog)
@ -26,9 +32,6 @@ public:
Glib::RefPtr<Session> const& core,
std::vector<tr_torrent_id_t> const& torrent_ids);
protected:
RelocateDialog(Gtk::Window& parent, Glib::RefPtr<Session> const& core, std::vector<tr_torrent_id_t> const& torrent_ids);
private:
class Impl;
std::unique_ptr<Impl> const impl_;

171
gtk/RelocateDialog.ui Normal file
View File

@ -0,0 +1,171 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface domain="transmission-gtk">
<requires lib="gtk+" version="3.24"/>
<object class="GtkDialog" id="RelocateDialog">
<property name="can-focus">False</property>
<property name="border-width">6</property>
<property name="title" translatable="yes">Set Torrent Location</property>
<property name="type-hint">dialog</property>
<child internal-child="vbox">
<object class="GtkBox" id="dialog_layout">
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child internal-child="action_area">
<object class="GtkButtonBox" id="dialog_buttons">
<property name="can-focus">False</property>
<property name="layout-style">end</property>
<child>
<object class="GtkButton" id="cancel_button">
<property name="label" translatable="yes">_Cancel</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="can-default">True</property>
<property name="receives-default">True</property>
<property name="use-underline">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="apply_button">
<property name="label" translatable="yes">_Apply</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="can-default">True</property>
<property name="receives-default">True</property>
<property name="use-underline">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox" id="dialog_content_layout">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel" id="set_location_section_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="hexpand">True</property>
<property name="label" translatable="yes">Location</property>
<property name="xalign">0</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<!-- n-columns=2 n-rows=3 -->
<object class="GtkGrid" id="set_location_section_layout">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="margin-start">18</property>
<property name="row-spacing">6</property>
<property name="column-spacing">12</property>
<child>
<object class="GtkLabel" id="new_location_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="label" translatable="yes">Torrent _location:</property>
<property name="use-underline">True</property>
<property name="mnemonic-widget">new_location_button</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object class="GtkFileChooserButton" id="new_location_button">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="hexpand">True</property>
<property name="action">select-folder</property>
<property name="title" translatable="yes"/>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object class="GtkRadioButton" id="move_data_radio">
<property name="label" translatable="yes">_Move from the current folder</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="valign">center</property>
<property name="use-underline">True</property>
<property name="active">True</property>
<property name="draw-indicator">True</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">1</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkRadioButton" id="find_data_radio">
<property name="label" translatable="yes">Local data is _already there</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">False</property>
<property name="valign">center</property>
<property name="use-underline">True</property>
<property name="active">True</property>
<property name="draw-indicator">True</property>
<property name="group">move_data_radio</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">2</property>
<property name="width">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
</object>
</child>
<action-widgets>
<action-widget response="-6">cancel_button</action-widget>
<action-widget response="-10">apply_button</action-widget>
</action-widgets>
</object>
</interface>

View File

@ -10,7 +10,6 @@
#include <fmt/core.h>
#include "HigWorkarea.h"
#include "PrefsDialog.h"
#include "Session.h"
#include "StatsDialog.h"
@ -21,7 +20,7 @@ static auto constexpr TR_RESPONSE_RESET = int{ 1 };
class StatsDialog::Impl
{
public:
Impl(StatsDialog& dialog, Glib::RefPtr<Session> const& core);
Impl(StatsDialog& dialog, Glib::RefPtr<Gtk::Builder> const& builder, Glib::RefPtr<Session> const& core);
~Impl();
TR_DISABLE_COPY_MOVE(Impl)
@ -116,70 +115,42 @@ void StatsDialog::Impl::dialogResponse(int response)
}
}
std::unique_ptr<StatsDialog> StatsDialog::create(Gtk::Window& parent, Glib::RefPtr<Session> const& core)
{
return std::unique_ptr<StatsDialog>(new StatsDialog(parent, core));
}
StatsDialog::StatsDialog(Gtk::Window& parent, Glib::RefPtr<Session> const& core)
: Gtk::Dialog(_("Statistics"), parent)
, impl_(std::make_unique<Impl>(*this, core))
StatsDialog::StatsDialog(
BaseObjectType* cast_item,
Glib::RefPtr<Gtk::Builder> const& builder,
Gtk::Window& parent,
Glib::RefPtr<Session> const& core)
: Gtk::Dialog(cast_item)
, impl_(std::make_unique<Impl>(*this, builder, core))
{
set_transient_for(parent);
}
StatsDialog::~StatsDialog() = default;
StatsDialog::Impl::Impl(StatsDialog& dialog, Glib::RefPtr<Session> const& core)
std::unique_ptr<StatsDialog> StatsDialog::create(Gtk::Window& parent, Glib::RefPtr<Session> const& core)
{
auto const builder = Gtk::Builder::create_from_resource(gtr_get_full_resource_path("StatsDialog.ui"));
return std::unique_ptr<StatsDialog>(gtr_get_widget_derived<StatsDialog>(builder, "StatsDialog", parent, core));
}
StatsDialog::Impl::Impl(StatsDialog& dialog, Glib::RefPtr<Gtk::Builder> const& builder, Glib::RefPtr<Session> const& core)
: dialog_(dialog)
, core_(core)
, one_up_lb_(gtr_get_widget<Gtk::Label>(builder, "current_uploaded_value_label"))
, one_down_lb_(gtr_get_widget<Gtk::Label>(builder, "current_downloaded_value_label"))
, one_ratio_lb_(gtr_get_widget<Gtk::Label>(builder, "current_ratio_value_label"))
, one_time_lb_(gtr_get_widget<Gtk::Label>(builder, "current_duration_value_label"))
, all_up_lb_(gtr_get_widget<Gtk::Label>(builder, "total_uploaded_value_label"))
, all_down_lb_(gtr_get_widget<Gtk::Label>(builder, "total_downloaded_value_label"))
, all_ratio_lb_(gtr_get_widget<Gtk::Label>(builder, "total_ratio_value_label"))
, all_time_lb_(gtr_get_widget<Gtk::Label>(builder, "total_duration_value_label"))
, all_sessions_lb_(gtr_get_widget<Gtk::Label>(builder, "start_count_label"))
{
guint row = 0;
dialog_.add_button(_("_Reset"), TR_RESPONSE_RESET);
dialog_.add_button(_("_Close"), Gtk::RESPONSE_CLOSE);
dialog_.set_default_response(Gtk::RESPONSE_CLOSE);
auto* t = Gtk::make_managed<HigWorkarea>();
t->add_section_title(row, _("Current Session"));
one_up_lb_ = Gtk::make_managed<Gtk::Label>();
one_up_lb_->set_single_line_mode(true);
t->add_row(row, _("Uploaded:"), *one_up_lb_);
one_down_lb_ = Gtk::make_managed<Gtk::Label>();
one_down_lb_->set_single_line_mode(true);
t->add_row(row, _("Downloaded:"), *one_down_lb_);
one_ratio_lb_ = Gtk::make_managed<Gtk::Label>();
one_ratio_lb_->set_single_line_mode(true);
t->add_row(row, _("Ratio:"), *one_ratio_lb_);
one_time_lb_ = Gtk::make_managed<Gtk::Label>();
one_time_lb_->set_single_line_mode(true);
t->add_row(row, _("Duration:"), *one_time_lb_);
t->add_section_divider(row);
t->add_section_title(row, _("Total"));
all_sessions_lb_ = Gtk::make_managed<Gtk::Label>(startedTimesText(1));
all_sessions_lb_->set_single_line_mode(true);
t->add_label_w(row, *all_sessions_lb_);
++row;
all_up_lb_ = Gtk::make_managed<Gtk::Label>();
all_up_lb_->set_single_line_mode(true);
t->add_row(row, _("Uploaded:"), *all_up_lb_);
all_down_lb_ = Gtk::make_managed<Gtk::Label>();
all_down_lb_->set_single_line_mode(true);
t->add_row(row, _("Downloaded:"), *all_down_lb_);
all_ratio_lb_ = Gtk::make_managed<Gtk::Label>();
all_ratio_lb_->set_single_line_mode(true);
t->add_row(row, _("Ratio:"), *all_ratio_lb_);
all_time_lb_ = Gtk::make_managed<Gtk::Label>();
all_time_lb_->set_single_line_mode(true);
t->add_row(row, _("Duration:"), *all_time_lb_);
gtr_dialog_set_content(dialog_, *t);
dialog_.signal_response().connect(sigc::mem_fun(*this, &Impl::dialogResponse));
updateStats();
dialog_.signal_response().connect(sigc::mem_fun(*this, &Impl::dialogResponse));
update_stats_tag_ = Glib::signal_timeout().connect_seconds(
sigc::mem_fun(*this, &Impl::updateStats),
SECONDARY_WINDOW_REFRESH_INTERVAL_SECONDS);

View File

@ -16,15 +16,17 @@ class Session;
class StatsDialog : public Gtk::Dialog
{
public:
StatsDialog(
BaseObjectType* cast_item,
Glib::RefPtr<Gtk::Builder> const& builder,
Gtk::Window& parent,
Glib::RefPtr<Session> const& core);
~StatsDialog() override;
TR_DISABLE_COPY_MOVE(StatsDialog)
static std::unique_ptr<StatsDialog> create(Gtk::Window& parent, Glib::RefPtr<Session> const& core);
protected:
StatsDialog(Gtk::Window& parent, Glib::RefPtr<Session> const& core);
private:
class Impl;
std::unique_ptr<Impl> const impl_;

379
gtk/StatsDialog.ui Normal file
View File

@ -0,0 +1,379 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface domain="transmission-gtk">
<requires lib="gtk+" version="3.24"/>
<object class="GtkDialog" id="StatsDialog">
<property name="can-focus">False</property>
<property name="border-width">6</property>
<property name="title" translatable="yes">Statistics</property>
<property name="type-hint">dialog</property>
<child internal-child="vbox">
<object class="GtkBox" id="dialog_layout">
<property name="can-focus">False</property>
<property name="margin-top">1</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child internal-child="action_area">
<object class="GtkButtonBox" id="dialog_buttons">
<property name="can-focus">False</property>
<property name="layout-style">end</property>
<child>
<object class="GtkButton" id="reset_button">
<property name="label" translatable="yes">_Reset</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="can-default">True</property>
<property name="receives-default">True</property>
<property name="use-underline">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="close_button">
<property name="label" translatable="yes">_Close</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="can-default">True</property>
<property name="receives-default">True</property>
<property name="use-underline">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox" id="dialog_content_layout">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel" id="current_session_section_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="hexpand">True</property>
<property name="label" translatable="yes">Current Session</property>
<property name="xalign">0</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<!-- n-columns=2 n-rows=4 -->
<object class="GtkGrid" id="current_session_section_layout">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="margin-start">18</property>
<property name="row-spacing">6</property>
<property name="column-spacing">12</property>
<child>
<object class="GtkLabel" id="current_uploaded_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Uploaded:</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="current_uploaded_value_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="hexpand">True</property>
<property name="label">...</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="current_downloaded_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Downloaded:</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="current_downloaded_value_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="hexpand">True</property>
<property name="label">...</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="current_ratio_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Ratio:</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="current_ratio_value_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="hexpand">True</property>
<property name="label">...</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="current_duration_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Duration:</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">3</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="current_duration_value_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="hexpand">True</property>
<property name="label">...</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">3</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkFixed" id="total_section_spacer">
<property name="height-request">6</property>
<property name="visible">True</property>
<property name="can-focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="total_section_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="hexpand">True</property>
<property name="label" translatable="yes">Total</property>
<property name="xalign">0</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
<child>
<!-- n-columns=2 n-rows=5 -->
<object class="GtkGrid" id="total_section_layout">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="margin-start">18</property>
<property name="row-spacing">6</property>
<property name="column-spacing">12</property>
<child>
<object class="GtkLabel" id="start_count_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="hexpand">True</property>
<property name="label">...</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">0</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="total_uploaded_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Uploaded:</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="total_uploaded_value_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="hexpand">True</property>
<property name="label">...</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="total_downloaded_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Downloaded:</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="total_downloaded_value_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="hexpand">True</property>
<property name="label">...</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">2</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="total_ratio_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Ratio:</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">3</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="total_ratio_value_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="hexpand">True</property>
<property name="label">...</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">3</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="total_duration_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Duration:</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">4</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="total_duration_value_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="hexpand">True</property>
<property name="label">...</property>
<property name="xalign">0</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">4</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">4</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
</child>
<action-widgets>
<action-widget response="1">reset_button</action-widget>
<action-widget response="-7">close_button</action-widget>
</action-widgets>
</object>
<object class="GtkSizeGroup" id="labels_width_group">
<widgets>
<widget name="current_uploaded_label"/>
<widget name="current_downloaded_label"/>
<widget name="current_ratio_label"/>
<widget name="current_duration_label"/>
<widget name="total_uploaded_label"/>
<widget name="total_downloaded_label"/>
<widget name="total_ratio_label"/>
<widget name="total_duration_label"/>
</widgets>
</object>
</interface>

View File

@ -19,7 +19,7 @@
#include <libtransmission/transmission.h>
#include <libtransmission/utils.h> /* tr_truncd() */
#include "HigWorkarea.h"
#include "HigWorkarea.h" // GUI_PAD, GUI_PAD_SMALL
#include "IconCache.h"
#include "TorrentCellRenderer.h"
#include "Utils.h"

View File

@ -0,0 +1,130 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface>
<requires lib="gtk+" version="3.24"/>
<object class="GtkDialog" id="TorrentUrlChooserDialog">
<property name="can-focus">False</property>
<property name="border-width">6</property>
<property name="title" translatable="yes">Open URL</property>
<property name="type-hint">dialog</property>
<child internal-child="vbox">
<object class="GtkBox" id="dialog_layout">
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child internal-child="action_area">
<object class="GtkButtonBox" id="dialog_buttons">
<property name="can-focus">False</property>
<property name="layout-style">end</property>
<child>
<object class="GtkButton" id="cancel_button">
<property name="label" translatable="yes">_Cancel</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="use-underline">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="open_button">
<property name="label" translatable="yes">_Open</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="receives-default">True</property>
<property name="use-underline">True</property>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox" id="dialog_content_layout">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property>
<child>
<object class="GtkLabel" id="url_section_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Open torrent from URL</property>
<property name="xalign">0</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<!-- n-columns=2 n-rows=1 -->
<object class="GtkGrid" id="url_section_layout">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="margin-start">18</property>
<property name="row-spacing">6</property>
<property name="column-spacing">12</property>
<child>
<object class="GtkLabel" id="url_label">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">_URL</property>
<property name="use-underline">True</property>
<property name="mnemonic-widget">url_entry</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">0</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="url_entry">
<property name="width-request">400</property>
<property name="visible">True</property>
<property name="can-focus">True</property>
<property name="hexpand">True</property>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">0</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
<action-widgets>
<action-widget response="-6">cancel_button</action-widget>
<action-widget response="-3">open_button</action-widget>
</action-widgets>
</object>
</interface>

View File

@ -24,7 +24,6 @@
#include <libtransmission/version.h> /* SHORT_VERSION_STRING */
#include <libtransmission/web-utils.h>
#include "HigWorkarea.h"
#include "Prefs.h"
#include "PrefsDialog.h"
#include "Session.h"
@ -446,6 +445,13 @@ void gtr_combo_box_set_active_enum(Gtk::ComboBox& combo_box, int value)
}
Gtk::ComboBox* gtr_combo_box_new_enum(std::vector<std::pair<Glib::ustring, int>> const& items)
{
auto w = Gtk::make_managed<Gtk::ComboBox>();
gtr_combo_box_set_enum(*w, items);
return w;
}
void gtr_combo_box_set_enum(Gtk::ComboBox& combo, std::vector<std::pair<Glib::ustring, int>> const& items)
{
auto store = Gtk::ListStore::create(enum_combo_cols);
@ -456,12 +462,12 @@ Gtk::ComboBox* gtr_combo_box_new_enum(std::vector<std::pair<Glib::ustring, int>>
(*iter)[enum_combo_cols.label] = label;
}
auto w = Gtk::make_managed<Gtk::ComboBox>(static_cast<Glib::RefPtr<Gtk::TreeModel> const&>(store));
auto* r = Gtk::make_managed<Gtk::CellRendererText>();
w->pack_start(*r, true);
w->add_attribute(r->property_text(), enum_combo_cols.label);
combo.clear();
combo.set_model(store);
return w;
auto* r = Gtk::make_managed<Gtk::CellRendererText>();
combo.pack_start(*r, true);
combo.add_attribute(r->property_text(), enum_combo_cols.label);
}
int gtr_combo_box_get_active_enum(Gtk::ComboBox const& combo_box)
@ -478,11 +484,20 @@ int gtr_combo_box_get_active_enum(Gtk::ComboBox const& combo_box)
Gtk::ComboBox* gtr_priority_combo_new()
{
return gtr_combo_box_new_enum({
{ _("High"), TR_PRI_HIGH },
{ _("Normal"), TR_PRI_NORMAL },
{ _("Low"), TR_PRI_LOW },
});
auto w = Gtk::make_managed<Gtk::ComboBox>();
gtr_priority_combo_init(*w);
return w;
}
void gtr_priority_combo_init(Gtk::ComboBox& combo)
{
gtr_combo_box_set_enum(
combo,
{
{ _("High"), TR_PRI_HIGH },
{ _("Normal"), TR_PRI_NORMAL },
{ _("Low"), TR_PRI_LOW },
});
}
/***

View File

@ -88,10 +88,12 @@ void gtr_dialog_set_content(Gtk::Dialog& dialog, Gtk::Widget& content);
***/
Gtk::ComboBox* gtr_priority_combo_new();
void gtr_priority_combo_init(Gtk::ComboBox& combo);
#define gtr_priority_combo_get_value(w) gtr_combo_box_get_active_enum(w)
#define gtr_priority_combo_set_value(w, val) gtr_combo_box_set_active_enum(w, val)
Gtk::ComboBox* gtr_combo_box_new_enum(std::vector<std::pair<Glib::ustring, int>> const& items);
void gtr_combo_box_set_enum(Gtk::ComboBox& combo, std::vector<std::pair<Glib::ustring, int>> const& items);
int gtr_combo_box_get_active_enum(Gtk::ComboBox const&);
void gtr_combo_box_set_active_enum(Gtk::ComboBox&, int value);
@ -229,6 +231,22 @@ struct fmt::formatter<Glib::ustring> : formatter<std::string>
}
};
template<typename T, typename... ArgTs>
T* gtr_get_widget(Glib::RefPtr<Gtk::Builder> const& builder, Glib::ustring const& name, ArgTs&&... args)
{
T* widget = nullptr;
builder->get_widget(name, widget, std::forward<ArgTs>(args)...);
return widget;
}
template<typename T, typename... ArgTs>
T* gtr_get_widget_derived(Glib::RefPtr<Gtk::Builder> const& builder, Glib::ustring const& name, ArgTs&&... args)
{
T* widget = nullptr;
builder->get_widget_derived(name, widget, std::forward<ArgTs>(args)...);
return widget;
}
namespace Glib
{

9
gtk/transmission-ui.css Normal file
View File

@ -0,0 +1,9 @@
.tr-workarea.frame {
border-left-width: 0;
border-right-width: 0;
border-radius: 0;
}
.tr-small {
font-size: small;
}

View File

@ -285,97 +285,6 @@
</submenu>
</menu>
<object class="GtkToolbar" id="main-window-toolbar">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkToolButton">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">Open a torrent</property>
<property name="is-important">True</property>
<property name="action-name">win.open-torrent</property>
<property name="label" translatable="yes">_Open</property>
<property name="use-underline">True</property>
<property name="icon-name">document-open</property>
</object>
<packing>
<property name="expand">False</property>
<property name="homogeneous">False</property>
</packing>
</child>
<child>
<object class="GtkToolButton">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">Start torrent</property>
<property name="action-name">win.torrent-start</property>
<property name="label" translatable="yes">_Start</property>
<property name="use-underline">True</property>
<property name="icon-name">media-playback-start</property>
</object>
<packing>
<property name="expand">False</property>
<property name="homogeneous">False</property>
</packing>
</child>
<child>
<object class="GtkToolButton">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">Pause torrent</property>
<property name="action-name">win.torrent-stop</property>
<property name="label" translatable="yes">_Pause</property>
<property name="use-underline">True</property>
<property name="icon-name">media-playback-pause</property>
</object>
<packing>
<property name="expand">False</property>
<property name="homogeneous">False</property>
</packing>
</child>
<child>
<object class="GtkToolButton">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="action-name">win.remove-torrent</property>
<property name="label" translatable="yes">Remove torrent</property>
<property name="use-underline">True</property>
<property name="icon-name">list-remove</property>
</object>
<packing>
<property name="expand">False</property>
<property name="homogeneous">False</property>
</packing>
</child>
<child>
<object class="GtkSeparatorToolItem">
<property name="visible">True</property>
<property name="can-focus">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
</packing>
</child>
<child>
<object class="GtkToolButton">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">Torrent properties</property>
<property name="is-important">True</property>
<property name="action-name">win.show-torrent-properties</property>
<property name="label" translatable="yes">_Properties</property>
<property name="use-underline">True</property>
<property name="icon-name">document-properties</property>
</object>
<packing>
<property name="expand">False</property>
<property name="homogeneous">False</property>
</packing>
</child>
</object>
<menu id="main-window-popup">
<section>
<item>

View File

@ -6,6 +6,20 @@
<file alias="icons/scalable/actions/ratio-symbolic.svg">icons/ratio-symbolic.svg</file>
<file alias="icons/scalable/actions/turtle-symbolic.svg">icons/turtle-symbolic.svg</file>
<file alias="icons/scalable/apps/transmission.svg" compressed="true" preprocess="xml-stripblanks">icons/hicolor_apps_scalable_transmission.svg</file>
<file compressed="true">transmission-ui.css</file>
<file compressed="true" preprocess="xml-stripblanks">transmission-ui.xml</file>
<file compressed="true" preprocess="xml-stripblanks">AddTrackerDialog.ui</file>
<file compressed="true" preprocess="xml-stripblanks">DetailsDialog.ui</file>
<file compressed="true" preprocess="xml-stripblanks">EditTrackersDialog.ui</file>
<file compressed="true" preprocess="xml-stripblanks">FilterBar.ui</file>
<file compressed="true" preprocess="xml-stripblanks">MainWindow.ui</file>
<file compressed="true" preprocess="xml-stripblanks">MakeDialog.ui</file>
<file compressed="true" preprocess="xml-stripblanks">MakeProgressDialog.ui</file>
<file compressed="true" preprocess="xml-stripblanks">MessageLogWindow.ui</file>
<file compressed="true" preprocess="xml-stripblanks">OptionsDialog.ui</file>
<file compressed="true" preprocess="xml-stripblanks">PrefsDialog.ui</file>
<file compressed="true" preprocess="xml-stripblanks">RelocateDialog.ui</file>
<file compressed="true" preprocess="xml-stripblanks">StatsDialog.ui</file>
<file compressed="true" preprocess="xml-stripblanks">TorrentUrlChooserDialog.ui</file>
</gresource>
</gresources>