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
This commit is contained in:
Zsolt Foldvari 2008-05-27 19:53:25 +00:00
parent 998908175f
commit 12369d04a8
18 changed files with 681 additions and 340 deletions

View File

@ -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

View File

@ -20,6 +20,8 @@
# $Id$
__all__ = ["IconButton", "WarnButton", "SimpleButton", "PrivacyButton"]
#-------------------------------------------------------------------------
#
# Standard python modules

View File

@ -20,6 +20,8 @@
# $Id$
__all__ = ["ExpandCollapseArrow"]
#-------------------------------------------------------------------------
#
# Standard python modules

View File

@ -20,6 +20,9 @@
# $Id$
__all__ = ["LinkLabel", "EditLabel", "BasicLabel", "GenderLabel",
"MarkupLabel", "DualMarkupLabel"]
#-------------------------------------------------------------------------
#
# Standard python modules

View File

@ -20,6 +20,8 @@
# $Id$
__all__ = ["LinkBox"]
#-------------------------------------------------------------------------
#
# Standard python modules

View File

@ -20,6 +20,10 @@
# $Id$
__all__ = ["MonitoredCheckbox", "MonitoredEntry", "MonitoredSpinButton",
"MonitoredText", "MonitoredType", "MonitoredDataType",
"MonitoredMenu", "MonitoredStrMenu", "MonitoredDate"]
#-------------------------------------------------------------------------
#
# Standard python modules

View File

@ -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)

View File

@ -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)

View File

@ -20,6 +20,8 @@
# $Id$
__all__ = ["Statusbar"]
#-------------------------------------------------------------------------
#
# Standard python modules

View File

@ -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

View File

@ -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 = '''
<toolitem action="%d"/>
<toolitem action="spring"/>
<toolitem action="clear"/>
<toolitem action="test"/>
</toolbar>
</ui>
''' % (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
return (size > 0) and (size < 73)

View File

@ -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)

View File

@ -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()

View File

@ -20,6 +20,8 @@
# $Id$
__all__ = ["IntEdit", "TypeCellRenderer"]
#-------------------------------------------------------------------------
#
# Standard python modules

View File

@ -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()
# <hack description="set the GTK_ENTRY (self._entry)->is_cell_renderer
# <hack description="set the GTK_ENTRY(self._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)

View File

@ -20,6 +20,8 @@
# $Id$
__all__ = ["MaskedEntry", "ValidatableMaskedEntry"]
#-------------------------------------------------------------------------
#
# Standard python modules

167
src/widgets/valueaction.py Normal file
View File

@ -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

View File

@ -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