summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog20
-rw-r--r--ChangeLog.pre-2-1020
-rw-r--r--ChangeLog.pre-2-620
-rw-r--r--ChangeLog.pre-2-820
-rw-r--r--gtk/Makefile.am2
-rw-r--r--gtk/gtklabel.c56
-rw-r--r--gtk/gtkmenushell.c178
-rw-r--r--gtk/gtkmenushell.h7
-rw-r--r--gtk/gtkmnemonichash.c197
-rw-r--r--gtk/gtkmnemonichash.h54
-rw-r--r--gtk/gtkwindow.c206
11 files changed, 594 insertions, 186 deletions
diff --git a/ChangeLog b/ChangeLog
index 65a8165745..7d21818a20 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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)