GEP 18 - General improvement: Add a cited in tab to sources for general overview of uses

svn: r22472
This commit is contained in:
Benny Malengier 2013-06-03 14:34:44 +00:00
parent 9310d5c32d
commit 72748498b2
8 changed files with 483 additions and 40 deletions

View File

@ -244,6 +244,9 @@ class SrcTemplate(object):
"""
Compute the reference based on data present.
At the moment no style is applied!
THIS IS UGLY CODE AT THE MOMENT! SHOULD BE ENTIRELY REWRITTEN, FOR
NOW IT JUST GIVES ME SOMETHING TO USE IN THE PROTOTYPE !!
"""
reflist = self.tempstruct[reftype]
# reflist is typically a list like
@ -292,7 +295,7 @@ class SrcTemplate(object):
fieldadded[-1] = True
ref[-1] += ldeltodo
if len(ref[-1]) and ref[-1][-1] == '.':
ref[-1] += ' ' + field.capitalize()
ref[-1] += ' ' + field[0].capitalize() + field[1:]
elif len(ref[-1]) and ref[-1][-1] in [',', ':', '-']:
ref[-1] += ' ' + field
else:
@ -372,7 +375,10 @@ class SrcTemplate(object):
fieldadded[-1] = False
ref = ''.join(ref)
return ref.capitalize()
if ref:
return ref[0].capitalize() + ref[1:]
else:
return ref
def author_gedcom(self, attr_list=None):
if attr_list:

View File

@ -39,6 +39,7 @@ from .embeddedlist import EmbeddedList, TEXT_COL, MARKUP_COL, ICON_COL
from .addrembedlist import AddrEmbedList
from .attrembedlist import AttrEmbedList
from .backreflist import BackRefList
from .citedintab import CitedInTab
from .eventbackreflist import EventBackRefList
from .eventembedlist import EventEmbedList
from .familyattrembedlist import FamilyAttrEmbedList

View File

