From df32be43deeee39ea69a687cea1bb04555665ef3 Mon Sep 17 00:00:00 2001 From: Brian Matherly Date: Sat, 1 May 2010 04:12:42 +0000 Subject: [PATCH] 0003796: Make export available when no GUI available. Patch from jmodule (Jakim Friant). svn: r15294 --- autogen.sh | 2 +- configure.in | 6 +- src/ExportAssistant.py | 3 + src/Makefile.am | 2 - src/Merge/_MergePerson.py | 3 +- src/PluginUtils/Makefile.am | 26 - src/PluginUtils/_GuiOptions.py | 1538 ------ src/PluginUtils/_Options.py | 476 -- src/PluginUtils/_PluginDialogs.py | 316 -- src/PluginUtils/_PluginWindows.py | 1221 ----- src/PluginUtils/_Tool.py | 310 -- src/PluginUtils/__init__.py | 47 - src/ReportBase/Makefile.am | 35 - src/ReportBase/_Bibliography.py | 242 - src/ReportBase/_CommandLineReport.py | 486 -- src/ReportBase/_Constants.py | 75 - src/ReportBase/_DocReportDialog.py | 261 - src/ReportBase/_DrawReportDialog.py | 107 - src/ReportBase/_Endnotes.py | 179 - src/ReportBase/_FileEntry.py | 101 - src/ReportBase/_GraphvizReportDialog.py | 1193 ----- src/ReportBase/_PaperMenu.py | 383 -- src/ReportBase/_Report.py | 62 - src/ReportBase/_ReportDialog.py | 648 --- src/ReportBase/_ReportOptions.py | 819 --- src/ReportBase/_ReportUtils.py | 289 -- src/ReportBase/_StyleComboBox.py | 86 - src/ReportBase/_StyleEditor.py | 378 -- src/ReportBase/_TextReportDialog.py | 108 - src/ReportBase/_WebReportDialog.py | 57 - src/ReportBase/__init__.py | 40 - src/Simple/_SimpleAccess.py | 11 +- src/cli/Makefile.am | 3 + src/cli/arghandler.py | 18 +- src/cli/plug/Makefile.am | 66 + src/cli/plug/__init__.py | 1467 ++++++ src/gen/plug/Makefile.am | 7 +- src/gen/plug/__init__.py | 2 + src/gen/plug/_gramplet.py | 3 +- src/gen/plug/_options.py | 1431 +++++ src/gen/plug/report/Makefile.am | 84 + src/gen/plug/report/__init__.py | 102 + src/gen/plug/report/_bibliography.py | 729 +++ src/gen/plug/report/_constants.py | 228 + src/gen/plug/report/_options.py | 2463 +++++++++ src/gen/plug/report/_paper.py | 342 ++ src/gen/plug/report/_reportbase.py | 189 + src/gen/plug/report/endnotes.py | 540 ++ src/gen/plug/report/utils.py | 873 ++++ src/gui/Makefile.am | 1 + src/gui/plug/Makefile.am | 78 + src/gui/plug/__init__.py | 150 + src/gui/plug/_dialogs.py | 954 ++++ src/gui/plug/_guioptions.py | 4617 +++++++++++++++++ src/gui/plug/_windows.py | 3666 +++++++++++++ src/gui/plug/report/Makefile.am | 87 + src/gui/plug/report/__init__.py | 105 + src/gui/plug/report/_docreportdialog.py | 786 +++ src/gui/plug/report/_drawreportdialog.py | 324 ++ src/gui/plug/report/_fileentry.py | 306 ++ src/gui/plug/report/_graphvizreportdialog.py | 3582 +++++++++++++ src/gui/plug/report/_options.py | 183 + src/gui/plug/report/_papermenu.py | 921 ++++ src/gui/plug/report/_reportdialog.py | 1947 +++++++ src/gui/plug/report/_stylecombobox.py | 261 + src/gui/plug/report/_styleeditor.py | 1137 ++++ src/gui/plug/report/_textreportdialog.py | 327 ++ src/gui/plug/report/_webreportdialog.py | 174 + src/gui/plug/tool.py | 933 ++++ src/gui/viewmanager.py | 29 +- src/plugins/BookReport.py | 11 +- src/plugins/Records.py | 5 +- src/plugins/docgen/ODFDoc.py | 3 +- src/plugins/docgen/PSDrawDoc.py | 3 +- src/plugins/drawreport/AncestorTree.py | 5 +- src/plugins/drawreport/Calendar.py | 5 +- src/plugins/drawreport/DescendTree.py | 5 +- src/plugins/drawreport/FanChart.py | 5 +- src/plugins/drawreport/StatisticsChart.py | 5 +- src/plugins/drawreport/TimeLine.py | 5 +- src/plugins/export/ExportCsv.py | 13 +- src/plugins/export/ExportFtree.py | 11 +- src/plugins/export/ExportGedcom.py | 8 +- src/plugins/export/ExportGeneWeb.py | 15 +- src/plugins/export/ExportPkg.py | 10 +- src/plugins/export/ExportVCalendar.py | 11 +- src/plugins/export/ExportVCard.py | 11 +- src/plugins/export/ExportXml.py | 14 +- src/plugins/gramplet/DescendGramplet.py | 3 +- src/plugins/gramplet/WhatsNext.py | 3 +- src/plugins/graph/GVFamilyLines.py | 5 +- src/plugins/graph/GVHourGlass.py | 5 +- src/plugins/graph/GVRelGraph.py | 5 +- src/plugins/lib/libcairodoc.py | 3 +- src/plugins/lib/libnarrate.py | 3 +- src/plugins/textreport/AncestorReport.py | 5 +- src/plugins/textreport/BirthdayReport.py | 5 +- src/plugins/textreport/CustomBookText.py | 4 +- src/plugins/textreport/DescendReport.py | 6 +- src/plugins/textreport/DetAncestralReport.py | 14 +- src/plugins/textreport/DetDescendantReport.py | 14 +- src/plugins/textreport/EndOfLineReport.py | 5 +- src/plugins/textreport/FamilyGroup.py | 5 +- src/plugins/textreport/IndivComplete.py | 8 +- src/plugins/textreport/KinshipReport.py | 5 +- src/plugins/textreport/MarkerReport.py | 5 +- .../textreport/NumberOfAncestorsReport.py | 5 +- src/plugins/textreport/PlaceReport.py | 4 +- src/plugins/textreport/SimpleBookTitle.py | 4 +- src/plugins/textreport/Summary.py | 5 +- src/plugins/tool/ChangeNames.py | 14 +- src/plugins/tool/ChangeTypes.py | 11 +- src/plugins/tool/Check.py | 11 +- src/plugins/tool/DateParserDisplayTest.py | 11 +- src/plugins/tool/Desbrowser.py | 11 +- src/plugins/tool/DumpGenderStats.py | 12 +- src/plugins/tool/Eval.py | 11 +- src/plugins/tool/EventCmp.py | 13 +- src/plugins/tool/EventNames.py | 11 +- src/plugins/tool/ExtractCity.py | 11 +- src/plugins/tool/FindDupes.py | 11 +- src/plugins/tool/Leak.py | 7 +- src/plugins/tool/MediaManager.py | 11 +- src/plugins/tool/NotRelated.py | 13 +- src/plugins/tool/OwnerEditor.py | 11 +- src/plugins/tool/PatchNames.py | 11 +- src/plugins/tool/Rebuild.py | 11 +- src/plugins/tool/RebuildRefMap.py | 11 +- src/plugins/tool/RelCalc.py | 11 +- src/plugins/tool/RemoveUnused.py | 11 +- src/plugins/tool/ReorderIds.py | 11 +- src/plugins/tool/SortEvents.py | 5 +- src/plugins/tool/SoundGen.py | 11 +- src/plugins/tool/TestcaseGenerator.py | 11 +- src/plugins/tool/Verify.py | 11 +- src/plugins/webreport/NarrativeWeb.py | 7 +- src/plugins/webreport/WebCal.py | 5 +- 137 files changed, 29439 insertions(+), 9737 deletions(-) delete mode 100644 src/PluginUtils/Makefile.am delete mode 100644 src/PluginUtils/_GuiOptions.py delete mode 100644 src/PluginUtils/_Options.py delete mode 100644 src/PluginUtils/_PluginDialogs.py delete mode 100644 src/PluginUtils/_PluginWindows.py delete mode 100644 src/PluginUtils/_Tool.py delete mode 100644 src/PluginUtils/__init__.py delete mode 100644 src/ReportBase/Makefile.am delete mode 100644 src/ReportBase/_Bibliography.py delete mode 100644 src/ReportBase/_CommandLineReport.py delete mode 100644 src/ReportBase/_Constants.py delete mode 100644 src/ReportBase/_DocReportDialog.py delete mode 100644 src/ReportBase/_DrawReportDialog.py delete mode 100644 src/ReportBase/_Endnotes.py delete mode 100644 src/ReportBase/_FileEntry.py delete mode 100644 src/ReportBase/_GraphvizReportDialog.py delete mode 100644 src/ReportBase/_PaperMenu.py delete mode 100644 src/ReportBase/_Report.py delete mode 100644 src/ReportBase/_ReportDialog.py delete mode 100644 src/ReportBase/_ReportOptions.py delete mode 100644 src/ReportBase/_ReportUtils.py delete mode 100644 src/ReportBase/_StyleComboBox.py delete mode 100644 src/ReportBase/_StyleEditor.py delete mode 100644 src/ReportBase/_TextReportDialog.py delete mode 100644 src/ReportBase/_WebReportDialog.py delete mode 100644 src/ReportBase/__init__.py create mode 100644 src/cli/plug/Makefile.am create mode 100644 src/cli/plug/__init__.py create mode 100644 src/gen/plug/_options.py create mode 100644 src/gen/plug/report/Makefile.am create mode 100644 src/gen/plug/report/__init__.py create mode 100644 src/gen/plug/report/_bibliography.py create mode 100644 src/gen/plug/report/_constants.py create mode 100644 src/gen/plug/report/_options.py create mode 100644 src/gen/plug/report/_paper.py create mode 100644 src/gen/plug/report/_reportbase.py create mode 100644 src/gen/plug/report/endnotes.py create mode 100644 src/gen/plug/report/utils.py create mode 100644 src/gui/plug/Makefile.am create mode 100644 src/gui/plug/__init__.py create mode 100644 src/gui/plug/_dialogs.py create mode 100644 src/gui/plug/_guioptions.py create mode 100644 src/gui/plug/_windows.py create mode 100644 src/gui/plug/report/Makefile.am create mode 100644 src/gui/plug/report/__init__.py create mode 100644 src/gui/plug/report/_docreportdialog.py create mode 100644 src/gui/plug/report/_drawreportdialog.py create mode 100644 src/gui/plug/report/_fileentry.py create mode 100644 src/gui/plug/report/_graphvizreportdialog.py create mode 100644 src/gui/plug/report/_options.py create mode 100644 src/gui/plug/report/_papermenu.py create mode 100644 src/gui/plug/report/_reportdialog.py create mode 100644 src/gui/plug/report/_stylecombobox.py create mode 100644 src/gui/plug/report/_styleeditor.py create mode 100644 src/gui/plug/report/_textreportdialog.py create mode 100644 src/gui/plug/report/_webreportdialog.py create mode 100644 src/gui/plug/tool.py diff --git a/autogen.sh b/autogen.sh index e068e0383..95ba48f11 100755 --- a/autogen.sh +++ b/autogen.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # Run this to generate all the initial makefiles, etc. # $Id$ diff --git a/configure.in b/configure.in index a557f0e5d..8dd9eac21 100644 --- a/configure.in +++ b/configure.in @@ -93,6 +93,7 @@ gramps.sh src/const.py src/Makefile src/cli/Makefile +src/cli/plug/Makefile src/gen/Makefile src/gen/db/Makefile src/gen/display/Makefile @@ -102,11 +103,14 @@ src/gen/plug/Makefile src/gen/plug/docbackend/Makefile src/gen/plug/docgen/Makefile src/gen/plug/menu/Makefile +src/gen/plug/report/Makefile src/gen/proxy/Makefile src/gen/utils/Makefile src/gui/Makefile src/gui/editors/Makefile src/gui/editors/displaytabs/Makefile +src/gui/plug/Makefile +src/gui/plug/report/Makefile src/gui/selectors/Makefile src/gui/views/Makefile src/gui/views/treemodels/Makefile @@ -127,8 +131,6 @@ src/Filters/Rules/Note/Makefile src/Filters/SideBar/Makefile src/Simple/Makefile src/GrampsLocale/Makefile -src/PluginUtils/Makefile -src/ReportBase/Makefile src/plugins/Makefile src/plugins/docgen/Makefile src/plugins/drawreport/Makefile diff --git a/src/ExportAssistant.py b/src/ExportAssistant.py index 6a090e98e..9e8c5316f 100644 --- a/src/ExportAssistant.py +++ b/src/ExportAssistant.py @@ -4,6 +4,7 @@ # Copyright (C) 2004-2007 Donald N. Allingham # Copyright (C) 2008 Brian G. Matherly # Contribution 2009 by Brad Crittenden +# Copyright (C) 2010 Jakim Friant # # 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 @@ -56,6 +57,7 @@ import config from gui.pluginmanager import GuiPluginManager import Utils import ManagedWindow +from QuestionDialog import ErrorDialog #------------------------------------------------------------------------- # @@ -603,6 +605,7 @@ class ExportAssistant(gtk.Assistant, ManagedWindow.ManagedWindow) : export_function = self.map_exporters[ix].get_export_function() success = export_function(self.dbstate.db, filename, + ErrorDialog, self.option_box_instance, self.callback) return success diff --git a/src/Makefile.am b/src/Makefile.am index 58d53ee1a..4fbe5c3b5 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -15,9 +15,7 @@ SUBDIRS = \ images \ Merge \ mapstraction \ - PluginUtils \ plugins \ - ReportBase \ Simple gdirdir=$(prefix)/share/gramps diff --git a/src/Merge/_MergePerson.py b/src/Merge/_MergePerson.py index 7206b7ff7..07ca5a4ba 100644 --- a/src/Merge/_MergePerson.py +++ b/src/Merge/_MergePerson.py @@ -3,6 +3,7 @@ # # Copyright (C) 2000-2007 Donald N. Allingham # Copyright (C) 2010 Michiel D. Nauta +# Copyright (C) 2010 Jakim Friant # # 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 @@ -36,7 +37,7 @@ import pango #------------------------------------------------------------------------- from gen.ggettext import sgettext as _ import gen.lib -from ReportBase import ReportUtils +from gen.plug.report import utils as ReportUtils from gen.display.name import displayer as name_displayer import const import DateHandler diff --git a/src/PluginUtils/Makefile.am b/src/PluginUtils/Makefile.am deleted file mode 100644 index bee0e209e..000000000 --- a/src/PluginUtils/Makefile.am +++ /dev/null @@ -1,26 +0,0 @@ -# This is the src/PluginUtils level Makefile for Gramps -# We could use GNU make's ':=' syntax for nice wildcard use, -# but that is not necessarily portable. -# If not using GNU make, then list all .py files individually - -pkgdatadir = $(datadir)/@PACKAGE@/PluginUtils - -pkgdata_PYTHON = \ - __init__.py\ - _GuiOptions.py\ - _Options.py\ - _Tool.py\ - _PluginWindows.py\ - _PluginDialogs.py - -pkgpyexecdir = @pkgpyexecdir@/PluginUtils -pkgpythondir = @pkgpythondir@/PluginUtils - -# 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/_GuiOptions.py b/src/PluginUtils/_GuiOptions.py deleted file mode 100644 index eb42d10d7..000000000 --- a/src/PluginUtils/_GuiOptions.py +++ /dev/null @@ -1,1538 +0,0 @@ -# -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2007-2008 Brian G. Matherly -# Copyright (C) 2008,2010 Gary Burton -# Copyright (C) 2008 Craig J. Anderson -# Copyright (C) 2009 Nick Hall -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# 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$ - -""" -Specific option handling for a GUI. -""" -#------------------------------------------------------------------------ -# -# python modules -# -#------------------------------------------------------------------------ -from gen.ggettext import gettext as _ -import os -import sys - -#------------------------------------------------------------------------- -# -# gtk modules -# -#------------------------------------------------------------------------- -import gtk -import gobject - -#------------------------------------------------------------------------- -# -# gramps modules -# -#------------------------------------------------------------------------- -import Utils -from gui.utils import ProgressMeter -from gui.pluginmanager import GuiPluginManager -from gui import widgets -import ManagedWindow -from QuestionDialog import OptionDialog -from gui.selectors import SelectorFactory -from gen.display.name import displayer as _nd -from Filters import GenericFilterFactory, GenericFilter, Rules -import gen - -#------------------------------------------------------------------------ -# -# Dialog window used to select a surname -# -#------------------------------------------------------------------------ -class LastNameDialog(ManagedWindow.ManagedWindow): - """ - A dialog that allows the selection of a surname from the database. - """ - def __init__(self, database, uistate, track, surnames, skip_list=set()): - - ManagedWindow.ManagedWindow.__init__(self, uistate, track, self) - flags = gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT | \ - gtk.DIALOG_NO_SEPARATOR - buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_OK, - gtk.RESPONSE_ACCEPT) - self.__dlg = gtk.Dialog(None, uistate.window, flags, buttons) - self.__dlg.set_position(gtk.WIN_POS_CENTER_ON_PARENT) - self.set_window(self.__dlg, None, _('Select surname')) - self.window.set_default_size(400, 400) - - # build up a container to display all of the people of interest - self.__model = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_INT) - self.__tree_view = gtk.TreeView(self.__model) - col1 = gtk.TreeViewColumn(_('Surname'), gtk.CellRendererText(), text=0) - col2 = gtk.TreeViewColumn(_('Count'), gtk.CellRendererText(), text=1) - col1.set_resizable(True) - col2.set_resizable(True) - col1.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE) - col2.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE) - col1.set_sort_column_id(0) - col2.set_sort_column_id(1) - self.__tree_view.append_column(col1) - self.__tree_view.append_column(col2) - scrolled_window = gtk.ScrolledWindow() - scrolled_window.add(self.__tree_view) - scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - scrolled_window.set_shadow_type(gtk.SHADOW_OUT) - self.__dlg.vbox.pack_start(scrolled_window, expand=True, fill=True) - scrolled_window.show_all() - - if len(surnames) == 0: - # we could use database.get_surname_list(), but if we do that - # all we get is a list of names without a count...therefore - # we'll traverse the entire database ourself and build up a - # list that we can use -# for name in database.get_surname_list(): -# self.__model.append([name, 0]) - - # build up the list of surnames, keeping track of the count for each - # name (this can be a lengthy process, so by passing in the - # dictionary we can be certain we only do this once) - progress = ProgressMeter(_('Finding Surnames')) - progress.set_pass(_('Finding surnames'), - database.get_number_of_people()) - for person in database.iter_people(): - progress.step() - key = person.get_primary_name().get_surname() - count = 0 - if key in surnames: - count = surnames[key] - surnames[key] = count + 1 - progress.close() - - # insert the names and count into the model - for key in surnames: - if key.encode('iso-8859-1','xmlcharrefreplace') not in skip_list: - self.__model.append([key, surnames[key]]) - - # keep the list sorted starting with the most popular last name - self.__model.set_sort_column_id(1, gtk.SORT_DESCENDING) - - # the "OK" button should be enabled/disabled based on the selection of - # a row - self.__tree_selection = self.__tree_view.get_selection() - self.__tree_selection.set_mode(gtk.SELECTION_MULTIPLE) - self.__tree_selection.select_path(0) - - def run(self): - """ - Display the dialog and return the selected surnames when done. - """ - response = self.__dlg.run() - surname_set = set() - if response == gtk.RESPONSE_ACCEPT: - (mode, paths) = self.__tree_selection.get_selected_rows() - for path in paths: - i = self.__model.get_iter(path) - surname = self.__model.get_value(i, 0) - surname_set.add(surname) - self.__dlg.destroy() - return surname_set - -#------------------------------------------------------------------------- -# -# GuiStringOption class -# -#------------------------------------------------------------------------- -class GuiStringOption(gtk.Entry): - """ - This class displays an option that is a simple one-line string. - """ - def __init__(self, option, dbstate, uistate, track): - """ - @param option: The option to display. - @type option: gen.plug.menu.StringOption - @return: nothing - """ - gtk.Entry.__init__(self) - self.__option = option - self.set_text( self.__option.get_value() ) - self.connect('changed', self.__text_changed) - self.set_tooltip_text(self.__option.get_help()) - - self.__option.connect('avail-changed', self.__update_avail) - self.__update_avail() - - def __text_changed(self, obj): # IGNORE:W0613 - obj is unused - """ - Handle the change of the value. - """ - self.__option.set_value( self.get_text() ) - - def __update_avail(self): - """ - Update the availability (sensitivity) of this widget. - """ - avail = self.__option.get_available() - self.set_sensitive(avail) - -#------------------------------------------------------------------------- -# -# GuiColorOption class -# -#------------------------------------------------------------------------- -class GuiColorOption(gtk.ColorButton): - """ - This class displays an option that allows the selection of a colour. - """ - def __init__(self, option, dbstate, uistate, track): - self.__option = option - value = self.__option.get_value() - gtk.ColorButton.__init__( self, gtk.gdk.color_parse(value) ) - self.connect('color-set', self.__value_changed) - self.set_tooltip_text(self.__option.get_help()) - - def __value_changed(self, obj): # IGNORE:W0613 - obj is unused - """ - Handle the change of color. - """ - colour = self.get_color() - value = '#%02x%02x%02x' % ( - int(colour.red * 256 / 65536), - int(colour.green * 256 / 65536), - int(colour.blue * 256 / 65536)) - self.__option.set_value(value) - -#------------------------------------------------------------------------- -# -# GuiNumberOption class -# -#------------------------------------------------------------------------- -class GuiNumberOption(gtk.SpinButton): - """ - This class displays an option that is a simple number with defined maximum - and minimum values. - """ - def __init__(self, option, dbstate, uistate, track): - self.__option = option - - decimals = 0 - step = self.__option.get_step() - adj = gtk.Adjustment(1, - self.__option.get_min(), - self.__option.get_max(), - step) - - # Calculate the number of decimal places if necessary - if step < 1: - import math - decimals = int(math.log10(step) * -1) - - gtk.SpinButton.__init__(self, adj, digits=decimals) - gtk.SpinButton.set_numeric(self, True) - - self.set_value(self.__option.get_value()) - self.connect('value_changed', self.__value_changed) - self.set_tooltip_text(self.__option.get_help()) - - self.__option.connect('avail-changed', self.__update_avail) - self.__update_avail() - - def __value_changed(self, obj): # IGNORE:W0613 - obj is unused - """ - Handle the change of the value. - """ - vtype = type(self.__option.get_value()) - self.__option.set_value( vtype(self.get_value()) ) - - def __update_avail(self): - """ - Update the availability (sensitivity) of this widget. - """ - avail = self.__option.get_available() - self.set_sensitive(avail) - -#------------------------------------------------------------------------- -# -# GuiTextOption class -# -#------------------------------------------------------------------------- -class GuiTextOption(gtk.ScrolledWindow): - """ - This class displays an option that is a multi-line string. - """ - def __init__(self, option, dbstate, uistate, track): - self.__option = option - gtk.ScrolledWindow.__init__(self) - self.set_shadow_type(gtk.SHADOW_IN) - self.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - - # Add a TextView - value = self.__option.get_value() - gtext = gtk.TextView() - gtext.get_buffer().set_text("\n".join(value)) - gtext.set_editable(1) - self.add(gtext) - - # Required for tooltip - gtext.add_events(gtk.gdk.ENTER_NOTIFY_MASK) - gtext.add_events(gtk.gdk.LEAVE_NOTIFY_MASK) - gtext.set_tooltip_text(self.__option.get_help()) - - self.__buff = gtext.get_buffer() - self.__buff.connect('changed', self.__value_changed) - - def __value_changed(self, obj): # IGNORE:W0613 - obj is unused - """ - Handle the change of the value. - """ - text_val = unicode( self.__buff.get_text( self.__buff.get_start_iter(), - self.__buff.get_end_iter(), - False) ) - self.__option.set_value( text_val.split('\n') ) - -#------------------------------------------------------------------------- -# -# GuiBooleanOption class -# -#------------------------------------------------------------------------- -class GuiBooleanOption(gtk.CheckButton): - """ - This class displays an option that is a boolean (True or False). - """ - def __init__(self, option, dbstate, uistate, track): - self.__option = option - gtk.CheckButton.__init__(self, self.__option.get_label()) - self.set_active(self.__option.get_value()) - self.connect('toggled', self.__value_changed) - self.set_tooltip_text(self.__option.get_help()) - - self.__option.connect('avail-changed', self.__update_avail) - self.__update_avail() - - def __value_changed(self, obj): # IGNORE:W0613 - obj is unused - """ - Handle the change of the value. - """ - self.__option.set_value( self.get_active() ) - - def __update_avail(self): - """ - Update the availability (sensitivity) of this widget. - """ - avail = self.__option.get_available() - self.set_sensitive(avail) - -#------------------------------------------------------------------------- -# -# GuiEnumeratedListOption class -# -#------------------------------------------------------------------------- -class GuiEnumeratedListOption(gtk.HBox): - """ - This class displays an option that provides a finite number of values. - Each possible value is assigned a value and a description. - """ - def __init__(self, option, dbstate, uistate, track): - gtk.HBox.__init__(self) - evtBox = gtk.EventBox() - self.__option = option - self.__combo = gtk.combo_box_new_text() - evtBox.add(self.__combo) - self.pack_start(evtBox, True, True) - - self.__update_options() - - self.set_tooltip_text(self.__option.get_help()) - - self.__combo.connect('changed', self.__value_changed) - self.__option.connect('options-changed', self.__update_options) - self.__option.connect('avail-changed', self.__update_avail) - self.__update_avail() - - def __value_changed(self, obj): # IGNORE:W0613 - obj is unused - """ - Handle the change of the value. - """ - index = self.__combo.get_active() - if index < 0: - return - items = self.__option.get_items() - value, description = items[index] # IGNORE:W0612 - description is unused - self.__option.set_value( value ) - self.value_changed() - - def value_changed(self): - pass - - def __update_options(self): - """ - Handle the change of the available options. - """ - self.__combo.get_model().clear() - cur_val = self.__option.get_value() - active_index = 0 - current_index = 0 - for (value, description) in self.__option.get_items(): - self.__combo.append_text(description) - if value == cur_val: - active_index = current_index - current_index += 1 - self.__combo.set_active( active_index ) - - def __update_avail(self): - """ - Update the availability (sensitivity) of this widget. - """ - avail = self.__option.get_available() - self.set_sensitive(avail) - -#------------------------------------------------------------------------- -# -# GuiPersonOption class -# -#------------------------------------------------------------------------- -class GuiPersonOption(gtk.HBox): - """ - This class displays an option that allows a person from the - database to be selected. - """ - def __init__(self, option, dbstate, uistate, track): - """ - @param option: The option to display. - @type option: gen.plug.menu.PersonOption - @return: nothing - """ - gtk.HBox.__init__(self) - self.__option = option - self.__dbstate = dbstate - self.__db = dbstate.get_database() - self.__uistate = uistate - self.__track = track - self.__person_label = gtk.Label() - self.__person_label.set_alignment(0.0, 0.5) - - pevt = gtk.EventBox() - pevt.add(self.__person_label) - person_button = widgets.SimpleButton(gtk.STOCK_INDEX, - self.__get_person_clicked) - person_button.set_relief(gtk.RELIEF_NORMAL) - - self.pack_start(pevt, False) - self.pack_end(person_button, False) - - person_handle = self.__uistate.get_active('Person') - person = self.__dbstate.db.get_person_from_handle(person_handle) - if not person: - person = self.__db.get_default_person() - self.__update_person(person) - - pevt.set_tooltip_text(self.__option.get_help()) - person_button.set_tooltip_text(_('Select a different person')) - - self.__option.connect('avail-changed', self.__update_avail) - self.__update_avail() - - def __get_person_clicked(self, obj): # IGNORE:W0613 - obj is unused - """ - Handle the button to choose a different person. - """ - # Create a filter for the person selector. - rfilter = GenericFilter() - rfilter.set_logical_op('or') - rfilter.add_rule(Rules.Person.IsBookmarked([])) - rfilter.add_rule(Rules.Person.HasIdOf([self.__option.get_value()])) - - # Add the database home person if one exists. - default_person = self.__db.get_default_person() - if default_person: - gid = default_person.get_gramps_id() - rfilter.add_rule(Rules.Person.HasIdOf([gid])) - - # Add the selected person if one exists. - person_handle = self.__uistate.get_active('Person') - active_person = self.__dbstate.db.get_person_from_handle(person_handle) - if active_person: - gid = active_person.get_gramps_id() - rfilter.add_rule(Rules.Person.HasIdOf([gid])) - - select_class = SelectorFactory('Person') - sel = select_class(self.__dbstate, self.__uistate, self.__track, - title=_('Select a person for the report'), - filter=rfilter ) - person = sel.run() - self.__update_person(person) - - def __update_person(self, person): - """ - Update the currently selected person. - """ - if person: - name = _nd.display(person) - gid = person.get_gramps_id() - self.__person_label.set_text( "%s (%s)" % (name, gid) ) - self.__option.set_value(gid) - - def __update_avail(self): - """ - Update the availability (sensitivity) of this widget. - """ - avail = self.__option.get_available() - self.set_sensitive(avail) - -#------------------------------------------------------------------------- -# -# GuiFamilyOption class -# -#------------------------------------------------------------------------- -class GuiFamilyOption(gtk.HBox): - """ - This class displays an option that allows a family from the - database to be selected. - """ - def __init__(self, option, dbstate, uistate, track): - """ - @param option: The option to display. - @type option: gen.plug.menu.FamilyOption - @return: nothing - """ - gtk.HBox.__init__(self) - self.__option = option - self.__dbstate = dbstate - self.__db = dbstate.get_database() - self.__uistate = uistate - self.__track = track - self.__family_label = gtk.Label() - self.__family_label.set_alignment(0.0, 0.5) - - pevt = gtk.EventBox() - pevt.add(self.__family_label) - family_button = widgets.SimpleButton(gtk.STOCK_INDEX, - self.__get_family_clicked) - family_button.set_relief(gtk.RELIEF_NORMAL) - - self.pack_start(pevt, False) - self.pack_end(family_button, False) - - self.__initialize_family() - - pevt.set_tooltip_text(self.__option.get_help()) - family_button.set_tooltip_text(_('Select a different family')) - - self.__option.connect('avail-changed', self.__update_avail) - self.__update_avail() - - def __initialize_family(self): - """ - Find a family to initialize the option with. Any family will do, but - try to find a family that the user is likely interested in. - """ - family_list = [] - - # Use the active family if one is selected - family = self.__uistate.get_active('Family') - if family: - family_list = [family] - - if not family_list: - # Next try the family of the active person - person_handle = self.__uistate.get_active('Person') - person = self.__dbstate.db.get_person_from_handle(person_handle) - if person: - family_list = person.get_family_handle_list() - - if not family_list: - # Next try the family of the default person in the database. - person = self.__db.get_default_person() - if person: - family_list = person.get_family_handle_list() - - if not family_list: - # Finally, take any family you can find. - for family in self.__db.iter_family_handles(): - self.__update_family(family) - break - else: - self.__update_family(family_list[0]) - - def __get_family_clicked(self, obj): # IGNORE:W0613 - obj is unused - """ - Handle the button to choose a different family. - """ - # Create a filter for the person selector. - rfilter = GenericFilterFactory('Family')() - rfilter.set_logical_op('or') - - # Add the current family - rfilter.add_rule(Rules.Family.HasIdOf([self.__option.get_value()])) - - # Add all bookmarked families - rfilter.add_rule(Rules.Family.IsBookmarked([])) - - # Add the families of the database home person if one exists. - default_person = self.__db.get_default_person() - if default_person: - family_list = default_person.get_family_handle_list() - for family_handle in family_list: - family = self.__db.get_family_from_handle(family_handle) - gid = family.get_gramps_id() - rfilter.add_rule(Rules.Family.HasIdOf([gid])) - - # Add the families of the selected person if one exists. - active_person = self.__db.get_default_person() - if active_person: - family_list = active_person.get_family_handle_list() - for family_handle in family_list: - family = self.__db.get_family_from_handle(family_handle) - gid = family.get_gramps_id() - rfilter.add_rule(Rules.Family.HasIdOf([gid])) - - select_class = SelectorFactory('Family') - sel = select_class(self.__dbstate, self.__uistate, self.__track, - filter=rfilter ) - family = sel.run() - if family: - self.__update_family(family.get_handle()) - - def __update_family(self, handle): - """ - Update the currently selected family. - """ - if handle: - family = self.__dbstate.db.get_family_from_handle(handle) - family_id = family.get_gramps_id() - fhandle = family.get_father_handle() - mhandle = family.get_mother_handle() - - if fhandle: - father = self.__db.get_person_from_handle(fhandle) - father_name = _nd.display(father) - else: - father_name = _("unknown father") - - if mhandle: - mother = self.__db.get_person_from_handle(mhandle) - mother_name = _nd.display(mother) - else: - mother_name = _("unknown mother") - - name = _("%s and %s (%s)") % (father_name, mother_name, family_id) - - self.__family_label.set_text( name ) - self.__option.set_value(family_id) - - def __update_avail(self): - """ - Update the availability (sensitivity) of this widget. - """ - avail = self.__option.get_available() - self.set_sensitive(avail) - -#------------------------------------------------------------------------- -# -# GuiNoteOption class -# -#------------------------------------------------------------------------- -class GuiNoteOption(gtk.HBox): - """ - This class displays an option that allows a note from the - database to be selected. - """ - def __init__(self, option, dbstate, uistate, track): - """ - @param option: The option to display. - @type option: gen.plug.menu.NoteOption - @return: nothing - """ - gtk.HBox.__init__(self) - self.__option = option - self.__dbstate = dbstate - self.__db = dbstate.get_database() - self.__uistate = uistate - self.__track = track - self.__note_label = gtk.Label() - self.__note_label.set_alignment(0.0, 0.5) - - pevt = gtk.EventBox() - pevt.add(self.__note_label) - note_button = widgets.SimpleButton(gtk.STOCK_INDEX, - self.__get_note_clicked) - note_button.set_relief(gtk.RELIEF_NORMAL) - - self.pack_start(pevt, False) - self.pack_end(note_button, False) - - # Initialize to the current value - nid = self.__option.get_value() - note = self.__db.get_note_from_gramps_id(nid) - self.__update_note(note) - - pevt.set_tooltip_text(self.__option.get_help()) - note_button.set_tooltip_text(_('Select an existing note')) - - self.__option.connect('avail-changed', self.__update_avail) - self.__update_avail() - - def __get_note_clicked(self, obj): # IGNORE:W0613 - obj is unused - """ - Handle the button to choose a different note. - """ - select_class = SelectorFactory('Note') - sel = select_class(self.__dbstate, self.__uistate, self.__track) - note = sel.run() - self.__update_note(note) - - def __update_note(self, note): - """ - Update the currently selected note. - """ - if note: - note_id = note.get_gramps_id() - txt = " ".join(note.get().split()) - #String must be unicode for truncation to work for non ascii characters - txt = unicode(txt) - if len(txt) > 35: - txt = txt[:35] + "..." - txt = "%s [%s]" % (txt, note_id) - - self.__note_label.set_text( txt ) - self.__option.set_value(note_id) - else: - txt = "%s" % _('No note given, click button to select one') - self.__note_label.set_text( txt ) - self.__note_label.set_use_markup(True) - self.__option.set_value("") - - def __update_avail(self): - """ - Update the availability (sensitivity) of this widget. - """ - avail = self.__option.get_available() - self.set_sensitive(avail) - -#------------------------------------------------------------------------- -# -# GuiMediaOption class -# -#------------------------------------------------------------------------- -class GuiMediaOption(gtk.HBox): - """ - This class displays an option that allows a media object from the - database to be selected. - """ - def __init__(self, option, dbstate, uistate, track): - """ - @param option: The option to display. - @type option: gen.plug.menu.MediaOption - @return: nothing - """ - gtk.HBox.__init__(self) - self.__option = option - self.__dbstate = dbstate - self.__db = dbstate.get_database() - self.__uistate = uistate - self.__track = track - self.__media_label = gtk.Label() - self.__media_label.set_alignment(0.0, 0.5) - - pevt = gtk.EventBox() - pevt.add(self.__media_label) - media_button = widgets.SimpleButton(gtk.STOCK_INDEX, - self.__get_media_clicked) - media_button.set_relief(gtk.RELIEF_NORMAL) - - self.pack_start(pevt, False) - self.pack_end(media_button, False) - - # Initialize to the current value - mid = self.__option.get_value() - media = self.__db.get_object_from_gramps_id(mid) - self.__update_media(media) - - pevt.set_tooltip_text(self.__option.get_help()) - media_button.set_tooltip_text(_('Select an existing media object')) - - self.__option.connect('avail-changed', self.__update_avail) - self.__update_avail() - - def __get_media_clicked(self, obj): # IGNORE:W0613 - obj is unused - """ - Handle the button to choose a different note. - """ - select_class = SelectorFactory('MediaObject') - sel = select_class(self.__dbstate, self.__uistate, self.__track) - media = sel.run() - self.__update_media(media) - - def __update_media(self, media): - """ - Update the currently selected media. - """ - if media: - media_id = media.get_gramps_id() - txt = "%s [%s]" % (media.get_description(), media_id) - - self.__media_label.set_text( txt ) - self.__option.set_value(media_id) - else: - txt = "%s" % _('No image given, click button to select one') - self.__media_label.set_text( txt ) - self.__media_label.set_use_markup(True) - self.__option.set_value("") - - def __update_avail(self): - """ - Update the availability (sensitivity) of this widget. - """ - avail = self.__option.get_available() - self.set_sensitive(avail) - -#------------------------------------------------------------------------- -# -# GuiPersonListOption class -# -#------------------------------------------------------------------------- -class GuiPersonListOption(gtk.HBox): - """ - This class displays a widget that allows multiple people from the - database to be selected. - """ - def __init__(self, option, dbstate, uistate, track): - """ - @param option: The option to display. - @type option: gen.plug.menu.PersonListOption - @return: nothing - """ - gtk.HBox.__init__(self) - self.__option = option - self.__dbstate = dbstate - self.__db = dbstate.get_database() - self.__uistate = uistate - self.__track = track - - self.__model = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING) - self.__tree_view = gtk.TreeView(self.__model) - self.__tree_view.set_size_request(150, 150) - col1 = gtk.TreeViewColumn(_('Name' ), gtk.CellRendererText(), text=0) - col2 = gtk.TreeViewColumn(_('ID' ), gtk.CellRendererText(), text=1) - col1.set_resizable(True) - col2.set_resizable(True) - col1.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE) - col2.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE) - col1.set_sort_column_id(0) - col2.set_sort_column_id(1) - self.__tree_view.append_column(col1) - self.__tree_view.append_column(col2) - self.__scrolled_window = gtk.ScrolledWindow() - self.__scrolled_window.add(self.__tree_view) - self.__scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, - gtk.POLICY_AUTOMATIC) - self.__scrolled_window.set_shadow_type(gtk.SHADOW_OUT) - - self.pack_start(self.__scrolled_window, expand=True, fill=True) - - value = self.__option.get_value() - for gid in value.split(): - person = self.__db.get_person_from_gramps_id(gid) - if person: - name = _nd.display(person) - self.__model.append([name, gid]) - - # now setup the '+' and '-' pushbutton for adding/removing people from - # the container - self.__add_person = widgets.SimpleButton(gtk.STOCK_ADD, - self.__add_person_clicked) - self.__del_person = widgets.SimpleButton(gtk.STOCK_REMOVE, - self.__del_person_clicked) - self.__vbbox = gtk.VButtonBox() - self.__vbbox.add(self.__add_person) - self.__vbbox.add(self.__del_person) - self.__vbbox.set_layout(gtk.BUTTONBOX_SPREAD) - self.pack_end(self.__vbbox, expand=False) - - self.__tree_view.set_tooltip_text(self.__option.get_help()) - - def __update_value(self): - """ - Parse the object and return. - """ - gidlist = '' - i = self.__model.get_iter_first() - while (i): - gid = self.__model.get_value(i, 1) - gidlist = gidlist + gid + ' ' - i = self.__model.iter_next(i) - self.__option.set_value(gidlist) - - def __add_person_clicked(self, obj): # IGNORE:W0613 - obj is unused - """ - Handle the add person button. - """ - # people we already have must be excluded - # so we don't list them multiple times - skip_list = set() - i = self.__model.get_iter_first() - while (i): - gid = self.__model.get_value(i, 1) # get the GID stored in column #1 - person = self.__db.get_person_from_gramps_id(gid) - skip_list.add(person.get_handle()) - i = self.__model.iter_next(i) - - select_class = SelectorFactory('Person') - sel = select_class(self.__dbstate, self.__uistate, - self.__track, skip=skip_list) - person = sel.run() - if person: - name = _nd.display(person) - gid = person.get_gramps_id() - self.__model.append([name, gid]) - - # if this person has a spouse, ask if we should include the spouse - # in the list of "people of interest" - # - # NOTE: we may want to make this an optional thing, determined - # by the use of a parameter at the time this class is instatiated - family_list = person.get_family_handle_list() - for family_handle in family_list: - family = self.__db.get_family_from_handle(family_handle) - - if person.get_handle() == family.get_father_handle(): - spouse_handle = family.get_mother_handle() - else: - spouse_handle = family.get_father_handle() - - if spouse_handle and (spouse_handle not in skip_list): - spouse = self.__db.get_person_from_handle( - spouse_handle) - spouse_name = _nd.display(spouse) - text = _('Also include %s?') % spouse_name - - prompt = OptionDialog(_('Select Person'), - text, - _('No'), None, - _('Yes'), None) - if prompt.get_response() == gtk.RESPONSE_YES: - gid = spouse.get_gramps_id() - self.__model.append([spouse_name, gid]) - - self.__update_value() - - def __del_person_clicked(self, obj): # IGNORE:W0613 - obj is unused - """ - Handle the delete person button. - """ - (path, column) = self.__tree_view.get_cursor() - if (path): - i = self.__model.get_iter(path) - self.__model.remove(i) - self.__update_value() - -#------------------------------------------------------------------------- -# -# GuiPlaceListOption class -# -#------------------------------------------------------------------------- -class GuiPlaceListOption(gtk.HBox): - """ - This class displays a widget that allows multiple places from the - database to be selected. - """ - def __init__(self, option, dbstate, uistate, track): - """ - @param option: The option to display. - @type option: gen.plug.menu.PlaceListOption - @return: nothing - """ - gtk.HBox.__init__(self) - self.__option = option - self.__dbstate = dbstate - self.__db = dbstate.get_database() - self.__uistate = uistate - self.__track = track - - self.__model = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING) - self.__tree_view = gtk.TreeView(self.__model) - self.__tree_view.set_size_request(150, 150) - col1 = gtk.TreeViewColumn(_('Place' ), gtk.CellRendererText(), text=0) - col2 = gtk.TreeViewColumn(_('ID' ), gtk.CellRendererText(), text=1) - col1.set_resizable(True) - col2.set_resizable(True) - col1.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE) - col2.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE) - col1.set_sort_column_id(0) - col2.set_sort_column_id(1) - self.__tree_view.append_column(col1) - self.__tree_view.append_column(col2) - self.__scrolled_window = gtk.ScrolledWindow() - self.__scrolled_window.add(self.__tree_view) - self.__scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, - gtk.POLICY_AUTOMATIC) - self.__scrolled_window.set_shadow_type(gtk.SHADOW_OUT) - - self.pack_start(self.__scrolled_window, expand=True, fill=True) - - value = self.__option.get_value() - for gid in value.split(): - place = self.__db.get_place_from_gramps_id(gid) - if place: - place_name = place.get_title() - self.__model.append([place_name, gid]) - - # now setup the '+' and '-' pushbutton for adding/removing places from - # the container - self.__add_place = widgets.SimpleButton(gtk.STOCK_ADD, - self.__add_place_clicked) - self.__del_place = widgets.SimpleButton(gtk.STOCK_REMOVE, - self.__del_place_clicked) - self.__vbbox = gtk.VButtonBox() - self.__vbbox.add(self.__add_place) - self.__vbbox.add(self.__del_place) - self.__vbbox.set_layout(gtk.BUTTONBOX_SPREAD) - self.pack_end(self.__vbbox, expand=False) - - self.__tree_view.set_tooltip_text(self.__option.get_help()) - - def __update_value(self): - """ - Parse the object and return. - """ - gidlist = '' - i = self.__model.get_iter_first() - while (i): - gid = self.__model.get_value(i, 1) - gidlist = gidlist + gid + ' ' - i = self.__model.iter_next(i) - self.__option.set_value(gidlist) - - def __add_place_clicked(self, obj): # IGNORE:W0613 - obj is unused - """ - Handle the add place button. - """ - # places we already have must be excluded - # so we don't list them multiple times - skip_list = set() - i = self.__model.get_iter_first() - while (i): - gid = self.__model.get_value(i, 1) # get the GID stored in column #1 - place = self.__db.get_place_from_gramps_id(gid) - skip_list.add(place.get_handle()) - i = self.__model.iter_next(i) - - select_class = SelectorFactory('Place') - sel = select_class(self.__dbstate, self.__uistate, - self.__track, skip=skip_list) - place = sel.run() - if place: - place_name = place.get_title() - gid = place.get_gramps_id() - self.__model.append([place_name, gid]) - self.__update_value() - - def __del_place_clicked(self, obj): # IGNORE:W0613 - obj is unused - """ - Handle the delete place button. - """ - (path, column) = self.__tree_view.get_cursor() - if (path): - i = self.__model.get_iter(path) - self.__model.remove(i) - self.__update_value() - -#------------------------------------------------------------------------- -# -# GuiSurnameColorOption class -# -#------------------------------------------------------------------------- -class GuiSurnameColorOption(gtk.HBox): - """ - This class displays a widget that allows multiple surnames to be - selected from the database, and to assign a colour (not necessarily - unique) to each one. - """ - def __init__(self, option, dbstate, uistate, track): - """ - @param option: The option to display. - @type option: gen.plug.menu.SurnameColorOption - @return: nothing - """ - gtk.HBox.__init__(self) - self.__option = option - self.__dbstate = dbstate - self.__db = dbstate.get_database() - self.__uistate = uistate - self.__track = track - - # This will get populated the first time the dialog is run, - # and used each time after. - self.__surnames = {} # list of surnames and count - - self.__model = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING) - self.__tree_view = gtk.TreeView(self.__model) - self.__tree_view.set_size_request(150, 150) - self.__tree_view.connect('row-activated', self.__row_clicked) - col1 = gtk.TreeViewColumn(_('Surname'), gtk.CellRendererText(), text=0) - col2 = gtk.TreeViewColumn(_('Colour'), gtk.CellRendererText(), text=1) - col1.set_resizable(True) - col2.set_resizable(True) - col1.set_sort_column_id(0) - col1.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE) - col2.set_sizing(gtk.TREE_VIEW_COLUMN_AUTOSIZE) - self.__tree_view.append_column(col1) - self.__tree_view.append_column(col2) - self.scrolled_window = gtk.ScrolledWindow() - self.scrolled_window.add(self.__tree_view) - self.scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, - gtk.POLICY_AUTOMATIC) - self.scrolled_window.set_shadow_type(gtk.SHADOW_OUT) - self.pack_start(self.scrolled_window, expand=True, fill=True) - - self.add_surname = widgets.SimpleButton(gtk.STOCK_ADD, - self.__add_clicked) - self.del_surname = widgets.SimpleButton(gtk.STOCK_REMOVE, - self.__del_clicked) - self.vbbox = gtk.VButtonBox() - self.vbbox.add(self.add_surname) - self.vbbox.add(self.del_surname) - self.vbbox.set_layout(gtk.BUTTONBOX_SPREAD) - self.pack_end(self.vbbox, expand=False) - - # populate the surname/colour treeview - # - # For versions prior to 3.0.2, the fields were delimited with - # whitespace. However, this causes problems when the surname - # also has a space within it. When populating the control, - # support both the new and old format -- look for the \xb0 - # delimiter, and if it isn't there, assume this is the old- - # style space-delimited format. (Bug #2162.) - if (self.__option.get_value().find(u'\xb0') >= 0): - tmp = self.__option.get_value().split(u'\xb0') - else: - tmp = self.__option.get_value().split(' ') - while len(tmp) > 1: - surname = tmp.pop(0) - colour = tmp.pop(0) - self.__model.append([surname, colour]) - - self.__tree_view.set_tooltip_text(self.__option.get_help()) - - def __value_changed(self): - """ - Parse the object and return. - """ - surname_colours = '' - i = self.__model.get_iter_first() - while (i): - surname = self.__model.get_value(i, 0) - #surname = surname.encode('iso-8859-1','xmlcharrefreplace') - colour = self.__model.get_value(i, 1) - # Tried to use a dictionary, and tried to save it as a tuple, - # but coulnd't get this to work right -- this is lame, but now - # the surnames and colours are saved as a plain text string - # - # Hmmm...putting whitespace between the fields causes - # problems when the surname has whitespace -- for example, - # with surnames like "Del Monte". So now we insert a non- - # whitespace character which is unlikely to appear in - # a surname. (See bug report #2162.) - surname_colours += surname + u'\xb0' + colour + u'\xb0' - i = self.__model.iter_next(i) - self.__option.set_value( surname_colours ) - - def __row_clicked(self, treeview, path, column): - """ - Handle the case of a row being clicked on. - """ - # get the surname and colour value for this family - i = self.__model.get_iter(path) - surname = self.__model.get_value(i, 0) - colour = gtk.gdk.color_parse(self.__model.get_value(i, 1)) - - title = 'Select colour for %s' % surname - colour_dialog = gtk.ColorSelectionDialog(title) - colorsel = colour_dialog.colorsel - colorsel.set_current_color(colour) - response = colour_dialog.run() - - if response == gtk.RESPONSE_OK: - colour = colorsel.get_current_color() - colour_name = '#%02x%02x%02x' % ( - int(colour.red *256/65536), - int(colour.green*256/65536), - int(colour.blue *256/65536)) - self.__model.set_value(i, 1, colour_name) - - colour_dialog.destroy() - self.__value_changed() - - def __add_clicked(self, obj): # IGNORE:W0613 - obj is unused - """ - Handle the the add surname button. - """ - skip_list = set() - i = self.__model.get_iter_first() - while (i): - surname = self.__model.get_value(i, 0) - skip_list.add(surname.encode('iso-8859-1','xmlcharrefreplace')) - i = self.__model.iter_next(i) - - ln_dialog = LastNameDialog(self.__db, self.__uistate, - self.__track, self.__surnames, skip_list) - surname_set = ln_dialog.run() - for surname in surname_set: - self.__model.append([surname, '#ffffff']) - self.__value_changed() - - def __del_clicked(self, obj): # IGNORE:W0613 - obj is unused - """ - Handle the the delete surname button. - """ - (path, column) = self.__tree_view.get_cursor() - if (path): - i = self.__model.get_iter(path) - self.__model.remove(i) - self.__value_changed() - -#------------------------------------------------------------------------- -# -# GuiDestinationOption class -# -#------------------------------------------------------------------------- -class GuiDestinationOption(gtk.HBox): - """ - This class displays an option that allows the user to select a - DestinationOption. - """ - def __init__(self, option, dbstate, uistate, track): - """ - @param option: The option to display. - @type option: gen.plug.menu.DestinationOption - @return: nothing - """ - gtk.HBox.__init__(self) - self.__option = option - self.__entry = gtk.Entry() - self.__entry.set_text( self.__option.get_value() ) - self.__entry.connect('changed', self.__text_changed) - - self.__button = gtk.Button() - img = gtk.Image() - img.set_from_stock(gtk.STOCK_OPEN, gtk.ICON_SIZE_BUTTON) - self.__button.add(img) - self.__button.connect('clicked', self.__select_file) - - self.pack_start(self.__entry, True, True) - self.pack_end(self.__button, False, False) - - self.set_tooltip_text(self.__option.get_help()) - - self.__option.connect('options-changed', self.__option_changed) - - self.__option.connect('avail-changed', self.__update_avail) - self.__update_avail() - - def __text_changed(self, obj): # IGNORE:W0613 - obj is unused - """ - Handle the change of the value. - """ - self.__option.set_value( self.__entry.get_text() ) - - def __select_file(self, obj): - """ - Handle the user's request to select a file (or directory). - """ - if self.__option.get_directory_entry(): - my_action = gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER - else: - my_action = gtk.FILE_CHOOSER_ACTION_SAVE - - fcd = gtk.FileChooserDialog(_("Save As"), action=my_action, - buttons=(gtk.STOCK_CANCEL, - gtk.RESPONSE_CANCEL, - gtk.STOCK_OPEN, - gtk.RESPONSE_OK)) - - name = os.path.abspath(self.__option.get_value()) - if self.__option.get_directory_entry(): - while not os.path.isdir(name): - # Keep looking up levels to find a valid drive. - name, tail = os.path.split(name) - if not name: - # Avoid infinite loops - name = os.getcwd() - fcd.set_current_folder(name) - else: - fcd.set_current_name(name) - - status = fcd.run() - if status == gtk.RESPONSE_OK: - path = Utils.get_unicode_path(fcd.get_filename()) - if path: - if not self.__option.get_directory_entry() and \ - not path.endswith(self.__option.get_extension()): - path = path + self.__option.get_extension() - self.__entry.set_text(path) - self.__option.set_value(path) - fcd.destroy() - - def __option_changed(self): - """ - Handle a change of the option. - """ - extension = self.__option.get_extension() - directory = self.__option.get_directory_entry() - value = self.__option.get_value() - - if not directory and not value.endswith(extension): - value = value + extension - self.__option.set_value(value) - elif directory and value.endswith(extension): - value = value[:-len(extension)] - self.__option.set_value(value) - - self.__entry.set_text( self.__option.get_value() ) - - def __update_avail(self): - """ - Update the availability (sensitivity) of this widget. - """ - avail = self.__option.get_available() - self.set_sensitive(avail) - -#------------------------------------------------------------------------- -# -# GuiStyleOption class -# -#------------------------------------------------------------------------- -class GuiStyleOption(GuiEnumeratedListOption): - """ - This class displays a StyleOption. - """ - def __init__(self, option, dbstate, uistate, track): - """ - @param option: The option to display. - @type option: gen.plug.menu.StyleOption - @return: nothing - """ - GuiEnumeratedListOption.__init__(self, option, dbstate, - uistate, track) - self.__option = option - - self.__button = gtk.Button("%s..." % _("Style Editor")) - self.__button.connect('clicked', self.__on_style_edit_clicked) - - self.pack_end(self.__button, False, False) - - 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, update the displayed styles.""" - from gen.plug.docgen import StyleSheetList - from ReportBase._StyleEditor import StyleListDisplay - style_list = StyleSheetList(self.__option.get_style_file(), - self.__option.get_default_style()) - StyleListDisplay(style_list, None, None) - - new_items = [] - for style_name in style_list.get_style_names(): - new_items.append( (style_name, style_name) ) - self.__option.set_items(new_items) - -#------------------------------------------------------------------------- -# -# GuiBooleanListOption class -# -#------------------------------------------------------------------------- -class GuiBooleanListOption(gtk.HBox): - """ - This class displays an option that provides a list of check boxes. - Each possible value is assigned a value and a description. - """ - def __init__(self, option, dbstate, uistate, track): - gtk.HBox.__init__(self) - self.__option = option - self.__cbutton = [] - - COLUMNS = 2 # Number of checkbox columns - column = [] - for i in range(COLUMNS): - vbox = gtk.VBox() - self.pack_start(vbox, True, True) - column.append(vbox) - vbox.show() - - counter = 0 - default = option.get_value().split(',') - for description in option.get_descriptions(): - button = gtk.CheckButton(description) - self.__cbutton.append(button) - if default[counter] == 'True': - button.set_active(True) - button.connect("toggled", self.__value_changed) - column[counter % COLUMNS].pack_start(button, True, True) - button.show() - counter += 1 - - self.set_tooltip_text(self.__option.get_help()) - - self.__option.connect('avail-changed', self.__update_avail) - self.__update_avail() - - def __value_changed(self, button): - """ - Handle the change of the value. - """ - value = '' - for button in self.__cbutton: - value = value + str(button.get_active()) + ',' - value = value[:len(value)-1] - self.__option.set_value(value) - - def __update_avail(self): - """ - Update the availability (sensitivity) of this widget. - """ - avail = self.__option.get_available() - self.set_sensitive(avail) - -#------------------------------------------------------------------------ -# -# GuiMenuOptions class -# -#------------------------------------------------------------------------ -class GuiMenuOptions(object): - """ - Introduction - ============ - A GuiMenuOptions is used to implement the necessary funtions for adding - options to a GTK dialog. - """ - def __init__(self): - self.menu = gen.plug.menu.Menu() - - # Fill options_dict with report/tool defaults: - self.options_dict = {} - self.options_help = {} - self.add_menu_options(self.menu) - for name in self.menu.get_all_option_names(): - option = self.menu.get_option_by_name(name) - self.options_dict[name] = option.get_value() - self.options_help[name] = [ "", option.get_help() ] - - def make_default_style(self, default_style): - """ - This function is currently required by some reports. - """ - pass - - def add_menu_options(self, menu): - """ - Add the user defined options to the menu. - - @param menu: A menu class for the options to belong to. - @type menu: Menu - @return: nothing - """ - raise NotImplementedError - - def add_menu_option(self, category, name, option): - """ - Add a single option to the menu. - """ - self.menu.add_option(category, name, option) - self.options_dict[name] = option.get_value() - self.options_help[name] = [ "", option.get_help() ] - - def add_user_options(self, dialog): - """ - Generic method to add user options to the gui. - """ - for category in self.menu.get_categories(): - for name in self.menu.get_option_names(category): - option = self.menu.get_option(category, name) - - # override option default with xml-saved value: - if name in self.options_dict: - option.set_value(self.options_dict[name]) - - widget, label = make_gui_option(option, dialog.dbstate, - dialog.uistate, dialog.track) - - if widget is None: - print "UNKNOWN OPTION: ", option - else: - if label: - dialog.add_frame_option(category, - option.get_label(), - widget) - else: - dialog.add_frame_option(category, "", widget) - - def parse_user_options(self, dialog): # IGNORE:W0613 - dialog is unused - """ - Load the changed values into the saved options. - """ - for name in self.menu.get_all_option_names(): - option = self.menu.get_option_by_name(name) - self.options_dict[name] = option.get_value() - -def make_gui_option(option, dbstate, uistate, track): - """ - Stand-alone function so that Options can be used in other - ways, too. Takes an Option and returns a GuiOption. - """ - widget = None - label = True - pmgr = GuiPluginManager.get_instance() - external_options = pmgr.get_external_opt_dict() - if isinstance(option, gen.plug.menu.PersonOption): - widget = GuiPersonOption(option, dbstate, uistate, track) - elif isinstance(option, gen.plug.menu.FamilyOption): - widget = GuiFamilyOption(option, dbstate, uistate, track) - elif isinstance(option, gen.plug.menu.NoteOption): - widget = GuiNoteOption(option, dbstate, uistate, track) - elif isinstance(option, gen.plug.menu.MediaOption): - widget = GuiMediaOption(option, dbstate, uistate, track) - elif isinstance(option, gen.plug.menu.PersonListOption): - widget = GuiPersonListOption(option, dbstate, uistate, track) - elif isinstance(option, gen.plug.menu.NumberOption): - widget = GuiNumberOption(option, dbstate, uistate, track) - elif isinstance(option, gen.plug.menu.BooleanOption): - widget = GuiBooleanOption(option, dbstate, uistate, track) - label = False - elif isinstance(option, gen.plug.menu.DestinationOption): - widget = GuiDestinationOption(option, dbstate, uistate, track) - elif isinstance(option, gen.plug.menu.StringOption): - widget = GuiStringOption(option, dbstate, uistate, track) - elif isinstance(option, gen.plug.menu.StyleOption): - widget = GuiStyleOption(option, dbstate, uistate, track) - elif isinstance(option, gen.plug.menu.EnumeratedListOption): - widget = GuiEnumeratedListOption(option, dbstate, uistate, track) - elif isinstance(option, gen.plug.menu.TextOption): - widget = GuiTextOption(option, dbstate, uistate, track) - elif isinstance(option, gen.plug.menu.ColorOption): - widget = GuiColorOption(option, dbstate, uistate, track) - elif isinstance(option, gen.plug.menu.SurnameColorOption): - widget = GuiSurnameColorOption(option, dbstate, uistate, track) - elif isinstance(option, gen.plug.menu.PlaceListOption): - widget = GuiPlaceListOption(option, dbstate, uistate, track) - elif isinstance(option, gen.plug.menu.BooleanListOption): - widget = GuiBooleanListOption(option, dbstate, uistate, track) - elif option.__class__ in external_options: - widget = external_options[option.__class__](option, dbstate, uistate, - track) - else: - raise AttributeError( - "can't make GuiOption: unknown option type: '%s'" % option) - return widget, label diff --git a/src/PluginUtils/_Options.py b/src/PluginUtils/_Options.py deleted file mode 100644 index 65b769a3f..000000000 --- a/src/PluginUtils/_Options.py +++ /dev/null @@ -1,476 +0,0 @@ -# -# 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:_Options.py 9912 2008-01-22 09:17:46Z acraphae $ - -# Written by Alex Roitman - -""" -General option handling, including saving and parsing. -""" - -#------------------------------------------------------------------------- -# -# Standard Python modules -# -#------------------------------------------------------------------------- -import os - -#------------------------------------------------------------------------- -# -# SAX interface -# -#------------------------------------------------------------------------- -try: - from xml.sax import make_parser, handler,SAXParseException - from xml.sax.saxutils import quoteattr -except: - from _xmlplus.sax import make_parser, handler,SAXParseException - from _xmlplus.sax.saxutils import quoteattr - -#------------------------------------------------------------------------- -# -# gramps modules -# -#------------------------------------------------------------------------- -import Utils - -#------------------------------------------------------------------------- -# -# List of options for a single module -# -#------------------------------------------------------------------------- -class OptionList(object): - """ - Implements a set of options to parse and store for a given module. - """ - - def __init__(self): - self.options = {} - - def set_options(self, options): - """ - Set 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): - """ - Return the whole bunch of options for the OptionList. - @returns: list of options - @rtype: list - """ - return self.options - - def set_option(self, name,value): - """ - Set 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): - """ - Remove a particular option from the OptionList. - @param name: name of the option to remove. - @type name: str - """ - if name in self.options: - del self.options[name] - - def get_option(self, name): - """ - Return 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(object): - """ - Implements a collection of option lists. - """ - - def __init__(self,filename): - """ - Create 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): - """ - Return 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): - """ - Return 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): - """ - Return 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): - """ - Add or replaces an option_list in the OptionListCollection. - @param name: name associated 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' % quoteattr(module_name)) - options = option_list.get_options() - for option_name, option_data in options.iteritems(): - if isinstance(option_data, (list, tuple)): - f.write(' \n') - else: - f.write(' \n') - - f.write('\n') - f.close() - - def parse(self): - """ - Loads the OptionList from the associated file, if it exists. - """ - try: - if os.path.isfile(self.filename): - p = make_parser() - p.setContentHandler(OptionParser(self)) - p.parse(self.filename) - except (IOError,OSError,SAXParseException): - pass - -#------------------------------------------------------------------------- -# -# OptionParser -# -#------------------------------------------------------------------------- -class OptionParser(handler.ContentHandler): - """ - SAX parsing class for the OptionListCollection XML file. - """ - - def __init__(self,collection): - """ - Create 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(object): - """ - 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): - """ - Set 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, option_data in options.iteritems(): - if option_name not in self.options_dict: - 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(option_data) - except ValueError: - pass - except TypeError: - 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, option_data in self.options_dict.iteritems(): - if option_data == 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_person_id(self): - return self.person_id - - def set_person_id(self,val): - self.person_id = val - -#------------------------------------------------------------------------ -# -# Base Options class -# -#------------------------------------------------------------------------ -class Options(object): - - """ - 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): - """ - Initialize the class, performing usual house-keeping tasks. - Subclasses MUST call this in their __init__() method. - - Modules that need custom options need to override this method. - Two dictionaries allow the addition of custom options: - - 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. - """ - self.name = name - self.person_id = person_id - self.options_dict = {} - self.options_help = {} - self.handler = None - - def load_previous_values(self): - """ - Modifies all options to have the value they were last used as. Call this - function after all options have been added. - """ - self.handler = OptionHandler(self.name,self.options_dict,self.person_id) - - def add_user_options(self,dialog): - """ - Set 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.ReportDialog 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.ReportDialog 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 diff --git a/src/PluginUtils/_PluginDialogs.py b/src/PluginUtils/_PluginDialogs.py deleted file mode 100644 index 432fe0243..000000000 --- a/src/PluginUtils/_PluginDialogs.py +++ /dev/null @@ -1,316 +0,0 @@ -# -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2000-2006 Donald N. Allingham -# Copyright (C) 2008 Brian G. Matherly -# -# 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$ - -#------------------------------------------------------------------------- -# -# GTK libraries -# -#------------------------------------------------------------------------- -import gtk - -#------------------------------------------------------------------------- -# -# Standard Python modules -# -#------------------------------------------------------------------------- -from gen.ggettext import gettext as _ -from collections import defaultdict - -#------------------------------------------------------------------------- -# -# GRAMPS modules -# -#------------------------------------------------------------------------- -import const -from ReportBase import report, standalone_categories -import _Tool -from gen.plug import REPORT, TOOL -from gui.pluginmanager import GuiPluginManager -import ManagedWindow - -#------------------------------------------------------------------------- -# -# Constants -# -#------------------------------------------------------------------------- -_REPORTS = 0 -_TOOLS = 1 -_UNSUPPORTED = _("Unsupported") - -#------------------------------------------------------------------------- -# -# PluginDialog interface class -# -#------------------------------------------------------------------------- -class PluginDialog(ManagedWindow.ManagedWindow): - """ - Displays the dialog box that allows the user to select the - plugin that is desired. - """ - def __init__(self, state, uistate, track, categories, msg, - label=None, button_label=None, tool_tip=None, - content=_REPORTS): - """ - Display the dialog box, and build up the list of available - reports. This is used to build the selection tree on the left - hand side of the dialog box. - """ - self.active = uistate.get_active('Person') - self.imap = {} - self.msg = msg - self.content = content - self._pmgr = GuiPluginManager.get_instance() - - ManagedWindow.ManagedWindow.__init__(self, uistate, track, - self.__class__) - - self.state = state - self.uistate = uistate - - self.dialog = gtk.Builder() - self.dialog.add_from_file(const.PLUGINS_GLADE) - self.dialog.connect_signals({ - "on_report_apply_clicked" : self.on_apply_clicked, - "destroy_passed_object" : self.close, - "on_delete_event": self.close, - }) - - self.tree = self.dialog.get_object("tree") - window = self.dialog.get_object("report") - self.title = self.dialog.get_object("title") - - self.set_window(window, self.title, msg ) - - self.store = gtk.TreeStore(str) - self.selection = self.tree.get_selection() - self.selection.connect('changed', self.on_node_selected) - col = gtk.TreeViewColumn('', gtk.CellRendererText(), text=0) - self.tree.append_column(col) - self.tree.set_model(self.store) - - self.description = self.dialog.get_object("description") - if label: - self.description.set_text(label) - self.status = self.dialog.get_object("report_status") - - self.author_name = self.dialog.get_object("author_name") - self.author_email = self.dialog.get_object("author_email") - - self.apply_button = self.dialog.get_object("apply") - if button_label: - self.apply_button.set_label(button_label) - else: - self.apply_button.set_label(_("_Apply")) - self.apply_button.set_use_underline(True) - if tool_tip: - self.apply_button.set_tooltip_text(tool_tip) - - self.item = None - - if content == _REPORTS: - reg_list = self._pmgr.get_reg_reports() - elif content == _TOOLS: - reg_list = self._pmgr.get_reg_tools() - else: - reg_list = [] - self.build_plugin_tree(reg_list, categories) - self.show() - - def rebuild(self): - # This method needs to be overridden in the subclass - assert False, "This method needs to be overridden in the subclass." - - def build_menu_names(self, obj): - return (self.msg, None) - - def on_apply_clicked(self, obj): - """Execute the selected report""" - if not self.item: - return - self.run_plugin(self.item) - - def on_node_selected(self, obj): - """Updates the informational display on the right hand side of - the dialog box with the description of the selected report""" - - store, node = self.selection.get_selected() - if node: - path = store.get_path(node) - if not node or path not in self.imap: - return - pdata = self.imap[path] - - #(report_class, options_class, title, category, name, - # doc,status,author,email,unsupported,require_active) = data - self.description.set_text(pdata.description) - if not pdata.supported: - status = _UNSUPPORTED - self.status.set_text(pdata.statustext()) - self.title.set_text('%s' \ - % pdata.name) - self.title.set_use_markup(1) - self.author_name.set_text(', '.join(pdata.authors)) - self.author_email.set_text(', '.join(pdata.authors_email)) - self.item = pdata - - def build_plugin_tree(self, reg_list, categories): - """Populates a GtkTree with each menu item associated with a entry - in the lists. The list consists of PluginData objects for reports or - tools. - - old data was (item_class, options_class,title,category, name, - doc,status,author,email) - - Items in the same category are grouped under the same submenu. - The categories must be dicts from integer to string. - """ - ilist = [] - self.store.clear() - - # build the tree items and group together based on the category name - item_hash = defaultdict(list) - for plugin in reg_list: - if not plugin.supported: - category = _UNSUPPORTED - else: - category = categories[plugin.category] - item_hash[category].append(plugin) - - # add a submenu for each category, and populate it with the - # GtkTreeItems that are associated with it. - key_list = [item for item in item_hash if item != _UNSUPPORTED] - key_list.sort(reverse=True) - - prev = None - if _UNSUPPORTED in item_hash: - key = _UNSUPPORTED - data = item_hash[key] - node = self.store.insert_after(None, prev) - self.store.set(node, 0, key) - next = None - data.sort(lambda x, y: cmp(x.name, y.name)) - for item in data: - next = self.store.insert_after(node, next) - ilist.append((next, item)) - self.store.set(next, 0, item.name) - for key in key_list: - data = item_hash[key] - node = self.store.insert_after(None, prev) - self.store.set(node, 0, key) - next = None - data.sort(key=lambda k:k.name) - for item in data: - next = self.store.insert_after(node, next) - ilist.append((next, item)) - self.store.set(next, 0, item.name) - for next, tab in ilist: - path = self.store.get_path(next) - self.imap[path] = tab - - def run_plugin(self, pdata): - """ - run a plugin based on it's PluginData: - 1/ load plugin. - 2/ the report is run - """ - mod = self._pmgr.load_plugin(pdata) - if not mod: - #import of plugin failed - return - - if pdata.ptype == REPORT: - active_handle = self.uistate.get_active('Person') - report(self.state, self.uistate, - self.state.db.get_person_from_handle(active_handle), - eval('mod.' + pdata.reportclass), - eval('mod.' + pdata.optionclass), - pdata.name, pdata.id, - pdata.category, pdata.require_active) - else: - _Tool.gui_tool(self.state, self.uistate, - eval('mod.' + pdata.toolclass), - eval('mod.' + pdata.optionclass), - pdata.name, pdata.id, pdata.category, - self.state.db.request_rebuild) - -#------------------------------------------------------------------------- -# -# ReportPluginDialog -# -#------------------------------------------------------------------------- -class ReportPluginDialog(PluginDialog): - """ - Displays the dialog box that allows the user to select the - report that is desired. - """ - - def __init__(self, dbstate, uistate, track): - """Display the dialog box, and build up the list of available - reports. This is used to build the selection tree on the left - hand side of the dailog box.""" - - PluginDialog.__init__( - self, - dbstate, - uistate, - track, - standalone_categories, - _("Report Selection"), - _("Select a report from those available on the left."), - _("_Generate"), _("Generate selected report"), - _REPORTS) - - self._pmgr.connect('plugins-reloaded', self.rebuild) - - def rebuild(self): - report_list = self._pmgr.get_reg_reports() - self.build_plugin_tree(report_list, standalone_categories) - -#------------------------------------------------------------------------- -# -# ToolPluginDialog -# -#------------------------------------------------------------------------- -class ToolPluginDialog(PluginDialog): - """Displays the dialog box that allows the user to select the tool - that is desired.""" - - def __init__(self, dbstate, uistate, track): - """Display the dialog box, and build up the list of available - reports. This is used to build the selection tree on the left - hand side of the dailog box.""" - - PluginDialog.__init__( - self, - dbstate, - uistate, - track, - _Tool.tool_categories, - _("Tool Selection"), - _("Select a tool from those available on the left."), - _("_Run"), - _("Run selected tool"), - _TOOLS) - - def rebuild(self): - tool_list = self._pmgr.get_reg_tools() - self.build_plugin_tree(tool_list, _Tool.tool_categories) diff --git a/src/PluginUtils/_PluginWindows.py b/src/PluginUtils/_PluginWindows.py deleted file mode 100644 index 56cb22b77..000000000 --- a/src/PluginUtils/_PluginWindows.py +++ /dev/null @@ -1,1221 +0,0 @@ -# -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2000-2006 Donald N. Allingham -# Copyright (C) 2008 Brian G. Matherly -# -# 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$ - -#------------------------------------------------------------------------- -# -# Python modules -# -#------------------------------------------------------------------------- -import traceback -import os -import sys - -#------------------------------------------------------------------------- -# -# GTK modules -# -#------------------------------------------------------------------------- -import gtk -import pango -import gobject - -#------------------------------------------------------------------------- -# -# GRAMPS modules -# -#------------------------------------------------------------------------- -import ManagedWindow -import Errors -from gen.plug import PluginRegister, PTYPE_STR, make_environment -from gen.ggettext import gettext as _ -from gui.utils import open_file_with_default_application -from gui.pluginmanager import GuiPluginManager -import _Tool as Tool -from QuestionDialog import InfoDialog -from gui.editors import EditPerson -import Utils -import const -import config - -def display_message(message): - """ - A default callback for displaying messages. - """ - print message - -def version_str_to_tup(sversion, positions): - """ - Given a string version and positions count, returns a tuple of - integers. - - >>> version_str_to_tup("1.02.9", 2) - (1, 2) - """ - try: - tup = tuple(([int(n) for n in - sversion.split(".", sversion.count("."))] + - [0] * positions)[0:positions]) - except: - tup = (0,) * positions - return tup - -class newplugin(object): - """ - Fake newplugin. - """ - def __init__(self): - globals()["register_results"].append({}) - def __setattr__(self, attr, value): - globals()["register_results"][-1][attr] = value - -def register(ptype, **kwargs): - """ - Fake registration. Side-effect sets register_results to kwargs. - """ - retval = {"ptype": ptype} - retval.update(kwargs) - # Get the results back to calling function - if "register_results" in globals(): - globals()["register_results"].append(retval) - else: - globals()["register_results"] = [retval] - -class Zipfile(object): - """ - Class to duplicate the methods of tarfile.TarFile, for Python 2.5. - """ - def __init__(self, buffer): - import zipfile - self.buffer = buffer - self.zip_obj = zipfile.ZipFile(buffer) - - def extractall(self, path, members=None): - """ - Extract all of the files in the zip into path. - """ - names = self.zip_obj.namelist() - for name in self.get_paths(names): - fullname = os.path.join(path, name) - if not os.path.exists(fullname): - os.mkdir(fullname) - for name in self.get_files(names): - fullname = os.path.join(path, name) - outfile = file(fullname, 'wb') - outfile.write(self.zip_obj.read(name)) - outfile.close() - - def extractfile(self, name): - """ - Extract a name from the zip file. - - >>> Zipfile(buffer).extractfile("Dir/dile.py").read() - - """ - class ExtractFile(object): - def __init__(self, zip_obj, name): - self.zip_obj = zip_obj - self.name = name - def read(self): - data = self.zip_obj.read(self.name) - del self.zip_obj - return data - return ExtractFile(self.zip_obj, name) - - def close(self): - """ - Close the zip object. - """ - self.zip_obj.close() - - def getnames(self): - """ - Get the files and directories of the zipfile. - """ - return self.zip_obj.namelist() - - def get_paths(self, items): - """ - Get the directories from the items. - """ - return (name for name in items if self.is_path(name) and not self.is_file(name)) - - def get_files(self, items): - """ - Get the files from the items. - """ - return (name for name in items if self.is_file(name)) - - def is_path(self, name): - """ - Is the name a path? - """ - return os.path.split(name)[0] - - def is_file(self, name): - """ - Is the name a directory? - """ - return os.path.split(name)[1] - -#------------------------------------------------------------------------- -# -# PluginStatus: overview of all plugins -# -#------------------------------------------------------------------------- -class PluginStatus(ManagedWindow.ManagedWindow): - """Displays a dialog showing the status of loaded plugins""" - HIDDEN = '%s' % _('Hidden') - AVAILABLE = '%s'\ - % _('Visible') - - def __init__(self, uistate, track=[]): - self.__uistate = uistate - self.title = _("Plugin Manager") - ManagedWindow.ManagedWindow.__init__(self, uistate, track, - self.__class__) - - self.__pmgr = GuiPluginManager.get_instance() - self.__preg = PluginRegister.get_instance() - self.set_window(gtk.Dialog("", uistate.window, - gtk.DIALOG_DESTROY_WITH_PARENT, - (gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE)), - None, self.title) - self.window.set_size_request(750, 400) - self.window.connect('response', self.close) - - notebook = gtk.Notebook() - - #first page with all registered plugins - vbox_reg = gtk.VBox() - scrolled_window_reg = gtk.ScrolledWindow() - self.list_reg = gtk.TreeView() - # model: plugintype, hidden, pluginname, plugindescr, pluginid - self.model_reg = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING, - gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING) - self.selection_reg = self.list_reg.get_selection() - self.list_reg.set_model(self.model_reg) - self.list_reg.set_rules_hint(True) - self.list_reg.connect('button-press-event', self.button_press_reg) - col0_reg = gtk.TreeViewColumn(_('Type'), gtk.CellRendererText(), text=0) - col0_reg.set_sort_column_id(0) - col0_reg.set_resizable(True) - self.list_reg.append_column(col0_reg) - col = gtk.TreeViewColumn(_('Status'), gtk.CellRendererText(), markup=1) - col.set_sort_column_id(1) - self.list_reg.append_column(col) - col2_reg = gtk.TreeViewColumn(_('Name'), gtk.CellRendererText(), text=2) - col2_reg.set_sort_column_id(2) - col2_reg.set_resizable(True) - self.list_reg.append_column(col2_reg) - col = gtk.TreeViewColumn(_('Description'), gtk.CellRendererText(), text=3) - col.set_sort_column_id(3) - col.set_resizable(True) - self.list_reg.append_column(col) - self.list_reg.set_search_column(2) - - scrolled_window_reg.add(self.list_reg) - vbox_reg.pack_start(scrolled_window_reg) - hbutbox = gtk.HButtonBox() - hbutbox.set_layout(gtk.BUTTONBOX_SPREAD) - self.__info_btn = gtk.Button(_("Info")) - hbutbox.add(self.__info_btn) - self.__info_btn.connect('clicked', self.__info, self.list_reg, 4) # id_col - self.__hide_btn = gtk.Button(_("Hide/Unhide")) - hbutbox.add(self.__hide_btn) - self.__hide_btn.connect('clicked', self.__hide, - self.list_reg, 4, 1) # list, id_col, hide_col - if __debug__: - self.__edit_btn = gtk.Button(_("Edit")) - hbutbox.add(self.__edit_btn) - self.__edit_btn.connect('clicked', self.__edit, self.list_reg, 4) # id_col - self.__load_btn = gtk.Button(_("Load")) - hbutbox.add(self.__load_btn) - self.__load_btn.connect('clicked', self.__load, self.list_reg, 4) # id_col - vbox_reg.pack_start(hbutbox, expand=False, padding=5) - - notebook.append_page(vbox_reg, - tab_label=gtk.Label(_('Registered Plugins'))) - - #second page with loaded plugins - vbox_loaded = gtk.VBox() - scrolled_window = gtk.ScrolledWindow() - self.list = gtk.TreeView() - self.model = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING, - gobject.TYPE_STRING, object, - gobject.TYPE_STRING, gobject.TYPE_STRING) - self.selection = self.list.get_selection() - self.list.set_model(self.model) - self.list.set_rules_hint(True) - self.list.connect('button-press-event', self.button_press) - col = gtk.TreeViewColumn(_('Loaded'), gtk.CellRendererText(), - markup=0) - col.set_sort_column_id(0) - col.set_resizable(True) - self.list.append_column(col) - col1 = gtk.TreeViewColumn(_('File'), gtk.CellRendererText(), - text=1) - col1.set_sort_column_id(1) - col1.set_resizable(True) - self.list.append_column(col1) - col = gtk.TreeViewColumn(_('Status'), gtk.CellRendererText(), - markup=5) - col.set_sort_column_id(5) - self.list.append_column(col) - col2 = gtk.TreeViewColumn(_('Message'), gtk.CellRendererText(), text=2) - col2.set_sort_column_id(2) - col2.set_resizable(True) - self.list.append_column(col2) - self.list.set_search_column(1) - - scrolled_window.add(self.list) - vbox_loaded.pack_start(scrolled_window) - hbutbox = gtk.HButtonBox() - hbutbox.set_layout(gtk.BUTTONBOX_SPREAD) - self.__info_btn = gtk.Button(_("Info")) - hbutbox.add(self.__info_btn) - self.__info_btn.connect('clicked', self.__info, self.list, 4) # id_col - self.__hide_btn = gtk.Button(_("Hide/Unhide")) - hbutbox.add(self.__hide_btn) - self.__hide_btn.connect('clicked', self.__hide, - self.list, 4, 5) # list, id_col, hide_col - - if __debug__: - self.__edit_btn = gtk.Button(_("Edit")) - hbutbox.add(self.__edit_btn) - self.__edit_btn.connect('clicked', self.__edit, self.list, 4) # id_col - self.__load_btn = gtk.Button(_("Load")) - hbutbox.add(self.__load_btn) - self.__load_btn.connect('clicked', self.__load, self.list, 4) # id_col - vbox_loaded.pack_start(hbutbox, expand=False, padding=5) - notebook.append_page(vbox_loaded, - tab_label=gtk.Label(_('Loaded Plugins'))) - - #third page with method to install plugin - install_page = gtk.VBox() - scrolled_window = gtk.ScrolledWindow() - self.addon_list = gtk.TreeView() - # model: help_name, name, ptype, image, desc, use, rating, contact, download, url - self.addon_model = gtk.ListStore(gobject.TYPE_STRING, - gobject.TYPE_STRING, - gobject.TYPE_STRING, - gobject.TYPE_STRING, - gobject.TYPE_STRING, - gobject.TYPE_STRING, - gobject.TYPE_STRING, - gobject.TYPE_STRING, - gobject.TYPE_STRING, - gobject.TYPE_STRING) - self.addon_list.set_model(self.addon_model) - self.addon_list.set_rules_hint(True) - #self.addon_list.connect('button-press-event', self.button_press) - col = gtk.TreeViewColumn(_('Addon Name'), gtk.CellRendererText(), - text=1) - col.set_sort_column_id(1) - self.addon_list.append_column(col) - col = gtk.TreeViewColumn(_('Type'), gtk.CellRendererText(), - text=2) - col.set_sort_column_id(2) - self.addon_list.append_column(col) - col = gtk.TreeViewColumn(_('Description'), gtk.CellRendererText(), - text=4) - col.set_sort_column_id(4) - self.addon_list.append_column(col) - self.addon_list.connect('cursor-changed', self.button_press_addon) - - install_row = gtk.HBox() - install_row.pack_start(gtk.Label(_("Path to Addon:")), expand=False) - self.install_addon_path = gtk.Entry() - - button = gtk.Button() - img = gtk.Image() - img.set_from_stock(gtk.STOCK_OPEN, gtk.ICON_SIZE_BUTTON) - button.add(img) - button.connect('clicked', self.__select_file) - install_row.pack_start(self.install_addon_path, expand=True) - install_row.pack_start(button, expand=False, fill=False) - - scrolled_window.add(self.addon_list) - install_page.pack_start(scrolled_window) - #add some spce under the scrollbar - install_page.pack_start(gtk.Label(''), expand=False, fill=False) - #path to addon path line - install_page.pack_start(install_row, expand=False, fill=False) - - hbutbox = gtk.HButtonBox() - hbutbox.set_layout(gtk.BUTTONBOX_SPREAD) - self.__add_btn = gtk.Button(_("Install Addon")) - hbutbox.add(self.__add_btn) - self.__add_btn.connect('clicked', self.__get_addon_top) - self.__add_all_btn = gtk.Button(_("Install All Addons")) - hbutbox.add(self.__add_all_btn) - self.__add_all_btn.connect('clicked', self.__get_all_addons) - self.__refresh_btn = gtk.Button(_("Refresh Addon List")) - hbutbox.add(self.__refresh_btn) - self.__refresh_btn.connect('clicked', self.__refresh_addon_list) - install_page.pack_start(hbutbox, expand=False, padding=5) - notebook.append_page(install_page, - tab_label=gtk.Label(_('Install Addons'))) - - #add the notebook to the window - self.window.vbox.add(notebook) - - if __debug__: - # Only show the "Reload" button when in debug mode - # (without -O on the command line) - self.__reload_btn = gtk.Button(_("Reload")) - self.window.action_area.add(self.__reload_btn) - self.__reload_btn.connect('clicked', self.__reload) - - #obtain hidden plugins from the pluginmanager - self.hidden = self.__pmgr.get_hidden_plugin_ids() - - self.window.show_all() - self.__populate_lists() - self.list_reg.columns_autosize() - - def __refresh_addon_list(self, obj): - """ - Reloads the addons from the wiki into the list. - """ - import urllib - from gui.utils import ProgressMeter - URL = "%s%s" % (const.URL_WIKISTRING, const.WIKI_EXTRAPLUGINS_RAWDATA) - try: - fp = urllib.urlopen(URL) - except: - print "Error: cannot open %s" % URL - return - pm = ProgressMeter(_("Refreshing Addon List")) - pm.set_pass(header=_("Reading gramps-project.org...")) - state = "read" - rows = [] - row = [] - lines = fp.readlines() - pm.set_pass(total=len(lines), header=_("Reading gramps-project.org...")) - for line in lines: - pm.step() - if line.startswith("|-") or line.startswith("|}"): - if row != []: - rows.append(row) - state = "row" - row = [] - elif state == "row": - if line.startswith("|"): - row.append(line[1:].strip()) - else: - state = "read" - fp.close() - rows.sort(key=lambda row: (row[1], row[0])) - self.addon_model.clear() - # clear the config list: - config.get('plugin.addonplugins')[:] = [] - pm.set_pass(total=len(rows), header=_("Checking addon...")) - for row in rows: - pm.step() - try: - # from wiki: - help_name, ptype, image, desc, use, rating, contact, download = row - except: - continue - help_url = _("Unknown Help URL") - if help_name.startswith("[[") and help_name.endswith("]]"): - name = help_name[2:-2] - if "|" in name: - help_url, name = name.split("|", 1) - elif help_name.startswith("[") and help_name.endswith("]"): - name = help_name[1:-1] - if " " in name: - help_url, name = name.split(" ", 1) - else: - name = help_name - url = _("Unknown URL") - if download.startswith("[[") and download.endswith("]]"): - # Not directly possible to get the URL: - url = download[2:-2] - if "|" in url: - url, text = url.split("|", 1) - # need to get a page that says where it is: - fp = urllib.urlopen("%s%s%s" % (const.URL_WIKISTRING, url, - "&action=edit&externaledit=true&mode=file")) - for line in fp: - if line.startswith("URL="): - junk, url = line.split("=", 1) - break - fp.close() - elif download.startswith("[") and download.endswith("]"): - url = download[1:-1] - if " " in url: - url, text = url.split(" ", 1) - if (url.endswith(".zip") or - url.endswith(".ZIP") or - url.endswith(".tar.gz") or - url.endswith(".tgz")): - # Then this is ok: - self.addon_model.append(row=[help_name, name, ptype, image, desc, use, - rating, contact, download, url]) - config.get('plugin.addonplugins').append([help_name, name, ptype, image, desc, use, - rating, contact, download, url]) - pm.close() - config.save() - - def __get_all_addons(self, obj): - """ - Get all addons from the wiki and install them. - """ - import urllib - from gui.utils import ProgressMeter - pm = ProgressMeter(_("Install all Addons"), _("Installing..."), message_area=True) - pm.set_pass(total=len(self.addon_model)) - for row in self.addon_model: - pm.step() - (help_name, name, ptype, image, desc, use, rating, contact, - download, url) = row - self.__load_addon_file(url, callback=pm.append_message) - pm.message_area_ok.set_sensitive(True) - self.__rebuild_load_list() - self.__rebuild_reg_list() - - def __get_addon_top(self, obj): - """ - Toplevel method to get an addon. - """ - from gui.utils import ProgressMeter - pm = ProgressMeter(_("Installing Addon"), message_area=True) - pm.set_pass(total=2, header=_("Reading gramps-project.org...")) - pm.step() - self.__get_addon(obj, callback=pm.append_message) - pm.step() - pm.message_area_ok.set_sensitive(True) - - def __get_addon(self, obj, callback=display_message): - """ - Get an addon from the wiki or file system and install it. - """ - path = self.install_addon_path.get_text() - self.__load_addon_file(path, callback) - self.__rebuild_load_list() - self.__rebuild_reg_list() - - def __load_addon_file(self, path, callback=display_message): - """ - Load an addon from a particular path (from URL or file system). - """ - import urllib - import tarfile - import cStringIO - if (path.startswith("http://") or - path.startswith("https://") or - path.startswith("ftp://")): - try: - fp = urllib.urlopen(path) - except: - callback(_("Unable to open '%s'") % path) - return - else: - try: - fp = open(path) - except: - callback(_("Unable to open '%s'") % path) - return - buffer = cStringIO.StringIO(fp.read()) - fp.close() - # file_obj is either Zipfile or TarFile - if path.endswith(".zip") or path.endswith(".ZIP"): - file_obj = Zipfile(buffer) - elif path.endswith(".tar.gz") or path.endswith(".tgz"): - try: - file_obj = tarfile.open(None, fileobj=buffer) - except: - callback(_("Error: cannot open '%s'") % path) - return - else: - callback(_("Error: unknown file type: '%s'") % path) - return - # First, see what versions we have/are getting: - good_gpr = set() - for gpr_file in [name for name in file_obj.getnames() if name.endswith(".gpr.py")]: - callback((_("Examining '%s'...") % gpr_file) + "\n") - contents = file_obj.extractfile(gpr_file).read() - # Put a fake register and _ function in environment: - env = make_environment(register=register, - newplugin=newplugin, - _=lambda text: text) - # clear out the result variable: - globals()["register_results"] = [] - # evaluate the contents: - try: - exec(contents, env) - except: - msg = _("Error in '%s' file: cannot load.") % gpr_file - callback(" " + msg + "\n") - continue - # There can be multiple addons per gpr file: - for results in globals()["register_results"]: - gramps_target_version = results.get("gramps_target_version", None) - if gramps_target_version: - vtup = version_str_to_tup(gramps_target_version, 2) - # Is it for the right version of gramps? - if vtup == const.VERSION_TUPLE[0:2]: - # If this version is not installed, or > installed, install it - good_gpr.add(gpr_file) - callback(" " + (_("'%s' is for this version of Gramps.") % gpr_file) + "\n") - else: - # If the plugin is for another version; inform and do nothing - callback(" " + (_("'%s' is NOT for this version of Gramps.") % gpr_file) + "\n") - callback(" " + (_("It is for version %d.%d" % vtup) + "\n")) - continue - else: - # another register function doesn't have gramps_target_version - if gpr_file in good_gpr: - s.remove(gpr_file) - callback(" " + (_("Error: missing gramps_target_version in '%s'...") % gpr_file) + "\n") - if len(good_gpr) > 0: - # Now, install the ok ones - file_obj.extractall(const.USER_PLUGINS) - callback((_("Installing '%s'...") % path) + "\n") - gpr_files = set([os.path.split(os.path.join(const.USER_PLUGINS, name))[0] - for name in good_gpr]) - for gpr_file in gpr_files: - # Convert gpr_file to unicode otherwise the callback will not - # work with non ASCII characters in filenames in Windows. - # But don't use converted filenames - # in the call to self.__pmgr.reg_plugins - # as that will break in reg_plugins. - u_gpr_file = unicode(gpr_file, sys.getfilesystemencoding()) - callback(" " + (_("Registered '%s'") % u_gpr_file) + "\n") - self.__pmgr.reg_plugins(gpr_file) - - file_obj.close() - - def __select_file(self, obj): - """ - Select a file from the file system. - """ - fcd = gtk.FileChooserDialog(_("Load Addon"), - buttons=(gtk.STOCK_CANCEL, - gtk.RESPONSE_CANCEL, - gtk.STOCK_OPEN, - gtk.RESPONSE_OK)) - name = self.install_addon_path.get_text() - dir = os.path.dirname(name) - if not os.path.isdir(dir): - dir = const.USER_HOME - name = '' - elif not os.path.isfile(name): - name = '' - fcd.set_current_folder(dir) - if name: - fcd.set_filename(name) - - status = fcd.run() - if status == gtk.RESPONSE_OK: - path = Utils.get_unicode_path(fcd.get_filename()) - if path: - self.install_addon_path.set_text(path) - fcd.destroy() - - def __populate_lists(self): - """ Build the lists of plugins """ - self.__populate_load_list() - self.__populate_reg_list() - self.__populate_addon_list() - - def __populate_addon_list(self): - """ - Build the list of addons from the config setting. - """ - self.addon_model.clear() - for row in config.get('plugin.addonplugins'): - try: - help_name, name, ptype, image, desc, use, rating, contact, download, url = row - except: - continue - self.addon_model.append(row=[help_name, name, ptype, image, desc, use, - rating, contact, download, url]) - - def __populate_load_list(self): - """ Build list of loaded plugins""" - fail_list = self.__pmgr.get_fail_list() - - for i in fail_list: - # i = (filename, (exception-type, exception, traceback), pdata) - err = i[1][0] - pdata = i[2] - hidden = pdata.id in self.hidden - if hidden: - hiddenstr = self.HIDDEN - else: - hiddenstr = self.AVAILABLE - if err == Errors.UnavailableError: - self.model.append(row=[ - '%s' % _('Unavailable'), - i[0], str(i[1][1]), None, pdata.id, hiddenstr]) - else: - self.model.append(row=[ - '%s' % _('Fail'), - i[0], str(i[1][1]), i[1], pdata.id, hiddenstr]) - - success_list = self.__pmgr.get_success_list() - for i in success_list: - # i = (filename, module, pdata) - pdata = i[2] - modname = i[1].__name__ - hidden = pdata.id in self.hidden - if hidden: - hiddenstr = self.HIDDEN - else: - hiddenstr = self.AVAILABLE - self.model.append(row=[ - '%s' % _("OK"), - i[0], pdata.description, None, pdata.id, hiddenstr]) - - def __populate_reg_list(self): - """ Build list of registered plugins""" - for (type, typestr) in PTYPE_STR.iteritems(): - for pdata in self.__preg.type_plugins(type): - # model: plugintype, hidden, pluginname, plugindescr, pluginid - hidden = pdata.id in self.hidden - if hidden: - hiddenstr = self.HIDDEN - else: - hiddenstr = self.AVAILABLE - self.model_reg.append(row=[ - typestr, hiddenstr, pdata.name, pdata.description, - pdata.id]) - - def __rebuild_load_list(self): - self.model.clear() - self.__populate_load_list() - - def __rebuild_reg_list(self): - self.model_reg.clear() - self.__populate_reg_list() - - def button_press(self, obj, event): - """ Callback function from the user clicking on a line """ - if event.type == gtk.gdk._2BUTTON_PRESS and event.button == 1: - model, node = self.selection.get_selected() - data = model.get_value(node, 3) - name = model.get_value(node, 1) - if data: - PluginTrace(self.uistate, [], data, name) - - def button_press_reg(self, obj, event): - """ Callback function from the user clicking on a line in reg plugin - """ - if event.type == gtk.gdk._2BUTTON_PRESS and event.button == 1: - self.__info(obj, self.list_reg, 4) - - def button_press_addon(self, obj): - """ Callback function from the user clicking on a line in reg plugin - """ - import urllib - selection = self.addon_list.get_selection() - model, node = selection.get_selected() - if node: - url = model.get_value(node, 9) - self.install_addon_path.set_text(url) - - def build_menu_names(self, obj): - return (self.title, "") - - def __reload(self, obj): - """ Callback function from the "Reload" button """ - self.__pmgr.reload_plugins() - self.__rebuild_load_list() - self.__rebuild_reg_list() - - def __info(self, obj, list_obj, id_col): - """ Callback function from the "Info" button - """ - selection = list_obj.get_selection() - model, node = selection.get_selected() - if not node: - return - id = model.get_value(node, id_col) - pdata = self.__preg.get_plugin(id) - typestr = pdata.ptype - auth = ' - '.join(pdata.authors) - email = ' - '.join(pdata.authors_email) - if len(auth) > 60: - auth = auth[:60] + '...' - if len(email) > 60: - email = email[:60] + '...' - if pdata: - infotxt = """Plugin name: %(name)s [%(typestr)s] - -Description: %(descr)s -Authors: %(authors)s -Email: %(email)s -Filename: %(fname)s -Location: %(fpath)s -""" % { - 'name': pdata.name, - 'typestr': typestr, - 'descr': pdata.description, - 'authors': auth, - 'email': email, - 'fname': pdata.fname, - 'fpath': pdata.fpath, - } - InfoDialog('Detailed Info', infotxt, parent=self.window) - - def __hide(self, obj, list_obj, id_col, hide_col): - """ Callback function from the "Hide" button - """ - selection = list_obj.get_selection() - model, node = selection.get_selected() - if not node: - return - id = model.get_value(node, id_col) - if id in self.hidden: - #unhide - self.hidden.remove(id) - model.set_value(node, hide_col, self.AVAILABLE) - self.__pmgr.unhide_plugin(id) - else: - #hide - self.hidden.add(id) - model.set_value(node, hide_col, self.HIDDEN) - self.__pmgr.hide_plugin(id) - - def __load(self, obj, list_obj, id_col): - """ Callback function from the "Load" button - """ - selection = list_obj.get_selection() - model, node = selection.get_selected() - if not node: - return - idv = model.get_value(node, id_col) - pdata = self.__preg.get_plugin(idv) - self.__pmgr.load_plugin(pdata) - self.__rebuild_load_list() - - def __edit(self, obj, list_obj, id_col): - """ Callback function from the "Load" button - """ - selection = list_obj.get_selection() - model, node = selection.get_selected() - if not node: - return - id = model.get_value(node, id_col) - pdata = self.__preg.get_plugin(id) - open_file_with_default_application( - os.path.join(pdata.fpath, pdata.fname) - ) - -#------------------------------------------------------------------------- -# -# Details for an individual plugin that failed -# -#------------------------------------------------------------------------- -class PluginTrace(ManagedWindow.ManagedWindow): - """Displays a dialog showing the status of loaded plugins""" - - def __init__(self, uistate, track, data, name): - self.name = name - title = "%s: %s" % (_("Plugin Error"), name) - ManagedWindow.ManagedWindow.__init__(self, uistate, track, self) - - self.set_window(gtk.Dialog("", uistate.window, - gtk.DIALOG_DESTROY_WITH_PARENT, - (gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE)), - None, title) - self.window.set_size_request(600, 400) - self.window.connect('response', self.close) - - scrolled_window = gtk.ScrolledWindow() - scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - self.text = gtk.TextView() - scrolled_window.add(self.text) - self.text.get_buffer().set_text( - "".join(traceback.format_exception(data[0],data[1],data[2]))) - - self.window.vbox.add(scrolled_window) - self.window.show_all() - - def build_menu_names(self, obj): - return (self.name, None) - - -#------------------------------------------------------------------------- -# -# Classes for tools -# -#------------------------------------------------------------------------- -class LinkTag(gtk.TextTag): - def __init__(self, link, buffer): - gtk.TextTag.__init__(self, link) - tag_table = buffer.get_tag_table() - self.set_property('foreground', "#0000ff") - self.set_property('underline', pango.UNDERLINE_SINGLE) - try: - tag_table.add(self) - except ValueError: - pass # already in table - -class ToolManagedWindowBase(ManagedWindow.ManagedWindow): - """ - Copied from src/ReportBase/_BareReportDialog.py BareReportDialog - """ - border_pad = 6 - HELP_TOPIC = None - def __init__(self, dbstate, uistate, option_class, name, callback=None): - self.name = name - ManagedWindow.ManagedWindow.__init__(self, uistate, [], self) - - self.extra_menu = None - self.widgets = [] - self.frame_names = [] - self.frames = {} - self.format_menu = None - self.style_button = None - - window = gtk.Dialog('Tool') - self.set_window(window, None, self.get_title()) - #self.window.set_has_separator(False) - - #self.window.connect('response', self.close) - self.cancel = self.window.add_button(gtk.STOCK_CLOSE, - gtk.RESPONSE_CANCEL) - self.cancel.connect('clicked', self.close) - - self.ok = self.window.add_button(gtk.STOCK_EXECUTE, gtk.RESPONSE_OK) - self.ok.connect('clicked', self.on_ok_clicked) - - 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.notebook = gtk.Notebook() - self.notebook.set_border_width(6) - self.window.vbox.add(self.notebook) - - self.results_text = gtk.TextView() - self.results_text.connect('button-press-event', - self.on_button_press) - self.results_text.connect('motion-notify-event', - self.on_motion) - self.tags = [] - self.link_cursor = gtk.gdk.Cursor(gtk.gdk.LEFT_PTR) - self.standard_cursor = gtk.gdk.Cursor(gtk.gdk.XTERM) - - self.setup_other_frames() - self.set_current_frame(self.initial_frame()) - self.show() - - #------------------------------------------------------------------------ - # - # Callback functions from the dialog - # - #------------------------------------------------------------------------ - def on_cancel(self, *obj): - pass # cancel just closes - - def on_ok_clicked(self, obj): - """ - The user is satisfied with the dialog choices. Parse all options - and run the tool. - """ - # Save options - self.options.parse_user_options(self) - self.options.handler.save_options() - self.pre_run() - self.run() # activate results tab - self.post_run() - - def initial_frame(self): - return None - - def on_motion(self, view, event): - buffer_location = view.window_to_buffer_coords(gtk.TEXT_WINDOW_TEXT, - int(event.x), - int(event.y)) - iter = view.get_iter_at_location(*buffer_location) - for (tag, person_handle) in self.tags: - if iter.has_tag(tag): - _window = view.get_window(gtk.TEXT_WINDOW_TEXT) - _window.set_cursor(self.link_cursor) - return False # handle event further, if necessary - view.get_window(gtk.TEXT_WINDOW_TEXT).set_cursor(self.standard_cursor) - return False # handle event further, if necessary - - def on_button_press(self, view, event): - buffer_location = view.window_to_buffer_coords(gtk.TEXT_WINDOW_TEXT, - int(event.x), - int(event.y)) - iter = view.get_iter_at_location(*buffer_location) - for (tag, person_handle) in self.tags: - if iter.has_tag(tag): - person = self.db.get_person_from_handle(person_handle) - if event.button == 1: - if event.type == gtk.gdk._2BUTTON_PRESS: - try: - EditPerson(self.dbstate, self.uistate, [], person) - except Errors.WindowActiveError: - pass - else: - self.uistate.set_active(person_handle, 'Person') - return True # handled event - return False # did not handle event - - def results_write_link(self, text, person, person_handle): - self.results_write(" ") - buffer = self.results_text.get_buffer() - iter = buffer.get_end_iter() - offset = buffer.get_char_count() - self.results_write(text) - start = buffer.get_iter_at_offset(offset) - end = buffer.get_end_iter() - self.tags.append((LinkTag(person_handle, buffer), person_handle)) - buffer.apply_tag(self.tags[-1][0], start, end) - - def results_write(self, text): - buffer = self.results_text.get_buffer() - mark = buffer.create_mark("end", buffer.get_end_iter()) - self.results_text.scroll_to_mark(mark, 0) - buffer.insert_at_cursor(text) - buffer.delete_mark_by_name("end") - - def write_to_page(self, page, text): - buffer = page.get_buffer() - mark = buffer.create_mark("end", buffer.get_end_iter()) - self.results_text.scroll_to_mark(mark, 0) - buffer.insert_at_cursor(text) - buffer.delete_mark_by_name("end") - - def clear(self, text): - # Remove all tags and clear text - buffer = text.get_buffer() - tag_table = buffer.get_tag_table() - start = buffer.get_start_iter() - end = buffer.get_end_iter() - for (tag, handle) in self.tags: - buffer.remove_tag(tag, start, end) - tag_table.remove(tag) - self.tags = [] - buffer.set_text("") - - def results_clear(self): - # Remove all tags and clear text - buffer = self.results_text.get_buffer() - tag_table = buffer.get_tag_table() - start = buffer.get_start_iter() - end = buffer.get_end_iter() - for (tag, handle) in self.tags: - buffer.remove_tag(tag, start, end) - tag_table.remove(tag) - self.tags = [] - buffer.set_text("") - - def pre_run(self): - from gui.utils import ProgressMeter - self.progress = ProgressMeter(self.get_title()) - - def run(self): - raise NotImplementedError, "tool needs to define a run() method" - - def post_run(self): - self.progress.close() - - #------------------------------------------------------------------------ - # - # Functions related to setting up the dialog window. - # - #------------------------------------------------------------------------ - def get_title(self): - """The window title for this dialog""" - return "Tool" # self.title - - 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 self.get_title() - - 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.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.get_title()) - label = gtk.Label('%s' % title) - label.set_use_markup(True) - self.window.vbox.pack_start(label, False, False, self.border_pad) - - def add_frame_option(self, frame_name, label_text, widget): - """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 frame_name in self.frames: - self.frames[frame_name].append((label_text, widget)) - else: - self.frames[frame_name] = [(label_text, widget)] - self.frame_names.append(frame_name) - - def set_current_frame(self, name): - if name is None: - self.notebook.set_current_page(0) - else: - for frame_name in self.frame_names: - if name == frame_name: - if len(self.frames[frame_name]) > 0: - fname, child = self.frames[frame_name][0] - page = self.notebook.page_num(child) - self.notebook.set_current_page(page) - return - - def add_results_frame(self, frame_name="Results"): - if frame_name not in self.frames: - window = gtk.ScrolledWindow() - window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - window.add(self.results_text) - window.set_shadow_type(gtk.SHADOW_IN) - self.frames[frame_name] = [[frame_name, window]] - self.frame_names.append(frame_name) - l = gtk.Label("%s" % _(frame_name)) - l.set_use_markup(True) - self.notebook.append_page(window, l) - self.notebook.show_all() - else: - self.results_clear() - return self.results_text - - def add_page(self, frame_name="Help"): - if frame_name not in self.frames: - text = gtk.TextView() - text.set_wrap_mode(gtk.WRAP_WORD) - window = gtk.ScrolledWindow() - window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - window.add(text) - window.set_shadow_type(gtk.SHADOW_IN) - self.frames[frame_name] = [[frame_name, window]] - self.frame_names.append(frame_name) - l = gtk.Label("%s" % _(frame_name)) - l.set_use_markup(True) - self.notebook.append_page(window, l) - self.notebook.show_all() - else: - # FIXME: get text - # - text = self.frames[frame_name][0][1].something - return text - - def setup_other_frames(self): - """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.""" - 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 += 1 - self.notebook.show_all() - - #------------------------------------------------------------------------ - # - # 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 build_menu_names(self, obj): - return (_('Main window'), self.get_title()) - - - -class ToolManagedWindowBatch(Tool.BatchTool, ToolManagedWindowBase): - def __init__(self, dbstate, uistate, options_class, name, callback=None): - # This constructor will ask a question, set self.fail: - self.dbstate = dbstate - self.uistate = uistate - Tool.BatchTool.__init__(self, dbstate, options_class, name) - if not self.fail: - ToolManagedWindowBase.__init__(self, dbstate, uistate, - options_class, name, callback) - -class ToolManagedWindow(Tool.Tool, ToolManagedWindowBase): - def __init__(self, dbstate, uistate, options_class, name, callback=None): - self.dbstate = dbstate - self.uistate = uistate - Tool.Tool.__init__(self, dbstate, options_class, name) - ToolManagedWindowBase.__init__(self, dbstate, uistate, options_class, - name, callback) diff --git a/src/PluginUtils/_Tool.py b/src/PluginUtils/_Tool.py deleted file mode 100644 index 98f8b990e..000000000 --- a/src/PluginUtils/_Tool.py +++ /dev/null @@ -1,310 +0,0 @@ -# -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2005-2007 Donald N. Allingham -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -# - -# $Id$ - -"ToolGeneration Framework" - -#------------------------------------------------------------------------- -# -# Python modules -# -#------------------------------------------------------------------------- -from types import ClassType, InstanceType -from gen.ggettext import gettext as _ -import logging -log = logging.getLogger(".") - -#------------------------------------------------------------------------- -# -# GRAMPS modules -# -#------------------------------------------------------------------------- -import const -import Utils -from gen.display.name import displayer as name_displayer -import Errors -from PluginUtils import (Options, OptionHandler, OptionList, - OptionListCollection) -from gen.plug import (TOOL_DEBUG, TOOL_ANAL, TOOL_DBPROC, TOOL_DBFIX, - TOOL_REVCTL, TOOL_UTILS) - -#------------------------------------------------------------------------- -# -# Constants -# -#------------------------------------------------------------------------- - -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"), - } - -#------------------------------------------------------------------------- -# -# Tool -# -#------------------------------------------------------------------------- -class Tool(object): - """ - 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, dbstate, options_class, name): - from PluginUtils import MenuToolOptions - self.db = dbstate.db - try: - if issubclass(options_class, MenuToolOptions): - # FIXME: pass in person_id - self.options = options_class(name, None, dbstate) - else: # must be some kind of class or we get a TypeError - self.options = options_class(name) - except TypeError: - self.options = options_class - - self.options.load_previous_values() - - def run_tool(self): - pass - - -class BatchTool(Tool): - """ - Same as Tool, except the warning is displayed about the potential - loss of undo history. Should be used for tools using batch transactions. - - """ - - def __init__(self, dbstate, options_class, name): - from QuestionDialog import QuestionDialog2 - warn_dialog = QuestionDialog2( - _('Undo history warning'), - _('Proceeding with this tool will erase the undo history ' - 'for this session. In particular, you will not be able ' - 'to revert the changes made by this tool or any changes ' - 'made prior to it.\n\n' - 'If you think you may want to revert running this tool, ' - 'please stop here and backup your database.'), - _('_Proceed with the tool'), _('_Stop')) - if not warn_dialog.run(): - self.fail = True - return - - Tool.__init__(self, dbstate, options_class, name) - self.fail = False - - -class ActivePersonTool(Tool): - """ - Same as Tool , except the existence of the active person is checked - and the tool is aborted if no active person exists. Should be used - for tools that depend on active person. - """ - - def __init__(self, dbstate, uistate, options_class, name): - - if not uistate.get_active('Person'): - from QuestionDialog import ErrorDialog - - ErrorDialog(_('Active person has not been set'), - _('You must select an active person for this ' - 'tool to work properly.')) - self.fail = True - return - - Tool.__init__(self, dbstate, options_class, name) - self.fail = False - -#------------------------------------------------------------------------ -# -# Command-line tool -# -#------------------------------------------------------------------------ -class CommandLineTool(object): - """ - Provide 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.option_class.load_previous_values() - 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."], } - - if noopt: - return - - # Add tool-specific options - for key in self.option_class.handler.options_dict: - if key not in self.options_dict: - 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: - if key not in self.options_help: - self.options_help[key] = self.option_class.options_help[key] - - def parse_option_str(self): - for opt in self.options_str_dict: - if opt in self.options_dict: - 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 in self.database.iter_people(): - id_list.append("%s\t%s" % ( - person.get_gramps_id(), - name_displayer.display(person))) - self.options_help['id'].append(id_list) - self.options_help['id'].append(False) - - def show_options(self): - if not self.show: - return - elif self.show == 'all': - print " Available options:" - for key in self.options_dict: - print " %s" % key - print " Use 'show=option' to see description and acceptable values" - elif self.show in self.options_dict: - 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 isinstance(vals, (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(dbstate, uistate, tool_class, options_class, translated_name, - name, category, callback): - """ - 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(dbstate, uistate, options_class, name, callback) - except Errors.WindowActiveError: - pass - except: - log.error("Failed to start tool.", exc_info=True) - -# Command-line generic task -def cli_tool(dbstate, name,category,tool_class, options_class, options_str_dict): - - clt = CommandLineTool(dbstate.db, name,category, - options_class, options_str_dict) - - # Exit here if show option was given - if clt.show: - return - - # run tool - try: - tool_class(dbstate,None, options_class, name,None) - except: - log.error("Failed to start tool.", exc_info=True) - -#------------------------------------------------------------------------- -# -# Class handling options for plugins -# -#------------------------------------------------------------------------- -class ToolOptionHandler(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): - """ - Initialize the class, performing usual house-keeping tasks. - Subclasses MUST call this in their __init__() method. - """ - self.name = name - self.person_id = person_id - self.options_dict = {} - self.options_help = {} - self.handler = None - - def load_previous_values(self): - self.handler = ToolOptionHandler(self.name,self.options_dict,self.person_id) - diff --git a/src/PluginUtils/__init__.py b/src/PluginUtils/__init__.py deleted file mode 100644 index be835dede..000000000 --- a/src/PluginUtils/__init__.py +++ /dev/null @@ -1,47 +0,0 @@ -# -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2001-2006 Donald N. Allingham -# Copyright (C) 2008 Brian Matherly -# Copyright (C) 2008 Gary Burton -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -# -# $Id$ -# -from _GuiOptions import GuiMenuOptions, make_gui_option -from _Options import Options, OptionListCollection, OptionList, OptionHandler - -from _Tool import gui_tool, cli_tool -import _Tool as Tool -from _PluginDialogs import ReportPluginDialog, ToolPluginDialog -import _PluginWindows as PluginWindows - -# This needs to go above Tool and MenuOption as it needs both -class MenuToolOptions(GuiMenuOptions,Tool.ToolOptions): - """ - The MenuToolOptions class implements the ToolOptions - functionality in a generic way so that the user does not need to - be concerned with the graphical representation of the options. - - The user should inherit the MenuToolOptions class and override the - add_menu_options function. The user can add options to the menu - and the MenuToolOptions class will worry about setting up the GUI. - """ - def __init__(self, name, person_id=None, dbstate=None): - Tool.ToolOptions.__init__(self, name, person_id) - GuiMenuOptions.__init__(self) - - diff --git a/src/ReportBase/Makefile.am b/src/ReportBase/Makefile.am deleted file mode 100644 index 2ac631c47..000000000 --- a/src/ReportBase/Makefile.am +++ /dev/null @@ -1,35 +0,0 @@ -# This is the src/Report level Makefile for Gramps - -pkgdatadir = $(datadir)/@PACKAGE@/ReportBase - -pkgdata_PYTHON = \ - __init__.py\ - _Bibliography.py\ - _CommandLineReport.py\ - _Constants.py\ - _DocReportDialog.py\ - _DrawReportDialog.py\ - _Endnotes.py\ - _FileEntry.py\ - _GraphvizReportDialog.py\ - _PaperMenu.py\ - _Report.py\ - _ReportDialog.py\ - _ReportOptions.py\ - _ReportUtils.py\ - _StyleComboBox.py\ - _StyleEditor.py\ - _TextReportDialog.py\ - _WebReportDialog.py - -pkgpyexecdir = @pkgpyexecdir@/ReportBase -pkgpythondir = @pkgpythondir@/ReportBase - -# 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/ReportBase/_Bibliography.py b/src/ReportBase/_Bibliography.py deleted file mode 100644 index 32f7fe4e5..000000000 --- a/src/ReportBase/_Bibliography.py +++ /dev/null @@ -1,242 +0,0 @@ -# -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2007 Brian G. Matherly -# -# 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$ - -""" -Contain and organize bibliographic information. -""" -import string -import math -from gen.lib import SourceRef - -class Citation(object): - """ - Store information about a citation and all of its references. - """ - def __init__(self): - """ - Initialize members. - """ - self.__src_handle = None - self.__ref_list = [] - - def get_source_handle(self): - """ - Provide the handle to the source that this citation is for. - - @return: Source Handle - @rtype: handle - """ - return self.__src_handle - - def set_source_handle(self, handle): - """ - Set the handle for the source that this citation is for. - - @param handle: Source Handle - @type handle: handle - """ - self.__src_handle = handle - - def get_ref_list(self): - """ - List all the references to this citation. - - @return: a list of references - @rtype: list of L{gen.lib.srcref} objects - """ - return self.__ref_list - - def add_reference(self, source_ref): - """ - Add a reference to this citation. If a similar reference exists, don't - add another one. - - @param source_ref: Source Reference - @type source_ref: L{gen.lib.srcref} - @return: The key of the added reference among all the references. - @rtype: char - """ - letter_count = len(string.ascii_lowercase) - ref_count = len(self.__ref_list) - x_ref_count = ref_count - # Return "a" for ref_count = 0, otherwise log(0) does not work - if ref_count == 0: - self.__ref_list.append(("a", source_ref)) - return "a" - last_letter = string.ascii_lowercase[ ref_count % letter_count ] - key = "" - # Calculate prek number of digits. - number_of_letters = int(math.log(float(ref_count), float(letter_count)))+1 - # Exclude index for number_of_letters-1 - for n in range(1, number_of_letters-1): - ref_count -= pow(letter_count, n) - # Adjust number_of_letters for new index - number_of_letters = int(math.log(float(ref_count), float(letter_count))) +1 - for n in range(1, number_of_letters): - x_ref_count -= pow(letter_count, n) - for letter in range(1, number_of_letters): - index = x_ref_count / pow(letter_count, letter) % letter_count - key += string.ascii_lowercase[ index ] - key = key + last_letter - self.__ref_list.append((key, source_ref)) - return key - -class Bibliography(object): - """ - Store and organize multiple citations into a bibliography. - """ - MODE_DATE = 2**0 - MODE_PAGE = 2**1 - MODE_CONF = 2**2 - MODE_NOTE = 2**3 - MODE_ALL = MODE_DATE | MODE_PAGE | MODE_CONF | MODE_NOTE - - def __init__(self, mode=MODE_ALL): - """ - A bibliography will store citations (sources) and references to those - citations (source refs). Duplicate entries will not be added. To change - what is considered duplicate, you can tell the bibliography what source - ref information you are interested in by passing in the mode. - - Possible modes include: - MODE_DATE - MODE_PAGE - MODE_CONF - MODE_NOTE - MODE_ALL - - If you only care about pages, set "mode=MODE_PAGE". - If you only care about dates and pages, set "mode=MODE_DATE|MODE_PAGE". - If you care about everything, set "mode=MODE_ALL". - """ - self.__citation_list = [] - self.mode = mode - - def add_reference(self, source_ref): - """ - Add a reference to a source to this bibliography. If the source already - exists, don't add it again. If a similar reference exists, don't - add another one. - - @param source_ref: Source Reference - @type source_ref: L{gen.lib.srcref} - @return: A tuple containing the index of the source among all the - sources and the key of the reference among all the references. If - there is no reference information, the second element will be None. - @rtype: (int,char) or (int,None) - """ - source_handle = source_ref.get_reference_handle() - cindex = 0 - rkey = "" - citation = None - citation_found = False - for citation in self.__citation_list: - if citation.get_source_handle() == source_handle: - citation_found = True - break - cindex += 1 - - if not citation_found: - citation = Citation() - citation.set_source_handle(source_handle) - cindex = len(self.__citation_list) - self.__citation_list.append(citation) - - if self.__sref_has_info(source_ref): - for key, ref in citation.get_ref_list(): - if self.__srefs_are_equal(ref, source_ref): - # if a reference like this already exists, don't add - # another one - return (cindex, key) - rkey = citation.add_reference(source_ref) - - return (cindex, rkey) - - def get_citation_count(self): - """ - Report the number of citations in this bibliography. - - @return: number of citations - @rtype: int - """ - return len(self.__citation_list) - - def get_citation_list(self): - """ - Return a list containing all the citations in this bibliography. - - @return: citation list - @rtype: list of L{Citation} objects - """ - return self.__citation_list - - def __sref_has_info(self, source_ref): - """ - Determine if this source_ref has any useful information based on the - current mode. - """ - if ( self.mode & self.MODE_PAGE ) == self.MODE_PAGE: - if source_ref.get_page() != "": - return True - if ( self.mode & self.MODE_DATE ) == self.MODE_DATE: - date = source_ref.get_date_object() - if date is not None and not date.is_empty(): - return True - if ( self.mode & self.MODE_CONF ) == self.MODE_CONF: - confidence = source_ref.get_confidence_level() - if confidence is not None and confidence != SourceRef.CONF_NORMAL: - return True - if ( self.mode & self.MODE_NOTE ) == self.MODE_NOTE: - if len(source_ref.get_note_list()) != 0: - return True - # Can't find anything interesting. - return False - - def __srefs_are_equal(self, source_ref1, source_ref2): - """ - Determine if two source references are equal based on the - current mode. - """ - if self.mode == self.MODE_ALL: - return source_ref1.is_equal(source_ref2) - if ( self.mode & self.MODE_PAGE ) == self.MODE_PAGE: - if source_ref1.get_page() != source_ref2.get_page(): - return False - if ( self.mode & self.MODE_DATE ) == self.MODE_DATE: - date1 = source_ref1.get_date_object() - date2 = source_ref2.get_date_object() - if date1.is_equal(date2): - return False - if ( self.mode & self.MODE_CONF ) == self.MODE_CONF: - conf1 = source_ref1.get_confidence_level() - conf2 = source_ref2.get_confidence_level() - if conf1 != conf2: - return False - if ( self.mode & self.MODE_NOTE ) == self.MODE_NOTE: - nl1 = source_ref1.get_note_list() - nl2 = source_ref2.get_note_list() - if len(nl1) != len(nl2): - return False - for notehandle in nl1: - if notehandle not in nl2: - return False - # Can't find anything different. They must be equal. - return True diff --git a/src/ReportBase/_CommandLineReport.py b/src/ReportBase/_CommandLineReport.py deleted file mode 100644 index afe7f8b84..000000000 --- a/src/ReportBase/_CommandLineReport.py +++ /dev/null @@ -1,486 +0,0 @@ -# -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2001-2007 Donald N. Allingham -# Copyright (C) 2008 Lukasz Rymarczyk -# Copyright (C) 2008 Raphael Ackermann -# Copyright (C) 2008 Brian G. Matherly -# -# 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$ - - -#------------------------------------------------------------------------- -# -# Python modules -# -#------------------------------------------------------------------------- -from gen.ggettext import gettext as _ -import traceback -import os -import sys - -import logging -log = logging.getLogger(".") - -#------------------------------------------------------------------------- -# -# Gramps modules -# -#------------------------------------------------------------------------- -import Utils -from gen.plug import BasePluginManager -from gen.plug.docgen import (StyleSheet, StyleSheetList, PaperStyle, - PAPER_PORTRAIT, PAPER_LANDSCAPE) -from gen.plug.menu import (FamilyOption, PersonOption, NoteOption, - MediaOption, PersonListOption, NumberOption, - BooleanOption, DestinationOption, StringOption, - TextOption, EnumeratedListOption) -from gen.display.name import displayer as name_displayer -from Errors import ReportError -from ReportBase import (CATEGORY_TEXT, CATEGORY_DRAW, CATEGORY_BOOK, - CATEGORY_GRAPHVIZ, CATEGORY_CODE) -from _PaperMenu import paper_sizes -import const -import DbState -from cli.grampscli import CLIManager - -#------------------------------------------------------------------------ -# -# Private Functions -# -#------------------------------------------------------------------------ -def _validate_options(options, dbase): - """ - Validate all options by making sure that their values are consistent with - the database. - - menu: The Menu class - dbase: the database the options will be applied to - """ - if not hasattr(options, "menu"): - print 'no menu' - return - menu = options.menu - - for name in menu.get_all_option_names(): - option = menu.get_option_by_name(name) - - if isinstance(option, PersonOption): - pid = option.get_value() - person = dbase.get_person_from_gramps_id(pid) - if not person: - person = dbase.get_default_person() - if not person: - phandle = dbase.iter_person_handles().next() - person = dbase.get_person_from_handle(phandle) - if not person: - print "ERROR: Please specify a person" - if person: - option.set_value(person.get_gramps_id()) - - elif isinstance(option, FamilyOption): - fid = option.get_value() - family = dbase.get_family_from_gramps_id(fid) - if not family: - person = dbase.get_default_person() - family_list = [] - family_handle = None - if person: - family_list = person.get_family_handle_list() - if family_list: - family_handle = family_list[0] - else: - family_handle = dbase.iter_family_handles().next() - if family_handle: - family = dbase.get_family_from_handle(family_handle) - option.set_value(family.get_gramps_id()) - else: - print "ERROR: Please specify a family" - -#------------------------------------------------------------------------ -# -# Command-line report -# -#------------------------------------------------------------------------ -class CommandLineReport(object): - """ - Provide a way to generate report from the command line. - """ - - def __init__(self, database, name, category, option_class, options_str_dict, - noopt=False): - - pmgr = BasePluginManager.get_instance() - self.__textdoc_plugins = [] - self.__drawdoc_plugins = [] - self.__bookdoc_plugins = [] - for plugin in pmgr.get_docgen_plugins(): - if plugin.get_text_support() and plugin.get_extension(): - self.__textdoc_plugins.append(plugin) - if plugin.get_draw_support() and plugin.get_extension(): - self.__drawdoc_plugins.append(plugin) - if plugin.get_text_support() and \ - plugin.get_draw_support() and \ - plugin.get_extension(): - self.__bookdoc_plugins.append(plugin) - - self.database = database - self.category = category - self.format = None - self.option_class = option_class(name, database) - self.option_class.load_previous_values() - _validate_options(self.option_class, database) - self.show = options_str_dict.pop('show', None) - self.options_str_dict = options_str_dict - self.init_standard_options(noopt) - self.init_report_options() - self.parse_options() - self.show_options() - - def init_standard_options(self, noopt): - """ - Initialize the options that are hard-coded into the report system. - """ - 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(), - 'css' : self.option_class.handler.get_css_filename(), - } - - 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.", ""], - 'css' : ["=css filename", "CSS filename to use, html format" - " only", ""], - } - - if noopt: - return - - self.options_help['of'][2] = os.path.join(const.USER_HOME, - "whatever_name") - - if self.category == CATEGORY_TEXT: - for plugin in self.__textdoc_plugins: - self.options_help['off'][2].append( - plugin.get_extension() + "\t" + plugin.get_description() ) - elif self.category == CATEGORY_DRAW: - for plugin in self.__drawdoc_plugins: - self.options_help['off'][2].append( - plugin.get_extension() + "\t" + plugin.get_description() ) - elif self.category == CATEGORY_BOOK: - for plugin in self.__bookdoc_plugins: - self.options_help['off'][2].append( - plugin.get_extension() + "\t" + plugin.get_description() ) - else: - self.options_help['off'][2] = "NA" - - self.options_help['papers'][2] = \ - [ paper.get_name() for paper in paper_sizes - if paper.get_name() != _("Custom Size") ] - - self.options_help['papero'][2] = [ - "%d\tPortrait" % PAPER_PORTRAIT, - "%d\tLandscape" % PAPER_LANDSCAPE ] - - self.options_help['css'][2] = os.path.join(const.USER_HOME, - "whatever_name.css") - - if self.category in (CATEGORY_TEXT, CATEGORY_DRAW): - default_style = 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 = StyleSheetList(style_file, default_style) - - self.options_help['style'][2] = self.style_list.get_style_names() - - def init_report_options(self): - """ - Initialize the options that are defined by each report. - """ - if not hasattr(self.option_class, "menu"): - return - menu = self.option_class.menu - for name in menu.get_all_option_names(): - option = menu.get_option_by_name(name) - self.options_dict[name] = option.get_value() - self.options_help[name] = [ "", option.get_help() ] - - if isinstance(option, PersonOption): - 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(), - name_displayer.display(person))) - self.options_help[name].append(id_list) - elif isinstance(option, FamilyOption): - id_list = [] - for family in self.database.iter_families(): - mname = "" - fname = "" - mhandle = family.get_mother_handle() - if mhandle: - mother = self.database.get_person_from_handle(mhandle) - if mother: - mname = name_displayer.display(mother) - fhandle = family.get_father_handle() - if fhandle: - father = self.database.get_person_from_handle(fhandle) - if father: - fname = name_displayer.display(father) - text = "%s:\t%s, %s" % \ - (family.get_gramps_id(), fname, mname) - id_list.append(text) - self.options_help[name].append(id_list) - elif isinstance(option, NoteOption): - id_list = [] - for nhandle in self.database.get_note_handles(): - note = self.database.get_note_from_handle(nhandle) - id_list.append(note.get_gramps_id()) - self.options_help[name].append(id_list) - elif isinstance(option, MediaOption): - id_list = [] - for mhandle in self.database.get_media_object_handles(): - mobject = self.database.get_object_from_handle(mhandle) - id_list.append(mobject.get_gramps_id()) - self.options_help[name].append(id_list) - elif isinstance(option, PersonListOption): - self.options_help[name].append("") - elif isinstance(option, NumberOption): - self.options_help[name].append("A number") - elif isinstance(option, BooleanOption): - self.options_help[name].append(["0\tno", "1\tyes"]) - elif isinstance(option, DestinationOption): - self.options_help[name].append("A file system path") - elif isinstance(option, StringOption): - self.options_help[name].append("Any text") - elif isinstance(option, TextOption): - self.options_help[name].append("Any text") - elif isinstance(option, EnumeratedListOption): - ilist = [] - for (value, description) in option.get_items(): - ilist.append("%s\t%s" % (value, description)) - self.options_help[name].append(ilist) - else: - print "Unknown option: ", option - - def parse_options(self): - """ - Load the options that the user has entered. - """ - if not hasattr(self.option_class, "menu"): - return - menu = self.option_class.menu - menu_opt_names = menu.get_all_option_names() - for opt in self.options_str_dict: - if opt in self.options_dict: - 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] - - if opt in menu_opt_names: - option = menu.get_option_by_name(opt) - option.set_value(self.options_dict[opt]) - - else: - print "Ignoring unknown option: %s" % opt - - self.option_class.handler.output = self.options_dict['of'] - - self.css_filename = None - if self.category == CATEGORY_TEXT: - for plugin in self.__textdoc_plugins: - if plugin.get_extension() == self.options_dict['off']: - self.format = plugin.get_basedoc() - self.css_filename = self.options_dict['css'] - if self.format is None: - # Pick the first one as the default. - self.format = self.__textdoc_plugins[0].get_basedoc() - elif self.category == CATEGORY_DRAW: - for plugin in self.__drawdoc_plugins: - if plugin.get_extension() == self.options_dict['off']: - self.format = plugin.get_basedoc() - if self.format is None: - # Pick the first one as the default. - self.format = self.__drawdoc_plugins[0].get_basedoc() - elif self.category == CATEGORY_BOOK: - for plugin in self.__bookdoc_plugins: - if plugin.get_extension() == self.options_dict['off']: - self.format = plugin.get_basedoc() - if self.format is None: - # Pick the first one as the default. - self.format = self.__bookdoc_plugins[0].get_basedoc() - 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.orien = self.options_dict['papero'] - - if self.category in (CATEGORY_TEXT, CATEGORY_DRAW): - default_style = 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 = 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) - - def show_options(self): - """ - Print available options on the CLI. - """ - if not self.show: - return - elif self.show == 'all': - print " Available options:" - for key in self.options_dict: - if key in self.options_help: - opt = self.options_help[key] - # Make the output nicer to read, assume that tab has 8 spaces - tabs = '\t' if len(key) < 10 else '\t'*2 - print " %s%s%s (%s)" % (key, tabs, opt[1], opt[0]) - else: - print " %s" % key - print " Use 'show=option' to see description and acceptable values" - elif self.show in self.options_help: - opt = self.options_help[self.show] - tabs = '\t' if len(self.show) < 10 else '\t'*2 - print ' %s%s%s%s' % (self.show, tabs, opt[0], opt[1]) - print " Available values are:" - vals = opt[2] - if isinstance(vals, (list, tuple)): - for val in vals: - print " %s" % val - else: - print " %s" % opt[2] - - else: - #there was a show option given, but the option is invalid - print ("option %s not valid. Use 'show=all' to see all valid " - "options." % self.show) - -#------------------------------------------------------------------------ -# -# Command-line report generic task -# -#------------------------------------------------------------------------ -def cl_report(database, name, category, report_class, options_class, - options_str_dict): - - err_msg = _("Failed to write report. ") - clr = CommandLineReport(database, name, category, options_class, - options_str_dict) - - # Exit here if show option was given - if clr.show: - return - - # write report - try: - if category in [CATEGORY_TEXT, CATEGORY_DRAW, CATEGORY_BOOK, \ - CATEGORY_GRAPHVIZ]: - clr.option_class.handler.doc = clr.format( - clr.selected_style, - PaperStyle(clr.paper,clr.orien)) - if clr.css_filename is not None and hasattr(clr.option_class.handler.doc, 'set_css_filename'): - clr.option_class.handler.doc.set_css_filename(clr.css_filename) - MyReport = report_class(database, clr.option_class) - MyReport.doc.init() - MyReport.begin_report() - MyReport.write_report() - MyReport.end_report() - return clr - except ReportError, msg: - (m1, m2) = msg.messages() - print err_msg - print m1 - except: - if len(log.handlers) > 0: - log.error(err_msg, exc_info=True) - else: - print >> sys.stderr, err_msg - ## Something seems to eat the exception above. - ## Hack to re-get the exception: - try: - raise - except: - traceback.print_exc() - -def run_report(db, name, **options_str_dict): - """ - Given a database, run a given report. - - db is a Db database - - name is the name of a report - - options_str_dict is the same kind of options - given at the command line. For example: - - >>> run_report(db, "ancestor_report", off="txt", - of="ancestor-007.txt", pid="I37") - - returns CommandLineReport (clr) if successfully runs the report, - None otherwise. - - You can see: - options and values used in clr.option_class.options_dict - filename in clr.option_class.get_output() - """ - dbstate = DbState.DbState() - climanager = CLIManager(dbstate, False) # don't load db - climanager.do_reg_plugins() - pmgr = BasePluginManager.get_instance() - cl_list = pmgr.get_reg_reports() - clr = None - for pdata in cl_list: - if name == pdata.id: - mod = pmgr.load_plugin(pdata) - if not mod: - #import of plugin failed - return clr - category = pdata.category - report_class = getattr(mod, pdata.reportclass) - options_class = getattr(mod, pdata.optionclass) - if category in (CATEGORY_BOOK, CATEGORY_CODE): - options_class(db, name, category, - options_str_dict) - else: - clr = cl_report(db, name, category, - report_class, options_class, - options_str_dict) - return clr - return clr diff --git a/src/ReportBase/_Constants.py b/src/ReportBase/_Constants.py deleted file mode 100644 index bd0842517..000000000 --- a/src/ReportBase/_Constants.py +++ /dev/null @@ -1,75 +0,0 @@ -# -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2001-2006 Donald N. Allingham -# Copyright (C) 2010 Rob G. Healey -# -# 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" - -#------------------------------------------------------------------------- -# -# standard python modules -# -#------------------------------------------------------------------------- -from gen.ggettext import gettext as _ - -#------------------------------------------------------------------------- -# -# Constants -# -#------------------------------------------------------------------------- - -# Report categories -from gen.plug import CATEGORY_TEXT, CATEGORY_DRAW, CATEGORY_CODE, CATEGORY_WEB,\ - CATEGORY_BOOK, CATEGORY_GRAPHVIZ - -standalone_categories = { - CATEGORY_TEXT : _("Text Reports"), - CATEGORY_DRAW : _("Graphical Reports"), - CATEGORY_CODE : _("Code Generators"), - CATEGORY_WEB : _("Web Pages"), - CATEGORY_BOOK : _("Books"), - CATEGORY_GRAPHVIZ : _("Graphs"), -} - -book_categories = { - CATEGORY_TEXT : _("Text"), - CATEGORY_DRAW : _("Graphics"), -} - -#Common data for html reports -## TODO: move to a system where css files are registered -# This information defines the list of styles in the Web reports -# options dialog as well as the location of the corresponding -# stylesheets in src/data. - -CSS_FILES = [ - # First is used as default selection. - [_("Basic-Ash"), 'Web_Basic-Ash.css'], - [_("Basic-Blue"), 'Web_Basic-Blue.css'], - [_("Basic-Cypress"), 'Web_Basic-Cypress.css'], - [_("Basic-Lilac"), 'Web_Basic-Lilac.css'], - [_("Basic-Peach"), 'Web_Basic-Peach.css'], - [_("Basic-Spruce"), 'Web_Basic-Spruce.css'], - [_("Mainz"), 'Web_Mainz.css'], - [_("Nebraska"), 'Web_Nebraska.css'], - [_("Visually Impaired"), 'Web_Visually.css'], - [_("No style sheet"), ''], - ] diff --git a/src/ReportBase/_DocReportDialog.py b/src/ReportBase/_DocReportDialog.py deleted file mode 100644 index e2fb18e82..000000000 --- a/src/ReportBase/_DocReportDialog.py +++ /dev/null @@ -1,261 +0,0 @@ -# -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2007 Brian G. Matherly -# -# 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$ - -#------------------------------------------------------------------------- -# -# Python modules -# -#------------------------------------------------------------------------- -import os -from gen.ggettext import gettext as _ - -#------------------------------------------------------------------------- -# -# GTK+ modules -# -#------------------------------------------------------------------------- -import gtk - -#------------------------------------------------------------------------- -# -# GRAMPS modules -# -#------------------------------------------------------------------------- -import const -from _ReportDialog import ReportDialog -from _Constants import CSS_FILES -from _PaperMenu import PaperFrame - -#------------------------------------------------------------------------- -# -# ReportDialog class -# -#------------------------------------------------------------------------- -class DocReportDialog(ReportDialog): - """ - The DocReportDialog base class. This is a base class for generating - dialogs for docgen derived reports. - """ - - def __init__(self, dbstate, uistate, option_class, name, trans_name): - """Initialize a dialog to request that the user select options - for a basic *stand-alone* report.""" - - self.style_name = "default" - self.firstpage_added = False - ReportDialog.__init__(self, dbstate, uistate, option_class, - name, trans_name) - - # Allow for post processing of the format frame, since the - # show_all task calls events that may reset values - - def init_interface(self): - ReportDialog.init_interface(self) - self.doc_type_changed(self.format_menu) - - #------------------------------------------------------------------------ - # - # 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.""" - raise NotImplementedError - - def make_document(self): - """Create a document of the type requested by the user. - """ - pstyle = self.paper_frame.get_paper_style() - - self.doc = self.format(self.selected_style, pstyle) - if not self.format_menu.get_active_plugin().get_paper_used(): - #set css filename - self.doc.set_css_filename(const.DATA_DIR + os.sep + - self.css_filename) - - self.options.set_document(self.doc) - - if self.open_with_app.get_active(): - self.doc.open_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 css - file. Those chances are made here.""" - docgen_plugin = obj.get_active_plugin() - if docgen_plugin.get_extension(): - self.open_with_app.set_sensitive (True) - else: - self.open_with_app.set_sensitive (False) - - # Is this to be a printed report or an electronic report - # (i.e. a set of web pages) - - if self.firstpage_added: - self.notebook.remove_page(0) - if docgen_plugin.get_paper_used(): - self.paper_label = gtk.Label('%s'%_("Paper Options")) - self.paper_label.set_use_markup(True) - self.notebook.insert_page(self.paper_frame, self.paper_label, 0) - self.paper_frame.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() - self.firstpage_added = True - - ext_val = docgen_plugin.get_extension() - if ext_val: - fname = self.target_fileentry.get_full_path(0) - (spath, ext) = os.path.splitext(fname) - - fname = spath + "." + ext_val - self.target_fileentry.set_filename(fname) - self.target_fileentry.set_sensitive(True) - else: - self.target_fileentry.set_filename("") - self.target_fileentry.set_sensitive(False) - - # Does this report format use styles? - if self.style_button: - self.style_button.set_sensitive(docgen_plugin.get_style_support()) - self.style_menu.set_sensitive(docgen_plugin.get_style_support()) - - 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.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.row, self.row+1, gtk.SHRINK|gtk.FILL) - self.tbl.attach(self.format_menu, 2, 4, self.row, self.row+1, - yoptions=gtk.SHRINK) - self.row += 1 - - self.open_with_app = gtk.CheckButton(_("Open with default viewer")) - self.tbl.attach(self.open_with_app, 2, 4, self.row, self.row+1, - yoptions=gtk.SHRINK) - self.row += 1 - - ext = self.format_menu.get_active_plugin().get_extension() - if ext is None: - ext = "" - else: - spath = self.get_default_directory() - base = "%s.%s" % (self.raw_name, ext) - spath = os.path.normpath(os.path.join(spath, base)) - self.target_fileentry.set_filename(spath) - - def setup_report_options_frame(self): - self.paper_frame = PaperFrame(self.options.handler.get_paper_metric(), - self.options.handler.get_paper_name(), - self.options.handler.get_orientation(), - self.options.handler.get_margins(), - self.options.handler.get_custom_paper_size() - ) - self.setup_html_frame() - ReportDialog.setup_report_options_frame(self) - - 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:" % _("CSS file")) - label.set_alignment(0.0,0.5) - self.html_table.attach(label, 1, 2, 1, 2, gtk.SHRINK|gtk.FILL, - yoptions=gtk.SHRINK) - - self.css_combo = gtk.combo_box_new_text() - - css_filename = self.options.handler.get_css_filename() - active_index = 0 - index = 0 - for style in CSS_FILES: - self.css_combo.append_text(style[0]) - if css_filename == style[1]: - active_index = index - index += 1 - - self.html_table.attach(self.css_combo,2,3,1,2, yoptions=gtk.SHRINK) - self.css_combo.set_active(active_index) - - def parse_format_frame(self): - """Parse the format frame of the dialog. Save the user - selected output format for later use.""" - docgen_plugin = self.format_menu.get_active_plugin() - self.format = docgen_plugin.get_basedoc() - format_name = docgen_plugin.get_extension() - self.options.handler.set_format_name(format_name) - - 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.""" - - self.css_filename = CSS_FILES[self.css_combo.get_active()][1] - self.options.handler.set_css_filename(self.css_filename) - - 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_html_frame() - - self.options.handler.set_paper_metric(self.paper_frame.get_paper_metric()) - self.options.handler.set_paper_name(self.paper_frame.get_paper_name()) - self.options.handler.set_orientation(self.paper_frame.get_orientation()) - self.options.handler.set_margins(self.paper_frame.get_paper_margins()) - self.options.handler.set_custom_paper_size(self.paper_frame.get_custom_paper_size()) - - self.parse_user_options() - - # Create the output document. - self.make_document() - - # Save options - self.options.handler.save_options() diff --git a/src/ReportBase/_DrawReportDialog.py b/src/ReportBase/_DrawReportDialog.py deleted file mode 100644 index 7e85a1a81..000000000 --- a/src/ReportBase/_DrawReportDialog.py +++ /dev/null @@ -1,107 +0,0 @@ -# -# 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$ - -#------------------------------------------------------------------------- -# -# GTK modules -# -#------------------------------------------------------------------------- -import gtk -import gobject - -#------------------------------------------------------------------------- -# -# GRAMPS modules -# -#------------------------------------------------------------------------- -from _Constants import CATEGORY_DRAW -from _DocReportDialog import DocReportDialog -from gui.pluginmanager import GuiPluginManager - -#------------------------------------------------------------------------- -# -# _DrawFormatComboBox -# -#------------------------------------------------------------------------- -class _DrawFormatComboBox(gtk.ComboBox): - """ - This class is a combo box that allows the selection of a docgen plugin - from all drawdoc plugins. - """ - def __init__(self, active): - - gtk.ComboBox.__init__(self) - - pmgr = GuiPluginManager.get_instance() - self.__drawdoc_plugins = [] - for plugin in pmgr.get_docgen_plugins(): - if plugin.get_draw_support(): - self.__drawdoc_plugins.append(plugin) - - self.store = gtk.ListStore(gobject.TYPE_STRING) - self.set_model(self.store) - cell = gtk.CellRendererText() - self.pack_start(cell, True) - self.add_attribute(cell, 'text', 0) - - index = 0 - active_index = 0 - for plugin in self.__drawdoc_plugins: - name = plugin.get_name() - self.store.append(row=[name]) - if plugin.get_extension() == active: - active_index = index - index += 1 - self.set_active(active_index) - - def get_active_plugin(self): - """ - Get the plugin represented by the currently active selection. - """ - return self.__drawdoc_plugins[self.get_active()] - -#----------------------------------------------------------------------- -# -# DrawReportDialog -# -#----------------------------------------------------------------------- -class DrawReportDialog(DocReportDialog): - """ - A class of ReportDialog customized for drawing based reports. - """ - def __init__(self, dbstate, uistate, 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.format_menu = None - self.category = CATEGORY_DRAW - DocReportDialog.__init__(self, dbstate, uistate, opt, - name, translated_name) - - def make_doc_menu(self,active=None): - """ - Build a menu of document types that are appropriate for - this drawing report. - """ - self.format_menu = _DrawFormatComboBox( active ) diff --git a/src/ReportBase/_Endnotes.py b/src/ReportBase/_Endnotes.py deleted file mode 100644 index fa8084c09..000000000 --- a/src/ReportBase/_Endnotes.py +++ /dev/null @@ -1,179 +0,0 @@ -## -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2007 Brian G. Matherly -# Copyright (C) 2010 Peter Landgren -# -# 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$ - -""" -Provide utilities for printing endnotes in text reports. -""" -from gen.plug.docgen import FontStyle, ParagraphStyle, FONT_SANS_SERIF -from gen.lib import NoteType -from gen.ggettext import gettext as _ - -def add_endnote_styles(style_sheet): - """ - Add paragraph styles to a style sheet to be used for displaying endnotes. - - @param style_sheet: Style sheet - @type style_sheet: L{docgen.StyleSheet} - """ - font = FontStyle() - font.set(face=FONT_SANS_SERIF, size=14, italic=1) - para = ParagraphStyle() - para.set_font(font) - para.set_header_level(2) - para.set_top_margin(0.25) - para.set_bottom_margin(0.25) - para.set_description(_('The style used for the generation header.')) - style_sheet.add_paragraph_style("Endnotes-Header", para) - - para = ParagraphStyle() - para.set(first_indent=-0.75, lmargin=.75) - para.set_top_margin(0.25) - para.set_bottom_margin(0.25) - para.set_description(_('The basic style used for the endnotes source display.')) - style_sheet.add_paragraph_style("Endnotes-Source", para) - - para = ParagraphStyle() - para.set(first_indent=-0.9, lmargin=1.9) -# para.set(lmargin=1.5) - para.set_top_margin(0.25) - para.set_bottom_margin(0.25) - para.set_description(_('The basic style used for the endnotes reference display.')) - style_sheet.add_paragraph_style("Endnotes-Ref", para) - - para = ParagraphStyle() - para.set(lmargin=1.5) - para.set_top_margin(0.25) - para.set_bottom_margin(0.25) - para.set_description(_('The basic style used for the endnotes notes display.')) - style_sheet.add_paragraph_style("Endnotes-Notes", para) - -def cite_source(bibliography, obj): - """ - Cite any sources for the object and add them to the bibliography. - - @param bibliography: The bibliography to contain the citations. - @type bibliography: L{Bibliography} - @param obj: An object with source references. - @type obj: L{gen.lib.srcbase} - """ - txt = "" - slist = obj.get_source_references() - if slist: - first = 1 - for ref in slist: - if not first: - txt += ', ' - first = 0 - (cindex, key) = bibliography.add_reference(ref) - txt += "%d" % (cindex + 1) - if key is not None: - txt += key - return txt - -def write_endnotes(bibliography, database, doc, printnotes=False): - """ - Write all the entries in the bibliography as endnotes. - - @param bibliography: The bibliography that contains the citations. - @type bibliography: L{Bibliography} - @param database: The database that the sources come from. - @type database: DbBase - @param doc: The document to write the endnotes into. - @type doc: L{docgen.TextDoc} - @param printnotes: Indicate if the notes attached to a source must be - written too. - @type printnotes: bool - """ - if bibliography.get_citation_count() == 0: - return - - doc.start_paragraph('Endnotes-Header') - doc.write_text(_('Endnotes')) - doc.end_paragraph() - - cindex = 0 - for citation in bibliography.get_citation_list(): - cindex += 1 - source = database.get_source_from_handle(citation.get_source_handle()) - first = True - - doc.start_paragraph('Endnotes-Source', "%d." % cindex) - - src_txt = _format_source_text(source) - - doc.write_text(src_txt) - doc.end_paragraph() - - ref_list = citation.get_ref_list() - - if ref_list: - first = True - reflines = "" - for key, ref in ref_list: - txt = "%s: %s" % (key, ref.get_page()) - if first: - reflines += txt - first = False - else: - reflines += ('\n%s' % txt) - doc.write_endnotes_ref(reflines,'Endnotes-Ref') - - if printnotes: - note_list = source.get_note_list() - ind = 1 - for notehandle in note_list: - note = database.get_note_from_handle(notehandle) - doc.start_paragraph('Endnotes-Notes') - doc.write_text(_('Note %(ind)d - Type: %(type)s') % { - 'ind': ind, - 'type': str(note.get_type())}) - doc.end_paragraph() - doc.write_styled_note(note.get_styledtext(), - note.get_format(),'Endnotes-Notes', - contains_html= note.get_type() \ - == NoteType.HTML_CODE) - ind += 1 - -def _format_source_text(source): - if not source: return "" - - src_txt = "" - - if source.get_author(): - src_txt += source.get_author() - - if source.get_title(): - if src_txt: - src_txt += ", " - src_txt += '"%s"' % source.get_title() - - if source.get_publication_info(): - if src_txt: - src_txt += ", " - src_txt += source.get_publication_info() - - if source.get_abbreviation(): - if src_txt: - src_txt += ", " - src_txt += "(%s)" % source.get_abbreviation() - - return src_txt diff --git a/src/ReportBase/_FileEntry.py b/src/ReportBase/_FileEntry.py deleted file mode 100644 index 7bbb25a4d..000000000 --- a/src/ReportBase/_FileEntry.py +++ /dev/null @@ -1,101 +0,0 @@ -# -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2001-2006 Donald N. Allingham -# Copyright (C) 2009 Brian G. Matherly -# -# 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:_FileEntry.py 9912 2008-01-22 09:17:46Z acraphae $ - -import os -import gtk -import Utils - -class FileEntry(gtk.HBox): - """ A widget that allows the user to select a file from the file system """ - def __init__(self, defname, title): - gtk.HBox.__init__(self) - - self.title = title - self.dir = False - self.__base_path = "" - self.__file_name = "" - 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() - image = gtk.Image() - image.set_from_stock(gtk.STOCK_OPEN, gtk.ICON_SIZE_BUTTON) - self.button.add(image) - 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): - """ Call back function to handle the open button press """ - if self.dir: - my_action = gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER - else: - my_action = gtk.FILE_CHOOSER_ACTION_SAVE - - dialog = 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): - dialog.set_current_name(name) - elif os.path.isdir(os.path.basename(name)): - dialog.set_current_name(os.path.basename(name)) - else: - dialog.set_current_name(name) - dialog.set_current_folder(self.__base_path) - dialog.present() - status = dialog.run() - if status == gtk.RESPONSE_OK: - self.set_filename(Utils.get_unicode_path(dialog.get_filename())) - dialog.destroy() - - def set_filename(self, path): - """ Set the currently selected dialog. """ - if not path: - return - if os.path.dirname(path): - self.__base_path = os.path.dirname(path) - self.__file_name = os.path.basename(path) - else: - self.__base_path = os.getcwd() - self.__file_name = path - self.entry.set_text(os.path.join(self.__base_path, self.__file_name)) - - def get_full_path(self, val): - """ Get the full path of the currently selected file. """ - return self.entry.get_text() - - def set_directory_entry(self, opt): - """ - Configure the FileEntry to either select a directory or a file. - Set it to True to select a directory. - Set it to False to select a file. - """ - self.dir = opt diff --git a/src/ReportBase/_GraphvizReportDialog.py b/src/ReportBase/_GraphvizReportDialog.py deleted file mode 100644 index e958128c9..000000000 --- a/src/ReportBase/_GraphvizReportDialog.py +++ /dev/null @@ -1,1193 +0,0 @@ -# -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2007-2008 Brian G. Matherly -# Copyright (C) 2007-2009 Stephane Charette -# Copyright (C) 2009 Gary Burton -# Contribution 2009 by Bob Ham -# -# 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$ - -#------------------------------------------------------------------------ -# -# python modules -# -#------------------------------------------------------------------------ -import os -from cStringIO import StringIO -import tempfile -import threading -import time -from types import ClassType, InstanceType -from gen.ggettext import gettext as _ - -#------------------------------------------------------------------------------- -# -# GTK+ modules -# -#------------------------------------------------------------------------------- -import gtk -import gobject - -#------------------------------------------------------------------------------- -# -# GRAMPS modules -# -#------------------------------------------------------------------------------- -import Utils -from gui.utils import ProgressMeter, open_file_with_default_application -from gen.plug.docgen import BaseDoc, GVDoc -import config -from ReportBase import CATEGORY_GRAPHVIZ -from _ReportDialog import ReportDialog -from _PaperMenu import PaperFrame -from gen.plug.menu import NumberOption, TextOption, EnumeratedListOption, \ - BooleanOption -import constfunc - -#------------------------------------------------------------------------------- -# -# Private Constants -# -#------------------------------------------------------------------------------- -_FONTS = [ { 'name' : _("Default"), 'value' : "" }, - { 'name' : _("PostScript / Helvetica"), 'value' : "Helvetica" }, - { 'name' : _("TrueType / FreeSans"), 'value' : "FreeSans" } ] - -_RANKDIR = [ { 'name' : _("Vertical (top to bottom)"), 'value' : "TB" }, - { 'name' : _("Vertical (bottom to top)"), 'value' : "BT" }, - { 'name' : _("Horizontal (left to right)"), 'value' : "LR" }, - { 'name' : _("Horizontal (right to left)"), 'value' : "RL" } ] - -_PAGEDIR = [ { 'name' : _("Bottom, left"), 'value' :"BL" }, - { 'name' : _("Bottom, right"), 'value' :"BR" }, - { 'name' : _("Top, left"), 'value' :"TL" }, - { 'name' : _("Top, Right"), 'value' :"TR" }, - { 'name' : _("Right, bottom"), 'value' :"RB" }, - { 'name' : _("Right, top"), 'value' :"RT" }, - { 'name' : _("Left, bottom"), 'value' :"LB" }, - { 'name' : _("Left, top"), 'value' :"LT" } ] - -_RATIO = [ { 'name' : _("Minimal size"), 'value': "compress" }, - { 'name' : _("Fill the given area"), 'value': "fill" }, - { 'name' : _("Use optimal number of pages"), 'value': "expand" } ] - -_NOTELOC = [ { 'name' : _("Top"), 'value' : "t" }, - { 'name' : _("Bottom"), 'value' : "b" }] - -if constfunc.win(): - _DOT_FOUND = Utils.search_for("dot.exe") - - if Utils.search_for("gswin32c.exe") == 1: - _GS_CMD = "gswin32c.exe" - elif Utils.search_for("gswin32.exe") == 1: - _GS_CMD = "gswin32.exe" - else: - _GS_CMD = "" -else: - _DOT_FOUND = Utils.search_for("dot") - - if Utils.search_for("gs") == 1: - _GS_CMD = "gs" - else: - _GS_CMD = "" - -#------------------------------------------------------------------------------- -# -# Private Functions -# -#------------------------------------------------------------------------------- -def _run_long_process_in_thread(func, header): - """ - This function will spawn a new thread to execute the provided function. - While the function is running, a progress bar will be created. - The progress bar will show activity while the function executes. - - @param func: A function that will take an unknown amount of time to - complete. - @type category: callable - @param header: A header for the progress bar. - Example: "Updating Data" - @type name: string - @return: nothing - - """ - pbar = ProgressMeter(_('Processing File')) - pbar.set_pass(total=40, - mode=ProgressMeter.MODE_ACTIVITY, - header=header) - - sys_thread = threading.Thread(target=func) - sys_thread.start() - - while sys_thread.isAlive(): - # The loop runs 20 times per second until the thread completes. - # With the progress pass total set at 40, it should move across the bar - # every two seconds. - time.sleep(0.05) - pbar.step() - - pbar.close() - -#------------------------------------------------------------------------------- -# -# GVDocBase -# -#------------------------------------------------------------------------------- -class GVDocBase(BaseDoc, GVDoc): - """ - Base document generator for all Graphviz document generators. Classes that - inherit from this class will only need to implement the close function. - The close function will generate the actual file of the appropriate type. - """ - def __init__(self, options, paper_style): - BaseDoc.__init__(self, None, paper_style) - - self._filename = None - self._dot = StringIO() - self._paper = paper_style - - menu = options.menu - - self.dpi = menu.get_option_by_name('dpi').get_value() - self.fontfamily = menu.get_option_by_name('font_family').get_value() - self.fontsize = menu.get_option_by_name('font_size').get_value() - self.hpages = menu.get_option_by_name('h_pages').get_value() - self.nodesep = menu.get_option_by_name('nodesep').get_value() - self.noteloc = menu.get_option_by_name('noteloc').get_value() - self.notesize = menu.get_option_by_name('notesize').get_value() - self.note = menu.get_option_by_name('note').get_value() - self.pagedir = menu.get_option_by_name('page_dir').get_value() - self.rankdir = menu.get_option_by_name('rank_dir').get_value() - self.ranksep = menu.get_option_by_name('ranksep').get_value() - self.ratio = menu.get_option_by_name('ratio').get_value() - self.vpages = menu.get_option_by_name('v_pages').get_value() - self.usesubgraphs = menu.get_option_by_name('usesubgraphs').get_value() - - paper_size = paper_style.get_size() - - # Subtract 0.01" from the drawing area to make some room between - # this area and the margin in order to compensate for different - # rounding errors internally in dot - sizew = ( paper_size.get_width() - - self._paper.get_left_margin() - - self._paper.get_right_margin() ) / 2.54 - 0.01 - sizeh = ( paper_size.get_height() - - self._paper.get_top_margin() - - self._paper.get_bottom_margin() ) / 2.54 - 0.01 - - pheight = paper_size.get_height_inches() - pwidth = paper_size.get_width_inches() - - xmargin = self._paper.get_left_margin() / 2.54 - ymargin = self._paper.get_top_margin() / 2.54 - - sizew *= self.hpages - sizeh *= self.vpages - - self.write( 'digraph GRAMPS_graph\n' ) - self.write( '{\n' ) - self.write( ' bgcolor=white;\n' ) - self.write( ' center="true"; \n' ) - self.write( ' charset="utf8";\n' ) - self.write( ' concentrate="false";\n' ) - self.write( ' dpi="%d";\n' % self.dpi ) - self.write( ' graph [fontsize=%d];\n' % self.fontsize ) - self.write( ' margin="%3.2f,%3.2f"; \n' % (xmargin, ymargin)) - self.write( ' mclimit="99";\n' ) - self.write( ' nodesep="%.2f";\n' % self.nodesep ) - self.write( ' outputorder="edgesfirst";\n' ) - if self.hpages == 1 and self.vpages == 1: - self.write( '#' ) # comment out "page=" if the graph is on 1 page (bug #2121) - self.write( ' page="%3.2f,%3.2f";\n' % (pwidth, pheight) ) - self.write( ' pagedir="%s";\n' % self.pagedir ) - self.write( ' rankdir="%s";\n' % self.rankdir ) - self.write( ' ranksep="%.2f";\n' % self.ranksep ) - self.write( ' ratio="%s";\n' % self.ratio ) - self.write( ' searchsize="100";\n' ) - self.write( ' size="%3.2f,%3.2f"; \n' % (sizew, sizeh) ) - self.write( ' splines="true";\n' ) - self.write( '\n' ) - self.write( ' edge [len=0.5 style=solid fontsize=%d];\n' % self.fontsize ) - if self.fontfamily: - self.write( ' node [style=filled fontname="%s" fontsize=%d];\n' - % ( self.fontfamily, self.fontsize ) ) - else: - self.write( ' node [style=filled fontsize=%d];\n' - % self.fontsize ) - self.write( '\n' ) - - def write(self, text): - """ Write text to the dot file """ - self._dot.write(text.encode('utf8', 'xmlcharrefreplace')) - - def open(self, filename): - """ Implement BaseDoc.open() """ - self._filename = os.path.normpath(os.path.abspath(filename)) - - def close(self): - """ - This isn't useful by itself. Other classes need to override this and - actually generate a file. - """ - if self.note: - # build up the label - label = u'' - for line in self.note: # for every line in the note... - line = line.strip() # ...strip whitespace from this line... - if line != '': # ...and if we still have a line... - if label != '': # ...see if we need to insert a newline... - label += '\\n' - label += line.replace('"', '\\\"') - - # after all that, see if we have a label to display - if label != '': - self.write( '\n') - self.write( ' label="%s";\n' % label ) - self.write( ' labelloc="%s";\n' % self.noteloc ) - self.write( ' fontsize="%d";\n' % self.notesize ) - - self.write( '}\n\n' ) - - def add_node(self, node_id, label, shape="", color="", - style="", fillcolor="", url="", htmloutput=False): - """ - Add a node to this graph. Nodes can be different shapes like boxes and - circles. - - Implements GVDoc.add_node(). - """ - text = '[' - - if shape: - text += ' shape="%s"' % shape - - if color: - text += ' color="%s"' % color - - if fillcolor: - text += ' fillcolor="%s"' % fillcolor - - if style: - text += ' style="%s"' % style - - # note that we always output a label -- even if an empty string -- - # otherwise GraphViz uses the node ID as the label which is unlikely - # to be what the user wants to see in the graph - if label.startswith("<") or htmloutput: - text += ' label=<%s>' % label - else: - text += ' label="%s"' % label - - if url: - text += ' URL="%s"' % url - - text += " ]" - self.write(' %s %s;\n' % (node_id, text)) - - def add_link(self, id1, id2, style="", head="", tail="", comment=""): - """ - Add a link between two nodes. - - Implements GVDoc.add_link(). - """ - self.write(' %s -> %s' % (id1, id2)) - - if style or head or tail: - self.write(' [') - - if style: - self.write(' style=%s' % style) - if head: - self.write(' arrowhead=%s' % head) - if tail: - self.write(' arrowtail=%s' % tail) - if head: - if tail: - self.write(' dir=both') - else: - self.write(' dir=forward') - else: - if tail: - self.write(' dir=back') - else: - self.write(' dir=none') - self.write(' ]') - - self.write(';') - - if comment: - self.write(' // %s' % comment) - - self.write('\n') - - def add_comment(self, comment): - """ - Add a comment. - - Implements GVDoc.add_comment(). - """ - tmp = comment.split('\n') - for line in tmp: - text = line.strip() - if text == "": - self.write('\n') - elif text.startswith('#'): - self.write('%s\n' % text) - else: - self.write('# %s\n' % text) - - def start_subgraph(self, graph_id): - """ Implement GVDoc.start_subgraph() """ - self.write(' subgraph cluster_%s\n' % graph_id) - self.write(' {\n') - self.write(' style="invis";\n') # no border around subgraph (#0002176) - - def end_subgraph(self): - """ Implement GVDoc.end_subgraph() """ - self.write(' }\n') - -#------------------------------------------------------------------------------- -# -# GVDotDoc -# -#------------------------------------------------------------------------------- -class GVDotDoc(GVDocBase): - """ GVDoc implementation that generates a .gv text file. """ - - def close(self): - """ Implements GVDocBase.close() """ - GVDocBase.close(self) - - # Make sure the extension is correct - if self._filename[-3:] != ".gv": - self._filename += ".gv" - - _run_long_process_in_thread(self.__generate, self._filename) - - def __generate(self): - """ - This function will generate the actual file. - It is nice to run this function in the background so that the - application does not appear to hang. - """ - dotfile = open(self._filename, "w") - dotfile.write(self._dot.getvalue()) - dotfile.close() - - if self.open_req: - open_file_with_default_application(self._filename) - -#------------------------------------------------------------------------------- -# -# GVPsDoc -# -#------------------------------------------------------------------------------- -class GVPsDoc(GVDocBase): - """ GVDoc implementation that generates a .ps file using Graphviz. """ - - def __init__(self, options, paper_style): - # DPI must always be 72 for PDF. - # GV documentation says dpi is only for image formats. - options.menu.get_option_by_name('dpi').set_value(72) - GVDocBase.__init__(self, options, paper_style) - # GV documentation allow multiple pages only for ps format, - # But it does not work with -Tps:cairo in order to - # show Non Latin-1 letters. Force to only 1 page. - # See bug tracker issue 2815 - options.menu.get_option_by_name('v_pages').set_value(1) - options.menu.get_option_by_name('h_pages').set_value(1) - GVDocBase.__init__(self, options, paper_style) - - def close(self): - """ Implements GVDocBase.close() """ - GVDocBase.close(self) - - # Make sure the extension is correct - if self._filename[-3:] != ".ps": - self._filename += ".ps" - - _run_long_process_in_thread(self.__generate, self._filename) - - def __generate(self): - """ - This function will generate the actual file. - It is nice to run this function in the background so that the - application does not appear to hang. - """ - # Create a temporary dot file - (handle, tmp_dot) = tempfile.mkstemp(".gv" ) - dotfile = os.fdopen(handle,"w") - dotfile.write(self._dot.getvalue()) - dotfile.close() - - # Generate the PS file. - # Reason for using -Tps:cairo. Needed for Non Latin-1 letters - # Some testing with Tps:cairo. Non Latin-1 letters are OK i all cases: - # Output format: ps PDF-GostScript PDF-GraphViz - # Single page OK OK OK - # Multip page 1 page, OK 1 page, - # corrupted set by gramps - # If I take a correct multip page PDF and convert it with pdf2ps I get multip pages, - # but the output is clipped, some margins have disappeared. I used 1 inch margins always. - # See bug tracker issue 2815 - - os.system( 'dot -Tps:cairo -o"%s" "%s"' % (self._filename, tmp_dot) ) - # Delete the temporary dot file - os.remove(tmp_dot) - - if self.open_req: - open_file_with_default_application(self._filename) - -#------------------------------------------------------------------------------- -# -# GVSvgDoc -# -#------------------------------------------------------------------------------- -class GVSvgDoc(GVDocBase): - """ GVDoc implementation that generates a .svg file using Graphviz. """ - - def __init__(self, options, paper_style): - # GV documentation allow multiple pages only for ps format, - # which also includes pdf via ghostscript. - options.menu.get_option_by_name('v_pages').set_value(1) - options.menu.get_option_by_name('h_pages').set_value(1) - GVDocBase.__init__(self, options, paper_style) - - def close(self): - """ Implements GVDocBase.close() """ - GVDocBase.close(self) - - # Make sure the extension is correct - if self._filename[-4:] != ".svg": - self._filename += ".svg" - - _run_long_process_in_thread(self.__generate, self._filename) - - def __generate(self): - """ - This function will generate the actual file. - It is nice to run this function in the background so that the - application does not appear to hang. - """ - # Create a temporary dot file - (handle, tmp_dot) = tempfile.mkstemp(".gv" ) - dotfile = os.fdopen(handle,"w") - dotfile.write(self._dot.getvalue()) - dotfile.close() - - # Generate the SVG file. - os.system( 'dot -Tsvg -o"%s" "%s"' % (self._filename, tmp_dot) ) - - # Delete the temporary dot file - os.remove(tmp_dot) - - if self.open_req: - open_file_with_default_application(self._filename) - -#------------------------------------------------------------------------------- -# -# GVSvgzDoc -# -#------------------------------------------------------------------------------- -class GVSvgzDoc(GVDocBase): - """ GVDoc implementation that generates a .svg file using Graphviz. """ - - def __init__(self, options, paper_style): - # GV documentation allow multiple pages only for ps format, - # which also includes pdf via ghostscript. - options.menu.get_option_by_name('v_pages').set_value(1) - options.menu.get_option_by_name('h_pages').set_value(1) - GVDocBase.__init__(self, options, paper_style) - - def close(self): - """ Implements GVDocBase.close() """ - GVDocBase.close(self) - - # Make sure the extension is correct - if self._filename[-5:] != ".svgz": - self._filename += ".svgz" - - _run_long_process_in_thread(self.__generate, self._filename) - - def __generate(self): - """ - This function will generate the actual file. - It is nice to run this function in the background so that the - application does not appear to hang. - """ - # Create a temporary dot file - (handle, tmp_dot) = tempfile.mkstemp(".gv" ) - dotfile = os.fdopen(handle,"w") - dotfile.write(self._dot.getvalue()) - dotfile.close() - - # Generate the SVGZ file. - os.system( 'dot -Tsvgz -o"%s" "%s"' % (self._filename, tmp_dot) ) - - # Delete the temporary dot file - os.remove(tmp_dot) - - if self.open_req: - open_file_with_default_application(self._filename) - -#------------------------------------------------------------------------------- -# -# GVPngDoc -# -#------------------------------------------------------------------------------- -class GVPngDoc(GVDocBase): - """ GVDoc implementation that generates a .png file using Graphviz. """ - - def __init__(self, options, paper_style): - # GV documentation allow multiple pages only for ps format, - # which also includes pdf via ghostscript. - options.menu.get_option_by_name('v_pages').set_value(1) - options.menu.get_option_by_name('h_pages').set_value(1) - GVDocBase.__init__(self, options, paper_style) - - def close(self): - """ Implements GVDocBase.close() """ - GVDocBase.close(self) - - # Make sure the extension is correct - if self._filename[-4:] != ".png": - self._filename += ".png" - - _run_long_process_in_thread(self.__generate, self._filename) - - def __generate(self): - """ - This function will generate the actual file. - It is nice to run this function in the background so that the - application does not appear to hang. - """ - # Create a temporary dot file - (handle, tmp_dot) = tempfile.mkstemp(".gv" ) - dotfile = os.fdopen(handle,"w") - dotfile.write(self._dot.getvalue()) - dotfile.close() - - # Generate the PNG file. - os.system( 'dot -Tpng -o"%s" "%s"' % (self._filename, tmp_dot) ) - - # Delete the temporary dot file - os.remove(tmp_dot) - - if self.open_req: - open_file_with_default_application(self._filename) - -#------------------------------------------------------------------------------- -# -# GVJpegDoc -# -#------------------------------------------------------------------------------- -class GVJpegDoc(GVDocBase): - """ GVDoc implementation that generates a .jpg file using Graphviz. """ - - def __init__(self, options, paper_style): - # GV documentation allow multiple pages only for ps format, - # which also includes pdf via ghostscript. - options.menu.get_option_by_name('v_pages').set_value(1) - options.menu.get_option_by_name('h_pages').set_value(1) - GVDocBase.__init__(self, options, paper_style) - - def close(self): - """ Implements GVDocBase.close() """ - GVDocBase.close(self) - - # Make sure the extension is correct - if self._filename[-4:] != ".jpg": - self._filename += ".jpg" - - _run_long_process_in_thread(self.__generate, self._filename) - - def __generate(self): - """ - This function will generate the actual file. - It is nice to run this function in the background so that the - application does not appear to hang. - """ - # Create a temporary dot file - (handle, tmp_dot) = tempfile.mkstemp(".gv" ) - dotfile = os.fdopen(handle,"w") - dotfile.write(self._dot.getvalue()) - dotfile.close() - - # Generate the JPEG file. - os.system( 'dot -Tjpg -o"%s" "%s"' % (self._filename, tmp_dot) ) - - # Delete the temporary dot file - os.remove(tmp_dot) - - if self.open_req: - open_file_with_default_application(self._filename) - -#------------------------------------------------------------------------------- -# -# GVGifDoc -# -#------------------------------------------------------------------------------- -class GVGifDoc(GVDocBase): - """ GVDoc implementation that generates a .gif file using Graphviz. """ - - def __init__(self, options, paper_style): - # GV documentation allow multiple pages only for ps format, - # which also includes pdf via ghostscript. - options.menu.get_option_by_name('v_pages').set_value(1) - options.menu.get_option_by_name('h_pages').set_value(1) - GVDocBase.__init__(self, options, paper_style) - - def close(self): - """ Implements GVDocBase.close() """ - GVDocBase.close(self) - - # Make sure the extension is correct - if self._filename[-4:] != ".gif": - self._filename += ".gif" - - _run_long_process_in_thread(self.__generate, self._filename) - - def __generate(self): - """ - This function will generate the actual file. - It is nice to run this function in the background so that the - application does not appear to hang. - """ - # Create a temporary dot file - (handle, tmp_dot) = tempfile.mkstemp(".gv" ) - dotfile = os.fdopen(handle,"w") - dotfile.write(self._dot.getvalue()) - dotfile.close() - - # Generate the GIF file. - os.system( 'dot -Tgif -o"%s" "%s"' % (self._filename, tmp_dot) ) - - # Delete the temporary dot file - os.remove(tmp_dot) - - if self.open_req: - open_file_with_default_application(self._filename) - -#------------------------------------------------------------------------------- -# -# GVPdfGvDoc -# -#------------------------------------------------------------------------------- -class GVPdfGvDoc(GVDocBase): - """ GVDoc implementation that generates a .pdf file using Graphviz. """ - - def __init__(self, options, paper_style): - # DPI must always be 72 for PDF. - # GV documentation says dpi is only for image formats. - options.menu.get_option_by_name('dpi').set_value(72) - # GV documentation allow multiple pages only for ps format, - # which also includes pdf via ghostscript. - options.menu.get_option_by_name('v_pages').set_value(1) - options.menu.get_option_by_name('h_pages').set_value(1) - GVDocBase.__init__(self, options, paper_style) - - def close(self): - """ Implements GVDocBase.close() """ - GVDocBase.close(self) - - # Make sure the extension is correct - if self._filename[-4:] != ".pdf": - self._filename += ".pdf" - - _run_long_process_in_thread(self.__generate, self._filename) - - def __generate(self): - """ - This function will generate the actual file. - It is nice to run this function in the background so that the - application does not appear to hang. - """ - # Create a temporary dot file - (handle, tmp_dot) = tempfile.mkstemp(".gv" ) - dotfile = os.fdopen(handle,"w") - dotfile.write(self._dot.getvalue()) - dotfile.close() - - # Generate the PDF file. - os.system( 'dot -Tpdf -o"%s" "%s"' % (self._filename, tmp_dot) ) - - # Delete the temporary dot file - os.remove(tmp_dot) - - if self.open_req: - open_file_with_default_application(self._filename) - -#------------------------------------------------------------------------------- -# -# GVPdfGsDoc -# -#------------------------------------------------------------------------------- -class GVPdfGsDoc(GVDocBase): - """ GVDoc implementation that generates a .pdf file using Ghostscript. """ - def __init__(self, options, paper_style): - # DPI must always be 72 for PDF. - # GV documentation says dpi is only for image formats. - options.menu.get_option_by_name('dpi').set_value(72) - GVDocBase.__init__(self, options, paper_style) - - def close(self): - """ Implements GVDocBase.close() """ - GVDocBase.close(self) - - # Make sure the extension is correct - if self._filename[-4:] != ".pdf": - self._filename += ".pdf" - - _run_long_process_in_thread(self.__generate, self._filename) - - def __generate(self): - """ - This function will generate the actual file. - It is nice to run this function in the background so that the - application does not appear to hang. - """ - # Create a temporary dot file - (handle, tmp_dot) = tempfile.mkstemp(".gv" ) - dotfile = os.fdopen(handle,"w") - dotfile.write(self._dot.getvalue()) - dotfile.close() - - # Create a temporary PostScript file - (handle, tmp_ps) = tempfile.mkstemp(".ps" ) - os.close( handle ) - - # Generate PostScript using dot - # Reason for using -Tps:cairo. Needed for Non Latin-1 letters - # See bug tracker issue 2815 - command = 'dot -Tps:cairo -o"%s" "%s"' % ( tmp_ps, tmp_dot ) - os.system(command) - - # Add .5 to remove rounding errors. - paper_size = self._paper.get_size() - width_pt = int( (paper_size.get_width_inches() * 72) + 0.5 ) - height_pt = int( (paper_size.get_height_inches() * 72) + 0.5 ) - - # Convert to PDF using ghostscript - command = '%s -q -sDEVICE=pdfwrite -dNOPAUSE -dDEVICEWIDTHPOINTS=%d' \ - ' -dDEVICEHEIGHTPOINTS=%d -sOutputFile="%s" "%s" -c quit' \ - % ( _GS_CMD, width_pt, height_pt, self._filename, tmp_ps ) - os.system(command) - - os.remove(tmp_ps) - os.remove(tmp_dot) - - if self.open_req: - open_file_with_default_application(self._filename) - -#------------------------------------------------------------------------------- -# -# Various Graphviz formats. -# -#------------------------------------------------------------------------------- -_FORMATS = [] - -if _DOT_FOUND: - - if _GS_CMD != "": - _FORMATS += [{ 'type' : "gspdf", - 'ext' : "pdf", - 'descr': _("PDF (Ghostscript)"), - 'mime' : "application/pdf", - 'class': GVPdfGsDoc }] - - _FORMATS += [{ 'type' : "gvpdf", - 'ext' : "pdf", - 'descr': _("PDF (Graphviz)"), - 'mime' : "application/pdf", - 'class': GVPdfGvDoc }] - - _FORMATS += [{ 'type' : "ps", - 'ext' : "ps", - 'descr': _("PostScript"), - 'mime' : "application/postscript", - 'class': GVPsDoc }] - - _FORMATS += [{ 'type' : "svg", - 'ext' : "svg", - 'descr': _("Structured Vector Graphics (SVG)"), - 'mime' : "image/svg", - 'class': GVSvgDoc }] - - _FORMATS += [{ 'type' : "svgz", - 'ext' : "svgz", - 'descr': _("Compressed Structured Vector Graphs (SVGZ)"), - 'mime' : "image/svgz", - 'class': GVSvgzDoc }] - - _FORMATS += [{ 'type' : "jpg", - 'ext' : "jpg", - 'descr': _("JPEG image"), - 'mime' : "image/jpeg", - 'class': GVJpegDoc }] - - _FORMATS += [{ 'type' : "gif", - 'ext' : "gif", - 'descr': _("GIF image"), - 'mime' : "image/gif", - 'class': GVGifDoc }] - - _FORMATS += [{ 'type' : "png", - 'ext' : "png", - 'descr': _("PNG image"), - 'mime' : "image/png", - 'class': GVPngDoc }] - -_FORMATS += [{ 'type' : "dot", - 'ext' : "gv", - 'descr': _("Graphviz File"), - 'mime' : "text/x-graphviz", - 'class': GVDotDoc }] - -#------------------------------------------------------------------------------- -# -# GraphvizFormatComboBox -# -#------------------------------------------------------------------------------- -class GraphvizFormatComboBox(gtk.ComboBox): - """ - Format combo box class for Graphviz report. - """ - def set(self, active=None): - self.store = gtk.ListStore(gobject.TYPE_STRING) - self.set_model(self.store) - cell = gtk.CellRendererText() - self.pack_start(cell, True) - self.add_attribute(cell, 'text', 0) - - index = 0 - active_index = 0 - for item in _FORMATS: - name = item["descr"] - self.store.append(row=[name]) - if item['type'] == active: - active_index = index - index += 1 - self.set_active(active_index) - - def get_label(self): - return _FORMATS[self.get_active()]["descr"] - - def get_reference(self): - return _FORMATS[self.get_active()]["class"] - - def get_paper(self): - return 1 - - def get_styles(self): - return 0 - - def get_ext(self): - return '.%s' % _FORMATS[self.get_active()]['ext'] - - def get_format_str(self): - return _FORMATS[self.get_active()]["type"] - - def is_file_output(self): - return True - - def get_clname(self): - return _FORMATS[self.get_active()]["type"] - -#----------------------------------------------------------------------- -# -# GraphvizReportDialog -# -#----------------------------------------------------------------------- -class GraphvizReportDialog(ReportDialog): - """A class of ReportDialog customized for graphviz based reports.""" - def __init__(self, dbstate, uistate, opt, name, translated_name): - """Initialize a dialog to request that the user select options - for a graphviz report. See the ReportDialog class for - more information.""" - self.category = CATEGORY_GRAPHVIZ - ReportDialog.__init__(self, dbstate, uistate, opt, - name, translated_name) - - def init_options(self, option_class): - try: - if (issubclass(option_class, object) or # New-style class - isinstance(options_class, ClassType)): # Old-style class - self.options = option_class(self.raw_name, - self.dbstate.get_database()) - except TypeError: - self.options = option_class - - ################################ - category = _("GraphViz Layout") - ################################ - font_family = EnumeratedListOption(_("Font family"), "") - index = 0 - for item in _FONTS: - font_family.add_item(item["value"], item["name"]) - index += 1 - font_family.set_help(_("Choose the font family. If international " - "characters don't show, use FreeSans font. " - "FreeSans is available from: " - "http://www.nongnu.org/freefont/")) - self.options.add_menu_option(category, "font_family", font_family) - - font_size = NumberOption(_("Font size"), 14, 8, 128) - font_size.set_help(_("The font size, in points.")) - self.options.add_menu_option(category, "font_size", font_size) - - rank_dir = EnumeratedListOption(_("Graph Direction"), "TB") - index = 0 - for item in _RANKDIR: - rank_dir.add_item(item["value"], item["name"]) - index += 1 - rank_dir.set_help(_("Whether graph goes from top to bottom " - "or left to right.")) - self.options.add_menu_option(category, "rank_dir", rank_dir) - - h_pages = NumberOption(_("Number of Horizontal Pages"), 1, 1, 25) - h_pages.set_help(_("GraphViz can create very large graphs by " - "spreading the graph across a rectangular " - "array of pages. This controls the number " - "pages in the array horizontally. " - "Only valid for dot and pdf via Ghostscript.")) - self.options.add_menu_option(category, "h_pages", h_pages) - - v_pages = NumberOption(_("Number of Vertical Pages"), 1, 1, 25) - v_pages.set_help(_("GraphViz can create very large graphs by " - "spreading the graph across a rectangular " - "array of pages. This controls the number " - "pages in the array vertically. " - "Only valid for dot and pdf via Ghostscript.")) - self.options.add_menu_option(category, "v_pages", v_pages) - - page_dir = EnumeratedListOption(_("Paging Direction"), "BL") - index = 0 - for item in _PAGEDIR: - page_dir.add_item(item["value"], item["name"]) - index += 1 - page_dir.set_help(_("The order in which the graph pages are output. " - "This option only applies if the horizontal pages " - "or vertical pages are greater than 1.")) - self.options.add_menu_option(category, "page_dir", page_dir) - - # the page direction option only makes sense when the - # number of horizontal and/or vertical pages is > 1, - # so we need to remember these 3 controls for later - self.h_pages = h_pages - self.v_pages = v_pages - self.page_dir = page_dir - - # the page direction option only makes sense when the - # number of horizontal and/or vertical pages is > 1 - self.h_pages.connect('value-changed', self.pages_changed) - self.v_pages.connect('value-changed', self.pages_changed) - - ################################ - category = _("GraphViz Options") - ################################ - - aspect_ratio = EnumeratedListOption(_("Aspect ratio"), "fill") - index = 0 - for item in _RATIO: - aspect_ratio.add_item(item["value"], item["name"]) - index += 1 - aspect_ratio.set_help(_("Affects greatly how the graph is layed out " - "on the page.")) - self.options.add_menu_option(category, "ratio", aspect_ratio) - - dpi = NumberOption(_("DPI"), 75, 20, 1200) - dpi.set_help(_( "Dots per inch. When creating images such as " - ".gif or .png files for the web, try numbers " - "such as 100 or 300 DPI. When creating PostScript " - "or PDF files, use 72 DPI.")) - self.options.add_menu_option(category, "dpi", dpi) - - nodesep = NumberOption(_("Node spacing"), 0.20, 0.01, 5.00, 0.01) - nodesep.set_help(_( "The minimum amount of free space, in inches, " - "between individual nodes. For vertical graphs, " - "this corresponds to spacing between columns. " - "For horizontal graphs, this corresponds to " - "spacing between rows.")) - self.options.add_menu_option(category, "nodesep", nodesep) - - ranksep = NumberOption(_("Rank spacing"), 0.20, 0.01, 5.00, 0.01) - ranksep.set_help(_( "The minimum amount of free space, in inches, " - "between ranks. For vertical graphs, this " - "corresponds to spacing between rows. For " - "horizontal graphs, this corresponds to spacing " - "between columns.")) - self.options.add_menu_option(category, "ranksep", ranksep) - - use_subgraphs = BooleanOption(_('Use subgraphs'), True) - use_subgraphs.set_help(_("Subgraphs can help GraphViz position " - "spouses together, but with non-trivial " - "graphs will result in longer lines and " - "larger graphs.")) - self.options.add_menu_option(category, "usesubgraphs", use_subgraphs) - - ################################ - category = _("Note") - ################################ - - note = TextOption(_("Note to add to the graph"), - [""] ) - note.set_help(_("This text will be added to the graph.")) - self.options.add_menu_option(category, "note", note) - - noteloc = EnumeratedListOption(_("Note location"), 't') - for i in range( 0, len(_NOTELOC) ): - noteloc.add_item(_NOTELOC[i]["value"], _NOTELOC[i]["name"]) - noteloc.set_help(_("Whether note will appear on top " - "or bottom of the page.")) - self.options.add_menu_option(category, "noteloc", noteloc) - - notesize = NumberOption(_("Note size"), 32, 8, 128) - notesize.set_help(_("The size of note text, in points.")) - self.options.add_menu_option(category, "notesize", notesize) - - self.options.load_previous_values() - - def pages_changed(self): - """ - This method gets called every time the v_pages or h_pages - options are changed; when both vertical and horizontal - pages are set to "1", then the page_dir control needs to - be unavailable - """ - if self.v_pages.get_value() > 1 or \ - self.h_pages.get_value() > 1: - self.page_dir.set_available(True) - else: - self.page_dir.set_available(False) - - def init_interface(self): - ReportDialog.init_interface(self) - self.doc_type_changed(self.format_menu) - - def setup_format_frame(self): - """Set up the format frame of the dialog.""" - self.format_menu = GraphvizFormatComboBox() - self.format_menu.set(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.row, self.row+1, gtk.SHRINK|gtk.FILL) - self.tbl.attach(self.format_menu, 2, 4, self.row, self.row+1, - yoptions=gtk.SHRINK) - self.row += 1 - - self.open_with_app = gtk.CheckButton(_("Open with default viewer")) - self.tbl.attach(self.open_with_app, 2, 4, self.row, self.row+1, - yoptions=gtk.SHRINK) - self.row += 1 - - ext = self.format_menu.get_ext() - if ext is None: - ext = "" - else: - spath = self.get_default_directory() - base = "%s%s" % (self.raw_name, ext) - spath = os.path.normpath(os.path.join(spath, base)) - self.target_fileentry.set_filename(spath) - - def setup_report_options_frame(self): - self.paper_label = gtk.Label('%s'%_("Paper Options")) - self.paper_label.set_use_markup(True) - - self.paper_frame = PaperFrame( - self.options.handler.get_paper_metric(), - self.options.handler.get_paper_name(), - self.options.handler.get_orientation(), - self.options.handler.get_margins(), - self.options.handler.get_custom_paper_size() - ) - self.notebook.insert_page(self.paper_frame, self.paper_label, 0) - self.paper_frame.show_all() - - ReportDialog.setup_report_options_frame(self) - - 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. - """ - self.open_with_app.set_sensitive(True) - - 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) - - def make_document(self): - """Create a document of the type requested by the user. - """ - pstyle = self.paper_frame.get_paper_style() - - self.doc = self.format(self.options, pstyle) - - self.options.set_document(self.doc) - - if self.open_with_app.get_active(): - self.doc.open_requested() - - 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_user_options() - - self.options.handler.set_paper_metric( - self.paper_frame.get_paper_metric()) - self.options.handler.set_paper_name(self.paper_frame.get_paper_name()) - self.options.handler.set_orientation(self.paper_frame.get_orientation()) - self.options.handler.set_margins(self.paper_frame.get_paper_margins()) - self.options.handler.set_custom_paper_size( - self.paper_frame.get_custom_paper_size()) - - # Create the output document. - self.make_document() - - # Save options - self.options.handler.save_options() - - 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 setup_style_frame(self): - """Required by ReportDialog""" - pass diff --git a/src/ReportBase/_PaperMenu.py b/src/ReportBase/_PaperMenu.py deleted file mode 100644 index 110bc2f15..000000000 --- a/src/ReportBase/_PaperMenu.py +++ /dev/null @@ -1,383 +0,0 @@ -# -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2000-2007 Donald N. Allingham -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -# - -# $Id$ - -#------------------------------------------------------------------------- -# -# Python modules -# -#------------------------------------------------------------------------- -from gen.ggettext import sgettext as _ - -#------------------------------------------------------------------------- -# -# GNOME modules -# -#------------------------------------------------------------------------- -import gtk -import gobject - -#------------------------------------------------------------------------- -# -# GRAMPS modules -# -#------------------------------------------------------------------------- -from gen.plug.docgen import PaperStyle, PaperSize, PAPER_PORTRAIT, \ - PAPER_LANDSCAPE -from gen.plug.utils import gfloat -import const -from glade import Glade - -#------------------------------------------------------------------------- -# -# Try to abstract SAX1 from SAX2 -# -#------------------------------------------------------------------------- -try: - from xml.sax import make_parser, handler,SAXParseException -except: - from _xmlplus.sax import make_parser, handler,SAXParseException - -#------------------------------------------------------------------------- -# -# Constants -# -#------------------------------------------------------------------------- -paper_sizes = [] - -#------------------------------------------------------------------------- -# -# PaperComboBox -# -#------------------------------------------------------------------------- -class PaperComboBox(gtk.ComboBox): - - def __init__(self,default_name): - gtk.ComboBox.__init__(self) - - self.store = gtk.ListStore(gobject.TYPE_STRING) - self.set_model(self.store) - cell = gtk.CellRendererText() - self.pack_start(cell,True) - self.add_attribute(cell,'text',0) - self.mapping = {} - - index = 0 - start_index = 0 - for key in paper_sizes: - self.mapping[key.get_name()] = key - self.store.append(row=[key.get_name()]) - if key.get_name() == default_name: - start_index = index - index += 1 - - self.set_active(start_index) - - def get_value(self): - active = self.get_active() - if active < 0: - return None - key = unicode(self.store[active][0]) - return (self.mapping[key],key) - -#------------------------------------------------------------------------- -# -# OrientationComboBox -# -#------------------------------------------------------------------------- -class OrientationComboBox(gtk.ComboBox): - - def __init__(self,default=PAPER_PORTRAIT): - gtk.ComboBox.__init__(self) - - self.store = gtk.ListStore(gobject.TYPE_STRING) - self.set_model(self.store) - cell = gtk.CellRendererText() - self.pack_start(cell,True) - self.add_attribute(cell,'text',0) - self.mapping = {} - - self.store.append(row=[_('Portrait')]) - self.store.append(row=[_('Landscape')]) - if default == PAPER_PORTRAIT: - self.set_active(0) - else: - self.set_active(1) - - def set_value(self,value=0): - if value == PAPER_PORTRAIT: - self.set_active(0) - else: - self.set_active(1) - - def get_value(self): - active = self.get_active() - if active < 0: - return None - if active == 0: - return PAPER_PORTRAIT - else: - return PAPER_LANDSCAPE - -#------------------------------------------------------------------------- -# -# PaperFrame -# -#------------------------------------------------------------------------- -class PaperFrame(gtk.HBox): - """PaperFrame provides all the entry necessary to specify a paper style. """ - def __init__(self,default_metric,default_name,default_orientation, - margins=[2.54,2.54,2.54,2.54], custom=[29.7,21.0]): - gtk.HBox.__init__(self) - glade_xml = Glade() - - self.paper_table = glade_xml.get_object('paper_table') - - - # get all the widgets - widgets = ('pwidth', 'pheight', 'lmargin', 'rmargin', 'tmargin', - 'bmargin', 'lunits1', 'lunits2', 'lunits3', 'lunits4', - 'lunits5', 'lunits6', 'metric') - - for w in widgets: - setattr(self, w, glade_xml.get_object(w)) - - # insert custom widgets - self.papersize_menu = PaperComboBox(default_name) - self.orientation_menu = OrientationComboBox(default_orientation) - self.metric.set_active(default_metric) - - # connect all widgets - format_table = glade_xml.get_object('format_table') - format_table.attach(self.papersize_menu, 1, 3, 0, 1, - yoptions=gtk.SHRINK) - format_table.attach(self.orientation_menu, 1, 3, 3, 4, - yoptions=gtk.SHRINK) - - # connect signals - self.papersize_menu.connect('changed',self.size_changed) - self.metric.connect('toggled',self.units_changed) - - # set initial values - self.paper_unit = 'cm' - self.paper_unit_multiplier = 1.0 - - self.pwidth.set_text("%.2f" % custom[0]) - self.pheight.set_text("%.2f" % custom[1]) - self.lmargin.set_text("%.2f" % margins[0]) - self.rmargin.set_text("%.2f" % margins[1]) - self.tmargin.set_text("%.2f" % margins[2]) - self.bmargin.set_text("%.2f" % margins[3]) - - self.paper_table.show_all() - self.paper_table.reparent(self) - - self.units_changed(self.metric) - self.size_changed(None) - - def size_changed(self, obj): - """Paper size combobox 'changed' callback.""" - size, name = self.get_paper_size() - - is_custom = name == _("Custom Size") - self.pwidth.set_sensitive(is_custom) - self.pheight.set_sensitive(is_custom) - - if self.paper_unit == 'cm': - self.pwidth.set_text("%.2f" % size.get_width()) - self.pheight.set_text("%.2f" % size.get_height()) - elif self.paper_unit == 'in.': - self.pwidth.set_text("%.2f" % size.get_width_inches()) - self.pheight.set_text("%.2f" % size.get_height_inches()) - else: - raise ValueError('Paper dimension unit "%s" is not allowed' % - self.paper_unit) - - def units_changed(self, checkbox): - """Metric checkbox 'toggled' callback.""" - paper_size, paper_name = self.get_paper_size() - paper_margins = self.get_paper_margins() - - if checkbox.get_active(): - self.paper_unit = 'cm' - self.paper_unit_multiplier = 1.0 - paper_unit_text = _("cm") - else: - self.paper_unit = 'in.' - self.paper_unit_multiplier = 2.54 - paper_unit_text = _("inch|in.") - - self.lunits1.set_text(paper_unit_text) - self.lunits2.set_text(paper_unit_text) - self.lunits3.set_text(paper_unit_text) - self.lunits4.set_text(paper_unit_text) - self.lunits5.set_text(paper_unit_text) - self.lunits6.set_text(paper_unit_text) - - if self.paper_unit == 'cm': - self.pwidth.set_text("%.2f" % paper_size.get_width()) - self.pheight.set_text("%.2f" % paper_size.get_height()) - else: - self.pwidth.set_text("%.2f" % paper_size.get_width_inches()) - self.pheight.set_text("%.2f" % paper_size.get_height_inches()) - - self.lmargin.set_text("%.2f" % - (paper_margins[0] / self.paper_unit_multiplier)) - self.rmargin.set_text("%.2f" % - (paper_margins[1] / self.paper_unit_multiplier)) - self.tmargin.set_text("%.2f" % - (paper_margins[2] / self.paper_unit_multiplier)) - self.bmargin.set_text("%.2f" % - (paper_margins[3] / self.paper_unit_multiplier)) - - def get_paper_size(self): - """Read and validate paper size values. - - If needed update the dimensions from the width, height entries, - and worst case fallback to A4 size. - - """ - papersize, papername = self.papersize_menu.get_value() - # FIXME it is wrong to use translatable text in comparison. - # How can we distinguish custom size though? - if papername == _('Custom Size'): - try: - h = float(unicode(self.pheight.get_text().replace(",", "."))) - w = float(unicode(self.pwidth.get_text().replace(",", ".") )) - - if h <= 1.0 or w <= 1.0: - papersize.set_height(29.7) - papersize.set_width(21.0) - else: - papersize.set_height(h * self.paper_unit_multiplier) - papersize.set_width(w * self.paper_unit_multiplier) - except: - papersize.set_height(29.7) - papersize.set_width(21.0) - - return papersize, papername - - def get_paper_margins(self): - """Get and validate margin values from dialog entries. - - Values returned in [cm]. - - """ - paper_margins = [unicode(margin.get_text()) for margin in - self.lmargin, self.rmargin, self.tmargin, self.bmargin] - - for i, margin in enumerate(paper_margins): - try: - paper_margins[i] = float(margin.replace(",", ".")) - paper_margins[i] = paper_margins[i] * self.paper_unit_multiplier - paper_margins[i] = max(paper_margins[i], 0) - except: - paper_margins[i] = 2.54 - - return paper_margins - - def get_custom_paper_size(self): - width = float(self.pwidth.get_text().replace(",", ".")) * \ - self.paper_unit_multiplier - height = float(self.pheight.get_text().replace(",", ".")) * \ - self.paper_unit_multiplier - - paper_size = [max(width, 1.0), max(height, 1.0)] - - return paper_size - - def get_paper_style(self): - paper_size, paper_name = self.get_paper_size() - paper_orientation = self.orientation_menu.get_value() - paper_margins = self.get_paper_margins() - - pstyle = PaperStyle(paper_size, - paper_orientation, - *paper_margins) - return pstyle - - def get_paper_metric(self): - return self.metric.get_active() - - def get_paper_name(self): - paper_size, paper_name = self.get_paper_size() - return paper_name - - def get_orientation(self): - return self.orientation_menu.get_value() - -#------------------------------------------------------------------------- -# -# PageSizeParser -# -#------------------------------------------------------------------------- -class PageSizeParser(handler.ContentHandler): - """Parses the XML file and builds the list of page sizes""" - - def __init__(self,paper_list): - handler.ContentHandler.__init__(self) - self.paper_list = paper_list - - def setDocumentLocator(self,locator): - self.locator = locator - - def startElement(self,tag,attrs): - if tag == "page": - name = attrs['name'] - height = gfloat(attrs['height']) - width = gfloat(attrs['width']) - self.paper_list.append(PaperSize(name, height,width)) - -#------------------------------------------------------------------------- -# -# Parse XML file. If failed, used default -# -#------------------------------------------------------------------------- -try: - parser = make_parser() - parser.setContentHandler(PageSizeParser(paper_sizes)) - the_file = open(const.PAPERSIZE) - parser.parse(the_file) - the_file.close() - paper_sizes.append(PaperSize(_("Custom Size"),-1,-1)) -except (IOError,OSError,SAXParseException): - paper_sizes = [ - PaperSize("Letter",27.94,21.59), - PaperSize("Legal",35.56,21.59), - PaperSize("A0",118.9,84.1), - PaperSize("A1",84.1,59.4), - PaperSize("A2",59.4,42.0), - PaperSize("A3",42.0,29.7), - PaperSize("A4",29.7,21.0), - PaperSize("A5",21.0,14.8), - PaperSize("B0",141.4,100.0), - PaperSize("B1",100.0,70.7), - PaperSize("B2",70.7,50.0), - PaperSize("B3",50.0,35.3), - PaperSize("B4",35.3,25.0), - PaperSize("B5",25.0,17.6), - PaperSize("B6",17.6,12.5), - PaperSize("B",43.18,27.94), - PaperSize("C",55.88,43.18), - PaperSize("D",86.36, 55.88), - PaperSize("E",111.76,86.36), - PaperSize(_("Custom Size"),-1,-1) - ] diff --git a/src/ReportBase/_Report.py b/src/ReportBase/_Report.py deleted file mode 100644 index b1bbe5d87..000000000 --- a/src/ReportBase/_Report.py +++ /dev/null @@ -1,62 +0,0 @@ -# -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2001 David R. Hampton -# Copyright (C) 2001-2006 Donald N. Allingham -# Copyright (C) 2007 Brian G. Matherly -# -# 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 -# -#------------------------------------------------------------------------- -class Report(object): - """ - 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, options_class): - self.database = database - self.options_class = options_class - - self.doc = options_class.get_document() - - creator = database.get_researcher().get_name() - self.doc.set_creator(creator) - - output = options_class.get_output() - if output: - self.standalone = True - self.doc.open(options_class.get_output()) - else: - self.standalone = False - - def begin_report(self): - pass - - def write_report(self): - pass - - def end_report(self): - if self.standalone: - self.doc.close() - diff --git a/src/ReportBase/_ReportDialog.py b/src/ReportBase/_ReportDialog.py deleted file mode 100644 index 23f431b0f..000000000 --- a/src/ReportBase/_ReportDialog.py +++ /dev/null @@ -1,648 +0,0 @@ -# -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2001-2006 Donald N. Allingham -# Copyright (C) 2008 Brian G. Matherly -# -# 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$ - -#------------------------------------------------------------------------- -# -# Python modules -# -#------------------------------------------------------------------------- -import os -from types import ClassType -from gen.ggettext import gettext as _ - -import logging -LOG = logging.getLogger(".") - -#------------------------------------------------------------------------- -# -# GTK+ modules -# -#------------------------------------------------------------------------- -import gtk - -#------------------------------------------------------------------------- -# -# GRAMPS modules -# -#------------------------------------------------------------------------- -import config -import Errors -from QuestionDialog import ErrorDialog, OptionDialog -from ReportBase import (CATEGORY_TEXT, CATEGORY_DRAW, CATEGORY_BOOK, - CATEGORY_CODE, CATEGORY_WEB, CATEGORY_GRAPHVIZ, - standalone_categories) -from gen.plug.docgen import StyleSheet, StyleSheetList -import ManagedWindow -from _StyleComboBox import StyleComboBox -from _StyleEditor import StyleListDisplay -from _FileEntry import FileEntry -from const import URL_MANUAL_PAGE - -#------------------------------------------------------------------------- -# -# Private Constants -# -#------------------------------------------------------------------------- -URL_REPORT_PAGE = URL_MANUAL_PAGE + "_-_Reports" - -#------------------------------------------------------------------------- -# -# ReportDialog class -# -#------------------------------------------------------------------------- -class ReportDialog(ManagedWindow.ManagedWindow): - """ - 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. - """ - border_pad = 6 - - def __init__(self, dbstate, uistate, option_class, name, trans_name, - track=[]): - """Initialize a dialog to request that the user select options - for a basic *stand-alone* report.""" - - self.style_name = "default" - self.firstpage_added = False - self.raw_name = name - self.dbstate = dbstate - self.db = dbstate.db - self.report_name = trans_name - - ManagedWindow.ManagedWindow.__init__(self, uistate, track, self) - - self.init_options(option_class) - self.init_interface() - - def init_options(self, option_class): - try: - if (issubclass(option_class, object) or # New-style class - isinstance(option_class, ClassType)): # Old-style class - self.options = option_class(self.raw_name, self.db) - except TypeError: - self.options = option_class - - self.options.load_previous_values() - - def build_window_key(self, obj): - key = self.raw_name - return key - - def build_menu_names(self, obj): - return (_("Configuration"), self.report_name) - - def init_interface(self): - 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() - - window = gtk.Dialog('Gramps') - self.set_window(window, None, self.get_title()) - self.window.set_has_separator(False) - self.window.set_modal(True) - - self.help = self.window.add_button(gtk.STOCK_HELP, gtk.RESPONSE_HELP) - self.help.connect('clicked', self.on_help_clicked) - - self.cancel = self.window.add_button(gtk.STOCK_CANCEL, - gtk.RESPONSE_CANCEL) - self.cancel.connect('clicked', self.on_cancel) - - self.ok = self.window.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK) - self.ok.connect('clicked', self.on_ok_clicked) - - 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.row = 0 - - # Build the list of widgets that are used to extend the Options - # frame and to create other frames - self.add_user_options() - - self.setup_main_options() - self.setup_init() - self.setup_format_frame() - self.setup_target_frame() - self.setup_style_frame() - - self.notebook = gtk.Notebook() - self.notebook.set_scrollable(True) - self.notebook.set_border_width(6) - self.window.vbox.add(self.notebook) - - self.setup_report_options_frame() - self.setup_other_frames() - self.notebook.set_current_page(0) - - self.window.vbox.add(self.tbl) - self.show() - - 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) - - #------------------------------------------------------------------------ - # - # 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): - """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 responsible 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)) - - def add_frame_option(self, frame_name, label_text, widget): - """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 responsible 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 frame_name in self.frames: - self.frames[frame_name].append((label_text, widget)) - else: - self.frames[frame_name] = [(label_text, widget)] - self.frame_names.append(frame_name) - - #------------------------------------------------------------------------ - # - # 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.window.set_title(self.get_title()) - - def setup_header(self): - """Set up the header line bar of the dialog.""" - label = gtk.Label('%s' % - self.report_name) - label.set_use_markup(True) - self.window.vbox.pack_start(label, True, True, self.border_pad) - - 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.""" - # Build the default style set for this report. - self.default_style = StyleSheet() - self.options.make_default_style(self.default_style) - - if self.default_style.is_empty(): - # Don't display the option of no styles are used - return - - # Styles Frame - label = gtk.Label("%s:" % _("Style")) - label.set_alignment(0.0, 0.5) - - self.style_menu = StyleComboBox() - 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.row, self.row+1, gtk.SHRINK|gtk.FILL) - self.tbl.attach(self.style_menu, 2, 3, self.row, self.row+1, - yoptions=gtk.SHRINK) - self.tbl.attach(self.style_button, 3, 4, self.row, self.row+1, - xoptions=gtk.SHRINK|gtk.FILL, yoptions=gtk.SHRINK) - self.row += 1 - - # 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 = 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 is used in most - (but not all) dialog boxes.""" - - row = 0 - 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 - - # 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: - if key == "": - continue - 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 += 1 - - def setup_main_options(self): - if "" in self.frames: - flist = self.frames[""] - for (text, widget) in flist: - label = gtk.Label("%s" % text) - label.set_use_markup(True) - label.set_alignment(0.0, 0.5) - - self.tbl.set_border_width(12) - self.tbl.attach(label, 0, 4, self.row, self.row+1) - self.row += 1 - - self.tbl.attach(widget, 2, 4, self.row, self.row+1) - self.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 - - #------------------------------------------------------------------------ - # - # 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('paths.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('paths.report-directory', value) - - #------------------------------------------------------------------------ - # - # Functions related to setting up the dialog window. - # - #------------------------------------------------------------------------ - def setup_init(self): - # add any elements that we are going to need: - hid = self.style_name - if hid[-4:] == ".xml": - hid = hid[0:-4] - self.target_fileentry = FileEntry(hid, _("Save As")) - spath = self.get_default_directory() - self.target_fileentry.set_filename(spath) - # need any labels at top: - 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.row, self.row+1, gtk.FILL) - self.row += 1 - - 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 - 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.row, self.row+1, - xoptions=gtk.SHRINK|gtk.FILL,yoptions=gtk.SHRINK) - self.tbl.attach(self.target_fileentry, 2, 4, self.row, self.row+1, - xoptions=gtk.EXPAND|gtk.FILL,yoptions=gtk.SHRINK) - self.row += 1 - - #------------------------------------------------------------------------ - # - # 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): - - # 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 existing file and we need a file - if os.path.isfile(self.target_path): - 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_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.""" - if not self.default_style.is_empty(): - (style_name, self.selected_style) = self.style_menu.get_value() - self.options.handler.set_default_stylesheet_name(style_name) - - #------------------------------------------------------------------------ - # - # Callback functions from the dialog - # - #------------------------------------------------------------------------ - 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_style_frame() - self.parse_user_options() - - # Save options - self.options.handler.save_options() - - def on_cancel(self, *obj): - pass - - def on_help_clicked(self, *obj): - import GrampsDisplay - GrampsDisplay.help(URL_REPORT_PAGE, self.report_name.replace(" ", "_")) - - 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) - -#------------------------------------------------------------------------ -# -# Generic task function a standalone GUI report -# -#------------------------------------------------------------------------ -def report(dbstate, uistate, person, report_class, options_class, - trans_name, name, category, require_active): - """ - 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 require_active and not person: - ErrorDialog( - _('Active person has not been set'), - _('You must select an active person for this report to work ' - 'properly.')) - return - - if category == CATEGORY_TEXT: - from _TextReportDialog import TextReportDialog - dialog_class = TextReportDialog - elif category == CATEGORY_DRAW: - from _DrawReportDialog import DrawReportDialog - dialog_class = DrawReportDialog - elif category == CATEGORY_GRAPHVIZ: - from _GraphvizReportDialog import GraphvizReportDialog - dialog_class = GraphvizReportDialog - elif category == CATEGORY_WEB: - from _WebReportDialog import WebReportDialog - dialog_class = WebReportDialog - elif category in (CATEGORY_BOOK, CATEGORY_CODE): - try: - report_class(dbstate, uistate) - except Errors.WindowActiveError: - pass - return - else: - dialog_class = ReportDialog - - dialog = dialog_class(dbstate, uistate, options_class, name, trans_name) - - while True: - response = dialog.window.run() - if response == gtk.RESPONSE_OK: - dialog.close() - try: - MyReport = report_class(dialog.db, 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 IOError, msg: - ErrorDialog(_("Report could not be created"), str(msg)) - except Errors.ReportError, msg: - (m1, m2) = msg.messages() - ErrorDialog(m1, m2) - except Errors.DatabaseError,msg: - ErrorDialog(_("Report could not be created"), str(msg)) -# The following except statement will catch all "NoneType" exceptions. -# This is useful for released code where the exception is most likely -# a corrupt database. But it is less useful for developing new reports -# where the exception is most likely a report bug. -# except AttributeError,msg: -# if str(msg).startswith("'NoneType' object has no attribute"): -# # "'NoneType' object has no attribute ..." usually means -# # database corruption -# RunDatabaseRepair(str(msg)) -# else: -# raise - raise - except: - LOG.error("Failed to run report.", exc_info=True) - break - elif response == gtk.RESPONSE_CANCEL: - dialog.close() - break - elif response == gtk.RESPONSE_DELETE_EVENT: - #just stop, in ManagedWindow, delete-event is already coupled to - #correct action. - break diff --git a/src/ReportBase/_ReportOptions.py b/src/ReportBase/_ReportOptions.py deleted file mode 100644 index 7e4fbf001..000000000 --- a/src/ReportBase/_ReportOptions.py +++ /dev/null @@ -1,819 +0,0 @@ -# -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2004-2007 Donald N. Allingham -# Copyright (C) 2008 Gary Burton -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -# -# $Id$ - -# Written by Alex Roitman - -""" -Report option handling, including saving and parsing. -""" - -#------------------------------------------------------------------------- -# -# Standard Python modules -# -#------------------------------------------------------------------------- -import os -import copy -from xml.sax.saxutils import escape - -def escxml(d): - return escape(d, { '"' : '"' } ) - -#------------------------------------------------------------------------- -# -# SAX interface -# -#------------------------------------------------------------------------- -try: - from xml.sax import make_parser, SAXParseException -except: - from _xmlplus.sax import make_parser, SAXParseException - -#------------------------------------------------------------------------- -# -# gramps modules -# -#------------------------------------------------------------------------- -import const -import config -from gen.plug.docgen import PAPER_PORTRAIT -from PluginUtils import _Options, GuiMenuOptions - -#------------------------------------------------------------------------- -# -# List of options for a single report -# -#------------------------------------------------------------------------- -class OptionList(_Options.OptionList): - """ - Implements a set of options to parse and store for a given report. - """ - - def __init__(self): - _Options.OptionList.__init__(self) - self.style_name = None - self.paper_metric = None - self.paper_name = None - self.orientation = None - self.custom_paper_size = [29.7, 21.0] - self.margins = [2.54, 2.54, 2.54, 2.54] - self.format_name = None - self.css_filename = None - - def set_style_name(self, style_name): - """ - Set 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): - """ - Return the style name of the OptionList. - @returns: string representing the style name - @rtype: str - """ - return self.style_name - - def set_paper_metric(self, paper_metric): - """ - Set the paper metric for the OptionList. - @param paper_metric: whether to use metric. - @type paper_name: boolean - """ - self.paper_metric = paper_metric - - def get_paper_metric(self): - """ - Return the paper metric of the OptionList. - @returns: returns whether to use metric - @rtype: boolean - """ - return self.paper_metric - - def set_paper_name(self, paper_name): - """ - Set 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): - """ - Return the paper name of the OptionList. - @returns: returns the paper name - @rtype: str - """ - return self.paper_name - - def set_orientation(self, orientation): - """ - Set the orientation for the OptionList. - @param orientation: orientation to set. Possible values are - PAPER_LANDSCAPE or PAPER_PORTRAIT - @type orientation: int - """ - self.orientation = orientation - - def get_orientation(self): - """ - Return the orientation for the OptionList. - @returns: returns the selected orientation. Valid values are - PAPER_LANDSCAPE or PAPER_PORTRAIT - @rtype: int - """ - return self.orientation - - def set_custom_paper_size(self, paper_size): - """ - Set the custom paper size for the OptionList. - @param paper_size: paper size to set in cm. - @type paper_size: [float, float] - """ - self.custom_paper_size = paper_size - - def get_custom_paper_size(self): - """ - Return the custom paper size for the OptionList. - @returns: returns the custom paper size in cm - @rtype: [float, float] - """ - return self.custom_paper_size - - def set_margins(self, margins): - """ - Set the margins for the OptionList. - @param margins: margins to set. Possible values are floats in cm - @type margins: [float, float, float, float] - """ - self.margins = copy.copy(margins) - - def get_margins(self): - """ - Return the margins for the OptionList. - @returns margins: returns the margins, floats in cm - @rtype margins: [float, float, float, float] - """ - return copy.copy(self.margins) - - def set_margin(self, pos, value): - """ - Set a margin for the OptionList. - @param pos: Position of margin [left, right, top, bottom] - @param value: floating point in cm - @type pos: int - @type value: float - """ - self.margins[pos] = value - - def get_margin(self, pos): - """ - Return a margin for the OptionList. - @param pos: Position of margin [left, right, top, bottom] - @type pos: int - @returns: float cm of margin - @rtype: float - """ - return self.margins[pos] - - def set_css_filename(self, css_filename): - """ - Set the template name for the OptionList. - @param template_name: name of the template to set. - @type template_name: str - """ - self.css_filename = css_filename - - def get_css_filename(self): - """ - Return the template name of the OptionList. - @returns: template name - @rtype: str - """ - return self.css_filename - - def set_format_name(self, format_name): - """ - Set 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): - """ - Return the format name of the OptionList. - @returns: returns the format name - @rtype: str - """ - return self.format_name - -#------------------------------------------------------------------------- -# -# Collection of option lists -# -#------------------------------------------------------------------------- -class OptionListCollection(_Options.OptionListCollection): - """ - Implements a collection of option lists. - """ - def __init__(self, filename): - _Options.OptionListCollection.__init__(self, filename) - - def init_common(self): - # Default values for common options - self.default_style_name = "default" - self.default_paper_metric = config.get('preferences.paper-metric') - self.default_paper_name = config.get('preferences.paper-preference') - self.default_orientation = PAPER_PORTRAIT - self.default_css_filename = "" - self.default_custom_paper_size = [29.7, 21.0] - self.default_margins = [2.54, 2.54, 2.54, 2.54] - self.default_format_name = 'print' - - self.last_paper_metric = self.default_paper_metric - self.last_paper_name = self.default_paper_name - self.last_orientation = self.default_orientation - self.last_custom_paper_size = copy.copy(self.default_custom_paper_size) - self.last_margins = copy.copy(self.default_margins) - self.last_css_filename = self.default_css_filename - self.last_format_name = self.default_format_name - self.option_list_map = {} - - def set_last_paper_metric(self, paper_metric): - """ - Set the last paper metric used for the any report in this collection. - @param paper_metric: whether to use metric. - @type paper_name: boolean - """ - self.last_paper_metric = paper_metric - - def get_last_paper_metric(self): - """ - Return the last paper metric used for the any report in this collection. - @returns: returns whether or not to use metric - @rtype: boolean - """ - return self.last_paper_metric - - def set_last_paper_name(self, paper_name): - """ - Set 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): - """ - Return 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): - """ - Set 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): - """ - Return the last orientation used for the any report in this - collection. - @returns: last orientation used - @rtype: int - """ - return self.last_orientation - - def set_last_custom_paper_size(self, custom_paper_size): - """ - Set the last custom paper size used for the any report in this collection. - @param custom_paper_size: size to set in cm (width, height) - @type margins: [float, float] - """ - self.last_custom_paper_size = copy.copy(custom_paper_size) - - def get_last_custom_paper_size(self): - """ - Return the last custom paper size used for the any report in this - collection. - @returns: list of last custom paper size used in cm (width, height) - @rtype: [float, float] - """ - return copy.copy(self.last_custom_paper_size) - - def set_last_margins(self, margins): - """ - Set the last margins used for the any report in this collection. - @param margins: margins to set in cm (left, right, top, bottom) - @type margins: [float, float, float, float] - """ - self.last_margins = copy.copy(margins) - - def get_last_margins(self): - """ - Return the last margins used for the any report in this - collection. - @returns: list of last margins used in cm (left, right, top, bottom) - @rtype: [float, float, float, float] - """ - return copy.copy(self.last_margins) - - def set_last_margin(self, pos, value): - """ - Set the last margin used for the any report in this collection. - @param pos: pos to set (0-4) (left, right, top, bottom) - @type pos: int - @param value: value to set the margin to in cm - @type value: float - """ - self.last_margins[pos] = value - - def get_last_margin(self, pos): - """ - Return the last margins used for the any report in this - collection. - @param pos: position in margins list - @type pos: int - @returns: last margin used in pos - @rtype: float - """ - return self.last_margins[pos] - - def set_last_css_filename(self, css_filename): - """ - Set the last css used for the any report in this collection. - - css_filename: name of the style to set. - """ - self.last_css_name = css_filename - - def get_last_css_filename(self): - """ - Return the last template used for the any report in this collection. - """ - return self.last_css_filename - - def set_last_format_name(self, format_name): - """ - Set 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): - """ - Return 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_metric() != self.default_paper_metric: - f.write(' \n' % self.get_last_paper_metric() ) - if self.get_last_custom_paper_size() != self.default_custom_paper_size: - size = self.get_last_custom_paper_size() - f.write(' \n' % (size[0], size[1]) ) - if self.get_last_paper_name() != self.default_paper_name: - f.write(' \n' % escxml(self.get_last_paper_name()) ) - if self.get_last_css_filename() != self.default_css_filename: - f.write(' \n' % escxml(self.get_last_css_filename()) ) - if self.get_last_format_name() != self.default_format_name: - f.write(' \n' % escxml(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('