From 2e2851dc13d73352530dd4495c7e05603b2e520d Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 10 Apr 2024 23:38:38 +0200 Subject: Adding upstream version 2.1.2~dev0+20240219. Signed-off-by: Daniel Baumann --- deluge/ui/gtk3/options_tab.py | 220 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 220 insertions(+) create mode 100644 deluge/ui/gtk3/options_tab.py (limited to 'deluge/ui/gtk3/options_tab.py') 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 +# 2017 Calum Lind +# +# 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) -- cgit v1.2.3