Merge pull request #1098 from prculley/noteref

This commit is contained in:
Nick Hall 2022-02-26 18:22:04 +00:00
commit 1ba1265356
39 changed files with 762 additions and 799 deletions

View File

@ -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,

View File

@ -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):
"""

View File

@ -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):
"""

View File

@ -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`

View File

@ -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)

View File

@ -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):

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -258,6 +258,7 @@ class PersonCheck(BaseMergeCheck):
</source>
<source handle="_s0001" id="S0001">
<stitle>Source 1</stitle>
<reporef hlink="_r0001" medium="Electronic"/>
</source>
</sources>
<places>
@ -278,9 +279,40 @@ class PersonCheck(BaseMergeCheck):
<file src="image1.jpg" mime="image/jpeg" description="Image 1"/>
</object>
</objects>
<repositories>
<repository handle="_r0000" id="R0000">
<rname>New York Public Library</rname>
<type>Library</type>
</repository>
<repository handle="_r0001" id="R0001">
<rname>Aunt Martha's Attic</rname>
<type>Collection</type>
</repository>
</repositories>
<notes>
<note handle="_n0000" id="N0000" type="Event Note">
<text>Note 0</text>
<text>Note 0.</text>
<style name="link" value="gramps://Citation/handle/c0001">
<range start="0" end="1"/>
</style>
<style name="link" value="gramps://Event/handle/e0001">
<range start="1" end="2"/>
</style>
<style name="link" value="gramps://Media/handle/o0001">
<range start="2" end="3"/>
</style>
<style name="link" value="gramps://Note/handle/n0001">
<range start="3" end="4"/>
</style>
<style name="link" value="gramps://Place/handle/p0001">
<range start="4" end="5"/>
</style>
<style name="link" value="gramps://Repository/handle/r0001">
<range start="5" end="6"/>
</style>
<style name="link" value="gramps://Source/handle/s0001">
<range start="6" end="7"/>
</style>
</note>
<note handle="_n0001" id="N0001" type="Event Note">
<text>Note 1</text>
@ -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):
</name>
</person>
</people>
<notes>
<note handle="_n0000" id="N0000" type="Person Note">
<text>Note 0.</text>
<style name="link" value="gramps://Person/handle/i0001">
<range start="0" end="1"/>
</style>
</note>
</notes>
</database>"""
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):
<rel type="Unknown"/>
</family>
</families>
<notes>
<note handle="_n0000" id="N0000" type="Person Note">
<text>Note 0</text>
<style name="link" value="gramps://Person/handle/i0002">
<range start="0" end="2"/>
</style>
</note>
<note handle="_n0001" id="N0001" type="Family Note">
<text>Note 1</text>
<style name="link" value="gramps://Family/handle/f0001">
<range start="0" end="4"/>
</style>
</note>
<note handle="_n0003" id="N0003" type="Person Note">
<text>Note 0</text>
<style name="link" value="gramps://Person/handle/i0003">
<range start="0" end="2"/>
</style>
</note>
</notes>
</database>"""
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):
<mother hlink="_i0003"/>
</family>
</families>
<notes>
<note handle="_n0000" id="N0000" type="Family Note">
<text>Note 0.</text>
<style name="link" value="gramps://Family/handle/f0001">
<range start="0" end="1"/>
</style>
</note>
</notes>
</database>"""
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'

View File

@ -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)

View File

@ -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

View File

@ -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:

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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
#-------------------------------------------------------------------------
#

View File

@ -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

View File

@ -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):
"""

View File

@ -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():

View File

@ -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)

View File

@ -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):
"""

View File

@ -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):
"""

View File

@ -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)

View File

@ -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():

View File

@ -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):
"""

View File

@ -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():

View File

@ -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():

View File

@ -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)