Re: Validate entry in Gtk::Entry



On 03/21/01 Chris Dunlop wrote:
I'm trying to do input checking for a Gtk::Entry widget.  The checking
is for a valid date, so I need to check the field once all changes have
been made rather than as each individual change is made.  I thought the
way to do this would be be to use the "focus_out_event" to do the
checking, and prevent the focus leaving the widget if the input wasn't
valid.  My code is like this:
[...]
    if ($self->{changed}) {
      if (...invalid input...) {
      print "Uh-oh.  Invalid input...\n";
      $self->signal_emit_stop_by_name("focus_out_event");
      $self->grab_focus();
      return 1;
[...]
This _nearly_ works: the widget I'm trying to prevent the focus from
leaving looks to be still selected ie. it has a highlight around it, and
it still has a vertical cursor in it.  However the following widget
(also a Gtk::Entry widget) is _also_ selected, and _also_ has a cursor
in it.  And when I start typing, the characters go into the following
widget rather than the original one I'm interested in.  Bummer.  Also,
if I switch the focus between my application and something else on my
screen (eg. clicking on an XTerm), the "phantom" highlighting and cursor
stay on the original widget but come and go on the following widget.  So
I guess it's a bug, but I've no idea if it's in my code (quite likely!)
or in Glade-Perl or Gtk-Perl or Gtk...

IMHO, it's a "bug" in Gtk+. Grabbing the focus on the widget while
the focus is being transfered on another one confuses the toolkit.
There is a workaround I used quite some time ago: grab the focus in
an idle event:
        Gtk->idle_add(sub {$self->grab_focus; return 0});

You should probably add also a cue for the user, maybe a beep or
something like that or he may get confused too:-)

"signal_emit_stop_by_name" is only mentioned in the one place in the
tutorial but it's not clear when or where it should be used. I tried
searching for it in the the list archives at:

  http://mail.gnome.org/archives/gtk-perl-list/

You may want to search the C gtk lists too. Anyway signal_emit_stop_by_name
is used to stop the current emission of the named signal: that means that
the other signal handlers installed for that signal will not be called
(not even the handler defined in the widget class itself). This is often
needed when handling 'event' signals whre returning a TRUE value only
stops the event propagation to the toplevel widget and not the
signal emission itself: ie returning TRUE there will result in the
other signal handlers running anyway. Gtk 2.0 should change this mechanism
somewhat, though.

I got a bit excited when I found:

  GTK FAQ: 6.15. How do I validate/limit/filter the input to a GtkEntry?
  http://www.gtk.org/faq/#AEN812

But unfortunately I can't relate the Gtk-Perl interface to the sample C
code.

I added this recipe to Gtk::cookbook that should get you started
(it requires a fix in cvs to correctly position the cursor, though):

=head2 Validating input in a text entry

B<Problem:>

You want to restrict the characters inserted in a text entry field to a
set of valid ones.

B<Solution:>

Connect to the C<text-insert> signal, check the text for invalid characters
and insert only the valid ones.

        use Gtk -init;

        my $window = new Gtk::Window;
        $window->signal_connect('destroy', sub {Gtk->main_quit});
        my $entry = new Gtk::Entry;
        # we store the signal connection id in the entry for later
        $entry->{signalid} = $entry->signal_connect (
                insert_text => \&validate);
        $window->add($entry);
        $window->show_all;
        Gtk->main;

        # We allow only uppercase characters in the entry
        sub validate {
                my ($entry, $text, $len, $pos) = @_;
                my $newtext = uc $text;
                $newtext =~ s/[^A-Z]//g;
                if (length($newtext)) {
                        # we temporarily block the signal to avoid recursion
                        $entry->signal_handler_block($entry->{signalid});
                        $pos = $entry->insert_text($newtext, $pos);
                        $entry->signal_handler_unblock($entry->{signalid});
                }
                # we already inserted the text if it was valid: no need
                # for the entry to process this signal emission
                $entry->signal_emit_stop_by_name('insert-text');
                # return the new position of the cursor here
                $pos;
        }

lupus

-- 
-----------------------------------------------------------------
lupus debian org                                     debian/rules
lupus ximian com                             Monkeys do it better




[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]