import gtk
import cairo
import sys
import os
from fringwalker import FringWalker
from fringrenderer import FringRenderer
GPL = """
fring is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
fring 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 Lesser General Public License
along with fring; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA.
"""
ui = """
"""
class UI( gtk.Window ):
def __init__(self, path):
self.busy_cursor = 0
self.backgroundColour = (1,1,1)
self.data = None
self.zoomfactor = 1.0
self.renderer = FringRenderer()
self.walker = FringWalker();
# create gui
gtk.Window.__init__(self)
self.set_title("fring");
self.__init_gui(path)
# walk directory
self.walker.connect("list-changed",self.__list_changed)
self.walker.connect("finished", self.__walker_finished)
self.open_folder(path)
def __init_gui(self, path):
# create menubar
uimanager = gtk.UIManager()
accelgroup = uimanager.get_accel_group()
self.add_accel_group(accelgroup)
ag_global = gtk.ActionGroup('ag_global')
ag_global.add_actions([
('FRing', None, "_Folder"),
('View', None, "_View"),
('Help', None, "_Help"),
('OpenParent', gtk.STOCK_GO_UP, "Open _Parent", "Up", "Open the parent folder", self.open_parent),
('SelectFolder', gtk.STOCK_OPEN, "_Open Folder...", None, "Open Folder...", self.__selectfolder),
('Quit', gtk.STOCK_QUIT, "_Quit", None, "Quit", self.close),
('Zoom In', gtk.STOCK_ZOOM_IN, "Zoom _In", "Up", "Zoom In", lambda w: self.zoom(w,0.2)),
('Zoom Out', gtk.STOCK_ZOOM_OUT, "Zoom _Out", "Down", "Zoom Out", lambda w: self.zoom(w,-0.2)),
('Save Image...', gtk.STOCK_SAVE_AS, "_Save Image...", None, "Save Image...", self.save_image),
('Refresh', gtk.STOCK_REFRESH, "_Refresh Tree", "R", "Refresh Tree", lambda w: self.__set_path(self.path)),
('About', gtk.STOCK_ABOUT, "_About", None, "About", self.about_dialog),
])
action = gtk.ToggleAction("Show Hidden Files", "Show Hidden Files", None, None)
action.connect("toggled",self.__hidden_files_toggled)
ag_global.add_action(action)
uimanager.insert_action_group(ag_global, 0)
uimanager.add_ui_from_string(ui)
menubar = uimanager.get_widget('/MenuBar')
menubar.show()
# create toolbar
toolbar = gtk.HBox()
b = gtk.Button()
img = gtk.image_new_from_stock(gtk.STOCK_GO_UP, gtk.ICON_SIZE_BUTTON)
b.set_property("image", img)
uimanager.get_action("/MenuBar/FRing/OpenParent").connect_proxy(b)
toolbar.pack_start( b, False )
b = gtk.FileChooserButton('Select a Folder')
b.set_action(gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
b.connect("current-folder-changed", lambda w: self.__set_path(w.get_current_folder()))
toolbar.pack_start( b, True )
self.filechooserbutton = b
# create canvas
self.eventbox = gtk.EventBox()
self.image = gtk.Image()
self.eventbox.add(self.image)
self.eventbox.add_events( gtk.gdk.EXPOSURE_MASK
| gtk.gdk.LEAVE_NOTIFY_MASK
| gtk.gdk.BUTTON_PRESS_MASK
| gtk.gdk.POINTER_MOTION_MASK )
self.eventbox.connect("scroll_event", self.__scroll_event)
self.eventbox.connect("button-press-event",self.__click_event)
self.eventbox.connect("motion-notify-event",self.__move_event)
# aligh menubar, toolbar and canvas vertically
vbox = gtk.VBox();
vbox.pack_start(menubar,False,False)
vbox.pack_start(toolbar,False,False)
f = gtk.Frame()
f.set_shadow_type(gtk.SHADOW_IN)
f.add(self.eventbox)
vbox.pack_start(f)
self.add(vbox)
# show everything
# Note: With set_default_size, the image can't be downsized.
self.set_size_request(100,100) # minimum size, needed!
self.resize(500,350)
self.show_all()
self.__create_canvas()
self.eventbox.connect("size-allocate",self.__on_resize)
self.connect("delete_event", self.close)
self.__show_busy_cursor(1)
def open_folder(self,path):
print "open",path
# change folder by changing the chooser button
self.filechooserbutton.set_current_folder(path)
def about_dialog(self,widget):
d = gtk.AboutDialog()
d.set_name("fring");
d.set_license(GPL);
d.set_copyright("copyright 2006 Lennart Poettering, Frederic Back")
d.run()
d.destroy()
def close(self,w=None,e=None):
self.walker.stop()
self.destroy()
gtk.main_quit()
def open_parent(self,widget):
p = os.path.split(self.path)
p = os.path.join(p[:-1])
self.open_folder(p[0])
def zoom(self,widget,factor):
self.zoomfactor += factor
if self.zoomfactor < 0:
self.zoomfactor = 0
self.redraw()
def redraw(self,widget=None):
# clear the canvas
self.__show_busy_cursor(1)
self.ctx.rectangle(0,0,self.width,self.height)
self.ctx.set_source_rgb(*self.backgroundColour)
self.ctx.fill()
# draw segments
if not self.data: return
self.renderer.WIDTH = self.width
self.renderer.HEIGHT = self.height
self.renderer.INNER_RADIUS = self.height/15
self.renderer.RING_RADIUS = self.height/10
#self.renderer.INNER_RADIUS *= self.zoomfactor
self.renderer.RING_RADIUS *= self.zoomfactor
self.renderer.RINGS_MAX = 3
self.renderer.LABEL_UNTIL_RING = int(self.zoomfactor - 0.4)
if self.zoomfactor < 0.8: self.renderer.LABEL_UNTIL_RING = -1
self.renderer.hotspots = []
self.renderer.draw_segment(self.ctx, 0, 0, 1, 0, 1, self.data, self.path)
self.image.queue_draw()
self.__show_busy_cursor(-1)
def save_image(self, widget=None):
d = gtk.FileChooserDialog("Save As...",
self.get_toplevel(), gtk.FILE_CHOOSER_ACTION_SAVE,
(gtk.STOCK_CANCEL,0,gtk.STOCK_OK,1), None)
d.set_current_name( self.data.name.replace(" ","_") + ".png" )
r = d.run()
if r == 1:
#print d.get_uri()
self.__show_busy_cursor(1)
pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB,True,8,self.width,self.height)
pixmap, mask = self.image.get_pixmap()
pixbuf.get_from_drawable(pixmap,pixmap.get_colormap(),0,0,0,0,-1,-1)
#pixbuf.save(d.get_filename(), "jpeg", {"quality":"100"})
pixbuf.save(d.get_filename(), "png", {})
self.__show_busy_cursor(-1)
d.destroy()
#----------------------------------------------------------- private methods
def __set_path(self,path):
""" Set a path and start parsing. Also used to refresh or reset. """
self.path = path
self.walker.stop()
if path is None: return
self.walker.walk(path);
def __selectfolder(self,widget):
""" Open a dialog to select a folder """
d = gtk.FileChooserDialog(None, self,
gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER,
(gtk.STOCK_CANCEL,0,gtk.STOCK_OK,1))
if d.run() == 1: self.open_folder(d.get_current_folder())
d.destroy()
def __hidden_files_toggled(self,widget):
if self.walker.showhidden == widget.get_active(): return
self.walker.showhidden = widget.get_active()
self.__set_path(self.path)
def __move_event(self, widget, event):
if self.busy_cursor > 0:
return
f = self.renderer.get_hotspot_at(event.x, event.y)
if f is None:
self.eventbox.window.set_cursor(None) #gtk.gdk.Cursor(gtk.gdk.CROSSHAIR))
else:
self.eventbox.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.HAND1))
def __click_event(self, widget, event):
f = self.renderer.get_hotspot_at(event.x, event.y)
if f is not None: self.open_folder(f)
def __scroll_event(self, widget, event):
if event.direction is gtk.gdk.SCROLL_UP:
self.zoom(widget, 0.2)
elif event.direction is gtk.gdk.SCROLL_DOWN:
self.zoom(widget, -0.2)
def __create_canvas(self):
# create a new pixmap and cairo context
r = self.eventbox.get_allocation()
self.width, self.height = r.width, r.height
self.pixmap = gtk.gdk.Pixmap(self.window,r.width,r.height)
self.image.set_from_pixmap(self.pixmap,None)
self.ctx = self.pixmap.cairo_create()
self.renderer.prepare_layouts(self.ctx)
def __list_changed(self,widget,data):
self.data = data
self.redraw()
#print data.name
def __walker_finished(self, widget, data):
self.__show_busy_cursor(-1)
def __on_resize(self, widget, event):
r = self.eventbox.get_allocation()
if (r.width,r.height) != (self.width,self.height):
self.__create_canvas()
self.redraw()
def __show_busy_cursor(self, value):
if self.busy_cursor <= 0 and self.busy_cursor+value >= 0:
self.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
elif self.busy_cursor > 0 and self.busy_cursor+value <= 0:
self.window.set_cursor(None)
self.busy_cursor += value