"""
Pathbar implementation in Python.
All mentions of 'path' in this assume a
filereader.Path() object, not a string.
They are using GTK_ARROW_LEFT and GTK_ARROW_RIGHT
for the arrow 'slider' buttons in nautilus-pathbar.c
"""
import os, gtk, pango, gobject
import fsutil, mime
from filereader import Path
from helpers import Property
def ignore_clicks(f):
def func(self, *args):
if self._ignore_clicks: return
self._ignore_clicks = True
f(self, *args)
self._ignore_clicks = False
return func
def get_true_label_size(label):
"""Given a label, returns the maximum size request (x, y) for
that label were it to be bolded. The nautilus pathbar way is
to take the max of the original pixel size and the bold one;
I just take the bold one. It looks more like Thunar."""
layout = label.get_layout()
path = label.get_text()
layout.set_markup('%s' % path)
return layout.get_pixel_size()
class PathBar(gtk.HBox):
def __init__(self):
gtk.HBox.__init__(self)
self.set_spacing(3)
self.__path = None
self._ignore_clicks = False
self.show()
@Property
def path():
def fget(self):
return self.__path
def fset(self, path):
self.__rebuild(path)
@ignore_clicks
def __pathButtonClicked(self, button):
for child in self.get_children():
if isinstance(child, PathButton):
child.set_active(False)
button.set_active()
self.emit('directory-changed', button.get_path())
def __rebuild(self, path):
# remove all existing buttons
self.foreach(lambda child: child.destroy())
icon_size = gtk.icon_size_lookup(gtk.ICON_SIZE_BUTTON)[0]
align = gtk.Alignment(1.0, 1.0, 1.0, 1.0)
align.set_property('width-request', 16)
self.pack_start(align, False, False, 0)
align.show()
self.__path = path
larrow = ArrowButton(gtk.ARROW_LEFT)
rarrow = ArrowButton(gtk.ARROW_RIGHT)
self.pack_start(larrow, False, False, 0)
self.reorder_child(larrow, 0)
larrow.show()
for _path in path.parents():
button = PathButton(_path)
button.connect('clicked', lambda button: self.__pathButtonClicked(button))
self.pack_start(button, False, False, 0)
self.reorder_child(button, 0)
if _path == path: button.set_active(True)
button.show()
self.pack_start(rarrow, False, False, 0)
self.reorder_child(rarrow, 0)
rarrow.show()
class ArrowButton(gtk.Button):
def __init__(self, direction, shadow=gtk.SHADOW_OUT):
gtk.Button.__init__(self)
self.set_focus_on_click(False)
arrow = gtk.Arrow(direction, shadow)
self.add(arrow)
self.__arrow = arrow
def show(self):
self.__arrow.show()
gtk.Button.show(self)
class PathButton(gtk.ToggleButton):
def __init__(self, path):
gtk.ToggleButton.__init__(self)
self.set_focus_on_click(False)
self.__path = path
icon, label = self.__icon_label()
# create a horizontal box to hold the label and possibly an icon
hbox = gtk.HBox(False, 2)
self.add(hbox)
hbox.pack_start(icon, False, False, 0)
hbox.pack_start(label, True, True, 0)
self.__label, self.__icon, self.__hbox = label, icon, hbox
def show(self):
"""Show/unshow self and all components."""
self.__label.show()
self.__icon.show()
self.__hbox.show()
gtk.ToggleButton.show(self)
def hide(self):
self.__label.hide()
self.__icon.hide()
self.__hbox.hide()
gtk.ToggleButton.hide(self)
def set_active(self, active=True):
"""Take care of bolding or unbolding the text in the button."""
# if we are not changing state, don't do anything
# TODO: profile this optimisation to see if it's worth it
if self.get_active() == active: return
if active:
self.__label.set_text("%s" % self.__label_text())
self.__label.set_use_markup(True)
else: self.__label.set_text(self.__label_text())
gtk.ToggleButton.set_active(self, active)
def get_path(self): return self.__path
def __label_text(self):
if self.__path.path != '/': return self.__path.escapedName()
else: return ''
def __icon_label(self):
"""Create the appropriate icon & label for this button.
@returns: gtk.Image, gtk.Label"""
path = self.__path
if path.icon: icon = gtk.image_new_from_icon_name(path.icon, gtk.ICON_SIZE_MENU)
else: icon = gtk.Image()
text = path.escapedName()
if text == '/': return icon, gtk.Label('')
label = gtk.Label(text)
label.set_size_request(*get_true_label_size(label))
label.set_use_markup(True)
return icon, label
class FileView(gtk.TreeView):
def __init__(self, showHidden=False):
gtk.TreeView.__init__(self)
self.__options = {'showHidden' : showHidden, }
self.__initFileView()
self.__initColumns()
self.__initStore()
self.__initSignals()
self.__fileReader = None
self.show()
@Property
def fileReader():
def fget(self):
return self.__fileReader
def fset(self, fr):
self.__rebuild(fr)
def __initFileView(self):
"""Set some default options we want on the view."""
# Allow multiple selection
self.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
# Allow rubber banding (Gtk 2.10+) (this is really crappy)
#if hasattr(self, 'set_rubber_banding'):
# self.set_rubber_banding(True)
def __initColumns(self):
n = gtk.TreeViewColumn('Name')
s = gtk.TreeViewColumn('Size', gtk.CellRendererText(), text=2)
n.pack_start(gtk.CellRendererPixbuf(), False)
n.pack_start(gtk.CellRendererText(), True)
n.set_attributes(n.get_cell_renderers()[0], pixbuf=0)
n.set_attributes(n.get_cell_renderers()[1], text=1)
n.set_expand(True)
n.resizeable = True
n.set_property('sort-indicator', True)
# align this to the right
s.get_cell_renderers()[0].set_property('xalign', 1.0)
s.set_alignment(1.0)
s.set_expand(False)
s.resizeable = False
self.append_column(n)
self.append_column(s)
def __initStore(self):
s = gtk.ListStore(gtk.gdk.Pixbuf, str, str, gobject.TYPE_OBJECT)
self.set_model(s)
def __initSignals(self):
self.connect('row_activated', self._on_row_activated)
def __rebuild(self, reader):
store = self.get_model()
store.clear()
self.__fileReader = reader
for entry in reader.directories + reader.files:
if not entry.isHidden or self.__options['showHidden']:
store.append((entry.icon, entry.name, entry.humanSize(), entry.path))
self.columns_autosize()
# signal handlers
def _on_row_activated(self, *args):
#args = [treeview (self), position, column]
view, position, column = args[0], args[1], args[2]
position = position[0]
model = self.get_model()
iter = model.get_iter(position)
val = lambda x: model.get_value(iter, x)
# str, str, Path
name, size, path = val(1), val(2), val(3)
print name, size, path
if path.isdir():
self.emit('directory-changed', path)
gobject.type_register(FileView)
gobject.type_register(PathBar)
gobject.type_register(PathButton)
gobject.signal_new('directory-changed', FileView, \
gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, \
[Path])
gobject.signal_new('directory-changed', PathBar, \
gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, \
[Path])