Initial prototype.

* Changed database to introduce Citations
* Introduced new Citation Primary object and CitationBase (equivalent to CitationRef) child object
* Implemented CitationTreeModel and CitationListModel
* Implemented CitationTreeView and CitationListView for new citation views in the navigator
* Implemented EditCitation which is used both for the citation views in the navigator and for the citations of an object
* Implemented the CitationEmbedList to display the citations of an object
* Modified the bottom bar gramplets to support citations
* Implemented the citation selector.
* Modified Media object to include references to Citations
* Initial work on deleting citations


svn: r17960
This commit is contained in:
Tim G L Lyons 2011-07-24 18:30:28 +00:00
parent 1012286be7
commit 9ff46d9eab
50 changed files with 3629 additions and 26 deletions

View File

@ -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."

View File

@ -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
@ -63,6 +64,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'),

View File

@ -365,6 +365,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"),

View File

@ -9,6 +9,7 @@ pkgdata_PYTHON = \
_SidebarFilter.py \
_PersonSidebarFilter.py\
_SourceSidebarFilter.py\
_CitationSidebarFilter.py\
_PlaceSidebarFilter.py\
_MediaSidebarFilter.py\
_RepoSidebarFilter.py\

View File

@ -0,0 +1,141 @@
#
# 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: _SourceSidebarFilter.py 17555 2011-05-24 20:31:54Z m_d_n $
#-------------------------------------------------------------------------
#
# Python modules
#
#-------------------------------------------------------------------------
from gen.ggettext import gettext as _
#-------------------------------------------------------------------------
#
# gtk
#
#-------------------------------------------------------------------------
import gtk
#-------------------------------------------------------------------------
#
# GRAMPS modules
#
#-------------------------------------------------------------------------
from Filters.SideBar import SidebarFilter
from Filters import GenericFilterFactory, build_filter_model, Rules
from Filters.Rules.Source import (RegExpIdOf, HasIdOf, HasSource,
HasNoteMatchingSubstringOf, HasNoteRegexp,
MatchesFilter)
# FIXME: need to add Citation filter rules.
GenericSourceFilter = GenericFilterFactory('Citation')
#-------------------------------------------------------------------------
#
# PersonSidebarFilter class
#
#-------------------------------------------------------------------------
class CitationSidebarFilter(SidebarFilter):
def __init__(self, dbstate, uistate, clicked):
self.clicked_func = clicked
self.filter_id = gtk.Entry()
self.filter_title = gtk.Entry()
self.filter_author = gtk.Entry()
self.filter_pub = gtk.Entry()
self.filter_note = gtk.Entry()
self.filter_regex = gtk.CheckButton(_('Use regular expressions'))
self.generic = gtk.ComboBox()
SidebarFilter.__init__(self, dbstate, uistate, "Source")
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('Source')
self.add_text_entry(_('ID'), self.filter_id)
self.add_text_entry(_('Title'), self.filter_title)
self.add_text_entry(_('Author'), self.filter_author)
self.add_text_entry(_('Publication'), self.filter_pub)
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_title.set_text('')
self.filter_author.set_text('')
self.filter_pub.set_text('')
self.filter_note.set_text('')
self.generic.set_active(0)
def get_filter(self):
gid = unicode(self.filter_id.get_text()).strip()
title = unicode(self.filter_title.get_text()).strip()
author = unicode(self.filter_author.get_text()).strip()
pub = unicode(self.filter_pub.get_text()).strip()
note = unicode(self.filter_note.get_text()).strip()
regex = self.filter_regex.get_active()
gen = self.generic.get_active() > 0
empty = not (gid or title or author or pub or note or regex or gen)
if empty:
generic_filter = None
else:
generic_filter = GenericSourceFilter()
if gid:
if regex:
rule = RegExpIdOf([gid])
else:
rule = HasIdOf([gid])
generic_filter.add_rule(rule)
rule = HasSource([title, author, pub], 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 == 'Source':
all_filter = GenericSourceFilter()
all_filter.set_name(_("None"))
all_filter.add_rule(Rules.Source.AllSources([]))
self.generic.set_model(build_filter_model('Source', [all_filter]))
self.generic.set_active(0)

View File

@ -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

View File

@ -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
@ -974,10 +975,22 @@ def get_source_referents(source_handle, db):
"""
_primaries = ('Person', 'Family', 'Event', 'Place',
'Source', 'MediaObject', 'Repository')
'Source', 'MediaObject', 'Repository', '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(citation_handle, db, _primaries))
def get_media_referents(media_handle, db):
""" Find objects that refer the media object.
@ -1456,11 +1469,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.ref)
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()

View File

@ -5,6 +5,7 @@
# Copyright (C) 2005-2007 Donald N. Allingham
# Copyright (C) 2008-2009 Gary Burton
# Copyright (C) 2009 Doug Blank <doug.blank@gmail.com>
# Copyright (C) 20111 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
@ -266,6 +267,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)

View File

@ -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
@ -610,6 +611,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
@ -735,6 +742,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.

View File

@ -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
@ -41,9 +42,9 @@ __all__ = (
'DBRECOVFN', 'DBLOGNAME', 'DBFLAGS_O', 'DBFLAGS_R', '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')
@ -82,5 +83,6 @@ REPOSITORY_KEY = 6
REFERENCE_KEY = 7
NOTE_KEY = 8
TAG_KEY = 9
CITATION_KEY = 10
TXNADD, TXNUPD, TXNDEL = 0, 1, 2

View File

@ -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, <object> 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,17 @@ 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.
"""
LOG.debug("cid_index %s" % [self.cid_trans])
self.cmap_index, gid = self.__find_next_gramps_id(self.citation_prefix,
self.cmap_index, self.cid_trans)
LOG.debug("gid %s" % gid)
return gid
def find_next_family_gramps_id(self):
"""
Return the next available GRAMPS' ID for a Family object based off the
@ -613,6 +646,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 +776,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 +879,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 +955,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 +1050,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 +1076,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 +1088,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 +1104,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 +1193,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 +1315,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 +1494,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 +1564,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 +1596,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 +1680,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,

View File

