From 07e1529f2799cf8d7d112c805b4a58be775f0ff7 Mon Sep 17 00:00:00 2001 From: Brian Matherly Date: Fri, 18 Jan 2008 05:39:50 +0000 Subject: [PATCH] Refactoring the report system. Decouple MenuOptions from the code that displays them. (Book Report is broken and needs to be fixed.) svn: r9875 --- ChangeLog | 34 + po/POTFILES.in | 2 + src/Filters/Rules/Family/Makefile.am | 1 + src/Filters/Rules/Family/_IsBookmarked.py | 53 ++ src/Filters/Rules/Family/__init__.py | 2 + src/PluginUtils/Makefile.am | 1 + src/PluginUtils/_GuiOptions.py | 969 ++++++++++++++++++++++ src/PluginUtils/_MenuOptions.py | 885 +++----------------- src/PluginUtils/__init__.py | 13 +- src/ReportBase/_GraphvizReportDialog.py | 43 +- src/ReportBase/_ReportOptions.py | 6 +- src/plugins/AncestorChart.py | 11 +- src/plugins/AncestorReport.py | 11 +- src/plugins/BookReport.py | 10 +- src/plugins/CalculateEstimatedDates.py | 43 +- src/plugins/Calendar.py | 55 +- src/plugins/DescendChart.py | 11 +- src/plugins/DescendReport.py | 11 +- src/plugins/DetAncestralReport.py | 11 +- src/plugins/DetDescendantReport.py | 11 +- src/plugins/EndOfLineReport.py | 9 +- src/plugins/FamilyGroup.py | 62 +- src/plugins/FanChart.py | 13 +- src/plugins/GVFamilyLines.py | 18 +- src/plugins/GVHourGlass.py | 11 +- src/plugins/GVRelGraph.py | 117 ++- src/plugins/IndivComplete.py | 52 +- src/plugins/KinshipReport.py | 11 +- src/plugins/MarkerReport.py | 5 +- src/plugins/StatisticsChart.py | 47 +- src/plugins/TimeLine.py | 44 +- 31 files changed, 1568 insertions(+), 1004 deletions(-) create mode 100644 src/Filters/Rules/Family/_IsBookmarked.py create mode 100644 src/PluginUtils/_GuiOptions.py diff --git a/ChangeLog b/ChangeLog index 03d336e9c..a58528156 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,37 @@ +2008-01-17 Brian Matherly + * src/ReportBase/_GraphvizReportDialog.py: + * src/ReportBase/_ReportOptions.py: + * src/plugins/KinshipReport.py: + * src/plugins/DetDescendantReport.py: + * src/plugins/DescendReport.py: + * src/plugins/IndivComplete.py: + * src/plugins/CalculateEstimatedDates.py: + * src/plugins/BookReport.py: + * src/plugins/TimeLine.py: + * src/plugins/GVFamilyLines.py: + * src/plugins/Calendar.py: + * src/plugins/AncestorReport.py: + * src/plugins/MarkerReport.py: + * src/plugins/DescendChart.py: + * src/plugins/EndOfLineReport.py: + * src/plugins/AncestorChart.py: + * src/plugins/DetAncestralReport.py: + * src/plugins/FamilyGroup.py: + * src/plugins/GVRelGraph.py: + * src/plugins/GVHourGlass.py: + * src/plugins/StatisticsChart.py: + * src/plugins/FanChart.py: + * src/PluginUtils/__init__.py: + * src/PluginUtils/_MenuOptions.py: + * src/PluginUtils/_GuiOptions.py: Added + * src/PluginUtils/Makefile.am: + * src/Filters/Rules/Family/__init__.py: + * src/Filters/Rules/Family/_IsBookmarked.py: Added + * src/Filters/Rules/Family/Makefile.am: + * po/POTFILES.in: + Refactoring the report system. Decouple MenuOptions from the code that + displays them. (Book Report is broken and needs to be fixed.) + 2008-01-17 Douglas S. Blank * src/DataViews/GrampletView.py (Gramplet.link): added size, tooltip to links diff --git a/po/POTFILES.in b/po/POTFILES.in index ff36a9664..6549b448d 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -330,6 +330,7 @@ src/plugins/ReadGrdb.py # PluginUtils package src/PluginUtils/__init__.py +src/PluginUtils/GuiOptions.py src/PluginUtils/_Options.py src/PluginUtils/_PluginMgr.py src/PluginUtils/_Plugins.py @@ -541,6 +542,7 @@ src/Filters/Rules/Family/_HasNoteMatchingSubstringOf.py src/Filters/Rules/Family/_HasNoteRegexp.py src/Filters/Rules/Family/_HasReferenceCountOf.py src/Filters/Rules/Family/_HasRelType.py +src/Filters/Rules/Family/_IsBookmarked.py src/Filters/Rules/Family/_RegExpIdOf.py src/Filters/Rules/Family/_MatchesFilter.py src/Filters/Rules/Family/_MotherHasIdOf.py diff --git a/src/Filters/Rules/Family/Makefile.am b/src/Filters/Rules/Family/Makefile.am index e3093c80e..180e9a542 100644 --- a/src/Filters/Rules/Family/Makefile.am +++ b/src/Filters/Rules/Family/Makefile.am @@ -13,6 +13,7 @@ pkgdata_PYTHON = \ _HasReferenceCountOf.py\ _HasRelType.py\ __init__.py\ + _IsBookmarked.py\ _RegExpIdOf.py\ _MatchesFilter.py\ _FatherHasNameOf.py\ diff --git a/src/Filters/Rules/Family/_IsBookmarked.py b/src/Filters/Rules/Family/_IsBookmarked.py new file mode 100644 index 000000000..3b5c3cb71 --- /dev/null +++ b/src/Filters/Rules/Family/_IsBookmarked.py @@ -0,0 +1,53 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2008 Brian 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: $ + +#------------------------------------------------------------------------- +# +# Standard Python modules +# +#------------------------------------------------------------------------- +from gettext import gettext as _ + +#------------------------------------------------------------------------- +# +# GRAMPS modules +# +#------------------------------------------------------------------------- +from Filters.Rules._Rule import Rule + +#------------------------------------------------------------------------- +# +# IsBookmarked +# +#------------------------------------------------------------------------- +class IsBookmarked(Rule): + """Rule that checks for the bookmark list in the database""" + + name = _('Bookmarked families') + category = _('General filters') + description = _("Matches the families on the bookmark list") + + def prepare(self, db): + self.bookmarks = db.get_family_bookmarks().get() + + def apply(self, db, family): + return family.get_handle() in self.bookmarks diff --git a/src/Filters/Rules/Family/__init__.py b/src/Filters/Rules/Family/__init__.py index 82b1bffb3..031f8372b 100644 --- a/src/Filters/Rules/Family/__init__.py +++ b/src/Filters/Rules/Family/__init__.py @@ -44,6 +44,7 @@ from _FamilyPrivate import FamilyPrivate from _HasAttribute import HasAttribute from _HasEvent import HasEvent from _HasMarkerOf import HasMarkerOf +from _IsBookmarked import IsBookmarked from _MatchesFilter import MatchesFilter from _FatherHasNameOf import FatherHasNameOf from _FatherHasIdOf import FatherHasIdOf @@ -64,6 +65,7 @@ editor_rule_list = [ HasEvent, HasAttribute, HasMarkerOf, + IsBookmarked, MatchesFilter, FatherHasNameOf, FatherHasIdOf, diff --git a/src/PluginUtils/Makefile.am b/src/PluginUtils/Makefile.am index 4ee03e824..5e9fb0b9f 100644 --- a/src/PluginUtils/Makefile.am +++ b/src/PluginUtils/Makefile.am @@ -7,6 +7,7 @@ pkgdatadir = $(datadir)/@PACKAGE@/PluginUtils pkgdata_PYTHON = \ __init__.py\ + _GuiOptions.py\ _MenuOptions.py\ _Options.py\ _Tool.py\ diff --git a/src/PluginUtils/_GuiOptions.py b/src/PluginUtils/_GuiOptions.py new file mode 100644 index 000000000..9920a29f1 --- /dev/null +++ b/src/PluginUtils/_GuiOptions.py @@ -0,0 +1,969 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2007-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: $ + +""" +Specific option handling for a GUI. +""" +#------------------------------------------------------------------------ +# +# python modules +# +#------------------------------------------------------------------------ +from gettext import gettext as _ + +#------------------------------------------------------------------------- +# +# gramps modules +# +#------------------------------------------------------------------------- +import gtk +import gobject +import Utils +import GrampsWidgets +import ManagedWindow +from QuestionDialog import OptionDialog +from Selectors import selector_factory +from BasicUtils import name_displayer as _nd +from Filters import GenericFilter, Rules +import _MenuOptions + +#------------------------------------------------------------------------ +# +# 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 = Utils.ProgressMeter(_('Finding surnames')) + progress.set_pass(_('Finding surnames'), + database.get_number_of_people()) + for person_handle in database.get_person_handles(False): + progress.step() + person = database.get_person_from_handle(person_handle) + 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, tooltip): + """ + @param option: The option to display. + @type option: MenuOption.StringOption + @return: nothing + """ + gtk.Entry.__init__(self) + self.__option = option + self.set_text( self.__option.get_value() ) + self.connect('changed', self.__text_changed) + tooltip.set_tip(self, self.__option.get_help()) + + def __text_changed(self, obj): # IGNORE:W0613 - obj is unused + """ + Handle the change of the value. + """ + self.__option.set_value( self.__entry.get_text() ) + +#------------------------------------------------------------------------- +# +# GuiColourOption class +# +#------------------------------------------------------------------------- +class GuiColourOption(gtk.ColorButton): + """ + This class displays an option that allows the selection of a colour. + """ + def __init__(self, option, dbstate, uistate, track, tooltip): + self.__option = option + value = self.__option.get_value() + gtk.ColorButton.__init__( self, gtk.gdk.color_parse(value) ) + self.connect('color-set', self.__value_changed) + tooltip.set_tip(self, 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, tooltip): + 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) + + self.set_value(self.__option.get_value()) + self.connect('changed', self.__value_changed) + tooltip.set_tip(self, self.__option.get_help()) + + def __value_changed(self, obj): # IGNORE:W0613 - obj is unused + """ + Handle the change of the value. + """ + self.__option.set_value( int(self.get_value_as_int()) ) + +#------------------------------------------------------------------------- +# +# GuiTextOption class +# +#------------------------------------------------------------------------- +class GuiTextOption(gtk.ScrolledWindow): + """ + This class displays an option that is a multi-line string. + """ + def __init__(self, option, dbstate, uistate, track, tooltip): + 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) + tooltip.set_tip(gtext, 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, tooltip): + self.__option = option + gtk.CheckButton.__init__(self, self.__option.get_label()) + self.set_active(self.__option.get_value()) + self.connect('toggled', self.__value_changed) + tooltip.set_tip(self, self.__option.get_help()) + + def __value_changed(self, obj): # IGNORE:W0613 - obj is unused + """ + Handle the change of the value. + """ + self.__option.set_value( self.get_active() ) + +#------------------------------------------------------------------------- +# +# GuiEnumeratedListOption class +# +#------------------------------------------------------------------------- +class GuiEnumeratedListOption(gtk.EventBox): + """ + 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, tooltip): + gtk.EventBox.__init__(self) + self.__option = option + self.__combo = gtk.combo_box_new_text() + self.add(self.__combo) + + self.__update_options() + + tooltip.set_tip(self, 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 ) + + 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, tooltip): + """ + @param option: The option to display. + @type option: MenuOption.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 = GrampsWidgets.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 = self.__db.get_person_from_gramps_id(self.__option.get_value()) + if not person: + person = self.__dbstate.get_active_person() + if not person: + person = self.__db.get_default_person() + self.__update_person(person) + + tooltip.set_tip(pevt, self.__option.get_help()) + tooltip.set_tip(person_button, _('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. + active_person = self.__dbstate.get_active_person() + if active_person: + gid = active_person.get_gramps_id() + rfilter.add_rule(Rules.Person.HasIdOf([gid])) + + select_class = selector_factory('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, tooltip): + """ + @param option: The option to display. + @type option: MenuOption.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 = GrampsWidgets.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) + + family = self.__db.get_family_from_gramps_id(self.__option.get_value()) + if not family: + person = self.__dbstate.get_active_person() + family_list = person.get_family_handle_list() + if not family_list: + person = self.__db.get_default_person() + family_list = person.get_family_handle_list() + family = self.__db.get_family_from_handle(family_list[0]) + self.__update_family(family) + + tooltip.set_tip(pevt, self.__option.get_help()) + tooltip.set_tip(family_button, _('Select a different family')) + + self.__option.connect('avail-changed', self.__update_avail) + self.__update_avail() + + 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 = GenericFilter() + 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 = selector_factory('Family') + sel = select_class(self.__dbstate, self.__uistate, self.__track, + filter=rfilter ) + family = sel.run() + self.__update_family(family) + + def __update_family(self, family): + """ + Update the currently selected family. + """ + if family: + 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) + +#------------------------------------------------------------------------- +# +# 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, tooltip): + """ + @param option: The option to display. + @type option: MenuOption.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 = GrampsWidgets.SimpleButton(gtk.STOCK_ADD, + self.__add_person_clicked) + self.__del_person = GrampsWidgets.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) + + tooltip.set_tip(self.__tree_view, 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 = selector_factory('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() + +#------------------------------------------------------------------------- +# +# GuiSurnameColourOption class +# +#------------------------------------------------------------------------- +class GuiSurnameColourOption(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, tooltip): + """ + @param option: The option to display. + @type option: MenuOption.SurnameColourOption + @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 = GrampsWidgets.SimpleButton(gtk.STOCK_ADD, + self.__add_clicked) + self.del_surname = GrampsWidgets.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 + tmp = self.__option.get_value().split() + while len(tmp) > 1: + surname = tmp.pop(0) + colour = tmp.pop(0) + self.__model.append([surname, colour]) + + tooltip.set_tip(self.__tree_view, 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 + surname_colours += surname + ' ' + colour + ' ' + 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() + +#------------------------------------------------------------------------ +# +# GuiMenuOptions class +# +#------------------------------------------------------------------------ +class GuiMenuOptions: + """ + Introduction + ============ + A GuiMenuOptions is used to implement the necessary funtions for adding + options to a GTK dialog. + """ + def __init__(self, dbstate): + self.menu = _MenuOptions.Menu() + self.__dbstate = dbstate + + # Fill options_dict with report/tool defaults: + self.options_dict = {} + self.options_help = {} + self.__tooltips = gtk.Tooltips() + self.add_menu_options(self.menu, dbstate) + 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, dbstate): + """ + 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]) + + found = True + label = True + if isinstance(option, _MenuOptions.PersonOption): + widget = GuiPersonOption(option, self.__dbstate, + dialog.uistate, dialog.track, + self.__tooltips) + elif isinstance(option, _MenuOptions.NumberOption): + widget = GuiNumberOption(option, self.__dbstate, + dialog.uistate, dialog.track, + self.__tooltips) + elif isinstance(option, _MenuOptions.BooleanOption): + widget = GuiBooleanOption(option, self.__dbstate, + dialog.uistate, dialog.track, + self.__tooltips) + label = False + elif isinstance(option, _MenuOptions.StringOption): + widget = GuiStringOption(option, self.__dbstate, + dialog.uistate, dialog.track, + self.__tooltips) + elif isinstance(option, _MenuOptions.EnumeratedListOption): + widget = GuiEnumeratedListOption(option, self.__dbstate, + dialog.uistate, dialog.track, + self.__tooltips) + elif isinstance(option, _MenuOptions.TextOption): + widget = GuiTextOption(option, self.__dbstate, + dialog.uistate, dialog.track, + self.__tooltips) + elif isinstance(option, _MenuOptions.FamilyOption): + widget = GuiFamilyOption(option, self.__dbstate, + dialog.uistate, dialog.track, + self.__tooltips) + elif isinstance(option, _MenuOptions.PersonListOption): + widget = GuiPersonListOption(option, self.__dbstate, + dialog.uistate, dialog.track, + self.__tooltips) + elif isinstance(option, _MenuOptions.ColourOption): + widget = GuiColourOption(option, self.__dbstate, + dialog.uistate, dialog.track, + self.__tooltips) + elif isinstance(option, _MenuOptions.SurnameColourOption): + widget = GuiSurnameColourOption(option, self.__dbstate, + dialog.uistate, dialog.track, + self.__tooltips) + else: + found = False + + if not found: + 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() + diff --git a/src/PluginUtils/_MenuOptions.py b/src/PluginUtils/_MenuOptions.py index 3203b0c15..c23223018 100644 --- a/src/PluginUtils/_MenuOptions.py +++ b/src/PluginUtils/_MenuOptions.py @@ -22,132 +22,28 @@ """ Abstracted option handling. """ -#------------------------------------------------------------------------ -# -# python modules -# -#------------------------------------------------------------------------ -from gettext import gettext as _ - #------------------------------------------------------------------------- # # gramps modules # #------------------------------------------------------------------------- -import gtk -import gobject -import Utils -import GrampsWidgets -import ManagedWindow -from Selectors import selector_factory -from BasicUtils import name_displayer as _nd - -#------------------------------------------------------------------------ -# -# 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()): - - self.title = _('Select surname') - 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, self.title) - 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 = Utils.ProgressMeter(_('Finding surnames')) - progress.set_pass(_('Finding surnames'), - database.get_number_of_people()) - for person_handle in database.get_person_handles(False): - progress.step() - person = database.get_person_from_handle(person_handle) - 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 +import gen.utils #------------------------------------------------------------------------- # # Option class # #------------------------------------------------------------------------- -class Option: +class Option(gen.utils.GrampsDBCallback): """ 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. """ + + __signals__ = { 'value-changed' : None, + 'avail-changed' : None} + def __init__(self, label, value): """ @param label: A friendly label to be applied to this option. @@ -158,9 +54,11 @@ class Option: @type value: The type will depend on the type of option. @return: nothing """ + gen.utils.GrampsDBCallback.__init__(self) self.__value = value self.__label = label self.__help_str = "" + self.__available = True def get_label(self): """ @@ -199,6 +97,7 @@ class Option: @return: nothing """ self.__value = value + self.emit('value-changed') def get_help(self): """ @@ -219,20 +118,31 @@ class Option: @return: nothing """ self.__help_str = help_text - - def add_dialog_category(self, dialog, category): - """ - Add the GUI object to the dialog on the appropriate tab. - """ - dialog.add_frame_option(category, self.get_label(), self.gobj) - - def add_tooltip(self, tooltip): - """ - Add the option's help to the GUI object. - """ - tooltip.set_tip(self.gobj, self.get_help()) + def set_available(self, avail): + """ + Set the availability of this option. + @param avail: An indicator of whether this option is currently + available. True indicates that the option is available. False indicates + that the option is not available. + @type avail: Bool + @return: nothing + """ + if avail != self.__available: + self.__available = avail + self.emit('avail-changed') + + def get_available(self): + """ + Get the availability of this option. + + @return: A Bool indicating the availablity of this option. + True indicates that the option is available. + False indicates that the option is not available. + """ + return self.__available + #------------------------------------------------------------------------- # # StringOption class @@ -254,26 +164,12 @@ class StringOption(Option): """ Option.__init__(self, label, value) - def make_gui_obj(self, dialog): - """ - Add a StringOption (single line text) to the dialog. - """ - value = self.get_value() - self.gobj = gtk.Entry() - self.gobj.set_text(value) - - def parse(self): - """ - Parse the string option (single line text). - """ - return self.gobj.get_text() - #------------------------------------------------------------------------- # -# ColourButtonOption class +# ColourOption class # #------------------------------------------------------------------------- -class ColourButtonOption(Option): +class ColourOption(Option): """ This class describes an option that allows the selection of a colour. """ @@ -284,29 +180,11 @@ class ColourButtonOption(Option): @type label: string @param value: An initial value for this option. Example: "#ff00a0" - @type value: string, interpreted as a colour by gtk.gdk.color_parse() + @type value: string @return: nothing """ Option.__init__(self, label, value) - def make_gui_obj(self, dialog): - """ - Add a ColorButton to the dialog. - """ - value = self.get_value() - self.gobj = gtk.ColorButton(gtk.gdk.color_parse(value)) - - def parse(self): - """ - Parse the colour and return as a string. - """ - colour = self.gobj.get_color() - value = '#%02x%02x%02x' % ( - int(colour.red * 256 / 65536), - int(colour.green * 256 / 65536), - int(colour.blue * 256 / 65536)) - return value - #------------------------------------------------------------------------- # # NumberOption class @@ -317,7 +195,7 @@ 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_val, max_val): + def __init__(self, label, value, min_val, max_val, step = 1): """ @param label: A friendly label to be applied to this option. Example: "Number of generations to include" @@ -331,11 +209,15 @@ class NumberOption(Option): @param max: The maximum value for this option. Example: 10 @type value: int + @param step: The step size for this option. + Example: 0.01 + @type value: int or float @return: nothing """ Option.__init__(self, label, value) self.__min = min_val self.__max = max_val + self.__step = step def get_min(self): """ @@ -352,59 +234,14 @@ class NumberOption(Option): @return: an int that represents the maximum value for this option. """ return self.__max - - def make_gui_obj(self, dialog): + + def get_step(self): """ - Add a NumberOption to the dialog. + Get the step size for this option. + + @return: an int that represents the step size for this option. """ - value = self.get_value() - adj = gtk.Adjustment(1, self.get_min(), self.get_max(), 1) - - self.gobj = gtk.SpinButton(adj) - self.gobj.set_value(value) - - def parse(self): - """ - Parse the object and return. - """ - return int(self.gobj.get_value_as_int()) - - -#------------------------------------------------------------------------- -# -# FloatOption class -# -#------------------------------------------------------------------------- -class FloatOption(NumberOption): - """ - This class performs like NumberOption, but allows for float values - for the minimum/maximum/increment. - """ - def __init__(self, label, value, min_val, max_val): - # Someone who knows python better than I will probably - # want to add a parameter for the caller to specify how - # many decimal points are needed. - # - # At the time this function was written, the only code - # that needed this class required 2 decimals. - NumberOption.__init__(self, label, value, min_val, max_val) - - def make_gui_obj(self, dialog): - """ - Add a FloatOption to the dialog. - """ - value = self.get_value() - adj = gtk.Adjustment(value, lower=self.get_min(), - upper=self.get_max(), step_incr=0.01) - - self.gobj = gtk.SpinButton(adjustment=adj, digits=2) - self.gobj.set_value(value) - - def parse(self): - """ - Parse the object and return. - """ - return float(self.gobj.get_value()) + return self.__step #------------------------------------------------------------------------- # @@ -426,32 +263,6 @@ class TextOption(Option): @return: nothing """ Option.__init__(self, label, value) - - def make_gui_obj(self, dialog): - """ - Add a TextOption to the dialog. - """ - value = self.get_value() - self.gtext = gtk.TextView() - self.gtext.get_buffer().set_text("\n".join(value)) - self.gtext.set_editable(1) - self.gobj = gtk.ScrolledWindow() - self.gobj.set_shadow_type(gtk.SHADOW_IN) - self.gobj.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC) - self.gobj.add(self.gtext) - # Required for tooltip - self.gobj.add_events(gtk.gdk.ENTER_NOTIFY_MASK) - self.gobj.add_events(gtk.gdk.LEAVE_NOTIFY_MASK) - - def parse(self): - """ - Parse the text option (multi-line text). - """ - buff = self.gtext.get_buffer() - text_val = unicode( buff.get_text( buff.get_start_iter(), - buff.get_end_iter(), - False) ) - return text_val.split('\n') #------------------------------------------------------------------------- # @@ -473,21 +284,6 @@ class BooleanOption(Option): @return: nothing """ Option.__init__(self, label, value) - - def make_gui_obj(self, dialog): - """ - Add a BooleanOption to the dialog. - """ - value = self.get_value() - self.gobj = gtk.CheckButton(self.get_label()) - self.set_label("") - self.gobj.set_active(value) - - def parse(self): - """ - Parse the object and return. - """ - return self.gobj.get_active() #------------------------------------------------------------------------- # @@ -499,6 +295,9 @@ 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. """ + + __signals__ = { 'options-changed' : None } + def __init__(self, label, value): """ @param label: A friendly label to be applied to this option. @@ -525,6 +324,19 @@ class EnumeratedListOption(Option): @return: nothing """ self.__items.append((value, description)) + self.emit('options-changed') + + def set_items(self, items): + """ + Add a list of items to the list of possible values. + + @param items: A list of tuples containing value, description pairs. + Example: [ (5,"8.5 x 11"), (6,"11 x 17")] + @type items: array + @return: nothing + """ + self.__items = items + self.emit('options-changed') def get_items(self): """ @@ -533,133 +345,68 @@ class EnumeratedListOption(Option): @return: an array of tuples containing (value,description) pairs. """ return self.__items - - def make_gui_obj(self, dialog): + + def clear(self): """ - Add an EnumeratedListOption to the dialog. + Clear all possible values from this option. + + @return: nothing. """ - cur_val = self.get_value() - active_index = 0 - current_index = 0 - self.combo = gtk.combo_box_new_text() - self.gobj = gtk.EventBox() - self.gobj.add(self.combo) - for (value, description) in self.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 parse(self): - """ - Parse the EnumeratedListOption and return. - """ - index = self.combo.get_active() - items = self.get_items() - value, description = items[index] - return value + self.__items = [] + self.emit('options-changed') #------------------------------------------------------------------------- # # PersonFilterOption class # #------------------------------------------------------------------------- -class PersonFilterOption(Option): +class PersonFilterOption(EnumeratedListOption): """ This class describes an option that provides a list of person filters. Each possible value represents one of the possible filters. """ - def __init__(self, label, dbstate, value=0, include_single=True): + def __init__(self, label, value): """ @param label: A friendly label to be applied to this option. Example: "Filter" @type label: string - @param dbstate: A DbState instance - @type dbstate: DbState @param value: A default value for the option. Example: 1 @type label: int - @param include_single: Whether a filter should be included for a - single person. - @type include_single: bool @return: nothing """ - from ReportBase import ReportUtils - Option.__init__(self, label, value) - self.__dbstate = dbstate - self.__include_single = include_single - self.__person = self.__dbstate.get_active_person() - self.__filters = ReportUtils.get_person_filters(self.__person, - self.__include_single) - if self.get_value() > len(self.__filters): - self.set_value(0) + EnumeratedListOption.__init__(self, label, value) + self.__filters = [] - def get_center_person(self): + def set_filters(self, filter_list): """ - Get the person for whom the filters have been generated. + Set the list of filters available to be chosen from. + + @param filter_list: An array of person filters. + @type filter_list: array + @return: nothing """ - return self.__person + items = [] + curval = self.get_value() + + value = 0 + for filt in filter_list: + items.append((value, filt.get_name())) + value += 1 + + self.__filters = filter_list + self.clear() + self.set_items( items ) + + self.set_value(curval) def get_filter(self): """ Return the currently selected filter object. + + @return: A person filter object. """ return self.__filters[self.get_value()] - - def make_gui_obj(self, dialog): - """ - Add an PersonFilterOption to the dialog. - """ - self.dialog = dialog - self.combo = gtk.combo_box_new_text() - self.combo.connect('changed', self.__on_value_changed) - self.gobj = gtk.HBox() - self.change_button = gtk.Button("%s..." % _('C_hange') ) - self.change_button.connect('clicked', self.on_change_clicked) - self.gobj.pack_start(self.combo, False) - self.gobj.pack_start(self.change_button, False) - - self.update_gui_obj() - - def __on_value_changed(self, obj): - """ - Handle the change of the value. - """ - self.set_value( int(self.combo.get_active()) ) - - def on_change_clicked(self, *obj): - """ - Handle the "Change..." button press. - """ - select_class = selector_factory('Person') - sel_person = select_class(self.dialog.dbstate, - self.dialog.uistate, - self.dialog.track) - new_person = sel_person.run() - if new_person: - self.__person = new_person - self.update_gui_obj() - - def update_gui_obj(self): - # update the gui object with new filter info - from ReportBase import ReportUtils - self.combo.get_model().clear() - self.__filters = ReportUtils.get_person_filters(self.__person, - self.__include_single) - for filt in self.__filters: - self.combo.append_text(filt.get_name()) - - if self.get_value() >= len(self.__filters): - # Set the value to zero if it is not valid. - self.set_value(0) - self.combo.set_active(self.get_value()) - - def parse(self): - """ - Parse the object and return. - """ - return self.get_value() #------------------------------------------------------------------------- # @@ -671,7 +418,7 @@ class PersonOption(Option): This class describes an option that allows a person from the database to be selected. """ - def __init__(self, label, value, dbstate): + def __init__(self, label): """ @param label: A friendly label to be applied to this option. Example: "Center Person" @@ -679,81 +426,33 @@ class PersonOption(Option): @param value: A default Gramps ID of a person for this option. Example: "p11" @type value: string + @return: nothing + """ + Option.__init__(self, label, "") + +#------------------------------------------------------------------------- +# +# FamilyOption class +# +#------------------------------------------------------------------------- +class FamilyOption(Option): + """ + This class describes an option that allows a family from the + database to be selected. + """ + def __init__(self, label): + """ + @param label: A friendly label to be applied to this option. + Example: "Center Family" + @type label: string + @param value: A default Gramps ID of a family for this option. + Example: "f11" + @type value: string @param dbstate: The database state for the database to be used.. @type value: DbState @return: nothing """ - self.__dbstate = dbstate - self.__db = dbstate.get_database() - Option.__init__(self, label, value) - - def make_gui_obj(self, dialog): - self.dialog = dialog - self.gobj = gtk.HBox() - self.person_label = gtk.Label() - self.person_label.set_alignment(0.0, 0.5) - self.person_button = GrampsWidgets.SimpleButton(gtk.STOCK_INDEX, - self.get_person_clicked) - self.person_button.set_relief(gtk.RELIEF_NORMAL) - - self.pevt = gtk.EventBox() - self.pevt.add(self.person_label) - - self.gobj.pack_start(self.pevt, False) - self.gobj.pack_end(self.person_button, False) - - person = self.__db.get_person_from_gramps_id(self.get_value()) - if not person: - person = self.__dbstate.get_active_person() - self.update_person(person) - - def parse(self): - return self.get_value() - - def get_person_clicked(self, obj): - """ - Handle the button to choose a different person. - """ - from Filters import GenericFilter, Rules - rfilter = GenericFilter() - rfilter.set_logical_op('or') - rfilter.add_rule(Rules.Person.IsBookmarked([])) - - default_person = self.__db.get_default_person() - if default_person: - gid = default_person.get_gramps_id() - rfilter.add_rule(Rules.Person.HasIdOf([gid])) - - active_person = self.__dbstate.get_active_person() - if active_person: - gid = active_person.get_gramps_id() - rfilter.add_rule(Rules.Person.HasIdOf([gid])) - - select_class = selector_factory('Person') - sel = select_class(self.__dbstate, self.dialog.uistate, - self.dialog.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.set_value(gid) - - def add_tooltip(self, tooltip): - """ - Add the option's help to the GUI object. - """ - tooltip.set_tip(self.pevt, self.get_help()) - tooltip.set_tip(self.person_button, _('Select a different person')) - + Option.__init__(self, label, "") #------------------------------------------------------------------------- # # PersonListOption class @@ -764,7 +463,7 @@ class PersonListOption(Option): This class describes a widget that allows multiple people from the database to be selected. """ - def __init__(self, label, value, dbstate): + def __init__(self, label): """ @param label: A friendly label to be applied to this option. Example: "People of interest" @@ -774,135 +473,7 @@ class PersonListOption(Option): @type value: string @return: nothing """ - self.__db = dbstate.get_database() - self.__dbstate = dbstate - Option.__init__(self, label, value) - - def make_gui_obj(self, dialog): - """ - Add a "people picker" widget to the dialog. - """ - self.dialog = dialog - - value = self.get_value() - 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.hbox = gtk.HBox() - self.hbox.pack_start(self.scrolled_window, expand=True, fill=True) - - 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 = GrampsWidgets.SimpleButton(gtk.STOCK_ADD, - self.add_person_clicked) - self.del_person = GrampsWidgets.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.hbox.pack_end(self.vbbox, expand=False) - - # parent expects the widget as "self.gobj" - self.gobj = self.hbox - - def parse(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) - return gidlist - - def add_person_clicked(self, obj): - """ - 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 = selector_factory('Person') - sel = select_class(self.__dbstate, self.dialog.uistate, - self.dialog.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 = gtk.MessageDialog(parent=self.dialog.window, - flags=gtk.DIALOG_MODAL, - type=gtk.MESSAGE_QUESTION, - buttons=gtk.BUTTONS_YES_NO, - message_format=text) - prompt.set_default_response(gtk.RESPONSE_YES) - prompt.set_position(gtk.WIN_POS_CENTER_ON_PARENT) - prompt.set_title(_('Select Person')) - button = prompt.run() - prompt.destroy() - if button == gtk.RESPONSE_YES: - gid = spouse.get_gramps_id() - self.model.append([spouse_name, gid]) - - def del_person_clicked(self, obj): - """ - Handle the delete person button. - """ - (path, column) = self.tree_view.get_cursor() - if (path): - i = self.model.get_iter(path) - self.model.remove(i) + Option.__init__(self, label, "") #------------------------------------------------------------------------- # @@ -915,7 +486,7 @@ class SurnameColourOption(Option): selected from the database, and to assign a colour (not necessarily unique) to each one. """ - def __init__(self, label, value, dbstate): + def __init__(self, label): """ @param label: A friendly label to be applied to this option. Example: "Family lines" @@ -925,125 +496,7 @@ class SurnameColourOption(Option): @type value: string @return: nothing """ - self.__db = dbstate.get_database() - self.__dbstate = dbstate - Option.__init__(self, label, value) - - def make_gui_obj(self, dialog): - """ - Add a "surname-colour" widget to the dialog. - """ - self.dialog = dialog - 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.hbox = gtk.HBox() - self.hbox.pack_start(self.scrolled_window, expand=True, fill=True) - - self.add_surname = GrampsWidgets.SimpleButton(gtk.STOCK_ADD, - self.add_surname_clicked) - self.del_surname = GrampsWidgets.SimpleButton(gtk.STOCK_REMOVE, - self.del_surname_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.hbox.pack_end(self.vbbox, expand=False) - - # populate the surname/colour treeview - tmp = self.get_value().split() - while len(tmp) > 1: - surname = tmp.pop(0) - colour = tmp.pop(0) - self.model.append([surname, colour]) - - # parent expects the widget as "self.gobj" - self.gobj = self.hbox - - def parse(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 - surname_colours += surname + ' ' + colour + ' ' - i = self.model.iter_next(i) - return 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() - - def add_surname_clicked(self, obj): - """ - 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.dialog.uistate, - self.dialog.track, self.surnames, skip_list) - surname_set = ln_dialog.run() - for surname in surname_set: - self.model.append([surname, '#ffffff']) - - def del_surname_clicked(self, obj): - """ - 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) + Option.__init__(self, label, "") #------------------------------------------------------------------------- # @@ -1149,97 +602,3 @@ class Menu: if oname == name: return option return None - -#------------------------------------------------------------------------ -# -# MenuOptions class -# -#------------------------------------------------------------------------ -class MenuOptions: - """ - Introduction - ============ - A MenuOptions is used to implement the necessary funtions for adding - options to a dialog. - """ - def __init__(self, dbstate): - self.menu = Menu() - - # Fill options_dict with report/tool defaults: - self.options_dict = {} - self.options_help = {} - self.tooltips = gtk.Tooltips() - self.add_menu_options(self.menu, dbstate) - 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, dbstate): - """ - 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]) - option.make_gui_obj(dialog) - option.add_dialog_category(dialog, category) - option.add_tooltip(self.tooltips) - - # give the reports the opportunity to tweak the - # controls or possibly setup some event connections - self.post_init(dialog) - - def post_init(self, dialog): - """ - Inheritable method to give reports the chance to setup - control event connections. - """ - pass - - def parse_user_options(self, dialog): - """ - Generic method to parse the user options and cache result in options_dict. - """ - for name in self.menu.get_all_option_names(): - self.options_dict[name] = self.menu.get_option_by_name(name).parse() - - def get_option_names(self): - """ - Return all names of options. - """ - return self.menu.get_all_option_names() - - def get_user_value(self, name): - """ - Get and parse the users choice. - """ - return self.menu.get_option_by_name(name).parse() - diff --git a/src/PluginUtils/__init__.py b/src/PluginUtils/__init__.py index dcae67e4c..dea8a8534 100644 --- a/src/PluginUtils/__init__.py +++ b/src/PluginUtils/__init__.py @@ -28,10 +28,11 @@ #Better would be to do: import _PluginMgr as PluginMgr and then access # the list as PluginUtils.PluginMgr, or use a function that returns the pointer # of the list. -from _MenuOptions import MenuOptions, \ - NumberOption, FloatOption, BooleanOption, TextOption, \ - EnumeratedListOption, PersonFilterOption, StringOption, ColourButtonOption, \ - PersonOption, PersonListOption, SurnameColourOption +from _MenuOptions import \ + NumberOption, BooleanOption, TextOption, \ + EnumeratedListOption, PersonFilterOption, StringOption, ColourOption, \ + PersonOption, PersonListOption, SurnameColourOption, FamilyOption +from _GuiOptions import GuiMenuOptions from _PluginMgr import \ register_export, register_import, \ register_tool, register_report, \ @@ -48,7 +49,7 @@ import _Plugins as Plugins import _PluginWindows as PluginWindows # This needs to go above Tool and MenuOption as it needs both -class MenuToolOptions(MenuOptions,Tool.ToolOptions): +class MenuToolOptions(GuiMenuOptions,Tool.ToolOptions): """ The MenuToolOptions class implementes the ToolOptions functionality in a generic way so that the user does not need to @@ -60,6 +61,6 @@ class MenuToolOptions(MenuOptions,Tool.ToolOptions): """ def __init__(self, name, person_id=None, dbstate=None): Tool.ToolOptions.__init__(self,name, person_id) - MenuOptions.__init__(self, dbstate) + GuiMenuOptions.__init__(self, dbstate) diff --git a/src/ReportBase/_GraphvizReportDialog.py b/src/ReportBase/_GraphvizReportDialog.py index 2557f2108..a28a91b07 100644 --- a/src/ReportBase/_GraphvizReportDialog.py +++ b/src/ReportBase/_GraphvizReportDialog.py @@ -1,7 +1,7 @@ # # Gramps - a GTK+/GNOME based genealogy program # -# Copyright (C) 2007 Brian G. Matherly +# Copyright (C) 2007-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 @@ -28,6 +28,7 @@ import os from cStringIO import StringIO import tempfile from types import ClassType, InstanceType +from gettext import gettext as _ #------------------------------------------------------------------------------- # @@ -48,7 +49,7 @@ import Config from _Constants import CATEGORY_GRAPHVIZ from _ReportDialog import ReportDialog from _PaperMenu import PaperFrame -from PluginUtils import NumberOption, FloatOption, EnumeratedListOption, \ +from PluginUtils import NumberOption, EnumeratedListOption, \ TextOption, BooleanOption #------------------------------------------------------------------------------- @@ -769,6 +770,11 @@ class GraphvizReportDialog(ReportDialog): 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") ################################ @@ -789,7 +795,7 @@ class GraphvizReportDialog(ReportDialog): "300 or 600 DPI.")) self.options.add_menu_option(category, "dpi", dpi) - nodesep = FloatOption(_("Node spacing"), 0.20, 0.01, 5.00) + 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. " @@ -797,7 +803,7 @@ class GraphvizReportDialog(ReportDialog): "spacing between rows.")) self.options.add_menu_option(category, "nodesep", nodesep) - ranksep = FloatOption(_("Rank spacing"), 0.20, 0.01, 5.00) + 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 " @@ -827,30 +833,23 @@ class GraphvizReportDialog(ReportDialog): self.options.load_previous_values() - def pages_changed(self, sp): - # this method gets called every time the v_pages or h_pages - # spinbuttons are changed; when both vertical and horizontal - # pages are set to "1", then the page_dir control needs to - # be grayed out - if self.v_pages.gobj.get_value_as_int() > 1 or \ - self.h_pages.gobj.get_value_as_int() > 1: - self.page_dir.combo.set_sensitive(True) + 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.combo.set_sensitive(False) + self.page_dir.set_available(False) def init_interface(self): ReportDialog.init_interface(self) self.doc_type_changed(self.format_menu) - # now that the controls have all been created, - # we can finally setup the event connections - - # the page direction option only makes sense when the - # number of horizontal and/or vertical pages is > 1 - self.h_pages.gobj.connect('value-changed', self.pages_changed) - self.v_pages.gobj.connect('value-changed', self.pages_changed) - self.pages_changed(self.h_pages.gobj) - def setup_format_frame(self): """Set up the format frame of the dialog.""" diff --git a/src/ReportBase/_ReportOptions.py b/src/ReportBase/_ReportOptions.py index dd367604d..fac9e423f 100644 --- a/src/ReportBase/_ReportOptions.py +++ b/src/ReportBase/_ReportOptions.py @@ -55,7 +55,7 @@ except: import const import Config import BaseDoc -from PluginUtils import _Options, MenuOptions +from PluginUtils import _Options, GuiMenuOptions #------------------------------------------------------------------------- # @@ -770,7 +770,7 @@ class ReportOptions(_Options.Options): # MenuReportOptions # #------------------------------------------------------------------------- -class MenuReportOptions(MenuOptions,ReportOptions): +class MenuReportOptions(GuiMenuOptions,ReportOptions): """ The MenuReportOptions class implementes the ReportOptions @@ -788,7 +788,7 @@ class MenuReportOptions(MenuOptions,ReportOptions): else: active_person = None ReportOptions.__init__(self,name,active_person) - MenuOptions.__init__(self,dbstate) + GuiMenuOptions.__init__(self,dbstate) def load_previous_values(self): ReportOptions.load_previous_values(self) diff --git a/src/plugins/AncestorChart.py b/src/plugins/AncestorChart.py index bfece004a..829fa1d35 100644 --- a/src/plugins/AncestorChart.py +++ b/src/plugins/AncestorChart.py @@ -454,14 +454,11 @@ class AncestorChartOptions(MenuReportOptions): def add_menu_options(self,menu,dbstate): - id = "" - if dbstate: - id = dbstate.get_active_person().get_gramps_id() - pid = PersonOption(_("Center Person"),id,dbstate) - pid.set_help(_("The center person for the report")) - menu.add_option("","pid",pid) - category_name = _("Report Options") + + pid = PersonOption(_("Center Person")) + pid.set_help(_("The center person for the report")) + menu.add_option(category_name,"pid",pid) max_gen = NumberOption(_("Generations"),10,1,15) max_gen.set_help(_("The number of generations to include in the report")) diff --git a/src/plugins/AncestorReport.py b/src/plugins/AncestorReport.py index f0d300497..4afc7479d 100644 --- a/src/plugins/AncestorReport.py +++ b/src/plugins/AncestorReport.py @@ -232,15 +232,12 @@ class AncestorOptions(MenuReportOptions): """ Add options to the menu for the ancestor report. """ - id = "" - if dbstate: - id = dbstate.get_active_person().get_gramps_id() - pid = PersonOption(_("Center Person"),id,dbstate) - pid.set_help(_("The center person for the report")) - menu.add_option("","pid",pid) - category_name = _("Report Options") + pid = PersonOption(_("Center Person")) + pid.set_help(_("The center person for the report")) + menu.add_option(category_name,"pid",pid) + maxgen = NumberOption(_("Generations"),10,1,15) maxgen.set_help(_("The number of generations to include in the report")) menu.add_option(category_name,"maxgen",maxgen) diff --git a/src/plugins/BookReport.py b/src/plugins/BookReport.py index a8e170a6e..4b7a6044f 100644 --- a/src/plugins/BookReport.py +++ b/src/plugins/BookReport.py @@ -83,6 +83,8 @@ from ReportBase._DocReportDialog import DocReportDialog from ReportBase._CommandLineReport import CommandLineReport from ReportBase._ReportOptions import ReportOptions +from BasicUtils import name_displayer as _nd + #------------------------------------------------------------------------ # # Private Functions @@ -103,13 +105,13 @@ def _get_subject(options,db): option_names = menu.get_all_option_names() for name in option_names: option = menu.get_option_by_name(name) - if isinstance(option,PersonOption): - from BasicUtils import name_displayer as _nd + if isinstance(option, PersonFilterOption): + return option.get_filter().get_name() + elif isinstance(option, PersonOption): gid = option.get_value() person = db.get_person_from_gramps_id(gid) return _nd.display(person) - elif isinstance(option,PersonFilterOption): - return option.get_filter().get_name() + return _("Not Applicable") #------------------------------------------------------------------------ diff --git a/src/plugins/CalculateEstimatedDates.py b/src/plugins/CalculateEstimatedDates.py index 9701fea33..00a8fd75a 100644 --- a/src/plugins/CalculateEstimatedDates.py +++ b/src/plugins/CalculateEstimatedDates.py @@ -2,6 +2,7 @@ # Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 2007 Donald N. Allingham +# Copyright (C) 2008 Brian 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 @@ -37,11 +38,12 @@ import time #------------------------------------------------------------------------ from PluginUtils import Tool, register_tool, PluginWindows, \ MenuToolOptions, BooleanOption, PersonFilterOption, StringOption, \ - NumberOption + NumberOption, PersonOption import gen.lib import Config from BasicUtils import name_displayer import Errors +from ReportBase import ReportUtils #------------------------------------------------------------------------ # @@ -52,12 +54,21 @@ class CalcEstDateOptions(MenuToolOptions): """ Calculate Estimated Date options """ def add_menu_options(self, menu, dbstate): + self.__db = dbstate.get_database() + """ Adds the options """ category_name = _("Options") - - filter = PersonFilterOption(_("Filter"), dbstate, 0, False) - filter.set_help(_("Select filter to restrict people")) - menu.add_option(category_name,"filter", filter) + + self.__pid = PersonOption(_("Filter Person")) + self.__pid.set_help(_("The center person for the filter")) + menu.add_option(category_name, "pid", self.__pid) + self.__pid.connect('value-changed', self.__update_filters) + + self.__filter = PersonFilterOption(_("Filter"), 0) + self.__filter.set_help(_("Select filter to restrict people")) + self.__update_filters() + menu.add_option(category_name, "filter", self.__filter) + self.__filter.connect('value-changed', self.__filter_changed) source_text = StringOption(_("Source text"), _("Calculated Date Estimates")) @@ -105,6 +116,28 @@ class CalcEstDateOptions(MenuToolOptions): 0, 200) num.set_help(_("Average years between two generations")) menu.add_option(category_name, "AVG_GENERATION_GAP", num) + + def __update_filters(self): + """ + Update the filter list based on the selected person + """ + gid = self.__pid.get_value() + person = self.__db.get_person_from_gramps_id(gid) + filter_list = ReportUtils.get_person_filters(person, False) + self.__filter.set_filters(filter_list) + + def __filter_changed(self): + """ + Handle filter change. If the filter is not specific to a person, + disable the person option + """ + filter_value = self.__filter.get_value() + if filter_value in [1, 2, 3, 4]: + # Filters 0, 2, 3, 4 and 5 rely on the center person + self.__pid.set_available(True) + else: + # The rest don't + self.__pid.set_available(False) class CalcToolManagedWindow(PluginWindows.ToolManagedWindowBatch): diff --git a/src/plugins/Calendar.py b/src/plugins/Calendar.py index 24dc84414..7487612a2 100644 --- a/src/plugins/Calendar.py +++ b/src/plugins/Calendar.py @@ -1,6 +1,7 @@ # Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 2000-2007 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 @@ -45,7 +46,7 @@ from ReportBase import Report, ReportUtils, MenuReportOptions, \ CATEGORY_DRAW, CATEGORY_TEXT, \ MODE_GUI, MODE_BKI, MODE_CLI from PluginUtils import NumberOption, BooleanOption, StringOption, \ - PersonFilterOption, EnumeratedListOption + PersonFilterOption, EnumeratedListOption, PersonOption import GrampsLocale import gen.lib from Utils import probably_alive, ProgressMeter @@ -144,6 +145,7 @@ class Calendar(Report): self.text3 = options_class.handler.options_dict['text3'] self.filter_option = options_class.menu.get_option_by_name('filter') self.filter = self.filter_option.get_filter() + self.pid = options_class.handler.options_dict['pid'] self.title = _("Calendar Report") #% name @@ -313,7 +315,7 @@ class Calendar(Report): self.progress.set_pass(_('Filtering data...'), 0) people = self.filter.apply(self.database, self.database.get_person_handles(sort_handles=False)) - center_person = self.filter_option.get_center_person() + center_person = self.database.get_person_from_gramps_id(self.pid) rel_calc = relationship_class() self.progress.set_pass(_('Filtering data...'), len(people)) for person_handle in people: @@ -471,18 +473,32 @@ class CalendarReport(Calendar): class CalendarOptions(MenuReportOptions): """ Calendar options for graphic calendar """ + def __init__(self, name, dbstate=None): + self.__dbstate = dbstate + self.__pid = None + self.__filter = None + MenuReportOptions.__init__(self, name, dbstate) - def add_menu_options(self, menu,dbstate): + def add_menu_options(self, menu, dbstate): """ Adds the options for the graphical calendar """ category_name = _("Report Options") - year = NumberOption(_("Year of calendar"), time.localtime()[0], 1000, 3000) + year = NumberOption(_("Year of calendar"), time.localtime()[0], + 1000, 3000) year.set_help(_("Year of calendar")) menu.add_option(category_name,"year", year) - filter = PersonFilterOption(_("Filter"),dbstate,0,False) - filter.set_help(_("Select filter to restrict people that appear on calendar")) - menu.add_option(category_name,"filter", filter) + self.__pid = PersonOption(_("Filter Person")) + self.__pid.set_help(_("The center person for the filter")) + menu.add_option(category_name, "pid", self.__pid) + self.__pid.connect('value-changed', self.__update_filters) + + self.__filter = PersonFilterOption(_("Filter"), 0) + self.__filter.set_help( + _("Select filter to restrict people that appear on calendar")) + self.__update_filters() + menu.add_option(category_name, "filter", self.__filter) + self.__filter.connect('value-changed', self.__filter_changed) name_format = EnumeratedListOption(_("Name format"), -1) for num, name, fmt_str, act in name_displayer.get_name_format(): @@ -537,6 +553,29 @@ class CalendarOptions(MenuReportOptions): text3 = StringOption(_("Text Area 3"), "http://gramps-project.org/",) text3.set_help(_("Third line of text at bottom of calendar")) menu.add_option(category_name,"text3", text3) + + def __update_filters(self): + """ + Update the filter list based on the selected person + """ + _db = self.__dbstate.get_database() + gid = self.__pid.get_value() + person = _db.get_person_from_gramps_id(gid) + filter_list = ReportUtils.get_person_filters(person, False) + self.__filter.set_filters(filter_list) + + def __filter_changed(self): + """ + Handle filter change. If the filter is not specific to a person, + disable the person option + """ + filter_value = self.__filter.get_value() + if filter_value in [1, 2, 3, 4]: + # Filters 1, 2, 3 and 4 rely on the center person + self.__pid.set_available(True) + else: + # The rest don't + self.__pid.set_available(False) def make_my_style(self, default_style, name, description, size=9, font=BaseDoc.FONT_SERIF, justified ="left", @@ -603,6 +642,8 @@ class CalendarOptions(MenuReportOptions): class CalendarReportOptions(CalendarOptions): """ Options for the calendar (birthday and anniversary) report """ + def __init__(self, name, dbstate=None): + CalendarOptions.__init__(self, name, dbstate) def add_menu_options(self, menu,dbstate): """ Adds the options for the graphical calendar """ diff --git a/src/plugins/DescendChart.py b/src/plugins/DescendChart.py index 8dd121e09..837c8ec00 100644 --- a/src/plugins/DescendChart.py +++ b/src/plugins/DescendChart.py @@ -410,15 +410,12 @@ class DescendChartOptions(MenuReportOptions): """ Add options to the menu for the descendant report. """ - id = "" - if dbstate: - id = dbstate.get_active_person().get_gramps_id() - pid = PersonOption(_("Center Person"),id,dbstate) - pid.set_help(_("The center person for the report")) - menu.add_option("","pid",pid) - category_name = _("Report Options") + pid = PersonOption(_("Center Person")) + pid.set_help(_("The center person for the report")) + menu.add_option(category_name, "pid", pid) + 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) diff --git a/src/plugins/DescendReport.py b/src/plugins/DescendReport.py index b43efdadf..2de2b8f96 100644 --- a/src/plugins/DescendReport.py +++ b/src/plugins/DescendReport.py @@ -201,15 +201,12 @@ class DescendantOptions(MenuReportOptions): MenuReportOptions.__init__(self,name,dbstate) def add_menu_options(self,menu,dbstate): - id = "" - if dbstate: - id = dbstate.get_active_person().get_gramps_id() - pid = PersonOption(_("Center Person"),id,dbstate) - pid.set_help(_("The center person for the report")) - menu.add_option("","pid",pid) - category_name = _("Report Options") + pid = PersonOption(_("Center Person")) + pid.set_help(_("The center person for the report")) + menu.add_option(category_name, "pid", pid) + gen = NumberOption(_("Generations"),10,1,15) gen.set_help(_("The number of generations to include in the report")) menu.add_option(category_name,"gen",gen) diff --git a/src/plugins/DetAncestralReport.py b/src/plugins/DetAncestralReport.py index 8830c7b48..7b6c7ad0f 100644 --- a/src/plugins/DetAncestralReport.py +++ b/src/plugins/DetAncestralReport.py @@ -674,15 +674,12 @@ class DetAncestorOptions(MenuReportOptions): MenuReportOptions.__init__(self,name,dbstate) def add_menu_options(self,menu,dbstate): - id = "" - if dbstate: - id = dbstate.get_active_person().get_gramps_id() - pid = PersonOption(_("Center Person"),id,dbstate) - pid.set_help(_("The center person for the report")) - menu.add_option("","pid",pid) - category_name = _("Report Options") + pid = PersonOption(_("Center Person")) + pid.set_help(_("The center person for the report")) + menu.add_option(category_name, "pid", pid) + gen = NumberOption(_("Generations"),10,1,100) gen.set_help(_("The number of generations to include in the report")) menu.add_option(category_name,"gen",gen) diff --git a/src/plugins/DetDescendantReport.py b/src/plugins/DetDescendantReport.py index f56231ccd..7ec02d7f4 100644 --- a/src/plugins/DetDescendantReport.py +++ b/src/plugins/DetDescendantReport.py @@ -635,15 +635,12 @@ class DetDescendantOptions(MenuReportOptions): """ Add options to the menu for the detailed descendant report. """ - id = "" - if dbstate: - id = dbstate.get_active_person().get_gramps_id() - pid = PersonOption(_("Center Person"),id,dbstate) - pid.set_help(_("The center person for the report")) - menu.add_option("","pid",pid) - category_name = _("Report Options") + pid = PersonOption(_("Center Person")) + pid.set_help(_("The center person for the report")) + menu.add_option(category_name, "pid", pid) + gen = NumberOption(_("Generations"),10,1,100) gen.set_help(_("The number of generations to include in the report")) menu.add_option(category_name,"gen",gen) diff --git a/src/plugins/EndOfLineReport.py b/src/plugins/EndOfLineReport.py index dc9c039a1..106730878 100644 --- a/src/plugins/EndOfLineReport.py +++ b/src/plugins/EndOfLineReport.py @@ -225,12 +225,11 @@ class EndOfLineOptions(MenuReportOptions): """ Add options to the menu for the End of Line report. """ - id = "" - if dbstate: - id = dbstate.get_active_person().get_gramps_id() - pid = PersonOption(_("Center Person"),id,dbstate) + category_name = _("Report Options") + + pid = PersonOption(_("Center Person")) pid.set_help(_("The center person for the report")) - menu.add_option("","pid",pid) + menu.add_option(category_name, "pid", pid) def make_default_style(self,default_style): """Make the default output style for the End of Line Report.""" diff --git a/src/plugins/FamilyGroup.py b/src/plugins/FamilyGroup.py index 7a73ef22f..a91e70482 100644 --- a/src/plugins/FamilyGroup.py +++ b/src/plugins/FamilyGroup.py @@ -23,25 +23,17 @@ """Generate files/Family Group Report""" -#------------------------------------------------------------------------ -# -# Gnome/GTK modules -# -#------------------------------------------------------------------------ -import gtk - #------------------------------------------------------------------------ # # GRAMPS # #------------------------------------------------------------------------ import gen.lib -from PluginUtils import register_report, BooleanOption, EnumeratedListOption +from PluginUtils import register_report, BooleanOption, FamilyOption from ReportBase import Report, ReportUtils, MenuReportOptions, \ CATEGORY_TEXT, MODE_GUI, MODE_BKI, MODE_CLI import BaseDoc import DateHandler -import Utils from TransUtils import sgettext as _ from BasicUtils import name_displayer as _nd @@ -73,14 +65,8 @@ class FamilyGroup(Report): self.family_handle = None family_id = options_class.handler.options_dict['family_id'] - if family_id: - family_list = person.get_family_handle_list() - for family_handle in family_list: - family = database.get_family_from_handle(family_handle) - this_family_id = family.get_gramps_id() - if this_family_id == family_id: - self.family_handle = family_handle - break + family = database.get_family_from_gramps_id(family_id) + self.family_handle = family.get_handle() self.recursive = options_class.handler.options_dict['recursive'] self.missingInfo = options_class.handler.options_dict['missinginfo'] @@ -596,19 +582,9 @@ class FamilyGroupOptions(MenuReportOptions): category_name = _("Report Options") ########################## - if dbstate: - db = dbstate.get_database() - person = dbstate.get_active_person() - else: - db = None - person = None - families = self.get_families(db, person) - - family_id = EnumeratedListOption(_("Spouse"), "") - for item in families: - family_id.add_item(item[0], item[1]) - family_id.set_help(_("Gramps ID of the person's family.")) - menu.add_option(category_name,"family_id",family_id) + family_id = FamilyOption(_("Center Family")) + family_id.set_help(_("The center family for the report")) + menu.add_option(category_name, "family_id", family_id) recursive = BooleanOption(_('Recursive'),False) recursive.set_help(_("Create reports for all decendants " @@ -671,32 +647,6 @@ class FamilyGroupOptions(MenuReportOptions): "information.")) menu.add_option(category_name,"missinginfo",missinginfo) - def get_families(self,database,person): - """ - Create a mapping of all spouse names:families to be put - into the 'extra' option menu in the report options box. If - the selected person has never been married then this routine - will return a placebo label and disable the OK button. - """ - families = [] - family_id = None - family_list = person.get_family_handle_list() - for family_handle in family_list: - family = database.get_family_from_handle(family_handle) - family_id = family.get_gramps_id() - if person.get_handle() == family.get_father_handle(): - spouse_handle = family.get_mother_handle() - else: - spouse_handle = family.get_father_handle() - if spouse_handle: - spouse = database.get_person_from_handle(spouse_handle) - name = spouse.get_primary_name().get_name() - else: - name = _("unknown") - name = "%s (%s)" % (name,family_id) - families.append((family_id,name)) - return families - def make_default_style(self,default_style): """Make default output style for the Family Group Report.""" para = BaseDoc.ParagraphStyle() diff --git a/src/plugins/FanChart.py b/src/plugins/FanChart.py index 35eedf208..d62728161 100644 --- a/src/plugins/FanChart.py +++ b/src/plugins/FanChart.py @@ -326,15 +326,12 @@ class FanChartOptions(MenuReportOptions): """ Add options to the menu for the fan chart. """ - id = "" - if dbstate: - id = dbstate.get_active_person().get_gramps_id() - pid = PersonOption(_("Center Person"),id,dbstate) - pid.set_help(_("The center person for the report")) - menu.add_option("","pid",pid) - category_name = _("Report Options") - + + pid = PersonOption(_("Center Person")) + pid.set_help(_("The center person for the report")) + menu.add_option(category_name, "pid", pid) + 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) diff --git a/src/plugins/GVFamilyLines.py b/src/plugins/GVFamilyLines.py index 6c754c380..743450b78 100644 --- a/src/plugins/GVFamilyLines.py +++ b/src/plugins/GVFamilyLines.py @@ -49,8 +49,10 @@ import gen.lib import Utils import ThumbNails from DateHandler import displayer as _dd -from ReportBase import Report, ReportUtils, MenuReportOptions, CATEGORY_GRAPHVIZ, MODE_GUI -from PluginUtils import register_report, EnumeratedListOption, BooleanOption, NumberOption, ColourButtonOption, PersonListOption, SurnameColourOption +from ReportBase import Report, ReportUtils, MenuReportOptions, \ + CATEGORY_GRAPHVIZ, MODE_GUI +from PluginUtils import register_report, EnumeratedListOption, BooleanOption, \ + NumberOption, ColourOption, PersonListOption, SurnameColourOption #------------------------------------------------------------------------ @@ -87,7 +89,7 @@ class FamilyLinesOptions(MenuReportOptions): category = _('People of Interest') # -------------------------------- - personList = PersonListOption( _('People of interest'), '', dbstate) + personList = PersonListOption( _('People of interest')) personList.set_help( _('People of interest are used as a starting point when determining \"family lines\".')) menu.add_option(category, 'FLgidlist', personList) @@ -107,7 +109,7 @@ class FamilyLinesOptions(MenuReportOptions): category = _('Family Colours') # ---------------------------- - surnameColour = SurnameColourOption(_('Family colours'), '', dbstate) + surnameColour = SurnameColourOption(_('Family colours')) surnameColour.set_help( _('Colours to use for various family lines.')) menu.add_option(category, 'FLsurnameColours', surnameColour) @@ -115,19 +117,19 @@ class FamilyLinesOptions(MenuReportOptions): category = _('Individuals') # ------------------------- - colourMales = ColourButtonOption( _('Males'), '#e0e0ff') + colourMales = ColourOption( _('Males'), '#e0e0ff') colourMales.set_help( _('The colour to use to display men.')) menu.add_option(category, 'FLcolourMales', colourMales) - colourFemales = ColourButtonOption( _('Females'), '#ffe0e0') + colourFemales = ColourOption( _('Females'), '#ffe0e0') colourFemales.set_help( _('The colour to use to display women.')) menu.add_option(category, 'FLcolourFemales', colourFemales) - colourUnknown = ColourButtonOption( _('Unknown'), '#e0e0e0') + colourUnknown = ColourOption( _('Unknown'), '#e0e0e0') colourUnknown.set_help( _('The colour to use when the gender is unknown.')) menu.add_option(category, 'FLcolourUnknown', colourUnknown) - colourFamily = ColourButtonOption( _('Families'), '#ffffe0') + colourFamily = ColourOption( _('Families'), '#ffffe0') colourFamily.set_help( _('The colour to use to display families.')) menu.add_option(category, 'FLcolourFamilies', colourFamily) diff --git a/src/plugins/GVHourGlass.py b/src/plugins/GVHourGlass.py index ca004d1e1..832fe45e5 100644 --- a/src/plugins/GVHourGlass.py +++ b/src/plugins/GVHourGlass.py @@ -169,15 +169,12 @@ class HourGlassOptions(MenuReportOptions): """ Create all the menu options for this report. """ - gid = "" - if dbstate: - gid = dbstate.get_active_person().get_gramps_id() - pid = PersonOption(_("Center Person"), gid, dbstate) - pid.set_help(_("The center person for the report")) - menu.add_option("", "pid", pid) - category_name = _("Report Options") + pid = PersonOption(_("Center Person")) + pid.set_help(_("The center person for the report")) + menu.add_option(category_name, "pid", pid) + max_gen = NumberOption(_('Max Descendant Generations'), 10, 1, 15) max_gen.set_help(_("The number of generations of descendants to " \ "include in the report")) diff --git a/src/plugins/GVRelGraph.py b/src/plugins/GVRelGraph.py index 49cbab984..cf12d82df 100644 --- a/src/plugins/GVRelGraph.py +++ b/src/plugins/GVRelGraph.py @@ -1,7 +1,7 @@ # # Gramps - a GTK+/GNOME based genealogy program # -# Copyright (C) 2007 Brian G. Matherly +# Copyright (C) 2007-2008 Brian G. Matherly # # Adapted from GraphViz.py (now deprecated) # Copyright (C) 2000-2007 Donald N. Allingham @@ -29,14 +29,21 @@ Create a relationship graph using Graphviz """ +#------------------------------------------------------------------------ +# +# python modules +# +#------------------------------------------------------------------------ +from TransUtils import sgettext as _ + #------------------------------------------------------------------------ # # GRAMPS modules # #------------------------------------------------------------------------ from PluginUtils import register_report, PersonFilterOption, \ - EnumeratedListOption, BooleanOption -from ReportBase import Report, MenuReportOptions, \ + EnumeratedListOption, BooleanOption, PersonOption +from ReportBase import Report, ReportUtils, MenuReportOptions, \ MODE_GUI, MODE_CLI, CATEGORY_GRAPHVIZ from BasicUtils import name_displayer import DateHandler @@ -408,35 +415,48 @@ class RelGraphOptions(MenuReportOptions): """ Defines options and provides handling interface. """ - def __init__(self,name,dbstate=None): - MenuReportOptions.__init__(self,name,dbstate) + def __init__(self, name, dbstate=None): + self.__pid = None + self.__filter = None + self.__include_images = None + self.__image_on_side = None + self.__dbstate = dbstate + MenuReportOptions.__init__(self, name, dbstate) - def add_menu_options(self,menu,dbstate): + def add_menu_options(self, menu, dbstate): ################################ category_name = _("Report Options") ################################ + + self.__pid = PersonOption(_("Filter Person")) + self.__pid.set_help(_("The center person for the filter")) + menu.add_option(category_name, "pid", self.__pid) + self.__pid.connect('value-changed', self.__update_filters) - filter = PersonFilterOption(_("Filter"),dbstate,0,False) - filter.set_help(_("Select the filter to be applied to the report")) - menu.add_option(category_name,"filter", filter) + self.__filter = PersonFilterOption(_("Filter"), 0) + self.__filter.set_help( + _("Determines what people are included in the graph")) + self.__update_filters() + menu.add_option(category_name, "filter", self.__filter) + self.__filter.connect('value-changed', self.__filter_changed) incdate = BooleanOption( _("Include Birth, Marriage and Death dates"), True) incdate.set_help(_("Include the dates that the individual was born, " "got married and/or died in the graph labels.")) - menu.add_option(category_name,"incdate", incdate) + menu.add_option(category_name, "incdate", incdate) justyears = BooleanOption(_("Limit dates to years only"), False) justyears.set_help(_("Prints just dates' year, neither " "month or day nor date approximation " "or interval are shown.")) - menu.add_option(category_name,"justyears", justyears) + menu.add_option(category_name, "justyears", justyears) placecause = BooleanOption(_("Place/cause when no date"), True) placecause.set_help(_("When no birth, marriage, or death date is " "available, the correspondent place field (or " "cause field when blank place) will be used.")) - menu.add_option(category_name,"placecause", placecause) + menu.add_option(category_name, "placecause", placecause) url = BooleanOption(_("Include URLs"), False) url.set_help(_("Include a URL in each graph node so " @@ -444,23 +464,26 @@ class RelGraphOptions(MenuReportOptions): "generated that contain active links " "to the files generated by the 'Narrated " "Web Site' report.")) - menu.add_option(category_name,"url", url) + menu.add_option(category_name, "url", url) incid = BooleanOption(_("Include IDs"), False) incid.set_help(_("Include individual and family IDs.")) - menu.add_option(category_name,"incid", incid) + menu.add_option(category_name, "incid", incid) - self.includeImages = BooleanOption( + self.__include_images = BooleanOption( _('Include thumbnail images of people'), False) - self.includeImages.set_help(_("Whether to include thumbnails of people.")) - menu.add_option(category_name,"includeImages", self.includeImages) + self.__include_images.set_help( + _("Whether to include thumbnails of people.")) + menu.add_option(category_name, "includeImages", self.__include_images) + self.__include_images.connect('value-changed', self.__image_changed) - self.imageOnTheSide = EnumeratedListOption(_("Thumbnail Location"), 0) - self.imageOnTheSide.add_item(0, _('Above the name')) - self.imageOnTheSide.add_item(1, _('Beside the name')) - self.imageOnTheSide.set_help(_("Where the thumbnail image should appear " - "relative to the name")) - menu.add_option(category_name,"imageOnTheSide",self.imageOnTheSide) + self.__image_on_side = EnumeratedListOption(_("Thumbnail Location"), 0) + self.__image_on_side.add_item(0, _('Above the name')) + self.__image_on_side.add_item(1, _('Beside the name')) + self.__image_on_side.set_help( + _("Where the thumbnail image should appear " + "relative to the name")) + menu.add_option(category_name, "imageOnTheSide", self.__image_on_side) ################################ category_name = _("Graph Style") @@ -472,35 +495,55 @@ class RelGraphOptions(MenuReportOptions): color.set_help(_("Males will be shown with blue, females " "with red. If the sex of an individual " "is unknown it will be shown with gray.")) - menu.add_option(category_name,"color",color) + menu.add_option(category_name, "color", color) arrow = EnumeratedListOption(_("Arrowhead direction"), 'd') for i in range( 0, len(_ARROWS) ): arrow.add_item(_ARROWS[i]["value"], _ARROWS[i]["name"]) arrow.set_help(_("Choose the direction that the arrows point.")) - menu.add_option(category_name,"arrow",arrow) + menu.add_option(category_name, "arrow", arrow) dashed = BooleanOption( _("Indicate non-birth relationships with dotted lines"), True) dashed.set_help(_("Non-birth relationships will show up " "as dotted lines in the graph.")) - menu.add_option(category_name,"dashed", dashed) + menu.add_option(category_name, "dashed", dashed) showfamily = BooleanOption(_("Show family nodes"), True) showfamily.set_help(_("Families will show up as ellipses, linked " "to parents and children.")) - menu.add_option(category_name,"showfamily", showfamily) - - - def imageChanged(self, button): - self.imageOnTheSide.gobj.set_sensitive(self.includeImages.gobj.get_active()) - - - def post_init(self, dialog): - self.includeImages.gobj.connect('toggled', self.imageChanged) - self.imageChanged(self.includeImages.gobj) - + menu.add_option(category_name, "showfamily", showfamily) + def __update_filters(self): + """ + Update the filter list based on the selected person + """ + _db = self.__dbstate.get_database() + gid = self.__pid.get_value() + person = _db.get_person_from_gramps_id(gid) + filter_list = ReportUtils.get_person_filters(person, False) + self.__filter.set_filters(filter_list) + + def __filter_changed(self): + """ + Handle filter change. If the filter is not specific to a person, + disable the person option + """ + filter_value = self.__filter.get_value() + if filter_value in [1, 2, 3, 4]: + # Filters 1, 2, 3 and 4 rely on the center person + self.__pid.set_available(True) + else: + # The rest don't + self.__pid.set_available(False) + + def __image_changed(self): + """ + Handle thumbnail change. If the image is not to be included, make the + image location option unavailable. + """ + self.__image_on_side.set_available(self.__include_images.get_value()) + #------------------------------------------------------------------------ # # diff --git a/src/plugins/IndivComplete.py b/src/plugins/IndivComplete.py index 92ecc7c1c..03d7f904d 100644 --- a/src/plugins/IndivComplete.py +++ b/src/plugins/IndivComplete.py @@ -35,12 +35,10 @@ from gettext import gettext as _ # #------------------------------------------------------------------------ import gen.lib -import const -import Utils import BaseDoc -from Filters import GenericFilter, Rules import DateHandler -from PluginUtils import register_report, PersonFilterOption, BooleanOption +from PluginUtils import register_report, PersonFilterOption, BooleanOption, \ + PersonOption from ReportBase import Report, ReportUtils, MenuReportOptions, \ CATEGORY_TEXT, MODE_GUI, MODE_BKI, MODE_CLI from ReportBase import Bibliography, Endnotes @@ -531,21 +529,55 @@ class IndivCompleteOptions(MenuReportOptions): """ Defines options and provides handling interface. """ - def __init__(self,name,dbstate=None): - MenuReportOptions.__init__(self,name,dbstate) + def __init__(self, name, dbstate=None): + self.__dbstate = dbstate + self.__pid = None + self.__filter = None + MenuReportOptions.__init__(self, name, dbstate) - def add_menu_options(self,menu,dbstate): + def add_menu_options(self, menu, dbstate): ################################ category_name = _("Report Options") ################################ - filter = PersonFilterOption(_("Filter"),dbstate,0,True) - filter.set_help(_("Select the filter to be applied to the report")) - menu.add_option(category_name,"filter", filter) + self.__pid = PersonOption(_("Filter Person")) + self.__pid.set_help(_("The center person for the filter")) + menu.add_option(category_name, "pid", self.__pid) + self.__pid.connect('value-changed', self.__update_filters) + + self.__filter = PersonFilterOption(_("Filter"), 0) + self.__filter.set_help( + _("Select the filter to be applied to the report")) + self.__update_filters() + menu.add_option(category_name, "filter", self.__filter) + self.__filter.connect('value-changed', self.__filter_changed) cites = BooleanOption(_("Include Source Information"), True) cites.set_help(_("Whether to cite sources.")) menu.add_option(category_name,"cites", cites) + + def __update_filters(self): + """ + Update the filter list based on the selected person + """ + _db = self.__dbstate.get_database() + gid = self.__pid.get_value() + person = _db.get_person_from_gramps_id(gid) + filter_list = ReportUtils.get_person_filters(person, True) + self.__filter.set_filters(filter_list) + + def __filter_changed(self): + """ + Handle filter change. If the filter is not specific to a person, + disable the person option + """ + filter_value = self.__filter.get_value() + if filter_value in [0, 2, 3, 4, 5]: + # Filters 0, 2, 3, 4 and 5 rely on the center person + self.__pid.set_available(True) + else: + # The rest don't + self.__pid.set_available(False) def make_default_style(self,default_style): """Make the default output style for the Individual Complete Report.""" diff --git a/src/plugins/KinshipReport.py b/src/plugins/KinshipReport.py index e7265c575..c5a7d02e6 100644 --- a/src/plugins/KinshipReport.py +++ b/src/plugins/KinshipReport.py @@ -330,15 +330,12 @@ class KinshipOptions(MenuReportOptions): """ Add options to the menu for the kinship report. """ - id = "" - if dbstate: - id = dbstate.get_active_person().get_gramps_id() - pid = PersonOption(_("Center Person"),id,dbstate) - pid.set_help(_("The center person for the report")) - menu.add_option("","pid",pid) - category_name = _("Report Options") + pid = PersonOption(_("Center Person")) + pid.set_help(_("The center person for the report")) + menu.add_option(category_name, "pid", pid) + maxdescend = NumberOption(_("Max Descendant Generations"),2,1,20) maxdescend.set_help(_("The maximum number of descendant generations")) menu.add_option(category_name,"maxdescend",maxdescend) diff --git a/src/plugins/MarkerReport.py b/src/plugins/MarkerReport.py index d5209569a..9da199f74 100644 --- a/src/plugins/MarkerReport.py +++ b/src/plugins/MarkerReport.py @@ -39,7 +39,7 @@ from ReportBase import Report, ReportUtils, MenuReportOptions, \ CATEGORY_TEXT, MODE_GUI, MODE_BKI, MODE_CLI import BaseDoc from gen.lib import MarkerType, FamilyRelType -from Filters import GenericFilter, GenericFilterFactory, Rules +from Filters import GenericFilterFactory, Rules from BasicUtils import name_displayer import DateHandler @@ -433,7 +433,8 @@ class MarkerOptions(MenuReportOptions): """ category_name = _("Report Options") - marker = EnumeratedListOption(_('Marker'),0) + marker = EnumeratedListOption(_('Marker'), + MarkerType._I2EMAP[MarkerType.COMPLETE]) # Add built-in marker types for mtype in MarkerType._I2SMAP: if mtype != MarkerType.NONE and mtype != MarkerType.CUSTOM: diff --git a/src/plugins/StatisticsChart.py b/src/plugins/StatisticsChart.py index dabdde762..2b98b9419 100644 --- a/src/plugins/StatisticsChart.py +++ b/src/plugins/StatisticsChart.py @@ -48,11 +48,10 @@ from gen.lib import Person, FamilyRelType, EventType # gender and report type names import BaseDoc from PluginUtils import register_report -from PluginUtils import BooleanOption, PersonFilterOption, EnumeratedListOption, \ - NumberOption +from PluginUtils import BooleanOption, PersonFilterOption, PersonOption, \ + EnumeratedListOption, NumberOption from ReportBase import Report, ReportUtils, MenuReportOptions, \ CATEGORY_DRAW, MODE_GUI, MODE_BKI, MODE_CLI -from Filters import GenericFilter, Rules import DateHandler from Utils import ProgressMeter @@ -660,7 +659,10 @@ class StatisticsChart(Report): class StatisticsChartOptions(MenuReportOptions): def __init__(self,name,dbstate=None): - MenuReportOptions.__init__(self,name,dbstate) + self.__pid = None + self.__filter = None + self.__dbstate = dbstate + MenuReportOptions.__init__(self, name, dbstate) def add_menu_options(self,menu,dbstate): """ @@ -668,9 +670,17 @@ class StatisticsChartOptions(MenuReportOptions): """ category_name = _("Report Options") - filter = PersonFilterOption(_("Filter"),dbstate,0,False) - filter.set_help(_("Determines what people are included in the report")) - menu.add_option(category_name,"filter", filter) + self.__pid = PersonOption(_("Filter Person")) + self.__pid.set_help(_("The center person for the filter")) + menu.add_option(category_name, "pid", self.__pid) + self.__pid.connect('value-changed', self.__update_filters) + + self.__filter = PersonFilterOption(_("Filter"), 0) + self.__filter.set_help( + _("Determines what people are included in the report")) + self.__update_filters() + menu.add_option(category_name, "filter", self.__filter) + self.__filter.connect('value-changed', self.__filter_changed) sortby = EnumeratedListOption(_('Sort chart items by'), _options.SORT_VALUE ) @@ -735,6 +745,29 @@ class StatisticsChartOptions(MenuReportOptions): menu.get_option_by_name("data_gender").set_value(True) menu.get_option_by_name("data_ccount").set_value(True) menu.get_option_by_name("data_bmonth").set_value(True) + + def __update_filters(self): + """ + Update the filter list based on the selected person + """ + _db = self.__dbstate.get_database() + gid = self.__pid.get_value() + person = _db.get_person_from_gramps_id(gid) + filter_list = ReportUtils.get_person_filters(person, False) + self.__filter.set_filters(filter_list) + + def __filter_changed(self): + """ + Handle filter change. If the filter is not specific to a person, + disable the person option + """ + filter_value = self.__filter.get_value() + if filter_value in [1, 2, 3, 4]: + # Filters 1, 2, 3 and 4 rely on the center person + self.__pid.set_available(True) + else: + # The rest don't + self.__pid.set_available(False) def make_default_style(self, default_style): """Make the default output style for the Statistics report.""" diff --git a/src/plugins/TimeLine.py b/src/plugins/TimeLine.py index 3f053d77f..e4c70e950 100644 --- a/src/plugins/TimeLine.py +++ b/src/plugins/TimeLine.py @@ -38,7 +38,8 @@ from TransUtils import sgettext as _ # #------------------------------------------------------------------------ from PluginUtils import register_report -from PluginUtils import PersonFilterOption, EnumeratedListOption +from PluginUtils import PersonFilterOption, EnumeratedListOption, \ + PersonOption from ReportBase import Report, ReportUtils, MenuReportOptions, \ CATEGORY_DRAW, MODE_GUI, MODE_BKI, MODE_CLI pt2cm = ReportUtils.pt2cm @@ -296,15 +297,25 @@ class TimeLine(Report): class TimeLineOptions(MenuReportOptions): def __init__(self,name,dbstate=None): + self.__pid = None + self.__filter = None + self.__dbstate = dbstate MenuReportOptions.__init__(self,name,dbstate) def add_menu_options(self,menu,dbstate): category_name = _("Report Options") - filter = PersonFilterOption(_("Filter"),dbstate,0,False) - filter.set_help(_("Determine what people will be included in " - "the report")) - menu.add_option(category_name,"filter", filter) + self.__pid = PersonOption(_("Filter Person")) + self.__pid.set_help(_("The center person for the filter")) + menu.add_option(category_name, "pid", self.__pid) + self.__pid.connect('value-changed', self.__update_filters) + + self.__filter = PersonFilterOption(_("Filter"), 0) + self.__filter.set_help( + _("Determines what people are included in the report")) + self.__update_filters() + menu.add_option(category_name, "filter", self.__filter) + self.__filter.connect('value-changed', self.__filter_changed) sortby = EnumeratedListOption(_('Sort by'), 0 ) idx = 0 @@ -314,6 +325,29 @@ class TimeLineOptions(MenuReportOptions): sortby.set_help( _("Sorting method to use")) menu.add_option(category_name,"sortby",sortby) + def __update_filters(self): + """ + Update the filter list based on the selected person + """ + _db = self.__dbstate.get_database() + gid = self.__pid.get_value() + person = _db.get_person_from_gramps_id(gid) + filter_list = ReportUtils.get_person_filters(person, False) + self.__filter.set_filters(filter_list) + + def __filter_changed(self): + """ + Handle filter change. If the filter is not specific to a person, + disable the person option + """ + filter_value = self.__filter.get_value() + if filter_value in [1, 2, 3, 4]: + # Filters 1, 2, 3 and 4 rely on the center person + self.__pid.set_available(True) + else: + # The rest don't + self.__pid.set_available(False) + def make_default_style(self,default_style): """Make the default output style for the Timeline report.""" # Paragraph Styles