]> Pileus Git - ~andy/gtk/blob - gtk/gtkmenushell.c
Only show the mnemonic underline when pressing Alt
[~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 "gtkbindings.h"
32 #include "gtkkeyhash.h"
33 #include "gtklabel.h"
34 #include "gtkmain.h"
35 #include "gtkmarshalers.h"
36 #include "gtkmenu.h"
37 #include "gtkmenubar.h"
38 #include "gtkmenuitem.h"
39 #include "gtkmenushell.h"
40 #include "gtkmnemonichash.h"
41 #include "gtktearoffmenuitem.h"
42 #include "gtkwindow.h"
43 #include "gtkprivate.h"
44 #include "gtkintl.h"
45 #include "gtkalias.h"
46
47 #define MENU_SHELL_TIMEOUT   500
48
49 #define PACK_DIRECTION(m)                                 \
50    (GTK_IS_MENU_BAR (m)                                   \
51      ? gtk_menu_bar_get_pack_direction (GTK_MENU_BAR (m)) \
52      : GTK_PACK_DIRECTION_LTR)
53
54 enum {
55   DEACTIVATE,
56   SELECTION_DONE,
57   MOVE_CURRENT,
58   ACTIVATE_CURRENT,
59   CANCEL,
60   CYCLE_FOCUS,
61   MOVE_SELECTED,
62   LAST_SIGNAL
63 };
64
65 enum {
66   PROP_0,
67   PROP_TAKE_FOCUS
68 };
69
70 /* Terminology:
71  * 
72  * A menu item can be "selected", this means that it is displayed
73  * in the prelight state, and if it has a submenu, that submenu
74  * will be popped up. 
75  * 
76  * A menu is "active" when it is visible onscreen and the user
77  * is selecting from it. A menubar is not active until the user
78  * clicks on one of its menuitems. When a menu is active,
79  * passing the mouse over a submenu will pop it up.
80  *
81  * menu_shell->active_menu_item, is however, not an "active"
82  * menu item (there is no such thing) but rather, the selected
83  * menu item in that MenuShell, if there is one.
84  *
85  * There is also is a concept of the current menu and a current
86  * menu item. The current menu item is the selected menu item
87  * that is furthest down in the hierarchy. (Every active menu_shell
88  * does not necessarily contain a selected menu item, but if
89  * it does, then menu_shell->parent_menu_shell must also contain
90  * a selected menu item. The current menu is the menu that 
91  * contains the current menu_item. It will always have a GTK
92  * grab and receive all key presses.
93  *
94  *
95  * Action signals:
96  *
97  *  ::move_current (GtkMenuDirection *dir)
98  *     Moves the current menu item in direction 'dir':
99  *
100  *       GTK_MENU_DIR_PARENT: To the parent menu shell
101  *       GTK_MENU_DIR_CHILD: To the child menu shell (if this item has
102  *          a submenu.
103  *       GTK_MENU_DIR_NEXT/PREV: To the next or previous item
104  *          in this menu.
105  * 
106  *     As a a bit of a hack to get movement between menus and
107  *     menubars working, if submenu_placement is different for
108  *     the menu and its MenuShell then the following apply:
109  * 
110  *       - For 'parent' the current menu is not just moved to
111  *         the parent, but moved to the previous entry in the parent
112  *       - For 'child', if there is no child, then current is
113  *         moved to the next item in the parent.
114  *
115  *    Note that the above explanation of ::move_current was written
116  *    before menus and menubars had support for RTL flipping and
117  *    different packing directions, and therefore only applies for
118  *    when text direction and packing direction are both left-to-right.
119  * 
120  *  ::activate_current (GBoolean *force_hide)
121  *     Activate the current item. If 'force_hide' is true, hide
122  *     the current menu item always. Otherwise, only hide
123  *     it if menu_item->klass->hide_on_activate is true.
124  *
125  *  ::cancel ()
126  *     Cancels the current selection
127  */
128
129 #define GTK_MENU_SHELL_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_MENU_SHELL, GtkMenuShellPrivate))
130
131 typedef struct _GtkMenuShellPrivate GtkMenuShellPrivate;
132
133 struct _GtkMenuShellPrivate
134 {
135   GtkMnemonicHash *mnemonic_hash;
136   GtkKeyHash *key_hash;
137
138   guint take_focus : 1;
139   guint activated_submenu : 1;
140 };
141
142 static void gtk_menu_shell_set_property      (GObject           *object,
143                                               guint              prop_id,
144                                               const GValue      *value,
145                                               GParamSpec        *pspec);
146 static void gtk_menu_shell_get_property      (GObject           *object,
147                                               guint              prop_id,
148                                               GValue            *value,
149                                               GParamSpec        *pspec);
150 static void gtk_menu_shell_realize           (GtkWidget         *widget);
151 static void gtk_menu_shell_finalize          (GObject           *object);
152 static gint gtk_menu_shell_button_press      (GtkWidget         *widget,
153                                               GdkEventButton    *event);
154 static gint gtk_menu_shell_button_release    (GtkWidget         *widget,
155                                               GdkEventButton    *event);
156 static gint gtk_menu_shell_key_press         (GtkWidget         *widget,
157                                               GdkEventKey       *event);
158 static gint gtk_menu_shell_enter_notify      (GtkWidget         *widget,
159                                               GdkEventCrossing  *event);
160 static gint gtk_menu_shell_leave_notify      (GtkWidget         *widget,
161                                               GdkEventCrossing  *event);
162 static void gtk_menu_shell_screen_changed    (GtkWidget         *widget,
163                                               GdkScreen         *previous_screen);
164 static gboolean gtk_menu_shell_grab_broken       (GtkWidget         *widget,
165                                               GdkEventGrabBroken *event);
166 static void gtk_menu_shell_add               (GtkContainer      *container,
167                                               GtkWidget         *widget);
168 static void gtk_menu_shell_remove            (GtkContainer      *container,
169                                               GtkWidget         *widget);
170 static void gtk_menu_shell_forall            (GtkContainer      *container,
171                                               gboolean           include_internals,
172                                               GtkCallback        callback,
173                                               gpointer           callback_data);
174 static void gtk_menu_shell_real_insert       (GtkMenuShell *menu_shell,
175                                               GtkWidget    *child,
176                                               gint          position);
177 static void gtk_real_menu_shell_deactivate   (GtkMenuShell      *menu_shell);
178 static gint gtk_menu_shell_is_item           (GtkMenuShell      *menu_shell,
179                                               GtkWidget         *child);
180 static GtkWidget *gtk_menu_shell_get_item    (GtkMenuShell      *menu_shell,
181                                               GdkEvent          *event);
182 static GType    gtk_menu_shell_child_type  (GtkContainer      *container);
183 static void gtk_menu_shell_real_select_item  (GtkMenuShell      *menu_shell,
184                                               GtkWidget         *menu_item);
185 static gboolean gtk_menu_shell_select_submenu_first (GtkMenuShell   *menu_shell); 
186
187 static void gtk_real_menu_shell_move_current (GtkMenuShell      *menu_shell,
188                                               GtkMenuDirectionType direction);
189 static void gtk_real_menu_shell_activate_current (GtkMenuShell      *menu_shell,
190                                                   gboolean           force_hide);
191 static void gtk_real_menu_shell_cancel           (GtkMenuShell      *menu_shell);
192 static void gtk_real_menu_shell_cycle_focus      (GtkMenuShell      *menu_shell,
193                                                   GtkDirectionType   dir);
194
195 static void     gtk_menu_shell_reset_key_hash    (GtkMenuShell *menu_shell);
196 static gboolean gtk_menu_shell_activate_mnemonic (GtkMenuShell *menu_shell,
197                                                   GdkEventKey  *event);
198 static gboolean gtk_menu_shell_real_move_selected (GtkMenuShell  *menu_shell, 
199                                                    gint           distance);
200
201 static guint menu_shell_signals[LAST_SIGNAL] = { 0 };
202
203 G_DEFINE_ABSTRACT_TYPE (GtkMenuShell, gtk_menu_shell, GTK_TYPE_CONTAINER)
204
205 static void
206 gtk_menu_shell_class_init (GtkMenuShellClass *klass)
207 {
208   GObjectClass *object_class;
209   GtkWidgetClass *widget_class;
210   GtkContainerClass *container_class;
211
212   GtkBindingSet *binding_set;
213
214   object_class = (GObjectClass*) klass;
215   widget_class = (GtkWidgetClass*) klass;
216   container_class = (GtkContainerClass*) klass;
217
218   object_class->set_property = gtk_menu_shell_set_property;
219   object_class->get_property = gtk_menu_shell_get_property;
220   object_class->finalize = gtk_menu_shell_finalize;
221
222   widget_class->realize = gtk_menu_shell_realize;
223   widget_class->button_press_event = gtk_menu_shell_button_press;
224   widget_class->button_release_event = gtk_menu_shell_button_release;
225   widget_class->grab_broken_event = gtk_menu_shell_grab_broken;
226   widget_class->key_press_event = gtk_menu_shell_key_press;
227   widget_class->enter_notify_event = gtk_menu_shell_enter_notify;
228   widget_class->leave_notify_event = gtk_menu_shell_leave_notify;
229   widget_class->screen_changed = gtk_menu_shell_screen_changed;
230
231   container_class->add = gtk_menu_shell_add;
232   container_class->remove = gtk_menu_shell_remove;
233   container_class->forall = gtk_menu_shell_forall;
234   container_class->child_type = gtk_menu_shell_child_type;
235
236   klass->submenu_placement = GTK_TOP_BOTTOM;
237   klass->deactivate = gtk_real_menu_shell_deactivate;
238   klass->selection_done = NULL;
239   klass->move_current = gtk_real_menu_shell_move_current;
240   klass->activate_current = gtk_real_menu_shell_activate_current;
241   klass->cancel = gtk_real_menu_shell_cancel;
242   klass->select_item = gtk_menu_shell_real_select_item;
243   klass->insert = gtk_menu_shell_real_insert;
244   klass->move_selected = gtk_menu_shell_real_move_selected;
245
246   menu_shell_signals[DEACTIVATE] =
247     g_signal_new (I_("deactivate"),
248                   G_OBJECT_CLASS_TYPE (object_class),
249                   G_SIGNAL_RUN_FIRST,
250                   G_STRUCT_OFFSET (GtkMenuShellClass, deactivate),
251                   NULL, NULL,
252                   _gtk_marshal_VOID__VOID,
253                   G_TYPE_NONE, 0);
254
255   menu_shell_signals[SELECTION_DONE] =
256     g_signal_new (I_("selection-done"),
257                   G_OBJECT_CLASS_TYPE (object_class),
258                   G_SIGNAL_RUN_FIRST,
259                   G_STRUCT_OFFSET (GtkMenuShellClass, selection_done),
260                   NULL, NULL,
261                   _gtk_marshal_VOID__VOID,
262                   G_TYPE_NONE, 0);
263
264   menu_shell_signals[MOVE_CURRENT] =
265     g_signal_new (I_("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
274   menu_shell_signals[ACTIVATE_CURRENT] =
275     g_signal_new (I_("activate-current"),
276                   G_OBJECT_CLASS_TYPE (object_class),
277                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
278                   G_STRUCT_OFFSET (GtkMenuShellClass, activate_current),
279                   NULL, NULL,
280                   _gtk_marshal_VOID__BOOLEAN,
281                   G_TYPE_NONE, 1,
282                   G_TYPE_BOOLEAN);
283
284   menu_shell_signals[CANCEL] =
285     g_signal_new (I_("cancel"),
286                   G_OBJECT_CLASS_TYPE (object_class),
287                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
288                   G_STRUCT_OFFSET (GtkMenuShellClass, cancel),
289                   NULL, NULL,
290                   _gtk_marshal_VOID__VOID,
291                   G_TYPE_NONE, 0);
292
293   menu_shell_signals[CYCLE_FOCUS] =
294     g_signal_new_class_handler (I_("cycle-focus"),
295                                 G_OBJECT_CLASS_TYPE (object_class),
296                                 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
297                                 G_CALLBACK (gtk_real_menu_shell_cycle_focus),
298                                 NULL, NULL,
299                                 _gtk_marshal_VOID__ENUM,
300                                 G_TYPE_NONE, 1,
301                                 GTK_TYPE_DIRECTION_TYPE);
302
303   /**
304    * GtkMenuShell::move-selected:
305    * @menu_shell: the object on which the signal is emitted
306    * @distance: +1 to move to the next item, -1 to move to the previous
307    *
308    * The ::move-selected signal is emitted to move the selection to
309    * another item.
310    *
311    * Returns: %TRUE to stop the signal emission, %FALSE to continue
312    *
313    * Since: 2.12
314    */
315   menu_shell_signals[MOVE_SELECTED] =
316     g_signal_new (I_("move-selected"),
317                   G_OBJECT_CLASS_TYPE (object_class),
318                   G_SIGNAL_RUN_LAST,
319                   G_STRUCT_OFFSET (GtkMenuShellClass, move_selected),
320                   _gtk_boolean_handled_accumulator, NULL,
321                   _gtk_marshal_BOOLEAN__INT,
322                   G_TYPE_BOOLEAN, 1,
323                   G_TYPE_INT);
324
325   binding_set = gtk_binding_set_by_class (klass);
326   gtk_binding_entry_add_signal (binding_set,
327                                 GDK_Escape, 0,
328                                 "cancel", 0);
329   gtk_binding_entry_add_signal (binding_set,
330                                 GDK_Return, 0,
331                                 "activate-current", 1,
332                                 G_TYPE_BOOLEAN,
333                                 TRUE);
334   gtk_binding_entry_add_signal (binding_set,
335                                 GDK_ISO_Enter, 0,
336                                 "activate-current", 1,
337                                 G_TYPE_BOOLEAN,
338                                 TRUE);
339   gtk_binding_entry_add_signal (binding_set,
340                                 GDK_KP_Enter, 0,
341                                 "activate-current", 1,
342                                 G_TYPE_BOOLEAN,
343                                 TRUE);
344   gtk_binding_entry_add_signal (binding_set,
345                                 GDK_space, 0,
346                                 "activate-current", 1,
347                                 G_TYPE_BOOLEAN,
348                                 FALSE);
349   gtk_binding_entry_add_signal (binding_set,
350                                 GDK_KP_Space, 0,
351                                 "activate-current", 1,
352                                 G_TYPE_BOOLEAN,
353                                 FALSE);
354   gtk_binding_entry_add_signal (binding_set,
355                                 GDK_F10, 0,
356                                 "cycle-focus", 1,
357                                 GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_FORWARD);
358   gtk_binding_entry_add_signal (binding_set,
359                                 GDK_F10, GDK_SHIFT_MASK,
360                                 "cycle-focus", 1,
361                                 GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_BACKWARD);
362
363   /**
364    * GtkMenuShell:take-focus:
365    *
366    * A boolean that determines whether the menu and its submenus grab the
367    * keyboard focus. See gtk_menu_shell_set_take_focus() and
368    * gtk_menu_shell_get_take_focus().
369    *
370    * Since: 2.8
371    **/
372   g_object_class_install_property (object_class,
373                                    PROP_TAKE_FOCUS,
374                                    g_param_spec_boolean ("take-focus",
375                                                          P_("Take Focus"),
376                                                          P_("A boolean that determines whether the menu grabs the keyboard focus"),
377                                                          TRUE,
378                                                          GTK_PARAM_READWRITE));
379
380   g_type_class_add_private (object_class, sizeof (GtkMenuShellPrivate));
381 }
382
383 static GType
384 gtk_menu_shell_child_type (GtkContainer     *container)
385 {
386   return GTK_TYPE_MENU_ITEM;
387 }
388
389 static void
390 gtk_menu_shell_init (GtkMenuShell *menu_shell)
391 {
392   GtkMenuShellPrivate *priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
393
394   menu_shell->children = NULL;
395   menu_shell->active_menu_item = NULL;
396   menu_shell->parent_menu_shell = NULL;
397   menu_shell->active = FALSE;
398   menu_shell->have_grab = FALSE;
399   menu_shell->have_xgrab = FALSE;
400   menu_shell->button = 0;
401   menu_shell->activate_time = 0;
402
403   priv->mnemonic_hash = NULL;
404   priv->key_hash = NULL;
405   priv->take_focus = TRUE;
406   priv->activated_submenu = FALSE;
407 }
408
409 static void
410 gtk_menu_shell_set_property (GObject      *object,
411                              guint         prop_id,
412                              const GValue *value,
413                              GParamSpec   *pspec)
414 {
415   GtkMenuShell *menu_shell = GTK_MENU_SHELL (object);
416
417   switch (prop_id)
418     {
419     case PROP_TAKE_FOCUS:
420       gtk_menu_shell_set_take_focus (menu_shell, g_value_get_boolean (value));
421       break;
422     default:
423       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
424       break;
425     }
426 }
427
428 static void
429 gtk_menu_shell_get_property (GObject     *object,
430                              guint        prop_id,
431                              GValue      *value,
432                              GParamSpec  *pspec)
433 {
434   GtkMenuShell *menu_shell = GTK_MENU_SHELL (object);
435
436   switch (prop_id)
437     {
438     case PROP_TAKE_FOCUS:
439       g_value_set_boolean (value, gtk_menu_shell_get_take_focus (menu_shell));
440       break;
441     default:
442       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
443       break;
444     }
445 }
446
447 static void
448 gtk_menu_shell_finalize (GObject *object)
449 {
450   GtkMenuShell *menu_shell = GTK_MENU_SHELL (object);
451   GtkMenuShellPrivate *priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
452
453   if (priv->mnemonic_hash)
454     _gtk_mnemonic_hash_free (priv->mnemonic_hash);
455   if (priv->key_hash)
456     _gtk_key_hash_free (priv->key_hash);
457
458   G_OBJECT_CLASS (gtk_menu_shell_parent_class)->finalize (object);
459 }
460
461
462 void
463 gtk_menu_shell_append (GtkMenuShell *menu_shell,
464                        GtkWidget    *child)
465 {
466   gtk_menu_shell_insert (menu_shell, child, -1);
467 }
468
469 void
470 gtk_menu_shell_prepend (GtkMenuShell *menu_shell,
471                         GtkWidget    *child)
472 {
473   gtk_menu_shell_insert (menu_shell, child, 0);
474 }
475
476 void
477 gtk_menu_shell_insert (GtkMenuShell *menu_shell,
478                        GtkWidget    *child,
479                        gint          position)
480 {
481   GtkMenuShellClass *class;
482
483   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
484   g_return_if_fail (GTK_IS_MENU_ITEM (child));
485
486   class = GTK_MENU_SHELL_GET_CLASS (menu_shell);
487
488   if (class->insert)
489     class->insert (menu_shell, child, position);
490 }
491
492 static void
493 gtk_menu_shell_real_insert (GtkMenuShell *menu_shell,
494                             GtkWidget    *child,
495                             gint          position)
496 {
497   menu_shell->children = g_list_insert (menu_shell->children, child, position);
498
499   gtk_widget_set_parent (child, GTK_WIDGET (menu_shell));
500 }
501
502 void
503 gtk_menu_shell_deactivate (GtkMenuShell *menu_shell)
504 {
505   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
506
507   g_signal_emit (menu_shell, menu_shell_signals[DEACTIVATE], 0);
508 }
509
510 static void
511 gtk_menu_shell_realize (GtkWidget *widget)
512 {
513   GdkWindowAttr attributes;
514   gint attributes_mask;
515
516   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
517
518   attributes.x = widget->allocation.x;
519   attributes.y = widget->allocation.y;
520   attributes.width = widget->allocation.width;
521   attributes.height = widget->allocation.height;
522   attributes.window_type = GDK_WINDOW_CHILD;
523   attributes.wclass = GDK_INPUT_OUTPUT;
524   attributes.visual = gtk_widget_get_visual (widget);
525   attributes.colormap = gtk_widget_get_colormap (widget);
526   attributes.event_mask = gtk_widget_get_events (widget);
527   attributes.event_mask |= (GDK_EXPOSURE_MASK |
528                             GDK_BUTTON_PRESS_MASK |
529                             GDK_BUTTON_RELEASE_MASK |
530                             GDK_KEY_PRESS_MASK |
531                             GDK_ENTER_NOTIFY_MASK |
532                             GDK_LEAVE_NOTIFY_MASK);
533
534   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
535   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
536   gdk_window_set_user_data (widget->window, widget);
537
538   widget->style = gtk_style_attach (widget->style, widget->window);
539   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
540 }
541
542 void
543 _gtk_menu_shell_activate (GtkMenuShell *menu_shell)
544 {
545   if (!menu_shell->active)
546     {
547       gtk_grab_add (GTK_WIDGET (menu_shell));
548       menu_shell->have_grab = TRUE;
549       menu_shell->active = TRUE;
550     }
551 }
552
553 static gint
554 gtk_menu_shell_button_press (GtkWidget      *widget,
555                              GdkEventButton *event)
556 {
557   GtkMenuShell *menu_shell;
558   GtkWidget *menu_item;
559
560   if (event->type != GDK_BUTTON_PRESS)
561     return FALSE;
562
563   menu_shell = GTK_MENU_SHELL (widget);
564
565   if (menu_shell->parent_menu_shell)
566     return gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
567
568   menu_item = gtk_menu_shell_get_item (menu_shell, (GdkEvent *)event);
569
570   if (menu_item && _gtk_menu_item_is_selectable (menu_item) &&
571       menu_item != GTK_MENU_SHELL (menu_item->parent)->active_menu_item)
572     {
573       /*  select the menu item *before* activating the shell, so submenus
574        *  which might be open are closed the friendly way. If we activate
575        *  (and thus grab) this menu shell first, we might get grab_broken
576        *  events which will close the entire menu hierarchy. Selecting the
577        *  menu item also fixes up the state as if enter_notify() would
578        *  have run before (which normally selects the item).
579        */
580       if (GTK_MENU_SHELL_GET_CLASS (menu_item->parent)->submenu_placement != GTK_TOP_BOTTOM)
581         {
582           gtk_menu_shell_select_item (GTK_MENU_SHELL (menu_item->parent), menu_item);
583         }
584     }
585
586   if (!menu_shell->active || !menu_shell->button)
587     {
588       _gtk_menu_shell_activate (menu_shell);
589
590       menu_shell->button = event->button;
591
592       if (menu_item && _gtk_menu_item_is_selectable (menu_item) &&
593           menu_item->parent == widget &&
594           menu_item != menu_shell->active_menu_item)
595         {
596           if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement == GTK_TOP_BOTTOM)
597             {
598               menu_shell->activate_time = event->time;
599               gtk_menu_shell_select_item (menu_shell, menu_item);
600             }
601         }
602     }
603   else
604     {
605       widget = gtk_get_event_widget ((GdkEvent*) event);
606       if (widget == GTK_WIDGET (menu_shell))
607         {
608           gtk_menu_shell_deactivate (menu_shell);
609           g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0);
610         }
611     }
612
613   if (menu_item && _gtk_menu_item_is_selectable (menu_item) &&
614       GTK_MENU_ITEM (menu_item)->submenu != NULL &&
615       !GTK_WIDGET_VISIBLE (GTK_MENU_ITEM (menu_item)->submenu))
616     {
617       GtkMenuShellPrivate *priv;
618
619       _gtk_menu_item_popup_submenu (menu_item, FALSE);
620
621       priv = GTK_MENU_SHELL_GET_PRIVATE (menu_item->parent);
622       priv->activated_submenu = TRUE;
623     }
624
625   return TRUE;
626 }
627
628 static gboolean
629 gtk_menu_shell_grab_broken (GtkWidget          *widget,
630                             GdkEventGrabBroken *event)
631 {
632   GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget);
633
634   if (menu_shell->have_xgrab && event->grab_window == NULL)
635     {
636       /* Unset the active menu item so gtk_menu_popdown() doesn't see it.
637        */
638       
639       gtk_menu_shell_deselect (menu_shell);
640       
641       gtk_menu_shell_deactivate (menu_shell);
642       g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0);
643     }
644
645   return TRUE;
646 }
647
648 static gint
649 gtk_menu_shell_button_release (GtkWidget      *widget,
650                                GdkEventButton *event)
651 {
652   GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget);
653   GtkMenuShellPrivate *priv = GTK_MENU_SHELL_GET_PRIVATE (widget);
654
655   if (menu_shell->active)
656     {
657       GtkWidget *menu_item;
658       gboolean   deactivate = TRUE;
659
660       if (menu_shell->button && (event->button != menu_shell->button))
661         {
662           menu_shell->button = 0;
663           if (menu_shell->parent_menu_shell)
664             return gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
665         }
666
667       menu_shell->button = 0;
668       menu_item = gtk_menu_shell_get_item (menu_shell, (GdkEvent*) event);
669
670       if ((event->time - menu_shell->activate_time) > MENU_SHELL_TIMEOUT)
671         {
672           if (menu_item && (menu_shell->active_menu_item == menu_item) &&
673               _gtk_menu_item_is_selectable (menu_item))
674             {
675               GtkWidget *submenu = GTK_MENU_ITEM (menu_item)->submenu;
676
677               if (submenu == NULL)
678                 {
679                   gtk_menu_shell_activate_item (menu_shell, menu_item, TRUE);
680
681                   deactivate = FALSE;
682                 }
683               else if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement != GTK_TOP_BOTTOM ||
684                        priv->activated_submenu)
685                 {
686                   gint popdown_delay;
687                   GTimeVal *popup_time;
688                   gint64 usec_since_popup = 0;
689
690                   g_object_get (gtk_widget_get_settings (widget),
691                                 "gtk-menu-popdown-delay", &popdown_delay,
692                                 NULL);
693
694                   popup_time = g_object_get_data (G_OBJECT (submenu),
695                                                   "gtk-menu-exact-popup-time");
696
697                   if (popup_time)
698                     {
699                       GTimeVal current_time;
700
701                       g_get_current_time (&current_time);
702
703                       usec_since_popup = ((gint64) current_time.tv_sec * 1000 * 1000 +
704                                           (gint64) current_time.tv_usec -
705                                           (gint64) popup_time->tv_sec * 1000 * 1000 -
706                                           (gint64) popup_time->tv_usec);
707
708                       g_object_set_data (G_OBJECT (submenu),
709                                          "gtk-menu-exact-popup-time", NULL);
710                     }
711
712                   /*  only close the submenu on click if we opened the
713                    *  menu explicitely (usec_since_popup == 0) or
714                    *  enough time has passed since it was opened by
715                    *  GtkMenuItem's timeout (usec_since_popup > delay).
716                    */
717                   if (!priv->activated_submenu &&
718                       (usec_since_popup == 0 ||
719                        usec_since_popup > popdown_delay * 1000))
720                     {
721                       _gtk_menu_item_popdown_submenu (menu_item);
722                     }
723                   else
724                     {
725                       gtk_menu_item_select (GTK_MENU_ITEM (menu_item));
726                     }
727
728                   deactivate = FALSE;
729                 }
730             }
731           else if (menu_item &&
732                    !_gtk_menu_item_is_selectable (menu_item) &&
733                    GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement != GTK_TOP_BOTTOM)
734             {
735               deactivate = FALSE;
736             }
737           else if (menu_shell->parent_menu_shell)
738             {
739               menu_shell->active = TRUE;
740               gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
741               deactivate = FALSE;
742             }
743
744           /* If we ended up on an item with a submenu, leave the menu up.
745            */
746           if (menu_item && (menu_shell->active_menu_item == menu_item) &&
747               GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement != GTK_TOP_BOTTOM)
748             {
749               deactivate = FALSE;
750             }
751         }
752       else /* a very fast press-release */
753         {
754           /* We only ever want to prevent deactivation on the first
755            * press/release. Setting the time to zero is a bit of a
756            * hack, since we could be being triggered in the first
757            * few fractions of a second after a server time wraparound.
758            * the chances of that happening are ~1/10^6, without
759            * serious harm if we lose.
760            */
761           menu_shell->activate_time = 0;
762           deactivate = FALSE;
763         }
764
765       if (deactivate)
766         {
767           gtk_menu_shell_deactivate (menu_shell);
768           g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0);
769         }
770
771       priv->activated_submenu = FALSE;
772     }
773
774   return TRUE;
775 }
776
777 void
778 _gtk_menu_shell_set_keyboard_mode (GtkMenuShell *menu_shell,
779                                    gboolean      keyboard_mode)
780 {
781   menu_shell->keyboard_mode = keyboard_mode;
782 }
783
784 gboolean
785 _gtk_menu_shell_get_keyboard_mode (GtkMenuShell *menu_shell)
786 {
787   return menu_shell->keyboard_mode;
788 }
789
790 void
791 _gtk_menu_shell_update_mnemonics (GtkMenuShell *menu_shell)
792 {
793   GtkMenuShell *target;
794   gboolean auto_mnemonics;
795   gboolean found;
796   gboolean mnemonics_visible;
797
798   g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu_shell)),
799                 "gtk-auto-mnemonics", &auto_mnemonics, NULL);
800
801   if (!auto_mnemonics)
802     return;
803
804   target = menu_shell;
805   found = FALSE;
806   while (target)
807     {
808       /* The idea with keyboard mode is that once you start using
809        * the keyboard to navigate the menus, we show mnemonics
810        * until the menu navigation is over. To that end, we spread
811        * the keyboard mode upwards in the menu hierarchy here.
812        * Also see gtk_menu_popup, where we inherit it downwards.
813        */
814       if (menu_shell->keyboard_mode)
815         target->keyboard_mode = TRUE;
816
817       /* While navigating menus, the first parent menu with an active
818        * item is the one where mnemonics are effective, as can be seen
819        * in gtk_menu_shell_key_press below.
820        * We also show mnemonics in context menus. The grab condition is
821        * necessary to ensure we remove underlines from menu bars when
822        * dismissing menus.
823        */
824       mnemonics_visible = target->keyboard_mode &&
825                           ((target->active_menu_item && !found) ||
826                            (target == menu_shell &&
827                             !target->parent_menu_shell &&
828                             gtk_widget_has_grab (target)));
829
830       /* While menus are up, only show underlines inside the menubar,
831        * not in the entire window.
832        */
833       if (GTK_IS_MENU_BAR (target))
834         _gtk_label_mnemonics_visible_apply_recursively (GTK_WIDGET (target),
835                                                         mnemonics_visible);
836       else
837         gtk_window_set_mnemonics_visible (GTK_WINDOW (gtk_widget_get_toplevel (target)),
838                                           mnemonics_visible);
839
840       if (target->active_menu_item)
841         found = TRUE;
842
843       target = GTK_MENU_SHELL (target->parent_menu_shell);
844     }
845 }
846
847 static gint
848 gtk_menu_shell_key_press (GtkWidget   *widget,
849                           GdkEventKey *event)
850 {
851   GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget);
852   gboolean enable_mnemonics;
853
854   menu_shell->keyboard_mode = TRUE;
855
856   if (!menu_shell->active_menu_item && menu_shell->parent_menu_shell)
857     return gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent *)event);
858
859   if (gtk_bindings_activate_event (GTK_OBJECT (widget), event))
860     return TRUE;
861
862   g_object_get (gtk_widget_get_settings (widget),
863                 "gtk-enable-mnemonics", &enable_mnemonics,
864                 NULL);
865
866   if (enable_mnemonics)
867     return gtk_menu_shell_activate_mnemonic (menu_shell, event);
868
869   return FALSE;
870 }
871
872 static gint
873 gtk_menu_shell_enter_notify (GtkWidget        *widget,
874                              GdkEventCrossing *event)
875 {
876   GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget);
877
878   if (event->mode == GDK_CROSSING_GTK_GRAB ||
879       event->mode == GDK_CROSSING_GTK_UNGRAB ||
880       event->mode == GDK_CROSSING_STATE_CHANGED)
881     return TRUE;
882
883   if (menu_shell->active)
884     {
885       GtkWidget *menu_item;
886
887       menu_item = gtk_get_event_widget ((GdkEvent*) event);
888
889       if (!menu_item ||
890           (GTK_IS_MENU_ITEM (menu_item) && 
891            !_gtk_menu_item_is_selectable (menu_item)))
892         return TRUE;
893
894       if (menu_item->parent == widget &&
895           GTK_IS_MENU_ITEM (menu_item))
896         {
897           if (menu_shell->ignore_enter)
898             return TRUE;
899
900           if (event->detail != GDK_NOTIFY_INFERIOR)
901             {
902               if (GTK_WIDGET_STATE (menu_item) != GTK_STATE_PRELIGHT)
903                 gtk_menu_shell_select_item (menu_shell, menu_item);
904
905               /* If any mouse button is down, and there is a submenu
906                * that is not yet visible, activate it. It's sufficient
907                * to check for any button's mask (not only the one
908                * matching menu_shell->button), because there is no
909                * situation a mouse button could be pressed while
910                * entering a menu item where we wouldn't want to show
911                * its submenu.
912                */
913               if ((event->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK)) &&
914                   GTK_MENU_ITEM (menu_item)->submenu != NULL)
915                 {
916                   GtkMenuShellPrivate *priv;
917
918                   priv = GTK_MENU_SHELL_GET_PRIVATE (menu_item->parent);
919                   priv->activated_submenu = TRUE;
920
921                   if (!GTK_WIDGET_VISIBLE (GTK_MENU_ITEM (menu_item)->submenu))
922                     {
923                       gboolean touchscreen_mode;
924
925                       g_object_get (gtk_widget_get_settings (widget),
926                                     "gtk-touchscreen-mode", &touchscreen_mode,
927                                     NULL);
928
929                       if (touchscreen_mode)
930                         _gtk_menu_item_popup_submenu (menu_item, TRUE);
931                     }
932                 }
933             }
934         }
935       else if (menu_shell->parent_menu_shell)
936         {
937           gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
938         }
939     }
940
941   return TRUE;
942 }
943
944 static gint
945 gtk_menu_shell_leave_notify (GtkWidget        *widget,
946                              GdkEventCrossing *event)
947 {
948   if (event->mode == GDK_CROSSING_GTK_GRAB ||
949       event->mode == GDK_CROSSING_GTK_GRAB ||
950       event->mode == GDK_CROSSING_STATE_CHANGED)
951     return TRUE;
952
953   if (GTK_WIDGET_VISIBLE (widget))
954     {
955       GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget);
956       GtkWidget *event_widget = gtk_get_event_widget ((GdkEvent*) event);
957       GtkMenuItem *menu_item;
958
959       if (!event_widget || !GTK_IS_MENU_ITEM (event_widget))
960         return TRUE;
961
962       menu_item = GTK_MENU_ITEM (event_widget);
963
964       if (!_gtk_menu_item_is_selectable (event_widget))
965         return TRUE;
966
967       if ((menu_shell->active_menu_item == event_widget) &&
968           (menu_item->submenu == NULL))
969         {
970           if ((event->detail != GDK_NOTIFY_INFERIOR) &&
971               (GTK_WIDGET_STATE (menu_item) != GTK_STATE_NORMAL))
972             {
973               gtk_menu_shell_deselect (menu_shell);
974             }
975         }
976       else if (menu_shell->parent_menu_shell)
977         {
978           gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
979         }
980     }
981
982   return TRUE;
983 }
984
985 static void
986 gtk_menu_shell_screen_changed (GtkWidget *widget,
987                                GdkScreen *previous_screen)
988 {
989   gtk_menu_shell_reset_key_hash (GTK_MENU_SHELL (widget));
990 }
991
992 static void
993 gtk_menu_shell_add (GtkContainer *container,
994                     GtkWidget    *widget)
995 {
996   gtk_menu_shell_append (GTK_MENU_SHELL (container), widget);
997 }
998
999 static void
1000 gtk_menu_shell_remove (GtkContainer *container,
1001                        GtkWidget    *widget)
1002 {
1003   GtkMenuShell *menu_shell = GTK_MENU_SHELL (container);
1004   gint was_visible;
1005
1006   was_visible = GTK_WIDGET_VISIBLE (widget);
1007   menu_shell->children = g_list_remove (menu_shell->children, widget);
1008   
1009   if (widget == menu_shell->active_menu_item)
1010     {
1011       gtk_item_deselect (GTK_ITEM (menu_shell->active_menu_item));
1012       menu_shell->active_menu_item = NULL;
1013     }
1014
1015   gtk_widget_unparent (widget);
1016   
1017   /* queue resize regardless of GTK_WIDGET_VISIBLE (container),
1018    * since that's what is needed by toplevels.
1019    */
1020   if (was_visible)
1021     gtk_widget_queue_resize (GTK_WIDGET (container));
1022 }
1023
1024 static void
1025 gtk_menu_shell_forall (GtkContainer *container,
1026                        gboolean      include_internals,
1027                        GtkCallback   callback,
1028                        gpointer      callback_data)
1029 {
1030   GtkMenuShell *menu_shell = GTK_MENU_SHELL (container);
1031   GtkWidget *child;
1032   GList *children;
1033
1034   children = menu_shell->children;
1035   while (children)
1036     {
1037       child = children->data;
1038       children = children->next;
1039
1040       (* callback) (child, callback_data);
1041     }
1042 }
1043
1044
1045 static void
1046 gtk_real_menu_shell_deactivate (GtkMenuShell *menu_shell)
1047 {
1048   if (menu_shell->active)
1049     {
1050       menu_shell->button = 0;
1051       menu_shell->active = FALSE;
1052       menu_shell->activate_time = 0;
1053
1054       if (menu_shell->active_menu_item)
1055         {
1056           gtk_menu_item_deselect (GTK_MENU_ITEM (menu_shell->active_menu_item));
1057           menu_shell->active_menu_item = NULL;
1058         }
1059
1060       if (menu_shell->have_grab)
1061         {
1062           menu_shell->have_grab = FALSE;
1063           gtk_grab_remove (GTK_WIDGET (menu_shell));
1064         }
1065       if (menu_shell->have_xgrab)
1066         {
1067           GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (menu_shell));
1068
1069           menu_shell->have_xgrab = FALSE;
1070           gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
1071           gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
1072         }
1073
1074       menu_shell->keyboard_mode = FALSE;
1075
1076       _gtk_menu_shell_update_mnemonics (menu_shell);
1077     }
1078 }
1079
1080 static gint
1081 gtk_menu_shell_is_item (GtkMenuShell *menu_shell,
1082                         GtkWidget    *child)
1083 {
1084   GtkWidget *parent;
1085
1086   g_return_val_if_fail (GTK_IS_MENU_SHELL (menu_shell), FALSE);
1087   g_return_val_if_fail (child != NULL, FALSE);
1088
1089   parent = child->parent;
1090   while (GTK_IS_MENU_SHELL (parent))
1091     {
1092       if (parent == (GtkWidget*) menu_shell)
1093         return TRUE;
1094       parent = GTK_MENU_SHELL (parent)->parent_menu_shell;
1095     }
1096
1097   return FALSE;
1098 }
1099
1100 static GtkWidget*
1101 gtk_menu_shell_get_item (GtkMenuShell *menu_shell,
1102                          GdkEvent     *event)
1103 {
1104   GtkWidget *menu_item;
1105
1106   menu_item = gtk_get_event_widget ((GdkEvent*) event);
1107   
1108   while (menu_item && !GTK_IS_MENU_ITEM (menu_item))
1109     menu_item = menu_item->parent;
1110
1111   if (menu_item && gtk_menu_shell_is_item (menu_shell, menu_item))
1112     return menu_item;
1113   else
1114     return NULL;
1115 }
1116
1117 /* Handlers for action signals */
1118
1119 void
1120 gtk_menu_shell_select_item (GtkMenuShell *menu_shell,
1121                             GtkWidget    *menu_item)
1122 {
1123   GtkMenuShellClass *class;
1124
1125   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1126   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1127
1128   class = GTK_MENU_SHELL_GET_CLASS (menu_shell);
1129
1130   if (class->select_item &&
1131       !(menu_shell->active &&
1132         menu_shell->active_menu_item == menu_item))
1133     class->select_item (menu_shell, menu_item);
1134 }
1135
1136 void _gtk_menu_item_set_placement (GtkMenuItem         *menu_item,
1137                                    GtkSubmenuPlacement  placement);
1138
1139 static void
1140 gtk_menu_shell_real_select_item (GtkMenuShell *menu_shell,
1141                                  GtkWidget    *menu_item)
1142 {
1143   GtkPackDirection pack_dir = PACK_DIRECTION (menu_shell);
1144
1145   gtk_menu_shell_deselect (menu_shell);
1146
1147   if (!_gtk_menu_item_is_selectable (menu_item))
1148     return;
1149
1150   menu_shell->active_menu_item = menu_item;
1151   if (pack_dir == GTK_PACK_DIRECTION_TTB || pack_dir == GTK_PACK_DIRECTION_BTT)
1152     _gtk_menu_item_set_placement (GTK_MENU_ITEM (menu_shell->active_menu_item),
1153                                   GTK_LEFT_RIGHT);
1154   else
1155     _gtk_menu_item_set_placement (GTK_MENU_ITEM (menu_shell->active_menu_item),
1156                                   GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement);
1157   gtk_menu_item_select (GTK_MENU_ITEM (menu_shell->active_menu_item));
1158
1159   _gtk_menu_shell_update_mnemonics (menu_shell);
1160
1161   /* This allows the bizarre radio buttons-with-submenus-display-history
1162    * behavior
1163    */
1164   if (GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu)
1165     gtk_widget_activate (menu_shell->active_menu_item);
1166 }
1167
1168 void
1169 gtk_menu_shell_deselect (GtkMenuShell *menu_shell)
1170 {
1171   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1172
1173   if (menu_shell->active_menu_item)
1174     {
1175       gtk_menu_item_deselect (GTK_MENU_ITEM (menu_shell->active_menu_item));
1176       menu_shell->active_menu_item = NULL;
1177       _gtk_menu_shell_update_mnemonics (menu_shell);
1178     }
1179 }
1180
1181 void
1182 gtk_menu_shell_activate_item (GtkMenuShell      *menu_shell,
1183                               GtkWidget         *menu_item,
1184                               gboolean           force_deactivate)
1185 {
1186   GSList *slist, *shells = NULL;
1187   gboolean deactivate = force_deactivate;
1188
1189   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1190   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1191
1192   if (!deactivate)
1193     deactivate = GTK_MENU_ITEM_GET_CLASS (menu_item)->hide_on_activate;
1194
1195   g_object_ref (menu_shell);
1196   g_object_ref (menu_item);
1197
1198   if (deactivate)
1199     {
1200       GtkMenuShell *parent_menu_shell = menu_shell;
1201
1202       do
1203         {
1204           g_object_ref (parent_menu_shell);
1205           shells = g_slist_prepend (shells, parent_menu_shell);
1206           parent_menu_shell = (GtkMenuShell*) parent_menu_shell->parent_menu_shell;
1207         }
1208       while (parent_menu_shell);
1209       shells = g_slist_reverse (shells);
1210
1211       gtk_menu_shell_deactivate (menu_shell);
1212   
1213       /* flush the x-queue, so any grabs are removed and
1214        * the menu is actually taken down
1215        */
1216       gdk_display_sync (gtk_widget_get_display (menu_item));
1217     }
1218
1219   gtk_widget_activate (menu_item);
1220
1221   for (slist = shells; slist; slist = slist->next)
1222     {
1223       g_signal_emit (slist->data, menu_shell_signals[SELECTION_DONE], 0);
1224       g_object_unref (slist->data);
1225     }
1226   g_slist_free (shells);
1227
1228   g_object_unref (menu_shell);
1229   g_object_unref (menu_item);
1230 }
1231
1232 /* Distance should be +/- 1 */
1233 static gboolean
1234 gtk_menu_shell_real_move_selected (GtkMenuShell  *menu_shell, 
1235                                    gint           distance)
1236 {
1237   if (menu_shell->active_menu_item)
1238     {
1239       GList *node = g_list_find (menu_shell->children,
1240                                  menu_shell->active_menu_item);
1241       GList *start_node = node;
1242       gboolean wrap_around;
1243
1244       g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu_shell)),
1245                     "gtk-keynav-wrap-around", &wrap_around,
1246                     NULL);
1247
1248       if (distance > 0)
1249         {
1250           node = node->next;
1251           while (node != start_node && 
1252                  (!node || !_gtk_menu_item_is_selectable (node->data)))
1253             {
1254               if (node)
1255                 node = node->next;
1256               else if (wrap_around)
1257                 node = menu_shell->children;
1258               else
1259                 {
1260                   gtk_widget_error_bell (GTK_WIDGET (menu_shell));
1261                   break;
1262                 }
1263             }
1264         }
1265       else
1266         {
1267           node = node->prev;
1268           while (node != start_node &&
1269                  (!node || !_gtk_menu_item_is_selectable (node->data)))
1270             {
1271               if (node)
1272                 node = node->prev;
1273               else if (wrap_around)
1274                 node = g_list_last (menu_shell->children);
1275               else
1276                 {
1277                   gtk_widget_error_bell (GTK_WIDGET (menu_shell));
1278                   break;
1279                 }
1280             }
1281         }
1282       
1283       if (node)
1284         gtk_menu_shell_select_item (menu_shell, node->data);
1285     }
1286
1287   return TRUE;
1288 }
1289
1290 /* Distance should be +/- 1 */
1291 static void
1292 gtk_menu_shell_move_selected (GtkMenuShell  *menu_shell, 
1293                               gint           distance)
1294 {
1295   gboolean handled = FALSE;
1296
1297   g_signal_emit (menu_shell, menu_shell_signals[MOVE_SELECTED], 0,
1298                  distance, &handled);
1299 }
1300
1301 /**
1302  * gtk_menu_shell_select_first:
1303  * @menu_shell: a #GtkMenuShell
1304  * @search_sensitive: if %TRUE, search for the first selectable
1305  *                    menu item, otherwise select nothing if
1306  *                    the first item isn't sensitive. This
1307  *                    should be %FALSE if the menu is being
1308  *                    popped up initially.
1309  * 
1310  * Select the first visible or selectable child of the menu shell;
1311  * don't select tearoff items unless the only item is a tearoff
1312  * item.
1313  *
1314  * Since: 2.2
1315  **/
1316 void
1317 gtk_menu_shell_select_first (GtkMenuShell *menu_shell,
1318                              gboolean      search_sensitive)
1319 {
1320   GtkWidget *to_select = NULL;
1321   GList *tmp_list;
1322
1323   tmp_list = menu_shell->children;
1324   while (tmp_list)
1325     {
1326       GtkWidget *child = tmp_list->data;
1327       
1328       if ((!search_sensitive && GTK_WIDGET_VISIBLE (child)) ||
1329           _gtk_menu_item_is_selectable (child))
1330         {
1331           to_select = child;
1332           if (!GTK_IS_TEAROFF_MENU_ITEM (child))
1333             break;
1334         }
1335       
1336       tmp_list = tmp_list->next;
1337     }
1338
1339   if (to_select)
1340     gtk_menu_shell_select_item (menu_shell, to_select);
1341 }
1342
1343 void
1344 _gtk_menu_shell_select_last (GtkMenuShell *menu_shell,
1345                              gboolean      search_sensitive)
1346 {
1347   GtkWidget *to_select = NULL;
1348   GList *tmp_list;
1349
1350   tmp_list = g_list_last (menu_shell->children);
1351   while (tmp_list)
1352     {
1353       GtkWidget *child = tmp_list->data;
1354       
1355       if ((!search_sensitive && GTK_WIDGET_VISIBLE (child)) ||
1356           _gtk_menu_item_is_selectable (child))
1357         {
1358           to_select = child;
1359           if (!GTK_IS_TEAROFF_MENU_ITEM (child))
1360             break;
1361         }
1362       
1363       tmp_list = tmp_list->prev;
1364     }
1365
1366   if (to_select)
1367     gtk_menu_shell_select_item (menu_shell, to_select);
1368 }
1369
1370 static gboolean
1371 gtk_menu_shell_select_submenu_first (GtkMenuShell     *menu_shell)
1372 {
1373   GtkMenuItem *menu_item;
1374
1375   if (menu_shell->active_menu_item == NULL)
1376     return FALSE;
1377
1378   menu_item = GTK_MENU_ITEM (menu_shell->active_menu_item); 
1379   
1380   if (menu_item->submenu)
1381     {
1382       _gtk_menu_item_popup_submenu (GTK_WIDGET (menu_item), FALSE);
1383       gtk_menu_shell_select_first (GTK_MENU_SHELL (menu_item->submenu), TRUE);
1384       if (GTK_MENU_SHELL (menu_item->submenu)->active_menu_item)
1385         return TRUE;
1386     }
1387
1388   return FALSE;
1389 }
1390
1391 static void
1392 gtk_real_menu_shell_move_current (GtkMenuShell         *menu_shell,
1393                                   GtkMenuDirectionType  direction)
1394 {
1395   GtkMenuShell *parent_menu_shell = NULL;
1396   gboolean had_selection;
1397   gboolean touchscreen_mode;
1398
1399   had_selection = menu_shell->active_menu_item != NULL;
1400
1401   g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu_shell)),
1402                 "gtk-touchscreen-mode", &touchscreen_mode,
1403                 NULL);
1404
1405   if (menu_shell->parent_menu_shell)
1406     parent_menu_shell = GTK_MENU_SHELL (menu_shell->parent_menu_shell);
1407
1408   switch (direction)
1409     {
1410     case GTK_MENU_DIR_PARENT:
1411       if (touchscreen_mode &&
1412           menu_shell->active_menu_item &&
1413           GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu &&
1414           GTK_WIDGET_VISIBLE (GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu))
1415         {
1416           /* if we are on a menu item that has an open submenu but the
1417            * focus is not in that submenu (e.g. because it's empty or
1418            * has only insensitive items), close that submenu instead
1419            * of running into the code below which would close *this*
1420            * menu.
1421            */
1422           _gtk_menu_item_popdown_submenu (menu_shell->active_menu_item);
1423           _gtk_menu_shell_update_mnemonics (menu_shell);
1424         }
1425       else if (parent_menu_shell)
1426         {
1427           if (touchscreen_mode)
1428             {
1429               /* close menu when returning from submenu. */
1430               _gtk_menu_item_popdown_submenu (GTK_MENU (menu_shell)->parent_menu_item);
1431               _gtk_menu_shell_update_mnemonics (parent_menu_shell);
1432               break;
1433             }
1434
1435           if (GTK_MENU_SHELL_GET_CLASS (parent_menu_shell)->submenu_placement ==
1436               GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement)
1437             gtk_menu_shell_deselect (menu_shell);
1438           else
1439             {
1440               if (PACK_DIRECTION (parent_menu_shell) == GTK_PACK_DIRECTION_LTR)
1441                 gtk_menu_shell_move_selected (parent_menu_shell, -1);
1442               else
1443                 gtk_menu_shell_move_selected (parent_menu_shell, 1);
1444               gtk_menu_shell_select_submenu_first (parent_menu_shell);
1445             }
1446         }
1447       /* If there is no parent and the submenu is in the opposite direction
1448        * to the menu, then make the PARENT direction wrap around to
1449        * the bottom of the submenu.
1450        */
1451       else if (menu_shell->active_menu_item &&
1452                _gtk_menu_item_is_selectable (menu_shell->active_menu_item) &&
1453                GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu)
1454         {
1455           GtkMenuShell *submenu = GTK_MENU_SHELL (GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu);
1456
1457           if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement !=
1458               GTK_MENU_SHELL_GET_CLASS (submenu)->submenu_placement)
1459             _gtk_menu_shell_select_last (submenu, TRUE);
1460         }
1461       break;
1462
1463     case GTK_MENU_DIR_CHILD:
1464       if (menu_shell->active_menu_item &&
1465           _gtk_menu_item_is_selectable (menu_shell->active_menu_item) &&
1466           GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu)
1467         {
1468           if (gtk_menu_shell_select_submenu_first (menu_shell))
1469             break;
1470         }
1471
1472       /* Try to find a menu running the opposite direction */
1473       while (parent_menu_shell &&
1474              (GTK_MENU_SHELL_GET_CLASS (parent_menu_shell)->submenu_placement ==
1475               GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement))
1476         {
1477           parent_menu_shell = GTK_MENU_SHELL (parent_menu_shell->parent_menu_shell);
1478         }
1479
1480       if (parent_menu_shell)
1481         {
1482           if (PACK_DIRECTION (parent_menu_shell) == GTK_PACK_DIRECTION_LTR)
1483             gtk_menu_shell_move_selected (parent_menu_shell, 1);
1484           else
1485             gtk_menu_shell_move_selected (parent_menu_shell, -1);
1486
1487           gtk_menu_shell_select_submenu_first (parent_menu_shell);
1488         }
1489       break;
1490
1491     case GTK_MENU_DIR_PREV:
1492       gtk_menu_shell_move_selected (menu_shell, -1);
1493       if (!had_selection &&
1494           !menu_shell->active_menu_item &&
1495           menu_shell->children)
1496         _gtk_menu_shell_select_last (menu_shell, TRUE);
1497       break;
1498
1499     case GTK_MENU_DIR_NEXT:
1500       gtk_menu_shell_move_selected (menu_shell, 1);
1501       if (!had_selection &&
1502           !menu_shell->active_menu_item &&
1503           menu_shell->children)
1504         gtk_menu_shell_select_first (menu_shell, TRUE);
1505       break;
1506     }
1507 }
1508
1509 static void
1510 gtk_real_menu_shell_activate_current (GtkMenuShell      *menu_shell,
1511                                       gboolean           force_hide)
1512 {
1513   if (menu_shell->active_menu_item &&
1514       _gtk_menu_item_is_selectable (menu_shell->active_menu_item))
1515   {
1516     if (GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu == NULL)
1517       gtk_menu_shell_activate_item (menu_shell,
1518                                     menu_shell->active_menu_item,
1519                                     force_hide);
1520     else
1521       _gtk_menu_item_popup_submenu (menu_shell->active_menu_item, FALSE);
1522   }
1523 }
1524
1525 static void
1526 gtk_real_menu_shell_cancel (GtkMenuShell      *menu_shell)
1527 {
1528   /* Unset the active menu item so gtk_menu_popdown() doesn't see it.
1529    */
1530   gtk_menu_shell_deselect (menu_shell);
1531   
1532   gtk_menu_shell_deactivate (menu_shell);
1533   g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0);
1534 }
1535
1536 static void
1537 gtk_real_menu_shell_cycle_focus (GtkMenuShell      *menu_shell,
1538                                  GtkDirectionType   dir)
1539 {
1540   while (menu_shell && !GTK_IS_MENU_BAR (menu_shell))
1541     {
1542       if (menu_shell->parent_menu_shell)
1543         menu_shell = GTK_MENU_SHELL (menu_shell->parent_menu_shell);
1544       else
1545         menu_shell = NULL;
1546     }
1547
1548   if (menu_shell)
1549     _gtk_menu_bar_cycle_focus (GTK_MENU_BAR (menu_shell), dir);
1550 }
1551
1552 gint
1553 _gtk_menu_shell_get_popup_delay (GtkMenuShell *menu_shell)
1554 {
1555   GtkMenuShellClass *klass = GTK_MENU_SHELL_GET_CLASS (menu_shell);
1556   
1557   if (klass->get_popup_delay)
1558     {
1559       return klass->get_popup_delay (menu_shell);
1560     }
1561   else
1562     {
1563       gint popup_delay;
1564       GtkWidget *widget = GTK_WIDGET (menu_shell);
1565       
1566       g_object_get (gtk_widget_get_settings (widget),
1567                     "gtk-menu-popup-delay", &popup_delay,
1568                     NULL);
1569       
1570       return popup_delay;
1571     }
1572 }
1573
1574 /**
1575  * gtk_menu_shell_cancel:
1576  * @menu_shell: a #GtkMenuShell
1577  * 
1578  * Cancels the selection within the menu shell.  
1579  * 
1580  * Since: 2.4
1581  */
1582 void
1583 gtk_menu_shell_cancel (GtkMenuShell *menu_shell)
1584 {
1585   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1586
1587   g_signal_emit (menu_shell, menu_shell_signals[CANCEL], 0);
1588 }
1589
1590 static GtkMnemonicHash *
1591 gtk_menu_shell_get_mnemonic_hash (GtkMenuShell *menu_shell,
1592                                   gboolean      create)
1593 {
1594   GtkMenuShellPrivate *private = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
1595
1596   if (!private->mnemonic_hash && create)
1597     private->mnemonic_hash = _gtk_mnemonic_hash_new ();
1598   
1599   return private->mnemonic_hash;
1600 }
1601
1602 static void
1603 menu_shell_add_mnemonic_foreach (guint    keyval,
1604                                  GSList  *targets,
1605                                  gpointer data)
1606 {
1607   GtkKeyHash *key_hash = data;
1608
1609   _gtk_key_hash_add_entry (key_hash, keyval, 0, GUINT_TO_POINTER (keyval));
1610 }
1611
1612 static GtkKeyHash *
1613 gtk_menu_shell_get_key_hash (GtkMenuShell *menu_shell,
1614                              gboolean      create)
1615 {
1616   GtkMenuShellPrivate *private = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
1617   GtkWidget *widget = GTK_WIDGET (menu_shell);
1618
1619   if (!private->key_hash && create && gtk_widget_has_screen (widget))
1620     {
1621       GtkMnemonicHash *mnemonic_hash = gtk_menu_shell_get_mnemonic_hash (menu_shell, FALSE);
1622       GdkScreen *screen = gtk_widget_get_screen (widget);
1623       GdkKeymap *keymap = gdk_keymap_get_for_display (gdk_screen_get_display (screen));
1624
1625       if (!mnemonic_hash)
1626         return NULL;
1627       
1628       private->key_hash = _gtk_key_hash_new (keymap, NULL);
1629
1630       _gtk_mnemonic_hash_foreach (mnemonic_hash,
1631                                   menu_shell_add_mnemonic_foreach,
1632                                   private->key_hash);
1633     }
1634   
1635   return private->key_hash;
1636 }
1637
1638 static void
1639 gtk_menu_shell_reset_key_hash (GtkMenuShell *menu_shell)
1640 {
1641   GtkMenuShellPrivate *private = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
1642
1643   if (private->key_hash)
1644     {
1645       _gtk_key_hash_free (private->key_hash);
1646       private->key_hash = NULL;
1647     }
1648 }
1649
1650 static gboolean
1651 gtk_menu_shell_activate_mnemonic (GtkMenuShell *menu_shell,
1652                                   GdkEventKey  *event)
1653 {
1654   GtkMnemonicHash *mnemonic_hash;
1655   GtkKeyHash *key_hash;
1656   GSList *entries;
1657   gboolean result = FALSE;
1658
1659   mnemonic_hash = gtk_menu_shell_get_mnemonic_hash (menu_shell, FALSE);
1660   if (!mnemonic_hash)
1661     return FALSE;
1662
1663   key_hash = gtk_menu_shell_get_key_hash (menu_shell, TRUE);
1664   if (!key_hash)
1665     return FALSE;
1666   
1667   entries = _gtk_key_hash_lookup (key_hash,
1668                                   event->hardware_keycode,
1669                                   event->state,
1670                                   gtk_accelerator_get_default_mod_mask (),
1671                                   event->group);
1672
1673   if (entries)
1674     result = _gtk_mnemonic_hash_activate (mnemonic_hash,
1675                                           GPOINTER_TO_UINT (entries->data));
1676
1677   return result;
1678 }
1679
1680 void
1681 _gtk_menu_shell_add_mnemonic (GtkMenuShell *menu_shell,
1682                               guint      keyval,
1683                               GtkWidget *target)
1684 {
1685   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1686   g_return_if_fail (GTK_IS_WIDGET (target));
1687
1688   _gtk_mnemonic_hash_add (gtk_menu_shell_get_mnemonic_hash (menu_shell, TRUE),
1689                           keyval, target);
1690   gtk_menu_shell_reset_key_hash (menu_shell);
1691 }
1692
1693 void
1694 _gtk_menu_shell_remove_mnemonic (GtkMenuShell *menu_shell,
1695                                  guint      keyval,
1696                                  GtkWidget *target)
1697 {
1698   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1699   g_return_if_fail (GTK_IS_WIDGET (target));
1700   
1701   _gtk_mnemonic_hash_remove (gtk_menu_shell_get_mnemonic_hash (menu_shell, TRUE),
1702                              keyval, target);
1703   gtk_menu_shell_reset_key_hash (menu_shell);
1704 }
1705
1706 /**
1707  * gtk_menu_shell_get_take_focus:
1708  * @menu_shell: a #GtkMenuShell
1709  *
1710  * Returns %TRUE if the menu shell will take the keyboard focus on popup.
1711  *
1712  * Returns: %TRUE if the menu shell will take the keyboard focus on popup.
1713  *
1714  * Since: 2.8
1715  **/
1716 gboolean
1717 gtk_menu_shell_get_take_focus (GtkMenuShell *menu_shell)
1718 {
1719   GtkMenuShellPrivate *priv;
1720
1721   g_return_val_if_fail (GTK_IS_MENU_SHELL (menu_shell), FALSE);
1722
1723   priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
1724
1725   return priv->take_focus;
1726 }
1727
1728 /**
1729  * gtk_menu_shell_set_take_focus:
1730  * @menu_shell: a #GtkMenuShell
1731  * @take_focus: %TRUE if the menu shell should take the keyboard focus on popup.
1732  *
1733  * If @take_focus is %TRUE (the default) the menu shell will take the keyboard 
1734  * focus so that it will receive all keyboard events which is needed to enable
1735  * keyboard navigation in menus.
1736  *
1737  * Setting @take_focus to %FALSE is useful only for special applications
1738  * like virtual keyboard implementations which should not take keyboard
1739  * focus.
1740  *
1741  * The @take_focus state of a menu or menu bar is automatically propagated
1742  * to submenus whenever a submenu is popped up, so you don't have to worry
1743  * about recursively setting it for your entire menu hierarchy. Only when
1744  * programmatically picking a submenu and popping it up manually, the
1745  * @take_focus property of the submenu needs to be set explicitely.
1746  *
1747  * Note that setting it to %FALSE has side-effects:
1748  *
1749  * If the focus is in some other app, it keeps the focus and keynav in
1750  * the menu doesn't work. Consequently, keynav on the menu will only
1751  * work if the focus is on some toplevel owned by the onscreen keyboard.
1752  *
1753  * To avoid confusing the user, menus with @take_focus set to %FALSE
1754  * should not display mnemonics or accelerators, since it cannot be
1755  * guaranteed that they will work.
1756  *
1757  * See also gdk_keyboard_grab()
1758  *
1759  * Since: 2.8
1760  **/
1761 void
1762 gtk_menu_shell_set_take_focus (GtkMenuShell *menu_shell,
1763                                gboolean      take_focus)
1764 {
1765   GtkMenuShellPrivate *priv;
1766
1767   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1768
1769   priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
1770
1771   if (priv->take_focus != take_focus)
1772     {
1773       priv->take_focus = take_focus;
1774       g_object_notify (G_OBJECT (menu_shell), "take-focus");
1775     }
1776 }
1777
1778 #define __GTK_MENU_SHELL_C__
1779 #include "gtkaliasdef.c"