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