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