source: trunk/third/gtk/gtk/gtkmenu.c @ 15781

Revision 15781, 42.8 KB checked in by ghudson, 24 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r15780, which included commits to RCS files with non-trunk default branches.
Line 
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 <ctype.h>
28#include "gdk/gdkkeysyms.h"
29#include "gtkbindings.h"
30#include "gtklabel.h"
31#include "gtkmain.h"
32#include "gtkmenu.h"
33#include "gtkmenuitem.h"
34#include "gtksignal.h"
35#include "gtkwindow.h"
36
37
38#define MENU_ITEM_CLASS(w)   GTK_MENU_ITEM_CLASS (GTK_OBJECT (w)->klass)
39#define MENU_NEEDS_RESIZE(m) GTK_MENU_SHELL (m)->menu_flag
40
41#define SUBMENU_NAV_REGION_PADDING 2
42#define SUBMENU_NAV_HYSTERESIS_TIMEOUT 333
43
44typedef struct _GtkMenuAttachData       GtkMenuAttachData;
45
46struct _GtkMenuAttachData
47{
48  GtkWidget *attach_widget;
49  GtkMenuDetachFunc detacher;
50};
51
52
53static void     gtk_menu_class_init    (GtkMenuClass      *klass);
54static void     gtk_menu_init          (GtkMenu           *menu);
55static void     gtk_menu_destroy       (GtkObject         *object);
56static void     gtk_menu_realize       (GtkWidget         *widget);
57static void     gtk_menu_size_request  (GtkWidget         *widget,
58                                        GtkRequisition    *requisition);
59static void     gtk_menu_size_allocate (GtkWidget         *widget,
60                                        GtkAllocation     *allocation);
61static void     gtk_menu_paint         (GtkWidget         *widget);
62static void     gtk_menu_draw          (GtkWidget         *widget,
63                                        GdkRectangle      *area);
64static gboolean gtk_menu_expose        (GtkWidget         *widget,
65                                        GdkEventExpose    *event);
66static gboolean gtk_menu_key_press     (GtkWidget         *widget,
67                                        GdkEventKey       *event);
68static gboolean gtk_menu_motion_notify (GtkWidget         *widget,
69                                        GdkEventMotion    *event);
70static gboolean gtk_menu_enter_notify  (GtkWidget         *widget,
71                                        GdkEventCrossing  *event);
72static gboolean gtk_menu_leave_notify  (GtkWidget         *widget,
73                                        GdkEventCrossing  *event);
74
75static void     gtk_menu_stop_navigating_submenu       (GtkMenu          *menu);
76static gboolean gtk_menu_stop_navigating_submenu_cb    (gpointer          user_data);
77static gboolean gtk_menu_navigating_submenu            (GtkMenu          *menu,
78                                                        gint              event_x,
79                                                        gint              event_y);
80static void     gtk_menu_set_submenu_navigation_region (GtkMenu          *menu,
81                                                        GtkMenuItem      *menu_item,
82                                                        GdkEventCrossing *event);
83static GdkRegion *gtk_menu_get_navigation_region       (GtkMenu          *menu);
84static void     gtk_menu_set_navigation_region         (GtkMenu          *menu,
85                                                        GdkRegion        *region);
86static guint    gtk_menu_get_navigation_timeout        (GtkMenu          *menu);
87static void     gtk_menu_set_navigation_timeout        (GtkMenu          *menu,
88                                                        guint            timeout);
89 
90static void gtk_menu_deactivate     (GtkMenuShell      *menu_shell);
91static void gtk_menu_show_all       (GtkWidget         *widget);
92static void gtk_menu_hide_all       (GtkWidget         *widget);
93static void gtk_menu_position       (GtkMenu           *menu);
94static void gtk_menu_reparent       (GtkMenu           *menu,
95                                     GtkWidget         *new_parent,
96                                     gboolean           unrealize);
97static void gtk_menu_remove         (GtkContainer      *menu,
98                                     GtkWidget         *widget);
99
100static GtkMenuShellClass *parent_class = NULL;
101static const gchar       *attach_data_key = "gtk-menu-attach-data";
102static GQuark             quark_uline_accel_group = 0;
103
104static const gchar       *navigation_region_key = "gtk-menu-navigation_region";
105static GQuark             navigation_region_key_id = 0;
106static const gchar       *navigation_timeout_key = "gtk-menu-navigation_timeout";
107static GQuark             navigation_timeout_key_id = 0;
108
109
110GtkType
111gtk_menu_get_type (void)
112{
113  static GtkType menu_type = 0;
114 
115  if (!menu_type)
116    {
117      static const GtkTypeInfo menu_info =
118      {
119        "GtkMenu",
120        sizeof (GtkMenu),
121        sizeof (GtkMenuClass),
122        (GtkClassInitFunc) gtk_menu_class_init,
123        (GtkObjectInitFunc) gtk_menu_init,
124        /* reserved_1 */ NULL,
125        /* reserved_2 */ NULL,
126        (GtkClassInitFunc) NULL,
127      };
128     
129      menu_type = gtk_type_unique (gtk_menu_shell_get_type (), &menu_info);
130    }
131 
132  return menu_type;
133}
134
135static void
136gtk_menu_class_init (GtkMenuClass *class)
137{
138  GtkObjectClass *object_class;
139  GtkWidgetClass *widget_class;
140  GtkContainerClass *container_class;
141  GtkMenuShellClass *menu_shell_class;
142
143  GtkBindingSet *binding_set;
144 
145  object_class = (GtkObjectClass*) class;
146  widget_class = (GtkWidgetClass*) class;
147  container_class = (GtkContainerClass*) class;
148  menu_shell_class = (GtkMenuShellClass*) class;
149  parent_class = gtk_type_class (gtk_menu_shell_get_type ());
150 
151  object_class->destroy = gtk_menu_destroy;
152 
153  widget_class->realize = gtk_menu_realize;
154  widget_class->draw = gtk_menu_draw;
155  widget_class->size_request = gtk_menu_size_request;
156  widget_class->size_allocate = gtk_menu_size_allocate;
157  widget_class->expose_event = gtk_menu_expose;
158  widget_class->key_press_event = gtk_menu_key_press;
159  widget_class->motion_notify_event = gtk_menu_motion_notify;
160  widget_class->show_all = gtk_menu_show_all;
161  widget_class->hide_all = gtk_menu_hide_all;
162  widget_class->enter_notify_event = gtk_menu_enter_notify;
163  widget_class->leave_notify_event = gtk_menu_leave_notify;
164
165  container_class->remove = gtk_menu_remove;
166 
167  menu_shell_class->submenu_placement = GTK_LEFT_RIGHT;
168  menu_shell_class->deactivate = gtk_menu_deactivate;
169
170  binding_set = gtk_binding_set_by_class (class);
171  gtk_binding_entry_add_signal (binding_set,
172                                GDK_Up, 0,
173                                "move_current", 1,
174                                GTK_TYPE_MENU_DIRECTION_TYPE,
175                                GTK_MENU_DIR_PREV);
176  gtk_binding_entry_add_signal (binding_set,
177                                GDK_Down, 0,
178                                "move_current", 1,
179                                GTK_TYPE_MENU_DIRECTION_TYPE,
180                                GTK_MENU_DIR_NEXT);
181  gtk_binding_entry_add_signal (binding_set,
182                                GDK_Left, 0,
183                                "move_current", 1,
184                                GTK_TYPE_MENU_DIRECTION_TYPE,
185                                GTK_MENU_DIR_PARENT);
186  gtk_binding_entry_add_signal (binding_set,
187                                GDK_Right, 0,
188                                "move_current", 1,
189                                GTK_TYPE_MENU_DIRECTION_TYPE,
190                                GTK_MENU_DIR_CHILD);
191}
192
193static gboolean
194gtk_menu_window_event (GtkWidget *window,
195                       GdkEvent  *event,
196                       GtkWidget *menu)
197{
198  gboolean handled = FALSE;
199
200  gtk_widget_ref (window);
201  gtk_widget_ref (menu);
202
203  switch (event->type)
204    {
205    case GDK_KEY_PRESS:
206    case GDK_KEY_RELEASE:
207      gtk_widget_event (menu, event);
208      handled = TRUE;
209      break;
210    default:
211      break;
212    }
213
214  gtk_widget_unref (window);
215  gtk_widget_unref (menu);
216
217  return handled;
218}
219
220static void
221gtk_menu_init (GtkMenu *menu)
222{
223  menu->parent_menu_item = NULL;
224  menu->old_active_menu_item = NULL;
225  menu->accel_group = NULL;
226  menu->position_func = NULL;
227  menu->position_func_data = NULL;
228
229  menu->toplevel = gtk_window_new (GTK_WINDOW_POPUP);
230  gtk_signal_connect (GTK_OBJECT (menu->toplevel),
231                      "event",
232                      GTK_SIGNAL_FUNC (gtk_menu_window_event),
233                      GTK_OBJECT (menu));
234  gtk_window_set_policy (GTK_WINDOW (menu->toplevel),
235                         FALSE, FALSE, TRUE);
236
237  gtk_container_add (GTK_CONTAINER (menu->toplevel), GTK_WIDGET (menu));
238
239  /* Refloat the menu, so that reference counting for the menu isn't
240   * affected by it being a child of the toplevel
241   */
242  GTK_WIDGET_SET_FLAGS (menu, GTK_FLOATING);
243
244  menu->tearoff_window = NULL;
245  menu->torn_off = FALSE;
246
247  MENU_NEEDS_RESIZE (menu) = TRUE;
248}
249
250static void
251gtk_menu_destroy (GtkObject         *object)
252{
253  GtkMenu *menu;
254  GtkMenuAttachData *data;
255 
256  g_return_if_fail (object != NULL);
257  g_return_if_fail (GTK_IS_MENU (object));
258
259  menu = GTK_MENU (object);
260 
261  gtk_object_ref (object);
262 
263  data = gtk_object_get_data (object, attach_data_key);
264  if (data)
265    gtk_menu_detach (menu);
266 
267  gtk_menu_stop_navigating_submenu (menu);
268
269  gtk_menu_set_accel_group (menu, NULL);
270
271  if (menu->old_active_menu_item)
272    {
273      gtk_widget_unref (menu->old_active_menu_item);
274      menu->old_active_menu_item = NULL;
275    }
276
277  /* Add back the reference count for being a child */
278  gtk_object_ref (object);
279 
280  gtk_widget_destroy (menu->toplevel);
281  if (menu->tearoff_window)
282    gtk_widget_destroy (menu->tearoff_window);
283
284  if (GTK_OBJECT_CLASS (parent_class)->destroy)
285    (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
286 
287  gtk_object_unref (object);
288}
289
290
291void
292gtk_menu_attach_to_widget (GtkMenu             *menu,
293                           GtkWidget           *attach_widget,
294                           GtkMenuDetachFunc    detacher)
295{
296  GtkMenuAttachData *data;
297 
298  g_return_if_fail (menu != NULL);
299  g_return_if_fail (GTK_IS_MENU (menu));
300  g_return_if_fail (attach_widget != NULL);
301  g_return_if_fail (GTK_IS_WIDGET (attach_widget));
302  g_return_if_fail (detacher != NULL);
303 
304  /* keep this function in sync with gtk_widget_set_parent()
305   */
306 
307  data = gtk_object_get_data (GTK_OBJECT (menu), attach_data_key);
308  if (data)
309    {
310      g_warning ("gtk_menu_attach_to_widget(): menu already attached to %s",
311                 gtk_type_name (GTK_OBJECT_TYPE (data->attach_widget)));
312      return;
313    }
314 
315  gtk_object_ref (GTK_OBJECT (menu));
316  gtk_object_sink (GTK_OBJECT (menu));
317 
318  data = g_new (GtkMenuAttachData, 1);
319  data->attach_widget = attach_widget;
320  data->detacher = detacher;
321  gtk_object_set_data (GTK_OBJECT (menu), attach_data_key, data);
322 
323  if (GTK_WIDGET_STATE (menu) != GTK_STATE_NORMAL)
324    gtk_widget_set_state (GTK_WIDGET (menu), GTK_STATE_NORMAL);
325 
326  /* we don't need to set the style here, since
327   * we are a toplevel widget.
328   */
329}
330
331GtkWidget*
332gtk_menu_get_attach_widget (GtkMenu             *menu)
333{
334  GtkMenuAttachData *data;
335 
336  g_return_val_if_fail (menu != NULL, NULL);
337  g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
338 
339  data = gtk_object_get_data (GTK_OBJECT (menu), attach_data_key);
340  if (data)
341    return data->attach_widget;
342  return NULL;
343}
344
345void
346gtk_menu_detach (GtkMenu             *menu)
347{
348  GtkMenuAttachData *data;
349 
350  g_return_if_fail (menu != NULL);
351  g_return_if_fail (GTK_IS_MENU (menu));
352 
353  /* keep this function in sync with gtk_widget_unparent()
354   */
355  data = gtk_object_get_data (GTK_OBJECT (menu), attach_data_key);
356  if (!data)
357    {
358      g_warning ("gtk_menu_detach(): menu is not attached");
359      return;
360    }
361  gtk_object_remove_data (GTK_OBJECT (menu), attach_data_key);
362 
363  data->detacher (data->attach_widget, menu);
364 
365  if (GTK_WIDGET_REALIZED (menu))
366    gtk_widget_unrealize (GTK_WIDGET (menu));
367 
368  g_free (data);
369 
370  gtk_widget_unref (GTK_WIDGET (menu));
371}
372
373GtkWidget*
374gtk_menu_new (void)
375{
376  return GTK_WIDGET (gtk_type_new (gtk_menu_get_type ()));
377}
378
379void
380gtk_menu_append (GtkMenu   *menu,
381                 GtkWidget *child)
382{
383  gtk_menu_shell_append (GTK_MENU_SHELL (menu), child);
384}
385
386void
387gtk_menu_prepend (GtkMenu   *menu,
388                  GtkWidget *child)
389{
390  gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), child);
391}
392
393void
394gtk_menu_insert (GtkMenu   *menu,
395                 GtkWidget *child,
396                 gint       position)
397{
398  gtk_menu_shell_insert (GTK_MENU_SHELL (menu), child, position);
399}
400
401void
402gtk_menu_remove(GtkContainer *container,
403                GtkWidget    *widget)
404{
405  GtkMenu *menu;
406  g_return_if_fail (GTK_IS_MENU (container));
407  g_return_if_fail (GTK_IS_MENU_ITEM (widget));
408
409  menu = GTK_MENU (container);
410
411  /* Clear out old_active_menu_item if it matches the item we are removing
412   */
413  if (menu->old_active_menu_item == widget)
414    {
415      gtk_widget_unref (menu->old_active_menu_item);
416      menu->old_active_menu_item = NULL;
417    }
418
419  GTK_CONTAINER_CLASS (parent_class)->remove (container, widget);
420}
421
422
423static void
424gtk_menu_tearoff_bg_copy (GtkMenu *menu)
425{
426  GtkWidget *widget;
427
428  widget = GTK_WIDGET (menu);
429
430  if (menu->torn_off)
431    {
432      GdkPixmap *pixmap;
433      GdkGC *gc;
434      GdkGCValues gc_values;
435     
436      gc_values.subwindow_mode = GDK_INCLUDE_INFERIORS;
437      gc = gdk_gc_new_with_values (widget->window,
438                                   &gc_values, GDK_GC_SUBWINDOW);
439     
440      pixmap = gdk_pixmap_new (widget->window,
441                               widget->requisition.width,
442                               widget->requisition.height,
443                               -1);
444
445      gdk_draw_pixmap (pixmap, gc,
446                       widget->window,
447                       0, 0, 0, 0, -1, -1);
448      gdk_gc_unref (gc);
449     
450      gtk_widget_set_usize (menu->tearoff_window,
451                            widget->requisition.width,
452                            widget->requisition.height);
453     
454      gdk_window_set_back_pixmap (menu->tearoff_window->window, pixmap, FALSE);
455      gdk_pixmap_unref (pixmap);
456    }
457}
458
459void
460gtk_menu_popup (GtkMenu             *menu,
461                GtkWidget           *parent_menu_shell,
462                GtkWidget           *parent_menu_item,
463                GtkMenuPositionFunc  func,
464                gpointer             data,
465                guint                button,
466                guint32              activate_time)
467{
468  GtkWidget *widget;
469  GtkWidget *xgrab_shell;
470  GtkWidget *parent;
471  GdkEvent *current_event;
472  GtkMenuShell *menu_shell;
473 
474  g_return_if_fail (menu != NULL);
475  g_return_if_fail (GTK_IS_MENU (menu));
476 
477  widget = GTK_WIDGET (menu);
478  menu_shell = GTK_MENU_SHELL (menu);
479 
480  menu_shell->parent_menu_shell = parent_menu_shell;
481  menu_shell->active = TRUE;
482  menu_shell->button = button;
483
484  /* If we are popping up the menu from something other than, a button
485   * press then, as a heuristic, we ignore enter events for the menu
486   * until we get a MOTION_NOTIFY. 
487   */
488
489  current_event = gtk_get_current_event();
490  if (current_event)
491    {
492      if ((current_event->type != GDK_BUTTON_PRESS) &&
493          (current_event->type != GDK_ENTER_NOTIFY))
494        menu_shell->ignore_enter = TRUE;
495      gdk_event_free (current_event);
496    }
497
498  if (menu->torn_off)
499    {
500      gtk_menu_tearoff_bg_copy (menu);
501
502      /* We force an unrealize here so that we don't trigger redrawing/
503       * clearing code - we just want to reveal our backing pixmap.
504       */
505      gtk_menu_reparent (menu, menu->toplevel, TRUE);
506    }
507 
508  menu->parent_menu_item = parent_menu_item;
509  menu->position_func = func;
510  menu->position_func_data = data;
511  menu_shell->activate_time = activate_time;
512
513  gtk_menu_position (menu);
514
515  /* We need to show the menu _here_ because code expects to be
516   * able to tell if the menu is onscreen by looking at the
517   * GTK_WIDGET_VISIBLE (menu)
518   */
519  gtk_widget_show (GTK_WIDGET (menu));
520  gtk_widget_show (menu->toplevel);
521 
522  /* Find the last viewable ancestor, and make an X grab on it
523   */
524  parent = GTK_WIDGET (menu);
525  xgrab_shell = NULL;
526  while (parent)
527    {
528      gboolean viewable = TRUE;
529      GtkWidget *tmp = parent;
530     
531      while (tmp)
532        {
533          if (!GTK_WIDGET_MAPPED (tmp))
534            {
535              viewable = FALSE;
536              break;
537            }
538          tmp = tmp->parent;
539        }
540     
541      if (viewable)
542        xgrab_shell = parent;
543     
544      parent = GTK_MENU_SHELL (parent)->parent_menu_shell;
545    }
546 
547  if (xgrab_shell && (!GTK_MENU_SHELL (xgrab_shell)->have_xgrab))
548    {
549      if ((gdk_pointer_grab (xgrab_shell->window, TRUE,
550                             GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
551                             GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
552                             GDK_POINTER_MOTION_MASK,
553                             NULL, NULL, activate_time) == 0))
554        {
555          if (gdk_keyboard_grab (xgrab_shell->window, TRUE,
556                              activate_time) == 0)
557            GTK_MENU_SHELL (xgrab_shell)->have_xgrab = TRUE;
558          else
559            {
560              gdk_pointer_ungrab (activate_time);
561            }
562        }
563    }
564 
565  gtk_grab_add (GTK_WIDGET (menu));
566}
567
568void
569gtk_menu_popdown (GtkMenu *menu)
570{
571  GtkMenuShell *menu_shell;
572 
573  g_return_if_fail (menu != NULL);
574  g_return_if_fail (GTK_IS_MENU (menu));
575 
576  menu_shell = GTK_MENU_SHELL (menu);
577 
578  menu_shell->parent_menu_shell = NULL;
579  menu_shell->active = FALSE;
580  menu_shell->ignore_enter = FALSE;
581 
582  gtk_menu_stop_navigating_submenu (menu);
583 
584  if (menu_shell->active_menu_item)
585    {
586      if (menu->old_active_menu_item)
587        gtk_widget_unref (menu->old_active_menu_item);
588      menu->old_active_menu_item = menu_shell->active_menu_item;
589      gtk_widget_ref (menu->old_active_menu_item);
590    }
591
592  gtk_menu_shell_deselect (menu_shell);
593 
594  /* The X Grab, if present, will automatically be removed when we hide
595   * the window */
596  gtk_widget_hide (menu->toplevel);
597
598  if (menu->torn_off)
599    {
600      if (GTK_BIN (menu->toplevel)->child)
601        {
602          gtk_menu_reparent (menu, menu->tearoff_window, FALSE);
603        }
604      else
605        {
606          /* We popped up the menu from the tearoff, so we need to
607           * release the grab - we aren't actually hiding the menu.
608           */
609          if (menu_shell->have_xgrab)
610            {
611              gdk_pointer_ungrab (GDK_CURRENT_TIME);
612              gdk_keyboard_ungrab (GDK_CURRENT_TIME);
613            }
614        }
615    }
616  else
617    gtk_widget_hide (GTK_WIDGET (menu));
618       
619  menu_shell->have_xgrab = FALSE;
620  gtk_grab_remove (GTK_WIDGET (menu));
621}
622
623GtkWidget*
624gtk_menu_get_active (GtkMenu *menu)
625{
626  GtkWidget *child;
627  GList *children;
628 
629  g_return_val_if_fail (menu != NULL, NULL);
630  g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
631 
632  if (!menu->old_active_menu_item)
633    {
634      child = NULL;
635      children = GTK_MENU_SHELL (menu)->children;
636     
637      while (children)
638        {
639          child = children->data;
640          children = children->next;
641         
642          if (GTK_BIN (child)->child)
643            break;
644          child = NULL;
645        }
646     
647      menu->old_active_menu_item = child;
648      if (menu->old_active_menu_item)
649        gtk_widget_ref (menu->old_active_menu_item);
650    }
651 
652  return menu->old_active_menu_item;
653}
654
655void
656gtk_menu_set_active (GtkMenu *menu,
657                     guint    index)
658{
659  GtkWidget *child;
660  GList *tmp_list;
661 
662  g_return_if_fail (menu != NULL);
663  g_return_if_fail (GTK_IS_MENU (menu));
664 
665  tmp_list = g_list_nth (GTK_MENU_SHELL (menu)->children, index);
666  if (tmp_list)
667    {
668      child = tmp_list->data;
669      if (GTK_BIN (child)->child)
670        {
671          if (menu->old_active_menu_item)
672            gtk_widget_unref (menu->old_active_menu_item);
673          menu->old_active_menu_item = child;
674          gtk_widget_ref (menu->old_active_menu_item);
675        }
676    }
677}
678
679void
680gtk_menu_set_accel_group (GtkMenu       *menu,
681                          GtkAccelGroup *accel_group)
682{
683  g_return_if_fail (GTK_IS_MENU (menu));
684 
685  if (menu->accel_group != accel_group)
686    {
687      if (menu->accel_group)
688        gtk_accel_group_unref (menu->accel_group);
689      menu->accel_group = accel_group;
690      if (menu->accel_group)
691        gtk_accel_group_ref (menu->accel_group);
692    }
693}
694
695GtkAccelGroup*
696gtk_menu_get_accel_group (GtkMenu *menu)
697{
698  g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
699
700  return menu->accel_group;
701}
702
703GtkAccelGroup*
704gtk_menu_ensure_uline_accel_group (GtkMenu *menu)
705{
706  GtkAccelGroup *accel_group;
707
708  g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
709
710  if (!quark_uline_accel_group)
711    quark_uline_accel_group = g_quark_from_static_string ("GtkMenu-uline-accel-group");
712
713  accel_group = gtk_object_get_data_by_id (GTK_OBJECT (menu), quark_uline_accel_group);
714  if (!accel_group)
715    {
716      accel_group = gtk_accel_group_new ();
717      gtk_accel_group_attach (accel_group, GTK_OBJECT (menu));
718      gtk_object_set_data_by_id_full (GTK_OBJECT (menu),
719                                      quark_uline_accel_group,
720                                      accel_group,
721                                      (GtkDestroyNotify) gtk_accel_group_unref);
722    }
723
724  return accel_group;
725}
726
727GtkAccelGroup*
728gtk_menu_get_uline_accel_group (GtkMenu *menu)
729{
730  g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
731
732  return gtk_object_get_data_by_id (GTK_OBJECT (menu), quark_uline_accel_group);
733}
734
735void
736gtk_menu_reposition (GtkMenu *menu)
737{
738  g_return_if_fail (menu != NULL);
739  g_return_if_fail (GTK_IS_MENU (menu));
740
741  if (GTK_WIDGET_DRAWABLE (menu) && !menu->torn_off)
742    gtk_menu_position (menu);
743}
744
745
746void       
747gtk_menu_set_tearoff_state (GtkMenu  *menu,
748                            gboolean  torn_off)
749{
750  g_return_if_fail (menu != NULL);
751  g_return_if_fail (GTK_IS_MENU (menu));
752
753  if (menu->torn_off != torn_off)
754    {
755      menu->torn_off = torn_off;
756     
757      if (menu->torn_off)
758        {
759          if (GTK_WIDGET_VISIBLE (menu))
760            gtk_menu_popdown (menu);
761
762          if (!menu->tearoff_window)
763            {
764              GtkWidget *attach_widget;
765              gchar *title;
766             
767              menu->tearoff_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
768              gtk_widget_set_app_paintable (menu->tearoff_window, TRUE);
769              gtk_signal_connect (GTK_OBJECT (menu->tearoff_window), 
770                                  "event",
771                                  GTK_SIGNAL_FUNC (gtk_menu_window_event),
772                                  GTK_OBJECT (menu));
773              gtk_widget_realize (menu->tearoff_window);
774             
775              title = gtk_object_get_data (GTK_OBJECT (menu), "gtk-menu-title");
776              if (!title)
777                {
778                  attach_widget = gtk_menu_get_attach_widget (menu);
779                  if (GTK_IS_MENU_ITEM (attach_widget))
780                    {
781                      GtkWidget *child = GTK_BIN (attach_widget)->child;
782                      if (GTK_IS_LABEL (child))
783                        gtk_label_get (GTK_LABEL (child), &title);
784                    }
785                }
786
787              if (title)
788                gdk_window_set_title (menu->tearoff_window->window, title);
789
790              gdk_window_set_decorations (menu->tearoff_window->window,
791                                          GDK_DECOR_ALL |
792                                          GDK_DECOR_RESIZEH |
793                                          GDK_DECOR_MINIMIZE |
794                                          GDK_DECOR_MAXIMIZE);
795              gtk_window_set_policy (GTK_WINDOW (menu->tearoff_window),
796                                     FALSE, FALSE, TRUE);
797            }
798          gtk_menu_reparent (menu, menu->tearoff_window, FALSE);
799
800          gtk_menu_position (menu);
801         
802          gtk_widget_show (GTK_WIDGET (menu));
803          gtk_widget_show (menu->tearoff_window);
804        }
805      else
806        {
807          gtk_widget_hide (menu->tearoff_window);
808          gtk_menu_reparent (menu, menu->toplevel, FALSE);
809        }
810    }
811}
812
813void       
814gtk_menu_set_title (GtkMenu     *menu,
815                    const gchar *title)
816{
817  g_return_if_fail (menu != NULL);
818  g_return_if_fail (GTK_IS_MENU (menu));
819
820  gtk_object_set_data_full (GTK_OBJECT (menu), "gtk-menu-title",
821                            g_strdup (title), (GtkDestroyNotify) g_free);
822}
823
824void
825gtk_menu_reorder_child (GtkMenu   *menu,
826                        GtkWidget *child,
827                        gint       position)
828{
829  GtkMenuShell *menu_shell;
830  g_return_if_fail (GTK_IS_MENU (menu));
831  g_return_if_fail (GTK_IS_MENU_ITEM (child));
832  menu_shell = GTK_MENU_SHELL (menu);
833  if (g_list_find (menu_shell->children, child))
834    {   
835      menu_shell->children = g_list_remove (menu_shell->children, child);
836      menu_shell->children = g_list_insert (menu_shell->children, child, position);   
837      if (GTK_WIDGET_VISIBLE (menu_shell))
838        gtk_widget_queue_resize (GTK_WIDGET (menu_shell));
839    }   
840}
841
842static void
843gtk_menu_realize (GtkWidget *widget)
844{
845  GdkWindowAttr attributes;
846  gint attributes_mask;
847 
848  g_return_if_fail (widget != NULL);
849  g_return_if_fail (GTK_IS_MENU (widget));
850 
851  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
852 
853  attributes.window_type = GDK_WINDOW_CHILD;
854  attributes.x = widget->allocation.x;
855  attributes.y = widget->allocation.y;
856  attributes.width = widget->allocation.width;
857  attributes.height = widget->allocation.height;
858  attributes.wclass = GDK_INPUT_OUTPUT;
859  attributes.visual = gtk_widget_get_visual (widget);
860  attributes.colormap = gtk_widget_get_colormap (widget);
861  attributes.event_mask = gtk_widget_get_events (widget);
862  attributes.event_mask |= (GDK_EXPOSURE_MASK | GDK_KEY_PRESS_MASK);
863 
864  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
865  widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
866  gdk_window_set_user_data (widget->window, widget);
867 
868  widget->style = gtk_style_attach (widget->style, widget->window);
869  gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
870  gtk_menu_paint(widget);
871}
872
873static void
874gtk_menu_size_request (GtkWidget      *widget,
875                       GtkRequisition *requisition)
876{
877  GtkMenu *menu;
878  GtkMenuShell *menu_shell;
879  GtkWidget *child;
880  GList *children;
881  guint max_toggle_size;
882  guint max_accel_width;
883  GtkRequisition child_requisition;
884 
885  g_return_if_fail (widget != NULL);
886  g_return_if_fail (GTK_IS_MENU (widget));
887  g_return_if_fail (requisition != NULL);
888 
889  menu = GTK_MENU (widget);
890  menu_shell = GTK_MENU_SHELL (widget);
891 
892  requisition->width = 0;
893  requisition->height = 0;
894 
895  max_toggle_size = 0;
896  max_accel_width = 0;
897 
898  children = menu_shell->children;
899  while (children)
900    {
901      child = children->data;
902      children = children->next;
903     
904      if (GTK_WIDGET_VISIBLE (child))
905        {
906          GTK_MENU_ITEM (child)->show_submenu_indicator = TRUE;
907          gtk_widget_size_request (child, &child_requisition);
908         
909          requisition->width = MAX (requisition->width, child_requisition.width);
910          requisition->height += child_requisition.height;
911         
912          max_toggle_size = MAX (max_toggle_size, MENU_ITEM_CLASS (child)->toggle_size);
913          max_accel_width = MAX (max_accel_width, GTK_MENU_ITEM (child)->accelerator_width);
914        }
915    }
916 
917  requisition->width += max_toggle_size + max_accel_width;
918  requisition->width += (GTK_CONTAINER (menu)->border_width +
919                         widget->style->klass->xthickness) * 2;
920  requisition->height += (GTK_CONTAINER (menu)->border_width +
921                          widget->style->klass->ythickness) * 2;
922 
923  children = menu_shell->children;
924  while (children)
925    {
926      child = children->data;
927      children = children->next;
928     
929      GTK_MENU_ITEM (child)->toggle_size = max_toggle_size;
930    }
931}
932
933static void
934gtk_menu_size_allocate (GtkWidget     *widget,
935                        GtkAllocation *allocation)
936{
937  GtkMenu *menu;
938  GtkMenuShell *menu_shell;
939  GtkWidget *child;
940  GtkAllocation child_allocation;
941  GList *children;
942 
943  g_return_if_fail (widget != NULL);
944  g_return_if_fail (GTK_IS_MENU (widget));
945  g_return_if_fail (allocation != NULL);
946 
947  menu = GTK_MENU (widget);
948  menu_shell = GTK_MENU_SHELL (widget);
949 
950  widget->allocation = *allocation;
951  if (GTK_WIDGET_REALIZED (widget))
952    gdk_window_move_resize (widget->window,
953                            allocation->x, allocation->y,
954                            allocation->width, allocation->height);
955
956
957  if (menu_shell->children)
958    {
959      child_allocation.x = (GTK_CONTAINER (menu)->border_width +
960                            widget->style->klass->xthickness);
961      child_allocation.y = (GTK_CONTAINER (menu)->border_width +
962                            widget->style->klass->ythickness);
963      child_allocation.width = MAX (1, (gint)allocation->width - child_allocation.x * 2);
964     
965      children = menu_shell->children;
966      while (children)
967        {
968          child = children->data;
969          children = children->next;
970         
971          if (GTK_WIDGET_VISIBLE (child))
972            {
973              GtkRequisition child_requisition;
974              gtk_widget_get_child_requisition (child, &child_requisition);
975             
976              child_allocation.height = child_requisition.height;
977             
978              gtk_widget_size_allocate (child, &child_allocation);
979              gtk_widget_queue_draw (child);
980             
981              child_allocation.y += child_allocation.height;
982            }
983        }
984    }
985}
986
987static void
988gtk_menu_paint (GtkWidget *widget)
989{
990  g_return_if_fail (widget != NULL);
991  g_return_if_fail (GTK_IS_MENU (widget));
992 
993  if (GTK_WIDGET_DRAWABLE (widget))
994    {
995      gtk_paint_box (widget->style,
996                     widget->window,
997                     GTK_STATE_NORMAL,
998                     GTK_SHADOW_OUT,
999                     NULL, widget, "menu",
1000                     0, 0, -1, -1);
1001    }
1002}
1003
1004static void
1005gtk_menu_draw (GtkWidget    *widget,
1006               GdkRectangle *area)
1007{
1008  GtkMenuShell *menu_shell;
1009  GtkWidget *child;
1010  GdkRectangle child_area;
1011  GList *children;
1012 
1013  g_return_if_fail (widget != NULL);
1014  g_return_if_fail (GTK_IS_MENU (widget));
1015  g_return_if_fail (area != NULL);
1016 
1017  if (GTK_WIDGET_DRAWABLE (widget))
1018    {
1019      gtk_menu_paint (widget);
1020     
1021      menu_shell = GTK_MENU_SHELL (widget);
1022     
1023      children = menu_shell->children;
1024      while (children)
1025        {
1026          child = children->data;
1027          children = children->next;
1028         
1029          if (gtk_widget_intersect (child, area, &child_area))
1030            gtk_widget_draw (child, &child_area);
1031        }
1032    }
1033}
1034
1035static gboolean
1036gtk_menu_expose (GtkWidget      *widget,
1037                 GdkEventExpose *event)
1038{
1039  GtkMenuShell *menu_shell;
1040  GtkWidget *child;
1041  GdkEventExpose child_event;
1042  GList *children;
1043  GtkMenu *menu;
1044 
1045  g_return_val_if_fail (widget != NULL, FALSE);
1046  g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
1047  g_return_val_if_fail (event != NULL, FALSE);
1048
1049  menu_shell = GTK_MENU_SHELL (widget);
1050  menu = GTK_MENU (widget);
1051 
1052  if (GTK_WIDGET_DRAWABLE (widget))
1053    {
1054      gtk_menu_paint (widget);
1055     
1056      child_event = *event;
1057     
1058      children = menu_shell->children;
1059      while (children)
1060        {
1061          child = children->data;
1062          children = children->next;
1063         
1064          if (GTK_WIDGET_NO_WINDOW (child) &&
1065              gtk_widget_intersect (child, &event->area, &child_event.area))
1066            gtk_widget_event (child, (GdkEvent*) &child_event);
1067        }
1068    }
1069 
1070  return FALSE;
1071}
1072
1073static gboolean
1074gtk_menu_key_press (GtkWidget   *widget,
1075                    GdkEventKey *event)
1076{
1077  GtkMenuShell *menu_shell;
1078  gboolean delete = FALSE;
1079 
1080  g_return_val_if_fail (widget != NULL, FALSE);
1081  g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
1082  g_return_val_if_fail (event != NULL, FALSE);
1083     
1084  menu_shell = GTK_MENU_SHELL (widget);
1085
1086  gtk_menu_stop_navigating_submenu (GTK_MENU (widget));
1087
1088  if (GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event))
1089    return TRUE;
1090
1091  switch (event->keyval)
1092    {
1093    case GDK_Delete:
1094    case GDK_KP_Delete:
1095    case GDK_BackSpace:
1096      delete = TRUE;
1097      break;
1098    default:
1099      break;
1100    }
1101
1102  /* Modify the accelerators */
1103  if (menu_shell->active_menu_item &&
1104      GTK_BIN (menu_shell->active_menu_item)->child &&
1105      GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu == NULL &&
1106      !gtk_widget_accelerators_locked (menu_shell->active_menu_item) &&
1107      (delete ||
1108       (gtk_accelerator_valid (event->keyval, event->state) &&
1109        (event->state ||
1110         !gtk_menu_get_uline_accel_group (GTK_MENU (menu_shell)) ||
1111         (event->keyval >= GDK_F1 && event->keyval <= GDK_F35)))))
1112    {
1113      GtkMenuItem *menu_item;
1114      GtkAccelGroup *accel_group;
1115     
1116      menu_item = GTK_MENU_ITEM (menu_shell->active_menu_item);
1117     
1118      if (!GTK_MENU (widget)->accel_group)
1119        accel_group = gtk_accel_group_get_default ();
1120      else
1121        accel_group = GTK_MENU (widget)->accel_group;
1122     
1123      gtk_widget_remove_accelerators (GTK_WIDGET (menu_item),
1124                                      gtk_signal_name (menu_item->accelerator_signal),
1125                                      TRUE);
1126     
1127      if (!delete &&
1128          0 == gtk_widget_accelerator_signal (GTK_WIDGET (menu_item),
1129                                              accel_group,
1130                                              event->keyval,
1131                                              event->state))
1132        {
1133          GSList *slist;
1134         
1135          slist = gtk_accel_group_entries_from_object (GTK_OBJECT (menu_item));
1136          while (slist)
1137            {
1138              GtkAccelEntry *ac_entry;
1139             
1140              ac_entry = slist->data;
1141             
1142              if (ac_entry->signal_id == menu_item->accelerator_signal)
1143                break;
1144             
1145              slist = slist->next;
1146            }
1147         
1148          if (!slist)
1149            gtk_widget_add_accelerator (GTK_WIDGET (menu_item),
1150                                        gtk_signal_name (menu_item->accelerator_signal),
1151                                        accel_group,
1152                                        event->keyval,
1153                                        event->state,
1154                                        GTK_ACCEL_VISIBLE);
1155        }
1156    }
1157 
1158  return TRUE;
1159}
1160
1161static gboolean
1162gtk_menu_motion_notify  (GtkWidget         *widget,
1163                         GdkEventMotion    *event)
1164{
1165  GtkWidget *menu_item;
1166  GtkMenu *menu;
1167  GdkRegion *navigation_region;
1168  GtkMenuShell *menu_shell;
1169
1170  gboolean need_enter;
1171
1172  /* We received the event for one of two reasons:
1173   *
1174   * a) We are the active menu, and did gtk_grab_add()
1175   * b) The widget is a child of ours, and the event was propagated
1176   *
1177   * Since for computation of navigation regions, we want the menu which
1178   * is the parent of the menu item, for a), we need to find that menu,
1179   * which may be different from 'widget'.
1180   */
1181 
1182  menu_item = gtk_get_event_widget ((GdkEvent*) event);
1183  if (!menu_item || !GTK_IS_MENU_ITEM (menu_item) || !GTK_WIDGET_IS_SENSITIVE (menu_item) ||
1184      !GTK_IS_MENU (menu_item->parent))
1185    return FALSE;
1186
1187  menu_shell = GTK_MENU_SHELL (menu_item->parent);
1188  menu = GTK_MENU (menu_shell);
1189
1190  navigation_region = gtk_menu_get_navigation_region (menu);
1191 
1192  need_enter = (navigation_region != NULL || menu_shell->ignore_enter);
1193
1194  /* Check to see if we are within an active submenu's navigation region
1195   */
1196  if (gtk_menu_navigating_submenu (menu, event->x_root, event->y_root))
1197    return TRUE;
1198
1199  if (need_enter)
1200    {
1201      /* The menu is now sensitive to enter events on its items, but
1202       * was previously sensitive.  So we fake an enter event.
1203       */
1204      gint width, height;
1205     
1206      menu_shell->ignore_enter = FALSE;
1207     
1208      gdk_window_get_size (event->window, &width, &height);
1209      if (event->x >= 0 && event->x < width &&
1210          event->y >= 0 && event->y < height)
1211        {
1212          GdkEvent send_event;
1213         
1214          send_event.crossing.type = GDK_ENTER_NOTIFY;
1215          send_event.crossing.window = event->window;
1216          send_event.crossing.time = event->time;
1217          send_event.crossing.send_event = TRUE;
1218          send_event.crossing.x_root = event->x_root;
1219          send_event.crossing.y_root = event->y_root;
1220          send_event.crossing.x = event->x;
1221          send_event.crossing.y = event->y;
1222
1223          /* We send the event to 'widget', the currently active menu,
1224           * instead of 'menu', the menu that the pointer is in. This
1225           * will ensure that the event will be ignored unless the
1226           * menuitem is a child of the active menu or some parent
1227           * menu of the active menu.
1228           */
1229          return gtk_widget_event (widget, &send_event);
1230        }
1231    }
1232
1233  return FALSE;
1234}
1235
1236static gboolean
1237gtk_menu_enter_notify (GtkWidget        *widget,
1238                       GdkEventCrossing *event)
1239{
1240  GtkWidget *menu_item;
1241
1242  /* If this is a faked enter (see gtk_menu_motion_notify), 'widget'
1243   * will not correspond to the event widget's parent.  Check to see
1244   * if we are in the parent's navigation region.
1245   */
1246  menu_item = gtk_get_event_widget ((GdkEvent*) event);
1247  if (menu_item && GTK_IS_MENU_ITEM (menu_item) && GTK_IS_MENU (menu_item->parent) &&
1248      gtk_menu_navigating_submenu (GTK_MENU (menu_item->parent), event->x_root, event->y_root))
1249    return TRUE;
1250
1251  return GTK_WIDGET_CLASS (parent_class)->enter_notify_event (widget, event);
1252}
1253
1254static gboolean
1255gtk_menu_leave_notify (GtkWidget        *widget,
1256                       GdkEventCrossing *event)
1257{
1258  GtkMenuShell *menu_shell;
1259  GtkMenu *menu;
1260  GtkMenuItem *menu_item;
1261  GtkWidget *event_widget;
1262
1263  menu = GTK_MENU (widget);
1264  menu_shell = GTK_MENU_SHELL (widget);
1265 
1266  if (gtk_menu_navigating_submenu (menu, event->x_root, event->y_root))
1267    return TRUE;
1268 
1269  event_widget = gtk_get_event_widget ((GdkEvent*) event);
1270 
1271  if (!event_widget || !GTK_IS_MENU_ITEM (event_widget))
1272    return TRUE;
1273 
1274  menu_item = GTK_MENU_ITEM (event_widget);
1275 
1276  /* Here we check to see if we're leaving an active menu item with a submenu,
1277   * in which case we enter submenu navigation mode.
1278   */
1279  if (menu_shell->active_menu_item != NULL
1280      && menu_item->submenu != NULL
1281      && menu_item->submenu_placement == GTK_LEFT_RIGHT)
1282    {
1283      if (menu_item->submenu->window != NULL)
1284        {
1285          gtk_menu_set_submenu_navigation_region (menu, menu_item, event);
1286          return TRUE;
1287        }
1288    }
1289 
1290  return GTK_WIDGET_CLASS (parent_class)->leave_notify_event (widget, event);
1291}
1292
1293static void
1294gtk_menu_stop_navigating_submenu (GtkMenu *menu)
1295{
1296  GdkRegion *navigation_region;
1297  guint navigation_timeout;
1298
1299  navigation_region = gtk_menu_get_navigation_region (menu);
1300  navigation_timeout = gtk_menu_get_navigation_timeout (menu);
1301
1302  if (navigation_region)
1303    {
1304      gdk_region_destroy (navigation_region);
1305      gtk_menu_set_navigation_region (menu, NULL);
1306    }
1307 
1308  if (navigation_timeout)
1309    {
1310      gtk_timeout_remove (navigation_timeout);
1311      gtk_menu_set_navigation_timeout (menu, 0);
1312    }
1313}
1314
1315/* When the timeout is elapsed, the navigation region is destroyed
1316 * and the menuitem under the pointer (if any) is selected.
1317 */
1318static gboolean
1319gtk_menu_stop_navigating_submenu_cb (gpointer user_data)
1320{
1321  GdkEventCrossing send_event;
1322
1323  GtkMenu *menu = user_data;
1324  GdkWindow *child_window;
1325
1326  gtk_menu_stop_navigating_submenu (menu);
1327 
1328  if (GTK_WIDGET_REALIZED (menu))
1329    {
1330      child_window = gdk_window_get_pointer (GTK_WIDGET (menu)->window, NULL, NULL, NULL);
1331
1332      if (child_window)
1333        {
1334          send_event.window = child_window;
1335          send_event.type = GDK_ENTER_NOTIFY;
1336          send_event.time = GDK_CURRENT_TIME; /* Bogus */
1337          send_event.send_event = TRUE;
1338
1339          GTK_WIDGET_CLASS (parent_class)->enter_notify_event (GTK_WIDGET (menu), &send_event);
1340        }
1341    }
1342
1343  return FALSE;
1344}
1345
1346static gboolean
1347gtk_menu_navigating_submenu (GtkMenu *menu,
1348                             gint     event_x,
1349                             gint     event_y)
1350{
1351  GdkRegion *navigation_region;
1352
1353  navigation_region = gtk_menu_get_navigation_region (menu);
1354
1355  if (navigation_region)
1356    {
1357      if (gdk_region_point_in (navigation_region, event_x, event_y))
1358        return TRUE;
1359      else
1360        {
1361          gtk_menu_stop_navigating_submenu (menu);
1362          return FALSE;
1363        }
1364    }
1365  return FALSE;
1366}
1367
1368static void
1369gtk_menu_set_submenu_navigation_region (GtkMenu          *menu,
1370                                        GtkMenuItem      *menu_item,
1371                                        GdkEventCrossing *event)
1372{
1373  gint submenu_left = 0;
1374  gint submenu_right = 0;
1375  gint submenu_top = 0;
1376  gint submenu_bottom = 0;
1377  gint width = 0;
1378  gint height = 0;
1379  GdkPoint point[3];
1380  GtkWidget *event_widget;
1381  GdkRegion *navigation_region;
1382  guint navigation_timeout;
1383
1384  g_return_if_fail (menu_item->submenu != NULL);
1385  g_return_if_fail (event != NULL);
1386 
1387  event_widget = gtk_get_event_widget ((GdkEvent*) event);
1388 
1389  gdk_window_get_origin (menu_item->submenu->window, &submenu_left, &submenu_top);
1390  gdk_window_get_size (menu_item->submenu->window, &width, &height);
1391  submenu_right = submenu_left + width;
1392  submenu_bottom = submenu_top + height;
1393 
1394  gdk_window_get_size (event_widget->window, &width, &height);
1395 
1396  if (event->x >= 0 && event->x < width)
1397    {
1398      /* Set navigation region */
1399      /* We fudge/give a little padding in case the user
1400       * ``misses the vertex'' of the triangle/is off by a pixel or two.
1401       */
1402      if (menu_item->submenu_direction == GTK_DIRECTION_RIGHT)
1403        point[0].x = event->x_root - SUBMENU_NAV_REGION_PADDING;
1404      else                             
1405        point[0].x = event->x_root + SUBMENU_NAV_REGION_PADDING; 
1406     
1407      /* Exiting the top or bottom? */
1408      if (event->y < 0)
1409        { /* top */
1410          point[0].y = event->y_root + SUBMENU_NAV_REGION_PADDING;
1411          point[1].y = submenu_top;
1412
1413          if (point[0].y <= point[1].y)
1414            return;
1415        }
1416      else
1417        { /* bottom */
1418          point[0].y = event->y_root - SUBMENU_NAV_REGION_PADDING;
1419          point[1].y = submenu_bottom;
1420
1421          if (point[0].y >= point[1].y)
1422            return;
1423        }
1424     
1425      /* Submenu is to the left or right? */
1426      if (menu_item->submenu_direction == GTK_DIRECTION_RIGHT)
1427        point[1].x = submenu_left;  /* right */
1428      else
1429        point[1].x = submenu_right; /* left */
1430     
1431      point[2].x = point[1].x;
1432      point[2].y = point[0].y;
1433
1434      gtk_menu_stop_navigating_submenu (menu);
1435     
1436      navigation_region = gdk_region_polygon (point, 3, GDK_WINDING_RULE);
1437
1438      gtk_menu_set_navigation_region (menu, navigation_region);
1439
1440      navigation_timeout = gtk_timeout_add (SUBMENU_NAV_HYSTERESIS_TIMEOUT,
1441                                            gtk_menu_stop_navigating_submenu_cb, menu);
1442
1443      gtk_menu_set_navigation_timeout (menu, navigation_timeout);
1444    }
1445}
1446
1447static GdkRegion *
1448gtk_menu_get_navigation_region (GtkMenu *menu)
1449{
1450  GdkRegion *region;
1451
1452  g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
1453
1454  if (!navigation_region_key_id)
1455    navigation_region_key_id = g_quark_from_static_string (navigation_region_key);
1456
1457  region = gtk_object_get_data_by_id (GTK_OBJECT (menu),
1458                                      navigation_region_key_id);
1459
1460  return region;
1461}
1462
1463static void
1464gtk_menu_set_navigation_region (GtkMenu   *menu,
1465                                GdkRegion *region)
1466{
1467  g_return_if_fail (GTK_IS_MENU (menu));
1468
1469  if (!navigation_region_key_id)
1470    navigation_region_key_id = g_quark_from_static_string (navigation_region_key);
1471
1472  gtk_object_set_data_by_id (GTK_OBJECT (menu),
1473                             navigation_region_key_id,
1474                             region);
1475}
1476
1477static guint
1478gtk_menu_get_navigation_timeout (GtkMenu *menu)
1479{
1480  gpointer data;
1481
1482  g_return_val_if_fail (GTK_IS_MENU (menu), 0);
1483
1484  if (!navigation_timeout_key_id)
1485    navigation_timeout_key_id = g_quark_from_static_string (navigation_timeout_key);
1486
1487  data = gtk_object_get_data_by_id (GTK_OBJECT (menu),
1488                                    navigation_timeout_key_id);
1489
1490  return GPOINTER_TO_UINT (data);
1491}
1492
1493static void
1494gtk_menu_set_navigation_timeout (GtkMenu *menu,
1495                                 guint    timeout)
1496{
1497  gpointer data;
1498
1499  g_return_if_fail (GTK_IS_MENU (menu));
1500
1501  if (!navigation_timeout_key_id)
1502    navigation_timeout_key_id = g_quark_from_static_string (navigation_timeout_key);
1503
1504  data = GUINT_TO_POINTER (timeout);
1505
1506  gtk_object_set_data_by_id (GTK_OBJECT (menu),
1507                             navigation_timeout_key_id,
1508                             data);
1509}
1510
1511static void
1512gtk_menu_deactivate (GtkMenuShell *menu_shell)
1513{
1514  GtkWidget *parent;
1515 
1516  g_return_if_fail (menu_shell != NULL);
1517  g_return_if_fail (GTK_IS_MENU (menu_shell));
1518 
1519  parent = menu_shell->parent_menu_shell;
1520 
1521  menu_shell->activate_time = 0;
1522  gtk_menu_popdown (GTK_MENU (menu_shell));
1523 
1524  if (parent)
1525    gtk_menu_shell_deactivate (GTK_MENU_SHELL (parent));
1526}
1527
1528
1529static void
1530gtk_menu_position (GtkMenu *menu)
1531{
1532  GtkWidget *widget;
1533  GtkRequisition requisition;
1534  gint x, y;
1535 
1536  g_return_if_fail (menu != NULL);
1537  g_return_if_fail (GTK_IS_MENU (menu));
1538
1539  widget = GTK_WIDGET (menu);
1540
1541  gdk_window_get_pointer (NULL, &x, &y, NULL);
1542
1543  /* We need the requisition to figure out the right place to
1544   * popup the menu. In fact, we always need to ask here, since
1545   * if one a size_request was queued while we weren't popped up,
1546   * the requisition won't have been recomputed yet.
1547   */
1548  gtk_widget_size_request (widget, &requisition);
1549     
1550  if (menu->position_func)
1551    (* menu->position_func) (menu, &x, &y, menu->position_func_data);
1552  else
1553    {
1554      gint screen_width;
1555      gint screen_height;
1556     
1557      screen_width = gdk_screen_width ();
1558      screen_height = gdk_screen_height ();
1559         
1560      x = CLAMP (x - 2, 0, MAX (0, screen_width - requisition.width));
1561      y = CLAMP (y - 2, 0, MAX (0, screen_height - requisition.height));
1562    }
1563
1564  /* FIXME: The MAX() here is because gtk_widget_set_uposition
1565   * is broken. Once we provide an alternate interface that
1566   * allows negative values, then we can remove them.
1567   */
1568  gtk_widget_set_uposition (GTK_MENU_SHELL (menu)->active ?
1569                                menu->toplevel : menu->tearoff_window,
1570                            MAX (x, 0), MAX (y, 0));
1571}
1572
1573/* Reparent the menu, taking care of the refcounting
1574 */
1575static void
1576gtk_menu_reparent (GtkMenu      *menu,
1577                   GtkWidget    *new_parent,
1578                   gboolean      unrealize)
1579{
1580  GtkObject *object = GTK_OBJECT (menu);
1581  GtkWidget *widget = GTK_WIDGET (menu);
1582  gboolean was_floating = GTK_OBJECT_FLOATING (object);
1583
1584  gtk_object_ref (object);
1585  gtk_object_sink (object);
1586
1587  if (unrealize)
1588    {
1589      gtk_object_ref (object);
1590      gtk_container_remove (GTK_CONTAINER (widget->parent), widget);
1591      gtk_container_add (GTK_CONTAINER (new_parent), widget);
1592      gtk_object_unref (object);
1593    }
1594  else
1595    gtk_widget_reparent (GTK_WIDGET (menu), new_parent);
1596  gtk_widget_set_usize (new_parent, -1, -1);
1597 
1598  if (was_floating)
1599    GTK_OBJECT_SET_FLAGS (object, GTK_FLOATING);
1600  else
1601    gtk_object_unref (object);
1602}
1603
1604static void
1605gtk_menu_show_all (GtkWidget *widget)
1606{
1607  g_return_if_fail (widget != NULL);
1608  g_return_if_fail (GTK_IS_MENU (widget));
1609
1610  /* Show children, but not self. */
1611  gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_show_all, NULL);
1612}
1613
1614
1615static void
1616gtk_menu_hide_all (GtkWidget *widget)
1617{
1618  g_return_if_fail (widget != NULL);
1619  g_return_if_fail (GTK_IS_MENU (widget));
1620
1621  /* Hide children, but not self. */
1622  gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_hide_all, NULL);
1623}
Note: See TracBrowser for help on using the repository browser.