Re: Patch to use normal focus system for menu keynav
- From: "Padraig O'Briain" <Padraig Obriain Sun COM>
- To: otaylor redhat com
- Cc: gtk-devel-list gnome org
- Subject: Re: Patch to use normal focus system for menu keynav
- Date: Mon, 21 Jan 2002 17:30:10 +0000 (GMT)
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]