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