]> Pileus Git - ~andy/gtk/blob - gtk/gtkmenuitem.c
Patch from Bill Haneman (with many modifications) to make the focus color
[~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 "gtkmarshalers.h"
31 #include "gtkmenu.h"
32 #include "gtkmenubar.h"
33 #include "gtkmenuitem.h"
34 #include "gtktearoffmenuitem.h"
35 #include "gtkseparatormenuitem.h"
36 #include "gtksignal.h"
37
38
39 #define BORDER_SPACING  3
40 #define SELECT_TIMEOUT  75
41
42 #define MENU_ITEM_CLASS(w)  GTK_MENU_ITEM_CLASS (GTK_OBJECT (w)->klass)
43
44
45 enum {
46   ACTIVATE,
47   ACTIVATE_ITEM,
48   TOGGLE_SIZE_REQUEST,
49   TOGGLE_SIZE_ALLOCATE,
50   LAST_SIGNAL
51 };
52
53
54 static void gtk_menu_item_class_init     (GtkMenuItemClass *klass);
55 static void gtk_menu_item_init           (GtkMenuItem      *menu_item);
56 static void gtk_menu_item_destroy        (GtkObject        *object);
57 static void gtk_menu_item_finalize       (GObject          *object);
58 static void gtk_menu_item_size_request   (GtkWidget        *widget,
59                                           GtkRequisition   *requisition);
60 static void gtk_menu_item_size_allocate  (GtkWidget        *widget,
61                                           GtkAllocation    *allocation);
62 static void gtk_menu_item_realize        (GtkWidget        *widget);
63 static void gtk_menu_item_unrealize      (GtkWidget        *widget);
64 static void gtk_menu_item_map            (GtkWidget        *widget);
65 static void gtk_menu_item_unmap          (GtkWidget        *widget);
66 static void gtk_menu_item_paint          (GtkWidget        *widget,
67                                           GdkRectangle     *area);
68 static gint gtk_menu_item_expose         (GtkWidget        *widget,
69                                           GdkEventExpose   *event);
70
71
72 static void gtk_real_menu_item_select               (GtkItem     *item);
73 static void gtk_real_menu_item_deselect             (GtkItem     *item);
74 static void gtk_real_menu_item_activate_item        (GtkMenuItem *item);
75 static void gtk_real_menu_item_toggle_size_request  (GtkMenuItem *menu_item,
76                                                      gint        *requisition);
77 static void gtk_real_menu_item_toggle_size_allocate (GtkMenuItem *menu_item,
78                                                      gint         allocation);
79 static gboolean gtk_menu_item_mnemonic_activate     (GtkWidget   *widget,
80                                                      gboolean     group_cycling);
81
82 static gint gtk_menu_item_select_timeout (gpointer          data);
83 static void gtk_menu_item_popup_submenu  (gpointer     data);
84 static void gtk_menu_item_position_menu  (GtkMenu          *menu,
85                                           gint             *x,
86                                           gint             *y,
87                                           gboolean         *push_in,
88                                           gpointer          user_data);
89 static void gtk_menu_item_show_all       (GtkWidget        *widget);
90 static void gtk_menu_item_hide_all       (GtkWidget        *widget);
91 static void gtk_menu_item_forall         (GtkContainer    *container,
92                                           gboolean         include_internals,
93                                           GtkCallback      callback,
94                                           gpointer         callback_data);
95
96
97 static GtkItemClass *parent_class;
98 static guint menu_item_signals[LAST_SIGNAL] = { 0 };
99 static guint32  last_submenu_deselect_time = 0;
100
101
102
103 GtkType
104 gtk_menu_item_get_type (void)
105 {
106   static GtkType menu_item_type = 0;
107
108   if (!menu_item_type)
109     {
110       static const GTypeInfo menu_item_info =
111       {
112         sizeof (GtkMenuItemClass),
113         NULL,           /* base_init */
114         NULL,           /* base_finalize */
115         (GClassInitFunc) gtk_menu_item_class_init,
116         NULL,           /* class_finalize */
117         NULL,           /* class_data */
118         sizeof (GtkMenuItem),
119         16,             /* n_preallocs */
120         (GInstanceInitFunc) gtk_menu_item_init,
121       };
122
123       menu_item_type = g_type_register_static (GTK_TYPE_ITEM, "GtkMenuItem", &menu_item_info, 0);
124     }
125
126   return menu_item_type;
127 }
128
129 static void
130 gtk_menu_item_class_init (GtkMenuItemClass *klass)
131 {
132   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
133   GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass);
134   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
135   GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
136   GtkItemClass *item_class = GTK_ITEM_CLASS (klass);
137
138   parent_class = g_type_class_peek_parent (klass);
139
140   gobject_class->finalize = gtk_menu_item_finalize;
141
142   object_class->destroy = gtk_menu_item_destroy;
143
144   widget_class->size_request = gtk_menu_item_size_request;
145   widget_class->size_allocate = gtk_menu_item_size_allocate;
146   widget_class->expose_event = gtk_menu_item_expose;
147   widget_class->realize = gtk_menu_item_realize;
148   widget_class->unrealize = gtk_menu_item_unrealize;
149   widget_class->map = gtk_menu_item_map;
150   widget_class->unmap = gtk_menu_item_unmap;
151   widget_class->show_all = gtk_menu_item_show_all;
152   widget_class->hide_all = gtk_menu_item_hide_all;
153   widget_class->mnemonic_activate = gtk_menu_item_mnemonic_activate;
154   
155   container_class->forall = gtk_menu_item_forall;
156
157   item_class->select = gtk_real_menu_item_select;
158   item_class->deselect = gtk_real_menu_item_deselect;
159
160   klass->activate = NULL;
161   klass->activate_item = gtk_real_menu_item_activate_item;
162   klass->toggle_size_request = gtk_real_menu_item_toggle_size_request;
163   klass->toggle_size_allocate = gtk_real_menu_item_toggle_size_allocate;
164
165   klass->hide_on_activate = TRUE;
166
167   menu_item_signals[ACTIVATE] =
168     gtk_signal_new ("activate",
169                     GTK_RUN_FIRST | GTK_RUN_ACTION,
170                     GTK_CLASS_TYPE (object_class),
171                     GTK_SIGNAL_OFFSET (GtkMenuItemClass, activate),
172                     _gtk_marshal_VOID__VOID,
173                     GTK_TYPE_NONE, 0);
174   widget_class->activate_signal = menu_item_signals[ACTIVATE];
175
176   menu_item_signals[ACTIVATE_ITEM] =
177     gtk_signal_new ("activate_item",
178                     GTK_RUN_FIRST,
179                     GTK_CLASS_TYPE (object_class),
180                     GTK_SIGNAL_OFFSET (GtkMenuItemClass, activate_item),
181                     gtk_signal_default_marshaller,
182                     GTK_TYPE_NONE, 0);
183
184   menu_item_signals[TOGGLE_SIZE_REQUEST] =
185     gtk_signal_new ("toggle_size_request",
186                     GTK_RUN_FIRST,
187                     GTK_CLASS_TYPE (object_class),
188                     GTK_SIGNAL_OFFSET (GtkMenuItemClass, toggle_size_request),
189                     _gtk_marshal_VOID__BOXED,
190                     GTK_TYPE_NONE, 1,
191                     GTK_TYPE_REQUISITION);
192
193   menu_item_signals[TOGGLE_SIZE_ALLOCATE] =
194     gtk_signal_new ("toggle_size_allocate",
195                     GTK_RUN_FIRST,
196                     GTK_CLASS_TYPE (object_class),
197                     GTK_SIGNAL_OFFSET (GtkMenuItemClass, toggle_size_allocate),
198                     _gtk_marshal_NONE__INT,
199                     GTK_TYPE_NONE, 1,
200                     GTK_TYPE_INT);
201 }
202
203 static void
204 gtk_menu_item_init (GtkMenuItem *menu_item)
205 {
206   GTK_WIDGET_SET_FLAGS (menu_item, GTK_NO_WINDOW);
207   
208   menu_item->submenu = NULL;
209   menu_item->toggle_size = 0;
210   menu_item->accelerator_width = 0;
211   menu_item->show_submenu_indicator = FALSE;
212   menu_item->submenu_direction = GTK_DIRECTION_RIGHT;
213   menu_item->submenu_placement = GTK_TOP_BOTTOM;
214   menu_item->right_justify = FALSE;
215
216   menu_item->timer = 0;
217 }
218
219 GtkWidget*
220 gtk_menu_item_new (void)
221 {
222   return GTK_WIDGET (gtk_type_new (gtk_menu_item_get_type ()));
223 }
224
225 GtkWidget*
226 gtk_menu_item_new_with_label (const gchar *label)
227 {
228   GtkWidget *menu_item;
229   GtkWidget *accel_label;
230
231   menu_item = gtk_menu_item_new ();
232   accel_label = gtk_accel_label_new (label);
233   gtk_misc_set_alignment (GTK_MISC (accel_label), 0.0, 0.5);
234
235   gtk_container_add (GTK_CONTAINER (menu_item), accel_label);
236   gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (accel_label), menu_item);
237   gtk_widget_show (accel_label);
238
239   return menu_item;
240 }
241
242
243 /**
244  * gtk_menu_item_new_with_mnemonic:
245  * @label: The text of the button, with an underscore in front of the
246  *         mnemonic character
247  * @returns: a new #GtkMenuItem
248  *
249  * Creates a new #GtkMenuItem containing a label. The label
250  * will be created using gtk_label_new_with_mnemonic(), so underscores
251  * in @label indicate the mnemonic for the menu item.
252  **/
253 GtkWidget*
254 gtk_menu_item_new_with_mnemonic (const gchar *label)
255 {
256   GtkWidget *menu_item;
257   GtkWidget *accel_label;
258
259   menu_item = gtk_menu_item_new ();
260   accel_label = gtk_type_new (GTK_TYPE_ACCEL_LABEL);
261   gtk_label_set_text_with_mnemonic (GTK_LABEL (accel_label), label);
262   gtk_misc_set_alignment (GTK_MISC (accel_label), 0.0, 0.5);
263
264   gtk_container_add (GTK_CONTAINER (menu_item), accel_label);
265   gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (accel_label), menu_item);
266   gtk_widget_show (accel_label);
267
268   return menu_item;
269 }
270
271 static void
272 gtk_menu_item_destroy (GtkObject *object)
273 {
274   GtkMenuItem *menu_item;
275
276   g_return_if_fail (GTK_IS_MENU_ITEM (object));
277
278   menu_item = GTK_MENU_ITEM (object);
279
280   if (menu_item->submenu)
281     gtk_widget_destroy (menu_item->submenu);
282
283   GTK_OBJECT_CLASS (parent_class)->destroy (object);
284 }
285
286 static void
287 gtk_menu_item_finalize (GObject *object)
288 {
289   GtkMenuItem *menu_item = GTK_MENU_ITEM (object);
290
291   g_free (menu_item->accel_path);
292
293   G_OBJECT_CLASS (parent_class)->finalize (object);
294 }
295
296 static void
297 gtk_menu_item_detacher (GtkWidget     *widget,
298                         GtkMenu       *menu)
299 {
300   GtkMenuItem *menu_item;
301
302   g_return_if_fail (GTK_IS_MENU_ITEM (widget));
303
304   menu_item = GTK_MENU_ITEM (widget);
305   g_return_if_fail (menu_item->submenu == (GtkWidget*) menu);
306
307   menu_item->submenu = NULL;
308 }
309
310 void
311 gtk_menu_item_set_submenu (GtkMenuItem *menu_item,
312                            GtkWidget   *submenu)
313 {
314   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
315   
316   if (menu_item->submenu != submenu)
317     {
318       gtk_menu_item_remove_submenu (menu_item);
319       
320       menu_item->submenu = submenu;
321       gtk_menu_attach_to_widget (GTK_MENU (submenu),
322                                  GTK_WIDGET (menu_item),
323                                  gtk_menu_item_detacher);
324       
325       if (GTK_WIDGET (menu_item)->parent)
326         gtk_widget_queue_resize (GTK_WIDGET (menu_item));
327     }
328 }
329
330 /**
331  * gtk_menu_item_get_submenu:
332  * @menu_item: a #GtkMenuItem
333  *
334  * Gets the submenu underneath this menu item, if any. See
335  * gtk_menu_item_set_submenu().
336  *
337  * Return value: submenu for this menu item, or %NULL if none.
338  **/
339 GtkWidget *
340 gtk_menu_item_get_submenu (GtkMenuItem *menu_item)
341 {
342   g_return_val_if_fail (GTK_IS_MENU_ITEM (menu_item), NULL);
343
344   return menu_item->submenu;
345 }
346
347 void
348 gtk_menu_item_remove_submenu (GtkMenuItem         *menu_item)
349 {
350   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
351       
352   if (menu_item->submenu)
353     gtk_menu_detach (GTK_MENU (menu_item->submenu));
354 }
355
356 void
357 _gtk_menu_item_set_placement (GtkMenuItem         *menu_item,
358                              GtkSubmenuPlacement  placement)
359 {
360   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
361
362   menu_item->submenu_placement = placement;
363 }
364
365 void
366 gtk_menu_item_select (GtkMenuItem *menu_item)
367 {
368   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
369   
370   gtk_item_select (GTK_ITEM (menu_item));
371 }
372
373 void
374 gtk_menu_item_deselect (GtkMenuItem *menu_item)
375 {
376   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
377   
378   gtk_item_deselect (GTK_ITEM (menu_item));
379 }
380
381 void
382 gtk_menu_item_activate (GtkMenuItem *menu_item)
383 {
384   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
385   
386   gtk_signal_emit (GTK_OBJECT (menu_item), menu_item_signals[ACTIVATE]);
387 }
388
389 void
390 gtk_menu_item_toggle_size_request (GtkMenuItem *menu_item,
391                                    gint        *requisition)
392 {
393   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
394
395   gtk_signal_emit (GTK_OBJECT (menu_item), menu_item_signals[TOGGLE_SIZE_REQUEST], requisition);
396 }
397
398 void
399 gtk_menu_item_toggle_size_allocate (GtkMenuItem *menu_item,
400                                     gint         allocation)
401 {
402   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
403
404   gtk_signal_emit (GTK_OBJECT (menu_item), menu_item_signals[TOGGLE_SIZE_ALLOCATE], allocation);
405 }
406
407 static void
408 gtk_menu_item_accel_width_foreach (GtkWidget *widget,
409                                    gpointer data)
410 {
411   guint *width = data;
412
413   if (GTK_IS_ACCEL_LABEL (widget))
414     {
415       guint w;
416
417       w = gtk_accel_label_get_accel_width (GTK_ACCEL_LABEL (widget));
418       *width = MAX (*width, w);
419     }
420   else if (GTK_IS_CONTAINER (widget))
421     gtk_container_foreach (GTK_CONTAINER (widget),
422                            gtk_menu_item_accel_width_foreach,
423                            data);
424 }
425
426 static void
427 gtk_menu_item_size_request (GtkWidget      *widget,
428                             GtkRequisition *requisition)
429 {
430   GtkMenuItem *menu_item;
431   GtkBin *bin;
432   guint accel_width;
433
434   g_return_if_fail (GTK_IS_MENU_ITEM (widget));
435   g_return_if_fail (requisition != NULL);
436
437   bin = GTK_BIN (widget);
438   menu_item = GTK_MENU_ITEM (widget);
439
440   requisition->width = (GTK_CONTAINER (widget)->border_width +
441                         widget->style->xthickness +
442                         BORDER_SPACING) * 2;
443   requisition->height = (GTK_CONTAINER (widget)->border_width +
444                          widget->style->ythickness) * 2;
445
446   if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
447     {
448       GtkRequisition child_requisition;
449       
450       gtk_widget_size_request (bin->child, &child_requisition);
451
452       requisition->width += child_requisition.width;
453       requisition->height += child_requisition.height;
454     }
455
456   if (menu_item->submenu && menu_item->show_submenu_indicator)
457     requisition->width += 21;
458
459   accel_width = 0;
460   gtk_container_foreach (GTK_CONTAINER (menu_item),
461                          gtk_menu_item_accel_width_foreach,
462                          &accel_width);
463   menu_item->accelerator_width = accel_width;
464 }
465
466 static void
467 gtk_menu_item_size_allocate (GtkWidget     *widget,
468                              GtkAllocation *allocation)
469 {
470   GtkMenuItem *menu_item;
471   GtkBin *bin;
472   GtkAllocation child_allocation;
473
474   g_return_if_fail (GTK_IS_MENU_ITEM (widget));
475   g_return_if_fail (allocation != NULL);
476
477   menu_item = GTK_MENU_ITEM (widget);
478   bin = GTK_BIN (widget);
479   
480   widget->allocation = *allocation;
481
482   if (bin->child)
483     {
484       child_allocation.x = (GTK_CONTAINER (widget)->border_width +
485                             widget->style->xthickness +
486                             BORDER_SPACING);
487       child_allocation.y = (GTK_CONTAINER (widget)->border_width +
488                             widget->style->ythickness);
489       child_allocation.width = MAX (1, (gint)allocation->width - child_allocation.x * 2);
490       child_allocation.height = MAX (1, (gint)allocation->height - child_allocation.y * 2);
491       child_allocation.x += GTK_MENU_ITEM (widget)->toggle_size;
492       child_allocation.width -= GTK_MENU_ITEM (widget)->toggle_size;
493       child_allocation.x += widget->allocation.x;
494       child_allocation.y += widget->allocation.y;
495       
496       if (menu_item->submenu && menu_item->show_submenu_indicator)
497         child_allocation.width -= 21;
498       
499       gtk_widget_size_allocate (bin->child, &child_allocation);
500     }
501
502   if (GTK_WIDGET_REALIZED (widget))
503     gdk_window_move_resize (menu_item->event_window,
504                             allocation->x, allocation->y,
505                             allocation->width, allocation->height);
506
507   if (menu_item->submenu)
508     gtk_menu_reposition (GTK_MENU (menu_item->submenu));
509 }
510
511 static void
512 gtk_menu_item_realize (GtkWidget *widget)
513 {
514   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
515   GdkWindowAttr attributes;
516   gint attributes_mask;
517
518   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
519
520   widget->window = gtk_widget_get_parent_window (widget);
521   gdk_window_ref (widget->window);
522   
523   attributes.x = widget->allocation.x;
524   attributes.y = widget->allocation.y;
525   attributes.width = widget->allocation.width;
526   attributes.height = widget->allocation.height;
527   attributes.window_type = GDK_WINDOW_CHILD;
528   attributes.wclass = GDK_INPUT_ONLY;
529   attributes.event_mask = (gtk_widget_get_events (widget) |
530                            GDK_EXPOSURE_MASK |
531                            GDK_BUTTON_PRESS_MASK |
532                            GDK_BUTTON_RELEASE_MASK |
533                            GDK_ENTER_NOTIFY_MASK |
534                            GDK_LEAVE_NOTIFY_MASK |
535                            GDK_POINTER_MOTION_MASK);
536
537   attributes_mask = GDK_WA_X | GDK_WA_Y;
538   menu_item->event_window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
539   gdk_window_set_user_data (menu_item->event_window, widget);
540
541   widget->style = gtk_style_attach (widget->style, widget->window);
542 }
543
544 static void
545 gtk_menu_item_unrealize (GtkWidget *widget)
546 {
547   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
548
549   gdk_window_set_user_data (menu_item->event_window, NULL);
550   gdk_window_destroy (menu_item->event_window);
551   menu_item->event_window = NULL;
552   
553   if (GTK_WIDGET_CLASS (parent_class)->unrealize)
554     (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
555 }
556
557 static void
558 gtk_menu_item_map (GtkWidget *widget)
559 {
560   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
561   
562   gdk_window_show (menu_item->event_window);
563
564   GTK_WIDGET_CLASS (parent_class)->map (widget);
565 }
566
567 static void
568 gtk_menu_item_unmap (GtkWidget *widget)
569 {
570   GtkMenuItem *menu_item = GTK_MENU_ITEM (widget);
571     
572   gdk_window_hide (menu_item->event_window);
573
574   GTK_WIDGET_CLASS (parent_class)->unmap (widget);
575 }
576
577 static void
578 gtk_menu_item_paint (GtkWidget    *widget,
579                      GdkRectangle *area)
580 {
581   GtkMenuItem *menu_item;
582   GtkStateType state_type;
583   GtkShadowType shadow_type;
584   gint width, height;
585   gint x, y;
586   gint border_width = GTK_CONTAINER (widget)->border_width;
587
588   if (GTK_WIDGET_DRAWABLE (widget))
589     {
590       menu_item = GTK_MENU_ITEM (widget);
591
592       state_type = widget->state;
593
594       x = widget->allocation.x + border_width;
595       y = widget->allocation.y + border_width;
596       width = widget->allocation.width - border_width * 2;
597       height = widget->allocation.height - border_width * 2;
598       
599       if ((state_type == GTK_STATE_PRELIGHT) &&
600           (GTK_BIN (menu_item)->child))
601         gtk_paint_box (widget->style,
602                        widget->window,
603                        GTK_STATE_PRELIGHT,
604                        GTK_SHADOW_OUT,
605                        area, widget, "menuitem",
606                        x, y, width, height);
607
608       if (menu_item->submenu && menu_item->show_submenu_indicator)
609         {
610           shadow_type = GTK_SHADOW_OUT;
611           if (state_type == GTK_STATE_PRELIGHT)
612             shadow_type = GTK_SHADOW_IN;
613
614           gtk_paint_arrow (widget->style, widget->window,
615                            state_type, shadow_type, 
616                            area, widget, "menuitem", 
617                            GTK_ARROW_RIGHT, TRUE,
618                            x + width - 15, y + height / 2 - 5, 10, 10);
619         }
620       else if (!GTK_BIN (menu_item)->child)
621         {
622            gtk_paint_hline (widget->style, widget->window, GTK_STATE_NORMAL,
623                             area, widget, "menuitem",
624                             widget->allocation.x, widget->allocation.width,
625                             widget->allocation.y);
626         }
627     }
628 }
629
630 static gint
631 gtk_menu_item_expose (GtkWidget      *widget,
632                       GdkEventExpose *event)
633 {
634   g_return_val_if_fail (GTK_IS_MENU_ITEM (widget), FALSE);
635   g_return_val_if_fail (event != NULL, FALSE);
636
637   if (GTK_WIDGET_DRAWABLE (widget))
638     {
639       gtk_menu_item_paint (widget, &event->area);
640
641       (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
642     }
643
644   return FALSE;
645 }
646
647 static void
648 gtk_real_menu_item_select (GtkItem *item)
649 {
650   GtkMenuItem *menu_item;
651
652   g_return_if_fail (GTK_IS_MENU_ITEM (item));
653
654   menu_item = GTK_MENU_ITEM (item);
655
656   /*  if (menu_item->submenu && !GTK_WIDGET_VISIBLE (menu_item->submenu))*/
657   if (menu_item->submenu)
658     {
659       guint32 etime;
660       GdkEvent *event = gtk_get_current_event ();
661
662       etime = event ? gdk_event_get_time (event) : GDK_CURRENT_TIME;
663       if (etime >= last_submenu_deselect_time &&
664           last_submenu_deselect_time + SELECT_TIMEOUT > etime)
665         menu_item->timer = gtk_timeout_add (SELECT_TIMEOUT - (etime - last_submenu_deselect_time),
666                                             gtk_menu_item_select_timeout,
667                                             menu_item);
668       else
669         gtk_menu_item_popup_submenu (menu_item);
670       if (event)
671         gdk_event_free(event);
672     }
673   
674   gtk_widget_set_state (GTK_WIDGET (menu_item), GTK_STATE_PRELIGHT);
675   gtk_widget_draw (GTK_WIDGET (menu_item), NULL);
676 }
677
678 static void
679 gtk_real_menu_item_deselect (GtkItem *item)
680 {
681   GtkMenuItem *menu_item;
682
683   g_return_if_fail (GTK_IS_MENU_ITEM (item));
684
685   menu_item = GTK_MENU_ITEM (item);
686
687   if (menu_item->submenu)
688     {
689       guint32 etime;
690       GdkEvent *event = gtk_get_current_event ();
691
692       if (menu_item->timer)
693         {
694           gtk_timeout_remove (menu_item->timer);
695           menu_item->timer = 0;
696         }
697       else
698         gtk_menu_popdown (GTK_MENU (menu_item->submenu));
699
700       etime = event ? gdk_event_get_time (event) : GDK_CURRENT_TIME;
701       if (etime > last_submenu_deselect_time)
702         last_submenu_deselect_time = etime;
703       if (event)
704         gdk_event_free(event);
705     }
706
707   gtk_widget_set_state (GTK_WIDGET (menu_item), GTK_STATE_NORMAL);
708   gtk_widget_draw (GTK_WIDGET (menu_item), NULL);
709 }
710
711 static gboolean
712 gtk_menu_item_mnemonic_activate (GtkWidget *widget,
713                                  gboolean   group_cycling)
714 {
715   if (group_cycling)
716     {
717       if (widget->parent &&
718           GTK_IS_MENU_SHELL (widget->parent))
719         gtk_menu_shell_select_item (GTK_MENU_SHELL (widget->parent),
720                                     widget);
721
722     }
723   else
724     gtk_signal_emit (GTK_OBJECT (widget), menu_item_signals[ACTIVATE_ITEM]);
725   
726   return TRUE;
727 }
728
729
730 static void
731 gtk_real_menu_item_activate_item (GtkMenuItem *menu_item)
732 {
733   GtkWidget *widget;
734   GtkMenuShell *submenu; 
735
736   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
737
738   widget = GTK_WIDGET (menu_item);
739   
740   if (widget->parent &&
741       GTK_IS_MENU_SHELL (widget->parent))
742     {
743       if (menu_item->submenu == NULL)
744         gtk_menu_shell_activate_item (GTK_MENU_SHELL (widget->parent),
745                                       widget, TRUE);
746       else
747         {
748           GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget->parent);
749
750           if (!menu_shell->active)
751             {
752               gtk_grab_add (GTK_WIDGET (menu_shell));
753               menu_shell->have_grab = TRUE;
754               menu_shell->active = TRUE;
755             }
756
757           gtk_menu_shell_select_item (GTK_MENU_SHELL (widget->parent), widget); 
758           gtk_menu_item_popup_submenu (widget); 
759
760           submenu = GTK_MENU_SHELL (menu_item->submenu);
761           if (submenu->children)
762             {
763               if (submenu->children->next &&
764                   GTK_IS_TEAROFF_MENU_ITEM (submenu->children->data))
765                 gtk_menu_shell_select_item (submenu, submenu->children->next->data);
766               else
767                 gtk_menu_shell_select_item (submenu, submenu->children->data);
768             }
769         }
770     }
771 }
772 static void
773 gtk_real_menu_item_toggle_size_request (GtkMenuItem *menu_item,
774                                         gint        *requisition)
775 {
776   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
777
778   *requisition = 0;
779 }
780
781 static void
782 gtk_real_menu_item_toggle_size_allocate (GtkMenuItem *menu_item,
783                                          gint         allocation)
784 {
785   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
786
787   menu_item->toggle_size = allocation;
788 }
789
790 static gint
791 gtk_menu_item_select_timeout (gpointer data)
792 {
793   GDK_THREADS_ENTER ();
794
795   gtk_menu_item_popup_submenu (data);
796
797   GDK_THREADS_LEAVE ();
798
799   return FALSE;  
800 }
801
802 static void
803 gtk_menu_item_popup_submenu (gpointer data)
804 {
805   GtkMenuItem *menu_item;
806
807   menu_item = GTK_MENU_ITEM (data);
808   menu_item->timer = 0;
809
810   if (GTK_WIDGET_IS_SENSITIVE (menu_item->submenu))
811     {
812       gtk_menu_popup (GTK_MENU (menu_item->submenu),
813                       GTK_WIDGET (menu_item)->parent,
814                       GTK_WIDGET (menu_item),
815                       gtk_menu_item_position_menu,
816                       menu_item,
817                       GTK_MENU_SHELL (GTK_WIDGET (menu_item)->parent)->button,
818                       0);
819     }
820 }
821
822 static void
823 gtk_menu_item_position_menu (GtkMenu  *menu,
824                              gint     *x,
825                              gint     *y,
826                              gboolean *push_in,
827                              gpointer  user_data)
828 {
829   GtkMenuItem *menu_item;
830   GtkWidget *widget;
831   GtkWidget *parent_menu_item;
832   gint screen_width;
833   gint screen_height;
834   gint twidth, theight;
835   gint tx, ty;
836
837   g_return_if_fail (menu != NULL);
838   g_return_if_fail (x != NULL);
839   g_return_if_fail (y != NULL);
840
841   menu_item = GTK_MENU_ITEM (user_data);
842   widget = GTK_WIDGET (user_data);
843
844   twidth = GTK_WIDGET (menu)->requisition.width;
845   theight = GTK_WIDGET (menu)->requisition.height;
846
847   screen_width = gdk_screen_width ();
848   screen_height = gdk_screen_height ();
849
850   if (!gdk_window_get_origin (widget->window, &tx, &ty))
851     {
852       g_warning ("Menu not on screen");
853       return;
854     }
855
856   tx += widget->allocation.x;
857   ty += widget->allocation.y;
858
859   switch (menu_item->submenu_placement)
860     {
861     case GTK_TOP_BOTTOM:
862       if ((ty + widget->allocation.height + theight) <= screen_height)
863         ty += widget->allocation.height;
864       else if ((ty - theight) >= 0)
865         ty -= theight;
866       else if (screen_height - (ty + widget->allocation.height) > ty)
867         ty += widget->allocation.height;
868       else
869         ty -= theight;
870       break;
871
872     case GTK_LEFT_RIGHT:
873       menu_item->submenu_direction = GTK_DIRECTION_RIGHT;
874       parent_menu_item = GTK_MENU (widget->parent)->parent_menu_item;
875       if (parent_menu_item)
876         menu_item->submenu_direction = GTK_MENU_ITEM (parent_menu_item)->submenu_direction;
877
878       switch (menu_item->submenu_direction)
879         {
880         case GTK_DIRECTION_LEFT:
881           if ((tx - twidth) >= 0)
882             tx -= twidth;
883           else
884             {
885               menu_item->submenu_direction = GTK_DIRECTION_RIGHT;
886               tx += widget->allocation.width - 5;
887             }
888           break;
889
890         case GTK_DIRECTION_RIGHT:
891           if ((tx + widget->allocation.width + twidth - 5) <= screen_width)
892             tx += widget->allocation.width - 5;
893           else
894             {
895               menu_item->submenu_direction = GTK_DIRECTION_LEFT;
896               tx -= twidth;
897             }
898           break;
899         }
900
901       ty += widget->allocation.height / 4;
902
903       /* If the height of the menu doesn't fit we move it upward. */
904       ty = CLAMP (ty, 0, MAX (0, screen_height - theight));
905       break;
906     }
907
908   /* If we have negative, tx, here it is because we can't get
909    * the menu all the way on screen. Favor the left portion.
910    */
911   *x = CLAMP (tx, 0, MAX (0, screen_width - twidth));
912   *y = ty;
913 }
914
915 /**
916  * gtk_menu_item_set_right_justified:
917  * @menu_item: a #GtkMenuItem.
918  * @right_justified: if %TRUE the menu item will appear at the 
919  *   far right if added to a menu bar.
920  * 
921  * Sets whether the menu item appears justified at the right
922  * side of a menu bar. This was traditionally done for "Help" menu
923  * items, but is now considered a bad idea. (If the widget
924  * layout is reversed for a right-to-left language like Hebrew
925  * or Arabic, right-justified-menu-items appear at the left.)
926  **/
927 void
928 gtk_menu_item_set_right_justified (GtkMenuItem *menu_item,
929                                    gboolean     right_justified)
930 {
931   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
932
933   right_justified = right_justified != FALSE;
934
935   if (right_justified != menu_item->right_justify)
936     {
937       menu_item->right_justify = right_justified;
938       gtk_widget_queue_resize (GTK_WIDGET (menu_item));
939     }
940 }
941
942 /**
943  * gtk_menu_item_get_right_justified:
944  * @menu_item: a #GtkMenuItem
945  * 
946  * Gets whether the menu item appears justified at the right
947  * side of the menu bar.
948  * 
949  * Return value: %TRUE if the menu item will appear at the
950  *   far right if added to a menu bar.
951  **/
952 gboolean
953 gtk_menu_item_get_right_justified (GtkMenuItem *menu_item)
954 {
955   g_return_val_if_fail (GTK_IS_MENU_ITEM (menu_item), FALSE);
956   
957   return menu_item->right_justify;
958 }
959
960
961 static void
962 gtk_menu_item_show_all (GtkWidget *widget)
963 {
964   GtkMenuItem *menu_item;
965
966   g_return_if_fail (GTK_IS_MENU_ITEM (widget));
967
968   menu_item = GTK_MENU_ITEM (widget);
969
970   /* show children including submenu */
971   if (menu_item->submenu)
972     gtk_widget_show_all (menu_item->submenu);
973   gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_show_all, NULL);
974
975   gtk_widget_show (widget);
976 }
977
978 static void
979 gtk_menu_item_hide_all (GtkWidget *widget)
980 {
981   GtkMenuItem *menu_item;
982
983   g_return_if_fail (GTK_IS_MENU_ITEM (widget));
984
985   gtk_widget_hide (widget);
986
987   menu_item = GTK_MENU_ITEM (widget);
988
989   /* hide children including submenu */
990   gtk_container_foreach (GTK_CONTAINER (widget), (GtkCallback) gtk_widget_hide_all, NULL);
991   if (menu_item->submenu)
992     gtk_widget_hide_all (menu_item->submenu);
993 }
994
995 static void
996 gtk_menu_item_accel_name_foreach (GtkWidget *widget,
997                                   gpointer data)
998 {
999   const gchar **path_p = data;
1000
1001   if (!*path_p)
1002     {
1003       if (GTK_IS_LABEL (widget))
1004         {
1005           *path_p = gtk_label_get_text (GTK_LABEL (widget));
1006           if (*path_p && (*path_p)[0] == 0)
1007             *path_p = NULL;
1008         }
1009       else if (GTK_IS_CONTAINER (widget))
1010         gtk_container_foreach (GTK_CONTAINER (widget),
1011                                gtk_menu_item_accel_name_foreach,
1012                                data);
1013     }
1014 }
1015
1016 void
1017 _gtk_menu_item_refresh_accel_path (GtkMenuItem   *menu_item,
1018                                    const gchar   *prefix,
1019                                    GtkAccelGroup *accel_group,
1020                                    gboolean       group_changed)
1021 {
1022   const gchar *path;
1023   GtkWidget *widget;
1024
1025   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1026   g_return_if_fail (GTK_IS_ACCEL_GROUP (accel_group));
1027
1028   widget = GTK_WIDGET (menu_item);
1029
1030   path = _gtk_widget_get_accel_path (widget);
1031   if (!path)                                    /* no active accel_path yet */
1032     {
1033       path = menu_item->accel_path;
1034       if (!path && prefix)
1035         {
1036           gchar *postfix = NULL;
1037
1038           /* try to construct one from label text */
1039           gtk_container_foreach (GTK_CONTAINER (menu_item),
1040                                  gtk_menu_item_accel_name_foreach,
1041                                  &postfix);
1042           menu_item->accel_path = postfix ? g_strconcat (prefix, "/", postfix, NULL) : NULL;
1043           path = menu_item->accel_path;
1044         }
1045       if (path)
1046         _gtk_widget_set_accel_path (widget, path, accel_group);
1047     }
1048   else if (group_changed)                       /* reinstall accelerators */
1049     _gtk_widget_set_accel_path (widget, path, accel_group);
1050 }
1051
1052 /**
1053  * gtk_menu_item_set_accel_path
1054  * @menu_item:  a valid #GtkMenuItem
1055  * @accel_path: accelerator path, corresponding to this menu item's funcitonality
1056  *
1057  * Set the accelerator path on @menu_item, through which runtime changes of the
1058  * menu item's accelerator caused by the user can be identified and saved to
1059  * persistant storage (see gtk_accel_map_save() on this).
1060  * To setup a default accelerator for this menu item, call
1061  * gtk_accel_map_add_entry() with the same @accel_path.
1062  * See also gtk_accel_map_add_entry() on the specifics of accelerator paths,
1063  * and gtk_menu_set_accel_path() for a more convenient variant of this function.
1064  */
1065 void
1066 gtk_menu_item_set_accel_path (GtkMenuItem *menu_item,
1067                               const gchar *accel_path)
1068 {
1069   GtkWidget *widget;
1070
1071   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1072   g_return_if_fail (accel_path && accel_path[0] == '<' && strchr (accel_path, '/'));
1073
1074   widget = GTK_WIDGET (menu_item);
1075
1076   /* store new path */
1077   g_free (menu_item->accel_path);
1078   menu_item->accel_path = g_strdup (accel_path);
1079
1080   /* forget accelerators associated with old path */
1081   _gtk_widget_set_accel_path (widget, NULL, NULL);
1082
1083   /* install accelerators associated with new path */
1084   if (widget->parent)
1085     {
1086       GtkMenu *menu = GTK_MENU (widget->parent);
1087
1088       if (menu->accel_group)
1089         _gtk_menu_item_refresh_accel_path (GTK_MENU_ITEM (widget),
1090                                            NULL,
1091                                            menu->accel_group,
1092                                            FALSE);
1093     }
1094 }
1095
1096 static void
1097 gtk_menu_item_forall (GtkContainer *container,
1098                       gboolean      include_internals,
1099                       GtkCallback   callback,
1100                       gpointer      callback_data)
1101 {
1102   GtkBin *bin;
1103   GtkMenuItem *menu_item;
1104
1105   g_return_if_fail (GTK_IS_MENU_ITEM (container));
1106   g_return_if_fail (callback != NULL);
1107
1108   bin = GTK_BIN (container);
1109   menu_item = GTK_MENU_ITEM (container);
1110
1111   if (bin->child)
1112     callback (bin->child, callback_data);
1113 }
1114
1115 gboolean
1116 _gtk_menu_item_is_selectable (GtkWidget *menu_item)
1117 {
1118   if ((!GTK_BIN (menu_item)->child &&
1119        G_OBJECT_TYPE (menu_item) == GTK_TYPE_MENU_ITEM) ||
1120       GTK_IS_SEPARATOR_MENU_ITEM (menu_item) ||
1121       !GTK_WIDGET_IS_SENSITIVE (menu_item) ||
1122       !GTK_WIDGET_VISIBLE (menu_item))
1123     return FALSE;
1124
1125   return TRUE;
1126 }