From 99a5c07c8cfaa02d54c3b448fec1fb60f104eff2 Mon Sep 17 00:00:00 2001 From: Martijn Voncken Date: Sun, 20 Jul 2008 21:04:51 +0000 Subject: [PATCH] label plugin (clean label/build dir) --- deluge/plugins/label/TODO | 13 + deluge/plugins/label/label/core.py | 29 +- .../plugins/label/label/data/label_pref.glade | 450 ++++++++++++++++++ deluge/plugins/label/label/gtkui.py | 366 -------------- deluge/plugins/label/label/gtkui/__init__.py | 130 +++++ .../plugins/label/label/gtkui/label_config.py | 268 +++++++++++ .../{gtk_sidebar.py => gtkui/sidebar.py} | 77 ++- deluge/plugins/label/label/gtkui/submenu.py | 98 ++++ deluge/plugins/label/label/{ => gtkui}/ui.py | 0 .../label/{webui.py => webui/__init__.py} | 12 +- .../{webui_config.py => webui/config.py} | 2 + .../label/{webui_pages.py => webui/pages.py} | 0 deluge/plugins/label/label/webui/ui.py | 53 +++ deluge/plugins/label/setup.py | 4 +- 14 files changed, 1103 insertions(+), 399 deletions(-) create mode 100644 deluge/plugins/label/TODO create mode 100644 deluge/plugins/label/label/data/label_pref.glade delete mode 100644 deluge/plugins/label/label/gtkui.py create mode 100644 deluge/plugins/label/label/gtkui/__init__.py create mode 100644 deluge/plugins/label/label/gtkui/label_config.py rename deluge/plugins/label/label/{gtk_sidebar.py => gtkui/sidebar.py} (66%) create mode 100644 deluge/plugins/label/label/gtkui/submenu.py rename deluge/plugins/label/label/{ => gtkui}/ui.py (100%) rename deluge/plugins/label/label/{webui.py => webui/__init__.py} (91%) rename deluge/plugins/label/label/{webui_config.py => webui/config.py} (96%) rename deluge/plugins/label/label/{webui_pages.py => webui/pages.py} (100%) create mode 100644 deluge/plugins/label/label/webui/ui.py diff --git a/deluge/plugins/label/TODO b/deluge/plugins/label/TODO new file mode 100644 index 000000000..1724b1696 --- /dev/null +++ b/deluge/plugins/label/TODO @@ -0,0 +1,13 @@ +*grey bars in sidebar are ugly +*label sub-menu is broken on 1'st popup. +*replacing/restoring the sidebar model is a hack +*config should save a label on bottom ok-button, not a seperate save-button per label +*filters : add "Traffic" , use label-core for filtering ; needs hooks in torrentview. +*torrentview: bugs/hacks in adding and removing columns +*webui is functional but not polished. +*move_torrent_to is not implemeted +*no client-side validation (could be solved by a ui.aclient exception-plugin) + + + + diff --git a/deluge/plugins/label/label/core.py b/deluge/plugins/label/label/core.py index 6fefb0231..a4f751449 100644 --- a/deluge/plugins/label/label/core.py +++ b/deluge/plugins/label/label/core.py @@ -46,7 +46,7 @@ import re RE_VALID = re.compile("[a-z0-9_-]*\Z") RE_VALID = re.compile("[a-z0-9_-]*\Z") -KNOWN_STATES = ['Downloading','Seeding','Paused','Checking','Allocating','Queued','Error'] +KNOWN_STATES = ['Downloading','Seeding','Paused','Checking','Queued','Error'] STATE = "state" TRACKER = "tracker" KEYWORD = "keyword" @@ -58,7 +58,7 @@ CONFIG_DEFAULTS = { "gtk_alfa":False } OPTIONS_KEYS = ["max_download_speed", "max_upload_speed", - "max_connections", "max_upload_slots", "prioritize_first_last"] + "max_connections", "max_upload_slots", "prioritize_first_last","apply_max","move_completed_to"] NO_LABEL = "No Label" @@ -264,7 +264,7 @@ class Core(CorePluginBase): label_id = label_id.lower() CheckInput(RE_VALID.match(label_id) , _("Invalid label, valid characters:[a-z0-9_-]")) CheckInput(label_id, _("Empty Label")) - CheckInput(not (label_id in self.labels) , _("Unknown Label")) + CheckInput(not (label_id in self.labels) , _("Label already exists")) #default to current global per-torrent settings. @@ -273,7 +273,9 @@ class Core(CorePluginBase): "max_upload_speed":self.core_cfg.config["max_upload_speed_per_torrent"], "max_connections":self.core_cfg.config["max_connections_per_torrent"], "max_upload_slots":self.core_cfg.config["max_upload_slots_per_torrent"], - "prioritize_first_last":self.core_cfg.config["prioritize_first_last_pieces"] + "prioritize_first_last":self.core_cfg.config["prioritize_first_last_pieces"], + "apply_max":True, + "move_completed_to":None } def export_remove(self, label_id): @@ -291,12 +293,14 @@ class Core(CorePluginBase): "max_upload_speed":float(), "max_connections":int(), "max_upload_slots":int(), - "prioritize_first_last":bool(), + #"prioritize_first_last":bool(), + "apply_max":bool(), + "move_completed_to":string() or None } apply : applies download-options to all torrents currently labelled by label_id """ - CheckInput(not (label_id in self.labels) , _("Unknown Label")) + CheckInput(label_id in self.labels , _("Unknown Label")) for key in options_dict.keys(): if not key in OPTIONS_KEYS: raise Exception("label: Invalid options_dict key:%s" % key) @@ -339,12 +343,13 @@ class Core(CorePluginBase): self.torrent_labels[torrent_id] = label_id #set speeds, etc: options = self.labels[label_id] - torrent = self.torrents[torrent_id] - torrent.set_max_download_speed(options["max_download_speed"]) - torrent.set_max_upload_speed(options["max_upload_speed"]) - torrent.set_max_connections(options["max_connections"]) - torrent.set_max_upload_slots(options["max_upload_slots"]) - torrent.set_prioritize_first_last(options["prioritize_first_last"]) + if ("apply_max" in options) and options["apply_max"]: + torrent = self.torrents[torrent_id] + torrent.set_max_download_speed(options["max_download_speed"]) + torrent.set_max_upload_speed(options["max_upload_speed"]) + torrent.set_max_connections(options["max_connections"]) + torrent.set_max_upload_slots(options["max_upload_slots"]) + torrent.set_prioritize_first_last(options["prioritize_first_last"]) self.config.save() diff --git a/deluge/plugins/label/label/data/label_pref.glade b/deluge/plugins/label/label/data/label_pref.glade new file mode 100644 index 000000000..d4be23368 --- /dev/null +++ b/deluge/plugins/label/label/data/label_pref.glade @@ -0,0 +1,450 @@ + + + + + + + + True + 5 + + + True + 0 + GTK_SHADOW_NONE + + + True + 12 + + + True + 5 + + + True + 5 + + + True + + + False + False + + + + + True + True + Hide zero hits + 0 + True + + + 1 + + + + + False + False + + + + + + + + + True + 5 + <b>General</b> + True + + + label_item + + + + + False + False + + + + + True + 0 + GTK_SHADOW_NONE + + + True + True + 150 + + + True + + + True + 1 + 2 + + + True + True + True + add + 0 + + + 1 + 2 + GTK_EXPAND + + + + + 100 + True + True + + + + + + + 250 + True + True + True + + + 1 + + + + + True + True + True + Remove + 0 + + + False + 2 + + + + + False + False + + + + + True + + + 30 + True + <b>label</b> + True + + + + + True + 12 + 3 + 5 + 5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 50 + True + True + True + Apply to all + 0 + + + 1 + 3 + 5 + 6 + + + + + True + Select A Folder + + + 3 + 7 + 8 + GTK_EXPAND + + + + + True + True + -1 -1 9999 1 10 10 + + + 1 + 2 + 3 + 4 + GTK_EXPAND + GTK_EXPAND + + + + + True + True + Apply maximum limits on add: + 0 + True + + + 3 + + + + + True + True + Move completed to: + 0 + True + + + 3 + 6 + 7 + + + + + True + Kib/s + + + 2 + 3 + 2 + 3 + GTK_EXPAND | GTK_SHRINK | GTK_FILL + GTK_EXPAND + + + + + True + True + -1 -1 9999 1 10 10 + + + 1 + 2 + 2 + 3 + GTK_EXPAND + GTK_EXPAND + + + + + True + True + -1 -1 9999 1 10 10 + True + + + 1 + 2 + 1 + 2 + + GTK_EXPAND + + + + + True + 0 + Upload Slots: + + + 4 + 5 + GTK_FILL + + + + + + True + 0 + Upload Speed: + + + 3 + 4 + GTK_FILL + + + + + + True + 0 + Download Speed: + True + + + 2 + 3 + GTK_FILL + + + + + + True + 0 + Connections: + + + 1 + 2 + GTK_FILL + + + + + + True + Kib/s + + + 2 + 3 + 3 + 4 + GTK_EXPAND + + + + + True + True + -1 -1 9999 1 10 10 + True + + + 1 + 2 + 4 + 5 + GTK_EXPAND + GTK_EXPAND + + + + + True + True + True + Save Label Settings + 0 + + + 3 + 9 + 10 + + + + + 1 + + + + + + + + True + True + + + + + + + True + 5 + <b>Labels</b> + True + + + label_item + + + + + False + False + 1 + + + + + + + + + diff --git a/deluge/plugins/label/label/gtkui.py b/deluge/plugins/label/label/gtkui.py deleted file mode 100644 index eab8d8ce4..000000000 --- a/deluge/plugins/label/label/gtkui.py +++ /dev/null @@ -1,366 +0,0 @@ -# -# blocklist/gtkui.py -# -# 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 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 os -import pkg_resources # access plugin egg -from deluge.log import LOG as log -from deluge import component # for systray -import ui -import gtk, gobject -from deluge.ui.client import aclient -import gtk_sidebar - -from deluge.configmanager import ConfigManager -config = ConfigManager("label.conf") -GTK_ALFA = config.get("gtk_alfa") -NO_LABEL = "No Label" - - -def cell_data_label(column, cell, model, row, data): - cell.set_property('text', str(model.get_value(row, data))) - -def cell_data_tracker_host(column, cell, model, row, data): - cell.set_property('text', str(model.get_value(row, data))) - -class GtkUI(ui.UI): - def __init__(self, plugin_api, plugin_name): - log.debug("Calling UI init") - # Call UI constructor - ui.UI.__init__(self, plugin_api, plugin_name) - log.debug("Label GtkUI plugin initalized..") - - def enable(self): - self.plugin.register_hook("on_apply_prefs", self.apply_prefs) - self.load_interface() - - def disable(self): - #deluge.component.get("StatusBar").remove_item(self.blocklist_status) - self.plugin.deregister_hook("on_apply_prefs", self.apply_prefs) - self.plugin.remove_preferences_page("Label") - - def get_pixmap(self, fname): - """Returns a pixmap file included with plugin""" - return pkg_resources.resource_filename("blocklist", os.path.join("data", fname)) - - def unload_interface(self): - self.plugin.remove_preferences_page("Label") - - - def load_interface(self): - if not GTK_ALFA: - self.plugin.add_preferences_page("Label", gtk.Label( - "Sorry, the Gtk UI for the Label-plugin is still in development.")) - return - - log.debug("replace sidebar") - - try : - labelsidebar = gtk_sidebar.LabelSideBar() - #sidebar.hpaned.remove(sidebar.scrolled) - except Exception, e: - log.debug(e) - - log.debug("add items to torrentview-popup menu.") - - torrentmenu = component.get("MenuBar").torrentmenu - - self.label_menu = LabelMenu() - torrentmenu.append(self.label_menu) - self.label_menu.show_all() - - log.debug("add columns") - - component.get("TorrentView").add_func_column(_("Label"), - cell_data_label, - [str], - status_field=["label"]) - - component.get("TorrentView").add_func_column(_("Tracker"), - cell_data_tracker_host, - [str], - status_field=["tracker_host"]) - - component.get("TorrentView").create_model_filter() #todo:improve. - #TODO! - log.debug("Beginning gtk pane initialization") - self.config_frame = LabelConfigFrame() - - self.blocklist_pref_page = gtk.VBox() - self.blocklist_pref_page.set_spacing(6) - - self.blocklist_pref_page.pack_start(self.config_frame,True,True) - - # Add preferences page to preferences page - log.debug('Adding Label Preferences page') - self.plugin.add_preferences_page("Label", self.blocklist_pref_page) - - # Load settings from config and fill widgets with settings - self.fetch_prefs() - #log.debug('Finished loading Label preferences') - - - def fetch_prefs(self): # Fetch settings dictionary from plugin core and pass it to GTK ui settings - log.info('LABEL: Fetching and loading Preferences via GTK ui') - #aclient.block_list_get_options(self.callback_load_prefs) - self.config_frame.load_settings() - - def apply_prefs(self): - log.info('Blocklist: Preferences saved via Gtk ui') - """settings_dict = { - "url": self.url.get_text(), - "listtype": self.get_ltype(), - "check_after_days": self.check_after_days.get_value_as_int(), - "load_on_start":self.load_on_start.get_active(), - "try_times": self.try_times.get_value_as_int(), - "timeout": self.timeout.get_value_as_int() - } - aclient.block_list_set_options(None, settings_dict) - # Needs to go in another thread or wait until window is closed - #gobject.idle_add(self.call_critical_setting) - """ - - - # GTK Gui Callback functions - def callback_load_prefs(self, dict): - self.config_frame.load_settings() - log.info('Blocklist: Callback Load Prefs GTK ui') - """self.settings_url(dict['url']) - self.settings_listtype(dict['listtype']) - self.settings_load(dict['load_on_start']) - self.settings_check_after_days(dict['check_after_days']) - self.settings_timeout(dict['timeout']) - self.settings_try_times(dict['try_times']) - """ - -def cb_none(args): - "hack for empty callbacks." - pass - -""" -class LabelList(gtk.TreeView): - "a simple listbox is way too hard in gtk :(." - def __init__(self): - pass -""" - -class LabelMenu(gtk.MenuItem): - def __init__(self): - gtk.MenuItem.__init__(self, "Label") - self.show_all() - - #attach.. - torrentmenu = component.get("MenuBar").torrentmenu - torrentmenu.connect("show", self.on_show, None) - - aclient.connect_on_new_core(self._on_new_core) - - def _on_new_core(self, data): - self.on_show() - - def get_torrent_ids(self): - return component.get("TorrentView").get_selected_torrents() - - - def on_show(self, widget=None, data=None): - log.debug("label-on-show") - aclient.label_get_labels(self.cb_labels) - aclient.force_call(block=True) - - def cb_labels(self , labels): - log.debug("cb_labels-start") - self.sub_menu = gtk.Menu() - for label in [NO_LABEL] + labels: - item = gtk.MenuItem(label) - item.connect("activate", self.on_select_label, label) - self.sub_menu.append(item) - self.set_submenu(self.sub_menu) - self.show_all() - log.debug("cb_labels-end") - - def on_select_label(self, widget=None, label_id = None): - log.debug("select label:%s,%s" % (label_id ,self.get_torrent_ids()) ) - for torrent_id in self.get_torrent_ids(): - aclient.label_set_torrent(cb_none, torrent_id, label_id) - #aclient.force_call(block=True) - -class LabelConfigFrame(gtk.Frame): - def __init__(self): - gtk.Frame.__init__(self) - self.build_label_view() - self.build_label_options() - self.build_ui() - self.labels = [] - aclient.connect_on_new_core(self._on_new_core) - - def _on_new_core(self, data): - log.debug("NEW CORE") - self.load_settings() - - def load_settings(self ,widget=None ,data=None): - aclient.label_get_labels(self.cb_update_labels) - - def cb_update_labels(self, labels): - self.labels = labels - self.label_store.clear() - for label in labels: - self.label_store.append([label]) - - def on_add(self, widget, data=None): - label = self.txt_add.get_text() - self.txt_add.set_text("") - if label in self.labels: - return - aclient.label_add(cb_none, label) - aclient.label_get_labels(self.cb_update_labels) - self.select_label(label) - - #aclient.force_call(block=True) - - def on_remove(self, widget, data=None): - label = self.get_selected_label() - aclient.label_remove(cb_none, label) - aclient.label_get_labels(self.cb_update_labels) - self.select_label(0) - - def get_selected_label(self): - model , iter = self.label_view.get_selection().get_selected() - return self.label_store.get_value(iter,0) - - def select_label(self, label): - aclient.force_call(block=True) #sync.. - if label: - it = self.label_store.iter_nth_child(None,self.labels.index(label)) - else: - it = self.label_store.iter_nth_child(None,0) - self.label_view.get_selection().select_iter(it) - - def build_label_view(self): - "gtk should have a simple listbox widget..." - self.label_store = gtk.ListStore(str) - - column = gtk.TreeViewColumn(_("Label")) - renderer = gtk.CellRendererText() - column.pack_start(renderer) - column.set_attributes(renderer, text = 0) - - self.label_view = gtk.TreeView(self.label_store) - self.label_view.append_column(column) - self.label_view.set_headers_visible(False) - - #self.label_scroll = gtk.ScrolledWindow() - #self.label_scroll.add_with_viewport(self.label_view) - - - def build_label_options(self): - self.label_options = gtk.Label("Per label options") - - def build_ui(self): - #vbox - #general - #[x] stuff - #labels: - #hpaned : left-right - #right: listbox with labels - #left: label-options - #--- - #label-add - #-- - - label = gtk.Label() - label.set_markup('' + _('General') + '') - - - self.set_shadow_type(gtk.SHADOW_NONE) - self.set_label_widget(label) - - self.btn_load = gtk.Button("Load Settings") - self.btn_load.connect("clicked", self.load_settings, None) - - self.btn_remove = gtk.Button("Remove") - self.btn_remove.connect("clicked", self.on_remove, None) - - vb = gtk.VBox() - self.add(vb) - vb.add(self.btn_load) - vb.add(gtk.Label("Label is in developent, you're testing pre-alfa!!!")) - - - self.hide_zero_hits = gtk.CheckButton(_('Hide Zero Hits')) - vb.add(self.hide_zero_hits) - - label = gtk.Label() - label.set_markup('' + _('Labels') + '') - vb.add(label) - - hp = gtk.HPaned() - hp.add1(self.label_view) - - - hp.add2(self.label_options) - - hp.set_position(100) - - hp.set_size_request(400, 200) #bug.. - - - hbAdd = gtk.HBox() - hbAdd.add(gtk.Label("Label:")) - self.txt_add = gtk.Entry() - hbAdd.add(self.txt_add) - btn_add = gtk.Button("Add") - hbAdd.add(btn_add) - btn_add.connect("clicked", self.on_add, None) - - vb.pack_end(hbAdd) - - label = gtk.Label() - label.set_markup('' + _('Add') + '') - - - - vb.pack_end(label) - - vb.pack_end(self.btn_remove, True , True) - - vb.pack_end(hp,True , True) - - - - - - - - - - diff --git a/deluge/plugins/label/label/gtkui/__init__.py b/deluge/plugins/label/label/gtkui/__init__.py new file mode 100644 index 000000000..9e1a1004b --- /dev/null +++ b/deluge/plugins/label/label/gtkui/__init__.py @@ -0,0 +1,130 @@ +# +# blocklist/gtkui.py +# +# Copyright (C) 2008 Martijn Voncken +# +# 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 os +import pkg_resources # access plugin egg +from deluge.log import LOG as log +from deluge import component # for systray +import ui +import gtk, gobject +from deluge.ui.client import aclient + +import sidebar +import label_config +import submenu + +from deluge.configmanager import ConfigManager +config = ConfigManager("label.conf") +NO_LABEL = "No Label" + + +def cell_data_label(column, cell, model, row, data): + cell.set_property('text', str(model.get_value(row, data))) + +def cell_data_tracker_host(column, cell, model, row, data): + cell.set_property('text', str(model.get_value(row, data))) + +class GtkUI(ui.UI): + def __init__(self, plugin_api, plugin_name): + log.debug("Calling UI init") + # Call UI constructor + ui.UI.__init__(self, plugin_api, plugin_name) + log.debug("Label GtkUI plugin initalized..") + self.labelcfg = None + self.sidebar = None + + def enable(self): + self.load_interface() + + def disable(self): + self.labelcfg.unload() + try: + component.get("TorrentView").remove_column(_("Label")) + log.debug(1.1) + component.get("TorrentView").remove_column(_("Tracker")) + log.debug(1.2) + except Exception, e: + log.debug(e) #fix this! + log.debug(1.2) + self.sidebar.unload() + log.debug(2) + + + def get_pixmap(self, fname): + """Returns a pixmap file included with plugin""" + return pkg_resources.resource_filename("blocklist", os.path.join("data", fname)) + + + def load_interface(self): + #sidebar + log.debug("replace sidebar") + try : + if not self.sidebar: + self.sidebar = sidebar.LabelSideBar() + self.sidebar.load() + except Exception, e: + log.debug(e) + + #menu: + log.debug("add items to torrentview-popup menu.") + torrentmenu = component.get("MenuBar").torrentmenu + self.label_menu = submenu.LabelMenu() + torrentmenu.append(self.label_menu) + self.label_menu.show_all() + + #columns: + self.load_columns() + + #config: + if not self.labelcfg: + self.labelcfg = label_config.LabelConfig(self.plugin) + self.labelcfg.load() + + log.debug('Finished loading Label plugin') + + def load_columns(self): + log.debug("add columns") + + component.get("TorrentView").add_func_column(_("Label"), + cell_data_label, + [str], + status_field=["label"]) + + component.get("TorrentView").add_func_column(_("Tracker"), + cell_data_tracker_host, + [str], + status_field=["tracker_host"]) + + component.get("TorrentView").create_model_filter() #todo:improve. + + diff --git a/deluge/plugins/label/label/gtkui/label_config.py b/deluge/plugins/label/label/gtkui/label_config.py new file mode 100644 index 000000000..33ed868f5 --- /dev/null +++ b/deluge/plugins/label/label/gtkui/label_config.py @@ -0,0 +1,268 @@ +# +# Copyright (C) 2008 Martijn Voncken +# +# 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 gtk +import gtk.glade +import os +import pkg_resources # access plugin egg +import deluge.component as component +import deluge.common +from deluge.log import LOG as log +from deluge.ui.client import aclient + + + +class LabelConfig(object): + def __init__(self, plugin): + self.plugin = plugin + self.labels = [] + + def load(self): + #self.glade = gtk.glade.XML(self.get_resource("label_pref.glade")) + log.debug('Adding Label Preferences page') + + + + self.glade = gtk.glade.XML(self.get_resource("label_pref.glade")) + self.prefs_box = self.glade.get_widget("label_prefs_box") + self.label_view = self.glade.get_widget("label_view") + self.txt_add = self.glade.get_widget("txt_add") + self.btn_add = self.glade.get_widget("btn_add") + self.btn_remove = self.glade.get_widget("btn_remove") + self.label_view = self.glade.get_widget("label_view") + + + self.btn_remove.connect("clicked", self.on_remove, None) + self.btn_add.connect("clicked", self.on_add, None) + + self.build_label_view() + + self.plugin.add_preferences_page("Label", self.glade.get_widget("label_prefs_box")) + self.plugin.register_hook("on_show_prefs", self.load_settings) + self.plugin.register_hook("on_apply_prefs", self.on_apply_prefs) + + self.glade.get_widget("chk_move_completed").connect("clicked",self.on_chk_move_changed) + self.glade.get_widget("apply_max").connect("clicked",self.on_apply_max_changed) + self.glade.get_widget("btn_save").connect("clicked",self.on_save_label) + self.glade.get_widget("btn_apply").connect("clicked",self.on_apply_label) + + + self.load_settings() + + def unload(self): + self.plugin.remove_preferences_page("Label") + self.plugin.deregister_hook("on_show_prefs", self.load_settings) + + def get_resource(self, filename): + return pkg_resources.resource_filename("label", os.path.join("data", filename)) + + def load_settings(self ,widget = None , data = None): + aclient.label_get_labels(self.cb_update_labels) + aclient.label_get_global_options(self.cb_global_options) + + def cb_global_options(self, data): + self.glade.get_widget("hide_zero_hits").set_active(data["hide_zero_hits"]) + + def cb_update_labels(self, labels): + log.debug("update labels") + self.labels = labels + self.label_store.clear() + for label in labels: + self.label_store.append([label]) + self.label_view.get_selection().select_iter(self.label_store.get_iter_first()) + + def cb_label_options(self, data): + for key in ["max_download_speed", "max_upload_speed", "max_connections" ,"max_upload_slots"]: + spin = self.glade.get_widget(key) + spin.set_value(data[key]) + + self.glade.get_widget("apply_max").set_active(data["apply_max"]) + self.glade.get_widget("chk_move_completed").set_active(bool(data["move_completed_to"])) + self.glade.get_widget("move_completed_to").set_filename(data["move_completed_to"] or "") + + self.on_apply_max_changed() + self.on_chk_move_changed() + + def on_apply_prefs(self): + options = {"hide_zero_hits":self.glade.get_widget("hide_zero_hits").get_active()} + aclient.label_set_global_options(None, options) + + def on_chk_move_changed(self, data = None): + self.glade.get_widget("move_completed_to").set_sensitive( + self.glade.get_widget("chk_move_completed").get_active()) + + def on_apply_max_changed(self, data = None): + active = self.glade.get_widget("apply_max").get_active() + for key in ["max_download_speed", "max_upload_speed", "max_connections" ,"max_upload_slots","btn_apply"]: + spin = self.glade.get_widget(key) + spin.set_sensitive(active) + + + def on_add(self, widget, data = None): + label = self.txt_add.get_text() + self.txt_add.set_text("") + if label in self.labels: + return + aclient.label_add(None , label) + aclient.label_get_labels(self.cb_update_labels) + aclient.force_call(block=True) + self.select_label(label) + + def on_remove(self, widget, data=None): + label = self.get_selected_label() + aclient.label_remove(None, label) + aclient.label_get_labels(self.cb_update_labels) + self.select_label(0) + + def on_save_label(self, arg = None, apply = False): + options = {} + for key in ["max_download_speed", "max_upload_speed", "max_connections" ,"max_upload_slots"]: + options[key] = self.glade.get_widget(key).get_value() + options["apply_max"] = self.glade.get_widget("apply_max").get_active() + + if self.glade.get_widget("chk_move_completed").get_active(): + options["move_completed_to"] = self.glade.get_widget("move_completed_to").get_filename() + else: + options["move_completed_to"] = None + + aclient.label_set_options(None, self.label, options , apply) + + def on_apply_label(self, arg = None): + self.on_save_label(apply = True) + + def get_selected_label(self): + model , iter = self.label_view.get_selection().get_selected() + return self.label_store.get_value(iter,0) + + def select_label(self, label): + aclient.force_call(block=True) #sync.. + if label: + it = self.label_store.iter_nth_child(None,self.labels.index(label)) + else: + it = self.label_store.iter_nth_child(None,0) + self.label_view.get_selection().select_iter(it) + + def build_label_view(self): + "gtk should have a simple listbox widget..." + self.label_store = gtk.ListStore(str) + + column = gtk.TreeViewColumn(_("Label")) + renderer = gtk.CellRendererText() + column.pack_start(renderer) + column.set_attributes(renderer, text = 0) + + + self.label_view.set_model(self.label_store) + self.label_view.append_column(column) + self.label_view.set_headers_visible(False) + + self.label_view.get_selection().connect("changed", self.on_label_changed) + + def on_label_changed(self, selection): + (model, row) = self.label_view.get_selection().get_selected() + self.label = model.get_value(row, 0) + self.glade.get_widget("txt_label").set_markup("%s" % self.label) + aclient.label_get_options(self.cb_label_options, self.label) + + + + + + + + + + + + + +""" + + label = gtk.Label() + label.set_markup('' + _('General') + '') + + + self.set_shadow_type(gtk.SHADOW_NONE) + self.set_label_widget(label) + + self.btn_load = gtk.Button("Load Settings") + self.btn_load.connect("clicked", self.load_settings, None) + + self.btn_remove = gtk.Button("Remove") + self.btn_remove.connect("clicked", self.on_remove, None) + + vb = gtk.VBox() + self.add(vb) + #vb.add(self.btn_load) + #vb.add(gtk.Label("Label is in developent, you're testing pre-alfa!!!")) + + + self.hide_zero_hits = gtk.CheckButton(_('Hide Zero Hits')) + vb.add(self.hide_zero_hits) + + label = gtk.Label() + label.set_markup('' + _('Labels') + '') + vb.add(label) + + hp = gtk.HPaned() + hp.add1(self.label_view) + + + hp.add2(self.label_options) + + hp.set_position(100) + + hp.set_size_request(400, 200) #bug.. + + + hbAdd = gtk.HBox() + hbAdd.add(gtk.Label("Label:")) + self.txt_add = gtk.Entry() + hbAdd.add(self.txt_add) + btn_add = gtk.Button("Add") + hbAdd.add(btn_add) + btn_add.connect("clicked", self.on_add, None) + + vb.pack_end(hbAdd) + + label = gtk.Label() + label.set_markup('' + _('Add') + '') + + + + vb.pack_end(label) + + vb.pack_end(self.btn_remove, True , True) + + vb.pack_end(hp,True , True) + +""" diff --git a/deluge/plugins/label/label/gtk_sidebar.py b/deluge/plugins/label/label/gtkui/sidebar.py similarity index 66% rename from deluge/plugins/label/label/gtk_sidebar.py rename to deluge/plugins/label/label/gtkui/sidebar.py index b8f72e42d..fbef3ebfb 100644 --- a/deluge/plugins/label/label/gtk_sidebar.py +++ b/deluge/plugins/label/label/gtkui/sidebar.py @@ -62,11 +62,23 @@ class LabelSideBar(component.Component): self.filters = {} # Create the liststore - #cat,value,count , pixmap. - self.liststore = gtk.ListStore(str, str, int, gtk.gdk.Pixbuf) - self.filters[("state", "All")] = self.liststore.append(["state","All",0,gtk.gdk.pixbuf_new_from_file( - deluge.common.get_pixmap("dht16.png"))]) + #cat,value,count , pixmap , visible + self.treestore = gtk.TreeStore(str, str, int, gtk.gdk.Pixbuf, bool) + #add Cat nodes: + self.cat_nodes = {} + self.cat_nodes["state"] = self.treestore.append(None, ["cat", "State", 0, None, True]) + self.cat_nodes["tracker"] = self.treestore.append(None, ["cat","Tracker", 0,None, True]) + self.cat_nodes["label"] = self.treestore.append(None, ["cat", "Label", 0, None, True]) + + #default node: + self.filters[("state", "All")] = self.treestore.append(self.cat_nodes["state"], + ["state", "All", 0, gtk.gdk.pixbuf_new_from_file(deluge.common.get_pixmap("dht16.png")), True]) + + + #remove all old columns: + for c in self.label_view.get_columns(): + self.label_view.remove_column(c) # Create the column column = gtk.TreeViewColumn(_("Filters")) @@ -75,44 +87,80 @@ class LabelSideBar(component.Component): column.pack_start(render, expand=False) column.add_attribute(render, 'pixbuf', 3) render = gtk.CellRendererText() - column.pack_start(render, expand=True) + column.pack_start(render, expand=False) column.set_cell_data_func(render, self.render_cell_data,None) self.label_view.append_column(column) + self.label_view.set_show_expanders(False) - self.label_view.set_model(self.liststore) + self.label_view.set_model(self.treestore) 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()) + self.treestore.get_iter_first()) + self.create_model_filter() #init..... self._start() + self.label_view.expand_all() + self.hpaned.set_position(170) + def load(self): + self.label_view.set_model(self.model_filter) + + def unload(self): + #hacks! + old_sidebar = component.get("SideBar") + del old_sidebar + new_sidebar = deluge.ui.gtkui.sidebar.SideBar() + + + def create_model_filter(self): + self.model_filter = self.treestore.filter_new() + self.model_filter.set_visible_column(4) + self.label_view.set_model(self.model_filter) def cb_update_filter_items(self, filter_items): + visible_filters = [] for cat,filters in filter_items.iteritems(): for value, count in filters: self.update_row(cat, value , count) + visible_filters.append((cat, value)) - def update_row(self, cat, value , count ): + for f in self.filters: + if not f in visible_filters: + self.treestore.set_value(self.filters[f], 4, False) + + self.label_view.expand_all() + + def update_row(self, cat, value , count): if (cat, value) in self.filters: row = self.filters[(cat, value)] - self.liststore.set_value(row, 2, count) + self.treestore.set_value(row, 2, count) else: pix = self.get_pixmap(cat, value) - row = self.liststore.append([cat, value, count , pix]) + row = self.treestore.append(self.cat_nodes[cat],[cat, value, count , pix, True]) self.filters[(cat, value)] = row + self.treestore.set_value(row, 4, True) + + def render_cell_data(self, column, cell, model, row, data): "cell renderer" + cat = model.get_value(row, 0) value = model.get_value(row, 1) count = model.get_value(row, 2) - txt = "%s (%s)" % (value, count) + if cat == "cat": + txt = value + col = gtk.gdk.color_parse('gray') + else: + txt = "%s (%s)" % (value, count) + col = gtk.gdk.color_parse('white') cell.set_property('text', txt) + cell.set_property("cell-background-gdk",col) def get_pixmap(self, cat, value): if cat == "state": @@ -144,7 +192,7 @@ class LabelSideBar(component.Component): cat = "tracker_host" filter = (cat, value) - if value == "All": + if value == "All" or cat == "cat": filter = (None, None) elif (cat == "label" and value == "No Label"): filter = ("label","") @@ -157,6 +205,9 @@ class LabelSideBar(component.Component): return None def update(self): - aclient.label_filter_items(self.cb_update_filter_items) + try: + aclient.label_filter_items(self.cb_update_filter_items) + except Exception, e: + log.debug(e) diff --git a/deluge/plugins/label/label/gtkui/submenu.py b/deluge/plugins/label/label/gtkui/submenu.py new file mode 100644 index 000000000..e919fa206 --- /dev/null +++ b/deluge/plugins/label/label/gtkui/submenu.py @@ -0,0 +1,98 @@ +# +# blocklist/gtkui.py +# +# 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 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 os +import pkg_resources # access plugin egg +from deluge.log import LOG as log +from deluge import component # for systray +import ui +import gtk, gobject +from deluge.ui.client import aclient + +from deluge.configmanager import ConfigManager +config = ConfigManager("label.conf") +GTK_ALFA = config.get("gtk_alfa") +NO_LABEL = "No Label" + +def cb_none(args): + "hack for empty callbacks." + pass + +class LabelMenu(gtk.MenuItem): + def __init__(self): + gtk.MenuItem.__init__(self, "Label") + self.show_all() + + #attach.. + torrentmenu = component.get("MenuBar").torrentmenu + torrentmenu.connect("show", self.on_show, None) + + aclient.connect_on_new_core(self._on_new_core) + + def _on_new_core(self, data): + self.on_show() + + def get_torrent_ids(self): + return component.get("TorrentView").get_selected_torrents() + + + def on_show(self, widget=None, data=None): + log.debug("label-on-show") + aclient.label_get_labels(self.cb_labels) + aclient.force_call(block=True) + + def cb_labels(self , labels): + log.debug("cb_labels-start") + self.sub_menu = gtk.Menu() + for label in [NO_LABEL] + labels: + item = gtk.MenuItem(label) + item.connect("activate", self.on_select_label, label) + self.sub_menu.append(item) + self.set_submenu(self.sub_menu) + self.show_all() + log.debug("cb_labels-end") + + def on_select_label(self, widget=None, label_id = None): + log.debug("select label:%s,%s" % (label_id ,self.get_torrent_ids()) ) + for torrent_id in self.get_torrent_ids(): + aclient.label_set_torrent(cb_none, torrent_id, label_id) + #aclient.force_call(block=True) + + + + + + + + diff --git a/deluge/plugins/label/label/ui.py b/deluge/plugins/label/label/gtkui/ui.py similarity index 100% rename from deluge/plugins/label/label/ui.py rename to deluge/plugins/label/label/gtkui/ui.py diff --git a/deluge/plugins/label/label/webui.py b/deluge/plugins/label/label/webui/__init__.py similarity index 91% rename from deluge/plugins/label/label/webui.py rename to deluge/plugins/label/label/webui/__init__.py index 5bdc03f75..06c3f72dd 100644 --- a/deluge/plugins/label/label/webui.py +++ b/deluge/plugins/label/label/webui/__init__.py @@ -38,8 +38,8 @@ from deluge.ui.client import sclient from deluge import component import ui -import webui_config -import webui_pages +import config +import pages class WebUI(ui.UI): def __init__(self, plugin_api, plugin_name): @@ -49,12 +49,12 @@ class WebUI(ui.UI): log.debug("Label WebUI plugin initalized..") def enable(self): - webui_pages.register() - webui_config.register() + pages.register() + config.register() def disable(self): - webui_pages.unregister() - webui_config.unregister() + pages.unregister() + config.unregister() diff --git a/deluge/plugins/label/label/webui_config.py b/deluge/plugins/label/label/webui/config.py similarity index 96% rename from deluge/plugins/label/label/webui_config.py rename to deluge/plugins/label/label/webui/config.py index c11c85650..435973c56 100644 --- a/deluge/plugins/label/label/webui_config.py +++ b/deluge/plugins/label/label/webui/config.py @@ -80,10 +80,12 @@ class LabelUpdateCfgForm(forms.Form): sclient.label_set_options(label_id, dict(data), apply) #input fields : + apply_max = forms.CheckBox(_("Apply Maximum settings on add")) max_connections = forms.DelugeInt(_("Maximum Connections")) max_download_speed = forms.DelugeFloat(_("Maximum Download Speed (Kib/s)")) max_upload_speed = forms.DelugeFloat(_("Maximum Upload Speed (Kib/s)")) max_upload_slots = forms.DelugeInt(_("Maximum Upload Slots")) + move_completed_to = forms.ServerFolder(_("Move completed to"), required=False) apply = forms.CheckBox(_("Apply")) delete = forms.CheckBox(_("Delete")) diff --git a/deluge/plugins/label/label/webui_pages.py b/deluge/plugins/label/label/webui/pages.py similarity index 100% rename from deluge/plugins/label/label/webui_pages.py rename to deluge/plugins/label/label/webui/pages.py diff --git a/deluge/plugins/label/label/webui/ui.py b/deluge/plugins/label/label/webui/ui.py new file mode 100644 index 000000000..94b5f4b9f --- /dev/null +++ b/deluge/plugins/label/label/webui/ui.py @@ -0,0 +1,53 @@ +# +# blocklist/ui.py +# +# 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 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 gettext +import locale +import pkg_resources +import deluge.component +from deluge.ui.client import aclient as client +from deluge.log import LOG as log + +class UI: + def __init__(self, plugin_api, plugin_name): + self.plugin = plugin_api + + def enable(self): + pass + + def disable(self): + pass + + \ No newline at end of file diff --git a/deluge/plugins/label/setup.py b/deluge/plugins/label/setup.py index 959b5c9cd..0923934f1 100644 --- a/deluge/plugins/label/setup.py +++ b/deluge/plugins/label/setup.py @@ -47,8 +47,8 @@ setup( version="0.1", description=__doc__, author=__author__, - packages=["label"], - package_data = {"label": ["template/*"]}, + packages=["label","label.gtkui","label.webui"], + package_data = {"label": ["template/*","data/*"]}, entry_points=""" [deluge.plugin.core] Label = label:CorePlugin