1#
2# Gramps - a GTK+/GNOME based genealogy program
3#
4# Copyright (C) 2004-2007  Donald N. Allingham
5# Copyright (C) 2010       Brian G. Matherly
6# Copyright (C) 2014       Paul Franklin
7#
8# This program is free software; you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as published by
10# the Free Software Foundation; either version 2 of the License, or
11# (at your option) any later version.
12#
13# This program is distributed in the hope that it will be useful,
14# but WITHOUT ANY WARRANTY; without even the implied warranty of
15# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16# GNU General Public License for more details.
17#
18# You should have received a copy of the GNU General Public License
19# along with this program; if not, write to the Free Software
20# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21#
22
23"""
24Class handling language-specific displaying of names.
25
26Specific symbols for parts of a name are defined:
27
28    ======  ===============================================================
29    Symbol  Description
30    ======  ===============================================================
31    't'     title
32    'f'     given (first names)
33    'l'     full surname (lastname)
34    'c'     callname
35    'x'     nick name, call, or otherwise first first name (common name)
36    'i'     initials of the first names
37    'm'     primary surname (main)
38    '0m'    primary surname prefix
39    '1m'    primary surname surname
40    '2m'    primary surname connector
41    'y'     pa/matronymic surname (father/mother) - assumed unique
42    '0y'    pa/matronymic prefix
43    '1y'    pa/matronymic surname
44    '2y'    pa/matronymic connector
45    'o'     surnames without pa/matronymic and primary
46    'r'     non primary surnames (rest)
47    'p'     list of all prefixes
48    'q'     surnames without prefixes and connectors
49    's'     suffix
50    'n'     nick name
51    'g'     family nick name
52    ======  ===============================================================
53"""
54
55#-------------------------------------------------------------------------
56#
57# Python modules
58#
59#-------------------------------------------------------------------------
60import re
61import logging
62LOG = logging.getLogger(".gramps.gen")
63
64#-------------------------------------------------------------------------
65#
66# Gramps modules
67#
68#-------------------------------------------------------------------------
69from ..const import ARABIC_COMMA, ARABIC_SEMICOLON, GRAMPS_LOCALE as glocale
70_ = glocale.translation.sgettext
71from ..lib.name import Name
72from ..lib.nameorigintype import NameOriginType
73
74try:
75    from ..config import config
76    WITH_GRAMPS_CONFIG=True
77except ImportError:
78    WITH_GRAMPS_CONFIG=False
79
80
81#-------------------------------------------------------------------------
82#
83# Constants
84#
85#-------------------------------------------------------------------------
86_FIRSTNAME = 4
87_SURNAME_LIST = 5
88_SUFFIX = 6
89_TITLE = 7
90_TYPE = 8
91_GROUP = 9
92_SORT = 10
93_DISPLAY = 11
94_CALL = 12
95_NICK = 13
96_FAMNICK = 14
97_SURNAME_IN_LIST = 0
98_PREFIX_IN_LIST = 1
99_PRIMARY_IN_LIST = 2
100_TYPE_IN_LIST = 3
101_CONNECTOR_IN_LIST = 4
102_ORIGINPATRO = NameOriginType.PATRONYMIC
103_ORIGINMATRO = NameOriginType.MATRONYMIC
104
105_ACT = True
106_INA = False
107
108_F_NAME = 0  # name of the format
109_F_FMT = 1   # the format string
110_F_ACT = 2   # if the format is active
111_F_FN = 3    # name format function
112_F_RAWFN = 4 # name format raw function
113
114PAT_AS_SURN = False
115
116#-------------------------------------------------------------------------
117#
118# Local functions
119#
120#-------------------------------------------------------------------------
121# Because of occurring in an exec(), this couldn't be in a lambda:
122# we sort names first on longest first, then last letter first, this to
123# avoid translations of shorter terms which appear in longer ones, eg
124# namelast may not be mistaken with name, so namelast must first be
125# converted to %k before name is converted.
126##def _make_cmp(a, b): return -cmp((len(a[1]),a[1]), (len(b[1]), b[1]))
127def _make_cmp_key(a): return (len(a[1]),a[1])  # set reverse to True!!
128
129#-------------------------------------------------------------------------
130#
131# NameDisplayError class
132#
133#-------------------------------------------------------------------------
134class NameDisplayError(Exception):
135    """
136    Error used to report that the name display format string is invalid.
137    """
138    def __init__(self, value):
139        Exception.__init__(self)
140        self.value = value
141
142    def __str__(self):
143        return self.value
144
145#-------------------------------------------------------------------------
146#
147# Functions to extract data from raw lists (unserialized objects)
148#
149#-------------------------------------------------------------------------
150
151def _raw_full_surname(raw_surn_data_list):
152    """method for the 'l' symbol: full surnames"""
153    result = ""
154    for raw_surn_data in raw_surn_data_list:
155        result += "%s %s %s " % (raw_surn_data[_PREFIX_IN_LIST],
156                                 raw_surn_data[_SURNAME_IN_LIST],
157                                 raw_surn_data[_CONNECTOR_IN_LIST])
158    return ' '.join(result.split()).strip()
159
160def _raw_primary_surname(raw_surn_data_list):
161    """method for the 'm' symbol: primary surname"""
162    global PAT_AS_SURN
163    nrsur = len(raw_surn_data_list)
164    for raw_surn_data in raw_surn_data_list:
165        if raw_surn_data[_PRIMARY_IN_LIST]:
166            #if there are multiple surnames, return the primary. If there
167            #is only one surname, then primary has little meaning, and we
168            #assume a pa/matronymic should not be given as primary as it
169            #normally is defined independently
170            if not PAT_AS_SURN and nrsur == 1 and \
171                    (raw_surn_data[_TYPE_IN_LIST][0] == _ORIGINPATRO
172                    or raw_surn_data[_TYPE_IN_LIST][0] == _ORIGINMATRO):
173                return ''
174            else:
175                result = "%s %s %s" % (raw_surn_data[_PREFIX_IN_LIST],
176                                       raw_surn_data[_SURNAME_IN_LIST],
177                                       raw_surn_data[_CONNECTOR_IN_LIST])
178                return ' '.join(result.split())
179    return ''
180
181def _raw_primary_surname_only(raw_surn_data_list):
182    """method to obtain the raw primary surname data, so this returns a string
183    """
184    global PAT_AS_SURN
185    nrsur = len(raw_surn_data_list)
186    for raw_surn_data in raw_surn_data_list:
187        if raw_surn_data[_PRIMARY_IN_LIST]:
188            if not PAT_AS_SURN and nrsur == 1 and \
189                    (raw_surn_data[_TYPE_IN_LIST][0] == _ORIGINPATRO
190                    or raw_surn_data[_TYPE_IN_LIST][0] == _ORIGINMATRO):
191                return ''
192            else:
193                return raw_surn_data[_SURNAME_IN_LIST]
194    return ''
195
196def _raw_primary_prefix_only(raw_surn_data_list):
197    """method to obtain the raw primary surname data"""
198    global PAT_AS_SURN
199    nrsur = len(raw_surn_data_list)
200    for raw_surn_data in raw_surn_data_list:
201        if raw_surn_data[_PRIMARY_IN_LIST]:
202            if not PAT_AS_SURN and nrsur == 1 and \
203                    (raw_surn_data[_TYPE_IN_LIST][0] == _ORIGINPATRO
204                    or raw_surn_data[_TYPE_IN_LIST][0] == _ORIGINMATRO):
205                return ''
206            else:
207                return raw_surn_data[_PREFIX_IN_LIST]
208    return ''
209
210def _raw_primary_conn_only(raw_surn_data_list):
211    """method to obtain the raw primary surname data"""
212    global PAT_AS_SURN
213    nrsur = len(raw_surn_data_list)
214    for raw_surn_data in raw_surn_data_list:
215        if raw_surn_data[_PRIMARY_IN_LIST]:
216            if not PAT_AS_SURN and nrsur == 1 and \
217                    (raw_surn_data[_TYPE_IN_LIST][0] == _ORIGINPATRO
218                    or raw_surn_data[_TYPE_IN_LIST][0] == _ORIGINMATRO):
219                return ''
220            else:
221                return raw_surn_data[_CONNECTOR_IN_LIST]
222    return ''
223
224def _raw_patro_surname(raw_surn_data_list):
225    """method for the 'y' symbol: patronymic surname"""
226    for raw_surn_data in raw_surn_data_list:
227        if (raw_surn_data[_TYPE_IN_LIST][0] == _ORIGINPATRO or
228            raw_surn_data[_TYPE_IN_LIST][0] == _ORIGINMATRO):
229            result = "%s %s %s" % (raw_surn_data[_PREFIX_IN_LIST],
230                                   raw_surn_data[_SURNAME_IN_LIST],
231                                   raw_surn_data[_CONNECTOR_IN_LIST])
232            return ' '.join(result.split())
233    return ''
234
235def _raw_patro_surname_only(raw_surn_data_list):
236    """method for the '1y' symbol: patronymic surname only"""
237    for raw_surn_data in raw_surn_data_list:
238        if (raw_surn_data[_TYPE_IN_LIST][0] == _ORIGINPATRO or
239            raw_surn_data[_TYPE_IN_LIST][0] == _ORIGINMATRO):
240            result = "%s" % (raw_surn_data[_SURNAME_IN_LIST])
241            return ' '.join(result.split())
242    return ''
243
244def _raw_patro_prefix_only(raw_surn_data_list):
245    """method for the '0y' symbol: patronymic prefix only"""
246    for raw_surn_data in raw_surn_data_list:
247        if (raw_surn_data[_TYPE_IN_LIST][0] == _ORIGINPATRO or
248            raw_surn_data[_TYPE_IN_LIST][0] == _ORIGINMATRO):
249            result = "%s" % (raw_surn_data[_PREFIX_IN_LIST])
250            return ' '.join(result.split())
251    return ''
252
253def _raw_patro_conn_only(raw_surn_data_list):
254    """method for the '2y' symbol: patronymic conn only"""
255    for raw_surn_data in raw_surn_data_list:
256        if (raw_surn_data[_TYPE_IN_LIST][0] == _ORIGINPATRO or
257            raw_surn_data[_TYPE_IN_LIST][0] == _ORIGINMATRO):
258            result = "%s" % (raw_surn_data[_CONNECTOR_IN_LIST])
259            return ' '.join(result.split())
260    return ''
261
262def _raw_nonpatro_surname(raw_surn_data_list):
263    """method for the 'o' symbol: full surnames without pa/matronymic or
264       primary
265    """
266    result = ""
267    for raw_surn_data in raw_surn_data_list:
268        if ((not raw_surn_data[_PRIMARY_IN_LIST]) and
269            raw_surn_data[_TYPE_IN_LIST][0] != _ORIGINPATRO and
270            raw_surn_data[_TYPE_IN_LIST][0] != _ORIGINMATRO):
271            result += "%s %s %s " % (raw_surn_data[_PREFIX_IN_LIST],
272                                     raw_surn_data[_SURNAME_IN_LIST],
273                                     raw_surn_data[_CONNECTOR_IN_LIST])
274    return ' '.join(result.split()).strip()
275
276def _raw_nonprimary_surname(raw_surn_data_list):
277    """method for the 'r' symbol: nonprimary surnames"""
278    result = ''
279    for raw_surn_data in raw_surn_data_list:
280        if not raw_surn_data[_PRIMARY_IN_LIST]:
281            result = "%s %s %s %s" % (result, raw_surn_data[_PREFIX_IN_LIST],
282                                      raw_surn_data[_SURNAME_IN_LIST],
283                                      raw_surn_data[_CONNECTOR_IN_LIST])
284    return ' '.join(result.split())
285
286def _raw_prefix_surname(raw_surn_data_list):
287    """method for the 'p' symbol: all prefixes"""
288    result = ""
289    for raw_surn_data in raw_surn_data_list:
290        result += "%s " % (raw_surn_data[_PREFIX_IN_LIST])
291    return ' '.join(result.split()).strip()
292
293def _raw_single_surname(raw_surn_data_list):
294    """method for the 'q' symbol: surnames without prefix and connectors"""
295    result = ""
296    for raw_surn_data in raw_surn_data_list:
297        result += "%s " % (raw_surn_data[_SURNAME_IN_LIST])
298    return ' '.join(result.split()).strip()
299
300def cleanup_name(namestring):
301    """Remove too long white space due to missing name parts,
302       so "a   b" becomes "a b" and "a , b" becomes "a, b"
303    """
304    parts = namestring.split()
305    if not parts:
306        return ""
307    result = parts[0]
308    for val in parts[1:]:
309        if len(val) == 1 and val in [',', ';', ':',
310                                     ARABIC_COMMA, ARABIC_SEMICOLON]:
311            result +=  val
312        else:
313            result += ' ' + val
314    return result
315
316#-------------------------------------------------------------------------
317#
318# NameDisplay class
319#
320#-------------------------------------------------------------------------
321class NameDisplay:
322    """
323    Base class for displaying of Name instances.
324
325    Property:
326      *default_format*
327        the default name format to use
328      *pas_as_surn*
329        if only one surname, see if pa/ma should be considered as 'the' surname.
330    """
331
332    format_funcs = {}
333    raw_format_funcs = {}
334
335    def __init__(self, xlocale=glocale):
336        """
337        Initialize the NameDisplay class.
338
339        If xlocale is passed in (a GrampsLocale), then
340        the translated script will be returned instead.
341
342        :param xlocale: allow selection of the displayer script
343        :type xlocale: a GrampsLocale instance
344        """
345        global WITH_GRAMPS_CONFIG
346        global PAT_AS_SURN
347
348        # translators: needed for Arabic, ignore otherwise
349        COMMAGLYPH = xlocale.translation.gettext(',')
350
351        self.STANDARD_FORMATS = [
352            (Name.DEF,   _("Default format (defined by Gramps preferences)"),
353                         '', _ACT),
354            (Name.LNFN,  _("Surname, Given Suffix"),
355                         '%l' + COMMAGLYPH + ' %f %s', _ACT),
356            (Name.FN,    _("Given"),
357                         '%f', _ACT),
358            (Name.FNLN,  _("Given Surname Suffix"),
359                         '%f %l %s', _ACT),
360            # primary name primconnector other, given pa/matronynic suffix, primprefix
361            # translators: long string, have a look at Preferences dialog
362            (Name.LNFNP, _("Main Surnames, Given Patronymic Suffix Prefix"),
363                         '%1m %2m %o' + COMMAGLYPH + ' %f %1y %s %0m', _ACT),
364            # DEPRECATED FORMATS
365            (Name.PTFN,  _("Patronymic, Given"),
366                         '%y' + COMMAGLYPH + ' %s %f', _INA),
367        ]
368
369        self.LNFN_STR = "%s" + COMMAGLYPH + " %s %s"
370
371        self.name_formats = {}
372
373        if WITH_GRAMPS_CONFIG:
374            self.default_format = config.get('preferences.name-format')
375            if self.default_format == 0:
376                self.default_format = Name.LNFN
377                config.set('preferences.name-format', self.default_format)
378            #if only one surname, see if pa/ma should be considered as
379            # 'the' surname.
380            PAT_AS_SURN = config.get('preferences.patronimic-surname')
381            config.connect('preferences.patronimic-surname', self.change_pa_sur)
382        else:
383            self.default_format = Name.LNFN
384            PAT_AS_SURN = False
385
386        #preinit the name formats, this should be updated with the data
387        #in the database once a database is loaded
388        self.set_name_format(self.STANDARD_FORMATS)
389
390    def change_pa_sur(self, *args):
391        """ How to handle single patronymic as surname is changed"""
392        global PAT_AS_SURN
393        PAT_AS_SURN = config.get('preferences.patronimic-surname')
394
395    def get_pat_as_surn(self):
396        global PAT_AS_SURN
397        return PAT_AS_SURN
398
399    def _format_fn(self, fmt_str):
400        return lambda x: self.format_str(x, fmt_str)
401
402    def _format_raw_fn(self, fmt_str):
403        return lambda x: self.format_str_raw(x, fmt_str)
404
405    def _raw_lnfn(self, raw_data):
406        result = self.LNFN_STR % (_raw_full_surname(raw_data[_SURNAME_LIST]),
407                             raw_data[_FIRSTNAME],
408                             raw_data[_SUFFIX])
409        return ' '.join(result.split())
410
411    def _raw_fnln(self, raw_data):
412        result = "%s %s %s" % (raw_data[_FIRSTNAME],
413                               _raw_full_surname(raw_data[_SURNAME_LIST]),
414                               raw_data[_SUFFIX])
415        return ' '.join(result.split())
416
417    def _raw_fn(self, raw_data):
418        result = raw_data[_FIRSTNAME]
419        return ' '.join(result.split())
420
421    def clear_custom_formats(self):
422        self.name_formats = {num: value
423                             for num, value in self.name_formats.items()
424                             if num >= 0}
425
426    def set_name_format(self, formats):
427
428        raw_func_dict = {
429            Name.LNFN : self._raw_lnfn,
430            Name.FNLN : self._raw_fnln,
431            Name.FN   : self._raw_fn,
432            }
433
434        for (num, name, fmt_str, act) in formats:
435            func = self._format_fn(fmt_str)
436            func_raw = raw_func_dict.get(num, self._format_raw_fn(fmt_str))
437            self.name_formats[num] = (name, fmt_str, act, func, func_raw)
438        self.set_default_format(self.get_default_format())
439
440    def add_name_format(self, name, fmt_str):
441        for num in self.name_formats:
442            if fmt_str in self.name_formats.get(num):
443                return num
444        num = -1
445        while num in self.name_formats:
446            num -= 1
447        self.set_name_format([(num, name, fmt_str,_ACT)])
448        return num
449
450    def edit_name_format(self, num, name, fmt_str):
451        self.set_name_format([(num, name, fmt_str,_ACT)])
452        if self.default_format == num:
453            self.set_default_format(num)
454
455    def del_name_format(self, num):
456        try:
457            del self.name_formats[num]
458        except:
459            pass
460
461    def set_default_format(self, num):
462        if num not in self.name_formats:
463            num = Name.LNFN
464        # if user sets default format to the Gramps default format,
465        # then we select LNFN as format.
466        if num == Name.DEF:
467            num = Name.LNFN
468
469        self.default_format = num
470
471        self.name_formats[Name.DEF] = (self.name_formats[Name.DEF][_F_NAME],
472                                       self.name_formats[Name.DEF][_F_FMT],
473                                       self.name_formats[Name.DEF][_F_ACT],
474                                       self.name_formats[num][_F_FN],
475                                       self.name_formats[num][_F_RAWFN])
476
477    def get_default_format(self):
478        return self.default_format
479
480    def set_format_inactive(self, num):
481        try:
482            self.name_formats[num] = (self.name_formats[num][_F_NAME],
483                                      self.name_formats[num][_F_FMT],
484                                      _INA,
485                                      self.name_formats[num][_F_FN],
486                                      self.name_formats[num][_F_RAWFN])
487        except:
488            pass
489
490    def get_name_format(self, also_default=False,
491                        only_custom=False,
492                        only_active=True):
493        """
494        Get a list of tuples (num, name,fmt_str,act)
495        """
496        the_list = []
497
498        keys = sorted(self.name_formats, key=self.cmp_to_key(self._sort_name_format))
499
500        for num in keys:
501            if ((also_default or num) and
502                (not only_custom or (num < 0)) and
503                (not only_active or self.name_formats[num][_F_ACT])):
504                the_list.append((num,) + self.name_formats[num][_F_NAME:_F_FN])
505
506        return the_list
507
508    def cmp_to_key(self, mycmp):
509        """
510        python 2 to 3 conversion, python recipe http://code.activestate.com/recipes/576653/
511        Convert a :func:`cmp` function into a :func:`key` function
512        We use this in Gramps as understanding the old compare function is
513        not trivial. This should be replaced by a proper key function
514        """
515        class K:
516            def __init__(self, obj, *args):
517                self.obj = obj
518            def __lt__(self, other):
519                return mycmp(self.obj, other.obj) < 0
520            def __gt__(self, other):
521                return mycmp(self.obj, other.obj) > 0
522            def __eq__(self, other):
523                return mycmp(self.obj, other.obj) == 0
524            def __le__(self, other):
525                return mycmp(self.obj, other.obj) <= 0
526            def __ge__(self, other):
527                return mycmp(self.obj, other.obj) >= 0
528            def __ne__(self, other):
529                return mycmp(self.obj, other.obj) != 0
530        return K
531    def _sort_name_format(self, x, y):
532        if x < 0:
533            if y < 0:
534                return x+y
535            else:
536                return -x+y
537        else:
538            if y < 0:
539                return -x+y
540            else:
541                return x-y
542
543    def _is_format_valid(self, num):
544        try:
545            if not self.name_formats[num][_F_ACT]:
546                num = 0
547        except:
548            num = 0
549        return num
550
551    #-------------------------------------------------------------------------
552
553
554    def _gen_raw_func(self, format_str):
555        """The job of building the name from a format string is rather
556        expensive and it is called lots and lots of times. So it is worth
557        going to some length to optimise it as much as possible.
558
559        This method constructs a new function that is specifically written
560        to format a name given a particular format string. This is worthwhile
561        because the format string itself rarely changes, so by caching the new
562        function and calling it directly when asked to format a name to the
563        same format string again we can be as quick as possible.
564
565        The new function is of the form::
566
567        def fn(raw_data):
568            return "%s %s %s" % (raw_data[_TITLE],
569                   raw_data[_FIRSTNAME],
570                   raw_data[_SUFFIX])
571
572        Specific symbols for parts of a name are defined (keywords given):
573        't' : title = title
574        'f' : given = given (first names)
575        'l' : surname = full surname (lastname)
576        'c' : call = callname
577        'x' : common = nick name, call, otherwise first first name (common name)
578        'i' : initials = initials of the first names
579        'm' : primary = primary surname (main)
580        '0m': primary[pre]= prefix primary surname (main)
581        '1m': primary[sur]= surname primary surname (main)
582        '2m': primary[con]= connector primary surname (main)
583        'y' : patronymic = pa/matronymic surname (father/mother) - assumed unique
584        '0y': patronymic[pre] = prefix      "
585        '1y': patronymic[sur] = surname     "
586        '2y': patronymic[con] = connector   "
587        'o' : notpatronymic = surnames without pa/matronymic and primary
588        'r' : rest = non primary surnames
589        'p' : prefix = list of all prefixes
590        'q' : rawsurnames = surnames without prefixes and connectors
591        's' : suffix = suffix
592        'n' : nickname = nick name
593        'g' : familynick = family nick name
594
595
596        """
597
598        # we need the names of each of the variables or methods that are
599        # called to fill in each format flag.
600        # Dictionary is "code": ("expression", "keyword", "i18n-keyword")
601        d = {"t": ("raw_data[_TITLE]",     "title",
602                                _("Person|title")),
603             "f": ("raw_data[_FIRSTNAME]", "given",
604                                _("given")),
605             "l": ("_raw_full_surname(raw_data[_SURNAME_LIST])",   "surname",
606                                _("surname")),
607             "s": ("raw_data[_SUFFIX]",    "suffix",
608                                _("suffix")),
609             "c": ("raw_data[_CALL]",      "call",
610                                _("Name|call")),
611             "x": ("(raw_data[_NICK] or raw_data[_CALL] or raw_data[_FIRSTNAME].split(' ')[0])",
612                                "common",
613                                _("Name|common")),
614             "i": ("''.join([word[0] +'.' for word in ('. ' +" +
615                   " raw_data[_FIRSTNAME]).split()][1:])",
616                                "initials",
617                                _("initials")),
618             "m": ("_raw_primary_surname(raw_data[_SURNAME_LIST])",
619                                "primary",
620                                _("Name|primary")),
621             "0m": ("_raw_primary_prefix_only(raw_data[_SURNAME_LIST])",
622                                "primary[pre]",
623                                _("primary[pre]")),
624             "1m": ("_raw_primary_surname_only(raw_data[_SURNAME_LIST])",
625                                "primary[sur]",
626                                _("primary[sur]")),
627             "2m": ("_raw_primary_conn_only(raw_data[_SURNAME_LIST])",
628                                "primary[con]",
629                                _("primary[con]")),
630             "y": ("_raw_patro_surname(raw_data[_SURNAME_LIST])", "patronymic",
631                                _("patronymic")),
632             "0y": ("_raw_patro_prefix_only(raw_data[_SURNAME_LIST])", "patronymic[pre]",
633                                _("patronymic[pre]")),
634             "1y": ("_raw_patro_surname_only(raw_data[_SURNAME_LIST])", "patronymic[sur]",
635                                _("patronymic[sur]")),
636             "2y": ("_raw_patro_conn_only(raw_data[_SURNAME_LIST])", "patronymic[con]",
637                                _("patronymic[con]")),
638             "o": ("_raw_nonpatro_surname(raw_data[_SURNAME_LIST])", "notpatronymic",
639                                _("notpatronymic")),
640             "r": ("_raw_nonprimary_surname(raw_data[_SURNAME_LIST])",
641                                "rest",
642                                _("Remaining names|rest")),
643             "p": ("_raw_prefix_surname(raw_data[_SURNAME_LIST])",
644                                "prefix",
645                                _("prefix")),
646             "q": ("_raw_single_surname(raw_data[_SURNAME_LIST])",
647                                "rawsurnames",
648                                _("rawsurnames")),
649             "n": ("raw_data[_NICK]",      "nickname",
650                                _("nickname")),
651             "g": ("raw_data[_FAMNICK]",      "familynick",
652                                _("familynick")),
653             }
654        args = "raw_data"
655        return self._make_fn(format_str, d, args)
656
657    def _gen_cooked_func(self, format_str):
658        """The job of building the name from a format string is rather
659        expensive and it is called lots and lots of times. So it is worth
660        going to some length to optimise it as much as possible.
661
662        This method constructs a new function that is specifically written
663        to format a name given a particular format string. This is worthwhile
664        because the format string itself rarely changes, so by caching the new
665        function and calling it directly when asked to format a name to the
666        same format string again we can be as quick as possible.
667
668        The new function is of the form::
669
670        def fn(first, raw_surname_list, suffix, title, call,):
671            return "%s %s" % (first,suffix)
672
673        Specific symbols for parts of a name are defined (keywords given):
674        't' : title = title
675        'f' : given = given (first names)
676        'l' : surname = full surname (lastname)
677        'c' : call = callname
678        'x' : common = nick name, call, or otherwise first first name (common name)
679        'i' : initials = initials of the first names
680        'm' : primary = primary surname (main)
681        '0m': primary[pre]= prefix primary surname (main)
682        '1m': primary[sur]= surname primary surname (main)
683        '2m': primary[con]= connector primary surname (main)
684        'y' : patronymic = pa/matronymic surname (father/mother) - assumed unique
685        '0y': patronymic[pre] = prefix      "
686        '1y': patronymic[sur] = surname     "
687        '2y': patronymic[con] = connector   "
688        'o' : notpatronymic = surnames without pa/matronymic and primary
689        'r' : rest = non primary surnames
690        'p' : prefix = list of all prefixes
691        'q' : rawsurnames = surnames without prefixes and connectors
692        's' : suffix = suffix
693        'n' : nickname = nick name
694        'g' : familynick = family nick name
695
696        """
697
698        # we need the names of each of the variables or methods that are
699        # called to fill in each format flag.
700        # Dictionary is "code": ("expression", "keyword", "i18n-keyword")
701        d = {"t": ("title",      "title",
702                        _("Person|title")),
703             "f": ("first",      "given",
704                        _("given")),
705             "l": ("_raw_full_surname(raw_surname_list)",   "surname",
706                        _("surname")),
707             "s": ("suffix",     "suffix",
708                        _("suffix")),
709             "c": ("call",       "call",
710                        _("Name|call")),
711             "x": ("(nick or call or first.split(' ')[0])", "common",
712                        _("Name|common")),
713             "i": ("''.join([word[0] +'.' for word in ('. ' + first).split()][1:])",
714                        "initials",
715                        _("initials")),
716             "m": ("_raw_primary_surname(raw_surname_list)", "primary",
717                        _("Name|primary")),
718             "0m":("_raw_primary_prefix_only(raw_surname_list)",
719                        "primary[pre]", _("primary[pre]")),
720             "1m":("_raw_primary_surname_only(raw_surname_list)",
721                        "primary[sur]",_("primary[sur]")),
722             "2m":("_raw_primary_conn_only(raw_surname_list)",
723                        "primary[con]", _("primary[con]")),
724             "y": ("_raw_patro_surname(raw_surname_list)", "patronymic",
725                        _("patronymic")),
726             "0y":("_raw_patro_prefix_only(raw_surname_list)", "patronymic[pre]",
727                        _("patronymic[pre]")),
728             "1y":("_raw_patro_surname_only(raw_surname_list)", "patronymic[sur]",
729                        _("patronymic[sur]")),
730             "2y":("_raw_patro_conn_only(raw_surname_list)", "patronymic[con]",
731                        _("patronymic[con]")),
732             "o": ("_raw_nonpatro_surname(raw_surname_list)", "notpatronymic",
733                        _("notpatronymic")),
734             "r": ("_raw_nonprimary_surname(raw_surname_list)", "rest",
735                        _("Remaining names|rest")),
736             "p": ("_raw_prefix_surname(raw_surname_list)", "prefix",
737                        _("prefix")),
738             "q": ("_raw_single_surname(raw_surname_list)", "rawsurnames",
739                        _("rawsurnames")),
740             "n": ("nick",       "nickname",
741                        _("nickname")),
742             "g": ("famnick",    "familynick",
743                        _("familynick")),
744             }
745        args = "first,raw_surname_list,suffix,title,call,nick,famnick"
746        return self._make_fn(format_str, d, args)
747
748    def format_str(self, name, format_str):
749        return self._format_str_base(name.first_name, name.surname_list,
750                                     name.suffix, name.title,
751                                     name.call, name.nick, name.famnick,
752                                     format_str)
753
754    def format_str_raw(self, raw_data, format_str):
755        """
756        Format a name from the raw name list. To make this as fast as possible
757        this uses :func:`_gen_raw_func` to generate a new method for each new
758        format_string.
759
760        Is does not call :meth:`_format_str_base` because it would introduce an
761        extra method call and we need all the speed we can squeeze out of this.
762        """
763        func = self.__class__.raw_format_funcs.get(format_str)
764        if func is None:
765            func = self._gen_raw_func(format_str)
766            self.__class__.raw_format_funcs[format_str] = func
767
768        return func(raw_data)
769
770    def _format_str_base(self, first, surname_list, suffix, title, call,
771                         nick, famnick, format_str):
772        """
773        Generates name from a format string.
774
775        The following substitutions are made:
776        '%t' : title
777        '%f' : given (first names)
778        '%l' : full surname (lastname)
779        '%c' : callname
780        '%x' : nick name, call, or otherwise first first name (common name)
781        '%i' : initials of the first names
782        '%m' : primary surname (main)
783        '%0m': prefix primary surname (main)
784        '%1m': surname primary surname (main)
785        '%2m': connector primary surname (main)
786        '%y' : pa/matronymic surname (father/mother) - assumed unique
787        '%0y': prefix      "
788        '%1y': surname     "
789        '%2y': connector   "
790        '%o' : surnames without patronymic
791        '%r' : non-primary surnames (rest)
792        '%p' : list of all prefixes
793        '%q' : surnames without prefixes and connectors
794        '%s' : suffix
795        '%n' : nick name
796        '%g' : family nick name
797        The capital letters are substituted for capitalized name components.
798        The %% is substituted with the single % character.
799        All the other characters in the fmt_str are unaffected.
800        """
801        func = self.__class__.format_funcs.get(format_str)
802        if func is None:
803            func = self._gen_cooked_func(format_str)
804            self.__class__.format_funcs[format_str] = func
805        try:
806            s = func(first, [surn.serialize() for surn in surname_list],
807                     suffix, title, call, nick, famnick)
808        except (ValueError, TypeError,):
809            raise NameDisplayError("Incomplete format string")
810
811        return s
812
813    #-------------------------------------------------------------------------
814
815    def primary_surname(self, name):
816        global PAT_AS_SURN
817        nrsur = len(name.surname_list)
818        sur = name.get_primary_surname()
819        if not PAT_AS_SURN and nrsur <= 1 and \
820                (sur.get_origintype().value == _ORIGINPATRO
821                 or sur.get_origintype().value == _ORIGINMATRO):
822            return ''
823        return sur.get_surname()
824
825    def sort_string(self, name):
826        return "%-25s%-30s%s" % (self.primary_surname(name),
827                                  name.first_name, name.suffix)
828
829    def sorted(self, person):
830        """
831        Return a text string representing the :class:`~.person.Person`
832        instance's :class:`~.name.Name` in a manner that should be used for
833        displaying a sortedname.
834
835        :param person: :class:`~.person.Person` instance that contains the
836                       :class:`~.name.Name` that is to be displayed. The
837                       primary name is used for the display.
838        :type person: :class:`~.person.Person`
839        :returns: Returns the :class:`~.person.Person` instance's name
840        :rtype: str
841        """
842        name = person.get_primary_name()
843        return self.sorted_name(name)
844
845    def sorted_name(self, name):
846        """
847        Return a text string representing the :class:`~.name.Name` instance
848        in a manner that should be used for sorting the name in a list.
849
850        :param name: :class:`~.name.Name` instance that is to be displayed.
851        :type name: :class:`~.name.Name`
852        :returns: Returns the :class:`~.name.Name` string representation
853        :rtype: str
854        """
855        num = self._is_format_valid(name.sort_as)
856        return self.name_formats[num][_F_FN](name)
857
858    def truncate(self, full_name, max_length=15, elipsis="..."):
859        name_out = ""
860        if len(full_name) <= max_length:
861            name_out = full_name
862        else:
863            last_space = full_name.rfind(" ", max_length)
864            if (last_space) > -1:
865                name_out = full_name[:last_space]
866            else:
867                name_out = full_name[:max_length]
868            name_out += " " + elipsis
869        return name_out
870
871    def raw_sorted_name(self, raw_data):
872        """
873        Return a text string representing the :class:`~.name.Name` instance
874        in a manner that should be used for sorting the name in a list.
875
876        :param name: raw unserialized data of name that is to be displayed.
877        :type name: tuple
878        :returns: Returns the :class:`~.name.Name` string representation
879        :rtype: str
880        """
881        num = self._is_format_valid(raw_data[_SORT])
882        return self.name_formats[num][_F_RAWFN](raw_data)
883
884    def display(self, person):
885        """
886        Return a text string representing the :class:`~.person.Person`
887        instance's :class:`~.name.Name` in a manner that should be used for
888        normal displaying.
889
890        :param person: :class:`~.person.Person` instance that contains the
891                       :class:`~.name.Name` that is to be displayed. The
892                       primary name is used for the display.
893        :type person: :class:`~.person.Person`
894        :returns: Returns the :class:`~.person.Person` instance's name
895        :rtype: str
896        """
897        name = person.get_primary_name()
898        return self.display_name(name)
899
900    def display_format(self, person, num):
901        """
902        Return a text string representing the L{gen.lib.Person} instance's
903        L{Name} using num format.
904
905        @param person: L{gen.lib.Person} instance that contains the
906        L{Name} that is to be displayed. The primary name is used for
907        the display.
908        @type person: L{gen.lib.Person}
909        @param num: num of the format to be used, as return by
910        name_displayer.add_name_format('name','format')
911        @type num: int
912        @returns: Returns the L{gen.lib.Person} instance's name
913        @rtype: str
914        """
915        name = person.get_primary_name()
916        return self.name_formats[num][_F_FN](name)
917
918    def display_formal(self, person):
919        """
920        Return a text string representing the :class:`~.person.Person`
921        instance's :class:`~.name.Name` in a manner that should be used for
922        formal displaying.
923
924        :param person: :class:`~.person.Person` instance that contains the
925                       :class:`~.name.Name` that is to be displayed. The
926                       primary name is used for the display.
927        :type person: :class:`~.person.Person`
928        :returns: Returns the :class:`~.person.Person` instance's name
929        :rtype: str
930        """
931        # FIXME: At this time, this is just duplicating display() method
932        name = person.get_primary_name()
933        return self.display_name(name)
934
935    def display_name(self, name):
936        """
937        Return a text string representing the :class:`~.name.Name` instance
938        in a manner that should be used for normal displaying.
939
940        :param name: :class:`~.name.Name` instance that is to be displayed.
941        :type name: :class:`~.name.Name`
942        :returns: Returns the :class:`~.name.Name` string representation
943        :rtype: str
944        """
945        if name is None:
946            return ""
947
948        num = self._is_format_valid(name.display_as)
949        return self.name_formats[num][_F_FN](name)
950
951    def raw_display_name(self, raw_data):
952        """
953        Return a text string representing the :class:`~.name.Name` instance
954        in a manner that should be used for normal displaying.
955
956        :param name: raw unserialized data of name that is to be displayed.
957        :type name: tuple
958        :returns: Returns the :class:`~.name.Name` string representation
959        :rtype: str
960        """
961        num = self._is_format_valid(raw_data[_DISPLAY])
962        return self.name_formats[num][_F_RAWFN](raw_data)
963
964    def display_given(self, person):
965        return self.format_str(person.get_primary_name(),'%f')
966
967    def name_grouping(self, db, person):
968        """
969        Return the name under which to group this person. This is defined as:
970
971        1. if group name is defined on primary name, use that
972        2. if group name is defined for the primary surname of the primary
973           name, use that
974        3. use primary surname of primary name otherwise
975        """
976        return self.name_grouping_name(db, person.primary_name)
977
978    def name_grouping_name(self, db, pn):
979        """
980        Return the name under which to group. This is defined as:
981
982        1. if group name is defined, use that
983        2. if group name is defined for the primary surname, use that
984        3. use primary surname itself otherwise
985
986        :param pn: :class:`~.name.Name` object
987        :type pn: :class:`~.name.Name` instance
988        :returns: Returns the groupname string representation
989        :rtype: str
990        """
991        if pn.group_as:
992            return pn.group_as
993        return db.get_name_group_mapping(pn.get_primary_surname().get_surname())
994
995    def name_grouping_data(self, db, pn):
996        """
997        Return the name under which to group. This is defined as:
998
999        1. if group name is defined, use that
1000        2. if group name is defined for the primary surname, use that
1001        3. use primary surname itself otherwise
1002
1003        :param pn: raw unserialized data of name
1004        :type pn: tuple
1005        :returns: Returns the groupname string representation
1006        :rtype: str
1007        """
1008        if pn[_GROUP]:
1009            return pn[_GROUP]
1010        return db.get_name_group_mapping(_raw_primary_surname_only(
1011                                                    pn[_SURNAME_LIST]))
1012
1013    def _make_fn(self, format_str, d, args):
1014        """
1015        Create the name display function and handles dependent
1016        punctuation.
1017        """
1018        # d is a dict: dict[code] = (expr, word, translated word)
1019
1020        # First, go through and do internationalization-based
1021        # key-word replacement. Just replace ikeywords with
1022        # %codes (ie, replace "irstnamefay" with "%f", and
1023        # "IRSTNAMEFAY" for %F)
1024
1025        if (len(format_str) > 2 and
1026            format_str[0] == format_str[-1] == '"'):
1027            pass
1028        else:
1029            d_keys = [(code, _tuple[2]) for code, _tuple in d.items()]
1030            d_keys.sort(key=_make_cmp_key, reverse=True) # reverse on length and by ikeyword
1031            for (code, ikeyword) in d_keys:
1032                exp, keyword, ikeyword = d[code]
1033                format_str = format_str.replace(ikeyword, "%"+ code)
1034                format_str = format_str.replace(ikeyword.title(), "%"+ code)
1035                format_str = format_str.replace(ikeyword.upper(), "%"+ code.upper())
1036        # Next, go through and do key-word replacement.
1037        # Just replace keywords with
1038        # %codes (ie, replace "firstname" with "%f", and
1039        # "FIRSTNAME" for %F)
1040        if (len(format_str) > 2 and
1041            format_str[0] == format_str[-1] == '"'):
1042            pass
1043        else:
1044            d_keys = [(code, _tuple[1]) for code, _tuple in d.items()]
1045            d_keys.sort(key=_make_cmp_key, reverse=True) # reverse sort on length and by keyword
1046            # if in double quotes, just use % codes
1047            for (code, keyword) in d_keys:
1048                exp, keyword, ikeyword = d[code]
1049                format_str = format_str.replace(keyword, "%"+ code)
1050                format_str = format_str.replace(keyword.title(), "%"+ code)
1051                format_str = format_str.replace(keyword.upper(), "%"+ code.upper())
1052        # Get lower and upper versions of codes:
1053        codes = list(d.keys()) + [c.upper() for c in d]
1054        # Next, list out the matching patterns:
1055        # If it starts with "!" however, treat the punctuation verbatim:
1056        if len(format_str) > 0 and format_str[0] == "!":
1057            patterns = ["%(" + ("|".join(codes)) + ")",          # %s
1058                        ]
1059            format_str = format_str[1:]
1060        else:
1061            patterns = [
1062                ",\\W*\"%(" + ("|".join(codes)) + ")\"",    # ,\W*"%s"
1063                ",\\W*\\(%(" + ("|".join(codes)) + ")\\)",  # ,\W*(%s)
1064                ",\\W*%(" + ("|".join(codes)) + ")",        # ,\W*%s
1065                "\"%(" + ("|".join(codes)) + ")\"",         # "%s"
1066                "_%(" + ("|".join(codes)) + ")_",           # _%s_
1067                "\\(%(" + ("|".join(codes)) + ")\\)",       # (%s)
1068                "%(" + ("|".join(codes)) + ")",             # %s
1069                ]
1070        new_fmt = format_str
1071
1072        # replace the specific format string flags with a
1073        # flag that works in standard python format strings.
1074        new_fmt = re.sub("|".join(patterns), "%s", new_fmt)
1075
1076        # replace special meaning codes we need to have verbatim in output
1077        if (len(new_fmt) > 2 and new_fmt[0] == new_fmt[-1] == '"'):
1078            new_fmt = new_fmt.replace('\\', r'\\')
1079            new_fmt = new_fmt[1:-1].replace('"', r'\"')
1080        else:
1081            new_fmt = new_fmt.replace('\\', r'\\')
1082            new_fmt = new_fmt.replace('"', '\\\"')
1083
1084        # find each format flag in the original format string
1085        # for each one we find the variable name that is needed to
1086        # replace it and add this to a list. This list will be used to
1087        # generate the replacement tuple.
1088
1089        # This compiled pattern should match all of the format codes.
1090        pat = re.compile("|".join(patterns))
1091        param = ()
1092        mat = pat.search(format_str)
1093        while mat:
1094            match_pattern = mat.group(0) # the matching pattern
1095            # prefix, code, suffix:
1096            p, code, s = re.split("%(.)", match_pattern)
1097            if code in '0123456789':
1098                code = code + s[0]
1099                s = s[1:]
1100            field = d[code.lower()][0]
1101            if code.isupper():
1102                field += ".upper()"
1103            if p == '' and s == '':
1104                param = param + (field,)
1105            else:
1106                param = param + ("ifNotEmpty(%s,'%s','%s')" % (field, p, s), )
1107            mat = pat.search(format_str, mat.end())
1108        s = """
1109def fn(%s):
1110    def ifNotEmpty(str,p,s):
1111        if str == '':
1112            return ''
1113        else:
1114            return p + str + s
1115    return cleanup_name("%s" %% (%s))""" % (args, new_fmt, ",".join(param))
1116        try:
1117            exec(s) in globals(), locals()
1118            return locals()['fn']
1119        except:
1120            LOG.error("\n" + 'Wrong name format string %s' % new_fmt
1121                       +"\n" + ("ERROR, Edit Name format in Preferences->Display to correct")
1122                       +"\n" + _('Wrong name format string %s') % new_fmt
1123                       +"\n" + ("ERROR, Edit Name format in Preferences->Display to correct")
1124                     )
1125            def errfn(*arg):
1126                return _("ERROR, Edit Name format in Preferences")
1127            return errfn
1128
1129displayer = NameDisplay()
1130