From 29cc4ee6e9643e156b2dd35bbc8734a26138f0b4 Mon Sep 17 00:00:00 2001 From: Tim G L Lyons Date: Thu, 1 Sep 2011 18:13:42 +0000 Subject: [PATCH] * various updates to CitationView, CitationEmbedView and CitationEditor to take account of Rev 17973 for two different object types in selectors * updates to citationviews to support all the functionality (add source etc.) provided by sourceview (including different tooltips for tree view and list view) * moved registration of citationtreeview into view.grp.py * removed citationtreeview.grp.py module * fixed update to citation when note is deleted * fixed update to citation when media object is deleted * re-enabled edit button in sourcebackreflist * improved date display in mergecitation * changed default note type for citation notes * changed backref in citation editor to CitationBackRefList * fixed removal of citation handles from the list in primary objects when a citation is deleted * fixes for pylint svn: r18095 --- src/Merge/mergecitation.py | 8 +- src/Utils.py | 4 +- src/gen/lib/citation.py | 14 +- src/gen/lib/citationbase.py | 5 +- .../displaytabs/citationbackreflist.py | 1 + .../editors/displaytabs/citationembedlist.py | 61 +++-- .../editors/displaytabs/citationrefmodel.py | 1 + .../editors/displaytabs/sourcebackreflist.py | 39 --- src/gui/editors/editcitation.py | 93 ++++--- src/gui/editors/editmedia.py | 9 +- src/gui/editors/editnote.py | 7 +- src/gui/selectors/selectcitation.py | 6 + src/gui/views/treemodels/citationmodel.py | 46 ++-- src/plugins/gramplet/Gallery.py | 1 + src/plugins/lib/libcitationview.py | 251 +++++++++++++----- src/plugins/view/Makefile.am | 1 - src/plugins/view/citationlistview.py | 2 + src/plugins/view/citationtreeview.gpr.py | 14 - src/plugins/view/citationtreeview.py | 12 +- src/plugins/view/view.gpr.py | 16 ++ 20 files changed, 370 insertions(+), 221 deletions(-) delete mode 100644 src/plugins/view/citationtreeview.gpr.py diff --git a/src/Merge/mergecitation.py b/src/Merge/mergecitation.py index 04b16fc1b..0f4d47981 100644 --- a/src/Merge/mergecitation.py +++ b/src/Merge/mergecitation.py @@ -30,13 +30,13 @@ Provide merge capabilities for citations. # Gramps modules # #------------------------------------------------------------------------- -from gen.lib import (Person, Family, Event, Place, Source, Repository, - MediaObject) +from gen.lib import (Person, Family, Event, Place, MediaObject) from gen.db import DbTxn from gen.ggettext import sgettext as _ import const import GrampsDisplay import ManagedWindow +import DateHandler from Errors import MergeError from Utils import confidence @@ -84,8 +84,8 @@ class MergeCitations(ManagedWindow.ManagedWindow): entry1 = self.get_widget("date1") entry2 = self.get_widget("date2") - entry1.set_text(repr(self.citation1.get_date_object())) - entry2.set_text(repr(self.citation2.get_date_object())) + entry1.set_text(DateHandler.get_date(self.citation1)) + entry2.set_text(DateHandler.get_date(self.citation2)) if entry1.get_text() == entry2.get_text(): for widget_name in ('date1', 'date2', 'date_btn1', 'date_btn2'): diff --git a/src/Utils.py b/src/Utils.py index 4d9c19486..f379ef093 100644 --- a/src/Utils.py +++ b/src/Utils.py @@ -998,7 +998,7 @@ def get_media_referents(media_handle, db): to a given media handle in a given database. """ - _primaries = ('Person', 'Family', 'Event', 'Place', 'Source') + _primaries = ('Person', 'Family', 'Event', 'Place', 'Source', 'Citation') return (get_referents(media_handle, db, _primaries)) @@ -1010,7 +1010,7 @@ def get_note_referents(note_handle, db): """ _primaries = ('Person', 'Family', 'Event', 'Place', - 'Source', 'MediaObject', 'Repository') + 'Source', 'Citation', 'MediaObject', 'Repository') return (get_referents(note_handle, db, _primaries)) diff --git a/src/gen/lib/citation.py b/src/gen/lib/citation.py index f5c275be5..12665d36c 100644 --- a/src/gen/lib/citation.py +++ b/src/gen/lib/citation.py @@ -1,6 +1,8 @@ # # Gramps - a GTK+/GNOME based genealogy program # +# Copyright (C) 2000-2007 Donald N. Allingham +# Copyright (C) 2010 Michiel D. Nauta # Copyright (C) 2011 Tim G L Lyons # # This program is free software; you can redistribute it and/or modify @@ -42,7 +44,6 @@ from gen.lib.mediabase import MediaBase from gen.lib.notebase import NoteBase from gen.lib.datebase import DateBase from gen.lib.refbase import RefBase -from gen.lib.const import DIFFERENT, EQUAL, IDENTICAL #------------------------------------------------------------------------- # @@ -144,9 +145,6 @@ class Citation(MediaBase, NoteBase, PrimaryObject, RefBase, DateBase): :param handle_list: The list of handles to be removed. :type handle_list: str """ - # FIXME: Citations can refer to Notes, MediaObjects and one Source. - # MediaObjects and dealt with in Primary object, - # Notes do not seem to be dealt with at all !! if classname == 'Source' and \ self.get_reference_handle() in handle_list: self.set_reference_handle(None) @@ -162,9 +160,6 @@ class Citation(MediaBase, NoteBase, PrimaryObject, RefBase, DateBase): :param new_handle: The handle to replace the old one with. :type new_handle: str """ - # FIXME: Citations can refer to Notes, MediaObjects and one Source. - # MediaObjects and dealt with in Primary object, - # Notes do not seem to be dealt with at all !! if classname == 'Source' and \ RefBase.get_reference_handle(self) == old_handle: self.ref = RefBase.set_reference_handle(self, new_handle) @@ -176,8 +171,6 @@ class Citation(MediaBase, NoteBase, PrimaryObject, RefBase, DateBase): :returns: Returns the list of all textual attributes of the object. :rtype: list """ - # FIXME: Presumably this does not include references to primary objects - # (Notes, Media, Source) that contain text return [self.page, self.gramps_id] + self.datamap.keys() + self.datamap.values() @@ -263,7 +256,8 @@ class Citation(MediaBase, NoteBase, PrimaryObject, RefBase, DateBase): :param src_handle_list: The list of source handles to be removed. :type src_handle_list: list """ - self.set_reference_handle(None) + if self.get_reference_handle() in src_handle_list: + self.set_reference_handle(None) def replace_source_references(self, old_handle, new_handle): """ diff --git a/src/gen/lib/citationbase.py b/src/gen/lib/citationbase.py index 4ffb80414..f1c0d2e03 100644 --- a/src/gen/lib/citationbase.py +++ b/src/gen/lib/citationbase.py @@ -1,6 +1,8 @@ # # Gramps - a GTK+/GNOME based genealogy program # +# Copyright (C) 2000-2007 Donald N. Allingham +# Copyright (C) 2010 Michiel D. Nauta # Copyright (C) 2011 Tim G L Lyons # # This program is free software; you can redistribute it and/or modify @@ -100,7 +102,8 @@ class CitationBase(object): from the list of citations :type handle: str """ - LOG.debug('enter remove_citation handle %s' % handle) + LOG.debug('enter remove_citation handle: %s self: %s citation_list: %s' + % (handle, self, self.citation_list)) if handle in self.citation_list: LOG.debug('remove handle %s from citation_list %s' % (handle, self.citation_list)) diff --git a/src/gui/editors/displaytabs/citationbackreflist.py b/src/gui/editors/displaytabs/citationbackreflist.py index 8d80fe4af..738d7348d 100644 --- a/src/gui/editors/displaytabs/citationbackreflist.py +++ b/src/gui/editors/displaytabs/citationbackreflist.py @@ -1,6 +1,7 @@ # # Gramps - a GTK+/GNOME based genealogy program # +# Copyright (C) 2000-2006 Donald N. Allingham # Copyright (C) 2011 Tim G L Lyons # # This program is free software; you can redistribute it and/or modify diff --git a/src/gui/editors/displaytabs/citationembedlist.py b/src/gui/editors/displaytabs/citationembedlist.py index f0b297fa0..2943b8d50 100644 --- a/src/gui/editors/displaytabs/citationembedlist.py +++ b/src/gui/editors/displaytabs/citationembedlist.py @@ -1,6 +1,7 @@ # # Gramps - a GTK+/GNOME based genealogy program # +# Copyright (C) 2000-2007 Donald N. Allingham # Copyright (C) 2011 Tim G L Lyons # # This program is free software; you can redistribute it and/or modify @@ -42,12 +43,12 @@ LOG = logging.getLogger(".citation") #------------------------------------------------------------------------- import Errors import gen.lib +from gen.lib import Source, Citation from gui.dbguielement import DbGUIElement from gui.selectors import SelectorFactory from citationrefmodel import CitationRefModel from embeddedlist import EmbeddedList from DdTargets import DdTargets -from gen.lib.refbase import RefBase #------------------------------------------------------------------------- # @@ -65,10 +66,10 @@ class CitationEmbedList(EmbeddedList, DbGUIElement): _DND_TYPE = DdTargets.NOTE_LINK _MSG = { - 'add' : _('Create and add a new citation'), + 'add' : _('Create and add a new citation and new source'), 'del' : _('Remove the existing citation'), 'edit' : _('Edit the selected citation'), - 'share' : _('Add an existing citation'), + 'share' : _('Add an existing citation or source'), 'up' : _('Move the selected citation upwards'), 'down' : _('Move the selected citation downwards'), } @@ -95,10 +96,11 @@ class CitationEmbedList(EmbeddedList, DbGUIElement): """ Implement base class DbGUIElement method """ - #citation: citation-rebuild closes the editors, so no need to connect to it + #citation: citation-rebuild closes the editors, so no need to connect + # to it self.callman.register_callbacks( - {'citation-delete': self.citation_delete, # delete a citation we track - 'citation-update': self.citation_update, # change a citation we track + {'citation-delete': self.citation_delete, + 'citation-update': self.citation_update, }) self.callman.connect_all(keys=['citation']) @@ -129,7 +131,6 @@ class CitationEmbedList(EmbeddedList, DbGUIElement): If the window already exists (Errors.WindowActiveError), we ignore it. This prevents the dialog from coming up twice on the same object. """ - citation = gen.lib.Citation() try: from gui.editors import EditCitation EditCitation(self.dbstate, self.uistate, self.track, @@ -151,18 +152,33 @@ class CitationEmbedList(EmbeddedList, DbGUIElement): SelectCitation = SelectorFactory('Citation') sel = SelectCitation(self.dbstate, self.uistate, self.track) - citation = sel.run() - LOG.debug("selected citation: %s" % citation) - if citation: - try: - from gui.editors import EditCitation - EditCitation(self.dbstate, self.uistate, self.track, citation, - callback=self.add_callback, - callertitle=self.callertitle) - except Errors.WindowActiveError: - from QuestionDialog import WarningDialog - WarningDialog(_("Cannot share this reference"), - self.__blocked_text()) + object = sel.run() + LOG.debug("selected object: %s" % object) + # the object returned should either be a Source or a Citation + if object: + if isinstance(object, Source): + try: + from gui.editors import EditCitation + EditCitation(self.dbstate, self.uistate, self.track, + gen.lib.Citation(), object, + callback=self.add_callback, + callertitle=self.callertitle) + except Errors.WindowActiveError: + from QuestionDialog import WarningDialog + WarningDialog(_("Cannot share this reference"), + self.__blocked_text()) + elif isinstance(object, Citation): + try: + from gui.editors import EditCitation + EditCitation(self.dbstate, self.uistate, self.track, + object, callback=self.add_callback, + callertitle=self.callertitle) + except Errors.WindowActiveError: + from QuestionDialog import WarningDialog + WarningDialog(_("Cannot share this reference"), + self.__blocked_text()) + else: + raise ValueError("selection must be either source or citation") def edit_button_clicked(self, obj): """ @@ -209,10 +225,3 @@ class CitationEmbedList(EmbeddedList, DbGUIElement): if handle in self.data: self.rebuild() break - -# FIXME: Are these functions needed for citations? -# def get_editor(self): -# pass -# -# def get_user_values(self): -# return [] diff --git a/src/gui/editors/displaytabs/citationrefmodel.py b/src/gui/editors/displaytabs/citationrefmodel.py index 6859e100f..d7fa1b941 100644 --- a/src/gui/editors/displaytabs/citationrefmodel.py +++ b/src/gui/editors/displaytabs/citationrefmodel.py @@ -1,6 +1,7 @@ # # Gramps - a GTK+/GNOME based genealogy program # +# Copyright (C) 2000-2006 Donald N. Allingham # Copyright (C) 2011 Tim G L Lyons # # This program is free software; you can redistribute it and/or modify diff --git a/src/gui/editors/displaytabs/sourcebackreflist.py b/src/gui/editors/displaytabs/sourcebackreflist.py index 16ed66ec0..3e040b265 100644 --- a/src/gui/editors/displaytabs/sourcebackreflist.py +++ b/src/gui/editors/displaytabs/sourcebackreflist.py @@ -21,13 +21,6 @@ # $Id$ -#------------------------------------------------------------------------- -# -# GTK libraries -# -#------------------------------------------------------------------------- -import gtk - #------------------------------------------------------------------------- # # GRAMPS classes @@ -35,7 +28,6 @@ import gtk #------------------------------------------------------------------------- from backrefmodel import BackRefModel from backreflist import BackRefList -from gui.widgets import SimpleButton class SourceBackRefList(BackRefList): @@ -43,36 +35,5 @@ class SourceBackRefList(BackRefList): BackRefList.__init__(self, dbstate, uistate, track, obj, BackRefModel, callback=callback) - def _create_buttons(self, share=False, move=False, jump=False, top_label=None): - """ - SourceBackrefList inherits from BackrefList inherits from EmbeddedList - inherits from ButtonTab - _create_buttons is defined in ButtonTab, and overridden in BackRefList. - But needs to be overriden here so that there is no edit button for - References to Source, because they will all be citations, - and the Citations will be displayed in the top part of the - editcitation dialogue. - - Create a button box consisting of one button: Edit. - This has to be created here because backreflist.py sets it sensitive - This button box is then not appended hbox (self). - Method has signature of, and overrides create_buttons from _ButtonTab.py - """ - self.edit_btn = SimpleButton(gtk.STOCK_EDIT, self.edit_button_clicked) - self.edit_btn.set_tooltip_text(_('Edit reference')) - - hbox = gtk.HBox() - hbox.set_spacing(6) -# hbox.pack_start(self.edit_btn, False) - hbox.show_all() - self.pack_start(hbox, False) - - self.add_btn = None - self.del_btn = None - - self.track_ref_for_deletion("edit_btn") - self.track_ref_for_deletion("add_btn") - self.track_ref_for_deletion("del_btn") - def get_icon_name(self): return 'gramps-source' diff --git a/src/gui/editors/editcitation.py b/src/gui/editors/editcitation.py index 36d8db3b3..038094b1b 100644 --- a/src/gui/editors/editcitation.py +++ b/src/gui/editors/editcitation.py @@ -1,6 +1,8 @@ # # Gramps - a GTK+/GNOME based genealogy program # +# Copyright (C) 2000-2006 Donald N. Allingham +# Copyright (C) 2009 Gary Burton # Copyright (C) 2011 Tim G L Lyons, Nick Hall # # This program is free software; you can redistribute it and/or modify @@ -20,6 +22,10 @@ # $Id$ +""" +EditCitation class for GRAMPS. +""" + #------------------------------------------------------------------------- # # Python modules @@ -29,13 +35,6 @@ from gen.ggettext import gettext as _ import logging LOG = logging.getLogger(".citation") -#------------------------------------------------------------------------- -# -# GTK/Gnome modules -# -#------------------------------------------------------------------------- -import gtk - #------------------------------------------------------------------------- # # gramps modules @@ -46,7 +45,7 @@ from gen.db import DbTxn from editprimary import EditPrimary from displaytabs import (NoteTab, GalleryTab, DataEmbedList, - SourceBackRefList, RepoEmbedList) + SourceBackRefList, RepoEmbedList, CitationBackRefList) from gui.widgets import (MonitoredEntry, PrivacyButton, MonitoredMenu, MonitoredDate) from QuestionDialog import ErrorDialog @@ -60,19 +59,23 @@ from glade import Glade #------------------------------------------------------------------------- class EditCitation(EditPrimary): + """ + Create an EditCitation window. Associate a citation with the window. + + This class is called both to edit the Citation Primary object + and to edit references from other objects to citations. + It is called from gui.editors.__init__ for editing the primary object + and is called from CitationEmbedList for editing references + + @param callertitle: Text passed by calling object to add to title + @type callertitle: str + """ def __init__(self, dbstate, uistate, track, obj, source=None, callback=None, callertitle = None): """ - Create an EditCitation window. Associate a citation with the window. - - This class is called both to edit the Citation Primary object - and to edit references from other objects to citations. - It is called from gui.editors.__init__ for editing the primary object - and is called from CitationEmbedList for editing references - - @param callertitle: Text passed by calling object to add to title - @type callertitle: str + The obj parameter is mandatory. If the source parameter is not + provided, it will be deduced from the obj Citation object. """ if not source and obj.get_reference_handle(): source = dbstate.db.get_source_from_handle( @@ -82,6 +85,10 @@ class EditCitation(EditPrimary): EditPrimary.__init__(self, dbstate, uistate, track, obj, dbstate.db.get_citation_from_handle, dbstate.db.get_citation_from_gramps_id, callback) + # FIXME: EitPrimary calls ManagedWindow.__init__, which checks whether + # a window is already open which is editing obj. However, for + # EditCitation, not only do we need to protect obj (which will be + # a Citation, but we also need to protect the associated Source. def empty_object(self): """ @@ -92,6 +99,10 @@ class EditCitation(EditPrimary): return gen.lib.Citation() def get_menu_title(self): + """ + Construct the menu title, which may include the name of the object that + contains a reference to this citation. + """ title = self.obj.get_page() if title: if self.callertitle: @@ -117,19 +128,19 @@ class EditCitation(EditPrimary): # are normally inherited from editreference, # but have to be defined here because this class inherits from # EditPrimary instead - def define_warn_box(self,box): + def define_warn_box(self, box): self.warn_box = box def enable_warnbox(self): self.warn_box.show() - def define_warn_box2(self,box): + def define_warn_box2(self, box): self.warn_box2 = box def enable_warnbox2(self): self.warn_box2.show() - def define_expander(self,expander): + def define_expander(self, expander): expander.set_expanded(True) def _local_init(self): @@ -172,7 +183,7 @@ class EditCitation(EditPrimary): Called by the init routine of the base class L{EditPrimary}. """ - self.define_ok_button(self.glade.get_object('ok'),self.save) + self.define_ok_button(self.glade.get_object('ok'), self.save) self.define_cancel_button(self.glade.get_object('cancel')) self.define_help_button(self.glade.get_object('help')) @@ -261,7 +272,7 @@ class EditCitation(EditPrimary): self.comment_tab = NoteTab(self.dbstate, self.uistate, self.track, self.obj.get_note_list(), self.get_menu_title(), - notetype=gen.lib.NoteType.SOURCEREF) + notetype=gen.lib.NoteType.CITATION) self._add_tab(notebook_ref, self.comment_tab) self.track_ref_for_deletion("comment_tab") @@ -275,7 +286,7 @@ class EditCitation(EditPrimary): self._add_tab(notebook_ref, self.data_tab) self.track_ref_for_deletion("data_tab") - self.citationref_list = SourceBackRefList(self.dbstate, self.uistate, + self.citationref_list = CitationBackRefList(self.dbstate, self.uistate, self.track, self.db.find_backlink_handles(self.obj.handle), self.enable_warnbox2) @@ -310,7 +321,7 @@ class EditCitation(EditPrimary): self._add_tab(notebook_src, self.repo_tab) self.track_ref_for_deletion("repo_tab") - self.srcref_list = SourceBackRefList(self.dbstate,self.uistate, + self.srcref_list = SourceBackRefList(self.dbstate, self.uistate, self.track, self.db.find_backlink_handles(self.source.handle), self.enable_warnbox) @@ -337,16 +348,16 @@ class EditCitation(EditPrimary): self.ok_button.set_sensitive(True) return - (uses_dupe_id, id) = self._uses_duplicate_id() + (uses_dupe_id, gramps_id) = self._uses_duplicate_id() if uses_dupe_id: - prim_object = self.get_from_gramps_id(id) + prim_object = self.get_from_gramps_id(gramps_id) name = prim_object.get_page() msg1 = _("Cannot save citation. ID already exists.") msg2 = _("You have attempted to use the existing Gramps ID with " - "value %(id)s. This value is already used by '" - "%(prim_object)s'. Please enter a different ID or leave " - "blank to get the next available ID value.") % { - 'id' : id, 'prim_object' : name } + "value %(gramps_id)s. This value is already used by '" + "%(prim_object)s'. Please enter a different ID or leave " + "blank to get the next available ID value.") % { + 'gramps_id' : gramps_id, 'prim_object' : name } ErrorDialog(msg1, msg2) self.ok_button.set_sensitive(True) return @@ -358,7 +369,8 @@ class EditCitation(EditPrimary): msg = _("Add Source (%s)") % self.source.get_title() else: if not self.source.get_gramps_id(): - self.source.set_gramps_id(self.db.find_next_source_gramps_id()) + self.source.set_gramps_id( + self.db.find_next_source_gramps_id()) self.db.commit_source(self.source, trans) msg = _("Edit Source (%s)") % self.source.get_title() @@ -370,7 +382,8 @@ class EditCitation(EditPrimary): msg += _(" " + "Add Citation (%s)") % self.obj.get_page() else: if not self.obj.get_gramps_id(): - self.obj.set_gramps_id(self.db.find_next_citation_gramps_id()) + self.obj.set_gramps_id( + self.db.find_next_citation_gramps_id()) self.db.commit_citation(self.obj, trans) msg += _("\n" + "Edit Citation (%s)") % self.obj.get_page() trans.set_description(msg) @@ -394,41 +407,41 @@ class DeleteCitationQuery(object): (person_list, family_list, event_list, place_list, source_list, media_list, repo_list) = self.the_lists - ctn_handle_list = [self.citation.get_handle()] + ctn_handle = self.citation.get_handle() for handle in person_list: person = self.db.get_person_from_handle(handle) - person.remove_citation_references(ctn_handle_list) + person.remove_citation(ctn_handle) 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) + family.remove_citation(ctn_handle) 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) + event.remove_citation(ctn_handle) 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) + place.remove_citation(ctn_handle) 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) + source.remove_citation(ctn_handle) self.db.commit_source(source, trans) for handle in media_list: media = self.db.get_object_from_handle(handle) - media.remove_citation_references(ctn_handle_list) + media.remove_citation(ctn_handle) self.db.commit_media_object(media, trans) for handle in repo_list: repo = self.db.get_repository_from_handle(handle) - repo.remove_citation_references(ctn_handle_list) + repo.remove_citation(ctn_handle) self.db.commit_repository(repo, trans) self.db.enable_signals() diff --git a/src/gui/editors/editmedia.py b/src/gui/editors/editmedia.py index af57ffc13..830c86038 100644 --- a/src/gui/editors/editmedia.py +++ b/src/gui/editors/editmedia.py @@ -342,7 +342,7 @@ class DeleteMediaQuery(object): self.db.disable_signals() (person_list, family_list, event_list, - place_list, source_list) = self.the_lists + place_list, source_list, citation_list) = self.the_lists for handle in person_list: person = self.db.get_person_from_handle(handle) @@ -379,5 +379,12 @@ class DeleteMediaQuery(object): 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_object(self.media_handle, trans) diff --git a/src/gui/editors/editnote.py b/src/gui/editors/editnote.py index 0f0377b73..aea6ba528 100644 --- a/src/gui/editors/editnote.py +++ b/src/gui/editors/editnote.py @@ -346,7 +346,7 @@ class DeleteNoteQuery(object): self.db.disable_signals() (person_list, family_list, event_list, place_list, source_list, - media_list, repo_list) = self.the_lists + citation_list, media_list, repo_list) = self.the_lists note_handle = self.note.get_handle() @@ -375,6 +375,11 @@ class DeleteNoteQuery(object): source.remove_note(note_handle) self.db.commit_source(source, trans) + for handle in citation_list: + source = self.db.get_citation_from_handle(handle) + citation.remove_note(note_handle) + self.db.commit_citation(citation, trans) + for handle in media_list: media = self.db.get_object_from_handle(handle) media.remove_note(note_handle) diff --git a/src/gui/selectors/selectcitation.py b/src/gui/selectors/selectcitation.py index 48b197d5d..26ae86cfc 100644 --- a/src/gui/selectors/selectcitation.py +++ b/src/gui/selectors/selectcitation.py @@ -1,6 +1,8 @@ # # Gramps - a GTK+/GNOME based genealogy program # +# Copyright (C) 2003-2006 Donald N. Allingham +# 2009 Gary Burton # Copyright (C) 2011 Tim G L Lyons, Nick Hall # # This program is free software; you can redistribute it and/or modify @@ -20,6 +22,10 @@ # $Id$ +""" +SelectCitation class for GRAMPS. +""" + #------------------------------------------------------------------------- # # internationalization diff --git a/src/gui/views/treemodels/citationmodel.py b/src/gui/views/treemodels/citationmodel.py index 151de612b..a19f21e85 100644 --- a/src/gui/views/treemodels/citationmodel.py +++ b/src/gui/views/treemodels/citationmodel.py @@ -1,6 +1,7 @@ # # Gramps - a GTK+/GNOME based genealogy program # +# Copyright (C) 2000-2006 Donald N. Allingham # Copyright (C) 2011 Tim G L Lyons, Nick Hall # # This program is free software; you can redistribute it and/or modify @@ -19,6 +20,10 @@ # # $Id$ +""" +CitationBaseModel, CitationListModel and CitationTreeModel classes for GRAMPS. +""" + #------------------------------------------------------------------------- # # python modules @@ -29,6 +34,13 @@ import logging log = logging.getLogger(".") LOG = logging.getLogger(".citation") +#------------------------------------------------------------------------- +# +# internationalization +# +#------------------------------------------------------------------------- +from gen.ggettext import gettext as _ + #------------------------------------------------------------------------- # # GNOME/GTK modules @@ -78,7 +90,7 @@ INVALID_DATE_FORMAT = config.get('preferences.invalid-date-format') #------------------------------------------------------------------------- class CitationBaseModel(object): - def __init__(self,db): + def __init__(self, db): self.map = db.get_raw_citation_data self.gen_cursor = db.get_citation_cursor self.fmap = [ @@ -126,7 +138,7 @@ class CitationBaseModel(object): def on_get_n_columns(self): return len(self.fmap)+1 - def column_date(self,data): + def column_date(self, data): if data[COLUMN_DATE]: citation = gen.lib.Citation() citation.unserialize(data) @@ -139,25 +151,25 @@ class CitationBaseModel(object): return retval return u'' - def column_id(self,data): + def column_id(self, data): return unicode(data[COLUMN_ID]) - def column_page(self,data): + def column_page(self, data): return unicode(data[COLUMN_PAGE]) - def column_confidence(self,data): + def column_confidence(self, data): return unicode(confidence[data[COLUMN_CONFIDENCE]]) - def column_handle(self,data): + def column_handle(self, data): return unicode(data[COLUMN_HANDLE]) - def column_change(self,data): + def column_change(self, data): return format_time(data[COLUMN_CHANGE]) - def sort_change(self,data): + def sort_change(self, data): return "%012x" % data[COLUMN_CHANGE] - def column_src_title(self,data): + def column_src_title(self, data): source_handle = data[COLUMN_SOURCE] try: source = self.db.get_source_from_handle(source_handle) @@ -165,7 +177,7 @@ class CitationBaseModel(object): except: return u'' - def column_src_id(self,data): + def column_src_id(self, data): source_handle = data[COLUMN_SOURCE] try: source = self.db.get_source_from_handle(source_handle) @@ -173,7 +185,7 @@ class CitationBaseModel(object): except: return u'' - def column_src_auth(self,data): + def column_src_auth(self, data): source_handle = data[COLUMN_SOURCE] try: source = self.db.get_source_from_handle(source_handle) @@ -181,7 +193,7 @@ class CitationBaseModel(object): except: return u'' - def column_src_abbr(self,data): + def column_src_abbr(self, data): source_handle = data[COLUMN_SOURCE] try: source = self.db.get_source_from_handle(source_handle) @@ -189,7 +201,7 @@ class CitationBaseModel(object): except: return u'' - def column_src_pinfo(self,data): + def column_src_pinfo(self, data): source_handle = data[COLUMN_SOURCE] try: source = self.db.get_source_from_handle(source_handle) @@ -197,7 +209,7 @@ class CitationBaseModel(object): except: return u'' - def column_src_chan(self,data): + def column_src_chan(self, data): source_handle = data[COLUMN_SOURCE] try: source = self.db.get_source_from_handle(source_handle) @@ -205,13 +217,13 @@ class CitationBaseModel(object): except: return u'' - def column_tooltip(self,data): + def column_tooltip(self, data): if const.USE_TIPS: try: t = ToolTips.TipFromFunction(self.db, lambda: - self.db.get_citation_from_handle(data[0])) + self.db.get_citation_from_handle(data[0])) except: - log.error("Failed to create tooltip.",exc_info=True) + log.error("Failed to create tooltip.", exc_info=True) return t else: return u'' diff --git a/src/plugins/gramplet/Gallery.py b/src/plugins/gramplet/Gallery.py index 849923315..f87f4b7b8 100644 --- a/src/plugins/gramplet/Gallery.py +++ b/src/plugins/gramplet/Gallery.py @@ -1,6 +1,7 @@ # Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 2011 Nick Hall +# Copyright (C) 2011 Tim G L Lyons # # 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 diff --git a/src/plugins/lib/libcitationview.py b/src/plugins/lib/libcitationview.py index e253ee02d..6e17a7dd3 100644 --- a/src/plugins/lib/libcitationview.py +++ b/src/plugins/lib/libcitationview.py @@ -1,5 +1,7 @@ # Gramps - a GTK+/GNOME based genealogy program # +# Copyright (C) 2001-2006 Donald N. Allingham +# Copyright (C) 2008 Gary Burton # Copyright (C) 2011 Tim G L Lyons, Nick Hall # # This program is free software; you can redistribute it and/or modify @@ -43,16 +45,14 @@ import gtk # #------------------------------------------------------------------------- import gen.lib -import config from gui.views.listview import ListView -from gui.views.treemodels import CitationListModel import Utils import Bookmarks import Errors from DdTargets import DdTargets -from gui.selectors import SelectorFactory from QuestionDialog import ErrorDialog -from gui.editors import EditCitation, DeleteCitationQuery, EditSource +from gui.editors import EditCitation, DeleteCitationQuery, EditSource, \ + DeleteSrcQuery from Filters.SideBar import SourceSidebarFilter from gen.plug import CATEGORY_QR_SOURCE @@ -63,7 +63,6 @@ from gen.plug import CATEGORY_QR_SOURCE #------------------------------------------------------------------------- from gen.ggettext import gettext as _ - #------------------------------------------------------------------------- # # CitationView @@ -108,9 +107,12 @@ class BaseCitationView(ListView): COL_SRC_ABBR, COL_SRC_PINFO, COL_SRC_CHAN]), ('columns.size', [200, 75, 100, 100, 100, 200, 75, 75, 100, 150, 100]) ) - ADD_MSG = _("Add a new citation to an existing source") + ADD_MSG = _("Add a new citation and a new source") + ADD_SOURCE_MSG = _("Add a new source") + ADD_CITATION_MSG = _("Add a new citation to an existing source") + # Edit delete and merge messages are overridden for the tree view as + # they can apply to sources or citations EDIT_MSG = _("Edit the selected citation") - SHARE_MSG = _("Share the selected source") DEL_MSG = _("Delete the selected citation") MERGE_MSG = _("Merge the selected citations") FILTER_TYPE = "Citation" @@ -123,6 +125,10 @@ class BaseCitationView(ListView): 'citation-update' : self.row_update, 'citation-delete' : self.row_delete, 'citation-rebuild' : self.object_build, + 'source-add' : self.row_add, + 'source-update' : self.row_update, + 'source-delete' : self.row_delete, + 'source-rebuild' : self.object_build, } ListView.__init__( @@ -151,13 +157,34 @@ class BaseCitationView(ListView): return DdTargets.SOURCE_LINK def define_actions(self): + """ + This defines the possible actions for the citation views. + Possible actions are: + add_source: Add a new source (this is also available from the + source view) + add: Add a new citation and a new source (this can also be done + by source view add a source, then citation view add a new + citation to an existing source) + share: Add a new citation to an existing source (when a source is + selected) + edit: Edit a source or a citation. + merge: Merge the selected sources or citations. + remove: Delete the selected sources or citations. + + + """ ListView.define_actions(self) -# self._add_action('Share', gtk.STOCK_EDIT, _("Share..."), -# accel=None, -# tip=self.SHARE_MSG, -# callback=self.share) -# +# gtk stock icons are at http://www.pygtk.org/docs/pygtk/gtk-stock-items.html + self._add_action('Add source', 'gramps-source', _("Add source..."), + accel=None, + tip=self.ADD_SOURCE_MSG, + callback=self.add_source) + self._add_action('Add citation', 'gramps-source', _("Add citation..."), + accel=None, + tip=self.ADD_CITATION_MSG, + callback=self.share) + self.all_action = gtk.ActionGroup(self.title + "/CitationAll") self.edit_action = gtk.ActionGroup(self.title + "/CitationEdit") @@ -173,6 +200,12 @@ class BaseCitationView(ListView): return 'gramps-citation' def additional_ui(self): + """ + Defines the UI string for UIManager + + This is overridden in citationtreeview because that has additional + popup items for open and close all nodes + """ return ''' @@ -196,6 +229,7 @@ class BaseCitationView(ListView): + @@ -210,6 +244,7 @@ class BaseCitationView(ListView): + @@ -237,45 +272,117 @@ class BaseCitationView(ListView): """ pass + def add_source(self, obj): + """ + add_source: Add a new source (this is also available from the + source view) + + Create a new Source instance and call the EditSource editor with the + new source. + + Called when the Add_source button is clicked. + If the window already exists (Errors.WindowActiveError), we ignore it. + This prevents the dialog from coming up twice on the same object. + + However, since the window is identified by the Source object, and + we have just created a new one, it seems to be impossible for the + window to already exist, so this is just an extra safety measure. + """ + try: + EditSource(self.dbstate, self.uistate, [], gen.lib.Source()) + except Errors.WindowActiveError: + pass + def add(self, obj): """ - Add a new Citation to a user selected source + add: Add a new citation and a new source (this can also be done + by source view add a source, then citation view add a new + citation to an existing source) + + Create a new Source instance and Citation instance and call the + EditSource editor with the new source. + + Called when the Add button is clicked. + If the window already exists (Errors.WindowActiveError), we ignore it. + This prevents the dialog from coming up twice on the same object. + + However, since the window is identified by the Source object, and + we have just created a new one, it seems to be impossible for the + window to already exist, so this is just an extra safety measure. """ - SelectSource = SelectorFactory('Source') - sel = SelectSource(self.dbstate, self.uistate) - source = sel.run() - if source: - try: - EditCitation(self.dbstate, self.uistate, [], gen.lib.Citation(), - source) - except Errors.WindowActiveError: - from QuestionDialog import WarningDialog - WarningDialog(_("Cannot share this reference"), - self.__blocked_text()) + try: + EditCitation(self.dbstate, self.uistate, [], gen.lib.Citation(), + gen.lib.Source()) + except Errors.WindowActiveError: + pass -# def share(self, obj): -# SelectSource = SelectorFactory('Source') -# sel = SelectSource(self.dbstate,self.uistate) -# source = sel.run() -# if source: -# try: -# EditCitation(self.dbstate, self.uistate, [], gen.lib.Citation(), -# source) -# except Errors.WindowActiveError: -# from QuestionDialog import WarningDialog -# WarningDialog(_("Cannot share this reference"), -# self.__blocked_text()) + def share(self, obj): + """ + share: Add a new citation to an existing source (when a source is + selected) + """ + for handle in self.selected_handles(): + # The handle will either be a Source handle or a Citation handle + source = self.dbstate.db.get_source_from_handle(handle) + citation = self.dbstate.db.get_citation_from_handle(handle) + if (not source and not citation) or (source and citation): + raise ValueError("selection must be either source or citation") + if source: + try: + EditCitation(self.dbstate, self.uistate, [], + gen.lib.Citation(), source) + except Errors.WindowActiveError: + from QuestionDialog import WarningDialog + WarningDialog(_("Cannot share this reference"), + self.__blocked_text()) + else: + msg = _("Cannot add citation.") + msg2 = _("In order to add a citation to an existing source, " + " you must select a source.") + ErrorDialog(msg, msg2) # def remove(self, obj): self.remove_selected_objects() def remove_object_from_handle(self, handle): - the_lists = Utils.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) + # The handle will either be a Source handle or a Citation handle + source = self.dbstate.db.get_source_from_handle(handle) + citation = self.dbstate.db.get_citation_from_handle(handle) + if (not source and not citation) or (source and citation): + raise ValueError("selection must be either source or citation") + if citation: + the_lists = Utils.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) + else: + # FIXME: this is copied from SourceView, because import with + # from plugins.view.sourceview import SourceView doesn't + # seem to work! + the_lists = Utils.get_source_referents(handle, self.dbstate.db) + LOG.debug('source referents %s' % [the_lists]) + citation_referents_list = [] + for citation in the_lists[7]: + LOG.debug('citation %s' % citation) + refs = Utils.get_citation_referents(citation, self.dbstate.db) + citation_referents_list += [(citation, refs)] + LOG.debug('citation_referents_list %s' % [citation_referents_list]) + + (person_list, family_list, event_list, place_list, source_list, + media_list, repo_list, citation_list) = the_lists + the_lists = (person_list, family_list, event_list, place_list, + source_list, media_list, repo_list, citation_list, + citation_referents_list) + + 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): """ @@ -283,17 +390,18 @@ class BaseCitationView(ListView): """ for handle in self.selected_handles(): # The handle will either be a Source handle or a Citation handle + source = self.dbstate.db.get_source_from_handle(handle) citation = self.dbstate.db.get_citation_from_handle(handle) + if (not source and not citation) or (source and citation): + raise ValueError("selection must be either source or citation") if citation: LOG.debug("citation handle %s page %s" % (handle, citation.page)) - source = self.dbstate.db.get_source_from_handle(citation.ref) try: - EditCitation(self.dbstate, self.uistate, [], citation, source) + EditCitation(self.dbstate, self.uistate, [], citation) except Errors.WindowActiveError: pass else: - source = self.dbstate.db.get_source_from_handle(handle) LOG.debug("source handle %s title %s " % (source, source.title)) EditSource(self.dbstate, self.uistate, [], source) @@ -314,26 +422,47 @@ class BaseCitationView(ListView): """ mlist = self.selected_handles() - # FIXME: needs to be enhanced to take account of the fact that - # the selected handles can be either sources or citations. if len(mlist) != 2: msg = _("Cannot merge citations.") - msg2 = _("Exactly two citations must be selected to perform a merge. " - "A second citation can be selected by holding down the " - "control key while clicking on the desired citation.") - ErrorDialog(msg, msg2) - elif not self.dbstate.db.get_citation_from_handle( - mlist[0]).get_reference_handle() == \ - self.dbstate.db.get_citation_from_handle( - mlist[1]).get_reference_handle(): - msg = _("Cannot merge citations.") - msg2 = _("The two selected citations must have the same source " - "to perform a merge. If you want to merge these two " - "citations, then you must merge the sources first.") + msg2 = _("Exactly two citations must be selected to perform a " + "merge. A second citation can be selected by holding " + "down the control key while clicking on the desired " + "citation.") ErrorDialog(msg, msg2) else: - import Merge - Merge.MergeCitations(self.dbstate, self.uistate, mlist[0], mlist[1]) + source1 = self.dbstate.db.get_source_from_handle(mlist[0]) + citation1 = self.dbstate.db.get_citation_from_handle(mlist[0]) + if (not source1 and not citation1) or (source1 and citation1): + raise ValueError("selection must be either source or citation") + + source2 = self.dbstate.db.get_source_from_handle(mlist[1]) + citation2 = self.dbstate.db.get_citation_from_handle(mlist[1]) + if (not source2 and not citation2) or (source2 and citation2): + raise ValueError("selection must be either source or citation") + + if citation1 and citation2: + if not citation1.get_reference_handle() == \ + citation2.get_reference_handle(): + msg = _("Cannot merge citations.") + msg2 = _("The two selected citations must have the same " + "source to perform a merge. If you want to merge " + "these two citations, then you must merge the " + "sources first.") + ErrorDialog(msg, msg2) + else: + import Merge + Merge.MergeCitations(self.dbstate, self.uistate, + mlist[0], mlist[1]) + elif source1 and source2: + import Merge + Merge.MergeSources(self.dbstate, self.uistate, + mlist[0], mlist[1]) + else: + msg = _("Cannot perform merge.") + msg2 = _("Both objects must be of the same type, either " + "both must be sources, or both must be " + "citations.") + ErrorDialog(msg, msg2) def get_handle_from_gramps_id(self, gid): obj = self.dbstate.db.get_citation_from_gramps_id(gid) diff --git a/src/plugins/view/Makefile.am b/src/plugins/view/Makefile.am index 9f24089ec..14f9a927c 100644 --- a/src/plugins/view/Makefile.am +++ b/src/plugins/view/Makefile.am @@ -7,7 +7,6 @@ pkgdatadir = $(datadir)/@PACKAGE@/plugins/view pkgdata_PYTHON = \ citationlistview.py \ - citationtreeview.grp.py \ citationtreeview.py \ eventview.py \ familyview.py \ diff --git a/src/plugins/view/citationlistview.py b/src/plugins/view/citationlistview.py index 6893bfb78..d1c1f7843 100644 --- a/src/plugins/view/citationlistview.py +++ b/src/plugins/view/citationlistview.py @@ -1,5 +1,7 @@ # Gramps - a GTK+/GNOME based genealogy program # +# Copyright (C) 2001-2006 Donald N. Allingham +# Copyright (C) 2008 Gary Burton # Copyright (C) 2011 Tim G L Lyons # # This program is free software; you can redistribute it and/or modify diff --git a/src/plugins/view/citationtreeview.gpr.py b/src/plugins/view/citationtreeview.gpr.py deleted file mode 100644 index 26f05fe1f..000000000 --- a/src/plugins/view/citationtreeview.gpr.py +++ /dev/null @@ -1,14 +0,0 @@ -register(VIEW, - id = 'citationtreeview', - name = _("Citation Tree View"), - description = _("A view displaying citations in a tree format."), - version = '1.0', - gramps_target_version = '3.4', - status = STABLE, - fname = 'citationtreeview.py', - authors = [u"Tim G L Lyons", u"Nick Hall"], - authors_email = [""], - category = ("Citations", _("Citations")), - viewclass = 'CitationTreeView', - stock_icon = 'gramps-tree-group', - ) diff --git a/src/plugins/view/citationtreeview.py b/src/plugins/view/citationtreeview.py index 75888e007..1b9b656a9 100644 --- a/src/plugins/view/citationtreeview.py +++ b/src/plugins/view/citationtreeview.py @@ -1,5 +1,6 @@ # Gramps - a GTK+/GNOME based genealogy program # +# Copyright (C) 2009-2010 Nick Hall # Copyright (C) 2011 Tim G L Lyons # # This program is free software; you can redistribute it and/or modify @@ -38,10 +39,6 @@ LOG = logging.getLogger(".citation") from gui.views.listview import LISTTREE from libcitationview import BaseCitationView from gui.views.treemodels.citationmodel import CitationTreeModel -import gen.lib -import Errors -from gui.editors import EditCitation -from Utils import preset_name #------------------------------------------------------------------------- # @@ -81,6 +78,9 @@ class CitationTreeView(BaseCitationView): """ Define actions for the popup menu specific to the tree view. """ + self.EDIT_MSG = _("Edit the selected citation or source") + self.DEL_MSG = _("Delete the selected citation or source") + self.MERGE_MSG = _("Merge the selected citations or selected sources") BaseCitationView.define_actions(self) self.all_action.add_actions([ @@ -116,6 +116,8 @@ class CitationTreeView(BaseCitationView): + + @@ -130,6 +132,8 @@ class CitationTreeView(BaseCitationView): + + diff --git a/src/plugins/view/view.gpr.py b/src/plugins/view/view.gpr.py index 07531fc78..ffa409613 100644 --- a/src/plugins/view/view.gpr.py +++ b/src/plugins/view/view.gpr.py @@ -226,3 +226,19 @@ category = ("Citations", _("Citations")), viewclass = 'CitationListView', order = START, ) + +register(VIEW, +id = 'citationtreeview', +name = _("Citation Tree View"), +description = _("A view displaying citations and sources in a tree format."), +version = '1.0', +gramps_target_version = '3.4', +status = STABLE, +fname = 'citationtreeview.py', +authors = [u"Tim G L Lyons", u"Nick Hall"], +authors_email = [""], +category = ("Citations", _("Citations")), +viewclass = 'CitationTreeView', +stock_icon = 'gramps-tree-group', +order = START, + )