From 107e614f3dd083e3a4227754dd843a8dfae0cea6 Mon Sep 17 00:00:00 2001 From: Nick Hall Date: Sun, 2 Mar 2014 19:12:50 +0000 Subject: [PATCH] Simplify citation editor and add source selection --- gramps/gui/editors/editcitation.py | 318 ++------- gramps/gui/editors/editsource.py | 4 +- gramps/gui/editors/objectentries.py | 57 +- gramps/gui/glade/editcitation.glade | 969 ++++++++-------------------- 4 files changed, 389 insertions(+), 959 deletions(-) diff --git a/gramps/gui/editors/editcitation.py b/gramps/gui/editors/editcitation.py index b9029ebb4..df3464715 100644 --- a/gramps/gui/editors/editcitation.py +++ b/gramps/gui/editors/editcitation.py @@ -3,7 +3,8 @@ # # Copyright (C) 2000-2006 Donald N. Allingham # Copyright (C) 2009 Gary Burton -# Copyright (C) 2011 Tim G L Lyons, Nick Hall +# Copyright (C) 2011 Tim G L Lyons +# Copyright (C) 2011,2014 Nick Hall # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -20,10 +21,8 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # -# $Id$ - """ -EditCitation class for GRAMPS. +EditCitation class for Gramps. """ #------------------------------------------------------------------------- @@ -36,6 +35,13 @@ _ = glocale.translation.gettext import logging LOG = logging.getLogger(".citation") +#------------------------------------------------------------------------- +# +# GTK/Gnome modules +# +#------------------------------------------------------------------------- +from gi.repository import Gtk + #------------------------------------------------------------------------- # # gramps modules @@ -44,13 +50,12 @@ LOG = logging.getLogger(".citation") from gramps.gen.lib import Citation, NoteType, Source from gramps.gen.db import DbTxn from .editprimary import EditPrimary - +from .objectentries import SourceEntry from .displaytabs import (NoteTab, GalleryTab, SrcAttrEmbedList, - SourceBackRefList, RepoEmbedList, CitationBackRefList) + CitationBackRefList) from ..widgets import (MonitoredEntry, PrivacyButton, MonitoredMenu, MonitoredDate, MonitoredTagList) from ..dialog import ErrorDialog -from .editreference import RefTab from ..glade import Glade #------------------------------------------------------------------------- @@ -78,49 +83,12 @@ class EditCitation(EditPrimary): The obj parameter is mandatory. If the source parameter is not provided, it will be deduced from the obj Citation object. """ - if not source and obj.get_reference_handle(): - source = dbstate.db.get_source_from_handle( - obj.get_reference_handle()) - self.source = source + if source: + obj.set_reference_handle(source.get_handle()) self.callertitle = callertitle EditPrimary.__init__(self, dbstate, uistate, track, obj, dbstate.db.get_citation_from_handle, dbstate.db.get_citation_from_gramps_id, callback) - # FIXME: EitPrimary calls ManagedWindow.__init__, which checks whether - # a window is already open which is editing obj. However, for - # EditCitation, not only do we need to protect obj (which will be - # a Citation, but we also need to protect the associated Source. - - def build_window_key(self, obj): - """ - Return a key for the edit window that is opened. - This function overrides the build_window_key in EditPrimary. - - There is a problem with database object locking. The database locking is - handled by the ManagedWindow class, which will only allow one primary - object to be edited at a time. - - Normally, the window key is derived from the obj that is being edited. - However, in the case of EditCitation, there are two objects being - edited, the Citation and the Source. Both must be protected against - against the user trying to edit them twice. - - What we do here is to derive the window key from the Source object, if - one exists. A Citation always points to exactly one Source object, so if - we try to edit the same Citation twice, the associated Source objects - will be the same so this will be prevented. If we try to edit a Source - object and a Citation object that refers to the same Source, then again, - the window key will be the same and this will be prevented. - """ - if obj and obj.get_reference_handle(): - # citation already points to source - return obj.get_reference_handle() - elif self.source and self.source.get_handle(): - # Citation doesn't yet point to source, but source exists and has a - # handle - return self.source.get_handle() - else: - return id(self) def empty_object(self): """ @@ -156,68 +124,27 @@ class EditCitation(EditPrimary): title = _('New Citation') return title - # The functions define_warn_box, enable_warn_box and define_expander - # are normally inherited from editreference, - # but have to be defined here because this class inherits from - # EditPrimary instead - def define_warn_box(self, box): - self.warn_box = box - - def enable_warnbox(self): - self.warn_box.show() - - def define_warn_box2(self, box): - self.warn_box2 = box - - def enable_warnbox2(self): - self.warn_box2.show() - - def define_expander(self, expander): - expander.set_expanded(True) - def _local_init(self): - """Local initialization function. + """ + Local initialization function. Perform basic initialization, including setting up widgets and the glade interface. It is called by the base class L{EditPrimary}, and overridden here. - """ self.width_key = 'interface.citation-width' self.height_key = 'interface.citation-height' - assert(self.obj) self.glade = Glade() self.set_window(self.glade.toplevel, None, self.get_menu_title()) - self.define_warn_box(self.glade.get_object("warn_box")) - self.define_warn_box2(self.glade.get_object("warn_box2")) - self.define_expander(self.glade.get_object("src_expander")) - - tblref = self.glade.get_object('table67') - notebook = self.glade.get_object('notebook_ref') - #recreate start page as GrampsTab - notebook.remove_page(0) - self.reftab = RefTab(self.dbstate, self.uistate, self.track, - _('General'), tblref) - tblref = self.glade.get_object('table68') - notebook = self.glade.get_object('notebook_src') - #recreate start page as GrampsTab - notebook.remove_page(0) - self.primtab = RefTab(self.dbstate, self.uistate, self.track, - _('General'), tblref) - - def _post_init(self): - title = self.glade.get_object('title') - volume = self.glade.get_object('volume') - if not title.get_text_length(): - title.grab_focus(); - elif not volume.get_text_length(): - volume.grab_focus(); + self.share_btn = self.glade.get_object('select_source') + self.add_del_btn = self.glade.get_object('add_del_source') def _connect_signals(self): - """Connects any signals that need to be connected. + """ + Connects any signals that need to be connected. Called by the init routine of the base class L{EditPrimary}. """ @@ -234,21 +161,20 @@ class EditCitation(EditPrimary): whilst editing it. If the object is deleted we need to close the editor windows and clean up. If the database emits a rebuild signal for the database object type we also abort the edit. - - The Citation editor edits two primary objects, and therefore we need to - check if either have been deleted. If the source is deleted, the - citation must have been deleted first and will emit a signal, so we - shouldn't have to connect to the source-delete signal. It should not be - necessary to connect to the source- rebuild signal for similar reasons. """ self._add_db_signal('citation-rebuild', self._do_close) self._add_db_signal('citation-delete', self.check_for_close) def _setup_fields(self): - """Get control widgets and attach them to Citation's attributes.""" - - # Populate the Citation section + """ + Get control widgets and attach them to Citation's attributes. + """ + self.source_field = SourceEntry(self.dbstate, self.uistate, self.track, + self.glade.get_object("source"), + self.obj.set_reference_handle, + self.obj.get_reference_handle, + self.add_del_btn, self.share_btn) self.date = MonitoredDate( self.glade.get_object("date_entry"), @@ -259,7 +185,7 @@ class EditCitation(EditPrimary): self.db.readonly) self.gid = MonitoredEntry( - self.glade.get_object('gid2'), self.obj.set_gramps_id, + self.glade.get_object('gid'), self.obj.set_gramps_id, self.obj.get_gramps_id,self.db.readonly) self.volume = MonitoredEntry( @@ -278,8 +204,8 @@ class EditCitation(EditPrimary): self.db.readonly) self.tags2 = MonitoredTagList( - self.glade.get_object("tag_label2"), - self.glade.get_object("tag_button2"), + self.glade.get_object("tag_label"), + self.glade.get_object("tag_button"), self.obj.set_tag_list, self.obj.get_tag_list, self.db, @@ -288,114 +214,40 @@ class EditCitation(EditPrimary): 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.tags = MonitoredTagList( - self.glade.get_object("tag_label"), - self.glade.get_object("tag_button"), - self.source.set_tag_list, - self.source.get_tag_list, - self.db, - self.uistate, self.track, - self.db.readonly) - - self.source_privacy = PrivacyButton( - self.glade.get_object("private"), - self.source, self.db.readonly) - - self.abbrev = MonitoredEntry( - self.glade.get_object('abbrev'), self.source.set_abbreviation, - self.source.get_abbreviation,self.db.readonly) - - self.pubinfo = MonitoredEntry( - self.glade.get_object('pub_info'), self.source.set_publication_info, - self.source.get_publication_info,self.db.readonly) def _create_tabbed_pages(self): """ Create the notebook tabs and inserts them into the main window. """ - # create notebook tabs for Citation - - notebook_ref = self.glade.get_object('notebook_ref') - self._add_tab(notebook_ref, self.reftab) + notebook = Gtk.Notebook() - self.comment_tab = NoteTab(self.dbstate, self.uistate, self.track, + self.note_tab = NoteTab(self.dbstate, self.uistate, self.track, self.obj.get_note_list(), self.get_menu_title(), notetype=NoteType.CITATION) - self._add_tab(notebook_ref, self.comment_tab) - self.track_ref_for_deletion("comment_tab") + self._add_tab(notebook, self.note_tab) + self.track_ref_for_deletion("note_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._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_ref, self.attr_tab) + self._add_tab(notebook, self.attr_tab) self.track_ref_for_deletion("attr_tab") self.citationref_list = CitationBackRefList(self.dbstate, self.uistate, self.track, - self.db.find_backlink_handles(self.obj.handle), - self.enable_warnbox2) - self._add_tab(notebook_ref, self.citationref_list) + self.db.find_backlink_handles(self.obj.handle)) + self._add_tab(notebook, 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=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.sattr_tab = SrcAttrEmbedList(self.dbstate, self.uistate, self.track, - self.source.get_attribute_list()) - self._add_tab(notebook_src, self.sattr_tab) - self.track_ref_for_deletion("sattr_tab") - - self.repo_tab = RepoEmbedList(self.dbstate, self.uistate, self.track, - self.source.get_reporef_list()) - self._add_tab(notebook_src, self.repo_tab) - self.track_ref_for_deletion("repo_tab") - - self.srcref_list = SourceBackRefList(self.dbstate, self.uistate, - self.track, - self.db.find_backlink_handles(self.source.handle), - self.enable_warnbox) - self._add_tab(notebook_src, self.srcref_list) - self.track_ref_for_deletion("srcref_list") + self._setup_notebook_tabs(notebook) - self._setup_notebook_tabs(notebook_src) - self._setup_notebook_tabs(notebook_ref) + notebook.show_all() + self.glade.get_object('vbox').pack_start(notebook, True, True, 0) def build_menu_names(self, source): """ @@ -405,12 +257,13 @@ class EditCitation(EditPrimary): return (_('Edit Citation'), self.get_menu_title()) def save(self, *obj): - """Save the data.""" + """ + Save the data. + """ self.ok_button.set_sensitive(False) - if self.source_is_empty(self.source): - ErrorDialog(_("Cannot save source"), - _("No data exists for this source. Please " - "enter data or cancel the edit.")) + if not self.obj.get_reference_handle(): + ErrorDialog(_("Cannot save citation. No source selected."), + _("Please select a source or cancel the edit.")) self.ok_button.set_sensitive(True) return @@ -428,78 +281,24 @@ class EditCitation(EditPrimary): self.ok_button.set_sensitive(True) return - (uses_dupe_id, gramps_id) = self.source_uses_duplicate_id(self.source) - if uses_dupe_id: - prim_object = self.db.get_source_from_gramps_id(gramps_id) - name = prim_object.get_title() - msg1 = _("Cannot save source. ID already exists.") - msg2 = _("You have attempted to use the existing Gramps ID with " - "value %(gramps_id)s. This value is already used by '" - "%(prim_object)s'. Please enter a different ID or leave " - "blank to get the next available ID value.") % { - 'gramps_id' : gramps_id, 'prim_object' : name } - ErrorDialog(msg1, msg2) - self.ok_button.set_sensitive(True) - return - with DbTxn('', self.db) as trans: - # First commit the Source Primary object - if not self.source.get_handle(): - self.db.add_source(self.source, trans) - msg = _("Add Source (%s)") % self.source.get_title() - else: - if not self.source.get_gramps_id(): - self.source.set_gramps_id( - self.db.find_next_source_gramps_id()) - self.db.commit_source(self.source, trans) - msg = _("Edit Source (%s)") % self.source.get_title() - - self.obj.set_reference_handle(self.source.handle) - - # Now commit the Citation Primary object if not self.obj.get_handle(): self.db.add_citation(self.obj, trans) - msg += "\n" + _("Add Citation (%s)") % self.obj.get_page() + 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() + msg = _("Edit Citation (%s)") % self.obj.get_page() trans.set_description(msg) if self.callback: self.callback(self.obj.get_handle()) self.close() - def source_is_empty(self, obj): - empty_object = Source() - return obj.serialize()[1:] == empty_object.serialize()[1:] - - def source_uses_duplicate_id(self, obj): - """ - Check whether a changed or added GRAMPS ID already exists in the DB. - - Return True if a duplicate GRAMPS ID has been detected. - - """ - original = self.db.get_source_from_handle(obj.get_handle()) - if original and original.get_gramps_id() == obj.get_gramps_id(): - return (False, 0) - else: - idval = obj.get_gramps_id() - if self.db.get_source_from_gramps_id(idval): - return (True, idval) - return (False, 0) - def data_has_changed(self): - return self.citation_data_has_changed() or \ - self.source_data_has_changed() - - def citation_data_has_changed(self): """ - This checks whether the citation data has changed - A date comparison can fail incorrectly because we have made the decision to store entered text in the date. However, there is no entered date when importing from a XML file, so we can get an @@ -518,23 +317,6 @@ class EditCitation(EditPrimary): cmp_obj = self.empty_object() return cmp_obj.serialize(True)[1:] != self.obj.serialize()[1:] - def source_data_has_changed(self): - """ - This checks whether the source data has changed - """ - if self.db.readonly: - return False - elif self.source.handle: - orig = self.db.get_source_from_handle(self.source.handle) - if orig: - cmp_obj = orig - else: - cmp_obj = Source() - return cmp_obj.serialize()[1:] != self.source.serialize()[1:] - else: - cmp_obj = Source() - return cmp_obj.serialize()[1:] != self.source.serialize()[1:] - class DeleteCitationQuery(object): def __init__(self, dbstate, uistate, citation, the_lists): self.citation = citation diff --git a/gramps/gui/editors/editsource.py b/gramps/gui/editors/editsource.py index 85b0f0502..5c0addf93 100644 --- a/gramps/gui/editors/editsource.py +++ b/gramps/gui/editors/editsource.py @@ -67,7 +67,7 @@ class EditSource(EditPrimary): EditPrimary.__init__(self, dbstate, uistate, track, source, dbstate.db.get_source_from_handle, - dbstate.db.get_source_from_gramps_id) + dbstate.db.get_source_from_gramps_id, callback) def empty_object(self): return Source() @@ -219,6 +219,8 @@ class EditSource(EditPrimary): trans.set_description(msg) self.close() + if self.callback: + self.callback(self.obj) class DeleteSrcQuery(object): def __init__(self, dbstate, uistate, source, the_lists): diff --git a/gramps/gui/editors/objectentries.py b/gramps/gui/editors/objectentries.py index e601ebdd4..a6faf8b8d 100644 --- a/gramps/gui/editors/objectentries.py +++ b/gramps/gui/editors/objectentries.py @@ -50,8 +50,9 @@ from gi.repository import Pango # Gramps modules # #------------------------------------------------------------------------- -from gramps.gen.lib import (Place, MediaObject, Note) +from gramps.gen.lib import (Place, Source, MediaObject, Note) from .editplace import EditPlace +from .editsource import EditSource from .editmedia import EditMedia from .editnote import EditNote from ..selectors import SelectorFactory @@ -300,6 +301,60 @@ class PlaceEntry(ObjEntry): cls = SelectorFactory('Place') return cls(self.dbstate, self.uistate, self.track) +class SourceEntry(ObjEntry): + """ + Handles the selection of a existing or new Source. Supports Drag and Drop + to select a source. + """ + EMPTY_TEXT = "%s" % _('To select a source, use drag-and-drop ' + 'or use the buttons') + EMPTY_TEXT_RED = "%s" % _('No place given, click button to select one') + EDIT_STR = _('Edit source') + SHARE_STR = _('Select an existing source') + ADD_STR = _('Add a new source') + DEL_STR = _('Remove source') + + def __init__(self, dbstate, uistate, track, label, set_val, + get_val, add_edt, share): + ObjEntry.__init__(self, dbstate, uistate, track, label, set_val, + get_val, add_edt, share) + + def _init_dnd(self): + """connect drag and drop of sources + """ + self.label.drag_dest_set(Gtk.DestDefaults.ALL, [], Gdk.DragAction.COPY) + tglist = Gtk.TargetList.new([]) + tglist.add(DdTargets.PLACE_LINK.atom_drag_type, + DdTargets.PLACE_LINK.target_flags, + DdTargets.PLACE_LINK.app_id) + self.label.drag_dest_set_target_list(tglist) + self.label.connect('drag_data_received', self.drag_data_received) + + def get_from_handle(self, handle): + """ return the object given the handle + """ + return self.db.get_source_from_handle(handle) + + def get_label(self, source): + return "%s [%s]" % (source.get_title(), source.gramps_id) + + def call_editor(self, obj=None): + if obj is None: + source = Source() + func = self.obj_added + else: + source = obj + func = self.after_edit + try: + EditSource(self.dbstate, self.uistate, self.track, + source, func) + except WindowActiveError: + pass + + def call_selector(self): + cls = SelectorFactory('Source') + return cls(self.dbstate, self.uistate, self.track) + # FIXME isn't used anywhere class MediaEntry(ObjEntry): """ diff --git a/gramps/gui/glade/editcitation.glade b/gramps/gui/glade/editcitation.glade index 5e74ed572..23b5fbf4a 100644 --- a/gramps/gui/glade/editcitation.glade +++ b/gramps/gui/glade/editcitation.glade @@ -1,6 +1,7 @@ + @@ -12,7 +13,7 @@ 600 dialog - + True False @@ -23,12 +24,10 @@ gtk-help - False True True True True - False True @@ -40,12 +39,10 @@ gtk-cancel - False True True True True - False True @@ -57,12 +54,10 @@ gtk-ok - False True True True True - False True @@ -80,142 +75,142 @@ - + True False - 0 - 6 - 3 - Citation information - True - center - - - - - - False - False - 1 - - - - - True - True - 6 + 12 + 5 + 4 + 12 + 6 - + + + + True False - 12 - 5 - 3 - 12 - 6 - - + 1 + _Date: + True + center + date_entry + + + 1 + 2 + GTK_FILL + + + + + + True + True + True + True + Invoke date editor + none + + + + Date + - + True False - 1 - _Date: - True - center - date_entry - - - GTK_FILL - - - - - - False - True - True - True - True - Invoke date editor - False - none - + gramps-date - - Date - - - - - True - False - gramps-date - - - Date - - + + Date - - 2 - 3 - GTK_FILL - - - - - - True - True - Specific location within the information referenced. For a published work, this could include the volume of a multi-volume work and the page number(s). For a periodical, it could include volume, issue, and page numbers. For a newspaper, it could include a column number and page number. For an unpublished source, this could be a sheet number, page number, frame number, etc. A census record might have a line number or dwelling and family numbers in addition to the page number. - - - - 1 - 2 - 1 - 2 - - - - - - True - False - 1 - _Volume/Page: - True - center - volume - - - 1 - 2 - GTK_FILL - - - - - - True - False - 1 - Con_fidence: - True - center - confidence - - - 2 - 3 - GTK_FILL - - + + + 3 + 4 + 1 + 2 + GTK_FILL + + + + + + True + True + Specific location within the information referenced. For a published work, this could include the volume of a multi-volume work and the page number(s). For a periodical, it could include volume, issue, and page numbers. For a newspaper, it could include a column number and page number. For an unpublished source, this could be a sheet number, page number, frame number, etc. A census record might have a line number or dwelling and family numbers in addition to the page number. + + False + False + + + 1 + 3 + 2 + 3 + + + + + + True + False + 1 + _Volume/Page: + True + center + volume + + + 2 + 3 + GTK_FILL + + + + + + True + False + 1 + Con_fidence: + True + center + + + 3 + 4 + GTK_FILL + + + + + + True + True + The date of the entry in the source you are referencing, e.g. the date a house was visited during a census, or the date an entry was made in a birth log/registry. + + False + False + + + 1 + 3 + 1 + 2 + + + + + + True + False + 12 True @@ -234,125 +229,9 @@ Very High =Direct and primary evidence used, or by dominance of the evidence

