diff --git a/configure.in b/configure.in index 0859ee3fd..23ba23c4b 100644 --- a/configure.in +++ b/configure.in @@ -128,6 +128,7 @@ src/Filters/Rules/Place/Makefile src/Filters/Rules/MediaObject/Makefile src/Filters/Rules/Repository/Makefile src/Filters/Rules/Note/Makefile +src/Filters/Rules/Citation/Makefile src/Filters/SideBar/Makefile src/Simple/Makefile src/GrampsLocale/Makefile diff --git a/data/grampsxml.dtd b/data/grampsxml.dtd index 70441b0a6..ee0c79f7a 100644 --- a/data/grampsxml.dtd +++ b/data/grampsxml.dtd @@ -1,9 +1,11 @@ + + @@ -46,6 +48,7 @@ DATABASE families events places + citations sources objects repositories @@ -56,10 +59,10 @@ DATABASE tags --> - - + + + + + + + + @@ -430,11 +448,14 @@ SHARED ELEMENTS - + + + + @@ -462,9 +483,10 @@ SHARED ELEMENTS hlink IDREF #REQUIRED > - + + - + - + + status?, sealed_to?, noteref*, citationref*)> @@ -87,6 +87,12 @@ + + + + + + @@ -196,8 +202,8 @@ - - + + @@ -248,8 +254,8 @@ - - + + @@ -293,8 +299,8 @@ - - + + @@ -380,8 +386,8 @@ - - + + @@ -394,8 +400,8 @@ - - + + @@ -416,14 +422,34 @@ - - + + + + + + + + + + + + + + + + + + + + + + @@ -472,8 +498,8 @@ - - + + @@ -491,8 +517,8 @@ - - + + @@ -581,33 +607,14 @@ - + - - - - - - - - - - - + - - - - - - - - - - + @@ -651,8 +658,8 @@ - - + + @@ -679,8 +686,8 @@ - - + + @@ -709,8 +716,8 @@ - - + + diff --git a/po/POTFILES.in b/po/POTFILES.in index d320cbb14..9bf79c7fa 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -99,6 +99,7 @@ src/gui/editors/objectentries.py src/gui/editors/editaddress.py src/gui/editors/editattribute.py src/gui/editors/editchildref.py +src/gui/editors/editcitation.py src/gui/editors/editevent.py src/gui/editors/editeventref.py src/gui/editors/editfamily.py @@ -116,7 +117,6 @@ src/gui/editors/editprimary.py src/gui/editors/editreporef.py src/gui/editors/editrepository.py src/gui/editors/editsource.py -src/gui/editors/editsourceref.py src/gui/editors/editurl.py # gui/editors/displaytabs - the GUI display tabs package @@ -125,6 +125,7 @@ src/gui/editors/displaytabs/attrembedlist.py src/gui/editors/displaytabs/backreflist.py src/gui/editors/displaytabs/backrefmodel.py src/gui/editors/displaytabs/buttontab.py +src/gui/editors/displaytabs/citationembedlist.py src/gui/editors/displaytabs/dataembedlist.py src/gui/editors/displaytabs/embeddedlist.py src/gui/editors/displaytabs/eventembedlist.py @@ -139,7 +140,6 @@ src/gui/editors/displaytabs/notetab.py src/gui/editors/displaytabs/personeventembedlist.py src/gui/editors/displaytabs/personrefembedlist.py src/gui/editors/displaytabs/repoembedlist.py -src/gui/editors/displaytabs/sourceembedlist.py src/gui/editors/displaytabs/surnametab.py src/gui/editors/displaytabs/webembedlist.py @@ -156,6 +156,7 @@ src/gui/plug/report/_styleeditor.py src/gui/plug/tool.py # gui/selectors - the GUI selectors package +src/gui/selectors/selectcitation.py src/gui/selectors/selectevent.py src/gui/selectors/selectfamily.py src/gui/selectors/selectnote.py @@ -172,6 +173,7 @@ src/gui/views/pageview.py src/gui/views/tags.py # gui/views/treemodels - the GUI views package +src/gui/views/treemodels/citationtreemodel.py src/gui/views/treemodels/mediamodel.py src/gui/views/treemodels/peoplemodel.py src/gui/views/treemodels/placemodel.py @@ -197,6 +199,7 @@ src/Simple/_SimpleTable.py src/config.py # Merge package +src/Merge/mergecitation.py src/Merge/mergeevent.py src/Merge/mergefamily.py src/Merge/mergemedia.py @@ -264,6 +267,7 @@ src/plugins/gramplet/Backlinks.py src/plugins/gramplet/bottombar.gpr.py src/plugins/gramplet/CalendarGramplet.py src/plugins/gramplet/Children.py +src/plugins/gramplet/Citations.py src/plugins/gramplet/DescendGramplet.py src/plugins/gramplet/EditExifMetadata.py src/plugins/gramplet/Events.py @@ -271,17 +275,18 @@ src/plugins/gramplet/FanChartGramplet.py src/plugins/gramplet/FaqGramplet.py src/plugins/gramplet/GivenNameGramplet.py src/plugins/gramplet/gramplet.gpr.py -src/plugins/gramplet/MetadataViewer.py +#src/plugins/gramplet/MetadataViewer.py src/plugins/gramplet/Notes.py src/plugins/gramplet/PedigreeGramplet.py src/plugins/gramplet/PersonDetails.py src/plugins/gramplet/PersonResidence.py src/plugins/gramplet/PlaceDetails.py +src/plugins/gramplet/PopulateGramplet.py +src/plugins/gramplet/PopulateGramplet.gpr.py src/plugins/gramplet/QuickViewGramplet.py src/plugins/gramplet/RelativeGramplet.py src/plugins/gramplet/RepositoryDetails.py src/plugins/gramplet/SessionLogGramplet.py -src/plugins/gramplet/Sources.py src/plugins/gramplet/StatsGramplet.py src/plugins/gramplet/SurnameCloudGramplet.py src/plugins/gramplet/ToDoGramplet.py @@ -382,6 +387,7 @@ src/plugins/tool/ExtractCity.py src/plugins/tool/FindDupes.py src/plugins/tool/Leak.py src/plugins/tool/MediaManager.py +src/plugins/tool/MergeCitations.py src/plugins/tool/NotRelated.py src/plugins/tool/OwnerEditor.py src/plugins/tool/PatchNames.py @@ -396,6 +402,8 @@ src/plugins/tool/tools.gpr.py src/plugins/tool/Verify.py #plugins/view directory +src/plugins/view/citationtreeview.py +src/plugins/view/citationlistview.py src/plugins/view/eventview.py src/plugins/view/familyview.py src/plugins/view/fanchartview.py @@ -456,6 +464,7 @@ src/Filters/Rules/Person/_HasAlternateName.py src/Filters/Rules/Person/_HasAssociation.py src/Filters/Rules/Person/_HasAttribute.py src/Filters/Rules/Person/_HasBirth.py +src/Filters/Rules/Person/_HasCitation.py src/Filters/Rules/Person/_HasCommonAncestorWithFilterMatch.py src/Filters/Rules/Person/_HasCommonAncestorWith.py src/Filters/Rules/Person/_HasDeath.py @@ -473,7 +482,7 @@ src/Filters/Rules/Person/_HasNote.py src/Filters/Rules/Person/_HasNoteMatchingSubstringOf.py src/Filters/Rules/Person/_HasNoteRegexp.py src/Filters/Rules/Person/_HasRelationship.py -src/Filters/Rules/Person/_HasSource.py +src/Filters/Rules/Person/_HasSourceCount.py src/Filters/Rules/Person/_HasSourceOf.py src/Filters/Rules/Person/_HasTag.py src/Filters/Rules/Person/_HasTextMatchingSubstringOf.py @@ -530,6 +539,7 @@ src/Filters/Rules/Family/_FamilyPrivate.py src/Filters/Rules/Family/_FatherHasIdOf.py src/Filters/Rules/Family/_FatherHasNameOf.py src/Filters/Rules/Family/_HasAttribute.py +src/Filters/Rules/Family/_HasCitation.py src/Filters/Rules/Family/_HasEvent.py src/Filters/Rules/Family/_HasGallery.py src/Filters/Rules/Family/_HasIdOf.py @@ -539,7 +549,7 @@ src/Filters/Rules/Family/_HasNoteMatchingSubstringOf.py src/Filters/Rules/Family/_HasNoteRegexp.py src/Filters/Rules/Family/_HasReferenceCountOf.py src/Filters/Rules/Family/_HasRelType.py -src/Filters/Rules/Family/_HasSource.py +src/Filters/Rules/Family/_HasSourceCount.py src/Filters/Rules/Family/_HasTag.py src/Filters/Rules/Family/_IsBookmarked.py src/Filters/Rules/Family/_MatchesFilter.py @@ -559,6 +569,7 @@ src/Filters/Rules/Event/_AllEvents.py src/Filters/Rules/Event/_ChangedSince.py src/Filters/Rules/Event/_EventPrivate.py src/Filters/Rules/Event/_HasAttribute.py +src/Filters/Rules/Event/_HasCitation.py src/Filters/Rules/Event/_HasData.py src/Filters/Rules/Event/_HasGallery.py src/Filters/Rules/Event/_HasIdOf.py @@ -566,7 +577,7 @@ src/Filters/Rules/Event/_HasNote.py src/Filters/Rules/Event/_HasNoteMatchingSubstringOf.py src/Filters/Rules/Event/_HasNoteRegexp.py src/Filters/Rules/Event/_HasReferenceCountOf.py -src/Filters/Rules/Event/_HasSource.py +src/Filters/Rules/Event/_HasSourceCount.py src/Filters/Rules/Event/_HasType.py src/Filters/Rules/Event/_MatchesFilter.py src/Filters/Rules/Event/_MatchesPersonFilter.py @@ -602,13 +613,29 @@ src/Filters/Rules/Source/_HasNoteMatchingSubstringOf.py src/Filters/Rules/Source/_HasReferenceCountOf.py src/Filters/Rules/Source/_HasRepository.py src/Filters/Rules/Source/_HasRepositoryCallNumberRef.py -src/Filters/Rules/Source/_HasSource.py src/Filters/Rules/Source/_MatchesFilter.py src/Filters/Rules/Source/_MatchesRepositoryFilter.py src/Filters/Rules/Source/_MatchesTitleSubstringOf.py src/Filters/Rules/Source/_SourcePrivate.py src/Filters/Rules/Source/_RegExpIdOf.py +# Filters.Rules.Citation package +src/Filters/Rules/Citation/_AllCitations.py +src/Filters/Rules/Citation/_ChangedSince.py +src/Filters/Rules/Citation/_CitationPrivate.py +src/Filters/Rules/Citation/_HasGallery.py +src/Filters/Rules/Citation/_HasIdOf.py +src/Filters/Rules/Citation/_HasNote.py +src/Filters/Rules/Citation/_HasNoteMatchingSubstringOf.py +src/Filters/Rules/Citation/_HasNoteRegexp.py +src/Filters/Rules/Citation/_HasReferenceCountOf.py +src/Filters/Rules/Citation/_HasSource.py +src/Filters/Rules/Citation/_MatchesFilter.py +src/Filters/Rules/Citation/_MatchesPageSubstringOf.py +src/Filters/Rules/Citation/_RegExpIdOf.py +src/Filters/Rules/Citation/_MatchesRepositoryFilter.py +src/Filters/Rules/Citation/_MatchesSourceFilter.py + # Filters.Rules.MediaObject package src/Filters/Rules/MediaObject/_AllMedia.py src/Filters/Rules/MediaObject/_ChangedSince.py @@ -650,6 +677,7 @@ src/Filters/Rules/Note/_RegExpIdOf.py src/Filters/Rules/Note/_NotePrivate.py # Filters.SideBar package +src/Filters/SideBar/_CitationSidebarFilter.py src/Filters/SideBar/_EventSidebarFilter.py src/Filters/SideBar/_FamilySidebarFilter.py src/Filters/SideBar/_PersonSidebarFilter.py @@ -677,6 +705,7 @@ src/glade/dateedit.glade src/glade/editsource.glade src/glade/styleeditor.glade src/glade/dbman.glade +src/glade/editcitation.glade src/glade/editurl.glade src/glade/editrepository.glade src/glade/editreporef.glade @@ -696,6 +725,7 @@ src/glade/editplace.glade src/glade/editsourceref.glade src/glade/editname.glade src/glade/editevent.glade +src/glade/mergecitation.glade src/glade/mergedata.glade src/glade/mergeevent.glade src/glade/mergefamily.glade @@ -720,6 +750,7 @@ src/plugins/tool/eventcmp.glade src/plugins/import/importgedcom.glade src/plugins/tool/leak.glade src/plugins/tool/finddupes.glade +src/plugins/tool/mergecitations.glade src/plugins/tool/ownereditor.glade src/plugins/tool/patchnames.glade src/plugins/tool/phpgedview.glade diff --git a/po/POTFILES.skip b/po/POTFILES.skip index 62eb284bf..b01094763 100644 --- a/po/POTFILES.skip +++ b/po/POTFILES.skip @@ -65,6 +65,7 @@ src/Filters/Rules/Note/__init__.py # Filters.Rules package src/Filters/Rules/_ChangedSinceBase.py src/Filters/Rules/_HasAttributeBase.py +src/Filters/Rules/_HasCitationBase.py src/Filters/Rules/_HasEventBase.py src/Filters/Rules/_HasGalleryBase.py src/Filters/Rules/_HasLDSBase.py @@ -73,6 +74,7 @@ src/Filters/Rules/_HasNoteRegexBase.py src/Filters/Rules/_HasNoteSubstrBase.py src/Filters/Rules/_HasReferenceCountBase.py src/Filters/Rules/_HasSourceBase.py +src/Filters/Rules/_HasSourceCountBase.py src/Filters/Rules/_HasTagBase.py src/Filters/Rules/_HasTextMatchingRegexpOf.py src/Filters/Rules/__init__.py diff --git a/src/Bookmarks.py b/src/Bookmarks.py index c7954262f..9f156c249 100644 --- a/src/Bookmarks.py +++ b/src/Bookmarks.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 @@ -364,6 +365,18 @@ class SourceBookmarks(ListBookmarks) : def connect_signals(self): self.dbstate.db.connect('source-delete', self.remove_handles) +class CitationBookmarks(ListBookmarks) : + "Handle the bookmarks interface for Gramps." + def __init__(self, dbstate, uistate, bookmarks, goto_handle): + ListBookmarks.__init__(self, dbstate, uistate, bookmarks, + goto_handle) + + def make_label(self, handle): + return Utils.navigation_label(self.dbstate.db, 'Citation', handle) + + def connect_signals(self): + self.dbstate.db.connect('citation-delete', self.remove_handles) + class MediaBookmarks(ListBookmarks) : "Handle the bookmarks interface for Gramps." diff --git a/src/DateHandler/_Date_zh.py b/src/DateHandler/_Date_zh.py index d4e6e3f58..21b0192f6 100644 --- a/src/DateHandler/_Date_zh.py +++ b/src/DateHandler/_Date_zh.py @@ -19,7 +19,7 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # DateHandler/_Date_zh.py -# $Id$ +# $Id: _Date_zh.py 18361 2011-10-23 03:13:50Z paul-franklin $ # #------------------------------------------------------------------------- diff --git a/src/DbState.py b/src/DbState.py index 403e8aaab..daf79c545 100644 --- a/src/DbState.py +++ b/src/DbState.py @@ -2,6 +2,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 # it under the terms of the GNU General Public License as published by @@ -65,6 +66,7 @@ class DbState(Callback): config.get('preferences.oprefix'), config.get('preferences.fprefix'), config.get('preferences.sprefix'), + config.get('preferences.cprefix'), config.get('preferences.pprefix'), config.get('preferences.eprefix'), config.get('preferences.rprefix'), diff --git a/src/DdTargets.py b/src/DdTargets.py index fc18d96b3..572a7bb1f 100644 --- a/src/DdTargets.py +++ b/src/DdTargets.py @@ -4,6 +4,7 @@ # 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 @@ -135,6 +136,7 @@ class _DdTargets(object): self.SOURCE_LINK = _DdType(self, 'source-link') self.URL = _DdType(self, 'url') self.SURNAME = _DdType(self, 'surname') + self.CITATION_LINK = _DdType(self, 'citation-link') # List of all types that are used between # gramps widgets but should not be exported @@ -162,7 +164,8 @@ class _DdTargets(object): self.SOURCEREF, self.SOURCE_LINK, self.URL, - self.SURNAME + self.SURNAME, + self.CITATION_LINK ] self.CHILD = _DdType(self, 'child') diff --git a/src/DisplayState.py b/src/DisplayState.py index c30c52c18..288865ae5 100644 --- a/src/DisplayState.py +++ b/src/DisplayState.py @@ -4,6 +4,7 @@ # Copyright (C) 2000-2007 Donald N. Allingham # Copyright (C) 2008 Brian G. Matherly # 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 @@ -365,6 +366,7 @@ class DisplayState(gen.utils.Callback): 'Event': _("No active event"), 'Place': _("No active place"), 'Source': _("No active source"), + 'Citation': _("No active citation"), 'Repository': _("No active repository"), 'Media': _("No active media"), 'Note': _("No active note"), diff --git a/src/Filters/Rules/Citation/Makefile.am b/src/Filters/Rules/Citation/Makefile.am new file mode 100644 index 000000000..2ce89afa3 --- /dev/null +++ b/src/Filters/Rules/Citation/Makefile.am @@ -0,0 +1,33 @@ +# This is the src/Filters/Rules/Citation level Makefile for Gramps + +pkgdatadir = $(datadir)/@PACKAGE@/Filters/Rules/Citation + +pkgdata_PYTHON = \ + __init__.py \ + _AllCitations.py \ + _ChangedSince.py \ + _CitationPrivate.py \ + _HasGallery.py \ + _HasIdOf.py \ + _HasNote.py \ + _HasNoteMatchingSubstringOf.py \ + _HasNoteRegexp.py \ + _HasReferenceCountOf.py \ + _HasSource.py \ + _MatchesFilter.py \ + _MatchesPageSubstringOf.py \ + _MatchesRepositoryFilter.py \ + _MatchesSourceFilter.py \ + _RegExpIdOf.py + +pkgpyexecdir = @pkgpyexecdir@/Filters/Rules/Citation +pkgpythondir = @pkgpythondir@/Filters/Rules/Citation + +# Clean up all the byte-compiled files +MOSTLYCLEANFILES = *pyc *pyo + +GRAMPS_PY_MODPATH = "../../../" + +pycheck: + (export PYTHONPATH=$(GRAMPS_PY_MODPATH); \ + pychecker $(pkgdata_PYTHON)); diff --git a/src/Filters/Rules/Citation/_AllCitations.py b/src/Filters/Rules/Citation/_AllCitations.py new file mode 100644 index 000000000..4e5b72909 --- /dev/null +++ b/src/Filters/Rules/Citation/_AllCitations.py @@ -0,0 +1,47 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2002-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 +# 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$ + +#------------------------------------------------------------------------- +# +# Standard Python modules +# +#------------------------------------------------------------------------- +from gen.ggettext import gettext as _ + +#------------------------------------------------------------------------- +# +# GRAMPS modules +# +#------------------------------------------------------------------------- +from Filters.Rules._Everything import Everything + +#------------------------------------------------------------------------- +# +# Everyone +# +#------------------------------------------------------------------------- +class AllCitations(Everything): + """Matches every citation""" + + name = _('Every citation') + description = _('Matches every citation in the database') diff --git a/src/Filters/Rules/Citation/_ChangedSince.py b/src/Filters/Rules/Citation/_ChangedSince.py new file mode 100644 index 000000000..b96c75474 --- /dev/null +++ b/src/Filters/Rules/Citation/_ChangedSince.py @@ -0,0 +1,50 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2002-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 +# 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$ + +#------------------------------------------------------------------------- +# +# Standard Python modules +# +#------------------------------------------------------------------------- +from gen.ggettext import gettext as _ + +#------------------------------------------------------------------------- +# +# GRAMPS modules +# +#------------------------------------------------------------------------- +from Filters.Rules._ChangedSinceBase import ChangedSinceBase + +#------------------------------------------------------------------------- +# +# ChangedSince +# +#------------------------------------------------------------------------- +class ChangedSince(ChangedSinceBase): + """Rule that checks for citations changed since a specific time.""" + + labels = [ _('Changed after:'), _('but before:') ] + name = _('Citations changed after ') + description = _("Matches citation records changed after a specified " + "date-time (yyyy-mm-dd hh:mm:ss) or in the range, if a second " + "date-time is given.") diff --git a/src/Filters/Rules/Citation/_CitationPrivate.py b/src/Filters/Rules/Citation/_CitationPrivate.py new file mode 100644 index 000000000..b4276fc3a --- /dev/null +++ b/src/Filters/Rules/Citation/_CitationPrivate.py @@ -0,0 +1,45 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2002-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 +# 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$ + +#------------------------------------------------------------------------- +# +# Standard Python modules +# +#------------------------------------------------------------------------- +from gen.ggettext import gettext as _ + +#------------------------------------------------------------------------- +# +# GRAMPS modules +# +#------------------------------------------------------------------------- +from Filters.Rules._IsPrivate import IsPrivate + +#------------------------------------------------------------------------- +# "Family marked private" +#------------------------------------------------------------------------- +class CitationPrivate(IsPrivate): + """Citation marked private""" + + name = _('Citations marked private') + description = _("Matches citations that are indicated as private") diff --git a/src/Filters/Rules/Citation/_HasGallery.py b/src/Filters/Rules/Citation/_HasGallery.py new file mode 100755 index 000000000..48f3a1902 --- /dev/null +++ b/src/Filters/Rules/Citation/_HasGallery.py @@ -0,0 +1,47 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2008 Brian G. Matherly +# Copyright (C) 2008 Jerome Rapinat +# Copyright (C) 2008 Benny Malengier +# 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$ + +#------------------------------------------------------------------------- +# +# Standard Python modules +# +#------------------------------------------------------------------------- +from gen.ggettext import gettext as _ + +#------------------------------------------------------------------------- +# +# GRAMPS modules +# +#------------------------------------------------------------------------- +from Filters.Rules._HasGalleryBase import HasGalleryBase + +#------------------------------------------------------------------------- +# "Sources who have media object reference" +#------------------------------------------------------------------------- +class HasGallery(HasGalleryBase): + """Rule that checks for citation who has media object reference""" + + name = _('Citations with media') + description = _("Matches citations with a certain number of items in the gallery") diff --git a/src/Filters/Rules/Citation/_HasIdOf.py b/src/Filters/Rules/Citation/_HasIdOf.py new file mode 100644 index 000000000..10bdf5f02 --- /dev/null +++ b/src/Filters/Rules/Citation/_HasIdOf.py @@ -0,0 +1,47 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2002-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 +# 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$ + +#------------------------------------------------------------------------- +# +# Standard Python modules +# +#------------------------------------------------------------------------- +from gen.ggettext import gettext as _ + +#------------------------------------------------------------------------- +# +# GRAMPS modules +# +#------------------------------------------------------------------------- +from Filters.Rules import HasGrampsId + +#------------------------------------------------------------------------- +# +# HasIdOf +# +#------------------------------------------------------------------------- +class HasIdOf(HasGrampsId): + """Rule that checks for a citation with a specific GRAMPS ID""" + + name = _('Citation with ') + description = _("Matches a citation with a specified Gramps ID") diff --git a/src/Filters/Rules/Citation/_HasNote.py b/src/Filters/Rules/Citation/_HasNote.py new file mode 100755 index 000000000..46324e3b2 --- /dev/null +++ b/src/Filters/Rules/Citation/_HasNote.py @@ -0,0 +1,48 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2002-2007 Donald N. Allingham +# Copyright (C) 2007-2008 Brian G. Matherly +# Copyright (C) 2008 Jerome Rapinat +# Copyright (C) 2008 Benny Malengier +# 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$ + +#------------------------------------------------------------------------- +# +# Standard Python modules +# +#------------------------------------------------------------------------- +from gen.ggettext import gettext as _ + +#------------------------------------------------------------------------- +# +# GRAMPS modules +# +#------------------------------------------------------------------------- +from Filters.Rules._HasNoteBase import HasNoteBase + +#------------------------------------------------------------------------- +# "Sources having notes" +#------------------------------------------------------------------------- +class HasNote(HasNoteBase): + """Citations having notes""" + + name = _('Citations having notes') + description = _("Matches citations having a certain number of notes") diff --git a/src/Filters/Rules/Citation/_HasNoteMatchingSubstringOf.py b/src/Filters/Rules/Citation/_HasNoteMatchingSubstringOf.py new file mode 100644 index 000000000..870d5fdae --- /dev/null +++ b/src/Filters/Rules/Citation/_HasNoteMatchingSubstringOf.py @@ -0,0 +1,47 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2002-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 +# 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$ + +#------------------------------------------------------------------------- +# +# Standard Python modules +# +#------------------------------------------------------------------------- +from gen.ggettext import gettext as _ + +#------------------------------------------------------------------------- +# +# GRAMPS modules +# +#------------------------------------------------------------------------- +from Filters.Rules._HasNoteSubstrBase import HasNoteSubstrBase + +#------------------------------------------------------------------------- +# "Events having notes that contain a substring" +#------------------------------------------------------------------------- +class HasNoteMatchingSubstringOf(HasNoteSubstrBase): + """Citations having notes containing """ + + name = _('Citations having notes containing ') + description = _("Matches citations whose notes contain text " + "matching a substring") + diff --git a/src/Filters/Rules/Citation/_HasNoteRegexp.py b/src/Filters/Rules/Citation/_HasNoteRegexp.py new file mode 100644 index 000000000..727bca318 --- /dev/null +++ b/src/Filters/Rules/Citation/_HasNoteRegexp.py @@ -0,0 +1,45 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2002-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 +# 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$ + +#------------------------------------------------------------------------- +# +# Standard Python modules +# +#------------------------------------------------------------------------- +from gen.ggettext import gettext as _ + +#------------------------------------------------------------------------- +# +# GRAMPS modules +# +#------------------------------------------------------------------------- +from Filters.Rules._HasNoteRegexBase import HasNoteRegexBase + +#------------------------------------------------------------------------- +# "Sources having notes that contain a substring" +#------------------------------------------------------------------------- +class HasNoteRegexp(HasNoteRegexBase): + + name = _('Citations having notes containing ') + description = _("Matches citations whose notes contain text " + "matching a regular expression") diff --git a/src/Filters/Rules/Citation/_HasReferenceCountOf.py b/src/Filters/Rules/Citation/_HasReferenceCountOf.py new file mode 100644 index 000000000..88681cc6a --- /dev/null +++ b/src/Filters/Rules/Citation/_HasReferenceCountOf.py @@ -0,0 +1,46 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2007 Stephane Charette +# 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$ + +#------------------------------------------------------------------------- +# +# Standard Python modules +# +#------------------------------------------------------------------------- +from gen.ggettext import gettext as _ + +#------------------------------------------------------------------------- +# +# GRAMPS modules +# +#------------------------------------------------------------------------- +from Filters.Rules._HasReferenceCountBase import HasReferenceCountBase + +#------------------------------------------------------------------------- +# "Source objects with a certain reference count" +#------------------------------------------------------------------------- +class HasReferenceCountOf(HasReferenceCountBase): + """Citation objects with a reference count of """ + + name = _('Citations with a reference count of ') + description = _("Matches citations with a certain reference count") + diff --git a/src/Filters/Rules/Source/_HasSource.py b/src/Filters/Rules/Citation/_HasSource.py similarity index 69% rename from src/Filters/Rules/Source/_HasSource.py rename to src/Filters/Rules/Citation/_HasSource.py index ea2e3c01c..ca6e68347 100644 --- a/src/Filters/Rules/Source/_HasSource.py +++ b/src/Filters/Rules/Citation/_HasSource.py @@ -2,6 +2,7 @@ # Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 2002-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 @@ -20,6 +21,9 @@ # $Id$ +""" +Filter rule to match citation with a particular source. +""" #------------------------------------------------------------------------- # # Standard Python modules @@ -32,32 +36,27 @@ from gen.ggettext import gettext as _ # GRAMPS modules # #------------------------------------------------------------------------- -from Filters.Rules._Rule import Rule +from Filters.Rules._HasSourceBase import HasSourceBase #------------------------------------------------------------------------- # -# HasSource +# HasEvent # #------------------------------------------------------------------------- -class HasSource(Rule): - """Rule that checks for a person with a particular value""" - +class HasSource(HasSourceBase): + """Rule that checks for an citation with a particular value""" labels = [ _('Title:'), _('Author:'), _('Publication:') ] name = _('Sources matching parameters') - description = _("Matches sources with particular parameters") - category = _('General filters') - - def apply(self,db,source): - if not self.match_substring(0,source.get_title()): - return False - - if not self.match_substring(1,source.get_author()): - return False - - if not self.match_substring(2,source.get_publication_info()): - return False - - return True + description = _("Matches citations with a source of a particular " + "value") + category = _('Source filters') + + def apply(self, dbase, citation): + source = dbase.get_source_from_handle( + citation.get_reference_handle()) + if HasSourceBase.apply(self, dbase, source): + return True + return False diff --git a/src/Filters/Rules/Citation/_MatchesFilter.py b/src/Filters/Rules/Citation/_MatchesFilter.py new file mode 100644 index 000000000..50bf757a1 --- /dev/null +++ b/src/Filters/Rules/Citation/_MatchesFilter.py @@ -0,0 +1,48 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2002-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 +# 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$ + +#------------------------------------------------------------------------- +# +# Standard Python modules +# +#------------------------------------------------------------------------- +from gen.ggettext import gettext as _ + +#------------------------------------------------------------------------- +# +# GRAMPS modules +# +#------------------------------------------------------------------------- +from Filters.Rules._MatchesFilterBase import MatchesFilterBase + +#------------------------------------------------------------------------- +# +# MatchesFilter +# +#------------------------------------------------------------------------- +class MatchesFilter(MatchesFilterBase): + """Rule that checks against another filter""" + + name = _('Citations matching the ') + description = _("Matches citations matched by the specified filter name") + namespace = 'Citation' diff --git a/src/Filters/Rules/Citation/_MatchesPageSubstringOf.py b/src/Filters/Rules/Citation/_MatchesPageSubstringOf.py new file mode 100644 index 000000000..d58c2eec8 --- /dev/null +++ b/src/Filters/Rules/Citation/_MatchesPageSubstringOf.py @@ -0,0 +1,54 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2011 Helge Herz +# +# 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$ + +#------------------------------------------------------------------------- +# +# Standard Python modules +# +#------------------------------------------------------------------------- +from gen.ggettext import gettext as _ + +#------------------------------------------------------------------------- +# +# GRAMPS modules +# +#------------------------------------------------------------------------- +from Filters.Rules import Rule + +#------------------------------------------------------------------------- +# "Sources having a title that contain a substring" +#------------------------------------------------------------------------- +class MatchesPageSubstringOf(Rule): + """Citation Volume/Page title containing """ + + labels = [ _('Substring:')] + name = _('Citation Volume/Page containing ') + description = _("Matches citations whose Volume/Page contains a " + "certain substring") + category = _('General filters') + + def apply(self, db, object): + """ Apply the filter """ + title = object.get_page() + if title.upper().find(self.list[0].upper()) != -1: + return True + return False diff --git a/src/Filters/Rules/Citation/_MatchesRepositoryFilter.py b/src/Filters/Rules/Citation/_MatchesRepositoryFilter.py new file mode 100644 index 000000000..17d315c47 --- /dev/null +++ b/src/Filters/Rules/Citation/_MatchesRepositoryFilter.py @@ -0,0 +1,72 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2011 Benny Malengier +# 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$ + +#------------------------------------------------------------------------- +# +# Standard Python modules +# +#------------------------------------------------------------------------- +import logging +LOG = logging.getLogger(".citation") +from gen.ggettext import gettext as _ + +#------------------------------------------------------------------------- +# +# GRAMPS modules +# +#------------------------------------------------------------------------- +from Filters.Rules import MatchesFilterBase + +#------------------------------------------------------------------------- +# "Sources which reference a repository by selection" +#------------------------------------------------------------------------- +class MatchesRepositoryFilter(MatchesFilterBase): + """Citations which have a source which references the selected repository""" + + labels = [ _('Repository filter name:') ] + name = _('Citations with a source with a repository reference ' + 'matching the ') + description = _("Matches citations with sources with a repository " + "reference that match a certain repository filter") + category = _('General filters') + + # we want to have this filter show repository filters + namespace = 'Repository' + + + def prepare(self, db): + MatchesFilterBase.prepare(self, db) + self.MRF_filt = self.find_filter() + + def apply(self, db, object): + if self.MRF_filt is None : + return False + + source_handle = object.source_handle + source = db.get_source_from_handle(source_handle) + repolist = [x.ref for x in source.get_reporef_list()] + for repohandle in repolist: + #check if repo in repository filter + if self.MRF_filt.check(db, repohandle): + return True + return False diff --git a/src/Filters/Rules/Citation/_MatchesSourceFilter.py b/src/Filters/Rules/Citation/_MatchesSourceFilter.py new file mode 100644 index 000000000..3f6e99fa1 --- /dev/null +++ b/src/Filters/Rules/Citation/_MatchesSourceFilter.py @@ -0,0 +1,69 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2002-2006 Donald N. Allingham +# Copyright (C) 2010 Benny Malengier +# 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$ + +#------------------------------------------------------------------------- +# +# Standard Python modules +# +#------------------------------------------------------------------------- +from gen.ggettext import gettext as _ + +#------------------------------------------------------------------------- +# +# GRAMPS modules +# +#------------------------------------------------------------------------- +from Filters.Rules import MatchesFilterBase + +#------------------------------------------------------------------------- +# +# MatchesFilter +# +#------------------------------------------------------------------------- +class MatchesSourceFilter(MatchesFilterBase): + """ + Rule that checks against another filter. + """ + + labels = [_('Source filter name:')] + name = _('Citations with source matching the ') + description = _("Matches citations with sources that match the " + "specified source filter name") + category = _('General filters') + + # we want to have this filter show source filters + namespace = 'Source' + + def prepare(self, db): + MatchesFilterBase.prepare(self, db) + self.MRF_filt = self.find_filter() + + def apply(self, db, object): + if self.MRF_filt is None : + return False + + source_handle = object.source_handle + if self.MRF_filt.check(db, source_handle): + return True + return False diff --git a/src/Filters/Rules/Citation/_RegExpIdOf.py b/src/Filters/Rules/Citation/_RegExpIdOf.py new file mode 100644 index 000000000..d2fd256b0 --- /dev/null +++ b/src/Filters/Rules/Citation/_RegExpIdOf.py @@ -0,0 +1,51 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2002-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 +# 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$ + +#------------------------------------------------------------------------- +# +# Standard Python modules +# +#------------------------------------------------------------------------- +from gen.ggettext import gettext as _ + +#------------------------------------------------------------------------- +# +# GRAMPS modules +# +#------------------------------------------------------------------------- +from Filters.Rules._RegExpIdBase import RegExpIdBase + +#------------------------------------------------------------------------- +# +# HasIdOf +# +#------------------------------------------------------------------------- +class RegExpIdOf(RegExpIdBase): + """ + Rule that checks for a citation whose GRAMPS ID + matches regular expression. + """ + + name = _('Citations with matching regular expression') + description = _("Matches citations whose Gramps ID matches " + "the regular expression") diff --git a/src/Filters/Rules/Citation/__init__.py b/src/Filters/Rules/Citation/__init__.py new file mode 100644 index 000000000..af897260d --- /dev/null +++ b/src/Filters/Rules/Citation/__init__.py @@ -0,0 +1,63 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2002-2007 Donald N. Allingham +# Copyright (C) 2007-2008 Brian G. Matherly +# 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$ + +""" +Package providing filter rules for GRAMPS. +""" + +from Filters.Rules._HasCitationBase import HasCitationBase as HasCitation + +from _AllCitations import AllCitations +from _ChangedSince import ChangedSince +from _CitationPrivate import CitationPrivate +from _HasGallery import HasGallery +from _HasIdOf import HasIdOf +from _HasNote import HasNote +from _HasNoteMatchingSubstringOf import HasNoteMatchingSubstringOf +from _HasNoteRegexp import HasNoteRegexp +from _HasReferenceCountOf import HasReferenceCountOf +from _HasSource import HasSource +from _MatchesFilter import MatchesFilter +from _MatchesPageSubstringOf import MatchesPageSubstringOf +from _MatchesRepositoryFilter import MatchesRepositoryFilter +from _MatchesSourceFilter import MatchesSourceFilter +from _RegExpIdOf import RegExpIdOf + +editor_rule_list = [ + AllCitations, + ChangedSince, + CitationPrivate, + HasGallery, + HasIdOf, + HasNote, + HasNoteMatchingSubstringOf, + HasNoteRegexp, + HasReferenceCountOf, + HasSource, + MatchesFilter, + MatchesPageSubstringOf, + MatchesRepositoryFilter, + MatchesSourceFilter, + RegExpIdOf +] diff --git a/src/Filters/Rules/Event/Makefile.am b/src/Filters/Rules/Event/Makefile.am index 5d6f498bf..2223bc098 100644 --- a/src/Filters/Rules/Event/Makefile.am +++ b/src/Filters/Rules/Event/Makefile.am @@ -9,11 +9,12 @@ pkgdata_PYTHON = \ _HasNoteRegexp.py\ _RegExpIdOf.py\ _AllEvents.py\ + _HasCitation.py \ _HasData.py\ _HasGallery.py \ _HasIdOf.py\ _HasNote.py \ - _HasSource.py \ + _HasSourceCount.py \ _HasType.py\ _HasNoteMatchingSubstringOf.py\ _HasReferenceCountOf.py\ diff --git a/src/Filters/Rules/Event/_HasCitation.py b/src/Filters/Rules/Event/_HasCitation.py new file mode 100644 index 000000000..9381463d1 --- /dev/null +++ b/src/Filters/Rules/Event/_HasCitation.py @@ -0,0 +1,61 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2002-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 +# 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$ + +""" +Filter rule to match event with a particular citation. +""" +#------------------------------------------------------------------------- +# +# Standard Python modules +# +#------------------------------------------------------------------------- +from gen.ggettext import gettext as _ + +#------------------------------------------------------------------------- +# +# GRAMPS modules +# +#------------------------------------------------------------------------- +from Filters.Rules._HasCitationBase import HasCitationBase + +#------------------------------------------------------------------------- +# +# HasEvent +# +#------------------------------------------------------------------------- +class HasCitation(HasCitationBase): + """Rule that checks for an event with a particular value""" + + labels = [ _('Volume/Page:'), + _('Date:'), + _('Confidence level:')] + name = _('Event with the ') + description = _("Matches events with a citation of a particular " + "value") + + def apply(self, dbase, event): + for citation_handle in event.get_citation_list(): + citation = dbase.get_citation_from_handle(citation_handle) + if HasCitationBase.apply(self, dbase, citation): + return True + return False diff --git a/src/Filters/Rules/Event/_HasSource.py b/src/Filters/Rules/Event/_HasSourceCount.py similarity index 92% rename from src/Filters/Rules/Event/_HasSource.py rename to src/Filters/Rules/Event/_HasSourceCount.py index 7b829259c..cf1be9f34 100644 --- a/src/Filters/Rules/Event/_HasSource.py +++ b/src/Filters/Rules/Event/_HasSourceCount.py @@ -19,9 +19,8 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # -# Filters/Rules/Event/_HasSource.py + # $Id$ -# #------------------------------------------------------------------------- # @@ -35,12 +34,12 @@ from gen.ggettext import gettext as _ # GRAMPS modules # #------------------------------------------------------------------------- -from Filters.Rules._HasSourceBase import HasSourceBase +from Filters.Rules._HasSourceCountBase import HasSourceCountBase #------------------------------------------------------------------------- # "People having sources" #------------------------------------------------------------------------- -class HasSource(HasSourceBase): +class HasSourceCount(HasSourceCountBase): """Events with sources""" name = _('Events with sources') diff --git a/src/Filters/Rules/Event/_MatchesSourceFilter.py b/src/Filters/Rules/Event/_MatchesSourceFilter.py index b865e7eac..ba5eab4fc 100644 --- a/src/Filters/Rules/Event/_MatchesSourceFilter.py +++ b/src/Filters/Rules/Event/_MatchesSourceFilter.py @@ -3,6 +3,7 @@ # # Copyright (C) 2002-2006 Donald N. Allingham # Copyright (C) 2010 Benny Malengier +# 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 @@ class MatchesSourceFilter(MatchesSourceFilterBase): name = _('Events with source matching the ') description = _("Matches events with sources that match the " "specified source filter name") - category = _('General filters') + category = _('Citation/source filters') # we want to have this filter show source filters namespace = 'Source' diff --git a/src/Filters/Rules/Event/__init__.py b/src/Filters/Rules/Event/__init__.py index affc9cdaa..0a81c8e9a 100644 --- a/src/Filters/Rules/Event/__init__.py +++ b/src/Filters/Rules/Event/__init__.py @@ -3,6 +3,7 @@ # # Copyright (C) 2002-2006 Donald N. Allingham # Copyright (C) 2007-2008 Brian G. Matherly +# 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 @@ -32,11 +33,12 @@ from _AllEvents import AllEvents from _HasGallery import HasGallery from _HasIdOf import HasIdOf from _RegExpIdOf import RegExpIdOf +from _HasCitation import HasCitation from _HasNote import HasNote from _HasNoteRegexp import HasNoteRegexp from _HasNoteMatchingSubstringOf import HasNoteMatchingSubstringOf from _HasReferenceCountOf import HasReferenceCountOf -from _HasSource import HasSource +from _HasSourceCount import HasSourceCount from _EventPrivate import EventPrivate from _MatchesFilter import MatchesFilter from _MatchesPersonFilter import MatchesPersonFilter @@ -52,11 +54,12 @@ editor_rule_list = [ HasIdOf, HasGallery, RegExpIdOf, + HasCitation, HasNote, HasNoteRegexp, HasNoteMatchingSubstringOf, HasReferenceCountOf, - HasSource, + HasSourceCount, EventPrivate, MatchesFilter, MatchesPersonFilter, diff --git a/src/Filters/Rules/Family/Makefile.am b/src/Filters/Rules/Family/Makefile.am index eb8a8d5fb..1856247d6 100644 --- a/src/Filters/Rules/Family/Makefile.am +++ b/src/Filters/Rules/Family/Makefile.am @@ -8,6 +8,7 @@ pkgdata_PYTHON = \ _FamilyPrivate.py\ _HasEvent.py\ _HasAttribute.py\ + _HasCitation.py \ _HasGallery.py \ _HasIdOf.py\ _HasLDS.py \ @@ -16,7 +17,7 @@ pkgdata_PYTHON = \ _HasNoteRegexp.py\ _HasReferenceCountOf.py\ _HasRelType.py\ - _HasSource.py \ + _HasSourceCount.py \ _HasTag.py \ __init__.py\ _IsBookmarked.py\ diff --git a/src/Filters/Rules/Family/_HasCitation.py b/src/Filters/Rules/Family/_HasCitation.py new file mode 100644 index 000000000..79629c34c --- /dev/null +++ b/src/Filters/Rules/Family/_HasCitation.py @@ -0,0 +1,61 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2002-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 +# 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$ + +""" +Filter rule to match family with a particular citation. +""" +#------------------------------------------------------------------------- +# +# Standard Python modules +# +#------------------------------------------------------------------------- +from gen.ggettext import gettext as _ + +#------------------------------------------------------------------------- +# +# GRAMPS modules +# +#------------------------------------------------------------------------- +from Filters.Rules._HasCitationBase import HasCitationBase + +#------------------------------------------------------------------------- +# +# HasEvent +# +#------------------------------------------------------------------------- +class HasCitation(HasCitationBase): + """Rule that checks for a family with a particular value""" + + labels = [ _('Volume/Page:'), + _('Date:'), + _('Confidence level:')] + name = _('Family with the ') + description = _("Matches families with a citation of a particular " + "value") + + def apply(self, dbase, family): + for citation_handle in family.get_citation_list(): + citation = dbase.get_citation_from_handle(citation_handle) + if HasCitationBase.apply(self, dbase, citation): + return True + return False diff --git a/src/Filters/Rules/Family/_HasSource.py b/src/Filters/Rules/Family/_HasSourceCount.py similarity index 93% rename from src/Filters/Rules/Family/_HasSource.py rename to src/Filters/Rules/Family/_HasSourceCount.py index e0f5bc97b..cc2873242 100755 --- a/src/Filters/Rules/Family/_HasSource.py +++ b/src/Filters/Rules/Family/_HasSourceCount.py @@ -35,12 +35,12 @@ from gen.ggettext import gettext as _ # GRAMPS modules # #------------------------------------------------------------------------- -from Filters.Rules._HasSourceBase import HasSourceBase +from Filters.Rules._HasSourceCountBase import HasSourceCountBase #------------------------------------------------------------------------- # "Families having sources" #------------------------------------------------------------------------- -class HasSource(HasSourceBase): +class HasSourceCount(HasSourceCountBase): """Families with sources""" name = _('Families with sources') diff --git a/src/Filters/Rules/Family/__init__.py b/src/Filters/Rules/Family/__init__.py index 3eb635713..db9f0a1b3 100644 --- a/src/Filters/Rules/Family/__init__.py +++ b/src/Filters/Rules/Family/__init__.py @@ -3,6 +3,7 @@ # # Copyright (C) 2002-2007 Donald N. Allingham # Copyright (C) 2007-2008 Brian G. Matherly +# 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 @@ -41,8 +42,9 @@ from _RegExpIdOf import RegExpIdOf from _HasNote import HasNote from _HasNoteRegexp import HasNoteRegexp from _HasNoteMatchingSubstringOf import HasNoteMatchingSubstringOf -from _HasSource import HasSource +from _HasSourceCount import HasSourceCount from _HasReferenceCountOf import HasReferenceCountOf +from _HasCitation import HasCitation from _FamilyPrivate import FamilyPrivate from _HasAttribute import HasAttribute from _HasEvent import HasEvent @@ -69,7 +71,8 @@ editor_rule_list = [ HasNoteRegexp, HasNoteMatchingSubstringOf, HasReferenceCountOf, - HasSource, + HasSourceCount, + HasCitation, FamilyPrivate, HasEvent, HasAttribute, diff --git a/src/Filters/Rules/Makefile.am b/src/Filters/Rules/Makefile.am index 552ff18fa..7176e895c 100644 --- a/src/Filters/Rules/Makefile.am +++ b/src/Filters/Rules/Makefile.am @@ -1,6 +1,6 @@ # This is the src/Filters/Rules level Makefile for Gramps -SUBDIRS = Person Family Event Place Source MediaObject Repository Note +SUBDIRS = Person Family Event Place Source MediaObject Repository Note Citation pkgdatadir = $(datadir)/@PACKAGE@/Filters/Rules @@ -16,6 +16,7 @@ pkgdata_PYTHON = \ _HasNoteRegexBase.py\ _HasNoteSubstrBase.py\ _HasReferenceCountBase.py \ + _HasSourceCountBase.py \ _HasSourceBase.py \ _HasTagBase.py \ _HasTextMatchingRegexpOf.py\ @@ -25,9 +26,10 @@ pkgdata_PYTHON = \ _RegExpIdBase.py\ _Rule.py\ _MatchesFilterBase.py\ - _MatchesEventFilterBase.py \ - _MatchesSourceConfidenceBase.py \ - _MatchesSourceFilterBase.py + _MatchesEventFilterBase.py \ + _MatchesSourceConfidenceBase.py \ + _MatchesSourceFilterBase.py \ + _HasCitationBase.py pkgpyexecdir = @pkgpyexecdir@/Filters/Rules diff --git a/src/Filters/Rules/Person/Makefile.am b/src/Filters/Rules/Person/Makefile.am index aec4380df..5a3f15a71 100644 --- a/src/Filters/Rules/Person/Makefile.am +++ b/src/Filters/Rules/Person/Makefile.am @@ -13,6 +13,7 @@ pkgdata_PYTHON = \ _HasAlternateName.py \ _HasAssociation.py \ _HasBirth.py \ + _HasCitation.py \ _HasCommonAncestorWith.py \ _HasCommonAncestorWithFilterMatch.py \ _HasDeath.py \ @@ -29,7 +30,7 @@ pkgdata_PYTHON = \ _HasNote.py \ _HasNoteMatchingSubstringOf.py \ _HasRelationship.py \ - _HasSource.py \ + _HasSourceCount.py \ _HasSourceOf.py \ _HasTag.py \ _HasTextMatchingRegexpOf.py \ diff --git a/src/Filters/Rules/Person/_HasCitation.py b/src/Filters/Rules/Person/_HasCitation.py new file mode 100644 index 000000000..558fe4cce --- /dev/null +++ b/src/Filters/Rules/Person/_HasCitation.py @@ -0,0 +1,60 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2002-2006 Donald N. Allingham +# +# 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$ + +""" +Filter rule to match persons with a particular citation. +""" +#------------------------------------------------------------------------- +# +# Standard Python modules +# +#------------------------------------------------------------------------- +from gen.ggettext import gettext as _ + +#------------------------------------------------------------------------- +# +# GRAMPS modules +# +#------------------------------------------------------------------------- +from Filters.Rules._HasCitationBase import HasCitationBase + +#------------------------------------------------------------------------- +# +# HasEvent +# +#------------------------------------------------------------------------- +class HasCitation(HasCitationBase): + """Rule that checks for a person with a particular value""" + + labels = [ _('Volume/Page:'), + _('Date:'), + _('Confidence level:')] + name = _('People with the ') + description = _("Matches people with a citation of a particular " + "value") + + def apply(self, dbase, person): + for citation_handle in person.get_citation_list(): + citation = dbase.get_citation_from_handle(citation_handle) + if HasCitationBase.apply(self, dbase, citation): + return True + return False diff --git a/src/Filters/Rules/Person/_HasSource.py b/src/Filters/Rules/Person/_HasSourceCount.py similarity index 93% rename from src/Filters/Rules/Person/_HasSource.py rename to src/Filters/Rules/Person/_HasSourceCount.py index eaaa0601a..8abdc0f19 100755 --- a/src/Filters/Rules/Person/_HasSource.py +++ b/src/Filters/Rules/Person/_HasSourceCount.py @@ -35,12 +35,12 @@ from gen.ggettext import gettext as _ # GRAMPS modules # #------------------------------------------------------------------------- -from Filters.Rules._HasSourceBase import HasSourceBase +from Filters.Rules._HasSourceCountBase import HasSourceCountBase #------------------------------------------------------------------------- # "People having sources" #------------------------------------------------------------------------- -class HasSource(HasSourceBase): +class HasSourceCount(HasSourceCountBase): """People with sources""" name = _('People with sources') diff --git a/src/Filters/Rules/Person/_HasSourceOf.py b/src/Filters/Rules/Person/_HasSourceOf.py index becb49437..2330f7570 100644 --- a/src/Filters/Rules/Person/_HasSourceOf.py +++ b/src/Filters/Rules/Person/_HasSourceOf.py @@ -2,6 +2,7 @@ # Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 2002-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 @@ -44,7 +45,7 @@ class HasSourceOf(Rule): labels = [ _('Source ID:') ] name = _('People with the ') - category = _('General filters') + category = _('Citation/source filters') description = _('Matches people who have a particular source') def prepare(self,db): @@ -55,14 +56,22 @@ class HasSourceOf(Rule): self.nosource = False try: - self.source_handle = db.get_source_from_gramps_id(self.list[0]).get_handle() + self.source_handle = db.get_source_from_gramps_id( + self.list[0]).get_handle() except: self.source_handle = None def apply(self, db, person): if not self.source_handle: if self.nosource: - return len(person.get_source_references()) == 0 + # check whether the citation list is empty as a proxy for + # there being no sources + return len(person.get_all_citation_lists()) == 0 else: return False - return person.has_source_reference(self.source_handle) + else: + for citation_handle in person.get_all_citation_lists(): + citation = db.get_citation_from_handle(citation_handle) + if citation.get_reference_handle() == self.source_handle: + return True + return False diff --git a/src/Filters/Rules/Person/_HasTextMatchingSubstringOf.py b/src/Filters/Rules/Person/_HasTextMatchingSubstringOf.py index bb10e0fa9..ef0a4504d 100644 --- a/src/Filters/Rules/Person/_HasTextMatchingSubstringOf.py +++ b/src/Filters/Rules/Person/_HasTextMatchingSubstringOf.py @@ -2,6 +2,7 @@ # Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 2002-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 @@ -26,13 +27,15 @@ # #------------------------------------------------------------------------- from gen.ggettext import gettext as _ +import logging +LOG = logging.getLogger(".citationfilter") #------------------------------------------------------------------------- # # GRAMPS modules # #------------------------------------------------------------------------- -from Utils import get_source_referents +from Utils import get_source_and_citation_referents from Filters.Rules._Rule import Rule #------------------------------------------------------------------------- @@ -179,15 +182,30 @@ class HasTextMatchingSubstringOf(Rule): # search all sources and match all referents of a matching source for source in self.db.iter_sources(): match = self.match_object(source) + LOG.debug("cache_sources match %s string %s source %s" % + (match, self.list[0], source.gramps_id)) if not match: if any(reporef.get_reference_handle() in self.repo_map for reporef in source.get_reporef_list() ): - + match = True + LOG.debug("cache_sources repomatch %s string %s source %s" % + (match, self.list[0], source.gramps_id)) + (citation_list, citation_referents_list) = \ + get_source_and_citation_referents(source.handle, self.db) + LOG.debug("the_lists %s %s" % + (citation_list, citation_referents_list)) + for (citation_handle, refs) in citation_referents_list: + citation = self.db.get_citation_from_handle(citation_handle) + LOG.debug("cache_sources match %s matchcitation %s string %s" + " source %s citation %s" % + (match, self.match_object(citation), + self.list[0], source.gramps_id, + citation.gramps_id)) + if match or self.match_object(citation): # Update the maps to reflect the reference - (person_list, family_list, event_list, place_list, - source_list, media_list, repo_list - ) = get_source_referents(source.handle,self.db) + (person_list, family_list, event_list, place_list, + source_list, media_list, repo_list) = refs self.person_map.update(person_list) self.family_map.update(family_list) self.event_map.update(event_list) diff --git a/src/Filters/Rules/Person/_IsRelatedWith.py b/src/Filters/Rules/Person/_IsRelatedWith.py index f870ca750..964d541c4 100644 --- a/src/Filters/Rules/Person/_IsRelatedWith.py +++ b/src/Filters/Rules/Person/_IsRelatedWith.py @@ -18,7 +18,7 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # -# $Id$ +# $Id: _IsRelatedWith.py 18338 2011-10-16 20:21:22Z paul-franklin $ #------------------------------------------------------------------------- # diff --git a/src/Filters/Rules/Person/__init__.py b/src/Filters/Rules/Person/__init__.py index da759bd18..bb2c734d5 100644 --- a/src/Filters/Rules/Person/__init__.py +++ b/src/Filters/Rules/Person/__init__.py @@ -3,6 +3,7 @@ # # Copyright (C) 2002-2007 Donald N. Allingham # Copyright (C) 2007-2008 Brian G. Matherly +# 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 @@ -33,6 +34,7 @@ from _HasAlternateName import HasAlternateName from _HasAssociation import HasAssociation from _HasAttribute import HasAttribute from _HasBirth import HasBirth +from _HasCitation import HasCitation from _HasCommonAncestorWith import HasCommonAncestorWith from _HasCommonAncestorWithFilterMatch import HasCommonAncestorWithFilterMatch from _HasDeath import HasDeath @@ -50,7 +52,7 @@ from _HasNote import HasNote from _HasNoteMatchingSubstringOf import HasNoteMatchingSubstringOf from _HasNoteRegexp import HasNoteRegexp from _HasRelationship import HasRelationship -from _HasSource import HasSource +from _HasSourceCount import HasSourceCount from _HasSourceOf import HasSourceOf from _HasTag import HasTag from _HasTextMatchingRegexpOf import HasTextMatchingRegexpOf @@ -132,12 +134,13 @@ editor_rule_list = [ HasRelationship, HasDeath, HasBirth, + HasCitation, HasEvent, HasFamilyEvent, HasAttribute, HasFamilyAttribute, HasTag, - HasSource, + HasSourceCount, HasSourceOf, HaveAltFamilies, HavePhotos, diff --git a/src/Filters/Rules/Source/Makefile.am b/src/Filters/Rules/Source/Makefile.am index 5c36fcdfb..707eaeddd 100644 --- a/src/Filters/Rules/Source/Makefile.am +++ b/src/Filters/Rules/Source/Makefile.am @@ -12,7 +12,6 @@ pkgdata_PYTHON = \ _HasReferenceCountOf.py\ _AllSources.py\ _HasIdOf.py\ - _HasSource.py\ _HasNote.py \ _HasNoteRegexp.py\ _HasRepository.py\ diff --git a/src/Filters/Rules/Source/__init__.py b/src/Filters/Rules/Source/__init__.py index bac4c398b..9eef81791 100644 --- a/src/Filters/Rules/Source/__init__.py +++ b/src/Filters/Rules/Source/__init__.py @@ -3,6 +3,7 @@ # # Copyright (C) 2002-2007 Donald N. Allingham # Copyright (C) 2007-2008 Brian G. Matherly +# 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 @@ -25,6 +26,8 @@ Package providing filter rules for GRAMPS. """ +from Filters.Rules._HasSourceBase import HasSourceBase as HasSource + from _AllSources import AllSources from _HasGallery import HasGallery from _HasIdOf import HasIdOf @@ -35,7 +38,6 @@ from _HasNoteMatchingSubstringOf import HasNoteMatchingSubstringOf from _HasReferenceCountOf import HasReferenceCountOf from _SourcePrivate import SourcePrivate from _MatchesFilter import MatchesFilter -from _HasSource import HasSource from _ChangedSince import ChangedSince from _HasRepository import HasRepository from _MatchesTitleSubstringOf import MatchesTitleSubstringOf diff --git a/src/Filters/Rules/_HasCitationBase.py b/src/Filters/Rules/_HasCitationBase.py new file mode 100644 index 000000000..779984541 --- /dev/null +++ b/src/Filters/Rules/_HasCitationBase.py @@ -0,0 +1,78 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2002-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 +# 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$ + +#------------------------------------------------------------------------- +# +# Standard Python modules +# +#------------------------------------------------------------------------- +from gen.ggettext import gettext as _ + +#------------------------------------------------------------------------- +# +# GRAMPS modules +# +#------------------------------------------------------------------------- +import DateHandler +from Filters.Rules._Rule import Rule + +#------------------------------------------------------------------------- +# +# HasCitation +# +#------------------------------------------------------------------------- +class HasCitationBase(Rule): + """Rule that checks for a citation with a particular value + + First parameter is [Volume/page, Date, Confidence] + """ + + + labels = [ _('Volume/Page:'), + _('Date:'), + _('Confidence:') ] + name = _('Citations matching parameters') + description = _("Matches citations with particular parameters") + category = _('Citation/source filters') + + def prepare(self, db): + self.date = None + try: + if self.list[1]: + self.date = DateHandler.parser.parse(self.list[1]) + except: + pass + + def apply(self,db,citation): + if not self.match_substring(0,citation.get_page()): + return False + + if self.date: + if not citation.get_date_object().match(self.date): + return False + + if self.list[2]: + if citation.get_confidence_level() < int(self.list[2]): + return False + + return True diff --git a/src/Filters/Rules/_HasSourceBase.py b/src/Filters/Rules/_HasSourceBase.py old mode 100755 new mode 100644 index 47b44abe2..e0f44fac9 --- a/src/Filters/Rules/_HasSourceBase.py +++ b/src/Filters/Rules/_HasSourceBase.py @@ -1,10 +1,8 @@ # # Gramps - a GTK+/GNOME based genealogy program # -# Copyright (C) 2002-2007 Donald N. Allingham -# Copyright (C) 2007-2008 Brian G. Matherly -# Copyright (C) 2008 Jerome Rapinat -# Copyright (C) 2008 Benny Malengier +# Copyright (C) 2002-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 @@ -20,9 +18,8 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # -# Filters/Rules/_HasSourceBase.py + # $Id$ -# #------------------------------------------------------------------------- # @@ -39,32 +36,29 @@ from gen.ggettext import gettext as _ from Filters.Rules._Rule import Rule #------------------------------------------------------------------------- -# "Objects having sources" +# +# HasSource +# #------------------------------------------------------------------------- class HasSourceBase(Rule): - """Objects having notes""" + """Rule that checks for a source with a particular value""" - labels = [ _('Number of instances:'), _('Number must be:')] - name = _('Objects with sources') - description = _("Matches objects that have a certain number of sources connected to it") - category = _('General filters') - def prepare(self, db): - # things we want to do just once, not for every handle - if self.list[1] == 'lesser than': - self.count_type = 0 - elif self.list[1] == 'greater than': - self.count_type = 2 - else: - self.count_type = 1 # "equal to" + labels = [ _('Title:'), + _('Author:'), + _('Publication:') ] + name = _('Sources matching parameters') + description = _("Matches sources with particular parameters") + category = _('Citation/source filters') - self.userSelectedCount = int(self.list[0]) + def apply(self,db,source): + if not self.match_substring(0,source.get_title()): + return False - def apply(self, db, obj): - count = len(obj.get_source_references()) - if self.count_type == 0: # "lesser than" - return count < self.userSelectedCount - elif self.count_type == 2: # "greater than" - return count > self.userSelectedCount - # "equal to" - return count == self.userSelectedCount + if not self.match_substring(1,source.get_author()): + return False + + if not self.match_substring(2,source.get_publication_info()): + return False + + return True diff --git a/src/Filters/Rules/_HasSourceCountBase.py b/src/Filters/Rules/_HasSourceCountBase.py new file mode 100755 index 000000000..2aecdb501 --- /dev/null +++ b/src/Filters/Rules/_HasSourceCountBase.py @@ -0,0 +1,70 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2002-2007 Donald N. Allingham +# Copyright (C) 2007-2008 Brian G. Matherly +# Copyright (C) 2008 Jerome Rapinat +# Copyright (C) 2008 Benny Malengier +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +# $Id$ + +#------------------------------------------------------------------------- +# +# Standard Python modules +# +#------------------------------------------------------------------------- +from gen.ggettext import gettext as _ + +#------------------------------------------------------------------------- +# +# GRAMPS modules +# +#------------------------------------------------------------------------- +from Filters.Rules._Rule import Rule + +#------------------------------------------------------------------------- +# "Objects having sources" +#------------------------------------------------------------------------- +class HasSourceCountBase(Rule): + """Objects having sources""" + + labels = [ _('Number of instances:'), _('Number must be:')] + name = _('Objects with sources') + description = _("Matches objects that have a certain number of sources " + "connected to it (actually citations are counted)") + category = _('Citation/source filters') + + def prepare(self, db): + # things we want to do just once, not for every handle + if self.list[1] == 'lesser than': + self.count_type = 0 + elif self.list[1] == 'greater than': + self.count_type = 2 + else: + self.count_type = 1 # "equal to" + + self.userSelectedCount = int(self.list[0]) + + def apply(self, db, obj): + count = len(obj.get_citation_list()) + if self.count_type == 0: # "lesser than" + return count < self.userSelectedCount + elif self.count_type == 2: # "greater than" + return count > self.userSelectedCount + # "equal to" + return count == self.userSelectedCount diff --git a/src/Filters/Rules/_MatchesSourceConfidenceBase.py b/src/Filters/Rules/_MatchesSourceConfidenceBase.py index fce35f7fc..1673d6773 100644 --- a/src/Filters/Rules/_MatchesSourceConfidenceBase.py +++ b/src/Filters/Rules/_MatchesSourceConfidenceBase.py @@ -4,6 +4,7 @@ # Copyright (C) 2011 Jerome Rapinat # Copyright (C) 2011 Douglas S. Blank # Copyright (C) 2011 Benny Malengier +# 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 @@ -47,11 +48,12 @@ class MatchesSourceConfidenceBase(Rule): labels = [_('Confidence level:')] name = _('Object with at least one direct source >= ') description = _("Matches objects with at least one direct source with confidence level(s)") - category = _('General filters') + category = _('Citation/source filters') def apply(self, db, obj): - for source in obj.get_source_references(): - required_conf = int(self.list[0]) - if required_conf <= source.get_confidence_level(): + required_conf = int(self.list[0]) + for citation_handle in obj.get_citation_list(): + citation = db.get_citation_from_handle(citation_handle) + if required_conf <= citation.get_confidence_level(): return True return False diff --git a/src/Filters/Rules/_MatchesSourceFilterBase.py b/src/Filters/Rules/_MatchesSourceFilterBase.py index 79b015d68..1f482b212 100644 --- a/src/Filters/Rules/_MatchesSourceFilterBase.py +++ b/src/Filters/Rules/_MatchesSourceFilterBase.py @@ -2,6 +2,7 @@ # Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 2010 Benny Malengier +# 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 @@ class MatchesSourceFilterBase(MatchesFilterBase): name = _('Objects with source matching the ') description = _("Matches objects with sources that match the " "specified source filter name") - category = _('General filters') + category = _('Citation/source filters') # we want to have this filter show source filters namespace = 'Source' @@ -61,9 +62,9 @@ class MatchesSourceFilterBase(MatchesFilterBase): if self.MSF_filt is None : return False - sourcelist = [x.ref for x in object.get_source_references()] - for sourcehandle in sourcelist: - #check if source in source filter + for citation_handle in object.get_citation_list(): + citation = db.get_citation_from_handle(citation_handle) + sourcehandle = citation.get_reference_handle() if self.MSF_filt.check(db, sourcehandle): return True return False diff --git a/src/Filters/Rules/__init__.py b/src/Filters/Rules/__init__.py index f64e5f6d5..06d07c15d 100644 --- a/src/Filters/Rules/__init__.py +++ b/src/Filters/Rules/__init__.py @@ -2,6 +2,7 @@ # Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 2002-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 @@ -22,6 +23,46 @@ """ Package providing filter rules for GRAMPS. + +The following filters are provided in Filters/Rules. + +Match given values: +_HasCitationBase Citation with a particular value (HasCitation) + also used for Person, Family and Event having a + particular Citation +_HasEventBase Event with a particular value (HasEvent) + also used for Family and Person having a particular + Event +_HasSourceBase Source with a particular value (HasSource) + also used for Citation having a particular Source + +Match on sub-objects +_ChangedSinceBase Object changed since date +_HasAttributeBase Object has particular attribute value +_HasGrampsId Object has a specific Gramps Id +_HasNoteRegexBase Object has notes matching regular expression +_HasNoteSubstrBase Object has note containing substring +_HasTagBase Object has a particular tag +_HasTextMatchingRegexpOf Object has text matching regular expression +_HasTextMatchingSubstringOf Object has text containing substring +_IsPrivate Object is marked as private +_MatchesFilterBase Object matches another filter +_RegExpldBase Object has Gramps Id matching regular expression + +Match on related objects +_MatchesFilterEventBase Object has an event that matches another filter +_MatchesSourceConfidenceBase Object with specific confidence on direct sources +_MatchesSourceFilterBase Object matches another filter on direct sources + +Count based +_HasGalleryBase Object has /= number of media objects +_HasLDSBase Object has /= number of LDS sub-objects +_HasNoteBase Object has /= number of notes +_HasReferenceCountBase Object has /= number of references +_HasSourceCountBase Object has /= number of sources + +_Rule Base rule class +_Everything Match every object in the database """ # Need to expose this to be available for filter plugins: @@ -39,5 +80,5 @@ from Filters.Rules._MatchesSourceConfidenceBase import MatchesSourceConfidenceBa from Filters.Rules._MatchesSourceFilterBase import MatchesSourceFilterBase from Filters.Rules._ChangedSinceBase import ChangedSinceBase -from Filters.Rules import (Person, Family, Event, Source, Place, MediaObject, - Repository, Note) +from Filters.Rules import (Person, Family, Event, Source, Citation, Place, + MediaObject, Repository, Note) diff --git a/src/Filters/SideBar/Makefile.am b/src/Filters/SideBar/Makefile.am index 404497a04..23dc08615 100644 --- a/src/Filters/SideBar/Makefile.am +++ b/src/Filters/SideBar/Makefile.am @@ -9,6 +9,7 @@ pkgdata_PYTHON = \ _SidebarFilter.py \ _PersonSidebarFilter.py\ _SourceSidebarFilter.py\ + _CitationSidebarFilter.py\ _PlaceSidebarFilter.py\ _MediaSidebarFilter.py\ _RepoSidebarFilter.py\ diff --git a/src/Filters/SideBar/_CitationSidebarFilter.py b/src/Filters/SideBar/_CitationSidebarFilter.py new file mode 100644 index 000000000..f494d71ac --- /dev/null +++ b/src/Filters/SideBar/_CitationSidebarFilter.py @@ -0,0 +1,164 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2002-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 +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +# $Id$ + +#------------------------------------------------------------------------- +# +# Python modules +# +#------------------------------------------------------------------------- +from gen.ggettext import gettext as _ + +#------------------------------------------------------------------------- +# +# gtk +# +#------------------------------------------------------------------------- +import gtk + +#------------------------------------------------------------------------- +# +# GRAMPS modules +# +#------------------------------------------------------------------------- +from gui.widgets import MonitoredMenu +import gen.lib +from Filters.SideBar import SidebarFilter +from Filters import GenericFilterFactory, build_filter_model, Rules +from Filters.Rules.Citation import (RegExpIdOf, HasIdOf, HasCitation, + HasNoteMatchingSubstringOf, HasNoteRegexp, + MatchesFilter) +from Utils import confidence +GenericCitationFilter = GenericFilterFactory('Citation') +#------------------------------------------------------------------------- +# +# PersonSidebarFilter class +# +#------------------------------------------------------------------------- +class CitationSidebarFilter(SidebarFilter): + + def __init__(self, dbstate, uistate, clicked): + self.clicked_func = clicked + self.filter_id = gtk.Entry() + self.filter_page = gtk.Entry() + self.filter_date = gtk.Entry() + + self.filter_conf = gtk.ComboBox() + model = gtk.ListStore(str) + for conf_value in sorted(confidence.keys()): + model.append((confidence[conf_value],)) + self.filter_conf.set_model(model) + self.filter_conf.set_active(2) # gen.lib.Citation.CONF_NORMAL + + self.filter_note = gtk.Entry() + + self.filter_regex = gtk.CheckButton(_('Use regular expressions')) + + self.generic = gtk.ComboBox() + + SidebarFilter.__init__(self, dbstate, uistate, "Citation") + + def create_widget(self): + cell = gtk.CellRendererText() + cell.set_property('width', self._FILTER_WIDTH) + cell.set_property('ellipsize', self._FILTER_ELLIPSIZE) + self.generic.pack_start(cell, True) + self.generic.add_attribute(cell, 'text', 0) + self.on_filters_changed('Citation') + + cell = gtk.CellRendererText() + cell.set_property('width', self._FILTER_WIDTH) + cell.set_property('ellipsize', self._FILTER_ELLIPSIZE) + self.filter_conf.pack_start(cell, True) + self.filter_conf.add_attribute(cell, 'text', 0) + + self.add_text_entry(_('ID'), self.filter_id) + self.add_text_entry(_('Volume/Page'), self.filter_page) + self.add_text_entry(_('Date'), self.filter_date) + self.add_entry(_('Confidence'), self.filter_conf) + self.add_text_entry(_('Note'), self.filter_note) + self.add_filter_entry(_('Custom filter'), self.generic) + self.add_entry(None, self.filter_regex) + + def clear(self, obj): + self.filter_id.set_text('') + self.filter_page.set_text('') + self.filter_date.set_text('') + self.filter_conf.set_active(2) + self.filter_note.set_text('') + self.generic.set_active(0) + + def get_filter(self): + gid = unicode(self.filter_id.get_text()).strip() + page = unicode(self.filter_page.get_text()).strip() + date = unicode(self.filter_date.get_text()).strip() + model = self.filter_conf.get_model() + node = self.filter_conf.get_active_iter() + conf_name = model.get_value(node, 0) # The value is actually the text + conf = 2 + for i in confidence.keys(): + if confidence[i] == conf_name: + conf = i + break +# conf = self.citn.get_confidence_level() + note = unicode(self.filter_note.get_text()).strip() + regex = self.filter_regex.get_active() + gen = self.generic.get_active() > 0 + + empty = not (gid or page or date or conf or note or regex or gen) + if empty: + generic_filter = None + else: + generic_filter = GenericCitationFilter() + if gid: + if regex: + rule = RegExpIdOf([gid]) + else: + rule = HasIdOf([gid]) + generic_filter.add_rule(rule) + + rule = HasCitation([page, date, conf], use_regex=regex) + generic_filter.add_rule(rule) + + if note: + if regex: + rule = HasNoteRegexp([note]) + else: + rule = HasNoteMatchingSubstringOf([note]) + generic_filter.add_rule(rule) + + if self.generic.get_active() != 0: + model = self.generic.get_model() + node = self.generic.get_active_iter() + obj = unicode(model.get_value(node, 0)) + rule = MatchesFilter([obj]) + generic_filter.add_rule(rule) + + return generic_filter + + def on_filters_changed(self, name_space): + if name_space == 'Citation': + all_filter = GenericCitationFilter() + all_filter.set_name(_("None")) + all_filter.add_rule(Rules.Citation.AllCitations([])) + self.generic.set_model(build_filter_model('Citation', [all_filter])) + self.generic.set_active(0) diff --git a/src/Filters/SideBar/__init__.py b/src/Filters/SideBar/__init__.py index 99ffa24c0..86498f51e 100644 --- a/src/Filters/SideBar/__init__.py +++ b/src/Filters/SideBar/__init__.py @@ -2,6 +2,7 @@ # Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 2002-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 @@ -29,6 +30,7 @@ from _PersonSidebarFilter import PersonSidebarFilter from _FamilySidebarFilter import FamilySidebarFilter from _EventSidebarFilter import EventSidebarFilter from _SourceSidebarFilter import SourceSidebarFilter +from _CitationSidebarFilter import CitationSidebarFilter from _PlaceSidebarFilter import PlaceSidebarFilter from _MediaSidebarFilter import MediaSidebarFilter from _RepoSidebarFilter import RepoSidebarFilter diff --git a/src/Filters/_GenericFilter.py b/src/Filters/_GenericFilter.py index 6489edf9e..f4437ecf5 100644 --- a/src/Filters/_GenericFilter.py +++ b/src/Filters/_GenericFilter.py @@ -2,6 +2,7 @@ # Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 2002-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 @@ -276,6 +277,20 @@ class GenericSourceFilter(GenericFilter): def find_from_handle(self, db, handle): return db.get_source_from_handle(handle) +class GenericCitationFilter(GenericFilter): + + def __init__(self, source=None): + GenericFilter.__init__(self, source) + + def get_cursor(self, db): + return db.get_citation_cursor() + + def make_obj(self): + return gen.lib.Citation() + + def find_from_handle(self, db, handle): + return db.get_citation_from_handle(handle) + class GenericPlaceFilter(GenericFilter): def __init__(self, source=None): @@ -342,6 +357,8 @@ def GenericFilterFactory(namespace): return GenericEventFilter elif namespace == 'Source': return GenericSourceFilter + elif namespace == 'Citation': + return GenericCitationFilter elif namespace == 'Place': return GenericPlaceFilter elif namespace == 'MediaObject': 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 f092240b1..81fa17487 100644 --- a/src/Merge/__init__.py +++ b/src/Merge/__init__.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 @@ -28,6 +29,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..77db47411 --- /dev/null +++ b/src/Merge/mergecitation.py @@ -0,0 +1,231 @@ +# +# 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, MediaObject, Repository) +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 + +#------------------------------------------------------------------------- +# +# 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(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'): + 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) + elif class_name == Repository.__name__: + repository = self.database.get_repository_from_handle(handle) + assert(repository.has_citation_reference(old_handle)) + repository.replace_citation_references(old_handle, + new_handle) + self.database.commit_repository(repository, 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/mergesource.py b/src/Merge/mergesource.py index 987e51c88..f1fe88061 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 @@ -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 @@ -206,41 +207,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.get_reference_handle() == old_handle) + citation.set_reference_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/QuickReports.py b/src/QuickReports.py index 20ce14dea..4fe37d61a 100644 --- a/src/QuickReports.py +++ b/src/QuickReports.py @@ -3,6 +3,7 @@ # # Copyright (C) 2007 B. Malengier # Copyright (C) 2008 Brian G. Matherly +# 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 @@ -57,7 +58,8 @@ from gui.pluginmanager import GuiPluginManager from gen.plug import (CATEGORY_QR_PERSON, CATEGORY_QR_FAMILY, CATEGORY_QR_MEDIA, CATEGORY_QR_EVENT, CATEGORY_QR_SOURCE, CATEGORY_QR_MISC, CATEGORY_QR_PLACE, CATEGORY_QR_REPOSITORY, - CATEGORY_QR_NOTE) + CATEGORY_QR_NOTE, CATEGORY_QR_CITATION, + CATEGORY_QR_SOURCE_OR_CITATION) def flatten(L): """ @@ -119,7 +121,8 @@ def create_quickreport_menu(category,dbstate,uistate, handle) : It collects the reports of the requested category, which must be one of CATEGORY_QR_PERSON, CATEGORY_QR_FAMILY, CATEGORY_QR_EVENT, CATEGORY_QR_SOURCE, CATEGORY_QR_MEDIA, - CATEGORY_QR_PLACE, CATEGORY_QR_REPOSITORY + CATEGORY_QR_PLACE, CATEGORY_QR_REPOSITORY, + CATEGORY_QR_CITATION, CATEGORY_QR_SOURCE_OR_CITATION It constructs the ui string of the quick report menu, and it's actions The action callback function is constructed, using the dbstate and the handle as input method. @@ -166,7 +169,8 @@ def get_quick_report_list(qv_category=None): Returns a list of PluginData of quick views of category qv_category CATEGORY_QR_PERSON, CATEGORY_QR_FAMILY, CATEGORY_QR_EVENT, CATEGORY_QR_SOURCE, CATEGORY_QR_MISC, CATEGORY_QR_PLACE, - CATEGORY_QR_REPOSITORY, CATEGORY_QR_MEDIA or None for all + CATEGORY_QR_REPOSITORY, CATEGORY_QR_MEDIA, + CATEGORY_QR_CITATION, CATEGORY_QR_SOURCE_OR_CITATION or None for all """ names = [] pmgr = GuiPluginManager.get_instance() @@ -254,6 +258,17 @@ def run_report(dbstate, uistate, category, handle, pdata, container=None, obj = dbstate.db.get_event_from_handle(handle) elif category == CATEGORY_QR_SOURCE : obj = dbstate.db.get_source_from_handle(handle) + elif category == CATEGORY_QR_CITATION : + obj = dbstate.db.get_citation_from_handle(handle) + elif category == CATEGORY_QR_SOURCE_OR_CITATION : + source = dbstate.db.get_source_from_handle(handle) + citation = 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: + obj = citation + else: + obj = source elif category == CATEGORY_QR_PLACE : obj = dbstate.db.get_place_from_handle(handle) elif category == CATEGORY_QR_MEDIA : diff --git a/src/ScratchPad.py b/src/ScratchPad.py index 002dea49a..68cb95f7f 100644 --- a/src/ScratchPad.py +++ b/src/ScratchPad.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 modiy # it under the terms of the GNU General Public License as published by @@ -87,6 +88,7 @@ for (name, file) in ( ('name', 'geo-show-person.png'), ('repository', 'gramps-repository.png'), ('source', 'gramps-source.png'), + ('citation', 'gramps-citation.png'), ('text', 'gramps-font.png'), ('url', 'gramps-geo.png'), ): @@ -104,7 +106,7 @@ def map2class(target): 'personref': ScratchPersonRef, 'childref': ScratchChildRef, 'source-link': ScratchSourceLink, - 'srcref': ScratchSourceRef, + 'citation-link': ScratchCitation, 'repo-link': ScratchRepositoryLink, 'pevent': ScratchEvent, 'eventref': ScratchEventRef, @@ -119,6 +121,7 @@ def obj2class(target): d= {"Person": ScratchPersonLink, "Family": ScratchFamilyLink, 'Source': ScratchSourceLink, + 'Citation': ScratchCitation, 'Repository': ScratchRepositoryLink, 'Event': ScratchEvent, 'Media': ScratchMediaObj, @@ -131,6 +134,7 @@ def obj2target(target): d = {"Person": 'person-link', "Family": 'family-link', 'Source': 'source-link', + 'Citation': 'citation-link', 'Repository': 'repo-link', 'Event': 'pevent', 'Media': 'mediaobj', @@ -437,24 +441,25 @@ class ScratchFamilyAttribute(ScratchObjWrapper): self._title = str(self._obj.get_type()) self._value = self._obj.get_value() -class ScratchSourceRef(ScratchObjWrapper): +class ScratchCitation(ScratchHandleWrapper): - DROP_TARGETS = [DdTargets.SOURCEREF] - DRAG_TARGET = DdTargets.SOURCEREF - ICON = LINK_PIC + DROP_TARGETS = [DdTargets.CITATION_LINK] + DRAG_TARGET = DdTargets.CITATION_LINK + ICON = ICONS["citation"] def __init__(self, dbstate, obj): - super(ScratchSourceRef, self).__init__(dbstate, obj) - self._type = _("Source ref") - if self._obj: - base = self._db.get_source_from_handle(self._obj.get_reference_handle()) - if base: - self._title = base.get_title() + super(ScratchCitation, self).__init__(dbstate, obj) + self._type = _("Citation") + self._objclass = 'Citation' + if self._handle: + citation = self._db.get_citation_from_handle(self._handle) + if citation: + self._title = citation.get_gramps_id() notelist = map(self._db.get_note_from_handle, - self._obj.get_note_list()) + citation.get_note_list()) srctxtlist = [note for note in notelist - if note.get_type() == gen.lib.NoteType.SOURCE_TEXT] - page = self._obj.get_page() + if note.get_type() == gen.lib.NoteType.SOURCE_TEXT] + page = citation.get_page() if not page: page = _('not available|NA') text = "" @@ -470,6 +475,14 @@ class ScratchSourceRef(ScratchObjWrapper): 'sourcetext' : text, } + def is_valid(self): + data = pickle.loads(self._obj) + handle = data[2] + obj = self._db.get_citation_from_handle(handle) + if obj: + return True + return False + class ScratchRepoRef(ScratchObjWrapper): DROP_TARGETS = [DdTargets.REPOREF] @@ -968,7 +981,6 @@ class ScratchPadListView(object): self.register_wrapper_class(ScratchEvent) self.register_wrapper_class(ScratchPlace) self.register_wrapper_class(ScratchEventRef) - self.register_wrapper_class(ScratchSourceRef) self.register_wrapper_class(ScratchRepoRef) self.register_wrapper_class(ScratchFamilyEvent) self.register_wrapper_class(ScratchUrl) @@ -979,6 +991,7 @@ class ScratchPadListView(object): self.register_wrapper_class(ScratchMediaObj) self.register_wrapper_class(ScratchMediaRef) self.register_wrapper_class(ScratchSourceLink) + self.register_wrapper_class(ScratchCitation) self.register_wrapper_class(ScratchPersonLink) self.register_wrapper_class(ScratchFamilyLink) self.register_wrapper_class(ScratchDropList) diff --git a/src/Simple/_SimpleAccess.py b/src/Simple/_SimpleAccess.py index 07a866b60..2cee37b92 100644 --- a/src/Simple/_SimpleAccess.py +++ b/src/Simple/_SimpleAccess.py @@ -3,6 +3,7 @@ # # Copyright (C) 2007 Donald N. Allingham # Copyright (C) 2010 Jakim Friant +# 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 @@ -849,6 +850,20 @@ class SimpleAccess(object): return source.get_title() return u'' + def page(self, citation): + """ + Return the page of the citation. + + @param citation: Source object + @type citation: L{gen.lib.Citation} + @return: title of the citation + @rtype: unicode + """ + assert(isinstance(citation, (gen.lib.Citation, NoneType))) + if citation: + return citation.get_page() + return u'' + def author(self, source): """ Return the author of the source. @@ -952,6 +967,8 @@ class SimpleAccess(object): return obj.desc elif isinstance(obj, gen.lib.Source): return self.title(obj) + elif isinstance(obj, gen.lib.Citation): + return self.page(obj) elif isinstance(obj, gen.lib.Place): return place_name(self.dbase, obj.handle) elif isinstance(obj, gen.lib.Repository): diff --git a/src/Simple/_SimpleTable.py b/src/Simple/_SimpleTable.py index 68a96fc3d..423323585 100644 --- a/src/Simple/_SimpleTable.py +++ b/src/Simple/_SimpleTable.py @@ -3,6 +3,7 @@ # # Copyright (C) 2008 Donald N. Allingham # Copyright (C) 2009 Douglas S. Blank +# 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 @@ -352,6 +353,10 @@ class SimpleTable(object): retval.append(self.access.describe(item)) if (self.__link_col == col or link is None): link = ('Source', item.handle) + elif isinstance(item, gen.lib.Citation): + retval.append(self.access.describe(item)) + if (self.__link_col == col or link is None): + link = ('Citation', item.handle) elif isinstance(item, gen.lib.Event): retval.append(self.access.describe(item)) if (self.__link_col == col or link is None): diff --git a/src/Utils.py b/src/Utils.py index 86c46b45d..3e45858b4 100644 --- a/src/Utils.py +++ b/src/Utils.py @@ -3,6 +3,7 @@ # # Copyright (C) 2000-2007 Donald N. Allingham # Copyright (C) 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 @@ -88,11 +89,11 @@ def format_gender( type): return gender.get(type[0], _("Invalid")) confidence = { - gen.lib.SourceRef.CONF_VERY_HIGH : _("Very High"), - gen.lib.SourceRef.CONF_HIGH : _("High"), - gen.lib.SourceRef.CONF_NORMAL : _("Normal"), - gen.lib.SourceRef.CONF_LOW : _("Low"), - gen.lib.SourceRef.CONF_VERY_LOW : _("Very Low"), + gen.lib.Citation.CONF_VERY_HIGH : _("Very High"), + gen.lib.Citation.CONF_HIGH : _("High"), + gen.lib.Citation.CONF_NORMAL : _("Normal"), + gen.lib.Citation.CONF_LOW : _("Low"), + gen.lib.Citation.CONF_VERY_LOW : _("Very Low"), } family_rel_descriptions = { @@ -972,11 +973,78 @@ def get_source_referents(source_handle, db): This function finds all primary objects that refer (directly or through secondary child-objects) to a given source handle in a given database. + Only Citations can refer to sources, so that is all we need to check + """ + _primaries = ('Citation',) + + return (get_referents(source_handle, db, _primaries)) + +def get_citation_referents(citation_handle, db): + """ Find objects that refer the citation. + + This function finds all primary objects that refer (directly or through + secondary child-objects) to a given citation handle in a given database. + """ _primaries = ('Person', 'Family', 'Event', 'Place', 'Source', 'MediaObject', 'Repository') - return (get_referents(source_handle, db, _primaries)) + return (get_referents(citation_handle, db, _primaries)) + +def get_source_and_citation_referents(source_handle, db): + """ + Find all citations that refer to the sources, and recursively, all objects + that refer to the sources. + + This function finds all primary objects that refer (directly or through + secondary child-objects) to a given source handle in a given database. + + Objects -> Citations -> Source + e.g. + Media object M1 -> Citation C1 -> Source S1 + Media object M2 -> Citation C1 -> Source S1 + Person object P1 -> Citation C2 -> Source S1 + + The returned structure is rather ugly, but provides all the information in + a way that is consistent with the other Util functions. + ( + tuple of objects that refer to the source - only first element is present + ([C1, C2],), + list of citations with objects that refer to them + [ + (C1, + tuple of reference lists + P, F, E, Pl, S, M, R + ([], [], [], [], [], [M1, M2]. []) + ) + (C2, + tuple of reference lists + P, F, E, Pl, S, M, R + ([P1], [], [], [], [], []. []) + ) + ] + ) +#47738: DEBUG: citationtreeview.py: line 428: source referents [(['bfe59e90dbb555d0d87'],)] +#47743: DEBUG: citationtreeview.py: line 432: citation bfe59e90dbb555d0d87 +#47825: DEBUG: citationtreeview.py: line 435: citation_referents_list [[('bfe59e90dbb555d0d87', ([], [], ['ba77932bf0b2d59eccb'], [], [], [], []))]] +#47827: DEBUG: citationtreeview.py: line 440: the_lists [((['bfe59e90dbb555d0d87'],), [('bfe59e90dbb555d0d87', ([], [], ['ba77932bf0b2d59eccb'], [], [], [], []))])] + + """ + the_lists = get_source_referents(source_handle, db) + LOG.debug('source referents %s' % [the_lists]) + # now, for each citation, get the objects that refer to that citation + citation_referents_list = [] + for citation in the_lists[0]: + LOG.debug('citation %s' % citation) + refs = get_citation_referents(citation, db) + citation_referents_list += [(citation, refs)] + LOG.debug('citation_referents_list %s' % [citation_referents_list]) + + (citation_list) = the_lists + the_lists = (citation_list, citation_referents_list) + + LOG.debug('the_lists %s' % [the_lists]) + return the_lists def get_media_referents(media_handle, db): """ Find objects that refer the media object. @@ -985,7 +1053,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)) @@ -997,7 +1065,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)) @@ -1456,11 +1524,18 @@ def navigation_label(db, nav_type, handle): obj = db.get_source_from_handle(handle) if obj: label = obj.get_title() + elif nav_type == 'Citation': + obj = db.get_citation_from_handle(handle) + if obj: + label = obj.get_page() + src = db.get_source_from_handle(obj.get_reference_handle()) + if src: + label = src.get_title() + " " + label elif nav_type == 'Repository': obj = db.get_repository_from_handle(handle) if obj: label = obj.get_name() - elif nav_type == 'Media': + elif nav_type == 'Media' or nav_type == 'MediaObject': obj = db.get_object_from_handle(handle) if obj: label = obj.get_description() diff --git a/src/cli/user.py b/src/cli/user.py index 754e83f30..2c3e14931 100644 --- a/src/cli/user.py +++ b/src/cli/user.py @@ -17,7 +17,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # -# $Id$ +# $Id: user.py 18393 2011-10-31 16:46:50Z paul-franklin $ # """ diff --git a/src/config.py b/src/config.py index 1b624c574..9e45c6005 100644 --- a/src/config.py +++ b/src/config.py @@ -5,6 +5,7 @@ # Copyright (C) 2005-2007 Donald N. Allingham # Copyright (C) 2008-2009 Gary Burton # Copyright (C) 2009 Doug Blank +# 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 @@ -177,13 +178,17 @@ register('interface.attribute-height', 350) register('interface.attribute-width', 600) register('interface.child-ref-height', 450) register('interface.child-ref-width', 600) +register('interface.citation-height', 450) +register('interface.citation-sel-height', 450) +register('interface.citation-sel-width', 600) +register('interface.citation-width', 600) register('interface.clipboard-height', 300) register('interface.clipboard-width', 300) register('interface.dont-ask', False) register('interface.view-categories', ["Gramplets", "People", "Relationships", "Families", "Ancestry", "Events", "Places", "Geography", "Sources", - "Repositories", "Media", "Notes"]) + "Citations", "Repositories", "Media", "Notes"]) register('interface.edit-rule-width', 600) register('interface.edit-rule-height', 450) register('interface.event-height', 450) @@ -266,6 +271,7 @@ register('paths.quick-backup-filename', register('preferences.date-format', 0) register('preferences.calendar-format-report', 0) +register('preferences.cprefix', 'C%04d') register('preferences.default-source', False) register('preferences.eprefix', 'E%04d') register('preferences.family-warn', True) diff --git a/src/data/authors.xml b/src/data/authors.xml index 58dbd99bc..992262b6d 100644 --- a/src/data/authors.xml +++ b/src/data/authors.xml @@ -133,4 +133,7 @@ Craig J. Anderson <ander882@hotmail.com> + + Tim G L Lyons <tim.g.lyons@gmail.com> + diff --git a/src/gen/db/backup.py b/src/gen/db/backup.py index 20d1829f8..9a44d6409 100644 --- a/src/gen/db/backup.py +++ b/src/gen/db/backup.py @@ -2,6 +2,7 @@ # Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 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 @@ -63,7 +64,7 @@ import cPickle as pickle #------------------------------------------------------------------------ from gen.db.exceptions import DbException from gen.db.write import FAMILY_TBL, PLACES_TBL, SOURCES_TBL, MEDIA_TBL, \ - EVENTS_TBL, PERSON_TBL, REPO_TBL, NOTE_TBL, TAG_TBL, META + EVENTS_TBL, PERSON_TBL, REPO_TBL, NOTE_TBL, TAG_TBL, META, CITATIONS_TBL #------------------------------------------------------------------------ # @@ -204,6 +205,7 @@ def __build_tbl_map(database): ( FAMILY_TBL, database.family_map.db), ( PLACES_TBL, database.place_map.db), ( SOURCES_TBL, database.source_map.db), + ( CITATIONS_TBL, database.citation_map.db), ( REPO_TBL, database.repository_map.db), ( NOTE_TBL, database.note_map.db), ( MEDIA_TBL, database.media_map.db), diff --git a/src/gen/db/base.py b/src/gen/db/base.py index 4564510b9..cd8ad494c 100644 --- a/src/gen/db/base.py +++ b/src/gen/db/base.py @@ -3,6 +3,7 @@ # # Copyright (C) 2000-2007 Donald N. Allingham # 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 @@ -611,6 +612,12 @@ class DbReadBase(object): """ raise NotImplementedError + def get_raw_citation_data(self, handle): + """ + Return raw (serialized and pickled) Citation object from handle + """ + raise NotImplementedError + def get_raw_tag_data(self, handle): """ Return raw (serialized and pickled) Tag object from handle @@ -736,6 +743,44 @@ class DbReadBase(object): """ raise NotImplementedError + def get_citation_bookmarks(self): + """ + Return the list of Citation handles in the bookmarks. + """ + raise NotImplementedError + + def get_citation_cursor(self): + """ + Return a reference to a cursor over Citation objects + """ + raise NotImplementedError + + def get_citation_from_gramps_id(self, val): + """ + Find a Citation in the database from the passed gramps' ID. + + If no such Citation exists, None is returned. + Needs to be overridden by the derived class. + """ + raise NotImplementedError + + def get_citation_from_handle(self, handle): + """ + Find a Citation in the database from the passed gramps' ID. + + If no such Citation exists, None is returned. + """ + raise NotImplementedError + + def get_citation_handles(self, sort_handles=False): + """ + Return a list of database handles, one handle for each Citation in + the database. + + If sort_handles is True, the list is sorted by Citation title. + """ + raise NotImplementedError + def get_surname_list(self): """ Return the list of locale-sorted surnames contained in the database. diff --git a/src/gen/db/dbconst.py b/src/gen/db/dbconst.py index 1765893b2..8694ac8d8 100644 --- a/src/gen/db/dbconst.py +++ b/src/gen/db/dbconst.py @@ -2,6 +2,7 @@ # Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 2004-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 @@ -42,9 +43,9 @@ __all__ = ( 'DBFLAGS_D', ) + - ('PERSON_KEY', 'FAMILY_KEY', 'SOURCE_KEY', 'EVENT_KEY', - 'MEDIA_KEY', 'PLACE_KEY', 'REPOSITORY_KEY', 'NOTE_KEY', - 'REFERENCE_KEY', 'TAG_KEY' + ('PERSON_KEY', 'FAMILY_KEY', 'SOURCE_KEY', 'CITATION_KEY', + 'EVENT_KEY', 'MEDIA_KEY', 'PLACE_KEY', 'REPOSITORY_KEY', + 'NOTE_KEY', 'REFERENCE_KEY', 'TAG_KEY' ) + ('TXNADD', 'TXNUPD', 'TXNDEL') @@ -84,5 +85,6 @@ REPOSITORY_KEY = 6 REFERENCE_KEY = 7 NOTE_KEY = 8 TAG_KEY = 9 +CITATION_KEY = 10 TXNADD, TXNUPD, TXNDEL = 0, 1, 2 diff --git a/src/gen/db/read.py b/src/gen/db/read.py index fd497307e..deb568cd8 100644 --- a/src/gen/db/read.py +++ b/src/gen/db/read.py @@ -3,6 +3,7 @@ # # Copyright (C) 2000-2007 Donald N. Allingham # 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 @@ -52,8 +53,8 @@ import logging # GRAMPS libraries # #------------------------------------------------------------------------- -from gen.lib import (MediaObject, Person, Family, Source, Event, Place, - Repository, Note, Tag, GenderStats, Researcher, +from gen.lib import (MediaObject, Person, Family, Source, Citation, Event, + Place, Repository, Note, Tag, GenderStats, Researcher, NameOriginType) from gen.db.dbconst import * from gen.utils.callback import Callback @@ -62,6 +63,7 @@ from Utils import create_id import Errors LOG = logging.getLogger(DBLOGNAME) +LOG = logging.getLogger(".citation") #------------------------------------------------------------------------- # # constants @@ -69,8 +71,9 @@ LOG = logging.getLogger(DBLOGNAME) #------------------------------------------------------------------------- from gen.db.dbconst import * -_SIGBASE = ('person', 'family', 'source', 'event', - 'media', 'place', 'repository', 'reference', 'note', 'tag') +_SIGBASE = ('person', 'family', 'source', 'citation', + 'event', 'media', 'place', 'repository', + 'reference', 'note', 'tag') DBERRS = (db.DBRunRecoveryError, db.DBAccessError, db.DBPageNotFoundError, db.DBInvalidArgError) @@ -153,9 +156,10 @@ class DbReadCursor(BsddbBaseCursor): class DbBsddbRead(DbReadBase, Callback): """ Read class for the GRAMPS databases. Implements methods necessary to read - the various object classes. Currently, there are eight (8) classes: + the various object classes. Currently, there are nine (9) classes: - Person, Family, Event, Place, Source, MediaObject, Repository and Note + Person, Family, Event, Place, Source, Citation, MediaObject, + Repository and Note For each object class, there are methods to retrieve data in various ways. In the methods described below, can be one of person, family, @@ -240,6 +244,13 @@ class DbBsddbRead(DbReadBase, Callback): "class_func": Source, "cursor_func": self.get_source_cursor, }, + 'Citation': + { + "handle_func": self.get_citation_from_handle, + "gramps_id_func": self.get_citation_from_gramps_id, + "class_func": Citation, + "cursor_func": self.get_citation_cursor, + }, 'Event': { "handle_func": self.get_event_from_handle, @@ -288,6 +299,7 @@ class DbBsddbRead(DbReadBase, Callback): self.set_object_id_prefix('O%04d') self.set_family_id_prefix('F%04d') self.set_source_id_prefix('S%04d') + self.set_citation_id_prefix('C%04d') self.set_place_id_prefix('P%04d') self.set_event_id_prefix('E%04d') self.set_repository_id_prefix('R%04d') @@ -296,6 +308,7 @@ class DbBsddbRead(DbReadBase, Callback): self.readonly = False self.rand = random.Random(time.time()) self.smap_index = 0 + self.cmap_index = 0 self.emap_index = 0 self.pmap_index = 0 self.fmap_index = 0 @@ -328,6 +341,7 @@ class DbBsddbRead(DbReadBase, Callback): self.fid_trans = {} self.pid_trans = {} self.sid_trans = {} + self.cid_trans = {} self.oid_trans = {} self.rid_trans = {} self.nid_trans = {} @@ -338,6 +352,7 @@ class DbBsddbRead(DbReadBase, Callback): self.family_map = {} self.place_map = {} self.source_map = {} + self.citation_map = {} self.repository_map = {} self.note_map = {} self.media_map = {} @@ -361,6 +376,7 @@ class DbBsddbRead(DbReadBase, Callback): self.event_bookmarks = DbBookmarks() self.place_bookmarks = DbBookmarks() self.source_bookmarks = DbBookmarks() + self.citation_bookmarks = DbBookmarks() self.repo_bookmarks = DbBookmarks() self.media_bookmarks = DbBookmarks() self.note_bookmarks = DbBookmarks() @@ -370,12 +386,13 @@ class DbBsddbRead(DbReadBase, Callback): self.txn = None self.has_changed = False - def set_prefixes(self, person, media, family, source, place, event, - repository, note): + def set_prefixes(self, person, media, family, source, citation, place, + event, repository, note): self.set_person_id_prefix(person) self.set_object_id_prefix(media) self.set_family_id_prefix(family) self.set_source_id_prefix(source) + self.set_citation_id_prefix(citation) self.set_place_id_prefix(place) self.set_event_id_prefix(event) self.set_repository_id_prefix(repository) @@ -418,6 +435,9 @@ class DbBsddbRead(DbReadBase, Callback): def get_source_cursor(self, *args, **kwargs): return self.get_cursor(self.source_map, *args, **kwargs) + def get_citation_cursor(self, *args, **kwargs): + return self.get_cursor(self.citation_map, *args, **kwargs) + def get_media_cursor(self, *args, **kwargs): return self.get_cursor(self.media_map, *args, **kwargs) @@ -451,6 +471,7 @@ class DbBsddbRead(DbReadBase, Callback): ## self.event_bookmarks = None ## self.place_bookmarks = None ## self.source_bookmarks = None +## self.citation_bookmarks = None ## self.repo_bookmarks = None ## self.media_bookmarks = None ## self.note_bookmarks = None @@ -471,6 +492,7 @@ class DbBsddbRead(DbReadBase, Callback): self.emit('family-rebuild') self.emit('place-rebuild') self.emit('source-rebuild') + self.emit('citation-rebuild') self.emit('media-rebuild') self.emit('event-rebuild') self.emit('repository-rebuild') @@ -533,6 +555,15 @@ class DbBsddbRead(DbReadBase, Callback): self.smap_index, self.sid_trans) return gid + def find_next_citation_gramps_id(self): + """ + Return the next available GRAMPS' ID for a Source object based off the + source ID prefix. + """ + self.cmap_index, gid = self.__find_next_gramps_id(self.citation_prefix, + self.cmap_index, self.cid_trans) + return gid + def find_next_family_gramps_id(self): """ Return the next available GRAMPS' ID for a Family object based off the @@ -613,6 +644,14 @@ class DbBsddbRead(DbReadBase, Callback): """ return self.get_from_handle(handle, Source, self.source_map) + def get_citation_from_handle(self, handle): + """ + Find a Citation in the database from the passed handle. + + If no such Citation exists, None is returned. + """ + return self.get_from_handle(handle, Citation, self.citation_map) + def get_object_from_handle(self, handle): """ Find an Object in the database from the passed handle. @@ -735,6 +774,15 @@ class DbBsddbRead(DbReadBase, Callback): return self.__get_obj_from_gramps_id(val, self.sid_trans, Source, self.source_map) + def get_citation_from_gramps_id(self, val): + """ + Find a Citation in the database from the passed gramps' ID. + + If no such Citation exists, None is returned. + """ + return self.__get_obj_from_gramps_id(val, self.cid_trans, Citation, + self.citation_map) + def get_object_from_gramps_id(self, val): """ Find a MediaObject in the database from the passed gramps' ID. @@ -829,6 +877,12 @@ class DbBsddbRead(DbReadBase, Callback): """ return self.get_number_of_records(self.source_map) + def get_number_of_citations(self): + """ + Return the number of citations currently in the database. + """ + return self.get_number_of_records(self.citation_map) + def get_number_of_media_objects(self): """ Return the number of media objects currently in the database. @@ -899,6 +953,20 @@ class DbBsddbRead(DbReadBase, Callback): return handle_list return [] + def get_citation_handles(self, sort_handles=False): + """ + Return a list of database handles, one handle for each Citation in + the database. + + If sort_handles is True, the list is sorted by Citation Volume/Page. + """ + if self.db_is_open: + handle_list = self.all_handles(self.citation_map) + if sort_handles: + handle_list.sort(key=self.__sortbycitation_key) + return handle_list + return [] + def get_media_object_handles(self, sort_handles=False): """ Return a list of database handles, one handle for each MediaObject in @@ -980,6 +1048,7 @@ class DbBsddbRead(DbReadBase, Callback): iter_event_handles = _f(get_event_cursor) iter_place_handles = _f(get_place_cursor) iter_source_handles = _f(get_source_cursor) + iter_citation_handles = _f(get_citation_cursor) iter_media_object_handles = _f(get_media_cursor) iter_repository_handles = _f(get_repository_cursor) iter_note_handles = _f(get_note_cursor) @@ -1005,6 +1074,7 @@ class DbBsddbRead(DbReadBase, Callback): iter_events = _f(get_event_cursor, Event) iter_places = _f(get_place_cursor, Place) iter_sources = _f(get_source_cursor, Source) + iter_citations = _f(get_citation_cursor, Citation) iter_media_objects = _f(get_media_cursor, MediaObject) iter_repositories = _f(get_repository_cursor, Repository) iter_notes = _f(get_note_cursor, Note) @@ -1016,6 +1086,7 @@ class DbBsddbRead(DbReadBase, Callback): PERSON_KEY: self.id_trans, FAMILY_KEY: self.fid_trans, SOURCE_KEY: self.sid_trans, + CITATION_KEY: self.cid_trans, EVENT_KEY: self.eid_trans, MEDIA_KEY: self.oid_trans, PLACE_KEY: self.pid_trans, @@ -1031,6 +1102,7 @@ class DbBsddbRead(DbReadBase, Callback): PERSON_KEY: self.id_trans, FAMILY_KEY: self.fid_trans, SOURCE_KEY: self.sid_trans, + CITATION_KEY: self.cid_trans, EVENT_KEY: self.eid_trans, MEDIA_KEY: self.oid_trans, PLACE_KEY: self.pid_trans, @@ -1119,6 +1191,17 @@ class DbBsddbRead(DbReadBase, Callback): self.source_prefix = self._validated_id_prefix(val, "S") self.sid2user_format = self.__id2user_format(self.source_prefix) + def set_citation_id_prefix(self, val): + """ + Set the naming template for GRAMPS Citation ID values. + + The string is expected to be in the form of a simple text string, or + in a format that contains a C/Python style format string using %d, + such as C%d or C%04d. + """ + self.citation_prefix = self._validated_id_prefix(val, "C") + self.cid2user_format = self.__id2user_format(self.citation_prefix) + def set_object_id_prefix(self, val): """ Set the naming template for GRAMPS MediaObject ID values. @@ -1230,6 +1313,10 @@ class DbBsddbRead(DbReadBase, Callback): """Return the list of Person handles in the bookmarks.""" return self.source_bookmarks + def get_citation_bookmarks(self): + """Return the list of Citation handles in the bookmarks.""" + return self.citation_bookmarks + def get_media_bookmarks(self): """Return the list of Person handles in the bookmarks.""" return self.media_bookmarks @@ -1405,6 +1492,9 @@ class DbBsddbRead(DbReadBase, Callback): def get_raw_source_data(self, handle): return self.__get_raw_data(self.source_map, handle) + def get_raw_citation_data(self, handle): + return self.__get_raw_data(self.citation_map, handle) + def get_raw_repository_data(self, handle): return self.__get_raw_data(self.repository_map, handle) @@ -1472,6 +1562,12 @@ class DbBsddbRead(DbReadBase, Callback): """ return self.__has_handle(self.source_map, handle) + def has_citation_handle(self, handle): + """ + Return True if the handle exists in the current Citation database. + """ + return self.__has_handle(self.citation_map, handle) + def has_tag_handle(self, handle): """ Return True if the handle exists in the current Tag database. @@ -1498,6 +1594,15 @@ class DbBsddbRead(DbReadBase, Callback): source = unicode(self.source_map[str(key)][2]) return locale.strxfrm(source) + def __sortbycitation(self, first, second): + citation1 = unicode(self.citation_map[str(first)][3]) + citation2 = unicode(self.citation_map[str(second)][3]) + return locale.strcoll(citation1, citation2) + + def __sortbycitation_key(self, key): + citation = unicode(self.citation_map[str(key)][3]) + return locale.strxfrm(citation) + def __sortbymedia(self, first, second): media1 = self.media_map[str(first)][4] media2 = self.media_map[str(second)][4] @@ -1573,6 +1678,10 @@ class DbBsddbRead(DbReadBase, Callback): 'cursor_func': self.get_source_cursor, 'class_func': Source, }, + 'Citation': { + 'cursor_func': self.get_citation_cursor, + 'class_func': Citation, + }, 'MediaObject': { 'cursor_func': self.get_media_cursor, 'class_func': MediaObject, diff --git a/src/gen/db/undoredo.py b/src/gen/db/undoredo.py index f39fdf54d..d3fe0ad7f 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 bc8834b7e..bc6c573a4 100644 --- a/src/gen/db/upgrade.py +++ b/src/gen/db/upgrade.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 @@ -25,6 +26,8 @@ from __future__ import with_statement from gen.lib.markertype import MarkerType from gen.lib.tag import Tag import time +import logging +LOG = logging.getLogger(".citation") """ methods to upgrade a database from version 13 to current version @@ -37,6 +40,507 @@ else: from gen.db import BSDDBTxn from gen.lib.nameorigintype import NameOriginType from gen.db.write import _mkname, SURNAMES +from gen.db.dbconst import (PERSON_KEY, FAMILY_KEY, EVENT_KEY, + MEDIA_KEY, PLACE_KEY, REPOSITORY_KEY) +from QuestionDialog import (InfoDialog) + +def gramps_upgrade_16(self): + """Upgrade database from version 15 to 16. This upgrade converts all + SourceRef child objects to Citation Primary objects. + + For each primary object that has a sourceref, what we have to do is: + + (1) create each citation + (2) update the object to reference the Citations + (3) remove backlinks for references from object to Source + (4) add backlinks for references from object to Citations + (5) add backlinks for references from Citation to Source + + the backlinks are all updated at the end by calling + reindex_reference_map + + """ + length = (len(self.note_map) + len(self.person_map) + + len(self.event_map) + len(self.family_map) + + len(self.repository_map) + len(self.media_map) + + len(self.place_map) + len(self.source_map)) + 10 + self.set_total(length) + + # Setup data for upgrade statistics information dialogue + keyorder = [PERSON_KEY, FAMILY_KEY, EVENT_KEY, MEDIA_KEY, + PLACE_KEY, REPOSITORY_KEY] + key2data = { + PERSON_KEY : 0, + FAMILY_KEY : 1, + EVENT_KEY: 2, + MEDIA_KEY: 3, + PLACE_KEY: 4, + REPOSITORY_KEY: 5, + } + key2string = { + PERSON_KEY : _('%6d People upgraded with %6d citations in %6d secs\n'), + FAMILY_KEY : _('%6d Families upgraded with %6d citations in %6d secs\n'), + EVENT_KEY : _('%6d Events upgraded with %6d citations in %6d secs\n'), + MEDIA_KEY : _('%6d Media Objects upgraded with %6d citations in %6d secs\n'), + PLACE_KEY : _('%6d Places upgraded with %6d citations in %6d secs\n'), + REPOSITORY_KEY : _('%6d Repositories upgraded with %6d citations in %6d secs\n'), + } + data_upgradeobject = [0] * 6 + + # Initialise the citation gramps ID number + self.cmap_index = 0 + + # --------------------------------- + # Modify Person + # --------------------------------- + start_num_citations = self.cmap_index + start_time = time.time() + for person_handle in self.person_map.keys(): + person = self.person_map[person_handle] + (handle, gramps_id, gender, primary_name, alternate_names, + death_ref_index, birth_ref_index, event_ref_list, family_list, + parent_family_list, media_list, address_list, attribute_list, + urls, lds_seal_list, source_list, note_list, change, tag_list, + private, person_ref_list) = person + if primary_name: + primary_name = upgrade_name_16(self, primary_name) + if alternate_names: + alternate_names = upgrade_name_list_16( + self, alternate_names) + if address_list: + address_list = upgrade_address_list_16( + self, address_list) + if media_list: + media_list = upgrade_media_list_16( + self, media_list) + if attribute_list: + attribute_list = upgrade_attribute_list_16( + self, attribute_list) + if lds_seal_list: + lds_seal_list = upgrade_lds_seal_list_16( + self, lds_seal_list) + if source_list: + new_citation_list = convert_source_list_to_citation_list_16( + self, source_list) + else: + new_citation_list = [] + if person_ref_list: + person_ref_list = upgrade_person_ref_list_16( + self, person_ref_list) + if event_ref_list: + event_ref_list = upgrade_event_ref_list_16(self, event_ref_list) + if primary_name or alternate_names or address_list or \ + media_list or attribute_list or lds_seal_list or source_list or \ + person_ref_list or event_ref_list: + new_person = (handle, gramps_id, gender, primary_name, + alternate_names, death_ref_index, + birth_ref_index, event_ref_list, family_list, + parent_family_list, media_list, address_list, + attribute_list, urls, lds_seal_list, + new_citation_list, note_list, change, tag_list, + private, person_ref_list) + with BSDDBTxn(self.env, self.person_map) as txn: + txn.put(str(handle), new_person) + self.update() + + LOG.debug("%d persons upgraded with %d citations in %d seconds. " % + (len(self.person_map.keys()), + self.cmap_index - start_num_citations, + time.time() - start_time)) + data_upgradeobject[key2data[PERSON_KEY]] = (len(self.person_map.keys()), + self.cmap_index - start_num_citations, + time.time() - start_time) + + # --------------------------------- + # Modify Media + # --------------------------------- + start_num_citations = self.cmap_index + start_time = time.time() + for media_handle in self.media_map.keys(): + media = self.media_map[media_handle] + LOG.debug("upgrade media %s" % media[4]) + (handle, gramps_id, path, mime, desc, + attribute_list, source_list, note_list, change, + date, tag_list, private) = media + new_citation_list = convert_source_list_to_citation_list_16( + self, source_list) + new_attribute_list = upgrade_attribute_list_16( + self, attribute_list) + + new_media = (handle, gramps_id, path, mime, desc, + new_attribute_list, new_citation_list, note_list, + change, date, tag_list, private) + LOG.debug(" upgrade new_media %s" % [new_media]) + with BSDDBTxn(self.env, self.media_map) as txn: + txn.put(str(handle), new_media) + LOG.debug(" update ref map media %s" % [handle, + self.get_object_from_handle(handle) ]) + self.update() + + LOG.debug("Media upgrade %d citations upgraded in %d seconds" % + (self.cmap_index - start_num_citations, + int(time.time() - start_time))) + data_upgradeobject[key2data[MEDIA_KEY]] = (len(self.media_map.keys()), + self.cmap_index - start_num_citations, + time.time() - start_time) + + # --------------------------------- + # Modify Places + # --------------------------------- + start_num_citations = self.cmap_index + start_time = time.time() + for place_handle in self.place_map.keys(): + place = self.place_map[place_handle] + (handle, gramps_id, title, long, lat, + main_loc, alt_loc, urls, media_list, source_list, note_list, + change, private) = place + if source_list: + new_citation_list = convert_source_list_to_citation_list_16( + self, source_list) + else: + new_citation_list = [] + if media_list: + media_list = upgrade_media_list_16( + self, media_list) + if source_list or media_list: + new_place = (handle, gramps_id, title, + long, lat, main_loc, alt_loc, urls, + media_list, new_citation_list, note_list, + change, private) + with BSDDBTxn(self.env, self.place_map) as txn: + txn.put(str(handle), new_place) + self.update() + + LOG.debug("%d places upgraded with %d citations in %d seconds. " % + (len(self.place_map.keys()), + self.cmap_index - start_num_citations, + time.time() - start_time)) + data_upgradeobject[key2data[PLACE_KEY]] = (len(self.place_map.keys()), + self.cmap_index - start_num_citations, + time.time() - start_time) + + # --------------------------------- + # Modify Families + # --------------------------------- + start_num_citations = self.cmap_index + start_time = time.time() + for family_handle in self.family_map.keys(): + family = self.family_map[family_handle] + (handle, gramps_id, father_handle, mother_handle, + child_ref_list, the_type, event_ref_list, media_list, + attribute_list, lds_seal_list, source_list, note_list, + change, tag_list, private) = family + if source_list: + new_citation_list = convert_source_list_to_citation_list_16( + self, source_list) + else: + new_citation_list = [] + if child_ref_list: + child_ref_list = upgrade_child_ref_list_16( + self, child_ref_list) + if lds_seal_list: + lds_seal_list = upgrade_lds_seal_list_16( + self, lds_seal_list) + if media_list: + media_list = upgrade_media_list_16( + self, media_list) + if attribute_list: + attribute_list = upgrade_attribute_list_16( + self, attribute_list) + if event_ref_list: + event_ref_list = upgrade_event_ref_list_16(self, event_ref_list) + if source_list or media_list or child_ref_list or \ + attribute_list or lds_seal_list or event_ref_list: + new_family = (handle, gramps_id, father_handle, mother_handle, + child_ref_list, the_type, event_ref_list, media_list, + attribute_list, lds_seal_list, new_citation_list, + note_list, change, tag_list, private) + with BSDDBTxn(self.env, self.family_map) as txn: + txn.put(str(handle), new_family) + self.update() + + LOG.debug("%d familys upgraded with %d citations in %d seconds. " % + (len(self.family_map.keys()), + self.cmap_index - start_num_citations, + time.time() - start_time)) + data_upgradeobject[key2data[FAMILY_KEY]] = (len(self.family_map.keys()), + self.cmap_index - start_num_citations, + time.time() - start_time) + # --------------------------------- + # Modify Events + # --------------------------------- + upgrade_time = 0 + backlink_time = 0 + start_num_citations = self.cmap_index + start_time = time.time() + for event_handle in self.event_map.keys(): + t1 = time.time() + event = self.event_map[event_handle] + (handle, gramps_id, the_type, date, description, place, + source_list, note_list, media_list, attribute_list, + change, private) = event + if source_list: + new_citation_list = convert_source_list_to_citation_list_16( + self, source_list) + else: + new_citation_list = [] + if attribute_list: + attribute_list = upgrade_attribute_list_16( + self, attribute_list) + if media_list: + media_list = upgrade_media_list_16( + self, media_list) + if source_list or attribute_list or media_list: + new_event = (handle, gramps_id, the_type, date, description, place, + new_citation_list, note_list, media_list, + attribute_list, + change, private) + with BSDDBTxn(self.env, self.event_map) as txn: + txn.put(str(handle), new_event) + t2 = time.time() + upgrade_time += t2 - t1 + t3 = time.time() + backlink_time += t3 - t2 + self.update() + + LOG.debug("%d events upgraded with %d citations in %d seconds. " + "Backlinks took %d seconds" % + (len(self.event_map.keys()), + self.cmap_index - start_num_citations, + int(upgrade_time), int(backlink_time))) + data_upgradeobject[key2data[EVENT_KEY]] = (len(self.event_map.keys()), + self.cmap_index - start_num_citations, + time.time() - start_time) + + # --------------------------------- + # Modify Repositories + # --------------------------------- + start_num_citations = self.cmap_index + start_time = time.time() + for repository_handle in self.repository_map.keys(): + repository = self.repository_map[repository_handle] + (handle, gramps_id, the_type, name, note_list, + address_list, urls, change, private) = repository + if address_list: + address_list = upgrade_address_list_16( + self, address_list) + if address_list: + new_repository = (handle, gramps_id, the_type, name, note_list, + address_list, urls, change, private) + with BSDDBTxn(self.env, self.repository_map) as txn: + txn.put(str(handle), new_repository) + self.update() + + LOG.debug("%d repositorys upgraded with %d citations in %d seconds. " % + (len(self.repository_map.keys()), + self.cmap_index - start_num_citations, + time.time() - start_time)) + data_upgradeobject[key2data[REPOSITORY_KEY]] = (len(self.repository_map.keys()), + self.cmap_index - start_num_citations, + time.time() - start_time) + # --------------------------------- + + + # --------------------------------- + # Example database from repository took: + # 3403 events upgraded with 8 citations in 23 seconds. Backlinks took 1071 seconds + # actually 4 of these citations were from: + # Media upgrade 4 citations upgraded in 4 seconds + # by only doing the backlinks when there might be something to do, + # improved to: + # 3403 events upgraded with 8 citations in 19 seconds. Backlinks took 1348 seconds + # further improved by skipping debug logging: + # 3403 events upgraded with 8 citations in 2 seconds. Backlinks took 167 seconds + + #Number of new objects upgraded: +# 2090 People upgraded with 2092 citations in 2148 secs +# 734 Families upgraded with 735 citations in 768 secs +# 3403 Events upgraded with 4 citations in 212 secs +# 7 Media Objects upgraded with 4 citations in 3 secs +# 852 Places upgraded with 0 citations in 39 secs + +# with reduced diagnostics +#Number of new objects upgraded: +# 73 People upgraded with 76 citations in 74 secs +# 35 Families upgraded with 36 citations in 31 secs +# 3403 Events upgraded with 4 citations in 7 secs +# 7 Media Objects upgraded with 4 citations in 3 secs +# 852 Places upgraded with 0 citations in 1 secs + +# without doing any backlinks +#Number of new objects upgraded: +# 73 People upgraded with 76 citations in 43 secs +# 35 Families upgraded with 36 citations in 24 secs +# 3403 Events upgraded with 4 citations in 6 secs +# 7 Media Objects upgraded with 4 citations in 2 secs +# 852 Places upgraded with 0 citations in 1 secs + +# another run about the same code: +#Number of new objects upgraded: +# 73 People upgraded with 76 citations in 48 secs +# 35 Families upgraded with 36 citations in 21 secs +# 3403 Events upgraded with 4 citations in 9 secs +# 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 + +# without incorrect nestetd tranaction structure: +#Number of new objects upgraded: +# 73 People upgraded with 76 citations in 0 secs +# 35 Families upgraded with 36 citations in 0 secs +# 3403 Events upgraded with 4 citations in 0 secs +# 7 Media Objects upgraded with 4 citations in 0 secs +# 852 Places upgraded with 0 citations in 0 secs + +#[[(73, 76, 0.12430405616760254), (35, 36, 0.042523860931396484), (3403, 4, 0.52303886413574219), (7, 4, 0.058229923248291016), (852, 0, 0.14816904067993164)]] + + + + + + + # Bump up database version. Separate transaction to save metadata. + with BSDDBTxn(self.env, self.metadata) as txn: + txn.put('version', 16) + + LOG.debug([data_upgradeobject]) + txt = _("Number of new objects upgraded:\n") + for key in keyorder: + try: + txt += key2string[key] % data_upgradeobject[key2data[key]] + except: + txt += key2string[key] + txt += _("\n\nYou may want to run\n" + "Tools -> Family Tree Processing -> Merge\n" + "in order to merge citations that contain similar\n" + "information") + InfoDialog(_('Upgrade Statistics'), txt) + +def upgrade_media_list_16(self, media_list): + new_media_list = [] + for media in media_list: + (privacy, source_list, note_list, attribute_list, ref, rect) = media + new_citation_list = convert_source_list_to_citation_list_16( + self, source_list) + new_attribute_list = upgrade_attribute_list_16( + self, attribute_list) + new_media = (privacy, new_citation_list, note_list, new_attribute_list, + ref, rect) + new_media_list.append((new_media)) + return new_media_list + +def upgrade_attribute_list_16(self, attribute_list): + new_attribute_list = [] + for attribute in attribute_list: + (privacy, source_list, note_list, the_type, + value) = attribute + new_citation_list = convert_source_list_to_citation_list_16( + self, source_list) + new_attribute = (privacy, new_citation_list, note_list, + the_type, value) + new_attribute_list.append((new_attribute)) + return new_attribute_list + +def upgrade_child_ref_list_16(self, child_ref_list): + new_child_ref_list = [] + for child_ref in child_ref_list: + (privacy, source_list, note_list, ref, frel, mrel) = child_ref + new_citation_list = convert_source_list_to_citation_list_16( + self, source_list) + new_child_ref = (privacy, new_citation_list, note_list, ref, frel, mrel) + new_child_ref_list.append((new_child_ref)) + return new_child_ref_list + +def upgrade_lds_seal_list_16(self, lds_seal_list): + new_lds_seal_list = [] + for lds_seal in lds_seal_list: + (source_list, note_list, date, type, place, + famc, temple, status, private) = lds_seal + new_citation_list = convert_source_list_to_citation_list_16( + self, source_list) + new_lds_seal = (new_citation_list, note_list, date, type, place, + famc, temple, status, private) + new_lds_seal_list.append((new_lds_seal)) + return new_lds_seal_list + +def upgrade_address_list_16(self, address_list): + new_address_list = [] + for address in address_list: + (privacy, source_list, note_list, date, location) = address + new_citation_list = convert_source_list_to_citation_list_16( + self, source_list) + new_address = (privacy, new_citation_list, note_list, date, location) + new_address_list.append((new_address)) + return new_address_list + +def upgrade_name_list_16(self, name_list): + new_name_list = [] + for name in name_list: + new_name = upgrade_name_16(self, name) + new_name_list.append((new_name)) + return new_name_list + +def upgrade_name_16(self, name): + (privacy, source_list, note, date, first_name, surname_list, suffix, + title, name_type, group_as, sort_as, display_as, call, nick, + famnick) = name + new_citation_list = convert_source_list_to_citation_list_16( + self, source_list) + new_name = (privacy, new_citation_list, note, date, first_name, + surname_list, suffix, title, name_type, group_as, sort_as, + display_as, call, nick, famnick) + return new_name + +def upgrade_person_ref_list_16(self, person_ref_list): + new_person_ref_list = [] + for person_ref in person_ref_list: + (privacy, source_list, note_list, ref, rel) = person_ref + new_citation_list = convert_source_list_to_citation_list_16( + self, source_list) + new_person_ref = (privacy, new_citation_list, note_list, ref, rel) + new_person_ref_list.append((new_person_ref)) + return new_person_ref_list + +def upgrade_event_ref_list_16(self, event_ref_list): + new_event_ref_list = [] + for event_ref in event_ref_list: + (privacy, note_list, attribute_list, ref, role) = event_ref + new_attribute_list = upgrade_attribute_list_16( + self, attribute_list) + new_event_ref = (privacy, note_list, new_attribute_list, ref, role) + new_event_ref_list.append((new_event_ref)) + return new_event_ref_list + +def convert_source_list_to_citation_list_16(self, source_list): + citation_list = [] + for source in source_list: + (date, private, note_list, confidence, ref, page) = source + new_handle = self.create_id() + new_media_list = [] + new_data_map = {} + new_change = time.time() + new_gramps_id = self.citation_prefix % self.cmap_index + new_citation = (new_handle, new_gramps_id, + date, page, confidence, ref, note_list, new_media_list, + new_data_map, new_change, private) + with BSDDBTxn(self.env, self.citation_map) as txn: + txn.put(str(new_handle), new_citation) + self.cmap_index += 1 +# # add backlinks for references from Citation to Source +# with BSDDBTxn(self.env) as txn: +# self.update_reference_map( +# self.get_citation_from_handle(new_handle), +# transaction, +# txn.txn) + citation_list.append((new_handle)) + return citation_list def gramps_upgrade_15(self): """Upgrade database from version 14 to 15. This upgrade adds: diff --git a/src/gen/db/write.py b/src/gen/db/write.py index 458664655..8907cbb2e 100644 --- a/src/gen/db/write.py +++ b/src/gen/db/write.py @@ -3,6 +3,7 @@ # # Copyright (C) 2000-2008 Donald N. Allingham # 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 @@ -54,7 +55,7 @@ else: # #------------------------------------------------------------------------- from gen.lib import (GenderStats, Person, Family, Event, Place, Source, - MediaObject, Repository, Note, Tag) + Citation, MediaObject, Repository, Note, Tag) from gen.db import (DbBsddbRead, DbWriteBase, BSDDBTxn, DbTxn, BsddbBaseCursor, BsddbDowngradeError, DbVersionError, DbEnvironmentError, DbUpgradeRequiredError, find_surname, @@ -66,8 +67,9 @@ import Errors import constfunc _LOG = logging.getLogger(DBLOGNAME) +LOG = logging.getLogger(".citation") _MINVERSION = 9 -_DBVERSION = 15 +_DBVERSION = 16 IDTRANS = "person_id" FIDTRANS = "family_id" @@ -77,6 +79,7 @@ EIDTRANS = "event_id" RIDTRANS = "repo_id" NIDTRANS = "note_id" SIDTRANS = "source_id" +CIDTRANS = "citation_id" TAGTRANS = "tag_name" SURNAMES = "surnames" NAME_GROUP = "name_group" @@ -85,6 +88,7 @@ META = "meta_data" FAMILY_TBL = "family" PLACES_TBL = "place" SOURCES_TBL = "source" +CITATIONS_TBL = "citation" MEDIA_TBL = "media" EVENTS_TBL = "event" PERSON_TBL = "person" @@ -108,6 +112,7 @@ DBERRS = (db.DBRunRecoveryError, db.DBAccessError, CLASS_TO_KEY_MAP = {Person.__name__: PERSON_KEY, Family.__name__: FAMILY_KEY, Source.__name__: SOURCE_KEY, + Citation.__name__: CITATION_KEY, Event.__name__: EVENT_KEY, MediaObject.__name__: MEDIA_KEY, Place.__name__: PLACE_KEY, @@ -118,6 +123,7 @@ CLASS_TO_KEY_MAP = {Person.__name__: PERSON_KEY, KEY_TO_CLASS_MAP = {PERSON_KEY: Person.__name__, FAMILY_KEY: Family.__name__, SOURCE_KEY: Source.__name__, + CITATION_KEY: Citation.__name__, EVENT_KEY: Event.__name__, MEDIA_KEY: MediaObject.__name__, PLACE_KEY: Place.__name__, @@ -129,6 +135,7 @@ KEY_TO_NAME_MAP = {PERSON_KEY: 'person', FAMILY_KEY: 'family', EVENT_KEY: 'event', SOURCE_KEY: 'source', + CITATION_KEY: 'citation', PLACE_KEY: 'place', MEDIA_KEY: 'media', REPOSITORY_KEY: 'repository', @@ -195,7 +202,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback): __signals__ = dict((obj+'-'+op, signal) for obj in ['person', 'family', 'event', 'place', - 'source', 'media', 'note', 'repository', 'tag'] + 'source', 'citation', 'media', 'note', 'repository', 'tag'] for op, signal in zip( ['add', 'update', 'delete', 'rebuild'], [(list,), (list,), (list,), None] @@ -392,8 +399,9 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback): return True # See if we lack write access to any files in the directory - for base in [FAMILY_TBL, PLACES_TBL, SOURCES_TBL, MEDIA_TBL, - EVENTS_TBL, PERSON_TBL, REPO_TBL, NOTE_TBL, REF_MAP, META]: + for base in [FAMILY_TBL, PLACES_TBL, SOURCES_TBL, CITATIONS_TBL, + MEDIA_TBL, EVENTS_TBL, PERSON_TBL, REPO_TBL, + NOTE_TBL, REF_MAP, META]: path = os.path.join(name, base + DBEXT) if os.path.isfile(path) and not os.access(path, os.W_OK): return True @@ -499,6 +507,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback): ("family_map", FAMILY_TBL, db.DB_HASH), ("place_map", PLACES_TBL, db.DB_HASH), ("source_map", SOURCES_TBL, db.DB_HASH), + ("citation_map", CITATIONS_TBL, db.DB_HASH), ("media_map", MEDIA_TBL, db.DB_HASH), ("event_map", EVENTS_TBL, db.DB_HASH), ("person_map", PERSON_TBL, db.DB_HASH), @@ -600,6 +609,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback): self.family_bookmarks.set(meta('family_bookmarks')) self.event_bookmarks.set(meta('event_bookmarks')) self.source_bookmarks.set(meta('source_bookmarks')) + self.citation_bookmarks.set(meta('citation_bookmarks')) self.repo_bookmarks.set(meta('repo_bookmarks')) self.media_bookmarks.set(meta('media_bookmarks')) self.place_bookmarks.set(meta('place_bookmarks')) @@ -646,6 +656,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback): ("eid_trans", EIDTRANS, db.DB_HASH, 0), ("pid_trans", PIDTRANS, db.DB_HASH, 0), ("sid_trans", SIDTRANS, db.DB_HASH, 0), + ("cid_trans", CIDTRANS, db.DB_HASH, 0), ("oid_trans", OIDTRANS, db.DB_HASH, 0), ("rid_trans", RIDTRANS, db.DB_HASH, 0), ("nid_trans", NIDTRANS, db.DB_HASH, 0), @@ -668,6 +679,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback): (self.event_map, self.eid_trans, find_idmap), (self.place_map, self.pid_trans, find_idmap), (self.source_map, self.sid_trans, find_idmap), + (self.citation_map, self.cid_trans, find_idmap), (self.media_map, self.oid_trans, find_idmap), (self.repository_map, self.rid_trans, find_idmap), (self.note_map, self.nid_trans, find_idmap), @@ -684,6 +696,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback): self.secondary_connected = True self.smap_index = len(self.source_map) + self.cmap_index = len(self.citation_map) self.emap_index = len(self.event_map) self.pmap_index = len(self.person_map) self.fmap_index = len(self.family_map) @@ -710,6 +723,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback): ( self.eid_trans, EIDTRANS ), ( self.rid_trans, RIDTRANS ), ( self.nid_trans, NIDTRANS ), + ( self.cid_trans, CIDTRANS ), ( self.tag_trans, TAGTRANS ), ( self.reference_map_primary_map, REF_PRI), ( self.reference_map_referenced_map, REF_REF), @@ -959,6 +973,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback): (self.get_event_cursor, Event), (self.get_place_cursor, Place), (self.get_source_cursor, Source), + (self.get_citation_cursor, Citation), (self.get_media_cursor, MediaObject), (self.get_repository_cursor, Repository), (self.get_note_cursor, Note), @@ -969,6 +984,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback): # to loop through each of the primary object tables. for cursor_func, class_func in primary_table: + print "Rebuilding %s reference map" % class_func.__name__ with cursor_func() as cursor: for found_handle, val in cursor: obj = class_func() @@ -1003,6 +1019,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback): txn.put('family_bookmarks', self.family_bookmarks.get()) txn.put('event_bookmarks', self.event_bookmarks.get()) txn.put('source_bookmarks', self.source_bookmarks.get()) + txn.put('citation_bookmarks', self.citation_bookmarks.get()) txn.put('place_bookmarks', self.place_bookmarks.get()) txn.put('repo_bookmarks', self.repo_bookmarks.get()) txn.put('media_bookmarks', self.media_bookmarks.get()) @@ -1068,6 +1085,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback): self.nid_trans.close() self.oid_trans.close() self.sid_trans.close() + self.cid_trans.close() self.pid_trans.close() self.tag_trans.close() self.reference_map_primary_map.close() @@ -1083,6 +1101,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback): self.note_map.close() self.place_map.close() self.source_map.close() + self.citation_map.close() self.media_map.close() self.event_map.close() self.tag_map.close() @@ -1095,6 +1114,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback): self.note_map = None self.place_map = None self.source_map = None + self.citation_map = None self.media_map = None self.event_map = None self.tag_map = None @@ -1112,6 +1132,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback): self.note_map = None self.place_map = None self.source_map = None + self.citation_map = None self.media_map = None self.event_map = None self.tag_map = None @@ -1184,6 +1205,17 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback): self.find_next_source_gramps_id if set_gid else None, self.commit_source) + def add_citation(self, citation, transaction, set_gid=True): + """ + Add a Citation to the database, assigning internal IDs if they have + not already been defined. + + If not set_gid, then gramps_id is not set. + """ + return self.__add_object(citation, transaction, + self.find_next_citation_gramps_id if set_gid else None, + self.commit_citation) + def add_event(self, event, transaction, set_gid=True): """ Add an Event to the database, assigning internal IDs if they have @@ -1311,6 +1343,14 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback): self.__do_remove(handle, transaction, self.source_map, SOURCE_KEY) + def remove_citation(self, handle, transaction): + """ + Remove the Citation specified by the database handle from the + database, preserving the change in the passed transaction. + """ + self.__do_remove(handle, transaction, self.citation_map, + CITATION_KEY) + def remove_event(self, handle, transaction): """ Remove the Event specified by the database handle from the @@ -1548,6 +1588,20 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback): if attr.type.is_custom() and str(attr.type)] self.media_attributes.update(attr_list) + def commit_citation(self, citation, transaction, change_time=None): + """ + Commit the specified Citation to the database, storing the changes as + part of the transaction. + """ + self.commit_base(citation, self.citation_map, CITATION_KEY, + transaction, change_time) + + attr_list = [] + for mref in citation.media_list: + attr_list += [str(attr.type) for attr in mref.attribute_list + if attr.type.is_custom() and str(attr.type)] + self.media_attributes.update(attr_list) + def commit_place(self, place, transaction, change_time=None): """ Commit the specified Place to the database, storing the changes as @@ -1854,6 +1908,21 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback): upgrade.gramps_upgrade_14(self) if version < 15: upgrade.gramps_upgrade_15(self) + if version < 16: + upgrade.gramps_upgrade_16(self) + + self.reset() + self.set_total(6) + self.__connect_secondary() + # Open undo database + self.__open_undodb() + self.db_is_open = True + self.reindex_reference_map(self.update) + self.reset() + # Close undo database + self.__close_undodb() + self.db_is_open = False + _LOG.debug("Upgrade time: %d seconds" % int(time.time()-t)) diff --git a/src/gen/lib/Makefile.am b/src/gen/lib/Makefile.am index a8fc70c6c..e93115a7f 100644 --- a/src/gen/lib/Makefile.am +++ b/src/gen/lib/Makefile.am @@ -15,6 +15,8 @@ pkgdata_PYTHON = \ calendar.py \ childref.py \ childreftype.py \ + citation.py \ + citationbase.py \ const.py \ datebase.py \ date.py \ @@ -54,11 +56,9 @@ pkgdata_PYTHON = \ repotype.py \ researcher.py \ secondaryobj.py \ - srcbase.py \ srcmediatype.py \ srcnote.py \ src.py \ - srcref.py \ surname.py \ surnamebase.py \ styledtext.py \ diff --git a/src/gen/lib/__init__.py b/src/gen/lib/__init__.py index a5d487b9a..b01fd9a74 100644 --- a/src/gen/lib/__init__.py +++ b/src/gen/lib/__init__.py @@ -2,6 +2,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 # it under the terms of the GNU General Public License as published by @@ -35,7 +36,6 @@ from gen.lib.ldsord import LdsOrd from gen.lib.mediaref import MediaRef from gen.lib.name import Name from gen.lib.reporef import RepoRef -from gen.lib.srcref import SourceRef from gen.lib.surname import Surname from gen.lib.url import Url from gen.lib.witness import Witness @@ -52,6 +52,7 @@ from gen.lib.src import Source from gen.lib.mediaobj import MediaObject from gen.lib.repo import Repository from gen.lib.note import Note +from gen.lib.citation import Citation # Table objects from gen.lib.tag import Tag diff --git a/src/gen/lib/address.py b/src/gen/lib/address.py index 6b6d9898b..38c526ac0 100644 --- a/src/gen/lib/address.py +++ b/src/gen/lib/address.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 @@ -32,7 +33,7 @@ Address class for GRAMPS. #------------------------------------------------------------------------- from gen.lib.secondaryobj import SecondaryObject from gen.lib.privacybase import PrivacyBase -from gen.lib.srcbase import SourceBase +from gen.lib.citationbase import CitationBase from gen.lib.notebase import NoteBase from gen.lib.datebase import DateBase from gen.lib.locationbase import LocationBase @@ -43,7 +44,7 @@ from gen.lib.const import IDENTICAL, EQUAL, DIFFERENT # Address for Person/Repository # #------------------------------------------------------------------------- -class Address(SecondaryObject, PrivacyBase, SourceBase, NoteBase, DateBase, +class Address(SecondaryObject, PrivacyBase, CitationBase, NoteBase, DateBase, LocationBase): """Provide address information.""" @@ -52,7 +53,7 @@ class Address(SecondaryObject, PrivacyBase, SourceBase, NoteBase, DateBase, Create a new Address instance, copying from the source if provided. """ PrivacyBase.__init__(self, source) - SourceBase.__init__(self, source) + CitationBase.__init__(self, source) NoteBase.__init__(self, source) DateBase.__init__(self, source) LocationBase.__init__(self, source) @@ -62,7 +63,7 @@ class Address(SecondaryObject, PrivacyBase, SourceBase, NoteBase, DateBase, Convert the object to a serialized tuple of data. """ return (PrivacyBase.serialize(self), - SourceBase.serialize(self), + CitationBase.serialize(self), NoteBase.serialize(self), DateBase.serialize(self), LocationBase.serialize(self)) @@ -71,10 +72,10 @@ class Address(SecondaryObject, PrivacyBase, SourceBase, NoteBase, DateBase, """ Convert a serialized tuple of data to an object. """ - (privacy, source_list, note_list, date, location) = data + (privacy, citation_list, note_list, date, location) = data PrivacyBase.unserialize(self, privacy) - SourceBase.unserialize(self, source_list) + CitationBase.unserialize(self, citation_list) NoteBase.unserialize(self, note_list) DateBase.unserialize(self, date) LocationBase.unserialize(self, location) @@ -96,7 +97,7 @@ class Address(SecondaryObject, PrivacyBase, SourceBase, NoteBase, DateBase, :returns: Returns the list of child objects that may carry textual data. :rtype: list """ - return self.source_list + return [] def get_note_child_list(self): """ @@ -105,7 +106,7 @@ class Address(SecondaryObject, PrivacyBase, SourceBase, NoteBase, DateBase, :returns: Returns the list of child secondary child objects that may refer notes. :rtype: list """ - return self.source_list + return [] def get_handle_referents(self): """ @@ -115,7 +116,7 @@ class Address(SecondaryObject, PrivacyBase, SourceBase, NoteBase, DateBase, :returns: Returns the list of objects referencing primary objects. :rtype: list """ - return self.source_list + return [] def get_referenced_handles(self): """ @@ -125,7 +126,8 @@ class Address(SecondaryObject, PrivacyBase, SourceBase, NoteBase, DateBase, :returns: List of (classname, handle) tuples for referenced objects. :rtype: list """ - return self.get_referenced_note_handles() + return self.get_referenced_note_handles() + \ + self.get_referenced_citation_handles() def is_equivalent(self, other): """ @@ -158,4 +160,4 @@ class Address(SecondaryObject, PrivacyBase, SourceBase, NoteBase, DateBase, """ self._merge_privacy(acquisition) self._merge_note_list(acquisition) - self._merge_source_reference_list(acquisition) + self._merge_citation_list(acquisition) diff --git a/src/gen/lib/attribute.py b/src/gen/lib/attribute.py index 306287788..a8ed25bf6 100644 --- a/src/gen/lib/attribute.py +++ b/src/gen/lib/attribute.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 @@ -32,7 +33,7 @@ Attribute class for GRAMPS. #------------------------------------------------------------------------- from gen.lib.secondaryobj import SecondaryObject from gen.lib.privacybase import PrivacyBase -from gen.lib.srcbase import SourceBase +from gen.lib.citationbase import CitationBase from gen.lib.notebase import NoteBase from gen.lib.attrtype import AttributeType from gen.lib.const import IDENTICAL, EQUAL, DIFFERENT @@ -42,7 +43,7 @@ from gen.lib.const import IDENTICAL, EQUAL, DIFFERENT # Attribute for Person/Family/MediaObject/MediaRef # #------------------------------------------------------------------------- -class Attribute(SecondaryObject, PrivacyBase, SourceBase, NoteBase): +class Attribute(SecondaryObject, PrivacyBase, CitationBase, NoteBase): """ Provide a simple key/value pair for describing properties. Used to store descriptive information. @@ -63,7 +64,7 @@ class Attribute(SecondaryObject, PrivacyBase, SourceBase, NoteBase): Create a new Attribute object, copying from the source if provided. """ PrivacyBase.__init__(self, source) - SourceBase.__init__(self, source) + CitationBase.__init__(self, source) NoteBase.__init__(self, source) if source: @@ -78,7 +79,7 @@ class Attribute(SecondaryObject, PrivacyBase, SourceBase, NoteBase): Convert the object to a serialized tuple of data. """ return (PrivacyBase.serialize(self), - SourceBase.serialize(self), + CitationBase.serialize(self), NoteBase.serialize(self), self.type.serialize(), self.value) @@ -86,9 +87,9 @@ class Attribute(SecondaryObject, PrivacyBase, SourceBase, NoteBase): """ Convert a serialized tuple of data to an object. """ - (privacy, source_list, note_list, the_type, self.value) = data + (privacy, citation_list, note_list, the_type, self.value) = data PrivacyBase.unserialize(self, privacy) - SourceBase.unserialize(self, source_list) + CitationBase.unserialize(self, citation_list) NoteBase.unserialize(self, note_list) self.type.unserialize(the_type) return self @@ -109,7 +110,7 @@ class Attribute(SecondaryObject, PrivacyBase, SourceBase, NoteBase): :returns: Returns the list of child objects that may carry textual data. :rtype: list """ - return self.source_list + return [] def get_note_child_list(self): """ @@ -119,7 +120,7 @@ class Attribute(SecondaryObject, PrivacyBase, SourceBase, NoteBase): refer notes. :rtype: list """ - return self.source_list + return [] def get_handle_referents(self): """ @@ -129,7 +130,7 @@ class Attribute(SecondaryObject, PrivacyBase, SourceBase, NoteBase): :returns: Returns the list of objects referencing primary objects. :rtype: list """ - return self.source_list + return [] def get_referenced_handles(self): """ @@ -139,7 +140,8 @@ class Attribute(SecondaryObject, PrivacyBase, SourceBase, NoteBase): :returns: List of (classname, handle) tuples for referenced objects. :rtype: list """ - return self.get_referenced_note_handles() + return self.get_referenced_note_handles() + \ + self.get_referenced_citation_handles() def is_equivalent(self, other): """ @@ -169,7 +171,7 @@ class Attribute(SecondaryObject, PrivacyBase, SourceBase, NoteBase): :rtype acquisition: Attribute """ self._merge_privacy(acquisition) - self._merge_source_reference_list(acquisition) + self._merge_citation_list(acquisition) self._merge_note_list(acquisition) def set_type(self, val): diff --git a/src/gen/lib/childref.py b/src/gen/lib/childref.py index 372dc6254..4a6ac012c 100644 --- a/src/gen/lib/childref.py +++ b/src/gen/lib/childref.py @@ -3,6 +3,7 @@ # # Copyright (C) 2006-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 @@ -31,7 +32,7 @@ Child Reference class for GRAMPS. #------------------------------------------------------------------------- from gen.lib.secondaryobj import SecondaryObject from gen.lib.privacybase import PrivacyBase -from gen.lib.srcbase import SourceBase +from gen.lib.citationbase import CitationBase from gen.lib.notebase import NoteBase from gen.lib.refbase import RefBase from gen.lib.childreftype import ChildRefType @@ -42,7 +43,7 @@ from gen.lib.const import IDENTICAL, EQUAL, DIFFERENT # Person References for Person/Family # #------------------------------------------------------------------------- -class ChildRef(SecondaryObject, PrivacyBase, SourceBase, NoteBase, RefBase): +class ChildRef(SecondaryObject, PrivacyBase, CitationBase, NoteBase, RefBase): """ Person reference class. @@ -53,7 +54,7 @@ class ChildRef(SecondaryObject, PrivacyBase, SourceBase, NoteBase, RefBase): def __init__(self, source=None): PrivacyBase.__init__(self, source) - SourceBase.__init__(self, source) + CitationBase.__init__(self, source) NoteBase.__init__(self, source) RefBase.__init__(self, source) if source: @@ -68,7 +69,7 @@ class ChildRef(SecondaryObject, PrivacyBase, SourceBase, NoteBase, RefBase): Convert the object to a serialized tuple of data. """ return (PrivacyBase.serialize(self), - SourceBase.serialize(self), + CitationBase.serialize(self), NoteBase.serialize(self), RefBase.serialize(self), self.frel.serialize(), @@ -78,9 +79,9 @@ class ChildRef(SecondaryObject, PrivacyBase, SourceBase, NoteBase, RefBase): """ Convert a serialized tuple of data to an object. """ - (privacy, source_list, note_list, ref, frel, mrel) = data + (privacy, citation_list, note_list, ref, frel, mrel) = data PrivacyBase.unserialize(self, privacy) - SourceBase.unserialize(self, source_list) + CitationBase.unserialize(self, citation_list) NoteBase.unserialize(self, note_list) RefBase.unserialize(self, ref) self.frel = ChildRefType() @@ -105,7 +106,7 @@ class ChildRef(SecondaryObject, PrivacyBase, SourceBase, NoteBase, RefBase): :returns: Returns the list of child objects that may carry textual data. :rtype: list """ - return self.source_list + return [] def get_note_child_list(self): """ @@ -115,7 +116,7 @@ class ChildRef(SecondaryObject, PrivacyBase, SourceBase, NoteBase, RefBase): refer notes. :rtype: list """ - return self.source_list + return [] def get_referenced_handles(self): """ @@ -125,7 +126,8 @@ class ChildRef(SecondaryObject, PrivacyBase, SourceBase, NoteBase, RefBase): :returns: List of (classname, handle) tuples for referenced objects. :rtype: list """ - ret = self.get_referenced_note_handles() + ret = self.get_referenced_note_handles() + \ + self.get_referenced_citation_handles() if self.ref: ret += [('Person', self.ref)] return ret @@ -138,7 +140,7 @@ class ChildRef(SecondaryObject, PrivacyBase, SourceBase, NoteBase, RefBase): :returns: Returns the list of objects referencing primary objects. :rtype: list """ - return self.source_list + return [] def is_equivalent(self, other): """ @@ -169,7 +171,7 @@ class ChildRef(SecondaryObject, PrivacyBase, SourceBase, NoteBase, RefBase): """ self._merge_privacy(acquisition) self._merge_note_list(acquisition) - self._merge_source_reference_list(acquisition) + self._merge_citation_list(acquisition) if (self.mrel != acquisition.mrel) or (self.frel != acquisition.frel): if self.mrel == ChildRefType.UNKNOWN: self.set_mother_relation(acquisition.mrel) diff --git a/src/gen/lib/citation.py b/src/gen/lib/citation.py new file mode 100644 index 000000000..ac9d24a6e --- /dev/null +++ b/src/gen/lib/citation.py @@ -0,0 +1,272 @@ +# +# 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 +# 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$ + +""" +Citation object for GRAMPS. +""" + +#------------------------------------------------------------------------- +# +# standard python modules +# +#------------------------------------------------------------------------- +import logging +LOG = logging.getLogger(".citation") + +#------------------------------------------------------------------------- +# +# GRAMPS modules +# +#------------------------------------------------------------------------- +from gen.lib.primaryobj import PrimaryObject +from gen.lib.mediabase import MediaBase +from gen.lib.notebase import NoteBase +from gen.lib.datebase import DateBase + +#------------------------------------------------------------------------- +# +# Citation class +# +#------------------------------------------------------------------------- +class Citation(MediaBase, NoteBase, PrimaryObject, DateBase): + """ + A record of a citation of a source of information. + + In GEDCOM this is called a SOURCE_CITATION. + The data provided in the <> structure is source-related + information specific to the data being cited. + """ + + CONF_VERY_HIGH = 4 + CONF_HIGH = 3 + CONF_NORMAL = 2 + CONF_LOW = 1 + CONF_VERY_LOW = 0 + + def __init__(self): + """Create a new Citation instance.""" + PrimaryObject.__init__(self) + MediaBase.__init__(self) # 7 + NoteBase.__init__(self) # 6 + DateBase.__init__(self) # 2 + self.source_handle = None # 5 + self.page = "" # 3 + self.confidence = Citation.CONF_NORMAL # 4 + self.datamap = {} # 8 + + def serialize(self, no_text_date = False): + """ + Convert the object to a serialized tuple of data. + """ + return (self.handle, # 0 + self.gramps_id, # 1 + DateBase.serialize(self, no_text_date),# 2 + unicode(self.page), # 3 + self.confidence, # 4 + self.source_handle, # 5 + NoteBase.serialize(self), # 6 + MediaBase.serialize(self), # 7 + self.datamap, # 8 + self.change, # 9 + self.private) # 10 + + def unserialize(self, data): + """ + Convert the data held in a tuple created by the serialize method + back into the data in a Citation structure. + """ + (self.handle, # 0 + self.gramps_id, # 1 + date, # 2 + self.page, # 3 + self.confidence, # 4 + self.source_handle, # 5 + note_list, # 6 + media_list, # 7 + self.datamap, # 8 + self.change, # 9 + self.private # 10 + ) = data + + DateBase.unserialize(self, date) + NoteBase.unserialize(self, note_list) + MediaBase.unserialize(self, media_list) + + def _has_handle_reference(self, classname, handle): + """ + Return True if the object has reference to a given handle of given + primary object type. + + :param classname: The name of the primary object class. + :type classname: str + :param handle: The handle to be checked. + :type handle: str + :returns: Returns whether the object has reference to this handle of + this object type. + :rtype: bool + """ + if classname == 'Note': + return handle in [ref.ref for ref in self.note_list] + elif classname == 'Media': + return handle in [ref.ref for ref in self.media_list] + elif classname == 'Source': + return handle == self.get_reference_handle() + return False + + def _remove_handle_references(self, classname, handle_list): + """ + Remove all references in this object to object handles in the list. + + :param classname: The name of the primary object class. + :type classname: str + :param handle_list: The list of handles to be removed. + :type handle_list: str + """ + if classname == 'Source' and \ + self.get_reference_handle() in handle_list: + self.set_reference_handle(None) + + def _replace_handle_reference(self, classname, old_handle, new_handle): + """ + Replace all references to old handle with those to the new handle. + + :param classname: The name of the primary object class. + :type classname: str + :param old_handle: The handle to be replaced. + :type old_handle: str + :param new_handle: The handle to replace the old one with. + :type new_handle: str + """ + if classname == 'Source' and \ + self.get_reference_handle() == old_handle: + self.set_reference_handle(new_handle) + + def get_text_data_list(self): + """ + Return the list of all textual attributes of the object. + + :returns: Returns the list of all textual attributes of the object. + :rtype: list + """ + return [self.page, + self.gramps_id] + self.datamap.keys() + self.datamap.values() + + def get_text_data_child_list(self): + """ + Return the list of child objects that may carry textual data. + + :returns: Returns the list of child objects that may carry textual data. + :rtype: list + """ + return self.media_list + + def get_note_child_list(self): + """ + Return the list of child secondary objects that may refer notes. + + :returns: Returns the list of child secondary child objects that may + refer notes. + :rtype: list + """ + return self.media_list + + def get_handle_referents(self): + """ + Return the list of child objects which may, directly or through + their children, reference primary objects. + + :returns: Returns the list of objects referencing primary objects. + :rtype: list + """ + return self.media_list + + def get_referenced_handles(self): + """ + Return the list of (classname, handle) tuples for all directly + referenced primary objects. + + :returns: List of (classname, handle) tuples for referenced objects. + :rtype: list + """ + ret = self.get_referenced_note_handles() + if self.get_reference_handle(): + ret += [('Source', self.get_reference_handle())] + return ret + + def merge(self, acquisition): + """ + Merge the content of acquisition into this source. + + :param acquisition: The source to merge with the present source. + :rtype acquisition: Source + """ + self._merge_privacy(acquisition) + self._merge_note_list(acquisition) + self._merge_media_list(acquisition) + # merge confidence + level_priority = [0, 4, 1, 3, 2] + idx = min(level_priority.index(self.confidence), + level_priority.index(acquisition.confidence)) + self.confidence = level_priority[idx] + my_datamap = self.get_data_map() + acquisition_map = acquisition.get_data_map() + for key in acquisition.get_data_map(): + if key not in my_datamap: + self.datamap[key] = acquisition_map[key] + # N.B. a Citation can refer to only one 'Source', so the + # 'Source' from acquisition cannot be merged in + + def get_data_map(self): + """Return the data map of attributes for the source.""" + return self.datamap + + def set_data_map(self, datamap): + """Set the data map of attributes for the source.""" + self.datamap = datamap + + def set_data_item(self, key, value): + """Set the particular data item in the attribute data map.""" + self.datamap[key] = value + + def set_confidence_level(self, val): + """Set the confidence level.""" + self.confidence = val + + def get_confidence_level(self): + """Return the confidence level.""" + return self.confidence + + def set_page(self, page): + """Set the page indicator of the Citation.""" + self.page = page + + def get_page(self): + """Get the page indicator of the Citation.""" + return self.page + + def set_reference_handle(self, val): + self.source_handle = val + + def get_reference_handle(self): + return self.source_handle diff --git a/src/gen/lib/citationbase.py b/src/gen/lib/citationbase.py new file mode 100644 index 000000000..58332538d --- /dev/null +++ b/src/gen/lib/citationbase.py @@ -0,0 +1,236 @@ +# +# 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 +# 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$ + +""" +CitationBase class for GRAMPS. +""" + +#------------------------------------------------------------------------- +# +# Python modules +# +#------------------------------------------------------------------------- +import logging +LOG = logging.getLogger(".citation") + +#------------------------------------------------------------------------- +# +# CitationBase class +# +#------------------------------------------------------------------------- +class CitationBase(object): + """ + Base class for storing citations. + + Starting in 3.4, the objects may have multiple citations. + Internally, this class maintains a list of Citation handles, + as a citation_list attribute of the CitationBase object. + This class is analogous to the notebase class. + Both these have no attributes of their own; in this respect, they differ + from classes like MediaRef, which does have attributes (in that case, + privacy, sources, notes and attributes). + + This class, together with the Citation class, replaces the old SourceRef + class. I.e. SourceRef = CitationBase + Citation + """ + def __init__(self, source=None): + """ + Create a new CitationBase, copying from source if not None. + + :param source: Object used to initialize the new object + :type source: CitationBase + """ + self.citation_list = list(source.citation_list) if source else [] + + def serialize(self): + """ + Convert the object to a serialized tuple of data. + """ + return self.citation_list + + def unserialize(self, data): + """ + Convert a serialized tuple of data to an object. + """ + self.citation_list = list(data) + + def add_citation(self, handle): + """ + Add the :class:`~gen.lib.citation.Citation` handle to the list of + citation handles. + + :param handle: :class:`~gen.lib.citation.Citation` handle to add the + list of citations + :type handle: str + + :returns: True if handle was added, False if it already was in the list + :rtype: bool + """ + if handle in self.citation_list: + return False + else: + self.citation_list.append(handle) + return True + + def remove_citation_references(self, citation_handle_list): + """ + Remove the specified handles from the list of citation handles, and all + secondary child objects. + + :param citation_handle_list: The list of citation handles to be removed + :type handle: list + """ + LOG.debug('enter remove_citation handle: %s self: %s citation_list: %s' + % (citation_handle_list, self, self.citation_list)) + for handle in citation_handle_list: + if handle in self.citation_list: + LOG.debug('remove handle %s from citation_list %s' % + (handle, self.citation_list)) + self.citation_list.remove(handle) + LOG.debug('get_citation_child_list %s' % + self.get_citation_child_list()) + for item in self.get_citation_child_list(): + item.remove_citation_references(citation_handle_list) + + def get_citation_child_list(self): + """ + Return the list of child secondary objects that may refer citations. + + All methods which inherit from CitationBase and have other child objects + with citations, should return here a list of child objects which are + CitationBase + + :returns: Returns the list of child secondary child objects that may + refer citations. + :rtype: list + """ + return [] + + def get_citation_list(self): + """ + Return the list of :class:`~gen.lib.citation.Citation` handles + associated with the object. + + :returns: The list of :class:`~gen.lib.citation.Citation` handles + :rtype: list + """ + return self.citation_list + + def get_all_citation_lists(self): + """ + Return the list of :class:`~gen.lib.citation.Citation` handles + associated with the object or with child objects. + + :returns: The list of :class:`~gen.lib.citation.Citation` handles + :rtype: list + """ + list = self.citation_list + + for item in self.get_citation_child_list(): + list += item.get_citation_list() + return list + + def has_citation_reference(self, citation_handle): + """ + Return True if the object or any of its child objects has reference + to this citation handle. + + :param citation_handle: The citation handle to be checked. + :type citation_handle: str + :returns: Returns whether the object or any of its child objects has + reference to this citation handle. + :rtype: bool + """ + for citation_ref in self.citation_list: + if citation_ref == citation_handle: + return True + + LOG.debug("citation child list %s" % self.get_citation_child_list()) + for item in self.get_citation_child_list(): + if item.has_citation_reference(citation_handle): + return True + + return False + + def set_citation_list(self, citation_list): + """ + Assign the passed list to be object's list of + :class:`~gen.lib.citation.Citation` handles. + + :param citation_list: List of :class:`~gen.lib.citation.Citation` + handles to be set on the object + :type citation_list: list + """ + self.citation_list = citation_list + + def _merge_citation_list(self, acquisition): + """ + Merge the list of citations from acquisition with our own. + + :param acquisition: The citation list of this object will be merged + with the current citation list. + :rtype acquisition: CitationBase + """ + for addendum in acquisition.citation_list: + self.add_citation(addendum) + + def get_referenced_citation_handles(self): + """ + Return the list of (classname, handle) tuples for all referenced + citations. + + This method should be used to get the + :class:`~gen.lib.citation.Citation` portion of the list by objects + that store citation lists. + + :returns: List of (classname, handle) tuples for referenced objects. + :rtype: list + """ + return [('Citation', handle) for handle in self.citation_list] + + def replace_citation_references(self, old_handle, new_handle): + """ + Replace references to citation handles in the list of this object and + all child objects and merge equivalent entries. + + :param old_handle: The citation handle to be replaced. + :type old_handle: str + :param new_handle: The citation handle to replace the old one with. + :type new_handle: str + """ + refs_list = self.citation_list[:] + new_ref = None + if new_handle in self.citation_list: + new_ref = new_handle + n_replace = refs_list.count(old_handle) + for ix_replace in xrange(n_replace): + idx = refs_list.index(old_handle) + if new_ref: + self.citation_list.pop(idx) + refs_list.pop(idx) + else: + self.citation_list[idx] = new_handle + + for item in self.get_citation_child_list(): + item.replace_citation_references(old_handle, new_handle) diff --git a/src/gen/lib/event.py b/src/gen/lib/event.py index 49ba5fc5c..0ca340886 100644 --- a/src/gen/lib/event.py +++ b/src/gen/lib/event.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 @@ -25,13 +26,21 @@ Event object for GRAMPS. """ +#------------------------------------------------------------------------- +# +# standard python modules +# +#------------------------------------------------------------------------- +import logging +LOG = logging.getLogger(".citation") + #------------------------------------------------------------------------- # # GRAMPS modules # #------------------------------------------------------------------------- from gen.lib.primaryobj import PrimaryObject -from gen.lib.srcbase import SourceBase +from gen.lib.citationbase import CitationBase from gen.lib.notebase import NoteBase from gen.lib.mediabase import MediaBase from gen.lib.attrbase import AttributeBase @@ -44,7 +53,7 @@ from gen.lib.eventtype import EventType # Event class # #------------------------------------------------------------------------- -class Event(SourceBase, NoteBase, MediaBase, AttributeBase, +class Event(CitationBase, NoteBase, MediaBase, AttributeBase, DateBase, PlaceBase, PrimaryObject): """ The Event record is used to store information about some type of @@ -67,7 +76,7 @@ class Event(SourceBase, NoteBase, MediaBase, AttributeBase, """ PrimaryObject.__init__(self, source) - SourceBase.__init__(self, source) + CitationBase.__init__(self, source) NoteBase.__init__(self, source) MediaBase.__init__(self, source) AttributeBase.__init__(self) @@ -102,7 +111,7 @@ class Event(SourceBase, NoteBase, MediaBase, AttributeBase, return (self.handle, self.gramps_id, self.__type.serialize(), DateBase.serialize(self, no_text_date), self.__description, self.place, - SourceBase.serialize(self), + CitationBase.serialize(self), NoteBase.serialize(self), MediaBase.serialize(self), AttributeBase.serialize(self), @@ -119,7 +128,7 @@ class Event(SourceBase, NoteBase, MediaBase, AttributeBase, """ (self.handle, self.gramps_id, the_type, date, self.__description, self.place, - source_list, note_list, media_list, attribute_list, + citation_list, note_list, media_list, attribute_list, self.change, self.private) = data self.__type = EventType() @@ -127,7 +136,7 @@ class Event(SourceBase, NoteBase, MediaBase, AttributeBase, DateBase.unserialize(self, date) MediaBase.unserialize(self, media_list) AttributeBase.unserialize(self, attribute_list) - SourceBase.unserialize(self, source_list) + CitationBase.unserialize(self, citation_list) NoteBase.unserialize(self, note_list) return self @@ -190,14 +199,14 @@ class Event(SourceBase, NoteBase, MediaBase, AttributeBase, :returns: Returns the list of child objects that may carry textual data. :rtype: list """ - return self.media_list + self.source_list + self.attribute_list + return self.media_list + 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.media_list + self.attribute_list @@ -210,7 +219,7 @@ class Event(SourceBase, NoteBase, MediaBase, AttributeBase, refer notes. :rtype: list """ - return self.media_list + self.attribute_list + self.source_list + return self.media_list + self.attribute_list def get_referenced_handles(self): """ @@ -220,7 +229,8 @@ class Event(SourceBase, NoteBase, MediaBase, AttributeBase, :returns: List of (classname, handle) tuples for referenced objects. :rtype: list """ - ret = self.get_referenced_note_handles() + ret = self.get_referenced_note_handles() + \ + self.get_referenced_citation_handles() if self.place: ret.append(('Place', self.place)) return ret @@ -233,7 +243,7 @@ class Event(SourceBase, NoteBase, MediaBase, AttributeBase, :returns: Returns the list of objects referencing primary objects. :rtype: list """ - return self.get_sourcref_child_list() + self.source_list + return self.get_citation_child_list() def is_empty(self): """ @@ -266,12 +276,13 @@ class Event(SourceBase, 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_source_references()) != len(other.get_source_references()): + len(self.get_citation_list()) != \ + len(other.get_citation_list()): return False index = 0 - olist = other.get_source_references() - for a in self.get_source_references(): + olist = other.get_citation_list() + for a in self.get_citation_list(): if not a.is_equal(olist[index]): return False index += 1 @@ -290,7 +301,7 @@ class Event(SourceBase, NoteBase, MediaBase, AttributeBase, self._merge_privacy(acquisition) self._merge_attribute_list(acquisition) self._merge_note_list(acquisition) - self._merge_source_reference_list(acquisition) + self._merge_citation_list(acquisition) self._merge_media_list(acquisition) def set_type(self, the_type): diff --git a/src/gen/lib/eventref.py b/src/gen/lib/eventref.py index 2e8dc7daf..e5f97b1b4 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,48 +150,48 @@ 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) : + def has_citation_reference(self, citation_handle) : """ - Return True if any of the child objects has reference to this source + Return True if any of the child objects has reference to this citation handle. - :param src_handle: The source handle to be checked. - :type src_handle: str + :param citation_handle: The citation handle to be checked. + :type citation_handle: str :returns: Returns whether any of it's child objects has reference to - this source handle. + this citation handle. :rtype: bool """ - for item in self.get_sourcref_child_list(): - if item.has_source_reference(src_handle): + for item in self.get_citation_child_list(): + if item.has_citation_reference(citation_handle): return True return False - def remove_source_references(self, src_handle_list): + def remove_citation_references(self, citation_handle_list): """ - Remove references to all source handles in the list in all child + Remove references to all citation 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 + :param citation_handle_list: The list of citation handles to be removed. + :type citation_handle_list: list """ - for item in self.get_sourcref_child_list(): - item.remove_source_references(src_handle_list) + for item in self.get_citation_child_list(): + item.remove_citation_references(citation_handle_list) - def replace_source_references(self, old_handle, new_handle): + def replace_citation_references(self, old_handle, new_handle): """ - Replace references to source handles in the list in this object and + Replace references to citation handles in the list in this object and all child objects and merge equivalent entries. - :param old_handle: The source handle to be replaced. + :param old_handle: The citation handle to be replaced. :type old_handle: str - :param new_handle: The source handle to replace the old one with. + :param new_handle: The citation 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) + for item in self.get_citation_child_list(): + item.replace_citation_references(old_handle, new_handle) def is_equivalent(self, other): """ diff --git a/src/gen/lib/family.py b/src/gen/lib/family.py index a4215a497..cf0eb5a67 100644 --- a/src/gen/lib/family.py +++ b/src/gen/lib/family.py @@ -4,6 +4,7 @@ # Copyright (C) 2000-2007 Donald N. Allingham # Copyright (C) 2010 Michiel D. Nauta # 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 @@ -32,6 +33,8 @@ Family object for GRAMPS. # #------------------------------------------------------------------------- from warnings import warn +import logging +LOG = logging.getLogger(".citation") #------------------------------------------------------------------------- # @@ -39,7 +42,7 @@ from warnings import warn # #------------------------------------------------------------------------- from gen.lib.primaryobj import PrimaryObject -from gen.lib.srcbase import SourceBase +from gen.lib.citationbase import CitationBase from gen.lib.notebase import NoteBase from gen.lib.mediabase import MediaBase from gen.lib.attrbase import AttributeBase @@ -55,7 +58,7 @@ from gen.lib.const import IDENTICAL, EQUAL, DIFFERENT # Family class # #------------------------------------------------------------------------- -class Family(SourceBase, NoteBase, MediaBase, AttributeBase, LdsOrdBase, +class Family(CitationBase, NoteBase, MediaBase, AttributeBase, LdsOrdBase, TagBase, PrimaryObject): """ The Family record is the GRAMPS in-memory representation of the @@ -82,7 +85,7 @@ class Family(SourceBase, NoteBase, MediaBase, AttributeBase, LdsOrdBase, including the database handle. """ PrimaryObject.__init__(self) - SourceBase.__init__(self) + CitationBase.__init__(self) NoteBase.__init__(self) MediaBase.__init__(self) AttributeBase.__init__(self) @@ -121,7 +124,7 @@ class Family(SourceBase, NoteBase, MediaBase, AttributeBase, LdsOrdBase, MediaBase.serialize(self), AttributeBase.serialize(self), LdsOrdBase.serialize(self), - SourceBase.serialize(self), + CitationBase.serialize(self), NoteBase.serialize(self), self.change, TagBase.serialize(self), self.private) @@ -132,7 +135,7 @@ class Family(SourceBase, NoteBase, MediaBase, AttributeBase, LdsOrdBase, """ (self.handle, self.gramps_id, self.father_handle, self.mother_handle, child_ref_list, the_type, event_ref_list, media_list, - attribute_list, lds_seal_list, source_list, note_list, + attribute_list, lds_seal_list, citation_list, note_list, self.change, tag_list, self.private) = data self.type = FamilyRelType() @@ -143,7 +146,7 @@ class Family(SourceBase, NoteBase, MediaBase, AttributeBase, LdsOrdBase, for cr in child_ref_list] MediaBase.unserialize(self, media_list) AttributeBase.unserialize(self, attribute_list) - SourceBase.unserialize(self, source_list) + CitationBase.unserialize(self, citation_list) NoteBase.unserialize(self, note_list) LdsOrdBase.unserialize(self, lds_seal_list) TagBase.unserialize(self, tag_list) @@ -270,15 +273,14 @@ class Family(SourceBase, NoteBase, MediaBase, AttributeBase, LdsOrdBase, :rtype: list """ add_list = filter(None, self.lds_ord_list) - return self.media_list + self.attribute_list + \ - self.source_list + add_list + return self.media_list + self.attribute_list + add_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 """ check_list = self.media_list + self.attribute_list + \ @@ -295,7 +297,7 @@ class Family(SourceBase, NoteBase, MediaBase, AttributeBase, LdsOrdBase, :rtype: list """ check_list = self.media_list + self.attribute_list + \ - self.lds_ord_list + self.child_ref_list + self.source_list + \ + self.lds_ord_list + self.child_ref_list + \ self.event_ref_list return check_list @@ -307,7 +309,8 @@ class Family(SourceBase, NoteBase, MediaBase, AttributeBase, LdsOrdBase, :returns: List of (classname, handle) tuples for referenced objects. :rtype: list """ - ret = self.get_referenced_note_handles() + ret = self.get_referenced_note_handles() + \ + self.get_referenced_citation_handles() ret += [('Person', handle) for handle in ([ref.ref for ref in self.child_ref_list] + [self.father_handle, self.mother_handle]) @@ -323,7 +326,7 @@ class Family(SourceBase, NoteBase, MediaBase, AttributeBase, LdsOrdBase, :returns: Returns the list of objects referencing primary objects. :rtype: list """ - return self.get_sourcref_child_list() + self.source_list + return self.get_citation_child_list() def merge(self, acquisition): """ @@ -343,7 +346,7 @@ class Family(SourceBase, NoteBase, MediaBase, AttributeBase, LdsOrdBase, self._merge_child_ref_list(acquisition) self._merge_attribute_list(acquisition) self._merge_note_list(acquisition) - self._merge_source_reference_list(acquisition) + self._merge_citation_list(acquisition) self._merge_tag_list(acquisition) def set_relationship(self, relationship_type): diff --git a/src/gen/lib/ldsord.py b/src/gen/lib/ldsord.py index 172f69013..85f102c47 100644 --- a/src/gen/lib/ldsord.py +++ b/src/gen/lib/ldsord.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 @@ -39,7 +40,7 @@ from warnings import warn # #------------------------------------------------------------------------- from gen.lib.secondaryobj import SecondaryObject -from gen.lib.srcbase import SourceBase +from gen.lib.citationbase import CitationBase from gen.lib.notebase import NoteBase from gen.lib.datebase import DateBase from gen.lib.placebase import PlaceBase @@ -51,7 +52,7 @@ from gen.lib.const import IDENTICAL, EQUAL, DIFFERENT # LDS Ordinance class # #------------------------------------------------------------------------- -class LdsOrd(SecondaryObject, SourceBase, NoteBase, +class LdsOrd(SecondaryObject, CitationBase, NoteBase, DateBase, PlaceBase, PrivacyBase): """ Class that contains information about LDS Ordinances. @@ -116,7 +117,7 @@ class LdsOrd(SecondaryObject, SourceBase, NoteBase, def __init__(self, source=None): """Create a LDS Ordinance instance.""" - SourceBase.__init__(self, source) + CitationBase.__init__(self, source) NoteBase.__init__(self, source) DateBase.__init__(self, source) PlaceBase.__init__(self, source) @@ -137,7 +138,7 @@ class LdsOrd(SecondaryObject, SourceBase, NoteBase, """ Convert the object to a serialized tuple of data. """ - return (SourceBase.serialize(self), + return (CitationBase.serialize(self), NoteBase.serialize(self), DateBase.serialize(self), self.type, self.place, @@ -147,9 +148,9 @@ class LdsOrd(SecondaryObject, SourceBase, NoteBase, """ Convert a serialized tuple of data to an object. """ - (source_list, note_list, date, self.type, self.place, + (citation_list, note_list, date, self.type, self.place, self.famc, self.temple, self.status, self.private) = data - SourceBase.unserialize(self, source_list) + CitationBase.unserialize(self, citation_list) NoteBase.unserialize(self, note_list) DateBase.unserialize(self, date) return self @@ -171,7 +172,7 @@ class LdsOrd(SecondaryObject, SourceBase, NoteBase, :returns: Returns the list of child objects that may carry textual data. :rtype: list """ - return self.source_list + return [] def get_note_child_list(self): """ @@ -181,7 +182,7 @@ class LdsOrd(SecondaryObject, SourceBase, NoteBase, refer notes. :rtype: list """ - return self.source_list + return [] def get_referenced_handles(self): """ @@ -191,7 +192,8 @@ class LdsOrd(SecondaryObject, SourceBase, NoteBase, :returns: List of (classname, handle) tuples for referenced objects. :rtype: list """ - ret = self.get_referenced_note_handles() + ret = self.get_referenced_note_handles() + \ + self.get_referenced_citation_handles() if self.place: ret += [('Place', self.place)] if self.famc: @@ -206,7 +208,7 @@ class LdsOrd(SecondaryObject, SourceBase, NoteBase, :returns: Returns the list of objects referencing primary objects. :rtype: list """ - return self.source_list + return [] def is_equivalent(self, other): """ @@ -241,7 +243,7 @@ class LdsOrd(SecondaryObject, SourceBase, NoteBase, """ self._merge_privacy(acquisition) self._merge_note_list(acquisition) - self._merge_source_reference_list(acquisition) + self._merge_citation_list(acquisition) def get_type(self): """ diff --git a/src/gen/lib/mediaobj.py b/src/gen/lib/mediaobj.py index a0af0e54d..94a3423ea 100644 --- a/src/gen/lib/mediaobj.py +++ b/src/gen/lib/mediaobj.py @@ -4,6 +4,7 @@ # Copyright (C) 2000-2007 Donald N. Allingham # Copyright (C) 2010 Michiel D. Nauta # 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 @@ -32,6 +33,8 @@ Media object for GRAMPS. # #------------------------------------------------------------------------- import os +import logging +LOG = logging.getLogger(".citation") #------------------------------------------------------------------------- # @@ -39,7 +42,7 @@ import os # #------------------------------------------------------------------------- from gen.lib.primaryobj import PrimaryObject -from gen.lib.srcbase import SourceBase +from gen.lib.citationbase import CitationBase from gen.lib.notebase import NoteBase from gen.lib.datebase import DateBase from gen.lib.attrbase import AttributeBase @@ -50,7 +53,7 @@ from gen.lib.tagbase import TagBase # MediaObject class # #------------------------------------------------------------------------- -class MediaObject(SourceBase, NoteBase, DateBase, AttributeBase, +class MediaObject(CitationBase, NoteBase, DateBase, AttributeBase, TagBase, PrimaryObject): """ Container for information about an image file, including location, @@ -68,7 +71,7 @@ class MediaObject(SourceBase, NoteBase, DateBase, AttributeBase, :type source: MediaObject """ PrimaryObject.__init__(self, source) - SourceBase.__init__(self, source) + CitationBase.__init__(self, source) NoteBase.__init__(self, source) DateBase.__init__(self, source) AttributeBase.__init__(self, source) @@ -105,7 +108,7 @@ class MediaObject(SourceBase, NoteBase, DateBase, AttributeBase, """ return (self.handle, self.gramps_id, self.path, self.mime, self.desc, AttributeBase.serialize(self), - SourceBase.serialize(self), + CitationBase.serialize(self), NoteBase.serialize(self), self.change, DateBase.serialize(self, no_text_date), @@ -121,11 +124,11 @@ class MediaObject(SourceBase, NoteBase, DateBase, AttributeBase, :type data: tuple """ (self.handle, self.gramps_id, self.path, self.mime, self.desc, - attribute_list, source_list, note_list, self.change, + attribute_list, citation_list, note_list, self.change, date, tag_list, self.private) = data AttributeBase.unserialize(self, attribute_list) - SourceBase.unserialize(self, source_list) + CitationBase.unserialize(self, citation_list) NoteBase.unserialize(self, note_list) DateBase.unserialize(self, date) TagBase.unserialize(self, tag_list) @@ -146,14 +149,14 @@ class MediaObject(SourceBase, NoteBase, DateBase, AttributeBase, :returns: Returns the list of child objects that may carry textual data. :rtype: list """ - return self.attribute_list + self.source_list + 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 to citations. :returns: Returns the list of child secondary child objects that may - refer sources. + refer to citations. :rtype: list """ return self.attribute_list @@ -166,7 +169,7 @@ class MediaObject(SourceBase, NoteBase, DateBase, AttributeBase, refer notes. :rtype: list """ - return self.attribute_list + self.source_list + return self.attribute_list + self.citation_list def get_referenced_handles(self): """ @@ -177,7 +180,8 @@ class MediaObject(SourceBase, NoteBase, DateBase, AttributeBase, :rtype: list """ return self.get_referenced_note_handles() + \ - self.get_referenced_tag_handles() + self.get_referenced_tag_handles() + \ + self.get_referenced_citation_handles() def get_handle_referents(self): """ @@ -187,7 +191,7 @@ class MediaObject(SourceBase, NoteBase, DateBase, AttributeBase, :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 merge(self, acquisition): """ @@ -201,8 +205,9 @@ class MediaObject(SourceBase, NoteBase, DateBase, AttributeBase, self._merge_privacy(acquisition) self._merge_attribute_list(acquisition) self._merge_note_list(acquisition) - self._merge_source_reference_list(acquisition) + self._merge_citation_list(acquisition) self._merge_tag_list(acquisition) + self.merge_citation_list(acquisition) def set_mime_type(self, mime_type): """ diff --git a/src/gen/lib/mediaref.py b/src/gen/lib/mediaref.py index 428e5505c..d7d6b6aae 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 @@ -32,7 +33,7 @@ Media Reference class for GRAMPS. #------------------------------------------------------------------------- from gen.lib.secondaryobj import SecondaryObject from gen.lib.privacybase import PrivacyBase -from gen.lib.srcbase import SourceBase +from gen.lib.citationbase import CitationBase from gen.lib.notebase import NoteBase from gen.lib.refbase import RefBase from gen.lib.attrbase import AttributeBase @@ -43,12 +44,12 @@ from gen.lib.const import IDENTICAL, EQUAL, DIFFERENT # MediaObject References for Person/Place/Source # #------------------------------------------------------------------------- -class MediaRef(SecondaryObject, PrivacyBase, SourceBase, NoteBase, RefBase, +class MediaRef(SecondaryObject, PrivacyBase, CitationBase, NoteBase, RefBase, AttributeBase): """Media reference class.""" def __init__(self, source=None): PrivacyBase.__init__(self, source) - SourceBase.__init__(self, source) + CitationBase.__init__(self, source) NoteBase.__init__(self, source) RefBase.__init__(self, source) AttributeBase.__init__(self, source) @@ -63,7 +64,7 @@ class MediaRef(SecondaryObject, PrivacyBase, SourceBase, NoteBase, RefBase, Convert the object to a serialized tuple of data. """ return (PrivacyBase.serialize(self), - SourceBase.serialize(self), + CitationBase.serialize(self), NoteBase.serialize(self), AttributeBase.serialize(self), RefBase.serialize(self), @@ -73,9 +74,9 @@ class MediaRef(SecondaryObject, PrivacyBase, SourceBase, NoteBase, RefBase, """ Convert a serialized tuple of data to an object. """ - (privacy, source_list, note_list,attribute_list,ref,self.rect) = data + (privacy, citation_list, note_list,attribute_list,ref,self.rect) = data PrivacyBase.unserialize(self, privacy) - SourceBase.unserialize(self, source_list) + CitationBase.unserialize(self, citation_list) NoteBase.unserialize(self, note_list) AttributeBase.unserialize(self, attribute_list) RefBase.unserialize(self, ref) @@ -88,14 +89,14 @@ class MediaRef(SecondaryObject, PrivacyBase, SourceBase, NoteBase, RefBase, :returns: Returns the list of child objects that may carry textual data. :rtype: list """ - return self.attribute_list + self.source_list + 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 @@ -108,7 +109,7 @@ class MediaRef(SecondaryObject, PrivacyBase, SourceBase, NoteBase, RefBase, refer notes. :rtype: list """ - return self.attribute_list + self.source_list + return self.attribute_list def get_referenced_handles(self): """ @@ -118,7 +119,8 @@ class MediaRef(SecondaryObject, PrivacyBase, SourceBase, NoteBase, RefBase, :returns: List of (classname, handle) tuples for referenced objects. :rtype: list """ - ret = self.get_referenced_note_handles() + ret = self.get_referenced_note_handles() + \ + self.get_referenced_citation_handles() if self.ref: ret += [('MediaObject', self.ref)] return ret @@ -131,7 +133,7 @@ class MediaRef(SecondaryObject, PrivacyBase, SourceBase, 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): """ @@ -162,7 +164,7 @@ class MediaRef(SecondaryObject, PrivacyBase, SourceBase, NoteBase, RefBase, """ self._merge_privacy(acquisition) self._merge_attribute_list(acquisition) - self._merge_source_reference_list(acquisition) + self._merge_citation_list(acquisition) self._merge_note_list(acquisition) def set_rectangle(self, coord): diff --git a/src/gen/lib/name.py b/src/gen/lib/name.py index ae5f39ce8..c810536db 100644 --- a/src/gen/lib/name.py +++ b/src/gen/lib/name.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 @@ -32,7 +33,7 @@ Name class for GRAMPS. #------------------------------------------------------------------------- from gen.lib.secondaryobj import SecondaryObject from gen.lib.privacybase import PrivacyBase -from gen.lib.srcbase import SourceBase +from gen.lib.citationbase import CitationBase from gen.lib.notebase import NoteBase from gen.lib.datebase import DateBase from gen.lib.surnamebase import SurnameBase @@ -44,7 +45,7 @@ from gen.lib.const import IDENTICAL, EQUAL, DIFFERENT # Personal Name # #------------------------------------------------------------------------- -class Name(SecondaryObject, PrivacyBase, SurnameBase, SourceBase, NoteBase, +class Name(SecondaryObject, PrivacyBase, SurnameBase, CitationBase, NoteBase, DateBase): """ Provide name information about a person. @@ -73,18 +74,18 @@ class Name(SecondaryObject, PrivacyBase, SurnameBase, SourceBase, NoteBase, """ PrivacyBase.__init__(self, source) SurnameBase.__init__(self, source) - SourceBase.__init__(self, source) + CitationBase.__init__(self, source) NoteBase.__init__(self, source) DateBase.__init__(self, source) if data: - (privacy, source_list, note, date, + (privacy, citation_list, note, date, self.first_name, surname_list, self.suffix, self.title, name_type, self.group_as, self.sort_as, self.display_as, self.call, self.nick, self.famnick) = data self.type = NameType(name_type) SurnameBase.unserialize(self, surname_list) PrivacyBase.unserialize(self, privacy) - SourceBase.unserialize(self, source_list) + CitationBase.unserialize(self, citation_list) NoteBase.unserialize(self, note) DateBase.unserialize(self, date) elif source: @@ -115,7 +116,7 @@ class Name(SecondaryObject, PrivacyBase, SurnameBase, SourceBase, NoteBase, Convert the object to a serialized tuple of data. """ return (PrivacyBase.serialize(self), - SourceBase.serialize(self), + CitationBase.serialize(self), NoteBase.serialize(self), DateBase.serialize(self), self.first_name, @@ -140,14 +141,14 @@ class Name(SecondaryObject, PrivacyBase, SurnameBase, SourceBase, NoteBase, """ Convert a serialized tuple of data to an object. """ - (privacy, source_list, note_list, date, + (privacy, citation_list, note_list, date, self.first_name, surname_list, self.suffix, self.title, name_type, self.group_as, self.sort_as, self.display_as, self.call, self.nick, self.famnick) = data self.type = NameType(name_type) PrivacyBase.unserialize(self, privacy) SurnameBase.unserialize(self, surname_list) - SourceBase.unserialize(self, source_list) + CitationBase.unserialize(self, citation_list) NoteBase.unserialize(self, note_list) DateBase.unserialize(self, date) return self @@ -169,7 +170,7 @@ class Name(SecondaryObject, PrivacyBase, SurnameBase, SourceBase, NoteBase, :returns: Returns the list of child objects that may carry textual data. :rtype: list """ - return self.source_list + self.surname_list + return self.surname_list def get_note_child_list(self): """ @@ -179,7 +180,7 @@ class Name(SecondaryObject, PrivacyBase, SurnameBase, SourceBase, NoteBase, refer notes. :rtype: list """ - return self.source_list + return [] def get_handle_referents(self): """ @@ -189,7 +190,7 @@ class Name(SecondaryObject, PrivacyBase, SurnameBase, SourceBase, NoteBase, :returns: Returns the list of objects referencing primary objects. :rtype: list """ - return self.source_list + return [] def get_referenced_handles(self): """ @@ -199,7 +200,8 @@ class Name(SecondaryObject, PrivacyBase, SurnameBase, SourceBase, NoteBase, :returns: List of (classname, handle) tuples for referenced objects. :rtype: list """ - return self.get_referenced_note_handles() + return self.get_referenced_note_handles() + \ + self.get_referenced_citation_handles() def is_equivalent(self, other): """ @@ -238,7 +240,7 @@ class Name(SecondaryObject, PrivacyBase, SurnameBase, SourceBase, NoteBase, self._merge_privacy(acquisition) self._merge_surname_list(acquisition) self._merge_note_list(acquisition) - self._merge_source_reference_list(acquisition) + self._merge_citation_list(acquisition) def set_group_as(self, name): """ diff --git a/src/gen/lib/person.py b/src/gen/lib/person.py index 94589a562..fb1a92850 100644 --- a/src/gen/lib/person.py +++ b/src/gen/lib/person.py @@ -4,6 +4,7 @@ # Copyright (C) 2000-2007 Donald N. Allingham # Copyright (C) 2010 Michiel D. Nauta # 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 @@ -32,7 +33,7 @@ Person object for GRAMPS. # #------------------------------------------------------------------------- from gen.lib.primaryobj import PrimaryObject -from gen.lib.srcbase import SourceBase +from gen.lib.citationbase import CitationBase from gen.lib.notebase import NoteBase from gen.lib.mediabase import MediaBase from gen.lib.attrbase import AttributeBase @@ -53,7 +54,7 @@ from gen.lib.const import IDENTICAL, EQUAL, DIFFERENT # Person class # #------------------------------------------------------------------------- -class Person(SourceBase, NoteBase, AttributeBase, MediaBase, +class Person(CitationBase, NoteBase, AttributeBase, MediaBase, AddressBase, UrlBase, LdsOrdBase, TagBase, PrimaryObject): """ The Person record is the GRAMPS in-memory representation of an @@ -85,7 +86,7 @@ class Person(SourceBase, NoteBase, AttributeBase, MediaBase, handle. """ PrimaryObject.__init__(self) - SourceBase.__init__(self) + CitationBase.__init__(self) NoteBase.__init__(self) MediaBase.__init__(self) AttributeBase.__init__(self) @@ -149,7 +150,7 @@ class Person(SourceBase, NoteBase, AttributeBase, MediaBase, AttributeBase.serialize(self), # 12 UrlBase.serialize(self), # 13 LdsOrdBase.serialize(self), # 14 - SourceBase.serialize(self), # 15 + CitationBase.serialize(self), # 15 NoteBase.serialize(self), # 16 self.change, # 17 TagBase.serialize(self), # 18 @@ -181,7 +182,7 @@ class Person(SourceBase, NoteBase, AttributeBase, MediaBase, attribute_list, # 12 urls, # 13 lds_ord_list, # 14 - source_list, # 15 + citation_list, # 15 note_list, # 16 self.change, # 17 tag_list, # 18 @@ -202,7 +203,7 @@ class Person(SourceBase, NoteBase, AttributeBase, MediaBase, AddressBase.unserialize(self, address_list) AttributeBase.unserialize(self, attribute_list) UrlBase.unserialize(self, urls) - SourceBase.unserialize(self, source_list) + CitationBase.unserialize(self, citation_list) NoteBase.unserialize(self, note_list) TagBase.unserialize(self, tag_list) return self @@ -369,18 +370,17 @@ class Person(SourceBase, NoteBase, AttributeBase, MediaBase, self.address_list + self.attribute_list + self.urls + - self.source_list + self.event_ref_list + add_list + self.person_ref_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.primary_name] + @@ -408,7 +408,6 @@ class Person(SourceBase, NoteBase, AttributeBase, MediaBase, self.attribute_list + self.lds_ord_list + self.person_ref_list + - self.source_list + self.event_ref_list ) @@ -422,7 +421,8 @@ class Person(SourceBase, NoteBase, AttributeBase, MediaBase, """ return [('Family', handle) for handle in (self.family_list + self.parent_family_list)] + ( - self.get_referenced_note_handles() + + self.get_referenced_note_handles() + + self.get_referenced_citation_handles() + self.get_referenced_tag_handles() ) @@ -434,8 +434,7 @@ class Person(SourceBase, NoteBase, AttributeBase, MediaBase, :returns: Returns the list of objects referencing primary objects. :rtype: list """ - #don't count double, notes can be found in sourcref - return self.get_sourcref_child_list() + self.source_list + return (self.get_citation_child_list()) def merge(self, acquisition): """ @@ -462,7 +461,7 @@ class Person(SourceBase, NoteBase, AttributeBase, MediaBase, self._merge_url_list(acquisition) self._merge_person_ref_list(acquisition) self._merge_note_list(acquisition) - self._merge_source_reference_list(acquisition) + self._merge_citation_list(acquisition) self._merge_tag_list(acquisition) map(self.add_parent_family_handle, diff --git a/src/gen/lib/personref.py b/src/gen/lib/personref.py index 7fcda1d02..de8d4b50a 100644 --- a/src/gen/lib/personref.py +++ b/src/gen/lib/personref.py @@ -3,6 +3,7 @@ # # Copyright (C) 2006-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 @@ -32,7 +33,7 @@ Person Reference class for GRAMPS. #------------------------------------------------------------------------- from gen.lib.secondaryobj import SecondaryObject from gen.lib.privacybase import PrivacyBase -from gen.lib.srcbase import SourceBase +from gen.lib.citationbase import CitationBase from gen.lib.notebase import NoteBase from gen.lib.refbase import RefBase from gen.lib.const import IDENTICAL, EQUAL, DIFFERENT @@ -42,7 +43,7 @@ from gen.lib.const import IDENTICAL, EQUAL, DIFFERENT # Person References for Person/Family # #------------------------------------------------------------------------- -class PersonRef(SecondaryObject, PrivacyBase, SourceBase, NoteBase, RefBase): +class PersonRef(SecondaryObject, PrivacyBase, CitationBase, NoteBase, RefBase): """ Person reference class. @@ -53,7 +54,7 @@ class PersonRef(SecondaryObject, PrivacyBase, SourceBase, NoteBase, RefBase): def __init__(self, source=None): PrivacyBase.__init__(self, source) - SourceBase.__init__(self, source) + CitationBase.__init__(self, source) NoteBase.__init__(self, source) RefBase.__init__(self, source) if source: @@ -66,7 +67,7 @@ class PersonRef(SecondaryObject, PrivacyBase, SourceBase, NoteBase, RefBase): Convert the object to a serialized tuple of data. """ return (PrivacyBase.serialize(self), - SourceBase.serialize(self), + CitationBase.serialize(self), NoteBase.serialize(self), RefBase.serialize(self), self.rel) @@ -75,9 +76,9 @@ class PersonRef(SecondaryObject, PrivacyBase, SourceBase, NoteBase, RefBase): """ Convert a serialized tuple of data to an object. """ - (privacy, source_list, note_list, ref, self.rel) = data + (privacy, citation_list, note_list, ref, self.rel) = data PrivacyBase.unserialize(self, privacy) - SourceBase.unserialize(self, source_list) + CitationBase.unserialize(self, citation_list) NoteBase.unserialize(self, note_list) RefBase.unserialize(self, ref) return self @@ -98,7 +99,7 @@ class PersonRef(SecondaryObject, PrivacyBase, SourceBase, NoteBase, RefBase): :returns: Returns the list of child objects that may carry textual data. :rtype: list """ - return self.source_list + return [] def get_note_child_list(self): """ @@ -108,7 +109,7 @@ class PersonRef(SecondaryObject, PrivacyBase, SourceBase, NoteBase, RefBase): refer notes. :rtype: list """ - return self.source_list + return [] def get_referenced_handles(self): """ @@ -118,7 +119,8 @@ class PersonRef(SecondaryObject, PrivacyBase, SourceBase, NoteBase, RefBase): :returns: List of (classname, handle) tuples for referenced objects. :rtype: list """ - ret = self.get_referenced_note_handles() + ret = self.get_referenced_note_handles() + \ + self.get_referenced_citation_handles() if self.ref: ret += [('Person', self.ref)] return ret @@ -131,7 +133,7 @@ class PersonRef(SecondaryObject, PrivacyBase, SourceBase, NoteBase, RefBase): :returns: Returns the list of objects referencing primary objects. :rtype: list """ - return self.source_list + return [] def is_equivalent(self, other): """ @@ -162,7 +164,7 @@ class PersonRef(SecondaryObject, PrivacyBase, SourceBase, NoteBase, RefBase): :param acquisition: PersonRef """ self._merge_privacy(acquisition) - self._merge_source_reference_list(acquisition) + self._merge_citation_list(acquisition) self._merge_note_list(acquisition) def set_relation(self, rel): diff --git a/src/gen/lib/place.py b/src/gen/lib/place.py index 964ccb0dc..375ceb88d 100644 --- a/src/gen/lib/place.py +++ b/src/gen/lib/place.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 @@ -31,7 +32,7 @@ Place object for GRAMPS. # #------------------------------------------------------------------------- from gen.lib.primaryobj import PrimaryObject -from gen.lib.srcbase import SourceBase +from gen.lib.citationbase import CitationBase from gen.lib.notebase import NoteBase from gen.lib.mediabase import MediaBase from gen.lib.urlbase import UrlBase @@ -44,7 +45,7 @@ _EMPTY_LOC = Location().serialize() # Place class # #------------------------------------------------------------------------- -class Place(SourceBase, NoteBase, MediaBase, UrlBase, PrimaryObject): +class Place(CitationBase, NoteBase, MediaBase, UrlBase, PrimaryObject): """ Contains information related to a place, including multiple address information (since place names can change with time), longitude, latitude, @@ -59,7 +60,7 @@ class Place(SourceBase, NoteBase, MediaBase, UrlBase, PrimaryObject): :type source: Place """ PrimaryObject.__init__(self, source) - SourceBase.__init__(self, source) + CitationBase.__init__(self, source) NoteBase.__init__(self, source) MediaBase.__init__(self, source) UrlBase.__init__(self, source) @@ -104,7 +105,7 @@ class Place(SourceBase, NoteBase, MediaBase, UrlBase, PrimaryObject): main_loc, [al.serialize() for al in self.alt_loc], UrlBase.serialize(self), MediaBase.serialize(self), - SourceBase.serialize(self), + CitationBase.serialize(self), NoteBase.serialize(self), self.change, self.private) @@ -118,7 +119,7 @@ class Place(SourceBase, NoteBase, MediaBase, UrlBase, PrimaryObject): :type data: tuple """ (self.handle, self.gramps_id, self.title, self.long, self.lat, - main_loc, alt_loc, urls, media_list, source_list, note_list, + main_loc, alt_loc, urls, media_list, citation_list, note_list, self.change, self.private) = data if main_loc is None: @@ -128,7 +129,7 @@ class Place(SourceBase, NoteBase, MediaBase, UrlBase, PrimaryObject): self.alt_loc = [Location().unserialize(al) for al in alt_loc] UrlBase.unserialize(self, urls) MediaBase.unserialize(self, media_list) - SourceBase.unserialize(self, source_list) + CitationBase.unserialize(self, citation_list) NoteBase.unserialize(self, note_list) return self @@ -149,16 +150,16 @@ class Place(SourceBase, NoteBase, MediaBase, UrlBase, PrimaryObject): :rtype: list """ - ret = self.media_list + self.source_list + self.alt_loc + self.urls + ret = self.media_list + self.alt_loc + self.urls if self.main_loc: ret.append(self.main_loc) return ret - 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: List of child secondary child objects that may refer sources. + :returns: List of child secondary child objects that may refer citations. :rtype: list """ return self.media_list @@ -171,7 +172,7 @@ class Place(SourceBase, NoteBase, MediaBase, UrlBase, PrimaryObject): refer notes. :rtype: list """ - return self.media_list + self.source_list + return self.media_list def get_handle_referents(self): """ @@ -181,7 +182,7 @@ class Place(SourceBase, NoteBase, MediaBase, UrlBase, PrimaryObject): :returns: Returns the list of objects referencing primary objects. :rtype: list """ - return self.media_list + self.source_list + return self.get_citation_child_list() def get_referenced_handles(self): """ @@ -191,7 +192,8 @@ class Place(SourceBase, NoteBase, MediaBase, UrlBase, PrimaryObject): :returns: List of (classname, handle) tuples for referenced objects. :rtype: list """ - return self.get_referenced_note_handles() + return self.get_referenced_note_handles() + \ + self.get_referenced_citation_handles() def merge(self, acquisition): """ Merge the content of acquisition into this place. @@ -204,7 +206,7 @@ class Place(SourceBase, NoteBase, MediaBase, UrlBase, PrimaryObject): self._merge_media_list(acquisition) self._merge_url_list(acquisition) self._merge_note_list(acquisition) - self._merge_source_reference_list(acquisition) + self._merge_citation_list(acquisition) def set_title(self, title): """ diff --git a/src/gen/lib/primaryobj.py b/src/gen/lib/primaryobj.py index a12fea822..25e0a3793 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 @@ -31,7 +32,7 @@ Basic Primary Object class for GRAMPS. #------------------------------------------------------------------------- from gen.lib.tableobj import TableObject from gen.lib.privacybase import PrivacyBase -from gen.lib.srcbase import SourceBase +from gen.lib.citationbase import CitationBase from gen.lib.mediabase import MediaBase #------------------------------------------------------------------------- @@ -130,15 +131,6 @@ class BasicPrimaryObject(TableObject, PrivacyBase): """ pass - def has_source_reference(self, handle): - """ - Indicate if the object has a source references. - - In the base class, no such references exist. Derived classes should - override this if they provide source references. - """ - return False - def has_media_reference(self, handle): """ Indicate if the object has a media references. @@ -148,7 +140,7 @@ class BasicPrimaryObject(TableObject, PrivacyBase): """ return False - def remove_source_references(self, handle_list): + def remove_citation_references(self, handle_list): """ Remove the specified source references from the object. @@ -166,7 +158,7 @@ class BasicPrimaryObject(TableObject, PrivacyBase): """ pass - def replace_source_references(self, old_handle, new_handle): + def replace_citation_references(self, old_handle, new_handle): pass def replace_media_references(self, old_handle, new_handle): @@ -214,8 +206,8 @@ class PrimaryObject(BasicPrimaryObject): of this object type. :rtype: bool """ - if classname == 'Source' and isinstance(self, SourceBase): - return self.has_source_reference(handle) + if classname == 'Citation' and isinstance(self, CitationBase): + return self.has_citation_reference(handle) elif classname == 'MediaObject' and isinstance(self, MediaBase): return self.has_media_reference(handle) else: @@ -230,8 +222,8 @@ class PrimaryObject(BasicPrimaryObject): :param handle_list: The list of handles to be removed. :type handle_list: str """ - if classname == 'Source' and isinstance(self, SourceBase): - self.remove_source_references(handle_list) + if classname == 'Citation' and isinstance(self, CitationBase): + self.remove_citation_references(handle_list) elif classname == 'MediaObject' and isinstance(self, MediaBase): self.remove_media_references(handle_list) else: @@ -248,8 +240,8 @@ class PrimaryObject(BasicPrimaryObject): :param new_handle: The handle to replace the old one with. :type new_handle: str """ - if classname == 'Source' and isinstance(self, SourceBase): - self.replace_source_references(old_handle, new_handle) + if classname == 'Citation' and isinstance(self, CitationBase): + self.replace_citation_references(old_handle, new_handle) elif classname == 'MediaObject' and isinstance(self, MediaBase): self.replace_media_references(old_handle, new_handle) else: diff --git a/src/gen/lib/repo.py b/src/gen/lib/repo.py index e9ff07623..1125f1803 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 @@ -99,12 +100,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 @@ -127,7 +128,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): """ @@ -139,46 +140,44 @@ class Repository(NoteBase, AddressBase, UrlBase, PrimaryObject): """ return self.get_referenced_note_handles() - def has_source_reference(self, src_handle) : + def has_citation_reference(self, citation_handle) : """ - Return True if any of the child objects has reference to this source + Return True if any of the child objects has reference to this citation handle. + + Note: for most objects, this is inherited from citationbase, which + checks both the object and the child objects. However, uniquely, + Repositories do not have citations for the primary object, but only for + child (secondary) objects. Hence, this function has to be implemented + directly in the primary object; it only checks the child objects. - :param src_handle: The source handle to be checked. - :type src_handle: str + :param citation_handle: The citation handle to be checked. + :type citation_handle: str :returns: Returns whether any of it's child objects has reference to - this source handle. + this citation handle. :rtype: bool """ - for item in self.get_sourcref_child_list(): - if item.has_source_reference(src_handle): + for item in self.get_citation_child_list(): + if item.has_citation_reference(citation_handle): return True return False - def remove_source_references(self, src_handle_list): + def replace_citation_references(self, old_handle, new_handle): """ - 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 + Replace references to citation handles in the list in this object and all child objects and merge equivalent entries. + + Note: the same comment about citationbase in has_citation_reference + applies here too. - :param old_handle: The source handle to be replaced. + :param old_handle: The citation handle to be replaced. :type old_handle: str - :param new_handle: The source handle to replace the old one with. + :param new_handle: The citation 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) + for item in self.get_citation_child_list(): + item.replace_citation_references(old_handle, new_handle) def merge(self, acquisition): """ diff --git a/src/gen/lib/src.py b/src/gen/lib/src.py index 84cb4721e..957149e54 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 @@ -151,12 +152,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 @@ -179,7 +180,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): """ @@ -191,47 +192,6 @@ class Source(MediaBase, NoteBase, PrimaryObject): """ return self.get_referenced_note_handles() - 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 merge(self, acquisition): """ Merge the content of acquisition into this source. diff --git a/src/gen/lib/srcbase.py b/src/gen/lib/srcbase.py deleted file mode 100644 index ef92e430a..000000000 --- a/src/gen/lib/srcbase.py +++ /dev/null @@ -1,194 +0,0 @@ -# -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2006 Donald N. Allingham -# Copyright (C) 2010 Michiel D. Nauta -# -# 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$ - -""" -SourceBase class for GRAMPS. -""" - -#------------------------------------------------------------------------- -# -# GRAMPS modules -# -#------------------------------------------------------------------------- -from gen.lib.srcref import SourceRef -from gen.lib.const import IDENTICAL, EQUAL, DIFFERENT - -#------------------------------------------------------------------------- -# -# SourceBase classes -# -#------------------------------------------------------------------------- -class SourceBase(object): - """ - Base class for storing source references. - """ - - def __init__(self, source=None): - """ - Create a new SourceBase, copying from source if not None. - - :param source: Object used to initialize the new object - :type source: SourceBase - """ - - self.source_list = map(SourceRef, source.source_list) if source else [] - - def serialize(self): - """ - Convert the object to a serialized tuple of data. - """ - return [sref.serialize() for sref in self.source_list] - - def unserialize(self, data): - """ - Convert a serialized tuple of data to an object. - """ - self.source_list = [SourceRef().unserialize(item) for item in data] - - def add_source_reference(self, src_ref) : - """ - Add a source reference to this object. - - :param src_ref: The source reference to be added to the - SourceNote's list of source references. - :type src_ref: :class:`~gen.lib.srcref.SourceRef` - """ - self.source_list.append(src_ref) - - def get_source_references(self) : - """ - Return the list of source references associated with the object. - - :returns: Returns the list of :class:`~gen.lib.srcref.SourceRef` objects associated with - the object. - :rtype: list - """ - return 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 [] - - def has_source_reference(self, src_handle) : - """ - Return True if the object or any of it's child objects has reference - to this source handle. - - :param src_handle: The source handle to be checked. - :type src_handle: str - :returns: Returns whether the object or any of it's child objects has - reference to this source handle. - :rtype: bool - """ - for src_ref in self.source_list: - # Using direct access here, not the getter method -- efficiency! - if src_ref.ref == src_handle: - return True - - 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 this object - and all child objects. - - :param src_handle_list: The list of source handles to be removed. - :type src_handle_list: list - """ - new_source_list = [src_ref for src_ref in self.source_list - if src_ref.ref not in src_handle_list] - self.source_list = new_source_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 - """ - refs_list = [ src_ref.ref for src_ref in self.source_list ] - new_ref = None - if new_handle in refs_list: - new_ref = self.source_list[refs_list.index(new_handle)] - n_replace = refs_list.count(old_handle) - for ix_replace in xrange(n_replace): - idx = refs_list.index(old_handle) - self.source_list[idx].ref = new_handle - refs_list[idx] = new_handle - if new_ref: - src_ref = self.source_list[idx] - equi = new_ref.is_equivalent(src_ref) - if equi != DIFFERENT: - if equi == EQUAL: - new_ref.merge(src_ref) - self.source_list.pop(idx) - refs_list.pop(idx) - - for item in self.get_sourcref_child_list(): - item.replace_source_references(old_handle, new_handle) - - def set_source_reference_list(self, src_ref_list) : - """ - Assign the passed list to the object's list of source references. - - :param src_ref_list: List of source references to ba associated - with the object - :type src_ref_list: list of :class:`~gen.lib.srcref.SourceRef` instances - """ - self.source_list = src_ref_list - - def _merge_source_reference_list(self, acquisition): - """ - Merge the list of source references from acquisition with our own. - - :param acquisition: the source references list of this object will be - merged with the current source references list. - :rtype acquisition: SourceRef - """ - srcref_list = self.source_list[:] - for addendum in acquisition.get_source_references(): - for srcref in srcref_list: - equi = srcref.is_equivalent(addendum) - if equi == IDENTICAL: - break - elif equi == EQUAL: - srcref.merge(addendum) - break - else: - self.source_list.append(addendum) diff --git a/src/gen/lib/srcref.py b/src/gen/lib/srcref.py deleted file mode 100644 index a9d8e7256..000000000 --- a/src/gen/lib/srcref.py +++ /dev/null @@ -1,176 +0,0 @@ -# -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2000-2007 Donald N. Allingham -# Copyright (C) 2010 Michiel D. Nauta -# -# 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$ - -""" -Source Reference class for GRAMPS. -""" - -#------------------------------------------------------------------------- -# -# Python modules -# -#------------------------------------------------------------------------- -from warnings import warn - -#------------------------------------------------------------------------- -# -# GRAMPS modules -# -#------------------------------------------------------------------------- -from gen.lib.secondaryobj import SecondaryObject -from gen.lib.datebase import DateBase -from gen.lib.privacybase import PrivacyBase -from gen.lib.notebase import NoteBase -from gen.lib.refbase import RefBase -from gen.lib.const import IDENTICAL, EQUAL, DIFFERENT - -#------------------------------------------------------------------------- -# -# Source References for all primary objects -# -#------------------------------------------------------------------------- -class SourceRef(SecondaryObject, DateBase, PrivacyBase, NoteBase, RefBase): - """ - Source reference, containing detailed information about how a referenced - source relates to it. - """ - - CONF_VERY_HIGH = 4 - CONF_HIGH = 3 - CONF_NORMAL = 2 - CONF_LOW = 1 - CONF_VERY_LOW = 0 - - def __init__(self, source=None): - """Create a new SourceRef, copying from the source if present.""" - DateBase.__init__(self, source) - PrivacyBase.__init__(self, source) - NoteBase.__init__(self, source) - RefBase.__init__(self, source) - if source: - self.confidence = source.confidence - self.page = source.page - else: - self.confidence = SourceRef.CONF_NORMAL - self.page = "" - - def serialize(self): - """ - Convert the object to a serialized tuple of data. - """ - return (DateBase.serialize(self), - PrivacyBase.serialize(self), - NoteBase.serialize(self), - self.confidence, - RefBase.serialize(self), - self.page) - - def unserialize(self, data): - """ - Convert a serialized tuple of data to an object. - """ - (date, privacy, note_list, - self.confidence, ref, self.page) = data - DateBase.unserialize(self, date) - PrivacyBase.unserialize(self, privacy) - NoteBase.unserialize(self, note_list) - RefBase.unserialize(self, ref) - return self - - def get_text_data_list(self): - """ - Return the list of all textual attributes of the object. - - :returns: Returns the list of all textual attributes of the object. - :rtype: list - """ - return [self.page] - - def get_referenced_handles(self): - """ - Return the list of (classname, handle) tuples for all directly - referenced primary objects. - - :returns: List of (classname, handle) tuples for referenced objects. - :rtype: list - """ - ret = self.get_referenced_note_handles() - if self.ref: - ret += [('Source', self.ref)] - return ret - - def is_equivalent(self, other): - """ - Return if this source reference is equivalent, that is agreees in - reference, source page and date, to other. - - :param other: The source reference to compare this one to. - :rtype other: SourceRef - ;returns: Constant indicating degree of equivalence. - :rtype: int - """ - if self.ref != other.ref or \ - self.page != other.page or \ - self.get_date_object() != other.get_date_object(): - return DIFFERENT - else: - if self.is_equal(other): - return IDENTICAL - else: - return EQUAL - - def merge(self, acquisition): - """ - Merge the content of acquisition into this source reference. - - :param acquisition: The source reference to merge with the present one. - :rtype acquisition: SourceRef - """ - self._merge_privacy(acquisition) - self._merge_note_list(acquisition) - # merge confidence - level_priority = [0, 4, 1, 3, 2] - idx = min(level_priority.index(self.confidence), - level_priority.index(acquisition.confidence)) - self.confidence = level_priority[idx] - - def set_confidence_level(self, val): - """Set the confidence level.""" - self.confidence = val - - def get_confidence_level(self): - """Return the confidence level.""" - return self.confidence - - def set_page(self, page): - """Set the page indicator of the SourceRef.""" - self.page = page - - def get_page(self): - """Get the page indicator of the SourceRef.""" - return self.page - - def are_equal(self, other): - """Deprecated function - use is_equal instead.""" - warn( "Use is_equal instead of are_equal", DeprecationWarning, 2) - return self.is_equal(other) diff --git a/src/gen/plug/__init__.py b/src/gen/plug/__init__.py index 4fcce9acb..070edbba2 100644 --- a/src/gen/plug/__init__.py +++ b/src/gen/plug/__init__.py @@ -3,6 +3,7 @@ # # Copyright (C) 2008 Brian G. Matherly # Copyright (C) 2010 Jakim Friant +# 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 @@ -33,6 +34,7 @@ from _pluginreg import (PluginData, PluginRegister, REPORT, TOOL, CATEGORY_QR_FAMILY, CATEGORY_QR_EVENT, CATEGORY_QR_SOURCE, CATEGORY_QR_PLACE, CATEGORY_QR_REPOSITORY, CATEGORY_QR_NOTE, CATEGORY_QR_DATE, PTYPE_STR, CATEGORY_QR_MEDIA, + CATEGORY_QR_CITATION, CATEGORY_QR_SOURCE_OR_CITATION, START, END, make_environment, ) from _manager import BasePluginManager @@ -54,4 +56,5 @@ __all__ = [ "docbackend", "docgen", "menu", Plugin, PluginData, CATEGORY_QR_FAMILY, CATEGORY_QR_EVENT, CATEGORY_QR_SOURCE, CATEGORY_QR_PLACE, CATEGORY_QR_REPOSITORY, CATEGORY_QR_NOTE, CATEGORY_QR_DATE, PTYPE_STR, CATEGORY_QR_MEDIA, + CATEGORY_QR_CITATION, CATEGORY_QR_SOURCE_OR_CITATION, START, END, make_environment] diff --git a/src/gen/plug/_pluginreg.py b/src/gen/plug/_pluginreg.py index 9f6472a9f..ac62a58ac 100644 --- a/src/gen/plug/_pluginreg.py +++ b/src/gen/plug/_pluginreg.py @@ -2,6 +2,7 @@ # Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 2009 Benny Malengier +# 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 @@ -116,6 +117,8 @@ CATEGORY_QR_REPOSITORY = 5 CATEGORY_QR_NOTE = 6 CATEGORY_QR_DATE = 7 CATEGORY_QR_MEDIA = 8 +CATEGORY_QR_CITATION = 9 +CATEGORY_QR_SOURCE_OR_CITATION = 10 # Modes for generating reports REPORT_MODE_GUI = 1 # Standalone report using GUI @@ -1009,6 +1012,8 @@ def make_environment(**kwargs): 'CATEGORY_QR_FAMILY': CATEGORY_QR_FAMILY, 'CATEGORY_QR_EVENT': CATEGORY_QR_EVENT, 'CATEGORY_QR_SOURCE': CATEGORY_QR_SOURCE, + 'CATEGORY_QR_CITATION': CATEGORY_QR_CITATION, + 'CATEGORY_QR_SOURCE_OR_CITATION': CATEGORY_QR_SOURCE_OR_CITATION, 'CATEGORY_QR_PLACE': CATEGORY_QR_PLACE, 'CATEGORY_QR_MEDIA': CATEGORY_QR_MEDIA, 'CATEGORY_QR_REPOSITORY': CATEGORY_QR_REPOSITORY, diff --git a/src/gen/plug/report/_bibliography.py b/src/gen/plug/report/_bibliography.py index fb02dddb7..5292f2359 100644 --- a/src/gen/plug/report/_bibliography.py +++ b/src/gen/plug/report/_bibliography.py @@ -3,6 +3,7 @@ # # Copyright (C) 2007 Brian G. Matherly # Copyright (C) 2010 Jakim Friant +# 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 @@ -25,7 +26,7 @@ Contain and organize bibliographic information. """ import string import math -from gen.lib import SourceRef +import gen.lib class Citation(object): """ @@ -71,7 +72,7 @@ class Citation(object): add another one. @param source_ref: Source Reference - @type source_ref: L{gen.lib.srcref} + @type source_ref: L{gen.lib.citation} @return: The key of the added reference among all the references. @rtype: char """ @@ -113,7 +114,7 @@ class Bibliography(object): def __init__(self, mode=MODE_ALL): """ A bibliography will store citations (sources) and references to those - citations (source refs). Duplicate entries will not be added. To change + citations (citations). Duplicate entries will not be added. To change what is considered duplicate, you can tell the bibliography what source ref information you are interested in by passing in the mode. @@ -131,20 +132,28 @@ class Bibliography(object): self.__citation_list = [] self.mode = mode - def add_reference(self, source_ref): + def add_reference(self, lib_citation): """ Add a reference to a source to this bibliography. If the source already exists, don't add it again. If a similar reference exists, don't add another one. - @param source_ref: Source Reference - @type source_ref: L{gen.lib.srcref} + @param citation: Citation object + @type citation: L{gen.lib.Citation} @return: A tuple containing the index of the source among all the sources and the key of the reference among all the references. If there is no reference information, the second element will be None. @rtype: (int,char) or (int,None) + + N.B. Within this file, the name 'citation' is used both for + gen.lib.Citation, and for _bibliography.Citation. It is not clear how + best to rename the concepts in this file to avoid the clash, so the + names have been retained. In this function, lib_citation is used for + gen.lib.Citation instances, and citation for _bibliography.Citation + instances. Elsewhere in this file, source_ref is used for + gen.lib.Citation instances. """ - source_handle = source_ref.get_reference_handle() + source_handle = lib_citation.get_reference_handle() cindex = 0 rkey = "" citation = None @@ -161,13 +170,13 @@ class Bibliography(object): cindex = len(self.__citation_list) self.__citation_list.append(citation) - if self.__sref_has_info(source_ref): + if self.__sref_has_info(lib_citation): for key, ref in citation.get_ref_list(): - if self.__srefs_are_equal(ref, source_ref): + if self.__srefs_are_equal(ref, lib_citation): # if a reference like this already exists, don't add # another one return (cindex, key) - rkey = citation.add_reference(source_ref) + rkey = citation.add_reference(lib_citation) return (cindex, rkey) @@ -203,7 +212,8 @@ class Bibliography(object): return True if ( self.mode & self.MODE_CONF ) == self.MODE_CONF: confidence = source_ref.get_confidence_level() - if confidence is not None and confidence != SourceRef.CONF_NORMAL: + if confidence is not None and confidence != \ + gen.lib.Citation.CONF_NORMAL: return True if ( self.mode & self.MODE_NOTE ) == self.MODE_NOTE: if len(source_ref.get_note_list()) != 0: @@ -216,8 +226,17 @@ class Bibliography(object): Determine if two source references are equal based on the current mode. """ + # The criterion for equality (in mode==MODE_ALL) is changed for + # citations. Previously, it was based on is_equal from SecondaryObject, + # which does a 'cmp' on the serialised data. (Note that this might not + # have worked properly for Dates; see comments in Date.is_equal and + # EditCitation.data_has_changed). The comparison is now made as to + # whether the two gen.lib.Citations have the same handle (i.e. they are + # actually the same database objects). It is felt that this better + # reflects the intent of Citation objects, which can be merged if they + # are intended to represent the same citation. if self.mode == self.MODE_ALL: - return source_ref1.is_equal(source_ref2) + return source_ref1.handle == source_ref2.handle if ( self.mode & self.MODE_PAGE ) == self.MODE_PAGE: if source_ref1.get_page() != source_ref2.get_page(): return False diff --git a/src/gen/plug/report/endnotes.py b/src/gen/plug/report/endnotes.py index a873ab642..c8d25e18a 100644 --- a/src/gen/plug/report/endnotes.py +++ b/src/gen/plug/report/endnotes.py @@ -5,6 +5,7 @@ # Copyright (C) 2010 Peter Landgren # Copyright (C) 2010 Jakim Friant # Copyright (C) 2011 Adam Stein +# 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 @@ -26,7 +27,7 @@ Provide utilities for printing endnotes in text reports. """ from gen.plug.docgen import FontStyle, ParagraphStyle, FONT_SANS_SERIF -from gen.lib import NoteType, SourceRef +from gen.lib import NoteType, Citation from gen.ggettext import gettext as _ from Utils import confidence from DateHandler import displayer @@ -76,24 +77,25 @@ def add_endnote_styles(style_sheet): para.set_description(_('The basic style used for the endnotes reference notes display.')) style_sheet.add_paragraph_style("Endnotes-Ref-Notes", para) -def cite_source(bibliography, obj): +def cite_source(bibliography, database, obj): """ Cite any sources for the object and add them to the bibliography. @param bibliography: The bibliography to contain the citations. @type bibliography: L{Bibliography} @param obj: An object with source references. - @type obj: L{gen.lib.srcbase} + @type obj: L{gen.lib.CitationBase} """ txt = "" - slist = obj.get_source_references() + slist = obj.get_citation_list() if slist: first = 1 for ref in slist: if not first: txt += ', ' first = 0 - (cindex, key) = bibliography.add_reference(ref) + citation = database.get_citation_from_handle(ref) + (cindex, key) = bibliography.add_reference(citation) txt += "%d" % (cindex + 1) if key is not None: txt += key @@ -186,7 +188,7 @@ def _format_ref_text(ref, key): ref_txt = ref.get_page() # Print only confidence level if it is not Normal - if ref.get_confidence_level() != SourceRef.CONF_NORMAL: + if ref.get_confidence_level() != Citation.CONF_NORMAL: ref_txt += " [" + confidence[ref.get_confidence_level()] + "]" return ref_txt diff --git a/src/gen/proxy/filter.py b/src/gen/proxy/filter.py index baa9ac783..27fdd99f7 100644 --- a/src/gen/proxy/filter.py +++ b/src/gen/proxy/filter.py @@ -4,6 +4,7 @@ # Copyright (C) 2007-2008 Brian G. Matherly # Copyright (C) 2008 Gary Burton # Copyright (C) 2008 Robert Cheramy +# 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 @@ -51,7 +52,7 @@ class FilterProxyDb(ProxyDbBase): def __init__(self, db, person_filter=None, event_filter=None, note_filter=None): """ - Create a new PrivateProxyDb instance. + Create a new FilterProxyDb instance. """ ProxyDbBase.__init__(self, db) self.person_filter = person_filter @@ -143,6 +144,17 @@ class FilterProxyDb(ProxyDbBase): self.sanitize_notebase(source) return source + def get_citation_from_handle(self, handle): + """ + Finds a Citation in the database from the passed gramps' ID. + If no such Citation exists, None is returned. + """ + citation = self.db.get_citation_from_handle(handle) + # Filter notes out + + self.sanitize_notebase(citation) + return citation + def get_object_from_handle(self, handle): """ Finds a MediaObject in the database from the passed GRAMPS' handle. @@ -151,7 +163,6 @@ class FilterProxyDb(ProxyDbBase): media = self.db.get_object_from_handle(handle) # Filter notes out self.sanitize_notebase(media) - self.sanitize_sourcebase(media) return media def get_place_from_handle(self, handle): @@ -162,7 +173,6 @@ class FilterProxyDb(ProxyDbBase): place = self.db.get_place_from_handle(handle) # Filter notes out self.sanitize_notebase(place) - self.sanitize_sourcebase(place) return place def get_event_from_handle(self, handle): @@ -174,7 +184,6 @@ class FilterProxyDb(ProxyDbBase): event = self.db.get_event_from_handle(handle) # Filter all notes out self.sanitize_notebase(event) - self.sanitize_sourcebase(event) return event else: return None @@ -204,7 +213,6 @@ class FilterProxyDb(ProxyDbBase): # Filter notes out self.sanitize_notebase(family) - self.sanitize_sourcebase(family) return family else: return None @@ -275,6 +283,15 @@ class FilterProxyDb(ProxyDbBase): if source: return self.get_source_from_handle(source.get_handle()) + def get_citation_from_gramps_id(self, val): + """ + Finds a Citation in the database from the passed gramps' ID. + If no such Citation exists, None is returned. + """ + citation = self.db.get_citation_from_gramps_id(val) + if citation: + return self.get_citation_from_handle(citation.get_handle()) + def get_object_from_gramps_id(self, val): """ Finds a MediaObject in the database from the passed gramps' ID. @@ -458,23 +475,11 @@ class FilterProxyDb(ProxyDbBase): new_note_list = [ note for note in note_list if note in self.nlist ] notebase.set_note_list(new_note_list) - def sanitize_sourcebase(self, sourcebase): - """ - Filter notes out of an SourceBase object - @param event: SourceBase object to clean - @type event: SourceBase - """ - if sourcebase: - sources = sourcebase.get_source_references() - for source in sources: - self.sanitize_notebase(source) - def sanitize_addressbase(self, addressbase): if addressbase: addresses = addressbase.get_address_list() for address in addresses: self.sanitize_notebase(address) - self.sanitize_sourcebase(address) def sanitize_person(self, person): """ @@ -485,16 +490,13 @@ class FilterProxyDb(ProxyDbBase): if person: # Filter note references self.sanitize_notebase(person) - self.sanitize_sourcebase(person) self.sanitize_addressbase(person) name = person.get_primary_name() self.sanitize_notebase(name) - self.sanitize_sourcebase(name) altnames = person.get_alternate_names() for name in altnames: self.sanitize_notebase(name) - self.sanitize_sourcebase(name) self.sanitize_addressbase(person) diff --git a/src/gen/proxy/private.py b/src/gen/proxy/private.py index bd06bae29..f09f19893 100644 --- a/src/gen/proxy/private.py +++ b/src/gen/proxy/private.py @@ -3,6 +3,7 @@ # # Copyright (C) 2007 Brian G. Matherly # 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 @@ -31,15 +32,17 @@ Proxy class for the GRAMPS databases. Filter out all data marked private. # #------------------------------------------------------------------------- from gen.ggettext import gettext as _ +import logging +LOG = logging.getLogger(".citation") #------------------------------------------------------------------------- # # GRAMPS libraries # #------------------------------------------------------------------------- -from gen.lib import (MediaRef, SourceRef, Attribute, Address, EventRef, +from gen.lib import (MediaRef, Attribute, Address, EventRef, Person, Name, Source, RepoRef, MediaObject, Place, Event, - Family, ChildRef, Repository, LdsOrd, Surname) + Family, ChildRef, Repository, LdsOrd, Surname, Citation) from proxybase import ProxyDbBase class PrivateProxyDb(ProxyDbBase): @@ -74,6 +77,16 @@ class PrivateProxyDb(ProxyDbBase): return sanitize_source(self.db, source) return None + def get_citation_from_handle(self, handle): + """ + Finds a Citation in the database from the passed gramps' ID. + If no such Citation exists, None is returned. + """ + citation = self.db.get_citation_from_handle(handle) + if citation and not citation.get_privacy(): + return sanitize_citation(self.db, citation) + return None + def get_object_from_handle(self, handle): """ Finds an Object in the database from the passed gramps' ID. @@ -184,6 +197,16 @@ class PrivateProxyDb(ProxyDbBase): return sanitize_source(self.db, source) return None + def get_citation_from_gramps_id(self, val): + """ + Finds a Citation in the database from the passed gramps' ID. + If no such Citation exists, None is returned. + """ + citation = self.db.get_citation_from_gramps_id(val) + if citation and not citation.get_privacy(): + return sanitize_citation(self.db, citation) + return None + def get_object_from_gramps_id(self, val): """ Finds a MediaObject in the database from the passed gramps' ID. @@ -244,6 +267,13 @@ class PrivateProxyDb(ProxyDbBase): obj = self.get_unfiltered_source(handle) return obj and not obj.get_privacy() + def include_citation(self, handle): + """ + Predicate returning True if object is to be included, else False + """ + obj = self.get_unfiltered_citation(handle) + return obj and not obj.get_privacy() + def include_place(self, handle): """ Predicate returning True if object is to be included, else False @@ -314,6 +344,15 @@ class PrivateProxyDb(ProxyDbBase): return True return False + def has_citation_handle(self, handle): + """ + returns True if the handle exists in the current Citation database. + """ + citation = self.db.get_citation_from_handle(handle) + if citation and not citation.get_privacy(): + return True + return False + def has_place_handle(self, handle): """ returns True if the handle exists in the current Place database. @@ -382,7 +421,7 @@ class PrivateProxyDb(ProxyDbBase): """ # This isn't done yet because it doesn't check if references are - # private (like a SourceRef or MediaRef). It only checks if the + # private (like a MediaRef). It only checks if the # referenced object is private. objects = { @@ -390,6 +429,7 @@ class PrivateProxyDb(ProxyDbBase): 'Family' : self.db.get_family_from_handle, 'Event' : self.db.get_event_from_handle, 'Source' : self.db.get_source_from_handle, + 'Citation' : self.db.get_citation_from_handle, 'Place' : self.db.get_place_from_handle, 'MediaObject' : self.db.get_object_from_handle, 'Note' : self.db.get_note_from_handle, @@ -426,25 +466,27 @@ def copy_media_ref_list(db, original_obj, clean_obj): if media_object and not media_object.get_privacy(): clean_obj.add_media_reference(sanitize_media_ref(db, media_ref)) -def copy_source_ref_list(db, original_obj, clean_obj): +def copy_citation_ref_list(db, original_obj, clean_obj): """ - Copies source references from one object to another - excluding private - references and references to private objects. + Copies citation references from one object to another - excluding references + to private citations, and references to citations that refer to private + sources. @param db: GRAMPS database to which the references belongs @type db: DbBase @param original_obj: Object that may have private references - @type original_obj: SourceBase + @type original_obj: CitationBase @param clean_obj: Object that will have only non-private references - @type original_obj: SourceBase + @type original_obj: CitationBase @returns: Nothing """ - for ref in original_obj.get_source_references(): - if ref and not ref.get_privacy(): - handle = ref.get_reference_handle() + for citation_handle in original_obj.get_citation_list(): + citation = db.get_citation_from_handle(citation_handle) + if citation and not citation.get_privacy(): + handle = citation.get_reference_handle() source = db.get_source_from_handle(handle) if source and not source.get_privacy(): - clean_obj.add_source_reference(sanitize_source_ref(db, ref)) + clean_obj.add_citation(citation_handle) def copy_notes(db, original_obj, clean_obj): """ @@ -504,7 +546,7 @@ def copy_attributes(db, original_obj, clean_obj): new_attribute.set_type(attribute.get_type()) new_attribute.set_value(attribute.get_value()) copy_notes(db, attribute, new_attribute) - copy_source_ref_list(db, attribute, new_attribute) + copy_citation_ref_list(db, attribute, new_attribute) clean_obj.add_attribute(new_attribute) def copy_urls(db, original_obj, clean_obj): @@ -589,7 +631,7 @@ def sanitize_lds_ord(db, lds_ord): if place and not place.get_privacy(): new_lds_ord.set_place_handle(place_handle) - copy_source_ref_list(db, lds_ord, new_lds_ord) + copy_citation_ref_list(db, lds_ord, new_lds_ord) copy_notes(db, lds_ord, new_lds_ord) return new_lds_ord @@ -620,7 +662,7 @@ def sanitize_address(db, address): new_address.set_phone(address.get_phone()) new_address.set_date_object(address.get_date_object()) - copy_source_ref_list(db, address, new_address) + copy_citation_ref_list(db, address, new_address) copy_notes(db, address, new_address) return new_address @@ -653,7 +695,7 @@ def sanitize_name(db, name): new_name.set_date_object(name.get_date_object()) new_name.set_surname_list(name.get_surname_list()) - copy_source_ref_list(db, name, new_name) + copy_citation_ref_list(db, name, new_name) copy_notes(db, name, new_name) return new_name @@ -678,32 +720,37 @@ def sanitize_media_ref(db, media_ref): new_ref.set_reference_handle(media_ref.get_reference_handle()) copy_notes(db, media_ref, new_ref) copy_attributes(db, media_ref, new_ref) - copy_source_ref_list(db, media_ref, new_ref) + copy_citation_ref_list(db, media_ref, new_ref) return new_ref -def sanitize_source_ref(db, source_ref): +def sanitize_citation(db, citation): """ - Create a new SourceRef instance based off the passed SourceRef + Create a new Citation instance based off the passed Citation instance. The returned instance has all private records removed from it. @param db: GRAMPS database to which the Person object belongs @type db: DbBase - @param source_ref: source SourceRef object that will be copied with + @param citation: source Citation object that will be copied with privacy records removed - @type source_ref: SourceRef - @returns: 'cleansed' SourceRef object - @rtype: SourceRef + @type citation: Citation + @returns: 'cleansed' Citation object + @rtype: Citation """ - new_ref = SourceRef() - new_ref.set_date_object(source_ref.get_date_object()) - new_ref.set_page(source_ref.get_page()) - new_ref.set_confidence_level(source_ref.get_confidence_level()) - new_ref.set_reference_handle(source_ref.get_reference_handle()) - copy_notes(db, source_ref, new_ref) + new_citation = Citation() + new_citation.set_date_object(citation.get_date_object()) + new_citation.set_page(citation.get_page()) + new_citation.set_confidence_level(citation.get_confidence_level()) + new_citation.set_reference_handle(citation.get_reference_handle()) + new_citation.set_data_map(citation.get_data_map()) + new_citation.set_gramps_id(citation.get_gramps_id()) + new_citation.set_handle(citation.get_handle()) + new_citation.set_change_time(citation.get_change_time()) + copy_notes(db, citation, new_citation) + copy_media_ref_list(db, citation, new_citation) - return new_ref + return new_citation def sanitize_event_ref(db, event_ref): """ @@ -812,7 +859,7 @@ def sanitize_person(db, person): copy_addresses(db, person, new_person) copy_attributes(db, person, new_person) - copy_source_ref_list(db, person, new_person) + copy_citation_ref_list(db, person, new_person) copy_urls(db, person, new_person) copy_media_ref_list(db, person, new_person) copy_lds_ords(db, person, new_person) @@ -883,7 +930,7 @@ def sanitize_media(db, media): new_media.set_date_object(media.get_date_object()) new_media.set_tag_list(media.get_tag_list()) - copy_source_ref_list(db, media, new_media) + copy_citation_ref_list(db, media, new_media) copy_attributes(db, media, new_media) copy_notes(db, media, new_media) @@ -914,7 +961,7 @@ def sanitize_place(db, place): new_place.set_main_location(place.get_main_location()) new_place.set_alternate_locations(place.get_alternate_locations()) - copy_source_ref_list(db, place, new_place) + copy_citation_ref_list(db, place, new_place) copy_notes(db, place, new_place) copy_media_ref_list(db, place, new_place) copy_urls(db, place, new_place) @@ -944,7 +991,7 @@ def sanitize_event(db, event): new_event.set_date_object(event.get_date_object()) new_event.set_change_time(event.get_change_time()) - copy_source_ref_list(db, event, new_event) + copy_citation_ref_list(db, event, new_event) copy_notes(db, event, new_event) copy_media_ref_list(db, event, new_event) copy_attributes(db, event, new_event) @@ -1006,7 +1053,7 @@ def sanitize_family(db, family): new_ref.set_father_relation(child_ref.get_father_relation()) new_ref.set_mother_relation(child_ref.get_mother_relation()) copy_notes(db, child_ref, new_ref) - copy_source_ref_list(db, child_ref, new_ref) + copy_citation_ref_list(db, child_ref, new_ref) new_family.add_child_ref(new_ref) # Copy event ref list. @@ -1016,7 +1063,7 @@ def sanitize_family(db, family): if event and not event.get_privacy(): new_family.add_event_ref(sanitize_event_ref(db, event_ref)) - copy_source_ref_list(db, family, new_family) + copy_citation_ref_list(db, family, new_family) copy_notes(db, family, new_family) copy_media_ref_list(db, family, new_family) copy_attributes(db, family, new_family) diff --git a/src/gen/proxy/proxybase.py b/src/gen/proxy/proxybase.py index 0bc945a60..fa6320824 100644 --- a/src/gen/proxy/proxybase.py +++ b/src/gen/proxy/proxybase.py @@ -2,6 +2,7 @@ # Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 2007 Brian G. Matherly +# 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 @@ -52,7 +53,7 @@ class ProxyDbBase(DbReadBase): def __init__(self, db): """ - Create a new PrivateProxyDb instance. + Create a new ProxyDb instance. """ self.db = self.basedb = db while isinstance(self.basedb, ProxyDbBase): @@ -95,6 +96,7 @@ class ProxyDbBase(DbReadBase): include_family = \ include_event = \ include_source = \ + include_citation = \ include_place = \ include_media_object = \ include_repository = \ @@ -142,6 +144,16 @@ class ProxyDbBase(DbReadBase): else: return [] + def get_citation_handles(self, sort_handles=False): + """ + Return a list of database handles, one handle for each Citation in + the database. + """ + if self.db.is_open: + return list(self.iter_citation_handles()) + else: + return [] + def get_place_handles(self, sort_handles=False): """ Return a list of database handles, one handle for each Place in @@ -228,6 +240,13 @@ class ProxyDbBase(DbReadBase): """ return ifilter(self.include_source, self.db.iter_source_handles()) + def iter_citation_handles(self): + """ + Return an iterator over database handles, one handle for each Citation + in the database. + """ + return ifilter(self.include_citation, self.db.iter_citation_handles()) + def iter_place_handles(self): """ Return an iterator over database handles, one handle for each Place in @@ -299,6 +318,12 @@ class ProxyDbBase(DbReadBase): """ return self.__iter_object(self.include_source, self.db.iter_sources) + def iter_citations(self): + """ + Return an iterator over Citation objects in the database + """ + return self.__iter_object(self.include_citation, self.db.iter_citations) + def iter_media_objects(self): """ Return an iterator over Media objects in the database @@ -390,6 +415,14 @@ class ProxyDbBase(DbReadBase): return self.gfilter(self.include_source, self.db.get_source_from_handle(handle)) + def get_citation_from_handle(self, handle): + """ + Finds a Citation in the database from the passed gramps handle. + If no such Citation exists, None is returned. + """ + return self.gfilter(self.include_citation, + self.db.get_citation_from_handle(handle)) + def get_place_from_handle(self, handle): """ Finds a Place in the database from the passed gramps handle. @@ -470,6 +503,14 @@ class ProxyDbBase(DbReadBase): return self.gfilter(self.include_source, self.db.get_source_from_gramps_id(val)) + def get_citation_from_gramps_id(self, val): + """ + Finds a Citation in the database from the passed gramps' ID. + If no such Citation exists, None is returned. + """ + return self.gfilter(self.include_citation, + self.db.get_citation_from_gramps_id(val)) + def get_object_from_gramps_id(self, val): """ Finds a MediaObject in the database from the passed gramps' ID. @@ -550,6 +591,12 @@ class ProxyDbBase(DbReadBase): """ return self.db.get_number_of_sources() + def get_number_of_citations(self): + """ + Return the number of Citations currently in the database. + """ + return self.db.get_number_of_citations() + def get_number_of_media_objects(self): """ Return the number of media objects currently in the database. @@ -666,6 +713,9 @@ class ProxyDbBase(DbReadBase): def get_raw_source_data(self, handle): return self.db.get_raw_source_data(handle) + def get_raw_citation_data(self, handle): + return self.db.get_raw_citation_data(handle) + def get_raw_repository_data(self, handle): return self.db.get_raw_repository_data(handle) @@ -703,6 +753,13 @@ class ProxyDbBase(DbReadBase): return self.gfilter(self.include_source, self.db.get_source_from_handle(handle)) is not None + def has_citation_handle(self, handle): + """ + returns True if the handle exists in the current Citation database. + """ + return self.gfilter(self.include_citation, + self.db.get_citation_from_handle(handle)) is not None + def has_place_handle(self, handle): """ returns True if the handle exists in the current Place database. diff --git a/src/gen/proxy/referenced.py b/src/gen/proxy/referenced.py index 0f3ac68b0..ba24861a1 100644 --- a/src/gen/proxy/referenced.py +++ b/src/gen/proxy/referenced.py @@ -2,6 +2,7 @@ # Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 2008 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 @@ -45,6 +46,7 @@ class ReferencedProxyDb(ProxyDbBase): ProxyDbBase.__init__(self, dbase) self.unreferenced_events = set() self.unreferenced_places = set() + self.unreferenced_citations = set() self.unreferenced_sources = set() self.unreferenced_repositories = set() self.unreferenced_media_objects = set() @@ -72,6 +74,12 @@ class ReferencedProxyDb(ProxyDbBase): """ return handle not in self.unreferenced_events + def include_citation(self, handle): + """ + Filter for citations + """ + return handle not in self.unreferenced_citations + def include_source(self, handle): """ Filter for sources @@ -126,6 +134,7 @@ class ReferencedProxyDb(ProxyDbBase): unref = { "Event" : self.unreferenced_events, "Place" : self.unreferenced_places, + "Citation" : self.unreferenced_citations, "Source" : self.unreferenced_sources, "Repository" : self.unreferenced_repositories, "MediaObject" : self.unreferenced_media_objects, @@ -156,6 +165,7 @@ class ReferencedProxyDb(ProxyDbBase): unrefs = ( (self.unreferenced_events, self.get_event_handles), (self.unreferenced_places, self.get_place_handles), + (self.unreferenced_citations, self.get_citation_handles), (self.unreferenced_sources, self.get_source_handles), (self.unreferenced_repositories, self.get_repository_handles), diff --git a/src/gen/proxy/referencedbyselection.py b/src/gen/proxy/referencedbyselection.py index 654e9d7c2..661a5c6ee 100644 --- a/src/gen/proxy/referencedbyselection.py +++ b/src/gen/proxy/referencedbyselection.py @@ -2,6 +2,7 @@ # Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 2010 Doug Blank +# 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 @@ -18,7 +19,6 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # -# gen/proxy/referencedbyselection.py # $Id$ """ @@ -84,6 +84,7 @@ class ReferencedBySelectionProxyDb(ProxyDbBase): "Event": set(), "Place": set(), "Source": set(), + "Citation": set(), "Repository": set(), "MediaObject": set(), "Note": set(), @@ -111,6 +112,10 @@ class ReferencedBySelectionProxyDb(ProxyDbBase): obj = self.db.get_source_from_handle(handle) if obj: self.process_source(obj) + elif class_name == "Citation": + obj = self.db.get_citation_from_handle(handle) + if obj: + self.process_citation(obj) elif class_name == "Repository": obj = self.db.get_repository_from_handle(handle) if obj: @@ -175,7 +180,7 @@ class ReferencedBySelectionProxyDb(ProxyDbBase): self.process_addresses(person) self.process_attributes(person) - self.process_source_ref_list(person) + self.process_citation_ref_list(person) self.process_urls(person) self.process_media_ref_list(person) self.process_lds_ords(person) @@ -199,7 +204,7 @@ class ReferencedBySelectionProxyDb(ProxyDbBase): continue self.process_object("Person", child_ref.ref) self.process_notes(child_ref) - self.process_source_ref_list(child_ref) + self.process_citation_ref_list(child_ref) for event_ref in family.get_event_ref_list(): if event_ref: @@ -207,7 +212,7 @@ class ReferencedBySelectionProxyDb(ProxyDbBase): if event: self.process_event_ref(event_ref) - self.process_source_ref_list(family) + self.process_citation_ref_list(family) self.process_notes(family) self.process_media_ref_list(family) self.process_attributes(family) @@ -221,7 +226,7 @@ class ReferencedBySelectionProxyDb(ProxyDbBase): if event is None or event.handle in self.referenced["Event"]: return self.referenced["Event"].add(event.handle) - self.process_source_ref_list(event) + self.process_citation_ref_list(event) self.process_notes(event) self.process_media_ref_list(event) self.process_attributes(event) @@ -239,7 +244,7 @@ class ReferencedBySelectionProxyDb(ProxyDbBase): if place is None or place.handle in self.referenced["Place"]: return self.referenced["Place"].add(place.handle) - self.process_source_ref_list(place) + self.process_citation_ref_list(place) self.process_notes(place) self.process_media_ref_list(place) self.process_urls(place) @@ -262,6 +267,22 @@ class ReferencedBySelectionProxyDb(ProxyDbBase): self.process_media_ref_list(source) self.process_notes(source) + def process_citation(self, citation): + """ + Follow the citation object and find all of the primary objects + that it references. + """ + if citation is None or citation.handle in self.referenced["Citation"]: + return + self.referenced["Citation"].add(citation.handle) + source_handle = citation.get_reference_handle() + if source_handle: + source = self.db.get_source_from_handle(source_handle) + if source: + self.process_source(source) + self.process_media_ref_list(citation) + self.process_notes(citation) + def process_repository(self, repository): """ Follow the repository object and find all of the primary objects @@ -282,7 +303,7 @@ class ReferencedBySelectionProxyDb(ProxyDbBase): if media is None or media.handle in self.referenced["MediaObject"]: return self.referenced["MediaObject"].add(media.handle) - self.process_source_ref_list(media) + self.process_citation_ref_list(media) self.process_attributes(media) self.process_notes(media) @@ -316,7 +337,7 @@ class ReferencedBySelectionProxyDb(ProxyDbBase): def process_name(self, name): """ Find all of the primary objects referred to """ - self.process_source_ref_list(name) + self.process_citation_ref_list(name) self.process_notes(name) def process_addresses(self, original_obj): @@ -327,7 +348,7 @@ class ReferencedBySelectionProxyDb(ProxyDbBase): def process_address(self, address): """ Find all of the primary objects referred to """ - self.process_source_ref_list(address) + self.process_citation_ref_list(address) self.process_notes(address) def process_attributes(self, original_obj): @@ -335,23 +356,15 @@ class ReferencedBySelectionProxyDb(ProxyDbBase): for attribute in original_obj.get_attribute_list(): if attribute: self.process_notes(attribute) - self.process_source_ref_list(attribute) + self.process_citation_ref_list(attribute) - def process_source_ref_list(self, original_obj): + def process_citation_ref_list(self, original_obj): """ Find all of the primary objects referred to """ - for ref in original_obj.get_source_references(): - if ref: - handle = ref.get_reference_handle() - source = self.db.get_source_from_handle(handle) - if source: - self.process_source_ref(ref) - - def process_source_ref(self, srcref): - """ Find all of the primary objects referred to """ - source = self.db.get_source_from_handle(srcref.ref) - if source: - self.process_source(source) - self.process_notes(srcref) + for handle in original_obj.get_citation_list(): + if handle: + citation = self.db.get_citation_from_handle(handle) + if citation: + self.process_citation(citation) def process_urls(self, original_obj): """ Find all of the primary objects referred to """ @@ -363,7 +376,7 @@ class ReferencedBySelectionProxyDb(ProxyDbBase): if media_ref: self.process_notes(media_ref) self.process_attributes(media_ref) - self.process_source_ref_list(media_ref) + self.process_citation_ref_list(media_ref) handle = media_ref.get_reference_handle() media_object = self.db.get_object_from_handle(handle) if media_object: @@ -387,14 +400,14 @@ class ReferencedBySelectionProxyDb(ProxyDbBase): if place: self.process_place(place) - self.process_source_ref_list(lds_ord) + self.process_citation_ref_list(lds_ord) self.process_notes(lds_ord) def process_associations(self, original_obj): """ Find all of the primary objects referred to """ for person_ref in original_obj.get_person_ref_list(): if person_ref: - self.process_source_ref_list(person_ref) + self.process_citation_ref_list(person_ref) self.process_notes(person_ref) person = self.db.get_person_from_handle(person_ref.ref) if person: @@ -446,6 +459,12 @@ class ReferencedBySelectionProxyDb(ProxyDbBase): """ return handle in self.referenced["Source"] + def include_citation(self, handle): + """ + Filter for citations + """ + return handle in self.referenced["Citation"] + def include_repository(self, handle): """ Filter for repositories diff --git a/src/gen/user.py b/src/gen/user.py index f1965bf49..1956ab9a4 100644 --- a/src/gen/user.py +++ b/src/gen/user.py @@ -17,7 +17,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # -# $Id$ +# $Id: user.py 18393 2011-10-31 16:46:50Z paul-franklin $ # """ diff --git a/src/gen/utils/callman.py b/src/gen/utils/callman.py index e595917f7..1c0deea8b 100644 --- a/src/gen/utils/callman.py +++ b/src/gen/utils/callman.py @@ -2,6 +2,7 @@ # Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 2009 Benny Malengier +# 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 @@ -44,6 +45,7 @@ EVENTKEY = 'event' PLACEKEY = 'place' MEDIAKEY = 'media' SOURCEKEY = 'source' +CITATIONKEY = 'citation' REPOKEY = 'repository' NOTEKEY = 'note' @@ -53,7 +55,7 @@ DELETE = '-delete' REBUILD = '-rebuild' KEYS = [PERSONKEY, FAMILYKEY, EVENTKEY, PLACEKEY, MEDIAKEY, SOURCEKEY, - REPOKEY, NOTEKEY] + CITATIONKEY, REPOKEY, NOTEKEY] METHODS = [ADD, UPDATE, DELETE, REBUILD] METHODS_LIST = [ADD, UPDATE, DELETE] @@ -65,6 +67,7 @@ EVENTCLASS = 'Event' PLACECLASS = 'Place' MEDIACLASS = 'MediaObject' SOURCECLASS = 'Source' +CITATIONCLASS = 'Citation' REPOCLASS = 'Repository' NOTECLASS = 'Note' @@ -75,6 +78,7 @@ CLASS2KEY = { PLACECLASS: PLACEKEY, MEDIACLASS: MEDIAKEY, SOURCECLASS: SOURCEKEY, + CITATIONCLASS: CITATIONKEY, REPOCLASS: REPOKEY, NOTECLASS: NOTEKEY } @@ -116,6 +120,7 @@ class CallbackManager(object): PLACEKEY: [], MEDIAKEY: [], SOURCEKEY: [], + CITATIONKEY: [], REPOKEY: [], NOTEKEY: [], } @@ -198,6 +203,7 @@ class CallbackManager(object): PLACEKEY: [], MEDIAKEY: [], SOURCEKEY: [], + CITATIONKEY: [], REPOKEY: [], NOTEKEY: [], } @@ -334,6 +340,7 @@ def directhandledict(baseobj): PLACEKEY: [], MEDIAKEY: [], SOURCEKEY: [], + CITATIONKEY: [], REPOKEY: [], NOTEKEY: [], } @@ -353,6 +360,7 @@ def handledict(baseobj): PLACEKEY: [], MEDIAKEY: [], SOURCEKEY: [], + CITATIONKEY: [], REPOKEY: [], NOTEKEY: [], } diff --git a/src/glade/Makefile.am b/src/glade/Makefile.am index c46c81aab..fd5942c31 100644 --- a/src/glade/Makefile.am +++ b/src/glade/Makefile.am @@ -21,6 +21,7 @@ dist_pkgdata_DATA = \ configure.glade \ dateedit.glade \ editsource.glade \ + editcitation.glade \ styleeditor.glade \ dbman.glade \ editurl.glade \ @@ -47,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/editcitation.glade b/src/glade/editcitation.glade new file mode 100644 index 000000000..c8ba67af9 --- /dev/null +++ b/src/glade/editcitation.glade @@ -0,0 +1,647 @@ + + + + + + + 600 + dialog + + + True + + + True + 0 + 6 + 3 + <b>Citation information</b> + True + True + center + + + False + False + 1 + + + + + True + True + 6 + + + True + 12 + 5 + 3 + 12 + 6 + + + True + 1 + _Date: + True + center + date_entry + + + GTK_FILL + + + + + + True + True + True + True + Invoke date editor + none + + + True + gramps-date + + + + + 2 + 3 + GTK_FILL + + + + + + True + True + Specific location within the information referenced. For a published work, this could include the volume of a multi-volume work and the page number(s). For a periodical, it could include volume, issue, and page numbers. For a newspaper, it could include a column number and page number. For an unpublished source, this could be a sheet number, page number, frame number, etc. A census record might have a line number or dwelling and family numbers in addition to the page number. + + + + 1 + 2 + 2 + 3 + + + + + + True + 1 + _Volume/Page: + True + center + volume + + + 2 + 3 + GTK_FILL + + + + + + True + 1 + Con_fidence: + True + center + confidence + + + 3 + 4 + GTK_FILL + + + + + + True + Conveys the submitter's quantitative evaluation of the credibility of a piece of information, based upon its supporting evidence. It is not intended to eliminate the receiver's need to evaluate the evidence for themselves. +Very Low =Unreliable evidence or estimated data +Low =Questionable reliability of evidence (interviews, census, oral genealogies, or potential for bias for example, an autobiography) +High =Secondary evidence, data officially recorded sometime after event +Very High =Direct and primary evidence used, or by dominance of the evidence + confidence_model + + + + 0 + + + + + 1 + 2 + 3 + 4 + GTK_FILL + + + + + + True + True + The date of the entry in the source you are referencing, e.g. the date a house was visited during a census, or the date an entry was made in a birth log/registry. + + + + 1 + 2 + + + + + + True + 12 + + + True + gtk-dialog-warning + 6 + + + False + False + 0 + + + + + 500 + True + 0 + 3 + <b>Note:</b> Any changes in the shared citation information will be reflected in the citation itself, for all items that reference the citation. + True + True + True + + + 1 + + + + + 3 + 4 + 5 + GTK_EXPAND | GTK_SHRINK | GTK_FILL + + + + + + True + 12 + + + True + True + A unique ID to identify the citation + + + + 0 + + + + + True + True + True + none + + + True + gramps-unlock + + + + + False + False + 1 + + + + + 1 + 2 + 1 + 2 + GTK_FILL + + + + + + True + 1 + ID: + True + gid + + + 1 + 2 + GTK_FILL + + + + + + + + + + + + + + + + + True + <b>General</b> + True + + + False + + + + + 2 + + + + + True + True + 6 + True + 6 + + + True + True + False + + + True + 12 + 6 + 2 + 12 + 6 + + + True + 0 + _Title: + True + center + title + + + GTK_FILL + + + + + + True + 0 + _Author: + True + center + author + + + 1 + 2 + GTK_FILL + + + + + + True + 0 + 3 + A_bbreviation: + True + center + abbrev + + + 3 + 4 + GTK_FILL + + + + + + True + 0 + _Pub. Info.: + True + center + pub_info + + + 4 + 5 + GTK_FILL + + + + + + True + True + Authors of the source. + + + + 1 + 2 + 1 + 2 + + + + + + 6 + 12 + + + True + gtk-dialog-warning + 6 + + + False + False + 0 + + + + + 500 + True + 0 + 3 + <b>Note:</b> Any changes in the shared source information will be reflected in the source itself, for all items that reference the source. + True + True + True + + + 1 + + + + + 2 + 5 + 6 + GTK_EXPAND | GTK_SHRINK | GTK_FILL + + + + + + True + True + Provide a short title used for sorting, filing, and retrieving source records. + + + + 1 + 2 + 3 + 4 + + + + + + True + True + Publication Information, such as city and year of publication, name of publisher, ... + + + + 1 + 2 + 4 + 5 + + + + + + True + 0 + _ID: + True + gid + + + 2 + 3 + GTK_FILL + + + + + + True + 12 + + + True + True + A unique ID to identify the source + + + + 0 + + + + + True + True + True + none + + + True + gramps-unlock + + + + + False + False + 1 + + + + + 1 + 2 + 2 + 3 + GTK_FILL + + + + + + True + True + Title of the source. + + + + 1 + 2 + + + + + + False + + + + + True + + + gtk-file + 1 + + + 0 + + + + + True + <b>General</b> + True + center + + + False + False + 1 + + + + + False + + + + + + + True + <b>Shared source information</b> + True + + + + + 3 + + + + + True + end + + + gtk-help + True + True + True + True + True + + + False + False + 0 + + + + + gtk-cancel + True + True + True + True + True + + + False + False + 1 + + + + + gtk-ok + True + True + True + True + True + + + False + False + 2 + + + + + False + end + 0 + + + + + + help + cancel + ok + + + + + + + + + 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/Makefile.am b/src/gui/editors/Makefile.am index 9dfb1d2dc..fb3175a3f 100644 --- a/src/gui/editors/Makefile.am +++ b/src/gui/editors/Makefile.am @@ -14,6 +14,7 @@ pkgdata_PYTHON = \ editaddress.py \ editattribute.py \ editchildref.py \ + editcitation.py \ editevent.py \ editeventref.py \ editfamily.py \ @@ -33,7 +34,6 @@ pkgdata_PYTHON = \ editreporef.py \ editsecondary.py \ editsource.py \ - editsourceref.py \ editurl.py \ objectentries.py diff --git a/src/gui/editors/__init__.py b/src/gui/editors/__init__.py index 4d1a88547..475671cd5 100644 --- a/src/gui/editors/__init__.py +++ b/src/gui/editors/__init__.py @@ -2,6 +2,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 # it under the terms of the GNU General Public License as published by @@ -24,6 +25,7 @@ from editaddress import EditAddress from editattribute import EditAttribute, EditFamilyAttribute from editchildref import EditChildRef +from editcitation import EditCitation, DeleteCitationQuery from editevent import EditEvent, DeleteEventQuery from editeventref import EditEventRef, EditFamilyEventRef from editfamily import EditFamily @@ -39,7 +41,6 @@ from editplace import EditPlace, DeletePlaceQuery from editrepository import EditRepository, DeleteRepositoryQuery from editreporef import EditRepoRef from editsource import EditSource, DeleteSrcQuery -from editsourceref import EditSourceRef from editurl import EditUrl from editlink import EditLink @@ -50,6 +51,7 @@ EDITORS = { 'Family': EditFamily, 'Media': EditMedia, 'Source': EditSource, + 'Citation': EditCitation, 'Place': EditPlace, 'Repository': EditRepository, 'Note': EditNote, diff --git a/src/gui/editors/displaytabs/Makefile.am b/src/gui/editors/displaytabs/Makefile.am index 615a008b1..c71d81eab 100644 --- a/src/gui/editors/displaytabs/Makefile.am +++ b/src/gui/editors/displaytabs/Makefile.am @@ -11,6 +11,9 @@ pkgdata_PYTHON = \ backrefmodel.py \ buttontab.py \ childmodel.py \ + citationbackreflist.py \ + citationembedlist.py \ + citationrefmodel.py \ dataembedlist.py \ datamodel.py \ embeddedlist.py \ @@ -40,8 +43,6 @@ pkgdata_PYTHON = \ repoembedlist.py \ reporefmodel.py \ sourcebackreflist.py \ - sourceembedlist.py \ - sourcerefmodel.py \ surnamemodel.py \ surnametab.py \ webembedlist.py \ diff --git a/src/gui/editors/displaytabs/__init__.py b/src/gui/editors/displaytabs/__init__.py index 119114911..9cfe0b3e2 100644 --- a/src/gui/editors/displaytabs/__init__.py +++ b/src/gui/editors/displaytabs/__init__.py @@ -2,6 +2,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 # it under the terms of the GNU General Public License as published by @@ -50,6 +51,8 @@ from mediabackreflist import MediaBackRefList from nameembedlist import NameEmbedList from notebackreflist import NoteBackRefList from notetab import NoteTab +from citationbackreflist import CitationBackRefList +from citationembedlist import CitationEmbedList from personeventembedlist import PersonEventEmbedList from personrefembedlist import PersonRefEmbedList from personbackreflist import PersonBackRefList @@ -57,5 +60,4 @@ from placebackreflist import PlaceBackRefList from repoembedlist import RepoEmbedList from surnametab import SurnameTab from sourcebackreflist import SourceBackRefList -from sourceembedlist import SourceEmbedList from webembedlist import WebEmbedList 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/backrefmodel.py b/src/gui/editors/displaytabs/backrefmodel.py index 0efd1b77e..8adc510d7 100644 --- a/src/gui/editors/displaytabs/backrefmodel.py +++ b/src/gui/editors/displaytabs/backrefmodel.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 @@ -91,6 +92,13 @@ class BackRefModel(gtk.ListStore): gid = p.gramps_id handle = p.handle name = p.get_title() + elif dtype == 'Citation': + p = self.db.get_citation_from_handle(ref[1]) + if not p: + continue + gid = p.gramps_id + handle = p.handle + name = p.get_page() elif dtype == 'Event': p = self.db.get_event_from_handle(ref[1]) if not p: diff --git a/src/gui/editors/displaytabs/citationbackreflist.py b/src/gui/editors/displaytabs/citationbackreflist.py new file mode 100644 index 000000000..738d7348d --- /dev/null +++ b/src/gui/editors/displaytabs/citationbackreflist.py @@ -0,0 +1,39 @@ +# +# 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 +# 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$ + +#------------------------------------------------------------------------- +# +# GRAMPS classes +# +#------------------------------------------------------------------------- +from backrefmodel import BackRefModel +from backreflist import BackRefList + +class CitationBackRefList(BackRefList): + + def __init__(self, dbstate, uistate, track, obj, callback=None): + BackRefList.__init__(self, dbstate, uistate, track, obj, + BackRefModel, callback=callback) + + def get_icon_name(self): + return 'gramps-citation' diff --git a/src/gui/editors/displaytabs/citationembedlist.py b/src/gui/editors/displaytabs/citationembedlist.py new file mode 100644 index 000000000..7668251a7 --- /dev/null +++ b/src/gui/editors/displaytabs/citationembedlist.py @@ -0,0 +1,275 @@ +# +# 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 +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +# $Id$ + +#------------------------------------------------------------------------- +# +# Python classes +# +#------------------------------------------------------------------------- +from gen.ggettext import gettext as _ +import logging +LOG = logging.getLogger(".citation") + +#------------------------------------------------------------------------- +# +# GTK/Gnome modules +# +#------------------------------------------------------------------------- + +#------------------------------------------------------------------------- +# +# GRAMPS classes +# +#------------------------------------------------------------------------- +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 + +#------------------------------------------------------------------------- +# +# CitationEmbedList +# +#------------------------------------------------------------------------- +class CitationEmbedList(EmbeddedList, DbGUIElement): + """ + Citation List display tab for edit dialogs. + + Derives from the EmbeddedList class. + """ + + _HANDLE_COL = 4 # Column number from CitationRefModel + _DND_TYPE = DdTargets.CITATION_LINK + _DND_EXTRA = DdTargets.SOURCE_LINK + + _MSG = { + '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 or source'), + 'up' : _('Move the selected citation upwards'), + 'down' : _('Move the selected citation downwards'), + } + + #index = column in model. Value = + # (name, sortcol in model, width, markup/text, weigth_col + _column_names = [ + (_('Title'), 0, 200, 0, -1), + (_('Author'), 1, 125, 0, -1), + (_('Page'), 2, 100, 0, -1), + (_('ID'), 3, 75, 0, -1), + ] + + def __init__(self, dbstate, uistate, track, data, callertitle=None): + self.data = data + self.callertitle = callertitle + EmbeddedList.__init__(self, dbstate, uistate, track, + _("_Source Citations"), CitationRefModel, + share_button=True, move_buttons=True) + DbGUIElement.__init__(self, dbstate.db) + self.callman.register_handles({'citation': self.data}) + + def _connect_db_signals(self): + """ + Implement base class DbGUIElement method + """ + #citation: citation-rebuild closes the editors, so no need to connect + # to it + self.callman.register_callbacks( + {'citation-delete': self.citation_delete, + 'citation-update': self.citation_update, + }) + self.callman.connect_all(keys=['citation']) + + def get_icon_name(self): + """ + Return the stock-id icon name associated with the display tab + """ + return 'gramps-source' + + def get_data(self): + """ + Return the data associated with display tab + """ + return self.data + + def column_order(self): + """ + Return the column order of the columns in the display tab. + """ + return ((1, 0), (1, 1), (1, 2), (1, 3)) + + def add_button_clicked(self, obj): + """ + Create a new Citation instance and call the EditCitation editor with + the new citation. + + 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. + """ + try: + from gui.editors import EditCitation + EditCitation(self.dbstate, self.uistate, self.track, + gen.lib.Citation(), gen.lib.Source(), + self.add_callback, self.callertitle) + except Errors.WindowActiveError: + pass + + def add_callback(self, value): + """ + Called to update the screen when a new citation is added + """ + self.get_data().append(value) + self.callman.register_handles({'citation': [value]}) + self.changed = True + self.rebuild() + + def share_button_clicked(self, obj): + SelectCitation = SelectorFactory('Citation') + + sel = SelectCitation(self.dbstate, self.uistate, self.track) + 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 __blocked_text(self): + """ + Return the common text used when citation cannot be edited + """ + return _("This citation cannot be created at this time. " + "Either the associated Source object is already being " + "edited, or another citation associated with the same " + "source is being edited.\n\nTo edit this " + "citation, you need to close the object.") + + def edit_button_clicked(self, obj): + """ + Get the selected Citation instance and call the EditCitation editor + with the citation. + + Called when the Edit 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. + """ + handle = self.get_selected() + if handle: + citation = self.dbstate.db.get_citation_from_handle(handle) + try: + from gui.editors import EditCitation + EditCitation(self.dbstate, self.uistate, self.track, citation, + callertitle = self.callertitle) + except Errors.WindowActiveError: + pass + + def citation_delete(self, del_citation_handle_list): + """ + Outside of this tab citation objects have been deleted. Check if tab + and object must be changed. + Note: delete of object will cause reference on database to be removed, + so this method need not do this + """ + rebuild = False + for handle in del_citation_handle_list : + while self.data.count(handle) > 0: + self.data.remove(handle) + rebuild = True + if rebuild: + self.rebuild() + + def citation_update(self, upd_citation_handle_list): + """ + Outside of this tab citation objects have been updated. Check if tab + and object must be updated. + """ + for handle in upd_citation_handle_list : + if handle in self.data: + self.rebuild() + break + + def _handle_drag(self, row, handle): + """ + A CITATION_LINK has been dragged + """ + if handle: + object = self.dbstate.db.get_citation_from_handle(handle) + if 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 handle_extra_type(self, objtype, handle): + """ + A SOURCE_LINK object has been dragged + """ + if handle: + object = self.dbstate.db.get_source_from_handle(handle) + 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()) + else: + raise ValueError("selection must be either source or citation") diff --git a/src/gui/editors/displaytabs/sourcerefmodel.py b/src/gui/editors/displaytabs/citationrefmodel.py similarity index 71% rename from src/gui/editors/displaytabs/sourcerefmodel.py rename to src/gui/editors/displaytabs/citationrefmodel.py index 1360d7e5f..d7fa1b941 100644 --- a/src/gui/editors/displaytabs/sourcerefmodel.py +++ b/src/gui/editors/displaytabs/citationrefmodel.py @@ -2,6 +2,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 # it under the terms of the GNU General Public License as published by @@ -29,22 +30,16 @@ import gtk #------------------------------------------------------------------------- # -# GRAMPS classes +# CitationModel # #------------------------------------------------------------------------- +class CitationRefModel(gtk.ListStore): - -#------------------------------------------------------------------------- -# -# SourceRefModel -# -#------------------------------------------------------------------------- -class SourceRefModel(gtk.ListStore): - - def __init__(self, sref_list, db): + def __init__(self, citation_list, db): gtk.ListStore.__init__(self, str, str, str, str, object) self.db = db - for sref in sref_list: - src = self.db.get_source_from_handle(sref.get_reference_handle()) - self.append(row=[src.gramps_id, src.title, src.author, - sref.page, sref, ]) + for handle in citation_list: + citation = self.db.get_citation_from_handle(handle) + src = self.db.get_source_from_handle(citation.get_reference_handle()) + self.append(row=[src.title, src.author, citation.page, + citation.gramps_id, handle, ]) diff --git a/src/gui/editors/displaytabs/namemodel.py b/src/gui/editors/displaytabs/namemodel.py index b67e08ed8..f5fd11f24 100644 --- a/src/gui/editors/displaytabs/namemodel.py +++ b/src/gui/editors/displaytabs/namemodel.py @@ -3,6 +3,7 @@ # # Copyright (C) 2000-2007 Donald N. Allingham # 2009 Benny Malengier +# 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 @@ -129,7 +130,7 @@ class NameModel(gtk.TreeStore): row=self.row(self.DEFINDEX, defname)) def hassource(self, name): - if len(name.get_source_references()): + if len(name.get_citation_list()): return YES return NO diff --git a/src/gui/editors/displaytabs/sourcebackreflist.py b/src/gui/editors/displaytabs/sourcebackreflist.py index 7dccef876..3e040b265 100644 --- a/src/gui/editors/displaytabs/sourcebackreflist.py +++ b/src/gui/editors/displaytabs/sourcebackreflist.py @@ -2,6 +2,7 @@ # Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 2000-2006 Donald N. Allingham +# Copyright (C) 2-11 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/gui/editors/displaytabs/sourceembedlist.py b/src/gui/editors/displaytabs/sourceembedlist.py deleted file mode 100644 index 2c327b97b..000000000 --- a/src/gui/editors/displaytabs/sourceembedlist.py +++ /dev/null @@ -1,230 +0,0 @@ -# -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2000-2006 Donald N. Allingham -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -# - -# $Id$ - -#------------------------------------------------------------------------- -# -# Python classes -# -#------------------------------------------------------------------------- -from gen.ggettext import gettext as _ - -#------------------------------------------------------------------------- -# -# GRAMPS classes -# -#------------------------------------------------------------------------- -import gen.lib -from gui.dbguielement import DbGUIElement -from gui.selectors import SelectorFactory -import Errors -from DdTargets import DdTargets -from sourcerefmodel import SourceRefModel -from embeddedlist import EmbeddedList - -#------------------------------------------------------------------------- -# -# SourceEmbedList -# -#------------------------------------------------------------------------- -class SourceEmbedList(EmbeddedList, DbGUIElement): - - _HANDLE_COL = 4 - _DND_TYPE = DdTargets.SOURCEREF - _DND_EXTRA = DdTargets.SOURCE_LINK - - _MSG = { - 'add' : _('Create and add a new source'), - 'del' : _('Remove the existing source'), - 'edit' : _('Edit the selected source'), - 'share' : _('Add an existing source'), - 'up' : _('Move the selected source upwards'), - 'down' : _('Move the selected source downwards'), - } - - #index = column in model. Value = - # (name, sortcol in model, width, markup/text, weigth_col - _column_names = [ - (_('ID'), 0, 75, 0, -1), - (_('Title'), 1, 200, 0, -1), - (_('Author'), 2, 125, 0, -1), - (_('Page'), 3, 100, 0, -1), - ] - - def __init__(self, dbstate, uistate, track, obj): - self.obj = obj - EmbeddedList.__init__(self, dbstate, uistate, track, _('_Sources'), - SourceRefModel, share_button=True, - move_buttons=True) - DbGUIElement.__init__(self, dbstate.db) - self.callman.register_handles({'source': [sref.ref for sref - in self.obj.get_source_references()]}) - - def _connect_db_signals(self): - """ - Implement base class DbGUIElement method - """ - #note: source-rebuild closes the editors, so no need to connect to it - self.callman.register_callbacks( - {'source-delete': self.source_delete, # delete a source we track - 'source-update': self.source_update, # change a source we track - }) - self.callman.connect_all(keys=['source']) - - def get_icon_name(self): - return 'gramps-source' - - def get_data(self): - return self.obj.get_source_references() - - def column_order(self): - return ((1, 0), (1, 1), (1, 2), (1, 3)) - - def add_button_clicked(self, obj): - from gui.editors import EditSourceRef - try: - sref = gen.lib.SourceRef() - src = gen.lib.Source() - EditSourceRef( - self.dbstate, - self.uistate, - self.track, - src, - sref, - self.object_added) - except Errors.WindowActiveError: - pass - - def __blocked_text(self): - """ - Return the common text used when sourceref cannot be edited - """ - return _("This source reference cannot be edited at this time. " - "Either the associated source is already being edited " - "or another source reference that is associated with " - "the same source is being edited.\n\nTo edit this " - "source reference, you need to close the source.") - - def share_button_clicked(self, obj): - from gui.editors import EditSourceRef - SelectSource = SelectorFactory('Source') - - sel = SelectSource(self.dbstate,self.uistate,self.track) - src = sel.run() - if src: - try: - ref = gen.lib.SourceRef() - EditSourceRef(self.dbstate, - self.uistate, - self.track, - src, - ref, - self.object_added) - - except Errors.WindowActiveError: - from QuestionDialog import WarningDialog - WarningDialog(_("Cannot share this reference"), - self.__blocked_text()) - - def edit_button_clicked(self, obj): - from gui.editors import EditSourceRef - sref = self.get_selected() - if sref: - src = self.dbstate.db.get_source_from_handle(sref.ref) - - try: - EditSourceRef(self.dbstate, self.uistate, self.track, - src, sref, self.object_edited) - except Errors.WindowActiveError: - from QuestionDialog import WarningDialog - WarningDialog(_("Cannot edit this reference"), - self.__blocked_text()) - - def object_added(self, reference, primary): - """ - Callback from sourceref editor after adding a new reference (to a new - or an existing source). - Note that if it was to an existing source already present in the - sourcelist, then the source-update signal will also cause a rebuild - at that time. - """ - reference.ref = primary.handle - self.get_data().append(reference) - self.callman.register_handles({'source': [primary.handle]}) - self.changed = True - self.rebuild() - - def object_edited(self, refererence, primary): - """ - Callback from sourceref editor. If the source changes itself, also - the source-change signal will cause a rebuild. - This could be solved in the source editor if it only calls this - method in the case the sourceref part only changes. - """ - self.changed = True - self.rebuild() - - def handle_extra_type(self, objtype, obj): - from gui.editors import EditSourceRef - sref = gen.lib.SourceRef() - src = self.dbstate.db.get_source_from_handle(obj) - try: - EditSourceRef(self.dbstate, self.uistate, self.track, - src, sref, self.object_added) - except Errors.WindowActiveError: - pass - - def source_delete(self, del_src_handle_list): - """ - Outside of this tab source objects have been deleted. Check if tab - and object must be changed. - Note: delete of object will cause reference on database to be removed, - so this method need not do this - """ - rebuild = False - sourceref_list = self.get_data() - ref_handles = [sref.ref for sref in sourceref_list] - for handle in del_src_handle_list : - while 1: - pos = None - try : - pos = ref_handles.index(handle) - except ValueError : - break - - if pos is not None: - #oeps, we need to remove this reference, and rebuild tab - del sourceref_list[pos] - del ref_handles[pos] - rebuild = True - if rebuild: - self.rebuild() - - def source_update(self, upd_src_handle_list): - """ - Outside of this tab media objects have been changed. Check if tab - and object must be changed. - """ - ref_handles = [sref.ref for sref in self.get_data()] - for handle in upd_src_handle_list : - if handle in ref_handles: - self.rebuild() - break 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/editattribute.py b/src/gui/editors/editattribute.py index cf022fd0f..02be2aeb1 100644 --- a/src/gui/editors/editattribute.py +++ b/src/gui/editors/editattribute.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 @@ -48,7 +49,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 MonitoredEntry, PrivacyButton, MonitoredDataType #------------------------------------------------------------------------- @@ -107,7 +108,10 @@ class EditAttribute(EditSecondary): def _create_tabbed_pages(self): 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 new file mode 100644 index 000000000..3f297f8ef --- /dev/null +++ b/src/gui/editors/editcitation.py @@ -0,0 +1,517 @@ +# +# 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 +# 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$ + +""" +EditCitation class for GRAMPS. +""" + +#------------------------------------------------------------------------- +# +# Python modules +# +#------------------------------------------------------------------------- +from gen.ggettext import gettext as _ +import logging +LOG = logging.getLogger(".citation") + +#------------------------------------------------------------------------- +# +# gramps modules +# +#------------------------------------------------------------------------- +import gen.lib +from gen.db import DbTxn +from editprimary import EditPrimary + +from displaytabs import (NoteTab, GalleryTab, DataEmbedList, + SourceBackRefList, RepoEmbedList, CitationBackRefList) +from gui.widgets import (MonitoredEntry, PrivacyButton, MonitoredMenu, + MonitoredDate) +from QuestionDialog import ErrorDialog +from editreference import RefTab +from glade import Glade + +#------------------------------------------------------------------------- +# +# EditCitationclass +# +#------------------------------------------------------------------------- + +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): + """ + 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( + obj.get_reference_handle()) + self.source = source + self.callertitle = callertitle + 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 build_window_key(self, obj): + """ + Return a key for the edit window that is opened. + This function overrides the build_window_key in EditPrimary. + + There is a problem with database object locking. The database locking is + handled by the ManagedWindow class, which will only allow one primary + object to be edited at a time. + + Normally, the window key is derived from the obj that is being edited. + However, in the case of EditCitation, there are two objects being + edited, the Citation and the Source. Both must be protected against + against the user trying to edit them twice. + + What we do here is to derive the window key from the Source object, if + one exists. A Citation always points to exactly one Source object, so if + we try to edit the same Citation twice, the associated Source objects + will be the same so this will be prevented. If we try to edit a Source + object and a Citation object that refers to the same Source, then again, + the window key will be the same and this will be prevented. + """ + if obj and obj.get_reference_handle(): + # citation already points to source + return obj.get_reference_handle() + elif self.source and self.source.get_handle(): + # Citation doesn't yet point to source, but source exists and has a + # handle + return self.source.get_handle() + else: + return id(self) + + def empty_object(self): + """ + Return an empty Citation object for comparison for changes. + + It is used by the base class L{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: + title = _('Citation') + \ + (': %(id)s - %(context)s' % { + 'id' : title, + 'context' : self.callertitle + }) + else: + title = _('Citation') + ": " + title + else: + if self.callertitle: + title = _('New Citation') + \ + (': %(id)s - %(context)s' % { + 'id' : title, + 'context' : self.callertitle + }) + else: + title = _('New Citation') + return title + + # The functions define_warn_box, enable_warn_box and define_expander + # are normally inherited from editreference, + # but have to be defined here because this class inherits from + # EditPrimary instead + def define_warn_box(self, box): + self.warn_box = box + + def enable_warnbox(self): + self.warn_box.show() + + def define_warn_box2(self, box): + self.warn_box2 = box + + def enable_warnbox2(self): + self.warn_box2.show() + + def define_expander(self, expander): + expander.set_expanded(True) + + def _local_init(self): + """Local initialization function. + + Perform basic initialization, including setting up widgets + and the glade interface. It is called by the base class L{EditPrimary}, + and overridden here. + + """ + self.width_key = 'interface.citation-width' + self.height_key = 'interface.citation-height' + assert(self.obj) + + self.glade = Glade() + self.set_window(self.glade.toplevel, None, + self.get_menu_title()) + + self.define_warn_box(self.glade.get_object("warn_box")) + self.define_warn_box2(self.glade.get_object("warn_box2")) + self.define_expander(self.glade.get_object("src_expander")) + + tblref = self.glade.get_object('table67') + notebook = self.glade.get_object('notebook_ref') + #recreate start page as GrampsTab + notebook.remove_page(0) + self.reftab = RefTab(self.dbstate, self.uistate, self.track, + _('General'), tblref) + tblref = self.glade.get_object('table68') + notebook = self.glade.get_object('notebook_src') + #recreate start page as GrampsTab + notebook.remove_page(0) + self.primtab = RefTab(self.dbstate, self.uistate, self.track, + _('General'), tblref) + + + def _connect_signals(self): + """Connects any signals that need to be connected. + + Called by the init routine of the base class L{EditPrimary}. + """ + 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')) + + def _connect_db_signals(self): + """ + Connect any signals that need to be connected. + Called by the init routine of the base class (_EditPrimary). + + What this code does is to check that the object edited is not deleted + whilst editing it. If the object is deleted we need to close the editor + windows and clean up. If the database emits a rebuild signal for the + database object type we also abort the edit. + + The Citation editor edits two primary objects, and therefore we need to + check if either have been deleted. If the source is deleted, the + citation must have been deleted first and will emit a signal, so we + shouldn't have to connect to the source-delete signal. It should not be + necessary to connect to the source- rebuild signal for similar reasons. + """ + + self._add_db_signal('citation-rebuild', self._do_close) + self._add_db_signal('citation-delete', self.check_for_close) + + def _setup_fields(self): + """Get control widgets and attach them to Citation's attributes.""" + + # Populate the Citation section + + self.date = MonitoredDate( + self.glade.get_object("date_entry"), + self.glade.get_object("date_stat"), + self.obj.get_date_object(), + self.uistate, + self.track, + self.db.readonly) + + self.gid = MonitoredEntry( + self.glade.get_object('gid2'), self.obj.set_gramps_id, + self.obj.get_gramps_id,self.db.readonly) + + self.volume = MonitoredEntry( + self.glade.get_object("volume"), self.obj.set_page, + self.obj.get_page, self.db.readonly) + + self.type_mon = MonitoredMenu( + self.glade.get_object('confidence'), + self.obj.set_confidence_level, + self.obj.get_confidence_level, [ + (_('Very Low'), gen.lib.Citation.CONF_VERY_LOW), + (_('Low'), gen.lib.Citation.CONF_LOW), + (_('Normal'), gen.lib.Citation.CONF_NORMAL), + (_('High'), gen.lib.Citation.CONF_HIGH), + (_('Very High'), gen.lib.Citation.CONF_VERY_HIGH)], + self.db.readonly) + + self.ref_privacy = PrivacyButton( + self.glade.get_object('privacy'), self.obj, self.db.readonly) + + # Populate the Source section + + self.title = MonitoredEntry( + self.glade.get_object('title'), + self.source.set_title, + self.source.get_title, + self.db.readonly) + + self.author = MonitoredEntry( + self.glade.get_object('author'), self.source.set_author, + self.source.get_author,self.db.readonly) + + self.gid = MonitoredEntry( + self.glade.get_object('gid'), self.source.set_gramps_id, + self.source.get_gramps_id,self.db.readonly) + + self.source_privacy = PrivacyButton( + self.glade.get_object("private"), + self.source, self.db.readonly) + + self.abbrev = MonitoredEntry( + self.glade.get_object('abbrev'), self.source.set_abbreviation, + self.source.get_abbreviation,self.db.readonly) + + self.pubinfo = MonitoredEntry( + self.glade.get_object('pub_info'), self.source.set_publication_info, + self.source.get_publication_info,self.db.readonly) + + def _create_tabbed_pages(self): + """ + Create the notebook tabs and inserts them into the main + window. + """ + # create notebook tabs for Citation + + notebook_ref = self.glade.get_object('notebook_ref') + self._add_tab(notebook_ref, self.reftab) + + self.comment_tab = NoteTab(self.dbstate, self.uistate, self.track, + self.obj.get_note_list(), self.get_menu_title(), + notetype=gen.lib.NoteType.CITATION) + self._add_tab(notebook_ref, self.comment_tab) + self.track_ref_for_deletion("comment_tab") + + self.gallery_tab = GalleryTab(self.dbstate, self.uistate, self.track, + self.obj.get_media_list()) + self._add_tab(notebook_ref, self.gallery_tab) + self.track_ref_for_deletion("gallery_tab") + + self.data_tab = DataEmbedList(self.dbstate, self.uistate, self.track, + self.obj) + self._add_tab(notebook_ref, self.data_tab) + self.track_ref_for_deletion("data_tab") + + self.citationref_list = CitationBackRefList(self.dbstate, self.uistate, + self.track, + self.db.find_backlink_handles(self.obj.handle), + self.enable_warnbox2) + self._add_tab(notebook_ref, self.citationref_list) + self.track_ref_for_deletion("citationref_list") + + # Create notebook tabs for Source + + notebook_src = self.glade.get_object('notebook_src') + + self._add_tab(notebook_src, self.primtab) + + self.note_tab = NoteTab(self.dbstate, self.uistate, self.track, + self.source.get_note_list(), + self.get_menu_title(), + notetype=gen.lib.NoteType.SOURCE) + self._add_tab(notebook_src, self.note_tab) + self.track_ref_for_deletion("note_tab") + + self.gallery_tab = GalleryTab(self.dbstate, self.uistate, self.track, + self.source.get_media_list()) + self._add_tab(notebook_src, self.gallery_tab) + self.track_ref_for_deletion("gallery_tab") + + self.data_tab = DataEmbedList(self.dbstate, self.uistate, self.track, + self.source) + self._add_tab(notebook_src, self.data_tab) + self.track_ref_for_deletion("data_tab") + + self.repo_tab = RepoEmbedList(self.dbstate, self.uistate, self.track, + self.source.get_reporef_list()) + self._add_tab(notebook_src, self.repo_tab) + self.track_ref_for_deletion("repo_tab") + + self.srcref_list = SourceBackRefList(self.dbstate, self.uistate, + self.track, + self.db.find_backlink_handles(self.source.handle), + self.enable_warnbox) + self._add_tab(notebook_src, self.srcref_list) + self.track_ref_for_deletion("srcref_list") + + self._setup_notebook_tabs(notebook_src) + self._setup_notebook_tabs(notebook_ref) + + def build_menu_names(self, source): + """ + Provide the information needed by the base class to define the + window management menu entries. + """ + return (_('Edit Citation'), self.get_menu_title()) + + def save(self, *obj): + """Save the data.""" + self.ok_button.set_sensitive(False) + # FIXME: CITATION: It should be possible to save a citation even when + # there is no data in it. On the other hand, it should not be possible + # to save a source that has no data. + if self.object_is_empty(): + ErrorDialog(_("Cannot save citation"), + _("No data exists for this citation. Please " + "enter data or cancel the edit.")) + self.ok_button.set_sensitive(True) + return + + # FIXME: CITATION: Not only citations, but also sources should be + # checked for duplicate Gramps IDs. + (uses_dupe_id, gramps_id) = self._uses_duplicate_id() + if uses_dupe_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 %(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 + + with DbTxn('', self.db) as trans: + # First commit the Source Primary object + if not self.source.get_handle(): + self.db.add_source(self.source, trans) + 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.db.commit_source(self.source, trans) + msg = _("Edit Source (%s)") % self.source.get_title() + + self.obj.set_reference_handle(self.source.handle) + + # Now commit the Citation Primary object + if not self.obj.get_handle(): + self.db.add_citation(self.obj, trans) + 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.db.commit_citation(self.obj, trans) + msg += _("\n" + "Edit Citation (%s)") % self.obj.get_page() + trans.set_description(msg) + + if self.callback: + self.callback(self.obj.get_handle()) + self.close() + + def data_has_changed(self): + """ + A date comparison can fail incorrectly because we have made the + decision to store entered text in the date. However, there is no + entered date when importing from a XML file, so we can get an + incorrect fail. + """ + # FIXME: CITATION: This should check whether either the citation data or + # the source data has changed. + if self.db.readonly: + return False + elif self.obj.handle: + orig = self.get_from_handle(self.obj.handle) + if orig: + cmp_obj = orig + else: + cmp_obj = self.empty_object() + return cmp(cmp_obj.serialize(True)[1:], + self.obj.serialize(True)[1:]) != 0 + else: + cmp_obj = self.empty_object() + return cmp(cmp_obj.serialize(True)[1:], + self.obj.serialize()[1:]) != 0 + +class DeleteCitationQuery(object): + def __init__(self, dbstate, uistate, citation, the_lists): + self.citation = citation + self.db = dbstate.db + self.uistate = uistate + self.the_lists = the_lists + + def query_response(self): + with DbTxn(_("Delete Citation (%s)") % self.citation.get_page(), + self.db) as trans: + self.db.disable_signals() + + (person_list, family_list, event_list, place_list, source_list, + media_list, repo_list) = self.the_lists + + ctn_handle_list = [self.citation.get_handle()] + + for handle in person_list: + person = self.db.get_person_from_handle(handle) + person.remove_citation_references(ctn_handle_list) + 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) + 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) + 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) + 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) + 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) + 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) + self.db.commit_repository(repo, trans) + + self.db.enable_signals() + self.db.remove_citation(self.citation.get_handle(), trans) diff --git a/src/gui/editors/editevent.py b/src/gui/editors/editevent.py index 2a5486552..4d6fe9a9e 100644 --- a/src/gui/editors/editevent.py +++ b/src/gui/editors/editevent.py @@ -3,6 +3,7 @@ # # Copyright (C) 2000-2007 Donald N. Allingham # Copyright (C) 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 @@ -48,7 +49,7 @@ from editprimary import EditPrimary from objectentries import PlaceEntry from glade import Glade from QuestionDialog import ErrorDialog -from displaytabs import (SourceEmbedList, NoteTab, GalleryTab, +from displaytabs import (CitationEmbedList, NoteTab, GalleryTab, EventBackRefList, AttrEmbedList) from gui.widgets import (MonitoredEntry, PrivacyButton, MonitoredDataType, MonitoredDate) @@ -172,11 +173,12 @@ class EditEvent(EditPrimary): """ notebook = gtk.Notebook() - self.source_list = SourceEmbedList(self.dbstate, - self.uistate, - self.track, - self.obj) - self._add_tab(notebook, self.source_list) + self.citation_list = CitationEmbedList(self.dbstate, + self.uistate, + self.track, + self.obj.get_citation_list(), + self.get_menu_title()) + self._add_tab(notebook, self.citation_list) self.note_list = NoteTab(self.dbstate, self.uistate, @@ -210,7 +212,7 @@ class EditEvent(EditPrimary): notebook.show_all() self.top.get_object('vbox').pack_start(notebook, True) - self.track_ref_for_deletion("source_list") + self.track_ref_for_deletion("citation_list") self.track_ref_for_deletion("note_list") self.track_ref_for_deletion("gallery_list") self.track_ref_for_deletion("attr_list") diff --git a/src/gui/editors/editeventref.py b/src/gui/editors/editeventref.py index 9c07a76ef..6bf5c6bc6 100644 --- a/src/gui/editors/editeventref.py +++ b/src/gui/editors/editeventref.py @@ -4,6 +4,7 @@ # Copyright (C) 2000-2006 Donald N. Allingham # 2009 Gary Burton # 2011 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 @@ -37,7 +38,7 @@ from gen.ggettext import gettext as _ import gen.lib from gen.db import DbTxn from glade import Glade -from displaytabs import (SourceEmbedList, NoteTab, GalleryTab, +from displaytabs import (CitationEmbedList, NoteTab, GalleryTab, EventBackRefList, AttrEmbedList) from gui.widgets import (PrivacyButton, MonitoredEntry, MonitoredDate, MonitoredDataType) @@ -178,10 +179,10 @@ class EditEventRef(EditReference): self.track_ref_for_deletion("primtab") self.track_ref_for_deletion("reftab") - self.srcref_list = SourceEmbedList(self.dbstate, - self.uistate, - self.track, - self.source) + self.srcref_list = CitationEmbedList(self.dbstate, + self.uistate, + self.track, + self.source.get_citation_list()) self._add_tab(notebook, self.srcref_list) self.track_ref_for_deletion("srcref_list") diff --git a/src/gui/editors/editfamily.py b/src/gui/editors/editfamily.py index 616a0d598..ba9dafcde 100644 --- a/src/gui/editors/editfamily.py +++ b/src/gui/editors/editfamily.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 @@ -70,7 +71,7 @@ from glade import Glade from editprimary import EditPrimary from editchildref import EditChildRef from editperson import EditPerson -from displaytabs import (EmbeddedList, EventEmbedList, SourceEmbedList, +from displaytabs import (EmbeddedList, EventEmbedList, CitationEmbedList, FamilyAttrEmbedList, NoteTab, GalleryTab, FamilyLdsEmbedList, ChildModel) from gui.widgets import (PrivacyButton, MonitoredEntry, MonitoredDataType, @@ -618,12 +619,13 @@ class EditFamily(EditPrimary): self._add_tab(notebook, self.event_list) self.track_ref_for_deletion("event_list") - self.source_list = SourceEmbedList(self.dbstate, + self.citation_list = CitationEmbedList(self.dbstate, self.uistate, self.track, - self.obj) - self._add_tab(notebook, self.source_list) - self.track_ref_for_deletion("source_list") + self.obj.get_citation_list(), + self.get_menu_title()) + self._add_tab(notebook, self.citation_list) + self.track_ref_for_deletion("citation_list") self.attr_list = FamilyAttrEmbedList(self.dbstate, self.uistate, diff --git a/src/gui/editors/editldsord.py b/src/gui/editors/editldsord.py index 45f93ca84..3b16921d1 100644 --- a/src/gui/editors/editldsord.py +++ b/src/gui/editors/editldsord.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 @@ import LdsUtils from glade import Glade from editsecondary import EditSecondary from objectentries import PlaceEntry -from displaytabs import SourceEmbedList,NoteTab +from displaytabs import CitationEmbedList,NoteTab from gui.widgets import (PrivacyButton, MonitoredDate, MonitoredMenu, MonitoredStrMenu) from gui.selectors import SelectorFactory @@ -239,10 +240,11 @@ class EditLdsOrd(EditSecondary): def _create_tabbed_pages(self): notebook = gtk.Notebook() - self.srcref_list = SourceEmbedList(self.dbstate, self.uistate, - self.track, self.obj) - self._add_tab(notebook, self.srcref_list) - self.track_ref_for_deletion("srcref_list") + self.citation_list = CitationEmbedList(self.dbstate, self.uistate, + self.track, + self.obj.get_citation_list()) + self._add_tab(notebook, self.citation_list) + self.track_ref_for_deletion("citation_list") self.note_tab = NoteTab(self.dbstate, self.uistate, self.track, self.obj.get_note_list(), @@ -404,7 +406,9 @@ class EditFamilyLdsOrd(EditSecondary): def _create_tabbed_pages(self): 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/editmedia.py b/src/gui/editors/editmedia.py index 752b26a6d..830c86038 100644 --- a/src/gui/editors/editmedia.py +++ b/src/gui/editors/editmedia.py @@ -4,6 +4,7 @@ # Copyright (C) 2000-2006 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 @@ -50,7 +51,7 @@ import Utils from editprimary import EditPrimary from gui.widgets import (MonitoredDate, MonitoredEntry, PrivacyButton, MonitoredTagList) -from displaytabs import (SourceEmbedList, AttrEmbedList, NoteTab, +from displaytabs import (CitationEmbedList, AttrEmbedList, NoteTab, MediaBackRefList) from addmedia import AddMediaObject from QuestionDialog import ErrorDialog @@ -191,13 +192,14 @@ class EditMedia(EditPrimary): def _create_tabbed_pages(self): notebook = gtk.Notebook() - self.src_tab = SourceEmbedList(self.dbstate, - self.uistate, - self.track, - self.obj) - self._add_tab(notebook, self.src_tab) - self.track_ref_for_deletion("src_tab") - + self.citation_tab = CitationEmbedList(self.dbstate, + self.uistate, + self.track, + self.obj.get_citation_list(), + self.get_menu_title()) + self._add_tab(notebook, self.citation_tab) + self.track_ref_for_deletion("citation_tab") + self.attr_tab = AttrEmbedList(self.dbstate, self.uistate, self.track, @@ -340,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) @@ -377,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/editmediaref.py b/src/gui/editors/editmediaref.py index 0854f0da3..fabab1c12 100644 --- a/src/gui/editors/editmediaref.py +++ b/src/gui/editors/editmediaref.py @@ -5,6 +5,7 @@ # 2008-2009 Stephane Charette # 2009 Gary Burton # 2011 Robert Cheramy +# 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 @@ import Utils from gen.lib import NoteType from gen.db import DbTxn from glade import Glade -from displaytabs import (SourceEmbedList, AttrEmbedList, MediaBackRefList, +from displaytabs import (CitationEmbedList, AttrEmbedList, MediaBackRefList, NoteTab) from gui.widgets import (MonitoredSpinButton, MonitoredEntry, PrivacyButton, MonitoredDate, MonitoredTagList) @@ -607,8 +608,10 @@ class EditMediaRef(EditReference): self._add_tab(notebook_src, self.primtab) self._add_tab(notebook_ref, self.reftab) - self.srcref_list = SourceEmbedList(self.dbstate,self.uistate,self.track, - self.source_ref) + self.srcref_list = CitationEmbedList(self.dbstate, + self.uistate, + self.track, + self.source_ref.get_citation_list()) self._add_tab(notebook_ref, self.srcref_list) self.track_ref_for_deletion("srcref_list") @@ -630,8 +633,10 @@ class EditMediaRef(EditReference): self._add_tab(notebook_ref, self.note_ref_tab) self.track_ref_for_deletion("note_ref_tab") - self.src_srcref_list = SourceEmbedList(self.dbstate,self.uistate, - self.track, self.source) + self.src_srcref_list = CitationEmbedList(self.dbstate, + self.uistate, + self.track, + self.source.get_citation_list()) self._add_tab(notebook_src, self.src_srcref_list) self.track_ref_for_deletion("src_srcref_list") 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/editnote.py b/src/gui/editors/editnote.py index 0f0377b73..a22ce0e0e 100644 --- a/src/gui/editors/editnote.py +++ b/src/gui/editors/editnote.py @@ -5,6 +5,7 @@ # Copyright (C) 2009 Gary Burton # Copyright (C) 2009 Benny Malengier # 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 @@ -346,7 +347,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 +376,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/editors/editperson.py b/src/gui/editors/editperson.py index d568eb5d3..4d7a3fffa 100644 --- a/src/gui/editors/editperson.py +++ b/src/gui/editors/editperson.py @@ -4,6 +4,7 @@ # Copyright (C) 2000-2007 Donald N. Allingham # Copyright (C) 2009-2011 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 @@ -65,7 +66,7 @@ import config from QuestionDialog import ErrorDialog, ICON from Errors import ValidationError -from displaytabs import (PersonEventEmbedList, NameEmbedList, SourceEmbedList, +from displaytabs import (PersonEventEmbedList, NameEmbedList, CitationEmbedList, AttrEmbedList, AddrEmbedList, NoteTab, GalleryTab, WebEmbedList, PersonRefEmbedList, LdsEmbedList, PersonBackRefList, SurnameTab) @@ -443,10 +444,11 @@ class EditPerson(EditPrimary): self._add_tab(notebook, self.name_list) self.track_ref_for_deletion("name_list") - self.srcref_list = SourceEmbedList(self.dbstate, + self.srcref_list = CitationEmbedList(self.dbstate, self.uistate, self.track, - self.obj) + self.obj.get_citation_list(), + self.get_menu_title()) 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/gui/editors/editplace.py b/src/gui/editors/editplace.py index 44d3d0748..b004173af 100644 --- a/src/gui/editors/editplace.py +++ b/src/gui/editors/editplace.py @@ -4,6 +4,7 @@ # Copyright (C) 2000-2006 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 @@ -46,7 +47,7 @@ import gtk import gen.lib from gen.db import DbTxn from editprimary import EditPrimary -from displaytabs import (GrampsTab, LocationEmbedList, SourceEmbedList, +from displaytabs import (GrampsTab, LocationEmbedList, CitationEmbedList, GalleryTab, NoteTab, WebEmbedList, PlaceBackRefList) from gui.widgets import MonitoredEntry, PrivacyButton from Errors import ValidationError @@ -244,12 +245,13 @@ class EditPlace(EditPrimary): self._add_tab(notebook, self.loc_list) self.track_ref_for_deletion("loc_list") - self.srcref_list = SourceEmbedList(self.dbstate, - self.uistate, - self.track, - self.obj) - self._add_tab(notebook, self.srcref_list) - self.track_ref_for_deletion("srcref_list") + self.citation_list = CitationEmbedList(self.dbstate, + self.uistate, + self.track, + self.obj.get_citation_list(), + self.get_menu_title()) + self._add_tab(notebook, self.citation_list) + self.track_ref_for_deletion("citation_list") self.note_tab = NoteTab(self.dbstate, self.uistate, diff --git a/src/gui/editors/editsource.py b/src/gui/editors/editsource.py index 48d795c44..acc894812 100644 --- a/src/gui/editors/editsource.py +++ b/src/gui/editors/editsource.py @@ -3,6 +3,7 @@ # # Copyright (C) 2000-2006 Donald N. Allingham # Copyright (C) 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 @@ -29,6 +30,7 @@ from gen.ggettext import gettext as _ import logging log = logging.getLogger(".") +LOG = logging.getLogger(".citation") #------------------------------------------------------------------------- # @@ -47,7 +49,7 @@ from gen.db import DbTxn from editprimary import EditPrimary from displaytabs import (NoteTab, GalleryTab, DataEmbedList, - SourceBackRefList, RepoEmbedList) + CitationBackRefList, RepoEmbedList) from gui.widgets import MonitoredEntry, PrivacyButton from QuestionDialog import ErrorDialog from glade import Glade @@ -158,7 +160,7 @@ class EditSource(EditPrimary): self._add_tab(notebook, self.repo_tab) self.track_ref_for_deletion("repo_tab") - self.backref_list = SourceBackRefList(self.dbstate, + self.backref_list = CitationBackRefList(self.dbstate, self.uistate, self.track, self.db.find_backlink_handles(self.obj.handle)) @@ -220,46 +222,69 @@ class DeleteSrcQuery(object): with DbTxn(_("Delete Source (%s)") % self.source.get_title(), self.db) as trans: self.db.disable_signals() + + # we can have: + # object(CitationBase) -> Citation(source_handle) -> Source + # We first have to remove the CitationBase references to the + # Citation. Then we remove the Citations. (We don't need to + # remove the source_handle references to the Source, because we are + # removing the whole Citation). Then we can remove the Source - (person_list, family_list, event_list, place_list, source_list, - media_list, repo_list) = self.the_lists + (citation_list, citation_referents_list) = self.the_lists + # citation_list is a tuple of lists. Only the first, for Citations, + # exists. + citation_list = citation_list[0] - src_handle_list = [self.source.get_handle()] - - for handle in person_list: - person = self.db.get_person_from_handle(handle) - person.remove_source_references(src_handle_list) - self.db.commit_person(person, trans) - - for handle in family_list: - family = self.db.get_family_from_handle(handle) - family.remove_source_references(src_handle_list) - self.db.commit_family(family, trans) - - for handle in event_list: - event = self.db.get_event_from_handle(handle) - event.remove_source_references(src_handle_list) - self.db.commit_event(event, trans) - - for handle in place_list: - place = self.db.get_place_from_handle(handle) - place.remove_source_references(src_handle_list) - self.db.commit_place(place, trans) - - for handle in source_list: - source = self.db.get_source_from_handle(handle) - source.remove_source_references(src_handle_list) - self.db.commit_source(source, trans) - - for handle in media_list: - media = self.db.get_object_from_handle(handle) - media.remove_source_references(src_handle_list) - self.db.commit_media_object(media, trans) - - for handle in repo_list: - repo = self.db.get_repository_from_handle(handle) - repo.remove_source_references(src_handle_list) - self.db.commit_repository(repo, trans) + # (1) delete the references to the citation + for (citation_handle, refs) in citation_referents_list: + LOG.debug('delete citation %s references %s' % + (citation_handle, refs)) + (person_list, family_list, event_list, place_list, source_list, + media_list, repo_list) = refs + + ctn_handle_list = [citation_handle] + + for handle in person_list: + person = self.db.get_person_from_handle(handle) + person.remove_citation_references(ctn_handle_list) + 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) + 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) + 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) + 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) + 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) + 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) + self.db.commit_repository(repo, trans) + # (2) delete the actual citations + LOG.debug('remove the actual citations %s' % citation_list) + for citation_handle in citation_list: + LOG.debug("remove_citation %s" % citation_handle) + self.db.remove_citation(citation_handle, trans) + + # (3) delete the source self.db.enable_signals() self.db.remove_source(self.source.get_handle(), trans) diff --git a/src/gui/editors/editsourceref.py b/src/gui/editors/editsourceref.py deleted file mode 100644 index 855ffa6f5..000000000 --- a/src/gui/editors/editsourceref.py +++ /dev/null @@ -1,228 +0,0 @@ -# -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2000-2006 Donald N. Allingham -# 2009 Gary Burton -# 2011 Michiel D. Nauta / MathieuMD -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -# - -# $Id$ - -#------------------------------------------------------------------------- -# -# Python modules -# -#------------------------------------------------------------------------- -from gen.ggettext import gettext as _ - -#------------------------------------------------------------------------- -# -# gramps modules -# -#------------------------------------------------------------------------- -import gen.lib -from gen.db import DbTxn -from glade import Glade -from displaytabs import (NoteTab, GalleryTab, SourceBackRefList, - DataEmbedList, RepoEmbedList) -from gui.widgets import (PrivacyButton, MonitoredEntry, MonitoredMenu, - MonitoredDate) -from editreference import RefTab, EditReference - -#------------------------------------------------------------------------- -# -# EditSourceRef class -# -#------------------------------------------------------------------------- -class EditSourceRef(EditReference): - - def __init__(self, state, uistate, track, source, source_ref, update): - - EditReference.__init__(self, state, uistate, track, source, - source_ref, update) - - def _local_init(self): - self.width_key = 'interface.event-ref-width' - self.height_key = 'interface.event-ref-height' - - self.top = Glade() - - self.set_window(self.top.toplevel, - self.top.get_object('source_title'), - _('Source Reference Editor')) - - self.define_warn_box(self.top.get_object("warn_box")) - self.define_expander(self.top.get_object("src_expander")) - - tblref = self.top.get_object('table67') - notebook = self.top.get_object('notebook_ref') - #recreate start page as GrampsTab - notebook.remove_page(0) - self.reftab = RefTab(self.dbstate, self.uistate, self.track, - _('General'), tblref) - tblref = self.top.get_object('table68') - notebook = self.top.get_object('notebook_src') - #recreate start page as GrampsTab - notebook.remove_page(0) - self.primtab = RefTab(self.dbstate, self.uistate, self.track, - _('General'), tblref) - - def _post_init(self): - title = self.top.get_object('title') - volume = self.top.get_object('volume') - if not title.get_text_length(): - title.grab_focus(); - elif not volume.get_text_length(): - volume.grab_focus(); - - def _connect_signals(self): - self.define_ok_button(self.top.get_object('ok'),self.ok_clicked) - self.define_cancel_button(self.top.get_object('cancel')) - self.define_help_button(self.top.get_object("help")) - - def _connect_db_signals(self): - """ - Connect any signals that need to be connected. - Called by the init routine of the base class (_EditPrimary). - """ - self._add_db_signal('source-rebuild', self.close) - self._add_db_signal('source-delete', self.check_for_close) - #note: at the moment, a source cannot be updated while an editor with - # that source shown is open. So no need to connect to source-update - - def _setup_fields(self): - self.ref_privacy = PrivacyButton( - self.top.get_object('privacy'), self.source_ref, self.db.readonly) - - self.volume = MonitoredEntry( - self.top.get_object("volume"), self.source_ref.set_page, - self.source_ref.get_page, self.db.readonly) - - self.gid = MonitoredEntry( - self.top.get_object('gid'), self.source.set_gramps_id, - self.source.get_gramps_id,self.db.readonly) - - self.source_privacy = PrivacyButton( - self.top.get_object("private"), - self.source, self.db.readonly) - - self.title = MonitoredEntry( - self.top.get_object('title'), - self.source.set_title, - self.source.get_title, - self.db.readonly) - - self.abbrev = MonitoredEntry( - self.top.get_object('abbrev'), self.source.set_abbreviation, - self.source.get_abbreviation,self.db.readonly) - - self.author = MonitoredEntry( - self.top.get_object('author'), self.source.set_author, - self.source.get_author,self.db.readonly) - - self.pubinfo = MonitoredEntry( - self.top.get_object('pub_info'), self.source.set_publication_info, - self.source.get_publication_info,self.db.readonly) - - self.type_mon = MonitoredMenu( - self.top.get_object('confidence'), - self.source_ref.set_confidence_level, - self.source_ref.get_confidence_level, [ - (_('Very Low'), gen.lib.SourceRef.CONF_VERY_LOW), - (_('Low'), gen.lib.SourceRef.CONF_LOW), - (_('Normal'), gen.lib.SourceRef.CONF_NORMAL), - (_('High'), gen.lib.SourceRef.CONF_HIGH), - (_('Very High'), gen.lib.SourceRef.CONF_VERY_HIGH)], - self.db.readonly) - - self.date = MonitoredDate( - self.top.get_object("date_entry"), - self.top.get_object("date_stat"), - self.source_ref.get_date_object(), - self.uistate, - self.track, - self.db.readonly) - - def _create_tabbed_pages(self): - """ - Create the notebook tabs and inserts them into the main - window. - """ - notebook_src = self.top.get_object('notebook_src') - notebook_ref = self.top.get_object('notebook_ref') - - self._add_tab(notebook_src, self.primtab) - self._add_tab(notebook_ref, self.reftab) - - self.note_tab = NoteTab(self.dbstate, self.uistate, self.track, - self.source.get_note_list(), - notetype=gen.lib.NoteType.SOURCE) - self._add_tab(notebook_src, self.note_tab) - self.track_ref_for_deletion("note_tab") - - self.gallery_tab = GalleryTab(self.dbstate, self.uistate, self.track, - self.source.get_media_list()) - self._add_tab(notebook_src, self.gallery_tab) - self.track_ref_for_deletion("gallery_tab") - - self.data_tab = DataEmbedList(self.dbstate, self.uistate, self.track, - self.source) - self._add_tab(notebook_src, self.data_tab) - self.track_ref_for_deletion("data_tab") - - self.repo_tab = RepoEmbedList(self.dbstate, self.uistate, self.track, - self.source.get_reporef_list()) - self._add_tab(notebook_src, self.repo_tab) - self.track_ref_for_deletion("repo_tab") - - self.srcref_list = SourceBackRefList(self.dbstate,self.uistate, self.track, - self.db.find_backlink_handles(self.source.handle), - self.enable_warnbox - ) - self._add_tab(notebook_src, self.srcref_list) - self.track_ref_for_deletion("srcref_list") - - self.comment_tab = NoteTab(self.dbstate, self.uistate, self.track, - self.source_ref.get_note_list(), - notetype=gen.lib.NoteType.SOURCEREF) - self._add_tab(notebook_ref, self.comment_tab) - self.track_ref_for_deletion("comment_tab") - - self._setup_notebook_tabs( notebook_src) - self._setup_notebook_tabs( notebook_ref) - - def build_menu_names(self,sourceref): - if self.source: - source_name = self.source.get_title() - submenu_label = _('Source: %s') % source_name - else: - submenu_label = _('New Source') - return (_('Source Reference Editor'),submenu_label) - - def ok_clicked(self, obj): - - if self.source.handle: - with DbTxn(_("Modify Source"), self.db) as trans: - self.db.commit_source(self.source,trans) - else: - with DbTxn(_("Add Source"), self.db) as trans: - self.db.add_source(self.source,trans) - - if self.update: - self.update(self.source_ref,self.source) - - self.close() diff --git a/src/gui/filtereditor.py b/src/gui/filtereditor.py index 3ee634a2a..95340d8a1 100644 --- a/src/gui/filtereditor.py +++ b/src/gui/filtereditor.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 @@ -85,6 +86,7 @@ _TITLES = { 'MediaObject' : _('Media Object Filters'), 'Repository' : _('Repository Filters'), 'Note' : _('Note Filters'), + 'Citation' : _('Citation Filters'), } _name2typeclass = { @@ -293,6 +295,7 @@ class MyID(gtk.HBox): 'MediaObject' : _('Media Object'), 'Repository' : _('Repository'), 'Note' : _('Note'), + 'Citation' : _('Citation'), } def __init__(self, dbstate, uistate, track, namespace='Person'): @@ -345,6 +348,9 @@ class MyID(gtk.HBox): elif self.namespace == 'Source': source = self.db.get_source_from_gramps_id(gramps_id) name = source.get_title() + elif self.namespace == 'Citation': + citation = self.db.get_citation_from_gramps_id(gramps_id) + name = citation.get_page() elif self.namespace == 'MediaObject': obj = self.db.get_object_from_gramps_id(gramps_id) name = obj.get_path() @@ -461,6 +467,8 @@ class EditRule(ManagedWindow.ManagedWindow): class_list = Rules.Event.editor_rule_list elif self.namespace == 'Source': class_list = Rules.Source.editor_rule_list + elif self.namespace == 'Citation': + class_list = Rules.Citation.editor_rule_list elif self.namespace == 'Place': class_list = Rules.Place.editor_rule_list elif self.namespace == 'MediaObject': @@ -915,6 +923,10 @@ class ShowResults(ManagedWindow.ManagedWindow): source = self.db.get_source_from_handle(handle) name = source.get_title() gid = source.get_gramps_id() + elif self.namespace == 'Citation': + citation = self.db.get_citation_from_handle(handle) + name = citation.get_title() + gid = citation.get_gramps_id() elif self.namespace == 'Place': place = self.db.get_place_from_handle(handle) name = place.get_title() @@ -946,6 +958,8 @@ class ShowResults(ManagedWindow.ManagedWindow): sortname = self.db.get_event_from_handle(handle).get_description() elif self.namespace == 'Source': sortname = self.db.get_source_from_handle(handle).get_title() + elif self.namespace == 'Citation': + sortname = self.db.get_citation_from_handle(handle).get_title() elif self.namespace == 'Place': sortname = self.db.get_place_from_handle(handle).get_title() elif self.namespace == 'MediaObject': @@ -1133,6 +1147,8 @@ class FilterEditor(ManagedWindow.ManagedWindow): return self.db.get_event_handles() elif self.namespace == 'Source': return self.db.get_source_handles() + elif self.namespace == 'Citation': + return self.db.get_citation_handles() elif self.namespace == 'Place': return self.db.iter_place_handles() elif self.namespace == 'MediaObject': diff --git a/src/gui/grampsgui.py b/src/gui/grampsgui.py index 144117091..93214157a 100644 --- a/src/gui/grampsgui.py +++ b/src/gui/grampsgui.py @@ -151,6 +151,7 @@ def register_stock_icons (): ('gramps-zoom-out', _('Zoom Out'), gtk.gdk.CONTROL_MASK, 0, ''), ('gramps-zoom-fit-width', _('Fit Width'), gtk.gdk.CONTROL_MASK, 0, ''), ('gramps-zoom-best-fit', _('Fit Page'), gtk.gdk.CONTROL_MASK, 0, ''), + ('gramps-citation', _('Citations'), gtk.gdk.CONTROL_MASK, 0, ''), ] # the following icons are not yet in new directory structure # they should be ported in the near future diff --git a/src/gui/selectors/Makefile.am b/src/gui/selectors/Makefile.am index ef318b790..0c5c666ff 100644 --- a/src/gui/selectors/Makefile.am +++ b/src/gui/selectors/Makefile.am @@ -8,6 +8,7 @@ pkgdatadir = $(datadir)/@PACKAGE@/gui/selectors pkgdata_PYTHON = \ __init__.py \ baseselector.py \ + selectcitation.py \ selectevent.py \ selectfamily.py \ selectnote.py \ diff --git a/src/gui/selectors/baseselector.py b/src/gui/selectors/baseselector.py index 7a8ee201f..d3e255846 100644 --- a/src/gui/selectors/baseselector.py +++ b/src/gui/selectors/baseselector.py @@ -193,6 +193,8 @@ class BaseSelector(ManagedWindow.ManagedWindow): id_list = self.get_selected_ids() if id_list and id_list[0]: result = self.get_from_handle_func()(id_list[0]) + if result is None and self.get_from_handle_func2: + result = self.get_from_handle_func2()(id_list[0]) self.close() elif val != gtk.RESPONSE_DELETE_EVENT: self.close() @@ -225,6 +227,9 @@ class BaseSelector(ManagedWindow.ManagedWindow): def get_from_handle_func(self): assert False, "Must be defined in the subclass" + def get_from_handle_func2(self): + return None + def get_handle_column(self): # return 3 assert False, "Must be defined in the subclass" diff --git a/src/gui/selectors/selectcitation.py b/src/gui/selectors/selectcitation.py new file mode 100644 index 000000000..f7146806c --- /dev/null +++ b/src/gui/selectors/selectcitation.py @@ -0,0 +1,77 @@ +# +# 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 +# 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$ + +""" +SelectCitation class for GRAMPS. +""" + +#------------------------------------------------------------------------- +# +# internationalization +# +#------------------------------------------------------------------------- +from gen.ggettext import gettext as _ + +#------------------------------------------------------------------------- +# +# gramps modules +# +#------------------------------------------------------------------------- +from gui.views.treemodels import CitationTreeModel +from baseselector import BaseSelector + +#------------------------------------------------------------------------- +# +# SelectSource +# +#------------------------------------------------------------------------- +class SelectCitation(BaseSelector): + + def _local_init(self): + """ + Perform local initialisation for this class + """ + self.width_key = 'interface.source-sel-width' + self.height_key = 'interface.source-sel-height' + + def get_window_title(self): + return _("Select Source or Citation") + + def get_model_class(self): + return CitationTreeModel + + def get_column_titles(self): + return [ + (_('Page'), 350, BaseSelector.TEXT, 0), + (_('ID'), 75, BaseSelector.TEXT, 1) + ] + + def get_from_handle_func(self): + return self.db.get_source_from_handle + + def get_from_handle_func2(self): + return self.db.get_citation_from_handle + + def get_handle_column(self): + return 8 diff --git a/src/gui/selectors/selectorfactory.py b/src/gui/selectors/selectorfactory.py index 875f09238..a3fd7de61 100644 --- a/src/gui/selectors/selectorfactory.py +++ b/src/gui/selectors/selectorfactory.py @@ -2,6 +2,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 # it under the terms of the GNU General Public License as published by @@ -38,6 +39,9 @@ def SelectorFactory(classname): elif classname == 'Source': from selectsource import SelectSource cls = SelectSource + elif classname == 'Citation': + from selectcitation import SelectCitation + cls = SelectCitation elif classname in ['MediaObject', 'Media']: from selectobject import SelectObject cls = SelectObject diff --git a/src/gui/user.py b/src/gui/user.py index 8a0083895..cbd1dabc2 100644 --- a/src/gui/user.py +++ b/src/gui/user.py @@ -17,7 +17,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # -# $Id$ +# $Id: user.py 18393 2011-10-31 16:46:50Z paul-franklin $ # """ diff --git a/src/gui/views/treemodels/Makefile.am b/src/gui/views/treemodels/Makefile.am index 8499b308c..413d96b35 100644 --- a/src/gui/views/treemodels/Makefile.am +++ b/src/gui/views/treemodels/Makefile.am @@ -16,6 +16,9 @@ pkgdata_PYTHON = \ placemodel.py \ repomodel.py \ sourcemodel.py \ + citationbasemodel.py \ + citationlistmodel.py \ + citationtreemodel.py \ treebasemodel.py pkgpyexecdir = @pkgpyexecdir@/gui/views/treemodels diff --git a/src/gui/views/treemodels/__init__.py b/src/gui/views/treemodels/__init__.py index e7c1faafb..beab6012c 100644 --- a/src/gui/views/treemodels/__init__.py +++ b/src/gui/views/treemodels/__init__.py @@ -1,6 +1,7 @@ # Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 2009 Benny Malengier +# 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 @@ -31,3 +32,6 @@ from placemodel import PlaceBaseModel, PlaceListModel, PlaceTreeModel from mediamodel import MediaModel from repomodel import RepositoryModel from notemodel import NoteModel +from citationbasemodel import CitationBaseModel +from citationlistmodel import CitationListModel +from citationtreemodel import CitationTreeModel diff --git a/src/gui/views/treemodels/citationbasemodel.py b/src/gui/views/treemodels/citationbasemodel.py new file mode 100644 index 000000000..955e6bf0c --- /dev/null +++ b/src/gui/views/treemodels/citationbasemodel.py @@ -0,0 +1,228 @@ +# +# 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 +# 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$ + +""" +CitationBaseModel classes for GRAMPS. +""" + +#------------------------------------------------------------------------- +# +# python modules +# +#------------------------------------------------------------------------- +import cgi +import logging +log = logging.getLogger(".") +LOG = logging.getLogger(".citation") + +#------------------------------------------------------------------------- +# +# GRAMPS modules +# +#------------------------------------------------------------------------- +import const +import ToolTips +import DateHandler +import gen.lib +from Utils import confidence, format_time +import config + +#------------------------------------------------------------------------- +# +# COLUMN constants +# +#------------------------------------------------------------------------- +# These are the column numbers in the serialize/unserialize interfaces in +# the Citation object +COLUMN_HANDLE = 0 +COLUMN_ID = 1 +COLUMN_DATE = 2 +COLUMN_PAGE = 3 +COLUMN_CONFIDENCE = 4 +COLUMN_SOURCE = 5 +COLUMN_CHANGE = 9 + +# Data for the Source object +COLUMN2_HANDLE = 0 +COLUMN2_ID = 1 +COLUMN2_TITLE = 2 +COLUMN2_AUTHOR = 3 +COLUMN2_PUBINFO = 4 +COLUMN2_ABBREV = 7 +COLUMN2_CHANGE = 8 + +INVALID_DATE_FORMAT = config.get('preferences.invalid-date-format') + +#------------------------------------------------------------------------- +# +# CitationModel +# +#------------------------------------------------------------------------- +class CitationBaseModel(object): + +# Fields access when 'data' is a Citation + + def citation_date(self, data): + if data[COLUMN_DATE]: + citation = gen.lib.Citation() + citation.unserialize(data) + date_str = DateHandler.get_date(citation) + if date_str != "": + retval = cgi.escape(date_str) + if not DateHandler.get_date_valid(citation): + return INVALID_DATE_FORMAT % retval + else: + return retval + return u'' + + def citation_sort_date(self, data): + if data[COLUMN_DATE]: + citation = gen.lib.Citation() + citation.unserialize(data) + retval = "%09d" % citation.get_date_object().get_sort_value() + if not DateHandler.get_date_valid(citation): + return INVALID_DATE_FORMAT % retval + else: + return retval + return u'' + + def citation_id(self, data): + return unicode(data[COLUMN_ID]) + + def citation_page(self, data): + return unicode(data[COLUMN_PAGE]) + + def citation_confidence(self, data): + return unicode(confidence[data[COLUMN_CONFIDENCE]]) + + def citation_handle(self, data): + return unicode(data[COLUMN_HANDLE]) + + def citation_change(self, data): + return format_time(data[COLUMN_CHANGE]) + + def citation_sort_change(self, data): + return "%012x" % data[COLUMN_CHANGE] + + def citation_source(self, data): + return data[COLUMN_SOURCE] + + def citation_src_title(self, data): + source_handle = data[COLUMN_SOURCE] + try: + source = self.db.get_source_from_handle(source_handle) + return unicode(source.get_title()) + except: + return u'' + + def citation_src_id(self, data): + source_handle = data[COLUMN_SOURCE] + try: + source = self.db.get_source_from_handle(source_handle) + return unicode(source.gramps_id) + except: + return u'' + + def citation_src_auth(self, data): + source_handle = data[COLUMN_SOURCE] + try: + source = self.db.get_source_from_handle(source_handle) + return unicode(source.get_author()) + except: + return u'' + + def citation_src_abbr(self, data): + source_handle = data[COLUMN_SOURCE] + try: + source = self.db.get_source_from_handle(source_handle) + return unicode(source.get_abbreviation()) + except: + return u'' + + def citation_src_pinfo(self, data): + source_handle = data[COLUMN_SOURCE] + try: + source = self.db.get_source_from_handle(source_handle) + return unicode(source.get_publication_info()) + except: + return u'' + + def citation_src_chan(self, data): + source_handle = data[COLUMN_SOURCE] + try: + source = self.db.get_source_from_handle(source_handle) + return format_time(source.change) + except: + return u'' + + def citation_tooltip(self, data): + if const.USE_TIPS: + try: + t = ToolTips.TipFromFunction(self.db, lambda: + self.db.get_citation_from_handle(data[0])) + except: + log.error("Failed to create tooltip.", exc_info=True) + return t + else: + return u'' + +# Fields access when 'data' is a Source + + def source_handle(self, data): + return unicode(data[COLUMN2_HANDLE]) + + def source_src_title(self, data): + return unicode(data[COLUMN2_TITLE]) + + def source_src_id(self, data): + return unicode(data[COLUMN2_ID]) + + def source_src_auth(self, data): + return unicode(data[COLUMN2_AUTHOR]) + + def source_src_abbr(self, data): + return unicode(data[COLUMN2_ABBREV]) + + def source_src_pinfo(self, data): + return unicode(data[COLUMN2_PUBINFO]) + + def source_src_chan(self, data): + return format_time(data[COLUMN2_CHANGE]) + + def source_sort2_change(self, data): + return "%012x" % data[COLUMN2_CHANGE] + + def source_tooltip(self, data): + if const.USE_TIPS: + try: + t = ToolTips.TipFromFunction(self.db, lambda: + self.db.get_source_from_handle(data[0])) + except: + log.error("Failed to create tooltip.", exc_info=True) + return t + else: + return u'' + + def dummy_sort_key(self, data): + # dummy sort key for columns that don't have data + return None + \ No newline at end of file diff --git a/src/gui/views/treemodels/citationlistmodel.py b/src/gui/views/treemodels/citationlistmodel.py new file mode 100644 index 000000000..0ddf414b8 --- /dev/null +++ b/src/gui/views/treemodels/citationlistmodel.py @@ -0,0 +1,109 @@ +# +# 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 +# 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$ + +""" +CitationListModel class for GRAMPS. +""" + +#------------------------------------------------------------------------- +# +# python modules +# +#------------------------------------------------------------------------- +import logging +log = logging.getLogger(".") +LOG = logging.getLogger(".citation") + +#------------------------------------------------------------------------- +# +# GNOME/GTK modules +# +#------------------------------------------------------------------------- +import gtk + +#------------------------------------------------------------------------- +# +# GRAMPS modules +# +#------------------------------------------------------------------------- +from gui.views.treemodels.flatbasemodel import FlatBaseModel +from gui.views.treemodels.citationbasemodel import CitationBaseModel + +#------------------------------------------------------------------------- +# +# CitationListModel +# +#------------------------------------------------------------------------- +class CitationListModel(CitationBaseModel, FlatBaseModel): + """ + Flat citation model. (Original code in CitationBaseModel). + """ + def __init__(self, db, scol=0, order=gtk.SORT_ASCENDING, search=None, + skip=set(), sort_map=None): + self.map = db.get_raw_citation_data + self.gen_cursor = db.get_citation_cursor + self.fmap = [ + self.citation_page, + self.citation_id, + self.citation_date, + self.citation_confidence, + self.citation_change, + self.citation_src_title, + self.citation_src_id, + self.citation_src_auth, + self.citation_src_abbr, + self.citation_src_pinfo, + self.citation_src_chan, + self.citation_handle, + self.citation_tooltip + ] + self.smap = [ + self.citation_page, + self.citation_id, + self.citation_sort_date, + self.citation_confidence, + self.citation_sort_change, + self.citation_src_title, + self.citation_src_id, + self.citation_src_auth, + self.citation_src_abbr, + self.citation_src_pinfo, + self.citation_src_chan, + self.citation_handle, + self.citation_tooltip + ] + FlatBaseModel.__init__(self, db, scol, order, tooltip_column=12, + search=search, skip=skip, sort_map=sort_map) + + def destroy(self): + """ + Unset all elements that can prevent garbage collection + """ + self.db = None + self.gen_cursor = None + self.map = None + self.fmap = None + self.smap = None + FlatBaseModel.destroy(self) + + def on_get_n_columns(self): + return len(self.fmap)+1 diff --git a/src/gui/views/treemodels/citationtreemodel.py b/src/gui/views/treemodels/citationtreemodel.py new file mode 100644 index 000000000..818940083 --- /dev/null +++ b/src/gui/views/treemodels/citationtreemodel.py @@ -0,0 +1,207 @@ +# +# 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 +# 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$ + +""" +CitationTreeModel classes for GRAMPS. +""" + +#------------------------------------------------------------------------- +# +# python modules +# +#------------------------------------------------------------------------- +import logging +log = logging.getLogger(".") +LOG = logging.getLogger(".citation") + +#------------------------------------------------------------------------- +# +# internationalization +# +#------------------------------------------------------------------------- +from gen.ggettext import gettext as _ + +#------------------------------------------------------------------------- +# +# GNOME/GTK modules +# +#------------------------------------------------------------------------- +import gtk + +#------------------------------------------------------------------------- +# +# GRAMPS modules +# +#------------------------------------------------------------------------- +from Utils import get_source_referents +from gui.views.treemodels.treebasemodel import TreeBaseModel +from gui.views.treemodels.citationbasemodel import CitationBaseModel + +#------------------------------------------------------------------------- +# +# CitationModel +# +#------------------------------------------------------------------------- +class CitationTreeModel(CitationBaseModel, TreeBaseModel): + """ + Hierarchical citation model. + """ + def __init__(self, db, scol=0, order=gtk.SORT_ASCENDING, search=None, + skip=set(), sort_map=None): + self.db = db + self.map = self.db.get_raw_source_data + self.gen_cursor = self.db.get_source_cursor + # The items here must correspond, in order, with data in + # CitationTreeView, and with the items in the secondary fmap, fmap2 + self.fmap = [ + self.source_src_title, # COL_TITLE_PAGE (both Source & Citation) + self.source_src_id, # COL_ID (both Source & Citation) + None, # COL_DATE (not for Source) + None, # COL_CONFIDENCE (not for Source) + self.source_src_chan, # COL_CHAN (both Source & Citation) + self.source_src_auth, # COL_SRC_AUTH (Source only) + self.source_src_abbr, # COL_SRC_ABBR (Source only) + self.source_src_pinfo, # COL_SRC_PINFO (Source only) + self.source_handle, + self.source_tooltip + ] + self.smap = [ + self.source_src_title, + self.source_src_id, + self.dummy_sort_key, + self.dummy_sort_key, + self.source_sort2_change, + self.source_src_auth, + self.source_src_abbr, + self.source_src_pinfo, + self.source_handle, + self.source_tooltip + ] + + TreeBaseModel.__init__(self, self.db, scol=scol, order=order, + tooltip_column=9, + search=search, skip=skip, sort_map=sort_map, + nrgroups = 1, + group_can_have_handle = True) + + def destroy(self): + """ + Unset all elements that can prevent garbage collection + """ + self.db = None + self.gen_cursor = None + self.map = None + self.fmap = None + self.map2 = None + self.fmap2 = None + self.smap = None + self.number_items = None + TreeBaseModel.destroy(self) + + def _set_base_data(self): + """See TreeBaseModel, for citations, most have been set in init of + CitationBaseModel + """ + self.number_items = self.db.get_number_of_citations + self.map2 = self.db.get_raw_citation_data + self.fmap2 = [ + self.citation_page, + self.citation_id, + self.citation_date, + self.citation_confidence, + self.citation_change, + None, + None, + None, + self.citation_handle, + self.citation_tooltip + ] + + def get_tree_levels(self): + """ + Return the headings of the levels in the hierarchy. + """ + return [_('Source'), _('Citation')] + + def add_row(self, handle, data): + """ + Add nodes to the node map for a single citation. + + handle The handle of the gramps object. + data The object data. + """ + # first add the source +# source_name = self.source_src_title(data) + sort_key = self.sort_func(data) + # add as node: parent, child, sortkey, handle; parent and child are + # nodes in the treebasemodel, and will be used as iters + if self.get_node(handle) is None: + self.add_node(None, handle, sort_key, handle) + # now add all the related citations + source_handle_list = [] + for i in get_source_referents(handle, self.db): + for j in i: + source_handle_list.append(j) + for citation_handle in source_handle_list: + if self.get_node(citation_handle) is None: + # # add as node: parent, child, sortkey, handle; parent and child are + # # nodes in the treebasemodel, and will be used as iters + citation = self.db.get_citation_from_handle(citation_handle) + citation_page = citation.get_page() + self.add_node(handle, citation_handle, citation_page, + citation_handle, secondary=True) +# try: +# source_handle = data[COLUMN_SOURCE] +# except: +# LOG.debug("add_row: data %s is empty, handle %s citation %s data %s" % +# (data, handle, self.db.get_citation_from_handle(handle), +# self.db.get_citation_from_handle(handle).serialize())) +# source = self.db.get_source_from_handle(source_handle) +# if source is not None: +# source_name = source.get_title() +# sort_key = self.sort_func(data) +# if self.get_node(source_handle) is None: +# self.add_node(None, source_handle, source_name, source_handle, +# secondary=True) +# self.add_node(source_handle, handle, sort_key, handle) +# else: +# log.warn("Citation %s does not have a source" % +# unicode(data[COLUMN_PAGE]), +# exc_info=True) + + def add_secondary_row(self, handle, data): + """ + Add a secondary node to the node map for a citation. + """ + # parameters are parent, child, sortkey, handle + self.add_node(self.citation_source(data), handle, + self.citation_page(data), handle, secondary=True) + + def on_get_n_columns(self): + return len(self.fmap)+1 + + def column_header(self, node): + """ + Return a column heading. This is called for nodes with no associated + Gramps handle. + """ + return node.name diff --git a/src/gui/views/treemodels/treebasemodel.py b/src/gui/views/treemodels/treebasemodel.py index f37792df7..428f88266 100644 --- a/src/gui/views/treemodels/treebasemodel.py +++ b/src/gui/views/treemodels/treebasemodel.py @@ -5,6 +5,7 @@ # Copyright (C) 2009 Gary Burton # Copyright (C) 2009-2010 Nick Hall # Copyright (C) 2009 Benny Malengier +# Copyright (C) 2011 Nick Hall, 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 @@ -81,10 +82,10 @@ class Node(object): children A list of (sortkey, nodeid) tuples for the children of the node. This list is always kept sorted. """ - __slots__ = ('name', 'sortkey', 'ref', 'handle', 'parent', 'prev', - 'next', 'children')#, '__weakref__') + __slots__ = ('name', 'sortkey', 'ref', 'handle', 'secondary', 'parent', + 'prev', 'next', 'children')#, '__weakref__') - def __init__(self, ref, parent, sortkey, handle): + def __init__(self, ref, parent, sortkey, handle, secondary): self.name = sortkey if sortkey: self.sortkey = map(conv_unicode_tosrtkey_ongtk, sortkey) @@ -92,17 +93,19 @@ class Node(object): self.sortkey = None self.ref = ref self.handle = handle + self.secondary = secondary self.parent = parent self.prev = None self.next = None self.children = [] - def set_handle(self, handle): + def set_handle(self, handle, secondary=False): """ Assign the handle of a Gramps object to this node. """ - if not self.handle: + if not self.handle or handle == None: self.handle = handle + self.secondary = secondary else: print ('WARNING: Attempt to add handle twice to the node (%s)' % handle) @@ -356,10 +359,12 @@ class TreeBaseModel(gtk.GenericTreeModel): self.gen_cursor = None self.number_items = None # function self.map = None + self.map2 = None self.smap = None self.fmap = None - self.hmap = None + self.smap2 = None + self.fmap2 = None def displayed(self): """ @@ -407,7 +412,7 @@ class TreeBaseModel(gtk.GenericTreeModel): self.handle2node.clear() self.nodemap.clear() #start with creating the new iters - topnode = Node(None, None, None, None) + topnode = Node(None, None, None, None, False) self.nodemap.add_node(topnode) self.tree[None] = topnode @@ -540,7 +545,8 @@ class TreeBaseModel(gtk.GenericTreeModel): status_col.end() status.end() - def add_node(self, parent, child, sortkey, handle, add_parent=True): + def add_node(self, parent, child, sortkey, handle, add_parent=True, + secondary=False): """ Add a node to the map. @@ -563,10 +569,12 @@ class TreeBaseModel(gtk.GenericTreeModel): if child in self.tree: #a node is added that is already present, child_node = self.tree[child] - self._add_dup_node(child_node, parent, child, sortkey, handle) + self._add_dup_node(child_node, parent, child, sortkey, handle, + secondary) else: parent_node = self.tree[parent] - child_node = Node(child, id(parent_node), sortkey, handle) + child_node = Node(child, id(parent_node), sortkey, handle, + secondary) parent_node.add_child(child_node, self.nodemap) self.tree[child] = child_node self.nodemap.add_node(child_node) @@ -583,7 +591,7 @@ class TreeBaseModel(gtk.GenericTreeModel): if handle: self.handle2node[handle] = child_node - def _add_dup_node(self, node, parent, child, sortkey, handle): + def _add_dup_node(self, node, parent, child, sortkey, handle, secondary): """ How to handle adding a node a second time Default: if group nodes can have handles, it is allowed to add it @@ -595,7 +603,7 @@ class TreeBaseModel(gtk.GenericTreeModel): % (str(parent), str(child))) return if handle: - node.set_handle(handle) + node.set_handle(handle, secondary) if not self._in_build: self.__total += 1 self.__displayed += 1 @@ -605,6 +613,7 @@ class TreeBaseModel(gtk.GenericTreeModel): Remove a node from the map. """ if node.children: + del self.handle2node[node.handle] node.set_handle(None) self.__displayed -= 1 self.__total -= 1 @@ -676,10 +685,15 @@ class TreeBaseModel(gtk.GenericTreeModel): (self.search and self.search.match(handle, self.db)): #row needs to be added to the model data = self.map(handle) - self.add_row(handle, data) + if data: + self.add_row(handle, data) + else: + self.add_secondary_row(handle, self.map2(handle)) _LOG.debug(self.__class__.__name__ + ' add_row_by_handle ' + str(time.clock() - cput) + ' sec') + _LOG.debug("displayed %d / total: %d" % + (self.__displayed, self.__total)) def delete_row_by_handle(self, handle): """ @@ -708,6 +722,8 @@ class TreeBaseModel(gtk.GenericTreeModel): _LOG.debug(self.__class__.__name__ + ' delete_row_by_handle ' + str(time.clock() - cput) + ' sec') + _LOG.debug("displayed %d / total: %d" % + (self.__displayed, self.__total)) def update_row_by_handle(self, handle): """ @@ -777,30 +793,34 @@ class TreeBaseModel(gtk.GenericTreeModel): if col == self.color_column(): return None - # Look for header fuction for column and call it - if self.hmap[col] is not None: - return self.hmap[col](node) - - # If no header fuction return an empty string - return u'' + # Return the node name for the first column + if col == 0: + return self.column_header(node) else: # return values for 'data' row, calling a function # according to column_defs table - return self._get_value(node.handle, col) + return self._get_value(node.handle, col, node.secondary) - def _get_value(self, handle, col): + def _get_value(self, handle, col, secondary=False): """ Returns the contents of a given column of a gramps object """ - try: - if handle in self.lru_data: - data = self.lru_data[handle] - else: + if handle in self.lru_data: + data = self.lru_data[handle] + else: + if not secondary: data = self.map(handle) - if not self._in_build: - self.lru_data[handle] = data - return (self.fmap[col](data)) + else: + data = self.map2(handle) + if not self._in_build: + self.lru_data[handle] = data + + try: + if not secondary: + return (self.fmap[col](data)) + else: + return (self.fmap2[col](data)) except: return None diff --git a/src/gui/widgets/basicentry.py b/src/gui/widgets/basicentry.py index 7d5119ca5..1c9ecb7a6 100644 --- a/src/gui/widgets/basicentry.py +++ b/src/gui/widgets/basicentry.py @@ -18,7 +18,7 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # -# $Id$ +# $Id: basicentry.py 17965 2011-07-25 22:47:57Z nick-h $ __all__ = ["BasicEntry"] diff --git a/src/images/16x16/Makefile.am b/src/images/16x16/Makefile.am index 39bbef0b2..cb0601126 100644 --- a/src/images/16x16/Makefile.am +++ b/src/images/16x16/Makefile.am @@ -19,6 +19,7 @@ dist_pkgdata_DATA = \ gramps-bookmark-edit.png \ gramps-bookmark-new.png \ gramps-bookmark.png \ + gramps-citation.png \ gramps-config.png \ gramps-date.png \ gramps-date-edit.png \ diff --git a/src/images/16x16/gramps-citation.png b/src/images/16x16/gramps-citation.png new file mode 100644 index 000000000..040991f49 Binary files /dev/null and b/src/images/16x16/gramps-citation.png differ diff --git a/src/images/22x22/Makefile.am b/src/images/22x22/Makefile.am index ed93a36c1..ed9db1b09 100644 --- a/src/images/22x22/Makefile.am +++ b/src/images/22x22/Makefile.am @@ -19,6 +19,7 @@ dist_pkgdata_DATA = \ gramps-bookmark-edit.png \ gramps-bookmark-new.png \ gramps-bookmark.png \ + gramps-citation.png \ gramps-config.png \ gramps-date-edit.png \ gramps-date.png \ diff --git a/src/images/22x22/gramps-citation.png b/src/images/22x22/gramps-citation.png new file mode 100644 index 000000000..8527b728e Binary files /dev/null and b/src/images/22x22/gramps-citation.png differ diff --git a/src/images/48x48/Makefile.am b/src/images/48x48/Makefile.am index dd5c5da37..04b295f28 100644 --- a/src/images/48x48/Makefile.am +++ b/src/images/48x48/Makefile.am @@ -19,6 +19,7 @@ dist_pkgdata_DATA = \ gramps-bookmark-edit.png \ gramps-bookmark-new.png \ gramps-bookmark.png \ + gramps-citation.png \ gramps-config.png \ gramps-date-edit.png \ gramps-date.png \ diff --git a/src/images/48x48/gramps-citation.png b/src/images/48x48/gramps-citation.png new file mode 100644 index 000000000..509bdc201 Binary files /dev/null and b/src/images/48x48/gramps-citation.png differ diff --git a/src/images/scalable/Makefile.am b/src/images/scalable/Makefile.am index 5b5d57e72..169864035 100644 --- a/src/images/scalable/Makefile.am +++ b/src/images/scalable/Makefile.am @@ -19,6 +19,7 @@ dist_pkgdata_DATA = \ gramps-bookmark-edit.svg \ gramps-bookmark-new.svg \ gramps-bookmark.svg \ + gramps-citation.png \ gramps-config.svg \ gramps-date-edit.svg \ gramps-date.svg \ diff --git a/src/images/scalable/gramps-citation.png b/src/images/scalable/gramps-citation.png new file mode 100644 index 000000000..1a597e823 Binary files /dev/null and b/src/images/scalable/gramps-citation.png differ diff --git a/src/plugins/export/ExportCsv.py b/src/plugins/export/ExportCsv.py index 0350f0dbf..f1fd57e43 100644 --- a/src/plugins/export/ExportCsv.py +++ b/src/plugins/export/ExportCsv.py @@ -5,6 +5,7 @@ # Copyright (C) 2004-2007 Donald N. Allingham # Copyright (C) 2008 Brian G. Matherly # Copyright (C) 2010 Jakim Friant +# 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 @@ -93,8 +94,9 @@ def get_primary_event_ref_from_type(db, person, event_name): return None def get_primary_source_title(db, obj): - for ref in obj.get_source_references(): - source = db.get_source_from_handle(ref.ref) + for citation_handle in obj.get_citation_list(): + citation = db.get_citation_from_handle(citation_handle) + source = db.get_source_from_handle(citation.get_reference_handle()) if source: return source.get_title() return "" diff --git a/src/plugins/export/ExportGedcom.py b/src/plugins/export/ExportGedcom.py index f0f7b3824..4f061b003 100644 --- a/src/plugins/export/ExportGedcom.py +++ b/src/plugins/export/ExportGedcom.py @@ -7,6 +7,7 @@ # Copyright (C) 2008 Robert Cheramy # Copyright (C) 2010 Jakim Friant # 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 @@ -109,10 +110,10 @@ MIME2GED = { } QUALITY_MAP = { - gen.lib.SourceRef.CONF_VERY_HIGH : "3", - gen.lib.SourceRef.CONF_HIGH : "2", - gen.lib.SourceRef.CONF_LOW : "1", - gen.lib.SourceRef.CONF_VERY_LOW : "0", + gen.lib.Citation.CONF_VERY_HIGH : "3", + gen.lib.Citation.CONF_HIGH : "2", + gen.lib.Citation.CONF_LOW : "1", + gen.lib.Citation.CONF_VERY_LOW : "0", } #------------------------------------------------------------------------- @@ -194,7 +195,7 @@ def event_has_subordinate_data(event, event_ref): event.get_attribute_list() or event_ref.get_attribute_list() or event.get_note_list() or - event.get_source_references() or + event.get_citation_list() or event.get_media_list()) else: return False @@ -488,7 +489,7 @@ class GedcomWriter(UpdateCallback): self.__writeln(level, "ASSO", "@%s@" % person.get_gramps_id()) self.__writeln(level+1, "RELA", ref.get_relation()) self.__note_references(ref.get_note_list(), level+1) - self.__source_references(ref.get_source_references(), level+1) + self.__source_references(ref.get_citation_list(), level+1) def __note_references(self, notelist, level): """ @@ -669,15 +670,15 @@ class GedcomWriter(UpdateCallback): else: continue self.__note_references(attr.get_note_list(), 2) - self.__source_references(attr.get_source_references(), 2) + self.__source_references(attr.get_citation_list(), 2) - def __source_references(self, ref_list, level): + def __source_references(self, citation_list, level): """ - Loop through the list of source references, writing the information + Loop through the list of citation handles, writing the information to the file. """ - for srcref in ref_list: - self.__source_ref_record(level, srcref) + for citation_handle in citation_list: + self.__source_ref_record(level, citation_handle) def __addresses(self, person): """ @@ -703,7 +704,7 @@ class GedcomWriter(UpdateCallback): self.__writeln(2, 'PHON', addr.get_phone()) self.__note_references(addr.get_note_list(), 2) - self.__source_references(addr.get_source_references(), 2) + self.__source_references(addr.get_citation_list(), 2) def __photos(self, media_list, level): """ @@ -743,11 +744,11 @@ class GedcomWriter(UpdateCallback): def __person_sources(self, person): """ - Loop through the list of source references, writing the information + Loop through the list of citations, writing the information to the file. """ - for srcref in person.get_source_references(): - self.__source_ref_record(1, srcref) + for citation_handle in person.get_citation_list(): + self.__source_ref_record(1, citation_handle) def __url_list(self, obj, level): """ @@ -809,7 +810,7 @@ class GedcomWriter(UpdateCallback): self.__family_events(family) self.__family_attributes(family.get_attribute_list(), 1) self.__family_child_list(family.get_child_ref_list()) - self.__source_references(family.get_source_references(), 1) + self.__source_references(family.get_citation_list(), 1) self.__photos(family.get_media_list(), 1) self.__note_references(family.get_note_list(), 1) self.__change(family.get_change_time(), 1) @@ -921,7 +922,7 @@ class GedcomWriter(UpdateCallback): self.__writeln(2, 'TYPE', str(attr.get_type())) self.__note_references(attr.get_note_list(), level+1) - self.__source_references(attr.get_source_references(), + self.__source_references(attr.get_citation_list(), level+1) def __sources(self): @@ -1137,7 +1138,7 @@ class GedcomWriter(UpdateCallback): self.__writeln(3, 'AGE', attr.get_value()) self.__note_references(event.get_note_list(), 2) - self.__source_references(event.get_source_references(), 2) + self.__source_references(event.get_citation_list(), 2) self.__photos(event.get_media_list(), 2) if place: @@ -1193,7 +1194,7 @@ class GedcomWriter(UpdateCallback): self.__writeln(2, 'STAT', LDS_STATUS[lds_ord.get_status()]) self.__note_references(lds_ord.get_note_list(), index+1) - self.__source_references(lds_ord.get_source_references(), index+1) + self.__source_references(lds_ord.get_citation_list(), index+1) def __date(self, level, date): """ @@ -1270,10 +1271,10 @@ class GedcomWriter(UpdateCallback): if nick: self.__writeln(2, 'NICK', nick) - self.__source_references(name.get_source_references(), 2) + self.__source_references(name.get_citation_list(), 2) self.__note_references(name.get_note_list(), 2) - def __source_ref_record(self, level, ref): + def __source_ref_record(self, level, citation_handle): """ n SOUR @@ /* pointer to source record */ {1:1} +1 PAGE {0:1} @@ -1288,7 +1289,9 @@ class GedcomWriter(UpdateCallback): +1 <> {0:M} """ - src_handle = ref.get_reference_handle() + citation = self.dbase.get_citation_from_handle(citation_handle) + + src_handle = citation.get_reference_handle() if src_handle is None: return @@ -1298,25 +1301,27 @@ class GedcomWriter(UpdateCallback): # Reference to the source self.__writeln(level, "SOUR", "@%s@" % src.get_gramps_id()) - if ref.get_page() != "": + if citation.get_page() != "": # PAGE can not have CONC lines. # WHERE_WITHIN_SOURCE:= {Size=1:248} # Maximize line to 248 and set limit to 248, for no line split - self.__writeln(level+1, 'PAGE', ref.get_page()[0:248], limit=248) + self.__writeln(level+1, 'PAGE', citation.get_page()[0:248], + limit=248) - conf = min(ref.get_confidence_level(), gen.lib.SourceRef.CONF_VERY_HIGH) - if conf != gen.lib.SourceRef.CONF_NORMAL and conf != -1: + conf = min(citation.get_confidence_level(), + gen.lib.Citation.CONF_VERY_HIGH) + if conf != gen.lib.Citation.CONF_NORMAL and conf != -1: self.__writeln(level+1, "QUAY", QUALITY_MAP[conf]) - if not ref.get_date_object().is_empty(): + if not citation.get_date_object().is_empty(): self.__writeln(level+1, 'DATA') - self.__date(level+2, ref.get_date_object()) + self.__date(level+2, citation.get_date_object()) - if len(ref.get_note_list()) > 0: + if len(citation.get_note_list()) > 0: note_list = [ self.dbase.get_note_from_handle(h) - for h in ref.get_note_list() ] + for h in citation.get_note_list() ] note_list = [ n for n in note_list if n.get_type() == gen.lib.NoteType.SOURCE_TEXT] @@ -1325,16 +1330,17 @@ class GedcomWriter(UpdateCallback): else: ref_text = "" - if ref_text != "" and ref.get_date_object().is_empty(): + if ref_text != "" and citation.get_date_object().is_empty(): self.__writeln(level+1, 'DATA') if ref_text != "": self.__writeln(level+2, "TEXT", ref_text) note_list = [ self.dbase.get_note_from_handle(h) - for h in ref.get_note_list() ] + for h in citation.get_note_list() ] note_list = [ n.handle for n in note_list if n and n.get_type() != gen.lib.NoteType.SOURCE_TEXT] self.__note_references(note_list, level+1) + self.__photos(citation.get_media_list(), level+1) def __photo(self, photo, level): """ diff --git a/src/plugins/export/ExportGeneWeb.py b/src/plugins/export/ExportGeneWeb.py index 3074726e9..ea3e55701 100644 --- a/src/plugins/export/ExportGeneWeb.py +++ b/src/plugins/export/ExportGeneWeb.py @@ -130,12 +130,12 @@ class GeneWebWriter(object): (self.get_ref_name(father), self.get_full_person_info_fam(father), self.get_wedding_data(family), - self.get_ref_name(mother), + self.get_ref_name(mother), self.get_full_person_info_fam(mother) ) ) self.write_witness( family) - self.write_sources( family.get_source_references()) + self.write_sources( family.get_citation_list()) self.write_children( family, father) self.write_notes( family, father, mother) if True: # FIXME: not (self.restrict and self.exclnotes): @@ -186,14 +186,14 @@ class GeneWebWriter(object): # return if reflist: - for sr in reflist: - sbase = sr.get_reference_handle() - if sbase: - source = self.db.get_source_from_handle(sbase) - if source: - self.writeln( "src %s" % - (self.rem_spaces(source.get_title())) - ) + for handle in reflist: + citation = self.db.get_citation_from_handle(handle) + src_handle = citation.get_reference_handle() + source = self.db.get_source_from_handle(src_handle) + if source: + self.writeln( "src %s" % + (self.rem_spaces(source.get_title())) + ) def write_children(self,family, father): father_lastname = father.get_primary_name().get_surname() @@ -380,7 +380,7 @@ class GeneWebWriter(object): if place_handle: place = self.db.get_place_from_handle(place_handle) m_place = place.get_title() - m_source = self.get_primary_source( event.get_source_references()) + m_source = self.get_primary_source( event.get_citation_list()) if event.get_type() == gen.lib.EventType.ENGAGEMENT: engaged = 1 eng_date = self.format_date( event.get_date_object()) @@ -388,7 +388,7 @@ class GeneWebWriter(object): if place_handle: place = self.db.get_place_from_handle(place_handle) eng_place = place.get_title() - eng_source = self.get_primary_source( event.get_source_references()) + eng_source = self.get_primary_source( event.get_citation_list()) if event.get_type() == gen.lib.EventType.DIVORCE: divorced = 1 div_date = self.format_date( event.get_date_object()) @@ -427,13 +427,13 @@ class GeneWebWriter(object): def get_primary_source(self,reflist): ret = "" if reflist: - for sr in reflist: - sbase = sr.get_reference_handle() - if sbase: - source = self.db.get_source_from_handle(sbase) - if source: - if ret != "": - ret = ret + ", " + for handle in reflist: + citation = self.db.get_citation_from_handle(handle) + src_handle = citation.get_reference_handle() + source = self.db.get_source_from_handle(src_handle) + if source: + if ret != "": + ret = ret + ", " ret = ret + source.get_title() return ret diff --git a/src/plugins/export/ExportXml.py b/src/plugins/export/ExportXml.py index 07ca96aa7..a73452f28 100644 --- a/src/plugins/export/ExportXml.py +++ b/src/plugins/export/ExportXml.py @@ -7,7 +7,7 @@ # Copyright (C) 2008 Robert Cheramy # Copyright (C) 2009 Douglas S. Blank # Copyright (C) 2010 Jakim Friant -# Copyright (C) 2010 Nick Hall +# Copyright (C) 2010-2011 Nick Hall # # 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 @@ -200,6 +200,7 @@ class GrampsXmlWriter(UpdateCallback): person_len = self.db.get_number_of_people() family_len = self.db.get_number_of_families() event_len = self.db.get_number_of_events() + citation_len = self.db.get_number_of_citations() source_len = self.db.get_number_of_sources() place_len = self.db.get_number_of_places() repo_len = self.db.get_number_of_repositories() @@ -207,8 +208,9 @@ class GrampsXmlWriter(UpdateCallback): note_len = self.db.get_number_of_notes() tag_len = self.db.get_number_of_tags() - total_steps = (person_len + family_len + event_len + source_len + - place_len + repo_len + obj_len + note_len + tag_len + total_steps = (person_len + family_len + event_len + citation_len + + source_len + place_len + repo_len + obj_len + note_len + + tag_len ) self.set_total(total_steps) @@ -282,6 +284,14 @@ class GrampsXmlWriter(UpdateCallback): self.update() self.g.write(" \n") + if citation_len > 0: + self.g.write(" \n") + for handle in self.db.get_citation_handles(): + citation = self.db.get_citation_from_handle(handle) + self.write_citation(citation,2) + self.update() + self.g.write(" \n") + if source_len > 0: self.g.write(" \n") for handle in sorted(self.db.get_source_handles()): @@ -523,8 +533,8 @@ class GrampsXmlWriter(UpdateCallback): self.write_note_list(person.get_note_list(),index+1) - for s in person.get_source_references(): - self.dump_source_ref(s,index+2) + for citation_handle in person.get_citation_list(): + self.write_ref("citationref", citation_handle, index+2) for tag_handle in person.get_tag_list(): self.write_ref("tagref", tag_handle, index+1) @@ -551,14 +561,26 @@ class GrampsXmlWriter(UpdateCallback): self.dump_child_ref(child_ref,index+1) self.write_attribute_list(family.get_attribute_list()) self.write_note_list(family.get_note_list(),index+1) - for s in family.get_source_references(): - self.dump_source_ref(s,index+1) + for citation_handle in family.get_citation_list(): + self.write_ref("citationref", citation_handle, index+1) for tag_handle in family.get_tag_list(): self.write_ref("tagref", tag_handle, index+1) self.g.write("%s\n" % sp) + def write_citation(self, citation, index=1): + sp = " " * index + self.write_primary_tag("citation", citation, index) + self.write_date(citation.get_date_object(), index+1) + self.write_line("page", citation.get_page(), index+1) + self.write_line("confidence", citation.get_confidence_level(), index+1) + self.write_note_list(citation.get_note_list(), index+1) + self.write_media_list(citation.get_media_list(), index+1) + self.write_data_map(citation.get_data_map()) + self.write_ref("sourceref", citation.get_reference_handle(), index+1) + self.g.write("%s\n" % sp) + def write_source(self,source,index=1): sp = " "*index self.write_primary_tag("source",source,index) @@ -603,8 +625,8 @@ class GrampsXmlWriter(UpdateCallback): self.write_line("postal",address.get_postal_code(),index+1) self.write_line("phone",address.get_phone(),index+1) self.write_note_list(address.get_note_list(),index+1) - for s in address.get_source_references(): - self.dump_source_ref(s,index+2) + for citation_handle in address.get_citation_list(): + self.write_ref("citationref", citation_handle, index+1) self.g.write('%s\n' % sp) def dump_person_ref(self,personref,index=1): @@ -614,16 +636,17 @@ class GrampsXmlWriter(UpdateCallback): priv_text = conf_priv(personref) rel_text = ' rel="%s"' % escxml(personref.get_relation()) - sreflist = personref.get_source_references() + citation_list = personref.get_citation_list() nreflist = personref.get_note_list() - if (len(sreflist) + len(nreflist) == 0): + if (len(citation_list) + len(nreflist) == 0): self.write_ref('personref',personref.ref,index,close=True, extra_text=priv_text+rel_text) else: self.write_ref('personref',personref.ref,index,close=False, extra_text=priv_text+rel_text) - for sref in sreflist: - self.dump_source_ref(sref,index+1) + for citation_handle in citation_list: + self.write_ref("citationref", citation_handle, index+1) + self.write_note_list(nreflist,index+1) self.g.write('%s\n' % sp) @@ -640,16 +663,16 @@ class GrampsXmlWriter(UpdateCallback): mrel_text = '' else: mrel_text = ' mrel="%s"' % escxml(childref.mrel.xml_str()) - sreflist = childref.get_source_references() + citation_list = childref.get_citation_list() nreflist = childref.get_note_list() - if (len(sreflist)+len(nreflist) == 0): + if (len(citation_list)+len(nreflist) == 0): self.write_ref('childref',childref.ref,index,close=True, extra_text=priv_text+mrel_text+frel_text) else: self.write_ref('childref',childref.ref,index,close=False, extra_text=priv_text+mrel_text+frel_text) - for sref in sreflist: - self.dump_source_ref(sref,index+1) + for citation_handle in citation_list: + self.write_ref("citationref", citation_handle, index+1) self.write_note_list(nreflist,index+1) self.g.write('%s\n' % sp) @@ -691,8 +714,8 @@ class GrampsXmlWriter(UpdateCallback): self.write_attribute_list(event.get_attribute_list(),index+1) self.write_note_list(event.get_note_list(),index+1) - for s in event.get_source_references(): - self.dump_source_ref(s,index+1) + for citation_handle in event.get_citation_list(): + self.write_ref("citationref", citation_handle, index+1) self.write_media_list(event.get_media_list(),index+1) self.g.write("%s\n" % sp) @@ -718,39 +741,9 @@ class GrampsXmlWriter(UpdateCallback): self.g.write('%s\n' % (sp2,"_"+ord.get_family_handle())) self.write_note_list(ord.get_note_list(),index+1) - for s in ord.get_source_references(): - self.dump_source_ref(s,index+1) + for citation_handle in ord.get_citation_list(): + self.write_ref("citationref", citation_handle, index+1) self.g.write('%s\n' % sp) - - def dump_source_ref(self,source_ref,index=1): - source = self.db.get_source_from_handle( - source_ref.get_reference_handle()) - if source: - p = source_ref.get_page() - n = source_ref.get_note_list() - d = source_ref.get_date_object() - q = source_ref.get_confidence_level() - self.g.write(" " * index) - - priv = conf_priv(source_ref) - - if p == "" and n == [] and d.is_empty() and q == 2: - self.g.write('\n' - % ("_"+source.get_handle(), priv) - ) - else: - if q == 2: - self.g.write('\n' - % ("_"+source.get_handle(), priv) - ) - else: - self.g.write('\n' - % ("_"+source.get_handle(), q, priv) - ) - self.write_line("spage",p,index+1) - self.write_note_list(n,index+1) - self.write_date(d,index+1) - self.g.write("%s\n" % (" " * index)) def write_ref(self,tagname, handle,index=1,close=True,extra_text=''): if handle: @@ -957,8 +950,8 @@ class GrampsXmlWriter(UpdateCallback): if name.date: self.write_date(name.date,4) self.write_note_list(name.get_note_list(),index+1) - for s in name.get_source_references(): - self.dump_source_ref(s,index+1) + for citation_handle in name.get_citation_list(): + self.write_ref("citationref", citation_handle, index+1) self.g.write('%s\n' % sp) @@ -1038,14 +1031,14 @@ class GrampsXmlWriter(UpdateCallback): (sp,conf_priv(attr),escxml(attr.get_type().xml_str()), self.fix(attr.get_value())) ) - slist = attr.get_source_references() + citation_list = attr.get_citation_list() nlist = attr.get_note_list() - if (len(nlist)+len(slist)) == 0: + if (len(nlist)+len(citation_list)) == 0: self.g.write('/>\n') else: self.g.write('>\n') - for s in attr.get_source_references(): - self.dump_source_ref(s,indent+1) + for citation_handle in citation_list: + self.write_ref("citationref", citation_handle, indent+1) self.write_note_list(attr.get_note_list(),indent+1) self.g.write('%s\n' % sp) @@ -1057,7 +1050,7 @@ class GrampsXmlWriter(UpdateCallback): if photo.get_privacy(): self.g.write(' priv="1"') proplist = photo.get_attribute_list() - refslist = photo.get_source_references() + citation_list = photo.get_citation_list() nreflist = photo.get_note_list() rect = photo.get_rectangle() if rect is not None : @@ -1074,7 +1067,7 @@ class GrampsXmlWriter(UpdateCallback): corner1_x == corner1_y == 0 and corner2_x == corner2_y == 100): rect = None - if (len(proplist) + len(nreflist) + len(refslist) == 0 and + if (len(proplist) + len(nreflist) + len(citation_list) == 0 and rect is None): self.g.write("/>\n") else: @@ -1090,8 +1083,8 @@ class GrampsXmlWriter(UpdateCallback): ) ) self.write_attribute_list(proplist,indent+1) - for ref in refslist: - self.dump_source_ref(ref, indent+1) + for citation_handle in citation_list: + self.write_ref("citationref", citation_handle, indent+1) self.write_note_list(nreflist, indent+1) self.g.write('%s\n' % sp) @@ -1170,7 +1163,7 @@ class GrampsXmlWriter(UpdateCallback): llen = (len(place.get_alternate_locations()) + len(place.get_url_list()) + len(place.get_media_list()) + - len(place.get_source_references()) + len(place.get_citation_list()) ) ml_empty = main_loc.is_empty() @@ -1187,8 +1180,8 @@ class GrampsXmlWriter(UpdateCallback): self.write_media_list(place.get_media_list(), index+1) self.write_url_list(place.get_url_list()) self.write_note_list(place.get_note_list(), index+1) - for s in place.get_source_references(): - self.dump_source_ref(s, index+1) + for citation_handle in place.get_citation_list(): + self.write_ref("citationref", citation_handle, index+1) self.g.write("%s\n" % (" "*index)) def write_object(self, obj, index=1): @@ -1217,8 +1210,8 @@ class GrampsXmlWriter(UpdateCallback): dval = obj.get_date_object() if not dval.is_empty(): self.write_date(dval, index+1) - for s in obj.get_source_references(): - self.dump_source_ref(s, index+1) + for citation_handle in obj.get_citation_list(): + self.write_ref("citationref", citation_handle, index+1) for tag_handle in obj.get_tag_list(): self.write_ref("tagref", tag_handle, index+1) diff --git a/src/plugins/gramplet/Backlinks.py b/src/plugins/gramplet/Backlinks.py index f2b55a80a..2f6bf6196 100644 --- a/src/plugins/gramplet/Backlinks.py +++ b/src/plugins/gramplet/Backlinks.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 @@ -172,6 +173,27 @@ class SourceBacklinks(Backlinks): else: self.set_has_data(False) +class CitationBacklinks(Backlinks): + """ + Displays the back references for a Citation,. + """ + def db_changed(self): + self.dbstate.db.connect('citation-update', self.update) + self.connect_signal('Citation', self.update) + self.update() + + def update_has_data(self): + active_handle = self.get_active('Citation') + self.set_has_data(self.get_has_data(active_handle)) + + def main(self): + active_handle = self.get_active('Citation') + self.model.clear() + if active_handle: + self.display_backlinks(active_handle) + else: + self.set_has_data(False) + class RepositoryBacklinks(Backlinks): """ Displays the back references for a repository. diff --git a/src/plugins/gramplet/Sources.py b/src/plugins/gramplet/Citations.py similarity index 54% rename from src/plugins/gramplet/Sources.py rename to src/plugins/gramplet/Citations.py index da48a3d94..fd8522348 100644 --- a/src/plugins/gramplet/Sources.py +++ b/src/plugins/gramplet/Citations.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 @@ -19,16 +20,16 @@ # $Id$ # -from gui.editors import EditSource +from gui.editors import EditSource, EditCitation from ListModel import ListModel, NOSORT from gen.plug import Gramplet from gen.ggettext import gettext as _ import Errors import gtk -class Sources(Gramplet): +class Citations(Gramplet): """ - Displays the sources for an object. + Displays the citations for an object. """ def init(self): self.gui.WIDGET = self.build_gui() @@ -40,186 +41,219 @@ class Sources(Gramplet): """ Build the GUI interface. """ - tip = _('Double-click on a row to edit the selected source.') + tip = _('Double-click on a row to edit the selected source/citation.') self.set_tooltip(tip) top = gtk.TreeView() titles = [('', NOSORT, 50,), - (_('Source'), 1, 200), - (_('Reference'), 2, 300), - (_('Author'), 3, 100)] - self.model = ListModel(top, titles, event_func=self.edit_source) + (_('Source/Citation'), 1, 350), + (_('Author'), 2, 200), + (_('Publisher'), 3, 150)] + self.model = ListModel(top, titles, list_mode="tree", + event_func=self.invoke_editor) return top - def add_sources(self, obj): - for source_ref in obj.get_source_references(): - self.add_source_ref(source_ref) + def add_citations(self, obj): + for citation_handle in obj.get_citation_list(): + self.add_citation_ref(citation_handle) - def add_name_sources(self, obj): + def add_name_citations(self, obj): names = [obj.get_primary_name()] + obj.get_alternate_names() for name in names: - self.add_sources(name) + self.add_citations(name) - def add_attribute_sources(self, obj): + def add_attribute_citations(self, obj): for attr in obj.get_attribute_list(): - self.add_sources(attr) + self.add_citations(attr) - def add_mediaref_sources(self, obj): + def add_mediaref_citations(self, obj): for media_ref in obj.get_media_list(): - self.add_sources(media_ref) - self.add_attribute_sources(media_ref) + self.add_citations(media_ref) + self.add_attribute_citations(media_ref) media = self.dbstate.db.get_object_from_handle(media_ref.ref) - self.add_media_sources(media) + self.add_media_citations(media) - def add_media_sources(self, media): - self.add_sources(media) - self.add_attribute_sources(media) + def add_media_citations(self, media): + self.add_citations(media) + self.add_attribute_citations(media) - def add_eventref_sources(self, obj): + def add_eventref_citations(self, obj): for event_ref in obj.get_event_ref_list(): - self.add_attribute_sources(event_ref) + self.add_attribute_citations(event_ref) event = self.dbstate.db.get_event_from_handle(event_ref.ref) - self.add_event_sources(event) + self.add_event_citations(event) - def add_event_sources(self, event): - self.add_sources(event) - self.add_attribute_sources(event) - self.add_mediaref_sources(event) + def add_event_citations(self, event): + self.add_citations(event) + self.add_attribute_citations(event) + self.add_mediaref_citations(event) place_handle = event.get_place_handle() place = self.dbstate.db.get_place_from_handle(place_handle) if place: - self.add_place_sources(place) + self.add_place_citations(place) - def add_place_sources(self, place): - self.add_sources(place) - self.add_mediaref_sources(place) + def add_place_citations(self, place): + self.add_citations(place) + self.add_mediaref_citations(place) - def add_address_sources(self, obj): + def add_address_citations(self, obj): for address in obj.get_address_list(): - self.add_sources(address) + self.add_citations(address) - def add_lds_sources(self, obj): + def add_lds_citations(self, obj): for lds in obj.get_lds_ord_list(): - self.add_sources(lds) + self.add_citations(lds) place_handle = lds.get_place_handle() place = self.dbstate.db.get_place_from_handle(place_handle) if place: - self.add_place_sources(place) + self.add_place_citations(place) - def add_association_sources(self, obj): + def add_association_citations(self, obj): for assoc in obj.get_person_ref_list(): - self.add_sources(assoc) + self.add_citations(assoc) - def add_source_ref(self, source_ref): + def add_citation_ref(self, citation_handle): """ - Add a source reference to the model. + Add a citation to the model. """ - page = source_ref.get_page() - source = self.dbstate.db.get_source_from_handle(source_ref.ref) + citation = self.dbstate.db.get_citation_from_handle(citation_handle) + page = citation.get_page() + if not page: + page = _('') + source_handle = citation.get_reference_handle() + source = self.dbstate.db.get_source_from_handle(source_handle) title = source.get_title() author = source.get_author() - self.model.add((source_ref.ref, title, page, author)) + publisher = source.get_publication_info() - def check_sources(self, obj): - return True if obj.get_source_references() else False + if source_handle not in self.source_nodes: + node = self.model.add([source_handle, title, author, publisher]) + self.source_nodes[source_handle] = node + + self.model.add([citation_handle, page, '', ''], + node=self.source_nodes[source_handle]) + + def check_citations(self, obj): + return True if obj.get_citation_list() else False - def check_name_sources(self, obj): + def check_name_citations(self, obj): names = [obj.get_primary_name()] + obj.get_alternate_names() for name in names: - if self.check_sources(name): + if self.check_citations(name): return True return False - def check_attribute_sources(self, obj): + def check_attribute_citations(self, obj): for attr in obj.get_attribute_list(): - if self.check_sources(attr): + if self.check_citations(attr): return True return False - def check_mediaref_sources(self, obj): + def check_mediaref_citations(self, obj): for media_ref in obj.get_media_list(): - if self.check_sources(media_ref): + if self.check_citations(media_ref): return True - if self.check_attribute_sources(media_ref): + if self.check_attribute_citations(media_ref): return True media = self.dbstate.db.get_object_from_handle(media_ref.ref) - if self.check_media_sources(media): + if self.check_media_citations(media): return True return False - def check_media_sources(self, media): - if self.check_sources(media): + def check_media_citations(self, media): + if self.check_citations(media): return True - if self.check_attribute_sources(media): + if self.check_attribute_citations(media): return True return False - def check_eventref_sources(self, obj): + def check_eventref_citations(self, obj): for event_ref in obj.get_event_ref_list(): - if self.check_attribute_sources(event_ref): + if self.check_attribute_citations(event_ref): return True event = self.dbstate.db.get_event_from_handle(event_ref.ref) - if self.check_event_sources(event): + if self.check_event_citations(event): return True return False - def check_event_sources(self, event): - if self.check_sources(event): + def check_event_citations(self, event): + if self.check_citations(event): return True - if self.check_attribute_sources(event): + if self.check_attribute_citations(event): return True - if self.check_mediaref_sources(event): + if self.check_mediaref_citations(event): return True place_handle = event.get_place_handle() place = self.dbstate.db.get_place_from_handle(place_handle) - if place and self.check_place_sources(place): + if place and self.check_place_citations(place): return True return False - def check_place_sources(self, place): - if self.check_sources(place): + def check_place_citations(self, place): + if self.check_citations(place): return True - if self.check_mediaref_sources(place): + if self.check_mediaref_citations(place): return True return False - def check_address_sources(self, obj): + def check_address_citations(self, obj): for address in obj.get_address_list(): - if self.check_sources(address): + if self.check_citations(address): return True return False - def check_lds_sources(self, obj): + def check_lds_citations(self, obj): for lds in obj.get_lds_ord_list(): - if self.check_sources(lds): + if self.check_citations(lds): return True place_handle = lds.get_place_handle() place = self.dbstate.db.get_place_from_handle(place_handle) - if place and self.check_place_sources(place): + if place and self.check_place_citations(place): return True return False - def check_association_sources(self, obj): + def check_association_citations(self, obj): for assoc in obj.get_person_ref_list(): - if self.check_sources(assoc): + if self.check_citations(assoc): return True return False - def edit_source(self, treeview): + def invoke_editor(self, treeview): """ - Edit the selected source. + Edit the selected source or citation. """ model, iter_ = treeview.get_selection().get_selected() if iter_: handle = model.get_value(iter_, 0) - try: - source = self.dbstate.db.get_source_from_handle(handle) - EditSource(self.dbstate, self.uistate, [], source) - except Errors.WindowActiveError: - pass + if len(model.get_path(iter_)) == 1: + self.edit_source(handle) + else: + self.edit_citation(handle) -class PersonSources(Sources): + def edit_source(self, handle): + """ + Edit the selected source. + """ + try: + source = self.dbstate.db.get_source_from_handle(handle) + EditSource(self.dbstate, self.uistate, [], source) + except Errors.WindowActiveError: + pass + + def edit_citation(self, handle): + """ + Edit the selected citation. + """ + try: + citation = self.dbstate.db.get_citation_from_handle(handle) + source_handle = citation.get_reference_handle() + source = self.dbstate.db.get_source_from_handle(source_handle) + EditCitation(self.dbstate, self.uistate, [], citation, source) + except Errors.WindowActiveError: + pass + +class PersonCitations(Citations): """ - Displays the sources for a person. + Displays the citations for a person. """ def db_changed(self): self.dbstate.db.connect('person-update', self.update) @@ -239,27 +273,29 @@ class PersonSources(Sources): self.model.clear() if active: - self.display_sources(active) + self.display_citations(active) else: self.set_has_data(False) - def display_sources(self, person): + def display_citations(self, person): """ - Display the sources for the active person. + Display the citations for the active person. """ - self.add_sources(person) - self.add_eventref_sources(person) + self.source_nodes = {} + self.add_citations(person) + self.add_eventref_citations(person) for handle in person.get_family_handle_list(): family = self.dbstate.db.get_family_from_handle(handle) - self.add_eventref_sources(family) - self.add_name_sources(person) - self.add_attribute_sources(person) - self.add_address_sources(person) - self.add_mediaref_sources(person) - self.add_association_sources(person) - self.add_lds_sources(person) + self.add_eventref_citations(family) + self.add_name_citations(person) + self.add_attribute_citations(person) + self.add_address_citations(person) + self.add_mediaref_citations(person) + self.add_association_citations(person) + self.add_lds_citations(person) self.set_has_data(self.model.count > 0) + self.model.tree.expand_all() def get_has_data(self, person): """ @@ -267,31 +303,31 @@ class PersonSources(Sources): """ if person is None: return False - if self.check_sources(person): + if self.check_citations(person): return True - if self.check_eventref_sources(person): + if self.check_eventref_citations(person): return True for handle in person.get_family_handle_list(): family = self.dbstate.db.get_family_from_handle(handle) - if self.check_eventref_sources(family): + if self.check_eventref_citations(family): return True - if self.check_name_sources(person): + if self.check_name_citations(person): return True - if self.check_attribute_sources(person): + if self.check_attribute_citations(person): return True - if self.check_address_sources(person): + if self.check_address_citations(person): return True - if self.check_mediaref_sources(person): + if self.check_mediaref_citations(person): return True - if self.check_association_sources(person): + if self.check_association_citations(person): return True - if self.check_lds_sources(person): + if self.check_lds_citations(person): return True return False -class EventSources(Sources): +class EventCitations(Citations): """ - Displays the sources for an event. + Displays the citations for an event. """ def db_changed(self): self.dbstate.db.connect('event-update', self.update) @@ -309,16 +345,18 @@ class EventSources(Sources): self.model.clear() if active: - self.display_sources(active) + self.display_citations(active) else: self.set_has_data(False) - def display_sources(self, event): + def display_citations(self, event): """ - Display the sources for the active event. + Display the citations for the active event. """ - self.add_event_sources(event) + self.source_nodes = {} + self.add_event_citations(event) self.set_has_data(self.model.count > 0) + self.model.tree.expand_all() def get_has_data(self, event): """ @@ -326,13 +364,13 @@ class EventSources(Sources): """ if event is None: return False - if self.check_event_sources(event): + if self.check_event_citations(event): return True return False -class FamilySources(Sources): +class FamilyCitations(Citations): """ - Displays the sources for a family. + Displays the citations for a family. """ def db_changed(self): self.dbstate.db.connect('family-update', self.update) @@ -350,21 +388,23 @@ class FamilySources(Sources): self.model.clear() if active: - self.display_sources(active) + self.display_citations(active) else: self.set_has_data(False) - def display_sources(self, family): + def display_citations(self, family): """ - Display the sources for the active family. + Display the citations for the active family. """ - self.add_sources(family) - self.add_eventref_sources(family) - self.add_attribute_sources(family) - self.add_mediaref_sources(family) - self.add_lds_sources(family) + self.source_nodes = {} + self.add_citations(family) + self.add_eventref_citations(family) + self.add_attribute_citations(family) + self.add_mediaref_citations(family) + self.add_lds_citations(family) self.set_has_data(self.model.count > 0) + self.model.tree.expand_all() def get_has_data(self, family): """ @@ -372,21 +412,21 @@ class FamilySources(Sources): """ if family is None: return False - if self.check_sources(family): + if self.check_citations(family): return True - if self.check_eventref_sources(family): + if self.check_eventref_citations(family): return True - if self.check_attribute_sources(family): + if self.check_attribute_citations(family): return True - if self.check_mediaref_sources(family): + if self.check_mediaref_citations(family): return True - if self.check_lds_sources(family): + if self.check_lds_citations(family): return True return False -class PlaceSources(Sources): +class PlaceCitations(Citations): """ - Displays the sources for a place. + Displays the citations for a place. """ def db_changed(self): self.dbstate.db.connect('place-update', self.update) @@ -404,16 +444,18 @@ class PlaceSources(Sources): self.model.clear() if active: - self.display_sources(active) + self.display_citations(active) else: self.set_has_data(False) - def display_sources(self, place): + def display_citations(self, place): """ - Display the sources for the active place. + Display the citations for the active place. """ - self.add_place_sources(place) + self.source_nodes = {} + self.add_place_citations(place) self.set_has_data(self.model.count > 0) + self.model.tree.expand_all() def get_has_data(self, place): """ @@ -421,13 +463,13 @@ class PlaceSources(Sources): """ if place is None: return False - if self.check_place_sources(place): + if self.check_place_citations(place): return True return False -class MediaSources(Sources): +class MediaCitations(Citations): """ - Displays the sources for a media object. + Displays the citations for a media object. """ def db_changed(self): self.dbstate.db.connect('media-update', self.update) @@ -445,16 +487,18 @@ class MediaSources(Sources): self.model.clear() if active: - self.display_sources(active) + self.display_citations(active) else: self.set_has_data(False) - def display_sources(self, media): + def display_citations(self, media): """ - Display the sources for the active media object. + Display the citations for the active media object. """ - self.add_media_sources(media) + self.source_nodes = {} + self.add_media_citations(media) self.set_has_data(self.model.count > 0) + self.model.tree.expand_all() def get_has_data(self, media): """ @@ -462,6 +506,6 @@ class MediaSources(Sources): """ if media is None: return False - if self.check_media_sources(media): + if self.check_media_citations(media): return True return False diff --git a/src/plugins/gramplet/Filter.py b/src/plugins/gramplet/Filter.py index 81bb9e07d..f95b10c17 100644 --- a/src/plugins/gramplet/Filter.py +++ b/src/plugins/gramplet/Filter.py @@ -2,6 +2,7 @@ # # Copyright (C) 2010 Doug Blank # 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 @@ -28,6 +29,7 @@ from gen.plug import Gramplet from Filters.SideBar import (PersonSidebarFilter, FamilySidebarFilter, EventSidebarFilter, SourceSidebarFilter, + CitationSidebarFilter, PlaceSidebarFilter, MediaSidebarFilter, RepoSidebarFilter, NoteSidebarFilter) @@ -101,6 +103,17 @@ class SourceFilter(Filter): """ FILTER_CLASS = SourceSidebarFilter +#------------------------------------------------------------------------- +# +# CitationFilter class +# +#------------------------------------------------------------------------- +class CitationFilter(Filter): + """ + A gramplet providing a Citation Filter. + """ + FILTER_CLASS = CitationSidebarFilter + #------------------------------------------------------------------------- # # PlaceFilter class diff --git a/src/plugins/gramplet/Gallery.py b/src/plugins/gramplet/Gallery.py index 6d6886a0f..d3985d57c 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 @@ -206,3 +207,27 @@ class SourceGallery(Gallery): else: self.set_has_data(False) +class CitationGallery(Gallery): + """ + Displays a gallery of media objects for a Citation. + """ + def db_changed(self): + self.dbstate.db.connect('event-update', self.update) + self.connect_signal('Citation', self.update) + self.update() + + def update_has_data(self): + active_handle = self.get_active('Citation') + active = self.dbstate.db.get_citation_from_handle(active_handle) + self.set_has_data(self.get_has_data(active)) + + def main(self): + active_handle = self.get_active('Citation') + active = self.dbstate.db.get_citation_from_handle(active_handle) + + self.clear_images() + if active: + self.load_images(active) + else: + self.set_has_data(False) + diff --git a/src/plugins/gramplet/Makefile.am b/src/plugins/gramplet/Makefile.am index 52d61e08d..d86af79b5 100644 --- a/src/plugins/gramplet/Makefile.am +++ b/src/plugins/gramplet/Makefile.am @@ -14,6 +14,7 @@ pkgdata_PYTHON = \ bottombar.gpr.py \ CalendarGramplet.py \ Children.py \ + Citations.py \ DescendGramplet.py \ EditExifMetadata.py \ Events.py \ @@ -35,7 +36,6 @@ pkgdata_PYTHON = \ RelativeGramplet.py \ RepositoryDetails.py \ SessionLogGramplet.py \ - Sources.py \ StatsGramplet.py \ SurnameCloudGramplet.py \ ToDoGramplet.py \ diff --git a/src/plugins/gramplet/Notes.py b/src/plugins/gramplet/Notes.py index d4023e974..6d527eece 100644 --- a/src/plugins/gramplet/Notes.py +++ b/src/plugins/gramplet/Notes.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 @@ -248,6 +249,29 @@ class SourceNotes(Notes): self.clear_text() self.set_has_data(False) +class CitationNotes(Notes): + """ + Displays the notes for a Citation. + """ + def db_changed(self): + self.dbstate.db.connect('citation-update', self.update) + self.connect_signal('Citation', self.update) + self.update() + + def update_has_data(self): + active_handle = self.get_active('Citation') + active = self.dbstate.db.get_citation_from_handle(active_handle) + self.set_has_data(self.get_has_data(active)) + + def main(self): + active_handle = self.get_active('Citation') + active = self.dbstate.db.get_citation_from_handle(active_handle) + if active: + self.get_notes(active) + else: + self.clear_text() + self.set_has_data(False) + class RepositoryNotes(Notes): """ Displays the notes for a repository. diff --git a/src/plugins/gramplet/PopulateGramplet.gpr.py b/src/plugins/gramplet/PopulateGramplet.gpr.py new file mode 100644 index 000000000..b096df8cb --- /dev/null +++ b/src/plugins/gramplet/PopulateGramplet.gpr.py @@ -0,0 +1,41 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2009 Benny Malengier +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +# $Id$ + +#------------------------------------------------------------------------ +# +# Register Gramplet +# +#------------------------------------------------------------------------ +register(GRAMPLET, + id="Populate", + name=_("Populate data"), + description = _("Gramplet to populate database"), + version="2.0.0", + gramps_target_version="3.4", + status = STABLE, + fname="PopulateGramplet.py", + height=200, + gramplet = 'PopulateGramplet', + gramplet_title=_("Populate data"), + ) + + diff --git a/src/plugins/gramplet/PopulateGramplet.py b/src/plugins/gramplet/PopulateGramplet.py new file mode 100644 index 000000000..0168c8f93 --- /dev/null +++ b/src/plugins/gramplet/PopulateGramplet.py @@ -0,0 +1,131 @@ +# Gramps - a GTK+/GNOME based genealogy program +# +# 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$ + +""" +Gramplet that populates the database with sources and citations. +""" + +#------------------------------------------------------------------------ +# +# Python modules +# +#------------------------------------------------------------------------ +import logging +LOG = logging.getLogger(".citation") + +#------------------------------------------------------------------------ +# +# GRAMPS modules +# +#------------------------------------------------------------------------ +from gen.plug import Gramplet +from gen.ggettext import sgettext as _ +import DateHandler +from QuickReports import run_quick_report_by_name +import gen.lib +from gen.db import DbTxn + +#------------------------------------------------------------------------ +# +# Gramplet class +# +#------------------------------------------------------------------------ +class PopulateGramplet(Gramplet): + """ + Gramplet that populates the database with sources and citations. + """ + def init(self): + """ + Constructs the GUI, consisting of a message, an entry, and + a Run button. + """ + import gtk + # GUI setup: + self.set_tooltip(_("Enter a date, click Run")) + vbox = gtk.VBox() + hbox = gtk.HBox() + # label, entry + description = gtk.TextView() + description.set_wrap_mode(gtk.WRAP_WORD) + description.set_editable(False) + buffer = description.get_buffer() + buffer.set_text(_("Enter a valid number of sources and citations." + " This will create the requested number of sources," + " and for each source, will create the requested" + " number of citations.")) + label_sources = gtk.Label() + label_sources.set_text(_("Number of sources") + ":") + self.num_sources = gtk.Entry() + label_citations = gtk.Label() + label_citations.set_text(_("Number of citations") + ":") + self.num_citations = gtk.Entry() + button = gtk.Button(_("Run")) + button.connect("clicked", self.run) + ##self.filter = + hbox.pack_start(label_sources, False) + hbox.pack_start(self.num_sources, True) + hbox.pack_start(label_citations, False) + hbox.pack_start(self.num_citations, True) + vbox.pack_start(description, True) + vbox.pack_start(hbox, False) + vbox.pack_start(button, False) + self.gui.get_container_widget().remove(self.gui.textview) + self.gui.get_container_widget().add_with_viewport(vbox) + vbox.show_all() + + def post_init(self): + self.disconnect("active-changed") + + def run(self, obj): + """ + Method that is run when you click the Run button. + The date is retrieved from the entry box, parsed as a date, + and then handed to the quick report. + """ + num_sources_text = self.num_sources.get_text() + num_sources = int(num_sources_text) + num_citations_text = self.num_citations.get_text() + num_citations = int(num_citations_text) + + LOG.debug("sources %04d citations %04d" % (num_sources, + num_citations)) + + source = gen.lib.Source() + citation = gen.lib.Citation() + db = self.gui.dbstate.db + + db.disable_signals() + with DbTxn('Populate citations', db) as trans: + for i in range(num_sources): + source.gramps_id = None + source.handle = None + source.title = "Source %04d" % (i + 1) + source_handle = db.add_source(source, trans) + + for j in range(num_citations): + citation.gramps_id = None + citation.handle = None + citation.source_handle = source_handle + citation.page = "Page %04d" % (j + 1) + db.add_citation(citation, trans) + LOG.debug("sources and citations added") + db.enable_signals() + db.request_rebuild() + diff --git a/src/plugins/gramplet/QuickViewGramplet.py b/src/plugins/gramplet/QuickViewGramplet.py index a4ea56522..a1d7ce517 100644 --- a/src/plugins/gramplet/QuickViewGramplet.py +++ b/src/plugins/gramplet/QuickViewGramplet.py @@ -1,6 +1,7 @@ # Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 2007-2009 Douglas S. Blank +# 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 @@ -36,7 +37,8 @@ from QuickReports import run_quick_report_by_name, get_quick_report_list from gen.plug import (CATEGORY_QR_PERSON, CATEGORY_QR_FAMILY, CATEGORY_QR_EVENT, CATEGORY_QR_SOURCE, CATEGORY_QR_NOTE, CATEGORY_QR_MISC, CATEGORY_QR_PLACE, CATEGORY_QR_MEDIA, - CATEGORY_QR_REPOSITORY) + CATEGORY_QR_REPOSITORY, CATEGORY_QR_CITATION, + CATEGORY_QR_SOURCE_OR_CITATION) #------------------------------------------------------------------------ # @@ -52,6 +54,7 @@ class QuickViewGramplet(Gramplet): self.connect_signal('Event', self._active_changed) self.connect_signal('Place', self._active_changed) self.connect_signal('Source', self._active_changed) + self.connect_signal('Citation', self._active_changed) self.connect_signal('Repository', self._active_changed) self.connect_signal('Media', self._active_changed) self.connect_signal('Note', self._active_changed) @@ -108,6 +111,7 @@ class QuickViewGramplet(Gramplet): ("Place", _("Place")), ("Repository", _("Repository")), ("Source", _("Source")), + ("Citation", _("Citation")), ]: type_list.add_item(item[0], item[1]) # Add particular lists: @@ -129,6 +133,8 @@ class QuickViewGramplet(Gramplet): "Family": CATEGORY_QR_FAMILY, "Event": CATEGORY_QR_EVENT, "Source": CATEGORY_QR_SOURCE, + "Citation": CATEGORY_QR_CITATION, + "Source or Citation": CATEGORY_QR_SOURCE_OR_CITATION, "Place": CATEGORY_QR_PLACE, "Media": CATEGORY_QR_MEDIA, "Note": CATEGORY_QR_NOTE, diff --git a/src/plugins/gramplet/bottombar.gpr.py b/src/plugins/gramplet/bottombar.gpr.py index 77a47f1bc..30df72f07 100644 --- a/src/plugins/gramplet/bottombar.gpr.py +++ b/src/plugins/gramplet/bottombar.gpr.py @@ -2,6 +2,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 @@ -217,6 +218,20 @@ register(GRAMPLET, navtypes=["Source"], ) +register(GRAMPLET, + id="Citation Gallery", + name=_("Citation Gallery"), + description = _("Gramplet showing media objects for a citation"), + version="1.0.0", + gramps_target_version="3.4", + status = STABLE, + fname="Gallery.py", + height=200, + gramplet = 'CitationGallery', + gramplet_title=_("Gallery"), + navtypes=["Citation"], + ) + register(GRAMPLET, id="Person Attributes", name=_("Person Attributes"), @@ -343,6 +358,20 @@ register(GRAMPLET, navtypes=["Source"], ) +register(GRAMPLET, + id="Citation Notes", + name=_("Citation Notes"), + description = _("Gramplet showing the notes for a citation"), + version="1.0.0", + gramps_target_version="3.4", + status = STABLE, + fname="Notes.py", + height=200, + gramplet = 'CitationNotes', + gramplet_title=_("Notes"), + navtypes=["Citation"], + ) + register(GRAMPLET, id="Repository Notes", name=_("Repository Notes"), @@ -372,72 +401,72 @@ register(GRAMPLET, ) register(GRAMPLET, - id="Person Sources", - name=_("Person Sources"), - description = _("Gramplet showing the sources for a person"), + id="Person Citations", + name=_("Person Citations"), + description = _("Gramplet showing the citations for a person"), version="1.0.0", gramps_target_version="3.4", status = STABLE, - fname="Sources.py", + fname="Citations.py", height=200, - gramplet = 'PersonSources', - gramplet_title=_("Sources"), + gramplet = 'PersonCitations', + gramplet_title=_("Citations"), navtypes=["Person"], ) register(GRAMPLET, - id="Event Sources", - name=_("Event Sources"), - description = _("Gramplet showing the sources for an event"), + id="Event Citations", + name=_("Event Citations"), + description = _("Gramplet showing the citations for an event"), version="1.0.0", gramps_target_version="3.4", status = STABLE, - fname="Sources.py", + fname="Citations.py", height=200, - gramplet = 'EventSources', - gramplet_title=_("Sources"), + gramplet = 'EventCitations', + gramplet_title=_("Citations"), navtypes=["Event"], ) register(GRAMPLET, - id="Family Sources", - name=_("Family Sources"), - description = _("Gramplet showing the sources for a family"), + id="Family Citations", + name=_("Family Citations"), + description = _("Gramplet showing the citations for a family"), version="1.0.0", gramps_target_version="3.4", status = STABLE, - fname="Sources.py", + fname="Citations.py", height=200, - gramplet = 'FamilySources', - gramplet_title=_("Sources"), + gramplet = 'FamilyCitations', + gramplet_title=_("Citations"), navtypes=["Family"], ) register(GRAMPLET, - id="Place Sources", - name=_("Place Sources"), - description = _("Gramplet showing the sources for a place"), + id="Place Citations", + name=_("Place Citations"), + description = _("Gramplet showing the citations for a place"), version="1.0.0", gramps_target_version="3.4", status = STABLE, - fname="Sources.py", + fname="Citations.py", height=200, - gramplet = 'PlaceSources', - gramplet_title=_("Sources"), + gramplet = 'PlaceCitations', + gramplet_title=_("Citations"), navtypes=["Place"], ) register(GRAMPLET, - id="Media Sources", - name=_("Media Sources"), - description = _("Gramplet showing the sources for a media object"), + id="Media Citations", + name=_("Media Citations"), + description = _("Gramplet showing the citations for a media object"), version="1.0.0", gramps_target_version="3.4", status = STABLE, - fname="Sources.py", + fname="Citations.py", height=200, - gramplet = 'MediaSources', - gramplet_title=_("Sources"), + gramplet = 'MediaCitations', + gramplet_title=_("Citations"), navtypes=["Media"], ) @@ -539,6 +568,20 @@ register(GRAMPLET, navtypes=["Source"], ) +register(GRAMPLET, + id="Citation Backlinks", + name=_("Citation Backlinks"), + description = _("Gramplet showing the backlinks for a citation"), + version="1.0.0", + gramps_target_version="3.4", + status = STABLE, + fname="Backlinks.py", + height=200, + gramplet = 'CitationBacklinks', + gramplet_title=_("References"), + navtypes=["Citation"], + ) + register(GRAMPLET, id="Repository Backlinks", name=_("Repository Backlinks"), @@ -637,6 +680,20 @@ register(GRAMPLET, navtypes=["Source"], ) +register(GRAMPLET, + id="Citation Filter", + name=_("Citation Filter"), + description = _("Gramplet providing a citation filter"), + version="1.0.0", + gramps_target_version="3.4", + status = STABLE, + fname="Filter.py", + height=200, + gramplet = 'CitationFilter', + gramplet_title=_("Filter"), + navtypes=["Citation"], + ) + register(GRAMPLET, id="Place Filter", name=_("Place Filter"), diff --git a/src/plugins/import/ImportCsv.py b/src/plugins/import/ImportCsv.py index 78e6cbb6d..2f2357f40 100644 --- a/src/plugins/import/ImportCsv.py +++ b/src/plugins/import/ImportCsv.py @@ -5,6 +5,7 @@ # Copyright (C) 2000-2007 Donald N. Allingham # Copyright (C) 2008 Raphael Ackerman # Copyright (C) 2008 Brian G. Matherly +# 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 @@ -482,17 +483,7 @@ class CSVParser(object): if source: # add, if new dummy_new, source = self.get_or_create_source(source) - source_refs = child.get_source_references() - found = 0 - for ref in source_refs: - LOG.debug("child: %s looking for ref: %s", ref.ref, - source.get_handle()) - if ref.ref == source.get_handle(): - found = 1 - if not found: - sref = gen.lib.SourceRef() - sref.set_reference_handle(source.get_handle()) - child.add_source_reference(sref) + self.find_and_set_citation(child, source) # put note on child if note: # append notes, if previous notes @@ -695,17 +686,7 @@ class CSVParser(object): if source: # add, if new new, source = self.get_or_create_source(source) - source_refs = person.get_source_references() - found = 0 - for ref in source_refs: - LOG.debug("person: %s looking for ref: %s", ref.ref, - source.get_handle()) - if ref.ref == source.get_handle(): - found = 1 - if not found: - sref = gen.lib.SourceRef() - sref.set_reference_handle(source.get_handle()) - person.add_source_reference(sref) + self.find_and_set_citation(person, source) self.db.commit_person(person, self.trans) def get_or_create_family(self, family_ref, husband, wife): @@ -772,17 +753,7 @@ class CSVParser(object): if place: event.set_place_handle(place.get_handle()) if source: - source_refs = event.get_source_references() - found = 0 - for ref in source_refs: - LOG.debug("get_or_create_event: %s looking for ref: %s", - ref.ref, source.get_handle()) - if ref.ref == source.get_handle(): - found = 1 - if not found: - sref = gen.lib.SourceRef() - sref.set_reference_handle(source.get_handle()) - event.add_source_reference(sref) + self.find_and_set_citation(event, source) self.db.commit_event(event, self.trans) LOG.debug(" returning existing event") return (0, event) @@ -796,16 +767,7 @@ class CSVParser(object): if place: event.set_place_handle(place.get_handle()) if source: - source_refs = event.get_source_references() - found = 0 - for ref in source_refs: - LOG.debug("%s looking for ref: %s", ref.ref, source.get_handle()) - if ref.ref == source.get_handle(): - found = 1 - if not found: - sref = gen.lib.SourceRef() - sref.set_reference_handle(source.get_handle()) - event.add_source_reference(sref) + self.find_and_set_citation(event, source) self.db.add_event(event, self.trans) return (1, event) @@ -836,8 +798,35 @@ class CSVParser(object): for source_handle in source_list: source = self.db.get_source_from_handle(source_handle) if source.get_title() == source_text: + LOG.debug(" returning existing source") return (0, source) + LOG.debug(" creating source") source = gen.lib.Source() source.set_title(source_text) self.db.add_source(source, self.trans) return (1, source) + + def find_and_set_citation(self, obj, source): + # look for the source in the existing citations for the object + LOG.debug("find_and_set_citation: looking for source: %s", + source.get_gramps_id()) + for citation_handle in obj.get_citation_list(): + citation = self.db.get_citation_from_handle(citation_handle) + LOG.debug("find_and_set_citation: existing citation: %s", + citation.get_gramps_id()) + poss_source = self.db.get_source_from_handle( + citation.get_reference_handle()) + LOG.debug(" compare source %s == %s", source.get_gramps_id(), + poss_source.get_gramps_id()) + if poss_source.get_handle() == source.get_handle(): + # The source is already cited + LOG.debug(" source already cited") + return + # we couldn't find an appropriate citation, so we have to create one. + citation = gen.lib.Citation() + LOG.debug(" creating citation") + citation.set_reference_handle(source.get_handle()) + self.db.add_citation(citation, self.trans) + LOG.debug(" created citation, citation %s %s" % + (citation, citation.get_gramps_id())) + obj.add_citation(citation.get_handle()) diff --git a/src/plugins/import/ImportGedcom.py b/src/plugins/import/ImportGedcom.py index 3f213e349..06646ac07 100644 --- a/src/plugins/import/ImportGedcom.py +++ b/src/plugins/import/ImportGedcom.py @@ -2,6 +2,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 # it under the terms of the GNU General Public License as published by diff --git a/src/plugins/import/ImportGeneWeb.py b/src/plugins/import/ImportGeneWeb.py index 51a382b36..3e0891c7e 100644 --- a/src/plugins/import/ImportGeneWeb.py +++ b/src/plugins/import/ImportGeneWeb.py @@ -256,14 +256,12 @@ class GeneWebParser(object): self.current_mode = None return None - - def read_source_line(self,line,fields): if not self.current_family: LOG.warn("Unknown family of child in line %d!" % self.lineno) return None source = self.get_or_create_source(self.decode(fields[1])) - self.current_family.add_source_reference(source) + self.current_family.add_citation(source.get_handle()) self.db.commit_family(self.current_family,self.trans) return None @@ -341,7 +339,7 @@ class GeneWebParser(object): birth.set_place_handle(self.current_child_birthplace_handle) self.db.commit_event(birth,self.trans) if self.current_child_source_handle: - child.add_source_reference(self.current_child_source_handle) + child.add_citation(self.current_child_source_handle) self.db.commit_person(child,self.trans) else: break @@ -745,7 +743,7 @@ class GeneWebParser(object): person.add_alternate_name(name) if source: - person.add_source_reference(source) + person.add_citation(source.get_handle()) if birth_date or birth_place or birth_source: birth = self.create_event(gen.lib.EventType.BIRTH, None, birth_date, birth_place, birth_source) @@ -833,7 +831,7 @@ class GeneWebParser(object): if place: event.set_place_handle(place.get_handle()) if source: - event.add_source_reference(source) + event.add_citation(source.get_handle()) self.db.add_event(event,self.trans) self.db.commit_event(event,self.trans) return event @@ -872,9 +870,11 @@ class GeneWebParser(object): self.db.add_source(source,self.trans) self.db.commit_source(source,self.trans) self.skeys[source_name] = source.get_handle() - sref = gen.lib.SourceRef() - sref.set_reference_handle(source.get_handle()) - return sref + citation = gen.lib.Citation() + citation.set_reference_handle(source.get_handle()) + self.db.add_citation(citation, self.trans) + self.db.commit_citation(citation, self.trans) + return citation def decode(self,s): s = s.replace('_',' ') diff --git a/src/plugins/import/ImportXml.py b/src/plugins/import/ImportXml.py index 874abf14d..8dba796f0 100644 --- a/src/plugins/import/ImportXml.py +++ b/src/plugins/import/ImportXml.py @@ -3,8 +3,9 @@ # # Copyright (C) 2000-2007 Donald N. Allingham # Copyright (C) 2009 Douglas S. Blank -# Copyright (C) 2010 Nick Hall +# Copyright (C) 2010-2011 Nick Hall # Copyright (C) 2011 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 @@ -52,7 +53,7 @@ import DateHandler from gen.display.name import displayer as name_displayer from gen.db.dbconst import (PERSON_KEY, FAMILY_KEY, SOURCE_KEY, EVENT_KEY, MEDIA_KEY, PLACE_KEY, REPOSITORY_KEY, NOTE_KEY, - TAG_KEY) + TAG_KEY, CITATION_KEY) from gen.updatecallback import UpdateCallback import const import libgrampsxml @@ -190,7 +191,7 @@ class ImportInfo(object): Class object that can hold information about the import """ keyorder = [PERSON_KEY, FAMILY_KEY, SOURCE_KEY, EVENT_KEY, MEDIA_KEY, - PLACE_KEY, REPOSITORY_KEY, NOTE_KEY, TAG_KEY] + PLACE_KEY, REPOSITORY_KEY, NOTE_KEY, TAG_KEY, CITATION_KEY] key2data = { PERSON_KEY : 0, FAMILY_KEY : 1, @@ -200,7 +201,8 @@ class ImportInfo(object): PLACE_KEY: 5, REPOSITORY_KEY: 6, NOTE_KEY: 7, - TAG_KEY: 8 + TAG_KEY: 8, + CITATION_KEY: 9 } def __init__(self): @@ -209,8 +211,8 @@ class ImportInfo(object): This creates the datastructures to hold info """ - self.data_mergecandidate = [{}, {}, {}, {}, {}, {}, {}, {}, {}] - self.data_newobject = [0] * 9 + self.data_mergecandidate = [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}] + self.data_newobject = [0] * 10 self.data_relpath = False def add(self, category, key, obj, sec_obj=None): @@ -260,6 +262,9 @@ class ImportInfo(object): 'id': obj.gramps_id, 'id2': sec_obj.gramps_id} elif key == TAG_KEY: pass # Tags can't be merged + elif key == CITATION_KEY: + return _(" Citation %(id)s with %(id2)s\n") % { + 'id': obj.gramps_id, 'id2': sec_obj.gramps_id} def info_text(self): """ @@ -275,6 +280,7 @@ class ImportInfo(object): REPOSITORY_KEY : _(' Repositories: %d\n'), NOTE_KEY : _(' Notes: %d\n'), TAG_KEY : _(' Tags: %d\n'), + CITATION_KEY : _(' Citations: %d\n'), } txt = _("Number of new objects imported:\n") for key in self.keyorder: @@ -453,8 +459,9 @@ class GrampsParser(UpdateCallback): self.person = None self.family = None self.address = None + self.citation = None + self.in_old_sourceref = False self.source = None - self.source_ref = None self.attribute = None self.placeobj = None self.locations = 0 @@ -500,6 +507,7 @@ class GrampsParser(UpdateCallback): self.idswap = {} self.fidswap = {} self.eidswap = {} + self.cidswap = {} self.sidswap = {} self.pidswap = {} self.oidswap = {} @@ -540,10 +548,14 @@ class GrampsParser(UpdateCallback): "childof": (self.start_childof, None), "childref": (self.start_childref, self.stop_childref), "personref": (self.start_personref, self.stop_personref), + "citation": (self.start_citation, self.stop_citation), + "citationref": (self.start_citationref, None), + "citations": (None, None), "city": (None, self.stop_city), "county": (None, self.stop_county), "country": (None, self.stop_country), "comment": (None, self.stop_comment), + "confidence": (None, self.stop_confidence), "created": (self.start_created, None), "ref": (None, self.stop_ref), "database": (self.start_database, self.stop_database), @@ -576,6 +588,7 @@ class GrampsParser(UpdateCallback): "objref": (self.start_objref, self.stop_objref), "object": (self.start_object, self.stop_object), "file": (self.start_file, None), + "page": (None, self.stop_page), "place": (self.start_place, self.stop_place), "dateval": (self.start_dateval, None), "daterange": (self.start_daterange, None), @@ -661,6 +674,7 @@ class GrampsParser(UpdateCallback): "event": self.db.get_raw_event_data, "place": self.db.get_raw_place_data, "source": self.db.get_raw_source_data, + "citation": self.db.get_raw_citation_data, "repository": self.db.get_raw_repository_data, "media": self.db.get_raw_object_data, "note": self.db.get_raw_note_data, @@ -688,6 +702,7 @@ class GrampsParser(UpdateCallback): "event": self.db.has_event_handle, "place": self.db.has_place_handle, "source": self.db.has_source_handle, + "citation": self.db.get_raw_citation_data, "repository": self.db.has_repository_handle, "media": self.db.has_object_handle, "note": self.db.has_note_handle, @@ -706,6 +721,7 @@ class GrampsParser(UpdateCallback): "event": self.db.add_event, "place": self.db.add_place, "source": self.db.add_source, + "citation": self.db.add_citation, "repository": self.db.add_repository, "media": self.db.add_object, "note": self.db.add_note}[target] @@ -1580,6 +1596,7 @@ class GrampsParser(UpdateCallback): if match: target = {"Person":"person", "Family":"family", "Event":"event", "Place":"place", "Source":"source", + "Citation":"citation", "Repository":"repository", "Media":"media", "Note":"note"}[str(match.group('object_class'))] if match.group('handle') in self.import_handles: @@ -1686,9 +1703,9 @@ class GrampsParser(UpdateCallback): self.note.format = int(attrs.get('format', gen.lib.Note.FLOWED)) # The order in this long if-then statement should reflect the # DTD: most deeply nested elements come first. - if self.source_ref: - self.note.type.set(gen.lib.NoteType.SOURCEREF) - self.note.private = self.source_ref.private + if self.citation: + self.note.type.set(gen.lib.NoteType.CITATION) + self.note.private = self.citation.private elif self.address: self.note.type.set(gen.lib.NoteType.ADDRESS) self.note.private = self.address.private @@ -1759,8 +1776,8 @@ class GrampsParser(UpdateCallback): # The order in this long if-then statement should reflect the # DTD: most deeply nested elements come first. - if self.source_ref: - self.source_ref.add_note(handle) + if self.citation: + self.citation.add_note(handle) elif self.address: self.address.add_note(handle) elif self.ord: @@ -1796,46 +1813,94 @@ class GrampsParser(UpdateCallback): elif self.repo: self.repo.add_note(handle) + def __add_citation(self, citation_handle): + """ + Add a citation to the object currently processed. + """ + if self.photo: + self.photo.add_citation(citation_handle) + elif self.ord: + self.ord.add_citation(citation_handle) + elif self.attribute: + self.attribute.add_citation(citation_handle) + elif self.object: + self.object.add_citation(citation_handle) + elif self.objref: + self.objref.add_citation(citation_handle) + elif self.event: + self.event.add_citation(citation_handle) + elif self.address: + self.address.add_citation(citation_handle) + elif self.name: + self.name.add_citation(citation_handle) + elif self.placeobj: + self.placeobj.add_citation(citation_handle) + elif self.childref: + self.childref.add_citation(citation_handle) + elif self.family: + self.family.add_citation(citation_handle) + elif self.personref: + self.personref.add_citation(citation_handle) + elif self.person: + self.person.add_citation(citation_handle) + + def start_citationref(self, attrs): + """ + Add a citation reference to the object currently processed. + """ + handle = self.inaugurate(attrs['hlink'], "citation", gen.lib.Citation) + + self.__add_citation(handle) + + def start_citation(self, attrs): + """ + Add a citation object to db if it doesn't exist yet and assign + id, privacy and changetime. + """ + self.update(self.p.CurrentLineNumber) + self.citation = gen.lib.Citation() + orig_handle = attrs['handle'].replace('_', '') + is_merge_candidate = (self.replace_import_handle and + self.db.has_citation_handle(orig_handle)) + self.inaugurate(orig_handle, "citation", self.citation) + gramps_id = self.legalize_id(attrs.get('id'), CITATION_KEY, + self.cidswap, self.db.cid2user_format, + self.db.find_next_citation_gramps_id) + self.citation.set_gramps_id(gramps_id) + if is_merge_candidate: + orig_citation = self.db.get_citation_from_handle(orig_handle) + self.info.add('merge-candidate', CITATION_KEY, orig_citation, + self.citation) + self.citation.private = bool(attrs.get("priv")) + self.citation.change = int(attrs.get('change', self.change)) + self.citation.confidence = self.conf # default + self.info.add('new-object', CITATION_KEY, self.citation) + def start_sourceref(self, attrs): """ Add a source reference to the object currently processed. """ - self.source_ref = gen.lib.SourceRef() if 'hlink' in attrs: handle = self.inaugurate(attrs['hlink'], "source", gen.lib.Source) else: handle = self.inaugurate_id(attrs.get('ref'), SOURCE_KEY, gen.lib.Source) - self.source_ref.ref = handle - self.source_ref.confidence = int(attrs.get("conf", self.conf)) - self.source_ref.private = bool(attrs.get("priv")) - - if self.photo: - self.photo.add_source_reference(self.source_ref) - elif self.ord: - self.ord.add_source_reference(self.source_ref) - elif self.attribute: - self.attribute.add_source_reference(self.source_ref) - elif self.object: - self.object.add_source_reference(self.source_ref) - elif self.objref: - self.objref.add_source_reference(self.source_ref) - elif self.event: - self.event.add_source_reference(self.source_ref) - elif self.address: - self.address.add_source_reference(self.source_ref) - elif self.name: - self.name.add_source_reference(self.source_ref) - elif self.placeobj: - self.placeobj.add_source_reference(self.source_ref) - elif self.childref: - self.childref.add_source_reference(self.source_ref) - elif self.family: - self.family.add_source_reference(self.source_ref) - elif self.personref: - self.personref.add_source_reference(self.source_ref) - elif self.person: - self.person.add_source_reference(self.source_ref) + + if self.citation: + self.citation.set_reference_handle(handle) + else: + # GRAMPS LEGACY: Prior to v1.5.0 there were no citation objects. + # We need to copy the contents of the old SourceRef into a new + # Citation object. + self.in_old_sourceref = True + + self.citation = gen.lib.Citation() + self.citation.set_reference_handle(handle) + self.citation.confidence = int(attrs.get("conf", self.conf)) + self.citation.private = bool(attrs.get("priv")) + + citation_handle = self.db.add_citation(self.citation, self.trans) + self.__add_citation(citation_handle) def start_source(self, attrs): """ @@ -2035,8 +2100,8 @@ class GrampsParser(UpdateCallback): self.start_compound_date(attrs, gen.lib.Date.MOD_SPAN) def start_compound_date(self, attrs, mode): - if self.source_ref: - date_value = self.source_ref.get_date_object() + if self.citation: + date_value = self.citation.get_date_object() elif self.ord: date_value = self.ord.get_date_object() elif self.object: @@ -2117,8 +2182,8 @@ class GrampsParser(UpdateCallback): newyear=newyear) def start_dateval(self, attrs): - if self.source_ref: - date_value = self.source_ref.get_date_object() + if self.citation: + date_value = self.citation.get_date_object() elif self.ord: date_value = self.ord.get_date_object() elif self.object: @@ -2196,8 +2261,8 @@ class GrampsParser(UpdateCallback): newyear=newyear) def start_datestr(self, attrs): - if self.source_ref: - date_value = self.source_ref.get_date_object() + if self.citation: + date_value = self.citation.get_date_object() elif self.ord: date_value = self.ord.get_date_object() elif self.object: @@ -2500,13 +2565,23 @@ class GrampsParser(UpdateCallback): self.source.title = tag def stop_sourceref(self, *tag): - self.source_ref = None + # if we are in an old style sourceref we need to commit the citation + if self.in_old_sourceref: + self.db.commit_citation(self.citation, self.trans, + self.citation.get_change_time()) + self.citation = None + self.in_old_sourceref = False def stop_source(self, *tag): self.db.commit_source(self.source, self.trans, self.source.get_change_time()) self.source = None + def stop_citation(self, *tag): + self.db.commit_citation(self.citation, self.trans, + self.citation.get_change_time()) + self.citation = None + def stop_sauthor(self, tag): self.source.author = tag @@ -2535,7 +2610,16 @@ class GrampsParser(UpdateCallback): self.address.set_postal_code(tag) def stop_spage(self, tag): - self.source_ref.set_page(tag) + # Valid for version <= 1.4.0 + self.citation.set_page(tag) + + def stop_page(self, tag): + # Valid for version >= 1.5.0 + self.citation.set_page(tag) + + def stop_confidence(self, tag): + # Valid for version >= 1.5.0 + self.citation.set_confidence_level(int(tag)) def stop_lds_ord(self, *tag): self.ord = None @@ -2556,14 +2640,14 @@ class GrampsParser(UpdateCallback): # So we create a new note, commit, and add the handle to note list. note = gen.lib.Note() note.handle = Utils.create_id() - note.private = self.source_ref.private + note.private = self.citation.private note.set(text) note.type.set(gen.lib.NoteType.SOURCE_TEXT) self.db.add_note(note, self.trans) #set correct change time self.db.commit_note(note, self.trans, self.change) self.info.add('new-object', NOTE_KEY, note) - self.source_ref.add_note(note.handle) + self.citation.add_note(note.handle) def stop_scomments(self, tag): if self.use_p: @@ -2573,14 +2657,14 @@ class GrampsParser(UpdateCallback): text = tag note = gen.lib.Note() note.handle = Utils.create_id() - note.private = self.source_ref.private + note.private = self.citation.private note.set(text) - note.type.set(gen.lib.NoteType.SOURCEREF) + note.type.set(gen.lib.NoteType.CITATION) self.db.add_note(note, self.trans) #set correct change time self.db.commit_note(note, self.trans, self.change) self.info.add('new-object', NOTE_KEY, note) - self.source_ref.add_note(note.handle) + self.citation.add_note(note.handle) def stop_last(self, tag): if self.surname: diff --git a/src/plugins/lib/libgedcom.py b/src/plugins/lib/libgedcom.py index 29fb65dfe..632536691 100644 --- a/src/plugins/lib/libgedcom.py +++ b/src/plugins/lib/libgedcom.py @@ -4,6 +4,7 @@ # Copyright (C) 2000-2007 Donald N. Allingham # Copyright (C) 2009-2010 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 @@ -2037,19 +2038,19 @@ class GedcomParser(UpdateCallback): TOKEN_RNOTE : self.__person_asso_note, } - self.srcref_parse_tbl = { - TOKEN_PAGE : self.__srcref_page, - TOKEN_DATE : self.__srcref_date, - TOKEN_DATA : self.__srcref_data, - TOKEN_OBJE : self.__srcref_obje, - TOKEN_REFN : self.__srcref_refn, + self.citation_parse_tbl = { + TOKEN_PAGE : self.__citation_page, + TOKEN_DATE : self.__citation_date, + TOKEN_DATA : self.__citation_data, + TOKEN_OBJE : self.__citation_obje, + TOKEN_REFN : self.__citation_refn, TOKEN_EVEN : self.__ignore, TOKEN_IGNORE : self.__ignore, TOKEN__LKD : self.__ignore, - TOKEN_QUAY : self.__srcref_quay, - TOKEN_NOTE : self.__srcref_note, - TOKEN_RNOTE : self.__srcref_note, - TOKEN_TEXT : self.__srcref_data_text, + TOKEN_QUAY : self.__citation_quay, + TOKEN_NOTE : self.__citation_note, + TOKEN_RNOTE : self.__citation_note, + TOKEN_TEXT : self.__citation_data_text, } self.object_parse_tbl = { @@ -2218,11 +2219,11 @@ class GedcomParser(UpdateCallback): TOKEN_NOTE : self.__optional_note, } - self.srcref_data_tbl = { - TOKEN_DATE : self.__srcref_data_date, - TOKEN_TEXT : self.__srcref_data_text, - TOKEN_RNOTE : self.__srcref_data_note, - TOKEN_NOTE : self.__srcref_data_note, + self.citation_data_tbl = { + TOKEN_DATE : self.__citation_data_date, + TOKEN_TEXT : self.__citation_data_text, + TOKEN_RNOTE : self.__citation_data_note, + TOKEN_NOTE : self.__citation_data_note, } self.header_sour = { @@ -2747,8 +2748,8 @@ class GedcomParser(UpdateCallback): @param state: The current state @type state: CurrentState """ - source_ref = self.handle_source(line, state.level) - state.person.add_source_reference(source_ref) + citation_handle = self.handle_source(line, state.level) + state.person.add_citation(citation_handle) def __person_attr(self, line, state): """ @@ -3354,8 +3355,8 @@ class GedcomParser(UpdateCallback): @param state: The current state @type state: CurrentState """ - sref = self.handle_source(line, state.level) - state.name.add_source_reference(sref) + citation_handle = self.handle_source(line, state.level) + state.name.add_citation(citation_handle) def __ignore(self, line, state): """ @@ -3548,8 +3549,8 @@ class GedcomParser(UpdateCallback): @param state: The current state @type state: CurrentState """ - srcref = self.handle_source(line, state.level) - state.lds_ord.add_source_reference(srcref) + citation_handle = self.handle_source(line, state.level) + state.lds_ord.add_citation(citation_handle) def __lds_note(self, line, state): """ @@ -3681,8 +3682,8 @@ class GedcomParser(UpdateCallback): @param state: The current state @type state: CurrentState """ - source_ref = self.handle_source(line, state.level) - state.person.add_source_reference(source_ref) + citation_handle = self.handle_source(line, state.level) + state.person.add_citation(citation_handle) def __person_fams(self, line, state): """ @@ -3765,7 +3766,7 @@ class GedcomParser(UpdateCallback): @param state: The current state @type state: CurrentState """ - state.ref.add_source_reference(self.handle_source(line, state.level)) + state.ref.add_citation(self.handle_source(line, state.level)) def __person_asso_note(self, line, state): """ @@ -4040,8 +4041,8 @@ class GedcomParser(UpdateCallback): @param state: The current state @type state: CurrentState """ - source_ref = self.handle_source(line, state.level) - state.family.add_source_reference(source_ref) + citation_handle = self.handle_source(line, state.level) + state.family.add_citation(citation_handle) def __family_object(self, line, state): """ @@ -4369,7 +4370,7 @@ class GedcomParser(UpdateCallback): @param state: The current state @type state: CurrentState """ - state.place.add_source_reference(self.handle_source(line, state.level)) + state.place.add_citation(self.handle_source(line, state.level)) def __place_map(self, line, state): """ @@ -4503,7 +4504,7 @@ class GedcomParser(UpdateCallback): @param state: The current state @type state: CurrentState """ - state.event.add_source_reference(self.handle_source(line, state.level)) + state.event.add_citation(self.handle_source(line, state.level)) def __event_cause(self, line, state): """ @@ -4531,7 +4532,7 @@ class GedcomParser(UpdateCallback): @param state: The current state @type state: CurrentState """ - state.attr.add_source_reference(self.handle_source(line, state.level)) + state.attr.add_citation(self.handle_source(line, state.level)) def __event_age(self, line, state): """ @@ -4821,7 +4822,7 @@ class GedcomParser(UpdateCallback): @param state: The current state @type state: CurrentState """ - state.addr.add_source_reference(self.handle_source(line, state.level)) + state.addr.add_citation(self.handle_source(line, state.level)) def __address_note(self, line, state): """ @@ -4834,7 +4835,7 @@ class GedcomParser(UpdateCallback): """ self.__parse_note(line, state.addr, state.level+1) - def __srcref_page(self, line, state): + def __citation_page(self, line, state): """ Parses the PAGE line of an SOUR instance tag @@ -4843,9 +4844,9 @@ class GedcomParser(UpdateCallback): @param state: The current state @type state: CurrentState """ - state.src_ref.set_page(line.data) + state.citation.set_page(line.data) - def __srcref_date(self, line, state): + def __citation_date(self, line, state): """ Parses the DATE line of an SOUR instance tag @@ -4854,9 +4855,9 @@ class GedcomParser(UpdateCallback): @param state: The current state @type state: CurrentState """ - state.src_ref.set_date_object(line.data) + state.citation.set_date_object(line.data) - def __srcref_data(self, line, state): + def __citation_data(self, line, state): """ Parses the DATA line of an SOUR instance tag @@ -4866,12 +4867,12 @@ class GedcomParser(UpdateCallback): @type state: CurrentState """ sub_state = CurrentState(level=state.level+1) - sub_state.src_ref = state.src_ref + sub_state.citation = state.citation - self.__parse_level(sub_state, self.srcref_data_tbl, self.__undefined) + self.__parse_level(sub_state, self.citation_data_tbl, self.__undefined) - def __srcref_data_date(self, line, state): - state.src_ref.set_date_object(line.data) + def __citation_data_date(self, line, state): + state.citation.set_date_object(line.data) def __source_text(self, line, state): note = gen.lib.Note() @@ -4883,7 +4884,7 @@ class GedcomParser(UpdateCallback): state.source.add_note(note.get_handle()) - def __srcref_data_text(self, line, state): + def __citation_data_text(self, line, state): note = gen.lib.Note() note.set(line.data) gramps_id = self.dbase.find_next_note_gramps_id() @@ -4891,12 +4892,12 @@ class GedcomParser(UpdateCallback): note.set_type(gen.lib.NoteType.SOURCE_TEXT) self.dbase.add_note(note, self.trans) - state.src_ref.add_note(note.get_handle()) + state.citation.add_note(note.get_handle()) - def __srcref_data_note(self, line, state): - self.__parse_note(line, state.src_ref, state.level) + def __citation_data_note(self, line, state): + self.__parse_note(line, state.citation, state.level) - def __srcref_obje(self, line, state): + def __citation_obje(self, line, state): """ Parses the OBJE line of an SOUR instance tag @@ -4908,12 +4909,10 @@ class GedcomParser(UpdateCallback): if line.data and line.data[0] == '@': self.__not_recognized(line, state.level) else: - src = self.dbase.get_source_from_handle(state.handle) (form, filename, title, note) = self.__obje(state.level) - self.build_media_object(src, form, filename, title, note) - self.dbase.commit_source(src, self.trans) + self.build_media_object(state.citation, form, filename, title, note) - def __srcref_refn(self, line, state): + def __citation_refn(self, line, state): """ Parses the REFN line of an SOUR instance tag @@ -4924,7 +4923,7 @@ class GedcomParser(UpdateCallback): """ self.__skip_subordinate_levels(state.level+1) - def __srcref_quay(self, line, state): + def __citation_quay(self, line, state): """ Parses the QUAY line of an SOUR instance tag @@ -4940,11 +4939,11 @@ class GedcomParser(UpdateCallback): # If value is greater than 3, cap at 3 val = min(val, 3) if val > 1: - state.src_ref.set_confidence_level(val+1) + state.citation.set_confidence_level(val+1) else: - state.src_ref.set_confidence_level(val) + state.citation.set_confidence_level(val) - def __srcref_note(self, line, state): + def __citation_note(self, line, state): """ Parses the NOTE line of an SOUR instance tag @@ -4953,7 +4952,7 @@ class GedcomParser(UpdateCallback): @param state: The current state @type state: CurrentState """ - self.__parse_note(line, state.src_ref, state.level+1) + self.__parse_note(line, state.citation, state.level+1) def __parse_source(self, name, level): """ @@ -5301,7 +5300,7 @@ class GedcomParser(UpdateCallback): @param state: The current state @type state: CurrentState """ - state.attr.add_source_reference(self.handle_source(line, state.level)) + state.attr.add_citation(self.handle_source(line, state.level)) def __person_attr_place(self, line, state): """ @@ -5732,14 +5731,14 @@ class GedcomParser(UpdateCallback): self.nid2id[new_note.gramps_id] = new_note.handle self.__skip_subordinate_levels(level) - def __parse_source_reference(self, src_ref, level, handle): + def __parse_source_reference(self, citation, level, handle): """ Read the data associated with a SOUR reference. """ state = CurrentState(level=level+1) - state.src_ref = src_ref + state.citation = citation state.handle = handle - self.__parse_level(state, self.srcref_parse_tbl, self.__ignore) + self.__parse_level(state, self.citation_parse_tbl, self.__ignore) def __parse_header_head(self): """ @@ -5769,7 +5768,7 @@ class GedcomParser(UpdateCallback): Handle the specified source, building a source reference to the object. """ - source_ref = gen.lib.SourceRef() + citation = gen.lib.Citation() if line.data and line.data[0] != "@": title = line.data handle = self.inline_srcs.get(title, Utils.create_id()) @@ -5780,9 +5779,10 @@ class GedcomParser(UpdateCallback): else: src = self.__find_or_create_source(self.sid_map[line.data]) self.dbase.commit_source(src, self.trans) - self.__parse_source_reference(source_ref, level, src.handle) - source_ref.set_reference_handle(src.handle) - return source_ref + self.__parse_source_reference(citation, level, src.handle) + citation.set_reference_handle(src.handle) + self.dbase.add_citation(citation, self.trans) + return citation.handle def __parse_change(self, line, obj, level): """ @@ -5954,9 +5954,10 @@ class GedcomParser(UpdateCallback): Add the default source to the object. """ if self.use_def_src and len(obj.get_source_references()) == 0: - sref = gen.lib.SourceRef() - sref.set_reference_handle(self.def_src.handle) - obj.add_source_reference(sref) + citation = gen.lib.Citation() + citation.set_reference_handle(self.def_src.handle) + self.dbase.add_citation(citation, self.trans) + obj.add_citation(citation.handle) def __subm_name(self, line, state): """ diff --git a/src/plugins/lib/libgrampsxml.py b/src/plugins/lib/libgrampsxml.py index cc2f79869..728474c9e 100644 --- a/src/plugins/lib/libgrampsxml.py +++ b/src/plugins/lib/libgrampsxml.py @@ -35,5 +35,5 @@ # Public Constants # #------------------------------------------------------------------------ -GRAMPS_XML_VERSION = "1.4.0" +GRAMPS_XML_VERSION = "1.5.0" diff --git a/src/plugins/lib/libhtmlconst.py b/src/plugins/lib/libhtmlconst.py index 01c7809a5..2f23edefe 100644 --- a/src/plugins/lib/libhtmlconst.py +++ b/src/plugins/lib/libhtmlconst.py @@ -123,7 +123,7 @@ _COPY_OPTIONS = [ openstreet_jsc = """ OpenLayers.Lang.setCode("%s"); - map = new OpenLayers.Map("map_canvas"); + map = new OpenLayers.Map("place_canvas"); var osm = new OpenLayers.Layer.OSM() map.addLayer(osm); @@ -155,7 +155,7 @@ function initialize() { mapTypeId: google.maps.MapTypeId.ROADMAP, center: myLatlng }; - var map = new google.maps.Map(document.getElementById("map_canvas"), mapOptions); + var map = new google.maps.Map(document.getElementById("place_canvas"), mapOptions); var marker = new google.maps.Marker({ map: map, diff --git a/src/plugins/lib/libmetadata.py b/src/plugins/lib/libmetadata.py index f792b0cca..2df19cd79 100644 --- a/src/plugins/lib/libmetadata.py +++ b/src/plugins/lib/libmetadata.py @@ -19,7 +19,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # -# $Id$ +# $Id: libmetadata.py 17931 2011-07-15 14:57:35Z nick-h $ # from ListModel import ListModel diff --git a/src/plugins/lib/libnarrate.py b/src/plugins/lib/libnarrate.py index 2a15e508b..ffe8f6e36 100644 --- a/src/plugins/lib/libnarrate.py +++ b/src/plugins/lib/libnarrate.py @@ -6,6 +6,7 @@ # Copyright (C) 2010 Jakim Friant # Copyright (C) 2011 Vlada Perić # Copyright (C) 2011 Matt Keenan +# 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 @@ -1366,11 +1367,11 @@ class Narrator(object): :type translate_text: callable(str) :param get_endnote_numbers: A callable to use for getting a string representing endnote numbers. - The function takes a :class:`~gen.lib.srcbase,SourceBase` instance. + The function takes a :class:`~gen.lib.CitationBase` instance. A typical return value from get_endnote_numbers() would be "2a" and would represent a reference to an endnote in a document. :type get_endnote_numbers: - callable( :class:`~gen.lib.srcbase,SourceBase` ) + callable( :class:`~gen.lib.CitationBase` ) """ self.__db = dbase self.__verbose = verbose diff --git a/src/plugins/lib/libpersonview.py b/src/plugins/lib/libpersonview.py index 5d39f2ae4..c4885dbb6 100644 --- a/src/plugins/lib/libpersonview.py +++ b/src/plugins/lib/libpersonview.py @@ -4,6 +4,7 @@ # Copyright (C) 2008 Gary Burton # Copyright (C) 2009-2010 Nick Hall # Copyright (C) 2010 Benny Malengier +# 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 @@ -450,7 +451,7 @@ class BasePersonView(ListView): "Person Gallery", "Person Events", "Person Children", - "Person Sources", + "Person Citations", "Person Notes", "Person Attributes", "Person Backlinks")) diff --git a/src/plugins/lib/libplaceview.py b/src/plugins/lib/libplaceview.py index 5a8d2c7e5..b17b7d591 100644 --- a/src/plugins/lib/libplaceview.py +++ b/src/plugins/lib/libplaceview.py @@ -3,6 +3,7 @@ # Copyright (C) 2001-2006 Donald N. Allingham # Copyright (C) 2008 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 @@ -428,7 +429,7 @@ class PlaceBaseView(ListView): return (("Place Filter",), ("Place Details", "Place Gallery", - "Place Sources", + "Place Citations", "Place Notes", "Place Backlinks")) diff --git a/src/plugins/quickview/References.py b/src/plugins/quickview/References.py index fbdf6d362..05708c88b 100644 --- a/src/plugins/quickview/References.py +++ b/src/plugins/quickview/References.py @@ -3,6 +3,7 @@ # # Copyright (C) 2000-2007 Donald N. Allingham # Copyright (C) 2007-2008 Brian G. Matherly +# 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 @@ -41,6 +42,8 @@ def get_ref(db, objclass, handle): ref = db.get_event_from_handle(handle) elif objclass == 'Source': ref = db.get_source_from_handle(handle) + elif objclass == 'Citation': + ref = db.get_citation_from_handle(handle) elif objclass == 'Place': ref = db.get_place_from_handle(handle) elif objclass == 'Note': @@ -83,6 +86,9 @@ run_person = lambda db, doc, obj: run(db, doc, obj, 'person', _("Person")) run_family = lambda db, doc, obj: run(db, doc, obj, 'family', _("Family")) run_event = lambda db, doc, obj: run(db, doc, obj, 'event', _("Event")) run_source = lambda db, doc, obj: run(db, doc, obj, 'source', _("Source")) +run_citation = lambda db, doc, obj: run(db, doc, obj, 'citation', _("Citation")) +run_source_or_citation = lambda db, doc, obj: run(db, doc, obj, + 'source or citation', _("Source or Citation")) run_place = lambda db, doc, obj: run(db, doc, obj, 'place', _("Place")) run_media = lambda db, doc, obj: run(db, doc, obj, 'media', _("Media")) run_note = lambda db, doc, obj: run(db, doc, obj, 'note', _("Note")) diff --git a/src/plugins/quickview/quickview.gpr.py b/src/plugins/quickview/quickview.gpr.py index c29355f67..68245ce5e 100644 --- a/src/plugins/quickview/quickview.gpr.py +++ b/src/plugins/quickview/quickview.gpr.py @@ -2,6 +2,7 @@ # Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 2009 Benny Malengier +# 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 @@ -202,6 +203,9 @@ refitems = [(CATEGORY_QR_PERSON, 'person', _("Person")), (CATEGORY_QR_PLACE, 'place', _("Place")), (CATEGORY_QR_MEDIA, 'media', _("Media")), (CATEGORY_QR_NOTE, 'note', _("Note")), + (CATEGORY_QR_CITATION, 'citation', _("Citation")), + (CATEGORY_QR_SOURCE_OR_CITATION, 'source or citation', + _("Source or Citation")) ] for (category, item, trans) in refitems: diff --git a/src/plugins/sidebar/categorysidebar.py b/src/plugins/sidebar/categorysidebar.py index 977f4d92a..6e4c881bb 100644 --- a/src/plugins/sidebar/categorysidebar.py +++ b/src/plugins/sidebar/categorysidebar.py @@ -5,6 +5,7 @@ # Copyright (C) 2008 Brian G. Matherly # Copyright (C) 2009 Benny Malengier # 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 @@ -69,7 +70,9 @@ CATEGORY_ICON = { 'Sources': 'gramps-source', 'Repositories': 'gramps-repository', 'Media': 'gramps-media', - 'Notes': 'gramps-notes'} + 'Notes': 'gramps-notes', + 'Citations': 'gramps-citation', +} #------------------------------------------------------------------------- # diff --git a/src/plugins/textreport/DetAncestralReport.py b/src/plugins/textreport/DetAncestralReport.py index e19a5ed6f..351a86be1 100644 --- a/src/plugins/textreport/DetAncestralReport.py +++ b/src/plugins/textreport/DetAncestralReport.py @@ -1,3 +1,4 @@ + # # Gramps - a GTK+/GNOME based genealogy program # @@ -8,6 +9,7 @@ # Copyright (C) 2009 Benny Malengier # Copyright (C) 2010 Jakim Friant # Copyright (C) 2010 Vlada Peri\u0107 +# 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 @@ -696,7 +698,7 @@ class DetAncestorReport(Report): if not obj or not self.inc_sources: return "" - txt = endnotes.cite_source(self.bibli, obj) + txt = endnotes.cite_source(self.bibli, self.database, obj) if txt: txt = '' + txt + '' return txt diff --git a/src/plugins/textreport/DetDescendantReport.py b/src/plugins/textreport/DetDescendantReport.py index beffa423d..150948f9b 100644 --- a/src/plugins/textreport/DetDescendantReport.py +++ b/src/plugins/textreport/DetDescendantReport.py @@ -10,6 +10,7 @@ # Copyright (C) 2010 Jakim Friant # Copyright (C) 2010 Vlada Peri\u0107 # Copyright (C) 2011 Matt Keenan +# 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 @@ -858,7 +859,7 @@ class DetDescendantReport(Report): if not obj or not self.inc_sources: return "" - txt = endnotes.cite_source(self.bibli, obj) + txt = endnotes.cite_source(self.bibli, self.database, obj) if txt: txt = '' + txt + '' return txt diff --git a/src/plugins/textreport/IndivComplete.py b/src/plugins/textreport/IndivComplete.py index 871703fbb..450b8ec57 100644 --- a/src/plugins/textreport/IndivComplete.py +++ b/src/plugins/textreport/IndivComplete.py @@ -6,6 +6,7 @@ # Copyright (C) 2009 Nick Hall # Copyright (C) 2009 Benny Malengier # Copyright (C) 2010 Jakim Friant +# 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 @@ -219,7 +220,7 @@ class IndivCompleteReport(Report): endnotes = "" if self.use_srcs: - endnotes = Endnotes.cite_source(self.bibli, event) + endnotes = Endnotes.cite_source(self.bibli, self.database, event) self.doc.start_row() self.normal_cell(column_1) @@ -359,7 +360,7 @@ class IndivCompleteReport(Report): text = self._name_display.display_name(name) endnotes = "" if self.use_srcs: - endnotes = Endnotes.cite_source(self.bibli, name) + endnotes = Endnotes.cite_source(self.bibli, self.database, name) self.normal_cell(text, endnotes) self.doc.end_row() self.doc.end_table() @@ -387,7 +388,7 @@ class IndivCompleteReport(Report): date = DateHandler.get_date(addr) endnotes = "" if self.use_srcs: - endnotes = Endnotes.cite_source(self.bibli, addr) + endnotes = Endnotes.cite_source(self.bibli, self.database, addr) self.doc.start_row() self.normal_cell(date) self.normal_cell(text, endnotes) @@ -579,7 +580,7 @@ class IndivCompleteReport(Report): mark = ReportUtils.get_person_mark(self.database, self.person) endnotes = "" if self.use_srcs: - endnotes = Endnotes.cite_source(self.bibli, name) + endnotes = Endnotes.cite_source(self.bibli, self.database, name) self.normal_cell(text, endnotes, mark) self.doc.end_row() diff --git a/src/plugins/tool/Check.py b/src/plugins/tool/Check.py index 0a8d1ef87..e3363346d 100644 --- a/src/plugins/tool/Check.py +++ b/src/plugins/tool/Check.py @@ -4,6 +4,7 @@ # Copyright (C) 2000-2007 Donald N. Allingham # Copyright (C) 2008 Brian G. Matherly # Copyright (C) 2010 Jakim Friant +# 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 @@ -231,7 +232,8 @@ class Check(tool.BatchTool): checker.check_person_references() checker.check_family_references() checker.check_place_references() - checker.check_source_references() + checker.check_citation_references() + # FIXME: CITATION should also check source references checker.check_media_references() checker.check_repo_references() checker.check_note_references() @@ -266,7 +268,7 @@ class CheckIntegrity(object): self.invalid_person_references = [] self.invalid_family_references = [] self.invalid_place_references = [] - self.invalid_source_references = [] + self.invalid_citation_references = [] self.invalid_repo_references = [] self.invalid_media_references = [] self.invalid_note_references = [] @@ -995,8 +997,8 @@ class CheckIntegrity(object): self.db.commit_event(event, self.trans) self.invalid_place_references.append(key) - def check_source_references(self): - known_handles = self.db.get_source_handles() + def check_citation_references(self): + known_handles = self.db.get_citation_handles() total = ( self.db.get_number_of_people() + @@ -1008,7 +1010,7 @@ class CheckIntegrity(object): self.db.get_number_of_repositories() ) - self.progress.set_pass(_('Looking for source reference problems'), + self.progress.set_pass(_('Looking for citation reference problems'), total) for handle in self.db.person_map.keys(): @@ -1018,14 +1020,14 @@ class CheckIntegrity(object): person.unserialize(info) handle_list = person.get_referenced_handles_recursively() bad_handles = [ item[1] for item in handle_list - if item[0] == 'Source' and + if item[0] == 'Citation' and item[1] not in known_handles ] if bad_handles: - person.remove_source_references(bad_handles) + person.remove_citation_references(bad_handles) self.db.commit_person(person,self.trans) new_bad_handles = [handle for handle in bad_handles if handle - not in self.invalid_source_references] - self.invalid_source_references += new_bad_handles + not in self.invalid_citation_references] + self.invalid_citation_references += new_bad_handles for handle in self.db.family_map.keys(): self.progress.step() @@ -1034,14 +1036,14 @@ class CheckIntegrity(object): family.unserialize(info) handle_list = family.get_referenced_handles_recursively() bad_handles = [ item[1] for item in handle_list - if item[0] == 'Source' and + if item[0] == 'Citation' and item[1] not in known_handles ] if bad_handles: - family.remove_source_references(bad_handles) + family.remove_citation_references(bad_handles) self.db.commit_family(family, self.trans) new_bad_handles = [handle for handle in bad_handles if handle - not in self.invalid_source_references] - self.invalid_source_references += new_bad_handles + not in self.invalid_citation_references] + self.invalid_citation_references += new_bad_handles for handle in self.db.place_map.keys(): self.progress.step() @@ -1050,14 +1052,14 @@ class CheckIntegrity(object): place.unserialize(info) handle_list = place.get_referenced_handles_recursively() bad_handles = [ item[1] for item in handle_list - if item[0] == 'Source' and + if item[0] == 'Citation' and item[1] not in known_handles ] if bad_handles: - place.remove_source_references(bad_handles) + place.remove_citation_references(bad_handles) self.db.commit_place(place,self.trans) new_bad_handles = [handle for handle in bad_handles if handle - not in self.invalid_source_references] - self.invalid_source_references += new_bad_handles + not in self.invalid_citation_references] + self.invalid_citation_references += new_bad_handles for handle in self.db.repository_map.keys(): self.progress.step() @@ -1066,31 +1068,31 @@ class CheckIntegrity(object): repo.unserialize(info) handle_list = repo.get_referenced_handles_recursively() bad_handles = [ item[1] for item in handle_list - if item[0] == 'Source' and + if item[0] == 'Citation' and item[1] not in known_handles ] if bad_handles: - repo.remove_source_references(bad_handles) + repo.remove_citation_references(bad_handles) self.db.commit_repository(repo, self.trans) new_bad_handles = [handle for handle in bad_handles if handle - not in self.invalid_source_references] - self.invalid_source_references += new_bad_handles + not in self.invalid_citation_references] + self.invalid_citation_references += new_bad_handles - #I think this for loop is useless! sources in sources map exist... + #I think this for loop is useless! citations in citations map exist... for handle in known_handles: self.progress.step() - info = self.db.source_map[handle] - source = gen.lib.Source() - source.unserialize(info) - handle_list = source.get_referenced_handles_recursively() + info = self.db.citation_map[handle] + citation = gen.lib.Citation() + citation.unserialize(info) + handle_list = citation.get_referenced_handles_recursively() bad_handles = [ item[1] for item in handle_list - if item[0] == 'Source' and + if item[0] == 'Citation' and item[1] not in known_handles ] if bad_handles: - source.remove_source_references(bad_handles) - self.db.commit_source(source, self.trans) + citation.remove_citation_references(bad_handles) + self.db.commit_citation(citation, self.trans) new_bad_handles = [handle for handle in bad_handles if handle - not in self.invalid_source_references] - self.invalid_source_references += new_bad_handles + not in self.invalid_citation_references] + self.invalid_citation_references += new_bad_handles for handle in self.db.media_map.keys(): self.progress.step() @@ -1099,14 +1101,14 @@ class CheckIntegrity(object): obj.unserialize(info) handle_list = obj.get_referenced_handles_recursively() bad_handles = [ item[1] for item in handle_list - if item[0] == 'Source' and + if item[0] == 'Citation' and item[1] not in known_handles ] if bad_handles: - obj.remove_source_references(bad_handles) + obj.remove_citation_references(bad_handles) self.db.commit_media_object(obj, self.trans) new_bad_handles = [handle for handle in bad_handles if handle - not in self.invalid_source_references] - self.invalid_source_references += new_bad_handles + not in self.invalid_citation_references] + self.invalid_citation_references += new_bad_handles for handle in self.db.event_map.keys(): self.progress.step() @@ -1115,14 +1117,14 @@ class CheckIntegrity(object): event.unserialize(info) handle_list = event.get_referenced_handles_recursively() bad_handles = [ item[1] for item in handle_list - if item[0] == 'Source' and + if item[0] == 'Citation' and item[1] not in known_handles ] if bad_handles: - event.remove_source_references(bad_handles) + event.remove_citation_references(bad_handles) self.db.commit_event(event, self.trans) new_bad_handles = [handle for handle in bad_handles if handle - not in self.invalid_source_references] - self.invalid_source_references += new_bad_handles + not in self.invalid_citation_references] + self.invalid_citation_references += new_bad_handles def check_media_references(self): known_handles = self.db.get_media_object_handles(False) @@ -1367,7 +1369,7 @@ class CheckIntegrity(object): family_references = len(self.invalid_family_references) invalid_dates = len(self.invalid_dates) place_references = len(self.invalid_place_references) - source_references = len(self.invalid_source_references) + citation_references = len(self.invalid_citation_references) repo_references = len(self.invalid_repo_references) media_references = len(self.invalid_media_references) note_references = len(self.invalid_note_references) @@ -1377,7 +1379,7 @@ class CheckIntegrity(object): errors = (photos + efam + blink + plink + slink + rel + event_invalid + person + person_references + family_references + place_references + - source_references + repo_references + media_references + + citation_references + repo_references + media_references + note_references + name_format + empty_objs + invalid_dates ) @@ -1559,11 +1561,11 @@ class CheckIntegrity(object): place_references) % {'quantity': place_references} ) - if source_references: + if citation_references: self.text.write( - ngettext("%(quantity)d source was referenced but not found\n", - "%(quantity)d sources were referenced, but not found\n", - source_references) % {'quantity': source_references} + ngettext("%(quantity)d citation was referenced but not found\n", + "%(quantity)d citations were referenced, but not found\n", + citation_references) % {'quantity': citation_references} ) if media_references: diff --git a/src/plugins/tool/Makefile.am b/src/plugins/tool/Makefile.am index ec1289531..107294e09 100644 --- a/src/plugins/tool/Makefile.am +++ b/src/plugins/tool/Makefile.am @@ -28,7 +28,8 @@ pkgdata_PYTHON = \ SortEvents.py \ SoundGen.py \ tools.gpr.py \ - Verify.py + Verify.py \ + MergeCitations.py # DumpGenderStats.py \ # PHPGedViewConnector.py \ # TestcaseGenerator.py @@ -52,7 +53,8 @@ GLADEFILES = \ relcalc.glade \ removeunused.glade \ soundgen.glade \ - verify.glade + verify.glade \ + mergecitations.glade dist_pkgdata_DATA = $(GLADEFILES) diff --git a/src/plugins/tool/MergeCitations.py b/src/plugins/tool/MergeCitations.py new file mode 100644 index 000000000..3322af6fd --- /dev/null +++ b/src/plugins/tool/MergeCitations.py @@ -0,0 +1,301 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000-2007 Donald N. Allingham +# Copyright (C) 2008 Brian G. Matherly +# Copyright (C) 2010 Jakim Friant +# 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$ + +"""Tools/Family Tree Processing/MergeCitations""" + +#------------------------------------------------------------------------ +# +# Python modules +# +#------------------------------------------------------------------------ +import logging +LOG = logging.getLogger(".citation") + +#------------------------------------------------------------------------- +# +# GNOME libraries +# +#------------------------------------------------------------------------- +import gtk + +#------------------------------------------------------------------------- +# +# GRAMPS modules +# +#------------------------------------------------------------------------- +from Utils import confidence +import const +from gui.utils import ProgressMeter +from gui.plug import tool +from QuestionDialog import OkDialog +import GrampsDisplay +import DateHandler +import ManagedWindow +from gen.ggettext import sgettext as _ +from glade import Glade +from gen.db import DbTxn +from gen.lib import (Person, Family, Event, Place, MediaObject, Citation, + Repository) +from Errors import MergeError + +#------------------------------------------------------------------------- +# +# Constants +# +#------------------------------------------------------------------------- +ALL_FIELDS = 0 +IGNORE_DATE = 1 +IGNORE_CONFIDENCE = 2 +IGNORE_BOTH = 3 + +_val2label = { + ALL_FIELDS : _("Match on Page/Volume, Date and Confidence"), + IGNORE_DATE : _("Ignore Date"), + IGNORE_CONFIDENCE : _("Ignore Confidence"), + IGNORE_BOTH : _("Ignore Date and Confidence") + } + +WIKI_HELP_PAGE = '%s_-_Tools' % const.URL_MANUAL_PAGE +WIKI_HELP_SEC = _('manual|Merge citations...') + +#------------------------------------------------------------------------- +# +# The Actual tool. +# +#------------------------------------------------------------------------- +class MergeCitations(tool.BatchTool,ManagedWindow.ManagedWindow): + + def __init__(self, dbstate, uistate, options_class, name, callback=None): + + ManagedWindow.ManagedWindow.__init__(self, uistate, [], self.__class__) + self.dbstate = dbstate + self.set_window(gtk.Window(), gtk.Label(), '') + + tool.BatchTool.__init__(self, dbstate, options_class, name) + + if not self.fail: + uistate.set_busy_cursor(True) + self.run() + uistate.set_busy_cursor(False) + + def run(self): + + top = Glade(toplevel="mergecitations") + + # retrieve options + fields = self.options.handler.options_dict['fields'] + dont_merge_notes = self.options.handler.options_dict['dont_merge_notes'] + + my_menu = gtk.ListStore(str, object) + for val in sorted(_val2label): + my_menu.append([_val2label[val], val]) + + self.notes_obj = top.get_object("notes") + self.notes_obj.set_active(dont_merge_notes) + self.notes_obj.show() + + self.menu = top.get_object("menu") + self.menu.set_model(my_menu) + self.menu.set_active(fields) + + window = top.toplevel + window.show() +# self.set_window(window, top.get_object('title'), +# _('Merge citations')) + self.set_window(window, top.get_object('title2'), + "Notes, media objects and data-items of matching " + "citations will be combined.") + + top.connect_signals({ + "on_merge_ok_clicked" : self.on_merge_ok_clicked, + "destroy_passed_object" : self.cancel, + "on_help_clicked" : self.on_help_clicked, + "on_delete_merge_event" : self.close, + "on_delete_event" : self.close, + }) + + self.show() + + def cancel(self, obj): + """ + on cancel, update the saved values of the options. + """ + fields = self.menu.get_model()[self.menu.get_active()][1] + dont_merge_notes = int(self.notes_obj.get_active()) + LOG.debug("cancel fields %d dont_merge_notes %d" % + (fields, dont_merge_notes)) + + self.options.handler.options_dict['fields'] = fields + self.options.handler.options_dict['dont_merge_notes'] = dont_merge_notes + # Save options + self.options.handler.save_options() + + self.close(obj) + + def build_menu_names(self, obj): + return (_("Tool settings"),_("Merge citations tool")) + + def on_help_clicked(self, obj): + """Display the relevant portion of GRAMPS manual""" + + GrampsDisplay.help(WIKI_HELP_PAGE , WIKI_HELP_SEC) + + def on_merge_ok_clicked(self, obj): + """ + Performs the actual merge of citations + (Derived from ExtractCity) + """ + fields = self.menu.get_model()[self.menu.get_active()][1] + dont_merge_notes = int(self.notes_obj.get_active()) + LOG.debug("fields %d dont_merge_notes %d" % (fields, dont_merge_notes)) + + self.options.handler.options_dict['fields'] = fields + self.options.handler.options_dict['dont_merge_notes'] = dont_merge_notes + # Save options + self.options.handler.save_options() + + self.progress = ProgressMeter(_('Checking Sources'), '') + self.progress.set_pass(_('Looking for citation fields'), + self.db.get_number_of_citations()) + + db = self.dbstate.db + + db.disable_signals() + num_merges = 0 + with DbTxn(_("Merge Citation"), db) as trans: + for handle in db.iter_source_handles(): + dict = {} + citation_handle_list = list(db.find_backlink_handles(handle)) + for (class_name, citation_handle) in citation_handle_list: + if class_name <> Citation.__name__: + raise MergeError("Encountered an object of type %s " + "that has a citation reference." % class_name) + + citation = db.get_citation_from_handle(citation_handle) + key = citation.get_page() + if fields <> IGNORE_DATE and fields <> IGNORE_BOTH: + key += "\n" + DateHandler.get_date(citation) + if fields <> IGNORE_CONFIDENCE and fields <> IGNORE_BOTH: + key += "\n" + \ + confidence[citation.get_confidence_level()] + if key in dict and \ + (not dont_merge_notes or len(citation.note_list) == 0): + citation_match_handle = dict[key] + citation_match = \ + db.get_citation_from_handle(citation_match_handle) + try: + self.Merge(db, citation_match, citation, trans) + except AssertionError: + print "Tool/Family Tree processing/MergeCitations", \ + "citation1 gramps_id", citation_match.get_gramps_id(), \ + "citation2 gramps_id", citation.get_gramps_id() , \ + "citation backlink handles", \ + list(db.find_backlink_handles(citation.get_handle())) + num_merges += 1 + else: + dict[key] = citation_handle + self.progress.step() + db.enable_signals() + db.request_rebuild() + self.progress.close() + OkDialog( + _("Number of merges done"), + _("%d citations merges" % num_merges), + parent=self.window) + + def Merge (self, db, citation1, citation2, trans): + """ + Merges two citations into a single citation. + """ + new_handle = citation1.get_handle() + old_handle = citation2.get_handle() + + citation1.merge(citation2) + + db.commit_citation(citation1, trans) + for (class_name, handle) in db.find_backlink_handles( + old_handle): + if class_name == Person.__name__: + person = db.get_person_from_handle(handle) + assert(person.has_citation_reference(old_handle)) + person.replace_citation_references(old_handle, new_handle) + db.commit_person(person, trans) + elif class_name == Family.__name__: + family = db.get_family_from_handle(handle) + assert(family.has_citation_reference(old_handle)) + family.replace_citation_references(old_handle, new_handle) + db.commit_family(family, trans) + elif class_name == Event.__name__: + event = db.get_event_from_handle(handle) + assert(event.has_citation_reference(old_handle)) + event.replace_citation_references(old_handle, new_handle) + db.commit_event(event, trans) + elif class_name == Place.__name__: + place = db.get_place_from_handle(handle) + assert(place.has_citation_reference(old_handle)) + place.replace_citation_references(old_handle, new_handle) + db.commit_place(place, trans) + elif class_name == MediaObject.__name__: + obj = db.get_object_from_handle(handle) + assert(obj.has_citation_reference(old_handle)) + obj.replace_citation_references(old_handle, new_handle) + db.commit_media_object(obj, trans) + elif class_name == Repository.__name__: + repository = db.get_repository_from_handle(handle) + assert(repository.has_citation_reference(old_handle)) + repository.replace_citation_references(old_handle, new_handle) + db.commit_repository(repository, trans) + else: + raise MergeError("Encountered an object of type %s that has " + "a citation reference." % class_name) + db.remove_citation(old_handle, trans) + +#------------------------------------------------------------------------ +# +# +# +#------------------------------------------------------------------------ +class MergeCitationsOptions(tool.ToolOptions): + """ + Defines options and provides handling interface. + """ + + def __init__(self, name,person_id=None): + tool.ToolOptions.__init__(self, name,person_id) + + # Options specific for this report + self.options_dict = { + 'fields' : 1, + 'dont_merge_notes' : 0, + } + self.options_help = { + 'dont_merge_notes' : + ("=0/1","Whether to merge citations if they have notes", + ["Merge citations with notes", + "Do not merge citations with notes"], + False), + 'fields' : ("=num","Threshold for matching", + "Integer number") + } diff --git a/src/plugins/tool/TestcaseGenerator.py b/src/plugins/tool/TestcaseGenerator.py index 5bf780719..8cf5b1c8a 100644 --- a/src/plugins/tool/TestcaseGenerator.py +++ b/src/plugins/tool/TestcaseGenerator.py @@ -5,6 +5,7 @@ # Copyright (C) 2000-2006 Martin Hawlisch, Donald N. Allingham # Copyright (C) 2008 Brian G. Matherly # Copyright (C) 2010 Jakim Friant +# 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 @@ -336,6 +337,30 @@ class TestcaseGenerator(tool.BatchTool): # self.rand_media() + # FIXME: generate_tags needs to be run before generate_broken_relations + # otherwise you get + +# File "/Users/tim/gramps/gramps33/src/plugins/tool/TestcaseGenerator.py", line 1404, in rand_tags +# tag = choice(self.generated_tags) +# File "/opt/local/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/random.py", line 261, in choice +# return seq[int(self.random() * len(seq))] # raises IndexError if seq is empty +#IndexError: list index out of range + + # FIXME: If tags have arbitrary forms, then you can get errors because + # add_ui_from_string parses the tag as part of parsing + + +# Traceback (most recent call last): +# File "/Users/tim/gramps/gramps33/src/gui/viewmanager.py", line 1265, in view_changed +# self.__change_page(page_num) +# File "/Users/tim/gramps/gramps33/src/gui/viewmanager.py", line 1278, in __change_page +# self.active_page.set_active() +# File "/Users/tim/gramps/gramps33/src/plugins/lib/libpersonview.py", line 399, in set_active +# self.uistate.viewmanager.tags.tag_enable() +# File "/Users/tim/gramps/gramps33/src/gui/views/tags.py", line 122, in tag_enable +# self.tag_id = self.uistate.uimanager.add_ui_from_string(self.tag_ui) +#GError: Error on line 6 char 470: '#+#000001#-#' is not a valid name + if self.options.handler.options_dict['bugs']: self.generate_broken_relations() diff --git a/src/plugins/tool/mergecitations.glade b/src/plugins/tool/mergecitations.glade new file mode 100644 index 000000000..d3b747c77 --- /dev/null +++ b/src/plugins/tool/mergecitations.glade @@ -0,0 +1,255 @@ + + + + + + True + + + True + 12 + + + True + center + + + False + False + 6 + 0 + + + + + True + 10 + Please be patient. This may take a while. + center + True + + + False + False + 20 + 1 + + + + + True + + + True + 0.10000000149 + + + 20 + 0 + + + + + False + False + 2 + + + + + + + + + + + + + 350 + dialog + False + + + + True + 8 + + + True + 6 + 6 + + + True + center + + + False + False + 6 + 0 + + + + + True + 12 + 5 + 2 + 12 + 6 + + + True + 0 + <b>Match Threshold</b> + True + + + 2 + GTK_FILL + + + + + + True + 0 + <b>Options</b> + True + + + 2 + 3 + 4 + GTK_FILL + + + + + + Don't merge if citation has notes + True + True + False + True + True + True + + + 1 + 2 + 4 + 5 + GTK_FILL + + + + + + True + liststore1 + + + + 0 + + + + + 1 + 2 + 1 + 2 + + + + + + + + + + + + + + + + + 1 + + + + + 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 + + + + + + button12 + button10 + button14 + + + diff --git a/src/plugins/tool/tools.gpr.py b/src/plugins/tool/tools.gpr.py index bf46d6755..9c58033a3 100644 --- a/src/plugins/tool/tools.gpr.py +++ b/src/plugins/tool/tools.gpr.py @@ -517,3 +517,27 @@ toolclass = 'Verify', optionclass = 'VerifyOptions', tool_modes = [TOOL_MODE_GUI, TOOL_MODE_CLI] ) + +#------------------------------------------------------------------------ +# +# Merge citations +# +#------------------------------------------------------------------------ + +register(TOOL, +id = 'mergecitations', +name = _("Merge Citations"), +description = _("Searches the entire database, looking for " + "citations that have the same Volume/Page, Date and Confidence."), +version = '1.0', +gramps_target_version = '3.4', +status = STABLE, +fname = 'MergeCitations.py', +authors = ["Tim G L Lyons"], +authors_email = ["gramps-project.org"], +category = TOOL_DBPROC, +toolclass = 'MergeCitations', +optionclass = 'MergeCitationsOptions', +tool_modes = [TOOL_MODE_GUI] + ) + diff --git a/src/plugins/view/Makefile.am b/src/plugins/view/Makefile.am index bfa7b61ab..14f9a927c 100644 --- a/src/plugins/view/Makefile.am +++ b/src/plugins/view/Makefile.am @@ -6,6 +6,8 @@ pkgdatadir = $(datadir)/@PACKAGE@/plugins/view pkgdata_PYTHON = \ + citationlistview.py \ + citationtreeview.py \ eventview.py \ familyview.py \ fanchartview.gpr.py \ diff --git a/src/plugins/view/citationlistview.py b/src/plugins/view/citationlistview.py new file mode 100644 index 000000000..28dcce52c --- /dev/null +++ b/src/plugins/view/citationlistview.py @@ -0,0 +1,358 @@ +# 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 +# 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$ + +""" +Citation List View +""" + +#------------------------------------------------------------------------- +# +# python modules +# +#------------------------------------------------------------------------- +import logging +LOG = logging.getLogger(".citation") + +#------------------------------------------------------------------------- +# +# GTK/Gnome modules +# +#------------------------------------------------------------------------- +import gtk + +#------------------------------------------------------------------------- +# +# gramps modules +# +#------------------------------------------------------------------------- +from gui.views.treemodels.citationlistmodel import CitationListModel +from gen.plug import CATEGORY_QR_CITATION +import gen.lib +from gui.views.listview import ListView +import Utils +import Bookmarks +import Errors +from DdTargets import DdTargets +from QuestionDialog import ErrorDialog +from gui.editors import EditCitation, DeleteCitationQuery +from Filters.SideBar import CitationSidebarFilter + +#------------------------------------------------------------------------- +# +# internationalization +# +#------------------------------------------------------------------------- +from gen.ggettext import gettext as _ + + +#------------------------------------------------------------------------- +# +# CitationView +# +#------------------------------------------------------------------------- +class CitationListView(ListView): + """ + A list view of citations. + + The citation list view only shows the citations (it does not show + sources as separate list entries). + """ + # The data items here have to correspond, in order, to the items in + # src/giu.views/treemodels/citationlismodel.py + COL_TITLE_PAGE = 0 + COL_ID = 1 + COL_DATE = 2 + COL_CONFIDENCE = 3 + COL_CHAN = 4 + COL_SRC_TITLE = 5 + COL_SRC_ID = 6 + COL_SRC_AUTH = 7 + COL_SRC_ABBR = 8 + COL_SRC_PINFO = 9 + COL_SRC_CHAN = 10 + # name of the columns + COLUMN_NAMES = [ + _('Volume/Page'), + _('ID'), + _('Date'), + _('Confidence'), + _('Last Changed'), + _('Source: Title'), + _('Source: ID'), + _('Source: Author'), + _('Source: Abbreviation'), + _('Source: Publication Information'), + _('Source: Last Changed'), + ] + # columns that contain markup + MARKUP_COLS = [COL_DATE] + # default setting with visible columns, order of the col, and their size + CONFIGSETTINGS = ( + ('columns.visible', [COL_TITLE_PAGE, COL_ID, COL_DATE, + COL_CONFIDENCE]), + ('columns.rank', [COL_TITLE_PAGE, COL_ID, COL_DATE, COL_CONFIDENCE, + COL_CHAN, COL_SRC_TITLE, COL_SRC_ID, COL_SRC_AUTH, + 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 and a new source") + ADD_SOURCE_MSG = _("Add a new source") + ADD_CITATION_MSG = _("Add a new citation to an existing source") + EDIT_MSG = _("Edit the selected citation") + DEL_MSG = _("Delete the selected citation") + MERGE_MSG = _("Merge the selected citations") + FILTER_TYPE = "Citation" + QR_CATEGORY = CATEGORY_QR_CITATION + + def __init__(self, pdata, dbstate, uistate, nav_group=0): + + signal_map = { + 'citation-add' : self.row_add, + 'citation-update' : self.row_update, + 'citation-delete' : self.row_delete, + 'citation-rebuild' : self.object_build, + } + + ListView.__init__( + self, _('Citation View'), pdata, dbstate, uistate, + self.COLUMN_NAMES, len(self.COLUMN_NAMES), + CitationListModel, signal_map, + dbstate.db.get_citation_bookmarks(), + Bookmarks.CitationBookmarks, nav_group, + multiple=True, + filter_class=CitationSidebarFilter, + markup = CitationListView.MARKUP_COLS) + + self.func_list.update({ + 'J' : self.jump, + 'BackSpace' : self.key_delete, + }) + + self.additional_uis.append(self.additional_ui()) + + def navigation_type(self): + return 'Citation' + + def get_bookmarks(self): + return self.dbstate.db.get_citation_bookmarks() + + def drag_info(self): + return DdTargets.CITATION_LINK + + def define_actions(self): + """ + This defines the possible actions for the citation views. + Possible actions are: + 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) + edit: Edit a citation. + merge: Merge the selected citations. + remove: Delete the selected citations. + + + """ + ListView.define_actions(self) + + self.all_action = gtk.ActionGroup(self.title + "/CitationAll") + self.edit_action = gtk.ActionGroup(self.title + "/CitationEdit") + + self._add_action('FilterEdit', None, _('Citation Filter Editor'), + callback=self.filter_editor,) + self._add_action('QuickReport', None, _("Quick View"), None, None, None) + self._add_action('Dummy', None, ' ', None, None, self.dummy_report) + + self._add_action_group(self.edit_action) + self._add_action_group(self.all_action) + + def get_stock(self): + return 'gramps-citation' + + def additional_ui(self): + """ + Defines the UI string for UIManager + """ + return ''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ''' + + def dummy_report(self, obj): + """ For the xml UI definition of popup to work, the submenu + Quick Report must have an entry in the xml + As this submenu will be dynamically built, we offer a dummy action + """ + pass + + def add(self, obj): + """ + 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 + EditCitation editor with the new source and new citation. + + 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. + """ + try: + EditCitation(self.dbstate, self.uistate, [], gen.lib.Citation(), + gen.lib.Source()) + except Errors.WindowActiveError: + pass + + 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) + + def edit(self, obj): + """ + Edit a Citation + """ + for handle in self.selected_handles(): + citation = self.dbstate.db.get_citation_from_handle(handle) + try: + EditCitation(self.dbstate, self.uistate, [], citation) + except Errors.WindowActiveError: + pass + + def __blocked_text(self): + """ + Return the common text used when citation cannot be edited + """ + 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): + """ + Merge the selected citations. + """ + mlist = self.selected_handles() + + 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) + else: + citation1 = self.dbstate.db.get_citation_from_handle(mlist[0]) + citation2 = self.dbstate.db.get_citation_from_handle(mlist[1]) + 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]) + + def get_handle_from_gramps_id(self, gid): + obj = self.dbstate.db.get_citation_from_gramps_id(gid) + if obj: + return obj.get_handle() + else: + return None + + def get_default_gramplets(self): + """ + Define the default gramplets for the sidebar and bottombar. + This is overridden for the tree view to give 'Source Filter' + """ + return (("Citation Filter",), + ("Citation Gallery", + "Citation Notes", + "Citation Backlinks")) diff --git a/src/plugins/view/citationtreeview.py b/src/plugins/view/citationtreeview.py new file mode 100644 index 000000000..b59cdd017 --- /dev/null +++ b/src/plugins/view/citationtreeview.py @@ -0,0 +1,582 @@ +# 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 +# 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$ + +""" +Citation Tree View (or Source tree view). +A view showing all the Sources with child Citations +""" +#------------------------------------------------------------------------- +# +# python modules +# +#------------------------------------------------------------------------- +import logging +LOG = logging.getLogger(".citation") +_LOG = logging.getLogger('.gui.citationtreeview') + +#------------------------------------------------------------------------- +# +# GTK/Gnome modules +# +#------------------------------------------------------------------------- +import gtk + +#------------------------------------------------------------------------- +# +# Gramps modules +# +#------------------------------------------------------------------------- +from gui.views.listview import LISTTREE +from gui.views.treemodels.citationtreemodel import CitationTreeModel +from gen.plug import CATEGORY_QR_SOURCE_OR_CITATION +import gen.lib +from gui.views.listview import ListView +import Utils +import Bookmarks +import Errors +from DdTargets import DdTargets +from QuestionDialog import ErrorDialog +from gui.editors import EditCitation, DeleteCitationQuery, EditSource, \ + DeleteSrcQuery +from Filters.SideBar import SourceSidebarFilter + +#------------------------------------------------------------------------- +# +# Internationalization +# +#------------------------------------------------------------------------- +from gen.ggettext import gettext as _ + +#------------------------------------------------------------------------- +# +# PlaceTreeView +# +#------------------------------------------------------------------------- +class CitationTreeView(ListView): + """ + A hierarchical view of sources with citations below them. + """ + # The data items here have to correspond, in order, to the items in + # src/giu.views/treemodels/citationtreemodel.py + COL_TITLE_PAGE = 0 + COL_ID = 1 + COL_DATE = 2 + COL_CONFIDENCE = 3 + COL_CHAN = 4 + COL_SRC_AUTH = 5 + COL_SRC_ABBR = 6 + COL_SRC_PINFO = 7 + # name of the columns + COLUMN_NAMES = [ + _('Title or Page'), + _('ID'), + _('Date'), + _('Confidence'), + _('Last Changed'), + _('Source: Author'), + _('Source: Abbreviation'), + _('Source: Publication Information'), + ] + COLUMN_FILTERABLE = [ + COL_TITLE_PAGE, + COL_ID, + COL_CHAN, + COL_SRC_AUTH, + COL_SRC_ABBR, + COL_SRC_PINFO + ] + # columns that contain markup + MARKUP_COLS = [COL_DATE] + # default setting with visible columns, order of the col, and their size + CONFIGSETTINGS = ( + ('columns.visible', [COL_TITLE_PAGE, COL_ID, COL_SRC_AUTH, + COL_SRC_PINFO]), + ('columns.rank', [COL_TITLE_PAGE, COL_ID, COL_DATE, COL_CONFIDENCE, + COL_CHAN, COL_SRC_AUTH, + COL_SRC_ABBR, COL_SRC_PINFO]), + ('columns.size', [200, 75, 100, 75, 100, 150, 100, 150]) + ) + 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_MSG = _("Edit the selected citation or source") + DEL_MSG = _("Delete the selected citation or source") + MERGE_MSG = _("Merge the selected citations or selected sources") + FILTER_TYPE = "Citation" + QR_CATEGORY = CATEGORY_QR_SOURCE_OR_CITATION + + def __init__(self, pdata, dbstate, uistate, nav_group=0): + + signal_map = { + 'citation-add' : self._citation_row_add, + 'citation-update' : self._citation_row_update, + 'citation-delete' : self._citation_row_delete, + 'citation-rebuild' : self._citation_object_build, + 'source-add' : self._source_row_add, + 'source-update' : self._source_row_update, + 'source-delete' : self._source_row_delete, + 'source-rebuild' : self._source_object_build, + } + + ListView.__init__( + self, _('Citation Tree View'), pdata, dbstate, uistate, + self.COLUMN_NAMES, len(self.COLUMN_NAMES), + CitationTreeModel, signal_map, + dbstate.db.get_citation_bookmarks(), + Bookmarks.CitationBookmarks, nav_group, + multiple=True, + filter_class=SourceSidebarFilter, + markup = CitationTreeView.MARKUP_COLS) + + self.func_list.update({ + 'J' : self.jump, + 'BackSpace' : self.key_delete, + }) + + self.additional_uis.append(self.additional_ui()) + + def setup_filter(self): + """ + Override the setup of the default Search Bar in listview, so that only + the searchable source fields are shown. This includes renaming the + 'Title or Page' search to 'Title' + """ + def name(i): + if i == 0: + return _('Title') + else: + return self.colinfo[i] + + self.search_bar.setup_filter( + [(name(pair[1]), pair[1], pair[1] in self.exact_search()) + for pair in self.column_order() if pair[0] and + pair[1] in self.COLUMN_FILTERABLE]) + + def _print_handles(self, text, handle_list): + for handle in handle_list: + source = self.dbstate.db.get_source_from_handle(handle) + citation = self.dbstate.db.get_citation_from_handle(handle) + _LOG.debug("\n\n\n") + if source: + _LOG.debug("---- %s -- source %s" % + (text, source.get_title())) + elif citation: + _LOG.debug("---- %s -- citation %s" % + (text, citation.get_page())) + else: + _LOG.debug("---- %s -- handle %s" % (text, handle)) + + def _citation_row_add(self, handle_list): + self._print_handles("citation row add", handle_list) + self.row_add(handle_list) + + def _citation_row_update(self, handle_list): + self._print_handles("citation row update", handle_list) + self.row_update(handle_list) + + def _citation_row_delete(self, handle_list): + self._print_handles("citation row delete", handle_list) + self.row_delete(handle_list) + + def _citation_object_build(self, *args): + _LOG.debug("citation object build") + self.object_build(*args) + + def _source_row_add(self, handle_list): + self._print_handles("source row add", handle_list) + self.row_add(handle_list) + + def _source_row_update(self, handle_list): + self._print_handles("source row update", handle_list) + self.row_update(handle_list) + + def _source_row_delete(self, handle_list): + self._print_handles("source row delete", handle_list) + self.row_delete(handle_list) + + def _source_object_build(self, *args): + _LOG.debug("source object build") + self.object_build(*args) + + def navigation_type(self): + return 'Citation' + + def get_bookmarks(self): + return self.dbstate.db.get_citation_bookmarks() + + def drag_info(self): + # Since drag only needs to work when just one row is selected, ideally, + # this should just return SOURCE_LINK if one source is selected and + # CITATION_LINK if one citation is selected, and probably None + # otherwise. However, this doesn't work. Drag and drop failed to work at + # all for citationtree view, and I think this was because None is + # returned during initialisation. There is also a problem where it seems + # at some point during a citation merge, neither a Source nor a Citation + # is selected. Hence the simplistic solution implemented below, where + # CITATION_LINK is always returned except when it is obviously correct + # to return SOURCE_LINK. + + selection = self.selected_handles() + if len(selection) == 1 and \ + self.dbstate.db.get_source_from_handle(selection[0]): + return DdTargets.SOURCE_LINK + else: + return DdTargets.CITATION_LINK + + def type_list(self): + """ + set the listtype, this governs eg keybinding + """ + return LISTTREE + + def get_stock(self): + return 'gramps-citation' + + def get_viewtype_stock(self): + """ + Override the default icon. Set for hierarchical view. + """ + return 'gramps-tree-group' + + 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('Add source', 'gramps-source', _("Add source..."), + accel=None, + tip=self.ADD_SOURCE_MSG, + callback=self.add_source) + self._add_action('Add citation', 'gramps-citation', + _("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") + + self._add_action('FilterEdit', None, _('Citation Filter Editor'), + callback=self.filter_editor,) + self._add_action('QuickReport', None, _("Quick View"), None, None, None) + self._add_action('Dummy', None, ' ', None, None, self.dummy_report) + + self._add_action_group(self.edit_action) + self._add_action_group(self.all_action) + + self.all_action.add_actions([ + ('OpenAllNodes', None, _("Expand all Nodes"), None, None, + self.open_all_nodes), + ('CloseAllNodes', None, _("Collapse all Nodes"), None, None, + self.close_all_nodes), + ]) + + def additional_ui(self): + """ + Defines the UI string for UIManager + """ + return ''' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ''' + + def dummy_report(self, obj): + """ For the xml UI definition of popup to work, the submenu + Quick Report must have an entry in the xml + As this submenu will be dynamically built, we offer a dummy action + """ + 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: 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. + """ + try: + EditCitation(self.dbstate, self.uistate, [], gen.lib.Citation(), + gen.lib.Source()) + except Errors.WindowActiveError: + pass + + 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 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: + the_lists = Utils.get_source_and_citation_referents(handle, + self.dbstate.db) + 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): + """ + Edit either a Source or a Citation, depending on user selection + """ + 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: + try: + EditCitation(self.dbstate, self.uistate, [], citation) + except Errors.WindowActiveError: + pass + else: # FIXME need try block here + try: + EditSource(self.dbstate, self.uistate, [], source) + except Errors.WindowActiveError: + from QuestionDialog import WarningDialog + WarningDialog(_("Cannot share this reference"), + self.__blocked_text2()) + + def __blocked_text(self): + """ + Return the common text used when citation cannot be edited + """ + return _("This citation cannot be created at this time. " + "Either the associated Source object is already being " + "edited, or another citation associated with the same " + "source is being edited.\n\nTo edit this " + "citation, you need to close the object.") + + def __blocked_text2(self): + """ + Return the common text used when citation cannot be edited + """ + return _("This source cannot be edited at this time. " + "Either the associated Source object is already being " + "edited, or another citation associated with the same " + "source is being edited.\n\nTo edit this " + "source, you need to close the object.") + + def merge(self, obj): + """ + Merge the selected citations. + """ + mlist = self.selected_handles() + + 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) + else: + 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) + if obj: + return obj.get_handle() + else: + return None + + def get_default_gramplets(self): + """ + Define the default gramplets for the sidebar and bottombar. + """ + return (("Source Filter",), + ("Citation Gallery", + "Citation Notes", + "Citation Backlinks")) diff --git a/src/plugins/view/eventview.py b/src/plugins/view/eventview.py index 8f7c6310e..fb9ef62ed 100644 --- a/src/plugins/view/eventview.py +++ b/src/plugins/view/eventview.py @@ -2,6 +2,7 @@ # # Copyright (C) 2001-2007 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 # it under the terms of the GNU General Public License as published by @@ -291,7 +292,7 @@ class EventView(ListView): """ return (("Event Filter",), ("Event Gallery", - "Event Sources", + "Event Citations", "Event Notes", "Event Attributes", "Event Backlinks")) diff --git a/src/plugins/view/familyview.py b/src/plugins/view/familyview.py index 433420bd7..ec9fe2b70 100644 --- a/src/plugins/view/familyview.py +++ b/src/plugins/view/familyview.py @@ -2,6 +2,7 @@ # # Copyright (C) 2001-2006 Donald N. Allingham # 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 @@ -348,7 +349,7 @@ class FamilyView(ListView): ("Family Gallery", "Family Events", "Family Children", - "Family Sources", + "Family Citations", "Family Notes", "Family Attributes", "Family Backlinks")) diff --git a/src/plugins/view/mediaview.py b/src/plugins/view/mediaview.py index 8f98e8c59..f1fafcbb4 100644 --- a/src/plugins/view/mediaview.py +++ b/src/plugins/view/mediaview.py @@ -3,6 +3,7 @@ # Copyright (C) 2001-2006 Donald N. Allingham # Copyright (C) 2008 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 @@ -425,7 +426,7 @@ class MediaView(ListView): """ return (("Media Filter",), ("Media Preview", - "Media Sources", + "Media Citations" "Media Notes", "Media Attributes", "Metadata Viewer", diff --git a/src/plugins/view/sourceview.py b/src/plugins/view/sourceview.py index 4d42ce542..543bd58ca 100644 --- a/src/plugins/view/sourceview.py +++ b/src/plugins/view/sourceview.py @@ -2,6 +2,7 @@ # # 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 # it under the terms of the GNU General Public License as published by @@ -30,6 +31,8 @@ Source View # #------------------------------------------------------------------------- import gtk +import logging +LOG = logging.getLogger(".citation") #------------------------------------------------------------------------- # @@ -210,7 +213,10 @@ class SourceView(ListView): self.remove_selected_objects() def remove_object_from_handle(self, handle): - the_lists = Utils.get_source_referents(handle, self.dbstate.db) + the_lists = Utils.get_source_and_citation_referents(handle, + self.dbstate.db) + 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) diff --git a/src/plugins/view/view.gpr.py b/src/plugins/view/view.gpr.py index f2c38abc1..e13080fc5 100644 --- a/src/plugins/view/view.gpr.py +++ b/src/plugins/view/view.gpr.py @@ -3,6 +3,7 @@ # Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 2009 Benny Malengier +# 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 @@ -211,3 +212,33 @@ category = ("Sources", _("Sources")), viewclass = 'SourceView', order = START, ) + +register(VIEW, +id = 'citationlistview', +name = _("Citation View"), +description = _("The view showing all the citations"), +version = '1.0', +gramps_target_version = '3.4', +status = STABLE, +fname = 'citationlistview.py', +authors = [u"The Gramps project"], +authors_email = ["http://gramps-project.org"], +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 = ("Sources", _("Sources")), +viewclass = 'CitationTreeView', +order = START, + ) diff --git a/src/plugins/webreport/NarrativeWeb.py b/src/plugins/webreport/NarrativeWeb.py index 3fe0746fc..4e73e1d8a 100644 --- a/src/plugins/webreport/NarrativeWeb.py +++ b/src/plugins/webreport/NarrativeWeb.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -#!/usr/bin/python +#!/usr/bin/env python # # Gramps - a GTK+/GNOME based genealogy program # @@ -13,6 +13,7 @@ # Copyright (C) 2010 Doug Blank # Copyright (C) 2010 Jakim Friant # Copyright (C) 2010 Serge Noiraud +# 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 @@ -343,7 +344,7 @@ class BasePage(object): self.inc_families = report.options['inc_families'] self.inc_events = report.options['inc_events'] - def complete_people(self, tcell, first_person, handle_list, ppl_handle_list, up =True): + def complete_people(self, tcell, first_person, handle_list, ppl_handle_list, up =True): """ completes the person column for classes EventListPage and EventPage @@ -362,10 +363,11 @@ class BasePage(object): use_link = check_person_database(handle, ppl_handle_list) if use_link: url = self.report.build_url_fname_html(handle, "ppl", up) - tcell += Html("span", self.person_link(url, _obj, _NAME_STYLE_DEFAULT, gid =_obj.get_gramps_id()), - class_="person", inline=True) + tcell += Html("span", self.person_link(url, _obj, + _NAME_STYLE_DEFAULT, gid=_obj.get_gramps_id()), class_ ="person", inline =True) else: - tcell += Html("span", self.get_name(_obj), class_="person", inline =True) + tcell += Html("span", self.get_name(_obj), class_="person", + inline=True) # family event else: @@ -384,7 +386,7 @@ class BasePage(object): use_link = check_person_database(husband_handle, ppl_handle_list) if use_link: url = self.report.build_url_fname_html(husband_handle, "ppl", up) - hlink = self.person_link(url, husband, _NAME_STYLE_DEFAULT, gid =husband.get_gramps_id()) + hlink = self.person_link(url, husband, _NAME_STYLE_DEFAULT, gid = husband.get_gramps_id()) else: hlink = self.get_name(husband) @@ -392,7 +394,7 @@ class BasePage(object): use_link = check_person_database(spouse_handle, ppl_handle_list) if use_link: url = self.report.build_url_fname_html(spouse_handle, "ppl", up) - slink = self.person_link(url, spouse, _NAME_STYLE_DEFAULT, gid =spouse.get_gramps_id()) + slink = self.person_link(url, spouse, _NAME_STYLE_DEFAULT, gid = spouse.get_gramps_id()) else: slink = self.get_name(spouse) @@ -422,38 +424,40 @@ class BasePage(object): ["Type", str(attr.get_type()) ], ["Value", attr.get_value() ], ["Notes", self.dump_notes(attr.get_note_list()) ], - ["Sources", self.get_citation_links(attr.get_source_references()) ] ] + ["Sources", self.get_citation_links(attr.get_citation_list()) ] ] ) # return table row to its caller return trow - def get_citation_links(self, source_ref_list): + def get_citation_links(self, citation_handle_list): """ - get citation link from the source reference list + get citation link from the citation handle list - @param: source_ref_list = list of source references + @param: citation_handle_list = list of gen/lib/Citation """ - gid_list = [] lnk = (self.report.cur_fname, self.page_title, self.gid) - - for sref in source_ref_list: - handle = sref.get_reference_handle() - gid_list.append(sref) - - if handle in self.src_list: - if lnk not in self.src_list[handle]: - self.src_list[handle].append(lnk) - else: - self.src_list[handle] = [lnk] - text = "" - if len(gid_list): - for ref in gid_list: - index, key = self.bibli.add_reference(ref) - id_ = "%d%s" % (index+1, key) - text += ' [%s]' % (id_, id_) + + for citation_handle in citation_handle_list: + citation = self.report.database.get_citation_from_handle( + citation_handle) + + # Add the source information to src_list for use when displaying the + # Sources page + source_handle = citation.get_reference_handle() + if source_handle in self.src_list: + if lnk not in self.src_list[source_handle]: + self.src_list[source_handle].append(lnk) + else: + self.src_list[source_handle] = [lnk] + + # Add the citation information to the bibliography, and construct + # the citation reference text + index, key = self.bibli.add_reference(citation) + id_ = "%d%s" % (index+1, key) + text += ' [%s]' % (id_, id_) # return citation list text to its callers return text @@ -632,7 +636,7 @@ class BasePage(object): trow += Html("td", htmllist, class_ = "ColumnNotes") # get event source references - srcrefs = self.get_citation_links(event.get_source_references()) or " " + srcrefs = self.get_citation_links(event.get_citation_list()) or " " trow += Html("td", srcrefs, class_ = "ColumnSources") # return events table row to its callers @@ -646,21 +650,37 @@ class BasePage(object): @param: event -- event object from database @param: place_lat_long -- for use in Family Map Pages """ + place_handle = place.get_handle() placetitle = place.get_title() latitude = place.get_latitude() longitude = place.get_longitude() if (latitude and longitude): - found = any(data[3] == place.get_handle() for data in place_lat_long) + found = any(data[3] == place_handle for data in place_lat_long) if not found: latitude, longitude = conv_lat_lon(latitude, longitude, "D.D8") # 0 = latitude, 1 = longitude, 2 = place title, - # 3 = place handle, 4 = event date... + # 3 = place handle, 4 = event date, 5 = Marker color + # red = birth, blue = death, purple = census if latitude is not None: - place_lat_long.append( [latitude, longitude, placetitle, - place.get_handle(), event.get_date_object()] ) + + # get event type for color marker chooser... + etype = event.get_type() + if etype in [gen.lib.EventType.BIRTH, gen.lib.EventType.BAPTISM, + gen.lib.EventType.ADULT_CHRISTEN, gen.lib.EventType.CHRISTEN]: + marker = "Red" + elif etype in [gen.lib.EventType.DEATH, gen.lib.EventType.BURIAL]: + marker = "Blue" + elif etype == gen.lib.EventType.CENSUS: + marker = "Purple" + else: + marker = None + + if marker is not None: + place_lat_long.append([latitude, longitude, placetitle, + place.get_handle(), event.get_date_object(), marker]) def _get_event_place(self, person, ppl_handle_list, place_lat_long): """ @@ -674,7 +694,7 @@ class BasePage(object): # check to see if this person is in the report database? use_link = check_person_database(person.get_handle(), ppl_handle_list) - if use_link: + if use_link: evt_ref_list = person.get_event_ref_list() if evt_ref_list: for evt_ref in evt_ref_list: @@ -822,7 +842,7 @@ class BasePage(object): ["LDSTemple", ord.get_temple()], ["LDSPlace", place_hyper], ["LDSStatus", ord.get_status()], - ["LDSSources", self.get_citation_links(ord.get_source_references() )] ] + ["LDSSources", self.get_citation_links(ord.get_citation_list() )] ] ) # return table to its callers @@ -960,7 +980,7 @@ class BasePage(object): # get source citation list if showsrc in [True, None]: addr_data_row.append(["Sources", self.get_citation_links( - address.get_source_references() )]) + address.get_citation_list() )]) trow.extend( Html("td", value or " ", class_="Column" + colclass, inline=True) @@ -1755,7 +1775,9 @@ class BasePage(object): will create the "Source References" section for an object """ - map(self.bibli.add_reference, srcobj.get_source_references()) + map(lambda i: self.bibli.add_reference( + self.report.database.get_citation_from_handle(i)), + srcobj.get_citation_list()) sourcerefs = self.display_source_refs(self.bibli) # return to its callers @@ -1853,10 +1875,7 @@ class BasePage(object): """ creates a link to the family map """ - - # return hyperlink to its caller - return Html("a", _("Family Map"), href = url, title = _("Family Map"), - class_ = "familymap", inline = True) + return Html("a", _("Family Map"), href = url, title =_("Family Map"), class_ ="familymap", inline =True) def display_relationships(self, ppl_handle_list, place_lat_long): """ @@ -3209,21 +3228,19 @@ class PlacePage(BasePage): placedetail += Html("h4", _("Place Map"), inline =True) # begin map_canvas division - with Html("div", id ="map_canvas") as canvas: + with Html("div", id ="place_canvas") as canvas: placedetail += canvas # begin inline javascript code # because jsc is a docstring, it does NOT have to be properly indented with Html("script", type = "text/javascript") as jsc: + head += jsc if self.mapservice == "Google": - head += jsc jsc += google_jsc % (latitude, longitude) else: # do not need to write on head, load into canvas - jsc += openstreet_jsc % (Utils.xml_lang()[3:5].lower(), - longitude, latitude) - canvas += jsc + jsc += openstreet_jsc % (Utils.xml_lang()[3:5].lower(), longitude, latitude) # there is no need to add an ending "", # as it will be added automatically! @@ -3792,17 +3809,18 @@ class MediaPage(BasePage): self.XHTMLWriter(mediapage, of) def media_nav_link(self, handle, name, up = False): - + """ + Creates the Media Page Navigation hyperlinks for Next and Prev + """ url = self.report.build_url_fname_html(handle, "img", up) - img_name = html_escape(name) - hyper = Html("a", img_name, name = img_name, id = img_name, href = url, title = img_name, inline = True) - - # return hyperlink to its callers - return hyper + name = html_escape(name) + return Html("a", name, name =name, id =name, href =url, title =name, inline =True) def display_media_sources(self, photo): - map(self.bibli.add_reference, photo.get_source_references()) + map(lambda i: self.bibli.add_reference( + self.report.database.get_citation_from_handle(i)), + photo.get_citation_list()) sourcerefs = self.display_source_refs(self.bibli) # return source references to its caller @@ -4688,12 +4706,16 @@ class IndividualPage(BasePage): self.place_list = place_list self.sort_name = self.get_name(person) self.name = self.get_name(person) + + self.familymappages = self.report.options['familymappages'] + self.placemappages = self.report.options['placemappages'] + self.mapservice = self.report.options['mapservice'] + self.googleopts = self.report.options['googleopts'] db = report.database of = self.report.create_file(person.get_handle(), "ppl") self.up = True indivdetpage, head, body = self.write_header(self.sort_name) - self.familymappages = self.report.options['familymappages'] # attach the ancestortree style sheet if ancestor graph is being created? if self.report.options["ancestortree"]: @@ -4821,10 +4843,6 @@ class IndividualPage(BasePage): if not place_lat_long: return - self.familymappages = self.report.options['familymappages'] - self.mapservice = self.report.options['mapservice'] - self.googleopts = self.report.options['googleopts'] - minx, maxx = Decimal("0.00000001"), Decimal("0.00000001") miny, maxy = Decimal("0.00000001"), Decimal("0.00000001") xwidth, yheight = [], [] @@ -4832,7 +4850,7 @@ class IndividualPage(BasePage): number_markers = len(place_lat_long) if number_markers > 1: - for (lat, long, p, h, d) in place_lat_long: + for (lat, long, p, h, d, marker) in place_lat_long: xwidth.append(lat) yheight.append(long) xwidth.sort() @@ -4939,15 +4957,15 @@ class IndividualPage(BasePage): var myLatLng = new google.maps.LatLng(%s, %s); var myOptions = { - zoom: %d, - center: myLatLng, + zoom: %d, + center: myLatLng, mapTypeId: google.maps.MapTypeId.ROADMAP }; var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions); var flightPath = new google.maps.Polyline({ - path: %s, + path: %s, strokeColor: "#FF0000", strokeOpacity: 1.0, strokeWeight: 2 @@ -4962,74 +4980,69 @@ class IndividualPage(BasePage): midX_, midY_ = conv_lat_lon(place_lat_long[0][0], place_lat_long[0][1], "D.D8") jsc += """ - //""" % zoomlevel + bounds = new google.LatLngBounds(); + + for (var iterator = 0; iterator < myCoordinates.length; iterator++) { + var locations = myCoordinates[iterator]; + + var myLatLng = new google.maps.LatLng(locations[0], locations[1]); + + var image; + if (locations[2] = 'Red') + { + image = '../../../images/red_marker.png'; + } else if (locations[2] = 'Blue') + { + image = '../../../images/blue_marker.png'; + } else { + image = '../../../images/purple_marker.png'; + } + + var marker = new google.maps.Marker({ + position: myLatLng, + draggable: true, + title: locations[2], + icon: image, + map: map, + zIndex: locations[6] + }); + bounds.extend(myLatLng); + map.fitBounds(bounds); + } + }""" % zoomlevel # there is no need to add an ending "", # as it will be added automatically by libhtml()! - dont = """ -$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ - function setMarkers(map, locations) { - var bounds = new google.maps.LatLngBounds(); - for (var i = 0; i < locations.length; i++) { - var coordinates = locations[i]; - - var myLatLng = new google.maps.LatLng(coordinates[1], coordinates[2]); - var marker = new google.maps.Marker({ - position: myLatLng, - map: map, - title: coordinates[0] - }); - bounds.extend(locations[i]); - map.fitBounds(bounds); - }""" - with Html("div", class_ ="content", id ="FamilyMapDetail") as mapbackground: body += mapbackground @@ -5043,15 +5056,14 @@ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ "take you to that page’s page.") mapbackground += Html("p", msg, id = "description") - # if Google and Markers are selected, then add "Drop Markers" button? - if (self.mapservice == "Google" and self.googleopts == "Markers"): - button_ = Html("button", _("Drop Markers"), id ="drop", onclick ="drop()", inline =True) - mapbackground += button_ - # here is where the map is held in the CSS/ Page with Html("div", id ="map_canvas", inline =True) as canvas: mapbackground += canvas + # if Google and Markers are selected, then add "Drop Markers" button? + #if (self.mapservice == "Google" and self.googleopts == "Markers"): + # mapbackground += Html("button", _("Drop Markers"), id ="drop", onclick ="drop()", inline =True) + if self.mapservice == "OpenStreetMap": with Html("script", type ="text/javascript") as jsc: mapbackground += jsc @@ -5061,7 +5073,6 @@ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ jsc += openstreet_jsc % (Utils.xml_lang()[3:5].lower(), data[0], data[1] ) else: jsc += """ - //""" + controls['selector'].activate();""" with Html("div", class_ ="subsection", id ="references") as section: mapbackground += section @@ -5120,7 +5130,7 @@ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ # 0 = latitude, 1 = longitude, 2 = place title, 3 = handle, and 4 = date place_lat_long = sorted(place_lat_long, key =operator.itemgetter(4, 3, 0, 1)) - for (lat, long, pname, handle, date) in place_lat_long: + for (lat, long, pname, handle, date, marker) in place_lat_long: list = Html("li", self.place_link(handle, pname, up =self.up)) ordered += list @@ -5361,7 +5371,7 @@ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ person_link, person_ref.get_relation(), self.dump_notes(person_ref.get_note_list()), - self.get_citation_links(person_ref.get_source_references()), + self.get_citation_links(person_ref.get_citation_list()), ]: # get colclass from assoc_row @@ -5474,8 +5484,8 @@ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ for name in all_names: pname = _nd.display_name(name) if name == primary_name: - pname += self.get_citation_links(self.person.get_source_references() ) - pname += self.get_citation_links( name.get_source_references() ) + pname += self.get_citation_links(self.person.get_citation_list() ) + pname += self.get_citation_links( name.get_citation_list() ) # if we have just a firstname, then the name is preceeded by ", " # which doesn't exactly look very nice printed on the web page @@ -5513,7 +5523,7 @@ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ for name in all_names: call_name = name.get_call_name() if call_name and call_name != first_name: - call_name += self.get_citation_links(name.get_source_references() ) + call_name += self.get_citation_links(name.get_citation_list() ) trow = Html("tr") + ( Html("td", _("Call Name"), class_ = "ColumnAttribute", inline = True), Html("td", call_name, class_ = "ColumnValue", inline = True) @@ -5523,7 +5533,7 @@ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ # display the nickname attribute nick_name = self.person.get_nick_name() if nick_name and nick_name != first_name: - nick_name += self.get_citation_links(self.person.get_source_references() ) + nick_name += self.get_citation_links(self.person.get_citation_list() ) trow = Html("tr") + ( Html("td", _("Nick Name"), class_ = "ColumnAttribute", inline = True), Html("td", nick_name, class_ = "ColumnValue", inline = True) @@ -5646,9 +5656,7 @@ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ return None db = self.report.database - birthorder = self.report.options['birthorder'] - familymappages = self.report.options['familymappages'] # begin parents division with Html("div", class_ = "subsection", id = "parents") as section: @@ -6598,10 +6606,6 @@ class NavWebReport(Report): fname = CSS["NarrativeMaps"]["filename"] self.copy_file(fname, "narrative-maps.css", "styles") - # if OpenStreetMap is being used, copy blue marker? - if self.mapservice == "OpenStreetMap": - imgs += CSS["NarrativeMaps"]["images"] - # Copy the Creative Commons icon if the Creative Commons # license is requested if 0 < self.copyright <= len(_CC): @@ -6800,7 +6804,8 @@ class NavWebReport(Report): gc.collect() # Reduce memory usage when there are many images. next = None if index == total else photo_keys[index] # Notice. Here self.photo_list[photo_handle] is used not self.photo_list - MediaPage(self, self.title, photo_handle, source_list, self.photo_list[photo_handle], (prev, next, index, total)) + MediaPage(self, self.title, photo_handle, source_list, self.photo_list[photo_handle], + (prev, next, index, total)) self.user.step_progress() prev = photo_handle index += 1 diff --git a/src/plugins/webstuff/css/Web_Basic-Blue.css b/src/plugins/webstuff/css/Web_Basic-Blue.css index bddd1f63b..c9c5532a6 100644 --- a/src/plugins/webstuff/css/Web_Basic-Blue.css +++ b/src/plugins/webstuff/css/Web_Basic-Blue.css @@ -55,6 +55,13 @@ body { /* General Elements ================================================= */ +button#FamilyMap, button#Next, button#Prev { + background-color: purple; + color: #FFF; + font: bold .8em sans-serif; + padding: 10px; + border: solid 2px #00029D; +} div { margin: 0; padding: 0; diff --git a/src/plugins/webstuff/css/narrative-maps.css b/src/plugins/webstuff/css/narrative-maps.css index 9daeece59..14435675d 100644 --- a/src/plugins/webstuff/css/narrative-maps.css +++ b/src/plugins/webstuff/css/narrative-maps.css @@ -23,24 +23,33 @@ ###################################################### */ body#FamilyMap { background-color: #000; - margin-left: 7px; - margin-right: 7px; - width: 965px; -} -div#map_canvas { - margin-left: 10px; - margin-right: 10px; - border: solid 4px #000; - width: 931px; - height: 800px; + padding: 0 14px; + width: 965px; } button#drop { background-color: purple; - color: #FFF; - font: bold .8em sans-serif; - padding: 10px; - margin-top: 0px; - margin-bottom: 10px; - margin-left: 10px; - border: solid 2px green; + color: #FFF; + font: bold .8em sans-serif; + padding: 10px; + margin-top: 10px; + margin-left: 10px; + border: solid 4px #00029D; +} + +/* Family Maps +------------------------------------------------------ */ +div#map_canvas { + margin-left: 10px; + border: solid 4px #000; + width: 1200px; + height: 1000px; +} + +/* Place Maps +------------------------------------------------------ */ +div#place_canvas { + margin-left: 210px; + border: solid 4px #000; + width: 500px; + height: 400px; } diff --git a/src/plugins/webstuff/webstuff.py b/src/plugins/webstuff/webstuff.py index 5ddc04bdc..8983656e1 100644 --- a/src/plugins/webstuff/webstuff.py +++ b/src/plugins/webstuff/webstuff.py @@ -96,9 +96,6 @@ def load_on_reg(dbstate, uistate, plugin): ["Visually Impaired", 1, _("Visually Impaired"), path_css('Web_Visually.css'), "narrative-menus.css", [], [] ], - # no style sheet option - ["No style sheet",1, _("No style sheet"), [], None, [], [] ], - # ancestor tree style sheet and its images ["ancestortree", 0, "ancestortree", path_css("ancestortree.css"), None, @@ -137,12 +134,10 @@ def load_on_reg(dbstate, uistate, plugin): path_img("gramps-geo-birth.png"), path_img("gramps-geo-death.png"), path_img("gramps-geo-mainmap.png"), - path_img("gramps-geo-marriage.png")], + path_img("gramps-geo-marriage.png")], [] ], - [path_js("mapstraction", "mxn.core.js"), - path_js("mapstraction", "mxn.googlev3.core.js"), - path_js("mapstraction", "mxn.js"), - path_js("mapstraction", "mxn.openlayers.core.js")]], + # no style sheet option + ["No style sheet",1, _("No style sheet"), [], None, [], [] ], # all other images for use in NarrativeWeb ['All Images', 0, 'All Images', None, None, @@ -160,19 +155,6 @@ def load_on_reg(dbstate, uistate, plugin): # document image in case the media object is not an image ['Document', 0, 'Document', path_img("document.png"), None, [], []], - - # Google core javascript - ["Google Core", 0, "Google Core", - path_js("mapstraction", "mxn.google.core.js"), None, [], []], - - # Google Earth core javascript - ["Google Earth", 0, "Google Earth", - path_js("mapstraction", "mxn.googleearth.core.js"), None, [], []], - - # Google GeoCoder javascript - ["Google GeoCoder", 0, "Google GeoCoder", - path_js("mapstraction", "mxn.google.geocoder.js"), None, [], []], - ] return CSS_FILES