]> Pileus Git - ~andy/gtk/commitdiff
Support no-Alt mnemnonics in menu bars (#101309, Owen Taylor)
authorMatthias Clasen <mclasen@redhat.com>
Fri, 10 Dec 2004 22:09:22 +0000 (22:09 +0000)
committerMatthias Clasen <matthiasc@src.gnome.org>
Fri, 10 Dec 2004 22:09:22 +0000 (22:09 +0000)
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.

ChangeLog
ChangeLog.pre-2-10
ChangeLog.pre-2-6
ChangeLog.pre-2-8
gtk/Makefile.am
gtk/gtklabel.c
gtk/gtkmenushell.c
gtk/gtkmenushell.h
gtk/gtkmnemonichash.c [new file with mode: 0644]
gtk/gtkmnemonichash.h [new file with mode: 0644]
gtk/gtkwindow.c

index 65a8165745d9d308d9d9ba25a3ecf69e74720aa4..7d21818a203b1dfce6a49cc12e67dcaf015f5568 100644 (file)
--- 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.
index 65a8165745d9d308d9d9ba25a3ecf69e74720aa4..7d21818a203b1dfce6a49cc12e67dcaf015f5568 100644 (file)
@@ -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.
index 65a8165745d9d308d9d9ba25a3ecf69e74720aa4..7d21818a203b1dfce6a49cc12e67dcaf015f5568 100644 (file)
@@ -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.
index 65a8165745d9d308d9d9ba25a3ecf69e74720aa4..7d21818a203b1dfce6a49cc12e67dcaf015f5568 100644 (file)
@@ -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.
index 79dead6416efe8321fabdd88be28b9e5604c4a8f..d4fdb05332c0a6d756c0cb522a7bd06d16470f44 100644 (file)
@@ -425,6 +425,8 @@ gtk_c_sources =                 \
        gtkmenutoolbutton.c     \
        gtkmessagedialog.c      \
        gtkmisc.c               \
+       gtkmnemonichash.c       \
+       gtkmnemonichash.h       \
        gtkmodules.c            \
        gtknotebook.c           \
        gtkobject.c             \
index b9c4c2971f48fa74be041bf7cd15ea655d8ae483..8a95d945122f699f72113744a4de014edf0e5ccb 100644 (file)
@@ -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
index 4e1a664e198403446060c3462039625368b67024..d318e167b80cdedba21fc54aa85b661e8916ad73 100644 (file)
 #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
@@ -673,6 +711,13 @@ gtk_menu_shell_leave_notify (GtkWidget        *widget,
   return TRUE;
 }
 
+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);
+}
+
index 791cf570165c6c343e8a277861d3fc9c9e0abbf1..499fc3175cd97a7c65655ba40b3ce0edccba8752 100644 (file)
@@ -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 (file)
index 0000000..c9f3840
--- /dev/null
@@ -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 (file)
index 0000000..534137f
--- /dev/null
@@ -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__ */
index 41c64cb5b0aa46946af7768e43b88379138b2446..d54e417c29d664e8447fe513f4f799b212e70654 100644 (file)
@@ -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)