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