diff options
-rw-r--r-- | ChangeLog | 20 | ||||
-rw-r--r-- | ChangeLog.pre-2-10 | 20 | ||||
-rw-r--r-- | ChangeLog.pre-2-6 | 20 | ||||
-rw-r--r-- | ChangeLog.pre-2-8 | 20 | ||||
-rw-r--r-- | gtk/Makefile.am | 2 | ||||
-rw-r--r-- | gtk/gtklabel.c | 56 | ||||
-rw-r--r-- | gtk/gtkmenushell.c | 178 | ||||
-rw-r--r-- | gtk/gtkmenushell.h | 7 | ||||
-rw-r--r-- | gtk/gtkmnemonichash.c | 197 | ||||
-rw-r--r-- | gtk/gtkmnemonichash.h | 54 | ||||
-rw-r--r-- | gtk/gtkwindow.c | 206 |
11 files changed, 594 insertions, 186 deletions
@@ -1,3 +1,23 @@ +2004-12-10 Matthias Clasen <mclasen@redhat.com> + + Support no-Alt mnemnonics in menu bars (#101309, Owen Taylor) + + * gtk/gtkwindow.c: Factor out mnemonic hash code into + a separate file. + + * gtk/gtkmnemonichash.[hc]: Factored out mnemonic hash + code from gtkwindow.c. + + * gtk/Makefile.am (gtk_c_sources): Add gtkmnemonichash.[hc]. + + * gtk/gtkmenushell.c (struct _GtkMenuShellPrivate): Give + menu shells their own mnemonic hash. + + * gtk/gtkmenushell.h: Add private api to support mnemonics. + + * gtk/gtklabel.c (gtk_label_setup_mnemonic): Add mnemonic to + the menushell mnemonic hash when inside a menu. + Fri Dec 10 13:59:32 2004 Manish Singh <yosh@gimp.org> * gtk/gtk.symbols: add recent new functions. diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index 65a8165745..7d21818a20 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -1,3 +1,23 @@ +2004-12-10 Matthias Clasen <mclasen@redhat.com> + + Support no-Alt mnemnonics in menu bars (#101309, Owen Taylor) + + * gtk/gtkwindow.c: Factor out mnemonic hash code into + a separate file. + + * gtk/gtkmnemonichash.[hc]: Factored out mnemonic hash + code from gtkwindow.c. + + * gtk/Makefile.am (gtk_c_sources): Add gtkmnemonichash.[hc]. + + * gtk/gtkmenushell.c (struct _GtkMenuShellPrivate): Give + menu shells their own mnemonic hash. + + * gtk/gtkmenushell.h: Add private api to support mnemonics. + + * gtk/gtklabel.c (gtk_label_setup_mnemonic): Add mnemonic to + the menushell mnemonic hash when inside a menu. + Fri Dec 10 13:59:32 2004 Manish Singh <yosh@gimp.org> * gtk/gtk.symbols: add recent new functions. diff --git a/ChangeLog.pre-2-6 b/ChangeLog.pre-2-6 index 65a8165745..7d21818a20 100644 --- a/ChangeLog.pre-2-6 +++ b/ChangeLog.pre-2-6 @@ -1,3 +1,23 @@ +2004-12-10 Matthias Clasen <mclasen@redhat.com> + + Support no-Alt mnemnonics in menu bars (#101309, Owen Taylor) + + * gtk/gtkwindow.c: Factor out mnemonic hash code into + a separate file. + + * gtk/gtkmnemonichash.[hc]: Factored out mnemonic hash + code from gtkwindow.c. + + * gtk/Makefile.am (gtk_c_sources): Add gtkmnemonichash.[hc]. + + * gtk/gtkmenushell.c (struct _GtkMenuShellPrivate): Give + menu shells their own mnemonic hash. + + * gtk/gtkmenushell.h: Add private api to support mnemonics. + + * gtk/gtklabel.c (gtk_label_setup_mnemonic): Add mnemonic to + the menushell mnemonic hash when inside a menu. + Fri Dec 10 13:59:32 2004 Manish Singh <yosh@gimp.org> * gtk/gtk.symbols: add recent new functions. diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8 index 65a8165745..7d21818a20 100644 --- a/ChangeLog.pre-2-8 +++ b/ChangeLog.pre-2-8 @@ -1,3 +1,23 @@ +2004-12-10 Matthias Clasen <mclasen@redhat.com> + + Support no-Alt mnemnonics in menu bars (#101309, Owen Taylor) + + * gtk/gtkwindow.c: Factor out mnemonic hash code into + a separate file. + + * gtk/gtkmnemonichash.[hc]: Factored out mnemonic hash + code from gtkwindow.c. + + * gtk/Makefile.am (gtk_c_sources): Add gtkmnemonichash.[hc]. + + * gtk/gtkmenushell.c (struct _GtkMenuShellPrivate): Give + menu shells their own mnemonic hash. + + * gtk/gtkmenushell.h: Add private api to support mnemonics. + + * gtk/gtklabel.c (gtk_label_setup_mnemonic): Add mnemonic to + the menushell mnemonic hash when inside a menu. + Fri Dec 10 13:59:32 2004 Manish Singh <yosh@gimp.org> * gtk/gtk.symbols: add recent new functions. diff --git a/gtk/Makefile.am b/gtk/Makefile.am index 79dead6416..d4fdb05332 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -425,6 +425,8 @@ gtk_c_sources = \ gtkmenutoolbutton.c \ gtkmessagedialog.c \ gtkmisc.c \ + gtkmnemonichash.c \ + gtkmnemonichash.h \ gtkmodules.c \ gtknotebook.c \ gtkobject.c \ diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c index b9c4c2971f..8a95d94512 100644 --- a/gtk/gtklabel.c +++ b/gtk/gtklabel.c @@ -833,27 +833,59 @@ static void gtk_label_setup_mnemonic (GtkLabel *label, guint last_key) { + GtkWidget *widget = GTK_WIDGET (label); GtkWidget *toplevel; - - if (last_key != GDK_VoidSymbol && label->mnemonic_window) + GtkWidget *mnemonic_menu; + + mnemonic_menu = g_object_get_data (G_OBJECT (label), "gtk-mnemonic-menu"); + + if (last_key != GDK_VoidSymbol) { - gtk_window_remove_mnemonic (label->mnemonic_window, - last_key, - GTK_WIDGET (label)); - label->mnemonic_window = NULL; + if (label->mnemonic_window) + { + gtk_window_remove_mnemonic (label->mnemonic_window, + last_key, + widget); + label->mnemonic_window = NULL; + } + if (mnemonic_menu) + { + _gtk_menu_shell_remove_mnemonic (GTK_MENU_SHELL (mnemonic_menu), + last_key, + widget); + label->mnemonic_window = NULL; + } } if (label->mnemonic_keyval == GDK_VoidSymbol) return; - - toplevel = gtk_widget_get_toplevel (GTK_WIDGET (label)); + + toplevel = gtk_widget_get_toplevel (widget); if (GTK_WIDGET_TOPLEVEL (toplevel)) { - gtk_window_add_mnemonic (GTK_WINDOW (toplevel), - label->mnemonic_keyval, - GTK_WIDGET (label)); - label->mnemonic_window = GTK_WINDOW (toplevel); + GtkWidget *menu_shell; + + menu_shell = gtk_widget_get_ancestor (widget, + GTK_TYPE_MENU_SHELL); + + if (menu_shell) + { + _gtk_menu_shell_add_mnemonic (GTK_MENU_SHELL (menu_shell), + label->mnemonic_keyval, + widget); + mnemonic_menu = menu_shell; + } + + if (!(menu_shell && GTK_IS_MENU (menu_shell))) + { + gtk_window_add_mnemonic (GTK_WINDOW (toplevel), + label->mnemonic_keyval, + widget); + label->mnemonic_window = GTK_WINDOW (toplevel); + } } + + g_object_set_data (G_OBJECT (label), "gtk-mnemonic-menu", mnemonic_menu); } static void diff --git a/gtk/gtkmenushell.c b/gtk/gtkmenushell.c index 4e1a664e19..d318e167b8 100644 --- a/gtk/gtkmenushell.c +++ b/gtk/gtkmenushell.c @@ -30,11 +30,13 @@ #include "gdk/gdkkeysyms.h" #include "gtkalias.h" #include "gtkbindings.h" +#include "gtkkeyhash.h" #include "gtkmain.h" #include "gtkmarshalers.h" #include "gtkmenubar.h" #include "gtkmenuitem.h" #include "gtkmenushell.h" +#include "gtkmnemonichash.h" #include "gtktearoffmenuitem.h" #include "gtkwindow.h" @@ -112,9 +114,20 @@ typedef void (*GtkMenuShellSignal2) (GtkObject *object, * Cancels the current selection */ +#define GTK_MENU_SHELL_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_MENU_SHELL, GtkMenuShellPrivate)) + +typedef struct _GtkMenuShellPrivate GtkMenuShellPrivate; + +struct _GtkMenuShellPrivate +{ + GtkMnemonicHash *mnemonic_hash; + GtkKeyHash *key_hash; +}; + static void gtk_menu_shell_class_init (GtkMenuShellClass *klass); static void gtk_menu_shell_init (GtkMenuShell *menu_shell); static void gtk_menu_shell_realize (GtkWidget *widget); +static void gtk_menu_shell_finalize (GObject *object); static gint gtk_menu_shell_button_press (GtkWidget *widget, GdkEventButton *event); static gint gtk_menu_shell_button_release (GtkWidget *widget, @@ -125,6 +138,8 @@ static gint gtk_menu_shell_enter_notify (GtkWidget *widget, GdkEventCrossing *event); static gint gtk_menu_shell_leave_notify (GtkWidget *widget, GdkEventCrossing *event); +static void gtk_menu_shell_screen_changed (GtkWidget *widget, + GdkScreen *previous_screen); static void gtk_menu_shell_add (GtkContainer *container, GtkWidget *widget); static void gtk_menu_shell_remove (GtkContainer *container, @@ -154,6 +169,10 @@ static void gtk_real_menu_shell_cancel (GtkMenuShell *menu_shell) static void gtk_real_menu_shell_cycle_focus (GtkMenuShell *menu_shell, GtkDirectionType dir); +static void gtk_menu_shell_reset_key_hash (GtkMenuShell *menu_shell); +static gboolean gtk_menu_shell_activate_mnemonic (GtkMenuShell *menu_shell, + GdkEventKey *event); + static GtkContainerClass *parent_class = NULL; static guint menu_shell_signals[LAST_SIGNAL] = { 0 }; @@ -202,12 +221,15 @@ gtk_menu_shell_class_init (GtkMenuShellClass *klass) parent_class = g_type_class_peek_parent (klass); + object_class->finalize = gtk_menu_shell_finalize; + widget_class->realize = gtk_menu_shell_realize; widget_class->button_press_event = gtk_menu_shell_button_press; widget_class->button_release_event = gtk_menu_shell_button_release; widget_class->key_press_event = gtk_menu_shell_key_press; widget_class->enter_notify_event = gtk_menu_shell_enter_notify; widget_class->leave_notify_event = gtk_menu_shell_leave_notify; + widget_class->screen_changed = gtk_menu_shell_screen_changed; container_class->add = gtk_menu_shell_add; container_class->remove = gtk_menu_shell_remove; @@ -308,6 +330,8 @@ gtk_menu_shell_class_init (GtkMenuShellClass *klass) GDK_F10, GDK_SHIFT_MASK, "cycle_focus", 1, GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_BACKWARD); + + g_type_class_add_private (object_class, sizeof (GtkMenuShellPrivate)); } static GType @@ -319,6 +343,8 @@ gtk_menu_shell_child_type (GtkContainer *container) static void gtk_menu_shell_init (GtkMenuShell *menu_shell) { + GtkMenuShellPrivate *priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell); + menu_shell->children = NULL; menu_shell->active_menu_item = NULL; menu_shell->parent_menu_shell = NULL; @@ -327,8 +353,26 @@ gtk_menu_shell_init (GtkMenuShell *menu_shell) menu_shell->have_xgrab = FALSE; menu_shell->button = 0; menu_shell->activate_time = 0; + + priv->mnemonic_hash = NULL; + priv->key_hash = NULL; } +static void +gtk_menu_shell_finalize (GObject *object) +{ + GtkMenuShell *menu_shell = GTK_MENU_SHELL (object); + GtkMenuShellPrivate *priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell); + + if (priv->mnemonic_hash) + _gtk_mnemonic_hash_free (priv->mnemonic_hash); + if (priv->key_hash) + _gtk_key_hash_free (priv->key_hash); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + + void gtk_menu_shell_append (GtkMenuShell *menu_shell, GtkWidget *child) @@ -563,11 +607,10 @@ gtk_menu_shell_button_release (GtkWidget *widget, } static gint -gtk_menu_shell_key_press (GtkWidget *widget, +gtk_menu_shell_key_press (GtkWidget *widget, GdkEventKey *event) { GtkMenuShell *menu_shell; - GtkWidget *toplevel; g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE); g_return_val_if_fail (event != NULL, FALSE); @@ -580,12 +623,7 @@ gtk_menu_shell_key_press (GtkWidget *widget, if (gtk_bindings_activate_event (GTK_OBJECT (widget), event)) return TRUE; - toplevel = gtk_widget_get_toplevel (widget); - if (GTK_IS_WINDOW (toplevel) && - gtk_window_activate_key (GTK_WINDOW (toplevel), event)) - return TRUE; - - return FALSE; + return gtk_menu_shell_activate_mnemonic (menu_shell, event); } static gint @@ -674,6 +712,13 @@ gtk_menu_shell_leave_notify (GtkWidget *widget, } static void +gtk_menu_shell_screen_changed (GtkWidget *widget, + GdkScreen *previous_screen) +{ + gtk_menu_shell_reset_key_hash (GTK_MENU_SHELL (widget)); +} + +static void gtk_menu_shell_add (GtkContainer *container, GtkWidget *widget) { @@ -1218,3 +1263,120 @@ gtk_menu_shell_cancel (GtkMenuShell *menu_shell) g_signal_emit (menu_shell, menu_shell_signals[CANCEL], 0); } + +static GtkMnemonicHash * +gtk_menu_shell_get_mnemonic_hash (GtkMenuShell *menu_shell, + gboolean create) +{ + GtkMenuShellPrivate *private = GTK_MENU_SHELL_GET_PRIVATE (menu_shell); + + if (!private->mnemonic_hash && create) + private->mnemonic_hash = _gtk_mnemonic_hash_new (); + + return private->mnemonic_hash; +} + +static void +menu_shell_add_mnemonic_foreach (guint keyval, + GSList *targets, + gpointer data) +{ + GtkKeyHash *key_hash = data; + + _gtk_key_hash_add_entry (key_hash, keyval, 0, GUINT_TO_POINTER (keyval)); +} + +static GtkKeyHash * +gtk_menu_shell_get_key_hash (GtkMenuShell *menu_shell, + gboolean create) +{ + GtkMenuShellPrivate *private = GTK_MENU_SHELL_GET_PRIVATE (menu_shell); + GtkWidget *widget = GTK_WIDGET (menu_shell); + + if (!private->key_hash && create && gtk_widget_has_screen (widget)) + { + GtkMnemonicHash *mnemonic_hash = gtk_menu_shell_get_mnemonic_hash (menu_shell, FALSE); + GdkScreen *screen = gtk_widget_get_screen (widget); + GdkKeymap *keymap = gdk_keymap_get_for_display (gdk_screen_get_display (screen)); + + if (!mnemonic_hash) + return NULL; + + private->key_hash = _gtk_key_hash_new (keymap, NULL); + + _gtk_mnemonic_hash_foreach (mnemonic_hash, + menu_shell_add_mnemonic_foreach, + private->key_hash); + } + + return private->key_hash; +} + +static void +gtk_menu_shell_reset_key_hash (GtkMenuShell *menu_shell) +{ + GtkMenuShellPrivate *private = GTK_MENU_SHELL_GET_PRIVATE (menu_shell); + + if (private->key_hash) + { + _gtk_key_hash_free (private->key_hash); + private->key_hash = NULL; + } +} + +static gboolean +gtk_menu_shell_activate_mnemonic (GtkMenuShell *menu_shell, + GdkEventKey *event) +{ + GtkMnemonicHash *mnemonic_hash; + GtkKeyHash *key_hash; + GSList *entries; + gboolean result = FALSE; + + mnemonic_hash = gtk_menu_shell_get_mnemonic_hash (menu_shell, FALSE); + if (!mnemonic_hash) + return FALSE; + + key_hash = gtk_menu_shell_get_key_hash (menu_shell, TRUE); + if (!key_hash) + return FALSE; + + entries = _gtk_key_hash_lookup (key_hash, + event->hardware_keycode, + event->state, + gtk_accelerator_get_default_mod_mask (), + event->group); + + if (entries) + result = _gtk_mnemonic_hash_activate (mnemonic_hash, + GPOINTER_TO_UINT (entries->data)); + + return result; +} + +void +_gtk_menu_shell_add_mnemonic (GtkMenuShell *menu_shell, + guint keyval, + GtkWidget *target) +{ + g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell)); + g_return_if_fail (GTK_IS_WIDGET (target)); + + _gtk_mnemonic_hash_add (gtk_menu_shell_get_mnemonic_hash (menu_shell, TRUE), + keyval, target); + gtk_menu_shell_reset_key_hash (menu_shell); +} + +void +_gtk_menu_shell_remove_mnemonic (GtkMenuShell *menu_shell, + guint keyval, + GtkWidget *target) +{ + g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell)); + g_return_if_fail (GTK_IS_WIDGET (target)); + + _gtk_mnemonic_hash_remove (gtk_menu_shell_get_mnemonic_hash (menu_shell, TRUE), + keyval, target); + gtk_menu_shell_reset_key_hash (menu_shell); +} + diff --git a/gtk/gtkmenushell.h b/gtk/gtkmenushell.h index 791cf57016..499fc3175c 100644 --- a/gtk/gtkmenushell.h +++ b/gtk/gtkmenushell.h @@ -118,6 +118,13 @@ void _gtk_menu_shell_activate (GtkMenuShell *menu_shell); gint _gtk_menu_shell_get_popup_delay (GtkMenuShell *menu_shell); void gtk_menu_shell_cancel (GtkMenuShell *menu_shell); +void _gtk_menu_shell_add_mnemonic (GtkMenuShell *menu_shell, + guint keyval, + GtkWidget *target); +void _gtk_menu_shell_remove_mnemonic (GtkMenuShell *menu_shell, + guint keyval, + GtkWidget *target); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/gtk/gtkmnemonichash.c b/gtk/gtkmnemonichash.c new file mode 100644 index 0000000000..c9f38408c8 --- /dev/null +++ b/gtk/gtkmnemonichash.c @@ -0,0 +1,197 @@ +/* gtkmnemonichash.c: Sets of mnemonics with cycling + * + * GTK - The GIMP Toolkit + * Copyright (C) 2002, Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "gtkmnemonichash.h" + +struct _GtkMnemnonicHash +{ + GHashTable *hash; +}; + + +GtkMnemonicHash * +_gtk_mnemonic_hash_new (void) +{ + GtkMnemonicHash *mnemonic_hash = g_new (GtkMnemonicHash, 1); + + mnemonic_hash->hash = g_hash_table_new (g_direct_hash, NULL); + + return mnemonic_hash; +} + +static void +mnemonic_hash_free_foreach (gpointer key, + gpointer value, + gpointer user) +{ + guint keyval = GPOINTER_TO_UINT (key); + GSList *targets = value; + + gchar *name = gtk_accelerator_name (keyval, 0); + + g_warning ("mnemonic \"%s\" wasn't removed for widget (%p)", + name, targets->data); + g_free (name); + + g_slist_free (targets); +} + +void +_gtk_mnemonic_hash_free (GtkMnemonicHash *mnemonic_hash) +{ + g_hash_table_foreach (mnemonic_hash->hash, + mnemonic_hash_free_foreach, + NULL); + + g_hash_table_destroy (mnemonic_hash->hash); + g_free (mnemonic_hash); +} + +void +_gtk_mnemonic_hash_add (GtkMnemonicHash *mnemonic_hash, + guint keyval, + GtkWidget *target) +{ + gpointer key = GUINT_TO_POINTER (keyval); + GSList *targets, *new_targets; + + g_return_if_fail (GTK_IS_WIDGET (target)); + + targets = g_hash_table_lookup (mnemonic_hash->hash, key); + g_return_if_fail (g_slist_find (targets, target) == NULL); + + new_targets = g_slist_append (targets, target); + if (new_targets != targets) + g_hash_table_insert (mnemonic_hash->hash, key, new_targets); +} + +void +_gtk_mnemonic_hash_remove (GtkMnemonicHash *mnemonic_hash, + guint keyval, + GtkWidget *target) +{ + gpointer key = GUINT_TO_POINTER (keyval); + GSList *targets, *new_targets; + + g_return_if_fail (GTK_IS_WIDGET (target)); + + targets = g_hash_table_lookup (mnemonic_hash->hash, key); + + g_return_if_fail (targets && g_slist_find (targets, target) != NULL); + + new_targets = g_slist_remove (targets, target); + if (new_targets != targets) + { + if (new_targets == NULL) + g_hash_table_remove (mnemonic_hash->hash, key); + else + g_hash_table_insert (mnemonic_hash->hash, key, new_targets); + } +} + +gboolean +_gtk_mnemonic_hash_activate (GtkMnemonicHash *mnemonic_hash, + guint keyval) +{ + GSList *list, *targets; + GtkWidget *widget, *chosen_widget; + gboolean overloaded; + + targets = g_hash_table_lookup (mnemonic_hash->hash, + GUINT_TO_POINTER (keyval)); + if (!targets) + return FALSE; + + overloaded = FALSE; + chosen_widget = NULL; + for (list = targets; list; list = list->next) + { + widget = GTK_WIDGET (list->data); + + if (GTK_WIDGET_IS_SENSITIVE (widget) && + GTK_WIDGET_MAPPED (widget)) + { + if (chosen_widget) + { + overloaded = TRUE; + break; + } + else + chosen_widget = widget; + } + } + + if (chosen_widget) + { + /* For round robin we put the activated entry on + * the end of the list after activation + */ + targets = g_slist_remove (targets, chosen_widget); + targets = g_slist_append (targets, chosen_widget); + g_hash_table_insert (mnemonic_hash->hash, + GUINT_TO_POINTER (keyval), + targets); + + return gtk_widget_mnemonic_activate (chosen_widget, overloaded); + } + return FALSE; +} + +GSList * +_gtk_mnemonic_hash_lookup (GtkMnemonicHash *mnemonic_hash, + guint keyval) +{ + return g_hash_table_lookup (mnemonic_hash->hash, GUINT_TO_POINTER (keyval)); +} + +static void +mnemonic_hash_foreach_func (gpointer key, + gpointer value, + gpointer data) +{ + struct { + GtkMnemonicHashForeach func; + gpointer func_data; + } *info = data; + + guint keyval = GPOINTER_TO_UINT (key); + GSList *targets = value; + + (*info->func) (keyval, targets, info->func_data); +} + +void +_gtk_mnemonic_hash_foreach (GtkMnemonicHash *mnemonic_hash, + GtkMnemonicHashForeach func, + gpointer func_data) +{ + struct { + GtkMnemonicHashForeach func; + gpointer func_data; + } info; + + info.func = func; + info.func_data = func_data; + + g_hash_table_foreach (mnemonic_hash->hash, + mnemonic_hash_foreach_func, + &info); +} diff --git a/gtk/gtkmnemonichash.h b/gtk/gtkmnemonichash.h new file mode 100644 index 0000000000..534137f077 --- /dev/null +++ b/gtk/gtkmnemonichash.h @@ -0,0 +1,54 @@ +/* gtkmnemonichash.h: Sets of mnemonics with cycling + * + * GTK - The GIMP Toolkit + * Copyright (C) 2002, Red Hat Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GTK_MNEMONIC_HASH_H__ +#define __GTK_MNEMONIC_HASH_H__ + +#include <gdk/gdk.h> +#include <gtk/gtkwidget.h> + +G_BEGIN_DECLS + +typedef struct _GtkMnemnonicHash GtkMnemonicHash; + +typedef void (*GtkMnemonicHashForeach) (guint keyval, + GSList *targets, + gpointer data); + +GtkMnemonicHash *_gtk_mnemonic_hash_new (void); +void _gtk_mnemonic_hash_free (GtkMnemonicHash *mnemonic_hash); +void _gtk_mnemonic_hash_add (GtkMnemonicHash *mnemonic_hash, + guint keyval, + GtkWidget *target); +void _gtk_mnemonic_hash_remove (GtkMnemonicHash *mnemonic_hash, + guint keyval, + GtkWidget *target); +gboolean _gtk_mnemonic_hash_activate (GtkMnemonicHash *mnemonic_hash, + guint keyval); +GSList * _gtk_mnemonic_hash_lookup (GtkMnemonicHash *mnemonic_hash, + guint keyval); +void _gtk_mnemonic_hash_foreach (GtkMnemonicHash *mnemonic_hash, + GtkMnemonicHashForeach func, + gpointer func_data); + +G_END_DECLS + +#endif /* __GTK_MNEMONIC_HASH_H__ */ diff --git a/gtk/gtkwindow.c b/gtk/gtkwindow.c index 41c64cb5b0..d54e417c29 100644 --- a/gtk/gtkwindow.c +++ b/gtk/gtkwindow.c @@ -38,6 +38,7 @@ #include "gtkbindings.h" #include "gtkkeyhash.h" #include "gtkmain.h" +#include "gtkmnemonichash.h" #include "gtkiconfactory.h" #include "gtkicontheme.h" #include "gtkintl.h" @@ -147,21 +148,14 @@ struct _GtkWindowGeometryInfo GtkWindowLastGeometryInfo last; }; -typedef struct _GtkWindowMnemonic GtkWindowMnemonic; - -struct _GtkWindowMnemonic { - GtkWindow *window; - guint keyval; - - GSList *targets; -}; - #define GTK_WINDOW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_WINDOW, GtkWindowPrivate)) typedef struct _GtkWindowPrivate GtkWindowPrivate; struct _GtkWindowPrivate { + GtkMnemonicHash *mnemonic_hash; + guint above_initially : 1; guint below_initially : 1; guint fullscreen_initially : 1; @@ -278,7 +272,6 @@ static GtkKeyHash *gtk_window_get_key_hash (GtkWindow *window); static void gtk_window_free_key_hash (GtkWindow *window); static GSList *toplevel_list = NULL; -static GHashTable *mnemonic_hash_table = NULL; static GtkBinClass *parent_class = NULL; static guint window_signals[LAST_SIGNAL] = { 0 }; static GList *default_icon_list = NULL; @@ -297,35 +290,6 @@ static void gtk_window_get_property (GObject *object, GParamSpec *pspec); -static guint -mnemonic_hash (gconstpointer key) -{ - const GtkWindowMnemonic *k; - guint h; - - k = (GtkWindowMnemonic *)key; - - h = (gulong) k->window; - h ^= k->keyval << 16; - h ^= k->keyval >> 16; - - return h; -} - -static gboolean -mnemonic_equal (gconstpointer a, gconstpointer b) -{ - const GtkWindowMnemonic *ka; - const GtkWindowMnemonic *kb; - - ka = (GtkWindowMnemonic *)a; - kb = (GtkWindowMnemonic *)b; - - return - (ka->window == kb->window) && - (ka->keyval == kb->keyval); -} - GType gtk_window_get_type (void) { @@ -403,8 +367,6 @@ gtk_window_class_init (GtkWindowClass *klass) parent_class = g_type_class_peek_parent (klass); - mnemonic_hash_table = g_hash_table_new (mnemonic_hash, mnemonic_equal); - gobject_class->dispose = gtk_window_dispose; gobject_class->finalize = gtk_window_finalize; @@ -1404,6 +1366,17 @@ gtk_window_remove_accel_group (GtkWindow *window, _gtk_accel_group_detach (accel_group, G_OBJECT (window)); } +static GtkMnemonicHash * +gtk_window_get_mnemonic_hash (GtkWindow *window, + gboolean create) +{ + GtkWindowPrivate *private = GTK_WINDOW_GET_PRIVATE (window); + if (!private->mnemonic_hash && create) + private->mnemonic_hash = _gtk_mnemonic_hash_new (); + + return private->mnemonic_hash; +} + /** * gtk_window_add_mnemonic: * @window: a #GtkWindow @@ -1417,28 +1390,11 @@ gtk_window_add_mnemonic (GtkWindow *window, guint keyval, GtkWidget *target) { - GtkWindowMnemonic key; - GtkWindowMnemonic *mnemonic; - g_return_if_fail (GTK_IS_WINDOW (window)); g_return_if_fail (GTK_IS_WIDGET (target)); - - key.window = window; - key.keyval = keyval; - mnemonic = g_hash_table_lookup (mnemonic_hash_table, &key); - if (mnemonic) - { - g_return_if_fail (g_slist_find (mnemonic->targets, target) == NULL); - mnemonic->targets = g_slist_append (mnemonic->targets, target); - } - else - { - mnemonic = g_new (GtkWindowMnemonic, 1); - *mnemonic = key; - mnemonic->targets = g_slist_prepend (NULL, target); - g_hash_table_insert (mnemonic_hash_table, mnemonic, mnemonic); - } + _gtk_mnemonic_hash_add (gtk_window_get_mnemonic_hash (window, TRUE), + keyval, target); gtk_window_notify_keys_changed (window); } @@ -1455,24 +1411,11 @@ gtk_window_remove_mnemonic (GtkWindow *window, guint keyval, GtkWidget *target) { - GtkWindowMnemonic key; - GtkWindowMnemonic *mnemonic; - g_return_if_fail (GTK_IS_WINDOW (window)); g_return_if_fail (GTK_IS_WIDGET (target)); - key.window = window; - key.keyval = keyval; - mnemonic = g_hash_table_lookup (mnemonic_hash_table, &key); - - g_return_if_fail (mnemonic && g_slist_find (mnemonic->targets, target) != NULL); - - mnemonic->targets = g_slist_remove (mnemonic->targets, target); - if (mnemonic->targets == NULL) - { - g_hash_table_remove (mnemonic_hash_table, mnemonic); - g_free (mnemonic); - } + _gtk_mnemonic_hash_remove (gtk_window_get_mnemonic_hash (window, TRUE), + keyval, target); gtk_window_notify_keys_changed (window); } @@ -1490,56 +1433,15 @@ gtk_window_mnemonic_activate (GtkWindow *window, guint keyval, GdkModifierType modifier) { - GtkWindowMnemonic key; - GtkWindowMnemonic *mnemonic; - GSList *list; - GtkWidget *widget, *chosen_widget; - gboolean overloaded; - g_return_val_if_fail (GTK_IS_WINDOW (window), FALSE); - if (window->mnemonic_modifier != (modifier & gtk_accelerator_get_default_mod_mask ())) - return FALSE; - - key.window = window; - key.keyval = keyval; - mnemonic = g_hash_table_lookup (mnemonic_hash_table, &key); - - if (!mnemonic) - return FALSE; - - overloaded = FALSE; - chosen_widget = NULL; - list = mnemonic->targets; - while (list) - { - widget = GTK_WIDGET (list->data); - - if (GTK_WIDGET_IS_SENSITIVE (widget) && - GTK_WIDGET_DRAWABLE (widget) && - gdk_window_is_viewable (widget->window)) - { - if (chosen_widget) - { - overloaded = TRUE; - break; - } - else - chosen_widget = widget; - } - list = g_slist_next (list); - } - - if (chosen_widget) - { - /* For round robin we put the activated entry on - * the end of the list after activation - */ - mnemonic->targets = g_slist_remove (mnemonic->targets, chosen_widget); - mnemonic->targets = g_slist_append (mnemonic->targets, chosen_widget); + if (window->mnemonic_modifier == (modifier & gtk_accelerator_get_default_mod_mask ())) + { + GtkMnemonicHash *mnemonic_hash = gtk_window_get_mnemonic_hash (window, FALSE); + if (mnemonic_hash) + return _gtk_mnemonic_hash_activate (mnemonic_hash, keyval); + } - return gtk_widget_mnemonic_activate (chosen_widget, overloaded); - } return FALSE; } @@ -3847,45 +3749,21 @@ gtk_window_destroy (GtkObject *object) GTK_OBJECT_CLASS (parent_class)->destroy (object); } -static gboolean -gtk_window_mnemonic_hash_remove (gpointer key, - gpointer value, - gpointer user) -{ - GtkWindowMnemonic *mnemonic = key; - GtkWindow *window = user; - - if (mnemonic->window == window) - { - if (mnemonic->targets) - { - gchar *name = gtk_accelerator_name (mnemonic->keyval, 0); - - g_warning ("mnemonic \"%s\" wasn't removed for widget (%p)", - name, mnemonic->targets->data); - g_free (name); - } - g_slist_free (mnemonic->targets); - g_free (mnemonic); - - return TRUE; - } - return FALSE; -} - static void gtk_window_finalize (GObject *object) { GtkWindow *window = GTK_WINDOW (object); + GtkMnemonicHash *mnemonic_hash; g_free (window->title); g_free (window->wmclass_name); g_free (window->wmclass_class); g_free (window->wm_role); - g_hash_table_foreach_remove (mnemonic_hash_table, - gtk_window_mnemonic_hash_remove, - window); + mnemonic_hash = gtk_window_get_mnemonic_hash (window, FALSE); + if (mnemonic_hash) + _gtk_mnemonic_hash_free (mnemonic_hash); + if (window->geometry_info) { if (window->geometry_info->widget) @@ -4500,11 +4378,8 @@ _gtk_window_query_nonaccels (GtkWindow *window, /* mnemonics are considered locked accels */ if (accel_mods == window->mnemonic_modifier) { - GtkWindowMnemonic mkey; - - mkey.window = window; - mkey.keyval = accel_key; - if (g_hash_table_lookup (mnemonic_hash_table, &mkey)) + GtkMnemonicHash *mnemonic_hash = gtk_window_get_mnemonic_hash (window, FALSE); + if (mnemonic_hash && _gtk_mnemonic_hash_lookup (mnemonic_hash, accel_key)) return TRUE; } @@ -7288,9 +7163,9 @@ gtk_window_parse_geometry (GtkWindow *window, } static void -gtk_window_mnemonic_hash_foreach (gpointer key, - gpointer value, - gpointer data) +gtk_window_mnemonic_hash_foreach (guint keyval, + GSList *targets, + gpointer data) { struct { GtkWindow *window; @@ -7298,10 +7173,7 @@ gtk_window_mnemonic_hash_foreach (gpointer key, gpointer func_data; } *info = data; - GtkWindowMnemonic *mnemonic = value; - - if (mnemonic->window == info->window) - (*info->func) (info->window, mnemonic->keyval, info->window->mnemonic_modifier, TRUE, info->func_data); + (*info->func) (info->window, keyval, info->window->mnemonic_modifier, TRUE, info->func_data); } void @@ -7310,6 +7182,7 @@ _gtk_window_keys_foreach (GtkWindow *window, gpointer func_data) { GSList *groups; + GtkMnemonicHash *mnemonic_hash; struct { GtkWindow *window; @@ -7321,9 +7194,10 @@ _gtk_window_keys_foreach (GtkWindow *window, info.func = func; info.func_data = func_data; - g_hash_table_foreach (mnemonic_hash_table, - gtk_window_mnemonic_hash_foreach, - &info); + mnemonic_hash = gtk_window_get_mnemonic_hash (window, FALSE); + if (mnemonic_hash) + _gtk_mnemonic_hash_foreach (mnemonic_hash, + gtk_window_mnemonic_hash_foreach, &info); groups = gtk_accel_groups_from_object (G_OBJECT (window)); while (groups) |