@ -0,0 +1,410 @@
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2013 Benny Malengier
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# $Id$
from __future__ import print_function
#-------------------------------------------------------------------------
#
# Python modules
#
#-------------------------------------------------------------------------
from gramps.gen.const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
#-------------------------------------------------------------------------
#
# GTK libraries
#
#-------------------------------------------------------------------------
from gi.repository import Gdk
from gi.repository import Gtk
#-------------------------------------------------------------------------
#
# Gramps libraries
#
#-------------------------------------------------------------------------
from gramps.gen.display.name import displayer as _nd
from gramps.gen.utils.db import (get_citation_referents, family_name,
get_participant_from_event)
from .grampstab import GrampsTab
from ...widgets import SimpleButton
#-------------------------------------------------------------------------
#
# Classes
#
#-------------------------------------------------------------------------
class CitedInTab(GrampsTab):
"""
This class provides the tabpage for overview of where a source is
cited.
It shows these objects in a treeviewl and allows to load citations in the
top part of the source editor.
"""
def __init__(self, dbstate, uistate, track, src):
"""
@param dbstate: The database state. Contains a reference to
the database, along with other state information. The GrampsTab
uses this to access the database and to pass to and created
child windows (such as edit dialogs).
@type dbstate: DbState
@param uistate: The UI state. Used primarily to pass to any created
subwindows.
@type uistate: DisplayState
@param track: The window tracking mechanism used to manage windows.
This is only used to pass to generted child windows.
@type track: list
@param src: source which we manage in this tab
@type src: gen.lib.Source
@param glade: glade objects with the needed widgets
"""
self.src = src
self.readonly = dbstate.db.readonly
self.srtdata = []
GrampsTab.__init__(self, dbstate, uistate, track, _("Cited In"))
self._set_label()
def build_interface(self):
"""
method called in init of GrampsTab
"""
self.generate_data()
self.build_model()
self.setup_interface()
self.show_all()
def get_icon_name(self):
return 'gramps-citation'
def is_empty(self):
"""
Return True if there is no data to show
"""
return len(self.srtdata) == 0
def generate_data(self):
"""
Obtain all objects this source is cited in
"""
self.srtdata = []
self.obj2citemap = {}
if not self.src.handle:
#new object
return
db = self.dbstate.db
#we don't nest calls to find_backlink_handles, so obtain first for
#the source, then in loop for citations
listtopobj = [x for x in db.find_backlink_handles(self.src.handle)]
for (cobjclass, chandle) in listtopobj:
#this will only be citations!
##print ('t1', cobjclass, chandle)
if cobjclass == 'Citation':
cite = db.get_citation_from_handle(chandle)
for (objclass, handle) in db.find_backlink_handles(chandle):
##print ('t2', objclass, handle)
if objclass == 'Person':
ref = db.get_person_from_handle(handle)
self.__add_person(ref, cite)
elif objclass == 'Family':
ref = db.get_family_from_handle(handle)
self.__add_family(ref, cite)
elif objclass == 'Event':
ref = db.get_event_from_handle(handle)
self.__add_event(ref, cite)
elif objclass == 'Place':
ref = db.get_place_from_handle(handle)
self.__add_place(ref, cite)
elif objclass == 'Repository':
ref = db.get_repository_from_handle(handle)
self.__add_repo(ref, cite)
elif objclass in ['MediaObject', 'Media']:
ref = db.get_object_from_handle(handle)
self.__add_media(ref, cite)
else:
#most strange, not possible for citation there!
print ("Error in citedintab.py: citation referenced "
"outside citation")
else:
#most strange, not possible !
print ("Error in citedintab.py: source referenced "
"outside citation")
self.srtdata = sorted(self.srtdata, key=lambda x: glocale.sort_key(x[0]))
def __add_object(self, obj, cite, descr_obj, shortdescr):
"""
obtain citation data of the object and store here so it can be shown
in a treeview
"""
if not obj.handle in self.obj2citemap:
self.obj2citemap[obj.handle] = {'prim': [], 'sec': [], 'subsec': []}
#add for sorting in the treeview to map
self.srtdata.append((descr_obj, obj.handle, shortdescr))
#we analyse the object to determine where the citation is used.
if hasattr(obj, 'get_citation_list'):
for citehandle in obj.get_citation_list():
##print ('t4', citehandle)
if cite.handle == citehandle:
self.obj2citemap[obj.handle]['prim'].append(cite.handle)
#now search the citation in secondary objects. This can maximally be
# 2 levels deep, eg citation in attribute of eventref
for objsec in obj.get_citation_child_list():
##print ('t5', objsec)
if hasattr(objsec, 'get_citation_list'):
for citehandle in objsec.get_citation_list():
##print ('t6', citehandle)
if cite.handle == citehandle:
self.obj2citemap[obj.handle]['sec'].append(
(cite.handle, self.format_sec_obj(objsec)))
if hasattr(objsec, 'get_citation_child_list'):
for objsubsec in objsec.get_citation_child_list():
##print ('t7', objsubsec)
#eg attribute of eventref of person
for citehandle in objsubsec.get_citation_list():
if cite.handle == citehandle:
self.obj2citemap[obj.handle]['subsec'].append(
(cite.handle,
_('%(first)s -> %(sec)s') % {
'first': self.format_sec_obj(objsec),
'sec' : self.format_sec_obj(objsubsec)}))
def __add_person(self, obj, cite):
"""
see __add_object
"""
name = _nd.display_name(obj.get_primary_name())
self.__add_object(obj, cite, _("Person %(id)s: %(descr)s") % {
'id': obj.get_gramps_id(),
'descr': name}, _("Cited in Person"))
def __add_family(self, obj, cite):
"""
see __add_object
"""
name = family_name(obj, self.dbstate.db, _("Unknown Family"))
self.__add_object(obj, cite, _("Family %(id)s: %(descr)s") % {
'id': obj.get_gramps_id(),
'descr': name}, _("Cited in Family"))
def __add_event(self, obj, cite):
"""
see __add_object
"""
who = get_participant_from_event(self.dbstate.db, obj.handle)
desc = obj.get_description()
event_name = obj.get_type()
if desc:
event_name = '%s - %s' % (event_name, desc)
if who:
event_name = '%s - %s' % (event_name, who)
name = _('Event %(id)s: %(descr)s') % {
'id': obj.get_gramps_id(),
'descr': event_name}
self.__add_object(obj, cite, name, _("Cited in Event"))
def __add_place(self, obj, cite):
"""
see __add_object
"""
self.__add_object(obj, cite, _('Place %(id)s: %(descr)s') % {
'id': obj.get_gramps_id(),
'descr': obj.get_title()}, _("Cited in Place"))
def __add_repo(self, obj, cite):
"""
see __add_object
"""
self.__add_object(obj, cite, _('Repository %(id)s: %(descr)s') % {
'id': obj.get_gramps_id(),
'descr': obj.get_name()}, _("Cited in Repository"))
def __add_media(self, obj, cite):
"""
see __add_object
"""
name = obj.get_description().strip()
if not name:
name = obj.get_path()
if not name:
name = obj.get_mime_type()
self.__add_object(obj, cite, _('Media %(id)s: %(descr)s') % {
'id': obj.get_gramps_id(),
'descr': name}, _("Cited in Media"))
def format_sec_obj(self, objsec):
"""
text for treeview on citation in secondary object
"""
classname = objsec.__class__.__name__
classobj = classname
descr = '' #TODO TO SET THIS !!
if classname == "Address":
descr = objsec.get_street()
classobj = _("Address")
elif classname == "Attribute":
descr = str(objsec.get_type())
classobj = _("Attribute")
elif classname == "ChildRef":
ref = objsec.get_reference_handle()
person = self.dbstate.db.get_person_from_handle(ref)
descr = _nd.display_name(person.get_primary_name())
classobj = _("Child")
elif classname == "EventRef":
ref = objsec.get_reference_handle()
event = self.dbstate.db.get_event_from_handle(ref)
descr = str(event.get_type())
classobj = _("Event Reference")
elif classname == "LdsOrd":
descr = objsec.type2str()
classobj = _("LDS Ordinance")
elif classname == "MediaRef":
ref = objsec.get_reference_handle()
obj = self.dbstate.db.get_object_from_handle(ref)
descr = obj.get_description().strip()
if not descr:
descr = obj.get_path()
if not descr:
descr = obj.get_mime_type()
classobj = _("Media Reference")
elif classname == "Name":
descr = _nd.display_name(objsec)
classobj = _("Name")
elif classname == "PersonRef":
ref = objsec.get_reference_handle()
person = self.dbstate.db.get_person_from_handle(ref)
if person is None:
descr = ref
else:
descr = _nd.display_name(person.get_primary_name())
descr = _("%(secobj)s: %(descr)s") % {
'secobj': classobj,
'descr' : descr}
return descr
def setup_interface(self):
"""
Set all information on the widgets
* button tabs to load citation
* treeview in scrollable with info
"""
##print (self.srtdata)
##print(self.obj2citemap)
#create the load button, add it to a hbox, and add that box to the
#tab page
self.load_btn = SimpleButton(Gtk.STOCK_APPLY, self.load_button_clicked)
hbox = Gtk.HBox()
hbox.set_spacing(6)
hbox.pack_start(self.load_btn, False, True, 0)
hbox.show_all()
self.pack_start(hbox, False, True, 0)
if self.dbstate.db.readonly:
self.load_btn.set_sensitive(False)
# create the tree, turn on rule hinting and connect the
# button press to the double click function.
self.tree = Gtk.TreeView()
self.tree.set_rules_hint(True)
self.tree.connect('button_press_event', self.double_click)
self.tree.connect('key_press_event', self.key_pressed)
self.make_columns()
self.tree.set_model(self.model)
self.tree.expand_all()
# create the scrolled window, and attach the treeview
scroll = Gtk.ScrolledWindow()
scroll.set_shadow_type(Gtk.ShadowType.IN)
scroll.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
scroll.add(self.tree)
#add this to the tab
self.pack_start(scroll, True, True, 0)
def double_click(self, obj, event):
"""
Handles the double click on list. If the double click occurs,
the load button handler is called
"""
if event.type == Gdk.EventType._2BUTTON_PRESS and event.button == 1:
self.load_button_clicked(obj)
def key_pressed(self, obj, event):
"""
Handles the return key being pressed on list. If the key is pressed,
the Load button handler is called
"""
if event.type == Gdk.EventType.KEY_PRESS:
#print 'key pressed', event.keyval, event.get_state(), _ADD
if event.keyval in (_RETURN, _KP_ENTER):
self.load_button_clicked(obj)
return True
else:
return GrampsTab.key_pressed(self, obj, event)
return False
def load_button_clicked(self, obj):
"""
Function called with the Load button is clicked. This function
should be overridden by the derived class.
"""
print("Uncaught Add clicked")
def build_model(self):
"""
set up the model the treeview will use based on the data
"""
# store (citationhandle, primobjhandle, name, citationgid, index)
# here, depending on the leve, name will be primobjname, secobjname, or
# subsecobjname
# citationhandle will be '' for rows which create sublevels
self.model = Gtk.TreeStore(str, str, str, str, int)
for (descr, primhandle, shortdescr) in self.srtdata:
data = self.obj2citemap[primhandle]
#top level node
iter = self.model.append(None, ['', primhandle, descr, '', -1])
for ind, chandle in enumerate(data['prim']):
citation = self.dbstate.db.get_citation_from_handle(chandle)
self.model.append(iter, [chandle, primhandle, shortdescr,
citation.get_gramps_id(), ind])
base = len(data['prim'])
for ind, val in enumerate(data['sec']):
chandle, secdescr = val
citation = self.dbstate.db.get_citation_from_handle(chandle)
self.model.append(iter, [chandle, primhandle, secdescr,
citation.get_gramps_id(), base+ind])
base += len(data['sec'])
for ind, val in enumerate(data['subsec']):
chandle, subsecdescr = val
citation = self.dbstate.db.get_citation_from_handle(chandle)
self.model.append(iter, [chandle, primhandle, subsecdescr,
citation.get_gramps_id(), base+ind])
def make_columns(self):
#make the columns in the treeview
renderer = Gtk.CellRendererText()
column = Gtk.TreeViewColumn(_("Cited in"), renderer, text=2)
self.tree.append_column(column)
column = Gtk.TreeViewColumn(_("Citation"), renderer, text=3)
self.tree.append_column(column)

