diff --git a/src/MergeData.py b/src/MergeData.py new file mode 100644 index 000000000..fb9405809 --- /dev/null +++ b/src/MergeData.py @@ -0,0 +1,808 @@ +# +# 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 +# + +import RelLib +import soundex +import intl +import utils +import Config +import const +_ = intl.gettext + +import string + +import gtk +import gnome.ui +import libglade + +class MergePeople: + + def __init__(self,db,person1,person2,update): + self.db = db + self.p1 = person1 + self.p2 = person2 + self.update = update + + self.glade = libglade.GladeXML(const.mergeFile,"merge") + self.top = self.glade.get_widget("merge") + self.altname = self.glade.get_widget("altname") + self.altbirth = self.glade.get_widget("altbirth") + self.altdeath = self.glade.get_widget("altdeath") + self.family_list = db.getFamilyMap().values() + + self.glade.signal_autoconnect({ + "on_merge_clicked" : self.on_merge_clicked, + "on_next_clicked" : self.on_merge_edit_clicked, + "destroy_passed_object" : utils.destroy_passed_object, + }) + + label_text = "Merge %s and %s" % (Config.nameof(person1),Config.nameof(person2)) + self.glade.get_widget("progress").set_text(label_text) + f1 = person1.getMainFamily() + f2 = person2.getMainFamily() + + name1 = Config.nameof(person1) + death1 = person1.getDeath().getDate() + dplace1 = self.place_name(person1.getDeath()) + birth1 = person1.getBirth().getDate() + bplace1 = self.place_name(person1.getBirth()) + + name2 = Config.nameof(person2) + death2 = person2.getDeath().getDate() + dplace2 = self.place_name(person2.getDeath()) + birth2 = person2.getBirth().getDate() + bplace2 = self.place_name(person2.getBirth()) + + if f2 and not f1: + self.glade.get_widget("bfather2").set_active(1) + else: + self.glade.get_widget("bfather1").set_active(1) + + if f1: + father1 = name_of(f1.getFather()) + mother1 = name_of(f1.getMother()) + else: + father1 = "" + mother1 = "" + + if f2: + father2 = name_of(f2.getFather()) + mother2 = name_of(f2.getMother()) + else: + father2 = "" + mother2 = "" + + label1 = "%s (%s)" % (_("First Person"),person1.getId()) + label2 = "%s (%s)" % (_("Second Person"),person2.getId()) + + self.glade.get_widget("PersonFrame1").set_label(label1) + self.glade.get_widget("PersonFrame2").set_label(label2) + self.glade.get_widget("name1_text").set_text(name1) + self.glade.get_widget("name1_text").set_position(0) + self.glade.get_widget("name2_text").set_text(name2) + self.glade.get_widget("name2_text").set_position(0) + + self.bname1 = self.glade.get_widget("bname1") + self.bname1.set_active(1) + + self.glade.get_widget("birth1_text").set_text(birth1) + self.glade.get_widget("birth1_text").set_position(0) + self.glade.get_widget("birth2_text").set_text(birth2) + self.glade.get_widget("birth2_text").set_position(0) + self.glade.get_widget("bplace1_text").set_text(bplace1) + self.glade.get_widget("bplace1_text").set_position(0) + self.glade.get_widget("bplace2_text").set_text(bplace2) + self.glade.get_widget("bplace2_text").set_position(0) + + if not birth1 and not bplace1 and birth2 or bplace2: + self.glade.get_widget('bbirth2').set_active(1) + else: + self.glade.get_widget('bbirth1').set_active(1) + + if not death1 and not dplace1 and death2 or dplace2: + self.glade.get_widget('death2').set_active(1) + else: + self.glade.get_widget('death1').set_active(1) + + self.glade.get_widget("death1_text").set_text(death1) + self.glade.get_widget("death1_text").set_position(0) + self.glade.get_widget("dplace1_text").set_text(dplace1) + self.glade.get_widget("dplace1_text").set_position(0) + + self.glade.get_widget("death2_text").set_text(death2) + self.glade.get_widget("death2_text").set_position(0) + self.glade.get_widget("dplace2_text").set_text(dplace2) + self.glade.get_widget("dplace2_text").set_position(0) + + self.glade.get_widget("father1").set_text(father1) + self.glade.get_widget("father1").set_position(0) + self.glade.get_widget("father2").set_text(father2) + self.glade.get_widget("father2").set_position(0) + self.glade.get_widget("mother1").set_text(mother1) + self.glade.get_widget("mother1").set_position(0) + self.glade.get_widget("mother2").set_text(mother2) + self.glade.get_widget("mother2").set_position(0) + + p1list = person1.getFamilyList() + p2list = person2.getFamilyList() + + length = min(len(p1list),3) + self.glade.get_widget("spouse1").clear() + for index in range(0,3): + if index < length and p1list[index]: + if person1.getGender() == RelLib.Person.male: + spouse = p1list[index].getMother() + else: + spouse = p1list[index].getFather() + + if spouse == None: + name = "unknown" + else: + name = "%s (%s)" % (Config.nameof(spouse),spouse.getId()) + self.glade.get_widget("spouse1").append([name]) + + length = min(len(p2list),3) + self.glade.get_widget("spouse2").clear() + for index in range(0,3): + if index < length and p2list[index]: + if person2.getGender() == RelLib.Person.male: + spouse = p2list[index].getMother() + else: + spouse = p2list[index].getFather() + + if spouse == None: + name = "unknown" + else: + name = "%s (%s)" % (Config.nameof(spouse),spouse.getId()) + self.glade.get_widget("spouse2").append([name]) + + if name1 != name2: + self.altname.set_sensitive(1) + self.altname.set_active(1) + else: + self.altname.set_sensitive(0) + self.altname.set_active(0) + + if not birth1 and not bplace1 or not birth2 and not bplace2: + self.altbirth.set_active(0) + else: + self.altbirth.set_active(1) + + if not death1 and not dplace1 or not death2 and not dplace2: + self.altdeath.set_active(0) + else: + self.altdeath.set_active(1) + + def place_name(self,event): + place = event.getPlace() + if place: + return "%s (%s)" % (place.get_title(),place.getId()) + else: + return "" + + def on_merge_edit_clicked(self,obj): + import EditPerson + self.on_merge_clicked(obj) + EditPerson.EditPerson(self.p1,self.db,self.update) + + #--------------------------------------------------------------------- + # + # + # + #--------------------------------------------------------------------- + def on_merge_clicked(self,obj): + utils.modified() + + for name in self.p2.getAlternateNames(): + self.p1.addAlternateName(name) + for event in self.p2.getEventList(): + self.p1.addEvent(event) + + if self.bname1.get_active(): + if self.altname.get_active(): + self.p1.addAlternateName(self.p2.getPrimaryName()) + else: + if self.altname.get_active(): + self.p1.addAlternateName(self.p1.getPrimaryName()) + self.p1.setPrimaryName(self.p2.getPrimaryName()) + + alt = self.glade.get_widget("altbirth").get_active() + if self.glade.get_widget("bbirth2").get_active(): + if alt: + event = self.p1.getBirth() + event.setName("Alternate Birth") + self.p1.addEvent(event) + self.p1.setBirth(self.p2.getBirth()) + else: + if alt: + event = self.p2.getBirth() + event.setName("Alternate Birth") + self.p1.addEvent(event) + + alt = self.glade.get_widget("altdeath").get_active() + if self.glade.get_widget("bbirth2").get_active(): + if alt: + event = self.p1.getDeath() + event.setName("Alternate Death") + self.p1.addEvent(event) + self.p1.setDeath(self.p2.getDeath()) + else: + if alt: + event = self.p2.getDeath() + event.setName("Alternate Death") + self.p1.addEvent(event) + + if self.glade.get_widget("bfather2").get_active(): + orig_family = self.p1.getMainFamily() + if orig_family: + orig_family.removeChild(self.p1) + + source_family = self.p2.getMainFamily() + self.p1.setMainFamily(source_family) + + if source_family: + if self.p2 in source_family.getChildList(): + source_family.removeChild(self.p2) + if self.p1 not in source_family.getChildList(): + source_family.addChild(self.p1) + else: + source_family = self.p2.getMainFamily() + if source_family: + source_family.removeChild(self.p2) + self.p2.setMainFamily(None) + + self.merge_families() + + for photo in self.p2.getPhotoList(): + self.p1.addPhoto(photo) + + if self.p1.getNickName() == "": + self.p1.setNickName(self.p2.getNickName()) + if self.p2.getNote() != "": + old_note = self.p1.getNote() + if old_note: + old_note = old_note + "\n\n" + self.p1.setNote(old_note + self.p2.getNote()) + + del self.db.getPersonMap()[self.p2.getId()] + self.update(self.p2) + utils.destroy_passed_object(self.top) + + #--------------------------------------------------------------------- + # + # + # + #--------------------------------------------------------------------- + def find_family(self,family): + if self.p1.getGender() == RelLib.Person.male: + mother = family.getMother() + father = self.p1 + else: + father = family.getFather() + mother = self.p1 + + for myfamily in self.family_list: + if myfamily.getFather() == father and \ + myfamily.getMother() == mother: + return myfamily + + return None + + #--------------------------------------------------------------------- + # + # + # + #--------------------------------------------------------------------- + def merge_families(self): + + family_num = 0 + mylist = self.p2.getFamilyList()[:] + for src_family in mylist: + + family_num = family_num + 1 + + if not self.db.getFamilyMap().has_key(src_family.getId()): + continue + if src_family in self.p1.getFamilyList(): + continue + + tgt_family = self.find_family(src_family) + + + # + # This is the case where a new family to be added to the + # p1 as a result of the merge already exists as a + # family. In this case, we need to remove the old source + # family (with the pre-merge identity of the p1) from + # both the parents + # + if tgt_family in self.p1.getFamilyList(): + if tgt_family.getFather() != None and \ + src_family in tgt_family.getFather().getFamilyList(): + tgt_family.getFather().removeFamily(src_family) + if tgt_family.getMother() != None and \ + src_family in tgt_family.getMother().getFamilyList(): + tgt_family.getMother().removeFamily(src_family) + + # copy children from source to target + + # delete the old source family + del self.db.getFamilyMap()[src_family.getId()] + + continue + + # + # This is the case where a new family to be added + # and it is not already in the list. + # + + if tgt_family: + + # tgt_family a duplicate family, transfer children from + # the p2 family, and delete the family. Not sure + # what to do about marriage/divorce date/place yet. + + # transfer child to new family, alter children to + # point to the correct family + + for child in src_family.getChildList(): + if child not in tgt_family.getChildList(): + tgt_family.addChild(child) + if child.getMainFamily() == src_family: + child.setMainFamily(tgt_family) + else: + index = 0 + for fam in child.getAltFamilies(): + if fam == src_family: + child.getAltFamilies()[index] = tgt_family + index = index + 1 + + # add family events from the old to the new + for event in src_family.getEventList(): + tgt_family.addEvent(event) + + # change parents of the family to point to the new + # family + + if src_family.getFather(): + src_family.getFather().removeFamily(src_family) + src_family.getFather().addFamily(tgt_family) + + if src_family.getMother(): + src_family.getMother().removeFamily(src_family) + src_family.getMother().addFamily(tgt_family) + + del self.db.getFamilyMap()[src_family.getId()] + else: + self.remove_marriage(src_family,self.p2) + if src_family not in self.p1.getFamilyList(): + self.p1.addFamily(src_family) + if self.p1.getGender() == RelLib.Person.male: + src_family.setFather(self.p1) + else: + src_family.setMother(self.p1) + + # a little debugging here + + for fam in self.db.getFamilyMap().values(): + name = self.p2.getPrimaryName().getName() + if self.p2 in fam.getChildList(): + fam.removeChild(self.p2) + fam.addChild(self.p1) + if self.p2 == fam.getFather(): + fam.setFather(self.p1) + if self.p2 == fam.getMother(): + fam.setMother(self.p1) + + #--------------------------------------------------------------------- + # + # + # + #--------------------------------------------------------------------- + def remove_marriage(self,family,person): + if not person: + return + index = 0 + for fam in person.getFamilyList(): + if fam == family: + del person.getFamilyList()[index] + return + index = index + 1 + + +def compare_people(p1,p2): + + name1 = p1.getPrimaryName() + name2 = p2.getPrimaryName() + + chance = name_match(name1,name2) + if chance == -1 : + return -1 + + birth1 = p1.getBirth() + death1 = p1.getDeath() + birth2 = p2.getBirth() + death2 = p2.getDeath() + + value = date_match(birth1.getDateObj(),birth2.getDateObj()) + if value == -1 : + return -1 + chance = chance + value + + value = date_match(death1.getDateObj(),death2.getDateObj()) + if value == -1 : + return -1 + chance = chance + value + + value = place_match(birth1.getPlace(),birth2.getPlace()) + if value == -1 : + return -1 + chance = chance + value + + value = place_match(death1.getPlace(),death2.getPlace()) + if value == -1 : + return -1 + chance = chance + value + + ancestors = [] + ancestors_of(p1,ancestors) + if p2 in ancestors: + return -1 + + ancestors = [] + ancestors_of(p2,ancestors) + if p1 in ancestors: + return -1 + + f1 = p1.getMainFamily() + f2 = p2.getMainFamily() + + if f1 and f1.getFather(): + dad1 = f1.getFather().getPrimaryName() + else: + dad1 = None + + if f2 and f2.getFather(): + dad2 = f2.getFather().getPrimaryName() + else: + dad2 = None + + value = name_match(dad1,dad2) + + if value == -1: + return -1 + + chance = chance + value + + if f1 and f1.getMother(): + mom1 = f1.getMother().getPrimaryName() + else: + mom1 = None + + if f2 and f2.getMother(): + mom2 = f2.getMother().getPrimaryName() + else: + mom2 = None + + value = name_match(mom1,mom2) + if value == -1: + return -1 + + chance = chance + value + + for f1 in p1.getFamilyList(): + for f2 in p2.getFamilyList(): + if p1.getGender() == RelLib.Person.female: + father1 = f1.getFather() + father2 = f2.getFather() + if father1 and father2: + if father1 == father2: + chance = chance + 1 + else: + fname1 = Config.nameof(father1) + fname2 = Config.nameof(father2) + value = name_match(fname1,fname2) + if value != -1: + chance = chance + value + else: + mother1 = f1.getMother() + mother2 = f2.getMother() + if mother1 and mother2: + if mother1 == mother2: + chance = chance + 1 + else: + mname1 = Config.nameof(mother1) + mname2 = Config.nameof(mother2) + value = name_match(mname1,mname2) + if value != -1: + chance = chance + value + + return chance + + +#----------------------------------------------------------------- +# +# +# +#----------------------------------------------------------------- +def name_compare(s1,s2): + return s1 == s2 + +#----------------------------------------------------------------- +# +# +# +#----------------------------------------------------------------- +def date_match(date1,date2): + if date1.getDate() == "" or date2.getDate() == "": + return 0 + if date1.getDate() == date2.getDate(): + return 1 + + if date1.isRange() or date2.isRange(): + return range_compare(date1,date2) + + date1 = date1.get_start_date() + date2 = date2.get_start_date() + + if date1.getYear() == date2.getYear(): + if date1.getMonth() == date2.getMonth(): + return 0.75 + if date1.getMonth() == -1 or date2.getMonth() == -1: + return 0.75 + else: + return -1 + else: + return -1 + +#----------------------------------------------------------------- +# +# +# +#----------------------------------------------------------------- +def range_compare(date1,date2): + if date1.isRange() and date2.isRange(): + if date1.get_start_date() >= date2.get_start_date() and \ + date1.get_start_date() <= date2.get_stop_date() or \ + date2.get_start_date() >= date1.get_start_date() and \ + date2.get_start_date() <= date1.get_stop_date() or \ + date1.get_stop_date() >= date2.get_start_date() and \ + date1.get_stop_date() <= date2.get_stop_date() or \ + date2.get_stop_date() >= date1.get_start_date() and \ + date2.get_stop_date() <= date1.get_stop_date(): + return 0.5 + else: + return -1 + elif date2.isRange(): + if date1.get_start_date() >= date2.get_start_date() and \ + date1.get_start_date() <= date2.get_stop_date(): + return 0.5 + else: + return -1 + else: + if date2.get_start_date() >= date1.get_start_date() and \ + date2.get_start_date() <= date1.get_stop_date(): + return 0.5 + else: + return -1 + +def name_match(name,name1): + + if not name1 or not name: + return 0 + + srn1 = name.getSurname() + sfx1 = name.getSuffix() + srn2 = name1.getSurname() + sfx2 = name1.getSuffix() + + if not name_compare(srn1,srn2): + return -1 + if sfx1 != sfx2: + if sfx1 != "" and sfx2 != "": + return -1 + + if name.getFirstName() == name1.getFirstName(): + return 1 + else: + list1 = string.split(name.getFirstName()) + list2 = string.split(name1.getFirstName()) + + if len(list1) < len(list2): + return list_reduce(list1,list2) + else: + return list_reduce(list2,list1) + +#--------------------------------------------------------------------- +# +# +# +#--------------------------------------------------------------------- +def list_reduce(list1,list2): + value = 0 + for name in list1: + for name2 in list2: + if is_initial(name) and name[0] == name2[0]: + value = value + 0.25 + break + if is_initial(name2) and name2[0] == name[0]: + value = value + 0.25 + break + if name == name2: + value = value + 0.5 + break + if name[0] == name2[0] and name_compare(name,name2): + value = value + 0.25 + break + if value == 0: + return -1 + else: + return min(value,1) + +#--------------------------------------------------------------------- +# +# +# +#--------------------------------------------------------------------- +def place_match(p1,p2): + if p1 == p2: + return 1 + + if p1 == None: + name1 = "" + else: + name1 = p1.get_title() + + if p2 == None: + name2 = "" + else: + name2 = p2.get_title() + + if name1 == "" or name2 == "": + return 0 + if name1 == name2: + return 1 + + list1 = string.split(string.replace(name1,","," ")) + list2 = string.split(string.replace(name2,","," ")) + + value = 0 + for name in list1: + for name2 in list2: + if name == name2: + value = value + 0.5 + break + if name[0] == name2[0] and name_compare(name,name2): + value = value + 0.25 + break + if value == 0: + return -1 + else: + return min(value,1) + +#------------------------------------------------------------------------- +# +# +# +#------------------------------------------------------------------------- +def is_initial(name): + if len(name) > 2: + return 0 + elif len(name) == 2: + if name[0] in string.uppercase and name[1] == '.': + return 1 + else: + return name[0] in string.uppercase + +def ancestors_of(p1,list): + if p1 == None: + return + list.append(p1) + f1 = p1.getMainFamily() + if f1 != None: + ancestors_of(f1.getFather(),list) + ancestors_of(f1.getMother(),list) + +def name_of(p): + if not p: + return "" + return "%s (%s)" % ( Config.nameof(p),p.getId()) + +#------------------------------------------------------------------------- +# +# +# +#------------------------------------------------------------------------- +class MergePlaces: + def __init__(self,database,place1,place2,update): + self.db = database + self.p1 = place1 + self.p2 = place2 + self.update = update + + self.glade = libglade.GladeXML(const.mergeFile,"merge_places") + self.top = self.glade.get_widget("merge_places") + self.glade.get_widget("title1_text").set_text(place1.get_title()) + self.glade.get_widget("title2_text").set_text(place2.get_title()) + self.t3 = self.glade.get_widget("title3_text") + self.t3.set_text(place1.get_title()) + + self.glade.signal_autoconnect({ + "destroy_passed_object" : utils.destroy_passed_object, + "on_merge_places_clicked" : self.on_merge_places_clicked, + }) + self.top.show() + + def on_merge_places_clicked(self,obj): + if self.glade.get_widget("title2").get_active(): + self.p1.set_title(self.p2.get_title()) + elif self.glade.get_widget("title3").get_active(): + self.p1.set_title(self.t3.get_text()) + if self.p1.get_longitude() == "" and self.p2.get_longitude() != "": + self.p1.set_longitude(self.p2.get_longitude()) + if self.p1.get_latitude() == "" and self.p2.get_latitude() != "": + self.p1.set_latitude(self.p2.get_latitude()) + for url in self.p2.getUrlList(): + self.p1.addUrl(url) + for photo in self.p2.getPhotoList(): + self.p1.addPhoto(photo) + for source in self.p2.getSourceRefList(): + self.p1.addSource(source) + note = self.p2.getNote() + if note != "": + if self.p1.getNote() == "": + self.p1.setNote(note) + elif self.p1.getNote() != note: + self.p1.setNote("%s\n\n%s" % (self.p1.getNote(),note)) + for l in [self.p2.get_main_location()] + self.p2.get_alternate_locations(): + if not l.is_empty(): + self.p1.add_alternate_locations(l) + for p in self.db.getPersonMap().values(): + for event in [p.getBirth(), p.getDeath()] + p.getEventList(): + if event.getPlace() == self.p2: + event.setPlace(self.p1) + for f in self.db.getFamilyMap().values(): + for event in f.getEventList(): + if event.getPlace() == self.p2: + event.setPlace(self.p1) + del self.db.getPlaceMap()[self.p2.getId()] + self.update() + utils.modified() + utils.destroy_passed_object(obj) + +#------------------------------------------------------------------------- +# +# +# +#------------------------------------------------------------------------- +def new_after_edit(epo): + pass + +if __name__ == "__main__": + import sys + import ReadXML + + database = RelLib.RelDataBase() + ReadXML.loadData(database,sys.argv[1]) + + person1 = database.getPersonMap().values()[0] + person2 = database.getPersonMap().values()[1] + MergePeople(database,person1,person2) + gtk.mainloop() diff --git a/src/OpenDrawDoc.py b/src/OpenDrawDoc.py index be1a65863..a9a6fb6e0 100644 --- a/src/OpenDrawDoc.py +++ b/src/OpenDrawDoc.py @@ -350,7 +350,7 @@ class OpenDrawDoc(DrawDoc): self.f.write('') self.f.write('') self.f.write('') diff --git a/src/RelLib.py b/src/RelLib.py index a07c693d9..6f3924e5f 100644 --- a/src/RelLib.py +++ b/src/RelLib.py @@ -293,6 +293,9 @@ class Location: self.state = "" self.country = "" + def is_empty(self): + return self.city == "" and self.county == "" and self.state == "" and self.country == "" + def set_city(self,data): """sets the city name of the Location object""" self.city = data diff --git a/src/SelectChild.py b/src/SelectChild.py index 9c46c09ad..5c1e89d4a 100644 --- a/src/SelectChild.py +++ b/src/SelectChild.py @@ -88,8 +88,10 @@ class SelectChild: mtitle = _("Relationship to %s") % mname self.xml.get_widget("mlabel").set_text(mtitle) - self.xml.get_widget("mrel").set_text(_("Birth")) - self.xml.get_widget("frel").set_text(_("Birth")) + self.mrel = self.xml.get_widget("mrel") + self.frel = self.xml.get_widget("frel") + self.mrel.set_text(_("Birth")) + self.frel.set_text(_("Birth")) self.redraw_child_list(2) self.top.show() @@ -171,7 +173,6 @@ class SelectChild: def on_save_child_clicked(self,obj): for row in self.add_child.selection: - select_child = self.add_child.get_row_data(row) if self.family == None: self.family = database.newFamily() @@ -183,13 +184,13 @@ class SelectChild: self.family.addChild(select_child) - mrel = const.childRelations[self.xml.get_widget("mrel").get_text()] + mrel = const.childRelations[self.mrel.get_text()] mother = self.family.getMother() if mother and mother.getGender() != Person.female: if mrel == "Birth": mrel = "Unknown" - frel = const.childRelations[self.xml.get_widget("frel").get_text()] + frel = const.childRelations[self.frel.get_text()] father = self.family.getFather() if father and father.getGender() != Person.male: if frel == "Birth": diff --git a/src/const.py b/src/const.py index 4b18177fd..c24e879a7 100644 --- a/src/const.py +++ b/src/const.py @@ -64,6 +64,7 @@ editnoteFile = rootDir + os.sep + "editnote.glade" configFile = rootDir + os.sep + "config.glade" stylesFile = rootDir + os.sep + "styles.glade" dialogFile = rootDir + os.sep + "dialog.glade" +mergeFile = rootDir + os.sep + "mergedata.glade" pluginsDir = rootDir + os.sep + "plugins" filtersDir = rootDir + os.sep + "filters" dataDir = rootDir + os.sep + "data" diff --git a/src/filters/EventPlace.py b/src/filters/EventPlace.py index 675735745..1bde40477 100644 --- a/src/filters/EventPlace.py +++ b/src/filters/EventPlace.py @@ -44,7 +44,7 @@ class EventPlace(Filter.Filter): list.append(person.getBirth()) list.append(person.getDeath()) for event in list: - if self.regexp.search(event.getPlace().get_title()): + if self.regexp.search(event.getPlaceName()): val = 1 break return val diff --git a/src/gramps.glade b/src/gramps.glade index 64f53ef8b..602b0161e 100644 --- a/src/gramps.glade +++ b/src/gramps.glade @@ -193,6 +193,19 @@ GNOMEUIINFO_MENU_FIND_ITEM + + + GtkPixmapMenuItem + merge + + activate + on_merge_activate + Tue, 23 Oct 2001 13:34:57 GMT + + + False + GNOME_STOCK_MENU_CONVERT + @@ -820,7 +833,7 @@ 8 250,60,70,150,100,5,5,5 - GTK_SELECTION_SINGLE + GTK_SELECTION_EXTENDED True GTK_SHADOW_IN @@ -2379,7 +2392,7 @@ 6 236,47,102,88,77,80 - GTK_SELECTION_SINGLE + GTK_SELECTION_EXTENDED True GTK_SHADOW_IN diff --git a/src/gramps_main.py b/src/gramps_main.py index 30701ae15..f5e33ba99 100755 --- a/src/gramps_main.py +++ b/src/gramps_main.py @@ -112,6 +112,7 @@ nameArrow = None deathArrow = None dateArrow = None canvas = None +merge_button = None sort_column = 5 sort_direct = SORT_ASCENDING DataFilter = Filter.Filter("") @@ -146,6 +147,34 @@ def find_goto_to(person): goto_active_person() update_display(0) +#------------------------------------------------------------------------- +# +# Merge +# +#------------------------------------------------------------------------- +def on_merge_activate(obj): + """Calls up the merge dialog for the selection""" + + page = notebook.get_current_page() + if page == 0: + if len(person_list.selection) != 2: + msg = _("Exactly two people must be selected to perform a merge") + GnomeErrorDialog() + else: + import MergeData + p1 = person_list.get_row_data(person_list.selection[0]) + p2 = person_list.get_row_data(person_list.selection[1]) + MergeData.MergePeople(database,p1[0],p2[0],remove_from_person_list) + elif page == 4: + if len(place_list.selection) != 2: + msg = _("Exactly two places must be selected to perform a merge") + GnomeErrorDialog(msg) + else: + import MergeData + p1 = place_list.get_row_data(place_list.selection[0]) + p2 = place_list.get_row_data(place_list.selection[1]) + MergeData.MergePlaces(database,p1,p2,load_places) + #------------------------------------------------------------------------- # # Exiting @@ -499,6 +528,10 @@ def on_add_place_clicked(obj): def on_delete_place_clicked(obj): if len(obj.selection) == 0: return + elif len(obj.selection) > 1: + msg = _("Currently, you can only delete on place at a time") + topWindow.error(msg) + return else: index = obj.selection[0] @@ -589,7 +622,7 @@ def is_source_used(source): #------------------------------------------------------------------------- # -# +# Edit callbacks # #------------------------------------------------------------------------- def on_edit_source_clicked(obj): @@ -598,40 +631,24 @@ def on_edit_source_clicked(obj): source = obj.get_row_data(index) EditSource.EditSource(source,database,update_display_after_edit) -#------------------------------------------------------------------------- -# -# -# -#------------------------------------------------------------------------- def on_edit_place_clicked(obj): - if len(obj.selection) > 0: - index = obj.selection[0] - place = obj.get_row_data(index) - EditPlace.EditPlace(place,database,update_display_after_edit) + """Display the selected places in the EditPlace display""" + if len(obj.selection) > 5: + msg = _("You requested too many places to edit at the same time") + GnomeErrorDialog(msg) + else: + for p in obj.selection: + place = obj.get_row_data(p) + EditPlace.EditPlace(place,database,update_display_after_edit) -#------------------------------------------------------------------------- -# -# -# -#------------------------------------------------------------------------- def new_source_after_edit(source): database.addSource(source) update_display(0) -#------------------------------------------------------------------------- -# -# -# -#------------------------------------------------------------------------- def new_place_after_edit(place): database.addPlace(place) update_display(0) -#------------------------------------------------------------------------- -# -# -# -#------------------------------------------------------------------------- def update_display_after_edit(place): update_display(0) @@ -769,8 +786,14 @@ def save_file(filename,comment): # #------------------------------------------------------------------------- def load_active_person(obj): - """Display the active person in the EditPerson display""" - load_person(active_person) + """Display the selected people in the EditPerson display""" + if len(person_list.selection) > 5: + msg = _("You requested too many people to edit at the same time") + GnomeErrorDialog(msg) + else: + for p in person_list.selection: + person = person_list.get_row_data(p) + load_person(person[0]) def on_edit_spouse_clicked(obj): """Display the active spouse in the EditPerson display""" @@ -800,9 +823,11 @@ def load_new_person(obj): # #------------------------------------------------------------------------- def on_delete_person_clicked(obj): - if active_person: + if len(person_list.selection) == 1: msg = _("Do you really wish to delete %s?") % Config.nameof(active_person) topWindow.question( msg, delete_person_response) + elif len(person_list.selection) > 1: + topWindow.error(_("Currently, you can only delete one person at a time")) def delete_person_response(val): if val == 1: @@ -879,10 +904,10 @@ def on_delete_parents_clicked(obj): # # #------------------------------------------------------------------------- -def on_person_list_select_row(obj,a,b,c): - person,alt = obj.get_row_data(a) - obj.set_data("a",person) - change_active_person(person) +def on_person_list_select_row(obj,row,b,c): + if row == obj.selection[0]: + person,alt = obj.get_row_data(row) + change_active_person(person) #------------------------------------------------------------------------- # @@ -1260,26 +1285,32 @@ def display_comment_box(filename): def on_person_list1_activate(obj): """Switches to the person list view""" notebook.set_page(0) + merge_button.set_sensitive(1) def on_family1_activate(obj): """Switches to the family view""" notebook.set_page(1) + merge_button.set_sensitive(0) def on_pedegree1_activate(obj): """Switches to the pedigree view""" notebook.set_page(2) + merge_button.set_sensitive(0) def on_sources_activate(obj): """Switches to the sources view""" notebook.set_page(3) + merge_button.set_sensitive(0) def on_places_activate(obj): """Switches to the places view""" notebook.set_page(4) + merge_button.set_sensitive(1) def on_media_activate(obj): """Switches to the media view""" notebook.set_page(5) + merge_button.set_sensitive(0) #------------------------------------------------------------------------- # @@ -2531,7 +2562,7 @@ def main(arg): global database, gtop global statusbar,notebook global person_list, source_list, place_list, canvas, media_list - global topWindow, preview + global topWindow, preview, merge_button global nameArrow, dateArrow, deathArrow global cNameArrow, cDateArrow global mid, mtype, mdesc, mpath, mdetails @@ -2568,7 +2599,8 @@ def main(arg): nameArrow = gtop.get_widget("nameSort") dateArrow = gtop.get_widget("dateSort") deathArrow = gtop.get_widget("deathSort") - + merge_button= gtop.get_widget("merge") + t = [ ('STRING', 0, 0), ('text/plain',0,0), ('text/uri-list',0,2), @@ -2652,6 +2684,7 @@ def main(arg): "on_media_list_select_row" : on_media_list_select_row, "on_media_list_drag_data_get" : on_media_list_drag_data_get, "on_media_list_drag_data_received" : on_media_list_drag_data_received, + "on_merge_activate" : on_merge_activate, "on_places_activate" : on_places_activate, "on_preferences_activate" : on_preferences_activate, "on_remove_child_clicked" : on_remove_child_clicked, diff --git a/src/mergedata.glade b/src/mergedata.glade new file mode 100644 index 000000000..4855fec72 --- /dev/null +++ b/src/mergedata.glade @@ -0,0 +1,1768 @@ + + + + + merge + merge + + src + pixmaps + C + True + True + + + + GtkDialog + merge + Gramps - Merge People + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + True + True + False + + + GtkVBox + Dialog:vbox + dialog-vbox1 + False + 0 + + + GtkHBox + Dialog:action_area + dialog-action_area1 + 10 + True + 5 + + 0 + False + True + GTK_PACK_END + + + + GtkHButtonBox + hbuttonbox1 + GTK_BUTTONBOX_END + 30 + 85 + 27 + 7 + 0 + + 0 + True + True + + + + GtkButton + merge_btn + True + True + + clicked + on_merge_clicked + merge + Wed, 21 Feb 2001 00:49:36 GMT + + + GTK_RELIEF_NORMAL + + + + GtkButton + next_btn + True + True + + clicked + on_next_clicked + merge + Tue, 20 Feb 2001 23:29:49 GMT + + + GTK_RELIEF_NORMAL + + + + GtkButton + button4 + True + True + + clicked + destroy_passed_object + merge + Tue, 20 Feb 2001 23:27:39 GMT + + GNOME_STOCK_BUTTON_CANCEL + GTK_RELIEF_NORMAL + + + + + + GtkVBox + vbox1 + 600 + False + 0 + + 0 + True + True + + + + GtkLabel + progress + + GTK_JUSTIFY_CENTER + False + 0.5 + 0.5 + 0 + 0 + + 10 + False + False + + + + + GtkHBox + hbox1 + True + 0 + + 0 + True + True + + + + GtkFrame + PersonFrame1 + 300 + + 0 + GTK_SHADOW_ETCHED_IN + + 2 + True + True + + + + GtkTable + table3 + 12 + 3 + False + 0 + 2 + + + GtkRadioButton + bname1 + True + + False + True + name + + 2 + 3 + 0 + 1 + 0 + 0 + False + False + True + False + False + False + + + + + GtkLabel + label8 + + GTK_JUSTIFY_CENTER + False + 1 + 0.5 + 5 + 0 + + 0 + 1 + 2 + 3 + 0 + 0 + False + False + False + False + True + False + + + + + GtkLabel + label12 + + GTK_JUSTIFY_CENTER + False + 1 + 0.5 + 5 + 0 + + 0 + 1 + 5 + 6 + 0 + 0 + False + False + False + False + True + False + + + + + GtkLabel + label22 + + GTK_JUSTIFY_CENTER + False + 1 + 0.5 + 5 + 0 + + 0 + 1 + 9 + 10 + 0 + 0 + False + False + False + False + True + False + + + + + GtkLabel + label4 + + GTK_JUSTIFY_CENTER + False + 1 + 0.5 + 5 + 0 + + 0 + 1 + 0 + 1 + 0 + 0 + False + False + False + False + True + False + + + + + GtkLabel + label26 + + GTK_JUSTIFY_CENTER + False + 1 + 0.5 + 5 + 0 + + 0 + 1 + 3 + 4 + 0 + 0 + False + False + False + False + True + False + + + + + GtkLabel + label30 + + GTK_JUSTIFY_CENTER + False + 1 + 0.5 + 5 + 0 + + 0 + 1 + 6 + 7 + 0 + 0 + False + False + False + False + True + False + + + + + GtkRadioButton + bfather1 + True + + False + True + father + + 2 + 3 + 8 + 9 + 0 + 0 + False + False + True + False + False + False + + + + + GtkRadioButton + death1 + True + + True + True + death + + 2 + 3 + 5 + 6 + 0 + 0 + False + False + True + True + False + False + + + + + GtkRadioButton + bbirth1 + True + + False + True + birth + + 2 + 3 + 2 + 3 + 0 + 0 + False + False + True + True + False + False + + + + + GtkEntry + name1_text + True + False + True + 0 + + + 1 + 2 + 0 + 1 + 0 + 0 + True + False + False + False + True + False + + + + + GtkEntry + birth1_text + True + False + True + 0 + + + 1 + 2 + 2 + 3 + 0 + 0 + True + False + False + False + True + False + + + + + GtkEntry + bplace1_text + True + False + True + 0 + + + 1 + 2 + 3 + 4 + 0 + 0 + True + False + False + False + True + False + + + + + GtkEntry + death1_text + True + False + True + 0 + + + 1 + 2 + 5 + 6 + 0 + 0 + True + False + False + False + True + False + + + + + GtkEntry + dplace1_text + True + False + True + 0 + + + 1 + 2 + 6 + 7 + 0 + 0 + True + False + False + False + True + False + + + + + GtkEntry + father1 + True + False + True + 0 + + + 1 + 2 + 8 + 9 + 0 + 0 + True + False + False + False + True + False + + + + + GtkEntry + mother1 + True + False + True + 0 + + + 1 + 2 + 9 + 10 + 0 + 0 + True + False + False + False + True + False + + + + + GtkLabel + label16 + + GTK_JUSTIFY_CENTER + False + 1 + 0.5 + 5 + 0 + + 0 + 1 + 8 + 9 + 0 + 0 + False + False + False + False + True + False + + + + + GtkHSeparator + hseparator2 + + 0 + 3 + 7 + 8 + 0 + 5 + False + False + False + False + True + True + + + + + GtkHSeparator + hseparator4 + + 0 + 3 + 10 + 11 + 0 + 5 + False + False + False + False + True + True + + + + + GtkScrolledWindow + scrolledwindow1 + GTK_POLICY_NEVER + GTK_POLICY_AUTOMATIC + GTK_UPDATE_CONTINUOUS + GTK_UPDATE_CONTINUOUS + + 1 + 3 + 11 + 12 + 0 + 0 + False + False + False + False + True + True + + + + GtkCList + spouse1 + True + 1 + 80 + GTK_SELECTION_SINGLE + False + GTK_SHADOW_IN + + + GtkLabel + CList:title + label51 + + GTK_JUSTIFY_CENTER + False + 0.5 + 0.5 + 0 + 0 + + + + + + GtkLabel + label33 + + GTK_JUSTIFY_CENTER + False + 1 + 0 + 5 + 0 + + 0 + 1 + 11 + 12 + 0 + 0 + False + True + False + False + True + True + + + + + GtkHSeparator + hseparator7 + + 0 + 3 + 4 + 5 + 0 + 0 + False + True + False + False + True + True + + + + + GtkHSeparator + hseparator8 + + 0 + 3 + 1 + 2 + 0 + 0 + False + True + False + False + True + True + + + + + + + GtkFrame + PersonFrame2 + 300 + + 0 + GTK_SHADOW_ETCHED_IN + + 2 + True + True + + + + GtkTable + table4 + 12 + 3 + False + 0 + 2 + + + GtkRadioButton + bname2 + True + + False + True + name + + 2 + 3 + 0 + 1 + 0 + 0 + False + False + True + False + False + False + + + + + GtkRadioButton + death2 + True + + False + True + death + + 2 + 3 + 5 + 6 + 0 + 0 + False + False + True + False + False + False + + + + + GtkLabel + label13 + + GTK_JUSTIFY_CENTER + False + 1 + 0.5 + 5 + 0 + + 0 + 1 + 5 + 6 + 0 + 0 + False + False + False + False + True + False + + + + + GtkLabel + label25 + + GTK_JUSTIFY_CENTER + False + 1 + 0.5 + 5 + 0 + + 0 + 1 + 9 + 10 + 0 + 0 + False + False + False + False + True + False + + + + + GtkLabel + label5 + + GTK_JUSTIFY_CENTER + False + 1 + 0.5 + 5 + 0 + + 0 + 1 + 0 + 1 + 0 + 0 + False + False + False + False + True + False + + + + + GtkLabel + label27 + + GTK_JUSTIFY_CENTER + False + 1 + 0.5 + 5 + 0 + + 0 + 1 + 3 + 4 + 0 + 0 + False + False + False + False + True + False + + + + + GtkLabel + label9 + + GTK_JUSTIFY_CENTER + False + 1 + 0.5 + 5 + 0 + + 0 + 1 + 2 + 3 + 0 + 0 + False + False + False + False + True + False + + + + + GtkRadioButton + bbirth2 + True + + False + True + birth + + 2 + 3 + 2 + 3 + 0 + 0 + False + False + True + False + False + False + + + + + GtkLabel + label32 + + GTK_JUSTIFY_CENTER + False + 1 + 0.5 + 5 + 0 + + 0 + 1 + 6 + 7 + 0 + 0 + False + False + False + False + True + False + + + + + GtkRadioButton + bfather2 + True + + False + True + father + + 2 + 3 + 8 + 9 + 0 + 0 + False + False + True + False + False + False + + + + + GtkEntry + name2_text + True + False + True + 0 + + + 1 + 2 + 0 + 1 + 0 + 0 + True + False + False + False + True + False + + + + + GtkEntry + birth2_text + True + False + True + 0 + + + 1 + 2 + 2 + 3 + 0 + 0 + True + False + False + False + True + False + + + + + GtkEntry + bplace2_text + True + False + True + 0 + + + 1 + 2 + 3 + 4 + 0 + 0 + True + False + False + False + True + False + + + + + GtkEntry + death2_text + True + False + True + 0 + + + 1 + 2 + 5 + 6 + 0 + 0 + True + False + False + False + True + False + + + + + GtkEntry + dplace2_text + True + False + True + 0 + + + 1 + 2 + 6 + 7 + 0 + 0 + True + False + False + False + True + False + + + + + GtkEntry + father2 + True + False + True + 0 + + + 1 + 2 + 8 + 9 + 0 + 0 + True + False + False + False + True + False + + + + + GtkEntry + mother2 + True + False + True + 0 + + + 1 + 2 + 9 + 10 + 0 + 0 + True + False + False + False + True + False + + + + + GtkLabel + label34 + + GTK_JUSTIFY_CENTER + False + 1 + 0 + 5 + 0 + + 0 + 1 + 11 + 12 + 0 + 0 + False + True + False + False + True + True + + + + + GtkLabel + label19 + + GTK_JUSTIFY_CENTER + False + 1 + 0.5 + 5 + 0 + + 0 + 1 + 8 + 9 + 0 + 0 + False + False + False + False + True + False + + + + + GtkHSeparator + hseparator3 + + 0 + 3 + 7 + 8 + 0 + 5 + False + False + False + False + True + True + + + + + GtkHSeparator + hseparator5 + + 0 + 3 + 10 + 11 + 0 + 5 + False + False + False + False + True + True + + + + + GtkScrolledWindow + scrolledwindow2 + GTK_POLICY_NEVER + GTK_POLICY_AUTOMATIC + GTK_UPDATE_CONTINUOUS + GTK_UPDATE_CONTINUOUS + + 1 + 3 + 11 + 12 + 0 + 0 + False + False + False + False + True + True + + + + GtkCList + spouse2 + True + 1 + 80 + GTK_SELECTION_SINGLE + False + GTK_SHADOW_IN + + + GtkLabel + CList:title + label50 + + GTK_JUSTIFY_CENTER + False + 0.5 + 0.5 + 0 + 0 + + + + + + GtkHSeparator + hseparator9 + + 0 + 3 + 1 + 2 + 0 + 0 + False + True + False + False + True + True + + + + + GtkHSeparator + hseparator10 + + 0 + 3 + 4 + 5 + 0 + 0 + False + True + False + False + True + True + + + + + + + + GtkHBox + hbox7 + False + 0 + + 3 + False + True + + + + GtkLabel + label53 + + GTK_JUSTIFY_CENTER + False + 0.5 + 0.5 + 0 + 0 + + 0 + True + True + + + + + GtkVBox + vbox3 + False + 0 + + 0 + False + False + + + + GtkCheckButton + altname + True + + False + True + + 0 + False + False + + + + + GtkCheckButton + altbirth + True + + False + True + + 0 + False + False + + + + + GtkCheckButton + altdeath + True + + False + True + + 0 + False + False + + + + + + GtkLabel + label54 + + GTK_JUSTIFY_CENTER + False + 0.5 + 0.5 + 0 + 0 + + 0 + True + True + + + + + + + + + GnomeDialog + merge_places + 400 + Gramps - Merge Places + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + True + False + True + False + False + False + + + GtkVBox + GnomeDialog:vbox + dialog-vbox2 + False + 8 + + 4 + True + True + + + + GtkHButtonBox + GnomeDialog:action_area + dialog-action_area2 + 400 + GTK_BUTTONBOX_END + 8 + 85 + 27 + 7 + 0 + + 0 + False + True + GTK_PACK_END + + + + GtkButton + button7 + True + True + + clicked + on_merge_places_clicked + merge_places + Tue, 23 Oct 2001 23:26:52 GMT + + GNOME_STOCK_BUTTON_OK + + + + GtkButton + button9 + True + True + + clicked + destroy_passed_object + merge_places + Tue, 23 Oct 2001 23:00:28 GMT + + GNOME_STOCK_BUTTON_CANCEL + + + + + GtkVBox + vbox2 + False + 0 + + 0 + True + True + + + + GtkLabel + label56 + + GTK_JUSTIFY_CENTER + False + 0.5 + 0.5 + 0 + 0 + + 0 + False + False + + + + + GtkHSeparator + hseparator6 + + 10 + False + True + + + + + GtkTable + table5 + 3 + 2 + False + 0 + 0 + + 0 + True + True + + + + GtkEntry + title3_text + True + True + True + 0 + + + 1 + 2 + 2 + 3 + 0 + 0 + True + False + False + False + True + False + + + + + GtkLabel + title1_text + + GTK_JUSTIFY_CENTER + False + 0 + 0.5 + 0 + 0 + + 1 + 2 + 0 + 1 + 0 + 0 + False + False + False + False + True + False + + + + + GtkLabel + title2_text + + GTK_JUSTIFY_CENTER + False + 0 + 0.5 + 0 + 0 + + 1 + 2 + 1 + 2 + 0 + 0 + False + False + False + False + True + False + + + + + GtkRadioButton + title1 + True + + True + True + title + + 0 + 1 + 0 + 1 + 5 + 5 + False + False + False + False + True + False + + + + + GtkRadioButton + title2 + True + + False + True + title + + 0 + 1 + 1 + 2 + 5 + 5 + False + False + False + False + True + False + + + + + GtkRadioButton + title3 + True + + False + True + title + + 0 + 1 + 2 + 3 + 5 + 5 + False + False + False + False + True + False + + + + + + + + diff --git a/src/plugins/ReadGedcom.py b/src/plugins/ReadGedcom.py index 8f5f71106..4e83f8802 100644 --- a/src/plugins/ReadGedcom.py +++ b/src/plugins/ReadGedcom.py @@ -58,11 +58,11 @@ photo_types = [ "jpeg", "bmp", "pict", "pntg", "tpic", "png", "gif", _ADDRX = [ "ADDR", "ADR1", "ADR2" ] -ged2rel = {} +ged2gramps = {} for val in const.personalConstantEvents.keys(): key = const.personalConstantEvents[val] if key != "": - ged2rel[key] = val + ged2gramps[key] = val ged2fam = {} for val in const.familyConstantEvents.keys(): @@ -688,7 +688,7 @@ class GedcomParser: else: event = Event() try: - event.setName(ged2rel[matches[1]]) + event.setName(ged2gramps[matches[1]]) except: event.setName(matches[1]) if matches[2] != None: @@ -977,8 +977,8 @@ class GedcomParser: break elif matches[1] == "TYPE": if event.getName() == "": - if ged2rel.has_key(matches[2]): - name = ged2rel[matches[2]] + if ged2gramps.has_key(matches[2]): + name = ged2gramps[matches[2]] else: name = matches[2] event.setName(name) @@ -1053,7 +1053,7 @@ class GedcomParser: #--------------------------------------------------------------------- def parse_family_event(self,event,level): global ged2fam - global ged2rel + global ged2gramps while 1: matches = self.get_next() @@ -1335,7 +1335,7 @@ class GedcomParser: return else: label = self.parse_label(level+1) - ged2rel[matches[1]] = label + ged2gramps[matches[1]] = label #--------------------------------------------------------------------- #