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