Source code for gramps.gui.widgets.validatedcomboentry

#
# 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#

"The ValidatedComboEntry widget class."

__all__ = ["ValidatedComboEntry"]

#-------------------------------------------------------------------------
#
# Python modules
#
#-------------------------------------------------------------------------
import logging
_LOG = logging.getLogger(".widgets.validatedcomboentry")

#-------------------------------------------------------------------------
#
# GTK modules
#
#-------------------------------------------------------------------------
from gi.repository import GObject
from gi.repository import Gdk
from gi.repository import Gtk

#-------------------------------------------------------------------------
#
# ValidatedComboEntry class
#
#-------------------------------------------------------------------------
[docs]class ValidatedComboEntry(Gtk.ComboBox): """ A ComboBoxEntry widget with validation. ValidatedComboEntry may have data type other then string, and is set with the ``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, 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 validator function is given at instantiation. The entry can be set as editable or not editable using the :meth:`set_entry_editable` method. """ __gtype_name__ = "ValidatedComboEntry" def __init__(self, datatype, model=None, column=-1, validator=None, width=-1): #GObject.GObject.__init__(self, model) Gtk.ComboBox.__init__(self, model=model) self._entry = Gtk.Entry() self._entry.set_width_chars(width) # <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 = Gdk.Event(Gdk.EventType.NOTHING) self._entry.start_editing(dummy_event) # </hack> self.add(self._entry) self._entry.show() self._text_renderer = Gtk.CellRendererText() self.pack_start(self._text_renderer, False) 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 self._entry.connect('activate', self._on_entry_activate) 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.connect('changed', self._on_changed) self._internal_change = False self._has_frame_changed() self.connect('notify', self._on_notify) # Virtual overriden methods
[docs] def do_mnemonic_activate(self, group_cycling): self._entry.grab_focus() return True
[docs] def do_grab_focus(self): self._entry.grab_focus() # Signal handlers
def _on_entry_activate(self, entry): """ Signal handler. Called when the entry is activated. """ self._entry_changed(entry) def _on_entry_focus_in_event(self, widget, event): """ Signal handler. Called when the focus enters the entry, and is used for saving the entry's text for later comparison. """ self._text_on_focus_in = self._entry.get_text() def _on_entry_focus_out_event(self, widget, event): """ Signal handler. Called when the focus leaves the entry. """ if (self._entry.get_text() != self._text_on_focus_in): self._entry_changed(widget) def _on_entry_key_press_event(self, entry, event): """ Signal handler. Its purpose is to handle escape button. """ # FIXME Escape never reaches here, the dialog eats it, I assume. if event.keyval == Gdk.KEY_Escape: entry.set_text(self._active_text) entry.set_position(-1) return True return False def _on_changed(self, combobox): """ Signal handler. 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_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): """ Signal handler. Called whenever a property of the object is changed. """ if gparamspec and gparamspec.name == 'has-frame': self._has_frame_changed() # Private methods def _entry_changed(self, entry): new_text = entry.get_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._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 :rtype: 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
[docs] def set_data_column(self, data_column): if data_column < 0: return model = self.get_model() if model is None: return if data_column > model.get_n_columns(): return if self._data_column == -1: self._data_column = data_column self.add_attribute(self._text_renderer, "text", data_column)
[docs] def get_data_column(self): return self._data_column
[docs] def set_active_data(self, data): # set it via entry so that it will be also validated if self._entry: self._entry.set_text(str(data)) self._entry_changed(self._entry)
[docs] def get_active_data(self): return self._active_data
[docs] def set_entry_editable(self, is_editable): self._entry.set_editable(is_editable)