diff --git a/deluge/config.py b/deluge/config.py index 589abd4e3..8ec226f5c 100644 --- a/deluge/config.py +++ b/deluge/config.py @@ -114,6 +114,10 @@ class Config: log.warning("Key does not exist, returning None") return None + def get_config(self): + """Returns the entire configuration as a dictionary.""" + return self.config + def __getitem__(self, key): return self.config[key] diff --git a/deluge/core/core.py b/deluge/core/core.py index 75c4159ec..f5bda969d 100644 --- a/deluge/core/core.py +++ b/deluge/core/core.py @@ -52,7 +52,23 @@ DEFAULT_PREFS = { "download_location": deluge.common.get_default_download_dir(), "listen_ports": [6881, 6891], "torrentfiles_location": deluge.common.get_default_torrent_dir(), - "plugins_location": deluge.common.get_default_plugin_dir() + "plugins_location": deluge.common.get_default_plugin_dir(), + "prioritize_first_last_pieces": False, + "random_port": False, + "dht": False, + "upnp": False, + "natpmp": False, + "utpex": False, + "enc_in_policy": 1, + "enc_out_policy": 1, + "enc_level": 1, + "enc_prefer_rc4": True, + "max_connections_global": -1, + "max_upload_speed": -1.0, + "max_download_speed": -1.0, + "max_upload_slots_global": -1, + "max_connections_per_torrent": -1, + "max_upload_slots_per_torrent": -1 } class Core(dbus.service.Object): @@ -188,6 +204,32 @@ class Core(dbus.service.Object): """Save the current session state to file.""" # Have the TorrentManager save it's state self.torrents.save_state() + + @dbus.service.method(dbus_interface="org.deluge_torrent.Deluge", + in_signature="", + out_signature="ay") + def get_config(self): + """Get all the preferences as a dictionary""" + config = self.config.get_config() + config = pickle.dumps(config) + return config + + @dbus.service.method(dbus_interface="org.deluge_torrent.Deluge", + in_signature="ay") + def set_config(self, config): + """Set the config with values from dictionary""" + # Convert the byte array into the dictionary + config = "".join(chr(b) for b in config) + config = pickle.loads(config) + # Load all the values into the configuration + for key in config.keys(): + self.config[key] = config[key] + + @dbus.service.method(dbus_interface="org.deluge_torrent.Deluge", + out_signature="i") + def get_listen_port(self): + """Returns the active listen port""" + return self.session.listen_port() # Signals @dbus.service.signal(dbus_interface="org.deluge_torrent.Deluge", diff --git a/deluge/log.py b/deluge/log.py index 09324bda3..a126aace3 100644 --- a/deluge/log.py +++ b/deluge/log.py @@ -38,7 +38,7 @@ import logging # Setup the logger logging.basicConfig( level=logging.DEBUG, - format="[%(levelname)-8s] %(name)s:%(module)s:%(lineno)d %(message)s" + format="[%(levelname)-8s] %(module)s:%(lineno)d %(message)s" ) # Get the logger diff --git a/deluge/ui/functions.py b/deluge/ui/functions.py index 505000ece..6bcf4ac54 100644 --- a/deluge/ui/functions.py +++ b/deluge/ui/functions.py @@ -129,3 +129,25 @@ def get_session_state(core=None): # De-serialize the object state = pickle.loads(state) return state + +def get_config(core=None): + if core is None: + core = get_core() + config = core.get_config() + config = "".join(chr(b) for b in config) + config = pickle.loads(config) + return config + +def set_config(config, core=None): + if config == {}: + return + if core is None: + core = get_core() + config = pickle.dumps(config) + core.set_config(config) + +def get_listen_port(core=None): + if core is None: + core = get_core() + return int(core.get_listen_port()) + diff --git a/deluge/ui/gtkui/glade/preferences_dialog.glade b/deluge/ui/gtkui/glade/preferences_dialog.glade index b42d77d0e..7e436bcee 100644 --- a/deluge/ui/gtkui/glade/preferences_dialog.glade +++ b/deluge/ui/gtkui/glade/preferences_dialog.glade @@ -1,6 +1,6 @@ - + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK @@ -24,11 +24,17 @@ True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + True - True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - True + GTK_RESIZE_QUEUE + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + False @@ -120,6 +126,8 @@ 0 True True + radio_ask_save + False @@ -206,6 +214,7 @@ Use Compact Allocation 0 True + radio_full_allocation False @@ -385,9 +394,12 @@ True True + 5 1 0 0 65535 1 10 10 1 + True + True False @@ -411,9 +423,12 @@ True True + 5 1 0 0 65535 1 10 10 1 + True + True False @@ -756,7 +771,7 @@ Full Stream - + True True Prefer to encrypt the entire stream @@ -866,54 +881,60 @@ Full Stream 2 15 - + True - The maximum upload speed for all torrents. Set -1 for unlimited. - 0 - Maximum Upload Speed (KiB/s): - - - 2 - 3 - GTK_FILL - - - - - True - The maximum number of connections allowed. Set -1 for unlimited. - 0 - Maximum Connections: - - - GTK_FILL - - - - - True - The maximum upload speed for all torrents. Set -1 for unlimited. - 0 - Maximum Upload Slots: + True + The maximum upload slots for all torrents. Set -1 for unlimited. + 1 + -1 -1 9000 1 10 10 + 1 + True + True + 1 + 2 3 4 GTK_FILL - + True True - The maximum number of connections allowed. Set -1 for unlimited. + The maximum upload speed for all torrents. Set -1 for unlimited. 1 - -1 -1 1000 1 10 10 + -1 -1 9000 1 10 10 1 + 1 + True 1 2 + 2 + 3 + GTK_FILL + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + The maximum download speed for all torrents. Set -1 for unlimited. + 1 + -1 -1 9000 1 10 10 + 1 + 1 + True + + + 1 + 2 + 1 + 2 GTK_FILL @@ -931,57 +952,61 @@ Full Stream - + True True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - The maximum download speed for all torrents. Set -1 for unlimited. - 1 - 0 -1 9000 1 10 10 - 1 - - - 1 - 2 - 1 - 2 - GTK_FILL - - - - - True - True - The maximum upload speed for all torrents. Set -1 for unlimited. - 1 - 0 -1 9000 1 10 10 - 1 - - - 1 - 2 - 2 - 3 - GTK_FILL - - - - - True - True - The maximum upload slots for all torrents. Set -1 for unlimited. + The maximum number of connections allowed. Set -1 for unlimited. + 4 1 -1 -1 9000 1 10 10 1 + True + True + GTK_UPDATE_IF_VALID 1 2 + GTK_FILL + + + + + True + The maximum upload speed for all torrents. Set -1 for unlimited. + 0 + Maximum Upload Slots: + + 3 4 GTK_FILL + + + True + The maximum number of connections allowed. Set -1 for unlimited. + 0 + Maximum Connections: + + + GTK_FILL + + + + + True + The maximum upload speed for all torrents. Set -1 for unlimited. + 0 + Maximum Upload Speed (KiB/s): + + + 2 + 3 + GTK_FILL + + @@ -1025,18 +1050,40 @@ Full Stream 2 15 - + True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 0 - Maximum Upload Slots: + True + The maximum upload slots per torrent. Set -1 for unlimited. + 1 + -1 -1 9000 1 10 10 + 1 + True + True + 1 + 2 1 2 GTK_FILL + + + True + True + The maximum number of connections per torrent. Set -1 for unlimited. + 1 + -1 -1 9000 1 10 10 + True + True + + + 1 + 2 + GTK_FILL + + True @@ -1049,31 +1096,13 @@ Full Stream - + True - True - The maximum number of connections per torrent. Set -1 for unlimited. - 1 - -1 -1 1000 1 10 10 + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + Maximum Upload Slots: - 1 - 2 - GTK_FILL - - - - - True - True - The maximum upload slots per torrent. Set -1 for unlimited. - 1 - -1 -1 1000 1 10 10 - 1 - - - 1 - 2 1 2 GTK_FILL @@ -1322,14 +1351,31 @@ Full Stream 2 10 - + True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Open folder with: + 0 + True + True + + + GTK_FILL + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Custom: + 0 + True + True + radio_open_folder_stock - 1 - 2 1 2 GTK_FILL @@ -1359,35 +1405,19 @@ Thunar - + True True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - Custom: - 0 - True - True + 1 + 2 1 2 GTK_FILL - - - True - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - Open folder with: - 0 - True - True - - - GTK_FILL - - @@ -1485,7 +1515,7 @@ Thunar True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 0 - Help us improve Deluge by sending us your Python and PyGTK versions, OS and processor types. Absolutely no other information is sent. + Help us improve Deluge by sending us your Python version, PyGTK version, OS and processor types. Absolutely no other information is sent. True diff --git a/deluge/ui/gtkui/menubar.py b/deluge/ui/gtkui/menubar.py index 015e442f1..01c725c10 100644 --- a/deluge/ui/gtkui/menubar.py +++ b/deluge/ui/gtkui/menubar.py @@ -70,7 +70,6 @@ class MenuBar: ## Edit Menu "on_menuitem_preferences_activate": \ self.on_menuitem_preferences_activate, - "on_menuitem_plugins_activate": self.on_menuitem_plugins_activate, ## View Menu "on_menuitem_toolbar_toggled": self.on_menuitem_toolbar_toggled, @@ -120,9 +119,6 @@ class MenuBar: def on_menuitem_preferences_activate(self, data=None): log.debug("on_menuitem_preferences_activate") self.window.preferences.show() - - def on_menuitem_plugins_activate(self, data=None): - log.debug("on_menuitem_plugins_activate") ## Torrent Menu ## def on_menuitem_pause_activate(self, data=None): diff --git a/deluge/ui/gtkui/preferences.py b/deluge/ui/gtkui/preferences.py index 3b64b7ec3..355a68bfc 100644 --- a/deluge/ui/gtkui/preferences.py +++ b/deluge/ui/gtkui/preferences.py @@ -37,16 +37,18 @@ import gtk, gtk.glade import pkg_resources from deluge.log import LOG as log +import deluge.ui.functions as functions class Preferences: def __init__(self, window): self.window = window - self.pref_glade = gtk.glade.XML( + self.glade = gtk.glade.XML( pkg_resources.resource_filename("deluge.ui.gtkui", "glade/preferences_dialog.glade")) - self.pref_dialog = self.pref_glade.get_widget("pref_dialog") - self.treeview = self.pref_glade.get_widget("treeview") - self.notebook = self.pref_glade.get_widget("notebook") + self.pref_dialog = self.glade.get_widget("pref_dialog") + self.treeview = self.glade.get_widget("treeview") + self.notebook = self.glade.get_widget("notebook") + self.core = functions.get_core() # Setup the liststore for the categories (tab pages) self.liststore = gtk.ListStore(int, str) self.treeview.set_model(self.liststore) @@ -54,26 +56,166 @@ class Preferences: column = gtk.TreeViewColumn("Categories", render, text=1) self.treeview.append_column(column) # Add the default categories - self.liststore.append([0, "Downloads"]) - self.liststore.append([1, "Network"]) - self.liststore.append([2, "Bandwidth"]) - self.liststore.append([3, "Other"]) - self.liststore.append([4, "Plugins"]) - + i = 0 + for category in ["Downloads", "Network", "Bandwidth", "Other", + "Plugins"]: + self.liststore.append([i, category]) + i += 1 + # Connect to the 'changed' event of TreeViewSelection to get selection # changes. self.treeview.get_selection().connect("changed", self.on_selection_changed) - self.pref_glade.signal_autoconnect({ + self.glade.signal_autoconnect({ "on_pref_dialog_delete_event": self.on_pref_dialog_delete_event, "on_button_ok_clicked": self.on_button_ok_clicked, "on_button_apply_clicked": self.on_button_apply_clicked, - "on_button_cancel_clicked": self.on_button_cancel_clicked + "on_button_cancel_clicked": self.on_button_cancel_clicked, + "on_radio_save_all_to_toggled": self.on_toggle }) + + def add_page(self, name, widget): + """Add a another page to the notebook""" + index = self.notebook.append_page(widget) + self.liststore.append([index, name]) + + def get_config(self): + """Get the configuration from the core.""" + # Get the config dictionary from the core + self.config = functions.get_config(self.core) def show(self): + self.get_config() + # Update the preferences dialog to reflect current config settings + + ## Downloads tab ## + # FIXME: Add GtkUI specific prefs here + # Core specific options for Downloads tab + + # This one will need to be re-evaluated if the core is running on a + # different machine.. We won't be able to use the local file browser to + # choose a download location.. It will be specific to the machine core + # is running on. + self.glade.get_widget("download_path_button").set_filename( + self.config["download_location"]) + self.glade.get_widget("radio_compact_allocation").set_active( + self.config["compact_allocation"]) + self.glade.get_widget("radio_full_allocation").set_active( + not self.config["compact_allocation"]) + self.glade.get_widget("chk_prioritize_first_last_pieces").set_active( + self.config["prioritize_first_last_pieces"]) + + ## Network tab ## + self.glade.get_widget("spin_port_min").set_value( + self.config["listen_ports"][0]) + self.glade.get_widget("spin_port_max").set_value( + self.config["listen_ports"][1]) + self.glade.get_widget("active_port_label").set_text( + str(functions.get_listen_port(self.core))) + self.glade.get_widget("chk_random_port").set_active( + self.config["random_port"]) + self.glade.get_widget("chk_dht").set_active( + self.config["dht"]) + self.glade.get_widget("chk_upnp").set_active( + self.config["upnp"]) + self.glade.get_widget("chk_natpmp").set_active( + self.config["natpmp"]) + self.glade.get_widget("chk_utpex").set_active( + self.config["utpex"]) + self.glade.get_widget("combo_encin").set_active( + self.config["enc_in_policy"]) + self.glade.get_widget("combo_encout").set_active( + self.config["enc_out_policy"]) + self.glade.get_widget("combo_enclevel").set_active( + self.config["enc_level"]) + self.glade.get_widget("chk_pref_rc4").set_active( + self.config["enc_prefer_rc4"]) + + ## Bandwidth tab ## + self.glade.get_widget("spin_max_connections_global").set_value( + self.config["max_connections_global"]) + self.glade.get_widget("spin_max_download").set_value( + self.config["max_download_speed"]) + self.glade.get_widget("spin_max_upload").set_value( + self.config["max_upload_speed"]) + self.glade.get_widget("spin_max_upload_slots_global").set_value( + self.config["max_upload_slots_global"]) + self.glade.get_widget("spin_max_connections_per_torrent").set_value( + self.config["max_connections_per_torrent"]) + self.glade.get_widget("spin_max_upload_slots_per_torrent").set_value( + self.config["max_upload_slots_per_torrent"]) + + ## Other tab ## + # All of it is UI only. + + # Now show the dialog self.pref_dialog.show() + + def set_config(self): + """Sets all altered config values in the core""" + # Get the values from the dialog + new_config = {} + ## Downloads tab ## + new_config["download_location"] = \ + self.glade.get_widget("download_path_button").get_filename() + new_config["compact_allocation"] = \ + self.glade.get_widget("radio_compact_allocation").get_active() + new_config["prioritize_first_last_pieces"] = \ + self.glade.get_widget( + "chk_prioritize_first_last_pieces").get_active() + + ## Network tab ## + listen_ports = [] + listen_ports.append( + self.glade.get_widget("spin_port_min").get_value_as_int()) + listen_ports.append( + self.glade.get_widget("spin_port_max").get_value_as_int()) + new_config["listen_ports"] = listen_ports + new_config["random_port"] = \ + self.glade.get_widget("chk_random_port").get_active() + new_config["dht"] = self.glade.get_widget("chk_dht").get_active() + new_config["upnp"] = self.glade.get_widget("chk_upnp").get_active() + new_config["natpmp"] = self.glade.get_widget("chk_natpmp").get_active() + new_config["utpex"] = self.glade.get_widget("chk_utpex").get_active() + new_config["enc_in_policy"] = \ + self.glade.get_widget("combo_encin").get_active() + new_config["enc_out_policy"] = \ + self.glade.get_widget("combo_encout").get_active() + new_config["enc_level"] = \ + self.glade.get_widget("combo_enclevel").get_active() + new_config["enc_prefer_rc4"] = \ + self.glade.get_widget("chk_pref_rc4").get_active() + + ## Bandwidth tab ## + new_config["max_connections_global"] = \ + self.glade.get_widget( + "spin_max_connections_global").get_value_as_int() + new_config["max_download_speed"] = \ + self.glade.get_widget("spin_max_download").get_value() + new_config["max_upload_speed"] = \ + self.glade.get_widget("spin_max_upload").get_value() + new_config["max_upload_slots_global"] = \ + self.glade.get_widget( + "spin_max_upload_slots_global").get_value_as_int() + new_config["max_connections_per_torrent"] = \ + self.glade.get_widget( + "spin_max_connections_per_torrent").get_value_as_int() + new_config["max_upload_slots_per_torrent"] = \ + self.glade.get_widget( + "spin_max_upload_slots_per_torrent").get_value_as_int() + + config_to_set = {} + for key in new_config.keys(): + # The values do not match so this needs to be updated + if self.config[key] != new_config[key]: + config_to_set[key] = new_config[key] + + # Set each changed config value in the core + functions.set_config(config_to_set, self.core) + + # Update the configuration + self.config.update(config_to_set) def hide(self): self.pref_dialog.hide() @@ -82,14 +224,26 @@ class Preferences: self.hide() return True + def on_toggle(self, widget): + """Handles widget sensitivity based on radio/check button values.""" + value = widget.get_active() + if widget == self.glade.get_widget('radio_save_all_to'): + self.glade.get_widget('download_path_button').set_sensitive(value) + def on_button_ok_clicked(self, data): log.debug("on_button_ok_clicked") + self.set_config() + self.hide() + return True def on_button_apply_clicked(self, data): log.debug("on_button_apply_clicked") + self.set_config() def on_button_cancel_clicked(self, data): log.debug("on_button_cancel_clicked") + self.hide() + return True def on_selection_changed(self, treeselection): # Show the correct notebook page based on what row is selected.