Merged geps21 branch, changes r15866-16034, into trunk

svn: r16035
This commit is contained in:
Benny Malengier 2010-10-24 14:43:47 +00:00
commit e109e3d743
48 changed files with 3589 additions and 1548 deletions

View File

@ -59,6 +59,7 @@ DATABASE
bookmarks?, namemaps?)> bookmarks?, namemaps?)>
<!ATTLIST database xmlns CDATA #FIXED "http://gramps-project.org/xml/1.4.0/"> <!ATTLIST database xmlns CDATA #FIXED "http://gramps-project.org/xml/1.4.0/">
<!-- ************************************************************ <!-- ************************************************************
HEADER HEADER
@ -98,7 +99,7 @@ PEOPLE
home IDREF #IMPLIED home IDREF #IMPLIED
> >
<!ELEMENT person (gender, name*, nick?, eventref*, lds_ord*, <!ELEMENT person (gender, name*, eventref*, lds_ord*,
objref*, address*, attribute*, url*, childof*, objref*, address*, attribute*, url*, childof*,
parentin*, personref*, noteref*, sourceref*, tagref*)> parentin*, personref*, noteref*, sourceref*, tagref*)>
<!ATTLIST person <!ATTLIST person
@ -114,7 +115,7 @@ GENDER has values of M, F, or U.
--> -->
<!ELEMENT gender (#PCDATA)> <!ELEMENT gender (#PCDATA)>
<!ELEMENT name (first?, call?, last?, suffix?, patronymic?, title?, <!ELEMENT name (first?, call?, surname*, nick?, familynick?, suffix?, title?, group?
(daterange|datespan|dateval|datestr)?, noteref*, sourceref*)> (daterange|datespan|dateval|datestr)?, noteref*, sourceref*)>
<!ATTLIST name <!ATTLIST name
alt (0|1) #IMPLIED alt (0|1) #IMPLIED
@ -126,16 +127,18 @@ GENDER has values of M, F, or U.
<!ELEMENT first (#PCDATA)> <!ELEMENT first (#PCDATA)>
<!ELEMENT call (#PCDATA)> <!ELEMENT call (#PCDATA)>
<!ELEMENT last (#PCDATA)>
<!ATTLIST last
prefix CDATA #IMPLIED
group CDATA #IMPLIED
>
<!ELEMENT suffix (#PCDATA)> <!ELEMENT suffix (#PCDATA)>
<!ELEMENT patronymic (#PCDATA)>
<!ELEMENT title (#PCDATA)> <!ELEMENT title (#PCDATA)>
<!ELEMENT nick (#PCDATA)> <!ELEMENT nick (#PCDATA)>
<!ELEMENT familynick (#PCDATA)>
<!ELEMENT group (#PCDATA)>
<!ELEMENT surname (#PCDATA)>
<!ATTLIST surname
prefix CDATA #IMPLIED
primary (1|0) #IMPLIED
derivation CDATA #IMPLIED
connector CDATA #IMPLIED
>
<!ELEMENT childof EMPTY> <!ELEMENT childof EMPTY>
<!ATTLIST childof hlink IDREF #REQUIRED <!ATTLIST childof hlink IDREF #REQUIRED

View File

@ -161,7 +161,6 @@
<zeroOrMore><element name="name"> <zeroOrMore><element name="name">
<ref name="name-content"/> <ref name="name-content"/>
</element></zeroOrMore> </element></zeroOrMore>
<optional><element name="nick"><text/></element></optional>
<zeroOrMore><element name="eventref"> <zeroOrMore><element name="eventref">
<ref name="eventref-content"/> <ref name="eventref-content"/>
</element></zeroOrMore> </element></zeroOrMore>
@ -235,13 +234,13 @@
<optional><attribute name="display"><text/></attribute></optional> <optional><attribute name="display"><text/></attribute></optional>
<optional><element name="first"><text/></element></optional> <optional><element name="first"><text/></element></optional>
<optional><element name="call"><text/></element></optional> <optional><element name="call"><text/></element></optional>
<optional><element name="last"> <optional><element name="nick"><text/></element></optional>
<text/> <optional><element name="familynick"><text/></element></optional>
<optional><attribute name="prefix"><text/></attribute></optional> <optional><element name="group"><text/></element></optional>
<optional><attribute name="group"><text/></attribute></optional> <zeroOrMore><element name="surname">
</element></optional> <ref name="surname-content"/>
</element></zeroOrMore>
<optional><element name="suffix"><text/></element></optional> <optional><element name="suffix"><text/></element></optional>
<optional><element name="patronymic"><text/></element></optional>
<optional><element name="title"><text/></element></optional> <optional><element name="title"><text/></element></optional>
<optional><ref name="date-content"/></optional> <optional><ref name="date-content"/></optional>
<zeroOrMore><element name="noteref"> <zeroOrMore><element name="noteref">
@ -252,6 +251,24 @@
</element></zeroOrMore> </element></zeroOrMore>
</define> </define>
<define name="surname-content">
<element name="surname">
<text/>
<optional><attribute name="prefix"><text/></attribute></optional>
<optional><attribute name="primary"><choice>
<value>1</value>
<value>0</value>
</choice></attribute></optional>
<optional><attribute name="derivation"><choice>
<value>inherited</value>
<value>patronymic</value>
<value>matronymic</value>
<value>other</value>
</choice></attribute></optional>
<optional><attribute name="connector"><text/></attribute></optional>
</element>
</define>
<define name="address-content"> <define name="address-content">
<optional><attribute name="priv"> <optional><attribute name="priv">
<ref name="priv-content"/> <ref name="priv-content"/>

View File

@ -110,12 +110,15 @@ src/gen/lib/eventref.py
src/gen/lib/privsrcnote.py src/gen/lib/privsrcnote.py
src/gen/lib/placebase.py src/gen/lib/placebase.py
src/gen/lib/name.py src/gen/lib/name.py
src/gen/lib/nametype.py
src/gen/lib/nameorigintype.py
src/gen/lib/addressbase.py src/gen/lib/addressbase.py
src/gen/lib/family.py src/gen/lib/family.py
src/gen/lib/event.py src/gen/lib/event.py
src/gen/lib/nametype.py
src/gen/lib/secondaryobj.py src/gen/lib/secondaryobj.py
src/gen/lib/srcbase.py src/gen/lib/srcbase.py
src/gen/lib/surname.py
src/gen/lib/surnamebase.py
src/gen/lib/eventtype.py src/gen/lib/eventtype.py
src/gen/lib/researcher.py src/gen/lib/researcher.py
src/gen/lib/familyreltype.py src/gen/lib/familyreltype.py
@ -270,6 +273,8 @@ src/gui/editors/displaytabs/reporefmodel.py
src/gui/editors/displaytabs/sourcebackreflist.py src/gui/editors/displaytabs/sourcebackreflist.py
src/gui/editors/displaytabs/sourceembedlist.py src/gui/editors/displaytabs/sourceembedlist.py
src/gui/editors/displaytabs/sourcerefmodel.py src/gui/editors/displaytabs/sourcerefmodel.py
src/gui/editors/displaytabs/surnametab.py
src/gui/editors/displaytabs/surnamemodel.py
src/gui/editors/displaytabs/webembedlist.py src/gui/editors/displaytabs/webembedlist.py
src/gui/editors/displaytabs/webmodel.py src/gui/editors/displaytabs/webmodel.py
src/gui/editors/displaytabs/__init__.py src/gui/editors/displaytabs/__init__.py

View File

@ -133,6 +133,7 @@ class _DdTargets(object):
self.SOURCEREF = _DdType(self, 'srcref') self.SOURCEREF = _DdType(self, 'srcref')
self.SOURCE_LINK = _DdType(self, 'source-link') self.SOURCE_LINK = _DdType(self, 'source-link')
self.URL = _DdType(self, 'url') self.URL = _DdType(self, 'url')
self.SURNAME = _DdType(self, 'surname')
# List of all types that are used between # List of all types that are used between
# gramps widgets but should not be exported # gramps widgets but should not be exported
@ -159,6 +160,7 @@ class _DdTargets(object):
self.SOURCEREF, self.SOURCEREF,
self.SOURCE_LINK, self.SOURCE_LINK,
self.URL, self.URL,
self.SURNAME
] ]
self.CHILD = _DdType(self, 'child') self.CHILD = _DdType(self, 'child')

View File

@ -34,6 +34,7 @@ from gen.ggettext import sgettext as _
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
from Filters.Rules._Rule import Rule from Filters.Rules._Rule import Rule
from gen.lib import NameOriginType
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# #
@ -43,41 +44,76 @@ from Filters.Rules._Rule import Rule
class HasNameOf(Rule): class HasNameOf(Rule):
"""Rule that checks for full or partial name matches""" """Rule that checks for full or partial name matches"""
labels = [ _('Given name:'), labels = [ _('Given name:'),
_('Family name:'), _('Full Family name:'),
_('Suffix:'),
_('person|Title:'), _('person|Title:'),
_('Suffix:'),
_('Call Name:'),
_('Nick Name:'),
_('Prefix:'), _('Prefix:'),
_('Single Surname:'),
_('Connector'),
_('Patronymic:'), _('Patronymic:'),
_('Call Name:'),] _('Family Nick Name:')]
name = _('People with the <name>') name = _('People with the <name>')
description = _("Matches people with a specified (partial) name") description = _("Matches people with a specified (partial) name")
category = _('General filters') category = _('General filters')
def apply(self,db,person): def prepare(self, db):
self.firstn = self.list[0] self.firstn = self.list[0].upper()
self.lastn = self.list[1] self.lastn = self.list[1].upper()
self.surn = self.list[2] self.title = self.list[2].upper()
self.title = self.list[3] self.suffix = self.list[3].upper()
self.prefix = self.list[4] self.calln = self.list[4].upper()
self.patr = self.list[5] self.nick = self.list[5].upper()
self.calln = self.list[6] self.famnick = self.list[10].upper()
#surname parts
self.prefix = self.list[6].upper()
self.surn = self.list[7].upper()
self.con = self.list[8].upper()
self.patr = self.list[9].upper()
def apply(self, db, person):
for name in [person.get_primary_name()] + person.get_alternate_names(): for name in [person.get_primary_name()] + person.get_alternate_names():
val = 1 val = 1
if self.firstn and name.get_first_name().upper().find(self.firstn.upper()) == -1: valpref = 0
if not self.prefix:
valpref = 1
valsurn = 0
if not self.surn:
valsurn = 1
valcon = 0
if not self.con:
valcon = 1
valpatr = 0
if not self.patr:
valpatr = 1
if self.firstn and name.get_first_name().upper().find(self.firstn) == -1:
val = 0 val = 0
if self.lastn and name.get_surname().upper().find(self.lastn.upper()) == -1: elif self.lastn and name.get_surname().upper().find(self.lastn) == -1:
val = 0 val = 0
if self.surn and name.get_suffix().upper().find(self.surn.upper()) == -1: elif self.suffix and name.get_suffix().upper().find(self.surn) == -1:
val = 0 val = 0
if self.title and name.get_title().upper().find(self.title.upper()) == -1: elif self.title and name.get_title().upper().find(self.title) == -1:
val = 0 val = 0
if self.prefix and name.get_prefix().upper().find(self.prefix.upper()) == -1: elif self.calln and name.get_call_name().upper().find(self.calln) == -1:
val = 0 val = 0
if self.patr and name.get_patronymic().upper().find(self.patr.upper()) == -1: elif self.nick and name.get_nick_name().upper().find(self.nick) == -1:
val = 0 val = 0
if self.calln and name.get_call_name().upper().find(self.calln.upper()) == -1: elif self.famnick and name.get_family_nick_name().upper().find(self.famnick) == -1:
val = 0 val = 0
if val == 1: else:
#obtain surnames
for surn in name.get_surname_list():
if self.prefix and surn.get_prefix().upper().find(self.prefix) != -1:
valpref = 1
if self.surn and surn.get_surname().upper().find(self.surn) != -1:
valsurn = 1
if self.con and surn.get_connector().upper().find(self.con) != -1:
valcon = 1
if self.patr and surn.get_origintype().value == NameOriginType.PATRONYMIC \
and surn.get_surname().upper().find(self.patr) != -1:
valpatr = 1
if val == 1 and valpref == 1 and valsurn == 1 and valcon == 1 and valpatr ==1:
return True return True
return False return False

View File

@ -48,8 +48,9 @@ class IncompleteNames(Rule):
def apply(self,db,person): def apply(self,db,person):
for name in [person.get_primary_name()] + person.get_alternate_names(): for name in [person.get_primary_name()] + person.get_alternate_names():
if name.get_first_name() == "": if name.get_first_name().strip() == "":
return True
if name.get_surname() == "":
return True return True
for surn in name.get_surname_list():
if surn.get_surname().strip() == "":
return True
return False return False

View File

@ -59,8 +59,8 @@ class RegExpName(Rule):
def apply(self,db,person): def apply(self,db,person):
for name in [person.get_primary_name()] + person.get_alternate_names(): for name in [person.get_primary_name()] + person.get_alternate_names():
for field in [name.first_name, name.surname, name.suffix, name.title, for field in [name.first_name, name.get_surname(), name.suffix,
name.prefix, name.patronymic, name.call]: name.title, name.nick, name.famnick, name.call]:
if self.match.match(field): if self.match.match(field):
return True return True
else: else:

View File

@ -49,12 +49,13 @@ class SearchName(Rule):
category = _('General filters') category = _('General filters')
def apply(self, db, person): def apply(self, db, person):
src = self.list[0].upper() src = self.list[0].upper()
if not src:
return False
for name in [person.get_primary_name()] + person.get_alternate_names(): for name in [person.get_primary_name()] + person.get_alternate_names():
for field in [name.first_name, name.surname, name.suffix, name.title, for field in [name.first_name, name.get_surname(), name.suffix,
name.prefix, name.patronymic, name.call]: name.title, name.nick, name.famnick, name.call]:
if src and field.upper().find(src) != -1: if src and field.upper().find(src) != -1:
return True return True
else: else:

View File

@ -507,6 +507,19 @@ class ScratchName(ScratchObjWrapper):
self._title = str(self._obj.get_type()) self._title = str(self._obj.get_type())
self._value = self._obj.get_name() self._value = self._obj.get_name()
class ScratchSurname(ScratchObjWrapper):
DROP_TARGETS = [DdTargets.SURNAME]
DRAG_TARGET = DdTargets.SURNAME
ICON = ICONS['name']
def __init__(self, dbstate, obj):
super(ScratchSurname, self).__init__(dbstate, obj)
self._type = _("Surname")
if self._obj:
self._title = self._obj.get_surname()
self._value = self._obj.get_surname()
class ScratchText(ScratchWrapper): class ScratchText(ScratchWrapper):
DROP_TARGETS = DdTargets.all_text() DROP_TARGETS = DdTargets.all_text()

View File

@ -1139,13 +1139,18 @@ def profile(func, *args):
# keyword, code, translated standard, translated upper # keyword, code, translated standard, translated upper
KEYWORDS = [("title", "t", _("Person|Title"), _("Person|TITLE")), KEYWORDS = [("title", "t", _("Person|Title"), _("Person|TITLE")),
("given", "f", _("Given"), _("GIVEN")), ("given", "f", _("Given"), _("GIVEN")),
("prefix", "p", _("Prefix"), _("PREFIX")), ("surname", "l", _("Surname"), _("SURNAME")),
("surname", "l", _("Surname"), _("SURNAME")), ("call", "c", _("Name|Call"), _("Name|CALL")),
("common", "x", _("Name|Common"), _("Name|COMMON")),
("initials", "i", _("Initials"), _("INITIALS")),
("suffix", "s", _("Suffix"), _("SUFFIX")), ("suffix", "s", _("Suffix"), _("SUFFIX")),
("patronymic","y", _("Patronymic"),_("PATRONYMIC")), ("rawsurnames", "q", _("Rawsurnames"), _("RAWSURNAMES")),
("call", "c", _("Call"), _("CALL")), ("patronymic", "y", _("Patronymic"), _("PATRONYMIC")),
("common", "x", _("Common"), _("COMMON")), ("notpatronymic", "o", _("Notpatronymic"),_("NOTPATRONYMIC")),
("initials", "i", _("Initials"), _("INITIALS")) ("primary", "m", _("Primary"), _("PRIMARY")),
("prefix", "p", _("Prefix"), _("PREFIX")),
("nickname", "n", _("Nickname"), _("NICKNAME")),
("familynick", "g", _("Familynick"), _("FAMILYNICK")),
] ]
KEY_TO_TRANS = {} KEY_TO_TRANS = {}
TRANS_TO_KEY = {} TRANS_TO_KEY = {}

View File

@ -211,7 +211,6 @@ register('interface.note-height', 500)
register('interface.note-sel-height', 450) register('interface.note-sel-height', 450)
register('interface.note-sel-width', 600) register('interface.note-sel-width', 600)
register('interface.note-width', 700) register('interface.note-width', 700)
register('interface.patro-title', 0)
register('interface.pedview-layout', 0) register('interface.pedview-layout', 0)
register('interface.pedview-show-images', True) register('interface.pedview-show-images', True)
register('interface.pedview-show-marriage', False) register('interface.pedview-show-marriage', False)
@ -228,7 +227,6 @@ register('interface.place-height', 450)
register('interface.place-sel-height', 450) register('interface.place-sel-height', 450)
register('interface.place-sel-width', 600) register('interface.place-sel-width', 600)
register('interface.place-width', 650) register('interface.place-width', 650)
register('interface.prefix-suffix', 0)
register('interface.repo-height', 450) register('interface.repo-height', 450)
register('interface.repo-ref-height', 450) register('interface.repo-ref-height', 450)
register('interface.repo-ref-width', 600) register('interface.repo-ref-width', 600)

View File

@ -356,6 +356,13 @@ class DbReadBase(object):
""" """
raise NotImplementedError raise NotImplementedError
def get_origin_types(self):
"""
Return a list of all custom origin types associated with Person/Surname
instances in the database.
"""
raise NotImplementedError
def get_note_bookmarks(self): def get_note_bookmarks(self):
""" """
Return the list of Note handles in the bookmarks. Return the list of Note handles in the bookmarks.

View File

@ -47,7 +47,8 @@ import logging
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
from gen.lib import (MediaObject, Person, Family, Source, Event, Place, from gen.lib import (MediaObject, Person, Family, Source, Event, Place,
Repository, Note, Tag, GenderStats, Researcher) Repository, Note, Tag, GenderStats, Researcher,
NameOriginType)
from gen.db.dbconst import * from gen.db.dbconst import *
from gen.utils.callback import Callback from gen.utils.callback import Callback
from gen.db import (BsddbBaseCursor, DbReadBase) from gen.db import (BsddbBaseCursor, DbReadBase)
@ -68,6 +69,41 @@ _SIGBASE = ('person', 'family', 'source', 'event',
DBERRS = (db.DBRunRecoveryError, db.DBAccessError, DBERRS = (db.DBRunRecoveryError, db.DBAccessError,
db.DBPageNotFoundError, db.DBInvalidArgError) db.DBPageNotFoundError, db.DBInvalidArgError)
#-------------------------------------------------------------------------
#
# Helper functions
#
#-------------------------------------------------------------------------
def find_surname(key, data):
"""
Creating a surname from raw data of a person, to use for sort and index
"""
return __index_surname(data[3][5])
def find_surname_name(key, data):
"""
Creating a surname from raw name, to use for sort and index
"""
return __index_surname(data[5])
def __index_surname(surn_list):
"""
All non pa/matronymic surnames are used in indexing.
pa/matronymic not as they change for every generation!
"""
if surn_list:
surn = " ".join([x[0] for x in surn_list if not (x[3][0] in [
NameOriginType.PATRONYMIC, NameOriginType.MATRONYMIC]) ])
else:
surn = ""
return str(surn)
#-------------------------------------------------------------------------
#
# class DbBookmarks
#
#-------------------------------------------------------------------------
class DbBookmarks(object): class DbBookmarks(object):
def __init__(self, default=[]): def __init__(self, default=[]):
self.bookmarks = list(default) # want a copy (not an alias) self.bookmarks = list(default) # want a copy (not an alias)
@ -269,6 +305,7 @@ class DbBsddbRead(DbReadBase, Callback):
self.family_rel_types = set() self.family_rel_types = set()
self.event_role_names = set() self.event_role_names = set()
self.name_types = set() self.name_types = set()
self.origin_types = set()
self.repository_types = set() self.repository_types = set()
self.note_types = set() self.note_types = set()
self.source_media_types = set() self.source_media_types = set()
@ -1252,6 +1289,13 @@ class DbBsddbRead(DbReadBase, Callback):
""" """
return list(self.name_types) return list(self.name_types)
def get_origin_types(self):
"""
Return a list of all custom origin types assocated with Person/Surname
instances in the database.
"""
return list(self.origin_types)
def get_repository_types(self): def get_repository_types(self):
""" """
Return a list of all custom repository types assocated with Repository Return a list of all custom repository types assocated with Repository
@ -1385,7 +1429,8 @@ class DbBsddbRead(DbReadBase, Callback):
return self.__has_handle(self.tag_map, handle) return self.__has_handle(self.tag_map, handle)
def __sortbyperson_key(self, person): def __sortbyperson_key(self, person):
return locale.strxfrm(self.person_map.get(str(person))[3][5]) return locale.strxfrm(find_surname(str(person),
self.person_map.get(str(person))))
def __sortbyplace(self, first, second): def __sortbyplace(self, first, second):
return locale.strcoll(self.place_map.get(str(first))[2], return locale.strcoll(self.place_map.get(str(first))[2],

View File

@ -21,22 +21,29 @@
# $Id$ # $Id$
from __future__ import with_statement from __future__ import with_statement
from gen.db import BSDDBTxn
from gen.lib.markertype import MarkerType from gen.lib.markertype import MarkerType
from gen.lib.tag import Tag from gen.lib.tag import Tag
import time import time
""" """
upgrade methods to upgrade a database from version 13 to current version
""" """
from bsddb import db
from gen.db import BSDDBTxn
from gen.lib.nameorigintype import NameOriginType
from gen.db.write import _mkname, SURNAMES
def gramps_upgrade_15(self): def gramps_upgrade_15(self):
"""Upgrade database from version 14 to 15.""" """Upgrade database from version 14 to 15. This upgrade adds:
# This upgrade adds tagging * tagging
* surname list
* remove marker
"""
length = (len(self.note_map) + len(self.person_map) + length = (len(self.note_map) + len(self.person_map) +
len(self.event_map) + len(self.family_map) + len(self.event_map) + len(self.family_map) +
len(self.repository_map) + len(self.media_map) + len(self.repository_map) + len(self.media_map) +
len(self.place_map) + len(self.source_map)) len(self.place_map) + len(self.source_map)) + 10
self.set_total(length) self.set_total(length)
self.tags = {} self.tags = {}
@ -46,16 +53,70 @@ def gramps_upgrade_15(self):
# Replace the old marker field with the new tag list field. # Replace the old marker field with the new tag list field.
for handle in self.person_map.keys(): for handle in self.person_map.keys():
person = self.person_map[handle] person = self.person_map[handle]
new_person = list(person)
tag_handle = convert_marker(self, new_person[18]) (junk_handle, # 0
gramps_id, # 1
gender, # 2
primary_name, # 3
alternate_names, # 4
death_ref_index, # 5
birth_ref_index, # 6
event_ref_list, # 7
family_list, # 8
parent_family_list, # 9
media_list, # 10
address_list, # 11
attribute_list, # 12
urls, # 13
ord_list, # 14
psource_list, # 15
pnote_list, # 16
change, # 17
marker, # 18
pprivate, # 19
person_ref_list, # 20
) = person
tag_handle = convert_marker(self, marker)
if tag_handle: if tag_handle:
new_person[18] = [tag_handle] tags = [tag_handle]
else: else:
new_person[18] = [] tags = []
new_person = tuple(new_person) new_primary_name = convert_name_15(primary_name)
new_alternate_names = [convert_name_15(altname) for altname in
alternate_names]
new_person = (junk_handle, # 0
gramps_id, # 1
gender, # 2
new_primary_name, # 3
new_alternate_names,# 4
death_ref_index, # 5
birth_ref_index, # 6
event_ref_list, # 7
family_list, # 8
parent_family_list, # 9
media_list, # 10
address_list, # 11
attribute_list, # 12
urls, # 13
ord_list, # 14
psource_list, # 15
pnote_list, # 16
change, # 17
tags, # 18
pprivate, # 19
person_ref_list # 20
)
with BSDDBTxn(self.env, self.person_map) as txn: with BSDDBTxn(self.env, self.person_map) as txn:
txn.put(str(handle), new_person) txn.put(str(handle), new_person)
self.update() self.update(length)
#surname is now different, remove secondary index with names
_db = db.DB(self.env)
try:
_db.remove(_mkname(self.full_name, SURNAMES), SURNAMES)
except db.DBNoSuchFileError:
pass
# --------------------------------- # ---------------------------------
# Modify Family # Modify Family
@ -182,6 +243,33 @@ def convert_marker(self, marker_field):
else: else:
return None return None
def convert_name_15(name):
(privacy, source_list, note_list, date,
first_name, surname, suffix, title,
name_type, prefix, patronymic,
group_as, sort_as, display_as, call) = name
connector = u""
origintype = (NameOriginType.NONE, u"")
patorigintype = (NameOriginType.PATRONYMIC, u"")
if patronymic.strip() == u"":
#no patronymic, create a single surname
surname_list = [(surname, prefix, True, origintype, connector)]
else:
#a patronymic, if no surname or equal as patronymic, a single surname
if (surname.strip() == u"") or (surname == patronymic and prefix == u""):
surname_list = [(patronymic, prefix, True, patorigintype, connector)]
else:
#two surnames, first patronymic, then surname which is primary
surname_list = [(patronymic, u"", False, patorigintype, u""),
(surname, prefix, True, origintype, connector)]
#return new value, add two empty strings for nick and family nick
return (privacy, source_list, note_list, date,
first_name, surname_list, suffix, title, name_type,
group_as, sort_as, display_as, call, u"", u"")
def gramps_upgrade_14(self): def gramps_upgrade_14(self):
"""Upgrade database from version 13 to 14.""" """Upgrade database from version 13 to 14."""
# This upgrade modifies notes and dates # This upgrade modifies notes and dates

View File

@ -53,7 +53,7 @@ from gen.lib import (GenderStats, Person, Family, Event, Place, Source,
MediaObject, Repository, Note, Tag) MediaObject, Repository, Note, Tag)
from gen.db import (DbBsddbRead, DbWriteBase, BSDDBTxn, from gen.db import (DbBsddbRead, DbWriteBase, BSDDBTxn,
DbTxn, BsddbBaseCursor, DbVersionError, DbTxn, BsddbBaseCursor, DbVersionError,
DbUpgradeRequiredError, DbUpgradeRequiredError, find_surname, find_surname_name,
DbUndoBSDDB as DbUndo) DbUndoBSDDB as DbUndo)
from gen.db.dbconst import * from gen.db.dbconst import *
from gen.utils.callback import Callback from gen.utils.callback import Callback
@ -125,10 +125,7 @@ KEY_TO_CLASS_MAP = {PERSON_KEY: Person.__name__,
# #
# Helper functions # Helper functions
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
def find_surname(key, data):
return str(data[3][5])
def find_idmap(key, data): def find_idmap(key, data):
return str(data[1]) return str(data[1])
@ -557,6 +554,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
self.family_rel_types = set(meta('family_rels')) self.family_rel_types = set(meta('family_rels'))
self.event_role_names = set(meta('event_roles')) self.event_role_names = set(meta('event_roles'))
self.name_types = set(meta('name_types')) self.name_types = set(meta('name_types'))
self.origin_types = set(meta('origin_types'))
self.repository_types = set(meta('repo_types')) self.repository_types = set(meta('repo_types'))
self.note_types = set(meta('note_types')) self.note_types = set(meta('note_types'))
self.source_media_types = set(meta('sm_types')) self.source_media_types = set(meta('sm_types'))
@ -986,6 +984,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
txn.put('family_rels', list(self.family_rel_types)) txn.put('family_rels', list(self.family_rel_types))
txn.put('event_roles', list(self.event_role_names)) txn.put('event_roles', list(self.event_role_names))
txn.put('name_types', list(self.name_types)) txn.put('name_types', list(self.name_types))
txn.put('origin_types', list(self.origin_types))
txn.put('repo_types', list(self.repository_types)) txn.put('repo_types', list(self.repository_types))
txn.put('note_types', list(self.note_types)) txn.put('note_types', list(self.note_types))
txn.put('sm_types', list(self.source_media_types)) txn.put('sm_types', list(self.source_media_types))
@ -1316,7 +1315,8 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
""" """
Build surname list for use in autocompletion Build surname list for use in autocompletion
""" """
self.surname_list = sorted(map(unicode, set(self.surnames.keys())), key=locale.strxfrm) self.surname_list = sorted(map(unicode, set(self.surnames.keys())),
key=locale.strxfrm)
def add_to_surname_list(self, person, batch_transaction): def add_to_surname_list(self, person, batch_transaction):
""" """
@ -1324,7 +1324,8 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
""" """
if batch_transaction: if batch_transaction:
return return
name = unicode(person.get_primary_name().get_surname()) name = unicode(find_surname_name(person.handle,
person.get_primary_name().serialize()))
i = bisect.bisect(self.surname_list, name) i = bisect.bisect(self.surname_list, name)
if 0 < i <= len(self.surname_list): if 0 < i <= len(self.surname_list):
if self.surname_list[i-1] != name: if self.surname_list[i-1] != name:
@ -1341,7 +1342,8 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
If not then we need to remove the name from the list. If not then we need to remove the name from the list.
The function must be overridden in the derived class. The function must be overridden in the derived class.
""" """
name = str(person.get_primary_name().get_surname()) name = str(find_surname_name(person.handle,
person.get_primary_name().serialize()))
try: try:
cursor = self.surnames.cursor(txn=self.txn) cursor = self.surnames.cursor(txn=self.txn)
cursor.set(name) cursor.set(name)
@ -1401,7 +1403,10 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
self.genderStats.count_person(person) self.genderStats.count_person(person)
# Update surname list if necessary # Update surname list if necessary
if (old_person.primary_name.surname !=person.primary_name.surname): if (find_surname_name(old_person.handle,
old_person.primary_name.serialize()) !=
find_surname_name(person.handle,
person.primary_name.serialize())):
self.remove_from_surname_list(old_person) self.remove_from_surname_list(old_person)
self.add_to_surname_list(person, transaction.batch) self.add_to_surname_list(person, transaction.batch)
else: else:
@ -1420,7 +1425,13 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
for name in ([person.primary_name] for name in ([person.primary_name]
+ person.alternate_names) + person.alternate_names)
if name.type.is_custom()]) if name.type.is_custom()])
all_surn = person.primary_name.get_surname_list()
for asurname in person.alternate_names:
all_surn += asurname.get_surname_list()
self.origin_types.update([str(surn.origintype) for surn in all_surn
if surn.origintype.is_custom()])
self.url_types.update([str(url.type) for url in person.urls self.url_types.update([str(url.type) for url in person.urls
if url.type.is_custom()]) if url.type.is_custom()])

View File

@ -23,6 +23,22 @@
""" """
Class handling language-specific displaying of names. Class handling language-specific displaying of names.
Specific symbols for parts of a name are defined:
't' : title
'f' : given (first names)
'l' : full surname (lastname)
'c' : callname
'x' : callname if existing, otherwise first first name (common name)
'i' : initials of the first names
'y' : patronymic surname (father)
'o' : surnames without patronymic
'm' : primary surname (main)
'p' : list of all prefixes
'q' : surnames without prefixes and connectors
's' : suffix
'n' : nick name
'g' : family nick name
""" """
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
@ -38,7 +54,7 @@ import re
# GRAMPS modules # GRAMPS modules
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
from gen.lib import Name from gen.lib import Name, NameOriginType
try: try:
import config import config
@ -52,17 +68,23 @@ except ImportError:
# Constants # Constants
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
_FIRSTNAME = 4 _FIRSTNAME = 4
_SURNAME = 5 _SURNAME_LIST = 5
_SUFFIX = 6 _SUFFIX = 6
_TITLE = 7 _TITLE = 7
_TYPE = 8 _TYPE = 8
_PREFIX = 9 _GROUP = 9
_PATRONYM = 10 _SORT = 10
_GROUP = 11 _DISPLAY = 11
_SORT = 12 _CALL = 12
_DISPLAY = 13 _NICK = 13
_CALL = 14 _FAMNICK = 14
_SURNAME_IN_LIST = 0
_PREFIX_IN_LIST = 1
_PRIMARY_IN_LIST = 2
_TYPE_IN_LIST = 3
_CONNECTOR_IN_LIST = 4
_ORIGINPATRO = NameOriginType.PATRONYMIC
_ACT = True _ACT = True
_INA = False _INA = False
@ -79,7 +101,11 @@ _F_RAWFN = 4 # name format raw function
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# Because of occurring in an exec(), this couldn't be in a lambda: # Because of occurring in an exec(), this couldn't be in a lambda:
def _make_cmp(a, b): return -cmp(a[1], b[1]) # we sort names first on longest first, then last letter first, this to
# avoid translations of shorter terms which appear in longer ones, eg
# namelast may not be mistaken with name, so namelast must first be
# converted to %k before name is converted.
def _make_cmp(a, b): return -cmp((len(a[1]),a[1]), (len(b[1]), b[1]))
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# #
@ -97,6 +123,63 @@ class NameDisplayError(Exception):
def __str__(self): def __str__(self):
return self.value return self.value
#-------------------------------------------------------------------------
#
# Functions to extract data from raw lists (unserialized objects)
#
#-------------------------------------------------------------------------
def _raw_full_surname(raw_surn_data_list):
"""method for the 'l' symbol: full surnames"""
result = ""
for raw_surn_data in raw_surn_data_list:
result += "%s %s %s " % (raw_surn_data[_PREFIX_IN_LIST],
raw_surn_data[_SURNAME_IN_LIST],
raw_surn_data[_CONNECTOR_IN_LIST])
return ' '.join(result.split()).strip()
def _raw_primary_surname(raw_surn_data_list):
"""method for the 'm' symbol: primary surname"""
for raw_surn_data in raw_surn_data_list:
if raw_surn_data[_PRIMARY_IN_LIST]:
result = "%s %s" % (raw_surn_data[_PREFIX_IN_LIST],
raw_surn_data[_SURNAME_IN_LIST])
return ' '.join(result.split())
return ''
def _raw_patro_surname(raw_surn_data_list):
"""method for the 'y' symbol: patronymic surname"""
for raw_surn_data in raw_surn_data_list:
if raw_surn_data[_TYPE_IN_LIST][0] == _ORIGINPATRO:
result = "%s %s" % (raw_surn_data[_PREFIX_IN_LIST],
raw_surn_data[_SURNAME_IN_LIST])
return ' '.join(result.split())
return ''
def _raw_nonpatro_surname(raw_surn_data_list):
"""method for the 'o' symbol: full surnames without patronymic"""
result = ""
for raw_surn_data in raw_surn_data_list:
if raw_surn_data[_TYPE_IN_LIST][0] != _ORIGINPATRO:
result += "%s %s %s " % (raw_surn_data[_PREFIX_IN_LIST],
raw_surn_data[_SURNAME_IN_LIST],
raw_surn_data[_CONNECTOR_IN_LIST])
return ' '.join(result.split()).strip()
def _raw_prefix_surname(raw_surn_data_list):
"""method for the 'p' symbol: all prefixes"""
result = ""
for raw_surn_data in raw_surn_data_list:
result += "%s " % (raw_surn_data[_PREFIX_IN_LIST])
return ' '.join(result.split()).strip()
def _raw_single_surname(raw_surn_data_list):
"""method for the 'q' symbol: surnames without prefix and connectors"""
result = ""
for raw_surn_data in raw_surn_data_list:
result += "%s " % (raw_surn_data[_SURNAME_IN_LIST])
return ' '.join(result.split()).strip()
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# #
# NameDisplay class # NameDisplay class
@ -112,10 +195,11 @@ class NameDisplay(object):
STANDARD_FORMATS = [ STANDARD_FORMATS = [
(Name.DEF,_("Default format (defined by Gramps preferences)"),'',_ACT), (Name.DEF,_("Default format (defined by Gramps preferences)"),'',_ACT),
(Name.LNFN,_("Surname, Given Patronymic"),'%p %l, %f %y %s',_ACT), (Name.LNFN,_("Surname, Given"),'%l, %f %s',_ACT),
(Name.FNLN,_("Given Surname"),'%f %y %p %l %s',_ACT), (Name.FN,_("Given"),'%f',_ACT),
(Name.PTFN,_("Patronymic, Given"),'%p %y, %s %f',_ACT), (Name.FNLN,_("Given Surname"),'%f %l %s',_ACT),
(Name.FN,_("Given"),'%f',_ACT) # DEPRECATED FORMATS
(Name.PTFN,_("Patronymic, Given"),'%y, %s %f',_INA),
] ]
def __init__(self): def __init__(self):
@ -125,11 +209,12 @@ class NameDisplay(object):
if WITH_GRAMPS_CONFIG: if WITH_GRAMPS_CONFIG:
self.default_format = config.get('preferences.name-format') self.default_format = config.get('preferences.name-format')
if self.default_format == 0: if self.default_format == 0 \
or self.default_format not in Name.NAMEFORMATS :
self.default_format = Name.LNFN self.default_format = Name.LNFN
config.set('preferences.name-format', self.default_format) config.set('preferences.name-format', self.default_format)
else: else:
self.default_format = 1 self.default_format = Name.LNFN
self.set_default_format(self.default_format) self.set_default_format(self.default_format)
@ -138,28 +223,17 @@ class NameDisplay(object):
def _format_raw_fn(self, fmt_str): def _format_raw_fn(self, fmt_str):
return lambda x: self.format_str_raw(x, fmt_str) return lambda x: self.format_str_raw(x, fmt_str)
def _raw_lnfn(self, raw_data): def _raw_lnfn(self, raw_data):
result = "%s %s, %s %s %s" % (raw_data[_PREFIX], result = "%s, %s %s" % (_raw_full_surname(raw_data[_SURNAME_LIST]),
raw_data[_SURNAME], raw_data[_FIRSTNAME],
raw_data[_FIRSTNAME], raw_data[_SUFFIX])
raw_data[_PATRONYM],
raw_data[_SUFFIX])
return ' '.join(result.split()) return ' '.join(result.split())
def _raw_fnln(self, raw_data): def _raw_fnln(self, raw_data):
result = "%s %s %s %s %s" % (raw_data[_FIRSTNAME], result = "%s %s %s" % (raw_data[_FIRSTNAME],
raw_data[_PATRONYM], _raw_full_surname(raw_data[_SURNAME_LIST]),
raw_data[_PREFIX], raw_data[_SUFFIX])
raw_data[_SURNAME],
raw_data[_SUFFIX])
return ' '.join(result.split())
def _raw_ptfn(self, raw_data):
result = "%s %s, %s %s" % (raw_data[_PREFIX],
raw_data[_PATRONYM],
raw_data[_SUFFIX],
raw_data[_FIRSTNAME])
return ' '.join(result.split()) return ' '.join(result.split())
def _raw_fn(self, raw_data): def _raw_fn(self, raw_data):
@ -170,7 +244,6 @@ class NameDisplay(object):
raw_func_dict = { raw_func_dict = {
Name.LNFN : self._raw_lnfn, Name.LNFN : self._raw_lnfn,
Name.FNLN : self._raw_fnln, Name.FNLN : self._raw_fnln,
Name.PTFN : self._raw_ptfn,
Name.FN : self._raw_fn, Name.FN : self._raw_fn,
} }
@ -279,31 +352,65 @@ class NameDisplay(object):
The new function is of the form: The new function is of the form:
def fn(raw_data): def fn(raw_data):
return "%s %s %s %s %s" % (raw_data[_TITLE], return "%s %s %s" % (raw_data[_TITLE],
raw_data[_FIRSTNAME], raw_data[_FIRSTNAME],
raw_data[_PREFIX],
raw_data[_SURNAME],
raw_data[_SUFFIX]) raw_data[_SUFFIX])
Specific symbols for parts of a name are defined (keywords given):
't' : title = title
'f' : given = given (first names)
'l' : surname = full surname (lastname)
'c' : call = callname
'x' : common = callname if existing, otherwise first first name (common name)
'i' : initials = initials of the first names
'y' : patronymic = patronymic surname (father)
'o' : notpatronymic = surnames without patronymic
'm' : primary = primary surname (main)
'p' : prefix = list of all prefixes
'q' : rawsurnames = surnames without prefixes and connectors
's' : suffix = suffix
'n' : nickname = nick name
'g' : familynick = family nick name
""" """
# we need the names of each of the variables or methods that are # we need the names of each of the variables or methods that are
# called to fill in each format flag. # called to fill in each format flag.
# Dictionary is "code": ("expression", "keyword", "i18n-keyword") # Dictionary is "code": ("expression", "keyword", "i18n-keyword")
d = {"t": ("raw_data[_TITLE]", "title", _("Person|title")), d = {"t": ("raw_data[_TITLE]", "title",
"f": ("raw_data[_FIRSTNAME]", "given", _("given")), _("Person|title")),
"p": ("raw_data[_PREFIX]", "prefix", _("prefix")), "f": ("raw_data[_FIRSTNAME]", "given",
"l": ("raw_data[_SURNAME]", "surname", _("surname")), _("given")),
"s": ("raw_data[_SUFFIX]", "suffix", _("suffix")), "l": ("_raw_full_surname(raw_data[_SURNAME_LIST])", "surname",
"y": ("raw_data[_PATRONYM]", "patronymic", _("patronymic")), _("surname")),
"c": ("raw_data[_CALL]", "call", _("call")), "s": ("raw_data[_SUFFIX]", "suffix",
_("suffix")),
"c": ("raw_data[_CALL]", "call",
_("Name|call")),
"x": ("(raw_data[_CALL] or raw_data[_FIRSTNAME].split(' ')[0])", "x": ("(raw_data[_CALL] or raw_data[_FIRSTNAME].split(' ')[0])",
"common", "common",
_("common")), _("Name|common")),
"i": ("''.join([word[0] +'.' for word in ('. ' +" + "i": ("''.join([word[0] +'.' for word in ('. ' +" +
" raw_data[_FIRSTNAME]).split()][1:])", " raw_data[_FIRSTNAME]).split()][1:])",
"initials", "initials",
_("initials")) _("initials")),
"y": ("_raw_patro_surname(raw_data[_SURNAME_LIST])", "patronymic",
_("patronymic")),
"o": ("_raw_nonpatro_surname(raw_data[_SURNAME_LIST])", "notpatronymic",
_("notpatronymic")),
"m": ("_raw_primary_surname(raw_data[_SURNAME_LIST])",
"primary",
_("Name|primary")),
"p": ("_raw_prefix_surname(raw_data[_SURNAME_LIST])",
"prefix",
_("prefix")),
"q": ("_raw_single_surname(raw_data[_SURNAME_LIST])",
"rawsurnames",
_("rawsurnames")),
"n": ("raw_data[_NICK]", "nickname",
_("nickname")),
"g": ("raw_data[_FAMNICK]", "familynick",
_("familynick")),
} }
args = "raw_data" args = "raw_data"
return self._make_fn(format_str, d, args) return self._make_fn(format_str, d, args)
@ -321,26 +428,61 @@ class NameDisplay(object):
The new function is of the form: The new function is of the form:
def fn(first,surname,prefix,suffix,patronymic,title,call,): def fn(first, raw_surname_list, suffix, title, call,):
return "%s %s %s %s %s" % (first,surname,prefix,suffix,patronymic) return "%s %s" % (first,suffix)
Specific symbols for parts of a name are defined (keywords given):
't' : title = title
'f' : given = given (first names)
'l' : surname = full surname (lastname)
'c' : call = callname
'x' : common = callname if existing, otherwise first first name (common name)
'i' : initials = initials of the first names
'y' : patronymic = patronymic surname (father)
'o' : notpatronymic = surnames without patronymic
'm' : primary = primary surname (main)
'p' : prefix = list of all prefixes
'q' : rawsurnames = surnames without prefixes and connectors
's' : suffix = suffix
'n' : nickname = nick name
'g' : familynick = family nick name
""" """
# we need the names of each of the variables or methods that are # we need the names of each of the variables or methods that are
# called to fill in each format flag. # called to fill in each format flag.
# Dictionary is "code": ("expression", "keyword", "i18n-keyword") # Dictionary is "code": ("expression", "keyword", "i18n-keyword")
d = {"t": ("title", "title", _("Person|title")), d = {"t": ("title", "title",
"f": ("first", "given", _("given")), _("Person|title")),
"p": ("prefix", "prefix", _("prefix")), "f": ("first", "given",
"l": ("surname", "surname", _("surname")), _("given")),
"s": ("suffix", "suffix", _("suffix")), "l": ("_raw_full_surname(raw_surname_list)", "surname",
"y": ("patronymic", "patronymic", _("patronymic")), _("surname")),
"c": ("call", "call", _("call")), "s": ("suffix", "suffix",
"x": ("(call or first.split(' ')[0])", "common", _("common")), _("suffix")),
"c": ("call", "call",
_("Name|call")),
"x": ("(call or first.split(' ')[0])", "common",
_("Name|common")),
"i": ("''.join([word[0] +'.' for word in ('. ' + first).split()][1:])", "i": ("''.join([word[0] +'.' for word in ('. ' + first).split()][1:])",
"initials", _("initials")) "initials",
_("initials")),
"y": ("_raw_patro_surname(raw_surname_list)", "patronymic",
_("patronymic")),
"o": ("_raw_nonpatro_surname(raw_surname_list)", "notpatronymic",
_("notpatronymic")),
"m": ("_raw_primary_surname(raw_surname_list)", "primary",
_("Name|primary")),
"p": ("_raw_prefix_surname(raw_surname_list)", "prefix",
_("prefix")),
"q": ("_raw_single_surname(raw_surname_list)", "rawsurnames",
_("rawsurnames")),
"n": ("nick", "nickname",
_("nickname")),
"g": ("famnick", "familynick",
_("familynick")),
} }
args = "first,surname,prefix,suffix,patronymic,title,call" args = "first,raw_surname_list,suffix,title,call,nick,famnick"
return self._make_fn(format_str, d, args) return self._make_fn(format_str, d, args)
def _make_fn(self, format_str, d, args): def _make_fn(self, format_str, d, args):
@ -360,7 +502,7 @@ class NameDisplay(object):
pass pass
else: else:
d_keys = [(code, _tuple[2]) for code, _tuple in d.iteritems()] d_keys = [(code, _tuple[2]) for code, _tuple in d.iteritems()]
d_keys.sort(_make_cmp) # reverse sort by ikeyword d_keys.sort(_make_cmp) # reverse on length and by ikeyword
for (code, ikeyword) in d_keys: for (code, ikeyword) in d_keys:
exp, keyword, ikeyword = d[code] exp, keyword, ikeyword = d[code]
#ikeyword = unicode(ikeyword, "utf8") #ikeyword = unicode(ikeyword, "utf8")
@ -376,7 +518,7 @@ class NameDisplay(object):
pass pass
else: else:
d_keys = [(code, _tuple[1]) for code, _tuple in d.iteritems()] d_keys = [(code, _tuple[1]) for code, _tuple in d.iteritems()]
d_keys.sort(_make_cmp) # reverse sort by keyword d_keys.sort(_make_cmp) # reverse sort on length and by keyword
# if in double quotes, just use % codes # if in double quotes, just use % codes
for (code, keyword) in d_keys: for (code, keyword) in d_keys:
exp, keyword, ikeyword = d[code] exp, keyword, ikeyword = d[code]
@ -410,7 +552,7 @@ class NameDisplay(object):
# find each format flag in the original format string # find each format flag in the original format string
# for each one we find the variable name that is needed to # for each one we find the variable name that is needed to
# replace it and add this to a list. This list will be used # replace it and add this to a list. This list will be used to
# generate the replacement tuple. # generate the replacement tuple.
# This compiled pattern should match all of the format codes. # This compiled pattern should match all of the format codes.
@ -442,9 +584,10 @@ def fn(%s):
return fn return fn
def format_str(self, name, format_str): def format_str(self, name, format_str):
return self._format_str_base(name.first_name, name.surname, name.prefix, return self._format_str_base(name.first_name, name.surname_list,
name.suffix, name.patronymic, name.title, name.suffix, name.title,
name.call,format_str) name.call, name.nick, name.famnick,
format_str)
def format_str_raw(self, raw_data, format_str): def format_str_raw(self, raw_data, format_str):
""" """
@ -463,22 +606,27 @@ def fn(%s):
return ' '.join(s.split()) return ' '.join(s.split())
def _format_str_base(self, first, surname, prefix, suffix, patronymic, def _format_str_base(self, first, surname_list, suffix, title, call,
title, call, format_str): nick, famnick, format_str):
""" """
Generates name from a format string. Generates name from a format string.
The following substitutions are made: The following substitutions are made:
%t -> title '%t' : title
%f -> given (first name) '%f' : given (first names)
%p -> prefix '%l' : full surname (lastname)
%s -> suffix '%c' : callname
%l -> surname (last name) '%x' : callname if existing, otherwise first first name (common name)
%y -> patronymic '%i' : initials of the first names
%c -> call '%y' : patronymic surname (father)
%x -> common '%o' : surnames without patronymic
%i -> initials '%m' : primary surname (main)
The capital letters are substituted for capitalized name components. '%p' : list of all prefixes
'%q' : surnames without prefixes and connectors
'%s' : suffix
'%n' : nick name
'%g' : family nick name
The capital letters are substituted for capitalized name components.
The %% is substituted with the single % character. The %% is substituted with the single % character.
All the other characters in the fmt_str are unaffected. All the other characters in the fmt_str are unaffected.
""" """
@ -487,7 +635,8 @@ def fn(%s):
func = self._gen_cooked_func(format_str) func = self._gen_cooked_func(format_str)
self.__class__.format_funcs[format_str] = func self.__class__.format_funcs[format_str] = func
try: try:
s = func(first,surname,prefix,suffix,patronymic,title,call) s = func(first, [surn.serialize() for surn in surname_list],
suffix, title, call, nick, famnick)
except (ValueError, TypeError,): except (ValueError, TypeError,):
raise NameDisplayError, "Incomplete format string" raise NameDisplayError, "Incomplete format string"
@ -496,7 +645,8 @@ def fn(%s):
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
def sort_string(self, name): def sort_string(self, name):
return u"%-25s%-30s%s" % (name.surname, name.first_name, name.suffix) return u"%-25s%-30s%s" % (name.get_primary_surname, name.first_name,
name.suffix)
def sorted(self, person): def sorted(self, person):
""" """
@ -573,7 +723,7 @@ def fn(%s):
def display_formal(self, person): def display_formal(self, person):
""" """
Return a text string representing the L{gen.lib.Person} instance's Return a text string representing the L{gen.lib.Person} instance's
L{Name} in a manner that should be used for normal displaying. L{Name} in a manner that should be used for formal displaying.
@param person: L{gen.lib.Person} instance that contains the @param person: L{gen.lib.Person} instance that contains the
L{Name} that is to be displayed. The primary name is used for L{Name} that is to be displayed. The primary name is used for
@ -603,7 +753,7 @@ def fn(%s):
return self.name_formats[num][_F_FN](name) return self.name_formats[num][_F_FN](name)
def display_given(self, person): def display_given(self, person):
return self.format_str(person.get_primary_name(),'%f %y') return self.format_str(person.get_primary_name(),'%f')
def name_grouping(self, db, person): def name_grouping(self, db, person):
return self.name_grouping_name(db, person.primary_name) return self.name_grouping_name(db, person.primary_name)
@ -612,26 +762,29 @@ def fn(%s):
if pn.group_as: if pn.group_as:
return pn.group_as return pn.group_as
sv = pn.sort_as sv = pn.sort_as
if sv == Name.LNFN or sv == Name.DEF: if sv == Name.DEF:
return db.get_name_group_mapping(pn.surname) return db.get_name_group_mapping(pn.get_primary_surname())
elif sv == Name.PTFN: elif sv == Name.LNFN:
return db.get_name_group_mapping(pn.patronymic) return db.get_name_group_mapping(pn.get_surname())
elif sv == Name.FN: elif sv == Name.FN:
return db.get_name_group_mapping(pn.first_name) return db.get_name_group_mapping(pn.first_name)
else: else:
return db.get_name_group_mapping(pn.surname) return db.get_name_group_mapping(pn.get_primary_surname())
def name_grouping_data(self, db, pn): def name_grouping_data(self, db, pn):
if pn[_GROUP]: if pn[_GROUP]:
return pn[_GROUP] return pn[_GROUP]
sv = pn[_SORT] sv = pn[_SORT]
if sv == Name.LNFN or sv == Name.DEF: if sv == Name.DEF:
return db.get_name_group_mapping(pn[_SURNAME]) return db.get_name_group_mapping(_raw_primary_surname(
elif sv == Name.PTFN: pn[_SURNAME_LIST]))
return db.get_name_group_mapping(pn[_PATRONYM]) elif sv == Name.LNFN:
return db.get_name_group_mapping(_raw_full_surname(
pn[_SURNAME_LIST]))
elif sv == Name.FN: elif sv == Name.FN:
return db.get_name_group_mapping(pn[_FIRSTNAME]) return db.get_name_group_mapping(pn[_FIRSTNAME])
else: else:
return db.get_name_group_mapping(pn[_SURNAME]) return db.get_name_group_mapping(_raw_primary_surname(
pn[_SURNAME_LIST]))
displayer = NameDisplay() displayer = NameDisplay()

View File

@ -37,6 +37,7 @@ pkgdata_PYTHON = \
mediaref.py \ mediaref.py \
name.py \ name.py \
nametype.py \ nametype.py \
nameorigintype.py \
notebase.py \ notebase.py \
note.py \ note.py \
notetype.py \ notetype.py \
@ -58,6 +59,8 @@ pkgdata_PYTHON = \
srcnote.py \ srcnote.py \
src.py \ src.py \
srcref.py \ srcref.py \
surname.py \
surnamebase.py \
styledtext.py \ styledtext.py \
styledtexttag.py \ styledtexttag.py \
styledtexttagtype.py \ styledtexttagtype.py \

View File

@ -36,6 +36,7 @@ from gen.lib.mediaref import MediaRef
from gen.lib.name import Name from gen.lib.name import Name
from gen.lib.reporef import RepoRef from gen.lib.reporef import RepoRef
from gen.lib.srcref import SourceRef from gen.lib.srcref import SourceRef
from gen.lib.surname import Surname
from gen.lib.url import Url from gen.lib.url import Url
from gen.lib.witness import Witness from gen.lib.witness import Witness
from gen.lib.childref import ChildRef from gen.lib.childref import ChildRef
@ -71,6 +72,7 @@ from gen.lib.familyreltype import FamilyRelType
from gen.lib.srcmediatype import SourceMediaType from gen.lib.srcmediatype import SourceMediaType
from gen.lib.eventroletype import EventRoleType from gen.lib.eventroletype import EventRoleType
from gen.lib.markertype import MarkerType from gen.lib.markertype import MarkerType
from gen.lib.nameorigintype import NameOriginType
from gen.lib.notetype import NoteType from gen.lib.notetype import NoteType
from gen.lib.styledtexttagtype import StyledTextTagType from gen.lib.styledtexttagtype import StyledTextTagType

View File

@ -35,6 +35,7 @@ from gen.lib.privacybase import PrivacyBase
from gen.lib.srcbase import SourceBase from gen.lib.srcbase import SourceBase
from gen.lib.notebase import NoteBase from gen.lib.notebase import NoteBase
from gen.lib.datebase import DateBase from gen.lib.datebase import DateBase
from gen.lib.surnamebase import SurnameBase
from gen.lib.nametype import NameType from gen.lib.nametype import NameType
from gen.lib.const import IDENTICAL, EQUAL, DIFFERENT from gen.lib.const import IDENTICAL, EQUAL, DIFFERENT
@ -43,18 +44,23 @@ from gen.lib.const import IDENTICAL, EQUAL, DIFFERENT
# Personal Name # Personal Name
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
class Name(SecondaryObject, PrivacyBase, SourceBase, NoteBase, DateBase): class Name(SecondaryObject, PrivacyBase, SurnameBase, SourceBase, NoteBase,
DateBase):
""" """
Provide name information about a person. Provide name information about a person.
A person may have more that one name throughout his or her life. A person may have more that one name throughout his or her life. The Name
object stores one of them
""" """
DEF = 0 # Default format (determined by gramps-wide prefs) DEF = 0 # Default format (determined by gramps-wide prefs)
LNFN = 1 # last name first name [patronymic] LNFN = 1 # last name first name
FNLN = 2 # first name last name FNLN = 2 # first name last name
PTFN = 3 # patronymic first name
FN = 4 # first name FN = 4 # first name
NAMEFORMATS = (DEF, LNFN, FNLN, FN)
#deprecated :
PTFN = 3 # patronymic first name
def __init__(self, source=None, data=None): def __init__(self, source=None, data=None):
"""Create a new Name instance, copying from the source if provided. """Create a new Name instance, copying from the source if provided.
@ -65,43 +71,43 @@ class Name(SecondaryObject, PrivacyBase, SourceBase, NoteBase, DateBase):
saved differently. saved differently.
""" """
PrivacyBase.__init__(self, source) PrivacyBase.__init__(self, source)
SurnameBase.__init__(self, source)
SourceBase.__init__(self, source) SourceBase.__init__(self, source)
NoteBase.__init__(self, source) NoteBase.__init__(self, source)
DateBase.__init__(self, source) DateBase.__init__(self, source)
if data: if data:
(privacy, source_list, note, date, (privacy, source_list, note, date,
self.first_name, self.surname, self.suffix, self.title, self.first_name, surname_list, self.suffix, self.title, name_type,
name_type, self.prefix, self.patronymic, self.group_as, self.sort_as, self.display_as, self.call,
self.group_as, self.sort_as, self.display_as, self.call) = data self.nick, self.famnick) = data
self.type = NameType(name_type) self.type = NameType(name_type)
SurnameBase.unserialize(self, surname_list)
PrivacyBase.unserialize(self, privacy) PrivacyBase.unserialize(self, privacy)
SourceBase.unserialize(self, source_list) SourceBase.unserialize(self, source_list)
NoteBase.unserialize(self, note) NoteBase.unserialize(self, note)
DateBase.unserialize(self, date) DateBase.unserialize(self, date)
elif source: elif source:
self.first_name = source.first_name self.first_name = source.first_name
self.surname = source.surname
self.suffix = source.suffix self.suffix = source.suffix
self.title = source.title self.title = source.title
self.type = source.type self.type = source.type
self.prefix = source.prefix
self.patronymic = source.patronymic
self.group_as = source.group_as self.group_as = source.group_as
self.sort_as = source.sort_as self.sort_as = source.sort_as
self.display_as = source.display_as self.display_as = source.display_as
self.call = source.call self.call = source.call
self.nick = source.nick
self.famnick = source.famnick
else: else:
self.first_name = "" self.first_name = ""
self.surname = ""
self.suffix = "" self.suffix = ""
self.title = "" self.title = ""
self.type = NameType() self.type = NameType()
self.prefix = ""
self.patronymic = ""
self.group_as = "" self.group_as = ""
self.sort_as = self.DEF self.sort_as = self.DEF
self.display_as = self.DEF self.display_as = self.DEF
self.call = u'' self.call = u''
self.nick = u''
self.famnick = u''
def serialize(self): def serialize(self):
""" """
@ -111,28 +117,35 @@ class Name(SecondaryObject, PrivacyBase, SourceBase, NoteBase, DateBase):
SourceBase.serialize(self), SourceBase.serialize(self),
NoteBase.serialize(self), NoteBase.serialize(self),
DateBase.serialize(self), DateBase.serialize(self),
self.first_name, self.surname, self.suffix, self.title, self.first_name,
self.type.serialize(), self.prefix, self.patronymic, SurnameBase.serialize(self),
self.group_as, self.sort_as, self.display_as, self.call) self.suffix, self.title,
self.type.serialize(),
self.group_as, self.sort_as, self.display_as, self.call,
self.nick, self.famnick)
def is_empty(self): def is_empty(self):
""" """
Indicate if the name is empty. Indicate if the name is empty.
""" """
return (self.first_name == u"" and self.surname == u"" and namefieldsempty = (self.first_name == u"" and
self.suffix == u"" and self.title == u"" and self.suffix == u"" and self.title == u"" and self.nick ==u""
self.prefix == u"" and self.patronymic == u"") and self.famnick == u"")
surnamefieldsempty = not (False in
[surn.is_empty() for surn in self.surname_list])
return namefieldsempty and surnamefieldsempty
def unserialize(self, data): def unserialize(self, data):
""" """
Convert a serialized tuple of data to an object. Convert a serialized tuple of data to an object.
""" """
(privacy, source_list, note_list, date, (privacy, source_list, note_list, date,
self.first_name, self.surname, self.suffix, self.title, self.first_name, surname_list, self.suffix, self.title, name_type,
name_type, self.prefix, self.patronymic, self.group_as, self.sort_as, self.display_as, self.call,
self.group_as, self.sort_as, self.display_as, self.call) = data self.nick, self.famnick) = data
self.type = NameType(name_type) self.type = NameType(name_type)
PrivacyBase.unserialize(self, privacy) PrivacyBase.unserialize(self, privacy)
SurnameBase.unserialize(self, surname_list)
SourceBase.unserialize(self, source_list) SourceBase.unserialize(self, source_list)
NoteBase.unserialize(self, note_list) NoteBase.unserialize(self, note_list)
DateBase.unserialize(self, date) DateBase.unserialize(self, date)
@ -145,8 +158,8 @@ class Name(SecondaryObject, PrivacyBase, SourceBase, NoteBase, DateBase):
:returns: Returns the list of all textual attributes of the object. :returns: Returns the list of all textual attributes of the object.
:rtype: list :rtype: list
""" """
return [self.first_name, self.surname, self.suffix, self.title, return [self.first_name, self.suffix, self.title,
str(self.type), self.prefix, self.patronymic, self.call] str(self.type), self.call, self.nick, self.famnick]
def get_text_data_child_list(self): def get_text_data_child_list(self):
""" """
@ -155,7 +168,7 @@ class Name(SecondaryObject, PrivacyBase, SourceBase, NoteBase, DateBase):
:returns: Returns the list of child objects that may carry textual data. :returns: Returns the list of child objects that may carry textual data.
:rtype: list :rtype: list
""" """
return self.source_list return self.source_list + self.surname_list
def get_note_child_list(self): def get_note_child_list(self):
""" """
@ -189,8 +202,8 @@ class Name(SecondaryObject, PrivacyBase, SourceBase, NoteBase, DateBase):
def is_equivalent(self, other): def is_equivalent(self, other):
""" """
Return if this name is equivalent, that is agrees in type, first Return if this name is equivalent, that is agrees in type, first,
call, last, suffix, patronymic, title and date, to other. call, surname_list, suffix, title and date, to other.
:param other: The name to compare this name to. :param other: The name to compare this name to.
:rtype other: Name :rtype other: Name
@ -199,7 +212,8 @@ class Name(SecondaryObject, PrivacyBase, SourceBase, NoteBase, DateBase):
""" """
# TODO what to do with sort and display? # TODO what to do with sort and display?
if self.get_text_data_list() != other.get_text_data_list() or \ if self.get_text_data_list() != other.get_text_data_list() or \
self.get_date_object() != other.get_date_object(): self.get_date_object() != other.get_date_object() or \
SurnameBase.serialize(self) != SurnameBase.serialize(other):
return DIFFERENT return DIFFERENT
else: else:
if self.is_equal(other): if self.is_equal(other):
@ -210,8 +224,10 @@ class Name(SecondaryObject, PrivacyBase, SourceBase, NoteBase, DateBase):
def merge(self, acquisition): def merge(self, acquisition):
""" """
Merge the content of acquisition into this name. Merge the content of acquisition into this name.
Normally the person merge code should opt for adding an alternate
name if names are actually different (like not equal surname list)
Lost: type, first, call, last, suffix, patronymic, title and date of Lost: type, first, call, suffix, title, nick, famnick and date of
acquisition. acquisition.
:param acquisition: The name to merge with the present name. :param acquisition: The name to merge with the present name.
@ -219,6 +235,7 @@ class Name(SecondaryObject, PrivacyBase, SourceBase, NoteBase, DateBase):
""" """
# TODO what to do with sort and display? # TODO what to do with sort and display?
self._merge_privacy(acquisition) self._merge_privacy(acquisition)
self._merge_surname_list(acquisition)
self._merge_note_list(acquisition) self._merge_note_list(acquisition)
self._merge_source_reference_list(acquisition) self._merge_source_reference_list(acquisition)
@ -249,7 +266,7 @@ class Name(SecondaryObject, PrivacyBase, SourceBase, NoteBase, DateBase):
if self.group_as: if self.group_as:
return self.group_as return self.group_as
else: else:
return self.surname return self.get_primary_surname().get_surname()
def set_sort_as(self, value): def set_sort_as(self, value):
""" """
@ -305,22 +322,42 @@ class Name(SecondaryObject, PrivacyBase, SourceBase, NoteBase, DateBase):
""" """
self.call = val self.call = val
def get_surname_prefix(self): def get_nick_name(self):
""" """
Return the prefix (or article) of a surname. Return the nick name.
The prefix is not used for sorting or grouping. The nick name of the person, a not official name the person is known
with.
""" """
return self.prefix return self.nick
def set_surname_prefix(self, val): def set_nick_name(self, val):
""" """
Set the prefix (or article) of a surname. Set the nick name.
Examples of articles would be 'de' or 'van'. The nick name of the person, a not official name the person is known
with.
""" """
self.prefix = val self.nick = val
def get_family_nick_name(self):
"""
Return the family nick name.
The family nick name of the family of the person, a not official name
use to denote the entire family.
"""
return self.famnick
def set_family_nick_name(self, val):
"""
Set the family nick name.
The family nick name of the family of the person, a not official name
use to denote the entire family.
"""
self.famnick = val
def set_type(self, the_type): def set_type(self, the_type):
"""Set the type of the Name instance.""" """Set the type of the Name instance."""
self.type.set(the_type) self.type.set(the_type)
@ -333,33 +370,13 @@ class Name(SecondaryObject, PrivacyBase, SourceBase, NoteBase, DateBase):
"""Set the given name for the Name instance.""" """Set the given name for the Name instance."""
self.first_name = name self.first_name = name
def set_patronymic(self, name):
"""Set the patronymic name for the Name instance."""
self.patronymic = name
def set_surname(self, name):
"""Set the surname (or last name) for the Name instance."""
self.surname = name
def set_suffix(self, name):
"""Set the suffix (such as Jr., III, etc.) for the Name instance."""
self.suffix = name
def get_first_name(self): def get_first_name(self):
"""Return the given name for the Name instance.""" """Return the given name for the Name instance."""
return self.first_name return self.first_name
def get_patronymic(self): def set_suffix(self, name):
"""Return the patronymic name for the Name instance.""" """Set the suffix (such as Jr., III, etc.) for the Name instance."""
return self.patronymic self.suffix = name
def get_surname(self):
"""Return the surname (or last name) for the Name instance."""
return self.surname
def get_upper_surname(self):
"""Return the surname (or last name) for the Name instance."""
return self.surname.upper()
def get_suffix(self): def get_suffix(self):
"""Return the suffix for the Name instance.""" """Return the suffix for the Name instance."""
@ -376,85 +393,55 @@ class Name(SecondaryObject, PrivacyBase, SourceBase, NoteBase, DateBase):
def get_name(self): def get_name(self):
""" """
Return a name string built from the components of the Name instance, Return a name string built from the components of the Name instance,
in the form of surname, Firstname. in the form of: surname, Firstname.
""" """
first = self.first_name
if self.patronymic: surname = self.get_surname()
first = "%s %s" % (self.first_name, self.patronymic)
else:
first = self.first_name
if self.suffix: if self.suffix:
if self.prefix: return "%s, %s %s" % (surname, first, self.suffix)
return "%s %s, %s %s" % (self.prefix, self.surname,
first, self.suffix)
else:
return "%s, %s %s" % (self.surname, first, self.suffix)
else: else:
if self.prefix: return "%s, %s" % (surname, first)
return "%s %s, %s" % (self.prefix, self.surname, first)
else:
return "%s, %s" % (self.surname, first)
def get_upper_name(self): def get_upper_name(self):
""" """
Return a name string built from the components of the Name instance, Return a name string built from the components of the Name instance,
in the form of surname, Firstname. in the form of SURNAME, Firstname.
""" """
first = self.first_name
if self.patronymic: surname = self.get_surname().upper()
first = "%s %s" % (self.first_name, self.patronymic)
else:
first = self.first_name
if self.suffix: if self.suffix:
if self.prefix: return "%s, %s %s" % (surname, first, self.suffix)
return "%s %s, %s %s" % (self.prefix.upper(),
self.surname.upper(), first,
self.suffix)
else:
return "%s, %s %s" % (self.surname.upper(), first, self.suffix)
else: else:
if self.prefix: return "%s, %s" % (surname, first)
return "%s %s, %s" % (self.prefix.upper(),
self.surname.upper(),
first)
else:
return "%s, %s" % (self.surname.upper(), first)
def get_regular_name(self): def get_regular_name(self):
""" """
Return a name string built from the components of the Name instance, Return a name string built from the components of the Name instance,
in the form of Firstname surname. in the form of Firstname surname.
""" """
if self.patronymic: first = self.first_name
first = "%s %s" % (self.first_name, self.patronymic) surname = self.get_surname()
else:
first = self.first_name
if (self.suffix == ""): if (self.suffix == ""):
if self.prefix: return "%s %s" % (first, surname)
return "%s %s %s" % (first, self.prefix, self.surname)
else:
return "%s %s" % (first, self.surname)
else: else:
if self.prefix: return "%s %s, %s" % (first, surname, self.suffix)
return "%s %s %s, %s" % (first, self.prefix, self.surname,
self.suffix)
else:
return "%s %s, %s" % (first, self.surname, self.suffix)
def get_gedcom_parts(self): def get_gedcom_parts(self):
""" """
Returns a GEDCOM-formatted name dictionary. Returns a GEDCOM-formatted name dictionary.
Note, field patronymic and prefix are deprecated, prefix_list and
surname list, added.
""" """
retval = {} retval = {}
retval['given'] = self.first_name.strip() retval['given'] = self.first_name.strip()
retval['patronymic'] = self.patronymic.strip() retval['surname'] = self.get_surname().replace('/', '?')
if retval['patronymic']:
retval['given'] = "%s %s" % (retval['given'],
retval['patronymic'])
retval['surname'] = self.surname.replace('/', '?')
retval['prefix'] = self.prefix.replace('/', '?')
retval['suffix'] = self.suffix retval['suffix'] = self.suffix
retval['title'] = self.title retval['title'] = self.title
retval['surnamelist'] = self.get_surnames()
retval['prefixes'] = self.get_prefixes()
retval['connectors'] = self.get_connectors()
retval['nick'] = self.nick
retval['famnick'] = self.famnick
return retval return retval
def get_gedcom_name(self): def get_gedcom_name(self):
@ -462,21 +449,10 @@ class Name(SecondaryObject, PrivacyBase, SourceBase, NoteBase, DateBase):
Returns a GEDCOM-formatted name. Returns a GEDCOM-formatted name.
""" """
firstname = self.first_name.strip() firstname = self.first_name.strip()
patron = self.patronymic.strip() surname = self.get_surname().replace('/', '?')
if patron:
firstname = "%s %s" % (firstname, patron)
surname = self.surname.replace('/', '?')
surprefix = self.prefix.replace('/', '?')
suffix = self.suffix suffix = self.suffix
title = self.title title = self.title
if suffix == "": if suffix == "":
if surprefix == "": return '%s /%s/' % (firstname, surname)
return '%s /%s/' % (firstname, surname)
else:
return '%s /%s %s/' % (firstname, surprefix, surname)
elif surprefix == "":
return '%s /%s/ %s' % (firstname, surname, suffix)
else: else:
return '%s /%s %s/ %s' % (firstname, surprefix, surname, suffix) return '%s /%s/ %s' % (firstname, surname, suffix)

View File

@ -0,0 +1,87 @@
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2010 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$
"""
Name types.
"""
#-------------------------------------------------------------------------
#
# Python modules
#
#-------------------------------------------------------------------------
from gen.ggettext import sgettext as _
#-------------------------------------------------------------------------
#
# GRAMPS modules
#
#-------------------------------------------------------------------------
from gen.lib.grampstype import GrampsType
class NameOriginType(GrampsType):
"""
Name Origina Types
.. attribute UNKNOWN: Unknown origin
.. attribute CUSTOM: Custom user defined origin
.. attribute NONE: no given origin
.. attribute INHERITED: name was inherited from parents
.. attribute GIVEN: name was bestowed on the individual
.. attribute TAKEN: name was chosen by the individual
.. attribute PATRONYMIC: name is derived from father's given name
.. attribute MATRONYMIC: name is derived from mother's given name
.. attribute FEUDAL: name refers to the holding of land in a fief
.. attribute PSEUDONYM: name is fictitious
"""
UNKNOWN = -1
CUSTOM = 0
NONE = 1
INHERITED = 2
GIVEN = 3
TAKEN = 4
PATRONYMIC = 5
MATRONYMIC = 6
FEUDAL = 7
PSEUDONYM = 8
_CUSTOM = CUSTOM
_DEFAULT = NONE
_DATAMAP = [
(UNKNOWN , _("Unknown"), "Unknown"),
(CUSTOM , _("Custom"), "Custom"),
(NONE , "", ""),
(INHERITED , _("Surname|Inherited"), "Inherited"),
(GIVEN , _("Surname|Given"), "Given"),
(TAKEN , _("Surname|Taken"), "Taken"),
(PATRONYMIC, _("Patronymic"), "Patronymic"),
(MATRONYMIC, _("Matronymic"), "Matronymic"),
(FEUDAL , _("Surname|Feudal"), "Feudal"),
(PSEUDONYM , _("Pseudonym"), "Pseudonym"),
]
def __init__(self, value=None):
GrampsType.__init__(self, value)

196
src/gen/lib/surname.py Normal file
View File

@ -0,0 +1,196 @@
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2010 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$
"""
Surname class for GRAMPS.
"""
#-------------------------------------------------------------------------
#
# GRAMPS modules
#
#-------------------------------------------------------------------------
from gen.lib.secondaryobj import SecondaryObject
from gen.lib.nameorigintype import NameOriginType
from gen.lib.const import IDENTICAL, EQUAL, DIFFERENT
#-------------------------------------------------------------------------
#
# Personal Name
#
#-------------------------------------------------------------------------
class Surname(SecondaryObject):
"""
Provide surname information of a name.
A person may have more that one surname in his name
"""
def __init__(self, source=None, data=None):
"""Create a new Surname instance, copying from the source if provided.
By default a surname is created as primary, use set_primary to change
"""
if source:
self.surname = source.surname
self.prefix = source.prefix
self.primary = source.primary
self.origintype = source.origintype
self.connector = source.connector
else:
self.surname = ""
self.prefix = ""
self.primary = True
self.origintype = NameOriginType()
self.connector = ""
if data:
self.unserialize(data)
def serialize(self):
"""
Convert the object to a serialized tuple of data.
"""
return (self.surname, self.prefix, self.primary,
self.origintype.serialize(), self.connector)
def is_empty(self):
"""
Indicate if the surname is empty.
"""
return (self.surname == u"" and self.prefix == u"" and
self.connector == u"")
def unserialize(self, data):
"""
Convert a serialized tuple of data to an object.
"""
(self.surname, self.prefix, self.primary, origin_type,
self.connector) = data
self.origintype = NameOriginType(origin_type)
return self
def get_text_data_list(self):
"""
Return the list of all textual attributes of the object.
:returns: Returns the list of all textual attributes of the object.
:rtype: list
"""
return [self.surname, self.prefix, self.connector,
str(self.origintype)]
def is_equivalent(self, other):
"""
Return if this surname is equivalent, that is agrees in type, surname,
..., to other.
:param other: The surname to compare this name to.
:rtype other: Surame
:returns: Constant indicating degree of equivalence.
:rtype: int
"""
# TODO what to do with sort and display?
if self.get_text_data_list() != other.get_text_data_list() or \
self.primary != other.primary:
return DIFFERENT
else:
if self.is_equal(other):
return IDENTICAL
else:
return EQUAL
def merge(self, acquisition):
"""
Merge the content of acquisition into this surname.
Lost: primary, surname, prefix, connector, origintype
:param acquisition: The surname to merge with the present surname.
:rtype acquisition: Surname
"""
pass
def get_surname(self):
"""
Return the surname.
The surname is one of the not given names coming from the parents
"""
return self.surname
def set_surname(self, val):
"""
Set the surname.
The surname is one of the not given names coming from the parents
"""
self.surname = val
def get_prefix(self):
"""
Return the prefix (or article) of the surname.
The prefix is not used for sorting or grouping.
"""
return self.prefix
def set_prefix(self, val):
"""
Set the prefix (or article) of the surname.
Examples of articles would be 'de' or 'van'.
"""
self.prefix = val
def set_origintype(self, the_type):
"""Set the origin type of the Surname instance."""
self.origintype.set(the_type)
def get_origintype(self):
"""Return the origin type of the Surname instance."""
return self.origintype
def set_connector(self, connector):
"""Set the connector for the Surname instance. This defines how a
surname connects to the next surname (eg in Spanish names).
"""
self.connector = connector
def get_connector(self):
"""Get the connector for the Surname instance. This defines how a
surname connects to the next surname (eg in Spanish names).
"""
return self.connector
def get_primary(self):
"""Return if this surname is the primary surname"""
return self.primary
def set_primary(self, primary=True):
"""Set if this surname is the primary surname.replace
Use :class:`~gen.lib.surname.SurnameBase` to set the primary surname
via :method:`~gen.lib.surname.SurnameBase.set_primary_surname`
:param primary: primay surname or not
:type primary: bool
"""
self.primary = primary

230
src/gen/lib/surnamebase.py Normal file
View File

@ -0,0 +1,230 @@
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2010 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$
"""
SurnameBase class for GRAMPS.
"""
from gen.ggettext import gettext as _
#-------------------------------------------------------------------------
#
# GRAMPS modules
#
#-------------------------------------------------------------------------
from gen.lib.surname import Surname
from gen.lib.const import IDENTICAL, EQUAL
#-------------------------------------------------------------------------
#
# SurnameBase classes
#
#-------------------------------------------------------------------------
class SurnameBase(object):
"""
Base class for surname-aware objects.
"""
def __init__(self, source=None):
"""
Initialize a SurnameBase.
If the source is not None, then object is initialized from values of
the source object.
:param source: Object used to initialize the new object
:type source: SurnameBase
"""
self.surname_list = map(Surname, source.surname_list) if source else []
def serialize(self):
"""
Convert the object to a serialized tuple of data.
"""
return [surname.serialize() for surname in self.surname_list]
def unserialize(self, data):
"""
Convert a serialized tuple of data to an object.
"""
self.surname_list = [Surname().unserialize(item) for item in data]
def add_surname(self, surname):
"""
Add the :class:`~gen.lib.surname.Surname` instance to the object's
list of surnames.
:param surname: :class:`~gen.lib.surname.Surname` instance to add to
the object's address list.
:type address: list
"""
self.surname_list.append(surname)
def remove_surname(self, surname):
"""
Remove the specified :class:`~gen.lib.surname.Surname` instance from
the surname list.
If the instance does not exist in the list, the operation has
no effect.
:param surname: :class:`~gen.lib.surname.Surname` instance to remove
from the list
:type surname: :class:`~gen.lib.surname.Surname`
:returns: True if the surname was removed, False if it was not in the list.
:rtype: bool
"""
if surname in self.surname_list:
self.surname_list.remove(surname)
return True
else:
return False
def get_surname_list(self):
"""
Return the list of :class:`~gen.lib.surname.Surname` instances a
ssociated with the object.
:returns: Returns the list of :class:`~gen.lib.surname.Surname` instances
:rtype: list
"""
return self.surname_list
def set_surname_list(self, surname_list):
"""
Assign the passed list to the object's list of
:class:`~gen.lib.surname.Surname` instances.
:param surname_list: List of :class:`~gen.lib.surname.surname` instances
to be associated with the object
:type surname_list: list
"""
self.surname_list = surname_list
def get_primary_surname(self):
"""
Return the string of the surname that is the primary surname
:returns: Returns the surname instance that
is the primary surname. If primary not set, and there is a surname,
the first surname is given, if no surnames, None is returned
:rtype: :class:`~gen.lib.surname.Surname` or None
"""
for surname in self.surname_list:
if surname.primary:
return surname
if self.surname_list:
return self.surname_list[0]
return None
def set_primary_surname(self, surnamenr=0):
"""
Set the surname with surnamenr in the surname list as primary surname
Counting starts at 0
"""
if surnamenr >= len(self.surname_list):
return
for surname in self.surname_list:
surname.set_primary(False)
self.surname_list[surnamenr].set_primary(True)
def _merge_surname_list(self, acquisition):
"""
Merge the list of surname from acquisition with our own.
This method is normally only called when surnames are equal, if they
are different, the merge code should fall back to storing an
alternate name. For completeness, the code is present nevertheless.
:param acquisition: the surname list of this object will be merged with
the current surname list.
:rtype acquisition: SurnameBase
"""
surname_list = self.surname_list[:]
for addendum in acquisition.get_surname_list():
for surname in surname_list:
equi = surname.is_equivalent(addendum)
if equi == IDENTICAL:
break
elif equi == EQUAL:
#This should normally never happen, an alternate name
# should be added
surname.merge(addendum)
break
else:
self.surname_list.append(addendum)
def get_surname(self):
"""
Return a fully formatted surname utilizing the surname_list
"""
totalsurn = ""
for surn in self.surname_list:
partsurn = surn.get_surname()
if surn.get_prefix():
fsurn = _('%(first)s %(second)s') % {'first': surn.get_prefix(),
'second': partsurn}
else:
fsurn = partsurn
fsurn = fsurn.strip()
if surn.get_connector():
fsurn = _('%(first)s %(second)s') % {'first': fsurn,
'second': surn.get_connector()}
fsurn = fsurn.strip()
totalsurn = _('%(first)s %(second)s') % {'first': totalsurn,
'second': fsurn}
return totalsurn.strip()
def get_upper_surname(self):
"""Return a fully formatted surname capitalized"""
return self.get_surname().upper()
def get_surnames(self):
"""
Return a list of surnames (no prefix or connectors)
"""
surnl = []
for surn in self.surname_list:
realsurn = surn.get_surname()
if realsurn:
surnl.append(realsurn)
def get_prefixes(self):
"""
Return a list of prefixes
"""
prefixl = []
for surn in self.surname_list:
prefix = surn.get_prefix()
if prefix:
prefixl.append(prefix)
def get_connectors(self):
"""
Return a list of surnames (no prefix or connectors)
"""
connl = []
for surn in self.surname_list:
conn = surn.get_connector()
if conn:
connl.append(conn)

View File

@ -623,6 +623,11 @@ class ProxyDbBase(DbReadBase):
instances in the database""" instances in the database"""
return self.db.get_name_types() return self.db.get_name_types()
def get_origin_types(self):
"""returns a list of all custom origin types associated with Person/Surname
instances in the database"""
return self.db.get_origin_types()
def get_repository_types(self): def get_repository_types(self):
"""returns a list of all custom repository types associated with """returns a list of all custom repository types associated with
Repository instances in the database""" Repository instances in the database"""

View File

@ -12,275 +12,331 @@
<child> <child>
<object class="GtkVBox" id="vbox33"> <object class="GtkVBox" id="vbox33">
<property name="visible">True</property> <property name="visible">True</property>
<property name="orientation">vertical</property>
<child> <child>
<object class="GtkTable" id="table65"> <object class="GtkVBox" id="vbox2">
<property name="visible">True</property> <property name="visible">True</property>
<property name="border_width">12</property>
<property name="n_rows">4</property>
<property name="n_columns">5</property>
<property name="column_spacing">12</property>
<property name="row_spacing">6</property>
<child> <child>
<object class="GtkLabel" id="label592"> <object class="GtkHBox" id="hbox110">
<property name="visible">True</property> <property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">_Given:</property>
<property name="use_underline">True</property>
<property name="justify">center</property>
<property name="mnemonic_widget">alt_given</property>
</object>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label597">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">P_atronymic:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">patronymic</property>
</object>
<packing>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="UndoableEntry" id="patronymic">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">&#x25CF;</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label598">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">_Family:</property>
<property name="use_underline">True</property>
<property name="justify">center</property>
<property name="mnemonic_widget">alt_surname</property>
</object>
<packing>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="UndoableEntry" id="alt_surname">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">&#x25CF;</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label596">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">_Prefix:</property>
<property name="use_underline">True</property>
<property name="justify">center</property>
<property name="mnemonic_widget">alt_prefix</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label595">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Tit_le:</property>
<property name="use_underline">True</property>
<property name="justify">center</property>
<property name="mnemonic_widget">alt_title</property>
</object>
<packing>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="UndoableEntry" id="alt_title">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">&#x25CF;</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label593">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Suffi_x:</property>
<property name="use_underline">True</property>
<property name="justify">center</property>
<property name="mnemonic_widget">alt_suffix</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label594">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="yalign">0</property>
<property name="xpad">1</property>
<property name="label" translatable="yes">_Type:</property>
<property name="use_underline">True</property>
<property name="justify">center</property>
<property name="mnemonic_widget">name_type</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="UndoableEntry" id="alt_prefix">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">&#x25CF;</property>
</object>
<packing>
<property name="left_attach">3</property>
<property name="right_attach">5</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="GtkComboBoxEntry" id="name_type">
<property name="visible">True</property>
</object>
<packing>
<property name="left_attach">3</property>
<property name="right_attach">5</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="UndoableEntry" id="alt_suffix">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">&#x25CF;</property>
</object>
<packing>
<property name="left_attach">3</property>
<property name="right_attach">4</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="GtkToggleButton" id="priv">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="relief">none</property>
<child> <child>
<object class="GtkImage" id="image2683"> <object class="GtkLabel" id="label269">
<property name="visible">True</property> <property name="visible">True</property>
<property name="icon_name">gtk-dialog-authentication</property> <property name="xalign">0</property>
<property name="icon-size">1</property> <property name="xpad">4</property>
<property name="label" translatable="yes">_Type:</property>
<property name="use_underline">True</property>
<property name="justify">center</property>
<property name="mnemonic_widget">ntype</property>
</object>
<packing>
<property name="expand">False</property>
<property name="padding">8</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkComboBoxEntry" id="ntype">
<property name="visible">True</property>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child>
<object class="GtkToggleButton" id="priv">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="relief">none</property>
<child>
<object class="GtkImage" id="image2683">
<property name="visible">True</property>
<property name="icon_name">gtk-dialog-authentication</property>
<property name="icon-size">1</property>
</object>
</child>
</object>
<packing>
<property name="pack_type">end</property>
<property name="position">6</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="padding">3</property>
<property name="position">0</property>
</packing>
</child>
<child>
<placeholder/>
</child>
<child>
<object class="GtkFrame" id="givenframe">
<property name="visible">True</property>
<property name="label_xalign">0.029999999329447746</property>
<child>
<object class="GtkAlignment" id="alignment2">
<property name="visible">True</property>
<property name="left_padding">12</property>
<child>
<object class="GtkTable" id="table2">
<property name="visible">True</property>
<property name="n_rows">2</property>
<property name="n_columns">6</property>
<property name="column_spacing">7</property>
<property name="row_spacing">7</property>
<child>
<object class="GtkLabel" id="label21">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">_Given:</property>
<property name="use_underline">True</property>
<property name="justify">center</property>
<property name="mnemonic_widget">given_name</property>
</object>
<packing>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="GtkLabel" id="titlelabel">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">T_itle:</property>
<property name="use_underline">True</property>
</object>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label4">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Suffi_x:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">suffix</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label444">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">C_all Name:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">call</property>
</object>
<packing>
<property name="left_attach">4</property>
<property name="right_attach">5</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="UndoableEntry" id="given_name">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="has_default">True</property>
<property name="invisible_char">&#x25CF;</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">4</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label6">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">_Nick Name:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">nickname</property>
</object>
<packing>
<property name="left_attach">4</property>
<property name="right_attach">5</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="ValidatableMaskedEntry" id="call">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">&#x25CF;</property>
</object>
<packing>
<property name="left_attach">5</property>
<property name="right_attach">6</property>
</packing>
</child>
<child>
<object class="UndoableEntry" id="title_field">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">&#x25CF;</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<object class="UndoableEntry" id="suffix">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">&#x25CF;</property>
</object>
<packing>
<property name="left_attach">3</property>
<property name="right_attach">4</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
<child>
<object class="UndoableEntry" id="nickname">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">&#x25CF;</property>
</object>
<packing>
<property name="left_attach">5</property>
<property name="right_attach">6</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
</packing>
</child>
</object>
</child>
</object>
</child>
<child type="label">
<object class="GtkLabel" id="label3">
<property name="visible">True</property>
<property name="label" translatable="yes">&lt;i&gt;Given Name(s) &lt;/i&gt;</property>
<property name="use_markup">True</property>
</object> </object>
</child> </child>
</object> </object>
<packing> <packing>
<property name="left_attach">4</property> <property name="expand">False</property>
<property name="right_attach">5</property> <property name="fill">False</property>
<property name="top_attach">2</property> <property name="padding">3</property>
<property name="bottom_attach">3</property> <property name="position">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing> </packing>
</child> </child>
<child> <child>
<object class="UndoableEntry" id="alt_given"> <object class="GtkFrame" id="multsurnamefr">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="label_xalign">0.029999999329447746</property>
<property name="invisible_char">&#x25CF;</property> <property name="shadow_type">none</property>
<child>
<object class="GtkAlignment" id="alignment3">
<property name="visible">True</property>
<property name="left_padding">12</property>
<child>
<object class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<child>
<object class="GtkHBox" id="hboxmultsurnames">
<property name="visible">True</property>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
</object>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox1">
<property name="visible">True</property>
<child>
<object class="GtkLabel" id="label7">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">_Family Nick Name:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">familynickname</property>
</object>
<packing>
<property name="expand">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="UndoableEntry" id="familynickname">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">&#x25CF;</property>
<property name="width_chars">25</property>
</object>
<packing>
<property name="expand">False</property>
<property name="padding">3</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="padding">3</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
</child>
<child type="label">
<object class="GtkLabel" id="label5">
<property name="visible">True</property>
<property name="label" translatable="yes">&lt;i&gt;Family Names &lt;/i&gt;</property>
<property name="use_markup">True</property>
</object>
</child>
</object> </object>
<packing> <packing>
<property name="left_attach">1</property> <property name="expand">False</property>
<property name="right_attach">2</property> <property name="fill">False</property>
<property name="top_attach">1</property> <property name="padding">2</property>
<property name="bottom_attach">2</property> <property name="position">3</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label654">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Call _Name:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">call</property>
</object>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="UndoableEntry" id="call">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="invisible_char">&#x25CF;</property>
</object>
<packing>
<property name="left_attach">3</property>
<property name="right_attach">5</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="y_options"></property>
</packing> </packing>
</child> </child>
</object> </object>
@ -288,6 +344,12 @@
<property name="position">0</property> <property name="position">0</property>
</packing> </packing>
</child> </child>
<child>
<placeholder/>
</child>
<child>
<placeholder/>
</child>
<child> <child>
<object class="GtkNotebook" id="notebook"> <object class="GtkNotebook" id="notebook">
<property name="visible">True</property> <property name="visible">True</property>
@ -367,7 +429,6 @@
<child> <child>
<object class="GtkComboBox" id="display_as"> <object class="GtkComboBox" id="display_as">
<property name="visible">True</property> <property name="visible">True</property>
<property name="model">liststore2</property>
<child> <child>
<object class="GtkCellRendererText" id="cellrenderertext2"/> <object class="GtkCellRendererText" id="cellrenderertext2"/>
<attributes> <attributes>
@ -405,7 +466,6 @@
<child> <child>
<object class="GtkComboBox" id="sort_as"> <object class="GtkComboBox" id="sort_as">
<property name="visible">True</property> <property name="visible">True</property>
<property name="model">liststore1</property>
<child> <child>
<object class="GtkCellRendererText" id="cellrenderertext1"/> <object class="GtkCellRendererText" id="cellrenderertext1"/>
<attributes> <attributes>
@ -555,7 +615,7 @@
</child> </child>
</object> </object>
<packing> <packing>
<property name="position">1</property> <property name="position">3</property>
</packing> </packing>
</child> </child>
</object> </object>
@ -591,7 +651,6 @@
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="can_default">True</property> <property name="can_default">True</property>
<property name="has_default">True</property>
<property name="receives_default">True</property> <property name="receives_default">True</property>
<property name="has_tooltip">True</property> <property name="has_tooltip">True</property>
<property name="tooltip_markup">Accept changes and close window</property> <property name="tooltip_markup">Accept changes and close window</property>
@ -634,16 +693,4 @@
<action-widget response="-11">button131</action-widget> <action-widget response="-11">button131</action-widget>
</action-widgets> </action-widgets>
</object> </object>
<object class="GtkListStore" id="liststore1">
<columns>
<!-- column-name item -->
<column type="gchararray"/>
</columns>
</object>
<object class="GtkListStore" id="liststore2">
<columns>
<!-- column-name item -->
<column type="gchararray"/>
</columns>
</object>
</interface> </interface>

File diff suppressed because it is too large Load Diff

View File

@ -53,7 +53,7 @@ from gen.display.name import displayer as _nd
from gen.display.name import NameDisplayError from gen.display.name import NameDisplayError
import Utils import Utils
import gen.lib import gen.lib
from gen.lib import Name from gen.lib import Name, Surname, NameOriginType
import ManagedWindow import ManagedWindow
from gui.widgets import MarkupLabel, BasicLabel from gui.widgets import MarkupLabel, BasicLabel
from QuestionDialog import ErrorDialog, QuestionDialog2, OkDialog from QuestionDialog import ErrorDialog, QuestionDialog2, OkDialog
@ -98,21 +98,28 @@ class DisplayNameEditor(ManagedWindow.ManagedWindow):
table = self.dialog._build_custom_name_ui() table = self.dialog._build_custom_name_ui()
label = gtk.Label(_("""The following keywords will be replaced with the name: label = gtk.Label(_("""The following keywords will be replaced with the name:
<tt> <tt>
<b>Given</b> - given name (first name) <b>Given</b> - given name (first name) | <b>Surname</b> - surnames (with prefix and connectors)
<b>Surname</b> - surname (last name) <b>Title</b> - title (Dr., Mrs.) | <b>Suffix</b> - suffix (Jr., Sr.)
<b>Title</b> - title (Dr., Mrs.) <b>Call</b> - call name | <b>Nickname</b> - nick name
<b>Prefix</b> - prefix (von, de, de la) <b>Initials</b> - first letters of Given | <b>Common</b> - Call, otherwise first of Given
<b>Suffix</b> - suffix (Jr., Sr.) <b>Primary</b> - primary surname (main) | <b>Familynick</b> - Family nick name
<b>Call</b> - call name, or nickname Also:
<b>Common</b> - call name, otherwise first part of Given <b>Patronymic</b> - patronymic surname (father's name)
<b>Patronymic</b> - patronymic (father's name) <b>Notpatronymic</b> - all surnames except patronymic
<b>Initials</b> - persons's first letters of given names <b>Prefix</b> - all surnames prefixes (von, de, de la)
<b>Rawsurnames</b> - all surnames without prefixes and connectors
</tt> </tt>
Use the same keyword in UPPERCASE to force to upper. Parentheses and commas Use the same keyword in UPPERCASE to force to upper. Parentheses and commas
will be removed around empty fields. Other text will appear literally.""")) will be removed around empty fields. Other text will appear literally.
<b>Example fictituous name</b>: 'Dr. Edwin Jose von der Smith and Weston Wilson Sr ("Ed") - Underhills'
Here <i>Edwin Jose</i> are given names, <i>Smith</i> and <i>Weston</i> surnames, <i>Wilson</i> patronymic surname,
<i>Dr.</i> a title, <i>Sr</i> a suffix, <i>Ed</i> the nick name, <i>Underhills</i> family nick name.
Callname is <i>Jose</i>.
"""))
label.set_use_markup(True) label.set_use_markup(True)
self.window.vbox.add(label) self.window.vbox.pack_start(label, expand=False)
self.window.vbox.add(table) self.window.vbox.pack_start(table)
self.window.set_default_size(600, 550) self.window.set_default_size(600, 550)
self.window.connect('response', self.close) self.window.connect('response', self.close)
self.show() self.show()
@ -485,7 +492,7 @@ class GrampsPreferences(ConfigureDialog):
gobject.TYPE_STRING, gobject.TYPE_STRING,
gobject.TYPE_STRING, gobject.TYPE_STRING,
gobject.TYPE_STRING) gobject.TYPE_STRING)
index = 0 index = 0
the_index = 0 the_index = 0
for num, name, fmt_str, act in _nd.get_name_format(): for num, name, fmt_str, act in _nd.get_name_format():
translation = fmt_str translation = fmt_str
@ -503,40 +510,49 @@ class GrampsPreferences(ConfigureDialog):
lyst = ["%s, %s %s (%s)" % (_("Surname"), _("Given"), _("Suffix"), lyst = ["%s, %s %s (%s)" % (_("Surname"), _("Given"), _("Suffix"),
_("Common")), _("Common")),
"%s, %s %s (%s)" % (_("Surname"), _("Given"), _("Suffix"), "%s, %s %s (%s)" % (_("Surname"), _("Given"), _("Suffix"),
_("Call")), _("Nickname")),
"%s, %s %s (%s)" % (_("Surname"), _("Name|Common"), _("Suffix"),
_("Nickname")),
"%s, %s %s" % (_("Surname"), _("Name|Common"), _("Suffix")),
"%s, %s %s (%s)" % (_("SURNAME"), _("Given"), _("Suffix"), "%s, %s %s (%s)" % (_("SURNAME"), _("Given"), _("Suffix"),
_("Call")), _("Call")),
"%s, %s (%s)" % (_("Surname"), _("Given"), _("Common")), "%s, %s (%s)" % (_("Surname"), _("Given"), _("Name|Common")),
"%s, %s (%s)" % (_("Surname"), _("Given"), _("Call")), "%s, %s (%s)" % (_("Surname"), _("Name|Common"), _("Nickname")),
"%s %s" % (_("Given"), _("Surname")), "%s %s" % (_("Given"), _("Surname")),
"%s %s, %s" % (_("Given"), _("Surname"), _("Suffix")), "%s %s, %s" % (_("Given"), _("Surname"), _("Suffix")),
"%s %s %s" % (_("Given"), _("Surname"), _("Patronymic")), "%s %s %s" % (_("Given"), _("NotPatronymic"), _("Patronymic")),
"%s, %s %s (%s)" % (_("SURNAME"), _("Given"), _("Suffix"), "%s, %s %s (%s)" % (_("SURNAME"), _("Given"), _("Suffix"),
_("Common")), _("Common")),
"%s, %s (%s)" % (_("SURNAME"), _("Given"), _("Common")), "%s, %s (%s)" % (_("SURNAME"), _("Given"), _("Name|Common")),
"%s, %s (%s)" % (_("SURNAME"), _("Given"), _("Call")), "%s, %s (%s)" % (_("SURNAME"), _("Given"), _("Nickname")),
"%s %s" % (_("Given"), _("SURNAME")), "%s %s" % (_("Given"), _("SURNAME")),
"%s %s, %s" % (_("Given"), _("SURNAME"), _("Suffix")), "%s %s, %s" % (_("Given"), _("SURNAME"), _("Suffix")),
"%s /%s/" % (_("Given"), _("SURNAME")), "%s /%s/" % (_("Given"), _("SURNAME")),
"%s %s, %s" % (_("Given"), _("Rawsurnames"), _("Suffix")),
] ]
fmtlyst = ["%s, %s %s (%s)" % ("Surname", "Given", "Suffix", #repeat above list, but not translated.
"Common"), fmtlyst = ["%s, %s %s (%s)" % (("Surname"), ("Given"), ("Suffix"),
"%s, %s %s (%s)" % ("Surname", "Given", "Suffix", ("Common")),
"Call"), "%s, %s %s (%s)" % (("Surname"), ("Given"), ("Suffix"),
"%s, %s %s (%s)" % ("SURNAME", "Given", "Suffix", ("Nickname")),
"Call"), "%s, %s %s (%s)" % (("Surname"), ("Name|Common"), ("Suffix"),
"%s, %s (%s)" % ("Surname", "Given", "Common"), ("Nickname")),
"%s, %s (%s)" % ("Surname", "Given", "Call"), "%s, %s %s" % (("Surname"), ("Name|Common"), ("Suffix")),
"%s %s" % ("Given", "Surname"), "%s, %s %s (%s)" % (("SURNAME"), ("Given"), ("Suffix"),
"%s %s, %s" % ("Given", "Surname", "Suffix"), ("Call")),
"%s %s %s" % ("Given", "Surname", "Patronymic"), "%s, %s (%s)" % (("Surname"), ("Given"), ("Name|Common")),
"%s, %s %s (%s)" % ("SURNAME", "Given", "Suffix", "%s, %s (%s)" % (("Surname"), ("Name|Common"), ("Nickname")),
"Common"), "%s %s" % (("Given"), ("Surname")),
"%s, %s (%s)" % ("SURNAME", "Given", "Common"), "%s %s, %s" % (("Given"), ("Surname"), ("Suffix")),
"%s, %s (%s)" % ("SURNAME", "Given", "Call"), "%s %s %s" % (("Given"), ("NotPatronymic"), ("Patronymic")),
"%s %s" % ("Given", "SURNAME"), "%s, %s %s (%s)" % (("SURNAME"), ("Given"), ("Suffix"),
"%s %s, %s" % ("Given", "SURNAME", "Suffix"), ("Common")),
"%s /%s/" % ("Given", "SURNAME"), "%s, %s (%s)" % (("SURNAME"), ("Given"), ("Name|Common")),
"%s, %s (%s)" % (("SURNAME"), ("Given"), ("Nickname")),
"%s %s" % (("Given"), ("SURNAME")),
"%s %s, %s" % (("Given"), ("SURNAME"), ("Suffix")),
"%s /%s/" % (("Given"), ("SURNAME")),
"%s %s, %s" % (("Given"), ("Rawsurnames"), ("Suffix")),
] ]
rand = int(random.random() * len(lyst)) rand = int(random.random() * len(lyst))
f = lyst[rand] f = lyst[rand]
@ -687,11 +703,9 @@ class GrampsPreferences(ConfigureDialog):
self.insert_button = gtk.Button(stock=gtk.STOCK_ADD) self.insert_button = gtk.Button(stock=gtk.STOCK_ADD)
self.insert_button.connect('clicked', self.__new_name) self.insert_button.connect('clicked', self.__new_name)
#self.cb_insert_fmt_str)
self.edit_button = gtk.Button(stock=gtk.STOCK_EDIT) self.edit_button = gtk.Button(stock=gtk.STOCK_EDIT)
self.edit_button.connect('clicked', self.__edit_name) self.edit_button.connect('clicked', self.__edit_name)
#self.cb_edit_fmt_str)
self.edit_button.set_sensitive(False) self.edit_button.set_sensitive(False)
self.remove_button = gtk.Button(stock=gtk.STOCK_REMOVE) self.remove_button = gtk.Button(stock=gtk.STOCK_REMOVE)
@ -746,44 +760,6 @@ class GrampsPreferences(ConfigureDialog):
self.edit_button.set_sensitive(idx) self.edit_button.set_sensitive(idx)
self.name_renderer.set_property('editable', idx) self.name_renderer.set_property('editable', idx)
def cb_edit_fmt_str(self, obj):
"""
Name format editor Edit button callback
"""
num, name, fmt = self.selected_fmt[COL_NUM:COL_EXPL]
dlg = NameFormatEditDlg(name, fmt, self.examplename)
dlg.dlg.set_transient_for(self.window)
(res, name, fmt) = dlg.run()
if res == gtk.RESPONSE_OK and (name != self.selected_fmt[COL_NAME] or
fmt != self.selected_fmt[COL_FMT]):
exmpl = _nd.format_str(self.examplename, fmt)
self.fmt_model.set(self.iter, COL_NAME, name,
COL_FMT, fmt,
COL_EXPL, exmpl)
self.selected_fmt = (num, name, fmt, exmpl)
_nd.edit_name_format(num, name, fmt)
self.dbstate.db.name_formats = _nd.get_name_format(only_custom=True,
only_active=False)
def cb_insert_fmt_str(self, obj):
"""
Name format editor Insert button callback
"""
dlg = NameFormatEditDlg('', '', self.examplename)
dlg.dlg.set_transient_for(self.window)
(res, n, f) = dlg.run()
if res == gtk.RESPONSE_OK:
i = _nd.add_name_format(n, f)
self.fmt_model.append(row=[i, n, f,
_nd.format_str(self.examplename, f)])
self.dbstate.db.name_formats = _nd.get_name_format(only_custom=True,
only_active=False)
def cb_del_fmt_str(self, obj): def cb_del_fmt_str(self, obj):
""" """
Name format editor Remove button callback Name format editor Remove button callback
@ -807,13 +783,26 @@ class GrampsPreferences(ConfigureDialog):
# Display name: # Display name:
self.examplename = Name() self.examplename = Name()
examplesurname = Surname()
examplesurnamesecond = Surname()
examplesurnamepat = Surname()
self.examplename.set_title('Dr.') self.examplename.set_title('Dr.')
self.examplename.set_first_name('Edwin Jose') self.examplename.set_first_name('Edwin Jose')
self.examplename.set_surname_prefix('von der') examplesurname.set_prefix('von der')
self.examplename.set_surname('Smith') examplesurname.set_surname('Smith')
examplesurname.set_connector('and')
self.examplename.add_surname(examplesurname)
examplesurnamesecond.set_surname('Weston')
self.examplename.add_surname(examplesurnamesecond)
examplesurnamepat.set_surname('Wilson')
examplesurnamepat.set_origintype(
NameOriginType(NameOriginType.PATRONYMIC))
self.examplename.add_surname(examplesurnamepat)
self.examplename.set_primary_surname(0)
self.examplename.set_suffix('Sr') self.examplename.set_suffix('Sr')
self.examplename.set_patronymic('Wilson') self.examplename.set_call_name('Jose')
self.examplename.set_call_name('Ed') self.examplename.set_nick_name('Ed')
self.examplename.set_family_nick_name('Underhills')
# get the model for the combo and the treeview # get the model for the combo and the treeview
active = _nd.get_default_format() active = _nd.get_default_format()
self.fmt_model, active = self._build_name_format_model(active) self.fmt_model, active = self._build_name_format_model(active)
@ -1151,71 +1140,3 @@ class GrampsPreferences(ConfigureDialog):
button.add(image) button.add(image)
button.show() button.show()
return button return button
class NameFormatEditDlg(object):
"""
"""
def __init__(self, fmt_name, fmt_str, name):
self.fmt_name = fmt_name
self.fmt_str = fmt_str
self.name = name
self.valid = True
self.top = Glade()
self.dlg = self.top.get_object('namefmt_edit')
ManagedWindow.set_titles(self.dlg, None, _('Name Format Editor'))
self.examplelabel = self.top.get_object('example_label')
self.nameentry = self.top.get_object('name_entry')
self.nameentry.set_text('<span weight="bold">%s</span>' % self.fmt_name)
self.nameentry.set_use_markup(True)
self.formatentry = self.top.get_object('format_entry')
self.formatentry.connect('changed', self.cb_format_changed)
self.formatentry.set_text(self.fmt_str)
def run(self):
running = True
while running:
self.response = self.dlg.run()
running = False
self.fmt_name = self.nameentry.get_text()
self.fmt_str = self.formatentry.get_text()
if self.response == gtk.RESPONSE_OK:
if not self.valid:
q = QuestionDialog2(
_('The format definition is invalid'),
_('What would you like to do?'),
_('_Continue anyway'), _('_Modify format'),
parent=self.dlg)
running = not q.run()
self.response = gtk.RESPONSE_CANCEL
elif self.fmt_name == '' and self.fmt_str == '':
self.response = gtk.RESPONSE_CANCEL
elif (self.fmt_name == '') ^ (self.fmt_str == ''):
ErrorDialog(
_('Both Format name and definition have to be defined.'),
parent=self.dlg)
running = True
self.dlg.destroy()
return (self.response, self.fmt_name, self.fmt_str)
def cb_format_changed(self, obj):
try:
t = (_nd.format_str(self.name, escape(obj.get_text())))
sample = '<span weight="bold" style="italic">%s</span>' % t
self.valid = True
except NameDisplayError:
t = _("Invalid or incomplete format definition.")
sample = '<span foreground="#FF0000">%s</span>' % t
self.valid = False
self.examplelabel.set_text(sample)
self.examplelabel.set_use_markup(True)
self.nameentry.set_text('<span weight="bold">%s</span>' % obj.get_text())
self.nameentry.set_use_markup(True)

View File

@ -42,6 +42,8 @@ pkgdata_PYTHON = \
sourcebackreflist.py \ sourcebackreflist.py \
sourceembedlist.py \ sourceembedlist.py \
sourcerefmodel.py \ sourcerefmodel.py \
surnamemodel.py \
surnametab.py \
webembedlist.py \ webembedlist.py \
webmodel.py \ webmodel.py \
__init__.py __init__.py

View File

@ -55,6 +55,7 @@ from personrefembedlist import PersonRefEmbedList
from personbackreflist import PersonBackRefList from personbackreflist import PersonBackRefList
from placebackreflist import PlaceBackRefList from placebackreflist import PlaceBackRefList
from repoembedlist import RepoEmbedList from repoembedlist import RepoEmbedList
from surnametab import SurnameTab
from sourcebackreflist import SourceBackRefList from sourcebackreflist import SourceBackRefList
from sourceembedlist import SourceEmbedList from sourceembedlist import SourceEmbedList
from webembedlist import WebEmbedList from webembedlist import WebEmbedList

View File

@ -443,6 +443,7 @@ class EmbeddedList(ButtonTab):
# insert the colum into the tree # insert the colum into the tree
column.set_resizable(True) column.set_resizable(True)
column.set_clickable(True) column.set_clickable(True)
column.set_expand(True)
column.set_min_width(self._column_names[pair[1]][2]) column.set_min_width(self._column_names[pair[1]][2])
column.set_sort_column_id(self._column_names[pair[1]][1]) column.set_sort_column_id(self._column_names[pair[1]][1])
self.columns.append(column) self.columns.append(column)

View File

@ -0,0 +1,55 @@
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2000-2006 Donald N. Allingham
# Copyright (C) 2010 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$
#-------------------------------------------------------------------------
#
# GTK libraries
#
#-------------------------------------------------------------------------
import gtk
import gobject
#-------------------------------------------------------------------------
#
# GRAMPS classes
#
#-------------------------------------------------------------------------
#-------------------------------------------------------------------------
#
# SurnamModel
#
#-------------------------------------------------------------------------
class SurnameModel(gtk.ListStore):
def __init__(self, surn_list, db):
#setup model for the treeview
gtk.ListStore.__init__(self, str, str, str, str,
bool, object)
for surn in surn_list:
# fill the liststore
self.append(row=[surn.get_prefix(), surn.get_surname(),
surn.get_connector(), str(surn.get_origintype()),
surn.get_primary(), surn])
self.db = db

View File

@ -0,0 +1,385 @@
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2000-2006 Donald N. Allingham
# Copyright (C) 2010 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 classes
#
#-------------------------------------------------------------------------
from gen.ggettext import gettext as _
import locale
#-------------------------------------------------------------------------
#
# GTK classes
#
#-------------------------------------------------------------------------
import gtk
import gobject
import pango
_TAB = gtk.gdk.keyval_from_name("Tab")
_ENTER = gtk.gdk.keyval_from_name("Enter")
#-------------------------------------------------------------------------
#
# GRAMPS classes
#
#-------------------------------------------------------------------------
from surnamemodel import SurnameModel
from embeddedlist import EmbeddedList
from DdTargets import DdTargets
import AutoComp
from gen.lib import Surname, NameOriginType
#-------------------------------------------------------------------------
#
# SurnameTab
#
#-------------------------------------------------------------------------
class SurnameTab(EmbeddedList):
_HANDLE_COL = 5
_DND_TYPE = DdTargets.SURNAME
_MSG = {
'add' : _('Create and add a new surname'),
'del' : _('Remove the selected surname'),
'edit' : _('Edit the selected surname'),
'up' : _('Move the selected surname upwards'),
'down' : _('Move the selected surname downwards'),
}
#index = column in model. Value =
# (name, sortcol in model, width, markup/text
_column_names = [
(_('Prefix'), -1, 150, 0, -1),
(_('Surname'), -1, 250, 0, -1),
(_('Connector'), -1, 100, 0, -1),
]
_column_combo = (_('Origin'), -1, 150, 3) # name, sort, width, modelcol
_column_toggle = (_('Primary'), -1, 80, 4)
def __init__(self, dbstate, uistate, track, name):
self.obj = name
self.curr_col = -1
self.curr_cellr = None
self.curr_celle = None
EmbeddedList.__init__(self, dbstate, uistate, track, _('Family Surnames'),
SurnameModel, move_buttons=True)
def build_columns(self):
#first the standard text columns with normal method
EmbeddedList.build_columns(self)
# Need to add attributes to renderers
# and connect renderers to the 'edited' signal
for colno in range(len(self.columns)):
for renderer in self.columns[colno].get_cell_renderers():
renderer.set_property('editable', not self.dbstate.db.readonly)
renderer.connect('editing_started', self.on_edit_start, colno)
renderer.connect('edited', self.on_edit_inline, self.column_order()[colno][1])
# now we add the two special columns
# combobox for type
colno = len(self.columns)
name = self._column_combo[0]
renderer = gtk.CellRendererCombo()
renderer.set_property('ellipsize', pango.ELLIPSIZE_END)
# set up the comboentry editable
no = NameOriginType()
self.cmborig = gtk.ListStore(gobject.TYPE_INT, gobject.TYPE_STRING)
self.cmborigmap = no.get_map().copy()
keys = sorted(self.cmborigmap, self.by_value)
for key in keys:
if key != no.get_custom():
self.cmborig.append(row=[key, self.cmborigmap[key]])
additional = self.dbstate.db.get_origin_types()
if additional:
for type in additional:
if type:
self.cmborig.append(row=[no.get_custom(), type])
renderer.set_property("model", self.cmborig)
renderer.set_property("text-column", 1)
renderer.set_property('editable', not self.dbstate.db.readonly)
renderer.connect('editing_started', self.on_edit_start_cmb, colno)
renderer.connect('edited', self.on_orig_edited, self._column_combo[3])
# add to treeview
column = gtk.TreeViewColumn(name, renderer, text=self._column_combo[3])
column.set_resizable(True)
column.set_sort_column_id(self._column_combo[1])
column.set_min_width(self._column_combo[2])
column.set_expand(True)
self.columns.append(column)
self.tree.append_column(column)
# toggle box for primary
colno += 1
name = self._column_toggle[0]
renderer = gtk.CellRendererToggle()
renderer.set_property('activatable', True)
renderer.set_property('radio', True)
renderer.connect( 'toggled', self.on_prim_toggled, self._column_toggle[3])
# add to treeview
column = gtk.TreeViewColumn(name, renderer, active=self._column_toggle[3])
column.set_resizable(False)
column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
column.set_alignment(0.5)
column.set_sort_column_id(self._column_toggle[1])
column.set_min_width(self._column_toggle[2])
self.columns.append(column)
self.tree.append_column(column)
def by_value(self, first, second):
"""
Method for sorting keys based on the values.
"""
fvalue = self.cmborigmap[first]
svalue = self.cmborigmap[second]
return locale.strcoll(fvalue, svalue)
def get_data(self):
return self.obj.get_surname_list()
def is_empty(self):
return len(self.model)==0
def _get_surn_from_model(self):
"""
Return new surname_list for storing in the name based on content of
the model
"""
new_list = []
for idx in range(len(self.model)):
node = self.model.get_iter(idx)
surn = self.model.get_value(node, 5)
surn.set_prefix(unicode(self.model.get_value(node, 0)))
surn.set_surname(unicode(self.model.get_value(node, 1)))
surn.set_connector(unicode(self.model.get_value(node, 2)))
surn.get_origintype().set(unicode(self.model.get_value(node, 3)))
surn.set_primary(self.model.get_value(node, 4))
new_list += [surn]
return new_list
def update(self):
"""
Store the present data in the model to the name object
"""
new_map = self._get_surn_from_model()
self.obj.set_surname_list(new_map)
# update name in title name editor
# TODO
def column_order(self):
# order of columns for EmbeddedList. Only the text columns here
return ((1, 0), (1, 1), (1, 2))
def add_button_clicked(self, obj):
"""Add button is clicked, add a surname to the person"""
prim = False
if len(self.obj.get_surname_list()) == 0:
prim = true
node = self.model.append(row=['', '', '', NameOriginType(), prim,
Surname()])
self.selection.select_iter(node)
path = self.model.get_path(node)
self.tree.set_cursor_on_cell(path,
focus_column=self.columns[0],
focus_cell=None,
start_editing=True)
def del_button_clicked(self, obj):
"""
Delete button is clicked. Remove from the model
"""
(model, node) = self.selection.get_selected()
if node:
self.model.remove(node)
self.update()
def on_edit_start(self, cellr, celle, path, colnr):
""" start of editing. Store stuff so we know when editing ends where we
are
"""
self.curr_col = colnr
self.curr_cellr = cellr
self.curr_celle = celle
def on_edit_start_cmb(self, cellr, celle, path, colnr):
"""
An edit starts in the origin type column
This means a cmb has been created as celle, and we can set up the stuff
we want this cmb to contain: autocompletion, stop edit when selection
in the cmb happens.
"""
self.on_edit_start(cellr, celle, path, colnr)
#set up autocomplete
completion = gtk.EntryCompletion()
completion.set_model(self.cmborig)
completion.set_minimum_key_length(1)
completion.set_text_column(1)
celle.child.set_completion(completion)
#
celle.connect('changed', self.on_origcmb_change, path, colnr)
def on_edit_start_toggle(self, cellr, celle, path, colnr):
"""
Edit
"""
self.on_edit_start(cellr, celle, path, colnr)
def on_edit_inline(self, cell, path, new_text, colnr):
"""
Edit is happening. The model is updated and the surname objects updated.
colnr must be the column in the model.
"""
node = self.model.get_iter(path)
self.model.set_value(node, colnr, new_text)
self.update()
def on_orig_edited(self, cellr, path, new_text, colnr):
"""
An edit is finished in the origin type column. For a cmb in an editor,
the model may only be updated when typing is finished, as editing stops
automatically on update of the model.
colnr must be the column in the model.
"""
self.on_edit_inline(cellr, path, new_text, colnr)
def on_origcmb_change(self, cmb, path, colnr):
"""
A selection occured in the cmb of the origin type column. colnr must
be the column in the model.
"""
act = cmb.get_active()
if act == -1:
return
self.on_orig_edited(None, path,
self.cmborig.get_value(
self.cmborig.get_iter((act,)),1),
colnr)
def on_prim_toggled(self, cell, path, colnr):
"""
Primary surname on path is toggled. colnr must be the col
in the model
"""
#obtain current value
node = self.model.get_iter(path)
old_val = self.model.get_value(node, colnr)
for nr in range(len(self.obj.get_surname_list())):
if nr == int(path[0]):
if old_val:
#True remains True
break
else:
#This value becomes True
self.model.set_value(self.model.get_iter((nr,)), colnr, True)
else:
self.model.set_value(self.model.get_iter((nr,)), colnr, False)
self.update()
return
def edit_button_clicked(self, obj):
""" Edit button clicked
"""
(model, node) = self.selection.get_selected()
if node:
path = self.model.get_path(node)
self.tree.set_cursor_on_cell(path,
focus_column=self.columns[0],
focus_cell=None,
start_editing=True)
def key_pressed(self, obj, event):
"""
Handles the key being pressed.
Here we make sure tab moves to next or previous value in row on TAB
"""
if not EmbeddedList.key_pressed(self, obj, event):
if event.type == gtk.gdk.KEY_PRESS and event.keyval in (_TAB,):
if event.state not in (gtk.gdk.SHIFT_MASK, gtk.gdk.CONTROL_MASK):
return self.next_cell()
elif event.state in (gtk.gdk.SHIFT_MASK, gtk.gdk.CONTROL_MASK):
return self.prev_cell()
else:
return
else:
return
return True
def next_cell(self):
"""
Move to the next cell to edit it
"""
(model, node) = self.selection.get_selected()
if node:
path = int(self.model.get_path(node)[0])
nccol = self.curr_col+1
if nccol < 4:
self.tree.set_cursor_on_cell(path,
focus_column=self.columns[nccol],
focus_cell=None,
start_editing=True)
elif nccol == 4:
#go to next line if there is one
if path < len(self.obj.get_surname_list()):
newpath = (path+1,)
self.selection.select_path(newpath)
self.tree.set_cursor_on_cell(newpath,
focus_column=self.columns[0],
focus_cell=None,
start_editing=True)
else:
#stop editing
self.curr_celle.editing_done()
return
return True
def prev_cell(self):
"""
Move to the next cell to edit it
"""
(model, node) = self.selection.get_selected()
if node:
path = int(self.model.get_path(node)[0])
if self.curr_col > 0:
self.tree.set_cursor_on_cell(path,
focus_column=self.columns[self.curr_col-1],
focus_cell=None,
start_editing=True)
elif self.curr_col == 0:
#go to prev line if there is one
if path > 0:
newpath = (path-1,)
self.selection.select_path(newpath)
self.tree.set_cursor_on_cell(newpath,
focus_column=self.columns[-2],
focus_cell=None,
start_editing=True)
else:
#stop editing
self.curr_celle.editing_done()
return
return True

View File

@ -28,7 +28,9 @@
# Standard python modules # Standard python modules
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
import gobject
from gen.ggettext import gettext as _ from gen.ggettext import gettext as _
from copy import copy
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# #
@ -45,7 +47,7 @@ import gtk
from gen.display.name import displayer as name_displayer from gen.display.name import displayer as name_displayer
from editsecondary import EditSecondary from editsecondary import EditSecondary
from gen.lib import NoteType from gen.lib import NoteType
from displaytabs import GrampsTab,SourceEmbedList,NoteTab from displaytabs import GrampsTab, SourceEmbedList, NoteTab, SurnameTab
from gui.widgets import (MonitoredEntry, MonitoredMenu, MonitoredDate, from gui.widgets import (MonitoredEntry, MonitoredMenu, MonitoredDate,
MonitoredDataType, PrivacyButton) MonitoredDataType, PrivacyButton)
from glade import Glade from glade import Glade
@ -111,12 +113,13 @@ class EditName(EditSecondary):
self.top = Glade() self.top = Glade()
self.set_window(self.top.toplevel, self.set_window(self.top.toplevel, None, _("Name Editor"))
self.top.get_object("title"),
_("Name Editor"))
tblgnam = self.top.get_object('table23') tblgnam = self.top.get_object('table23')
notebook = self.top.get_object('notebook') notebook = self.top.get_object('notebook')
hbox_surn = self.top.get_object('hboxmultsurnames')
hbox_surn.pack_start(SurnameTab(self.dbstate, self.uistate, self.track,
self.obj))
#recreate start page as GrampsTab #recreate start page as GrampsTab
notebook.remove_page(0) notebook.remove_page(0)
self.gennam = GeneralNameTab(self.dbstate, self.uistate, self.track, self.gennam = GeneralNameTab(self.dbstate, self.uistate, self.track,
@ -147,7 +150,19 @@ class EditName(EditSecondary):
def _connect_signals(self): def _connect_signals(self):
self.define_cancel_button(self.top.get_object('button119')) self.define_cancel_button(self.top.get_object('button119'))
self.define_help_button(self.top.get_object('button131')) self.define_help_button(self.top.get_object('button131'))
self.define_ok_button(self.top.get_object('button118'),self.save) self.define_ok_button(self.top.get_object('button118'), self.save)
def _validate_call(self, widget, text):
""" a callname must be a part of the given name, see if this is the
case """
validcall = self.given_field.obj.get_text().split()
dummy = copy(validcall)
for item in dummy:
validcall += item.split('-')
if text in validcall:
return
return ValidationError(_("Call name must be the given name that "
"is normally used."))
def _setup_fields(self): def _setup_fields(self):
self.group_as = MonitoredEntry( self.group_as = MonitoredEntry(
@ -180,7 +195,7 @@ class EditName(EditSecondary):
self.db.readonly) self.db.readonly)
self.given_field = MonitoredEntry( self.given_field = MonitoredEntry(
self.top.get_object("alt_given"), self.top.get_object("given_name"),
self.obj.set_first_name, self.obj.set_first_name,
self.obj.get_first_name, self.obj.get_first_name,
self.db.readonly) self.db.readonly)
@ -190,39 +205,42 @@ class EditName(EditSecondary):
self.obj.set_call_name, self.obj.set_call_name,
self.obj.get_call_name, self.obj.get_call_name,
self.db.readonly) self.db.readonly)
self.call_field.connect("validate", self._validate_call)
#force validation now with initial entry
self.call_field.obj.validate(force=True)
self.title_field = MonitoredEntry( self.title_field = MonitoredEntry(
self.top.get_object("alt_title"), self.top.get_object("title_field"),
self.obj.set_title, self.obj.set_title,
self.obj.get_title, self.obj.get_title,
self.db.readonly) self.db.readonly)
self.suffix_field = MonitoredEntry( self.suffix_field = MonitoredEntry(
self.top.get_object("alt_suffix"), self.top.get_object("suffix"),
self.obj.set_suffix, self.obj.set_suffix,
self.obj.get_suffix, self.obj.get_suffix,
self.db.readonly) self.db.readonly)
self.patronymic_field = MonitoredEntry( self.nick = MonitoredEntry(
self.top.get_object("patronymic"), self.top.get_object("nickname"),
self.obj.set_patronymic, self.obj.set_nick_name,
self.obj.get_patronymic, self.obj.get_nick_name,
self.db.readonly) self.db.readonly)
self.surname_field = MonitoredEntry( self.famnick = MonitoredEntry(
self.top.get_object("alt_surname"), self.top.get_object("familynickname"),
self.obj.set_surname, self.obj.set_family_nick_name,
self.obj.get_surname, self.obj.get_family_nick_name,
self.db.readonly,
autolist=self.db.get_surname_list() if not self.db.readonly else [],
changed=self.update_group_as)
self.prefix_field = MonitoredEntry(
self.top.get_object("alt_prefix"),
self.obj.set_surname_prefix,
self.obj.get_surname_prefix,
self.db.readonly) self.db.readonly)
#self.surname_field = MonitoredEntry(
# self.top.get_object("alt_surname"),
# self.obj.set_surname,
# self.obj.get_surname,
# self.db.readonly,
# autolist=self.db.get_surname_list() if not self.db.readonly else [],
# changed=self.update_group_as)
self.date = MonitoredDate( self.date = MonitoredDate(
self.top.get_object("date_entry"), self.top.get_object("date_entry"),
self.top.get_object("date_stat"), self.top.get_object("date_stat"),
@ -232,7 +250,7 @@ class EditName(EditSecondary):
self.db.readonly) self.db.readonly)
self.obj_combo = MonitoredDataType( self.obj_combo = MonitoredDataType(
self.top.get_object("name_type"), self.top.get_object("ntype"),
self.obj.set_type, self.obj.set_type,
self.obj.get_type, self.obj.get_type,
self.db.readonly, self.db.readonly,
@ -322,7 +340,7 @@ class EditName(EditSecondary):
surname = self.obj.get_surname() surname = self.obj.get_surname()
self.group_as.set_text(surname) self.group_as.set_text(surname)
def save(self,*obj): def save(self, *obj):
"""Save the name setting. All is ok, except grouping. We need to """Save the name setting. All is ok, except grouping. We need to
consider: consider:
1/ global set, not local set --> unset (ask if global unset) 1/ global set, not local set --> unset (ask if global unset)

View File

@ -32,6 +32,7 @@ to edit information about a particular Person.
# Standard python modules # Standard python modules
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
from copy import copy
from gen.ggettext import sgettext as _ from gen.ggettext import sgettext as _
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
@ -59,11 +60,12 @@ from editmediaref import EditMediaRef
from editname import EditName from editname import EditName
import config import config
from QuestionDialog import ErrorDialog, ICON from QuestionDialog import ErrorDialog, ICON
from Errors import ValidationError
from displaytabs import (PersonEventEmbedList, NameEmbedList, SourceEmbedList, from displaytabs import (PersonEventEmbedList, NameEmbedList, SourceEmbedList,
AttrEmbedList, AddrEmbedList, NoteTab, GalleryTab, AttrEmbedList, AddrEmbedList, NoteTab, GalleryTab,
WebEmbedList, PersonRefEmbedList, LdsEmbedList, WebEmbedList, PersonRefEmbedList, LdsEmbedList,
PersonBackRefList) PersonBackRefList, SurnameTab)
from gen.plug import CATEGORY_QR_PERSON from gen.plug import CATEGORY_QR_PERSON
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
@ -104,7 +106,11 @@ class EditPerson(EditPrimary):
This is used by the base class (EditPrimary). This is used by the base class (EditPrimary).
""" """
return gen.lib.Person() person = gen.lib.Person()
#the editor requires a surname
person.primary_name.add_surname(gen.lib.Surname())
person.primary_name.set_primary_surname(0)
return person
def get_menu_title(self): def get_menu_title(self):
if self.obj and self.obj.get_handle(): if self.obj and self.obj.get_handle():
@ -143,9 +149,14 @@ class EditPerson(EditPrimary):
self.obj_photo = self.top.get_object("personPix") self.obj_photo = self.top.get_object("personPix")
self.eventbox = self.top.get_object("eventbox1") self.eventbox = self.top.get_object("eventbox1")
self.singsurnfr = self.top.get_object("surnamefr")
self.multsurnfr = self.top.get_object("multsurnamefr")
self.singlesurn_active = True
self.surntab = SurnameTab(self.dbstate, self.uistate, self.track,
self.obj.get_primary_name())
self.top.get_object("hboxmultsurnames").pack_start(self.surntab)
self.set_contexteventbox(self.top.get_object("eventboxtop")) self.set_contexteventbox(self.top.get_object("eventboxtop"))
def _post_init(self): def _post_init(self):
""" """
@ -158,10 +169,18 @@ class EditPerson(EditPrimary):
""" """
self.load_person_image() self.load_person_image()
if self.pname.get_surname() and not self.pname.get_first_name(): self.given.grab_focus()
self.given.grab_focus()
if len(self.obj.get_primary_name().get_surname_list()) > 1:
self.singsurnfr.hide_all()
self.singlesurn_active = False
else: else:
self.surname_field.grab_focus() self.multsurnfr.hide_all()
self.singlesurn_active = True
#if self.pname.get_surname() and not self.pname.get_first_name():
# self.given.grab_focus()
#else:
# self.surname_field.grab_focus()
def _connect_signals(self): def _connect_signals(self):
""" """
@ -173,8 +192,10 @@ class EditPerson(EditPrimary):
self.define_help_button(self.top.get_object("button134")) self.define_help_button(self.top.get_object("button134"))
self.given.connect("focus_out_event", self._given_focus_out_event) self.given.connect("focus_out_event", self._given_focus_out_event)
self.top.get_object("button177").connect("clicked", self.top.get_object("editnamebtn").connect("clicked",
self._edit_name_clicked) self._edit_name_clicked)
self.top.get_object("multsurnamebtn").connect("clicked",
self._mult_surn_clicked)
self.eventbox.connect('button-press-event', self.eventbox.connect('button-press-event',
self._image_button_press) self._image_button_press)
@ -229,6 +250,18 @@ class EditPerson(EditPrimary):
# we just rebuild the view always # we just rebuild the view always
self.event_list.rebuild_callback() 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
case """
validcall = self.given.obj.get_text().split()
dummy = copy(validcall)
for item in dummy:
validcall += item.split('-')
if text in validcall:
return
return ValidationError(_("Call name must be the given name that "
"is normally used."))
def _setup_fields(self): def _setup_fields(self):
""" """
Connect the GrampsWidget objects to field in the interface. Connect the GrampsWidget objects to field in the interface.
@ -260,44 +293,64 @@ class EditPerson(EditPrimary):
self.pname.get_type, self.pname.get_type,
self.db.readonly, self.db.readonly,
self.db.get_name_types()) self.db.get_name_types())
self.prefix_suffix = widgets.MonitoredComboSelectedEntry(
self.top.get_object("prefixcmb"),
self.top.get_object("prefixentry"),
[_('Prefix'), _('Suffix')],
[self.pname.set_surname_prefix, self.pname.set_suffix],
[self.pname.get_surname_prefix, self.pname.get_suffix],
default = config.get('interface.prefix-suffix'),
read_only = self.db.readonly)
self.patro_title = widgets.MonitoredComboSelectedEntry( #part of Given Name section
self.top.get_object("patrocmb"),
self.top.get_object("patroentry"),
[_('Patronymic'), _('Person|Title')],
[self.pname.set_patronymic, self.pname.set_title],
[self.pname.get_patronymic, self.pname.get_title],
default = config.get('interface.patro-title'),
read_only = self.db.readonly)
self.call = widgets.MonitoredEntry(
self.top.get_object("call"),
self.pname.set_call_name,
self.pname.get_call_name,
self.db.readonly)
self.given = widgets.MonitoredEntry( self.given = widgets.MonitoredEntry(
self.top.get_object("given_name"), self.top.get_object("given_name"),
self.pname.set_first_name, self.pname.set_first_name,
self.pname.get_first_name, self.pname.get_first_name,
self.db.readonly) self.db.readonly)
self.call = widgets.MonitoredEntry(
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.db.readonly)
self.suffix = widgets.MonitoredEntry(
self.top.get_object("suffix"),
self.pname.set_suffix,
self.pname.get_suffix,
self.db.readonly)
self.nick = widgets.MonitoredEntry(
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.surname_field = widgets.MonitoredEntry(
self.top.get_object("surname"), self.top.get_object("surname"),
self.pname.set_surname, self.pname.get_primary_surname().set_surname,
self.pname.get_surname, self.pname.get_primary_surname().get_surname,
self.db.readonly, self.db.readonly,
autolist=self.db.get_surname_list() if not self.db.readonly else []) autolist=self.db.get_surname_list() if not self.db.readonly else [])
self.prefix = widgets.MonitoredEntry(
self.top.get_object("prefix"),
self.pname.get_primary_surname().set_prefix,
self.pname.get_primary_surname().get_prefix,
self.db.readonly)
self.ortype_field = widgets.MonitoredDataType(
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())
#other fields
self.tags = widgets.MonitoredTagList( self.tags = widgets.MonitoredTagList(
self.top.get_object("tag_label"), self.top.get_object("tag_label"),
self.top.get_object("tag_button"), self.top.get_object("tag_button"),
@ -314,11 +367,11 @@ class EditPerson(EditPrimary):
self.db.readonly) self.db.readonly)
#make sure title updates automatically #make sure title updates automatically
for obj in [self.top.get_object("surname"), for obj in [self.top.get_object("given_name"),
self.top.get_object("given_name"),
self.top.get_object("patroentry"),
self.top.get_object("call"), self.top.get_object("call"),
self.top.get_object("prefixentry"), self.top.get_object("suffix"),
self.top.get_object("prefix"),
self.top.get_object("surname"),
]: ]:
obj.connect('changed', self._changed_name) obj.connect('changed', self._changed_name)
@ -767,6 +820,14 @@ class EditPerson(EditPrimary):
EditName(self.dbstate, self.uistate, self.track, EditName(self.dbstate, self.uistate, self.track,
self.pname, self._update_name) self.pname, self._update_name)
def _mult_surn_clicked(self, obj):
"""
Show the list entry of multiple surnames
"""
self.singsurnfr.hide_all()
self.singlesurn_active = False
self.multsurnfr.show_all()
def _update_name(self, name): def _update_name(self, name):
""" """
Called when the primary name has been changed by the EditName Called when the primary name has been changed by the EditName
@ -775,9 +836,15 @@ class EditPerson(EditPrimary):
This allows us to update the main form in response to any changes. This allows us to update the main form in response to any changes.
""" """
for obj in (self.prefix_suffix, self.patro_title, self.given, for obj in (self.ntype_field, self.given, self.call, self.title,
self.ntype_field, self.surname_field, self.call): self.suffix, self.nick, self.surname_field, self.prefix,
self.ortype_field):
obj.update() obj.update()
if len(self.obj.get_primary_name().get_surname_list()) > 1:
#TODO: multiple surname must be activated if not yet the case
print 'person editor TODO'
#TODO: update list of surnames
print 'person editor TODO 2'
def load_person_image(self): def load_person_image(self):
""" """
@ -887,9 +954,8 @@ class EditPerson(EditPrimary):
return child_ref_list return child_ref_list
def _cleanup_on_exit(self): def _cleanup_on_exit(self):
config.set('interface.prefix-suffix', self.prefix_suffix.active_key) pass
config.set('interface.patro-title', self.patro_title.active_key) #config.save()
config.save()
class GenderDialog(gtk.MessageDialog): class GenderDialog(gtk.MessageDialog):

View File

@ -32,6 +32,7 @@ class EditSecondary(ManagedWindow.ManagedWindow, DbGUIElement):
"""Create an edit window. Associates a person with the window.""" """Create an edit window. Associates a person with the window."""
self.obj = obj self.obj = obj
self.old_obj = obj.serialize()
self.dbstate = state self.dbstate = state
self.uistate = uistate self.uistate = uistate
self.db = state.db self.db = state.db
@ -113,13 +114,20 @@ class EditSecondary(ManagedWindow.ManagedWindow, DbGUIElement):
button.set_sensitive(not self.db.readonly) button.set_sensitive(not self.db.readonly)
def define_cancel_button(self,button): def define_cancel_button(self,button):
button.connect('clicked',self.close) button.connect('clicked', self.canceledits)
def define_help_button(self, button, webpage='', section=''): def define_help_button(self, button, webpage='', section=''):
button.connect('clicked', lambda x: GrampsDisplay.help(webpage, button.connect('clicked', lambda x: GrampsDisplay.help(webpage,
section)) section))
def close(self,*obj): def canceledits(self, *obj):
"""
Undo the edits that happened on this secondary object
"""
self.obj.unserialize(self.old_obj)
self.close(obj)
def close(self, *obj):
self._cleanup_db_connects() self._cleanup_db_connects()
self._cleanup_on_exit() self._cleanup_on_exit()
ManagedWindow.ManagedWindow.close(self) ManagedWindow.ManagedWindow.close(self)

View File

@ -185,7 +185,7 @@ class PeopleBaseModel(object):
def sort_name(self, data): def sort_name(self, data):
n = Name() n = Name()
n.unserialize(data[COLUMN_NAME]) n.unserialize(data[COLUMN_NAME])
return (n.get_surname(), n.get_first_name()) return (n.get_primary_surname().get_surname(), n.get_first_name())
def column_name(self, data): def column_name(self, data):
handle = data[0] handle = data[0]
@ -512,7 +512,6 @@ class PersonTreeModel(PeopleBaseModel, TreeBaseModel):
data The object data. data The object data.
""" """
ngn = name_displayer.name_grouping_data ngn = name_displayer.name_grouping_data
nsn = name_displayer.raw_sorted_name
name_data = data[COLUMN_NAME] name_data = data[COLUMN_NAME]
group_name = ngn(self.db, name_data) group_name = ngn(self.db, name_data)

View File

@ -498,14 +498,12 @@ class GedcomWriter(UpdateCallback):
""" """
Write the names associated with the person to the current level. Write the names associated with the person to the current level.
Since nicknames are now separate from the name structure, we search Since nicknames in version < 3.3 are separate from the name structure,
the attribute list to see if we can find a nickname. Because we do we search the attribute list to see if we can find a nickname.
not know the mappings, we just take the first nickname we find, and Because we do not know the mappings, we just take the first nickname
add it to the primary name. we find, and add it to the primary name.
If a nickname is present in the name structure, it has precedence
All other names are assumed to not have a nickname, even if other
nicknames exist in the attribute list.
""" """
nicknames = [ attr.get_value() for attr in person.get_attribute_list() nicknames = [ attr.get_value() for attr in person.get_attribute_list()
if int(attr.get_type()) == gen.lib.AttributeType.NICKNAME ] if int(attr.get_type()) == gen.lib.AttributeType.NICKNAME ]
@ -1193,7 +1191,7 @@ class GedcomWriter(UpdateCallback):
elif date.get_text(): elif date.get_text():
self.__writeln(level, 'DATE', date.get_text()) self.__writeln(level, 'DATE', date.get_text())
def __person_name(self, name, nick): def __person_name(self, name, attr_nick):
""" """
n NAME <NAME_PERSONAL> {1:1} n NAME <NAME_PERSONAL> {1:1}
+1 NPFX <NAME_PIECE_PREFIX> {0:1} +1 NPFX <NAME_PIECE_PREFIX> {0:1}
@ -1208,13 +1206,21 @@ class GedcomWriter(UpdateCallback):
gedcom_name = name.get_gedcom_name() gedcom_name = name.get_gedcom_name()
firstname = name.get_first_name().strip() firstname = name.get_first_name().strip()
patron = name.get_patronymic().strip() surns = []
if patron: surprefs = []
firstname = "%s %s" % (firstname, patron) for surn in name.get_surname_list():
surname = name.get_surname().replace('/', '?') surns.append(surn.get_surname().replace('/', '?'))
surprefix = name.get_surname_prefix().replace('/', '?') if surn.get_connector():
#we store connector with the surname
surns[-1] = surns[-1] + ' ' + surn.get_connector()
surprefs.append(surn.get_prefix().replace('/', '?'))
surname = ', '.join(surns)
surprefix = ', '.join(surprefs)
suffix = name.get_suffix() suffix = name.get_suffix()
title = name.get_title() title = name.get_title()
nick = name.get_nick_name()
if nick.strip() == '':
nick = attr_nick
self.__writeln(1, 'NAME', gedcom_name) self.__writeln(1, 'NAME', gedcom_name)
@ -1224,7 +1230,6 @@ class GedcomWriter(UpdateCallback):
self.__writeln(2, 'SPFX', surprefix) self.__writeln(2, 'SPFX', surprefix)
if surname: if surname:
self.__writeln(2, 'SURN', surname) self.__writeln(2, 'SURN', surname)
if name.get_suffix(): if name.get_suffix():
self.__writeln(2, 'NSFX', suffix) self.__writeln(2, 'NSFX', suffix)
if name.get_title(): if name.get_title():

View File

@ -783,16 +783,26 @@ class GrampsXmlWriter(UpdateCallback):
if rel != "": if rel != "":
self.g.write(' %s<rel type="%s"/>\n' % (sp,rel) ) self.g.write(' %s<rel type="%s"/>\n' % (sp,rel) )
def write_last(self, name,indent=1): def write_surname(self, surname, indent=1):
p = name.get_surname_prefix() """
n = name.get_surname() Writes a surname of the name
g = name.get_group_as() """
self.g.write('%s<last' % (' '*indent)) pre = surname.get_prefix()
if p: con = surname.get_connector()
self.g.write(' prefix="%s"' % escxml(p)) nam = surname.get_surname()
if g: der = surname.get_origintype().xml_str()
self.g.write(' group="%s"' % escxml(g)) pri = surname.get_primary()
self.g.write('>%s</last>\n' % self.fix(n)) self.g.write('%s<surname' % (' '*indent))
if pre:
self.g.write(' prefix="%s"' % escxml(pre))
if not pri:
self.g.write(' prim="0"')
if con:
self.g.write(' connector="%s"' % escxml(con))
if der:
self.g.write(' derivation="%s"' % escxml(der))
self.g.write('>%s</surname>\n' % self.fix(nam))
def write_line(self,tagname,value,indent=1): def write_line(self,tagname,value,indent=1):
if value: if value:
@ -915,12 +925,15 @@ class GrampsXmlWriter(UpdateCallback):
if name.get_display_as() != 0: if name.get_display_as() != 0:
self.g.write(' display="%d"' % name.get_display_as()) self.g.write(' display="%d"' % name.get_display_as())
self.g.write('>\n') self.g.write('>\n')
self.write_line("first", name.get_first_name(),index+1) self.write_line("first", escxml(name.get_first_name()),index+1)
self.write_line("call", name.get_call_name(),index+1) self.write_line("call", escxml(name.get_call_name()),index+1)
self.write_last(name,index+1) for surname in name.get_surname_list():
self.write_line("suffix", name.get_suffix(),index+1) self.write_surname(surname,index+1)
self.write_line("patronymic", name.get_patronymic(),index+1) self.write_line("suffix", escxml(name.get_suffix()),index+1)
self.write_line("title", name.get_title(),index+1) self.write_line("title", escxml(name.get_title()),index+1)
self.write_line("nick", escxml(name.get_nick_name()), index+1)
self.write_line("familynick", escxml(name.get_family_nick_name()), index+1)
self.write_line("group", escxml(name.get_group_as()), index+1)
if name.date: if name.date:
self.write_date(name.date,4) self.write_date(name.date,4)
self.write_note_list(name.get_note_list(),index+1) self.write_note_list(name.get_note_list(),index+1)

View File

@ -77,8 +77,9 @@ def find_surname(key, data):
""" """
Return the surname from the data stream. Used for building a secondary Return the surname from the data stream. Used for building a secondary
index. index.
This function is not needed, as we don't use the secondary index.
""" """
return str(data[3][5]) return str("a")
def find_idmap(key, data): def find_idmap(key, data):
""" """
@ -138,6 +139,10 @@ class GrampsBSDDB(DbGrdb, UpdateCallback):
This is replaced for internal use by gen/db/dbdir.py This is replaced for internal use by gen/db/dbdir.py
However, this class is still used for import of the 2.2.x However, this class is still used for import of the 2.2.x
GRDB format. In 3.0+ this format is no longer used. GRDB format. In 3.0+ this format is no longer used.
We only need to upgrade the old main tables.
That will be used to append data to the database this GrampsBSDDB is
imported to.
""" """
def __init__(self, use_txn = True): def __init__(self, use_txn = True):
@ -1247,19 +1252,6 @@ class GrampsBSDDB(DbGrdb, UpdateCallback):
self.surname_list = list(set(self.surnames.keys())) self.surname_list = list(set(self.surnames.keys()))
self.sort_surname_list() self.sort_surname_list()
def remove_from_surname_list(self, person):
"""
Check whether there are persons with the same surname left in
the database. If not then we need to remove the name from the list.
The function must be overridden in the derived class.
"""
name = str(person.get_primary_name().get_surname())
try:
if self.surnames.keys().count(name) == 1:
self.surname_list.remove(unicode(name))
except ValueError:
pass
def __get_obj_from_gramps_id(self, val, tbl, class_init, prim_tbl): def __get_obj_from_gramps_id(self, val, tbl, class_init, prim_tbl):
if tbl.has_key(str(val)): if tbl.has_key(str(val)):
#if str(val) in tbl: #if str(val) in tbl:
@ -1563,6 +1555,8 @@ class GrampsBSDDB(DbGrdb, UpdateCallback):
self.gramps_upgrade_13() self.gramps_upgrade_13()
if version < 14: if version < 14:
self.gramps_upgrade_14() self.gramps_upgrade_14()
if version < 15:
self.gramps_upgrade_15()
LOG.debug("Upgrade time: %s %s", int(time.time()-t), "seconds") LOG.debug("Upgrade time: %s %s", int(time.time()-t), "seconds")
def gramps_upgrade_10(self): def gramps_upgrade_10(self):
@ -2589,6 +2583,108 @@ class GrampsBSDDB(DbGrdb, UpdateCallback):
name_type, prefix, patronymic, name_type, prefix, patronymic,
group_as, sort_as, display_as, call) group_as, sort_as, display_as, call)
def gramps_upgrade_15(self):
"""Upgrade database from version 14 to 15. This upgrade adds:
* tagging
* surname list
"""
length = len(self.person_map)+10
self.set_total(length)
# ---------------------------------
# Modify Person
# ---------------------------------
for handle in self.person_map.keys():
person = self.person_map[handle]
(junk_handle, # 0
gramps_id, # 1
gender, # 2
primary_name, # 3
alternate_names, # 4
death_ref_index, # 5
birth_ref_index, # 6
event_ref_list, # 7
family_list, # 8
parent_family_list, # 9
media_list, # 10
address_list, # 11
attribute_list, # 12
urls, # 13
ord_list, # 14
psource_list, # 15
pnote_list, # 16
change, # 17
marker, # 18
pprivate, # 19
person_ref_list, # 20
) = person
new_primary_name = self.convert_name_15(primary_name)
new_alternate_names = [self.convert_name_15(altname) for altname in
alternate_names]
new_person = (junk_handle, # 0
gramps_id, # 1
gender, # 2
new_primary_name, # 3
new_alternate_names,# 4
death_ref_index, # 5
birth_ref_index, # 6
event_ref_list, # 7
family_list, # 8
parent_family_list, # 9
media_list, # 10
address_list, # 11
attribute_list, # 12
urls, # 13
ord_list, # 14
psource_list, # 15
pnote_list, # 16
change, # 17
marker, # 18
pprivate, # 19
person_ref_list, # 20
[] # 21, tags
)
the_txn = self.env.txn_begin()
self.person_map.put(str(handle), new_person, txn=the_txn)
the_txn.commit()
self.update(length)
#surname is now different, normally remove secondary index with names
#we skip this, as this database will not be used after the import
# Bump up database version. Separate transaction to save metadata.
the_txn = self.env.txn_begin()
self.metadata.put('version', 15, txn=the_txn)
the_txn.commit()
def convert_name_15(self, name):
(privacy, source_list, note_list, date,
first_name, surname, suffix, title,
name_type, prefix, patronymic,
group_as, sort_as, display_as, call) = name
connector = u""
origintype = (NameOriginType.NONE, u"")
patorigintype = (NameOriginType.PATRONYMIC, u"")
if patronymic.strip() == u"":
#no patronymic, create a single surname
surname_list = [(surname, prefix, True, origintype, connector)]
else:
#a patronymic, if no surname or equal as patronymic, a single surname
if (surname.strip() == u"") or (surname == patronymic and prefix == u""):
surname_list = [(patronymic, prefix, True, patorigintype, connector)]
else:
#two surnames, first patronymic, then surname which is primary
surname_list = [(patronymic, u"", False, patorigintype, u""),
(surname, prefix, True, origintype, connector)]
#return new value, add two empty strings for nick and family nick
return (privacy, source_list, note_list, date,
first_name, surname_list, suffix, title, name_type,
group_as, sort_as, display_as, call, u"", u"")
def set_auto_remove(self): def set_auto_remove(self):
""" """
BSDDB change log settings using new method with renamed attributes BSDDB change log settings using new method with renamed attributes

View File

@ -96,9 +96,23 @@ def importData(database, filename, callback=None, cl=0):
database.smap = {} database.smap = {}
database.pmap = {} database.pmap = {}
database.fmap = {} database.fmap = {}
xml_file = open_file(filename, cl)
versionparser = VersionParser(xml_file)
if xml_file is None or \
version_is_valid(versionparser, cl) is False:
if cl:
sys.exit(1)
else:
return
version_string = versionparser.get_xmlns_version()
#reset file to the start
xml_file.seek(0)
change = os.path.getmtime(filename) change = os.path.getmtime(filename)
parser = GrampsParser(database, callback, change) parser = GrampsParser(database, callback, change, version_string)
linecounter = LineParser(filename) linecounter = LineParser(filename)
line_cnt = linecounter.get_count() line_cnt = linecounter.get_count()
@ -107,17 +121,7 @@ def importData(database, filename, callback=None, cl=0):
read_only = database.readonly read_only = database.readonly
database.readonly = False database.readonly = False
xml_file = open_file(filename, cl)
if xml_file is None or \
version_is_valid(xml_file, cl) is False:
if cl:
sys.exit(1)
else:
return
try: try:
xml_file.seek(0)
info = parser.parse(xml_file, line_cnt, person_cnt) info = parser.parse(xml_file, line_cnt, person_cnt)
except IOError, msg: except IOError, msg:
if cl: if cl:
@ -348,8 +352,10 @@ class LineParser(object):
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
class GrampsParser(UpdateCallback): class GrampsParser(UpdateCallback):
def __init__(self, database, callback, change): def __init__(self, database, callback, change, version_string):
UpdateCallback.__init__(self, callback) UpdateCallback.__init__(self, callback)
#version of the xml file
self.version_string = version_string
self.stext_list = [] self.stext_list = []
self.scomments_list = [] self.scomments_list = []
self.note_list = [] self.note_list = []
@ -423,6 +429,8 @@ class GrampsParser(UpdateCallback):
self.childref = None self.childref = None
self.personref = None self.personref = None
self.name = None self.name = None
self.surname = None
self.surnamepat = None
self.home = None self.home = None
self.owner = gen.lib.Researcher() self.owner = gen.lib.Researcher()
self.func_list = [None]*50 self.func_list = [None]*50
@ -440,17 +448,33 @@ class GrampsParser(UpdateCallback):
self.eidswap = {} self.eidswap = {}
self.func_map = { self.func_map = {
#name part
"name": (self.start_name, self.stop_name),
"first": (None, self.stop_first),
"call": (None, self.stop_call),
"aka": (self.start_name, self.stop_aka), #deprecated < 1.3.0
"last": (self.start_last, self.stop_last), #deprecated in 1.4.0
"nick": (None, self.stop_nick),
"title": (None, self.stop_title),
"suffix": (None, self.stop_suffix),
"patronymic": (self.start_patronymic, self.stop_patronymic), #deprecated in 1.4.0
"familynick": (None, self.stop_familynick), #new in 1.4.0
"group": (None, self.stop_group), #new in 1.4.0, replaces attribute
#new in 1.4.0
"surname": (self.start_surname, self.stop_surname),
#
"namemaps": (None, None),
"name-formats": (None, None),
#other
"address": (self.start_address, self.stop_address), "address": (self.start_address, self.stop_address),
"addresses": (None, None), "addresses": (None, None),
"childlist": (None, None), "childlist": (None, None),
"aka": (self.start_name, self.stop_aka),
"attribute": (self.start_attribute, self.stop_attribute), "attribute": (self.start_attribute, self.stop_attribute),
"attr_type": (None, self.stop_attr_type), "attr_type": (None, self.stop_attr_type),
"attr_value": (None, self.stop_attr_value), "attr_value": (None, self.stop_attr_value),
"bookmark": (self.start_bmark, None), "bookmark": (self.start_bmark, None),
"bookmarks": (None, None), "bookmarks": (None, None),
"format": (self.start_format, None), "format": (self.start_format, None),
"name-formats": (None, None),
"child": (self.start_child, None), "child": (self.start_child, None),
"childof": (self.start_childof, None), "childof": (self.start_childof, None),
"childref": (self.start_childref, self.stop_childref), "childref": (self.start_childref, self.stop_childref),
@ -476,17 +500,11 @@ class GrampsParser(UpdateCallback):
"rel": (self.start_rel, None), "rel": (self.start_rel, None),
"region": (self.start_region, None), "region": (self.start_region, None),
"father": (self.start_father, None), "father": (self.start_father, None),
"first": (None, self.stop_first),
"call": (None, self.stop_call),
"gender": (None, self.stop_gender), "gender": (None, self.stop_gender),
"header": (None, None), "header": (None, None),
"last": (self.start_last, self.stop_last),
"map": (self.start_namemap, None), "map": (self.start_namemap, None),
"mediapath": (None, self.stop_mediapath), "mediapath": (None, self.stop_mediapath),
"mother": (self.start_mother, None), "mother": (self.start_mother, None),
"name": (self.start_name, self.stop_name),
"namemaps": (None, None),
"nick": (None, self.stop_nick),
"note": (self.start_note, self.stop_note), "note": (self.start_note, self.stop_note),
"noteref": (self.start_noteref, None), "noteref": (self.start_noteref, None),
"p": (None, self.stop_ptag), "p": (None, self.stop_ptag),
@ -511,7 +529,6 @@ class GrampsParser(UpdateCallback):
"status": (self.start_status, None), "status": (self.start_status, None),
"sealed_to": (self.start_sealed_to, None), "sealed_to": (self.start_sealed_to, None),
"coord": (self.start_coord, None), "coord": (self.start_coord, None),
"patronymic": (None, self.stop_patronymic),
"pos": (self.start_pos, None), "pos": (self.start_pos, None),
"postal": (None, self.stop_postal), "postal": (None, self.stop_postal),
"range": (self.start_range, None), "range": (self.start_range, None),
@ -536,13 +553,11 @@ class GrampsParser(UpdateCallback):
"stext": (None, self.stop_stext), "stext": (None, self.stop_stext),
"stitle": (None, self.stop_stitle), "stitle": (None, self.stop_stitle),
"street": (None, self.stop_street), "street": (None, self.stop_street),
"style": (self.start_style, None), "style": (self.start_style, None),
"suffix": (None, self.stop_suffix),
"tag": (self.start_tag, None), "tag": (self.start_tag, None),
"tagref": (self.start_tagref, None), "tagref": (self.start_tagref, None),
"tags": (None, None), "tags": (None, None),
"text": (None, self.stop_text), "text": (None, self.stop_text),
"title": (None, self.stop_title),
"url": (self.start_url, None), "url": (self.start_url, None),
"repository": (self.start_repo, self.stop_repo), "repository": (self.start_repo, self.stop_repo),
"reporef": (self.start_reporef, self.stop_reporef), "reporef": (self.start_reporef, self.stop_reporef),
@ -743,6 +758,11 @@ class GrampsParser(UpdateCallback):
return self.nidswap[gramps_id] return self.nidswap[gramps_id]
def parse(self, ifile, linecount=0, personcount=0): def parse(self, ifile, linecount=0, personcount=0):
"""
Parse the xml file
:param ifile: must be a file handle that is already open, with position
at the start of the file
"""
if personcount < 1000: if personcount < 1000:
no_magic = True no_magic = True
else: else:
@ -1305,6 +1325,14 @@ class GrampsParser(UpdateCallback):
except KeyError: except KeyError:
pass pass
def start_surname(self, attrs):
self.surname = gen.lib.Surname()
self.surname.set_prefix(attrs.get("prefix", ""))
self.surname.set_primary(bool(attrs.get("primary",0)))
self.surname.set_connector(attrs.get("connector", ""))
origin_type = attrs.get("derivation", "")
self.surname.origintype.set_from_xml_str(origin_type)
def start_namemap(self, attrs): def start_namemap(self, attrs):
type = attrs.get('type') type = attrs.get('type')
key = attrs['key'] key = attrs['key']
@ -1321,9 +1349,17 @@ class GrampsParser(UpdateCallback):
self.db.set_name_group_mapping(key, value) self.db.set_name_group_mapping(key, value)
def start_last(self, attrs): def start_last(self, attrs):
self.name.prefix = attrs.get('prefix', '') """ This is the element in version < 1.4.0 to do the surname"""
self.surname = gen.lib.Surname()
self.surname.prefix = attrs.get('prefix', '')
self.name.group_as = attrs.get('group', '') self.name.group_as = attrs.get('group', '')
def start_patronymic(self, attrs):
""" This is the element in version < 1.4.0 to do the patronymic"""
self.surnamepat = gen.lib.Surname()
self.surnamepat.set_origintype(gen.lib.NameTypeOrigin(
gen.lib.NameTypeOrigin.PATRONYMIC))
def start_style(self, attrs): def start_style(self, attrs):
""" """
Styled text tag in notes (v1.4.0 onwards). Styled text tag in notes (v1.4.0 onwards).
@ -1927,14 +1963,8 @@ class GrampsParser(UpdateCallback):
self.num_places = 0 self.num_places = 0
def start_database(self, attrs): def start_database(self, attrs):
try: # we already parsed xml once in VersionParser to obtain version
# This is a proper way to get the XML version pass
xmlns = attrs.get('xmlns')
self.version_string = xmlns.split('/')[4]
except:
# Before we had a proper DTD, the version was hard to determine
# so we're setting it to 1.0.0
self.version_string = '1.0.0'
def start_pos(self, attrs): def start_pos(self, attrs):
self.person.position = (int(attrs["x"]), int(attrs["y"])) self.person.position = (int(attrs["x"]), int(attrs["y"]))
@ -2078,15 +2108,62 @@ class GrampsParser(UpdateCallback):
self.db.commit_note(note, self.trans, self.change) self.db.commit_note(note, self.trans, self.change)
self.info.add('new-object', NOTE_KEY, note) self.info.add('new-object', NOTE_KEY, note)
self.event.add_note(note.handle) self.event.add_note(note.handle)
elif self.alt_name: else:
# former aka tag -- alternate name #first correct old xml that has no nametype set
if self.name.get_type() == "": if self.alt_name:
self.name.set_type(gen.lib.NameType.AKA) # alternate name or former aka tag
self.person.add_alternate_name(self.name) if self.name.get_type() == "":
self.name.set_type(gen.lib.NameType.AKA)
else:
if self.name.get_type() == "":
self.name.set_type(gen.lib.NameType.BIRTH)
#same logic as bsddb upgrade for xml < 1.4.0 which will
#have a surnamepat and/or surname. From 1.4.0 surname has been
#added to name in self.stop_surname
if not self.surnamepat:
#no patronymic, only add surname if present
if self.surname:
self.name.add_surname(self.surname)
self.name.set_primary_surname(0)
else:
#a patronymic, if no surname, a single surname
if not self.surname:
self.name.add_surname(self.surnamepat)
self.name.set_primary_surname(0)
else:
#two surnames, first patronymic, then surname which is primary
self.name.add_surname(self.surnamepat)
self.name.add_surname(self.surname)
self.name.set_primary_surname(1)
if self.alt_name:
self.person.add_alternate_name(self.name)
else:
self.person.set_primary_name(self.name)
self.name = None
self.surname = None
self.surnamepat = None
def stop_aka(self, tag):
if self.name.get_type() == "":
self.name.set_type(gen.lib.NameType.AKA)
if not self.surnamepat:
#no patronymic, only add surname if present
if self.surname:
self.name.add_surname(self.surname)
self.name.set_primary_surname(0)
else: else:
if self.name.get_type() == "": #a patronymic, if no surname, a single surname
self.name.set_type(gen.lib.NameType.BIRTH) if not self.surname:
self.person.set_primary_name (self.name) self.name.add_surname(self.surnamepat)
self.name.set_primary_surname(0)
else:
#two surnames, first patronymic, then surname which is primary
self.name.add_surname(self.surnamepat)
self.name.add_surname(self.surname)
self.name.set_primary_surname(1)
self.person.add_alternate_name(self.name)
self.name = None self.name = None
def stop_rname(self, tag): def stop_rname(self, tag):
@ -2256,28 +2333,53 @@ class GrampsParser(UpdateCallback):
self.source_ref.add_note(note.handle) self.source_ref.add_note(note.handle)
def stop_last(self, tag): def stop_last(self, tag):
if self.surname:
self.surname.set_surname(tag)
if not tag.strip() and not self.surname.get_prefix().strip():
#consider empty surname as no surname
self.surname = None
def stop_surname(self, tag):
if self.name: if self.name:
self.name.set_surname(tag) self.surname.set_surname(tag)
self.name.add_surname(self.surname)
self.surname = None
def stop_group(self, tag):
""" group name of a name"""
if self.name:
self.name.set_group_as(tag)
def stop_suffix(self, tag): def stop_suffix(self, tag):
if self.name: if self.name:
self.name.set_suffix(tag) self.name.set_suffix(tag)
def stop_patronymic(self, tag): def stop_patronymic(self, tag):
if self.name: if self.surnamepat:
self.name.set_patronymic(tag) self.surnamepat.set_surname(tag)
if not tag.strip():
self.surnamepat = None
def stop_title(self, tag): def stop_title(self, tag):
if self.name: if self.name:
self.name.set_title(tag) self.name.set_title(tag)
def stop_nick(self, tag): def stop_nick(self, tag):
if self.person: """in < 1.3.0 nick is on person and mapped to attribute
from 1.4.0 it is a name element
"""
if self.name:
self.name.set_nick_name(tag)
elif self.person:
attr = gen.lib.Attribute() attr = gen.lib.Attribute()
attr.set_type(gen.lib.AttributeType.NICKNAME) attr.set_type(gen.lib.AttributeType.NICKNAME)
attr.set_value(tag) attr.set_value(tag)
self.person.add_attribute(attr) self.person.add_attribute(attr)
def stop_familynick(self, tag):
if self.name:
self.name.set_family_nick_name(tag)
def stop_text(self, tag): def stop_text(self, tag):
self.note_text = tag self.note_text = tag
@ -2379,12 +2481,6 @@ class GrampsParser(UpdateCallback):
elif self.in_scomments: elif self.in_scomments:
self.scomments_list.append(tag) self.scomments_list.append(tag)
def stop_aka(self, tag):
self.person.add_alternate_name(self.name)
if self.name.get_type() == "":
self.name.set_type(gen.lib.NameType.AKA)
self.name = None
def startElement(self, tag, attrs): def startElement(self, tag, attrs):
self.func_list[self.func_index] = (self.func, self.tlist) self.func_list[self.func_index] = (self.func, self.tlist)
self.func_index += 1 self.func_index += 1
@ -2469,7 +2565,6 @@ class VersionParser(object):
self.__p.StartElementHandler = self.__element_handler self.__p.StartElementHandler = self.__element_handler
self.__gramps_version = 'unknown' self.__gramps_version = 'unknown'
self.__xml_version = '1.0.0' self.__xml_version = '1.0.0'
xml_file.seek(0) xml_file.seek(0)
self.__p.ParseFile(xml_file) self.__p.ParseFile(xml_file)
@ -2541,25 +2636,25 @@ def open_file(filename, cli):
return xml_file return xml_file
def version_is_valid(filename, cli): def version_is_valid(versionparser, cli):
""" """
Validate the xml version. Validate the xml version.
:param versionparser: A VersionParser object to work with
""" """
parser = VersionParser(filename)
if parser.get_xmlns_version() > libgrampsxml.GRAMPS_XML_VERSION: if versionparser.get_xmlns_version() > libgrampsxml.GRAMPS_XML_VERSION:
msg = _("The .gramps file you are importing was made by version %(newer)s of " msg = _("The .gramps file you are importing was made by version %(newer)s of "
"Gramps, while you are running an older version %(older)s. " "Gramps, while you are running an older version %(older)s. "
"The file will not be imported. Please upgrade to the latest " "The file will not be imported. Please upgrade to the latest "
"version of Gramps and try again." ) % { "version of Gramps and try again." ) % {
'newer' : parser.get_gramps_version(), 'older' : const.VERSION } 'newer' : versionparser.get_gramps_version(), 'older' : const.VERSION }
if cli: if cli:
LOG.warn(msg) LOG.warn(msg)
return False return False
else: else:
ErrorDialog(msg) ErrorDialog(msg)
return False return False
if parser.get_xmlns_version() < '1.0.0': if versionparser.get_xmlns_version() < '1.0.0':
msg = _("The .gramps file you are importing was made by version " msg = _("The .gramps file you are importing was made by version "
"%(oldgramps)s of Gramps, while you are running a more " "%(oldgramps)s of Gramps, while you are running a more "
"recent version %(newgramps)s.\n\n" "recent version %(newgramps)s.\n\n"
@ -2567,9 +2662,9 @@ def version_is_valid(filename, cli):
" Gramps that supports version %(xmlversion)s of the xml.\nSee" " Gramps that supports version %(xmlversion)s of the xml.\nSee"
"\n http://gramps-project.org/wiki/index.php?title=GRAMPS_XML\n " "\n http://gramps-project.org/wiki/index.php?title=GRAMPS_XML\n "
"for more info." "for more info."
) % {'oldgramps': parser.get_gramps_version(), ) % {'oldgramps': versionparser.get_gramps_version(),
'newgramps': const.VERSION, 'newgramps': const.VERSION,
'xmlversion': parser.get_xmlns_version(), 'xmlversion': versionparser.get_xmlns_version(),
} }
if cli: if cli:
LOG.warn(msg) LOG.warn(msg)
@ -2577,7 +2672,7 @@ def version_is_valid(filename, cli):
else: else:
ErrorDialog(_('The file will not be imported'), msg) ErrorDialog(_('The file will not be imported'), msg)
return False return False
elif parser.get_xmlns_version() < '1.1.0': elif versionparser.get_xmlns_version() < '1.1.0':
msg = _("The .gramps file you are importing was made by version " msg = _("The .gramps file you are importing was made by version "
"%(oldgramps)s of Gramps, while you are running a much " "%(oldgramps)s of Gramps, while you are running a much "
"more recent version %(newgramps)s.\n\n" "more recent version %(newgramps)s.\n\n"
@ -2587,9 +2682,9 @@ def version_is_valid(filename, cli):
"is version %(xmlversion)s of the xml.\nSee" "is version %(xmlversion)s of the xml.\nSee"
"\n http://gramps-project.org/wiki/index.php?title=GRAMPS_XML\n" "\n http://gramps-project.org/wiki/index.php?title=GRAMPS_XML\n"
"for more info." "for more info."
) % {'oldgramps': parser.get_gramps_version(), ) % {'oldgramps': versionparser.get_gramps_version(),
'newgramps': const.VERSION, 'newgramps': const.VERSION,
'xmlversion': parser.get_xmlns_version(), 'xmlversion': versionparser.get_xmlns_version(),
} }
if cli: if cli:
LOG.warn(msg) LOG.warn(msg)

View File

@ -1673,16 +1673,25 @@ class GedcomParser(UpdateCallback):
match = SURNAME_RE.match(text) match = SURNAME_RE.match(text)
if match: if match:
#/surname/ extra, we assume extra is given name
names = match.groups() names = match.groups()
name.set_first_name(names[1].strip()) name.set_first_name(names[1].strip())
name.set_surname(names[0].strip()) surn = gen.lib.Surname()
surn.set_surname(names[0].strip())
surn.set_primary()
name.set_surname_list([surn])
else: else:
try: try:
names = NAME_RE.match(text).groups() names = NAME_RE.match(text).groups()
# given /surname/ extra, we assume extra is suffix
name.set_first_name(names[0].strip()) name.set_first_name(names[0].strip())
name.set_surname(names[2].strip()) surn = gen.lib.Surname()
surn.set_surname(names[2].strip())
surn.set_primary()
name.set_surname_list([surn])
name.set_suffix(names[4].strip()) name.set_suffix(names[4].strip())
except: except:
# something strange, set as first name
name.set_first_name(text.strip()) name.set_first_name(text.strip())
return name return name
@ -2782,7 +2791,7 @@ class GedcomParser(UpdateCallback):
sub_state.name = name sub_state.name = name
sub_state.level = 2 sub_state.level = 2
self.__parse_level(sub_state, self.name_parse_tbl, self.__undefined) self.__parse_level(sub_state, self.name_parse_tbl, self.__undefined)
def __person_object(self, line, state): def __person_object(self, line, state):
""" """
@ -3164,7 +3173,13 @@ class GedcomParser(UpdateCallback):
@param state: The current state @param state: The current state
@type state: CurrentState @type state: CurrentState
""" """
state.name.set_surname_prefix(line.data.strip()) if state.name.get_surname_list():
state.name.get_surname_list()[0].set_prefix(line.data.strip())
else:
surn = gen.lib.Surname()
surn.set_prefix(line.data.strip())
surn.set_primary()
state.name.set_surname_list([surn])
self.__skip_subordinate_levels(state.level+1) self.__skip_subordinate_levels(state.level+1)
def __name_surn(self, line, state): def __name_surn(self, line, state):
@ -3174,7 +3189,13 @@ class GedcomParser(UpdateCallback):
@param state: The current state @param state: The current state
@type state: CurrentState @type state: CurrentState
""" """
state.name.set_surname(line.data.strip()) if state.name.get_surname_list():
state.name.get_surname_list()[0].set_surname(line.data.strip())
else:
surn = gen.lib.Surname()
surn.set_surname(line.data.strip())
surn.set_primary()
state.name.set_surname_list([surn])
self.__skip_subordinate_levels(state.level+1) self.__skip_subordinate_levels(state.level+1)
def __name_marnm(self, line, state): def __name_marnm(self, line, state):
@ -3188,7 +3209,10 @@ class GedcomParser(UpdateCallback):
data = text.split() data = text.split()
if len(data) == 1: if len(data) == 1:
name = gen.lib.Name(state.person.primary_name) name = gen.lib.Name(state.person.primary_name)
name.set_surname(data[0].strip()) surn = gen.lib.Surname()
surn.set_surname(data[0].strip())
surn.set_primary()
name.set_surname_list([surn])
name.set_type(gen.lib.NameType.MARRIED) name.set_type(gen.lib.NameType.MARRIED)
state.person.add_alternate_name(name) state.person.add_alternate_name(name)
elif len(data) > 1: elif len(data) > 1:
@ -3203,8 +3227,12 @@ class GedcomParser(UpdateCallback):
@param state: The current state @param state: The current state
@type state: CurrentState @type state: CurrentState
""" """
if state.name.get_suffix() == "": if state.name.get_suffix() == "" or state.name.get_suffix() == line.data:
#suffix might be set before when parsing name string
state.name.set_suffix(line.data) state.name.set_suffix(line.data)
else:
#previously set suffix different, to not loose information, append
state.name.set_suffix(state.name.get_suffix() + ' ' + line.data)
self.__skip_subordinate_levels(state.level+1) self.__skip_subordinate_levels(state.level+1)
def __name_nick(self, line, state): def __name_nick(self, line, state):
@ -3214,10 +3242,7 @@ class GedcomParser(UpdateCallback):
@param state: The current state @param state: The current state
@type state: CurrentState @type state: CurrentState
""" """
attr = gen.lib.Attribute() state.name.set_nick_name(line.data.strip())
attr.set_type(gen.lib.AttributeType.NICKNAME)
attr.set_value(line.data)
state.person.add_attribute(attr)
self.__skip_subordinate_levels(state.level+1) self.__skip_subordinate_levels(state.level+1)
def __name_aka(self, line, state): def __name_aka(self, line, state):

View File

@ -211,6 +211,7 @@ class DbGrdb(Callback):
self.family_rel_types = set() self.family_rel_types = set()
self.event_role_names = set() self.event_role_names = set()
self.name_types = set() self.name_types = set()
self.origin_types = set()
self.repository_types = set() self.repository_types = set()
self.note_types = set() self.note_types = set()
self.source_media_types = set() self.source_media_types = set()

View File

@ -259,9 +259,12 @@ class BasePersonView(ListView):
Add a new person to the database. Add a new person to the database.
""" """
person = gen.lib.Person() person = gen.lib.Person()
#the editor requires a surname
person.primary_name.add_surname(gen.lib.Surname())
person.primary_name.set_primary_surname(0)
try: try:
EditPerson(self.dbstate, self.uistate, [], gen.lib.Person()) EditPerson(self.dbstate, self.uistate, [], person)
except Errors.WindowActiveError: except Errors.WindowActiveError:
pass pass

View File

@ -54,7 +54,7 @@ class SameSurname(Rule):
def apply(self, db, person): def apply(self, db, person):
src = self.list[0].upper() src = self.list[0].upper()
for name in [person.get_primary_name()] + person.get_alternate_names(): for name in [person.get_primary_name()] + person.get_alternate_names():
if name.surname and name.surname.upper() == src.upper(): if name.get_surname() and name.get_surname().upper() == src.upper():
return True return True
return False return False
@ -103,7 +103,7 @@ def run(database, document, person):
surname = person surname = person
rsurname = person rsurname = person
# display the title # display the title
sdoc.title(_("People with the surname '%s'") % surname) sdoc.title(_("People sharing the surname '%s'") % surname)
sdoc.paragraph("") sdoc.paragraph("")
stab.columns(_("Person"), _("Birth Date"), _("Name type")) stab.columns(_("Person"), _("Birth Date"), _("Name type"))
filter = GenericFilterFactory('Person')() filter = GenericFilterFactory('Person')()

View File

@ -37,6 +37,7 @@ import gtk
# gramps modules # gramps modules
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
from gen.db import find_surname_name
import const import const
from gui.utils import ProgressMeter from gui.utils import ProgressMeter
import GrampsDisplay import GrampsDisplay
@ -243,11 +244,12 @@ class ChangeNames(tool.BatchTool, ManagedWindow.ManagedWindow):
#person = Person(data) #person = Person(data)
change = False change = False
for name in [person.get_primary_name()] + person.get_alternate_names(): for name in [person.get_primary_name()] + person.get_alternate_names():
sname = name.get_surname() sname = find_surname_name(handle, name.serialize())
if sname in changelist: if sname in changelist:
change = True change = True
sname = self.name_cap(sname) for surn in name.get_surname_list():
name.set_surname(sname) sname = self.name_cap(surn.get_surname())
surn.set_surname(sname)
if change: if change:
#cursor.update(handle, person.serialize()) #cursor.update(handle, person.serialize())
self.db.commit_person(person, transaction=self.trans) self.db.commit_person(person, transaction=self.trans)

View File

@ -144,7 +144,7 @@ class Merge(tool.Tool,ManagedWindow.ManagedWindow):
GrampsDisplay.help(WIKI_HELP_PAGE , WIKI_HELP_SEC) GrampsDisplay.help(WIKI_HELP_PAGE , WIKI_HELP_SEC)
def ancestors_of(self,p1_id,id_list): def ancestors_of(self, p1_id, id_list):
if (not p1_id) or (p1_id in id_list): if (not p1_id) or (p1_id in id_list):
return return
id_list.append(p1_id) id_list.append(p1_id)
@ -181,7 +181,7 @@ class Merge(tool.Tool,ManagedWindow.ManagedWindow):
except Errors.WindowActiveError: except Errors.WindowActiveError:
pass pass
def find_potentials(self,thresh): def find_potentials(self, thresh):
self.progress = ProgressMeter(_('Find Duplicates'), self.progress = ProgressMeter(_('Find Duplicates'),
_('Looking for duplicate people')) _('Looking for duplicate people'))
@ -197,7 +197,7 @@ class Merge(tool.Tool,ManagedWindow.ManagedWindow):
for p1_id in self.db.iter_person_handles(): for p1_id in self.db.iter_person_handles():
self.progress.step() self.progress.step()
p1 = self.db.get_person_from_handle(p1_id) p1 = self.db.get_person_from_handle(p1_id)
key = self.gen_key(p1.get_primary_name().get_surname()) key = self.gen_key(get_surnames(p1.get_primary_name()))
if p1.get_gender() == gen.lib.Person.MALE: if p1.get_gender() == gen.lib.Person.MALE:
if key in males: if key in males:
males[key].append(p1_id) males[key].append(p1_id)
@ -216,7 +216,7 @@ class Merge(tool.Tool,ManagedWindow.ManagedWindow):
self.progress.step() self.progress.step()
p1 = self.db.get_person_from_handle(p1key) p1 = self.db.get_person_from_handle(p1key)
key = self.gen_key(p1.get_primary_name().get_surname()) key = self.gen_key(get_surnames(p1.get_primary_name()))
if p1.get_gender() == gen.lib.Person.MALE: if p1.get_gender() == gen.lib.Person.MALE:
remaining = males[key] remaining = males[key]
else: else:
@ -246,7 +246,7 @@ class Merge(tool.Tool,ManagedWindow.ManagedWindow):
self.length = len(self.list) self.length = len(self.list)
self.progress.close() self.progress.close()
def gen_key(self,val): def gen_key(self, val):
if self.use_soundex: if self.use_soundex:
try: try:
return soundex.soundex(val) return soundex.soundex(val)
@ -255,7 +255,7 @@ class Merge(tool.Tool,ManagedWindow.ManagedWindow):
else: else:
return val return val
def compare_people(self,p1,p2): def compare_people(self, p1, p2):
name1 = p1.get_primary_name() name1 = p1.get_primary_name()
name2 = p2.get_primary_name() name2 = p2.get_primary_name()
@ -397,7 +397,7 @@ class Merge(tool.Tool,ManagedWindow.ManagedWindow):
chance += value chance += value
return chance return chance
def name_compare(self,s1,s2): def name_compare(self, s1, s2):
if self.use_soundex: if self.use_soundex:
try: try:
return soundex.compare(s1,s2) return soundex.compare(s1,s2)
@ -406,7 +406,7 @@ class Merge(tool.Tool,ManagedWindow.ManagedWindow):
else: else:
return s1 == s2 return s1 == s2
def date_match(self,date1,date2): def date_match(self, date1, date2):
if date1.is_empty() or date2.is_empty(): if date1.is_empty() or date2.is_empty():
return 0 return 0
if date1.is_equal(date2): if date1.is_equal(date2):
@ -425,7 +425,7 @@ class Merge(tool.Tool,ManagedWindow.ManagedWindow):
else: else:
return -1 return -1
def range_compare(self,date1,date2): def range_compare(self, date1, date2):
start_date_1 = date1.get_start_date()[0:3] start_date_1 = date1.get_start_date()[0:3]
start_date_2 = date2.get_start_date()[0:3] start_date_2 = date2.get_start_date()[0:3]
stop_date_1 = date1.get_stop_date()[0:3] stop_date_1 = date1.get_stop_date()[0:3]
@ -454,9 +454,9 @@ class Merge(tool.Tool,ManagedWindow.ManagedWindow):
if not name1 or not name: if not name1 or not name:
return 0 return 0
srn1 = name.get_surname() srn1 = get_surnames(name)
sfx1 = name.get_suffix() sfx1 = name.get_suffix()
srn2 = name1.get_surname() srn2 = get_surnames(name1)
sfx2 = name1.get_suffix() sfx2 = name1.get_suffix()
if not self.name_compare(srn1,srn2): if not self.name_compare(srn1,srn2):
@ -476,7 +476,7 @@ class Merge(tool.Tool,ManagedWindow.ManagedWindow):
else: else:
return self.list_reduce(list2,list1) return self.list_reduce(list2,list1)
def place_match(self,p1_id,p2_id): def place_match(self, p1_id, p2_id):
if p1_id == p2_id: if p1_id == p2_id:
return 1 return 1
@ -509,7 +509,7 @@ class Merge(tool.Tool,ManagedWindow.ManagedWindow):
value += 0.25 value += 0.25
return min(value,1) if value else -1 return min(value,1) if value else -1
def list_reduce(self,list1,list2): def list_reduce(self, list1, list2):
value = 0 value = 0
for name in list1: for name in list1:
for name2 in list2: for name2 in list2:
@ -526,7 +526,7 @@ class Merge(tool.Tool,ManagedWindow.ManagedWindow):
class ShowMatches(ManagedWindow.ManagedWindow): class ShowMatches(ManagedWindow.ManagedWindow):
def __init__(self,dbstate,uistate,track,the_list,the_map,callback): def __init__(self, dbstate, uistate, track, the_list, the_map, callback):
ManagedWindow.ManagedWindow.__init__(self,uistate,track,self.__class__) ManagedWindow.ManagedWindow.__init__(self,uistate,track,self.__class__)
self.dellist = {} self.dellist = {}
@ -632,7 +632,11 @@ def get_name_obj(person):
return person.get_primary_name() return person.get_primary_name()
else: else:
return None return None
def get_surnames(name):
"""Construct a full surname of the surnames"""
' '.join([surn.get_surname() for surn in name.get_surname_list()])
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# #
# #

View File

@ -70,24 +70,19 @@ WIKI_HELP_SEC = _('manual|Extract_Information_from_Names')
# List of possible surname prefixes. Notice that you must run the tool # List of possible surname prefixes. Notice that you must run the tool
# multiple times for prefixes such as "van der". # multiple times for prefixes such as "van der".
prefix_list = [ PREFIX_LIST = [
"de", "van", "von", "di", "le", "du", "dela", "della", "de", "van", "von", "di", "le", "du", "dela", "della",
"des", "vande", "ten", "da", "af", "den", "das", "dello", "des", "vande", "ten", "da", "af", "den", "das", "dello",
"del", "en", "ein", "el" "et", "les", "lo", "los", "un", "del", "en", "ein", "el" "et", "les", "lo", "los", "un",
"um", "una", "uno", "der", "ter", "te", "die", "um", "una", "uno", "der", "ter", "te", "die",
] ]
CONNECTOR_LIST = ['e', 'y', ]
CONNECTOR_LIST_NONSPLIT = ['de', 'van']
_title_re = re.compile(r"^ ([A-Za-z][A-Za-z]+\.) \s+ (.+) $", re.VERBOSE) _title_re = re.compile(r"^ ([A-Za-z][A-Za-z]+\.) \s+ (.+) $", re.VERBOSE)
_nick_re = re.compile(r"(.+) \s* [(\"] (.+) [)\"]", re.VERBOSE) _nick_re = re.compile(r"(.+) \s* [(\"] (.+) [)\"]", re.VERBOSE)
# Find a prefix in the first_name
_fn_prefix_re = re.compile("(\S+)\s+(%s)\s*$" % '|'.join(prefix_list),
re.IGNORECASE)
# Find a prefix in the surname
_sn_prefix_re = re.compile("^\s*(%s)\s+(.+)" % '|'.join(prefix_list),
re.IGNORECASE)
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# #
@ -101,7 +96,11 @@ _sn_prefix_re = re.compile("^\s*(%s)\s+(.+)" % '|'.join(prefix_list),
class PatchNames(tool.BatchTool, ManagedWindow.ManagedWindow): class PatchNames(tool.BatchTool, ManagedWindow.ManagedWindow):
titleid = 1
nickid = 2
pref1id = 3
compid = 4
def __init__(self, dbstate, uistate, options_class, name, callback=None): def __init__(self, dbstate, uistate, options_class, name, callback=None):
self.label = _('Name and title extraction tool') self.label = _('Name and title extraction tool')
ManagedWindow.ManagedWindow.__init__(self, uistate, [], self.__class__) ManagedWindow.ManagedWindow.__init__(self, uistate, [], self.__class__)
@ -110,12 +109,63 @@ class PatchNames(tool.BatchTool, ManagedWindow.ManagedWindow):
tool.BatchTool.__init__(self, dbstate, options_class, name) tool.BatchTool.__init__(self, dbstate, options_class, name)
if self.fail: if self.fail:
return return
winprefix = gtk.Dialog("Default prefix and connector settings",
self.uistate.window,
gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
(gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
winprefix.set_has_separator(False)
winprefix.vbox.set_spacing(5)
hboxpref = gtk.HBox()
hboxpref.pack_start(gtk.Label(_('Prefixes to search for:')),
expand=False, padding=5)
self.prefixbox = gtk.Entry()
self.prefixbox.set_text(', '.join(PREFIX_LIST))
hboxpref.pack_start(self.prefixbox)
winprefix.vbox.pack_start(hboxpref)
hboxcon = gtk.HBox()
hboxcon.pack_start(gtk.Label(_('Connectors splitting surnames:')),
expand=False, padding=5)
self.conbox = gtk.Entry()
self.conbox.set_text(', '.join(CONNECTOR_LIST))
hboxcon.pack_start(self.conbox)
winprefix.vbox.pack_start(hboxcon)
hboxconns = gtk.HBox()
hboxconns.pack_start(gtk.Label(_('Connectors not splitting surnames:')),
expand=False, padding=5)
self.connsbox = gtk.Entry()
self.connsbox.set_text(', '.join(CONNECTOR_LIST_NONSPLIT))
hboxconns.pack_start(self.connsbox)
winprefix.vbox.pack_start(hboxconns)
winprefix.show_all()
winprefix.resize(700, 100)
response = winprefix.run()
self.prefix_list = self.prefixbox.get_text().split(',')
self.prefix_list = map(strip, self.prefix_list)
self.prefixbox = None
self.connector_list = self.conbox.get_text().split(',')
self.connector_list = map(strip, self.connector_list)
self.conbox = None
self.connector_list_nonsplit = self.connsbox.get_text().split(',')
self.connector_list_nonsplit = map(strip, self.connector_list_nonsplit)
self.connsbox = None
# Find a prefix in the first_name
self._fn_prefix_re = re.compile("(\S+)\s+(%s)\s*$" % '|'.join(self.prefix_list),
re.IGNORECASE)
# Find a prefix in the surname
self._sn_prefix_re = re.compile("^\s*(%s)\s+(.+)" % '|'.join(self.prefix_list),
re.IGNORECASE)
# Find a connector in the surname
self._sn_con_re = re.compile("^\s*(.+)\s+(%s)\s+(.+)" % '|'.join(self.connector_list),
re.IGNORECASE)
winprefix.destroy()
self.cb = callback self.cb = callback
self.title_list = [] self.handle_to_action = {}
self.nick_list = []
self.prefix1_list = []
self.prefix2_list = []
self.progress = ProgressMeter( self.progress = ProgressMeter(
_('Extracting Information from Names'), '') _('Extracting Information from Names'), '')
@ -127,6 +177,18 @@ class PatchNames(tool.BatchTool, ManagedWindow.ManagedWindow):
name = person.get_primary_name() name = person.get_primary_name()
first = name.get_first_name() first = name.get_first_name()
sname = name.get_surname() sname = name.get_surname()
old_prefix = []
old_surn = []
old_con = []
old_prim = []
old_orig = []
for surn in name.get_surname_list():
old_prefix.append(surn.get_prefix())
old_surn.append(surn.get_surname())
old_con.append(surn.get_connector())
old_prim.append(surn.get_primary())
old_orig.append(surn.get_origintype())
if name.get_title(): if name.get_title():
old_title = [name.get_title()] old_title = [name.get_title()]
@ -140,46 +202,160 @@ class PatchNames(tool.BatchTool, ManagedWindow.ManagedWindow):
first = groups[1] first = groups[1]
new_title.append(groups[0]) new_title.append(groups[0])
match = _title_re.match(first) match = _title_re.match(first)
matchnick = _nick_re.match(first)
if new_title: if new_title:
self.title_list.append((key, " ".join(old_title+new_title), titleval = (" ".join(old_title+new_title), first)
first)) if key in self.handle_to_action:
continue self.handle_to_action[key][self.titleid] = titleval
match = _nick_re.match(first)
if match:
groups = match.groups()
self.nick_list.append((key, groups[0], groups[1]))
continue
old_prefix = name.get_surname_prefix()
# First try to find the name prefix in the first_name
match = _fn_prefix_re.match(first)
if match:
groups = match.groups()
if old_prefix:
# Put the found prefix before the old prefix
new_prefix = " ".join([groups[1], old_prefix])
else: else:
new_prefix = groups[1] self.handle_to_action[key] = {self.titleid: titleval}
self.prefix1_list.append((key, groups[0], new_prefix)) elif matchnick:
continue # we check for nick, which changes given name like title
groups = matchnick.groups()
# Next, try to find the name prefix in the surname nickval = (groups[0], groups[1])
match = _sn_prefix_re.match(sname) if key in self.handle_to_action:
if match: self.handle_to_action[key][self.nickid] = nickval
groups = match.groups()
if old_prefix:
# Put the found prefix after the old prefix
new_prefix = " ".join([old_prefix, groups[0]])
else: else:
new_prefix = groups[0] self.handle_to_action[key] = {self.nickid: nickval}
self.prefix2_list.append((key, groups[1], new_prefix)) else:
# Try to find the name prefix in the given name, also this
# changes given name
match = self._fn_prefix_re.match(first)
if match:
groups = match.groups()
if old_prefix[0]:
# Put the found prefix before the old prefix
new_prefix = " ".join([groups[1], old_prefix[0]])
else:
new_prefix = groups[1]
pref1val = (groups[0], new_prefix, groups[1])
if key in self.handle_to_action:
self.handle_to_action[key][self.pref1id] = pref1val
else:
self.handle_to_action[key] = {self.pref1id: pref1val}
#check for Gedcom import of compound surnames
if len(old_surn) == 1 and old_con[0] == '':
prefixes = old_prefix[0].split(',')
surnames = old_surn[0].split(',')
if len(prefixes) > 1 and len(prefixes) == len(surnames):
#assume a list of prefix and a list of surnames
prefixes = map(strip, prefixes)
surnames = map(strip, surnames)
primaries = [False] * len(prefixes)
primaries[0] = True
origs = []
for ind in range(len(prefixes)):
origs.append(gen.lib.NameOriginType())
origs[0] = old_orig[0]
compoundval = (surnames, prefixes, ['']*len(prefixes),
primaries, origs)
if key in self.handle_to_action:
self.handle_to_action[key][self.compid] = compoundval
else:
self.handle_to_action[key] = {self.compid: compoundval}
#we cannot check compound surnames, so continue the loop
continue
# Next, try to split surname in compounds: prefix surname connector
found = False
new_prefix_list = []
new_surname_list = []
new_connector_list = []
new_prim_list = []
new_orig_list = []
ind = 0
cont = True
for pref, surn, con, prim, orig in zip(old_prefix, old_surn,
old_con, old_prim, old_orig):
surnval = surn.split()
if surnval == []:
new_prefix_list.append(pref)
new_surname_list.append('')
new_connector_list.append(con)
new_prim_list.append(prim)
new_orig_list.append(orig)
cont = False
continue
val = surnval.pop(0)
while cont:
new_prefix_list.append(pref)
new_surname_list.append('')
new_connector_list.append(con)
new_prim_list.append(prim)
new_orig_list.append(orig)
while cont and (val.lower() in self.prefix_list):
found = True
if new_prefix_list[-1]:
new_prefix_list[-1] += ' ' + val
else:
new_prefix_list[-1] = val
try:
val = surnval.pop(0)
except IndexError:
val = ''
cont = False
#after prefix we have a surname
if cont:
new_surname_list[-1] = val
try:
val = surnval.pop(0)
except IndexError:
val = ''
cont = False
#if value after surname indicates continue, then continue
while cont and (val.lower() in self.connector_list_nonsplit):
#add this val to the current surname
new_surname_list[-1] += ' ' + val
try:
val = surnval.pop(0)
except IndexError:
val = ''
cont = False
# if previous is non-splitting connector, then add new val to
# current surname
if cont and (new_surname_list[-1].split()[-1].lower() \
in self.connector_list_nonsplit):
new_surname_list[-1] += ' ' + val
try:
val = surnval.pop(0)
except IndexError:
val = ''
cont = False
#if next is a connector, add it to the surname
if cont and val.lower() in self.connector_list:
found = True
if new_connector_list[-1]:
new_connector_list[-1] = ' ' + val
else:
new_connector_list[-1] = val
try:
val = surnval.pop(0)
except IndexError:
val = ''
cont = False
#initialize for a next surname in case there are still
#val
if cont:
found = True # we split surname
pref=''
con = ''
prim = False
orig = gen.lib.NameOriginType()
ind += 1
if found:
compoundval = (new_surname_list, new_prefix_list,
new_connector_list, new_prim_list, new_orig_list)
if key in self.handle_to_action:
self.handle_to_action[key][self.compid] = compoundval
else:
self.handle_to_action[key] = {self.compid: compoundval}
self.progress.step() self.progress.step()
if self.nick_list or self.title_list or self.prefix1_list or self.prefix2_list: if self.handle_to_action:
self.display() self.display()
else: else:
self.progress.close() self.progress.close()
@ -228,7 +404,7 @@ class PatchNames(tool.BatchTool, ManagedWindow.ManagedWindow):
c = gtk.TreeViewColumn(_('Value'), gtk.CellRendererText(), text=3) c = gtk.TreeViewColumn(_('Value'), gtk.CellRendererText(), text=3)
self.list.append_column(c) self.list.append_column(c)
c = gtk.TreeViewColumn(_('Name'), gtk.CellRendererText(), text=4) c = gtk.TreeViewColumn(_('Current Name'), gtk.CellRendererText(), text=4)
self.list.append_column(c) self.list.append_column(c)
self.list.set_model(self.model) self.list.set_model(self.model)
@ -236,58 +412,65 @@ class PatchNames(tool.BatchTool, ManagedWindow.ManagedWindow):
self.nick_hash = {} self.nick_hash = {}
self.title_hash = {} self.title_hash = {}
self.prefix1_hash = {} self.prefix1_hash = {}
self.prefix2_hash = {} self.compound_hash = {}
self.progress.set_pass(_('Building display'), self.progress.set_pass(_('Building display'),
len(self.nick_list)+len(self.title_list) len(self.handle_to_action.keys()))
+len(self.prefix1_list)+len(self.prefix2_list))
for (pid, name, nick) in self.nick_list: for key, data in self.handle_to_action.items():
p = self.db.get_person_from_handle(pid) p = self.db.get_person_from_handle(key)
gid = p.get_gramps_id() gid = p.get_gramps_id()
handle = self.model.append() if self.nickid in data:
self.model.set_value(handle, 0, 1) given, nick = data[self.nickid]
self.model.set_value(handle, 1, gid) handle = self.model.append()
self.model.set_value(handle, 2, _('Nickname')) self.model.set_value(handle, 0, 1)
self.model.set_value(handle, 3, nick) self.model.set_value(handle, 1, gid)
self.model.set_value(handle, 4, p.get_primary_name().get_name()) self.model.set_value(handle, 2, _('Nickname'))
self.nick_hash[pid] = handle self.model.set_value(handle, 3, nick)
self.progress.step() self.model.set_value(handle, 4, p.get_primary_name().get_name())
self.nick_hash[key] = handle
for (pid, title, name) in self.title_list:
p = self.db.get_person_from_handle(pid) if self.titleid in data:
gid = p.get_gramps_id() title, given = data[self.titleid]
handle = self.model.append() handle = self.model.append()
self.model.set_value(handle, 0, 1) self.model.set_value(handle, 0, 1)
self.model.set_value(handle, 1, gid) self.model.set_value(handle, 1, gid)
self.model.set_value(handle, 2, _('Person|Title')) self.model.set_value(handle, 2, _('Person|Title'))
self.model.set_value(handle, 3, title) self.model.set_value(handle, 3, title)
self.model.set_value(handle, 4, p.get_primary_name().get_name()) self.model.set_value(handle, 4, p.get_primary_name().get_name())
self.title_hash[pid] = handle self.title_hash[key] = handle
self.progress.step()
if self.pref1id in data:
for (pid, fname, prefix) in self.prefix1_list: given, prefixtotal, new_prefix = data[self.pref1id]
p = self.db.get_person_from_handle(pid) handle = self.model.append()
gid = p.get_gramps_id() self.model.set_value(handle, 0, 1)
handle = self.model.append() self.model.set_value(handle, 1, gid)
self.model.set_value(handle, 0, 1) self.model.set_value(handle, 2, _('Prefix in given name'))
self.model.set_value(handle, 1, gid) self.model.set_value(handle, 3, prefixtotal)
self.model.set_value(handle, 2, _('Prefix')) self.model.set_value(handle, 4, p.get_primary_name().get_name())
self.model.set_value(handle, 3, prefix) self.prefix1_hash[key] = handle
self.model.set_value(handle, 4, p.get_primary_name().get_name())
self.prefix1_hash[pid] = handle if self.compid in data:
self.progress.step() surn_list, pref_list, con_list, prims, origs = data[self.compid]
handle = self.model.append()
for (pid, sname, prefix) in self.prefix2_list: self.model.set_value(handle, 0, 1)
p = self.db.get_person_from_handle(pid) self.model.set_value(handle, 1, gid)
gid = p.get_gramps_id() self.model.set_value(handle, 2, _('Compound surname'))
handle = self.model.append() newval = ''
self.model.set_value(handle, 0, 1) for sur, pre, con in zip(surn_list, pref_list, con_list):
self.model.set_value(handle, 1, gid) if newval:
self.model.set_value(handle, 2, _('Prefix')) newval += '-['
self.model.set_value(handle, 3, prefix) else:
self.model.set_value(handle, 4, p.get_primary_name().get_name()) newval = '['
self.prefix2_hash[pid] = handle newval += pre + ',' + sur
if con:
newval += ',' + con + ']'
else:
newval += ']'
self.model.set_value(handle, 3, newval)
self.model.set_value(handle, 4, p.get_primary_name().get_name())
self.compound_hash[key] = handle
self.progress.step() self.progress.step()
self.progress.close() self.progress.close()
@ -300,49 +483,58 @@ class PatchNames(tool.BatchTool, ManagedWindow.ManagedWindow):
def on_ok_clicked(self, obj): def on_ok_clicked(self, obj):
trans = self.db.transaction_begin("", batch=True) trans = self.db.transaction_begin("", batch=True)
self.db.disable_signals() self.db.disable_signals()
for grp in self.nick_list:
handle = self.nick_hash[grp[0]]
val = self.model.get_value(handle, 0)
if val:
p = self.db.get_person_from_handle(grp[0])
name = p.get_primary_name()
name.set_first_name(grp[1].strip())
nick_name = grp[2].strip()
attr = gen.lib.Attribute()
attr.set_type(gen.lib.AttributeType.NICKNAME)
attr.set_value(nick_name)
p.add_attribute(attr)
self.db.commit_person(p, trans)
for grp in self.title_list: for key, data in self.handle_to_action.items():
handle = self.title_hash[grp[0]] p = self.db.get_person_from_handle(key)
val = self.model.get_value(handle, 0) if self.nickid in data:
if val: modelhandle = self.nick_hash[key]
p = self.db.get_person_from_handle(grp[0]) val = self.model.get_value(modelhandle, 0)
name = p.get_primary_name() if val:
name.set_first_name(grp[2].strip()) given, nick = data[self.nickid]
name.set_title(grp[1].strip()) name = p.get_primary_name()
self.db.commit_person(p, trans) name.set_first_name(given.strip())
name.set_nick_name(nick.strip())
for grp in self.prefix1_list:
handle = self.prefix1_hash[grp[0]] if self.titleid in data:
val = self.model.get_value(handle, 0) modelhandle = self.title_hash[key]
if val: val = self.model.get_value(modelhandle, 0)
p = self.db.get_person_from_handle(grp[0]) if val:
name = p.get_primary_name() title, given = data[self.titleid]
name.set_first_name(grp[1].strip()) name = p.get_primary_name()
name.set_surname_prefix(grp[2].strip()) name.set_first_name(given.strip())
self.db.commit_person(p, trans) name.set_title(title.strip())
for grp in self.prefix2_list: if self.pref1id in data:
handle = self.prefix2_hash[grp[0]] modelhandle = self.prefix1_hash[key]
val = self.model.get_value(handle, 0) val = self.model.get_value(modelhandle, 0)
if val: if val:
p = self.db.get_person_from_handle(grp[0]) given, prefixtotal, prefix = data[self.pref1id]
name = p.get_primary_name() name = p.get_primary_name()
name.set_surname(grp[1].strip()) name.set_first_name(given.strip())
name.set_surname_prefix(grp[2].strip()) oldpref = name.get_surname_list()[0].get_prefix().strip()
self.db.commit_person(p, trans) if oldpref == '' or oldpref == prefix.strip():
name.get_surname_list()[0].set_prefix(prefix)
else:
name.get_surname_list()[0].set_prefix('%s %s' % (prefix, oldpref))
if self.compid in data:
modelhandle = self.compound_hash[key]
val = self.model.get_value(modelhandle, 0)
if val:
surns, prefs, cons, prims, origs = data[self.compid]
name = p.get_primary_name()
new_surn_list = []
for surn, pref, con, prim, orig in zip(surns, prefs, cons,
prims, origs):
new_surn_list.append(gen.lib.Surname())
new_surn_list[-1].set_surname(surn.strip())
new_surn_list[-1].set_prefix(pref.strip())
new_surn_list[-1].set_connector(con.strip())
new_surn_list[-1].set_primary(prim)
new_surn_list[-1].set_origintype(orig)
name.set_surname_list(new_surn_list)
self.db.commit_person(p, trans)
self.db.transaction_commit(trans, self.db.transaction_commit(trans,
_("Extract information from names")) _("Extract information from names"))
@ -351,7 +543,6 @@ class PatchNames(tool.BatchTool, ManagedWindow.ManagedWindow):
self.close() self.close()
self.cb() self.cb()
class PatchNamesOptions(tool.ToolOptions): class PatchNamesOptions(tool.ToolOptions):
""" """
Defines options and provides handling interface. Defines options and provides handling interface.
@ -359,3 +550,7 @@ class PatchNamesOptions(tool.ToolOptions):
def __init__(self, name, person_id=None): def __init__(self, name, person_id=None):
tool.ToolOptions.__init__(self, name, person_id) tool.ToolOptions.__init__(self, name, person_id)
def strip(arg):
return arg.strip()

View File

@ -12,12 +12,10 @@
<child internal-child="vbox"> <child internal-child="vbox">
<object class="GtkVBox" id="dialog-vbox1"> <object class="GtkVBox" id="dialog-vbox1">
<property name="visible">True</property> <property name="visible">True</property>
<property name="orientation">vertical</property>
<child> <child>
<object class="GtkVBox" id="vbox2"> <object class="GtkVBox" id="vbox2">
<property name="visible">True</property> <property name="visible">True</property>
<property name="border_width">6</property> <property name="border_width">6</property>
<property name="orientation">vertical</property>
<child> <child>
<object class="GtkLabel" id="title"> <object class="GtkLabel" id="title">
<property name="visible">True</property> <property name="visible">True</property>
@ -32,10 +30,18 @@
<child> <child>
<object class="GtkLabel" id="label3"> <object class="GtkLabel" id="label3">
<property name="visible">True</property> <property name="visible">True</property>
<property name="xalign">0</property>
<property name="ypad">10</property> <property name="ypad">10</property>
<property name="label" translatable="yes">Below is a list of the nicknames, titles and family name prefixes that Gramps can extract from the <property name="label" translatable="yes">Below is a list of the nicknames, titles, prefixes and compound surnames that Gramps can extract from the family tree.
current database. If you accept the changes, Gramps will modify the entries If you accept the changes, Gramps will modify the entries that have been selected.
that have been selected.</property>
Compound surnames are shown as lists of [prefix, surname, connector].
For example, with the defaults, the name "de Mascarenhas da Silva e Lencastre" shows as:
[de, Mascarenhas]-[da, Silva, e]-[,Lencastre]
Run this tool several times to correct names that have multiple information that can be extracted.</property>
<property name="wrap">True</property>
<property name="max_width_chars">100</property>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>

View File

@ -158,17 +158,27 @@ class PersonTreeView(BasePersonView):
# attempt to get the current surname # attempt to get the current surname
(model, pathlist) = self.selection.get_selected_rows() (model, pathlist) = self.selection.get_selected_rows()
name = u"" name = gen.lib.Name()
basepers = None
if len(pathlist) == 1: if len(pathlist) == 1:
path = pathlist[0] path = pathlist[0]
if len(path) == 1: if len(path) == 1:
name = model.on_get_iter(path).name path = (path[0], 0)
else: node = model.get_iter(path)
node = model.on_get_iter(path) handle = model.get_value(node, self.handle_col)
name = model.on_iter_parent(node).name basepers = self.dbstate.db.get_person_from_handle(handle)
if basepers:
surnlist = []
primname = basepers.get_primary_name()
for surn in primname.get_surname_list():
surnlist.append(gen.lib.Surname(source=surn))
name.set_surname_list(surnlist)
name.set_family_nick_name(primname.get_family_nick_name())
name.set_group_as(primname.get_group_as())
name.set_sort_as(primname.get_sort_as())
try: try:
person.get_primary_name().set_surname(name) person.set_primary_name(name)
EditPerson(self.dbstate, self.uistate, [], person) EditPerson(self.dbstate, self.uistate, [], person)
except Errors.WindowActiveError: except Errors.WindowActiveError:
pass pass