From 5a3cef062c33ee6a0fcadf8d25794054e9e91e72 Mon Sep 17 00:00:00 2001 From: Tim G L Lyons Date: Tue, 30 Aug 2011 10:40:30 +0000 Subject: [PATCH] * Implemented merge Citation * Updated merge Sources * Implemented undo-redo for citations (revision 18069 had only added citations to the undo-redo list, not acted on that types of element) * Fixed embedded lists for some of the object type editors that were done in Rev 18069 * Modified EditCitation so the source parameter is optional * Added svn properties for all /src/Merge/merge*.py modules * Added support for EditCitation from backreflist.py svn: r18085 --- src/Merge/Makefile.am | 1 + src/Merge/__init__.py | 3 +- src/Merge/mergecitation.py | 225 ++++++++ src/Merge/mergeplace.py | 2 +- src/Merge/mergesource.py | 45 +- src/gen/db/undoredo.py | 6 +- src/gen/db/upgrade.py | 13 +- src/gen/db/write.py | 4 + src/gen/lib/citation.py | 91 ++-- src/gen/lib/event.py | 7 +- src/gen/lib/eventref.py | 9 +- src/gen/lib/family.py | 4 +- src/gen/lib/mediaobj.py | 16 +- src/gen/lib/mediaref.py | 5 +- src/gen/lib/person.py | 4 +- src/gen/lib/place.py | 4 +- src/gen/lib/primaryobj.py | 2 + src/gen/lib/repo.py | 9 +- src/gen/lib/src.py | 9 +- src/gen/lib/srcbase.py | 3 +- src/glade/Makefile.am | 1 + src/glade/mergecitation.glade | 508 ++++++++++++++++++ src/gui/editors/displaytabs/backreflist.py | 10 +- .../editors/displaytabs/citationembedlist.py | 12 +- src/gui/editors/editaddress.py | 8 +- src/gui/editors/editchildref.py | 9 +- src/gui/editors/editcitation.py | 7 +- src/gui/editors/editmedia.py | 4 +- src/gui/editors/editname.py | 7 +- src/gui/editors/editpersonref.py | 8 +- src/plugins/gramplet/Citations.py | 2 +- src/plugins/lib/libcitationview.py | 25 +- 32 files changed, 907 insertions(+), 156 deletions(-) create mode 100644 src/Merge/mergecitation.py create mode 100644 src/glade/mergecitation.glade diff --git a/src/Merge/Makefile.am b/src/Merge/Makefile.am index f2e4999c3..20462d8ad 100644 --- a/src/Merge/Makefile.am +++ b/src/Merge/Makefile.am @@ -12,6 +12,7 @@ pkgdata_PYTHON = \ mergeevent.py \ mergeplace.py \ mergesource.py \ + mergecitation.py \ mergerepository.py \ mergemedia.py \ mergenote.py diff --git a/src/Merge/__init__.py b/src/Merge/__init__.py index 54d871f5d..6e0b31118 100644 --- a/src/Merge/__init__.py +++ b/src/Merge/__init__.py @@ -18,7 +18,7 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # -# $Id: __init__.py 15645 2010-07-22 02:16:32Z dsblank $ +# $Id$ """ """ @@ -28,6 +28,7 @@ from mergefamily import * from mergeevent import * from mergeplace import * from mergesource import * +from mergecitation import * from mergerepository import * from mergemedia import * from mergenote import * diff --git a/src/Merge/mergecitation.py b/src/Merge/mergecitation.py new file mode 100644 index 000000000..04b16fc1b --- /dev/null +++ b/src/Merge/mergecitation.py @@ -0,0 +1,225 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000-2005 Donald N. Allingham +# 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 +# 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$ + +""" +Provide merge capabilities for citations. +""" + +#------------------------------------------------------------------------- +# +# Gramps modules +# +#------------------------------------------------------------------------- +from gen.lib import (Person, Family, Event, Place, Source, Repository, + MediaObject) +from gen.db import DbTxn +from gen.ggettext import sgettext as _ +import const +import GrampsDisplay +import ManagedWindow +from Errors import MergeError +from Utils import confidence + +#------------------------------------------------------------------------- +# +# Gramps constants +# +#------------------------------------------------------------------------- +WIKI_HELP_PAGE = '%s_-_Entering_and_Editing_Data:_Detailed_-_part_3' % \ + const.URL_MANUAL_PAGE +WIKI_HELP_SEC = _('manual|Merge_Citations') +_GLADE_FILE = 'mergecitation.glade' + +#------------------------------------------------------------------------- +# +# Merge Citations +# +#------------------------------------------------------------------------- +class MergeCitations(ManagedWindow.ManagedWindow): + """ + Displays a dialog box that allows the citations to be combined into one. + """ + def __init__(self, dbstate, uistate, handle1, handle2): + ManagedWindow.ManagedWindow.__init__(self, uistate, [], self.__class__) + self.dbstate = dbstate + database = dbstate.db + self.citation1 = database.get_citation_from_handle(handle1) + self.citation2 = database.get_citation_from_handle(handle2) + + self.define_glade('mergecitation', _GLADE_FILE) + self.set_window(self._gladeobj.toplevel, + self.get_widget('citation_title'), + _("Merge Citations")) + + # Detailed Selection widgets + page1 = self.citation1.get_page() + page2 = self.citation2.get_page() + entry1 = self.get_widget("page1") + entry2 = self.get_widget("page2") + entry1.set_text(page1) + entry2.set_text(page2) + if entry1.get_text() == entry2.get_text(): + for widget_name in ('page1', 'page2', 'page_btn1', 'page_btn2'): + self.get_widget(widget_name).set_sensitive(False) + + 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())) + if entry1.get_text() == entry2.get_text(): + for widget_name in ('date1', 'date2', 'date_btn1', + 'date_btn2'): + self.get_widget(widget_name).set_sensitive(False) + + entry1 = self.get_widget("confidence1") + entry2 = self.get_widget("confidence2") + entry1.set_text(confidence[self.citation1.get_confidence_level()]) + entry2.set_text(confidence[self.citation2.get_confidence_level()]) + if entry1.get_text() == entry2.get_text(): + for widget_name in ('confidence1', 'confidence2', 'confidence_btn1', + 'confidence_btn2'): + self.get_widget(widget_name).set_sensitive(False) + + gramps1 = self.citation1.get_gramps_id() + gramps2 = self.citation2.get_gramps_id() + entry1 = self.get_widget("gramps1") + entry2 = self.get_widget("gramps2") + entry1.set_text(gramps1) + entry2.set_text(gramps2) + if entry1.get_text() == entry2.get_text(): + for widget_name in ('gramps1', 'gramps2', 'gramps_btn1', + 'gramps_btn2'): + self.get_widget(widget_name).set_sensitive(False) + + # Main window widgets that determine which handle survives + rbutton1 = self.get_widget("handle_btn1") + rbutton_label1 = self.get_widget("label_handle_btn1") + rbutton_label2 = self.get_widget("label_handle_btn2") + rbutton_label1.set_label(page1 + " [" + gramps1 + "]") + rbutton_label2.set_label(page2 + " [" + gramps2 + "]") + rbutton1.connect("toggled", self.on_handle1_toggled) + + self.connect_button('citation_help', self.cb_help) + self.connect_button('citation_ok', self.cb_merge) + self.connect_button('citation_cancel', self.close) + self.show() + + def on_handle1_toggled(self, obj): + """first chosen citation changes""" + if obj.get_active(): + self.get_widget("page_btn1").set_active(True) + self.get_widget("date_btn1").set_active(True) + self.get_widget("confidence_btn1").set_active(True) + self.get_widget("gramps_btn1").set_active(True) + else: + self.get_widget("page_btn2").set_active(True) + self.get_widget("date_btn2").set_active(True) + self.get_widget("confidence_btn2").set_active(True) + self.get_widget("gramps_btn2").set_active(True) + + def cb_help(self, obj): + """Display the relevant portion of Gramps manual""" + GrampsDisplay.help(webpage = WIKI_HELP_PAGE, section = WIKI_HELP_SEC) + + def cb_merge(self, obj): + """ + Performs the merge of the citations when the merge button is clicked. + """ + self.uistate.set_busy_cursor(True) + use_handle1 = self.get_widget("handle_btn1").get_active() + if use_handle1: + phoenix = self.citation1 + titanic = self.citation2 + unselect_path = (1,) + else: + phoenix = self.citation2 + titanic = self.citation1 + unselect_path = (0,) + + if self.get_widget("page_btn1").get_active() ^ use_handle1: + phoenix.set_page(titanic.get_page()) + if self.get_widget("date_btn1").get_active() ^ use_handle1: + phoenix.set_date_object(titanic.get_date_object()) + if self.get_widget("confidence_btn1").get_active() ^ use_handle1: + phoenix.get_confidence_level(titanic.get_confidence_level()) + if self.get_widget("gramps_btn1").get_active() ^ use_handle1: + phoenix.set_gramps_id(titanic.get_gramps_id()) + + query = MergeCitationQuery(self.dbstate, phoenix, titanic) + query.execute() + self.uistate.viewmanager.active_page.selection.unselect_path( + unselect_path) + self.uistate.set_busy_cursor(False) + self.close() + +class MergeCitationQuery(object): + """ + Create database query to merge two citations. + """ + def __init__(self, dbstate, phoenix, titanic): + self.database = dbstate.db + self.phoenix = phoenix + self.titanic = titanic + + def execute(self): + """ + Merges to citations into a single citation. + """ + new_handle = self.phoenix.get_handle() + old_handle = self.titanic.get_handle() + + self.phoenix.merge(self.titanic) + + with DbTxn(_("Merge Citation"), self.database) as trans: + self.database.commit_citation(self.phoenix, trans) + for (class_name, handle) in self.database.find_backlink_handles( + old_handle): + if class_name == Person.__name__: + person = self.database.get_person_from_handle(handle) + assert(person.has_citation_reference(old_handle)) + person.replace_citation_references(old_handle, new_handle) + self.database.commit_person(person, trans) + elif class_name == Family.__name__: + family = self.database.get_family_from_handle(handle) + assert(family.has_citation_reference(old_handle)) + family.replace_citation_references(old_handle, new_handle) + self.database.commit_family(family, trans) + elif class_name == Event.__name__: + event = self.database.get_event_from_handle(handle) + assert(event.has_citation_reference(old_handle)) + event.replace_citation_references(old_handle, new_handle) + self.database.commit_event(event, trans) + elif class_name == Place.__name__: + place = self.database.get_place_from_handle(handle) + assert(place.has_citation_reference(old_handle)) + place.replace_citation_references(old_handle, new_handle) + self.database.commit_place(place, trans) + elif class_name == MediaObject.__name__: + obj = self.database.get_object_from_handle(handle) + assert(obj.has_citation_reference(old_handle)) + obj.replace_citation_references(old_handle, new_handle) + self.database.commit_media_object(obj, trans) + else: + raise MergeError("Encounter an object of type %s that has " + "a citation reference." % class_name) + self.database.remove_citation(old_handle, trans) diff --git a/src/Merge/mergeplace.py b/src/Merge/mergeplace.py index d2b7afb65..c872ad9b9 100644 --- a/src/Merge/mergeplace.py +++ b/src/Merge/mergeplace.py @@ -19,7 +19,7 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # -# $Id: $ +# $Id$ """ Provide merge capabilities for places. diff --git a/src/Merge/mergesource.py b/src/Merge/mergesource.py index db387ac20..f60b8318d 100644 --- a/src/Merge/mergesource.py +++ b/src/Merge/mergesource.py @@ -3,6 +3,7 @@ # # Copyright (C) 2000-2005 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 # it under the terms of the GNU General Public License as published by @@ -19,7 +20,7 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # -# $Id: $ +# $Id$ """ Provide merge capabilities for sources. @@ -31,7 +32,7 @@ Provide merge capabilities for sources. # #------------------------------------------------------------------------- from gen.lib import (Person, Family, Event, Place, Source, Repository, - MediaObject) + MediaObject, Citation) from gen.db import DbTxn from gen.ggettext import sgettext as _ import const @@ -205,41 +206,11 @@ class MergeSourceQuery(object): self.database.commit_source(self.phoenix, trans) for (class_name, handle) in self.database.find_backlink_handles( old_handle): - if class_name == Person.__name__: - person = self.database.get_person_from_handle(handle) - assert(person.has_source_reference(old_handle)) - person.replace_source_references(old_handle, new_handle) - self.database.commit_person(person, trans) - elif class_name == Family.__name__: - family = self.database.get_family_from_handle(handle) - assert(family.has_source_reference(old_handle)) - family.replace_source_references(old_handle, new_handle) - self.database.commit_family(family, trans) - elif class_name == Event.__name__: - event = self.database.get_event_from_handle(handle) - assert(event.has_source_reference(old_handle)) - event.replace_source_references(old_handle, new_handle) - self.database.commit_event(event, trans) - elif class_name == Source.__name__: - source = self.database.get_source_from_handle(handle) - assert(source.has_source_reference(old_handle)) - source.replace_source_references(old_handle, new_handle) - self.database.commit_source(source, trans) - elif class_name == Place.__name__: - place = self.database.get_place_from_handle(handle) - assert(place.has_source_reference(old_handle)) - place.replace_source_references(old_handle, new_handle) - self.database.commit_place(place, trans) - elif class_name == MediaObject.__name__: - obj = self.database.get_object_from_handle(handle) - assert(obj.has_source_reference(old_handle)) - obj.replace_source_references(old_handle, new_handle) - self.database.commit_media_object(obj, trans) - elif class_name == Repository.__name__: - repo = self.database.get_repository_from_handle(handle) - assert(repo.has_source_reference(old_handle)) - repo.replace_source_references(old_handle, new_handle) - self.database.commit_repository(repo, trans) + if class_name == Citation.__name__: + citation = self.database.get_citation_from_handle(handle) + assert(citation.has_source_reference(old_handle)) + citation.replace_source_references(old_handle, new_handle) + self.database.commit_citation(citation, trans) else: raise MergeError("Encounter an object of type %s that has " "a source reference." % class_name) diff --git a/src/gen/db/undoredo.py b/src/gen/db/undoredo.py index f39fdf54d..5641bd799 100644 --- a/src/gen/db/undoredo.py +++ b/src/gen/db/undoredo.py @@ -2,6 +2,7 @@ # Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 2004-2006 Donald N. Allingham +# 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 @@ -60,7 +61,7 @@ DBERRS = (db.DBRunRecoveryError, db.DBAccessError, db.DBPageNotFoundError, db.DBInvalidArgError) _SIGBASE = ('person', 'family', 'source', 'event', 'media', - 'place', 'repository', 'reference', 'note', 'tag') + 'place', 'repository', 'reference', 'note', 'tag', 'citation') #------------------------------------------------------------------------- # # DbUndo class @@ -84,6 +85,8 @@ class DbUndo(object): self.redoq = deque() self.undo_history_timestamp = time.time() self.txn = None + # N.B. the databases have to be in the same order as the numbers in + # xxx_KEY in gen/db/dbconst.py self.mapbase = ( self.db.person_map, self.db.family_map, @@ -95,6 +98,7 @@ class DbUndo(object): self.db.reference_map, self.db.note_map, self.db.tag_map, + self.db.citation_map, ) def clear(self): diff --git a/src/gen/db/upgrade.py b/src/gen/db/upgrade.py index 8ff99df87..3248f363f 100644 --- a/src/gen/db/upgrade.py +++ b/src/gen/db/upgrade.py @@ -405,12 +405,17 @@ def gramps_upgrade_16(self): # 7 Media Objects upgraded with 4 citations in 4 secs # 852 Places upgraded with 0 citations in 1 secs +# another run +#Number of new objects upgraded: +# 73 People upgraded with 76 citations in 36 secs +# 35 Families upgraded with 36 citations in 18 secs +# 3403 Events upgraded with 4 citations in 9 secs +# 7 Media Objects upgraded with 4 citations in 2 secs +# 852 Places upgraded with 0 citations in 1 secs + + - self.reset() - self.set_total(6) - self.reindex_reference_map(self.update) - self.reset() # Bump up database version. Separate transaction to save metadata. with BSDDBTxn(self.env, self.metadata) as txn: diff --git a/src/gen/db/write.py b/src/gen/db/write.py index 9fc1a45ef..a02d22cf0 100644 --- a/src/gen/db/write.py +++ b/src/gen/db/write.py @@ -1880,6 +1880,10 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback): self.__open_undodb() self.db_is_open = True upgrade.gramps_upgrade_16(self) + self.reset() + self.set_total(6) + self.reindex_reference_map(self.update) + self.reset() # Close undo database self.__close_undodb() self.db_is_open = False diff --git a/src/gen/lib/citation.py b/src/gen/lib/citation.py index c868bcca2..f5c275be5 100644 --- a/src/gen/lib/citation.py +++ b/src/gen/lib/citation.py @@ -191,15 +191,16 @@ class Citation(MediaBase, NoteBase, PrimaryObject, RefBase, DateBase): return self.media_list # def get_sourcref_child_list(self): -# # FIXME: I think we no longer need to handle source references # """ # Return the list of child secondary objects that may refer sources. +# Only the Citation Primary object refers to sources, none of the +# child objects do. # # :returns: Returns the list of child secondary child objects that may # refer sources. # :rtype: list # """ -# return self.media_list + self.ref +# return [] def get_note_child_list(self): """ @@ -234,46 +235,48 @@ class Citation(MediaBase, NoteBase, PrimaryObject, RefBase, DateBase): ret += [('Source', self.ref)] return ret -# def has_source_reference(self, src_handle) : -# """ -# Return True if any of the child objects has reference to this source -# handle. -# -# :param src_handle: The source handle to be checked. -# :type src_handle: str -# :returns: Returns whether any of it's child objects has reference to -# this source handle. -# :rtype: bool -# """ -# for item in self.get_sourcref_child_list(): -# if item.has_source_reference(src_handle): -# return True -# -# return False -# -# def remove_source_references(self, src_handle_list): -# """ -# Remove references to all source handles in the list in all child -# objects. -# -# :param src_handle_list: The list of source handles to be removed. -# :type src_handle_list: list -# """ -# for item in self.get_sourcref_child_list(): -# item.remove_source_references(src_handle_list) -# -# def replace_source_references(self, old_handle, new_handle): -# """ -# Replace references to source_handles in the list in this object and -# all child objects and merge equivalent entries. -# -# :param old_handle: The source handle to be replaced. -# :type old_handle: str -# :param new_handle: The source handle to replace the old one with. -# :type new_handle: str -# """ -# for item in self.get_sourcref_child_list(): -# item.replace_source_references(old_handle, new_handle) + def has_source_reference(self, src_handle) : + """ + Return True if any of the child objects has reference to this source + handle. + + :param src_handle: The source handle to be checked. + :type src_handle: str + :returns: Returns whether any of it's child objects has reference to + this source handle. + :rtype: bool + + Only the Citation Primary object refers to sources, none of the + child objects do. Also, the Citation object only ever refers to one + Source, so only that reference needs to be checked. + """ + if src_handle == self.get_reference_handle(): + return True + + return False + + def remove_source_references(self, src_handle_list): + """ + Remove references to all source handles in the list in all child + objects. + + :param src_handle_list: The list of source handles to be removed. + :type src_handle_list: list + """ + self.set_reference_handle(None) + + def replace_source_references(self, old_handle, new_handle): + """ + Replace references to source_handles in the list in this object and + all child objects and merge equivalent entries. + + :param old_handle: The source handle to be replaced. + :type old_handle: str + :param new_handle: The source handle to replace the old one with. + :type new_handle: str + """ + if old_handle == self.get_reference_handle(): + self.set_reference_handle(new_handle) def merge(self, acquisition): """ @@ -319,9 +322,9 @@ class Citation(MediaBase, NoteBase, PrimaryObject, RefBase, DateBase): return self.confidence def set_page(self, page): - """Set the page indicator of the SourceRef.""" + """Set the page indicator of the Citation.""" self.page = page def get_page(self): - """Get the page indicator of the SourceRef.""" + """Get the page indicator of the Citation.""" return self.page diff --git a/src/gen/lib/event.py b/src/gen/lib/event.py index 67230ce8d..e5cb78942 100644 --- a/src/gen/lib/event.py +++ b/src/gen/lib/event.py @@ -200,7 +200,7 @@ class Event(CitationBase, NoteBase, MediaBase, AttributeBase, """ return self.media_list + self.attribute_list - def get_citationref_child_list(self): + def get_citation_child_list(self): """ Return the list of child secondary objects that may refer citations. @@ -242,7 +242,7 @@ class Event(CitationBase, NoteBase, MediaBase, AttributeBase, :returns: Returns the list of objects referencing primary objects. :rtype: list """ - return self.get_citationref_child_list() + return self.get_citation_child_list() def is_empty(self): """ @@ -275,7 +275,8 @@ class Event(CitationBase, NoteBase, MediaBase, AttributeBase, self.__description != other.__description \ or self.private != other.private or \ (not self.get_date_object().is_equal(other.get_date_object())) or \ - len(self.get_citation_references()) != len(other.get_citation_references()): + len(self.get_citation_references()) != \ + len(other.get_citation_references()): return False index = 0 diff --git a/src/gen/lib/eventref.py b/src/gen/lib/eventref.py index 2e8dc7daf..af87b17ec 100644 --- a/src/gen/lib/eventref.py +++ b/src/gen/lib/eventref.py @@ -3,6 +3,7 @@ # # 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 # it under the terms of the GNU General Public License as published by @@ -107,12 +108,12 @@ class EventRef(SecondaryObject, PrivacyBase, NoteBase, AttributeBase, RefBase): """ return self.attribute_list - def get_sourcref_child_list(self): + def get_citation_child_list(self): """ - Return the list of child secondary objects that may refer sources. + Return the list of child secondary objects that may refer citations. :returns: Returns the list of child secondary child objects that may - refer sources. + refer citations. :rtype: list """ return self.attribute_list @@ -149,7 +150,7 @@ class EventRef(SecondaryObject, PrivacyBase, NoteBase, AttributeBase, RefBase): :returns: Returns the list of objects referencing primary objects. :rtype: list """ - return self.get_sourcref_child_list() + return self.get_citation_child_list() def has_source_reference(self, src_handle) : """ diff --git a/src/gen/lib/family.py b/src/gen/lib/family.py index cb87fca18..6b5dd4000 100644 --- a/src/gen/lib/family.py +++ b/src/gen/lib/family.py @@ -274,7 +274,7 @@ class Family(CitationBase, NoteBase, MediaBase, AttributeBase, LdsOrdBase, add_list = filter(None, self.lds_ord_list) return self.media_list + self.attribute_list + add_list - def get_citationref_child_list(self): + def get_citation_child_list(self): """ Return the list of child secondary objects that may refer citations. @@ -325,7 +325,7 @@ class Family(CitationBase, NoteBase, MediaBase, AttributeBase, LdsOrdBase, :returns: Returns the list of objects referencing primary objects. :rtype: list """ - return self.get_citationref_child_list() + return self.get_citation_child_list() def merge(self, acquisition): """ diff --git a/src/gen/lib/mediaobj.py b/src/gen/lib/mediaobj.py index 138c3ed61..a425f8dd1 100644 --- a/src/gen/lib/mediaobj.py +++ b/src/gen/lib/mediaobj.py @@ -151,17 +151,7 @@ class MediaObject(CitationBase, NoteBase, DateBase, AttributeBase, """ return self.attribute_list + self.source_list -# def get_sourcref_child_list(self): -# """ -# Return the list of child secondary objects that may refer sources. -# -# :returns: Returns the list of child secondary child objects that may -# refer sources. -# :rtype: list -# """ -# return self.attribute_list -# - def get_citationref_child_list(self): + def get_citation_child_list(self): """ Return the list of child secondary objects that may refer to citations. @@ -169,8 +159,6 @@ class MediaObject(CitationBase, NoteBase, DateBase, AttributeBase, refer to citations. :rtype: list """ - # N.B. the citation_list of the media object is not a child object - # it is a direct reference from Media to a citation. return self.attribute_list def get_note_child_list(self): @@ -203,7 +191,7 @@ class MediaObject(CitationBase, NoteBase, DateBase, AttributeBase, :returns: Returns the list of objects referencing primary objects. :rtype: list """ - return self.attribute_list + return self.get_citation_child_list() def merge(self, acquisition): """ diff --git a/src/gen/lib/mediaref.py b/src/gen/lib/mediaref.py index 9b1c4afe4..9f6e2b44a 100644 --- a/src/gen/lib/mediaref.py +++ b/src/gen/lib/mediaref.py @@ -3,6 +3,7 @@ # # 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 # it under the terms of the GNU General Public License as published by @@ -108,7 +109,7 @@ class MediaRef(SecondaryObject, PrivacyBase, CitationBase, NoteBase, RefBase, refer notes. :rtype: list """ - return self.attribute_list # + self.source_list + return self.attribute_list def get_referenced_handles(self): """ @@ -132,7 +133,7 @@ class MediaRef(SecondaryObject, PrivacyBase, CitationBase, NoteBase, RefBase, :returns: Returns the list of objects referencing primary objects. :rtype: list """ - return self.attribute_list # + self.source_list + return self.get_citation_child_list() def is_equivalent(self, other): """ diff --git a/src/gen/lib/person.py b/src/gen/lib/person.py index b87b4ccb5..5ce18323d 100644 --- a/src/gen/lib/person.py +++ b/src/gen/lib/person.py @@ -375,7 +375,7 @@ class Person(CitationBase, NoteBase, AttributeBase, MediaBase, self.person_ref_list ) - def get_citationref_child_list(self): + def get_citation_child_list(self): """ Return the list of child secondary objects that may refer citations. @@ -434,7 +434,7 @@ class Person(CitationBase, NoteBase, AttributeBase, MediaBase, :returns: Returns the list of objects referencing primary objects. :rtype: list """ - return self.get_citationref_child_list() + return self.get_citation_child_list() def merge(self, acquisition): """ diff --git a/src/gen/lib/place.py b/src/gen/lib/place.py index 5936cc3be..b9f745507 100644 --- a/src/gen/lib/place.py +++ b/src/gen/lib/place.py @@ -154,7 +154,7 @@ class Place(CitationBase, NoteBase, MediaBase, UrlBase, PrimaryObject): ret.append(self.main_loc) return ret - def get_citationref_child_list(self): + def get_citation_child_list(self): """ Return the list of child secondary objects that may refer citations. @@ -181,7 +181,7 @@ class Place(CitationBase, NoteBase, MediaBase, UrlBase, PrimaryObject): :returns: Returns the list of objects referencing primary objects. :rtype: list """ - return self.media_list + return self.get_citation_child_list() def get_referenced_handles(self): """ diff --git a/src/gen/lib/primaryobj.py b/src/gen/lib/primaryobj.py index a12fea822..f8371e10d 100644 --- a/src/gen/lib/primaryobj.py +++ b/src/gen/lib/primaryobj.py @@ -2,6 +2,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 # it under the terms of the GNU General Public License as published by @@ -214,6 +215,7 @@ class PrimaryObject(BasicPrimaryObject): of this object type. :rtype: bool """ + # FIXME: SourceBase is no longer used so this needs to be changed if classname == 'Source' and isinstance(self, SourceBase): return self.has_source_reference(handle) elif classname == 'MediaObject' and isinstance(self, MediaBase): diff --git a/src/gen/lib/repo.py b/src/gen/lib/repo.py index 9429af3f5..f1c772fbf 100644 --- a/src/gen/lib/repo.py +++ b/src/gen/lib/repo.py @@ -3,6 +3,7 @@ # # 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 # it under the terms of the GNU General Public License as published by @@ -98,12 +99,12 @@ class Repository(NoteBase, AddressBase, UrlBase, PrimaryObject): """ return self.address_list + self.urls - def get_sourcref_child_list(self): + def get_citation_child_list(self): """ - Return the list of child secondary objects that may refer sources. + Return the list of child secondary objects that may refer citations. :returns: Returns the list of child secondary child objects that may - refer sources. + refer citations. :rtype: list """ return self.address_list @@ -126,7 +127,7 @@ class Repository(NoteBase, AddressBase, UrlBase, PrimaryObject): :returns: Returns the list of objects referencing primary objects. :rtype: list """ - return self.address_list + return self.get_citation_child_list() def get_referenced_handles(self): """ diff --git a/src/gen/lib/src.py b/src/gen/lib/src.py index 4384329cf..25181e573 100644 --- a/src/gen/lib/src.py +++ b/src/gen/lib/src.py @@ -3,6 +3,7 @@ # # 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 # it under the terms of the GNU General Public License as published by @@ -150,12 +151,12 @@ class Source(MediaBase, NoteBase, PrimaryObject): """ return self.media_list + self.reporef_list - def get_sourcref_child_list(self): + def get_citation_child_list(self): """ - Return the list of child secondary objects that may refer sources. + Return the list of child secondary objects that may refer citations. :returns: Returns the list of child secondary child objects that may - refer sources. + refer citations. :rtype: list """ return self.media_list @@ -178,7 +179,7 @@ class Source(MediaBase, NoteBase, PrimaryObject): :returns: Returns the list of objects referencing primary objects. :rtype: list """ - return self.media_list + self.reporef_list + return self.get_citation_child_list() + self.reporef_list def get_referenced_handles(self): """ diff --git a/src/gen/lib/srcbase.py b/src/gen/lib/srcbase.py index ef92e430a..e0d324548 100644 --- a/src/gen/lib/srcbase.py +++ b/src/gen/lib/srcbase.py @@ -3,6 +3,7 @@ # # Copyright (C) 2006 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 # it under the terms of the GNU General Public License as published by @@ -42,7 +43,7 @@ class SourceBase(object): """ Base class for storing source references. """ - +# FIXME: SourceBase is no longer used so this module needs to be removed def __init__(self, source=None): """ Create a new SourceBase, copying from source if not None. diff --git a/src/glade/Makefile.am b/src/glade/Makefile.am index b1b4c7881..fd5942c31 100644 --- a/src/glade/Makefile.am +++ b/src/glade/Makefile.am @@ -48,6 +48,7 @@ dist_pkgdata_DATA = \ mergeevent.glade \ mergeplace.glade \ mergesource.glade \ + mergecitation.glade \ mergerepository.glade \ mergemedia.glade \ mergenote.glade \ diff --git a/src/glade/mergecitation.glade b/src/glade/mergecitation.glade new file mode 100644 index 000000000..c9985bc2b --- /dev/null +++ b/src/glade/mergecitation.glade @@ -0,0 +1,508 @@ + + + + + + True + 500 + dialog + False + + + True + vertical + + + True + + + True + True + + + False + False + 15 + + + + + True + Select the citation that will provide the +primary data for the merged citation. + + + False + 1 + + + + + True + + + True + True + True + + + True + True + + + + + False + False + + + + + True + True + True + handle_btn1 + + + True + True + + + + + False + False + 1 + + + + + False + 5 + 2 + + + + + True + True + + + True + + + True + 6 + 6 + 4 + 6 + 6 + + + True + <b>Source 1</b> + True + + + GTK_FILL + + + + + + True + <b>Source 2</b> + True + + + 2 + 3 + GTK_FILL + + + + + + Volume/Page: + True + True + False + True + True + + + 1 + 2 + GTK_FILL + + + + + + Volume/Page: + True + True + False + True + True + page_btn1 + + + 2 + 3 + 1 + 2 + GTK_FILL + + + + + + Date: + True + True + False + True + True + + + 2 + 3 + GTK_FILL + + + + + + Date: + True + True + False + True + True + date_btn1 + + + 2 + 3 + 2 + 3 + GTK_FILL + + + + + + Confidence: + True + True + False + True + True + + + 3 + 4 + GTK_FILL + + + + + + Confidence: + True + True + False + True + True + confidence_btn1 + + + 2 + 3 + 3 + 4 + GTK_FILL + + + + + + Gramps ID: + True + True + False + True + True + + + 5 + 6 + GTK_FILL + + + + + + Gramps ID: + True + True + False + True + True + gramps_btn1 + + + 2 + 3 + 5 + 6 + GTK_FILL + + + + + + True + True + False + + + 1 + 2 + 1 + 2 + + + + + + True + True + False + + + 3 + 4 + 1 + 2 + + + + + + True + True + False + + + 1 + 2 + 2 + 3 + + + + + + True + True + False + + + 3 + 4 + 2 + 3 + + + + + + True + True + False + + + 1 + 2 + 3 + 4 + + + + + + True + True + False + + + 3 + 4 + 3 + 4 + + + + + + True + True + False + + + 1 + 2 + 5 + 6 + + + + + + True + True + False + + + 3 + 4 + 5 + 6 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + True + Notes, media objects and data-items of both citations will be combined. + True + + + 6 + 1 + + + + + + + True + Detailed Selection + + + + + 3 + False + + + + + 1 + + + + + True + end + + + gtk-cancel + True + True + True + False + True + + + False + False + 0 + + + + + gtk-ok + True + True + True + False + True + + + False + False + 1 + + + + + gtk-help + True + True + True + False + True + + + False + False + 2 + + + + + False + end + 0 + + + + + + citation_cancel + citation_ok + citation_help + + + diff --git a/src/gui/editors/displaytabs/backreflist.py b/src/gui/editors/displaytabs/backreflist.py index d04c0e053..bb2fd95c0 100644 --- a/src/gui/editors/displaytabs/backreflist.py +++ b/src/gui/editors/displaytabs/backreflist.py @@ -3,6 +3,7 @@ # # Copyright (C) 2000-2007 Donald N. Allingham # Copyright (C) 2009-2011 Gary Burton +# 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 @@ -135,7 +136,8 @@ class BackRefList(EmbeddedList): def edit_button_clicked(self, obj): from gui.editors import EditEvent, EditPerson, EditFamily, EditSource, \ - EditPlace, EditMedia, EditRepository + EditPlace, EditMedia, EditRepository, \ + EditCitation (reftype, ref) = self.find_node() if reftype == 'Person': @@ -156,6 +158,12 @@ class BackRefList(EmbeddedList): EditSource(self.dbstate, self.uistate, [], source) except Errors.WindowActiveError: pass + elif reftype == 'Citation': + try: + citation = self.dbstate.db.get_citation_from_handle(ref) + EditCitation(self.dbstate, self.uistate, [], citation) + except Errors.WindowActiveError: + pass elif reftype == 'Place': try: place = self.dbstate.db.get_place_from_handle(ref) diff --git a/src/gui/editors/displaytabs/citationembedlist.py b/src/gui/editors/displaytabs/citationembedlist.py index 422c2a429..f0b297fa0 100644 --- a/src/gui/editors/displaytabs/citationembedlist.py +++ b/src/gui/editors/displaytabs/citationembedlist.py @@ -106,7 +106,7 @@ class CitationEmbedList(EmbeddedList, DbGUIElement): """ Return the stock-id icon name associated with the display tab """ - return 'gramps-citations' + return 'gramps-source' def get_data(self): """ @@ -155,11 +155,10 @@ class CitationEmbedList(EmbeddedList, DbGUIElement): LOG.debug("selected citation: %s" % citation) if citation: try: - source = self.dbstate.db.get_source_from_handle(citation.ref) from gui.editors import EditCitation - EditCitation(self.dbstate, self.uistate, self.track, - citation, source, self.add_callback, - self.callertitle) + 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"), @@ -179,11 +178,10 @@ class CitationEmbedList(EmbeddedList, DbGUIElement): if handle: citation = self.dbstate.db.get_citation_from_handle(handle) LOG.debug("selected citation: %s" % citation) - source = self.dbstate.db.get_source_from_handle(citation.ref) try: from gui.editors import EditCitation EditCitation(self.dbstate, self.uistate, self.track, citation, - source, callertitle = self.callertitle) + callertitle = self.callertitle) except Errors.WindowActiveError: pass diff --git a/src/gui/editors/editaddress.py b/src/gui/editors/editaddress.py index 6a8635b35..64f363c46 100644 --- a/src/gui/editors/editaddress.py +++ b/src/gui/editors/editaddress.py @@ -4,6 +4,7 @@ # Copyright (C) 2000-2007 Donald N. Allingham # Copyright (C) 2009 Gary Burton # Copyright (C) 2010 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 @@ -49,7 +50,7 @@ import gtk from editsecondary import EditSecondary from gen.lib import NoteType from glade import Glade -from displaytabs import SourceEmbedList, NoteTab +from displaytabs import CitationEmbedList, NoteTab from gui.widgets import MonitoredDate, MonitoredEntry, PrivacyButton #------------------------------------------------------------------------- @@ -134,7 +135,10 @@ class EditAddress(EditSecondary): notebook = gtk.Notebook() - self.srcref_list = SourceEmbedList(self.dbstate,self.uistate,self.track,self.obj) + self.srcref_list = CitationEmbedList(self.dbstate, + self.uistate, + self.track, + self.obj.get_citation_list()) self._add_tab(notebook, self.srcref_list) self.track_ref_for_deletion("srcref_list") diff --git a/src/gui/editors/editchildref.py b/src/gui/editors/editchildref.py index 79d352ef1..4f676c27a 100644 --- a/src/gui/editors/editchildref.py +++ b/src/gui/editors/editchildref.py @@ -3,6 +3,7 @@ # # Copyright (C) 2000-2006 Donald N. Allingham # 2009 Gary Burton +# 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 @@ -49,7 +50,7 @@ from editsecondary import EditSecondary from gen.lib import NoteType import Errors from glade import Glade -from displaytabs import SourceEmbedList, NoteTab +from displaytabs import CitationEmbedList, NoteTab from gui.widgets import MonitoredDataType, PrivacyButton from gen.display.name import displayer as name_displayer @@ -144,8 +145,10 @@ class EditChildRef(EditSecondary): """ notebook = gtk.Notebook() - self.srcref_list = SourceEmbedList( - self.dbstate, self.uistate, self.track, self.obj) + self.srcref_list = CitationEmbedList(self.dbstate, + self.uistate, + self.track, + self.obj.get_citation_list()) self._add_tab(notebook, self.srcref_list) self.track_ref_for_deletion("srcref_list") diff --git a/src/gui/editors/editcitation.py b/src/gui/editors/editcitation.py index 108d51aef..36d8db3b3 100644 --- a/src/gui/editors/editcitation.py +++ b/src/gui/editors/editcitation.py @@ -61,19 +61,22 @@ from glade import Glade class EditCitation(EditPrimary): - def __init__(self, dbstate, uistate, track, obj, source, callback=None, + 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 citation. + 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 """ + if not source and obj.get_reference_handle(): + source = dbstate.db.get_source_from_handle( + obj.get_reference_handle()) self.source = source self.callertitle = callertitle EditPrimary.__init__(self, dbstate, uistate, track, obj, diff --git a/src/gui/editors/editmedia.py b/src/gui/editors/editmedia.py index aa6eca4c1..af57ffc13 100644 --- a/src/gui/editors/editmedia.py +++ b/src/gui/editors/editmedia.py @@ -51,8 +51,8 @@ import Utils from editprimary import EditPrimary from gui.widgets import (MonitoredDate, MonitoredEntry, PrivacyButton, MonitoredTagList) -from displaytabs import (SourceEmbedList, AttrEmbedList, NoteTab, - CitationEmbedList, MediaBackRefList) +from displaytabs import (CitationEmbedList, AttrEmbedList, NoteTab, + MediaBackRefList) from addmedia import AddMediaObject from QuestionDialog import ErrorDialog from glade import Glade diff --git a/src/gui/editors/editname.py b/src/gui/editors/editname.py index 112b11e15..6c1324348 100644 --- a/src/gui/editors/editname.py +++ b/src/gui/editors/editname.py @@ -5,6 +5,7 @@ # 2008-2009 Benny Malengier # 2009 Gary Burton # 2010 Michiel D. Nauta +# 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 @@ -48,7 +49,7 @@ import config from gen.display.name import displayer as name_displayer from editsecondary import EditSecondary from gen.lib import NoteType -from displaytabs import GrampsTab, SourceEmbedList, NoteTab, SurnameTab +from displaytabs import GrampsTab, CitationEmbedList, NoteTab, SurnameTab from gui.widgets import (MonitoredEntry, MonitoredMenu, MonitoredDate, MonitoredDataType, PrivacyButton) from glade import Glade @@ -272,7 +273,9 @@ class EditName(EditSecondary): self._add_tab(notebook, self.gennam) self.track_ref_for_deletion("gennam") - self.srcref_list = SourceEmbedList(self.dbstate,self.uistate,self.track,self.obj) + self.srcref_list = CitationEmbedList(self.dbstate, self.uistate, + self.track, + self.obj.get_citation_list()) self._add_tab(notebook, self.srcref_list) self.track_ref_for_deletion("srcref_list") diff --git a/src/gui/editors/editpersonref.py b/src/gui/editors/editpersonref.py index 1fbdc4dc7..3c3bd03ba 100644 --- a/src/gui/editors/editpersonref.py +++ b/src/gui/editors/editpersonref.py @@ -3,6 +3,7 @@ # # Copyright (C) 2000-2007 Donald N. Allingham # 2009 Gary Burton +# 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 @@ -50,7 +51,7 @@ from editsecondary import EditSecondary from gen.lib import NoteType from gui.widgets import MonitoredEntry, PrivacyButton from gui.selectors import SelectorFactory -from displaytabs import SourceEmbedList, NoteTab +from displaytabs import CitationEmbedList, NoteTab from glade import Glade #------------------------------------------------------------------------- @@ -141,8 +142,9 @@ class EditPersonRef(EditSecondary): notebook = gtk.Notebook() - self.srcref_list = SourceEmbedList(self.dbstate, self.uistate, - self.track, self.obj) + self.srcref_list = CitationEmbedList(self.dbstate, self.uistate, + self.track, + self.obj.get_citation_list()) self._add_tab(notebook, self.srcref_list) self.track_ref_for_deletion("srcref_list") diff --git a/src/plugins/gramplet/Citations.py b/src/plugins/gramplet/Citations.py index 24c37e2b9..f9d98dcd7 100644 --- a/src/plugins/gramplet/Citations.py +++ b/src/plugins/gramplet/Citations.py @@ -45,7 +45,7 @@ class Citations(Gramplet): top = gtk.TreeView() titles = [('', NOSORT, 50,), (_('Source'), 1, 200), - (_('Reference'), 2, 300), + (_('Volume/Page'), 2, 300), (_('Author'), 3, 100)] self.model = ListModel(top, titles, event_func=self.edit_citation) return top diff --git a/src/plugins/lib/libcitationview.py b/src/plugins/lib/libcitationview.py index 5b12a32cf..e253ee02d 100644 --- a/src/plugins/lib/libcitationview.py +++ b/src/plugins/lib/libcitationview.py @@ -108,7 +108,7 @@ 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") + ADD_MSG = _("Add a new citation to an existing source") EDIT_MSG = _("Edit the selected citation") SHARE_MSG = _("Share the selected source") DEL_MSG = _("Delete the selected citation") @@ -300,13 +300,13 @@ class BaseCitationView(ListView): def __blocked_text(self): """ - Return the common text used when mediaref cannot be edited + Return the common text used when citation cannot be edited """ - return _("This media reference cannot be edited at this time. " - "Either the associated media object is already being " - "edited or another media reference that is associated with " - "the same media object is being edited.\n\nTo edit this " - "media reference, you need to close the media object.") + return _("This citation cannot be edited at this time. " + "Either the associated citation is already being " + "edited or another object that is associated with " + "the same citation is being edited.\n\nTo edit this " + "citation, you need to close the object.") def merge(self, obj): """ @@ -314,12 +314,23 @@ 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.") + ErrorDialog(msg, msg2) else: import Merge Merge.MergeCitations(self.dbstate, self.uistate, mlist[0], mlist[1])