View File

@ -118,8 +118,8 @@ class GrampsTab(Gtk.VBox):
else:
func = Gtk.Image.new_from_stock
name = icon
self.tab_image = func(name, Gtk.IconSize.MENU)
self.tab_image = func(name, Gtk.IconSize.MENU)
self.track_ref_for_deletion("tab_image")
self.label = Gtk.Label(label=self.tab_name)
self.track_ref_for_deletion("label")

View File

@ -129,6 +129,33 @@ class SrcTemplateTab(GrampsTab):
#a predefined template,
self.reset_template_fields(srcattr.EVIDENCETEMPLATES[index])
def _add_entry(self, row, srcattrtype, label):
"""
Add an entryfield to the grid of fields at row row, to edit the given
srcattrtype value. Use label label if given to indicate the field
(otherwise the srcattrtype string description is used)
Note srcattrtype should actually be the integer key of the type!
"""
self.gridfields.insert_row(row)
field = srcattrtype
#setup label
if not label:
srcattr = SrcAttributeType(field)
label = str(srcattr)
lbl = Gtk.Label(_("%s:") % label)
lbl.set_halign(Gtk.Align.START)
self.gridfields.attach(lbl, 0, row-1, 1, 1)
self.lbls.append(lbl)
#setup entry
inpt = UndoableEntry()
inpt.set_halign(Gtk.Align.FILL)
inpt.set_hexpand(True)
self.gridfields.attach(inpt, 1, row-1, 1, 1)
self.inpts.append(inpt)
MonitoredEntry(inpt, self.set_field, self.get_field,
read_only=self.dbstate.db.readonly,
parameter=srcattrtype)
def reset_template_fields(self, template):
# first remove old fields
for lbl in self.lbls:
@ -139,25 +166,9 @@ class SrcTemplateTab(GrampsTab):
self.inpts = []
row = 1
# now add new fields
for fielddef in template[REF_TYPE_F]:
self.gridfields.insert_row(row)
for fielddef in template[REF_TYPE_L]:
self._add_entry(row, fielddef[1], '')
row += 1
field = fielddef[1]
#setup label
srcattr = SrcAttributeType(field)
lbl = Gtk.Label(_("%s:") %str(srcattr))
lbl.set_halign(Gtk.Align.START)
self.gridfields.attach(lbl, 0, row-1, 1, 1)
self.lbls.append(lbl)
#setup entry
inpt = UndoableEntry()
inpt.set_halign(Gtk.Align.FILL)
inpt.set_hexpand(True)
self.gridfields.attach(inpt, 1, row-1, 1, 1)
self.inpts.append(inpt)
MonitoredEntry(inpt, self.set_field, self.get_field,
read_only=self.dbstate.db.readonly,
parameter=field)
self.show_all()
@ -196,7 +207,6 @@ class SrcTemplateTab(GrampsTab):
src.add_attribute(foundattr)
#indicate source object changed
self.callback_src_changed()
## def setup_autocomp_combobox(self):
## """