@ -25,6 +25,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
@ -38,6 +40,94 @@ from gen.db import BSDDBTxn
from gen.lib.nameorigintype import NameOriginType
from gen.db.write import _mkname, SURNAMES
def gramps_upgrade_16(self):
"""Upgrade database from version 15 to 16. This upgrade converts all
SourceRef child objects to Citation Primary objects.
"""
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)
LOG.debug("self %s" % self)
LOG.debug("self.find_next_citation_gramps_id %s" % self.find_next_citation_gramps_id)
# ---------------------------------
# Modify Media
# ---------------------------------
for media_handle in self.media_map.keys():
media = self.media_map[media_handle]
LOG.debug("upgrade media %s" % media[4])
if len(media) == 12:
LOG.debug(" len == 12")
(handle, gramps_id, path, mime, desc,
attribute_list, source_list, note_list, change,
date, tag_list, private) = media
new_citation_list = convert_sourceref_to_citation_15(self, source_list)
new_media = (handle, gramps_id, path, mime, desc,
attribute_list, source_list, note_list, change,
date, tag_list, new_citation_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)
self.update()
def convert_sourceref_to_citation_15(self, source_list):
new_citation_list = []
LOG.debug(" convert_sourceref_to_citation_15")
for source in source_list:
LOG.debug(" old sourceref %s" % [source])
(date, private, note_list, confidence, ref, page) = source
new_handle = self.create_id()
new_media_list = []
new_data_map = {}
new_change = time.time()
LOG.debug(" self %s" % [self])
# FIXME: I don't understand why I can't use find_next_citation_gramps_id.
# Attempting to use it fails. This seems to be because cid_trans
# is not initialised properly. However I don't understand how this
# is ever initialised.
# Also, self.cmap_index does not seem to be initialised, but
# again I don't see how it is initialised for find_next_citation_gramps_id
# Should self.citation_map and/or cmap_index be committed to the
# database after being updated?
LOG.debug(" cmap_index %s" % self.cmap_index)
LOG.debug(" len(self.citation_map) %s" % len(self.citation_map))
(self.cmap_index, new_gramps_id) = \
__find_next_gramps_id(self, self.citation_prefix,
self.cmap_index)
LOG.debug(" new_gramps_id %s" % new_gramps_id)
new_citation = (new_handle, new_gramps_id,
date, page, confidence, ref, note_list, new_media_list,
new_data_map, new_change, private)
LOG.debug(" new_citation %s" % [new_citation])
with BSDDBTxn(self.env, self.citation_map) as txn:
txn.put(str(new_handle), new_citation)
new_citation_list.append((new_handle))
return new_citation_list
def __find_next_gramps_id(self, prefix, map_index):
"""
Helper function for find_next_<object>_gramps_id methods
"""
index = prefix % map_index
# This uses a generator expression, see PEP 289:
# http://www.python.org/dev/peps/pep-0289/
# This avoids evaluating a whole list at once.
# This is equivalent to:
# used_ids = {}
# for handle in self.citation_map.keys()
# used_ids += self.citation_map[handle][1]
used_ids = (self.citation_map[handle][1] for handle in self.citation_map.keys())
for i in used_ids:
LOG.debug(" used_ids %s" % i)
while index in used_ids:
map_index += 1
index = prefix % map_index
map_index += 1
return (map_index, index)
def gramps_upgrade_15(self):
"""Upgrade database from version 14 to 15. This upgrade adds:
* tagging

View File

@ -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, DbVersionError, DbEnvironmentError,
DbUpgradeRequiredError, find_surname, find_surname_name,
@ -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]
@ -370,8 +377,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
@ -475,6 +483,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),
@ -576,6 +585,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'))
@ -622,6 +632,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),
@ -644,6 +655,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),
@ -660,6 +672,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)
@ -686,6 +699,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),
@ -935,6 +949,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),
@ -979,6 +994,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())
@ -1044,6 +1060,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()
@ -1059,6 +1076,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()
@ -1071,6 +1089,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
@ -1088,6 +1107,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
@ -1151,6 +1171,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
@ -1278,6 +1309,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
@ -1515,6 +1554,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
@ -1821,6 +1874,8 @@ 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)
_LOG.debug("Upgrade time: %d seconds" % int(time.time()-t))

View File

@ -15,6 +15,8 @@ pkgdata_PYTHON = \
calendar.py \
childref.py \
childreftype.py \
citation.py \
citationbase.py \
const.py \
datebase.py \
date.py \

View File

@ -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
@ -52,6 +53,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

336
src/gen/lib/citation.py Normal file
View File

@ -0,0 +1,336 @@
#
# 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: src.py 16425 2011-01-21 16:27:54Z gbritton $
"""
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
from gen.lib.refbase import RefBase
from gen.lib.const import DIFFERENT, EQUAL, IDENTICAL
#-------------------------------------------------------------------------
#
# Citation class
#
#-------------------------------------------------------------------------
class Citation(MediaBase, NoteBase, PrimaryObject, RefBase, DateBase):
"""A record of a citation of a source of information."""
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
RefBase.__init__(self) # 5
self.page = "" # 3
self.confidence = Citation.CONF_NORMAL # 4
self.datamap = {} # 8
def serialize(self):
"""
Convert the object to a serialized tuple of data.
"""
return (self.handle, # 0
self.gramps_id, # 1
DateBase.serialize(self), # 2
unicode(self.page), # 3
self.confidence, # 4
RefBase.serialize(self), # 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
ref, # 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)
RefBase.unserialize(self, ref)
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
"""
# FIXME: it appears that this is only called for 'Event', 'Person',
# 'Place' and 'Repository', hence this is untested and may be
# unnecessary.
# FIXME: and libgrdb find_backlink_handles for all primary types
# should add 'Note', 'Media', 'Source'
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
"""
# FIXME: The following primary objects can refer to Citations:
# Person, Family, Event, MediaObject, Place
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
"""
# FIXME: The following primary objects can refer to Citations:
# Person, Family, Event, MediaObject, Place
if classname == 'Source' and \
RefBase.get_reference_handle(self) == old_handle:
self.ref = RefBase.set_reference_handle(self, 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
"""
# FIXME: Presumably this does not include references to primary objects
# (Notes, Media, Source) that contain text
return [self.page,
self.gramps_id] + self.datamap.keys() + self.datamap.values()
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
"""
# FIXME: Apparently does not include 'Note' child objects
return self.media_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
"""
# FIXME: should this also return the source reference child
# secondary object as this will refer to a Source Primary object
return self.media_list + self.ref
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
"""
# FIXME: should this also return the source reference child
# secondary object as this will refer to a Source Primary object
# that can itself refer to notes
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
"""
# FIXME: should this also return the source reference child
# secondary object as this will refer to a Primary objects,
# namely the Source object?
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
"""
# FIXME: Apparently this does not include 'Media'
ret = self.get_referenced_note_handles()
if self.ref:
ret += [('Source', self.ref)]
LOG.debug ("Citation: %s get_referenced_handles: %s" %
(self.page, ret))
return ret
def has_source_reference(self, src_handle) :
"""
Return True if any of the child objects has reference to this source
handle.
:param src_handle: The source handle to be checked.
:type src_handle: str
:returns: Returns whether any of it's child objects has reference to
this source handle.
:rtype: bool
"""
for item in self.get_sourcref_child_list():
if item.has_source_reference(src_handle):
return True
return False
def remove_source_references(self, src_handle_list):
"""
Remove references to all source handles in the list in all child
objects.
:param src_handle_list: The list of source handles to be removed.
:type src_handle_list: list
"""
for item in self.get_sourcref_child_list():
item.remove_source_references(src_handle_list)
def replace_source_references(self, old_handle, new_handle):
"""
Replace references to source_handles in the list in this object and
all child objects and merge equivalent entries.
:param old_handle: The source handle to be replaced.
:type old_handle: str
:param new_handle: The source handle to replace the old one with.
:type new_handle: str
"""
for item in self.get_sourcref_child_list():
item.replace_source_references(old_handle, new_handle)
def 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 SourceRef."""
self.page = page
def get_page(self):
"""Get the page indicator of the SourceRef."""
return self.page

208
src/gen/lib/citationbase.py Normal file
View File

@ -0,0 +1,208 @@
#
# 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: notebase.py 15645 2010-07-22 02:16:32Z dsblank $
"""
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(self, handle):
"""
Remove the specified handle from the list of citation handles, and all
secondary child objects.
:param handle: :class:`~gen.lib.citation.Citation` handle to remove from the list of citations
:type handle: str
"""
LOG.debug('enter remove_citation handle %s' % handle)
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(handle)
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 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
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)

View File

@ -32,6 +32,8 @@ Media object for GRAMPS.
#
#-------------------------------------------------------------------------
import os
import logging
LOG = logging.getLogger(".citation")
#-------------------------------------------------------------------------
#
@ -40,6 +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(SourceBase, CitationBase, NoteBase, DateBase, AttributeBase,
TagBase, PrimaryObject):
"""
Container for information about an image file, including location,
@ -73,6 +76,7 @@ class MediaObject(SourceBase, NoteBase, DateBase, AttributeBase,
DateBase.__init__(self, source)
AttributeBase.__init__(self, source)
TagBase.__init__(self)
CitationBase.__init__(self)
if source:
self.path = source.path
@ -110,6 +114,7 @@ class MediaObject(SourceBase, NoteBase, DateBase, AttributeBase,
self.change,
DateBase.serialize(self, no_text_date),
TagBase.serialize(self),
CitationBase.serialize(self),
self.private)
def unserialize(self, data):
@ -122,13 +127,16 @@ class MediaObject(SourceBase, NoteBase, DateBase, AttributeBase,
"""
(self.handle, self.gramps_id, self.path, self.mime, self.desc,
attribute_list, source_list, note_list, self.change,
date, tag_list, self.private) = data
date, tag_list,
citation_list,
self.private) = data
AttributeBase.unserialize(self, attribute_list)
SourceBase.unserialize(self, source_list)
NoteBase.unserialize(self, note_list)
DateBase.unserialize(self, date)
TagBase.unserialize(self, tag_list)
CitationBase.unserialize(self, citation_list)
def get_text_data_list(self):
"""
@ -158,6 +166,18 @@ class MediaObject(SourceBase, NoteBase, DateBase, AttributeBase,
"""
return self.attribute_list
def get_citation_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
"""
# N.B. the citation_list of the media object is not a child object
# it is a direct reference from Media to a citation.
return []
def get_note_child_list(self):
"""
Return the list of child secondary objects that may refer notes.
@ -166,7 +186,7 @@ class MediaObject(SourceBase, NoteBase, DateBase, AttributeBase,
refer notes.
:rtype: list
"""
return self.attribute_list + self.source_list
return self.attribute_list + self.source_list + self.citation_list
def get_referenced_handles(self):
"""
@ -176,8 +196,14 @@ class MediaObject(SourceBase, NoteBase, DateBase, AttributeBase,
:returns: List of (classname, handle) tuples for referenced objects.
:rtype: list
"""
LOG.debug ("Media: %s get_referenced_handles: %s" %
(self.desc,
self.get_referenced_note_handles() +
self.get_referenced_tag_handles() +
self.get_referenced_citation_handles()))
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,6 +213,11 @@ class MediaObject(SourceBase, NoteBase, DateBase, AttributeBase,
:returns: Returns the list of objects referencing primary objects.
:rtype: list
"""
LOG.debug ("Media: %s get_handle_referents: %s" %
(self.desc,
self.attribute_list + self.source_list))
# FIXME: This is wrong, because it returns the handle, when it should return the
# citation object. This is probably because the citation unpack has not been done.
return self.attribute_list + self.source_list
def merge(self, acquisition):
@ -203,6 +234,7 @@ class MediaObject(SourceBase, NoteBase, DateBase, AttributeBase,
self._merge_note_list(acquisition)
self._merge_source_reference_list(acquisition)
self._merge_tag_list(acquisition)
self.merge_citation_list(acquisition)
def set_mime_type(self, mime_type):
"""

View File

@ -44,6 +44,7 @@ EVENTKEY = 'event'
PLACEKEY = 'place'
MEDIAKEY = 'media'
SOURCEKEY = 'source'
CITATIONKEY = 'citation'
REPOKEY = 'repository'
NOTEKEY = 'note'
@ -53,7 +54,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 +66,7 @@ EVENTCLASS = 'Event'
PLACECLASS = 'Place'
MEDIACLASS = 'MediaObject'
SOURCECLASS = 'Source'
CITATIONCLASS = 'Citation'
REPOCLASS = 'Repository'
NOTECLASS = 'Note'
@ -75,6 +77,7 @@ CLASS2KEY = {
PLACECLASS: PLACEKEY,
MEDIACLASS: MEDIAKEY,
SOURCECLASS: SOURCEKEY,
CITATIONCLASS: CITATIONKEY,
REPOCLASS: REPOKEY,
NOTECLASS: NOTEKEY
}
@ -116,6 +119,7 @@ class CallbackManager(object):
PLACEKEY: [],
MEDIAKEY: [],
SOURCEKEY: [],
CITATIONKEY: [],
REPOKEY: [],
NOTEKEY: [],
}
@ -198,6 +202,7 @@ class CallbackManager(object):
PLACEKEY: [],
MEDIAKEY: [],
SOURCEKEY: [],
CITATIONKEY: [],
REPOKEY: [],
NOTEKEY: [],
}
@ -334,6 +339,7 @@ def directhandledict(baseobj):
PLACEKEY: [],
MEDIAKEY: [],
SOURCEKEY: [],
CITATIONKEY: [],
REPOKEY: [],
NOTEKEY: [],
}
@ -353,6 +359,7 @@ def handledict(baseobj):
PLACEKEY: [],
MEDIAKEY: [],
SOURCEKEY: [],
CITATIONKEY: [],
REPOKEY: [],
NOTEKEY: [],
}

View File

@ -21,6 +21,7 @@ dist_pkgdata_DATA = \
configure.glade \
dateedit.glade \
editsource.glade \
editcitation.glade \
styleeditor.glade \
dbman.glade \
editurl.glade \

View File

@ -0,0 +1,565 @@
<?xml version="1.0"?>
<interface>
<!-- interface-requires gtk+ 2.12 -->
<!-- interface-requires grampswidgets 0.0 -->
<!-- interface-naming-policy project-wide -->
<object class="GtkDialog" id="editcitation">
<property name="default_width">600</property>
<property name="type_hint">dialog</property>
<property name="has_separator">False</property>
<child internal-child="vbox">
<object class="GtkVBox" id="dialog-vbox17">
<property name="visible">True</property>
<child>
<object class="GtkLabel" id="label602">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="xpad">6</property>
<property name="ypad">3</property>
<property name="label" translatable="yes">&lt;b&gt;Reference information&lt;/b&gt;</property>
<property name="use_markup">True</property>
<property name="use_underline">True</property>
<property name="justify">center</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkNotebook" id="notebook_ref">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="border_width">6</property>
<child>
<object class="GtkTable" id="table67">
<property name="visible">True</property>
<property name="border_width">12</property>
<property name="n_rows">3</property>
<property name="n_columns">3</property>
<property name="column_spacing">12</property>
<property name="row_spacing">6</property>
<child>
<object class="GtkLabel" id="label612">
<property name="visible">True</property>
<property name="xalign">1</property>
<property name="label" translatable="yes">_Date:</property>
<property name="use_underline">True</property>
<property name="justify">center</property>
<property name="mnemonic_widget">date_entry</property>
</object>
<packing>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="GtkButton" id="date_stat">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="has_tooltip">True</property>
<property name="tooltip_text" translatable="yes">Invoke date editor</property>
<property name="relief">none</property>
<child>
<object class="GtkImage" id="image2682">
<property name="visible">True</property>
<property name="icon_name">gramps-date</property>
</object>
</child>
</object>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="UndoableEntry" id="volume">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">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. </property>
<property name="invisible_char">&#x25CF;</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label613">
<property name="visible">True</property>
<property name="xalign">1</property>
<property name="label" translatable="yes">_Volume/Page:</property>
<property name="use_underline">True</property>
<property name="justify">center</property>
<property name="mnemonic_widget">volume</property>
</object>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label614">
<property name="visible">True</property>
<property name="xalign">1</property>
<property name="label" translatable="yes">Con_fidence:</property>
<property name="use_underline">True</property>
<property name="justify">center</property>
<property name="mnemonic_widget">confidence</property>
</object>
<packing>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="GtkToggleButton" id="privacy">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="relief">none</property>
<child>
<object class="GtkImage" id="image2677">
<property name="visible">True</property>
<property name="icon_name">gramps-unlock</property>
</object>
</child>
</object>
<packing>
<property name="left_attach">2</property>
<property name="right_attach">3</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="GtkComboBox" id="confidence">
<property name="visible">True</property>
<property name="tooltip_text" translatable="yes">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 </property>
<property name="model">confidence_model</property>
<child>
<object class="GtkCellRendererText" id="cellrenderertext1"/>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="ValidatableMaskedEntry" id="date_entry">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">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. </property>
<property name="invisible_char">&#x25CF;</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<placeholder/>
</child>
</object>
</child>
<child type="tab">
<object class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="label" translatable="yes">&lt;b&gt;General&lt;/b&gt;</property>
<property name="use_markup">True</property>
</object>
<packing>
<property name="tab_fill">False</property>
</packing>
</child>
</object>
<packing>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkExpander" id="src_expander">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="border_width">6</property>
<property name="expanded">True</property>
<property name="spacing">6</property>
<child>
<object class="GtkNotebook" id="notebook_src">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="show_border">False</property>
<child>
<object class="GtkTable" id="table68">
<property name="visible">True</property>
<property name="border_width">12</property>
<property name="n_rows">6</property>
<property name="n_columns">2</property>
<property name="column_spacing">12</property>
<property name="row_spacing">6</property>
<child>
<object class="GtkLabel" id="label603">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">_Title:</property>
<property name="use_underline">True</property>
<property name="justify">center</property>
<property name="mnemonic_widget">title</property>
</object>
<packing>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label604">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">_Author:</property>
<property name="use_underline">True</property>
<property name="justify">center</property>
<property name="mnemonic_widget">author</property>
</object>
<packing>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label606">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="ypad">3</property>
<property name="label" translatable="yes">A_bbreviation:</property>
<property name="use_underline">True</property>
<property name="justify">center</property>
<property name="mnemonic_widget">abbrev</property>
</object>
<packing>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label607">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">_Pub. Info.:</property>
<property name="use_underline">True</property>
<property name="justify">center</property>
<property name="mnemonic_widget">pub_info</property>
</object>
<packing>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="UndoableEntry" id="author">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">Authors of the source.</property>
<property name="invisible_char">&#x25CF;</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">1</property>
<property name="bottom_attach">2</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="GtkHBox" id="warn_box">
<property name="border_width">6</property>
<property name="spacing">12</property>
<child>
<object class="GtkImage" id="warn_icon">
<property name="visible">True</property>
<property name="stock">gtk-dialog-warning</property>
<property name="icon-size">6</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="warn_text">
<property name="width_request">500</property>
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="ypad">3</property>
<property name="label" translatable="yes">&lt;b&gt;Note:&lt;/b&gt; Any changes in the shared source information will be reflected in the source itself, for all items that reference the source.</property>
<property name="use_markup">True</property>
<property name="use_underline">True</property>
<property name="wrap">True</property>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="right_attach">2</property>
<property name="top_attach">5</property>
<property name="bottom_attach">6</property>
<property name="x_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="UndoableEntry" id="abbrev">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">Provide a short title used for sorting, filing, and retrieving source records.</property>
<property name="invisible_char">&#x25CF;</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="UndoableEntry" id="pub_info">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">Publication Information, such as city and year of publication, name of publisher, ...</property>
<property name="invisible_char">&#x25CF;</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">4</property>
<property name="bottom_attach">5</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label611">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">_ID:</property>
<property name="use_underline">True</property>
<property name="mnemonic_widget">gid</property>
</object>
<packing>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="GtkHBox" id="hbox135">
<property name="visible">True</property>
<property name="spacing">12</property>
<child>
<object class="UndoableEntry" id="gid">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">A unique ID to identify the source</property>
<property name="invisible_char">&#x25CF;</property>
</object>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkToggleButton" id="private">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="relief">none</property>
<child>
<object class="GtkImage" id="image2709">
<property name="visible">True</property>
<property name="icon_name">gramps-unlock</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="top_attach">2</property>
<property name="bottom_attach">3</property>
<property name="x_options">GTK_FILL</property>
<property name="y_options"></property>
</packing>
</child>
<child>
<object class="UndoableEntry" id="title">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tooltip_text" translatable="yes">Title of the source.</property>
<property name="invisible_char">&#x25CF;</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">2</property>
<property name="y_options"></property>
</packing>
</child>
</object>
<packing>
<property name="tab_fill">False</property>
</packing>
</child>
<child type="tab">
<object class="GtkHBox" id="hbox127">
<property name="visible">True</property>
<child>
<object class="GtkImage" id="image2681">
<property name="stock">gtk-file</property>
<property name="icon-size">1</property>
</object>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label609">
<property name="visible">True</property>
<property name="label" translatable="yes">&lt;b&gt;General&lt;/b&gt;</property>
<property name="use_markup">True</property>
<property name="justify">center</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="tab_fill">False</property>
</packing>
</child>
</object>
</child>
<child type="label">
<object class="GtkLabel" id="label610">
<property name="visible">True</property>
<property name="label" translatable="yes">&lt;b&gt;Shared source information&lt;/b&gt;</property>
<property name="use_markup">True</property>
</object>
</child>
</object>
<packing>
<property name="position">3</property>
</packing>
</child>
<child internal-child="action_area">
<object class="GtkHButtonBox" id="dialog-action_area17">
<property name="visible">True</property>
<property name="layout_style">end</property>
<child>
<object class="GtkButton" id="help">
<property name="label">gtk-help</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="cancel">
<property name="label">gtk-cancel</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="ok">
<property name="label">gtk-ok</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can_default">True</property>
<property name="receives_default">True</property>
<property name="use_stock">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="pack_type">end</property>
<property name="position">0</property>
</packing>
</child>
</object>
</child>
<action-widgets>
<action-widget response="-11">help</action-widget>
<action-widget response="-6">cancel</action-widget>
<action-widget response="-5">ok</action-widget>
</action-widgets>
</object>
<object class="GtkListStore" id="confidence_model">
<columns>
<!-- column-name gchararray1 -->
<column type="gchararray"/>
</columns>
</object>
</interface>

View File

@ -14,6 +14,7 @@ pkgdata_PYTHON = \
editaddress.py \
editattribute.py \
editchildref.py \
editcitation.py \
editevent.py \
editeventref.py \
editfamily.py \

View File

@ -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
@ -21,6 +22,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
@ -47,6 +49,7 @@ EDITORS = {
'Family': EditFamily,
'Media': EditMedia,
'Source': EditSource,
'Citation': EditCitation,
'Place': EditPlace,
'Repository': EditRepository,
'Note': EditNote,

View File

@ -11,6 +11,9 @@ pkgdata_PYTHON = \
backrefmodel.py \
buttontab.py \
childmodel.py \
citationbackreflist.py \
citationenbededlist.py \
citationrefmodel.py \
dataembedlist.py \
datamodel.py \
embeddedlist.py \

View File

@ -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

View File

@ -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:

View File

@ -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: sourcebackreflist.py 13821 2009-12-16 06:11:06Z pez4brian $
#-------------------------------------------------------------------------
#
# 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'

View File

@ -0,0 +1,220 @@
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2000-2007 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: notetab.py 14091 2010-01-18 04:42:17Z pez4brian $
#-------------------------------------------------------------------------
#
# 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 gui.dbguielement import DbGUIElement
from gui.selectors import SelectorFactory
from citationrefmodel import CitationRefModel
from embeddedlist import EmbeddedList
from DdTargets import DdTargets
from gen.lib.refbase import RefBase
#-------------------------------------------------------------------------
#
# 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.NOTE_LINK
_MSG = {
'add' : _('Create and add a new citation'),
'del' : _('Remove the existing citation'),
'edit' : _('Edit the selected citation'),
'share' : _('Add an existing citation'),
'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, _("_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, # delete a citation we track
'citation-update': self.citation_update, # change a citation we track
})
self.callman.connect_all(keys=['citation'])
def get_icon_name(self):
"""
Return the stock-id icon name associated with the display tab
"""
return 'gramps-citations'
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.
"""
citation = gen.lib.Citation()
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)
citation = sel.run()
LOG.debug("selected citation: %s" % citation)
if citation:
try:
source = self.dbstate.db.get_source_from_handle(citation.ref)
from gui.editors import EditCitation
EditCitation(self.dbstate, self.uistate, self.track,
citation, source, self.add_callback,
self.callertitle)
except Errors.WindowActiveError:
from QuestionDialog import WarningDialog
WarningDialog(_("Cannot share this reference"),
self.__blocked_text())
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()
LOG.debug('selected handle %s' % handle)
if handle:
citation = self.dbstate.db.get_citation_from_handle(handle)
LOG.debug("selected citation: %s" % citation)
source = self.dbstate.db.get_source_from_handle(citation.ref)
try:
from gui.editors import EditCitation
EditCitation(self.dbstate, self.uistate, self.track, citation,
source, callertitle = self.callertitle)
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
# FIXME: Are these functions needed for citations?
# def get_editor(self):
# pass
#
# def get_user_values(self):
# return []

View File

@ -0,0 +1,44 @@
#
# 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: notemodel.py 15597 2010-06-28 07:43:41Z ldnp $
#-------------------------------------------------------------------------
#
# GTK libraries
#
#-------------------------------------------------------------------------
import gtk
#-------------------------------------------------------------------------
#
# CitationModel
#
#-------------------------------------------------------------------------
class CitationRefModel(gtk.ListStore):
def __init__(self, citation_list, db):
gtk.ListStore.__init__(self, str, str, str, str, object)
self.db = db
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, ])

View File

@ -0,0 +1,438 @@
#
# 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: editsource.py 16680 2011-02-20 10:52:06Z bmcage $
#-------------------------------------------------------------------------
#
# Python modules
#
#-------------------------------------------------------------------------
from gen.ggettext import gettext as _
import logging
LOG = logging.getLogger(".citation")
#-------------------------------------------------------------------------
#
# GTK/Gnome modules
#
#-------------------------------------------------------------------------
import gtk
#-------------------------------------------------------------------------
#
# gramps modules
#
#-------------------------------------------------------------------------
import gen.lib
from gen.db import DbTxn
from editprimary import EditPrimary
from displaytabs import (NoteTab, GalleryTab, DataEmbedList,
SourceBackRefList, RepoEmbedList)
from gui.widgets import (MonitoredEntry, PrivacyButton, MonitoredMenu,
MonitoredDate)
from QuestionDialog import ErrorDialog
from editreference import RefTab
from glade import Glade
#-------------------------------------------------------------------------
#
# EditCitationclass
#
#-------------------------------------------------------------------------
class EditCitation(EditPrimary):
def __init__(self, dbstate, uistate, track, obj, source, callback=None,
callertitle = None):
"""
Create an EditCitation window. Associate a citation with the window.
This class is called both to edit the Citation Primary object
and to edit references from other objects to citation.
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
"""
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)
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):
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
# FIXME: There will have to be two warnings,
# one because Source may be shared and one because Citation may be shared.
# These three functions 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_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.source-width'
self.height_key = 'interface.source-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_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).
"""
# FIXME: Should this be modified so that the 'close' routines
# are executed not only for the 'Citation', bit also for the 'Source'
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)
# FIXME: This needs to be changed to reflect the correct names
# and uncommented when src/glade/editcitation.glade
# has been amended to include an id in the Citation section.
# self.gid = MonitoredEntry(
# self.glade.get_object('gid'), 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.obj, 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.SOURCEREF)
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")
# FIXME: This needs to enable the shared Citation warning box
self.citationref_list = SourceBackRefList(self.dbstate,self.uistate, self.track,
self.db.find_backlink_handles(self.obj.handle),
self.enable_warnbox
)
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")
# FIXME:
# SourceBackrefList inherits from BackrefList inherits from EmbeddedList
# inherits from ButtonTab
# _create_buttons is defined in ButtonTab, and overridden in BackRefList.
# But needs to be overriden here so that there is no edit button for
# References to Source, because they will all be citations,
# and the Citations will be displayed in the top part of the
# edit dialogue.
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)
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
(uses_dupe_id, id) = self._uses_duplicate_id()
if uses_dupe_id:
prim_object = self.get_from_gramps_id(id)
name = prim_object.get_page()
msg1 = _("Cannot save citation. ID already exists.")
msg2 = _("You have attempted to use the existing Gramps ID with "
"value %(id)s. This value is already used by '"
"%(prim_object)s'. Please enter a different ID or leave "
"blank to get the next available ID value.") % {
'id' : id, 'prim_object' : name }
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.ref = 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)
LOG.debug(msg)
if self.callback:
self.callback(self.obj.get_handle())
self.close()
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)

