diff --git a/ChangeLog b/ChangeLog index 5f1f42526..003692931 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2007-09-04 Brian Matherly + * src/ReportBase/__init__.py: Add MenuOptions + * src/ReportBase/_MenuOptions.py: Added + * src/ReportBase/Makefile.am: Add MenuOptions + * src/plugins/DescendChart.py: Use MenuOptions + * src/plugins/AncestorChart.py: Use MenuOptions + * src/plugins/FanChart.py: Use MenuOptions + * src/Utils.py (get_type_converter): Handle boolean types. + 2007-09-03 Don Allingham * plugins/WritePkg.py: fix package export * GrampsDbUtils/_WriteXML.py: fix package export diff --git a/src/ReportBase/Makefile.am b/src/ReportBase/Makefile.am index 5565d295c..ad4693f3f 100644 --- a/src/ReportBase/Makefile.am +++ b/src/ReportBase/Makefile.am @@ -12,6 +12,7 @@ pkgdata_PYTHON = \ _DrawReportDialog.py\ _Endnotes.py\ _FileEntry.py\ + _MenuOptions.py\ __init__.py\ _PaperMenu.py\ _PrintTools.py\ diff --git a/src/ReportBase/_MenuOptions.py b/src/ReportBase/_MenuOptions.py new file mode 100644 index 000000000..8b009ef9c --- /dev/null +++ b/src/ReportBase/_MenuOptions.py @@ -0,0 +1,533 @@ +# +# 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: $ + +""" +Abstracted option handling. +""" +#------------------------------------------------------------------------ +# +# gtk +# +#------------------------------------------------------------------------ +import gtk + +#------------------------------------------------------------------------- +# +# gramps modules +# +#------------------------------------------------------------------------- +from _ReportOptions import ReportOptions + +#------------------------------------------------------------------------- +# +# Option class +# +#------------------------------------------------------------------------- +class Option: + """ + This class serves as a base class for all options. All Options must + minimally provide the services provided by this class. Options are allowed + to add additional functionality. + """ + def __init__(self,label,value): + """ + @param label: A friendly label to be applied to this option. + Example: "Exclude living people" + @type label: string + @param value: An initial value for this option. + Example: True + @type value: The type will depend on the type of option. + @return: nothing + """ + self.__value = value + self.__label = label + self.__help_str = "" + + def get_label(self): + """ + Get the friendly label for this option. + + @return: string + """ + return self.__label + + def set_label(self,label): + """ + Set the friendly label for this option. + + @param label: A friendly label to be applied to this option. + Example: "Exclude living people" + @type label: string + @return: nothing + """ + self.__label = label + + def get_value(self): + """ + Get the value of this option. + + @return: The option value. + """ + return self.__value + + def set_value(self,value): + """ + Set the value of this option. + + @param value: A value for this option. + Example: True + @type value: The type will depend on the type of option. + @return: nothing + """ + self.__value = value + + def get_help(self): + """ + Get the help information for this option. + + @return: A string that provides additional help beyond the label. + """ + return self.__help_str + + def set_help(self,help): + """ + Set the help information for this option. + + @param help: A string that provides additional help beyond the label. + Example: "Whether to include or exclude people who are calculated + to be alive at the time of the generation of this report" + @type value: string + @return: nothing + """ + self.__help_str = help + +#------------------------------------------------------------------------- +# +# StringOption class +# +#------------------------------------------------------------------------- +class StringOption(Option): + """ + This class describes an option that is a simple one-line string. + """ + def __init__(self,label,value): + """ + @param label: A friendly label to be applied to this option. + Example: "Page header" + @type label: string + @param value: An initial value for this option. + Example: "Generated by GRAMPS" + @type value: string + @return: nothing + """ + Option.__init__(self,label,value) + +#------------------------------------------------------------------------- +# +# NumberOption class +# +#------------------------------------------------------------------------- +class NumberOption(Option): + """ + This class describes an option that is a simple number with defined maximum + and minimum values. + """ + def __init__(self,label,value,min,max): + """ + @param label: A friendly label to be applied to this option. + Example: "Number of generations to include" + @type label: string + @param value: An initial value for this option. + Example: 5 + @type value: int + @param min: The minimum value for this option. + Example: 1 + @type min: int + @param max: The maximum value for this option. + Example: 10 + @type value: int + @return: nothing + """ + Option.__init__(self,label,value) + self.__min = min + self.__max = max + + def get_min(self): + """ + Get the minimum value for this option. + + @return: an int that represents the minimum value for this option. + """ + return self.__min + + def get_max(self): + """ + Get the maximum value for this option. + + @return: an int that represents the maximum value for this option. + """ + return self.__max + +#------------------------------------------------------------------------- +# +# TextOption class +# +#------------------------------------------------------------------------- +class TextOption(Option): + """ + This class describes an option that is a multi-line string. + """ + def __init__(self,label,value): + """ + @param label: A friendly label to be applied to this option. + Example: "Page header" + @type label: string + @param value: An initial value for this option. + Example: "Generated by GRAMPS\nCopyright 2007" + @type value: string + @return: nothing + """ + Option.__init__(self,label,value) + +#------------------------------------------------------------------------- +# +# BooleanOption class +# +#------------------------------------------------------------------------- +class BooleanOption(Option): + """ + This class describes an option that is a boolean (True or False). + """ + def __init__(self,label,value): + """ + @param label: A friendly label to be applied to this option. + Example: "Exclude living people" + @type label: string + @param value: An initial value for this option. + Example: True + @type value: boolean + @return: nothing + """ + Option.__init__(self,label,value) + +#------------------------------------------------------------------------- +# +# EnumeratedListOption class +# +#------------------------------------------------------------------------- +class EnumeratedListOption(Option): + """ + This class describes an option that provides a finite number of values. + Each possible value is assigned a value and a description. + """ + def __init__(self,label,value): + """ + @param label: A friendly label to be applied to this option. + Example: "Paper Size" + @type label: string + @param value: An initial value for this option. + Example: 5 + @type value: int + @return: nothing + """ + Option.__init__(self,label,value) + self.__items = [] + + def add_item(self,value,description): + """ + Add an item to the list of possible values. + + @param value: The value that corresponds to this item. + Example: 5 + @type value: int + @param description: A description of this value. + Example: "8.5 x 11" + @type description: string + @return: nothing + """ + self.__items.append((value, description)) + + def get_items(self): + """ + Get all the possible values for this option. + + @return: an array of tuples containing (value,description) pairs. + """ + return self.__items + +#------------------------------------------------------------------------- +# +# Menu class +# +#------------------------------------------------------------------------- +class Menu: + """ + Introduction + ============ + A Menu is used to maintain a collection of options that need to be + represented to the user in a non-implementation specific way. The options + can be described using the various option classes. A menu contains many + options and associates them with a unique name and category. + + Usage + ===== + Menus are used in the following way. + + 1. Create a option object and configure all the attributes of the option. + 2. Add the option to the menu by specifying the option, name and category. + 3. Add as many options as necessary. + 4. When all the options are added, the menu can be stored and passed to + the part of the system that will actually represent the menu to + the user. + """ + def __init__(self): + self.__options = {} + + def add_option(self,category,name,option): + """ + Add an option to the menu. + + @param category: A label that describes the category that the option + belongs to. + Example: "Report Options" + @type category: string + @param name: A name that is unique to this option. + Example: "generations" + @type name: string + @param option: The option instance to be added to this menu. + @type option: Option + @return: nothing + """ + if not self.__options.has_key(category): + self.__options[category] = [] + self.__options[category].append((name,option)) + + def get_categories(self): + """ + Get a list of categories in this menu. + + @return: a list of strings + """ + categories = [] + for category in self.__options: + categories.append(category) + return categories + + def get_option_names(self,category): + """ + Get a list of option names for the specified category. + + @return: a list of strings + """ + names = [] + for (name,option) in self.__options[category]: + names.append(name) + return names + + def get_option(self,category,name): + """ + Get an option with the specified category and name. + + @return: an Option instance or None on failure. + """ + for (oname,option) in self.__options[category]: + if oname == name: + return option + return None + + def get_all_option_names(self): + """ + Get a list of all the option names in this menu. + + @return: a list of strings + """ + names = [] + for category in self.__options: + for (name,option) in self.__options[category]: + names.append(name) + return names + + def get_option_by_name(self,name): + """ + Get an option with the specified name. + + @return: an Option instance or None on failure. + """ + for category in self.__options.keys(): + for (oname,option) in self.__options[category]: + if oname == name: + return option + return None + +#------------------------------------------------------------------------ +# +# MenuOptions class +# +#------------------------------------------------------------------------ +class MenuOptions(ReportOptions): + """ + The MenuOptions class implementes the ReportOptions 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 MenuOptions class and override the + add_menu_options function. The user can add options to the menu and the + MenuOptions class will worry about setting up the GUI. + """ + def __init__(self,name,person_id=None): + self.menu = Menu() + ReportOptions.__init__(self,name, person_id) + + def make_default_style(self,default_style): + pass + + def set_new_options(self): + 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 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_user_options(self,dialog): + self.gui = {} + self.tooltips = gtk.Tooltips() + + for category in self.menu.get_categories(): + for name in self.menu.get_option_names(category): + option = self.menu.get_option(category,name) + otype = option.__class__ + + if otype == NumberOption: + self.__add_number_option(category,name,option,dialog) + elif otype == TextOption: + self.__add_text_option(category,name,option,dialog) + elif otype == BooleanOption: + self.__add_boolean_option(category,name,option,dialog) + elif otype == EnumeratedListOption: + self.__add_enumerated_list_option(category,name,option, + dialog) + else: + raise NotImplementedError + + def parse_user_options(self,dialog): + for name in self.menu.get_all_option_names(): + option = self.menu.get_option_by_name(name) + otype = option.__class__ + + if otype == NumberOption: + self.__parse_number_option(name,option) + elif otype == TextOption: + self.__parse_text_option(name,option) + elif otype == BooleanOption: + self.__parse_boolean_option(name,option) + elif otype == EnumeratedListOption: + self.__parse_enumerated_list_option(name,option) + else: + raise NotImplementedError + + def __add_number_option(self,category,name,option,dialog): + """ + Add a NumberOption to the dialog. + """ + adj = gtk.Adjustment(1,option.get_min(),option.get_max(),1) + self.gui[name] = gtk.SpinButton(adj) + self.gui[name].set_value(self.options_dict[name]) + dialog.add_frame_option(category,option.get_label(),self.gui[name]) + + self.tooltips.set_tip(self.gui[name],option.get_help()) + + def __parse_number_option(self,name,option): + self.options_dict[name] = int(self.gui[name].get_value_as_int()) + + def __add_text_option(self,category,name,option,dialog): + """ + Add a TextOption to the dialog. + """ + self.gui[name] = gtk.TextView() + self.gui[name].get_buffer().set_text("\n".join(self.options_dict[name])) + self.gui[name].set_editable(1) + swin = gtk.ScrolledWindow() + swin.set_shadow_type(gtk.SHADOW_IN) + swin.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC) + swin.add(self.gui[name]) + dialog.add_frame_option(category,option.get_label(),swin) + + # Required for tooltip + self.gui[name].add_events(gtk.gdk.ENTER_NOTIFY_MASK) + self.gui[name].add_events(gtk.gdk.LEAVE_NOTIFY_MASK) + self.tooltips.set_tip(self.gui[name],option.get_help()) + + def __parse_text_option(self,name,option): + b = self.gui[name].get_buffer() + text_val = unicode( b.get_text( b.get_start_iter(), + b.get_end_iter(), + False) ) + self.options_dict[name] = text_val.split('\n') + + def __add_boolean_option(self,category,name,option,dialog): + """ + Add a BooleanOption to the dialog. + """ + self.gui[name] = gtk.CheckButton(option.get_label()) + self.gui[name].set_active(self.options_dict[name]) + dialog.add_frame_option(category,"",self.gui[name]) + + self.tooltips.set_tip(self.gui[name],option.get_help()) + + def __parse_boolean_option(self,name,option): + self.options_dict[name] = self.gui[name].get_active() + + def __add_enumerated_list_option(self,category,name,option,dialog): + """ + Add an EnumeratedListOption to the dialog. + """ + active_index = 0 + current_index = 0 + self.gui[name] = gtk.combo_box_new_text() + for (value,description) in option.get_items(): + self.gui[name].append_text(description) + if value == self.options_dict[name]: + active_index = current_index + current_index += 1 + self.gui[name].set_active( active_index ) + dialog.add_frame_option(category,option.get_label(),self.gui[name]) + + self.tooltips.set_tip(self.gui[name],option.get_help()) + + def __parse_enumerated_list_option(self,name,option): + index = self.gui[name].get_active() + items = option.get_items() + (value,description) = items[index] + self.options_dict[name] = value \ No newline at end of file diff --git a/src/ReportBase/__init__.py b/src/ReportBase/__init__.py index 2225c11a2..b7c862b2b 100644 --- a/src/ReportBase/__init__.py +++ b/src/ReportBase/__init__.py @@ -37,6 +37,8 @@ from _DrawReportDialog import DrawReportDialog from _TextReportDialog import TextReportDialog from _ReportOptions import ReportOptions +from _MenuOptions import MenuOptions, NumberOption, BooleanOption, TextOption, \ + EnumeratedListOption import _ReportUtils as ReportUtils from _Bibliography import Bibliography, Citation diff --git a/src/Utils.py b/src/Utils.py index 71fba75ef..b964d89aa 100644 --- a/src/Utils.py +++ b/src/Utils.py @@ -831,6 +831,11 @@ def get_new_filename(ext,folder='~/'): ix = ix + 1 return os.path.expanduser(_NEW_NAME_PATTERN % (folder,os.path.sep,ix,ext)) +def cast_to_bool(val): + if val == str(True): + return True + return False + def get_type_converter(val): """ Returns function that converts strings into the type of val. @@ -842,6 +847,8 @@ def get_type_converter(val): return int elif val_type == float: return float + elif val_type == bool: + return cast_to_bool elif val_type in (list,tuple): return list diff --git a/src/plugins/AncestorChart.py b/src/plugins/AncestorChart.py index 6adbb8449..4bdd7e6bb 100644 --- a/src/plugins/AncestorChart.py +++ b/src/plugins/AncestorChart.py @@ -31,13 +31,6 @@ import math from gettext import gettext as _ -#------------------------------------------------------------------------ -# -# gtk -# -#------------------------------------------------------------------------ -import gtk - #------------------------------------------------------------------------ # # GRAMPS modules @@ -46,7 +39,8 @@ import gtk import BaseDoc from SubstKeywords import SubstKeywords from PluginUtils import register_report -from ReportBase import Report, ReportUtils, ReportOptions, \ +from ReportBase import Report, ReportUtils, \ + MenuOptions, NumberOption, BooleanOption, TextOption, \ CATEGORY_DRAW, MODE_GUI, MODE_BKI, MODE_CLI from BasicUtils import name_displayer pt2cm = ReportUtils.pt2cm @@ -235,7 +229,7 @@ class AncestorChart(Report): em = self.doc.string_width(font,"m") subst = SubstKeywords(self.database,person_handle) - self.text[index] = subst.replace_and_clean(self.display.split('\n')) + self.text[index] = subst.replace_and_clean(self.display) for line in self.text[index]: this_box_width = self.doc.string_width(font,line) @@ -440,83 +434,38 @@ class AncestorChart(Report): # # #------------------------------------------------------------------------ -class AncestorChartOptions(ReportOptions): +class AncestorChartOptions(MenuOptions): """ Defines options and provides handling interface. """ def __init__(self,name,person_id=None): - ReportOptions.__init__(self,name,person_id) - - def set_new_options(self): - # Options specific for this report - self.options_dict = { - 'dispf' : "$n\n%s $b\n%s $d" % (_BORN,_DIED), - 'maxgen' : 10, - 'singlep' : 1, - 'incblank' : 1, - 'compress' : 1, - } - self.options_help = { - 'dispf' : ("=str","Display format for the outputbox.", - "Allows you to customize the data in the boxes in the report", - True), - 'maxgen' : ("=int","Generations", - "The number of generations to include in the report", - True), - 'singlep' : ("=0/1","Whether to scale to fit on a single page.", - ["Do not scale to fit","Scale to fit"], - True), - 'incblank' : ("=0/1","Whether to include pages that are blank.", - ["Do not include blank pages","Include blank pages"], - True), - 'compress' : ("=0/1","Whether to compress chart.", - ["Do not compress chart","Compress chart"], - True), - } - - def add_user_options(self,dialog): - """ - Override the base class add_user_options task to add a menu that allows - the user to select the sort method. - """ - self.max_gen = gtk.SpinButton(gtk.Adjustment(1,1,100,1)) - self.max_gen.set_value(self.options_dict['maxgen']) - dialog.add_option(_('Generations'),self.max_gen) + MenuOptions.__init__(self,name,person_id) - self.extra_textbox = gtk.TextView() - self.extra_textbox.get_buffer().set_text(self.options_dict['dispf']) - self.extra_textbox.set_editable(1) - swin = gtk.ScrolledWindow() - swin.set_shadow_type(gtk.SHADOW_IN) - swin.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC) - swin.add(self.extra_textbox) - dialog.add_option(_("Display Format"),swin) - - self.scale = gtk.CheckButton(_('Sc_ale to fit on a single page')) - self.scale.set_active(self.options_dict['singlep']) - dialog.add_option('',self.scale) - - self.blank = gtk.CheckButton(_('Include Blank Pages')) - self.blank.set_active(self.options_dict['incblank']) - dialog.add_option('',self.blank) - - self.compress = gtk.CheckButton(_('Co_mpress chart')) - self.compress.set_active(self.options_dict['compress']) - dialog.add_option('',self.compress) - - def parse_user_options(self,dialog): - """ - Parses the custom options that we have added. - """ - b = self.extra_textbox.get_buffer() - text_val = unicode(b.get_text(b.get_start_iter(),b.get_end_iter(),False)) - self.options_dict['dispf'] = text_val - self.options_dict['maxgen'] = int(self.max_gen.get_value_as_int()) - self.options_dict['singlep'] = int(self.scale.get_active ()) - self.options_dict['incblank'] = int(self.blank.get_active()) - self.options_dict['compress'] = int(self.compress.get_active ()) + def add_menu_options(self,menu): + category_name = _("Report Options") + + max_gen = NumberOption(_("Generations"),10,1,50) + max_gen.set_help(_("The number of generations to include in the report")) + menu.add_option(category_name,"maxgen",max_gen) + + disp = TextOption(_("Display Format"), + ["$n","%s $b" % _BORN,"%s $d" %_DIED] ) + disp.set_help(_("Display format for the outputbox.")) + menu.add_option(category_name,"dispf",disp) + + scale = BooleanOption(_('Sc_ale to fit on a single page'),True) + scale.set_help(_("Whether to scale to fit on a single page.")) + menu.add_option(category_name,"singlep",scale) + + blank = BooleanOption(_('Include Blank Pages'),True) + blank.set_help(_("Whether to include pages that are blank.")) + menu.add_option(category_name,"incblank",blank) + + compress = BooleanOption(_('Co_mpress chart'),True) + compress.set_help(_("Whether to compress chart.")) + menu.add_option(category_name,"compress",compress) def make_default_style(self,default_style): """Make the default output style for the Ancestor Chart report.""" diff --git a/src/plugins/DescendChart.py b/src/plugins/DescendChart.py index 366fe58fd..b41904223 100644 --- a/src/plugins/DescendChart.py +++ b/src/plugins/DescendChart.py @@ -28,29 +28,23 @@ # python modules # #------------------------------------------------------------------------ -import math +from BasicUtils import name_displayer +from PluginUtils import register_report +from ReportBase import Report, ReportOptions, ReportUtils, \ + MenuOptions, NumberOption, BooleanOption, TextOption, \ + CATEGORY_DRAW, MODE_GUI, MODE_BKI, MODE_CLI +from SubstKeywords import SubstKeywords from gettext import gettext as _ - -#------------------------------------------------------------------------ -# -# gtk -# -#------------------------------------------------------------------------ -import gtk +import BaseDoc +import math #------------------------------------------------------------------------ # # GRAMPS modules # #------------------------------------------------------------------------ -import BaseDoc -from PluginUtils import register_report -from ReportBase import Report, ReportOptions, ReportUtils, \ - CATEGORY_DRAW, MODE_GUI, MODE_BKI, MODE_CLI pt2cm = ReportUtils.pt2cm cm2pt = ReportUtils.cm2pt -from SubstKeywords import SubstKeywords -from BasicUtils import name_displayer #------------------------------------------------------------------------ # @@ -174,7 +168,7 @@ class DescendChart(Report): em = self.doc.string_width(font,"m") subst = SubstKeywords(self.database,person_handle) - self.text[(x,y)] = subst.replace_and_clean(self.display.split('\n')) + self.text[(x,y)] = subst.replace_and_clean(self.display) for line in self.text[(x,y)]: this_box_width = self.doc.string_width(font,line) + 2*em self.box_width = max(self.box_width,this_box_width) @@ -394,76 +388,39 @@ class DescendChart(Report): # # #------------------------------------------------------------------------ -class DescendChartOptions(ReportOptions): +class DescendChartOptions(MenuOptions): """ Defines options and provides handling interface. """ def __init__(self,name,person_id=None): - ReportOptions.__init__(self,name,person_id) - - def set_new_options(self): - # Options specific for this report - self.options_dict = { - 'dispf' : "$n\n%s $b\n%s $d" % (_BORN,_DIED), - 'maxgen' : 32, - 'singlep' : 1, - 'incblank' : 1, - 'incblank' : 1, - } - self.options_help = { - 'dispf' : ("=str","Display format for the outputbox.", - "Allows you to customize the data in the boxes in the report", - True), - 'maxgen' : ("=int","Generations", - "The number of generations to include in the report", - True), - 'singlep' : ("=0/1","Whether to scale to fit on a single page.", - ["Do not scale to fit","Scale to fit"], - True), - 'incblank' : ("=0/1","Whether to include pages that are blank.", - ["Do not include blank pages","Include blank pages"], - True), - } - - def add_user_options(self,dialog): - """ - Override the base class add_user_options task to add a menu that allows - the user to select the sort method. - """ - self.max_gen = gtk.SpinButton(gtk.Adjustment(1,1,100,1)) - self.max_gen.set_value(self.options_dict['maxgen']) - dialog.add_option(_('Generations'),self.max_gen) + MenuOptions.__init__(self,name,person_id) - self.extra_textbox = gtk.TextView() - self.extra_textbox.get_buffer().set_text(self.options_dict['dispf']) - self.extra_textbox.set_editable(1) - swin = gtk.ScrolledWindow() - swin.set_shadow_type(gtk.SHADOW_IN) - swin.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC) - swin.add(self.extra_textbox) - dialog.add_option(_("Display Format"),swin) - - self.scale = gtk.CheckButton(_('Sc_ale to fit on a single page')) - self.scale.set_active(self.options_dict['singlep']) - dialog.add_option('',self.scale) + def add_menu_options(self,menu): + category_name = _("Report Options") + + max_gen = NumberOption(_("Generations"),10,1,50) + max_gen.set_help(_("The number of generations to include in the report")) + menu.add_option(category_name,"maxgen",max_gen) + + disp = TextOption( _("Display Format"), + ["$n","%s $b" % _BORN,"%s $d" %_DIED] ) + disp.set_help(_("Display format for the outputbox.")) + menu.add_option(category_name,"dispf",disp) + + scale = BooleanOption(_('Sc_ale to fit on a single page'),True) + scale.set_help(_("Whether to scale to fit on a single page.")) + menu.add_option(category_name,"singlep",scale) + + blank = BooleanOption(_('Include Blank Pages'),True) + blank.set_help(_("Whether to include pages that are blank.")) + menu.add_option(category_name,"incblank",blank) + + compress = BooleanOption(_('Co_mpress chart'),True) + compress.set_help(_("Whether to compress chart.")) + menu.add_option(category_name,"compress",compress) - self.blank = gtk.CheckButton(_('Include Blank Pages')) - self.blank.set_active(self.options_dict['incblank']) - dialog.add_option('',self.blank) - - def parse_user_options(self,dialog): - """ - Parses the custom options that we have added. - """ - b = self.extra_textbox.get_buffer() - text_val = unicode(b.get_text(b.get_start_iter(),b.get_end_iter(),False)) - self.options_dict['dispf'] = text_val - self.options_dict['maxgen'] = int(self.max_gen.get_value_as_int()) - self.options_dict['singlep'] = int(self.scale.get_active ()) - self.options_dict['incblank'] = int(self.blank.get_active()) - def make_default_style(self,default_style): """Make the default output style for the Ancestor Chart report.""" ## Paragraph Styles: @@ -507,7 +464,7 @@ class DescendChartOptions(ReportOptions): # #------------------------------------------------------------------------ register_report( - name = 'descend_chart2', + name = 'descend_chart', category = CATEGORY_DRAW, report_class = DescendChart, options_class = DescendChartOptions, diff --git a/src/plugins/FanChart.py b/src/plugins/FanChart.py index 44d5880cc..1bb781f84 100644 --- a/src/plugins/FanChart.py +++ b/src/plugins/FanChart.py @@ -28,13 +28,6 @@ #------------------------------------------------------------------------ from gettext import gettext as _ -#------------------------------------------------------------------------ -# -# gnome/gtk -# -#------------------------------------------------------------------------ -import gtk - #------------------------------------------------------------------------ # # gramps modules @@ -43,8 +36,25 @@ import gtk import BaseDoc from PluginUtils import register_report from ReportBase import Report, ReportUtils, ReportOptions, \ + MenuOptions, NumberOption, EnumeratedListOption, \ CATEGORY_DRAW, MODE_GUI, MODE_BKI, MODE_CLI from SubstKeywords import SubstKeywords + +#------------------------------------------------------------------------ +# +# private constants +# +#------------------------------------------------------------------------ +FULL_CIRCLE = 0 +HALF_CIRCLE = 1 +QUAR_CIRCLE = 2 + +BACKGROUND_WHITE = 0 +BACKGROUND_GEN = 1 + +RADIAL_UPRIGHT = 0 +RADIAL_ROUNDABOUT = 1 + pt2cm = ReportUtils.pt2cm #------------------------------------------------------------------------ @@ -68,24 +78,20 @@ class FanChart(Report): that come in the options class. maxgen - Maximum number of generations to include. - full_circle - != 0: draw a full circle; half_circle and quar_circle should be 0. - half_circle - != 0: draw a half circle; full_circle and quar_circle should be 0. - quar_circle - != 0: draw a quarter circle; full_circle and half_circle should be 0. - backgr_white - 0: Background color is generation dependent; 1: Background color is white. - radial_upright - 0: Print radial texts roundabout; 1: Print radial texts as upright as possible. + circle - Draw a full circle, half circle, or quarter circle. + background - Background color is generation dependent or white. + radial - Print radial texts roundabout or as upright as possible. """ self.max_generations = options_class.handler.options_dict['maxgen'] - self.full_circle = options_class.handler.options_dict['full_circle'] - self.half_circle = options_class.handler.options_dict['half_circle'] - self.quar_circle = options_class.handler.options_dict['quar_circle'] - self.backgr_white = options_class.handler.options_dict['backgr_white'] - self.radial_upright = options_class.handler.options_dict['radial_upright'] + self.circle = options_class.handler.options_dict['circle'] + self.background = options_class.handler.options_dict['background'] + self.radial = options_class.handler.options_dict['radial'] self.background_style = [] self.text_style = [] for i in range (0, self.max_generations): - if self.backgr_white: + if self.background == BACKGROUND_WHITE: background_style_name = 'background_style_white' else: background_style_name = 'background_style' + '%d' % i @@ -140,7 +146,7 @@ class FanChart(Report): self.apply_filter(self.start_person.get_handle(),1) n = self.start_person.get_primary_name().get_regular_name() - if self.full_circle: + if self.circle == FULL_CIRCLE: max_angle = 360.0 start_angle = 90 max_circular = 5 @@ -148,7 +154,7 @@ class FanChart(Report): y = self.doc.get_usable_height() / 2.0 min_xy = min (x, y) - elif self.half_circle: + elif self.circle == HALF_CIRCLE: max_angle = 180.0 start_angle = 180 max_circular = 3 @@ -216,9 +222,9 @@ class FanChart(Report): else: name = pn.get_first_name() + pn.get_surname() - if self.full_circle: + if self.circle == FULL_CIRCLE: return [ name, val ] - elif self.half_circle: + elif self.circle == HALF_CIRCLE: return [ name, val ] else: if (name != "") and (val != ""): @@ -227,9 +233,9 @@ class FanChart(Report): string = name + val return [string] elif generation == 6: - if self.full_circle: + if self.circle == FULL_CIRCLE: return [ pn.get_first_name(), pn.get_surname(), val ] - elif self.half_circle: + elif self.circle == HALF_CIRCLE: return [ pn.get_first_name(), pn.get_surname(), val ] else: if (pn.get_first_name() != "") and (pn.get_surname() != ""): @@ -257,7 +263,7 @@ class FanChart(Report): (xc,yc) = ReportUtils.draw_wedge(self.doc,background_style, x, y, rad2, start_angle, end_angle, rad1) if self.map[index]: - if (generation == 0) and self.full_circle: + if (generation == 0) and self.circle == FULL_CIRCLE: yc = y self.doc.rotate_text(text_style, self.get_info(self.map[index], @@ -273,10 +279,10 @@ class FanChart(Report): text_angle = start_angle - delta / 2.0 background_style = self.background_style[generation] text_style = self.text_style[generation] - if self.full_circle: + if self.circle == FULL_CIRCLE: rad1 = size * ((generation * 2) - 5) rad2 = size * ((generation * 2) - 3) - elif self.half_circle: + elif self.circle == HALF_CIRCLE: rad1 = size * ((generation * 2) - 3) rad2 = size * ((generation * 2) - 1) else: # quarter circle @@ -290,7 +296,7 @@ class FanChart(Report): start_angle, end_angle, rad1) text_angle += delta if self.map[index]: - if self.radial_upright and (start_angle >= 90) and (start_angle < 270): + if self.radial == RADIAL_UPRIGHT and (start_angle >= 90) and (start_angle < 270): self.doc.rotate_text(text_style, self.get_info(self.map[index], generation), @@ -306,95 +312,41 @@ class FanChart(Report): # # #------------------------------------------------------------------------ -class FanChartOptions(ReportOptions): - - """ - Defines options and provides handling interface for Fan Chart. - """ +class FanChartOptions(MenuOptions): def __init__(self,name,person_id=None): - ReportOptions.__init__(self,name,person_id) - self.MAX_GENERATIONS = 8 - - def set_new_options(self): - # Options specific for this report - self.options_dict = { - 'maxgen' : 5, - 'full_circle' : 0, - 'half_circle' : 1, - 'quar_circle' : 0, - 'backgr_white' : 0, - 'backgr_generation' : 1, - 'radial_upright' : 1, - 'radial_roundabout' : 0, - } - self.options_help = { - 'maxgen' : ("=num","Number of generations to print.", - [], - True), - 'full_circle': ("=0/1","The form of the diagram shall be a full circle.", - ["half or quarter circle","full circle"], - True), - 'half_circle': ("=0/1","The form of the diagram shall be a half circle.", - ["full or quarter circle","half circle"], - True), - 'quar_circle': ("=0/1","The form of the diagram shall be a quarter circle.", - ["full or half circle","quarter circle"], - True), - 'backgr_white': ("=0/1","Background color is white.", - ["generation dependent","white"], - True), - 'backgr_generation': ("=0/1","Background color is generation dependent.", - ["white","generation dependent"], - True), - 'radial_upright': ("=0/1","Print radial texts as upright as possible.", - ["roundabout","upright"], - True), - 'radial_roundabout': ("=0/1","Print radial texts roundabout.", - ["upright","roundabout"], - True), - } - - def add_user_options(self,dialog): - """ - Override the base class add_user_options task to add a menu that allows - the user to select the number of generations to print, .... - """ - self.max_gen = gtk.SpinButton(gtk.Adjustment(5,2,self.MAX_GENERATIONS,1)) - self.max_gen.set_value(self.options_dict['maxgen']) - self.max_gen.set_wrap(True) - dialog.add_option(_('Generations'),self.max_gen) - self.type_box = gtk.combo_box_new_text () - self.type_box.append_text (_('full circle')) - self.type_box.append_text (_('half circle')) - self.type_box.append_text (_('quarter circle')) - self.type_box.set_active(self.options_dict['half_circle'] + 2 * self.options_dict['quar_circle']) - dialog.add_option(_('Type of graph'),self.type_box) - self.backgr_box = gtk.combo_box_new_text () - self.backgr_box.append_text (_('white')) - self.backgr_box.append_text (_('generation dependent')) - self.backgr_box.set_active(self.options_dict['backgr_generation']) - dialog.add_option(_('Background color'),self.backgr_box) - self.radial_box = gtk.combo_box_new_text () - self.radial_box.append_text (_('upright')) - self.radial_box.append_text (_('roundabout')) - self.radial_box.set_active(self.options_dict['radial_roundabout']) - dialog.add_option(_('Orientation of radial texts'),self.radial_box) - - def parse_user_options(self,dialog): - """ - Parses the custom options that we have added. - """ - self.options_dict['maxgen'] = int(self.max_gen.get_value_as_int()) - self.options_dict['full_circle'] = int(self.type_box.get_active() == 0) - self.options_dict['half_circle'] = int(self.type_box.get_active() == 1) - self.options_dict['quar_circle'] = int(self.type_box.get_active() == 2) - self.options_dict['backgr_white'] = int(self.backgr_box.get_active() == 0) - self.options_dict['backgr_generation'] = int(self.backgr_box.get_active() == 1) - self.options_dict['radial_upright'] = int(self.radial_box.get_active() == 0) - self.options_dict['radial_roundabout'] = int(self.radial_box.get_active() == 1) - + + MenuOptions.__init__(self,name,person_id) + + def add_menu_options(self,menu): + category_name = _("Report Options") + + max_gen = NumberOption(_("Generations"),5,1,self.MAX_GENERATIONS) + max_gen.set_help(_("The number of generations to include in the report")) + menu.add_option(category_name,"maxgen",max_gen) + + circle = EnumeratedListOption(_('Type of graph'),HALF_CIRCLE) + circle.add_item(FULL_CIRCLE,_('full circle')) + circle.add_item(HALF_CIRCLE,_('half circle')) + circle.add_item(QUAR_CIRCLE,_('quarter circle')) + circle.set_help( _("The form of the graph: full circle, half circle," + " or quarter circle.")) + menu.add_option(category_name,"circle",circle) + + background = EnumeratedListOption(_('Background color'),BACKGROUND_GEN) + background.add_item(BACKGROUND_WHITE,_('white')) + background.add_item(BACKGROUND_GEN,_('generation dependent')) + background.set_help(_("Background color is either white or generation" + " dependent")) + menu.add_option(category_name,"background",background) + + radial = EnumeratedListOption( _('Orientation of radial texts'), + RADIAL_UPRIGHT ) + radial.add_item(RADIAL_UPRIGHT,_('upright')) + radial.add_item(RADIAL_ROUNDABOUT,_('roundabout')) + radial.set_help(_("Print raidal texts upright or roundabout")) + menu.add_option(category_name,"radial",radial) def make_default_style(self,default_style): """Make the default output style for the Fan Chart report."""