Field-level validation



This patch provides basic support for field-level validation.

That is, any widget can attach one or more handlers to the "validate"
signal.  If any of these handles returns FALSE, then the keyboard focus
will not leave the widget.

This patch only affects keyboard focus.  Specifically, it does not
prevent the user from pressing buttons or activating menu items (since
those buttons or menu items might be useful things like `cancel',
`reset', `revert', `copy', `paste' etc. etc. etc.)  It is up to the
individual applications (that want to use field-level validation) to
make sure that actions check the fields first, if necessary.

I've tested it on Linux/X11, and it seems to work properly.
Specifically, the focus stays put and the widget does not receive the focus-out-event if validate returns FALSE.

I tested an earlier version on Win32, and it also seems to work
properly, but I haven't tested this exact version.

It works on the assumption that all user-initiated focus changes go
through either gtk_window_set_focus or gtk_window_move_focus.  This
assumption seems to hold for X11 and Win32, but I have no idea about
nanox or fb.

Comments?

Thanks,
Steven
Index: gtk/gtkcontainer.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkcontainer.c,v
retrieving revision 1.75
diff -r1.75 gtkcontainer.c
109c109,110
< 
---
> static void   gtk_container_validate_children_cb (GtkWidget *widget,
> 						  gpointer user_data);
2226a2228,2266
>     }
> }
> 
> /**
>  * gtk_container_validate_children:
>  * @container: a #GtkContainer
>  * @returns: the first invalid child found, or NULL
>  *
>  * This routine is used for field-level validation.  It validates all
>  * of the container's children (and if they are containers, each of their
>  * children) until it finds one which is invalid.
>  */
> GtkWidget *
> gtk_container_validate_children (GtkContainer *container)
> {
>   GtkWidget *retval = NULL;
> 
>   gtk_container_foreach (container, 
> 			 gtk_container_validate_children_cb,
> 			 &retval);
>   return retval;
> }
> 
> static void   
> gtk_container_validate_children_cb (GtkWidget *widget,
> 				    gpointer user_data)
> {
>   GtkWidget **retval = (GtkWidget **) user_data;
>   
>   if (*retval == NULL)
>     {
>       if (GTK_IS_CONTAINER (widget))
> 	{
> 	  gtk_container_foreach ((GtkContainer *) widget,
> 				 gtk_container_validate_children_cb,
> 				 retval);
> 	}
>       else if (!gtk_widget_validate (widget))
> 	*retval = widget;
Index: gtk/gtkcontainer.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkcontainer.h,v
retrieving revision 1.37
diff -r1.37 gtkcontainer.h
235a236,237
> GtkWidget *gtk_container_validate_children   (GtkContainer *container);
> 
Index: gtk/gtkmain.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkmain.c,v
retrieving revision 1.161
diff -r1.161 gtkmain.c
1709a1710,1723
> 
> gboolean
> _gtk_boolean_and_accumulator (GSignalInvocationHint *ihint,
> 			      GValue                *return_accu,
> 			      const GValue          *handler_return,
> 			      gpointer               dummy)
> {
>   gboolean value;
> 
>   value = g_value_get_boolean (handler_return);
>   g_value_set_boolean (return_accu, value);
> 
>   return value;
> }
Index: gtk/gtkmain.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkmain.h,v
retrieving revision 1.38
diff -r1.38 gtkmain.h
200a201,205
> gboolean _gtk_boolean_and_accumulator (GSignalInvocationHint *ihint,
>                                    GValue                *return_accu,
>                                    const GValue          *handler_return,
>                                    gpointer               dummy);
> 
Index: gtk/gtkwidget.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkwidget.c,v
retrieving revision 1.217
diff -r1.217 gtkwidget.c
109a110
>   VALIDATE,
211a213,214
> static gboolean          gtk_widget_default_validate (GtkWidget *widget);
> 
362a366,367
>   klass->validate = gtk_widget_default_validate;
> 
1003c1008,1016
<   
---
>   widget_signals[VALIDATE] =
>     g_signal_newc ("validate",
> 		   G_TYPE_FROM_CLASS(object_class),
> 		   G_SIGNAL_RUN_LAST | GTK_RUN_ACTION,
> 		   G_STRUCT_OFFSET (GtkWidgetClass, validate),
> 		   _gtk_boolean_and_accumulator, NULL,
> 		   gtk_marshal_BOOLEAN__VOID,
> 		   G_TYPE_BOOLEAN, 0);
> 
5752a5766,5801
> }
> 
> /**
>  * gtk_widget_validate:
>  * @widget: a #GtkWidget
>  * @returns: TRUE if the widget has valid data
>  *
>  * This routine is used for field-level validation.  It returns TRUE if
>  * the field contains valid data and focus can be moved away from it.
>  *
>  * Corresponds to the "validate" signal.  The results from handlers connected 
>  * to the "validate" signal are combined using a short-circuited AND.  If no
>  * handlers are connected, it returns TRUE (valid-by-default).
>  * 
>  **/
> gboolean
> gtk_widget_validate (GtkWidget *widget)
> {
>   if (widget)
>     {
>       gboolean retval;
> 
>       g_signal_emit (G_OBJECT(widget),
> 		     widget_signals[VALIDATE],
> 		     0,
> 		     &retval);
>       return retval;
>     }
>   else
>     return TRUE;
> }
> 
> static gboolean          
> gtk_widget_default_validate (GtkWidget *widget)
> {
>   return TRUE;
Index: gtk/gtkwidget.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkwidget.h,v
retrieving revision 1.107
diff -r1.107 gtkwidget.h
388a389,391
>   /* Used for field-level validation */
>   gboolean     (* validate)        (GtkWidget          *widget);
> 
694a698,699
> 
> gboolean          gtk_widget_validate      (GtkWidget *widget);
Index: gtk/gtkwindow.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkwindow.c,v
retrieving revision 1.120
diff -r1.120 gtkwindow.c
810c810,824
<  * gtk_window_set_focus:
---
>  * gtk_window_get_focus_widget:
>  * @window: a #GtkWindow
>  * @returns: the widget which currently has the focus
>  *
>  * This returns the widget which currently has the focus for this window.
>  * May return NULL.
>  */
> GtkWidget*
> gtk_window_get_focus_widget (GtkWindow *window)
> {
>   return window->focus_widget;
> }
> 
> /**
>  * gtk_window_force_focus:
819a834,835
>  * This routine does not check for field-level validation.  Use
>  * gtk_window_set_focus instead.
822,823c838,839
< gtk_window_set_focus (GtkWindow *window,
< 		      GtkWidget *focus)
---
> gtk_window_force_focus (GtkWindow *window,
> 		        GtkWidget *focus)
838a855,881
>  * gtk_window_set_focus:
>  * @window: a #GtkWindow
>  * @focus: widget to be the new focus widget
>  * @returns: TRUE if the focus has been set to the desired widget
>  *
>  * Checks if the current focused field is valid.  If it is, calls
>  * gtk_window_force_focus.
>  **/
> gboolean
> gtk_window_set_focus (GtkWindow *window,
> 		      GtkWidget *focus)
> {
>   if (window->focus_widget != focus)
>     {
>       if (gtk_widget_validate (window->focus_widget))
> 	{
> 	  gtk_window_force_focus (window, focus);
> 	  return TRUE;
> 	}      
>       else
> 	return FALSE;
>     }
>   else
>     return TRUE;
> }
> 
> /**
2193c2236,2238
<   gtk_container_focus (GTK_CONTAINER (window), dir);
---
>   if (gtk_widget_validate (window->focus_widget))
>     {
>       gtk_container_focus (GTK_CONTAINER (window), dir);
2195,2196c2240,2242
<   if (!GTK_CONTAINER (window)->focus_child)
<     gtk_window_set_focus (window, NULL);
---
>       if (!GTK_CONTAINER (window)->focus_child)
> 	gtk_window_set_focus (window, NULL);
>     }
Index: gtk/gtkwindow.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkwindow.h,v
retrieving revision 1.40
diff -r1.40 gtkwindow.h
227a228,229
> GtkWidget *gtk_window_get_focus_widget         (GtkWindow           *window);
> 
229c231,233
< void       gtk_window_set_focus                (GtkWindow           *window,
---
> void       gtk_window_force_focus              (GtkWindow           *window,
> 						GtkWidget           *focus);
> gboolean   gtk_window_set_focus                (GtkWindow           *window,


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