diff --git a/gramps/gen/db/base.py b/gramps/gen/db/base.py
index 66f586653..7fc9705c1 100644
--- a/gramps/gen/db/base.py
+++ b/gramps/gen/db/base.py
@@ -1894,14 +1894,10 @@ class DbWriteBase(DbReadBase):
handle = person.get_handle()
- person_list = [
- item[1] for item in
- self.find_backlink_handles(handle, ['Person'])]
-
- for phandle in person_list:
- prsn = self.get_person_from_handle(phandle)
- prsn.remove_handle_references('Person', [handle])
- self.commit_person(prsn, trans)
+ for obj_type, ohandle in self.find_backlink_handles(handle):
+ obj = self.method("get_%s_from_handle", obj_type)(ohandle)
+ obj.remove_handle_references('Person', [handle])
+ self.method("commit_%s", obj_type)(obj, trans)
self.remove_person(handle, trans)
def remove_family_relationships(self, family_handle, trans=None):
@@ -1919,13 +1915,11 @@ class DbWriteBase(DbReadBase):
"""
Remove a family and all that references it; trans is compulsory.
"""
- person_list = [item[1] for item in
- self.find_backlink_handles(family_handle, ['Person'])]
- for phandle in person_list:
- person = self.get_person_from_handle(phandle)
- if person:
- person.remove_handle_references('Family', [family_handle])
- self.commit_person(person, trans)
+ for obj_type, ohandle in self.find_backlink_handles(family_handle):
+ obj = self.method("get_%s_from_handle", obj_type)(ohandle)
+ if obj:
+ obj.remove_handle_references('Family', [family_handle])
+ self.method("commit_%s", obj_type)(obj, trans)
self.remove_family(family_handle, trans)
def remove_parent_from_family(self, person_handle, family_handle,
diff --git a/gramps/gen/filters/rules/test/media_rules_test.py b/gramps/gen/filters/rules/test/media_rules_test.py
index 395496109..5d2ddd338 100644
--- a/gramps/gen/filters/rules/test/media_rules_test.py
+++ b/gramps/gen/filters/rules/test/media_rules_test.py
@@ -114,7 +114,8 @@ class BaseTest(unittest.TestCase):
"""
rule = HasReferenceCountOf(['greater than', '1'])
self.assertEqual(self.filter_with_rule(rule), set([
- '238CGQ939HG18SS5MG', 'b39fe1cfc1305ac4a21']))
+ '238CGQ939HG18SS5MG', 'b39fe1cfc1305ac4a21',
+ 'Y3ARGQWE088EQRTTDH']))
def test_hassourcecount(self):
"""
diff --git a/gramps/gen/lib/note.py b/gramps/gen/lib/note.py
index 05a4d7506..9f1ae33a6 100644
--- a/gramps/gen/lib/note.py
+++ b/gramps/gen/lib/note.py
@@ -161,7 +161,79 @@ class Note(BasicPrimaryObject):
:returns: List of (classname, handle) tuples for referenced objects.
:rtype: list
"""
- return self.get_referenced_tag_handles()
+ reflist = []
+ for dom, obj, prop, hndl in self.get_links():
+ if dom != "gramps" or prop != "handle":
+ continue
+ else:
+ reflist.append((obj, hndl))
+ reflist.extend(self.get_referenced_tag_handles())
+ return reflist
+
+ def has_handle_reference(self, classname, handle):
+ """
+ Return True if the object has reference to a given handle of given
+ primary object type.
+
+ :param classname: The name of the primary object class.
+ :type classname: str
+ :param handle: The handle to be checked.
+ :type handle: str
+
+ :returns:
+ Returns whether the object has reference to this handle of
+ this object type.
+
+ :rtype: bool
+ """
+ for dom, obj, prop, hndl in self.get_links():
+ if dom == "gramps" and prop == "handle" and \
+ obj == classname and hndl == handle:
+ return True
+ return False
+
+ def remove_handle_references(self, classname, handle_list):
+ """
+ Remove all references in this object to object handles in the list.
+
+ :param classname: The name of the primary object class.
+ :type classname: str
+ :param handle_list: The list of handles to be removed.
+ :type handle_list: str
+
+ If the link is in the styled text, we just remove the style for that
+ link.
+ """
+ tags = []
+ for styledtext_tag in self.text.get_tags():
+ if(styledtext_tag.name == StyledTextTagType.LINK and
+ styledtext_tag.value.startswith("gramps://")):
+ obj, prop, value = styledtext_tag.value[9:].split("/", 2)
+ if obj == classname and prop == 'handle' and \
+ value in handle_list:
+ continue
+ tags.append(styledtext_tag)
+ self.text.set_tags(tags)
+
+ def replace_handle_reference(self, classname, old_handle, new_handle):
+ """
+ Replace all references to old handle with those to the new handle.
+
+ :param classname: The name of the primary object class.
+ :type classname: str
+ :param old_handle: The handle to be replaced.
+ :type old_handle: str
+ :param new_handle: The handle to replace the old one with.
+ :type new_handle: str
+ """
+ for styledtext_tag in self.text.get_tags():
+ if(styledtext_tag.name == StyledTextTagType.LINK and
+ styledtext_tag.value.startswith("gramps://")):
+ obj, prop, value = styledtext_tag.value[9:].split("/", 2)
+ if(obj == classname and prop == 'handle' and
+ value == old_handle):
+ styledtext_tag.value = styledtext_tag.value.replace(
+ old_handle, new_handle)
def merge(self, acquisition):
"""
diff --git a/gramps/gen/lib/notebase.py b/gramps/gen/lib/notebase.py
index 6bfb51de7..e9fe54b48 100644
--- a/gramps/gen/lib/notebase.py
+++ b/gramps/gen/lib/notebase.py
@@ -22,6 +22,15 @@
"""
NoteBase class for Gramps.
"""
+#-------------------------------------------------------------------------
+#
+# Python modules
+#
+#-------------------------------------------------------------------------
+import logging
+
+LOG = logging.getLogger(".note")
+
#-------------------------------------------------------------------------
#
@@ -132,6 +141,25 @@ class NoteBase:
return False
+ def remove_note_references(self, handle_list):
+ """
+ Remove the specified handles from the list of note handles, and all
+ secondary child objects.
+
+ :param citation_handle_list: The list of note handles to be removed
+ :type handle: list
+ """
+ LOG.debug('enter remove_note handle: %s self: %s note_list: %s',
+ handle_list, self, self.note_list)
+ for handle in handle_list:
+ if handle in self.note_list:
+ LOG.debug('remove handle %s from note_list %s',
+ handle, self.note_list)
+ self.note_list.remove(handle)
+ LOG.debug('get_note_child_list %s', self.get_note_child_list())
+ for item in self.get_note_child_list():
+ item.remove_note_references(handle_list)
+
def set_note_list(self, note_list):
"""
Assign the passed list to be object's list of :class:`~.note.Note`
diff --git a/gramps/gen/lib/primaryobj.py b/gramps/gen/lib/primaryobj.py
index 59a1a1342..863f8939d 100644
--- a/gramps/gen/lib/primaryobj.py
+++ b/gramps/gen/lib/primaryobj.py
@@ -39,8 +39,10 @@ from .tableobj import TableObject
from .privacybase import PrivacyBase
from .citationbase import CitationBase
from .mediabase import MediaBase
+from .notebase import NoteBase
from .tagbase import TagBase
+
#-------------------------------------------------------------------------
#
# Basic Primary Object class
@@ -274,6 +276,8 @@ class PrimaryObject(BasicPrimaryObject):
self.remove_citation_references(handle_list)
elif classname == 'Media' and isinstance(self, MediaBase):
self.remove_media_references(handle_list)
+ elif classname == 'Note' and isinstance(self, NoteBase):
+ self.remove_note_references(handle_list)
else:
self._remove_handle_references(classname, handle_list)
diff --git a/gramps/gen/lib/test/merge_test.py b/gramps/gen/lib/test/merge_test.py
index 69997e36d..ff6faf89d 100644
--- a/gramps/gen/lib/test/merge_test.py
+++ b/gramps/gen/lib/test/merge_test.py
@@ -967,6 +967,49 @@ class NoteCheck(unittest.TestCase, PrivacyBaseTest):
self.titanic = Note("hello world")
self.ref_obj = Note("hello world")
+ def test_note_replace_handle_reference(self):
+ ptag = StyledTextTag(name=StyledTextTagType.LINK,
+ value="gramps://Event/handle/e0000",
+ ranges=[0, 3])
+ self.phoenix.text.set_tags([ptag])
+ rtag = StyledTextTag(name=StyledTextTagType.LINK,
+ value="gramps://Event/handle/e0001",
+ ranges=[0, 3])
+ self.ref_obj.text.set_tags([rtag])
+ self.phoenix.replace_handle_reference('Event', 'e0000', 'e0001')
+ self.assertEqual(self.phoenix.serialize(), self.ref_obj.serialize())
+
+ def test_note_has_handle_reference(self):
+ ptag = StyledTextTag(name=StyledTextTagType.LINK,
+ value="gramps://Event/handle/e0000",
+ ranges=[0, 3])
+ self.phoenix.text.set_tags([ptag])
+ self.assertTrue(self.phoenix.has_handle_reference('Event', 'e0000'))
+ self.assertFalse(self.phoenix.has_handle_reference('Event', 'e0001'))
+
+ def test_note_get_referenced_handles(self):
+ tag0 = StyledTextTag(name=StyledTextTagType.LINK,
+ value="gramps://Event/handle/e0000",
+ ranges=[0, 2])
+ tag1 = StyledTextTag(name=StyledTextTagType.LINK,
+ value="gramps://Person/handle/i0001",
+ ranges=[2, 3])
+ self.phoenix.text.set_tags([tag0, tag1])
+ self.phoenix.add_tag("t1234")
+ tag_list = self.phoenix.get_referenced_handles()
+ self.assertEqual(tag_list, [('Event', 'e0000'), ('Person', 'i0001'),
+ ('Tag', 't1234')])
+ self.assertFalse(self.phoenix.has_handle_reference('Event', 'e0001'))
+
+ def test_note_remove_handle_references(self):
+ ptag = StyledTextTag(name=StyledTextTagType.LINK,
+ value="gramps://Event/handle/e0000",
+ ranges=[0, 3])
+ self.phoenix.text.set_tags([ptag])
+ self.phoenix.remove_handle_references('Event', ['e0000'])
+ self.assertEqual(self.phoenix.serialize(), self.ref_obj.serialize())
+
+
class NoteBaseCheck(unittest.TestCase):
def setUp(self):
self.phoenix = NoteBase()
@@ -984,6 +1027,7 @@ class NoteBaseCheck(unittest.TestCase):
def test_different(self):
ref_note_list = NoteBase(self.phoenix)
note = Note("note other")
+ note.set_handle('654321')
self.titanic.add_note(note.get_handle())
ref_note_list.add_note(note.get_handle())
self.phoenix._merge_note_list(self.titanic)
@@ -1018,6 +1062,14 @@ class NoteBaseCheck(unittest.TestCase):
self.phoenix.replace_note_references('','')
self.assertEqual(self.phoenix.serialize(), ref_note_list.serialize())
+ def test_remove_note_references(self):
+ note = Note("note other")
+ note.set_handle('654321')
+ self.phoenix.add_note(note.get_handle())
+ self.phoenix.remove_note_references(['123456', '654321'])
+ ref_note_list = NoteBase()
+ self.assertEqual(self.phoenix.serialize(), ref_note_list.serialize())
+
class PersonCheck(unittest.TestCase, PrivacyBaseTest, MediaBaseTest,
AttrBaseTest, NoteBaseTest, CitationBaseTest):
def setUp(self):
diff --git a/gramps/gen/merge/mergecitationquery.py b/gramps/gen/merge/mergecitationquery.py
index bd7e610e1..3e04af79b 100644
--- a/gramps/gen/merge/mergecitationquery.py
+++ b/gramps/gen/merge/mergecitationquery.py
@@ -28,8 +28,8 @@ Provide merge capabilities for citations.
# Gramps modules
#
#-------------------------------------------------------------------------
-from ..lib import (Person, Family, Event, Place,
- Media, Repository, Citation, Source)
+from ..lib import (Person, Family, Event, Place, Media, Repository,
+ Citation, Source, Note)
from ..db import DbTxn
from ..const import GRAMPS_LOCALE as glocale
_ = glocale.translation.sgettext
@@ -105,6 +105,12 @@ class MergeCitationQuery:
source.replace_citation_references(old_handle,
new_handle)
self.database.commit_source(source, trans)
+ elif class_name == Note.__name__:
+ note = self.database.get_note_from_handle(handle)
+ assert(note.has_handle_reference('Citation', old_handle))
+ note.replace_handle_reference(
+ 'Citation', old_handle, new_handle)
+ self.database.commit_note(note, trans)
else:
raise MergeError("Encounter an object of type %s that has "
"a citation reference." % class_name)
diff --git a/gramps/gen/merge/mergeeventquery.py b/gramps/gen/merge/mergeeventquery.py
index 28250424e..ba32f64f2 100644
--- a/gramps/gen/merge/mergeeventquery.py
+++ b/gramps/gen/merge/mergeeventquery.py
@@ -27,7 +27,7 @@ Provide merge capabilities for events.
# Gramps modules
#
#-------------------------------------------------------------------------
-from ..lib import Person, Family
+from ..lib import Person, Family, Note
from ..db import DbTxn
from ..const import GRAMPS_LOCALE as glocale
_ = glocale.translation.sgettext
@@ -88,6 +88,12 @@ class MergeEventQuery:
family.replace_handle_reference("Event", old_handle,
new_handle)
self.database.commit_family(family, trans)
+ elif class_name == Note.__name__:
+ note = self.database.get_note_from_handle(handle)
+ assert(note.has_handle_reference('Event', old_handle))
+ note.replace_handle_reference(
+ 'Event', old_handle, new_handle)
+ self.database.commit_note(note, trans)
else:
raise MergeError("Encounter an object of type %s that has "
"an event reference." % class_name)
diff --git a/gramps/gen/merge/mergefamilyquery.py b/gramps/gen/merge/mergefamilyquery.py
index 4dc8aff93..7659c92d9 100644
--- a/gramps/gen/merge/mergefamilyquery.py
+++ b/gramps/gen/merge/mergefamilyquery.py
@@ -194,12 +194,17 @@ class MergeFamilyQuery:
if phoenix_mother:
phoenix_mother.remove_family_handle(old_handle)
self.database.commit_person(phoenix_mother, trans)
- # replace the family in lds ordinances
- for (dummy, person_handle) in self.database.find_backlink_handles(
- old_handle, ['Person']):
- if person_handle in (self.titanic_fh, self.titanic_mh):
+ # replace the family in lds ordinances and notes
+ for (ref_obj, ref_handle) in self.database.find_backlink_handles(
+ old_handle, ['Person', 'Note']):
+ if ref_handle in (self.titanic_fh, self.titanic_mh):
continue
- person = self.database.get_person_from_handle(person_handle)
- person.replace_handle_reference('Family', old_handle,new_handle)
- self.database.commit_person(person, trans)
+ obj = self.database.method(
+ "get_%s_from_handle", ref_obj)(ref_handle)
+ assert obj.has_handle_reference('Family', old_handle)
+ obj.replace_handle_reference(
+ 'Family', old_handle, new_handle)
+ if ref_handle != old_handle:
+ self.database.method("commit_%s", ref_obj)(obj, trans)
+
self.database.remove_family(old_handle, trans)
diff --git a/gramps/gen/merge/mergemediaquery.py b/gramps/gen/merge/mergemediaquery.py
index b03e983b6..e153c784e 100644
--- a/gramps/gen/merge/mergemediaquery.py
+++ b/gramps/gen/merge/mergemediaquery.py
@@ -27,7 +27,7 @@ Provide merge capabilities for media objects.
# Gramps modules
#
#-------------------------------------------------------------------------
-from ..lib import Person, Family, Event, Source, Citation, Place
+from ..lib import Person, Family, Event, Source, Citation, Place, Note
from ..db import DbTxn
from ..const import GRAMPS_LOCALE as glocale
_ = glocale.translation.sgettext
@@ -90,6 +90,12 @@ class MergeMediaQuery:
assert(place.has_media_reference(old_handle))
place.replace_media_references(old_handle, new_handle)
self.database.commit_place(place, trans)
+ elif class_name == Note.__name__:
+ note = self.database.get_note_from_handle(handle)
+ assert(note.has_handle_reference('Media', old_handle))
+ note.replace_handle_reference(
+ 'Media', old_handle, new_handle)
+ self.database.commit_note(note, trans)
else:
raise MergeError("Encounter an object of type % s that has "
"a media object reference." % class_name)
diff --git a/gramps/gen/merge/mergenotequery.py b/gramps/gen/merge/mergenotequery.py
index 3261ccb09..86e097ce8 100644
--- a/gramps/gen/merge/mergenotequery.py
+++ b/gramps/gen/merge/mergenotequery.py
@@ -28,7 +28,7 @@ Provide merge capabilities for notes.
#
#-------------------------------------------------------------------------
from ..lib import (Person, Family, Event, Place, Source, Citation, Repository,
- Media)
+ Media, Note)
from ..db import DbTxn
from ..const import GRAMPS_LOCALE as glocale
_ = glocale.translation.sgettext
@@ -99,6 +99,12 @@ class MergeNoteQuery:
assert(repo.has_note_reference(old_handle))
repo.replace_note_references(old_handle, new_handle)
self.database.commit_repository(repo, trans)
+ elif class_name == Note.__name__:
+ note = self.database.get_note_from_handle(handle)
+ assert(note.has_handle_reference('Note', old_handle))
+ note.replace_handle_reference(
+ 'Note', old_handle, new_handle)
+ self.database.commit_note(note, trans)
else:
raise MergeError("Encounter object of type %s that has "
"a note reference." % class_name)
diff --git a/gramps/gen/merge/mergepersonquery.py b/gramps/gen/merge/mergepersonquery.py
index 93f134945..dfb0b8028 100644
--- a/gramps/gen/merge/mergepersonquery.py
+++ b/gramps/gen/merge/mergepersonquery.py
@@ -100,14 +100,16 @@ class MergePersonQuery:
spouse.remove_family_handle(family_handle)
self.database.commit_person(spouse, trans)
# replace the family in lds ordinances
- for (dummy, person_handle) in self.database.find_backlink_handles(
- family_handle, ['Person']):
- if person_handle == old_handle:
+ for (ref_obj, ref_handle) in self.database.find_backlink_handles(
+ family_handle, ['Person', 'Note']):
+ if ref_handle == old_handle:
continue
- person = self.database.get_person_from_handle(person_handle)
- person.replace_handle_reference('Family', family_handle,
- main_family_handle)
- self.database.commit_person(person, trans)
+ obj = self.database.method(
+ "get_%s_from_handle", ref_obj)(ref_handle)
+ assert obj.has_handle_reference('Family', family_handle)
+ obj.replace_handle_reference('Family', family_handle,
+ main_family_handle)
+ self.database.method("commit_%s", ref_obj)(obj, trans)
self.database.remove_family(family_handle, trans)
self.database.commit_family(main_family, trans)
@@ -133,13 +135,15 @@ class MergePersonQuery:
self.phoenix.merge(self.titanic)
self.database.commit_person(self.phoenix, trans)
- for (dummy, person_handle) in self.database.find_backlink_handles(
- old_handle, ['Person']):
- person = self.database.get_person_from_handle(person_handle)
- assert person.has_handle_reference('Person', old_handle)
- person.replace_handle_reference('Person', old_handle, new_handle)
- if person_handle != old_handle:
- self.database.commit_person(person, trans)
+ for (ref_obj, handle) in self.database.find_backlink_handles(
+ old_handle, ['Person', 'Note']):
+ obj = self.database.method(
+ "get_%s_from_handle", ref_obj)(handle)
+ assert obj.has_handle_reference('Person', old_handle)
+ obj.replace_handle_reference(
+ 'Person', old_handle, new_handle)
+ if handle != old_handle:
+ self.database.method("commit_%s", ref_obj)(obj, trans)
for family_handle in self.phoenix.get_parent_family_handle_list():
family = self.database.get_family_from_handle(family_handle)
diff --git a/gramps/gen/merge/mergeplacequery.py b/gramps/gen/merge/mergeplacequery.py
index 95f89677c..c168b58b6 100644
--- a/gramps/gen/merge/mergeplacequery.py
+++ b/gramps/gen/merge/mergeplacequery.py
@@ -28,7 +28,7 @@ Provide merge capabilities for places.
# Gramps modules
#
#-------------------------------------------------------------------------
-from ..lib import Person, Family, Event, Place
+from ..lib import Person, Family, Event, Place, Note
from ..db import DbTxn
from ..const import GRAMPS_LOCALE as glocale
_ = glocale.translation.sgettext
@@ -85,6 +85,12 @@ class MergePlaceQuery:
place.replace_handle_reference('Place', old_handle,
new_handle)
self.database.commit_place(place, trans)
+ elif class_name == Note.__name__:
+ note = self.database.get_note_from_handle(handle)
+ assert(note.has_handle_reference('Place', old_handle))
+ note.replace_handle_reference('Place', old_handle,
+ new_handle)
+ self.database.commit_note(note, trans)
else:
raise MergeError("Encounter an object of type %s that has "
"a place reference." % class_name)
diff --git a/gramps/gen/merge/mergerepositoryquery.py b/gramps/gen/merge/mergerepositoryquery.py
index dc6c5100e..57a6f6b6d 100644
--- a/gramps/gen/merge/mergerepositoryquery.py
+++ b/gramps/gen/merge/mergerepositoryquery.py
@@ -27,7 +27,7 @@ Provide merge capabilities for repositories.
# Gramps modules
#
#-------------------------------------------------------------------------
-from ..lib import Source
+from ..lib import Source, Note
from ..db import DbTxn
from ..const import GRAMPS_LOCALE as glocale
_ = glocale.translation.sgettext
@@ -65,6 +65,12 @@ class MergeRepositoryQuery:
assert source.has_handle_reference('Repository', old_handle)
source.replace_repo_references(old_handle, new_handle)
self.database.commit_source(source, trans)
+ elif class_name == Note.__name__:
+ note = self.database.get_note_from_handle(handle)
+ assert(note.has_handle_reference('Repository', old_handle))
+ note.replace_handle_reference(
+ 'Repository', old_handle, new_handle)
+ self.database.commit_note(note, trans)
else:
raise MergeError("Encounter an object of type %s that has "
"a repository reference." % class_name)
diff --git a/gramps/gen/merge/mergesourcequery.py b/gramps/gen/merge/mergesourcequery.py
index 89d95b604..fb6c4085b 100644
--- a/gramps/gen/merge/mergesourcequery.py
+++ b/gramps/gen/merge/mergesourcequery.py
@@ -29,8 +29,7 @@ Provide merge capabilities for sources.
# Gramps modules
#
#-------------------------------------------------------------------------
-from ..lib import (Person, Family, Event, Place, Source, Repository,
- Media, Citation)
+from ..lib import (Citation, Note)
from ..db import DbTxn
from ..const import GRAMPS_LOCALE as glocale
_ = glocale.translation.sgettext
@@ -68,6 +67,12 @@ class MergeSourceQuery:
assert(citation.get_reference_handle() == old_handle)
citation.set_reference_handle(new_handle)
self.database.commit_citation(citation, trans)
+ elif class_name == Note.__name__:
+ note = self.database.get_note_from_handle(handle)
+ assert(note.has_handle_reference('Source', old_handle))
+ note.replace_handle_reference(
+ 'Source', old_handle, new_handle)
+ self.database.commit_note(note, trans)
else:
raise MergeError("Encounter an object of type %s that has "
"a source reference." % class_name)
diff --git a/gramps/gen/merge/test/merge_ref_test.py b/gramps/gen/merge/test/merge_ref_test.py
index 265c91a1a..590356c66 100644
--- a/gramps/gen/merge/test/merge_ref_test.py
+++ b/gramps/gen/merge/test/merge_ref_test.py
@@ -258,6 +258,7 @@ class PersonCheck(BaseMergeCheck):
Source 1
+
@@ -278,9 +279,40 @@ class PersonCheck(BaseMergeCheck):
+
+
+ New York Public Library
+ Library
+
+
+ Aunt Martha's Attic
+ Collection
+
+
- Note 0
+ Note 0.
+
+
+
+
+
+
+
Note 1
@@ -291,7 +323,8 @@ class PersonCheck(BaseMergeCheck):
encoding='utf-8'))
def test_event_merge(self):
- """Merge two events"""
+ """Merge two events. Also checks that Event link in note is updated.
+ """
expect = ET.fromstring(self.basedoc, parser=self.parser)
eventref = expect.xpath("//g:person[@handle='_i0001']/g:eventref",
namespaces={"g": NS_G})[0]
@@ -299,6 +332,8 @@ class PersonCheck(BaseMergeCheck):
event = expect.xpath("//g:event[@handle='_e0001']",
namespaces={"g": NS_G})[0]
event.getparent().remove(event)
+ notetag = expect.xpath("//g:style", namespaces={"g": NS_G})[1]
+ notetag.attrib['value'] = "gramps://Event/handle/e0000"
self.do_case('E0000', 'E0001', self.basedoc, expect)
#print(str(ET.tostring(expect, pretty_print=True), 'utf-8'))
@@ -313,6 +348,8 @@ class PersonCheck(BaseMergeCheck):
placeobj.getparent().remove(placeobj)
placeobj = expect.xpath("//g:placeobj[@handle='_p0000']",
namespaces={"g": NS_G})[0]
+ notetag = expect.xpath("//g:style", namespaces={"g": NS_G})[4]
+ notetag.attrib['value'] = "gramps://Place/handle/p0000"
ET.SubElement(placeobj, NSP + 'pname', value='Place 1')
self.do_case('P0000', 'P0001', self.basedoc, expect)
@@ -325,6 +362,8 @@ class PersonCheck(BaseMergeCheck):
citation = expect.xpath("//g:citation[@handle='_c0001']",
namespaces={"g": NS_G})[0]
citation.getparent().remove(citation)
+ notetag = expect.xpath("//g:style", namespaces={"g": NS_G})[0]
+ notetag.attrib['value'] = "gramps://Citation/handle/c0000"
self.do_case('C0000', 'C0001', self.basedoc, expect)
def test_media_merge(self):
@@ -336,6 +375,8 @@ class PersonCheck(BaseMergeCheck):
object_ = expect.xpath("//g:object[@handle='_o0001']",
namespaces={"g": NS_G})[0]
object_.getparent().remove(object_)
+ notetag = expect.xpath("//g:style", namespaces={"g": NS_G})[2]
+ notetag.attrib['value'] = "gramps://Media/handle/o0000"
self.do_case('O0000', 'O0001', self.basedoc, expect)
def test_note_merge(self):
@@ -346,9 +387,24 @@ class PersonCheck(BaseMergeCheck):
noteref.attrib['hlink'] = '_n0000'
note = expect.xpath("//g:note[@handle='_n0001']",
namespaces={"g": NS_G})[0]
+ notetag = expect.xpath("//g:style", namespaces={"g": NS_G})[3]
+ notetag.attrib['value'] = "gramps://Note/handle/n0000"
note.getparent().remove(note)
self.do_case('N0000', 'N0001', self.basedoc, expect)
+ def test_repository_merge(self):
+ """Merge two repository objects"""
+ expect = ET.fromstring(self.basedoc, parser=self.parser)
+ reporef = expect.xpath("//g:source[@handle='_s0001']/g:reporef",
+ namespaces={"g": NS_G})[0]
+ reporef.attrib['hlink'] = '_r0000'
+ object_ = expect.xpath("//g:repository[@handle='_r0001']",
+ namespaces={"g": NS_G})[0]
+ object_.getparent().remove(object_)
+ notetag = expect.xpath("//g:style", namespaces={"g": NS_G})[5]
+ notetag.attrib['value'] = "gramps://Repository/handle/r0000"
+ self.do_case('R0000', 'R0001', self.basedoc, expect)
+
#-------------------------------------------------------------------------
#
@@ -1226,6 +1282,14 @@ class PersonPersonCheck(BaseMergeCheck):
+
+
+ Note 0.
+
+
+
"""
self.basedoc = bytes(bytearray(self.base_str + base_str,
encoding='utf-8'))
@@ -1256,6 +1320,8 @@ class PersonPersonCheck(BaseMergeCheck):
person = expect.xpath("//g:person[@handle='_i0001']",
namespaces={"g": NS_G})[0]
person.getparent().remove(person)
+ notetag = expect.xpath("//g:style", namespaces={"g": NS_G})[0]
+ notetag.attrib['value'] = "gramps://Person/handle/i0000"
input_doc = ET.tostring(input_ctxt)
self.do_case('I0000', 'I0001', input_doc, expect)
@@ -1286,6 +1352,8 @@ class PersonPersonCheck(BaseMergeCheck):
person = expect.xpath("//g:person[@handle='_i0001']",
namespaces={"g": NS_G})[0]
person.getparent().remove(person)
+ notetag = expect.xpath("//g:style", namespaces={"g": NS_G})[0]
+ notetag.attrib['value'] = "gramps://Person/handle/i0000"
input_doc = ET.tostring(input_ctxt)
self.do_case('I0000', 'I0001', input_doc, expect)
@@ -1309,6 +1377,8 @@ class PersonPersonCheck(BaseMergeCheck):
person = expect.xpath("//g:person[@handle='_i0001']",
namespaces={"g": NS_G})[0]
person.getparent().remove(person)
+ notetag = expect.xpath("//g:style", namespaces={"g": NS_G})[0]
+ notetag.attrib['value'] = "gramps://Person/handle/i0000"
input_doc = ET.tostring(input_ctxt)
self.do_case('I0000', 'I0001', input_doc, expect)
@@ -1422,6 +1492,26 @@ class FamilyPersonCheck(BaseMergeCheck):
+
+
+ Note 0
+
+
+
+ Note 1
+
+
+
+ Note 0
+
+
+
"""
self.basedoc = bytes(bytearray(self.base_str + base_str,
encoding='utf-8'))
@@ -1439,6 +1529,9 @@ class FamilyPersonCheck(BaseMergeCheck):
parentref = expect.xpath("//g:person[@handle='_i0000']/g:parentin",
namespaces={"g": NS_G})[0]
attr.addnext(parentref) # restore order of elements
+ notetag = expect.xpath("//g:note[@handle='_n0000']/g:style",
+ namespaces={"g": NS_G})[0]
+ notetag.attrib['value'] = "gramps://Person/handle/i0000"
persons[2].getparent().remove(persons[2])
self.do_case('I0000', 'I0002', self.basedoc, expect)
@@ -1467,6 +1560,9 @@ class FamilyPersonCheck(BaseMergeCheck):
father = expect.xpath("//g:family[@handle='_f0001']/g:father",
namespaces={"g": NS_G})[0]
father.attrib['hlink'] = '_i0000'
+ notetag = expect.xpath("//g:note[@handle='_n0000']/g:style",
+ namespaces={"g": NS_G})[0]
+ notetag.attrib['value'] = "gramps://Person/handle/i0000"
persons[2].getparent().remove(persons[2])
input_doc = ET.tostring(input_ctxt)
self.do_case('I0000', 'I0002', input_doc, expect)
@@ -1501,6 +1597,9 @@ class FamilyPersonCheck(BaseMergeCheck):
ET.SubElement(family, NSP + 'rel', type='Married')
ET.SubElement(family, NSP + 'father', hlink='_i0000')
ET.SubElement(family, NSP + 'mother', hlink='_i0003')
+ notetag = expect.xpath("//g:note[@handle='_n0000']/g:style",
+ namespaces={"g": NS_G})[0]
+ notetag.attrib['value'] = "gramps://Person/handle/i0000"
persons[2].getparent().remove(persons[2])
persons = input_ctxt.xpath("//g:person",
@@ -1547,7 +1646,13 @@ class FamilyPersonCheck(BaseMergeCheck):
parentref = expect.xpath("//g:person[@handle='_i0000']/g:parentin",
namespaces={"g": NS_G})[0]
attr.addnext(parentref) # restore order of elements
+ notetag = expect.xpath("//g:note[@handle='_n0000']/g:style",
+ namespaces={"g": NS_G})[0]
+ notetag.attrib['value'] = "gramps://Person/handle/i0000"
persons[2].getparent().remove(persons[2])
+ notetag = expect.xpath("//g:note[@handle='_n0001']/g:style",
+ namespaces={"g": NS_G})[0]
+ notetag.attrib['value'] = "gramps://Family/handle/f0000"
family = expect.xpath("//g:family[@handle='_f0001']",
namespaces={"g": NS_G})[0]
family.getparent().remove(family)
@@ -1583,6 +1688,12 @@ class FamilyPersonCheck(BaseMergeCheck):
parentref = expect.xpath("//g:person[@handle='_i0000']/g:parentin",
namespaces={"g": NS_G})[0]
attr.addnext(parentref) # restore order of elements
+ notetag = expect.xpath("//g:note[@handle='_n0000']/g:style",
+ namespaces={"g": NS_G})[0]
+ notetag.attrib['value'] = "gramps://Person/handle/i0000"
+ notetag = expect.xpath("//g:note[@handle='_n0001']/g:style",
+ namespaces={"g": NS_G})[0]
+ notetag.attrib['value'] = "gramps://Family/handle/f0000"
persons[2].getparent().remove(persons[2])
family = expect.xpath("//g:family[@handle='_f0001']",
namespaces={"g": NS_G})[0]
@@ -1615,6 +1726,12 @@ class FamilyPersonCheck(BaseMergeCheck):
namespaces={"g": NS_G})[0]
attr.addnext(parentref) # restore order of elements
persons[3].getparent().remove(persons[3])
+ notetag = expect.xpath("//g:note[@handle='_n0001']/g:style",
+ namespaces={"g": NS_G})[0]
+ notetag.attrib['value'] = "gramps://Family/handle/f0000"
+ notetag = expect.xpath("//g:note[@handle='_n0003']/g:style",
+ namespaces={"g": NS_G})[0]
+ notetag.attrib['value'] = "gramps://Person/handle/i0001"
family = expect.xpath("//g:family[@handle='_f0001']",
namespaces={"g": NS_G})[0]
family.getparent().remove(family)
@@ -1655,7 +1772,13 @@ class FamilyPersonCheck(BaseMergeCheck):
parentref = expect.xpath("//g:person[@handle='_i0000']/g:parentin",
namespaces={"g": NS_G})[0]
attr.addnext(parentref) # restore order of elements
+ notetag = expect.xpath("//g:note[@handle='_n0000']/g:style",
+ namespaces={"g": NS_G})[0]
+ notetag.attrib['value'] = "gramps://Person/handle/i0000"
persons[2].getparent().remove(persons[2])
+ notetag = expect.xpath("//g:note[@handle='_n0001']/g:style",
+ namespaces={"g": NS_G})[0]
+ notetag.attrib['value'] = "gramps://Family/handle/f0000"
families = expect.xpath("//g:family",
namespaces={"g": NS_G})
families[1].getparent().remove(families[1])
@@ -1695,7 +1818,13 @@ class FamilyPersonCheck(BaseMergeCheck):
parentref = expect.xpath("//g:person[@handle='_i0000']/g:parentin",
namespaces={"g": NS_G})[0]
attr.addnext(parentref) # restore order of elements
+ notetag = expect.xpath("//g:note[@handle='_n0000']/g:style",
+ namespaces={"g": NS_G})[0]
+ notetag.attrib['value'] = "gramps://Person/handle/i0000"
persons[2].getparent().remove(persons[2])
+ notetag = expect.xpath("//g:note[@handle='_n0001']/g:style",
+ namespaces={"g": NS_G})[0]
+ notetag.attrib['value'] = "gramps://Family/handle/f0000"
families = expect.xpath("//g:family",
namespaces={"g": NS_G})
families[1].getparent().remove(families[1])
@@ -1733,7 +1862,13 @@ class FamilyPersonCheck(BaseMergeCheck):
parentref = expect.xpath("//g:person[@handle='_i0000']/g:parentin",
namespaces={"g": NS_G})[0]
attr.addnext(parentref) # restore order of elements
+ notetag = expect.xpath("//g:note[@handle='_n0000']/g:style",
+ namespaces={"g": NS_G})[0]
+ notetag.attrib['value'] = "gramps://Person/handle/i0000"
persons[2].getparent().remove(persons[2])
+ notetag = expect.xpath("//g:note[@handle='_n0001']/g:style",
+ namespaces={"g": NS_G})[0]
+ notetag.attrib['value'] = "gramps://Family/handle/f0000"
families = expect.xpath("//g:family",
namespaces={"g": NS_G})
families[1].getparent().remove(families[1])
@@ -1776,7 +1911,13 @@ class FamilyPersonCheck(BaseMergeCheck):
parentref = expect.xpath("//g:person[@handle='_i0000']/g:parentin",
namespaces={"g": NS_G})[0]
attr.addnext(parentref) # restore order of elements
+ notetag = expect.xpath("//g:note[@handle='_n0000']/g:style",
+ namespaces={"g": NS_G})[0]
+ notetag.attrib['value'] = "gramps://Person/handle/i0000"
persons[2].getparent().remove(persons[2])
+ notetag = expect.xpath("//g:note[@handle='_n0001']/g:style",
+ namespaces={"g": NS_G})[0]
+ notetag.attrib['value'] = "gramps://Family/handle/f0000"
families = expect.xpath("//g:family",
namespaces={"g": NS_G})
families[1].getparent().remove(families[1])
@@ -1817,7 +1958,13 @@ class FamilyPersonCheck(BaseMergeCheck):
parentref = expect.xpath("//g:person[@handle='_i0000']/g:parentin",
namespaces={"g": NS_G})[0]
attr.addnext(parentref) # restore order of elements
+ notetag = expect.xpath("//g:note[@handle='_n0000']/g:style",
+ namespaces={"g": NS_G})[0]
+ notetag.attrib['value'] = "gramps://Person/handle/i0000"
persons[2].getparent().remove(persons[2])
+ notetag = expect.xpath("//g:note[@handle='_n0001']/g:style",
+ namespaces={"g": NS_G})[0]
+ notetag.attrib['value'] = "gramps://Family/handle/f0000"
families = expect.xpath("//g:family",
namespaces={"g": NS_G})
families[1].getparent().remove(families[1])
@@ -1879,6 +2026,14 @@ class FamilyMergeCheck(BaseMergeCheck):
+
+
+ Note 0.
+
+
+
"""
self.basedoc = bytes(bytearray(self.base_str + base_str,
encoding='utf-8'))
@@ -1974,6 +2129,8 @@ class FamilyMergeCheck(BaseMergeCheck):
family = expect.xpath("//g:family[@handle='_f0001']",
namespaces={"g": NS_G})[0]
family.getparent().remove(family)
+ notetag = expect.xpath("//g:style", namespaces={"g": NS_G})[0]
+ notetag.attrib['value'] = "gramps://Family/handle/f0000"
self.do_family_case('F0000', 'F0001', 'i0000', 'i0001',
self.basedoc, expect)
@@ -2004,6 +2161,8 @@ class FamilyMergeCheck(BaseMergeCheck):
family = expect.xpath("//g:family[@handle='_f0001']",
namespaces={"g": NS_G})[0]
family.getparent().remove(family)
+ notetag = expect.xpath("//g:style", namespaces={"g": NS_G})[0]
+ notetag.attrib['value'] = "gramps://Family/handle/f0000"
father = expect.xpath("//g:family[@handle='_f0000']/g:father",
namespaces={"g": NS_G})[0]
father.attrib['hlink'] = '_i0002'
@@ -2036,6 +2195,8 @@ class FamilyMergeCheck(BaseMergeCheck):
family = expect.xpath("//g:family[@handle='_f0001']",
namespaces={"g": NS_G})[0]
family.getparent().remove(family)
+ notetag = expect.xpath("//g:style", namespaces={"g": NS_G})[0]
+ notetag.attrib['value'] = "gramps://Family/handle/f0000"
input_doc = ET.tostring(input_ctxt)
self.do_family_case('F0000', 'F0001', 'i0000', 'i0001',
input_doc, expect)
@@ -2068,6 +2229,8 @@ class FamilyMergeCheck(BaseMergeCheck):
family = expect.xpath("//g:family[@handle='_f0001']",
namespaces={"g": NS_G})[0]
family.getparent().remove(family)
+ notetag = expect.xpath("//g:style", namespaces={"g": NS_G})[0]
+ notetag.attrib['value'] = "gramps://Family/handle/f0000"
family = expect.xpath("//g:family[@handle='_f0000']",
namespaces={"g": NS_G})[0]
mother = ET.SubElement(family, NSP + 'mother', hlink='_i0003')
@@ -2115,6 +2278,8 @@ class FamilyMergeCheck(BaseMergeCheck):
family = expect.xpath("//g:family[@handle='_f0001']",
namespaces={"g": NS_G})[0]
family.getparent().remove(family)
+ notetag = expect.xpath("//g:style", namespaces={"g": NS_G})[0]
+ notetag.attrib['value'] = "gramps://Family/handle/f0000"
childof = expect.xpath("//g:person[@handle='_i0004']/g:childof",
namespaces={"g": NS_G})[0]
childof.attrib['hlink'] = '_f0000'
@@ -2167,6 +2332,8 @@ class FamilyMergeCheck(BaseMergeCheck):
family = expect.xpath("//g:family[@handle='_f0001']",
namespaces={"g": NS_G})[0]
family.getparent().remove(family)
+ notetag = expect.xpath("//g:style", namespaces={"g": NS_G})[0]
+ notetag.attrib['value'] = "gramps://Family/handle/f0000"
childof = expect.xpath("//g:person[@handle='_i0004']/g:childof",
namespaces={"g": NS_G})[1]
childof.getparent().remove(childof)
@@ -2214,6 +2381,8 @@ class FamilyMergeCheck(BaseMergeCheck):
family = expect.xpath("//g:family[@handle='_f0001']",
namespaces={"g": NS_G})[0]
family.getparent().remove(family)
+ notetag = expect.xpath("//g:style", namespaces={"g": NS_G})[0]
+ notetag.attrib['value'] = "gramps://Family/handle/f0000"
sealedto = expect.xpath("//g:sealed_to",
namespaces={"g": NS_G})[0]
sealedto.attrib['hlink'] = '_f0000'
diff --git a/gramps/gui/dialog.py b/gramps/gui/dialog.py
index 638e11245..e14145a8b 100644
--- a/gramps/gui/dialog.py
+++ b/gramps/gui/dialog.py
@@ -437,11 +437,29 @@ class MissingMediaDialog:
parent=self.top)
return True
+
class MultiSelectDialog:
+ """
+ Allows a Yes, No, Cancel dialog that includes a checkbox
+ with "Use this answer for the rest of the items" that works with 'Yes'
+ """
def __init__(self, msg1_func, msg2_func, items, lookup,
cancel_func=None, no_func=None, yes_func=None,
multi_yes_func=None, parent=None):
"""
+ msg1_func a function to display big bold text at top
+ msg2_func a function to display normal text at center of dialog
+ items a list of objects (often handles) passed to lookup
+ Must be a list, not a generator
+ lookup A function that looks up an object to pass to yes/no/multi_func
+ cancel_func A function or None called on cancel.
+ yes_func A function or None called on Yes.
+ no_func A function or None called on No.
+ multi_yes_func A function or None called on Yes with
+ "use this answer for rest" checked.
+ The above xxx_func calls will have parameters:
+ object from lookup function
+ "parent=" set to this dialog for transient parent purposes.
"""
self.xml = Glade(toplevel='multiselectdialog')
@@ -468,7 +486,7 @@ class MultiSelectDialog:
self.top.connect('delete_event', self.warn)
default_action = 0
- for selected in items:
+ for indx, selected in enumerate(items):
item = self.lookup(selected)
if default_action == 0:
msg1 = self.msg1_func(item)
@@ -491,22 +509,22 @@ class MultiSelectDialog:
if check_button.get_active():
# run the multiple yes if 'do remainder' is checked
if multi_yes_func and response == 3:
- multi_yes_func(items)
+ multi_yes_func(items[indx:], parent=self.top)
break
default_action = response
else:
response = default_action
- ### Now do it
- if response == 1: # Cancel
+ # Now do it
+ if response == 1: # Cancel
if self.cancel_func:
- self.cancel_func(item)
+ self.cancel_func(item, parent=self.top)
break
- elif response == 2: # No
+ elif response == 2: # No
if self.no_func:
- self.no_func(item)
- elif response == 3: # Yes
+ self.no_func(item, parent=self.top)
+ elif response == 3: # Yes
if self.yes_func:
- self.yes_func(item)
+ self.yes_func(item, parent=self.top)
self.top.destroy()
if parent and parent_modal:
parent.set_modal(True)
diff --git a/gramps/gui/editors/__init__.py b/gramps/gui/editors/__init__.py
index f1f4ec5ca..fba6db615 100644
--- a/gramps/gui/editors/__init__.py
+++ b/gramps/gui/editors/__init__.py
@@ -23,25 +23,25 @@
from .editaddress import EditAddress
from .editattribute import EditAttribute, EditSrcAttribute
from .editchildref import EditChildRef
-from .editcitation import EditCitation, DeleteCitationQuery
+from .editcitation import EditCitation
from .editdate import EditDate
-from .editevent import EditEvent, DeleteEventQuery
+from .editevent import EditEvent
from .editeventref import EditEventRef
from .editfamily import EditFamily
from .editldsord import EditLdsOrd, EditFamilyLdsOrd
from .editlocation import EditLocation
-from .editmedia import EditMedia, DeleteMediaQuery
+from .editmedia import EditMedia
from .editmediaref import EditMediaRef
from .editname import EditName
-from .editnote import EditNote, DeleteNoteQuery
+from .editnote import EditNote
from .editperson import EditPerson
from .editpersonref import EditPersonRef
-from .editplace import EditPlace, DeletePlaceQuery
+from .editplace import EditPlace
from .editplacename import EditPlaceName
from .editplaceref import EditPlaceRef
-from .editrepository import EditRepository, DeleteRepositoryQuery
+from .editrepository import EditRepository
from .editreporef import EditRepoRef
-from .editsource import EditSource, DeleteSrcQuery
+from .editsource import EditSource
from .edittaglist import EditTagList
from .editurl import EditUrl
from .editlink import EditLink
diff --git a/gramps/gui/editors/displaytabs/backrefmodel.py b/gramps/gui/editors/displaytabs/backrefmodel.py
index a8044493f..a3a922497 100644
--- a/gramps/gui/editors/displaytabs/backrefmodel.py
+++ b/gramps/gui/editors/displaytabs/backrefmodel.py
@@ -134,6 +134,15 @@ class BackRefModel(Gtk.ListStore):
name = p.get_name()
gid = p.gramps_id
handle = p.handle
+ elif dtype == 'Note':
+ p = self.db.get_note_from_handle(ref[1])
+ if not p:
+ continue
+ name = " ".join(p.get().split())
+ if len(name) > 80:
+ name = name[:80] + "..."
+ gid = p.gramps_id
+ handle = p.handle
else:
p = self.db.get_media_from_handle(ref[1])
if not p:
diff --git a/gramps/gui/editors/editcitation.py b/gramps/gui/editors/editcitation.py
index d691e1819..11e0b2341 100644
--- a/gramps/gui/editors/editcitation.py
+++ b/gramps/gui/editors/editcitation.py
@@ -364,58 +364,3 @@ class EditCitation(EditPrimary):
else:
cmp_obj = self.empty_object()
return cmp_obj.serialize(True)[1:] != self.obj.serialize()[1:]
-
-class DeleteCitationQuery:
- def __init__(self, dbstate, uistate, citation, the_lists):
- self.citation = citation
- self.db = dbstate.db
- self.uistate = uistate
- self.the_lists = the_lists
-
- def query_response(self):
- with DbTxn(_("Delete Citation (%s)") % self.citation.get_page(),
- self.db) as trans:
- self.db.disable_signals()
-
- (person_list, family_list, event_list, place_list, source_list,
- media_list, repo_list) = self.the_lists
-
- ctn_handle_list = [self.citation.get_handle()]
-
- for handle in person_list:
- person = self.db.get_person_from_handle(handle)
- person.remove_citation_references(ctn_handle_list)
- self.db.commit_person(person, trans)
-
- for handle in family_list:
- family = self.db.get_family_from_handle(handle)
- family.remove_citation_references(ctn_handle_list)
- self.db.commit_family(family, trans)
-
- for handle in event_list:
- event = self.db.get_event_from_handle(handle)
- event.remove_citation_references(ctn_handle_list)
- self.db.commit_event(event, trans)
-
- for handle in place_list:
- place = self.db.get_place_from_handle(handle)
- place.remove_citation_references(ctn_handle_list)
- self.db.commit_place(place, trans)
-
- for handle in source_list:
- source = self.db.get_source_from_handle(handle)
- source.remove_citation_references(ctn_handle_list)
- self.db.commit_source(source, trans)
-
- for handle in media_list:
- media = self.db.get_media_from_handle(handle)
- media.remove_citation_references(ctn_handle_list)
- self.db.commit_media(media, trans)
-
- for handle in repo_list:
- repo = self.db.get_repository_from_handle(handle)
- repo.remove_citation_references(ctn_handle_list)
- self.db.commit_repository(repo, trans)
-
- self.db.enable_signals()
- self.db.remove_citation(self.citation.get_handle(), trans)
diff --git a/gramps/gui/editors/editevent.py b/gramps/gui/editors/editevent.py
index fa0a5eb93..01e0186d2 100644
--- a/gramps/gui/editors/editevent.py
+++ b/gramps/gui/editors/editevent.py
@@ -319,37 +319,3 @@ class EditEvent(EditPrimary):
self.top.get_object("place").set_markup(
self.place_field.EMPTY_TEXT)
self.place_field.set_button(False)
-
-
-#-------------------------------------------------------------------------
-#
-# Delete Query class
-#
-#-------------------------------------------------------------------------
-class DeleteEventQuery:
- def __init__(self, dbstate, uistate, event, person_list, family_list):
- self.event = event
- self.db = dbstate.db
- self.uistate = uistate
- self.person_list = person_list
- self.family_list = family_list
-
- def query_response(self):
- with DbTxn(_("Delete Event (%s)") % self.event.get_gramps_id(),
- self.db) as trans:
- self.db.disable_signals()
-
- ev_handle_list = [self.event.get_handle()]
-
- for handle in self.person_list:
- person = self.db.get_person_from_handle(handle)
- person.remove_handle_references('Event', ev_handle_list)
- self.db.commit_person(person, trans)
-
- for handle in self.family_list:
- family = self.db.get_family_from_handle(handle)
- family.remove_handle_references('Event', ev_handle_list)
- self.db.commit_family(family, trans)
-
- self.db.enable_signals()
- self.db.remove_event(self.event.get_handle(), trans)
diff --git a/gramps/gui/editors/editmedia.py b/gramps/gui/editors/editmedia.py
index 933fd3213..f874f699f 100644
--- a/gramps/gui/editors/editmedia.py
+++ b/gramps/gui/editors/editmedia.py
@@ -359,63 +359,3 @@ class EditMedia(EditPrimary):
else:
cmp_obj = self.empty_object()
return cmp_obj.serialize(True)[1:] != self.obj.serialize()[1:]
-
-class DeleteMediaQuery:
-
- def __init__(self, dbstate, uistate, media_handle, the_lists):
- self.db = dbstate.db
- self.uistate = uistate
- self.media_handle = media_handle
- self.the_lists = the_lists
-
- def query_response(self):
- with DbTxn(_("Remove Media Object"), self.db) as trans:
- self.db.disable_signals()
-
- (person_list, family_list, event_list,
- place_list, source_list, citation_list) = self.the_lists
-
- for handle in person_list:
- person = self.db.get_person_from_handle(handle)
- new_list = [photo for photo in person.get_media_list()
- if photo.get_reference_handle() != self.media_handle]
- person.set_media_list(new_list)
- self.db.commit_person(person, trans)
-
- for handle in family_list:
- family = self.db.get_family_from_handle(handle)
- new_list = [photo for photo in family.get_media_list()
- if photo.get_reference_handle() != self.media_handle]
- family.set_media_list(new_list)
- self.db.commit_family(family, trans)
-
- for handle in event_list:
- event = self.db.get_event_from_handle(handle)
- new_list = [photo for photo in event.get_media_list()
- if photo.get_reference_handle() != self.media_handle]
- event.set_media_list(new_list)
- self.db.commit_event(event, trans)
-
- for handle in place_list:
- place = self.db.get_place_from_handle(handle)
- new_list = [photo for photo in place.get_media_list()
- if photo.get_reference_handle() != self.media_handle]
- place.set_media_list(new_list)
- self.db.commit_place(place, trans)
-
- for handle in source_list:
- source = self.db.get_source_from_handle(handle)
- new_list = [photo for photo in source.get_media_list()
- if photo.get_reference_handle() != self.media_handle]
- source.set_media_list(new_list)
- self.db.commit_source(source, trans)
-
- for handle in citation_list:
- citation = self.db.get_citation_from_handle(handle)
- new_list = [photo for photo in citation.get_media_list()
- if photo.get_reference_handle() != self.media_handle]
- citation.set_media_list(new_list)
- self.db.commit_citation(citation, trans)
-
- self.db.enable_signals()
- self.db.remove_media(self.media_handle, trans)
diff --git a/gramps/gui/editors/editnote.py b/gramps/gui/editors/editnote.py
index 81c5a208b..104771388 100644
--- a/gramps/gui/editors/editnote.py
+++ b/gramps/gui/editors/editnote.py
@@ -349,71 +349,3 @@ class EditNote(EditPrimary):
self._do_close()
if self.callback:
self.callback(self.obj.get_handle())
-
-class DeleteNoteQuery:
- def __init__(self, dbstate, uistate, note, the_lists):
- self.note = note
- self.db = dbstate.db
- self.uistate = uistate
- self.the_lists = the_lists
-
- def query_response(self):
- with DbTxn(_("Delete Note (%s)") % self.note.get_gramps_id(),
- self.db) as trans:
- self.db.disable_signals()
-
- (person_list, family_list, event_list, place_list, source_list,
- citation_list, media_list, repo_list) = self.the_lists
-
- note_handle = self.note.get_handle()
-
- for handle in person_list:
- person = self.db.get_person_from_handle(handle)
- if person:
- person.remove_note(note_handle)
- self.db.commit_person(person, trans)
-
- for handle in family_list:
- family = self.db.get_family_from_handle(handle)
- if family:
- family.remove_note(note_handle)
- self.db.commit_family(family, trans)
-
- for handle in event_list:
- event = self.db.get_event_from_handle(handle)
- if event:
- event.remove_note(note_handle)
- self.db.commit_event(event, trans)
-
- for handle in place_list:
- place = self.db.get_place_from_handle(handle)
- if place:
- place.remove_note(note_handle)
- self.db.commit_place(place, trans)
-
- for handle in source_list:
- source = self.db.get_source_from_handle(handle)
- if source:
- source.remove_note(note_handle)
- self.db.commit_source(source, trans)
-
- for handle in citation_list:
- citation = self.db.get_citation_from_handle(handle)
- if citation:
- citation.remove_note(note_handle)
- self.db.commit_citation(citation, trans)
-
- for handle in media_list:
- media = self.db.get_media_from_handle(handle)
- if media:
- media.remove_note(note_handle)
- self.db.commit_media(media, trans)
-
- for handle in repo_list:
- repo = self.db.get_repository_from_handle(handle)
- if repo:
- repo.remove_note(note_handle)
- self.db.commit_repository(repo, trans)
-
- self.db.enable_signals()
- self.db.remove_note(note_handle, trans)
diff --git a/gramps/gui/editors/editplace.py b/gramps/gui/editors/editplace.py
index d141bca62..8c685748c 100644
--- a/gramps/gui/editors/editplace.py
+++ b/gramps/gui/editors/editplace.py
@@ -379,44 +379,3 @@ class EditPlace(EditPrimary):
self._do_close()
if self.callback:
self.callback(self.obj)
-
-#-------------------------------------------------------------------------
-#
-# DeletePlaceQuery
-#
-#-------------------------------------------------------------------------
-class DeletePlaceQuery:
-
- def __init__(self, dbstate, uistate, place, person_list, family_list,
- event_list):
- self.db = dbstate.db
- self.uistate = uistate
- self.obj = place
- self.person_list = person_list
- self.family_list = family_list
- self.event_list = event_list
-
- def query_response(self):
- place_title = place_displayer.display(self.db, self.obj)
- with DbTxn(_("Delete Place (%s)") % place_title, self.db) as trans:
- self.db.disable_signals()
-
- place_handle = self.obj.get_handle()
-
- for handle in self.person_list:
- person = self.db.get_person_from_handle(handle)
- person.remove_handle_references('Place', place_handle)
- self.db.commit_person(person, trans)
-
- for handle in self.family_list:
- family = self.db.get_family_from_handle(handle)
- family.remove_handle_references('Place', place_handle)
- self.db.commit_family(family, trans)
-
- for handle in self.event_list:
- event = self.db.get_event_from_handle(handle)
- event.remove_handle_references('Place', place_handle)
- self.db.commit_event(event, trans)
-
- self.db.enable_signals()
- self.db.remove_place(place_handle, trans)
diff --git a/gramps/gui/editors/editrepository.py b/gramps/gui/editors/editrepository.py
index f95fccafa..1c8268063 100644
--- a/gramps/gui/editors/editrepository.py
+++ b/gramps/gui/editors/editrepository.py
@@ -210,23 +210,3 @@ class EditRepository(EditPrimary):
self._do_close()
if self.callback:
self.callback(self.obj)
-
-class DeleteRepositoryQuery:
- def __init__(self, dbstate, uistate, repository, sources):
- self.obj = repository
- self.db = dbstate.db
- self.uistate = uistate
- self.sources = sources
-
- def query_response(self):
- with DbTxn(_("Delete Repository (%s)") % self.obj.get_name(),
- self.db) as trans:
-
- repos_handle_list = [self.obj.get_handle()]
-
- for handle in self.sources:
- source = self.db.get_source_from_handle(handle)
- source.remove_repo_references(repos_handle_list)
- self.db.commit_source(source, trans)
-
- self.db.remove_repository(self.obj.get_handle(), trans)
diff --git a/gramps/gui/editors/editsource.py b/gramps/gui/editors/editsource.py
index 9c14af25d..86e57aabe 100644
--- a/gramps/gui/editors/editsource.py
+++ b/gramps/gui/editors/editsource.py
@@ -231,81 +231,3 @@ class EditSource(EditPrimary):
self._do_close()
if self.callback:
self.callback(self.obj)
-
-class DeleteSrcQuery:
- def __init__(self, dbstate, uistate, source, the_lists):
- self.source = source
- self.db = dbstate.db
- self.uistate = uistate
- self.the_lists = the_lists
-
- def query_response(self):
- with DbTxn(_("Delete Source (%s)") % self.source.get_title(),
- self.db) as trans:
- self.db.disable_signals()
-
- # we can have:
- # object(CitationBase) -> Citation(source_handle) -> Source
- # We first have to remove the CitationBase references to the
- # Citation. Then we remove the Citations. (We don't need to
- # remove the source_handle references to the Source, because we are
- # removing the whole Citation). Then we can remove the Source
-
- (citation_list, citation_referents_list) = self.the_lists
- # citation_list is a tuple of lists. Only the first, for Citations,
- # exists.
- citation_list = citation_list[0]
-
- # (1) delete the references to the citation
- for (citation_handle, refs) in citation_referents_list:
- LOG.debug('delete citation %s references %s' %
- (citation_handle, refs))
- (person_list, family_list, event_list, place_list, source_list,
- media_list, repo_list) = refs
-
- ctn_handle_list = [citation_handle]
-
- for handle in person_list:
- person = self.db.get_person_from_handle(handle)
- person.remove_citation_references(ctn_handle_list)
- self.db.commit_person(person, trans)
-
- for handle in family_list:
- family = self.db.get_family_from_handle(handle)
- family.remove_citation_references(ctn_handle_list)
- self.db.commit_family(family, trans)
-
- for handle in event_list:
- event = self.db.get_event_from_handle(handle)
- event.remove_citation_references(ctn_handle_list)
- self.db.commit_event(event, trans)
-
- for handle in place_list:
- place = self.db.get_place_from_handle(handle)
- place.remove_citation_references(ctn_handle_list)
- self.db.commit_place(place, trans)
-
- for handle in source_list:
- source = self.db.get_source_from_handle(handle)
- source.remove_citation_references(ctn_handle_list)
- self.db.commit_source(source, trans)
-
- for handle in media_list:
- media = self.db.get_media_from_handle(handle)
- media.remove_citation_references(ctn_handle_list)
- self.db.commit_media(media, trans)
-
- for handle in repo_list:
- repo = self.db.get_repository_from_handle(handle)
- repo.remove_citation_references(ctn_handle_list)
- self.db.commit_repository(repo, trans)
-
- # (2) delete the actual citations
- LOG.debug('remove the actual citations %s' % citation_list)
- for citation_handle in citation_list:
- LOG.debug("remove_citation %s" % citation_handle)
- self.db.remove_citation(citation_handle, trans)
-
- # (3) delete the source
- self.db.enable_signals()
- self.db.remove_source(self.source.get_handle(), trans)
diff --git a/gramps/gui/utils.py b/gramps/gui/utils.py
index d6346fb9d..ba5315924 100644
--- a/gramps/gui/utils.py
+++ b/gramps/gui/utils.py
@@ -569,12 +569,14 @@ def get_link_color(context):
return rgb_to_hex((col.red, col.green, col.blue))
+
def edit_object(dbstate, uistate, reftype, ref):
"""
Invokes the appropriate editor for an object type and given handle.
"""
from .editors import (EditEvent, EditPerson, EditFamily, EditSource,
- EditPlace, EditMedia, EditRepository, EditCitation)
+ EditPlace, EditMedia, EditRepository, EditCitation,
+ EditNote)
if reftype == 'Person':
try:
@@ -642,6 +644,13 @@ def edit_object(dbstate, uistate, reftype, ref):
EditRepository(dbstate, uistate, [], repo)
except WindowActiveError:
pass
+ elif reftype == 'Note':
+ try:
+ note = dbstate.db.get_note_from_handle(ref)
+ EditNote(dbstate, uistate, [], note)
+ except WindowActiveError:
+ pass
+
#-------------------------------------------------------------------------
#
diff --git a/gramps/gui/views/listview.py b/gramps/gui/views/listview.py
index b44804f02..7ea0dacda 100644
--- a/gramps/gui/views/listview.py
+++ b/gramps/gui/views/listview.py
@@ -59,6 +59,7 @@ from .navigationview import NavigationView
from ..uimanager import ActionGroup
from ..columnorder import ColumnOrder
from gramps.gen.config import config
+from gramps.gen.db import DbTxn
from gramps.gen.errors import WindowActiveError, FilterError, HandleError
from ..filters import SearchBar
from ..widgets.menuitem import add_menuitem
@@ -66,7 +67,8 @@ from gramps.gen.const import CUSTOM_FILTERS
from gramps.gen.utils.debug import profile
from gramps.gen.utils.string import data_recover_msg
from gramps.gen.plug import CATEGORY_QR_PERSON
-from ..dialog import QuestionDialog, QuestionDialog3, ErrorDialog
+from ..dialog import (QuestionDialog, QuestionDialog3, ErrorDialog,
+ MultiSelectDialog)
from ..editors import FilterEditor
from ..ddtargets import DdTargets
from ..plug.quick import create_quickreport_menu, create_web_connect_menu
@@ -532,57 +534,138 @@ class ListView(NavigationView):
def get_column_widths(self):
return [column.get_width() for column in self.columns]
- def remove_selected_objects(self):
+ ####################################################################
+ # Object Delete functions
+ ####################################################################
+ # def remove(self): # this is over-ridden in each view
+ # """
+ # must return the tuple of (object type and handle) for each
+ # selected item
+ # """
+ # handles = self.selected_handles()
+ # ht_list = [('Person', hndl) for hndl in handles]
+ # self.remove_selected_objects(ht_list)
+
+ def remove_selected_objects(self, ht_list=None):
"""
Function to remove selected objects
"""
- prompt = True
- if len(self.selected_handles()) > 1:
- ques = QuestionDialog3(
- _("Multiple Selection Delete"),
- _("More than one item has been selected for deletion. "
- "Select the option indicating how to delete the items:"),
- _("Delete All"),
- _("Confirm Each Delete"),
- parent=self.uistate.window)
- res = ques.run()
- if res == -1: # Cancel
- return
- else:
- prompt = not res # we prompt on 'Confirm Each Delete'
+ if len(ht_list) == 1:
+ obj = self.dbstate.db.method(
+ "get_%s_from_handle", ht_list[0][0])(ht_list[0][1])
+ msg1 = self._message1_format(obj)
+ msg2 = self._message2_format(obj)
+ msg2 = "%s %s" % (msg2, data_recover_msg)
+ QuestionDialog(msg1,
+ msg2,
+ _('_Delete'),
+ lambda: self.delete_object_response(obj, parent=self.uistate.window),
+ parent=self.uistate.window)
+ else:
+ MultiSelectDialog(self._message1_format,
+ self._message2_format,
+ ht_list,
+ lambda x: self.dbstate.db.method(
+ "get_%s_from_handle", x[0])(x[1]),
+ yes_func=self.delete_object_response,
+ multi_yes_func=self.delete_multi_object_response,
+ parent=self.uistate.window)
- if not prompt:
- self.uistate.set_busy_cursor(True)
+ def _message1_format(self, obj):
+ """
+ Header format for remove dialogs.
+ """
+ return _('Delete {type} [{gid}]?').format(
+ type=_(obj.__class__.__name__), gid=obj.gramps_id)
- for handle in self.selected_handles():
- (query, is_used, object) = self.remove_object_from_handle(handle)
- if prompt:
- if is_used:
- msg = _('This item is currently being used. '
- 'Deleting it will remove it from the database and '
- 'from all other items that reference it.')
- else:
- msg = _('Deleting item will remove it from the database.')
+ def _message2_format(self, _obj):
+ """
+ Detailed message format for the remove dialogs.
+ """
+ return _('Deleting item will remove it from the database.')
- msg += ' ' + data_recover_msg
- #descr = object.get_description()
- #if descr == "":
- descr = object.get_gramps_id()
- ques = QuestionDialog3(_('Delete %s?') % descr, msg,
- _('_Yes'), _('_No'),
- parent=self.uistate.window)
- res = ques.run()
- if res == -1: # Cancel
- return
- elif res: # If true, perfom the delete
- self.uistate.set_busy_cursor(True)
- query.query_response()
- self.uistate.set_busy_cursor(False)
- else:
- query.query_response()
+ def _message3_format(self, obj):
+ """
+ Transaction label format
+ """
+ return "%s %s [%s]" % (_("Delete"), _(obj.__class__.__name__),
+ obj.get_gramps_id())
- if not prompt:
- self.uistate.set_busy_cursor(False)
+ def delete_object_response(self, obj, parent=None):
+ """
+ Delete the object from the database.
+ """
+ with DbTxn(self._message3_format(obj), self.dbstate.db) as trans:
+ #self.db.disable_signals()
+ self.remove_object_from_handle(
+ obj.__class__.__name__, obj.handle, trans, in_use_prompt=True, parent=parent)
+ #self.dbstate.db.enable_signals()
+ self.uistate.set_busy_cursor(False)
+
+ def delete_multi_object_response(self, ht_list=None, parent=None):
+ """
+ Deletes multiple objects from the database.
+ """
+ # set the busy cursor, so the user knows that we are working
+ self.uistate.set_busy_cursor(True)
+ self.uistate.progress.show()
+ self.uistate.push_message(self.dbstate, _("Processing..."))
+ hndl_cnt = len(ht_list) / 100
+ _db = self.dbstate.db
+ _db.disable_signals()
+
+ # create the transaction
+ with DbTxn('', _db) as trans:
+ for (indx, item) in enumerate(ht_list):
+ result = self.remove_object_from_handle(
+ *item, trans, in_use_prompt=False, parent=parent)
+ self.uistate.pulse_progressbar(indx / hndl_cnt)
+ if result == -1:
+ break
+ trans.set_description(_("Multiple Selection Delete"))
+
+ _db.enable_signals()
+ _db.request_rebuild()
+ self.uistate.progress.hide()
+ self.uistate.set_busy_cursor(False)
+
+ def remove_object_from_handle(self, obj_type, handle,
+ trans, in_use_prompt=False, parent=None):
+ """
+ deletes a single object from database
+ """
+ obj = self.dbstate.db.method("get_%s_from_handle", obj_type)(handle)
+ bl_list = list(self.dbstate.db.find_backlink_handles(handle))
+ if in_use_prompt:
+ res = self._in_use_prompt(obj, bl_list, parent=parent)
+ if res != 1: # Cancel or No
+ return res
+ # perfom the cleanup
+ for ref_type, ref_hndl in bl_list:
+ ref_obj = self.dbstate.db.method(
+ "get_%s_from_handle", ref_type)(ref_hndl)
+ ref_obj.remove_handle_references(obj_type, [handle])
+ self.dbstate.db.method("commit_%s", ref_type)(ref_obj, trans)
+ self.dbstate.db.method("remove_%s", obj_type)(
+ obj.get_handle(), trans)
+
+ def _in_use_prompt(self, obj, bl_list, parent=None):
+ """
+ Prompt user if he wants to continue becasue in use
+ """
+ if bl_list:
+ msg = _('This item is currently being used. '
+ 'Deleting it will remove it from the database and '
+ 'from all other items that reference it.')
+ else:
+ msg = _('Deleting item will remove it from the database.')
+
+ msg += ' ' + data_recover_msg
+ descr = obj.get_gramps_id()
+ ques = QuestionDialog3(_('Delete %s?') % descr, msg,
+ _('_Yes'), _('_No'),
+ parent=parent)
+ return ques.run()
def blist(self, store, path, iter_, sel_list):
'''GtkTreeSelectionForeachFunc
@@ -1188,12 +1271,6 @@ class ListView(NavigationView):
Template function to allow the merger of two objects.
"""
- @abstractmethod
- def remove_object_from_handle(self, handle):
- """
- Template function to allow the removal of an object by its handle
- """
-
def open_all_nodes(self, *obj):
"""
Method for Treeviews to open all groups
diff --git a/gramps/plugins/lib/libpersonview.py b/gramps/plugins/lib/libpersonview.py
index 23748411d..0c6ad005e 100644
--- a/gramps/plugins/lib/libpersonview.py
+++ b/gramps/plugins/lib/libpersonview.py
@@ -46,12 +46,10 @@ _LOG = logging.getLogger(".gui.personview")
#
#-------------------------------------------------------------------------
from gramps.gen.lib import Person, Surname
-from gramps.gen.db import DbTxn
from gramps.gui.views.listview import ListView, TEXT, MARKUP, ICON
from gramps.gui.uimanager import ActionGroup
-from gramps.gen.utils.string import data_recover_msg
from gramps.gen.display.name import displayer as name_displayer
-from gramps.gui.dialog import ErrorDialog, MultiSelectDialog, QuestionDialog
+from gramps.gui.dialog import ErrorDialog
from gramps.gen.errors import WindowActiveError
from gramps.gui.views.bookmarks import PersonBookmarks
from gramps.gen.config import config
@@ -443,27 +441,8 @@ class BasePersonView(ListView):
Remove a person from the database.
"""
handles = self.selected_handles()
- if len(handles) == 1:
- person = self._lookup_person(handles[0])
- msg1 = self._message1_format(person)
- msg2 = self._message2_format(person)
- msg2 = "%s %s" % (msg2, data_recover_msg)
- # This gets person to delete self.active_person:
- QuestionDialog(msg1,
- msg2,
- _('_Delete Person'),
- self.delete_person_response,
- parent=self.uistate.window)
- else:
- # Ask to delete; option to cancel, delete rest
- # This gets person to delete from parameter
- MultiSelectDialog(self._message1_format,
- self._message2_format,
- handles,
- self._lookup_person,
- yes_func=self.delete_person_response,
- multi_yes_func=self.delete_multi_person_response,
- parent=self.uistate.window)
+ ht_list = [('Person', hndl) for hndl in handles]
+ self.remove_selected_objects(ht_list)
def _message1_format(self, person):
return _('Delete %s?') % (name_displayer.display(person) +
@@ -473,65 +452,19 @@ class BasePersonView(ListView):
return _('Deleting the person will remove the person '
'from the database.')
- def _lookup_person(self, handle):
+ def _message3_format(self, person):
"""
- Get the next person from handle.
+ Transaction label format
+ """
+ return _("Delete Person (%s)") % name_displayer.display(person)
+
+ def remove_object_from_handle(self, _obj_type, handle,
+ trans, in_use_prompt=False, parent=None):
+ """
+ deletes a single object from database
"""
person = self.dbstate.db.get_person_from_handle(handle)
- self.active_person = person
- return person
-
- def delete_person_response(self, person=None):
- """
- Deletes the person from the database.
- """
- # set the busy cursor, so the user knows that we are working
- self.uistate.set_busy_cursor(True)
-
- # create the transaction
- with DbTxn('', self.dbstate.db) as trans:
-
- # create name to save
- person = self.active_person
- active_name = _("Delete Person (%s)") % name_displayer.display(person)
-
- # delete the person from the database
- # Above will emit person-delete, which removes the person via
- # callback to the model, so row delete is signaled
- self.dbstate.db.delete_person_from_database(person, trans)
- trans.set_description(active_name)
-
- self.uistate.set_busy_cursor(False)
-
- def delete_multi_person_response(self, handles=None):
- """
- Deletes multiple persons from the database.
- """
- # set the busy cursor, so the user knows that we are working
- self.uistate.set_busy_cursor(True)
- self.uistate.progress.show()
- self.uistate.push_message(self.dbstate, _("Processing..."))
- hndl_cnt = len(handles) / 100
- self.dbstate.db.disable_signals()
-
- # create the transaction
- with DbTxn('', self.dbstate.db) as trans:
- for (indx, handle) in enumerate(handles):
- person = self.dbstate.db.get_person_from_handle(handle)
- self.dbstate.db.delete_person_from_database(person, trans)
- self.uistate.pulse_progressbar(indx / hndl_cnt)
- trans.set_description(_("Multiple Selection Delete"))
-
- self.dbstate.db.enable_signals()
- self.dbstate.db.request_rebuild()
- self.uistate.progress.hide()
- self.uistate.set_busy_cursor(False)
-
- def remove_object_from_handle(self, handle):
- """
- The remove_selected_objects method is not called in this view.
- """
- pass
+ self.dbstate.db.delete_person_from_database(person, trans)
def define_actions(self):
"""
diff --git a/gramps/plugins/lib/libplaceview.py b/gramps/plugins/lib/libplaceview.py
index 9401d35bf..4a34ba495 100644
--- a/gramps/plugins/lib/libplaceview.py
+++ b/gramps/plugins/lib/libplaceview.py
@@ -45,7 +45,7 @@ from gramps.gen.config import config
from gramps.gui.dialog import ErrorDialog
from gramps.gui.pluginmanager import GuiPluginManager
from gramps.gui.ddtargets import DdTargets
-from gramps.gui.editors import EditPlace, DeletePlaceQuery
+from gramps.gui.editors import EditPlace
from gramps.gui.filters.sidebar import PlaceSidebarFilter
from gramps.gui.merge import MergePlace
from gramps.gen.plug import CATEGORY_QR_PLACE
@@ -503,34 +503,17 @@ class PlaceBaseView(ListView):
pass
def remove(self, *obj):
+ ht_list = []
for handle in self.selected_handles():
- for link in self.dbstate.db.find_backlink_handles(handle,['Place']):
+ for _link in self.dbstate.db.find_backlink_handles(handle,
+ ['Place']):
msg = _("Cannot delete place.")
- msg2 = _("This place is currently referenced by another place. "
- "First remove the places it contains.")
+ msg2 = _("This place is currently referenced by another place."
+ " First remove the places it contains.")
ErrorDialog(msg, msg2, parent=self.uistate.window)
return
- self.remove_selected_objects()
-
- def remove_object_from_handle(self, handle):
- person_list = [
- item[1] for item in
- self.dbstate.db.find_backlink_handles(handle,['Person'])]
-
- family_list = [
- item[1] for item in
- self.dbstate.db.find_backlink_handles(handle,['Family'])]
-
- event_list = [
- item[1] for item in
- self.dbstate.db.find_backlink_handles(handle,['Event'])]
-
- object = self.dbstate.db.get_place_from_handle(handle)
- query = DeletePlaceQuery(self.dbstate, self.uistate, object,
- person_list, family_list, event_list)
-
- is_used = len(person_list) + len(family_list) + len(event_list) > 0
- return (query, is_used, object)
+ ht_list.append(('Place', handle))
+ self.remove_selected_objects(ht_list)
def edit(self, *obj):
for handle in self.selected_handles():
diff --git a/gramps/plugins/lib/libsourceview.py b/gramps/plugins/lib/libsourceview.py
new file mode 100644
index 000000000..331a04800
--- /dev/null
+++ b/gramps/plugins/lib/libsourceview.py
@@ -0,0 +1,104 @@
+# Gramps - a GTK+/GNOME based genealogy program
+#
+# Copyright (C) 2020 Paul Culley
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+
+"""
+Library common to SourceView and CitationTreeView
+"""
+from gramps.gen.errors import HandleError
+
+
+#-------------------------------------------------------------------------
+#
+# SourceLibView
+#
+#-------------------------------------------------------------------------
+class LibSourceView():
+ """
+ This contains the common delete related methods for the views.
+ It was written specifically for CitationTreeView, but works for SourceView
+ as well; there just will never be an citation handles in selection.
+
+ This must be placed before Listview in the MRO to properly override the
+ included methods. For example:
+ class SourceView(LibSourceView, ListView)
+ """
+
+ def remove(self, *obj):
+ """
+ Method called when deleting source(s) or citations from the views.
+ """
+ ht_list = []
+ handles = self.selected_handles()
+ for hndl in handles:
+ if self.dbstate.db.has_source_handle(hndl):
+ ht_list.append(('Source', hndl))
+ else:
+ ht_list.append(('Citation', hndl))
+ self.remove_selected_objects(ht_list)
+
+ def remove_object_from_handle(self, obj_type, handle,
+ trans, in_use_prompt=False, parent=None):
+ """
+ deletes a single object from database
+ """
+ try: # need this in case user selects both source and its Citations
+ obj = self.dbstate.db.method(
+ "get_%s_from_handle", obj_type)(handle)
+ except HandleError:
+ return
+ bl_list = list(self.dbstate.db.find_backlink_handles(handle))
+ if in_use_prompt:
+ res = self._in_use_prompt(obj, bl_list, parent=parent)
+ if res != 1: # Cancel or No
+ return res
+ # perfom the cleanup
+ if obj_type == 'Source':
+ # we need to delete all back linked citations, so sort these out
+ cit_list = []
+ nbl_list = []
+ for item in bl_list:
+ if item[0] == 'Citation':
+ cit_list.append(item[1])
+ else: # save any other back links for later.
+ nbl_list.append(item)
+ # now lets go through citations and clean up their back-refs
+ hndl_cnt = len(cit_list) / 100
+ for indx, cit_hndl in enumerate(cit_list):
+ # the following introduces another pass with the progressbar
+ # to keep the user from wondering what is happening if there
+ # are a lot of citations on the source
+ self.uistate.pulse_progressbar(indx / hndl_cnt)
+ cit_bl_list = list(self.dbstate.db.find_backlink_handles(
+ cit_hndl))
+ for ref_type, ref_hndl in cit_bl_list:
+ ref_obj = self.dbstate.db.method(
+ "get_%s_from_handle", ref_type)(ref_hndl)
+ ref_obj.remove_handle_references(obj_type, [cit_hndl])
+ self.dbstate.db.method("commit_%s", ref_type)(
+ ref_obj, trans)
+ # and delete the citation
+ self.dbstate.db.remove_citation(cit_hndl, trans)
+ bl_list = nbl_list # to clean up any other back refs to source
+ for ref_type, ref_hndl in bl_list:
+ ref_obj = self.dbstate.db.method(
+ "get_%s_from_handle", ref_type)(ref_hndl)
+ ref_obj.remove_handle_references(obj_type, [handle])
+ self.dbstate.db.method("commit_%s", ref_type)(ref_obj, trans)
+ # and delete the source
+ self.dbstate.db.remove_source(handle, trans)
diff --git a/gramps/plugins/view/citationlistview.py b/gramps/plugins/view/citationlistview.py
index 44188981b..dd1b3e3ea 100644
--- a/gramps/plugins/view/citationlistview.py
+++ b/gramps/plugins/view/citationlistview.py
@@ -47,12 +47,11 @@ from gramps.gui.views.treemodels.citationlistmodel import CitationListModel
from gramps.gen.plug import CATEGORY_QR_CITATION
from gramps.gen.lib import Citation, Source
from gramps.gui.views.listview import ListView, TEXT, MARKUP, ICON
-from gramps.gen.utils.db import get_citation_referents
from gramps.gui.views.bookmarks import CitationBookmarks
from gramps.gen.errors import WindowActiveError
from gramps.gui.ddtargets import DdTargets
from gramps.gui.dialog import ErrorDialog
-from gramps.gui.editors import EditCitation, DeleteCitationQuery
+from gramps.gui.editors import EditCitation
from gramps.gui.filters.sidebar import CitationSidebarFilter
from gramps.gui.merge import MergeCitation
@@ -367,15 +366,9 @@ class CitationListView(ListView):
pass
def remove(self, *obj):
- self.remove_selected_objects()
-
- def remove_object_from_handle(self, handle):
- the_lists = get_citation_referents(handle, self.dbstate.db)
- object = self.dbstate.db.get_citation_from_handle(handle)
- query = DeleteCitationQuery(self.dbstate, self.uistate, object,
- the_lists)
- is_used = any(the_lists)
- return (query, is_used, object)
+ handles = self.selected_handles()
+ ht_list = [('Citation', hndl) for hndl in handles]
+ self.remove_selected_objects(ht_list)
def edit(self, *obj):
"""
diff --git a/gramps/plugins/view/citationtreeview.py b/gramps/plugins/view/citationtreeview.py
index ddb4248b3..8f71535b3 100644
--- a/gramps/plugins/view/citationtreeview.py
+++ b/gramps/plugins/view/citationtreeview.py
@@ -48,16 +48,14 @@ from gramps.gui.views.treemodels.citationtreemodel import CitationTreeModel
from gramps.gen.plug import CATEGORY_QR_SOURCE_OR_CITATION
from gramps.gen.lib import Citation, Source
from gramps.gui.views.listview import ListView
-from gramps.gen.utils.db import (get_source_and_citation_referents,
- get_citation_referents)
from gramps.gui.views.bookmarks import CitationBookmarks
from gramps.gen.errors import WindowActiveError, HandleError
from gramps.gui.ddtargets import DdTargets
from gramps.gui.dialog import ErrorDialog
-from gramps.gui.editors import EditCitation, DeleteCitationQuery, EditSource, \
- DeleteSrcQuery
+from gramps.gui.editors import EditCitation, EditSource
from gramps.gui.filters.sidebar import SourceSidebarFilter
from gramps.gui.merge import MergeCitation, MergeSource
+from gramps.plugins.lib.libsourceview import LibSourceView
#-------------------------------------------------------------------------
#
@@ -67,12 +65,13 @@ from gramps.gui.merge import MergeCitation, MergeSource
from gramps.gen.const import GRAMPS_LOCALE as glocale
_ = glocale.translation.sgettext
+
#-------------------------------------------------------------------------
#
# PlaceTreeView
#
#-------------------------------------------------------------------------
-class CitationTreeView(ListView):
+class CitationTreeView(LibSourceView, ListView):
"""
A hierarchical view of sources with citations below them.
"""
@@ -590,28 +589,6 @@ class CitationTreeView(ListView):
WarningDialog(_("Cannot share this reference"),
self.__blocked_text(),
parent=self.uistate.window)
-#
- def remove(self, *obj):
- self.remove_selected_objects()
-
- def remove_object_from_handle(self, handle):
- # The handle will either be a Source handle or a Citation handle
- source, citation = self.get_source_or_citation(handle)
- if citation:
- the_lists = get_citation_referents(handle, self.dbstate.db)
- query = DeleteCitationQuery(self.dbstate, self.uistate, citation,
- the_lists)
- is_used = any(the_lists)
- return (query, is_used, citation)
- else:
- the_lists = get_source_and_citation_referents(handle,
- self.dbstate.db)
- LOG.debug('the_lists %s' % [the_lists])
-
- query = DeleteSrcQuery(self.dbstate, self.uistate, source,
- the_lists)
- is_used = any(the_lists)
- return (query, is_used, source)
def edit(self, *obj):
"""
diff --git a/gramps/plugins/view/eventview.py b/gramps/plugins/view/eventview.py
index 935618de3..2622b45c3 100644
--- a/gramps/plugins/view/eventview.py
+++ b/gramps/plugins/view/eventview.py
@@ -40,14 +40,12 @@ _LOG = logging.getLogger(".plugins.eventview")
from gramps.gen.const import GRAMPS_LOCALE as glocale
_ = glocale.translation.sgettext
-from gramps.gui.dialog import ErrorDialog, MultiSelectDialog, QuestionDialog
-from gramps.gen.db import DbTxn
+from gramps.gui.dialog import ErrorDialog
from gramps.gen.errors import WindowActiveError
from gramps.gen.lib import Event
-from gramps.gen.utils.string import data_recover_msg
from gramps.gui.ddtargets import DdTargets
-from gramps.gui.editors import EditEvent, DeleteEventQuery
+from gramps.gui.editors import EditEvent
from gramps.gui.filters.sidebar import EventSidebarFilter
from gramps.gui.merge import MergeEvent
from gramps.gen.plug import CATEGORY_QR_EVENT
@@ -376,24 +374,8 @@ class EventView(ListView):
Method called when deleting event(s) from the event view.
"""
handles = self.selected_handles()
- if len(handles) == 1:
- event = self.dbstate.db.get_event_from_handle(handles[0])
- msg1 = self._message1_format(event)
- msg2 = self._message2_format(event)
- msg2 = "%s %s" % (msg2, data_recover_msg)
- QuestionDialog(msg1,
- msg2,
- _('_Delete Event'),
- lambda: self.delete_event_response(event),
- parent=self.uistate.window)
- else:
- MultiSelectDialog(self._message1_format,
- self._message2_format,
- handles,
- self.dbstate.db.get_event_from_handle,
- yes_func=self.delete_event_response,
- multi_yes_func=self.delete_multi_event_response,
- parent=self.uistate.window)
+ ht_list = [('Event', hndl) for hndl in handles]
+ self.remove_selected_objects(ht_list)
def _message1_format(self, event):
"""
@@ -402,73 +384,6 @@ class EventView(ListView):
return _('Delete {type} [{gid}]?').format(type=str(event.type),
gid=event.gramps_id)
- def _message2_format(self, event):
- """
- Detailed message format for the remove dialogs.
- """
- return _('Deleting item will remove it from the database.')
-
- def delete_event_response(self, event):
- """
- Delete the event from the database.
- """
- person_list = [item[1] for item in
- self.dbstate.db.find_backlink_handles(event.handle, ['Person'])]
- family_list = [item[1] for item in
- self.dbstate.db.find_backlink_handles(event.handle, ['Family'])]
-
- query = DeleteEventQuery(self.dbstate, self.uistate, event,
- person_list, family_list)
- query.query_response()
-
- def delete_multi_event_response(self, handles=None):
- """
- Deletes multiple events from the database.
- """
- # set the busy cursor, so the user knows that we are working
- self.uistate.set_busy_cursor(True)
- self.uistate.progress.show()
- self.uistate.push_message(self.dbstate, _("Processing..."))
- hndl_cnt = len(handles) / 100
- _db = self.dbstate.db
- _db.disable_signals()
-
- # create the transaction
- with DbTxn('', _db) as trans:
- for (indx, handle) in enumerate(handles):
- ev_handle_list = [handle]
-
- person_list = [
- item[1] for item in
- _db.find_backlink_handles(handle, ['Person'])]
- for hndl in person_list:
- person = _db.get_person_from_handle(hndl)
- person.remove_handle_references('Event', ev_handle_list)
- _db.commit_person(person, trans)
-
- family_list = [
- item[1] for item in
- _db.find_backlink_handles(handle, ['Family'])]
- for hndl in family_list:
- family = _db.get_family_from_handle(hndl)
- family.remove_handle_references('Event', ev_handle_list)
- _db.commit_family(family, trans)
-
- _db.remove_event(handle, trans)
- self.uistate.pulse_progressbar(indx / hndl_cnt)
- trans.set_description(_("Multiple Selection Delete"))
-
- _db.enable_signals()
- _db.request_rebuild()
- self.uistate.progress.hide()
- self.uistate.set_busy_cursor(False)
-
- def remove_object_from_handle(self, handle):
- """
- The remove_selected_objects method is not called in this view.
- """
- pass
-
def edit(self, *obj):
for handle in self.selected_handles():
event = self.dbstate.db.get_event_from_handle(handle)
diff --git a/gramps/plugins/view/familyview.py b/gramps/plugins/view/familyview.py
index 9ac8529c8..61ffc56b8 100644
--- a/gramps/plugins/view/familyview.py
+++ b/gramps/plugins/view/familyview.py
@@ -359,26 +359,9 @@ class FamilyView(ListView):
"""
Method called when deleting a family from a family view.
"""
- from gramps.gui.dialog import QuestionDialog, MultiSelectDialog
- from gramps.gen.utils.string import data_recover_msg
handles = self.selected_handles()
- if len(handles) == 1:
- family = self.dbstate.db.get_family_from_handle(handles[0])
- msg1 = self._message1_format(family)
- msg2 = self._message2_format(family)
- msg2 = "%s %s" % (msg2, data_recover_msg)
- QuestionDialog(msg1,
- msg2,
- _('_Delete Family'),
- lambda: self.delete_family_response(family),
- parent=self.uistate.window)
- else:
- MultiSelectDialog(self._message1_format,
- self._message2_format,
- handles,
- self.dbstate.db.get_family_from_handle,
- yes_func=self.delete_family_response,
- parent=self.uistate.window)
+ ht_list = [('Family', hndl) for hndl in handles]
+ self.remove_selected_objects(ht_list)
def _message1_format(self, family):
"""
@@ -387,32 +370,13 @@ class FamilyView(ListView):
return _('Delete %s?') % (_('family') +
(" [%s]" % family.gramps_id))
- def _message2_format(self, family):
+ def remove_object_from_handle(self, _obj_type, handle,
+ trans, in_use_prompt=False, parent=None):
"""
- Detailed message format for the remove dialogs.
+ deletes a single object from database
"""
- return _('Deleting item will remove it from the database.')
-
- def delete_family_response(self, family):
- """
- Deletes the family from the database. Callback to remove
- dialogs.
- """
- from gramps.gen.db import DbTxn
- # set the busy cursor, so the user knows that we are working
- self.uistate.set_busy_cursor(True)
- # create the transaction
- with DbTxn('', self.dbstate.db) as trans:
- gramps_id = family.gramps_id
- self.dbstate.db.remove_family_relationships(family.handle, trans)
- trans.set_description(_("Family [%s]") % gramps_id)
- self.uistate.set_busy_cursor(False)
-
- def remove_object_from_handle(self, handle):
- """
- The remove_selected_objects method is not called in this view.
- """
- pass
+ family = self.dbstate.db.get_family_from_handle(handle)
+ self.dbstate.db.remove_family_relationships(handle, trans)
def edit(self, *obj):
for handle in self.selected_handles():
diff --git a/gramps/plugins/view/mediaview.py b/gramps/plugins/view/mediaview.py
index 582321e05..7744ef72b 100644
--- a/gramps/plugins/view/mediaview.py
+++ b/gramps/plugins/view/mediaview.py
@@ -54,12 +54,11 @@ from gramps.gui.views.treemodels import MediaModel
from gramps.gen.config import config
from gramps.gen.utils.file import (media_path, relative_path, media_path_full,
create_checksum)
-from gramps.gen.utils.db import get_media_referents
from gramps.gui.views.bookmarks import MediaBookmarks
from gramps.gen.mime import get_type, is_valid_type
from gramps.gen.lib import Media
from gramps.gen.db import DbTxn
-from gramps.gui.editors import EditMedia, DeleteMediaQuery
+from gramps.gui.editors import EditMedia
from gramps.gen.errors import WindowActiveError
from gramps.gui.filters.sidebar import MediaSidebarFilter
from gramps.gui.merge import MergeMedia
@@ -456,18 +455,9 @@ class MediaView(ListView):
pass
def remove(self, *obj):
- self.remove_selected_objects()
-
- def remove_object_from_handle(self, handle):
- """
- Remove the selected objects from the database after getting
- user verification.
- """
- the_lists = get_media_referents(handle, self.dbstate.db)
- object = self.dbstate.db.get_media_from_handle(handle)
- query = DeleteMediaQuery(self.dbstate, self.uistate, handle, the_lists)
- is_used = any(the_lists)
- return (query, is_used, object)
+ handles = self.selected_handles()
+ ht_list = [('Media', hndl) for hndl in handles]
+ self.remove_selected_objects(ht_list)
def edit(self, *obj):
"""
diff --git a/gramps/plugins/view/noteview.py b/gramps/plugins/view/noteview.py
index b4d24f266..9e0e3f2a2 100644
--- a/gramps/plugins/view/noteview.py
+++ b/gramps/plugins/view/noteview.py
@@ -46,7 +46,6 @@ from gi.repository import Gtk
#-------------------------------------------------------------------------
from gramps.gui.views.listview import ListView, TEXT, MARKUP, ICON
from gramps.gui.views.treemodels import NoteModel
-from gramps.gen.utils.db import get_note_referents
from gramps.gen.errors import WindowActiveError
from gramps.gui.views.bookmarks import NoteBookmarks
from gramps.gen.config import config
@@ -54,7 +53,7 @@ from gramps.gen.lib import Note
from gramps.gui.ddtargets import DdTargets
from gramps.gui.dialog import ErrorDialog
from gramps.gui.filters.sidebar import NoteSidebarFilter
-from gramps.gui.editors import EditNote, DeleteNoteQuery
+from gramps.gui.editors import EditNote
from gramps.gui.merge import MergeNote
from gramps.gen.plug import CATEGORY_QR_NOTE
@@ -330,14 +329,9 @@ class NoteView(ListView):
pass
def remove(self, *obj):
- self.remove_selected_objects()
-
- def remove_object_from_handle(self, handle):
- the_lists = get_note_referents(handle, self.dbstate.db)
- object = self.dbstate.db.get_note_from_handle(handle)
- query = DeleteNoteQuery(self.dbstate, self.uistate, object, the_lists)
- is_used = any(the_lists)
- return (query, is_used, object)
+ handles = self.selected_handles()
+ ht_list = [('Note', hndl) for hndl in handles]
+ self.remove_selected_objects(ht_list)
def edit(self, *obj):
for handle in self.selected_handles():
diff --git a/gramps/plugins/view/repoview.py b/gramps/plugins/view/repoview.py
index bc95c456d..3183707ff 100644
--- a/gramps/plugins/view/repoview.py
+++ b/gramps/plugins/view/repoview.py
@@ -40,7 +40,7 @@ from gramps.gui.views.treemodels import RepositoryModel
from gramps.gui.views.bookmarks import RepoBookmarks
from gramps.gen.errors import WindowActiveError
from gramps.gen.config import config
-from gramps.gui.editors import EditRepository, DeleteRepositoryQuery
+from gramps.gui.editors import EditRepository
from gramps.gui.ddtargets import DdTargets
from gramps.gui.dialog import ErrorDialog
from gramps.gui.filters.sidebar import RepoSidebarFilter
@@ -333,17 +333,12 @@ class RepositoryView(ListView):
EditRepository(self.dbstate, self.uistate, [], Repository())
def remove(self, *obj):
- self.remove_selected_objects()
-
- def remove_object_from_handle(self, handle):
- source_list = [
- item[1] for item in
- self.dbstate.db.find_backlink_handles(handle, ['Source'])]
- object = self.dbstate.db.get_repository_from_handle(handle)
- query = DeleteRepositoryQuery(self.dbstate, self.uistate, object,
- source_list)
- is_used = len(source_list) > 0
- return (query, is_used, object)
+ """
+ Method called when deleting repo(s) from the repo view.
+ """
+ handles = self.selected_handles()
+ ht_list = [('Repository', hndl) for hndl in handles]
+ self.remove_selected_objects(ht_list)
def edit(self, *obj):
for handle in self.selected_handles():
diff --git a/gramps/plugins/view/sourceview.py b/gramps/plugins/view/sourceview.py
index 081302bff..06526771e 100644
--- a/gramps/plugins/view/sourceview.py
+++ b/gramps/plugins/view/sourceview.py
@@ -41,15 +41,15 @@ from gramps.gen.lib import Source
from gramps.gen.config import config
from gramps.gui.views.listview import ListView, TEXT, MARKUP, ICON
from gramps.gui.views.treemodels import SourceModel
-from gramps.gen.utils.db import get_source_and_citation_referents
from gramps.gui.views.bookmarks import SourceBookmarks
from gramps.gen.errors import WindowActiveError
from gramps.gui.ddtargets import DdTargets
from gramps.gui.dialog import ErrorDialog
-from gramps.gui.editors import EditSource, DeleteSrcQuery
+from gramps.gui.editors import EditSource
from gramps.gui.filters.sidebar import SourceSidebarFilter
from gramps.gui.merge import MergeSource
from gramps.gen.plug import CATEGORY_QR_SOURCE
+from gramps.plugins.lib.libsourceview import LibSourceView
#-------------------------------------------------------------------------
#
@@ -65,7 +65,7 @@ _ = glocale.translation.sgettext
# SourceView
#
#-------------------------------------------------------------------------
-class SourceView(ListView):
+class SourceView(LibSourceView, ListView):
""" sources listview class
"""
COL_TITLE = 0
@@ -318,18 +318,6 @@ class SourceView(ListView):
def add(self, *obj):
EditSource(self.dbstate, self.uistate, [], Source())
- def remove(self, *obj):
- self.remove_selected_objects()
-
- def remove_object_from_handle(self, handle):
- the_lists = get_source_and_citation_referents(handle, self.dbstate.db)
- LOG.debug('the_lists %s' % [the_lists])
-
- object = self.dbstate.db.get_source_from_handle(handle)
- query = DeleteSrcQuery(self.dbstate, self.uistate, object, the_lists)
- is_used = any(the_lists)
- return (query, is_used, object)
-
def edit(self, *obj):
for handle in self.selected_handles():
source = self.dbstate.db.get_source_from_handle(handle)