From bad1414dfc9605c6fe07cc66843688bcc64cc49d Mon Sep 17 00:00:00 2001 From: Alex Roitman Date: Wed, 10 Aug 2005 14:28:16 +0000 Subject: [PATCH] * src/ScratchPad.py: added * src/stock_link.png: added * src/DbState.py: better ui management, history management * src/EditPerson.py: better ui management * src/GrampsDbBase.py: better ui management * src/PageView.py: better ui management * src/PersonView.py: better ui management, history management * src/ViewManager.py: better ui management svn: r5044 --- gramps2/ChangeLog | 10 + gramps2/src/DbState.py | 52 ++- gramps2/src/EditPerson.py | 28 +- gramps2/src/PageView.py | 18 +- gramps2/src/ScratchPad.py | 902 +++++++++++++++++++++++++++++++++++++ gramps2/src/ViewManager.py | 128 +++++- gramps2/src/stock_link.png | Bin 0 -> 601 bytes 7 files changed, 1098 insertions(+), 40 deletions(-) create mode 100644 gramps2/src/ScratchPad.py create mode 100755 gramps2/src/stock_link.png diff --git a/gramps2/ChangeLog b/gramps2/ChangeLog index d36c34476..9aaaaf1b3 100644 --- a/gramps2/ChangeLog +++ b/gramps2/ChangeLog @@ -1,3 +1,13 @@ +2005-08-09 Don Allingham + * src/ScratchPad.py: added + * src/stock_link.png: added + * src/DbState.py: better ui management, history management + * src/EditPerson.py: better ui management + * src/GrampsDbBase.py: better ui management + * src/PageView.py: better ui management + * src/PersonView.py: better ui management, history management + * src/ViewManager.py: better ui management + 2005-08-09 Alex Roitman * src/const.py.in: Remove obsolete constants. diff --git a/gramps2/src/DbState.py b/gramps2/src/DbState.py index 7f7e249de..557cd97c9 100644 --- a/gramps2/src/DbState.py +++ b/gramps2/src/DbState.py @@ -44,7 +44,6 @@ import GrampsDBCallback import GrampsKeys import NameDisplay - class History: def __init__(self): @@ -75,14 +74,43 @@ class History: for c in range(mhc): self.mhistory.remove(del_id) + def push(self,person_handle): + self.prune() + if len(self.history) == 0 or person_handle != self.history[-1]: + self.history.append(person_handle) + self.mhistory.append(person_handle) + self.index += 1 + + def forward(self,step=1): + self.index += step + self.mhistory.append(self.history[self.index]) + return str(self.history[self.index]) + + def back(self,step=1): + self.index -= step + self.mhistory.append(self.history[self.index]) + return str(self.history[self.index]) + + def at_end(self): + return self.index+1 == len(self.history) + + def at_front(self): + return self.index == 0 + + def prune(self): + if not self.at_end(): + self.history = self.history[0:self.index+1] + class DbState(GrampsDBCallback.GrampsDBCallback): __signals__ = { 'database-changed' : (GrampsDbBase.GrampsDbBase,), - 'active-changed' : (str,), + 'active-changed' : (str,), + 'no-database' : None, } - def __init__(self,window,status): + def __init__(self,window,status,uimanager): + self.uimanager = uimanager self.window = window GrampsDBCallback.GrampsDBCallback.__init__(self) self.db = GrampsDbBase.GrampsDbBase() @@ -91,15 +119,19 @@ class DbState(GrampsDBCallback.GrampsDBCallback): self.status_id = status.get_context_id('GRAMPS') self.phistory = History() + def get_widget(self,path): + return self.uimanager.get_widget(path) + def clear_history(self): self.phistory.clear() def change_active_person(self,person): self.active = person - try: - self.emit('active-changed',(person.handle,)) - except: - self.emit('active-changed',(None,)) + if person: + try: + self.emit('active-changed',(person.handle,)) + except: + self.emit('active-changed',("",)) def change_active_handle(self,handle): self.change_active_person(self.db.get_person_from_handle(handle)) @@ -109,7 +141,11 @@ class DbState(GrampsDBCallback.GrampsDBCallback): def change_database(self,db): self.db = db - self.emit('database-changed',(self.db,)) + self.emit('database-changed',(self.db,)) + + def no_database(self): + self.db = GrampsDbBase.GrampsDbBase() + self.emit('no-database') def modify_statusbar(self): diff --git a/gramps2/src/EditPerson.py b/gramps2/src/EditPerson.py index ee3fd74c6..f905a18c2 100644 --- a/gramps2/src/EditPerson.py +++ b/gramps2/src/EditPerson.py @@ -88,9 +88,10 @@ class EditPerson: use_patronymic = locale.getlocale(locale.LC_TIME)[0] in _use_patronymic - def __init__(self,parent,person,db,callback=None): + def __init__(self,state,person,callback=None): """Creates an edit window. Associates a person with the window.""" + self.state = state self.retval = const.UPDATE_PERSON self.dp = DateHandler.parser @@ -100,17 +101,16 @@ class EditPerson: # done to ensure that the person object is not stale, as it could # have been changed by something external (merge, tool, etc). if self.orig_handle: - person = db.get_person_from_handle(self.orig_handle) + person = self.state.db.get_person_from_handle(self.orig_handle) self.person = person self.orig_surname = self.person.get_primary_name().get_group_name() - self.parent = parent - if self.parent.child_windows.has_key(self.orig_handle): - self.parent.child_windows[self.orig_handle].present(None) - return - self.db = db + #if self.parent_window.child_windows.has_key(self.orig_handle): + # self.parent_window.child_windows[self.orig_handle].present(None) + # return + self.db = self.state.db self.callback = callback self.child_windows = {} - self.path = db.get_save_path() + self.path = self.db.get_save_path() self.not_loaded = True self.lds_not_loaded = True self.lists_changed = False @@ -121,8 +121,8 @@ class EditPerson: person.get_gender () == RelLib.Person.UNKNOWN) - for key in db.get_place_handles(): - p = db.get_place_from_handle(key).get_display_info() + for key in self.db.get_place_handles(): + p = self.db.get_place_from_handle(key).get_display_info() self.pdmap[p[0]] = key mod = not self.db.readonly @@ -454,14 +454,15 @@ class EditPerson: self.window.destroy() def add_itself_to_winsmenu(self): - self.parent.child_windows[self.orig_handle] = self + return + self.parent_window.child_windows[self.orig_handle] = self win_menu_label = self.name_display.display(self.person) if not win_menu_label.strip(): win_menu_label = _("New Person") self.win_menu_item = gtk.MenuItem(win_menu_label) self.win_menu_item.set_submenu(gtk.Menu()) self.win_menu_item.show() - self.parent.winsmenu.append(self.win_menu_item) + self.state.winsmenu.append(self.win_menu_item) self.winsmenu = self.win_menu_item.get_submenu() self.menu_item = gtk.MenuItem(_('Edit Person')) self.menu_item.connect("activate",self.present) @@ -469,7 +470,8 @@ class EditPerson: self.winsmenu.append(self.menu_item) def remove_itself_from_winsmenu(self): - del self.parent.child_windows[self.orig_handle] + return + del self.parent_window.child_windows[self.orig_handle] self.menu_item.destroy() self.winsmenu.destroy() self.win_menu_item.destroy() diff --git a/gramps2/src/PageView.py b/gramps2/src/PageView.py index dc769eec8..033bf8b73 100644 --- a/gramps2/src/PageView.py +++ b/gramps2/src/PageView.py @@ -30,9 +30,20 @@ class PageView: self.action_list = [] self.action_toggle_list = [] self.action_group = None + self.additional_action_groups = [] self.widget = None self.ui = "" - + self.state.connect('no-database',self.disable_action_group) + self.state.connect('database-changed',self.enable_action_group) + + def disable_action_group(self): + if self.action_group: + self.action_group.set_visible(False) + + def enable_action_group(self,obj): + if self.action_group: + self.action_group.set_visible(True) + def get_stock(self): try: return gtk.STOCK_MEDIA_MISSING @@ -73,4 +84,7 @@ class PageView: if not self.action_group: self.define_actions() self._build_action_group() - return self.action_group + return [self.action_group] + self.additional_action_groups + + def add_action_group(self,group): + self.additional_action_groups.append(group) diff --git a/gramps2/src/ScratchPad.py b/gramps2/src/ScratchPad.py new file mode 100644 index 000000000..c8929d04c --- /dev/null +++ b/gramps2/src/ScratchPad.py @@ -0,0 +1,902 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000-2005 Donald N. Allingham +# +# This program is free software; you can redistribute it and/or modiy +# 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$ + +#------------------------------------------------------------------------ +# +# standard python modules +# +#------------------------------------------------------------------------ +import cPickle as pickle +import os +from xml.sax.saxutils import escape +from gettext import gettext as _ + +#------------------------------------------------------------------------- +# +# GTK/Gnome modules +# +#------------------------------------------------------------------------- +import gtk +import gtk.glade +from gtk.gdk import ACTION_COPY, BUTTON1_MASK +from gnome import help_display + +#------------------------------------------------------------------------- +# +# gramps modules +# +#------------------------------------------------------------------------- +import const +import TreeTips + +from DdTargets import DdTargets + + +#------------------------------------------------------------------------- +# +# icons used in the object listing +# +#------------------------------------------------------------------------- + +LINK_PIC = gtk.gdk.pixbuf_new_from_file( "%s/%s" % (os.path.dirname(__file__), + 'stock_link.png')) +BLANK_PIC = gtk.gdk.Pixbuf(0,0,8,1,1) + +#------------------------------------------------------------------------- +# +# wrapper classes to provide object specific listing in the ListView +# +#------------------------------------------------------------------------- + +class ScratchPadWrapper(object): + + def __init__(self,model,obj): + self._gramps_model = model + self.database_changed(model.db) + self._gramps_model.connect('database-changed', self.database_changed) + + self._obj = obj + self._type = _("Unknown") + self._title = '' + self._value = '' + + def database_changed(self,db): + self._db = db + + def get_type(self): + return self._type + + def get_title(self): + return self._title + + def get_value(self): + return self._value + + def pack(self): + return str(self._obj) + +class ScratchPadGrampsTypeWrapper(ScratchPadWrapper): + + def __init__(self,model,obj): + ScratchPadWrapper.__init__(self,model,obj) + + #unpack object + exec 'unpack_data = %s' % self._obj + exec 'o_type = "%s"' % unpack_data[0] + self._obj = pickle.loads(unpack_data[2]) + self._pickle = obj + + def pack(self): + return self._pickle + + +class ScratchPadAddress(ScratchPadGrampsTypeWrapper): + + DROP_TARGETS = [DdTargets.ADDRESS] + DRAG_TARGET = DdTargets.ADDRESS + ICON = BLANK_PIC + + def __init__(self,model,obj): + ScratchPadGrampsTypeWrapper.__init__(self,model,obj) + self._type = _("Address") + self._title = self._obj.get_date() + self._value = "%s %s %s %s" % (self._obj.get_street(),self._obj.get_city(), + self._obj.get_state(),self._obj.get_country()) + + + def tooltip(self): + global escape + s = "%s\n\n"\ + "\t%s:\t%s\n"\ + "\t%s:\n"\ + "\t\t%s\n"\ + "\t\t%s\n"\ + "\t\t%s\n"\ + "\t\t%s\n"\ + "\t\t%s\n"\ + "\t%s:\t%s\n" % ( + _("Address"), + _("Date"), escape(self._obj.get_date()), + _("Location"), + escape(self._obj.get_street()), + escape(self._obj.get_city()), + escape(self._obj.get_state()), + escape(self._obj.get_country()), + escape(self._obj.get_postal_code()), + _("Telephone"), escape(self._obj.get_phone())) + + if len(self._obj.get_source_references()) > 0: + psrc_ref = self._obj.get_source_references()[0] + psrc_id = psrc_ref.get_base_handle() + psrc = self._db.get_source_from_handle(psrc_id) + s += "\n%s\n\n"\ + "\t%s:\t%s\n" % ( + _("Sources"), + _("Name"),escape(short(psrc.get_title()))) + + return s + +class ScratchPadEvent(ScratchPadGrampsTypeWrapper): + + DROP_TARGETS = [DdTargets.EVENT] + DRAG_TARGET = DdTargets.EVENT + ICON = LINK_PIC + + def __init__(self,model,obj): + ScratchPadGrampsTypeWrapper.__init__(self,model,obj) + self._type = _("Event") + self._title = const.display_pevent(self._obj.get_name()) + self._value = self._obj.get_description() + + + def tooltip(self): + global escape + + s = "%s\n\n"\ + "\t%s:\t%s\n"\ + "\t%s:\t%s\n"\ + "\t%s:\t%s\n"\ + "\t%s:\t%s\n"\ + "\t%s:\t%s\n" % ( + _("Event"), + _("Type"),escape(const.display_pevent(self._obj.get_name())), + _("Date"),escape(self._obj.get_date()), + _("Place"),escape(place_title(self._db,self._obj)), + _("Cause"),escape(self._obj.get_cause()), + _("Description"), escape(self._obj.get_description())) + + if len(self._obj.get_source_references()) > 0: + psrc_ref = self._obj.get_source_references()[0] + psrc_id = psrc_ref.get_base_handle() + psrc = self._db.get_source_from_handle(psrc_id) + + s += "\n%s\n\n"\ + "\t%s:\t%s\n" % ( + _("Primary source"), + _("Name"), + escape(short(psrc.get_title()))) + + return s + +class ScratchPadFamilyEvent(ScratchPadGrampsTypeWrapper): + + DROP_TARGETS = [DdTargets.FAMILY_EVENT] + DRAG_TARGET = DdTargets.FAMILY_EVENT + ICON = BLANK_PIC + + def __init__(self,model,obj): + ScratchPadGrampsTypeWrapper.__init__(self,model,obj) + self._type = _("Family Event") + self._title = const.display_fevent(self._obj.get_name()) + self._value = self._obj.get_description() + + + def tooltip(self): + global escape + + s = "%s\n\n"\ + "\t%s:\t%s\n"\ + "\t%s:\t%s\n"\ + "\t%s:\t%s\n"\ + "\t%s:\t%s\n"\ + "\t%s:\t%s\n" % ( + _("Family Event"), + _("Type"),escape(const.display_fevent(self._obj.get_name())), + _("Date"),escape(self._obj.get_date()), + _("Place"),escape(place_title(self.db,self._obj)), + _("Cause"),escape(self._obj.get_cause()), + _("Description"), escape(self._obj.get_description())) + + if len(self._obj.get_source_references()) > 0: + psrc_ref = self._obj.get_source_references()[0] + psrc_id = psrc_ref.get_base_handle() + psrc = self._db.get_source_from_handle(psrc_id) + + s += "\n%s\n\n"\ + "\t%s:\t%s\n" % ( + _("Primary source"), + _("Name"), + escape(short(psrc.get_title()))) + + return s + +class ScratchPadUrl(ScratchPadGrampsTypeWrapper): + + DROP_TARGETS = [DdTargets.URL] + DRAG_TARGET = DdTargets.URL + ICON = BLANK_PIC + + def __init__(self,model,obj): + ScratchPadGrampsTypeWrapper.__init__(self,model,obj) + self._type = _("Url") + self._title = self._obj.get_path() + self._value = self._obj.get_description() + + + def tooltip(self): + global escape + return "%s\n\n"\ + "\t%s:\t%s\n"\ + "\t%s:\t%s" % (_("Url"), + _("Path"), + escape(self._obj.get_path()), + _("Description"), + escape(self._obj.get_description())) + +class ScratchPadAttribute(ScratchPadGrampsTypeWrapper): + + DROP_TARGETS = [DdTargets.ATTRIBUTE] + DRAG_TARGET = DdTargets.ATTRIBUTE + ICON = BLANK_PIC + + def __init__(self,model,obj): + ScratchPadGrampsTypeWrapper.__init__(self,model,obj) + self._type = _("Attribute") + self._title = const.display_pattr(self._obj.get_type()) + self._value = self._obj.get_value() + + def tooltip(self): + global escape + s = "%s\n\n"\ + "\t%s:\t%s\n"\ + "\t%s:\t%s" % (_("Attribute"), + _("Type"), + escape(const.display_pattr(self._obj.get_type())), + _("Value"), + escape(self._obj.get_value())) + + if len(self._obj.get_source_references()) > 0: + psrc_ref = self._obj.get_source_references()[0] + psrc_id = psrc_ref.get_base_handle() + psrc = self._db.get_source_from_handle(psrc_id) + s += "\n%s\n\n"\ + "\t%s:\t%s\n" % ( + _("Sources"), + _("Name"),escape(short(psrc.get_title()))) + + return s + +class ScratchPadFamilyAttribute(ScratchPadGrampsTypeWrapper): + + DROP_TARGETS = [DdTargets.FAMILY_ATTRIBUTE] + DRAG_TARGET = DdTargets.FAMILY_ATTRIBUTE + ICON = BLANK_PIC + + def __init__(self,model,obj): + ScratchPadGrampsTypeWrapper.__init__(self,model,obj) + self._type = _("Family Attribute") + self._title = const.display_fattr(self._obj.get_type()) + self._value = self._obj.get_value() + + def tooltip(self): + global escape + s = "%s\n\n"\ + "\t%s:\t%s\n"\ + "\t%s:\t%s" % (_("Family Attribute"), + _("Type"), + escape(const.display_fattr(self._obj.get_type())), + _("Value"), + escape(self._obj.get_value())) + + if len(self._obj.get_source_references()) > 0: + psrc_ref = self._obj.get_source_references()[0] + psrc_id = psrc_ref.get_base_handle() + psrc = self._db.get_source_from_handle(psrc_id) + s += "\n%s\n\n"\ + "\t%s:\t%s\n" % ( + _("Sources"), + _("Name"),escape(short(psrc.get_title()))) + + return s + +class ScratchPadSourceRef(ScratchPadGrampsTypeWrapper): + + DROP_TARGETS = [DdTargets.SOURCEREF] + DRAG_TARGET = DdTargets.SOURCEREF + ICON = BLANK_PIC + + def __init__(self,model,obj): + ScratchPadGrampsTypeWrapper.__init__(self,model,obj) + self._type = _("SourceRef") + + base = self._db.get_source_from_handle(self._obj.get_base_handle()) + self._title = base.get_title() + self._value = self._obj.get_text(), + + def tooltip(self): + global escape + base = self._db.get_source_from_handle(self._obj.get_base_handle()) + s = "%s\n\n"\ + "\t%s:\t%s\n"\ + "\t%s:\t%s\n"\ + "\t%s:\t%s\n"\ + "\t%s:\t%s" % ( + _("SourceRef"), + _("Title"),escape(base.get_title()), + _("Page"), escape(self._obj.get_page()), + _("Text"), escape(self._obj.get_text()), + _("Comment"), escape(self._obj.get_note())) + + return s + +class ScratchPadName(ScratchPadGrampsTypeWrapper): + + DROP_TARGETS = [DdTargets.NAME] + DRAG_TARGET = DdTargets.NAME + ICON = BLANK_PIC + + def __init__(self,model,obj): + ScratchPadGrampsTypeWrapper.__init__(self,model,obj) + self._type = _("Name") + self._title = self._obj.get_name() + self._value = self._obj.get_type() + + + def tooltip(self): + global escape + + s = "%s\n\n"\ + "\t%s:\t%s\n"\ + "\t%s:\t%s\n" % ( + _("Name"), + _("Name"),escape(self._obj.get_name()), + _("Type"),escape(self._obj.get_type())) + + if len(self._obj.get_source_references()) > 0: + psrc_ref = self._obj.get_source_references()[0] + psrc_id = psrc_ref.get_base_handle() + psrc = self._db.get_source_from_handle(psrc_id) + + s += "\n%s\n\n"\ + "\t%s:\t%s\n" % ( + _("Primary source"), + _("Name"), + escape(short(psrc.get_title()))) + + return s + +class ScratchPadText(ScratchPadWrapper): + + DROP_TARGETS = DdTargets.all_text() + DRAG_TARGET = DdTargets.TEXT + ICON = BLANK_PIC + + def __init__(self,model,obj): + ScratchPadWrapper.__init__(self,model,obj) + self._type = _("Text") + + self._title = "" + self._value = self._obj + + def tooltip(self): + global escape + return "%s\n"\ + "%s" % (_("Text"), + escape(self._obj)) + +class ScratchMediaObj(ScratchPadWrapper): + + DROP_TARGETS = [DdTargets.MEDIAOBJ] + DRAG_TARGET = DdTargets.MEDIAOBJ + ICON = LINK_PIC + + def __init__(self,model,obj): + ScratchPadWrapper.__init__(self,model,obj) + self._type = _("Media Object") + + self._title = "" + self._value = "" + + def tooltip(self): + global escape + return "%s\n"\ + "%s" % (_("Media Object"), + escape(self._obj)) + +class ScratchPersonLink(ScratchPadWrapper): + + DROP_TARGETS = [DdTargets.PERSON_LINK] + DRAG_TARGET = DdTargets.PERSON_LINK + ICON = LINK_PIC + + def __init__(self,model,obj): + ScratchPadWrapper.__init__(self,model,obj) + self._type = _("Person Link") + + person = self._db.get_person_from_handle(self._obj) + self._title = person.get_primary_name().get_name() + birth_handle = person.get_birth_handle() + if birth_handle: + birth = self._db.get_event_from_handle(birth_handle) + if birth.get_date() and birth.get_date() != "": + self._value = escape(birth.get_date()) + + + def tooltip(self): + global escape + + person = self._db.get_person_from_handle(self._obj) + + s = "%s\n\n"\ + "\t%s:\t%s\n"\ + "\t%s:\t%s\n" % ( + _("Person Link"), + _("Name"),escape(self._title), + _("Birth"),escape(self._value)) + + if len(person.get_source_references()) > 0: + psrc_ref = person.get_source_references()[0] + psrc_id = psrc_ref.get_base_handle() + psrc = self._db.get_source_from_handle(psrc_id) + + s += "\n%s\n\n"\ + "\t%s:\t%s\n" % ( + _("Primary source"), + _("Name"), + escape(short(psrc.get_title()))) + + return s + +#------------------------------------------------------------------------- +# +# Wrapper classes to deal with lists of objects +# +#------------------------------------------------------------------------- + +class ScratchDropList(object): + + def __init__(self,model,obj_list): + self._model = model + self._obj_list = pickle.loads(obj_list) + + def get_objects(self): + return [self._cls(self._model,obj) for obj in self._obj_list] + +class ScratchPersonLinkList(ScratchDropList): + + DROP_TARGETS = [DdTargets.PERSON_LINK_LIST] + DRAG_TARGET = None + + def __init__(self,model,obj_list): + ScratchDropList.__init__(self,model,obj_list) + self._cls = ScratchPersonLink + + + +#------------------------------------------------------------------------- +# +# ScratchPadListModel class +# +#------------------------------------------------------------------------- +class ScratchPadListModel(gtk.ListStore): + + def __init__(self): + gtk.ListStore.__init__(self, + str, # object type + object, # object + object # tooltip callback + ) + + +#------------------------------------------------------------------------- +# +# ScratchPadListView class +# +#------------------------------------------------------------------------- +class ScratchPadListView: + + LOCAL_DRAG_TARGET = ('MY_TREE_MODEL_ROW', gtk.TARGET_SAME_WIDGET, 0) + LOCAL_DRAG_TYPE = 'MY_TREE_MODEL_ROW' + + def __init__(self, model, widget): + self._gramps_model = model + + self.database_changed(self._gramps_model.db) + self._gramps_model.connect('database-changed', self.database_changed) + + self._widget = widget + + self._target_type_to_wrapper_class_map = {} + self._previous_drop_time = 0 + + self.otitles = [(_('Type'),-1,150), + (_('Title'),-1,150), + (_('Value'),-1,150), + ('',-1,0)] # To hold the tooltip text + + # Create the tree columns + self._col1 = gtk.TreeViewColumn(_("Type")) + self._col2 = gtk.TreeViewColumn(_("Title")) + self._col3 = gtk.TreeViewColumn(_("Value")) + + # Add columns + self._widget.append_column(self._col1) + self._widget.append_column(self._col2) + self._widget.append_column(self._col3) + + # Create cell renders + self._col1_cellpb = gtk.CellRendererPixbuf() + self._col1_cell = gtk.CellRendererText() + self._col2_cell = gtk.CellRendererText() + self._col3_cell = gtk.CellRendererText() + + # Add cells to view + self._col1.pack_start(self._col1_cellpb, False) + self._col1.pack_start(self._col1_cell, True) + self._col2.pack_start(self._col2_cell, True) + self._col3.pack_start(self._col3_cell, True) + + # Setup the cell data callback funcs + self._col1.set_cell_data_func(self._col1_cellpb, self.object_pixbuf) + self._col1.set_cell_data_func(self._col1_cell, self.object_type) + self._col2.set_cell_data_func(self._col2_cell, self.object_title) + self._col3.set_cell_data_func(self._col3_cell, self.object_value) + + self.treetips = TreeTips.TreeTips(self._widget,2,True) + + # Set the column that inline searching will use. + # The search does not appear to work properly so I am disabling it for now. + self._widget.set_enable_search(False) + #self._widget.set_search_column(1) + + self._widget.drag_dest_set(gtk.DEST_DEFAULT_ALL, + (ScratchPadListView.LOCAL_DRAG_TARGET,) + \ + DdTargets.all_targets(), + ACTION_COPY) + + self._widget.connect('drag_data_get', self.object_drag_data_get) + self._widget.connect('drag_begin', self.object_drag_begin) + self._widget.connect('drag_data_received', + self.object_drag_data_received) + + self.register_wrapper_classes() + + def database_changed(self,db): + self._db = db + + # Method to manage the wrapper classes. + + def register_wrapper_classes(self): + self.register_wrapper_class(ScratchPadAddress) + self.register_wrapper_class(ScratchPadEvent) + self.register_wrapper_class(ScratchPadFamilyEvent) + self.register_wrapper_class(ScratchPadUrl) + self.register_wrapper_class(ScratchPadAttribute) + self.register_wrapper_class(ScratchPadFamilyAttribute) + self.register_wrapper_class(ScratchPadSourceRef) + self.register_wrapper_class(ScratchPadName) + self.register_wrapper_class(ScratchPadText) + self.register_wrapper_class(ScratchMediaObj) + self.register_wrapper_class(ScratchPersonLink) + self.register_wrapper_class(ScratchPersonLinkList) + + + def register_wrapper_class(self,wrapper_class): + for drop_target in wrapper_class.DROP_TARGETS: + self._target_type_to_wrapper_class_map[drop_target.drag_type] = wrapper_class + + + # Methods for rendering the cells. + + def object_pixbuf(self, column, cell, model, iter, user_data=None): + o = model.get_value(iter, 1) + cell.set_property('pixbuf', o.__class__.ICON) + + def object_type(self, column, cell, model, iter, user_data=None): + o = model.get_value(iter, 1) + cell.set_property('text', o.get_type()) + + def object_title(self, column, cell, model, iter, user_data=None): + o = model.get_value(iter, 1) + cell.set_property('text', o.get_title()) + + + def object_value(self, column, cell, model, iter, user_data=None): + o = model.get_value(iter, 1) + cell.set_property('text', o.get_value()) + + + # handlers for the drag and drop events. + + def on_object_select_row(self,obj): + tree_selection = self._widget.get_selection() + model,iter = tree_selection.get_selected() + + self._widget.unset_rows_drag_source() + + if iter != None: + o = model.get_value(iter,1) + + targets = [ScratchPadListView.LOCAL_DRAG_TARGET] + \ + [target.target() for target in o.__class__.DROP_TARGETS] + + self._widget.enable_model_drag_source(BUTTON1_MASK, targets, ACTION_COPY) + + def object_drag_begin(self, context, a): + return + + def object_drag_data_get(self, widget, context, sel_data, info, time): + tree_selection = widget.get_selection() + model,iter = tree_selection.get_selected() + o = model.get_value(iter,1) + + sel_data.set(sel_data.target, 8, o.pack()) + + def object_drag_data_received(self,widget,context,x,y,selection,info,time): + + # Ignore drops from the same widget. + if ScratchPadListView.LOCAL_DRAG_TYPE in context.targets: + return + + model = widget.get_model() + sel_data = selection.data + + # There is a strange bug that means that if there is a selection + # in the list we get multiple drops of the same object. Luckily + # the time values are the same so we can drop all but the first. + if time == self._previous_drop_time: + return + + # Find a wrapper class + possible_wrappers = [target for target in context.targets \ + if target in self._target_type_to_wrapper_class_map.keys()] + + if len(possible_wrappers) == 0: + # No wrapper for this class + return + + # Just select the first match. + wrapper_class = self._target_type_to_wrapper_class_map[str(possible_wrappers[0])] + + o = wrapper_class(self._gramps_model,sel_data) + + # If the wrapper object is a subclass of ScratchDropList then + # the drag data was a list of objects and we need to decode + # all of them. + if isinstance(o,ScratchDropList): + o_list = o.get_objects() + else: + o_list = [o] + + for o in o_list: + drop_info = widget.get_dest_row_at_pos(x, y) + if drop_info: + path, position = drop_info + iter = model.get_iter(path) + if (position == gtk.TREE_VIEW_DROP_BEFORE + or position == gtk.TREE_VIEW_DROP_INTO_OR_BEFORE): + model.insert_before(iter,[o.__class__.DRAG_TARGET.drag_type,o,o.tooltip]) + else: + model.insert_after(iter,[o.__class__.DRAG_TARGET.drag_type,o,o.tooltip]) + else: + model.append([o.__class__.DRAG_TARGET.drag_type,o,o.tooltip]) + + + # remember time for double drop workaround. + self._previous_drop_time = time + + + # proxy methods to provide access to the real widget functions. + + def set_model(self,model=None): + self._widget.set_model(model) + self._widget.get_selection().connect('changed',self.on_object_select_row) + + def get_model(self): + return self._widget.get_model() + + def get_selection(self): + return self._widget.get_selection() + +#------------------------------------------------------------------------- +# +# ScatchPadWindow class +# +#------------------------------------------------------------------------- +class ScratchPadWindow: + """ + The ScratchPad provides a temporary area to hold objects that can + be reused accross multiple Person records. The pad provides a window + onto which objects can be dropped and then dragged into new Person + dialogs. The objects are stored as the pickles that are built by the + origininating widget. The objects are only unpickled in order to + provide the text in the display. + + No attempt is made to ensure that any references contained within + the pickles are valid. Because the pad extends the life time of drag + and drop objects, it is possible that references that were valid + when an object is copied to the pad are invalid by the time they + are dragged to a new Person. For this reason, using the pad places + a responsibility on all '_drag_data_received' methods to check the + references of objects before attempting to use them. + """ + + # Class attribute used to hold the content of the + # ScratchPad. A class attribute is used so that the content + # it preserved even when the ScratchPad window is closed. + # As there is only ever one ScratchPad we do not need to + # maintain a list of these. + otree = None + + def __init__(self,database,parent): + """Initializes the ScratchPad class, and displays the window""" + + self.parent = parent + if self.parent.child_windows.has_key(self.__class__): + self.parent.child_windows[self.__class__].present(None) + return + self.win_key = self.__class__ + + + self.database_changed(database) + self.parent.connect('database-changed', self.database_changed) + + base = os.path.dirname(__file__) + self.glade_file = "%s/%s" % (base,"scratchpad.glade") + + self.top = gtk.glade.XML(self.glade_file,"scratch_pad","gramps") + self.window = self.top.get_widget("scratch_pad") + self.window.set_icon(self.parent.topWindow.get_icon()) + self.clear_all_btn = self.top.get_widget("btn_clear_all") + self.clear_btn = self.top.get_widget("btn_clear") + + self.object_list = ScratchPadListView(self.parent,self.top.get_widget('objectlist')) + self.object_list.get_selection().connect('changed',self.set_clear_btn_sensitivity) + self.set_clear_btn_sensitivity(sel=self.object_list.get_selection()) + + if not ScratchPadWindow.otree: + ScratchPadWindow.otree = ScratchPadListModel() + + self.set_clear_all_btn_sensitivity(treemodel=ScratchPadWindow.otree) + ScratchPadWindow.otree.connect('row-deleted',self.set_clear_all_btn_sensitivity) + ScratchPadWindow.otree.connect('row-inserted',self.set_clear_all_btn_sensitivity) + + + self.object_list.set_model(ScratchPadWindow.otree) + + self.top.signal_autoconnect({ + "on_close_scratchpad" : self.on_close_scratchpad, + "on_clear_clicked": self.on_clear_clicked, + "on_help_clicked": self.on_help_clicked, + "on_objectlist_delete_event": self.on_delete_event, + "on_scratch_pad_delete_event": self.on_delete_event + }) + + self.clear_all_btn.connect_object('clicked', gtk.ListStore.clear, ScratchPadWindow.otree) + self.parent.connect('database-changed', lambda x: ScratchPadWindow.otree.clear()) + + self.add_itself_to_menu() + self.window.show() + + def database_changed(self,database): + self.db = database + + def set_clear_all_btn_sensitivity(self, treemodel=None, path=None, iter=None, user_param1=None): + if len(treemodel) == 0: + self.clear_all_btn.set_sensitive(False) + else: + self.clear_all_btn.set_sensitive(True) + + def set_clear_btn_sensitivity(self, sel=None, user_param1=None): + if sel.count_selected_rows() == 0: + self.clear_btn.set_sensitive(False) + else: + self.clear_btn.set_sensitive(True) + + + def on_delete_event(self,obj,b): + self.remove_itself_from_menu() + + def add_itself_to_menu(self): + """Add the ScratchPad window to the list of windows in the + main GRAMPS interface. If this is the first instance to be + created a submenu is created, if it is not the first instance + then an entry is created in the existing sub menu.""" + self.parent.child_windows[self.win_key] = self + self.parent_menu_item = gtk.MenuItem(_('Scratch Pad')) + self.parent_menu_item.connect("activate",self.present) + self.parent_menu_item.show() + self.parent.winsmenu.append(self.parent_menu_item) + + def remove_itself_from_menu(self): + """Remove the instance of the pad from the Window menu in the + main GRAMPS window. If this is the last pad then remove the + ScratchPad sub menu as well.""" + + del self.parent.child_windows[self.win_key] + self.parent_menu_item.destroy() + + def present(self,obj): + self.window.present() + + def on_help_clicked(self,obj): + """Display the relevant portion of GRAMPS manual""" + help_display('gramps-manual','tools-util-scratch-pad') + + def on_close_scratchpad(self,obj): + self.remove_itself_from_menu() + self.window.destroy() + + def on_clear_clicked(self,obj): + """Deletes the selected object from the object list""" + selection = self.object_list.get_selection() + model, iter = selection.get_selected() + if iter: + model.remove(iter) + return + + +def short(val,size=60): + if len(val) > size: + return "%s..." % val[0:size] + else: + return val + +def place_title(db,event): + pid = event.get_place_handle() + if pid: + return db.get_place_from_handle(pid).get_title() + else: + return u'' + +#------------------------------------------------------------------------- +# +# +# +#------------------------------------------------------------------------- +def ScratchPad(state,person,callback,parent=None): + ScratchPadWindow(state.db,parent) + +#------------------------------------------------------------------------- +# +# +# +#------------------------------------------------------------------------- +from PluginMgr import register_tool + +register_tool( + ScratchPad, + _("Scratch Pad"), + category=_("Utilities"), + description=_("The Scratch Pad provides a temporary note pad to store " + "objects for easy reuse.") + ) diff --git a/gramps2/src/ViewManager.py b/gramps2/src/ViewManager.py index 17ac2acc8..6279115a5 100644 --- a/gramps2/src/ViewManager.py +++ b/gramps2/src/ViewManager.py @@ -141,7 +141,6 @@ class ViewManager: self.window.set_size_request(775,500) self.statusbar = gtk.Statusbar() - self.state = DbState.DbState(self.window,self.statusbar) self.RelClass = PluginMgr.relationship_class @@ -161,18 +160,21 @@ class ViewManager: hbox.pack_start(self.notebook,True) self.menubar = self.uimanager.get_widget('/MenuBar') self.toolbar = self.uimanager.get_widget('/ToolBar') + print self.uimanager.get_widget('/MenuBar/GoMenu') vbox.pack_start(self.menubar, False) vbox.pack_start(self.toolbar, False) vbox.add(hbox) vbox.pack_end(self.statusbar,False) self.notebook.connect('switch-page',self.change_page) - + self.state = DbState.DbState(self.window,self.statusbar,self.uimanager) self.window.show_all() def init_interface(self): self.create_pages() self.change_page(None,None,0) + self.state.no_database() + self.actiongroup.set_visible(False) def set_color(self,obj): style = obj.get_style().copy() @@ -192,28 +194,34 @@ class ViewManager: self.window.add_accel_group(accelgroup) self.actiongroup = gtk.ActionGroup('MainWindow') + self.fileactions = gtk.ActionGroup('FileWindow') + self.fileactions.add_actions([ + ('FileMenu', None, '_File'), + ('New', gtk.STOCK_NEW, '_New', "n", None, self.on_new_activate), + ('Open', gtk.STOCK_OPEN, '_Open', "o", None, self.on_open_activate), + ('OpenRecent', gtk.STOCK_OPEN, 'Open _Recent'), + ('Quit', gtk.STOCK_QUIT, '_Quit', 'q', None, gtk.main_quit), + ('ViewMenu', None, '_View'), + ('Preferences',gtk.STOCK_PREFERENCES,'_Preferences'), + ('ColumnEdit', gtk.STOCK_PROPERTIES, '_Column Editor'), + ('HelpMenu', None, '_Help'), + ('About', gtk.STOCK_ABOUT, '_About'), + ]) + self.actiongroup.add_actions([ # Name Stock Icon Label - ('FileMenu', None, '_File'), - ('New', gtk.STOCK_NEW, '_New', "n", None, self.on_new_activate), - ('Open', gtk.STOCK_OPEN, '_Open', "o", None, self.on_open_activate), - ('OpenRecent', gtk.STOCK_OPEN, 'Open _Recent'), - ('Import', gtk.STOCK_CONVERT, '_Import'), ('SaveAs', gtk.STOCK_SAVE_AS, '_Save As'), ('Export', gtk.STOCK_SAVE_AS, '_Export'), ('Abandon', gtk.STOCK_REVERT_TO_SAVED, '_Abandon changes and quit'), - ('Quit', gtk.STOCK_QUIT, '_Quit', 'q', None, gtk.main_quit), ('Undo', gtk.STOCK_UNDO, '_Undo', 'z' ), - ('Preferences',gtk.STOCK_PREFERENCES, '_Preferences'), - ('ColumnEdit', gtk.STOCK_PROPERTIES, '_Column Editor'), ('CmpMerge', None, '_Compare and merge'), ('FastMerge', None, '_Fast merge'), ('ScratchPad', gtk.STOCK_PASTE, '_ScratchPad', None, None, self.on_scratchpad), + ('Import', gtk.STOCK_CONVERT, '_Import', None, None, self.on_import), ('Reports', gtk.STOCK_DND_MULTIPLE, '_Reports'), ('Tools', gtk.STOCK_EXECUTE, '_Tools'), ('EditMenu', None, '_Edit'), ('GoMenu', None, '_Go'), - ('ViewMenu', None, '_View'), ('BookMenu', None, '_Bookmarks'), ('AddBook', gtk.STOCK_INDEX, '_Add bookmark', 'd'), ('EditBook', None, '_Edit bookmarks', 'b'), @@ -221,16 +229,15 @@ class ViewManager: ('ReportsMenu',None, '_Reports'), ('ToolsMenu', None, '_Tools'), ('WindowsMenu',None, '_Windows'), - ('HelpMenu', None, '_Help'), - ('About', gtk.STOCK_ABOUT, '_About'), ]) - self.actiongroup.add_toggle_actions([ + self.fileactions.add_toggle_actions([ ('Sidebar', None, '_Sidebar', None, None, self.sidebar_toggle), ('Toolbar', None, '_Toolbar', None, None, self.toolbar_toggle), ]) merge_id = self.uimanager.add_ui_from_string(uidefault) + self.uimanager.insert_action_group(self.fileactions,1) self.uimanager.insert_action_group(self.actiongroup,1) def sidebar_toggle(self,obj): @@ -287,12 +294,16 @@ class ViewManager: if self.merge_id: self.uimanager.remove_ui(self.merge_id) if self.active_page: - self.uimanager.remove_action_group(self.active_page.get_actions()) + groups = self.active_page.get_actions() + for grp in groups: + self.uimanager.remove_action_group(grp) if len(self.pages) > 0: self.active_page = self.pages[num] + groups = self.active_page.get_actions() - self.uimanager.insert_action_group(self.active_page.get_actions(),1) + for grp in groups: + self.uimanager.insert_action_group(grp,1) self.merge_id = self.uimanager.add_ui_from_string(self.active_page.ui_definition()) def on_open_activate(self,obj): @@ -523,6 +534,7 @@ class ViewManager: #self.undo_callback(None) #self.redo_callback(None) #self.goto_active_person() + self.actiongroup.set_visible(True) return 1 def load_database(self,name,callback=None,mode="w"): @@ -573,7 +585,89 @@ class ViewManager: def on_scratchpad(self,obj): import ScratchPad - ScratchPad.ScratchPadWindow(self.db, self) + ScratchPad.ScratchPadWindow(self.state, self) + + def on_import(self,obj): + print "import" + choose = gtk.FileChooserDialog(_('GRAMPS: Import database'), + self.state.window, + gtk.FILE_CHOOSER_ACTION_OPEN, + (gtk.STOCK_CANCEL, + gtk.RESPONSE_CANCEL, + gtk.STOCK_OPEN, + gtk.RESPONSE_OK)) + choose.set_local_only(False) + # Always add automatic (macth all files) filter + add_all_files_filter(choose) + add_grdb_filter(choose) + add_xml_filter(choose) + add_gedcom_filter(choose) + + format_list = [const.app_gramps,const.app_gramps_xml,const.app_gedcom] + + # Add more data type selections if opening existing db + for (importData,mime_filter,mime_type,native_format,format_name) in PluginMgr.import_list: + if not native_format: + choose.add_filter(mime_filter) + format_list.append(mime_type) + _KNOWN_FORMATS[mime_type] = format_name + + (box,type_selector) = format_maker(format_list) + choose.set_extra_widget(box) + + # Suggested folder: try last open file, import, then last export, + # then home. + default_dir = GrampsKeys.get_last_import_dir() + if len(default_dir)<=1: + default_dir = os.path.split(GrampsKeys.get_lastfile())[0] + os.path.sep + if len(default_dir)<=1: + default_dir = GrampsKeys.get_last_export_dir() + if len(default_dir)<=1: + default_dir = '~/' + + choose.set_current_folder(default_dir) + response = choose.run() + if response == gtk.RESPONSE_OK: + filename = choose.get_filename() + filetype = type_selector.get_value() + if filetype == 'auto': + try: + filetype = get_mime_type(filename) + except RuntimeError,msg: + QuestionDialog.ErrorDialog( + _("Could not open file: %s") % filename, + str(msg)) + return False + + if filetype == const.app_gramps: + choose.destroy() + ReadGrdb.importData(self.state.db,filename) + self.parent.import_tool_callback() + return True + elif filetype == const.app_gramps_xml: + choose.destroy() + import ReadXML + ReadXML.importData(self.state.db,filename) + return True + elif filetype == const.app_gedcom: + choose.destroy() + import ReadGedcom + ReadGedcom.importData(self.state.db,filename) + return True + + (the_path,the_file) = os.path.split(filename) + GrampsKeys.save_last_import_dir(the_path) + for (importData,mime_filter,mime_type,native_format,format_name) in PluginMgr.import_list: + if filetype == mime_type or the_file == mime_type: + choose.destroy() + importData(self.state.db,filename) + self.parent.import_tool_callback() + return True + QuestionDialog.ErrorDialog( + _("Could not open file: %s") % filename, + _('File type "%s" is unknown to GRAMPS.\n\nValid types are: GRAMPS database, GRAMPS XML, GRAMPS package, and GEDCOM.') % filetype) + choose.destroy() + return False def add_all_files_filter(chooser): """ diff --git a/gramps2/src/stock_link.png b/gramps2/src/stock_link.png new file mode 100755 index 0000000000000000000000000000000000000000..ddad2494341999f8064e4f428fbc5d06ddcfa0c5 GIT binary patch literal 601 zcmV-f0;c_mP)(w{RwaeWP#MSFl{mo2ztHV4RF0!Eat$x-EOnbIa;nj2q%6xq2!Uyu z^!xpI?+Eynq(n8K632ct8pX*Kz$@SdaJ)&uwj2oH4e%IXGMU8d=fFq61#Z?=(5?h8 zfzN-fzXeW!DG($f0C;B@2CnNe9*?msi%O*e@EW))Uvr`=3Vq*S0g-8%5fF7cod}4e zy5po(B&O4;02JG{#Vyr}1lE%FBG+-813+swn>s)gMKP!^l_Vm#48!nH`lbOb48!>L z*RoZRq>Ak2aybUZ^?Ka_oFE7;WG7un0$cux^p81=wae;3=TATCH@cREoRj7vQ@LQ;_68xB@-^ nr$G>8B=}eP2YA~;^zYjr=!er^8sz#W00000NkvXXu0mjfTQUh8 literal 0 HcmV?d00001