diff --git a/data/gnome-mime-application-x-gedcom.png b/data/application-x-gedcom.png similarity index 100% rename from data/gnome-mime-application-x-gedcom.png rename to data/application-x-gedcom.png diff --git a/data/gnome-mime-application-x-gedcom.svg b/data/application-x-gedcom.svg similarity index 100% rename from data/gnome-mime-application-x-gedcom.svg rename to data/application-x-gedcom.svg diff --git a/data/gnome-mime-application-x-geneweb.png b/data/application-x-geneweb.png similarity index 100% rename from data/gnome-mime-application-x-geneweb.png rename to data/application-x-geneweb.png diff --git a/data/gnome-mime-application-x-geneweb.svg b/data/application-x-geneweb.svg similarity index 100% rename from data/gnome-mime-application-x-geneweb.svg rename to data/application-x-geneweb.svg diff --git a/data/gnome-mime-application-x-gramps-package.png b/data/application-x-gramps-package.png similarity index 100% rename from data/gnome-mime-application-x-gramps-package.png rename to data/application-x-gramps-package.png diff --git a/data/gnome-mime-application-x-gramps-package.svg b/data/application-x-gramps-package.svg similarity index 100% rename from data/gnome-mime-application-x-gramps-package.svg rename to data/application-x-gramps-package.svg diff --git a/data/gnome-mime-application-x-gramps-xml.png b/data/application-x-gramps-xml.png similarity index 100% rename from data/gnome-mime-application-x-gramps-xml.png rename to data/application-x-gramps-xml.png diff --git a/data/gnome-mime-application-x-gramps-xml.svg b/data/application-x-gramps-xml.svg similarity index 100% rename from data/gnome-mime-application-x-gramps-xml.svg rename to data/application-x-gramps-xml.svg diff --git a/data/gnome-mime-application-x-gramps.png b/data/application-x-gramps.png similarity index 100% rename from data/gnome-mime-application-x-gramps.png rename to data/application-x-gramps.png diff --git a/data/gnome-mime-application-x-gramps.svg b/data/application-x-gramps.svg similarity index 100% rename from data/gnome-mime-application-x-gramps.svg rename to data/application-x-gramps.svg diff --git a/data/grampsxml.dtd b/data/grampsxml.dtd index 160d4add5..6bd622d3e 100644 --- a/data/grampsxml.dtd +++ b/data/grampsxml.dtd @@ -251,7 +251,7 @@ PLACES - + - diff --git a/debian/changelog b/debian/changelog index 4de28caf7..951372468 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,19 @@ +gramps (5.1.4-1) unstable; urgency=medium + + * New release + * Add new copyrights + * Update watch file + * Patch probably alive test to fix FTBFS + + -- Ross Gammon Sun, 15 Aug 2021 18:31:56 +0200 + +gramps (5.1.3-1) focal; urgency=medium + + * New release + * Update debian/copyright for Alex Roitman + + -- Ross Gammon Sun, 16 Aug 2020 20:23:34 +0200 + gramps (5.1.2-1) unstable; urgency=medium * New release diff --git a/debian/copyright b/debian/copyright index e2d1f1cc3..10ff8b324 100644 --- a/debian/copyright +++ b/debian/copyright @@ -92,6 +92,8 @@ Copyright: 2000-2007, Alex Roitman 2018, Robin van der Vliet 2018, Theo van Rijn 2019, Matthias Kemmer + 2020, Jan Sparreboom + 2021, Mirko Leonhaeuser License: GPL-2+ Files: debian/* diff --git a/debian/patches/fix-probably_alive_test.patch b/debian/patches/fix-probably_alive_test.patch new file mode 100644 index 000000000..9c3dc5405 --- /dev/null +++ b/debian/patches/fix-probably_alive_test.patch @@ -0,0 +1,22 @@ +Description: Fix probably alive test + The probably alive funtion was fixed just prior to the Gramps 5.1.4 + release. It appears the relevant unit test was not updated to match. + The relevant commit: + https://github.com/gramps-project/gramps/commit/a685b96f700dcfc6b953413cb3adc8be61d87438 +Author: Ross Gammon +Forwarded: no +Applied-Upstream: no +Last-Update: 2021-08-09 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +--- a/gramps/gen/filters/rules/test/person_rules_test.py ++++ b/gramps/gen/filters/rules/test/person_rules_test.py +@@ -347,7 +347,7 @@ + """ + rule = ProbablyAlive(['1900']) + res = self.filter_with_rule(rule) +- self.assertEqual(len(res), 766) ++ self.assertEqual(len(res), 733) + + def test_RegExpName(self): + """ diff --git a/debian/patches/series b/debian/patches/series new file mode 100644 index 000000000..1ee1833c6 --- /dev/null +++ b/debian/patches/series @@ -0,0 +1 @@ +fix-probably_alive_test.patch diff --git a/debian/watch b/debian/watch index 37a5375ec..95985b298 100644 --- a/debian/watch +++ b/debian/watch @@ -1,8 +1,6 @@ -version=3 - +version=4 opts=\ -dversionmangle=s/(\~|\+)(debian|dfsg|ds|deb)(\.\d+)?$//,\ -filenamemangle=s/.+\/v?(\d\S*)\.tar\.gz/$1\.tar\.gz/,\ +filenamemangle=s/.+\/v?(\d\S+)\.tar\.gz/gramps-project-$1\.tar\.gz/,\ repacksuffix=~dfsg \ https://github.com/gramps-project/gramps/tags \ -.*/archive/v?([\d\.]+).tar.gz +.*/v?(\d\S+)\.tar\.gz diff --git a/gramps/gen/const.py b/gramps/gen/const.py index bd8fb728d..9eaa4244b 100644 --- a/gramps/gen/const.py +++ b/gramps/gen/const.py @@ -226,7 +226,7 @@ GTK_GETTEXT_DOMAIN = 'gtk30' # #------------------------------------------------------------------------- COPYRIGHT_MSG = "© 2001-2006 Donald N. Allingham\n" \ - "© 2007-2020 The Gramps Developers" + "© 2007-2022 The Gramps Developers" COMMENTS = _("Gramps\n (Genealogical Research and Analysis " "Management Programming System)\n" "is a personal genealogy program.") diff --git a/gramps/gen/datehandler/_date_nl.py b/gramps/gen/datehandler/_date_nl.py index add9a14cc..83c4fbde1 100644 --- a/gramps/gen/datehandler/_date_nl.py +++ b/gramps/gen/datehandler/_date_nl.py @@ -83,35 +83,37 @@ class DateParserNL(DateParser): month_to_int["xbris"] = 12 modifier_to_int = { - 'voor' : Date.MOD_BEFORE, - 'na' : Date.MOD_AFTER, - 'tegen' : Date.MOD_ABOUT, - 'om' : Date.MOD_ABOUT, - 'rond' : Date.MOD_ABOUT, - 'circa' : Date.MOD_ABOUT, - 'ca.' : Date.MOD_ABOUT, + 'voor' : Date.MOD_BEFORE, + 'na' : Date.MOD_AFTER, + 'ca.' : Date.MOD_ABOUT, + 'circa' : Date.MOD_ABOUT, + 'om' : Date.MOD_ABOUT, + 'omstreeks' : Date.MOD_ABOUT, + 'ongeveer' : Date.MOD_ABOUT, + 'rond' : Date.MOD_ABOUT, + 'tegen' : Date.MOD_ABOUT, } calendar_to_int = { - 'gregoriaans' : Date.CAL_GREGORIAN, - 'greg.' : Date.CAL_GREGORIAN, - 'juliaans' : Date.CAL_JULIAN, - 'jul.' : Date.CAL_JULIAN, - 'hebreeuws' : Date.CAL_HEBREW, - 'hebr.' : Date.CAL_HEBREW, - 'islamitisch' : Date.CAL_ISLAMIC, - 'isl.' : Date.CAL_ISLAMIC, - 'franse republiek': Date.CAL_FRENCH, - 'fran.' : Date.CAL_FRENCH, - 'persisch' : Date.CAL_PERSIAN, - 'zweeds' : Date.CAL_SWEDISH, - 'z' : Date.CAL_SWEDISH, + 'gregoriaans' : Date.CAL_GREGORIAN, + 'greg.' : Date.CAL_GREGORIAN, + 'juliaans' : Date.CAL_JULIAN, + 'jul.' : Date.CAL_JULIAN, + 'hebreeuws' : Date.CAL_HEBREW, + 'hebr.' : Date.CAL_HEBREW, + 'islamitisch' : Date.CAL_ISLAMIC, + 'isl.' : Date.CAL_ISLAMIC, + 'frans republiekeins' : Date.CAL_FRENCH, + 'fran.' : Date.CAL_FRENCH, + 'persisch' : Date.CAL_PERSIAN, + 'zweeds' : Date.CAL_SWEDISH, + 'z' : Date.CAL_SWEDISH, } quality_to_int = { - 'geschat' : Date.QUAL_ESTIMATED, + 'geschat' : Date.QUAL_ESTIMATED, 'gesch.' : Date.QUAL_ESTIMATED, - 'berekend' : Date.QUAL_CALCULATED, + 'berekend' : Date.QUAL_CALCULATED, 'ber.' : Date.QUAL_CALCULATED, } @@ -147,17 +149,17 @@ class DateDisplayNL(DateDisplay): calendar = ( "", "juliaans", "hebreeuws", - "franse republiek", "persisch", "islamitisch", + "frans republikeins", "persisch", "islamitisch", "zweeds" ) - _mod_str = ("", "voor ", "na ", "rond ", "", "", "") + _mod_str = ("", "voor ", "na ", "omstreeks ", "", "", "") _qual_str = ("", "geschat ", "berekend ") _bce_str = "%s v. Chr." formats = ( - "JJJJ-MM-DD (ISO)", "Numerisch DD/MM/JJ", "Maand Dag, Jaar", + "JJJJ-MM-DD (ISO)", "Numeriek DD/MM/JJJJ", "Maand Dag, Jaar", "Mnd. Dag Jaar", "Dag Maand Jaar", "Dag Mnd. Jaar" ) # this definition must agree with its "_display_gregorian" method diff --git a/gramps/gen/db/dummydb.py b/gramps/gen/db/dummydb.py index 2efec5f93..79b702c58 100644 --- a/gramps/gen/db/dummydb.py +++ b/gramps/gen/db/dummydb.py @@ -52,7 +52,6 @@ methods should be changed to generate exceptions. Possibly by globally changing # #------------------------------------------------------------------------- import logging -import os import inspect from abc import ABCMeta from types import FunctionType @@ -160,10 +159,12 @@ def wrapper(method): """ class_name = args[0].__class__.__name__ func_name = method.__name__ - caller_frame = inspect.stack()[1] + frame = inspect.currentframe() + c_frame = frame.f_back + c_code = c_frame.f_code LOG.debug('calling %s.%s()... from file %s, line %s in %s', - class_name, func_name, os.path.split(caller_frame[1])[1], - caller_frame[2], caller_frame[3]) + class_name, func_name, c_code.co_filename, c_frame.f_lineno, + c_code.co_name) return method(*args, **keywargs) return wrapped diff --git a/gramps/gen/db/txn.py b/gramps/gen/db/txn.py index 52620c6dd..fe07e5390 100644 --- a/gramps/gen/db/txn.py +++ b/gramps/gen/db/txn.py @@ -78,15 +78,13 @@ class DbTxn(defaultdict): elapsed_time = time.time() - self.start_time if __debug__: - caller_frame = inspect.stack()[1] + frame = inspect.currentframe() + c_frame = frame.f_back + c_code = c_frame.f_code _LOG.debug(" **** DbTxn %s exited. Called from file %s, " - "line %s, in %s **** %.2f seconds" % - ((hex(id(self)),)+ - (os.path.split(caller_frame[1])[1],)+ - tuple(caller_frame[i] for i in range(2, 4))+ - (elapsed_time,) - ) - ) + "line %s, in %s **** %.2f seconds", + hex(id(self)), c_code.co_filename, c_frame.f_lineno, + c_code.co_name, elapsed_time) return False diff --git a/gramps/gen/db/utils.py b/gramps/gen/db/utils.py index b5a1336f7..ad9c87dc1 100644 --- a/gramps/gen/db/utils.py +++ b/gramps/gen/db/utils.py @@ -42,6 +42,8 @@ from ..const import PLUGINS_DIR, USER_PLUGINS from ..constfunc import win, get_env_var from ..config import config from .dbconst import DBLOGNAME, DBLOCKFN, DBBACKEND +from ..const import GRAMPS_LOCALE as glocale +_ = glocale.translation.gettext #------------------------------------------------------------------------- # @@ -70,12 +72,14 @@ def make_database(plugin_id): database = getattr(mod, pdata.databaseclass) db = database() import inspect - caller_frame = inspect.stack()[1] + frame = inspect.currentframe() + c_frame = frame.f_back + c_code = c_frame.f_code _LOG.debug("Database class instance created Class:%s instance:%s. " - "Called from File %s, line %s, in %s" - % ((db.__class__.__name__, hex(id(db))) - + (os.path.split(caller_frame[1])[1],) - + tuple(caller_frame[i] for i in range(2, 4)))) + "Called from File %s, line %s, in %s", + db.__class__.__name__, hex(id(db)), c_code.co_filename, + c_frame.f_lineno, c_code.co_name) + return db else: raise Exception("can't load database backend: '%s'" % plugin_id) @@ -209,8 +213,8 @@ def write_lock_file(name): if win(): user = get_env_var('USERNAME') host = get_env_var('USERDOMAIN') - if host is None: - host = "" + if not user: + user = _("Unknown") else: host = os.uname()[1] # An ugly workaround for os.getlogin() issue with Konsole diff --git a/gramps/gen/dbstate.py b/gramps/gen/dbstate.py index 21e7e4a5b..6e748cafe 100644 --- a/gramps/gen/dbstate.py +++ b/gramps/gen/dbstate.py @@ -29,7 +29,6 @@ Provide the database state class # #------------------------------------------------------------------------ import sys -import os import logging import inspect @@ -88,10 +87,12 @@ class DbState(Callback): """ class_name = self.__class__.__name__ func_name = "is_open" - caller_frame = inspect.stack()[1] + frame = inspect.currentframe() + c_frame = frame.f_back + c_code = c_frame.f_code _LOG.debug('calling %s.%s()... from file %s, line %s in %s', - class_name, func_name, os.path.split(caller_frame[1])[1], - caller_frame[2], caller_frame[3]) + class_name, func_name, c_code.co_filename, c_frame.f_lineno, + c_code.co_name) return (self.db is not None) and self.db.is_open() def change_database(self, database): diff --git a/gramps/gen/display/name.py b/gramps/gen/display/name.py index 8012e9c5a..10b269189 100644 --- a/gramps/gen/display/name.py +++ b/gramps/gen/display/name.py @@ -999,6 +999,8 @@ class NameDisplay: 1. if group name is defined, use that 2. if group name is defined for the primary surname, use that 3. use primary surname itself otherwise + 4. if no primary surname, do we have a ma/patronymic surname ? + in this case, group name will be the ma/patronymic name. :param pn: raw unserialized data of name :type pn: tuple @@ -1007,8 +1009,25 @@ class NameDisplay: """ if pn[_GROUP]: return pn[_GROUP] - return db.get_name_group_mapping(_raw_primary_surname_only( - pn[_SURNAME_LIST])) + name = pn[_GROUP] + if not name: + # if we have no primary surname, perhaps we have a + # patronymic/matronynic name ? + srnme = pn[_ORIGINPATRO] + surname = [] + for _surname in srnme: + if (_surname[_TYPE_IN_LIST][0] == _ORIGINPATRO + or _surname[_TYPE_IN_LIST][0] == _ORIGINMATRO): + # Yes, we have one. + surname = [_surname] + # name1 is the ma/patronymic name. + name1 = _raw_patro_surname_only(surname) + if name1 and len(srnme) == 1: + name = db.get_name_group_mapping(name1) + if not name: + name = db.get_name_group_mapping(_raw_primary_surname_only( + pn[_SURNAME_LIST])) + return name def _make_fn(self, format_str, d, args): """ diff --git a/gramps/gen/filters/rules/media/__init__.py b/gramps/gen/filters/rules/media/__init__.py index 189383e42..2508347bf 100644 --- a/gramps/gen/filters/rules/media/__init__.py +++ b/gramps/gen/filters/rules/media/__init__.py @@ -51,6 +51,7 @@ editor_rule_list = [ MediaPrivate, MatchesFilter, MatchesSourceConfidence, + HasMedia, HasAttribute, ChangedSince, HasTag, diff --git a/gramps/gen/filters/rules/person/_matcheseventfilter.py b/gramps/gen/filters/rules/person/_matcheseventfilter.py index 542cdcbdb..b352a2acf 100644 --- a/gramps/gen/filters/rules/person/_matcheseventfilter.py +++ b/gramps/gen/filters/rules/person/_matcheseventfilter.py @@ -52,7 +52,7 @@ class MatchesEventFilter(MatchesEventFilterBase): name = _('Persons with events matching the ') description = _("Matches persons who have events that match a certain" " event filter") - category = _('General filters') + category = _('Event filters') # we want to have this filter show event filters namespace = 'Event' diff --git a/gramps/gen/filters/rules/test/person_rules_test.py b/gramps/gen/filters/rules/test/person_rules_test.py index e7997b025..beb982f22 100644 --- a/gramps/gen/filters/rules/test/person_rules_test.py +++ b/gramps/gen/filters/rules/test/person_rules_test.py @@ -98,9 +98,10 @@ class BaseTest(unittest.TestCase): filter_.set_invert(invert) stime = perf_counter() results = filter_.apply(self.db) - if __debug__: - rulename = inspect.stack()[1][3] - print("%s: %.2f\n" % (rulename, perf_counter() - stime)) + # if __debug__: + # frame = inspect.currentframe() + # rulename = frame.f_back.f_code.co_name + # print("%s: %.2f\n" % (rulename, perf_counter() - stime)) return set(results) def test_Complex_1(self): @@ -346,7 +347,7 @@ class BaseTest(unittest.TestCase): """ rule = ProbablyAlive(['1900']) res = self.filter_with_rule(rule) - self.assertEqual(len(res), 766) + self.assertEqual(len(res), 733) def test_RegExpName(self): """ diff --git a/gramps/gen/lib/date.py b/gramps/gen/lib/date.py index 83917de72..d2a7fdbf8 100644 --- a/gramps/gen/lib/date.py +++ b/gramps/gen/lib/date.py @@ -77,14 +77,14 @@ class Span: self.precision = 2 self.negative = False if self.valid: - if self.date1.calendar != Date.CAL_GREGORIAN: - self.date1 = self.date1.to_calendar("gregorian") - if self.date2.calendar != Date.CAL_GREGORIAN: - self.date2 = self.date2.to_calendar("gregorian") if self.date1.sortval < self.date2.sortval: self.date1 = date2 self.date2 = date1 self.negative = True + if self.date1.calendar != Date.CAL_GREGORIAN: + self.date1 = self.date1.to_calendar("gregorian") + if self.date2.calendar != Date.CAL_GREGORIAN: + self.date2 = self.date2.to_calendar("gregorian") if self.date1.get_modifier() == Date.MOD_NONE: if self.date2.get_modifier() == Date.MOD_NONE: val = self.date1.sortval - self.date2.sortval diff --git a/gramps/gen/lib/test/date_test.py b/gramps/gen/lib/test/date_test.py index d4312637b..13517224a 100644 --- a/gramps/gen/lib/test/date_test.py +++ b/gramps/gen/lib/test/date_test.py @@ -38,7 +38,7 @@ from ...datehandler import get_date_formats, set_format from ...datehandler import parser as _dp from ...datehandler import displayer as _dd from ...datehandler._datedisplay import DateDisplayEn -from ...lib.date import Date, DateError, Today, calendar_has_fixed_newyear +from ...lib.date import Date, DateError, Today, calendar_has_fixed_newyear, Span date_tests = {} @@ -432,6 +432,36 @@ class ArithmeticDateTest(BaseDateTest): self.assertEqual(val1, val2, "'%s' should be '%s' but was '%s'" % (exp1, val2, val1)) +#------------------------------------------------------------------------- +# +# SpanTest +# +#------------------------------------------------------------------------- +class SpanTest(BaseDateTest): + """ + Test spans. + """ + tests = [((2000, 1, 31), (2000, 1, 1), 30), + ((1799, 11, 19), (8, 2, 18, Date.CAL_FRENCH), 10), + ((8, 2, 18, Date.CAL_FRENCH), (1799, 11, 4), 5), + ((8, 2, 18, Date.CAL_FRENCH), (3, 2, 9, Date.CAL_FRENCH), 1836)] + + def test_evaluate(self): + for value1, value2, duration in self.tests: + date1 = self._get_date(value1) + date2 = self._get_date(value2) + span1 = Span(date1, date2) + self.assertEqual(int(span1), duration) + span2 = Span(date2, date1) + self.assertEqual(int(span2), -duration) + + def _get_date(self, value): + date = Date() + if len(value) == 4: + date.set_calendar(value[3]) + date.set_yr_mon_day(value[0], value[1], value[2]) + return date + #------------------------------------------------------------------------- # # SwedishDateTest diff --git a/gramps/gen/merge/mergepersonquery.py b/gramps/gen/merge/mergepersonquery.py index f7162819c..93f134945 100644 --- a/gramps/gen/merge/mergepersonquery.py +++ b/gramps/gen/merge/mergepersonquery.py @@ -31,8 +31,8 @@ Provide merge capabilities for persons. #------------------------------------------------------------------------- from ..db import DbTxn from ..const import GRAMPS_LOCALE as glocale -_ = glocale.translation.sgettext from ..errors import MergeError +_ = glocale.translation.sgettext #------------------------------------------------------------------------- # @@ -49,11 +49,12 @@ class MergePersonQuery: self.titanic = titanic if self.check_for_spouse(self.phoenix, self.titanic): raise MergeError(_("Spouses cannot be merged. To merge these " - "people, you must first break the relationship between them.")) + "people, you must first break the relationship" + " between them.")) if self.check_for_child(self.phoenix, self.titanic): raise MergeError(_("A parent and child cannot be merged. To merge " - "these people, you must first break the relationship between " - "them.")) + "these people, you must first break the relatio" + "nship between them.")) def check_for_spouse(self, person1, person2): """Return if person1 and person2 are spouses of eachother.""" @@ -80,12 +81,12 @@ class MergePersonQuery: main_family.merge(family) for childref in family.get_child_ref_list(): child = self.database.get_person_from_handle( - childref.get_reference_handle()) + childref.get_reference_handle()) if main_family_handle in child.parent_family_list: child.remove_handle_references('Family', [family_handle]) else: child.replace_handle_reference('Family', family_handle, - main_family_handle) + main_family_handle) self.database.commit_person(child, trans) if self.phoenix: self.phoenix.remove_family_handle(family_handle) @@ -143,7 +144,8 @@ class MergePersonQuery: for family_handle in self.phoenix.get_parent_family_handle_list(): family = self.database.get_family_from_handle(family_handle) if family.has_handle_reference('Person', old_handle): - family.replace_handle_reference('Person', old_handle,new_handle) + family.replace_handle_reference('Person', old_handle, + new_handle) self.database.commit_family(family, trans) family_merge_guard = False @@ -182,7 +184,10 @@ class MergePersonQuery: self.database.commit_family(family, trans) parent_list.append(parents) - if self.database.get_default_handle() == old_handle: - self.database.set_default_person_handle(None) + hp_hdl = self.database.get_default_handle() + if (hp_hdl in (self.phoenix.get_handle(), self.titanic.get_handle()) + and hp_hdl != self.phoenix.get_handle()): + self.database.set_default_person_handle(self.phoenix.get_handle()) + self.database.remove_person(old_handle, trans) return family_merge_ok diff --git a/gramps/gen/plug/docgen/graphdoc.py b/gramps/gen/plug/docgen/graphdoc.py index ea3ed242f..670e88128 100644 --- a/gramps/gen/plug/docgen/graphdoc.py +++ b/gramps/gen/plug/docgen/graphdoc.py @@ -113,6 +113,10 @@ else: _GS_CMD = where_is("gs") +def esc(id_txt): + return id_txt.replace('"', '\\"') + + #------------------------------------------------------------------------------ # # GVOptions @@ -575,7 +579,7 @@ class GVDocBase(BaseDoc, GVDoc): text += ' URL="%s"' % url text += " ]" - self.write(' "%s" %s;\n' % (node_id, text)) + self.write(' "%s" %s;\n' % (esc(node_id), text)) def add_link(self, id1, id2, style="", head="", tail="", comment=""): """ @@ -583,7 +587,7 @@ class GVDocBase(BaseDoc, GVDoc): Implements GVDocBase.add_link(). """ - self.write(' "%s" -> "%s"' % (id1, id2)) + self.write(' "%s" -> "%s"' % (esc(id1), esc(id2))) if style or head or tail: self.write(' [') @@ -635,7 +639,7 @@ class GVDocBase(BaseDoc, GVDoc): Implements GVDocBase.add_samerank(). """ - self.write(' {rank=same "%s" "%s"}\n' % (id1, id2)) + self.write(' {rank=same "%s" "%s"}\n' % (esc(id1), esc(id2))) def rewrite_label(self, id, label): """ @@ -643,7 +647,7 @@ class GVDocBase(BaseDoc, GVDoc): Implements GVDocBase.rewrite_label(). """ - self.write(' "%s" [label = "%s"]\n' % (id, label)) + self.write(' "%s" [label = "%s"]\n' % (esc(id), label)) def start_subgraph(self, graph_id): """ Implement GVDocBase.start_subgraph() """ diff --git a/gramps/gen/utils/alive.py b/gramps/gen/utils/alive.py index 84d8b62a6..240db5930 100644 --- a/gramps/gen/utils/alive.py +++ b/gramps/gen/utils/alive.py @@ -142,6 +142,8 @@ class ProbablyAlive: # person died more than MAX after current year if death_date.is_valid(): birth_date = death_date.copy_offset_ymd(year=-self.MAX_AGE_PROB_ALIVE) + else: + birth_date = death_date explain = _("death date") if not death_date and birth_date: diff --git a/gramps/gen/utils/callback.py b/gramps/gen/utils/callback.py index b2f801cd7..a3f94e444 100644 --- a/gramps/gen/utils/callback.py +++ b/gramps/gen/utils/callback.py @@ -324,12 +324,16 @@ class Callback: return # Check signal exists + frame = inspect.currentframe() + c_frame = frame.f_back + c_code = c_frame.f_code + frame_info = (c_code.co_filename, c_frame.f_lineno, c_code.co_name) if signal_name not in self.__signal_map: self._warn("Attempt to emit to unknown signal: %s\n" " from: file: %s\n" " line: %d\n" " func: %s\n" - % ((str(signal_name), ) + inspect.stack()[1][1:4])) + % ((str(signal_name), ) + frame_info)) return # check that the signal is not already being emitted. This prevents @@ -340,7 +344,7 @@ class Callback: " from: file: %s\n" " line: %d\n" " func: %s\n" - % ((str(signal_name), ) + inspect.stack()[1][1:4])) + % ((str(signal_name), ) + frame_info)) return try: @@ -358,7 +362,7 @@ class Callback: " from: file: %s\n" " line: %d\n" " func: %s\n" - % ((str(signal_name), ) + inspect.stack()[1][1:4])) + % ((str(signal_name), ) + frame_info)) return # type check arguments @@ -369,7 +373,7 @@ class Callback: " from: file: %s\n" " line: %d\n" " func: %s\n" - % ((str(signal_name), ) + inspect.stack()[1][1:4])) + % ((str(signal_name), ) + frame_info)) return if len(args) > 0: @@ -379,7 +383,7 @@ class Callback: " from: file: %s\n" " line: %d\n" " func: %s\n" - % ((str(signal_name), ) + inspect.stack()[1][1:4])) + % ((str(signal_name), ) + frame_info)) return if arg_types is not None: @@ -391,7 +395,7 @@ class Callback: " line: %d\n" " func: %s\n" " arg passed was: %s, type of arg passed %s, type should be: %s\n" - % ((str(signal_name), ) + inspect.stack()[1][1:4] +\ + % ((str(signal_name), ) + frame_info +\ (args[i], repr(type(args[i])), repr(arg_types[i])))) return if signal_name in self.__callback_map: diff --git a/gramps/gen/utils/docgen/csvtab.py b/gramps/gen/utils/docgen/csvtab.py index 01822ebf4..7899081cf 100644 --- a/gramps/gen/utils/docgen/csvtab.py +++ b/gramps/gen/utils/docgen/csvtab.py @@ -49,7 +49,7 @@ class CSVTab(TabbedDoc): else: self.filename = filename - self.f = open(self.filename, "w", + self.f = open(self.filename, "w", newline='', encoding='utf_8_sig' if win() else 'utf_8') self.writer = csv.writer(self.f) diff --git a/gramps/gen/utils/grampslocale.py b/gramps/gen/utils/grampslocale.py index 77c430894..c5b4a4faf 100644 --- a/gramps/gen/utils/grampslocale.py +++ b/gramps/gen/utils/grampslocale.py @@ -526,6 +526,8 @@ class GrampsLocale: # with locale instead of gettext. Win32 doesn't support bindtextdomain. if self.localedir: if not sys.platform == 'win32': + # bug12278, _build_popup_ui() under linux and macOS + locale.textdomain(self.localedomain) locale.bindtextdomain(self.localedomain, self.localedir) else: self._win_bindtextdomain(self.localedomain.encode('utf-8'), diff --git a/gramps/grampsapp.py b/gramps/grampsapp.py index be28cfd28..bc0f36ae9 100644 --- a/gramps/grampsapp.py +++ b/gramps/grampsapp.py @@ -127,15 +127,32 @@ if win(): elif not os.path.isdir(HOME_DIR): os.makedirs(HOME_DIR) sys.stdout = sys.stderr = open(logfile, "w", encoding='utf-8') -stderrh = logging.StreamHandler(sys.stderr) -stderrh.setFormatter(form) -stderrh.setLevel(logging.DEBUG) +# macOS sets stderr to /dev/null when running without a terminal, +# e.g. if Gramps.app is lauched by double-clicking on it in +# finder. Write to a file instead. +if mac() and not sys.stdin.isatty(): + from tempfile import gettempdir -# Setup the base level logger, this one gets -# everything. -l = logging.getLogger() -l.setLevel(logging.WARNING) -l.addHandler(stderrh) + log_file_name = 'gramps-' + str(os.getpid()) + '.log' + log_file_path = os.path.join(gettempdir(), log_file_name) + log_file_handler = logging.FileHandler(log_file_path, mode='a', + encoding='utf-8') + log_file_handler.setFormatter(form) + log_file_handler.setLevel(logging.DEBUG) + + logger = logging.getLogger() + logger.setLevel(logging.WARNING) + logger.addHandler(log_file_handler) +else: + stderrh = logging.StreamHandler(sys.stderr) + stderrh.setFormatter(form) + stderrh.setLevel(logging.DEBUG) + + # Setup the base level logger, this one gets + # everything. + l = logging.getLogger() + l.setLevel(logging.WARNING) + l.addHandler(stderrh) def exc_hook(err_type, value, t_b): diff --git a/gramps/gui/clipboard.py b/gramps/gui/clipboard.py index 7d33b95c2..e04f88f41 100644 --- a/gramps/gui/clipboard.py +++ b/gramps/gui/clipboard.py @@ -471,27 +471,36 @@ class ClipCitation(ClipHandleWrapper): def refresh(self): if self._handle: - citation = clipdb.get_citation_from_handle(self._handle) - if citation: - self._title = citation.get_gramps_id() - notelist = list(map(clipdb.get_note_from_handle, - citation.get_note_list())) - srctxtlist = [note for note in notelist - if note.get_type() == NoteType.SOURCE_TEXT] - page = citation.get_page() - if not page: - page = _('NA', 'not available') - text = "" - if srctxtlist: - text = " ".join(srctxtlist[0].get().split()) - #String must be unicode for truncation to work for non - #ascii characters - text = str(text) - if len(text) > 60: - text = text[:60] + "..." - self._value = _("Volume/Page: %(pag)s -- %(sourcetext)s") % { - 'pag' : page, - 'sourcetext' : text} + try: + citation = clipdb.get_citation_from_handle(self._handle) + if citation: + self._title = citation.get_gramps_id() + notelist = list(map(clipdb.get_note_from_handle, + citation.get_note_list())) + srctxtlist = [note for note in notelist + if note.get_type() == NoteType.SOURCE_TEXT] + page = citation.get_page() + if not page: + page = _('NA', 'not available') + text = "" + if srctxtlist: + text = " ".join(srctxtlist[0].get().split()) + #String must be unicode for truncation to work for non + #ascii characters + text = str(text) + if len(text) > 60: + text = text[:60] + "..." + self._value = _("Volume/Page: %(pag)s -- %(sourcetext)s" + ) % { 'pag' : page, + 'sourcetext' : text} + except: + # We are in the Source tree view. The shortcuts only + # work for citations. + print("We cannot copy the source from this view." + " Use drag and drop.") + self._title = self._value = '' + self._pickle = self._type = self._objclass = None + self._handle = self._dbid = self._dbname = None class ClipRepoRef(ClipObjWrapper): @@ -1281,6 +1290,14 @@ class ClipboardListView: model.insert_before(node, data) else: model.insert_after(node, data) + elif isinstance(data[1], ClipCitation): + if data[3]: + # we have a real citation + model.append(data) + #else: + # We are in a Source treeview and trying + # to copy a source with a shortcut. + # Use drag and drop to do that. else: model.append(data) diff --git a/gramps/gui/dbloader.py b/gramps/gui/dbloader.py index 9c62e6d35..ff8b589e7 100644 --- a/gramps/gui/dbloader.py +++ b/gramps/gui/dbloader.py @@ -469,7 +469,7 @@ class GrampsImportFileDialog(ManagedWindow): return True else: try: - f = open(filename,'w') + f = open(filename, 'w') f.close() os.remove(filename) except IOError: @@ -485,7 +485,6 @@ class GrampsImportFileDialog(ManagedWindow): self.import_info = None self._begin_progress() self.uistate.set_sensitive(False) - self.uistate.viewmanager.enable_menu(False) try: #an importer can return an object with info, object.info_text() @@ -506,7 +505,6 @@ class GrampsImportFileDialog(ManagedWindow): except Exception: _LOG.error("Failed to import database.", exc_info=True) self.uistate.set_sensitive(True) - self.uistate.viewmanager.enable_menu(True) self._end_progress() def build_menu_names(self, obj): # this is meaningless since it's modal diff --git a/gramps/gui/dbman.py b/gramps/gui/dbman.py index 4fcecd371..7656affbb 100644 --- a/gramps/gui/dbman.py +++ b/gramps/gui/dbman.py @@ -1013,7 +1013,7 @@ class DbManager(CLIDbManager, ManagedWindow): """ Handle the reception of drag data """ - drag_value = selection.get_data().decode() + drag_value = selection.get_data().decode().strip(' \r\n\x00') fname = None type = None title = None diff --git a/gramps/gui/displaystate.py b/gramps/gui/displaystate.py index 904b2d102..9be76ba42 100644 --- a/gramps/gui/displaystate.py +++ b/gramps/gui/displaystate.py @@ -538,7 +538,10 @@ class DisplayState(Callback): history.push(handle) def set_sensitive(self, state): - self.window.set_sensitive(state) + tbar = self.uimanager.get_widget('ToolBar') + tbar.set_sensitive(state) + self.viewmanager.hpane.set_sensitive(state) + self.uimanager.enable_all_actions(state) def db_changed(self, db): db.connect('long-op-start', self.progress_monitor.add_op) diff --git a/gramps/gui/editors/editname.py b/gramps/gui/editors/editname.py index dc81d4864..ccb5c35d2 100644 --- a/gramps/gui/editors/editname.py +++ b/gramps/gui/editors/editname.py @@ -366,6 +366,7 @@ class EditName(EditSecondary): 5/ local set, not global set --> set (change local) 6/ local set, global set --> set (set to global if possible) """ + ngm = False # name group mapping setting closeit = True surname = self.obj.get_primary_surname().get_surname() group_as= self.obj.get_group_as() @@ -388,7 +389,7 @@ class EditName(EditSecondary): val = q.run() if val: #delete the grouping link on database - self.db.set_name_group_mapping(surname, None) + ngm = None # delay setting until dialog closes self.obj.set_group_as("") else : closeit = False @@ -421,9 +422,9 @@ class EditName(EditSecondary): val = q.run() if val: if group_as == surname : - self.db.set_name_group_mapping(surname, None) + ngm = None # delay setting until dialog closes else: - self.db.set_name_group_mapping(surname, group_as) + ngm = group_as # delay setting until dialog closes self.obj.set_group_as("") else: if self.global_group_set : @@ -455,10 +456,15 @@ class EditName(EditSecondary): pass if closeit: + db = self.db # close cleanup loses self.db, so save for later if self.callback: self.callback(self.obj) self.callback = None self.close() + # bug 12328 to avoid gui interaction during view rebuild, delay + # the rebuild until closeing this dialog + if ngm is not False: + db.set_name_group_mapping(surname, ngm) def _cleanup_on_exit(self): """ diff --git a/gramps/gui/editors/editplace.py b/gramps/gui/editors/editplace.py index ff9c1fd64..d141bca62 100644 --- a/gramps/gui/editors/editplace.py +++ b/gramps/gui/editors/editplace.py @@ -186,13 +186,40 @@ class EditPlace(EditPrimary): self.db.readonly) def set_latlongitude(self, value): + """ + This method is useful for directly copying the coordinates + of openstreetmap, googlemaps, and perhaps other if they + provide coordinates like it is define in conv_lat_lon + (see gramps/gen/utils/place.py) + + To copy the coordinates: + + - openstreetmap: + 1 - choose the place where you want to save the coordinates. + 2 - right click on this place + 3 - select "show address" + 4 - On the left side of the map, copy the coordinates of + "Result from internal" + 5 - In the latlon field of the edit place window of gramps, + type V + + - googlemap: + 1 - choose the place where you want to save the coordinates. + 2 - right click on this place + 3 - select the coordinates at the top of the popup window. + They are automaticaly copied. + 4 - In the latlon field of the edit place window of gramps, + type V + + """ try: - parts = value.index(', ') + # Bug 12349, 12374 + parts = value.split(', ') if len(parts) == 2: - longitude = parts[0].strip().replace(',', '.') - latitude = parts[1].strip().replace(',', '.') + latitude = parts[0].strip().replace(',', '.') + longitude = parts[1].strip().replace(',', '.') else: - longitude, latitude = value.split(',') + latitude, longitude = value.split(',') self.longitude.set_text(longitude) self.latitude.set_text(latitude) diff --git a/gramps/gui/editors/editplaceref.py b/gramps/gui/editors/editplaceref.py index 4aee31212..30c41ffc7 100644 --- a/gramps/gui/editors/editplaceref.py +++ b/gramps/gui/editors/editplaceref.py @@ -181,12 +181,13 @@ class EditPlaceRef(EditReference): def set_latlongitude(self, value): try: - parts = value.index(', ') + # Bug 12349, 12374 + parts = value.split(', ') if len(parts) == 2: - longitude = parts[0].strip().replace(',', '.') - latitude = parts[1].strip().replace(',', '.') + latitude = parts[0].strip().replace(',', '.') + longitude = parts[1].strip().replace(',', '.') else: - longitude, latitude = value.split(',') + latitude, longitude = value.split(',') self.longitude.set_text(longitude) self.latitude.set_text(latitude) diff --git a/gramps/gui/glade/editplace.glade b/gramps/gui/glade/editplace.glade index 32e97ac93..685c7965e 100644 --- a/gramps/gui/glade/editplace.glade +++ b/gramps/gui/glade/editplace.glade @@ -1,11 +1,13 @@ - + - False dialog + + + True @@ -101,7 +103,7 @@ True - True + False start Either use the two fields below to enter coordinates (latitude and longitude), True diff --git a/gramps/gui/selectors/baseselector.py b/gramps/gui/selectors/baseselector.py index 325db1703..ed45e9b6c 100644 --- a/gramps/gui/selectors/baseselector.py +++ b/gramps/gui/selectors/baseselector.py @@ -325,7 +325,6 @@ class BaseSelector(ManagedWindow): self.sortorder = Gtk.SortType.ASCENDING else: self.sortorder = Gtk.SortType.DESCENDING - self.model.reverse_order() self.build_tree() return True diff --git a/gramps/gui/uimanager.py b/gramps/gui/uimanager.py index ace191868..fc5c46128 100644 --- a/gramps/gui/uimanager.py +++ b/gramps/gui/uimanager.py @@ -496,6 +496,16 @@ class UIManager(): """ return group.act_group.lookup_action(actionname) + def enable_all_actions(self, state): + for group in self.action_groups: + if group.act_group: + for item in group.actionlist: + action = group.act_group.lookup_action(item[ACTION_NAME]) + if action: + # We check in case the group has not been inserted into + # UIManager yet + action.set_enabled(group.sensitive if state else False) + def dump_all_accels(self): ''' A function used diagnostically to see what accels are present. This will only dump the current accel set, if other non-open windows diff --git a/gramps/gui/utils.py b/gramps/gui/utils.py index a6f963e8d..187814c49 100644 --- a/gramps/gui/utils.py +++ b/gramps/gui/utils.py @@ -450,7 +450,7 @@ def open_file_with_default_application(path, uistate): GLib.timeout_add_seconds(1, poll_external, (proc, errstrings, uistate)) return -def process_pending_events(max_count=10): +def process_pending_events(max_count=20): """ Process pending events, but don't get into an infinite loop. """ diff --git a/gramps/gui/viewmanager.py b/gramps/gui/viewmanager.py index 9c5e9f9b9..218989975 100644 --- a/gramps/gui/viewmanager.py +++ b/gramps/gui/viewmanager.py @@ -277,13 +277,13 @@ class ViewManager(CLIManager): Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.MOD1_MASK) vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL) self.window.add(vbox) - hpane = Gtk.Paned() + self.hpane = Gtk.Paned() self.ebox = Gtk.EventBox() self.navigator = Navigator(self) self.ebox.add(self.navigator.get_top()) - hpane.pack1(self.ebox, False, False) - hpane.show() + self.hpane.pack1(self.ebox, False, False) + self.hpane.show() self.notebook = Gtk.Notebook() self.notebook.set_scrollable(True) @@ -292,13 +292,14 @@ class ViewManager(CLIManager): self.__init_lists() self.__build_ui_manager() - hpane.add2(self.notebook) + self.hpane.add2(self.notebook) toolbar = self.uimanager.get_widget('ToolBar') + toolbar.show_all() self.statusbar = Statusbar() self.statusbar.show() vbox.pack_end(self.statusbar, False, True, 0) vbox.pack_start(toolbar, False, True, 0) - vbox.pack_end(hpane, True, True, 0) + vbox.pack_end(self.hpane, True, True, 0) vbox.show() self.uistate = DisplayState(self.window, self.statusbar, @@ -839,6 +840,7 @@ class ViewManager(CLIManager): hbox.add(Gtk.Label(label=pdata.name)) hbox.show_all() page_num = self.notebook.append_page(page.get_display(), hbox) + self.active_page.post_create() if not self.file_loaded: self.uimanager.set_actions_visible(self.actiongroup, False) self.uimanager.set_actions_visible(self.readonlygroup, False) @@ -887,12 +889,17 @@ class ViewManager(CLIManager): while Gtk.events_pending(): Gtk.main_iteration() - self.uimanager.update_menu() + # bug 12048 this avoids crash if part of toolbar in view is not shown + # because of a small screen when changing views. Part of the Gtk code + # was deleting a toolbar object too soon; and another part of Gtk still + # had a reference. + def page_changer(self): + self.uimanager.update_menu() + self.active_page.change_page() + return False - while Gtk.events_pending(): - Gtk.main_iteration() - - self.active_page.change_page() + GLib.idle_add(page_changer, self, + priority=GLib.PRIORITY_DEFAULT_IDLE - 10) def __delete_pages(self): """ @@ -997,7 +1004,8 @@ class ViewManager(CLIManager): The method called after load of a new database. Inherit CLI method to add GUI part """ - self._post_load_newdb_nongui(filename, title) + if self.dbstate.db.is_open(): + self._post_load_newdb_nongui(filename, title) self._post_load_newdb_gui(filename, filetype, title) def _post_load_newdb_gui(self, filename, filetype, title=None): @@ -1065,51 +1073,6 @@ class ViewManager(CLIManager): config.set('paths.recent-file', '') config.save() - def enable_menu(self, enable): - """ Enable/disable the menues. Used by the dbloader for import to - prevent other operations during import. Needed because simpler methods - don't work under Gnome with application menus at top of screen (instead - of Gramps window). - Note: enable must be set to False on first call. - """ - if not enable: - self.action_st = ( - self.uimanager.get_actions_sensitive(self.actiongroup), - self.uimanager.get_actions_sensitive(self.readonlygroup), - self.uimanager.get_actions_sensitive(self.undoactions), - self.uimanager.get_actions_sensitive(self.redoactions), - self.uimanager.get_actions_sensitive(self.fileactions), - self.uimanager.get_actions_sensitive(self.toolactions), - self.uimanager.get_actions_sensitive(self.reportactions), - self.uimanager.get_actions_sensitive( - self.recent_manager.action_group)) - self.uimanager.set_actions_sensitive(self.actiongroup, enable) - self.uimanager.set_actions_sensitive(self.readonlygroup, enable) - self.uimanager.set_actions_sensitive(self.undoactions, enable) - self.uimanager.set_actions_sensitive(self.redoactions, enable) - self.uimanager.set_actions_sensitive(self.fileactions, enable) - self.uimanager.set_actions_sensitive(self.toolactions, enable) - self.uimanager.set_actions_sensitive(self.reportactions, enable) - self.uimanager.set_actions_sensitive( - self.recent_manager.action_group, enable) - else: - self.uimanager.set_actions_sensitive( - self.actiongroup, self.action_st[0]) - self.uimanager.set_actions_sensitive( - self.readonlygroup, self.action_st[1]) - self.uimanager.set_actions_sensitive( - self.undoactions, self.action_st[2]) - self.uimanager.set_actions_sensitive( - self.redoactions, self.action_st[3]) - self.uimanager.set_actions_sensitive( - self.fileactions, self.action_st[4]) - self.uimanager.set_actions_sensitive( - self.toolactions, self.action_st[5]) - self.uimanager.set_actions_sensitive( - self.reportactions, self.action_st[6]) - self.uimanager.set_actions_sensitive( - self.recent_manager.action_group, self.action_st[7]) - def __change_undo_label(self, label, update_menu=True): """ Change the UNDO label diff --git a/gramps/gui/views/pageview.py b/gramps/gui/views/pageview.py index 7961ec56f..d5700a301 100644 --- a/gramps/gui/views/pageview.py +++ b/gramps/gui/views/pageview.py @@ -138,6 +138,7 @@ class PageView(DbGUIElement, metaclass=ABCMeta): self.sidebar = None self.bottombar = None self.widget = None + self.vpane = None DbGUIElement.__init__(self, dbstate.db) @@ -154,18 +155,20 @@ class PageView(DbGUIElement, metaclass=ABCMeta): self.ident + "_bottombar", defaults[1]) hpane = Gtk.Paned() - vpane = Gtk.Paned(orientation=Gtk.Orientation.VERTICAL) - hpane.pack1(vpane, resize=True, shrink=False) + self.vpane = Gtk.Paned(orientation=Gtk.Orientation.VERTICAL) + hpane.pack1(self.vpane, resize=True, shrink=False) hpane.pack2(self.sidebar, resize=False, shrink=False) hpane.show() - vpane.show() + self.vpane.show() self.widget = self.build_widget() self.widget.show_all() self.widget.set_name('view') - vpane.pack1(self.widget, resize=True, shrink=False) - vpane.pack2(self.bottombar, resize=False, shrink=True) - self._setup_slider_config(vpane, 'vpane.slider-position') + self.vpane.pack1(self.widget, resize=True, shrink=False) + self.vpane.pack2(self.bottombar, resize=False, shrink=False) + self.vpane.show() + self._config.register('vpane.slider-position', -1) + self.vpane.set_position(self._config.get('vpane.slider-position')) self.sidebar_toggled(self.sidebar.get_property('visible')) self.hpane_sig = hpane.connect("draw", self.set_page_slider) @@ -343,6 +346,11 @@ class PageView(DbGUIElement, metaclass=ABCMeta): self.bottombar.set_inactive() self.active = False + def post_create(self): + if self.vpane: + self._setup_slider_config(self.vpane, 'vpane.slider-position') + self.vpane = None + @abstractmethod def build_tree(self): """ diff --git a/gramps/gui/widgets/grampletbar.py b/gramps/gui/widgets/grampletbar.py index 3cb19733f..29c727aab 100644 --- a/gramps/gui/widgets/grampletbar.py +++ b/gramps/gui/widgets/grampletbar.py @@ -373,10 +373,6 @@ class GrampletBar(Gtk.Notebook): """ Add a tab to the notebook for the given gramplet. """ - width = -1 # Allow tab width to adjust (smaller) to sidebar - height = min(int(self.uistate.screen_height() * 0.20), 400) - gramplet.set_size_request(width, height) - label = self.__create_tab_label(gramplet) page_num = self.append_page(gramplet, label) return page_num diff --git a/gramps/gui/widgets/grampletpane.py b/gramps/gui/widgets/grampletpane.py index 71a317077..4434826e9 100644 --- a/gramps/gui/widgets/grampletpane.py +++ b/gramps/gui/widgets/grampletpane.py @@ -1240,6 +1240,12 @@ class GrampletPane(Gtk.ScrolledWindow): else: cnt = 0 for item in base_opts["data"]: + # If we have a "%" in a string, + # escape it by writing "%%" + # to avoid InterpolationSyntaxError + # in python configparser module. + if isinstance(item, str): + item = item.replace("%", "%%") fp.write("data[%d]=%s\n" % (cnt, item)) cnt += 1 else: diff --git a/gramps/plugins/db/dbapi/dbapi.py b/gramps/plugins/db/dbapi/dbapi.py index e732cdd3f..7b432f16a 100644 --- a/gramps/plugins/db/dbapi/dbapi.py +++ b/gramps/plugins/db/dbapi/dbapi.py @@ -225,6 +225,16 @@ class DBAPI(DbGeneric): if self.transaction == None: self.dbapi.rollback() + def _collation(self, locale): + """ + Get the adjusted collation if there is one, falling back on + the locale.collation. + """ + collation = self.dbapi.check_collation(locale) + if collation == None: + return locale.get_collation() + return collation + def transaction_begin(self, transaction): """ Transactions are handled automatically by the db layer. @@ -365,12 +375,9 @@ class DBAPI(DbGeneric): :type locale: A GrampsLocale object. """ if sort_handles: - if locale != glocale: - self.dbapi.check_collation(locale) - self.dbapi.execute('SELECT handle FROM person ' 'ORDER BY surname ' - 'COLLATE "%s"' % locale.get_collation()) + 'COLLATE "%s"' % self._collation(locale)) else: self.dbapi.execute("SELECT handle FROM person") rows = self.dbapi.fetchall() @@ -387,9 +394,6 @@ class DBAPI(DbGeneric): :type locale: A GrampsLocale object. """ if sort_handles: - if locale != glocale: - self.dbapi.check_collation(locale) - sql = ('SELECT family.handle ' + 'FROM family ' + 'LEFT JOIN person AS father ' + @@ -404,7 +408,7 @@ class DBAPI(DbGeneric): 'THEN mother.given_name ' + 'ELSE father.given_name ' + 'END) ' + - 'COLLATE "%s"' % locale.get_collation()) + 'COLLATE "%s"' % self._collation(locale)) self.dbapi.execute(sql) else: self.dbapi.execute("SELECT handle FROM family") @@ -431,12 +435,9 @@ class DBAPI(DbGeneric): :type locale: A GrampsLocale object. """ if sort_handles: - if locale != glocale: - self.dbapi.check_collation(locale) - self.dbapi.execute('SELECT handle FROM citation ' 'ORDER BY page ' - 'COLLATE "%s"' % locale.get_collation()) + 'COLLATE "%s"' % self._collation(locale)) else: self.dbapi.execute("SELECT handle FROM citation") rows = self.dbapi.fetchall() @@ -453,12 +454,9 @@ class DBAPI(DbGeneric): :type locale: A GrampsLocale object. """ if sort_handles: - if locale != glocale: - self.dbapi.check_collation(locale) - self.dbapi.execute('SELECT handle FROM source ' 'ORDER BY title ' - 'COLLATE "%s"' % locale.get_collation()) + 'COLLATE "%s"' % self._collation(locale)) else: self.dbapi.execute("SELECT handle from source") rows = self.dbapi.fetchall() @@ -475,12 +473,9 @@ class DBAPI(DbGeneric): :type locale: A GrampsLocale object. """ if sort_handles: - if locale != glocale: - self.dbapi.check_collation(locale) - self.dbapi.execute('SELECT handle FROM place ' 'ORDER BY title ' - 'COLLATE "%s"' % locale.get_collation()) + 'COLLATE "%s"' % self._collation(locale)) else: self.dbapi.execute("SELECT handle FROM place") rows = self.dbapi.fetchall() @@ -506,12 +501,9 @@ class DBAPI(DbGeneric): :type locale: A GrampsLocale object. """ if sort_handles: - if locale != glocale: - self.dbapi.check_collation(locale) - self.dbapi.execute('SELECT handle FROM media ' 'ORDER BY desc ' - 'COLLATE "%s"' % locale.get_collation()) + 'COLLATE "%s"' % self._collation(locale)) else: self.dbapi.execute("SELECT handle FROM media") rows = self.dbapi.fetchall() @@ -537,12 +529,9 @@ class DBAPI(DbGeneric): :type locale: A GrampsLocale object. """ if sort_handles: - if locale != glocale: - self.dbapi.check_collation(locale) - self.dbapi.execute('SELECT handle FROM tag ' 'ORDER BY name ' - 'COLLATE "%s"' % locale.get_collation()) + 'COLLATE "%s"' % self._collation(locale)) else: self.dbapi.execute("SELECT handle FROM tag") rows = self.dbapi.fetchall() @@ -589,12 +578,13 @@ class DBAPI(DbGeneric): "WHERE name = ?", [grouping, name]) elif row and grouping is None: self.dbapi.execute("DELETE FROM name_group WHERE name = ?", [name]) - grouping = '' else: self.dbapi.execute( "INSERT INTO name_group (name, grouping) VALUES (?, ?)", [name, grouping]) self._txn_commit() + if grouping is None: + grouping = '' self.emit('person-groupname-rebuild', (name, grouping)) def _commit_base(self, obj, obj_key, trans, change_time): diff --git a/gramps/plugins/db/dbapi/sqlite.py b/gramps/plugins/db/dbapi/sqlite.py index f27fe8e32..1d22a5719 100644 --- a/gramps/plugins/db/dbapi/sqlite.py +++ b/gramps/plugins/db/dbapi/sqlite.py @@ -117,6 +117,8 @@ class Connection: collation = locale.get_collation().translate(self.__tmap) if collation not in self.__collations: self.__connection.create_collation(collation, locale.strcoll) + self.__collations.append(collation) + return collation def execute(self, *args, **kwargs): """ diff --git a/gramps/plugins/docgen/svgdrawdoc.py b/gramps/plugins/docgen/svgdrawdoc.py index 377573ad1..e60c2c29f 100644 --- a/gramps/plugins/docgen/svgdrawdoc.py +++ b/gramps/plugins/docgen/svgdrawdoc.py @@ -31,7 +31,7 @@ SVG document generator. # #------------------------------------------------------------------------- from io import StringIO - +from xml.sax.saxutils import escape #------------------------------------------------------------------------- # # Gramps modules @@ -147,7 +147,7 @@ class SvgDrawDoc(BaseDoc, DrawDoc): linex = xpos + (width - self.string_width(font, line)) / 2 self.buffer.write( '' % (linex, size) + - line + + escape(line) + '' ) self.buffer.write('\n') @@ -273,7 +273,7 @@ class SvgDrawDoc(BaseDoc, DrawDoc): self.buffer.write(' font-family:serif;') self.buffer.write( '">' + - line + + escape(line) + '\n' ) @@ -307,7 +307,7 @@ class SvgDrawDoc(BaseDoc, DrawDoc): self.buffer.write('font-family:serif;') self.buffer.write( '">' + - text + + escape(text) + '\n' ) diff --git a/gramps/plugins/drawreport/calendarreport.py b/gramps/plugins/drawreport/calendarreport.py index 7cda0cb9b..e325e1545 100644 --- a/gramps/plugins/drawreport/calendarreport.py +++ b/gramps/plugins/drawreport/calendarreport.py @@ -267,7 +267,8 @@ class Calendar(Report): day_col * cell_width + cell_width/2, header + week_row * cell_height) list_ = self.calendar.get(month, {}).get(thisday.day, []) - list_.sort() # to get CAL-Holiday on bottom + # sort the list to get CAL-Holiday on bottom + list_.sort(key=lambda x: (x[0], x[1])) position = spacing for (format, p, m_list) in list_: for line in reversed(p.split("\n")): diff --git a/gramps/plugins/export/exportvcalendar.py b/gramps/plugins/export/exportvcalendar.py index 4c2d031f4..6dd577f2f 100644 --- a/gramps/plugins/export/exportvcalendar.py +++ b/gramps/plugins/export/exportvcalendar.py @@ -202,7 +202,7 @@ class CalendarWriter: date = event.get_date_object() place_handle = event.get_place_handle() date_string = self.format_date(date, 1) - if date_string is not "": + if date_string != "": # self.writeln("") self.writeln("BEGIN:VEVENT") time_s = time.gmtime(event.change) diff --git a/gramps/plugins/gramplet/backlinks.py b/gramps/plugins/gramplet/backlinks.py index 5e3861b05..a2832febe 100644 --- a/gramps/plugins/gramplet/backlinks.py +++ b/gramps/plugins/gramplet/backlinks.py @@ -125,13 +125,17 @@ class Backlinks(Gramplet): edit_object(self.dbstate, self.uistate, objclass, handle) + def db_changed(self): + for item in ['person', 'family', 'source', 'citation', 'event', + 'media', 'place', 'repository', 'note']: + self.connect(self.dbstate.db, '%s-delete' % item, self.update) + self.connect(self.dbstate.db, '%s-add' % item, self.update) + self.connect(self.dbstate.db, '%s-update' % item, self.update) + class PersonBacklinks(Backlinks): """ Displays the back references for a person. """ - def db_changed(self): - self.connect(self.dbstate.db, 'person-update', self.update) - def active_changed(self, handle): self.update() @@ -152,7 +156,7 @@ class EventBacklinks(Backlinks): Displays the back references for an event. """ def db_changed(self): - self.connect(self.dbstate.db, 'event-update', self.update) + super().db_changed() self.connect_signal('Event', self.update) def update_has_data(self): @@ -172,7 +176,7 @@ class FamilyBacklinks(Backlinks): Displays the back references for a family. """ def db_changed(self): - self.connect(self.dbstate.db, 'family-update', self.update) + super().db_changed() self.connect_signal('Family', self.update) def update_has_data(self): @@ -192,7 +196,7 @@ class PlaceBacklinks(Backlinks): Displays the back references for a place. """ def db_changed(self): - self.connect(self.dbstate.db, 'place-update', self.update) + super().db_changed() self.connect_signal('Place', self.update) def update_has_data(self): @@ -212,7 +216,7 @@ class SourceBacklinks(Backlinks): Displays the back references for a source,. """ def db_changed(self): - self.connect(self.dbstate.db, 'source-update', self.update) + super().db_changed() self.connect_signal('Source', self.update) def update_has_data(self): @@ -232,7 +236,7 @@ class CitationBacklinks(Backlinks): Displays the back references for a Citation,. """ def db_changed(self): - self.connect(self.dbstate.db, 'citation-update', self.update) + super().db_changed() self.connect_signal('Citation', self.update) def update_has_data(self): @@ -252,7 +256,7 @@ class RepositoryBacklinks(Backlinks): Displays the back references for a repository. """ def db_changed(self): - self.connect(self.dbstate.db, 'repository-update', self.update) + super().db_changed() self.connect_signal('Repository', self.update) def update_has_data(self): @@ -272,7 +276,7 @@ class MediaBacklinks(Backlinks): Displays the back references for a media object. """ def db_changed(self): - self.connect(self.dbstate.db, 'media-update', self.update) + super().db_changed() self.connect_signal('Media', self.update) def update_has_data(self): @@ -292,7 +296,7 @@ class NoteBacklinks(Backlinks): Displays the back references for a note. """ def db_changed(self): - self.connect(self.dbstate.db, 'note-update', self.update) + super().db_changed() self.connect_signal('Note', self.update) def update_has_data(self): diff --git a/gramps/plugins/gramplet/locations.py b/gramps/plugins/gramplet/locations.py index d60960959..808f317f6 100644 --- a/gramps/plugins/gramplet/locations.py +++ b/gramps/plugins/gramplet/locations.py @@ -300,6 +300,8 @@ class DateRange: """ start = None stop = None + if date.is_empty(): + return (None, None) if date.modifier == Date.MOD_NONE: start = date.sortval stop = date.sortval diff --git a/gramps/plugins/lib/libplaceview.py b/gramps/plugins/lib/libplaceview.py index 7f2325a8c..9401d35bf 100644 --- a/gramps/plugins/lib/libplaceview.py +++ b/gramps/plugins/lib/libplaceview.py @@ -206,8 +206,10 @@ class PlaceBaseView(ListView): """ if action: action.set_state(value) - self.mapservice = mapkey = value.get_string() - config.set('interface.mapservice', mapkey) + self.mapservice = value.get_string() + else: + self.mapservice = value + config.set('interface.mapservice', self.mapservice) config.save() _ui = self.__create_maps_menu_actions() self.uimanager.add_ui_from_string(_ui) diff --git a/gramps/plugins/lib/maps/geography.py b/gramps/plugins/lib/maps/geography.py index ea732cd24..80c762746 100644 --- a/gramps/plugins/lib/maps/geography.py +++ b/gramps/plugins/lib/maps/geography.py @@ -292,9 +292,6 @@ class GeoGraphyView(OsmGps, NavigationView): if self.active: self.bookmarks.redraw() self.build_tree() - if self.osm: - self.osm.grab_focus() - self.set_crosshair(config.get("geography.show_cross")) def can_configure(self): """ diff --git a/gramps/plugins/lib/maps/osmgps.py b/gramps/plugins/lib/maps/osmgps.py index 0c3d97a3c..4d64e6e91 100644 --- a/gramps/plugins/lib/maps/osmgps.py +++ b/gramps/plugins/lib/maps/osmgps.py @@ -191,14 +191,11 @@ class OsmGps: self.osm = DummyMapNoGpsPoint() else: if http_proxy: - self.osm = osmgpsmap.Map(tile_cache=tiles_path, - proxy_uri=http_proxy, - map_source=constants.MAP_TYPE[ - map_type]) + self.osm = osmgpsmap.Map(proxy_uri=http_proxy) else: - self.osm = osmgpsmap.Map(tile_cache=tiles_path, - map_source=constants.MAP_TYPE[ - map_type]) + self.osm = osmgpsmap.Map() + self.osm.set_property("tile_cache", tiles_path) + self.osm.set_property("map_source", constants.MAP_TYPE[map_type]) self.osm.props.tile_cache = osmgpsmap.MAP_CACHE_AUTO current_map = osmgpsmap.MapOsd(show_dpad=False, show_zoom=True) self.end_selection = None diff --git a/gramps/plugins/rel/rel_nl.py b/gramps/plugins/rel/rel_nl.py index 44561a0ff..c3a82ac54 100644 --- a/gramps/plugins/rel/rel_nl.py +++ b/gramps/plugins/rel/rel_nl.py @@ -5,6 +5,7 @@ # Copyright (C) 2003-2005 Donald N. Allingham # Copyright (C) 2008 Brian G. Matherly # Copyright (C) 2018 Robin van der Vliet +# Copyright (C) 2020 Jan Sparreboom # # 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 @@ -35,7 +36,7 @@ import gramps.gen.relationship #------------------------------------------------------------------------- # -# +# Levels # #------------------------------------------------------------------------- @@ -69,9 +70,207 @@ _child_level = [ "", "", _nibling_level = [ "", "", "achter", "achterachter", "achterachterachter" ] +_parents_level = ["", + "ouders", + "grootouders", + "overgrootouders", + "betovergrootouders", + "overgrootouders 5e graad", + "overgrootouders 6e graad", + "overgrootouders 7e graad", + "overgrootouders 8e graad", + "overgrootouders 9e graad", + "overgrootouders 10e graad", + "overgrootouders 11e graad", + "overgrootouders 12e graad", + "overgrootouders 13e graad", + "overgrootouders 14e graad", + "overgrootouders 15e graad", + "overgrootouders 16e graad", + "overgrootouders 17e graad", + "overgrootouders 18e graad", + "overgrootouders 19e graad", + "overgrootouders 20e graad", + "overgrootouders 21e graad", + "overgrootouders 22e graad", + "overgrootouders 23e graad", + "overgrootouders 24e graad", + "overgrootouders 25e graad", + "overgrootouders 26e graad", + "overgrootouders 27e graad", + "overgrootouders 28e graad", + "overgrootouders 29e graad", + "overgrootouders 30e graad", + "overgrootouders 31e graad", + "overgrootouders 32e graad", + "overgrootouders 33e graad", + "overgrootouders 34e graad", + "overgrootouders 35e graad", + "overgrootouders 36e graad", + "overgrootouders 37e graad", + "overgrootouders 38e graad", + "overgrootouders 39e graad", + "overgrootouders 40e graad", + "overgrootouders 41e graad", + "overgrootouders 42e graad", + "overgrootouders 43e graad", + "overgrootouders 44e graad", + "overgrootouders 45e graad", + "overgrootouders 46e graad", + "overgrootouders 47e graad", + "overgrootouders 48e graad", + "overgrootouders 49e graad", + "overgrootouders 50e graad", ] + +_siblings_level = ["", + "broers en zussen", + "ooms en tantes", + "oudooms en -tantes", + "overoudooms en -tantes", + "overoudooms en -tantes 5e graad", + "overoudooms en -tantes 6e graad", + "overoudooms en -tantes 7e graad", + "overoudooms en -tantes 8e graad", + "overoudooms en -tantes 9e graad", + "overoudooms en -tantes 10e graad", + "overoudooms en -tantes 11e graad", + "overoudooms en -tantes 12e graad", + "overoudooms en -tantes 13e graad", + "overoudooms en -tantes 14e graad", + "overoudooms en -tantes 15e graad", + "overoudooms en -tantes 16e graad", + "overoudooms en -tantes 17e graad", + "overoudooms en -tantes 18e graad", + "overoudooms en -tantes 19e graad", + "overoudooms en -tantes 20e graad", + "overoudooms en -tantes 21e graad", + "overoudooms en -tantes 22e graad", + "overoudooms en -tantes 23e graad", + "overoudooms en -tantes 24e graad", + "overoudooms en -tantes 25e graad", + "overoudooms en -tantes 26e graad", + "overoudooms en -tantes 27e graad", + "overoudooms en -tantes 28e graad", + "overoudooms en -tantes 29e graad", + "overoudooms en -tantes 30e graad", + "overoudooms en -tantes 41e graad", + "overoudooms en -tantes 42e graad", + "overoudooms en -tantes 43e graad", + "overoudooms en -tantes 44e graad", + "overoudooms en -tantes 45e graad", + "overoudooms en -tantes 46e graad", + "overoudooms en -tantes 47e graad", + "overoudooms en -tantes 48e graad", + "overoudooms en -tantes 49e graad", + "overoudooms en -tantes 50e graad", ] + +_children_level = ["", + "kinderen", + "kleinkinderen", + "achterkleinkinderen", + "betachterkleinkinderen", + "kleinkinderen 5e graad", + "kleinkinderen 6e graad", + "kleinkinderen 7e graad", + "kleinkinderen 8e graad", + "kleinkinderen 9e graad", + "kleinkinderen 10e graad", + "kleinkinderen 11e graad", + "kleinkinderen 12e graad", + "kleinkinderen 13e graad", + "kleinkinderen 14e graad", + "kleinkinderen 15e graad", + "kleinkinderen 16e graad", + "kleinkinderen 17e graad", + "kleinkinderen 18e graad", + "kleinkinderen 19e graad", + "kleinkinderen 20e graad", + "kleinkinderen 21e graad", + "kleinkinderen 22e graad", + "kleinkinderen 23e graad", + "kleinkinderen 24e graad", + "kleinkinderen 25e graad", + "kleinkinderen 26e graad", + "kleinkinderen 27e graad", + "kleinkinderen 28e graad", + "kleinkinderen 29e graad", + "kleinkinderen 30e graad", + "kleinkinderen 31e graad", + "kleinkinderen 32e graad", + "kleinkinderen 33e graad", + "kleinkinderen 34e graad", + "kleinkinderen 35e graad", + "kleinkinderen 36e graad", + "kleinkinderen 37e graad", + "kleinkinderen 38e graad", + "kleinkinderen 39e graad", + "kleinkinderen 40e graad", + "kleinkinderen 41e graad", + "kleinkinderen 42e graad", + "kleinkinderen 43e graad", + "kleinkinderen 44e graad", + "kleinkinderen 45e graad", + "kleinkinderen 46e graad", + "kleinkinderen 47e graad", + "kleinkinderen 48e graad", + "kleinkinderen 49e graad", + "kleinkinderen 50e graad", ] + +_nephews_nieces_level = ["", + "broers en zussen", + "neven en nichten", + "achterneven en -nichten", + "achterneven en -nichten 4e graad", + "achterneven en -nichten 5e graad", + "achterneven en -nichten 6e graad", + "achterneven en -nichten 7e graad", + "achterneven en -nichten 8e graad", + "achterneven en -nichten 9e graad", + "achterneven en -nichten 10e graad", + "achterneven en -nichten 11e graad", + "achterneven en -nichten 12e graad", + "achterneven en -nichten 13e graad", + "achterneven en -nichten 14e graad", + "achterneven en -nichten 15e graad", + "achterneven en -nichten 16e graad", + "achterneven en -nichten 17e graad", + "achterneven en -nichten 18e graad", + "achterneven en -nichten 19e graad", + "achterneven en -nichten 20e graad", + "achterneven en -nichten 21e graad", + "achterneven en -nichten 22e graad", + "achterneven en -nichten 23e graad", + "achterneven en -nichten 24e graad", + "achterneven en -nichten 25e graad", + "achterneven en -nichten 26e graad", + "achterneven en -nichten 27e graad", + "achterneven en -nichten 28e graad", + "achterneven en -nichten 29e graad", + "achterneven en -nichten 30e graad", + "achterneven en -nichten 31e graad", + "achterneven en -nichten 32e graad", + "achterneven en -nichten 33e graad", + "achterneven en -nichten 34e graad", + "achterneven en -nichten 35e graad", + "achterneven en -nichten 36e graad", + "achterneven en -nichten 37e graad", + "achterneven en -nichten 38e graad", + "achterneven en -nichten 39e graad", + "achterneven en -nichten 40e graad", + "achterneven en -nichten 41e graad", + "achterneven en -nichten 42e graad", + "achterneven en -nichten 43e graad", + "achterneven en -nichten 44e graad", + "achterneven en -nichten 45e graad", + "achterneven en -nichten 46e graad", + "achterneven en -nichten 47e graad", + "achterneven en -nichten 48e graad", + "achterneven en -nichten 49e graad", + "achterneven en -nichten 50e graad", ] + #------------------------------------------------------------------------- # -# +# Relationship calculator Dutch version # #------------------------------------------------------------------------- @@ -273,6 +472,104 @@ class RelationshipCalculator(gramps.gen.relationship.RelationshipCalculator): else: return "%s%snicht (kozijn)" % (inlaw, step) \ + " " + _ordinal_level[removed] + " graad" +# NIEUW + def get_plural_relationship_string(self, Ga, Gb, + reltocommon_a='', reltocommon_b='', + only_birth=True, + in_law_a=False, in_law_b=False): + """ + Provide a string that describes the relationsip between a person, and + a group of people with the same relationship. E.g. "grandparents" or + "children". + + Ga and Gb can be used to mathematically calculate the relationship. + + .. seealso:: + http://en.wikipedia.org/wiki/Cousin#Mathematical_definitions + + :param Ga: The number of generations between the main person and the + common ancestor. + :type Ga: int + :param Gb: The number of generations between the group of people and the + common ancestor + :type Gb: int + :param reltocommon_a: relation path to common ancestor or common + Family for person a. + Note that length = Ga + :type reltocommon_a: str + :param reltocommon_b: relation path to common ancestor or common + Family for person b. + Note that length = Gb + :type reltocommon_b: str + :param only_birth: True if relation between a and b is by birth only + False otherwise + :type only_birth: bool + :param in_law_a: True if path to common ancestors is via the partner + of person a + :type in_law_a: bool + :param in_law_b: True if path to common ancestors is via the partner + of person b + :type in_law_b: bool + :returns: A string describing the relationship between the person and + the group. + :rtype: str + """ + rel_str = "verre familie" + if Ga == 0: + # These are descendants + if Gb < len(_children_level): + rel_str = _children_level[Gb] + else: + rel_str = "verre afstammelingen" + elif Gb == 0: + # These are parents/grand parents + if Ga < len(_parents_level): + rel_str = _parents_level[Ga] + else: + rel_str = "verre voorouders" + elif Gb == 1: + # These are siblings/aunts/uncles + if Ga < len(_siblings_level): + rel_str = _siblings_level[Ga] + else: + rel_str = "verre ooms/tantes" + elif Ga == 1: + # These are nieces/nephews + if Gb < len(_nephews_nieces_level): + rel_str = _nephews_nieces_level[Gb] + else: + rel_str = "verre neven/nichten" + elif Ga > 1 and Ga == Gb: + # These are cousins in the same generation + if Ga <= len(_ordinal_level): + rel_str = "%s neven" % _ordinal_level[Ga-1] + else: + rel_str = "verre neven" + elif Ga > 1 and Ga > Gb: + # These are cousins in different generations with the second person + # being in a higher generation from the common ancestor than the + # first person. + if Gb <= len(_LEVEL_NAME) and (Ga-Gb) < len(_removed_level): + rel_str = "%s neven%s (omhoog)" % (_ordinal_level[Gb-1], + _removed_level[Ga-Gb]) + else: + rel_str = "verre neven" + elif Gb > 1 and Gb > Ga: + # These are cousins in different generations with the second person + # being in a lower generation from the common ancestor than the + # first person. + if Ga <= len(_LEVEL_NAME) and (Gb-Ga) < len(_removed_level): + rel_str = "%s neven%s (omlaag)" % (_ordinal_level[Ga-1], + _removed_level[Gb-Ga]) + else: + rel_str = "verre neven" + + if in_law_b is True: + rel_str = "echtgenoten van %s" % rel_str + + return rel_str + + def get_single_relationship_string(self, Ga, Gb, gender_a, gender_b, reltocommon_a, reltocommon_b, @@ -424,3 +721,6 @@ if __name__ == "__main__": from gramps.gen.relationship import test RC = RelationshipCalculator() test(RC, True) + + + diff --git a/gramps/plugins/test/db_undo_and_signals_test.py b/gramps/plugins/test/db_undo_and_signals_test.py index bddd9461b..41234cda9 100644 --- a/gramps/plugins/test/db_undo_and_signals_test.py +++ b/gramps/plugins/test/db_undo_and_signals_test.py @@ -132,7 +132,7 @@ class DbTestClassBase(object): self._log_sig("note-delete", args) def _log_sig(self, sig, args): - print("('%s', %s)," % (sig, args)) + # print("('%s', %s)," % (sig, args)) self.sigs.append((sig, args[0])) def _cm_pers_add(self, *args): diff --git a/gramps/plugins/textreport/birthdayreport.py b/gramps/plugins/textreport/birthdayreport.py index 1b5040417..8e9c85b17 100644 --- a/gramps/plugins/textreport/birthdayreport.py +++ b/gramps/plugins/textreport/birthdayreport.py @@ -271,6 +271,7 @@ class BirthdayReport(Report): for person_handle in people: step() person = self.database.get_person_from_handle(person_handle) + short_name = self.get_name(person) birth_ref = person.get_birth_ref() birth_date = None if birth_ref: diff --git a/gramps/plugins/textreport/tagreport.py b/gramps/plugins/textreport/tagreport.py index ef8806a23..3542f4294 100644 --- a/gramps/plugins/textreport/tagreport.py +++ b/gramps/plugins/textreport/tagreport.py @@ -441,7 +441,8 @@ class TagReport(Report): for place_handle in place_list: place = self.database.get_place_from_handle(place_handle) - place_title = _pd.display(self.database, place, self.place_format) + place_title = _pd.display(self.database, place, None, + self.place_format) self.doc.start_row() diff --git a/gramps/plugins/view/geoclose.py b/gramps/plugins/view/geoclose.py index 62443d533..a731525dc 100644 --- a/gramps/plugins/view/geoclose.py +++ b/gramps/plugins/view/geoclose.py @@ -247,6 +247,14 @@ class GeoClose(GeoGraphyView): self.add_item = None self.newmenu = None self.config_meeting_slider = None + self.dbstate.connect('database-changed', self.reset_change_db) + + def reset_change_db(self, dummy_dbase): + """ + Used to reset the family reference + """ + self.refperson = None + def get_title(self): """ diff --git a/gramps/plugins/view/geofamclose.py b/gramps/plugins/view/geofamclose.py index 317571930..912958fc4 100644 --- a/gramps/plugins/view/geofamclose.py +++ b/gramps/plugins/view/geofamclose.py @@ -244,6 +244,13 @@ class GeoFamClose(GeoGraphyView): self.cal = config.get('preferences.calendar-format-report') self.no_show_places_in_status_bar = False self.config_meeting_slider = None + self.dbstate.connect('database-changed', self.reset_change_db) + + def reset_change_db(self, dummy_dbase): + """ + Used to reset the family reference + """ + self.reffamily = None def get_title(self): """ diff --git a/gramps/plugins/view/geofamily.py b/gramps/plugins/view/geofamily.py index 5402e3c2d..942c20051 100644 --- a/gramps/plugins/view/geofamily.py +++ b/gramps/plugins/view/geofamily.py @@ -370,8 +370,9 @@ class GeoFamily(GeoGraphyView): _("Family places for %s") % self.family_label(family)) person = None if family: - person = dbstate.db.get_person_from_handle( - family.get_father_handle()) + handle = family.get_father_handle() + if handle: + person = dbstate.db.get_person_from_handle(handle) else: return family_id = family.gramps_id diff --git a/images/hicolor/128x128/apps/gramps.png b/images/hicolor/128x128/apps/gramps.png new file mode 100644 index 000000000..58ba57009 Binary files /dev/null and b/images/hicolor/128x128/apps/gramps.png differ diff --git a/images/hicolor/256x256/apps/gramps.png b/images/hicolor/256x256/apps/gramps.png new file mode 100644 index 000000000..1e5fb1c07 Binary files /dev/null and b/images/hicolor/256x256/apps/gramps.png differ diff --git a/mac/Info.plist b/mac/Info.plist index e118f32aa..0148e6ed2 100755 --- a/mac/Info.plist +++ b/mac/Info.plist @@ -7,7 +7,7 @@ CFBundleExecutable Gramps CFBundleGetInfoString - Gramps-5.1.3-1, (C) 1997-2020 The Gramps Team http://www.gramps-project.org + Gramps-5.1.5-1, (C) 1997-2022 The Gramps Team http://www.gramps-project.org CFBundleIconFile gramps.icns CFBundleIdentifier @@ -17,15 +17,15 @@ CFBundlePackageType APPL CFBundleShortVersionString - Gramps-5.1.3-1 + Gramps-5.1.5-1 CFBundleSignature ???? CFBundleVersion - Gramps-5.1.3-1 + Gramps-5.1.5-1 NSHumanReadableCopyright - Copyright 1997 - 2020 The Gramps Team, GNU General Public License. + Copyright 1997 - 2022 The Gramps Team, GNU General Public License. LSMinimumSystemVersion - 10.9 + 10.12 GtkOSXLaunchScriptFile gramps_launcher.py NSHighResolutionCapable diff --git a/mac/gramps.bundle b/mac/gramps.bundle index 6de0f21de..3d757c914 100644 --- a/mac/gramps.bundle +++ b/mac/gramps.bundle @@ -12,8 +12,9 @@ ${project}/Info.plist + ${project}/release.entitlements.plist - - ${prefix}/lib/libgtkmacintegration-gtk3.2.dylib + ${prefix}/lib/libgtkmacintegration-gtk3.4.dylib @@ -65,7 +62,7 @@ - ${prefix}/lib/enchant + ${prefix}/lib/enchant-2 @@ -77,7 +74,7 @@ - ${prefix}/lib/libhunspell-1.6.0.dylib + ${prefix}/lib/libhunspell-1.7.0.dylib @@ -96,6 +93,14 @@ ${prefix}/lib/libgexiv2.dylib + + ${prefix}/lib/libgeocode-glib.dylib + + + + ${prefix}/lib/gio/modules/libgiognutls.so + + ${prefix}/share/gir-1.0/*.gir @@ -137,6 +142,10 @@ ${project}/gramps_launcher.py + + ${prefix}/etc/fonts + + ${prefix}/share/glib-2.0/schemas @@ -144,19 +153,19 @@ - ${prefix}/lib/python3.6/*.py + ${prefix}/lib/python3.9/*.py - ${prefix}/lib/python3.6/config-3.6m-darwin/ + ${prefix}/lib/python3.9/config-3.9-darwin/ - ${prefix}/lib/python3.6/site-packages/gramps/gen/utils/resource-path + ${prefix}/lib/python3.9/site-packages/gramps/gen/utils/resource-path - ${prefix}/include/python3.6m/pyconfig.h + ${prefix}/include/python3.9/pyconfig.h @@ -173,7 +182,7 @@ - ${prefix}/lib/python3.6/site-packages/gramps/*.glade + ${prefix}/lib/python3.9/site-packages/gramps/*.glade @@ -205,6 +214,7 @@ ${prefix}/share/icons + - - + + - + @@ -81,35 +71,10 @@ gtk-mac-bundler gtk-osx-build/projects/gramps/gramps.bundle - - - - - - - - - - - - - - - - - - - - - - - + + @@ -128,46 +93,34 @@ gtk-mac-bundler gtk-osx-build/projects/gramps/gramps.bundle - + - - - - - - - - - - - - - + - + - + + - - - + + hash="sha256:a787ab10d7bb5494e5f76536ac460741788f1fbce851068d73a87ca7c35fc3e1"> @@ -175,18 +128,74 @@ gtk-mac-bundler gtk-osx-build/projects/gramps/gramps.bundle - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - @@ -202,7 +211,7 @@ gtk-mac-bundler gtk-osx-build/projects/gramps/gramps.bundle - + diff --git a/mac/gramps_launcher.py b/mac/gramps_launcher.py index 70c317aa4..e9e79eb88 100644 --- a/mac/gramps_launcher.py +++ b/mac/gramps_launcher.py @@ -24,6 +24,7 @@ environ['GTK_PATH'] = bundle_res environ['PANGO_RC_FILE'] = join(bundle_etc, 'pango', 'pangorc') environ['PANGO_SYSCONFDIR'] = bundle_etc environ['PANGO_LIBDIR'] = bundle_lib +environ['GIO_MODULE_DIR'] = join(bundle_lib, 'gio', 'modules') environ['GDK_PIXBUF_MODULE_FILE'] = join(bundle_lib, 'gdk-pixbuf-2.0', '2.10.0', 'loaders.cache') environ['GI_TYPELIB_PATH'] = join(bundle_lib, 'girepository-1.0') @@ -40,6 +41,8 @@ environ['USERPROFILE'] = environ['HOME'] environ['APPDATA'] = join(environ['HOME'], 'Library', 'Application Support') environ['PATH'] = join(bundle_contents, 'MacOS') + ':' + environ['PATH'] +if __name__ == '__main__': + __file__ = 'gramps_launcher.py' import gramps.grampsapp as app app.main() diff --git a/mac/patches/berkeleydb-4.8-atomic.patch b/mac/patches/berkeleydb-4.8-atomic.patch new file mode 100644 index 000000000..3e74c558b --- /dev/null +++ b/mac/patches/berkeleydb-4.8-atomic.patch @@ -0,0 +1,40 @@ +--- a/dist/configure 2010-04-12 13:25:23.000000000 -0700 ++++ b/dist/configure 2021-04-11 11:27:32.000000000 -0700 +@@ -19158,7 +19158,7 @@ + # x86_64/gcc: FreeBSD, NetBSD, BSD/OS, Linux + cat confdefs.h - <<_ACEOF >conftest.$ac_ext + /* end confdefs.h. */ +- ++#include + int + main () + { +@@ -20197,7 +20197,7 @@ + if test "$db_cv_atomic" = no; then + cat confdefs.h - <<_ACEOF >conftest.$ac_ext + /* end confdefs.h. */ +- ++#include + int + main () + { +--- a/dbinc/atomic.h 2013-03-12 14:07:22.000000000 -0400 ++++ b/dbinc/atomic.h 2013-03-12 14:06:35.000000000 -0400 +@@ -144,7 +144,7 @@ + #define atomic_inc(env, p) __atomic_inc(p) + #define atomic_dec(env, p) __atomic_dec(p) + #define atomic_compare_exchange(env, p, o, n) \ +- __atomic_compare_exchange((p), (o), (n)) ++ __atomic_compare_exchange_db((p), (o), (n)) + static inline int __atomic_inc(db_atomic_t *p) + { + int temp; +@@ -176,7 +176,7 @@ + * http://gcc.gnu.org/onlinedocs/gcc-4.1.0/gcc/Atomic-Builtins.html + * which configure could be changed to use. + */ +-static inline int __atomic_compare_exchange( ++static inline int __atomic_compare_exchange_db( + db_atomic_t *p, atomic_value_t oldval, atomic_value_t newval) + { + atomic_value_t was; diff --git a/mac/patches/pyfontconfig-curexc.patch b/mac/patches/pyfontconfig-curexc.patch new file mode 100644 index 000000000..f3cda3b25 --- /dev/null +++ b/mac/patches/pyfontconfig-curexc.patch @@ -0,0 +1,56 @@ + +--- a/fontconfig.c 2011-11-02 07:59:41.000000000 -0700 ++++ b/fontconfig.c 2021-08-09 18:07:50.000000000 -0700 +@@ -4525,12 +4525,12 @@ + Py_INCREF(local_type); + Py_INCREF(local_value); + Py_INCREF(local_tb); +- tmp_type = tstate->exc_type; +- tmp_value = tstate->exc_value; +- tmp_tb = tstate->exc_traceback; +- tstate->exc_type = local_type; +- tstate->exc_value = local_value; +- tstate->exc_traceback = local_tb; ++ tmp_type = tstate->curexc_type; ++ tmp_value = tstate->curexc_value; ++ tmp_tb = tstate->curexc_traceback; ++ tstate->curexc_type = local_type; ++ tstate->curexc_value = local_value; ++ tstate->curexc_traceback = local_tb; + /* Make sure tstate is in a consistent state when we XDECREF + these objects (XDECREF may run arbitrary code). */ + Py_XDECREF(tmp_type); +@@ -4735,9 +4735,9 @@ + + static CYTHON_INLINE void __Pyx_ExceptionSave(PyObject **type, PyObject **value, PyObject **tb) { + PyThreadState *tstate = PyThreadState_GET(); +- *type = tstate->exc_type; +- *value = tstate->exc_value; +- *tb = tstate->exc_traceback; ++ *type = tstate->curexc_type; ++ *value = tstate->curexc_value; ++ *tb = tstate->curexc_traceback; + Py_XINCREF(*type); + Py_XINCREF(*value); + Py_XINCREF(*tb); +@@ -4746,12 +4746,12 @@ + static void __Pyx_ExceptionReset(PyObject *type, PyObject *value, PyObject *tb) { + PyObject *tmp_type, *tmp_value, *tmp_tb; + PyThreadState *tstate = PyThreadState_GET(); +- tmp_type = tstate->exc_type; +- tmp_value = tstate->exc_value; +- tmp_tb = tstate->exc_traceback; +- tstate->exc_type = type; +- tstate->exc_value = value; +- tstate->exc_traceback = tb; ++ tmp_type = tstate->curexc_type; ++ tmp_value = tstate->curexc_value; ++ tmp_tb = tstate->curexc_traceback; ++ tstate->curexc_type = type; ++ tstate->curexc_value = value; ++ tstate->curexc_traceback = tb; + Py_XDECREF(tmp_type); + Py_XDECREF(tmp_value); + Py_XDECREF(tmp_tb); + +Diff finished. Mon Aug 9 18:09:16 2021 diff --git a/setup.py b/setup.py index 3e9052a30..9e84a0276 100755 --- a/setup.py +++ b/setup.py @@ -353,8 +353,8 @@ data_files_core.append(('share/gramps/css/swanky-purse/images', SWANKY_IMG)) PNG_FILES = glob.glob(os.path.join('data', '*.png')) SVG_FILES = glob.glob(os.path.join('data', '*.svg')) -data_files_core.append(('share/icons/gnome/48x48/mimetypes', PNG_FILES)) -data_files_core.append(('share/icons/gnome/scalable/mimetypes', SVG_FILES)) +data_files_core.append(('share/icons/hicolor/48x48/mimetypes', PNG_FILES)) +data_files_core.append(('share/icons/hicolor/scalable/mimetypes', SVG_FILES)) DTD_FILES = glob.glob(os.path.join('data', '*.dtd')) RNG_FILES = glob.glob(os.path.join('data', '*.rng')) @@ -381,11 +381,15 @@ APP_16 = os.path.join(THEME, '16x16', 'apps', 'gramps.png') APP_22 = os.path.join(THEME, '22x22', 'apps', 'gramps.png') APP_24 = os.path.join(THEME, '24x24', 'apps', 'gramps.png') APP_48 = os.path.join(THEME, '48x48', 'apps', 'gramps.png') +APP_128 = os.path.join(THEME, '128x128', 'apps', 'gramps.png') +APP_256 = os.path.join(THEME, '256x256', 'apps', 'gramps.png') APP_SC = os.path.join(THEME, 'scalable', 'apps', 'gramps.svg') data_files_gui.append(('share/icons/hicolor/16x16/apps', [APP_16])) data_files_gui.append(('share/icons/hicolor/22x22/apps', [APP_22])) data_files_gui.append(('share/icons/hicolor/24x24/apps', [APP_24])) data_files_gui.append(('share/icons/hicolor/48x48/apps', [APP_48])) +data_files_gui.append(('share/icons/hicolor/128x128/apps', [APP_128])) +data_files_gui.append(('share/icons/hicolor/256x256/apps', [APP_256])) data_files_gui.append(('share/icons/hicolor/scalable/apps', [APP_SC])) data_files = data_files_core + data_files_gui