From 4ffa3e8c4924887da0c40ab8c408ba75c13010bb Mon Sep 17 00:00:00 2001 From: Nick Hall Date: Mon, 14 Jul 2014 22:53:01 +0100 Subject: [PATCH] 7846: Prevent creation of a place cycle when merging --- gramps/gen/utils/location.py | 23 +++++++++++++++++++++++ gramps/plugins/lib/libplaceview.py | 12 ++++++++++-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/gramps/gen/utils/location.py b/gramps/gen/utils/location.py index 1862909ed..3de50789f 100644 --- a/gramps/gen/utils/location.py +++ b/gramps/gen/utils/location.py @@ -90,3 +90,26 @@ def get_locations(db, place): if len(place.get_placeref_list()) == 0: locations.append(dict(tree)) return locations + +#------------------------------------------------------------------------- +# +# located_in +# +#------------------------------------------------------------------------- +def located_in(db, handle1, handle2): + """ + Determine if the place identified by handle1 is located within the place + identified by handle2. + """ + place = db.get_place_from_handle(handle1) + todo = [(place, [handle1])] + while len(todo): + place, visited = todo.pop() + for parent in place.get_placeref_list(): + if parent.ref == handle2: + return True + if parent.ref not in visited: + parent_place = db.get_place_from_handle(parent.ref) + parent_visited = visited + [parent.ref] + todo.append((parent_place, parent_visited)) + return False diff --git a/gramps/plugins/lib/libplaceview.py b/gramps/plugins/lib/libplaceview.py index 8e478fd5f..1a41754da 100644 --- a/gramps/plugins/lib/libplaceview.py +++ b/gramps/plugins/lib/libplaceview.py @@ -57,6 +57,7 @@ from gramps.gui.editors import EditPlace, DeletePlaceQuery from gramps.gui.filters.sidebar import PlaceSidebarFilter from gramps.gui.merge import MergePlace from gramps.gen.plug import CATEGORY_QR_PLACE +from gramps.gen.utils.location import located_in #------------------------------------------------------------------------- # @@ -413,8 +414,15 @@ class PlaceBaseView(ListView): "control key while clicking on the desired place.") ErrorDialog(msg, msg2) else: - MergePlace(self.dbstate, self.uistate, mlist[0], mlist[1], - self.merged) + if (located_in(self.dbstate.db, mlist[0], mlist[1]) or + located_in(self.dbstate.db, mlist[1], mlist[0])): + msg = _("Cannot merge places.") + msg2 = _("Merging these places would create a cycle in the " + "place hierarchy.") + ErrorDialog(msg, msg2) + else: + MergePlace(self.dbstate, self.uistate, mlist[0], mlist[1], + self.merged) def merged(self): """