diff --git a/ChangeLog b/ChangeLog index a62d502c5..d52d55d0f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2007-09-08 Benny Malengier + * src/DataViews/_RelationView.py : expand/collapse, edit button on/off bug fix + * src/GrampsWidgets.py : Expand widget, changes link labels + * src/DisplayState.py : history bugs + * src/ViewManager.py : history bugs + * src/PageView.py : history bugs + Based on patch of Douglas S. Blank , GEPS-2 + Still some strange things in history, but better + 2007-09-07 Don Allingham * various: const.py changes diff --git a/src/DataViews/_RelationView.py b/src/DataViews/_RelationView.py index 9fdb25142..855c0a884 100644 --- a/src/DataViews/_RelationView.py +++ b/src/DataViews/_RelationView.py @@ -65,13 +65,13 @@ _GenderCode = { } _NAME_START = 0 -_LABEL_START = 1 -_LABEL_STOP = 2 +_LABEL_START = 0 +_LABEL_STOP = 1 _DATA_START = _LABEL_STOP _DATA_STOP = _DATA_START+1 _BTN_START = _DATA_STOP _BTN_STOP = _BTN_START+2 -_PLABEL_START = 2 +_PLABEL_START = 1 _PLABEL_STOP = _PLABEL_START+1 _PDATA_START = _PLABEL_STOP _PDATA_STOP = _PDATA_START+2 @@ -83,12 +83,12 @@ _CDATA_START = _CLABEL_STOP _CDATA_STOP = _CDATA_START+1 _CDTLS_START = _CDATA_START _CDTLS_STOP = _CDTLS_START+1 -_ALABEL_START = 1 +_ALABEL_START = 0 _ALABEL_STOP = _ALABEL_START+1 _ADATA_START = _ALABEL_STOP _ADATA_STOP = _ADATA_START+3 -_SDATA_START = 3 -_SDATA_STOP = 5 +_SDATA_START = 2 +_SDATA_STOP = 4 class AttachList: @@ -129,11 +129,12 @@ class RelationshipView(PageView.PersonNavView): Config.client.notify_add("/apps/gramps/preferences/relation-shade", self.shade_update) - Config.client.notify_add("/apps/gramps/interface/editbutton", + Config.client.notify_add("/apps/gramps/interface/releditbtn", self.config_update) Config.client.notify_add("/apps/gramps/interface/toolbar-on", self.shade_update) self.reorder_sensitive = False + self.collapsed_items = {} def set_active(self): PageView.PersonNavView.set_active(self) @@ -337,7 +338,7 @@ class RelationshipView(PageView.PersonNavView): None, None, self.siblings_toggle, self.show_siblings) - self.order_action.set_sensitive(self.reorder_sensitive) + self.order_action.set_sensitive(self.reorder_sensitive) self.family_action.set_sensitive(False) def siblings_toggle(self, obj): @@ -426,22 +427,22 @@ class RelationshipView(PageView.PersonNavView): if family_handle_list: for family_handle in family_handle_list: if family_handle: - self.write_parents(family_handle) + self.write_parents(family_handle, person) else: - self.write_label("%s:" % _('Parents'), None, True) + self.write_label("%s:" % _('Parents'), None, True, person) self.row += 1 family_handle_list = person.get_family_handle_list() - + if not self.reorder_sensitive: self.reorder_sensitive = len(family_handle_list)> 1 if family_handle_list: for family_handle in family_handle_list: if family_handle: - self.write_family(family_handle) + self.write_family(family_handle, person) else: - self.write_label("%s:" % _('Family'), None, False) + self.write_label("%s:" % _('Family'), None, False, person) self.row += 1 self.row = 0 @@ -472,7 +473,7 @@ class RelationshipView(PageView.PersonNavView): x1 = d[2] if not found: if x0 > 4: - x0 -= 1 + x0 -= 1 if x1 > 4: x1 -= 1 self.child.attach(d[0], x0, x1, d[3], d[4], d[5], d[6]) @@ -495,7 +496,7 @@ class RelationshipView(PageView.PersonNavView): table = gtk.Table(2, 3) table.set_col_spacings(12) table.set_row_spacings(6) - + # name and edit button name = name_displayer.display(person) fmt = '%s' @@ -631,9 +632,26 @@ class RelationshipView(PageView.PersonNavView): self.row, self.row+1) self.row += 1 - def write_label(self, title, family, is_parent): - msg = "%s" % cgi.escape(title) - self.attach.attach(GrampsWidgets.MarkupLabel(msg), + def write_label(self, title, family, is_parent, person = None): + msg = '%s' % cgi.escape(title) + hbox = gtk.HBox() + label = GrampsWidgets.MarkupLabel(msg) + # Draw the collapse/expand button: + if family != None: + if self.check_collapsed(person, family.handle): + arrow = GrampsWidgets.ExpandCollapseArrow(True, + self.expand_collapse_press, + (person, family.handle)) + else: + arrow = GrampsWidgets.ExpandCollapseArrow(False, + self.expand_collapse_press, + (person, family.handle)) + else : + arrow = gtk.Arrow(gtk.ARROW_RIGHT, gtk.SHADOW_OUT) + hbox.pack_start(arrow, False) + + hbox.pack_start(label, False) + self.attach.attach(hbox, _LABEL_START, _LABEL_STOP, self.row, self.row+1, gtk.SHRINK|gtk.FILL) @@ -645,92 +663,165 @@ class RelationshipView(PageView.PersonNavView): _DATA_START, _DATA_STOP, self.row, self.row+1, gtk.SHRINK|gtk.FILL) - hbox = gtk.HBox() - hbox.set_spacing(12) - if is_parent: - call_fcn = self.add_parent_family - del_fcn = self.delete_parent_family - add_msg = _('Add parents') - sel_msg = _('Select existing parents') - edit_msg = _('Edit parents') - del_msg = _('Remove parents') + if family and self.check_collapsed(person, family.handle): + # show family names later + pass else: - add_msg = _('Add spouse') - sel_msg = _('Select spouse') - edit_msg = _('Edit family') - del_msg = _('Remove from family') - call_fcn = self.add_family - del_fcn = self.delete_family - - if not self.toolbar_visible and not self.dbstate.db.readonly: - # Show edit-Buttons if toolbar is not visible - if self.reorder_sensitive: - add = GrampsWidgets.IconButton(self.reorder, None, - gtk.STOCK_SORT_ASCENDING) - self.tooltips.set_tip(add, _('Reorder families')) - hbox.pack_start(add, False) - - add = GrampsWidgets.IconButton(call_fcn, None, gtk.STOCK_ADD) - self.tooltips.set_tip(add, add_msg) - hbox.pack_start(add, False) - + hbox = gtk.HBox() + hbox.set_spacing(12) if is_parent: - add = GrampsWidgets.IconButton(self.select_family, None, gtk.STOCK_INDEX) - self.tooltips.set_tip(add, sel_msg) + call_fcn = self.add_parent_family + del_fcn = self.delete_parent_family + add_msg = _('Add parents') + sel_msg = _('Select existing parents') + edit_msg = _('Edit parents') + del_msg = _('Remove parents') + else: + add_msg = _('Add spouse') + sel_msg = _('Select spouse') + edit_msg = _('Edit family') + del_msg = _('Remove from family') + call_fcn = self.add_family + del_fcn = self.delete_family + + if not self.toolbar_visible and not self.dbstate.db.readonly: + # Show edit-Buttons if toolbar is not visible + if self.reorder_sensitive: + add = GrampsWidgets.IconButton(self.reorder, None, + gtk.STOCK_SORT_ASCENDING) + self.tooltips.set_tip(add, _('Reorder families')) + hbox.pack_start(add, False) + + add = GrampsWidgets.IconButton(call_fcn, None, gtk.STOCK_ADD) + self.tooltips.set_tip(add, add_msg) hbox.pack_start(add, False) - if family: - edit = GrampsWidgets.IconButton(self.edit_family, family.handle, - gtk.STOCK_EDIT) - self.tooltips.set_tip(edit, edit_msg) - hbox.pack_start(edit, False) - if not self.dbstate.db.readonly: - delete = GrampsWidgets.IconButton(del_fcn, family.handle, - gtk.STOCK_REMOVE) - self.tooltips.set_tip(delete, del_msg) - hbox.pack_start(delete, False) - self.attach.attach(hbox, _BTN_START, _BTN_STOP, self.row, self.row+1) + if is_parent: + add = GrampsWidgets.IconButton(self.select_family, None, gtk.STOCK_INDEX) + self.tooltips.set_tip(add, sel_msg) + hbox.pack_start(add, False) + + if family: + edit = GrampsWidgets.IconButton(self.edit_family, family.handle, + gtk.STOCK_EDIT) + self.tooltips.set_tip(edit, edit_msg) + hbox.pack_start(edit, False) + if not self.dbstate.db.readonly: + delete = GrampsWidgets.IconButton(del_fcn, family.handle, + gtk.STOCK_REMOVE) + self.tooltips.set_tip(delete, del_msg) + hbox.pack_start(delete, False) + self.attach.attach(hbox, _BTN_START, _BTN_STOP, self.row, self.row+1) self.row += 1 ###################################################################### - def write_parents(self, family_handle): + def write_parents(self, family_handle, person = None): family = self.dbstate.db.get_family_from_handle(family_handle) if not family: return - self.write_label("%s:" % _('Parents'), family, True) - self.write_person(_('Father'), family.get_father_handle()) - self.write_person(_('Mother'), family.get_mother_handle()) - - if self.show_siblings: + if person and self.check_collapsed(person, family_handle): + # don't show rest + self.write_label("%s:" % _('Parents'), family, True, person) + self.row -= 1 # back up one row for summary names active = self.dbstate.active.handle - - child_list = [ref.ref for ref in family.get_child_ref_list()\ + child_list = [ref.ref for ref in family.get_child_ref_list() if ref.ref != active] - if child_list: - eventbox = gtk.EventBox() + count = len(child_list) + else: + count = 0 + if count > 1 : + childmsg = _(" (%d siblings)") % count + elif count == 1 : + gender = self.dbstate.db.get_person_from_handle(\ + child_list[0]).gender + if gender == RelLib.Person.MALE : + childmsg = _(" (1 brother)") + elif gender == RelLib.Person.FEMALE : + childmsg = _(" (1 sister)") + else : + childmsg = _(" (1 sibling)") + else : + childmsg = _(" (only child)") + box = self.get_people_box(family.get_father_handle(), + family.get_mother_handle(), + post_msg=childmsg) + eventbox = gtk.EventBox() + if self.use_shade: + eventbox.modify_bg(gtk.STATE_NORMAL, self.color) + eventbox.add(box) + self.attach.attach( + eventbox, _PDATA_START, _PDATA_STOP, + self.row, self.row+1) + self.row += 1 # now advance it + else: + self.write_label("%s:" % _('Parents'), family, True, person) + self.write_person(_('Father'), family.get_father_handle()) + self.write_person(_('Mother'), family.get_mother_handle()) + + if self.show_siblings: + active = self.dbstate.active.handle + + child_list = [ref.ref for ref in family.get_child_ref_list()\ + if ref.ref != active] + + if child_list: + eventbox = gtk.EventBox() + if self.use_shade: + eventbox.modify_bg(gtk.STATE_NORMAL, self.color) + vbox = gtk.VBox() + label_cell = self.build_label_cell(_('Siblings')) + label_cell.set_alignment(0, 0) + self.attach.attach( + label_cell, _CLABEL_START, _CLABEL_STOP, self.row, + self.row+1, xoptions=gtk.FILL|gtk.SHRINK, + yoptions=gtk.FILL) + + i = 1 + for child_handle in child_list: + self.write_child(vbox, child_handle, i) + i += 1 + + eventbox.add(vbox) + self.attach.attach( + eventbox, _CDATA_START, _CDATA_STOP, self.row, + self.row+1) + + self.row += 1 + + def get_people_box(self, *handles, **kwargs): + vbox = gtk.HBox() + initial_name = True + for handle in handles: + if not initial_name: + link_label = gtk.Label(" %s " % _('and')) + link_label.show() + vbox.pack_start(link_label, expand=False) + initial_name = False + if handle: + name = self.get_name(handle, True) + link_label = GrampsWidgets.LinkLabel(name, + self.button_press, handle) if self.use_shade: - eventbox.modify_bg(gtk.STATE_NORMAL, self.color) - vbox = gtk.VBox() - label_cell = self.build_label_cell(_('Siblings')) - label_cell.set_alignment(0, 0) - self.attach.attach( - label_cell, _CLABEL_START, _CLABEL_STOP, self.row, - self.row+1, xoptions=gtk.FILL|gtk.SHRINK, - yoptions=gtk.FILL) - - i = 1 - for child_handle in child_list: - self.write_child(vbox, child_handle, i) - i += 1 - - eventbox.add(vbox) - self.attach.attach( - eventbox, _CDATA_START, _CDATA_STOP, self.row, - self.row+1) - - self.row += 1 + link_label.modify_bg(gtk.STATE_NORMAL, self.color) + if Config.get(Config.RELEDITBTN): + button = GrampsWidgets.IconButton(self.edit_button_press, + handle) + self.tooltips.set_tip(button, _('Edit %s') % name[0]) + else: + button = None + vbox.pack_start(GrampsWidgets.LinkBox(link_label, button), + expand=False) + else: + link_label = gtk.Label(_('Unknown')) + link_label.show() + vbox.pack_start(link_label, expand=False) + if "post_msg" in kwargs and kwargs["post_msg"]: + link_label = gtk.Label(kwargs["post_msg"]) + link_label.show() + vbox.pack_start(link_label, expand=False) + return vbox def write_person(self, title, handle): if title: @@ -793,8 +884,14 @@ class RelationshipView(PageView.PersonNavView): return lbl def write_child(self, vbox, handle, index): + parent = has_children(self.dbstate.db, + self.dbstate.db.get_person_from_handle(handle)) + if parent: + format = 'underline="single" weight="heavy" style="italic"' + else: + format = 'underline="single"' link_label = GrampsWidgets.LinkLabel(self.get_name(handle, True), - self.button_press, handle) + self.button_press, handle, format) if self.use_shade: link_label.modify_bg(gtk.STATE_NORMAL, self.color) link_label.set_padding(3, 0) @@ -856,6 +953,27 @@ class RelationshipView(PageView.PersonNavView): value = "" return value + def check_collapsed(self, person, handle): + """ Returns true if collapsed. """ + return (handle in self.collapsed_items.get(person.handle, [])) + + def expand_collapse_press(self, obj, event, pair): + """ Calback function for ExpandCollapseArrow, user param is pair, + which is a tuple (person, handle) with handle the section of which + the collapse state must change, so a parent, siblings id, + family id, family children id, etc + """ + if event.type == gtk.gdk.BUTTON_PRESS and event.button == 1: + person, handle = pair + if self.collapsed_items.has_key(person.handle): + if handle in self.collapsed_items[person.handle]: + self.collapsed_items[person.handle].remove(handle) + else: + self.collapsed_items[person.handle].append(handle) + else: + self.collapsed_items[person.handle] = [handle] + self.redraw() + def button_press(self, obj, event, handle): if event.type == gtk.gdk.BUTTON_PRESS and event.button == 1: self.dbstate.change_active_handle(handle) @@ -945,7 +1063,7 @@ class RelationshipView(PageView.PersonNavView): self.write_data( vbox, '%(event_type)s:' % value, start_col, stop_col) - def write_family(self, family_handle): + def write_family(self, family_handle, person = None): family = self.dbstate.db.get_family_from_handle(family_handle) if family == None: from QuestionDialog import WarningDialog @@ -961,37 +1079,63 @@ class RelationshipView(PageView.PersonNavView): else: handle = father_handle - self.write_label("%s:" % _('Family'), family, False) - if handle: - box = self.write_person(_('Spouse'), handle) - - if not self.write_marriage(box, family): - self.write_relationship(box, family) - - child_list = family.get_child_ref_list() - if child_list: + # collapse button + if self.check_collapsed(person, family_handle): + # show "> Family: ..." and nothing else + self.write_label("%s:" % _('Family'), family, False, person) + self.row -= 1 # back up + child_list = family.get_child_ref_list() + if child_list: + count = len(child_list) + else: + count = 0 + if count > 1 : + childmsg = _(" (%d children)") % count + elif count == 1 : + childmsg = _(" (1 child)") + else : + childmsg = _(" (no children)") + box = self.get_people_box(handle, post_msg=childmsg) eventbox = gtk.EventBox() if self.use_shade: eventbox.modify_bg(gtk.STATE_NORMAL, self.color) - vbox = gtk.VBox() - label_cell = self.build_label_cell(_('Children')) - label_cell.set_alignment(0, 0) + eventbox.add(box) self.attach.attach( - label_cell, _CLABEL_START, _CLABEL_STOP, self.row, - self.row+1, xoptions=gtk.FILL|gtk.SHRINK, - yoptions=gtk.FILL) + eventbox, _PDATA_START, _PDATA_STOP, + self.row, self.row+1) + self.row += 1 # now advance it + else: + # show "V Family: ..." and the rest + self.write_label("%s:" % _('Family'), family, False, person) + if handle: + box = self.write_person(_('Spouse'), handle) - i = 1 - for child_ref in child_list: - self.write_child(vbox, child_ref.ref, i) - i += 1 + if not self.write_marriage(box, family): + self.write_relationship(box, family) - eventbox.add(vbox) - self.attach.attach( - eventbox, _CDATA_START, _CDATA_STOP, self.row, - self.row+1) + child_list = family.get_child_ref_list() + if child_list: + eventbox = gtk.EventBox() + if self.use_shade: + eventbox.modify_bg(gtk.STATE_NORMAL, self.color) + vbox = gtk.VBox() + label_cell = self.build_label_cell(_('Children')) + label_cell.set_alignment(0, 0) + self.attach.attach( + label_cell, _CLABEL_START, _CLABEL_STOP, self.row, + self.row+1, xoptions=gtk.FILL|gtk.SHRINK, + yoptions=gtk.FILL) - self.row += 1 + i = 1 + for child_ref in child_list: + self.write_child(vbox, child_ref.ref, i) + i += 1 + + eventbox.add(vbox) + self.attach.attach( + eventbox, _CDATA_START, _CDATA_STOP, self.row, + self.row+1) + self.row += 1 def edit_button_press(self, obj, event, handle): if event.type == gtk.gdk.BUTTON_PRESS and event.button == 1: @@ -1150,3 +1294,19 @@ class RelationshipView(PageView.PersonNavView): self.dbstate.active.handle) except Errors.WindowActiveError: pass + +#------------------------------------------------------------------------- +# +# Function to return if person has children +# +#------------------------------------------------------------------------- +def has_children(db,p): + """ + Returns if a person has children. + """ + for family_handle in p.get_family_handle_list(): + family = db.get_family_from_handle(family_handle) + childlist = family.get_child_ref_list() + if childlist and len(childlist) > 0: + return True + return False diff --git a/src/DisplayState.py b/src/DisplayState.py index dfa05a1de..7d73b4212 100644 --- a/src/DisplayState.py +++ b/src/DisplayState.py @@ -64,6 +64,10 @@ DISABLED = -1 # #------------------------------------------------------------------------- class History(GrampsDb.GrampsDBCallback): + """ History manages the objects of a certain type that have been viewed, + with ability to go back, or forward. + When accessing an object, it should be pushed on the History. + """ __signals__ = { 'changed' : (list, ), @@ -130,7 +134,18 @@ class History(GrampsDb.GrampsDBCallback): return str(self.history[self.index]) except IndexError: return u"" - + + def present(self): + '''return the person handle that is now active in the history + ''' + try : + if self.history : + return self.history[self.index] + else: + return u"" + except IndexError: + return u"" + def at_end(self): return self.index+1 == len(self.history) @@ -315,8 +330,15 @@ class DisplayState(GrampsDb.GrampsDBCallback): else: return u"" - def clear_history(self): + def clear_history(self,handle=None): + '''Clear the history. If handle is given, then the history is + immediately initialized with a first entry + (you'd eg want active person you view there as History contains the + present object too!) + ''' self.phistory.clear() + if handle : + self.phistory.push(handle) def set_busy_cursor(self, value): if value == self.busy: diff --git a/src/GrampsWidgets.py b/src/GrampsWidgets.py index 6ab2b712c..d871115f0 100644 --- a/src/GrampsWidgets.py +++ b/src/GrampsWidgets.py @@ -87,14 +87,44 @@ hand_cursor = gtk.gdk.Cursor(gtk.gdk.HAND2) def realize_cb(widget): widget.window.set_cursor(hand_cursor) +class ExpandCollapseArrow(gtk.EventBox): + """ + Arrow to be used for expand/collapse of sections. + Note: shadow does not work, we indicate action with realize_cb + """ + def __init__(self, collapsed, onbuttonpress, pair): + """ + Constructor for the ExpandCollapseArrow class. + + @param collapsed: True if arrow must be shown collapsed, + False otherwise + @type collapsed: bool + @param onbuttonpress: The callback function for button press + @type onbuttonpress: callback + @param pair: user param for onbuttonpress function + """ + gtk.EventBox.__init__(self) + self.tooltips = gtk.Tooltips() + if collapsed : + self.arrow = gtk.Arrow(gtk.ARROW_RIGHT, gtk.SHADOW_OUT) + self.tooltips.set_tip(self, _("Expand this section")) + else: + self.arrow = gtk.Arrow(gtk.ARROW_DOWN, gtk.SHADOW_OUT) + self.tooltips.set_tip(self, _("Collapse this section")) + + self.add(self.arrow) + self.connect('button-press-event', onbuttonpress, pair) + self.connect('realize', realize_cb) + class LinkLabel(gtk.EventBox): - def __init__(self, label, func, handle): + def __init__(self, label, func, handle, decoration='underline="single"'): gtk.EventBox.__init__(self) self.orig_text = cgi.escape(label[0]) self.gender = label[1] self.tooltips = gtk.Tooltips() - text = '%s' % self.orig_text + self.decoration = decoration + text = '%s' % (self.decoration, self.orig_text) msg = _('Click to make the active person\n' 'Right click to display the edit menu') @@ -110,7 +140,8 @@ class LinkLabel(gtk.EventBox): hbox = gtk.HBox() hbox.pack_start(self.label, False, False, 0) if label[1]: - hbox.pack_start(GenderLabel(label[1]), False, False, 4) + hbox.pack_start(GenderLabel(label[1]), False, False, 0) + hbox.set_spacing(4) self.add(hbox) self.connect('button-press-event', func, handle) @@ -122,12 +153,12 @@ class LinkLabel(gtk.EventBox): self.label.set_padding(x, y) def enter_text(self, obj, event, handle): - text = '%s' % self.orig_text + text = '%s' % (self.decoration, self.orig_text) self.label.set_text(text) self.label.set_use_markup(True) def leave_text(self, obj, event, handle): - text = '%s' % self.orig_text + text = '%s' % (self.decoration, self.orig_text) self.label.set_text(text) self.label.set_use_markup(True) diff --git a/src/PageView.py b/src/PageView.py index cf96da3b0..99180f6c1 100644 --- a/src/PageView.py +++ b/src/PageView.py @@ -425,9 +425,11 @@ class PersonNavView(BookMarkView): def handle_history(self, handle): """ Updates the person history information + It will push the person at the end of the history if that person is + not present person """ hobj = self.uistate.phistory - if handle and not hobj.lock: + if handle and not hobj.lock and not (handle == hobj.present()): hobj.push(handle) self.fwd_action.set_sensitive(not hobj.at_end()) self.back_action.set_sensitive(not hobj.at_front()) diff --git a/src/ViewManager.py b/src/ViewManager.py index 20022a9bf..50e0835a2 100644 --- a/src/ViewManager.py +++ b/src/ViewManager.py @@ -1003,7 +1003,11 @@ class ViewManager: def post_load(self): # This method is for the common UI post_load, both new files # and added data like imports. - self.uistate.clear_history() + if self.state.active : + #clear history and fill history with first entry, active person + self.uistate.clear_history(self.state.active.handle) + else : + self.uistate.clear_history(None) self.uistate.progress.hide() self.state.db.undo_callback = self.change_undo_label