diff --git a/deluge/common.py b/deluge/common.py index 923e1585d..61ab0095a 100644 --- a/deluge/common.py +++ b/deluge/common.py @@ -2,19 +2,19 @@ # common.py # # Copyright (C) 2007, 2008 Andrew Resch ('andar') -# +# # Deluge is free software. -# +# # You may redistribute it and/or modify it under the terms of the # GNU General Public License, as published by the Free Software # Foundation; either version 3 of the License, or (at your option) # any later version. -# +# # deluge is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with deluge. If not, write to: # The Free Software Foundation, Inc., @@ -90,9 +90,9 @@ def get_revision(): f.close() except IOError, e: pass - + return revision - + def get_default_config_dir(filename=None): """ Returns the config path if no filename is specified Returns the config directory + filename as a path if filename is specified @@ -134,7 +134,7 @@ def get_pixmap(fname): def get_logo(size): """Returns a deluge logo pixbuf based on the size parameter.""" import gtk - if windows_check(): + if windows_check(): return gtk.gdk.pixbuf_new_from_file_at_size(get_pixmap("deluge.png"), \ size, size) else: @@ -161,16 +161,16 @@ def open_url_in_browser(url): webbrowser.open(self.url) BrowserThread(url).start() return False - + import gobject gobject.idle_add(start_browser) -def build_menu_radio_list(value_list, callback, pref_value=None, - suffix=None, show_notset=False, notset_label=None, notset_lessthan=0, +def build_menu_radio_list(value_list, callback, pref_value=None, + suffix=None, show_notset=False, notset_label=None, notset_lessthan=0, show_other=False, show_activated=False, activated_label=None): - # Build a menu with radio menu items from a list and connect them to - # the callback. The pref_value is what you would like to test for the + # Build a menu with radio menu items from a list and connect them to + # the callback. The pref_value is what you would like to test for the # default active radio item. import gtk if notset_label is None: @@ -185,14 +185,14 @@ def build_menu_radio_list(value_list, callback, pref_value=None, if pref_value > -1 and pref_value not in value_list: value_list.pop() value_list.append(pref_value) - + for value in sorted(value_list): if suffix != None: menuitem = gtk.RadioMenuItem(group, str(value) + " " + \ suffix) else: menuitem = gtk.RadioMenuItem(group, str(value)) - + group = menuitem if value == pref_value and pref_value != None: @@ -206,7 +206,7 @@ def build_menu_radio_list(value_list, callback, pref_value=None, if show_activated is True: for value in sorted(value_list): menuitem = gtk.RadioMenuItem(group, str(activated_label)) - + group = menuitem if value == pref_value and pref_value != None: @@ -226,7 +226,7 @@ def build_menu_radio_list(value_list, callback, pref_value=None, menuitem.set_active(True) menuitem.connect("toggled", callback) menu.append(menuitem) - + # Add the Other... menuitem if show_other is True: menuitem = gtk.SeparatorMenuItem() @@ -235,7 +235,7 @@ def build_menu_radio_list(value_list, callback, pref_value=None, menuitem.set_name(_("Other...")) menuitem.connect("activate", callback) menu.append(menuitem) - + return menu def show_other_dialog(string, default=None): @@ -243,7 +243,7 @@ def show_other_dialog(string, default=None): import gtk import gtk.glade dialog_glade = gtk.glade.XML( - pkg_resources.resource_filename("deluge.ui.gtkui", + pkg_resources.resource_filename("deluge.ui.gtkui", "glade/dgtkpopups.glade")) speed_dialog = dialog_glade.get_widget("speed_dialog") spin_title = dialog_glade.get_widget("spin_title") @@ -258,10 +258,10 @@ def show_other_dialog(string, default=None): else: speed_dialog.destroy() return None - + speed_dialog.destroy() return value - + ## Formatting text functions def fsize(fsize_b): @@ -292,7 +292,7 @@ def fpeer(num_peers, total_peers): return "%d (%d)" % (num_peers, total_peers) else: return "%d" % num_peers - + def ftime(seconds): """Returns a formatted time string""" if seconds == 0: @@ -323,7 +323,7 @@ def is_url(url): return bool(re.search('^(https?|ftp|udp)://', url)) def fetch_url(url): - """Downloads a torrent file from a given + """Downloads a torrent file from a given URL and checks the file's validity.""" import urllib from deluge.log import LOG as log @@ -338,7 +338,7 @@ def fetch_url(url): else: log.debug("URL doesn't appear to be a valid torrent file: %s", url) return None - + def pythonize(var): """Translates DBUS types back to basic Python types.""" if isinstance(var, list): diff --git a/deluge/component.py b/deluge/component.py index 90a937fc7..b44db7c9d 100644 --- a/deluge/component.py +++ b/deluge/component.py @@ -2,19 +2,19 @@ # component.py # # Copyright (C) 2007, 2008 Andrew Resch ('andar') -# +# # Deluge is free software. -# +# # You may redistribute it and/or modify it under the terms of the # GNU General Public License, as published by the Free Software # Foundation; either version 3 of the License, or (at your option) # any later version. -# +# # deluge is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with deluge. If not, write to: # The Free Software Foundation, Inc., @@ -47,18 +47,18 @@ class Component: self._interval = interval self._timer = None self._state = COMPONENT_STATE.index("Stopped") - + def get_state(self): return self._state - + def start(self): pass - + def _start(self): self._state = COMPONENT_STATE.index("Started") if self._update(): self._timer = gobject.timeout_add(self._interval, self._update) - + def stop(self): pass @@ -68,20 +68,20 @@ class Component: gobject.source_remove(self._timer) except: pass - + def _pause(self): self._state = COMPONENT_STATE.index("Paused") try: gobject.source_remove(self._timer) except: pass - + def _resume(self): self._start() - + def shutdown(self): pass - + def _update(self): try: self.update() @@ -90,29 +90,29 @@ class Component: # update method. return False return True - - + + class ComponentRegistry: def __init__(self): self.components = {} self.depend = {} - + def register(self, name, obj, depend): """Registers a component.. depend must be list or None""" log.debug("Registered %s with ComponentRegistry..", name) self.components[name] = obj if depend != None: self.depend[name] = depend - + def get(self, name): """Returns a reference to the component 'name'""" return self.components[name] - + def start(self): """Starts all components""" for component in self.components.keys(): self.start_component(component) - + def start_component(self, name): """Starts a component""" # Check to see if this component has any dependencies @@ -125,24 +125,24 @@ class ComponentRegistry: log.debug("Starting component %s..", name) self.components[name].start() self.components[name]._start() - + def stop(self): """Stops all components""" for component in self.components.keys(): self.stop_component(component) - + def stop_component(self, component): if self.components[component].get_state() != \ COMPONENT_STATE.index("Stopped"): log.debug("Stopping component %s..", component) self.components[component].stop() self.components[component]._stop() - + def pause(self): """Pauses all components. Stops calling update()""" for component in self.components.keys(): self.pause_component(component) - + def pause_component(self, component): if self.components[component].get_state() not in \ [COMPONENT_STATE.index("Paused"), COMPONENT_STATE.index("Stopped")]: @@ -153,11 +153,11 @@ class ComponentRegistry: """Resumes all components. Starts calling update()""" for component in self.components.keys(): self.resume_component(component) - + def resume_component(self, component): if self.components[component].get_state() == COMPONENT_STATE.index("Paused"): log.debug("Resuming component %s..", component) - self.components[component]._resume() + self.components[component]._resume() def update(self): """Updates all components""" @@ -166,9 +166,9 @@ class ComponentRegistry: if self.components[component].get_state() == \ COMPONENT_STATE.index("Started"): self.components[component].update() - + return True - + def shutdown(self): """Shuts down all components. This should be called when the program exits so that components can do any necessary clean-up.""" @@ -180,8 +180,8 @@ class ComponentRegistry: self.components[component].shutdown() except Exception, e: log.debug("Unable to call shutdown(): %s", e) - - + + _ComponentRegistry = ComponentRegistry() def register(name, obj, depend=None): @@ -215,7 +215,7 @@ def resume(component=None): _ComponentRegistry.resume() else: _ComponentRegistry.resume_component(component) - + def update(): """Updates all components""" _ComponentRegistry.update() @@ -223,7 +223,7 @@ def update(): def shutdown(): """Shutdowns all components""" _ComponentRegistry.shutdown() - + def get(component): """Return a reference to the component""" return _ComponentRegistry.get(component) diff --git a/deluge/config.py b/deluge/config.py index 2306e4a2e..752274974 100644 --- a/deluge/config.py +++ b/deluge/config.py @@ -2,19 +2,19 @@ # config.py # # Copyright (C) 2007 Andrew Resch ('andar') -# +# # Deluge is free software. -# +# # You may redistribute it and/or modify it under the terms of the # GNU General Public License, as published by the Free Software # Foundation; either version 3 of the License, or (at your option) # any later version. -# +# # deluge is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with deluge. If not, write to: # The Free Software Foundation, Inc., @@ -42,7 +42,7 @@ from deluge.log import LOG as log class Config: """This class is used to access configuration files.""" - + def __init__(self, filename, defaults=None, config_dir=None): log.debug("Config created with filename: %s", filename) log.debug("Config defaults: %s", defaults) @@ -50,7 +50,7 @@ class Config: self.previous_config = {} self.set_functions = {} self._change_callback = None - + # If defaults is not None then we need to use "defaults". if defaults != None: self.config = defaults @@ -60,18 +60,18 @@ class Config: self.config_file = deluge.common.get_default_config_dir(filename) else: self.config_file = os.path.join(config_dir, filename) - + self.load(self.config_file) # Save self.save() - + # This will get set with a gobject.timeout_add whenever a config option # is set. self.save_timer = None - + def __del__(self): self.save() - + def load(self, filename=None): """Load a config file either by 'filename' or the filename set during construction of this object.""" @@ -88,7 +88,7 @@ class Config: log.warning("IOError: Unable to load file '%s'", filename) except EOFError: pkl_file.close() - + def save(self, filename=None): """Save configuration to either 'filename' or the filename set during construction of this object.""" @@ -107,16 +107,16 @@ class Config: return except (EOFError, IOError): log.warning("IOError: Unable to open file: '%s'", filename) - + try: pkl_file = open(filename, "wb") cPickle.dump(self.config, pkl_file) pkl_file.close() except IOError: log.warning("IOError: Unable to save file '%s'", filename) - + self.save_timer = None - + def set(self, key, value): """Set the 'key' with 'value'.""" # Sets the "key" with "value" in the config dict @@ -143,7 +143,7 @@ class Config: def get(self, key): """Get the value of 'key'. If it is an invalid key then get() will return None.""" - # Attempts to get the "key" value and returns None if the key is + # Attempts to get the "key" value and returns None if the key is # invalid try: value = self.config[key] @@ -156,15 +156,15 @@ class Config: def get_config(self): """Returns the entire configuration as a dictionary.""" return self.config - + def get_previous_config(self): """Returns the config prior to the last set()""" return self.previous_config - + def register_change_callback(self, callback): """Registers a callback that will be called when a value is changed""" self._change_callback = callback - + def register_set_function(self, key, function, apply_now=True): """Register a function to be run when a config value changes.""" log.debug("Registering function for %s key..", key) @@ -173,13 +173,13 @@ class Config: if apply_now: self.set_functions[key](key, self.config[key]) return - + def apply_all(self): """Runs all set functions""" log.debug("Running all set functions..") for key in self.set_functions.keys(): self.set_functions[key](key, self.config[key]) - + def __getitem__(self, key): return self.config[key] diff --git a/deluge/configmanager.py b/deluge/configmanager.py index 956849c37..6fd94e191 100644 --- a/deluge/configmanager.py +++ b/deluge/configmanager.py @@ -2,19 +2,19 @@ # configmanager.py # # Copyright (C) 2007 Andrew Resch ('andar') -# +# # Deluge is free software. -# +# # You may redistribute it and/or modify it under the terms of the # GNU General Public License, as published by the Free Software # Foundation; either version 3 of the License, or (at your option) # any later version. -# +# # deluge is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with deluge. If not, write to: # The Free Software Foundation, Inc., @@ -62,36 +62,36 @@ class _ConfigManager: os.makedirs(directory) except Exception, e: log.warning("Unable to make config directory: %s", e) - + self.config_directory = directory - + def get_config_dir(self): log.debug("get_config_dir: %s", self.config_directory) return self.config_directory - + def close(self, config): """Closes a config file.""" try: del self.config_files[config] except KeyError: pass - + def save(self): """Saves all the configs to disk.""" for key in self.config_files.keys(): self.config_files[key].save() # We need to return True to keep the timer active return True - + def get_config(self, config_file, defaults=None): """Get a reference to the Config object for this filename""" log.debug("Getting config '%s'", config_file) # Create the config object if not already created if config_file not in self.config_files.keys(): self.config_files[config_file] = Config(config_file, defaults, self.config_directory) - + return self.config_files[config_file] - + # Singleton functions _configmanager = _ConfigManager() @@ -107,6 +107,6 @@ def get_config_dir(filename=None): return os.path.join(_configmanager.get_config_dir(), filename) else: return _configmanager.get_config_dir() - + def close(config): return _configmanager.close(config) diff --git a/deluge/core/alertmanager.py b/deluge/core/alertmanager.py index 464e9b3dc..1cdb769c1 100644 --- a/deluge/core/alertmanager.py +++ b/deluge/core/alertmanager.py @@ -2,19 +2,19 @@ # alertmanager.py # # Copyright (C) 2007 Andrew Resch ('andar') -# +# # Deluge is free software. -# +# # You may redistribute it and/or modify it under the terms of the # GNU General Public License, as published by the Free Software # Foundation; either version 3 of the License, or (at your option) # any later version. -# +# # deluge is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with deluge. If not, write to: # The Free Software Foundation, Inc., @@ -54,17 +54,17 @@ class AlertManager(component.Component): lt.alert.category_t.tracker_notification | lt.alert.category_t.status_notification | lt.alert.category_t.ip_block_notification) - + # handlers is a dictionary of lists {"alert_type": [handler1,h2,..]} self.handlers = {} def update(self): self.handle_alerts() - + def shutdown(self): del self.session del self.handlers - + def register_handler(self, alert_type, handler): """Registers a function that will be called when 'alert_type' is pop'd in handle_alerts. The handler function should look like: @@ -75,11 +75,11 @@ class AlertManager(component.Component): # There is no entry for this alert type yet, so lets make it with an # empty list. self.handlers[alert_type] = [] - + # Append the handler to the list in the handlers dictionary self.handlers[alert_type].append(handler) log.debug("Registered handler for alert %s", alert_type) - + def deregister_handler(self, handler): """De-registers the 'handler' function from all alert types.""" # Iterate through all handlers and remove 'handler' where found @@ -87,7 +87,7 @@ class AlertManager(component.Component): if handler in value: # Handler is in this alert type list value.remove(handler) - + def handle_alerts(self, wait=False): """Pops all libtorrent alerts in the session queue and handles them appropriately.""" @@ -101,7 +101,7 @@ class AlertManager(component.Component): log.debug("%s: %s", alert_type, alert.message()) except RuntimeError: log.debug("%s", alert_type) - + # Call any handlers for this alert type if alert_type in self.handlers.keys(): for handler in self.handlers[alert_type]: @@ -109,7 +109,7 @@ class AlertManager(component.Component): gobject.idle_add(handler, alert) else: handler(alert) - + alert = self.session.pop_alert() # Return True so that the timer continues diff --git a/deluge/core/autoadd.py b/deluge/core/autoadd.py index b15b1d0cc..ef21cf547 100644 --- a/deluge/core/autoadd.py +++ b/deluge/core/autoadd.py @@ -2,19 +2,19 @@ # autoadd.py # # Copyright (C) 2008 Andrew Resch ('andar') -# +# # Deluge is free software. -# +# # You may redistribute it and/or modify it under the terms of the # GNU General Public License, as published by the Free Software # Foundation; either version 3 of the License, or (at your option) # any later version. -# +# # deluge is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with deluge. If not, write to: # The Free Software Foundation, Inc., @@ -48,7 +48,7 @@ class AutoAdd(component.Component): component.Component.__init__(self, "AutoAdd", depend=["TorrentManager"], interval=5000) # Get the core config self.config = ConfigManager("core.conf") - + # A list of filenames self.invalid_torrents = [] # Filename:Attempts @@ -59,19 +59,19 @@ class AutoAdd(component.Component): self._on_autoadd_enable, apply_now=True) self.config.register_set_function("autoadd_location", self._on_autoadd_location) - + def update(self): if not self.config["autoadd_enable"]: # We shouldn't be updating because autoadd is not enabled component.pause("AutoAdd") return - + # Check the auto add folder for new torrents to add if not os.path.exists(self.config["autoadd_location"]): log.warning("Invalid AutoAdd folder: %s", self.config["autoadd_location"]) component.pause("AutoAdd") return - + for filename in os.listdir(self.config["autoadd_location"]): if filename.split(".")[-1] == "torrent": filepath = os.path.join(self.config["autoadd_location"], filename) @@ -88,16 +88,16 @@ class AutoAdd(component.Component): os.rename(filepath, filepath + ".invalid") del self.attempts[filename] self.invalid_torrents.remove(filename) - else: + else: self.invalid_torrents.append(filename) self.attempts[filename] = 1 continue - + # The torrent looks good, so lets add it to the session component.get("TorrentManager").add(filedump=filedump, filename=filename) os.remove(filepath) - + def load_torrent(self, filename): try: log.debug("Attempting to open %s for add.", filename) @@ -109,10 +109,10 @@ class AutoAdd(component.Component): except IOError, e: log.warning("Unable to open %s: %s", filename, e) raise e - + # Get the info to see if any exceptions are raised info = lt.torrent_info(lt.bdecode(filedump)) - + return filedump def _on_autoadd_enable(self, key, value): diff --git a/deluge/core/daemon.py b/deluge/core/daemon.py index b88241a62..d2fbf0d1e 100644 --- a/deluge/core/daemon.py +++ b/deluge/core/daemon.py @@ -2,19 +2,19 @@ # daemon.py # # Copyright (C) 2007 Andrew Resch ('andar') -# +# # Deluge is free software. -# +# # You may redistribute it and/or modify it under the terms of the # GNU General Public License, as published by the Free Software # Foundation; either version 3 of the License, or (at your option) # any later version. -# +# # deluge is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with deluge. If not, write to: # The Free Software Foundation, Inc., @@ -39,8 +39,8 @@ class Daemon: def __init__(self, options, args): version = deluge.common.get_version() if deluge.common.get_revision() != "": - version = version + "r" + deluge.common.get_revision() - + version = version + "r" + deluge.common.get_revision() + log.info("Deluge daemon %s", version) log.debug("options: %s", options) log.debug("args: %s", args) diff --git a/deluge/core/oldstateupgrader.py b/deluge/core/oldstateupgrader.py index ac08b50fb..ff2220ff5 100644 --- a/deluge/core/oldstateupgrader.py +++ b/deluge/core/oldstateupgrader.py @@ -2,19 +2,19 @@ # oldstateupgrader.py # # Copyright (C) 2008 Andrew Resch ('andar') -# +# # Deluge is free software. -# +# # You may redistribute it and/or modify it under the terms of the # GNU General Public License, as published by the Free Software # Foundation; either version 3 of the License, or (at your option) # any later version. -# +# # deluge is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with deluge. If not, write to: # The Free Software Foundation, Inc., @@ -73,14 +73,14 @@ class OldStateUpgrader: if os.path.exists(self.state05_location) and not os.path.exists(self.state10_location): # If the 0.5 state file exists and the 1.0 doesn't, then let's upgrade it self.upgrade05() - + def upgrade05(self): try: state = PickleUpgrader(open(self.state05_location, "rb")).load() except Exception, e: log.debug("Unable to open 0.5 state file: %s", e) return - + new_state = deluge.core.torrentmanager.TorrentManagerState() for ti, uid in state.torrents.items(): torrent_path = os.path.join(self.config["config_location"], "torrentfiles", ti.filename) @@ -92,7 +92,7 @@ class OldStateUpgrader: _file.close() except (IOError, RuntimeError), e: log.warning("Unable to open %s: %s", filepath, e) - + # Copy the torrent file to the new location import shutil shutil.copyfile(torrent_path, os.path.join(self.config["state_location"], str(torrent_info.info_hash()) + ".torrent")) @@ -116,7 +116,7 @@ class OldStateUpgrader: ) # Append the object to the state list new_state.torrents.append(new_torrent) - + # Now we need to write out the new state file try: log.debug("Saving torrent state file.") @@ -127,7 +127,7 @@ class OldStateUpgrader: except IOError, e: log.warning("Unable to save state file: %s", e) return - + # Rename the persistent.state file try: os.rename(self.state05_location, self.state05_location + ".old") diff --git a/deluge/core/pluginmanager.py b/deluge/core/pluginmanager.py index e24459be9..9401eb9fa 100644 --- a/deluge/core/pluginmanager.py +++ b/deluge/core/pluginmanager.py @@ -2,19 +2,19 @@ # pluginmanager.py # # Copyright (C) 2007 Andrew Resch ('andar') -# +# # Deluge is free software. -# +# # You may redistribute it and/or modify it under the terms of the # GNU General Public License, as published by the Free Software # Foundation; either version 3 of the License, or (at your option) # any later version. -# +# # deluge is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with deluge. If not, write to: # The Free Software Foundation, Inc., @@ -39,11 +39,11 @@ import deluge.pluginmanagerbase import deluge.component as component from deluge.log import LOG as log -class PluginManager(deluge.pluginmanagerbase.PluginManagerBase, +class PluginManager(deluge.pluginmanagerbase.PluginManagerBase, component.Component): """PluginManager handles the loading of plugins and provides plugins with functions to access parts of the core.""" - + def __init__(self, core): component.Component.__init__(self, "PluginManager") self.core = core @@ -53,7 +53,7 @@ class PluginManager(deluge.pluginmanagerbase.PluginManagerBase, "post_torrent_remove": [], "post_session_load": [] } - + self.status_fields = {} # Call the PluginManagerBase constructor @@ -63,19 +63,19 @@ class PluginManager(deluge.pluginmanagerbase.PluginManagerBase, def start(self): # Enable plugins that are enabled in the config self.enable_plugins() - + # Set update timer to call update() in plugins every second self.update_timer = gobject.timeout_add(1000, self.update_plugins) def stop(self): # Disable all enabled plugins - self.disable_plugins() + self.disable_plugins() # Stop the update timer gobject.source_remove(self.update_timer) - + def shutdown(self): self.stop() - + def update_plugins(self): for plugin in self.plugins.keys(): try: @@ -87,13 +87,13 @@ class PluginManager(deluge.pluginmanagerbase.PluginManagerBase, def get_core(self): """Returns a reference to the core""" return self.core - + def register_status_field(self, field, function): """Register a new status field. This can be used in the same way the client requests other status information from core.""" log.debug("Registering status field %s with PluginManager", field) self.status_fields[field] = function - + def deregister_status_field(self, field): """Deregisters a status field""" log.debug("Deregistering status field %s with PluginManager", field) @@ -101,7 +101,7 @@ class PluginManager(deluge.pluginmanagerbase.PluginManagerBase, del self.status_fields[field] except: log.warning("Unable to deregister status field %s", field) - + def get_status(self, torrent_id, fields): """Return the value of status fields for the selected torrent_id.""" status = {} @@ -112,27 +112,27 @@ class PluginManager(deluge.pluginmanagerbase.PluginManagerBase, log.warning("Status field %s is not registered with the\ PluginManager.", field) return status - + def register_hook(self, hook, function): """Register a hook function with the plugin manager""" try: self.hooks[hook].append(function) except KeyError: log.warning("Plugin attempting to register invalid hook.") - + def deregister_hook(self, hook, function): """Deregisters a hook function""" try: self.hooks[hook].remove(function) except: log.warning("Unable to deregister hook %s", hook) - + def run_post_torrent_add(self, torrent_id): """This hook is run after a torrent has been added to the session.""" log.debug("run_post_torrent_add") for function in self.hooks["post_torrent_add"]: function(torrent_id) - + def run_post_torrent_remove(self, torrent_id): """This hook is run after a torrent has been removed from the session. """ @@ -147,16 +147,16 @@ class PluginManager(deluge.pluginmanagerbase.PluginManagerBase, log.debug("run_post_session_load") for function in self.hooks["post_session_load"]: function() - + def get_torrent_list(self): """Returns a list of torrent_id's in the current session.""" return component.get("TorrentManager").get_torrent_list() - + def block_ip_range(self, range): """Blocks the ip range in the core""" return self.core.export_block_ip_range(range) - + def reset_ip_filter(self): """Resets the ip filter""" return self.core.export_reset_ip_filter() - + diff --git a/deluge/core/signalmanager.py b/deluge/core/signalmanager.py index d95fc9aba..c7655723d 100644 --- a/deluge/core/signalmanager.py +++ b/deluge/core/signalmanager.py @@ -2,19 +2,19 @@ # signalmanager.py # # Copyright (C) 2007, 2008 Andrew Resch ('andar') -# +# # Deluge is free software. -# +# # You may redistribute it and/or modify it under the terms of the # GNU General Public License, as published by the Free Software # Foundation; either version 3 of the License, or (at your option) # any later version. -# +# # deluge is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with deluge. If not, write to: # The Free Software Foundation, Inc., diff --git a/deluge/error.py b/deluge/error.py index 0002ce3f8..d549219aa 100644 --- a/deluge/error.py +++ b/deluge/error.py @@ -2,19 +2,19 @@ # error.py # # Copyright (C) 2008 Andrew Resch ('andar') -# +# # Deluge is free software. -# +# # You may redistribute it and/or modify it under the terms of the # GNU General Public License, as published by the Free Software # Foundation; either version 3 of the License, or (at your option) # any later version. -# +# # deluge is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with deluge. If not, write to: # The Free Software Foundation, Inc., @@ -36,7 +36,7 @@ class DelugeError(Exception): self.value = value def __str__(self): return repr(self.value) - + class NoCoreError(DelugeError): pass - + diff --git a/deluge/log.py b/deluge/log.py index 062683c1c..81b7a44e1 100644 --- a/deluge/log.py +++ b/deluge/log.py @@ -2,19 +2,19 @@ # log.py # # Copyright (C) 2007 Andrew Resch ('andar') -# +# # Deluge is free software. -# +# # You may redistribute it and/or modify it under the terms of the # GNU General Public License, as published by the Free Software # Foundation; either version 3 of the License, or (at your option) # any later version. -# +# # deluge is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with deluge. If not, write to: # The Free Software Foundation, Inc., diff --git a/deluge/main.py b/deluge/main.py index ed64b4146..b31aced5f 100644 --- a/deluge/main.py +++ b/deluge/main.py @@ -2,19 +2,19 @@ # main.py # # Copyright (C) 2007 Andrew Resch ('andar') -# +# # Deluge is free software. -# +# # You may redistribute it and/or modify it under the terms of the # GNU General Public License, as published by the Free Software # Foundation; either version 3 of the License, or (at your option) # any later version. -# +# # deluge is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with deluge. If not, write to: # The Free Software Foundation, Inc., @@ -31,7 +31,7 @@ # this exception statement from your version. If you delete this exception # statement from all source files in the program, then also delete it here. -# The main starting point for the program. This function is called when the +# The main starting point for the program. This function is called when the # user runs the command 'deluge'. """Main starting point for Deluge. Contains the main() entry point.""" @@ -46,7 +46,7 @@ import deluge.common def start_ui(): """Entry point for ui script""" # Setup the argument parser - parser = OptionParser(usage="%prog [options] [actions]", + parser = OptionParser(usage="%prog [options] [actions]", version=deluge.common.get_version()) parser.add_option("-u", "--ui", dest="ui", @@ -55,7 +55,7 @@ def start_ui(): help="Set the config location", action="store", type="str") parser.add_option("-l", "--logfile", dest="logfile", help="Output to designated logfile instead of stdout", action="store", type="str") - + # Get the options and args from the OptionParser (options, args) = parser.parse_args() @@ -69,11 +69,11 @@ def start_ui(): else: if not os.path.exists(deluge.common.get_default_config_dir()): os.makedirs(deluge.common.get_default_config_dir()) - + # Always log to a file in Windows if deluge.common.windows_check() and not options.logfile: options.logfile = "deluge.log" - + if options.logfile: if options.config: logfile = os.path.join(options.config, options.logfile) @@ -83,13 +83,13 @@ def start_ui(): sys.stdout = open(logfile, "wb") sys.stderr = sys.stdout sys.stdin = None - + from deluge.log import LOG as log version = deluge.common.get_version() if deluge.common.get_revision() != "": version = version + "r" + deluge.common.get_revision() - + log.info("Deluge ui %s", version) log.debug("options: %s", options) log.debug("args: %s", args) @@ -97,15 +97,15 @@ def start_ui(): from deluge.ui.ui import UI log.info("Starting ui..") UI(options, args) - + def start_daemon(): """Entry point for daemon script""" import deluge.common - + # Setup the argument parser - parser = OptionParser(usage="%prog [options] [actions]", + parser = OptionParser(usage="%prog [options] [actions]", version=deluge.common.get_version()) - parser.add_option("-p", "--port", dest="port", + parser.add_option("-p", "--port", dest="port", help="Port daemon will listen on", action="store", type="int") parser.add_option("-d", "--do-not-daemonize", dest="donot", help="Do not daemonize", action="store_true", default=False) @@ -113,7 +113,7 @@ def start_daemon(): help="Set the config location", action="store", type="str") parser.add_option("-l", "--logfile", dest="logfile", help="Set the logfile location", action="store", type="str") - + # Get the options and args from the OptionParser (options, args) = parser.parse_args() @@ -127,7 +127,7 @@ def start_daemon(): else: if not os.path.exists(deluge.common.get_default_config_dir()): os.makedirs(deluge.common.get_default_config_dir()) - + # If the donot daemonize is set, then we just skip the forking if not options.donot and not deluge.common.windows_check(): if os.fork() == 0: @@ -141,7 +141,7 @@ def start_daemon(): else: config_dir = deluge.common.get_default_config_dir() logfile = os.path.join(config_dir, "deluged.log") - + sys.stdout = open(logfile, "wb") sys.stderr = sys.stdout sys.stdin = None @@ -160,11 +160,11 @@ def start_daemon(): else: config_dir = deluge.common.get_default_config_dir() logfile = os.path.join(config_dir, "deluged.log") - + sys.stdout = open(logfile, "wb") sys.stderr = sys.stdout sys.stdin = None - + from deluge.core.daemon import Daemon Daemon(options, args) diff --git a/deluge/plugins/blocklist/blocklist/core.py b/deluge/plugins/blocklist/blocklist/core.py index 8e20af3e9..e9f5a8ca2 100644 --- a/deluge/plugins/blocklist/blocklist/core.py +++ b/deluge/plugins/blocklist/blocklist/core.py @@ -2,19 +2,19 @@ # core.py # # Copyright (C) 2008 Andrew Resch ('andar') -# +# # Deluge is free software. -# +# # You may redistribute it and/or modify it under the terms of the # GNU General Public License, as published by the Free Software # Foundation; either version 3 of the License, or (at your option) # any later version. -# +# # deluge is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with deluge. If not, write to: # The Free Software Foundation, Inc., @@ -66,17 +66,17 @@ FORMATS = { 'p2bgz': ["PeerGuardian P2B (GZip)", PGReader] } -class Core(CorePluginBase): +class Core(CorePluginBase): def enable(self): log.debug('Blocklist: Plugin enabled..') - + self.is_downloading = False self.is_importing = False self.num_blocked = 0 self.file_progress = 0.0 - + self.core = component.get("Core") - + self.config = deluge.configmanager.ConfigManager("blocklist.conf", DEFAULT_PREFS) if self.config["load_on_start"]: self.export_import(self.need_new_blocklist()) @@ -86,13 +86,13 @@ class Core(CorePluginBase): self.update_timer = gobject.timeout_add( self.config["check_after_days"] * 24 * 60 * 60 * 1000, self.download_blocklist, True) - + def disable(self): log.debug("Reset IP Filter..") component.get("Core").export_reset_ip_filter() self.config.save() log.debug('Blocklist: Plugin disabled') - + def update(self): pass @@ -100,7 +100,7 @@ class Core(CorePluginBase): def export_download(self, _import=False): """Download the blocklist specified in the config as url""" self.download_blocklist(_import) - + def export_import(self, download=False, force=False): """Import the blocklist from the blocklist.cache, if load is True, then it will download the blocklist file if needed.""" @@ -109,12 +109,12 @@ class Core(CorePluginBase): def export_get_config(self): """Returns the config dictionary""" return self.config.get_config() - + def export_set_config(self, config): """Sets the config based on values in 'config'""" for key in config.keys(): self.config[key] = config[key] - + def export_get_status(self): """Returns the status of the plugin.""" status = {} @@ -124,40 +124,40 @@ class Core(CorePluginBase): status["state"] = "Importing" else: status["state"] = "Idle" - + status["num_blocked"] = self.num_blocked status["file_progress"] = self.file_progress status["file_type"] = self.config["file_type"] status["file_url"] = self.config["file_url"] status["file_size"] = self.config["file_size"] status["file_date"] = self.config["file_date"] - + return status - + #### - - + + def on_download_blocklist(self, load): self.is_downloading = False if load: self.export_import() - + def import_blocklist(self, download=False, force=False): """Imports the downloaded blocklist into the session""" if self.is_downloading: return - + if download: if force or self.need_new_blocklist(): self.download_blocklist(True) return - - self.is_importing = True + + self.is_importing = True log.debug("Reset IP Filter..") component.get("Core").export_reset_ip_filter() - + self.num_blocked = 0 - + # Open the file for reading try: read_list = FORMATS[self.config["listtype"]][1]( @@ -177,21 +177,21 @@ class Core(CorePluginBase): log.debug("Exception during import: %s", e) else: log.debug("Blocklist import complete!") - + self.is_importing = False - + def download_blocklist(self, load=False): """Runs download_blocklist_thread() in a thread and calls on_download_blocklist when finished. If load is True, then we will import the blocklist upon download completion.""" if self.is_importing: return - + self.is_downloading = True threading.Thread( - target=self.download_blocklist_thread, + target=self.download_blocklist_thread, args=(self.on_download_blocklist, load)).start() - + def download_blocklist_thread(self, callback, load): """Downloads the blocklist specified by 'url' in the config""" def _call_callback(callback, load): @@ -203,10 +203,10 @@ class Core(CorePluginBase): if fp > 1.0: fp = 1.0 self.file_progress = fp - + import socket socket.setdefaulttimeout(self.config["timeout"]) - + for i in xrange(self.config["try_times"]): log.debug("Attempting to download blocklist %s", self.config["url"]) try: @@ -225,10 +225,10 @@ class Core(CorePluginBase): list_stats = os.stat(deluge.configmanager.get_config_dir("blocklist.cache")) self.config["file_date"] = datetime.datetime.fromtimestamp(list_stats.st_mtime).ctime() self.config["file_size"] = list_size = list_stats.st_size - + gobject.idle_add(_call_callback, callback, load) return - + def need_new_blocklist(self): """Returns True if a new blocklist file should be downloaded""" try: @@ -247,5 +247,5 @@ class Core(CorePluginBase): if current_time >= (list_time + datetime.timedelta(days=self.config["check_after_days"])): return True - - return False + + return False diff --git a/deluge/plugins/blocklist/blocklist/gtkui.py b/deluge/plugins/blocklist/blocklist/gtkui.py index 427eb8744..d94624359 100644 --- a/deluge/plugins/blocklist/blocklist/gtkui.py +++ b/deluge/plugins/blocklist/blocklist/gtkui.py @@ -2,19 +2,19 @@ # gtkui.py # # Copyright (C) 2008 Andrew Resch ('andar') -# +# # Deluge is free software. -# +# # You may redistribute it and/or modify it under the terms of the # GNU General Public License, as published by the Free Software # Foundation; either version 3 of the License, or (at your option) # any later version. -# +# # deluge is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with deluge. If not, write to: # The Free Software Foundation, Inc., @@ -44,59 +44,59 @@ from core import FORMATS class GtkUI(ui.UI): def enable(self): log.debug("Blocklist GtkUI enable..") - + self.load_preferences_page() - + self.status_item = component.get("StatusBar").add_item( image=self.get_resource("blocklist16.png"), text="", callback=self._on_status_item_clicked, tooltip="Blocked IP Ranges") - + # Register some hooks self.plugin.register_hook("on_apply_prefs", self._on_apply_prefs) self.plugin.register_hook("on_show_prefs", self._on_show_prefs) - + def disable(self): log.debug("Blocklist GtkUI disable..") - + # Remove the preferences page self.plugin.remove_preferences_page("Blocklist") - + # Remove status item component.get("StatusBar").remove_item(self.status_item) del self.status_item - + # Deregister the hooks self.plugin.deregister_hook("on_apply_prefs", self._on_apply_prefs) self.plugin.deregister_hook("on_show_prefs", self._on_show_prefs) - + del self.glade - + def update(self): def _on_get_status(status): if status["state"] == "Downloading": self.table_info.hide() self.glade.get_widget("button_check_download").set_sensitive(False) self.glade.get_widget("button_force_download").set_sensitive(False) - + self.status_item.set_text( "Downloading %.2f%%" % (status["file_progress"] * 100)) self.progress_bar.set_text("Downloading %.2f%%" % (status["file_progress"] * 100)) self.progress_bar.set_fraction(status["file_progress"]) self.progress_bar.show() - + elif status["state"] == "Importing": self.table_info.hide() self.glade.get_widget("button_check_download").set_sensitive(False) self.glade.get_widget("button_force_download").set_sensitive(False) - + self.status_item.set_text( "Importing " + str(status["num_blocked"])) self.progress_bar.set_text("Importing %s" % (status["num_blocked"])) self.progress_bar.pulse() self.progress_bar.show() - + elif status["state"] == "Idle": self.progress_bar.hide() self.glade.get_widget("button_check_download").set_sensitive(True) @@ -113,30 +113,30 @@ class GtkUI(ui.UI): FORMATS[status["file_type"]][0]) except KeyError: self.glade.get_widget("label_type").set_text("") - + self.glade.get_widget("label_url").set_text( status["file_url"]) client.blocklist_get_status(_on_get_status) - + def _on_show_prefs(self): def _on_get_config(config): # Update the combo box. It's ugly, get over it. self.glade.get_widget("combobox_types").set_active_iter( self.glade.get_widget("combobox_types").get_model().\ get_iter(FORMATS[config["listtype"]][1])) - + self.glade.get_widget("entry_url").set_text( config["url"]) - + self.glade.get_widget("spin_check_days").set_value( config["check_after_days"]) - + self.glade.get_widget("chk_import_on_start").set_active( config["load_on_start"]) - + client.blocklist_get_config(_on_get_config) - + def _on_apply_prefs(self): config = {} config["listtype"] = self.glade.get_widget("combobox_types").\ @@ -145,33 +145,33 @@ class GtkUI(ui.UI): config["check_after_days"] = self.glade.get_widget("spin_check_days").get_value_as_int() config["load_on_start"] = self.glade.get_widget("chk_import_on_start").get_active() client.blocklist_set_config(None, config) - + def _on_button_check_download_clicked(self, widget): client.blocklist_import(None, True, False) - + def _on_button_force_download_clicked(self, widget): client.blocklist_import(None, True, True) - + def _on_status_item_clicked(self, widget, event): component.get("Preferences").show("Blocklist") - + def load_preferences_page(self): """Initializes the preferences page and adds it to the preferences dialog""" # Load the preferences page self.glade = gtk.glade.XML(self.get_resource("blocklist_pref.glade")) - + self.progress_bar = self.glade.get_widget("progressbar") self.table_info = self.glade.get_widget("table_info") - + # Hide the progress bar initially self.progress_bar.hide() self.table_info.show() - + self.glade.signal_autoconnect({ "on_button_check_download_clicked": self._on_button_check_download_clicked, "on_button_force_download_clicked": self._on_button_force_download_clicked }) - + # Setup types combobox combo = self.glade.get_widget("combobox_types") combo_list = gtk.ListStore(str, str) @@ -182,21 +182,21 @@ class GtkUI(ui.UI): for k in FORMATS.keys(): i = combo_list.append([FORMATS[k][0], k]) - FORMATS[k][1] = combo_list.get_path(i) + FORMATS[k][1] = combo_list.get_path(i) combo.set_active(0) - + # Set button icons self.glade.get_widget("image_download").set_from_file( self.get_resource("blocklist_download24.png")) - + self.glade.get_widget("image_import").set_from_file( self.get_resource("blocklist_import24.png")) - + # Update the preferences page with config values from the core self._on_show_prefs() - + # Add the page to the preferences dialog self.plugin.add_preferences_page( - "Blocklist", - self.glade.get_widget("blocklist_prefs_box")) + "Blocklist", + self.glade.get_widget("blocklist_prefs_box")) diff --git a/deluge/plugins/blocklist/blocklist/text.py b/deluge/plugins/blocklist/blocklist/text.py index 480adc6bb..d607effa1 100644 --- a/deluge/plugins/blocklist/blocklist/text.py +++ b/deluge/plugins/blocklist/blocklist/text.py @@ -19,7 +19,7 @@ class FormatException(TextException): pass class TextBase: - + def __init__(self, fd, regexp): log.debug("TextBase loading") self.count = 0 @@ -28,7 +28,7 @@ class TextBase: def next(self): self.count += 1 - + txt = self.fd.readline() if txt == "": return False @@ -89,7 +89,7 @@ class GZMuleReader(MuleReader): log.debug("Wrong file type or corrupted blocklist file.") -# Reads zip files from SafePeer style files +# Reads zip files from SafePeer style files class PGZip(TextBase): def __init__(self, filename): @@ -125,7 +125,7 @@ class PGZip(TextBase): # End of zip return False return ret - + except FormatException, e: log.debug("Blocklist: PGZip: Got format exception for zipfile") # Just skip diff --git a/deluge/plugins/blocklist/blocklist/ui.py b/deluge/plugins/blocklist/blocklist/ui.py index 48d5e0b0b..d21ba8cc2 100644 --- a/deluge/plugins/blocklist/blocklist/ui.py +++ b/deluge/plugins/blocklist/blocklist/ui.py @@ -2,19 +2,19 @@ # ui.py # # Copyright (C) 2008 Andrew Resch ('andar') -# +# # Deluge is free software. -# +# # You may redistribute it and/or modify it under the terms of the # GNU General Public License, as published by the Free Software # Foundation; either version 3 of the License, or (at your option) # any later version. -# +# # deluge is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with deluge. If not, write to: # The Free Software Foundation, Inc., @@ -40,9 +40,9 @@ class UI: def enable(self): pass - + def disable(self): pass - + def get_resource(self, filename): return pkg_resources.resource_filename("blocklist", os.path.join("data", filename)) diff --git a/deluge/plugins/corepluginbase.py b/deluge/plugins/corepluginbase.py index d995f3c9b..e21846ad3 100644 --- a/deluge/plugins/corepluginbase.py +++ b/deluge/plugins/corepluginbase.py @@ -2,19 +2,19 @@ # core.py # # Copyright (C) 2007 Andrew Resch ('andar') -# +# # Deluge is free software. -# +# # You may redistribute it and/or modify it under the terms of the # GNU General Public License, as published by the Free Software # Foundation; either version 3 of the License, or (at your option) # any later version. -# +# # deluge is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with deluge. If not, write to: # The Free Software Foundation, Inc., @@ -39,7 +39,7 @@ class CorePluginBase: # Register all export_* functions for func in dir(self): if func.startswith("export_"): - log.debug("Registering export function %s as %s", func, + log.debug("Registering export function %s as %s", func, plugin_name.lower() + "_" + func[7:]) self.plugin.get_core().register_function( getattr(self, "%s" % func), plugin_name.lower()\ diff --git a/deluge/plugins/init.py b/deluge/plugins/init.py index 3c9506fc7..540262540 100644 --- a/deluge/plugins/init.py +++ b/deluge/plugins/init.py @@ -2,19 +2,19 @@ # init.py # # Copyright (C) 2007 Andrew Resch ('andar') -# +# # Deluge is free software. -# +# # You may redistribute it and/or modify it under the terms of the # GNU General Public License, as published by the Free Software # Foundation; either version 3 of the License, or (at your option) # any later version. -# +# # deluge is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with deluge. If not, write to: # The Free Software Foundation, Inc., @@ -36,7 +36,7 @@ from deluge.log import LOG as log class PluginBase: def __init__(self): self.plugin = None - + def enable(self): try: self.plugin.enable() @@ -45,7 +45,7 @@ class PluginBase: else: # If plugin was enabled, call it's update() right away self.update() - + def disable(self): try: self.plugin.disable() @@ -55,4 +55,4 @@ class PluginBase: def update(self): if hasattr(self.plugin, "update"): self.plugin.update() - + diff --git a/deluge/plugins/label/label/ui.py b/deluge/plugins/label/label/ui.py index 4c6ac4aaa..c03621f47 100644 --- a/deluge/plugins/label/label/ui.py +++ b/deluge/plugins/label/label/ui.py @@ -4,19 +4,19 @@ # Copyright (C) 2007 Andrew Resch ('andar') # Copyright (C) 2008 Mark Stahler ('kramed') -# +# # Deluge is free software. -# +# # You may redistribute it and/or modify it under the terms of the # GNU General Public License, as published by the Free Software # Foundation; either version 3 of the License, or (at your option) # any later version. -# +# # deluge is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with deluge. If not, write to: # The Free Software Foundation, Inc., @@ -46,8 +46,7 @@ class UI: def enable(self): pass - + def disable(self): pass - - \ No newline at end of file + diff --git a/deluge/ui/gtkui/aboutdialog.py b/deluge/ui/gtkui/aboutdialog.py index 855254289..2c1bbafae 100644 --- a/deluge/ui/gtkui/aboutdialog.py +++ b/deluge/ui/gtkui/aboutdialog.py @@ -2,19 +2,19 @@ # aboutdialog.py # # Copyright (C) 2007 Marcos Pinto ('markybob') -# +# # Deluge is free software. -# +# # You may redistribute it and/or modify it under the terms of the # GNU General Public License, as published by the Free Software # Foundation; either version 3 of the License, or (at your option) # any later version. -# +# # deluge is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with deluge. If not, write to: # The Free Software Foundation, Inc., @@ -58,7 +58,7 @@ class AboutDialog: self.about.set_copyright(u'Copyright \u00A9 2007-2008 Andrew Resch') self.about.set_comments("A peer-to-peer file sharing program\nutilizing the Bittorrent protocol.") self.about.set_version(version) - self.about.set_authors(["Andrew Resch", "Marcos Pinto", + self.about.set_authors(["Andrew Resch", "Marcos Pinto", "Sadrul Habib Chowdhury", "Martijn Voncken"]) self.about.set_artists(["Andrew Wedderburn", "Andrew Resch"]) self.about.set_translator_credits("Aaron Wang Shi \nabbigss\nABCdatos \nAbcx\nActam \nAdam \nadaminikisi\nadi_oporanu\nAdrian Goll \nafby\nAhmades \nAhmad Farghal \nAhmad Gharbeia أحمد غربية\nakira\nAlan Pepelko \nAlberto\nAlberto Ferrer\nalbertotomaduz\nalcatr4z\nAleksej Korgenkov\nAlessio Treglia\nAlexander Ilyashov\nAlexander Matveev\nAlexander Saltykov\nAlexander Taubenkorb \nAlexander Telenga\nAlexander Yurtsev\nAlexandre Martani \nAlexandre Rosenfeld\nAlexandre Sapata Carbonell\nAlexey Osipov\nAlin Claudiu Radut \nallah\nAlSim \nAlvaro Carrillanca P.\nA.Matveev \nAndras Hipsag \nAndrás Kárász\nAndrea Ratto\nAndreas Johansson \nAndreas Str\nAndré F. Oliveira\nAndreiF \nandrewh\nAngel Guzman Maeso\nAníbal Deboni Neto\nanimarval\nAntonio Cono\nantoniojreyes\nAnton Shestakov\nAnton Yakutovich\nantou\nArkadiusz Kalinowski\nArtin\nartir\nAstur \nAthanasios Lefteris \nAugusta Carla Klug\nAvoledo Marco\naxaard\nAxelRafn \nAxezium\nAyont \nb3rx \nBae Taegil \nBajusz Tamás\nBalaam's Miracle \nBallestein \nBalliba \nBent Ole Fosse\nberto89\nbigx\nBjorn Inge Berg\nblackbird\nBlackeyed\nblackmx\nBlueSky\nBlutheo\nbmhm \nbob00work\nboenki\nBogdan Bădic-Spătariu\nbonpu\nBoone\nboss01\nBranislav Jovanović \nbronze\nbrownie\nBrus46\nbumper\nbutely\nButterflyOfFire\nBXCracer \nc0nfidencal\nCan Kaya\nCarlos Alexandro Becker\ncassianoleal\nCédric.h\nCésar Rubén\nChangHyo Jeong\nchaoswizard\nChen Tao\nchicha\nChien Cheng Wei\nChristian Kopac\nChristian Widell\nChristoffer Brodd-Reijer\nchristooss\nCityAceE\nClopy\nClusty\ncnu\nCommandant\nConstantinos Koniaris\nCoolmax\ncosmix\nCostin Chirvasuta\nCoVaLiDiTy\ncow_2001\nCrispin Kirchner\ncrom\nCruster\nCybolic \nDan Bishop \nDanek\nDani\nDaniel Demarco\nDaniel Ferreira \nDaniel Frank\nDaniel Holm\nDaniel Høyer Iversen\nDaniel Marynicz\nDaniel Nylander\nDaniel Patriche\nDaniel Schildt \nDaniil Sorokin\nDante Díaz\ndarh00\nDaria Michalska\nDarkenCZ \nDaspah\nDavid Eurenius\ndavidhjelm\nDavid Machakhelidze \nDawid Dziurdzia\ndcruz\nDeady\nDereck Wonnacott\nDevgru\nDevid Antonio Filoni\nDevilDogTG\ndi0rz`\nDialecti Valsamou\nDiego Medeiros\nDkzoffy\nDmitrij D. Czarkoff\nDmitriy Geels\nDmitry Olyenyov\nDominik Kozaczko\nDominik Lübben\ndoomster\nDorota Król\nDoyen Philippe\nDread Knight\nDreamSonic\nduan\nDuong Thanh An \nDvoglavaZver\ndwori\ndylansmrjones\nEbuntor\nEdgar Alejandro Jarquin Flores\nEetu\nekerazha\nElias Julkunen\nelparia\nEmberke\nEmiliano Goday Caneda \nEndelWar\neng.essam\nenubuntu\nercangun\nErdal Ronahi\nergin üresin\nEric\nÉric Lassauge\nErlend Finvåg\nErrdil\nethan shalev\nEvgeni Spasov \nezekielnin\nFabian Ordelmans\nFabio Mazanatti\nFábio Nogueira\nFaCuZ\nFelipe Lerena\nFernando Pereira\nfjetland\nFlorian Schäfer\nFolke\nForce\nfosk\nfragarray\nfreddeg\nFrédéric Perrin\nFredrik Kilegran\nFreeAtMind\nFulvio Ciucci\nGabor Kelemen\nGalatsanos Panagiotis\nGaussian\ngdevitis\nGeorg Brzyk\nGeorge Dumitrescu\nGeorgi Arabadjiev\nGeorg Sieber\nGerd Radecke\nGermán Heusdens\nGianni Vialetto\nGigih Aji Ibrahim \nGiorgio Wicklein\nGiovanni Rapagnani\nGiuseppe\ngl\nglen\ngranjerox\nGreen Fish\ngreentea\nGreyhound\nG. U.\nGuillaume BENOIT\nGuillaume Pelletier\nGuillaume Pelletier \nGustavo Henrique Klug\ngutocarvalho\nGuybrush88\nHans-Jørgen Martinus Hansen\nHans Rødtang\nHardDisk\nHargas Gábor\nHeitor Thury Barreiros Barbosa\nhelios91940\nhelix84\nHelton Rodrigues\nHendrik Luup \nHenrique Ferreiro\nHenry Goury-Laffont\nHezy Amiel \nhidro\nhoball \nhokten\nHolmsss \nhristo.num\nHubert Życiński\nIarwain\nibe\nibear\nId2ndR\nIgor Zubarev\nimen\nIon Ikon\nIonuț Jula\nIsabelle STEVANT\nIstván Nyitrai\nIvan Petrovic\nIvan Prignano\nIvaSerge\njackmc\nJacks0nxD\nJack Shen\nJacky Yeung\nJacques Stadler\nJanek Thomaschewski\nJan Kaláb\nJan Niklas Hasse\nJasper Groenewegen\nJavi Rodríguez \njayasimha3000 \njeannich\nJeff Bailes\nJesse Zilstorff\nJoan Duran\nJoão Santos\nJoar Bagge\nJoe Anderson \nJoel Calado\nJohn Garland \nJojan\njollyr0ger\nJonas Bo Grimsgaard\nJonas Granqvist\nJonas Slivka\nJonathan Zeppettini\nJørgen\nJørgen Tellnes\njosé\nJosé Geraldo Gouvêa\nJosé Iván León Islas\nJosé Lou C.\nJose Sun\nJr.\nJukka Kauppinen\nJulián Alarcón\njulietgolf\nJusic\nJustzupi\nKaarel\nKai Thomsen\nKalman Tarnay\nKamil Páral\nKane_F\nkaotiks@gmail.com\nKateikyoushii\nkaxhinaz\nKazuhiro NISHIYAMA\nKerberos\nKeresztes Ákos\nkevintyk\nkiersie\nKimbo^\nKim Lübbe\nkitzOgen\nKjetil Rydland\nkluon\nkmikz\nKnedlyk\nkoleoptero\nKőrösi Krisztián\nKouta\nKrakatos\nKrešo Kunjas\nkripkenstein\nKristaps\nKristian Øllegaard\nKristoffer Egil Bonarjee\nKrzysztof Janowski\nLarry Wei Liu\nlaughterwym\nLaur Mõtus\nlazka\nleandrud\nlê bình\nLe Coz Florent\nLeo\nliorda\nLKRaider\nLoLo_SaG\nLong Tran\nLorenz\nLow Kian Seong\nLuca Andrea Rossi\nLuca Ferretti\nLucky LIX\nLuis Gomes\nLuis Reis\nŁukasz Wyszyński\nluojie-dune\nmaaark\nMaciej Chojnacki\nMads Peter Rommedahl\nMajor Kong\nmalde\nMantas Kriaučiūnas\nMara Sorella\nmarazmista\nMarcin\nMarcin Falkiewicz\nmarcobra\nMarco da Silva\nMarco de Moulin\nMarco Rodrigues\nMarcos Escalier\nMarcos Pinto\nMarcus Ekstrom\nMarek Dębowski\nMário Buči\nMario César Señoranis\nMario Munda\nMarius Andersen\nMarius Hudea\nMarius Mihai\nMariusz Cielecki\nMark Krapivner\nmarko-markovic\nMarkus Brummer\nMarkus Sutter\nMartin \nMartin Dybdal\nMartin Iglesias\nMartin Lettner\nMartin Pihl\nMasoud Kalali\nmat02\nMatej Urbančič\nMathias-K\nMathieu Arès\nMathijs\nMatrik\nMatteo Renzulli\nMatteo Settenvini\nMatthew Gadd\nMatthias Benkard \nMatthias Mailänder\nMattias Ohlsson\nMauro de Carvalho\nMax Molchanov\nMe\nMercuryCC\nMert Bozkurt\nMert Dirik\nMFX\nmhietar\nmibtha\nMichael Budde\nMichael Kaliszka\nMichalis Makaronides\nMichał Tokarczyk\nMiguel Pires da Rosa\nMihai Capotă\nMiika Metsälä\nMikael Fernblad\nMike Sierra\nmikhalek\nMilan Prvulović\nMilo Casagrande\nMindaugas\nMiroslav Matejaš\nmithras\nMitja Pagon\nM.Kitchen\nMohamed Magdy\nmoonkey\nMrBlonde\nmuczy\nMünir Ekinci\nMustafa Temizel\nmvoncken\nNagyMarton\nneaion\nNeil Lin\nNemo\nNerijus Arlauskas\nNicklas Larsson\nNicolaj Wyke\nNicola Piovesan\nNicolas Sabatier\nNicolas Velin\nNightfall\nNiKoB\nNikolai M. Riabov\nNiko_Thien\nniska\nNithir\nnoisemonkey\nnosense\nnull\nNuno Estêvão\nNuno Santos\nnxxs\nnyo\nobo\nOjan\nOlav Andreas Lindekleiv\noldbeggar\nOlivier FAURAX \norphe\nosantana\nOsman Tosun\nOssiR\notypoks\nounn\nOz123\nÖzgür BASKIN\nPablo Carmona A.\nPablo Ledesma\nPablo Navarro Castillo\nPål-Eivind Johnsen\npano\nPaolo Naldini\nParacelsus\nPatryk13_03\nPatryk Skorupa\nPattogoTehen\nPaul Lange\nPavcio\nPaweł Wysocki\nPedro Brites Moita\nPedro Clemente Pereira Neto\nPekka 'PEXI' Niemistö\nPenegal\nPenzo\nperdido\nPerez71\nPeter Kotrcka\nPeter Skov\nPeter Van den Bosch\nPetter Eklund\nPetter Viklund\nphatsphere\nPhenomen\nPhilipi\nPhilippides Homer\nphoenix\npidi\nPierre Quillery\nPierre Slamich\nPietrao\nPiotr Strębski\nPiotr Wicijowski\nPittmann Tamás\nPlaymolas\nPrescott\nPrescott_SK\nPrzemysław Kulczycki\nPumy\nPY\nqubicllj\nr21vo\nrainofchaos\nRajbir \nras0ir\nRat\nrd1381\nRenato\nRene Hennig\nRene Pärts\nRicardo Duarte\nRichard\nRobert Hrovat\nRoberth Sjonøy\nRobert Lundmark\nRobin Jakobsson\nRobin Kåveland\nRodrigo Donado\nRoel Groeneveld\nrohmaru\nRolf Christensen\nRolf Leggewie\nRoni Kantis\nRonmi\nRostislav Raykov\nroyto\nRuiAmaro\nRui Araújo\nRui Moura\nRune Svendsen\nRusna\nRytis\nSabirov Mikhail\nsalseeg\nSami Koskinen\nSamir van de Sand\nSamuel Arroyo Acuña\nSamuel R. C. Vale\nSanel \nSanti\nSanti Martínez Cantelli\nSargate Kanogan\nSarmad Jari\nSaša Bodiroža\nsat0shi\nSaulius Pranckevičius\nSavvas Radević\nSebastian Krauß\nSebastián Porta\nSedir\nsekolands\nsemsomi\nSergii Golovatiuk\nsetarcos\nSheki\nShironeko\nShlomil\nsilfiriel \nSimone Tolotti\nSimone Vendemia\nsirkubador\nSławomir Więch\nslyon\nsmoke\nSonja\nspectral\nspin_555\nspitf1r3\nSpiziuz\nSpyros Theodoritsis\nSqUe\nSquigly\nsrtck\nStefan Horning\nStefano Maggiolo\nStefano Roberto Soleti\nsteinberger\nStéphane Travostino\nStephan Klein\nSteven De Winter\nStevie\nStian24\nstylius\nSukarn Maini\nSunjae Park\nSusana Pereira\nszymon siglowy\ntakercena\nTAS\nTaygeto\ntexxxxxx\nthamood\nThanos Chatziathanassiou\nTharawut Paripaiboon\nTheodoor\nThéophane Anestis\nThor Marius K. Høgås\nTiago Silva\nTiago Sousa\nTikkel\ntim__b\nTim Bordemann\nTim Fuchs\nTim Kornhammar\nTimo\nTimo Jyrinki\nTimothy Babych\nTitkosRejtozo\nTom\nTomas Gustavsson\nTomas Valentukevičius\nTomasz Dominikowski\nTomislav Plavčić\nTom Mannerhagen\nTommy Mikkelsen\nTom Verdaat\nTony Manco\nTor Erling H. Opsahl\nToudi\ntqm_z\nTrapanator\nTribaal\nTriton \nTuniX12\nTuomo Sipola\nturbojugend_gr\nTurtle.net\ntwilight\ntymmej\nUlrik\nUmarzuki Mochlis\nunikob\nVadim Gusev\nVagi\nValentin Bora\nValmantas Palikša\nVASKITTU\nVassilis Skoullis\nvetal17\nvicedo\nviki\nvillads hamann\nVincent Garibal\nvinchi007\nVinícius de Figueiredo Silva\nVinzenz Vietzke\nvirtoo\nvirtual_spirit\nVitor Caike\nVitor Lamas Gatti\nVladimir Lazic\nVladimir Sharshov\nWanderlust\nWander Nauta\nWard De Ridder\nWebCrusader\nwebdr\nWentao Tang\nwilana\nWilfredo Ernesto Guerrero Campos\nWim Champagne\nWorld Sucks\nXabi Ezpeleta \nXavi de Moner\nXavierToo\nXChesser\nXiaodong Xu\nxyb\nYaron\nYasen Pramatarov\nYesPoX\nYuren Ju\nYves MATHIEU\nzekopeko\nzhuqin\nZissan\nΓιάννης Κατσαμπίρης\nАртём Попов\nМиша\nШаймарданов Максим\n蔡查理") @@ -76,7 +76,7 @@ if not, see .")) self.about.set_icon(deluge.common.get_logo(32)) self.about.set_logo(gtk.gdk.pixbuf_new_from_file( deluge.common.get_pixmap("deluge-about.png"))) - + def run(self): self.about.show_all() self.about.run() diff --git a/deluge/ui/gtkui/coreconfig.py b/deluge/ui/gtkui/coreconfig.py index 5d960cc5f..5dce59ba3 100644 --- a/deluge/ui/gtkui/coreconfig.py +++ b/deluge/ui/gtkui/coreconfig.py @@ -2,19 +2,19 @@ # coreconfig.py # # Copyright (C) 2008 Andrew Resch ('andar') -# +# # Deluge is free software. -# +# # You may redistribute it and/or modify it under the terms of the # GNU General Public License, as published by the Free Software # Foundation; either version 3 of the License, or (at your option) # any later version. -# +# # deluge is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with deluge. If not, write to: # The Free Software Foundation, Inc., @@ -42,22 +42,22 @@ class CoreConfig(component.Component): self.config = {} component.get("Signals").connect_to_signal("config_value_changed", self._on_config_value_changed) - + def start(self): client.get_config(self._on_get_config) - + def stop(self): self.config = {} - + def __getitem__(self, key): return self.config[key] - + def __setitem__(self, key, value): client.set_config({key: value}) - + def _on_get_config(self, config): self.config = config - + def _on_config_value_changed(self, key, value): self.config[key] = value - + diff --git a/deluge/ui/gtkui/dbusinterface.py b/deluge/ui/gtkui/dbusinterface.py index 0518fe435..e46bce696 100644 --- a/deluge/ui/gtkui/dbusinterface.py +++ b/deluge/ui/gtkui/dbusinterface.py @@ -2,19 +2,19 @@ # dbusinterface.py # # Copyright (C) 2007 Andrew Resch ('andar') -# +# # Deluge is free software. -# +# # You may redistribute it and/or modify it under the terms of the # GNU General Public License, as published by the Free Software # Foundation; either version 3 of the License, or (at your option) # any later version. -# +# # deluge is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with deluge. If not, write to: # The Free Software Foundation, Inc., @@ -46,7 +46,7 @@ elif dbus.version >= (0,80,0): import deluge.component as component import deluge.common from deluge.log import LOG as log - + class DbusInterface(dbus.service.Object, component.Component): def __init__(self, args, path="/org/deluge_torrent/Deluge"): component.Component.__init__(self, "DbusInterface") @@ -64,11 +64,11 @@ class DbusInterface(dbus.service.Object, component.Component): if not deluge.common.is_url(arg): new_args.append(os.path.abspath(arg)) args = new_args - + # Send the args to the running session if args != [] and args != None: bus = dbus.SessionBus() - proxy = bus.get_object("org.deluge_torrent.Deluge", + proxy = bus.get_object("org.deluge_torrent.Deluge", "/org/deluge_torrent/Deluge") ui = dbus.Interface(proxy, "org.deluge_torrent.Deluge") ui.process_args(args) @@ -80,7 +80,7 @@ class DbusInterface(dbus.service.Object, component.Component): self.process_args(args) # Register Deluge with Dbus log.info("Registering with DBUS..") - bus_name = dbus.service.BusName("org.deluge_torrent.Deluge", + bus_name = dbus.service.BusName("org.deluge_torrent.Deluge", bus=dbus.SessionBus()) dbus.service.Object.__init__(self, bus_name, path) @@ -89,4 +89,4 @@ class DbusInterface(dbus.service.Object, component.Component): """Process arguments sent to already running Deluge""" from ipcinterface import process_args process_args(args) - + diff --git a/deluge/ui/gtkui/details_tab.py b/deluge/ui/gtkui/details_tab.py index 35337ea56..f54a27a6b 100644 --- a/deluge/ui/gtkui/details_tab.py +++ b/deluge/ui/gtkui/details_tab.py @@ -2,19 +2,19 @@ # details_tab.py # # Copyright (C) 2008 Andrew Resch ('andar') -# +# # Deluge is free software. -# +# # You may redistribute it and/or modify it under the terms of the # GNU General Public License, as published by the Free Software # Foundation; either version 3 of the License, or (at your option) # any later version. -# +# # deluge is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with deluge. If not, write to: # The Free Software Foundation, Inc., @@ -46,11 +46,11 @@ class DetailsTab(Tab): # Get the labels we need to update. # widgetname, modifier function, status keys glade = component.get("MainWindow").main_glade - + self._name = "Details" self._child_widget = glade.get_widget("details_tab") self._tab_label = glade.get_widget("details_tab_label") - + self.label_widgets = [ (glade.get_widget("summary_name"), None, ("name",)), (glade.get_widget("summary_total_size"), deluge.common.fsize, ("total_size",)), @@ -60,11 +60,11 @@ class DetailsTab(Tab): (glade.get_widget("summary_message"), str, ("message",)), (glade.get_widget("summary_hash"), str, ("hash",)) ] - + def update(self): # Get the first selected torrent selected = component.get("TorrentView").get_selected_torrents() - + # Only use the first torrent in the list or return if None selected if len(selected) != 0: selected = selected[0] @@ -72,20 +72,20 @@ class DetailsTab(Tab): # No torrent is selected in the torrentview self.clear() return - + # Get the torrent status status_keys = ["name", "total_size", "num_files", "tracker", "save_path", "message", "hash"] - + client.get_torrent_status( self._on_get_torrent_status, selected, status_keys) - + def _on_get_torrent_status(self, status): # Check to see if we got valid data from the core if status is None: return - - # Update all the label widgets + + # Update all the label widgets for widget in self.label_widgets: if widget[1] != None: args = [] @@ -95,14 +95,14 @@ class DetailsTab(Tab): except Exception, e: log.debug("Unable to get status value: %s", e) continue - + txt = widget[1](*args) else: txt = status[widget[2][0]] if widget[0].get_text() != txt: widget[0].set_text(txt) - + def clear(self): for widget in self.label_widgets: widget[0].set_text("") diff --git a/deluge/ui/gtkui/edittrackersdialog.py b/deluge/ui/gtkui/edittrackersdialog.py index c89648ab6..9e8adc998 100644 --- a/deluge/ui/gtkui/edittrackersdialog.py +++ b/deluge/ui/gtkui/edittrackersdialog.py @@ -2,19 +2,19 @@ # edittrackersdialog.py # # Copyright (C) 2007 Andrew Resch ('andar') -# +# # Deluge is free software. -# +# # You may redistribute it and/or modify it under the terms of the # GNU General Public License, as published by the Free Software # Foundation; either version 3 of the License, or (at your option) # any later version. -# +# # deluge is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with deluge. If not, write to: # The Free Software Foundation, Inc., @@ -43,18 +43,18 @@ class EditTrackersDialog: def __init__(self, torrent_id, parent=None): self.torrent_id = torrent_id self.glade = gtk.glade.XML( - pkg_resources.resource_filename("deluge.ui.gtkui", + pkg_resources.resource_filename("deluge.ui.gtkui", "glade/edit_trackers.glade")) - + self.dialog = self.glade.get_widget("edit_trackers_dialog") self.treeview = self.glade.get_widget("tracker_treeview") self.add_tracker_dialog = self.glade.get_widget("add_tracker_dialog") self.add_tracker_dialog.set_transient_for(self.dialog) self.edit_tracker_entry = self.glade.get_widget("edit_tracker_entry") self.edit_tracker_entry.set_transient_for(self.dialog) - + self.dialog.set_icon(deluge.common.get_logo(32)) - + if parent != None: self.dialog.set_transient_for(parent) @@ -72,24 +72,24 @@ class EditTrackersDialog: "on_button_add_ok_clicked": self.on_button_add_ok_clicked, "on_button_add_cancel_clicked": self.on_button_add_cancel_clicked }) - + # Create a liststore for tier, url self.liststore = gtk.ListStore(int, str) - + # Create the columns self.treeview.append_column( gtk.TreeViewColumn(_("Tier"), gtk.CellRendererText(), text=0)) self.treeview.append_column( gtk.TreeViewColumn(_("Tracker"), gtk.CellRendererText(), text=1)) - self.treeview.set_model(self.liststore) + self.treeview.set_model(self.liststore) self.liststore.set_sort_column_id(0, gtk.SORT_ASCENDING) - + def run(self): # Make sure we have a torrent_id.. if not just return if self.torrent_id == None: return - + # Get the trackers for this torrent client.get_torrent_status( @@ -100,17 +100,17 @@ class EditTrackersDialog: """Display trackers dialog""" for tracker in status["trackers"]: self.add_tracker(tracker["tier"], tracker["url"]) - + self.dialog.show() def add_tracker(self, tier, url): """Adds a tracker to the list""" self.liststore.append([tier, url]) - + def get_selected(self): """Returns the selected tracker""" return self.treeview.get_selection().get_selected()[1] - + def on_button_up_clicked(self, widget): log.debug("on_button_up_clicked") selected = self.get_selected() @@ -120,13 +120,13 @@ class EditTrackersDialog: new_tier = tier + 1 # Now change the tier for this tracker self.liststore.set_value(selected, 0, new_tier) - + def on_button_add_clicked(self, widget): log.debug("on_button_add_clicked") # Show the add tracker dialog self.add_tracker_dialog.show() self.glade.get_widget("entry_tracker").grab_focus() - + def on_button_remove_clicked(self, widget): log.debug("on_button_remove_clicked") selected = self.get_selected() @@ -165,7 +165,7 @@ class EditTrackersDialog: new_tier = tier - 1 # Now change the tier for this tracker self.liststore.set_value(selected, 0, new_tier) - + def on_button_ok_clicked(self, widget): log.debug("on_button_ok_clicked") self.trackers = [] @@ -178,11 +178,11 @@ class EditTrackersDialog: # Set the torrens trackers client.set_torrent_trackers(self.torrent_id, self.trackers) self.dialog.destroy() - + def on_button_cancel_clicked(self, widget): log.debug("on_button_cancel_clicked") self.dialog.destroy() - + def on_button_add_ok_clicked(self, widget): log.debug("on_button_add_ok_clicked") from re import search as re_search @@ -190,7 +190,7 @@ class EditTrackersDialog: if not re_search("[udp|http]s?://", tracker): # Bad url.. lets prepend http:// tracker = "http://" + tracker - + # Figure out what tier number to use.. it's going to be the highest+1 # Also check for duplicates self.highest_tier = 0 @@ -209,7 +209,7 @@ class EditTrackersDialog: self.liststore.foreach(tier_count, tracker) else: self.highest_tier = -1 - + # If not a duplicate, then add it to the list if not self.duplicate: # Add the tracker to the list @@ -218,9 +218,9 @@ class EditTrackersDialog: # Clear the entry widget and hide the dialog self.glade.get_widget("entry_tracker").set_text("") self.add_tracker_dialog.hide() - + def on_button_add_cancel_clicked(self, widget): log.debug("on_button_add_cancel_clicked") # Clear the entry widget and hide the dialog self.glade.get_widget("entry_tracker").set_text("") - self.add_tracker_dialog.hide() + self.add_tracker_dialog.hide() diff --git a/deluge/ui/gtkui/files_tab.py b/deluge/ui/gtkui/files_tab.py index 95437c2d2..c2962b934 100644 --- a/deluge/ui/gtkui/files_tab.py +++ b/deluge/ui/gtkui/files_tab.py @@ -2,19 +2,19 @@ # files_tab.py # # Copyright (C) 2008 Andrew Resch ('andar') -# +# # Deluge is free software. -# +# # You may redistribute it and/or modify it under the terms of the # GNU General Public License, as published by the Free Software # Foundation; either version 3 of the License, or (at your option) # any later version. -# +# # deluge is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with deluge. If not, write to: # The Free Software Foundation, Inc., @@ -104,8 +104,8 @@ class FilesTab(Tab): self.listview = glade.get_widget("files_listview") # filename, size, progress string, progress value, priority, file index, icon id self.treestore = gtk.TreeStore(str, gobject.TYPE_UINT64, str, int, int, int, str) - - # Filename column + + # Filename column column = gtk.TreeViewColumn(_("Filename")) render = gtk.CellRendererPixbuf() column.pack_start(render, False) @@ -121,7 +121,7 @@ class FilesTab(Tab): column.set_reorderable(True) self.listview.append_column(column) - # Size column + # Size column column = gtk.TreeViewColumn(_("Size")) render = gtk.CellRendererText() column.pack_start(render, False) @@ -134,7 +134,7 @@ class FilesTab(Tab): column.set_reorderable(True) self.listview.append_column(column) - # Progress column + # Progress column column = gtk.TreeViewColumn(_("Progress")) render = gtk.CellRendererProgress() column.pack_start(render) @@ -146,8 +146,8 @@ class FilesTab(Tab): column.set_min_width(10) column.set_reorderable(True) self.listview.append_column(column) - - # Priority column + + # Priority column column = gtk.TreeViewColumn(_("Priority")) render = gtk.CellRendererPixbuf() column.pack_start(render, False) @@ -164,9 +164,9 @@ class FilesTab(Tab): self.listview.append_column(column) self.listview.set_model(self.treestore) - + self.listview.get_selection().set_mode(gtk.SELECTION_MULTIPLE) - + self.file_menu = glade.get_widget("menu_file_tab") self.listview.connect("row-activated", self._on_row_activated) self.listview.connect("button-press-event", self._on_button_press_event) @@ -179,22 +179,22 @@ class FilesTab(Tab): "on_menuitem_highest_activate": self._on_menuitem_highest_activate, "on_menuitem_expand_all_activate": self._on_menuitem_expand_all_activate }) - + # Attempt to load state self.load_state() - + # torrent_id: (filepath, size) self.files_list = {} - + self.torrent_id = None - + def save_state(self): filename = "files_tab.state" state = [] for index, column in enumerate(self.listview.get_columns()): - state.append(ColumnState(column.get_title(), index, column.get_width(), + state.append(ColumnState(column.get_title(), index, column.get_width(), column.get_sort_indicator(), int(column.get_sort_order()))) - + # Get the config location for saving the state file config_location = ConfigManager("gtkui.conf")["config_location"] @@ -205,13 +205,13 @@ class FilesTab(Tab): state_file.close() except IOError, e: log.warning("Unable to save state file: %s", e) - + def load_state(self): filename = "files_tab.state" # Get the config location for loading the state file config_location = ConfigManager("gtkui.conf")["config_location"] state = None - + try: log.debug("Loading FilesTab state file: %s", filename) state_file = open(os.path.join(config_location, filename), "rb") @@ -219,10 +219,10 @@ class FilesTab(Tab): state_file.close() except (EOFError, IOError), e: log.warning("Unable to load state file: %s", e) - + if state == None: return - + for column_state in state: # Find matching columns in the listview for (index, column) in enumerate(self.listview.get_columns()): @@ -239,7 +239,7 @@ class FilesTab(Tab): self.listview.move_column_after(column, None) else: self.listview.move_column_after(column, self.listview.get_columns()[column_state.position - 1]) - + def update(self): # Get the first selected torrent torrent_id = component.get("TorrentView").get_selected_torrents() @@ -256,13 +256,13 @@ class FilesTab(Tab): # We only want to do this if the torrent_id has changed self.treestore.clear() self.torrent_id = torrent_id - + if self.torrent_id not in self.files_list.keys(): # We need to get the files list log.debug("Getting file list from core..") client.get_torrent_status( - self._on_get_torrent_files, - self.torrent_id, + self._on_get_torrent_files, + self.torrent_id, ["files", "file_progress", "file_priorities"]) client.force_call(block=True) else: @@ -285,23 +285,23 @@ class FilesTab(Tab): def get_file_path(self, row, path=""): if not row: return path - + path = self.treestore.get_value(row, 0) + path return self.get_file_path(self.treestore.iter_parent(row), path) - + def _on_open_file(self, status): paths = self.listview.get_selection().get_selected_rows()[1] selected = [] for path in paths: selected.append(self.treestore.get_iter(path)) - + for select in selected: path = self.get_file_path(select).split("/") filepath = os.path.join(status["save_path"], *path) log.debug("Open file '%s'", filepath) deluge.common.open_file(filepath) - ## The following 3 methods create the folder/file view in the treeview + ## The following 3 methods create the folder/file view in the treeview def prepare_file_store(self, files): split_files = { } i = 0 @@ -320,7 +320,7 @@ class FilesTab(Tab): files_storage[file_name_chunk] = { } self.prepare_file(file, file_name[first_slash_index+1:], file_num, files_storage[file_name_chunk]) - + def add_files(self, parent_iter, split_files): ret = 0 for key,value in split_files.iteritems(): @@ -335,12 +335,12 @@ class FilesTab(Tab): value[1]["size"], "", 0, 0, value[0], gtk.STOCK_FILE]) ret += value[1]["size"] return ret - ### + ### def update_files(self): self.prepare_file_store(self.files_list[self.torrent_id]) self.listview.expand_row("0", False) - + def get_selected_files(self): """Returns a list of file indexes that are selected""" def get_iter_children(itr, selected): @@ -350,7 +350,7 @@ class FilesTab(Tab): if self.treestore.iter_has_child(i): get_iter_children(i, selected) i = self.treestore.iter_next(i) - + selected = [] paths = self.listview.get_selection().get_selected_rows()[1] for path in paths: @@ -358,24 +358,24 @@ class FilesTab(Tab): selected.append(self.treestore[i][5]) if self.treestore.iter_has_child(i): get_iter_children(i, selected) - + return selected def _on_get_torrent_files(self, status): self.files_list[self.torrent_id] = status["files"] self.update_files() self._on_get_torrent_status(status) - + def get_files_from_tree(self, rows, files_list, indent): if not rows: return None - + for row in rows: if row[5] > -1: files_list.append((row[5], row)) self.get_files_from_tree(row.iterchildren(), files_list, indent+1) return None - + def _on_get_torrent_status(self, status): # (index, iter) files_list = [] @@ -391,7 +391,7 @@ class FilesTab(Tab): file_priority = status["file_priorities"][index] if row[4] != file_priority: row[4] = file_priority - + def _on_button_press_event(self, widget, event): """This is a callback for showing the right-click context menu.""" log.debug("on_button_press_event") @@ -409,10 +409,10 @@ class FilesTab(Tab): self.listview.get_selection().select_iter(row) else: self.listview.get_selection().select_iter(row) - + self.file_menu.popup(None, None, None, event.button, event.time) return True - + def _on_menuitem_open_file_activate(self, menuitem): self._on_row_activated(None, None, None) @@ -431,27 +431,27 @@ class FilesTab(Tab): file_priorities.sort() priorities = [p[1] for p in file_priorities] log.debug("priorities: %s", priorities) - + client.set_torrent_file_priorities(self.torrent_id, priorities) - + def _on_menuitem_donotdownload_activate(self, menuitem): self._set_file_priorities_on_user_change( - self.get_selected_files(), + self.get_selected_files(), deluge.common.FILE_PRIORITY["Do Not Download"]) - + def _on_menuitem_normal_activate(self, menuitem): self._set_file_priorities_on_user_change( - self.get_selected_files(), + self.get_selected_files(), deluge.common.FILE_PRIORITY["Normal Priority"]) def _on_menuitem_high_activate(self, menuitem): self._set_file_priorities_on_user_change( - self.get_selected_files(), + self.get_selected_files(), deluge.common.FILE_PRIORITY["High Priority"]) def _on_menuitem_highest_activate(self, menuitem): self._set_file_priorities_on_user_change( - self.get_selected_files(), + self.get_selected_files(), deluge.common.FILE_PRIORITY["Highest Priority"]) def _on_menuitem_expand_all_activate(self, menuitem): diff --git a/deluge/ui/gtkui/listview.py b/deluge/ui/gtkui/listview.py index a11264409..ed3b5bbc4 100644 --- a/deluge/ui/gtkui/listview.py +++ b/deluge/ui/gtkui/listview.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# -*- coding: utf-8 -*- # # listview.py # diff --git a/deluge/ui/gtkui/menubar.py b/deluge/ui/gtkui/menubar.py index 184fc2ca6..6d4028064 100644 --- a/deluge/ui/gtkui/menubar.py +++ b/deluge/ui/gtkui/menubar.py @@ -2,19 +2,19 @@ # menubar.py # # Copyright (C) 2007, 2008 Andrew Resch ('andar') -# +# # Deluge is free software. -# +# # You may redistribute it and/or modify it under the terms of the # GNU General Public License, as published by the Free Software # Foundation; either version 3 of the License, or (at your option) # any later version. -# +# # deluge is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with deluge. If not, write to: # The Free Software Foundation, Inc., @@ -50,19 +50,19 @@ class MenuBar(component.Component): component.Component.__init__(self, "MenuBar") self.window = component.get("MainWindow") self.config = ConfigManager("gtkui.conf") - + # Get the torrent menu from the glade file self.torrentmenu_glade = gtk.glade.XML( - pkg_resources.resource_filename("deluge.ui.gtkui", + pkg_resources.resource_filename("deluge.ui.gtkui", "glade/torrent_menu.glade")) # Attach remove torrent menu self.torrentmenu_glade.get_widget("menuitem_remove").set_submenu( self.torrentmenu_glade.get_widget("remove_torrent_menu")) - + self.torrentmenu_glade.get_widget("menuitem_queue").set_submenu( self.torrentmenu_glade.get_widget("queue_torrent_menu")) - + # Attach options torrent menu self.torrentmenu_glade.get_widget("menuitem_options").set_submenu( self.torrentmenu_glade.get_widget("options_torrent_menu")) @@ -70,7 +70,7 @@ class MenuBar(component.Component): common.get_pixmap("downloading16.png")) self.torrentmenu_glade.get_widget("upload-limit-image").set_from_file( common.get_pixmap("seeding16.png")) - + for menuitem in ("menuitem_down_speed", "menuitem_up_speed", "menuitem_max_connections", "menuitem_upload_slots"): submenu = gtk.Menu() @@ -84,7 +84,7 @@ class MenuBar(component.Component): submenu.append(item) submenu.show_all() self.torrentmenu_glade.get_widget(menuitem).set_submenu(submenu) - + submenu = gtk.Menu() item = gtk.MenuItem(_("On")) item.connect("activate", self.on_menuitem_set_automanaged_on) @@ -94,10 +94,10 @@ class MenuBar(component.Component): submenu.append(item) submenu.show_all() self.torrentmenu_glade.get_widget("menuitem_auto_managed").set_submenu(submenu) - + self.torrentmenu = self.torrentmenu_glade.get_widget("torrent_menu") self.menu_torrent = self.window.main_glade.get_widget("menu_torrent") - + # Attach the torrent_menu to the Torrent file menu self.menu_torrent.set_submenu(self.torrentmenu) @@ -107,7 +107,7 @@ class MenuBar(component.Component): self.config["show_sidebar"]) self.window.main_glade.get_widget("menuitem_statusbar").set_active( self.config["show_statusbar"]) - + ### Connect Signals ### self.window.main_glade.signal_autoconnect({ ## File Menu @@ -122,12 +122,12 @@ class MenuBar(component.Component): self.on_menuitem_preferences_activate, "on_menuitem_connectionmanager_activate": \ self.on_menuitem_connectionmanager_activate, - + ## View Menu "on_menuitem_toolbar_toggled": self.on_menuitem_toolbar_toggled, "on_menuitem_labels_toggled": self.on_menuitem_labels_toggled, "on_menuitem_statusbar_toggled": self.on_menuitem_statusbar_toggled, - + ## Help Menu "on_menuitem_homepage_activate": self.on_menuitem_homepage_activate, "on_menuitem_faq_activate": self.on_menuitem_faq_activate, @@ -135,7 +135,7 @@ class MenuBar(component.Component): self.on_menuitem_community_activate, "on_menuitem_about_activate": self.on_menuitem_about_activate }) - + self.torrentmenu_glade.signal_autoconnect({ ## Torrent Menu "on_menuitem_pause_activate": self.on_menuitem_pause_activate, @@ -160,24 +160,24 @@ class MenuBar(component.Component): "on_menuitem_queue_up_activate": self.on_menuitem_queue_up_activate, "on_menuitem_queue_down_activate": self.on_menuitem_queue_down_activate, "on_menuitem_queue_bottom_activate": self.on_menuitem_queue_bottom_activate, - + }) - + self.change_sensitivity = [ "menuitem_addtorrent" ] - + if self.config["classic_mode"]: # We need to remove the 'quit and shutdown daemon' menu item self.window.main_glade.get_widget("menuitem_quitdaemon").hide() self.window.main_glade.get_widget("separatormenuitem").hide() self.window.main_glade.get_widget("menuitem_connectionmanager").hide() - + def start(self): for widget in self.change_sensitivity: self.window.main_glade.get_widget(widget).set_sensitive(True) - # Hide the Open Folder menuitem and separator if not connected to a + # Hide the Open Folder menuitem and separator if not connected to a # localhost. non_remote_items = [ "menuitem_open_folder", @@ -190,14 +190,14 @@ class MenuBar(component.Component): else: for widget in non_remote_items: self.torrentmenu_glade.get_widget(widget).set_no_show_all(False) - + # Show the Torrent menu because we're connected to a host self.menu_torrent.show() - + if not self.config["classic_mode"]: self.window.main_glade.get_widget("separatormenuitem").show() self.window.main_glade.get_widget("menuitem_quitdaemon").show() - + def stop(self): for widget in self.change_sensitivity: self.window.main_glade.get_widget(widget).set_sensitive(False) @@ -224,20 +224,20 @@ class MenuBar(component.Component): self.torrentmenu.append(sep) sep.show() return sep - + ### Callbacks ### - + ## File Menu ## def on_menuitem_addtorrent_activate(self, data=None): log.debug("on_menuitem_addtorrent_activate") component.get("AddTorrentDialog").show() - + def on_menuitem_quitdaemon_activate(self, data=None): log.debug("on_menuitem_quitdaemon_activate") # Tell the core to shutdown client.shutdown() self.window.quit() - + def on_menuitem_quit_activate(self, data=None): log.debug("on_menuitem_quit_activate") if self.config["classic_mode"]: @@ -246,7 +246,7 @@ class MenuBar(component.Component): except deluge.error.NoCoreError: pass self.window.quit() - + ## Edit Menu ## def on_menuitem_preferences_activate(self, data=None): log.debug("on_menuitem_preferences_activate") @@ -255,31 +255,31 @@ class MenuBar(component.Component): def on_menuitem_connectionmanager_activate(self, data=None): log.debug("on_menuitem_connectionmanager_activate") component.get("ConnectionManager").show() - + ## Torrent Menu ## def on_menuitem_pause_activate(self, data=None): log.debug("on_menuitem_pause_activate") client.pause_torrent( component.get("TorrentView").get_selected_torrents()) - + def on_menuitem_resume_activate(self, data=None): log.debug("on_menuitem_resume_activate") client.resume_torrent( component.get("TorrentView").get_selected_torrents()) - + def on_menuitem_updatetracker_activate(self, data=None): log.debug("on_menuitem_updatetracker_activate") client.force_reannounce( - component.get("TorrentView").get_selected_torrents()) - + component.get("TorrentView").get_selected_torrents()) + def on_menuitem_edittrackers_activate(self, data=None): log.debug("on_menuitem_edittrackers_activate") from edittrackersdialog import EditTrackersDialog dialog = EditTrackersDialog( - component.get("TorrentView").get_selected_torrent(), + component.get("TorrentView").get_selected_torrent(), component.get("MainWindow").window) dialog.run() - + def on_menuitem_remove_session_activate(self, data=None): log.debug("on_menuitem_remove_session_activate") from removetorrentdialog import RemoveTorrentDialog @@ -299,7 +299,7 @@ class MenuBar(component.Component): RemoveTorrentDialog( component.get("TorrentView").get_selected_torrents(), remove_data=True).run() - + def on_menuitem_remove_both_activate(self, data=None): log.debug("on_menuitem_remove_both_activate") from removetorrentdialog import RemoveTorrentDialog @@ -312,7 +312,7 @@ class MenuBar(component.Component): log.debug("on_menuitem_recheck_activate") client.force_recheck( component.get("TorrentView").get_selected_torrents()) - + def on_menuitem_open_folder_activate(self, data=None): log.debug("on_menuitem_open_folder") def _on_torrent_status(status): @@ -342,11 +342,11 @@ class MenuBar(component.Component): else: client.get_torrent_status(self.show_move_storage_dialog, component.get("TorrentView").get_selected_torrent(), ["save_path"]) client.force_call(False) - + def show_move_storage_dialog(self, status): log.debug("show_move_storage_dialog") glade = gtk.glade.XML( - pkg_resources.resource_filename("deluge.ui.gtkui", + pkg_resources.resource_filename("deluge.ui.gtkui", "glade/move_storage_dialog.glade")) dialog = glade.get_widget("move_storage_dialog") dialog.set_transient_for(self.window.window) @@ -360,7 +360,7 @@ class MenuBar(component.Component): dialog.hide() dialog.connect("response", _on_response_event) dialog.show() - + def on_menuitem_queue_top_activate(self, value): log.debug("on_menuitem_queue_top_activate") client.queue_top(None, component.get("TorrentView").get_selected_torrents()) @@ -368,15 +368,15 @@ class MenuBar(component.Component): def on_menuitem_queue_up_activate(self, value): log.debug("on_menuitem_queue_up_activate") client.queue_up(None, component.get("TorrentView").get_selected_torrents()) - + def on_menuitem_queue_down_activate(self, value): log.debug("on_menuitem_queue_down_activate") client.queue_down(None, component.get("TorrentView").get_selected_torrents()) - + def on_menuitem_queue_bottom_activate(self, value): log.debug("on_menuitem_queue_bottom_activate") client.queue_bottom(None, component.get("TorrentView").get_selected_torrents()) - + ## View Menu ## def on_menuitem_toolbar_toggled(self, value): log.debug("on_menuitem_toolbar_toggled") @@ -385,7 +385,7 @@ class MenuBar(component.Component): def on_menuitem_labels_toggled(self, value): log.debug("on_menuitem_labels_toggled") component.get("SideBar").visible(value.get_active()) - + def on_menuitem_statusbar_toggled(self, value): log.debug("on_menuitem_statusbar_toggled") component.get("StatusBar").visible(value.get_active()) @@ -419,7 +419,7 @@ class MenuBar(component.Component): if widget.name in funcs.keys(): for torrent in component.get("TorrentView").get_selected_torrents(): funcs[widget.name](torrent, -1) - + def on_menuitem_set_other(self, widget): log.debug("widget.name: %s", widget.name) funcs = { @@ -429,7 +429,7 @@ class MenuBar(component.Component): "menuitem_upload_slots": client.set_torrent_max_upload_slots } dialog_glade = gtk.glade.XML( - pkg_resources.resource_filename("deluge.ui.gtkui", + pkg_resources.resource_filename("deluge.ui.gtkui", "glade/dgtkpopups.glade")) speed_dialog = dialog_glade.get_widget("speed_dialog") spin_title = dialog_glade.get_widget("spin_title") @@ -441,7 +441,7 @@ class MenuBar(component.Component): spin_title.set_text(_("Set Max Connections:")) elif widget.name == "menuitem_upload_slots": spin_title.set_text(_("Set Max Upload Slots:")) - + spin_speed = dialog_glade.get_widget("spin_speed") spin_speed.set_value(-1) spin_speed.select_region(0, -1) @@ -457,7 +457,7 @@ class MenuBar(component.Component): speed_dialog.destroy() if widget.name in funcs.keys(): for torrent in component.get("TorrentView").get_selected_torrents(): - funcs[widget.name](torrent, value) + funcs[widget.name](torrent, value) def on_menuitem_set_automanaged_on(self, widget): for torrent in component.get("TorrentView").get_selected_torrents(): diff --git a/deluge/ui/gtkui/new_release_dialog.py b/deluge/ui/gtkui/new_release_dialog.py index 5387c08a2..f5c92f12c 100644 --- a/deluge/ui/gtkui/new_release_dialog.py +++ b/deluge/ui/gtkui/new_release_dialog.py @@ -2,19 +2,19 @@ # new_release_dialog.py # # Copyright (C) 2008 Andrew Resch ('andar') -# +# # Deluge is free software. -# +# # You may redistribute it and/or modify it under the terms of the # GNU General Public License, as published by the Free Software # Foundation; either version 3 of the License, or (at your option) # any later version. -# +# # deluge is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with deluge. If not, write to: # The Free Software Foundation, Inc., @@ -38,7 +38,7 @@ from deluge.configmanager import ConfigManager class NewReleaseDialog: def __init__(self): pass - + def show(self, available_version): self.config = ConfigManager("gtkui.conf") glade = component.get("MainWindow").main_glade @@ -52,14 +52,14 @@ class NewReleaseDialog: "clicked", self._on_button_goto_downloads) glade.get_widget("button_close_new_release").connect( "clicked", self._on_button_close_new_release) - + self.dialog.show_all() - + def _on_button_goto_downloads(self, widget): deluge.common.open_url_in_browser("http://deluge-torrent.org") self.config["show_new_releases"] = not self.chk_not_show_dialog.get_active() self.dialog.destroy() - + def _on_button_close_new_release(self, widget): self.config["show_new_releases"] = not self.chk_not_show_dialog.get_active() self.dialog.destroy() diff --git a/deluge/ui/gtkui/peers_tab.py b/deluge/ui/gtkui/peers_tab.py index 79dd163ce..fe0f58956 100644 --- a/deluge/ui/gtkui/peers_tab.py +++ b/deluge/ui/gtkui/peers_tab.py @@ -2,19 +2,19 @@ # peers_tab.py # # Copyright (C) 2008 Andrew Resch ('andar') -# +# # Deluge is free software. -# +# # You may redistribute it and/or modify it under the terms of the # GNU General Public License, as published by the Free Software # Foundation; either version 3 of the License, or (at your option) # any later version. -# +# # deluge is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with deluge. If not, write to: # The Free Software Foundation, Inc., @@ -53,7 +53,7 @@ class ColumnState: self.width = width self.sort = sort self.sort_order = sort_order - + class PeersTab(Tab): def __init__(self): Tab.__init__(self) @@ -62,19 +62,19 @@ class PeersTab(Tab): self._name = "Peers" self._child_widget = glade.get_widget("peers_tab") self._tab_label = glade.get_widget("peers_tab_label") - + self.listview = glade.get_widget("peers_listview") # country pixbuf, ip, client, downspeed, upspeed, country code, int_ip, seed/peer icon self.liststore = gtk.ListStore(gtk.gdk.Pixbuf, str, str, int, int, str, gobject.TYPE_UINT, gtk.gdk.Pixbuf) self.cached_flag_pixbufs = {} - + self.seed_pixbuf = gtk.gdk.pixbuf_new_from_file(deluge.common.get_pixmap("seeding16.png")) self.peer_pixbuf = gtk.gdk.pixbuf_new_from_file(deluge.common.get_pixmap("downloading16.png")) # key is ip address, item is row iter self.peers = {} - - # Country column + + # Country column column = gtk.TreeViewColumn() render = gtk.CellRendererPixbuf() column.pack_start(render, False) @@ -86,8 +86,8 @@ class PeersTab(Tab): column.set_min_width(10) column.set_reorderable(True) self.listview.append_column(column) - - # Address column + + # Address column column = gtk.TreeViewColumn(_("Address")) render = gtk.CellRendererPixbuf() column.pack_start(render, False) @@ -103,7 +103,7 @@ class PeersTab(Tab): column.set_reorderable(True) self.listview.append_column(column) - # Client column + # Client column column = gtk.TreeViewColumn(_("Client")) render = gtk.CellRendererText() column.pack_start(render, False) @@ -127,8 +127,8 @@ class PeersTab(Tab): column.set_expand(False) column.set_min_width(10) column.set_reorderable(True) - self.listview.append_column(column) - + self.listview.append_column(column) + # Up Speed column column = gtk.TreeViewColumn(_("Up Speed")) render = gtk.CellRendererText() @@ -140,21 +140,21 @@ class PeersTab(Tab): column.set_expand(False) column.set_min_width(10) column.set_reorderable(True) - self.listview.append_column(column) + self.listview.append_column(column) self.listview.set_model(self.liststore) - + self.load_state() - + self.torrent_id = None def save_state(self): filename = "peers_tab.state" state = [] for index, column in enumerate(self.listview.get_columns()): - state.append(ColumnState(column.get_title(), index, column.get_width(), + state.append(ColumnState(column.get_title(), index, column.get_width(), column.get_sort_indicator(), int(column.get_sort_order()))) - + # Get the config location for saving the state file config_location = ConfigManager("gtkui.conf")["config_location"] @@ -165,13 +165,13 @@ class PeersTab(Tab): state_file.close() except IOError, e: log.warning("Unable to save state file: %s", e) - + def load_state(self): filename = "peers_tab.state" # Get the config location for loading the state file config_location = ConfigManager("gtkui.conf")["config_location"] state = None - + try: log.debug("Loading PeersTab state file: %s", filename) state_file = open(os.path.join(config_location, filename), "rb") @@ -179,14 +179,14 @@ class PeersTab(Tab): state_file.close() except (EOFError, IOError), e: log.warning("Unable to load state file: %s", e) - + if state == None: return - + if len(state) != len(self.listview.get_columns()): log.warning("peers_tab.state is not compatible! rejecting..") return - + for column_state in state: # Find matching columns in the listview for (index, column) in enumerate(self.listview.get_columns()): @@ -203,7 +203,7 @@ class PeersTab(Tab): self.listview.move_column_after(column, None) else: self.listview.move_column_after(column, self.listview.get_columns()[column_state.position - 1]) - + def update(self): # Get the first selected torrent torrent_id = component.get("TorrentView").get_selected_torrents() @@ -215,7 +215,7 @@ class PeersTab(Tab): # No torrent is selected in the torrentview self.liststore.clear() return - + if torrent_id != self.torrent_id: # We only want to do this if the torrent_id has changed self.liststore.clear() @@ -227,20 +227,20 @@ class PeersTab(Tab): def get_flag_pixbuf(self, country): if country == " ": return None - + if not self.cached_flag_pixbufs.has_key(country): # We haven't created a pixbuf for this country yet try: self.cached_flag_pixbufs[country] = gtk.gdk.pixbuf_new_from_file( pkg_resources.resource_filename( - "deluge", + "deluge", os.path.join("data", "pixmaps", "flags", country.lower() + ".png"))) except Exception, e: log.debug("Unable to load flag: %s", e) return None - + return self.cached_flag_pixbufs[country] - + def _on_get_torrent_status(self, status): new_ips = set() for peer in status["peers"]: @@ -267,10 +267,10 @@ class PeersTab(Tab): if icon != values[3]: self.liststore.set_value(row, 7, icon) - + else: # Peer is not in list so we need to add it - + # Create an int IP address for sorting purposes ip_int = sum([int(byte) << shift for byte, shift in izip(peer["ip"].split(":")[0].split("."), (24, 16, 8, 0))]) @@ -279,19 +279,19 @@ class PeersTab(Tab): icon = self.seed_pixbuf else: icon = self.peer_pixbuf - + row = self.liststore.append([ self.get_flag_pixbuf(peer["country"]), peer["ip"], peer["client"], - peer["down_speed"], + peer["down_speed"], peer["up_speed"], peer["country"], ip_int, icon]) - + self.peers[peer["ip"]] = row - + # Now we need to remove any ips that were not in status["peers"] list for ip in set(self.peers.keys()).difference(new_ips): self.liststore.remove(self.peers[ip]) diff --git a/deluge/ui/gtkui/pluginmanager.py b/deluge/ui/gtkui/pluginmanager.py index d273b9b9a..09b10d407 100644 --- a/deluge/ui/gtkui/pluginmanager.py +++ b/deluge/ui/gtkui/pluginmanager.py @@ -2,19 +2,19 @@ # pluginmanager.py # # Copyright (C) 2007, 2008 Andrew Resch ('andar') -# +# # Deluge is free software. -# +# # You may redistribute it and/or modify it under the terms of the # GNU General Public License, as published by the Free Software # Foundation; either version 3 of the License, or (at your option) # any later version. -# +# # deluge is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with deluge. If not, write to: # The Free Software Foundation, Inc., @@ -37,7 +37,7 @@ from deluge.ui.client import aclient as client from deluge.configmanager import ConfigManager from deluge.log import LOG as log -class PluginManager(deluge.pluginmanagerbase.PluginManagerBase, +class PluginManager(deluge.pluginmanagerbase.PluginManagerBase, component.Component): def __init__(self): component.Component.__init__(self, "PluginManager", depend=["Signals"]) @@ -56,14 +56,14 @@ class PluginManager(deluge.pluginmanagerbase.PluginManagerBase, self.hooks[hook].append(function) except KeyError: log.warning("Plugin attempting to register invalid hook.") - + def deregister_hook(self, hook, function): """Deregisters a hook function""" try: self.hooks[hook].remove(function) except: log.warning("Unable to deregister hook %s", hook) - + def start(self): """Start the plugin manager""" # Update the enabled_plugins from the core @@ -72,13 +72,13 @@ class PluginManager(deluge.pluginmanagerbase.PluginManagerBase, def stop(self): # Disable the plugins self.disable_plugins() - + def update(self): # We call the plugins' update() method every second for plugin in self.plugins.values(): if hasattr(plugin, "update"): plugin.update() - + def _on_get_enabled_plugins(self, enabled_plugins): log.debug("Core has these plugins enabled: %s", enabled_plugins) for plugin in enabled_plugins: @@ -92,7 +92,7 @@ class PluginManager(deluge.pluginmanagerbase.PluginManagerBase, log.debug("run_on_show_prefs") for function in self.hooks["on_show_prefs"]: function() - + def run_on_apply_prefs(self): """This hook is run after the user clicks Apply or OK in the preferences dialog. @@ -100,39 +100,39 @@ class PluginManager(deluge.pluginmanagerbase.PluginManagerBase, log.debug("run_on_apply_prefs") for function in self.hooks["on_apply_prefs"]: function() - + ## Plugin functions.. will likely move to own class.. - + def add_torrentview_text_column(self, *args, **kwargs): return component.get("TorrentView").add_text_column(*args, **kwargs) - + def remove_torrentview_column(self, *args): return component.get("TorrentView").remove_column(*args) - + def add_toolbar_separator(self): return component.get("ToolBar").add_separator() - + def add_toolbar_button(self, *args, **kwargs): return component.get("ToolBar").add_toolbutton(*args, **kwargs) - + def remove_toolbar_button(self, *args): return component.get("ToolBar").remove(*args) - + def add_torrentmenu_menu(self, *args): return component.get("MenuBar").torrentmenu.append(*args) - + def add_torrentmenu_separator(self): return component.get("MenuBar").add_torrentmenu_separator() def remove_torrentmenu_item(self, *args): return component.get("MenuBar").torrentmenu.remove(*args) - + def add_preferences_page(self, *args): return component.get("Preferences").add_page(*args) - + def remove_preferences_page(self, *args): return component.get("Preferences").remove_page(*args) - + def update_torrent_view(self, *args): return component.get("TorrentView").update(*args) diff --git a/deluge/ui/gtkui/preferences.py b/deluge/ui/gtkui/preferences.py index 09c34b9a6..19d54b05f 100644 --- a/deluge/ui/gtkui/preferences.py +++ b/deluge/ui/gtkui/preferences.py @@ -2,19 +2,19 @@ # preferences.py # # Copyright (C) 2007, 2008 Andrew Resch ('andar') -# +# # Deluge is free software. -# +# # You may redistribute it and/or modify it under the terms of the # GNU General Public License, as published by the Free Software # Foundation; either version 3 of the License, or (at your option) # any later version. -# +# # deluge is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with deluge. If not, write to: # The Free Software Foundation, Inc., @@ -63,7 +63,7 @@ class Preferences(component.Component): self.treeview.append_column(column) # Add the default categories i = 0 - for category in ["Downloads", "Network", "Bandwidth", "Interface", + for category in ["Downloads", "Network", "Bandwidth", "Interface", "Other", "Daemon", "Queue", "Plugins"]: self.liststore.append([i, category]) i += 1 @@ -79,15 +79,15 @@ class Preferences(component.Component): gtk.TreeViewColumn(_("Enabled"), render, active=1)) self.plugin_listview.append_column( gtk.TreeViewColumn(_("Plugin"), gtk.CellRendererText(), text=0)) - + # Connect to the 'changed' event of TreeViewSelection to get selection # changes. - self.treeview.get_selection().connect("changed", + self.treeview.get_selection().connect("changed", self.on_selection_changed) - + 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, @@ -97,10 +97,10 @@ class Preferences(component.Component): "on_test_port_clicked": self.on_test_port_clicked }) - # These get updated by requests done to the core + # These get updated by requests done to the core self.all_plugins = [] self.enabled_plugins = [] - + def __del__(self): del self.gtkui_config @@ -125,9 +125,9 @@ class Preferences(component.Component): vbox.pack_start(align, False, False, 0) scrolled = gtk.ScrolledWindow() viewport = gtk.Viewport() - viewport.set_shadow_type(gtk.SHADOW_NONE) + viewport.set_shadow_type(gtk.SHADOW_NONE) viewport.add(vbox) - scrolled.add(viewport) + scrolled.add(viewport) scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) scrolled.show_all() # Add this page to the notebook @@ -139,7 +139,7 @@ class Preferences(component.Component): """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: @@ -147,26 +147,26 @@ class Preferences(component.Component): 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 != None: self.notebook.remove_page(self.page_num_to_remove) if self.iter_to_remove != None: self.liststore.remove(self.iter_to_remove) - + def _on_get_config(self, config): self.core_config = config - + def _on_get_available_plugins(self, plugins): self.all_plugins = plugins - + def _on_get_enabled_plugins(self, plugins): self.enabled_plugins = plugins - + def _on_get_listen_port(self, port): self.active_port = port - + def show(self, page=None): """Page should be the string in the left list.. ie, 'Network' or 'Bandwidth'""" if page != None: @@ -174,7 +174,7 @@ class Preferences(component.Component): if page == string: self.treeview.get_selection().select_path(index) break - + # Update the preferences dialog to reflect current config settings self.core_config = {} try: @@ -208,7 +208,7 @@ class Preferences(component.Component): "radio_full_allocation": \ ("not_active", self.core_config["compact_allocation"]), "chk_prioritize_first_last_pieces": \ - ("active", + ("active", self.core_config["prioritize_first_last_pieces"]), "spin_port_min": ("value", self.core_config["listen_ports"][0]), "spin_port_max": ("value", self.core_config["listen_ports"][1]), @@ -264,7 +264,7 @@ class Preferences(component.Component): "spin_share_ratio": ("value", self.core_config["stop_seed_ratio"]), "chk_remove_ratio": ("active", self.core_config["remove_seed_at_ratio"]) } - + # 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() @@ -276,7 +276,7 @@ class Preferences(component.Component): 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") @@ -294,7 +294,7 @@ class Preferences(component.Component): 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() + self.glade.get_widget("folder_autoadd").show() # Update the widgets accordingly for key in core_widgets.keys(): @@ -305,7 +305,7 @@ class Preferences(component.Component): for child in widget.get_children(): child.set_sensitive(True) widget.set_sensitive(True) - + if modifier == "filename": if value: try: @@ -402,11 +402,11 @@ class Preferences(component.Component): self.gtkui_config["lock_tray"]) self.glade.get_widget("chk_classic_mode").set_active( self.gtkui_config["classic_mode"]) - + ## Other tab ## self.glade.get_widget("chk_show_new_releases").set_active( self.gtkui_config["show_new_releases"]) - + ## Plugins tab ## all_plugins = self.all_plugins enabled_plugins = self.enabled_plugins @@ -421,22 +421,22 @@ class Preferences(component.Component): row = self.plugin_liststore.append() self.plugin_liststore.set_value(row, 0, plugin) self.plugin_liststore.set_value(row, 1, enabled) - + component.get("PluginManager").run_on_show_prefs() # Now show the dialog self.pref_dialog.show() - + def set_config(self): """Sets all altered config values in the core""" 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() @@ -460,7 +460,7 @@ class Preferences(component.Component): 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(): @@ -469,7 +469,7 @@ class Preferences(component.Component): 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"] = \ @@ -501,7 +501,7 @@ class Preferences(component.Component): 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( @@ -532,7 +532,7 @@ class Preferences(component.Component): "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() - + ## Interface tab ## new_gtkui_config["enable_system_tray"] = \ self.glade.get_widget("chk_use_tray").get_active() @@ -548,8 +548,8 @@ class Preferences(component.Component): new_gtkui_config["tray_password"] = passhex new_gtkui_config["classic_mode"] = \ self.glade.get_widget("chk_classic_mode").get_active() - - ## Other tab ## + + ## Other tab ## new_gtkui_config["show_new_releases"] = \ self.glade.get_widget("chk_show_new_releases").get_active() new_core_config["send_info"] = \ @@ -562,7 +562,7 @@ class Preferences(component.Component): 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() - + ## Queue tab ## new_core_config["queue_new_to_top"] = \ self.glade.get_widget("chk_queue_new_top").get_active() @@ -586,16 +586,16 @@ class Preferences(component.Component): 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() - + # 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.get_core_uri() != None: # Only do this if we're connected to a daemon @@ -610,17 +610,17 @@ class Preferences(component.Component): client.force_call(True) # Update the configuration self.core_config.update(config_to_set) - + # Re-show the dialog to make sure everything has been updated self.show() - + def hide(self): self.pref_dialog.hide() - + 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: @@ -674,7 +674,7 @@ class Preferences(component.Component): 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() @@ -690,7 +690,7 @@ class Preferences(component.Component): "http://deluge-torrent.org/test-port.php?port=%s" % port) client.get_listen_port(on_get_listen_port) client.force_call() - + def on_plugin_toggled(self, renderer, path): log.debug("on_plugin_toggled") row = self.plugin_liststore.get_iter_from_string(path) @@ -703,7 +703,7 @@ class Preferences(component.Component): else: client.disable_plugin(name) component.get("PluginManager").disable_plugin(name) - + def on_plugin_selection_changed(self, treeselection): log.debug("on_plugin_selection_changed") - + diff --git a/deluge/ui/gtkui/queuedtorrents.py b/deluge/ui/gtkui/queuedtorrents.py index 784793775..351bc39d4 100644 --- a/deluge/ui/gtkui/queuedtorrents.py +++ b/deluge/ui/gtkui/queuedtorrents.py @@ -2,19 +2,19 @@ # queuedtorrents.py # # Copyright (C) 2007 Andrew Resch ('andar') -# +# # Deluge is free software. -# +# # You may redistribute it and/or modify it under the terms of the # GNU General Public License, as published by the Free Software # Foundation; either version 3 of the License, or (at your option) # any later version. -# +# # deluge is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with deluge. If not, write to: # The Free Software Foundation, Inc., @@ -48,16 +48,16 @@ class QueuedTorrents(component.Component): component.Component.__init__(self, "QueuedTorrents", depend=["StatusBar"]) self.queue = [] self.status_item = None - + self.config = ConfigManager("gtkui.conf") self.glade = gtk.glade.XML( - pkg_resources.resource_filename("deluge.ui.gtkui", + pkg_resources.resource_filename("deluge.ui.gtkui", "glade/queuedtorrents.glade")) self.glade.get_widget("chk_autoadd").set_active( - self.config["autoadd_queued"]) + self.config["autoadd_queued"]) self.dialog = self.glade.get_widget("queued_torrents_dialog") self.dialog.set_icon(deluge.common.get_logo(32)) - + self.glade.signal_autoconnect({ "on_button_remove_clicked": self.on_button_remove_clicked, "on_button_clear_clicked": self.on_button_clear_clicked, @@ -65,14 +65,14 @@ class QueuedTorrents(component.Component): "on_button_add_clicked": self.on_button_add_clicked, "on_chk_autoadd_toggled": self.on_chk_autoadd_toggled }) - + self.treeview = self.glade.get_widget("treeview") self.treeview.append_column( gtk.TreeViewColumn(_("Torrent"), gtk.CellRendererText(), text=0)) - + self.liststore = gtk.ListStore(str, str) self.treeview.set_model(self.liststore) - + def run(self): self.dialog.set_transient_for(component.get("MainWindow").window) self.dialog.show() @@ -90,12 +90,12 @@ class QueuedTorrents(component.Component): # We only want the add button sensitive if we're connected to a host self.glade.get_widget("button_add").set_sensitive(True) self.run() - + def stop(self): # We only want the add button sensitive if we're connected to a host self.glade.get_widget("button_add").set_sensitive(False) self.update_status_bar() - + def add_to_queue(self, torrents): """Adds the list of torrents to the queue""" # Add to the queue while removing duplicates @@ -105,10 +105,10 @@ class QueuedTorrents(component.Component): self.liststore.clear() for torrent in self.queue: self.liststore.append([os.path.split(torrent)[1], torrent]) - + # Update the status bar self.update_status_bar() - + def update_status_bar(self): """Attempts to update status bar""" # If there are no queued torrents.. remove statusbar widgets and return @@ -117,7 +117,7 @@ class QueuedTorrents(component.Component): component.get("StatusBar").remove_item(self.status_item) self.status_item = None return False - + try: statusbar = component.get("StatusBar") except Exception, e: @@ -125,18 +125,18 @@ class QueuedTorrents(component.Component): # update it later. gobject.timeout_add(100, self.update_status_bar) return False - + # Set the label text for statusbar if len(self.queue) > 1: label = str(len(self.queue)) + _(" Torrents Queued") else: label = str(len(self.queue)) + _(" Torrent Queued") - + # Add the statusbar items if needed, or just modify the label if they # have already been added. if self.status_item == None: self.status_item = component.get("StatusBar").add_item( - stock=gtk.STOCK_SORT_DESCENDING, + stock=gtk.STOCK_SORT_DESCENDING, text=label, callback=self.on_statusbar_click) else: @@ -144,11 +144,11 @@ class QueuedTorrents(component.Component): # We return False so the timer stops return False - + def on_statusbar_click(self, widget, event): log.debug("on_statusbar_click") self.run() - + def on_button_remove_clicked(self, widget): selected = self.treeview.get_selection().get_selected()[1] if selected != None: @@ -173,13 +173,13 @@ class QueuedTorrents(component.Component): component.get("AddTorrentDialog").show(self.config["focus_add_dialog"]) else: client.add_torrent_file([torrent_path]) - + self.liststore.foreach(add_torrent, None) del self.queue[:] self.dialog.hide() self.update_status_bar() - + def on_chk_autoadd_toggled(self, widget): self.config["autoadd_queued"] = widget.get_active() - - + + diff --git a/deluge/ui/gtkui/removetorrentdialog.py b/deluge/ui/gtkui/removetorrentdialog.py index 1b74ebc21..241063216 100644 --- a/deluge/ui/gtkui/removetorrentdialog.py +++ b/deluge/ui/gtkui/removetorrentdialog.py @@ -2,19 +2,19 @@ # removetorrentdialog.py # # Copyright (C) 2007 Andrew Resch ('andar') -# +# # Deluge is free software. -# +# # You may redistribute it and/or modify it under the terms of the # GNU General Public License, as published by the Free Software # Foundation; either version 3 of the License, or (at your option) # any later version. -# +# # deluge is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with deluge. If not, write to: # The Free Software Foundation, Inc., @@ -44,27 +44,27 @@ class RemoveTorrentDialog: self.torrent_ids = torrent_ids self.remove_torrentfile = remove_torrentfile self.remove_data = remove_data - + self.glade = gtk.glade.XML( - pkg_resources.resource_filename("deluge.ui.gtkui", + pkg_resources.resource_filename("deluge.ui.gtkui", "glade/remove_torrent_dialog.glade")) - + self.dialog = self.glade.get_widget("remove_torrent_dialog") self.dialog.set_icon(deluge.common.get_logo(32)) self.dialog.set_transient_for(component.get("MainWindow").window) - + self.glade.signal_autoconnect({ "on_button_ok_clicked": self.on_button_ok_clicked, "on_button_cancel_clicked": self.on_button_cancel_clicked }) - + if len(self.torrent_ids) > 1: # We need to pluralize the dialog self.dialog.set_title("Remove Torrents?") self.glade.get_widget("label_title").set_markup( _("Are you sure you want to remove the selected torrents?")) self.glade.get_widget("button_ok").set_label(_("Remove Selected Torrents")) - + if self.remove_torrentfile or self.remove_data: self.glade.get_widget("hseparator1").show() if self.remove_torrentfile: @@ -77,14 +77,14 @@ class RemoveTorrentDialog: self.dialog.destroy() return self.dialog.show() - + def on_button_ok_clicked(self, widget): client.remove_torrent( self.torrent_ids, self.remove_torrentfile, self.remove_data) # Unselect all to avoid issues with the selection changed event component.get("TorrentView").treeview.get_selection().unselect_all() self.dialog.destroy() - + def on_button_cancel_clicked(self, widget): self.dialog.destroy() - + diff --git a/deluge/ui/gtkui/sidebar.py b/deluge/ui/gtkui/sidebar.py index eb9904e46..6ce00a7d2 100644 --- a/deluge/ui/gtkui/sidebar.py +++ b/deluge/ui/gtkui/sidebar.py @@ -2,19 +2,19 @@ # sidebar.py # # Copyright (C) 2007 Andrew Resch ('andar') -# +# # Deluge is free software. -# +# # You may redistribute it and/or modify it under the terms of the # GNU General Public License, as published by the Free Software # Foundation; either version 3 of the License, or (at your option) # any later version. -# +# # deluge is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with deluge. If not, write to: # The Free Software Foundation, Inc., @@ -49,12 +49,12 @@ class SideBar(component.Component): self.scrolled = glade.get_widget("scrolledwindow_sidebar") self.is_visible = True self.config = ConfigManager("gtkui.conf") - + # Create the liststore # state str, str that's visible, icon self.liststore = gtk.ListStore(str, str, gtk.gdk.Pixbuf) self.liststore.append(["All", _("All"), None]) - self.liststore.append(["Downloading", _("Downloading"), + self.liststore.append(["Downloading", _("Downloading"), gtk.gdk.pixbuf_new_from_file( deluge.common.get_pixmap("downloading16.png"))]) self.liststore.append(["Seeding", _("Seeding"), @@ -82,29 +82,29 @@ class SideBar(component.Component): column.pack_start(render, expand=True) column.add_attribute(render, 'text', 1) self.label_view.append_column(column) - + self.label_view.set_model(self.liststore) - - self.label_view.get_selection().connect("changed", + + self.label_view.get_selection().connect("changed", self.on_selection_changed) - + # Select the 'All' label on init self.label_view.get_selection().select_iter( self.liststore.get_iter_first()) - + # Hide if necessary self.visible(self.config["show_sidebar"]) - + def visible(self, visible): if visible: self.scrolled.show() else: self.scrolled.hide() self.hpaned.set_position(-1) - + self.is_visible = visible self.config["show_sidebar"] = visible - + def on_selection_changed(self, selection): try: (model, row) = self.label_view.get_selection().get_selected() @@ -112,7 +112,7 @@ class SideBar(component.Component): log.debug(e) # paths is likely None .. so lets return None return None - + value = model.get_value(row, 0) if value == "All": component.get("TorrentView").set_filter(None, None) diff --git a/deluge/ui/gtkui/signals.py b/deluge/ui/gtkui/signals.py index 0b5e98ea9..f974ae92c 100644 --- a/deluge/ui/gtkui/signals.py +++ b/deluge/ui/gtkui/signals.py @@ -2,19 +2,19 @@ # signals.py # # Copyright (C) 2007, 2008 Andrew Resch ('andar') -# +# # Deluge is free software. -# +# # You may redistribute it and/or modify it under the terms of the # GNU General Public License, as published by the Free Software # Foundation; either version 3 of the License, or (at your option) # any later version. -# +# # deluge is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with deluge. If not, write to: # The Free Software Foundation, Inc., @@ -46,21 +46,21 @@ class Signals(component.Component): self.config = ConfigManager("gtkui.conf") self.config["signal_port"] = self.receiver.get_port() self.config.save() - + def start(self): self.receiver.set_remote(not client.is_localhost()) self.receiver.run() - self.receiver.connect_to_signal("torrent_added", + self.receiver.connect_to_signal("torrent_added", self.torrent_added_signal) - self.receiver.connect_to_signal("torrent_removed", + self.receiver.connect_to_signal("torrent_removed", self.torrent_removed_signal) self.receiver.connect_to_signal("torrent_paused", self.torrent_paused) - self.receiver.connect_to_signal("torrent_resumed", + self.receiver.connect_to_signal("torrent_resumed", self.torrent_resumed) - self.receiver.connect_to_signal("torrent_all_paused", + self.receiver.connect_to_signal("torrent_all_paused", self.torrent_all_paused) - self.receiver.connect_to_signal("torrent_all_resumed", + self.receiver.connect_to_signal("torrent_all_resumed", self.torrent_all_resumed) self.receiver.connect_to_signal("config_value_changed", self.config_value_changed) @@ -74,17 +74,17 @@ class Signals(component.Component): self.args_from_external) self.receiver.connect_to_signal("torrent_state_changed", self.torrent_state_changed) - + def stop(self): try: self.receiver.shutdown() except: pass - + def connect_to_signal(self, signal, callback): """Connects a callback to a signal""" self.receiver.connect_to_signal(signal, callback) - + def torrent_added_signal(self, torrent_id): log.debug("torrent_added signal received..") log.debug("torrent id: %s", torrent_id) @@ -104,54 +104,54 @@ class Signals(component.Component): component.get("TorrentView").update() component.get("ToolBar").update_buttons() component.get("MenuBar").update_menu() - + def torrent_resumed(self, torrent_id): log.debug("torrent_resumed signal received..") component.get("TorrentView").mark_dirty(torrent_id) component.get("TorrentView").update() component.get("ToolBar").update_buttons() component.get("MenuBar").update_menu() - + def torrent_all_paused(self): log.debug("torrent_all_paused signal received..") component.get("TorrentView").mark_dirty() component.get("TorrentView").update() component.get("ToolBar").update_buttons() component.get("MenuBar").update_menu() - + def torrent_all_resumed(self): log.debug("torrent_all_resumed signal received..") component.get("TorrentView").mark_dirty() component.get("TorrentView").update() component.get("ToolBar").update_buttons() component.get("MenuBar").update_menu() - + def config_value_changed(self, key, value): log.debug("config_value_changed signal received..") component.get("StatusBar").config_value_changed(key, value) component.get("SystemTray").config_value_changed(key, value) - + def torrent_queue_changed(self): log.debug("torrent_queue_changed signal received..") component.get("TorrentView").mark_dirty() component.get("TorrentView").update() - + def torrent_resume_at_stop_ratio(self): log.debug("torrent_resume_at_stop_ratio") component.get("StatusBar").display_warning( text=_("Torrent is past stop ratio.")) - + def new_version_available(self, value): log.debug("new_version_available: %s", value) if self.config["show_new_releases"]: from deluge.ui.gtkui.new_release_dialog import NewReleaseDialog NewReleaseDialog().show(value) - + def args_from_external(self, value): log.debug("args_from_external: %s", value) import ipcinterface ipcinterface.process_args(value) - + def torrent_state_changed(self, value): log.debug("torrent_state_changed: %s", value) - + diff --git a/deluge/ui/gtkui/systemtray.py b/deluge/ui/gtkui/systemtray.py index bb5a1580a..2869fd8c9 100644 --- a/deluge/ui/gtkui/systemtray.py +++ b/deluge/ui/gtkui/systemtray.py @@ -2,19 +2,19 @@ # systemtray.py # # Copyright (C) 2007, 2008 Andrew Resch ('andar') -# +# # Deluge is free software. -# +# # You may redistribute it and/or modify it under the terms of the # GNU General Public License, as published by the Free Software # Foundation; either version 3 of the License, or (at your option) # any later version. -# +# # deluge is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with deluge. If not, write to: # The Free Software Foundation, Inc., @@ -58,7 +58,7 @@ class SystemTray(component.Component): "separatormenuitem3", "separatormenuitem4" ] - self.config.register_set_function("enable_system_tray", + self.config.register_set_function("enable_system_tray", self.on_enable_system_tray_set) self.max_download_speed = -1.0 @@ -70,12 +70,12 @@ class SystemTray(component.Component): "max_download_speed": self._on_max_download_speed, "max_upload_speed": self._on_max_upload_speed } - + def enable(self): """Enables the system tray icon.""" log.debug("Enabling the system tray icon..") self.tray_glade = gtk.glade.XML( - pkg_resources.resource_filename("deluge.ui.gtkui", + pkg_resources.resource_filename("deluge.ui.gtkui", "glade/tray_menu.glade")) if deluge.common.windows_check(): @@ -87,11 +87,11 @@ class SystemTray(component.Component): except: log.warning("Update PyGTK to 2.10 or greater for SystemTray..") return - + self.tray.connect("activate", self.on_tray_clicked) self.tray.connect("popup-menu", self.on_tray_popup) - - + + self.tray_glade.signal_autoconnect({ "on_menuitem_show_deluge_activate": \ self.on_menuitem_show_deluge_activate, @@ -105,7 +105,7 @@ class SystemTray(component.Component): "on_menuitem_quitdaemon_activate": \ self.on_menuitem_quitdaemon_activate }) - + self.tray_menu = self.tray_glade.get_widget("tray_menu") self.tray_glade.get_widget("download-limit-image").set_from_file( @@ -118,12 +118,12 @@ class SystemTray(component.Component): self.hide_widget_list.remove("separatormenuitem4") self.tray_glade.get_widget("menuitem_quitdaemon").hide() self.tray_glade.get_widget("separatormenuitem4").hide() - + if client.get_core_uri() == None: # Hide menu widgets because we're not connected to a host. for widget in self.hide_widget_list: self.tray_glade.get_widget(widget).hide() - + def start(self): if self.config["enable_system_tray"]: # Show widgets in the hide list because we've connected to a host @@ -132,7 +132,7 @@ class SystemTray(component.Component): # Build the bandwidth speed limit menus self.build_tray_bwsetsubmenu() - + # Get some config values client.get_config_value( self._on_max_download_speed, "max_download_speed") @@ -147,10 +147,10 @@ class SystemTray(component.Component): self.tray_glade.get_widget(widget).hide() except Exception, e: log.debug("Unable to hide system tray menu widgets: %s", e) - + def shutdown(self): self.tray.set_visible(False) - + def send_status_request(self): client.get_download_rate(self._on_get_download_rate) client.get_upload_rate(self._on_get_upload_rate) @@ -158,31 +158,31 @@ class SystemTray(component.Component): def config_value_changed(self, key, value): """This is called when we received a config_value_changed signal from the core.""" - + if key in self.config_value_changed_dict.keys(): self.config_value_changed_dict[key](value) - + def _on_max_download_speed(self, max_download_speed): if self.max_download_speed != max_download_speed: self.max_download_speed = max_download_speed self.build_tray_bwsetsubmenu() - + def _on_get_download_rate(self, download_rate): self.download_rate = deluge.common.fsize(download_rate) - + def _on_max_upload_speed(self, max_upload_speed): if self.max_upload_speed != max_upload_speed: self.max_upload_speed = max_upload_speed self.build_tray_bwsetsubmenu() - + def _on_get_upload_rate(self, upload_rate): self.upload_rate = deluge.common.fsize(upload_rate) - + def update(self): # Set the tool tip text max_download_speed = self.max_download_speed max_upload_speed = self.max_upload_speed - + if max_download_speed == -1: max_download_speed = _("Unlimited") else: @@ -191,39 +191,39 @@ class SystemTray(component.Component): max_upload_speed = _("Unlimited") else: max_upload_speed = "%s KiB/s" % (max_upload_speed) - + msg = '%s\n%s: %s (%s)\n%s: %s (%s)' % (\ _("Deluge"), _("Down"), self.download_rate, \ max_download_speed, _("Up"), self.upload_rate, max_upload_speed) - + # Set the tooltip self.tray.set_tooltip(msg) - + self.send_status_request() - + def build_tray_bwsetsubmenu(self): # Create the Download speed list sub-menu submenu_bwdownset = deluge.common.build_menu_radio_list( self.config["tray_download_speed_list"], self.tray_setbwdown, self.max_download_speed, _("KiB/s"), show_notset=True, show_other=True) - + # Create the Upload speed list sub-menu submenu_bwupset = deluge.common.build_menu_radio_list( - self.config["tray_upload_speed_list"], self.tray_setbwup, + self.config["tray_upload_speed_list"], self.tray_setbwup, self.max_upload_speed, _("KiB/s"), show_notset=True, show_other=True) - + # Add the sub-menus to the tray menu self.tray_glade.get_widget("menuitem_download_limit").set_submenu( submenu_bwdownset) self.tray_glade.get_widget("menuitem_upload_limit").set_submenu( submenu_bwupset) - + # Show the sub-menus for all to see submenu_bwdownset.show_all() submenu_bwupset.show_all() - + def disable(self): """Disables the system tray icon.""" log.debug("Disabling the system tray icon..") @@ -234,14 +234,14 @@ class SystemTray(component.Component): del self.tray_menu except Exception, e: log.debug("Unable to disable system tray: %s", e) - + def on_enable_system_tray_set(self, key, value): """Called whenever the 'enable_system_tray' config key is modified""" if value: self.enable() else: self.disable() - + def on_tray_clicked(self, icon): """Called when the tray icon is left clicked.""" if self.window.active(): @@ -251,18 +251,18 @@ class SystemTray(component.Component): if not self.unlock_tray(): return self.window.present() - + def on_tray_popup(self, status_icon, button, activate_time): """Called when the tray icon is right clicked.""" if self.window.visible(): self.tray_glade.get_widget("menuitem_show_deluge").set_active(True) else: self.tray_glade.get_widget("menuitem_show_deluge").set_active(False) - + popup_function = gtk.status_icon_position_menu if deluge.common.windows_check(): popup_function = None - self.tray_menu.popup(None, None, popup_function, + self.tray_menu.popup(None, None, popup_function, button, activate_time, status_icon) def on_menuitem_show_deluge_activate(self, menuitem): @@ -274,20 +274,20 @@ class SystemTray(component.Component): self.window.present() elif not menuitem.get_active() and self.window.visible(): self.window.hide() - + def on_menuitem_add_torrent_activate(self, menuitem): log.debug("on_menuitem_add_torrent_activate") from addtorrentdialog import AddTorrentDialog client.add_torrent_file(AddTorrentDialog().show()) - + def on_menuitem_pause_all_activate(self, menuitem): log.debug("on_menuitem_pause_all_activate") client.pause_all_torrents() - + def on_menuitem_resume_all_activate(self, menuitem): log.debug("on_menuitem_resume_all_activate") client.resume_all_torrents() - + def on_menuitem_quit_activate(self, menuitem): log.debug("on_menuitem_quit_activate") if self.config["lock_tray"]: @@ -298,7 +298,7 @@ class SystemTray(component.Component): client.shutdown() self.window.quit() - + def on_menuitem_quitdaemon_activate(self, menuitem): log.debug("on_menuitem_quitdaemon_activate") if self.config["lock_tray"]: @@ -307,54 +307,54 @@ class SystemTray(component.Component): client.shutdown() self.window.quit() - - def tray_setbwdown(self, widget, data=None): - self.setbwlimit(widget, _("Download"), "max_download_speed", + + def tray_setbwdown(self, widget, data=None): + self.setbwlimit(widget, _("Download"), "max_download_speed", "tray_download_speed_list", self.max_download_speed) - + def tray_setbwup(self, widget, data=None): self.setbwlimit(widget, _("Upload"), "max_upload_speed", "tray_upload_speed_list", self.max_upload_speed) - + def setbwlimit(self, widget, string, core_key, ui_key, default): """Sets the bandwidth limit based on the user selection.""" value = widget.get_children()[0].get_text().rstrip(" " + _("KiB/s")) if value == _("Unlimited"): value = -1 - + if value == _("Other..."): value = deluge.common.show_other_dialog( string + " Speed (KiB/s):", default) if value == None: return - + # Set the config in the core value = float(value) config_to_set = {core_key: value} client.set_config(config_to_set) self.build_tray_bwsetsubmenu() - + def unlock_tray(self, is_showing_dlg=[False]): try: from hashlib import sha1 as sha_hash except ImportError: from sha import new as sha_hash - + log.debug("Show tray lock dialog") result = False - + if is_showing_dlg[0]: return is_showing_dlg[0] = True - + entered_pass = gtk.Entry(25) entered_pass.set_activates_default(True) entered_pass.set_width_chars(25) entered_pass.set_visibility(False) entered_pass.show() tray_lock = gtk.Dialog(title=_("Deluge is locked"), parent=None, - buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_OK, + buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)) label = gtk.Label(_("Deluge is password protected.\nTo show the Deluge \ window, please enter your password")) @@ -372,6 +372,6 @@ window, please enter your password")) result = True tray_lock.destroy() is_showing_dlg[0] = False - + return result diff --git a/deluge/ui/gtkui/toolbar.py b/deluge/ui/gtkui/toolbar.py index fee81c772..87f6f09cd 100644 --- a/deluge/ui/gtkui/toolbar.py +++ b/deluge/ui/gtkui/toolbar.py @@ -2,19 +2,19 @@ # toolbar.py # # Copyright (C) 2007, 2008 Andrew Resch ('andar') -# +# # Deluge is free software. -# +# # You may redistribute it and/or modify it under the terms of the # GNU General Public License, as published by the Free Software # Foundation; either version 3 of the License, or (at your option) # any later version. -# +# # deluge is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with deluge. If not, write to: # The Free Software Foundation, Inc., @@ -70,7 +70,7 @@ class ToolBar(component.Component): "toolbutton_queue_up", "toolbutton_queue_down" ] - + # Set the Remove Torrent toolbuttons drop-down menu tb_remove = self.window.main_glade.get_widget("toolbutton_remove") tb_remove.set_menu( @@ -78,7 +78,7 @@ class ToolBar(component.Component): if self.config["classic_mode"]: self.window.main_glade.get_widget("toolbutton_connectionmanager").hide() - + # Hide if necessary self.visible(self.config["show_toolbar"]) @@ -86,19 +86,19 @@ class ToolBar(component.Component): for widget in self.change_sensitivity: self.window.main_glade.get_widget(widget).set_sensitive(True) self.update_buttons() - + def stop(self): for widget in self.change_sensitivity: self.window.main_glade.get_widget(widget).set_sensitive(False) - + def visible(self, visible): if visible: self.toolbar.show() else: self.toolbar.hide() - + self.config["show_toolbar"] = visible - + def add_toolbutton(self, callback, label=None, image=None, stock=None, tooltip=None): """Adds a toolbutton to the toolbar""" @@ -115,15 +115,15 @@ class ToolBar(component.Component): toolbutton.set_tooltip_text(tooltip) # Connect the 'clicked' event callback toolbutton.connect("clicked", callback) - + # Append the button to the toolbar self.toolbar.insert(toolbutton, -1) - + # Show the new toolbutton toolbutton.show_all() - + return toolbutton - + def add_separator(self, position=None): """Adds a separator toolitem""" sep = gtk.SeparatorToolItem() @@ -134,13 +134,13 @@ class ToolBar(component.Component): self.toolbar.insert(sep, -1) sep.show() - + return sep - + def remove(self, widget): """Removes a widget from the toolbar""" self.toolbar.remove(widget) - + ### Callbacks ### def on_toolbutton_add_clicked(self, data): log.debug("on_toolbutton_add_clicked") @@ -156,7 +156,7 @@ class ToolBar(component.Component): log.debug("on_toolbutton_pause_clicked") # Use the menubar's callbacks component.get("MenuBar").on_menuitem_pause_activate(data) - + def on_toolbutton_resume_clicked(self, data): log.debug("on_toolbutton_resume_clicked") # Use the menubar's calbacks @@ -171,18 +171,18 @@ class ToolBar(component.Component): log.debug("on_toolbutton_connectionmanager_clicked") # Use the menubar's callbacks component.get("MenuBar").on_menuitem_connectionmanager_activate(data) - + def on_toolbutton_queue_up_clicked(self, data): log.debug("on_toolbutton_queue_up_clicked") component.get("MenuBar").on_menuitem_queue_up_activate(data) - + def on_toolbutton_queue_down_clicked(self, data): log.debug("on_toolbutton_queue_down_clicked") component.get("MenuBar").on_menuitem_queue_down_activate(data) def update_buttons(self, action=None, torrent_id=None): if action == None: - # If all the selected torrents are paused, then disable the 'Pause' + # If all the selected torrents are paused, then disable the 'Pause' # button. # The same goes for the 'Resume' button. pause = False @@ -205,18 +205,18 @@ class ToolBar(component.Component): if pause and resume: break - # Enable the 'Remove Torrent' button only if there's some selected + # Enable the 'Remove Torrent' button only if there's some selected # torrent. remove = (len(selected) > 0) - + for name, sensitive in (("toolbutton_pause", pause), ("toolbutton_resume", resume), ("toolbutton_remove", remove)): self.window.main_glade.get_widget(name).set_sensitive(sensitive) - - + + return False - + pause = False resume = False if action == "paused": @@ -231,5 +231,5 @@ class ToolBar(component.Component): self.window.main_glade.get_widget("toolbutton_resume").set_sensitive(resume) else: self.update_buttons() - + return False diff --git a/deluge/ui/gtkui/torrentdetails.py b/deluge/ui/gtkui/torrentdetails.py index edbc9b516..08c130409 100644 --- a/deluge/ui/gtkui/torrentdetails.py +++ b/deluge/ui/gtkui/torrentdetails.py @@ -2,19 +2,19 @@ # torrentdetails.py # # Copyright (C) 2007 Andrew Resch ('andar') -# +# # Deluge is free software. -# +# # You may redistribute it and/or modify it under the terms of the # GNU General Public License, as published by the Free Software # Foundation; either version 3 of the License, or (at your option) # any later version. -# +# # deluge is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with deluge. If not, write to: # The Free Software Foundation, Inc., @@ -49,38 +49,38 @@ class Tab: self.is_visible = True self.position = -1 self.weight = -1 - + def get_name(self): return self._name - + def get_child_widget(self): parent = self._child_widget.get_parent() if parent is not None: parent.remove(self._child_widget) - + return self._child_widget - + def get_tab_label(self): parent = self._tab_label.get_parent() log.debug("parent: %s", parent) if parent is not None: parent.remove(self._tab_label) - + return self._tab_label - + class TorrentDetails(component.Component): def __init__(self): component.Component.__init__(self, "TorrentDetails", interval=2000) self.window = component.get("MainWindow") glade = self.window.main_glade - + self.notebook = glade.get_widget("torrent_info") - + # This is the menu item we'll attach the tabs checklist menu to self.menu_tabs = glade.get_widget("menu_tabs") - + self.notebook.connect("switch-page", self._on_switch_page) - + # Tabs holds references to the Tab objects by their name self.tabs = {} @@ -98,7 +98,7 @@ class TorrentDetails(component.Component): "Peers": PeersTab, "Options": OptionsTab } - + # tab_name, visible default_order = [ ("Statistics", True), @@ -107,7 +107,7 @@ class TorrentDetails(component.Component): ("Peers", True), ("Options", True) ] - + # Get the state from saved file state = self.load_state() @@ -117,7 +117,7 @@ class TorrentDetails(component.Component): log.debug("Old tabs.state, using default..") state = None break - + # The state is a list of tab_names in the order they should appear if state == None: # Set the default order @@ -126,15 +126,15 @@ class TorrentDetails(component.Component): # Add the tabs in the order from the state for tab_name, visible in state: self.add_tab(default_tabs[tab_name]()) - + # Hide any of the non-visible ones for tab_name, visible in state: if not visible: self.hide_tab(tab_name) - + # Generate the checklist menu self.generate_menu() - + def add_tab(self, tab_object, position=-1, generate_menu=True): """Adds a tab object to the notebook.""" self.tabs[tab_object.get_name()] = tab_object @@ -145,18 +145,18 @@ class TorrentDetails(component.Component): tab_object.position = pos tab_object.weight = pos - + # Regenerate positions if an insert occured if position > -1: self.regenerate_positions() - + if generate_menu: self.generate_menu() - + if not self.notebook.get_property("visible"): # If the notebook isn't visible, show it self.visible(True) - + def regenerate_positions(self): """This will sync up the positions in the tab, with the position stored in the tab object""" @@ -171,7 +171,7 @@ class TorrentDetails(component.Component): del self.tabs[tab_name] self.regenerate_positions() self.generate_menu() - + # If there are no tabs visible, then do not show the notebook if len(self.tabs) == 0: self.visible(False) @@ -181,13 +181,13 @@ class TorrentDetails(component.Component): log.debug("n_pages: %s", self.notebook.get_n_pages()) for n in xrange(self.notebook.get_n_pages() - 1, -1, -1): self.notebook.remove_page(n) - + for tab in self.tabs: self.tabs[tab].is_visible = False log.debug("n_pages: %s", self.notebook.get_n_pages()) self.generate_menu() self.visible(False) - + def show_all_tabs(self): """Shows all tabs""" for tab in self.tabs: @@ -201,7 +201,7 @@ class TorrentDetails(component.Component): if not self.notebook.get_property("visible"): # If the notebook isn't visible, show it self.visible(True) - + def hide_tab(self, tab_name): """Hides tab by name""" self.notebook.remove_page(self.tabs[tab_name].position) @@ -217,11 +217,11 @@ class TorrentDetails(component.Component): # Determine insert position based on weight # weights is a list of visible tab names in weight order weights = [] - + for tab in self.tabs: if self.tabs[tab].is_visible: weights.append((self.tabs[tab].weight, self.tabs[tab].get_name())) - + weights.sort() log.debug("weights: %s", weights) position = self.tabs[tab_name].position @@ -230,7 +230,7 @@ class TorrentDetails(component.Component): if w[0] >= self.tabs[tab_name].weight: position = self.tabs[w[1]].position break - + log.debug("position: %s", position) self.notebook.insert_page( self.tabs[tab_name].get_child_widget(), @@ -239,7 +239,7 @@ class TorrentDetails(component.Component): self.tabs[tab_name].is_visible = True self.regenerate_positions() self.generate_menu() - + def generate_menu(self): """Generates the checklist menu for all the tabs and attaches it""" menu = gtk.Menu() @@ -252,35 +252,35 @@ class TorrentDetails(component.Component): all_tabs = False break menuitem.set_active(all_tabs) - menuitem.connect("toggled", self._on_menuitem_toggled) + menuitem.connect("toggled", self._on_menuitem_toggled) menu.append(menuitem) - + menuitem = gtk.SeparatorMenuItem() menu.append(menuitem) - + # Create a list in order of tabs to create menu menuitem_list = [] for tab_name in self.tabs: menuitem_list.append((self.tabs[tab_name].weight, tab_name)) menuitem_list.sort() - + for pos, name in menuitem_list: menuitem = gtk.CheckMenuItem(name) menuitem.set_active(self.tabs[name].is_visible) menuitem.connect("toggled", self._on_menuitem_toggled) menu.append(menuitem) - + self.menu_tabs.set_submenu(menu) self.menu_tabs.show_all() - + def visible(self, visible): if visible: self.notebook.show() else: self.notebook.hide() self.window.vpaned.set_position(-1) - + def set_tab_visible(self, tab_name, visible): """Sets the tab to visible""" log.debug("set_tab_visible name: %s visible: %s", tab_name, visible) @@ -288,10 +288,10 @@ class TorrentDetails(component.Component): self.show_tab(tab_name) elif not visible and self.tabs[tab_name].is_visible: self.hide_tab(tab_name) - + def stop(self): self.clear() - + def shutdown(self): # Save the state of the tabs for tab in self.tabs: @@ -299,7 +299,7 @@ class TorrentDetails(component.Component): self.tabs[tab].save_state() except AttributeError: pass - + # Save tabs state self.save_state() @@ -307,7 +307,7 @@ class TorrentDetails(component.Component): if len(component.get("TorrentView").get_selected_torrents()) == 0: # No torrents selected, so just clear self.clear() - + if self.notebook.get_property("visible"): if page_num == None: page_num = self.notebook.get_current_page() @@ -322,7 +322,7 @@ class TorrentDetails(component.Component): # Update the tab that is in view if name: self.tabs[name].update() - + def clear(self): # Get the tab name try: @@ -349,9 +349,9 @@ class TorrentDetails(component.Component): else: self.hide_all_tabs() return - + self.set_tab_visible(name, widget.get_active()) - + def save_state(self): """We save the state, which is basically the tab_index list""" filename = "tabs.state" @@ -362,7 +362,7 @@ class TorrentDetails(component.Component): # Sort by weight state.sort() state = [(n, v) for w, n, v in state] - + # Get the config location for saving the state file config_location = ConfigManager("gtkui.conf")["config_location"] @@ -379,7 +379,7 @@ class TorrentDetails(component.Component): # Get the config location for loading the state file config_location = ConfigManager("gtkui.conf")["config_location"] state = None - + try: log.debug("Loading TorrentDetails state file: %s", filename) state_file = open(os.path.join(config_location, filename), "rb") @@ -387,5 +387,5 @@ class TorrentDetails(component.Component): state_file.close() except (EOFError, IOError), e: log.warning("Unable to load state file: %s", e) - + return state diff --git a/deluge/ui/gtkui/torrentview.py b/deluge/ui/gtkui/torrentview.py index e776cd647..16257185b 100644 --- a/deluge/ui/gtkui/torrentview.py +++ b/deluge/ui/gtkui/torrentview.py @@ -2,19 +2,19 @@ # torrentview.py # # Copyright (C) 2007, 2008 Andrew Resch ('andar') -# +# # Deluge is free software. -# +# # You may redistribute it and/or modify it under the terms of the # GNU General Public License, as published by the Free Software # Foundation; either version 3 of the License, or (at your option) # any later version. -# +# # deluge is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with deluge. If not, write to: # The Free Software Foundation, Inc., @@ -59,7 +59,7 @@ icon_queued = gtk.gdk.pixbuf_new_from_file( deluge.common.get_pixmap("queued16.png")) icon_checking = gtk.gdk.pixbuf_new_from_file( deluge.common.get_pixmap("checking16.png")) - + # Holds the info for which status icon to display based on state ICON_STATE = { "Allocating": icon_checking, @@ -70,7 +70,7 @@ ICON_STATE = { "Error": icon_alert, "Queued": icon_queued } - + def cell_data_statusicon(column, cell, model, row, data): """Display text with an icon""" try: @@ -79,7 +79,7 @@ def cell_data_statusicon(column, cell, model, row, data): cell.set_property("pixbuf", icon) except KeyError: pass - + def cell_data_progress(column, cell, model, row, data): """Display progress bar with text""" (value, state_str) = model.get(row, *data) @@ -88,7 +88,7 @@ def cell_data_progress(column, cell, model, row, data): textstr = "%s" % state_str if state_str != "Seeding" and value < 100: - textstr = textstr + " %.2f%%" % value + textstr = textstr + " %.2f%%" % value if cell.get_property("text") != textstr: cell.set_property("text", textstr) @@ -98,19 +98,19 @@ def cell_data_queue(column, cell, model, row, data): cell.set_property("text", "") else: cell.set_property("text", value + 1) - + class TorrentView(listview.ListView, component.Component): """TorrentView handles the listing of torrents.""" def __init__(self): component.Component.__init__(self, "TorrentView", interval=2000) self.window = component.get("MainWindow") # Call the ListView constructor - listview.ListView.__init__(self, + listview.ListView.__init__(self, self.window.main_glade.get_widget("torrent_view")) log.debug("TorrentView Init..") # Try to load the state file if available self.load_state("torrentview.state") - + # This is where status updates are put self.status = {} @@ -118,31 +118,31 @@ class TorrentView(listview.ListView, component.Component): # accordingly. self.register_checklist_menu( self.window.main_glade.get_widget("menu_columns")) - + # Add the columns to the listview self.add_text_column("torrent_id", hidden=True) self.add_bool_column("filter", hidden=True) self.add_bool_column("dirty", hidden=True) self.add_func_column("#", cell_data_queue, [int], status_field=["queue"]) - self.add_texticon_column(_("Name"), status_field=["state", "name"], + self.add_texticon_column(_("Name"), status_field=["state", "name"], function=cell_data_statusicon) - self.add_func_column(_("Size"), - listview.cell_data_size, + self.add_func_column(_("Size"), + listview.cell_data_size, [gobject.TYPE_UINT64], status_field=["total_wanted"]) - self.add_progress_column(_("Progress"), + self.add_progress_column(_("Progress"), status_field=["progress", "state"], col_types=[float, str], function=cell_data_progress) self.add_func_column(_("Seeders"), listview.cell_data_peer, [int, int], - status_field=["num_seeds", + status_field=["num_seeds", "total_seeds"]) self.add_func_column(_("Peers"), listview.cell_data_peer, [int, int], - status_field=["num_peers", + status_field=["num_peers", "total_peers"]) self.add_func_column(_("Down Speed"), listview.cell_data_speed, @@ -177,11 +177,11 @@ class TorrentView(listview.ListView, component.Component): self.on_button_press_event) # Connect to the 'changed' event of TreeViewSelection to get selection # changes. - self.treeview.get_selection().connect("changed", + self.treeview.get_selection().connect("changed", self.on_selection_changed) - + self.treeview.connect("drag-drop", self.on_drag_drop) - + def start(self): """Start the torrentview""" # We need to get the core session state to know which torrents are in @@ -190,7 +190,7 @@ class TorrentView(listview.ListView, component.Component): def create_model_filter(self): """create new filter-model - must be called after listview.create_new_liststore + must be called after listview.create_new_liststore """ sort_column = None if self.treeview.get_model(): @@ -209,10 +209,10 @@ class TorrentView(listview.ListView, component.Component): for torrent_id in state: self.add_row(torrent_id, update=False) self.mark_dirty(torrent_id) - - self.update_filter() + + self.update_filter() self.update() - + def stop(self): """Stops the torrentview""" # We need to clear the liststore @@ -221,27 +221,27 @@ class TorrentView(listview.ListView, component.Component): def shutdown(self): """Called when GtkUi is exiting""" self.save_state("torrentview.state") - + def set_filter(self, field, condition): """Sets filters for the torrentview..""" if self.filter != (None, None): self.filter = (None, None) self.update_filter() - + self.filter = (field, condition) self.update_filter() self.update() - + def send_status_request(self, columns=None): # Store the 'status_fields' we need to send to core status_keys = [] # Store the actual columns we will be updating self.columns_to_update = [] - + if columns is None: # We need to iterate through all columns columns = self.columns.keys() - + # Iterate through supplied list of columns to update for column in columns: # Make sure column is visible and has 'status_field' set. @@ -252,17 +252,17 @@ class TorrentView(listview.ListView, component.Component): for field in self.columns[column].status_field: status_keys.append(field) self.columns_to_update.append(column) - + # Remove duplicate keys - self.columns_to_update = list(set(self.columns_to_update)) + self.columns_to_update = list(set(self.columns_to_update)) # If there is nothing in status_keys then we must not continue if status_keys is []: return - + # Remove duplicates from status_key list status_keys = list(set(status_keys)) - + # Create list of torrent_ids in need of status updates torrent_ids = [] for row in self.liststore: @@ -279,7 +279,7 @@ class TorrentView(listview.ListView, component.Component): # will deal with the return in a signal callback. client.get_torrents_status( self._on_get_torrents_status, torrent_ids, status_keys) - + def update_filter(self): # Update the filter view for row in self.liststore: @@ -301,11 +301,11 @@ class TorrentView(listview.ListView, component.Component): row[filter_column] = True else: row[filter_column] = False - + def update(self): # Send a status request self.send_status_request() - + def update_view(self, columns=None): """Update the view. If columns is not None, it will attempt to only update those columns selected. @@ -319,7 +319,7 @@ class TorrentView(listview.ListView, component.Component): for column in self.columns_to_update: column_index = self.get_column_index(column) if type(column_index) is not list: - # We only have a single list store column we need to + # We only have a single list store column we need to # update try: # Only update if different @@ -328,7 +328,7 @@ class TorrentView(listview.ListView, component.Component): row[column_index] = status[torrent_id][ self.columns[column].status_field[0]] except (TypeError, KeyError), e: - log.warning("Unable to update column %s: %s", + log.warning("Unable to update column %s: %s", column, e) else: # We have more than 1 liststore column to update @@ -340,7 +340,7 @@ class TorrentView(listview.ListView, component.Component): status[torrent_id][ self.columns[column].status_field[ column_index.index(index)]]: - + row[index] = \ status[torrent_id][ self.columns[column].status_field[ @@ -351,7 +351,7 @@ class TorrentView(listview.ListView, component.Component): # Update the toolbar buttons just in case some state has changed component.get("ToolBar").update_buttons() component.get("MenuBar").update_menu() - + def _on_get_torrents_status(self, status): """Callback function for get_torrents_status(). 'status' should be a dictionary of {torrent_id: {key, value}}.""" @@ -359,10 +359,10 @@ class TorrentView(listview.ListView, component.Component): self.status = status else: self.status = {} - + if self.status != {}: self.update_view() - + def add_row(self, torrent_id, update=True): """Adds a new torrent row to the treeview""" # Insert a new row to the liststore @@ -370,12 +370,12 @@ class TorrentView(listview.ListView, component.Component): # Store the torrent id self.liststore.set_value( row, - self.columns["torrent_id"].column_indices[0], + self.columns["torrent_id"].column_indices[0], torrent_id) if update: self.update() self.update_filter() - + def remove_row(self, torrent_id): """Removes a row with torrent_id""" for row in self.liststore: @@ -392,7 +392,7 @@ class TorrentView(listview.ListView, component.Component): log.debug("marking %s dirty", torrent_id) row[self.columns["dirty"].column_indices[0]] = True if torrent_id: break - + def get_selected_torrent(self): """Returns a torrent_id or None. If multiple torrents are selected, it will return the torrent_id of the first one.""" @@ -401,7 +401,7 @@ class TorrentView(listview.ListView, component.Component): return selected[0] else: return selected - + def get_selected_torrents(self): """Returns a list of selected torrents or None""" torrent_ids = [] @@ -417,7 +417,7 @@ class TorrentView(listview.ListView, component.Component): except Exception, e: log.debug("Unable to get iter from path: %s", e) continue - + child_row = self.model_filter.convert_iter_to_child_iter(None, row) child_row = self.model_filter.get_model().convert_iter_to_child_iter(child_row) if self.liststore.iter_is_valid(child_row): @@ -429,22 +429,22 @@ class TorrentView(listview.ListView, component.Component): torrent_ids.append(value) if len(torrent_ids) == 0: return [] - + return torrent_ids except ValueError, TypeError: return [] - + def get_torrent_status(self, torrent_id): """Returns data stored in self.status, it may not be complete""" try: return self.status[torrent_id] except: return {} - + def get_visible_torrents(self): return self.status.keys() - - ### Callbacks ### + + ### Callbacks ### def on_button_press_event(self, widget, event): """This is a callback for showing the right-click context menu.""" log.debug("on_button_press_event") @@ -465,13 +465,13 @@ class TorrentView(listview.ListView, component.Component): torrentmenu = component.get("MenuBar").torrentmenu torrentmenu.popup(None, None, None, event.button, event.time) return True - + def on_selection_changed(self, treeselection): """This callback is know when the selection has changed.""" log.debug("on_selection_changed") component.get("TorrentDetails").update() component.get("ToolBar").update_buttons() component.get("MenuBar").update_menu() - + def on_drag_drop(self, widget, drag_context, x, y, timestamp): widget.stop_emission("drag-drop") diff --git a/deluge/ui/null/deluge_shell.py b/deluge/ui/null/deluge_shell.py index 793d35583..f98486b39 100755 --- a/deluge/ui/null/deluge_shell.py +++ b/deluge/ui/null/deluge_shell.py @@ -201,7 +201,7 @@ class CommandConfig(Command): color = CYAN elif isinstance(value, list): color = MAGENTA - + print ("* " + BLUE_B + "%s:" + color + " %s" + NORMAL) % (key, value) client.get_config(_on_get_config) diff --git a/deluge/ui/signalreceiver.py b/deluge/ui/signalreceiver.py index 2a4ce716c..20220c9a7 100644 --- a/deluge/ui/signalreceiver.py +++ b/deluge/ui/signalreceiver.py @@ -2,19 +2,19 @@ # signalreceiver.py # # Copyright (C) 2007, 2008 Andrew Resch ('andar') -# +# # Deluge is free software. -# +# # You may redistribute it and/or modify it under the terms of the # GNU General Public License, as published by the Free Software # Foundation; either version 3 of the License, or (at your option) # any later version. -# +# # deluge is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with deluge. If not, write to: # The Free Software Foundation, Inc., @@ -47,23 +47,23 @@ import socket from deluge.log import LOG as log class SignalReceiver(ThreadingMixIn, SimpleXMLRPCServer.SimpleXMLRPCServer): - + def __init__(self): log.debug("SignalReceiver init..") self.signals = {} self.emitted_signals = [] - + self.remote = False - + self.start_server() - + def start_server(self, port=None): # Setup the xmlrpc server host = "127.0.0.1" if self.remote: host = "" - + server_ready = False while not server_ready: if port: @@ -81,10 +81,10 @@ class SignalReceiver(ThreadingMixIn, SimpleXMLRPCServer.SimpleXMLRPCServer): else: self.port = _port server_ready = True - + # Register the emit_signal function self.register_function(self.emit_signal) - + def shutdown(self): """Shutdowns receiver thread""" self._shutdown = True @@ -94,18 +94,18 @@ class SignalReceiver(ThreadingMixIn, SimpleXMLRPCServer.SimpleXMLRPCServer): client.force_call() except Exception, e: log.debug("Unable to deregister client from server: %s", e) - + log.debug("Shutting down signalreceiver") self.socket.shutdown(socket.SHUT_RDWR) log.debug("Joining listening thread..") self.listening_thread.join(1.0) return - + def set_remote(self, remote): self.remote = remote self.start_server(self.port) - + def run(self): """This gets called when we start the thread""" # Set to true so that the receiver thread will exit @@ -113,16 +113,16 @@ class SignalReceiver(ThreadingMixIn, SimpleXMLRPCServer.SimpleXMLRPCServer): # Register the signal receiver with the core client.register_client(str(self.port)) - + self.listening_thread = threading.Thread(target=self.handle_thread) - + gobject.timeout_add(50, self.handle_signals) - + try: self.listening_thread.start() except Exception, e: log.debug("Thread: %s", e) - + def handle_thread(self): try: while not self._shutdown: @@ -130,11 +130,11 @@ class SignalReceiver(ThreadingMixIn, SimpleXMLRPCServer.SimpleXMLRPCServer): self._shutdown = False except Exception, e: log.debug("handle_thread: %s", e) - + def get_port(self): """Get the port that the SignalReceiver is listening on""" return self.port - + def emit_signal(self, signal, *data): """Exported method used by the core to emit a signal to the client""" self.emitted_signals.append((signal, data)) @@ -145,13 +145,13 @@ class SignalReceiver(ThreadingMixIn, SimpleXMLRPCServer.SimpleXMLRPCServer): try: for callback in self.signals[signal]: gobject.idle_add(callback, *data) - + except Exception, e: log.warning("Unable to call callback for signal %s: %s", signal, e) self.emitted_signals = [] return True - + def connect_to_signal(self, signal, callback): """Connect to a signal""" try: diff --git a/deluge/ui/ui.py b/deluge/ui/ui.py index 006f63af4..874929cbd 100644 --- a/deluge/ui/ui.py +++ b/deluge/ui/ui.py @@ -2,19 +2,19 @@ # ui.py # # Copyright (C) 2007 Andrew Resch ('andar') -# +# # Deluge is free software. -# +# # You may redistribute it and/or modify it under the terms of the # GNU General Public License, as published by the Free Software # Foundation; either version 3 of the License, or (at your option) # any later version. -# +# # deluge is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License # along with deluge. If not, write to: # The Free Software Foundation, Inc., @@ -45,17 +45,17 @@ class UI: # Set the config directory deluge.configmanager.set_config_dir(options.config) - + config = deluge.configmanager.ConfigManager("ui.conf", DEFAULT_PREFS) - + if not options.ui: selected_ui = config["default_ui"] else: selected_ui = options.ui - + config.save() del config - + if selected_ui == "gtk": log.info("Starting GtkUI..") from deluge.ui.gtkui.gtkui import GtkUI diff --git a/deluge/ui/webui/lib/newforms_portable/models.py b/deluge/ui/webui/lib/newforms_portable/models.py index 0590839b2..a005908a7 100644 --- a/deluge/ui/webui/lib/newforms_portable/models.py +++ b/deluge/ui/webui/lib/newforms_portable/models.py @@ -305,7 +305,7 @@ class ModelChoiceField(ChoiceField): help_text=None, *args, **kwargs): self.empty_label = empty_label self.cache_choices = cache_choices - + # Call Field instead of ChoiceField __init__() because we don't need # ChoiceField.__init__(). Field.__init__(self, required, widget, label, initial, help_text, @@ -321,8 +321,8 @@ class ModelChoiceField(ChoiceField): queryset = property(_get_queryset, _set_queryset) - # this method will be used to create object labels by the QuerySetIterator. - # Override it to customize the label. + # this method will be used to create object labels by the QuerySetIterator. + # Override it to customize the label. def label_from_instance(self, obj): """ This method is used to convert objects into strings; it's used to @@ -330,7 +330,7 @@ class ModelChoiceField(ChoiceField): can override this method to customize the display of the choices. """ return smart_unicode(obj) - + def _get_choices(self): # If self._choices is set, then somebody must have manually set # the property self.choices. In this case, just return self._choices. diff --git a/deluge/ui/webui/lib/webpy022/__init__.py b/deluge/ui/webui/lib/webpy022/__init__.py index 25e03d137..bc8721a0b 100644 --- a/deluge/ui/webui/lib/webpy022/__init__.py +++ b/deluge/ui/webui/lib/webpy022/__init__.py @@ -32,7 +32,7 @@ except ImportError: def main(): import doctest - + doctest.testmod(utils) doctest.testmod(db) doctest.testmod(net) @@ -40,21 +40,21 @@ def main(): doctest.testmod(http) doctest.testmod(webapi) doctest.testmod(request) - + try: doctest.testmod(cheetah) except NameError: pass - + template.test() - + import sys urls = ('/web.py', 'source') class source: def GET(self): header('Content-Type', 'text/python') print open(sys.argv[0]).read() - + if listget(sys.argv, 1) != 'test': run(urls, locals()) diff --git a/deluge/ui/webui/lib/webpy022/cheetah.py b/deluge/ui/webui/lib/webpy022/cheetah.py index db9fbf305..6e99d412e 100644 --- a/deluge/ui/webui/lib/webpy022/cheetah.py +++ b/deluge/ui/webui/lib/webpy022/cheetah.py @@ -20,22 +20,22 @@ def upvars(level=2): r_include = re_compile(r'(?!\\)#include \"(.*?)\"($|#)', re.M) def __compiletemplate(template, base=None, isString=False): - if isString: + if isString: text = template - else: + else: text = open('templates/'+template).read() # implement #include at compile-time def do_include(match): text = open('templates/'+match.groups()[0]).read() return text - while r_include.findall(text): + while r_include.findall(text): text = r_include.sub(do_include, text) execspace = _compiletemplate.bases.copy() tmpl_compiler = Compiler(source=text, mainClassName='GenTemplate') tmpl_compiler.addImportedVarNames(execspace.keys()) exec str(tmpl_compiler) in execspace - if base: + if base: _compiletemplate.bases[base] = execspace['GenTemplate'] return execspace['GenTemplate'] @@ -43,17 +43,17 @@ def __compiletemplate(template, base=None, isString=False): _compiletemplate = memoize(__compiletemplate) _compiletemplate.bases = {} -def render(template, terms=None, asTemplate=False, base=None, +def render(template, terms=None, asTemplate=False, base=None, isString=False): """ Renders a template, caching where it can. - + `template` is the name of a file containing the a template in - the `templates/` folder, unless `isString`, in which case it's the + the `templates/` folder, unless `isString`, in which case it's the template itself. `terms` is a dictionary used to fill the template. If it's None, then - the caller's local variables are used instead, plus context, if it's not + the caller's local variables are used instead, plus context, if it's not already set, is set to `context`. If asTemplate is False, it `output`s the template directly. Otherwise, @@ -69,7 +69,7 @@ def render(template, terms=None, asTemplate=False, base=None, if isinstance(terms, list): new = {} old = upvars() - for k in terms: + for k in terms: new[k] = old[k] terms = new # default: grab all locals @@ -77,22 +77,22 @@ def render(template, terms=None, asTemplate=False, base=None, terms = {'context': ctx, 'ctx':ctx} terms.update(sys._getframe(1).f_locals) # terms=d means use d as the searchList - if not isinstance(terms, tuple): + if not isinstance(terms, tuple): terms = (terms,) - - if 'headers' in ctx and not isString and template.endswith('.html'): + + if 'headers' in ctx and not isString and template.endswith('.html'): header('Content-Type','text/html; charset=utf-8', unique=True) - + if loadhooks.has_key('reloader'): compiled_tmpl = __compiletemplate(template, base=base, isString=isString) else: compiled_tmpl = _compiletemplate(template, base=base, isString=isString) compiled_tmpl = compiled_tmpl(searchList=terms, filter=WebSafe) - if asTemplate: + if asTemplate: return compiled_tmpl - else: + else: return output(str(compiled_tmpl)) class WebSafe(Filter): - def filter(self, val, **keywords): + def filter(self, val, **keywords): return websafe(val) diff --git a/deluge/ui/webui/lib/webpy022/db.py b/deluge/ui/webui/lib/webpy022/db.py index 2438a162f..89d48b3f2 100644 --- a/deluge/ui/webui/lib/webpy022/db.py +++ b/deluge/ui/webui/lib/webpy022/db.py @@ -12,7 +12,7 @@ __all__ = [ "sqllist", "sqlors", "aparam", "reparam", "SQLQuery", "sqlquote", "SQLLiteral", "sqlliteral", - "connect", + "connect", "TransactionError", "transaction", "transact", "commit", "rollback", "query", "select", "insert", "update", "delete" @@ -45,17 +45,17 @@ def _interpolate(format): Takes a format string and returns a list of 2-tuples of the form (boolean, string) where boolean says whether string should be evaled or not. - + from (public domain, Ka-Ping Yee) """ from tokenize import tokenprog - + def matchorfail(text, pos): match = tokenprog.match(text, pos) if match is None: raise _ItplError(text, pos) return match, match.end() - + namechars = "abcdefghijklmnopqrstuvwxyz" \ "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; chunks = [] @@ -63,7 +63,7 @@ def _interpolate(format): while 1: dollar = format.find("$", pos) - if dollar < 0: + if dollar < 0: break nextchar = format[dollar + 1] @@ -74,9 +74,9 @@ def _interpolate(format): match, pos = matchorfail(format, pos) tstart, tend = match.regs[3] token = format[tstart:tend] - if token == "{": + if token == "{": level = level + 1 - elif token == "}": + elif token == "}": level = level - 1 chunks.append((1, format[dollar + 2:pos - 1])) @@ -93,11 +93,11 @@ def _interpolate(format): match, pos = matchorfail(format, pos) tstart, tend = match.regs[3] token = format[tstart:tend] - if token[0] in "([": + if token[0] in "([": level = level + 1 - elif token[0] in ")]": + elif token[0] in ")]": level = level - 1 - else: + else: break chunks.append((1, format[dollar + 1:pos])) @@ -105,14 +105,14 @@ def _interpolate(format): chunks.append((0, format[pos:dollar + 1])) pos = dollar + 1 + (nextchar == "$") - if pos < len(format): + if pos < len(format): chunks.append((0, format[pos:])) return chunks class UnknownParamstyle(Exception): """ raised for unsupported db paramstyles - + (currently supported: qmark, numeric, format, pyformat) """ pass @@ -122,7 +122,7 @@ def aparam(): Returns the appropriate string to be used to interpolate a value with the current `web.ctx.db_module` or simply %s if there isn't one. - + >>> aparam() '%s' """ @@ -130,12 +130,12 @@ def aparam(): style = web.ctx.db_module.paramstyle else: style = 'pyformat' - - if style == 'qmark': + + if style == 'qmark': return '?' - elif style == 'numeric': + elif style == 'numeric': return ':1' - elif style in ['format', 'pyformat']: + elif style in ['format', 'pyformat']: return '%s' raise UnknownParamstyle, style @@ -143,7 +143,7 @@ def reparam(string_, dictionary): """ Takes a string and a dictionary and interpolates the string using values from the dictionary. Returns an `SQLQuery` for the result. - + >>> reparam("s = $s", dict(s=True)) """ @@ -159,7 +159,7 @@ def reparam(string_, dictionary): def sqlify(obj): """ converts `obj` to its proper SQL version - + >>> sqlify(None) 'NULL' >>> sqlify(True) @@ -167,10 +167,10 @@ def sqlify(obj): >>> sqlify(3) '3' """ - + # because `1 == True and hash(1) == hash(True)` # we have to do this the hard way... - + if obj is None: return 'NULL' elif obj is True: @@ -191,7 +191,7 @@ class SQLQuery: # tested in sqlquote's docstring def __init__(self, s='', v=()): self.s, self.v = str(s), tuple(v) - + def __getitem__(self, key): # for backwards-compatibility return [self.s, self.v][key] @@ -209,13 +209,13 @@ class SQLQuery: return self else: return NotImplemented - + def __str__(self): try: return self.s % tuple([sqlify(x) for x in self.v]) except (ValueError, TypeError): return self.s - + def __repr__(self): return '' % repr(str(self)) @@ -226,10 +226,10 @@ class SQLLiteral: >>> insert('foo', time=SQLLiteral('NOW()'), _test=True) """ - def __init__(self, v): + def __init__(self, v): self.v = v - def __repr__(self): + def __repr__(self): return self.v sqlliteral = SQLLiteral @@ -237,7 +237,7 @@ sqlliteral = SQLLiteral def sqlquote(a): """ Ensures `a` is quoted properly for use in a SQL query. - + >>> 'WHERE x = ' + sqlquote(True) + ' AND y = ' + sqlquote(3) """ @@ -249,19 +249,19 @@ class UnknownDB(Exception): def connect(dbn, **keywords): """ - Connects to the specified database. - - `dbn` currently must be "postgres", "mysql", or "sqlite". - + Connects to the specified database. + + `dbn` currently must be "postgres", "mysql", or "sqlite". + If DBUtils is installed, connection pooling will be used. """ - if dbn == "postgres": - try: + if dbn == "postgres": + try: import psycopg2 as db - except ImportError: - try: + except ImportError: + try: import psycopg as db - except ImportError: + except ImportError: import pgdb as db if 'pw' in keywords: keywords['password'] = keywords['pw'] @@ -289,7 +289,7 @@ def connect(dbn, **keywords): web.config._hasPooling = False keywords['database'] = keywords['db'] del keywords['db'] - + elif dbn == "firebird": import kinterbasdb as db if 'pw' in keywords: @@ -298,7 +298,7 @@ def connect(dbn, **keywords): keywords['database'] = keywords['db'] del keywords['db'] - else: + else: raise UnknownDB, dbn web.ctx.db_name = dbn @@ -313,12 +313,12 @@ def connect(dbn, **keywords): return PooledDB.PooledDB(dbapi=db, **keywords) else: return PooledDB.PooledDB(creator=db, **keywords) - + def db_cursor(): if isinstance(web.ctx.db, dict): keywords = web.ctx.db if web.config._hasPooling: - if 'db' not in globals(): + if 'db' not in globals(): globals()['db'] = _PooledDB(db, keywords) web.ctx.db = globals()['db'].connection() else: @@ -327,12 +327,12 @@ def connect(dbn, **keywords): web.ctx.db_cursor = db_cursor web.ctx.dbq_count = 0 - + def db_execute(cur, sql_query, dorollback=True): """executes an sql query""" web.ctx.dbq_count += 1 - + try: a = time.time() out = cur.execute(sql_query.s, sql_query.v) @@ -372,31 +372,31 @@ def transact(): """Start a transaction.""" if not web.ctx.db_transaction: # commit everything up to now, so we don't rollback it later - if hasattr(web.ctx.db, 'commit'): + if hasattr(web.ctx.db, 'commit'): web.ctx.db.commit() else: db_cursor = web.ctx.db_cursor() - web.ctx.db_execute(db_cursor, + web.ctx.db_execute(db_cursor, SQLQuery("SAVEPOINT webpy_sp_%s" % web.ctx.db_transaction)) web.ctx.db_transaction += 1 def commit(): """Commits a transaction.""" web.ctx.db_transaction -= 1 - if web.ctx.db_transaction < 0: + if web.ctx.db_transaction < 0: raise TransactionError, "not in a transaction" if not web.ctx.db_transaction: - if hasattr(web.ctx.db, 'commit'): + if hasattr(web.ctx.db, 'commit'): web.ctx.db.commit() else: db_cursor = web.ctx.db_cursor() - web.ctx.db_execute(db_cursor, + web.ctx.db_execute(db_cursor, SQLQuery("RELEASE SAVEPOINT webpy_sp_%s" % web.ctx.db_transaction)) def rollback(care=True): """Rolls back a transaction.""" - web.ctx.db_transaction -= 1 + web.ctx.db_transaction -= 1 if web.ctx.db_transaction < 0: web.db_transaction = 0 if care: @@ -405,7 +405,7 @@ def rollback(care=True): return if not web.ctx.db_transaction: - if hasattr(web.ctx.db, 'rollback'): + if hasattr(web.ctx.db, 'rollback'): web.ctx.db.rollback() else: db_cursor = web.ctx.db_cursor() @@ -416,9 +416,9 @@ def rollback(care=True): def query(sql_query, vars=None, processed=False, _test=False): """ Execute SQL query `sql_query` using dictionary `vars` to interpolate it. - If `processed=True`, `vars` is a `reparam`-style list to use + If `processed=True`, `vars` is a `reparam`-style list to use instead of interpolating. - + >>> query("SELECT * FROM foo", _test=True) >>> query("SELECT * FROM foo WHERE x = $x", vars=dict(x='f'), _test=True) @@ -427,15 +427,15 @@ def query(sql_query, vars=None, processed=False, _test=False): """ if vars is None: vars = {} - + if not processed and not isinstance(sql_query, SQLQuery): sql_query = reparam(sql_query, vars) - + if _test: return sql_query - + db_cursor = web.ctx.db_cursor() web.ctx.db_execute(db_cursor, sql_query) - + if db_cursor.description: names = [x[0] for x in db_cursor.description] def iterwrapper(): @@ -450,28 +450,28 @@ def query(sql_query, vars=None, processed=False, _test=False): for x in db_cursor.fetchall()] else: out = db_cursor.rowcount - + if not web.ctx.db_transaction: web.ctx.db.commit() return out def sqllist(lst): """ Converts the arguments for use in something like a WHERE clause. - + >>> sqllist(['a', 'b']) 'a, b' >>> sqllist('a') 'a' - + """ - if isinstance(lst, str): + if isinstance(lst, str): return lst else: return ', '.join(lst) def sqlors(left, lst): """ - `left is a SQL clause like `tablename.arg = ` + `left is a SQL clause like `tablename.arg = ` and `lst` is a list of values. Returns a reparam-style pair featuring the SQL that ORs together the clause for each item in the lst. @@ -490,11 +490,11 @@ def sqlors(left, lst): ln = len(lst) if ln == 0: return SQLQuery("2+2=5", []) - if ln == 1: + if ln == 1: lst = lst[0] if isinstance(lst, iters): - return SQLQuery('(' + left + + return SQLQuery('(' + left + (' OR ' + left).join([aparam() for param in lst]) + ")", lst) else: return SQLQuery(left + aparam(), [lst]) @@ -502,24 +502,24 @@ def sqlors(left, lst): def sqlwhere(dictionary, grouping=' AND '): """ Converts a `dictionary` to an SQL WHERE clause `SQLQuery`. - + >>> sqlwhere({'cust_id': 2, 'order_id':3}) >>> sqlwhere({'cust_id': 2, 'order_id':3}, grouping=', ') """ - + return SQLQuery(grouping.join([ '%s = %s' % (k, aparam()) for k in dictionary.keys() ]), dictionary.values()) -def select(tables, vars=None, what='*', where=None, order=None, group=None, +def select(tables, vars=None, what='*', where=None, order=None, group=None, limit=None, offset=None, _test=False): """ - Selects `what` from `tables` with clauses `where`, `order`, - `group`, `limit`, and `offset`. Uses vars to interpolate. + Selects `what` from `tables` with clauses `where`, `order`, + `group`, `limit`, and `offset`. Uses vars to interpolate. Otherwise, each clause can be a SQLQuery. - + >>> select('foo', _test=True) >>> select(['foo', 'bar'], where="foo.bar_id = bar.id", limit=5, _test=True) @@ -527,7 +527,7 @@ def select(tables, vars=None, what='*', where=None, order=None, group=None, """ if vars is None: vars = {} qout = "" - + def gen_clause(sql, val): if isinstance(val, (int, long)): if sql == 'WHERE': @@ -540,14 +540,14 @@ def select(tables, vars=None, what='*', where=None, order=None, group=None, nout = val elif val: nout = reparam(val, vars) - else: + else: return "" out = "" - if qout: out += " " + if qout: out += " " out += sql + " " + nout return out - + if web.ctx.get('db_name') == "firebird": for (sql, val) in ( ('FIRST', limit), @@ -588,11 +588,11 @@ def insert(tablename, seqname=None, _test=False, **values): Inserts `values` into `tablename`. Returns current sequence ID. Set `seqname` to the ID if it's not the default, or to `False` if there isn't one. - + >>> insert('foo', joe='bob', a=2, _test=True) """ - + if values: sql_query = SQLQuery("INSERT INTO %s (%s) VALUES (%s)" % ( tablename, @@ -603,12 +603,12 @@ def insert(tablename, seqname=None, _test=False, **values): sql_query = SQLQuery("INSERT INTO %s DEFAULT VALUES" % tablename) if _test: return sql_query - + db_cursor = web.ctx.db_cursor() - if seqname is False: + if seqname is False: pass - elif web.ctx.db_name == "postgres": - if seqname is None: + elif web.ctx.db_name == "postgres": + if seqname is None: seqname = tablename + "_id_seq" sql_query += "; SELECT currval('%s')" % seqname elif web.ctx.db_name == "mysql": @@ -618,13 +618,13 @@ def insert(tablename, seqname=None, _test=False, **values): web.ctx.db_execute(db_cursor, sql_query) # not really the same... sql_query = SQLQuery("SELECT last_insert_rowid()") - + web.ctx.db_execute(db_cursor, sql_query) - try: + try: out = db_cursor.fetchone()[0] - except Exception: + except Exception: out = None - + if not web.ctx.db_transaction: web.ctx.db.commit() return out @@ -633,14 +633,14 @@ def update(tables, where, vars=None, _test=False, **values): """ Update `tables` with clause `where` (interpolated using `vars`) and setting `values`. - + >>> joe = 'Joseph' >>> update('foo', where='name = $joe', name='bob', age=5, ... vars=locals(), _test=True) """ if vars is None: vars = {} - + if isinstance(where, (int, long)): where = "id = " + sqlquote(where) elif isinstance(where, (list, tuple)) and len(where) == 2: @@ -649,24 +649,24 @@ def update(tables, where, vars=None, _test=False, **values): pass else: where = reparam(where, vars) - + query = ( - "UPDATE " + sqllist(tables) + - " SET " + sqlwhere(values, ', ') + + "UPDATE " + sqllist(tables) + + " SET " + sqlwhere(values, ', ') + " WHERE " + where) - + if _test: return query - + db_cursor = web.ctx.db_cursor() web.ctx.db_execute(db_cursor, query) - + if not web.ctx.db_transaction: web.ctx.db.commit() return db_cursor.rowcount def delete(table, where=None, using=None, vars=None, _test=False): """ Deletes from `table` with clauses `where` and `using`. - + >>> name = 'Joe' >>> delete('foo', where='name = $name', vars=locals(), _test=True) @@ -689,9 +689,9 @@ def delete(table, where=None, using=None, vars=None, _test=False): q += ' WHERE ' + where if using and web.ctx.get('db_name') != "firebird": q += ' USING ' + sqllist(using) - + if _test: return q - + db_cursor = web.ctx.db_cursor() web.ctx.db_execute(db_cursor, q) diff --git a/deluge/ui/webui/lib/webpy022/debugerror.py b/deluge/ui/webui/lib/webpy022/debugerror.py index 1de465a81..c275b249c 100644 --- a/deluge/ui/webui/lib/webpy022/debugerror.py +++ b/deluge/ui/webui/lib/webpy022/debugerror.py @@ -2,7 +2,7 @@ pretty debug errors (part of web.py) -adapted from Django +adapted from Django Copyright (c) 2005, the Lawrence Journal-World Used under the modified BSD license: http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5 @@ -37,11 +37,11 @@ $def with (exception_type, exception_value, frames) h2 span { font-size:80%; color:#666; font-weight:normal; } h3 { margin:1em 0 .5em 0; } h4 { margin:0 0 .5em 0; font-weight: normal; } - table { + table { border:1px solid #ccc; border-collapse: collapse; background:white; } tbody td, tbody th { vertical-align:top; padding:2px 3px; } - thead th { - padding:1px 6px 1px 3px; background:#fefefe; text-align:left; + thead th { + padding:1px 6px 1px 3px; background:#fefefe; text-align:left; font-weight:normal; font-size:11px; border:1px solid #ddd; } tbody th { text-align:right; color:#666; padding-right:.5em; } table.vars { margin:5px 0 2px 40px; } @@ -49,14 +49,14 @@ $def with (exception_type, exception_value, frames) table td.code { width:100%;} table td.code div { overflow:hidden; } table.source th { color:#666; } - table.source td { + table.source td { font-family:monospace; white-space:pre; border-bottom:1px solid #eee; } ul.traceback { list-style-type:none; } ul.traceback li.frame { margin-bottom:1em; } div.context { margin: 10px 0; } - div.context ol { + div.context ol { padding-left:30px; margin:0 10px; list-style-position: inside; } - div.context ol li { + div.context ol li { font-family:monospace; white-space:pre; color:#666; cursor:pointer; } div.context ol.context-line li { color:black; background-color:#ccc; } div.context ol.context-line li span { float: right; } @@ -78,7 +78,7 @@ $def with (exception_type, exception_value, frames)