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