diff --git a/gramps/gen/plug/docgen/treedoc.py b/gramps/gen/plug/docgen/treedoc.py
index b584b6bd0..354a73eb3 100644
--- a/gramps/gen/plug/docgen/treedoc.py
+++ b/gramps/gen/plug/docgen/treedoc.py
@@ -475,8 +475,7 @@ class TreeDocBase(BaseDoc, TreeDoc):
if os.path.isfile(path):
if win():
path = path.replace('\\', '/')
- self.write(level+1, 'image = {{%s}%s},\n' %
- os.path.splitext(path))
+ self.write(level+1, 'image = {%s},\n' % path)
break # first image only
self.write(level, '}\n')
diff --git a/gramps/gen/proxy/referencedbyselection.py b/gramps/gen/proxy/referencedbyselection.py
index 4b678ccb3..5d95f286c 100644
--- a/gramps/gen/proxy/referencedbyselection.py
+++ b/gramps/gen/proxy/referencedbyselection.py
@@ -346,7 +346,7 @@ class ReferencedBySelectionProxyDb(ProxyDbBase):
for tag in note.text.get_tags():
if tag.name == 'Link':
if tag.value.startswith("gramps://"):
- obj_class, prop, value = tag.value[9:].split("/")
+ obj_class, prop, value = tag.value[9:].split("/", 2)
if obj_class == "Media": # bug6493
obj_class = "Media"
if prop == "handle":
diff --git a/gramps/grampsapp.py b/gramps/grampsapp.py
index 6fa0880ec..bc739f47d 100644
--- a/gramps/grampsapp.py
+++ b/gramps/grampsapp.py
@@ -446,9 +446,16 @@ def show_settings():
.replace('(', '').replace(')', '')
bsddb_location_str = bsddb.__file__
except:
- bsddb_str = 'not found'
- bsddb_db_str = 'not found'
- bsddb_location_str = 'not found'
+ try:
+ import berkeleydb as bsddb
+ bsddb_str = bsddb.__version__
+ bsddb_db_str = str(bsddb.db.version()).replace(', ', '.')\
+ .replace('(', '').replace(')', '')
+ bsddb_location_str = bsddb.__file__
+ except:
+ bsddb_str = 'not found'
+ bsddb_db_str = 'not found'
+ bsddb_location_str = 'not found'
try:
import sqlite3
diff --git a/gramps/gui/aboutdialog.py b/gramps/gui/aboutdialog.py
index db4fabae0..de84ab565 100644
--- a/gramps/gui/aboutdialog.py
+++ b/gramps/gui/aboutdialog.py
@@ -68,7 +68,11 @@ try:
import bsddb3 as bsddb ## ok, in try/except
BSDDB_STR = ellipses(str(bsddb.__version__) + " " + str(bsddb.db.version()))
except:
- BSDDB_STR = 'not found'
+ try:
+ import berkeleydb as bsddb
+ BSDDB_STR = ellipses(str(bsddb.__version__) + " " + str(bsddb.db.version()))
+ except:
+ BSDDB_STR = 'not found'
try:
import sqlite3
diff --git a/gramps/gui/listmodel.py b/gramps/gui/listmodel.py
index 3dc48456a..97e91d47b 100644
--- a/gramps/gui/listmodel.py
+++ b/gramps/gui/listmodel.py
@@ -233,7 +233,7 @@ class ListModel:
new_value = not self.model[path][col]
self.model[path][col] = new_value
if col in self.function:
- self.function[col](int(path), new_value)
+ self.function[col](path, new_value)
def __edited_cb(self, cell, path, new_text, col):
"""
@@ -241,7 +241,7 @@ class ListModel:
"""
self.model[path][col] = new_text
if col in self.function:
- self.function[col](int(path), new_text)
+ self.function[col](path, new_text)
def unselect(self):
"""
diff --git a/gramps/gui/logger/_errorreportassistant.py b/gramps/gui/logger/_errorreportassistant.py
index ff95e2f0e..5dacc932c 100644
--- a/gramps/gui/logger/_errorreportassistant.py
+++ b/gramps/gui/logger/_errorreportassistant.py
@@ -37,7 +37,11 @@ try:
import bsddb3 as bsddb # ok, in try/except
BSDDB_STR = str(bsddb.__version__) + " " + str(bsddb.db.version())
except:
- BSDDB_STR = 'not found'
+ try:
+ import berkeleydb as bsddb
+ BSDDB_STR = str(bsddb.__version__) + " " + str(bsddb.db.version())
+ except:
+ BSDDB_STR = 'not found'
try:
import sqlite3
diff --git a/gramps/gui/utils.py b/gramps/gui/utils.py
index ba5315924..c8cd36692 100644
--- a/gramps/gui/utils.py
+++ b/gramps/gui/utils.py
@@ -517,11 +517,26 @@ def color_graph_box(alive=False, gender=Person.MALE):
# color functions. For hsv and hls values, use import colorsys !
+def name_to_hex(value):
+ """
+ Convert a named color to a 6 digit hexadecimal value to rgb.
+ """
+ if value[:1] != "#":
+ # We have color names like "green", "orange", "yellow",...
+ # We need to convert them to hex format
+ Color = Gdk.RGBA()
+ Color.parse(value)
+ value = "#%02x%02x%02x" % (int(Color.red * 255),
+ int(Color.green * 255),
+ int(Color.blue * 255))
+ return value
+
def hex_to_rgb_float(value):
"""
Convert a 6 or 12 digit hexademical value to rgb. Returns tuple of floats
between 0 and 1.
"""
+ value = name_to_hex(value)
value = value.lstrip('#')
lenv = len(value)
return tuple(int(value[i:i+lenv//3], 16)/16.0**(lenv//3)
@@ -531,6 +546,7 @@ def hex_to_rgb(value):
"""
Convert a 6 or 12 digit hexadecimal value to rgb. Returns tuple of integers.
"""
+ value = name_to_hex(value)
value = value.lstrip('#')
lenv = len(value)
return tuple(int(value[i:i+lenv//3], 16) for i in range(0, lenv, lenv//3))
diff --git a/gramps/gui/viewmanager.py b/gramps/gui/viewmanager.py
index 5fe551964..fddde5159 100644
--- a/gramps/gui/viewmanager.py
+++ b/gramps/gui/viewmanager.py
@@ -790,7 +790,12 @@ class ViewManager(CLIManager):
self.__create_page(page_def[0], page_def[1])
self.notebook.set_current_page(page_num)
- return self.pages[page_num]
+ try:
+ return self.pages[page_num]
+ except IndexError:
+ # The following is to avoid 'IndexError: list index out of range'
+ # Should solve bug 12636
+ return self.pages[0]
def get_category(self, cat_name):
"""
@@ -841,7 +846,8 @@ 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 self.active_page:
+ 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)
@@ -884,7 +890,12 @@ class ViewManager(CLIManager):
"""
self.__disconnect_previous_page()
- self.active_page = self.pages[page_num]
+ # The following is to avoid 'IndexError: list index out of range'
+ # Bugs: 12304, 12429, 12623, 12695
+ try:
+ self.active_page = self.pages[page_num]
+ except IndexError:
+ self.active_page = self.pages[0]
self.__connect_active_page(page_num)
self.active_page.set_active()
while Gtk.events_pending():
diff --git a/gramps/gui/views/treemodels/eventmodel.py b/gramps/gui/views/treemodels/eventmodel.py
index bdd1c57e5..a6c0a7849 100644
--- a/gramps/gui/views/treemodels/eventmodel.py
+++ b/gramps/gui/views/treemodels/eventmodel.py
@@ -160,6 +160,8 @@ class EventModel(FlatBaseModel):
date_str = get_date(event)
if date_str != "":
retval = escape(date_str)
+ else:
+ retval = ""
if not get_date_valid(event):
return INVALID_DATE_FORMAT % retval
else:
diff --git a/gramps/gui/widgets/styledtexteditor.py b/gramps/gui/widgets/styledtexteditor.py
index c859837cc..5be0faa24 100644
--- a/gramps/gui/widgets/styledtexteditor.py
+++ b/gramps/gui/widgets/styledtexteditor.py
@@ -396,7 +396,7 @@ class StyledTextEditor(Gtk.TextView):
simple_access = SimpleAccess(win_obj.dbstate.db)
url = link_tag.data
if url.startswith("gramps://"):
- obj_class, prop, value = url[9:].split("/")
+ obj_class, prop, value = url[9:].split("/", 2)
display = simple_access.display(obj_class, prop, value) or url
return display + ((_("\nCommand-Click to follow link") if mac() else
_("\nCtrl-Click to follow link"))
@@ -809,7 +809,7 @@ class StyledTextEditor(Gtk.TextView):
win_obj = find_parent_with_attr(self, attr="dbstate")
if win_obj:
# Edit the object:
- obj_class, prop, value = url[9:].split("/")
+ obj_class, prop, value = url[9:].split("/", 2)
from ..editors import EditObject
EditObject(win_obj.dbstate,
win_obj.uistate,
diff --git a/gramps/plugins/docgen/htmldoc.py b/gramps/plugins/docgen/htmldoc.py
index 6f2111020..f067099b7 100644
--- a/gramps/plugins/docgen/htmldoc.py
+++ b/gramps/plugins/docgen/htmldoc.py
@@ -556,7 +556,10 @@ class HtmlDoc(BaseDoc, TextDoc):
"""
self._empty = 0
size = int(max(w_cm, h_cm) * float(150.0/2.54))
- refname = "is%s" % os.path.basename(name)
+ if crop:
+ refname = "is-%d-%d-%d-%d-%s" % (crop[0], crop[1], crop[2], crop[3], os.path.basename(name))
+ else:
+ refname = "is%s" % os.path.basename(name)
imdir = self._backend.datadirfull()
diff --git a/gramps/plugins/export/exportgedcom.py b/gramps/plugins/export/exportgedcom.py
index 66d9ec54e..21b92c4ea 100644
--- a/gramps/plugins/export/exportgedcom.py
+++ b/gramps/plugins/export/exportgedcom.py
@@ -160,10 +160,9 @@ def breakup(txt, limit):
data = []
while len(txt) > limit:
# look for non-space pair to break between
- # do not break within a UTF-8 byte sequence, i. e. first char >127
+ # fix issue #0012709 by removing Python2 code obsoleted by Python3
idx = limit
- while (idx > 0 and (txt[idx - 1].isspace() or txt[idx].isspace() or
- ord(txt[idx - 1]) > 127)):
+ while (idx > 0 and (txt[idx - 1].isspace() or txt[idx].isspace())):
idx -= 1
if idx == 0:
#no words to break on, just break at limit anyway
@@ -1352,6 +1351,8 @@ class GedcomWriter(UpdateCallback):
"""
citation = self.dbase.get_citation_from_handle(citation_handle)
+ if citation is None: # removed by proxy
+ return
src_handle = citation.get_reference_handle()
if src_handle is None:
diff --git a/gramps/plugins/gramplet/leak.py b/gramps/plugins/gramplet/leak.py
index 36e5867bc..3303a1513 100644
--- a/gramps/plugins/gramplet/leak.py
+++ b/gramps/plugins/gramplet/leak.py
@@ -184,10 +184,13 @@ class Leak(Gramplet):
try:
from bsddb3.db import DBError
except:
- class DBError(Exception):
- """
- Dummy.
- """
+ try:
+ from berkeleydb.db import DBError
+ except:
+ class DBError(Exception):
+ """
+ Dummy.
+ """
self.parent = self.top.get_toplevel()
progress = ProgressMeter(
_('Updating display...'), '', parent=self.parent, can_cancel=True)
diff --git a/gramps/plugins/lib/libhtmlbackend.py b/gramps/plugins/lib/libhtmlbackend.py
index f237693f6..db440d071 100644
--- a/gramps/plugins/lib/libhtmlbackend.py
+++ b/gramps/plugins/lib/libhtmlbackend.py
@@ -300,7 +300,7 @@ class HtmlBackend(DocBackend):
"""
if value.startswith("gramps://"):
if self.build_link:
- obj_class, prop, handle = value[9:].split("/", 3)
+ obj_class, prop, handle = value[9:].split("/", 2)
if prop in ["handle", "gramps_id"]:
value = self.build_link(prop, handle, obj_class)
if not value:
diff --git a/gramps/plugins/lib/libsubstkeyword.py b/gramps/plugins/lib/libsubstkeyword.py
index 3a9d5d34a..23faa0a19 100644
--- a/gramps/plugins/lib/libsubstkeyword.py
+++ b/gramps/plugins/lib/libsubstkeyword.py
@@ -346,7 +346,7 @@ class PlaceFormat(GenericFormat):
return None
def _default_format(self, place):
- return _pd.display(self.database, place)
+ return _pd.display(self.database, place, place.event_date)
def parse_format(self, database, place):
""" Parse the place """
@@ -432,6 +432,8 @@ class EventFormat(GenericFormat):
""" start formatting a place in this event """
place_format = PlaceFormat(self.database, self.string_in)
place = place_format.get_place(self.database, event)
+ if event and place:
+ place.event_date = event.get_date_object()
return place_format.parse_format(self.database, place)
def format_attrib():
@@ -889,6 +891,8 @@ class VariableParse:
return the result """
place_f = PlaceFormat(self.database, self._in)
place = place_f.get_place(self.database, event)
+ if event and place:
+ place.event_date = event.get_date_object()
if self.empty_item(place):
return
return place_f.parse_format(self.database, place)
diff --git a/gramps/plugins/lib/maps/geography.py b/gramps/plugins/lib/maps/geography.py
index 9ac4e0afc..3bed70cf1 100644
--- a/gramps/plugins/lib/maps/geography.py
+++ b/gramps/plugins/lib/maps/geography.py
@@ -218,10 +218,12 @@ class GeoGraphyView(OsmGps, NavigationView):
"""
self.goto_handle(None)
- def add_bookmark(self, *obj):
+ def add_bookmark(self, menu, handle):
"""
Add the place to the bookmark
"""
+ dummy_menu = menu
+ dummy_hdle = handle
mlist = self.selected_handles()
if mlist:
self.bookmarks.add(mlist[0])
diff --git a/gramps/plugins/tool/eventcmp.py b/gramps/plugins/tool/eventcmp.py
index 8d0ec800c..6cd322d9b 100644
--- a/gramps/plugins/tool/eventcmp.py
+++ b/gramps/plugins/tool/eventcmp.py
@@ -60,6 +60,7 @@ _ = glocale.translation.sgettext
from gramps.gui.glade import Glade
from gramps.gui.editors import FilterEditor
from gramps.gen.constfunc import get_curr_dir
+from gramps.gen.display.place import displayer as _pd
#-------------------------------------------------------------------------
#
@@ -332,7 +333,7 @@ class EventComparisonResults(ManagedWindow):
if ename in the_map and len(the_map[ename]) > 0:
event_handle = the_map[ename][0]
del the_map[ename][0]
- date = place = ""
+ date = p_title = ""
if event_handle:
event = self.db.get_event_from_handle(event_handle)
@@ -343,8 +344,9 @@ class EventComparisonResults(ManagedWindow):
place_handle = event.get_place_handle()
if place_handle:
place = self.db.get_place_from_handle(
- place_handle).get_title()
- tlist += [date, sortdate, place]
+ place_handle)
+ p_title = _pd.display(self.dbstate.db, place)
+ tlist += [date, sortdate, p_title]
added = True
else:
tlist += [""]*3
diff --git a/gramps/plugins/view/familyview.py b/gramps/plugins/view/familyview.py
index 61ffc56b8..8201cbba7 100644
--- a/gramps/plugins/view/familyview.py
+++ b/gramps/plugins/view/familyview.py
@@ -348,6 +348,16 @@ class FamilyView(ListView):
_("A bookmark could not be set because "
"no one was selected."), parent=self.uistate.window)
+ def get_handle_from_gramps_id(self, gid):
+ """
+ Return the handle of the family having the given Gramps ID.
+ """
+ obj = self.dbstate.db.get_family_from_gramps_id(gid)
+ if obj:
+ return obj.get_handle()
+ else:
+ return None
+
def add(self, *obj):
family = Family()
try:
diff --git a/gramps/plugins/webreport/basepage.py b/gramps/plugins/webreport/basepage.py
index 333da6c62..1f12b40af 100644
--- a/gramps/plugins/webreport/basepage.py
+++ b/gramps/plugins/webreport/basepage.py
@@ -3334,9 +3334,17 @@ class BasePage:
if self.reference_sort:
role = self.birth_death_dates(gid)
elif role[1:2] == ':':
+ # format of role is role_type:ISO date string
+ if role.count(':') > 1:
+ print("Invalid date :", role[2:], " for individual with ID:", gid,
+ ". Please, use the 'verify the data' tool to correct this.")
+ cal, role = role.split(':', 1)
+ else:
+ cal, role = role.split(':')
+
# cal is the original calendar
- cal, role = role.split(':')
- # conver ISO date to Date for translation.
+
+ # convert ISO date to Date for translation.
# all modifiers are in english, so convert them
# to the local language
if len(role.split(' - ')) > 1:
diff --git a/gramps/plugins/webreport/media.py b/gramps/plugins/webreport/media.py
index b7125d608..56b80539d 100644
--- a/gramps/plugins/webreport/media.py
+++ b/gramps/plugins/webreport/media.py
@@ -486,7 +486,13 @@ class MediaPages(BasePage):
if orig_image_path != newpath:
url = self.report.build_url_fname(
newpath, None, self.uplink, image=True)
- s_width = 'width: %dpx;' % max_width
+ regions = self.media_ref_rect_regions(media_handle)
+ if regions:
+ s_width = 'width: %dpx;' % max_width
+ elif width < max_width:
+ s_width = 'width: %dpx;' % width
+ else:
+ s_width = 'width: %dpx;' % max_width
mediadisplay += Html("a", href=url) + (
Html("img", src=url,
style=s_width,
diff --git a/gramps/plugins/webreport/webcal.py b/gramps/plugins/webreport/webcal.py
index e2f901642..51a9fc2f7 100644
--- a/gramps/plugins/webreport/webcal.py
+++ b/gramps/plugins/webreport/webcal.py
@@ -1358,7 +1358,7 @@ return false;
age_at_death = age_at_death.format(dlocale=self.rlocale)
# determine birthday information???
- if (self.birthday and birth_date is not Date()
+ if (self.birthday and birth_date != Date()
and birth_date.is_valid()):
birth_date = gregorian(birth_date)
diff --git a/mac/Info.plist b/mac/Info.plist
index 0148e6ed2..5de0fbc91 100755
--- a/mac/Info.plist
+++ b/mac/Info.plist
@@ -7,7 +7,7 @@
CFBundleExecutable
Gramps
CFBundleGetInfoString
- Gramps-5.1.5-1, (C) 1997-2022 The Gramps Team http://www.gramps-project.org
+ Gramps-5.1.5-2, (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.5-1
+ Gramps-5.1.5-2
CFBundleSignature
????
CFBundleVersion
- Gramps-5.1.5-1
+ Gramps-5.1.5-2
NSHumanReadableCopyright
Copyright 1997 - 2022 The Gramps Team, GNU General Public License.
LSMinimumSystemVersion
- 10.12
+ 10.13
GtkOSXLaunchScriptFile
gramps_launcher.py
NSHighResolutionCapable
diff --git a/mac/gramps.bundle b/mac/gramps.bundle
index 3d757c914..b21bc9b46 100644
--- a/mac/gramps.bundle
+++ b/mac/gramps.bundle
@@ -42,7 +42,7 @@
- ${prefix}/lib/python3.9/*.so
+ ${prefix}/lib/python3.10/*.so
@@ -153,19 +153,19 @@
- ${prefix}/lib/python3.9/*.py
+ ${prefix}/lib/python3.10/*.py
- ${prefix}/lib/python3.9/config-3.9-darwin/
+ ${prefix}/lib/python3.10/config-3.10-darwin/
- ${prefix}/lib/python3.9/site-packages/gramps/gen/utils/resource-path
+ ${prefix}/lib/python3.10/site-packages/gramps/gen/utils/resource-path
- ${prefix}/include/python3.9/pyconfig.h
+ ${prefix}/include/python3.10/pyconfig.h
@@ -182,7 +182,7 @@
- ${prefix}/lib/python3.9/site-packages/gramps/*.glade
+ ${prefix}/lib/python3.10/site-packages/gramps/*.glade
diff --git a/mac/gramps.modules b/mac/gramps.modules
index 07b7384c0..58b30c9ff 100644
--- a/mac/gramps.modules
+++ b/mac/gramps.modules
@@ -57,7 +57,8 @@ gtk-mac-bundler gtk-osx-build/projects/gramps/gramps.bundle
+ checkoutdir="gramps-gramps-5.1.5">
+
@@ -101,7 +102,7 @@ gtk-mac-bundler gtk-osx-build/projects/gramps/gramps.bundle
-
@@ -147,6 +148,7 @@ https://files.pythonhosted.org/packages/
repo="oracle"
hash="sha256:12edc0df75bf9abd7f82f821795bcee50f42cb2e5f76a6a281b85732798364ef" >
+
@@ -163,6 +165,7 @@ https://files.pythonhosted.org/packages/
repo="oracle"
hash="sha256:12edc0df75bf9abd7f82f821795bcee50f42cb2e5f76a6a281b85732798364ef">
+
@@ -210,7 +213,6 @@ https://files.pythonhosted.org/packages/
-
diff --git a/mac/patches/berkeleydb-4.8-mutex.patch b/mac/patches/berkeleydb-4.8-mutex.patch
new file mode 100644
index 000000000..bb31dd23f
--- /dev/null
+++ b/mac/patches/berkeleydb-4.8-mutex.patch
@@ -0,0 +1,115 @@
+--- a/dist/configure 2022-12-26 13:46:24.000000000 -0800
++++ b/dist/configure 2022-12-27 11:35:26.000000000 -0800
+@@ -18756,6 +18756,7 @@
+ /* end confdefs.h. */
+
+ #include
++#include
+ int
+ main ()
+ {
+@@ -18792,7 +18793,8 @@
+ /* end confdefs.h. */
+
+ #include
+-main() {
++#include
++int main() {
+ pthread_cond_t cond;
+ pthread_mutex_t mutex;
+ pthread_condattr_t condattr;
+@@ -18828,6 +18830,7 @@
+ /* end confdefs.h. */
+
+ #include
++#include
+ int
+ main ()
+ {
+@@ -18864,7 +18867,7 @@
+ /* end confdefs.h. */
+
+ #include
+-main() {
++int main() {
+ pthread_cond_t cond;
+ pthread_mutex_t mutex;
+ pthread_condattr_t condattr;
+@@ -18899,6 +18902,7 @@
+ /* end confdefs.h. */
+
+ #include
++#include
+ int
+ main ()
+ {
+@@ -18933,7 +18937,8 @@
+ /* end confdefs.h. */
+
+ #include
+-main() {
++#include
++int main() {
+ pthread_cond_t cond;
+ pthread_mutex_t mutex;
+ pthread_condattr_t condattr;
+@@ -18967,6 +18972,7 @@
+ /* end confdefs.h. */
+
+ #include
++#include
+ int
+ main ()
+ {
+@@ -19001,7 +19007,8 @@
+ /* end confdefs.h. */
+
+ #include
+-main() {
++#include
++int main() {
+ pthread_cond_t cond;
+ pthread_mutex_t mutex;
+ pthread_condattr_t condattr;
+@@ -19038,6 +19045,7 @@
+ /* end confdefs.h. */
+
+ #include
++#include
+ int
+ main ()
+ {
+@@ -19069,6 +19077,7 @@
+
+ #include
+ #include
++#include
+ int
+ main ()
+ {
+@@ -19099,6 +19108,7 @@
+
+ #include
+ #include
++#include
+ int
+ main ()
+ {
+@@ -20743,7 +20753,7 @@
+ /* end confdefs.h. */
+
+ #include
+-main() {
++int main() {
+ struct timespec t;
+ return (clock_gettime(CLOCK_MONOTONIC, &t) != 0);
+ }
+@@ -21634,7 +21644,7 @@
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+ /* end confdefs.h. */
+
+- main() {
++ int main() {
+ $db_cv_seq_type l;
+ unsigned $db_cv_seq_type u;
+ char buf[100];
diff --git a/mac/patches/gramps-berkeleydb.patch b/mac/patches/gramps-berkeleydb.patch
new file mode 100644
index 000000000..29b871673
--- /dev/null
+++ b/mac/patches/gramps-berkeleydb.patch
@@ -0,0 +1,273 @@
+From 9ea0164f1f84f92deea7a0bde0343e2f0e485525 Mon Sep 17 00:00:00 2001
+From: John Ralls
+Date: Tue, 27 Dec 2022 12:50:14 -0800
+Subject: [PATCH] Try to import berkeleydb if bsddb3 isn't found.
+
+berkelydb is usable for python >= 3.6 and required for
+python >= 3.10. See https://www.jcea.es/programacion/pybsddb.htm.
+---
+ gramps/grampsapp.py | 13 ++++++++++---
+ gramps/gui/aboutdialog.py | 6 +++++-
+ gramps/gui/logger/_errorreportassistant.py | 6 +++++-
+ gramps/plugins/db/bsddb/bsddbtxn.py | 5 ++++-
+ gramps/plugins/db/bsddb/cursor.py | 17 ++++++++++-------
+ gramps/plugins/db/bsddb/read.py | 15 +++++++++------
+ gramps/plugins/db/bsddb/summary.py | 6 ++++--
+ gramps/plugins/db/bsddb/test/cursor_test.py | 6 ++++--
+ gramps/plugins/db/bsddb/undoredo.py | 13 ++++++++-----
+ gramps/plugins/db/bsddb/upgrade.py | 6 ++++--
+ gramps/plugins/db/bsddb/write.py | 10 ++++++++--
+ gramps/plugins/gramplet/leak.py | 11 +++++++----
+ 12 files changed, 78 insertions(+), 36 deletions(-)
+
+diff --git a/gramps/grampsapp.py b/gramps/grampsapp.py
+index 2163edc8a..fed06207d 100644
+--- a/gramps/grampsapp.py
++++ b/gramps/grampsapp.py
+@@ -325,9 +325,16 @@ def show_settings():
+ .replace('(', '').replace(')', '')
+ bsddb_location_str = bsddb.__file__
+ except:
+- bsddb_str = 'not found'
+- bsddb_db_str = 'not found'
+- bsddb_location_str = 'not found'
++ try:
++ import berkeleydb as bsddb
++ bsddb_str = bsddb.__version__
++ bsddb_db_str = str(bsddb.db.version()).replace(', ', '.')\
++ .replace('(', '').replace(')', '')
++ bsddb_location_str = bsddb.__file__
++ except:
++ bsddb_str = 'not found'
++ bsddb_db_str = 'not found'
++ bsddb_location_str = 'not found'
+
+ try:
+ import sqlite3
+diff --git a/gramps/gui/aboutdialog.py b/gramps/gui/aboutdialog.py
+index f2b28a036..2afb77816 100644
+--- a/gramps/gui/aboutdialog.py
++++ b/gramps/gui/aboutdialog.py
+@@ -68,7 +68,11 @@ try:
+ import bsddb3 as bsddb ## ok, in try/except
+ BSDDB_STR = ellipses(str(bsddb.__version__) + " " + str(bsddb.db.version()))
+ except:
+- BSDDB_STR = 'not found'
++ try:
++ import berkeleydb as bsddb
++ BSDDB_STR = ellipses(str(bsddb.__version__) + " " + str(bsddb.db.version()))
++ except:
++ BSDDB_STR = 'not found'
+
+ try:
+ import sqlite3
+diff --git a/gramps/gui/logger/_errorreportassistant.py b/gramps/gui/logger/_errorreportassistant.py
+index af9979693..1924701e2 100644
+--- a/gramps/gui/logger/_errorreportassistant.py
++++ b/gramps/gui/logger/_errorreportassistant.py
+@@ -37,7 +37,11 @@ try:
+ import bsddb3 as bsddb # ok, in try/except
+ BSDDB_STR = str(bsddb.__version__) + " " + str(bsddb.db.version())
+ except:
+- BSDDB_STR = 'not found'
++ try:
++ import berkeleydb as bsddb
++ BSDDB_STR = str(bsddb.__version__) + " " + str(bsddb.db.version())
++ except:
++ BSDDB_STR = 'not found'
+
+ try:
+ import sqlite3
+diff --git a/gramps/plugins/db/bsddb/bsddbtxn.py b/gramps/plugins/db/bsddb/bsddbtxn.py
+index 7af99494e..0f3fe0de4 100644
+--- a/gramps/plugins/db/bsddb/bsddbtxn.py
++++ b/gramps/plugins/db/bsddb/bsddbtxn.py
+@@ -218,7 +218,10 @@ class BSDDBTxn:
+ # test code
+ if __name__ == "__main__":
+ print("1")
+- from bsddb3 import db, dbshelve
++ try:
++ from bsddb3 import db, dbshelve
++ except:
++ from berkeleydb import db, dbshelve
+ print("2")
+ x = db.DBEnv()
+ print("3")
+diff --git a/gramps/plugins/db/bsddb/cursor.py b/gramps/plugins/db/bsddb/cursor.py
+index 1eecffc03..4ddd9a64c 100644
+--- a/gramps/plugins/db/bsddb/cursor.py
++++ b/gramps/plugins/db/bsddb/cursor.py
+@@ -29,14 +29,17 @@ from pickle import dumps, loads
+ try:
+ from bsddb3 import db
+ except:
++ try:
++ from berkeleydb import db
++ except:
+ # FIXME: make this more abstract to deal with other backends
+- class db:
+- DB_RMW = 0
+- DB_FIRST = 0
+- DB_LAST = 0
+- DB_CURRENT = 0
+- DB_PREV = 0
+- DB_NEXT = 0
++ class db:
++ DB_RMW = 0
++ DB_FIRST = 0
++ DB_LAST = 0
++ DB_CURRENT = 0
++ DB_PREV = 0
++ DB_NEXT = 0
+
+ #-------------------------------------------------------------------------
+ #
+diff --git a/gramps/plugins/db/bsddb/read.py b/gramps/plugins/db/bsddb/read.py
+index 57906b795..6b0b6a7a8 100644
+--- a/gramps/plugins/db/bsddb/read.py
++++ b/gramps/plugins/db/bsddb/read.py
+@@ -41,12 +41,15 @@ from functools import partial
+ try:
+ from bsddb3 import db
+ except:
+- # FIXME: make this more abstract to deal with other backends
+- class db:
+- DBRunRecoveryError = 0
+- DBAccessError = 0
+- DBPageNotFoundError = 0
+- DBInvalidArgError = 0
++ try:
++ from berkeleydb import db
++ except:
++ # FIXME: make this more abstract to deal with other backends
++ class db:
++ DBRunRecoveryError = 0
++ DBAccessError = 0
++ DBPageNotFoundError = 0
++ DBInvalidArgError = 0
+
+ import re
+ import logging
+diff --git a/gramps/plugins/db/bsddb/summary.py b/gramps/plugins/db/bsddb/summary.py
+index e73908128..a911d666f 100644
+--- a/gramps/plugins/db/bsddb/summary.py
++++ b/gramps/plugins/db/bsddb/summary.py
+@@ -22,8 +22,10 @@
+ ## specific to bsddb
+
+ import os
+-from bsddb3 import dbshelve, db
+-
++try:
++ from bsddb3 import dbshelve, db
++except:
++ from berkeleydb import db, dbshelve
+ from gramps.gen.db import META, PERSON_TBL
+ from gramps.gen.db.dbconst import BDBVERSFN
+
+diff --git a/gramps/plugins/db/bsddb/test/cursor_test.py b/gramps/plugins/db/bsddb/test/cursor_test.py
+index e90e16fd4..1411aca43 100644
+--- a/gramps/plugins/db/bsddb/test/cursor_test.py
++++ b/gramps/plugins/db/bsddb/test/cursor_test.py
+@@ -23,8 +23,10 @@ import os
+ import tempfile
+ import shutil
+
+-from bsddb3 import dbshelve, db
+-
++try:
++ from bsddb3 import dbshelve, db
++except:
++ from berkeleydb import db, dbshelve
+ from ..read import DbBsddbTreeCursor
+
+ class Data:
+diff --git a/gramps/plugins/db/bsddb/undoredo.py b/gramps/plugins/db/bsddb/undoredo.py
+index 53ab39a60..729f21eb9 100644
+--- a/gramps/plugins/db/bsddb/undoredo.py
++++ b/gramps/plugins/db/bsddb/undoredo.py
+@@ -36,12 +36,15 @@ from collections import deque
+ try:
+ from bsddb3 import db
+ except:
++ try:
++ from berkeleydb import db
++ except:
+ # FIXME: make this more abstract to deal with other backends
+- class db:
+- DBRunRecoveryError = 0
+- DBAccessError = 0
+- DBPageNotFoundError = 0
+- DBInvalidArgError = 0
++ class db:
++ DBRunRecoveryError = 0
++ DBAccessError = 0
++ DBPageNotFoundError = 0
++ DBInvalidArgError = 0
+
+ from gramps.gen.const import GRAMPS_LOCALE as glocale
+ _ = glocale.translation.gettext
+diff --git a/gramps/plugins/db/bsddb/upgrade.py b/gramps/plugins/db/bsddb/upgrade.py
+index 48fd189dd..6861fcd5c 100644
+--- a/gramps/plugins/db/bsddb/upgrade.py
++++ b/gramps/plugins/db/bsddb/upgrade.py
+@@ -32,8 +32,10 @@ import os
+ import re
+ import time
+ import logging
+-from bsddb3 import db
+-
++try:
++ from bsddb3 import db
++except:
++ from berkeleydb import db
+ #-------------------------------------------------------------------------
+ #
+ # Gramps modules
+diff --git a/gramps/plugins/db/bsddb/write.py b/gramps/plugins/db/bsddb/write.py
+index f1743fd21..809b77383 100644
+--- a/gramps/plugins/db/bsddb/write.py
++++ b/gramps/plugins/db/bsddb/write.py
+@@ -40,8 +40,14 @@ import logging
+ from sys import maxsize, getfilesystemencoding, version_info
+ from ast import literal_eval as safe_eval
+
+-from bsddb3 import dbshelve, db
+-from bsddb3.db import DB_CREATE, DB_AUTO_COMMIT, DB_DUP, DB_DUPSORT, DB_RDONLY
++try:
++ from bsddb3 import dbshelve, db
++except:
++ from berkeleydb import db, dbshelve
++try:
++ from bsddb3.db import DB_CREATE, DB_AUTO_COMMIT, DB_DUP, DB_DUPSORT, DB_RDONLY
++except:
++ from berkeleydb.db import DB_CREATE, DB_AUTO_COMMIT, DB_DUP, DB_DUPSORT, DB_RDONLY
+
+ DBFLAGS_O = DB_CREATE | DB_AUTO_COMMIT # Default flags for database open
+ DBFLAGS_R = DB_RDONLY # Flags to open a database read-only
+diff --git a/gramps/plugins/gramplet/leak.py b/gramps/plugins/gramplet/leak.py
+index 949531cf7..0c09c0c4e 100644
+--- a/gramps/plugins/gramplet/leak.py
++++ b/gramps/plugins/gramplet/leak.py
+@@ -184,10 +184,13 @@ class Leak(Gramplet):
+ try:
+ from bsddb3.db import DBError
+ except:
+- class DBError(Exception):
+- """
+- Dummy.
+- """
++ try:
++ from berkeleydb.db import DBError
++ except:
++ class DBError(Exception):
++ """
++ Dummy.
++ """
+ self.parent = self.top.get_toplevel()
+ progress = ProgressMeter(
+ _('Updating display...'), '', parent=self.parent, can_cancel=True)
+--
+2.37.1 (Apple Git-137.1)
+