diff options
Diffstat (limited to 'deluge/plugins/Label/deluge_label/gtkui')
4 files changed, 453 insertions, 0 deletions
diff --git a/deluge/plugins/Label/deluge_label/gtkui/__init__.py b/deluge/plugins/Label/deluge_label/gtkui/__init__.py new file mode 100644 index 0000000..6170716 --- /dev/null +++ b/deluge/plugins/Label/deluge_label/gtkui/__init__.py @@ -0,0 +1,74 @@ +# +# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com> +# +# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with +# the additional special exception to link portions of this program with the OpenSSL library. +# See LICENSE for more details. +# + +import logging + +from deluge import component # for systray +from deluge.plugins.pluginbase import Gtk3PluginBase + +from . import label_config, sidebar_menu, submenu + +log = logging.getLogger(__name__) + +NO_LABEL = 'No Label' + + +def cell_data_label(column, cell, model, row, data): + cell.set_property('text', str(model.get_value(row, data))) + + +class GtkUI(Gtk3PluginBase): + def start(self): + if self.label_menu: + self.label_menu.on_show() + + def enable(self): + self.plugin = component.get('PluginManager') + self.torrentmenu = component.get('MenuBar').torrentmenu + self.label_menu = None + self.labelcfg = None + self.sidebar_menu = None + self.load_interface() + + def disable(self): + if self.label_menu in self.torrentmenu.get_children(): + self.torrentmenu.remove(self.label_menu) + + self.labelcfg.unload() + self.sidebar_menu.unload() + del self.sidebar_menu + + component.get('TorrentView').remove_column(_('Label')) + + def load_interface(self): + # sidebar + # disabled + if not self.sidebar_menu: + self.sidebar_menu = sidebar_menu.LabelSidebarMenu() + # self.sidebar.load() + + # menu: + log.debug('add items to torrentview-popup menu.') + self.label_menu = submenu.LabelMenu() + self.torrentmenu.append(self.label_menu) + self.label_menu.show_all() + + # columns: + self.load_columns() + + # config: + if not self.labelcfg: + self.labelcfg = label_config.LabelConfig(self.plugin) + self.labelcfg.load() + + log.debug('Finished loading Label plugin') + + def load_columns(self): + log.debug('add columns') + + component.get('TorrentView').add_text_column(_('Label'), status_field=['label']) diff --git a/deluge/plugins/Label/deluge_label/gtkui/label_config.py b/deluge/plugins/Label/deluge_label/gtkui/label_config.py new file mode 100644 index 0000000..26c827e --- /dev/null +++ b/deluge/plugins/Label/deluge_label/gtkui/label_config.py @@ -0,0 +1,58 @@ +# +# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com> +# +# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with +# the additional special exception to link portions of this program with the OpenSSL library. +# See LICENSE for more details. +# + +import logging + +from gi.repository.Gtk import Builder + +from deluge.ui.client import client + +from ..common import get_resource + +log = logging.getLogger(__name__) + + +class LabelConfig: + """ + there used to be some options here... + """ + + def __init__(self, plugin): + self.plugin = plugin + + def load(self): + log.debug('Adding Label Preferences page') + builder = Builder() + builder.add_from_file(get_resource('label_pref.ui')) + + self.plugin.add_preferences_page( + _('Label'), builder.get_object('label_prefs_box') + ) + self.plugin.register_hook('on_show_prefs', self.load_settings) + self.plugin.register_hook('on_apply_prefs', self.on_apply_prefs) + + self.load_settings() + + def unload(self): + self.plugin.remove_preferences_page(_('Label')) + self.plugin.deregister_hook('on_apply_prefs', self.on_apply_prefs) + self.plugin.deregister_hook('on_show_prefs', self.load_settings) + + def load_settings(self, widget=None, data=None): + client.label.get_config().addCallback(self.cb_global_options) + + def cb_global_options(self, options): + log.debug('options=%s', options) + + # for id in self.chk_ids: + # self.glade.get_widget(id).set_active(bool(options[id])) + + def on_apply_prefs(self): + options = {} + # update options dict here. + client.label.set_config(options) diff --git a/deluge/plugins/Label/deluge_label/gtkui/sidebar_menu.py b/deluge/plugins/Label/deluge_label/gtkui/sidebar_menu.py new file mode 100644 index 0000000..9d164b2 --- /dev/null +++ b/deluge/plugins/Label/deluge_label/gtkui/sidebar_menu.py @@ -0,0 +1,259 @@ +# +# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com> +# Copyright (C) 2007 Andrew Resch <andrewresch@gmail.com> +# +# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with +# the additional special exception to link portions of this program with the OpenSSL library. +# See LICENSE for more details. +# + +import logging + +import gi # isort:skip (Required before Gtk import). + +gi.require_version('Gtk', '3.0') + +# isort:imports-thirdparty +from gi.repository import Gtk + +# isort:imports-firstparty +import deluge.component as component +from deluge.ui.client import client + +# isort:imports-localfolder +from ..common import get_resource + +log = logging.getLogger(__name__) + +NO_LABEL = 'No Label' + + +# menu +class LabelSidebarMenu: + def __init__(self): + self.treeview = component.get('FilterTreeView') + self.menu = self.treeview.menu + self.items = [] + + # add items, in reverse order, because they are prepended. + sep = Gtk.SeparatorMenuItem() + self.items.append(sep) + self.menu.prepend(sep) + self._add_item('options', _('Label _Options')) + self._add_item('remove', _('_Remove Label')) + self._add_item('add', _('_Add Label')) + + self.menu.show_all() + # dialogs: + self.add_dialog = AddDialog() + self.options_dialog = OptionsDialog() + # hooks: + self.menu.connect('show', self.on_show, None) + + def _add_item(self, item_id, label): + """ + id is automatically-added as self.item_<id> + """ + item = Gtk.MenuItem.new_with_mnemonic(label) + func = getattr(self, 'on_%s' % item_id) + item.connect('activate', func) + self.menu.prepend(item) + setattr(self, 'item_%s' % item_id, item) + self.items.append(item) + return item + + def on_add(self, event=None): + self.add_dialog.show() + + def on_remove(self, event=None): + client.label.remove(self.treeview.value) + + def on_options(self, event=None): + self.options_dialog.show(self.treeview.value) + + def on_show(self, widget=None, data=None): + """No Label:disable options/del.""" + log.debug('label-sidebar-popup:on-show') + + cat = self.treeview.cat + label = self.treeview.value + if cat == 'label' or (cat == 'cat' and label == 'label'): + # is a label : show menu-items + for item in self.items: + item.show() + # default items + sensitive = (label not in (NO_LABEL, None, '', 'All')) and (cat != 'cat') + for item in self.items: + item.set_sensitive(sensitive) + + # add is always enabled. + self.item_add.set_sensitive(True) + else: + # not a label -->hide everything. + for item in self.items: + item.hide() + + def unload(self): + log.debug('disable01') + for item in list(self.items): + item.hide() + item.destroy() + log.debug('disable02') + self.items = [] + + +# dialogs: +class AddDialog: + def __init__(self): + pass + + def show(self): + self.builder = Gtk.Builder() + self.builder.add_from_file(get_resource('label_add.ui')) + self.dialog = self.builder.get_object('dlg_label_add') + self.dialog.set_transient_for(component.get('MainWindow').window) + + self.builder.connect_signals(self) + self.dialog.run() + + def on_add_ok(self, event=None): + value = self.builder.get_object('txt_add').get_text() + client.label.add(value) + self.dialog.destroy() + + def on_add_cancel(self, event=None): + self.dialog.destroy() + + +class OptionsDialog: + spin_ids = ['max_download_speed', 'max_upload_speed', 'stop_ratio'] + spin_int_ids = ['max_upload_slots', 'max_connections'] + chk_ids = [ + 'apply_max', + 'apply_queue', + 'stop_at_ratio', + 'apply_queue', + 'remove_at_ratio', + 'apply_move_completed', + 'move_completed', + 'is_auto_managed', + 'auto_add', + ] + + # list of tuples, because order matters when nesting. + sensitive_groups = [ + ( + 'apply_max', + [ + 'max_download_speed', + 'max_upload_speed', + 'max_upload_slots', + 'max_connections', + ], + ), + ('apply_queue', ['is_auto_managed', 'stop_at_ratio']), + ('stop_at_ratio', ['remove_at_ratio', 'stop_ratio']), # nested + ('apply_move_completed', ['move_completed']), + ('move_completed', ['move_completed_path']), # nested + ('auto_add', ['auto_add_trackers']), + ] + + def __init__(self): + pass + + def show(self, label): + self.label = label + self.builder = Gtk.Builder() + self.builder.add_from_file(get_resource('label_options.ui')) + self.dialog = self.builder.get_object('dlg_label_options') + self.dialog.set_transient_for(component.get('MainWindow').window) + self.builder.connect_signals(self) + # Show the label name in the header label + self.builder.get_object('label_header').set_markup( + '<b>{}:</b> {}'.format(_('Label Options'), self.label) + ) + + for chk_id, group in self.sensitive_groups: + chk = self.builder.get_object(chk_id) + chk.connect('toggled', self.apply_sensitivity) + + client.label.get_options(self.label).addCallback(self.load_options) + + self.dialog.run() + + def load_options(self, options): + log.debug(list(options)) + + for spin_id in self.spin_ids + self.spin_int_ids: + self.builder.get_object(spin_id).set_value(options[spin_id]) + for chk_id in self.chk_ids: + self.builder.get_object(chk_id).set_active(bool(options[chk_id])) + + if client.is_localhost(): + self.builder.get_object('move_completed_path').set_filename( + options['move_completed_path'] + ) + self.builder.get_object('move_completed_path').show() + self.builder.get_object('move_completed_path_entry').hide() + else: + self.builder.get_object('move_completed_path_entry').set_text( + options['move_completed_path'] + ) + self.builder.get_object('move_completed_path_entry').show() + self.builder.get_object('move_completed_path').hide() + + self.builder.get_object('auto_add_trackers').get_buffer().set_text( + '\n'.join(options['auto_add_trackers']) + ) + + self.apply_sensitivity() + + def on_options_ok(self, event=None): + """Save options.""" + options = {} + + for spin_id in self.spin_ids: + options[spin_id] = self.builder.get_object(spin_id).get_value() + for spin_int_id in self.spin_int_ids: + options[spin_int_id] = self.builder.get_object( + spin_int_id + ).get_value_as_int() + for chk_id in self.chk_ids: + options[chk_id] = self.builder.get_object(chk_id).get_active() + + if client.is_localhost(): + options['move_completed_path'] = self.builder.get_object( + 'move_completed_path' + ).get_filename() + else: + options['move_completed_path'] = self.builder.get_object( + 'move_completed_path_entry' + ).get_text() + + buff = self.builder.get_object( + 'auto_add_trackers' + ).get_buffer() # sometimes I hate gtk... + tracker_lst = ( + buff.get_text( + buff.get_start_iter(), buff.get_end_iter(), include_hidden_chars=False + ) + .strip() + .split('\n') + ) + options['auto_add_trackers'] = [ + x for x in tracker_lst if x + ] # filter out empty lines. + + log.debug(options) + client.label.set_options(self.label, options) + self.dialog.destroy() + + def apply_sensitivity(self, event=None): + for chk_id, sensitive_list in self.sensitive_groups: + chk = self.builder.get_object(chk_id) + sens = chk.get_active() and chk.get_property('sensitive') + for widget_id in sensitive_list: + self.builder.get_object(widget_id).set_sensitive(sens) + + def on_options_cancel(self, event=None): + self.dialog.destroy() diff --git a/deluge/plugins/Label/deluge_label/gtkui/submenu.py b/deluge/plugins/Label/deluge_label/gtkui/submenu.py new file mode 100644 index 0000000..54b6594 --- /dev/null +++ b/deluge/plugins/Label/deluge_label/gtkui/submenu.py @@ -0,0 +1,62 @@ +# +# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com> +# +# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with +# the additional special exception to link portions of this program with the OpenSSL library. +# See LICENSE for more details. +# + + +import logging + +from gi.repository.Gtk import Menu, MenuItem + +from deluge import component # for systray +from deluge.ui.client import client + +log = logging.getLogger(__name__) + + +# Deferred Translation +def _(message): + return message + + +NO_LABEL = _('No Label') +del _ + + +class LabelMenu(MenuItem): + def __init__(self): + MenuItem.__init__(self, _('Label')) # noqa: F821 + + self.sub_menu = Menu() + self.set_submenu(self.sub_menu) + self.items = [] + + # attach.. + self.sub_menu.connect('show', self.on_show, None) + + def get_torrent_ids(self): + return component.get('TorrentView').get_selected_torrents() + + def on_show(self, widget=None, data=None): + log.debug('label-on-show') + client.label.get_labels().addCallback(self.cb_labels) + + def cb_labels(self, labels): + for child in self.sub_menu.get_children(): + self.sub_menu.remove(child) + for label in [NO_LABEL] + list(labels): + if label == NO_LABEL: + item = MenuItem(_(NO_LABEL)) # noqa: F821 + else: + item = MenuItem(label) + item.connect('activate', self.on_select_label, label) + self.sub_menu.append(item) + self.show_all() + + def on_select_label(self, widget=None, label_id=None): + log.debug('select label:%s,%s', label_id, self.get_torrent_ids()) + for torrent_id in self.get_torrent_ids(): + client.label.set_torrent(torrent_id, label_id) |