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