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