From 0ab5eb9a0d8026f75db76108448e62d5161e1357 Mon Sep 17 00:00:00 2001 From: Alex Roitman Date: Wed, 31 May 2006 00:48:07 +0000 Subject: [PATCH] 2006-05-30 Alex Roitman * various: break Report into a package. svn: r6820 --- ChangeLog | 3 + src/DataViews/_PedigreeView.py | 2 +- src/DataViews/_RelationView.py | 2 +- src/Editors/_EditFamily.py | 2 +- src/PluginUtils/Report/Makefile.am | 37 + src/PluginUtils/Report/_BareReportDialog.py | 642 +++++ src/PluginUtils/Report/_BookFormatComboBox.py | 71 + .../Report/_BookReport.py} | 49 +- src/PluginUtils/Report/_CommandLineReport.py | 265 +++ src/PluginUtils/Report/_Constants.py | 63 + src/PluginUtils/Report/_DrawFormatComboBox.py | 78 + src/PluginUtils/Report/_DrawReportDialog.py | 50 + src/PluginUtils/Report/_FileEntry.py | 88 + src/PluginUtils/{ => Report}/_PaperMenu.py | 0 src/PluginUtils/Report/_PrintTools.py | 52 + src/PluginUtils/Report/_Report.py | 141 ++ src/PluginUtils/Report/_ReportDialog.py | 609 +++++ .../{ => Report}/_ReportOptions.py | 2 +- src/PluginUtils/{ => Report}/_ReportUtils.py | 0 src/PluginUtils/Report/_StyleComboBox.py | 85 + src/PluginUtils/{ => Report}/_StyleEditor.py | 0 src/PluginUtils/Report/_TemplateParser.py | 111 + src/PluginUtils/Report/_TextFormatComboBox.py | 78 + src/PluginUtils/Report/_TextReportDialog.py | 64 + src/PluginUtils/Report/__init__.py | 38 + src/PluginUtils/_Plugins.py | 6 +- src/PluginUtils/_Report.py | 2099 ----------------- src/PluginUtils/__init__.py | 3 - src/plugins/Makefile.am | 1 - 29 files changed, 2519 insertions(+), 2122 deletions(-) create mode 100644 src/PluginUtils/Report/Makefile.am create mode 100644 src/PluginUtils/Report/_BareReportDialog.py create mode 100644 src/PluginUtils/Report/_BookFormatComboBox.py rename src/{plugins/BookReport.py => PluginUtils/Report/_BookReport.py} (96%) create mode 100644 src/PluginUtils/Report/_CommandLineReport.py create mode 100644 src/PluginUtils/Report/_Constants.py create mode 100644 src/PluginUtils/Report/_DrawFormatComboBox.py create mode 100644 src/PluginUtils/Report/_DrawReportDialog.py create mode 100644 src/PluginUtils/Report/_FileEntry.py rename src/PluginUtils/{ => Report}/_PaperMenu.py (100%) create mode 100644 src/PluginUtils/Report/_PrintTools.py create mode 100644 src/PluginUtils/Report/_Report.py create mode 100644 src/PluginUtils/Report/_ReportDialog.py rename src/PluginUtils/{ => Report}/_ReportOptions.py (99%) rename src/PluginUtils/{ => Report}/_ReportUtils.py (100%) create mode 100644 src/PluginUtils/Report/_StyleComboBox.py rename src/PluginUtils/{ => Report}/_StyleEditor.py (100%) create mode 100644 src/PluginUtils/Report/_TemplateParser.py create mode 100644 src/PluginUtils/Report/_TextFormatComboBox.py create mode 100644 src/PluginUtils/Report/_TextReportDialog.py create mode 100644 src/PluginUtils/Report/__init__.py delete mode 100644 src/PluginUtils/_Report.py diff --git a/ChangeLog b/ChangeLog index 50ea12449..849ee8f4a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +2006-05-30 Alex Roitman + * various: break Report into a package. + 2006-05-29 Brian Matherly * src/plugins/DetDescendantReport.py: insert name keys for indexing. * src/plugins/DetAncestralReport.py: insert name keys for indexing. diff --git a/src/DataViews/_PedigreeView.py b/src/DataViews/_PedigreeView.py index 425a43f0e..a8e5dfc31 100644 --- a/src/DataViews/_PedigreeView.py +++ b/src/DataViews/_PedigreeView.py @@ -56,7 +56,7 @@ import Utils import DateHandler import ImgManip import Errors -from PluginUtils import ReportUtils +from PluginUtils.Report import ReportUtils from Editors import EditPerson from DdTargets import DdTargets import cPickle as pickle diff --git a/src/DataViews/_RelationView.py b/src/DataViews/_RelationView.py index 08b211887..bb9717f3a 100644 --- a/src/DataViews/_RelationView.py +++ b/src/DataViews/_RelationView.py @@ -28,7 +28,7 @@ import GrampsWidgets import Errors import GrampsDb -from PluginUtils import ReportUtils +from PluginUtils.Report import ReportUtils _GenderCode = { RelLib.Person.MALE : u'\u2642', diff --git a/src/Editors/_EditFamily.py b/src/Editors/_EditFamily.py index 2c03e1e72..2b96a21fa 100644 --- a/src/Editors/_EditFamily.py +++ b/src/Editors/_EditFamily.py @@ -71,7 +71,7 @@ import Config import Errors from _EditPrimary import EditPrimary -from PluginUtils import ReportUtils +from PluginUtils.Report import ReportUtils from DdTargets import DdTargets from DisplayTabs import \ EmbeddedList,EventEmbedList,SourceEmbedList,FamilyAttrEmbedList,\ diff --git a/src/PluginUtils/Report/Makefile.am b/src/PluginUtils/Report/Makefile.am new file mode 100644 index 000000000..ac4b536f0 --- /dev/null +++ b/src/PluginUtils/Report/Makefile.am @@ -0,0 +1,37 @@ +# This is the src/Report level Makefile for Gramps + +pkgdatadir = $(datadir)/@PACKAGE@/Report + +pkgdata_PYTHON = \ + _BareReportDialog.py\ + _BookFormatComboBox.py\ + _BookReport.py\ + _CommandLineReport.py\ + _Constants.py\ + _DrawFormatComboBox.py\ + _DrawReportDialog.py\ + _FileEntry.py\ + __init__.py\ + _PaperMenu.py\ + _PrintTools.py\ + _ReportDialog.py\ + _ReportUtils.py\ + _ReportOptions.py\ + _Report.py\ + _StyleComboBox.py\ + _StyleEditor.py\ + _TemplateParser.py\ + _TextFormatComboBox.py\ + _TextReportDialog.py + +pkgpyexecdir = @pkgpyexecdir@/Report +pkgpythondir = @pkgpythondir@/Report + +# Clean up all the byte-compiled files +MOSTLYCLEANFILES = *pyc *pyo + +GRAMPS_PY_MODPATH = "../" + +pycheck: + (export PYTHONPATH=$(GRAMPS_PY_MODPATH); \ + pychecker $(pkgdata_PYTHON)); diff --git a/src/PluginUtils/Report/_BareReportDialog.py b/src/PluginUtils/Report/_BareReportDialog.py new file mode 100644 index 000000000..10269d244 --- /dev/null +++ b/src/PluginUtils/Report/_BareReportDialog.py @@ -0,0 +1,642 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2001-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: _Report.py 6669 2006-05-15 15:53:42Z rshura $ + + +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 = FilterComboBox() + 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 = Rules.Person.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.""" + StyleListDisplay(self.style_sheet_list,self.build_style_menu, + self.window) + + def on_center_person_change_clicked(self,*obj): + from Selectors import selector_factory + SelectPerson = selector_factory('Person') + sel_person = 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) diff --git a/src/PluginUtils/Report/_BookFormatComboBox.py b/src/PluginUtils/Report/_BookFormatComboBox.py new file mode 100644 index 000000000..b797551e5 --- /dev/null +++ b/src/PluginUtils/Report/_BookFormatComboBox.py @@ -0,0 +1,71 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2001-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: _Report.py 6669 2006-05-15 15:53:42Z rshura $ + +#------------------------------------------------------------------------- +# +# get_text_doc_menu +# +#------------------------------------------------------------------------- +class BookFormatComboBox(gtk.ComboBox): + + def set(self,tables,callback,obj=None,active=None): + self.store = gtk.ListStore(str) + self.set_model(self.store) + cell = gtk.CellRendererText() + self.pack_start(cell,True) + self.add_attribute(cell,'text',0) + + out_pref = Config.get(Config.OUTPUT_PREFERENCE) + index = 0 + _PluginMgr.drawdoc_list.sort() + active_index = 0 + self.data = [] + for item in _PluginMgr.bookdoc_list: + if tables and item[2] == 0: + continue + self.data.append(item) + name = item[0] + self.store.append(row=[name]) + if item[7] == active: + active_index = index + elif not active and name == out_pref: + active_index = index + index += 1 + self.set_active(active_index) + + def get_reference(self): + return self.data[self.get_active()][1] + + def get_label(self): + return self.data[self.get_active()][0] + + def get_paper(self): + return self.data[self.get_active()][3] + + def get_ext(self): + return self.data[self.get_active()][5] + + def get_printable(self): + return self.data[self.get_active()][6] + + def get_clname(self): + return self.data[self.get_active()][7] diff --git a/src/plugins/BookReport.py b/src/PluginUtils/Report/_BookReport.py similarity index 96% rename from src/plugins/BookReport.py rename to src/PluginUtils/Report/_BookReport.py index 506c06ef3..f026432d4 100644 --- a/src/plugins/BookReport.py +++ b/src/PluginUtils/Report/_BookReport.py @@ -66,6 +66,7 @@ from RelLib import Person import const import Utils import ListModel +import Errors from PluginUtils import Plugins, Report, ReportOptions, \ bkitems_list, register_report import BaseDoc @@ -1112,20 +1113,44 @@ def cl_report(database,name,category,options_str_dict): item.write_report() doc.close() +#------------------------------------------------------------------------ +# +# Generic task function for book report +# +#------------------------------------------------------------------------ +# 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 + #------------------------------------------------------------------------ # # # #------------------------------------------------------------------------ -register_report( - name = 'book', - category = Report.CATEGORY_BOOK, - report_class = BookReportSelector, - options_class = cl_report, - modes = Report.MODE_GUI | Report.MODE_CLI, - translated_name = _("Book Report"), - status = _("Stable"), - description = _("Creates a book containing several reports."), - author_name = "Alex Roitman", - author_email = "shura@gramps-project.org" - ) +## register_report( +## name = 'book', +## category = Report.CATEGORY_BOOK, +## report_class = BookReportSelector, +## options_class = cl_report, +## modes = Report.MODE_GUI | Report.MODE_CLI, +## translated_name = _("Book Report"), +## status = _("Stable"), +## description = _("Creates a book containing several reports."), +## author_name = "Alex Roitman", +## author_email = "shura@gramps-project.org" +## ) diff --git a/src/PluginUtils/Report/_CommandLineReport.py b/src/PluginUtils/Report/_CommandLineReport.py new file mode 100644 index 000000000..079c32743 --- /dev/null +++ b/src/PluginUtils/Report/_CommandLineReport.py @@ -0,0 +1,265 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2001-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: _Report.py 6669 2006-05-15 15:53:42Z rshura $ + +import logging +log = logging.getLogger(".") + +import PluginUtils +import NameDisplay +import Utils +import BaseDoc +from Report import CATEGORY_TEXT, CATEGORY_DRAW, CATEGORY_BOOK +from _PaperMenu import paper_sizes + +#------------------------------------------------------------------------ +# +# 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 + 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.join(const.user_home, + "whatever_name")) + + if self.category == CATEGORY_TEXT: + for item in PluginUtils.textdoc_list: + if item[7] == self.options_dict['off']: + self.format = item[1] + self.options_help['off'].append( + [ item[7] for item in PluginUtils.textdoc_list ] + ) + self.options_help['off'].append(False) + elif self.category == CATEGORY_DRAW: + for item in PluginUtils.drawdoc_list: + if item[6] == self.options_dict['off']: + self.format = item[1] + self.options_help['off'].append( + [ item[6] for item in PluginUtils.drawdoc_list ] + ) + self.options_help['off'].append(False) + elif self.category == CATEGORY_BOOK: + for item in PluginUtils.bookdoc_list: + if item[6] == self.options_dict['off']: + self.format = item[1] + self.options_help['off'].append( + [ item[6] for item in PluginUtils.bookdoc_list ] + ) + self.options_help['off'].append(False) + else: + self.format = None + + for paper in 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 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.join(const.user_home, + "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 + +#------------------------------------------------------------------------ +# +# Command-line report 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/src/PluginUtils/Report/_Constants.py b/src/PluginUtils/Report/_Constants.py new file mode 100644 index 000000000..6f0d5c7e2 --- /dev/null +++ b/src/PluginUtils/Report/_Constants.py @@ -0,0 +1,63 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2001-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: _Report.py 6669 2006-05-15 15:53:42Z rshura $ + +"Report Generation Framework" + +#------------------------------------------------------------------------- +# +# standard python modules +# +#------------------------------------------------------------------------- +from gettext import gettext as _ + +#------------------------------------------------------------------------- +# +# Constants +# +#------------------------------------------------------------------------- + +# 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"), +} diff --git a/src/PluginUtils/Report/_DrawFormatComboBox.py b/src/PluginUtils/Report/_DrawFormatComboBox.py new file mode 100644 index 000000000..fa3f45d28 --- /dev/null +++ b/src/PluginUtils/Report/_DrawFormatComboBox.py @@ -0,0 +1,78 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2001-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: _Report.py 6669 2006-05-15 15:53:42Z rshura $ + +import gtk +import Config +import PluginUtils + +#------------------------------------------------------------------------- +# +# get_text_doc_menu +# +#------------------------------------------------------------------------- +class DrawFormatComboBox(gtk.ComboBox): + + def set(self,tables,callback,obj=None,active=None): + self.store = gtk.ListStore(str) + self.set_model(self.store) + cell = gtk.CellRendererText() + self.pack_start(cell,True) + self.add_attribute(cell,'text',0) + + out_pref = Config.get(Config.OUTPUT_PREFERENCE) + index = 0 + PluginUtils.drawdoc_list.sort() + active_index = 0 + for item in PluginUtils.drawdoc_list: + if tables and item[2] == 0: + continue + name = item[0] + self.store.append(row=[name]) + #if callback: + # menuitem.connect("activate",callback) + if item[6] == active: + active_index = index + elif not active and name == out_pref: + active_index = index + index = index + 1 + self.set_active(active_index) + + def get_reference(self): + return PluginUtils.drawdoc_list[self.get_active()][1] + + def get_label(self): + return PluginUtils.drawdoc_list[self.get_active()][0] + + def get_paper(self): + return PluginUtils.drawdoc_list[self.get_active()][2] + + def get_styles(self): + return PluginUtils.drawdoc_list[self.get_active()][3] + + def get_ext(self): + return PluginUtils.drawdoc_list[self.get_active()][4] + + def get_printable(self): + return PluginUtil.drawdoc_list[self.get_active()][5] + + def get_clname(self): + return PluginUtils.drawdoc_list[self.get_active()][6] diff --git a/src/PluginUtils/Report/_DrawReportDialog.py b/src/PluginUtils/Report/_DrawReportDialog.py new file mode 100644 index 000000000..c5bce51f3 --- /dev/null +++ b/src/PluginUtils/Report/_DrawReportDialog.py @@ -0,0 +1,50 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2001-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: _Report.py 6669 2006-05-15 15:53:42Z rshura $ + +from _Constants import CATEGORY_DRAW +from _ReportDialog import ReportDialog +from _DrawFormatComboBox import DrawFormatComboBox + +#----------------------------------------------------------------------- +# +# 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 = DrawFormatComboBox() + self.format_menu.set(False,self.doc_type_changed, None, active) diff --git a/src/PluginUtils/Report/_FileEntry.py b/src/PluginUtils/Report/_FileEntry.py new file mode 100644 index 000000000..5e18cd42c --- /dev/null +++ b/src/PluginUtils/Report/_FileEntry.py @@ -0,0 +1,88 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2001-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: _Report.py 6669 2006-05-15 15:53:42Z rshura $ + +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 diff --git a/src/PluginUtils/_PaperMenu.py b/src/PluginUtils/Report/_PaperMenu.py similarity index 100% rename from src/PluginUtils/_PaperMenu.py rename to src/PluginUtils/Report/_PaperMenu.py diff --git a/src/PluginUtils/Report/_PrintTools.py b/src/PluginUtils/Report/_PrintTools.py new file mode 100644 index 000000000..1e0c2409b --- /dev/null +++ b/src/PluginUtils/Report/_PrintTools.py @@ -0,0 +1,52 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2001-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: _Report.py 6669 2006-05-15 15:53:42Z rshura $ + +"Report Generation Framework" + +#------------------------------------------------------------------------- +# +# standard python modules +# +#------------------------------------------------------------------------- +import os +from gettext import gettext as _ + +#------------------------------------------------------------------------- +# +# 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 ()) diff --git a/src/PluginUtils/Report/_Report.py b/src/PluginUtils/Report/_Report.py new file mode 100644 index 000000000..40c6c1509 --- /dev/null +++ b/src/PluginUtils/Report/_Report.py @@ -0,0 +1,141 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2001 David R. Hampton +# Copyright (C) 2001-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: _Report.py 6669 2006-05-15 15:53:42Z rshura $ + +import Utils + +#------------------------------------------------------------------------- +# +# 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) diff --git a/src/PluginUtils/Report/_ReportDialog.py b/src/PluginUtils/Report/_ReportDialog.py new file mode 100644 index 000000000..7c3dc5d2e --- /dev/null +++ b/src/PluginUtils/Report/_ReportDialog.py @@ -0,0 +1,609 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2001-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: _Report.py 6669 2006-05-15 15:53:42Z rshura $ + +from _BareReportDialog import BareReportDialog + +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(Config.REPORT_DIRECTORY) + + 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.set(Config.REPORT_DIRECTORY,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 name == "Custom Size": + self.pwidth.set_sensitive(1) + self.pheight.set_sensitive(1) + else: + self.pwidth.set_sensitive(0) + self.pheight.set_sensitive(0) + + if paper.get_width() > 0 and paper.get_height() > 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() + +#------------------------------------------------------------------------ +# +# Generic task function a standalone GUI report +# +#------------------------------------------------------------------------ +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() diff --git a/src/PluginUtils/_ReportOptions.py b/src/PluginUtils/Report/_ReportOptions.py similarity index 99% rename from src/PluginUtils/_ReportOptions.py rename to src/PluginUtils/Report/_ReportOptions.py index 641704d15..98298bcac 100644 --- a/src/PluginUtils/_ReportOptions.py +++ b/src/PluginUtils/Report/_ReportOptions.py @@ -51,7 +51,7 @@ import const import Config import Utils import BaseDoc -import _Options +from PluginUtils import _Options #------------------------------------------------------------------------- # diff --git a/src/PluginUtils/_ReportUtils.py b/src/PluginUtils/Report/_ReportUtils.py similarity index 100% rename from src/PluginUtils/_ReportUtils.py rename to src/PluginUtils/Report/_ReportUtils.py diff --git a/src/PluginUtils/Report/_StyleComboBox.py b/src/PluginUtils/Report/_StyleComboBox.py new file mode 100644 index 000000000..cd9a71d79 --- /dev/null +++ b/src/PluginUtils/Report/_StyleComboBox.py @@ -0,0 +1,85 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2001-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: _Report.py 6669 2006-05-15 15:53:42Z rshura $ + +#------------------------------------------------------------------------- +# +# StyleComboBox +# +#------------------------------------------------------------------------- +class StyleComboBox(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]) diff --git a/src/PluginUtils/_StyleEditor.py b/src/PluginUtils/Report/_StyleEditor.py similarity index 100% rename from src/PluginUtils/_StyleEditor.py rename to src/PluginUtils/Report/_StyleEditor.py diff --git a/src/PluginUtils/Report/_TemplateParser.py b/src/PluginUtils/Report/_TemplateParser.py new file mode 100644 index 000000000..ad319b48a --- /dev/null +++ b/src/PluginUtils/Report/_TemplateParser.py @@ -0,0 +1,111 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2001-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: _Report.py 6669 2006-05-15 15:53:42Z rshura $ + +#----------------------------------------------------------------------- +# +# Python modules +# +#----------------------------------------------------------------------- + +from gettext import gettext as _ +import os + +#----------------------------------------------------------------------- +# +# XML modules +# +#----------------------------------------------------------------------- +try: + from xml.sax import make_parser,handler,SAXParseException +except: + from _xmlplus.sax import make_parser,handler,SAXParseException + +#----------------------------------------------------------------------- +# +# GRAMPS modules +# +#----------------------------------------------------------------------- +import const + +#----------------------------------------------------------------------- +# +# 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