- 1 - 2 - 2 - 3 - GTK_FILL - - -
- - - True - True - The date of the entry in the source you are referencing, e.g. the date a house was visited during a census, or the date an entry was made in a birth log/registry. - - - - 1 - 2 - - - - - - True - False - 12 - - - True - False - gtk-dialog-warning - 6 - - - False - False - 0 - - - - - 500 - True - False - 0 - 3 - <b>Note:</b> Any changes in the shared citation information will be reflected in the citation itself, for all items that reference the citation. - True - True - True - - - True - True - 1 - - - - - 3 - 4 - 5 - GTK_EXPAND | GTK_SHRINK | GTK_FILL - - - - - - True - False - 6 - - - 75 - True - True - A unique ID to identify the citation - - 6 - - - False - True - 0 - - - - - True - False - 0 - Tags: - - - False - True - 1 - - - - - True - False - 0 - - - True - True - 2 - - - - - 1 - 2 - 3 - 4 - GTK_FILL - + True + True + 0 @@ -362,478 +241,190 @@ Very High =Direct and primary evidence used, or by dominance of the evidence

1 _ID: True - gid2 + gid
- 3 - 4 - GTK_FILL - + False + True + 1
- - False + True True - True - False - none - - - - Private - - - - - True - False - gtk-dialog-authentication - - - Privacy - - - - + A unique ID to identify the citation + + False + False - 2 - 3 - 2 - 3 - - + True + True + 2 + + + 1 + 3 + 3 + 4 + GTK_FILL + + + + + + True + False + 1 + Source: + + + GTK_FILL + + + + + + True + True + True + none + + + 3 + 4 + GTK_FILL + + + + + + True + False - - False + True - True - True - False - True + False 0 - 2 - 3 - 3 - 4 - - + True + True + 0 + + + + + True + True + True + none + + + False + True + 1 + + 1 + 3 + GTK_FILL + + - + + + True + True + True + none + + + + Private + + + + + True + False + gtk-dialog-authentication + + + Privacy + + + + + + + 3 + 4 + 2 + 3 + GTK_FILL + + + + + + + True False - General - - - + 1 + Tags: - False + 4 + 5 + GTK_FILL + + + + + + True + False + 0 + label + + + 1 + 2 + 4 + 5 + GTK_FILL + + + + + + True + True + True + none + + + 3 + 4 + 4 + 5 + GTK_FILL + False True - 2 - - - - - True - True - 6 - True - 6 - - - True - True - False - - - True - False - 12 - 6 - 3 - 12 - 6 - - - True - False - 0 - _Title: - True - center - title - - - GTK_FILL - - - - - - True - False - 0 - _Author: - True - center - author - - - 1 - 2 - GTK_FILL - - - - - - True - False - 0 - 3 - A_bbreviation: - True - center - abbrev - - - 3 - 4 - GTK_FILL - - - - - - True - False - 0 - _Pub. Info.: - True - center - pub_info - - - 2 - 3 - GTK_FILL - - - - - - True - True - Authors of the source. - - - - 1 - 3 - 1 - 2 - - - - - - False - 6 - 12 - - - True - False - gtk-dialog-warning - 6 - - - False - False - 0 - - - - - 500 - True - False - 0 - 3 - <b>Note:</b> Any changes in the shared source information will be reflected in the source itself, for all items that reference the source. - True - True - True - - - True - True - 1 - - - - - 3 - 5 - 6 - GTK_EXPAND | GTK_SHRINK | GTK_FILL - - - - - - True - True - Provide a short title used for sorting, filing, and retrieving source records. - - - - 1 - 2 - 3 - 4 - - - - - - True - True - Publication Information, such as city and year of publication, name of publisher, ... - - - - 1 - 3 - 2 - 3 - - - - - - True - False - 0 - _ID: - True - gid - - - 4 - 5 - GTK_FILL - - - - - - True - False - 12 - - - True - False - 6 - - - 75 - True - True - A unique ID to identify the source - - 6 - - - False - True - 0 - - - - - True - False - 0 - Tags: - - - False - True - 1 - - - - - True - False - 0 - - - True - True - 2 - - - - - True - True - 1 - - - - - 1 - 2 - 4 - 5 - GTK_FILL - - - - - - True - True - Title of the source. - - - - 1 - 3 - - - - - - False - True - True - True - False - none - - - Private - - - - - True - False - gtk-dialog-authentication - - - Privacy - - - - - - - 2 - 3 - 3 - 4 - - - - - - - False - True - True - True - False - True - - - 2 - 3 - 4 - 5 - - - - - - - False - - - - - True - False - - - False - gtk-file - 1 - - - True - True - 0 - - - - - True - False - General - center - - - - - - False - False - 1 - - - - - False - - - - - - - True - False - Shared source information - - - - - - - - False - True - 3 + 1