View File

@ -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
@ -51,7 +52,7 @@ from editprimary import EditPrimary
from gui.widgets import (MonitoredDate, MonitoredEntry, PrivacyButton,
MonitoredTagList)
from displaytabs import (SourceEmbedList, AttrEmbedList, NoteTab,
MediaBackRefList)
CitationEmbedList, MediaBackRefList)
from addmedia import AddMediaObject
from QuestionDialog import ErrorDialog
from glade import Glade
@ -213,6 +214,14 @@ class EditMedia(EditPrimary):
self._add_tab(notebook, self.note_tab)
self.track_ref_for_deletion("note_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.backref_tab = MediaBackRefList(self.dbstate,
self.uistate,
self.track,

View File

@ -29,6 +29,7 @@
from gen.ggettext import gettext as _
import logging
log = logging.getLogger(".")
LOG = logging.getLogger(".citation")
#-------------------------------------------------------------------------
#
@ -221,10 +222,62 @@ class DeleteSrcQuery(object):
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
# we can have:
# object(CitationBase) -> Citation(RefBase) -> Source
# We first have to remove the
(person_list, family_list, event_list, place_list, source_list,
media_list, repo_list, citation_list, citation_referents_list) = self.the_lists
# (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
# for handle in person_list:
# person = self.db.get_person_from_handle(handle)
# person.remove_citation(citation_handle)
# self.db.commit_person(person, trans)
#
# for handle in family_list:
# family = self.db.get_family_from_handle(handle)
# family.remove_citation(citation_handle)
# self.db.commit_family(family, trans)
#
# for handle in event_list:
# event = self.db.get_event_from_handle(handle)
# event.remove_citation(citation_handle)
# self.db.commit_event(event, trans)
#
# for handle in place_list:
# place = self.db.get_place_from_handle(handle)
# place.remove_citation(citation_handle)
# self.db.commit_place(place, trans)
#
# for handle in source_list:
# source = self.db.get_source_from_handle(handle)
# source.remove_citation(citation_handle)
# self.db.commit_source(source, trans)
#
for handle in media_list:
media = self.db.get_object_from_handle(handle)
media.remove_citation(citation_handle)
self.db.commit_media_object(media, trans)
# for handle in repo_list:
# repo = self.db.get_repository_from_handle(handle)
# repo.remove_citation(citation_handle)
# self.db.commit_repository(repo, trans)
# (2) delete the actual citation
LOG.debug('remove the actual citations %s' % citation_list)
for citation_handle in citation_list:
self.db.remove_citation(citation_handle, trans)
# (3) delete the references to the source
src_handle_list = [self.source.get_handle()]
LOG.debug('remove the source references to %s' % src_handle_list)
for handle in person_list:
person = self.db.get_person_from_handle(handle)
@ -261,5 +314,9 @@ class DeleteSrcQuery(object):
repo.remove_source_references(src_handle_list)
self.db.commit_repository(repo, trans)
# FIXME: we need to remove all the citations that point to this source object,
# and before that, all the CitationBase pointers in all objects that point to
# this source.
self.db.enable_signals()
self.db.remove_source(self.source.get_handle(), trans)

View File

@ -8,6 +8,7 @@ pkgdatadir = $(datadir)/@PACKAGE@/gui/selectors
pkgdata_PYTHON = \
__init__.py \
baseselector.py \
selectcitation.py \
selectevent.py \
selectfamily.py \
selectnote.py \

View File

@ -0,0 +1,69 @@
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2003-2006 Donald N. Allingham
# 2009 Gary Burton
#
# 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: selectsource.py 14091 2010-01-18 04:42:17Z pez4brian $
#-------------------------------------------------------------------------
#
# 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_citation_from_handle
def get_handle_column(self):
return 11

View File

@ -38,6 +38,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

View File

@ -16,6 +16,7 @@ pkgdata_PYTHON = \
placemodel.py \
repomodel.py \
sourcemodel.py \
citationmodel.py \
treebasemodel.py
pkgpyexecdir = @pkgpyexecdir@/gui/views/treemodels

View File

@ -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,4 @@ from placemodel import PlaceBaseModel, PlaceListModel, PlaceTreeModel
from mediamodel import MediaModel
from repomodel import RepositoryModel
from notemodel import NoteModel
from citationmodel import CitationBaseModel, CitationListModel, CitationTreeModel

View File

@ -0,0 +1,317 @@
#
# 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:_SourceModel.py 9912 2008-01-22 09:17:46Z acraphae $
#-------------------------------------------------------------------------
#
# python modules
#
#-------------------------------------------------------------------------
import cgi
import logging
log = logging.getLogger(".")
LOG = logging.getLogger(".citation")
#-------------------------------------------------------------------------
#
# GNOME/GTK modules
#
#-------------------------------------------------------------------------
import gtk
#-------------------------------------------------------------------------
#
# GRAMPS modules
#
#-------------------------------------------------------------------------
import const
import ToolTips
import DateHandler
import gen.lib
from Utils import confidence, format_time
import config
from gui.views.treemodels.flatbasemodel import FlatBaseModel
from gui.views.treemodels.treebasemodel import TreeBaseModel
#-------------------------------------------------------------------------
#
# 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
INVALID_DATE_FORMAT = config.get('preferences.invalid-date-format')
#-------------------------------------------------------------------------
#
# CitationModel
#
#-------------------------------------------------------------------------
class CitationBaseModel(object):
def __init__(self,db):
self.map = db.get_raw_citation_data
self.gen_cursor = db.get_citation_cursor
self.fmap = [
self.column_page,
self.column_id,
self.column_date,
self.column_confidence,
self.column_change,
self.column_src_title,
self.column_src_id,
self.column_src_auth,
self.column_src_abbr,
self.column_src_pinfo,
self.column_src_chan,
self.column_handle,
self.column_tooltip
]
self.smap = [
self.column_page,
self.column_id,
self.column_date,
self.column_confidence,
self.sort_change,
self.column_src_title,
self.column_src_id,
self.column_src_auth,
self.column_src_abbr,
self.column_src_pinfo,
self.column_src_chan,
self.column_handle,
self.column_tooltip
]
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
def column_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 column_id(self,data):
return unicode(data[COLUMN_ID])
def column_page(self,data):
return unicode(data[COLUMN_PAGE])
def column_confidence(self,data):
return unicode(confidence[data[COLUMN_CONFIDENCE]])
def column_handle(self,data):
return unicode(data[COLUMN_HANDLE])
def column_change(self,data):
return format_time(data[COLUMN_CHANGE])
def sort_change(self,data):
return "%012x" % data[8]
def column_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 column_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 column_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 column_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 column_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 column_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 column_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''
#-------------------------------------------------------------------------
#
# 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):
CitationBaseModel.__init__(self, db)
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
"""
CitationBaseModel.destroy(self)
FlatBaseModel.destroy(self)
#-------------------------------------------------------------------------
#
# CitationTreeModel
#
#-------------------------------------------------------------------------
class CitationTreeModel(CitationBaseModel, TreeBaseModel):
"""
Hierarchical citation model.
"""
def __init__(self, db, scol=0, order=gtk.SORT_ASCENDING, search=None,
skip=set(), sort_map=None):
CitationBaseModel.__init__(self, db)
TreeBaseModel.__init__(self, db, scol=scol, order=order,
tooltip_column=12,
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.smap = None
# Can't call FlatBaseModel.destroy(self), because it fails when a treemodel
# is passed, so can't just do:
# CitationBaseModel.destroy(self)
self.hmap = None
self.number_items = None
TreeBaseModel.destroy(self)
def _set_base_data(self):
"""See TreeBaseModel, for place, most have been set in init of
CitationBaseModel
"""
self.number_items = self.db.get_number_of_citations
# FIXME: What should the number in the next line be?
# FIXME: Only the first element of hmap ever seems to be populated.
# Why is this, and is it correct?
self.hmap = [self.column_header] + [None]*14
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.
"""
source_handle = data[COLUMN_SOURCE]
if source_handle:
source = self.db.get_source_from_handle(source_handle)
if source:
source_name = source.get_title()
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
self.add_node(source_name, handle, sort_key, handle)
else:
log.warn("Citation %s still has a pointer (handle %s) to a deleted source" %
(data[COLUMN_ID], source_handle))
else:
log.warn("Citation %s does not have a source" % unicode(data[COLUMN_PAGE]),
exc_info=True)
def column_header(self, node):
"""
Return a column heading. This is called for nodes with no associated
Gramps handle.
"""
return node.name

View File

@ -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.

View File

@ -2,6 +2,7 @@
#
# Copyright (C) 2010 Doug Blank <doug.blank@gmail.com>
# 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

View File

@ -206,3 +206,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)

