Fix for delete of a ref'd primary obj while editing an added obj. (#779)

Also fixed to update the ref'd obj on changes from outside the
editor.

Fixes #10999, #11000, #11001, #11002
This commit is contained in:
Paul Culley
2019-02-13 21:31:21 -06:00
committed by Sam Manzi
parent d65ad470d6
commit 62f8049d6a
8 changed files with 226 additions and 10 deletions

View File

@@ -131,15 +131,15 @@ class EventEmbedList(DbGUIElement, GroupEmbeddedList):
refs = self.get_data()[self._WORKGROUP] refs = self.get_data()[self._WORKGROUP]
ref_list = [eref.ref for eref in refs] ref_list = [eref.ref for eref in refs]
indexlist = [] indexlist = []
last = 0 last = -1
while True: while True:
try: try:
last = ref_list.index(handle) last = ref_list.index(handle, last + 1)
indexlist.append(last) indexlist.append(last)
except ValueError: except ValueError:
break break
#remove the deleted workgroup events from the object #remove the deleted workgroup events from the object
for index in indexlist.reverse(): for index in reversed(indexlist):
del refs[index] del refs[index]
#now rebuild the display tab #now rebuild the display tab
self.rebuild_callback() self.rebuild_callback()

View File

@@ -38,13 +38,15 @@ from gramps.gen.errors import WindowActiveError
from ...ddtargets import DdTargets from ...ddtargets import DdTargets
from .personrefmodel import PersonRefModel from .personrefmodel import PersonRefModel
from .embeddedlist import EmbeddedList, TEXT_COL, MARKUP_COL, ICON_COL from .embeddedlist import EmbeddedList, TEXT_COL, MARKUP_COL, ICON_COL
from ...dbguielement import DbGUIElement
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# #
# #
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
class PersonRefEmbedList(EmbeddedList): class PersonRefEmbedList(DbGUIElement, EmbeddedList):
_HANDLE_COL = 4 _HANDLE_COL = 4
_DND_TYPE = DdTargets.PERSONREF _DND_TYPE = DdTargets.PERSONREF
@@ -69,15 +71,61 @@ class PersonRefEmbedList(EmbeddedList):
def __init__(self, dbstate, uistate, track, data): def __init__(self, dbstate, uistate, track, data):
self.data = data self.data = data
DbGUIElement.__init__(self, dbstate.db)
EmbeddedList.__init__(self, dbstate, uistate, track, EmbeddedList.__init__(self, dbstate, uistate, track,
_('_Associations'), PersonRefModel, _('_Associations'), PersonRefModel,
move_buttons=True) move_buttons=True)
def _connect_db_signals(self):
"""
called on init of DbGUIElement, connect to db as required.
"""
#note: person-rebuild closes the editors, so no need to connect to it
self.callman.register_callbacks(
{'person-update': self.person_change, # change to person we track
'person-delete': self.person_delete, # delete of person we track
})
self.callman.connect_all(keys=['person'])
def person_change(self, *obj):
"""
Callback method called when a tracked person changes (description
changes...)
"""
self.rebuild()
def person_delete(self, hndls):
"""
Callback method called when a tracked person is deleted.
There are two possibilities:
* a tracked non-workgroup person is deleted, just rebuilding the view
will correct this.
* a workgroup person is deleted. The person must be removed from the
obj so that no inconsistent data is shown.
"""
for handle in hndls:
ref_list = [pref.ref for pref in self.data]
indexlist = []
last = -1
while True:
try:
last = ref_list.index(handle, last + 1)
indexlist.append(last)
except ValueError:
break
#remove the deleted workgroup persons from the object
for index in reversed(indexlist):
del self.data[index]
#now rebuild the display tab
self.rebuild()
def get_ref_editor(self): def get_ref_editor(self):
from .. import EditPersonRef from .. import EditPersonRef
return EditPersonRef return EditPersonRef
def get_data(self): def get_data(self):
self.callman.register_handles(
{'person': [pref.ref for pref in self.data]})
return self.data return self.data
def column_order(self): def column_order(self):

View File

