# # Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 2000-2007 Donald N. Allingham # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program 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 this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # $Id: _MyGrampsView.py $ """ MyGrampsView interface """ __author__ = "Doug Blank" __revision__ = "$Revision: $" import gtk import gobject import traceback import time import types import pango import os from gettext import gettext as _ import Errors import const import PageView import ManagedWindow import ConfigParser import Utils from QuickReports import run_quick_report_by_name AVAILABLE_GADGETS = {} GADGET_FILENAME = os.path.join(const.HOME_DIR,"gadgets.ini") NL = "\n" debug = False def register_gadget(data_dict): global AVAILABLE_GADGETS base_opts = {"name":"Unnamed Gadget", "tname": _("Unnamed Gadget"), "state":"maximized", "column": -1, "row": -1, "data": []} base_opts.update(data_dict) AVAILABLE_GADGETS[base_opts["name"]] = base_opts def register(**data): if "type" in data: if data["type"].lower() == "gadget": register_gadget(data) else: print ("Unknown plugin type: '%s'" % data["type"]) else: print ("Plugin did not define type.") def get_gadget_opts(name, opts): if name in AVAILABLE_GADGETS: data = AVAILABLE_GADGETS[name] my_data = data.copy() my_data.update(opts) return my_data else: print ("Unknown gadget name: '%s'" % name) return {} def get_gadget_options_by_name(name): if debug: print "name:", name if name in AVAILABLE_GADGETS: return AVAILABLE_GADGETS[name].copy() else: print ("Unknown gadget name: '%s'" % name) return None def get_gadget_options_by_tname(name): if debug: print "name:", name for key in AVAILABLE_GADGETS: if AVAILABLE_GADGETS[key]["tname"] == name: return AVAILABLE_GADGETS[key].copy() print ("Unknown gadget name: '%s'" % name) return None def make_requested_gadget(viewpage, name, opts, dbstate, uistate): if name in AVAILABLE_GADGETS: gui = GuiGadget(viewpage, dbstate, uistate, **opts) if opts.get("content", None): opts["content"](gui) # now that we have user code, set the tooltips msg = None if gui.pui: msg = gui.pui.tooltip if msg == None: msg = _("Drag Properties Button to move and click it for setup") if msg: gui.tooltips = gtk.Tooltips() gui.tooltips.set_tip(gui.scrolledwindow, msg) return gui return None class LinkTag(gtk.TextTag): lid = 0 def __init__(self, buffer): LinkTag.lid += 1 gtk.TextTag.__init__(self, str(LinkTag.lid)) tag_table = buffer.get_tag_table() self.set_property('foreground', "#0000ff") #self.set_property('underline', pango.UNDERLINE_SINGLE) tag_table.add(self) class GadgetWindow(ManagedWindow.ManagedWindow): def __init__(self, gadget): self.title = gadget.title + " Gadget" self.gadget = gadget ManagedWindow.ManagedWindow.__init__(self, gadget.uistate, [], gadget) self.set_window(gtk.Dialog("",gadget.uistate.window, gtk.DIALOG_DESTROY_WITH_PARENT, (gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE)), None, self.title) self.window.set_size_request(400,300) self.window.connect('response', self.close) self.gadget.mainframe.reparent(self.window.vbox) self.window.show() def build_menu_names(self, obj): return (self.title, 'Gadget') def get_title(self): return self.title def close(self, *args): """ Closes the detached GadgetWindow. """ self.gadget.gvclose.show() self.gadget.gvstate.show() self.gadget.gvproperties.show() self.gadget.viewpage.detached_gadgets.remove(self.gadget) self.gadget.state = "maximized" self.gadget.mainframe.reparent(self.gadget.parent) expand,fill,padding,pack = self.gadget.parent.query_child_packing(self.gadget.mainframe) self.gadget.parent.set_child_packing(self.gadget.mainframe,self.gadget.expand,fill,padding,pack) ManagedWindow.ManagedWindow.close(self, *args) #------------------------------------------------------------------------ class Gadget(object): def __init__(self, gui): self._idle_id = 0 self._generator = None self._need_to_update = False self._tags = [] self.tooltip = None self.link_cursor = gtk.gdk.Cursor(gtk.gdk.LEFT_PTR) self.standard_cursor = gtk.gdk.Cursor(gtk.gdk.XTERM) # links to each other: self.gui = gui # plugin gadget has link to gui gui.pui = self # gui has link to plugin ui self.dbstate = gui.dbstate self.init() self.on_load() self.dbstate.connect('database-changed', self._db_changed) self.dbstate.connect('active-changed', self.active_changed) self.gui.textview.connect('button-press-event', self.on_button_press) self.gui.textview.connect('motion-notify-event', self.on_motion) if self.dbstate.active: # already changed self._db_changed(self.dbstate.db) self.active_changed(self.dbstate.active.handle) def init(self): # once, constructor pass def main(self): # return false finishes """ Generator which will be run in the background. """ if debug: print "%s dummy" % self.gui.title yield False def on_load(self): """ Gadgets should override this to take care of loading previously their special data. """ pass def on_save(self): """ Gadgets should override this to take care of saving their special data. """ if debug: print ("on_save: '%s'" % self.gui.title) return def active_changed(self, handle): pass def db_changed(self): if debug: print "%s is connecting" % self.gui.title pass def link(self, text, link_type, data): buffer = self.gui.buffer iter = buffer.get_end_iter() offset = buffer.get_char_count() self.append_text(text) start = buffer.get_iter_at_offset(offset) end = buffer.get_end_iter() self._tags.append((LinkTag(buffer), link_type, data)) buffer.apply_tag(self._tags[-1][0], start, end) def get_text(self): start = self.gui.buffer.get_start_iter() end = self.gui.buffer.get_end_iter() return self.gui.buffer.get_text(start, end) def insert_text(self, text): self.gui.buffer.insert_at_cursor(text) def clear_text(self): self.gui.buffer.set_text('') def set_text(self, text, scroll_to='start'): self.gui.buffer.set_text('') self.append_text(text, scroll_to) def append_text(self, text, scroll_to="end"): enditer = self.gui.buffer.get_end_iter() start = self.gui.buffer.create_mark(None, enditer, True) self.gui.buffer.insert(enditer, text) if scroll_to == "end": enditer = self.gui.buffer.get_end_iter() end = self.gui.buffer.create_mark(None, enditer, True) self.gui.textview.scroll_to_mark(end, 0.0, True, 0, 0) else: self.gui.textview.scroll_to_mark(start, 0.0, True, 0, 0) def load_data_to_text(self, pos=0): if len(self.gui.data) >= pos + 1: text = self.gui.data[pos] text = text.replace("\\n", chr(10)) self.set_text(text, 'end') def save_text_to_data(self): text = self.get_text() text = text.replace(chr(10), "\\n") self.gui.data.append(text) def update(self, *args): if self._idle_id != 0: if debug: print "%s interrupt!" % self.gui.title self.interrupt() if debug: print "%s creating generator" % self.gui.title self._generator = self.main() if debug: print "%s adding to gobject" % self.gui.title self._idle_id = gobject.idle_add(self._updater, priority=gobject.PRIORITY_LOW - 10) def interrupt(self): """ Force the generator to stop running. """ if self._idle_id == 0: if debug: print "%s removing from gobject" % self.gui.title gobject.source_remove(self._idle_id) self._idle_id = 0 def _db_changed(self, db): if debug: print "%s is _connecting" % self.gui.title self.dbstate.db = db self.gui.dbstate.db = db self.db_changed() self.update() def _updater(self): """ Runs the generator. """ if debug: print "%s _updater" % self.gui.title if type(self._generator) != types.GeneratorType: self._idle_id = 0 return False try: retval = self._generator.next() if retval == False: self._idle_id = 0 return retval except StopIteration: self._idle_id = 0 return False except Exception, e: print "Gadget gave an error" traceback.print_exc() print "Continuing after gadget error..." self._idle_id = 0 return False def on_motion(self, view, event): buffer_location = view.window_to_buffer_coords(gtk.TEXT_WINDOW_TEXT, int(event.x), int(event.y)) iter = view.get_iter_at_location(*buffer_location) cursor = self.standard_cursor for (tag, link_type, handle) in self._tags: if iter.has_tag(tag): tag.set_property('underline', pango.UNDERLINE_SINGLE) cursor = self.link_cursor else: tag.set_property('underline', pango.UNDERLINE_NONE) view.get_window(gtk.TEXT_WINDOW_TEXT).set_cursor(cursor) return False # handle event further, if necessary def on_button_press(self, view, event): from Editors import EditPerson buffer_location = view.window_to_buffer_coords(gtk.TEXT_WINDOW_TEXT, int(event.x), int(event.y)) iter = view.get_iter_at_location(*buffer_location) for (tag, link_type, handle) in self._tags: if iter.has_tag(tag): if link_type == 'Person': person = self.dbstate.db.get_person_from_handle(handle) if person != None: if event.button == 1: # left mouse if event.type == gtk.gdk._2BUTTON_PRESS: # double try: EditPerson(self.gui.dbstate, self.gui.uistate, [], person) return True # handled event except Errors.WindowActiveError: pass else: # single click self.gui.dbstate.change_active_person(person) return True # handled event elif link_type == 'Surname': if event.button == 1: # left mouse if event.type == gtk.gdk._2BUTTON_PRESS: # double run_quick_report_by_name(self.gui.dbstate, self.gui.uistate, 'samesurnames', handle) return True return False # did not handle event def logical_true(value): return value in ["True", True, 1, "1"] class GuiGadget: """ Class that handles the plugin interfaces for the MyGrampsView. """ TARGET_TYPE_FRAME = 80 LOCAL_DRAG_TYPE = 'GADGET' LOCAL_DRAG_TARGET = (LOCAL_DRAG_TYPE, 0, TARGET_TYPE_FRAME) def __init__(self, viewpage, dbstate, uistate, title, **kwargs): self.viewpage = viewpage self.dbstate = dbstate self.uistate = uistate self.title = title ########## Set defaults self.name = kwargs.get("name", "Unnamed Gadget") self.expand = logical_true(kwargs.get("expand", False)) self.height = int(kwargs.get("height", 200)) self.column = int(kwargs.get("column", -1)) self.row = int(kwargs.get("row", -1)) self.state = kwargs.get("state", "maximized") self.data = kwargs.get("data", []) ########## self.pui = None # user code self.xml = gtk.glade.XML(const.GLADE_FILE, 'gvgadget', "gramps") self.mainframe = self.xml.get_widget('gvgadget') self.textview = self.xml.get_widget('gvtextview') self.buffer = self.textview.get_buffer() self.scrolledwindow = self.xml.get_widget('gvscrolledwindow') self.titlelabel = self.xml.get_widget('gvtitle') self.titlelabel.set_text("%s" % self.title) self.titlelabel.set_use_markup(True) self.gvclose = self.xml.get_widget('gvclose') self.gvclose.connect('clicked', self.close) self.gvstate = self.xml.get_widget('gvstate') self.gvstate.connect('clicked', self.change_state) self.gvproperties = self.xml.get_widget('gvproperties') self.gvproperties.connect('clicked', self.set_properties) self.xml.get_widget('gvcloseimage').set_from_stock(gtk.STOCK_CLOSE, gtk.ICON_SIZE_MENU) self.xml.get_widget('gvstateimage').set_from_stock(gtk.STOCK_REMOVE, gtk.ICON_SIZE_MENU) self.xml.get_widget('gvpropertiesimage').set_from_stock(gtk.STOCK_PROPERTIES, gtk.ICON_SIZE_MENU) # source: drag = self.gvproperties drag.drag_source_set(gtk.gdk.BUTTON1_MASK, [GuiGadget.LOCAL_DRAG_TARGET], gtk.gdk.ACTION_COPY) def close(self, *obj): if self.state == "windowed": return self.state = "closed" self.viewpage.closed_gadgets.append(self) self.mainframe.get_parent().remove(self.mainframe) def detach(self): # hide buttons: self.set_state("maximized") self.gvclose.hide() self.gvstate.hide() self.gvproperties.hide() # keep a pointer to old parent frame: self.parent = self.mainframe.get_parent() self.viewpage.detached_gadgets.append(self) # make a window, and attach it there self.detached_window = GadgetWindow(self) self.state = "windowed" def set_state(self, state): self.state = state if state == "minimized": self.scrolledwindow.hide() self.xml.get_widget('gvstateimage').set_from_stock(gtk.STOCK_ADD, gtk.ICON_SIZE_MENU) column = self.mainframe.get_parent() # column expand,fill,padding,pack = column.query_child_packing(self.mainframe) column.set_child_packing(self.mainframe,False,fill,padding,pack) else: self.scrolledwindow.show() self.xml.get_widget('gvstateimage').set_from_stock(gtk.STOCK_REMOVE, gtk.ICON_SIZE_MENU) column = self.mainframe.get_parent() # column expand,fill,padding,pack = column.query_child_packing(self.mainframe) column.set_child_packing(self.mainframe,self.expand,fill,padding,pack) def change_state(self, obj): if self.state == "windowed": pass # don't change if windowed else: if self.state == "maximized": self.set_state("minimized") else: self.set_state("maximized") def set_properties(self, obj): if self.state == "windowed": pass else: self.detach() return # FIXME: add control for expand AND detach self.expand = not self.expand if self.state == "maximized": column = self.mainframe.get_parent() # column expand,fill,padding,pack = column.query_child_packing(self.mainframe) column.set_child_packing(self.mainframe,self.expand,fill,padding,pack) def append_text(self, text): self.buffer.insert_at_cursor(text) def clear_text(self): self.buffer.set_text('') def set_text(self, text): self.buffer.set_text(text) def get_source_widget(self): """ Hack to allow us to send this object to the drop_widget method as a context. """ return self.gvproperties class MyScrolledWindow(gtk.ScrolledWindow): def show_all(self): # first show them all: gtk.ScrolledWindow.show_all(self) # Hack to get around show_all that shows hidden items # do once, the first time showing if self.viewpage: gadgets = [g for g in self.viewpage.gadget_map.values() if g != None] self.viewpage = None for gadget in gadgets: if gadget.state == "minimized": gadget.set_state("minimized") class MyGrampsView(PageView.PageView): """ MyGrampsView interface """ def __init__(self, dbstate, uistate): """ Creates a MyGrampsView, with the current dbstate and uistate """ PageView.PageView.__init__(self, _('My Gramps'), dbstate, uistate) self._popup_xy = None def change_db(self, event): """ """ # FIXME: remove/add widgets from new db ini file pass def build_widget(self): """ Builds the container widget for the interface. Must be overridden by the the base class. Returns a gtk container widget. """ # load the user's gadgets and set columns, etc user_gadgets = self.load_gadgets() # build the GUI: frame = MyScrolledWindow() msg = _("Right click to add gadgets") self.tooltips = gtk.Tooltips() self.tooltips.set_tip(frame, msg) frame.viewpage = self frame.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) self.hbox = gtk.HBox(homogeneous=True) # Set up drag and drop frame.drag_dest_set(gtk.DEST_DEFAULT_MOTION | gtk.DEST_DEFAULT_HIGHLIGHT | gtk.DEST_DEFAULT_DROP, [('GADGET', 0, 80)], gtk.gdk.ACTION_COPY) frame.connect('drag_drop', self.drop_widget) frame.connect('button-press-event', self._button_press) frame.add_with_viewport(self.hbox) # Create the columns: self.columns = [] for i in range(self.column_count): self.columns.append(gtk.VBox()) self.hbox.pack_start(self.columns[-1],expand=True) # Load the gadgets self.gadget_map = {} # title->gadget self.frame_map = {} # frame->gadget self.detached_gadgets = [] # list of detached gadgets self.closed_gadgets = [] # list of closed gadgets self.closed_opts = [] # list of closed options from ini file # get the user's gadgets from ~/.gramps/gadgets.ini # Load the user's gadgets: for (name, opts) in user_gadgets: all_opts = get_gadget_opts(name, opts) if "title" not in all_opts: all_opts["title"] = "Untitled Gadget" if "state" not in all_opts: all_opts["state"] = "maximized" # uniqify titles: unique = all_opts["title"] cnt = 1 while unique in self.gadget_map: unique = all_opts["title"] + ("-%d" % cnt) cnt += 1 all_opts["title"] = unique if all_opts["state"] == "closed": self.gadget_map[all_opts["title"]] = None # save closed name self.closed_opts.append(all_opts) continue g = make_requested_gadget(self, name, all_opts, self.dbstate, self.uistate) if g: self.gadget_map[all_opts["title"]] = g self.frame_map[str(g.mainframe)] = g else: print "Can't make gadget of type '%s'." % name self.place_gadgets() return frame def clear_gadgets(self): """ Detach all of the mainframe gadgets from the columns. """ gadgets = [g for g in self.gadget_map.values() if g != None] for gadget in gadgets: column = gadget.mainframe.get_parent() column.remove(gadget.mainframe) def place_gadgets(self): """ Place the gadget mainframes in the columns. """ gadgets = [g for g in self.gadget_map.values() if g != None] # put the gadgets where they go: # sort by row gadgets.sort(lambda a, b: cmp(a.row, b.row)) cnt = 0 for gadget in gadgets: # see if the user wants this in a particular location: # and if there are that many columns if gadget.column >= 0 and gadget.column < self.column_count: pos = gadget.column else: # else, spread them out: pos = cnt % self.column_count self.columns[pos].pack_start(gadget.mainframe, expand=gadget.expand) gadget.column = pos # set height on gadget.scrolledwindow here: gadget.scrolledwindow.set_size_request(-1, gadget.height) # Can't minimize here, because GRAMPS calls show_all later: #if gadget.state == "minimized": # starts max, change to min it # gadget.set_state("minimized") # minimize it # set minimized is called in page subclass hack (above) if gadget.state == "windowed": gadget.detach() elif gadget.state == "closed": gadget.close() cnt += 1 def load_gadgets(self): self.column_count = 2 # default for new user retval = [] filename = GADGET_FILENAME if filename and os.path.exists(filename): cp = ConfigParser.ConfigParser() cp.read(filename) for sec in cp.sections(): if sec == "My Gramps View Options": if "column_count" in cp.options(sec): self.column_count = int(cp.get(sec, "column_count")) else: data = {"title": sec} for opt in cp.options(sec): if opt.startswith("data["): temp = data.get("data", []) temp.append(cp.get(sec, opt).strip()) data["data"] = temp else: data[opt] = cp.get(sec, opt).strip() if "name" not in data: data["name"] = "Unnamed Gadget" data["tname"]= _("Unnamed Gadget") retval.append((data["name"], data)) # name, opts else: # give defaults as currently known for name in ["Top Surnames Gadget", "Welcome Gadget"]: if name in AVAILABLE_GADGETS: retval.append((name, AVAILABLE_GADGETS[name])) return retval def save(self, *args): if debug: print "saving" if len(self.frame_map.keys() + self.detached_gadgets + self.closed_gadgets) == 0: return # something is the matter filename = GADGET_FILENAME try: fp = open(filename, "w") except: print "Failed writing '%s'; gadgets not saved" % filename return fp.write(";; Gramps gadgets file" + NL) fp.write((";; Automatically created at %s" % time.strftime("%Y/%m/%d %H:%M:%S")) + NL + NL) fp.write("[My Gramps View Options]" + NL) fp.write(("column_count=%d" + NL + NL) % self.column_count) # showing gadgets: for col in range(self.column_count): row = 0 for gframe in self.columns[col]: gadget = self.frame_map[str(gframe)] opts = get_gadget_options_by_name(gadget.name) if opts != None: base_opts = opts.copy() for key in base_opts: if key in gadget.__dict__: base_opts[key] = gadget.__dict__[key] fp.write(("[%s]" + NL) % gadget.title) for key in base_opts: if key == "content": continue elif key == "title": continue elif key == "column": continue elif key == "row": continue elif key == "data": if type(base_opts["data"]) not in [list, tuple]: fp.write(("data[0]=%s" + NL) % base_opts["data"]) else: cnt = 0 for item in base_opts["data"]: fp.write(("data[%d]=%s" + NL) % (cnt, item)) cnt += 1 else: fp.write(("%s=%s" + NL)% (key, base_opts[key])) fp.write(("column=%d" + NL) % col) fp.write(("row=%d" + NL) % row) fp.write(NL) row += 1 for gadget in self.detached_gadgets + self.closed_gadgets: opts = get_gadget_options_by_name(gadget.name) if opts != None: base_opts = opts.copy() for key in base_opts: if key in gadget.__dict__: base_opts[key] = gadget.__dict__[key] fp.write(("[%s]" + NL) % gadget.title) for key in base_opts: if key == "content": continue elif key == "title": continue elif key == "data": if type(base_opts["data"]) not in [list, tuple]: fp.write(("data[0]=%s" + NL) % base_opts["data"]) else: cnt = 0 for item in base_opts["data"]: fp.write(("data[%d]=%s" + NL) % (cnt, item)) cnt += 1 else: fp.write(("%s=%s" + NL)% (key, base_opts[key])) fp.write(NL) for opts in self.closed_opts: fp.write(("[%s]" + NL) % opts["title"]) for key in opts: if key == "content": continue elif key == "title": continue elif key == "data": if type(opts["data"]) not in [list, tuple]: fp.write(("data[0]=%s" + NL) % opts["data"]) else: cnt = 0 for item in opts["data"]: fp.write(("data[%d]=%s" + NL) % (cnt, item)) cnt += 1 else: fp.write(("%s=%s" + NL)% (key, opts[key])) fp.write(NL) fp.close() def drop_widget(self, source, context, x, y, timedata): """ This is the destination method for handling drag and drop of a gadget onto the main scrolled window. """ button = context.get_source_widget() hbox = button.get_parent() mainframe = hbox.get_parent() rect = source.get_allocation() sx, sy = rect.width, rect.height # first, find column: col = 0 for i in range(len(self.columns)): if x < (sx/len(self.columns) * (i + 1)): col = i break fromcol = mainframe.get_parent() if fromcol: fromcol.remove(mainframe) # now find where to insert in column: stack = [] for gframe in self.columns[col]: rect = gframe.get_allocation() if y < (rect.y + 15): # starts at 0, this allows insert before self.columns[col].remove(gframe) stack.append(gframe) maingadget = self.frame_map.get(str(mainframe), None) maingadget.column = col if maingadget.state == "maximized": expand = maingadget.expand else: expand = False self.columns[col].pack_start(mainframe, expand=expand) for gframe in stack: gadget = self.frame_map[str(gframe)] if gadget.state == "maximized": expand = gadget.expand else: expand = False self.columns[col].pack_start(gframe, expand=expand) return True def define_actions(self): """ Defines the UIManager actions. Called by the ViewManager to set up the View. The user typically defines self.action_list and self.action_toggle_list in this function. """ self.action = gtk.ActionGroup(self.title + "/Gadgets") self.action.add_actions([('AddGadget',gtk.STOCK_ADD,_("_Add a gadget")), ('RestoreGadget',None,_("_Restore a gadget")), ('DeleteGadget',None,_("_Delete a gadget")), ('Columns1',None,_("Set columns to _1"), None,None, lambda obj:self.set_columns(1)), ('Columns2',None,_("Set columns to _2"), None,None, lambda obj:self.set_columns(2)), ('Columns3',None,_("Set columns to _3"), None,None, lambda obj:self.set_columns(3)), ]) self._add_action_group(self.action) def set_columns(self, num): # clear the gadgets: self.clear_gadgets() # clear the columns: for column in self.columns: frame = column.get_parent() frame.remove(column) del column # create the new ones: self.column_count = num self.columns = [] for i in range(self.column_count): self.columns.append(gtk.VBox()) self.columns[-1].show() self.hbox.pack_start(self.columns[-1],expand=True) # place the gadgets back in the new columns self.place_gadgets() self.widget.show() def delete_gadget(self, obj): name = obj.get_child().get_label() ############### First kind: from current session for gadget in self.closed_gadgets: if gadget.title == name: self.closed_gadgets.remove(gadget) self.gadget_map[gadget.title] self.frame_map[str(gadget.mainframe)] del gadget return ################ Second kind: from options for opts in self.closed_opts: if opts["title"] == name: self.closed_opts.remove(opts) return def restore_gadget(self, obj): name = obj.get_child().get_label() ############### First kind: from current session for gadget in self.closed_gadgets: if gadget.title == name: gadget.state = "maximized" self.closed_gadgets.remove(gadget) if self._popup_xy != None: self.drop_widget(self.widget, gadget, self._popup_xy[0], self._popup_xy[1], 0) else: self.drop_widget(self.widget, gadget, 0, 0, 0) return ################ Second kind: from options for opts in self.closed_opts: if opts["title"] == name: self.closed_opts.remove(opts) g = make_requested_gadget(self, opts["name"], opts, self.dbstate, self.uistate) if g: self.gadget_map[opts["title"]] = g self.frame_map[str(g.mainframe)] = g else: print "Can't make gadget of type '%s'." % name if g: gadget = g gadget.state = "maximized" if gadget.column >= 0 and gadget.column < len(self.columns): pos = gadget.column else: pos = 0 self.columns[pos].pack_start(gadget.mainframe, expand=gadget.expand) # set height on gadget.scrolledwindow here: gadget.scrolledwindow.set_size_request(-1, gadget.height) ## now drop it in right place if self._popup_xy != None: self.drop_widget(self.widget, gadget, self._popup_xy[0], self._popup_xy[1], 0) else: self.drop_widget(self.widget, gadget, 0, 0, 0) def add_gadget(self, obj): tname = obj.get_child().get_label() all_opts = get_gadget_options_by_tname(tname) name = all_opts["name"] if all_opts == None: print "Unknown gadget type: '%s'; bad gadgets.ini file?" % name return if "title" not in all_opts: all_opts["title"] = "Untitled Gadget" # uniqify titles: unique = all_opts["title"] cnt = 1 while unique in self.gadget_map: unique = all_opts["title"] + ("-%d" % cnt) cnt += 1 all_opts["title"] = unique if all_opts["title"] not in self.gadget_map: g = make_requested_gadget(self, name, all_opts, self.dbstate, self.uistate) if g: self.gadget_map[all_opts["title"]] = g self.frame_map[str(g.mainframe)] = g gadget = g if gadget.column >= 0 and gadget.column < len(self.columns): pos = gadget.column else: pos = 0 self.columns[pos].pack_start(gadget.mainframe, expand=gadget.expand) # set height on gadget.scrolledwindow here: gadget.scrolledwindow.set_size_request(-1, gadget.height) ## now drop it in right place if self._popup_xy != None: self.drop_widget(self.widget, gadget, self._popup_xy[0], self._popup_xy[1], 0) else: self.drop_widget(self.widget, gadget, 0, 0, 0) else: print "Can't make gadget of type '%s'." % name def get_stock(self): """ Returns image associated with the view, which is used for the icon for the button. """ return 'gtk-home' def build_tree(self): return def ui_definition(self): return """ """ def _button_press(self, obj, event): if event.type == gtk.gdk.BUTTON_PRESS and event.button == 3: self._popup_xy = (event.x, event.y) menu = self.uistate.uimanager.get_widget('/Popup') ag_menu = self.uistate.uimanager.get_widget('/Popup/AddGadget') if ag_menu: qr_menu = ag_menu.get_submenu() if qr_menu == None: qr_menu = gtk.Menu() names = [AVAILABLE_GADGETS[key]["tname"] for key in AVAILABLE_GADGETS.keys()] names.sort() for name in names: Utils.add_menuitem(qr_menu, name, None, self.add_gadget) self.uistate.uimanager.get_widget('/Popup/AddGadget').set_submenu(qr_menu) rg_menu = self.uistate.uimanager.get_widget('/Popup/RestoreGadget') dg_menu = self.uistate.uimanager.get_widget('/Popup/DeleteGadget') if rg_menu: qr_menu = rg_menu.get_submenu() if qr_menu != None: rg_menu.remove_submenu() qr2_menu = dg_menu.get_submenu() if qr2_menu != None: dg_menu.remove_submenu() names = [] for gadget in self.closed_gadgets: names.append(gadget.title) for opts in self.closed_opts: names.append(opts["title"]) names.sort() if len(names) > 0: qr_menu = gtk.Menu() qr2_menu = gtk.Menu() for name in names: Utils.add_menuitem(qr_menu, name, None, self.restore_gadget) Utils.add_menuitem(qr2_menu, name, None, self.delete_gadget) self.uistate.uimanager.get_widget('/Popup/RestoreGadget').set_submenu(qr_menu) self.uistate.uimanager.get_widget('/Popup/DeleteGadget').set_submenu(qr2_menu) if menu: menu.popup(None, None, None, event.button, event.time) return True return False def on_delete(self): gadgets = [g for g in self.gadget_map.values() if g != None] for gadget in gadgets: # this is the only place where the gui runs user code directly if gadget.pui: gadget.pui.on_save() self.save()