gramps/gramps/gui/editors/displaytabs/eventembedlist.py

417 lines
15 KiB
Python

#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2000-2006 Donald N. Allingham
# Copyright (C) 2009 B. Malengier
#
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# $Id$
#-------------------------------------------------------------------------
#
# Python classes
#
#-------------------------------------------------------------------------
from gramps.gen.const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
from gi.repository import Gtk
from gi.repository import GObject
#-------------------------------------------------------------------------
#
# GRAMPS classes
#
#-------------------------------------------------------------------------
from gramps.gen.lib import Event, EventRef, EventRoleType, EventType
from gramps.gen.errors import WindowActiveError
from ...ddtargets import DdTargets
from .embeddedlist import TEXT_COL, MARKUP_COL, ICON_COL
from .groupembeddedlist import GroupEmbeddedList
from .eventrefmodel import EventRefModel
from ...dbguielement import DbGUIElement
from ...selectors import SelectorFactory
#-------------------------------------------------------------------------
#
# EventEmbedList
#
#-------------------------------------------------------------------------
class EventEmbedList(DbGUIElement, GroupEmbeddedList):
_HANDLE_COL = 8
_DND_TYPE = DdTargets.EVENTREF
_DND_EXTRA = DdTargets.EVENT
_WORKGROUP = EventRefModel._ROOTINDEX
_WORKNAME = _("Family")
_FATHNAME = _("Father")
_MOTHNAME = _("Mother")
_MSG = {
'add' : _('Add a new family event'),
'del' : _('Remove the selected family event'),
'edit' : _('Edit the selected family event or edit person'),
'share' : _('Share an existing event'),
'up' : _('Move the selected event upwards'),
'down' : _('Move the selected event downwards'),
}
#index = column in model. Value =
# (name, sortcol in model, width, markup/text, weigth_col
_column_names = [
(_('Description'), EventRefModel.COL_DESCR[0], 150, TEXT_COL, -1, None),
(_('Type'), EventRefModel.COL_TYPE[0], 120, TEXT_COL,
EventRefModel.COL_FONTWEIGHT[0], None),
(_('ID'), EventRefModel.COL_GID[0], 60, TEXT_COL, -1, None),
(_('Date'), EventRefModel.COL_SORTDATE[0], 150, MARKUP_COL, -1, None),
(_('Place'), EventRefModel.COL_PLACE[0], 240, 0, -1, None),
(_('Role'), EventRefModel.COL_ROLE[0], 80, TEXT_COL, -1, None),
(_('Main Participants'), EventRefModel.COL_PARTIC[0], 240, TEXT_COL,
EventRefModel.COL_FONTWEIGHT[0], None),
None,
None,
None,
(_('Age'), EventRefModel.COL_SORTAGE[0], 60, TEXT_COL, -1, None),
None,
(_('Private'), EventRefModel.COL_PRIVATE[0], 30, ICON_COL, -1, 'gramps-lock')
]
def __init__(self, dbstate, uistate, track, obj, build_model=EventRefModel, **kwargs):
self.obj = obj
self._groups = []
self._data = []
DbGUIElement.__init__(self, dbstate.db)
GroupEmbeddedList.__init__(self, dbstate, uistate, track, _('_Events'),
build_model, share_button=True,
move_buttons=True, **kwargs)
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(
{'event-update': self.event_change, #change to an event we track
'event-delete': self.event_delete, #delete of event we track
})
self.callman.connect_all(keys=['event'])
def event_change(self, *obj):
"""
Callback method called when a tracked event changes (description
changes, source added, ...)
Note that adding an event
"""
self.rebuild_callback()
def event_delete(self, obj):
"""
Callback method called when a tracked event is deleted.
There are two possibilities:
* a tracked non-workgroup event is deleted, just rebuilding the view
will correct this.
* a workgroup event is deleted. The event must be removed from the obj
so that no inconsistent data is shown.
"""
for handle in obj:
refs = self.get_data()[self._WORKGROUP]
ref_list = [eref.ref for eref in refs]
indexlist = []
last = 0
while True:
try:
last = ref_list.index(handle)
indexlist.append(last)
except ValueError:
break
#remove the deleted workgroup events from the object
for index in indexlist.reverse():
del refs[index]
#now rebuild the display tab
self.rebuild_callback()
def get_ref_editor(self):
from .. import EditFamilyEventRef
return EditFamilyEventRef
def get_icon_name(self):
return 'gramps-event'
def get_data(self):
#family events
if not self._data or self.changed:
self._data = [self.obj.get_event_ref_list()]
self._groups = [(self.obj.get_handle(), self._WORKNAME, '')]
#father events
fhandle = self.obj.get_father_handle()
if fhandle:
fdata = self.dbstate.db.get_person_from_handle(fhandle).\
get_event_ref_list()
if fdata:
self._groups.append((fhandle, self._FATHNAME, ''))
self._data.append(fdata)
#mother events
mhandle = self.obj.get_mother_handle()
if mhandle:
mdata = self.dbstate.db.get_person_from_handle(mhandle).\
get_event_ref_list()
if mdata:
self._groups.append((mhandle, self._MOTHNAME, ''))
self._data.append(mdata)
#we register all events that need to be tracked
for group in self._data:
self.callman.register_handles(
{'event': [eref.ref for eref in group]})
self.changed = False
return self._data
def groups(self):
"""
Return the (group key, group name)s in the order as given by get_data()
"""
return self._groups
def column_order(self):
"""
The columns to show as a tuple containing
tuples (show/noshow, model column)
"""
return ((1, 1), # type
(1, 6), # main participants
(1, 3), # date
(1, 4), # place
(1, 0), # description
(1, 12), # private
(1, 5), # role
(1, 2), # gramps id
(1, 10), # age
)
def default_types(self):
return [
EventType(EventType.MARRIAGE),
EventType(EventType.DIVORCE),
]
def default_type(self):
type_list = []
# combine return info into a single flat sequence
event = None
for event_ref in self.get_data()[0]:
event = self.dbstate.db.get_event_from_handle(event_ref.ref)
type_list.append(event.get_type())
_std_types = self.default_types()
for etype in _std_types:
if etype not in type_list:
return EventType(etype)
return _std_types[0]
def default_role(self):
return EventRoleType(EventRoleType.FAMILY)
def add_button_clicked(self, obj):
try:
ref = EventRef()
event = Event()
ref.set_role(self.default_role())
event.set_type(self.default_type())
self.get_ref_editor()(
self.dbstate, self.uistate, self.track,
event, ref, self.object_added)
except WindowActiveError:
pass
def __blocked_text(self):
"""
Return the common text used when eventref cannot be edited
"""
return _("This event reference cannot be edited at this time. "
"Either the associated event is already being edited "
"or another event reference that is associated with "
"the same event is being edited.\n\nTo edit this event "
"reference, you need to close the event.")
def share_button_clicked(self, obj):
SelectEvent = SelectorFactory('Event')
sel = SelectEvent(self.dbstate, self.uistate, self.track)
event = sel.run()
if event:
try:
ref = EventRef()
ref.set_role(self.default_role())
self.get_ref_editor()(
self.dbstate, self.uistate, self.track,
event, ref, self.object_added)
except WindowActiveError:
from ...dialog import WarningDialog
WarningDialog(_("Cannot share this reference"),
self.__blocked_text())
def edit_button_clicked(self, obj):
ref = self.get_selected()
if ref and ref[1] is not None and ref[0] == self._WORKGROUP:
event = self.dbstate.db.get_event_from_handle(ref[1].ref)
try:
self.get_ref_editor()(
self.dbstate, self.uistate, self.track,
event, ref[1], self.object_edited)
except WindowActiveError:
from ...dialog import WarningDialog
WarningDialog(_("Cannot edit this reference"),
self.__blocked_text())
elif ref and ref[0] != self._WORKGROUP:
#bring up family editor
key = self._groups[ref[0]][0]
self.editnotworkgroup(key)
def object_added(self, reference, primary):
reference.ref = primary.handle
data = self.get_data()[self._WORKGROUP]
data.append(reference)
self.callman.register_handles({'event': [primary.handle]})
self.changed = True
self.rebuild()
GObject.idle_add(self.tree.scroll_to_cell,
(self._WORKGROUP, len(data) - 1))
def object_edited(self, ref, event):
"""
Called as callback after eventref has been edited.
Note that if the event changes too (so not only the ref data), then
an event-update signal from the database will also be raised, and the
rebuild done here will not be needed. There is no way to avoid this ...
"""
self.changed = True
self.rebuild()
def get_popup_menu_items(self):
if self._tmpgroup == self._WORKGROUP:
return GroupEmbeddedList.get_popup_menu_items(self)
else:
return [
(True, True, Gtk.STOCK_ADD, self.add_button_clicked),
(False,True, Gtk.STOCK_EDIT, self.edit_button_clicked),
]
def _non_native_change(self):
"""
handle change request of non native data
"""
from ...dialog import WarningDialog
WarningDialog(
_("Cannot change Person"),
_("You cannot change Person events in the Family Editor")
)
def _handle_drag(self, row, obj):
"""
An event reference that is from a drag and drop has
an unknown event reference role, so it cannot just be added,
it needs to be edited and confirmed
"""
if row[0] == self._WORKGROUP:
from gramps.gen.lib import EventRoleType
obj.set_role((EventRoleType.UNKNOWN,''))
#add the event
GroupEmbeddedList._handle_drag(self, row, obj)
#call editor to set new eventref
event = self.dbstate.db.get_event_from_handle(obj.ref)
try:
self.get_ref_editor()(self.dbstate, self.uistate, self.track,
event, obj, self.object_edited)
except WindowActiveError:
from ...dialog import WarningDialog
WarningDialog(
_("Cannot edit this reference"),
_("This event reference cannot be edited at this time. "
"Either the associated event is already being edited "
"or another event reference that is associated with "
"the same event is being edited.\n\nTo edit this event "
"reference, you need to close the event.")
)
else:
self.dropnotworkgroup(row, obj)
def handle_extra_type(self, objtype, obj):
try:
ref = EventRef()
event = self.dbstate.db.get_event_from_handle(obj)
ref.set_role(self.default_role())
self.get_ref_editor()(
self.dbstate, self.uistate, self.track,
event, ref, self.object_added)
except WindowActiveError:
pass
def editnotworkgroup(self, key):
"""
Edit non native event in own editor
"""
person = self.dbstate.db.get_person_from_handle(key)
try:
from .. import EditPerson
EditPerson(self.dbstate, self.uistate, [], person)
except WindowActiveError:
pass
def dropnotworkgroup(self, row, obj):
"""
Drop of obj on row that is not WORKGROUP
"""
self._non_native_change()
def move_away_work(self, row_from, row_to, obj):
"""
move from the workgroup to a not workgroup
we allow to change the default name like this
"""
self.dropnotworkgroup(None, None)
def move_to_work(self, row_from, row_to, obj):
"""
move from a non workgroup to the workgroup
handle this as a drop from clipboard
"""
self._handle_drag(row_to, obj)
def _move_up_notwork(self, row_from, obj, selmethod=None):
"""
move up outside of workgroup
"""
self._non_native_change()
def _move_down_notwork(self, row_from, obj, selmethod=None):
"""
move up outside of workgroup
"""
self._non_native_change()
def post_rebuild(self, prebuildpath):
"""
Allow post rebuild specific handling.
@param prebuildpath: path selected before rebuild, None if none
@type prebuildpath: tree path
"""
self.tree.expand_all()
if not prebuildpath is None:
self.selection.select_path(prebuildpath)