Re: Patch to use normal focus system for menu keynav



I have reworked this patch as part of an effort to make toolbars accessible.

With the attached patch, F10 should put keyboard focus in a toolbar or a menu 
attached to a menubar. Ctrl+Tab and Shift+Ctrl+Tab should allow one to cycle 
focus between menubars and toolbars in a window. F10 should restore focus to the 
widget which had focus before the previous F10 was pressed.

Navigating items in a toolbar uses the normal focus mechanism.

Comments requested.

Padraig

> 
> 
> "Padraig O'Briain" <Padraig Obriain Sun COM> writes:
> 
> > This patch attempts to address one of the issues Owen raised in 
> > http://mail.gnome.org/archives/gtk-devel-list/2001-October/msg00224.html.
> > 
> > A grab is not done when F10 is used to focus the menubar or a item in the 
> > menubar is clicked by the mouse. Instead the focus is changed to the menubar 
and 
> > is changed back to the previously focussed widget when leaving the menu bar.
> > 
> > Can this patch be committed?
> 
> Hmmm, I guess I'd really have to see it in the context of the full
> change to be able to give a real answer.
> 
> > Index: gtk/gtkmenushell.c
> > ===================================================================
> > RCS file: /cvs/gnome/gtk+/gtk/gtkmenushell.c,v
> > retrieving revision 1.51
> > diff -u -p -r1.51 gtkmenushell.c
> > --- gtk/gtkmenushell.c	2002/01/16 08:53:15	1.51
> > +++ gtk/gtkmenushell.c	2002/01/16 09:25:33
> > @@ -147,6 +147,9 @@ static void gtk_real_menu_shell_activate
> >  						  gboolean           
> > force_hide);
> >  static void gtk_real_menu_shell_cancel           (GtkMenuShell      
> > *menu_shell);
> >  
> > +static void focus_widget_destroyed           (gpointer          data,
> > +                                              GtkWindow         *window);
> > +
> >  static GtkContainerClass *parent_class = NULL;
> >  static guint menu_shell_signals[LAST_SIGNAL] = { 0 };
> >  
> > @@ -402,7 +405,7 @@ gtk_menu_shell_button_press (GtkWidget  
> >      {
> >        if (!menu_shell->active)
> >  	{
> > -	  gtk_grab_add (GTK_WIDGET (widget));
> > +	  _gtk_menu_shell_activate (menu_shell);
> >  	  menu_shell->have_grab = TRUE;
> >  	  menu_shell->active = TRUE;
> 
>  * Setting the have_grab flag but not grabbing strikes me as (at the least)
>    pretty confusing. If we are reusing this flag to mean 'have focus' we
>    should rename it as well. 
> 
>  * If you are encapsulating the "grabbing"into _menu_shell_activate, you
>    should probably move the setting of have_grab and active as well.
> 
> >  	}
> > @@ -736,8 +739,42 @@ gtk_real_menu_shell_deactivate (GtkMenuS
> >  
> >        if (menu_shell->have_grab)
> >  	{
> > +          GtkWidget *toplevel;
> > +
> > +          toplevel = gtk_widget_get_toplevel (GTK_WIDGET (menu_shell));
> > +          if (toplevel && GTK_WIDGET_TOPLEVEL (toplevel))
> > +            {
> > +              GtkWindow *window;
> > +              GtkWidget *previous_focus;
> > +
> > +              window = GTK_WINDOW (toplevel);
> > +              previous_focus = g_object_get_data (G_OBJECT (window), 
> > +                                                  
"gtk-previous-focus-widget");
> > +
> > +              if (previous_focus)
> > +                {
> > +                  g_object_weak_unref (G_OBJECT (previous_focus), 
> > +                                        (GWeakNotify) 
focus_widget_destroyed,
> > +                                       window);
> > +                  g_object_set_data (G_OBJECT (window), 
> > +                                     "gtk-previous-focus-widget",
> > +                                     NULL);
> > +                  if (gtk_widget_is_ancestor (previous_focus, toplevel))
> > +                    {
> > +	              gtk_window_set_focus (window, previous_focus);
> > +                    }
> 
>  * Style nitpick, we don't include braces for single line blocks like this
> 
> > +                  else
> > +                    {
> > +	              gtk_window_set_focus (window, NULL);
> > +                    }
> > +
> > +                }
> > +              else
> > +                {
> > +	          gtk_window_set_focus (window, previous_focus);
> > +                }
> > +            }
> >  	  menu_shell->have_grab = FALSE;
> > -	  gtk_grab_remove (GTK_WIDGET (menu_shell));
> 
> 
> > +_gtk_menu_shell_activate (GtkMenuShell *menu_shell)
> > +{
> > +  GtkWidget *toplevel;
> > +
> > +  toplevel = gtk_widget_get_toplevel (GTK_WIDGET (menu_shell));
> > +  if (toplevel && GTK_WIDGET_TOPLEVEL (toplevel))
> > +    {
> > +      GtkWindow *window;
> > +      GtkWidget *focus_widget;
> > +
> > +      window = GTK_WINDOW (toplevel);
> > +      focus_widget = window->focus_widget;
> > +
> > +      if (focus_widget && !GTK_IS_MENU_SHELL (focus_widget))
> > +        {
> > +          GtkWidget *previous_focus;
> > +
> > +          previous_focus = g_object_get_data (G_OBJECT (window), 
> > +                                              "gtk-previous-focus-widget");
> > +          if (!previous_focus)
> > +            {
> > +              g_object_set_data (G_OBJECT (window), 
> > +                                 "gtk-previous-focus-widget",
> > +                                 focus_widget);
> > +              g_object_weak_ref (G_OBJECT (focus_widget),
> > +                                 (GWeakNotify) focus_widget_destroyed, 
> > +                                 window);
> 
> I'd probably want to take into account here the possibility that the toplevel
> might be destroyed before the menu shell is deactivated. I'm not sure
> that this can happen, but it is easier to worry about the reference counting
> separately from the overall code flow.
> > +
> > +            }
> > +        }
> > +      gtk_widget_grab_focus (GTK_WIDGET (menu_shell));
> 
> I don't think this is actually doing anything. The menu shell doesn't have
> the CAN_FOCUS flag set on it, so this silently does nothing. (Try focusing
> another widget then popping up the menu... the other widgets remains
> displayed as focused)
> 
> Regards,
>                                         Owen
> _______________________________________________
> gtk-devel-list mailing list
> gtk-devel-list gnome org
> http://mail.gnome.org/mailman/listinfo/gtk-devel-list
Index: gtkmenubar.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkmenubar.c,v
retrieving revision 1.38
diff -u -p -r1.38 gtkmenubar.c
--- gtkmenubar.c	2001/12/12 19:30:01	1.38
+++ gtkmenubar.c	2002/01/21 17:21:26
@@ -388,6 +388,13 @@ window_key_press_handler (GtkWidget   *w
           GtkMenuBar *menubar;
           GtkMenuShell *menushell;
           
+          /*
+           * If focus is in toolbar or menubar ignore F10
+           */
+          if (g_object_get_data (G_OBJECT (widget),
+                                 "gtk-previous-focus-widget"))
+            return retval;
+
           menubar = GTK_MENU_BAR (data);
           menushell = GTK_MENU_SHELL (menubar);
 
Index: gtkmenuitem.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkmenuitem.c,v
retrieving revision 1.69
diff -u -p -r1.69 gtkmenuitem.c
--- gtkmenuitem.c	2002/01/15 13:24:03	1.69
+++ gtkmenuitem.c	2002/01/21 17:21:26
@@ -752,9 +752,7 @@ gtk_real_menu_item_activate_item (GtkMen
 
 	  if (!menu_shell->active)
 	    {
-	      gtk_grab_add (GTK_WIDGET (menu_shell));
-	      menu_shell->have_grab = TRUE;
-	      menu_shell->active = TRUE;
+	      _gtk_menu_shell_activate (menu_shell);
 	    }
 
 	  gtk_menu_shell_select_item (GTK_MENU_SHELL (widget->parent), widget); 
Index: gtkmenushell.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkmenushell.c,v
retrieving revision 1.51
diff -u -p -r1.51 gtkmenushell.c
--- gtkmenushell.c	2002/01/16 08:53:15	1.51
+++ gtkmenushell.c	2002/01/21 17:21:26
@@ -32,6 +32,7 @@
 #include "gtkmenushell.h"
 #include "gtksignal.h"
 #include "gtkwindow.h"
+#include "gtktoolbar.h"
 
 #define MENU_SHELL_TIMEOUT   500
 
@@ -42,6 +43,7 @@ enum {
   MOVE_CURRENT,
   ACTIVATE_CURRENT,
   CANCEL,
+  CYCLE_FOCUS,
   LAST_SIGNAL
 };
 
@@ -146,7 +148,12 @@ static void gtk_real_menu_shell_move_cur
 static void gtk_real_menu_shell_activate_current (GtkMenuShell      *menu_shell,
 						  gboolean           force_hide);
 static void gtk_real_menu_shell_cancel           (GtkMenuShell      *menu_shell);
+static void gtk_real_menu_shell_cycle_focus      (GtkMenuShell      *menu_shell,
+                                                  gboolean           reverse);
 
+static void do_cycle_focus                       (GtkMenuShell      *menu_shell,
+                                                  gboolean           reverse);
+
 static GtkContainerClass *parent_class = NULL;
 static guint menu_shell_signals[LAST_SIGNAL] = { 0 };
 
@@ -209,6 +216,7 @@ gtk_menu_shell_class_init (GtkMenuShellC
   klass->move_current = gtk_real_menu_shell_move_current;
   klass->activate_current = gtk_real_menu_shell_activate_current;
   klass->cancel = gtk_real_menu_shell_cancel;
+  klass->cycle_focus = gtk_real_menu_shell_cycle_focus;
   klass->select_item = gtk_menu_shell_real_select_item;
   klass->insert = gtk_menu_shell_real_insert;
 
@@ -249,6 +257,14 @@ gtk_menu_shell_class_init (GtkMenuShellC
                     GTK_SIGNAL_OFFSET (GtkMenuShellClass, cancel),
                     _gtk_marshal_VOID__VOID,
 		    GTK_TYPE_NONE, 0);
+  menu_shell_signals[CYCLE_FOCUS] =
+    gtk_signal_new ("cycle_focus",
+		    GTK_RUN_LAST | GTK_RUN_ACTION,
+		    GTK_CLASS_TYPE (object_class),
+                    GTK_SIGNAL_OFFSET (GtkMenuShellClass, cycle_focus),
+                    _gtk_marshal_VOID__BOOLEAN,
+		    GTK_TYPE_NONE, 1,
+                    GTK_TYPE_BOOL);
 
   binding_set = gtk_binding_set_by_class (klass);
   gtk_binding_entry_add_signal (binding_set,
@@ -274,6 +290,36 @@ gtk_menu_shell_class_init (GtkMenuShellC
 				"activate_current", 1,
 				GTK_TYPE_BOOL,
 				FALSE);
+  gtk_binding_entry_add_signal (binding_set,
+				GDK_Tab, GDK_CONTROL_MASK,
+				"cycle_focus", 1,
+				GTK_TYPE_BOOL,
+				FALSE);
+  gtk_binding_entry_add_signal (binding_set,
+				GDK_KP_Tab, GDK_CONTROL_MASK,
+				"cycle_focus", 1,
+				GTK_TYPE_BOOL,
+				FALSE);
+  gtk_binding_entry_add_signal (binding_set,
+				GDK_ISO_Left_Tab, GDK_CONTROL_MASK,
+				"cycle_focus", 1,
+				GTK_TYPE_BOOL,
+				FALSE);
+  gtk_binding_entry_add_signal (binding_set,
+				GDK_Tab, GDK_SHIFT_MASK | GDK_CONTROL_MASK,
+				"cycle_focus", 1,
+				GTK_TYPE_BOOL,
+				FALSE);
+  gtk_binding_entry_add_signal (binding_set,
+				GDK_KP_Tab, GDK_SHIFT_MASK | GDK_CONTROL_MASK,
+				"cycle_focus", 1,
+				GTK_TYPE_BOOL,
+				FALSE);
+  gtk_binding_entry_add_signal (binding_set,
+				GDK_ISO_Left_Tab, GDK_SHIFT_MASK | GDK_CONTROL_MASK,
+				"cycle_focus", 1,
+				GTK_TYPE_BOOL,
+				FALSE);
 }
 
 static GtkType
@@ -289,7 +335,7 @@ gtk_menu_shell_init (GtkMenuShell *menu_
   menu_shell->active_menu_item = NULL;
   menu_shell->parent_menu_shell = NULL;
   menu_shell->active = FALSE;
-  menu_shell->have_grab = FALSE;
+  menu_shell->have_focus = FALSE;
   menu_shell->have_xgrab = FALSE;
   menu_shell->ignore_leave = FALSE;
   menu_shell->button = 0;
@@ -402,9 +448,7 @@ gtk_menu_shell_button_press (GtkWidget  
     {
       if (!menu_shell->active)
 	{
-	  gtk_grab_add (GTK_WIDGET (widget));
-	  menu_shell->have_grab = TRUE;
-	  menu_shell->active = TRUE;
+	  _gtk_menu_shell_activate (menu_shell);
 	}
       menu_shell->button = event->button;
 
@@ -734,10 +778,10 @@ gtk_real_menu_shell_deactivate (GtkMenuS
 	  menu_shell->active_menu_item = NULL;
 	}
 
-      if (menu_shell->have_grab)
+      if (menu_shell->have_focus)
 	{
-	  menu_shell->have_grab = FALSE;
-	  gtk_grab_remove (GTK_WIDGET (menu_shell));
+          _gtk_toolbar_restore_focus (GTK_WIDGET (menu_shell));
+	  menu_shell->have_focus = FALSE;
 	}
       if (menu_shell->have_xgrab)
 	{
@@ -1039,4 +1083,41 @@ gtk_real_menu_shell_cancel (GtkMenuShell
   
   gtk_menu_shell_deactivate (menu_shell);
   gtk_signal_emit (GTK_OBJECT (menu_shell), menu_shell_signals[SELECTION_DONE]);
+}
+
+void
+_gtk_menu_shell_activate (GtkMenuShell *menu_shell)
+{
+  _gtk_toolbar_save_focus (GTK_WIDGET (menu_shell));
+
+  menu_shell->have_focus = TRUE;
+  menu_shell->active = TRUE;
+}
+
+static void
+gtk_real_menu_shell_cycle_focus (GtkMenuShell *menu_shell,
+                                 gboolean      reverse)
+{
+  GtkWidget *parent;
+  GtkMenuShell *toplevel_menu_shell = menu_shell;
+
+  parent = menu_shell->parent_menu_shell;
+  while (parent)
+    {
+      toplevel_menu_shell = GTK_MENU_SHELL (parent);
+      parent = toplevel_menu_shell->parent_menu_shell;
+    }
+  if (toplevel_menu_shell && 
+      GTK_MENU_SHELL_GET_CLASS (toplevel_menu_shell)->submenu_placement == GTK_TOP_BOTTOM)
+    {
+      do_cycle_focus (toplevel_menu_shell, reverse);
+    }
+}
+
+static void
+do_cycle_focus (GtkMenuShell *menu_shell,
+                gboolean      reverse)
+{
+  gtk_menu_shell_deactivate (menu_shell);
+  _gtk_toolbar_move_focus (GTK_WIDGET (menu_shell), reverse);
 }
Index: gtkmenushell.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtkmenushell.h,v
retrieving revision 1.12
diff -u -p -r1.12 gtkmenushell.h
--- gtkmenushell.h	2000/11/22 01:00:26	1.12
+++ gtkmenushell.h	2002/01/21 17:21:26
@@ -60,7 +60,7 @@ struct _GtkMenuShell
   guint32 activate_time;
 
   guint active : 1;
-  guint have_grab : 1;
+  guint have_focus : 1;
   guint have_xgrab : 1;
   guint ignore_leave : 1;
   guint menu_flag : 1;
@@ -86,6 +86,8 @@ struct _GtkMenuShellClass
   void (*insert)           (GtkMenuShell *menu_shell,
 			    GtkWidget    *child,
 			    gint          position);
+  void (*cycle_focus)      (GtkMenuShell *menu_shell,
+                            gboolean      reverse);
 };
 
 
@@ -104,6 +106,7 @@ void    gtk_menu_shell_deselect    (GtkM
 void    gtk_menu_shell_activate_item  (GtkMenuShell      *menu_shell,
 				       GtkWidget         *menu_item,
 				       gboolean           force_deactivate);
+void	_gtk_menu_shell_activate   (GtkMenuShell *menu_shell);
 
 
 #ifdef __cplusplus
Index: gtktoolbar.c
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtktoolbar.c,v
retrieving revision 1.71
diff -u -p -r1.71 gtktoolbar.c
--- gtktoolbar.c	2002/01/08 23:05:46	1.71
+++ gtktoolbar.c	2002/01/21 17:21:27
@@ -25,6 +25,8 @@
  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
  */
 
+#include "gdk/gdkkeysyms.h"
+#include "gtkbindings.h"
 #include "gtkbutton.h"
 #include "gtktogglebutton.h"
 #include "gtkradiobutton.h"
@@ -38,6 +40,7 @@
 #include "gtksettings.h"
 #include "gtkintl.h"
 #include "gtkmarshalers.h"
+#include "gtkmenubar.h"
 
 
 #define DEFAULT_IPADDING 0
@@ -59,6 +62,7 @@ enum {
 enum {
   ORIENTATION_CHANGED,
   STYLE_CHANGED,
+  CYCLE_FOCUS,
   LAST_SIGNAL
 };
 
@@ -92,6 +96,8 @@ static void gtk_toolbar_style_set       
                                                   GtkStyle        *prev_style);
 static gboolean gtk_toolbar_focus                (GtkWidget       *widget,
                                                   GtkDirectionType dir);
+static void gtk_toolbar_hierarchy_changed        (GtkWidget       *widget,
+                                                  GtkWidget       *old_toplevel);
 static void gtk_toolbar_show_all                 (GtkWidget       *widget);
 static void gtk_toolbar_add                      (GtkContainer    *container,
 				                  GtkWidget       *widget);
@@ -106,6 +112,8 @@ static void gtk_real_toolbar_orientation
 						  GtkOrientation   orientation);
 static void gtk_real_toolbar_style_changed       (GtkToolbar      *toolbar,
 						  GtkToolbarStyle  style);
+static void gtk_real_toolbar_cycle_focus         (GtkToolbar      *toolbar,
+                                                  gboolean         reverse);
 
 static GtkWidget * gtk_toolbar_internal_insert_element (GtkToolbar          *toolbar,
                                                         GtkToolbarChildType  type,
@@ -135,7 +143,21 @@ static GtkReliefStyle       get_button_r
 static gint                 get_space_size    (GtkToolbar *toolbar);
 static GtkToolbarSpaceStyle get_space_style   (GtkToolbar *toolbar);
 
+static void   get_all_widgets   (GtkContainer     *container,
+                                 GList            **widgets);
+static gint   tab_compare       (gconstpointer    a,
+                                 gconstpointer    b);
+static GList* focus_sort_tab    (GtkContainer     *container,
+                                 GList            *children,
+                                 GtkDirectionType  dir);
+static void   do_focus          (GtkToolbar       *toolbar);
+
+static void   focus_widget_destroyed (gpointer      data,
+                                      GtkWindow    *window);
+static void   window_destroyed       (gpointer      data,
+                                      GtkWidget    *widget);
 
+
 static GtkContainerClass *parent_class;
 
 static guint toolbar_signals[LAST_SIGNAL] = { 0 };
@@ -174,6 +196,8 @@ gtk_toolbar_class_init (GtkToolbarClass 
   GtkWidgetClass *widget_class;
   GtkContainerClass *container_class;
 
+  GtkBindingSet *binding_set;
+
   gobject_class = G_OBJECT_CLASS (class);
   object_class = (GtkObjectClass *) class;
   widget_class = (GtkWidgetClass *) class;
@@ -193,6 +217,7 @@ gtk_toolbar_class_init (GtkToolbarClass 
   widget_class->style_set = gtk_toolbar_style_set;
   widget_class->show_all = gtk_toolbar_show_all;
   widget_class->focus = gtk_toolbar_focus;
+  widget_class->hierarchy_changed = gtk_toolbar_hierarchy_changed;
   
   container_class->add = gtk_toolbar_add;
   container_class->remove = gtk_toolbar_remove;
@@ -200,6 +225,7 @@ gtk_toolbar_class_init (GtkToolbarClass 
   
   class->orientation_changed = gtk_real_toolbar_orientation_changed;
   class->style_changed = gtk_real_toolbar_style_changed;
+  class->cycle_focus = gtk_real_toolbar_cycle_focus;
 
   toolbar_signals[ORIENTATION_CHANGED] =
     gtk_signal_new ("orientation_changed",
@@ -217,6 +243,14 @@ gtk_toolbar_class_init (GtkToolbarClass 
 		    _gtk_marshal_VOID__ENUM,
 		    GTK_TYPE_NONE, 1,
 		    GTK_TYPE_TOOLBAR_STYLE);
+  toolbar_signals[CYCLE_FOCUS] =
+    gtk_signal_new ("cycle_focus",
+		    GTK_RUN_LAST | GTK_RUN_ACTION,
+		    GTK_CLASS_TYPE (object_class),
+		    GTK_SIGNAL_OFFSET (GtkToolbarClass, cycle_focus),
+		    _gtk_marshal_VOID__BOOLEAN,
+		    GTK_TYPE_NONE, 1,
+		    GTK_TYPE_BOOL);
   
   g_object_class_install_property (gobject_class,
 				   PROP_ORIENTATION,
@@ -293,6 +327,39 @@ gtk_toolbar_class_init (GtkToolbarClass 
                                                     GTK_TYPE_ICON_SIZE,
                                                     GTK_ICON_SIZE_LARGE_TOOLBAR,
                                                     G_PARAM_READWRITE));  
+
+  binding_set = gtk_binding_set_by_class (class);
+  gtk_binding_entry_add_signal (binding_set,
+                                GDK_Tab, GDK_CONTROL_MASK,
+                                "cycle_focus", 1,
+                                GTK_TYPE_BOOL,
+                                FALSE);
+  gtk_binding_entry_add_signal (binding_set,
+                                GDK_KP_Tab, GDK_CONTROL_MASK,
+                                "cycle_focus", 1,
+                                GTK_TYPE_BOOL,
+                                FALSE);
+  gtk_binding_entry_add_signal (binding_set,
+                                GDK_ISO_Left_Tab, GDK_CONTROL_MASK,
+                                "cycle_focus", 1,
+                                GTK_TYPE_BOOL,
+                                FALSE);
+  gtk_binding_entry_add_signal (binding_set,
+                                GDK_Tab, GDK_SHIFT_MASK | GDK_CONTROL_MASK,
+                                "cycle_focus", 1,
+                                GTK_TYPE_BOOL,
+                                TRUE);
+  gtk_binding_entry_add_signal (binding_set,
+                                GDK_KP_Tab, GDK_SHIFT_MASK | GDK_CONTROL_MASK,
+                                "cycle_focus", 1,
+                                GTK_TYPE_BOOL,
+                                TRUE);
+  gtk_binding_entry_add_signal (binding_set,
+                                GDK_ISO_Left_Tab, GDK_SHIFT_MASK | GDK_CONTROL_MASK,
+                                "cycle_focus", 1,
+                                GTK_TYPE_BOOL,
+                                TRUE);
+
 }
 
 static void
@@ -348,6 +415,7 @@ gtk_toolbar_init (GtkToolbar *toolbar)
 
   toolbar->style_set = FALSE;
   toolbar->icon_size_set = FALSE;
+  toolbar->focus_set = FALSE;
   g_object_get (gtk_settings_get_default (),
                 "gtk-toolbar-icon-size",
                 &toolbar->icon_size,
@@ -788,9 +856,13 @@ static gboolean
 gtk_toolbar_focus (GtkWidget       *widget,
                    GtkDirectionType dir)
 {
-  /* Focus can't go in toolbars */
-  
-  return FALSE;
+  if (GTK_TOOLBAR (widget)->focus_set)
+    {
+      GTK_WIDGET_CLASS (parent_class)->focus (widget, dir);
+      return TRUE;
+    }
+  else
+    return FALSE;
 }
 
 static void
@@ -1398,7 +1470,9 @@ gtk_toolbar_internal_insert_element (Gtk
 	  gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (child->widget), FALSE);
 	}
 
+#if 0
       GTK_WIDGET_UNSET_FLAGS (child->widget, GTK_CAN_FOCUS);
+#endif
 
       if (callback)
 	gtk_signal_connect (GTK_OBJECT (child->widget), "clicked",
@@ -1790,4 +1864,353 @@ get_space_style (GtkToolbar *toolbar)
 
 
   return space_style;  
+}
+
+static void
+gtk_real_toolbar_cycle_focus (GtkToolbar *toolbar,
+                              gboolean   reverse)
+{
+  _gtk_toolbar_move_focus (GTK_WIDGET (toolbar), reverse);
+}
+
+void
+_gtk_toolbar_save_focus (GtkWidget *widget)
+{
+  GtkWidget *toplevel;
+
+  toplevel = gtk_widget_get_toplevel (widget);
+  if (toplevel && GTK_WIDGET_TOPLEVEL (toplevel))
+    {
+      GtkWindow *window;
+      GtkWidget *previous_focus;
+
+      window = GTK_WINDOW (toplevel);
+      previous_focus = g_object_get_data (G_OBJECT (window),
+                                          "gtk-previous-focus-widget");
+      if (!previous_focus)
+        {
+          GtkWidget *focus_widget;
+          focus_widget = gtk_window_get_focus (window);
+
+          if (!focus_widget)
+            focus_widget = toplevel;
+
+          if (!GTK_IS_MENU_BAR (focus_widget) &&
+              !GTK_IS_TOOLBAR (focus_widget))
+            {
+
+              g_object_set_data (G_OBJECT (window),
+                                 "gtk-previous-focus-widget",
+                                 focus_widget);
+              if (focus_widget != toplevel)
+                {
+                  g_object_weak_ref (G_OBJECT (focus_widget),
+                                     (GWeakNotify) focus_widget_destroyed,
+                                     window);
+                  g_object_weak_ref (G_OBJECT (window),
+                                     (GWeakNotify) window_destroyed,
+                                     focus_widget);
+                }
+            }
+        }
+    }
+}
+
+void
+_gtk_toolbar_restore_focus (GtkWidget *widget)
+{
+  GtkWidget *toplevel;
+
+  toplevel = gtk_widget_get_toplevel (widget);
+  if (toplevel && GTK_WIDGET_TOPLEVEL (toplevel))
+    {
+      GtkWindow *window;
+      GtkWidget *previous_focus;
+      gboolean focus_reset = FALSE;
+
+      window = GTK_WINDOW (toplevel);
+      previous_focus = g_object_get_data (G_OBJECT (window),
+                                          "gtk-previous-focus-widget");
+
+      if (previous_focus)
+        {
+          g_object_set_data (G_OBJECT (window),
+                             "gtk-previous-focus-widget",
+                             NULL);
+          if (previous_focus != toplevel)
+            {
+              g_object_weak_unref (G_OBJECT (window),
+                                   (GWeakNotify) window_destroyed,
+                                   previous_focus);
+              g_object_weak_unref (G_OBJECT (previous_focus),
+                                   (GWeakNotify) focus_widget_destroyed,
+                                   window);
+
+              if (gtk_widget_is_ancestor (previous_focus, toplevel))
+                {
+                  gtk_window_set_focus (window, previous_focus);
+                  focus_reset = TRUE;
+                }
+            }
+        }
+      if (!focus_reset)
+        gtk_window_set_focus (window, NULL);
+    }
+
+}
+
+void
+_gtk_toolbar_move_focus (GtkWidget *widget, 
+                         gboolean   reverse)
+{
+  GtkWidget *toplevel;
+  GtkContainer *container;
+  GtkWidget *focus_widget;
+  GList *widgets = NULL;
+  GList *tmp;
+
+  toplevel = gtk_widget_get_toplevel (widget);
+  if (!GTK_WIDGET_TOPLEVEL (toplevel))
+    return;
+
+  container = GTK_CONTAINER (toplevel);
+  get_all_widgets (container, &widgets);
+
+  widgets = focus_sort_tab (container, widgets, reverse ?
+                            GTK_DIR_TAB_BACKWARD : GTK_DIR_TAB_FORWARD);
+
+  tmp = g_list_find (widgets, widget);
+  g_return_if_fail (tmp);
+  if (tmp->next)
+    focus_widget = GTK_WIDGET (tmp->next->data);
+  else
+    focus_widget = GTK_WIDGET (widgets->data);
+  g_list_free (widgets);
+
+  if (GTK_IS_TOOLBAR (widget))
+    GTK_TOOLBAR (widget)->focus_set = FALSE;
+
+  if (GTK_IS_TOOLBAR (focus_widget))
+    do_focus (GTK_TOOLBAR (focus_widget));
+  else if (GTK_IS_MENU_SHELL (focus_widget))
+    {
+      GtkMenuShell *menu_shell;
+
+	menu_shell = GTK_MENU_SHELL (focus_widget);
+
+	if (menu_shell->children)
+	{
+	  gtk_signal_emit_by_name (GTK_OBJECT (menu_shell->children->data),
+                                   "activate_item");
+
+	}
+    }
+}
+
+static void
+get_all_widgets (GtkContainer *container,
+                 GList        **widgets)
+{
+  GList *children, *l;
+
+  children = gtk_container_get_children (container);
+
+  for (l = children; l; l = l->next)
+    {
+      GtkWidget *widget = l->data;
+
+      if ((GTK_IS_TOOLBAR (widget) || GTK_IS_MENU_BAR (widget)) &&
+          GTK_WIDGET_IS_SENSITIVE (widget))
+        *widgets = g_list_prepend (*widgets, widget);
+      else if (GTK_IS_CONTAINER (widget))
+        get_all_widgets (GTK_CONTAINER (widget), widgets);
+
+    }
+  g_list_free (children); 
+}
+
+static gint
+tab_compare (gconstpointer a,
+             gconstpointer b)
+{
+  const GtkWidget *child1 = a;
+  const GtkWidget *child2 = b;
+
+  gint y1 = child1->allocation.y + child1->allocation.height / 2;
+  gint y2 = child2->allocation.y + child2->allocation.height / 2;
+
+  if (y1 == y2)
+    {
+      gint x1 = child1->allocation.x + child1->allocation.width / 2;
+      gint x2 = child2->allocation.x + child2->allocation.width / 2;
+
+      return (x1 < x2) ? -1 : ((x1 == x2) ? 0 : 1);
+    }
+  else
+    return (y1 < y2) ? -1 : 1;
+}
+
+static GList*
+focus_sort_tab (GtkContainer     *container,
+                GList            *children,
+                GtkDirectionType  dir)
+{
+  children = g_list_sort (children, tab_compare);
+
+  if (dir == GTK_DIR_TAB_BACKWARD)
+    children = g_list_reverse (children);
+
+  return children;
+}
+
+static void
+do_focus (GtkToolbar *toolbar)
+{
+  _gtk_toolbar_save_focus (GTK_WIDGET (toolbar));
+
+  if (toolbar->children)
+    {
+      GList *children;
+
+      children = toolbar->children;
+
+      while (children)
+        {
+          GtkToolbarChild *child;
+
+          child = children->data;
+          if (child->widget)        
+            {
+              gtk_widget_grab_focus (child->widget);
+              toolbar->focus_set = TRUE;
+              break;
+            }
+          children = children->next;
+        }
+    }
+}
+
+static void
+focus_widget_destroyed (gpointer  data,
+                        GtkWindow *window)
+{
+  g_object_set_data (G_OBJECT (window),
+                     "gtk-previous-focus-widget",
+                     NULL);
+}
+
+static void
+window_destroyed (gpointer  data,
+                  GtkWidget *widget)
+{
+  g_object_weak_unref (G_OBJECT (widget),
+                       (GWeakNotify) focus_widget_destroyed,
+                       data);
+}
+
+static gboolean
+window_key_press_handler (GtkWidget   *widget,
+                          GdkEventKey *event,
+                          gpointer     data)
+{
+  gchar *accel = NULL;
+  gboolean retval = FALSE;
+  
+  g_object_get (G_OBJECT (gtk_widget_get_settings (widget)),
+                "gtk-menu-bar-accel",
+                &accel,
+                NULL);
+
+  if (accel)
+    {
+      guint keyval = 0;
+      GdkModifierType mods = 0;
+
+      gtk_accelerator_parse (accel, &keyval, &mods);
+
+      if (keyval == 0)
+        g_warning ("Failed to parse menu bar accelerator '%s'\n", accel);
+
+      /* FIXME this is wrong, needs to be in the global accel resolution
+       * thing, to properly consider i18n etc., but that probably requires
+       * AccelGroup changes etc.
+       */
+      if (event->keyval == keyval &&
+          ((event->state & gtk_accelerator_get_default_mod_mask ()) ==
+	   (mods & gtk_accelerator_get_default_mod_mask ())))
+        {
+          GtkToolbar *toolbar;
+
+          toolbar = GTK_TOOLBAR (data);
+          /*
+           * If focus is in toolbar reset focus
+           */
+          if (g_object_get_data (G_OBJECT (widget),
+                                 "gtk-previous-focus-widget"))
+            {
+              if (toolbar->focus_set)
+                {
+                  _gtk_toolbar_restore_focus (GTK_WIDGET (toolbar));
+                  toolbar->focus_set = FALSE;
+                }
+              return retval;
+            }
+  
+          do_focus (toolbar);
+          retval = TRUE;
+        }
+
+      g_free (accel);
+    }
+
+  return retval;
+}
+
+static void
+add_to_window (GtkWindow  *window,
+               GtkToolbar *toolbar)
+{
+  GtkToolbar *old_toolbar;
+
+  old_toolbar = g_object_get_data (G_OBJECT (window),
+                                   "gtk-tool-bar");
+  
+  if (old_toolbar)
+    return; 
+
+  g_object_set_data (G_OBJECT (window),
+                     "gtk-tool-bar",
+                     toolbar);
+
+  g_signal_connect (G_OBJECT (window),
+                    "key_press_event",
+                    G_CALLBACK (window_key_press_handler),
+                    toolbar);
+}
+
+static void
+remove_from_window (GtkWindow  *window,
+                    GtkToolbar *toolbar)
+{
+  g_signal_handlers_disconnect_by_func (G_OBJECT (window),
+                                        G_CALLBACK (window_key_press_handler),
+                                        toolbar);
+}
+
+static void
+gtk_toolbar_hierarchy_changed (GtkWidget *widget,
+                               GtkWidget *old_toplevel)
+{
+  GtkWidget *toplevel;  
+  GtkToolbar *toolbar;
+
+  toolbar = GTK_TOOLBAR (widget);
+
+  toplevel = gtk_widget_get_toplevel (widget);
+
+  if (old_toplevel)
+    remove_from_window (GTK_WINDOW (old_toplevel), toolbar);
+  
+  if (GTK_WIDGET_TOPLEVEL (toplevel))
+    add_to_window (GTK_WINDOW (toplevel), toolbar);
 }
Index: gtktoolbar.h
===================================================================
RCS file: /cvs/gnome/gtk+/gtk/gtktoolbar.h,v
retrieving revision 1.29
diff -u -p -r1.29 gtktoolbar.h
--- gtktoolbar.h	2001/06/24 15:34:47	1.29
+++ gtktoolbar.h	2002/01/21 17:21:27
@@ -97,6 +97,7 @@ struct _GtkToolbar
   
   guint            style_set : 1;
   guint            icon_size_set : 1;
+  guint            focus_set : 1;
 };
 
 struct _GtkToolbarClass
@@ -107,6 +108,8 @@ struct _GtkToolbarClass
 				GtkOrientation   orientation);
   void (* style_changed)       (GtkToolbar      *toolbar,
 				GtkToolbarStyle  style);
+  void (* cycle_focus)         (GtkToolbar      *toolbar,
+				gboolean         reverse);
 };
 
 
@@ -220,6 +223,10 @@ GtkToolbarStyle gtk_toolbar_get_style   
 GtkIconSize     gtk_toolbar_get_icon_size    (GtkToolbar           *toolbar);
 gboolean        gtk_toolbar_get_tooltips     (GtkToolbar           *toolbar);
 
+void            _gtk_toolbar_move_focus      (GtkWidget            *widget,
+                                              gboolean             reverse);
+void            _gtk_toolbar_save_focus      (GtkWidget            *widget);
+void            _gtk_toolbar_restore_focus   (GtkWidget            *widget);
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */


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