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