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