diff --git a/deluge/core/authmanager.py b/deluge/core/authmanager.py index 08dd0e8c7..7c2d5c1d2 100644 --- a/deluge/core/authmanager.py +++ b/deluge/core/authmanager.py @@ -51,10 +51,29 @@ AUTH_LEVEL_ADMIN = 10 AUTH_LEVEL_DEFAULT = AUTH_LEVEL_NORMAL +class Account(object): + __slots__ = ('username', 'password', 'auth_level') + def __init__(self, username, password, auth_level): + self.username = username + self.password = password + self.auth_level = auth_level + + def data(self, include_private=True): + rv = self.__dict__.copy() + if not include_private: + rv['password'] = '' + return rv + + def __repr__(self): + return ('' % + self.__dict__) + + class AuthManager(component.Component): def __init__(self): component.Component.__init__(self, "AuthManager") self.__auth = {} + self.__auth_modification_time = None def start(self): self.__load_auth_file() @@ -74,8 +93,10 @@ class AuthManager(component.Component): :returns: int, the auth level for this user :rtype: int - :raises AuthenticationRequired: if aditional details are required to authenticate - :raises BadLoginError: if the username does not exist or password does not match + :raises AuthenticationRequired: if aditional details are required to + authenticate. + :raises BadLoginError: if the username does not exist or password does + not match. """ if not username: @@ -85,21 +106,60 @@ class AuthManager(component.Component): self.__test_existing_account(username) - if self.__auth[username][0] == password: + if self.__auth[username].password == password: # Return the users auth level - return int(self.__auth[username][1]) - elif not password and self.__auth[username][0]: + return self.__auth[username].auth_level + elif not password and self.__auth[username].password: raise AuthenticationRequired("Password is required", username) else: raise BadLoginError("Password does not match") - def get_known_accounts(self): + def get_known_accounts(self, include_private_data=False): """ Returns a list of known deluge usernames. """ self.__load_auth_file() - return self.__auth.keys() + rv = {} + for account in self.__auth.items(): + rv[account.username] = account.data(include_private_data) + return rv + def create_account(self, username, password='', auth_level=AUTH_LEVEL_DEFAULT): + if username in self.__auth: + raise Something() + self.__create_account(username, password, auth_level) + + def update_account(self, username, password='', auth_level=AUTH_LEVEL_DEFAULT): + if username in self.__auth: + raise Something() + self.__create_account(username, password, auth_level) + + def remove_account(self, username): + if username in self.__auth: + raise Something() + del self.__auth[username] + self.write_auth_file() + if component.get("RPCServer").get_session_user() == username: + # Force a client logout by the server + component.get("RPCServer").logout_current_session() + + def write_auth_file(self): + old_auth_file = configmanager.get_config_dir("auth") + new_auth_file = old_auth_file + '.new' + fd = open(new_auth_file, "w") + for account in self.__auth.items(): + fd.write( + "%(username)s:%(password)s:%(auth_level)s\n" % account.__dict__ + ) + fd.flush() + os.fsync(fd.fileno()) + fd.close() + os.rename(new_auth_file, old_auth_file) + self.__load_auth_file() + + def __add_account(self, username, password, auth_level): + self.__auth[username] = Account(username, password, auth_level) + self.write_auth_file() def __test_existing_account(self, username): if username not in self.__auth: @@ -107,6 +167,7 @@ class AuthManager(component.Component): self.__load_auth_file() if username not in self.__auth: raise BadLoginError("Username does not exist") + return True def __create_localclient_account(self): """ @@ -117,25 +178,29 @@ class AuthManager(component.Component): from hashlib import sha1 as sha_hash except ImportError: from sha import new as sha_hash - return ("localclient:" + sha_hash(str(random.random())).hexdigest() + - ":" + str(AUTH_LEVEL_ADMIN) + "\n") + self.__auth["localclient"] = Account( + "localclient", + sha_hash(str(random.random())).hexdigest(), + AUTH_LEVEL_ADMIN + ) def __load_auth_file(self): auth_file = configmanager.get_config_dir("auth") # Check for auth file and create if necessary if not os.path.exists(auth_file): - localclient = self.__create_localclient_account() - fd = open(auth_file, "w") - fd.write(localclient) - fd.flush() - os.fsync(fd.fileno()) - fd.close() - # Change the permissions on the file so only this user can read/write it - os.chmod(auth_file, stat.S_IREAD | stat.S_IWRITE) - f = [localclient] - else: - # Load the auth file into a dictionary: {username: password, ...} - f = open(auth_file, "r").readlines() + self.__create_auth_file() + self.__create_localclient_account() + self.write_auth_file() + + auth_file_modification_time = os.stat(auth_file).st_mtime + if self.__auth_modification_time is None: + self.__auth_modification_time = auth_file_modification_time + elif self.__auth_modification_time == auth_file_modification_time: + # File didn't change, no need for re-parsing's + return + + # Load the auth file into a dictionary: {username: Account(...)} + f = open(auth_file, "r").readlines() for line in f: if line.startswith("#"): @@ -152,14 +217,36 @@ class AuthManager(component.Component): log.warning("Your auth entry for %s contains no auth level, " "using AUTH_LEVEL_DEFAULT(%s)..", username, AUTH_LEVEL_DEFAULT) - level = AUTH_LEVEL_DEFAULT + auth_level = AUTH_LEVEL_DEFAULT elif len(lsplit) == 3: - username, password, level = lsplit + username, password, auth_level = lsplit else: log.error("Your auth file is malformed: Incorrect number of fields!") continue - self.__auth[username.strip()] = (password.strip(), level) + username = username.strip() + password = password.strip() + try: + auth_level = int(auth_level) + except ValueError: + log.error("Your auth file is malformed: %r is not a valid auth " + "level" % auth_level) + continue + + self.__auth[username] = Account(username, password, auth_level) if "localclient" not in self.__auth: - open(auth_file, "a").write(self.__create_localclient_account()) + self.__create_localclient_account() + self.write_auth_file() + + + def __create_auth_file(self): + auth_file = configmanager.get_config_dir("auth") + # Check for auth file and create if necessary + if not os.path.exists(auth_file): + fd = open(auth_file, "w") + fd.flush() + os.fsync(fd.fileno()) + fd.close() + # Change the permissions on the file so only this user can read/write it + os.chmod(auth_file, stat.S_IREAD | stat.S_IWRITE) diff --git a/deluge/core/core.py b/deluge/core/core.py index 0fb9203c3..225979730 100644 --- a/deluge/core/core.py +++ b/deluge/core/core.py @@ -55,7 +55,7 @@ import deluge.common import deluge.component as component from deluge.event import * from deluge.error import * -from deluge.core.authmanager import AUTH_LEVEL_ADMIN +from deluge.core.authmanager import AUTH_LEVEL_ADMIN, AUTH_LEVEL_DEFAULT from deluge.core.torrentmanager import TorrentManager from deluge.core.pluginmanager import PluginManager from deluge.core.alertmanager import AlertManager @@ -829,6 +829,9 @@ class Core(component.Component): """ return lt.version - @export(AUTH_LEVEL_ADMIN) + @export(AUTH_LEVEL_DEFAULT) def get_known_accounts(self): - return self.authmanager.get_known_accounts() + auth_level = component.get("RPCServer").get_session_auth_level() + return self.authmanager.get_known_accounts( + include_private_data=(auth_level==AUTH_LEVEL_ADMIN) + ) diff --git a/deluge/core/rpcserver.py b/deluge/core/rpcserver.py index 60e190593..3a6aae27f 100644 --- a/deluge/core/rpcserver.py +++ b/deluge/core/rpcserver.py @@ -485,6 +485,12 @@ class RPCServer(component.Component): """ return session_id in self.factory.authorized_sessions + def logout_current_session(self): + """ + Makes the current session invalid logging out the current account + """ + self.factory.protocol.connectionLost("Server logged out client") + def emit_event(self, event): """ Emits the event to interested clients. diff --git a/deluge/ui/gtkui/preferences.py b/deluge/ui/gtkui/preferences.py index 4dad8d1ff..a13937a2d 100644 --- a/deluge/ui/gtkui/preferences.py +++ b/deluge/ui/gtkui/preferences.py @@ -1,7 +1,7 @@ # # preferences.py # -# Copyright (C) 2007-2010 Andrew Resch +# Copyright (C) 2007, 2008 Andrew Resch # # Deluge is free software. # @@ -33,59 +33,99 @@ # # -import os -import gtk +import pygtk +pygtk.require('2.0') +import gtk +import gtk.glade import logging import pkg_resources -from twisted.internet.defer import maybeDeferred import deluge.component as component -import dialogs from deluge.ui.client import client import deluge.common import deluge.error import common +from deluge.configmanager import ConfigManager +import deluge.configmanager log = logging.getLogger(__name__) -class PreferencePage(object): - """ - Must set a name and widget prior to adding to them Preferences dialog. - """ +class Preferences(component.Component): def __init__(self): - self.name = "" - self.widget = None - self.builder = None + component.Component.__init__(self, "Preferences") + self.window = component.get("MainWindow") + self.glade = gtk.glade.XML(pkg_resources.resource_filename( + "deluge.ui.gtkui", "glade/preferences_dialog.glade" + )) + self.pref_dialog = self.glade.get_widget("pref_dialog") + self.pref_dialog.set_icon(common.get_deluge_icon()) + self.treeview = self.glade.get_widget("treeview") + self.notebook = self.glade.get_widget("notebook") + self.gtkui_config = ConfigManager("gtkui.conf") - # Set the core widgets and their config keys - # {widget: (accessor, core_key, non_localhost_widget), ...} - self.core_widgets = {} - # Likewise for local widgets - self.local_widgets = {} + self.glade.get_widget("image_magnet").set_from_file( + deluge.common.get_pixmap("magnet.png")) - def show(self): - """ - Called when the page needs to have it's values updated. - """ - raise NotImplementedError + # 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 [_("Downloads"), _("Network"), _("Bandwidth"), + _("Interface"), _("Other"), _("Daemon"), _("Queue"), _("Proxy"), + _("Cache"), _("Plugins")]: + self.liststore.append([i, category]) + i += 1 - def apply(self): - """ - Called when the settings need to be saved. This method needs to be - defined by the subclass. - """ - raise NotImplementedError + # Setup plugin tab listview + self.plugin_liststore = gtk.ListStore(str, bool) + self.plugin_liststore.set_sort_column_id(0, gtk.SORT_ASCENDING) + self.plugin_listview = self.glade.get_widget("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=0)) - def set_widget(self, widget): - """ - Creates a scrolled window with a proper header for the widget. The - widget is likely a gtk.VBox or similar container. + # Connect to the 'changed' event of TreeViewSelection to get selection + # changes. + self.treeview.get_selection().connect("changed", + self.on_selection_changed) - :param widget: the container widget for all the pref widgets - :type widget: gtk.Widget + self.plugin_listview.get_selection().connect("changed", + self.on_plugin_selection_changed) - """ + self.glade.signal_autoconnect({ + "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_proxy_type_changed": self._on_combo_proxy_type_changed, + "on_button_associate_magnet_clicked": self._on_button_associate_magnet_clicked + }) + + # These get updated by requests done to the core + self.all_plugins = [] + self.enabled_plugins = [] + + 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: @@ -93,7 +133,7 @@ class PreferencePage(object): vbox = gtk.VBox() label = gtk.Label() label.set_use_markup(True) - label.set_markup("" + self.name + "") + label.set_markup("" + name + "") label.set_alignment(0.00, 0.50) label.set_padding(10, 10) vbox.pack_start(label, False, True, 0) @@ -110,318 +150,814 @@ class PreferencePage(object): viewport.add(vbox) scrolled.add(viewport) scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - self.widget = scrolled - - def set_widget_from_file(self, xmlfile, obj): - """ - Sets the widget from an object in a gtkBuilder xml file. - - :param xmlfile: the path to the xml file - :type xmlfile: string - :param obj: the object name to use - :type obj: string - - """ - if not os.path.isfile(xmlfile): - xmlfile = os.path.join(os.path.dirname(__file__), xmlfile) - self.builder = gtk.Builder() - self.builder.add_from_file(xmlfile) - self.set_widget(self.builder.get_object(obj)) - - -class GtkUIPreferencePage(PreferencePage): - def __init__(self, name, xml, widget): - super(GtkUIPreferencePage, self).__init__() - self.name = name - self.set_widget_from_file(xml, widget) - - def show(self): - """ - Called when the page needs to have it's values updated. - """ - if self.core_widgets: - coreconfig = component.get("CoreConfig") - for name, (accessor, key, non_localhost) in self.core_widgets.items(): - widget = self.builder.get_object(name) - getattr(widget, "set_" + accessor)(coreconfig[key]) - if client.is_localhost(): - # Hide any non_localhost widgets - if non_localhost: - self.builder.get_object(non_localhost).hide() - widget.show() - else: - if non_localhost: - # Hide this widget because it should only be shown - # if core is a localhost, but show its non_localhost widget - self.builder.get_object(non_localhost).show() - widget.hide() - - -class DownloadsPreferencePage(GtkUIPreferencePage): - def __init__(self, name, xml, widget): - super(DownloadsPreferencePage, self).__init__(name, xml, widget) - self.core_widgets = { - "download_path_button": ("current_folder", "download_location", "entry_download_path"), - "entry_download_path": ("text", "download_location", ""), - "chk_move_completed": ("active", "move_completed", ""), - "move_completed_path_button": ("current_folder", "move_completed_path", "entry_move_completed_path"), - "entry_move_completed_path": ("text", "move_completed_path", ""), - "chk_autoadd": ("active", "autoadd_enable", ""), - "folder_autoadd": ("current_folder", "autoadd_location", "entry_autoadd"), - "entry_autoadd": ("text", "autoadd_location", ""), - "chk_copy_torrent_file": ("active", "copy_torrent_file", ""), - "torrent_files_button": ("current_folder", "torrentfiles_location", "entry_torrents_path"), - "entry_torrents_path": ("text", "torrentfiles_location", ""), - "chk_del_copy_torrent_file": ("active", "del_copy_torrent_file", ""), - "radio_compact_allocation": ("active", "compact_allocation", ""), - "radio_full_allocation": ("active", "compact_allocation", ""), - "chk_prioritize_first_last_pieces": ("active", "prioritize_first_last_pieces", ""), - "chk_add_paused": ("active", "add_paused", "") - } - -class NetworkPreferencePage(PreferencePage): - pass - -class BandwidthPreferencePage(PreferencePage): - def __init__(self): - self.name = _("Bandwidth") - #xml = pkg_resources.resource_filename("deluge.ui.gtkui", - # "glade/preferences/bandwidth.glade") - xml = "glade/preferences/bandwidth.glade" - self.set_widget_from_file(xml, "bandwidth_prefs_page") - - def update(self): - pass - -class InterfacePreferencePage(PreferencePage): - def __init__(self): - self.name = _("Interface") - #xml = pkg_resources.resource_filename("deluge.ui.gtkui", - # "glade/preferences/interface.glade") - xml = "glade/preferences/interface.glade" - self.set_widget_from_file(xml, "interface_prefs_page") - - def update(self): - pass - -class OtherPreferencePage(PreferencePage): - def __init__(self): - self.name = _("Other") - #xml = pkg_resources.resource_filename("deluge.ui.gtkui", - # "glade/preferences/other.glade") - xml = "glade/preferences/other.glade" - self.set_widget_from_file(xml, "other_prefs_page") - - def update(self): - pass - -class DaemonPreferencePage(PreferencePage): - def __init__(self): - self.name = _("Daemon") - #xml = pkg_resources.resource_filename("deluge.ui.gtkui", - # "glade/preferences/daemon.glade") - xml = "glade/preferences/daemon.glade" - self.set_widget_from_file(xml, "daemon_prefs_page") - - def update(self): - pass - -class QueuePreferencePage(PreferencePage): - def __init__(self): - self.name = _("Queue") - #xml = pkg_resources.resource_filename("deluge.ui.gtkui", - # "glade/preferences/queue.glade") - xml = "glade/preferences/queue.glade" - self.set_widget_from_file(xml, "queue_prefs_page") - - def update(self): - pass - -class ProxyPreferencePage(PreferencePage): - def __init__(self): - self.name = _("Proxy") - #xml = pkg_resources.resource_filename("deluge.ui.gtkui", - # "glade/preferences/proxy.glade") - xml = "glade/preferences/proxy.glade" - self.set_widget_from_file(xml, "proxy_prefs_page") - - def update(self): - pass - -class CachePreferencePage(PreferencePage): - def __init__(self): - self.name = _("Cache") - #xml = pkg_resources.resource_filename("deluge.ui.gtkui", - # "glade/preferences/cache.glade") - xml = "glade/preferences/cache.glade" - self.set_widget_from_file(xml, "cache_prefs_page") - - def update(self): - pass - -class PluginsPreferencePage(PreferencePage): - def __init__(self): - self.name = _("Plugins") - #xml = pkg_resources.resource_filename("deluge.ui.gtkui", - # "glade/preferences/plugins.glade") - xml = "glade/preferences/plugins.glade" - self.set_widget_from_file(xml, "plugins_prefs_page") - - def update(self): - pass - -class Preferences(component.Component): - def __init__(self): - component.Component.__init__(self, "Preferences") - - self.dialog = gtk.Dialog(_("Preferences")) - self.dialog.set_default_size(560, 530) - #self.dialog.set_transient_for(component.get("MainWindow").window) - - # Set the buttons for the dialog - self.button_cancel = self.dialog.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL) - self.button_apply = self.dialog.add_button(gtk.STOCK_APPLY, gtk.RESPONSE_APPLY) - self.button_ok = self.dialog.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK) - - # Setup the content area - self.dialog_hpaned = gtk.HPaned() - vp = gtk.Viewport() - self.listview = gtk.TreeView() - vp.add(self.listview) - self.dialog_hpaned.pack1(vp) - - self.notebook = gtk.Notebook() - self.notebook.set_show_tabs(False) - self.dialog_hpaned.pack2(self.notebook) - self.dialog.get_content_area().pack_start(self.dialog_hpaned) - - # Setup the listview for the preference categories - self.liststore = gtk.ListStore(str) - render = gtk.CellRendererText() - column = gtk.TreeViewColumn(_("Categories"), render, text=0) - self.listview.append_column(column) - self.listview.set_model(self.liststore) - - self.listview.get_selection().connect("changed", self._on_selection_changed) - - # Store the PreferencePages currently in the Preferences dialog - self.pages = {} - - self.add_page(DownloadsPreferencePage(_("Downloads"), "glade/preferences/downloads.glade", "downloads_prefs_page")) - - - def add_page(self, page): - """ - Add a Preference page. - - :param page: the preference page to add - :type page: PreferencePage - - """ - if not isinstance(page, PreferencePage): - raise ValueError("Must be a PreferencePage!") - + scrolled.show_all() # Add this page to the notebook - index = self.notebook.append_page(page.widget) - self.liststore.append([page.name]) - self.pages[index] = page - return page.name + index = self.notebook.append_page(scrolled) + self.liststore.append([index, name]) + return name def remove_page(self, name): - """ - Removes a Preference page. + """Removes a page from the notebook""" + self.page_num_to_remove = None + self.iter_to_remove = None - :param name: the name of the preference page - :type name: string - """ - index = self.get_page_index(name) - if index: - del self.liststore[index] - self.notebook.remove_page(index) + 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 - def get_page_index(self, page): - """ - Returns the index for the page. - - :param page: the name of the preference page - :type page: string - - :returns: the index - :rtype: int - - """ - for index, row in enumerate(self.liststore): - if page == row[0]: - return index - - return None + self.liststore.foreach(check_row, name) + # Remove the page and row + if self.page_num_to_remove != None: + self.notebook.remove_page(self.page_num_to_remove) + if self.iter_to_remove != 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): - """ - Shows the Preferences dialog. + """Page should be the string in the left list.. ie, 'Network' or + 'Bandwidth'""" + if page != None: + for (index, string) in self.liststore: + if page == string: + self.treeview.get_selection().select_path(index) + break - :param page: the name of the page to show initially - :type page: string + component.get("PluginManager").run_on_show_prefs() - """ - if page: - index = self.get_page_index(page) - if index: - self.pages[index].show() - self.listview.get_selection().select_path(index) + # Update the preferences dialog to reflect current config settings + self.core_config = {} + if client.connected(): + 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_cache_status().addCallback(_on_get_cache_status) + + def _on_get_cache_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.listview.get_selection().select_path(0) + self._show() - self.dialog.show_all() + def _show(self): + if self.core_config != {} and self.core_config != None: + core_widgets = { + "download_path_button": \ + ("filename", self.core_config["download_location"]), + "chk_move_completed": \ + ("active", self.core_config["move_completed"]), + "move_completed_path_button": \ + ("filename", self.core_config["move_completed_path"]), + "chk_copy_torrent_file": \ + ("active", self.core_config["copy_torrent_file"]), + "chk_del_copy_torrent_file": \ + ("active", self.core_config["del_copy_torrent_file"]), + "torrent_files_button": \ + ("filename", self.core_config["torrentfiles_location"]), + "chk_autoadd": \ + ("active", self.core_config["autoadd_enable"]), + "folder_autoadd": \ + ("filename", self.core_config["autoadd_location"]), + "radio_compact_allocation": \ + ("active", self.core_config["compact_allocation"]), + "radio_full_allocation": \ + ("not_active", self.core_config["compact_allocation"]), + "chk_prioritize_first_last_pieces": \ + ("active", + self.core_config["prioritize_first_last_pieces"]), + "chk_add_paused": ("active", self.core_config["add_paused"]), + "spin_port_min": ("value", self.core_config["listen_ports"][0]), + "spin_port_max": ("value", self.core_config["listen_ports"][1]), + "active_port_label": ("text", str(self.active_port)), + "chk_random_port": ("active", self.core_config["random_port"]), + "spin_outgoing_port_min": ("value", self.core_config["outgoing_ports"][0]), + "spin_outgoing_port_max": ("value", self.core_config["outgoing_ports"][1]), + "chk_random_outgoing_ports": ("active", self.core_config["random_outgoing_ports"]), + "entry_interface": ("text", self.core_config["listen_interface"]), + "entry_peer_tos": ("text", self.core_config["peer_tos"]), + "chk_dht": ("active", self.core_config["dht"]), + "chk_upnp": ("active", self.core_config["upnp"]), + "chk_natpmp": ("active", self.core_config["natpmp"]), + "chk_utpex": ("active", self.core_config["utpex"]), + "chk_lsd": ("active", self.core_config["lsd"]), + "chk_new_releases": ("active", self.core_config["new_release_check"]), + "chk_send_info": ("active", self.core_config["send_info"]), + "entry_geoip": ("text", self.core_config["geoip_db_location"]), + "combo_encin": ("active", self.core_config["enc_in_policy"]), + "combo_encout": ("active", self.core_config["enc_out_policy"]), + "combo_enclevel": ("active", self.core_config["enc_level"]), + "chk_pref_rc4": ("active", self.core_config["enc_prefer_rc4"]), + "spin_max_connections_global": \ + ("value", self.core_config["max_connections_global"]), + "spin_max_download": \ + ("value", self.core_config["max_download_speed"]), + "spin_max_upload": \ + ("value", self.core_config["max_upload_speed"]), + "spin_max_upload_slots_global": \ + ("value", self.core_config["max_upload_slots_global"]), + "spin_max_half_open_connections": \ + ("value", self.core_config["max_half_open_connections"]), + "spin_max_connections_per_second": \ + ("value", self.core_config["max_connections_per_second"]), + "chk_ignore_limits_on_local_network": \ + ("active", self.core_config["ignore_limits_on_local_network"]), + "chk_rate_limit_ip_overhead": \ + ("active", self.core_config["rate_limit_ip_overhead"]), + "spin_max_connections_per_torrent": \ + ("value", self.core_config["max_connections_per_torrent"]), + "spin_max_upload_slots_per_torrent": \ + ("value", self.core_config["max_upload_slots_per_torrent"]), + "spin_max_download_per_torrent": \ + ("value", self.core_config["max_download_speed_per_torrent"]), + "spin_max_upload_per_torrent": \ + ("value", self.core_config["max_upload_speed_per_torrent"]), + "spin_daemon_port": \ + ("value", self.core_config["daemon_port"]), + "chk_allow_remote_connections": \ + ("active", self.core_config["allow_remote"]), + "spin_active": ("value", self.core_config["max_active_limit"]), + "spin_seeding": ("value", self.core_config["max_active_seeding"]), + "spin_downloading": ("value", self.core_config["max_active_downloading"]), + "chk_dont_count_slow_torrents": ("active", self.core_config["dont_count_slow_torrents"]), + "chk_queue_new_top": ("active", self.core_config["queue_new_to_top"]), + "spin_share_ratio_limit": ("value", self.core_config["share_ratio_limit"]), + "spin_seed_time_ratio_limit": \ + ("value", self.core_config["seed_time_ratio_limit"]), + "spin_seed_time_limit": ("value", self.core_config["seed_time_limit"]), + "chk_seed_ratio": ("active", self.core_config["stop_seed_at_ratio"]), + "spin_share_ratio": ("value", self.core_config["stop_seed_ratio"]), + "chk_remove_ratio": ("active", self.core_config["remove_seed_at_ratio"]), + "spin_cache_size": ("value", self.core_config["cache_size"]), + "spin_cache_expiry": ("value", self.core_config["cache_expiry"]) + } + # Add proxy stuff + for t in ("peer", "web_seed", "tracker", "dht"): + core_widgets["spin_proxy_port_%s" % t] = ("value", self.core_config["proxies"][t]["port"]) + core_widgets["combo_proxy_type_%s" % t] = ("active", self.core_config["proxies"][t]["type"]) + core_widgets["txt_proxy_server_%s" % t] = ("text", self.core_config["proxies"][t]["hostname"]) + core_widgets["txt_proxy_username_%s" % t] = ("text", self.core_config["proxies"][t]["username"]) + core_widgets["txt_proxy_password_%s" % t] = ("text", self.core_config["proxies"][t]["password"]) - def _on_selection_changed(self, treeselection): + # Change a few widgets if we're connected to a remote host + if not client.is_localhost(): + self.glade.get_widget("entry_download_path").show() + self.glade.get_widget("download_path_button").hide() + core_widgets.pop("download_path_button") + core_widgets["entry_download_path"] = ("text", self.core_config["download_location"]) + + self.glade.get_widget("entry_move_completed_path").show() + self.glade.get_widget("move_completed_path_button").hide() + core_widgets.pop("move_completed_path_button") + core_widgets["entry_move_completed_path"] = ("text", self.core_config["move_completed_path"]) + + self.glade.get_widget("entry_torrents_path").show() + self.glade.get_widget("torrent_files_button").hide() + core_widgets.pop("torrent_files_button") + core_widgets["entry_torrents_path"] = ("text", self.core_config["torrentfiles_location"]) + + self.glade.get_widget("entry_autoadd").show() + self.glade.get_widget("folder_autoadd").hide() + core_widgets.pop("folder_autoadd") + core_widgets["entry_autoadd"] = ("text", self.core_config["autoadd_location"]) + else: + self.glade.get_widget("entry_download_path").hide() + self.glade.get_widget("download_path_button").show() + self.glade.get_widget("entry_move_completed_path").hide() + self.glade.get_widget("move_completed_path_button").show() + self.glade.get_widget("entry_torrents_path").hide() + self.glade.get_widget("torrent_files_button").show() + self.glade.get_widget("entry_autoadd").hide() + self.glade.get_widget("folder_autoadd").show() + + # Update the widgets accordingly + for key in core_widgets.keys(): + modifier = core_widgets[key][0] + value = core_widgets[key][1] + widget = self.glade.get_widget(key) + if type(widget) == gtk.FileChooserButton: + for child in widget.get_children(): + child.set_sensitive(True) + widget.set_sensitive(True) + + if modifier == "filename": + if value: + try: + widget.set_current_folder(value) + except Exception, e: + log.debug("Unable to set_current_folder: %s", e) + elif 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": + widget.set_text(value) + + for key in core_widgets.keys(): + widget = self.glade.get_widget(key) + # Update the toggle status if necessary + self.on_toggle(widget) + else: + core_widget_list = [ + "download_path_button", + "chk_move_completed", + "move_completed_path_button", + "chk_copy_torrent_file", + "chk_del_copy_torrent_file", + "torrent_files_button", + "chk_autoadd", + "folder_autoadd", + "radio_compact_allocation", + "radio_full_allocation", + "chk_prioritize_first_last_pieces", + "chk_add_paused", + "spin_port_min", + "spin_port_max", + "active_port_label", + "chk_random_port", + "spin_outgoing_port_min", + "spin_outgoing_port_max", + "chk_random_outgoing_ports", + "entry_interface", + "entry_peer_tos", + "chk_dht", + "chk_upnp", + "chk_natpmp", + "chk_utpex", + "chk_lsd", + "chk_send_info", + "chk_new_releases", + "entry_geoip", + "combo_encin", + "combo_encout", + "combo_enclevel", + "chk_pref_rc4", + "spin_max_connections_global", + "spin_max_download", + "spin_max_upload", + "spin_max_upload_slots_global", + "spin_max_half_open_connections", + "spin_max_connections_per_second", + "chk_ignore_limits_on_local_network", + "chk_rate_limit_ip_overhead", + "spin_max_connections_per_torrent", + "spin_max_upload_slots_per_torrent", + "spin_max_download_per_torrent", + "spin_max_upload_per_torrent", + "spin_daemon_port", + "chk_allow_remote_connections", + "spin_seeding", + "spin_downloading", + "spin_active", + "chk_dont_count_slow_torrents", + "chk_queue_new_top", + "chk_seed_ratio", + "spin_share_ratio", + "chk_remove_ratio", + "spin_share_ratio_limit", + "spin_seed_time_ratio_limit", + "spin_seed_time_limit", + "spin_cache_size", + "spin_cache_expiry", + "button_cache_refresh", + "btn_testport" + ] + for t in ("peer", "web_seed", "tracker", "dht"): + core_widget_list.append("spin_proxy_port_%s" % t) + core_widget_list.append("combo_proxy_type_%s" % t) + core_widget_list.append("txt_proxy_username_%s" % t) + core_widget_list.append("txt_proxy_password_%s" % t) + core_widget_list.append("txt_proxy_server_%s" % t) + + # We don't appear to be connected to a daemon + for key in core_widget_list: + widget = self.glade.get_widget(key) + if type(widget) == gtk.FileChooserButton: + for child in widget.get_children(): + child.set_sensitive(False) + widget.set_sensitive(False) + + ## Downloads tab ## + self.glade.get_widget("chk_show_dialog").set_active( + self.gtkui_config["interactive_add"]) + self.glade.get_widget("chk_focus_dialog").set_active( + self.gtkui_config["focus_add_dialog"]) + + ## Interface tab ## + self.glade.get_widget("chk_use_tray").set_active( + self.gtkui_config["enable_system_tray"]) + self.glade.get_widget("chk_min_on_close").set_active( + self.gtkui_config["close_to_tray"]) + self.glade.get_widget("chk_start_in_tray").set_active( + self.gtkui_config["start_in_tray"]) + self.glade.get_widget("chk_enable_appindicator").set_active( + self.gtkui_config["enable_appindicator"]) + self.glade.get_widget("chk_lock_tray").set_active( + self.gtkui_config["lock_tray"]) + self.glade.get_widget("chk_classic_mode").set_active( + self.gtkui_config["classic_mode"]) + self.glade.get_widget("chk_show_rate_in_title").set_active( + self.gtkui_config["show_rate_in_title"]) + + ## Other tab ## + self.glade.get_widget("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: + if plugin in enabled_plugins: + enabled = True + else: + enabled = False + row = self.plugin_liststore.append() + self.plugin_liststore.set_value(row, 0, plugin) + self.plugin_liststore.set_value(row, 1, enabled) + + # Now show the dialog + self.pref_dialog.show() + + def set_config(self, hide=False): """ - This is called when the preference page changes or when it's initially - showed. We must call the page's update() method prior to showing the - page. + Sets all altered config values in the core. + + :param hide: bool, if True, will not re-show the dialog and will hide it instead """ - (model, row) = treeselection.get_selected() - index = self.get_page_index(model[row][0]) - if index is None: + try: + from hashlib import sha1 as sha_hash + except ImportError: + from sha import new as sha_hash + + # Get the values from the dialog + new_core_config = {} + new_gtkui_config = {} + + ## Downloads tab ## + new_gtkui_config["interactive_add"] = \ + self.glade.get_widget("chk_show_dialog").get_active() + new_gtkui_config["focus_add_dialog"] = \ + self.glade.get_widget("chk_focus_dialog").get_active() + new_core_config["copy_torrent_file"] = \ + self.glade.get_widget("chk_copy_torrent_file").get_active() + new_core_config["del_copy_torrent_file"] = \ + self.glade.get_widget("chk_del_copy_torrent_file").get_active() + new_core_config["move_completed"] = \ + self.glade.get_widget("chk_move_completed").get_active() + if client.is_localhost(): + new_core_config["download_location"] = \ + self.glade.get_widget("download_path_button").get_filename() + new_core_config["move_completed_path"] = \ + self.glade.get_widget("move_completed_path_button").get_filename() + new_core_config["torrentfiles_location"] = \ + self.glade.get_widget("torrent_files_button").get_filename() + else: + new_core_config["download_location"] = \ + self.glade.get_widget("entry_download_path").get_text() + new_core_config["move_completed_path"] = \ + self.glade.get_widget("entry_move_completed_path").get_text() + new_core_config["torrentfiles_location"] = \ + self.glade.get_widget("entry_torrents_path").get_text() + + new_core_config["autoadd_enable"] = \ + self.glade.get_widget("chk_autoadd").get_active() + if client.is_localhost(): + new_core_config["autoadd_location"] = \ + self.glade.get_widget("folder_autoadd").get_filename() + else: + new_core_config["autoadd_location"] = \ + self.glade.get_widget("entry_autoadd").get_text() + + new_core_config["compact_allocation"] = \ + self.glade.get_widget("radio_compact_allocation").get_active() + new_core_config["prioritize_first_last_pieces"] = \ + self.glade.get_widget( + "chk_prioritize_first_last_pieces").get_active() + new_core_config["add_paused"] = \ + self.glade.get_widget("chk_add_paused").get_active() + + ## Network tab ## + listen_ports = ( + self.glade.get_widget("spin_port_min").get_value_as_int(), + self.glade.get_widget("spin_port_max").get_value_as_int() + ) + new_core_config["listen_ports"] = listen_ports + new_core_config["random_port"] = \ + self.glade.get_widget("chk_random_port").get_active() + outgoing_ports = ( + self.glade.get_widget("spin_outgoing_port_min").get_value_as_int(), + self.glade.get_widget("spin_outgoing_port_max").get_value_as_int() + ) + new_core_config["outgoing_ports"] = outgoing_ports + new_core_config["random_outgoing_ports"] = \ + self.glade.get_widget("chk_random_outgoing_ports").get_active() + new_core_config["listen_interface"] = self.glade.get_widget("entry_interface").get_text() + new_core_config["peer_tos"] = self.glade.get_widget("entry_peer_tos").get_text() + new_core_config["dht"] = self.glade.get_widget("chk_dht").get_active() + new_core_config["upnp"] = self.glade.get_widget("chk_upnp").get_active() + new_core_config["natpmp"] = \ + self.glade.get_widget("chk_natpmp").get_active() + new_core_config["utpex"] = \ + self.glade.get_widget("chk_utpex").get_active() + new_core_config["lsd"] = \ + self.glade.get_widget("chk_lsd").get_active() + new_core_config["enc_in_policy"] = \ + self.glade.get_widget("combo_encin").get_active() + new_core_config["enc_out_policy"] = \ + self.glade.get_widget("combo_encout").get_active() + new_core_config["enc_level"] = \ + self.glade.get_widget("combo_enclevel").get_active() + new_core_config["enc_prefer_rc4"] = \ + self.glade.get_widget("chk_pref_rc4").get_active() + + ## Bandwidth tab ## + new_core_config["max_connections_global"] = \ + self.glade.get_widget( + "spin_max_connections_global").get_value_as_int() + new_core_config["max_download_speed"] = \ + self.glade.get_widget("spin_max_download").get_value() + new_core_config["max_upload_speed"] = \ + self.glade.get_widget("spin_max_upload").get_value() + new_core_config["max_upload_slots_global"] = \ + self.glade.get_widget( + "spin_max_upload_slots_global").get_value_as_int() + new_core_config["max_half_open_connections"] = \ + self.glade.get_widget("spin_max_half_open_connections").get_value_as_int() + new_core_config["max_connections_per_second"] = \ + self.glade.get_widget( + "spin_max_connections_per_second").get_value_as_int() + new_core_config["max_connections_per_torrent"] = \ + self.glade.get_widget( + "spin_max_connections_per_torrent").get_value_as_int() + new_core_config["max_upload_slots_per_torrent"] = \ + self.glade.get_widget( + "spin_max_upload_slots_per_torrent").get_value_as_int() + new_core_config["max_upload_speed_per_torrent"] = \ + self.glade.get_widget( + "spin_max_upload_per_torrent").get_value() + new_core_config["max_download_speed_per_torrent"] = \ + self.glade.get_widget( + "spin_max_download_per_torrent").get_value() + new_core_config["ignore_limits_on_local_network"] = \ + self.glade.get_widget("chk_ignore_limits_on_local_network").get_active() + new_core_config["rate_limit_ip_overhead"] = \ + self.glade.get_widget("chk_rate_limit_ip_overhead").get_active() + + ## Interface tab ## + new_gtkui_config["enable_system_tray"] = \ + self.glade.get_widget("chk_use_tray").get_active() + new_gtkui_config["close_to_tray"] = \ + self.glade.get_widget("chk_min_on_close").get_active() + new_gtkui_config["start_in_tray"] = \ + self.glade.get_widget("chk_start_in_tray").get_active() + new_gtkui_config["enable_appindicator"] = \ + self.glade.get_widget("chk_enable_appindicator").get_active() + new_gtkui_config["lock_tray"] = \ + self.glade.get_widget("chk_lock_tray").get_active() + passhex = sha_hash(\ + self.glade.get_widget("txt_tray_password").get_text()).hexdigest() + if passhex != "c07eb5a8c0dc7bb81c217b67f11c3b7a5e95ffd7": + new_gtkui_config["tray_password"] = passhex + new_gtkui_config["classic_mode"] = \ + self.glade.get_widget("chk_classic_mode").get_active() + new_gtkui_config["show_rate_in_title"] = \ + self.glade.get_widget("chk_show_rate_in_title").get_active() + + ## Other tab ## + new_gtkui_config["show_new_releases"] = \ + self.glade.get_widget("chk_show_new_releases").get_active() + new_core_config["send_info"] = \ + self.glade.get_widget("chk_send_info").get_active() + new_core_config["geoip_db_location"] = \ + self.glade.get_widget("entry_geoip").get_text() + + ## Daemon tab ## + new_core_config["daemon_port"] = \ + self.glade.get_widget("spin_daemon_port").get_value_as_int() + new_core_config["allow_remote"] = \ + self.glade.get_widget("chk_allow_remote_connections").get_active() + new_core_config["new_release_check"] = \ + self.glade.get_widget("chk_new_releases").get_active() + + ## Proxy tab ## + new_core_config["proxies"] = {} + for t in ("peer", "web_seed", "tracker", "dht"): + new_core_config["proxies"][t] = {} + new_core_config["proxies"][t]["type"] = \ + self.glade.get_widget("combo_proxy_type_%s" % t).get_active() + new_core_config["proxies"][t]["port"] = \ + self.glade.get_widget("spin_proxy_port_%s" % t).get_value_as_int() + new_core_config["proxies"][t]["username"] = \ + self.glade.get_widget("txt_proxy_username_%s" % t).get_text() + new_core_config["proxies"][t]["password"] = \ + self.glade.get_widget("txt_proxy_password_%s" % t).get_text() + new_core_config["proxies"][t]["hostname"] = \ + self.glade.get_widget("txt_proxy_server_%s" % t).get_text() + + ## Queue tab ## + new_core_config["queue_new_to_top"] = \ + self.glade.get_widget("chk_queue_new_top").get_active() + new_core_config["max_active_seeding"] = \ + self.glade.get_widget("spin_seeding").get_value_as_int() + new_core_config["max_active_downloading"] = \ + self.glade.get_widget("spin_downloading").get_value_as_int() + new_core_config["max_active_limit"] = \ + self.glade.get_widget("spin_active").get_value_as_int() + new_core_config["dont_count_slow_torrents"] = \ + self.glade.get_widget("chk_dont_count_slow_torrents").get_active() + new_core_config["stop_seed_at_ratio"] = \ + self.glade.get_widget("chk_seed_ratio").get_active() + new_core_config["remove_seed_at_ratio"] = \ + self.glade.get_widget("chk_remove_ratio").get_active() + new_core_config["stop_seed_ratio"] = \ + self.glade.get_widget("spin_share_ratio").get_value() + new_core_config["share_ratio_limit"] = \ + self.glade.get_widget("spin_share_ratio_limit").get_value() + new_core_config["seed_time_ratio_limit"] = \ + self.glade.get_widget("spin_seed_time_ratio_limit").get_value() + new_core_config["seed_time_limit"] = \ + self.glade.get_widget("spin_seed_time_limit").get_value() + + ## Cache tab ## + new_core_config["cache_size"] = \ + self.glade.get_widget("spin_cache_size").get_value_as_int() + new_core_config["cache_expiry"] = \ + self.glade.get_widget("spin_cache_expiry").get_value_as_int() + + # Run plugin hook to apply preferences + component.get("PluginManager").run_on_apply_prefs() + + # GtkUI + for key in new_gtkui_config.keys(): + # 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.keys(): + # 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() + + def hide(self): + self.glade.get_widget("port_img").hide() + self.pref_dialog.hide() + + def __update_cache_status(self): + # Updates the cache status labels with the info in the dict + for widget in self.glade.get_widget_prefix("label_cache_"): + key = widget.get_name()[len("label_cache_"):] + value = self.cache_status[key] + if type(value) == float: + value = "%.2f" % value + else: + value = str(value) + + widget.set_text(value) + + def _on_button_cache_refresh_clicked(self, widget): + def on_get_cache_status(status): + self.cache_status = status + self.__update_cache_status() + + client.core.get_cache_status().addCallback(on_get_cache_status) + + def on_pref_dialog_delete_event(self, widget, event): + self.hide() + return True + + def on_toggle(self, widget): + """Handles widget sensitivity based on radio/check button values.""" + try: + value = widget.get_active() + except: return - def on_page_show(result): - self.notebook.set_current_page(index) + dependents = { + "chk_show_dialog": {"chk_focus_dialog": True}, + "chk_random_port": {"spin_port_min": False, + "spin_port_max": False}, + "chk_random_outgoing_ports": {"spin_outgoing_port_min": False, + "spin_outgoing_port_max": False}, + "chk_use_tray": {"chk_min_on_close": True, + "chk_start_in_tray": True, + "chk_enable_appindicator": 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_button" : True}, + "chk_copy_torrent_file" : {"torrent_files_button" : True, + "chk_del_copy_torrent_file" : True}, + "chk_autoadd" : {"folder_autoadd" : True}, + "chk_seed_ratio" : {"spin_share_ratio": True, + "chk_remove_ratio" : True} + } + + def update_dependent_widgets(name, value): + dependency = dependents[name] + for dep in dependency.keys(): + depwidget = self.glade.get_widget(dep) + sensitive = [not value, value][dependency[dep]] + depwidget.set_sensitive(sensitive) + if dep in dependents: + update_dependent_widgets(dep, depwidget.get_active() and sensitive) + + for key in dependents.keys(): + if widget != self.glade.get_widget(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: - maybeDeferred(self.pages[index].show).addCallback(on_page_show) - except Exception, e: - dialogs.ErrorDialog( - _("Error with preference page"), - _("Could not show preference page correctly."), - self.dialog, - traceback=True).run() + self.notebook.set_current_page(model.get_value(row, 0)) + except TypeError: + pass -if __name__ == "__main__": - p = Preferences() - d = DownloadsPreferencePage(_("Downloads"), "glade/preferences/downloads.glade", "downloads_prefs_page") - p.add_page(d) - #d2 = NetworkPreferencePage(_("Network"), "glade/preferences/network.glade", "network_prefs_page") - #p.add_page(d2) - ##d3 = BandwidthPreferencePage() - ##p.add_page(d3) - ##d4 = InterfacePreferencePage() - ##p.add_page(d4) - ##d5 = OtherPreferencePage() - ##p.add_page(d5) - ##d6 = DaemonPreferencePage() - ##p.add_page(d6) - ##d7 = QueuePreferencePage() - ##p.add_page(d7) - ##d8 = ProxyPreferencePage() - ##p.add_page(d8) - ##d9 = CachePreferencePage() - ##p.add_page(d9) - ##d10 = PluginsPreferencePage() - ##p.add_page(d10) - p.show() - gtk.main() + def on_test_port_clicked(self, data): + log.debug("on_test_port_clicked") + def on_get_test(status): + if status: + self.glade.get_widget("port_img").set_from_stock(gtk.STOCK_YES, 4) + self.glade.get_widget("port_img").show() + else: + self.glade.get_widget("port_img").set_from_stock(gtk.STOCK_DIALOG_WARNING, 4) + self.glade.get_widget("port_img").show() + client.core.test_listen_port().addCallback(on_get_test) + self.glade.get_widget("port_img").set_from_file( + deluge.common.get_pixmap('loading.gif') + ) + self.glade.get_widget("port_img").show() + client.force_call() + def on_plugin_toggled(self, renderer, path): + log.debug("on_plugin_toggled") + 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) + self.plugin_liststore.set_value(row, 1, not value) + if not value: + client.core.enable_plugin(name) + else: + client.core.disable_plugin(name) + component.get("PluginManager").disable_plugin(name) + + 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.glade.get_widget("label_plugin_author").set_text(plugin_info["Author"]) + self.glade.get_widget("label_plugin_version").set_text(plugin_info["Version"]) + self.glade.get_widget("label_plugin_email").set_text(plugin_info["Author-email"]) + self.glade.get_widget("label_plugin_homepage").set_text(plugin_info["Home-page"]) + self.glade.get_widget("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 = chooser.get_filename() + else: + chooser.destroy() + return + + import base64 + import shutil + import os.path + filename = os.path.split(filepath)[1] + shutil.copyfile( + filepath, + os.path.join(deluge.configmanager.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 + filedump = base64.encodestring(open(filepath, "rb").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_proxy_type_changed(self, widget): + name = widget.get_name().replace("combo_proxy_type_", "") + proxy_type = widget.get_model()[widget.get_active()][0] + + prefixes = ["txt_proxy_", "label_proxy_", "spin_proxy_"] + hides = [] + shows = [] + + if proxy_type == "None": + hides.extend(["password", "username", "server", "port"]) + elif proxy_type in ("Socksv4", "Socksv5", "HTTP"): + hides.extend(["password", "username"]) + shows.extend(["server", "port"]) + elif proxy_type in ("Socksv5 W/ Auth", "HTTP W/ Auth"): + shows.extend(["password", "username", "server", "port"]) + + for h in hides: + for p in prefixes: + w = self.glade.get_widget(p + h + "_" + name) + if w: + w.hide() + for s in shows: + for p in prefixes: + w = self.glade.get_widget(p + s + "_" + name) + if w: + w.show() + + def _on_button_associate_magnet_clicked(self, widget): + common.associate_magnet_links(True)