- Preparation work for using six or future module for Py2/3 compat. The code will be written in Python 3 with Python 2 fallbacks. - Added some Py3 imports with Py2 fallbacks to make it easier to remove Py2 code in future. - Replace xrange with range (sort out import as top of files in future). - Workaround Py2to3 basestring issue with inline if in instances. This means every usage of basestring is more considered. - Replace iteritems and itervalues for items and values. There might be a performance penalty on Py2 so might need to revisit this change.
1207 lines
56 KiB
Python
1207 lines
56 KiB
Python
# -*- coding: utf-8 -*-
|
|
#
|
|
# Copyright (C) 2007, 2008 Andrew Resch <andrewresch@gmail.com>
|
|
# Copyright (C) 2011 Pedro Algarvio <pedro@algarvio.me>
|
|
#
|
|
# 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 __future__ import unicode_literals
|
|
|
|
import logging
|
|
import os
|
|
from hashlib import sha1 as sha
|
|
from urlparse import urlparse
|
|
|
|
import gtk
|
|
from gtk.gdk import Color
|
|
|
|
import deluge.common
|
|
import deluge.component as component
|
|
from deluge.configmanager import ConfigManager, get_config_dir
|
|
from deluge.error import AuthManagerError, NotAuthorizedError
|
|
from deluge.ui.client import client
|
|
from deluge.ui.common import DISK_CACHE_KEYS
|
|
from deluge.ui.gtkui.common import associate_magnet_links, get_deluge_icon
|
|
from deluge.ui.gtkui.dialogs import AccountDialog, ErrorDialog, InformationDialog, YesNoDialog
|
|
from deluge.ui.gtkui.path_chooser import PathChooser
|
|
from deluge.ui.translations_util import get_languages
|
|
|
|
try:
|
|
import appindicator
|
|
except ImportError:
|
|
appindicator = False
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
ACCOUNTS_USERNAME, ACCOUNTS_LEVEL, ACCOUNTS_PASSWORD = list(range(3))
|
|
COLOR_MISSING, COLOR_WAITING, COLOR_DOWNLOADING, COLOR_COMPLETED = list(range(4))
|
|
|
|
COLOR_STATES = {
|
|
'missing': COLOR_MISSING,
|
|
'waiting': COLOR_WAITING,
|
|
'downloading': COLOR_DOWNLOADING,
|
|
'completed': COLOR_COMPLETED
|
|
}
|
|
|
|
|
|
class Preferences(component.Component):
|
|
def __init__(self):
|
|
component.Component.__init__(self, 'Preferences')
|
|
self.builder = gtk.Builder()
|
|
self.builder.add_from_file(deluge.common.resource_filename(
|
|
'deluge.ui.gtkui', os.path.join('glade', 'preferences_dialog.ui')))
|
|
self.pref_dialog = self.builder.get_object('pref_dialog')
|
|
self.pref_dialog.set_transient_for(component.get('MainWindow').window)
|
|
self.pref_dialog.set_icon(get_deluge_icon())
|
|
self.treeview = self.builder.get_object('treeview')
|
|
self.notebook = self.builder.get_object('notebook')
|
|
self.gtkui_config = ConfigManager('gtkui.conf')
|
|
self.window_open = False
|
|
|
|
self.load_pref_dialog_state()
|
|
|
|
self.builder.get_object('image_magnet').set_from_file(
|
|
deluge.common.get_pixmap('magnet.png'))
|
|
|
|
# Hide the unused associate magnet button on OSX see: #2420
|
|
if deluge.common.osx_check():
|
|
self.builder.get_object('button_associate_magnet').hide()
|
|
|
|
# Setup the liststore for the categories (tab pages)
|
|
self.liststore = gtk.ListStore(int, str)
|
|
self.treeview.set_model(self.liststore)
|
|
render = gtk.CellRendererText()
|
|
column = gtk.TreeViewColumn(_('Categories'), render, text=1)
|
|
self.treeview.append_column(column)
|
|
# Add the default categories
|
|
i = 0
|
|
for category in (_('Interface'), _('Downloads'), _('Bandwidth'), _('Queue'), _('Network'),
|
|
_('Proxy'), _('Cache'), _('Other'), _('Daemon'), _('Plugins'), '_separator_'):
|
|
self.liststore.append([i, category])
|
|
i += 1
|
|
|
|
def set_separator(model, _iter, data=None):
|
|
if model.get_value(_iter, 1) == '_separator_':
|
|
return True
|
|
self.treeview.set_row_separator_func(set_separator, None)
|
|
|
|
# Setup accounts tab lisview
|
|
self.accounts_levels_mapping = None
|
|
self.accounts_authlevel = self.builder.get_object('accounts_authlevel')
|
|
self.accounts_liststore = gtk.ListStore(str, str, str, int)
|
|
self.accounts_liststore.set_sort_column_id(ACCOUNTS_USERNAME, gtk.SORT_ASCENDING)
|
|
self.accounts_listview = self.builder.get_object('accounts_listview')
|
|
self.accounts_listview.append_column(
|
|
gtk.TreeViewColumn(_('Username'), gtk.CellRendererText(), text=ACCOUNTS_USERNAME))
|
|
self.accounts_listview.append_column(
|
|
gtk.TreeViewColumn(_('Level'), gtk.CellRendererText(), text=ACCOUNTS_LEVEL))
|
|
password_column = gtk.TreeViewColumn('password', gtk.CellRendererText(), text=ACCOUNTS_PASSWORD)
|
|
self.accounts_listview.append_column(password_column)
|
|
password_column.set_visible(False)
|
|
self.accounts_listview.set_model(self.accounts_liststore)
|
|
|
|
self.accounts_listview.get_selection().connect('changed', self._on_accounts_selection_changed)
|
|
self.accounts_frame = self.builder.get_object('AccountsFrame')
|
|
|
|
# Setup plugin tab listview
|
|
# The third entry is for holding translated plugin names
|
|
self.plugin_liststore = gtk.ListStore(str, bool, str)
|
|
self.plugin_liststore.set_sort_column_id(0, gtk.SORT_ASCENDING)
|
|
self.plugin_listview = self.builder.get_object('plugin_listview')
|
|
self.plugin_listview.set_model(self.plugin_liststore)
|
|
render = gtk.CellRendererToggle()
|
|
render.connect('toggled', self.on_plugin_toggled)
|
|
render.set_property('activatable', True)
|
|
self.plugin_listview.append_column(gtk.TreeViewColumn(_('Enabled'), render, active=1))
|
|
self.plugin_listview.append_column(gtk.TreeViewColumn(_('Plugin'), gtk.CellRendererText(), text=2))
|
|
|
|
# Connect to the 'changed' event of TreeViewSelection to get selection
|
|
# changes.
|
|
self.treeview.get_selection().connect('changed', self.on_selection_changed)
|
|
|
|
self.plugin_listview.get_selection().connect('changed', self.on_plugin_selection_changed)
|
|
|
|
self.builder.connect_signals({
|
|
'on_pref_dialog_delete_event': self.on_pref_dialog_delete_event,
|
|
'on_button_ok_clicked': self.on_button_ok_clicked,
|
|
'on_button_apply_clicked': self.on_button_apply_clicked,
|
|
'on_button_cancel_clicked': self.on_button_cancel_clicked,
|
|
'on_toggle': self.on_toggle,
|
|
'on_test_port_clicked': self.on_test_port_clicked,
|
|
'on_button_plugin_install_clicked': self._on_button_plugin_install_clicked,
|
|
'on_button_rescan_plugins_clicked': self._on_button_rescan_plugins_clicked,
|
|
'on_button_find_plugins_clicked': self._on_button_find_plugins_clicked,
|
|
'on_button_cache_refresh_clicked': self._on_button_cache_refresh_clicked,
|
|
'on_combo_encryption_changed': self._on_combo_encryption_changed,
|
|
'on_combo_proxy_type_changed': self._on_combo_proxy_type_changed,
|
|
'on_entry_proxy_host_paste_clipboard': self._on_entry_proxy_host_paste_clipboard,
|
|
'on_button_associate_magnet_clicked': self._on_button_associate_magnet_clicked,
|
|
'on_accounts_add_clicked': self._on_accounts_add_clicked,
|
|
'on_accounts_delete_clicked': self._on_accounts_delete_clicked,
|
|
'on_accounts_edit_clicked': self._on_accounts_edit_clicked,
|
|
'on_piecesbar_toggle_toggled': self._on_piecesbar_toggle_toggled,
|
|
'on_completed_color_set': self._on_completed_color_set,
|
|
'on_revert_color_completed_clicked': self._on_revert_color_completed_clicked,
|
|
'on_downloading_color_set': self._on_downloading_color_set,
|
|
'on_revert_color_downloading_clicked': self._on_revert_color_downloading_clicked,
|
|
'on_waiting_color_set': self._on_waiting_color_set,
|
|
'on_revert_color_waiting_clicked': self._on_revert_color_waiting_clicked,
|
|
'on_missing_color_set': self._on_missing_color_set,
|
|
'on_revert_color_missing_clicked': self._on_revert_color_missing_clicked,
|
|
'on_pref_dialog_configure_event': self.on_pref_dialog_configure_event,
|
|
'on_checkbutton_language_toggled': self._on_checkbutton_language_toggled,
|
|
})
|
|
|
|
# Radio buttons to choose between systray and appindicator
|
|
self.builder.get_object('alignment_tray_type').set_visible(bool(appindicator))
|
|
|
|
from deluge.ui.gtkui.gtkui import DEFAULT_PREFS
|
|
self.COLOR_DEFAULTS = {}
|
|
for key in ('missing', 'waiting', 'downloading', 'completed'):
|
|
self.COLOR_DEFAULTS[key] = DEFAULT_PREFS['pieces_color_%s' % key][:]
|
|
del DEFAULT_PREFS
|
|
|
|
# These get updated by requests done to the core
|
|
self.all_plugins = []
|
|
self.enabled_plugins = []
|
|
|
|
self.setup_path_choosers()
|
|
self.load_languages()
|
|
|
|
def setup_path_choosers(self):
|
|
self.download_location_hbox = self.builder.get_object('hbox_download_to_path_chooser')
|
|
self.download_location_path_chooser = PathChooser('download_location_paths_list')
|
|
self.download_location_hbox.add(self.download_location_path_chooser)
|
|
self.download_location_hbox.show_all()
|
|
|
|
self.move_completed_hbox = self.builder.get_object('hbox_move_completed_to_path_chooser')
|
|
self.move_completed_path_chooser = PathChooser('move_completed_paths_list')
|
|
self.move_completed_hbox.add(self.move_completed_path_chooser)
|
|
self.move_completed_hbox.show_all()
|
|
|
|
self.copy_torrents_to_hbox = self.builder.get_object('hbox_copy_torrent_files_path_chooser')
|
|
self.copy_torrent_files_path_chooser = PathChooser('copy_torrent_files_to_paths_list')
|
|
self.copy_torrents_to_hbox.add(self.copy_torrent_files_path_chooser)
|
|
self.copy_torrents_to_hbox.show_all()
|
|
|
|
def load_languages(self):
|
|
self.language_combo = self.builder.get_object('combobox_language')
|
|
self.language_checkbox = self.builder.get_object('checkbutton_language')
|
|
lang_model = self.language_combo.get_model()
|
|
langs = get_languages()
|
|
index = -1
|
|
for i, l in enumerate(langs):
|
|
lang_code, name = l
|
|
lang_model.append([lang_code, name])
|
|
if self.gtkui_config['language'] == lang_code:
|
|
index = i
|
|
|
|
if self.gtkui_config['language'] is None:
|
|
self.language_checkbox.set_active(True)
|
|
self.language_combo.set_visible(False)
|
|
else:
|
|
self.language_combo.set_visible(True)
|
|
if index != -1:
|
|
self.language_combo.set_active(index)
|
|
|
|
def __del__(self):
|
|
del self.gtkui_config
|
|
|
|
def add_page(self, name, widget):
|
|
"""Add a another page to the notebook"""
|
|
# Create a header and scrolled window for the preferences tab
|
|
parent = widget.get_parent()
|
|
if parent:
|
|
parent.remove(widget)
|
|
vbox = gtk.VBox()
|
|
label = gtk.Label()
|
|
label.set_use_markup(True)
|
|
label.set_markup('<b><i><big>' + name + '</big></i></b>')
|
|
label.set_alignment(0.00, 0.50)
|
|
label.set_padding(10, 10)
|
|
vbox.pack_start(label, False, True, 0)
|
|
sep = gtk.HSeparator()
|
|
vbox.pack_start(sep, False, True, 0)
|
|
align = gtk.Alignment()
|
|
align.set_padding(5, 0, 0, 0)
|
|
align.set(0, 0, 1, 1)
|
|
align.add(widget)
|
|
vbox.pack_start(align, True, True, 0)
|
|
scrolled = gtk.ScrolledWindow()
|
|
viewport = gtk.Viewport()
|
|
viewport.set_shadow_type(gtk.SHADOW_NONE)
|
|
viewport.add(vbox)
|
|
scrolled.add(viewport)
|
|
scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
|
|
scrolled.show_all()
|
|
# Add this page to the notebook
|
|
index = self.notebook.append_page(scrolled, None)
|
|
self.liststore.append([index, name])
|
|
return name
|
|
|
|
def remove_page(self, name):
|
|
"""Removes a page from the notebook"""
|
|
self.page_num_to_remove = None
|
|
self.iter_to_remove = None
|
|
|
|
def check_row(model, path, _iter, user_data):
|
|
row_name = model.get_value(_iter, 1)
|
|
if row_name == user_data:
|
|
# This is the row we need to remove
|
|
self.page_num_to_remove = model.get_value(_iter, 0)
|
|
self.iter_to_remove = _iter
|
|
return
|
|
|
|
self.liststore.foreach(check_row, name)
|
|
# Remove the page and row
|
|
if self.page_num_to_remove is not None:
|
|
self.notebook.remove_page(self.page_num_to_remove)
|
|
if self.iter_to_remove is not None:
|
|
self.liststore.remove(self.iter_to_remove)
|
|
|
|
# We need to re-adjust the index values for the remaining pages
|
|
for i, (index, name) in enumerate(self.liststore):
|
|
self.liststore[i][0] = i
|
|
|
|
def show(self, page=None):
|
|
"""Page should be the string in the left list.. ie, 'Network' or
|
|
'Bandwidth'"""
|
|
self.window_open = True
|
|
if page is not None:
|
|
for (index, string) in self.liststore:
|
|
if page == string:
|
|
self.treeview.get_selection().select_path(index)
|
|
break
|
|
|
|
component.get('PluginManager').run_on_show_prefs()
|
|
|
|
# Update the preferences dialog to reflect current config settings
|
|
self.core_config = {}
|
|
if client.connected():
|
|
self._get_accounts_tab_data()
|
|
|
|
def _on_get_config(config):
|
|
self.core_config = config
|
|
client.core.get_available_plugins().addCallback(_on_get_available_plugins)
|
|
|
|
def _on_get_available_plugins(plugins):
|
|
self.all_plugins = plugins
|
|
client.core.get_enabled_plugins().addCallback(_on_get_enabled_plugins)
|
|
|
|
def _on_get_enabled_plugins(plugins):
|
|
self.enabled_plugins = plugins
|
|
client.core.get_listen_port().addCallback(_on_get_listen_port)
|
|
|
|
def _on_get_listen_port(port):
|
|
self.active_port = port
|
|
client.core.get_session_status(DISK_CACHE_KEYS).addCallback(_on_get_session_status)
|
|
|
|
def _on_get_session_status(status):
|
|
self.cache_status = status
|
|
self._show()
|
|
|
|
# This starts a series of client.core requests prior to showing the window
|
|
client.core.get_config().addCallback(_on_get_config)
|
|
else:
|
|
self._show()
|
|
|
|
def start(self):
|
|
if self.window_open:
|
|
self.show()
|
|
|
|
def stop(self):
|
|
self.core_config = None
|
|
if self.window_open:
|
|
self._show()
|
|
|
|
def _show(self):
|
|
self.is_connected = self.core_config != {} and self.core_config is not None
|
|
core_widgets = {
|
|
'chk_move_completed': ('active', 'move_completed'),
|
|
'chk_copy_torrent_file': ('active', 'copy_torrent_file'),
|
|
'chk_del_copy_torrent_file': ('active', 'del_copy_torrent_file'),
|
|
'chk_pre_allocation': ('active', 'pre_allocate_storage'),
|
|
'chk_prioritize_first_last_pieces': ('active', 'prioritize_first_last_pieces'),
|
|
'chk_sequential_download': ('active', 'sequential_download'),
|
|
'chk_add_paused': ('active', 'add_paused'),
|
|
'active_port_label': ('text', lambda: str(self.active_port)),
|
|
'spin_incoming_port': ('value', lambda: self.core_config['listen_ports'][0]),
|
|
'chk_random_incoming_port': ('active', 'random_port'),
|
|
'spin_outgoing_port_min': ('value', lambda: self.core_config['outgoing_ports'][0]),
|
|
'spin_outgoing_port_max': ('value', lambda: self.core_config['outgoing_ports'][1]),
|
|
'chk_random_outgoing_ports': ('active', 'random_outgoing_ports'),
|
|
'entry_interface': ('text', 'listen_interface'),
|
|
'entry_peer_tos': ('text', 'peer_tos'),
|
|
'chk_dht': ('active', 'dht'),
|
|
'chk_upnp': ('active', 'upnp'),
|
|
'chk_natpmp': ('active', 'natpmp'),
|
|
'chk_utpex': ('active', 'utpex'),
|
|
'chk_lsd': ('active', 'lsd'),
|
|
'chk_new_releases': ('active', 'new_release_check'),
|
|
'chk_send_info': ('active', 'send_info'),
|
|
'entry_geoip': ('text', 'geoip_db_location'),
|
|
'combo_encin': ('active', 'enc_in_policy'),
|
|
'combo_encout': ('active', 'enc_out_policy'),
|
|
'combo_enclevel': ('active', 'enc_level'),
|
|
'spin_max_connections_global': ('value', 'max_connections_global'),
|
|
'spin_max_download': ('value', 'max_download_speed'),
|
|
'spin_max_upload': ('value', 'max_upload_speed'),
|
|
'spin_max_upload_slots_global': ('value', 'max_upload_slots_global'),
|
|
'spin_max_half_open_connections': ('value', 'max_connections_per_second'),
|
|
'spin_max_connections_per_second': ('value', 'max_connections_per_second'),
|
|
'chk_ignore_limits_on_local_network': ('active', 'ignore_limits_on_local_network'),
|
|
'chk_rate_limit_ip_overhead': ('active', 'rate_limit_ip_overhead'),
|
|
|
|
'spin_max_connections_per_torrent': ('value', 'max_connections_per_torrent'),
|
|
'spin_max_upload_slots_per_torrent': ('value', 'max_upload_slots_per_torrent'),
|
|
'spin_max_download_per_torrent': ('value', 'max_download_speed_per_torrent'),
|
|
'spin_max_upload_per_torrent': ('value', 'max_upload_speed_per_torrent'),
|
|
'spin_daemon_port': ('value', 'daemon_port'),
|
|
'chk_allow_remote_connections': ('active', 'allow_remote'),
|
|
'spin_active': ('value', 'max_active_limit'),
|
|
'spin_seeding': ('value', 'max_active_seeding'),
|
|
'spin_downloading': ('value', 'max_active_downloading'),
|
|
'chk_dont_count_slow_torrents': ('active', 'dont_count_slow_torrents'),
|
|
'chk_auto_manage_prefer_seeds': ('active', 'auto_manage_prefer_seeds'),
|
|
'chk_queue_new_top': ('active', 'queue_new_to_top'),
|
|
'spin_share_ratio_limit': ('value', 'share_ratio_limit'),
|
|
'spin_seed_time_ratio_limit': ('value', 'seed_time_ratio_limit'),
|
|
'spin_seed_time_limit': ('value', 'seed_time_limit'),
|
|
'chk_share_ratio': ('active', 'stop_seed_at_ratio'),
|
|
'spin_share_ratio': ('value', 'stop_seed_ratio'),
|
|
'radio_pause_ratio': ('active', 'stop_seed_at_ratio'),
|
|
'radio_remove_ratio': ('active', 'remove_seed_at_ratio'),
|
|
'spin_cache_size': ('value', 'cache_size'),
|
|
'spin_cache_expiry': ('value', 'cache_expiry'),
|
|
'combo_proxy_type': ('active', lambda: self.core_config['proxy']['type']),
|
|
'entry_proxy_user': ('text', lambda: self.core_config['proxy']['username']),
|
|
'entry_proxy_pass': ('text', lambda: self.core_config['proxy']['password']),
|
|
'entry_proxy_host': ('text', lambda: self.core_config['proxy']['hostname']),
|
|
'spin_proxy_port': ('value', lambda: self.core_config['proxy']['port']),
|
|
'chk_proxy_host_resolve': ('active', lambda: self.core_config['proxy']['proxy_hostnames']),
|
|
'chk_proxy_peer_conn': ('active', lambda: self.core_config['proxy']['proxy_peer_connections']),
|
|
'chk_proxy_tracker_conn': ('active', lambda: self.core_config['proxy']['proxy_tracker_connections']),
|
|
'chk_force_proxy': ('active', lambda: self.core_config['proxy']['force_proxy']),
|
|
'chk_anonymous_mode': ('active', lambda: self.core_config['proxy']['anonymous_mode']),
|
|
'accounts_add': (None, None),
|
|
'accounts_listview': (None, None),
|
|
'button_cache_refresh': (None, None),
|
|
'button_plugin_install': (None, None),
|
|
'button_rescan_plugins': (None, None),
|
|
'button_find_plugins': (None, None),
|
|
'button_testport': (None, None),
|
|
'plugin_listview': (None, None),
|
|
}
|
|
|
|
core_widgets[self.download_location_path_chooser] = ('path_chooser', 'download_location')
|
|
core_widgets[self.move_completed_path_chooser] = ('path_chooser', 'move_completed_path')
|
|
core_widgets[self.copy_torrent_files_path_chooser] = ('path_chooser', 'torrentfiles_location')
|
|
|
|
# Update the widgets accordingly
|
|
for key in core_widgets:
|
|
modifier = core_widgets[key][0]
|
|
try:
|
|
widget = self.builder.get_object(key)
|
|
except TypeError:
|
|
widget = key
|
|
|
|
widget.set_sensitive(self.is_connected)
|
|
|
|
if self.is_connected:
|
|
value = core_widgets[key][1]
|
|
try:
|
|
value = self.core_config[value]
|
|
except KeyError:
|
|
if callable(value):
|
|
value = value()
|
|
elif modifier:
|
|
value = {'active': False, 'not_active': False, 'value': 0, 'text': '', 'path_chooser': ''}[modifier]
|
|
|
|
if modifier == 'active':
|
|
widget.set_active(value)
|
|
elif modifier == 'not_active':
|
|
widget.set_active(not value)
|
|
elif modifier == 'value':
|
|
widget.set_value(float(value))
|
|
elif modifier == 'text':
|
|
if value is None:
|
|
value = ''
|
|
widget.set_text(value)
|
|
elif modifier == 'path_chooser':
|
|
widget.set_text(value, cursor_end=False, default_text=True)
|
|
|
|
if self.is_connected:
|
|
for key in core_widgets:
|
|
try:
|
|
widget = self.builder.get_object(key)
|
|
except TypeError:
|
|
widget = key
|
|
# Update the toggle status if necessary
|
|
self.on_toggle(widget)
|
|
|
|
# Downloads tab #
|
|
self.builder.get_object('chk_show_dialog').set_active(self.gtkui_config['interactive_add'])
|
|
self.builder.get_object('chk_focus_dialog').set_active(self.gtkui_config['focus_add_dialog'])
|
|
|
|
# Interface tab #
|
|
self.builder.get_object('chk_use_tray').set_active(self.gtkui_config['enable_system_tray'])
|
|
self.builder.get_object('chk_min_on_close').set_active(self.gtkui_config['close_to_tray'])
|
|
self.builder.get_object('chk_start_in_tray').set_active(self.gtkui_config['start_in_tray'])
|
|
self.builder.get_object('radio_appind').set_active(self.gtkui_config['enable_appindicator'])
|
|
self.builder.get_object('chk_lock_tray').set_active(self.gtkui_config['lock_tray'])
|
|
self.builder.get_object('radio_standalone').set_active(self.gtkui_config['standalone'])
|
|
self.builder.get_object('radio_thinclient').set_active(not self.gtkui_config['standalone'])
|
|
self.builder.get_object('chk_show_rate_in_title').set_active(self.gtkui_config['show_rate_in_title'])
|
|
self.builder.get_object('chk_focus_main_window_on_add').set_active(
|
|
self.gtkui_config['focus_main_window_on_add'])
|
|
self.builder.get_object('piecesbar_toggle').set_active(self.gtkui_config['show_piecesbar'])
|
|
self.__set_color('completed', from_config=True)
|
|
self.__set_color('downloading', from_config=True)
|
|
self.__set_color('waiting', from_config=True)
|
|
self.__set_color('missing', from_config=True)
|
|
|
|
# Other tab #
|
|
self.builder.get_object('chk_show_new_releases').set_active(self.gtkui_config['show_new_releases'])
|
|
|
|
# Cache tab #
|
|
if client.connected():
|
|
self.__update_cache_status()
|
|
|
|
# Plugins tab #
|
|
all_plugins = self.all_plugins
|
|
enabled_plugins = self.enabled_plugins
|
|
# Clear the existing list so we don't duplicate entries.
|
|
self.plugin_liststore.clear()
|
|
# Iterate through the lists and add them to the liststore
|
|
for plugin in all_plugins:
|
|
enabled = plugin in enabled_plugins
|
|
row = self.plugin_liststore.append()
|
|
self.plugin_liststore.set_value(row, 0, plugin)
|
|
self.plugin_liststore.set_value(row, 1, enabled)
|
|
self.plugin_liststore.set_value(row, 2, _(plugin))
|
|
|
|
# Now show the dialog
|
|
self.pref_dialog.show()
|
|
|
|
def set_config(self, hide=False):
|
|
"""
|
|
Sets all altered config values in the core.
|
|
|
|
:param hide: bool, if True, will not re-show the dialog and will hide it instead
|
|
"""
|
|
|
|
# Get the values from the dialog
|
|
new_core_config = {}
|
|
new_gtkui_config = {}
|
|
|
|
# Downloads tab #
|
|
new_gtkui_config['interactive_add'] = self.builder.get_object('chk_show_dialog').get_active()
|
|
new_gtkui_config['focus_add_dialog'] = self.builder.get_object('chk_focus_dialog').get_active()
|
|
|
|
for state in ('missing', 'waiting', 'downloading', 'completed'):
|
|
color = self.builder.get_object('%s_color' % state).get_color()
|
|
new_gtkui_config['pieces_color_%s' % state] = [
|
|
color.red, color.green, color.blue
|
|
]
|
|
|
|
new_core_config['copy_torrent_file'] = self.builder.get_object(
|
|
'chk_copy_torrent_file').get_active()
|
|
new_core_config['del_copy_torrent_file'] = self.builder.get_object(
|
|
'chk_del_copy_torrent_file').get_active()
|
|
new_core_config['move_completed'] = self.builder.get_object('chk_move_completed').get_active()
|
|
|
|
new_core_config['download_location'] = self.download_location_path_chooser.get_text()
|
|
new_core_config['move_completed_path'] = self.move_completed_path_chooser.get_text()
|
|
new_core_config['torrentfiles_location'] = self.copy_torrent_files_path_chooser.get_text()
|
|
new_core_config['prioritize_first_last_pieces'] = self.builder.get_object(
|
|
'chk_prioritize_first_last_pieces').get_active()
|
|
new_core_config['sequential_download'] = self.builder.get_object(
|
|
'chk_sequential_download').get_active()
|
|
new_core_config['add_paused'] = self.builder.get_object('chk_add_paused').get_active()
|
|
new_core_config['pre_allocate_storage'] = self.builder.get_object(
|
|
'chk_pre_allocation').get_active()
|
|
|
|
# Network tab #
|
|
listen_ports = [self.builder.get_object('spin_incoming_port').get_value_as_int()] * 2
|
|
new_core_config['listen_ports'] = listen_ports
|
|
new_core_config['random_port'] = self.builder.get_object('chk_random_incoming_port').get_active()
|
|
outgoing_ports = (
|
|
self.builder.get_object('spin_outgoing_port_min').get_value_as_int(),
|
|
self.builder.get_object('spin_outgoing_port_max').get_value_as_int()
|
|
)
|
|
new_core_config['outgoing_ports'] = outgoing_ports
|
|
new_core_config['random_outgoing_ports'] = self.builder.get_object(
|
|
'chk_random_outgoing_ports').get_active()
|
|
incoming_address = self.builder.get_object('entry_interface').get_text().strip()
|
|
if deluge.common.is_ip(incoming_address) or not incoming_address:
|
|
new_core_config['listen_interface'] = incoming_address
|
|
new_core_config['peer_tos'] = self.builder.get_object('entry_peer_tos').get_text()
|
|
new_core_config['dht'] = self.builder.get_object('chk_dht').get_active()
|
|
new_core_config['upnp'] = self.builder.get_object('chk_upnp').get_active()
|
|
new_core_config['natpmp'] = self.builder.get_object('chk_natpmp').get_active()
|
|
new_core_config['utpex'] = self.builder.get_object('chk_utpex').get_active()
|
|
new_core_config['lsd'] = self.builder.get_object('chk_lsd').get_active()
|
|
new_core_config['enc_in_policy'] = self.builder.get_object('combo_encin').get_active()
|
|
new_core_config['enc_out_policy'] = self.builder.get_object('combo_encout').get_active()
|
|
new_core_config['enc_level'] = self.builder.get_object('combo_enclevel').get_active()
|
|
|
|
# Bandwidth tab #
|
|
new_core_config['max_connections_global'] = self.builder.get_object(
|
|
'spin_max_connections_global').get_value_as_int()
|
|
new_core_config['max_download_speed'] = self.builder.get_object('spin_max_download').get_value()
|
|
new_core_config['max_upload_speed'] = self.builder.get_object('spin_max_upload').get_value()
|
|
new_core_config['max_upload_slots_global'] = self.builder.get_object(
|
|
'spin_max_upload_slots_global').get_value_as_int()
|
|
new_core_config['max_half_open_connections'] = self.builder.get_object(
|
|
'spin_max_half_open_connections').get_value_as_int()
|
|
new_core_config['max_connections_per_second'] = self.builder.get_object(
|
|
'spin_max_connections_per_second').get_value_as_int()
|
|
new_core_config['max_connections_per_torrent'] = self.builder.get_object(
|
|
'spin_max_connections_per_torrent').get_value_as_int()
|
|
new_core_config['max_upload_slots_per_torrent'] = self.builder.get_object(
|
|
'spin_max_upload_slots_per_torrent').get_value_as_int()
|
|
new_core_config['max_upload_speed_per_torrent'] = self.builder.get_object(
|
|
'spin_max_upload_per_torrent').get_value()
|
|
new_core_config['max_download_speed_per_torrent'] = self.builder.get_object(
|
|
'spin_max_download_per_torrent').get_value()
|
|
new_core_config['ignore_limits_on_local_network'] = self.builder.get_object(
|
|
'chk_ignore_limits_on_local_network').get_active()
|
|
new_core_config['rate_limit_ip_overhead'] = self.builder.get_object(
|
|
'chk_rate_limit_ip_overhead').get_active()
|
|
|
|
# Interface tab #
|
|
new_gtkui_config['enable_system_tray'] = self.builder.get_object('chk_use_tray').get_active()
|
|
new_gtkui_config['close_to_tray'] = self.builder.get_object('chk_min_on_close').get_active()
|
|
new_gtkui_config['start_in_tray'] = self.builder.get_object('chk_start_in_tray').get_active()
|
|
new_gtkui_config['enable_appindicator'] = self.builder.get_object('radio_appind').get_active()
|
|
new_gtkui_config['lock_tray'] = self.builder.get_object('chk_lock_tray').get_active()
|
|
passhex = sha(self.builder.get_object('txt_tray_password').get_text()).hexdigest()
|
|
if passhex != 'c07eb5a8c0dc7bb81c217b67f11c3b7a5e95ffd7':
|
|
new_gtkui_config['tray_password'] = passhex
|
|
|
|
was_standalone = self.gtkui_config['standalone']
|
|
new_gtkui_standalone = self.builder.get_object('radio_standalone').get_active()
|
|
new_gtkui_config['standalone'] = new_gtkui_standalone
|
|
|
|
new_gtkui_config['show_rate_in_title'] = self.builder.get_object(
|
|
'chk_show_rate_in_title').get_active()
|
|
new_gtkui_config['focus_main_window_on_add'] = self.builder.get_object(
|
|
'chk_focus_main_window_on_add').get_active()
|
|
|
|
# Other tab #
|
|
new_gtkui_config['show_new_releases'] = self.builder.get_object(
|
|
'chk_show_new_releases').get_active()
|
|
new_core_config['send_info'] = self.builder.get_object('chk_send_info').get_active()
|
|
new_core_config['geoip_db_location'] = self.builder.get_object('entry_geoip').get_text()
|
|
|
|
# Daemon tab #
|
|
new_core_config['daemon_port'] = self.builder.get_object('spin_daemon_port').get_value_as_int()
|
|
new_core_config['allow_remote'] = self.builder.get_object(
|
|
'chk_allow_remote_connections').get_active()
|
|
new_core_config['new_release_check'] = self.builder.get_object('chk_new_releases').get_active()
|
|
|
|
# Proxy tab #
|
|
new_core_config['proxy'] = {
|
|
'type': self.builder.get_object('combo_proxy_type').get_active(),
|
|
'username': self.builder.get_object('entry_proxy_user').get_text(),
|
|
'password': self.builder.get_object('entry_proxy_pass').get_text(),
|
|
'hostname': self.builder.get_object('entry_proxy_host').get_text(),
|
|
'port': self.builder.get_object('spin_proxy_port').get_value_as_int(),
|
|
'proxy_hostnames': self.builder.get_object('chk_proxy_host_resolve').get_active(),
|
|
'proxy_peer_connections': self.builder.get_object('chk_proxy_peer_conn').get_active(),
|
|
'proxy_tracker_connections': self.builder.get_object('chk_proxy_tracker_conn').get_active(),
|
|
'force_proxy': self.builder.get_object('chk_force_proxy').get_active(),
|
|
'anonymous_mode': self.builder.get_object('chk_anonymous_mode').get_active()
|
|
}
|
|
|
|
# Queue tab #
|
|
new_core_config['queue_new_to_top'] = self.builder.get_object('chk_queue_new_top').get_active()
|
|
new_core_config['max_active_seeding'] = self.builder.get_object(
|
|
'spin_seeding').get_value_as_int()
|
|
new_core_config['max_active_downloading'] = self.builder.get_object(
|
|
'spin_downloading').get_value_as_int()
|
|
new_core_config['max_active_limit'] = self.builder.get_object('spin_active').get_value_as_int()
|
|
new_core_config['dont_count_slow_torrents'] = self.builder.get_object(
|
|
'chk_dont_count_slow_torrents').get_active()
|
|
new_core_config['auto_manage_prefer_seeds'] = self.builder.get_object(
|
|
'chk_auto_manage_prefer_seeds').get_active()
|
|
new_core_config['stop_seed_at_ratio'] = self.builder.get_object('chk_share_ratio').get_active()
|
|
new_core_config['remove_seed_at_ratio'] = self.builder.get_object(
|
|
'radio_remove_ratio').get_active()
|
|
new_core_config['stop_seed_ratio'] = self.builder.get_object('spin_share_ratio').get_value()
|
|
new_core_config['share_ratio_limit'] = self.builder.get_object(
|
|
'spin_share_ratio_limit').get_value()
|
|
new_core_config['seed_time_ratio_limit'] = self.builder.get_object(
|
|
'spin_seed_time_ratio_limit').get_value()
|
|
new_core_config['seed_time_limit'] = self.builder.get_object('spin_seed_time_limit').get_value()
|
|
|
|
# Cache tab #
|
|
new_core_config['cache_size'] = self.builder.get_object('spin_cache_size').get_value_as_int()
|
|
new_core_config['cache_expiry'] = self.builder.get_object('spin_cache_expiry').get_value_as_int()
|
|
|
|
# Run plugin hook to apply preferences
|
|
component.get('PluginManager').run_on_apply_prefs()
|
|
|
|
# Language
|
|
if self.language_checkbox.get_active():
|
|
new_gtkui_config['language'] = None
|
|
else:
|
|
active = self.language_combo.get_active()
|
|
if active == -1:
|
|
dialog = InformationDialog(
|
|
_('Attention'),
|
|
_('You must choose a language')
|
|
)
|
|
dialog.run()
|
|
return
|
|
else:
|
|
model = self.language_combo.get_model()
|
|
new_gtkui_config['language'] = model.get(model.get_iter(active), 0)[0]
|
|
|
|
if new_gtkui_config['language'] != self.gtkui_config['language']:
|
|
dialog = InformationDialog(
|
|
_('Attention'),
|
|
_('You must now restart the deluge UI for the changes to take effect.')
|
|
)
|
|
dialog.run()
|
|
|
|
# GtkUI
|
|
for key in new_gtkui_config:
|
|
# The values do not match so this needs to be updated
|
|
if self.gtkui_config[key] != new_gtkui_config[key]:
|
|
self.gtkui_config[key] = new_gtkui_config[key]
|
|
|
|
# Core
|
|
if client.connected():
|
|
# Only do this if we're connected to a daemon
|
|
config_to_set = {}
|
|
for key in new_core_config:
|
|
# The values do not match so this needs to be updated
|
|
if self.core_config[key] != new_core_config[key]:
|
|
config_to_set[key] = new_core_config[key]
|
|
|
|
if config_to_set:
|
|
# Set each changed config value in the core
|
|
client.core.set_config(config_to_set)
|
|
client.force_call(True)
|
|
# Update the configuration
|
|
self.core_config.update(config_to_set)
|
|
|
|
if hide:
|
|
self.hide()
|
|
else:
|
|
# Re-show the dialog to make sure everything has been updated
|
|
self.show()
|
|
|
|
if was_standalone != new_gtkui_standalone:
|
|
def on_response(response):
|
|
if response == gtk.RESPONSE_YES:
|
|
shutdown_daemon = (not client.is_standalone() and
|
|
client.connected() and
|
|
client.is_localhost())
|
|
component.get('MainWindow').quit(shutdown=shutdown_daemon, restart=True)
|
|
else:
|
|
self.gtkui_config['standalone'] = not new_gtkui_standalone
|
|
self.builder.get_object('radio_standalone').set_active(
|
|
self.gtkui_config['standalone'])
|
|
self.builder.get_object('radio_thinclient').set_active(
|
|
not self.gtkui_config['standalone'])
|
|
mode = 'Thinclient' if was_standalone else 'Standalone'
|
|
dialog = YesNoDialog(
|
|
_('Switching Deluge Client Mode...'),
|
|
_('Do you want to restart to use %s mode?' % mode)
|
|
)
|
|
dialog.run().addCallback(on_response)
|
|
|
|
def hide(self):
|
|
self.window_open = False
|
|
self.builder.get_object('port_img').hide()
|
|
self.pref_dialog.hide()
|
|
|
|
def __update_cache_status(self):
|
|
# Updates the cache status labels with the info in the dict
|
|
cache_labels = ('label_cache_read_ops', 'label_cache_write_ops',
|
|
'label_cache_num_blocks_read', 'label_cache_num_blocks_written',
|
|
'label_cache_read_hit_ratio', 'label_cache_write_hit_ratio',
|
|
'label_cache_num_blocks_cache_hits', 'label_cache_disk_blocks_in_use',
|
|
'label_cache_read_cache_blocks')
|
|
|
|
for widget_name in cache_labels:
|
|
widget = self.builder.get_object(widget_name)
|
|
key = widget_name[len('label_cache_'):]
|
|
if not widget_name.endswith('ratio'):
|
|
key = 'disk.' + key
|
|
value = self.cache_status.get(key, 0)
|
|
if isinstance(value, float):
|
|
value = '%.2f' % value
|
|
else:
|
|
value = str(value)
|
|
|
|
widget.set_text(value)
|
|
|
|
def _on_button_cache_refresh_clicked(self, widget):
|
|
def on_get_session_status(status):
|
|
self.cache_status = status
|
|
self.__update_cache_status()
|
|
|
|
client.core.get_session_status(DISK_CACHE_KEYS).addCallback(on_get_session_status)
|
|
|
|
def on_pref_dialog_delete_event(self, widget, event):
|
|
self.hide()
|
|
return True
|
|
|
|
def load_pref_dialog_state(self):
|
|
w = self.gtkui_config['pref_dialog_width']
|
|
h = self.gtkui_config['pref_dialog_height']
|
|
if w is not None and h is not None:
|
|
self.pref_dialog.resize(w, h)
|
|
|
|
def on_pref_dialog_configure_event(self, widget, event):
|
|
self.gtkui_config['pref_dialog_width'] = event.width
|
|
self.gtkui_config['pref_dialog_height'] = event.height
|
|
|
|
def on_toggle(self, widget):
|
|
"""Handles widget sensitivity based on radio/check button values."""
|
|
try:
|
|
value = widget.get_active()
|
|
except Exception:
|
|
return
|
|
|
|
path_choosers = {
|
|
'download_location_path_chooser': self.download_location_path_chooser,
|
|
'move_completed_path_chooser': self.move_completed_path_chooser,
|
|
'torrentfiles_location_path_chooser': self.copy_torrent_files_path_chooser
|
|
}
|
|
|
|
dependents = {
|
|
'chk_show_dialog': {'chk_focus_dialog': True},
|
|
'chk_random_incoming_port': {'spin_incoming_port': False},
|
|
'chk_random_outgoing_ports': {'spin_outgoing_port_min': False,
|
|
'spin_outgoing_port_max': False},
|
|
'chk_use_tray': {'radio_appind': True,
|
|
'radio_systray': True,
|
|
'chk_min_on_close': True,
|
|
'chk_start_in_tray': True,
|
|
'alignment_tray_type': True,
|
|
'chk_lock_tray': True},
|
|
'chk_lock_tray': {'txt_tray_password': True,
|
|
'password_label': True},
|
|
'radio_open_folder_custom': {'combo_file_manager': False,
|
|
'txt_open_folder_location': True},
|
|
'chk_move_completed': {'move_completed_path_chooser': True},
|
|
'chk_copy_torrent_file': {'torrentfiles_location_path_chooser': True,
|
|
'chk_del_copy_torrent_file': True},
|
|
'chk_share_ratio': {'spin_share_ratio': True,
|
|
'radio_pause_ratio': True,
|
|
'radio_remove_ratio': True}
|
|
}
|
|
|
|
def update_dependent_widgets(name, value):
|
|
dependency = dependents[name]
|
|
for dep in dependency.keys():
|
|
if dep in path_choosers:
|
|
depwidget = path_choosers[dep]
|
|
else:
|
|
depwidget = self.builder.get_object(dep)
|
|
sensitive = [not value, value][dependency[dep]]
|
|
depwidget.set_sensitive(sensitive and self.is_connected)
|
|
if dep in dependents:
|
|
update_dependent_widgets(dep, depwidget.get_active() and sensitive)
|
|
|
|
for key in dependents:
|
|
if widget != self.builder.get_object(key):
|
|
continue
|
|
update_dependent_widgets(key, value)
|
|
|
|
def on_button_ok_clicked(self, data):
|
|
log.debug('on_button_ok_clicked')
|
|
self.set_config(hide=True)
|
|
return True
|
|
|
|
def on_button_apply_clicked(self, data):
|
|
log.debug('on_button_apply_clicked')
|
|
self.set_config()
|
|
|
|
def on_button_cancel_clicked(self, data):
|
|
log.debug('on_button_cancel_clicked')
|
|
self.hide()
|
|
return True
|
|
|
|
def on_selection_changed(self, treeselection):
|
|
# Show the correct notebook page based on what row is selected.
|
|
(model, row) = treeselection.get_selected()
|
|
try:
|
|
if model.get_value(row, 1) == _('Daemon'):
|
|
# Let's see update the accounts related stuff
|
|
if client.connected():
|
|
self._get_accounts_tab_data()
|
|
self.notebook.set_current_page(model.get_value(row, 0))
|
|
except TypeError:
|
|
pass
|
|
|
|
def on_test_port_clicked(self, data):
|
|
log.debug('on_test_port_clicked')
|
|
|
|
def on_get_test(status):
|
|
if status:
|
|
self.builder.get_object('port_img').set_from_stock(gtk.STOCK_YES, 4)
|
|
self.builder.get_object('port_img').show()
|
|
else:
|
|
self.builder.get_object('port_img').set_from_stock(gtk.STOCK_DIALOG_WARNING, 4)
|
|
self.builder.get_object('port_img').show()
|
|
client.core.test_listen_port().addCallback(on_get_test)
|
|
# XXX: Consider using gtk.Spinner() instead of the loading gif
|
|
# It requires gtk.ver > 2.12
|
|
self.builder.get_object('port_img').set_from_file(deluge.common.get_pixmap('loading.gif'))
|
|
self.builder.get_object('port_img').show()
|
|
client.force_call()
|
|
|
|
def on_plugin_toggled(self, renderer, path):
|
|
row = self.plugin_liststore.get_iter_from_string(path)
|
|
name = self.plugin_liststore.get_value(row, 0)
|
|
value = self.plugin_liststore.get_value(row, 1)
|
|
log.debug('on_plugin_toggled - %s: %s', name, value)
|
|
self.plugin_liststore.set_value(row, 1, not value)
|
|
if not value:
|
|
d = client.core.enable_plugin(name)
|
|
else:
|
|
d = client.core.disable_plugin(name)
|
|
|
|
def on_plugin_action(arg):
|
|
if not value and arg is False:
|
|
log.warn('Failed to enable plugin: %s', name)
|
|
self.plugin_liststore.set_value(row, 1, False)
|
|
|
|
d.addBoth(on_plugin_action)
|
|
|
|
def on_plugin_selection_changed(self, treeselection):
|
|
log.debug('on_plugin_selection_changed')
|
|
(model, itr) = treeselection.get_selected()
|
|
if not itr:
|
|
return
|
|
name = model[itr][0]
|
|
plugin_info = component.get('PluginManager').get_plugin_info(name)
|
|
self.builder.get_object('label_plugin_author').set_text(plugin_info['Author'])
|
|
self.builder.get_object('label_plugin_version').set_text(plugin_info['Version'])
|
|
self.builder.get_object('label_plugin_email').set_text(plugin_info['Author-email'])
|
|
self.builder.get_object('label_plugin_homepage').set_text(plugin_info['Home-page'])
|
|
self.builder.get_object('label_plugin_details').set_text(plugin_info['Description'])
|
|
|
|
def _on_button_plugin_install_clicked(self, widget):
|
|
log.debug('_on_button_plugin_install_clicked')
|
|
chooser = gtk.FileChooserDialog(
|
|
_('Select the Plugin'),
|
|
self.pref_dialog,
|
|
gtk.FILE_CHOOSER_ACTION_OPEN,
|
|
buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK)
|
|
)
|
|
|
|
chooser.set_transient_for(self.pref_dialog)
|
|
chooser.set_select_multiple(False)
|
|
chooser.set_property('skip-taskbar-hint', True)
|
|
|
|
file_filter = gtk.FileFilter()
|
|
file_filter.set_name(_('Plugin Eggs'))
|
|
file_filter.add_pattern('*.' + 'egg')
|
|
chooser.add_filter(file_filter)
|
|
|
|
# Run the dialog
|
|
response = chooser.run()
|
|
|
|
if response == gtk.RESPONSE_OK:
|
|
filepath = deluge.common.decode_bytes(chooser.get_filename())
|
|
else:
|
|
chooser.destroy()
|
|
return
|
|
|
|
import base64
|
|
import shutil
|
|
filename = os.path.split(filepath)[1]
|
|
shutil.copyfile(
|
|
filepath,
|
|
os.path.join(get_config_dir(), 'plugins', filename))
|
|
|
|
component.get('PluginManager').scan_for_plugins()
|
|
|
|
if not client.is_localhost():
|
|
# We need to send this plugin to the daemon
|
|
with open(filepath, 'rb') as _file:
|
|
filedump = base64.encodestring(_file.read())
|
|
client.core.upload_plugin(filename, filedump)
|
|
|
|
client.core.rescan_plugins()
|
|
chooser.destroy()
|
|
# We need to re-show the preferences dialog to show the new plugins
|
|
self.show()
|
|
|
|
def _on_button_rescan_plugins_clicked(self, widget):
|
|
component.get('PluginManager').scan_for_plugins()
|
|
if client.connected():
|
|
client.core.rescan_plugins()
|
|
self.show()
|
|
|
|
def _on_button_find_plugins_clicked(self, widget):
|
|
deluge.common.open_url_in_browser('http://dev.deluge-torrent.org/wiki/Plugins')
|
|
|
|
def _on_combo_encryption_changed(self, widget):
|
|
combo_encin = self.builder.get_object('combo_encin').get_active()
|
|
combo_encout = self.builder.get_object('combo_encout').get_active()
|
|
combo_enclevel = self.builder.get_object('combo_enclevel')
|
|
|
|
# If incoming and outgoing both set to disabled, disable level combobox
|
|
if combo_encin == 2 and combo_encout == 2:
|
|
combo_enclevel.set_sensitive(False)
|
|
elif self.is_connected:
|
|
combo_enclevel.set_sensitive(True)
|
|
|
|
def _on_combo_proxy_type_changed(self, widget):
|
|
proxy_type = self.builder.get_object('combo_proxy_type').get_active()
|
|
proxy_entries = [
|
|
'label_proxy_host', 'entry_proxy_host', 'label_proxy_port', 'spin_proxy_port',
|
|
'label_proxy_pass', 'entry_proxy_pass', 'label_proxy_user', 'entry_proxy_user',
|
|
'chk_proxy_host_resolve', 'chk_proxy_peer_conn', 'chk_proxy_tracker_conn']
|
|
|
|
# 0: None, 1: Socks4, 2: Socks5, 3: Socks5 Auth, 4: HTTP, 5: HTTP Auth, 6: I2P
|
|
show_entries = []
|
|
if proxy_type > 0:
|
|
show_entries.extend([
|
|
'label_proxy_host', 'entry_proxy_host', 'label_proxy_port', 'spin_proxy_port',
|
|
'chk_proxy_peer_conn', 'chk_proxy_tracker_conn'])
|
|
if proxy_type in (3, 5):
|
|
show_entries.extend([
|
|
'label_proxy_pass', 'entry_proxy_pass', 'label_proxy_user', 'entry_proxy_user'])
|
|
if proxy_type in (2, 3, 4, 5):
|
|
show_entries.extend(['chk_proxy_host_resolve'])
|
|
|
|
for entry in proxy_entries:
|
|
if entry in show_entries:
|
|
self.builder.get_object(entry).show()
|
|
else:
|
|
self.builder.get_object(entry).hide()
|
|
|
|
def _on_entry_proxy_host_paste_clipboard(self, widget):
|
|
text = gtk.clipboard_get().wait_for_text().strip()
|
|
log.debug('on_entry_proxy_host_paste-clipboard: got paste: %s', text)
|
|
text = text if '//' in text else '//' + text
|
|
parsed = urlparse(text)
|
|
if parsed.hostname:
|
|
widget.set_text(parsed.hostname)
|
|
widget.emit_stop_by_name('paste-clipboard')
|
|
if parsed.port:
|
|
self.builder.get_object('spin_proxy_port').set_value(parsed.port)
|
|
if parsed.username:
|
|
self.builder.get_object('entry_proxy_user').set_text(parsed.username)
|
|
if parsed.password:
|
|
self.builder.get_object('entry_proxy_pass').set_text(parsed.password)
|
|
|
|
def _on_button_associate_magnet_clicked(self, widget):
|
|
associate_magnet_links(True)
|
|
|
|
def _get_accounts_tab_data(self):
|
|
def on_ok(accounts):
|
|
self.accounts_frame.show()
|
|
self._on_get_known_accounts(accounts)
|
|
|
|
def on_fail(failure):
|
|
if failure.type == NotAuthorizedError:
|
|
self.accounts_frame.hide()
|
|
else:
|
|
ErrorDialog(
|
|
_('Server Side Error'),
|
|
_('An error ocurred on the server'),
|
|
parent=self.pref_dialog, details=failure.getErrorMessage()
|
|
).run()
|
|
client.core.get_known_accounts().addCallback(on_ok).addErrback(on_fail)
|
|
|
|
def _on_get_known_accounts(self, known_accounts):
|
|
known_accounts_to_log = []
|
|
for account in known_accounts:
|
|
account_to_log = {}
|
|
for key, value in account.copy().items():
|
|
if key == 'password':
|
|
value = '*' * len(value)
|
|
account_to_log[key] = value
|
|
known_accounts_to_log.append(account_to_log)
|
|
log.debug('_on_known_accounts: %s', known_accounts_to_log)
|
|
|
|
self.accounts_liststore.clear()
|
|
|
|
for account in known_accounts:
|
|
accounts_iter = self.accounts_liststore.append()
|
|
self.accounts_liststore.set_value(accounts_iter, ACCOUNTS_USERNAME, account['username'])
|
|
self.accounts_liststore.set_value(accounts_iter, ACCOUNTS_LEVEL, account['authlevel'])
|
|
self.accounts_liststore.set_value(accounts_iter, ACCOUNTS_PASSWORD, account['password'])
|
|
|
|
def _on_accounts_selection_changed(self, treeselection):
|
|
log.debug('_on_accounts_selection_changed')
|
|
(model, itr) = treeselection.get_selected()
|
|
if not itr:
|
|
return
|
|
username = model[itr][0]
|
|
if username:
|
|
self.builder.get_object('accounts_edit').set_sensitive(True)
|
|
self.builder.get_object('accounts_delete').set_sensitive(True)
|
|
else:
|
|
self.builder.get_object('accounts_edit').set_sensitive(False)
|
|
self.builder.get_object('accounts_delete').set_sensitive(False)
|
|
|
|
def _on_accounts_add_clicked(self, widget):
|
|
dialog = AccountDialog(levels_mapping=client.auth_levels_mapping, parent=self.pref_dialog)
|
|
|
|
def dialog_finished(response_id):
|
|
username = dialog.get_username()
|
|
password = dialog.get_password()
|
|
authlevel = dialog.get_authlevel()
|
|
|
|
def add_ok(rv):
|
|
accounts_iter = self.accounts_liststore.append()
|
|
self.accounts_liststore.set_value(accounts_iter, ACCOUNTS_USERNAME, username)
|
|
self.accounts_liststore.set_value(accounts_iter, ACCOUNTS_LEVEL, authlevel)
|
|
self.accounts_liststore.set_value(accounts_iter, ACCOUNTS_PASSWORD, password)
|
|
|
|
def add_fail(failure):
|
|
if failure.type == AuthManagerError:
|
|
ErrorDialog(
|
|
_('Error Adding Account'),
|
|
_('Authentication failed'),
|
|
parent=self.pref_dialog, details=failure.getErrorMessage()
|
|
).run()
|
|
else:
|
|
ErrorDialog(
|
|
_('Error Adding Account'),
|
|
_('An error ocurred while adding account'),
|
|
parent=self.pref_dialog, details=failure.getErrorMessage()
|
|
).run()
|
|
|
|
if response_id == gtk.RESPONSE_OK:
|
|
client.core.create_account(
|
|
username, password, authlevel
|
|
).addCallback(add_ok).addErrback(add_fail)
|
|
|
|
dialog.run().addCallback(dialog_finished)
|
|
|
|
def _on_accounts_edit_clicked(self, widget):
|
|
(model, itr) = self.accounts_listview.get_selection().get_selected()
|
|
if not itr:
|
|
return
|
|
|
|
dialog = AccountDialog(
|
|
model[itr][ACCOUNTS_USERNAME],
|
|
model[itr][ACCOUNTS_PASSWORD],
|
|
model[itr][ACCOUNTS_LEVEL],
|
|
levels_mapping=client.auth_levels_mapping,
|
|
parent=self.pref_dialog
|
|
)
|
|
|
|
def dialog_finished(response_id):
|
|
|
|
def update_ok(rc):
|
|
model.set_value(itr, ACCOUNTS_PASSWORD, dialog.get_username())
|
|
model.set_value(itr, ACCOUNTS_LEVEL, dialog.get_authlevel())
|
|
|
|
def update_fail(failure):
|
|
ErrorDialog(
|
|
_('Error Updating Account'),
|
|
_('An error ocurred while updating account'),
|
|
parent=self.pref_dialog, details=failure.getErrorMessage()
|
|
).run()
|
|
|
|
if response_id == gtk.RESPONSE_OK:
|
|
client.core.update_account(
|
|
dialog.get_username(),
|
|
dialog.get_password(),
|
|
dialog.get_authlevel()
|
|
).addCallback(update_ok).addErrback(update_fail)
|
|
|
|
dialog.run().addCallback(dialog_finished)
|
|
|
|
def _on_accounts_delete_clicked(self, widget):
|
|
(model, itr) = self.accounts_listview.get_selection().get_selected()
|
|
if not itr:
|
|
return
|
|
|
|
username = model[itr][0]
|
|
header = _('Remove Account')
|
|
text = _('Are you sure you want to remove the account with the '
|
|
'username "%(username)s"?' % dict(username=username))
|
|
dialog = YesNoDialog(header, text, parent=self.pref_dialog)
|
|
|
|
def dialog_finished(response_id):
|
|
def remove_ok(rc):
|
|
model.remove(itr)
|
|
|
|
def remove_fail(failure):
|
|
if failure.type == AuthManagerError:
|
|
ErrorDialog(
|
|
_('Error Removing Account'),
|
|
_('Auhentication failed'),
|
|
parent=self.pref_dialog, details=failure.getErrorMessage()
|
|
).run()
|
|
else:
|
|
ErrorDialog(
|
|
_('Error Removing Account'),
|
|
_('An error ocurred while removing account'),
|
|
parent=self.pref_dialog, details=failure.getErrorMessage()
|
|
).run()
|
|
if response_id == gtk.RESPONSE_YES:
|
|
client.core.remove_account(
|
|
username
|
|
).addCallback(remove_ok).addErrback(remove_fail)
|
|
dialog.run().addCallback(dialog_finished)
|
|
|
|
def _on_piecesbar_toggle_toggled(self, widget):
|
|
self.gtkui_config['show_piecesbar'] = widget.get_active()
|
|
colors_widget = self.builder.get_object('piecebar_colors_expander')
|
|
colors_widget.set_visible(widget.get_active())
|
|
|
|
def _on_checkbutton_language_toggled(self, widget):
|
|
self.language_combo.set_visible(not self.language_checkbox.get_active())
|
|
|
|
def _on_completed_color_set(self, widget):
|
|
self.__set_color('completed')
|
|
|
|
def _on_revert_color_completed_clicked(self, widget):
|
|
self.__revert_color('completed')
|
|
|
|
def _on_downloading_color_set(self, widget):
|
|
self.__set_color('downloading')
|
|
|
|
def _on_revert_color_downloading_clicked(self, widget):
|
|
self.__revert_color('downloading')
|
|
|
|
def _on_waiting_color_set(self, widget):
|
|
self.__set_color('waiting')
|
|
|
|
def _on_revert_color_waiting_clicked(self, widget):
|
|
self.__revert_color('waiting')
|
|
|
|
def _on_missing_color_set(self, widget):
|
|
self.__set_color('missing')
|
|
|
|
def _on_revert_color_missing_clicked(self, widget):
|
|
self.__revert_color('missing')
|
|
|
|
def __set_color(self, state, from_config=False):
|
|
if from_config:
|
|
color = Color(*self.gtkui_config['pieces_color_%s' % state])
|
|
log.debug('Setting %r color state from config to %s', state, (color.red, color.green, color.blue))
|
|
self.builder.get_object('%s_color' % state).set_color(color)
|
|
else:
|
|
color = self.builder.get_object('%s_color' % state).get_color()
|
|
log.debug('Setting %r color state to %s', state, (color.red, color.green, color.blue))
|
|
self.gtkui_config['pieces_color_%s' % state] = [color.red, color.green, color.blue]
|
|
self.gtkui_config.save()
|
|
self.gtkui_config.apply_set_functions('pieces_colors')
|
|
|
|
self.builder.get_object('revert_color_%s' % state).set_sensitive(
|
|
[color.red, color.green, color.blue] != self.COLOR_DEFAULTS[state])
|
|
|
|
def __revert_color(self, state, from_config=False):
|
|
log.debug('Reverting %r color state', state)
|
|
self.builder.get_object('%s_color' % state).set_color(Color(*self.COLOR_DEFAULTS[state]))
|
|
self.builder.get_object('revert_color_%s' % state).set_sensitive(False)
|
|
self.gtkui_config.apply_set_functions('pieces_colors')
|