summaryrefslogtreecommitdiffstats
path: root/deluge/ui/gtk3/options_tab.py
diff options
context:
space:
mode:
Diffstat (limited to 'deluge/ui/gtk3/options_tab.py')
-rw-r--r--deluge/ui/gtk3/options_tab.py220
1 files changed, 220 insertions, 0 deletions
diff --git a/deluge/ui/gtk3/options_tab.py b/deluge/ui/gtk3/options_tab.py
new file mode 100644
index 0000000..b0411a8
--- /dev/null
+++ b/deluge/ui/gtk3/options_tab.py
@@ -0,0 +1,220 @@
+#
+# Copyright (C) 2008 Andrew Resch <andrewresch@gmail.com>
+# 2017 Calum Lind <calumlind+deluge@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.
+#
+
+from gi.repository.Gdk import keyval_name
+
+import deluge.component as component
+from deluge.ui.client import client
+
+from .path_chooser import PathChooser
+from .torrentdetails import Tab
+
+
+class OptionsTab(Tab):
+ def __init__(self):
+ super().__init__('Options', 'options_tab', 'options_tab_label')
+
+ self.prev_torrent_ids = None
+ self.prev_status = None
+ self.inconsistent_keys = []
+
+ # Create TabWidget items with widget id, get/set func name, status key.
+ self.add_tab_widget('spin_max_download', 'value', ['max_download_speed'])
+ self.add_tab_widget('spin_max_upload', 'value', ['max_upload_speed'])
+ self.add_tab_widget('spin_max_connections', 'value_as_int', ['max_connections'])
+ self.add_tab_widget(
+ 'spin_max_upload_slots', 'value_as_int', ['max_upload_slots']
+ )
+ self.add_tab_widget(
+ 'chk_prioritize_first_last', 'active', ['prioritize_first_last_pieces']
+ )
+ self.add_tab_widget(
+ 'chk_sequential_download', 'active', ['sequential_download']
+ )
+ self.add_tab_widget('chk_auto_managed', 'active', ['auto_managed'])
+ self.add_tab_widget('chk_stop_at_ratio', 'active', ['stop_at_ratio'])
+ self.add_tab_widget('chk_remove_at_ratio', 'active', ['remove_at_ratio'])
+ self.add_tab_widget('spin_stop_ratio', 'value', ['stop_ratio'])
+ self.add_tab_widget('chk_move_completed', 'active', ['move_completed'])
+ self.add_tab_widget('chk_shared', 'active', ['shared'])
+ self.add_tab_widget('summary_owner', 'text', ['owner'])
+ self.add_tab_widget('chk_super_seeding', 'active', ['super_seeding'])
+
+ # Connect key press event for spin widgets.
+ for widget_id in self.tab_widgets:
+ if widget_id.startswith('spin_'):
+ self.tab_widgets[widget_id].obj.connect(
+ 'key-press-event', self.on_key_press_event
+ )
+
+ self.button_apply = self.main_builder.get_object('button_apply')
+
+ self.move_completed_path_chooser = PathChooser(
+ 'move_completed_paths_list', parent=component.get('MainWindow').window
+ )
+ self.move_completed_path_chooser.set_sensitive(
+ self.tab_widgets['chk_move_completed'].obj.get_active()
+ )
+ self.move_completed_path_chooser.connect(
+ 'text-changed', self.on_path_chooser_text_changed_event
+ )
+ self.status_keys.append('move_completed_path')
+
+ self.move_completed_hbox = self.main_builder.get_object(
+ 'hbox_move_completed_path_chooser'
+ )
+ self.move_completed_hbox.add(self.move_completed_path_chooser)
+ self.move_completed_hbox.show_all()
+
+ component.get('MainWindow').connect_signals(self)
+
+ def start(self):
+ pass
+
+ def stop(self):
+ pass
+
+ def clear(self):
+ self.prev_torrent_ids = None
+ self.prev_status = None
+ self.inconsistent_keys = []
+
+ def update(self):
+ torrent_ids = component.get('TorrentView').get_selected_torrents()
+
+ # Set True if torrent(s) selected in torrentview, else False.
+ self._child_widget.set_sensitive(bool(torrent_ids))
+
+ if torrent_ids:
+ if torrent_ids != self.prev_torrent_ids:
+ self.clear()
+
+ component.get('SessionProxy').get_torrents_status(
+ {'id': torrent_ids}, self.status_keys
+ ).addCallback(self.parse_torrents_statuses)
+
+ self.prev_torrent_ids = torrent_ids
+
+ def parse_torrents_statuses(self, statuses):
+ """Finds common status values to all torrrents in statuses.
+
+ Values which differ are replaced with config values.
+
+
+ Args:
+ statuses (dict): A status dict of {torrent_id: {key: value}}.
+
+ Returns:
+ dict: A single status dict.
+
+ """
+ status = {}
+ if len(statuses) == 1:
+ # A single torrent so pop torrent status.
+ status = statuses.popitem()[1]
+ self.button_apply.set_label('_Apply')
+ else:
+ for status_key in self.status_keys:
+ prev_value = None
+ for idx, status in enumerate(statuses.values()):
+ if idx == 0:
+ prev_value = status[status_key]
+ continue
+ elif status[status_key] != prev_value:
+ self.inconsistent_keys.append(status_key)
+ break
+ status[status_key] = prev_value
+ self.button_apply.set_label(_('_Apply to selected'))
+
+ self.on_get_torrent_status(status)
+
+ def on_get_torrent_status(self, new_status):
+ # So we don't overwrite the user's unapplied changes we only
+ # want to update values that have been applied in the core.
+ if self.prev_status is None:
+ self.prev_status = dict.fromkeys(new_status, None)
+
+ if new_status != self.prev_status:
+ for widget in self.tab_widgets.values():
+ status_key = widget.status_keys[0]
+ status_value = new_status[status_key]
+ if status_value != self.prev_status[status_key]:
+ set_func = 'set_' + widget.func.replace('_as_int', '')
+ getattr(widget.obj, set_func)(status_value)
+ if set_func == 'set_active':
+ widget.obj.set_inconsistent(
+ status_key in self.inconsistent_keys
+ )
+
+ if (
+ new_status['move_completed_path']
+ != self.prev_status['move_completed_path']
+ ):
+ text = new_status['move_completed_path']
+ self.move_completed_path_chooser.set_text(
+ text, cursor_end=False, default_text=True
+ )
+
+ # Update sensitivity of widgets.
+ self.tab_widgets['spin_stop_ratio'].obj.set_sensitive(
+ new_status['stop_at_ratio']
+ )
+ self.tab_widgets['chk_remove_at_ratio'].obj.set_sensitive(
+ new_status['stop_at_ratio']
+ )
+
+ # Ensure apply button sensitivity is set False.
+ self.button_apply.set_sensitive(False)
+ self.prev_status = new_status
+
+ # === Widget signal handlers === #
+
+ def on_button_apply_clicked(self, button):
+ options = {}
+ for widget in self.tab_widgets.values():
+ status_key = widget.status_keys[0]
+ if status_key == 'owner':
+ continue # A label so read-only
+ widget_value = getattr(widget.obj, 'get_' + widget.func)()
+ if widget_value != self.prev_status[status_key] or (
+ status_key in self.inconsistent_keys
+ and not widget.obj.get_inconsistent()
+ ):
+ options[status_key] = widget_value
+
+ move_completed_path = self.move_completed_path_chooser.get_text()
+ if move_completed_path != self.prev_status['move_completed_path']:
+ options['move_completed_path'] = move_completed_path
+
+ client.core.set_torrent_options(self.prev_torrent_ids, options)
+ self.button_apply.set_sensitive(False)
+
+ def on_chk_move_completed_toggled(self, widget):
+ self.move_completed_path_chooser.set_sensitive(widget.get_active())
+ self.on_chk_toggled(widget)
+
+ def on_chk_stop_at_ratio_toggled(self, widget):
+ self.tab_widgets['spin_stop_ratio'].obj.set_sensitive(widget.get_active())
+ self.tab_widgets['chk_remove_at_ratio'].obj.set_sensitive(widget.get_active())
+ self.on_chk_toggled(widget)
+
+ def on_chk_toggled(self, widget):
+ widget.set_inconsistent(False)
+ self.button_apply.set_sensitive(True)
+
+ def on_spin_value_changed(self, widget):
+ self.button_apply.set_sensitive(True)
+
+ def on_key_press_event(self, widget, event):
+ keyname = keyval_name(event.keyval).lstrip('KP_').lower()
+ if keyname.isdigit() or keyname in ['period', 'minus', 'delete', 'backspace']:
+ self.button_apply.set_sensitive(True)
+
+ def on_path_chooser_text_changed_event(self, widget, path):
+ self.button_apply.set_sensitive(True)