]> Pileus Git - ~andy/gtk/blob - gtk/gtkmenu.c
New function gtk_menu_reorder_child() from Tim Janik.
[~andy/gtk] / gtk / gtkmenu.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 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 typedef struct _GtkMenuAttachData       GtkMenuAttachData;
42
43 struct _GtkMenuAttachData
44 {
45   GtkWidget *attach_widget;
46   GtkMenuDetachFunc detacher;
47 };
48
49
50 static void gtk_menu_class_init     (GtkMenuClass      *klass);
51 static void gtk_menu_init           (GtkMenu           *menu);
52 static void gtk_menu_destroy        (GtkObject         *object);
53 static void gtk_menu_realize        (GtkWidget         *widget);
54 static void gtk_menu_size_request   (GtkWidget         *widget,
55                                      GtkRequisition    *requisition);
56 static void gtk_menu_size_allocate  (GtkWidget         *widget,
57                                      GtkAllocation     *allocation);
58 static void gtk_menu_paint          (GtkWidget         *widget);
59 static void gtk_menu_draw           (GtkWidget         *widget,
60                                      GdkRectangle      *area);
61 static gint gtk_menu_expose         (GtkWidget         *widget,
62                                      GdkEventExpose    *event);
63 static gint gtk_menu_key_press      (GtkWidget         *widget,
64                                      GdkEventKey       *event);
65 static gint gtk_menu_motion_notify  (GtkWidget         *widget,
66                                      GdkEventMotion    *event);
67 static void gtk_menu_deactivate     (GtkMenuShell      *menu_shell);
68 static void gtk_menu_show_all       (GtkWidget         *widget);
69 static void gtk_menu_hide_all       (GtkWidget         *widget);
70 static void gtk_menu_position       (GtkMenu           *menu);
71 static void gtk_menu_reparent       (GtkMenu           *menu, 
72                                      GtkWidget         *new_parent, 
73                                      gboolean           unrealize);
74
75 static GtkMenuShellClass *parent_class = NULL;
76 static const gchar      *attach_data_key = "gtk-menu-attach-data";
77
78
79 GtkType
80 gtk_menu_get_type (void)
81 {
82   static GtkType menu_type = 0;
83   
84   if (!menu_type)
85     {
86       static const GtkTypeInfo menu_info =
87       {
88         "GtkMenu",
89         sizeof (GtkMenu),
90         sizeof (GtkMenuClass),
91         (GtkClassInitFunc) gtk_menu_class_init,
92         (GtkObjectInitFunc) gtk_menu_init,
93         /* reserved_1 */ NULL,
94         /* reserved_2 */ NULL,
95         (GtkClassInitFunc) NULL,
96       };
97       
98       menu_type = gtk_type_unique (gtk_menu_shell_get_type (), &menu_info);
99     }
100   
101   return menu_type;
102 }
103
104 static void
105 gtk_menu_class_init (GtkMenuClass *class)
106 {
107   GtkObjectClass *object_class;
108   GtkWidgetClass *widget_class;
109   GtkContainerClass *container_class;
110   GtkMenuShellClass *menu_shell_class;
111
112   GtkBindingSet *binding_set;
113   
114   object_class = (GtkObjectClass*) class;
115   widget_class = (GtkWidgetClass*) class;
116   container_class = (GtkContainerClass*) class;
117   menu_shell_class = (GtkMenuShellClass*) class;
118   parent_class = gtk_type_class (gtk_menu_shell_get_type ());
119   
120   object_class->destroy = gtk_menu_destroy;
121   
122   widget_class->realize = gtk_menu_realize;
123   widget_class->draw = gtk_menu_draw;
124   widget_class->size_request = gtk_menu_size_request;
125   widget_class->size_allocate = gtk_menu_size_allocate;
126   widget_class->expose_event = gtk_menu_expose;
127   widget_class->key_press_event = gtk_menu_key_press;
128   widget_class->motion_notify_event = gtk_menu_motion_notify;
129   widget_class->show_all = gtk_menu_show_all;
130   widget_class->hide_all = gtk_menu_hide_all;
131   
132   menu_shell_class->submenu_placement = GTK_LEFT_RIGHT;
133   menu_shell_class->deactivate = gtk_menu_deactivate;
134
135   binding_set = gtk_binding_set_by_class (class);
136   gtk_binding_entry_add_signal (binding_set,
137                                 GDK_Up, 0,
138                                 "move_current", 1,
139                                 GTK_TYPE_MENU_DIRECTION_TYPE,
140                                 GTK_MENU_DIR_PREV);
141   gtk_binding_entry_add_signal (binding_set,
142                                 GDK_Down, 0,
143                                 "move_current", 1,
144                                 GTK_TYPE_MENU_DIRECTION_TYPE,
145                                 GTK_MENU_DIR_NEXT);
146   gtk_binding_entry_add_signal (binding_set,
147                                 GDK_Left, 0,
148                                 "move_current", 1,
149                                 GTK_TYPE_MENU_DIRECTION_TYPE,
150                                 GTK_MENU_DIR_PARENT);
151   gtk_binding_entry_add_signal (binding_set,
152                                 GDK_Right, 0,
153                                 "move_current", 1,
154                                 GTK_TYPE_MENU_DIRECTION_TYPE,
155                                 GTK_MENU_DIR_CHILD);
156 }
157
158 static void
159 gtk_menu_init (GtkMenu *menu)
160 {
161   menu->parent_menu_item = NULL;
162   menu->old_active_menu_item = NULL;
163   menu->accel_group = NULL;
164   menu->position_func = NULL;
165   menu->position_func_data = NULL;
166
167   menu->toplevel = gtk_window_new (GTK_WINDOW_POPUP);
168   gtk_signal_connect_object (GTK_OBJECT (menu->toplevel),  "key_press_event",
169                              GTK_SIGNAL_FUNC (gtk_menu_key_press), 
170                              GTK_OBJECT (menu));
171   gtk_window_set_policy (GTK_WINDOW (menu->toplevel),
172                          FALSE, FALSE, TRUE);
173
174   gtk_container_add (GTK_CONTAINER (menu->toplevel), GTK_WIDGET (menu));
175
176   /* Refloat the menu, so that reference counting for the menu isn't
177    * affected by it being a child of the toplevel
178    */
179   GTK_WIDGET_SET_FLAGS (menu, GTK_FLOATING);
180
181   menu->tearoff_window = NULL;
182   menu->torn_off = FALSE;
183
184   MENU_NEEDS_RESIZE (menu) = TRUE;
185 }
186
187 static void
188 gtk_menu_destroy (GtkObject         *object)
189 {
190   GtkMenu *menu;
191   GtkMenuAttachData *data;
192   
193   g_return_if_fail (object != NULL);
194   g_return_if_fail (GTK_IS_MENU (object));
195
196   menu = GTK_MENU (object);
197   
198   gtk_object_ref (object);
199   
200   data = gtk_object_get_data (object, attach_data_key);
201   if (data)
202     gtk_menu_detach (menu);
203   
204   gtk_menu_set_accel_group (menu, NULL);
205
206   /* Add back the reference count for being a child */
207   gtk_object_ref (object);
208   
209   gtk_widget_destroy (menu->toplevel);
210   if (menu->tearoff_window)
211     gtk_widget_destroy (menu->tearoff_window);
212
213   if (GTK_OBJECT_CLASS (parent_class)->destroy)
214     (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
215   
216   gtk_object_unref (object);
217 }
218
219
220 void
221 gtk_menu_attach_to_widget (GtkMenu             *menu,
222                            GtkWidget           *attach_widget,
223                            GtkMenuDetachFunc    detacher)
224 {
225   GtkMenuAttachData *data;
226   
227   g_return_if_fail (menu != NULL);
228   g_return_if_fail (GTK_IS_MENU (menu));
229   g_return_if_fail (attach_widget != NULL);
230   g_return_if_fail (GTK_IS_WIDGET (attach_widget));
231   g_return_if_fail (detacher != NULL);
232   
233   /* keep this function in sync with gtk_widget_set_parent()
234    */
235   
236   data = gtk_object_get_data (GTK_OBJECT (menu), attach_data_key);
237   if (data)
238     {
239       g_warning ("gtk_menu_attach_to_widget(): menu already attached to %s",
240                  gtk_type_name (GTK_OBJECT_TYPE (data->attach_widget)));
241       return;
242     }
243   
244   gtk_object_ref (GTK_OBJECT (menu));
245   gtk_object_sink (GTK_OBJECT (menu));
246   
247   data = g_new (GtkMenuAttachData, 1);
248   data->attach_widget = attach_widget;
249   data->detacher = detacher;
250   gtk_object_set_data (GTK_OBJECT (menu), attach_data_key, data);
251   
252   if (GTK_WIDGET_STATE (menu) != GTK_STATE_NORMAL)
253     gtk_widget_set_state (GTK_WIDGET (menu), GTK_STATE_NORMAL);
254   
255   /* we don't need to set the style here, since
256    * we are a toplevel widget.
257    */
258 }
259
260 GtkWidget*
261 gtk_menu_get_attach_widget (GtkMenu             *menu)
262 {
263   GtkMenuAttachData *data;
264   
265   g_return_val_if_fail (menu != NULL, NULL);
266   g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
267   
268   data = gtk_object_get_data (GTK_OBJECT (menu), attach_data_key);
269   if (data)
270     return data->attach_widget;
271   return NULL;
272 }
273
274 void
275 gtk_menu_detach (GtkMenu             *menu)
276 {
277   GtkMenuAttachData *data;
278   
279   g_return_if_fail (menu != NULL);
280   g_return_if_fail (GTK_IS_MENU (menu));
281   
282   /* keep this function in sync with gtk_widget_unparent()
283    */
284   data = gtk_object_get_data (GTK_OBJECT (menu), attach_data_key);
285   if (!data)
286     {
287       g_warning ("gtk_menu_detach(): menu is not attached");
288       return;
289     }
290   gtk_object_remove_data (GTK_OBJECT (menu), attach_data_key);
291   
292   data->detacher (data->attach_widget, menu);
293   
294   if (GTK_WIDGET_REALIZED (menu))
295     gtk_widget_unrealize (GTK_WIDGET (menu));
296   
297   g_free (data);
298   
299   gtk_widget_unref (GTK_WIDGET (menu));
300 }
301
302 GtkWidget*
303 gtk_menu_new (void)
304 {
305   return GTK_WIDGET (gtk_type_new (gtk_menu_get_type ()));
306 }
307
308 void
309 gtk_menu_append (GtkMenu   *menu,
310                  GtkWidget *child)
311 {
312   gtk_menu_shell_append (GTK_MENU_SHELL (menu), child);
313 }
314
315 void
316 gtk_menu_prepend (GtkMenu   *menu,
317                   GtkWidget *child)
318 {
319   gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), child);
320 }
321
322 void
323 gtk_menu_insert (GtkMenu   *menu,
324                  GtkWidget *child,
325                  gint       position)
326 {
327   gtk_menu_shell_insert (GTK_MENU_SHELL (menu), child, position);
328 }
329
330 void
331 gtk_menu_popup (GtkMenu             *menu,
332                 GtkWidget           *parent_menu_shell,
333                 GtkWidget           *parent_menu_item,
334                 GtkMenuPositionFunc  func,
335                 gpointer             data,
336                 guint                button,
337                 guint32              activate_time)
338 {
339   GtkWidget *widget;
340   GtkWidget *xgrab_shell;
341   GtkWidget *parent;
342   GdkEvent *current_event;
343   GtkMenuShell *menu_shell;
344   
345   g_return_if_fail (menu != NULL);
346   g_return_if_fail (GTK_IS_MENU (menu));
347   
348   widget = GTK_WIDGET (menu);
349   menu_shell = GTK_MENU_SHELL (menu);
350   
351   menu_shell->parent_menu_shell = parent_menu_shell;
352   menu_shell->active = TRUE;
353   menu_shell->button = button;
354
355   /* If we are popping up the menu from something other than, a button
356    * press then, as a heuristic, we ignore enter events for the menu
357    * until we get a MOTION_NOTIFY.  
358    */
359
360   current_event = gtk_get_current_event();
361   if (current_event)
362     {
363       if ((current_event->type != GDK_BUTTON_PRESS) &&
364           (current_event->type != GDK_ENTER_NOTIFY))
365         menu_shell->ignore_enter = TRUE;
366       gdk_event_free(current_event);
367     }
368
369   if (menu->torn_off)
370     {
371       GdkPixmap *pixmap;
372       GdkGC *gc;
373       GdkGCValues gc_values;
374       
375       gc_values.subwindow_mode = GDK_INCLUDE_INFERIORS;
376       gc = gdk_gc_new_with_values (widget->window,
377                                    &gc_values, GDK_GC_SUBWINDOW);
378       
379       pixmap = gdk_pixmap_new (widget->window,
380                                widget->requisition.width,
381                                widget->requisition.height,
382                                -1);
383       
384       gdk_draw_pixmap (pixmap, gc,
385                        widget->window,
386                        0, 0, 0, 0, -1, -1);
387       gdk_gc_unref(gc);
388
389       gtk_widget_set_usize (menu->tearoff_window,
390                             widget->requisition.width,
391                             widget->requisition.height);
392                     
393       gdk_window_set_back_pixmap (menu->tearoff_window->window, pixmap, FALSE);
394       gdk_pixmap_unref (pixmap);
395
396       /* We force an unrealize here so that we don't trigger redrawing/
397        * clearing code - we just want to reveal our backing pixmap.
398        */
399       gtk_menu_reparent (menu, menu->toplevel, TRUE);
400     }
401   
402   menu->parent_menu_item = parent_menu_item;
403   menu->position_func = func;
404   menu->position_func_data = data;
405   menu_shell->activate_time = activate_time;
406
407   gtk_menu_position (menu);
408
409   /* We need to show the menu _here_ because code expects to be
410    * able to tell if the menu is onscreen by looking at the
411    * GTK_WIDGET_VISIBLE (menu)
412    */
413   gtk_widget_show (GTK_WIDGET (menu));
414   gtk_widget_show (menu->toplevel);
415   
416   /* Find the last viewable ancestor, and make an X grab on it
417    */
418   parent = GTK_WIDGET (menu);
419   xgrab_shell = NULL;
420   while (parent)
421     {
422       gboolean viewable = TRUE;
423       GtkWidget *tmp = parent;
424       
425       while (tmp)
426         {
427           if (!GTK_WIDGET_MAPPED (tmp))
428             {
429               viewable = FALSE;
430               break;
431             }
432           tmp = tmp->parent;
433         }
434       
435       if (viewable)
436         xgrab_shell = parent;
437       
438       parent = GTK_MENU_SHELL (parent)->parent_menu_shell;
439     }
440   
441   if (xgrab_shell && (!GTK_MENU_SHELL (xgrab_shell)->have_xgrab))
442     {
443       GdkCursor *cursor = gdk_cursor_new (GDK_ARROW);
444
445       if ((gdk_pointer_grab (xgrab_shell->window, TRUE,
446                              GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
447                              GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK |
448                              GDK_POINTER_MOTION_MASK,
449                              NULL, cursor, activate_time) == 0))
450         {
451           if (gdk_keyboard_grab (xgrab_shell->window, TRUE,
452                               activate_time) == 0)
453             GTK_MENU_SHELL (xgrab_shell)->have_xgrab = TRUE;
454           else
455             {
456               gdk_pointer_ungrab (activate_time);
457             }
458         }
459
460       gdk_cursor_destroy (cursor);
461     }
462   
463   gtk_grab_add (GTK_WIDGET (menu));
464 }
465
466 void
467 gtk_menu_popdown (GtkMenu *menu)
468 {
469   GtkMenuShell *menu_shell;
470   
471   g_return_if_fail (menu != NULL);
472   g_return_if_fail (GTK_IS_MENU (menu));
473   
474   menu_shell = GTK_MENU_SHELL (menu);
475   
476   menu_shell->parent_menu_shell = NULL;
477   menu_shell->active = FALSE;
478   menu_shell->ignore_enter = FALSE;
479   
480   if (menu_shell->active_menu_item)
481     {
482       menu->old_active_menu_item = menu_shell->active_menu_item;
483       gtk_menu_item_deselect (GTK_MENU_ITEM (menu_shell->active_menu_item));
484       menu_shell->active_menu_item = NULL;
485     }
486   
487   /* The X Grab, if present, will automatically be removed when we hide
488    * the window */
489   gtk_widget_hide (menu->toplevel);
490
491   if (menu->torn_off)
492     {
493       if (GTK_BIN (menu->toplevel)->child) 
494         {
495           gtk_menu_reparent (menu, menu->tearoff_window, FALSE);
496         } 
497       else
498         {
499           /* We popped up the menu from the tearoff, so we need to 
500            * release the grab - we aren't actually hiding the menu.
501            */
502           if (menu_shell->have_xgrab)
503             {
504               gdk_pointer_ungrab (GDK_CURRENT_TIME);
505               gdk_keyboard_ungrab (GDK_CURRENT_TIME);
506             }
507         }
508     }
509   else
510     gtk_widget_hide (GTK_WIDGET (menu));
511         
512   menu_shell->have_xgrab = FALSE;
513   gtk_grab_remove (GTK_WIDGET (menu));
514 }
515
516 GtkWidget*
517 gtk_menu_get_active (GtkMenu *menu)
518 {
519   GtkWidget *child;
520   GList *children;
521   
522   g_return_val_if_fail (menu != NULL, NULL);
523   g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
524   
525   if (!menu->old_active_menu_item)
526     {
527       child = NULL;
528       children = GTK_MENU_SHELL (menu)->children;
529       
530       while (children)
531         {
532           child = children->data;
533           children = children->next;
534           
535           if (GTK_BIN (child)->child)
536             break;
537           child = NULL;
538         }
539       
540       menu->old_active_menu_item = child;
541     }
542   
543   return menu->old_active_menu_item;
544 }
545
546 void
547 gtk_menu_set_active (GtkMenu *menu,
548                      guint    index)
549 {
550   GtkWidget *child;
551   GList *tmp_list;
552   
553   g_return_if_fail (menu != NULL);
554   g_return_if_fail (GTK_IS_MENU (menu));
555   
556   tmp_list = g_list_nth (GTK_MENU_SHELL (menu)->children, index);
557   if (tmp_list)
558     {
559       child = tmp_list->data;
560       if (GTK_BIN (child)->child)
561         menu->old_active_menu_item = child;
562     }
563 }
564
565 void
566 gtk_menu_set_accel_group (GtkMenu       *menu,
567                           GtkAccelGroup *accel_group)
568 {
569   g_return_if_fail (menu != NULL);
570   g_return_if_fail (GTK_IS_MENU (menu));
571   
572   if (menu->accel_group != accel_group)
573     {
574       if (menu->accel_group)
575         gtk_accel_group_unref (menu->accel_group);
576       menu->accel_group = accel_group;
577       if (menu->accel_group)
578         gtk_accel_group_ref (menu->accel_group);
579     }
580 }
581
582
583 void
584 gtk_menu_reposition (GtkMenu *menu)
585 {
586   g_return_if_fail (menu != NULL);
587   g_return_if_fail (GTK_IS_MENU (menu));
588
589   if (GTK_WIDGET_DRAWABLE (menu) && !menu->torn_off)
590     gtk_menu_position (menu);
591 }
592
593
594 void       
595 gtk_menu_set_tearoff_state (GtkMenu  *menu,
596                             gboolean  torn_off)
597 {
598   g_return_if_fail (menu != NULL);
599   g_return_if_fail (GTK_IS_MENU (menu));
600
601   if (menu->torn_off != torn_off)
602     {
603       menu->torn_off = torn_off;
604       
605       if (menu->torn_off)
606         {
607           if (GTK_WIDGET_VISIBLE (menu))
608             gtk_menu_popdown (menu);
609
610           if (!menu->tearoff_window)
611             {
612               GtkWidget *attach_widget;
613               gchar *title;
614               
615               menu->tearoff_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
616               gtk_widget_set_app_paintable (menu->tearoff_window, TRUE);
617               gtk_signal_connect_object (GTK_OBJECT (menu->tearoff_window),  
618                                          "key_press_event",
619                                          GTK_SIGNAL_FUNC (gtk_menu_key_press), 
620                                          GTK_OBJECT (menu));
621               gtk_widget_realize (menu->tearoff_window);
622               
623               title = gtk_object_get_data (GTK_OBJECT (menu), "gtk-menu-title");
624               if (!title)
625                 {
626                   attach_widget = gtk_menu_get_attach_widget (menu);
627                   if (GTK_IS_MENU_ITEM (attach_widget))
628                     {
629                       GtkWidget *child = GTK_BIN (attach_widget)->child;
630                       if (GTK_IS_LABEL (child))
631                         gtk_label_get (GTK_LABEL (child), &title);
632                     }
633                 }
634
635               if (title)
636                 gdk_window_set_title (menu->tearoff_window->window, title);
637
638               gdk_window_set_decorations (menu->tearoff_window->window, 
639                                           GDK_DECOR_ALL |
640                                           GDK_DECOR_RESIZEH |
641                                           GDK_DECOR_MINIMIZE |
642                                           GDK_DECOR_MAXIMIZE);
643               gtk_window_set_policy (GTK_WINDOW (menu->tearoff_window),
644                                      FALSE, FALSE, TRUE);
645             }
646           gtk_menu_reparent (menu, menu->tearoff_window, FALSE);
647
648           gtk_menu_position (menu);
649           
650           gtk_widget_show (GTK_WIDGET (menu));
651           gtk_widget_show (menu->tearoff_window);
652         }
653       else
654         {
655           gtk_widget_hide (menu->tearoff_window);
656           gtk_menu_reparent (menu, menu->toplevel, FALSE);
657         }
658     }
659 }
660
661 void       
662 gtk_menu_set_title (GtkMenu     *menu,
663                     const gchar *title)
664 {
665   g_return_if_fail (menu != NULL);
666   g_return_if_fail (GTK_IS_MENU (menu));
667
668   gtk_object_set_data_full (GTK_OBJECT (menu), "gtk-menu-title",
669                             g_strdup (title), (GtkDestroyNotify) g_free);
670 }
671
672 void
673 gtk_menu_reorder_child (GtkMenu   *menu,
674                         GtkWidget *child,
675                         gint       position)
676 {
677   GtkMenuShell *menu_shell;
678   g_return_if_fail (GTK_IS_MENU (menu));
679   g_return_if_fail (GTK_IS_MENU_ITEM (child));
680   menu_shell = GTK_MENU_SHELL (menu);
681   if (g_list_find (menu_shell->children, child))
682     {   
683       menu_shell->children = g_list_remove (menu_shell->children, child);
684       menu_shell->children = g_list_insert (menu_shell->children, child, position);   
685       if (GTK_WIDGET_VISIBLE (menu_shell))
686         gtk_widget_queue_resize (GTK_WIDGET (menu_shell));
687     }   
688 }
689
690 static void
691 gtk_menu_realize (GtkWidget *widget)
692 {
693   GdkWindowAttr attributes;
694   gint attributes_mask;
695   
696   g_return_if_fail (widget != NULL);
697   g_return_if_fail (GTK_IS_MENU (widget));
698   
699   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
700   
701   attributes.window_type = GDK_WINDOW_CHILD;
702   attributes.x = widget->allocation.x;
703   attributes.y = widget->allocation.y;
704   attributes.width = widget->allocation.width;
705   attributes.height = widget->allocation.height;
706   attributes.wclass = GDK_INPUT_OUTPUT;
707   attributes.visual = gtk_widget_get_visual (widget);
708   attributes.colormap = gtk_widget_get_colormap (widget);
709   attributes.event_mask = gtk_widget_get_events (widget);
710   attributes.event_mask |= (GDK_EXPOSURE_MASK | GDK_KEY_PRESS_MASK);
711   
712   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
713   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
714   gdk_window_set_user_data (widget->window, widget);
715   
716   widget->style = gtk_style_attach (widget->style, widget->window);
717   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
718   gtk_menu_paint(widget);
719 }
720
721 static void
722 gtk_menu_size_request (GtkWidget      *widget,
723                        GtkRequisition *requisition)
724 {
725   GtkMenu *menu;
726   GtkMenuShell *menu_shell;
727   GtkWidget *child;
728   GList *children;
729   guint max_toggle_size;
730   guint max_accel_width;
731   GtkRequisition child_requisition;
732   
733   g_return_if_fail (widget != NULL);
734   g_return_if_fail (GTK_IS_MENU (widget));
735   g_return_if_fail (requisition != NULL);
736   
737   menu = GTK_MENU (widget);
738   menu_shell = GTK_MENU_SHELL (widget);
739   
740   requisition->width = 0;
741   requisition->height = 0;
742   
743   max_toggle_size = 0;
744   max_accel_width = 0;
745   
746   children = menu_shell->children;
747   while (children)
748     {
749       child = children->data;
750       children = children->next;
751       
752       if (GTK_WIDGET_VISIBLE (child))
753         {
754           GTK_MENU_ITEM (child)->show_submenu_indicator = TRUE;
755           gtk_widget_size_request (child, &child_requisition);
756           
757           requisition->width = MAX (requisition->width, child_requisition.width);
758           requisition->height += child_requisition.height;
759           
760           max_toggle_size = MAX (max_toggle_size, MENU_ITEM_CLASS (child)->toggle_size);
761           max_accel_width = MAX (max_accel_width, GTK_MENU_ITEM (child)->accelerator_width);
762         }
763     }
764   
765   requisition->width += max_toggle_size + max_accel_width;
766   requisition->width += (GTK_CONTAINER (menu)->border_width +
767                          widget->style->klass->xthickness) * 2;
768   requisition->height += (GTK_CONTAINER (menu)->border_width +
769                           widget->style->klass->ythickness) * 2;
770   
771   children = menu_shell->children;
772   while (children)
773     {
774       child = children->data;
775       children = children->next;
776       
777       GTK_MENU_ITEM (child)->toggle_size = max_toggle_size;
778     }
779 }
780
781 static void
782 gtk_menu_size_allocate (GtkWidget     *widget,
783                         GtkAllocation *allocation)
784 {
785   GtkMenu *menu;
786   GtkMenuShell *menu_shell;
787   GtkWidget *child;
788   GtkAllocation child_allocation;
789   GList *children;
790   
791   g_return_if_fail (widget != NULL);
792   g_return_if_fail (GTK_IS_MENU (widget));
793   g_return_if_fail (allocation != NULL);
794   
795   menu = GTK_MENU (widget);
796   menu_shell = GTK_MENU_SHELL (widget);
797   
798   widget->allocation = *allocation;
799   if (GTK_WIDGET_REALIZED (widget))
800     gdk_window_move_resize (widget->window,
801                             allocation->x, allocation->y,
802                             allocation->width, allocation->height);
803
804
805   if (menu_shell->children)
806     {
807       child_allocation.x = (GTK_CONTAINER (menu)->border_width +
808                             widget->style->klass->xthickness);
809       child_allocation.y = (GTK_CONTAINER (menu)->border_width +
810                             widget->style->klass->ythickness);
811       child_allocation.width = MAX (1, (gint)allocation->width - child_allocation.x * 2);
812       
813       children = menu_shell->children;
814       while (children)
815         {
816           child = children->data;
817           children = children->next;
818           
819           if (GTK_WIDGET_VISIBLE (child))
820             {
821               GtkRequisition child_requisition;
822               gtk_widget_get_child_requisition (child, &child_requisition);
823               
824               child_allocation.height = child_requisition.height;
825               
826               gtk_widget_size_allocate (child, &child_allocation);
827               gtk_widget_queue_draw (child);
828               
829               child_allocation.y += child_allocation.height;
830             }
831         }
832     }
833 }
834
835 static void
836 gtk_menu_paint (GtkWidget *widget)
837 {
838   g_return_if_fail (widget != NULL);
839   g_return_if_fail (GTK_IS_MENU (widget));
840   
841   if (GTK_WIDGET_DRAWABLE (widget))
842     {
843       gtk_paint_box (widget->style,
844                      widget->window,
845                      GTK_STATE_NORMAL,
846                      GTK_SHADOW_OUT,
847                      NULL, widget, "menu",
848                      0, 0, -1, -1);
849     }
850 }
851
852 static void
853 gtk_menu_draw (GtkWidget    *widget,
854                GdkRectangle *area)
855 {
856   GtkMenuShell *menu_shell;
857   GtkWidget *child;
858   GdkRectangle child_area;
859   GList *children;
860   
861   g_return_if_fail (widget != NULL);
862   g_return_if_fail (GTK_IS_MENU (widget));
863   g_return_if_fail (area != NULL);
864   
865   if (GTK_WIDGET_DRAWABLE (widget))
866     {
867       gtk_menu_paint (widget);
868       
869       menu_shell = GTK_MENU_SHELL (widget);
870       
871       children = menu_shell->children;
872       while (children)
873         {
874           child = children->data;
875           children = children->next;
876           
877           if (gtk_widget_intersect (child, area, &child_area))
878             gtk_widget_draw (child, &child_area);
879         }
880     }
881 }
882
883 static gint
884 gtk_menu_expose (GtkWidget      *widget,
885                  GdkEventExpose *event)
886 {
887   GtkMenuShell *menu_shell;
888   GtkWidget *child;
889   GdkEventExpose child_event;
890   GList *children;
891   
892   g_return_val_if_fail (widget != NULL, FALSE);
893   g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
894   g_return_val_if_fail (event != NULL, FALSE);
895   
896   if (GTK_WIDGET_DRAWABLE (widget))
897     {
898       gtk_menu_paint (widget);
899       
900       menu_shell = GTK_MENU_SHELL (widget);
901       child_event = *event;
902       
903       children = menu_shell->children;
904       while (children)
905         {
906           child = children->data;
907           children = children->next;
908           
909           if (GTK_WIDGET_NO_WINDOW (child) &&
910               gtk_widget_intersect (child, &event->area, &child_event.area))
911             gtk_widget_event (child, (GdkEvent*) &child_event);
912         }
913     }
914   
915   return FALSE;
916 }
917
918 static gint
919 gtk_menu_key_press (GtkWidget   *widget,
920                     GdkEventKey *event)
921 {
922   GtkMenuShell *menu_shell;
923   gboolean delete = FALSE;
924   
925   g_return_val_if_fail (widget != NULL, FALSE);
926   g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
927   g_return_val_if_fail (event != NULL, FALSE);
928       
929   menu_shell = GTK_MENU_SHELL (widget);
930
931   if (GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event))
932     return TRUE;
933
934   switch (event->keyval)
935     {
936     case GDK_Delete:
937     case GDK_KP_Delete:
938     case GDK_BackSpace:
939       delete = TRUE;
940       break;
941     default:
942       break;
943     }
944
945   /* Modify the accelerators */
946   if (delete || gtk_accelerator_valid (event->keyval, event->keyval))
947     {
948       if (menu_shell->active_menu_item &&
949           GTK_BIN (menu_shell->active_menu_item)->child &&
950           GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu == NULL)
951         {
952           GtkMenuItem *menu_item;
953           GtkAccelGroup *accel_group;
954           
955           menu_item = GTK_MENU_ITEM (menu_shell->active_menu_item);
956           
957           if (!GTK_MENU (widget)->accel_group)
958             accel_group = gtk_accel_group_get_default ();
959           else
960             accel_group = GTK_MENU (widget)->accel_group;
961
962           gtk_widget_remove_accelerators (GTK_WIDGET (menu_item),
963                                           gtk_signal_name (menu_item->accelerator_signal),
964                                           TRUE);
965           
966           if (!delete &&
967               0 == gtk_widget_accelerator_signal (GTK_WIDGET (menu_item),
968                                                   accel_group,
969                                                   event->keyval,
970                                                   event->state))
971             {
972               GSList *slist;
973               
974               slist = gtk_accel_group_entries_from_object (GTK_OBJECT (menu_item));
975               while (slist)
976                 {
977                   GtkAccelEntry *ac_entry;
978                   
979                   ac_entry = slist->data;
980                   
981                   if (ac_entry->signal_id == menu_item->accelerator_signal)
982                     break;
983                   
984                   slist = slist->next;
985                 }
986
987               if (!slist)
988                 gtk_widget_add_accelerator (GTK_WIDGET (menu_item),
989                                             gtk_signal_name (menu_item->accelerator_signal),
990                                             accel_group,
991                                             event->keyval,
992                                             event->state,
993                                             GTK_ACCEL_VISIBLE);
994             }
995         }
996     }
997   
998   return FALSE;
999 }
1000
1001 static gint 
1002 gtk_menu_motion_notify  (GtkWidget         *widget,
1003                          GdkEventMotion    *event)
1004 {
1005   g_return_val_if_fail (widget != NULL, FALSE);
1006   g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
1007
1008   if (GTK_MENU_SHELL (widget)->ignore_enter)
1009     GTK_MENU_SHELL (widget)->ignore_enter = FALSE;
1010   else
1011     {
1012       GdkEvent send_event;
1013
1014       send_event.crossing.type = GDK_ENTER_NOTIFY;
1015       send_event.crossing.window = event->window;
1016       send_event.crossing.time = event->time;
1017
1018       gtk_widget_event (widget, &send_event);
1019     }
1020       
1021   return FALSE;
1022 }
1023
1024 static void
1025 gtk_menu_deactivate (GtkMenuShell *menu_shell)
1026 {
1027   GtkWidget *parent;
1028   
1029   g_return_if_fail (menu_shell != NULL);
1030   g_return_if_fail (GTK_IS_MENU (menu_shell));
1031   
1032   parent = menu_shell->parent_menu_shell;
1033   
1034   menu_shell->activate_time = 0;
1035   gtk_menu_popdown (GTK_MENU (menu_shell));
1036   
1037   if (parent)
1038     gtk_menu_shell_deactivate (GTK_MENU_SHELL (parent));
1039 }
1040
1041
1042 static void
1043 gtk_menu_position (GtkMenu *menu)
1044 {
1045   GtkWidget *widget;
1046   GtkRequisition requisition;
1047   gint x, y;
1048  
1049   g_return_if_fail (menu != NULL);
1050   g_return_if_fail (GTK_IS_MENU (menu));
1051
1052   widget = GTK_WIDGET (menu);
1053
1054   gdk_window_get_pointer (NULL, &x, &y, NULL);
1055
1056   /* We need the requisition to figure out the right place to
1057    * popup the menu. In fact, we always need to ask here, since
1058    * if one a size_request was queued while we weren't popped up,
1059    * the requisition won't have been recomputed yet.
1060    */
1061   gtk_widget_size_request (widget, &requisition);
1062       
1063   if (menu->position_func)
1064     (* menu->position_func) (menu, &x, &y, menu->position_func_data);
1065   else
1066     {
1067       gint screen_width;
1068       gint screen_height;
1069       
1070       screen_width = gdk_screen_width ();
1071       screen_height = gdk_screen_height ();
1072           
1073       x -= 2;
1074       y -= 2;
1075       
1076       if ((x + requisition.width) > screen_width)
1077         x -= ((x + requisition.width) - screen_width);
1078       if (x < 0)
1079         x = 0;
1080       if ((y + requisition.height) > screen_height)
1081         y -= ((y + requisition.height) - screen_height);
1082       if (y < 0)
1083         y = 0;
1084     }
1085   
1086   gtk_widget_set_uposition (GTK_MENU_SHELL (menu)->active ?
1087                                 menu->toplevel : menu->tearoff_window, 
1088                             x, y);
1089 }
1090
1091 /* Reparent the menu, taking care of the refcounting
1092  */
1093 static void 
1094 gtk_menu_reparent (GtkMenu      *menu, 
1095                    GtkWidget    *new_parent, 
1096                    gboolean      unrealize)
1097 {
1098   GtkObject *object = GTK_OBJECT (menu);
1099   GtkWidget *widget = GTK_WIDGET (menu);
1100   gboolean was_floating = GTK_OBJECT_FLOATING (object);
1101
1102   gtk_object_ref (object);
1103   gtk_object_sink (object);
1104
1105   if (unrealize)
1106     {
1107       gtk_object_ref (object);
1108       gtk_container_remove (GTK_CONTAINER (widget->parent), widget);
1109       gtk_container_add (GTK_CONTAINER (new_parent), widget);
1110       gtk_object_unref (object);
1111     }
1112   else
1113     gtk_widget_reparent (GTK_WIDGET (menu), new_parent);
1114   
1115   if (was_floating)
1116     GTK_OBJECT_SET_FLAGS (object, GTK_FLOATING);
1117   else
1118     gtk_object_unref (object);
1119 }
1120
1121 static void
1122 gtk_menu_show_all (GtkWidget *widget)
1123 {
1124   g_return_if_fail (widget != NULL);
1125   g_return_if_fail (GTK_IS_MENU (widget));
1126
1127   /* Show children, but not self. */
1128   gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_show_all, NULL);
1129 }
1130
1131
1132 static void
1133 gtk_menu_hide_all (GtkWidget *widget)
1134 {
1135   g_return_if_fail (widget != NULL);
1136   g_return_if_fail (GTK_IS_MENU (widget));
1137
1138   /* Hide children, but not self. */
1139   gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_hide_all, NULL);
1140 }
1141