diff --git a/deluge/ui/gtkui/glade/main_window.tabs.ui b/deluge/ui/gtkui/glade/main_window.tabs.ui index b54361860..e6b959409 100644 --- a/deluge/ui/gtkui/glade/main_window.tabs.ui +++ b/deluge/ui/gtkui/glade/main_window.tabs.ui @@ -1597,13 +1597,13 @@ 0.69999998807907104 - gtk-apply + _Apply False True False True True - True + True diff --git a/deluge/ui/gtkui/options_tab.py b/deluge/ui/gtkui/options_tab.py index 18e647504..16e323d85 100644 --- a/deluge/ui/gtkui/options_tab.py +++ b/deluge/ui/gtkui/options_tab.py @@ -22,8 +22,9 @@ class OptionsTab(Tab): def __init__(self): super(OptionsTab, self).__init__('Options', 'options_tab', 'options_tab_label') - self.prev_torrent_id = None + 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']) @@ -66,56 +67,89 @@ class OptionsTab(Tab): def stop(self): pass + def clear(self): + self.prev_torrent_ids = None + self.prev_status = None + self.inconsistent_keys = [] + def update(self): - # Get the first selected torrent torrent_ids = component.get('TorrentView').get_selected_torrents() - # Only use the first torrent in the list or return if None selected + # Set True if torrent(s) selected in torrentview, else False. + self._child_widget.set_sensitive(bool(torrent_ids)) + if torrent_ids: - torrent_id = torrent_ids[0] - self._child_widget.set_sensitive(True) + 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: - # No torrent is selected in the torrentview - self._child_widget.set_sensitive(False) - return + 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')) - if torrent_id != self.prev_torrent_id: - self.prev_status = None + self.on_get_torrent_status(status) - component.get('SessionProxy').get_torrent_status( - torrent_id, self.status_keys - ).addCallback(self.on_get_torrent_status) - - self.prev_torrent_id = torrent_id - - def clear(self): - self.prev_torrent_id = None - self.prev_status = None - - def on_get_torrent_status(self, 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(status, None) + self.prev_status = dict.fromkeys(new_status, None) - if status != self.prev_status: + if new_status != self.prev_status: for widget in self.tab_widgets.values(): status_key = widget.status_keys[0] - if status[status_key] != self.prev_status[status_key]: + 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[status_key]) + getattr(widget.obj, set_func)(status_value) + if set_func == 'set_active': + widget.obj.set_inconsistent(status_key in self.inconsistent_keys) - if status['move_completed_path'] != self.prev_status['move_completed_path']: - self.move_completed_path_chooser.set_text( - status['move_completed_path'], cursor_end=False, default_text=True) + 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(status['stop_at_ratio']) - self.tab_widgets['chk_remove_at_ratio'].obj.set_sensitive(status['stop_at_ratio']) + 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 = status + self.prev_status = new_status + +# === Widget signal handlers === # def on_button_apply_clicked(self, button): options = {} @@ -124,26 +158,27 @@ class OptionsTab(Tab): 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]: + 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 if options.get('move_completed', False): options['move_completed_path'] = self.move_completed_path_chooser.get_text() - client.core.set_torrent_options([self.prev_torrent_id], options) + 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.button_apply.set_sensitive(True) + self.on_chk_toggled(widget) def on_chk_stop_at_ratio_toggled(self, widget): - is_active = widget.get_active() - self.tab_widgets['spin_stop_ratio'].obj.set_sensitive(is_active) - self.tab_widgets['chk_remove_at_ratio'].obj.set_sensitive(is_active) - self.button_apply.set_sensitive(True) + 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):