if move_completed is checked/True, options should be updated, not the other way round The path was updated the first time the move_completed option is selected and then ignored on further updated to the path. Fixed by checking instead if the path has changed. Closes: https://github.com/deluge-torrent/deluge/pull/374
221 lines
8.5 KiB
Python
221 lines
8.5 KiB
Python
#
|
|
# 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)
|