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