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