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