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