View File

@ -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.

View File

@ -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"),
@ -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"),

View File

@ -0,0 +1,332 @@
# 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: sourceview.py 17447 2011-05-07 18:42:31Z nick-h $
"""
Citation View
"""
#-------------------------------------------------------------------------
#
# python modules
#
#-------------------------------------------------------------------------
import logging
LOG = logging.getLogger(".citation")
#-------------------------------------------------------------------------
#
# GTK/Gnome modules
#
#-------------------------------------------------------------------------
import gtk
#-------------------------------------------------------------------------
#
# gramps modules
#
#-------------------------------------------------------------------------
import gen.lib
import config
from gui.views.listview import ListView
from gui.views.treemodels import CitationListModel
import Utils
import Bookmarks
import Errors
from DdTargets import DdTargets
from gui.selectors import SelectorFactory
from QuestionDialog import ErrorDialog
from gui.editors import EditCitation, DeleteCitationQuery
from Filters.SideBar import SourceSidebarFilter
from gen.plug import CATEGORY_QR_SOURCE
#-------------------------------------------------------------------------
#
# internationalization
#
#-------------------------------------------------------------------------
from gen.ggettext import gettext as _
#-------------------------------------------------------------------------
#
# CitationView
#
#-------------------------------------------------------------------------
class BaseCitationView(ListView):
""" citation listview class
"""
# The data items here have to correspond, in order, to the items in
# src/giu.views/treemodels/citationmodel.py
COL_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'),
]
# default setting with visible columns, order of the col, and their size
CONFIGSETTINGS = (
('columns.visible', [COL_SRC_TITLE,
COL_PAGE, COL_ID, COL_DATE, COL_CONFIDENCE]),
('columns.rank', [COL_SRC_TITLE, COL_SRC_ID, COL_SRC_AUTH,
COL_SRC_ABBR, COL_SRC_PINFO, COL_SRC_CHAN,
COL_PAGE, COL_ID, COL_DATE, COL_CONFIDENCE,
COL_CHAN]),
('columns.size', [200, 75, 150,
100, 150, 100,
75, 75, 100, 100,
100])
)
ADD_MSG = _("Add a new citation")
EDIT_MSG = _("Edit the selected citation")
SHARE_MSG = _("Share the selected source")
DEL_MSG = _("Delete the selected citation")
MERGE_MSG = _("Merge the selected citations")
FILTER_TYPE = "Citation"
QR_CATEGORY = CATEGORY_QR_SOURCE
def __init__(self, pdata, dbstate, uistate, title, model, 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, title, pdata, dbstate, uistate,
BaseCitationView.COLUMN_NAMES, len(BaseCitationView.COLUMN_NAMES),
model, signal_map,
dbstate.db.get_citation_bookmarks(),
Bookmarks.CitationBookmarks, nav_group,
multiple=True,
filter_class=SourceSidebarFilter)
self.func_list.update({
'<CONTROL>J' : self.jump,
'<CONTROL>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.SOURCE_LINK
def define_actions(self):
ListView.define_actions(self)
# self._add_action('Share', gtk.STOCK_EDIT, _("Share..."),
# accel=None,
# tip=self.SHARE_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)
def get_stock(self):
return 'gramps-citation'
def additional_ui(self):
return '''<ui>
<menubar name="MenuBar">
<menu action="FileMenu">
<placeholder name="LocalExport">
<menuitem action="ExportTab"/>
</placeholder>
</menu>
<menu action="BookMenu">
<placeholder name="AddEditBook">
<menuitem action="AddBook"/>
<menuitem action="EditBook"/>
</placeholder>
</menu>
<menu action="GoMenu">
<placeholder name="CommonGo">
<menuitem action="Back"/>
<menuitem action="Forward"/>
<separator/>
</placeholder>
</menu>
<menu action="EditMenu">
<placeholder name="CommonEdit">
<menuitem action="Add"/>
<menuitem action="Edit"/>
<menuitem action="Remove"/>
<menuitem action="Merge"/>
</placeholder>
<menuitem action="FilterEdit"/>
</menu>
</menubar>
<toolbar name="ToolBar">
<placeholder name="CommonNavigation">
<toolitem action="Back"/>
<toolitem action="Forward"/>
</placeholder>
<placeholder name="CommonEdit">
<toolitem action="Add"/>
<toolitem action="Edit"/>
<toolitem action="Remove"/>
<toolitem action="Merge"/>
</placeholder>
</toolbar>
<popup name="Popup">
<menuitem action="Back"/>
<menuitem action="Forward"/>
<separator/>
<menuitem action="Add"/>
<menuitem action="Edit"/>
<menuitem action="Remove"/>
<menuitem action="Merge"/>
<separator/>
<menu name="QuickReport" action="QuickReport">
<menuitem action="Dummy"/>
</menu>
</popup>
</ui>'''
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):
SelectSource = SelectorFactory('Source')
sel = SelectSource(self.dbstate,self.uistate)
source = sel.run()
if source:
try:
EditCitation(self.dbstate, self.uistate, [], gen.lib.Citation(),
source)
except Errors.WindowActiveError:
from QuestionDialog import WarningDialog
WarningDialog(_("Cannot share this reference"),
self.__blocked_text())
def 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):
for handle in self.selected_handles():
citation = self.dbstate.db.get_citation_from_handle(handle)
try:
source = self.dbstate.db.get_source_from_handle(citation.ref)
EditCitation(self.dbstate, self.uistate, [], citation, source)
except Errors.WindowActiveError:
pass
except:
LOG.warn("failed to find a Source for the selected Citation")
def __blocked_text(self):
"""
Return the common text used when mediaref cannot be edited
"""
return _("This media reference cannot be edited at this time. "
"Either the associated media object is already being "
"edited or another media reference that is associated with "
"the same media object is being edited.\n\nTo edit this "
"media reference, you need to close the media object.")
# def share(self, obj):
# SelectSource = SelectorFactory('Source')
# sel = SelectSource(self.dbstate,self.uistate)
# source = sel.run()
# if source:
# try:
# EditCitation(self.dbstate, self.uistate, [], gen.lib.Citation(),
# source)
# except Errors.WindowActiveError:
# from QuestionDialog import WarningDialog
# WarningDialog(_("Cannot share this reference"),
# self.__blocked_text())
#
def 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:
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.
"""
return (("Source Filter",),
("Citation Gallery",
"Citation Notes",
"Citation Backlinks"))

View File

@ -6,6 +6,9 @@
pkgdatadir = $(datadir)/@PACKAGE@/plugins/view
pkgdata_PYTHON = \
citationlistview.py \
citationtreeview.grp.py \
citationtreeview.py \
eventview.py \
familyview.py \
fanchartview.gpr.py \

View File

@ -0,0 +1,55 @@
# 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: sourceview.py 17447 2011-05-07 18:42:31Z nick-h $
"""
Citation List View
"""
#-------------------------------------------------------------------------
#
# gramps modules
#
#-------------------------------------------------------------------------
from libcitationview import BaseCitationView
from gui.views.treemodels.citationmodel import CitationListModel
#-------------------------------------------------------------------------
#
# internationalization
#
#-------------------------------------------------------------------------
from gen.ggettext import gettext as _
#-------------------------------------------------------------------------
#
# CitationView
#
#-------------------------------------------------------------------------
class CitationListView(BaseCitationView):
"""
A list view of citations.
"""
def __init__(self, pdata, dbstate, uistate, nav_group=0):
BaseCitationView.__init__(self, pdata, dbstate, uistate,
_('Citation View'), CitationListModel,
nav_group=nav_group)

View File

@ -0,0 +1,14 @@
register(VIEW,
id = 'citationtreeview',
name = _("Citation Tree View"),
description = _("A view displaying citations in a tree format."),
version = '1.0',
gramps_target_version = '3.4',
status = STABLE,
fname = 'citationtreeview.py',
authors = [u"Tim G L Lyons", u"Nick Hall"],
authors_email = [""],
category = ("Citations", _("Citations")),
viewclass = 'CitationTreeView',
stock_icon = 'gramps-tree-group',
)

View File

@ -0,0 +1,155 @@
# 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: placetreeview.py 14176 2010-02-01 07:01:45Z bmcage $
"""
Citation Tree View
"""
#-------------------------------------------------------------------------
#
# python modules
#
#-------------------------------------------------------------------------
import logging
LOG = logging.getLogger(".citation")
#-------------------------------------------------------------------------
#
# Gramps modules
#
#-------------------------------------------------------------------------
from gui.views.listview import LISTTREE
from libcitationview import BaseCitationView
from gui.views.treemodels.citationmodel import CitationTreeModel
import gen.lib
import Errors
from gui.editors import EditCitation
from Utils import preset_name
#-------------------------------------------------------------------------
#
# Internationalization
#
#-------------------------------------------------------------------------
from gen.ggettext import gettext as _
#-------------------------------------------------------------------------
#
# PlaceTreeView
#
#-------------------------------------------------------------------------
class CitationTreeView(BaseCitationView):
"""
A hierarchical view of the top three levels of places.
"""
def __init__(self, pdata, dbstate, uistate, nav_group=0):
BaseCitationView.__init__(self, pdata, dbstate, uistate,
_('Citation Tree View'), CitationTreeModel,
nav_group=nav_group)
def type_list(self):
"""
set the listtype, this governs eg keybinding
"""
return LISTTREE
def get_viewtype_stock(self):
"""
Override the default icon. Set for hierarchical view.
"""
return 'gramps-tree-group'
def define_actions(self):
"""
Define actions for the popup menu specific to the tree view.
"""
BaseCitationView.define_actions(self)
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 '''<ui>
<menubar name="MenuBar">
<menu action="FileMenu">
<placeholder name="LocalExport">
<menuitem action="ExportTab"/>
</placeholder>
</menu>
<menu action="BookMenu">
<placeholder name="AddEditBook">
<menuitem action="AddBook"/>
<menuitem action="EditBook"/>
</placeholder>
</menu>
<menu action="GoMenu">
<placeholder name="CommonGo">
<menuitem action="Back"/>
<menuitem action="Forward"/>
</placeholder>
</menu>
<menu action="EditMenu">
<placeholder name="CommonEdit">
<menuitem action="Add"/>
<menuitem action="Edit"/>
<menuitem action="Remove"/>
<menuitem action="Merge"/>
</placeholder>
<menuitem action="FilterEdit"/>
</menu>
</menubar>
<toolbar name="ToolBar">
<placeholder name="CommonNavigation">
<toolitem action="Back"/>
<toolitem action="Forward"/>
</placeholder>
<placeholder name="CommonEdit">
<toolitem action="Add"/>
<toolitem action="Edit"/>
<toolitem action="Remove"/>
<toolitem action="Merge"/>
</placeholder>
</toolbar>
<popup name="Popup">
<menuitem action="Back"/>
<menuitem action="Forward"/>
<separator/>
<menuitem action="OpenAllNodes"/>
<menuitem action="CloseAllNodes"/>
<separator/>
<menuitem action="Add"/>
<menuitem action="Edit"/>
<menuitem action="Remove"/>
<menuitem action="Merge"/>
<separator/>
<menu name="QuickReport" action="QuickReport">
<menuitem action="Dummy"/>
</menu>
</popup>
</ui>'''

View File

@ -30,6 +30,8 @@ Source View
#
#-------------------------------------------------------------------------
import gtk
import logging
LOG = logging.getLogger(".citation")
#-------------------------------------------------------------------------
#
@ -211,6 +213,37 @@ class SourceView(ListView):
def remove_object_from_handle(self, handle):
the_lists = Utils.get_source_referents(handle, self.dbstate.db)
LOG.debug('source referents %s' % [the_lists])
# per = []
# fam = []
# eve = []
# pla = []
# sou = []
# med = []
# rep = []
citation_referents_list = []
for citation in the_lists[7]:
LOG.debug('citation %s' % citation)
refs = Utils.get_citation_referents(citation, self.dbstate.db)
citation_referents_list += [(citation, refs)]
# (per2 , fam2, eve2, pla2, sou2, med2, rep2) = refs
# per += per2
# fam += fam2
# eve += eve2
# pla += pla2
# sou += sou2
# med += med2
# rep += rep2
# citation_referents_list = (per , fam, eve, pla, sou, med, rep)
LOG.debug('citation_referents_list %s' % [citation_referents_list])
(person_list, family_list, event_list, place_list, source_list,
media_list, repo_list, citation_list) = the_lists
the_lists = (person_list, family_list, event_list, place_list, source_list,
media_list, repo_list, citation_list, citation_referents_list)
LOG.debug('the_lists %s' % [the_lists])
object = self.dbstate.db.get_source_from_handle(handle)
query = DeleteSrcQuery(self.dbstate, self.uistate, object, the_lists)
is_used = any(the_lists)

View File

@ -211,3 +211,18 @@ 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,
)