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