]> Pileus Git - ~andy/gtk/blob - gtk/gtkcombo.c
call the base class init fucntions from all parent types upon class
[~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
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include <string.h>
21
22 #include "gtkarrow.h"
23 #include "gtklabel.h"
24 #include "gtklist.h"
25 #include "gtkentry.h"
26 #include "gtkeventbox.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 #define EMPTY_LIST_HEIGHT       (15)
41
42 static void         gtk_combo_class_init      (GtkComboClass *klass);
43 static void         gtk_combo_init            (GtkCombo      *combo);
44 static void         gtk_combo_destroy         (GtkObject     *combo);
45 static GtkListItem *gtk_combo_find            (GtkCombo      *combo);
46 static gchar *      gtk_combo_func            (GtkListItem  *li);
47 static gint         gtk_combo_focus_idle      (GtkCombo      *combo);
48 static gint         gtk_combo_entry_focus_out (GtkEntry      *entry, 
49                                                GdkEventFocus *event, 
50                                                GtkCombo      *combo);
51 static void         gtk_combo_get_pos         (GtkCombo      *combo, 
52                                                gint          *x, 
53                                                gint          *y, 
54                                                gint          *height, 
55                                                gint          *width);
56 static void         gtk_combo_popup_list      (GtkCombo      *combo);
57 static void         gtk_combo_activate        (GtkWidget        *widget,
58                                                GtkCombo         *combo);
59 static void         gtk_combo_popup_button_press (GtkWidget        *button,
60                                                   GdkEventButton   *event,
61                                                   GtkCombo         *combo);
62 static void         gtk_combo_popup_button_leave (GtkWidget        *button,
63                                                   GdkEventCrossing *event,
64                                                   GtkCombo         *combo);
65 static void         gtk_combo_update_entry    (GtkList       *list, 
66                                                GtkCombo      *combo);
67 static void         gtk_combo_update_list     (GtkEntry      *entry, 
68                                                GtkCombo      *combo);
69 static gint         gtk_combo_button_press    (GtkWidget     *widget,
70                                                GdkEvent      *event,
71                                                GtkCombo      *combo);
72 static gint         gtk_combo_button_release  (GtkWidget     *widget,
73                                                GdkEvent      *event,
74                                                GtkCombo      *combo);
75 static gint         gtk_combo_list_enter      (GtkWidget        *widget,
76                                                GdkEventCrossing *event,
77                                                GtkCombo         *combo);
78 static gint         gtk_combo_list_key_press  (GtkWidget     *widget, 
79                                                GdkEventKey   *event, 
80                                                GtkCombo      *combo);
81 static gint         gtk_combo_entry_key_press (GtkEntry      *widget, 
82                                                GdkEventKey   *event, 
83                                                GtkCombo      *combo);
84 static void         gtk_combo_item_destroy    (GtkObject     *object);
85 static void         gtk_combo_size_allocate   (GtkWidget     *widget,
86                                                GtkAllocation *allocation);
87
88 static GtkHBoxClass *parent_class = NULL;
89
90 static void
91 gtk_combo_class_init (GtkComboClass * klass)
92 {
93   GtkObjectClass *oclass;
94   GtkWidgetClass *widget_class;
95
96   parent_class = gtk_type_class (gtk_hbox_get_type ());
97   oclass = (GtkObjectClass *) klass;
98   widget_class = (GtkWidgetClass *) klass;
99
100   oclass->destroy = gtk_combo_destroy;
101   
102   widget_class->size_allocate = gtk_combo_size_allocate;
103 }
104
105 static void
106 gtk_combo_destroy (GtkObject * combo)
107 {
108   gtk_widget_destroy (GTK_COMBO (combo)->popwin);
109   gtk_widget_unref (GTK_COMBO (combo)->popwin);
110
111   if (GTK_OBJECT_CLASS (parent_class)->destroy)
112     (*GTK_OBJECT_CLASS (parent_class)->destroy) (combo);
113 }
114
115 static int
116 gtk_combo_entry_key_press (GtkEntry * entry, GdkEventKey * event, GtkCombo * combo)
117 {
118   GList *li;
119
120   /* completion */
121   if (event->keyval == GDK_Tab) 
122     {
123     GCompletion * cmpl;
124     gchar* prefix;
125     gchar* nprefix = NULL;
126     gint pos;
127     
128     gtk_signal_emit_stop_by_name (GTK_OBJECT (entry), "key_press_event");
129     cmpl = g_completion_new((GCompletionFunc)gtk_combo_func);
130     g_completion_add_items(cmpl, GTK_LIST(combo->list)->children);
131     pos = GTK_EDITABLE(entry)->current_pos;
132     prefix = gtk_editable_get_chars(GTK_EDITABLE(entry), 0, pos);
133     g_completion_complete(cmpl, prefix, &nprefix);
134     if (nprefix && strlen(nprefix) > strlen(prefix)) 
135       {
136         gtk_editable_insert_text(GTK_EDITABLE(entry), nprefix+pos, 
137                                  strlen(nprefix)-strlen(prefix), &pos);
138         GTK_EDITABLE(entry)->current_pos = pos;
139     }
140     g_free(prefix);
141     g_completion_free(cmpl);
142     return TRUE;
143   }
144   if (!combo->use_arrows || !GTK_LIST (combo->list)->children)
145     return FALSE;
146   li = g_list_find (GTK_LIST (combo->list)->children, gtk_combo_find (combo));
147
148   if ((event->keyval == GDK_Up)
149       || (event->keyval == GDK_KP_Up)
150       || ((event->state & GDK_MOD1_MASK) && ((event->keyval == 'p') || (event->keyval == 'P'))))
151     {
152       if (li)
153         li = li->prev;
154       if (!li && combo->use_arrows_always)
155         {
156           li = g_list_last (GTK_LIST (combo->list)->children);
157         }
158       if (li)
159         {
160           gtk_list_select_child (GTK_LIST (combo->list), GTK_WIDGET (li->data));
161           gtk_signal_emit_stop_by_name (GTK_OBJECT (entry), "key_press_event");
162           return TRUE;
163         }
164     }
165   else if ((event->keyval == GDK_Down)
166            || (event->keyval == GDK_KP_Down)
167            || ((event->state & GDK_MOD1_MASK) && ((event->keyval == 'n') || (event->keyval == 'N'))))
168     {
169       if (li)
170         li = li->next;
171       if (!li && combo->use_arrows_always)
172         {
173           li = GTK_LIST (combo->list)->children;
174         }
175       if (li)
176         {
177           gtk_list_select_child (GTK_LIST (combo->list), GTK_WIDGET (li->data));
178           gtk_signal_emit_stop_by_name (GTK_OBJECT (entry), "key_press_event");
179           return TRUE;
180         }
181     }
182   return FALSE;
183 }
184
185 static GtkListItem *
186 gtk_combo_find (GtkCombo * combo)
187 {
188   gchar *text;
189   gchar *ltext;
190   GList *clist;
191   int (*string_compare) (const char *, const char *);
192
193   if (combo->case_sensitive)
194     string_compare = strcmp;
195   else
196     string_compare = g_strcasecmp;
197
198   text = gtk_entry_get_text (GTK_ENTRY (combo->entry));
199   clist = GTK_LIST (combo->list)->children;
200
201   while (clist && clist->data)
202     {
203       ltext = gtk_combo_func (GTK_LIST_ITEM (clist->data));
204       if (!ltext)
205         continue;
206       if (!(*string_compare) (ltext, text))
207         return (GtkListItem *) clist->data;
208       clist = clist->next;
209     }
210
211   return NULL;
212 }
213
214 static gchar *
215 gtk_combo_func (GtkListItem * li)
216 {
217   GtkWidget *label;
218   gchar *ltext = NULL;
219
220   ltext = (gchar *) gtk_object_get_data (GTK_OBJECT (li), gtk_combo_string_key);
221   if (!ltext)
222     {
223       label = GTK_BIN (li)->child;
224       if (!label || !GTK_IS_LABEL (label))
225         return NULL;
226       gtk_label_get (GTK_LABEL (label), &ltext);
227     }
228   return ltext;
229 }
230
231 static gint
232 gtk_combo_focus_idle (GtkCombo * combo)
233 {
234   if (combo)
235     gtk_widget_grab_focus (combo->entry);
236   return FALSE;
237 }
238
239 static gint
240 gtk_combo_entry_focus_out (GtkEntry * entry, GdkEventFocus * event, GtkCombo * combo)
241 {
242
243   if (combo->value_in_list && !gtk_combo_find (combo))
244     {
245       /* gdk_beep(); *//* this can be annoying */
246       if (combo->ok_if_empty && !strcmp (gtk_entry_get_text (entry), ""))
247         return FALSE;
248 #ifdef TEST
249       printf ("INVALID ENTRY: `%s'\n", gtk_entry_get_text (entry));
250 #endif
251       gtk_grab_add (GTK_WIDGET (combo));
252       /* this is needed because if we call gtk_widget_grab_focus() 
253          it isn't guaranteed it's the *last* call before the main-loop,
254          so the focus can be lost anyway...
255          the signal_emit_stop doesn't seem to work either...
256        */
257       gtk_idle_add ((GtkFunction) gtk_combo_focus_idle, combo);
258       /*gtk_signal_emit_stop_by_name (GTK_OBJECT (entry), "focus_out_event"); */
259       return TRUE;
260     }
261   return FALSE;
262 }
263
264 static void
265 gtk_combo_get_pos (GtkCombo * combo, gint * x, gint * y, gint * height, gint * width)
266 {
267   GtkBin *popwin;
268   GtkWidget *widget;
269   GtkScrolledWindow *popup;
270   
271   gint real_height;
272   GtkRequisition list_requisition;
273   gboolean show_hscroll = FALSE;
274   gboolean show_vscroll = FALSE;
275   gint avail_height;
276   gint min_height;
277   gint alloc_width;
278   gint work_height;
279   gint old_height;
280   gint old_width;
281   
282   widget = GTK_WIDGET(combo);
283   popup  = GTK_SCROLLED_WINDOW (combo->popup);
284   popwin = GTK_BIN (combo->popwin);
285   
286   gdk_window_get_origin (combo->entry->window, x, y);
287   real_height = MIN (combo->entry->requisition.height, 
288                      combo->entry->allocation.height);
289   *y += real_height;
290   avail_height = gdk_screen_height () - *y;
291   
292   gtk_widget_size_request (combo->list, &list_requisition);
293   min_height = MIN (list_requisition.height, 
294                     popup->vscrollbar->requisition.height);
295   if (!GTK_LIST (combo->list)->children)
296     list_requisition.height += EMPTY_LIST_HEIGHT;
297   
298   alloc_width = (widget->allocation.width -
299                  2 * popwin->child->style->klass->xthickness -
300                  2 * GTK_CONTAINER (popwin->child)->border_width -
301                  2 * GTK_CONTAINER (combo->popup)->border_width -
302                  2 * GTK_CONTAINER (popup->viewport)->border_width - 
303                  2 * popup->viewport->style->klass->xthickness);
304   
305   work_height = (2 * popwin->child->style->klass->ythickness +
306                  2 * GTK_CONTAINER (popwin->child)->border_width +
307                  2 * GTK_CONTAINER (combo->popup)->border_width +
308                  2 * GTK_CONTAINER (popup->viewport)->border_width +
309                  2 * popup->viewport->style->klass->xthickness);
310   
311   do 
312     {
313       old_width = alloc_width;
314       old_height = work_height;
315       
316       if (!show_hscroll &&
317           alloc_width < list_requisition.width)
318         {
319           work_height += popup->hscrollbar->requisition.height +
320             GTK_SCROLLED_WINDOW_CLASS 
321             (GTK_OBJECT (combo->popup)->klass)->scrollbar_spacing;
322           show_hscroll = TRUE;
323         }
324       if (!show_vscroll && 
325           work_height + list_requisition.height > avail_height)
326         {
327           if (work_height + min_height > avail_height && 
328               *y - real_height > avail_height)
329             {
330               *y -= (work_height + list_requisition.height + real_height);
331               break;
332             }
333           alloc_width -= 
334             popup->vscrollbar->requisition.width +
335             GTK_SCROLLED_WINDOW_CLASS 
336             (GTK_OBJECT (combo->popup)->klass)->scrollbar_spacing;
337           show_vscroll = TRUE;
338         }
339     } while (old_width != alloc_width || old_height != work_height);
340   
341   *width = widget->allocation.width;
342   if (show_vscroll)
343     *height = avail_height;
344   else
345     *height = work_height + list_requisition.height;
346   
347   if (*x < 0)
348     *x = 0;
349 }
350
351 static void
352 gtk_combo_popup_list (GtkCombo * combo)
353 {
354   gint height, width, x, y;
355   gint old_width, old_height;
356
357   old_width = combo->popwin->allocation.width;
358   old_height  = combo->popwin->allocation.height;
359
360   gtk_combo_get_pos (combo, &x, &y, &height, &width);
361
362   /* workaround for gtk_scrolled_window_size_allocate bug */
363   if (old_width != width || old_height != height)
364     {
365       gtk_widget_hide (GTK_SCROLLED_WINDOW (combo->popup)->hscrollbar);
366       gtk_widget_hide (GTK_SCROLLED_WINDOW (combo->popup)->vscrollbar);
367     }
368
369   gtk_widget_set_uposition (combo->popwin, x, y);
370   gtk_widget_set_usize (combo->popwin, width, height);
371   gtk_widget_realize (combo->popwin);
372   gdk_window_resize (combo->popwin->window, width, height);
373   gtk_widget_show (combo->popwin);
374
375   gtk_widget_grab_focus (combo->popwin);
376 }
377
378 static void        
379 gtk_combo_activate (GtkWidget        *widget,
380                     GtkCombo         *combo)
381 {
382   gtk_combo_popup_list (combo);
383
384   if (!GTK_WIDGET_HAS_FOCUS (combo->entry))
385     gtk_widget_grab_focus (combo->entry);
386   gtk_grab_add (combo->popwin);
387   gdk_pointer_grab (combo->popwin->window, TRUE,
388                     GDK_BUTTON_PRESS_MASK | 
389                     GDK_BUTTON_RELEASE_MASK |
390                     GDK_POINTER_MOTION_MASK, 
391                     NULL, NULL, GDK_CURRENT_TIME);
392 }
393
394 static void        
395 gtk_combo_popup_button_press (GtkWidget        *button,
396                               GdkEventButton   *event,
397                               GtkCombo         *combo)
398 {
399   if (!GTK_WIDGET_HAS_FOCUS (combo->entry))
400     gtk_widget_grab_focus (combo->entry);
401   if (!combo->current_button && (event->button == 1))
402     gtk_combo_popup_list (combo);
403
404   combo->current_button = event->button;
405 }
406
407 static void         
408 gtk_combo_popup_button_leave (GtkWidget        *button,
409                               GdkEventCrossing *event,
410                               GtkCombo         *combo)
411 {
412   if (combo->current_button)
413     gtk_signal_emit_stop_by_name (GTK_OBJECT (button), "leave_notify_event");
414 }
415
416
417 static void
418 gtk_combo_update_entry (GtkList * list, GtkCombo * combo)
419 {
420   char *text;
421
422   gtk_grab_remove (GTK_WIDGET (combo));
423   gtk_signal_handler_block (GTK_OBJECT (list), combo->list_change_id);
424   if (list->selection)
425     {
426       text = gtk_combo_func (GTK_LIST_ITEM (list->selection->data));
427       if (!text)
428         text = "";
429       gtk_entry_set_text (GTK_ENTRY (combo->entry), text);
430     }
431   gtk_signal_handler_unblock (GTK_OBJECT (list), combo->list_change_id);
432 }
433
434 static void
435 gtk_combo_update_list (GtkEntry * entry, GtkCombo * combo)
436 {
437   GtkList *list = GTK_LIST (combo->list);
438   GList *slist = list->selection;
439   GtkListItem *li;
440
441   gtk_grab_remove (GTK_WIDGET (combo));
442
443   gtk_signal_handler_block (GTK_OBJECT (entry), combo->entry_change_id);
444   if (slist && slist->data)
445     gtk_list_unselect_child (list, GTK_WIDGET (slist->data));
446   li = gtk_combo_find (combo);
447   if (li)
448     gtk_list_select_child (list, GTK_WIDGET (li));
449   gtk_signal_handler_unblock (GTK_OBJECT (entry), combo->entry_change_id);
450 }
451
452 static gint
453 gtk_combo_button_press (GtkWidget * widget, GdkEvent * event, GtkCombo * combo)
454 {
455   GtkWidget *child;
456
457   child = gtk_get_event_widget (event);
458
459   /* We don't ask for button press events on the grab widget, so
460    *  if an event is reported directly to the grab widget, it must
461    *  be on a window outside the application (and thus we remove
462    *  the popup window). Otherwise, we check if the widget is a child
463    *  of the grab widget, and only remove the popup window if it
464    *  is not.
465    */
466   if (child != widget)
467     {
468       while (child)
469         {
470           if (child == widget)
471             return FALSE;
472           child = child->parent;
473         }
474     }
475
476   gtk_widget_hide (combo->popwin);
477   gtk_grab_remove (combo->popwin);
478   gdk_pointer_ungrab (event->button.time);
479
480   return TRUE;
481 }
482
483 static gint
484 gtk_combo_button_release (GtkWidget * widget, GdkEvent * event, GtkCombo * combo)
485 {
486   GtkWidget *child;
487
488   if ((combo->current_button != 0) && (event->button.button == 1))
489     {
490       /* This was the initial button press */
491
492       GdkEventCrossing tmp_event;
493
494       combo->current_button = 0;
495
496       if (widget != combo->button)
497         gtk_widget_event (combo->button, event);
498
499       /* Un-pre-hightlight */
500       
501       tmp_event.type = GDK_LEAVE_NOTIFY;
502       tmp_event.window = combo->button->window;
503       tmp_event.send_event = TRUE;
504       tmp_event.subwindow = NULL;
505       tmp_event.detail = GDK_NOTIFY_ANCESTOR;
506       
507       gtk_widget_event (combo->button, (GdkEvent *)&tmp_event);
508
509       /* Check to see if we released inside the button */
510       child = gtk_get_event_widget ((GdkEvent*) event);
511
512       while (child && child != (combo->button))
513         child = child->parent;
514
515       if (child == combo->button)
516         {
517           gtk_grab_add (combo->popwin);
518           gdk_pointer_grab (combo->popwin->window, TRUE,
519                             GDK_BUTTON_PRESS_MASK | 
520                             GDK_BUTTON_RELEASE_MASK |
521                             GDK_POINTER_MOTION_MASK, 
522                             NULL, NULL, GDK_CURRENT_TIME);
523           return FALSE;
524         }
525     }
526   else
527     {
528       /* The user has clicked inside the popwin and released */
529
530       gtk_grab_remove (combo->popwin);
531       gdk_pointer_ungrab (event->button.time);
532     }
533   
534   gtk_widget_hide (combo->popwin);
535
536   return TRUE;
537 }
538
539 static gint         
540 gtk_combo_list_enter (GtkWidget        *widget,
541                       GdkEventCrossing *event,
542                       GtkCombo         *combo)
543 {
544   GtkWidget *event_widget;
545
546   event_widget = gtk_get_event_widget ((GdkEvent*) event);
547   
548   if ((event_widget == combo->list) &&
549       (combo->current_button != 0) && 
550       (!GTK_WIDGET_HAS_GRAB (combo->list)))
551     {
552       GdkEvent tmp_event;
553       gint x, y;
554       GdkModifierType mask;
555
556       gtk_grab_remove (combo->popwin);
557
558       /* Transfer the grab over to the list by synthesizing
559        * a button press event
560        */
561       gdk_window_get_pointer (combo->list->window, &x, &y, &mask);
562
563       tmp_event.button.type = GDK_BUTTON_PRESS;
564       tmp_event.button.window = combo->list->window;
565       tmp_event.button.send_event = TRUE;
566       tmp_event.button.time = GDK_CURRENT_TIME; /* bad */
567       tmp_event.button.x = x;
568       tmp_event.button.y = y;
569       /* We leave all the XInput fields unfilled here, in the expectation
570        * that GtkList doesn't care.
571        */
572       tmp_event.button.button = combo->current_button;
573       tmp_event.button.state = mask;
574
575       gtk_widget_event (combo->list, &tmp_event);
576     }
577
578   return FALSE;
579 }
580
581 static int
582 gtk_combo_list_key_press (GtkWidget * widget, GdkEventKey * event, GtkCombo * combo)
583 {
584   if (event->keyval == GDK_Escape)
585     {
586       gtk_widget_hide (combo->popwin);
587       gtk_grab_remove (combo->popwin);
588       gdk_pointer_ungrab (GDK_CURRENT_TIME);
589       return TRUE;
590     }
591   return FALSE;
592 }
593
594 static void
595 gtk_combo_init (GtkCombo * combo)
596 {
597   GtkWidget *arrow;
598   GtkWidget *frame;
599   GtkWidget *event_box;
600   GdkCursor *cursor;
601
602   combo->case_sensitive = 0;
603   combo->value_in_list = 0;
604   combo->ok_if_empty = 1;
605   combo->use_arrows = 1;
606   combo->use_arrows_always = 0;
607   combo->entry = gtk_entry_new ();
608   combo->button = gtk_button_new ();
609   combo->current_button = 0;
610   arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_OUT);
611   gtk_widget_show (arrow);
612   gtk_container_add (GTK_CONTAINER (combo->button), arrow);
613   gtk_box_pack_start (GTK_BOX (combo), combo->entry, TRUE, TRUE, 0);
614   gtk_box_pack_end (GTK_BOX (combo), combo->button, FALSE, FALSE, 0);
615   GTK_WIDGET_UNSET_FLAGS (combo->button, GTK_CAN_FOCUS);
616   gtk_widget_show (combo->entry);
617   gtk_widget_show (combo->button);
618   combo->entry_change_id = gtk_signal_connect (GTK_OBJECT (combo->entry), "changed",
619                               (GtkSignalFunc) gtk_combo_update_list, combo);
620   gtk_signal_connect (GTK_OBJECT (combo->entry), "key_press_event",
621                       (GtkSignalFunc) gtk_combo_entry_key_press, combo);
622   gtk_signal_connect_after (GTK_OBJECT (combo->entry), "focus_out_event",
623                             (GtkSignalFunc) gtk_combo_entry_focus_out, combo);
624   combo->activate_id = gtk_signal_connect (GTK_OBJECT (combo->entry), "activate",
625                       (GtkSignalFunc) gtk_combo_activate, combo);
626   gtk_signal_connect_after (GTK_OBJECT (combo->button), "button_press_event",
627                             (GtkSignalFunc) gtk_combo_popup_button_press, combo);
628   gtk_signal_connect_after (GTK_OBJECT (combo->button), "button_release_event",
629                             (GtkSignalFunc) gtk_combo_button_release, combo);
630   gtk_signal_connect (GTK_OBJECT (combo->button), "leave_notify_event",
631                       (GtkSignalFunc) gtk_combo_popup_button_leave, combo);
632   /*gtk_signal_connect(GTK_OBJECT(combo->button), "clicked",
633      (GtkSignalFunc)prelight_bug, combo); */
634
635   combo->popwin = gtk_window_new (GTK_WINDOW_POPUP);
636   gtk_widget_ref (combo->popwin);
637   gtk_window_set_policy (GTK_WINDOW (combo->popwin), 1, 1, 0);
638   
639   gtk_widget_set_events (combo->popwin, GDK_KEY_PRESS_MASK);
640
641   event_box = gtk_event_box_new ();
642   gtk_container_add (GTK_CONTAINER (combo->popwin), event_box);
643   gtk_widget_show (event_box);
644
645   gtk_widget_realize (event_box);
646   cursor = gdk_cursor_new (GDK_TOP_LEFT_ARROW);
647   gdk_window_set_cursor (event_box->window, cursor);
648   gdk_cursor_destroy (cursor);
649
650   frame = gtk_frame_new (NULL);
651   gtk_container_add (GTK_CONTAINER (event_box), frame);
652   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT);
653   gtk_widget_show (frame);
654
655   combo->popup = gtk_scrolled_window_new (NULL, NULL);
656   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (combo->popup),
657                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
658   GTK_WIDGET_UNSET_FLAGS (GTK_SCROLLED_WINDOW (combo->popup)->hscrollbar, GTK_CAN_FOCUS);
659   GTK_WIDGET_UNSET_FLAGS (GTK_SCROLLED_WINDOW (combo->popup)->vscrollbar, GTK_CAN_FOCUS);
660   gtk_container_add (GTK_CONTAINER (frame), combo->popup);
661   gtk_widget_show (combo->popup);
662
663   combo->list = gtk_list_new ();
664   /* We'll use enter notify events to figure out when to transfer
665    * the grab to the list
666    */
667   gtk_widget_set_events (combo->list, GDK_ENTER_NOTIFY_MASK);
668
669   gtk_list_set_selection_mode(GTK_LIST(combo->list), GTK_SELECTION_BROWSE);
670   gtk_container_add (GTK_CONTAINER (combo->popup), combo->list);
671   gtk_container_set_focus_vadjustment (GTK_CONTAINER (combo->list),
672                                        gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (combo->popup)));
673   gtk_widget_show (combo->list);
674
675   combo->list_change_id = gtk_signal_connect (GTK_OBJECT (combo->list), "selection_changed",
676                              (GtkSignalFunc) gtk_combo_update_entry, combo);
677   gtk_signal_connect (GTK_OBJECT (combo->popwin), "key_press_event",
678                       (GtkSignalFunc) gtk_combo_list_key_press, combo);
679   gtk_signal_connect (GTK_OBJECT (combo->popwin), "button_press_event",
680                       GTK_SIGNAL_FUNC (gtk_combo_button_press), combo);
681
682   gtk_signal_connect (GTK_OBJECT (combo->list), "button_release_event",
683                       GTK_SIGNAL_FUNC (gtk_combo_button_release), combo);
684   /* We connect here on the button, because we'll have a grab on it
685    * when the event occurs. But we are actually interested in enters
686    * for the combo->list.
687    */
688   gtk_signal_connect (GTK_OBJECT (combo->button), "enter_notify_event",
689                       GTK_SIGNAL_FUNC (gtk_combo_list_enter), combo);
690 }
691
692 guint
693 gtk_combo_get_type (void)
694 {
695   static guint combo_type = 0;
696
697   if (!combo_type)
698     {
699       GtkTypeInfo combo_info =
700       {
701         "GtkCombo",
702         sizeof (GtkCombo),
703         sizeof (GtkComboClass),
704         (GtkClassInitFunc) gtk_combo_class_init,
705         (GtkObjectInitFunc) gtk_combo_init,
706         /* reversed_1 */ NULL,
707         /* reversed_2 */ NULL,
708         (GtkClassInitFunc) NULL,
709       };
710       combo_type = gtk_type_unique (gtk_hbox_get_type (), &combo_info);
711     }
712   return combo_type;
713 }
714
715 GtkWidget *
716 gtk_combo_new (void)
717 {
718   return GTK_WIDGET (gtk_type_new (gtk_combo_get_type ()));
719 }
720
721 void
722 gtk_combo_set_value_in_list (GtkCombo * combo, gint val, gint ok_if_empty)
723 {
724   g_return_if_fail (combo != NULL);
725   g_return_if_fail (GTK_IS_COMBO (combo));
726
727   combo->value_in_list = val;
728   combo->ok_if_empty = ok_if_empty;
729 }
730
731 void
732 gtk_combo_set_case_sensitive (GtkCombo * combo, gint val)
733 {
734   g_return_if_fail (combo != NULL);
735   g_return_if_fail (GTK_IS_COMBO (combo));
736
737   combo->case_sensitive = val;
738 }
739
740 void
741 gtk_combo_set_use_arrows (GtkCombo * combo, gint val)
742 {
743   g_return_if_fail (combo != NULL);
744   g_return_if_fail (GTK_IS_COMBO (combo));
745
746   combo->use_arrows = val;
747 }
748
749 void
750 gtk_combo_set_use_arrows_always (GtkCombo * combo, gint val)
751 {
752   g_return_if_fail (combo != NULL);
753   g_return_if_fail (GTK_IS_COMBO (combo));
754
755   combo->use_arrows_always = val;
756   combo->use_arrows = 1;
757 }
758
759 void
760 gtk_combo_set_popdown_strings (GtkCombo * combo, GList * strings)
761 {
762   GList *list;
763   GtkWidget *li;
764
765   g_return_if_fail (combo != NULL);
766   g_return_if_fail (GTK_IS_COMBO (combo));
767   g_return_if_fail (strings != NULL);
768
769   gtk_list_clear_items (GTK_LIST (combo->list), 0, -1);
770   list = strings;
771   while (list)
772     {
773       li = gtk_list_item_new_with_label ((gchar *) list->data);
774       gtk_widget_show (li);
775       gtk_container_add (GTK_CONTAINER (combo->list), li);
776       list = list->next;
777     }
778 }
779
780 static void
781 gtk_combo_item_destroy (GtkObject * object)
782 {
783   gchar *key;
784
785   key = gtk_object_get_data (object, gtk_combo_string_key);
786   if (key)
787     g_free (key);
788 }
789
790 void
791 gtk_combo_set_item_string (GtkCombo * combo, GtkItem * item, const gchar * item_value)
792 {
793   gchar *val;
794   gint connected = 0;
795
796   g_return_if_fail (combo != NULL);
797   g_return_if_fail (GTK_IS_COMBO (combo));
798   g_return_if_fail (item != NULL);
799
800   val = gtk_object_get_data (GTK_OBJECT (item), gtk_combo_string_key);
801   if (val) 
802     {
803       g_free (val);
804       connected = 1;
805     }
806   if (item_value)
807     {
808       val = g_strdup(item_value);
809       gtk_object_set_data (GTK_OBJECT (item), gtk_combo_string_key, val);
810       if (!connected)
811         gtk_signal_connect (GTK_OBJECT (item), "destroy",
812                           (GtkSignalFunc) gtk_combo_item_destroy, val);
813     }
814   else 
815     {
816       gtk_object_set_data (GTK_OBJECT (item), gtk_combo_string_key, NULL);
817       if (connected)
818         gtk_signal_disconnect_by_data(GTK_OBJECT (item), val);
819     }
820 }
821
822 static void
823 gtk_combo_size_allocate (GtkWidget     *widget,
824                          GtkAllocation *allocation)
825 {
826   GtkCombo *combo;
827
828   g_return_if_fail (widget != NULL);
829   g_return_if_fail (GTK_IS_COMBO (widget));
830   g_return_if_fail (allocation != NULL);
831
832   GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);
833   
834   combo = GTK_COMBO (widget);
835
836   if (combo->entry->allocation.height > combo->entry->requisition.height)
837     {
838       GtkAllocation button_allocation;
839
840       button_allocation = combo->button->allocation;
841       button_allocation.height = combo->entry->requisition.height;
842       button_allocation.y = combo->entry->allocation.y + 
843         (combo->entry->allocation.height - combo->entry->requisition.height) 
844         / 2;
845       gtk_widget_size_allocate (combo->button, &button_allocation);
846     }
847 }
848
849 void
850 gtk_combo_disable_activate (GtkCombo* combo)
851 {
852   g_return_if_fail (combo != NULL);
853   g_return_if_fail (GTK_IS_COMBO (combo));
854
855   if ( combo->activate_id ) {
856     gtk_signal_disconnect(GTK_OBJECT(combo->entry), combo->activate_id);
857     combo->activate_id = 0;
858   }
859 }