diff --git a/src/DisplayTabs/_EventEmbedList.py b/src/DisplayTabs/_EventEmbedList.py index 6682a273d..ad4d2757a 100644 --- a/src/DisplayTabs/_EventEmbedList.py +++ b/src/DisplayTabs/_EventEmbedList.py @@ -26,6 +26,7 @@ # #------------------------------------------------------------------------- from gettext import gettext as _ +import gtk #------------------------------------------------------------------------- # @@ -35,7 +36,7 @@ from gettext import gettext as _ import gen.lib import Errors from DdTargets import DdTargets -from _EmbeddedList import EmbeddedList +from _GroupEmbeddedList import GroupEmbeddedList from _EventRefModel import EventRefModel #------------------------------------------------------------------------- @@ -43,16 +44,21 @@ from _EventRefModel import EventRefModel # EventEmbedList # #------------------------------------------------------------------------- -class EventEmbedList(EmbeddedList): +class EventEmbedList(GroupEmbeddedList): _HANDLE_COL = 7 _DND_TYPE = DdTargets.EVENTREF _DND_EXTRA = DdTargets.EVENT + _WORKGROUP = EventRefModel._ROOTINDEX + + _WORKNAME = _("Family Events") + _FATHNAME = _("Events father") + _MOTHNAME = _("Events mother") _MSG = { - 'add' : _('Add a new event'), - 'del' : _('Remove the selected event'), - 'edit' : _('Edit the selected event'), + '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'), @@ -61,18 +67,26 @@ class EventEmbedList(EmbeddedList): #index = column in model. Value = # (name, sortcol in model, width, markup/text, weigth_col _column_names = [ - (_('Type'), 0, 100, 0, -1), - (_('Description'), 1, 175, 0, -1), - (_('ID'), 2, 60, 0, -1), - (_('Date'), 6, 150, 1, -1), - (_('Place'), 4, 140, 0, -1), - (_('Role'), 5, 80, 0, -1), + (_('Description'), -1, 195, 0, EventRefModel.COL_FONTWEIGHT[0]), + (_('Type'), EventRefModel.COL_TYPE[0], 100, 0, + EventRefModel.COL_FONTWEIGHT[0]), + (_('ID'), EventRefModel.COL_GID[0], 60, 0, + EventRefModel.COL_FONTWEIGHT[0]), + (_('Date'), EventRefModel.COL_SORTDATE[0], 150, 1, -1), + (_('Place'), EventRefModel.COL_PLACE[0], 140, 0, -1), + (_('Role'), EventRefModel.COL_ROLE[0], 80, 0, -1), + None, + None, + None ] - - def __init__(self, dbstate, uistate, track, obj): + + def __init__(self, dbstate, uistate, track, obj, + build_model=EventRefModel): self.obj = obj - EmbeddedList.__init__(self, dbstate, uistate, track, _('_Events'), - EventRefModel, share_button=True, + self._groups = [] + self._data = [] + GroupEmbeddedList.__init__(self, dbstate, uistate, track, _('_Events'), + build_model, share_button=True, move_buttons=True) def get_ref_editor(self): @@ -83,9 +97,41 @@ class EventEmbedList(EmbeddedList): return 'gramps-event' def get_data(self): - return self.obj.get_event_ref_list() + #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) + 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, 0), (1, 1), (1, 2), (1, 3), (1, 4), (1, 5)) def default_type(self): @@ -110,7 +156,7 @@ class EventEmbedList(EmbeddedList): from Selectors import selector_factory SelectEvent = selector_factory('Event') - sel = SelectEvent(self.dbstate,self.uistate,self.track) + sel = SelectEvent(self.dbstate, self.uistate, self.track) event = sel.run() if event: try: @@ -124,12 +170,12 @@ class EventEmbedList(EmbeddedList): def edit_button_clicked(self, obj): ref = self.get_selected() - if ref: - event = self.dbstate.db.get_event_from_handle(ref.ref) + 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, self.object_edited) + event, ref[1], self.object_edited) except Errors.WindowActiveError: from QuestionDialog import WarningDialog WarningDialog( @@ -140,10 +186,14 @@ class EventEmbedList(EmbeddedList): "the same event is being edited.\n\nTo edit this event " "reference, you need to close the event.") ) + 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 - self.get_data().append(reference) + self.get_data()[self._WORKGROUP].append(reference) self.changed = True self.rebuild() @@ -151,23 +201,44 @@ class EventEmbedList(EmbeddedList): self.changed = True self.rebuild() + def get_popup_menu_items(self): + if self._tmpgroup == self._WORKGROUP: + 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 QuestionDialog 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 + an unknown event reference role, so it cannot just be added, + it needs to be edited and confirmed """ - from gen.lib import EventRoleType - - obj.set_role((EventRoleType.UNKNOWN,'')) - EmbeddedList._handle_drag(self, row, obj) - - 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 Errors.WindowActiveError: - from QuestionDialog import WarningDialog - WarningDialog( + if row[0] == self._WORKGROUP: + from 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 Errors.WindowActiveError: + from QuestionDialog import WarningDialog + WarningDialog( _("Cannot edit this reference"), _("This event reference cannot be edited at this time. " "Either the associated event is already being edited " @@ -175,6 +246,8 @@ class EventEmbedList(EmbeddedList): "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: @@ -187,3 +260,48 @@ class EventEmbedList(EmbeddedList): except Errors.WindowActiveError: pass + def editnotworkgroup(self, key): + """ + Edit non native event in own editor + """ + from Editors import EditPerson + person = self.dbstate.db.get_person_from_handle(key) + try: + EditPerson(self.dbstate, self.uistate, [], person) + except Errors.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): + self.tree.expand_all() diff --git a/src/DisplayTabs/_EventRefModel.py b/src/DisplayTabs/_EventRefModel.py index 0f6f95fa0..96ce060ca 100644 --- a/src/DisplayTabs/_EventRefModel.py +++ b/src/DisplayTabs/_EventRefModel.py @@ -20,12 +20,20 @@ # $Id$ +#------------------------------------------------------------------------- +# +# python +# +#------------------------------------------------------------------------- +from gettext import gettext as _ + #------------------------------------------------------------------------- # # GTK libraries # #------------------------------------------------------------------------- import gtk +from pango import WEIGHT_NORMAL, WEIGHT_BOLD import cgi #------------------------------------------------------------------------- @@ -48,22 +56,65 @@ invalid_date_format = Config.get(Config.INVALID_DATE_FORMAT) # EventRefModel # #------------------------------------------------------------------------- -class EventRefModel(gtk.ListStore): +class EventRefModel(gtk.TreeStore): + #index of the working group + _ROOTINDEX = 0 + _GROUPSTRING = _('%(groupname)s - %(groupnumber)d') + + COL_DESCR = (0, str) + COL_TYPE = (1, str) + COL_GID = (2, str) + COL_DATE = (3, str) + COL_PLACE = (4, str) + COL_ROLE = (5, str) + COL_SORTDATE = (6, str) + COL_EVENTREF = (7, object) + COL_FONTWEIGHT = (8, int) + + COLS = (COL_DESCR, COL_TYPE, COL_GID, COL_DATE, COL_PLACE, COL_ROLE, + COL_SORTDATE, COL_EVENTREF, COL_FONTWEIGHT) - def __init__(self, event_list, db): - gtk.ListStore.__init__(self, str, str, str, str, str, str, str, object) + def __init__(self, event_list, db, groups): + """ + @param event_list: A list of lists, every entry is a group, the entries + in a group are the data that needs to be shown subordinate to the + group + @param db: a database objects that can be used to obtain info + @param groups: a list of (key, name) tuples. key is a key for the group + that might be used. name is the name for the group. + """ + typeobjs = (x[1] for x in self.COLS) + gtk.TreeStore.__init__(self, *typeobjs) self.db = db - for event_ref in event_list: - event = db.get_event_from_handle(event_ref.ref) - self.append(row=[str(event.get_type()), - event.get_description(), - event.get_gramps_id(), - self.column_date(event_ref), - self.column_place(event_ref), - self.column_role(event_ref), - self.column_sort_date(event_ref), - event_ref - ]) + self.groups = groups + for index, group in enumerate(event_list): + parentiter = self.append(None, row=self.row_group(index, group)) + for eventref in group: + event = db.get_event_from_handle(eventref.ref) + self.append(parentiter, row = self.row(index, eventref, event)) + + def row_group(self, index, group): + name = self.namegroup(index, len(group)) + return [name, '', '', '', '', '', '', (index, None), WEIGHT_BOLD] + + def namegroup(self, groupindex, length): + return self._GROUPSTRING % {'groupname': self.groups[groupindex][1], + 'groupnumber': length} + + def row(self, index, eventref, event): + return [event.get_description(), + str(event.get_type()), + event.get_gramps_id(), + self.column_date(eventref), + self.column_place(eventref), + self.column_role(eventref), + self.column_sort_date(eventref), + (index, eventref), + self.colweight(index), + ] + + def colweight(self, index): + return WEIGHT_NORMAL def column_role(self, event_ref): return str(event_ref.get_role()) diff --git a/src/DisplayTabs/_PersonEventEmbedList.py b/src/DisplayTabs/_PersonEventEmbedList.py index c6872cdcc..38bf6f1c0 100644 --- a/src/DisplayTabs/_PersonEventEmbedList.py +++ b/src/DisplayTabs/_PersonEventEmbedList.py @@ -20,13 +20,22 @@ # $Id$ +#------------------------------------------------------------------------- +# +# python +# +#------------------------------------------------------------------------- +from gettext import gettext as _ + #------------------------------------------------------------------------- # # GRAMPS classes # #------------------------------------------------------------------------- import gen.lib +from BasicUtils import name_displayer from _EventEmbedList import EventEmbedList +from _EventRefModel import EventRefModel _std_types = [ gen.lib.EventType(gen.lib.EventType.BIRTH), @@ -40,12 +49,53 @@ _std_types = [ # #------------------------------------------------------------------------- class PersonEventEmbedList(EventEmbedList): + + _WORKNAME = _("Personal Events") + _FAMNAME = _("With %(namepartner)s (%(famid)s)") + _UNKNOWNNAME = _("") + + _MSG = { + 'add' : _('Add a new personal event'), + 'del' : _('Remove the selected personal event'), + 'edit' : _('Edit the selected personal event or edit family'), + 'share' : _('Share an existing event'), + 'up' : _('Move the selected event upwards or change family order'), + 'down' : _('Move the selected event downwards or change family order'), + } - def __init__(self, dbstate, uistate, track, obj): - EventEmbedList.__init__(self, dbstate, uistate, track, obj) + def __init__(self, dbstate, uistate, track, obj): + EventEmbedList.__init__(self, dbstate, uistate, track, obj, + build_model=EventRefModel ) def get_data(self): - return self.obj.get_event_ref_list() + if not self._data or self.changed: + self._data = [self.obj.get_event_ref_list()] + self._groups = [(self.obj.get_handle(), self._WORKNAME)] + # own family events + family_handle_list = self.obj.get_family_handle_list() + if family_handle_list: + for family_handle in family_handle_list: + family = self.dbstate.db.get_family_from_handle(family_handle) + father_handle = family.get_father_handle() + mother_handle = family.get_mother_handle() + if self.obj.get_handle() == father_handle: + handlepartner = mother_handle + else: + handlepartner = father_handle + if handlepartner: + partner = self.dbstate.db.get_person_from_handle( + handlepartner) + groupname = name_displayer.display(partner) + else: + groupname = self._UNKNOWNNAME + self._data.append(family.get_event_ref_list()) + self._groups.append((family_handle, self._FAMNAME % { + 'namepartner': groupname, + 'famid': family.get_gramps_id() + })) + self.changed = False + + return self._data def default_role(self): return gen.lib.EventRoleType(gen.lib.EventRoleType.PRIMARY) @@ -56,7 +106,7 @@ class PersonEventEmbedList(EventEmbedList): # combine return info into a single flat sequence event = None - for event_ref in self.get_data(): + 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()) @@ -69,3 +119,62 @@ class PersonEventEmbedList(EventEmbedList): from Editors import EditEventRef return EditEventRef + def editnotworkgroup(self, key): + """ + Edit non native event in own editor + """ + from Editors import EditFamily + family = self.dbstate.db.get_family_from_handle(key) + try: + EditFamily(self.dbstate, self.uistate, [], family) + except Errors.WindowActiveError: + pass + + def _non_native_change(self): + """ + handle change request of non native data + """ + from QuestionDialog import WarningDialog + WarningDialog( + _("Cannot change Family"), + _("You cannot change Family events in the Person Editor") + ) + def _move_up_group(self, groupindex): + """ + move up pressed on the group + If on a family group, allow to reorder the order of families + as connected to this person. + """ + ref = self.get_selected() + if not ref or ref[1]: + return + if ref[0] == 1: + #cannot move up, already first family + return + #change family list and rebuild the tabpage + index = ref[0] - 1 + flist = self.obj.get_family_handle_list() + handle = flist.pop(index) + flist.insert(index-1, handle) + self.changed = True + self.rebuild() + + def _move_down_group(self, groupindex): + """ + move down pressed on the group + If on a family group, allow to reorder the order of families + as connected to this person. + """ + ref = self.get_selected() + if not ref or ref[1]: + return + if ref[0] == len(self._groups)-1: + #cannot move down, already last family + return + #change family list and rebuild the tabpage + index = ref[0] -1 + flist = self.obj.get_family_handle_list() + handle = flist.pop(index) + flist.insert(index+1, handle) + self.changed = True + self.rebuild() diff --git a/src/Editors/_EditFamily.py b/src/Editors/_EditFamily.py index 72a2c35d9..260f2388a 100644 --- a/src/Editors/_EditFamily.py +++ b/src/Editors/_EditFamily.py @@ -484,6 +484,8 @@ class EditFamily(EditPrimary): def event_updated(self, obj): self.load_data() + #place in event might have changed, or person event shown in the list + self.event_list.rebuild_callback() def show_buttons(self): """ diff --git a/src/Editors/_EditPerson.py b/src/Editors/_EditPerson.py index 602416a02..4322687bb 100644 --- a/src/Editors/_EditPerson.py +++ b/src/Editors/_EditPerson.py @@ -186,6 +186,9 @@ class EditPerson(EditPrimary): self._add_db_signal('family-delete', 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('event-update', 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=[]): """ @@ -213,6 +216,14 @@ class EditPerson(EditPrimary): self.obj.set_family_handle_list(person.get_family_handle_list()) self.obj.set_parent_family_handle_list( person.get_parent_family_handle_list()) + #a family groupname in event_list might need to be changed + # we just rebuild the view always + self.event_list.rebuild_callback() + + def event_updated(self, obj): + #place in event might have changed, or person event shown in the list + # we just rebuild the view always + self.event_list.rebuild_callback() def _setup_fields(self): """