diff --git a/deluge/core/core.py b/deluge/core/core.py index 562da3c7c..5a3c26906 100644 --- a/deluge/core/core.py +++ b/deluge/core/core.py @@ -74,7 +74,7 @@ DEFAULT_PREFS = { "max_upload_slots_global": -1, "max_connections_per_torrent": -1, "max_upload_slots_per_torrent": -1, - "enabled_plugins": ["Queue"] + "enabled_plugins": [] } class Core( diff --git a/deluge/pluginmanagerbase.py b/deluge/pluginmanagerbase.py index bce76b70b..ae41be976 100644 --- a/deluge/pluginmanagerbase.py +++ b/deluge/pluginmanagerbase.py @@ -65,7 +65,7 @@ class PluginManagerBase: def shutdown(self): log.debug("PluginManager shutting down..") for plugin in self.plugins.values(): - plugin.core.shutdown() + plugin.disable() del self.plugins def __getitem__(self, key): @@ -113,9 +113,11 @@ class PluginManagerBase: def disable_plugin(self, name): """Disables a plugin""" - try: - del self.plugins[name] - except: - log.warning("Unable to disable non-existant plugin %s", name) + + self.plugins[name].disable() + + del self.plugins[name] +# except: + # log.warning("Unable to disable non-existant plugin %s", name) log.info("Plugin %s disabled..", name) diff --git a/deluge/ui/client.py b/deluge/ui/client.py index c84a864e6..29f5af92c 100644 --- a/deluge/ui/client.py +++ b/deluge/ui/client.py @@ -33,6 +33,7 @@ import os.path import pickle +import socket import deluge.xmlrpclib as xmlrpclib @@ -48,17 +49,32 @@ class CoreProxy: self._uri = None self._core = None self._on_new_core_callbacks = [] - + self._on_no_core_callbacks = [] + def connect_on_new_core(self, callback): """Connect a callback to be called when a new core is connected to.""" self._on_new_core_callbacks.append(callback) - + + def connect_on_no_core(self, callback): + """Connect a callback to be called when the current core is disconnected + from.""" + self._on_no_core_callbacks.append(callback) + def set_core_uri(self, uri): log.info("Setting core uri as %s", uri) self._uri = uri - # Get a new core - self.get_core() - + if self._uri == None: + for callback in self._on_no_core_callbacks: + callback() + self._core = None + else: + # Get a new core + self.get_core() + + def get_core_uri(self): + """Returns the URI of the core currently being used.""" + return self._uri + def get_core(self): if self._core is None and self._uri is not None: log.debug("Creating ServerProxy..") @@ -88,13 +104,24 @@ def connect_on_new_core(callback): """Connect a callback whenever a new core is connected to.""" return _core.connect_on_new_core(callback) +def connect_on_no_core(callback): + """Connect a callback whenever the core is disconnected from.""" + return _core.connect_on_no_core(callback) + def set_core_uri(uri): """Sets the core uri""" return _core.set_core_uri(uri) - + +def get_core_uri(): + """Get the core URI""" + return _core.get_core_uri() + def shutdown(): """Shutdown the core daemon""" - get_core().shutdown() + try: + get_core().shutdown() + except (AttributeError, socket.error): + set_core_uri(None) def add_torrent_file(torrent_files): """Adds torrent files to the core @@ -112,8 +139,12 @@ def add_torrent_file(torrent_files): (path, filename) = os.path.split(torrent_file) fdump = xmlrpclib.Binary(f.read()) f.close() - result = get_core().add_torrent_file(filename, str(), fdump) - + try: + result = get_core().add_torrent_file(filename, str(), fdump) + except (AttributeError, socket.error): + set_core_uri(None) + result = False + if result is False: # The torrent was not added successfully. log.warning("Torrent %s was not added successfully.", filename) @@ -122,7 +153,12 @@ def add_torrent_url(torrent_url): """Adds torrents to the core via url""" from deluge.common import is_url if is_url(torrent_url): - result = get_core().add_torrent_url(torrent_url, str()) + try: + result = get_core().add_torrent_url(torrent_url, str()) + except (AttributeError, socket.error): + set_core_uri(None) + result = False + if result is False: # The torrent url was not added successfully. log.warning("Torrent %s was not added successfully.", torrent_url) @@ -132,62 +168,125 @@ def add_torrent_url(torrent_url): def remove_torrent(torrent_ids): """Removes torrent_ids from the core.. Expects a list of torrent_ids""" log.debug("Attempting to removing torrents: %s", torrent_ids) - for torrent_id in torrent_ids: - get_core().remove_torrent(torrent_id) - + try: + for torrent_id in torrent_ids: + get_core().remove_torrent(torrent_id) + except (AttributeError, socket.error): + set_core_uri(None) + def pause_torrent(torrent_ids): """Pauses torrent_ids""" - for torrent_id in torrent_ids: - get_core().pause_torrent(torrent_id) + try: + for torrent_id in torrent_ids: + get_core().pause_torrent(torrent_id) + except (AttributeError, socket.error): + set_core_uri(None) def resume_torrent(torrent_ids): """Resume torrent_ids""" - for torrent_id in torrent_ids: - get_core().resume_torrent(torrent_id) + try: + for torrent_id in torrent_ids: + get_core().resume_torrent(torrent_id) + except (AttributeError, socket.error): + set_core_uri(None) def force_reannounce(torrent_ids): """Reannounce to trackers""" - for torrent_id in torrent_ids: - get_core().force_reannounce(torrent_id) + try: + for torrent_id in torrent_ids: + get_core().force_reannounce(torrent_id) + except (AttributeError, socket.error): + set_core_uri(None) def get_torrent_status(torrent_id, keys): """Builds the status dictionary and returns it""" - status = get_core().get_torrent_status(torrent_id, keys) + try: + status = get_core().get_torrent_status(torrent_id, keys) + except (AttributeError, socket.error): + set_core_uri(None) + return {} return pickle.loads(status.data) def get_session_state(): - return get_core().get_session_state() - - + try: + state = get_core().get_session_state() + except (AttributeError, socket.error): + set_core_uri(None) + state = [] + return state + def get_config(): - return get_core().get_config() - + try: + config = get_core().get_config() + except (AttributeError, socket.error): + set_core_uri(None) + config = {} + return config + def get_config_value(key): - return get_core().get_config_value(key) + try: + config_value = get_core().get_config_value(key) + except (AttributeError, socket.error): + set_core_uri(None) + config_value = None + return config_value def set_config(config): if config == {}: return - get_core().set_config(config) + try: + get_core().set_config(config) + except (AttributeError, socket.error): + set_core_uri(None) def get_listen_port(): - return int(get_core().get_listen_port()) + try: + port = get_core().get_listen_port() + except (AttributeError, socket.error): + set_core_uri(None) + port = 0 + return int(port) def get_available_plugins(): - return get_core().get_available_plugins() + try: + available = get_core().get_available_plugins() + except (AttributeError, socket.error): + set_core_uri(None) + available = [] + return available def get_enabled_plugins(): - return get_core().get_enabled_plugins() - + try: + enabled = get_core().get_enabled_plugins() + except (AttributeError, socket.error): + set_core_uri(None) + enabled = [] + return enabled + def get_download_rate(): - return get_core().get_download_rate() - + try: + rate = get_core().get_download_rate() + except (AttributeError, socket.error): + set_core_uri(None) + rate = -1 + return rate + def get_upload_rate(): - return get_core().get_upload_rate() + try: + rate = get_core().get_upload_rate() + except (AttributeError, socket.error): + set_core_uri(None) + rate = -1 + return rate def get_num_connections(): - return get_core().get_num_connections() - + try: + num_connections = get_core().get_num_connections() + except (AttributeError, socket.error): + set_core_uri(None) + num_connections = 0 + return num_connections + def open_url_in_browser(url): """Opens link in the desktop's default browser""" def start_browser(): diff --git a/deluge/ui/component.py b/deluge/ui/component.py new file mode 100644 index 000000000..434c83391 --- /dev/null +++ b/deluge/ui/component.py @@ -0,0 +1,131 @@ +# +# component.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 2 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., +# 51 Franklin Street, Fifth Floor +# Boston, MA 02110-1301, USA. +# +# In addition, as a special exception, the copyright holders give +# permission to link the code of portions of this program with the OpenSSL +# library. +# You must obey the GNU General Public License in all respects for all of +# the code used other than OpenSSL. If you modify file(s) with this +# exception, you may extend this exception to your version of the file(s), +# but you are not obligated to do so. If you do not wish to do so, delete +# this exception statement from your version. If you delete this exception +# statement from all source files in the program, then also delete it here. + +import gobject +from deluge.log import LOG as log + +COMPONENT_STATE = [ + "Stopped", + "Started" +] + +class Component: + def __init__(self, name): + # Register with the ComponentRegistry + register(name, self) + 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") + + def stop(self): + pass + + def _stop(self): + self._state = COMPONENT_STATE.index("Stopped") + + def update(self): + pass + + +class ComponentRegistry: + def __init__(self): + self.components = {} + #self.component_state = {} + self.update_timer = None + + def register(self, name, obj): + """Registers a component""" + log.debug("Registered %s with ComponentRegistry..", name) + self.components[name] = obj + + def get(self, name): + """Returns a reference to the component 'name'""" + return self.components[name] + + def start(self, update_interval=1000): + """Starts all components""" + for component in self.components.keys(): + self.components[component].start() + self.components[component]._start() + + # Start the update timer + self.update_timer = gobject.timeout_add(update_interval, self.update) + # Do an update right away + self.update() + + def stop(self): + """Stops all components""" + for component in self.components.keys(): + self.components[component].stop() + self.components[component]._stop() + # Stop the update timer + gobject.source_remove(self.update_timer) + + def update(self): + """Updates all components""" + for component in self.components.keys(): + # Only update the component if it's started + if self.components[component].get_state() == \ + COMPONENT_STATE.index("Started"): + self.components[component].update() + + return True + +_ComponentRegistry = ComponentRegistry() + +def register(name, obj): + """Registers a UI component with the registry""" + _ComponentRegistry.register(name, obj) + +def start(): + """Starts all components""" + _ComponentRegistry.start() + +def stop(): + """Stops all components""" + _ComponentRegistry.stop() + +def update(): + """Updates all components""" + _ComponentRegistry.update() + +def get(component): + """Return a reference to the component""" + return _ComponentRegistry.get(component) diff --git a/deluge/ui/gtkui/connectionmanager.py b/deluge/ui/gtkui/connectionmanager.py index 9fe9e4f31..33a87e379 100644 --- a/deluge/ui/gtkui/connectionmanager.py +++ b/deluge/ui/gtkui/connectionmanager.py @@ -36,6 +36,7 @@ import pkg_resources import gobject import socket +import deluge.ui.component as component import deluge.xmlrpclib as xmlrpclib import deluge.common import deluge.ui.client as client @@ -46,14 +47,24 @@ DEFAULT_CONFIG = { "hosts": ["localhost:58846"] } -class ConnectionManager: - def __init__(self, window): +HOSTLIST_COL_PIXBUF = 0 +HOSTLIST_COL_URI = 1 +HOSTLIST_COL_STATUS = 2 + +HOSTLIST_STATUS = [ + "Offline", + "Online", + "Connected" +] +class ConnectionManager(component.Component): + def __init__(self): + component.Component.__init__(self, "ConnectionManager") # Get the glade file for the connection manager self.glade = gtk.glade.XML( pkg_resources.resource_filename("deluge.ui.gtkui", "glade/connection_manager.glade")) - self.window = window + self.window = component.get("MainWindow") self.config = ConfigManager("hostlist.conf", DEFAULT_CONFIG) self.connection_manager = self.glade.get_widget("connection_manager") self.hostlist = self.glade.get_widget("hostlist") @@ -62,20 +73,21 @@ class ConnectionManager: self.glade.get_widget("image1").set_from_pixbuf( deluge.common.get_logo(32)) - self.liststore = gtk.ListStore(gtk.gdk.Pixbuf, str) + self.liststore = gtk.ListStore(gtk.gdk.Pixbuf, str, int) # Fill in hosts from config file for host in self.config["hosts"]: row = self.liststore.append() - self.liststore.set_value(row, 1, host) + self.liststore.set_value(row, HOSTLIST_COL_URI, host) # Setup host list treeview self.hostlist.set_model(self.liststore) render = gtk.CellRendererPixbuf() - column = gtk.TreeViewColumn("Status", render, pixbuf=0) + column = gtk.TreeViewColumn( + "Status", render, pixbuf=HOSTLIST_COL_PIXBUF) self.hostlist.append_column(column) render = gtk.CellRendererText() - column = gtk.TreeViewColumn("Host", render, text=1) + column = gtk.TreeViewColumn("Host", render, text=HOSTLIST_COL_URI) self.hostlist.append_column(column) self.glade.signal_autoconnect({ @@ -83,60 +95,126 @@ class ConnectionManager: "on_button_removehost_clicked": self.on_button_removehost_clicked, "on_button_startdaemon_clicked": \ self.on_button_startdaemon_clicked, - "on_button_cancel_clicked": self.on_button_cancel_clicked, + "on_button_close_clicked": self.on_button_close_clicked, "on_button_connect_clicked": self.on_button_connect_clicked, }) self.connection_manager.connect("delete-event", self.on_delete_event) + # Connect to the 'changed' event of TreeViewSelection to get selection + # changes. + self.hostlist.get_selection().connect("changed", + self.on_selection_changed) def show(self): - self.update_timer = gobject.timeout_add(5000, self.update) - self.update() + self._update_timer = gobject.timeout_add(5000, self._update) + self._update() self.connection_manager.show_all() def hide(self): self.connection_manager.hide() - gobject.source_remove(self.update_timer) + gobject.source_remove(self._update_timer) - def update(self): + def _update(self): """Updates the host status""" def update_row(model=None, path=None, row=None, columns=None): - uri = model.get_value(row, 1) + uri = model.get_value(row, HOSTLIST_COL_URI) uri = "http://" + uri - online = True - host = None - try: - host = xmlrpclib.ServerProxy(uri) - host.ping() - except socket.error: - print "socket.error!" - online = False + online = self.test_online_status(uri) - print "online: ", online - del host - if online: image = gtk.STOCK_YES + online = HOSTLIST_STATUS.index("Online") else: image = gtk.STOCK_NO - + online = HOSTLIST_STATUS.index("Offline") + + if uri == current_uri: + # We are connected to this host, so lets display the connected + # icon. + image = gtk.STOCK_CONNECT + online = HOSTLIST_STATUS.index("Connected") + pixbuf = self.connection_manager.render_icon( image, gtk.ICON_SIZE_MENU) - model.set_value(row, 0, pixbuf) - - self.liststore.foreach(update_row) - return True + model.set_value(row, HOSTLIST_COL_PIXBUF, pixbuf) + model.set_value(row, HOSTLIST_COL_STATUS, online) + current_uri = client.get_core_uri() + self.liststore.foreach(update_row) + # Update the buttons + self.update_buttons() + return True + + def update_buttons(self): + """Updates the buttons based on selection""" + # Get the selected row's URI + paths = self.hostlist.get_selection().get_selected_rows()[1] + # If nothing is selected, just return + if len(paths) < 1: + return + row = self.liststore.get_iter(paths[0]) + uri = self.liststore.get_value(row, HOSTLIST_COL_URI) + status = self.liststore.get_value(row, HOSTLIST_COL_STATUS) + + # Check to see if a localhost is selected + localhost = False + if uri.split(":")[0] == "localhost" or uri.split(":")[0] == "127.0.0.1": + localhost = True + + # Make actual URI string + uri = "http://" + uri + + # See if this is the currently connected URI + if status == HOSTLIST_STATUS.index("Connected"): + # Display a disconnect button if we're connected to this host + self.glade.get_widget("button_connect").set_label("gtk-disconnect") + else: + self.glade.get_widget("button_connect").set_label("gtk-connect") + + # Update the start daemon button if the selected host is localhost + if localhost: + # Check to see if the host is online + if status == HOSTLIST_STATUS.index("Connected") \ + or status == HOSTLIST_STATUS.index("Online"): + self.glade.get_widget("image_startdaemon").set_from_stock( + gtk.STOCK_STOP, gtk.ICON_SIZE_MENU) + self.glade.get_widget("label_startdaemon").set_text( + "_Stop local daemon") + else: + # The localhost is not online + self.glade.get_widget("image_startdaemon").set_from_stock( + gtk.STOCK_EXECUTE, gtk.ICON_SIZE_MENU) + self.glade.get_widget("label_startdaemon").set_text( + "_Start local daemon") + # Make sure label is displayed correctly using mnemonics + self.glade.get_widget("label_startdaemon").set_use_underline( + True) + def save(self): """Save the current host list to file""" def append_row(model=None, path=None, row=None, columns=None): - hostlist.append(model.get_value(row, 1)) + hostlist.append(model.get_value(row, HOSTLIST_COL_URI)) hostlist = [] self.liststore.foreach(append_row, hostlist) self.config["hosts"] = hostlist self.config.save() + + def test_online_status(self, uri): + """Tests the status of URI.. Returns True or False depending on status. + """ + online = True + host = None + try: + host = xmlrpclib.ServerProxy(uri) + host.ping() + except socket.error: + online = False + + del host + + return online ## Callbacks def on_delete_event(self, widget, event): @@ -165,9 +243,11 @@ class ConnectionManager: port = port_spinbutton.get_value_as_int() hostname = hostname + ":" + str(port) row = self.liststore.append() - self.liststore.set_value(row, 1, hostname) + self.liststore.set_value(row, HOSTLIST_COL_URI, hostname) # Save the host list to file self.save() + # Update the status of the hosts + self._update() dialog.hide() @@ -184,16 +264,36 @@ class ConnectionManager: def on_button_startdaemon_clicked(self, widget): log.debug("on_button_startdaemon_clicked") - def on_button_cancel_clicked(self, widget): - log.debug("on_button_cancel_clicked") + def on_button_close_clicked(self, widget): + log.debug("on_button_close_clicked") self.hide() def on_button_connect_clicked(self, widget): log.debug("on_button_connect_clicked") paths = self.hostlist.get_selection().get_selected_rows()[1] row = self.liststore.get_iter(paths[0]) - uri = self.liststore.get_value(row, 1) + status = self.liststore.get_value(row, HOSTLIST_COL_STATUS) + uri = self.liststore.get_value(row, HOSTLIST_COL_URI) uri = "http://" + uri + if status == HOSTLIST_STATUS.index("Connected"): + # If we are connected to this host, then we will disconnect. + client.set_core_uri(None) + self._update() + return + + # Test the host to see if it is online or not. We don't use the status + # column information because it can be up to 5 seconds out of sync. + if not self.test_online_status(uri): + log.warning("Host does not appear to be online..") + # Update the list to show proper status + self._update() + return + + # Status is OK, so lets change to this host client.set_core_uri(uri) self.window.start() self.hide() + + def on_selection_changed(self, treeselection): + log.debug("on_selection_changed") + self.update_buttons() diff --git a/deluge/ui/gtkui/glade/connection_manager.glade b/deluge/ui/gtkui/glade/connection_manager.glade index ba85aee3b..8eae805ab 100644 --- a/deluge/ui/gtkui/glade/connection_manager.glade +++ b/deluge/ui/gtkui/glade/connection_manager.glade @@ -1,6 +1,6 @@ - + True @@ -99,7 +99,6 @@ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-add True - 0 @@ -111,7 +110,6 @@ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-remove True - 0 @@ -130,7 +128,6 @@ True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 0 @@ -138,14 +135,14 @@ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 2 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-execute - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK _Start local daemon @@ -193,7 +190,6 @@ True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK checkbutton - 0 True @@ -221,15 +217,14 @@ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK GTK_BUTTONBOX_END - + True True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - gtk-cancel + gtk-close True - 0 - + @@ -240,7 +235,6 @@ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-connect True - 0 @@ -344,7 +338,6 @@ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-cancel True - 0 diff --git a/deluge/ui/gtkui/gtkui.py b/deluge/ui/gtkui/gtkui.py index 8d761072d..79509b56d 100644 --- a/deluge/ui/gtkui/gtkui.py +++ b/deluge/ui/gtkui/gtkui.py @@ -38,7 +38,17 @@ import gettext import locale import pkg_resources +import deluge.ui.component as component +import deluge.ui.client as client from mainwindow import MainWindow +from menubar import MenuBar +from toolbar import ToolBar +from torrentview import TorrentView +from torrentdetails import TorrentDetails +from preferences import Preferences +from systemtray import SystemTray +from statusbar import StatusBar +from connectionmanager import ConnectionManager from signals import Signals from pluginmanager import PluginManager from deluge.configmanager import ConfigManager @@ -67,7 +77,7 @@ DEFAULT_PREFS = { "window_pane_position": -1, "tray_download_speed_list" : [5.0, 10.0, 30.0, 80.0, 300.0], "tray_upload_speed_list" : [5.0, 10.0, 30.0, 80.0, 300.0], - "enabled_plugins": ["Queue"] + "enabled_plugins": [] } class GtkUI: @@ -88,19 +98,31 @@ class GtkUI: # Make sure gtkui.conf has at least the defaults set config = ConfigManager("gtkui.conf", DEFAULT_PREFS) - - # Initialize the main window - self.mainwindow = MainWindow() + # We make sure that the UI components start once we get a core URI + client.connect_on_new_core(component.start) + client.connect_on_no_core(component.stop) + + # Initialize various components of the gtkui + self.mainwindow = MainWindow() + self.menubar = MenuBar() + self.toolbar = ToolBar() + self.torrentview = TorrentView() + self.torrentdetails = TorrentDetails() + self.preferences = Preferences() + self.systemtray = SystemTray() + self.statusbar = StatusBar() + self.connectionmanager = ConnectionManager() + # Start the signal receiver #self.signal_receiver = Signals(self) # Initalize the plugins self.plugins = PluginManager(self) - # Start the mainwindow and show it - #self.mainwindow.start() - + # Show the connection manager + self.connectionmanager.show() + # Start the gtk main loop gtk.gdk.threads_init() gtk.main() @@ -113,6 +135,12 @@ class GtkUI: # Clean-up del self.mainwindow + del self.systemtray + del self.menubar + del self.toolbar + del self.torrentview + del self.torrentdetails + del self.preferences # del self.signal_receiver del self.plugins del deluge.configmanager diff --git a/deluge/ui/gtkui/mainwindow.py b/deluge/ui/gtkui/mainwindow.py index 5d4d14316..df95d0df1 100644 --- a/deluge/ui/gtkui/mainwindow.py +++ b/deluge/ui/gtkui/mainwindow.py @@ -38,21 +38,16 @@ import gobject import pkg_resources import deluge.ui.client as client +import deluge.ui.component as component from deluge.configmanager import ConfigManager -from menubar import MenuBar -from toolbar import ToolBar -from torrentview import TorrentView -from torrentdetails import TorrentDetails -from preferences import Preferences -from systemtray import SystemTray -from statusbar import StatusBar -from connectionmanager import ConnectionManager + import deluge.common from deluge.log import LOG as log -class MainWindow: +class MainWindow(component.Component): def __init__(self): + component.Component.__init__(self, "MainWindow") self.config = ConfigManager("gtkui.conf") # Get the glade file for the main window self.main_glade = gtk.glade.XML( @@ -74,38 +69,13 @@ class MainWindow: self.window.connect("configure-event", self.on_window_configure_event) self.window.connect("delete-event", self.on_window_delete_event) self.vpaned.connect("notify::position", self.on_vpaned_position_event) - - # Initialize various components of the gtkui - self.menubar = MenuBar(self) - self.toolbar = ToolBar(self) - self.torrentview = TorrentView(self) - self.torrentdetails = TorrentDetails(self) - self.preferences = Preferences(self) - self.systemtray = SystemTray(self) - self.statusbar = StatusBar(self) - self.connectionmanager = ConnectionManager(self) - client.connect_on_new_core(self.start) + if not(self.config["start_in_tray"] and \ self.config["enable_system_tray"]) and not \ self.window.get_property("visible"): log.debug("Showing window") self.show() - self.connectionmanager.show() - - def start(self): - """Start the update thread and show the window""" - self.torrentview.start() - self.update_timer = gobject.timeout_add(1000, self.update) - - def update(self): - # Don't update the UI if the the window is minimized. - if self.is_minimized == True: - return True - self.torrentview.update() - self.torrentdetails.update() - self.statusbar.update() - return True - + def show(self): # Load the state prior to showing self.load_window_state() @@ -126,17 +96,6 @@ class MainWindow: return self.window.get_property("visible") def quit(self): - # Stop the update timer from running - try: - gobject.source_remove(self.update_timer) - except: - pass - del self.systemtray - del self.menubar - del self.toolbar - del self.torrentview - del self.torrentdetails - del self.preferences del self.config self.hide() gtk.main_quit() diff --git a/deluge/ui/gtkui/menubar.py b/deluge/ui/gtkui/menubar.py index 721228f3e..b50543784 100644 --- a/deluge/ui/gtkui/menubar.py +++ b/deluge/ui/gtkui/menubar.py @@ -36,14 +36,16 @@ pygtk.require('2.0') import gtk, gtk.glade import pkg_resources +import deluge.ui.component as component import deluge.ui.client as client from deluge.log import LOG as log -class MenuBar: - def __init__(self, window): +class MenuBar(component.Component): + def __init__(self): log.debug("MenuBar init..") - self.window = window + component.Component.__init__(self, "MenuBar") + self.window = component.get("MainWindow") # Get the torrent menu from the glade file torrentmenu_glade = gtk.glade.XML( pkg_resources.resource_filename("deluge.ui.gtkui", @@ -124,27 +126,27 @@ class MenuBar: ## Edit Menu ## def on_menuitem_preferences_activate(self, data=None): log.debug("on_menuitem_preferences_activate") - self.window.preferences.show() + component.get("Preferences").show() def on_menuitem_connectionmanager_activate(self, data=None): log.debug("on_menuitem_connectionmanager_activate") - self.window.connectionmanager.show() + component.get("ConnectionManager").show() ## Torrent Menu ## def on_menuitem_pause_activate(self, data=None): log.debug("on_menuitem_pause_activate") client.pause_torrent( - self.window.torrentview.get_selected_torrents()) + component.get("TorrentView").get_selected_torrents()) def on_menuitem_resume_activate(self, data=None): log.debug("on_menuitem_resume_activate") client.resume_torrent( - self.window.torrentview.get_selected_torrents()) + component.get("TorrentView").get_selected_torrents()) def on_menuitem_updatetracker_activate(self, data=None): log.debug("on_menuitem_updatetracker_activate") client.force_reannounce( - self.window.torrentview.get_selected_torrents()) + component.get("TorrentView").get_selected_torrents()) def on_menuitem_edittrackers_activate(self, data=None): log.debug("on_menuitem_edittrackers_activate") @@ -152,7 +154,7 @@ class MenuBar: def on_menuitem_remove_activate(self, data=None): log.debug("on_menuitem_remove_activate") client.remove_torrent( - self.window.torrentview.get_selected_torrents()) + component.get("TorrentView").get_selected_torrents()) ## View Menu ## def on_menuitem_toolbar_toggled(self, data=None): diff --git a/deluge/ui/gtkui/pluginmanager.py b/deluge/ui/gtkui/pluginmanager.py index 57217d415..990beb598 100644 --- a/deluge/ui/gtkui/pluginmanager.py +++ b/deluge/ui/gtkui/pluginmanager.py @@ -31,6 +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. +import deluge.ui.component as component import deluge.pluginmanagerbase import deluge.ui.client as client from deluge.configmanager import ConfigManager @@ -58,20 +59,25 @@ class PluginManager(deluge.pluginmanagerbase.PluginManagerBase): def get_torrentview(self): """Returns a reference to the torrentview component""" - return self._gtkui.mainwindow.torrentview + #return self._gtkui.mainwindow.torrentview + return component.get("TorrentView") def get_toolbar(self): """Returns a reference to the toolbar component""" - return self._gtkui.mainwindow.toolbar +# return self._gtkui.mainwindow.toolbar + return component.get("ToolBar") def get_menubar(self): """Returns a reference to the menubar component""" - return self._gtkui.mainwindow.menubar + # return self._gtkui.mainwindow.menubar + return component.get("MenuBar") def get_torrentmenu(self): """Returns a reference to the torrentmenu component""" - return self._gtkui.mainwindow.menubar.torrentmenu +# return self._gtkui.mainwindow.menubar.torrentmenu + return component.get("MenuBar").torrentmenu def get_selected_torrents(self): """Returns a list of the selected torrent_ids""" - return self._gtkui.mainwindow.torrentview.get_selected_torrents() +# return self._gtkui.mainwindow.torrentview.get_selected_torrents() + return component.get("TorrentView").get_selected_torrents() diff --git a/deluge/ui/gtkui/preferences.py b/deluge/ui/gtkui/preferences.py index e78178243..cc39c8719 100644 --- a/deluge/ui/gtkui/preferences.py +++ b/deluge/ui/gtkui/preferences.py @@ -36,14 +36,16 @@ pygtk.require('2.0') import gtk, gtk.glade import pkg_resources +import deluge.ui.component as component from deluge.log import LOG as log import deluge.ui.client as client import deluge.common from deluge.configmanager import ConfigManager -class Preferences: - def __init__(self, window): - self.window = window +class Preferences(component.Component): + def __init__(self): + component.Component.__init__(self, "Preferences") + self.window = component.get("MainWindow") self.glade = gtk.glade.XML( pkg_resources.resource_filename("deluge.ui.gtkui", "glade/preferences_dialog.glade")) @@ -395,6 +397,10 @@ class Preferences: name = self.plugin_liststore.get_value(row, 0) value = self.plugin_liststore.get_value(row, 1) self.plugin_liststore.set_value(row, 1, not value) + if not value: + functions.enable_plugin(name) + else: + functions.disable_plugin(name) def on_plugin_selection_changed(self, treeselection): log.debug("on_plugin_selection_changed") diff --git a/deluge/ui/gtkui/statusbar.py b/deluge/ui/gtkui/statusbar.py index 5d619bc8d..ffe36710d 100644 --- a/deluge/ui/gtkui/statusbar.py +++ b/deluge/ui/gtkui/statusbar.py @@ -33,12 +33,15 @@ import gtk +import deluge.ui.component as component import deluge.common import deluge.ui.client as client +from deluge.log import LOG as log -class StatusBar: - def __init__(self, window): - self.window = window +class StatusBar(component.Component): + def __init__(self): + component.Component.__init__(self, "StatusBar") + self.window = component.get("MainWindow") self.statusbar = self.window.main_glade.get_widget("statusbar") # Add a HBox to the statusbar after removing the initial label widget @@ -47,8 +50,13 @@ class StatusBar: frame = self.statusbar.get_children()[0] frame.remove(frame.get_children()[0]) frame.add(self.hbox) - + # Show the not connected status bar + self.show_not_connected() + + def start(self): + log.debug("StatusBar start..") # Add in images and labels + self.clear_statusbar() image = gtk.Image() image.set_from_stock(gtk.STOCK_NETWORK, gtk.ICON_SIZE_MENU) self.hbox.pack_start(image, expand=False, fill=False) @@ -65,12 +73,28 @@ class StatusBar: self.hbox.pack_start(image, expand=False, fill=False) self.label_upload_speed = gtk.Label() self.hbox.pack_start(self.label_upload_speed, - expand=False, fill=False) + expand=False, fill=False) - # Update once before showing -# self.update() self.statusbar.show_all() - + + def stop(self): + # When stopped, we just show the not connected thingy + self.show_not_connected() + + def show_not_connected(self): + self.clear_statusbar() + image = gtk.Image() + image.set_from_stock(gtk.STOCK_STOP, gtk.ICON_SIZE_MENU) + self.hbox.pack_start(image, expand=False, fill=False) + label = gtk.Label("Not connected to daemon..") + self.hbox.pack_start(label, expand=False, fill=False) + self.statusbar.show_all() + + def clear_statusbar(self): + def remove(child): + self.hbox.remove(child) + self.hbox.foreach(remove) + def update(self): # Set the max connections label max_connections = client.get_config_value("max_connections_global") @@ -101,3 +125,4 @@ class StatusBar: self.label_upload_speed.set_text("%s/s (%s)" % ( deluge.common.fsize(client.get_upload_rate()), max_upload_speed)) + diff --git a/deluge/ui/gtkui/systemtray.py b/deluge/ui/gtkui/systemtray.py index 3f85072be..8edeabfa8 100644 --- a/deluge/ui/gtkui/systemtray.py +++ b/deluge/ui/gtkui/systemtray.py @@ -34,14 +34,16 @@ import gtk import pkg_resources +import deluge.ui.component as component import deluge.ui.client as client import deluge.common from deluge.configmanager import ConfigManager from deluge.log import LOG as log -class SystemTray: - def __init__(self, window): - self.window = window +class SystemTray(component.Component): + def __init__(self): + component.Component.__init__(self, "SystemTray") + self.window = component.get("MainWindow") self.config = ConfigManager("gtkui.conf") self.config.register_set_function("enable_system_tray", self.on_enable_system_tray_set) @@ -79,6 +81,7 @@ class SystemTray: deluge.common.get_pixmap("seeding16.png")) def start(self): + log.debug("SystemTray start..") # Build the bandwidth speed limit menus self.build_tray_bwsetsubmenu() diff --git a/deluge/ui/gtkui/toolbar.py b/deluge/ui/gtkui/toolbar.py index f975fc9b0..e2cf8cd24 100644 --- a/deluge/ui/gtkui/toolbar.py +++ b/deluge/ui/gtkui/toolbar.py @@ -35,12 +35,14 @@ import pygtk pygtk.require('2.0') import gtk, gtk.glade +import deluge.ui.component as component from deluge.log import LOG as log -class ToolBar: - def __init__(self, window): +class ToolBar(component.Component): + def __init__(self): + component.Component.__init__(self, "ToolBar") log.debug("ToolBar Init..") - self.window = window + self.window = component.get("MainWindow") self.toolbar = self.window.main_glade.get_widget("toolbar") ### Connect Signals ### self.window.main_glade.signal_autoconnect({ @@ -94,34 +96,34 @@ class ToolBar: def on_toolbutton_add_clicked(self, data): log.debug("on_toolbutton_add_clicked") # Use the menubar's callback - self.window.menubar.on_menuitem_addtorrent_activate(data) + component.get("MenuBar").on_menuitem_addtorrent_activate(data) def on_toolbutton_remove_clicked(self, data): log.debug("on_toolbutton_remove_clicked") # Use the menubar's callbacks - self.window.menubar.on_menuitem_remove_activate(data) + component.get("MenuBar").on_menuitem_remove_activate(data) def on_toolbutton_clear_clicked(self, data): log.debug("on_toolbutton_clear_clicked") # Use the menubar's callbacks - self.window.menubar.on_menuitem_clear_activate(data) + component.get("MenuBar").on_menuitem_clear_activate(data) def on_toolbutton_pause_clicked(self, data): log.debug("on_toolbutton_pause_clicked") # Use the menubar's callbacks - self.window.menubar.on_menuitem_pause_activate(data) + 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 - self.window.menubar.on_menuitem_resume_activate(data) + component.get("MenuBar").on_menuitem_resume_activate(data) def on_toolbutton_preferences_clicked(self, data): log.debug("on_toolbutton_preferences_clicked") # Use the menubar's callbacks - self.window.menubar.on_menuitem_preferences_activate(data) + component.get("MenuBar").on_menuitem_preferences_activate(data) def on_toolbutton_plugins_clicked(self, data): log.debug("on_toolbutton_plugins_clicked") # Use the menubar's callbacks - self.window.menubar.on_menuitem_preferences_activate(data) + component.get("MenuBar").on_menuitem_preferences_activate(data) diff --git a/deluge/ui/gtkui/torrentdetails.py b/deluge/ui/gtkui/torrentdetails.py index df36673ec..46e352435 100644 --- a/deluge/ui/gtkui/torrentdetails.py +++ b/deluge/ui/gtkui/torrentdetails.py @@ -38,13 +38,15 @@ pygtk.require('2.0') import gtk, gtk.glade import gettext +import deluge.ui.component as component import deluge.ui.client as client import deluge.common from deluge.log import LOG as log -class TorrentDetails: - def __init__(self, window): - self.window = window +class TorrentDetails(component.Component): + def __init__(self): + component.Component.__init__(self, "TorrentDetails") + self.window = component.get("MainWindow") glade = self.window.main_glade self.notebook = glade.get_widget("torrent_info") @@ -71,12 +73,16 @@ class TorrentDetails: self.eta = glade.get_widget("summary_eta") self.torrent_path = glade.get_widget("summary_torrent_path") + def stop(self): + self.clear() + def update(self): # Only update if this page is showing if self.notebook.page_num(self.details_tab) is \ self.notebook.get_current_page(): # Get the first selected torrent - selected = self.window.torrentview.get_selected_torrents() + #selected = self.window.torrentview.get_selected_torrents() + selected = component.get("TorrentView").get_selected_torrents() # Only use the first torrent in the list or return if None selected if selected is not None: @@ -154,3 +160,4 @@ class TorrentDetails: self.tracker_status.set_text("") self.next_announce.set_text("") self.eta.set_text("") + self.torrent_path.set_text("") diff --git a/deluge/ui/gtkui/torrentview.py b/deluge/ui/gtkui/torrentview.py index f01649fff..f412b8f98 100644 --- a/deluge/ui/gtkui/torrentview.py +++ b/deluge/ui/gtkui/torrentview.py @@ -40,6 +40,7 @@ import gettext import gobject import deluge.common +import deluge.ui.component as component import deluge.ui.client as client from deluge.log import LOG as log import deluge.ui.gtkui.listview as listview @@ -96,10 +97,11 @@ def cell_data_progress(column, cell, model, row, data): textstr = textstr + " %.2f%%" % value cell.set_property("text", textstr) -class TorrentView(listview.ListView): +class TorrentView(listview.ListView, component.Component): """TorrentView handles the listing of torrents.""" - def __init__(self, window): - self.window = window + def __init__(self): + component.Component.__init__(self, "TorrentView") + self.window = component.get("MainWindow") # Call the ListView constructor listview.ListView.__init__(self, self.window.main_glade.get_widget("torrent_view")) @@ -170,7 +172,12 @@ class TorrentView(listview.ListView): session_state = client.get_session_state() for torrent_id in session_state: self.add_row(torrent_id) - + + def stop(self): + """Stops the torrentview""" + # We need to clear the liststore + self.liststore.clear() + def update(self, columns=None): """Update the view. If columns is not None, it will attempt to only update those columns selected. @@ -294,12 +301,12 @@ class TorrentView(listview.ListView): # We only care about right-clicks if event.button == 3: # Show the Torrent menu from the MenuBar - torrentmenu = self.window.menubar.torrentmenu + torrentmenu = component.get("MenuBar").torrentmenu torrentmenu.popup(None, None, None, event.button, event.time) def on_selection_changed(self, treeselection): """This callback is know when the selection has changed.""" log.debug("on_selection_changed") - self.window.torrentdetails.update() + component.get("TorrentDetails").update()