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