From 12369d04a87e6bd811c401e8a1d5d2f33e73c838 Mon Sep 17 00:00:00 2001 From: Zsolt Foldvari Date: Tue, 27 May 2008 19:53:25 +0000 Subject: [PATCH] Few changes in the 'widgets' package. * introducing __all__ in modules, so __init__ imports only public objects; * MultiTypeComboEntry renamed to ValidatedComboEntry and different data type support implemented; * ShortlistComboEntry introduced; * ValueAction and ValueToolItem introduced; * ComboToolAction and ComboToolItem are removed, ValueAction and ToolComboEntry are implemented instead; svn: r10763 --- src/widgets/__init__.py | 18 +- src/widgets/buttons.py | 2 + src/widgets/expandcollapsearrow.py | 2 + src/widgets/labels.py | 3 + src/widgets/linkbox.py | 2 + src/widgets/monitoredwidgets.py | 4 + src/widgets/shortlistcomboentry.py | 123 ++++++++ src/widgets/springseparator.py | 76 +++++ src/widgets/statusbar.py | 2 + src/widgets/styledtextbuffer.py | 3 + src/widgets/styledtexteditor.py | 63 ++-- src/widgets/toolbarwidgets.py | 271 ------------------ src/widgets/toolcomboentry.py | 77 +++++ src/widgets/unused.py | 2 + ...pecomboentry.py => validatedcomboentry.py} | 110 ++++--- src/widgets/validatedmaskedentry.py | 2 + src/widgets/valueaction.py | 167 +++++++++++ src/widgets/valuetoolitem.py | 94 ++++++ 18 files changed, 681 insertions(+), 340 deletions(-) create mode 100644 src/widgets/shortlistcomboentry.py create mode 100644 src/widgets/springseparator.py delete mode 100644 src/widgets/toolbarwidgets.py create mode 100644 src/widgets/toolcomboentry.py rename src/widgets/{multitypecomboentry.py => validatedcomboentry.py} (66%) create mode 100644 src/widgets/valueaction.py create mode 100644 src/widgets/valuetoolitem.py diff --git a/src/widgets/__init__.py b/src/widgets/__init__.py index 21de814ee..8bc7369ef 100644 --- a/src/widgets/__init__.py +++ b/src/widgets/__init__.py @@ -22,18 +22,22 @@ """Custom widgets.""" -from monitoredwidgets import * -from labels import * from buttons import * -from expandcollapsearrow import ExpandCollapseArrow -from linkbox import LinkBox +from expandcollapsearrow import * +from labels import * +from linkbox import * +from monitoredwidgets import * +from shortlistcomboentry import * +from springseparator import * from statusbar import Statusbar -from validatedmaskedentry import ValidatableMaskedEntry -from multitypecomboentry import MultiTypeComboEntry -from toolbarwidgets import * from styledtextbuffer import * from styledtexteditor import * +from toolcomboentry import * from unused import * +from validatedcomboentry import * +from validatedmaskedentry import * +from valueaction import * +from valuetoolitem import * # Enabling custom widgets to be included in Glade from gtk.glade import set_custom_handler diff --git a/src/widgets/buttons.py b/src/widgets/buttons.py index 1645f2efe..800331e48 100644 --- a/src/widgets/buttons.py +++ b/src/widgets/buttons.py @@ -20,6 +20,8 @@ # $Id$ +__all__ = ["IconButton", "WarnButton", "SimpleButton", "PrivacyButton"] + #------------------------------------------------------------------------- # # Standard python modules diff --git a/src/widgets/expandcollapsearrow.py b/src/widgets/expandcollapsearrow.py index a254cd8b8..44bd66612 100644 --- a/src/widgets/expandcollapsearrow.py +++ b/src/widgets/expandcollapsearrow.py @@ -20,6 +20,8 @@ # $Id$ +__all__ = ["ExpandCollapseArrow"] + #------------------------------------------------------------------------- # # Standard python modules diff --git a/src/widgets/labels.py b/src/widgets/labels.py index db2aa439a..5084660a0 100644 --- a/src/widgets/labels.py +++ b/src/widgets/labels.py @@ -20,6 +20,9 @@ # $Id$ +__all__ = ["LinkLabel", "EditLabel", "BasicLabel", "GenderLabel", + "MarkupLabel", "DualMarkupLabel"] + #------------------------------------------------------------------------- # # Standard python modules diff --git a/src/widgets/linkbox.py b/src/widgets/linkbox.py index b753bfd80..a6dab2bed 100644 --- a/src/widgets/linkbox.py +++ b/src/widgets/linkbox.py @@ -20,6 +20,8 @@ # $Id$ +__all__ = ["LinkBox"] + #------------------------------------------------------------------------- # # Standard python modules diff --git a/src/widgets/monitoredwidgets.py b/src/widgets/monitoredwidgets.py index 0ad85310f..246f6ac59 100644 --- a/src/widgets/monitoredwidgets.py +++ b/src/widgets/monitoredwidgets.py @@ -20,6 +20,10 @@ # $Id$ +__all__ = ["MonitoredCheckbox", "MonitoredEntry", "MonitoredSpinButton", + "MonitoredText", "MonitoredType", "MonitoredDataType", + "MonitoredMenu", "MonitoredStrMenu", "MonitoredDate"] + #------------------------------------------------------------------------- # # Standard python modules diff --git a/src/widgets/shortlistcomboentry.py b/src/widgets/shortlistcomboentry.py new file mode 100644 index 000000000..2df87f4c4 --- /dev/null +++ b/src/widgets/shortlistcomboentry.py @@ -0,0 +1,123 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2008 Zsolt Foldvari +# +# 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$ + +"ShortlistComboEntry class." + +__all__ = ["ShortlistComboEntry"] + +#------------------------------------------------------------------------- +# +# Python modules +# +#------------------------------------------------------------------------- +import logging +_LOG = logging.getLogger(".widgets.shortlistcomboentry") + +#------------------------------------------------------------------------- +# +# GTK modules +# +#------------------------------------------------------------------------- +import gobject +import gtk + +#------------------------------------------------------------------------- +# +# GRAMPS modules +# +#------------------------------------------------------------------------- +from widgets.validatedcomboentry import ValidatedComboEntry + +#------------------------------------------------------------------------- +# +# Constants +# +#------------------------------------------------------------------------- +_GTYPE = { + str: gobject.TYPE_STRING, + unicode: gobject.TYPE_STRING, + int: gobject.TYPE_INT, + long: gobject.TYPE_INT64, + float: gobject.TYPE_FLOAT, +} + +(COLUMN_ITEM, + COLUMN_IS_SEP,) = range(2) + +#------------------------------------------------------------------------- +# +# ShortlistComboEntry class +# +#------------------------------------------------------------------------- +class ShortlistComboEntry(ValidatedComboEntry): + """A ComboboxEntry class with optional shortlist. + """ + __gtype_name__ = "ShortlistComboEntry" + + def __init__(self, items, shortlist=True, validator=None): + if not items: + raise ValueError + + data_type = items[0].__class__ + gtype = _GTYPE.get(data_type, gobject.TYPE_PYOBJECT) + + # create the model and insert the items + model = gtk.ListStore(gtype, gobject.TYPE_BOOLEAN) + for item in items: + model.append((item, False)) + + ValidatedComboEntry.__init__(self, data_type, model, + COLUMN_ITEM, validator) + if shortlist: + self._shortlist = [] + self.connect("changed", self._on_combobox_changed) + + self.set_row_separator_func(self._is_row_separator) + + def _on_combobox_changed(self, combobox): + if self._internal_change: + return + + if self.get_active_iter(): + model = self.get_model() + + # if first item on shortlist insert a separator row + if not self._shortlist: + model.prepend((None, True)) + + # remove the existing shortlist from the model + iter = model.get_iter_first() + for n in range(len(self._shortlist)): + model.remove(iter) + + # update shortlist + if self._active_data in self._shortlist: + self._shortlist.remove(self._active_data) + self._shortlist.append(self._active_data) + self._shortlist = self._shortlist[-5:] + + # prepend shortlist to model + for data in self._shortlist: + model.prepend((data, False)) + + def _is_row_separator(self, model, iter): + return model.get_value(iter, COLUMN_IS_SEP) diff --git a/src/widgets/springseparator.py b/src/widgets/springseparator.py new file mode 100644 index 000000000..69bbb8f96 --- /dev/null +++ b/src/widgets/springseparator.py @@ -0,0 +1,76 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2008 Zsolt Foldvari +# +# 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$ + +"Separator classes used for Toolbar." + +__all__ = ["SpringSeparatorAction", "SpringSeparatorToolItem"] + +#------------------------------------------------------------------------- +# +# Python modules +# +#------------------------------------------------------------------------- +import logging +_LOG = logging.getLogger(".widgets.springseparator") + +#------------------------------------------------------------------------- +# +# GTK modules +# +#------------------------------------------------------------------------- +import gtk + +#------------------------------------------------------------------------- +# +# SpringSeparatorToolItem class +# +#------------------------------------------------------------------------- +class SpringSeparatorToolItem(gtk.SeparatorToolItem): + """Custom separator toolitem. + + Its only purpose is to push following tool items to the right end + of the toolbar. + + """ + __gtype_name__ = "SpringSeparatorToolItem" + + def __init__(self): + gtk.SeparatorToolItem.__init__(self) + + self.set_draw(False) + self.set_expand(True) + +#------------------------------------------------------------------------- +# +# SpringSeparatorAction class +# +#------------------------------------------------------------------------- +class SpringSeparatorAction(gtk.Action): + """Custom Action to hold a SpringSeparatorToolItem.""" + + __gtype_name__ = "SpringSeparatorAction" + + def __init__(self, name, label, tooltip, stock_id): + gtk.Action.__init__(self, name, label, tooltip, stock_id) + +SpringSeparatorAction.set_tool_item_type(SpringSeparatorToolItem) + diff --git a/src/widgets/statusbar.py b/src/widgets/statusbar.py index a58d70382..95e823eb0 100644 --- a/src/widgets/statusbar.py +++ b/src/widgets/statusbar.py @@ -20,6 +20,8 @@ # $Id$ +__all__ = ["Statusbar"] + #------------------------------------------------------------------------- # # Standard python modules diff --git a/src/widgets/styledtextbuffer.py b/src/widgets/styledtextbuffer.py index 77a9c6668..abefb4871 100644 --- a/src/widgets/styledtextbuffer.py +++ b/src/widgets/styledtextbuffer.py @@ -22,6 +22,9 @@ "Text buffer subclassed from gtk.TextBuffer handling L{StyledText}." +__all__ = ["ALLOWED_STYLES", "MATCH_START", "MATCH_END", "MATCH_FLAVOR", + "MATCH_STRING", "StyledTextBuffer"] + #------------------------------------------------------------------------- # # Python modules diff --git a/src/widgets/styledtexteditor.py b/src/widgets/styledtexteditor.py index d77064dc8..32ddbbe1c 100644 --- a/src/widgets/styledtexteditor.py +++ b/src/widgets/styledtexteditor.py @@ -22,6 +22,8 @@ "Text editor subclassed from gtk.TextView handling L{StyledText}." +__all__ = ["StyledTextEditor"] + #------------------------------------------------------------------------- # # Python modules @@ -50,7 +52,9 @@ from gen.lib import StyledTextTagType from widgets.styledtextbuffer import (StyledTextBuffer, ALLOWED_STYLES, MATCH_START, MATCH_END, MATCH_FLAVOR, MATCH_STRING) -from widgets.toolbarwidgets import (ComboToolAction, SpringSeparatorAction) +from widgets.valueaction import ValueAction +from widgets.toolcomboentry import ToolComboEntry +from widgets.springseparator import SpringSeparatorAction from Spell import Spell from GrampsDisplay import url as display_url @@ -74,6 +78,7 @@ FORMAT_TOOLBAR = ''' + ''' % (StyledTextTagType.ITALIC, @@ -354,27 +359,29 @@ class StyledTextEditor(gtk.TextView): # ...last the custom actions, which have custom proxies items = [f.get_name() for f in self.get_pango_context().list_families()] + items.sort() default = StyledTextTagType.STYLE_DEFAULT[StyledTextTagType.FONTFACE] - fontface_action = ComboToolAction(str(StyledTextTagType.FONTFACE), - _("Font family"), - _("Font family"), - None, - items, - str(default), - editable=False) - fontface_action.connect('activate', self._on_comboaction_activate) + fontface_action = ValueAction(str(StyledTextTagType.FONTFACE), + _("Font family"), + default, + ToolComboEntry, + items, + False, #editable + True, #shortlist + None) # validator + fontface_action.connect('changed', self._on_comboaction_changed) - items = [str(size) for size in FONT_SIZES] + items = FONT_SIZES default = StyledTextTagType.STYLE_DEFAULT[StyledTextTagType.FONTSIZE] - fontsize_action = ComboToolAction(str(StyledTextTagType.FONTSIZE), - _("Font size"), - _("Font size"), - None, - items, - str(default), - sortable=False, - validator=is_valid_fontsize) - fontsize_action.connect('activate', self._on_comboaction_activate) + fontsize_action = ValueAction(str(StyledTextTagType.FONTSIZE), + _("Font size"), + default, + ToolComboEntry, + items, + True, #editable + False, #shortlist + is_valid_fontsize) #validator + fontsize_action.connect('changed', self._on_comboaction_changed) spring = SpringSeparatorAction("spring", "", "", None) @@ -392,7 +399,7 @@ class StyledTextEditor(gtk.TextView): self.action_group.add_action(fontface_action) self.action_group.add_action(fontsize_action) self.action_group.add_action(spring) - + # define the toolbar and create the proxies via ensure_update() uimanager = gtk.UIManager() uimanager.insert_action_group(self.action_group, 0) @@ -488,16 +495,16 @@ class StyledTextEditor(gtk.TextView): (style, str(value))) self.textbuffer.apply_style(style, value) - def _on_comboaction_activate(self, action): + def _on_comboaction_changed(self, action): """Apply a format set by a ComboToolAction type of action.""" if self._internal_style_change: return style = int(action.get_name()) - text = action.get_active_value() + value = action.get_value() try: - value = StyledTextTagType.STYLE_TYPE[style](text) + value = StyledTextTagType.STYLE_TYPE[style](value) _LOG.debug("applying style '%d' with value '%s'" % (style, str(value))) self.textbuffer.apply_style(style, value) @@ -528,7 +535,7 @@ class StyledTextEditor(gtk.TextView): (style == StyledTextTagType.FONTSIZE)): action = self.action_group.get_action(str(style)) self._internal_style_change = True - action.set_active_value(str(changed_styles[style])) + action.set_value(changed_styles[style]) self._internal_style_change = False def _spell_change_cb(self, menuitem, language): @@ -610,10 +617,6 @@ def hex_to_color(hex): color = gtk.gdk.color_parse(hex) return color -def is_valid_fontsize(text): +def is_valid_fontsize(size): """Validator function for font size selector widget.""" - try: - size = int(text) - return (size > 0) and (size < 73) - except ValueError: - return False \ No newline at end of file + return (size > 0) and (size < 73) diff --git a/src/widgets/toolbarwidgets.py b/src/widgets/toolbarwidgets.py deleted file mode 100644 index d542cefcc..000000000 --- a/src/widgets/toolbarwidgets.py +++ /dev/null @@ -1,271 +0,0 @@ -# -# Gramps - a GTK+/GNOME based genealogy program -# -# Copyright (C) 2008 Zsolt Foldvari -# -# 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$ - -"Widget classes used for Toolbar." - -#------------------------------------------------------------------------- -# -# Python modules -# -#------------------------------------------------------------------------- -import logging -_LOG = logging.getLogger(".widgets.toolbarwidgets") - -#------------------------------------------------------------------------- -# -# GTK modules -# -#------------------------------------------------------------------------- -import gobject -import gtk - -#------------------------------------------------------------------------- -# -# GRAMPS modules -# -#------------------------------------------------------------------------- -from widgets.multitypecomboentry import MultiTypeComboEntry - -#------------------------------------------------------------------------- -# -# Constants -# -#------------------------------------------------------------------------- -(COLUMN_ITEM, - COLUMN_IS_SEP,) = range(2) - -#------------------------------------------------------------------------- -# -# ComboToolItem class -# -#------------------------------------------------------------------------- -class ComboToolItem(gtk.ToolItem): - - __gtype_name__ = "ComboToolItem" - - __gsignals__ = { - 'changed': (gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, #return value - ()), # arguments - } - - def __init__(self, model, editable, validator=None): - gtk.ToolItem.__init__(self) - - self.set_border_width(2) - self.set_homogeneous(False) - self.set_expand(False) - - combo_entry = MultiTypeComboEntry(model, COLUMN_ITEM, validator) - combo_entry.set_focus_on_click(False) - combo_entry.set_entry_editable(editable) - combo_entry.show() - self.add(combo_entry) - - combo_entry.connect('changed', self._on_combo_changed) - - def _on_combo_changed(self, combo_entry): - self.emit('changed') - - def set_active_iter(self, iter): - self.child.set_active_iter(iter) - - def get_active_iter(self): - return self.child.get_active_iter() - - def set_active_text(self, text): - self.child.set_active_text(text) - - def get_active_text(self): - return self.child.get_active_text() - -#------------------------------------------------------------------------- -# -# ComboToolAction class -# -#------------------------------------------------------------------------- -class ComboToolAction(gtk.Action): - - __gtype_name__ = "ComboToolAction" - - def __init__(self, name, label, tooltip, stock_id, items, - default=None, sortable=True, editable=True, - validator=None): - - gtk.Action.__init__(self, name, label, tooltip, stock_id) - - # create the model and insert the items - self.model = gtk.ListStore(gobject.TYPE_STRING, - gobject.TYPE_BOOLEAN) - for item in items: - self.model.append((item, False)) - - # sort the rows if allowed - if sortable: - self.model.set_sort_column_id(COLUMN_ITEM, gtk.SORT_ASCENDING) - self.model.sort_column_changed() - - # set the first row (after sorting) as default if default was not set - if (default is None) or (default not in items): - self.default = self.model.get_value(self.model.get_iter_first(), - COLUMN_ITEM) - else: - self.default = default - - self.set_active_value(self.default) - - # set the first row as separator - self.model.set_value(self.model.get_iter_first(), COLUMN_IS_SEP, True) - - # remember initial parameters - self.editable = editable - self.validator = validator - - def do_create_tool_item(self): - """Create a toolbar item widget that proxies for the given action. - - Override the default method, to be able to pass the required - parameters to the proxy's constructor. - - @returns: a toolbar item connected to the action. - @returntype: ComboToolItem - - """ - combo = ComboToolItem(self.model, self.editable, self.validator) - self.connect_proxy(combo) - return combo - - def connect_proxy(self, proxy): - """Connect a widget to an action object as a proxy. - - @param proxy: widget to be connected - @type proxy: gtk.Widget - - """ - if isinstance(proxy, ComboToolItem): - # do this before hand, so that we don't call the "changed" handler - proxy.set_active_iter(self.active_iter) - proxy.connect('changed', self._on_proxy_changed) - - # if this is called the proxy will appear on the proxy list twice. why? - #gtk.Action.connect_proxy(self, proxy) - - def _on_proxy_changed(self, proxy): - """Signal handler. - - Called when any of the proxies is changed. - - """ - # blocking proxies when they are synchronized from the action - if self._internal_change: - return - - # get active value from the changed proxy - if isinstance(proxy, ComboToolItem): - iter = proxy.get_active_iter() - - if iter is not None: - value = self.model.get_value(iter, COLUMN_ITEM) - else: - value = proxy.get_active_text() - - self.set_active_value(value) - - # emit the 'activate' signal - self.activate() - - def set_active_value(self, value): - """Set the active value of the action. - - Depending wheter the new value is in the model the active_iter - attribute is set to position or set to None. The active_value - attribute will contain the new value independently. - - Proxies are also updated accordingly. - - """ - # check first if the value is in the model - iter = self.model.get_iter_first() - while iter: - if self.model.get_value(iter, COLUMN_ITEM) == value: - break - iter = self.model.iter_next(iter) - - # here iter either points to the model or is set to None - self.active_value = value - self.active_iter = iter - - # update the proxies with signalling loop cut - self._internal_change = True - - for proxy in self.get_proxies(): - if isinstance(proxy, ComboToolItem): - if self.active_iter is not None: - proxy.set_active_iter(self.active_iter) - else: - proxy.set_active_text(self.active_value) - else: - _LOG.warning("Don't know how to activate %s widget" % - proxy.__class__) - - self._internal_change = False - - def get_active_value(self): - return self.active_value - -ComboToolAction.set_tool_item_type(ComboToolItem) - -#------------------------------------------------------------------------- -# -# SpringSeparatorToolItem class -# -#------------------------------------------------------------------------- -class SpringSeparatorToolItem(gtk.SeparatorToolItem): - """Custom separator toolitem. - - Its only purpose is to push following tool items to the right end - of the toolbar. - - """ - __gtype_name__ = "SpringSeparatorToolItem" - - def __init__(self): - gtk.SeparatorToolItem.__init__(self) - - self.set_draw(False) - self.set_expand(True) - -#------------------------------------------------------------------------- -# -# SpringSeparatorAction class -# -#------------------------------------------------------------------------- -class SpringSeparatorAction(gtk.Action): - """Custom Action to hold a SpringSeparatorToolItem.""" - - __gtype_name__ = "SpringSeparatorAction" - - def __init__(self, name, label, tooltip, stock_id): - gtk.Action.__init__(self, name, label, tooltip, stock_id) - -SpringSeparatorAction.set_tool_item_type(SpringSeparatorToolItem) - diff --git a/src/widgets/toolcomboentry.py b/src/widgets/toolcomboentry.py new file mode 100644 index 000000000..dfb727f20 --- /dev/null +++ b/src/widgets/toolcomboentry.py @@ -0,0 +1,77 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2008 Zsolt Foldvari +# +# 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$ + +"ToolComboEntry class." + +__all__ = ["ToolComboEntry"] + +#------------------------------------------------------------------------- +# +# Python modules +# +#------------------------------------------------------------------------- +import logging +_LOG = logging.getLogger(".widgets.toolcomboentry") + +#------------------------------------------------------------------------- +# +# GTK modules +# +#------------------------------------------------------------------------- +#import gobject +import gtk + +#------------------------------------------------------------------------- +# +# GRAMPS modules +# +#------------------------------------------------------------------------- +from widgets.valuetoolitem import ValueToolItem +from widgets.shortlistcomboentry import ShortlistComboEntry + +#------------------------------------------------------------------------- +# +# ToolEntry class +# +#------------------------------------------------------------------------- +class ToolComboEntry(ValueToolItem): + """Tool bar item containing a ShortlistComboEntry widget.""" + __gtype_name__ = "ToolComboEntry" + + def _create_widget(self, items, editable, shortlist=True, validator=None): + self.set_border_width(2) + self.set_homogeneous(False) + self.set_expand(False) + + combo = ShortlistComboEntry(items, shortlist, validator) + combo.set_focus_on_click(False) + combo.set_entry_editable(editable) + combo.show() + self.add(combo) + + combo.connect('changed', self._on_widget_changed) + + def set_value(self, value): + self.child.set_active_data(value) + + def get_value(self): + return self.child.get_active_data() \ No newline at end of file diff --git a/src/widgets/unused.py b/src/widgets/unused.py index cb70cfba3..2ccf41ee0 100644 --- a/src/widgets/unused.py +++ b/src/widgets/unused.py @@ -20,6 +20,8 @@ # $Id$ +__all__ = ["IntEdit", "TypeCellRenderer"] + #------------------------------------------------------------------------- # # Standard python modules diff --git a/src/widgets/multitypecomboentry.py b/src/widgets/validatedcomboentry.py similarity index 66% rename from src/widgets/multitypecomboentry.py rename to src/widgets/validatedcomboentry.py index 80eaf704e..97fcaec0e 100644 --- a/src/widgets/multitypecomboentry.py +++ b/src/widgets/validatedcomboentry.py @@ -20,7 +20,9 @@ # $Id$ -"The MultiTypeComboEntry widget class." +"The ValidatedComboEntry widget class." + +__all__ = ["ValidatedComboEntry"] #------------------------------------------------------------------------- # @@ -28,7 +30,7 @@ # #------------------------------------------------------------------------- import logging -_LOG = logging.getLogger(".widgets.multitypecomboentry") +_LOG = logging.getLogger(".widgets.validatedcomboentry") #------------------------------------------------------------------------- # @@ -39,18 +41,19 @@ import gtk #------------------------------------------------------------------------- # -# MultiTypeComboEntry class +# ValidatedComboEntry class # #------------------------------------------------------------------------- -class MultiTypeComboEntry(gtk.ComboBox, gtk.CellLayout): +class ValidatedComboEntry(gtk.ComboBox, gtk.CellLayout): """A ComboBoxEntry widget with validation. - MultiTypeComboEntry may have data type other then string (tbd.). + ValidatedComboEntry may have data type other then string, and is set + with the C{datatype} contructor parameter. Its behaviour is different from gtk.ComboBoxEntry in the way how the entry part of the widget is handled. While gtk.ComboBoxEntry emits the 'changed' signal immediatelly the text in the entry is - changed, MultiTypeComboEntry emits the signal only after the text is + changed, ValidatedComboEntry emits the signal only after the text is activated (enter is pressed, the focus is moved out) and validated. Validation function is an optional feature and activated only if a @@ -60,13 +63,13 @@ class MultiTypeComboEntry(gtk.ComboBox, gtk.CellLayout): L{set_entry_editable} method. """ - __gtype_name__ = "MultiTypeComboEntry" + __gtype_name__ = "ValidatedComboEntry" - def __init__(self, model=None, column=-1, validator=None): + def __init__(self, datatype, model=None, column=-1, validator=None): gtk.ComboBox.__init__(self, model) self._entry = gtk.Entry() - # is_cell_renderer # flag to TRUE in order to tell the entry to fill its allocation."> dummy_event = gtk.gdk.Event(gtk.gdk.NOTHING) self._entry.start_editing(dummy_event) @@ -77,9 +80,12 @@ class MultiTypeComboEntry(gtk.ComboBox, gtk.CellLayout): self._text_renderer = gtk.CellRendererText() self.pack_start(self._text_renderer, False) - self._text_column = -1 - self.set_text_column(column) + self._data_type = datatype + self._data_column = -1 + self.set_data_column(column) + self._active_text = '' + self._active_data = None self.set_active(-1) self._validator = validator @@ -88,7 +94,8 @@ class MultiTypeComboEntry(gtk.ComboBox, gtk.CellLayout): self._entry.connect('focus-in-event', self._on_entry_focus_in_event) self._entry.connect('focus-out-event', self._on_entry_focus_out_event) self._entry.connect('key-press-event', self._on_entry_key_press_event) - self.changed_cb_id = self.connect('changed', self._on_changed) + self.connect('changed', self._on_changed) + self._internal_change = False self._has_frame_changed() self.connect('notify', self._on_notify) @@ -139,6 +146,8 @@ class MultiTypeComboEntry(gtk.ComboBox, gtk.CellLayout): # FIXME Escape never reaches here, the dialog eats it, I assume. if event.keyval == gtk.keysyms.Escape: entry.set_text(self._active_text) + entry.set_position(-1) + return True return False @@ -148,10 +157,14 @@ class MultiTypeComboEntry(gtk.ComboBox, gtk.CellLayout): Called when the active row is changed in the combo box. """ + if self._internal_change: + return + iter = self.get_active_iter() if iter: model = self.get_model() - self._active_text = model.get_value(iter, self._text_column) + self._active_data = model.get_value(iter, self._data_column) + self._active_text = str(self._active_data) self._entry.set_text(self._active_text) def _on_notify(self, object, gparamspec): @@ -168,45 +181,78 @@ class MultiTypeComboEntry(gtk.ComboBox, gtk.CellLayout): def _entry_changed(self, entry): new_text = entry.get_text() - if (self._validator is not None) and not self._validator(new_text): + try: + new_data = self._data_type(new_text) + + if (self._validator is not None) and not self._validator(new_data): + raise ValueError + except ValueError: entry.set_text(self._active_text) + entry.set_position(-1) return self._active_text = new_text - self.handler_block(self.changed_cb_id) - self.set_active(-1) - self.handler_unblock(self.changed_cb_id) + self._active_data = new_data + + self._internal_change = True + new_iter = self._is_in_model(new_data) + if new_iter is None: + self.set_active(-1) + else: + self.set_active_iter(new_iter) + self._internal_change = False def _has_frame_changed(self): has_frame = self.get_property('has-frame') self._entry.set_has_frame(has_frame) + + def _is_in_model(self, data): + """Check if given data is in the model or not. + + @param data: data value to check + @type data: depends on the actual data type of the object + @returns: position of 'data' in the model + @returntype: gtk.TreeIter or None + + """ + model = self.get_model() + + iter = model.get_iter_first() + while iter: + if model.get_value(iter, self._data_column) == data: + break + iter = model.iter_next(iter) + + return iter # Public methods - def set_text_column(self, text_column): - if text_column < 0: + def set_data_column(self, data_column): + if data_column < 0: return - if text_column > self.get_model().get_n_columns(): + model = self.get_model() + if model is None: return - if self._text_column == -1: - self._text_column = text_column - self.set_attributes(self._text_renderer, text=text_column) + if data_column > model.get_n_columns(): + return - def get_text_column(self): - return self._text_column + if self._data_column == -1: + self._data_column = data_column + self.set_attributes(self._text_renderer, text=data_column) + + def get_data_column(self): + return self._data_column - def set_active_text(self, text): + def set_active_data(self, data): + # set it via entry so that it will be also validated if self._entry: - self._entry.set_text(text) + self._entry.set_text(str(data)) self._entry_changed(self._entry) - def get_active_text(self): - if self._entry: - return self._entry.get_text() - - return None + def get_active_data(self): + return self._active_data def set_entry_editable(self, is_editable): self._entry.set_editable(is_editable) diff --git a/src/widgets/validatedmaskedentry.py b/src/widgets/validatedmaskedentry.py index 3299338d0..d0d6421cf 100644 --- a/src/widgets/validatedmaskedentry.py +++ b/src/widgets/validatedmaskedentry.py @@ -20,6 +20,8 @@ # $Id$ +__all__ = ["MaskedEntry", "ValidatableMaskedEntry"] + #------------------------------------------------------------------------- # # Standard python modules diff --git a/src/widgets/valueaction.py b/src/widgets/valueaction.py new file mode 100644 index 000000000..20d0e73fc --- /dev/null +++ b/src/widgets/valueaction.py @@ -0,0 +1,167 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2008 Zsolt Foldvari +# +# 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$ + +"ValueAction class." + +__all__ = ["ValueAction"] + +#------------------------------------------------------------------------- +# +# Python modules +# +#------------------------------------------------------------------------- +import logging +_LOG = logging.getLogger(".widgets.valueaction") + +#------------------------------------------------------------------------- +# +# GTK modules +# +#------------------------------------------------------------------------- +import gobject +import gtk + +#------------------------------------------------------------------------- +# +# GRAMPS modules +# +#------------------------------------------------------------------------- +from widgets.valuetoolitem import ValueToolItem + +#------------------------------------------------------------------------- +# +# ValueAction class +# +#------------------------------------------------------------------------- +class ValueAction(gtk.Action): + """Value action class. + + (A ValueAction with menu item doesn't make any sense.) + + """ + __gtype_name__ = "ValueAction" + + __gsignals__ = { + 'changed': (gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, #return value + ()), # arguments + } + + def __init__(self, name, tooltip, default, itemtype, *args): + """Create a new ValueAction instance. + + @param name: the name of the action + @type name: str + @param tooltip: tooltip string + @type tooltip: str + @param default: default value for the action, it will set the type of + the action and thus the type of all the connected proxies. + @type default: set by itemtype + @param itemtype: default tool item class + @type itemtype: ValueToolItem subclass + @param args: arguments to be passed to the default toolitem class + at creation. see: L{do_create_tool_item} + @type args: list + + """ + gtk.Action.__init__(self, name, '', tooltip, None) + + self._value = default + self._data_type = type(default) + + # have to be remembered, because we can't access + # GtkAction->toolbar_item_type later. + self._default_toolitem_type = itemtype + self.set_tool_item_type(itemtype) + self._args_for_toolitem = args + + self._handlers = {} + + def do_changed(self): + """Default signal handler for 'changed' signal. + + Synchronize all the proxies with the active value. + + """ + for proxy in self.get_proxies(): + proxy.handler_block(self._handlers[proxy]) + proxy.set_value(self._value) + proxy.handler_unblock(self._handlers[proxy]) + + def do_create_tool_item(self): + """Create a 'default' toolbar item widget. + + Override the default method, to be able to pass the required + parameters to the proxy's constructor. + + This method is called from gtk.UIManager.ensure_update(), when a + 'toolitem' is found in the UI definition with a name refering to a + ValueAction. Thus, to use the action via the UIManager a 'default' + toolitem type has to be set with the gtk.Action.set_tool_item_type() + method, before invoking the gtk.UIManager.ensure_update() method. + + Widgets other than the default type has to be created and added + manually with the gtk.Action.connect_proxy() method. + + @returns: a toolbar item connected to the action. + @returntype: L{ValueToolItem} subclass + + """ + proxy = self._default_toolitem_type(self._data_type, + self._args_for_toolitem) + self.connect_proxy(proxy) + return proxy + + def _on_proxy_changed(self, proxy): + """Signal handler for the proxies' 'changed' signal.""" + value = proxy.get_value() + if value is not None: + self.set_value(value) + + def connect_proxy(self, proxy): + """Connect a widget to an action object as a proxy. + + @param proxy: widget to be connected + @type proxy: L{ValueToolItem} subclass + + """ + if not isinstance(proxy, ValueToolItem): + raise TypeError + + # do this before connecting, so that we don't call the handler + proxy.set_value(self._value) + self._handlers[proxy] = proxy.connect('changed', self._on_proxy_changed) + + # if this is called the proxy will appear on the proxy list twice. why? + #gtk.Action.connect_proxy(self, proxy) + + def set_value(self, value): + """Set value to action.""" + if not isinstance(value, self._data_type): + raise TypeError + + self._value = value + self.emit('changed') + + def get_value(self): + """Get the value from the action.""" + return self._value \ No newline at end of file diff --git a/src/widgets/valuetoolitem.py b/src/widgets/valuetoolitem.py new file mode 100644 index 000000000..655bbb5e4 --- /dev/null +++ b/src/widgets/valuetoolitem.py @@ -0,0 +1,94 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2008 Zsolt Foldvari +# +# 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$ + +"ValueToolItem class." + +__all__ = ["ValueToolItem"] + +#------------------------------------------------------------------------- +# +# Python modules +# +#------------------------------------------------------------------------- +import logging +_LOG = logging.getLogger(".widgets.valuetoolitem") + +#------------------------------------------------------------------------- +# +# GTK modules +# +#------------------------------------------------------------------------- +import gobject +import gtk + + +#------------------------------------------------------------------------- +# +# ValueToolItem class +# +#------------------------------------------------------------------------- +class ValueToolItem(gtk.ToolItem): + """ValueToolItem is an abstract toolbar proxy for ValueAction. + + For each kind of widget a separete tool item proxy has to be + subclassed from this ValueToolItem. + + """ + __gtype_name__ = "ValueToolItem" + + __gsignals__ = { + 'changed': (gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, #return value + ()), # arguments + } + + def __init__(self, data_type, args): + gtk.ToolItem.__init__(self) + + self._data_type = data_type + + self._create_widget(*args) + + def _on_widget_changed(self, widget): + self.emit('changed') + + def _create_widget(self, args): + """Create the apropriate widget for the actual proxy.""" + raise NotImplementedError + + def set_value(self, value): + """Set new value for the proxied widget. + + The method is responsible converting the data type between action and + widget. + + """ + raise NotImplementedError + + def get_value(self): + """Get value from the proxied widget. + + The method is responsible converting the data type between action and + widget. + + """ + raise NotImplementedError