]> Pileus Git - ~andy/gtk/blob - gtk/gtkcombo.c
fix popup calculations (removal of FIXMEs), changes from Lars Hamann.
[~andy/gtk] / gtk / gtkcombo.c
1 /* gtkcombo - combo widget for gtk+
2  * Copyright 1997 Paolo Molaro
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library 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  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the Free
16  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 /* $Id$ */
20
21 #include <string.h>
22
23 #include "gtkarrow.h"
24 #include "gtklabel.h"
25 #include "gtklist.h"
26 #include "gtkentry.h"
27 #include "gtkbutton.h"
28 #include "gtklistitem.h"
29 #include "gtkscrolledwindow.h"
30 #include "gtkmain.h"
31 #include "gtksignal.h"
32 #include "gtkwindow.h"
33 #include "gdk/gdkkeysyms.h"
34 #include "gtkcombo.h"
35 #include "gtkframe.h"
36
37 const gchar *gtk_combo_string_key = "gtk-combo-string-value";
38
39 #define COMBO_LIST_MAX_HEIGHT 400
40
41 static void         gtk_combo_class_init      (GtkComboClass *klass);
42 static void         gtk_combo_init            (GtkCombo      *combo);
43 static void         gtk_combo_destroy         (GtkObject     *combo);
44 static GtkListItem *gtk_combo_find            (GtkCombo      *combo);
45 static gchar *      gtk_combo_func            (GtkListItem  *li);
46 static gint         gtk_combo_focus_idle      (GtkCombo      *combo);
47 static gint         gtk_combo_entry_focus_out (GtkEntry      *entry, 
48                                                GdkEventFocus *event, 
49                                                GtkCombo      *combo);
50 static void         gtk_combo_get_pos         (GtkCombo      *combo, 
51                                                gint          *x, 
52                                                gint          *y, 
53                                                gint          *height, 
54                                                gint          *width);
55 static void         gtk_combo_popup_list      (GtkButton     *button, 
56                                                GtkCombo      *combo);
57 static void         gtk_combo_update_entry    (GtkList       *list, 
58                                                GtkCombo      *combo);
59 static void         gtk_combo_update_list     (GtkEntry      *entry, 
60                                                GtkCombo      *combo);
61 static gint         gtk_combo_button_press    (GtkWidget     *widget,
62                                                GdkEvent      *event,
63                                                GtkCombo      *combo);
64 static gint         gtk_combo_list_key_press  (GtkWidget     *widget, 
65                                                GdkEventKey   *event, 
66                                                GtkCombo      *combo);
67 static gint         gtk_combo_entry_key_press (GtkEntry      *widget, 
68                                                GdkEventKey   *event, 
69                                                GtkCombo      *combo);
70 static void         gtk_combo_item_destroy    (GtkObject     *object);
71 static void         gtk_combo_size_allocate   (GtkWidget     *widget,
72                                                GtkAllocation *allocation);
73
74 static GtkHBoxClass *parent_class = NULL;
75
76 void
77 gtk_combo_class_init (GtkComboClass * klass)
78 {
79   GtkObjectClass *oclass;
80   GtkWidgetClass *widget_class;
81
82   parent_class = gtk_type_class (gtk_hbox_get_type ());
83   oclass = (GtkObjectClass *) klass;
84   widget_class = (GtkWidgetClass *) klass;
85
86   oclass->destroy = gtk_combo_destroy;
87   
88   widget_class->size_allocate = gtk_combo_size_allocate;
89 }
90
91 static void
92 gtk_combo_destroy (GtkObject * combo)
93 {
94   gtk_widget_destroy (GTK_COMBO (combo)->popwin);
95   gtk_widget_unref (GTK_COMBO (combo)->popwin);
96
97   if (GTK_OBJECT_CLASS (parent_class)->destroy)
98     (*GTK_OBJECT_CLASS (parent_class)->destroy) (combo);
99 }
100
101 static int
102 gtk_combo_entry_key_press (GtkEntry * entry, GdkEventKey * event, GtkCombo * combo)
103 {
104   GList *li;
105   /* completion? */
106   /*if ( event->keyval == GDK_Tab ) {
107      gtk_signal_emit_stop_by_name (GTK_OBJECT (entry), "key_press_event");
108      return TRUE;
109      } else */
110   if (!combo->use_arrows || !GTK_LIST (combo->list)->children)
111     return FALSE;
112   li = g_list_find (GTK_LIST (combo->list)->children, gtk_combo_find (combo));
113
114   if ((event->keyval == GDK_Up)
115       || (event->keyval == GDK_KP_Up)
116       || ((event->state & GDK_MOD1_MASK) && ((event->keyval == 'p') || (event->keyval == 'P'))))
117     {
118       if (li)
119         li = li->prev;
120       if (!li && combo->use_arrows_always)
121         {
122           li = g_list_last (GTK_LIST (combo->list)->children);
123         }
124       if (li)
125         {
126           gtk_list_select_child (GTK_LIST (combo->list), GTK_WIDGET (li->data));
127           gtk_signal_emit_stop_by_name (GTK_OBJECT (entry), "key_press_event");
128           return TRUE;
129         }
130     }
131   else if ((event->keyval == GDK_Down)
132            || (event->keyval == GDK_KP_Down)
133            || ((event->state & GDK_MOD1_MASK) && ((event->keyval == 'n') || (event->keyval == 'N'))))
134     {
135       if (li)
136         li = li->next;
137       if (!li && combo->use_arrows_always)
138         {
139           li = GTK_LIST (combo->list)->children;
140         }
141       if (li)
142         {
143           gtk_list_select_child (GTK_LIST (combo->list), GTK_WIDGET (li->data));
144           gtk_signal_emit_stop_by_name (GTK_OBJECT (entry), "key_press_event");
145           return TRUE;
146         }
147     }
148   return FALSE;
149 }
150
151 static GtkListItem *
152 gtk_combo_find (GtkCombo * combo)
153 {
154   gchar *text;
155   gchar *ltext;
156   GList *clist;
157   int (*string_compare) (const char *, const char *);
158
159   if (combo->case_sensitive)
160     string_compare = strcmp;
161   else
162     string_compare = g_strcasecmp;
163
164   text = gtk_entry_get_text (GTK_ENTRY (combo->entry));
165   clist = GTK_LIST (combo->list)->children;
166
167   while (clist && clist->data)
168     {
169       ltext = gtk_combo_func (GTK_LIST_ITEM (clist->data));
170       if (!ltext)
171         continue;
172       if (!(*string_compare) (ltext, text))
173         return (GtkListItem *) clist->data;
174       clist = clist->next;
175     }
176
177   return NULL;
178 }
179
180 static gchar *
181 gtk_combo_func (GtkListItem * li)
182 {
183   GtkWidget *label;
184   gchar *ltext = NULL;
185
186   ltext = (gchar *) gtk_object_get_data (GTK_OBJECT (li), gtk_combo_string_key);
187   if (!ltext)
188     {
189       label = GTK_BIN (li)->child;
190       if (!label || !GTK_IS_LABEL (label))
191         return NULL;
192       gtk_label_get (GTK_LABEL (label), &ltext);
193     }
194   return ltext;
195 }
196
197 static gint
198 gtk_combo_focus_idle (GtkCombo * combo)
199 {
200   if (combo)
201     gtk_widget_grab_focus (combo->entry);
202   return FALSE;
203 }
204
205 static gint
206 gtk_combo_entry_focus_out (GtkEntry * entry, GdkEventFocus * event, GtkCombo * combo)
207 {
208
209   if (combo->value_in_list && !gtk_combo_find (combo))
210     {
211       /* gdk_beep(); *//* this can be annoying */
212       if (combo->ok_if_empty && !strcmp (gtk_entry_get_text (entry), ""))
213         return FALSE;
214 #ifdef TEST
215       printf ("INVALID ENTRY: `%s'\n", gtk_entry_get_text (entry));
216 #endif
217       gtk_grab_add (GTK_WIDGET (combo));
218       /* this is needed because if we call gtk_widget_grab_focus() 
219          it isn't guaranteed it's the *last* call before the main-loop,
220          so the focus can be lost anyway...
221          the signal_emit_stop doesn't seem to work either...
222        */
223       gtk_idle_add ((GtkFunction) gtk_combo_focus_idle, combo);
224       /*gtk_signal_emit_stop_by_name(GTK_OBJECT(entry), "focus_out_event"); */
225       return TRUE;
226     }
227   return FALSE;
228 }
229
230 static void
231 gtk_combo_get_pos (GtkCombo * combo, gint * x, gint * y, gint * height, gint * width)
232 {
233   GtkBin *popwin;
234   GtkWidget *widget;
235   GtkScrolledWindow *popup;
236
237   gint real_height;
238   GtkRequisition list_requisition;
239   gboolean show_hscroll = FALSE;
240   gboolean show_vscroll = FALSE;
241   gint avail_height;
242   gint min_height;
243   gint alloc_width;
244   gint work_height;
245   gint old_height;
246   gint old_width;
247
248   widget = GTK_WIDGET(combo);
249   popup  = GTK_SCROLLED_WINDOW (combo->popup);
250   popwin = GTK_BIN (combo->popwin);
251
252   gdk_window_get_origin (combo->entry->window, x, y);
253   real_height = MIN (combo->entry->requisition.height, 
254                      combo->entry->allocation.height);
255   *y += real_height;
256   avail_height = gdk_screen_height () - *y;
257
258   gtk_widget_size_request (combo->list, &list_requisition);
259   min_height = MIN (list_requisition.height, 
260                     popup->vscrollbar->requisition.height);
261
262
263   alloc_width = widget->allocation.width -
264     2 * popwin->child->style->klass->xthickness -
265     2 * GTK_CONTAINER (popwin->child)->border_width -
266     2 * GTK_CONTAINER (combo->popup)->border_width -
267     2 * GTK_CONTAINER (popup->viewport)->border_width - 
268     2 * popup->viewport->style->klass->xthickness;
269
270   work_height =  
271     2 * popwin->child->style->klass->ythickness +
272     2 * GTK_CONTAINER (popwin->child)->border_width +
273     2 * GTK_CONTAINER (combo->popup)->border_width +
274     2 * GTK_CONTAINER (popup->viewport)->border_width +
275     2 * popup->viewport->style->klass->xthickness;
276
277   do 
278     {
279       old_width = alloc_width;
280       old_height = work_height;
281
282       if (!show_hscroll &&
283           alloc_width < list_requisition.width)
284         {
285           work_height += popup->hscrollbar->requisition.height +
286             GTK_SCROLLED_WINDOW_CLASS 
287             (GTK_OBJECT (combo->popup)->klass)->scrollbar_spacing;
288           show_hscroll = TRUE;
289         }
290       if (!show_vscroll && 
291           work_height + list_requisition.height > avail_height)
292         {
293           if (work_height + min_height > avail_height && 
294               *y - real_height > avail_height)
295             {
296               *y -= (work_height +list_requisition.height + real_height);
297               break;
298             }
299           alloc_width -= 
300             popup->vscrollbar->requisition.width +
301             GTK_SCROLLED_WINDOW_CLASS 
302             (GTK_OBJECT (combo->popup)->klass)->scrollbar_spacing;
303           show_vscroll = TRUE;
304         }
305     } while (old_width != alloc_width || old_height != work_height);
306
307   *width = widget->allocation.width;
308   if (show_vscroll)
309       *height = avail_height;
310   else
311     *height = work_height + list_requisition.height;
312   
313   if (*x < 0)
314     *x = 0;
315 }
316
317 static void
318 gtk_combo_popup_list (GtkButton * button, GtkCombo * combo)
319 {
320   gint height, width, x, y;
321   gint old_width, old_height;
322
323   if (!GTK_LIST (combo->list)->children)
324     return;
325
326   old_width = combo->popwin->allocation.width;
327   old_height  = combo->popwin->allocation.height;
328
329   gtk_combo_get_pos (combo, &x, &y, &height, &width);
330
331   /* workaround for gtk_scrolled_window_size_allocate bug */
332   if (old_width != width || old_height != height)
333     {
334       gtk_widget_hide (GTK_SCROLLED_WINDOW (combo->popup)->hscrollbar);
335       gtk_widget_hide (GTK_SCROLLED_WINDOW (combo->popup)->vscrollbar);
336     }
337
338   gtk_widget_set_uposition (combo->popwin, x, y);
339   gtk_widget_set_usize (combo->popwin, width, height);
340   gtk_widget_realize (combo->popwin);
341   gdk_window_resize (combo->popwin->window, width, height);
342   gtk_widget_show (combo->popwin);
343
344   gtk_widget_grab_focus (combo->popwin);
345   gtk_grab_add (combo->popwin);
346   gdk_pointer_grab (combo->popwin->window, TRUE,
347                     GDK_BUTTON_PRESS_MASK, NULL, NULL, GDK_CURRENT_TIME);
348 }
349
350 #if 0
351 static void
352 prelight_bug (GtkButton * b, GtkCombo * combo)
353 {
354   /* avoid prelight state... */
355   gtk_widget_set_state (combo->button, GTK_STATE_NORMAL);
356
357 }
358 #endif
359
360 static void
361 gtk_combo_update_entry (GtkList * list, GtkCombo * combo)
362 {
363   char *text;
364
365   gtk_grab_remove (GTK_WIDGET (combo));
366   gtk_signal_handler_block (GTK_OBJECT (list), combo->list_change_id);
367   if (list->selection)
368     {
369       text = gtk_combo_func (GTK_LIST_ITEM (list->selection->data));
370       if (!text)
371         text = "";
372       gtk_entry_set_text (GTK_ENTRY (combo->entry), text);
373     }
374   gtk_widget_hide (combo->popwin);
375   gtk_grab_remove (combo->popwin);
376   gdk_pointer_ungrab (GDK_CURRENT_TIME);
377   gtk_signal_handler_unblock (GTK_OBJECT (list), combo->list_change_id);
378 }
379
380 static void
381 gtk_combo_update_list (GtkEntry * entry, GtkCombo * combo)
382 {
383   GtkList *list = GTK_LIST (combo->list);
384   GList *slist = list->selection;
385   GtkListItem *li;
386
387   gtk_grab_remove (GTK_WIDGET (combo));
388
389   gtk_signal_handler_block (GTK_OBJECT (entry), combo->entry_change_id);
390   if (slist && slist->data)
391     gtk_list_unselect_child (list, GTK_WIDGET (slist->data));
392   li = gtk_combo_find (combo);
393   if (li)
394     gtk_list_select_child (list, GTK_WIDGET (li));
395   gtk_signal_handler_unblock (GTK_OBJECT (entry), combo->entry_change_id);
396 }
397
398 static gint
399 gtk_combo_button_press (GtkWidget * widget, GdkEvent * event, GtkCombo * combo)
400 {
401   GtkWidget *child;
402
403   child = gtk_get_event_widget (event);
404
405   /* We don't ask for button press events on the grab widget, so
406    *  if an event is reported directly to the grab widget, it must
407    *  be on a window outside the application (and thus we remove
408    *  the popup window). Otherwise, we check if the widget is a child
409    *  of the grab widget, and only remove the popup window if it
410    *  is not.
411    */
412   if (child != widget)
413     {
414       while (child)
415         {
416           if (child == widget)
417             return FALSE;
418           child = child->parent;
419         }
420     }
421
422   gtk_widget_hide (combo->popwin);
423   gtk_grab_remove (combo->popwin);
424   gdk_pointer_ungrab (GDK_CURRENT_TIME);
425
426   return TRUE;
427 }
428
429 static int
430 gtk_combo_list_key_press (GtkWidget * widget, GdkEventKey * event, GtkCombo * combo)
431 {
432   if (event->keyval == GDK_Escape)
433     {
434       gtk_widget_hide (combo->popwin);
435       gtk_grab_remove (combo->popwin);
436       gdk_pointer_ungrab (GDK_CURRENT_TIME);
437       return TRUE;
438     }
439   return FALSE;
440 }
441
442 void
443 gtk_combo_init (GtkCombo * combo)
444 {
445   GtkWidget *arrow;
446   GtkWidget *frame;
447
448   combo->case_sensitive = 0;
449   combo->value_in_list = 0;
450   combo->ok_if_empty = 1;
451   combo->use_arrows = 1;
452   combo->use_arrows_always = 0;
453   combo->entry = gtk_entry_new ();
454   combo->button = gtk_button_new ();
455   arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_OUT);
456   gtk_widget_show (arrow);
457   gtk_container_add (GTK_CONTAINER (combo->button), arrow);
458   gtk_box_pack_start (GTK_BOX (combo), combo->entry, TRUE, TRUE, 0);
459   gtk_box_pack_end (GTK_BOX (combo), combo->button, FALSE, FALSE, 0);
460   gtk_widget_show (combo->entry);
461   gtk_widget_show (combo->button);
462   combo->entry_change_id = gtk_signal_connect (GTK_OBJECT (combo->entry), "changed",
463                               (GtkSignalFunc) gtk_combo_update_list, combo);
464   gtk_signal_connect (GTK_OBJECT (combo->entry), "key_press_event",
465                       (GtkSignalFunc) gtk_combo_entry_key_press, combo);
466   gtk_signal_connect_after (GTK_OBJECT (combo->entry), "focus_out_event",
467                             (GtkSignalFunc) gtk_combo_entry_focus_out, combo);
468   gtk_signal_connect (GTK_OBJECT (combo->entry), "activate",
469                       (GtkSignalFunc) gtk_combo_popup_list, combo);
470   gtk_signal_connect (GTK_OBJECT (combo->button), "clicked",
471                       (GtkSignalFunc) gtk_combo_popup_list, combo);
472   /*gtk_signal_connect(GTK_OBJECT(combo->button), "clicked",
473      (GtkSignalFunc)prelight_bug, combo); */
474
475   combo->popwin = gtk_window_new (GTK_WINDOW_POPUP);
476   gtk_widget_ref (combo->popwin);
477   gtk_window_set_policy (GTK_WINDOW (combo->popwin), 1, 1, 0);
478
479   frame = gtk_frame_new (NULL);
480   gtk_container_add (GTK_CONTAINER (combo->popwin), frame);
481   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT);
482   gtk_widget_show (frame);
483
484   combo->popup = gtk_scrolled_window_new (NULL, NULL);
485   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (combo->popup),
486                                 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
487   combo->list = gtk_list_new ();
488   gtk_list_set_selection_mode(GTK_LIST(combo->list), GTK_SELECTION_BROWSE);
489   gtk_container_add (GTK_CONTAINER (frame), combo->popup);
490   gtk_container_add (GTK_CONTAINER (combo->popup), combo->list);
491   gtk_widget_show (combo->list);
492   gtk_widget_show (combo->popup);
493   gtk_widget_set_events (combo->popwin, gtk_widget_get_events (combo->popwin) | GDK_KEY_PRESS_MASK);
494   combo->list_change_id = gtk_signal_connect (GTK_OBJECT (combo->list), "selection_changed",
495                              (GtkSignalFunc) gtk_combo_update_entry, combo);
496   gtk_signal_connect (GTK_OBJECT (combo->popwin), "key_press_event",
497                       (GtkSignalFunc) gtk_combo_list_key_press, combo);
498   gtk_signal_connect (GTK_OBJECT (combo->popwin), "button_press_event",
499                       GTK_SIGNAL_FUNC (gtk_combo_button_press), combo);
500   
501 }
502
503 guint
504 gtk_combo_get_type ()
505 {
506   static guint combo_type = 0;
507
508   if (!combo_type)
509     {
510       GtkTypeInfo combo_info =
511       {
512         "GtkCombo",
513         sizeof (GtkCombo),
514         sizeof (GtkComboClass),
515         (GtkClassInitFunc) gtk_combo_class_init,
516         (GtkObjectInitFunc) gtk_combo_init,
517         (GtkArgSetFunc) NULL,
518         (GtkArgGetFunc) NULL,
519       };
520       combo_type = gtk_type_unique (gtk_hbox_get_type (), &combo_info);
521     }
522   return combo_type;
523 }
524
525 GtkWidget *
526 gtk_combo_new ()
527 {
528   return GTK_WIDGET (gtk_type_new (gtk_combo_get_type ()));
529 }
530
531 void
532 gtk_combo_set_value_in_list (GtkCombo * combo, gint val, gint ok_if_empty)
533 {
534   g_return_if_fail (combo != NULL);
535   g_return_if_fail (GTK_IS_COMBO (combo));
536
537   combo->value_in_list = val;
538   combo->ok_if_empty = ok_if_empty;
539 }
540
541 void
542 gtk_combo_set_case_sensitive (GtkCombo * combo, gint val)
543 {
544   g_return_if_fail (combo != NULL);
545   g_return_if_fail (GTK_IS_COMBO (combo));
546
547   combo->case_sensitive = val;
548 }
549
550 void
551 gtk_combo_set_use_arrows (GtkCombo * combo, gint val)
552 {
553   g_return_if_fail (combo != NULL);
554   g_return_if_fail (GTK_IS_COMBO (combo));
555
556   combo->use_arrows = val;
557 }
558
559 void
560 gtk_combo_set_use_arrows_always (GtkCombo * combo, gint val)
561 {
562   g_return_if_fail (combo != NULL);
563   g_return_if_fail (GTK_IS_COMBO (combo));
564
565   combo->use_arrows_always = val;
566   combo->use_arrows = 1;
567 }
568
569 void
570 gtk_combo_set_popdown_strings (GtkCombo * combo, GList * strings)
571 {
572   GList *list;
573   GtkWidget *li;
574
575   g_return_if_fail (combo != NULL);
576   g_return_if_fail (GTK_IS_COMBO (combo));
577   g_return_if_fail (strings != NULL);
578
579   gtk_list_clear_items (GTK_LIST (combo->list), 0, -1);
580   list = strings;
581   while (list)
582     {
583       li = gtk_list_item_new_with_label ((gchar *) list->data);
584       gtk_widget_show (li);
585       gtk_container_add (GTK_CONTAINER (combo->list), li);
586       list = list->next;
587     }
588 }
589
590 static void
591 gtk_combo_item_destroy (GtkObject * object)
592 {
593   gchar *key;
594
595   key = gtk_object_get_data (object, gtk_combo_string_key);
596   if (key)
597     g_free (key);
598 }
599
600 void
601 gtk_combo_set_item_string (GtkCombo * combo, GtkItem * item, const gchar * item_value)
602 {
603   gchar *val;
604   gint connected = 0;
605
606   g_return_if_fail (combo != NULL);
607   g_return_if_fail (GTK_IS_COMBO (combo));
608   g_return_if_fail (item != NULL);
609
610   val = gtk_object_get_data (GTK_OBJECT (item), gtk_combo_string_key);
611   if (val) 
612     {
613       g_free (val);
614       connected = 1;
615     }
616   if (item_value)
617     {
618       val = g_strdup(item_value);
619       gtk_object_set_data (GTK_OBJECT (item), gtk_combo_string_key, val);
620       if (!connected)
621         gtk_signal_connect (GTK_OBJECT (item), "destroy",
622                           (GtkSignalFunc) gtk_combo_item_destroy, val);
623     }
624   else 
625     {
626       gtk_object_set_data (GTK_OBJECT (item), gtk_combo_string_key, NULL);
627       if (connected)
628         gtk_signal_disconnect_by_data(GTK_OBJECT (item), val);
629     }
630 }
631
632 static void
633 gtk_combo_size_allocate (GtkWidget     *widget,
634                          GtkAllocation *allocation)
635 {
636   GtkCombo *combo;
637
638   g_return_if_fail (widget != NULL);
639   g_return_if_fail (GTK_IS_COMBO (widget));
640   g_return_if_fail (allocation != NULL);
641
642   GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);
643   
644   combo = GTK_COMBO (widget);
645
646   if (combo->entry->allocation.height > combo->entry->requisition.height)
647     {
648       GtkAllocation button_allocation;
649
650       button_allocation = combo->button->allocation;
651       button_allocation.height = combo->entry->requisition.height;
652       button_allocation.y = combo->entry->allocation.y + 
653         (combo->entry->allocation.height - combo->entry->requisition.height) 
654         / 2;
655       gtk_widget_size_allocate (combo->button, &button_allocation);
656     }
657 }