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