]> Pileus Git - ~andy/gtk/blob - gtk/gtkmenuitem.c
Add note about GtkMenuPositionFunc API changes.
[~andy/gtk] / gtk / gtkmenuitem.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 Lesser 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  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser 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-2000.  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 <string.h>
28 #include "gtkaccellabel.h"
29 #include "gtkmain.h"
30 #include "gtkmenu.h"
31 #include "gtkmenubar.h"
32 #include "gtkmenuitem.h"
33 #include "gtksignal.h"
34
35
36 #define BORDER_SPACING  3
37 #define SELECT_TIMEOUT  75
38
39 #define MENU_ITEM_CLASS(w)  GTK_MENU_ITEM_CLASS (GTK_OBJECT (w)->klass)
40
41
42 enum {
43   ACTIVATE,
44   ACTIVATE_ITEM,
45   TOGGLE_SIZE_REQUEST,
46   TOGGLE_SIZE_ALLOCATE,
47   LAST_SIGNAL
48 };
49
50
51 static void gtk_menu_item_class_init     (GtkMenuItemClass *klass);
52 static void gtk_menu_item_init           (GtkMenuItem      *menu_item);
53 static void gtk_menu_item_destroy        (GtkObject        *object);
54 static void gtk_menu_item_size_request   (GtkWidget        *widget,
55                                           GtkRequisition   *requisition);
56 static void gtk_menu_item_size_allocate  (GtkWidget        *widget,
57                                           GtkAllocation    *allocation);
58 static void gtk_menu_item_paint          (GtkWidget        *widget,
59                                           GdkRectangle     *area);
60 static void gtk_menu_item_draw           (GtkWidget        *widget,
61                                           GdkRectangle     *area);
62 static gint gtk_menu_item_expose         (GtkWidget        *widget,
63                                           GdkEventExpose   *event);
64
65 static void gtk_real_menu_item_select               (GtkItem     *item);
66 static void gtk_real_menu_item_deselect             (GtkItem     *item);
67 static void gtk_real_menu_item_activate_item        (GtkMenuItem *item);
68 static void gtk_real_menu_item_toggle_size_request  (GtkMenuItem *menu_item,
69                                                      guint16     *requisition);
70 static void gtk_real_menu_item_toggle_size_allocate (GtkMenuItem *menu_item,
71                                                      guint16      allocation);
72
73 static gint gtk_menu_item_select_timeout (gpointer          data);
74 static void gtk_menu_item_popup_submenu  (gpointer     data);
75 static void gtk_menu_item_position_menu  (GtkMenu          *menu,
76                                           gint             *x,
77                                           gint             *y,
78                                           gboolean         *push_in,
79                                           gpointer          user_data);
80 static void gtk_menu_item_show_all       (GtkWidget        *widget);
81 static void gtk_menu_item_hide_all       (GtkWidget        *widget);
82 static void gtk_menu_item_forall         (GtkContainer    *container,
83                                           gboolean         include_internals,
84                                           GtkCallback      callback,
85                                           gpointer         callback_data);
86
87 static GtkItemClass *parent_class;
88 static guint menu_item_signals[LAST_SIGNAL] = { 0 };
89 static guint32  last_submenu_deselect_time = 0;
90
91
92
93 GtkType
94 gtk_menu_item_get_type (void)
95 {
96   static GtkType menu_item_type = 0;
97
98   if (!menu_item_type)
99     {
100       static const GTypeInfo menu_item_info =
101       {
102         sizeof (GtkMenuItemClass),
103         NULL,           /* base_init */
104         NULL,           /* base_finalize */
105         (GClassInitFunc) gtk_menu_item_class_init,
106         NULL,           /* class_finalize */
107         NULL,           /* class_data */
108         sizeof (GtkMenuItem),
109         16,             /* n_preallocs */
110         (GInstanceInitFunc) gtk_menu_item_init,
111       };
112
113       menu_item_type = g_type_register_static (GTK_TYPE_ITEM, "GtkMenuItem", &menu_item_info, 0);
114     }
115
116   return menu_item_type;
117 }
118
119 static void
120 gtk_menu_item_class_init (GtkMenuItemClass *klass)
121 {
122   GtkObjectClass *object_class;
123   GtkWidgetClass *widget_class;
124   GtkContainerClass *container_class;
125   GtkItemClass *item_class;
126
127   object_class = (GtkObjectClass*) klass;
128   widget_class = (GtkWidgetClass*) klass;
129   container_class = (GtkContainerClass*) klass;
130   item_class = (GtkItemClass*) klass;
131
132   parent_class = gtk_type_class (gtk_item_get_type ());
133
134   menu_item_signals[ACTIVATE] =
135     gtk_signal_new ("activate",
136                     GTK_RUN_FIRST | GTK_RUN_ACTION,
137                     GTK_CLASS_TYPE (object_class),
138                     GTK_SIGNAL_OFFSET (GtkMenuItemClass, activate),
139                     gtk_marshal_VOID__VOID,
140                     GTK_TYPE_NONE, 0);
141
142   menu_item_signals[ACTIVATE_ITEM] =
143     gtk_signal_new ("activate_item",
144                     GTK_RUN_FIRST,
145                     GTK_CLASS_TYPE (object_class),
146                     GTK_SIGNAL_OFFSET (GtkMenuItemClass, activate_item),
147                     gtk_signal_default_marshaller,
148                     GTK_TYPE_NONE, 0);
149
150   menu_item_signals[TOGGLE_SIZE_REQUEST] =
151     gtk_signal_new ("toggle_size_request",
152                     GTK_RUN_FIRST,
153                     GTK_CLASS_TYPE (object_class),
154                     GTK_SIGNAL_OFFSET (GtkMenuItemClass, toggle_size_request),
155                     gtk_marshal_NONE__POINTER,
156                     GTK_TYPE_NONE, 1,
157                     GTK_TYPE_POINTER);
158
159   menu_item_signals[TOGGLE_SIZE_ALLOCATE] =
160     gtk_signal_new ("toggle_size_allocate",
161                     GTK_RUN_FIRST,
162                     GTK_CLASS_TYPE (object_class),
163                     GTK_SIGNAL_OFFSET (GtkMenuItemClass, toggle_size_allocate),
164                     gtk_marshal_NONE__UINT,
165                     GTK_TYPE_NONE, 1,
166                     GTK_TYPE_UINT);
167
168   gtk_object_class_add_signals (object_class, menu_item_signals, LAST_SIGNAL);
169
170   object_class->destroy = gtk_menu_item_destroy;
171
172   widget_class->activate_signal = menu_item_signals[ACTIVATE];
173   widget_class->size_request = gtk_menu_item_size_request;
174   widget_class->size_allocate = gtk_menu_item_size_allocate;
175   widget_class->draw = gtk_menu_item_draw;
176   widget_class->expose_event = gtk_menu_item_expose;
177   widget_class->show_all = gtk_menu_item_show_all;
178   widget_class->hide_all = gtk_menu_item_hide_all;
179
180   container_class->forall = gtk_menu_item_forall;
181
182   item_class->select = gtk_real_menu_item_select;
183   item_class->deselect = gtk_real_menu_item_deselect;
184
185   klass->activate = NULL;
186   klass->activate_item = gtk_real_menu_item_activate_item;
187   klass->toggle_size_request = gtk_real_menu_item_toggle_size_request;
188   klass->toggle_size_allocate = gtk_real_menu_item_toggle_size_allocate;
189
190   klass->hide_on_activate = TRUE;
191 }
192
193 static void
194 gtk_menu_item_init (GtkMenuItem *menu_item)
195 {
196   menu_item->submenu = NULL;
197   menu_item->accelerator_signal = menu_item_signals[ACTIVATE];
198   menu_item->toggle_size = 0;
199   menu_item->accelerator_width = 0;
200   menu_item->show_toggle_indicator = FALSE;
201   menu_item->show_submenu_indicator = FALSE;
202   menu_item->submenu_direction = GTK_DIRECTION_RIGHT;
203   menu_item->submenu_placement = GTK_TOP_BOTTOM;
204   menu_item->right_justify = FALSE;
205
206   menu_item->timer = 0;
207 }
208
209 GtkWidget*
210 gtk_menu_item_new (void)
211 {
212   return GTK_WIDGET (gtk_type_new (gtk_menu_item_get_type ()));
213 }
214
215 GtkWidget*
216 gtk_menu_item_new_with_label (const gchar *label)
217 {
218   GtkWidget *menu_item;
219   GtkWidget *accel_label;
220
221   menu_item = gtk_menu_item_new ();
222   accel_label = gtk_accel_label_new (label);
223   gtk_misc_set_alignment (GTK_MISC (accel_label), 0.0, 0.5);
224
225   gtk_container_add (GTK_CONTAINER (menu_item), accel_label);
226   gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (accel_label), menu_item);
227   gtk_widget_show (accel_label);
228
229   return menu_item;
230 }
231
232 static void
233 gtk_menu_item_destroy (GtkObject *object)
234 {
235   GtkMenuItem *menu_item;
236
237   g_return_if_fail (object != NULL);
238   g_return_if_fail (GTK_IS_MENU_ITEM (object));
239
240   menu_item = GTK_MENU_ITEM (object);
241
242   if (menu_item->submenu)
243     gtk_widget_destroy (menu_item->submenu);
244
245   if (GTK_OBJECT_CLASS (parent_class)->destroy)
246     (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
247 }
248
249 static void
250 gtk_menu_item_detacher (GtkWidget     *widget,
251                         GtkMenu       *menu)
252 {
253   GtkMenuItem *menu_item;
254
255   g_return_if_fail (widget != NULL);
256   g_return_if_fail (GTK_IS_MENU_ITEM (widget));
257
258   menu_item = GTK_MENU_ITEM (widget);
259   g_return_if_fail (menu_item->submenu == (GtkWidget*) menu);
260
261   menu_item->submenu = NULL;
262 }
263
264 void
265 gtk_menu_item_set_submenu (GtkMenuItem *menu_item,
266                            GtkWidget   *submenu)
267 {
268   g_return_if_fail (menu_item != NULL);
269   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
270   
271   if (menu_item->submenu != submenu)
272     {
273       gtk_menu_item_remove_submenu (menu_item);
274       
275       menu_item->submenu = submenu;
276       gtk_menu_attach_to_widget (GTK_MENU (submenu),
277                                  GTK_WIDGET (menu_item),
278                                  gtk_menu_item_detacher);
279       
280       if (GTK_WIDGET (menu_item)->parent)
281         gtk_widget_queue_resize (GTK_WIDGET (menu_item));
282     }
283 }
284
285 void
286 gtk_menu_item_remove_submenu (GtkMenuItem         *menu_item)
287 {
288   g_return_if_fail (menu_item != NULL);
289   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
290       
291   if (menu_item->submenu)
292     gtk_menu_detach (GTK_MENU (menu_item->submenu));
293 }
294
295 void
296 gtk_menu_item_set_placement (GtkMenuItem         *menu_item,
297                              GtkSubmenuPlacement  placement)
298 {
299   g_return_if_fail (menu_item != NULL);
300   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
301
302   menu_item->submenu_placement = placement;
303 }
304
305 void
306 gtk_menu_item_configure (GtkMenuItem *menu_item,
307                          gint         show_toggle_indicator,
308                          gint         show_submenu_indicator)
309 {
310   g_return_if_fail (menu_item != NULL);
311   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
312
313   menu_item->show_toggle_indicator = (show_toggle_indicator == TRUE);
314   menu_item->show_submenu_indicator = (show_submenu_indicator == TRUE);
315 }
316
317 void
318 gtk_menu_item_select (GtkMenuItem *menu_item)
319 {
320   g_return_if_fail (menu_item != NULL);
321   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
322   
323   gtk_item_select (GTK_ITEM (menu_item));
324 }
325
326 void
327 gtk_menu_item_deselect (GtkMenuItem *menu_item)
328 {
329   g_return_if_fail (menu_item != NULL);
330   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
331   
332   gtk_item_deselect (GTK_ITEM (menu_item));
333 }
334
335 void
336 gtk_menu_item_activate (GtkMenuItem *menu_item)
337 {
338   g_return_if_fail (menu_item != NULL);
339   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
340   
341   gtk_signal_emit (GTK_OBJECT (menu_item), menu_item_signals[ACTIVATE]);
342 }
343
344 void
345 gtk_menu_item_toggle_size_request (GtkMenuItem *menu_item,
346                                    guint16     *requisition)
347 {
348   g_return_if_fail (menu_item != NULL);
349   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
350
351   gtk_signal_emit (GTK_OBJECT (menu_item), menu_item_signals[TOGGLE_SIZE_REQUEST], requisition);
352 }
353
354 void
355 gtk_menu_item_toggle_size_allocate (GtkMenuItem *menu_item,
356                                     guint16      allocation)
357 {
358   g_return_if_fail (menu_item != NULL);
359   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
360
361   gtk_signal_emit (GTK_OBJECT (menu_item), menu_item_signals[TOGGLE_SIZE_ALLOCATE], allocation);
362 }
363
364 static void
365 gtk_menu_item_accel_width_foreach (GtkWidget *widget,
366                                    gpointer data)
367 {
368   guint *width = data;
369
370   if (GTK_IS_ACCEL_LABEL (widget))
371     {
372       guint w;
373
374       w = gtk_accel_label_get_accel_width (GTK_ACCEL_LABEL (widget));
375       *width = MAX (*width, w);
376     }
377   else if (GTK_IS_CONTAINER (widget))
378     gtk_container_foreach (GTK_CONTAINER (widget),
379                            gtk_menu_item_accel_width_foreach,
380                            data);
381 }
382
383 static void
384 gtk_menu_item_size_request (GtkWidget      *widget,
385                             GtkRequisition *requisition)
386 {
387   GtkMenuItem *menu_item;
388   GtkBin *bin;
389   guint accel_width;
390
391   g_return_if_fail (widget != NULL);
392   g_return_if_fail (GTK_IS_MENU_ITEM (widget));
393   g_return_if_fail (requisition != NULL);
394
395   bin = GTK_BIN (widget);
396   menu_item = GTK_MENU_ITEM (widget);
397
398   requisition->width = (GTK_CONTAINER (widget)->border_width +
399                         widget->style->xthickness +
400                         BORDER_SPACING) * 2;
401   requisition->height = (GTK_CONTAINER (widget)->border_width +
402                          widget->style->ythickness) * 2;
403
404   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
405     {
406       GtkRequisition child_requisition;
407       
408       gtk_widget_size_request (bin->child, &child_requisition);
409
410       requisition->width += child_requisition.width;
411       requisition->height += child_requisition.height;
412     }
413
414   if (menu_item->submenu && menu_item->show_submenu_indicator)
415     requisition->width += 21;
416
417   accel_width = 0;
418   gtk_container_foreach (GTK_CONTAINER (menu_item),
419                          gtk_menu_item_accel_width_foreach,
420                          &accel_width);
421   menu_item->accelerator_width = accel_width;
422 }
423
424 static void
425 gtk_menu_item_size_allocate (GtkWidget     *widget,
426                              GtkAllocation *allocation)
427 {
428   GtkMenuItem *menu_item;
429   GtkBin *bin;
430   GtkAllocation child_allocation;
431
432   g_return_if_fail (widget != NULL);
433   g_return_if_fail (GTK_IS_MENU_ITEM (widget));
434   g_return_if_fail (allocation != NULL);
435
436   menu_item = GTK_MENU_ITEM (widget);
437   bin = GTK_BIN (widget);
438   
439   widget->allocation = *allocation;
440
441   if (bin->child)
442     {
443       child_allocation.x = (GTK_CONTAINER (widget)->border_width +
444                             widget->style->xthickness +
445                             BORDER_SPACING);
446       child_allocation.y = (GTK_CONTAINER (widget)->border_width +
447                             widget->style->ythickness);
448       child_allocation.width = MAX (1, (gint)allocation->width - child_allocation.x * 2);
449       child_allocation.height = MAX (1, (gint)allocation->height - child_allocation.y * 2);
450       child_allocation.x += GTK_MENU_ITEM (widget)->toggle_size;
451       child_allocation.width -= GTK_MENU_ITEM (widget)->toggle_size;
452       if (menu_item->submenu && menu_item->show_submenu_indicator)
453         child_allocation.width -= 21;
454       
455       gtk_widget_size_allocate (bin->child, &child_allocation);
456     }
457
458   if (GTK_WIDGET_REALIZED (widget))
459     gdk_window_move_resize (widget->window,
460                             allocation->x, allocation->y,
461                             allocation->width, allocation->height);
462
463   if (menu_item->submenu)
464     gtk_menu_reposition (GTK_MENU (menu_item->submenu));
465 }
466
467 static void
468 gtk_menu_item_paint (GtkWidget    *widget,
469                      GdkRectangle *area)
470 {
471   GtkMenuItem *menu_item;
472   GtkStateType state_type;
473   GtkShadowType shadow_type;
474   gint width, height;
475   gint x, y;
476
477   g_return_if_fail (widget != NULL);
478   g_return_if_fail (GTK_IS_MENU_ITEM (widget));
479
480   if (GTK_WIDGET_DRAWABLE (widget))
481     {
482       menu_item = GTK_MENU_ITEM (widget);
483
484       state_type = widget->state;
485
486       x = GTK_CONTAINER (menu_item)->border_width;
487       y = GTK_CONTAINER (menu_item)->border_width;
488       width = widget->allocation.width - x * 2;
489       height = widget->allocation.height - y * 2;
490       
491       if ((state_type == GTK_STATE_PRELIGHT) &&
492           (GTK_BIN (menu_item)->child))
493         gtk_paint_box (widget->style,
494                        widget->window,
495                        GTK_STATE_PRELIGHT,
496                        GTK_SHADOW_OUT,
497                        area, widget, "menuitem",
498                        x, y, width, height);
499       else
500         {
501           gdk_window_set_back_pixmap (widget->window, NULL, TRUE);
502           gdk_window_clear_area (widget->window, area->x, area->y, area->width, area->height);
503         }
504
505       if (menu_item->submenu && menu_item->show_submenu_indicator)
506         {
507           shadow_type = GTK_SHADOW_OUT;
508           if (state_type == GTK_STATE_PRELIGHT)
509             shadow_type = GTK_SHADOW_IN;
510
511           gtk_paint_arrow (widget->style, widget->window,
512                            state_type, shadow_type, 
513                            area, widget, "menuitem", 
514                            GTK_ARROW_RIGHT, TRUE,
515                            x + width - 15, y + height / 2 - 5, 10, 10);
516         }
517       else if (!GTK_BIN (menu_item)->child)
518         {
519            gtk_paint_hline (widget->style, widget->window, GTK_STATE_NORMAL,
520                             area, widget, "menuitem",
521                             0, widget->allocation.width, 0);
522         }
523     }
524 }
525
526 static void
527 gtk_menu_item_draw (GtkWidget    *widget,
528                     GdkRectangle *area)
529 {
530   GtkBin *bin;
531   GdkRectangle child_area;
532
533   g_return_if_fail (widget != NULL);
534   g_return_if_fail (GTK_IS_MENU_ITEM (widget));
535   g_return_if_fail (area != NULL);
536
537   if (GTK_WIDGET_DRAWABLE (widget))
538     {
539       gtk_menu_item_paint (widget, area);
540
541       bin = GTK_BIN (widget);
542
543       if (bin->child)
544         {
545           if (gtk_widget_intersect (bin->child, area, &child_area))
546             gtk_widget_draw (bin->child, &child_area);
547         }
548     }
549 }
550
551 static gint
552 gtk_menu_item_expose (GtkWidget      *widget,
553                       GdkEventExpose *event)
554 {
555   GtkBin *bin;
556   GdkEventExpose child_event;
557
558   g_return_val_if_fail (widget != NULL, FALSE);
559   g_return_val_if_fail (GTK_IS_MENU_ITEM (widget), FALSE);
560   g_return_val_if_fail (event != NULL, FALSE);
561
562   if (GTK_WIDGET_DRAWABLE (widget))
563     {
564       gtk_menu_item_paint (widget, &event->area);
565
566       bin = GTK_BIN (widget);
567
568       if (bin->child)
569         {
570           child_event = *event;
571
572           if (GTK_WIDGET_NO_WINDOW (bin->child) &&
573               gtk_widget_intersect (bin->child, &event->area, &child_event.area))
574             gtk_widget_event (bin->child, (GdkEvent*) &child_event);
575         }
576     }
577
578   return FALSE;
579 }
580
581 static void
582 gtk_real_menu_item_select (GtkItem *item)
583 {
584   GtkMenuItem *menu_item;
585
586   g_return_if_fail (item != NULL);
587   g_return_if_fail (GTK_IS_MENU_ITEM (item));
588
589   menu_item = GTK_MENU_ITEM (item);
590
591   /*  if (menu_item->submenu && !GTK_WIDGET_VISIBLE (menu_item->submenu))*/
592   if (menu_item->submenu)
593     {
594       guint32 etime;
595       GdkEvent *event = gtk_get_current_event ();
596
597       etime = event ? gdk_event_get_time (event) : GDK_CURRENT_TIME;
598       if (etime >= last_submenu_deselect_time &&
599           last_submenu_deselect_time + SELECT_TIMEOUT > etime)
600         menu_item->timer = gtk_timeout_add (SELECT_TIMEOUT - (etime - last_submenu_deselect_time),
601                                             gtk_menu_item_select_timeout,
602                                             menu_item);
603       else
604         gtk_menu_item_popup_submenu (menu_item);
605       if(event) gdk_event_free(event);
606     }
607   
608   gtk_widget_set_state (GTK_WIDGET (menu_item), GTK_STATE_PRELIGHT);
609   gtk_widget_draw (GTK_WIDGET (menu_item), NULL);
610 }
611
612 static void
613 gtk_real_menu_item_deselect (GtkItem *item)
614 {
615   GtkMenuItem *menu_item;
616
617   g_return_if_fail (item != NULL);
618   g_return_if_fail (GTK_IS_MENU_ITEM (item));
619
620   menu_item = GTK_MENU_ITEM (item);
621
622   if (menu_item->submenu)
623     {
624       guint32 etime;
625       GdkEvent *event = gtk_get_current_event ();
626
627       if (menu_item->timer)
628         {
629           gtk_timeout_remove (menu_item->timer);
630           menu_item->timer = 0;
631         }
632       else
633         gtk_menu_popdown (GTK_MENU (menu_item->submenu));
634
635       etime = event ? gdk_event_get_time (event) : GDK_CURRENT_TIME;
636       if (etime > last_submenu_deselect_time)
637         last_submenu_deselect_time = etime;
638       if(event) gdk_event_free(event);
639     }
640
641   gtk_widget_set_state (GTK_WIDGET (menu_item), GTK_STATE_NORMAL);
642   gtk_widget_draw (GTK_WIDGET (menu_item), NULL);
643 }
644
645 static void
646 gtk_real_menu_item_activate_item (GtkMenuItem *menu_item)
647 {
648   GtkWidget *widget;
649   GtkMenuShell *submenu; 
650
651   g_return_if_fail (menu_item != NULL);
652   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
653
654   widget = GTK_WIDGET (menu_item);
655   
656   if (widget->parent &&
657       GTK_IS_MENU_SHELL (widget->parent))
658     {
659       if (menu_item->submenu == NULL)
660         gtk_menu_shell_activate_item (GTK_MENU_SHELL (widget->parent),
661                                       widget, TRUE);
662       else
663         {
664           GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget->parent);
665
666           if (!menu_shell->active)
667             {
668               gtk_grab_add (GTK_WIDGET (menu_shell));
669               menu_shell->have_grab = TRUE;
670               menu_shell->active = TRUE;
671             }
672
673           gtk_menu_shell_select_item (GTK_MENU_SHELL (widget->parent), widget); 
674           gtk_menu_item_popup_submenu (widget); 
675
676           submenu = GTK_MENU_SHELL (menu_item->submenu);
677           if (submenu->children)
678             gtk_menu_shell_select_item (submenu, submenu->children->data);
679         }
680     }
681 }
682 static void
683 gtk_real_menu_item_toggle_size_request (GtkMenuItem *menu_item,
684                                         guint16     *requisition)
685 {
686   g_return_if_fail (menu_item != NULL);
687   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
688
689   *requisition = 0;
690 }
691
692 static void
693 gtk_real_menu_item_toggle_size_allocate (GtkMenuItem *menu_item,
694                                          guint16      allocation)
695 {
696   g_return_if_fail (menu_item != NULL);
697   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
698
699   menu_item->toggle_size = allocation;
700 }
701
702 static gint
703 gtk_menu_item_select_timeout (gpointer data)
704 {
705   GDK_THREADS_ENTER ();
706
707   gtk_menu_item_popup_submenu (data);
708
709   GDK_THREADS_LEAVE ();
710
711   return FALSE;  
712 }
713
714 static void
715 gtk_menu_item_popup_submenu (gpointer data)
716 {
717   GtkMenuItem *menu_item;
718
719   menu_item = GTK_MENU_ITEM (data);
720   menu_item->timer = 0;
721
722   if (GTK_WIDGET_IS_SENSITIVE (menu_item->submenu))
723     {
724       gtk_menu_popup (GTK_MENU (menu_item->submenu),
725                       GTK_WIDGET (menu_item)->parent,
726                       GTK_WIDGET (menu_item),
727                       gtk_menu_item_position_menu,
728                       menu_item,
729                       GTK_MENU_SHELL (GTK_WIDGET (menu_item)->parent)->button,
730                       0);
731     }
732 }
733
734 static void
735 gtk_menu_item_position_menu (GtkMenu  *menu,
736                              gint     *x,
737                              gint     *y,
738                              gboolean *push_in,
739                              gpointer  user_data)
740 {
741   GtkMenuItem *menu_item;
742   GtkWidget *parent_menu_item;
743   gint screen_width;
744   gint screen_height;
745   gint twidth, theight;
746   gint tx, ty;
747
748   g_return_if_fail (menu != NULL);
749   g_return_if_fail (x != NULL);
750   g_return_if_fail (y != NULL);
751
752   menu_item = GTK_MENU_ITEM (user_data);
753
754   twidth = GTK_WIDGET (menu)->requisition.width;
755   theight = GTK_WIDGET (menu)->requisition.height;
756
757   screen_width = gdk_screen_width ();
758   screen_height = gdk_screen_height ();
759
760   if (!gdk_window_get_origin (GTK_WIDGET (menu_item)->window, &tx, &ty))
761     {
762       g_warning ("Menu not on screen");
763       return;
764     }
765
766   switch (menu_item->submenu_placement)
767     {
768     case GTK_TOP_BOTTOM:
769       if ((ty + GTK_WIDGET (menu_item)->allocation.height + theight) <= screen_height)
770         ty += GTK_WIDGET (menu_item)->allocation.height;
771       else if ((ty - theight) >= 0)
772         ty -= theight;
773       else if (screen_height - (ty + GTK_WIDGET (menu_item)->allocation.height) > ty)
774         ty += GTK_WIDGET (menu_item)->allocation.height;
775       else
776         ty -= theight;
777       break;
778
779     case GTK_LEFT_RIGHT:
780       menu_item->submenu_direction = GTK_DIRECTION_RIGHT;
781       parent_menu_item = GTK_MENU (GTK_WIDGET (menu_item)->parent)->parent_menu_item;
782       if (parent_menu_item)
783         menu_item->submenu_direction = GTK_MENU_ITEM (parent_menu_item)->submenu_direction;
784
785       switch (menu_item->submenu_direction)
786         {
787         case GTK_DIRECTION_LEFT:
788           if ((tx - twidth) >= 0)
789             tx -= twidth;
790           else
791             {
792               menu_item->submenu_direction = GTK_DIRECTION_RIGHT;
793               tx += GTK_WIDGET (menu_item)->allocation.width - 5;
794             }
795           break;
796
797         case GTK_DIRECTION_RIGHT:
798           if ((tx + GTK_WIDGET (menu_item)->allocation.width + twidth - 5) <= screen_width)
799             tx += GTK_WIDGET (menu_item)->allocation.width - 5;
800           else
801             {
802               menu_item->submenu_direction = GTK_DIRECTION_LEFT;
803               tx -= twidth;
804             }
805           break;
806         }
807
808       ty += GTK_WIDGET (menu_item)->allocation.height / 4;
809
810       /* If the height of the menu doesn't fit we move it upward. */
811       ty = CLAMP (ty, 0, MAX (0, screen_height - theight));
812       break;
813     }
814
815   /* If we have negative, tx, here it is because we can't get
816    * the menu all the way on screen. Favor the left portion.
817    */
818   *x = CLAMP (tx, 0, MAX (0, screen_width - twidth));
819   *y = ty;
820 }
821
822 void
823 gtk_menu_item_right_justify(GtkMenuItem *menuitem)
824 {
825   g_return_if_fail (menuitem != NULL);
826   g_return_if_fail (GTK_IS_MENU_ITEM (menuitem));
827
828   menuitem->right_justify = 1;
829 }
830
831
832 static void
833 gtk_menu_item_show_all (GtkWidget *widget)
834 {
835   GtkMenuItem *menu_item;
836
837   g_return_if_fail (widget != NULL);
838   g_return_if_fail (GTK_IS_MENU_ITEM (widget));
839
840   menu_item = GTK_MENU_ITEM (widget);
841
842   /* show children including submenu */
843   if (menu_item->submenu)
844     gtk_widget_show_all (menu_item->submenu);
845   gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_show_all, NULL);
846
847   gtk_widget_show (widget);
848 }
849
850 static void
851 gtk_menu_item_hide_all (GtkWidget *widget)
852 {
853   GtkMenuItem *menu_item;
854
855   g_return_if_fail (widget != NULL);
856   g_return_if_fail (GTK_IS_MENU_ITEM (widget));
857
858   gtk_widget_hide (widget);
859
860   menu_item = GTK_MENU_ITEM (widget);
861
862   /* hide children including submenu */
863   gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_hide_all, NULL);
864   if (menu_item->submenu)
865     gtk_widget_hide_all (menu_item->submenu);
866 }
867
868 static void
869 gtk_menu_item_forall (GtkContainer *container,
870                       gboolean      include_internals,
871                       GtkCallback   callback,
872                       gpointer      callback_data)
873 {
874   GtkBin *bin;
875   GtkMenuItem *menu_item;
876
877   g_return_if_fail (container != NULL);
878   g_return_if_fail (GTK_IS_MENU_ITEM (container));
879   g_return_if_fail (callback != NULL);
880
881   bin = GTK_BIN (container);
882   menu_item = GTK_MENU_ITEM (container);
883
884   if (bin->child)
885     (* callback) (bin->child, callback_data);
886 }