[GTKUI] Rework the sidebar layout
* Changed variable names to be less confusing. * Flake8'd. * Move the 'count' to a separate render cell. * Reduced size of expander icon to make it less intrusive. * Enabled ellipsis on labels so count is still visible. * Used pango markup on cell labels and count. * No longer set a fixed colour to fix #1193.
This commit is contained in:
parent
e97140cbde
commit
5a6f202cf1
|
@ -1,41 +1,17 @@
|
||||||
#
|
# -*- coding: utf-8 -*-
|
||||||
# filtertreeview.py
|
|
||||||
#
|
#
|
||||||
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
|
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
|
||||||
# Copyright (C) 2008 Andrew Resch <andrewresch@gmail.com>
|
# 2008 Andrew Resch <andrewresch@gmail.com>
|
||||||
#
|
# 2014 Calum Lind <calumlind@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.
|
|
||||||
#
|
#
|
||||||
|
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
|
||||||
|
# the additional special exception to link portions of this program with the OpenSSL library.
|
||||||
|
# See LICENSE for more details.
|
||||||
#
|
#
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import gtk
|
import gtk
|
||||||
|
import pango
|
||||||
import logging
|
import logging
|
||||||
import warnings
|
import warnings
|
||||||
from gobject import GError
|
from gobject import GError
|
||||||
|
@ -67,25 +43,65 @@ TRACKER_PIX = {
|
||||||
|
|
||||||
FILTER_COLUMN = 5
|
FILTER_COLUMN = 5
|
||||||
|
|
||||||
#sidebar-treeview
|
|
||||||
class FilterTreeView(component.Component):
|
class FilterTreeView(component.Component):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
component.Component.__init__(self, "FilterTreeView", interval=2)
|
component.Component.__init__(self, "FilterTreeView", interval=2)
|
||||||
self.window = component.get("MainWindow")
|
self.window = component.get("MainWindow")
|
||||||
main_builder = self.window.get_builder()
|
|
||||||
self.hpaned = main_builder.get_object("main_window_hpaned")
|
|
||||||
self.scrolled = main_builder.get_object("scrolledwindow_sidebar")
|
|
||||||
self.sidebar = component.get("SideBar")
|
|
||||||
self.config = ConfigManager("gtkui.conf")
|
self.config = ConfigManager("gtkui.conf")
|
||||||
|
|
||||||
self.tracker_icons = component.get("TrackerIcons")
|
self.tracker_icons = component.get("TrackerIcons")
|
||||||
|
|
||||||
self.label_view = gtk.TreeView()
|
self.sidebar = component.get("SideBar")
|
||||||
self.sidebar.add_tab(self.label_view, "filters", "Filters")
|
self.treeview = gtk.TreeView()
|
||||||
|
self.sidebar.add_tab(self.treeview, "filters", "Filters")
|
||||||
|
|
||||||
#set filter to all when hidden:
|
#set filter to all when hidden:
|
||||||
self.sidebar.notebook.connect("hide", self._on_hide)
|
self.sidebar.notebook.connect("hide", self._on_hide)
|
||||||
|
|
||||||
#menu
|
# Create the treestore
|
||||||
|
#cat, value, label, count, pixmap, visible
|
||||||
|
self.treestore = gtk.TreeStore(str, str, str, int, gtk.gdk.Pixbuf, bool)
|
||||||
|
|
||||||
|
# Create the column and cells
|
||||||
|
column = gtk.TreeViewColumn("Filters")
|
||||||
|
column.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE)
|
||||||
|
# icon cell
|
||||||
|
self.cell_pix = gtk.CellRendererPixbuf()
|
||||||
|
column.pack_start(self.cell_pix, expand=False)
|
||||||
|
column.add_attribute(self.cell_pix, 'pixbuf', 4)
|
||||||
|
# label cell
|
||||||
|
cell_label = gtk.CellRendererText()
|
||||||
|
cell_label.set_property('ellipsize', pango.ELLIPSIZE_END)
|
||||||
|
column.pack_start(cell_label, expand=True)
|
||||||
|
column.set_cell_data_func(cell_label, self.render_cell_data, None)
|
||||||
|
# count cell
|
||||||
|
self.cell_count = gtk.CellRendererText()
|
||||||
|
self.cell_count.set_property('xalign', 1.0)
|
||||||
|
column.pack_start(self.cell_count, expand=False)
|
||||||
|
|
||||||
|
self.treeview.append_column(column)
|
||||||
|
|
||||||
|
# Style
|
||||||
|
self.treeview.set_show_expanders(True)
|
||||||
|
self.treeview.set_headers_visible(False)
|
||||||
|
self.treeview.set_level_indentation(-21)
|
||||||
|
# Force theme to use expander-size so we don't cut out entries due to indentation hack.
|
||||||
|
gtk.rc_parse_string("""style "treeview-style" {GtkTreeView::expander-size = 7}
|
||||||
|
class "GtkTreeView" style "treeview-style" """)
|
||||||
|
|
||||||
|
self.treeview.set_model(self.treestore)
|
||||||
|
self.treeview.get_selection().connect("changed", self.on_selection_changed)
|
||||||
|
self.create_model_filter()
|
||||||
|
|
||||||
|
self.treeview.connect("button-press-event", self.on_button_press_event)
|
||||||
|
|
||||||
|
#colors using current theme.
|
||||||
|
style = self.window.window.get_style()
|
||||||
|
self.colour_background = style.bg[gtk.STATE_NORMAL]
|
||||||
|
self.colour_foreground = style.fg[gtk.STATE_NORMAL]
|
||||||
|
|
||||||
|
# filtertree menu
|
||||||
builder = gtk.Builder()
|
builder = gtk.Builder()
|
||||||
builder.add_from_file(resource_filename("deluge.ui.gtkui", os.path.join("glade", "filtertree_menu.ui")))
|
builder.add_from_file(resource_filename("deluge.ui.gtkui", os.path.join("glade", "filtertree_menu.ui")))
|
||||||
self.menu = builder.get_object("filtertree_menu")
|
self.menu = builder.get_object("filtertree_menu")
|
||||||
|
@ -97,43 +113,6 @@ class FilterTreeView(component.Component):
|
||||||
|
|
||||||
self.default_menu_items = self.menu.get_children()
|
self.default_menu_items = self.menu.get_children()
|
||||||
|
|
||||||
# Create the liststore
|
|
||||||
#cat, value, label, count, pixmap, visible
|
|
||||||
self.treestore = gtk.TreeStore(str, str, str, int, gtk.gdk.Pixbuf, bool)
|
|
||||||
|
|
||||||
# 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', 4)
|
|
||||||
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(True)
|
|
||||||
self.label_view.set_headers_visible(False)
|
|
||||||
self.label_view.set_level_indentation(-35)
|
|
||||||
# Force the theme to use an expander-size of 15 so that we don't cut out
|
|
||||||
# entries due to our indentation hack.
|
|
||||||
gtk.rc_parse_string('style "treeview-style" { GtkTreeView::expander-size = 15 } class "GtkTreeView" style "treeview-style"')
|
|
||||||
|
|
||||||
self.label_view.set_model(self.treestore)
|
|
||||||
self.label_view.get_selection().connect("changed", self.on_selection_changed)
|
|
||||||
self.create_model_filter()
|
|
||||||
|
|
||||||
#init.....
|
|
||||||
self.label_view.connect("button-press-event", self.on_button_press_event)
|
|
||||||
|
|
||||||
#colors using current theme.
|
|
||||||
style = self.window.window.get_style()
|
|
||||||
self.colour_background = style.bg[gtk.STATE_NORMAL]
|
|
||||||
self.colour_foreground = style.fg[gtk.STATE_NORMAL]
|
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
#add Cat nodes:
|
#add Cat nodes:
|
||||||
self.cat_nodes = {}
|
self.cat_nodes = {}
|
||||||
|
@ -144,14 +123,15 @@ class FilterTreeView(component.Component):
|
||||||
for state in ["All", "Active"] + TORRENT_STATE:
|
for state in ["All", "Active"] + TORRENT_STATE:
|
||||||
self.update_row("state", state, 0, _(state))
|
self.update_row("state", state, 0, _(state))
|
||||||
|
|
||||||
self.cat_nodes["tracker_host"] = self.treestore.append(None, ["cat", "tracker_host", _("Trackers"), 0, None, False])
|
self.cat_nodes["tracker_host"] = self.treestore.append(None, ["cat", "tracker_host",
|
||||||
self.update_row("tracker_host", "All" , 0, _("All"))
|
_("Trackers"), 0, None, False])
|
||||||
self.update_row("tracker_host", "Error" , 0, _("Error"))
|
self.update_row("tracker_host", "All", 0, _("All"))
|
||||||
self.update_row("tracker_host", "" , 0, _("None"))
|
self.update_row("tracker_host", "Error", 0, _("Error"))
|
||||||
|
self.update_row("tracker_host", "", 0, _("None"))
|
||||||
|
|
||||||
self.cat_nodes["owner"] = self.treestore.append(None, ["cat", "owner", _("Owner"), 0, None, False])
|
self.cat_nodes["owner"] = self.treestore.append(None, ["cat", "owner", _("Owner"), 0, None, False])
|
||||||
self.update_row("owner", "localclient" , 0, _("Admin"))
|
self.update_row("owner", "localclient", 0, _("Admin"))
|
||||||
self.update_row("owner", "" , 0, _("None"))
|
self.update_row("owner", "", 0, _("None"))
|
||||||
|
|
||||||
# We set to this expand the rows on start-up
|
# We set to this expand the rows on start-up
|
||||||
self.expand_rows = True
|
self.expand_rows = True
|
||||||
|
@ -164,7 +144,7 @@ class FilterTreeView(component.Component):
|
||||||
def create_model_filter(self):
|
def create_model_filter(self):
|
||||||
self.model_filter = self.treestore.filter_new()
|
self.model_filter = self.treestore.filter_new()
|
||||||
self.model_filter.set_visible_column(FILTER_COLUMN)
|
self.model_filter.set_visible_column(FILTER_COLUMN)
|
||||||
self.label_view.set_model(self.model_filter)
|
self.treeview.set_model(self.model_filter)
|
||||||
|
|
||||||
def cb_update_filter_tree(self, filter_items):
|
def cb_update_filter_tree(self, filter_items):
|
||||||
#create missing cat_nodes
|
#create missing cat_nodes
|
||||||
|
@ -177,17 +157,14 @@ class FilterTreeView(component.Component):
|
||||||
|
|
||||||
#update rows
|
#update rows
|
||||||
visible_filters = []
|
visible_filters = []
|
||||||
for cat,filters in filter_items.iteritems():
|
for cat, filters in filter_items.iteritems():
|
||||||
for value, count in filters:
|
for value, count in filters:
|
||||||
self.update_row(cat, value, count)
|
self.update_row(cat, value, count)
|
||||||
visible_filters.append((cat, value))
|
visible_filters.append((cat, value))
|
||||||
|
|
||||||
# hide root-categories not returned by core-part of the plugin.
|
# hide root-categories not returned by core-part of the plugin.
|
||||||
for cat in self.cat_nodes:
|
for cat in self.cat_nodes:
|
||||||
if cat in filter_items:
|
self.treestore.set_value(self.cat_nodes[cat], FILTER_COLUMN, True if cat in filter_items else False)
|
||||||
self.treestore.set_value(self.cat_nodes[cat], FILTER_COLUMN, True)
|
|
||||||
else:
|
|
||||||
self.treestore.set_value(self.cat_nodes[cat], FILTER_COLUMN, False)
|
|
||||||
|
|
||||||
# hide items not returned by core-plugin.
|
# hide items not returned by core-plugin.
|
||||||
for f in self.filters:
|
for f in self.filters:
|
||||||
|
@ -195,13 +172,13 @@ class FilterTreeView(component.Component):
|
||||||
self.treestore.set_value(self.filters[f], FILTER_COLUMN, False)
|
self.treestore.set_value(self.filters[f], FILTER_COLUMN, False)
|
||||||
|
|
||||||
if self.expand_rows:
|
if self.expand_rows:
|
||||||
self.label_view.expand_all()
|
self.treeview.expand_all()
|
||||||
self.expand_rows = False
|
self.expand_rows = False
|
||||||
|
|
||||||
if not self.selected_path:
|
if not self.selected_path:
|
||||||
self.select_default_filter()
|
self.select_default_filter()
|
||||||
|
|
||||||
def update_row(self, cat, value , count, label=None):
|
def update_row(self, cat, value, count, label=None):
|
||||||
def on_get_icon(icon):
|
def on_get_icon(icon):
|
||||||
if icon:
|
if icon:
|
||||||
self.set_row_image(cat, value, icon.get_filename())
|
self.set_row_image(cat, value, icon.get_filename())
|
||||||
|
@ -220,7 +197,7 @@ class FilterTreeView(component.Component):
|
||||||
elif not label and value:
|
elif not label and value:
|
||||||
label = _(value)
|
label = _(value)
|
||||||
|
|
||||||
row = self.treestore.append(self.cat_nodes[cat],[cat, value, label, count , pix, True])
|
row = self.treestore.append(self.cat_nodes[cat], [cat, value, label, count, pix, True])
|
||||||
self.filters[(cat, value)] = row
|
self.filters[(cat, value)] = row
|
||||||
|
|
||||||
if cat == "tracker_host" and value not in ("All", "Error") and value:
|
if cat == "tracker_host" and value not in ("All", "Error") and value:
|
||||||
|
@ -231,9 +208,7 @@ class FilterTreeView(component.Component):
|
||||||
return row
|
return row
|
||||||
|
|
||||||
def render_cell_data(self, column, cell, model, row, data):
|
def render_cell_data(self, column, cell, model, row, data):
|
||||||
"cell renderer"
|
|
||||||
cat = model.get_value(row, 0)
|
cat = model.get_value(row, 0)
|
||||||
value = model.get_value(row, 1)
|
|
||||||
label = model.get_value(row, 2)
|
label = model.get_value(row, 2)
|
||||||
count = model.get_value(row, 3)
|
count = model.get_value(row, 3)
|
||||||
|
|
||||||
|
@ -245,21 +220,18 @@ class FilterTreeView(component.Component):
|
||||||
finally:
|
finally:
|
||||||
warnings.filters = original_filters
|
warnings.filters = original_filters
|
||||||
|
|
||||||
if pix:
|
self.cell_pix.set_property("visible", True if pix else False)
|
||||||
self.renderpix.set_property("visible", True)
|
|
||||||
else:
|
|
||||||
self.renderpix.set_property("visible", False)
|
|
||||||
|
|
||||||
if cat == "cat":
|
if cat == "cat":
|
||||||
txt = label
|
self.cell_count.set_property("visible", False)
|
||||||
cell.set_property("cell-background-gdk", self.colour_background)
|
cell.set_padding(10, 2)
|
||||||
cell.set_property("foreground-gdk", self.colour_foreground)
|
label = "<b>%s</b>" % label
|
||||||
else:
|
else:
|
||||||
txt = "%s (%s)" % (label, count)
|
count_txt = "<small>%s</small>" % count
|
||||||
cell.set_property("cell-background", None)
|
self.cell_count.set_property('markup', count_txt)
|
||||||
cell.set_property("foreground", None)
|
self.cell_count.set_property("visible", True)
|
||||||
|
cell.set_padding(2, 1)
|
||||||
cell.set_property('text', txt)
|
cell.set_property('markup', label)
|
||||||
|
|
||||||
def get_pixmap(self, cat, value):
|
def get_pixmap(self, cat, value):
|
||||||
pix = None
|
pix = None
|
||||||
|
@ -282,7 +254,7 @@ class FilterTreeView(component.Component):
|
||||||
|
|
||||||
def set_row_image(self, cat, value, filename):
|
def set_row_image(self, cat, value, filename):
|
||||||
pix = None
|
pix = None
|
||||||
try: #assume we could get trashed images here..
|
try: # assume we could get trashed images here..
|
||||||
pix = gtk.gdk.pixbuf_new_from_file_at_size(filename, 16, 16)
|
pix = gtk.gdk.pixbuf_new_from_file_at_size(filename, 16, 16)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
log.debug(e)
|
log.debug(e)
|
||||||
|
@ -293,10 +265,9 @@ class FilterTreeView(component.Component):
|
||||||
self.treestore.set_value(row, 4, pix)
|
self.treestore.set_value(row, 4, pix)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def on_selection_changed(self, selection):
|
def on_selection_changed(self, selection):
|
||||||
try:
|
try:
|
||||||
(model, row) = self.label_view.get_selection().get_selected()
|
(model, row) = self.treeview.get_selection().get_selected()
|
||||||
if not row:
|
if not row:
|
||||||
log.debug("nothing selected")
|
log.debug("nothing selected")
|
||||||
return
|
return
|
||||||
|
@ -322,18 +293,16 @@ class FilterTreeView(component.Component):
|
||||||
hide_cat = []
|
hide_cat = []
|
||||||
if not self.config["sidebar_show_trackers"]:
|
if not self.config["sidebar_show_trackers"]:
|
||||||
hide_cat = ["tracker_host"]
|
hide_cat = ["tracker_host"]
|
||||||
client.core.get_filter_tree(self.config["sidebar_show_zero"], hide_cat).addCallback(self.cb_update_filter_tree)
|
client.core.get_filter_tree(self.config["sidebar_show_zero"],
|
||||||
|
hide_cat).addCallback(self.cb_update_filter_tree)
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
log.debug(e)
|
log.debug(e)
|
||||||
|
|
||||||
|
|
||||||
### Callbacks ###
|
### Callbacks ###
|
||||||
def on_button_press_event(self, widget, event):
|
def on_button_press_event(self, widget, event):
|
||||||
"""This is a callback for showing the right-click context menu.
|
"""This is a callback for showing the right-click context menu."""
|
||||||
NOT YET!
|
|
||||||
"""
|
|
||||||
x, y = event.get_coords()
|
x, y = event.get_coords()
|
||||||
path = self.label_view.get_path_at_pos(int(x), int(y))
|
path = self.treeview.get_path_at_pos(int(x), int(y))
|
||||||
if not path:
|
if not path:
|
||||||
return
|
return
|
||||||
path = path[0]
|
path = path[0]
|
||||||
|
@ -342,20 +311,20 @@ class FilterTreeView(component.Component):
|
||||||
if event.button == 1:
|
if event.button == 1:
|
||||||
# Prevent selecting a category label
|
# Prevent selecting a category label
|
||||||
if cat == "cat":
|
if cat == "cat":
|
||||||
if self.label_view.row_expanded(path):
|
if self.treeview.row_expanded(path):
|
||||||
self.label_view.collapse_row(path)
|
self.treeview.collapse_row(path)
|
||||||
else:
|
else:
|
||||||
self.label_view.expand_row(path, False)
|
self.treeview.expand_row(path, False)
|
||||||
if not self.selected_path:
|
if not self.selected_path:
|
||||||
self.select_default_filter()
|
self.select_default_filter()
|
||||||
else:
|
else:
|
||||||
self.label_view.get_selection().select_path(self.selected_path)
|
self.treeview.get_selection().select_path(self.selected_path)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
elif event.button == 3:
|
elif event.button == 3:
|
||||||
#assign current cat, value to self:
|
#assign current cat, value to self:
|
||||||
x, y = event.get_coords()
|
x, y = event.get_coords()
|
||||||
path = self.label_view.get_path_at_pos(int(x), int(y))
|
path = self.treeview.get_path_at_pos(int(x), int(y))
|
||||||
if not path:
|
if not path:
|
||||||
return
|
return
|
||||||
row = self.model_filter.get_iter(path[0])
|
row = self.model_filter.get_iter(path[0])
|
||||||
|
@ -375,7 +344,7 @@ class FilterTreeView(component.Component):
|
||||||
|
|
||||||
def set_menu_sensitivity(self):
|
def set_menu_sensitivity(self):
|
||||||
#select-all/pause/resume
|
#select-all/pause/resume
|
||||||
sensitive = (self.cat != "cat" and self.count <> 0)
|
sensitive = (self.cat != "cat" and self.count != 0)
|
||||||
for item in self.default_menu_items:
|
for item in self.default_menu_items:
|
||||||
item.set_sensitive(sensitive)
|
item.set_sensitive(sensitive)
|
||||||
|
|
||||||
|
@ -402,4 +371,4 @@ class FilterTreeView(component.Component):
|
||||||
def select_default_filter(self):
|
def select_default_filter(self):
|
||||||
row = self.filters[("state", "All")]
|
row = self.filters[("state", "All")]
|
||||||
path = self.treestore.get_path(row)
|
path = self.treestore.get_path(row)
|
||||||
self.label_view.get_selection().select_path(path)
|
self.treeview.get_selection().select_path(path)
|
||||||
|
|
Loading…
Reference in New Issue