@@ -40,13 +40,15 @@ from ...ddtargets import DdTargets
from .placerefmodel import PlaceRefModel from .placerefmodel import PlaceRefModel
from .embeddedlist import EmbeddedList, TEXT_COL from .embeddedlist import EmbeddedList, TEXT_COL
from ...selectors import SelectorFactory from ...selectors import SelectorFactory
from ...dbguielement import DbGUIElement
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# #
# #
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
class PlaceRefEmbedList(EmbeddedList): class PlaceRefEmbedList(DbGUIElement, EmbeddedList):
_HANDLE_COL = 4 _HANDLE_COL = 4
_DND_TYPE = DdTargets.PLACEREF _DND_TYPE = DdTargets.PLACEREF
@@ -65,11 +67,57 @@ class PlaceRefEmbedList(EmbeddedList):
self.data = data self.data = data
self.handle = handle self.handle = handle
self.callback = callback self.callback = callback
DbGUIElement.__init__(self, dbstate.db)
EmbeddedList.__init__(self, dbstate, uistate, track, EmbeddedList.__init__(self, dbstate, uistate, track,
_('Enclosed By'), PlaceRefModel, _('Enclosed By'), PlaceRefModel,
share_button=True, move_buttons=True) share_button=True, move_buttons=True)
def _connect_db_signals(self):
"""
called on init of DbGUIElement, connect to db as required.
"""
#note: place-rebuild closes the editors, so no need to connect to it
self.callman.register_callbacks(
{'place-update': self.place_change, # change to place we track
'place-delete': self.place_delete, # delete of place we track
})
self.callman.connect_all(keys=['place'])
def place_change(self, *obj):
"""
Callback method called when a tracked place changes (description
changes...)
"""
self.rebuild()
def place_delete(self, hndls):
"""
Callback method called when a tracked place is deleted.
There are two possibilities:
* a tracked non-workgroup place is deleted, just rebuilding the view
will correct this.
* a workgroup place is deleted. The place must be removed from the
obj so that no inconsistent data is shown.
"""
for handle in hndls:
ref_list = [pref.ref for pref in self.data]
indexlist = []
last = -1
while True:
try:
last = ref_list.index(handle, last + 1)
indexlist.append(last)
except ValueError:
break
#remove the deleted workgroup places from the object
for index in reversed(indexlist):
del self.data[index]
#now rebuild the display tab
self.rebuild()
def get_data(self): def get_data(self):
self.callman.register_handles(
{'place': [pref.ref for pref in self.data]})
return self.data return self.data
def column_order(self): def column_order(self):

View File

@@ -174,6 +174,8 @@ class EditCitation(EditPrimary):
self._add_db_signal('citation-rebuild', self._do_close) self._add_db_signal('citation-rebuild', self._do_close)
self._add_db_signal('citation-delete', self.check_for_close) self._add_db_signal('citation-delete', self.check_for_close)
self._add_db_signal('source-delete', self.source_delete)
self._add_db_signal('source-update', self.source_update)
def _setup_fields(self): def _setup_fields(self):
""" """
@@ -269,6 +271,26 @@ class EditCitation(EditPrimary):
author = '' author = ''
self.glade.get_object("author").set_text(author) self.glade.get_object("author").set_text(author)
def source_update(self, hndls):
''' Source changed outside of dialog, update text if its ours '''
handle = self.obj.get_reference_handle()
if handle and handle in hndls:
source = self.db.get_source_from_handle(handle)
s_lbl = "%s [%s]" % (source.get_title(), source.gramps_id)
self.glade.get_object("source").set_text(s_lbl)
author = source.get_author()
self.glade.get_object("author").set_text(author)
def source_delete(self, hndls):
''' Source deleted outside of dialog, remove it if its ours'''
handle = self.obj.get_reference_handle()
if handle and handle in hndls:
self.obj.set_reference_handle(None)
self.glade.get_object("source").set_markup(
self.source_field.EMPTY_TEXT)
self.glade.get_object("author").set_text('')
self.source_field.set_button(False)
def build_menu_names(self, source): def build_menu_names(self, source):
""" """
Provide the information needed by the base class to define the Provide the information needed by the base class to define the