View File

@ -298,7 +298,7 @@ class EditCitation(EditPrimary):
self.db.readonly)
self.author = MonitoredEntry(
self.glade.get_object('author'), self.source.set_author,
self.glade.get_object('author'), self.eat_it,
self.source.get_author,self.db.readonly)
self.gid = MonitoredEntry(
@ -323,9 +323,15 @@ class EditCitation(EditPrimary):
self.source.get_abbreviation,self.db.readonly)
self.pubinfo = MonitoredEntry(
self.glade.get_object('pub_info'), self.source.set_publication_info,
self.glade.get_object('pub_info'), self.eat_it,
self.source.get_publication_info,self.db.readonly)
def eat_it(self, *pars):
"""
TODO: remove this method again, for prototype only
"""
pass
def _create_tabbed_pages(self):
"""
Create the notebook tabs and inserts them into the main

View File

@ -54,7 +54,7 @@ from .editreference import RefTab
from .editmediaref import EditMediaRef
from .displaytabs import (NoteTab, GalleryTab, SrcAttrEmbedList,
SrcTemplateTab,
SrcTemplateTab, CitedInTab,
CitationBackRefList, RepoEmbedList)
from ..widgets import MonitoredEntry, PrivacyButton, MonitoredTagList
from ..dialog import ErrorDialog
@ -286,7 +286,7 @@ class EditSource(EditPrimary):
callback_notebase_changed=self.update_notes)
self._add_tab(notebook, self.note_tab)
self.track_ref_for_deletion("note_tab")
self.gallery_tab = GalleryTab(self.dbstate,
self.uistate,
self.track,
@ -294,21 +294,26 @@ class EditSource(EditPrimary):
self.load_source_image)
self._add_tab(notebook, self.gallery_tab)
self.track_ref_for_deletion("gallery_tab")
self.attr_tab = SrcAttrEmbedList(self.dbstate,
self.uistate,
self.track,
self.obj.get_attribute_list())
self._add_tab(notebook, self.attr_tab)
self.track_ref_for_deletion("attr_tab")
self.repo_tab = RepoEmbedList(self.dbstate,
self.uistate,
self.track,
self.obj.get_reporef_list())
self._add_tab(notebook, self.repo_tab)
self.track_ref_for_deletion("repo_tab")
self.attr_tab = SrcAttrEmbedList(self.dbstate,
self.uistate,
self.track,
self.obj.get_attribute_list())
self._add_tab(notebook, self.attr_tab)
self.track_ref_for_deletion("attr_tab")
self.citedin_tab = CitedInTab(self.dbstate, self.uistate,
self.track, self.obj)
self._add_tab(notebook, self.citedin_tab)
self.track_ref_for_deletion("citedin_tab")
self.backref_list = CitationBackRefList(self.dbstate,
self.uistate,
self.track,

View File

@ -23,13 +23,19 @@
"""
A class to select source templates
"""
#-------------------------------------------------------------------------
#
# Python modules
#
#-------------------------------------------------------------------------
from gramps.gen.const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
#-------------------------------------------------------------------------
#
# GTK classes
#
#-------------------------------------------------------------------------
from gi.repository import Gdk
from gi.repository import Gtk
@ -38,7 +44,6 @@ from gi.repository import Gtk
# Gramps classes
#
#-------------------------------------------------------------------------
from gramps.gen.lib import SrcAttributeType
#-------------------------------------------------------------------------
@ -79,7 +84,7 @@ class SrcTemplateTreeView(Gtk.TreeView):
self.Str2I = srcattrt.S2I_SRCTEMPLATEMAP
self.Key2I = srcattrt.K2I_SRCTEMPLATEMAP
self.Key2Path = {}
# store (index, key, cat, cat_type, src_type)
# store (index, key, src_type)
self.model = Gtk.TreeStore(int, str, str)
alltexts = sorted(self.Str2I.keys())
parentiter = None
@ -136,7 +141,7 @@ class SrcTemplateTreeView(Gtk.TreeView):
def make_columns(self):
#make the column in the treeview
renderer = Gtk.CellRendererText()
column = Gtk.TreeViewColumn("Template", renderer, text=2)
column = Gtk.TreeViewColumn(_("Template"), renderer, text=2)
self.append_column(column)
#no headers needed:
self.set_headers_visible (False)