From 2a685cbd7a8f903c858bac73f180d84e15a0c5cc Mon Sep 17 00:00:00 2001 From: Benny Malengier Date: Fri, 10 Jul 2009 13:18:13 +0000 Subject: [PATCH] 3093: Enable grouped displaytab in the editor: Name tab svn: r12781 --- po/POTFILES.in | 1 + src/DisplayTabs/Makefile.am | 1 + src/DisplayTabs/_AddrEmbedList.py | 12 +- src/DisplayTabs/_AttrEmbedList.py | 6 +- src/DisplayTabs/_BackRefList.py | 8 +- src/DisplayTabs/_DataEmbedList.py | 6 +- src/DisplayTabs/_EmbeddedList.py | 26 +- src/DisplayTabs/_EventEmbedList.py | 14 +- src/DisplayTabs/_FamilyLdsEmbedList.py | 12 +- src/DisplayTabs/_GroupEmbeddedList.py | 348 +++++++++++++++++++++++++ src/DisplayTabs/_LdsEmbedList.py | 12 +- src/DisplayTabs/_LocationEmbedList.py | 12 +- src/DisplayTabs/_NameEmbedList.py | 112 ++++++-- src/DisplayTabs/_NameModel.py | 101 ++++++- src/DisplayTabs/_NoteTab.py | 6 +- src/DisplayTabs/_PersonRefEmbedList.py | 8 +- src/DisplayTabs/_RepoEmbedList.py | 10 +- src/DisplayTabs/_SourceEmbedList.py | 10 +- src/DisplayTabs/_WebEmbedList.py | 8 +- src/Editors/_EditFamily.py | 2 +- src/Editors/_EditName.py | 1 + src/Editors/_EditPerson.py | 12 +- 22 files changed, 638 insertions(+), 90 deletions(-) create mode 100644 src/DisplayTabs/_GroupEmbeddedList.py diff --git a/po/POTFILES.in b/po/POTFILES.in index 21d8747bf..172890aad 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -282,6 +282,7 @@ src/DisplayTabs/_FamilyAttrEmbedList.py src/DisplayTabs/_FamilyLdsEmbedList.py src/DisplayTabs/_GalleryTab.py src/DisplayTabs/_GrampsTab.py +src/DisplayTabs/_GroupEmbeddedList.py src/DisplayTabs/_LdsEmbedList.py src/DisplayTabs/_LdsModel.py src/DisplayTabs/_LocationEmbedList.py diff --git a/src/DisplayTabs/Makefile.am b/src/DisplayTabs/Makefile.am index 393a55e47..d3c873ca0 100644 --- a/src/DisplayTabs/Makefile.am +++ b/src/DisplayTabs/Makefile.am @@ -21,6 +21,7 @@ pkgdata_PYTHON = \ _FamilyLdsEmbedList.py \ _GalleryTab.py \ _GrampsTab.py \ + _GroupEmbeddedList.py \ _LdsEmbedList.py \ _LdsModel.py \ _LocationEmbedList.py \ diff --git a/src/DisplayTabs/_AddrEmbedList.py b/src/DisplayTabs/_AddrEmbedList.py index 2674dcf7c..e8e0d161c 100644 --- a/src/DisplayTabs/_AddrEmbedList.py +++ b/src/DisplayTabs/_AddrEmbedList.py @@ -65,12 +65,14 @@ class AddrEmbedList(EmbeddedList): 'down' : _('Move the selected address downwards'), } + #index = column in model. Value = + # (name, sortcol in model, width, markup/text, weigth_col _column_names = [ - (_('Date'), 0, 150), - (_('Address'), 1, 225), - (_('City'), 2, 100), - (_('State'), 3, 100), - (_('Country'), 4, 75), + (_('Date'), 0, 150, 1, -1), + (_('Address'), 1, 225, 0, -1), + (_('City'), 2, 100, 0, -1), + (_('State'), 3, 100, 0, -1), + (_('Country'), 4, 75, 0, -1), ] def __init__(self, dbstate, uistate, track, data): diff --git a/src/DisplayTabs/_AttrEmbedList.py b/src/DisplayTabs/_AttrEmbedList.py index 55b6b8cf4..b1d7f6222 100644 --- a/src/DisplayTabs/_AttrEmbedList.py +++ b/src/DisplayTabs/_AttrEmbedList.py @@ -56,9 +56,11 @@ class AttrEmbedList(EmbeddedList): 'down' : _('Move the selected attribute downwards'), } + #index = column in model. Value = + # (name, sortcol in model, width, markup/text, weigth_col _column_names = [ - (_('Type'), 0, 250), - (_('Value'), 1, 200), + (_('Type'), 0, 250, 0, -1), + (_('Value'), 1, 200, 0, -1), ] def __init__(self, dbstate, uistate, track, data): diff --git a/src/DisplayTabs/_BackRefList.py b/src/DisplayTabs/_BackRefList.py index 374351c52..b4b35dc11 100644 --- a/src/DisplayTabs/_BackRefList.py +++ b/src/DisplayTabs/_BackRefList.py @@ -53,10 +53,12 @@ class BackRefList(EmbeddedList): _HANDLE_COL = 3 + #index = column in model. Value = + # (name, sortcol in model, width, markup/text, weigth_col _column_names = [ - (_('Type'), 0, 100), - (_('ID'), 1, 75), - (_('Name'), 2, 250), + (_('Type'), 0, 100, 0, -1), + (_('ID'), 1, 75, 0, -1), + (_('Name'), 2, 250, 0, -1), ] def __init__(self, dbstate, uistate, track, obj, refmodel, callback=None): diff --git a/src/DisplayTabs/_DataEmbedList.py b/src/DisplayTabs/_DataEmbedList.py index d7dd1e830..985c0f7a9 100644 --- a/src/DisplayTabs/_DataEmbedList.py +++ b/src/DisplayTabs/_DataEmbedList.py @@ -53,9 +53,11 @@ class DataEmbedList(EmbeddedList): 'down' : _('Move the selected data entry downwards'), } + #index = column in model. Value = + # (name, sortcol in model, width, markup/text _column_names = [ - (_('Key'), 0, 150), - (_('Value'), 1, 250), + (_('Key'), 0, 150, 0, -1), + (_('Value'), 1, 250, 0, -1), ] def __init__(self, dbstate, uistate, track, obj): diff --git a/src/DisplayTabs/_EmbeddedList.py b/src/DisplayTabs/_EmbeddedList.py index b2ea8a8bb..cce8fc3e1 100644 --- a/src/DisplayTabs/_EmbeddedList.py +++ b/src/DisplayTabs/_EmbeddedList.py @@ -2,6 +2,7 @@ # Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 2000-2006 Donald N. Allingham +# 2009 Benny Malengier # # 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 @@ -100,7 +101,7 @@ class EmbeddedList(ButtonTab): elif event.type == gtk.gdk.BUTTON_PRESS and event.button == 2: fun = self.get_middle_click() if fun: - fun(obj) + fun() def get_popup_menu_items(self): """ @@ -182,6 +183,7 @@ class EmbeddedList(ButtonTab): self.tree.connect('drag_data_get', self.drag_data_get) if not self.dbstate.db.readonly: self.tree.connect('drag_data_received', self.drag_data_received) + self.tree.connect('drag_motion', self.tree_drag_motion) def drag_data_get(self, widget, context, sel_data, info, time): """ @@ -237,6 +239,13 @@ class EmbeddedList(ButtonTab): elif self._DND_EXTRA and mytype == self._DND_EXTRA.drag_type: self.handle_extra_type(mytype, obj) + def tree_drag_motion(self, *args): + """ + On drag motion one wants the list to show as the database + representation so it is clear how save will change the data + """ + pass + def handle_extra_type(self, objtype, obj): pass @@ -419,13 +428,18 @@ class EmbeddedList(ButtonTab): name = self._column_names[pair[1]][0] renderer = gtk.CellRendererText() renderer.set_property('ellipsize', pango.ELLIPSIZE_END) - if name == _("Date"): - column = gtk.TreeViewColumn(name, renderer, markup=pair[1]) - else: + if self._column_names[pair[1]][3] == 0: column = gtk.TreeViewColumn(name, renderer, text=pair[1]) + else: + column = gtk.TreeViewColumn(name, renderer, markup=pair[1]) + if not self._column_names[pair[1]][4] == -1: + #apply weight attribute + column.add_attribute(renderer, "weight", + self._column_names[pair[1]][4]) # insert the colum into the tree column.set_resizable(True) + column.set_clickable(True) column.set_min_width(self._column_names[pair[1]][2]) column.set_sort_column_id(self._column_names[pair[1]][1]) self.columns.append(column) @@ -453,3 +467,7 @@ class EmbeddedList(ButtonTab): #model and tree are reset, allow _selection_changed again, and force it self.dirty_selection = False self._selection_changed() + self.post_rebuild() + + def post_rebuild(self): + pass diff --git a/src/DisplayTabs/_EventEmbedList.py b/src/DisplayTabs/_EventEmbedList.py index 629ec8c9c..6682a273d 100644 --- a/src/DisplayTabs/_EventEmbedList.py +++ b/src/DisplayTabs/_EventEmbedList.py @@ -58,13 +58,15 @@ class EventEmbedList(EmbeddedList): 'down' : _('Move the selected event downwards'), } + #index = column in model. Value = + # (name, sortcol in model, width, markup/text, weigth_col _column_names = [ - (_('Type'), 0, 100), - (_('Description'), 1, 175), - (_('ID'), 2, 60), - (_('Date'), 6, 150), - (_('Place'), 4, 140), - (_('Role'), 5, 80), + (_('Type'), 0, 100, 0, -1), + (_('Description'), 1, 175, 0, -1), + (_('ID'), 2, 60, 0, -1), + (_('Date'), 6, 150, 1, -1), + (_('Place'), 4, 140, 0, -1), + (_('Role'), 5, 80, 0, -1), ] def __init__(self, dbstate, uistate, track, obj): diff --git a/src/DisplayTabs/_FamilyLdsEmbedList.py b/src/DisplayTabs/_FamilyLdsEmbedList.py index a40a87667..660d85a76 100644 --- a/src/DisplayTabs/_FamilyLdsEmbedList.py +++ b/src/DisplayTabs/_FamilyLdsEmbedList.py @@ -45,12 +45,14 @@ class FamilyLdsEmbedList(LdsEmbedList): _HANDLE_COL = 5 # _DND_TYPE = DdTargets.ADDRESS + #index = column in model. Value = + # (name, sortcol in model, width, markup/text, weigth_col _column_names = [ - (_('Type'), 0, 150), - (_('Date'), 1, 150), - (_('Status'), 3, 75), - (_('Temple'), 2, 200), - (_('Place'), 3, 100), + (_('Type'), 0, 150, 0, -1), + (_('Date'), 1, 150, 1, -1), + (_('Status'), 3, 75, 0, -1), + (_('Temple'), 2, 200, 0, -1), + (_('Place'), 3, 100, 0, -1), ] def __init__(self, dbstate, uistate, track, data): diff --git a/src/DisplayTabs/_GroupEmbeddedList.py b/src/DisplayTabs/_GroupEmbeddedList.py new file mode 100644 index 000000000..d7bb3acb3 --- /dev/null +++ b/src/DisplayTabs/_GroupEmbeddedList.py @@ -0,0 +1,348 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000-2006 Donald N. Allingham +# 2009 Benny Malengier +# +# 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$ + +#------------------------------------------------------------------------- +# +# python +# +#------------------------------------------------------------------------- +from gettext import gettext as _ +import cPickle as pickle + +#------------------------------------------------------------------------- +# +# GTK libraries +# +#------------------------------------------------------------------------- +import gtk +import pango + +#------------------------------------------------------------------------- +# +# GRAMPS classes +# +#------------------------------------------------------------------------- +from DisplayTabs._EmbeddedList import EmbeddedList + +#------------------------------------------------------------------------- +# +# Classes +# +#------------------------------------------------------------------------- +class GroupEmbeddedList(EmbeddedList): + """ + This class provides the base class for all the list tabs that show + grouped data. + + It maintains a gtk.TreeView, including the selection and button sensitivity. + """ + + _CANDRAGGROUP = [] + _WORKGROUP = 0 + + def __init__(self, dbstate, uistate, track, name, build_model, + share_button=False, move_buttons=False, jump_button=False): + """ + Create a new list, using the passed build_model to populate the list. + """ + EmbeddedList.__init__(self, dbstate, uistate, track, name, build_model, + share_button, move_buttons, jump_button) + #connect click on the first column + self.columns[0].connect('clicked', self.groupcol_click) + for col in self.columns[1:]: + col.connect('clicked', self.col_click) + self.dbsort = True + + def groupcol_click(self, obj): + """ + The group column is clicked, sort it as it was + """ + self.columns[0].set_sort_order(gtk.SORT_ASCENDING) + self.rebuild() + self.dbsort = True + + def col_click(self, obj): + self.dbsort = False + + def _on_button_press(self, obj, event): + """ + Handle button press, not double-click, that is done in init_interface + """ + if event.type == gtk.gdk.BUTTON_PRESS and event.button == 3: + obj = self.get_selected() + if obj and obj[1]: + self._tmpgroup = obj[0] + self.right_click(obj[1], event) + elif event.type == gtk.gdk.BUTTON_PRESS and event.button == 2: + fun = self.get_middle_click() + if fun: + fun() + + def is_empty(self): + """ + Return True if the get_data returns a length greater than + 0. Typically, get_data returns the list of associated data. + """ + return len(self.get_data()[self._WORKGROUP]) == 0 + + def drag_data_get(self, widget, context, sel_data, info, time): + """ + Provide the drag_data_get function, which passes a tuple consisting of: + + 1) Drag type defined by the .drag_type field specfied by the value + assigned to _DND_TYPE + 2) The id value of this object, used for the purpose of determining + the source of the object. If the source of the object is the same + as the object, we are doing a reorder instead of a normal drag + and drop + 3) Pickled data. The pickled version of the selected object + 4) Source row. Used for a reorder to determine the original position + of the object + """ + + # get the selected object, returning if not is defined + obj = self.get_selected() + if not obj or obj[1] is None: + #nothing selected or a grouping selected + return + if not obj[0] in self._CANDRAGGROUP: + return + + # pickle the data, and build the tuple to be passed + value = (self._DND_TYPE.drag_type, id(self), obj[1], + self.find_index(obj)) + data = pickle.dumps(value) + + # pass as a string (8 bits) + sel_data.set(sel_data.target, 8, data) + + def drag_data_received(self, widget, context, x, y, sel_data, info, time): + """ + Handle the standard gtk interface for drag_data_received. + + If the selection data is define, extract the value from sel_data.data, + and decide if this is a move or a reorder. + """ + if sel_data and sel_data.data: + (mytype, selfid, obj, row_from) = pickle.loads(sel_data.data) + + # make sure this is the correct DND type for this object + if mytype == self._DND_TYPE.drag_type: + + # determine the destination row + row = self._find_row(x, y) + + # if this is same object, we have a move, otherwise, + # it is a standard drag-n-drop + + if id(self) == selfid and self.get_selected() is not None: + self._move(row_from, row, obj) + else: + self._handle_drag(row, obj) + self.rebuild() + elif self._DND_EXTRA and mytype == self._DND_EXTRA.drag_type: + self.handle_extra_type(mytype, obj) + + def tree_drag_motion(self, *args): + """ + On drag motion one wants the list to show as the database + representation so it is clear how save will change the data + """ + if not self.dbsort: + self.columns[0].clicked() + + def find_index(self, obj): + """ + Returns the index of the object within the associated data. + This will be a path (groupindex, index) + """ + data = self.get_data() + groupindex = None + index = None + for groupindex, group in enumerate(data): + try: + index = group.index(obj[1]) + break + except ValueError: + pass + return (groupindex, index) + + def _find_row(self, x, y): + """ + Return a path as [groupindex, index] of the row on x,y. + If no row, then a new line in the working group is returned + """ + dest = self.tree.get_dest_row_at_pos(x, y) + if dest is None: + if self.is_empty(): + return [self._WORKGROUP, 0] + else: + return [self._WORKGROUP, len(self.get_data()[self._WORKGROUP])] + else: + row = dest[0] + if len(row) == 1: + #drop on a group node, change to first real row + row = (row[0], 0) + return row + + def _handle_drag(self, row, obj): + """ + drag from external place to row of obj + """ + if row[0] == self._WORKGROUP: + self.get_data()[self._WORKGROUP].insert(row[1], obj) + self.changed = True + self.rebuild() + else: + self.dropnotworkgroup(row, obj) + + def dropnotworkgroup(self, row, obj): + """ + Drop of obj on row that is not WORKGROUP + """ + pass + + def _move(self, row_from, row_to, obj): + """ + Drag and drop move of the order. Allow in workgroup + """ + if row_from[0] == row_to[0] and row_from[0] == self._WORKGROUP: + dlist = self.get_data()[self._WORKGROUP] + if row_from[1] < row_to[1]: + dlist.insert(row_to[1], obj) + del dlist[row_from[1]] + else: + del dlist[row_from[1]] + dlist.insert(row_to[1]-1, obj) + self.changed = True + self.rebuild() + elif row_from[0] == self._WORKGROUP: + self.move_away_work(row_from, row_to, obj) + elif row_to[0] == self._WORKGROUP: + self.move_to_work(row_from, row_to, obj) + + def move_away_work(self, row_from, row_to, obj): + """ + move from the workgroup to a not workgroup + handle in inherited class, default is nothing changes + """ + pass + + def move_to_work(self, row_from, row_to, obj): + """ + move from a non workgroup to the workgroup + handle in inherited class, default is nothing changes + """ + pass + + def _move_up(self, row_from, obj, selmethod=None): + """ + Move the item a position up in the EmbeddedList. + Eg: 0,1,2,3 needs to become 0,2,1,3, here row_from = 2 + """ + if row_from[0] == self._WORKGROUP: + if selmethod : + dlist = selmethod() + else : + dlist = self.get_data()[self._WORKGROUP] + del dlist[row_from[1]] + dlist.insert(row_from[1]-1, obj) + self.changed = True + self.rebuild() + #select the row + self.tree.get_selection().select_path((self._WORKGROUP, + row_from[1]-1)) + else: + self._move_up_notwork(row_from, obj, selmethod) + + def _move_up_notwork(self, row_from, obj, selmethod=None): + """ + move up outside of workgroup + """ + pass + + def _move_down(self, row_from, obj, selmethod=None): + """ + Move the item a position down in the EmbeddedList. + Eg: 0,1,2,3 needs to become 0,2,1,3, here row_from = 1 + """ + if row_from[0] == self._WORKGROUP: + if selmethod : + dlist = selmethod() + else : + dlist = self.get_data()[self._WORKGROUP] + del dlist[row_from[1]] + dlist.insert(row_from[1]+1, obj) + self.changed = True + self.rebuild() + #select the row + self.tree.get_selection().select_path((self._WORKGROUP, + row_from[1]+1)) + else: + self._move_down_notwork(row_from, obj, selmethod) + + def _move_down_notwork(self, row_from, obj, selmethod=None): + """ + move down outside of workgroup + """ + pass + + def get_icon_name(self): + """ + Specifies the basic icon used for a generic list. Typically, + a derived class will override this. The icon chosen is the + STOCK_JUSTIFY_FILL icon, which in the default GTK style + looks kind of like a list. + """ + return gtk.STOCK_JUSTIFY_FILL + + def del_button_clicked(self, obj): + ref = self.get_selected() + if ref and ref[1] is not None: + if ref[0]==self._WORKGROUP: + ref_list = self.get_data()[self._WORKGROUP] + ref_list.remove(ref[1]) + self.changed = True + self.rebuild() + else: + self.del_notwork(ref) + + def del_notwork(self, ref): + """ + delete of ref asked that is not part of workgroup + """ + pass + + def up_button_clicked(self, obj): + ref = self.get_selected() + if ref and ref[1] is not None: + pos = self.find_index(ref) + if pos[1] > 0 : + self._move_up(pos, ref[1]) + + def down_button_clicked(self, obj): + ref = self.get_selected() + if ref and ref[1] is not None: + pos = self.find_index(ref) + if pos[1] >=0 and pos[1] < len(self.get_data()[pos[0]])-1: + self._move_down(pos, ref[1]) diff --git a/src/DisplayTabs/_LdsEmbedList.py b/src/DisplayTabs/_LdsEmbedList.py index cbd5e8820..20d889e01 100644 --- a/src/DisplayTabs/_LdsEmbedList.py +++ b/src/DisplayTabs/_LdsEmbedList.py @@ -55,12 +55,14 @@ class LdsEmbedList(EmbeddedList): 'down' : _('Move the selected LDS ordinance downwards'), } + #index = column in model. Value = + # (name, sortcol in model, width, markup/text, weigth_col _column_names = [ - (_('Type'), 0, 150), - (_('Date'), 1, 150), - (_('Status'), 3, 75), - (_('Temple'), 2, 200), - (_('Place'), 3, 100), + (_('Type'), 0, 150, 0, -1), + (_('Date'), 1, 150, 1, -1), + (_('Status'), 3, 75, 0, -1), + (_('Temple'), 2, 200, 0, -1), + (_('Place'), 3, 100, 0, -1), ] def __init__(self, dbstate, uistate, track, data): diff --git a/src/DisplayTabs/_LocationEmbedList.py b/src/DisplayTabs/_LocationEmbedList.py index f753f735a..e641c562a 100644 --- a/src/DisplayTabs/_LocationEmbedList.py +++ b/src/DisplayTabs/_LocationEmbedList.py @@ -48,12 +48,14 @@ class LocationEmbedList(EmbeddedList): _HANDLE_COL = 5 _DND_TYPE = DdTargets.LOCATION + #index = column in model. Value = + # (name, sortcol in model, width, markup/text, weigth_col _column_names = [ - (_('Street'), 0, 150), - (_('City'), 1, 100), - (_('County'), 2, 100), - (_('State/Province'), 3, 100), - (_('Country'), 4, 75), + (_('Street'), 0, 150, 0, -1), + (_('City'), 1, 100, 0, -1), + (_('County'), 2, 100, 0, -1), + (_('State/Province'), 3, 100, 0, -1), + (_('Country'), 4, 75, 0, -1), ] def __init__(self, dbstate, uistate, track, data): diff --git a/src/DisplayTabs/_NameEmbedList.py b/src/DisplayTabs/_NameEmbedList.py index a21ce76ba..a33deee1e 100644 --- a/src/DisplayTabs/_NameEmbedList.py +++ b/src/DisplayTabs/_NameEmbedList.py @@ -2,6 +2,7 @@ # Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 2000-2006 Donald N. Allingham +# 2009 Benny Malengier # # 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 @@ -43,17 +44,19 @@ import gen.lib import Errors from DdTargets import DdTargets from DisplayTabs._NameModel import NameModel -from DisplayTabs._EmbeddedList import EmbeddedList +from DisplayTabs._GroupEmbeddedList import GroupEmbeddedList #------------------------------------------------------------------------- # # # #------------------------------------------------------------------------- -class NameEmbedList(EmbeddedList): +class NameEmbedList(GroupEmbeddedList): _HANDLE_COL = 2 _DND_TYPE = DdTargets.NAME + _CANDRAGGROUP = [NameModel._DEFINDEX, NameModel._ALTINDEX] + _WORKGROUP = NameModel._ALTINDEX _MSG = { 'add' : _('Create and add a new name'), @@ -63,9 +66,16 @@ class NameEmbedList(EmbeddedList): 'down' : _('Move the selected name downwards'), } + #index = column in model. Value = + # (name, sortcol in model, width, markup/text, weigth_col _column_names = [ - (_('Name'), 0, 250), - (_('Type'), 1, 100), + (_('Name'), -1, 250, 0, NameModel.COL_FONTWEIGHT[0]), + (_('Type'), NameModel.COL_TYPE[0], 100, 0, -1), + None, + None, + (_('Group As'), NameModel.COL_GROUPAS[0],100, 0, -1), + (_('Source'), NameModel.COL_HASSOURCE[0],60, 0, -1), + (_('Note Preview'), NameModel.COL_NOTEPREVIEW[0], 250, 0, -1), ] def __init__(self, dbstate, uistate, track, data, person, callback): @@ -73,33 +83,62 @@ class NameEmbedList(EmbeddedList): self.person = person self.callback = callback - EmbeddedList.__init__(self, dbstate, uistate, track, _('_Names'), + GroupEmbeddedList.__init__(self, dbstate, uistate, track, _('_Names'), NameModel, move_buttons=True) + self.tree.expand_all() def get_data(self): - return self.data + return ([self.person.get_primary_name()], + self.data) def column_order(self): - return ((1, 0), (1, 1)) + """ + The columns to show as a tuple of tuples containing + tuples (show/noshow, model column) + """ + return ((1, 0), (1, 1), (1, 4), (1, 5), (1, 6)) def get_popup_menu_items(self): - return [ - (True, True, gtk.STOCK_ADD, self.add_button_clicked), - (False,True, gtk.STOCK_EDIT, self.edit_button_clicked), - (True, True, gtk.STOCK_REMOVE, self.del_button_clicked), - (True, False, _('Set as default name'), self.name_button_clicked), - ] + if self._tmpgroup == self._WORKGROUP: + return [ + (True, True, gtk.STOCK_ADD, self.add_button_clicked), + (False,True, gtk.STOCK_EDIT, self.edit_button_clicked), + (True, True, gtk.STOCK_REMOVE, self.del_button_clicked), + (True, False, _('Set as default name'), self.name_button_clicked), + ] + else: + return [ + (True, True, gtk.STOCK_ADD, self.add_button_clicked), + (False,True, gtk.STOCK_EDIT, self.edit_button_clicked), + ] def name_button_clicked(self, obj): name = self.get_selected() + if name and name[1]: + self.set_default_name(name[1]) + + def set_default_name(self, name): pname = self.person.get_primary_name() - if name: - self.person.set_primary_name(name) - self.data.remove(name) + self.person.set_primary_name(name) + remove = [] + for altname in self.data: + if altname.is_equal(name): + remove.append(altname) + for altname in remove: + self.data.remove(altname) + #only non empty name should move to alternative names + if not name.is_equal(gen.lib.Name()): self.data.append(pname) self.rebuild() self.callback() - + + def update_defname(self): + """ + callback from person editor if change to the preferred name happens + """ + self.model.update_defname(self.person.get_primary_name()) + self.tree.expand_all() + def add_button_clicked(self, obj): name = gen.lib.Name() try: @@ -111,19 +150,48 @@ class NameEmbedList(EmbeddedList): pass def add_callback(self, name): - self.get_data().append(name) + self.get_data()[self._WORKGROUP].append(name) self.rebuild() def edit_button_clicked(self, obj): name = self.get_selected() - if name: + if name and name[1] is not None: try: from Editors import EditName - - EditName(self.dbstate, self.uistate, self.track, - name, self.edit_callback) + if name[0] == NameModel._ALTINDEX: + EditName(self.dbstate, self.uistate, self.track, + name[1], self.edit_callback) + elif name[0] == NameModel._DEFINDEX: + EditName(self.dbstate, self.uistate, self.track, + name[1], self.editdef_callback) except Errors.WindowActiveError: pass def edit_callback(self, name): self.rebuild() + + def editdef_callback(self, name): + """ + callback after default name has changed + """ + self.rebuild() + self.callback() + + def dropnotworkgroup(self, row, obj): + """ + Drop of obj on row that is not WORKGROUP + """ + if row[0] == NameModel._DEFINDEX: + #drop on default name + self.set_default_name(obj) + + def move_away_work(self, row_from, row_to, obj): + """ + move from the workgroup to a not workgroup + we allow to change the default name like this + """ + if row_from[0] == self._WORKGROUP and row_to[0] == NameModel._DEFINDEX: + self.set_default_name(obj) + + def post_rebuild(self): + self.tree.expand_all() diff --git a/src/DisplayTabs/_NameModel.py b/src/DisplayTabs/_NameModel.py index a4894d17a..1d7b913b3 100644 --- a/src/DisplayTabs/_NameModel.py +++ b/src/DisplayTabs/_NameModel.py @@ -2,6 +2,7 @@ # Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 2000-2007 Donald N. Allingham +# 2009 Benny Malengier # # 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 @@ -20,12 +21,20 @@ # $Id$ +#------------------------------------------------------------------------- +# +# python libraries +# +#------------------------------------------------------------------------- +from gettext import gettext as _ + #------------------------------------------------------------------------- # # GTK libraries # #------------------------------------------------------------------------- import gtk +from pango import WEIGHT_NORMAL, WEIGHT_BOLD #------------------------------------------------------------------------- # @@ -39,14 +48,90 @@ from BasicUtils import name_displayer # NameModel # #------------------------------------------------------------------------- -class NameModel(gtk.ListStore): + +YES = _('Yes') +NO = _('No') + +class NameModel(gtk.TreeStore): + #tree groups defined + _DEFINDEX = 0 + _DEFNAME = _('Default Name') + _ALTINDEX = 1 + _ALTNAME = _('Alternative Names') + + _GROUPSTRING = _('%(groupname)s - %(groupnumber)d') + + COL_NAME = (0, str) + COL_TYPE = (1, str) + COL_DATA = (2, object) + COL_FONTWEIGHT = (3, int) + COL_GROUPAS = (4, str) + COL_HASSOURCE = (5, str) + COL_NOTEPREVIEW = (6, str) + + COLS = (COL_NAME, COL_TYPE, COL_DATA, COL_FONTWEIGHT, COL_GROUPAS, + COL_HASSOURCE, COL_NOTEPREVIEW) def __init__(self, obj_list, db): - gtk.ListStore.__init__(self, str, str, object) + typeobjs = (x[1] for x in self.COLS) + gtk.TreeStore.__init__(self, *typeobjs) self.db = db - for obj in obj_list: - self.append(row=[ - name_displayer.display_name(obj), - str(obj.get_type()), - obj, - ]) + for index, group in enumerate(obj_list): + + parentiter = self.append(None, row=self.row_group(index, group)) + for obj in group: + self.append(parentiter, row = self.row(index, obj)) + + def row_group(self, index, group): + name = self.namegroup(index, len(group)) + return [name, '', (index, None), WEIGHT_NORMAL, '', '', ''] + + def row(self, index, name): + """ + Returns the row of the model in group index, and name as a + list + """ + return [name_displayer.display_name(name), + str(name.get_type()), + (index, name), + self.colweight(index), + name.get_group_as(), + self.hassource(name), + self.notepreview(name) + ] + def colweight(self, index): + if index == self._DEFINDEX: + return WEIGHT_BOLD + else: + return WEIGHT_NORMAL + + def namegroup(self, groupindex, length): + if groupindex == self._DEFINDEX: + return self._DEFNAME + return self._GROUPSTRING % {'groupname': self._ALTNAME, + 'groupnumber': length} + + def update_defname(self, defname): + """ + callback if change to the preferred name happens + """ + #default name is path (0,0) + self.remove(self.get_iter((self._DEFINDEX, 0))) + self.insert(self.get_iter(self._DEFINDEX), 0, + row=self.row(self._DEFINDEX, defname)) + + def hassource(self, name): + if len(name.get_source_references()): + return YES + return NO + + def notepreview(self, name): + nlist = name.get_note_list() + if nlist: + note = self.db.get_note_from_handle(nlist[0]) + text = unicode(note.get().replace('\n', ' ')) + if len(text) > 80: + text = text[:80]+"..." + return text + else: + return '' diff --git a/src/DisplayTabs/_NoteTab.py b/src/DisplayTabs/_NoteTab.py index 723b3cddc..fef18dd1a 100644 --- a/src/DisplayTabs/_NoteTab.py +++ b/src/DisplayTabs/_NoteTab.py @@ -68,9 +68,11 @@ class NoteTab(EmbeddedList): 'down' : _('Move the selected note downwards'), } + #index = column in model. Value = + # (name, sortcol in model, width, markup/text, weigth_col _column_names = [ - (_('Type'), 0, 100), - (_('Preview'), 1, 200), + (_('Type'), 0, 100, 0, -1), + (_('Preview'), 1, 200, 0, -1), ] def __init__(self, dbstate, uistate, track, data, callertitle=None, diff --git a/src/DisplayTabs/_PersonRefEmbedList.py b/src/DisplayTabs/_PersonRefEmbedList.py index 90587ecff..4c84359ea 100644 --- a/src/DisplayTabs/_PersonRefEmbedList.py +++ b/src/DisplayTabs/_PersonRefEmbedList.py @@ -56,10 +56,12 @@ class PersonRefEmbedList(EmbeddedList): 'down' : _('Move the selected association downwards'), } + #index = column in model. Value = + # (name, sortcol in model, width, markup/text _column_names = [ - (_('Name'), 0, 250), - (_('ID'), 1, 100), - (_('Association'), 2, 100), + (_('Name'), 0, 250, 0, -1), + (_('ID'), 1, 100, 0, -1), + (_('Association'), 2, 100, 0, -1), ] def __init__(self, dbstate, uistate, track, data): diff --git a/src/DisplayTabs/_RepoEmbedList.py b/src/DisplayTabs/_RepoEmbedList.py index fa4b87a55..6b67631f0 100644 --- a/src/DisplayTabs/_RepoEmbedList.py +++ b/src/DisplayTabs/_RepoEmbedList.py @@ -58,11 +58,13 @@ class RepoEmbedList(EmbeddedList): 'down' : _('Move the selected repository downwards'), } + #index = column in model. Value = + # (name, sortcol in model, width, markup/text, weigth_col _column_names = [ - (_('ID'), 0, 75), - (_('Title'), 1, 200), - (_('Call Number'), 2, 125), - (_('Type'), 3, 100), + (_('ID'), 0, 75, 0, -1), + (_('Title'), 1, 200, 0, -1), + (_('Call Number'), 2, 125, 0, -1), + (_('Type'), 3, 100, 0, -1), ] def __init__(self, dbstate, uistate, track, obj): diff --git a/src/DisplayTabs/_SourceEmbedList.py b/src/DisplayTabs/_SourceEmbedList.py index 7b3f8a998..41352cd97 100644 --- a/src/DisplayTabs/_SourceEmbedList.py +++ b/src/DisplayTabs/_SourceEmbedList.py @@ -58,11 +58,13 @@ class SourceEmbedList(EmbeddedList): 'down' : _('Move the selected source downwards'), } + #index = column in model. Value = + # (name, sortcol in model, width, markup/text, weigth_col _column_names = [ - (_('ID'), 0, 75), - (_('Title'), 1, 200), - (_('Author'), 2, 125), - (_('Page'), 3, 100), + (_('ID'), 0, 75, 0, -1), + (_('Title'), 1, 200, 0, -1), + (_('Author'), 2, 125, 0, -1), + (_('Page'), 3, 100, 0, -1), ] def __init__(self, dbstate, uistate, track, obj): diff --git a/src/DisplayTabs/_WebEmbedList.py b/src/DisplayTabs/_WebEmbedList.py index c726f02f6..5bfaaa5d9 100644 --- a/src/DisplayTabs/_WebEmbedList.py +++ b/src/DisplayTabs/_WebEmbedList.py @@ -58,10 +58,12 @@ class WebEmbedList(EmbeddedList): 'jump' : _('Jump to the selected web address'), } + #index = column in model. Value = + # (name, sortcol in model, width, markup/text, weigth_col _column_names = [ - (_('Type') , 0, 100), - (_('Path') , 1, 200), - (_('Description'), 2, 150), + (_('Type') , 0, 100, 0, -1), + (_('Path') , 1, 200, 0, -1), + (_('Description'), 2, 150, 0, -1), ] def __init__(self, dbstate, uistate, track, data): diff --git a/src/Editors/_EditFamily.py b/src/Editors/_EditFamily.py index 6bf8a3b46..72a2c35d9 100644 --- a/src/Editors/_EditFamily.py +++ b/src/Editors/_EditFamily.py @@ -288,7 +288,7 @@ class ChildEmbedList(EmbeddedList): pass break - def edit_child_button_clicked(self, obj): + def edit_child_button_clicked(self): handle = self.get_selected() if handle: from Editors import EditPerson diff --git a/src/Editors/_EditName.py b/src/Editors/_EditName.py index 19d5af91c..6fe64ec3d 100644 --- a/src/Editors/_EditName.py +++ b/src/Editors/_EditName.py @@ -2,6 +2,7 @@ # Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 2000-2007 Donald N. Allingham +# 2008-2009 Benny Malengier # 2009 Gary Burton # # This program is free software; you can redistribute it and/or modify diff --git a/src/Editors/_EditPerson.py b/src/Editors/_EditPerson.py index c964b28e4..602416a02 100644 --- a/src/Editors/_EditPerson.py +++ b/src/Editors/_EditPerson.py @@ -304,7 +304,7 @@ class EditPerson(EditPrimary): self.top.get_object("call"), self.top.get_object("prefixentry"), ]: - obj.connect('changed', self._changed_title) + obj.connect('changed', self._changed_name) def _create_tabbed_pages(self): """ @@ -399,18 +399,18 @@ class EditPerson(EditPrimary): self.top.get_object('vbox').pack_start(notebook, True) - def _changed_title(self, obj): + def _changed_name(self, obj): """ callback to changes typed by user to the person name. - Update the window title + Update the window title, and default name in name tab """ self.update_title(self.get_menu_title()) + self.name_list.update_defname() def name_callback(self): """ - Callback if changes happen in the name tab. - The Preferred Name is _NOT_ part of the name tab, but name tab allows - reorder and hence setting of new primary name. + Callback if changes happen in the name tab that impact the preferred + name. """ self.pname = self.obj.get_primary_name()