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