From 92f3ec3dfe22b9f08093a2d37479b7864ecefb8b Mon Sep 17 00:00:00 2001 From: Benny Malengier Date: Sat, 22 Jan 2011 19:55:59 +0000 Subject: [PATCH] Make sure surnames are saved in a way that makes sense: * no empty surnames if more than one * one primary surname Clean up whitespace svn: r16444 --- src/gui/editors/editperson.py | 360 ++++++++++++++++++---------------- 1 file changed, 187 insertions(+), 173 deletions(-) diff --git a/src/gui/editors/editperson.py b/src/gui/editors/editperson.py index 4133f2d96..2bbcad3cb 100644 --- a/src/gui/editors/editperson.py +++ b/src/gui/editors/editperson.py @@ -10,7 +10,7 @@ # 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, +# 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. @@ -63,12 +63,12 @@ import config from QuestionDialog import ErrorDialog, ICON from Errors import ValidationError -from displaytabs import (PersonEventEmbedList, NameEmbedList, SourceEmbedList, - AttrEmbedList, AddrEmbedList, NoteTab, GalleryTab, - WebEmbedList, PersonRefEmbedList, LdsEmbedList, +from displaytabs import (PersonEventEmbedList, NameEmbedList, SourceEmbedList, + AttrEmbedList, AddrEmbedList, NoteTab, GalleryTab, + WebEmbedList, PersonRefEmbedList, LdsEmbedList, PersonBackRefList, SurnameTab) from gen.plug import CATEGORY_QR_PERSON - + #------------------------------------------------------------------------- # # Constants @@ -85,7 +85,7 @@ class SingSurn(object): """ def __init__(self, gladeobj): self.top = gladeobj - + def hide_all(self): #self.top.get_object('prefixlabel').hide() self.top.get_object('prefix').hide() @@ -95,7 +95,7 @@ class SingSurn(object): self.top.get_object('originlabel').hide() self.top.get_object('cmborigin').hide() self.top.get_object('multsurnamebtn').hide() - + def show_all(self): #self.top.get_object('prefixlabel').show() self.top.get_object('prefix').show() @@ -105,38 +105,38 @@ class SingSurn(object): self.top.get_object('originlabel').show() self.top.get_object('cmborigin').show() self.top.get_object('multsurnamebtn').show() - + class EditPerson(EditPrimary): """ - The EditPerson dialog is derived from the EditPrimary class. - + The EditPerson dialog is derived from the EditPrimary class. + It allows for the editing of the primary object type of Person. - + """ QR_CATEGORY = CATEGORY_QR_PERSON def __init__(self, dbstate, uistate, track, person, callback=None): """ - Create an EditPerson window. - + Create an EditPerson window. + Associate a person with the window. - + """ - EditPrimary.__init__(self, dbstate, uistate, track, person, - dbstate.db.get_person_from_handle, + EditPrimary.__init__(self, dbstate, uistate, track, person, + dbstate.db.get_person_from_handle, dbstate.db.get_person_from_gramps_id, callback) # I don't know why this is necessary for EditPerson and no - # others, but without it, the height won't get smaller than a + # others, but without it, the height won't get smaller than a # certain size: self._set_size() def empty_object(self): """ - Return an empty Person object for comparison for changes. - + Return an empty Person object for comparison for changes. + This is used by the base class (EditPrimary). - + """ person = gen.lib.Person() #the editor requires a surname @@ -155,19 +155,19 @@ class EditPerson(EditPrimary): else: title = _('New Person') return title - + def get_preview_name(self): prevname = name_displayer.display(self.obj) return prevname def _local_init(self): """ - Performs basic initialization, including setting up widgets and the - glade interface. - - Local initialization function. + Performs basic initialization, including setting up widgets and the + glade interface. + + Local initialization function. This is called by the base class of EditPrimary, and overridden here. - + """ self.width_key = 'interface.person-width' self.height_key = 'interface.person-height' @@ -180,38 +180,38 @@ class EditPerson(EditPrimary): self.load_rect = None self.top = Glade() - self.set_window(self.top.toplevel, None, + self.set_window(self.top.toplevel, None, self.get_menu_title()) - + self.obj_photo = self.top.get_object("personPix") self.frame_photo = self.top.get_object("frame5") self.eventbox = self.top.get_object("eventbox1") self.singsurnfr = SingSurn(self.top) self.multsurnfr = self.top.get_object("hboxmultsurnames") - self.multsurnfr.set_size_request(-1, + self.multsurnfr.set_size_request(-1, int(config.get('interface.surname-box-height'))) self.singlesurn_active = True - self.surntab = SurnameTab(self.dbstate, self.uistate, self.track, + self.surntab = SurnameTab(self.dbstate, self.uistate, self.track, self.obj.get_primary_name(), on_change=self._changed_name) self.top.get_object("hboxmultsurnames").pack_start(self.surntab) - + self.set_contexteventbox(self.top.get_object("eventboxtop")) def _post_init(self): """ - Handle any initialization that needs to be done after the interface is + Handle any initialization that needs to be done after the interface is brought up. - - Post initalization function. - This is called by _EditPrimary's init routine, and overridden in the + + Post initalization function. + This is called by _EditPrimary's init routine, and overridden in the derived class (this class). - + """ self.load_person_image() self.given.grab_focus() self._changed_name(None) - + if len(self.obj.get_primary_name().get_surname_list()) > 1: self.singsurnfr.hide_all() self.multsurnfr.show_all() @@ -227,7 +227,7 @@ class EditPerson(EditPrimary): def _connect_signals(self): """ - Connect any signals that need to be connected. + Connect any signals that need to be connected. Called by the init routine of the base class (_EditPrimary). """ self.define_cancel_button(self.top.get_object("button15")) @@ -245,7 +245,7 @@ class EditPerson(EditPrimary): def _connect_db_signals(self): """ - Connect any signals that need to be connected. + Connect any signals that need to be connected. Called by the init routine of the base class (_EditPrimary). """ self._add_db_signal('person-rebuild', self._do_close) @@ -260,17 +260,17 @@ class EditPerson(EditPrimary): def family_change(self, handle_list=[]): """ - Callback for family change signals. - - This should rebuild the + Callback for family change signals. + + This should rebuild the backreferences to family in person when: - 1)a family the person is parent of changes. Person could have + 1)a family the person is parent of changes. Person could have been removed - 2)a family the person is child in changes. Child could have been + 2)a family the person is child in changes. Child could have been removed 3)a family is changed. The person could be added as child or parent - + """ #As this would be an extensive check, we choose the easy path and # rebuild family backreferences on all family changes @@ -294,7 +294,7 @@ class EditPerson(EditPrimary): self.event_list.rebuild_callback() def _validate_call(self, widget, text): - """ a callname must be a part of the given name, see if this is the + """ a callname must be a part of the given name, see if this is the case """ validcall = self.given.obj.get_text().split() dummy = copy(validcall) @@ -307,89 +307,89 @@ class EditPerson(EditPrimary): def _setup_fields(self): """ - Connect the GrampsWidget objects to field in the interface. - + Connect the GrampsWidget objects to field in the interface. + This allows the widgets to keep the data in the attached Person object up to date at all times, eliminating a lot of need in 'save' routine. - + """ self.private = widgets.PrivacyButton( - self.top.get_object('private'), - self.obj, + self.top.get_object('private'), + self.obj, self.db.readonly) self.gender = widgets.MonitoredMenu( - self.top.get_object('gender'), - self.obj.set_gender, - self.obj.get_gender, + self.top.get_object('gender'), + self.obj.set_gender, + self.obj.get_gender, ( - (_('female'), gen.lib.Person.FEMALE), - (_('male'), gen.lib.Person.MALE), + (_('female'), gen.lib.Person.FEMALE), + (_('male'), gen.lib.Person.MALE), (_('unknown'), gen.lib.Person.UNKNOWN) - ), + ), self.db.readonly) self.ntype_field = widgets.MonitoredDataType( - self.top.get_object("ntype"), - self.pname.set_type, + self.top.get_object("ntype"), + self.pname.set_type, self.pname.get_type, self.db.readonly, self.db.get_name_types()) - + #part of Given Name section self.given = widgets.MonitoredEntry( - self.top.get_object("given_name"), - self.pname.set_first_name, - self.pname.get_first_name, + self.top.get_object("given_name"), + self.pname.set_first_name, + self.pname.get_first_name, self.db.readonly) self.call = widgets.MonitoredEntry( - self.top.get_object("call"), - self.pname.set_call_name, - self.pname.get_call_name, + self.top.get_object("call"), + self.pname.set_call_name, + self.pname.get_call_name, self.db.readonly) self.call.connect("validate", self._validate_call) #force validation now with initial entry self.call.obj.validate(force=True) self.title = widgets.MonitoredEntry( - self.top.get_object("title"), - self.pname.set_title, - self.pname.get_title, + self.top.get_object("title"), + self.pname.set_title, + self.pname.get_title, self.db.readonly) self.suffix = widgets.MonitoredEntryIndicator( - self.top.get_object("suffix"), - self.pname.set_suffix, + self.top.get_object("suffix"), + self.pname.set_suffix, self.pname.get_suffix, _('suffix'), self.db.readonly) self.nick = widgets.MonitoredEntry( - self.top.get_object("nickname"), - self.pname.set_nick_name, - self.pname.get_nick_name, + self.top.get_object("nickname"), + self.pname.set_nick_name, + self.pname.get_nick_name, self.db.readonly) #part of Single Surname section self.surname_field = widgets.MonitoredEntry( - self.top.get_object("surname"), - self.pname.get_primary_surname().set_surname, - self.pname.get_primary_surname().get_surname, - self.db.readonly, + self.top.get_object("surname"), + self.pname.get_primary_surname().set_surname, + self.pname.get_primary_surname().get_surname, + self.db.readonly, autolist=self.db.get_surname_list() if not self.db.readonly else []) self.prefix = widgets.MonitoredEntryIndicator( - self.top.get_object("prefix"), - self.pname.get_primary_surname().set_prefix, - self.pname.get_primary_surname().get_prefix, + self.top.get_object("prefix"), + self.pname.get_primary_surname().set_prefix, + self.pname.get_primary_surname().get_prefix, _('prefix'), self.db.readonly) self.ortype_field = widgets.MonitoredDataType( - self.top.get_object("cmborigin"), - self.pname.get_primary_surname().set_origintype, + self.top.get_object("cmborigin"), + self.pname.get_primary_surname().set_origintype, self.pname.get_primary_surname().get_origintype, self.db.readonly, self.db.get_origin_types()) @@ -397,29 +397,29 @@ class EditPerson(EditPrimary): #other fields self.tags = widgets.MonitoredTagList( - self.top.get_object("tag_label"), - self.top.get_object("tag_button"), - self.obj.set_tag_list, + self.top.get_object("tag_label"), + self.top.get_object("tag_button"), + self.obj.set_tag_list, self.obj.get_tag_list, self.db, self.uistate, self.track, self.db.readonly) self.gid = widgets.MonitoredEntry( - self.top.get_object("gid"), - self.obj.set_gramps_id, - self.obj.get_gramps_id, + self.top.get_object("gid"), + self.obj.set_gramps_id, + self.obj.get_gramps_id, self.db.readonly) #make sure title updates automatically for obj in [self.top.get_object("given_name"), self.top.get_object("call"), self.top.get_object("suffix"), - self.top.get_object("prefix"), + self.top.get_object("prefix"), self.top.get_object("surname"), ]: obj.connect('changed', self._changed_name) - + self.preview_name = self.top.get_object("full_name") self.preview_name.modify_font(pango.FontDescription('sans bold 12')) @@ -432,75 +432,75 @@ class EditPerson(EditPrimary): notebook.set_scrollable(True) self.event_list = PersonEventEmbedList(self.dbstate, - self.uistate, + self.uistate, self.track, self.obj) - self._add_tab(notebook, self.event_list) + self._add_tab(notebook, self.event_list) self.track_ref_for_deletion("event_list") - + self.name_list = NameEmbedList(self.dbstate, self.uistate, - self.track, + self.track, self.obj.get_alternate_names(), self.obj, self.name_callback) self._add_tab(notebook, self.name_list) self.track_ref_for_deletion("name_list") - + self.srcref_list = SourceEmbedList(self.dbstate, self.uistate, self.track, self.obj) - self._add_tab(notebook, self.srcref_list) + self._add_tab(notebook, self.srcref_list) self.track_ref_for_deletion("srcref_list") - + self.attr_list = AttrEmbedList(self.dbstate, self.uistate, - self.track, + self.track, self.obj.get_attribute_list()) - self._add_tab(notebook, self.attr_list) + self._add_tab(notebook, self.attr_list) self.track_ref_for_deletion("attr_list") - + self.addr_list = AddrEmbedList(self.dbstate, self.uistate, - self.track, + self.track, self.obj.get_address_list()) - self._add_tab(notebook, self.addr_list) + self._add_tab(notebook, self.addr_list) self.track_ref_for_deletion("addr_list") - + self.note_tab = NoteTab(self.dbstate, self.uistate, - self.track, + self.track, self.obj.get_note_list(), self.get_menu_title(), notetype=gen.lib.NoteType.PERSON) - self._add_tab(notebook, self.note_tab) + self._add_tab(notebook, self.note_tab) self.track_ref_for_deletion("note_tab") - + self.gallery_tab = GalleryTab(self.dbstate, self.uistate, - self.track, + self.track, self.obj.get_media_list(), self.load_person_image) self._add_tab(notebook, self.gallery_tab) self.track_ref_for_deletion("gallery_tab") - + self.web_list = WebEmbedList(self.dbstate, self.uistate, - self.track, + self.track, self.obj.get_url_list()) self._add_tab(notebook, self.web_list) self.track_ref_for_deletion("web_list") - self.person_ref_list = PersonRefEmbedList(self.dbstate, self.uistate, - self.track, + self.person_ref_list = PersonRefEmbedList(self.dbstate, self.uistate, + self.track, self.obj.get_person_ref_list()) self._add_tab(notebook, self.person_ref_list) self.track_ref_for_deletion("person_ref_list") self.lds_list = LdsEmbedList(self.dbstate, self.uistate, - self.track, + self.track, self.obj.get_lds_ord_list()) self._add_tab(notebook, self.lds_list) self.track_ref_for_deletion("lds_list") @@ -540,24 +540,24 @@ class EditPerson(EditPrimary): self.nick.reinit(self.pname.set_nick_name, self.pname.get_nick_name) #part of Single Surname section self.surname_field.reinit( - self.pname.get_primary_surname().set_surname, + self.pname.get_primary_surname().set_surname, self.pname.get_primary_surname().get_surname) - self.prefix.reinit( - self.pname.get_primary_surname().set_prefix, + self.prefix.reinit( + self.pname.get_primary_surname().set_prefix, self.pname.get_primary_surname().get_prefix) - self.ortype_field.reinit( - self.pname.get_primary_surname().set_origintype, + self.ortype_field.reinit( + self.pname.get_primary_surname().set_origintype, self.pname.get_primary_surname().get_origintype) - + #remove present surname tab, and put new one based on current prim name msurhbox = self.top.get_object("hboxmultsurnames") msurhbox.remove(self.surntab) - self.surntab = SurnameTab(self.dbstate, self.uistate, self.track, + self.surntab = SurnameTab(self.dbstate, self.uistate, self.track, self.obj.get_primary_name()) msurhbox.pack_start(self.surntab) - + if len(self.pname.get_surname_list()) == 1: self.singlesurn_active = True else: @@ -578,34 +578,34 @@ class EditPerson(EditPrimary): def _image_callback(self, ref, obj): """ - Called when a media reference had been edited. - - This allows for the updating image on the main form which has just + Called when a media reference had been edited. + + This allows for the updating image on the main form which has just been modified. - + """ self.load_photo(Utils.media_path_full(self.dbstate.db, obj.get_path()), ref.get_rectangle()) def _image_button_press(self, obj, event): """ - Button press event that is caught when a button has been pressed while - on the image on the main form. - - This does not apply to the images in galleries, just the image on the + Button press event that is caught when a button has been pressed while + on the image on the main form. + + This does not apply to the images in galleries, just the image on the main form. - + """ if event.type == gtk.gdk._2BUTTON_PRESS and event.button == 1: media_list = self.obj.get_media_list() - if media_list: + if media_list: media_ref = media_list[0] object_handle = media_ref.get_reference_handle() media_obj = self.db.get_object_from_handle(object_handle) try: - EditMediaRef(self.dbstate, self.uistate, self.track, + EditMediaRef(self.dbstate, self.uistate, self.track, media_obj, media_ref, self._image_callback) except Errors.WindowActiveError: pass @@ -620,16 +620,16 @@ class EditPerson(EditPrimary): def _show_popup(self, photo, event): """ - Look for right-clicks on a picture and create a popup menu of the + Look for right-clicks on a picture and create a popup menu of the available actions. """ - + menu = gtk.Menu() menu.set_title(_("Media Object")) obj = self.db.get_object_from_handle(photo.get_reference_handle()) if obj: add_menuitem(menu, _("View"), photo, self._popup_view_photo) - add_menuitem(menu, _("Edit Object Properties"), photo, + add_menuitem(menu, _("Edit Object Properties"), photo, self._popup_change_description) menu.popup(None, None, None, event.button, event.time) @@ -654,37 +654,37 @@ class EditPerson(EditPrimary): media_ref = media_list[0] object_handle = media_ref.get_reference_handle() media_obj = self.db.get_object_from_handle(object_handle) - EditMediaRef(self.dbstate, self.uistate, self.track, + EditMediaRef(self.dbstate, self.uistate, self.track, media_obj, media_ref, self._image_callback) - + def _top_contextmenu(self): """ - Override from base class, the menuitems and actiongroups for the top + Override from base class, the menuitems and actiongroups for the top of context menu. """ self.all_action = gtk.ActionGroup("/PersonAll") self.home_action = gtk.ActionGroup("/PersonHome") self.track_ref_for_deletion("all_action") self.track_ref_for_deletion("home_action") - + self.all_action.add_actions([ - ('ActivePerson', gtk.STOCK_APPLY, _("Make Active Person"), + ('ActivePerson', gtk.STOCK_APPLY, _("Make Active Person"), None, None, self._make_active), ]) self.home_action.add_actions([ - ('HomePerson', gtk.STOCK_HOME, _("Make Home Person"), + ('HomePerson', gtk.STOCK_HOME, _("Make Home Person"), None, None, self._make_home_person), ]) - + self.all_action.set_visible(True) self.home_action.set_visible(True) - + ui_top_cm = ''' ''' - + return ui_top_cm, [self.all_action, self.home_action] - + def _post_build_popup_ui(self): """ Override base class, make inactive home action if not needed. @@ -695,10 +695,10 @@ class EditPerson(EditPrimary): self.home_action.set_sensitive(False) else : self.home_action.set_sensitive(True) - + def _make_active(self, obj): self.uistate.set_active(self.obj.get_handle(), 'Person') - + def _make_home_person(self, obj): handle = self.obj.get_handle() if handle: @@ -834,33 +834,47 @@ class EditPerson(EditPrimary): """ self.ok_button.set_sensitive(False) if self.object_is_empty(): - ErrorDialog(_("Cannot save person"), + ErrorDialog(_("Cannot save person"), _("No data exists for this person. Please " "enter data or cancel the edit.")) self.ok_button.set_sensitive(True) return - + # fix surname problems + for name in [self.obj.get_primary_name()] + self.obj.get_alternate_names(): + if len(name.get_surname_list()) > 1: + newlist = [surn for surn in name.get_surname_list() if not surn.is_empty()] + if len(newlist) != len(name.get_surname_list()): + name.set_surname_list(newlist) + if len(name.get_surname_list()) == 0: + name.set_surname_list([gen.lib.Surname()]) + try: + primind = [surn.get_primary() for surn in name.get_surname_list()].index(True) + except ValueError: + primind = 0 # no match + name.set_primary_surname(primind) + + # fix ide problems (uses_dupe_id, id) = self._uses_duplicate_id() if uses_dupe_id: prim_object = self.get_from_gramps_id(id) name = self.name_displayer.display(prim_object) msg1 = _("Cannot save person. ID already exists.") msg2 = _("You have attempted to use the existing Gramps ID with " - "value %(id)s. This value is already used by '" + "value %(id)s. This value is already used by '" "%(prim_object)s'. Please enter a different ID or leave " "blank to get the next available ID value.") % { 'id' : id, 'prim_object' : name } ErrorDialog(msg1, msg2) self.ok_button.set_sensitive(True) return - + self._check_for_unknown_gender() self.db.set_birth_death_index(self.obj) trans = self.db.transaction_begin() - + self._update_family_ids() if not self.obj.get_handle(): @@ -881,13 +895,13 @@ class EditPerson(EditPrimary): def _edit_name_clicked(self, obj): """ Bring up the EditName dialog for this name. - + Called when the edit name button is clicked for the primary name on the main form (not in the names tab). - + """ try: - EditName(self.dbstate, self.uistate, self.track, + EditName(self.dbstate, self.uistate, self.track, self.pname, self._update_name) except Errors.WindowActiveError: pass @@ -905,18 +919,18 @@ class EditPerson(EditPrimary): def _update_name(self, name): """ Called when the primary name has been changed by the EditName - dialog. - + dialog. + This allows us to update the main form in response to any changes. - + """ - for obj in (self.ntype_field, self.given, self.call, self.title, + for obj in (self.ntype_field, self.given, self.call, self.title, self.suffix, self.nick, self.surname_field, self.prefix, self.ortype_field): obj.update() - + self.__renewmultsurnames() - + if len(self.obj.get_primary_name().get_surname_list()) == 1: self.singlesurn_active = True else: @@ -924,30 +938,30 @@ class EditPerson(EditPrimary): if self.singlesurn_active: self.multsurnfr.hide_all() self.singsurnfr.show_all() - + else: self.singsurnfr.hide_all() self.multsurnfr.show_all() def __renewmultsurnames(self): - """Update mult surnames section with what is presently the + """Update mult surnames section with what is presently the correct surname. - It is easier to recreate the entire mult surnames GUI than + It is easier to recreate the entire mult surnames GUI than changing what has changed visually. """ #remove present surname tab, and put new one msurhbox = self.top.get_object("hboxmultsurnames") msurhbox.remove(self.surntab) - self.surntab = SurnameTab(self.dbstate, self.uistate, self.track, + self.surntab = SurnameTab(self.dbstate, self.uistate, self.track, self.obj.get_primary_name()) msurhbox.pack_start(self.surntab) def load_person_image(self): """ Load the primary image into the main form if it exists. - + Used as callback on Gallery Tab too. - + """ media_list = self.obj.get_media_list() if media_list: @@ -992,11 +1006,11 @@ class EditPerson(EditPrimary): def reorder_child_ref_list(self, person, child_ref_list): """ Reorder the child list to put the specified person in his/her - correct birth order. - - Only check *valid* birthdates. Move the person as short a distance as + correct birth order. + + Only check *valid* birthdates. Move the person as short a distance as possible. - + """ if self.birth_dates_in_order(child_ref_list): @@ -1073,14 +1087,14 @@ class EditPerson(EditPrimary): class GenderDialog(gtk.MessageDialog): def __init__(self, parent=None): gtk.MessageDialog.__init__(self, - parent, + parent, flags=gtk.DIALOG_MODAL, type=gtk.MESSAGE_QUESTION, ) self.set_icon(ICON) self.set_title('') - self.set_markup('%s' % + self.set_markup('%s' % _('Unknown gender specified')) self.format_secondary_text( _("The gender of the person is currently unknown. "