source: trunk/third/gtk/gtk/gtkmenushell.c @ 17071

Revision 17071, 29.3 KB checked in by ghudson, 23 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r17070, which included commits to RCS files with non-trunk default branches.
RevLine 
[14481]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 Library 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 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library 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-1999.  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 "gdk/gdkkeysyms.h"
28#include "gtkbindings.h"
29#include "gtkmain.h"
30#include "gtkmenuitem.h"
31#include "gtktearoffmenuitem.h" /* FIXME */
32#include "gtkmenushell.h"
33#include "gtksignal.h"
34
35
36#define MENU_SHELL_TIMEOUT   500
37#define MENU_SHELL_CLASS(w)  GTK_MENU_SHELL_CLASS (GTK_OBJECT (w)->klass)
38
39
40enum {
41  DEACTIVATE,
42  SELECTION_DONE,
43  MOVE_CURRENT,
44  ACTIVATE_CURRENT,
45  CANCEL,
46  LAST_SIGNAL
47};
48
49typedef void (*GtkMenuShellSignal1) (GtkObject           *object,
50                                     GtkMenuDirectionType arg1,
51                                     gpointer             data);
52typedef void (*GtkMenuShellSignal2) (GtkObject *object,
53                                     gboolean   arg1,
54                                     gpointer   data);
55
56/* Terminology:
57 *
58 * A menu item can be "selected", this means that it is displayed
59 * in the prelight state, and if it has a submenu, that submenu
60 * will be popped up.
61 *
62 * A menu is "active" when it is visible onscreen and the user
63 * is selecting from it. A menubar is not active until the user
64 * clicks on one of its menuitems. When a menu is active,
65 * passing the mouse over a submenu will pop it up.
66 *
67 * menu_shell->active_menu_item, is however, not an "active"
68 * menu item (there is no such thing) but rather, the selected
69 * menu item in that MenuShell, if there is one.
70 *
71 * There is also is a concept of the current menu and a current
72 * menu item. The current menu item is the selected menu item
73 * that is furthest down in the heirarchy. (Every active menu_shell
74 * does not necessarily contain a selected menu item, but if
75 * it does, then menu_shell->parent_menu_shell must also contain
76 * a selected menu item. The current menu is the menu that
77 * contains the current menu_item. It will always have a GTK
78 * grab and receive all key presses.
79 *
80 *
81 * Action signals:
82 *
83 *  ::move_current (GtkMenuDirection *dir)
84 *     Moves the current menu item in direction 'dir':
85 *
86 *       GTK_MENU_DIR_PARENT: To the parent menu shell
87 *       GTK_MENU_DIR_CHILD: To the child menu shell (if this item has
88 *          a submenu.
89 *       GTK_MENU_DIR_NEXT/PREV: To the next or previous item
90 *          in this menu.
91 *
92 *     As a a bit of a hack to get movement between menus and
93 *     menubars working, if submenu_placement is different for
94 *     the menu and its MenuShell then the following apply:
95 *
96 *       - For 'parent' the current menu is not just moved to
97 *         the parent, but moved to the previous entry in the parent
98 *       - For 'child', if there is no child, then current is
99 *         moved to the next item in the parent.
100 *
101 *
102 *  ::activate_current (GBoolean *force_hide)
103 *     Activate the current item. If 'force_hide' is true, hide
104 *     the current menu item always. Otherwise, only hide
105 *     it if menu_item->klass->hide_on_activate is true.
106 *
107 *  ::cancel ()
108 *     Cancels the current selection
109 */
110
111static void gtk_menu_shell_class_init        (GtkMenuShellClass *klass);
112static void gtk_menu_shell_init              (GtkMenuShell      *menu_shell);
113static void gtk_menu_shell_map               (GtkWidget         *widget);
114static void gtk_menu_shell_realize           (GtkWidget         *widget);
115static gint gtk_menu_shell_button_press      (GtkWidget         *widget,
116                                              GdkEventButton    *event);
117static gint gtk_menu_shell_button_release    (GtkWidget         *widget,
118                                              GdkEventButton    *event);
119static gint gtk_menu_shell_key_press         (GtkWidget         *widget,
120                                              GdkEventKey       *event);
121static gint gtk_menu_shell_enter_notify      (GtkWidget         *widget,
122                                              GdkEventCrossing  *event);
123static gint gtk_menu_shell_leave_notify      (GtkWidget         *widget,
124                                              GdkEventCrossing  *event);
125static void gtk_menu_shell_add               (GtkContainer      *container,
126                                              GtkWidget         *widget);
127static void gtk_menu_shell_remove            (GtkContainer      *container,
128                                              GtkWidget         *widget);
129static void gtk_menu_shell_forall            (GtkContainer      *container,
130                                              gboolean           include_internals,
131                                              GtkCallback        callback,
132                                              gpointer           callback_data);
133static void gtk_real_menu_shell_deactivate   (GtkMenuShell      *menu_shell);
134static gint gtk_menu_shell_is_item           (GtkMenuShell      *menu_shell,
135                                              GtkWidget         *child);
136static GtkWidget *gtk_menu_shell_get_item    (GtkMenuShell      *menu_shell,
137                                              GdkEvent          *event);
138static GtkType    gtk_menu_shell_child_type  (GtkContainer      *container);
[15780]139static void gtk_menu_shell_select_submenu_first (GtkMenuShell   *menu_shell);
[14481]140
141static void gtk_real_menu_shell_move_current (GtkMenuShell      *menu_shell,
142                                              GtkMenuDirectionType direction);
143static void gtk_real_menu_shell_activate_current (GtkMenuShell      *menu_shell,
144                                                  gboolean           force_hide);
145static void gtk_real_menu_shell_cancel           (GtkMenuShell      *menu_shell);
146
147static GtkContainerClass *parent_class = NULL;
148static guint menu_shell_signals[LAST_SIGNAL] = { 0 };
149
150
151GtkType
152gtk_menu_shell_get_type (void)
153{
154  static GtkType menu_shell_type = 0;
155
156  if (!menu_shell_type)
157    {
158      static const GtkTypeInfo menu_shell_info =
159      {
160        "GtkMenuShell",
161        sizeof (GtkMenuShell),
162        sizeof (GtkMenuShellClass),
163        (GtkClassInitFunc) gtk_menu_shell_class_init,
164        (GtkObjectInitFunc) gtk_menu_shell_init,
165        /* reserved_1 */ NULL,
166        /* reserved_2 */ NULL,
167        (GtkClassInitFunc) NULL,
168      };
169
170      menu_shell_type = gtk_type_unique (gtk_container_get_type (), &menu_shell_info);
171    }
172
173  return menu_shell_type;
174}
175
176static void
177gtk_menu_shell_class_init (GtkMenuShellClass *klass)
178{
179  GtkObjectClass *object_class;
180  GtkWidgetClass *widget_class;
181  GtkContainerClass *container_class;
182
183  GtkBindingSet *binding_set;
184
185  object_class = (GtkObjectClass*) klass;
186  widget_class = (GtkWidgetClass*) klass;
187  container_class = (GtkContainerClass*) klass;
188
189  parent_class = gtk_type_class (gtk_container_get_type ());
190
191  menu_shell_signals[DEACTIVATE] =
192    gtk_signal_new ("deactivate",
193                    GTK_RUN_FIRST,
194                    object_class->type,
195                    GTK_SIGNAL_OFFSET (GtkMenuShellClass, deactivate),
196                    gtk_marshal_NONE__NONE,
197                    GTK_TYPE_NONE, 0);
198  menu_shell_signals[SELECTION_DONE] =
199    gtk_signal_new ("selection-done",
200                    GTK_RUN_FIRST,
201                    object_class->type,
202                    GTK_SIGNAL_OFFSET (GtkMenuShellClass, selection_done),
203                    gtk_marshal_NONE__NONE,
204                    GTK_TYPE_NONE, 0);
205  menu_shell_signals[MOVE_CURRENT] =
206    gtk_signal_new ("move_current",
207                    GTK_RUN_LAST | GTK_RUN_ACTION,
208                    object_class->type,
209                    GTK_SIGNAL_OFFSET (GtkMenuShellClass, move_current),
210                    gtk_marshal_NONE__ENUM,
211                    GTK_TYPE_NONE, 1,
212                    GTK_TYPE_MENU_DIRECTION_TYPE);
213  menu_shell_signals[ACTIVATE_CURRENT] =
214    gtk_signal_new ("activate_current",
215                    GTK_RUN_LAST | GTK_RUN_ACTION,
216                    object_class->type,
217                    GTK_SIGNAL_OFFSET (GtkMenuShellClass, activate_current),
218                    gtk_marshal_NONE__BOOL,
219                    GTK_TYPE_NONE, 1,
220                    GTK_TYPE_BOOL);
221  menu_shell_signals[CANCEL] =
222    gtk_signal_new ("cancel",
223                    GTK_RUN_LAST | GTK_RUN_ACTION,
224                    object_class->type,
225                    GTK_SIGNAL_OFFSET (GtkMenuShellClass, cancel),
226                    gtk_marshal_NONE__NONE,
227                    GTK_TYPE_NONE, 0);
228 
229  gtk_object_class_add_signals (object_class, menu_shell_signals, LAST_SIGNAL);
230
231  widget_class->map = gtk_menu_shell_map;
232  widget_class->realize = gtk_menu_shell_realize;
233  widget_class->button_press_event = gtk_menu_shell_button_press;
234  widget_class->button_release_event = gtk_menu_shell_button_release;
235  widget_class->key_press_event = gtk_menu_shell_key_press;
236  widget_class->enter_notify_event = gtk_menu_shell_enter_notify;
237  widget_class->leave_notify_event = gtk_menu_shell_leave_notify;
238
239  container_class->add = gtk_menu_shell_add;
240  container_class->remove = gtk_menu_shell_remove;
241  container_class->forall = gtk_menu_shell_forall;
242  container_class->child_type = gtk_menu_shell_child_type;
243
244  klass->submenu_placement = GTK_TOP_BOTTOM;
245  klass->deactivate = gtk_real_menu_shell_deactivate;
246  klass->selection_done = NULL;
247  klass->move_current = gtk_real_menu_shell_move_current;
248  klass->activate_current = gtk_real_menu_shell_activate_current;
249  klass->cancel = gtk_real_menu_shell_cancel;
250
251  binding_set = gtk_binding_set_by_class (klass);
252  gtk_binding_entry_add_signal (binding_set,
253                                GDK_Escape, 0,
254                                "cancel", 0);
255  gtk_binding_entry_add_signal (binding_set,
256                                GDK_Return, 0,
257                                "activate_current", 1,
258                                GTK_TYPE_BOOL,
259                                TRUE);
260  gtk_binding_entry_add_signal (binding_set,
261                                GDK_space, 0,
262                                "activate_current", 1,
263                                GTK_TYPE_BOOL,
264                                FALSE);
265}
266
267static GtkType
268gtk_menu_shell_child_type (GtkContainer     *container)
269{
270  return GTK_TYPE_MENU_ITEM;
271}
272
273static void
274gtk_menu_shell_init (GtkMenuShell *menu_shell)
275{
276  menu_shell->children = NULL;
277  menu_shell->active_menu_item = NULL;
278  menu_shell->parent_menu_shell = NULL;
279  menu_shell->active = FALSE;
280  menu_shell->have_grab = FALSE;
281  menu_shell->have_xgrab = FALSE;
282  menu_shell->ignore_leave = FALSE;
283  menu_shell->button = 0;
284  menu_shell->menu_flag = 0;
285  menu_shell->activate_time = 0;
286}
287
288void
289gtk_menu_shell_append (GtkMenuShell *menu_shell,
290                       GtkWidget    *child)
291{
292  gtk_menu_shell_insert (menu_shell, child, -1);
293}
294
295void
296gtk_menu_shell_prepend (GtkMenuShell *menu_shell,
297                        GtkWidget    *child)
298{
299  gtk_menu_shell_insert (menu_shell, child, 0);
300}
301
302void
303gtk_menu_shell_insert (GtkMenuShell *menu_shell,
304                       GtkWidget    *child,
305                       gint          position)
306{
307  g_return_if_fail (menu_shell != NULL);
308  g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
309  g_return_if_fail (child != NULL);
310  g_return_if_fail (GTK_IS_MENU_ITEM (child));
311
312  menu_shell->children = g_list_insert (menu_shell->children, child, position);
313
314  gtk_widget_set_parent (child, GTK_WIDGET (menu_shell));
315
316  if (GTK_WIDGET_REALIZED (child->parent))
317    gtk_widget_realize (child);
318
319  if (GTK_WIDGET_VISIBLE (child->parent) && GTK_WIDGET_VISIBLE (child))
320    {
321      if (GTK_WIDGET_MAPPED (child->parent))
322        gtk_widget_map (child);
323
324      gtk_widget_queue_resize (child);
325    }
326}
327
328void
329gtk_menu_shell_deactivate (GtkMenuShell *menu_shell)
330{
331  g_return_if_fail (menu_shell != NULL);
332  g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
333
334  gtk_signal_emit (GTK_OBJECT (menu_shell), menu_shell_signals[DEACTIVATE]);
335}
336
337static void
338gtk_menu_shell_map (GtkWidget *widget)
339{
340  GtkMenuShell *menu_shell;
341  GtkWidget *child;
342  GList *children;
343
344  g_return_if_fail (widget != NULL);
345  g_return_if_fail (GTK_IS_MENU_SHELL (widget));
346
347  menu_shell = GTK_MENU_SHELL (widget);
348  GTK_WIDGET_SET_FLAGS (menu_shell, GTK_MAPPED);
349
350  children = menu_shell->children;
351  while (children)
352    {
353      child = children->data;
354      children = children->next;
355
356      if (GTK_WIDGET_VISIBLE (child) && !GTK_WIDGET_MAPPED (child))
357        gtk_widget_map (child);
358    }
359
360  gdk_window_show (widget->window);
361}
362
363static void
364gtk_menu_shell_realize (GtkWidget *widget)
365{
366  GdkWindowAttr attributes;
367  gint attributes_mask;
368
369  g_return_if_fail (widget != NULL);
370  g_return_if_fail (GTK_IS_MENU_SHELL (widget));
371
372  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
373
374  attributes.x = widget->allocation.x;
375  attributes.y = widget->allocation.y;
376  attributes.width = widget->allocation.width;
377  attributes.height = widget->allocation.height;
378  attributes.window_type = GDK_WINDOW_CHILD;
379  attributes.wclass = GDK_INPUT_OUTPUT;
380  attributes.visual = gtk_widget_get_visual (widget);
381  attributes.colormap = gtk_widget_get_colormap (widget);
382  attributes.event_mask = gtk_widget_get_events (widget);
383  attributes.event_mask |= (GDK_EXPOSURE_MASK |
384                            GDK_BUTTON_PRESS_MASK |
385                            GDK_BUTTON_RELEASE_MASK |
386                            GDK_KEY_PRESS_MASK |
387                            GDK_ENTER_NOTIFY_MASK |
388                            GDK_LEAVE_NOTIFY_MASK);
389
390  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
391  widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
392  gdk_window_set_user_data (widget->window, widget);
393
394  widget->style = gtk_style_attach (widget->style, widget->window);
395  gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
396}
397
398static gint
399gtk_menu_shell_button_press (GtkWidget      *widget,
400                             GdkEventButton *event)
401{
402  GtkMenuShell *menu_shell;
403  GtkWidget *menu_item;
404
405  g_return_val_if_fail (widget != NULL, FALSE);
406  g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
407  g_return_val_if_fail (event != NULL, FALSE);
408
409  if (event->type != GDK_BUTTON_PRESS)
410    return FALSE;
411
412  menu_shell = GTK_MENU_SHELL (widget);
413
414  if (menu_shell->parent_menu_shell)
415    {
416      gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
417    }
418  else if (!menu_shell->active || !menu_shell->button)
419    {
420      if (!menu_shell->active)
421        {
422          gtk_grab_add (GTK_WIDGET (widget));
423          menu_shell->have_grab = TRUE;
424          menu_shell->active = TRUE;
425        }
426      menu_shell->button = event->button;
427
428      menu_item = gtk_menu_shell_get_item (menu_shell, (GdkEvent *)event);
429
430      if (menu_item &&
431          GTK_WIDGET_IS_SENSITIVE (menu_item))
432        {
433          if ((menu_item->parent == widget) &&
434              (menu_item != menu_shell->active_menu_item))
435            gtk_menu_shell_select_item (menu_shell, menu_item);
436        }
437    }
438  else
439    {
440      widget = gtk_get_event_widget ((GdkEvent*) event);
441      if (widget == GTK_WIDGET (menu_shell))
442        {
443          gtk_menu_shell_deactivate (menu_shell);
444          gtk_signal_emit (GTK_OBJECT (menu_shell), menu_shell_signals[SELECTION_DONE]);
445        }
446    }
447
448  return TRUE;
449}
450
451static gint
452gtk_menu_shell_button_release (GtkWidget      *widget,
453                               GdkEventButton *event)
454{
455  GtkMenuShell *menu_shell;
456  GtkWidget *menu_item;
457  gint deactivate;
458
459  g_return_val_if_fail (widget != NULL, FALSE);
460  g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
461  g_return_val_if_fail (event != NULL, FALSE);
462
463  menu_shell = GTK_MENU_SHELL (widget);
464  if (menu_shell->active)
465    {
466      if (menu_shell->button && (event->button != menu_shell->button))
467        {
468          menu_shell->button = 0;
469          if (menu_shell->parent_menu_shell)
470            gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
471          return TRUE;
472        }
473     
474      menu_shell->button = 0;
475      menu_item = gtk_menu_shell_get_item (menu_shell, (GdkEvent*) event);
476
477      deactivate = TRUE;
478
479      if ((event->time - menu_shell->activate_time) > MENU_SHELL_TIMEOUT)
480        {
481          if (menu_item && (menu_shell->active_menu_item == menu_item) &&
482              GTK_WIDGET_IS_SENSITIVE (menu_item))
483            {
484              if (GTK_MENU_ITEM (menu_item)->submenu == NULL)
485                {
486                  gtk_menu_shell_activate_item (menu_shell, menu_item, TRUE);
487                  return TRUE;
488                }
489            }
490          else if (menu_shell->parent_menu_shell)
491            {
492              menu_shell->active = TRUE;
493              gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
494              return TRUE;
495            }
496        }
497      else
498        {
499          /* We only ever want to prevent deactivation on the first
500           * press/release. Setting the time to zero is a bit of a
501           * hack, since we could be being triggered in the first
502           * few fractions of a second after a server time wraparound.
503           * the chances of that happening are ~1/10^6, without
504           * serious harm if we lose.
505           */
506          menu_shell->activate_time = 0;
507          deactivate = FALSE;
508        }
509     
510      /* If the button click was very fast, or we ended up on a submenu,
511       * leave the menu up
512       */
513      if (!deactivate ||
514          (menu_item && (menu_shell->active_menu_item == menu_item)))
515        {
516          deactivate = FALSE;
517          menu_shell->ignore_leave = TRUE;
518        }
519      else
520        deactivate = TRUE;
521
522      if (deactivate)
523        {
524          gtk_menu_shell_deactivate (menu_shell);
525          gtk_signal_emit (GTK_OBJECT (menu_shell), menu_shell_signals[SELECTION_DONE]);
526        }
527    }
528
529  return TRUE;
530}
531
532static gint
533gtk_menu_shell_key_press (GtkWidget     *widget,
534                          GdkEventKey *event)
535{
536  GtkMenuShell *menu_shell;
537 
538  g_return_val_if_fail (widget != NULL, FALSE);
539  g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
540  g_return_val_if_fail (event != NULL, FALSE);
541     
542  menu_shell = GTK_MENU_SHELL (widget);
543
544  if (!menu_shell->active_menu_item && menu_shell->parent_menu_shell)
545    return gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent *)event);
546 
547  if (gtk_bindings_activate (GTK_OBJECT (widget),
548                             event->keyval,
549                             event->state))
550    return TRUE;
551
552  if (gtk_accel_groups_activate (GTK_OBJECT (widget), event->keyval, event->state))
553    return TRUE;
554
555  return FALSE;
556}
557
558static gint
559gtk_menu_shell_enter_notify (GtkWidget        *widget,
560                             GdkEventCrossing *event)
561{
562  GtkMenuShell *menu_shell;
563  GtkWidget *menu_item;
564
565  g_return_val_if_fail (widget != NULL, FALSE);
566  g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
567  g_return_val_if_fail (event != NULL, FALSE);
568
569  menu_shell = GTK_MENU_SHELL (widget);
570
[17070]571  if (menu_shell->active)
[14481]572    {
573      menu_item = gtk_get_event_widget ((GdkEvent*) event);
574
575      if (!menu_item || !GTK_WIDGET_IS_SENSITIVE (menu_item))
576        return TRUE;
577
578      if ((menu_item->parent == widget) &&
579          (menu_shell->active_menu_item != menu_item) &&
580          GTK_IS_MENU_ITEM (menu_item))
581        {
[17070]582          if (menu_shell->ignore_enter)
583            return TRUE;
584         
[14481]585          if ((event->detail != GDK_NOTIFY_INFERIOR) &&
586              (GTK_WIDGET_STATE (menu_item) != GTK_STATE_PRELIGHT))
587            {
588              gtk_menu_shell_select_item (menu_shell, menu_item);
589            }
590        }
591      else if (menu_shell->parent_menu_shell)
592        {
593          gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
594        }
595    }
596
597  return TRUE;
598}
599
600static gint
601gtk_menu_shell_leave_notify (GtkWidget        *widget,
602                             GdkEventCrossing *event)
603{
604  GtkMenuShell *menu_shell;
605  GtkMenuItem *menu_item;
606  GtkWidget *event_widget;
607
608  g_return_val_if_fail (widget != NULL, FALSE);
609  g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
610  g_return_val_if_fail (event != NULL, FALSE);
611
612  if (GTK_WIDGET_VISIBLE (widget))
613    {
614      menu_shell = GTK_MENU_SHELL (widget);
615      event_widget = gtk_get_event_widget ((GdkEvent*) event);
616
617      if (!event_widget || !GTK_IS_MENU_ITEM (event_widget))
618        return TRUE;
619
620      menu_item = GTK_MENU_ITEM (event_widget);
621
622      if (menu_shell->ignore_leave)
623        {
624          menu_shell->ignore_leave = FALSE;
625          return TRUE;
626        }
627
628      if (!GTK_WIDGET_IS_SENSITIVE (menu_item))
629        return TRUE;
630
631      if ((menu_shell->active_menu_item == event_widget) &&
632          (menu_item->submenu == NULL))
633        {
634          if ((event->detail != GDK_NOTIFY_INFERIOR) &&
635              (GTK_WIDGET_STATE (menu_item) != GTK_STATE_NORMAL))
636            {
637              gtk_menu_shell_deselect (menu_shell);
638            }
639        }
640      else if (menu_shell->parent_menu_shell)
641        {
642          gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
643        }
644    }
645
646  return TRUE;
647}
648
649static void
650gtk_menu_shell_add (GtkContainer *container,
651                    GtkWidget    *widget)
652{
653  gtk_menu_shell_append (GTK_MENU_SHELL (container), widget);
654}
655
656static void
657gtk_menu_shell_remove (GtkContainer *container,
658                       GtkWidget    *widget)
659{
660  GtkMenuShell *menu_shell;
661  gint was_visible;
662 
663  g_return_if_fail (container != NULL);
664  g_return_if_fail (GTK_IS_MENU_SHELL (container));
665  g_return_if_fail (widget != NULL);
666  g_return_if_fail (GTK_IS_MENU_ITEM (widget));
667 
668  was_visible = GTK_WIDGET_VISIBLE (widget);
669  menu_shell = GTK_MENU_SHELL (container);
670  menu_shell->children = g_list_remove (menu_shell->children, widget);
671 
[15780]672  if (widget == menu_shell->active_menu_item)
673    {
674      gtk_item_deselect (GTK_ITEM (menu_shell->active_menu_item));
675      menu_shell->active_menu_item = NULL;
676    }
677
[14481]678  gtk_widget_unparent (widget);
679 
680  /* queue resize regardless of GTK_WIDGET_VISIBLE (container),
681   * since that's what is needed by toplevels.
682   */
683  if (was_visible)
684    gtk_widget_queue_resize (GTK_WIDGET (container));
685}
686
687static void
688gtk_menu_shell_forall (GtkContainer *container,
689                       gboolean      include_internals,
690                       GtkCallback   callback,
691                       gpointer      callback_data)
692{
693  GtkMenuShell *menu_shell;
694  GtkWidget *child;
695  GList *children;
696
697  g_return_if_fail (container != NULL);
698  g_return_if_fail (GTK_IS_MENU_SHELL (container));
699  g_return_if_fail (callback != NULL);
700
701  menu_shell = GTK_MENU_SHELL (container);
702
703  children = menu_shell->children;
704  while (children)
705    {
706      child = children->data;
707      children = children->next;
708
709      (* callback) (child, callback_data);
710    }
711}
712
713
714static void
715gtk_real_menu_shell_deactivate (GtkMenuShell *menu_shell)
716{
717  g_return_if_fail (menu_shell != NULL);
718  g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
719
720  if (menu_shell->active)
721    {
722      menu_shell->button = 0;
723      menu_shell->active = FALSE;
724
725      if (menu_shell->active_menu_item)
726        {
727          gtk_menu_item_deselect (GTK_MENU_ITEM (menu_shell->active_menu_item));
728          menu_shell->active_menu_item = NULL;
729        }
730
731      if (menu_shell->have_grab)
732        {
733          menu_shell->have_grab = FALSE;
734          gtk_grab_remove (GTK_WIDGET (menu_shell));
735        }
736      if (menu_shell->have_xgrab)
737        {
738          menu_shell->have_xgrab = FALSE;
739          gdk_pointer_ungrab (GDK_CURRENT_TIME);
740          gdk_keyboard_ungrab (GDK_CURRENT_TIME);
741        }
742    }
743}
744
745static gint
746gtk_menu_shell_is_item (GtkMenuShell *menu_shell,
747                        GtkWidget    *child)
748{
749  GtkWidget *parent;
750
751  g_return_val_if_fail (menu_shell != NULL, FALSE);
752  g_return_val_if_fail (GTK_IS_MENU_SHELL (menu_shell), FALSE);
753  g_return_val_if_fail (child != NULL, FALSE);
754
755  parent = child->parent;
756  while (parent && GTK_IS_MENU_SHELL (parent))
757    {
758      if (parent == (GtkWidget*) menu_shell)
759        return TRUE;
760      parent = GTK_MENU_SHELL (parent)->parent_menu_shell;
761    }
762
763  return FALSE;
764}
765
766static GtkWidget*
767gtk_menu_shell_get_item (GtkMenuShell *menu_shell,
768                         GdkEvent     *event)
769{
770  GtkWidget *menu_item;
771
772  menu_item = gtk_get_event_widget ((GdkEvent*) event);
773 
774  while (menu_item && !GTK_IS_MENU_ITEM (menu_item))
775    menu_item = menu_item->parent;
776
777  if (menu_item && gtk_menu_shell_is_item (menu_shell, menu_item))
778    return menu_item;
779  else
780    return NULL;
781}
782
783/* Handlers for action signals */
784
785void
786gtk_menu_shell_select_item (GtkMenuShell *menu_shell,
787                            GtkWidget    *menu_item)
788{
789  g_return_if_fail (menu_shell != NULL);
790  g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
791  g_return_if_fail (menu_item != NULL);
792  g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
793
794  gtk_menu_shell_deselect (menu_shell);
795 
796  menu_shell->active_menu_item = menu_item;
797  gtk_menu_item_set_placement (GTK_MENU_ITEM (menu_shell->active_menu_item),
798                               MENU_SHELL_CLASS (menu_shell)->submenu_placement);
799  gtk_menu_item_select (GTK_MENU_ITEM (menu_shell->active_menu_item));
800
801  /* This allows the bizarre radio buttons-with-submenus-display-history
802   * behavior
803   */
804  if (GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu)
805    gtk_widget_activate (menu_shell->active_menu_item);
806}
807
808void
809gtk_menu_shell_deselect (GtkMenuShell *menu_shell)
810{
811  g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
812
813  if (menu_shell->active_menu_item)
814    {
815      gtk_menu_item_deselect (GTK_MENU_ITEM (menu_shell->active_menu_item));
816      menu_shell->active_menu_item = NULL;
817    }
818}
819
820void
821gtk_menu_shell_activate_item (GtkMenuShell      *menu_shell,
822                              GtkWidget         *menu_item,
823                              gboolean           force_deactivate)
824{
825  GSList *slist, *shells = NULL;
826  gboolean deactivate = force_deactivate;
827
828  g_return_if_fail (menu_shell != NULL);
829  g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
830  g_return_if_fail (menu_item != NULL);
831  g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
832
833  if (!deactivate)
834    deactivate = GTK_MENU_ITEM_CLASS (GTK_OBJECT (menu_item)->klass)->hide_on_activate;
835
836  gtk_widget_ref (GTK_WIDGET (menu_shell));
837
838  if (deactivate)
839    {
840      GtkMenuShell *parent_menu_shell = menu_shell;
841
842      do
843        {
844          gtk_widget_ref (GTK_WIDGET (parent_menu_shell));
845          shells = g_slist_prepend (shells, parent_menu_shell);
846          parent_menu_shell = (GtkMenuShell*) parent_menu_shell->parent_menu_shell;
847        }
848      while (parent_menu_shell);
849      shells = g_slist_reverse (shells);
850
851      gtk_menu_shell_deactivate (menu_shell);
852 
853      /* flush the x-queue, so any grabs are removed and
854       * the menu is actually taken down
855       */
856      gdk_flush ();
857    }
858
859  gtk_widget_activate (menu_item);
860
861  for (slist = shells; slist; slist = slist->next)
862    {
863      gtk_signal_emit (slist->data, menu_shell_signals[SELECTION_DONE]);
864      gtk_widget_unref (slist->data);
865    }
866  g_slist_free (shells);
867
868  gtk_widget_unref (GTK_WIDGET (menu_shell));
869}
870
871/* Distance should be +/- 1 */
872static void
873gtk_menu_shell_move_selected (GtkMenuShell  *menu_shell,
874                              gint           distance)
875{
876  if (menu_shell->active_menu_item)
877    {
878      GList *node = g_list_find (menu_shell->children,
879                                 menu_shell->active_menu_item);
880      GList *start_node = node;
881     
882      if (distance > 0)
883        {
884          node = node->next;
885          while (node != start_node &&
886                 (!node ||
887                  !GTK_WIDGET_IS_SENSITIVE (node->data) ||
888                  !GTK_WIDGET_VISIBLE (node->data) ))
889            {
890              if (!node)
891                node = menu_shell->children;
892              else
893                node = node->next;
894            }
895        }
896      else
897        {
898          node = node->prev;
899          while (node != start_node &&
900                 (!node ||
901                  !GTK_WIDGET_IS_SENSITIVE (node->data) ||
902                  !GTK_WIDGET_VISIBLE (node->data) ))
903            {
904              if (!node)
905                node = g_list_last (menu_shell->children);
906              else
907                node = node->prev;
908            }
909        }
910     
911      if (node)
912        gtk_menu_shell_select_item (menu_shell, node->data);
913    }
914}
915
916static void
[15780]917gtk_menu_shell_select_submenu_first (GtkMenuShell     *menu_shell)
918{
919  GtkMenuItem *menu_item;
920
921  menu_item = GTK_MENU_ITEM (menu_shell->active_menu_item);
922 
923  if (menu_item->submenu)
924    {
925      GtkMenuShell *submenu = GTK_MENU_SHELL (menu_item->submenu);
926      if (submenu->children)
927        gtk_menu_shell_select_item (submenu, submenu->children->data);
928    }
929}
930
931static void
[14481]932gtk_real_menu_shell_move_current (GtkMenuShell      *menu_shell,
933                                  GtkMenuDirectionType direction)
934{
935  GtkMenuShell *parent_menu_shell = NULL;
936  gboolean had_selection;
937
938  had_selection = menu_shell->active_menu_item != NULL;
939
940  if (menu_shell->parent_menu_shell)
941    parent_menu_shell = GTK_MENU_SHELL (menu_shell->parent_menu_shell);
942 
943  switch (direction)
944    {
945    case GTK_MENU_DIR_PARENT:
946      if (parent_menu_shell)
947        {
948          if (GTK_MENU_SHELL_CLASS (GTK_OBJECT (parent_menu_shell)->klass)->submenu_placement ==
949                       GTK_MENU_SHELL_CLASS (GTK_OBJECT (menu_shell)->klass)->submenu_placement)
950            gtk_menu_shell_deselect (menu_shell);
[15780]951          else
952            {
953              gtk_menu_shell_move_selected (parent_menu_shell, -1);
954              gtk_menu_shell_select_submenu_first (parent_menu_shell);
955            }
[14481]956        }
957      break;
958     
959    case GTK_MENU_DIR_CHILD:
960      if (menu_shell->active_menu_item &&
961          GTK_BIN (menu_shell->active_menu_item)->child &&
962          GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu)
963        {
964          menu_shell = GTK_MENU_SHELL (GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu);
965          if (menu_shell->children)
966            gtk_menu_shell_select_item (menu_shell, menu_shell->children->data);
967        }
968      else
969        {
970          /* Try to find a menu running the opposite direction */
971          while (parent_menu_shell &&
972                 (GTK_MENU_SHELL_CLASS (GTK_OBJECT (parent_menu_shell)->klass)->submenu_placement ==
973                  GTK_MENU_SHELL_CLASS (GTK_OBJECT (menu_shell)->klass)->submenu_placement))
974            parent_menu_shell = GTK_MENU_SHELL (parent_menu_shell->parent_menu_shell);
975         
976          if (parent_menu_shell)
[15780]977            {
978              gtk_menu_shell_move_selected (parent_menu_shell, 1);
979              gtk_menu_shell_select_submenu_first (parent_menu_shell);
980            }
[14481]981        }
982      break;
983     
984    case GTK_MENU_DIR_PREV:
985      gtk_menu_shell_move_selected (menu_shell, -1);
986      if (!had_selection &&
987          !menu_shell->active_menu_item &&
988          menu_shell->children)
989        gtk_menu_shell_select_item (menu_shell, g_list_last (menu_shell->children)->data);
990      break;
991    case GTK_MENU_DIR_NEXT:
992      gtk_menu_shell_move_selected (menu_shell, 1);
993      if (!had_selection &&
994          !menu_shell->active_menu_item &&
995          menu_shell->children)
996        gtk_menu_shell_select_item (menu_shell, menu_shell->children->data);
997      break;
998    }
999 
1000}
1001
1002static void
1003gtk_real_menu_shell_activate_current (GtkMenuShell      *menu_shell,
1004                                      gboolean           force_hide)
1005{
1006  if (menu_shell->active_menu_item &&
1007      GTK_WIDGET_IS_SENSITIVE (menu_shell->active_menu_item) &&
1008      GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu == NULL)
1009    {
1010      gtk_menu_shell_activate_item (menu_shell,
1011                                    menu_shell->active_menu_item,
1012                                    force_hide);
1013    }
1014}
1015
1016static void
1017gtk_real_menu_shell_cancel (GtkMenuShell      *menu_shell)
1018{
[15780]1019  /* Unset the active menu item so gtk_menu_popdown() doesn't see it.
1020   */
1021  gtk_menu_shell_deselect (menu_shell);
1022 
[14481]1023  gtk_menu_shell_deactivate (menu_shell);
1024  gtk_signal_emit (GTK_OBJECT (menu_shell), menu_shell_signals[SELECTION_DONE]);
1025}
1026
Note: See TracBrowser for help on using the repository browser.