]> Pileus Git - ~andy/gtk/blob - gtk/gtkoptionmenu.c
Remove trailing commas from enumerations. (#148035)
[~andy/gtk] / gtk / gtkoptionmenu.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Jsh 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 <config.h>
28 #include "gtkintl.h"
29 #include "gtkmenu.h"
30 #include "gtkmenuitem.h"
31 #include "gtkmarshalers.h"
32 #include "gdk/gdkkeysyms.h"
33
34 #undef GTK_DISABLE_DEPRECATED
35 #include "gtkoptionmenu.h"
36 #define GTK_DISABLE_DEPRECATED
37
38 #define CHILD_LEFT_SPACING        4
39 #define CHILD_RIGHT_SPACING       1
40 #define CHILD_TOP_SPACING         1
41 #define CHILD_BOTTOM_SPACING      1
42
43 typedef struct _GtkOptionMenuProps GtkOptionMenuProps;
44
45 struct _GtkOptionMenuProps
46 {
47   gboolean interior_focus;
48   GtkRequisition indicator_size;
49   GtkBorder indicator_spacing;
50   gint focus_width;
51   gint focus_pad;
52 };
53
54 static const GtkOptionMenuProps default_props = {
55   TRUE,
56   { 7, 13 },
57   { 7, 5, 2, 2 },               /* Left, right, top, bottom */
58   1,
59   0
60 };
61
62 static void gtk_option_menu_class_init      (GtkOptionMenuClass *klass);
63 static void gtk_option_menu_init            (GtkOptionMenu      *option_menu);
64 static void gtk_option_menu_destroy         (GtkObject          *object);
65 static void gtk_option_menu_set_property    (GObject            *object,
66                                              guint               prop_id,
67                                              const GValue       *value,
68                                              GParamSpec         *pspec);
69 static void gtk_option_menu_get_property    (GObject            *object,
70                                              guint               prop_id,
71                                              GValue             *value,
72                                              GParamSpec         *pspec);
73 static void gtk_option_menu_size_request    (GtkWidget          *widget,
74                                              GtkRequisition     *requisition);
75 static void gtk_option_menu_size_allocate   (GtkWidget          *widget,
76                                              GtkAllocation      *allocation);
77 static void gtk_option_menu_paint           (GtkWidget          *widget,
78                                              GdkRectangle       *area);
79 static gint gtk_option_menu_expose          (GtkWidget          *widget,
80                                              GdkEventExpose     *event);
81 static gint gtk_option_menu_button_press    (GtkWidget          *widget,
82                                              GdkEventButton     *event);
83 static gint gtk_option_menu_key_press       (GtkWidget          *widget,
84                                              GdkEventKey        *event);
85 static void gtk_option_menu_selection_done  (GtkMenuShell       *menu_shell,
86                                              GtkOptionMenu      *option_menu);
87 static void gtk_option_menu_update_contents (GtkOptionMenu      *option_menu);
88 static void gtk_option_menu_remove_contents (GtkOptionMenu      *option_menu);
89 static void gtk_option_menu_calc_size       (GtkOptionMenu      *option_menu);
90 static void gtk_option_menu_position        (GtkMenu            *menu,
91                                              gint               *x,
92                                              gint               *y,
93                                              gint               *scroll_offet,
94                                              gpointer            user_data);
95 static void gtk_option_menu_show_all        (GtkWidget          *widget);
96 static void gtk_option_menu_hide_all        (GtkWidget          *widget);
97 static gboolean gtk_option_menu_mnemonic_activate (GtkWidget    *widget,
98                                                    gboolean      group_cycling);
99 static GType gtk_option_menu_child_type   (GtkContainer       *container);
100 static gint gtk_option_menu_scroll_event    (GtkWidget          *widget,
101                                              GdkEventScroll     *event);
102
103 enum
104 {
105   CHANGED,
106   LAST_SIGNAL
107 };
108
109 enum
110 {
111   PROP_0,
112   PROP_MENU,
113   LAST_PROP
114 };
115
116 static GtkButtonClass *parent_class = NULL;
117 static guint           signals[LAST_SIGNAL] = { 0 };
118
119
120 GType
121 gtk_option_menu_get_type (void)
122 {
123   static GType option_menu_type = 0;
124
125   if (!option_menu_type)
126     {
127       static const GTypeInfo option_menu_info =
128       {
129         sizeof (GtkOptionMenuClass),
130         NULL,           /* base_init */
131         NULL,           /* base_finalize */
132         (GClassInitFunc) gtk_option_menu_class_init,
133         NULL,           /* class_finalize */
134         NULL,           /* class_data */
135         sizeof (GtkOptionMenu),
136         0,              /* n_preallocs */
137         (GInstanceInitFunc) gtk_option_menu_init,
138       };
139
140       option_menu_type =
141         g_type_register_static (GTK_TYPE_BUTTON, "GtkOptionMenu",
142                                 &option_menu_info, 0);
143     }
144
145   return option_menu_type;
146 }
147
148 static void
149 gtk_option_menu_class_init (GtkOptionMenuClass *class)
150 {
151   GObjectClass *gobject_class;
152   GtkObjectClass *object_class;
153   GtkWidgetClass *widget_class;
154   GtkButtonClass *button_class;
155   GtkContainerClass *container_class;
156
157   gobject_class = (GObjectClass*) class;
158   object_class = (GtkObjectClass*) class;
159   widget_class = (GtkWidgetClass*) class;
160   button_class = (GtkButtonClass*) class;
161   container_class = (GtkContainerClass*) class;
162
163   parent_class = g_type_class_peek_parent (class);
164
165   signals[CHANGED] =
166     g_signal_new ("changed",
167                   G_OBJECT_CLASS_TYPE (class),
168                   G_SIGNAL_RUN_LAST,
169                   G_STRUCT_OFFSET (GtkOptionMenuClass, changed),
170                   NULL, NULL,
171                   _gtk_marshal_VOID__VOID,
172                   G_TYPE_NONE, 0);
173
174   gobject_class->set_property = gtk_option_menu_set_property;
175   gobject_class->get_property = gtk_option_menu_get_property;
176   object_class->destroy = gtk_option_menu_destroy;
177   
178   widget_class->size_request = gtk_option_menu_size_request;
179   widget_class->size_allocate = gtk_option_menu_size_allocate;
180   widget_class->expose_event = gtk_option_menu_expose;
181   widget_class->button_press_event = gtk_option_menu_button_press;
182   widget_class->key_press_event = gtk_option_menu_key_press;
183   widget_class->scroll_event = gtk_option_menu_scroll_event;
184   widget_class->show_all = gtk_option_menu_show_all;
185   widget_class->hide_all = gtk_option_menu_hide_all;
186   widget_class->mnemonic_activate = gtk_option_menu_mnemonic_activate;
187
188   container_class->child_type = gtk_option_menu_child_type;
189
190   g_object_class_install_property (gobject_class,
191                                    PROP_MENU,
192                                    g_param_spec_object ("menu",
193                                                         P_("Menu"),
194                                                         P_("The menu of options"),
195                                                         GTK_TYPE_MENU,
196                                                         G_PARAM_READABLE | G_PARAM_WRITABLE));
197   
198   gtk_widget_class_install_style_property (widget_class,
199                                            g_param_spec_boxed ("indicator_size",
200                                                                P_("Indicator Size"),
201                                                                P_("Size of dropdown indicator"),
202                                                                GTK_TYPE_REQUISITION,
203                                                                G_PARAM_READABLE));
204   gtk_widget_class_install_style_property (widget_class,
205                                            g_param_spec_boxed ("indicator_spacing",
206                                                                P_("Indicator Spacing"),
207                                                                P_("Spacing around indicator"),
208                                                                GTK_TYPE_BORDER,
209                                                                G_PARAM_READABLE));
210 }
211
212 static GType
213 gtk_option_menu_child_type (GtkContainer       *container)
214 {
215   return G_TYPE_NONE;
216 }
217
218 static void
219 gtk_option_menu_init (GtkOptionMenu *option_menu)
220 {
221   GTK_WIDGET_SET_FLAGS (option_menu, GTK_CAN_FOCUS);
222   GTK_WIDGET_UNSET_FLAGS (option_menu, GTK_CAN_DEFAULT | GTK_RECEIVES_DEFAULT);
223
224   option_menu->menu = NULL;
225   option_menu->menu_item = NULL;
226   option_menu->width = 0;
227   option_menu->height = 0;
228 }
229
230 GtkWidget*
231 gtk_option_menu_new (void)
232 {
233   return g_object_new (GTK_TYPE_OPTION_MENU, NULL);
234 }
235
236 GtkWidget*
237 gtk_option_menu_get_menu (GtkOptionMenu *option_menu)
238 {
239   g_return_val_if_fail (GTK_IS_OPTION_MENU (option_menu), NULL);
240
241   return option_menu->menu;
242 }
243
244 static void
245 gtk_option_menu_detacher (GtkWidget     *widget,
246                           GtkMenu       *menu)
247 {
248   GtkOptionMenu *option_menu;
249
250   g_return_if_fail (GTK_IS_OPTION_MENU (widget));
251
252   option_menu = GTK_OPTION_MENU (widget);
253   g_return_if_fail (option_menu->menu == (GtkWidget*) menu);
254
255   gtk_option_menu_remove_contents (option_menu);
256   g_signal_handlers_disconnect_by_func (option_menu->menu,
257                                         gtk_option_menu_selection_done,
258                                         option_menu);
259   g_signal_handlers_disconnect_by_func (option_menu->menu,
260                                         gtk_option_menu_calc_size,
261                                         option_menu);
262   
263   option_menu->menu = NULL;
264   g_object_notify (G_OBJECT (option_menu), "menu");
265 }
266
267 void
268 gtk_option_menu_set_menu (GtkOptionMenu *option_menu,
269                           GtkWidget     *menu)
270 {
271   g_return_if_fail (GTK_IS_OPTION_MENU (option_menu));
272   g_return_if_fail (GTK_IS_MENU (menu));
273
274   if (option_menu->menu != menu)
275     {
276       gtk_option_menu_remove_menu (option_menu);
277
278       option_menu->menu = menu;
279       gtk_menu_attach_to_widget (GTK_MENU (menu),
280                                  GTK_WIDGET (option_menu),
281                                  gtk_option_menu_detacher);
282
283       gtk_option_menu_calc_size (option_menu);
284
285       g_signal_connect_after (option_menu->menu, "selection_done",
286                               G_CALLBACK (gtk_option_menu_selection_done),
287                               option_menu);
288       g_signal_connect_swapped (option_menu->menu, "size_request",
289                                 G_CALLBACK (gtk_option_menu_calc_size),
290                                 option_menu);
291
292       if (GTK_WIDGET (option_menu)->parent)
293         gtk_widget_queue_resize (GTK_WIDGET (option_menu));
294
295       gtk_option_menu_update_contents (option_menu);
296       
297       g_object_notify (G_OBJECT (option_menu), "menu");
298     }
299 }
300
301 void
302 gtk_option_menu_remove_menu (GtkOptionMenu *option_menu)
303 {
304   g_return_if_fail (GTK_IS_OPTION_MENU (option_menu));
305
306   if (option_menu->menu)
307     {
308       if (GTK_MENU_SHELL (option_menu->menu)->active)
309         gtk_menu_shell_cancel (GTK_MENU_SHELL (option_menu->menu));
310       
311       gtk_menu_detach (GTK_MENU (option_menu->menu));
312     }
313 }
314
315 void
316 gtk_option_menu_set_history (GtkOptionMenu *option_menu,
317                              guint          index)
318 {
319   GtkWidget *menu_item;
320
321   g_return_if_fail (GTK_IS_OPTION_MENU (option_menu));
322
323   if (option_menu->menu)
324     {
325       gtk_menu_set_active (GTK_MENU (option_menu->menu), index);
326       menu_item = gtk_menu_get_active (GTK_MENU (option_menu->menu));
327
328       if (menu_item != option_menu->menu_item)
329         gtk_option_menu_update_contents (option_menu);
330     }
331 }
332
333 /**
334  * gtk_option_menu_get_history:
335  * @option_menu: a #GtkOptionMenu
336  * 
337  * Retrieves the index of the currently selected menu item. The menu
338  * items are numbered from top to bottom, starting with 0. 
339  * 
340  * Return value: index of the selected menu item, or -1 if there are no menu items
341  * Deprecated: Use #GtkComboBox instead.
342  **/
343 gint
344 gtk_option_menu_get_history (GtkOptionMenu *option_menu)
345 {
346   GtkWidget *active_widget;
347   
348   g_return_val_if_fail (GTK_IS_OPTION_MENU (option_menu), -1);
349
350   if (option_menu->menu)
351     {
352       active_widget = gtk_menu_get_active (GTK_MENU (option_menu->menu));
353
354       if (active_widget)
355         return g_list_index (GTK_MENU_SHELL (option_menu->menu)->children,
356                              active_widget);
357       else
358         return -1;
359     }
360   else
361     return -1;
362 }
363
364 static void
365 gtk_option_menu_set_property (GObject            *object,
366                               guint               prop_id,
367                               const GValue       *value,
368                               GParamSpec         *pspec)
369 {
370   GtkOptionMenu *option_menu = GTK_OPTION_MENU (object);
371
372   switch (prop_id)
373     {
374     case PROP_MENU:
375       gtk_option_menu_set_menu (option_menu, g_value_get_object (value));
376       break;
377       
378     default:
379       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
380       break;
381     }
382 }
383
384 static void
385 gtk_option_menu_get_property (GObject            *object,
386                               guint               prop_id,
387                               GValue             *value,
388                               GParamSpec         *pspec)
389 {
390   GtkOptionMenu *option_menu = GTK_OPTION_MENU (object);
391
392   switch (prop_id)
393     {
394     case PROP_MENU:
395       g_value_set_object (value, option_menu->menu);
396       break;
397       
398     default:
399       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
400       break;
401     }
402 }
403
404 static void
405 gtk_option_menu_destroy (GtkObject *object)
406 {
407   GtkOptionMenu *option_menu;
408
409   g_return_if_fail (GTK_IS_OPTION_MENU (object));
410
411   option_menu = GTK_OPTION_MENU (object);
412
413   if (option_menu->menu)
414     gtk_widget_destroy (option_menu->menu);
415
416   if (GTK_OBJECT_CLASS (parent_class)->destroy)
417     (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
418 }
419
420 static void
421 gtk_option_menu_get_props (GtkOptionMenu       *option_menu,
422                            GtkOptionMenuProps  *props)
423 {
424   GtkRequisition *indicator_size;
425   GtkBorder *indicator_spacing;
426   
427   gtk_widget_style_get (GTK_WIDGET (option_menu),
428                         "indicator_size", &indicator_size,
429                         "indicator_spacing", &indicator_spacing,
430                         "interior_focus", &props->interior_focus,
431                         "focus_line_width", &props->focus_width,
432                         "focus_padding", &props->focus_pad,
433                         NULL);
434
435   if (indicator_size)
436     props->indicator_size = *indicator_size;
437   else
438     props->indicator_size = default_props.indicator_size;
439
440   if (indicator_spacing)
441     props->indicator_spacing = *indicator_spacing;
442   else
443     props->indicator_spacing = default_props.indicator_spacing;
444
445   g_free (indicator_size);
446   g_free (indicator_spacing);
447 }
448
449 static void
450 gtk_option_menu_size_request (GtkWidget      *widget,
451                               GtkRequisition *requisition)
452 {
453   GtkOptionMenu *option_menu = GTK_OPTION_MENU (widget);
454   GtkOptionMenuProps props;
455   gint tmp;
456   GtkRequisition child_requisition = { 0, 0 };
457       
458   gtk_option_menu_get_props (option_menu, &props);
459  
460   if (GTK_BIN (option_menu)->child && GTK_WIDGET_VISIBLE (GTK_BIN (option_menu)->child))
461     {
462       gtk_widget_size_request (GTK_BIN (option_menu)->child, &child_requisition);
463
464       requisition->width += child_requisition.width;
465       requisition->height += child_requisition.height;
466     }
467   
468   requisition->width = ((GTK_CONTAINER (widget)->border_width +
469                          GTK_WIDGET (widget)->style->xthickness + props.focus_pad) * 2 +
470                         MAX (child_requisition.width, option_menu->width) +
471                         props.indicator_size.width +
472                         props.indicator_spacing.left + props.indicator_spacing.right +
473                         CHILD_LEFT_SPACING + CHILD_RIGHT_SPACING + props.focus_width * 2);
474   requisition->height = ((GTK_CONTAINER (widget)->border_width +
475                           GTK_WIDGET (widget)->style->ythickness + props.focus_pad) * 2 +
476                          MAX (child_requisition.height, option_menu->height) +
477                          CHILD_TOP_SPACING + CHILD_BOTTOM_SPACING + props.focus_width * 2);
478
479   tmp = (requisition->height - MAX (child_requisition.height, option_menu->height) +
480          props.indicator_size.height + props.indicator_spacing.top + props.indicator_spacing.bottom);
481   requisition->height = MAX (requisition->height, tmp);
482 }
483
484 static void
485 gtk_option_menu_size_allocate (GtkWidget     *widget,
486                                GtkAllocation *allocation)
487 {
488   GtkWidget *child;
489   GtkButton *button = GTK_BUTTON (widget);
490   GtkAllocation child_allocation;
491   GtkOptionMenuProps props;
492   gint border_width;
493     
494   gtk_option_menu_get_props (GTK_OPTION_MENU (widget), &props);
495   border_width = GTK_CONTAINER (widget)->border_width;
496
497   widget->allocation = *allocation;
498   if (GTK_WIDGET_REALIZED (widget))
499     gdk_window_move_resize (button->event_window,
500                             allocation->x + border_width, allocation->y + border_width,
501                             allocation->width - border_width * 2, allocation->height - border_width * 2);
502
503   child = GTK_BIN (widget)->child;
504   if (child && GTK_WIDGET_VISIBLE (child))
505     {
506       gint xthickness = GTK_WIDGET (widget)->style->xthickness;
507       gint ythickness = GTK_WIDGET (widget)->style->ythickness;
508       
509       child_allocation.x = widget->allocation.x + border_width + xthickness + props.focus_width + props.focus_pad + CHILD_LEFT_SPACING;
510       child_allocation.y = widget->allocation.y + border_width + ythickness + props.focus_width + props.focus_pad + CHILD_TOP_SPACING;
511       child_allocation.width = MAX (1, allocation->width - (border_width + xthickness + props.focus_width + props.focus_pad) * 2 -
512                                     props.indicator_size.width - props.indicator_spacing.left - props.indicator_spacing.right -
513                                     CHILD_LEFT_SPACING - CHILD_RIGHT_SPACING);
514       child_allocation.height = MAX (1, allocation->height - (border_width + ythickness + props.focus_width + props.focus_pad) * 2 -
515                                      CHILD_TOP_SPACING - CHILD_BOTTOM_SPACING);
516
517       if (gtk_widget_get_direction (GTK_WIDGET (widget)) == GTK_TEXT_DIR_RTL) 
518         child_allocation.x += props.indicator_size.width + props.indicator_spacing.left + props.indicator_spacing.right;
519
520       gtk_widget_size_allocate (child, &child_allocation);
521     }
522 }
523
524 static void
525 gtk_option_menu_paint (GtkWidget    *widget,
526                        GdkRectangle *area)
527 {
528   GdkRectangle button_area;
529   GtkOptionMenuProps props;
530   gint border_width;
531   gint tab_x;
532
533   g_return_if_fail (GTK_IS_OPTION_MENU (widget));
534   g_return_if_fail (area != NULL);
535
536   if (GTK_WIDGET_DRAWABLE (widget))
537     {
538       border_width = GTK_CONTAINER (widget)->border_width;
539       gtk_option_menu_get_props (GTK_OPTION_MENU (widget), &props);
540
541       button_area.x = widget->allocation.x + border_width;
542       button_area.y = widget->allocation.y + border_width;
543       button_area.width = widget->allocation.width - 2 * border_width;
544       button_area.height = widget->allocation.height - 2 * border_width;
545
546       if (!props.interior_focus && GTK_WIDGET_HAS_FOCUS (widget))
547         {
548           button_area.x += props.focus_width + props.focus_pad;
549           button_area.y += props.focus_width + props.focus_pad;
550           button_area.width -= 2 * (props.focus_width + props.focus_pad);
551           button_area.height -= 2 * (props.focus_width + props.focus_pad);
552         }
553       
554       gtk_paint_box (widget->style, widget->window,
555                      GTK_WIDGET_STATE (widget), GTK_SHADOW_OUT,
556                      area, widget, "optionmenu",
557                      button_area.x, button_area.y,
558                      button_area.width, button_area.height);
559       
560       if (gtk_widget_get_direction (GTK_WIDGET (widget)) == GTK_TEXT_DIR_RTL) 
561         tab_x = button_area.x + props.indicator_spacing.right + 
562           widget->style->xthickness;
563       else
564         tab_x = button_area.x + button_area.width - 
565           props.indicator_size.width - props.indicator_spacing.right -
566           widget->style->xthickness;
567
568       gtk_paint_tab (widget->style, widget->window,
569                      GTK_WIDGET_STATE (widget), GTK_SHADOW_OUT,
570                      area, widget, "optionmenutab",
571                      tab_x,
572                      button_area.y + (button_area.height - props.indicator_size.height) / 2,
573                      props.indicator_size.width, props.indicator_size.height);
574       
575       if (GTK_WIDGET_HAS_FOCUS (widget))
576         {
577           if (props.interior_focus)
578             {
579               button_area.x += widget->style->xthickness + props.focus_pad;
580               button_area.y += widget->style->ythickness + props.focus_pad;
581               button_area.width -= 2 * (widget->style->xthickness + props.focus_pad) +
582                       props.indicator_spacing.left +
583                       props.indicator_spacing.right +
584                       props.indicator_size.width;
585               button_area.height -= 2 * (widget->style->ythickness + props.focus_pad);
586               if (gtk_widget_get_direction (GTK_WIDGET (widget)) == GTK_TEXT_DIR_RTL) 
587                 button_area.x += props.indicator_spacing.left +
588                   props.indicator_spacing.right +
589                   props.indicator_size.width;
590             }
591           else
592             {
593               button_area.x -= props.focus_width + props.focus_pad;
594               button_area.y -= props.focus_width + props.focus_pad;
595               button_area.width += 2 * (props.focus_width + props.focus_pad);
596               button_area.height += 2 * (props.focus_width + props.focus_pad);
597             }
598             
599           gtk_paint_focus (widget->style, widget->window, GTK_WIDGET_STATE (widget),
600                            area, widget, "button",
601                            button_area.x, 
602                            button_area.y, 
603                            button_area.width,
604                            button_area.height);
605         }
606     }
607 }
608
609 static gint
610 gtk_option_menu_expose (GtkWidget      *widget,
611                         GdkEventExpose *event)
612 {
613   g_return_val_if_fail (GTK_IS_OPTION_MENU (widget), FALSE);
614   g_return_val_if_fail (event != NULL, FALSE);
615
616   if (GTK_WIDGET_DRAWABLE (widget))
617     {
618       gtk_option_menu_paint (widget, &event->area);
619
620
621       /* The following code tries to draw the child in two places at
622        * once. It fails miserably for several reasons
623        *
624        * - If the child is not no-window, removing generates
625        *   more expose events. Bad, bad, bad.
626        * 
627        * - Even if the child is no-window, removing it now (properly)
628        *   clears the space where it was, so it does no good
629        */
630       
631 #if 0
632       remove_child = FALSE;
633       child = GTK_BUTTON (widget)->child;
634
635       if (!child)
636         {
637           if (!GTK_OPTION_MENU (widget)->menu)
638             return FALSE;
639           gtk_option_menu_update_contents (GTK_OPTION_MENU (widget));
640           child = GTK_BUTTON (widget)->child;
641           if (!child)
642             return FALSE;
643           remove_child = TRUE;
644         }
645
646       child_event = *event;
647
648       if (GTK_WIDGET_NO_WINDOW (child) &&
649           gtk_widget_intersect (child, &event->area, &child_event.area))
650         gtk_widget_event (child, (GdkEvent*) &child_event);
651
652       if (remove_child)
653         gtk_option_menu_remove_contents (GTK_OPTION_MENU (widget));
654 #else
655       if (GTK_BIN (widget)->child)
656         gtk_container_propagate_expose (GTK_CONTAINER (widget),
657                                         GTK_BIN (widget)->child,
658                                         event);
659 #endif /* 0 */
660     }
661
662   return FALSE;
663 }
664
665 static gint
666 gtk_option_menu_button_press (GtkWidget      *widget,
667                               GdkEventButton *event)
668 {
669   GtkOptionMenu *option_menu;
670   GtkWidget *menu_item;
671
672   g_return_val_if_fail (GTK_IS_OPTION_MENU (widget), FALSE);
673   g_return_val_if_fail (event != NULL, FALSE);
674
675   option_menu = GTK_OPTION_MENU (widget);
676
677   if ((event->type == GDK_BUTTON_PRESS) &&
678       (event->button == 1))
679     {
680       gtk_option_menu_remove_contents (option_menu);
681       gtk_menu_popup (GTK_MENU (option_menu->menu), NULL, NULL,
682                       gtk_option_menu_position, option_menu,
683                       event->button, event->time);
684       menu_item = gtk_menu_get_active (GTK_MENU (option_menu->menu));
685       if (menu_item)
686         gtk_menu_shell_select_item (GTK_MENU_SHELL (option_menu->menu), menu_item);
687       return TRUE;
688     }
689
690   return FALSE;
691 }
692
693 static gint
694 gtk_option_menu_key_press (GtkWidget   *widget,
695                            GdkEventKey *event)
696 {
697   GtkOptionMenu *option_menu;
698   GtkWidget *menu_item;
699
700   g_return_val_if_fail (GTK_IS_OPTION_MENU (widget), FALSE);
701   g_return_val_if_fail (event != NULL, FALSE);
702
703   option_menu = GTK_OPTION_MENU (widget);
704
705   switch (event->keyval)
706     {
707     case GDK_KP_Space:
708     case GDK_space:
709       gtk_option_menu_remove_contents (option_menu);
710       gtk_menu_popup (GTK_MENU (option_menu->menu), NULL, NULL,
711                       gtk_option_menu_position, option_menu,
712                       0, event->time);
713       menu_item = gtk_menu_get_active (GTK_MENU (option_menu->menu));
714       if (menu_item)
715         gtk_menu_shell_select_item (GTK_MENU_SHELL (option_menu->menu), menu_item);
716       return TRUE;
717     }
718   
719   return FALSE;
720 }
721
722 static void
723 gtk_option_menu_selection_done (GtkMenuShell  *menu_shell,
724                                 GtkOptionMenu *option_menu)
725 {
726   g_return_if_fail (menu_shell != NULL);
727   g_return_if_fail (GTK_IS_OPTION_MENU (option_menu));
728
729   gtk_option_menu_update_contents (option_menu);
730 }
731
732 static void
733 gtk_option_menu_changed (GtkOptionMenu *option_menu)
734 {
735   g_return_if_fail (GTK_IS_OPTION_MENU (option_menu));
736
737   g_signal_emit (option_menu, signals[CHANGED], 0);
738 }
739
740 static void
741 gtk_option_menu_select_first_sensitive (GtkOptionMenu *option_menu)
742 {
743   if (option_menu->menu)
744     {
745       GList *children = GTK_MENU_SHELL (option_menu->menu)->children;
746       gint index = 0;
747
748       while (children)
749         {
750           if (GTK_WIDGET_SENSITIVE (children->data))
751             {
752               gtk_option_menu_set_history (option_menu, index);
753               return;
754             }
755           
756           children = children->next;
757           index++;
758         }
759     }
760 }
761
762 static void
763 gtk_option_menu_item_state_changed_cb (GtkWidget      *widget,
764                                        GtkStateType    previous_state,
765                                        GtkOptionMenu  *option_menu)
766 {
767   GtkWidget *child = GTK_BIN (option_menu)->child;
768
769   if (child && GTK_WIDGET_SENSITIVE (child) != GTK_WIDGET_IS_SENSITIVE (widget))
770     gtk_widget_set_sensitive (child, GTK_WIDGET_IS_SENSITIVE (widget));
771 }
772
773 static void
774 gtk_option_menu_item_destroy_cb (GtkWidget     *widget,
775                                  GtkOptionMenu *option_menu)
776 {
777   GtkWidget *child = GTK_BIN (option_menu)->child;
778
779   if (child)
780     {
781       g_object_ref (child);
782       gtk_option_menu_remove_contents (option_menu);
783       gtk_widget_destroy (child);
784       g_object_unref (child);
785
786       gtk_option_menu_select_first_sensitive (option_menu);
787     }
788 }
789
790 static void
791 gtk_option_menu_update_contents (GtkOptionMenu *option_menu)
792 {
793   GtkWidget *child;
794   GtkRequisition child_requisition;
795
796   g_return_if_fail (GTK_IS_OPTION_MENU (option_menu));
797
798   if (option_menu->menu)
799     {
800       GtkWidget *old_item = option_menu->menu_item;
801       
802       gtk_option_menu_remove_contents (option_menu);
803
804       option_menu->menu_item = gtk_menu_get_active (GTK_MENU (option_menu->menu));
805       if (option_menu->menu_item)
806         {
807           g_object_ref (option_menu->menu_item);
808           child = GTK_BIN (option_menu->menu_item)->child;
809           if (child)
810             {
811               if (!GTK_WIDGET_IS_SENSITIVE (option_menu->menu_item))
812                 gtk_widget_set_sensitive (child, FALSE);
813               gtk_widget_reparent (child, GTK_WIDGET (option_menu));
814             }
815
816           g_signal_connect (option_menu->menu_item, "state_changed",
817                             G_CALLBACK (gtk_option_menu_item_state_changed_cb), option_menu);
818           g_signal_connect (option_menu->menu_item, "destroy",
819                             G_CALLBACK (gtk_option_menu_item_destroy_cb), option_menu);
820
821           gtk_widget_size_request (child, &child_requisition);
822           gtk_widget_size_allocate (GTK_WIDGET (option_menu),
823                                     &(GTK_WIDGET (option_menu)->allocation));
824
825           if (GTK_WIDGET_DRAWABLE (option_menu))
826             gtk_widget_queue_draw (GTK_WIDGET (option_menu));
827         }
828
829       if (old_item != option_menu->menu_item)
830         gtk_option_menu_changed (option_menu);
831     }
832 }
833
834 static void
835 gtk_option_menu_remove_contents (GtkOptionMenu *option_menu)
836 {
837   GtkWidget *child;
838   
839   g_return_if_fail (GTK_IS_OPTION_MENU (option_menu));
840
841   if (option_menu->menu_item)
842     {
843       child = GTK_BIN (option_menu)->child;
844   
845       if (child)
846         {
847           gtk_widget_set_sensitive (child, TRUE);
848           gtk_widget_set_state (child, GTK_STATE_NORMAL);
849           gtk_widget_reparent (child, option_menu->menu_item);
850         }
851
852       g_signal_handlers_disconnect_by_func (option_menu->menu_item,
853                                             gtk_option_menu_item_state_changed_cb,
854                                             option_menu);                                    
855       g_signal_handlers_disconnect_by_func (option_menu->menu_item,
856                                             gtk_option_menu_item_destroy_cb,
857                                             option_menu);   
858       
859       g_object_unref (option_menu->menu_item);
860       option_menu->menu_item = NULL;
861     }
862 }
863
864 static void
865 gtk_option_menu_calc_size (GtkOptionMenu *option_menu)
866 {
867   GtkWidget *child;
868   GList *children;
869   GtkRequisition child_requisition;
870   gint old_width = option_menu->width;
871   gint old_height = option_menu->height;
872
873   g_return_if_fail (GTK_IS_OPTION_MENU (option_menu));
874
875   option_menu->width = 0;
876   option_menu->height = 0;
877
878   if (option_menu->menu)
879     {
880       children = GTK_MENU_SHELL (option_menu->menu)->children;
881       while (children)
882         {
883           child = children->data;
884           children = children->next;
885
886           if (GTK_WIDGET_VISIBLE (child))
887             {
888               GtkWidget *inner = GTK_BIN (child)->child;
889
890               if (inner)
891                 {
892                   gtk_widget_size_request (inner, &child_requisition);
893
894                   option_menu->width = MAX (option_menu->width, child_requisition.width);
895                   option_menu->height = MAX (option_menu->height, child_requisition.height);
896                 }
897             }
898         }
899     }
900
901   if (old_width != option_menu->width || old_height != option_menu->height)
902     gtk_widget_queue_resize (GTK_WIDGET (option_menu));
903 }
904
905 static void
906 gtk_option_menu_position (GtkMenu  *menu,
907                           gint     *x,
908                           gint     *y,
909                           gboolean *push_in,
910                           gpointer  user_data)
911 {
912   GtkOptionMenu *option_menu;
913   GtkWidget *active;
914   GtkWidget *child;
915   GtkWidget *widget;
916   GtkRequisition requisition;
917   GList *children;
918   gint screen_width;
919   gint menu_xpos;
920   gint menu_ypos;
921   gint menu_width;
922
923   g_return_if_fail (GTK_IS_OPTION_MENU (user_data));
924
925   option_menu = GTK_OPTION_MENU (user_data);
926   widget = GTK_WIDGET (option_menu);
927
928   gtk_widget_get_child_requisition (GTK_WIDGET (menu), &requisition);
929   menu_width = requisition.width;
930
931   active = gtk_menu_get_active (GTK_MENU (option_menu->menu));
932   gdk_window_get_origin (widget->window, &menu_xpos, &menu_ypos);
933
934   menu_xpos += widget->allocation.x;
935   menu_ypos += widget->allocation.y + widget->allocation.height / 2 - 2;
936
937   if (active != NULL)
938     {
939       gtk_widget_get_child_requisition (active, &requisition);
940       menu_ypos -= requisition.height / 2;
941     }
942
943   children = GTK_MENU_SHELL (option_menu->menu)->children;
944   while (children)
945     {
946       child = children->data;
947
948       if (active == child)
949         break;
950
951       if (GTK_WIDGET_VISIBLE (child))
952         {
953           gtk_widget_get_child_requisition (child, &requisition);
954           menu_ypos -= requisition.height;
955         }
956
957       children = children->next;
958     }
959
960   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
961     menu_xpos = menu_xpos + widget->allocation.width - menu_width;
962
963   /* Clamp the position on screen */
964   screen_width = gdk_screen_get_width (gtk_widget_get_screen (widget));
965   
966   if (menu_xpos < 0)
967     menu_xpos = 0;
968   else if ((menu_xpos + menu_width) > screen_width)
969     menu_xpos -= ((menu_xpos + menu_width) - screen_width);
970
971   *x = menu_xpos;
972   *y = menu_ypos;
973   *push_in = TRUE;
974 }
975
976
977 static void
978 gtk_option_menu_show_all (GtkWidget *widget)
979 {
980   GtkContainer *container;
981   GtkOptionMenu *option_menu;
982   
983   g_return_if_fail (GTK_IS_OPTION_MENU (widget));
984   container = GTK_CONTAINER (widget);
985   option_menu = GTK_OPTION_MENU (widget);
986
987   gtk_widget_show (widget);
988   gtk_container_foreach (container, (GtkCallback) gtk_widget_show_all, NULL);
989   if (option_menu->menu)
990     gtk_widget_show_all (option_menu->menu);
991   if (option_menu->menu_item)
992     gtk_widget_show_all (option_menu->menu_item);
993 }
994
995
996 static void
997 gtk_option_menu_hide_all (GtkWidget *widget)
998 {
999   GtkContainer *container;
1000
1001   g_return_if_fail (GTK_IS_OPTION_MENU (widget));
1002   container = GTK_CONTAINER (widget);
1003
1004   gtk_widget_hide (widget);
1005   gtk_container_foreach (container, (GtkCallback) gtk_widget_hide_all, NULL);
1006 }
1007
1008 static gboolean
1009 gtk_option_menu_mnemonic_activate (GtkWidget *widget,
1010                                    gboolean   group_cycling)
1011 {
1012   gtk_widget_grab_focus (widget);
1013   return TRUE;
1014 }
1015
1016 static gint
1017 gtk_option_menu_scroll_event (GtkWidget          *widget,
1018                               GdkEventScroll     *event)
1019 {
1020   GtkOptionMenu *option_menu = GTK_OPTION_MENU (widget);
1021   gint index;
1022   gint n_children;
1023   gint index_dir;
1024   GList *l;
1025   GtkMenuItem *item;
1026     
1027   index = gtk_option_menu_get_history (option_menu);
1028
1029   if (index != -1)
1030     {
1031       n_children = g_list_length (GTK_MENU_SHELL (option_menu->menu)->children);
1032       
1033       if (event->direction == GDK_SCROLL_UP)
1034         index_dir = -1;
1035       else
1036         index_dir = 1;
1037
1038
1039       while (TRUE)
1040         {
1041           index += index_dir;
1042
1043           if (index < 0)
1044             break;
1045           if (index >= n_children)
1046             break;
1047
1048           l = g_list_nth (GTK_MENU_SHELL (option_menu->menu)->children, index);
1049           item = GTK_MENU_ITEM (l->data);
1050           if (GTK_WIDGET_VISIBLE (item) && GTK_WIDGET_IS_SENSITIVE (item))
1051             {
1052               gtk_option_menu_set_history (option_menu, index);
1053               gtk_menu_item_activate (GTK_MENU_ITEM (item));
1054               break;
1055             }
1056               
1057         }
1058     }
1059
1060   return TRUE;
1061 }
1062