# # Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 2000 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 # #------------------------------------------------------------------------- # # standard python modules # #------------------------------------------------------------------------- import pickle #------------------------------------------------------------------------- # # GTK/Gnome modules # #------------------------------------------------------------------------- import gobject import gtk import gtk.glade from gtk.gdk import ACTION_COPY, BUTTON1_MASK #------------------------------------------------------------------------- # # gramps modules # #------------------------------------------------------------------------- import const import sort import Utils import GrampsCfg import AddSpouse import SelectChild import DisplayTrace import Marriage import ChooseParents import RelLib from intl import gettext as _ from QuestionDialog import QuestionDialog,WarningDialog pycode_tgts = [('child', 0, 0)] #------------------------------------------------------------------------- # # FamilyView # #------------------------------------------------------------------------- class FamilyView: def __init__(self,parent): self.parent = parent self.top = parent.gtop self.ap_data = self.top.get_widget('ap_data').get_buffer() self.swap_btn = self.top.get_widget('swap_spouse_btn') self.add_spouse_btn = self.top.get_widget('add_spouse') self.remove_spouse_btn = self.top.get_widget('remove_spouse') self.ap_parents = self.top.get_widget('ap_parents') self.ap_parents_model = gtk.ListStore(gobject.TYPE_STRING) self.ap_parents.set_model(self.ap_parents_model) self.ap_selection = self.ap_parents.get_selection() column = gtk.TreeViewColumn('',gtk.CellRendererText(),text=0) self.ap_parents.append_column(column) self.ap_parents.connect('button-press-event',self.edit_ap_parents) self.sp_parents = self.top.get_widget('sp_parents') self.sp_parents_model = gtk.ListStore(gobject.TYPE_STRING) self.sp_parents.set_model(self.sp_parents_model) self.sp_selection = self.sp_parents.get_selection() column = gtk.TreeViewColumn('',gtk.CellRendererText(),text=0) self.sp_parents.append_column(column) self.sp_parents.connect('button-press-event',self.edit_sp_parents) self.spouse_list = self.top.get_widget('sp_list') self.spouse_model = gtk.ListStore(gobject.TYPE_STRING) self.spouse_list.set_model(self.spouse_model) self.spouse_selection = self.spouse_list.get_selection() self.spouse_selection.connect('changed',self.spouse_changed) self.spouse_list.connect('button-press-event',self.edit_relationship) self.top.get_widget('add_parents').connect('clicked',self.add_parents_clicked) self.top.get_widget('del_parents').connect('clicked',self.del_parents_clicked) self.top.get_widget('add_spparents').connect('clicked',self.add_sp_parents) self.top.get_widget('del_spparents').connect('clicked',self.del_sp_parents) self.top.get_widget('fam_back').connect('clicked',self.child_back) self.top.get_widget('del_child_btn').connect('clicked',self.remove_child_clicked) self.top.get_widget('add_child_btn').connect('clicked',self.add_child_clicked) column = gtk.TreeViewColumn('',gtk.CellRendererText(),text=0) self.spouse_list.append_column(column) self.selected_spouse = None self.child_list = self.top.get_widget('chlist') self.child_list.drag_dest_set(gtk.DEST_DEFAULT_ALL,pycode_tgts,ACTION_COPY) self.child_list.drag_source_set(BUTTON1_MASK, pycode_tgts, ACTION_COPY) self.child_list.connect('drag_data_get', self.drag_data_get) self.child_list.connect('drag_begin', self.drag_begin) self.child_list.connect('drag_data_received',self.drag_data_received) self.child_model = gtk.ListStore(gobject.TYPE_INT, gobject.TYPE_STRING, gobject.TYPE_STRING,gobject.TYPE_STRING, gobject.TYPE_STRING,gobject.TYPE_STRING, gobject.TYPE_STRING) self.child_selection = self.child_list.get_selection() self.child_list.connect('button-press-event',self.on_child_list_button_press) self.top.get_widget('ap_parents_btn').connect('clicked',self.ap_parents_clicked) self.top.get_widget('sp_parents_btn').connect('clicked',self.sp_parents_clicked) self.swap_btn.connect('clicked',self.spouse_swap) self.remove_spouse_btn.connect('clicked',self.remove_spouse) self.add_spouse_btn.connect('clicked',self.add_spouse) self.child_list.set_model(self.child_model) self.child_list.set_search_column(0) self.child_selection = self.child_list.get_selection() Utils.build_columns(self.child_list, [ (_(''),30,0), (_('Name'),250,1), (_('ID'),50,2), (_('Gender'),100,3), (_('Birth Date'),150,4), (_('Status'),150,5), ('',0,6) ]) def on_child_list_button_press(self,obj,event): if event.type == gtk.gdk._2BUTTON_PRESS and event.button == 1: model, iter = self.child_selection.get_selected() if iter: id = self.child_model.get_value(iter,2) self.parent.load_person(self.parent.db.getPerson(id)) def spouse_changed(self,obj): model, iter = obj.get_selected() if not iter: self.display_marriage(None) else: row = model.get_path(iter) self.display_marriage(self.person.getFamilyList()[row[0]]) def edit_relationship(self,obj,event): if event.type == gtk.gdk._2BUTTON_PRESS and event.button == 1: if self.person: try: if self.selected_spouse: Marriage.Marriage(self.family,self.parent.db, self.parent.new_after_edit) else: AddSpouse.AddSpouse(self.parent.db,self.person, self.load_family, self.parent.redisplay_person_list, self.family) except: DisplayTrace.DisplayTrace() def add_spouse(self,obj): if not self.person: return try: AddSpouse.AddSpouse(self.parent.db, self.person, self.load_family, self.parent.redisplay_person_list) except: DisplayTrace.DisplayTrace() def add_child_clicked(self,obj): if not self.person: return try: SelectChild.SelectChild(self.parent.db, self.family, self.person, self.load_family, self.parent.update_person_list) except: DisplayTrace.DisplayTrace() def remove_child_clicked(self,obj): if not self.family or not self.person: return model, iter = self.child_selection.get_selected() if not iter: return id = self.child_model.get_value(iter,2) child = self.parent.db.getPerson(id) self.family.removeChild(child) child.removeAltFamily(child) if len(self.family.getChildList()) == 0: if self.family.getFather() == None: self.delete_family_from(self.family.getMother()) elif self.family.getMother() == None: self.delete_family_from(self.family.getFather()) Utils.modified() self.load_family() def remove_spouse(self,obj): if self.selected_spouse: name = self.selected_spouse.getPrimaryName().getRegularName() QuestionDialog(_('Delete Spouse'), _('Do you wish to remove %s as a spouse?') % name, self.really_remove_spouse) def really_remove_spouse(self): """Delete the currently selected spouse from the family""" if self.person == None: return if self.selected_spouse == self.family.getFather(): self.family.setMother(None) else: self.family.setFather(None) if self.selected_spouse: self.selected_spouse.removeFamily(self.family) if len(self.family.getChildList()) == 0: self.person.removeFamily(self.family) self.parent.db.deleteFamily(self.family) if len(self.person.getFamilyList()) > 0: self.load_family(self.person.getFamilyList()[0]) else: self.load_family() else: self.load_family() if len(self.person.getFamilyList()) <= 1: self.spouse_selection.set_mode(gtk.SELECTION_NONE) Utils.modified() def spouse_swap(self,obj): if self.selected_spouse: self.parent.active_person = self.selected_spouse self.load_family() def ap_parents_clicked(self,obj): self.change_families(self.person) def sp_parents_clicked(self,obj): self.change_families(self.selected_spouse) def change_families(self,person): if not person: return plist = person.getParentList() if len(plist) == 0: return if len(plist) == 1: family,m,r = plist[0] else: model, iter = self.ap_selection.get_selected() path = model.get_path(iter) family,m,r = plist[path[0]] if family.getFather(): person = family.getFather() else: person = family.getMother() self.parent.change_active_person(person) self.load_family(family) def load_family(self,family=None): self.person = self.parent.active_person if not self.person: return n = "%s\n\tb. %s\n\td. %s " % (self.person.getPrimaryName().getName(), self.person.getBirth().getDate(), self.person.getDeath().getDate()) self.ap_data.set_text(n,len(n)) self.selected_spouse = None self.spouse_model.clear() self.child_model.clear() self.sp_parents_model.clear() splist = self.person.getFamilyList() f = None first_family = None first_spouse = None for f in splist: if not f: continue if f.getFather() == self.person: sp = f.getMother() else: sp = f.getFather() iter = self.spouse_model.append() if f == family: first_spouse = sp first_family = f elif first_spouse == None: first_spouse = sp first_family = f if len(splist) > 1: self.spouse_selection.set_mode(gtk.SELECTION_SINGLE) self.spouse_selection.select_iter(iter) else: self.spouse_selection.set_mode(gtk.SELECTION_NONE) if sp: if f.getMarriage(): mdate = " - %s" % f.getMarriage().getDate() else: mdate = "" v = "%s\n\t%s%s" % (sp.getPrimaryName().getName(), f.getRelationship(),mdate) self.spouse_model.set(iter,0,v) else: self.spouse_model.set(iter,0,"unknown\n") if first_family: self.display_marriage(first_family) self.update_list(self.ap_parents_model,self.ap_parents,self.person) self.family = first_family def update_list(self,model,tree,person): model.clear() sel = None selection = tree.get_selection() list = person.getParentList() for (f,mrel,frel) in list: father = self.nameof(_("Father"),f.getFather(),frel) mother = self.nameof(_("Mother"),f.getMother(),mrel) iter = model.append() if not sel: sel = iter v = "%s\n%s" % (father,mother) model.set(iter,0,v) if len(list) > 1: selection.set_mode(gtk.SELECTION_SINGLE) selection.select_iter(sel) else: selection.set_mode(gtk.SELECTION_NONE) def nameof(self,l,p,mode): if p: n = p.getPrimaryName().getName() return _("%s: %s\n\tRelationship: %s") % (l,n,mode) else: return _("%s: unknown") % (l) def delete_family_from(self,person): person.removeFamily(self.family) self.parent.db.deleteFamily(self.family) flist = self.person.getFamilyList() if len(flist) > 0: self.family = flist[0][0] else: self.family = None def display_marriage(self,family): self.child_model.clear() if not family: return else: self.family = family if family.getFather() == self.person: self.selected_spouse = family.getMother() else: self.selected_spouse = family.getFather() if self.selected_spouse: self.update_list(self.sp_parents_model,self.sp_parents, self.selected_spouse) i = 0 fiter = None child_list = list(family.getChildList()) child_list.sort(sort.by_birthdate) self.child_map = {} attr = "" for child in child_list: status = _("Unknown") if child.getGender() == RelLib.Person.male: gender = const.male elif child.getGender() == RelLib.Person.female: gender = const.female else: gender = const.unknown for fam in child.getParentList(): if fam[0] == family: if self.person == family.getFather(): status = "%s/%s" % (_(fam[2]),_(fam[1])) else: status = "%s/%s" % (_(fam[1]),_(fam[2])) iter = self.child_model.append() self.child_map[iter] = child.getId() if fiter == None: fiter = self.child_model.get_path(iter) self.child_model.set(iter, 0,(i+1), 1,GrampsCfg.nameof(child), 2,child.getId(), 3,gender, 4,Utils.birthday(child), 5,status, 6,attr) def edit_ap_parents(self,obj,event): if event.type == gtk.gdk._2BUTTON_PRESS and event.button == 1: self.parent_editor(self.person,self.ap_selection) def edit_sp_parents(self,obj,event): if event.type == gtk.gdk._2BUTTON_PRESS and event.button == 1: self.parent_editor(self.selected_spouse,self.sp_selection) def add_parents_clicked(self,obj): self.parent_add(self.person) def add_sp_parents(self,obj): if self.selected_spouse: self.parent_add(self.selected_spouse) def del_parents_clicked(self,obj): if len(self.person.getParentList()) == 0: return QuestionDialog(_('Delete Parents'), _('Do you wish to remove the selected parents?'), self.really_del_parents) def really_del_parents(self): self.parent_deleter(self.person,self.ap_selection) def del_sp_parents(self,obj): if not self.selected_spouse or len(self.selected_spouse.getParentList()) == 0: return QuestionDialog(_('Delete Parents'), _('Do you wish to remove the selected parents?'), self.really_del_sp_parents) def really_del_sp_parents(self): self.parent_deleter(self.selected_spouse,self.sp_selection) def child_back(self,obj): """makes the currently select child the active person""" model, iter = self.child_selection.get_selected() if iter: id = self.child_model.get_value(iter,2) self.parent.change_active_person(self.parent.db.getPerson(id)) self.load_family() def parent_editor(self,person,selection): if not person: return plist = person.getParentList() if len(plist) == 0: return elif len(plist) == 1: parents,mrel,frel = plist[0] else: model, iter = selection.get_selected() if not iter: return row = model.get_path(iter) parents,mrel,frel = plist[row[0]] try: ChooseParents.ModifyParents(self.parent.db,person,parents, self.load_family,self.parent.full_update) except: DisplayTrace.DisplayTrace() def parent_add(self,person): if not person: return try: ChooseParents.ChooseParents(self.parent.db,person,None, self.load_family,self.parent.full_update) except: DisplayTrace.DisplayTrace() def parent_deleter(self,person,selection): if not person: return plist = person.getParentList() if len(plist) == 0: return if len(plist) == 1: person.clearAltFamilyList() else: model, iter = selection.get_selected() if not iter: return row = model.get_path(iter) fam = person.getParentList()[row[0]] person.removeAltFamily(fam[0]) Utils.modified() self.load_family() def child_list_reordered(self,path,iter): print path,iter def on_child_list_row_move(self,clist,fm,to): """Validate whether or not this child can be moved within the clist. This routine is called in the middle of the clist's callbacks, so the state can be confusing. If the code is being debugged, the display at this point shows that the list has been reordered when in actuality it hasn't. All accesses to the clist data structure reference the state just prior to the move. This routine must keep/compute its own list indices as the functions list.remove(), list.insert(), list.reverse() etc. do not affect the values returned from the list.index() routine.""" family = clist.get_data("f") # Create a list based upon the current order of the clist clist_order = [] for i in range(clist.rows): clist_order = clist_order + [clist.get_row_data(i)] child = clist_order[fm] # This function deals with ascending order lists. Convert if # necessary. if (self.child_sort.sort_direction() == GTK.SORT_DESCENDING): clist_order.reverse() max_index = len(clist_order) - 1 fm = max_index - fm to = max_index - to # Create a new list to match the requested order desired_order = clist_order[:fm] + clist_order[fm+1:] desired_order = desired_order[:to] + [child] + desired_order[to:] # Check birth date order in the new list if (EditPerson.birth_dates_in_order(desired_order) == 0): clist.emit_stop_by_name("row_move") msg = _("Invalid move. Children must be ordered by birth date.") WarningDialog(msg) return # OK, this birth order works too. Update the family data structures. family.setChildList(desired_order) # Build a mapping of child item to list position. This would not # be necessary if indices worked properly i = 0 new_order = {} for tmp in desired_order: new_order[tmp] = i i = i + 1 # Convert the original list back to whatever ordering is being # used by the clist itself. if self.child_sort.sort_direction() == GTK.SORT_DESCENDING: clist_order.reverse() # Update the clist indices so any change of sorting works i = 0 for tmp in clist_order: clist.set_text(i,0,"%2d"%(new_order[tmp]+1)) i = i + 1 # Need to save the changed order Utils.modified() def drag_data_received(self,widget,context,x,y,sel_data,info,time): row = self.child_list.get_row_at(x,y) if sel_data and sel_data.data: exec 'data = %s' % sel_data.data exec 'mytype = "%s"' % data[0] exec 'person = "%s"' % data[1] if mytype != 'child': return elif person == self.person.getId(): print row # self.move_element(self.elist,self.etree.get_selected_row(),row) def drag_data_get(self,widget, context, sel_data, info, time): ev = self.child_list.get_selected_objects() bits_per = 8; # we're going to pass a string pickled = pickle.dumps(ev[0]); data = str(('child',self.person.getId(),pickled)); sel_data.set(sel_data.target, bits_per, data) def drag_begin(self, context, a): return