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