sidebar_menu
This commit is contained in:
parent
39025191d7
commit
df3471085c
|
@ -1,453 +0,0 @@
|
||||||
#
|
|
||||||
# gtk_sidebar.py
|
|
||||||
#
|
|
||||||
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
|
|
||||||
# Copyright (C) 2007 Andrew Resch ('andar') <andrewresch@gmail.com>
|
|
||||||
#
|
|
||||||
# Deluge is free software.
|
|
||||||
#
|
|
||||||
# You may redistribute it and/or modify it under the terms of the
|
|
||||||
# GNU General Public License, as published by the Free Software
|
|
||||||
# Foundation; either version 3 of the License, or (at your option)
|
|
||||||
# any later version.
|
|
||||||
#
|
|
||||||
# deluge is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
# See the GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with deluge. If not, write to:
|
|
||||||
# The Free Software Foundation, Inc.,
|
|
||||||
# 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 deluge.component as component
|
|
||||||
import deluge.common
|
|
||||||
from deluge.log import LOG as log
|
|
||||||
from deluge.ui.client import aclient
|
|
||||||
|
|
||||||
STATE_PIX = {
|
|
||||||
"Downloading":"downloading",
|
|
||||||
"Seeding":"seeding",
|
|
||||||
"Paused":"inactive",
|
|
||||||
"Checking":"checking",
|
|
||||||
"Queued":"queued",
|
|
||||||
"Error":"alert"
|
|
||||||
}
|
|
||||||
|
|
||||||
NO_LABEL = "No Label"
|
|
||||||
|
|
||||||
#helpers:
|
|
||||||
def get_resource(filename):
|
|
||||||
import pkg_resources
|
|
||||||
import os
|
|
||||||
return pkg_resources.resource_filename("label", os.path.join("data", filename))
|
|
||||||
|
|
||||||
#menu
|
|
||||||
class LabelMenu(gtk.Menu):
|
|
||||||
def __init__(self):
|
|
||||||
gtk.Menu.__init__(self)
|
|
||||||
self.default_items = []
|
|
||||||
self.all_items = []
|
|
||||||
self._add_default_item("add", _("_Add Label"), gtk.STOCK_ADD)
|
|
||||||
self._add_default_item("remove", _("_Remove Label"), gtk.STOCK_REMOVE)
|
|
||||||
self._add_default_item("options", _("Label _Options"), gtk.STOCK_PREFERENCES)
|
|
||||||
|
|
||||||
|
|
||||||
self.append(gtk.SeparatorMenuItem())
|
|
||||||
self._add_all_item(None, _("_Select All"), gtk.STOCK_JUSTIFY_FILL)
|
|
||||||
self._add_all_item("pause", None, gtk.STOCK_MEDIA_PAUSE)
|
|
||||||
self._add_all_item("resume", _("R_esume"), gtk.STOCK_MEDIA_PLAY)
|
|
||||||
|
|
||||||
#self._add_all_item("move", _("Move _Storage"), gtk.STOCK_SAVE_AS)
|
|
||||||
#self._add_all_item("recheck", _("_Force Re-check"), gtk.STOCK_REDO)
|
|
||||||
|
|
||||||
self.show_all()
|
|
||||||
self.label = None
|
|
||||||
#dialogs:
|
|
||||||
self.add_dialog = AddDialog()
|
|
||||||
self.options_dialog = OptionsDialog()
|
|
||||||
|
|
||||||
def _add_default_item(self, id, label ,stock):
|
|
||||||
"""attaches callback to self.on_<id>"""
|
|
||||||
func = getattr(self,"on_%s" % id)
|
|
||||||
self.default_items.append(self._add_item(id, label , stock, func))
|
|
||||||
|
|
||||||
def _add_all_item(self, id, label , stock):
|
|
||||||
"""1:selects all in torrentview
|
|
||||||
2:executes menubar.on_menuitem_<id>_activate
|
|
||||||
"""
|
|
||||||
def on_all_activate(self, event = None):
|
|
||||||
component.get("TorrentView").treeview.get_selection().select_all()
|
|
||||||
if id: #for select-all method.
|
|
||||||
func = getattr(component.get("MenuBar"), "on_menuitem_%s_activate" % id)
|
|
||||||
func(event)
|
|
||||||
|
|
||||||
self.all_items.append(self._add_item(id, label , stock, on_all_activate))
|
|
||||||
|
|
||||||
def _add_item(self, id, label ,stock, func):
|
|
||||||
"""I hate glade.
|
|
||||||
id is automatically-added as self.item_<id>
|
|
||||||
"""
|
|
||||||
item = gtk.ImageMenuItem(stock)
|
|
||||||
if label:
|
|
||||||
item.get_children()[0].set_label(label)
|
|
||||||
item.connect("activate", func)
|
|
||||||
self.append(item)
|
|
||||||
setattr(self,"item_%s" % id, item)
|
|
||||||
return item
|
|
||||||
|
|
||||||
def on_add(self, event=None):
|
|
||||||
self.add_dialog.show(self.label)
|
|
||||||
|
|
||||||
def on_remove(self, event=None):
|
|
||||||
aclient.label_remove(None, self.label)
|
|
||||||
|
|
||||||
def on_options (self, event=None):
|
|
||||||
self.options_dialog.show(self.label, (200,250))
|
|
||||||
|
|
||||||
def set_label(self, label, count):
|
|
||||||
"No Label:disable options/del"
|
|
||||||
self.label = label
|
|
||||||
#self.count = count
|
|
||||||
|
|
||||||
#None:only enable Add.
|
|
||||||
|
|
||||||
#default items
|
|
||||||
sensitive = (label not in (NO_LABEL, None))
|
|
||||||
for item in self.default_items:
|
|
||||||
item.set_sensitive(sensitive)
|
|
||||||
|
|
||||||
#"all" items:
|
|
||||||
sensitive = (count > 0)
|
|
||||||
for item in self.all_items:
|
|
||||||
item.set_sensitive(sensitive)
|
|
||||||
|
|
||||||
|
|
||||||
#add is allways enabled.
|
|
||||||
self.item_add.set_sensitive(True)
|
|
||||||
|
|
||||||
|
|
||||||
#dialogs:
|
|
||||||
class AddDialog(object):
|
|
||||||
def __init__(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def show(self, label ):
|
|
||||||
self.glade = gtk.glade.XML(get_resource("label_options.glade"))
|
|
||||||
self.dialog = self.glade.get_widget("dlg_label_add")
|
|
||||||
self.glade.signal_autoconnect({
|
|
||||||
"on_add_ok":self.on_ok,
|
|
||||||
"on_add_cancel":self.on_cancel,
|
|
||||||
})
|
|
||||||
self.dialog.run()
|
|
||||||
|
|
||||||
def on_ok(self, event=None):
|
|
||||||
value = self.glade.get_widget("txt_add").get_text()
|
|
||||||
aclient.label_add(None, value)
|
|
||||||
self.dialog.destroy()
|
|
||||||
|
|
||||||
def on_cancel(self, event=None):
|
|
||||||
self.dialog.destroy()
|
|
||||||
|
|
||||||
|
|
||||||
class OptionsDialog(object):
|
|
||||||
spin_ids = ["max_download_speed", "max_upload_speed", "max_upload_slots", "max_connections", "stop_ratio"]
|
|
||||||
chk_ids = ["apply_max", "apply_queue", "stop_at_ratio", "apply_queue", "remove_at_ratio",
|
|
||||||
"apply_move_completed", "move_completed", "is_auto_managed", "auto_add"]
|
|
||||||
|
|
||||||
#list of tuples, because order matters when nesting.
|
|
||||||
sensitive_groups = [
|
|
||||||
("apply_max", ["max_download_speed", "max_upload_speed", "max_upload_slots", "max_connections"]),
|
|
||||||
("apply_queue", ["is_auto_managed", "stop_at_ratio"]),
|
|
||||||
("stop_at_ratio", ["remove_at_ratio", "stop_ratio"]), #nested
|
|
||||||
("apply_move_completed", ["move_completed"]),
|
|
||||||
("move_completed", ["move_completed_path"]), #nested
|
|
||||||
("auto_add", ["auto_add_trackers"])
|
|
||||||
]
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def show(self, label , position):
|
|
||||||
self.label = label
|
|
||||||
self.glade = gtk.glade.XML(get_resource("label_options.glade"))
|
|
||||||
self.dialog = self.glade.get_widget("dlg_label_options")
|
|
||||||
self.glade.signal_autoconnect({
|
|
||||||
"on_options_ok":self.on_ok,
|
|
||||||
"on_options_cancel":self.on_cancel,
|
|
||||||
})
|
|
||||||
|
|
||||||
for chk_id, group in self.sensitive_groups:
|
|
||||||
chk = self.glade.get_widget(chk_id)
|
|
||||||
chk.connect("toggled",self.apply_sensitivity)
|
|
||||||
|
|
||||||
aclient.label_get_options(self.load_options, self.label)
|
|
||||||
self.dialog.move(*position)
|
|
||||||
self.dialog.run()
|
|
||||||
|
|
||||||
def load_options(self, options):
|
|
||||||
log.debug(options.keys())
|
|
||||||
|
|
||||||
for id in self.spin_ids:
|
|
||||||
self.glade.get_widget(id).set_value(options[id])
|
|
||||||
for id in self.chk_ids:
|
|
||||||
self.glade.get_widget(id).set_active(bool(options[id]))
|
|
||||||
|
|
||||||
self.glade.get_widget("move_completed_path").set_filename(options["move_completed_path"])
|
|
||||||
|
|
||||||
self.glade.get_widget("auto_add_trackers").get_buffer().set_text("\n".join(options["auto_add_trackers"]))
|
|
||||||
|
|
||||||
self.apply_sensitivity()
|
|
||||||
|
|
||||||
def on_ok(self, event=None):
|
|
||||||
"save options.."
|
|
||||||
options = {}
|
|
||||||
|
|
||||||
for id in self.spin_ids:
|
|
||||||
options[id] = self.glade.get_widget(id).get_value()
|
|
||||||
for id in self.chk_ids:
|
|
||||||
options[id] = self.glade.get_widget(id).get_active()
|
|
||||||
|
|
||||||
options["move_completed_path"] = self.glade.get_widget("move_completed_path").get_filename()
|
|
||||||
buff = self.glade.get_widget("auto_add_trackers").get_buffer() #sometimes I hate gtk...
|
|
||||||
tracker_lst = buff.get_text(buff.get_start_iter(), buff.get_end_iter()).strip().split("\n")
|
|
||||||
options["auto_add_trackers"] = [x for x in tracker_lst if x] #filter out empty lines.
|
|
||||||
|
|
||||||
log.debug(options)
|
|
||||||
aclient.label_set_options(None, self.label, options)
|
|
||||||
self.dialog.destroy()
|
|
||||||
|
|
||||||
def apply_sensitivity(self, event=None):
|
|
||||||
for chk_id , sensitive_list in self.sensitive_groups:
|
|
||||||
chk = self.glade.get_widget(chk_id)
|
|
||||||
sens = chk.get_active() and chk.get_property("sensitive")
|
|
||||||
for widget_id in sensitive_list:
|
|
||||||
self.glade.get_widget(widget_id).set_sensitive(sens)
|
|
||||||
|
|
||||||
def on_cancel(self, event=None):
|
|
||||||
self.dialog.destroy()
|
|
||||||
|
|
||||||
#sidebar-treeview
|
|
||||||
class LabelSideBar(component.Component):
|
|
||||||
def __init__(self):
|
|
||||||
component.Component.__init__(self, "LabelSideBar", interval=2000)
|
|
||||||
self.window = component.get("MainWindow")
|
|
||||||
glade = self.window.main_glade
|
|
||||||
self.hpaned = glade.get_widget("hpaned")
|
|
||||||
self.scrolled = glade.get_widget("scrolledwindow_sidebar")
|
|
||||||
self.is_visible = True
|
|
||||||
self.filters = {}
|
|
||||||
self.label_view = gtk.TreeView()
|
|
||||||
|
|
||||||
# Create the liststore
|
|
||||||
#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, False])
|
|
||||||
self.cat_nodes["tracker"] = self.treestore.append(None, ["cat","Tracker", 0,None, False])
|
|
||||||
self.cat_nodes["label"] = self.treestore.append(None, ["cat", "Label", 0, None, False])
|
|
||||||
|
|
||||||
#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])
|
|
||||||
|
|
||||||
# Create the column
|
|
||||||
column = gtk.TreeViewColumn(_("Filters"))
|
|
||||||
column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
|
|
||||||
render = gtk.CellRendererPixbuf()
|
|
||||||
self.renderpix = render
|
|
||||||
column.pack_start(render, expand=False)
|
|
||||||
column.add_attribute(render, 'pixbuf', 3)
|
|
||||||
render = gtk.CellRendererText()
|
|
||||||
column.pack_start(render, expand=False)
|
|
||||||
column.set_cell_data_func(render, self.render_cell_data,None)
|
|
||||||
|
|
||||||
self.label_view.append_column(column)
|
|
||||||
|
|
||||||
#style:
|
|
||||||
self.label_view.set_show_expanders(False)
|
|
||||||
self.label_view.set_headers_visible(False)
|
|
||||||
|
|
||||||
self.label_view.set_model(self.treestore)
|
|
||||||
|
|
||||||
self.label_view.get_selection().connect("changed",
|
|
||||||
self.on_selection_changed)
|
|
||||||
|
|
||||||
self.create_model_filter()
|
|
||||||
|
|
||||||
#init.....
|
|
||||||
self._start()
|
|
||||||
self.hpaned.set_position(170)
|
|
||||||
self.label_view.connect("button-press-event", self.on_button_press_event)
|
|
||||||
self.label_menu = LabelMenu()
|
|
||||||
|
|
||||||
|
|
||||||
def load(self):
|
|
||||||
sidebar = component.get("SideBar")
|
|
||||||
sidebar.add_tab(self.label_view, "filters", _("Filters"))
|
|
||||||
|
|
||||||
def unload(self):
|
|
||||||
log.debug("unload..")
|
|
||||||
sidebar = component.get("SideBar")
|
|
||||||
sidebar.remove_tab("filters")
|
|
||||||
log.debug("unload-end")
|
|
||||||
|
|
||||||
|
|
||||||
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))
|
|
||||||
|
|
||||||
# hide root-categories not returned by core-part of the plugin.
|
|
||||||
for cat in self.cat_nodes:
|
|
||||||
if cat in filter_items:
|
|
||||||
self.treestore.set_value(self.cat_nodes[cat], 4, True)
|
|
||||||
|
|
||||||
else:
|
|
||||||
self.treestore.set_value(self.cat_nodes[cat], 4, False)
|
|
||||||
|
|
||||||
# hide items not returned by core-plugin.
|
|
||||||
for f in self.filters:
|
|
||||||
if not f in visible_filters:
|
|
||||||
self.treestore.set_value(self.filters[f], 4, False)
|
|
||||||
|
|
||||||
# obsolete?
|
|
||||||
self.label_view.expand_all()
|
|
||||||
|
|
||||||
def update_row(self, cat, value , count):
|
|
||||||
if (cat, value) in self.filters:
|
|
||||||
row = self.filters[(cat, value)]
|
|
||||||
self.treestore.set_value(row, 2, count)
|
|
||||||
else:
|
|
||||||
pix = self.get_pixmap(cat, value)
|
|
||||||
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)
|
|
||||||
|
|
||||||
if cat == "state":
|
|
||||||
self.renderpix.set_property("visible", True)
|
|
||||||
else:
|
|
||||||
self.renderpix.set_property("visible", False)
|
|
||||||
|
|
||||||
cell.set_property('editable', False)
|
|
||||||
if cat == "cat":
|
|
||||||
txt = value
|
|
||||||
col = gtk.gdk.color_parse('#EEEEEE')
|
|
||||||
else:
|
|
||||||
txt = "%s (%s)" % (value, count)
|
|
||||||
col = gtk.gdk.color_parse('white')
|
|
||||||
|
|
||||||
cell.set_property('text', txt)
|
|
||||||
cell.set_property("cell-background-gdk",col)
|
|
||||||
self.renderpix.set_property("cell-background-gdk",col)
|
|
||||||
|
|
||||||
def get_pixmap(self, cat, value):
|
|
||||||
if cat == "state":
|
|
||||||
pix = STATE_PIX.get(value, "dht")
|
|
||||||
return gtk.gdk.pixbuf_new_from_file(deluge.common.get_pixmap("%s16.png" % pix))
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def visible(self, visible):
|
|
||||||
if visible:
|
|
||||||
self.scrolled.show()
|
|
||||||
else:
|
|
||||||
self.scrolled.hide()
|
|
||||||
self.hpaned.set_position(-1)
|
|
||||||
|
|
||||||
self.is_visible = visible
|
|
||||||
|
|
||||||
def on_selection_changed(self, selection):
|
|
||||||
try:
|
|
||||||
(model, row) = self.label_view.get_selection().get_selected()
|
|
||||||
if not row:
|
|
||||||
log.debug("nothing selected")
|
|
||||||
return
|
|
||||||
|
|
||||||
cat = model.get_value(row, 0)
|
|
||||||
value = model.get_value(row, 1)
|
|
||||||
|
|
||||||
#gtk-ui has it's own filtering logic on status-fields.
|
|
||||||
#not using the label-backend for filtering. (for now)
|
|
||||||
#just a few simple hacks to translate label-filters to gtk-filters.
|
|
||||||
if cat == "tracker":
|
|
||||||
cat = "tracker_host"
|
|
||||||
|
|
||||||
filter_dict = {cat: [value]}
|
|
||||||
if value == "All" or cat == "cat":
|
|
||||||
filter_dict = {}
|
|
||||||
elif (cat == "label" and value == NO_LABEL):
|
|
||||||
filter_dict["label"] = [""]
|
|
||||||
|
|
||||||
component.get("TorrentView").set_filter(filter_dict)
|
|
||||||
|
|
||||||
except Exception, e:
|
|
||||||
log.debug(e)
|
|
||||||
# paths is likely None .. so lets return None
|
|
||||||
return None
|
|
||||||
|
|
||||||
def update(self):
|
|
||||||
try:
|
|
||||||
aclient.label_filter_items(self.cb_update_filter_items)
|
|
||||||
except Exception, e:
|
|
||||||
log.debug(e)
|
|
||||||
|
|
||||||
|
|
||||||
### Callbacks ###
|
|
||||||
def on_button_press_event(self, widget, event):
|
|
||||||
"""This is a callback for showing the right-click context menu."""
|
|
||||||
|
|
||||||
# We only care about right-clicks
|
|
||||||
if event.button == 3:
|
|
||||||
x, y = event.get_coords()
|
|
||||||
path = self.label_view.get_path_at_pos(int(x), int(y))
|
|
||||||
if not path:
|
|
||||||
return
|
|
||||||
row = self.model_filter.get_iter(path[0])
|
|
||||||
cat = self.model_filter.get_value(row, 0)
|
|
||||||
value = self.model_filter.get_value(row, 1)
|
|
||||||
count = self.model_filter.get_value(row, 2)
|
|
||||||
|
|
||||||
#log.debug("right-click->cat='%s',value='%s'", cat ,value)
|
|
||||||
|
|
||||||
if cat == "label":
|
|
||||||
self.show_label_menu(value, count, event)
|
|
||||||
elif (cat == "cat" and value == "Label"): #add button on root node.
|
|
||||||
self.show_label_menu(None, 0, event)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def show_label_menu(self, label, count, event):
|
|
||||||
self.label_menu.set_label(label, count)
|
|
||||||
self.label_menu.popup(None, None, None, event.button, event.time)
|
|
||||||
|
|
|
@ -0,0 +1,227 @@
|
||||||
|
#
|
||||||
|
# gtk_sidebar.py
|
||||||
|
#
|
||||||
|
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
|
||||||
|
# Copyright (C) 2007 Andrew Resch ('andar') <andrewresch@gmail.com>
|
||||||
|
#
|
||||||
|
# Deluge is free software.
|
||||||
|
#
|
||||||
|
# You may redistribute it and/or modify it under the terms of the
|
||||||
|
# GNU General Public License, as published by the Free Software
|
||||||
|
# Foundation; either version 3 of the License, or (at your option)
|
||||||
|
# any later version.
|
||||||
|
#
|
||||||
|
# deluge is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
# See the GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with deluge. If not, write to:
|
||||||
|
# The Free Software Foundation, Inc.,
|
||||||
|
# 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 deluge.component as component
|
||||||
|
import deluge.common
|
||||||
|
from deluge.log import LOG as log
|
||||||
|
from deluge.ui.client import aclient
|
||||||
|
|
||||||
|
STATE_PIX = {
|
||||||
|
"Downloading":"downloading",
|
||||||
|
"Seeding":"seeding",
|
||||||
|
"Paused":"inactive",
|
||||||
|
"Checking":"checking",
|
||||||
|
"Queued":"queued",
|
||||||
|
"Error":"alert"
|
||||||
|
}
|
||||||
|
|
||||||
|
NO_LABEL = "No Label"
|
||||||
|
|
||||||
|
#helpers:
|
||||||
|
def get_resource(filename):
|
||||||
|
import pkg_resources
|
||||||
|
import os
|
||||||
|
return pkg_resources.resource_filename("label", os.path.join("data", filename))
|
||||||
|
|
||||||
|
#menu
|
||||||
|
class LabelSidebarMenu(object):
|
||||||
|
def __init__(self):
|
||||||
|
|
||||||
|
self.treeview = component.get("FilterTreeView")
|
||||||
|
self.menu = self.treeview.menu
|
||||||
|
self.items = []
|
||||||
|
|
||||||
|
#add items, in reverse order, because they are prepended.
|
||||||
|
sep = gtk.SeparatorMenuItem()
|
||||||
|
self.items.append(sep)
|
||||||
|
self.menu.prepend(sep)
|
||||||
|
self._add_item("options", _("Label _Options"), gtk.STOCK_PREFERENCES)
|
||||||
|
self._add_item("remove", _("_Remove Label"), gtk.STOCK_REMOVE)
|
||||||
|
self._add_item("add", _("_Add Label"), gtk.STOCK_ADD)
|
||||||
|
|
||||||
|
self.menu.show_all()
|
||||||
|
#dialogs:
|
||||||
|
self.add_dialog = AddDialog()
|
||||||
|
self.options_dialog = OptionsDialog()
|
||||||
|
#hooks:
|
||||||
|
self.menu.connect("show", self.on_show, None)
|
||||||
|
|
||||||
|
|
||||||
|
def _add_item(self, id, label ,stock):
|
||||||
|
"""I hate glade.
|
||||||
|
id is automatically-added as self.item_<id>
|
||||||
|
"""
|
||||||
|
func = getattr(self,"on_%s" % id)
|
||||||
|
item = gtk.ImageMenuItem(stock)
|
||||||
|
item.get_children()[0].set_label(label)
|
||||||
|
item.connect("activate", func)
|
||||||
|
self.menu.prepend(item)
|
||||||
|
setattr(self,"item_%s" % id, item)
|
||||||
|
self.items.append(item)
|
||||||
|
return item
|
||||||
|
|
||||||
|
def on_add(self, event=None):
|
||||||
|
self.add_dialog.show()
|
||||||
|
|
||||||
|
def on_remove(self, event=None):
|
||||||
|
aclient.label_remove(None, self.treeview.value)
|
||||||
|
|
||||||
|
def on_options (self, event=None):
|
||||||
|
self.options_dialog.show(self.treeview.value, (200,250))
|
||||||
|
|
||||||
|
def on_show(self, widget=None, data=None):
|
||||||
|
"No Label:disable options/del"
|
||||||
|
log.debug("label-sidebar-popup:on-show")
|
||||||
|
|
||||||
|
cat = self.treeview.cat
|
||||||
|
label = self.treeview.value
|
||||||
|
if cat == "label" or (cat == "cat" and label == "label"):
|
||||||
|
#is a label : show menu-items
|
||||||
|
for item in self.items:
|
||||||
|
item.show()
|
||||||
|
#default items
|
||||||
|
sensitive = ((label not in (NO_LABEL, None, "")) and (cat != "cat"))
|
||||||
|
for item in self.items:
|
||||||
|
item.set_sensitive(sensitive)
|
||||||
|
|
||||||
|
#add is allways enabled.
|
||||||
|
self.item_add.set_sensitive(True)
|
||||||
|
else:
|
||||||
|
#not a label -->hide everything.
|
||||||
|
for item in self.items:
|
||||||
|
item.hide()
|
||||||
|
|
||||||
|
|
||||||
|
#dialogs:
|
||||||
|
class AddDialog(object):
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def show(self):
|
||||||
|
self.glade = gtk.glade.XML(get_resource("label_options.glade"))
|
||||||
|
self.dialog = self.glade.get_widget("dlg_label_add")
|
||||||
|
self.glade.signal_autoconnect({
|
||||||
|
"on_add_ok":self.on_ok,
|
||||||
|
"on_add_cancel":self.on_cancel,
|
||||||
|
})
|
||||||
|
self.dialog.run()
|
||||||
|
|
||||||
|
def on_ok(self, event=None):
|
||||||
|
value = self.glade.get_widget("txt_add").get_text()
|
||||||
|
aclient.label_add(None, value)
|
||||||
|
self.dialog.destroy()
|
||||||
|
|
||||||
|
def on_cancel(self, event=None):
|
||||||
|
self.dialog.destroy()
|
||||||
|
|
||||||
|
|
||||||
|
class OptionsDialog(object):
|
||||||
|
spin_ids = ["max_download_speed", "max_upload_speed", "max_upload_slots", "max_connections", "stop_ratio"]
|
||||||
|
chk_ids = ["apply_max", "apply_queue", "stop_at_ratio", "apply_queue", "remove_at_ratio",
|
||||||
|
"apply_move_completed", "move_completed", "is_auto_managed", "auto_add"]
|
||||||
|
|
||||||
|
#list of tuples, because order matters when nesting.
|
||||||
|
sensitive_groups = [
|
||||||
|
("apply_max", ["max_download_speed", "max_upload_speed", "max_upload_slots", "max_connections"]),
|
||||||
|
("apply_queue", ["is_auto_managed", "stop_at_ratio"]),
|
||||||
|
("stop_at_ratio", ["remove_at_ratio", "stop_ratio"]), #nested
|
||||||
|
("apply_move_completed", ["move_completed"]),
|
||||||
|
("move_completed", ["move_completed_path"]), #nested
|
||||||
|
("auto_add", ["auto_add_trackers"])
|
||||||
|
]
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def show(self, label , position):
|
||||||
|
self.label = label
|
||||||
|
self.glade = gtk.glade.XML(get_resource("label_options.glade"))
|
||||||
|
self.dialog = self.glade.get_widget("dlg_label_options")
|
||||||
|
self.glade.signal_autoconnect({
|
||||||
|
"on_options_ok":self.on_ok,
|
||||||
|
"on_options_cancel":self.on_cancel,
|
||||||
|
})
|
||||||
|
|
||||||
|
for chk_id, group in self.sensitive_groups:
|
||||||
|
chk = self.glade.get_widget(chk_id)
|
||||||
|
chk.connect("toggled",self.apply_sensitivity)
|
||||||
|
|
||||||
|
aclient.label_get_options(self.load_options, self.label)
|
||||||
|
self.dialog.move(*position)
|
||||||
|
self.dialog.run()
|
||||||
|
|
||||||
|
def load_options(self, options):
|
||||||
|
log.debug(options.keys())
|
||||||
|
|
||||||
|
for id in self.spin_ids:
|
||||||
|
self.glade.get_widget(id).set_value(options[id])
|
||||||
|
for id in self.chk_ids:
|
||||||
|
self.glade.get_widget(id).set_active(bool(options[id]))
|
||||||
|
|
||||||
|
self.glade.get_widget("move_completed_path").set_filename(options["move_completed_path"])
|
||||||
|
|
||||||
|
self.glade.get_widget("auto_add_trackers").get_buffer().set_text("\n".join(options["auto_add_trackers"]))
|
||||||
|
|
||||||
|
self.apply_sensitivity()
|
||||||
|
|
||||||
|
def on_ok(self, event=None):
|
||||||
|
"save options.."
|
||||||
|
options = {}
|
||||||
|
|
||||||
|
for id in self.spin_ids:
|
||||||
|
options[id] = self.glade.get_widget(id).get_value()
|
||||||
|
for id in self.chk_ids:
|
||||||
|
options[id] = self.glade.get_widget(id).get_active()
|
||||||
|
|
||||||
|
options["move_completed_path"] = self.glade.get_widget("move_completed_path").get_filename()
|
||||||
|
buff = self.glade.get_widget("auto_add_trackers").get_buffer() #sometimes I hate gtk...
|
||||||
|
tracker_lst = buff.get_text(buff.get_start_iter(), buff.get_end_iter()).strip().split("\n")
|
||||||
|
options["auto_add_trackers"] = [x for x in tracker_lst if x] #filter out empty lines.
|
||||||
|
|
||||||
|
log.debug(options)
|
||||||
|
aclient.label_set_options(None, self.treeview.value, options)
|
||||||
|
self.dialog.destroy()
|
||||||
|
|
||||||
|
def apply_sensitivity(self, event=None):
|
||||||
|
for chk_id , sensitive_list in self.sensitive_groups:
|
||||||
|
chk = self.glade.get_widget(chk_id)
|
||||||
|
sens = chk.get_active() and chk.get_property("sensitive")
|
||||||
|
for widget_id in sensitive_list:
|
||||||
|
self.glade.get_widget(widget_id).set_sensitive(sens)
|
||||||
|
|
||||||
|
def on_cancel(self, event=None):
|
||||||
|
self.dialog.destroy()
|
Loading…
Reference in New Issue