View File

@@ -129,6 +129,8 @@ class EditEvent(EditPrimary):
""" """
self._add_db_signal('event-rebuild', self._do_close) self._add_db_signal('event-rebuild', self._do_close)
self._add_db_signal('event-delete', self.check_for_close) self._add_db_signal('event-delete', self.check_for_close)
self._add_db_signal('place-delete', self.place_delete)
self._add_db_signal('place-update', self.place_update)
def _setup_fields(self): def _setup_fields(self):
@@ -301,6 +303,24 @@ class EditEvent(EditPrimary):
cmp_obj = self.empty_object() cmp_obj = self.empty_object()
return cmp_obj.serialize(True)[1:] != self.obj.serialize()[1:] return cmp_obj.serialize(True)[1:] != self.obj.serialize()[1:]
def place_update(self, hndls):
''' Place changed outside of dialog, update text if its ours '''
handle = self.obj.get_place_handle()
if handle and handle in hndls:
place = self.db.get_place_from_handle(handle)
p_lbl = "%s [%s]" % (place.get_title(), place.gramps_id)
self.top.get_object("place").set_text(p_lbl)
def place_delete(self, hndls):
''' Place deleted outside of dialog, remove it if its ours'''
handle = self.obj.get_place_handle()
if handle and handle in hndls:
self.obj.set_place_handle(None)
self.top.get_object("place").set_markup(
self.place_field.EMPTY_TEXT)
self.place_field.set_button(False)
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# #
# Delete Query class # Delete Query class

View File

@@ -120,6 +120,8 @@ class EditEventRef(EditReference):
""" """
self._add_db_signal('event-rebuild', self.close) self._add_db_signal('event-rebuild', self.close)
self._add_db_signal('event-delete', self.check_for_close) self._add_db_signal('event-delete', self.check_for_close)
self._add_db_signal('place-delete', self.place_delete)
self._add_db_signal('place-update', self.place_update)
def _setup_fields(self): def _setup_fields(self):
@@ -279,3 +281,20 @@ class EditEventRef(EditReference):
self.update(self.source_ref,self.source) self.update(self.source_ref,self.source)
self.close() self.close()
def place_update(self, hndls):
''' Place changed outside of dialog, update text if its ours '''
handle = self.source.get_place_handle()
if handle and handle in hndls:
place = self.db.get_place_from_handle(handle)
p_lbl = "%s [%s]" % (place.get_title(), place.gramps_id)
self.top.get_object("eer_place").set_text(p_lbl)
def place_delete(self, hndls):
''' Place deleted outside of dialog, remove it if its ours'''
handle = self.source.get_place_handle()
if handle and handle in hndls:
self.source.set_place_handle(None)
self.top.get_object("eer_place").set_markup(
self.place_field.EMPTY_TEXT)
self.place_field.set_button(False)

View File

