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