diff --git a/gramps2/src/PluginUtils/_Options.py b/gramps2/src/PluginUtils/_Options.py
new file mode 100644
index 000000000..871f5a26a
--- /dev/null
+++ b/gramps2/src/PluginUtils/_Options.py
@@ -0,0 +1,513 @@
+#
+# Gramps - a GTK+/GNOME based genealogy program
+#
+# Copyright (C) 2004-2005 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$
+
+# Written by Alex Roitman
+
+"""
+General option handling, including saving and parsing.
+"""
+
+#-------------------------------------------------------------------------
+#
+# Standard Python modules
+#
+#-------------------------------------------------------------------------
+import os
+from gettext import gettext as _
+
+#-------------------------------------------------------------------------
+#
+# SAX interface
+#
+#-------------------------------------------------------------------------
+try:
+ from xml.sax import make_parser,handler,SAXParseException
+except:
+ from _xmlplus.sax import make_parser,handler,SAXParseException
+
+#-------------------------------------------------------------------------
+#
+# gramps modules
+#
+#-------------------------------------------------------------------------
+import Utils
+
+#-------------------------------------------------------------------------
+#
+# List of options for a single module
+#
+#-------------------------------------------------------------------------
+class OptionList:
+ """
+ Implements a set of options to parse and store for a given module.
+ """
+
+ def __init__(self):
+ self.options = {}
+
+ def set_options(self,options):
+ """
+ Sets the whole bunch of options for the OptionList.
+ @param options: list of options to set.
+ @type options: list
+ """
+ self.options = options
+
+ def get_options(self):
+ """
+ Returns the whole bunch of options for the OptionList.
+ @returns: list of options
+ @rtype: list
+ """
+ return self.options
+
+ def set_option(self,name,value):
+ """
+ Sets a particular option in the OptionList.
+ @param name: name of the option to set.
+ @type name: str
+ @param value: value of the option to set.
+ @type str
+ """
+ self.options[name] = value
+
+ def remove_option(self,name):
+ """
+ Removes a particular option from the OptionList.
+ @param name: name of the option to remove.
+ @type name: str
+ """
+ if self.options.has_key(name):
+ del self.options[name]
+
+ def get_option(self,name):
+ """
+ Returns the value of a particular option in the OptionList.
+ @param name: name of the option to retrieve
+ @type name: str
+ @returns: value associated with the passed option
+ @rtype: str
+ """
+ return self.options.get(name,None)
+
+#-------------------------------------------------------------------------
+#
+# Collection of option lists
+#
+#-------------------------------------------------------------------------
+class OptionListCollection:
+ """
+ Implements a collection of option lists.
+ """
+
+ def __init__(self,filename):
+ """
+ Creates an OptionListCollection instance from the list defined
+ in the specified file.
+ @param filename: XML file that contains option definitions
+ @type filename: str
+ """
+
+ self.filename = os.path.expanduser(filename)
+ self.option_list_map = {}
+ self.init_common()
+ self.parse()
+
+ def init_common(self):
+ pass
+
+ def get_option_list_map(self):
+ """
+ Returns the map of module names to option lists.
+ @returns: Returns the map of module names to option lists.
+ @rtype: dictionary
+ """
+ return self.option_list_map
+
+ def get_option_list(self,name):
+ """
+ Returns the option_list associated with the module name
+ @param name: name associated with the desired module.
+ @type name: str
+ @returns: returns the option list associated with the name,
+ or None of no such option exists
+ @rtype: str
+ """
+ return self.option_list_map.get(name,None)
+
+ def get_module_names(self):
+ """
+ Returns a list of all the module names in the OptionListCollection
+ @returns: returns the list of module names
+ @rtype: list
+ """
+ return self.option_list_map.keys()
+
+ def set_option_list(self,name,option_list):
+ """
+ Adds or replaces an option_list in the OptionListCollection.
+ @param name: name assocated with the module to add or replace.
+ @type name: str
+ @param option_list: list of options
+ @type option_list: str
+ """
+ self.option_list_map[name] = option_list
+
+ def write_common(self,f):
+ """
+ Stub function for common options. Overridden by reports.
+ """
+ pass
+
+ def write_module_common(self,f,option_list):
+ """
+ Stub function for common options. Overridden by reports.
+ """
+ pass
+
+ def save(self):
+ """
+ Saves the current OptionListCollection to the associated file.
+ """
+ f = open(self.filename,"w")
+ f.write("\n")
+ f.write('\n')
+
+ self.write_common(f)
+
+ for module_name in self.get_module_names():
+ option_list = self.get_option_list(module_name)
+ f.write('\n' % module_name)
+ options = option_list.get_options()
+ for option_name in options.keys():
+ if type(options[option_name]) in (type(list()),type(tuple())):
+ f.write(' \n')
+ else:
+ f.write(' \n' % (
+ option_name,options[option_name]) )
+
+ self.write_module_common(f,option_list)
+
+ f.write('\n')
+
+ f.write('\n')
+ f.close()
+
+ def parse(self):
+ """
+ Loads the OptionList from the associated file, if it exists.
+ """
+ try:
+ p = make_parser()
+ p.setContentHandler(OptionParser(self))
+ p.parse('file://' + self.filename)
+ except (IOError,OSError,SAXParseException):
+ pass
+
+#-------------------------------------------------------------------------
+#
+# OptionParser
+#
+#-------------------------------------------------------------------------
+class OptionParser(handler.ContentHandler):
+ """
+ SAX parsing class for the OptionListCollection XML file.
+ """
+
+ def __init__(self,collection):
+ """
+ Creates a OptionParser class that populates the passed collection.
+
+ collection: OptionListCollection to be loaded from the file.
+ """
+ handler.ContentHandler.__init__(self)
+ self.collection = collection
+
+ self.mname = None
+ self.option_list = None
+ self.oname = None
+ self.o = None
+ self.an_o = None
+ self.list_class = OptionList
+
+ def startElement(self,tag,attrs):
+ """
+ Overridden class that handles the start of a XML element
+ """
+ if tag in ("report","module"):
+ self.mname = attrs['name']
+ self.option_list = self.list_class()
+ self.o = {}
+ elif tag == "option":
+ self.oname = attrs['name']
+ if attrs.has_key('length'):
+ self.an_o = []
+ else:
+ self.an_o = attrs['value']
+ elif tag == "listitem":
+ self.an_o.append(attrs['value'])
+
+ def endElement(self,tag):
+ "Overridden class that handles the end of a XML element"
+ if tag == "option":
+ self.o[self.oname] = self.an_o
+ elif tag in ("report","module"):
+ self.option_list.set_options(self.o)
+ self.collection.set_option_list(self.mname,self.option_list)
+
+#-------------------------------------------------------------------------
+#
+# Class handling options for plugins
+#
+#-------------------------------------------------------------------------
+class OptionHandler:
+ """
+ Implements handling of the options for the plugins.
+ """
+
+ def __init__(self,module_name,options_dict,person_id=None):
+ self.module_name = module_name
+ self.default_options_dict = options_dict.copy()
+ self.options_dict = options_dict
+
+ # Retrieve our options from whole collection
+ self.init_subclass()
+ self.option_list_collection = self.collection_class(self.filename)
+ self.init_common()
+ self.saved_option_list = self.option_list_collection.get_option_list(module_name)
+ self.person_id = person_id
+
+ # Whatever was found should override the defaults
+ if self.saved_option_list:
+ self.set_options()
+ else:
+ # If nothing was found, set up the option list
+ self.saved_option_list = self.list_class()
+ self.option_list_collection.set_option_list(module_name,
+ self.saved_option_list)
+
+ def init_subclass(self):
+ self.collection_class = OptionListCollection
+ self.list_class = OptionList
+ self.filename = None
+
+ def init_common(self):
+ pass
+
+ def set_options(self):
+ """
+ Sets options to be used in this plugin according to the passed
+ options dictionary.
+
+ Dictionary values are all strings, since they were read from XML.
+ Here we need to convert them to the needed types. We use default
+ values to determine the type.
+ """
+ # First we set options_dict values based on the saved options
+ options = self.saved_option_list.get_options()
+ bad_opts = []
+ for option_name in options.keys():
+ if not self.options_dict.has_key(option_name):
+ print "Option %s is present in the %s but is not known "\
+ "to the module." % (option_name,
+ self.option_list_collection.filename)
+ print "Ignoring..."
+ bad_opts.append(option_name)
+ continue
+ try:
+ converter = Utils.get_type_converter(self.options_dict[option_name])
+ self.options_dict[option_name] = converter(options[option_name])
+ except ValueError:
+ pass
+
+ for option_name in bad_opts:
+ options.pop(option_name)
+
+ # Then we set common options from whatever was found
+ self.set_common_options()
+
+ def set_common_options(self):
+ pass
+
+ def save_options(self):
+ """
+ Saves options to file.
+
+ We need to only store non-default options. Therefore, we remove all
+ options whose values are the defaults prior to saving.
+ """
+
+ # First we save options from options_dict
+ for option_name in self.options_dict.keys():
+ if self.options_dict[option_name] == self.default_options_dict[option_name]:
+ self.saved_option_list.remove_option(option_name)
+ else:
+ self.saved_option_list.set_option(option_name,self.options_dict[option_name])
+
+ # Handle common options
+ self.save_common_options()
+
+ # Finally, save the whole collection into file
+ self.option_list_collection.save()
+
+ def save_common_options(self):
+ pass
+
+ def get_filter_number(self):
+ if self.default_options_dict.has_key('filter'):
+ return self.options_dict.get('filter',
+ self.default_options_dict['filter'])
+ else:
+ return None
+
+ def set_filter_number(self,val):
+ self.options_dict['filter'] = val
+
+ def get_person_id(self):
+ return self.person_id
+
+ def set_person_id(self,val):
+ self.person_id = val
+
+#------------------------------------------------------------------------
+#
+# Base Options class
+#
+#------------------------------------------------------------------------
+class Options:
+
+ """
+ Defines options and provides handling interface.
+
+ This is a base Options class for the modules. All modules' options
+ classes should derive from it.
+ """
+
+ def __init__(self,name,person_id=None):
+ """
+ Initializes the class, performing usual house-keeping tasks.
+ Subclasses MUST call this in their __init__() method.
+ """
+ self.set_new_options()
+ self.enable_options()
+
+ if self.enable_dict:
+ self.options_dict.update(self.enable_dict)
+ self.handler = OptionHandler(name,self.options_dict,person_id)
+
+ def set_new_options(self):
+ """
+ Sets options specific for this module.
+
+ Modules that need custom options need to override this method.
+ Two dictionaries MUST be defined here:
+
+ self.options_dict
+ This is a dictionary whose keys are option names
+ and values are the default option values.
+
+ self.options_help
+ This is a dictionary whose keys are option names
+ and values are 3- or 4- lists or tuples:
+ ('=example','Short description',VALUES,DO_PREPEND)
+ The VALUES is either a single string (in that case
+ the DO_PREPEND does not matter) or a list/tuple of
+ strings to list. In that case, if DO_PREPEND evaluates
+ as True then each string will be preneded with the ordinal
+ number when help is printed on the command line.
+
+ NOTE: Both dictionaries must have identical keys.
+
+ NOTE: If a particular module does not use custom options,
+ then it should not override this method.
+ """
+ self.options_dict = {}
+ self.options_help = {}
+
+ def enable_options(self):
+ """
+ Enables semi-common options for this module.
+
+ The semi-common option is the option which GRAMPS is aware of,
+ but not common enough to be present in all modules. Here's the list
+ of possible keys for semi-commons:
+
+ 'filter' - Filter number, selected among filters
+ available for this module. If defined,
+ get_module_filters() method must be defined
+ which returns the list of available filters.
+
+ A self.enable_dict dictionary MUST be defined here, whose keys
+ are the valid semi-common keys above, and whose values are the
+ desired default values for semi-commons.
+
+ NOTE: If a particular module does not use semi-common options,
+ then it should not override this method.
+ """
+ self.enable_dict = {}
+
+ def add_user_options(self,dialog):
+ """
+ Sets up UI controls (widgets) for the options specific for this modul.
+
+ This method MUST be overridden by modules that define new options.
+ The single argument 'dialog' is the Report.BareReportDialog instance.
+ Any attribute of the dialog is available.
+
+ After the widgets are defined, they MUST be added to the dialog
+ using the following call:
+ dialog.add_options(LABEL,widget)
+
+ NOTE: To really have any effect besides looking pretty, each widget
+ set up here must be also parsed in the parse_user_options()
+ method below.
+ """
+ pass
+
+ def parse_user_options(self,dialog):
+ """
+ Parses UI controls (widgets) for the options specific for this module.
+
+ This method MUST be overridden by modules that define new options.
+ The single argument 'dialog' is the Report.BareReportDialog instance.
+ Any attribute of the dialog is available.
+
+ After obtaining values from the widgets, they MUST be used to set the
+ appropriate options_dict values. Otherwise the values will not have
+ any user-visible effect.
+
+ NOTE: Any widget parsed here MUST be defined and added to the dialog
+ in the add_user_options() method above.
+ """
+ pass
+
+ def get_filter_number(self):
+ """
+ Return number of a filter to use.
+
+ This method MUST NOT be overridden by subclasses.
+ """
+ return self.handler.get_filter_number()
diff --git a/gramps2/src/PluginUtils/_Report.py b/gramps2/src/PluginUtils/_Report.py
new file mode 100644
index 000000000..ae4dac1e5
--- /dev/null
+++ b/gramps2/src/PluginUtils/_Report.py
@@ -0,0 +1,1942 @@
+#
+# Gramps - a GTK+/GNOME based genealogy program
+#
+# Copyright (C) 2001 David R. Hampton
+# Copyright (C) 2001-2005 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$
+
+"Report Generation Framework"
+
+__author__ = "David R. Hampton, Donald N. Allingham"
+__version__ = "$Revision$"
+
+#-------------------------------------------------------------------------
+#
+# standard python modules
+#
+#-------------------------------------------------------------------------
+import os
+from gettext import gettext as _
+from types import ClassType, InstanceType
+import logging
+
+log = logging.getLogger(".")
+
+#-------------------------------------------------------------------------
+#
+# GNOME/GTK modules
+#
+#-------------------------------------------------------------------------
+import gtk
+
+#-------------------------------------------------------------------------
+#
+# GRAMPS modules
+#
+#-------------------------------------------------------------------------
+import const
+import Utils
+import Plugins
+import PluginMgr
+import BaseDoc
+import StyleEditor
+import Config
+import PaperMenu
+import Errors
+import GenericFilter
+import NameDisplay
+from QuestionDialog import ErrorDialog, OptionDialog
+
+#-------------------------------------------------------------------------
+#
+# Import XML libraries
+#
+#-------------------------------------------------------------------------
+try:
+ from xml.sax import make_parser,handler,SAXParseException
+except:
+ from _xmlplus.sax import make_parser,handler,SAXParseException
+
+class FileEntry(gtk.HBox):
+ def __init__(self,defname,title):
+ gtk.HBox.__init__(self)
+
+ self.title = title
+ self.dir = False
+ self.entry = gtk.Entry()
+ self.entry.set_text(defname)
+ self.set_filename(defname)
+ self.set_spacing(6)
+ self.set_homogeneous(False)
+ self.button = gtk.Button()
+ im = gtk.Image()
+ im.set_from_stock(gtk.STOCK_OPEN,gtk.ICON_SIZE_BUTTON)
+ self.button.add(im)
+ self.button.connect('clicked',self.select_file)
+ self.pack_start(self.entry,True,True)
+ self.pack_end(self.button,False,False)
+
+ def select_file(self,obj):
+ if self.dir:
+ my_action = gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER
+ else:
+ my_action = gtk.FILE_CHOOSER_ACTION_SAVE
+
+ f = gtk.FileChooserDialog(self.title,
+ action=my_action,
+ buttons=(gtk.STOCK_CANCEL,
+ gtk.RESPONSE_CANCEL,
+ gtk.STOCK_OPEN,
+ gtk.RESPONSE_OK))
+
+ name = os.path.basename(self.entry.get_text())
+ if self.dir:
+ if os.path.isdir(name):
+ f.set_current_name(name)
+ elif os.path.isdir(os.path.basename(name)):
+ f.set_current_name(os.path.basename(name))
+ else:
+ f.set_current_name(name)
+ f.set_current_folder(self.spath)
+ status = f.run()
+ if status == gtk.RESPONSE_OK:
+ self.set_filename(f.get_filename())
+ f.destroy()
+
+ def set_filename(self,path):
+ if not path:
+ return
+ if os.path.dirname(path):
+ self.spath = os.path.dirname(path)
+ self.defname = os.path.basename(path)
+
+ else:
+ self.spath = os.getcwd()
+ self.defname = path
+ self.entry.set_text(os.path.join(self.spath,self.defname))
+
+ def gtk_entry(self):
+ return self.entry
+
+ def get_full_path(self,val):
+ return self.entry.get_text()
+
+ def set_directory_entry(self,opt):
+ self.dir = opt
+
+#-------------------------------------------------------------------------
+#
+# Constants
+#
+#-------------------------------------------------------------------------
+_default_template = _("Default Template")
+_user_template = _("User Defined Template")
+
+_template_map = {
+ _user_template : ""
+ }
+
+# Modes for generating reports
+MODE_GUI = 1 # Standalone report using GUI
+MODE_BKI = 2 # Book Item interface using GUI
+MODE_CLI = 4 # Command line interface (CLI)
+
+# Report categories
+CATEGORY_TEXT = 0
+CATEGORY_DRAW = 1
+CATEGORY_CODE = 2
+CATEGORY_WEB = 3
+CATEGORY_VIEW = 4
+CATEGORY_BOOK = 5
+
+standalone_categories = {
+ CATEGORY_TEXT : _("Text Reports"),
+ CATEGORY_DRAW : _("Graphical Reports"),
+ CATEGORY_CODE : _("Code Generators"),
+ CATEGORY_WEB : _("Web Page"),
+ CATEGORY_VIEW : _("View"),
+ CATEGORY_BOOK : _("Books"),
+}
+
+book_categories = {
+ CATEGORY_TEXT : _("Text"),
+ CATEGORY_DRAW : _("Graphics"),
+}
+
+#-------------------------------------------------------------------------
+#
+# Support for printing generated files
+#
+#-------------------------------------------------------------------------
+def get_print_dialog_app ():
+ """Return the name of a program which sends stdin (or the program's
+ arguments) to the printer."""
+ for printdialog in ["/usr/bin/kprinter --stdin",
+ "/usr/share/printconf/util/print.py"]:
+ if os.access (printdialog.split (' ')[0], os.X_OK):
+ return printdialog
+
+ return "lpr"
+
+def run_print_dialog (filename):
+ """Send file to the printer, possibly throwing up a dialog to
+ ask which one etc."""
+ os.environ["FILE"] = filename
+ return os.system ('cat "$FILE" | %s &' % get_print_dialog_app ())
+
+
+#-------------------------------------------------------------------------
+#
+# GrampsStyleComboBox
+#
+#-------------------------------------------------------------------------
+class GrampsStyleComboBox(gtk.ComboBox):
+ """
+ Derived from the ComboBox, this widget provides handling of Report
+ Styles.
+ """
+
+ def __init__(self,model=None):
+ """
+ Initializes the combobox, building the display column.
+ """
+ gtk.ComboBox.__init__(self,model)
+ cell = gtk.CellRendererText()
+ self.pack_start(cell,True)
+ self.add_attribute(cell,'text',0)
+
+ def set(self,style_map,default):
+ """
+ Sets the options for the ComboBox, using the passed style
+ map as the data.
+
+ @param style_map: dictionary of style names and the corresponding
+ style sheet
+ @type style_map: dictionary
+ @param default: Default selection in the ComboBox
+ @type default: str
+ """
+ self.store = gtk.ListStore(str)
+ self.set_model(self.store)
+ self.style_map = style_map
+ keys = style_map.keys()
+ keys.sort()
+ index = 0
+ start_index = 0
+ for key in keys:
+ if key == "default":
+ self.store.append(row=[_('default')])
+ else:
+ self.store.append(row=[key])
+ if key == default:
+ start_index = index
+ index += 1
+
+ self.set_active(start_index)
+
+ def get_value(self):
+ """
+ Returns the selected key (style sheet name).
+
+ @returns: Returns the name of the selected style sheet
+ @rtype: str
+ """
+ active = self.get_active()
+ if active < 0:
+ return None
+ key = self.store[active][0]
+ if key == _('default'):
+ key = "default"
+ return (key,self.style_map[key])
+
+#-------------------------------------------------------------------------
+#
+# Report
+#
+#-------------------------------------------------------------------------
+class Report:
+ """
+ The Report base class. This is a base class for generating
+ customized reports. It cannot be used as is, but it can be easily
+ sub-classed to create a functional report generator.
+ """
+
+ def __init__(self, database, person, options_class):
+ self.database = database
+ self.start_person = person
+ self.options_class = options_class
+
+ self.doc = options_class.get_document()
+
+ creator = database.get_researcher().get_name()
+ self.doc.creator(creator)
+
+ output = options_class.get_output()
+ if output:
+ self.standalone = True
+ self.doc.open(options_class.get_output())
+ else:
+ self.standalone = False
+
+ self.define_table_styles()
+ self.define_graphics_styles()
+
+ def begin_report(self):
+ if self.options_class.get_newpage():
+ self.doc.page_break()
+
+ def write_report(self):
+ pass
+
+ def end_report(self):
+ if self.standalone:
+ self.doc.close()
+
+ def define_table_styles(self):
+ """
+ This method MUST be used for adding table and cell styles.
+ """
+ pass
+
+ def define_graphics_styles(self):
+ """
+ This method MUST be used for adding drawing styles.
+ """
+ pass
+
+ def get_progressbar_data(self):
+ """The window title for this dialog, and the header line to
+ put at the top of the contents of the dialog box."""
+ return ("%s - GRAMPS" % _("Progress Report"), _("Working"))
+
+ def progress_bar_title(self,name,length):
+ markup = '%s'
+ self.lbl.set_text(markup % name)
+ self.lbl.set_use_markup(True)
+ self.pbar.set_fraction(0.0)
+
+ progress_steps = length
+ if length > 1:
+ progress_steps = progress_steps+1
+ progress_steps = progress_steps+1
+ self.pbar_max = length
+
+ def progress_bar_setup(self,total):
+ """Create a progress dialog. This routine calls a
+ customization function to find out how to fill out the dialog.
+ The argument to this function is the maximum number of items
+ that the report will progress; i.e. what's considered 100%,
+ i.e. the maximum number of times this routine will be
+ called."""
+
+ # Customize the dialog for this report
+ (title, header) = self.get_progressbar_data()
+ self.ptop = gtk.Dialog()
+ self.ptop.set_has_separator(False)
+ self.ptop.set_title(title)
+ self.ptop.set_border_width(12)
+ self.lbl = gtk.Label(header)
+ self.lbl.set_use_markup(True)
+ self.ptop.vbox.add(self.lbl)
+ self.ptop.vbox.set_spacing(10)
+ self.ptop.vbox.set_border_width(24)
+ self.pbar = gtk.ProgressBar()
+ self.pbar_max = total
+ self.pbar_index = 0.0
+
+ self.ptop.set_size_request(350,100)
+ self.ptop.vbox.add(self.pbar)
+ self.ptop.show_all()
+
+ def progress_bar_step(self):
+ """Click the progress bar over to the next value. Be paranoid
+ and insure that it doesn't go over 100%."""
+ self.pbar_index = self.pbar_index + 1.0
+ if (self.pbar_index > self.pbar_max):
+ self.pbar_index = self.pbar_max
+
+ val = self.pbar_index/self.pbar_max
+
+ self.pbar.set_text("%d of %d (%.1f%%)" % (self.pbar_index,self.pbar_max,(val*100)))
+ self.pbar.set_fraction(val)
+ while gtk.events_pending():
+ gtk.main_iteration()
+
+ def progress_bar_done(self):
+ """Done with the progress bar. It can be destroyed now."""
+ Utils.destroy_passed_object(self.ptop)
+
+
+class BareReportDialog:
+ """
+ The BareReportDialog base class. This is a base class for generating
+ customized dialogs to solicit some options for a report. This class
+ cannot be meaningfully used on its own since normally more options will
+ have to be solicited. The ReportDialog class adds this functionality.
+ The intended use of this class is either for ReportDialog or for being
+ subclassed by Bare Reports that are part of the Book.
+ """
+
+ frame_pad = 5
+ border_pad = 6
+
+ def __init__(self,database,person,option_class,name,translated_name):
+ """Initialize a dialog to request that the user select options
+ for a basic *bare* report."""
+
+ self.db = database
+ self.person = person
+ if type(option_class) == ClassType:
+ self.options = option_class(name)
+ elif type(option_class) == InstanceType:
+ self.options = option_class
+ self.report_name = translated_name
+ self.init_interface()
+
+ def init_interface(self):
+ #self.output_notebook = None
+ #self.notebook_page = 1
+ self.pagecount_menu = None
+ self.filter_combo = None
+ self.extra_menu = None
+ self.extra_textbox = None
+ self.pagebreak_checkbox = None
+ self.generations_spinbox = None
+ self.widgets = []
+ self.frame_names = []
+ self.frames = {}
+ self.format_menu = None
+ self.style_button = None
+
+ self.style_name = self.options.handler.get_default_stylesheet_name()
+ (self.max_gen,self.page_breaks) = self.options.handler.get_report_generations()
+ try:
+ self.local_filters = self.options.get_report_filters(self.person)
+ except AttributeError:
+ self.local_filters = []
+
+ self.window = gtk.Dialog('GRAMPS')
+ self.window.set_has_separator(False)
+ self.cancel = self.window.add_button(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL)
+ self.ok = self.window.add_button(gtk.STOCK_OK,gtk.RESPONSE_OK)
+
+ self.ok.connect('clicked',self.on_ok_clicked)
+ self.cancel.connect('clicked',self.on_cancel)
+
+ self.window.set_default_size(600,-1)
+
+ # Set up and run the dialog. These calls are not in top down
+ # order when looking at the dialog box as there is some
+ # interaction between the various frames.
+
+ self.setup_title()
+ self.setup_header()
+ self.tbl = gtk.Table(4,4,False)
+ self.tbl.set_col_spacings(12)
+ self.tbl.set_row_spacings(6)
+ self.tbl.set_border_width(6)
+ self.col = 0
+ self.window.vbox.add(self.tbl)
+
+ # Build the list of widgets that are used to extend the Options
+ # frame and to create other frames
+ self.add_user_options()
+
+ self.setup_center_person()
+ self.setup_target_frame()
+ self.setup_format_frame()
+ self.setup_style_frame()
+ #self.setup_output_notebook()
+
+ self.notebook = gtk.Notebook()
+ self.notebook.set_border_width(6)
+ self.window.vbox.add(self.notebook)
+
+ self.setup_paper_frame()
+ self.setup_html_frame()
+ self.setup_report_options_frame()
+ self.setup_other_frames()
+ self.notebook.set_current_page(0)
+ self.window.show_all()
+
+ def get_title(self):
+ """The window title for this dialog"""
+ return "%s - GRAMPS Book" % self.report_name
+
+ def get_header(self, name):
+ """The header line to put at the top of the contents of the
+ dialog box. By default this will just be the name of the
+ selected person. Most subclasses will customize this to give
+ some indication of what the report will be, i.e. 'Descendant
+ Report for %s'."""
+ return _("%(report_name)s for GRAMPS Book") % {
+ 'report_name' : self.report_name}
+
+ #------------------------------------------------------------------------
+ #
+ # Customization hooks for subclasses
+ #
+ #------------------------------------------------------------------------
+ def get_stylesheet_savefile(self):
+ """Where should new styles for this report be saved? This is
+ the name of an XML file that will be located in the ~/.gramps
+ directory. This file does not have to exist; it will be
+ created when needed. All subclasses should probably override
+ this function."""
+ return "basic_report.xml"
+
+ def get_report_filters(self):
+ """Return the data used to fill out the 'filter' combo box in
+ the report options box. The return value is the list of
+ strings to be inserted into the pulldown."""
+ return []
+
+ def get_report_generations(self):
+ """Return the default number of generations to start the
+ spinbox (zero to disable) and whether or not to include the
+ 'page break between generations' check box"""
+ return (10, 1)
+
+ def get_report_extra_menu_info(self):
+ """Return the data used to fill out the 'extra' option menu in
+ the report options box. The first value is the string to be
+ used as the label to the left of the menu. The second value
+ is a mapping of string:value pairs. The strings will be used
+ to label individual menu items, and the values are what will
+ be returned if a given menu item is selected. The third value
+ is the name of menu item to pre-select, and the final value is
+ a string to use as the tooltip for the textbox."""
+ return (None, None, None, None)
+
+ def get_report_extra_textbox_info(self):
+ """Return the data used to fill out the 'extra' textbox in the
+ report options dialog. The first value is the string to be
+ used as the label to the left of the textbox. The second
+ value is the string to use as the default contents of the
+ textbox. If None, then the text box will be hidden. The
+ final value is a string to use as the tooltip for the
+ textbox."""
+ return (None, None, None)
+
+ #------------------------------------------------------------------------
+ #
+ # Functions related to extending the options
+ #
+ #------------------------------------------------------------------------
+ def add_user_options(self):
+ """Called to allow subclasses add widgets to the dialog form.
+ It is called immediately before the window is displayed. All
+ calls to add_option or add_frame_option should be called in
+ this task."""
+ self.options.add_user_options(self)
+
+ def parse_user_options(self):
+ """Called to allow parsing of added widgets.
+ It is called when OK is pressed in a dialog.
+ All custom widgets should provide a parsing code here."""
+ try:
+ self.options.parse_user_options(self)
+ except:
+ log.error("Failed to parse user options.", exc_info=True)
+
+
+ def add_option(self,label_text,widget,tooltip=None):
+ """Takes a text string and a Gtk Widget, and stores them to be
+ appended to the Options section of the dialog. The text string
+ is used to create a label for the passed widget. This allows the
+ subclass to extend the Options section with its own widgets. The
+ subclass is reponsible for all managing of the widgets, including
+ extracting the final value before the report executes. This task
+ should only be called in the add_user_options task."""
+ self.widgets.append((label_text,widget))
+ if tooltip:
+ self.add_tooltip(widget,tooltip)
+
+ def add_frame_option(self,frame_name,label_text,widget,tooltip=None):
+ """Similar to add_option this method takes a frame_name, a
+ text string and a Gtk Widget. When the interface is built,
+ all widgets with the same frame_name are grouped into a
+ GtkFrame. This allows the subclass to create its own sections,
+ filling them with its own widgets. The subclass is reponsible for
+ all managing of the widgets, including extracting the final value
+ before the report executes. This task should only be called in
+ the add_user_options task."""
+
+ if self.frames.has_key(frame_name):
+ self.frames[frame_name].append((label_text,widget))
+ else:
+ self.frames[frame_name] = [(label_text,widget)]
+ self.frame_names.append(frame_name)
+ if tooltip:
+ self.add_tooltip(widget,tooltip)
+
+ #------------------------------------------------------------------------
+ #
+ # Functions to create a default output style.
+ #
+ #------------------------------------------------------------------------
+
+ def build_style_menu(self,default=None):
+ """Build a menu of style sets that are available for use in
+ this report. This menu will always have a default style
+ available, and will have any other style set name that the
+ user has previously created for this report. This menu is
+ created here instead of inline with the rest of the style
+ frame, because it must be recreated to reflect any changes
+ whenever the user closes the style editor dialog."""
+
+ if default is None:
+ default = self.style_name
+
+ style_sheet_map = self.style_sheet_list.get_style_sheet_map()
+ self.style_menu.set(style_sheet_map,default)
+
+ #------------------------------------------------------------------------
+ #
+ # Functions related to setting up the dialog window.
+ #
+ #------------------------------------------------------------------------
+ def setup_title(self):
+ """Set up the title bar of the dialog. This function relies
+ on the get_title() customization function for what the title
+ should be."""
+ self.name = NameDisplay.displayer.display(self.person)
+ self.window.set_title(self.get_title())
+
+ def setup_header(self):
+ """Set up the header line bar of the dialog. This function
+ relies on the get_header() customization function for what the
+ header line should read. If no customization function is
+ supplied by the subclass, the default is to use the full name
+ of the currently selected person."""
+
+ title = self.get_header(self.name)
+ label = gtk.Label('%s' % title)
+ label.set_use_markup(True)
+ self.window.vbox.pack_start(label, True, True,
+ ReportDialog.border_pad)
+
+ def setup_target_frame(self):
+ """Bare report dialog only uses Doc Options header."""
+
+ label = gtk.Label("%s" % _('Document Options'))
+ label.set_use_markup(1)
+ label.set_alignment(0.0,0.5)
+ self.tbl.set_border_width(12)
+ self.tbl.attach(label, 0, 4, self.col, self.col+1, gtk.FILL|gtk.EXPAND)
+ self.col += 1
+
+ def setup_center_person(self):
+ """Set up center person labels and change button.
+ Should be overwritten by standalone report dialogs. """
+
+ center_label = gtk.Label("%s" % _("Center Person"))
+ center_label.set_use_markup(True)
+ center_label.set_alignment(0.0,0.5)
+ self.tbl.set_border_width(12)
+ self.tbl.attach(center_label,0,4,self.col,self.col+1)
+ self.col += 1
+
+ name = NameDisplay.displayer.display(self.person)
+ self.person_label = gtk.Label( "%s" % name )
+ self.person_label.set_alignment(0.0,0.5)
+ self.tbl.attach(self.person_label,2,3,self.col,self.col+1)
+
+ change_button = gtk.Button("%s..." % _('C_hange') )
+ change_button.connect('clicked',self.on_center_person_change_clicked)
+ self.tbl.attach(change_button,3,4,self.col,self.col+1,gtk.SHRINK)
+ self.col += 1
+
+ def setup_style_frame(self):
+ """Set up the style frame of the dialog. This function relies
+ on other routines create the default style for this report,
+ and to read in any user defined styles for this report. It
+ the builds a menu of all the available styles for the user to
+ choose from."""
+
+ # Styles Frame
+ label = gtk.Label("%s:" % _("Style"))
+ label.set_alignment(0.0,0.5)
+
+ self.style_menu = GrampsStyleComboBox()
+ self.style_button = gtk.Button("%s..." % _("Style Editor"))
+ self.style_button.connect('clicked',self.on_style_edit_clicked)
+
+ self.tbl.attach(label,1,2,self.col,self.col+1,gtk.SHRINK|gtk.FILL)
+ self.tbl.attach(self.style_menu,2,3,self.col,self.col+1,
+ yoptions=gtk.SHRINK)
+ self.tbl.attach(self.style_button,3,4,self.col,self.col+1,
+ xoptions=gtk.SHRINK|gtk.FILL,yoptions=gtk.SHRINK)
+ self.col += 1
+
+ # Build the default style set for this report.
+ self.default_style = BaseDoc.StyleSheet()
+ self.options.make_default_style(self.default_style)
+
+ # Build the initial list of available styles sets. This
+ # includes the default style set and any style sets saved from
+ # previous invocations of gramps.
+ self.style_sheet_list = BaseDoc.StyleSheetList(
+ self.options.handler.get_stylesheet_savefile(),
+ self.default_style)
+
+ # Now build the actual menu.
+ style = self.options.handler.get_default_stylesheet_name()
+ self.build_style_menu(style)
+
+ def setup_report_options_frame(self):
+ """Set up the report options frame of the dialog. This
+ function relies on several report_xxx() customization
+ functions to determine which of the items should be present in
+ this box. *All* of these items are optional, although the
+ generations fields and the filter combo box are used in most
+ (but not all) dialog boxes."""
+
+ (em_label, extra_map, preset, em_tip) = self.get_report_extra_menu_info()
+ (et_label, string, et_tip) = self.get_report_extra_textbox_info()
+
+ row = 0
+ max_rows = 0
+ if self.max_gen:
+ max_rows = max_rows + 2
+ #if self.page_breaks:
+ # max_rows = max_rows + 1
+ if len(self.local_filters):
+ max_rows = max_rows + 1
+ if extra_map:
+ max_rows = max_rows + 1
+ if string:
+ max_rows = max_rows + 1
+
+ max_rows = max_rows + len(self.widgets)
+
+ if max_rows == 0:
+ return
+
+ table = gtk.Table(3,max_rows+1)
+ table.set_col_spacings(12)
+ table.set_row_spacings(6)
+
+ label = gtk.Label("%s" % _("Report Options"))
+ label.set_alignment(0.0,0.5)
+ label.set_use_markup(True)
+
+ table.set_border_width(6)
+ self.notebook.append_page(table,label)
+ row += 1
+
+ if len(self.local_filters):
+ self.filter_combo = GenericFilter.GrampsFilterComboBox()
+ label = gtk.Label("%s:" % _("Filter"))
+ label.set_alignment(0.0,0.5)
+ table.attach(label, 1, 2, row, row+1, gtk.SHRINK|gtk.FILL,
+ gtk.SHRINK|gtk.FILL)
+ table.attach(self.filter_combo, 2, 3, row, row+1,
+ gtk.SHRINK|gtk.FILL,gtk.SHRINK|gtk.FILL)
+
+ self.filter_combo.set(self.local_filters)
+ self.filter_combo.set_active(self.options.handler.get_filter_number())
+ row += 1
+
+ # Set up the generations spin and page break checkbox
+ if self.max_gen:
+ self.generations_spinbox = gtk.SpinButton(digits=0)
+ self.generations_spinbox.set_numeric(1)
+ adjustment = gtk.Adjustment(self.max_gen,1,999,1,0)
+ self.generations_spinbox.set_adjustment(adjustment)
+ adjustment.value_changed()
+ label = gtk.Label("%s:" % _("Generations"))
+ label.set_alignment(0.0,0.5)
+ table.attach(label, 1, 2, row, row+1,
+ gtk.SHRINK|gtk.FILL, gtk.SHRINK|gtk.FILL)
+ table.attach(self.generations_spinbox, 2, 3, row, row+1,
+ gtk.EXPAND|gtk.FILL,gtk.SHRINK|gtk.FILL)
+ row += 1
+
+ #if self.page_breaks:
+ msg = _("Page break between generations")
+ self.pagebreak_checkbox = gtk.CheckButton(msg)
+ self.pagebreak_checkbox.set_active(self.page_breaks)
+ table.attach(self.pagebreak_checkbox,2,3,row,row+1,
+ yoptions=gtk.SHRINK)
+ row += 1
+
+ # Now the "extra" option menu
+ if extra_map:
+ self.extra_menu_label = gtk.Label("%s:" % em_label)
+ self.extra_menu_label.set_alignment(0.0,0.5)
+ self.extra_menu = gtk.OptionMenu()
+ myMenu = Utils.build_string_optmenu(extra_map, preset)
+ self.extra_menu.set_menu(myMenu)
+ self.extra_menu.set_sensitive(len(extra_map) > 1)
+ self.add_tooltip(self.extra_menu,em_tip)
+ table.attach(self.extra_menu_label, 1, 2, row, row+1,
+ gtk.SHRINK|gtk.FILL, gtk.SHRINK)
+ table.attach(self.extra_menu, 2, 3, row, row+1,
+ yoptions=gtk.SHRINK)
+ row += 1
+
+ # Now the "extra" text box
+ if string:
+ self.extra_textbox_label = gtk.Label("%s:" % et_label)
+ self.extra_textbox_label.set_alignment(0.0,0)
+ swin = gtk.ScrolledWindow()
+ swin.set_shadow_type(gtk.SHADOW_IN)
+ swin.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC)
+ self.extra_textbox = gtk.TextView()
+ swin.add(self.extra_textbox)
+
+ self.extra_textbox.get_buffer().set_text('\n'.join(string))
+ self.extra_textbox.set_editable(1)
+ self.add_tooltip(self.extra_textbox,et_tip)
+ table.attach(self.extra_textbox_label, 1, 2, row, row+1,
+ gtk.SHRINK|gtk.FILL,gtk.SHRINK)
+ table.attach(swin, 2, 3, row, row+1,
+ yoptions=gtk.SHRINK)
+ row += 1
+
+ # Setup requested widgets
+ for (text,widget) in self.widgets:
+ if text:
+ text_widget = gtk.Label("%s:" % text)
+ text_widget.set_alignment(0.0,0.0)
+ table.attach(text_widget, 1, 2, row, row+1,
+ gtk.SHRINK|gtk.FILL, gtk.SHRINK)
+ table.attach(widget, 2, 3, row, row+1,
+ yoptions=gtk.SHRINK)
+ else:
+ table.attach(widget, 2, 3, row, row+1,
+ yoptions=gtk.SHRINK)
+ row += 1
+
+ def setup_other_frames(self):
+ for key in self.frame_names:
+ flist = self.frames[key]
+ table = gtk.Table(3,len(flist))
+ table.set_col_spacings(12)
+ table.set_row_spacings(6)
+ table.set_border_width(6)
+ l = gtk.Label("%s" % _(key))
+ l.set_use_markup(True)
+ self.notebook.append_page(table,l)
+
+ row = 0
+ for (text,widget) in flist:
+ if text:
+ text_widget = gtk.Label('%s:' % text)
+ text_widget.set_alignment(0.0,0.5)
+ table.attach(text_widget, 1, 2, row, row+1,
+ gtk.SHRINK|gtk.FILL, gtk.SHRINK)
+ table.attach(widget, 2, 3, row, row+1,
+ yoptions=gtk.SHRINK)
+ else:
+ table.attach(widget, 2, 3, row, row+1,
+ yoptions=gtk.SHRINK)
+ row = row + 1
+
+ #------------------------------------------------------------------------
+ #
+ # Customization hooks for stand-alone reports (subclass ReportDialog)
+ #
+ #------------------------------------------------------------------------
+ def setup_format_frame(self):
+ """Not used in bare report dialogs. Override in the subclass."""
+ pass
+
+ def setup_paper_frame(self):
+ """Not used in bare report dialogs. Override in the subclass."""
+ pass
+
+ def setup_html_frame(self):
+ """Not used in bare report dialogs. Override in the subclass."""
+ pass
+
+ def setup_output_notebook(self):
+ """Not used in bare report dialogs. Override in the subclass."""
+ pass
+
+ #------------------------------------------------------------------------
+ #
+ # Functions related to retrieving data from the dialog window
+ #
+ #------------------------------------------------------------------------
+ def parse_style_frame(self):
+ """Parse the style frame of the dialog. Save the user
+ selected output style for later use. Note that this routine
+ retrieves a value whether or not the menu is displayed on the
+ screen. The subclass will know whether this menu was enabled.
+ This is for simplicity of programming."""
+ (style_name,self.selected_style) = self.style_menu.get_value()
+ self.options.handler.set_default_stylesheet_name(style_name)
+
+ def parse_report_options_frame(self):
+ """Parse the report options frame of the dialog. Save the
+ user selected choices for later use. Note that this routine
+ retrieves a value from all fields in the frame, regardless of
+ whether or not they are displayed on the screen. The subclass
+ will know which ones it has enabled. This is for simplicity
+ of programming."""
+
+ if self.generations_spinbox:
+ self.max_gen = self.generations_spinbox.get_value_as_int()
+ else:
+ self.max_gen = 0
+
+ if self.pagebreak_checkbox and self.pagebreak_checkbox.get_active():
+ self.pg_brk = 1
+ else:
+ self.pg_brk = 0
+
+ if self.max_gen or self.pg_brk:
+ self.options.handler.set_report_generations(self.max_gen,self.pg_brk)
+
+ if self.filter_combo:
+ try:
+ self.filter = self.filter_combo.get_value()
+ active = max(0,self.filter_combo.get_active())
+ self.options.handler.set_filter_number(active)
+ except:
+ print "Error setting filter. Proceeding with 'Everyone'"
+ self.filter = GenericFilter.Everyone([])
+ else:
+ self.filter = None
+
+ if self.extra_menu:
+ self.report_menu = self.extra_menu.get_menu().get_active().get_data("d")
+ else:
+ self.report_menu = None
+
+ if self.extra_textbox:
+ b = self.extra_textbox.get_buffer()
+ text_val = unicode(b.get_text(b.get_start_iter(),b.get_end_iter(),False))
+ self.report_text = text_val.split('\n')
+ self.options.handler.set_display_format(self.report_text)
+ else:
+ self.report_text = []
+
+ def parse_other_frames(self):
+ """Do nothing. This sole purpose of this function is to give
+ subclass a place to hang a routine to parser any other frames
+ that are unique to that specific report."""
+ pass
+
+ #------------------------------------------------------------------------
+ #
+ # Callback functions from the dialog
+ #
+ #------------------------------------------------------------------------
+ def on_cancel(self,*obj):
+ pass
+
+ def on_ok_clicked(self, obj):
+ """The user is satisfied with the dialog choices. Parse all options
+ and close the window."""
+
+ # Preparation
+ self.parse_style_frame()
+ self.parse_report_options_frame()
+ self.parse_other_frames()
+ self.parse_user_options()
+
+ # Save options
+ self.options.handler.save_options()
+
+ def on_style_edit_clicked(self, *obj):
+ """The user has clicked on the 'Edit Styles' button. Create a
+ style sheet editor object and let them play. When they are
+ done, the previous routine will be called to update the dialog
+ menu for selecting a style."""
+ StyleEditor.StyleListDisplay(self.style_sheet_list,self.build_style_menu,
+ self.window)
+
+ def on_center_person_change_clicked(self,*obj):
+ import SelectPerson
+ sel_person = SelectPerson.SelectPerson(self.db,_('Select Person'))
+ new_person = sel_person.run()
+ if new_person:
+ self.new_person = new_person
+ new_name = NameDisplay.displayer.display(new_person)
+ if new_name:
+ self.person_label.set_text( "%s" % new_name )
+ self.person_label.set_use_markup(True)
+
+ #------------------------------------------------------------------------
+ #
+ # Functions related to creating the actual report document.
+ #
+ #------------------------------------------------------------------------
+ def make_report(self):
+ """Create the contents of the report. This is the meat and
+ potatoes of reports. The whole purpose of the dialog is to
+ get to this routine so that data is written to a file. This
+ routine should either write the data directly to the file, or
+ better yet, should create a subclass of a Report that will
+ write the data to a file."""
+ pass
+
+ #------------------------------------------------------------------------
+ #
+ # Miscellaneous functions.
+ #
+ #------------------------------------------------------------------------
+ def add_tooltip(self,widget,string):
+ """Adds a tooltip to the specified widget"""
+ if not widget or not string:
+ return
+ tip = gtk.Tooltips()
+ tip.set_tip(widget,string)
+
+
+class ReportDialog(BareReportDialog):
+ """
+ The ReportDialog base class. This is a base class for generating
+ customized dialogs to solicit options for a report. It cannot be
+ used as is, but it can be easily sub-classed to create a functional
+ dialog for a stand-alone report.
+ """
+
+ def __init__(self,database,person,option_class,name,translated_name):
+ """Initialize a dialog to request that the user select options
+ for a basic *stand-alone* report."""
+
+ self.style_name = "default"
+ self.page_html_added = False
+ BareReportDialog.__init__(self,database,person,option_class,
+ name,translated_name)
+
+ # Allow for post processing of the format frame, since the
+ # show_all task calls events that may reset values
+
+ def init_interface(self):
+ BareReportDialog.init_interface(self)
+ if self.format_menu:
+ self.doc_type_changed(self.format_menu)
+ self.setup_post_process()
+
+ def setup_post_process(self):
+ pass
+
+ def setup_center_person(self):
+ pass
+
+ def get_title(self):
+ """The window title for this dialog"""
+ name = self.report_name
+ category = standalone_categories[self.category]
+ return "%s - %s - GRAMPS" % (name,category)
+
+ def get_header(self, name):
+ """The header line to put at the top of the contents of the
+ dialog box. By default this will just be the name of the
+ report for the selected person. """
+ return _("%(report_name)s for %(person_name)s") % {
+ 'report_name' : self.report_name,
+ 'person_name' : name}
+
+ #------------------------------------------------------------------------
+ #
+ # Customization hooks for subclasses
+ #
+ #------------------------------------------------------------------------
+ def get_target_is_directory(self):
+ """Is the user being asked to input the name of a file or a
+ directory in the 'Save As' File Entry widget. This item
+ currently only selects the Filename/Directory prompt, and
+ whether or not the browser accepts filenames. In the future it
+ may also control checking of the selected filename."""
+ return None
+
+ def get_default_basename(self):
+ """What should the default name be?
+ """
+ spath = self.options.handler.get_stylesheet_savefile()
+ return spath.split('.')[0]
+
+ def get_print_pagecount_map(self):
+ """Return the data used to fill out the 'pagecount' option
+ menu in the print options box. The first value is a mapping
+ of string:value pairs. The strings will be used to label
+ individual menu items, and the values are what will be
+ returned if a given menu item is selected. The second value
+ is the name of menu item to pre-select."""
+ return (None, None)
+
+ #------------------------------------------------------------------------
+ #
+ # Functions related getting/setting the default directory for a dialog.
+ #
+ #------------------------------------------------------------------------
+ def get_default_directory(self):
+ """Get the name of the directory to which the target dialog
+ box should default. This value can be set in the preferences
+ panel."""
+ return Config.get_report_dir()
+
+ def set_default_directory(self, value):
+ """Save the name of the current directory, so that any future
+ reports will default to the most recently used directory.
+ This also changes the directory name that will appear in the
+ preferences panel, but does not change the preference in disk.
+ This means that the last directory used will only be
+ remembered for this session of gramps unless the user saves
+ his/her preferences."""
+ Config.save_report_dir(value)
+
+ #------------------------------------------------------------------------
+ #
+ # Functions related to selecting/changing the current file format.
+ #
+ #------------------------------------------------------------------------
+ def make_doc_menu(self,active=None):
+ """Build a menu of document types that are appropriate for
+ this report. This menu will be generated based upon the type
+ of document (text, draw, graph, etc. - a subclass), whether or
+ not the document requires table support, etc."""
+ return None
+
+ def make_document(self):
+ """Create a document of the type requested by the user."""
+
+ self.doc = self.format(self.selected_style,self.paper,
+ self.template_name,self.orien)
+ self.options.set_document(self.doc)
+ if self.print_report.get_active ():
+ self.doc.print_requested ()
+
+ def doc_type_changed(self, obj):
+ """This routine is called when the user selects a new file
+ formats for the report. It adjust the various dialog sections
+ to reflect the appropriate values for the currently selected
+ file format. For example, a HTML document doesn't need any
+ paper size/orientation options, but it does need a template
+ file. Those chances are made here."""
+
+ label = obj.get_printable()
+ if label:
+ self.print_report.set_label (label)
+ self.print_report.set_sensitive (True)
+ else:
+ self.print_report.set_label (_("Print a copy"))
+ self.print_report.set_sensitive (False)
+
+ # Is this to be a printed report or an electronic report
+ # (i.e. a set of web pages)
+
+ if self.page_html_added:
+ self.notebook.remove_page(0)
+ if obj.get_paper() == 1:
+ self.paper_label = gtk.Label('%s'%_("Paper Options"))
+ self.paper_label.set_use_markup(True)
+ self.notebook.insert_page(self.paper_table,self.paper_label,0)
+ self.paper_table.show_all()
+ else:
+ self.html_label = gtk.Label('%s' % _("HTML Options"))
+ self.html_label.set_use_markup(True)
+ self.notebook.insert_page(self.html_table,self.html_label,0)
+ self.html_table.show_all()
+
+ if not self.get_target_is_directory():
+ fname = self.target_fileentry.get_full_path(0)
+ (spath,ext) = os.path.splitext(fname)
+
+ ext_val = obj.get_ext()
+ if ext_val:
+ fname = spath + ext_val
+ else:
+ fname = spath
+ self.target_fileentry.set_filename(fname)
+
+ # Does this report format use styles?
+ if self.style_button:
+ self.style_button.set_sensitive(obj.get_styles())
+ self.style_menu.set_sensitive(obj.get_styles())
+ self.page_html_added = True
+
+ #------------------------------------------------------------------------
+ #
+ # Functions related to setting up the dialog window.
+ #
+ #------------------------------------------------------------------------
+ def setup_target_frame(self):
+ """Set up the target frame of the dialog. This function
+ relies on several target_xxx() customization functions to
+ determine whether the target is a directory or file, what the
+ title of any browser window should be, and what default
+ directory should be used."""
+
+ # Save Frame
+
+ label = gtk.Label("%s" % _('Document Options'))
+ label.set_use_markup(1)
+ label.set_alignment(0.0,0.5)
+ self.tbl.set_border_width(12)
+ self.tbl.attach(label, 0, 4, self.col, self.col+1, gtk.FILL)
+ self.col += 1
+
+ hid = self.get_stylesheet_savefile()
+ if hid[-4:]==".xml":
+ hid = hid[0:-4]
+ self.target_fileentry = FileEntry(hid,_("Save As"))
+
+ if self.get_target_is_directory():
+ self.target_fileentry.set_directory_entry(1)
+ self.doc_label = gtk.Label("%s:" % _("Directory"))
+ else:
+ self.doc_label = gtk.Label("%s:" % _("Filename"))
+ self.doc_label.set_alignment(0.0,0.5)
+
+ self.tbl.attach(self.doc_label, 1, 2, self.col, self.col+1,
+ xoptions=gtk.SHRINK|gtk.FILL,yoptions=gtk.SHRINK)
+ self.tbl.attach(self.target_fileentry, 2, 4, self.col, self.col+1,
+ xoptions=gtk.EXPAND|gtk.FILL,yoptions=gtk.SHRINK)
+ self.col += 1
+
+ spath = self.get_default_directory()
+
+ self.target_fileentry.set_filename(spath)
+ self.target_fileentry.gtk_entry().set_position(len(spath))
+
+ def setup_format_frame(self):
+ """Set up the format frame of the dialog. This function
+ relies on the make_doc_menu() function to do all the hard
+ work."""
+
+ self.print_report = gtk.CheckButton (_("Print a copy"))
+ self.tbl.attach(self.print_report,2,4,self.col,self.col+1,
+ yoptions=gtk.SHRINK)
+ self.col += 1
+
+ self.make_doc_menu(self.options.handler.get_format_name())
+ self.format_menu.connect('changed',self.doc_type_changed)
+ label = gtk.Label("%s:" % _("Output Format"))
+ label.set_alignment(0.0,0.5)
+ self.tbl.attach(label,1,2,self.col,self.col+1,gtk.SHRINK|gtk.FILL)
+ self.tbl.attach(self.format_menu,2,4,self.col,self.col+1,
+ yoptions=gtk.SHRINK)
+ self.col += 1
+
+ ext = self.format_menu.get_ext()
+ if ext == None:
+ ext = ""
+ else:
+ spath = self.get_default_directory()
+ if self.get_target_is_directory():
+ self.target_fileentry.set_filename(spath)
+ else:
+ base = self.get_default_basename()
+ spath = os.path.normpath("%s/%s%s" % (spath,base,ext))
+ self.target_fileentry.set_filename(spath)
+
+ def setup_output_notebook(self):
+ """Set up the output notebook of the dialog. This sole
+ purpose of this function is to grab a pointer for later use in
+ the callback from when the file format is changed."""
+ pass
+
+ def size_changed(self,obj):
+ (paper,name) = self.papersize_menu.get_value()
+ if paper.get_width() <= 0:
+ self.pwidth.set_sensitive(1)
+ self.pheight.set_sensitive(1)
+ else:
+ self.pwidth.set_sensitive(0)
+ self.pheight.set_sensitive(0)
+ self.pwidth.set_text("%.2f" % paper.get_width())
+ self.pheight.set_text("%.2f" % paper.get_height())
+
+ def setup_paper_frame(self):
+ """Set up the paper selection frame of the dialog. This
+ function relies on a paper_xxx() customization functions to
+ determine whether the pagecount menu should appear and what
+ its strings should be."""
+
+ (pagecount_map, start_text) = self.get_print_pagecount_map()
+
+ if pagecount_map:
+ self.paper_table = gtk.Table(2,6)
+ else:
+ self.paper_table = gtk.Table(3,6)
+ self.paper_table.set_col_spacings(12)
+ self.paper_table.set_row_spacings(6)
+ self.paper_table.set_border_width(6)
+
+ self.papersize_menu = PaperMenu.GrampsPaperComboBox()
+ self.papersize_menu.connect('changed',self.size_changed)
+
+ self.orientation_menu = PaperMenu.GrampsOrientationComboBox()
+ l = gtk.Label("%s:" % _("Size"))
+ l.set_alignment(0.0,0.5)
+
+ self.paper_table.attach(l,1,2,0,1,gtk.SHRINK|gtk.FILL)
+ self.paper_table.attach(self.papersize_menu,2,3,0,1,
+ yoptions=gtk.SHRINK)
+ l = gtk.Label("%s:" % _("Height"))
+ l.set_alignment(0.0,0.5)
+ self.paper_table.attach(l,3,4,0,1,gtk.SHRINK|gtk.FILL)
+
+ self.pheight = gtk.Entry()
+ self.pheight.set_sensitive(0)
+ self.paper_table.attach(self.pheight,4,5,0,1)
+
+ l = gtk.Label(_("cm"))
+ l.set_alignment(0.0,0.5)
+ self.paper_table.attach(l,5,6,0,1,gtk.SHRINK|gtk.FILL)
+
+ l = gtk.Label("%s:" % _("Orientation"))
+ l.set_alignment(0.0,0.5)
+ self.paper_table.attach(l,1,2,1,2,gtk.SHRINK|gtk.FILL)
+ self.paper_table.attach(self.orientation_menu,2,3,1,2,
+ yoptions=gtk.SHRINK)
+ l = gtk.Label("%s:" % _("Width"))
+ l.set_alignment(0.0,0.5)
+ self.paper_table.attach(l,3,4,1,2,gtk.SHRINK|gtk.FILL)
+
+ self.pwidth = gtk.Entry()
+ self.pwidth.set_sensitive(0)
+ self.paper_table.attach(self.pwidth,4,5,1,2)
+
+ l = gtk.Label(_("cm"))
+ l.set_alignment(0.0,0.5)
+ self.paper_table.attach(l,5,6,1,2,gtk.SHRINK|gtk.FILL)
+
+ self.papersize_menu.set(PaperMenu.paper_sizes,
+ self.options.handler.get_paper_name())
+ self.orientation_menu.set(self.options.handler.get_orientation())
+
+ # The optional pagecount stuff.
+ if pagecount_map:
+ self.pagecount_menu = gtk.OptionMenu()
+ myMenu = Utils.build_string_optmenu(pagecount_map, start_text)
+ self.pagecount_menu.set_menu(myMenu)
+ l = gtk.Label("%s:" % _("Page Count"))
+ l.set_alignment(0.0,0.5)
+ self.paper_table.attach(l,1,2,2,3,gtk.SHRINK|gtk.FILL)
+ self.paper_table.attach(self.pagecount_menu,2,3,2,3)
+
+ def html_file_enable(self,obj):
+ active = obj.get_active()
+ text = unicode(obj.get_model()[active][0])
+ if _template_map.has_key(text):
+ if _template_map[text]:
+ self.html_fileentry.set_sensitive(0)
+ else:
+ self.html_fileentry.set_sensitive(1)
+ else:
+ self.html_fileentry.set_sensitive(0)
+
+
+ def setup_html_frame(self):
+ """Set up the html frame of the dialog. This sole purpose of
+ this function is to grab a pointer for later use in the parse
+ html frame function."""
+
+ self.html_table = gtk.Table(3,3)
+ self.html_table.set_col_spacings(12)
+ self.html_table.set_row_spacings(6)
+ self.html_table.set_border_width(0)
+
+ label = gtk.Label("%s:" % _("Template"))
+ label.set_alignment(0.0,0.5)
+ self.html_table.attach(label, 1, 2, 1, 2, gtk.SHRINK|gtk.FILL)
+
+ self.template_combo = gtk.combo_box_new_text()
+ tlist = _template_map.keys()
+ tlist.sort()
+
+ template_name = self.options.handler.get_template_name()
+
+ self.template_combo.append_text(_default_template)
+ template_index = 1
+ active_index = 0
+ for template in tlist:
+ if template != _user_template:
+ self.template_combo.append_text(template)
+ if _template_map[template] == template_name:
+ active_index = template_index
+ template_index = template_index + 1
+ self.template_combo.append_text(_user_template)
+
+ self.template_combo.connect('changed',self.html_file_enable)
+
+ self.html_table.attach(self.template_combo,2,3,1,2)
+ label = gtk.Label("%s:" % _("User Template"))
+ label.set_alignment(0.0,0.5)
+ self.html_table.attach(label, 1, 2, 2, 3, gtk.SHRINK|gtk.FILL)
+ self.html_fileentry = FileEntry("HTML_Template",
+ _("Choose File"))
+ if template_name and not active_index:
+ active_index = template_index
+ user_template = template_name
+ self.html_fileentry.set_sensitive(True)
+ else:
+ user_template = ''
+ self.html_fileentry.set_sensitive(False)
+
+ if os.path.isfile(user_template):
+ self.html_fileentry.set_filename(user_template)
+ self.html_table.attach(self.html_fileentry,2,3,2,3)
+ self.template_combo.set_active(active_index)
+
+
+ #------------------------------------------------------------------------
+ #
+ # Functions related to retrieving data from the dialog window
+ #
+ #------------------------------------------------------------------------
+ def parse_target_frame(self):
+ """Parse the target frame of the dialog. If the target
+ filename is empty this routine returns a special value of None
+ to tell the calling routine to give up. This function also
+ saves the current directory so that any future reports will
+ default to the most recently used directory."""
+ self.target_path = self.target_fileentry.get_full_path(0)
+ if not self.target_path:
+ return None
+
+ # First we check whether the selected path exists
+ if os.path.exists(self.target_path):
+
+ # selected path is an existing dir and we need a dir
+ if os.path.isdir(self.target_path) \
+ and self.get_target_is_directory():
+
+ # check whether the dir has rwx permissions
+ if not os.access(self.target_path,os.R_OK|os.W_OK|os.X_OK):
+ ErrorDialog(_('Permission problem'),
+ _("You do not have permission to write "
+ "under the directory %s\n\n"
+ "Please select another directory or correct "
+ "the permissions." % self.target_path)
+ )
+ return None
+
+ # selected path is an exsting file and we need a file
+ if os.path.isfile(self.target_path) \
+ and not self.get_target_is_directory():
+ a = OptionDialog(_('File already exists'),
+ _('You can choose to either overwrite the '
+ 'file, or change the selected filename.'),
+ _('_Overwrite'),None,
+ _('_Change filename'),None)
+
+ if a.get_response() == gtk.RESPONSE_YES:
+ return None
+
+ # selected path does not exist yet
+ else:
+ # we will need to create the file/dir
+ # need to make sure we can create in the parent dir
+ parent_dir = os.path.dirname(os.path.normpath(self.target_path))
+ if not os.access(parent_dir,os.W_OK):
+ ErrorDialog(_('Permission problem'),
+ _("You do not have permission to create "
+ "%s\n\n"
+ "Please select another path or correct "
+ "the permissions." % self.target_path)
+ )
+ return None
+
+ self.set_default_directory(os.path.dirname(self.target_path) + os.sep)
+ self.options.handler.output = self.target_path
+ return 1
+
+ def parse_format_frame(self):
+ """Parse the format frame of the dialog. Save the user
+ selected output format for later use."""
+ self.format = self.format_menu.get_reference()
+ format_name = self.format_menu.get_clname()
+ self.options.handler.set_format_name(format_name)
+
+ def parse_paper_frame(self):
+ """Parse the paper frame of the dialog. Save the user
+ selected choices for later use. Note that this routine
+ retrieves a value from the pagecount menu, whether or not it
+ is displayed on the screen. The subclass will know which ones
+ it has enabled. This is for simplicity of programming."""
+
+ (self.paper,paper_name) = self.papersize_menu.get_value()
+
+ self.options.handler.set_paper_name(paper_name)
+ self.options.handler.set_paper(self.paper)
+
+ if self.paper.get_height() <= 0 or self.paper.get_width() <= 0:
+ try:
+ h = float(unicode(self.pheight.get_text()))
+ w = float(unicode(self.pwidth.get_text()))
+
+ if h <= 1.0 or w <= 1.0:
+ self.paper.set_height(29.7)
+ self.paper.set_width(21.0)
+ else:
+ self.paper.set_height(h)
+ self.paper.set_width(w)
+ except:
+ self.paper.set_height(29.7)
+ self.paper.set_width(21.0)
+
+ self.orien = self.orientation_menu.get_value()
+ self.options.handler.set_orientation(self.orien)
+
+ if self.pagecount_menu == None:
+ self.pagecount = 0
+ else:
+ self.pagecount = self.pagecount_menu.get_menu().get_active().get_data("d")
+
+ def parse_html_frame(self):
+ """Parse the html frame of the dialog. Save the user selected
+ html template name for later use. Note that this routine
+ retrieves a value whether or not the file entry box is
+ displayed on the screen. The subclass will know whether this
+ entry was enabled. This is for simplicity of programming."""
+
+ model = self.template_combo.get_model()
+ text = unicode(model[self.template_combo.get_active()][0])
+
+ if _template_map.has_key(text):
+ if text == _user_template:
+ self.template_name = self.html_fileentry.get_full_path(0)
+ else:
+ self.template_name = "%s/%s" % (const.template_dir,_template_map[text])
+ else:
+ self.template_name = ""
+ self.options.handler.set_template_name(self.template_name)
+
+ def on_ok_clicked(self, obj):
+ """The user is satisfied with the dialog choices. Validate
+ the output file name before doing anything else. If there is
+ a file name, gather the options and create the report."""
+
+ # Is there a filename? This should also test file permissions, etc.
+ if not self.parse_target_frame():
+ self.window.run()
+
+ # Preparation
+ self.parse_format_frame()
+ self.parse_style_frame()
+ self.parse_paper_frame()
+ self.parse_html_frame()
+ self.parse_report_options_frame()
+ self.parse_other_frames()
+ self.parse_user_options()
+
+ # Create the output document.
+ self.make_document()
+
+ # Save options
+ self.options.handler.save_options()
+
+#-----------------------------------------------------------------------
+#
+# Textual reports
+#
+#-----------------------------------------------------------------------
+class TextReportDialog(ReportDialog):
+ """A class of ReportDialog customized for text based reports."""
+
+ def __init__(self,database,person,options,name,translated_name):
+ """Initialize a dialog to request that the user select options
+ for a basic text report. See the ReportDialog class for more
+ information."""
+ self.category = CATEGORY_TEXT
+ ReportDialog.__init__(self,database,person,options,name,translated_name)
+
+ #------------------------------------------------------------------------
+ #
+ # Customization hooks for subclasses
+ #
+ #------------------------------------------------------------------------
+ def doc_uses_tables(self):
+ """Does this report require the ability to generate tables in
+ the file format. Override this for documents that do need
+ table support."""
+ return 0
+
+ #------------------------------------------------------------------------
+ #
+ # Functions related to selecting/changing the current file format.
+ #
+ #------------------------------------------------------------------------
+ def make_doc_menu(self,active=None):
+ """Build a menu of document types that are appropriate for
+ this text report. This menu will be generated based upon
+ whether the document requires table support, etc."""
+ self.format_menu = Plugins.GrampsTextFormatComboBox()
+ self.format_menu.set(self.doc_uses_tables(),
+ self.doc_type_changed, None, active)
+
+#-----------------------------------------------------------------------
+#
+# Drawing reports
+#
+#-----------------------------------------------------------------------
+class DrawReportDialog(ReportDialog):
+ """A class of ReportDialog customized for drawing based reports."""
+ def __init__(self,database,person,opt,name,translated_name):
+ """Initialize a dialog to request that the user select options
+ for a basic drawing report. See the ReportDialog class for
+ more information."""
+ self.category = CATEGORY_DRAW
+ ReportDialog.__init__(self,database,person,opt,name,translated_name)
+
+ #------------------------------------------------------------------------
+ #
+ # Functions related to selecting/changing the current file format.
+ #
+ #------------------------------------------------------------------------
+ def make_doc_menu(self,active=None):
+ """Build a menu of document types that are appropriate for
+ this drawing report."""
+ self.format_menu = Plugins.GrampsDrawFormatComboBox()
+ self.format_menu.set(False,self.doc_type_changed, None, active)
+
+
+#-----------------------------------------------------------------------
+#
+# Parser for templates file
+#
+#-----------------------------------------------------------------------
+class TemplateParser(handler.ContentHandler):
+ """
+ Interface to the document template file
+ """
+ def __init__(self,data,fpath):
+ """
+ Creates a template parser. The parser loads map of tempate names
+ to the file containing the tempate.
+
+ data - dictionary that holds the name to path mappings
+ fpath - filename of the XML file
+ """
+ handler.ContentHandler.__init__(self)
+ self.data = data
+ self.path = fpath
+
+ def setDocumentLocator(self,locator):
+ """Sets the XML document locator"""
+ self.locator = locator
+
+ def startElement(self,tag,attrs):
+ """
+ Loads the dictionary when an XML tag of 'template' is found. The format
+ XML tag is
+ """
+
+ if tag == "template":
+ self.data[attrs['title']] = attrs['file']
+
+#-----------------------------------------------------------------------
+#
+# Initialization
+#
+#-----------------------------------------------------------------------
+try:
+ parser = make_parser()
+ gspath = const.template_dir
+ parser.setContentHandler(TemplateParser(_template_map,gspath))
+ parser.parse("file://%s/templates.xml" % gspath)
+ parser = make_parser()
+ gspath = os.path.expanduser("~/.gramps/templates")
+ parser.setContentHandler(TemplateParser(_template_map,gspath))
+ parser.parse("file://%s/templates.xml" % gspath)
+except (IOError,OSError,SAXParseException):
+ pass
+
+#------------------------------------------------------------------------
+#
+# Command-line report
+#
+#------------------------------------------------------------------------
+class CommandLineReport:
+ """
+ Provides a way to generate report from the command line.
+
+ """
+
+ def __init__(self,database,name,category,option_class,options_str_dict,
+ noopt=False):
+ self.database = database
+ self.category = category
+ self.option_class = option_class(name)
+ self.show = options_str_dict.pop('show',None)
+ self.options_str_dict = options_str_dict
+ self.init_options(noopt)
+ self.parse_option_str()
+ self.show_options()
+
+ def init_options(self,noopt):
+ self.options_dict = {
+ 'of' : self.option_class.handler.module_name,
+ 'off' : self.option_class.handler.get_format_name(),
+ 'style' : self.option_class.handler.get_default_stylesheet_name(),
+ 'papers' : self.option_class.handler.get_paper_name(),
+ 'papero' : self.option_class.handler.get_orientation(),
+ 'template' : self.option_class.handler.get_template_name(),
+ 'id' : ''
+ }
+
+ self.options_help = {
+ 'of' : ["=filename","Output file name. MANDATORY"],
+ 'off' : ["=format","Output file format."],
+ 'style' : ["=name","Style name."],
+ 'papers' : ["=name","Paper size name."],
+ 'papero' : ["=num","Paper orientation number."],
+ 'template' : ["=name","Template name (HTML only)."],
+ 'id' : ["=ID","Gramps ID of a central person. MANDATORY"],
+ 'filter' : ["=num","Filter number."],
+ 'gen' : ["=num","Number of generations to follow."],
+ 'pagebbg' : ["=0/1","Page break between generations."],
+ 'dispf' : ["=str","Display format for the outputbox."],
+ }
+
+ if noopt:
+ return
+
+ # Add report-specific options
+ for key in self.option_class.handler.options_dict.keys():
+ if key not in self.options_dict.keys():
+ self.options_dict[key] = self.option_class.handler.options_dict[key]
+
+ # Add help for report-specific options
+ for key in self.option_class.options_help.keys():
+ if key not in self.options_help.keys():
+ self.options_help[key] = self.option_class.options_help[key]
+
+ def parse_option_str(self):
+ for opt in self.options_str_dict.keys():
+ if opt in self.options_dict.keys():
+ converter = Utils.get_type_converter(self.options_dict[opt])
+ self.options_dict[opt] = converter(self.options_str_dict[opt])
+ self.option_class.handler.options_dict[opt] = self.options_dict[opt]
+ else:
+ print "Ignoring unknown option: %s" % opt
+
+ person_id = self.options_dict['id']
+ self.person = self.database.get_person_from_gramps_id(person_id)
+ id_list = []
+ for person_handle in self.database.get_person_handles():
+ person = self.database.get_person_from_handle(person_handle)
+ id_list.append("%s\t%s" % (
+ person.get_gramps_id(),
+ NameDisplay.displayer.display(person)))
+ self.options_help['id'].append(id_list)
+ self.options_help['id'].append(False)
+
+ if self.options_dict.has_key('filter'):
+ filter_num = self.options_dict['filter']
+ self.filters = self.option_class.get_report_filters(self.person)
+ self.option_class.handler.set_filter_number(filter_num)
+
+ filt_list = [ filt.get_name() for filt in self.filters ]
+ cust_filt_list = [ filt2.get_name() for filt2 in
+ GenericFilter.CustomFilters.get_filters() ]
+ filt_list.extend(cust_filt_list)
+ self.options_help['filter'].append(filt_list)
+ self.options_help['filter'].append(True)
+
+ if self.options_dict.has_key('gen'):
+ max_gen = self.options_dict['gen']
+ page_breaks = self.options_dict['pagebbg']
+ self.option_class.handler.set_report_generations(max_gen,page_breaks)
+
+ self.options_help['gen'].append("Whatever Number You Wish")
+ self.options_help['pagebbg'].append([
+ "No page break","Page break"])
+ self.options_help['pagebbg'].append(True)
+
+ if self.options_dict.has_key('dispf'):
+ dispf = ''.join(self.options_dict['dispf']).replace('\\n','\n')
+ self.option_class.handler.set_display_format(dispf)
+
+ self.options_help['dispf'].append(
+ "Any string -- may use keyword substitutions")
+
+ self.option_class.handler.output = self.options_dict['of']
+ self.options_help['of'].append(os.path.expanduser("~/whatever_name"))
+
+ if self.category == CATEGORY_TEXT:
+ for item in PluginMgr.textdoc_list:
+ if item[7] == self.options_dict['off']:
+ self.format = item[1]
+ self.options_help['off'].append(
+ [ item[7] for item in PluginMgr.textdoc_list ]
+ )
+ self.options_help['off'].append(False)
+ elif self.category == CATEGORY_DRAW:
+ for item in PluginMgr.drawdoc_list:
+ if item[6] == self.options_dict['off']:
+ self.format = item[1]
+ self.options_help['off'].append(
+ [ item[6] for item in PluginMgr.drawdoc_list ]
+ )
+ self.options_help['off'].append(False)
+ elif self.category == CATEGORY_BOOK:
+ for item in PluginMgr.bookdoc_list:
+ if item[6] == self.options_dict['off']:
+ self.format = item[1]
+ self.options_help['off'].append(
+ [ item[6] for item in PluginMgr.bookdoc_list ]
+ )
+ self.options_help['off'].append(False)
+ else:
+ self.format = None
+
+ for paper in PaperMenu.paper_sizes:
+ if paper.get_name() == self.options_dict['papers']:
+ self.paper = paper
+ self.option_class.handler.set_paper(self.paper)
+ self.options_help['papers'].append(
+ [ paper.get_name() for paper in PaperMenu.paper_sizes
+ if paper.get_name() != 'Custom Size' ] )
+ self.options_help['papers'].append(False)
+
+ self.orien = self.options_dict['papero']
+ self.options_help['papero'].append([
+ "%d\tPortrait" % BaseDoc.PAPER_PORTRAIT,
+ "%d\tLandscape" % BaseDoc.PAPER_LANDSCAPE ] )
+ self.options_help['papero'].append(False)
+
+ self.template_name = self.options_dict['template']
+ self.options_help['template'].append(os.path.expanduser("~/whatever_name"))
+
+ if self.category in (CATEGORY_TEXT,CATEGORY_DRAW):
+ default_style = BaseDoc.StyleSheet()
+ self.option_class.make_default_style(default_style)
+
+ # Read all style sheets available for this item
+ style_file = self.option_class.handler.get_stylesheet_savefile()
+ self.style_list = BaseDoc.StyleSheetList(style_file,default_style)
+
+ # Get the selected stylesheet
+ style_name = self.option_class.handler.get_default_stylesheet_name()
+ self.selected_style = self.style_list.get_style_sheet(style_name)
+
+ self.options_help['style'].append(
+ self.style_list.get_style_names() )
+ self.options_help['style'].append(False)
+
+ def show_options(self):
+ if not self.show:
+ return
+ elif self.show == 'all':
+ print " Available options:"
+ for key in self.options_dict.keys():
+ print " %s" % key
+ print " Use 'show=option' to see description and acceptable values"
+ elif self.show in self.options_dict.keys():
+ print ' %s%s\t%s' % (self.show,
+ self.options_help[self.show][0],
+ self.options_help[self.show][1])
+ print " Available values are:"
+ vals = self.options_help[self.show][2]
+ if type(vals) in [list,tuple]:
+ if self.options_help[self.show][3]:
+ for num in range(len(vals)):
+ print " %d\t%s" % (num,vals[num])
+ else:
+ for val in vals:
+ print " %s" % val
+ else:
+ print " %s" % self.options_help[self.show][2]
+
+ else:
+ self.show = None
+
+#------------------------------------------------------------------------
+#
+# Generic task functions for reports
+#
+#------------------------------------------------------------------------
+# Standalone GUI report generic task
+def report(database,person,report_class,options_class,translated_name,name,category):
+ """
+ report - task starts the report. The plugin system requires that the
+ task be in the format of task that takes a database and a person as
+ its arguments.
+ """
+
+ if category == CATEGORY_TEXT:
+ dialog_class = TextReportDialog
+ elif category == CATEGORY_DRAW:
+ dialog_class = DrawReportDialog
+ elif category in (CATEGORY_BOOK,CATEGORY_VIEW,
+ CATEGORY_CODE,CATEGORY_WEB):
+ report_class(database,person)
+ return
+ else:
+ dialog_class = ReportDialog
+
+ dialog = dialog_class(database,person,options_class,name,translated_name)
+ response = dialog.window.run()
+ if response == gtk.RESPONSE_OK:
+ try:
+ MyReport = report_class(dialog.db,dialog.person,dialog.options)
+ MyReport.doc.init()
+ MyReport.begin_report()
+ MyReport.write_report()
+ MyReport.end_report()
+ except Errors.FilterError, msg:
+ (m1,m2) = msg.messages()
+ ErrorDialog(m1,m2)
+ except Errors.ReportError, msg:
+ (m1,m2) = msg.messages()
+ ErrorDialog(m1,m2)
+ except Errors.DatabaseError,msg:
+ ErrorDialog(_("Report could not be created"),str(msg))
+ except:
+ log.error("Failed to run report.", exc_info=True)
+ dialog.window.destroy()
+
+# Book item generic task
+def write_book_item(database,person,report_class,options_class):
+ """Write the Timeline Graph using options set.
+ All user dialog has already been handled and the output file opened."""
+ try:
+ if options_class.handler.get_person_id():
+ person = database.get_person_from_gramps_id(options_class.handler.get_person_id())
+ return report_class(database,person,options_class)
+ except Errors.ReportError, msg:
+ (m1,m2) = msg.messages()
+ ErrorDialog(m1,m2)
+ except Errors.FilterError, msg:
+ (m1,m2) = msg.messages()
+ ErrorDialog(m1,m2)
+ except:
+ log.error("Failed to write book item.", exc_info=True)
+ return None
+
+# Command-line generic task
+def cl_report(database,name,category,report_class,options_class,options_str_dict):
+
+ clr = CommandLineReport(database,name,category,options_class,options_str_dict)
+
+ # Exit here if show option was given
+ if clr.show:
+ return
+
+ # write report
+ try:
+ clr.option_class.handler.doc = clr.format(
+ clr.selected_style,clr.paper,clr.template_name,clr.orien)
+ MyReport = report_class(database, clr.person, clr.option_class)
+ MyReport.doc.init()
+ MyReport.begin_report()
+ MyReport.write_report()
+ MyReport.end_report()
+ except:
+ log.error("Failed to write report.", exc_info=True)
diff --git a/gramps2/src/PluginUtils/_ReportOptions.py b/gramps2/src/PluginUtils/_ReportOptions.py
new file mode 100644
index 000000000..b3f84fa72
--- /dev/null
+++ b/gramps2/src/PluginUtils/_ReportOptions.py
@@ -0,0 +1,600 @@
+#
+# Gramps - a GTK+/GNOME based genealogy program
+#
+# Copyright (C) 2004-2006 Donald N. Allingham
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+# $Id$
+
+# Written by Alex Roitman
+
+"""
+Report option handling, including saving and parsing.
+"""
+
+#-------------------------------------------------------------------------
+#
+# Standard Python modules
+#
+#-------------------------------------------------------------------------
+from gettext import gettext as _
+
+#-------------------------------------------------------------------------
+#
+# SAX interface
+#
+#-------------------------------------------------------------------------
+try:
+ from xml.sax import make_parser,handler,SAXParseException
+except:
+ from _xmlplus.sax import make_parser,handler,SAXParseException
+
+#-------------------------------------------------------------------------
+#
+# gramps modules
+#
+#-------------------------------------------------------------------------
+import const
+import Config
+import Utils
+import BaseDoc
+from _Options import *
+
+#-------------------------------------------------------------------------
+#
+# List of options for a single report
+#
+#-------------------------------------------------------------------------
+class OptionList(OptionList):
+ """
+ Implements a set of options to parse and store for a given report.
+ """
+
+ def __init__(self):
+ OptionList.__init__(self)
+ self.style_name = None
+ self.paper_name = None
+ self.orientation = None
+ self.template_name = None
+ self.format_name = None
+
+ def set_style_name(self,style_name):
+ """
+ Sets the style name for the OptionList.
+ @param style_name: name of the style to set.
+ @type style_name: str
+ """
+ self.style_name = style_name
+
+ def get_style_name(self):
+ """
+ Returns the style name of the OptionList.
+ @returns: string representing the style name
+ @rtype: str
+ """
+ return self.style_name
+
+ def set_paper_name(self,paper_name):
+ """
+ Sets the paper name for the OptionList.
+ @param paper_name: name of the paper to set.
+ @type paper_name: str
+ """
+ self.paper_name = paper_name
+
+ def get_paper_name(self):
+ """
+ Returns the paper name of the OptionList.
+ @returns: returns the paper name
+ @rtype: str
+ """
+ return self.paper_name
+
+ def set_orientation(self,orientation):
+ """
+ Sets the orientation for the OptionList.
+ @param orientation: orientation to set. Possible values are
+ BaseDoc.PAPER_LANDSCAPE or BaseDoc.PAPER_PORTRAIT
+ @type orientation: int
+ """
+ self.orientation = orientation
+
+ def get_orientation(self):
+ """
+ Returns the orientation for the OptionList.
+ @returns: returns the selected orientation. Valid values are
+ BaseDoc.PAPER_LANDSCAPE or BaseDoc.PAPER_PORTRAIT
+ @rtype: int
+ """
+ return self.orientation
+
+ def set_template_name(self,template_name):
+ """
+ Sets the template name for the OptionList.
+ @param template_name: name of the template to set.
+ @type template_name: str
+ """
+ self.template_name = template_name
+
+ def get_template_name(self):
+ """
+ Returns the template name of the OptionList.
+ @returns: template name
+ @rtype: str
+ """
+ return self.template_name
+
+ def set_format_name(self,format_name):
+ """
+ Sets the format name for the OptionList.
+ @param format_name: name of the format to set.
+ @type format_name: str
+ """
+ self.format_name = format_name
+
+ def get_format_name(self):
+ """
+ Returns the format name of the OptionList.
+ @returns: returns the format name
+ @rtype: str
+ """
+ return self.format_name
+
+#-------------------------------------------------------------------------
+#
+# Collection of option lists
+#
+#-------------------------------------------------------------------------
+class OptionListCollection(OptionListCollection):
+ """
+ Implements a collection of option lists.
+ """
+ def __init__(self,filename):
+ OptionListCollection.__init__(self,filename)
+
+ def init_common(self):
+ # Default values for common options
+ self.default_style_name = "default"
+ self.default_paper_name = Config.get_paper_preference()
+ self.default_template_name = ""
+ self.default_orientation = BaseDoc.PAPER_PORTRAIT
+ self.default_format_name = 'print'
+
+ self.last_paper_name = self.default_paper_name
+ self.last_orientation = self.default_orientation
+ self.last_template_name = self.default_template_name
+ self.last_format_name = self.default_format_name
+ self.option_list_map = {}
+
+ def set_last_paper_name(self,paper_name):
+ """
+ Sets the last paper name used for the any report in this collection.
+ @param paper_name: name of the paper to set.
+ @type paper_name: str
+ """
+ self.last_paper_name = paper_name
+
+ def get_last_paper_name(self):
+ """
+ Returns the last paper name used for the any report in this collection.
+ @returns: returns the name of the paper
+ @rtype: str
+ """
+ return self.last_paper_name
+
+ def set_last_orientation(self,orientation):
+ """
+ Sets the last orientation used for the any report in this collection.
+ @param orientation: orientation to set.
+ @type orientation: int
+ """
+ self.last_orientation = orientation
+
+ def get_last_orientation(self):
+ """
+ Returns the last orientation used for the any report in this
+ collection.
+ @returns: last orientation used
+ @rtype: int
+ """
+ return self.last_orientation
+
+ def set_last_template_name(self,template_name):
+ """
+ Sets the last template used for the any report in this collection.
+
+ template_name: name of the style to set.
+ """
+ self.last_template_name = template_name
+
+ def get_last_template_name(self):
+ """
+ Returns the last template used for the any report in this collection.
+ """
+ return self.last_template_name
+
+ def set_last_format_name(self,format_name):
+ """
+ Sets the last format used for the any report in this collection.
+
+ format_name: name of the format to set.
+ """
+ self.last_format_name = format_name
+
+ def get_last_format_name(self):
+ """
+ Returns the last format used for the any report in this collection.
+ """
+ return self.last_format_name
+
+ def write_common(self,f):
+ f.write('\n')
+ if self.get_last_paper_name() != self.default_paper_name:
+ f.write(' \n' % self.get_last_paper_name() )
+ if self.get_last_template_name() != self.default_template_name:
+ f.write(' \n' % self.get_last_template_name() )
+ if self.get_last_format_name() != self.default_format_name:
+ f.write(' \n' % self.get_last_format_name() )
+ if self.get_last_orientation() != self.default_orientation:
+ f.write(' \n' % self.get_last_orientation() )
+ f.write('\n')
+
+ def write_module_common(self,f,option_list):
+ if option_list.get_style_name() \
+ and option_list.get_style_name() != self.default_style_name:
+ f.write(' \n' % option_list.get_style_name() )
+ if option_list.get_paper_name() \
+ and option_list.get_paper_name() != self.default_paper_name:
+ f.write(' \n' % option_list.get_paper_name() )
+ if option_list.get_template_name() \
+ and option_list.get_template_name() != self.default_template_name:
+ f.write(' \n' % option_list.get_template_name() )
+ if option_list.get_format_name() \
+ and option_list.get_format_name() != self.default_format_name:
+ f.write(' \n' % option_list.get_format_name() )
+ if option_list.get_orientation() \
+ and option_list.get_orientation() != self.default_orientation:
+ f.write(' \n' % option_list.get_orientation() )
+
+ def parse(self):
+ """
+ Loads the OptionList from the associated file, if it exists.
+ """
+ try:
+ p = make_parser()
+ p.setContentHandler(OptionParser(self))
+ p.parse('file://' + self.filename)
+ except (IOError,OSError,SAXParseException):
+ pass
+
+#-------------------------------------------------------------------------
+#
+# OptionParser
+#
+#-------------------------------------------------------------------------
+class OptionParser(OptionParser):
+ """
+ SAX parsing class for the OptionListCollection XML file.
+ """
+
+ def __init__(self,collection):
+ """
+ Creates a OptionParser class that populates the passed collection.
+
+ collection: BookList to be loaded from the file.
+ """
+ OptionParser.__init__(self,collection)
+ self.common = False
+ self.list_class = OptionList
+
+ def startElement(self,tag,attrs):
+ """
+ Overridden class that handles the start of a XML element
+ """
+ # First we try report-specific tags
+ if tag == "last-common":
+ self.common = True
+ elif tag == "style":
+ self.option_list.set_style_name(attrs['name'])
+ elif tag == "paper":
+ if self.common:
+ self.collection.set_last_paper_name(attrs['name'])
+ else:
+ self.option_list.set_paper_name(attrs['name'])
+ elif tag == "template":
+ if self.common:
+ self.collection.set_last_template_name(attrs['name'])
+ else:
+ self.option_list.set_template_name(attrs['name'])
+ elif tag == "format":
+ if self.common:
+ self.collection.set_last_format_name(attrs['name'])
+ else:
+ self.option_list.set_format_name(attrs['name'])
+ elif tag == "orientation":
+ if self.common:
+ self.collection.set_last_orientation(int(attrs['value']))
+ else:
+ self.option_list.set_orientation(int(attrs['value']))
+ else:
+ # Tag is not report-specific, so we let the base class handle it.
+ OptionParser.startElement(self,tag,attrs)
+
+ def endElement(self,tag):
+ "Overridden class that handles the end of a XML element"
+ # First we try report-specific tags
+ if tag == "last-common":
+ self.common = False
+ else:
+ # Tag is not report-specific, so we let the base class handle it.
+ OptionParser.endElement(self,tag)
+
+#-------------------------------------------------------------------------
+#
+# Class handling options for plugins
+#
+#-------------------------------------------------------------------------
+class OptionHandler(OptionHandler):
+ """
+ Implements handling of the options for the plugins.
+ """
+ def __init__(self,module_name,options_dict,person_id=None):
+ OptionHandler.__init__(self,module_name,options_dict,person_id)
+
+ def init_subclass(self):
+ self.collection_class = OptionListCollection
+ self.list_class = OptionList
+ self.filename = const.report_options
+
+ def init_common(self):
+ """
+ Specific initialization for reports.
+ """
+ # These are needed for running reports.
+ # We will not need to save/retreive them, just keep around.
+ self.doc = None
+ self.output = None
+ self.newpage = False
+
+ # Retrieve our options from whole collection
+ self.style_name = self.option_list_collection.default_style_name
+ self.paper_name = self.option_list_collection.get_last_paper_name()
+ self.orientation = self.option_list_collection.get_last_orientation()
+ self.template_name = self.option_list_collection.get_last_template_name()
+ self.format_name = self.option_list_collection.get_last_format_name()
+
+ def set_common_options(self):
+ if self.saved_option_list.get_style_name():
+ self.style_name = self.saved_option_list.get_style_name()
+ if self.saved_option_list.get_orientation():
+ self.orientation = self.saved_option_list.get_orientation()
+ if self.saved_option_list.get_template_name():
+ self.template_name = self.saved_option_list.get_template_name()
+ if self.saved_option_list.get_paper_name():
+ self.paper_name = self.saved_option_list.get_paper_name()
+ if self.saved_option_list.get_format_name():
+ self.format_name = self.saved_option_list.get_format_name()
+
+ def save_common_options(self):
+ # First we save common options
+ self.saved_option_list.set_style_name(self.style_name)
+ self.saved_option_list.set_orientation(self.orientation)
+ self.saved_option_list.set_template_name(self.template_name)
+ self.saved_option_list.set_paper_name(self.paper_name)
+ self.saved_option_list.set_format_name(self.format_name)
+ self.option_list_collection.set_option_list(self.module_name,
+ self.saved_option_list)
+
+ # Then save last-common options from the current selection
+ self.option_list_collection.set_last_orientation(self.orientation)
+ self.option_list_collection.set_last_template_name(self.template_name)
+ self.option_list_collection.set_last_paper_name(self.paper_name)
+ self.option_list_collection.set_last_format_name(self.format_name)
+
+ def get_report_generations(self):
+ if self.default_options_dict.has_key('gen'):
+ max_gen = self.options_dict.get('gen',
+ self.default_options_dict['gen'])
+ page_breaks = self.options_dict.get('pagebbg',
+ self.default_options_dict['pagebbg'])
+ return (max_gen,page_breaks)
+ else:
+ return (0,0)
+
+ def set_report_generations(self,max_gen,page_breaks):
+ self.options_dict['gen'] = max_gen
+ self.options_dict['pagebbg'] = page_breaks
+
+ def get_stylesheet_savefile(self):
+ """Where to save user defined styles for this report."""
+ return "%s.xml" % self.module_name
+
+ def get_default_stylesheet_name(self):
+ return self.style_name
+
+ def set_default_stylesheet_name(self,style_name):
+ self.style_name = style_name
+
+ def get_display_format(self):
+ if self.default_options_dict.has_key('dispf'):
+ return self.options_dict.get('dispf',
+ self.default_options_dict['dispf'])
+ else:
+ return []
+
+ def set_display_format(self,val):
+ if type(val) != list:
+ val = val.split('\n')
+ self.options_dict['dispf'] = val
+
+ def get_format_name(self):
+ return self.format_name
+
+ def set_format_name(self,format_name):
+ self.format_name = format_name
+
+ def get_paper_name(self):
+ return self.paper_name
+
+ def set_paper_name(self,paper_name):
+ self.paper_name = paper_name
+
+ def get_paper(self):
+ """
+ This method is for temporary storage, not for saving/restoring.
+ """
+ return self.paper
+
+ def set_paper(self,paper):
+ """
+ This method is for temporary storage, not for saving/restoring.
+ """
+ self.paper = paper
+
+ def get_template_name(self):
+ return self.template_name
+
+ def set_template_name(self,template_name):
+ self.template_name = template_name
+
+ def get_orientation(self):
+ return self.orientation
+
+ def set_orientation(self,orientation):
+ self.orientation = orientation
+
+#------------------------------------------------------------------------
+#
+# Base Options class
+#
+#------------------------------------------------------------------------
+class ReportOptions(Options):
+
+ """
+ Defines options and provides handling interface.
+
+ This is a base Options class for the reports. All reports' options
+ classes should derive from it.
+ """
+
+ def __init__(self,name,person_id=None):
+ """
+ Initializes the class, performing usual house-keeping tasks.
+ Subclasses MUST call this in their __init__() method.
+ """
+
+ self.set_new_options()
+ self.enable_options()
+
+ if self.enable_dict:
+ self.options_dict.update(self.enable_dict)
+ self.handler = OptionHandler(name,self.options_dict,person_id)
+
+ def make_default_style(self,default_style):
+ """
+ Defines default style for this report.
+
+ This method MUST be overridden by reports that use the
+ user-adjustable paragraph styles.
+
+ NOTE: Unique names MUST be used for all style names, otherwise the
+ styles will collide when making a book with duplicate style
+ names. A rule of safety is to prepend style name with the
+ acronym based on report name. The following acronyms are
+ already taken:
+ AC- Ancestor Chart
+ AC2- Ancestor Chart 2 (Wall Chart)
+ AHN- Ahnentafel Report
+ AR- Comprehensive Ancestors report
+ CBT- Custom Book Text
+ DG- Descendant Graph
+ DR- Descendant Report
+ DAR- Detailed Ancestral Report
+ DDR- Detailed Descendant Report
+ FGR- Family Group Report
+ FC- Fan Chart
+ FTA- FTM Style Ancestral report
+ FTD- FTM Style Descendant report
+ IDS- Individual Complete Report
+ IVS- Individual Summary Report
+ SBT- Simple Boot Title
+ TLG- Timeline Graph
+ """
+ pass
+
+ def get_document(self):
+ """
+ Return document instance.
+
+ This method MUST NOT be overridden by subclasses.
+ """
+ return self.handler.doc
+
+ def set_document(self,val):
+ """
+ Set document to a given instance.
+
+ This method MUST NOT be overridden by subclasses.
+ """
+ self.handler.doc = val
+
+ def get_output(self):
+ """
+ Return document output destination.
+
+ This method MUST NOT be overridden by subclasses.
+ """
+ return self.handler.output
+
+ def set_output(self,val):
+ """
+ Set output destination to a given string.
+
+ This method MUST NOT be overridden by subclasses.
+ """
+ self.handler.output = val
+
+ def get_newpage(self):
+ """
+ Return value of whether or not insert new page before the report.
+
+ This method MUST NOT be overridden by subclasses.
+ """
+ return self.handler.newpage
+
+ def set_newpage(self,val):
+ """
+ Set newpage to a given value.
+
+ This method MUST NOT be overridden by subclasses.
+ """
+ self.handler.newpage = val
+
+ def get_report_generations(self):
+ """
+ Return (max_generations,page_breaks) tuple.
+
+ This method MUST NOT be overridden by subclasses.
+ """
+ return self.handler.get_report_generations()
+
+ def get_display_format(self):
+ """
+ Return display format for the option box of graphical report.
+
+ This method MUST NOT be overridden by subclasses.
+ """
+ return self.handler.get_display_format()
diff --git a/gramps2/src/PluginUtils/_ReportUtils.py b/gramps2/src/PluginUtils/_ReportUtils.py
new file mode 100644
index 000000000..0ba68da45
--- /dev/null
+++ b/gramps2/src/PluginUtils/_ReportUtils.py
@@ -0,0 +1,2199 @@
+#
+# Gramps - a GTK+/GNOME based genealogy program
+#
+# Copyright (C) 2000-2006 Donald N. Allingham
+#
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+# $Id$
+
+"""
+A collection of utilities to aid in the generation of reports.
+"""
+
+#------------------------------------------------------------------------
+#
+# GRAMPS modules
+#
+#------------------------------------------------------------------------
+import DateHandler
+import RelLib
+from NameDisplay import displayer as _nd
+import DateHandler
+from QuestionDialog import WarningDialog
+import time
+import os
+from gettext import gettext as _
+
+#------------------------------------------------------------------------
+#
+# Born strings
+#
+#------------------------------------------------------------------------
+
+born_full_date_with_place = [
+ {
+ RelLib.Person.UNKNOWN : _("This person was born on %(birth_date)s in %(birth_place)s."),
+ RelLib.Person.MALE : _("He was born on %(birth_date)s in %(birth_place)s."),
+ RelLib.Person.FEMALE : _("She was born on %(birth_date)s in %(birth_place)s."),
+ },
+ {
+ RelLib.Person.UNKNOWN : _("%(unknown_gender_name)s was born on %(birth_date)s in %(birth_place)s."),
+ RelLib.Person.MALE : _("%(male_name)s was born on %(birth_date)s in %(birth_place)s."),
+ RelLib.Person.FEMALE : _("%(female_name)s was born on %(birth_date)s in %(birth_place)s."),
+ },
+]
+
+born_modified_date_with_place = [
+ {
+ RelLib.Person.UNKNOWN : _("This person was born %(modified_date)s in %(birth_place)s."),
+ RelLib.Person.MALE : _("He was born %(modified_date)s in %(birth_place)s."),
+ RelLib.Person.FEMALE : _("She was born %(modified_date)s in %(birth_place)s."),
+ },
+ {
+ RelLib.Person.UNKNOWN : _("%(unknown_gender_name)s was born %(modified_date)s in %(birth_place)s."),
+ RelLib.Person.MALE : _("%(male_name)s was born %(modified_date)s in %(birth_place)s."),
+ RelLib.Person.FEMALE : _("%(female_name)s was born %(modified_date)s in %(birth_place)s."),
+ },
+]
+
+born_full_date_no_place = [
+ {
+ RelLib.Person.UNKNOWN : _("This person was born on %(birth_date)s."),
+ RelLib.Person.MALE : _("He was born on %(birth_date)s."),
+ RelLib.Person.FEMALE : _("She was born on %(birth_date)s."),
+ },
+ {
+ RelLib.Person.UNKNOWN : _("%(unknown_gender_name)s was born on %(birth_date)s."),
+ RelLib.Person.MALE : _("%(male_name)s was born on %(birth_date)s."),
+ RelLib.Person.FEMALE : _("%(female_name)s was born on %(birth_date)s."),
+ },
+]
+
+born_modified_date_no_place = [
+ {
+ RelLib.Person.UNKNOWN : _("This person was born %(modified_date)s."),
+ RelLib.Person.MALE : _("He was born %(modified_date)s."),
+ RelLib.Person.FEMALE : _("She was born %(modified_date)s."),
+ },
+ {
+ RelLib.Person.UNKNOWN : _("%(unknown_gender_name)s was born on %(modified_date)s."),
+ RelLib.Person.MALE : _("%(male_name)s was born on %(modified_date)s."),
+ RelLib.Person.FEMALE : _("%(female_name)s was born on %(modified_date)s."),
+ },
+]
+
+born_partial_date_with_place = [
+ {
+ RelLib.Person.UNKNOWN : _("This person was born in %(month_year)s in %(birth_place)s."),
+ RelLib.Person.MALE : _("He was born in %(month_year)s in %(birth_place)s."),
+ RelLib.Person.FEMALE : _("She was born in %(month_year)s in %(birth_place)s."),
+ },
+ {
+ RelLib.Person.UNKNOWN : _("%(unknown_gender_name)s was born in %(month_year)s in %(birth_place)s."),
+ RelLib.Person.MALE : _("%(male_name)s was born in %(month_year)s in %(birth_place)s."),
+ RelLib.Person.FEMALE : _("%(female_name)s was born in %(month_year)s in %(birth_place)s."),
+ },
+]
+
+born_partial_date_no_place = [
+ {
+ RelLib.Person.UNKNOWN : _("This person was born in %(month_year)s."),
+ RelLib.Person.MALE : _("He was born in %(month_year)s."),
+ RelLib.Person.FEMALE : _("She was born in %(month_year)s."),
+ },
+ {
+ RelLib.Person.UNKNOWN : _("%(unknown_gender_name)s was born in %(month_year)s."),
+ RelLib.Person.MALE : _("%(male_name)s was born in %(month_year)s."),
+ RelLib.Person.FEMALE : _("%(female_name)s was born in %(month_year)s."),
+ }
+]
+
+born_no_date_with_place = [
+ {
+ RelLib.Person.UNKNOWN : _("This person was born in %(birth_place)s."),
+ RelLib.Person.MALE : _("He was born in %(birth_place)s."),
+ RelLib.Person.FEMALE : _("She was born in %(birth_place)s."),
+ },
+ {
+ RelLib.Person.UNKNOWN : _("%(unknown_gender_name)s was born in %(birth_place)s."),
+ RelLib.Person.MALE : _("%(male_name)s was born in %(birth_place)s."),
+ RelLib.Person.FEMALE : _("%(female_name)s was born in %(birth_place)s."),
+ }
+]
+
+#------------------------------------------------------------------------
+#
+# Died strings
+#
+#------------------------------------------------------------------------
+
+died_full_date_with_place = [
+ { RelLib.Person.UNKNOWN : [
+ _("This person died on %(death_date)s in %(death_place)s."),
+ _("This person died on %(death_date)s in %(death_place)s at the age of %(age)d years."),
+ _("This person died on %(death_date)s in %(death_place)s at the age of %(age)d months."),
+ _("This person died on %(death_date)s in %(death_place)s at the age of %(age)d days."),
+ ],
+ RelLib.Person.MALE : [
+ _("He died on %(death_date)s in %(death_place)s."),
+ _("He died on %(death_date)s in %(death_place)s at the age of %(age)d years."),
+ _("He died on %(death_date)s in %(death_place)s at the age of %(age)d months."),
+ _("He died on %(death_date)s in %(death_place)s at the age of %(age)d days."),
+ ],
+ RelLib.Person.FEMALE : [
+ _("She died on %(death_date)s in %(death_place)s."),
+ _("She died on %(death_date)s in %(death_place)s at the age of %(age)d years."),
+ _("She died on %(death_date)s in %(death_place)s at the age of %(age)d months."),
+ _("She died on %(death_date)s in %(death_place)s at the age of %(age)d days."),
+ ],
+ },
+ { RelLib.Person.UNKNOWN : [
+ _("%(unknown_gender_name)s died on %(death_date)s in %(death_place)s."),
+ _("%(unknown_gender_name)s died on %(death_date)s in %(death_place)s at the age of %(age)d years."),
+ _("%(unknown_gender_name)s died on %(death_date)s in %(death_place)s at the age of %(age)d months."),
+ _("%(unknown_gender_name)s died on %(death_date)s in %(death_place)s at the age of %(age)d days."),
+ ],
+ RelLib.Person.MALE : [
+ _("%(male_name)s died on %(death_date)s in %(death_place)s."),
+ _("%(male_name)s died on %(death_date)s in %(death_place)s at the age of %(age)d years."),
+ _("%(male_name)s died on %(death_date)s in %(death_place)s at the age of %(age)d months."),
+ _("%(male_name)s died on %(death_date)s in %(death_place)s at the age of %(age)d days."),
+ ],
+ RelLib.Person.FEMALE : [
+ _("%(female_name)s died on %(death_date)s in %(death_place)s."),
+ _("%(female_name)s died on %(death_date)s in %(death_place)s at the age of %(age)d years."),
+ _("%(female_name)s died on %(death_date)s in %(death_place)s at the age of %(age)d months."),
+ _("%(female_name)s died on %(death_date)s in %(death_place)s at the age of %(age)d days."),
+ ],
+ },
+]
+
+died_modified_date_with_place = [
+ { RelLib.Person.UNKNOWN : [
+ _("This person died %(death_date)s in %(death_place)s."),
+ _("This person died %(death_date)s in %(death_place)s at the age of %(age)d years."),
+ _("This person died %(death_date)s in %(death_place)s at the age of %(age)d months."),
+ _("This person died %(death_date)s in %(death_place)s at the age of %(age)d days."),
+ ],
+ RelLib.Person.MALE : [
+ _("He died %(death_date)s in %(death_place)s."),
+ _("He died %(death_date)s in %(death_place)s at the age of %(age)d years."),
+ _("He died %(death_date)s in %(death_place)s at the age of %(age)d months."),
+ _("He died %(death_date)s in %(death_place)s at the age of %(age)d days."),
+ ],
+ RelLib.Person.FEMALE : [
+ _("She died %(death_date)s in %(death_place)s."),
+ _("She died %(death_date)s in %(death_place)s at the age of %(age)d years."),
+ _("She died %(death_date)s in %(death_place)s at the age of %(age)d months."),
+ _("She died %(death_date)s in %(death_place)s at the age of %(age)d days."),
+ ],
+ },
+ { RelLib.Person.UNKNOWN : [
+ _("%(unknown_gender_name)s died %(death_date)s in %(death_place)s."),
+ _("%(unknown_gender_name)s died %(death_date)s in %(death_place)s at the age of %(age)d years."),
+ _("%(unknown_gender_name)s died %(death_date)s in %(death_place)s at the age of %(age)d months."),
+ _("%(unknown_gender_name)s died %(death_date)s in %(death_place)s at the age of %(age)d days."),
+ ],
+ RelLib.Person.MALE : [
+ _("%(male_name)s died %(death_date)s in %(death_place)s."),
+ _("%(male_name)s died %(death_date)s in %(death_place)s at the age of %(age)d years."),
+ _("%(male_name)s died %(death_date)s in %(death_place)s at the age of %(age)d months."),
+ _("%(male_name)s died %(death_date)s in %(death_place)s at the age of %(age)d days."),
+ ],
+ RelLib.Person.FEMALE : [
+ _("%(female_name)s died %(death_date)s in %(death_place)s."),
+ _("%(female_name)s died %(death_date)s in %(death_place)s at the age of %(age)d years."),
+ _("%(female_name)s died %(death_date)s in %(death_place)s at the age of %(age)d months."),
+ _("%(female_name)s died %(death_date)s in %(death_place)s at the age of %(age)d days."),
+ ],
+ },
+]
+
+died_full_date_no_place = [
+ { RelLib.Person.UNKNOWN : [
+ _("This person died on %(death_date)s."),
+ _("This person died on %(death_date)s at the age of %(age)d years."),
+ _("This person died on %(death_date)s at the age of %(age)d months."),
+ _("This person died on %(death_date)s at the age of %(age)d days."),
+ ],
+ RelLib.Person.MALE : [
+ _("He died on %(death_date)s."),
+ _("He died on %(death_date)s at the age of %(age)d years."),
+ _("He died on %(death_date)s at the age of %(age)d months."),
+ _("He died on %(death_date)s at the age of %(age)d days."),
+ ],
+ RelLib.Person.FEMALE : [
+ _("She died on %(death_date)s."),
+ _("She died on %(death_date)s at the age of %(age)d years."),
+ _("She died on %(death_date)s at the age of %(age)d months."),
+ _("She died on %(death_date)s at the age of %(age)d days."),
+ ],
+ },
+ { RelLib.Person.UNKNOWN : [
+ _("%(unknown_gender_name)s died on %(death_date)s."),
+ _("%(unknown_gender_name)s died on %(death_date)s at the age of %(age)d years."),
+ _("%(unknown_gender_name)s died on %(death_date)s at the age of %(age)d months."),
+ _("%(unknown_gender_name)s died on %(death_date)s at the age of %(age)d days."),
+ ],
+ RelLib.Person.MALE : [
+ _("%(male_name)s died on %(death_date)s."),
+ _("%(male_name)s died on %(death_date)s at the age of %(age)d years."),
+ _("%(male_name)s died on %(death_date)s at the age of %(age)d months."),
+ _("%(male_name)s died on %(death_date)s at the age of %(age)d days."),
+ ],
+ RelLib.Person.FEMALE : [
+ _("%(female_name)s died on %(death_date)s."),
+ _("%(female_name)s died on %(death_date)s at the age of %(age)d years."),
+ _("%(female_name)s died on %(death_date)s at the age of %(age)d months."),
+ _("%(female_name)s died on %(death_date)s at the age of %(age)d days."),
+ ],
+ },
+]
+
+died_modified_date_no_place = [
+ { RelLib.Person.UNKNOWN : [
+ _("This person died %(death_date)s."),
+ _("This person died %(death_date)s at the age of %(age)d years."),
+ _("This person died %(death_date)s at the age of %(age)d months."),
+ _("This person died %(death_date)s at the age of %(age)d days."),
+ ],
+ RelLib.Person.MALE : [
+ _("He died %(death_date)s."),
+ _("He died %(death_date)s at the age of %(age)d years."),
+ _("He died %(death_date)s at the age of %(age)d months."),
+ _("He died %(death_date)s at the age of %(age)d days."),
+ ],
+ RelLib.Person.FEMALE : [
+ _("She died %(death_date)s."),
+ _("She died %(death_date)s at the age of %(age)d years."),
+ _("She died %(death_date)s at the age of %(age)d months."),
+ _("She died %(death_date)s at the age of %(age)d days."),
+ ],
+ },
+ { RelLib.Person.UNKNOWN : [
+ _("%(unknown_gender_name)s died %(death_date)s."),
+ _("%(unknown_gender_name)s died %(death_date)s at the age of %(age)d years."),
+ _("%(unknown_gender_name)s died %(death_date)s at the age of %(age)d months."),
+ _("%(unknown_gender_name)s died %(death_date)s at the age of %(age)d days."),
+ ],
+ RelLib.Person.MALE : [
+ _("%(male_name)s died %(death_date)s."),
+ _("%(male_name)s died %(death_date)s at the age of %(age)d years."),
+ _("%(male_name)s died %(death_date)s at the age of %(age)d months."),
+ _("%(male_name)s died %(death_date)s at the age of %(age)d days."),
+ ],
+ RelLib.Person.FEMALE : [
+ _("%(female_name)s died %(death_date)s."),
+ _("%(female_name)s died %(death_date)s at the age of %(age)d years."),
+ _("%(female_name)s died %(death_date)s at the age of %(age)d months."),
+ _("%(female_name)s died %(death_date)s at the age of %(age)d days."),
+ ],
+ },
+]
+
+died_partial_date_with_place = [
+ { RelLib.Person.UNKNOWN : [
+ _("This person died in %(month_year)s in %(death_place)s."),
+ _("This person died in %(month_year)s in %(death_place)s at the age of %(age)d years."),
+ _("This person died in %(month_year)s in %(death_place)s at the age of %(age)d months."),
+ _("This person died in %(month_year)s in %(death_place)s at the age of %(age)d days."),
+ ],
+ RelLib.Person.MALE : [
+ _("He died in %(month_year)s in %(death_place)s."),
+ _("He died in %(month_year)s in %(death_place)s at the age of %(age)d years."),
+ _("He died in %(month_year)s in %(death_place)s at the age of %(age)d months."),
+ _("He died in %(month_year)s in %(death_place)s at the age of %(age)d days."),
+ ],
+ RelLib.Person.FEMALE : [
+ _("She died in %(month_year)s in %(death_place)s."),
+ _("She died in %(month_year)s in %(death_place)s at the age of %(age)d years."),
+ _("She died in %(month_year)s in %(death_place)s at the age of %(age)d months."),
+ _("She died in %(month_year)s in %(death_place)s at the age of %(age)d days."),
+ ]
+ },
+ { RelLib.Person.UNKNOWN : [
+ _("%(unknown_gender_name)s died in %(month_year)s in %(death_place)s."),
+ _("%(unknown_gender_name)s died in %(month_year)s in %(death_place)s at the age of %(age)d years."),
+ _("%(unknown_gender_name)s died in %(month_year)s in %(death_place)s at the age of %(age)d months."),
+ _("%(unknown_gender_name)s died in %(month_year)s in %(death_place)s at the age of %(age)d days."),
+ ],
+ RelLib.Person.MALE : [
+ _("%(male_name)s died in %(month_year)s in %(death_place)s."),
+ _("%(male_name)s died in %(month_year)s in %(death_place)s at the age of %(age)d years."),
+ _("%(male_name)s died in %(month_year)s in %(death_place)s at the age of %(age)d months."),
+ _("%(male_name)s died in %(month_year)s in %(death_place)s at the age of %(age)d days."),
+ ],
+ RelLib.Person.FEMALE : [
+ _("%(female_name)s died in %(month_year)s in %(death_place)s."),
+ _("%(female_name)s died in %(month_year)s in %(death_place)s at the age of %(age)d years."),
+ _("%(female_name)s died in %(month_year)s in %(death_place)s at the age of %(age)d months."),
+ _("%(female_name)s died in %(month_year)s in %(death_place)s at the age of %(age)d days."),
+ ],
+ },
+]
+
+died_partial_date_no_place = [
+ { RelLib.Person.UNKNOWN : [
+ _("This person died in %(month_year)s."),
+ _("This person died in %(month_year)s at the age of %(age)d years."),
+ _("This person died in %(month_year)s at the age of %(age)d months."),
+ _("This person died in %(month_year)s at the age of %(age)d days."),
+ ],
+ RelLib.Person.MALE : [
+ _("He died in %(month_year)s."),
+ _("He died in %(month_year)s at the age of %(age)d years."),
+ _("He died in %(month_year)s at the age of %(age)d months."),
+ _("He died in %(month_year)s at the age of %(age)d days."),
+ ],
+ RelLib.Person.FEMALE : [
+ _("She died in %(month_year)s."),
+ _("She died in %(month_year)s at the age of %(age)d years."),
+ _("She died in %(month_year)s at the age of %(age)d months."),
+ _("She died in %(month_year)s at the age of %(age)d days."),
+ ],
+ },
+ { RelLib.Person.UNKNOWN : [
+ _("%(unknown_gender_name)s died in %(month_year)s."),
+ _("%(unknown_gender_name)s died in %(month_year)s at the age of %(age)d years."),
+ _("%(unknown_gender_name)s died in %(month_year)s at the age of %(age)d months."),
+ _("%(unknown_gender_name)s died in %(month_year)s at the age of %(age)d days."),
+ ],
+ RelLib.Person.MALE : [
+ _("%(male_name)s died in %(month_year)s."),
+ _("%(male_name)s died in %(month_year)s at the age of %(age)d years."),
+ _("%(male_name)s died in %(month_year)s at the age of %(age)d months."),
+ _("%(male_name)s died in %(month_year)s at the age of %(age)d days."),
+ ],
+ RelLib.Person.FEMALE : [
+ _("%(female_name)s died in %(month_year)s."),
+ _("%(female_name)s died in %(month_year)s at the age of %(age)d years."),
+ _("%(female_name)s died in %(month_year)s at the age of %(age)d months."),
+ _("%(female_name)s died in %(month_year)s at the age of %(age)d days."),
+ ],
+ }
+]
+
+died_no_date_with_place = [
+ {
+ RelLib.Person.UNKNOWN : [
+ _("This person died in %(death_place)s."),
+ _("This person died in %(death_place)s at the age of %(age)d years."),
+ _("This person died in %(death_place)s at the age of %(age)d months."),
+ _("This person died in %(death_place)s at the age of %(age)d days."),
+ ],
+ RelLib.Person.MALE : [
+ _("He died in %(death_place)s."),
+ _("He died in %(death_place)s at the age of %(age)d years."),
+ _("He died in %(death_place)s at the age of %(age)d months."),
+ _("He died in %(death_place)s at the age of %(age)d days."),
+ ],
+ RelLib.Person.FEMALE : [
+ _("She died in %(death_place)s."),
+ _("She died in %(death_place)s at the age of %(age)d years."),
+ _("She died in %(death_place)s at the age of %(age)d months."),
+ _("She died in %(death_place)s at the age of %(age)d days."),
+ ],
+ },
+ { RelLib.Person.UNKNOWN : [
+ _("%(unknown_gender_name)s died in %(death_place)s."),
+ _("%(unknown_gender_name)s died in %(death_place)s at the age of %(age)d years."),
+ _("%(unknown_gender_name)s died in %(death_place)s at the age of %(age)d months."),
+ _("%(unknown_gender_name)s died in %(death_place)s at the age of %(age)d days."),
+ ],
+ RelLib.Person.MALE : [
+ _("%(male_name)s died in %(death_place)s."),
+ _("%(male_name)s died in %(death_place)s at the age of %(age)d years."),
+ _("%(male_name)s died in %(death_place)s at the age of %(age)d months."),
+ _("%(male_name)s died in %(death_place)s at the age of %(age)d days."),
+ ],
+ RelLib.Person.FEMALE : [
+ _("%(female_name)s died in %(death_place)s."),
+ _("%(female_name)s died in %(death_place)s at the age of %(age)d years."),
+ _("%(female_name)s died in %(death_place)s at the age of %(age)d months."),
+ _("%(female_name)s died in %(death_place)s at the age of %(age)d days."),
+ ],
+ }
+]
+
+died_no_date_no_place = [
+ { RelLib.Person.UNKNOWN : [
+ "",
+ _("This person died at the age of %(age)d years."),
+ _("This person died at the age of %(age)d months."),
+ _("This person died at the age of %(age)d days."),
+ ],
+ RelLib.Person.MALE : [
+ "",
+ _("He died at the age of %(age)d years."),
+ _("He died at the age of %(age)d months."),
+ _("He died at the age of %(age)d days."),
+ ],
+ RelLib.Person.FEMALE : [
+ "",
+ _("She died at the age of %(age)d years."),
+ _("She died at the age of %(age)d months."),
+ _("She died at the age of %(age)d days."),
+ ],
+ },
+ { RelLib.Person.UNKNOWN : [
+ "",
+ _("%(unknown_gender_name)s died at the age of %(age)d years."),
+ _("%(unknown_gender_name)s died at the age of %(age)d months."),
+ _("%(unknown_gender_name)s died at the age of %(age)d days."),
+ ],
+ RelLib.Person.MALE : [
+ "",
+ _("%(male_name)s died at the age of %(age)d years."),
+ _("%(male_name)s died at the age of %(age)d months."),
+ _("%(male_name)s died at the age of %(age)d days."),
+ ],
+ RelLib.Person.FEMALE : [
+ "",
+ _("%(female_name)s died at the age of %(age)d years."),
+ _("%(female_name)s died at the age of %(age)d months."),
+ _("%(female_name)s died at the age of %(age)d days."),
+ ],
+ },
+]
+
+#------------------------------------------------------------------------
+#
+# Buried strings
+#
+#------------------------------------------------------------------------
+
+buried_full_date_place = {
+ RelLib.Person.MALE: [
+ _("%(male_name)s was buried on %(burial_date)s in %(burial_place)s."),
+ _("He was buried on %(burial_date)s in %(burial_place)s."),
+ ],
+ RelLib.Person.FEMALE: [
+ _("%(female_name)s was buried on %(burial_date)s in %(burial_place)s."),
+ _("She was buried on %(burial_date)s in %(burial_place)s."),
+ ],
+ RelLib.Person.UNKNOWN: [
+ _("%(unknown_gender_name)s was buried on %(burial_date)s in %(burial_place)s."),
+ _("This person was buried on %(burial_date)s in %(burial_place)s."),
+ ],
+ }
+
+buried_full_date_no_place = {
+ RelLib.Person.MALE: [
+ _("%(male_name)s was buried on %(burial_date)s."),
+ _("He was buried on %(burial_date)s."),
+ ],
+ RelLib.Person.FEMALE: [
+ _("%(female_name)s was buried on %(burial_date)s."),
+ _("She was buried on %(burial_date)s."),
+ ],
+ RelLib.Person.UNKNOWN: [
+ _("%(unknown_gender_name)s was buried on %(burial_date)s."),
+ _("This person was buried on %(burial_date)s."),
+ ],
+ }
+
+buried_partial_date_place = {
+ RelLib.Person.MALE: [
+ _("%(male_name)s was buried in %(month_year)s in %(burial_place)s."),
+ _("He was buried in %(month_year)s in %(burial_place)s."),
+ ],
+ RelLib.Person.FEMALE: [
+ _("%(female_name)s was buried in %(month_year)s in %(burial_place)s."),
+ _("She was buried in %(month_year)s in %(burial_place)s."),
+ ],
+ RelLib.Person.UNKNOWN: [
+ _("%(unknown_gender_name)s was buried in %(month_year)s in %(burial_place)s."),
+ _("This person was buried in %(month_year)s in %(burial_place)s."),
+ ],
+ }
+
+buried_partial_date_no_place = {
+ RelLib.Person.MALE: [
+ _("%(male_name)s was buried in %(month_year)s."),
+ _("He was buried in %(month_year)s."),
+ ],
+ RelLib.Person.FEMALE: [
+ _("%(female_name)s was buried in %(month_year)s."),
+ _("She was buried in %(month_year)s."),
+ ],
+ RelLib.Person.UNKNOWN: [
+ _("%(unknown_gender_name)s was buried in %(month_year)s."),
+ _("This person was buried in %(month_year)s."),
+ ],
+ }
+
+buried_modified_date_place = {
+ RelLib.Person.MALE: [
+ _("%(male_name)s was buried %(modified_date)s in %(burial_place)s."),
+ _("He was buried %(modified_date)s in %(burial_place)s."),
+ ],
+ RelLib.Person.FEMALE: [
+ _("%(female_name)s was buried %(modified_date)s in %(burial_place)s."),
+ _("She was buried %(modified_date)s in %(burial_place)s."),
+ ],
+ RelLib.Person.UNKNOWN: [
+ _("%(unknown_gender_name)s was buried %(modified_date)s in %(burial_place)s."),
+ _("This person was buried %(modified_date)s in %(burial_place)s."),
+ ],
+ }
+
+buried_modified_date_no_place = {
+ RelLib.Person.MALE: [
+ _("%(male_name)s was buried %(modified_date)s."),
+ _("He was buried %(modified_date)s."),
+ ],
+ RelLib.Person.FEMALE: [
+ _("%(female_name)s was buried %(modified_date)s."),
+ _("She was buried %(modified_date)s."),
+ ],
+ RelLib.Person.UNKNOWN: [
+ _("%(unknown_gender_name)s was buried %(modified_date)s."),
+ _("This person was buried %(modified_date)s."),
+ ],
+ }
+
+buried_no_date_place = {
+ RelLib.Person.MALE : [
+ _("%(male_name)s was buried in %(burial_place)s."),
+ _("He was buried in %(burial_place)s."),
+ ],
+ RelLib.Person.FEMALE : [
+ _("%(female_name)s was buried in %(burial_place)s."),
+ _("She was buried in %(burial_place)s."),
+ ],
+ RelLib.Person.UNKNOWN : [
+ _("%(unknown_gender_name)s was buried in %(burial_place)s."),
+ _("This person was buried in %(burial_place)s."),
+ ],
+ }
+
+buried_no_date_no_place = {
+ RelLib.Person.MALE : [
+ _("%(male_name)s was buried."),
+ _("He was buried."),
+ ],
+ RelLib.Person.FEMALE : [
+ _("%(female_name)s was buried."),
+ _("She was buried."),
+ ],
+ RelLib.Person.UNKNOWN : [
+ _("%(unknown_gender_name)s was buried."),
+ _("This person was buried."),
+ ]
+ }
+
+#------------------------------------------------------------------------
+#
+# Marriage strings
+#
+#------------------------------------------------------------------------
+
+marriage_first_date_place = {
+ RelLib.Person.UNKNOWN : [
+ _('This person married %(spouse)s in %(partial_date)s in %(place)s%(endnotes)s.'),
+ _('This person married %(spouse)s on %(full_date)s in %(place)s%(endnotes)s.'),
+ _('This person married %(spouse)s %(modified_date)s in %(place)s%(endnotes)s.'),
+ ],
+ RelLib.Person.MALE : [
+ _('He married %(spouse)s in %(partial_date)s in %(place)s%(endnotes)s.'),
+ _('He married %(spouse)s on %(full_date)s in %(place)s%(endnotes)s.'),
+ _('He married %(spouse)s %(modified_date)s in %(place)s%(endnotes)s.'),
+ ],
+ RelLib.Person.FEMALE : [
+ _('She married %(spouse)s in %(partial_date)s in %(place)s%(endnotes)s.'),
+ _('She married %(spouse)s on %(full_date)s in %(place)s%(endnotes)s.'),
+ _('She married %(spouse)s %(modified_date)s in %(place)s%(endnotes)s.'),
+ ],
+ }
+
+marriage_also_date_place = {
+ RelLib.Person.UNKNOWN : [
+ _('This person also married %(spouse)s in %(partial_date)s in %(place)s%(endnotes)s.'),
+ _('This person also married %(spouse)s on %(full_date)s in %(place)s%(endnotes)s.'),
+ _('This person also married %(spouse)s %(modified_date)s in %(place)s%(endnotes)s.'),
+ ],
+ RelLib.Person.MALE : [
+ _('He also married %(spouse)s in %(partial_date)s in %(place)s%(endnotes)s.'),
+ _('He also married %(spouse)s on %(full_date)s in %(place)s%(endnotes)s.'),
+ _('He also married %(spouse)s %(modified_date)s in %(place)s%(endnotes)s.'),
+ ],
+ RelLib.Person.FEMALE : [
+ _('She also married %(spouse)s in %(partial_date)s in %(place)s%(endnotes)s.'),
+ _('She also married %(spouse)s on %(full_date)s in %(place)s%(endnotes)s.'),
+ _('She also married %(spouse)s %(modified_date)s in %(place)s%(endnotes)s.'),
+ ],
+ }
+
+marriage_first_date = {
+ RelLib.Person.UNKNOWN : [
+ _('This person married %(spouse)s in %(partial_date)s%(endnotes)s.'),
+ _('This person married %(spouse)s on %(full_date)s%(endnotes)s.'),
+ _('This person married %(spouse)s %(modified_date)s%(endnotes)s.'),
+ ],
+ RelLib.Person.MALE : [
+ _('He married %(spouse)s in %(partial_date)s%(endnotes)s.'),
+ _('He married %(spouse)s on %(full_date)s%(endnotes)s.'),
+ _('He married %(spouse)s %(modified_date)s%(endnotes)s.'),
+ ],
+ RelLib.Person.FEMALE : [
+ _('She married %(spouse)s in %(partial_date)s%(endnotes)s.'),
+ _('She married %(spouse)s on %(full_date)s%(endnotes)s.'),
+ _('She married %(spouse)s %(modified_date)s%(endnotes)s.'),
+ ],
+ }
+
+marriage_also_date = {
+ RelLib.Person.UNKNOWN : [
+ _('This person also married %(spouse)s in %(partial_date)s%(endnotes)s.'),
+ _('This person also married %(spouse)s on %(full_date)s%(endnotes)s.'),
+ _('This person also married %(spouse)s %(modified_date)s%(endnotes)s.'),
+ ],
+ RelLib.Person.MALE : [
+ _('He also married %(spouse)s in %(partial_date)s%(endnotes)s.'),
+ _('He also married %(spouse)s on %(full_date)s%(endnotes)s.'),
+ _('He also married %(spouse)s %(modified_date)s%(endnotes)s.'),
+ ],
+ RelLib.Person.FEMALE : [
+ _('She also married %(spouse)s in %(partial_date)s%(endnotes)s.'),
+ _('She also married %(spouse)s on %(full_date)s%(endnotes)s.'),
+ _('She also married %(spouse)s %(modified_date)s%(endnotes)s.'),
+ ],
+ }
+
+marriage_first_place = {
+ RelLib.Person.UNKNOWN : _('This person married %(spouse)s in %(place)s%(endnotes)s.'),
+ RelLib.Person.MALE : _('He married %(spouse)s in %(place)s%(endnotes)s.'),
+ RelLib.Person.FEMALE : _('She married %(spouse)s in %(place)s%(endnotes)s.'),
+ }
+
+marriage_also_place = {
+ RelLib.Person.UNKNOWN : _('This person also married %(spouse)s in %(place)s%(endnotes)s.'),
+ RelLib.Person.MALE : _('He also married %(spouse)s in %(place)s%(endnotes)s.'),
+ RelLib.Person.FEMALE : _('She also married %(spouse)s in %(place)s%(endnotes)s.'),
+ }
+
+marriage_first_only = {
+ RelLib.Person.UNKNOWN : _('This person married %(spouse)s%(endnotes)s.'),
+ RelLib.Person.MALE : _('He married %(spouse)s%(endnotes)s.'),
+ RelLib.Person.FEMALE : _('She married %(spouse)s%(endnotes)s.'),
+ }
+
+marriage_also_only = {
+ RelLib.Person.UNKNOWN : _('This person also married %(spouse)s%(endnotes)s.'),
+ RelLib.Person.MALE : _('He also married %(spouse)s%(endnotes)s.'),
+ RelLib.Person.FEMALE : _('She also married %(spouse)s%(endnotes)s.'),
+ }
+
+#-------------------------------------------------------------------------
+#
+# child to parent relationships
+#
+#-------------------------------------------------------------------------
+
+child_father_mother = {
+ RelLib.Person.UNKNOWN: [
+ [
+ _("This person is the child of %(father)s and %(mother)s."),
+ _("This person was the child of %(father)s and %(mother)s."),
+ ],
+ [
+ _("%(male_name)s is the child of %(father)s and %(mother)s."),
+ _("%(male_name)s was the child of %(father)s and %(mother)s."),
+ ],
+ ],
+ RelLib.Person.MALE : [
+ [
+ _("He is the son of %(father)s and %(mother)s."),
+ _("He was the son of %(father)s and %(mother)s."),
+ ],
+ [
+ _("%(male_name)s is the child of %(father)s and %(mother)s."),
+ _("%(male_name)s was the child of %(father)s and %(mother)s."),
+ ]
+ ],
+ RelLib.Person.FEMALE : [
+ [
+ _("She is the daughter of %(father)s and %(mother)s."),
+ _("She was the daughter of %(father)s and %(mother)s."),
+ ],
+ [
+ _("%(female_name)s is the child of %(father)s and %(mother)s."),
+ _("%(female_name)s was the child of %(father)s and %(mother)s."),
+ ],
+ ]
+}
+
+child_father = {
+ RelLib.Person.UNKNOWN : [
+ [
+ _("This person is the child of %(father)s."),
+ _("This person was the child of %(father)s."),
+ ],
+ [
+ _("%(male_name)s is the child of %(father)s."),
+ _("%(male_name)s was the child of %(father)s."),
+ ],
+ ],
+ RelLib.Person.MALE : [
+ [
+ _("He is the son of %(father)s."),
+ _("He was the son of %(father)s."),
+ ],
+ [
+ _("%(male_name)s is the child of %(father)s."),
+ _("%(male_name)s was the child of %(father)s."),
+ ],
+ ],
+ RelLib.Person.FEMALE : [
+ [
+ _("She is the daughter of %(father)s."),
+ _("She was the daughter of %(father)s."),
+ ],
+ [
+ _("%(female_name)s is the child of %(father)s."),
+ _("%(female_name)s was the child of %(father)s."),
+ ],
+ ],
+}
+
+child_mother = {
+ RelLib.Person.UNKNOWN : [
+ [
+ _("This person is the child of %(mother)s."),
+ _("This person was the child of %(mother)s."),
+ ],
+ [
+ _("%(male_name)s is the child of %(mother)s."),
+ _("%(male_name)s was the child of %(mother)s."),
+ ],
+ ],
+ RelLib.Person.MALE : [
+ [
+ _("He is the son of %(mother)s."),
+ _("He was the son of %(mother)s."),
+ ],
+ [
+ _("%(male_name)s is the child of %(mother)s."),
+ _("%(male_name)s was the child of %(mother)s."),
+ ],
+ ],
+ RelLib.Person.FEMALE : [
+ [
+ _("She is the daughter of %(mother)s."),
+ _("She was the daughter of %(mother)s."),
+ ],
+ [
+ _("%(female_name)s is the child of %(mother)s."),
+ _("%(female_name)s was the child of %(mother)s."),
+ ],
+ ],
+}
+
+#-------------------------------------------------------------------------
+#
+# relationship types
+#
+#-------------------------------------------------------------------------
+_rtype = {
+ RelLib.Family.UNMARRIED : _("unmarried"),
+ RelLib.Family.CIVIL_UNION : _("civil union"),
+ RelLib.Family.UNKNOWN : _("Unknown"),
+ RelLib.Family.CUSTOM : _("Other"),
+ }
+
+#-------------------------------------------------------------------------
+#
+# Convert points to cm and back
+#
+#-------------------------------------------------------------------------
+def pt2cm(pt):
+ """
+ Converts points to centimeters. Fonts are typically specified in points,
+ but the BaseDoc classes use centimeters.
+
+ @param pt: points
+ @type pt: float or int
+ @returns: equivalent units in centimeters
+ @rtype: float
+ """
+ return pt/28.3465
+
+def cm2pt(cm):
+ """
+ Converts centimeters to points. Fonts are typically specified in points,
+ but the BaseDoc classes use centimeters.
+
+ @param cm: centimeters
+ @type cm: float or int
+ @returns: equivalent units in points
+ @rtype: float
+ """
+ return cm*182.88
+
+def rgb_color(color):
+ """
+ Converts color value from 0-255 integer range into 0-1 float range.
+
+ @param color: list or tuple of integer values for red, green, and blue
+ @type color: int
+ @returns: (r,g,b) tuple of floating point color values
+ @rtype: 3-tuple
+ """
+ r = float(color[0])/255.0
+ g = float(color[1])/255.0
+ b = float(color[2])/255.0
+ return (r,g,b)
+
+def draw_pie_chart(doc, center_x, center_y, radius, data, start=0):
+ """
+ Draws a pie chart in the specified document. The data passed is plotted as
+ a pie chart. The data should consist of the actual data. Percentages of
+ each slice are determined by the routine.
+
+ @param doc: Document to which the pie chart should be added
+ @type doc: BaseDoc derived class
+ @param center_x: x coordinate in centimeters where the center of the pie
+ chart should be. 0 is the left hand edge of the document.
+ @type center_x: float
+ @param center_y: y coordinate in centimeters where the center of the pie
+ chart should be. 0 is the top edge of the document
+ @type center_y: float
+ @param radius: radius of the pie chart. The pie charts width and height
+ will be twice this value.
+ @type radius: float
+ @param data: List of tuples containing the data to be plotted. The values
+ are (graphics_format, value), where graphics_format is a BaseDoc
+ GraphicsStyle, and value is a floating point number. Any other items in
+ the tuple are ignored. This allows you to share the same data list with
+ the L{draw_legend} function.
+ @type data: list
+ @param start: starting point in degrees, where the default of 0 indicates
+ a start point extending from the center to right in a horizontal line.
+ @type start: float
+ """
+
+ total = 0.0
+ for item in data:
+ total += item[1]
+
+ for item in data:
+ incr = 360.0*(item[1]/total)
+ doc.draw_wedge(item[0], center_x, center_y, radius, start, start + incr)
+ start += incr
+
+def draw_legend(doc, start_x, start_y, data, title=None):
+ """
+ Draws a legend for a graph in the specified document. The data passed is
+ used to define the legend. First item style is used for the optional
+ Legend title.
+
+ @param doc: Document to which the legend chart should be added
+ @type doc: BaseDoc derived class
+ @param start_x: x coordinate in centimeters where the left hand corner
+ of the legend is placed. 0 is the left hand edge of the document.
+ @type start_x: float
+ @param start_y: y coordinate in centimeters where the top of the legend
+ should be. 0 is the top edge of the document
+ @type start_y: float
+ @param data: List of tuples containing the data to be used to create the
+ legend. In order to be compatible with the graph plots, the first and
+ third values of the tuple used. The format is (graphics_format, value,
+ legend_description).
+ @type data: list
+ """
+ for (format, size, legend) in data:
+ gstyle = doc.get_draw_style(format)
+ pstyle = gstyle.get_paragraph_style()
+ size = pt2cm(doc.get_style(pstyle).get_font().get_size())
+ if title:
+ doc.write_at(pstyle, title, start_x + (3*size), start_y - (size*0.25))
+ start_y += size * 1.3
+ title = None
+ doc.draw_bar(format, start_x, start_y, start_x + (2*size), start_y + size)
+ doc.write_at(pstyle, legend, start_x + (3*size), start_y - (size*0.25))
+ start_y += size * 1.3
+
+def draw_vertical_bar_graph(doc, format, start_x, start_y, height, width, data):
+ """
+ Draws a vertical bar chart in the specified document. The data passed
+ should consist of the actual data. The bars are scaled appropriately by
+ the routine.
+
+ @param doc: Document to which the bar chart should be added
+ @type doc: BaseDoc derived class
+ @param start_x: x coordinate in centimeters where the left hand side of the
+ chart should be. 0 is the left hand edge of the document.
+ @type start_x: float
+ @param start_y: y coordinate in centimeters where the top of the chart
+ should be. 0 is the top edge of the document
+ @type start_y: float
+ @param height: height of the graph in centimeters
+ @type height: float
+ @param width: width of the graph in centimeters
+ @type width: float
+ @param data: List of tuples containing the data to be plotted. The values
+ are (graphics_format, value), where graphics_format is a BaseDoc
+ GraphicsStyle, and value is a floating point number. Any other items in
+ the tuple are ignored. This allows you to share the same data list with
+ the L{draw_legend} function.
+ @type data: list
+ """
+ doc.draw_line(format,start_x,start_y+height,start_x,start_y)
+ doc.draw_line(format,start_x,start_y+height,start_x+width,start_y+height)
+
+ largest = 0.0
+ for item in data:
+ largest = max(item[1],largest)
+
+ scale = float(height)/float(largest)
+ units = len(data)
+ box_width = (float(width) / (units*3.0+1.0))*2
+
+ bottom = float(start_y)+float(height)
+
+ start = 0.5*box_width + start_x
+ for index in range(units):
+ size = float(data[index][1]) * scale
+ doc.draw_bar(data[index][0],start,bottom-size,start+box_width,bottom)
+ start += box_width * 1.5
+
+
+_t = time.localtime(time.time())
+_TODAY = DateHandler.parser.parse("%04d-%02d-%02d" % (_t[0],_t[1],_t[2]))
+
+def estimate_age(db, person, end_handle=None, start_handle=None):
+ """
+ Estimates the age of a person based off the birth and death
+ dates of the person. A tuple containing the estimated upper
+ and lower bounds of the person's age is returned. If either
+ the birth or death date is missing, a (-1,-1) is returned.
+
+ @param db: GRAMPS database to which the Person object belongs
+ @type db: GrampsDbBase
+ @param person: Person object to calculate the age of
+ @type person: Person
+ @param end_handle: Determines the event handle that determines
+ the upper limit of the age. If None, the death event is used
+ @type end_handle: str
+ @param start_handle: Determines the event handle that determines
+ the lower limit of the event. If None, the birth event is
+ used
+ @type start_handle: str
+ @returns: tuple containing the lower and upper bounds of the
+ person's age, or (-1,-1) if it could not be determined.
+ @rtype: tuple
+ """
+
+ if start_handle:
+ bhandle = start_handle
+ else:
+ bhandle = person.get_birth_handle()
+
+ if end_handle:
+ dhandle = end_handle
+ else:
+ dhandle = person.get_death_handle()
+
+ # if either of the events is not defined, return an error message
+ if not bhandle:
+ return (-1,-1)
+
+ bdata = db.get_event_from_handle(bhandle).get_date_object()
+ if dhandle:
+ ddata = db.get_event_from_handle(dhandle).get_date_object()
+ else:
+ ddata = _TODAY
+
+ # if the date is not valid, return an error message
+ if not bdata.get_valid() or not ddata.get_valid():
+ return (-1,-1)
+
+ # if a year is not valid, return an error message
+ if not bdata.get_year_valid() or not ddata.get_year_valid():
+ return (-1,-1)
+
+ bstart = bdata.get_start_date()
+ bstop = bdata.get_stop_date()
+
+ dstart = ddata.get_start_date()
+ dstop = ddata.get_stop_date()
+
+ def _calc_diff(low,high):
+ if (low[1],low[0]) > (high[1],high[0]):
+ return high[2] - low[2] - 1
+ else:
+ return high[2] - low[2]
+
+ if bstop == RelLib.Date.EMPTY and dstop == RelLib.Date.EMPTY:
+ lower = _calc_diff(bstart,dstart)
+ age = (lower, lower)
+ elif bstop == RelLib.Date.EMPTY:
+ lower = _calc_diff(bstart,dstart)
+ upper = _calc_diff(bstart,dstop)
+ age = (lower,upper)
+ elif dstop == RelLib.Date.EMPTY:
+ lower = _calc_diff(bstop,dstart)
+ upper = _calc_diff(bstart,dstart)
+ age = (lower,upper)
+ else:
+ lower = _calc_diff(bstop,dstart)
+ upper = _calc_diff(bstart,dstop)
+ age = (lower,upper)
+ return age
+
+def sanitize_list(obj_list,exclude_private):
+ """
+ Removes private objects from the list.
+
+ @param obj_list: objects that have a privacy flag
+ @type obj_list: list
+ @param exclude_private: indicates if objects marked private
+ are eliminated from the list
+ @type obj_list: bool
+ @returns: objects that match the privacy request
+ @rtype: list
+ """
+ if exclude_private:
+ return [obj for obj in obj_list if not obj.private]
+ else:
+ return obj_list
+
+def sanitize_person(db,person):
+ """
+ Creates a new Person instance based off the passed Person
+ instance. The returned instance has all private records
+ removed from it.
+
+ @param db: GRAMPS database to which the Person object belongs
+ @type db: GrampsDbBase
+ @param person: source Person object that will be copied with
+ privacy records removed
+ @type person: Person
+ @returns: 'cleansed' Person object
+ @rtype: Person
+ """
+ new_person = RelLib.Person()
+
+ # copy gender
+ new_person.set_gender(person.get_gender())
+ new_person.set_gramps_id(person.get_gramps_id())
+ new_person.set_handle(person.get_handle())
+
+ # copy names if not private
+ name = person.get_primary_name()
+ if name.get_privacy() or person.get_privacy():
+ name = RelLib.Name()
+ name.set_surname(_('Private'))
+ else:
+ new_person.set_nick_name(person.get_nick_name())
+
+ new_person.set_primary_name(name)
+ # copy Family reference list
+ for handle in person.get_family_handle_list():
+ new_person.add_family_handle(handle)
+
+ # copy Family reference list
+ for item in person.get_parent_family_handle_list():
+ new_person.add_parent_family_handle(item[0],item[1],item[2])
+
+ if person.get_privacy():
+ return new_person
+
+ for name in person.get_alternate_names():
+ if not name.get_privacy():
+ new_person.add_alternate_name(name)
+
+ # set complete flag
+ new_person.set_complete_flag(person.get_complete_flag())
+
+ # copy birth event
+ event_handle = person.get_birth_handle()
+ event = db.get_event_from_handle(event_handle)
+ if event and not event.get_privacy():
+ new_person.set_birth_handle(event_handle)
+
+ # copy death event
+ event_handle = person.get_death_handle()
+ event = db.get_event_from_handle(event_handle)
+ if event and not event.get_privacy():
+ new_person.set_death_handle(event_handle)
+
+ # copy event list
+ for event_handle in person.get_event_list():
+ event = db.get_event_from_handle(event_handle)
+ if event and not event.get_privacy():
+ new_person.add_event_handle(event_handle)
+
+ # copy address list
+ for address in person.get_address_list():
+ if not address.get_privacy():
+ new_person.add_address(RelLib.Address(address))
+
+ # copy attribute list
+ for attribute in person.get_attribute_list():
+ if not attribute.get_privacy():
+ new_person.add_attribute(RelLib.Attribute(attribute))
+
+ # copy source references
+ for ref in person.get_source_references():
+ if not ref.get_privacy():
+ new_person.add_source_reference(RelLib.SourceRef(ref))
+
+ # copy URL list
+ for url in person.get_url_list():
+ if not url.get_privacy():
+ new_person.add_url(url)
+
+ # copy Media reference list
+ for obj in person.get_media_list():
+ if not obj.get_privacy():
+ new_person.add_media_reference(RelLib.MediaRef(obj))
+
+ # LDS ordinances
+ ordinance = person.get_lds_baptism()
+ if ordinance:
+ new_person.set_lds_baptism(ordinance)
+
+ ordinance = person.get_lds_endowment()
+ if ordinance:
+ new_person.set_lds_endowment(ordinance)
+
+ ordinance = person.get_lds_sealing()
+ if ordinance:
+ new_person.set_lds_sealing(ordinance)
+
+ new_person.set_note(person.get_note())
+
+ return new_person
+
+def dont_restrict(db,person):
+ return person
+
+def restrict_with_names(db,person):
+ return restrict_person(db,person,False)
+
+def restrict_no_names(db,person):
+ return restrict_person(db,person,True)
+
+def restrict_person(db,person,no_names=False):
+ """
+ Creates a new Person instance based off the passed Person
+ instance. The returned instance has all private records
+ removed from it.
+
+ @param db: GRAMPS database to which the Person object belongs
+ @type db: GrampsDbBase
+ @param person: source Person object that will be copied with
+ privacy records removed
+ @type person: Person
+ @returns: 'cleansed' Person object
+ @rtype: Person
+ """
+ new_person = RelLib.Person()
+
+ # copy gender
+ new_person.set_gender(person.get_gender())
+ new_person.set_gramps_id(person.get_gramps_id())
+ new_person.set_handle(person.get_handle())
+
+ # copy names if not private
+ if no_names:
+ name = RelLib.Name()
+ name.set_surname(_('Private'))
+ else:
+ name = person.get_primary_name()
+ name.set_source_reference_list([])
+
+ new_person.set_primary_name(name)
+
+ # copy Family reference list
+ for handle in person.get_family_handle_list():
+ new_person.add_family_handle(handle)
+
+ # copy Family reference list
+ for item in person.get_parent_family_handle_list():
+ new_person.add_parent_family_handle(item[0],item[1],item[2])
+
+ return new_person
+
+#-------------------------------------------------------------------------
+#
+# Roman numbers
+#
+#-------------------------------------------------------------------------
+def roman(num):
+ """ Integer to Roman numeral converter for 0 < num < 4000 """
+ if type(num) != int:
+ return "?"
+ if not 0 < num < 4000:
+ return "?"
+ vals = (1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1)
+ nums = ( 'M','CM', 'D','CD', 'C','XC', 'L','XL', 'X','IX', 'V','IV', 'I')
+ retval = ""
+ for i in range(len(vals)):
+ amount = int(num / vals[i])
+ retval += nums[i] * amount
+ num -= vals[i] * amount
+ return retval
+
+#-------------------------------------------------------------------------
+#
+#
+#
+#-------------------------------------------------------------------------
+def place_name(db,place_handle):
+ if place_handle:
+ place = db.get_place_from_handle(place_handle).get_title()
+ else:
+ place = ""
+ return place
+
+#-------------------------------------------------------------------------
+#
+# Functions commonly used in reports
+#
+#-------------------------------------------------------------------------
+def insert_image(database, doc, photo, w_cm=4.0, h_cm=4.0):
+ """
+ Insert pictures of a person into the document.
+ """
+
+ object_handle = photo.get_reference_handle()
+ media_object = database.get_object_from_handle(object_handle)
+ mime_type = media_object.get_mime_type()
+ if mime_type and mime_type.startswith("image"):
+ filename = media_object.get_path()
+ if os.path.exists(filename):
+ doc.add_media_object(filename,"right",w_cm,h_cm)
+ else:
+ WarningDialog(_("Could not add photo to page"),
+ "%s: %s" % (filename, _('File does not exist')))
+
+#-------------------------------------------------------------------------
+#
+# Strings commonly used in reports
+#
+#-------------------------------------------------------------------------
+def empty_notes(whatever):
+ # Empty stab function for when endnotes are not needed
+ return ""
+
+def get_birth_death_strings(database,person,empty_date="",empty_place=""):
+ """
+ Returns strings for dates and places of birth and death.
+ """
+
+ bplace = dplace = empty_place
+ bdate = ddate = empty_date
+ bdate_full = ddate_full = False
+ bdate_mod = False
+ ddate_mod = False
+
+ birth_ref = person.get_birth_ref()
+ if birth_ref and birth_ref.ref:
+ birth_handle = birth_ref.ref
+ birth = database.get_event_from_handle(birth_handle)
+ if birth:
+ bdate = DateHandler.get_date(birth)
+ bplace_handle = birth.get_place_handle()
+ if bplace_handle:
+ bplace = database.get_place_from_handle(bplace_handle).get_title()
+ bdate_obj = birth.get_date_object()
+ bdate_full = bdate_obj and bdate_obj.get_day_valid()
+ bdate_mod = bdate_obj and bdate_obj.get_modifier() != RelLib.Date.MOD_NONE
+
+ death_ref = person.get_death_ref()
+ if death_ref and death_ref.ref:
+ death_handle = death_ref.ref
+ death = database.get_event_from_handle(death_handle)
+ if death:
+ ddate = DateHandler.get_date(death)
+ dplace_handle = death.get_place_handle()
+ if dplace_handle:
+ dplace = database.get_place_from_handle(dplace_handle).get_title()
+ ddate_obj = death.get_date_object()
+ ddate_full = ddate_obj and ddate_obj.get_day_valid()
+ ddate_mod = ddate_obj and ddate_obj.get_modifier() != RelLib.Date.MOD_NONE
+
+ return (bdate,bplace,bdate_full,bdate_mod,ddate,dplace,ddate_full,ddate_mod)
+
+def born_died_str(database,person,endnotes=None,name_object=None,person_name=None):
+ """
+ Composes a string describing birth and death of a person.
+ Missing information will be omitted without loss of readability.
+ Optional references may be added to birth and death events.
+ Optional Name object may be used to override a person's Name instance.
+ Optional string may be used to override the string representation of a name.
+
+ @param database: GRAMPS database to which the Person object belongs
+ @type database: GrampsDbBase
+ @param person: Person instance for which the string has to be composed
+ @type person: Person
+ @param endnotes: Function to use for reference composition. If None
+ then references will not be added
+ @type endnotes: function
+ @param name_object: Name instance for which the phrase is composed. If None
+ then the regular primary name of the person will be used
+ @type name_object: Name
+ @param person_name: String to override the person's name. If None then the
+ regular primary name string will be used
+ @type person_name: unicode
+ @returns: A composed string
+ @rtype: unicode
+ """
+
+ if not endnotes:
+ endnotes = empty_notes
+
+ if not name_object:
+ name_object = person.get_primary_name()
+
+ if person_name == None:
+ person_name = _nd.display_name(name_object)
+ elif person_name == 0:
+ if person.get_gender() == RelLib.Person.MALE:
+ person_name = _('He')
+ else:
+ person_name = _('She')
+
+ bdate,bplace,bdate_full,bdate_mod,ddate,dplace,ddate_full,ddate_mod = \
+ get_birth_death_strings(database,person)
+
+ birth = database.get_event_from_handle(person.get_birth_handle())
+ death = database.get_event_from_handle(person.get_death_handle())
+
+ values = {
+ 'unknown_gender_name' : person_name,
+ 'name' : person_name,
+ 'male_name' : person_name,
+ 'female_name' : person_name,
+ 'endnotes' : endnotes(name_object),
+ 'birth_date' : bdate,
+ 'birth_place' : bplace,
+ 'birth_endnotes' : endnotes(birth),
+ 'death_date' : ddate,
+ 'death_place' : dplace,
+ 'death_endnotes' : endnotes(death),
+ }
+
+ if person.get_gender() == RelLib.Person.MALE:
+ if bdate:
+ if bplace:
+ if ddate:
+ if dplace:
+ text = _("%(male_name)s%(endnotes)s "
+ "was born %(birth_date)s in %(birth_place)s%(birth_endnotes)s, "
+ "and died %(death_date)s in %(death_place)s%(death_endnotes)s.") % values
+ else:
+ text = _("%(male_name)s%(endnotes)s "
+ "was born %(birth_date)s in %(birth_place)s%(birth_endnotes)s, "
+ "and died %(death_date)s%(death_endnotes)s.") % values
+ else:
+ if dplace:
+ text = _("%(male_name)s%(endnotes)s "
+ "was born %(birth_date)s in %(birth_place)s%(birth_endnotes)s, "
+ "and died in %(death_place)s%(death_endnotes)s.") % values
+ else:
+ text = _("%(male_name)s%(endnotes)s "
+ "was born %(birth_date)s in %(birth_place)s%(birth_endnotes)s.") % values
+ else:
+ if ddate:
+ if dplace:
+ text = _("%(male_name)s%(endnotes)s "
+ "was born %(birth_date)s%(birth_endnotes)s, "
+ "and died %(death_date)s in %(death_place)s%(death_endnotes)s.") % values
+ else:
+ text = _("%(male_name)s%(endnotes)s "
+ "was born %(birth_date)s%(birth_endnotes)s, "
+ "and died %(death_date)s%(death_endnotes)s.") % values
+ else:
+ if dplace:
+ text = _("%(male_name)s%(endnotes)s "
+ "was born %(birth_date)s%(birth_endnotes)s, "
+ "and died in %(death_place)s%(death_endnotes)s.") % values
+ else:
+ text = _("%(male_name)s%(endnotes)s "
+ "was born %(birth_date)s%(birth_endnotes)s.") % values
+ else:
+ if bplace:
+ if ddate:
+ if dplace:
+ text = _("%(male_name)s%(endnotes)s "
+ "was born in %(birth_place)s%(birth_endnotes)s, "
+ "and died %(death_date)s in %(death_place)s%(death_endnotes)s.") % values
+ else:
+ text = _("%(male_name)s%(endnotes)s "
+ "was born in %(birth_place)s%(birth_endnotes)s, "
+ "and died %(death_date)s%(death_endnotes)s.") % values
+ else:
+ if dplace:
+ text = _("%(male_name)s%(endnotes)s "
+ "was born in %(birth_place)s%(birth_endnotes)s, "
+ "and died in %(death_place)s%(death_endnotes)s.") % values
+ else:
+ text = _("%(male_name)s%(endnotes)s "
+ "was born in %(birth_place)s%(birth_endnotes)s.") % values
+ else:
+ if ddate:
+ if dplace:
+ text = _("%(male_name)s%(endnotes)s "
+ "died %(death_date)s in %(death_place)s%(death_endnotes)s.") % values
+ else:
+ text = _("%(male_name)s%(endnotes)s "
+ "died %(death_date)s%(death_endnotes)s.") % values
+ else:
+ if dplace:
+ text = _("%(male_name)s%(endnotes)s "
+ "died in %(death_place)s%(death_endnotes)s.") % values
+ else:
+ text = _("%(male_name)s%(endnotes)s.") % values
+ else:
+ if bdate:
+ if bplace:
+ if ddate:
+ if dplace:
+ text = _("%(female_name)s%(endnotes)s "
+ "was born %(birth_date)s in %(birth_place)s%(birth_endnotes)s, "
+ "and died %(death_date)s in %(death_place)s%(death_endnotes)s.") % values
+ else:
+ text = _("%(female_name)s%(endnotes)s "
+ "was born %(birth_date)s in %(birth_place)s%(birth_endnotes)s, "
+ "and died %(death_date)s%(death_endnotes)s.") % values
+ else:
+ if dplace:
+ text = _("%(female_name)s%(endnotes)s "
+ "was born %(birth_date)s in %(birth_place)s%(birth_endnotes)s, "
+ "and died in %(death_place)s%(death_endnotes)s.") % values
+ else:
+ text = _("%(female_name)s%(endnotes)s "
+ "was born %(birth_date)s in %(birth_place)s%(birth_endnotes)s.") % values
+ else:
+ if ddate:
+ if dplace:
+ text = _("%(female_name)s%(endnotes)s "
+ "was born %(birth_date)s%(birth_endnotes)s, "
+ "and died %(death_date)s in %(death_place)s%(death_endnotes)s.") % values
+ else:
+ text = _("%(female_name)s%(endnotes)s "
+ "was born %(birth_date)s%(birth_endnotes)s, "
+ "and died %(death_date)s%(death_endnotes)s.") % values
+ else:
+ if dplace:
+ text = _("%(female_name)s%(endnotes)s "
+ "was born %(birth_date)s%(birth_endnotes)s, "
+ "and died in %(death_place)s%(death_endnotes)s.") % values
+ else:
+ text = _("%(female_name)s%(endnotes)s "
+ "was born %(birth_date)s%(birth_endnotes)s.") % values
+ else:
+ if bplace:
+ if ddate:
+ if dplace:
+ text = _("%(female_name)s%(endnotes)s "
+ "was born in %(birth_place)s%(birth_endnotes)s, "
+ "and died %(death_date)s in %(death_place)s%(death_endnotes)s.") % values
+ else:
+ text = _("%(female_name)s%(endnotes)s "
+ "was born in %(birth_place)s%(birth_endnotes)s, "
+ "and died %(death_date)s%(death_endnotes)s.") % values
+ else:
+ if dplace:
+ text = _("%(female_name)s%(endnotes)s "
+ "was born in %(birth_place)s%(birth_endnotes)s, "
+ "and died in %(death_place)s%(death_endnotes)s.") % values
+ else:
+ text = _("%(female_name)s%(endnotes)s "
+ "was born in %(birth_place)s%(birth_endnotes)s.") % values
+ else:
+ if ddate:
+ if dplace:
+ text = _("%(female_name)s%(endnotes)s "
+ "died %(death_date)s in %(death_place)s%(death_endnotes)s.") % values
+ else:
+ text = _("%(female_name)s%(endnotes)s "
+ "died %(death_date)s%(death_endnotes)s.") % values
+ else:
+ if dplace:
+ text = _("%(female_name)s%(endnotes)s "
+ "died in %(death_place)s%(death_endnotes)s.") % values
+ else:
+ text = _("%(female_name)s%(endnotes)s.") % values
+ if text:
+ text = text + " "
+ return text
+
+#-------------------------------------------------------------------------
+#
+# married_str
+#
+#-------------------------------------------------------------------------
+def married_str(database,person,spouse,event,endnotes=None,
+ empty_date="",empty_place="",is_first=True):
+ """
+ Composes a string describing marriage of a person. Missing information will
+ be omitted without loss of readability. Optional references may be added to
+ birth and death events.
+
+ @param database: GRAMPS database to which the Person object belongs
+ @type db: GrampsDbBase
+ @param person: Person instance whose marriage is discussed
+ @type person: Person
+ @param spouse: Person instance to use as a spouse
+ @type spouse: Person
+ @param event: Event instance of marriage
+ @type event: Event
+ @param endnotes: Function to use for reference composition. If None
+ then references will not be added
+ @type endnotes: function
+ @returns: A composed string
+ @rtype: unicode
+ """
+
+ # not all families have a spouse.
+ if not spouse:
+ return u""
+
+ if not endnotes:
+ endnotes = empty_notes
+
+ date = empty_date
+ place = empty_place
+ spouse_name = _nd.display(spouse)
+
+ mdate = DateHandler.get_date(event)
+ if mdate:
+ date = mdate
+ place_handle = event.get_place_handle()
+ if place_handle:
+ place = database.get_place_from_handle(place_handle).get_title()
+
+ values = {
+ 'spouse' : spouse_name,
+ 'endnotes' : endnotes(event),
+ 'full_date' : date,
+ 'modified_date' : date,
+ 'partial_date' : date,
+ 'place' : place,
+ }
+
+ dobj = event.get_date_object()
+
+ if dobj.get_modifier() != RelLib.Date.MOD_NONE:
+ date_full = 2
+ elif dobj and dobj.get_day_valid():
+ date_full = 1
+ else:
+ date_full = 0
+
+ gender = person.get_gender()
+
+ if is_first:
+ if date and place:
+ text = marriage_first_date_place[gender][date_full] % values
+ elif date:
+ text = marriage_first_date[gender][date_full] % values
+ elif place:
+ text = marriage_first_place[gender] % values
+ else:
+ text = marriage_first_only[gender] % values
+ else:
+ if date and place:
+ text = marriage_also_date_place[gender][date_full] % values
+ elif date:
+ text = marriage_also_date[gender][date_full] % values
+ elif place:
+ text = marriage_also_place[gender] % values
+ else:
+ text = marriage_also_only[gender] % values
+
+ if text:
+ text = text + " "
+ return text
+
+#-------------------------------------------------------------------------
+#
+# relationship types
+#
+#-------------------------------------------------------------------------
+def married_rel_str(database,person,family,is_first=True):
+ """
+ Composes a string describing marriage of a person. Missing information will
+ be omitted without loss of readability. Optional references may be added to
+ birth and death events.
+
+ @param database: GRAMPS database to which the Person object belongs
+ @type db: GrampsDbBase
+ @param person: Person instance whose marriage is discussed
+ @type person: Person
+ @param family: Family instance whose marriage is discussed
+ @type family: Family
+ @param is_first: Indicates if this is a first marriage
+ @type is_first: bool
+ @returns: A composed string
+ @rtype: unicode
+ """
+ spouse_handle = find_spouse(person,family)
+ spouse = database.get_person_from_handle(spouse_handle)
+
+ # not all families have a spouse.
+ if not spouse_handle or not spouse:
+ return u""
+
+ values = {
+ 'spouse' :_nd.display(spouse)
+ }
+
+ if is_first:
+ if family.get_relationship() == RelLib.Family.MARRIED:
+ if person.get_gender() == RelLib.Person.MALE:
+ text = _('He married %(spouse)s.') % values
+ elif person.get_gender() == RelLib.Person.FEMALE:
+ text = _('She married %(spouse)s.') % values
+ else:
+ text = _('This person married %(spouse)s.') % values
+ elif family.get_relationship() == RelLib.Family.UNMARRIED:
+ if person.get_gender() == RelLib.Person.MALE:
+ text = _('He had an unmarried relationship with %(spouse)s.') % values
+ elif person.get_gender() == RelLib.Person.FEMALE:
+ text = _('This person had an unmarried relationship with %(spouse)s.') % values
+ else:
+ text = _('She had an unmarried relationship with %(spouse)s.') % values
+ else:
+ if person.get_gender() == RelLib.Person.MALE:
+ text = _('He had relationship with %(spouse)s.') % values
+ elif person.get_gender() == RelLib.Person.FEMALE:
+ text = _('She had relationship with %(spouse)s.') % values
+ else:
+ text = _('This person had relationship with %(spouse)s.') % values
+ else:
+ if family.get_relationship() == RelLib.Family.MARRIED:
+ if person.get_gender() == RelLib.Person.MALE:
+ text = _('He also married %(spouse)s.') % values
+ elif person.get_gender() == RelLib.Person.FEMALE:
+ text = _('She also married %(spouse)s.') % values
+ else:
+ text = _('This person also married %(spouse)s.') % values
+ elif family.get_relationship() == RelLib.Family.UNMARRIED:
+ if person.get_gender() == RelLib.Person.MALE:
+ text = _('He had an unmarried relationship with %(spouse)s.') % values
+ elif person.get_gender() == RelLib.Person.FEMALE:
+ text = _('She had an unmarried relationship with %(spouse)s.') % values
+ else:
+ text = _('This person had an unmarried relationship with %(spouse)s.') % values
+ else:
+ if person.get_gender() == RelLib.Person.MALE:
+ text = _('He also had relationship with %(spouse)s.') % values
+ elif person.get_gender() == RelLib.Person.FEMALE:
+ text = _('She also had relationship with %(spouse)s.') % values
+ else:
+ text = _('This person also had relationship with %(spouse)s.') % values
+ if text:
+ text = text + " "
+ return text
+
+#-------------------------------------------------------------------------
+#
+# child_str
+#
+#-------------------------------------------------------------------------
+def child_str(person, father_name="", mother_name="", dead=0, person_name=0):
+ """
+ Composes a string describing person being a child.
+
+ The string is composed in the following form:
+ 'He/She is/was the son/daughter of father_name and mother_name'
+ Missing information will be omitted without loss of readability.
+
+ @param person_gender: Person.MALE, Person.FEMALE, or Person.UNKNOWN
+ @type person: Person.MALE, Person.FEMALE, or Person.UNKNOWN~
+ @param father_name: String to use for father's name
+ @type father_name: unicode
+ @param mother_name: String to use for mother's name
+ @type mother_name: unicode
+ @param dead: Whether the person discussed is dead or not
+ @type dead: bool
+ @returns: A composed string
+ @rtype: unicode
+ """
+
+ values = {
+ 'father' : father_name,
+ 'mother' : mother_name,
+ 'male_name' : person_name,
+ 'name' : person_name,
+ 'female_name' : person_name,
+ 'unknown_gender_name' : person_name,
+ }
+
+ if person_name == 0:
+ index = 0
+ else:
+ index = 1
+
+ gender = person.get_gender()
+
+ text = ""
+ if mother_name and father_name:
+ text = child_father_mother[gender][index][dead] % values
+ elif mother_name:
+ text = child_mother[gender][index][dead] % values
+ elif father_name:
+ text = child_father[gender][index][dead] % values
+ if text:
+ text = text + " "
+ return text
+
+#-------------------------------------------------------------------------
+#
+# find_spouse
+#
+#-------------------------------------------------------------------------
+def find_spouse(person,family):
+ if person.get_handle() == family.get_father_handle():
+ spouse_handle = family.get_mother_handle()
+ else:
+ spouse_handle = family.get_father_handle()
+ return spouse_handle
+
+#-------------------------------------------------------------------------
+#
+# find_marriage
+#
+#-------------------------------------------------------------------------
+def find_marriage(database,family):
+ for event_handle in family.get_event_list():
+ event = database.get_event_from_handle(event_handle)
+ if event and event.get_name() == "Marriage":
+ return event
+ return None
+
+#-------------------------------------------------------------------------
+#
+# born_str
+#
+#-------------------------------------------------------------------------
+def born_str(database,person,person_name=None,empty_date="",empty_place=""):
+ """
+ Check birth record.
+ Statement formats name precedes this
+ was born on Date.
+ was born on Date in Place.
+ was born in Month_Year.
+ was born in Month_Year in Place.
+ was born in Place.
+ ''
+ """
+
+ name_index = 1
+ if person_name == None:
+ person_name = _nd.display(person)
+ elif person_name == 0:
+ name_index = 0
+
+ text = ""
+
+ bdate,bplace,bdate_full,bdate_mod,ddate,dplace,ddate_full,ddate_mod = \
+ get_birth_death_strings(database,person,empty_date,empty_place)
+
+ value_map = {
+ 'name' : person_name,
+ 'male_name' : person_name,
+ 'unknown_gender_name' : person_name,
+ 'female_name' : person_name,
+ 'birth_date' : bdate,
+ 'birth_place' : bplace,
+ 'month_year' : bdate,
+ 'modified_date' : bdate,
+ }
+
+ gender = person.get_gender()
+
+ if bdate:
+ if bdate_mod:
+ if dplace:
+ text = born_modified_date_with_place[name_index][gender] % value_map
+ else:
+ text = born_modified_date_no_place[name_index][gender] % value_map
+ elif bdate_full:
+ if bplace:
+ text = born_full_date_with_place[name_index][gender] % value_map
+ else:
+ text = born_full_date_no_place[name_index][gender] % value_map
+ else:
+ if bplace:
+ text = born_partial_date_with_place[name_index][gender] % value_map
+ else:
+ text = born_partial_date_no_place[name_index][gender] % value_map
+ else:
+ if bplace:
+ text = born_no_date_with_place[name_index][gender] % value_map
+ else:
+ text = ""
+ if text:
+ text = text + " "
+ return text
+
+#-------------------------------------------------------------------------
+#
+# died_str
+#
+#-------------------------------------------------------------------------
+def died_str(database,person,person_name=None,empty_date="",empty_place="",
+ age=None,age_units=0):
+ """
+ Write obit sentence.
+ FIRSTNAME died on Date
+ FIRSTNAME died on Date at the age of N Years
+ FIRSTNAME died on Date at the age of N Months
+ FIRSTNAME died on Date at the age of N Days
+ FIRSTNAME died on Date in Place
+ FIRSTNAME died on Date in Place at the age of N Years
+ FIRSTNAME died on Date in Place at the age of N Months
+ FIRSTNAME died on Date in Place at the age of N Days
+ FIRSTNAME died in Month_Year
+ FIRSTNAME died in Month_Year at the age of N Years
+ FIRSTNAME died in Month_Year at the age of N Months
+ FIRSTNAME died in Month_Year at the age of N Days
+ FIRSTNAME died in Month_Year in Place
+ FIRSTNAME died in Month_Year in Place at the age of N Years
+ FIRSTNAME died in Month_Year in Place at the age of N Months
+ FIRSTNAME died in Month_Year in Place at the age of N Days
+ FIRSTNAME died in Place
+ FIRSTNAME died in Place at the age of N Years
+ FIRSTNAME died in Place at the age of N Months
+ FIRSTNAME died in Place at the age of N Days
+ FIRSTNAME died
+ FIRSTNAME died at the age of N Years
+ FIRSTNAME died at the age of N Months
+ FIRSTNAME died at the age of N Days
+ """
+
+ name_index = 1
+ if person_name == None:
+ person_name = _nd.display(person)
+ elif person_name == 0:
+ name_index = 0
+
+ text = ""
+
+ bdate,bplace,bdate_full,bdate_mod,ddate,dplace,ddate_full,ddate_mod = \
+ get_birth_death_strings(database,person,empty_date,empty_place)
+
+ value_map = {
+ 'name' : person_name,
+ 'unknown_gender_name' : person_name,
+ 'male_name' : person_name,
+ 'female_name' : person_name,
+ 'death_date' : ddate,
+ 'modified_date' : ddate,
+ 'death_place' : dplace,
+ 'age' : age ,
+ 'month_year' : ddate,
+ }
+
+ gender = person.get_gender()
+
+ if ddate:
+ if ddate_mod:
+ if dplace:
+ text = died_modified_date_with_place[name_index][gender][age_units] % value_map
+ else:
+ text = died_modified_date_no_place[name_index][gender][age_units] % value_map
+ elif ddate_full:
+ if dplace:
+ text = died_full_date_with_place[name_index][gender][age_units] % value_map
+ else:
+ text = died_full_date_no_place[name_index][gender][age_units] % value_map
+ else:
+ if dplace:
+ text = died_partial_date_with_place[name_index][gender][age_units] % value_map
+ else:
+ text = died_partial_date_no_place[name_index][gender][age_units] % value_map
+ else:
+ if dplace:
+ text = died_no_date_with_place[name_index][gender][age_units] % value_map
+ else:
+ text = died_no_date_no_place[name_index][gender][age_units] % value_map
+ if text:
+ text = text + " "
+ return text
+
+#-------------------------------------------------------------------------
+#
+# buried_str
+#
+#-------------------------------------------------------------------------
+def buried_str(database,person,person_name=None,empty_date="",empty_place=""):
+ """
+ Check burial record.
+ Statement formats name precedes this
+ was buried on Date.
+ was buried on Date in Place.
+ was buried in Month_Year.
+ was buried in Month_Year in Place.
+ was buried in Place.
+ ''
+ """
+
+ name_index = 0
+ if person_name == None:
+ person_name = _nd.display(person)
+ elif person_name == 0:
+ name_index = 1
+
+ gender = person.get_gender()
+
+ text = ""
+
+ bplace = dplace = empty_place
+ bdate = ddate = empty_date
+ bdate_full = False
+ bdate_mod = False
+
+ burial = None
+ for event_handle in person.get_event_list():
+ event = database.get_event_from_handle(event_handle)
+ if event and event.get_name() == "Burial":
+ burial = event
+ break
+
+ if burial:
+ bdate = DateHandler.get_date(burial)
+ bplace_handle = burial.get_place_handle()
+ if bplace_handle:
+ bplace = database.get_place_from_handle(bplace_handle).get_title()
+ bdate_obj = burial.get_date_object()
+ bdate_full = bdate_obj and bdate_obj.get_day_valid()
+ bdate_mod = bdate_obj and bdate_obj.get_modifier() != RelLib.Date.MOD_NONE
+ else:
+ return text
+
+ values = {
+ 'unknown_gender_name' : person_name,
+ 'male_name' : person_name,
+ 'name' : person_name,
+ 'female_name' : person_name,
+ 'burial_date' : bdate,
+ 'burial_place' : bplace,
+ 'month_year' : bdate,
+ 'modified_date' : bdate,
+ }
+
+ if bdate and bdate_mod:
+ if bplace: #male, date, place
+ text = buried_modified_date_place[gender][name_index] % values
+ else: #male, date, no place
+ text = buried_modified_date_no_place[gender][name_index] % values
+ elif bdate and bdate_full:
+ if bplace: #male, date, place
+ text = buried_full_date_place[gender][name_index] % values
+ else: #male, date, no place
+ text = buried_full_date_no_place[gender][name_index] % values
+ elif bdate:
+ if bplace: #male, month_year, place
+ text = buried_partial_date_place[gender][name_index] % values
+ else: #male, month_year, no place
+ text = buried_partial_date_no_place[gender][name_index] % values
+ elif bplace: #male, no date, place
+ text = buried_no_date_place[gender][name_index] % values
+ else: #male, no date, no place
+ text = buried_no_date_no_place[gender][name_index] % values
+
+ if text:
+ text = text + " "
+ return text
+
+#-------------------------------------------------------------------------
+#
+# list_person_str
+#
+#-------------------------------------------------------------------------
+def list_person_str(database,person,empty_date="",empty_place=""):
+ """
+ Briefly list person and birth/death events.
+ """
+
+ bdate,bplace,bdate_full,bdate_mod,ddate,dplace,ddate_full,ddate_mod = \
+ get_birth_death_strings(database,person)
+
+ text = ""
+
+ values = {
+ 'birth_date' : bdate,
+ 'birth_place' : bplace,
+ 'death_date' : ddate,
+ 'death_place' : dplace,
+ }
+
+ if bdate:
+ if bplace:
+ if ddate:
+ if dplace:
+ text = _("Born: %(birth_date)s %(birth_place)s, "
+ "Died: %(death_date)s %(death_place)s.") % values
+ else:
+ text = _("Born: %(birth_date)s %(birth_place)s, "
+ "Died: %(death_date)s.") % values
+ else:
+ if dplace:
+ text = _("Born: %(birth_date)s %(birth_place)s, "
+ "Died: %(death_place)s.") % values
+ else:
+ text = _("Born: %(birth_date)s %(birth_place)s.") % values
+ else:
+ if ddate:
+ if dplace:
+ text = _("Born: %(birth_date)s, "
+ "Died: %(death_date)s %(death_place)s.") % values
+ else:
+ text = _("Born: %(birth_date)s, Died: %(death_date)s.") % values
+ else:
+ if dplace:
+ text = _("Born: %(birth_date)s, Died: %(death_place)s.") % values
+ else:
+ text = _("Born: %(birth_date)s.") % values
+ else:
+ if bplace:
+ if ddate:
+ if dplace:
+ text = _("Born: %(birth_place)s, "
+ "Died: %(death_date)s %(death_place)s.") % values
+ else:
+ text = _("Born: %(birth_place)s, "
+ "Died: %(death_date)s.") % values
+ else:
+ if dplace:
+ text = _("Born: %(birth_place)s, "
+ "Died: %(death_place)s.") % values
+ else:
+ text = _("Born: %(birth_place)s.") % values
+ else:
+ if ddate:
+ if dplace:
+ text = _("Died: %(death_date)s %(death_place)s.") % values
+ else:
+ text = _("Died: %(death_date)s.") % values
+ else:
+ if dplace:
+ text = _("Died: %(death_place)s.") % values
+ else:
+ text = ""
+ return text
+
+
+#-------------------------------------------------------------------------
+#
+# relationship_name
+#
+#-------------------------------------------------------------------------
+_rtype = {
+ RelLib.Family.MARRIED : _("Married"),
+ RelLib.Family.UNMARRIED : _("Unmarried"),
+ RelLib.Family.CIVIL_UNION : _("Civil Union"),
+ RelLib.Family.UNKNOWN : _("Unknown"),
+ RelLib.Family.CUSTOM : _("Other"),
+ }
+
+def relationship_name(rtype):
+ return _rtype.get(rtype)
+
+def old_calc_age(database,person):
+ """
+ Calulate age.
+
+ Returns a tuple (age,units) where units is an integer representing
+ time units:
+ no age info: 0
+ years: 1
+ months: 2
+ days: 3
+ """
+ YEARS = 1
+ MONTHS = 2
+ DAYS = 3
+
+ # FIXME: This is an old and ugly implementation.
+ # It must be changed to use the new age calculator.
+ age = 0
+ units = 0
+
+ birth_handle = person.get_birth_handle()
+ if birth_handle:
+ birth = database.get_event_from_handle(birth_handle).get_date_object()
+ birth_year_valid = birth.get_year_valid()
+ else:
+ birth_year_valid = None
+ death_handle = person.get_death_handle()
+ if death_handle:
+ death = database.get_event_from_handle(death_handle).get_date_object()
+ death_year_valid = death.get_year_valid()
+ else:
+ death_year_valid = None
+
+ # wihtout at least a year for each event we're clueless
+ if not (birth_year_valid and death_year_valid):
+ return (age,units)
+
+ # FIXME: The code below uses hard-coded 31 days in a month
+ # and 12 month in a year. This is incorrect for half the Gregorian
+ # months and for other calendars.
+ # FIXME: We need to move to estimate_age !!!
+
+ # If born and died in the same year, go to the months
+ if death.get_year() == birth.get_year():
+ if birth.get_month_valid() and death.get_month_valid():
+ # if born and died in the same month, do the days
+ if birth.get_month() == death.get_month() \
+ and birth.get_day_valid() and death.get_day_valid():
+ age = death.get_day() - birth.get_day()
+ units = DAYS
+ # if not the same month, just diff the months
+ else:
+ age = death.get_month() - birth.get_month()
+ units = MONTHS
+ # Born and died in different years
+ else:
+ age = death.get_year() - birth.get_year()
+ units = YEARS
+ if birth.get_month_valid() and death.get_month_valid():
+ # Subtract one year if less than a last full year
+ if birth.get_month() > death.get_month():
+ age = age - 1
+
+ # If less than a year (but still in different years)
+ # then calculate month diff modulo 12
+ if age == 0:
+ age = 12 + death.get_month() - birth.get_month()
+ units = MONTHS
+
+ # This is the case of birth on Dec 30 and death on Jan 2
+ # or birth on May 30 and death on June 2
+ if age == 1 and units == MONTHS \
+ and birth.get_day_valid() and death.get_day_valid() \
+ and birth.get_day() > death.get_day():
+ age = death.get_day() + 31 - birth.get_day()
+ unit = DAYS
+
+ return (age,units)
+
+
+def common_name(person,use_nick=False):
+ if use_nick and person.get_nick_name():
+ return person.get_nick_name()
+ else:
+ return person.get_primary_name().get_first_name()
diff --git a/gramps2/src/PluginUtils/_Tool.py b/gramps2/src/PluginUtils/_Tool.py
new file mode 100644
index 000000000..09b0f9652
--- /dev/null
+++ b/gramps2/src/PluginUtils/_Tool.py
@@ -0,0 +1,279 @@
+#
+# Gramps - a GTK+/GNOME based genealogy program
+#
+# Copyright (C) 2005-2006 Donald N. Allingham
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+# $Id$
+
+"ToolGeneration Framework"
+
+__author__ = "Alex Roitman"
+__version__ = "$Revision$"
+
+#-------------------------------------------------------------------------
+#
+# Python modules
+#
+#-------------------------------------------------------------------------
+from types import ClassType, InstanceType
+from gettext import gettext as _
+import logging
+log = logging.getLogger(".")
+
+#-------------------------------------------------------------------------
+#
+# GRAMPS modules
+#
+#-------------------------------------------------------------------------
+import const
+import Utils
+import GenericFilter
+import NameDisplay
+from _Options import *
+
+#-------------------------------------------------------------------------
+#
+# Constants
+#
+#-------------------------------------------------------------------------
+# Modes for running tools
+MODE_GUI = 1 # Standrt tool using GUI
+MODE_CLI = 2 # Command line interface (CLI)
+
+# Tool categories
+TOOL_DEBUG = -1
+TOOL_ANAL = 0
+TOOL_DBPROC = 1
+TOOL_DBFIX = 2
+TOOL_REVCTL = 3
+TOOL_UTILS = 4
+
+tool_categories = {
+ TOOL_DEBUG : _("Debug"),
+ TOOL_ANAL : _("Analysis and Exploration"),
+ TOOL_DBPROC : _("Database Processing"),
+ TOOL_DBFIX : _("Database Repair"),
+ TOOL_REVCTL : _("Revision Control"),
+ TOOL_UTILS : _("Utilities"),
+ }
+
+#-------------------------------------------------------------------------
+#
+# Report
+#
+#-------------------------------------------------------------------------
+class Tool:
+ """
+ The Tool base class. This is a base class for generating
+ customized tools. It cannot be used as is, but it can be easily
+ sub-classed to create a functional tool.
+ """
+
+ def __init__(self,database,person,options_class,name):
+ self.db = database
+ self.person = person
+ if type(options_class) == ClassType:
+ self.options = options_class(name)
+ elif type(options_class) == InstanceType:
+ self.options = options_class
+
+ def run_tool(self):
+ pass
+
+
+#------------------------------------------------------------------------
+#
+# Command-line tool
+#
+#------------------------------------------------------------------------
+class CommandLineTool:
+ """
+ Provides a way to run tool from the command line.
+
+ """
+
+ def __init__(self,database,name,category,option_class,options_str_dict,
+ noopt=False):
+ self.database = database
+ self.category = category
+ self.option_class = option_class(name)
+ self.show = options_str_dict.pop('show',None)
+ self.options_str_dict = options_str_dict
+ self.init_options(noopt)
+ self.parse_option_str()
+ self.show_options()
+
+ def init_options(self,noopt):
+ self.options_dict = {
+ 'id' : ''
+ }
+
+ self.options_help = {
+ 'id' : ["=ID","Gramps ID of a central person."],
+ 'filter' : ["=num","Filter number."],
+ }
+
+ if noopt:
+ return
+
+ # Add tool-specific options
+ for key in self.option_class.handler.options_dict.keys():
+ if key not in self.options_dict.keys():
+ self.options_dict[key] = self.option_class.handler.options_dict[key]
+
+ # Add help for tool-specific options
+ for key in self.option_class.options_help.keys():
+ if key not in self.options_help.keys():
+ self.options_help[key] = self.option_class.options_help[key]
+
+ def parse_option_str(self):
+ for opt in self.options_str_dict.keys():
+ if opt in self.options_dict.keys():
+ converter = Utils.get_type_converter(self.options_dict[opt])
+ self.options_dict[opt] = converter(self.options_str_dict[opt])
+ self.option_class.handler.options_dict[opt] = self.options_dict[opt]
+ else:
+ print "Ignoring unknown option: %s" % opt
+
+ person_id = self.options_dict['id']
+ self.person = self.database.get_person_from_gramps_id(person_id)
+ id_list = []
+ for person_handle in self.database.get_person_handles():
+ person = self.database.get_person_from_handle(person_handle)
+ id_list.append("%s\t%s" % (
+ person.get_gramps_id(),
+ NameDisplay.displayer.display(person)))
+ self.options_help['id'].append(id_list)
+ self.options_help['id'].append(False)
+
+ if self.options_dict.has_key('filter'):
+ filter_num = self.options_dict['filter']
+ self.filters = self.option_class.get_report_filters(self.person)
+ self.option_class.handler.set_filter_number(filter_num)
+
+ filt_list = [ filt.get_name() for filt in self.filters ]
+ cust_filt_list = [ filt2.get_name() for filt2 in
+ GenericFilter.CustomFilters.get_filters() ]
+ filt_list.extend(cust_filt_list)
+ self.options_help['filter'].append(filt_list)
+ self.options_help['filter'].append(True)
+
+ def show_options(self):
+ if not self.show:
+ return
+ elif self.show == 'all':
+ print " Available options:"
+ for key in self.options_dict.keys():
+ print " %s" % key
+ print " Use 'show=option' to see description and acceptable values"
+ elif self.show in self.options_dict.keys():
+ print ' %s%s\t%s' % (self.show,
+ self.options_help[self.show][0],
+ self.options_help[self.show][1])
+ print " Available values are:"
+ vals = self.options_help[self.show][2]
+ if type(vals) in [list,tuple]:
+ if self.options_help[self.show][3]:
+ for num in range(len(vals)):
+ print " %d\t%s" % (num,vals[num])
+ else:
+ for val in vals:
+ print " %s" % val
+ else:
+ print " %s" % self.options_help[self.show][2]
+
+ else:
+ self.show = None
+
+#------------------------------------------------------------------------
+#
+# Generic task functions for tools
+#
+#------------------------------------------------------------------------
+# Standard GUI tool generic task
+def gui_tool(database,person,tool_class,options_class,translated_name,
+ name,category,callback,parent):
+ """
+ tool - task starts the report. The plugin system requires that the
+ task be in the format of task that takes a database and a person as
+ its arguments.
+ """
+
+ try:
+ tool_class(database,person,options_class,name,callback,parent)
+ except:
+ log.error("Failed to start tool.", exc_info=True)
+
+# Command-line generic task
+def cli_tool(database,name,category,tool_class,options_class,options_str_dict):
+
+ clt = CommandLineTool(database,name,category,
+ options_class,options_str_dict)
+
+ # Exit here if show option was given
+ if clt.show:
+ return
+
+ # run tool
+ try:
+ tool_class(database,clt.person,clt.option_class,name)
+ except:
+ log.error("Failed to start tool.", exc_info=True)
+
+#-------------------------------------------------------------------------
+#
+# Class handling options for plugins
+#
+#-------------------------------------------------------------------------
+class OptionHandler(OptionHandler):
+ """
+ Implements handling of the options for the plugins.
+ """
+ def __init__(self,module_name,options_dict,person_id=None):
+ OptionHandler.__init__(self,module_name,options_dict,person_id)
+
+ def init_subclass(self):
+ self.collection_class = OptionListCollection
+ self.list_class = OptionList
+ self.filename = const.tool_options
+
+#------------------------------------------------------------------------
+#
+# Tool Options class
+#
+#------------------------------------------------------------------------
+class ToolOptions(Options):
+
+ """
+ Defines options and provides handling interface.
+
+ This is a base Options class for the tools. All tools' options
+ classes should derive from it.
+ """
+
+ def __init__(self,name,person_id=None):
+ """
+ Initializes the class, performing usual house-keeping tasks.
+ Subclasses MUST call this in their __init__() method.
+ """
+ self.set_new_options()
+ self.enable_options()
+
+ if self.enable_dict:
+ self.options_dict.update(self.enable_dict)
+ self.handler = OptionHandler(name,self.options_dict,person_id)
diff --git a/gramps2/src/PluginUtils/__init__.py b/gramps2/src/PluginUtils/__init__.py
index 210db24d7..f930247a9 100644
--- a/gramps2/src/PluginUtils/__init__.py
+++ b/gramps2/src/PluginUtils/__init__.py
@@ -20,6 +20,5 @@
# $Id: Report.py 6044 2006-03-03 00:10:52Z rshura $
-from _Report import *
-from _Tool import *
-from _ReportUtils import *
+import Report
+import Tool