@@ -80,6 +80,7 @@ from gramps.gen.utils.db import (get_birth_or_fallback, get_death_or_fallback,
from ..selectors import SelectorFactory from ..selectors import SelectorFactory
from gramps.gen.utils.id import create_id from gramps.gen.utils.id import create_id
from gramps.gen.const import URL_MANUAL_SECT1 from gramps.gen.const import URL_MANUAL_SECT1
from ..dbguielement import DbGUIElement
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# #
@@ -97,7 +98,8 @@ _KP_ENTER = Gdk.keyval_from_name("KP_Enter")
_LEFT_BUTTON = 1 _LEFT_BUTTON = 1
_RIGHT_BUTTON = 3 _RIGHT_BUTTON = 3
class ChildEmbedList(EmbeddedList):
class ChildEmbedList(DbGUIElement, EmbeddedList):
""" """
The child embed list is specific to the Edit Family dialog, so it The child embed list is specific to the Edit Family dialog, so it
is contained here instead of in displaytabs. is contained here instead of in displaytabs.
@@ -139,9 +141,54 @@ class ChildEmbedList(EmbeddedList):
Create the object, storing the passed family value Create the object, storing the passed family value
""" """
self.family = family self.family = family
DbGUIElement.__init__(self, dbstate.db)
EmbeddedList.__init__(self, dbstate, uistate, track, _('Chil_dren'), EmbeddedList.__init__(self, dbstate, uistate, track, _('Chil_dren'),
ChildModel, share_button=True, move_buttons=True) ChildModel, share_button=True, move_buttons=True)
def _connect_db_signals(self):
"""
called on init of DbGUIElement, connect to db as required.
"""
#note: event-rebuild closes the editors, so no need to connect to it
self.callman.register_callbacks(
{'person-update': self.person_change, # change to person we track
'person-delete': self.person_delete, # delete of person we track
})
self.callman.connect_all(keys=['person'])
def person_change(self, *obj):
"""
Callback method called when a tracked person changes (description
changes...)
"""
self.rebuild()
def person_delete(self, hndls):
"""
Callback method called when a tracked person is deleted.
There are two possibilities:
* a tracked non-workgroup person is deleted, just rebuilding the view
will correct this.
* a workgroup person is deleted. The person must be removed from the
obj so that no inconsistent data is shown.
"""
for handle in hndls:
prefs = self.get_data()
ref_list = [pref.ref for pref in prefs]
indexlist = []
last = -1
while True:
try:
last = ref_list.index(handle, last + 1)
indexlist.append(last)
except ValueError:
break
#remove the deleted workgroup persons from the object
for index in reversed(indexlist):
del prefs[index]
#now rebuild the display tab
self.rebuild()
def get_popup_menu_items(self): def get_popup_menu_items(self):
return [ return [
(False, _('Edit child'), self.edit_child_button_clicked), (False, _('Edit child'), self.edit_child_button_clicked),
@@ -163,7 +210,10 @@ class ChildEmbedList(EmbeddedList):
Normally, get_data returns a list. However, we return family Normally, get_data returns a list. However, we return family
object here instead. object here instead.
""" """
return self.family.get_child_ref_list() prefs = self.family.get_child_ref_list()
self.callman.register_handles(
{'person': [eref.ref for eref in prefs]})
return prefs
def column_order(self): def column_order(self):
return [(1, 13), (1, 0), (1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (1, 6), return [(1, 13), (1, 0), (1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (1, 6),
@@ -413,11 +463,22 @@ class EditFamily(EditPrimary):
'event-update': self.topdata_updated, # change eg birth event fath 'event-update': self.topdata_updated, # change eg birth event fath
'event-rebuild': self.topdata_updated, 'event-rebuild': self.topdata_updated,
'event-delete': self.topdata_updated, # delete eg birth event fath 'event-delete': self.topdata_updated, # delete eg birth event fath
'person-update': self.topdata_updated, # change eg name of father 'person-update': self.topdata_updated, # change eg name of father
'person-delete' : self.person_delete, # mother/father deleted?
'person-rebuild': self._do_close, 'person-rebuild': self._do_close,
}) })
self.callman.connect_all(keys=['family', 'event', 'person']) self.callman.connect_all(keys=['family', 'event', 'person'])
def person_delete(self, handles):
""" This checks if mother/father is deleted, specifically when newly
added before data is saved """
for hndl in handles:
if self.obj.father_handle == hndl:
self.obj.father_handle = None
if self.obj.mother_handle == hndl:
self.obj.mother_handle = None
self.load_data()
def check_for_family_change(self, handles): def check_for_family_change(self, handles):
""" """
Callback for family-update signal Callback for family-update signal

View File

@@ -274,9 +274,7 @@ class EditPerson(EditPrimary):
self._add_db_signal('family-delete', self.family_change) self._add_db_signal('family-delete', self.family_change)
self._add_db_signal('family-update', self.family_change) self._add_db_signal('family-update', self.family_change)
self._add_db_signal('family-add', self.family_change) self._add_db_signal('family-add', self.family_change)
self._add_db_signal('event-update', self.event_updated)
self._add_db_signal('event-rebuild', self.event_updated) self._add_db_signal('event-rebuild', self.event_updated)
self._add_db_signal('event-delete', self.event_updated)
def family_change(self, handle_list=[]): def family_change(self, handle_list=[]):
""" """