# This file contains utility classes and functions used by the GUI # Many of them 'cover up' differences between pygtk versions - these # follow the general pattern # # try: # do new thing # except: # fall back to the 'old way' import os import sys import inspect import gtk import gobject threads_enabled = False break_new_things = False def force_throwback(): """Used for unit testing the 'old style' code - make the new stuff break even if it would normally work. Call this to revert to pygtk 2.0 style""" global break_new_things break_new_things = True def unforce_throwback(): global break_new_things break_new_things = False def _throwback(): if break_new_things: raise AttributeError("Forcing use of old code") def threads_enter(): if threads_enabled: gtk.gdk.threads_enter() def threads_leave(): if threads_enabled: gtk.gdk.threads_leave() def idle_wrapper(callable, *args): threads_enter() callable(*args) threads_leave() def idle_add(callable, *args): """A wrapper around gtk.idle_add which wraps the callback in threads_enter/threads_leave if required""" try: _throwback() gobject.idle_add(idle_wrapper, callable, *args) except AttributeError: gtk.idle_add(idle_wrapper, callable, *args) def timeout_add(time,callable): try: _throwback() gobject.timeout_add(time,callable) except AttributeError: gtk.timeout_add(time,callable) def input_add(fd,cb): try: _throwback() return gobject.io_add_watch( fd, gobject.IO_IN | gobject.IO_HUP, cb) except AttributeError, err: return gtk.input_add(fd, gtk.gdk.INPUT_READ, cb) def find_resource(name, local_dir, installed_dir): 'try and find a file either locally or installed' local_name = os.path.join(local_dir,name) if os.path.exists(local_name): return local_name return os.path.join(sys.exec_prefix, installed_dir, name) def find_in_path(exe): # find an executable along PATH env var pathstring = os.environ["PATH"] if pathstring == None or pathstring == "": return None paths = pathstring.split(":") for path in paths: full_path = os.path.join(path,exe) if os.path.exists(full_path): return full_path return None def stack_trace(): stack = inspect.stack() str = "" for frame in stack[1:]: (frame_obj, filename, line, funcname, context, context_index) = frame try: args = inspect.formatargvalues(*inspect.getargvalues(frame_obj)) except Exception: args = "" frame_desc = "%s(%s)\t\t%s(%s)\n" % (filename, line, funcname, args) str += frame_desc return str def get_rgb_colormap(): # work around a difference between pygtk versions if hasattr(gtk.gdk,'rgb_get_colormap'): c = gtk.gdk.rgb_get_colormap() else: c = gtk.gdk.rgb_get_cmap() return c def get_directory_chooser(title,parent): try: _throwback() chooser = gtk.FileChooserDialog( title, parent, gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER, (gtk.STOCK_OK, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)) return chooser except: return gtk.FileSelection(title) def get_file_save_chooser(title, parent, patterns=[], extra_widget=None): try: _throwback() chooser = gtk.FileChooserDialog( title, parent, gtk.FILE_CHOOSER_ACTION_SAVE, (gtk.STOCK_OK, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)) filter = gtk.FileFilter() for pattern in patterns: filter.add_pattern(pattern) chooser.set_filter(filter) if extra_widget != None: chooser.set_extra_widget(extra_widget) return chooser except: fs = gtk.FileSelection(title) if extra_widget != None: fs.action_area.pack_start(extra_widget) return fs def get_file_chooser_extra_widget(chooser): try: _throwback() return chooser.get_extra_widget() except: return chooser.action_area.get_children()[0] def set_file_chooser_filename(chooser,name): try: _throwback() if name: chooser.set_current_folder(os.path.abspath(os.path.dirname(name))) chooser.set_current_name(os.path.basename(name)) except: pass def get_file_open_chooser(title, parent, patterns=[]): try: _throwback() chooser = gtk.FileChooserDialog( title, parent, gtk.FILE_CHOOSER_ACTION_OPEN, (gtk.STOCK_OK, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)) filter = gtk.FileFilter() for pattern in patterns: filter.add_pattern(pattern) chooser.set_filter(filter) return chooser except: return gtk.FileSelection(title) def file_chooser_set_preview(chooser, preview, preview_cb): try: _throwback() chooser.set_preview_widget(preview.widget) chooser.connect('update-preview', preview_cb, preview) except AttributeError, err: # not supported, skip pass def create_option_menu(items): try: _throwback() widget = gtk.combo_box_new_text() for item in items: widget.append_text(item) except Exception, exn: widget = gtk.OptionMenu() widget.item_list = items # for get_selected_value menu = gtk.Menu() for item in items: mi = gtk.MenuItem(item) menu.append(mi) widget.set_menu(menu) return widget def set_menu_from_list(menu, items): try: _throwback() model = menu.get_model().clear() for item in items: menu.append_text(item) except: menu.item_list = items submenu = menu.get_menu() for child in submenu.get_children(): submenu.remove(child) for item in items: mi = gtk.MenuItem(item) submenu.append(mi) def add_menu_item(menu, item): try: _throwback() menu.append_text(item) except: menu.get_menu().append(gtk.MenuItem(item)) def set_selected(menu, i): try: _throwback() menu.set_active(int(i)) except: menu.set_history(int(i)) def get_selected(menu): try: _throwback() return menu.get_active() except: return menu.get_history() def get_selected_value(menu): try: _throwback() iter = menu.get_active_iter() val = menu.get_model().get_value(iter,0) return val except: return menu.item_list[menu.get_history()] def set_selected_value(menu,val): try: _throwback() model = menu.get_model() i = 0 iter = model.get_iter_first() while iter != None: item = model.get_value(iter,0) if item == val: menu.set_active(i) return iter = model.iter_next(iter) i += 1 except: i = menu.item_list.index(val) menu.set_history(i) def create_color(r,g,b): # multiply up to match range expected by gtk try: _throwback() return gtk.gdk.Color(int(r*65535),int(g*65535),int(b*65535)) except Exception, exn: # old gtk doesn't have direct color constructor return gtk.gdk.color_parse( "#%04X%04X%04X" % (int(r*65535),int(g*65535),int(b*65535))) def floatColorFrom256(rgba): return [ rgba[0]/255.0, rgba[1]/255.0, rgba[2]/255.0, rgba[3]/255.0] def updateColor256FromFloat(r,g,b,color): return (int(r*255), int(g*255), int(b*255), color[3]) def launch_browser(prefs, url, window): browser = prefs.get("helpers","browser") cmd = browser % ('"' + url + '" &') try: os.system(cmd) except Exception, err: d = hig.ErrorAlert( primary=_("Error launching browser"), secondary=_("Try modifying your preferences or copy the URL manually to a browser window.\n") + \ str(err), parent=window) d.run() d.destroy() class ColorButton: def __init__(self,rgb, changed_cb, is_left): self.area = None self.set_color(rgb) self.changed_cb = changed_cb self.is_left = is_left try: _throwback() self.widget = gtk.ColorButton(self.color) def color_set(widget): color = widget.get_color() self.color_changed(color) self.widget.connect('color-set', self.on_color_set) except: # This GTK is too old to support ColorButton directly, fake one self.widget = gtk.Button() self.area = gtk.DrawingArea() self.area.set_size_request(16,10) self.widget.add(self.area) self.area.connect('expose_event', self.on_expose_event) self.csel_dialog = gtk.ColorSelectionDialog(_("Select a Color")) self.widget.connect('clicked', self.run_colorsel) def on_color_set(self, widget): self.color_changed(widget.get_color()) def set_color(self, rgb): self.color = create_color(rgb[0], rgb[1], rgb[2]) try: _throwback() self.widget.set_color(self.color) except: #print "sc", self.area, rgb if self.area: self.area_expose( self.area, 0,0, self.area.allocation.width,self.area.allocation.height) def on_expose_event(self, widget, event): r = event.area self.area_expose(widget, r.x, r.y, r.width, r.height) def area_expose(self, widget, x, y, w, h): if not widget.window: return gc = widget.window.new_gc(fill=gtk.gdk.SOLID) self.color = widget.get_colormap().alloc_color( self.color.red, self.color.green, self.color.blue) gc.set_foreground(self.color) widget.window.draw_rectangle(gc, True, x, y, w, h) def run_colorsel(self, widget): dlg = self.csel_dialog dlg.colorsel.set_current_color(self.color) result = dlg.run() if result == gtk.RESPONSE_OK: self.color = dlg.colorsel.get_current_color() self.color_changed(self.color) self.csel_dialog.hide() def set_sensitive(self,x): self.widget.set_sensitive(x) def color_changed(self,color): self.color = color self.changed_cb( color.red/65535.0, color.green/65535.0, color.blue/65535.0, self.is_left)