]> Pileus Git - ~andy/gtk/blob - gtk/gtkmenu.c
new fundtion to return the widget that the menu is attached to.
[~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 Free
16  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18 #include <ctype.h>
19 #include "gdk/gdkkeysyms.h"
20 #include "gtkmain.h"
21 #include "gtkmenu.h"
22 #include "gtkmenuitem.h"
23 #include "gtksignal.h"
24
25
26 #define MENU_ITEM_CLASS(w)  GTK_MENU_ITEM_CLASS (GTK_OBJECT (w)->klass)
27
28
29 typedef struct _GtkMenuAttachData       GtkMenuAttachData;
30
31 struct _GtkMenuAttachData
32 {
33   GtkWidget *attach_widget;
34   GtkMenuDetachFunc detacher;
35 };
36
37
38 static void gtk_menu_class_init     (GtkMenuClass      *klass);
39 static void gtk_menu_init           (GtkMenu           *menu);
40 static void gtk_menu_destroy        (GtkObject         *object);
41 static void gtk_menu_show           (GtkWidget         *widget);
42 static void gtk_menu_map            (GtkWidget         *widget);
43 static void gtk_menu_unmap          (GtkWidget         *widget);
44 static void gtk_menu_realize        (GtkWidget         *widget);
45 static void gtk_menu_size_request   (GtkWidget         *widget,
46                                      GtkRequisition    *requisition);
47 static void gtk_menu_size_allocate  (GtkWidget         *widget,
48                                      GtkAllocation     *allocation);
49 static void gtk_menu_paint          (GtkWidget         *widget);
50 static void gtk_menu_draw           (GtkWidget         *widget,
51                                      GdkRectangle      *area);
52 static gint gtk_menu_expose         (GtkWidget         *widget,
53                                      GdkEventExpose    *event);
54 static gint gtk_menu_configure      (GtkWidget         *widget,
55                                      GdkEventConfigure *event);
56 static gint gtk_menu_key_press      (GtkWidget         *widget,
57                                      GdkEventKey       *event);
58 static gint gtk_menu_need_resize    (GtkContainer      *container);
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
63 static GtkMenuShellClass *parent_class = NULL;
64 static const gchar      *attach_data_key = "gtk-menu-attach-data";
65
66
67 guint
68 gtk_menu_get_type ()
69 {
70   static guint menu_type = 0;
71
72   if (!menu_type)
73     {
74       GtkTypeInfo menu_info =
75       {
76         "GtkMenu",
77         sizeof (GtkMenu),
78         sizeof (GtkMenuClass),
79         (GtkClassInitFunc) gtk_menu_class_init,
80         (GtkObjectInitFunc) gtk_menu_init,
81         (GtkArgSetFunc) NULL,
82         (GtkArgGetFunc) NULL,
83       };
84
85       menu_type = gtk_type_unique (gtk_menu_shell_get_type (), &menu_info);
86     }
87
88   return menu_type;
89 }
90
91 static void
92 gtk_menu_class_init (GtkMenuClass *class)
93 {
94   GtkObjectClass *object_class;
95   GtkWidgetClass *widget_class;
96   GtkContainerClass *container_class;
97   GtkMenuShellClass *menu_shell_class;
98
99   object_class = (GtkObjectClass*) class;
100   widget_class = (GtkWidgetClass*) class;
101   container_class = (GtkContainerClass*) class;
102   menu_shell_class = (GtkMenuShellClass*) class;
103   parent_class = gtk_type_class (gtk_menu_shell_get_type ());
104
105   object_class->destroy = gtk_menu_destroy;
106
107   widget_class->show = gtk_menu_show;
108   widget_class->map = gtk_menu_map;
109   widget_class->unmap = gtk_menu_unmap;
110   widget_class->realize = gtk_menu_realize;
111   widget_class->draw = gtk_menu_draw;
112   widget_class->size_request = gtk_menu_size_request;
113   widget_class->size_allocate = gtk_menu_size_allocate;
114   widget_class->expose_event = gtk_menu_expose;
115   widget_class->configure_event = gtk_menu_configure;
116   widget_class->key_press_event = gtk_menu_key_press;
117   widget_class->show_all = gtk_menu_show_all;
118   widget_class->hide_all = gtk_menu_hide_all;  
119   
120   container_class->need_resize = gtk_menu_need_resize;
121
122   menu_shell_class->submenu_placement = GTK_LEFT_RIGHT;
123   menu_shell_class->deactivate = gtk_menu_deactivate;
124 }
125
126 static void
127 gtk_menu_init (GtkMenu *menu)
128 {
129   GTK_WIDGET_SET_FLAGS (menu, GTK_TOPLEVEL);
130
131   menu->parent_menu_item = NULL;
132   menu->old_active_menu_item = NULL;
133   menu->accelerator_table = NULL;
134   menu->position_func = NULL;
135   menu->position_func_data = NULL;
136   
137   GTK_MENU_SHELL (menu)->menu_flag = TRUE;
138   
139   /* we don't need to register as toplevel anymore,
140    * since there is the attach/detach functionality in place.
141    * gtk_container_register_toplevel (GTK_CONTAINER (menu));
142    */
143 }
144
145 static void
146 gtk_menu_destroy (GtkObject         *object)
147 {
148   GtkMenuAttachData *data;
149
150   g_return_if_fail (object != NULL);
151   g_return_if_fail (GTK_IS_MENU (object));
152
153   gtk_widget_ref (GTK_WIDGET (object));
154
155   data = gtk_object_get_data (object, attach_data_key);
156   if (data)
157     gtk_menu_detach (GTK_MENU (object));
158
159   /* we don't need this:
160    * gtk_container_unregister_toplevel (GTK_CONTAINER (object));
161    */
162
163   if (GTK_OBJECT_CLASS (parent_class)->destroy)
164     (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
165
166   gtk_widget_unref (GTK_WIDGET (object));
167 }
168
169
170 void
171 gtk_menu_attach_to_widget (GtkMenu             *menu,
172                            GtkWidget           *attach_widget,
173                            GtkMenuDetachFunc    detacher)
174 {
175   GtkMenuAttachData *data;
176
177   g_return_if_fail (menu != NULL);
178   g_return_if_fail (GTK_IS_MENU (menu));
179   g_return_if_fail (attach_widget != NULL);
180   g_return_if_fail (GTK_IS_WIDGET (attach_widget));
181   g_return_if_fail (detacher != NULL);
182
183   /* keep this function in sync with gtk_widget_set_parent()
184    */
185
186   data = gtk_object_get_data (GTK_OBJECT (menu), attach_data_key);
187   if (data)
188     {
189       g_warning ("gtk_menu_attach_to_widget(): menu already attached to %s",
190                  gtk_type_name (GTK_OBJECT_TYPE (data->attach_widget)));
191       return;
192     }
193
194   gtk_widget_ref (menu);
195   gtk_object_sink (GTK_OBJECT (menu));
196
197   data = g_new (GtkMenuAttachData, 1);
198   data->attach_widget = attach_widget;
199   data->detacher = detacher;
200   gtk_object_set_data (GTK_OBJECT (menu), attach_data_key, data);
201
202   if (GTK_WIDGET_STATE (menu) != GTK_STATE_NORMAL)
203     gtk_widget_set_state (GTK_WIDGET (menu), GTK_STATE_NORMAL);
204
205   /* we don't need to set the style here, since
206    * we are a toplevel widget.
207    */
208 }
209
210 GtkWidget*
211 gtk_menu_get_attach_widget (GtkMenu             *menu)
212 {
213   GtkMenuAttachData *data;
214
215   g_return_val_if_fail (menu != NULL, NULL);
216   g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
217
218   data = gtk_object_get_data (GTK_OBJECT (menu), attach_data_key);
219   if (data)
220     return data->attach_widget;
221   return NULL;
222 }
223
224 void
225 gtk_menu_detach (GtkMenu             *menu)
226 {
227   GtkMenuAttachData *data;
228
229   g_return_if_fail (menu != NULL);
230   g_return_if_fail (GTK_IS_MENU (menu));
231
232   /* keep this function in sync with gtk_widget_unparent()
233    */
234   data = gtk_object_get_data (GTK_OBJECT (menu), attach_data_key);
235   if (!data)
236     {
237       g_warning ("gtk_menu_detach(): menu is not attached");
238       return;
239     }
240   gtk_object_remove_data (GTK_OBJECT (menu), attach_data_key);
241
242   data->detacher (data->attach_widget, menu);
243
244   if (GTK_WIDGET_REALIZED (menu))
245     gtk_widget_unrealize (GTK_WIDGET (menu));
246
247   g_free (data);
248
249   gtk_widget_unref (GTK_WIDGET (menu));
250 }
251
252 GtkWidget*
253 gtk_menu_new ()
254 {
255   return GTK_WIDGET (gtk_type_new (gtk_menu_get_type ()));
256 }
257
258 void
259 gtk_menu_append (GtkMenu   *menu,
260                  GtkWidget *child)
261 {
262   gtk_menu_shell_append (GTK_MENU_SHELL (menu), child);
263 }
264
265 void
266 gtk_menu_prepend (GtkMenu   *menu,
267                   GtkWidget *child)
268 {
269   gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), child);
270 }
271
272 void
273 gtk_menu_insert (GtkMenu   *menu,
274                  GtkWidget *child,
275                  gint       position)
276 {
277   gtk_menu_shell_insert (GTK_MENU_SHELL (menu), child, position);
278 }
279
280 void
281 gtk_menu_popup (GtkMenu             *menu,
282                 GtkWidget           *parent_menu_shell,
283                 GtkWidget           *parent_menu_item,
284                 GtkMenuPositionFunc  func,
285                 gpointer             data,
286                 gint                 button,
287                 guint32              activate_time)
288 {
289   g_return_if_fail (menu != NULL);
290   g_return_if_fail (GTK_IS_MENU (menu));
291
292   GTK_MENU_SHELL (menu)->parent_menu_shell = parent_menu_shell;
293   GTK_MENU_SHELL (menu)->active = TRUE;
294   GTK_MENU_SHELL (menu)->button = button;
295
296   menu->parent_menu_item = parent_menu_item;
297   menu->position_func = func;
298   menu->position_func_data = data;
299   GTK_MENU_SHELL (menu)->activate_time = activate_time;
300
301   gtk_widget_show (GTK_WIDGET (menu));
302   gtk_grab_add (GTK_WIDGET (menu));
303 }
304
305 void
306 gtk_menu_popdown (GtkMenu *menu)
307 {
308   GtkMenuShell *menu_shell;
309
310   g_return_if_fail (menu != NULL);
311   g_return_if_fail (GTK_IS_MENU (menu));
312
313   menu_shell = GTK_MENU_SHELL (menu);
314
315   menu_shell->parent_menu_shell = NULL;
316   menu_shell->active = FALSE;
317
318   if (menu_shell->active_menu_item)
319     {
320       menu->old_active_menu_item = menu_shell->active_menu_item;
321       gtk_menu_item_deselect (GTK_MENU_ITEM (menu_shell->active_menu_item));
322       menu_shell->active_menu_item = NULL;
323     }
324
325   gtk_widget_hide (GTK_WIDGET (menu));
326   gtk_grab_remove (GTK_WIDGET (menu));
327 }
328
329 GtkWidget*
330 gtk_menu_get_active (GtkMenu *menu)
331 {
332   GtkWidget *child;
333   GList *children;
334
335   g_return_val_if_fail (menu != NULL, NULL);
336   g_return_val_if_fail (GTK_IS_MENU (menu), NULL);
337
338   if (!menu->old_active_menu_item)
339     {
340       child = NULL;
341       children = GTK_MENU_SHELL (menu)->children;
342
343       while (children)
344         {
345           child = children->data;
346           children = children->next;
347
348           if (GTK_BIN (child)->child)
349             break;
350           child = NULL;
351         }
352
353       menu->old_active_menu_item = child;
354     }
355
356   return menu->old_active_menu_item;
357 }
358
359 void
360 gtk_menu_set_active (GtkMenu *menu,
361                      gint     index)
362 {
363   GtkWidget *child;
364   GList *tmp_list;
365
366   g_return_if_fail (menu != NULL);
367   g_return_if_fail (GTK_IS_MENU (menu));
368
369   tmp_list = g_list_nth (GTK_MENU_SHELL (menu)->children, index);
370   if (tmp_list)
371     {
372       child = tmp_list->data;
373       if (GTK_BIN (child)->child)
374         menu->old_active_menu_item = child;
375     }
376 }
377
378 void
379 gtk_menu_set_accelerator_table (GtkMenu             *menu,
380                                 GtkAcceleratorTable *table)
381 {
382   g_return_if_fail (menu != NULL);
383   g_return_if_fail (GTK_IS_MENU (menu));
384
385   if (menu->accelerator_table != table)
386     {
387       if (menu->accelerator_table)
388         gtk_accelerator_table_unref (menu->accelerator_table);
389       menu->accelerator_table = table;
390       if (menu->accelerator_table)
391         gtk_accelerator_table_ref (menu->accelerator_table);
392     }
393 }
394
395
396 static void
397 gtk_menu_show (GtkWidget *widget)
398 {
399   g_return_if_fail (widget != NULL);
400   g_return_if_fail (GTK_IS_MENU (widget));
401
402   GTK_WIDGET_SET_FLAGS (widget, GTK_VISIBLE);
403   gtk_widget_map (widget);
404 }
405
406 static void
407 gtk_menu_map (GtkWidget *widget)
408 {
409   GtkMenu *menu;
410   GtkMenuShell *menu_shell;
411   GtkWidget *child;
412   GList *children;
413   GtkAllocation allocation;
414   gint x, y;
415
416   g_return_if_fail (widget != NULL);
417   g_return_if_fail (GTK_IS_MENU (widget));
418
419   menu = GTK_MENU (widget);
420   menu_shell = GTK_MENU_SHELL (widget);
421   GTK_WIDGET_SET_FLAGS (menu_shell, GTK_MAPPED);
422
423   gtk_widget_size_request (widget, &widget->requisition);
424
425   if (menu_shell->menu_flag)
426     {
427       menu_shell->menu_flag = FALSE;
428
429       allocation.x = widget->allocation.x;
430       allocation.y = widget->allocation.y;
431       allocation.width = widget->requisition.width;
432       allocation.height = widget->requisition.height;
433
434       gtk_widget_size_allocate (widget, &allocation);
435     }
436
437   gdk_window_get_pointer (NULL, &x, &y, NULL);
438
439   if (menu->position_func)
440     (* menu->position_func) (menu, &x, &y, menu->position_func_data);
441   else
442     {
443       gint screen_width;
444       gint screen_height;
445
446       screen_width = gdk_screen_width ();
447       screen_height = gdk_screen_height ();
448
449       x -= 2;
450       y -= 2;
451
452       if ((x + widget->requisition.width) > screen_width)
453         x -= ((x + widget->requisition.width) - screen_width);
454       if (x < 0)
455         x = 0;
456       if ((y + widget->requisition.height) > screen_height)
457         y -= ((y + widget->requisition.height) - screen_height);
458       if (y < 0)
459         y = 0;
460     }
461
462   gdk_window_move_resize (widget->window, x, y,
463                           widget->requisition.width,
464                           widget->requisition.height);
465
466   children = menu_shell->children;
467   while (children)
468     {
469       child = children->data;
470       children = children->next;
471
472       if (GTK_WIDGET_VISIBLE (child) && !GTK_WIDGET_MAPPED (child))
473         gtk_widget_map (child);
474     }
475
476   gdk_window_show (widget->window);
477 }
478
479 static void
480 gtk_menu_unmap (GtkWidget *widget)
481 {
482   g_return_if_fail (widget != NULL);
483   g_return_if_fail (GTK_IS_MENU (widget));
484
485   GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
486   gdk_window_hide (widget->window);
487 }
488
489 static void
490 gtk_menu_realize (GtkWidget *widget)
491 {
492   GdkWindowAttr attributes;
493   gint attributes_mask;
494
495   g_return_if_fail (widget != NULL);
496   g_return_if_fail (GTK_IS_MENU (widget));
497
498   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
499
500   attributes.x = widget->allocation.x;
501   attributes.y = widget->allocation.y;
502   attributes.width = widget->allocation.width;
503   attributes.height = widget->allocation.height;
504   attributes.wclass = GDK_INPUT_OUTPUT;
505   attributes.visual = gtk_widget_get_visual (widget);
506   attributes.colormap = gtk_widget_get_colormap (widget);
507   attributes.window_type = GDK_WINDOW_TEMP;
508   attributes.event_mask = gtk_widget_get_events (widget);
509   attributes.event_mask |= (GDK_EXPOSURE_MASK |
510                             GDK_KEY_PRESS_MASK |
511                             GDK_STRUCTURE_MASK);
512
513   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
514   widget->window = gdk_window_new (NULL, &attributes, attributes_mask);
515   gdk_window_set_user_data (widget->window, widget);
516
517   widget->style = gtk_style_attach (widget->style, widget->window);
518   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
519 }
520
521 static void
522 gtk_menu_size_request (GtkWidget      *widget,
523                        GtkRequisition *requisition)
524 {
525   GtkMenu *menu;
526   GtkMenuShell *menu_shell;
527   GtkWidget *child;
528   GList *children;
529   gint max_accelerator_size;
530   gint max_toggle_size;
531
532   g_return_if_fail (widget != NULL);
533   g_return_if_fail (GTK_IS_MENU (widget));
534   g_return_if_fail (requisition != NULL);
535
536   menu = GTK_MENU (widget);
537   menu_shell = GTK_MENU_SHELL (widget);
538
539   requisition->width = 0;
540   requisition->height = 0;
541
542   max_accelerator_size = 0;
543   max_toggle_size = 0;
544
545   children = menu_shell->children;
546   while (children)
547     {
548       child = children->data;
549       children = children->next;
550
551       if (GTK_WIDGET_VISIBLE (child))
552         {
553           GTK_MENU_ITEM (child)->show_submenu_indicator = TRUE;
554           gtk_widget_size_request (child, &child->requisition);
555
556           requisition->width = MAX (requisition->width, child->requisition.width);
557           requisition->height += child->requisition.height;
558
559           max_accelerator_size = MAX (max_accelerator_size, GTK_MENU_ITEM (child)->accelerator_size);
560           max_toggle_size = MAX (max_toggle_size, MENU_ITEM_CLASS (child)->toggle_size);
561         }
562     }
563
564   requisition->width += max_toggle_size + max_accelerator_size;
565   requisition->width += (GTK_CONTAINER (menu)->border_width +
566                          widget->style->klass->xthickness) * 2;
567   requisition->height += (GTK_CONTAINER (menu)->border_width +
568                           widget->style->klass->ythickness) * 2;
569
570   children = menu_shell->children;
571   while (children)
572     {
573       child = children->data;
574       children = children->next;
575
576       GTK_MENU_ITEM (child)->accelerator_size = max_accelerator_size;
577       GTK_MENU_ITEM (child)->toggle_size = max_toggle_size;
578     }
579 }
580
581 static void
582 gtk_menu_size_allocate (GtkWidget     *widget,
583                         GtkAllocation *allocation)
584 {
585   GtkMenu *menu;
586   GtkMenuShell *menu_shell;
587   GtkWidget *child;
588   GtkAllocation child_allocation;
589   GList *children;
590
591   g_return_if_fail (widget != NULL);
592   g_return_if_fail (GTK_IS_MENU (widget));
593   g_return_if_fail (allocation != NULL);
594
595   menu = GTK_MENU (widget);
596   menu_shell = GTK_MENU_SHELL (widget);
597
598   widget->allocation = *allocation;
599
600   if (menu_shell->children)
601     {
602       child_allocation.x = (GTK_CONTAINER (menu)->border_width +
603                             widget->style->klass->xthickness);
604       child_allocation.y = (GTK_CONTAINER (menu)->border_width +
605                             widget->style->klass->ythickness);
606       child_allocation.width = allocation->width - child_allocation.x * 2;
607
608       children = menu_shell->children;
609       while (children)
610         {
611           child = children->data;
612           children = children->next;
613
614           if (GTK_WIDGET_VISIBLE (child))
615             {
616               child_allocation.height = child->requisition.height;
617
618               gtk_widget_size_allocate (child, &child_allocation);
619
620               child_allocation.y += child_allocation.height;
621             }
622         }
623     }
624 }
625
626 static void
627 gtk_menu_paint (GtkWidget *widget)
628 {
629   g_return_if_fail (widget != NULL);
630   g_return_if_fail (GTK_IS_MENU (widget));
631
632   if (GTK_WIDGET_DRAWABLE (widget))
633     {
634       gtk_draw_shadow (widget->style,
635                        widget->window,
636                        GTK_STATE_NORMAL,
637                        GTK_SHADOW_OUT,
638                        0, 0,
639                        widget->allocation.width,
640                        widget->allocation.height);
641     }
642 }
643
644 static void
645 gtk_menu_draw (GtkWidget    *widget,
646                GdkRectangle *area)
647 {
648   GtkMenuShell *menu_shell;
649   GtkWidget *child;
650   GdkRectangle child_area;
651   GList *children;
652
653   g_return_if_fail (widget != NULL);
654   g_return_if_fail (GTK_IS_MENU (widget));
655   g_return_if_fail (area != NULL);
656
657   if (GTK_WIDGET_DRAWABLE (widget))
658     {
659       gtk_menu_paint (widget);
660
661       menu_shell = GTK_MENU_SHELL (widget);
662
663       children = menu_shell->children;
664       while (children)
665         {
666           child = children->data;
667           children = children->next;
668
669           if (gtk_widget_intersect (child, area, &child_area))
670             gtk_widget_draw (child, &child_area);
671         }
672     }
673 }
674
675 static gint
676 gtk_menu_expose (GtkWidget      *widget,
677                  GdkEventExpose *event)
678 {
679   GtkMenuShell *menu_shell;
680   GtkWidget *child;
681   GdkEventExpose child_event;
682   GList *children;
683
684   g_return_val_if_fail (widget != NULL, FALSE);
685   g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
686   g_return_val_if_fail (event != NULL, FALSE);
687
688   if (GTK_WIDGET_DRAWABLE (widget))
689     {
690       gtk_menu_paint (widget);
691
692       menu_shell = GTK_MENU_SHELL (widget);
693       child_event = *event;
694
695       children = menu_shell->children;
696       while (children)
697         {
698           child = children->data;
699           children = children->next;
700
701           if (GTK_WIDGET_NO_WINDOW (child) &&
702               gtk_widget_intersect (child, &event->area, &child_event.area))
703             gtk_widget_event (child, (GdkEvent*) &child_event);
704         }
705     }
706
707   return FALSE;
708 }
709
710 static gint
711 gtk_menu_configure (GtkWidget         *widget,
712                     GdkEventConfigure *event)
713 {
714   GtkAllocation allocation;
715
716   g_return_val_if_fail (widget != NULL, FALSE);
717   g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
718   g_return_val_if_fail (event != NULL, FALSE);
719
720   if (GTK_MENU_SHELL (widget)->menu_flag)
721     {
722       GTK_MENU_SHELL (widget)->menu_flag = FALSE;
723
724       allocation.x = 0;
725       allocation.y = 0;
726       allocation.width = event->width;
727       allocation.height = event->height;
728
729       gtk_widget_size_allocate (widget, &allocation);
730     }
731
732   return FALSE;
733 }
734
735 static gint
736 gtk_menu_key_press (GtkWidget   *widget,
737                     GdkEventKey *event)
738 {
739   GtkAllocation allocation;
740   GtkAcceleratorTable *table;
741   GtkMenuShell *menu_shell;
742   GtkMenuItem *menu_item;
743   gchar *signame;
744   int delete;
745
746   g_return_val_if_fail (widget != NULL, FALSE);
747   g_return_val_if_fail (GTK_IS_MENU (widget), FALSE);
748   g_return_val_if_fail (event != NULL, FALSE);
749
750   delete = ((event->keyval == GDK_Delete) ||
751             (event->keyval == GDK_BackSpace));
752
753   if (delete || ((event->keyval >= 0x20) && (event->keyval <= 0xFF)))
754     {
755       menu_shell = GTK_MENU_SHELL (widget);
756       menu_item = GTK_MENU_ITEM (menu_shell->active_menu_item);
757
758       if (menu_item && GTK_BIN (menu_item)->child)
759         {
760           /* block resizes */
761           gtk_container_block_resize (GTK_CONTAINER (widget));
762
763           table = NULL;
764           /* if the menu item currently has an accelerator then we'll
765            *  remove it before we do anything else.
766            */
767           if (menu_item->accelerator_signal)
768             {
769               signame = gtk_signal_name (menu_item->accelerator_signal);
770               table = gtk_accelerator_table_find (GTK_OBJECT (widget),
771                                                   signame,
772                                                   menu_item->accelerator_key,
773                                                   menu_item->accelerator_mods);
774               if (!table)
775                 table = GTK_MENU (widget)->accelerator_table;
776               gtk_widget_remove_accelerator (GTK_WIDGET (menu_item),
777                                              table, signame);
778             }
779
780           if (!table)
781             table = GTK_MENU (widget)->accelerator_table;
782
783           /* if we aren't simply deleting the accelerator, then we'll install
784            *  the new one now.
785            */
786           if (!delete)
787             gtk_widget_install_accelerator (GTK_WIDGET (menu_item),
788                                             table, "activate",
789                                             toupper (event->keyval),
790                                             event->state);
791
792           /* check and see if the menu has changed size. */
793           gtk_widget_size_request (widget, &widget->requisition);
794
795           allocation.x = widget->allocation.x;
796           allocation.y = widget->allocation.y;
797           allocation.width = widget->requisition.width;
798           allocation.height = widget->requisition.height;
799
800           if ((allocation.width == widget->allocation.width) &&
801               (allocation.height == widget->allocation.height))
802             {
803               gtk_widget_queue_draw (widget);
804             }
805           else
806             {
807               gtk_widget_size_allocate (GTK_WIDGET (widget), &allocation);
808               gtk_menu_map (widget);
809             }
810
811           /* unblock resizes */
812           gtk_container_unblock_resize (GTK_CONTAINER (widget));
813         }
814     }
815
816   return FALSE;
817 }
818
819 static gint
820 gtk_menu_need_resize (GtkContainer *container)
821 {
822   GtkAllocation allocation;
823
824   g_return_val_if_fail (container != NULL, FALSE);
825   g_return_val_if_fail (GTK_IS_MENU (container), FALSE);
826
827   if (GTK_WIDGET_VISIBLE (container))
828     {
829       GTK_MENU_SHELL (container)->menu_flag = FALSE;
830
831       gtk_widget_size_request (GTK_WIDGET (container),
832                                &GTK_WIDGET (container)->requisition);
833
834       allocation.x = GTK_WIDGET (container)->allocation.x;
835       allocation.y = GTK_WIDGET (container)->allocation.y;
836       allocation.width = GTK_WIDGET (container)->requisition.width;
837       allocation.height = GTK_WIDGET (container)->requisition.height;
838
839       gtk_widget_size_allocate (GTK_WIDGET (container), &allocation);
840     }
841   else
842     {
843       GTK_MENU_SHELL (container)->menu_flag = TRUE;
844     }
845
846   return FALSE;
847 }
848
849 static void
850 gtk_menu_deactivate (GtkMenuShell *menu_shell)
851 {
852   GtkMenuShell *parent;
853
854   g_return_if_fail (menu_shell != NULL);
855   g_return_if_fail (GTK_IS_MENU (menu_shell));
856
857   parent = GTK_MENU_SHELL (menu_shell->parent_menu_shell);
858
859   menu_shell->activate_time = 0;
860   gtk_menu_popdown (GTK_MENU (menu_shell));
861
862   if (parent)
863     gtk_menu_shell_deactivate (parent);
864 }
865
866
867 static void
868 gtk_menu_show_all (GtkWidget *widget)
869 {
870   GtkContainer *container;
871
872   g_return_if_fail (widget != NULL);
873   g_return_if_fail (GTK_IS_MENU (widget));
874   container = GTK_CONTAINER (widget);
875
876   /* Show children, but not self. */
877   gtk_container_foreach (container, (GtkCallback) gtk_widget_show_all, NULL);
878 }
879
880
881 static void
882 gtk_menu_hide_all (GtkWidget *widget)
883 {
884   GtkContainer *container;
885
886   g_return_if_fail (widget != NULL);
887   g_return_if_fail (GTK_IS_MENU (widget));
888   container = GTK_CONTAINER (widget);
889
890   /* Hide children, but not self. */
891   gtk_container_foreach (container, (GtkCallback) gtk_widget_hide_all, NULL);
892 }
893