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