]> Pileus Git - ~andy/gtk/blob - gtk/gtkcombobox.c
Position the popup above the sample if there is not enough room below.
[~andy/gtk] / gtk / gtkcombobox.c
1 /* gtkcombobox.c
2  * Copyright (C) 2002, 2003  Kristian Rietveld <kris@gtk.org>
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 "gtkcombobox.h"
21
22 #include "gtkarrow.h"
23 #include "gtkbindings.h"
24 #include "gtkcelllayout.h"
25 #include "gtkcellrenderertext.h"
26 #include "gtkcellview.h"
27 #include "gtkcellviewmenuitem.h"
28 #include "gtkeventbox.h"
29 #include "gtkframe.h"
30 #include "gtkliststore.h"
31 #include "gtkmain.h"
32 #include "gtkmenu.h"
33 #include "gtktogglebutton.h"
34 #include "gtktreeselection.h"
35 #include "gtkvseparator.h"
36 #include "gtkwindow.h"
37
38 #include <gdk/gdkkeysyms.h>
39
40 #include <gobject/gvaluecollector.h>
41
42 #include <string.h>
43 #include <stdarg.h>
44
45 #include "gtkmarshalers.h"
46 #include "gtkintl.h"
47
48
49 /* WELCOME, to THE house of evil code */
50
51 typedef struct _ComboCellInfo ComboCellInfo;
52 struct _ComboCellInfo
53 {
54   GtkCellRenderer *cell;
55   GSList *attributes;
56
57   GtkCellLayoutDataFunc func;
58   gpointer func_data;
59   GDestroyNotify destroy;
60
61   guint expand : 1;
62   guint pack : 1;
63 };
64
65 #define GTK_COMBO_BOX_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_COMBO_BOX, GtkComboBoxPrivate))
66
67 struct _GtkComboBoxPrivate
68 {
69   GtkTreeModel *model;
70
71   gint col_column;
72   gint row_column;
73
74   gint wrap_width;
75
76   gint active_item;
77
78   GtkWidget *tree_view;
79   GtkTreeViewColumn *column;
80
81   GtkWidget *cell_view;
82   GtkWidget *cell_view_frame;
83
84   GtkWidget *button;
85   GtkWidget *arrow;
86   GtkWidget *separator;
87
88   GtkWidget *popup_widget;
89   GtkWidget *popup_window;
90   GtkWidget *popup_frame;
91
92   guint inserted_id;
93   guint deleted_id;
94
95   gint width;
96   GSList *cells;
97
98   guint changed_id;
99
100   guint popup_in_progress : 1;
101 };
102
103 /* While debugging this evil code, I have learned that
104  * there are actually 4 modes to this widget, which can
105  * be characterized as follows
106  * 
107  * 1) menu mode, no child added
108  *
109  * tree_view -> NULL
110  * cell_view -> GtkCellView, regular child
111  * cell_view_frame -> NULL
112  * button -> GtkToggleButton set_parent to combo
113  * arrow -> GtkArrow set_parent to button
114  * separator -> GtkVSepator set_parent to button
115  * popup_widget -> GtkMenu
116  * popup_window -> NULL
117  * popup_frame -> NULL
118  *
119  * 2) menu mode, child added
120  * 
121  * tree_view -> NULL
122  * cell_view -> NULL 
123  * cell_view_frame -> NULL
124  * button -> GtkToggleButton set_parent to combo
125  * arrow -> GtkArrow, child of button
126  * separator -> NULL
127  * popup_widget -> GtkMenu
128  * popup_window -> NULL
129  * popup_frame -> NULL
130  *
131  * 3) list mode, no child added
132  * 
133  * tree_view -> GtkTreeView, child of popup_frame
134  * cell_view -> GtkCellView, regular child
135  * cell_view_frame -> GtkFrame, set parent to combo
136  * button -> GtkToggleButton, set_parent to combo
137  * arrow -> GtkArrow, child of button
138  * separator -> NULL
139  * popup_widget -> tree_view
140  * popup_window -> GtkWindow
141  * popup_frame -> GtkFrame, child of popup_window
142  *
143  * 4) list mode, child added
144  *
145  * tree_view -> GtkTreeView, child of popup_frame
146  * cell_view -> NULL
147  * cell_view_frame -> NULL
148  * button -> GtkToggleButton, set_parent to combo
149  * arrow -> GtkArrow, child of button
150  * separator -> NULL
151  * popup_widget -> tree_view
152  * popup_window -> GtkWindow
153  * popup_frame -> GtkFrame, child of popup_window
154  * 
155  */
156
157 enum {
158   CHANGED,
159   LAST_SIGNAL
160 };
161
162 enum {
163   PROP_0,
164   PROP_MODEL,
165   PROP_WRAP_WIDTH,
166   PROP_ROW_SPAN_COLUMN,
167   PROP_COLUMN_SPAN_COLUMN,
168   PROP_ACTIVE
169 };
170
171 static GtkBinClass *parent_class = NULL;
172 static guint combo_box_signals[LAST_SIGNAL] = {0,};
173
174 #define BONUS_PADDING 4
175
176
177 /* common */
178 static void     gtk_combo_box_class_init           (GtkComboBoxClass *klass);
179 static void     gtk_combo_box_cell_layout_init     (GtkCellLayoutIface *iface);
180 static void     gtk_combo_box_init                 (GtkComboBox      *combo_box);
181 static void     gtk_combo_box_finalize             (GObject          *object);
182 static void     gtk_combo_box_destroy              (GtkObject        *object);
183
184 static void     gtk_combo_box_set_property         (GObject         *object,
185                                                     guint            prop_id,
186                                                     const GValue    *value,
187                                                     GParamSpec      *spec);
188 static void     gtk_combo_box_get_property         (GObject         *object,
189                                                     guint            prop_id,
190                                                     GValue          *value,
191                                                     GParamSpec      *spec);
192
193 static void     gtk_combo_box_style_set            (GtkWidget       *widget,
194                                                     GtkStyle        *previous);
195 static void     gtk_combo_box_button_toggled       (GtkWidget       *widget,
196                                                     gpointer         data);
197 static void     gtk_combo_box_add                  (GtkContainer    *container,
198                                                     GtkWidget       *widget);
199
200 static ComboCellInfo *gtk_combo_box_get_cell_info  (GtkComboBox      *combo_box,
201                                                     GtkCellRenderer  *cell);
202
203 static void     gtk_combo_box_menu_show            (GtkWidget        *menu,
204                                                     gpointer          user_data);
205 static void     gtk_combo_box_menu_hide            (GtkWidget        *menu,
206                                                     gpointer          user_data);
207
208 static void     gtk_combo_box_set_popup_widget     (GtkComboBox      *combo_box,
209                                                     GtkWidget        *popup);
210 static void     gtk_combo_box_menu_position        (GtkMenu          *menu,
211                                                     gint             *x,
212                                                     gint             *y,
213                                                     gint             *push_in,
214                                                     gpointer          user_data);
215
216 static gint     gtk_combo_box_calc_requested_width (GtkComboBox      *combo_box,
217                                                     GtkTreePath      *path);
218 static void     gtk_combo_box_remeasure            (GtkComboBox      *combo_box);
219
220 static void     gtk_combo_box_unset_model          (GtkComboBox      *combo_box);
221 static void     gtk_combo_box_set_model_internal   (GtkComboBox      *combo_box);
222
223 static void     gtk_combo_box_size_request         (GtkWidget        *widget,
224                                                     GtkRequisition   *requisition);
225 static void     gtk_combo_box_size_allocate        (GtkWidget        *widget,
226                                                     GtkAllocation    *allocation);
227 static void     gtk_combo_box_forall               (GtkContainer     *container,
228                                                     gboolean          include_internals,
229                                                     GtkCallback       callback,
230                                                     gpointer          callback_data);
231 static gboolean gtk_combo_box_expose_event         (GtkWidget        *widget,
232                                                     GdkEventExpose   *event);
233 static gboolean gtk_combo_box_scroll_event         (GtkWidget        *widget,
234                                                     GdkEventScroll   *event);
235
236 /* list */
237 static void     gtk_combo_box_list_position        (GtkComboBox      *combo_box, 
238                                                     gint             *x, 
239                                                     gint             *y, 
240                                                     gint             *width,
241                                                     gint             *height);
242 static void     gtk_combo_box_list_setup           (GtkComboBox      *combo_box);
243 static void     gtk_combo_box_list_destroy         (GtkComboBox      *combo_box);
244
245 static void     gtk_combo_box_list_remove_grabs    (GtkComboBox      *combo_box);
246
247 static gboolean gtk_combo_box_list_button_released (GtkWidget        *widget,
248                                                     GdkEventButton   *event,
249                                                     gpointer          data);
250 static gboolean gtk_combo_box_list_key_press       (GtkWidget        *widget,
251                                                     GdkEventKey      *event,
252                                                     gpointer          data);
253 static gboolean gtk_combo_box_list_button_pressed  (GtkWidget        *widget,
254                                                     GdkEventButton   *event,
255                                                     gpointer          data);
256
257 static void     gtk_combo_box_list_row_changed     (GtkTreeModel     *model,
258                                                     GtkTreePath      *path,
259                                                     GtkTreeIter      *iter,
260                                                     gpointer          data);
261
262 /* menu */
263 static void     gtk_combo_box_menu_setup           (GtkComboBox      *combo_box,
264                                                     gboolean          add_childs);
265 static void     gtk_combo_box_menu_fill            (GtkComboBox      *combo_box);
266 static void     gtk_combo_box_menu_destroy         (GtkComboBox      *combo_box);
267
268 static void     gtk_combo_box_item_get_size        (GtkComboBox      *combo_box,
269                                                     gint              index,
270                                                     gint             *cols,
271                                                     gint             *rows);
272 static void     gtk_combo_box_relayout_item        (GtkComboBox      *combo_box,
273                                                     gint              index);
274 static void     gtk_combo_box_relayout             (GtkComboBox      *combo_box);
275
276 static gboolean gtk_combo_box_menu_button_press    (GtkWidget        *widget,
277                                                     GdkEventButton   *event,
278                                                     gpointer          user_data);
279 static void     gtk_combo_box_menu_item_activate   (GtkWidget        *item,
280                                                     gpointer          user_data);
281 static void     gtk_combo_box_menu_row_inserted    (GtkTreeModel     *model,
282                                                     GtkTreePath      *path,
283                                                     GtkTreeIter      *iter,
284                                                     gpointer          user_data);
285 static void     gtk_combo_box_menu_row_deleted     (GtkTreeModel     *model,
286                                                     GtkTreePath      *path,
287                                                     gpointer          user_data);
288 static void     gtk_combo_box_menu_row_changed     (GtkTreeModel     *model,
289                                                     GtkTreePath      *path,
290                                                     GtkTreeIter      *iter,
291                                                     gpointer          data);
292
293 /* cell layout */
294 static void     gtk_combo_box_cell_layout_pack_start         (GtkCellLayout         *layout,
295                                                               GtkCellRenderer       *cell,
296                                                               gboolean               expand);
297 static void     gtk_combo_box_cell_layout_pack_end           (GtkCellLayout         *layout,
298                                                               GtkCellRenderer       *cell,
299                                                               gboolean               expand);
300 static void     gtk_combo_box_cell_layout_clear              (GtkCellLayout         *layout);
301 static void     gtk_combo_box_cell_layout_add_attribute      (GtkCellLayout         *layout,
302                                                               GtkCellRenderer       *cell,
303                                                               const gchar           *attribute,
304                                                               gint                   column);
305 static void     gtk_combo_box_cell_layout_set_cell_data_func (GtkCellLayout         *layout,
306                                                               GtkCellRenderer       *cell,
307                                                               GtkCellLayoutDataFunc  func,
308                                                               gpointer               func_data,
309                                                               GDestroyNotify         destroy);
310 static void     gtk_combo_box_cell_layout_clear_attributes   (GtkCellLayout         *layout,
311                                                               GtkCellRenderer       *cell);
312 static void     gtk_combo_box_cell_layout_reorder            (GtkCellLayout         *layout,
313                                                               GtkCellRenderer       *cell,
314                                                               gint                   position);
315 static gboolean gtk_combo_box_mnemonic_activate              (GtkWidget    *widget,
316                                                               gboolean      group_cycling);
317
318
319 GType
320 gtk_combo_box_get_type (void)
321 {
322   static GType combo_box_type = 0;
323
324   if (!combo_box_type)
325     {
326       static const GTypeInfo combo_box_info =
327         {
328           sizeof (GtkComboBoxClass),
329           NULL, /* base_init */
330           NULL, /* base_finalize */
331           (GClassInitFunc) gtk_combo_box_class_init,
332           NULL, /* class_finalize */
333           NULL, /* class_data */
334           sizeof (GtkComboBox),
335           0,
336           (GInstanceInitFunc) gtk_combo_box_init
337         };
338
339       static const GInterfaceInfo cell_layout_info =
340         {
341           (GInterfaceInitFunc) gtk_combo_box_cell_layout_init,
342           NULL,
343           NULL
344         };
345
346       combo_box_type = g_type_register_static (GTK_TYPE_BIN,
347                                                "GtkComboBox",
348                                                &combo_box_info,
349                                                0);
350
351       g_type_add_interface_static (combo_box_type,
352                                    GTK_TYPE_CELL_LAYOUT,
353                                    &cell_layout_info);
354     }
355
356   return combo_box_type;
357 }
358
359 /* common */
360 static void
361 gtk_combo_box_class_init (GtkComboBoxClass *klass)
362 {
363   GObjectClass *object_class;
364   GtkBindingSet *binding_set;
365   GtkObjectClass *gtk_object_class;
366   GtkContainerClass *container_class;
367   GtkWidgetClass *widget_class;
368
369   binding_set = gtk_binding_set_by_class (klass);
370
371   container_class = (GtkContainerClass *)klass;
372   container_class->forall = gtk_combo_box_forall;
373   container_class->add = gtk_combo_box_add;
374
375   widget_class = (GtkWidgetClass *)klass;
376   widget_class->size_allocate = gtk_combo_box_size_allocate;
377   widget_class->size_request = gtk_combo_box_size_request;
378   widget_class->expose_event = gtk_combo_box_expose_event;
379   widget_class->scroll_event = gtk_combo_box_scroll_event;
380   widget_class->mnemonic_activate = gtk_combo_box_mnemonic_activate;
381   widget_class->style_set = gtk_combo_box_style_set;
382
383   gtk_object_class = (GtkObjectClass *)klass;
384   gtk_object_class->destroy = gtk_combo_box_destroy;
385
386   object_class = (GObjectClass *)klass;
387   object_class->finalize = gtk_combo_box_finalize;
388   object_class->set_property = gtk_combo_box_set_property;
389   object_class->get_property = gtk_combo_box_get_property;
390
391   parent_class = g_type_class_peek_parent (klass);
392
393   /* signals */
394   combo_box_signals[CHANGED] =
395     g_signal_new ("changed",
396                   G_OBJECT_CLASS_TYPE (klass),
397                   G_SIGNAL_RUN_LAST,
398                   G_STRUCT_OFFSET (GtkComboBoxClass, changed),
399                   NULL, NULL,
400                   g_cclosure_marshal_VOID__VOID,
401                   G_TYPE_NONE, 0);
402
403   /* properties */
404   g_object_class_install_property (object_class,
405                                    PROP_MODEL,
406                                    g_param_spec_object ("model",
407                                                         P_("ComboBox model"),
408                                                         P_("The model for the combo box"),
409                                                         GTK_TYPE_TREE_MODEL,
410                                                         G_PARAM_READWRITE));
411
412   g_object_class_install_property (object_class,
413                                    PROP_WRAP_WIDTH,
414                                    g_param_spec_int ("wrap_width",
415                                                      P_("Wrap width"),
416                                                      P_("Wrap width for layouting the items in a grid"),
417                                                      0,
418                                                      G_MAXINT,
419                                                      0,
420                                                      G_PARAM_READWRITE));
421
422   g_object_class_install_property (object_class,
423                                    PROP_ROW_SPAN_COLUMN,
424                                    g_param_spec_int ("row_span_column",
425                                                      P_("Row span column"),
426                                                      P_("TreeModel column containing the row span values"),
427                                                      0,
428                                                      G_MAXINT,
429                                                      0,
430                                                      G_PARAM_READWRITE));
431
432   g_object_class_install_property (object_class,
433                                    PROP_COLUMN_SPAN_COLUMN,
434                                    g_param_spec_int ("column_span_column",
435                                                      P_("Column span column"),
436                                                      P_("TreeModel column containing the column span values"),
437                                                      0,
438                                                      G_MAXINT,
439                                                      0,
440                                                      G_PARAM_READWRITE));
441
442   g_object_class_install_property (object_class,
443                                    PROP_ACTIVE,
444                                    g_param_spec_int ("active",
445                                                      P_("Active item"),
446                                                      P_("The item which is currently active"),
447                                                      0,
448                                                      G_MAXINT,
449                                                      0,
450                                                      G_PARAM_READWRITE));
451
452   gtk_widget_class_install_style_property (widget_class,
453                                            g_param_spec_boolean ("appearance",
454                                                                  P_("ComboBox appareance"),
455                                                                  P_("ComboBox appearance, where TRUE means Windows-style."),
456                                                                  FALSE,
457                                                                  G_PARAM_READWRITE));
458
459   g_type_class_add_private (object_class, sizeof (GtkComboBoxPrivate));
460 }
461
462 static void
463 gtk_combo_box_cell_layout_init (GtkCellLayoutIface *iface)
464 {
465   iface->pack_start = gtk_combo_box_cell_layout_pack_start;
466   iface->pack_end = gtk_combo_box_cell_layout_pack_end;
467   iface->clear = gtk_combo_box_cell_layout_clear;
468   iface->add_attribute = gtk_combo_box_cell_layout_add_attribute;
469   iface->set_cell_data_func = gtk_combo_box_cell_layout_set_cell_data_func;
470   iface->clear_attributes = gtk_combo_box_cell_layout_clear_attributes;
471   iface->reorder = gtk_combo_box_cell_layout_reorder;
472 }
473
474 static void
475 gtk_combo_box_init (GtkComboBox *combo_box)
476 {
477   combo_box->priv = GTK_COMBO_BOX_GET_PRIVATE (combo_box);
478
479   combo_box->priv->cell_view = gtk_cell_view_new ();
480   gtk_container_add (GTK_CONTAINER (combo_box), combo_box->priv->cell_view);
481   gtk_widget_show (combo_box->priv->cell_view);
482
483   combo_box->priv->width = 0;
484   combo_box->priv->wrap_width = 0;
485
486   combo_box->priv->active_item = -1;
487   combo_box->priv->col_column = -1;
488   combo_box->priv->row_column = -1;
489 }
490
491 static void
492 gtk_combo_box_set_property (GObject      *object,
493                             guint         prop_id,
494                             const GValue *value,
495                             GParamSpec   *pspec)
496 {
497   GtkComboBox *combo_box = GTK_COMBO_BOX (object);
498
499   switch (prop_id)
500     {
501       case PROP_MODEL:
502         gtk_combo_box_set_model (combo_box, g_value_get_object (value));
503         break;
504
505       case PROP_WRAP_WIDTH:
506         gtk_combo_box_set_wrap_width (combo_box, g_value_get_int (value));
507         break;
508
509       case PROP_ROW_SPAN_COLUMN:
510         gtk_combo_box_set_row_span_column (combo_box, g_value_get_int (value));
511         break;
512
513       case PROP_COLUMN_SPAN_COLUMN:
514         gtk_combo_box_set_column_span_column (combo_box, g_value_get_int (value));
515         break;
516
517       case PROP_ACTIVE:
518         gtk_combo_box_set_active (combo_box, g_value_get_int (value));
519         break;
520
521       default:
522         break;
523     }
524 }
525
526 static void
527 gtk_combo_box_get_property (GObject    *object,
528                             guint       prop_id,
529                             GValue     *value,
530                             GParamSpec *pspec)
531 {
532   GtkComboBox *combo_box = GTK_COMBO_BOX (object);
533
534   switch (prop_id)
535     {
536       case PROP_MODEL:
537         g_value_set_object (value, combo_box->priv->model);
538         break;
539
540       case PROP_WRAP_WIDTH:
541         g_value_set_int (value, combo_box->priv->wrap_width);
542         break;
543
544       case PROP_ROW_SPAN_COLUMN:
545         g_value_set_int (value, combo_box->priv->row_column);
546         break;
547
548       case PROP_COLUMN_SPAN_COLUMN:
549         g_value_set_int (value, combo_box->priv->col_column);
550         break;
551
552       case PROP_ACTIVE:
553         g_value_set_int (value, gtk_combo_box_get_active (combo_box));
554         break;
555
556       default:
557         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
558         break;
559     }
560 }
561
562 static void
563 gtk_combo_box_style_set (GtkWidget *widget,
564                          GtkStyle  *previous)
565 {
566   gboolean appearance;
567   GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
568
569   gtk_widget_queue_resize (widget);
570
571   /* if wrap_width > 0, then we are in grid-mode and forced to use
572    * unix style
573    */
574   if (combo_box->priv->wrap_width)
575     return;
576
577   gtk_widget_style_get (widget,
578                         "appearance", &appearance,
579                         NULL);
580
581   /* TRUE is windows style */
582   if (appearance)
583     {
584       /* Destroy all the menu mode widgets, if they exist. */
585       if (GTK_IS_MENU (combo_box->priv->popup_widget))
586         gtk_combo_box_menu_destroy (combo_box);
587
588       /* Create the list mode widgets, if they don't already exist. */
589       if (!GTK_IS_TREE_VIEW (combo_box->priv->tree_view))
590         gtk_combo_box_list_setup (combo_box);
591     }
592   else
593     {
594       /* Destroy all the list mode widgets, if they exist. */
595       if (GTK_IS_TREE_VIEW (combo_box->priv->tree_view))
596         gtk_combo_box_list_destroy (combo_box);
597
598       /* Create the menu mode widgets, if they don't already exist. */
599       if (!GTK_IS_MENU (combo_box->priv->popup_widget))
600         gtk_combo_box_menu_setup (combo_box, TRUE);
601     }
602 }
603
604 static void
605 gtk_combo_box_button_toggled (GtkWidget *widget,
606                               gpointer   data)
607 {
608   GtkComboBox *combo_box = GTK_COMBO_BOX (data);
609
610   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
611     {
612       if (!combo_box->priv->popup_in_progress)
613         gtk_combo_box_popup (combo_box);
614     }
615   else
616     gtk_combo_box_popdown (combo_box);
617 }
618
619 static void
620 gtk_combo_box_add (GtkContainer *container,
621                    GtkWidget    *widget)
622 {
623   GtkComboBox *combo_box = GTK_COMBO_BOX (container);
624
625   if (combo_box->priv->cell_view && combo_box->priv->cell_view->parent)
626     gtk_container_remove (container, combo_box->priv->cell_view);
627
628   (* GTK_CONTAINER_CLASS (parent_class)->add) (container, widget);
629
630   if (combo_box->priv->cell_view &&
631       widget != combo_box->priv->cell_view)
632     {
633       /* since the cell_view was unparented, it's gone now */
634       combo_box->priv->cell_view = NULL;
635
636       if (!combo_box->priv->tree_view && combo_box->priv->separator)
637         {
638           gtk_widget_unparent (combo_box->priv->separator);
639           combo_box->priv->separator = NULL;
640
641           g_object_ref (G_OBJECT (combo_box->priv->arrow));
642           gtk_widget_unparent (combo_box->priv->arrow);
643           gtk_container_add (GTK_CONTAINER (combo_box->priv->button),
644                              combo_box->priv->arrow);
645           g_object_unref (G_OBJECT (combo_box->priv->arrow));
646
647           gtk_widget_queue_resize (GTK_WIDGET (container));
648         }
649       else if (combo_box->priv->cell_view_frame)
650         {
651           gtk_widget_unparent (combo_box->priv->cell_view_frame);
652           combo_box->priv->cell_view_frame = NULL;
653         }
654     }
655 }
656
657 static ComboCellInfo *
658 gtk_combo_box_get_cell_info (GtkComboBox     *combo_box,
659                              GtkCellRenderer *cell)
660 {
661   GSList *i;
662
663   for (i = combo_box->priv->cells; i; i = i->next)
664     {
665       ComboCellInfo *info = (ComboCellInfo *)i->data;
666
667       if (info && info->cell == cell)
668         return info;
669     }
670
671   return NULL;
672 }
673
674 static void
675 gtk_combo_box_menu_show (GtkWidget *menu,
676                          gpointer   user_data)
677 {
678   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
679
680   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
681                                 TRUE);
682   combo_box->priv->popup_in_progress = FALSE;
683 }
684
685 static void
686 gtk_combo_box_menu_hide (GtkWidget *menu,
687                          gpointer   user_data)
688 {
689   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
690
691   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
692                                 FALSE);
693 }
694
695 static void
696 gtk_combo_box_detacher (GtkWidget *widget,
697                         GtkMenu   *menu)
698 {
699   GtkComboBox *combo_box;
700
701   g_return_if_fail (GTK_IS_COMBO_BOX (widget));
702
703   combo_box = GTK_COMBO_BOX (widget);
704   g_return_if_fail (combo_box->priv->popup_widget == (GtkWidget*) menu);
705
706   g_signal_handlers_disconnect_by_func (menu,
707                                         gtk_combo_box_menu_show,
708                                         combo_box);
709   g_signal_handlers_disconnect_by_func (menu,
710                                         gtk_combo_box_menu_hide,
711                                         combo_box);
712   
713   combo_box->priv->popup_widget = NULL;
714 }
715
716 static void
717 gtk_combo_box_set_popup_widget (GtkComboBox *combo_box,
718                                 GtkWidget   *popup)
719 {
720   if (GTK_IS_MENU (combo_box->priv->popup_widget))
721     {
722       gtk_menu_detach (GTK_MENU (combo_box->priv->popup_widget));
723       combo_box->priv->popup_widget = NULL;
724     }
725   else if (combo_box->priv->popup_widget)
726     {
727       gtk_container_remove (GTK_CONTAINER (combo_box->priv->popup_frame),
728                             combo_box->priv->popup_widget);
729       g_object_unref (G_OBJECT (combo_box->priv->popup_widget));
730       combo_box->priv->popup_widget = NULL;
731     }
732
733   if (GTK_IS_MENU (popup))
734     {
735       if (combo_box->priv->popup_window)
736         {
737           gtk_widget_destroy (combo_box->priv->popup_window);
738           combo_box->priv->popup_window = NULL;
739           combo_box->priv->popup_frame = NULL;
740         }
741
742       combo_box->priv->popup_widget = popup;
743
744       g_signal_connect (popup, "show",
745                         G_CALLBACK (gtk_combo_box_menu_show), combo_box);
746       g_signal_connect (popup, "hide",
747                         G_CALLBACK (gtk_combo_box_menu_hide), combo_box);
748
749       gtk_menu_attach_to_widget (GTK_MENU (popup),
750                                  GTK_WIDGET (combo_box),
751                                  gtk_combo_box_detacher);
752     }
753   else
754     {
755       if (!combo_box->priv->popup_window)
756         {
757           combo_box->priv->popup_window = gtk_window_new (GTK_WINDOW_POPUP);
758           gtk_window_set_resizable (GTK_WINDOW (combo_box->priv->popup_window), FALSE);
759           gtk_window_set_screen (GTK_WINDOW (combo_box->priv->popup_window),
760                                  gtk_widget_get_screen (GTK_WIDGET (combo_box)));
761
762           combo_box->priv->popup_frame = gtk_frame_new (NULL);
763           gtk_frame_set_shadow_type (GTK_FRAME (combo_box->priv->popup_frame),
764                                      GTK_SHADOW_ETCHED_IN);
765           gtk_container_add (GTK_CONTAINER (combo_box->priv->popup_window),
766                              combo_box->priv->popup_frame);
767
768           gtk_widget_show (combo_box->priv->popup_frame);
769         }
770
771       gtk_container_add (GTK_CONTAINER (combo_box->priv->popup_frame),
772                          popup);
773       gtk_widget_show (popup);
774       g_object_ref (G_OBJECT (popup));
775       combo_box->priv->popup_widget = popup;
776     }
777 }
778
779 static void
780 gtk_combo_box_menu_position (GtkMenu  *menu,
781                              gint     *x,
782                              gint     *y,
783                              gint     *push_in,
784                              gpointer  user_data)
785 {
786   gint sx, sy;
787   GtkWidget *child;
788   GtkRequisition req;
789   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
790
791   /* FIXME: is using the size request here broken? */
792   child = GTK_BIN (combo_box)->child;
793
794   gdk_window_get_origin (child->window, &sx, &sy);
795
796   gtk_widget_size_request (GTK_WIDGET (menu), &req);
797
798   if (gtk_widget_get_direction (GTK_WIDGET (combo_box)) == GTK_TEXT_DIR_RTL)
799     *x = sx;
800   else
801     *x = sx + child->allocation.width - req.width;
802   *y = sy + child->allocation.height;
803
804   if (GTK_WIDGET_NO_WINDOW (child))
805     {
806       *x += child->allocation.x;
807       *y += child->allocation.y;
808     }
809
810   *push_in = TRUE;
811 }
812
813 static void
814 gtk_combo_box_list_position (GtkComboBox *combo_box, 
815                              gint        *x, 
816                              gint        *y, 
817                              gint        *width,
818                              gint        *height)
819 {
820   GtkWidget *sample;
821   GdkScreen *screen;
822   gint monitor_num;
823   GdkRectangle monitor;
824   GtkRequisition popup_req;
825   
826   sample = GTK_BIN (combo_box)->child;
827
828   *width = sample->allocation.width;
829   gtk_widget_size_request (combo_box->priv->popup_window, &popup_req);
830   *height = popup_req.height;
831
832   gdk_window_get_origin (sample->window, x, y);
833
834   if (combo_box->priv->cell_view_frame)
835     {
836        *x -= GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
837              GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness;
838        *width += 2 * (GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
839             GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness);
840     }
841
842   if (GTK_WIDGET_NO_WINDOW (sample))
843     {
844       *x += sample->allocation.x;
845       *y += sample->allocation.y;
846     }
847   
848   screen = gtk_widget_get_screen (GTK_WIDGET (combo_box));
849   monitor_num = gdk_screen_get_monitor_at_window (screen, 
850                                                   GTK_WIDGET (combo_box)->window);
851   gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
852   
853   if (*x < monitor.x)
854     *x = monitor.x;
855   else if (*x + *width > monitor.x + monitor.width)
856     *x = monitor.x + monitor.width - *width;
857   
858   if (*y + sample->allocation.height + *height <= monitor.y + monitor.height)
859     *y += sample->allocation.height;
860   else
861     *y -= *height;
862
863
864 /**
865  * gtk_combo_box_popup:
866  * @combo_box: a #GtkComboBox
867  * 
868  * Pops up the menu or dropdown list of @combo_box. 
869  *
870  * This function is mostly intended for use by accessibility technologies;
871  * applications should have little use for it.
872  *
873  * Since: 2.4
874  **/
875 void
876 gtk_combo_box_popup (GtkComboBox *combo_box)
877 {
878   gint x, y, width, height;
879
880   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
881
882   if (GTK_WIDGET_MAPPED (combo_box->priv->popup_widget))
883     return;
884
885   if (GTK_IS_MENU (combo_box->priv->popup_widget))
886     {
887       if (combo_box->priv->active_item != -1)
888         {
889           GList *childs;
890
891           childs = gtk_container_get_children (GTK_CONTAINER (combo_box->priv->popup_widget));
892           gtk_menu_shell_select_item (GTK_MENU_SHELL (combo_box->priv->popup_widget),
893                                       g_list_nth_data (childs, combo_box->priv->active_item));
894           g_list_free (childs);
895         }
896
897       gtk_menu_popup (GTK_MENU (combo_box->priv->popup_widget),
898                       NULL, NULL,
899                       gtk_combo_box_menu_position, combo_box,
900                       0, 0);
901       return;
902     }
903
904   gtk_widget_show_all (combo_box->priv->popup_frame);
905   gtk_combo_box_list_position (combo_box, &x, &y, &width, &height);
906
907   gtk_widget_set_size_request (combo_box->priv->popup_window, width, -1);  
908   gtk_window_move (GTK_WINDOW (combo_box->priv->popup_window), x, y);
909
910   /* popup */
911   gtk_widget_show (combo_box->priv->popup_window);
912
913   gtk_widget_grab_focus (combo_box->priv->popup_window);
914   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
915                                 TRUE);
916
917   if (!GTK_WIDGET_HAS_FOCUS (combo_box->priv->tree_view))
918     {
919       gdk_keyboard_grab (combo_box->priv->popup_window->window,
920                          FALSE, GDK_CURRENT_TIME);
921       gtk_widget_grab_focus (combo_box->priv->tree_view);
922     }
923
924   gtk_grab_add (combo_box->priv->popup_window);
925   gdk_pointer_grab (combo_box->priv->popup_window->window, TRUE,
926                     GDK_BUTTON_PRESS_MASK |
927                     GDK_BUTTON_RELEASE_MASK |
928                     GDK_POINTER_MOTION_MASK,
929                     NULL, NULL, GDK_CURRENT_TIME);
930
931   gtk_grab_add (combo_box->priv->tree_view);
932 }
933
934 /**
935  * gtk_combo_box_popdown:
936  * @combo_box: a #GtkComboBox
937  * 
938  * Hides the menu or dropdown list of @combo_box.
939  *
940  * This function is mostly intended for use by accessibility technologies;
941  * applications should have little use for it.
942  *
943  * Since: 2.4
944  **/
945 void
946 gtk_combo_box_popdown (GtkComboBox *combo_box)
947 {
948   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
949
950   if (GTK_IS_MENU (combo_box->priv->popup_widget))
951     {
952       gtk_menu_popdown (GTK_MENU (combo_box->priv->popup_widget));
953       return;
954     }
955
956   gtk_combo_box_list_remove_grabs (combo_box);
957   gtk_widget_hide_all (combo_box->priv->popup_window);
958   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
959                                 FALSE);
960 }
961
962 static gint
963 gtk_combo_box_calc_requested_width (GtkComboBox *combo_box,
964                                     GtkTreePath *path)
965 {
966   gint padding;
967   GtkRequisition req;
968
969   if (combo_box->priv->cell_view)
970     gtk_widget_style_get (combo_box->priv->cell_view,
971                           "focus-line-width", &padding,
972                           NULL);
973   else
974     padding = 0;
975
976   /* add some pixels for good measure */
977   padding += BONUS_PADDING;
978
979   if (combo_box->priv->cell_view)
980     gtk_cell_view_get_size_of_row (GTK_CELL_VIEW (combo_box->priv->cell_view),
981                                    path, &req);
982   else
983     req.width = 0;
984
985   return req.width + padding;
986 }
987
988 static void
989 gtk_combo_box_remeasure (GtkComboBox *combo_box)
990 {
991   GtkTreeIter iter;
992   GtkTreePath *path;
993   gint padding = 0;
994
995   if (!gtk_tree_model_get_iter_first (combo_box->priv->model, &iter))
996     return;
997
998   combo_box->priv->width = 0;
999
1000   path = gtk_tree_path_new_from_indices (0, -1);
1001
1002   if (combo_box->priv->cell_view)
1003     gtk_widget_style_get (combo_box->priv->cell_view,
1004                           "focus-line-width", &padding,
1005                           NULL);
1006   else
1007     padding = 0;
1008
1009   /* add some pixels for good measure */
1010   padding += BONUS_PADDING;
1011
1012   do
1013     {
1014       GtkRequisition req;
1015
1016       if (combo_box->priv->cell_view)
1017         gtk_cell_view_get_size_of_row (GTK_CELL_VIEW (combo_box->priv->cell_view), 
1018                                        path, &req);
1019       else
1020         req.width = 0;
1021
1022       combo_box->priv->width = MAX (combo_box->priv->width,
1023                                     req.width + padding);
1024
1025       gtk_tree_path_next (path);
1026     }
1027   while (gtk_tree_model_iter_next (combo_box->priv->model, &iter));
1028
1029   gtk_tree_path_free (path);
1030 }
1031
1032 static void
1033 gtk_combo_box_size_request (GtkWidget      *widget,
1034                             GtkRequisition *requisition)
1035 {
1036   gint width, height;
1037   GtkRequisition bin_req;
1038
1039   GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1040
1041   /* common */
1042   gtk_widget_size_request (GTK_BIN (widget)->child, &bin_req);
1043   gtk_combo_box_remeasure (combo_box);
1044   bin_req.width = MAX (bin_req.width, combo_box->priv->width);
1045
1046   if (!combo_box->priv->tree_view)
1047     {
1048       /* menu mode */
1049
1050       if (combo_box->priv->cell_view)
1051         {
1052           GtkRequisition sep_req, arrow_req;
1053           gint border_width, xthickness, ythickness;
1054
1055           border_width = GTK_CONTAINER (combo_box->priv->button)->border_width;
1056           xthickness = combo_box->priv->button->style->xthickness;
1057           ythickness = combo_box->priv->button->style->ythickness;
1058
1059           bin_req.width = MAX (bin_req.width, combo_box->priv->width);
1060
1061           gtk_widget_size_request (combo_box->priv->separator, &sep_req);
1062           gtk_widget_size_request (combo_box->priv->arrow, &arrow_req);
1063
1064           height = MAX (sep_req.height, arrow_req.height);
1065           height = MAX (height, bin_req.height);
1066
1067           width = bin_req.width + sep_req.width + arrow_req.width;
1068
1069           height += border_width + 1 + xthickness * 2 + 4;
1070           width += border_width + 1 + ythickness * 2 + 4;
1071
1072           requisition->width = width;
1073           requisition->height = height;
1074         }
1075       else
1076         {
1077           GtkRequisition but_req;
1078
1079           gtk_widget_size_request (combo_box->priv->button, &but_req);
1080
1081           requisition->width = bin_req.width + but_req.width;
1082           requisition->height = MAX (bin_req.height, but_req.height);
1083         }
1084     }
1085   else
1086     {
1087       /* list mode */
1088       GtkRequisition button_req;
1089
1090       /* sample + frame */
1091       *requisition = bin_req;
1092
1093       if (combo_box->priv->cell_view_frame)
1094         {
1095           requisition->width += 2 *
1096             (GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
1097              GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness);
1098           requisition->height += 2 *
1099             (GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
1100              GTK_WIDGET (combo_box->priv->cell_view_frame)->style->ythickness);
1101         }
1102
1103       /* the button */
1104       gtk_widget_size_request (combo_box->priv->button, &button_req);
1105
1106       requisition->height = MAX (requisition->height, button_req.height);
1107       requisition->width += button_req.width;
1108     }
1109 }
1110
1111 static void
1112 gtk_combo_box_size_allocate (GtkWidget     *widget,
1113                              GtkAllocation *allocation)
1114 {
1115   GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1116   GtkAllocation child;
1117   GtkRequisition req;
1118   gboolean is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
1119
1120   widget->allocation = *allocation;
1121
1122   if (!combo_box->priv->tree_view)
1123     {
1124       if (combo_box->priv->cell_view)
1125         {
1126           gint border_width, xthickness, ythickness;
1127           gint width;
1128
1129           /* menu mode */
1130           gtk_widget_size_allocate (combo_box->priv->button, allocation);
1131
1132           /* set some things ready */
1133           border_width = GTK_CONTAINER (combo_box->priv->button)->border_width;
1134           xthickness = combo_box->priv->button->style->xthickness;
1135           ythickness = combo_box->priv->button->style->ythickness;
1136
1137           child.x = allocation->x + border_width + 1 + xthickness + 2;
1138           child.y = allocation->y + border_width + 1 + ythickness + 2;
1139
1140           width = allocation->width - (border_width + 1 + xthickness * 2 + 4);
1141
1142           /* handle the childs */
1143           gtk_widget_size_request (combo_box->priv->arrow, &req);
1144           child.width = req.width;
1145           child.height = allocation->height - 2 * (child.y - allocation->y);
1146           if (!is_rtl)
1147             child.x += width - req.width;
1148           gtk_widget_size_allocate (combo_box->priv->arrow, &child);
1149           if (is_rtl)
1150             child.x += req.width;
1151           gtk_widget_size_request (combo_box->priv->separator, &req);
1152           child.width = req.width;
1153           if (!is_rtl)
1154             child.x -= req.width;
1155           gtk_widget_size_allocate (combo_box->priv->separator, &child);
1156
1157           if (is_rtl)
1158             {
1159               child.x += req.width;
1160               child.width = allocation->x + allocation->width 
1161                 - (border_width + 1 + xthickness + 2) - child.x;
1162             }
1163           else 
1164             {
1165               child.width = child.x;
1166               child.x = allocation->x + border_width + 1 + xthickness + 2;
1167               child.width -= child.x;
1168             }
1169
1170           gtk_widget_size_allocate (GTK_BIN (widget)->child, &child);
1171         }
1172       else
1173         {
1174           gtk_widget_size_request (combo_box->priv->button, &req);
1175           if (is_rtl)
1176             child.x = allocation->x;
1177           else
1178             child.x = allocation->x + allocation->width - req.width;
1179           child.y = allocation->y;
1180           child.width = req.width;
1181           child.height = allocation->height;
1182           gtk_widget_size_allocate (combo_box->priv->button, &child);
1183
1184           if (is_rtl)
1185             child.x = allocation->x + req.width;
1186           else
1187             child.x = allocation->x;
1188           child.y = allocation->y;
1189           child.width = allocation->width - req.width;
1190           gtk_widget_size_allocate (GTK_BIN (widget)->child, &child);
1191         }
1192     }
1193   else
1194     {
1195       /* list mode */
1196
1197       /* button */
1198       gtk_widget_size_request (combo_box->priv->button, &req);
1199       if (is_rtl)
1200         child.x = allocation->x;
1201       else
1202         child.x = allocation->x + allocation->width - req.width;
1203       child.y = allocation->y;
1204       child.width = req.width;
1205       child.height = allocation->height;
1206       gtk_widget_size_allocate (combo_box->priv->button, &child);
1207
1208       /* frame */
1209       if (is_rtl)
1210         child.x = allocation->x + req.width;
1211       else
1212         child.x = allocation->x;
1213       child.y = allocation->y;
1214       child.width = allocation->width - req.width;
1215       child.height = allocation->height;
1216
1217       if (combo_box->priv->cell_view_frame)
1218         {
1219           gtk_widget_size_allocate (combo_box->priv->cell_view_frame, &child);
1220
1221           /* the sample */
1222           child.x +=
1223             GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
1224             GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness;
1225           child.y +=
1226             GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
1227             GTK_WIDGET (combo_box->priv->cell_view_frame)->style->ythickness;
1228           child.width -= 2 * (
1229             GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
1230             GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness);
1231           child.height -= 2 * (
1232             GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
1233             GTK_WIDGET (combo_box->priv->cell_view_frame)->style->ythickness);
1234         }
1235
1236       gtk_widget_size_allocate (GTK_BIN (combo_box)->child, &child);
1237     }
1238 }
1239
1240 static void
1241 gtk_combo_box_unset_model (GtkComboBox *combo_box)
1242 {
1243   if (combo_box->priv->inserted_id != -1)
1244     {
1245       g_signal_handler_disconnect (combo_box->priv->model,
1246                                    combo_box->priv->inserted_id);
1247       combo_box->priv->inserted_id = -1;
1248     }
1249   if (combo_box->priv->deleted_id != -1)
1250     {
1251       g_signal_handler_disconnect (combo_box->priv->model,
1252                                    combo_box->priv->deleted_id);
1253       combo_box->priv->deleted_id = -1;
1254     }
1255   if (combo_box->priv->changed_id != -1)
1256     {
1257       g_signal_handler_disconnect (combo_box->priv->model,
1258                                    combo_box->priv->changed_id);
1259       combo_box->priv->changed_id = -1;
1260     }
1261
1262   /* menu mode */
1263   if (!combo_box->priv->tree_view)
1264     {
1265       if (combo_box->priv->popup_widget)
1266         gtk_container_foreach (GTK_CONTAINER (combo_box->priv->popup_widget),
1267                                (GtkCallback)gtk_widget_destroy, NULL);
1268     }
1269 }
1270
1271 static void
1272 gtk_combo_box_set_model_internal (GtkComboBox *combo_box)
1273 {
1274   if (!combo_box->priv->tree_view)
1275     {
1276       /* menu mode */
1277       combo_box->priv->inserted_id =
1278         g_signal_connect (combo_box->priv->model, "row_inserted",
1279                           G_CALLBACK (gtk_combo_box_menu_row_inserted),
1280                           combo_box);
1281       combo_box->priv->deleted_id =
1282         g_signal_connect (combo_box->priv->model, "row_deleted",
1283                           G_CALLBACK (gtk_combo_box_menu_row_deleted),
1284                           combo_box);
1285       combo_box->priv->changed_id =
1286         g_signal_connect (combo_box->priv->model, "row_changed",
1287                           G_CALLBACK (gtk_combo_box_menu_row_changed),
1288                           combo_box);
1289     }
1290   else
1291     {
1292       /* list mode */
1293       gtk_tree_view_set_model (GTK_TREE_VIEW (combo_box->priv->tree_view),
1294                                combo_box->priv->model);
1295
1296       combo_box->priv->changed_id =
1297         g_signal_connect (combo_box->priv->model, "row_changed",
1298                           G_CALLBACK (gtk_combo_box_list_row_changed),
1299                           combo_box);
1300     }
1301 }
1302
1303 static void
1304 gtk_combo_box_forall (GtkContainer *container,
1305                       gboolean      include_internals,
1306                       GtkCallback   callback,
1307                       gpointer      callback_data)
1308 {
1309   GtkComboBox *combo_box = GTK_COMBO_BOX (container);
1310
1311   if (include_internals)
1312     {
1313       if (combo_box->priv->button)
1314         (* callback) (combo_box->priv->button, callback_data);
1315       if (combo_box->priv->separator)
1316         (* callback) (combo_box->priv->separator, callback_data);
1317       if (combo_box->priv->arrow)
1318         (* callback) (combo_box->priv->arrow, callback_data);
1319       if (combo_box->priv->cell_view_frame)
1320         (* callback) (combo_box->priv->cell_view_frame, callback_data);
1321     }
1322
1323   if (GTK_BIN (container)->child)
1324     (* callback) (GTK_BIN (container)->child, callback_data);
1325 }
1326
1327 static gboolean
1328 gtk_combo_box_expose_event (GtkWidget      *widget,
1329                             GdkEventExpose *event)
1330 {
1331   GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1332
1333   if (!combo_box->priv->tree_view)
1334     {
1335       gtk_container_propagate_expose (GTK_CONTAINER (widget),
1336                                       combo_box->priv->button, event);
1337
1338       if (combo_box->priv->separator)
1339         {
1340           gtk_container_propagate_expose (GTK_CONTAINER (combo_box->priv->button),
1341                                           combo_box->priv->separator, event);
1342
1343           /* if not in this case, arrow gets its expose event from button */
1344           gtk_container_propagate_expose (GTK_CONTAINER (combo_box->priv->button),
1345                                           combo_box->priv->arrow, event);
1346         }
1347     }
1348   else
1349     {
1350       gtk_container_propagate_expose (GTK_CONTAINER (widget),
1351                                       combo_box->priv->button, event);
1352
1353       if (combo_box->priv->cell_view_frame)
1354         gtk_container_propagate_expose (GTK_CONTAINER (widget),
1355                                         combo_box->priv->cell_view_frame, event);
1356     }
1357
1358   gtk_container_propagate_expose (GTK_CONTAINER (widget),
1359                                   GTK_BIN (widget)->child, event);
1360
1361   return FALSE;
1362 }
1363
1364 static gboolean
1365 gtk_combo_box_scroll_event (GtkWidget          *widget,
1366                             GdkEventScroll     *event)
1367 {
1368   GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1369   gint index;
1370   gint items;
1371     
1372   index = gtk_combo_box_get_active (combo_box);
1373
1374   if (index != -1)
1375     {
1376       items = gtk_tree_model_iter_n_children (combo_box->priv->model, NULL);
1377       
1378       if (event->direction == GDK_SCROLL_UP)
1379         index--;
1380       else 
1381         index++;
1382
1383       gtk_combo_box_set_active (combo_box, CLAMP (index, 0, items - 1));
1384     }
1385
1386   return TRUE;
1387 }
1388
1389 /*
1390  * menu style
1391  */
1392
1393 static void
1394 cell_view_sync_cells (GtkComboBox *combo_box,
1395                       GtkCellView *cell_view)
1396 {
1397   GSList *k;
1398
1399   for (k = combo_box->priv->cells; k; k = k->next)
1400     {
1401       GSList *j;
1402       ComboCellInfo *info = (ComboCellInfo *)k->data;
1403
1404       if (info->pack == GTK_PACK_START)
1405         gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (cell_view),
1406                                     info->cell, info->expand);
1407       else if (info->pack == GTK_PACK_END)
1408         gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (cell_view),
1409                                   info->cell, info->expand);
1410
1411       gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (cell_view),
1412                                           info->cell,
1413                                           info->func, info->func_data, NULL);
1414
1415       for (j = info->attributes; j; j = j->next->next)
1416         {
1417           gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (cell_view),
1418                                          info->cell,
1419                                          j->data,
1420                                          GPOINTER_TO_INT (j->next->data));
1421         }
1422     }
1423 }
1424
1425 static void
1426 gtk_combo_box_menu_setup (GtkComboBox *combo_box,
1427                           gboolean     add_childs)
1428 {
1429   GtkWidget *box;
1430
1431   /* Unset any existing model. */
1432   gtk_combo_box_unset_model (combo_box);
1433
1434   if (combo_box->priv->cell_view)
1435     {
1436       combo_box->priv->button = gtk_toggle_button_new ();
1437       g_signal_connect (combo_box->priv->button, "toggled",
1438                         G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
1439       gtk_widget_set_parent (combo_box->priv->button,
1440                              GTK_BIN (combo_box)->child->parent);
1441
1442       combo_box->priv->separator = gtk_vseparator_new ();
1443       gtk_widget_set_parent (combo_box->priv->separator,
1444                              combo_box->priv->button);
1445       gtk_widget_show (combo_box->priv->separator);
1446
1447       combo_box->priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
1448       gtk_widget_set_parent (combo_box->priv->arrow, combo_box->priv->button);
1449       gtk_widget_show (combo_box->priv->arrow);
1450
1451       gtk_widget_show_all (combo_box->priv->button);
1452
1453       if (GTK_WIDGET_MAPPED (GTK_BIN (combo_box)->child))
1454         {
1455           /* I have no clue why, but we need to manually map in this case. */
1456           gtk_widget_map (combo_box->priv->separator);
1457           gtk_widget_map (combo_box->priv->arrow);
1458         }
1459     }
1460   else
1461     {
1462       combo_box->priv->button = gtk_toggle_button_new ();
1463       g_signal_connect (combo_box->priv->button, "toggled",
1464                         G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
1465       gtk_widget_set_parent (combo_box->priv->button,
1466                              GTK_BIN (combo_box)->child->parent);
1467
1468       combo_box->priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
1469       gtk_container_add (GTK_CONTAINER (combo_box->priv->button),
1470                          combo_box->priv->arrow);
1471       gtk_widget_show_all (combo_box->priv->button);
1472     }
1473
1474   g_signal_connect (combo_box->priv->button, "button_press_event",
1475                     G_CALLBACK (gtk_combo_box_menu_button_press),
1476                     combo_box);
1477
1478   /* create our funky menu */
1479   box = gtk_menu_new ();
1480   gtk_combo_box_set_popup_widget (combo_box, box);
1481
1482   /* set the models */
1483   gtk_combo_box_set_model_internal (combo_box);
1484
1485   /* add items */
1486   if (add_childs)
1487     gtk_combo_box_menu_fill (combo_box);
1488
1489 }
1490
1491 static void
1492 gtk_combo_box_menu_fill (GtkComboBox *combo_box)
1493 {
1494   gint i, items;
1495   GtkWidget *menu;
1496   GtkWidget *tmp;
1497
1498   if (!combo_box->priv->model)
1499     return;
1500
1501   items = gtk_tree_model_iter_n_children (combo_box->priv->model, NULL);
1502   menu = combo_box->priv->popup_widget;
1503
1504   for (i = 0; i < items; i++)
1505     {
1506       GtkTreePath *path;
1507
1508       path = gtk_tree_path_new_from_indices (i, -1);
1509       tmp = gtk_cell_view_menu_item_new_from_model (combo_box->priv->model,
1510                                                     path);
1511       g_signal_connect (tmp, "activate",
1512                         G_CALLBACK (gtk_combo_box_menu_item_activate),
1513                         combo_box);
1514
1515       cell_view_sync_cells (combo_box,
1516                             GTK_CELL_VIEW (GTK_BIN (tmp)->child));
1517
1518       gtk_menu_shell_append (GTK_MENU_SHELL (menu), tmp);
1519       gtk_widget_show (tmp);
1520
1521       gtk_tree_path_free (path);
1522     }
1523 }
1524
1525 static void
1526 gtk_combo_box_menu_destroy (GtkComboBox *combo_box)
1527 {
1528   /* disconnect signal handlers */
1529   gtk_combo_box_unset_model (combo_box);
1530
1531   g_signal_handlers_disconnect_matched (combo_box->priv->button,
1532                                         G_SIGNAL_MATCH_DATA,
1533                                         0, 0, NULL,
1534                                         gtk_combo_box_menu_button_press, NULL);
1535
1536   /* unparent will remove our latest ref */
1537   if (combo_box->priv->cell_view)
1538     {
1539       gtk_widget_unparent (combo_box->priv->arrow);
1540       combo_box->priv->arrow = NULL;
1541
1542       gtk_widget_unparent (combo_box->priv->separator);
1543       combo_box->priv->separator = NULL;
1544
1545       gtk_widget_unparent (combo_box->priv->button);
1546       combo_box->priv->button = NULL;
1547     }
1548   else
1549     {
1550       /* will destroy the arrow too */
1551       gtk_widget_unparent (combo_box->priv->button);
1552
1553       combo_box->priv->button = NULL;
1554       combo_box->priv->arrow = NULL;
1555     }
1556
1557   /* changing the popup window will unref the menu and the childs */
1558 }
1559
1560 /*
1561  * grid
1562  */
1563
1564 static void
1565 gtk_combo_box_item_get_size (GtkComboBox *combo_box,
1566                              gint         index,
1567                              gint        *cols,
1568                              gint        *rows)
1569 {
1570   GtkTreeIter iter;
1571
1572   gtk_tree_model_iter_nth_child (combo_box->priv->model, &iter, NULL, index);
1573
1574   if (cols)
1575     {
1576       if (combo_box->priv->col_column == -1)
1577         *cols = 1;
1578       else
1579         gtk_tree_model_get (combo_box->priv->model, &iter,
1580                             combo_box->priv->col_column, cols,
1581                             -1);
1582     }
1583
1584   if (rows)
1585     {
1586       if (combo_box->priv->row_column == -1)
1587         *rows = 1;
1588       else
1589         gtk_tree_model_get (combo_box->priv->model, &iter,
1590                             combo_box->priv->row_column, rows,
1591                             -1);
1592     }
1593 }
1594
1595 static gboolean
1596 menu_occupied (GtkMenu *menu,
1597                guint    left_attach,
1598                guint    right_attach,
1599                guint    top_attach,
1600                guint    bottom_attach)
1601 {
1602   GList *i;
1603
1604   g_return_val_if_fail (GTK_IS_MENU (menu), TRUE);
1605   g_return_val_if_fail (left_attach < right_attach, TRUE);
1606   g_return_val_if_fail (top_attach < bottom_attach, TRUE);
1607
1608   for (i = GTK_MENU_SHELL (menu)->children; i; i = i->next)
1609     {
1610       guint l, r, b, t;
1611       gboolean h_intersect = FALSE;
1612       gboolean v_intersect = FALSE;
1613
1614       gtk_container_child_get (GTK_CONTAINER (menu), i->data,
1615                                "left_attach", &l,
1616                                "right_attach", &r,
1617                                "bottom_attach", &b,
1618                                "top_attach", &t,
1619                                NULL);
1620
1621       /* look if this item intersects with the given coordinates */
1622       h_intersect  = left_attach <= l && l <= right_attach;
1623       h_intersect &= left_attach <= r && r <= right_attach;
1624
1625       v_intersect  = top_attach <= t && t <= bottom_attach;
1626       v_intersect &= top_attach <= b && b <= bottom_attach;
1627
1628       if (h_intersect && v_intersect)
1629         return TRUE;
1630     }
1631
1632   return FALSE;
1633 }
1634
1635 static void
1636 gtk_combo_box_relayout_item (GtkComboBox *combo_box,
1637                              gint         index)
1638 {
1639   gint current_col = 0, current_row = 0;
1640   gint rows, cols;
1641   GList *list;
1642   GtkWidget *item;
1643   GtkWidget *menu;
1644
1645   menu = combo_box->priv->popup_widget;
1646   if (!GTK_IS_MENU_SHELL (menu))
1647     return;
1648
1649   list = gtk_container_get_children (GTK_CONTAINER (menu));
1650   item = g_list_nth_data (list, index);
1651   g_list_free (list);
1652
1653   gtk_combo_box_item_get_size (combo_box, index, &cols, &rows);
1654
1655   /* look for a good spot */
1656   while (1)
1657     {
1658       if (current_col + cols > combo_box->priv->wrap_width)
1659         {
1660           current_col = 0;
1661           current_row++;
1662         }
1663
1664       if (!menu_occupied (GTK_MENU (menu),
1665                           current_col, current_col + cols,
1666                           current_row, current_row + rows))
1667         break;
1668
1669       current_col++;
1670     }
1671
1672   /* set attach props */
1673   gtk_menu_attach (GTK_MENU (menu), item,
1674                    current_col, current_col + cols,
1675                    current_row, current_row + rows);
1676 }
1677
1678 static void
1679 gtk_combo_box_relayout (GtkComboBox *combo_box)
1680 {
1681   gint i, items;
1682   GList *list, *j;
1683   GtkWidget *menu;
1684
1685   /* ensure we are in menu style */
1686   if (combo_box->priv->tree_view)
1687     gtk_combo_box_list_destroy (combo_box);
1688
1689   menu = combo_box->priv->popup_widget;
1690
1691   if (!GTK_IS_MENU_SHELL (menu))
1692     {
1693       gtk_combo_box_menu_setup (combo_box, FALSE);
1694       menu = combo_box->priv->popup_widget;
1695     }
1696
1697   /* get rid of all children */
1698   g_return_if_fail (GTK_IS_MENU_SHELL (menu));
1699
1700   list = gtk_container_get_children (GTK_CONTAINER (menu));
1701
1702   for (j = g_list_last (list); j; j = j->prev)
1703     gtk_container_remove (GTK_CONTAINER (menu), j->data);
1704
1705   g_list_free (list);
1706
1707   /* and relayout */
1708   items = gtk_tree_model_iter_n_children (combo_box->priv->model, NULL);
1709
1710   for (i = 0; i < items; i++)
1711     {
1712       GtkWidget *tmp;
1713       GtkTreePath *path;
1714
1715       path = gtk_tree_path_new_from_indices (i, -1);
1716       tmp = gtk_cell_view_menu_item_new_from_model (combo_box->priv->model,
1717                                                     path);
1718
1719       g_signal_connect (tmp, "activate",
1720                         G_CALLBACK (gtk_combo_box_menu_item_activate),
1721                         combo_box);
1722
1723       cell_view_sync_cells (combo_box, GTK_CELL_VIEW (GTK_BIN (tmp)->child));
1724
1725       gtk_menu_shell_insert (GTK_MENU_SHELL (menu), tmp, i);
1726
1727       if (combo_box->priv->wrap_width)
1728         gtk_combo_box_relayout_item (combo_box, i);
1729
1730       gtk_widget_show (tmp);
1731
1732       gtk_tree_path_free (path);
1733     }
1734 }
1735
1736 /* callbacks */
1737 static gboolean
1738 gtk_combo_box_menu_button_press (GtkWidget      *widget,
1739                                  GdkEventButton *event,
1740                                  gpointer        user_data)
1741 {
1742   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1743
1744   if (! GTK_IS_MENU (combo_box->priv->popup_widget))
1745     return FALSE;
1746
1747   if (event->type == GDK_BUTTON_PRESS && event->button == 1)
1748     {
1749       combo_box->priv->popup_in_progress = TRUE;
1750       gtk_menu_popup (GTK_MENU (combo_box->priv->popup_widget),
1751                       NULL, NULL,
1752                       gtk_combo_box_menu_position, combo_box,
1753                       event->button, event->time);
1754
1755       return TRUE;
1756     }
1757
1758   return FALSE;
1759 }
1760
1761 static void
1762 gtk_combo_box_menu_item_activate (GtkWidget *item,
1763                                   gpointer   user_data)
1764 {
1765   gint index;
1766   GtkWidget *menu;
1767   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1768
1769   menu = combo_box->priv->popup_widget;
1770   g_return_if_fail (GTK_IS_MENU (menu));
1771
1772   index = g_list_index (GTK_MENU_SHELL (menu)->children, item);
1773
1774   gtk_combo_box_set_active (combo_box, index);
1775 }
1776
1777 static void
1778 gtk_combo_box_menu_row_inserted (GtkTreeModel *model,
1779                                  GtkTreePath  *path,
1780                                  GtkTreeIter  *iter,
1781                                  gpointer      user_data)
1782 {
1783   GtkWidget *menu;
1784   GtkWidget *item;
1785   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1786
1787   if (!combo_box->priv->popup_widget)
1788     return;
1789
1790   menu = combo_box->priv->popup_widget;
1791   g_return_if_fail (GTK_IS_MENU (menu));
1792
1793   item = gtk_cell_view_menu_item_new_from_model (model, path);
1794   g_signal_connect (item, "activate",
1795                     G_CALLBACK (gtk_combo_box_menu_item_activate),
1796                     combo_box);
1797
1798   cell_view_sync_cells (combo_box, GTK_CELL_VIEW (GTK_BIN (item)->child));
1799
1800   gtk_menu_shell_insert (GTK_MENU_SHELL (menu), item,
1801                          gtk_tree_path_get_indices (path)[0]);
1802   gtk_widget_show (item);
1803 }
1804
1805 static void
1806 gtk_combo_box_menu_row_deleted (GtkTreeModel *model,
1807                                 GtkTreePath  *path,
1808                                 gpointer      user_data)
1809 {
1810   gint index, items;
1811   GtkWidget *menu;
1812   GtkWidget *item;
1813   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1814
1815   if (!combo_box->priv->popup_widget)
1816     return;
1817
1818   index = gtk_tree_path_get_indices (path)[0];
1819   items = gtk_tree_model_iter_n_children (model, NULL);
1820
1821   if (gtk_combo_box_get_active (combo_box) == index)
1822     gtk_combo_box_set_active (combo_box, index + 1 % items);
1823
1824   menu = combo_box->priv->popup_widget;
1825   g_return_if_fail (GTK_IS_MENU (menu));
1826
1827   item = g_list_nth_data (GTK_MENU_SHELL (menu)->children, index);
1828   g_return_if_fail (GTK_IS_MENU_ITEM (item));
1829
1830   gtk_container_remove (GTK_CONTAINER (menu), item);
1831 }
1832
1833 static void
1834 gtk_combo_box_menu_row_changed (GtkTreeModel *model,
1835                                 GtkTreePath  *path,
1836                                 GtkTreeIter  *iter,
1837                                 gpointer      user_data)
1838 {
1839   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1840   gint width;
1841
1842   if (!combo_box->priv->popup_widget)
1843     return;
1844
1845   if (combo_box->priv->wrap_width)
1846     gtk_combo_box_relayout_item (combo_box,
1847                                  gtk_tree_path_get_indices (path)[0]);
1848
1849   width = gtk_combo_box_calc_requested_width (combo_box, path);
1850
1851   if (width > combo_box->priv->width)
1852     {
1853       if (combo_box->priv->cell_view)
1854         {
1855           gtk_widget_set_size_request (combo_box->priv->cell_view, width, -1);
1856           gtk_widget_queue_resize (combo_box->priv->cell_view);
1857         }
1858       combo_box->priv->width = width;
1859     }
1860 }
1861
1862 /*
1863  * list style
1864  */
1865
1866 static void
1867 gtk_combo_box_list_setup (GtkComboBox *combo_box)
1868 {
1869   GSList *i;
1870   GtkTreeSelection *sel;
1871
1872   /* Unset any existing model. */
1873   gtk_combo_box_unset_model (combo_box);
1874
1875   combo_box->priv->button = gtk_toggle_button_new ();
1876   gtk_widget_set_parent (combo_box->priv->button,
1877                          GTK_BIN (combo_box)->child->parent);
1878   g_signal_connect (combo_box->priv->button, "button_press_event",
1879                     G_CALLBACK (gtk_combo_box_list_button_pressed), combo_box);
1880   g_signal_connect (combo_box->priv->button, "toggled",
1881                     G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
1882
1883   combo_box->priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
1884   gtk_container_add (GTK_CONTAINER (combo_box->priv->button),
1885                      combo_box->priv->arrow);
1886   combo_box->priv->separator = NULL;
1887   gtk_widget_show_all (combo_box->priv->button);
1888
1889   if (combo_box->priv->cell_view)
1890     {
1891       combo_box->priv->cell_view_frame = gtk_frame_new (NULL);
1892       gtk_widget_set_parent (combo_box->priv->cell_view_frame,
1893                              GTK_BIN (combo_box)->child->parent);
1894       gtk_frame_set_shadow_type (GTK_FRAME (combo_box->priv->cell_view_frame),
1895                                  GTK_SHADOW_IN);
1896
1897       g_object_set (G_OBJECT (combo_box->priv->cell_view),
1898                     "background", "white",
1899                     "background_set", TRUE,
1900                     NULL);
1901
1902       gtk_widget_show (combo_box->priv->cell_view_frame);
1903     }
1904
1905   combo_box->priv->tree_view = gtk_tree_view_new ();
1906   sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (combo_box->priv->tree_view));
1907   gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1908   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (combo_box->priv->tree_view),
1909                                      FALSE);
1910
1911   g_signal_connect (combo_box->priv->tree_view, "button_press_event",
1912                     G_CALLBACK (gtk_combo_box_list_button_pressed),
1913                     combo_box);
1914   g_signal_connect (combo_box->priv->tree_view, "button_release_event",
1915                     G_CALLBACK (gtk_combo_box_list_button_released),
1916                     combo_box);
1917   g_signal_connect (combo_box->priv->tree_view, "key_press_event",
1918                     G_CALLBACK (gtk_combo_box_list_key_press),
1919                     combo_box);
1920
1921   combo_box->priv->column = gtk_tree_view_column_new ();
1922   gtk_tree_view_append_column (GTK_TREE_VIEW (combo_box->priv->tree_view),
1923                                combo_box->priv->column);
1924
1925   /* set the models */
1926   gtk_combo_box_set_model_internal (combo_box);
1927
1928   /* sync up */
1929   for (i = combo_box->priv->cells; i; i = i->next)
1930     {
1931       GSList *j;
1932       ComboCellInfo *info = (ComboCellInfo *)i->data;
1933
1934       if (info->pack == GTK_PACK_START)
1935         gtk_tree_view_column_pack_start (combo_box->priv->column,
1936                                          info->cell, info->expand);
1937       else if (info->pack == GTK_PACK_END)
1938         gtk_tree_view_column_pack_end (combo_box->priv->column,
1939                                        info->cell, info->expand);
1940
1941       for (j = info->attributes; j; j = j->next->next)
1942         {
1943           gtk_tree_view_column_add_attribute (combo_box->priv->column,
1944                                               info->cell,
1945                                               j->data,
1946                                               GPOINTER_TO_INT (j->next->data));
1947         }
1948     }
1949
1950   if (combo_box->priv->active_item != -1)
1951     {
1952       GtkTreePath *path;
1953
1954       path = gtk_tree_path_new_from_indices (combo_box->priv->active_item, -1);
1955       if (path)
1956         {
1957           gtk_tree_view_set_cursor (GTK_TREE_VIEW (combo_box->priv->tree_view),
1958                                     path, NULL, FALSE);
1959           gtk_tree_path_free (path);
1960         }
1961     }
1962
1963   /* set sample/popup widgets */
1964   gtk_combo_box_set_popup_widget (combo_box, combo_box->priv->tree_view);
1965
1966   gtk_widget_show (combo_box->priv->tree_view);
1967 }
1968
1969 static void
1970 gtk_combo_box_list_destroy (GtkComboBox *combo_box)
1971 {
1972   /* disconnect signals */
1973   gtk_combo_box_unset_model (combo_box);
1974
1975   g_signal_handlers_disconnect_matched (combo_box->priv->tree_view,
1976                                         G_SIGNAL_MATCH_DATA,
1977                                         0, 0, NULL, NULL, combo_box);
1978   g_signal_handlers_disconnect_matched (combo_box->priv->button,
1979                                         G_SIGNAL_MATCH_DATA,
1980                                         0, 0, NULL,
1981                                         gtk_combo_box_list_button_pressed,
1982                                         NULL);
1983
1984   /* destroy things (unparent will kill the latest ref from us)
1985    * last unref on button will destroy the arrow
1986    */
1987   gtk_widget_unparent (combo_box->priv->button);
1988   combo_box->priv->button = NULL;
1989   combo_box->priv->arrow = NULL;
1990
1991   if (combo_box->priv->cell_view)
1992     {
1993       g_object_set (G_OBJECT (combo_box->priv->cell_view),
1994                     "background_set", FALSE,
1995                     NULL);
1996
1997       gtk_widget_unparent (combo_box->priv->cell_view_frame);
1998       combo_box->priv->cell_view_frame = NULL;
1999     }
2000
2001   gtk_widget_destroy (combo_box->priv->tree_view);
2002
2003   combo_box->priv->tree_view = NULL;
2004   combo_box->priv->popup_widget = NULL;
2005 }
2006
2007 /* callbacks */
2008 static void
2009 gtk_combo_box_list_remove_grabs (GtkComboBox *combo_box)
2010 {
2011   if (GTK_WIDGET_HAS_GRAB (combo_box->priv->tree_view))
2012     gtk_grab_remove (combo_box->priv->tree_view);
2013
2014   if (GTK_WIDGET_HAS_GRAB (combo_box->priv->popup_window))
2015     {
2016       gtk_grab_remove (combo_box->priv->popup_window);
2017       gdk_keyboard_ungrab (GDK_CURRENT_TIME);
2018       gdk_pointer_ungrab (GDK_CURRENT_TIME);
2019     }
2020 }
2021
2022 static gboolean
2023 gtk_combo_box_list_button_pressed (GtkWidget      *widget,
2024                                    GdkEventButton *event,
2025                                    gpointer        data)
2026 {
2027   GtkComboBox *combo_box = GTK_COMBO_BOX (data);
2028
2029   GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *)event);
2030
2031   if (ewidget == combo_box->priv->tree_view)
2032     return TRUE;
2033
2034   if ((ewidget != combo_box->priv->button) ||
2035       gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (combo_box->priv->button)))
2036     return FALSE;
2037
2038   gtk_combo_box_popup (combo_box);
2039
2040   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
2041                                 TRUE);
2042
2043   combo_box->priv->popup_in_progress = TRUE;
2044
2045   return TRUE;
2046 }
2047
2048 static gboolean
2049 gtk_combo_box_list_button_released (GtkWidget      *widget,
2050                                     GdkEventButton *event,
2051                                     gpointer        data)
2052 {
2053   gboolean ret;
2054   GtkTreePath *path = NULL;
2055
2056   GtkComboBox *combo_box = GTK_COMBO_BOX (data);
2057
2058   gboolean popup_in_progress = FALSE;
2059
2060   GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *)event);
2061
2062   if (combo_box->priv->popup_in_progress)
2063     {
2064       popup_in_progress = TRUE;
2065       combo_box->priv->popup_in_progress = FALSE;
2066     }
2067
2068   if (ewidget != combo_box->priv->tree_view)
2069     {
2070       if (ewidget == combo_box->priv->button &&
2071           !popup_in_progress &&
2072           gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (combo_box->priv->button)))
2073         {
2074           gtk_combo_box_popdown (combo_box);
2075           return TRUE;
2076         }
2077
2078       /* released outside treeview */
2079       if (ewidget != combo_box->priv->button)
2080         {
2081           gtk_combo_box_popdown (combo_box);
2082
2083           return TRUE;
2084         }
2085
2086       return FALSE;
2087     }
2088
2089   /* select something cool */
2090   ret = gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
2091                                        event->x, event->y,
2092                                        &path,
2093                                        NULL, NULL, NULL);
2094
2095   if (!ret)
2096     return TRUE; /* clicked outside window? */
2097
2098   gtk_combo_box_set_active (combo_box, gtk_tree_path_get_indices (path)[0]);
2099   gtk_combo_box_popdown (combo_box);
2100
2101   gtk_tree_path_free (path);
2102
2103   return TRUE;
2104 }
2105
2106 static gboolean
2107 gtk_combo_box_list_key_press (GtkWidget   *widget,
2108                               GdkEventKey *event,
2109                               gpointer     data)
2110 {
2111   GtkComboBox *combo_box = GTK_COMBO_BOX (data);
2112
2113   if ((event->keyval == GDK_Return || event->keyval == GDK_KP_Enter ||
2114        event->keyval == GDK_space || event->keyval == GDK_KP_Space) ||
2115       event->keyval == GDK_Escape)
2116     {
2117       if (event->keyval != GDK_Escape)
2118         {
2119           gboolean ret;
2120           GtkTreeIter iter;
2121           GtkTreeModel *model = NULL;
2122           GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (combo_box->priv->tree_view));
2123
2124           ret = gtk_tree_selection_get_selected (sel, &model, &iter);
2125           if (ret)
2126             {
2127               GtkTreePath *path;
2128
2129               path = gtk_tree_model_get_path (model, &iter);
2130               if (path)
2131                 {
2132                   gtk_combo_box_set_active (combo_box, gtk_tree_path_get_indices (path)[0]);
2133                   gtk_tree_path_free (path);
2134                 }
2135             }
2136         }
2137       else
2138         /* reset active item -- this is incredibly lame and ugly */
2139         gtk_combo_box_set_active (combo_box,
2140                                   gtk_combo_box_get_active (combo_box));
2141
2142       gtk_combo_box_popdown (combo_box);
2143
2144       return TRUE;
2145     }
2146
2147   return FALSE;
2148 }
2149
2150 static void
2151 gtk_combo_box_list_row_changed (GtkTreeModel *model,
2152                                 GtkTreePath  *path,
2153                                 GtkTreeIter  *iter,
2154                                 gpointer      data)
2155 {
2156   GtkComboBox *combo_box = GTK_COMBO_BOX (data);
2157   gint width;
2158
2159   width = gtk_combo_box_calc_requested_width (combo_box, path);
2160
2161   if (width > combo_box->priv->width)
2162     {
2163       if (combo_box->priv->cell_view) 
2164         {
2165           gtk_widget_set_size_request (combo_box->priv->cell_view, width, -1);
2166           gtk_widget_queue_resize (combo_box->priv->cell_view);
2167         }
2168       combo_box->priv->width = width;
2169     }
2170 }
2171
2172 /*
2173  * GtkCellLayout implementation
2174  */
2175 static void
2176 gtk_combo_box_cell_layout_pack_start (GtkCellLayout   *layout,
2177                                       GtkCellRenderer *cell,
2178                                       gboolean         expand)
2179 {
2180   ComboCellInfo *info;
2181   GtkComboBox *combo_box;
2182   GtkWidget *menu;
2183
2184   g_return_if_fail (GTK_IS_COMBO_BOX (layout));
2185   g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
2186
2187   combo_box = GTK_COMBO_BOX (layout);
2188
2189   g_object_ref (G_OBJECT (cell));
2190   gtk_object_sink (GTK_OBJECT (cell));
2191
2192   info = g_new0 (ComboCellInfo, 1);
2193   info->cell = cell;
2194   info->expand = expand;
2195   info->pack = GTK_PACK_START;
2196
2197   combo_box->priv->cells = g_slist_append (combo_box->priv->cells, info);
2198
2199   if (combo_box->priv->cell_view)
2200     gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box->priv->cell_view),
2201                                 cell, expand);
2202
2203   if (combo_box->priv->column)
2204     gtk_tree_view_column_pack_start (combo_box->priv->column, cell, expand);
2205
2206   menu = combo_box->priv->popup_widget;
2207   if (GTK_IS_MENU (menu))
2208     {
2209       GList *i, *list;
2210
2211       list = gtk_container_get_children (GTK_CONTAINER (menu));
2212       for (i = list; i; i = i->next)
2213         {
2214           GtkCellView *view;
2215
2216           if (GTK_IS_CELL_VIEW_MENU_ITEM (i->data))
2217             view = GTK_CELL_VIEW (GTK_BIN (i->data)->child);
2218           else
2219             view = GTK_CELL_VIEW (i->data);
2220
2221           gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (view), cell, expand);
2222         }
2223       g_list_free (list);
2224     }
2225 }
2226
2227 static void
2228 gtk_combo_box_cell_layout_pack_end (GtkCellLayout   *layout,
2229                                     GtkCellRenderer *cell,
2230                                     gboolean         expand)
2231 {
2232   ComboCellInfo *info;
2233   GtkComboBox *combo_box;
2234   GtkWidget *menu;
2235
2236   g_return_if_fail (GTK_IS_COMBO_BOX (layout));
2237   g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
2238
2239   combo_box = GTK_COMBO_BOX (layout);
2240
2241   g_object_ref (G_OBJECT (cell));
2242   gtk_object_sink (GTK_OBJECT (cell));
2243
2244   info = g_new0 (ComboCellInfo, 1);
2245   info->cell = cell;
2246   info->expand = expand;
2247   info->pack = GTK_PACK_END;
2248
2249   if (combo_box->priv->cell_view)
2250     gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (combo_box->priv->cell_view),
2251                               cell, expand);
2252
2253   if (combo_box->priv->column)
2254     gtk_tree_view_column_pack_end (combo_box->priv->column, cell, expand);
2255
2256   menu = combo_box->priv->popup_widget;
2257   if (GTK_IS_MENU (menu))
2258     {
2259       GList *i, *list;
2260
2261       list = gtk_container_get_children (GTK_CONTAINER (menu));
2262       for (i = list; i; i = i->next)
2263         {
2264           GtkCellView *view;
2265
2266           if (GTK_IS_CELL_VIEW_MENU_ITEM (i->data))
2267             view = GTK_CELL_VIEW (GTK_BIN (i->data)->child);
2268           else
2269             view = GTK_CELL_VIEW (i->data);
2270
2271           gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (view), cell, expand);
2272         }
2273       g_list_free (list);
2274     }
2275 }
2276
2277 static void
2278 gtk_combo_box_cell_layout_clear (GtkCellLayout *layout)
2279 {
2280   GtkWidget *menu;
2281   GtkComboBox *combo_box;
2282   GSList *i;
2283   
2284   g_return_if_fail (GTK_IS_COMBO_BOX (layout));
2285
2286   combo_box = GTK_COMBO_BOX (layout);
2287  
2288   if (combo_box->priv->cell_view)
2289     gtk_cell_layout_clear (GTK_CELL_LAYOUT (combo_box->priv->cell_view));
2290
2291   if (combo_box->priv->column)
2292     gtk_tree_view_column_clear (combo_box->priv->column);
2293
2294   for (i = combo_box->priv->cells; i; i = i->next)
2295     {
2296      ComboCellInfo *info = (ComboCellInfo *)i->data;
2297
2298       gtk_combo_box_cell_layout_clear_attributes (layout, info->cell);
2299       g_object_unref (G_OBJECT (info->cell));
2300       g_free (info);
2301       i->data = NULL;
2302     }
2303   g_slist_free (combo_box->priv->cells);
2304   combo_box->priv->cells = NULL;
2305
2306   menu = combo_box->priv->popup_widget;
2307   if (GTK_IS_MENU (menu))
2308     {
2309       GList *i, *list;
2310
2311       list = gtk_container_get_children (GTK_CONTAINER (menu));
2312       for (i = list; i; i = i->next)
2313         {
2314           GtkCellView *view;
2315
2316           if (GTK_IS_CELL_VIEW_MENU_ITEM (i->data))
2317             view = GTK_CELL_VIEW (GTK_BIN (i->data)->child);
2318           else
2319             view = GTK_CELL_VIEW (i->data);
2320
2321           gtk_cell_layout_clear (GTK_CELL_LAYOUT (view));
2322         }
2323       g_list_free (list);
2324     }
2325 }
2326
2327 static void
2328 gtk_combo_box_cell_layout_add_attribute (GtkCellLayout   *layout,
2329                                          GtkCellRenderer *cell,
2330                                          const gchar     *attribute,
2331                                          gint             column)
2332 {
2333   ComboCellInfo *info;
2334   GtkComboBox *combo_box;
2335   GtkWidget *menu;
2336
2337   g_return_if_fail (GTK_IS_COMBO_BOX (layout));
2338   g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
2339
2340   combo_box = GTK_COMBO_BOX (layout);
2341
2342   info = gtk_combo_box_get_cell_info (combo_box, cell);
2343
2344   info->attributes = g_slist_prepend (info->attributes,
2345                                       GINT_TO_POINTER (column));
2346   info->attributes = g_slist_prepend (info->attributes,
2347                                       g_strdup (attribute));
2348
2349   if (combo_box->priv->cell_view)
2350     gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box->priv->cell_view),
2351                                    cell, attribute, column);
2352
2353   if (combo_box->priv->column)
2354     gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box->priv->column),
2355                                    cell, attribute, column);
2356
2357   menu = combo_box->priv->popup_widget;
2358   if (GTK_IS_MENU (menu))
2359     {
2360       GList *i, *list;
2361
2362       list = gtk_container_get_children (GTK_CONTAINER (menu));
2363       for (i = list; i; i = i->next)
2364         {
2365           GtkCellView *view;
2366
2367           if (GTK_IS_CELL_VIEW_MENU_ITEM (i->data))
2368             view = GTK_CELL_VIEW (GTK_BIN (i->data)->child);
2369           else
2370             view = GTK_CELL_VIEW (i->data);
2371
2372           gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (view), cell,
2373                                          attribute, column);
2374         }
2375       g_list_free (list);
2376     }
2377
2378   gtk_widget_queue_resize (GTK_WIDGET (combo_box));
2379 }
2380
2381 static void
2382 gtk_combo_box_cell_layout_set_cell_data_func (GtkCellLayout         *layout,
2383                                               GtkCellRenderer       *cell,
2384                                               GtkCellLayoutDataFunc  func,
2385                                               gpointer               func_data,
2386                                               GDestroyNotify         destroy)
2387 {
2388   ComboCellInfo *info;
2389   GtkComboBox *combo_box;
2390   GtkWidget *menu;
2391
2392   g_return_if_fail (GTK_IS_COMBO_BOX (layout));
2393
2394   combo_box = GTK_COMBO_BOX (layout);
2395
2396   info = gtk_combo_box_get_cell_info (combo_box, cell);
2397   g_return_if_fail (info != NULL);
2398
2399   if (info->destroy)
2400     {
2401       GDestroyNotify d = info->destroy;
2402
2403       info->destroy = NULL;
2404       d (info->func_data);
2405     }
2406
2407   info->func = func;
2408   info->func_data = func_data;
2409   info->destroy = destroy;
2410
2411   if (combo_box->priv->cell_view)
2412     gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo_box->priv->cell_view), cell, func, func_data, NULL);
2413
2414   if (combo_box->priv->column)
2415     gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo_box->priv->column), cell, func, func_data, NULL);
2416
2417   menu = combo_box->priv->popup_widget;
2418   if (GTK_IS_MENU (menu))
2419     {
2420       GList *i, *list;
2421
2422       list = gtk_container_get_children (GTK_CONTAINER (menu));
2423       for (i = list; i; i = i->next)
2424         {
2425           GtkCellView *view;
2426
2427           if (GTK_IS_CELL_VIEW_MENU_ITEM (i->data))
2428             view = GTK_CELL_VIEW (GTK_BIN (i->data)->child);
2429           else
2430             view = GTK_CELL_VIEW (i->data);
2431
2432           gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (view), cell,
2433                                               func, func_data, NULL);
2434         }
2435       g_list_free (list);
2436     }
2437
2438   gtk_widget_queue_resize (GTK_WIDGET (combo_box));
2439 }
2440
2441 static void
2442 gtk_combo_box_cell_layout_clear_attributes (GtkCellLayout   *layout,
2443                                             GtkCellRenderer *cell)
2444 {
2445   ComboCellInfo *info;
2446   GtkComboBox *combo_box;
2447   GtkWidget *menu;
2448   GSList *list;
2449
2450   g_return_if_fail (GTK_IS_COMBO_BOX (layout));
2451   g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
2452
2453   combo_box = GTK_COMBO_BOX (layout);
2454
2455   info = gtk_combo_box_get_cell_info (combo_box, cell);
2456   g_return_if_fail (info != NULL);
2457
2458   list = info->attributes;
2459   while (list && list->next)
2460     {
2461       g_free (list->data);
2462       list = list->next->next;
2463     }
2464   g_slist_free (info->attributes);
2465   info->attributes = NULL;
2466
2467   if (combo_box->priv->cell_view)
2468     gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (combo_box->priv->cell_view), cell);
2469
2470   if (combo_box->priv->column)
2471     gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (combo_box->priv->column), cell);
2472
2473   menu = combo_box->priv->popup_widget;
2474   if (GTK_IS_MENU (menu))
2475     {
2476       GList *i, *list;
2477
2478       list = gtk_container_get_children (GTK_CONTAINER (menu));
2479       for (i = list; i; i = i->next)
2480         {
2481           GtkCellView *view;
2482
2483           if (GTK_IS_CELL_VIEW_MENU_ITEM (i->data))
2484             view = GTK_CELL_VIEW (GTK_BIN (i->data)->child);
2485           else
2486             view = GTK_CELL_VIEW (i->data);
2487
2488           gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (view), cell);
2489         }
2490       g_list_free (list);
2491     }
2492
2493   gtk_widget_queue_resize (GTK_WIDGET (combo_box));
2494 }
2495
2496 static void
2497 gtk_combo_box_cell_layout_reorder (GtkCellLayout   *layout,
2498                                    GtkCellRenderer *cell,
2499                                    gint             position)
2500 {
2501   ComboCellInfo *info;
2502   GtkComboBox *combo_box;
2503   GtkWidget *menu;
2504   GSList *link;
2505
2506   g_return_if_fail (GTK_IS_COMBO_BOX (layout));
2507   g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
2508
2509   combo_box = GTK_COMBO_BOX (layout);
2510
2511   info = gtk_combo_box_get_cell_info (combo_box, cell);
2512
2513   g_return_if_fail (info != NULL);
2514   g_return_if_fail (position >= 0);
2515
2516   link = g_slist_find (combo_box->priv->cells, info);
2517
2518   g_return_if_fail (link != NULL);
2519
2520   combo_box->priv->cells = g_slist_remove_link (combo_box->priv->cells, link);
2521   combo_box->priv->cells = g_slist_insert (combo_box->priv->cells, info,
2522                                            position);
2523
2524   if (combo_box->priv->cell_view)
2525     gtk_cell_layout_reorder (GTK_CELL_LAYOUT (combo_box->priv->cell_view),
2526                              cell, position);
2527
2528   if (combo_box->priv->column)
2529     gtk_cell_layout_reorder (GTK_CELL_LAYOUT (combo_box->priv->column),
2530                              cell, position);
2531
2532   menu = combo_box->priv->popup_widget;
2533   if (GTK_IS_MENU (menu))
2534     {
2535       GList *i, *list;
2536
2537       list = gtk_container_get_children (GTK_CONTAINER (menu));
2538       for (i = list; i; i = i->next)
2539         {
2540           GtkCellView *view;
2541
2542           if (GTK_IS_CELL_VIEW_MENU_ITEM (i->data))
2543             view = GTK_CELL_VIEW (GTK_BIN (i->data)->child);
2544           else
2545             view = GTK_CELL_VIEW (i->data);
2546
2547           gtk_cell_layout_reorder (GTK_CELL_LAYOUT (view), cell, position);
2548         }
2549       g_list_free (list);
2550     }
2551
2552   gtk_widget_queue_draw (GTK_WIDGET (combo_box));
2553 }
2554
2555 /*
2556  * public API
2557  */
2558
2559 /**
2560  * gtk_combo_box_new:
2561  *
2562  * Creates a new empty #GtkComboBox.
2563  *
2564  * Return value: A new #GtkComboBox.
2565  *
2566  * Since: 2.4
2567  */
2568 GtkWidget *
2569 gtk_combo_box_new (void)
2570 {
2571   return GTK_WIDGET (g_object_new (GTK_TYPE_COMBO_BOX, NULL));
2572 }
2573
2574 /**
2575  * gtk_combo_box_new_with_model:
2576  * @model: A #GtkTreeModel.
2577  *
2578  * Creates a new #GtkComboBox with the model initialized to @model.
2579  *
2580  * Return value: A new #GtkComboBox.
2581  *
2582  * Since: 2.4
2583  */
2584 GtkWidget *
2585 gtk_combo_box_new_with_model (GtkTreeModel *model)
2586 {
2587   GtkComboBox *combo_box;
2588
2589   g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL);
2590
2591   combo_box = GTK_COMBO_BOX (g_object_new (GTK_TYPE_COMBO_BOX,
2592                                            "model", model,
2593                                            NULL));
2594
2595   return GTK_WIDGET (combo_box);
2596 }
2597
2598 /**
2599  * gtk_combo_box_set_wrap_width:
2600  * @combo_box: A #GtkComboBox.
2601  * @width: Preferred number of columns.
2602  *
2603  * Sets the wrap width of @combo_box to be @width. The wrap width is basically
2604  * the preferred number of columns when you want to the popup to be layed out
2605  * in a table.
2606  *
2607  * Since: 2.4
2608  */
2609 void
2610 gtk_combo_box_set_wrap_width (GtkComboBox *combo_box,
2611                               gint         width)
2612 {
2613   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2614   g_return_if_fail (width >= 0);
2615
2616   if (width != combo_box->priv->wrap_width)
2617     {
2618       combo_box->priv->wrap_width = width;
2619       
2620       gtk_combo_box_relayout (combo_box);
2621       gtk_combo_box_style_set (GTK_WIDGET (combo_box), NULL);
2622       
2623       g_object_notify (G_OBJECT (combo_box), "wrap_width");
2624     }
2625 }
2626
2627 /**
2628  * gtk_combo_box_set_row_span_column:
2629  * @combo_box: A #GtkComboBox.
2630  * @row_span: A column in the model passed during construction.
2631  *
2632  * Sets the column with row span information for @combo_box to be @row_span.
2633  * The row span column contains integers which indicate how many rows
2634  * an item should span.
2635  *
2636  * Since: 2.4
2637  */
2638 void
2639 gtk_combo_box_set_row_span_column (GtkComboBox *combo_box,
2640                                    gint         row_span)
2641 {
2642   gint col;
2643
2644   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2645
2646   col = gtk_tree_model_get_n_columns (combo_box->priv->model);
2647   g_return_if_fail (row_span >= 0 && row_span < col);
2648
2649   if (row_span != combo_box->priv->row_column)
2650     {
2651       combo_box->priv->row_column = row_span;
2652       
2653       gtk_combo_box_relayout (combo_box);
2654  
2655       g_object_notify (G_OBJECT (combo_box), "row_span_column");
2656     }
2657 }
2658
2659 /**
2660  * gtk_combo_box_set_column_span_column:
2661  * @combo_box: A #GtkComboBox.
2662  * @column_span: A column in the model passed during construction.
2663  *
2664  * Sets the column with column span information for @combo_box to be
2665  * @column_span. The column span column contains integers which indicate
2666  * how many columns an item should span.
2667  *
2668  * Since: 2.4
2669  */
2670 void
2671 gtk_combo_box_set_column_span_column (GtkComboBox *combo_box,
2672                                       gint         column_span)
2673 {
2674   gint col;
2675
2676   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2677
2678   col = gtk_tree_model_get_n_columns (combo_box->priv->model);
2679   g_return_if_fail (column_span >= 0 && column_span < col);
2680
2681   if (column_span != combo_box->priv->col_column)
2682     {
2683       combo_box->priv->col_column = column_span;
2684       
2685       gtk_combo_box_relayout (combo_box);
2686
2687       g_object_notify (G_OBJECT (combo_box), "column_span_column");
2688     }
2689 }
2690
2691 /**
2692  * gtk_combo_box_get_active:
2693  * @combo_box: A #GtkComboBox.
2694  *
2695  * Returns the index of the currently active item, or -1 if there's no
2696  * active item.
2697  *
2698  * Return value: An integer which is the index of the currently active item, or
2699  * -1 if there's no active item.
2700  *
2701  * Since: 2.4
2702  */
2703 gint
2704 gtk_combo_box_get_active (GtkComboBox *combo_box)
2705 {
2706   g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), 0);
2707
2708   return combo_box->priv->active_item;
2709 }
2710
2711 /**
2712  * gtk_combo_box_set_active:
2713  * @combo_box: A #GtkComboBox.
2714  * @index: An index in the model passed during construction, or -1 to have
2715  * no active item.
2716  *
2717  * Sets the active item of @combo_box to be the item at @index.
2718  *
2719  * Since: 2.4
2720  */
2721 void
2722 gtk_combo_box_set_active (GtkComboBox *combo_box,
2723                           gint         index)
2724 {
2725   GtkTreePath *path;
2726
2727   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2728   /* -1 means "no item selected" */
2729   g_return_if_fail (index >= -1);
2730
2731   if (combo_box->priv->active_item == index)
2732     return;
2733
2734   combo_box->priv->active_item = index;
2735
2736   if (index == -1)
2737     {
2738       if (combo_box->priv->tree_view)
2739         gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (combo_box->priv->tree_view)));
2740       else
2741         {
2742           GtkMenu *menu = GTK_MENU (combo_box->priv->popup_widget);
2743
2744           if (GTK_IS_MENU (menu))
2745             gtk_menu_set_active (menu, -1);
2746         }
2747
2748       if (combo_box->priv->cell_view)
2749         gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (combo_box->priv->cell_view), NULL);
2750     }
2751   else
2752     {
2753       path = gtk_tree_path_new_from_indices (index, -1);
2754
2755       if (combo_box->priv->tree_view)
2756         gtk_tree_view_set_cursor (GTK_TREE_VIEW (combo_box->priv->tree_view), path, NULL, FALSE);
2757       else
2758         {
2759           GtkMenu *menu = GTK_MENU (combo_box->priv->popup_widget);
2760
2761           if (GTK_IS_MENU (menu))
2762             gtk_menu_set_active (GTK_MENU (menu), index);
2763         }
2764
2765       if (combo_box->priv->cell_view)
2766         gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (combo_box->priv->cell_view), path);
2767
2768       gtk_tree_path_free (path);
2769     }
2770
2771   g_signal_emit_by_name (combo_box, "changed", NULL, NULL);
2772 }
2773
2774
2775 /**
2776  * gtk_combo_box_get_active_iter:
2777  * @combo_box: A #GtkComboBox
2778  * @iter: The uninitialized #GtkTreeIter.
2779  * 
2780  * Sets @iter to point to the current active item, if it exists.
2781  * 
2782  * Return value: %TRUE, if @iter was set
2783  *
2784  * Since: 2.4
2785  **/
2786 gboolean
2787 gtk_combo_box_get_active_iter (GtkComboBox     *combo_box,
2788                                GtkTreeIter     *iter)
2789 {
2790   GtkTreePath *path;
2791   gint active;
2792   gboolean retval;
2793
2794   g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
2795
2796   active = gtk_combo_box_get_active (combo_box);
2797   if (active < 0)
2798     return FALSE;
2799
2800   path = gtk_tree_path_new_from_indices (active, -1);
2801   retval = gtk_tree_model_get_iter (gtk_combo_box_get_model (combo_box),
2802                                     iter, path);
2803   gtk_tree_path_free (path);
2804
2805   return retval;
2806 }
2807
2808 /**
2809  * gtk_combo_box_set_active_iter:
2810  * @combo_box: A #GtkComboBox
2811  * @iter: The #GtkTreeIter.
2812  * 
2813  * Sets the current active item to be the one referenced by @iter. 
2814  * @iter must correspond to a path of depth one.
2815  * 
2816  * Since: 2.4
2817  **/
2818 void
2819 gtk_combo_box_set_active_iter (GtkComboBox     *combo_box,
2820                                GtkTreeIter     *iter)
2821 {
2822   GtkTreePath *path;
2823
2824   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2825
2826   path = gtk_tree_model_get_path (gtk_combo_box_get_model (combo_box), iter);
2827   g_return_if_fail (path != NULL);
2828   g_return_if_fail (gtk_tree_path_get_depth (path) == 1);
2829   
2830   gtk_combo_box_set_active (combo_box, gtk_tree_path_get_indices (path)[0]);
2831   gtk_tree_path_free (path);
2832 }
2833
2834 /**
2835  * gtk_combo_box_set_model:
2836  * @combo_box: A #GtkComboBox.
2837  * @model: A #GtkTreeModel.
2838  *
2839  * Sets the model used by @combo_box to be @model. Will unset a
2840  * previously set model (if applicable).
2841  *
2842  * Since: 2.4
2843  */
2844 void
2845 gtk_combo_box_set_model (GtkComboBox  *combo_box,
2846                          GtkTreeModel *model)
2847 {
2848   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2849   g_return_if_fail (GTK_IS_TREE_MODEL (model));
2850
2851   if (combo_box->priv->model)
2852     {
2853       gtk_combo_box_unset_model (combo_box);
2854       g_object_unref (G_OBJECT (combo_box->priv->model));
2855     }
2856
2857   combo_box->priv->model = model;
2858   g_object_ref (G_OBJECT (combo_box->priv->model));
2859
2860   gtk_combo_box_set_model_internal (combo_box);
2861   if (!combo_box->priv->tree_view && combo_box->priv->popup_widget)
2862     gtk_combo_box_menu_fill (combo_box);
2863
2864   if (combo_box->priv->cell_view)
2865     gtk_cell_view_set_model (GTK_CELL_VIEW (combo_box->priv->cell_view),
2866                              combo_box->priv->model);
2867 }
2868
2869 /**
2870  * gtk_combo_box_get_model
2871  * @combo_box: A #GtkComboBox.
2872  *
2873  * Returns the #GtkTreeModel which is acting as data source for @combo_box.
2874  *
2875  * Return value: A #GtkTreeModel which was passed during construction.
2876  *
2877  * Since: 2.4
2878  */
2879 GtkTreeModel *
2880 gtk_combo_box_get_model (GtkComboBox *combo_box)
2881 {
2882   g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
2883
2884   return combo_box->priv->model;
2885 }
2886
2887
2888 /* convenience API for simple text combos */
2889
2890 /**
2891  * gtk_combo_box_new_text:
2892  *
2893  * Convenience function which constructs a new text combo box, which is a
2894  * #GtkComboBox just displaying strings. If you use this function to create
2895  * a text combo box, you should only manipulate its data source with the
2896  * following convenience functions: gtk_combo_box_append_text(),
2897  * gtk_combo_box_insert_text(), gtk_combo_box_prepend_text() and
2898  * gtk_combo_box_remove_text().
2899  *
2900  * Return value: A new text combo box.
2901  *
2902  * Since: 2.4
2903  */
2904 GtkWidget *
2905 gtk_combo_box_new_text (void)
2906 {
2907   GtkWidget *combo_box;
2908   GtkCellRenderer *cell;
2909   GtkListStore *store;
2910
2911   store = gtk_list_store_new (1, G_TYPE_STRING);
2912
2913   combo_box = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store));
2914
2915   cell = gtk_cell_renderer_text_new ();
2916   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), cell, TRUE);
2917   gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), cell,
2918                                   "text", 0,
2919                                   NULL);
2920
2921   return combo_box;
2922 }
2923
2924 /**
2925  * gtk_combo_box_append_text:
2926  * @combo_box: A #GtkComboBox constructed using gtk_combo_box_new_text().
2927  * @text: A string.
2928  *
2929  * Appends @string to the list of strings stored in @combo_box. Note that
2930  * you can only use this function with combo boxes constructed with
2931  * gtk_combo_box_new_text().
2932  *
2933  * Since: 2.4
2934  */
2935 void
2936 gtk_combo_box_append_text (GtkComboBox *combo_box,
2937                            const gchar *text)
2938 {
2939   GtkTreeIter iter;
2940   GtkListStore *store;
2941
2942   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2943   g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model));
2944   g_return_if_fail (text != NULL);
2945
2946   store = GTK_LIST_STORE (combo_box->priv->model);
2947
2948   gtk_list_store_append (store, &iter);
2949   gtk_list_store_set (store, &iter, 0, text, -1);
2950 }
2951
2952 /**
2953  * gtk_combo_box_insert_text:
2954  * @combo_box: A #GtkComboBox constructed using gtk_combo_box_new_text().
2955  * @position: An index to insert @text.
2956  * @text: A string.
2957  *
2958  * Inserts @string at @position in the list of strings stored in @combo_box.
2959  * Note that you can only use this function with combo boxes constructed
2960  * with gtk_combo_box_new_text().
2961  *
2962  * Since: 2.4
2963  */
2964 void
2965 gtk_combo_box_insert_text (GtkComboBox *combo_box,
2966                            gint         position,
2967                            const gchar *text)
2968 {
2969   GtkTreeIter iter;
2970   GtkListStore *store;
2971
2972   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2973   g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model));
2974   g_return_if_fail (position >= 0);
2975   g_return_if_fail (text != NULL);
2976
2977   store = GTK_LIST_STORE (combo_box->priv->model);
2978
2979   gtk_list_store_insert (store, &iter, position);
2980   gtk_list_store_set (store, &iter, 0, text, -1);
2981 }
2982
2983 /**
2984  * gtk_combo_box_prepend_text:
2985  * @combo_box: A #GtkComboBox constructed with gtk_combo_box_new_text().
2986  * @text: A string.
2987  *
2988  * Prepends @string to the list of strings stored in @combo_box. Note that
2989  * you can only use this function with combo boxes constructed with
2990  * gtk_combo_box_new_text().
2991  *
2992  * Since: 2.4
2993  */
2994 void
2995 gtk_combo_box_prepend_text (GtkComboBox *combo_box,
2996                             const gchar *text)
2997 {
2998   GtkTreeIter iter;
2999   GtkListStore *store;
3000
3001   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
3002   g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model));
3003   g_return_if_fail (text != NULL);
3004
3005   store = GTK_LIST_STORE (combo_box->priv->model);
3006
3007   gtk_list_store_prepend (store, &iter);
3008   gtk_list_store_set (store, &iter, 0, text, -1);
3009 }
3010
3011 /**
3012  * gtk_combo_box_remove_text:
3013  * @combo_box: A #GtkComboBox constructed with gtk_combo_box_new_text().
3014  * @position: Index of the item to remove.
3015  *
3016  * Removes the string at @position from @combo_box. Note that you can only use
3017  * this function with combo boxes constructed with gtk_combo_box_new_text().
3018  *
3019  * Since: 2.4
3020  */
3021 void
3022 gtk_combo_box_remove_text (GtkComboBox *combo_box,
3023                            gint         position)
3024 {
3025   GtkTreeIter iter;
3026   GtkListStore *store;
3027
3028   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
3029   g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model));
3030   g_return_if_fail (position >= 0);
3031
3032   store = GTK_LIST_STORE (combo_box->priv->model);
3033
3034   if (gtk_tree_model_iter_nth_child (combo_box->priv->model, &iter,
3035                                      NULL, position))
3036     gtk_list_store_remove (store, &iter);
3037 }
3038
3039
3040 static gboolean
3041 gtk_combo_box_mnemonic_activate (GtkWidget *widget,
3042                                  gboolean   group_cycling)
3043 {
3044   GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
3045
3046   gtk_widget_grab_focus (combo_box->priv->button);
3047
3048   return TRUE;
3049 }
3050
3051 static void
3052 gtk_combo_box_destroy (GtkObject *object)
3053 {
3054   GtkComboBox *combo_box = GTK_COMBO_BOX (object);
3055
3056   GTK_OBJECT_CLASS (parent_class)->destroy (object);
3057
3058   combo_box->priv->cell_view = NULL;
3059 }
3060
3061 static void
3062 gtk_combo_box_finalize (GObject *object)
3063 {
3064   GtkComboBox *combo_box = GTK_COMBO_BOX (object);
3065   GSList *i;
3066   
3067   gtk_combo_box_unset_model (combo_box);
3068
3069   if (GTK_IS_MENU (combo_box->priv->popup_widget))
3070     gtk_combo_box_menu_destroy (combo_box);
3071   
3072   if (GTK_IS_TREE_VIEW (combo_box->priv->tree_view))
3073     gtk_combo_box_list_destroy (combo_box);
3074
3075   if (combo_box->priv->popup_window)
3076     gtk_widget_destroy (combo_box->priv->popup_window);
3077
3078   if (combo_box->priv->model)
3079     g_object_unref (combo_box->priv->model);
3080
3081   for (i = combo_box->priv->cells; i; i = i->next)
3082     {
3083       ComboCellInfo *info = (ComboCellInfo *)i->data;
3084       GSList *list = info->attributes;
3085
3086       if (info->destroy)
3087         info->destroy (info->func_data);
3088
3089       while (list && list->next)
3090         {
3091           g_free (list->data);
3092           list = list->next->next;
3093         }
3094       g_slist_free (info->attributes);
3095
3096       g_object_unref (G_OBJECT (info->cell));
3097       g_free (info);
3098     }
3099    g_slist_free (combo_box->priv->cells);
3100
3101    G_OBJECT_CLASS (parent_class)->finalize (object);
3102 }