]> Pileus Git - ~andy/gtk/blob - gtk/gtkmenushell.c
2.6.0 second try
[~andy/gtk] / gtk / gtkmenushell.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 #define GTK_MENU_INTERNALS
28
29 #include <config.h>
30 #include "gdk/gdkkeysyms.h"
31 #include "gtkalias.h"
32 #include "gtkbindings.h"
33 #include "gtkkeyhash.h"
34 #include "gtkmain.h"
35 #include "gtkmarshalers.h"
36 #include "gtkmenubar.h"
37 #include "gtkmenuitem.h"
38 #include "gtkmenushell.h"
39 #include "gtkmnemonichash.h"
40 #include "gtktearoffmenuitem.h"
41 #include "gtkwindow.h"
42
43 #define MENU_SHELL_TIMEOUT   500
44
45 enum {
46   DEACTIVATE,
47   SELECTION_DONE,
48   MOVE_CURRENT,
49   ACTIVATE_CURRENT,
50   CANCEL,
51   CYCLE_FOCUS,
52   LAST_SIGNAL
53 };
54
55 typedef void (*GtkMenuShellSignal1) (GtkObject           *object,
56                                      GtkMenuDirectionType arg1,
57                                      gpointer             data);
58 typedef void (*GtkMenuShellSignal2) (GtkObject *object,
59                                      gboolean   arg1,
60                                      gpointer   data);
61
62 /* Terminology:
63  * 
64  * A menu item can be "selected", this means that it is displayed
65  * in the prelight state, and if it has a submenu, that submenu
66  * will be popped up. 
67  * 
68  * A menu is "active" when it is visible onscreen and the user
69  * is selecting from it. A menubar is not active until the user
70  * clicks on one of its menuitems. When a menu is active,
71  * passing the mouse over a submenu will pop it up.
72  *
73  * menu_shell->active_menu_item, is however, not an "active"
74  * menu item (there is no such thing) but rather, the selected
75  * menu item in that MenuShell, if there is one.
76  *
77  * There is also is a concept of the current menu and a current
78  * menu item. The current menu item is the selected menu item
79  * that is furthest down in the heirarchy. (Every active menu_shell
80  * does not necessarily contain a selected menu item, but if
81  * it does, then menu_shell->parent_menu_shell must also contain
82  * a selected menu item. The current menu is the menu that 
83  * contains the current menu_item. It will always have a GTK
84  * grab and receive all key presses.
85  *
86  *
87  * Action signals:
88  *
89  *  ::move_current (GtkMenuDirection *dir)
90  *     Moves the current menu item in direction 'dir':
91  *
92  *       GTK_MENU_DIR_PARENT: To the parent menu shell
93  *       GTK_MENU_DIR_CHILD: To the child menu shell (if this item has
94  *          a submenu.
95  *       GTK_MENU_DIR_NEXT/PREV: To the next or previous item
96  *          in this menu.
97  * 
98  *     As a a bit of a hack to get movement between menus and
99  *     menubars working, if submenu_placement is different for
100  *     the menu and its MenuShell then the following apply:
101  * 
102  *       - For 'parent' the current menu is not just moved to
103  *         the parent, but moved to the previous entry in the parent
104  *       - For 'child', if there is no child, then current is
105  *         moved to the next item in the parent.
106  *
107  * 
108  *  ::activate_current (GBoolean *force_hide)
109  *     Activate the current item. If 'force_hide' is true, hide
110  *     the current menu item always. Otherwise, only hide
111  *     it if menu_item->klass->hide_on_activate is true.
112  *
113  *  ::cancel ()
114  *     Cancels the current selection
115  */
116
117 #define GTK_MENU_SHELL_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_MENU_SHELL, GtkMenuShellPrivate))
118
119 typedef struct _GtkMenuShellPrivate GtkMenuShellPrivate;
120
121 struct _GtkMenuShellPrivate
122 {
123   GtkMnemonicHash *mnemonic_hash;
124   GtkKeyHash *key_hash;
125 };
126
127 static void gtk_menu_shell_class_init        (GtkMenuShellClass *klass);
128 static void gtk_menu_shell_init              (GtkMenuShell      *menu_shell);
129 static void gtk_menu_shell_realize           (GtkWidget         *widget);
130 static void gtk_menu_shell_finalize          (GObject           *object);
131 static gint gtk_menu_shell_button_press      (GtkWidget         *widget,
132                                               GdkEventButton    *event);
133 static gint gtk_menu_shell_button_release    (GtkWidget         *widget,
134                                               GdkEventButton    *event);
135 static gint gtk_menu_shell_key_press         (GtkWidget         *widget,
136                                               GdkEventKey       *event);
137 static gint gtk_menu_shell_enter_notify      (GtkWidget         *widget,
138                                               GdkEventCrossing  *event);
139 static gint gtk_menu_shell_leave_notify      (GtkWidget         *widget,
140                                               GdkEventCrossing  *event);
141 static void gtk_menu_shell_screen_changed    (GtkWidget         *widget,
142                                               GdkScreen         *previous_screen);
143 static void gtk_menu_shell_add               (GtkContainer      *container,
144                                               GtkWidget         *widget);
145 static void gtk_menu_shell_remove            (GtkContainer      *container,
146                                               GtkWidget         *widget);
147 static void gtk_menu_shell_forall            (GtkContainer      *container,
148                                               gboolean           include_internals,
149                                               GtkCallback        callback,
150                                               gpointer           callback_data);
151 static void gtk_menu_shell_real_insert       (GtkMenuShell *menu_shell,
152                                               GtkWidget    *child,
153                                               gint          position);
154 static void gtk_real_menu_shell_deactivate   (GtkMenuShell      *menu_shell);
155 static gint gtk_menu_shell_is_item           (GtkMenuShell      *menu_shell,
156                                               GtkWidget         *child);
157 static GtkWidget *gtk_menu_shell_get_item    (GtkMenuShell      *menu_shell,
158                                               GdkEvent          *event);
159 static GType    gtk_menu_shell_child_type  (GtkContainer      *container);
160 static void gtk_menu_shell_real_select_item  (GtkMenuShell      *menu_shell,
161                                               GtkWidget         *menu_item);
162 static void gtk_menu_shell_select_submenu_first (GtkMenuShell   *menu_shell); 
163
164 static void gtk_real_menu_shell_move_current (GtkMenuShell      *menu_shell,
165                                               GtkMenuDirectionType direction);
166 static void gtk_real_menu_shell_activate_current (GtkMenuShell      *menu_shell,
167                                                   gboolean           force_hide);
168 static void gtk_real_menu_shell_cancel           (GtkMenuShell      *menu_shell);
169 static void gtk_real_menu_shell_cycle_focus      (GtkMenuShell      *menu_shell,
170                                                   GtkDirectionType   dir);
171
172 static void     gtk_menu_shell_reset_key_hash    (GtkMenuShell *menu_shell);
173 static gboolean gtk_menu_shell_activate_mnemonic (GtkMenuShell *menu_shell,
174                                                   GdkEventKey  *event);
175
176 static GtkContainerClass *parent_class = NULL;
177 static guint menu_shell_signals[LAST_SIGNAL] = { 0 };
178
179
180 GType
181 gtk_menu_shell_get_type (void)
182 {
183   static GType menu_shell_type = 0;
184
185   if (!menu_shell_type)
186     {
187       static const GTypeInfo menu_shell_info =
188       {
189         sizeof (GtkMenuShellClass),
190         NULL,           /* base_init */
191         NULL,           /* base_finalize */
192         (GClassInitFunc) gtk_menu_shell_class_init,
193         NULL,           /* class_finalize */
194         NULL,           /* class_data */
195         sizeof (GtkMenuShell),
196         0,              /* n_preallocs */
197         (GInstanceInitFunc) gtk_menu_shell_init,
198         NULL,           /* value_table */
199       };
200
201       menu_shell_type =
202         g_type_register_static (GTK_TYPE_CONTAINER, "GtkMenuShell",
203                                 &menu_shell_info, G_TYPE_FLAG_ABSTRACT);
204     }
205
206   return menu_shell_type;
207 }
208
209 static void
210 gtk_menu_shell_class_init (GtkMenuShellClass *klass)
211 {
212   GObjectClass *object_class;
213   GtkWidgetClass *widget_class;
214   GtkContainerClass *container_class;
215
216   GtkBindingSet *binding_set;
217
218   object_class = (GObjectClass*) klass;
219   widget_class = (GtkWidgetClass*) klass;
220   container_class = (GtkContainerClass*) klass;
221
222   parent_class = g_type_class_peek_parent (klass);
223
224   object_class->finalize = gtk_menu_shell_finalize;
225
226   widget_class->realize = gtk_menu_shell_realize;
227   widget_class->button_press_event = gtk_menu_shell_button_press;
228   widget_class->button_release_event = gtk_menu_shell_button_release;
229   widget_class->key_press_event = gtk_menu_shell_key_press;
230   widget_class->enter_notify_event = gtk_menu_shell_enter_notify;
231   widget_class->leave_notify_event = gtk_menu_shell_leave_notify;
232   widget_class->screen_changed = gtk_menu_shell_screen_changed;
233
234   container_class->add = gtk_menu_shell_add;
235   container_class->remove = gtk_menu_shell_remove;
236   container_class->forall = gtk_menu_shell_forall;
237   container_class->child_type = gtk_menu_shell_child_type;
238
239   klass->submenu_placement = GTK_TOP_BOTTOM;
240   klass->deactivate = gtk_real_menu_shell_deactivate;
241   klass->selection_done = NULL;
242   klass->move_current = gtk_real_menu_shell_move_current;
243   klass->activate_current = gtk_real_menu_shell_activate_current;
244   klass->cancel = gtk_real_menu_shell_cancel;
245   klass->select_item = gtk_menu_shell_real_select_item;
246   klass->insert = gtk_menu_shell_real_insert;
247
248   menu_shell_signals[DEACTIVATE] =
249     g_signal_new ("deactivate",
250                   G_OBJECT_CLASS_TYPE (object_class),
251                   G_SIGNAL_RUN_FIRST,
252                   G_STRUCT_OFFSET (GtkMenuShellClass, deactivate),
253                   NULL, NULL,
254                   _gtk_marshal_VOID__VOID,
255                   G_TYPE_NONE, 0);
256   menu_shell_signals[SELECTION_DONE] =
257     g_signal_new ("selection-done",
258                   G_OBJECT_CLASS_TYPE (object_class),
259                   G_SIGNAL_RUN_FIRST,
260                   G_STRUCT_OFFSET (GtkMenuShellClass, selection_done),
261                   NULL, NULL,
262                   _gtk_marshal_VOID__VOID,
263                   G_TYPE_NONE, 0);
264   menu_shell_signals[MOVE_CURRENT] =
265     g_signal_new ("move_current",
266                   G_OBJECT_CLASS_TYPE (object_class),
267                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
268                   G_STRUCT_OFFSET (GtkMenuShellClass, move_current),
269                   NULL, NULL,
270                   _gtk_marshal_VOID__ENUM,
271                   G_TYPE_NONE, 1, 
272                   GTK_TYPE_MENU_DIRECTION_TYPE);
273   menu_shell_signals[ACTIVATE_CURRENT] =
274     g_signal_new ("activate_current",
275                   G_OBJECT_CLASS_TYPE (object_class),
276                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
277                   G_STRUCT_OFFSET (GtkMenuShellClass, activate_current),
278                   NULL, NULL,
279                   _gtk_marshal_VOID__BOOLEAN,
280                   G_TYPE_NONE, 1, 
281                   G_TYPE_BOOLEAN);
282   menu_shell_signals[CANCEL] =
283     g_signal_new ("cancel",
284                   G_OBJECT_CLASS_TYPE (object_class),
285                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
286                   G_STRUCT_OFFSET (GtkMenuShellClass, cancel),
287                   NULL, NULL,
288                   _gtk_marshal_VOID__VOID,
289                   G_TYPE_NONE, 0);
290   menu_shell_signals[CYCLE_FOCUS] =
291     _gtk_binding_signal_new ("cycle_focus",
292                              G_OBJECT_CLASS_TYPE (object_class),
293                              G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
294                              G_CALLBACK (gtk_real_menu_shell_cycle_focus),
295                              NULL, NULL,
296                              _gtk_marshal_VOID__ENUM,
297                              G_TYPE_NONE, 1,
298                              GTK_TYPE_DIRECTION_TYPE);
299
300
301   binding_set = gtk_binding_set_by_class (klass);
302   gtk_binding_entry_add_signal (binding_set,
303                                 GDK_Escape, 0,
304                                 "cancel", 0);
305   gtk_binding_entry_add_signal (binding_set,
306                                 GDK_Return, 0,
307                                 "activate_current", 1,
308                                 G_TYPE_BOOLEAN,
309                                 TRUE);
310   gtk_binding_entry_add_signal (binding_set,
311                                 GDK_KP_Enter, 0,
312                                 "activate_current", 1,
313                                 G_TYPE_BOOLEAN,
314                                 TRUE);
315   gtk_binding_entry_add_signal (binding_set,
316                                 GDK_space, 0,
317                                 "activate_current", 1,
318                                 G_TYPE_BOOLEAN,
319                                 FALSE);
320   gtk_binding_entry_add_signal (binding_set,
321                                 GDK_KP_Space, 0,
322                                 "activate_current", 1,
323                                 G_TYPE_BOOLEAN,
324                                 FALSE);
325   gtk_binding_entry_add_signal (binding_set,
326                                 GDK_F10, 0,
327                                 "cycle_focus", 1,
328                                 GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_FORWARD);
329   gtk_binding_entry_add_signal (binding_set,
330                                 GDK_F10, GDK_SHIFT_MASK,
331                                 "cycle_focus", 1,
332                                 GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_BACKWARD);
333
334   g_type_class_add_private (object_class, sizeof (GtkMenuShellPrivate));
335 }
336
337 static GType
338 gtk_menu_shell_child_type (GtkContainer     *container)
339 {
340   return GTK_TYPE_MENU_ITEM;
341 }
342
343 static void
344 gtk_menu_shell_init (GtkMenuShell *menu_shell)
345 {
346   GtkMenuShellPrivate *priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
347
348   menu_shell->children = NULL;
349   menu_shell->active_menu_item = NULL;
350   menu_shell->parent_menu_shell = NULL;
351   menu_shell->active = FALSE;
352   menu_shell->have_grab = FALSE;
353   menu_shell->have_xgrab = FALSE;
354   menu_shell->button = 0;
355   menu_shell->activate_time = 0;
356
357   priv->mnemonic_hash = NULL;
358   priv->key_hash = NULL;
359 }
360
361 static void
362 gtk_menu_shell_finalize (GObject *object)
363 {
364   GtkMenuShell *menu_shell = GTK_MENU_SHELL (object);
365   GtkMenuShellPrivate *priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
366
367   if (priv->mnemonic_hash)
368     _gtk_mnemonic_hash_free (priv->mnemonic_hash);
369   if (priv->key_hash)
370     _gtk_key_hash_free (priv->key_hash);
371
372   G_OBJECT_CLASS (parent_class)->finalize (object);
373 }
374
375
376 void
377 gtk_menu_shell_append (GtkMenuShell *menu_shell,
378                        GtkWidget    *child)
379 {
380   gtk_menu_shell_insert (menu_shell, child, -1);
381 }
382
383 void
384 gtk_menu_shell_prepend (GtkMenuShell *menu_shell,
385                         GtkWidget    *child)
386 {
387   gtk_menu_shell_insert (menu_shell, child, 0);
388 }
389
390 void
391 gtk_menu_shell_insert (GtkMenuShell *menu_shell,
392                        GtkWidget    *child,
393                        gint          position)
394 {
395   GtkMenuShellClass *class;
396
397   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
398   g_return_if_fail (GTK_IS_MENU_ITEM (child));
399
400   class = GTK_MENU_SHELL_GET_CLASS (menu_shell);
401
402   if (class->insert)
403     class->insert (menu_shell, child, position);
404 }
405
406 static void
407 gtk_menu_shell_real_insert (GtkMenuShell *menu_shell,
408                             GtkWidget    *child,
409                             gint          position)
410 {
411   menu_shell->children = g_list_insert (menu_shell->children, child, position);
412
413   gtk_widget_set_parent (child, GTK_WIDGET (menu_shell));
414 }
415
416 void
417 gtk_menu_shell_deactivate (GtkMenuShell *menu_shell)
418 {
419   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
420
421   g_signal_emit (menu_shell, menu_shell_signals[DEACTIVATE], 0);
422 }
423
424 static void
425 gtk_menu_shell_realize (GtkWidget *widget)
426 {
427   GdkWindowAttr attributes;
428   gint attributes_mask;
429
430   g_return_if_fail (GTK_IS_MENU_SHELL (widget));
431
432   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
433
434   attributes.x = widget->allocation.x;
435   attributes.y = widget->allocation.y;
436   attributes.width = widget->allocation.width;
437   attributes.height = widget->allocation.height;
438   attributes.window_type = GDK_WINDOW_CHILD;
439   attributes.wclass = GDK_INPUT_OUTPUT;
440   attributes.visual = gtk_widget_get_visual (widget);
441   attributes.colormap = gtk_widget_get_colormap (widget);
442   attributes.event_mask = gtk_widget_get_events (widget);
443   attributes.event_mask |= (GDK_EXPOSURE_MASK |
444                             GDK_BUTTON_PRESS_MASK |
445                             GDK_BUTTON_RELEASE_MASK |
446                             GDK_KEY_PRESS_MASK |
447                             GDK_ENTER_NOTIFY_MASK |
448                             GDK_LEAVE_NOTIFY_MASK);
449
450   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
451   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
452   gdk_window_set_user_data (widget->window, widget);
453
454   widget->style = gtk_style_attach (widget->style, widget->window);
455   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
456 }
457
458 void
459 _gtk_menu_shell_activate (GtkMenuShell *menu_shell)
460 {
461   if (!menu_shell->active)
462     {
463       gtk_grab_add (GTK_WIDGET (menu_shell));
464       menu_shell->have_grab = TRUE;
465       menu_shell->active = TRUE;
466     }
467 }
468
469 static gint
470 gtk_menu_shell_button_press (GtkWidget      *widget,
471                              GdkEventButton *event)
472 {
473   GtkMenuShell *menu_shell;
474   GtkWidget *menu_item;
475
476   g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
477   g_return_val_if_fail (event != NULL, FALSE);
478
479   if (event->type != GDK_BUTTON_PRESS)
480     return FALSE;
481
482   menu_shell = GTK_MENU_SHELL (widget);
483
484   if (menu_shell->parent_menu_shell)
485     {
486       return gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
487     }
488   else if (!menu_shell->active || !menu_shell->button)
489     {
490       _gtk_menu_shell_activate (menu_shell);
491       
492       menu_shell->button = event->button;
493
494       menu_item = gtk_menu_shell_get_item (menu_shell, (GdkEvent *)event);
495
496       if (menu_item && _gtk_menu_item_is_selectable (menu_item))
497         {
498           if ((menu_item->parent == widget) &&
499               (menu_item != menu_shell->active_menu_item))
500             {
501               if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement == GTK_TOP_BOTTOM)
502                 {
503                   menu_shell->activate_time = event->time;
504                 }
505       
506               gtk_menu_shell_select_item (menu_shell, menu_item);
507             }
508         }
509     }
510   else
511     {
512       widget = gtk_get_event_widget ((GdkEvent*) event);
513       if (widget == GTK_WIDGET (menu_shell))
514         {
515           gtk_menu_shell_deactivate (menu_shell);
516           g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0);
517         }
518     }
519
520   return TRUE;
521 }
522
523 static gint
524 gtk_menu_shell_button_release (GtkWidget      *widget,
525                                GdkEventButton *event)
526 {
527   GtkMenuShell *menu_shell;
528   GtkWidget *menu_item;
529   gint deactivate;
530
531   g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
532   g_return_val_if_fail (event != NULL, FALSE);
533
534   menu_shell = GTK_MENU_SHELL (widget);
535   if (menu_shell->active)
536     {
537       if (menu_shell->button && (event->button != menu_shell->button))
538         {
539           menu_shell->button = 0;
540           if (menu_shell->parent_menu_shell)
541             return gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
542         }
543
544       menu_shell->button = 0;
545       menu_item = gtk_menu_shell_get_item (menu_shell, (GdkEvent*) event);
546
547       deactivate = TRUE;
548
549       if ((event->time - menu_shell->activate_time) > MENU_SHELL_TIMEOUT)
550         {
551           if (menu_item && (menu_shell->active_menu_item == menu_item) &&
552               _gtk_menu_item_is_selectable (menu_item))
553             {
554               if (GTK_MENU_ITEM (menu_item)->submenu == NULL)
555                 {
556                   gtk_menu_shell_activate_item (menu_shell, menu_item, TRUE);
557                   return TRUE;
558                 }
559               else if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement != GTK_TOP_BOTTOM)
560                 {
561                   gtk_menu_item_select (GTK_MENU_ITEM (menu_item));
562                   return TRUE;
563                 }
564             }
565           else if (menu_item &&
566                    !_gtk_menu_item_is_selectable (menu_item) &&
567                    GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement != GTK_TOP_BOTTOM)
568             {
569               deactivate = FALSE;
570             }
571           else if (menu_shell->parent_menu_shell)
572             {
573               menu_shell->active = TRUE;
574               gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
575               return TRUE;
576             }
577
578           /* If we ended up on an item with a submenu, leave the menu up.
579            */
580           if (menu_item && (menu_shell->active_menu_item == menu_item) &&
581               GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement != GTK_TOP_BOTTOM)
582             {
583               deactivate = FALSE;
584             }
585         }
586       else /* a very fast press-release */
587         {
588           /* We only ever want to prevent deactivation on the first
589            * press/release. Setting the time to zero is a bit of a
590            * hack, since we could be being triggered in the first
591            * few fractions of a second after a server time wraparound.
592            * the chances of that happening are ~1/10^6, without
593            * serious harm if we lose.
594            */
595           menu_shell->activate_time = 0;
596           deactivate = FALSE;
597         }
598       
599       if (deactivate)
600         {
601           gtk_menu_shell_deactivate (menu_shell);
602           g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0);
603         }
604     }
605
606   return TRUE;
607 }
608
609 static gint
610 gtk_menu_shell_key_press (GtkWidget   *widget,
611                           GdkEventKey *event)
612 {
613   GtkMenuShell *menu_shell;
614   
615   g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
616   g_return_val_if_fail (event != NULL, FALSE);
617       
618   menu_shell = GTK_MENU_SHELL (widget);
619
620   if (!menu_shell->active_menu_item && menu_shell->parent_menu_shell)
621     return gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent *)event);
622   
623   if (gtk_bindings_activate_event (GTK_OBJECT (widget), event))
624     return TRUE;
625
626   return gtk_menu_shell_activate_mnemonic (menu_shell, event);
627 }
628
629 static gint
630 gtk_menu_shell_enter_notify (GtkWidget        *widget,
631                              GdkEventCrossing *event)
632 {
633   GtkMenuShell *menu_shell;
634   GtkWidget *menu_item;
635
636   g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
637   g_return_val_if_fail (event != NULL, FALSE);
638
639   menu_shell = GTK_MENU_SHELL (widget);
640
641   if (menu_shell->active)
642     {
643       menu_item = gtk_get_event_widget ((GdkEvent*) event);
644
645       if (!menu_item ||
646           (GTK_IS_MENU_ITEM (menu_item) && 
647            !_gtk_menu_item_is_selectable (menu_item)))
648         return TRUE;
649       
650       if ((menu_item->parent == widget) &&
651           (menu_shell->active_menu_item != menu_item) &&
652           GTK_IS_MENU_ITEM (menu_item))
653         {
654           if (menu_shell->ignore_enter)
655             return TRUE;
656           
657           if ((event->detail != GDK_NOTIFY_INFERIOR) &&
658               (GTK_WIDGET_STATE (menu_item) != GTK_STATE_PRELIGHT))
659             {
660               gtk_menu_shell_select_item (menu_shell, menu_item);
661             }
662         }
663       else if (menu_shell->parent_menu_shell)
664         {
665           gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
666         }
667     }
668
669   return TRUE;
670 }
671
672 static gint
673 gtk_menu_shell_leave_notify (GtkWidget        *widget,
674                              GdkEventCrossing *event)
675 {
676   GtkMenuShell *menu_shell;
677   GtkMenuItem *menu_item;
678   GtkWidget *event_widget;
679
680   g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
681   g_return_val_if_fail (event != NULL, FALSE);
682
683   if (GTK_WIDGET_VISIBLE (widget))
684     {
685       menu_shell = GTK_MENU_SHELL (widget);
686       event_widget = gtk_get_event_widget ((GdkEvent*) event);
687
688       if (!event_widget || !GTK_IS_MENU_ITEM (event_widget))
689         return TRUE;
690
691       menu_item = GTK_MENU_ITEM (event_widget);
692
693       if (!_gtk_menu_item_is_selectable (event_widget))
694         return TRUE;
695
696       if ((menu_shell->active_menu_item == event_widget) &&
697           (menu_item->submenu == NULL))
698         {
699           if ((event->detail != GDK_NOTIFY_INFERIOR) &&
700               (GTK_WIDGET_STATE (menu_item) != GTK_STATE_NORMAL))
701             {
702               gtk_menu_shell_deselect (menu_shell);
703             }
704         }
705       else if (menu_shell->parent_menu_shell)
706         {
707           gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
708         }
709     }
710
711   return TRUE;
712 }
713
714 static void
715 gtk_menu_shell_screen_changed (GtkWidget *widget,
716                                GdkScreen *previous_screen)
717 {
718   gtk_menu_shell_reset_key_hash (GTK_MENU_SHELL (widget));
719 }
720
721 static void
722 gtk_menu_shell_add (GtkContainer *container,
723                     GtkWidget    *widget)
724 {
725   gtk_menu_shell_append (GTK_MENU_SHELL (container), widget);
726 }
727
728 static void
729 gtk_menu_shell_remove (GtkContainer *container,
730                        GtkWidget    *widget)
731 {
732   GtkMenuShell *menu_shell;
733   gint was_visible;
734   
735   g_return_if_fail (GTK_IS_MENU_SHELL (container));
736   g_return_if_fail (GTK_IS_MENU_ITEM (widget));
737   
738   was_visible = GTK_WIDGET_VISIBLE (widget);
739   menu_shell = GTK_MENU_SHELL (container);
740   menu_shell->children = g_list_remove (menu_shell->children, widget);
741   
742   if (widget == menu_shell->active_menu_item)
743     {
744       gtk_item_deselect (GTK_ITEM (menu_shell->active_menu_item));
745       menu_shell->active_menu_item = NULL;
746     }
747
748   gtk_widget_unparent (widget);
749   
750   /* queue resize regardless of GTK_WIDGET_VISIBLE (container),
751    * since that's what is needed by toplevels.
752    */
753   if (was_visible)
754     gtk_widget_queue_resize (GTK_WIDGET (container));
755 }
756
757 static void
758 gtk_menu_shell_forall (GtkContainer *container,
759                        gboolean      include_internals,
760                        GtkCallback   callback,
761                        gpointer      callback_data)
762 {
763   GtkMenuShell *menu_shell;
764   GtkWidget *child;
765   GList *children;
766
767   g_return_if_fail (GTK_IS_MENU_SHELL (container));
768   g_return_if_fail (callback != NULL);
769
770   menu_shell = GTK_MENU_SHELL (container);
771
772   children = menu_shell->children;
773   while (children)
774     {
775       child = children->data;
776       children = children->next;
777
778       (* callback) (child, callback_data);
779     }
780 }
781
782
783 static void
784 gtk_real_menu_shell_deactivate (GtkMenuShell *menu_shell)
785 {
786   if (menu_shell->active)
787     {
788       menu_shell->button = 0;
789       menu_shell->active = FALSE;
790       menu_shell->activate_time = 0;
791
792       if (menu_shell->active_menu_item)
793         {
794           gtk_menu_item_deselect (GTK_MENU_ITEM (menu_shell->active_menu_item));
795           menu_shell->active_menu_item = NULL;
796         }
797
798       if (menu_shell->have_grab)
799         {
800           menu_shell->have_grab = FALSE;
801           gtk_grab_remove (GTK_WIDGET (menu_shell));
802         }
803       if (menu_shell->have_xgrab)
804         {
805           GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (menu_shell));
806           
807           menu_shell->have_xgrab = FALSE;
808           gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
809           gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
810         }
811     }
812 }
813
814 static gint
815 gtk_menu_shell_is_item (GtkMenuShell *menu_shell,
816                         GtkWidget    *child)
817 {
818   GtkWidget *parent;
819
820   g_return_val_if_fail (GTK_IS_MENU_SHELL (menu_shell), FALSE);
821   g_return_val_if_fail (child != NULL, FALSE);
822
823   parent = child->parent;
824   while (parent && GTK_IS_MENU_SHELL (parent))
825     {
826       if (parent == (GtkWidget*) menu_shell)
827         return TRUE;
828       parent = GTK_MENU_SHELL (parent)->parent_menu_shell;
829     }
830
831   return FALSE;
832 }
833
834 static GtkWidget*
835 gtk_menu_shell_get_item (GtkMenuShell *menu_shell,
836                          GdkEvent     *event)
837 {
838   GtkWidget *menu_item;
839
840   menu_item = gtk_get_event_widget ((GdkEvent*) event);
841   
842   while (menu_item && !GTK_IS_MENU_ITEM (menu_item))
843     menu_item = menu_item->parent;
844
845   if (menu_item && gtk_menu_shell_is_item (menu_shell, menu_item))
846     return menu_item;
847   else
848     return NULL;
849 }
850
851 /* Handlers for action signals */
852
853 void
854 gtk_menu_shell_select_item (GtkMenuShell *menu_shell,
855                             GtkWidget    *menu_item)
856 {
857   GtkMenuShellClass *class;
858
859   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
860   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
861
862   class = GTK_MENU_SHELL_GET_CLASS (menu_shell);
863
864   if (class->select_item &&
865       !(menu_shell->active &&
866         menu_shell->active_menu_item == menu_item))
867     class->select_item (menu_shell, menu_item);
868 }
869
870 void _gtk_menu_item_set_placement (GtkMenuItem         *menu_item,
871                                    GtkSubmenuPlacement  placement);
872
873 static void
874 gtk_menu_shell_real_select_item (GtkMenuShell *menu_shell,
875                                  GtkWidget    *menu_item)
876 {
877   gtk_menu_shell_deselect (menu_shell);
878
879   if (!_gtk_menu_item_is_selectable (menu_item))
880     return;
881
882   menu_shell->active_menu_item = menu_item;
883   _gtk_menu_item_set_placement (GTK_MENU_ITEM (menu_shell->active_menu_item),
884                                GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement);
885   gtk_menu_item_select (GTK_MENU_ITEM (menu_shell->active_menu_item));
886
887   /* This allows the bizarre radio buttons-with-submenus-display-history
888    * behavior
889    */
890   if (GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu)
891     gtk_widget_activate (menu_shell->active_menu_item);
892 }
893
894 void
895 gtk_menu_shell_deselect (GtkMenuShell *menu_shell)
896 {
897   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
898
899   if (menu_shell->active_menu_item)
900     {
901       gtk_menu_item_deselect (GTK_MENU_ITEM (menu_shell->active_menu_item));
902       menu_shell->active_menu_item = NULL;
903     }
904 }
905
906 void
907 gtk_menu_shell_activate_item (GtkMenuShell      *menu_shell,
908                               GtkWidget         *menu_item,
909                               gboolean           force_deactivate)
910 {
911   GSList *slist, *shells = NULL;
912   gboolean deactivate = force_deactivate;
913
914   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
915   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
916
917   if (!deactivate)
918     deactivate = GTK_MENU_ITEM_GET_CLASS (menu_item)->hide_on_activate;
919
920   g_object_ref (menu_shell);
921
922   if (deactivate)
923     {
924       GtkMenuShell *parent_menu_shell = menu_shell;
925
926       do
927         {
928           g_object_ref (parent_menu_shell);
929           shells = g_slist_prepend (shells, parent_menu_shell);
930           parent_menu_shell = (GtkMenuShell*) parent_menu_shell->parent_menu_shell;
931         }
932       while (parent_menu_shell);
933       shells = g_slist_reverse (shells);
934
935       gtk_menu_shell_deactivate (menu_shell);
936   
937       /* flush the x-queue, so any grabs are removed and
938        * the menu is actually taken down
939        */
940       gdk_display_sync (gtk_widget_get_display (menu_item));
941     }
942
943   gtk_widget_activate (menu_item);
944
945   for (slist = shells; slist; slist = slist->next)
946     {
947       g_signal_emit (slist->data, menu_shell_signals[SELECTION_DONE], 0);
948       g_object_unref (slist->data);
949     }
950   g_slist_free (shells);
951
952   g_object_unref (menu_shell);
953 }
954
955 /* Distance should be +/- 1 */
956 static void
957 gtk_menu_shell_move_selected (GtkMenuShell  *menu_shell, 
958                               gint           distance)
959 {
960   if (menu_shell->active_menu_item)
961     {
962       GList *node = g_list_find (menu_shell->children,
963                                  menu_shell->active_menu_item);
964       GList *start_node = node;
965       
966       if (distance > 0)
967         {
968           node = node->next;
969           while (node != start_node && 
970                  (!node || !_gtk_menu_item_is_selectable (node->data)))
971             {
972               if (!node)
973                 node = menu_shell->children;
974               else
975                 node = node->next;
976             }
977         }
978       else
979         {
980           node = node->prev;
981           while (node != start_node &&
982                  (!node || !_gtk_menu_item_is_selectable (node->data)))
983             {
984               if (!node)
985                 node = g_list_last (menu_shell->children);
986               else
987                 node = node->prev;
988             }
989         }
990       
991       if (node)
992         gtk_menu_shell_select_item (menu_shell, node->data);
993     }
994 }
995
996 /**
997  * gtk_menu_shell_select_first:
998  * @menu_shell: a #GtkMenuShell
999  * @search_sensitive: if %TRUE, search for the first selectable
1000  *                    menu item, otherwise select nothing if
1001  *                    the first item isn't sensitive. This
1002  *                    should be %FALSE if the menu is being
1003  *                    popped up initially.
1004  * 
1005  * Select the first visible or selectable child of the menu shell;
1006  * don't select tearoff items unless the only item is a tearoff
1007  * item.
1008  *
1009  * Since: 2.2
1010  **/
1011 void
1012 gtk_menu_shell_select_first (GtkMenuShell *menu_shell,
1013                              gboolean      search_sensitive)
1014 {
1015   GtkWidget *to_select = NULL;
1016   GList *tmp_list;
1017
1018   tmp_list = menu_shell->children;
1019   while (tmp_list)
1020     {
1021       GtkWidget *child = tmp_list->data;
1022       
1023       if ((!search_sensitive && GTK_WIDGET_VISIBLE (child)) ||
1024           _gtk_menu_item_is_selectable (child))
1025         {
1026           to_select = child;
1027           if (!GTK_IS_TEAROFF_MENU_ITEM (child))
1028             break;
1029         }
1030       
1031       tmp_list = tmp_list->next;
1032     }
1033
1034   if (to_select)
1035     gtk_menu_shell_select_item (menu_shell, to_select);
1036 }
1037
1038 void
1039 _gtk_menu_shell_select_last (GtkMenuShell *menu_shell,
1040                              gboolean      search_sensitive)
1041 {
1042   GtkWidget *to_select = NULL;
1043   GList *tmp_list;
1044
1045   tmp_list = g_list_last (menu_shell->children);
1046   while (tmp_list)
1047     {
1048       GtkWidget *child = tmp_list->data;
1049       
1050       if ((!search_sensitive && GTK_WIDGET_VISIBLE (child)) ||
1051           _gtk_menu_item_is_selectable (child))
1052         {
1053           to_select = child;
1054           if (!GTK_IS_TEAROFF_MENU_ITEM (child))
1055             break;
1056         }
1057       
1058       tmp_list = tmp_list->prev;
1059     }
1060
1061   if (to_select)
1062     gtk_menu_shell_select_item (menu_shell, to_select);
1063 }
1064
1065 static void
1066 gtk_menu_shell_select_submenu_first (GtkMenuShell     *menu_shell)
1067 {
1068   GtkMenuItem *menu_item;
1069
1070   menu_item = GTK_MENU_ITEM (menu_shell->active_menu_item); 
1071   
1072   if (menu_item->submenu)
1073     {
1074       _gtk_menu_item_popup_submenu (GTK_WIDGET (menu_item));
1075       gtk_menu_shell_select_first (GTK_MENU_SHELL (menu_item->submenu), TRUE);
1076     }
1077 }
1078
1079 static void
1080 gtk_real_menu_shell_move_current (GtkMenuShell      *menu_shell,
1081                                   GtkMenuDirectionType direction)
1082 {
1083   GtkMenuShell *parent_menu_shell = NULL;
1084   gboolean had_selection;
1085
1086   had_selection = menu_shell->active_menu_item != NULL;
1087
1088   if (menu_shell->parent_menu_shell)
1089     parent_menu_shell = GTK_MENU_SHELL (menu_shell->parent_menu_shell);
1090   
1091   if (gtk_widget_get_direction (GTK_WIDGET (menu_shell)) == GTK_TEXT_DIR_RTL)
1092     {
1093       switch (direction) 
1094         {
1095         case GTK_MENU_DIR_PARENT:
1096           direction = GTK_MENU_DIR_CHILD;
1097           break;
1098         case GTK_MENU_DIR_CHILD:
1099           direction = GTK_MENU_DIR_PARENT;
1100           break;
1101         case GTK_MENU_DIR_PREV:
1102           if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement == GTK_TOP_BOTTOM)
1103             direction = GTK_MENU_DIR_NEXT;
1104           break;
1105         case GTK_MENU_DIR_NEXT:
1106           if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement == GTK_TOP_BOTTOM)
1107             direction = GTK_MENU_DIR_PREV;
1108           break;
1109         default: ;
1110         }
1111     }
1112   
1113   switch (direction)
1114     {
1115     case GTK_MENU_DIR_PARENT:
1116       if (parent_menu_shell)
1117         {
1118           if (GTK_MENU_SHELL_GET_CLASS (parent_menu_shell)->submenu_placement == 
1119                        GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement)
1120             gtk_menu_shell_deselect (menu_shell);
1121           else 
1122             {
1123               gtk_menu_shell_move_selected (parent_menu_shell, -1);
1124               gtk_menu_shell_select_submenu_first (parent_menu_shell); 
1125             }
1126         }
1127       /* If there is no parent and the submenu is in the opposite direction
1128        * to the menu, then make the PARENT direction wrap around to
1129        * the bottom of the submenu.
1130        */
1131       else if (menu_shell->active_menu_item &&
1132                _gtk_menu_item_is_selectable (menu_shell->active_menu_item) &&
1133                GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu)
1134         {
1135           GtkMenuShell *submenu = GTK_MENU_SHELL (GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu);
1136
1137           if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement !=
1138               GTK_MENU_SHELL_GET_CLASS (submenu)->submenu_placement)
1139             _gtk_menu_shell_select_last (submenu, TRUE);
1140         }
1141       break;
1142       
1143     case GTK_MENU_DIR_CHILD:
1144       if (menu_shell->active_menu_item &&
1145           _gtk_menu_item_is_selectable (menu_shell->active_menu_item) &&
1146           GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu)
1147         {
1148           gtk_menu_shell_select_submenu_first (menu_shell);
1149         }
1150       else
1151         {
1152           /* Try to find a menu running the opposite direction */
1153           while (parent_menu_shell && 
1154                  (GTK_MENU_SHELL_GET_CLASS (parent_menu_shell)->submenu_placement ==
1155                   GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement))
1156             {
1157               GtkWidget *tmp_widget = parent_menu_shell->parent_menu_shell;
1158
1159               if (tmp_widget)
1160                 parent_menu_shell = GTK_MENU_SHELL (tmp_widget);
1161               else
1162                 parent_menu_shell = NULL;
1163             }
1164
1165           if (parent_menu_shell)
1166             {
1167               gtk_menu_shell_move_selected (parent_menu_shell, 1);
1168               gtk_menu_shell_select_submenu_first (parent_menu_shell);
1169             }
1170         }
1171       break;
1172       
1173     case GTK_MENU_DIR_PREV:
1174       gtk_menu_shell_move_selected (menu_shell, -1);
1175       if (!had_selection &&
1176           !menu_shell->active_menu_item &&
1177           menu_shell->children)
1178         _gtk_menu_shell_select_last (menu_shell, TRUE);
1179       break;
1180     case GTK_MENU_DIR_NEXT:
1181       gtk_menu_shell_move_selected (menu_shell, 1);
1182       if (!had_selection &&
1183           !menu_shell->active_menu_item &&
1184           menu_shell->children)
1185         gtk_menu_shell_select_first (menu_shell, TRUE);
1186       break;
1187     }
1188   
1189 }
1190
1191 static void
1192 gtk_real_menu_shell_activate_current (GtkMenuShell      *menu_shell,
1193                                       gboolean           force_hide)
1194 {
1195   if (menu_shell->active_menu_item &&
1196       _gtk_menu_item_is_selectable (menu_shell->active_menu_item))
1197   {
1198    
1199     if (GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu == NULL)
1200       gtk_menu_shell_activate_item (menu_shell,
1201                                     menu_shell->active_menu_item,
1202                                     force_hide);
1203     else
1204       _gtk_menu_item_popup_submenu (menu_shell->active_menu_item);
1205   }
1206 }
1207
1208 static void
1209 gtk_real_menu_shell_cancel (GtkMenuShell      *menu_shell)
1210 {
1211   /* Unset the active menu item so gtk_menu_popdown() doesn't see it.
1212    */
1213   gtk_menu_shell_deselect (menu_shell);
1214   
1215   gtk_menu_shell_deactivate (menu_shell);
1216   g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0);
1217 }
1218
1219 static void
1220 gtk_real_menu_shell_cycle_focus (GtkMenuShell      *menu_shell,
1221                                  GtkDirectionType   dir)
1222 {
1223   while (menu_shell && !GTK_IS_MENU_BAR (menu_shell))
1224     {
1225       if (menu_shell->parent_menu_shell)
1226         menu_shell = GTK_MENU_SHELL (menu_shell->parent_menu_shell);
1227       else
1228         menu_shell = NULL;
1229     }
1230
1231   if (menu_shell)
1232     _gtk_menu_bar_cycle_focus (GTK_MENU_BAR (menu_shell), dir);
1233 }
1234
1235 gint
1236 _gtk_menu_shell_get_popup_delay (GtkMenuShell *menu_shell)
1237 {
1238   GtkMenuShellClass *klass = GTK_MENU_SHELL_GET_CLASS (menu_shell);
1239   
1240   if (klass->get_popup_delay)
1241     {
1242       return klass->get_popup_delay (menu_shell);
1243     }
1244   else
1245     {
1246       gint popup_delay;
1247       GtkWidget *widget = GTK_WIDGET (menu_shell);
1248       
1249       g_object_get (gtk_widget_get_settings (widget),
1250                     "gtk-menu-popup-delay", &popup_delay,
1251                     NULL);
1252       
1253       return popup_delay;
1254     }
1255 }
1256
1257 /**
1258  * gtk_menu_shell_cancel:
1259  * @menu_shell: a #GtkMenuShell
1260  * 
1261  * Cancels the selection within the menu shell.  
1262  * 
1263  * Since: 2.4
1264  */
1265 void
1266 gtk_menu_shell_cancel (GtkMenuShell *menu_shell)
1267 {
1268   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1269
1270   g_signal_emit (menu_shell, menu_shell_signals[CANCEL], 0);
1271 }
1272
1273 static GtkMnemonicHash *
1274 gtk_menu_shell_get_mnemonic_hash (GtkMenuShell *menu_shell,
1275                                   gboolean      create)
1276 {
1277   GtkMenuShellPrivate *private = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
1278
1279   if (!private->mnemonic_hash && create)
1280     private->mnemonic_hash = _gtk_mnemonic_hash_new ();
1281   
1282   return private->mnemonic_hash;
1283 }
1284
1285 static void
1286 menu_shell_add_mnemonic_foreach (guint    keyval,
1287                                  GSList  *targets,
1288                                  gpointer data)
1289 {
1290   GtkKeyHash *key_hash = data;
1291
1292   _gtk_key_hash_add_entry (key_hash, keyval, 0, GUINT_TO_POINTER (keyval));
1293 }
1294
1295 static GtkKeyHash *
1296 gtk_menu_shell_get_key_hash (GtkMenuShell *menu_shell,
1297                              gboolean      create)
1298 {
1299   GtkMenuShellPrivate *private = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
1300   GtkWidget *widget = GTK_WIDGET (menu_shell);
1301
1302   if (!private->key_hash && create && gtk_widget_has_screen (widget))
1303     {
1304       GtkMnemonicHash *mnemonic_hash = gtk_menu_shell_get_mnemonic_hash (menu_shell, FALSE);
1305       GdkScreen *screen = gtk_widget_get_screen (widget);
1306       GdkKeymap *keymap = gdk_keymap_get_for_display (gdk_screen_get_display (screen));
1307
1308       if (!mnemonic_hash)
1309         return NULL;
1310       
1311       private->key_hash = _gtk_key_hash_new (keymap, NULL);
1312
1313       _gtk_mnemonic_hash_foreach (mnemonic_hash,
1314                                   menu_shell_add_mnemonic_foreach,
1315                                   private->key_hash);
1316     }
1317   
1318   return private->key_hash;
1319 }
1320
1321 static void
1322 gtk_menu_shell_reset_key_hash (GtkMenuShell *menu_shell)
1323 {
1324   GtkMenuShellPrivate *private = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
1325
1326   if (private->key_hash)
1327     {
1328       _gtk_key_hash_free (private->key_hash);
1329       private->key_hash = NULL;
1330     }
1331 }
1332
1333 static gboolean
1334 gtk_menu_shell_activate_mnemonic (GtkMenuShell *menu_shell,
1335                                   GdkEventKey  *event)
1336 {
1337   GtkMnemonicHash *mnemonic_hash;
1338   GtkKeyHash *key_hash;
1339   GSList *entries;
1340   gboolean result = FALSE;
1341
1342   mnemonic_hash = gtk_menu_shell_get_mnemonic_hash (menu_shell, FALSE);
1343   if (!mnemonic_hash)
1344     return FALSE;
1345
1346   key_hash = gtk_menu_shell_get_key_hash (menu_shell, TRUE);
1347   if (!key_hash)
1348     return FALSE;
1349   
1350   entries = _gtk_key_hash_lookup (key_hash,
1351                                   event->hardware_keycode,
1352                                   event->state,
1353                                   gtk_accelerator_get_default_mod_mask (),
1354                                   event->group);
1355
1356   if (entries)
1357     result = _gtk_mnemonic_hash_activate (mnemonic_hash,
1358                                           GPOINTER_TO_UINT (entries->data));
1359
1360   return result;
1361 }
1362
1363 void
1364 _gtk_menu_shell_add_mnemonic (GtkMenuShell *menu_shell,
1365                               guint      keyval,
1366                               GtkWidget *target)
1367 {
1368   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1369   g_return_if_fail (GTK_IS_WIDGET (target));
1370
1371   _gtk_mnemonic_hash_add (gtk_menu_shell_get_mnemonic_hash (menu_shell, TRUE),
1372                           keyval, target);
1373   gtk_menu_shell_reset_key_hash (menu_shell);
1374 }
1375
1376 void
1377 _gtk_menu_shell_remove_mnemonic (GtkMenuShell *menu_shell,
1378                                  guint      keyval,
1379                                  GtkWidget *target)
1380 {
1381   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1382   g_return_if_fail (GTK_IS_WIDGET (target));
1383   
1384   _gtk_mnemonic_hash_remove (gtk_menu_shell_get_mnemonic_hash (menu_shell, TRUE),
1385                              keyval, target);
1386   gtk_menu_shell_reset_key_hash (menu_shell);
1387 }
1388