]> Pileus Git - ~andy/gtk/blob - gtk/gtkcombobox.c
Add show-popup and hide-popup signals and emit them when the popup is
[~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 "gtkeventbox.h"
29 #include "gtkframe.h"
30 #include "gtkhbox.h"
31 #include "gtkliststore.h"
32 #include "gtkmain.h"
33 #include "gtkmenu.h"
34 #include "gtkscrolledwindow.h"
35 #include "gtkseparatormenuitem.h"
36 #include "gtktearoffmenuitem.h"
37 #include "gtktogglebutton.h"
38 #include "gtktreeselection.h"
39 #include "gtkvseparator.h"
40 #include "gtkwindow.h"
41 #include "gtkprivate.h"
42
43 #include <gdk/gdkkeysyms.h>
44
45 #include <gobject/gvaluecollector.h>
46
47 #include <string.h>
48 #include <stdarg.h>
49
50 #include "gtkmarshalers.h"
51 #include "gtkintl.h"
52
53 #include "gtktreeprivate.h"
54 #include "gtkalias.h"
55
56 /* WELCOME, to THE house of evil code */
57
58 typedef struct _ComboCellInfo ComboCellInfo;
59 struct _ComboCellInfo
60 {
61   GtkCellRenderer *cell;
62   GSList *attributes;
63
64   GtkCellLayoutDataFunc func;
65   gpointer func_data;
66   GDestroyNotify destroy;
67
68   guint expand : 1;
69   guint pack : 1;
70 };
71
72 #define GTK_COMBO_BOX_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_COMBO_BOX, GtkComboBoxPrivate))
73
74 struct _GtkComboBoxPrivate
75 {
76   GtkTreeModel *model;
77
78   gint col_column;
79   gint row_column;
80
81   gint wrap_width;
82
83   GtkTreeRowReference *active_row;
84
85   GtkWidget *tree_view;
86   GtkTreeViewColumn *column;
87
88   GtkWidget *cell_view;
89   GtkWidget *cell_view_frame;
90
91   GtkWidget *button;
92   GtkWidget *box;
93   GtkWidget *arrow;
94   GtkWidget *separator;
95
96   GtkWidget *popup_widget;
97   GtkWidget *popup_window;
98   GtkWidget *popup_frame;
99   GtkWidget *scrolled_window;
100
101   guint inserted_id;
102   guint deleted_id;
103   guint reordered_id;
104   guint changed_id;
105   guint popup_idle_id;
106   guint scroll_timer;
107   guint resize_idle_id;
108
109   gint width;
110   GSList *cells;
111
112   guint popup_in_progress : 1;
113   guint destroying : 1;
114   guint add_tearoffs : 1;
115   guint has_frame : 1;
116   guint is_cell_renderer : 1;
117   guint editing_canceled : 1;
118   guint auto_scroll : 1;
119   guint focus_on_click : 1;
120
121   GtkTreeViewRowSeparatorFunc row_separator_func;
122   gpointer                    row_separator_data;
123   GtkDestroyNotify            row_separator_destroy;
124 };
125
126 /* While debugging this evil code, I have learned that
127  * there are actually 4 modes to this widget, which can
128  * be characterized as follows
129  * 
130  * 1) menu mode, no child added
131  *
132  * tree_view -> NULL
133  * cell_view -> GtkCellView, regular child
134  * cell_view_frame -> NULL
135  * button -> GtkToggleButton set_parent to combo
136  * arrow -> GtkArrow set_parent to button
137  * separator -> GtkVSepator set_parent to button
138  * popup_widget -> GtkMenu
139  * popup_window -> NULL
140  * popup_frame -> NULL
141  * scrolled_window -> NULL
142  *
143  * 2) menu mode, child added
144  * 
145  * tree_view -> NULL
146  * cell_view -> NULL 
147  * cell_view_frame -> NULL
148  * button -> GtkToggleButton set_parent to combo
149  * arrow -> GtkArrow, child of button
150  * separator -> NULL
151  * popup_widget -> GtkMenu
152  * popup_window -> NULL
153  * popup_frame -> NULL
154  * scrolled_window -> NULL
155  *
156  * 3) list mode, no child added
157  * 
158  * tree_view -> GtkTreeView, child of popup_frame
159  * cell_view -> GtkCellView, regular child
160  * cell_view_frame -> GtkFrame, set parent to combo
161  * button -> GtkToggleButton, set_parent to combo
162  * arrow -> GtkArrow, child of button
163  * separator -> NULL
164  * popup_widget -> tree_view
165  * popup_window -> GtkWindow
166  * popup_frame -> GtkFrame, child of popup_window
167  * scrolled_window -> GtkScrolledWindow, child of popup_frame
168  *
169  * 4) list mode, child added
170  *
171  * tree_view -> GtkTreeView, child of popup_frame
172  * cell_view -> NULL
173  * cell_view_frame -> NULL
174  * button -> GtkToggleButton, set_parent to combo
175  * arrow -> GtkArrow, child of button
176  * separator -> NULL
177  * popup_widget -> tree_view
178  * popup_window -> GtkWindow
179  * popup_frame -> GtkFrame, child of popup_window
180  * scrolled_window -> GtkScrolledWindow, child of popup_frame
181  * 
182  */
183
184 enum {
185   CHANGED,
186   POPUP_SHOW,
187   POPUP_HIDE,
188   LAST_SIGNAL
189 };
190
191 enum {
192   PROP_0,
193   PROP_MODEL,
194   PROP_WRAP_WIDTH,
195   PROP_ROW_SPAN_COLUMN,
196   PROP_COLUMN_SPAN_COLUMN,
197   PROP_ACTIVE,
198   PROP_ADD_TEAROFFS,
199   PROP_HAS_FRAME,
200   PROP_FOCUS_ON_CLICK
201 };
202
203 static GtkBinClass *parent_class = NULL;
204 static guint combo_box_signals[LAST_SIGNAL] = {0,};
205
206 #define BONUS_PADDING 4
207 #define SCROLL_TIME  100
208
209 /* common */
210
211 static void     gtk_combo_box_class_init           (GtkComboBoxClass *klass);
212 static void     gtk_combo_box_cell_layout_init     (GtkCellLayoutIface *iface);
213 static void     gtk_combo_box_cell_editable_init   (GtkCellEditableIface *iface);
214 static void     gtk_combo_box_init                 (GtkComboBox      *combo_box);
215 static void     gtk_combo_box_finalize             (GObject          *object);
216 static void     gtk_combo_box_destroy              (GtkObject        *object);
217
218 static void     gtk_combo_box_set_property         (GObject         *object,
219                                                     guint            prop_id,
220                                                     const GValue    *value,
221                                                     GParamSpec      *spec);
222 static void     gtk_combo_box_get_property         (GObject         *object,
223                                                     guint            prop_id,
224                                                     GValue          *value,
225                                                     GParamSpec      *spec);
226
227 static void     gtk_combo_box_state_changed        (GtkWidget        *widget,
228                                                     GtkStateType      previous);
229 static void     gtk_combo_box_grab_focus           (GtkWidget       *widget);
230 static void     gtk_combo_box_style_set            (GtkWidget       *widget,
231                                                     GtkStyle        *previous);
232 static void     gtk_combo_box_button_toggled       (GtkWidget       *widget,
233                                                     gpointer         data);
234 static void     gtk_combo_box_button_state_changed (GtkWidget       *widget,
235                                                     GtkStateType     previous,
236                                                     gpointer         data);
237 static void     gtk_combo_box_add                  (GtkContainer    *container,
238                                                     GtkWidget       *widget);
239 static void     gtk_combo_box_remove               (GtkContainer    *container,
240                                                     GtkWidget       *widget);
241
242 static ComboCellInfo *gtk_combo_box_get_cell_info  (GtkComboBox      *combo_box,
243                                                     GtkCellRenderer  *cell);
244
245 static void     gtk_combo_box_menu_show            (GtkWidget        *menu,
246                                                     gpointer          user_data);
247 static void     gtk_combo_box_menu_hide            (GtkWidget        *menu,
248                                                     gpointer          user_data);
249
250 static void     gtk_combo_box_set_popup_widget     (GtkComboBox      *combo_box,
251                                                     GtkWidget        *popup);
252 static void     gtk_combo_box_menu_position_below  (GtkMenu          *menu,
253                                                     gint             *x,
254                                                     gint             *y,
255                                                     gint             *push_in,
256                                                     gpointer          user_data);
257 static void     gtk_combo_box_menu_position_over   (GtkMenu          *menu,
258                                                     gint             *x,
259                                                     gint             *y,
260                                                     gint             *push_in,
261                                                     gpointer          user_data);
262 static void     gtk_combo_box_menu_position        (GtkMenu          *menu,
263                                                     gint             *x,
264                                                     gint             *y,
265                                                     gint             *push_in,
266                                                     gpointer          user_data);
267
268 static gint     gtk_combo_box_calc_requested_width (GtkComboBox      *combo_box,
269                                                     GtkTreePath      *path);
270 static void     gtk_combo_box_remeasure            (GtkComboBox      *combo_box);
271
272 static void     gtk_combo_box_unset_model          (GtkComboBox      *combo_box);
273
274 static void     gtk_combo_box_size_request         (GtkWidget        *widget,
275                                                     GtkRequisition   *requisition);
276 static void     gtk_combo_box_size_allocate        (GtkWidget        *widget,
277                                                     GtkAllocation    *allocation);
278 static void     gtk_combo_box_forall               (GtkContainer     *container,
279                                                     gboolean          include_internals,
280                                                     GtkCallback       callback,
281                                                     gpointer          callback_data);
282 static gboolean gtk_combo_box_expose_event         (GtkWidget        *widget,
283                                                     GdkEventExpose   *event);
284 static gboolean gtk_combo_box_scroll_event         (GtkWidget        *widget,
285                                                     GdkEventScroll   *event);
286 static void     gtk_combo_box_set_active_internal  (GtkComboBox      *combo_box,
287                                                     GtkTreePath      *path);
288 static gboolean gtk_combo_box_key_press            (GtkWidget        *widget,
289                                                     GdkEventKey      *event,
290                                                     gpointer          data);
291
292 static void     gtk_combo_box_check_appearance     (GtkComboBox      *combo_box);
293 static gchar *  gtk_combo_box_real_get_active_text (GtkComboBox      *combo_box);
294
295 /* listening to the model */
296 static void     gtk_combo_box_model_row_inserted   (GtkTreeModel     *model,
297                                                     GtkTreePath      *path,
298                                                     GtkTreeIter      *iter,
299                                                     gpointer          user_data);
300 static void     gtk_combo_box_model_row_deleted    (GtkTreeModel     *model,
301                                                     GtkTreePath      *path,
302                                                     gpointer          user_data);
303 static void     gtk_combo_box_model_rows_reordered (GtkTreeModel     *model,
304                                                     GtkTreePath      *path,
305                                                     GtkTreeIter      *iter,
306                                                     gint             *new_order,
307                                                     gpointer          user_data);
308 static void     gtk_combo_box_model_row_changed    (GtkTreeModel     *model,
309                                                     GtkTreePath      *path,
310                                                     GtkTreeIter      *iter,
311                                                     gpointer          data);
312 static void     gtk_combo_box_model_row_expanded   (GtkTreeModel     *model,
313                                                     GtkTreePath      *path,
314                                                     GtkTreeIter      *iter,
315                                                     gpointer          data);
316
317 /* list */
318 static void     gtk_combo_box_list_position        (GtkComboBox      *combo_box, 
319                                                     gint             *x, 
320                                                     gint             *y, 
321                                                     gint             *width,
322                                                     gint             *height);
323 static void     gtk_combo_box_list_setup           (GtkComboBox      *combo_box);
324 static void     gtk_combo_box_list_destroy         (GtkComboBox      *combo_box);
325
326 static gboolean gtk_combo_box_list_button_released (GtkWidget        *widget,
327                                                     GdkEventButton   *event,
328                                                     gpointer          data);
329 static gboolean gtk_combo_box_list_key_press       (GtkWidget        *widget,
330                                                     GdkEventKey      *event,
331                                                     gpointer          data);
332 static gboolean gtk_combo_box_list_enter_notify    (GtkWidget        *widget,
333                                                     GdkEventCrossing *event,
334                                                     gpointer          data);
335 static void     gtk_combo_box_list_auto_scroll     (GtkComboBox   *combo,
336                                                     gint           x,
337                                                     gint           y);
338 static gboolean gtk_combo_box_list_scroll_timeout  (GtkComboBox   *combo);
339 static gboolean gtk_combo_box_list_button_pressed  (GtkWidget        *widget,
340                                                     GdkEventButton   *event,
341                                                     gpointer          data);
342
343 static void     gtk_combo_box_list_row_changed     (GtkTreeModel     *model,
344                                                     GtkTreePath      *path,
345                                                     GtkTreeIter      *iter,
346                                                     gpointer          data);
347 static void     gtk_combo_box_list_popup_resize    (GtkComboBox      *combo_box);
348
349 /* menu */
350 static void     gtk_combo_box_menu_setup           (GtkComboBox      *combo_box,
351                                                     gboolean          add_children);
352 static void     gtk_combo_box_menu_fill            (GtkComboBox      *combo_box);
353 static void     gtk_combo_box_menu_fill_level      (GtkComboBox      *combo_box,
354                                                     GtkWidget        *menu,
355                                                     GtkTreeIter      *iter);
356 static void     gtk_combo_box_menu_destroy         (GtkComboBox      *combo_box);
357
358 static void     gtk_combo_box_relayout_item        (GtkComboBox      *combo_box,
359                                                     GtkWidget        *item,
360                                                     GtkTreeIter      *iter,
361                                                     GtkWidget        *last);
362 static void     gtk_combo_box_relayout             (GtkComboBox      *combo_box);
363
364 static gboolean gtk_combo_box_menu_button_press    (GtkWidget        *widget,
365                                                     GdkEventButton   *event,
366                                                     gpointer          user_data);
367 static void     gtk_combo_box_menu_item_activate   (GtkWidget        *item,
368                                                     gpointer          user_data);
369 static void     gtk_combo_box_menu_row_inserted    (GtkTreeModel     *model,
370                                                     GtkTreePath      *path,
371                                                     GtkTreeIter      *iter,
372                                                     gpointer          user_data);
373 static void     gtk_combo_box_menu_row_deleted     (GtkTreeModel     *model,
374                                                     GtkTreePath      *path,
375                                                     gpointer          user_data);
376 static void     gtk_combo_box_menu_rows_reordered  (GtkTreeModel     *model,
377                                                     GtkTreePath      *path,
378                                                     GtkTreeIter      *iter,
379                                                     gint             *new_order,
380                                                     gpointer          user_data);
381 static void     gtk_combo_box_menu_row_changed     (GtkTreeModel     *model,
382                                                     GtkTreePath      *path,
383                                                     GtkTreeIter      *iter,
384                                                     gpointer          data);
385 static gboolean gtk_combo_box_menu_key_press       (GtkWidget        *widget,
386                                                     GdkEventKey      *event,
387                                                     gpointer          data);
388 static void     gtk_combo_box_menu_popup           (GtkComboBox      *combo_box,
389                                                     guint             button, 
390                                                     guint32           activate_time);
391 static GtkWidget *gtk_cell_view_menu_item_new      (GtkComboBox      *combo_box,
392                                                     GtkTreeModel     *model,
393                                                     GtkTreeIter      *iter);
394
395 /* cell layout */
396 static void     gtk_combo_box_cell_layout_pack_start         (GtkCellLayout         *layout,
397                                                               GtkCellRenderer       *cell,
398                                                               gboolean               expand);
399 static void     gtk_combo_box_cell_layout_pack_end           (GtkCellLayout         *layout,
400                                                               GtkCellRenderer       *cell,
401                                                               gboolean               expand);
402 static void     gtk_combo_box_cell_layout_clear              (GtkCellLayout         *layout);
403 static void     gtk_combo_box_cell_layout_add_attribute      (GtkCellLayout         *layout,
404                                                               GtkCellRenderer       *cell,
405                                                               const gchar           *attribute,
406                                                               gint                   column);
407 static void     gtk_combo_box_cell_layout_set_cell_data_func (GtkCellLayout         *layout,
408                                                               GtkCellRenderer       *cell,
409                                                               GtkCellLayoutDataFunc  func,
410                                                               gpointer               func_data,
411                                                               GDestroyNotify         destroy);
412 static void     gtk_combo_box_cell_layout_clear_attributes   (GtkCellLayout         *layout,
413                                                               GtkCellRenderer       *cell);
414 static void     gtk_combo_box_cell_layout_reorder            (GtkCellLayout         *layout,
415                                                               GtkCellRenderer       *cell,
416                                                               gint                   position);
417 static gboolean gtk_combo_box_mnemonic_activate              (GtkWidget    *widget,
418                                                               gboolean      group_cycling);
419
420 static void     gtk_combo_box_sync_cells                     (GtkComboBox   *combo_box,
421                                                               GtkCellLayout *cell_layout);
422 static void     combo_cell_data_func                         (GtkCellLayout   *cell_layout,
423                                                               GtkCellRenderer *cell,
424                                                               GtkTreeModel    *tree_model,
425                                                               GtkTreeIter     *iter,
426                                                               gpointer         data);
427 static void     gtk_combo_box_child_show                     (GtkWidget       *widget,
428                                                               gpointer         user_data);
429 static void     gtk_combo_box_child_hide                     (GtkWidget       *widget,
430                                                               gpointer         user_data);
431
432
433 /* GtkCellEditable method implementations */
434 static void gtk_combo_box_start_editing (GtkCellEditable *cell_editable,
435                                          GdkEvent        *event);
436
437
438 GType
439 gtk_combo_box_get_type (void)
440 {
441   static GType combo_box_type = 0;
442
443   if (!combo_box_type)
444     {
445       static const GTypeInfo combo_box_info =
446         {
447           sizeof (GtkComboBoxClass),
448           NULL, /* base_init */
449           NULL, /* base_finalize */
450           (GClassInitFunc) gtk_combo_box_class_init,
451           NULL, /* class_finalize */
452           NULL, /* class_data */
453           sizeof (GtkComboBox),
454           0,
455           (GInstanceInitFunc) gtk_combo_box_init
456         };
457
458       static const GInterfaceInfo cell_layout_info =
459         {
460           (GInterfaceInitFunc) gtk_combo_box_cell_layout_init,
461           NULL,
462           NULL
463         };
464
465       static const GInterfaceInfo cell_editable_info =
466         {
467           (GInterfaceInitFunc) gtk_combo_box_cell_editable_init,
468           NULL,
469           NULL
470        };
471
472       combo_box_type = g_type_register_static (GTK_TYPE_BIN,
473                                                I_("GtkComboBox"),
474                                                &combo_box_info,
475                                                0);
476
477       g_type_add_interface_static (combo_box_type,
478                                    GTK_TYPE_CELL_LAYOUT,
479                                    &cell_layout_info);
480
481
482       g_type_add_interface_static (combo_box_type,
483                                    GTK_TYPE_CELL_EDITABLE,
484                                    &cell_editable_info);
485       
486
487     }
488
489   return combo_box_type;
490 }
491
492 /* common */
493 static void
494 gtk_combo_box_class_init (GtkComboBoxClass *klass)
495 {
496   GObjectClass *object_class;
497   GtkObjectClass *gtk_object_class;
498   GtkContainerClass *container_class;
499   GtkWidgetClass *widget_class;
500
501   klass->get_active_text = gtk_combo_box_real_get_active_text;
502
503   container_class = (GtkContainerClass *)klass;
504   container_class->forall = gtk_combo_box_forall;
505   container_class->add = gtk_combo_box_add;
506   container_class->remove = gtk_combo_box_remove;
507
508   widget_class = (GtkWidgetClass *)klass;
509   widget_class->size_allocate = gtk_combo_box_size_allocate;
510   widget_class->size_request = gtk_combo_box_size_request;
511   widget_class->expose_event = gtk_combo_box_expose_event;
512   widget_class->scroll_event = gtk_combo_box_scroll_event;
513   widget_class->mnemonic_activate = gtk_combo_box_mnemonic_activate;
514   widget_class->grab_focus = gtk_combo_box_grab_focus;
515   widget_class->style_set = gtk_combo_box_style_set;
516   widget_class->state_changed = gtk_combo_box_state_changed;
517
518   gtk_object_class = (GtkObjectClass *)klass;
519   gtk_object_class->destroy = gtk_combo_box_destroy;
520
521   object_class = (GObjectClass *)klass;
522   object_class->finalize = gtk_combo_box_finalize;
523   object_class->set_property = gtk_combo_box_set_property;
524   object_class->get_property = gtk_combo_box_get_property;
525
526   parent_class = g_type_class_peek_parent (klass);
527
528   /* signals */
529   /**
530    * GtkComboBox::changed:
531    * @widget: the object which received the signal
532    * 
533    * The changed signal gets emitted when the active
534    * item is changed. The can be due to the user selecting
535    * a different item from the list, or due to a 
536    * call to gtk_combo_box_set_active_iter().
537    *
538    * Since: 2.4
539    */
540   combo_box_signals[CHANGED] =
541     g_signal_new (I_("changed"),
542                   G_OBJECT_CLASS_TYPE (klass),
543                   G_SIGNAL_RUN_LAST,
544                   G_STRUCT_OFFSET (GtkComboBoxClass, changed),
545                   NULL, NULL,
546                   g_cclosure_marshal_VOID__VOID,
547                   G_TYPE_NONE, 0);
548
549   combo_box_signals[POPUP_SHOW] =
550     g_signal_new ("popup-show",
551                   G_OBJECT_CLASS_TYPE (klass),
552                   G_SIGNAL_RUN_LAST,
553                   NULL,
554                   NULL, NULL,
555                   g_cclosure_marshal_VOID__VOID,
556                   G_TYPE_NONE, 0);
557
558   combo_box_signals[POPUP_HIDE] =
559     g_signal_new ("popup-hide",
560                   G_OBJECT_CLASS_TYPE (klass),
561                   G_SIGNAL_RUN_LAST,
562                   NULL,
563                   NULL, NULL,
564                   g_cclosure_marshal_VOID__VOID,
565                   G_TYPE_NONE, 0);
566
567
568   /* properties */
569   /**
570    * GtkComboBox:model:
571    *
572    * The model from which the combo box takes the values shown
573    * in the list. 
574    *
575    * Since: 2.4
576    */
577   g_object_class_install_property (object_class,
578                                    PROP_MODEL,
579                                    g_param_spec_object ("model",
580                                                         P_("ComboBox model"),
581                                                         P_("The model for the combo box"),
582                                                         GTK_TYPE_TREE_MODEL,
583                                                         GTK_PARAM_READWRITE));
584
585   /**
586    * GtkComboBox:wrap-width:
587    *
588    * If wrap-width is set to a positive value, the list will be
589    * displayed in multiple columns, the number of columns is
590    * determined by wrap-width.
591    *
592    * Since: 2.4
593    */
594   g_object_class_install_property (object_class,
595                                    PROP_WRAP_WIDTH,
596                                    g_param_spec_int ("wrap-width",
597                                                      P_("Wrap width"),
598                                                      P_("Wrap width for laying out the items in a grid"),
599                                                      0,
600                                                      G_MAXINT,
601                                                      0,
602                                                      GTK_PARAM_READWRITE));
603
604
605   /**
606    * GtkComboBox:row-span-column:
607    *
608    * If this is set to a non-negative value, it must be the index of a column 
609    * of type %G_TYPE_INT in the model. 
610    *
611    * The values of that column are used to determine how many rows a value 
612    * in the list will span. Therefore, the values in the model column pointed 
613    * to by this property must be greater than zero and not larger than wrap-width.
614    *
615    * Since: 2.4
616    */
617   g_object_class_install_property (object_class,
618                                    PROP_ROW_SPAN_COLUMN,
619                                    g_param_spec_int ("row-span-column",
620                                                      P_("Row span column"),
621                                                      P_("TreeModel column containing the row span values"),
622                                                      -1,
623                                                      G_MAXINT,
624                                                      -1,
625                                                      GTK_PARAM_READWRITE));
626
627
628   /**
629    * GtkComboBox:column-span-column:
630    *
631    * If this is set to a non-negative value, it must be the index of a column 
632    * of type %G_TYPE_INT in the model. 
633    *
634    * The values of that column are used to determine how many columns a value 
635    * in the list will span. 
636    *
637    * Since: 2.4
638    */
639   g_object_class_install_property (object_class,
640                                    PROP_COLUMN_SPAN_COLUMN,
641                                    g_param_spec_int ("column-span-column",
642                                                      P_("Column span column"),
643                                                      P_("TreeModel column containing the column span values"),
644                                                      -1,
645                                                      G_MAXINT,
646                                                      -1,
647                                                      GTK_PARAM_READWRITE));
648
649
650   /**
651    * GtkComboBox:active:
652    *
653    * The item which is currently active. If the model is a non-flat treemodel,
654    * and the active item is not an immediate child of the root of the tree,
655    * this property has the value <literal>gtk_tree_path_get_indices (path)[0]</literal>,
656    * where <literal>path</literal> is the #GtkTreePath of the active item.
657    *
658    * Since: 2.4
659    */
660   g_object_class_install_property (object_class,
661                                    PROP_ACTIVE,
662                                    g_param_spec_int ("active",
663                                                      P_("Active item"),
664                                                      P_("The item which is currently active"),
665                                                      -1,
666                                                      G_MAXINT,
667                                                      -1,
668                                                      GTK_PARAM_READWRITE));
669
670   /**
671    * GtkComboBox:add-tearoffs:
672    *
673    * The add-tearoffs property controls whether generated menus 
674    * have tearoff menu items. 
675    *
676    * Note that this only affects menu style combo boxes.
677    *
678    * Since: 2.6
679    */
680   g_object_class_install_property (object_class,
681                                    PROP_ADD_TEAROFFS,
682                                    g_param_spec_boolean ("add-tearoffs",
683                                                          P_("Add tearoffs to menus"),
684                                                          P_("Whether dropdowns should have a tearoff menu item"),
685                                                          FALSE,
686                                                          GTK_PARAM_READWRITE));
687   
688   /**
689    * GtkComboBox:has-frame:
690    *
691    * The has-frame property controls whether a frame
692    * is drawn around the entry.
693    *
694    * Since: 2.6
695    */
696   g_object_class_install_property (object_class,
697                                    PROP_HAS_FRAME,
698                                    g_param_spec_boolean ("has-frame",
699                                                          P_("Has Frame"),
700                                                          P_("Whether the combo box draws a frame around the child"),
701                                                          TRUE,
702                                                          GTK_PARAM_READWRITE));
703   
704   g_object_class_install_property (object_class,
705                                    PROP_FOCUS_ON_CLICK,
706                                    g_param_spec_boolean ("focus-on-click",
707                                                          P_("Focus on click"),
708                                                          P_("Whether the combo box grabs focus when it is clicked with the mouse"),
709                                                          TRUE,
710                                                          GTK_PARAM_READWRITE));
711
712   gtk_widget_class_install_style_property (widget_class,
713                                            g_param_spec_boolean ("appears-as-list",
714                                                                  P_("Appears as list"),
715                                                                  P_("Whether dropdowns should look like lists rather than menus"),
716                                                                  FALSE,
717                                                                  GTK_PARAM_READABLE));
718
719   g_type_class_add_private (object_class, sizeof (GtkComboBoxPrivate));
720 }
721
722 static void
723 gtk_combo_box_cell_layout_init (GtkCellLayoutIface *iface)
724 {
725   iface->pack_start = gtk_combo_box_cell_layout_pack_start;
726   iface->pack_end = gtk_combo_box_cell_layout_pack_end;
727   iface->clear = gtk_combo_box_cell_layout_clear;
728   iface->add_attribute = gtk_combo_box_cell_layout_add_attribute;
729   iface->set_cell_data_func = gtk_combo_box_cell_layout_set_cell_data_func;
730   iface->clear_attributes = gtk_combo_box_cell_layout_clear_attributes;
731   iface->reorder = gtk_combo_box_cell_layout_reorder;
732 }
733
734 static void
735 gtk_combo_box_cell_editable_init (GtkCellEditableIface *iface)
736 {
737   iface->start_editing = gtk_combo_box_start_editing;
738 }
739
740 static void
741 gtk_combo_box_init (GtkComboBox *combo_box)
742 {
743   combo_box->priv = GTK_COMBO_BOX_GET_PRIVATE (combo_box);
744
745   combo_box->priv->cell_view = gtk_cell_view_new ();
746   gtk_widget_set_parent (combo_box->priv->cell_view, GTK_WIDGET (combo_box));
747   GTK_BIN (combo_box)->child = combo_box->priv->cell_view;
748   gtk_widget_show (combo_box->priv->cell_view);
749
750   combo_box->priv->width = 0;
751   combo_box->priv->wrap_width = 0;
752
753   combo_box->priv->active_row = NULL;
754   combo_box->priv->col_column = -1;
755   combo_box->priv->row_column = -1;
756
757   combo_box->priv->add_tearoffs = FALSE;
758   combo_box->priv->has_frame = TRUE;
759   combo_box->priv->is_cell_renderer = FALSE;
760   combo_box->priv->editing_canceled = FALSE;
761   combo_box->priv->auto_scroll = FALSE;
762   combo_box->priv->focus_on_click = TRUE;
763 }
764
765 static void
766 gtk_combo_box_set_property (GObject      *object,
767                             guint         prop_id,
768                             const GValue *value,
769                             GParamSpec   *pspec)
770 {
771   GtkComboBox *combo_box = GTK_COMBO_BOX (object);
772
773   switch (prop_id)
774     {
775       case PROP_MODEL:
776         gtk_combo_box_set_model (combo_box, g_value_get_object (value));
777         break;
778
779       case PROP_WRAP_WIDTH:
780         gtk_combo_box_set_wrap_width (combo_box, g_value_get_int (value));
781         break;
782
783       case PROP_ROW_SPAN_COLUMN:
784         gtk_combo_box_set_row_span_column (combo_box, g_value_get_int (value));
785         break;
786
787       case PROP_COLUMN_SPAN_COLUMN:
788         gtk_combo_box_set_column_span_column (combo_box, g_value_get_int (value));
789         break;
790
791       case PROP_ACTIVE:
792         gtk_combo_box_set_active (combo_box, g_value_get_int (value));
793         break;
794
795       case PROP_ADD_TEAROFFS:
796         gtk_combo_box_set_add_tearoffs (combo_box, g_value_get_boolean (value));
797         break;
798
799       case PROP_HAS_FRAME:
800         combo_box->priv->has_frame = g_value_get_boolean (value);
801         break;
802
803       case PROP_FOCUS_ON_CLICK:
804         gtk_combo_box_set_focus_on_click (combo_box, 
805                                           g_value_get_boolean (value));
806         break;
807
808       default:
809         break;
810     }
811 }
812
813 static void
814 gtk_combo_box_get_property (GObject    *object,
815                             guint       prop_id,
816                             GValue     *value,
817                             GParamSpec *pspec)
818 {
819   GtkComboBox *combo_box = GTK_COMBO_BOX (object);
820
821   switch (prop_id)
822     {
823       case PROP_MODEL:
824         g_value_set_object (value, combo_box->priv->model);
825         break;
826
827       case PROP_WRAP_WIDTH:
828         g_value_set_int (value, combo_box->priv->wrap_width);
829         break;
830
831       case PROP_ROW_SPAN_COLUMN:
832         g_value_set_int (value, combo_box->priv->row_column);
833         break;
834
835       case PROP_COLUMN_SPAN_COLUMN:
836         g_value_set_int (value, combo_box->priv->col_column);
837         break;
838
839       case PROP_ACTIVE:
840         g_value_set_int (value, gtk_combo_box_get_active (combo_box));
841         break;
842
843       case PROP_ADD_TEAROFFS:
844         g_value_set_boolean (value, gtk_combo_box_get_add_tearoffs (combo_box));
845         break;
846
847       case PROP_HAS_FRAME:
848         g_value_set_boolean (value, combo_box->priv->has_frame);
849         break;
850
851       case PROP_FOCUS_ON_CLICK:
852         g_value_set_boolean (value, combo_box->priv->focus_on_click);
853         break;
854
855       default:
856         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
857         break;
858     }
859 }
860
861 static void
862 gtk_combo_box_state_changed (GtkWidget    *widget,
863                              GtkStateType  previous)
864 {
865   GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
866
867   if (GTK_WIDGET_REALIZED (widget))
868     {
869       if (combo_box->priv->tree_view && combo_box->priv->cell_view)
870         gtk_cell_view_set_background_color (GTK_CELL_VIEW (combo_box->priv->cell_view), 
871                                             &widget->style->base[GTK_WIDGET_STATE (widget)]);
872     }
873
874   gtk_widget_queue_draw (widget);
875 }
876
877 static void
878 gtk_combo_box_button_state_changed (GtkWidget    *widget,
879                                     GtkStateType  previous,
880                                     gpointer      data)
881 {
882   GtkComboBox *combo_box = GTK_COMBO_BOX (data);
883
884   if (GTK_WIDGET_REALIZED (widget))
885     {
886       if (!combo_box->priv->tree_view && combo_box->priv->cell_view)
887         {
888           if ((GTK_WIDGET_STATE (widget) == GTK_STATE_INSENSITIVE) !=
889               (GTK_WIDGET_STATE (combo_box->priv->cell_view) == GTK_STATE_INSENSITIVE))
890             gtk_widget_set_sensitive (combo_box->priv->cell_view, GTK_WIDGET_SENSITIVE (widget));
891           
892           gtk_widget_set_state (combo_box->priv->cell_view, 
893                                 GTK_WIDGET_STATE (widget));
894           
895         }
896     }
897
898   gtk_widget_queue_draw (widget);
899 }
900
901 static void
902 gtk_combo_box_check_appearance (GtkComboBox *combo_box)
903 {
904   gboolean appears_as_list;
905
906   /* if wrap_width > 0, then we are in grid-mode and forced to use
907    * unix style
908    */
909   if (combo_box->priv->wrap_width)
910     appears_as_list = FALSE;
911   else
912     gtk_widget_style_get (GTK_WIDGET (combo_box),
913                           "appears-as-list", &appears_as_list,
914                           NULL);
915
916   if (appears_as_list)
917     {
918       /* Destroy all the menu mode widgets, if they exist. */
919       if (GTK_IS_MENU (combo_box->priv->popup_widget))
920         gtk_combo_box_menu_destroy (combo_box);
921
922       /* Create the list mode widgets, if they don't already exist. */
923       if (!GTK_IS_TREE_VIEW (combo_box->priv->tree_view))
924         gtk_combo_box_list_setup (combo_box);
925     }
926   else
927     {
928       /* Destroy all the list mode widgets, if they exist. */
929       if (GTK_IS_TREE_VIEW (combo_box->priv->tree_view))
930         gtk_combo_box_list_destroy (combo_box);
931
932       /* Create the menu mode widgets, if they don't already exist. */
933       if (!GTK_IS_MENU (combo_box->priv->popup_widget))
934         gtk_combo_box_menu_setup (combo_box, TRUE);
935     }
936 }
937
938 static void
939 gtk_combo_box_style_set (GtkWidget *widget,
940                          GtkStyle  *previous)
941 {
942   GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
943
944   gtk_combo_box_check_appearance (combo_box);
945
946   if (combo_box->priv->tree_view && combo_box->priv->cell_view)
947     gtk_cell_view_set_background_color (GTK_CELL_VIEW (combo_box->priv->cell_view), 
948                                         &widget->style->base[GTK_WIDGET_STATE (widget)]);
949 }
950
951 static void
952 gtk_combo_box_button_toggled (GtkWidget *widget,
953                               gpointer   data)
954 {
955   GtkComboBox *combo_box = GTK_COMBO_BOX (data);
956
957   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
958     {
959       if (!combo_box->priv->popup_in_progress)
960         gtk_combo_box_popup (combo_box);
961     }
962   else
963     gtk_combo_box_popdown (combo_box);
964 }
965
966 static void
967 gtk_combo_box_add (GtkContainer *container,
968                    GtkWidget    *widget)
969 {
970   GtkComboBox *combo_box = GTK_COMBO_BOX (container);
971
972   if (combo_box->priv->cell_view && combo_box->priv->cell_view->parent)
973     {
974       gtk_widget_unparent (combo_box->priv->cell_view);
975       GTK_BIN (container)->child = NULL;
976       gtk_widget_queue_resize (GTK_WIDGET (container));
977     }
978   
979   gtk_widget_set_parent (widget, GTK_WIDGET (container));
980   GTK_BIN (container)->child = widget;
981
982   if (combo_box->priv->cell_view &&
983       widget != combo_box->priv->cell_view)
984     {
985       /* since the cell_view was unparented, it's gone now */
986       combo_box->priv->cell_view = NULL;
987
988       if (!combo_box->priv->tree_view && combo_box->priv->separator)
989         {
990           gtk_container_remove (GTK_CONTAINER (combo_box->priv->separator->parent),
991                                 combo_box->priv->separator);
992           combo_box->priv->separator = NULL;
993
994           gtk_widget_queue_resize (GTK_WIDGET (container));
995         }
996       else if (combo_box->priv->cell_view_frame)
997         {
998           gtk_widget_unparent (combo_box->priv->cell_view_frame);
999           combo_box->priv->cell_view_frame = NULL;
1000           combo_box->priv->box = NULL;
1001         }
1002     }
1003 }
1004
1005 static void
1006 gtk_combo_box_remove (GtkContainer *container,
1007                       GtkWidget    *widget)
1008 {
1009   GtkComboBox *combo_box = GTK_COMBO_BOX (container);
1010   GtkTreePath *path;
1011   gboolean appears_as_list;
1012
1013   gtk_widget_unparent (widget);
1014   GTK_BIN (container)->child = NULL;
1015
1016   if (combo_box->priv->destroying)
1017     return;
1018
1019   gtk_widget_queue_resize (GTK_WIDGET (container));
1020
1021   if (!combo_box->priv->tree_view)
1022     appears_as_list = FALSE;
1023   else
1024     appears_as_list = TRUE;
1025   
1026   if (appears_as_list)
1027     gtk_combo_box_list_destroy (combo_box);
1028   else if (GTK_IS_MENU (combo_box->priv->popup_widget))
1029     {
1030       gtk_combo_box_menu_destroy (combo_box);
1031       gtk_menu_detach (GTK_MENU (combo_box->priv->popup_widget));
1032       combo_box->priv->popup_widget = NULL;
1033     }
1034
1035   if (!combo_box->priv->cell_view)
1036     {
1037       combo_box->priv->cell_view = gtk_cell_view_new ();
1038       gtk_widget_set_parent (combo_box->priv->cell_view, GTK_WIDGET (container));
1039       GTK_BIN (container)->child = combo_box->priv->cell_view;
1040       
1041       gtk_widget_show (combo_box->priv->cell_view);
1042       gtk_cell_view_set_model (GTK_CELL_VIEW (combo_box->priv->cell_view),
1043                                combo_box->priv->model);
1044       gtk_combo_box_sync_cells (combo_box, GTK_CELL_LAYOUT (combo_box->priv->cell_view));
1045     }
1046
1047
1048   if (appears_as_list)
1049     gtk_combo_box_list_setup (combo_box); 
1050   else
1051     gtk_combo_box_menu_setup (combo_box, TRUE);
1052
1053   if (gtk_tree_row_reference_valid (combo_box->priv->active_row))
1054     {
1055       path = gtk_tree_row_reference_get_path (combo_box->priv->active_row);
1056       gtk_combo_box_set_active_internal (combo_box, path);
1057       gtk_tree_path_free (path);
1058     }
1059   else
1060     gtk_combo_box_set_active_internal (combo_box, NULL);
1061 }
1062
1063 static ComboCellInfo *
1064 gtk_combo_box_get_cell_info (GtkComboBox     *combo_box,
1065                              GtkCellRenderer *cell)
1066 {
1067   GSList *i;
1068
1069   for (i = combo_box->priv->cells; i; i = i->next)
1070     {
1071       ComboCellInfo *info = (ComboCellInfo *)i->data;
1072
1073       if (info && info->cell == cell)
1074         return info;
1075     }
1076
1077   return NULL;
1078 }
1079
1080 static void
1081 gtk_combo_box_menu_show (GtkWidget *menu,
1082                          gpointer   user_data)
1083 {
1084   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1085
1086   gtk_combo_box_child_show (menu, user_data);
1087
1088   combo_box->priv->popup_in_progress = TRUE;
1089   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
1090                                 TRUE);
1091   combo_box->priv->popup_in_progress = FALSE;
1092 }
1093
1094 static void
1095 gtk_combo_box_menu_hide (GtkWidget *menu,
1096                          gpointer   user_data)
1097 {
1098   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1099
1100   gtk_combo_box_child_hide(menu,user_data);
1101
1102   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
1103                                 FALSE);
1104 }
1105
1106 static void
1107 gtk_combo_box_detacher (GtkWidget *widget,
1108                         GtkMenu   *menu)
1109 {
1110   GtkComboBox *combo_box;
1111
1112   g_return_if_fail (GTK_IS_COMBO_BOX (widget));
1113
1114   combo_box = GTK_COMBO_BOX (widget);
1115   g_return_if_fail (combo_box->priv->popup_widget == (GtkWidget*) menu);
1116
1117   g_signal_handlers_disconnect_by_func (menu->toplevel,
1118                                         gtk_combo_box_menu_show,
1119                                         combo_box);
1120   g_signal_handlers_disconnect_by_func (menu->toplevel,
1121                                         gtk_combo_box_menu_hide,
1122                                         combo_box);
1123   
1124   combo_box->priv->popup_widget = NULL;
1125 }
1126
1127 static void
1128 gtk_combo_box_set_popup_widget (GtkComboBox *combo_box,
1129                                 GtkWidget   *popup)
1130 {
1131   if (GTK_IS_MENU (combo_box->priv->popup_widget))
1132     {
1133       gtk_menu_detach (GTK_MENU (combo_box->priv->popup_widget));
1134       combo_box->priv->popup_widget = NULL;
1135     }
1136   else if (combo_box->priv->popup_widget)
1137     {
1138       gtk_container_remove (GTK_CONTAINER (combo_box->priv->popup_frame),
1139                             combo_box->priv->popup_widget);
1140       g_object_unref (combo_box->priv->popup_widget);
1141       combo_box->priv->popup_widget = NULL;
1142     }
1143
1144   if (GTK_IS_MENU (popup))
1145     {
1146       if (combo_box->priv->popup_window)
1147         {
1148           gtk_widget_destroy (combo_box->priv->popup_window);
1149           combo_box->priv->popup_window = NULL;
1150           combo_box->priv->popup_frame = NULL;
1151         }
1152
1153       combo_box->priv->popup_widget = popup;
1154
1155       /* 
1156        * Note that we connect to show/hide on the toplevel, not the
1157        * menu itself, since the menu is not shown/hidden when it is
1158        * popped up while torn-off.
1159        */
1160       g_signal_connect (GTK_MENU (popup)->toplevel, "show",
1161                         G_CALLBACK (gtk_combo_box_menu_show), combo_box);
1162       g_signal_connect (GTK_MENU (popup)->toplevel, "hide",
1163                         G_CALLBACK (gtk_combo_box_menu_hide), combo_box);
1164
1165       gtk_menu_attach_to_widget (GTK_MENU (popup),
1166                                  GTK_WIDGET (combo_box),
1167                                  gtk_combo_box_detacher);
1168     }
1169   else
1170     {
1171       if (!combo_box->priv->popup_window)
1172         {
1173           GtkWidget *toplevel;
1174           
1175           combo_box->priv->popup_window = gtk_window_new (GTK_WINDOW_POPUP);
1176
1177           g_signal_connect (GTK_WINDOW(combo_box->priv->popup_window),"show",
1178                             G_CALLBACK (gtk_combo_box_child_show),
1179                             combo_box);
1180           g_signal_connect (GTK_WINDOW(combo_box->priv->popup_window),"hide",
1181                             G_CALLBACK (gtk_combo_box_child_hide),
1182                             combo_box);
1183           
1184           toplevel = gtk_widget_get_toplevel (GTK_WIDGET (combo_box));
1185           if (GTK_IS_WINDOW (toplevel))
1186             gtk_window_group_add_window (_gtk_window_get_group (GTK_WINDOW (toplevel)), 
1187                                          GTK_WINDOW (combo_box->priv->popup_window));
1188
1189           gtk_window_set_resizable (GTK_WINDOW (combo_box->priv->popup_window), FALSE);
1190           gtk_window_set_screen (GTK_WINDOW (combo_box->priv->popup_window),
1191                                  gtk_widget_get_screen (GTK_WIDGET (combo_box)));
1192
1193           combo_box->priv->popup_frame = gtk_frame_new (NULL);
1194           gtk_frame_set_shadow_type (GTK_FRAME (combo_box->priv->popup_frame),
1195                                      GTK_SHADOW_ETCHED_IN);
1196           gtk_container_add (GTK_CONTAINER (combo_box->priv->popup_window),
1197                              combo_box->priv->popup_frame);
1198
1199           gtk_widget_show (combo_box->priv->popup_frame);
1200
1201           combo_box->priv->scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1202           
1203           gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window),
1204                                           GTK_POLICY_NEVER,
1205                                           GTK_POLICY_NEVER);
1206           gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window),
1207                                                GTK_SHADOW_NONE);
1208
1209           gtk_widget_show (combo_box->priv->scrolled_window);
1210           
1211           gtk_container_add (GTK_CONTAINER (combo_box->priv->popup_frame),
1212                              combo_box->priv->scrolled_window);
1213         }
1214
1215       gtk_container_add (GTK_CONTAINER (combo_box->priv->scrolled_window),
1216                          popup);
1217
1218       gtk_widget_show (popup);
1219       g_object_ref (popup);
1220       combo_box->priv->popup_widget = popup;
1221     }
1222 }
1223
1224 static void
1225 gtk_combo_box_menu_position_below (GtkMenu  *menu,
1226                                    gint     *x,
1227                                    gint     *y,
1228                                    gint     *push_in,
1229                                    gpointer  user_data)
1230 {
1231   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1232   gint sx, sy;
1233   GtkWidget *child;
1234   GtkRequisition req;
1235   GdkScreen *screen;
1236   gint monitor_num;
1237   GdkRectangle monitor;
1238   
1239   /* FIXME: is using the size request here broken? */
1240    child = GTK_BIN (combo_box)->child;
1241    
1242    gdk_window_get_origin (child->window, &sx, &sy);
1243    
1244    if (GTK_WIDGET_NO_WINDOW (child))
1245       {
1246         sx += child->allocation.x;
1247         sy += child->allocation.y;
1248       }
1249
1250    gtk_widget_size_request (GTK_WIDGET (menu), &req);
1251
1252    if (gtk_widget_get_direction (GTK_WIDGET (combo_box)) == GTK_TEXT_DIR_LTR)
1253      *x = sx;
1254    else
1255      *x = sx + child->allocation.width - req.width;
1256    *y = sy;
1257
1258   screen = gtk_widget_get_screen (GTK_WIDGET (combo_box));
1259   monitor_num = gdk_screen_get_monitor_at_window (screen, 
1260                                                   GTK_WIDGET (combo_box)->window);
1261   gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1262   
1263   if (*x < monitor.x)
1264     *x = monitor.x;
1265   else if (*x + req.width > monitor.x + monitor.width)
1266     *x = monitor.x + monitor.width - req.width;
1267   
1268   if (monitor.y + monitor.height - *y - child->allocation.height >= req.height)
1269     *y += child->allocation.height;
1270   else if (*y - monitor.y >= req.height)
1271     *y -= req.height;
1272   else if (monitor.y + monitor.height - *y - child->allocation.height > *y - monitor.y) 
1273     *y += child->allocation.height;
1274   else
1275     *y -= req.height;
1276
1277    *push_in = FALSE;
1278 }
1279
1280 static void
1281 gtk_combo_box_menu_position_over (GtkMenu  *menu,
1282                                   gint     *x,
1283                                   gint     *y,
1284                                   gboolean *push_in,
1285                                   gpointer  user_data)
1286 {
1287   GtkComboBox *combo_box;
1288   GtkWidget *active;
1289   GtkWidget *child;
1290   GtkWidget *widget;
1291   GtkRequisition requisition;
1292   GList *children;
1293   gint screen_width;
1294   gint menu_xpos;
1295   gint menu_ypos;
1296   gint menu_width;
1297
1298   g_return_if_fail (GTK_IS_COMBO_BOX (user_data));
1299   
1300   combo_box = GTK_COMBO_BOX (user_data);
1301   widget = GTK_WIDGET (combo_box);
1302
1303   gtk_widget_get_child_requisition (GTK_WIDGET (menu), &requisition);
1304   menu_width = requisition.width;
1305
1306   active = gtk_menu_get_active (GTK_MENU (combo_box->priv->popup_widget));
1307   gdk_window_get_origin (widget->window, &menu_xpos, &menu_ypos);
1308
1309   menu_xpos += widget->allocation.x;
1310   menu_ypos += widget->allocation.y + widget->allocation.height / 2 - 2;
1311
1312   if (active != NULL)
1313     {
1314       gtk_widget_get_child_requisition (active, &requisition);
1315       menu_ypos -= requisition.height / 2;
1316     }
1317
1318   children = GTK_MENU_SHELL (combo_box->priv->popup_widget)->children;
1319   while (children)
1320     {
1321       child = children->data;
1322
1323       if (active == child)
1324         break;
1325
1326       if (GTK_WIDGET_VISIBLE (child))
1327         {
1328           gtk_widget_get_child_requisition (child, &requisition);
1329           menu_ypos -= requisition.height;
1330         }
1331
1332       children = children->next;
1333     }
1334
1335   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
1336     menu_xpos = menu_xpos + widget->allocation.width - menu_width;
1337
1338   /* Clamp the position on screen */
1339   screen_width = gdk_screen_get_width (gtk_widget_get_screen (widget));
1340   
1341   if (menu_xpos < 0)
1342     menu_xpos = 0;
1343   else if ((menu_xpos + menu_width) > screen_width)
1344     menu_xpos -= ((menu_xpos + menu_width) - screen_width);
1345
1346   *x = menu_xpos;
1347   *y = menu_ypos;
1348
1349   *push_in = TRUE;
1350 }
1351
1352 static void
1353 gtk_combo_box_menu_position (GtkMenu  *menu,
1354                              gint     *x,
1355                              gint     *y,
1356                              gint     *push_in,
1357                              gpointer  user_data)
1358 {
1359   GtkComboBox *combo_box;
1360   GtkWidget *menu_item;
1361
1362   combo_box = GTK_COMBO_BOX (user_data);
1363
1364   if (combo_box->priv->wrap_width > 0 || combo_box->priv->cell_view == NULL)    
1365     gtk_combo_box_menu_position_below (menu, x, y, push_in, user_data);
1366   else
1367     {
1368       /* FIXME handle nested menus better */
1369       menu_item = gtk_menu_get_active (GTK_MENU (combo_box->priv->popup_widget));
1370       if (menu_item)
1371         gtk_menu_shell_select_item (GTK_MENU_SHELL (combo_box->priv->popup_widget), 
1372                                     menu_item);
1373
1374       gtk_combo_box_menu_position_over (menu, x, y, push_in, user_data);
1375     }
1376
1377 }
1378
1379 static void
1380 gtk_combo_box_list_position (GtkComboBox *combo_box, 
1381                              gint        *x, 
1382                              gint        *y, 
1383                              gint        *width,
1384                              gint        *height)
1385 {
1386   GtkWidget *sample;
1387   GdkScreen *screen;
1388   gint monitor_num;
1389   GdkRectangle monitor;
1390   GtkRequisition popup_req;
1391   GtkPolicyType hpolicy, vpolicy;
1392   
1393   sample = GTK_BIN (combo_box)->child;
1394
1395   gdk_window_get_origin (sample->window, x, y);
1396
1397   if (GTK_WIDGET_NO_WINDOW (sample))
1398     {
1399       *x += sample->allocation.x;
1400       *y += sample->allocation.y;
1401     }
1402   
1403   *width = sample->allocation.width;
1404   
1405   if (combo_box->priv->cell_view_frame && combo_box->priv->has_frame)
1406     {
1407        *x -= GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
1408              GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness;
1409        *width += 2 * (GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
1410             GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness);
1411     }
1412
1413   hpolicy = vpolicy = GTK_POLICY_NEVER;
1414   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window),
1415                                   hpolicy, vpolicy);
1416   gtk_widget_size_request (combo_box->priv->popup_frame, &popup_req);
1417
1418   if (popup_req.width > *width)
1419     {
1420       hpolicy = GTK_POLICY_ALWAYS;
1421       gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window),
1422                                       hpolicy, vpolicy);
1423       gtk_widget_size_request (combo_box->priv->popup_frame, &popup_req);
1424     }
1425
1426   *height = popup_req.height;
1427
1428   screen = gtk_widget_get_screen (GTK_WIDGET (combo_box));
1429   monitor_num = gdk_screen_get_monitor_at_window (screen, 
1430                                                   GTK_WIDGET (combo_box)->window);
1431   gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1432
1433   if (*x < monitor.x)
1434     *x = monitor.x;
1435   else if (*x + *width > monitor.x + monitor.width)
1436     *x = monitor.x + monitor.width - *width;
1437   
1438   if (*y + sample->allocation.height + *height <= monitor.y + monitor.height)
1439     *y += sample->allocation.height;
1440   else if (*y - *height >= monitor.y)
1441     *y -= *height;
1442   else if (monitor.y + monitor.height - (*y + sample->allocation.height) > *y - monitor.y)
1443     {
1444       *y += sample->allocation.height;
1445       *height = monitor.y + monitor.height - *y;
1446     }
1447   else 
1448     {
1449       *height = *y - monitor.y;
1450       *y = monitor.y;
1451     }
1452
1453   if (popup_req.height > *height)
1454     {
1455       vpolicy = GTK_POLICY_ALWAYS;
1456       
1457       gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window),
1458                                       hpolicy, vpolicy);
1459     }
1460
1461
1462 static gboolean
1463 cell_view_is_sensitive (GtkCellView *cell_view)
1464 {
1465   GList *cells, *list;
1466   gboolean sensitive;
1467   
1468   cells = gtk_cell_view_get_cell_renderers (cell_view);
1469
1470   sensitive = FALSE;
1471   list = cells;
1472   while (list)
1473     {
1474       g_object_get (list->data, "sensitive", &sensitive, NULL);
1475       
1476       if (sensitive)
1477         break;
1478
1479       list = list->next;
1480     }
1481   g_list_free (cells);
1482
1483   return sensitive;
1484 }
1485
1486 static gboolean
1487 tree_column_row_is_sensitive (GtkComboBox *combo_box,
1488                               GtkTreeIter *iter)
1489 {
1490   GList *cells, *list;
1491   gboolean sensitive;
1492
1493   if (!combo_box->priv->column)
1494     return TRUE;
1495
1496   if (combo_box->priv->row_separator_func)
1497     {
1498       if ((*combo_box->priv->row_separator_func) (combo_box->priv->model, iter,
1499                                                   combo_box->priv->row_separator_data))
1500         return FALSE;
1501     }
1502
1503   gtk_tree_view_column_cell_set_cell_data (combo_box->priv->column,
1504                                            combo_box->priv->model,
1505                                            iter, FALSE, FALSE);
1506
1507   cells = gtk_tree_view_column_get_cell_renderers (combo_box->priv->column);
1508
1509   sensitive = FALSE;
1510   list = cells;
1511   while (list)
1512     {
1513       g_object_get (list->data, "sensitive", &sensitive, NULL);
1514       
1515       if (sensitive)
1516         break;
1517
1518       list = list->next;
1519     }
1520   g_list_free (cells);
1521
1522   return sensitive;
1523 }
1524
1525 static void
1526 update_menu_sensitivity (GtkComboBox *combo_box,
1527                          GtkWidget   *menu)
1528 {
1529   GList *children, *child;
1530   GtkWidget *item, *submenu, *separator;
1531   GtkWidget *cell_view;
1532   gboolean sensitive;
1533
1534   if (!combo_box->priv->model)
1535     return;
1536
1537   children = gtk_container_get_children (GTK_CONTAINER (menu));
1538
1539   for (child = children; child; child = child->next)
1540     {
1541       item = GTK_WIDGET (child->data);
1542       cell_view = GTK_BIN (item)->child;
1543
1544       if (!GTK_IS_CELL_VIEW (cell_view))
1545         continue;
1546       
1547       submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (item));
1548       if (submenu != NULL)
1549         {
1550           gtk_widget_set_sensitive (item, TRUE);
1551           update_menu_sensitivity (combo_box, submenu);
1552         }
1553       else
1554         {
1555           sensitive = cell_view_is_sensitive (GTK_CELL_VIEW (cell_view));
1556
1557           if (menu != combo_box->priv->popup_widget && child == children)
1558             {
1559               separator = GTK_WIDGET (child->next->data);
1560               g_object_set (item, "visible", sensitive, NULL);
1561               g_object_set (separator, "visible", sensitive, NULL);
1562             }
1563           else
1564             gtk_widget_set_sensitive (item, sensitive);
1565         }
1566     }
1567
1568   g_list_free (children);
1569 }
1570
1571 static void 
1572 gtk_combo_box_menu_popup (GtkComboBox *combo_box,
1573                           guint        button, 
1574                           guint32      activate_time)
1575 {
1576   GtkTreePath *path;
1577   gint active_item;
1578   GtkRequisition requisition;
1579   gint width;
1580   
1581   update_menu_sensitivity (combo_box, combo_box->priv->popup_widget);
1582
1583   active_item = -1;
1584   if (gtk_tree_row_reference_valid (combo_box->priv->active_row))
1585     {
1586       path = gtk_tree_row_reference_get_path (combo_box->priv->active_row);
1587       active_item = gtk_tree_path_get_indices (path)[0];
1588       gtk_tree_path_free (path);
1589       
1590       if (combo_box->priv->add_tearoffs)
1591         active_item++;
1592     }
1593
1594   /* FIXME handle nested menus better */
1595   gtk_menu_set_active (GTK_MENU (combo_box->priv->popup_widget), active_item);
1596   
1597   if (combo_box->priv->wrap_width == 0)
1598     {
1599       width = GTK_WIDGET (combo_box)->allocation.width;
1600       gtk_widget_set_size_request (combo_box->priv->popup_widget, -1, -1);
1601       gtk_widget_size_request (combo_box->priv->popup_widget, &requisition);
1602       
1603       gtk_widget_set_size_request (combo_box->priv->popup_widget,
1604                                    MAX (width, requisition.width), -1);
1605     }
1606   
1607   gtk_menu_popup (GTK_MENU (combo_box->priv->popup_widget),
1608                   NULL, NULL,
1609                   gtk_combo_box_menu_position, combo_box,
1610                   button, activate_time);
1611 }
1612
1613 static gboolean
1614 popup_grab_on_window (GdkWindow *window,
1615                       guint32    activate_time,
1616                       gboolean   grab_keyboard)
1617 {
1618   if ((gdk_pointer_grab (window, TRUE,
1619                          GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1620                          GDK_POINTER_MOTION_MASK,
1621                          NULL, NULL, activate_time) == 0))
1622     {
1623       if (!grab_keyboard ||
1624           gdk_keyboard_grab (window, TRUE,
1625                              activate_time) == 0)
1626         return TRUE;
1627       else
1628         {
1629           gdk_display_pointer_ungrab (gdk_drawable_get_display (window),
1630                                       activate_time);
1631           return FALSE;
1632         }
1633     }
1634
1635   return FALSE;
1636 }
1637
1638 /**
1639  * gtk_combo_box_popup:
1640  * @combo_box: a #GtkComboBox
1641  * 
1642  * Pops up the menu or dropdown list of @combo_box. 
1643  *
1644  * This function is mostly intended for use by accessibility technologies;
1645  * applications should have little use for it.
1646  *
1647  * Since: 2.4
1648  **/
1649 void
1650 gtk_combo_box_popup (GtkComboBox *combo_box)
1651 {
1652   gint x, y, width, height;
1653   GtkTreePath *path, *ppath;
1654   GtkWidget *toplevel;
1655
1656   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
1657
1658   if (!GTK_WIDGET_REALIZED (combo_box))
1659     return;
1660
1661   if (GTK_WIDGET_MAPPED (combo_box->priv->popup_widget))
1662     return;
1663
1664   if (GTK_IS_MENU (combo_box->priv->popup_widget))
1665     {
1666       gtk_combo_box_menu_popup (combo_box, 0, 0);
1667       return;
1668     }
1669
1670   toplevel = gtk_widget_get_toplevel (GTK_WIDGET (combo_box));
1671   if (GTK_IS_WINDOW (toplevel))
1672     gtk_window_group_add_window (_gtk_window_get_group (GTK_WINDOW (toplevel)), 
1673                                  GTK_WINDOW (combo_box->priv->popup_window));
1674
1675   gtk_widget_show_all (combo_box->priv->popup_frame);
1676   gtk_combo_box_list_position (combo_box, &x, &y, &width, &height);
1677   
1678   gtk_widget_set_size_request (combo_box->priv->popup_window, width, height);  
1679   gtk_window_move (GTK_WINDOW (combo_box->priv->popup_window), x, y);
1680
1681   if (gtk_tree_row_reference_valid (combo_box->priv->active_row))
1682     {
1683       path = gtk_tree_row_reference_get_path (combo_box->priv->active_row);
1684       ppath = gtk_tree_path_copy (path);
1685       if (gtk_tree_path_up (ppath))
1686         gtk_tree_view_expand_to_path (GTK_TREE_VIEW (combo_box->priv->tree_view),
1687                                       ppath);
1688       gtk_tree_view_set_cursor (GTK_TREE_VIEW (combo_box->priv->tree_view),
1689                                 path, NULL, FALSE);
1690       gtk_tree_path_free (path);
1691       gtk_tree_path_free (ppath);
1692     }
1693   gtk_tree_view_set_hover_expand (GTK_TREE_VIEW (combo_box->priv->tree_view), 
1694                                   TRUE);
1695   
1696   /* popup */
1697   gtk_widget_show (combo_box->priv->popup_window);
1698
1699   gtk_widget_grab_focus (combo_box->priv->popup_window);
1700   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
1701                                 TRUE);
1702
1703   if (!GTK_WIDGET_HAS_FOCUS (combo_box->priv->tree_view))
1704     gtk_widget_grab_focus (combo_box->priv->tree_view);
1705
1706   if (!popup_grab_on_window (combo_box->priv->popup_window->window,
1707                              GDK_CURRENT_TIME, TRUE))
1708     {
1709       gtk_widget_hide (combo_box->priv->popup_window);
1710       return;
1711     }
1712
1713   gtk_grab_add (combo_box->priv->popup_window);
1714 }
1715
1716 /**
1717  * gtk_combo_box_popdown:
1718  * @combo_box: a #GtkComboBox
1719  * 
1720  * Hides the menu or dropdown list of @combo_box.
1721  *
1722  * This function is mostly intended for use by accessibility technologies;
1723  * applications should have little use for it.
1724  *
1725  * Since: 2.4
1726  **/
1727 void
1728 gtk_combo_box_popdown (GtkComboBox *combo_box)
1729 {
1730   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
1731
1732   if (GTK_IS_MENU (combo_box->priv->popup_widget))
1733     {
1734       gtk_menu_popdown (GTK_MENU (combo_box->priv->popup_widget));
1735       return;
1736     }
1737
1738   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (combo_box)))
1739     return;
1740
1741   gtk_grab_remove (combo_box->priv->popup_window);
1742   gtk_widget_hide_all (combo_box->priv->popup_window);
1743   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
1744                                 FALSE);
1745 }
1746
1747 static gint
1748 gtk_combo_box_calc_requested_width (GtkComboBox *combo_box,
1749                                     GtkTreePath *path)
1750 {
1751   gint padding;
1752   GtkRequisition req;
1753
1754   if (combo_box->priv->cell_view)
1755     gtk_widget_style_get (combo_box->priv->cell_view,
1756                           "focus-line-width", &padding,
1757                           NULL);
1758   else
1759     padding = 0;
1760
1761   /* add some pixels for good measure */
1762   padding += BONUS_PADDING;
1763
1764   if (combo_box->priv->cell_view)
1765     gtk_cell_view_get_size_of_row (GTK_CELL_VIEW (combo_box->priv->cell_view),
1766                                    path, &req);
1767   else
1768     req.width = 0;
1769
1770   return req.width + padding;
1771 }
1772
1773 static void
1774 gtk_combo_box_remeasure (GtkComboBox *combo_box)
1775 {
1776   GtkTreeIter iter;
1777   GtkTreePath *path;
1778
1779   if (!combo_box->priv->model ||
1780       !gtk_tree_model_get_iter_first (combo_box->priv->model, &iter))
1781     return;
1782
1783   combo_box->priv->width = 0;
1784
1785   path = gtk_tree_path_new_from_indices (0, -1);
1786
1787   do
1788     {
1789       GtkRequisition req;
1790
1791       if (combo_box->priv->cell_view)
1792         gtk_cell_view_get_size_of_row (GTK_CELL_VIEW (combo_box->priv->cell_view), 
1793                                        path, &req);
1794       else
1795         req.width = 0;
1796
1797       combo_box->priv->width = MAX (combo_box->priv->width, req.width);
1798
1799       gtk_tree_path_next (path);
1800     }
1801   while (gtk_tree_model_iter_next (combo_box->priv->model, &iter));
1802
1803   gtk_tree_path_free (path);
1804 }
1805
1806 static void
1807 gtk_combo_box_size_request (GtkWidget      *widget,
1808                             GtkRequisition *requisition)
1809 {
1810   gint width, height;
1811   gint focus_width, focus_pad;
1812   GtkRequisition bin_req;
1813
1814   GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1815
1816   gtk_combo_box_check_appearance (combo_box);
1817
1818   /* common */
1819   gtk_widget_size_request (GTK_BIN (widget)->child, &bin_req);
1820   gtk_combo_box_remeasure (combo_box);
1821   bin_req.width = MAX (bin_req.width, combo_box->priv->width);
1822
1823   gtk_widget_style_get (GTK_WIDGET (widget),
1824                         "focus-line-width", &focus_width,
1825                         "focus-padding", &focus_pad,
1826                         NULL);
1827
1828   if (!combo_box->priv->tree_view)
1829     {
1830       /* menu mode */
1831
1832       if (combo_box->priv->cell_view)
1833         {
1834           GtkRequisition button_req, sep_req, arrow_req;
1835           gint border_width, xthickness, ythickness;
1836
1837           gtk_widget_size_request (combo_box->priv->button, &button_req);
1838           border_width = GTK_CONTAINER (combo_box)->border_width;
1839           xthickness = combo_box->priv->button->style->xthickness;
1840           ythickness = combo_box->priv->button->style->ythickness;
1841
1842           bin_req.width = MAX (bin_req.width, combo_box->priv->width);
1843
1844           gtk_widget_size_request (combo_box->priv->separator, &sep_req);
1845           gtk_widget_size_request (combo_box->priv->arrow, &arrow_req);
1846
1847           height = MAX (sep_req.height, arrow_req.height);
1848           height = MAX (height, bin_req.height);
1849
1850           width = bin_req.width + sep_req.width + arrow_req.width;
1851
1852           height += 2*(border_width + ythickness + focus_width + focus_pad);
1853           width  += 2*(border_width + xthickness + focus_width + focus_pad);
1854
1855           requisition->width = width;
1856           requisition->height = height;
1857         }
1858       else
1859         {
1860           GtkRequisition but_req;
1861
1862           gtk_widget_size_request (combo_box->priv->button, &but_req);
1863
1864           requisition->width = bin_req.width + but_req.width;
1865           requisition->height = MAX (bin_req.height, but_req.height);
1866         }
1867     }
1868   else
1869     {
1870       /* list mode */
1871       GtkRequisition button_req, frame_req;
1872
1873       /* sample + frame */
1874       *requisition = bin_req;
1875
1876       requisition->width += 2 * focus_width;
1877       
1878       if (combo_box->priv->cell_view_frame)
1879         {
1880           gtk_widget_size_request (combo_box->priv->cell_view_frame, &frame_req);
1881           if (combo_box->priv->has_frame)
1882             {
1883               requisition->width += 2 *
1884                 (GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
1885                  GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness);
1886               requisition->height += 2 *
1887                 (GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
1888                  GTK_WIDGET (combo_box->priv->cell_view_frame)->style->ythickness);
1889             }
1890         }
1891
1892       /* the button */
1893       gtk_widget_size_request (combo_box->priv->button, &button_req);
1894
1895       requisition->height = MAX (requisition->height, button_req.height);
1896       requisition->width += button_req.width;
1897     }
1898 }
1899
1900 static void
1901 gtk_combo_box_size_allocate (GtkWidget     *widget,
1902                              GtkAllocation *allocation)
1903 {
1904   GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1905   gint focus_width, focus_pad;
1906   GtkAllocation child;
1907   GtkRequisition req;
1908   gboolean is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
1909
1910   gtk_combo_box_check_appearance (combo_box);
1911
1912   widget->allocation = *allocation;
1913
1914   gtk_widget_style_get (GTK_WIDGET (widget),
1915                         "focus-line-width", &focus_width,
1916                         "focus-padding", &focus_pad,
1917                         NULL);
1918
1919   if (!combo_box->priv->tree_view)
1920     {
1921       if (combo_box->priv->cell_view)
1922         {
1923           gint border_width, xthickness, ythickness;
1924           gint width;
1925
1926           /* menu mode */
1927           gtk_widget_size_allocate (combo_box->priv->button, allocation);
1928
1929           /* set some things ready */
1930           border_width = GTK_CONTAINER (combo_box->priv->button)->border_width;
1931           xthickness = combo_box->priv->button->style->xthickness;
1932           ythickness = combo_box->priv->button->style->ythickness;
1933
1934           child.x = allocation->x;
1935           child.y = allocation->y;
1936           width = allocation->width;
1937           child.height = allocation->height;
1938
1939           if (!combo_box->priv->is_cell_renderer)
1940             {
1941               child.x += border_width + xthickness + focus_width + focus_pad;
1942               child.y += border_width + ythickness + focus_width + focus_pad;
1943               width -= 2 * (child.x - allocation->x);
1944               child.height -= 2 * (child.y - allocation->y);
1945             }
1946
1947
1948           /* handle the children */
1949           gtk_widget_size_request (combo_box->priv->arrow, &req);
1950           child.width = req.width;
1951           if (!is_rtl)
1952             child.x += width - req.width;
1953           child.width = MAX (1, child.width);
1954           child.height = MAX (1, child.height);
1955           gtk_widget_size_allocate (combo_box->priv->arrow, &child);
1956           if (is_rtl)
1957             child.x += req.width;
1958           gtk_widget_size_request (combo_box->priv->separator, &req);
1959           child.width = req.width;
1960           if (!is_rtl)
1961             child.x -= req.width;
1962           child.width = MAX (1, child.width);
1963           child.height = MAX (1, child.height);
1964           gtk_widget_size_allocate (combo_box->priv->separator, &child);
1965
1966           if (is_rtl)
1967             {
1968               child.x += req.width;
1969               child.width = allocation->x + allocation->width 
1970                 - (border_width + xthickness + focus_width + focus_pad) 
1971                 - child.x;
1972             }
1973           else 
1974             {
1975               child.width = child.x;
1976               child.x = allocation->x 
1977                 + border_width + xthickness + focus_width + focus_pad;
1978               child.width -= child.x;
1979             }
1980
1981           child.width = MAX (1, child.width);
1982           child.height = MAX (1, child.height);
1983           gtk_widget_size_allocate (GTK_BIN (widget)->child, &child);
1984         }
1985       else
1986         {
1987           gtk_widget_size_request (combo_box->priv->button, &req);
1988           if (is_rtl)
1989             child.x = allocation->x;
1990           else
1991             child.x = allocation->x + allocation->width - req.width;
1992           child.y = allocation->y;
1993           child.width = req.width;
1994           child.height = allocation->height;
1995           child.width = MAX (1, child.width);
1996           child.height = MAX (1, child.height);
1997           gtk_widget_size_allocate (combo_box->priv->button, &child);
1998
1999           if (is_rtl)
2000             child.x = allocation->x + req.width;
2001           else
2002             child.x = allocation->x;
2003           child.y = allocation->y;
2004           child.width = allocation->width - req.width;
2005           child.width = MAX (1, child.width);
2006           child.height = MAX (1, child.height);
2007           gtk_widget_size_allocate (GTK_BIN (widget)->child, &child);
2008         }
2009     }
2010   else
2011     {
2012       /* list mode */
2013
2014       /* button */
2015       gtk_widget_size_request (combo_box->priv->button, &req);
2016       if (is_rtl)
2017         child.x = allocation->x;
2018       else
2019         child.x = allocation->x + allocation->width - req.width;
2020       child.y = allocation->y;
2021       child.width = req.width;
2022       child.height = allocation->height;
2023       child.width = MAX (1, child.width);
2024       child.height = MAX (1, child.height);
2025       gtk_widget_size_allocate (combo_box->priv->button, &child);
2026
2027       /* frame */
2028       if (is_rtl)
2029         child.x = allocation->x + req.width;
2030       else
2031         child.x = allocation->x;
2032       child.y = allocation->y;
2033       child.width = allocation->width - req.width;
2034       child.height = allocation->height;
2035
2036       if (combo_box->priv->cell_view_frame)
2037         {
2038           child.width = MAX (1, child.width);
2039           child.height = MAX (1, child.height);
2040           gtk_widget_size_allocate (combo_box->priv->cell_view_frame, &child);
2041
2042           /* the sample */
2043           if (combo_box->priv->has_frame)
2044             {
2045               child.x +=
2046                 GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
2047                 GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness;
2048               child.y +=
2049                 GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
2050                 GTK_WIDGET (combo_box->priv->cell_view_frame)->style->ythickness;
2051               child.width -= 2 * (
2052                                   GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
2053                                   GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness);
2054               child.height -= 2 * (
2055                                    GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
2056                                    GTK_WIDGET (combo_box->priv->cell_view_frame)->style->ythickness);
2057             }
2058         }
2059       
2060       child.width = MAX (1, child.width);
2061       child.height = MAX (1, child.height);
2062       gtk_widget_size_allocate (GTK_BIN (combo_box)->child, &child);
2063     }
2064 }
2065
2066 static void
2067 gtk_combo_box_unset_model (GtkComboBox *combo_box)
2068 {
2069   if (combo_box->priv->model)
2070     {
2071       g_signal_handler_disconnect (combo_box->priv->model,
2072                                    combo_box->priv->inserted_id);
2073       g_signal_handler_disconnect (combo_box->priv->model,
2074                                    combo_box->priv->deleted_id);
2075       g_signal_handler_disconnect (combo_box->priv->model,
2076                                    combo_box->priv->reordered_id);
2077       g_signal_handler_disconnect (combo_box->priv->model,
2078                                    combo_box->priv->changed_id);
2079     }
2080
2081   /* menu mode */
2082   if (!combo_box->priv->tree_view)
2083     {
2084       if (combo_box->priv->popup_widget)
2085         gtk_container_foreach (GTK_CONTAINER (combo_box->priv->popup_widget),
2086                                (GtkCallback)gtk_widget_destroy, NULL);
2087     }
2088
2089   if (combo_box->priv->model)
2090     {
2091       g_object_unref (combo_box->priv->model);
2092       combo_box->priv->model = NULL;
2093     }
2094
2095   if (combo_box->priv->active_row)
2096     {
2097       gtk_tree_row_reference_free (combo_box->priv->active_row);
2098       combo_box->priv->active_row = NULL;
2099     }
2100
2101   if (combo_box->priv->cell_view)
2102     gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (combo_box->priv->cell_view), NULL);
2103 }
2104
2105 static void
2106 gtk_combo_box_forall (GtkContainer *container,
2107                       gboolean      include_internals,
2108                       GtkCallback   callback,
2109                       gpointer      callback_data)
2110 {
2111   GtkComboBox *combo_box = GTK_COMBO_BOX (container);
2112
2113   if (include_internals)
2114     {
2115       if (combo_box->priv->button)
2116         (* callback) (combo_box->priv->button, callback_data);
2117       if (combo_box->priv->cell_view_frame)
2118         (* callback) (combo_box->priv->cell_view_frame, callback_data);
2119     }
2120
2121   if (GTK_BIN (container)->child)
2122     (* callback) (GTK_BIN (container)->child, callback_data);
2123 }
2124
2125 static void 
2126 gtk_combo_box_child_show (GtkWidget *widget,
2127                           gpointer   user_data) 
2128 {
2129   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
2130
2131   g_signal_emit_by_name (combo_box, "popup-show", NULL, NULL);    
2132 }
2133
2134 static void 
2135 gtk_combo_box_child_hide (GtkWidget *widget,
2136                           gpointer   user_data)
2137 {
2138   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);  
2139
2140   g_signal_emit_by_name (combo_box, "popup-hide", NULL, NULL);
2141 }
2142
2143 static gboolean
2144 gtk_combo_box_expose_event (GtkWidget      *widget,
2145                             GdkEventExpose *event)
2146 {
2147   GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
2148
2149   if (!combo_box->priv->tree_view)
2150     {
2151       gtk_container_propagate_expose (GTK_CONTAINER (widget),
2152                                       combo_box->priv->button, event);
2153     }
2154   else
2155     {
2156       gtk_container_propagate_expose (GTK_CONTAINER (widget),
2157                                       combo_box->priv->button, event);
2158
2159       if (combo_box->priv->cell_view_frame)
2160         gtk_container_propagate_expose (GTK_CONTAINER (widget),
2161                                         combo_box->priv->cell_view_frame, event);
2162     }
2163
2164   gtk_container_propagate_expose (GTK_CONTAINER (widget),
2165                                   GTK_BIN (widget)->child, event);
2166
2167   return FALSE;
2168 }
2169
2170 typedef struct {
2171   GtkComboBox *combo;
2172   GtkTreePath *path;
2173   GtkTreeIter iter;
2174   gboolean found;
2175   gboolean set;
2176   gboolean visible;
2177 } SearchData;
2178
2179 static gboolean
2180 path_visible (GtkTreeView *view,
2181               GtkTreePath *path)
2182 {
2183   GtkRBTree *tree;
2184   GtkRBNode *node;
2185   
2186   /* Note that we rely on the fact that collapsed rows don't have nodes 
2187    */
2188   return _gtk_tree_view_find_node (view, path, &tree, &node);
2189 }
2190
2191 static gboolean
2192 tree_next_func (GtkTreeModel *model,
2193                 GtkTreePath  *path,
2194                 GtkTreeIter  *iter,
2195                 gpointer      data)
2196 {
2197   SearchData *search_data = (SearchData *)data;
2198
2199   if (search_data->found) 
2200     {
2201       if (!tree_column_row_is_sensitive (search_data->combo, iter))
2202         return FALSE;
2203       
2204       if (search_data->visible &&
2205           !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
2206         return FALSE;
2207
2208       search_data->set = TRUE;
2209       search_data->iter = *iter;
2210
2211       return TRUE;
2212     }
2213  
2214   if (gtk_tree_path_compare (path, search_data->path) == 0)
2215     search_data->found = TRUE;
2216   
2217   return FALSE;
2218 }
2219
2220 static gboolean
2221 tree_next (GtkComboBox  *combo,
2222            GtkTreeModel *model,
2223            GtkTreeIter  *iter,
2224            GtkTreeIter  *next,
2225            gboolean      visible)
2226 {
2227   SearchData search_data;
2228
2229   search_data.combo = combo;
2230   search_data.path = gtk_tree_model_get_path (model, iter);
2231   search_data.visible = visible;
2232   search_data.found = FALSE;
2233   search_data.set = FALSE;
2234
2235   gtk_tree_model_foreach (model, tree_next_func, &search_data);
2236   
2237   *next = search_data.iter;
2238
2239   gtk_tree_path_free (search_data.path);
2240
2241   return search_data.set;
2242 }
2243
2244 static gboolean
2245 tree_prev_func (GtkTreeModel *model,
2246                 GtkTreePath  *path,
2247                 GtkTreeIter  *iter,
2248                 gpointer      data)
2249 {
2250   SearchData *search_data = (SearchData *)data;
2251
2252   if (gtk_tree_path_compare (path, search_data->path) == 0)
2253     {
2254       search_data->found = TRUE;
2255       return TRUE;
2256     }
2257   
2258   if (!tree_column_row_is_sensitive (search_data->combo, iter))
2259     return FALSE;
2260       
2261   if (search_data->visible &&
2262       !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
2263     return FALSE; 
2264   
2265   search_data->set = TRUE;
2266   search_data->iter = *iter;
2267   
2268   return FALSE; 
2269 }
2270
2271 static gboolean
2272 tree_prev (GtkComboBox  *combo,
2273            GtkTreeModel *model,
2274            GtkTreeIter  *iter,
2275            GtkTreeIter  *prev,
2276            gboolean      visible)
2277 {
2278   SearchData search_data;
2279
2280   search_data.combo = combo;
2281   search_data.path = gtk_tree_model_get_path (model, iter);
2282   search_data.visible = visible;
2283   search_data.found = FALSE;
2284   search_data.set = FALSE;
2285
2286   gtk_tree_model_foreach (model, tree_prev_func, &search_data);
2287   
2288   *prev = search_data.iter;
2289
2290   gtk_tree_path_free (search_data.path);
2291
2292   return search_data.set;
2293 }
2294
2295 static gboolean
2296 tree_last_func (GtkTreeModel *model,
2297                 GtkTreePath  *path,
2298                 GtkTreeIter  *iter,
2299                 gpointer      data)
2300 {
2301   SearchData *search_data = (SearchData *)data;
2302
2303   if (!tree_column_row_is_sensitive (search_data->combo, iter))
2304     return FALSE;
2305       
2306   /* Note that we rely on the fact that collapsed rows don't have nodes 
2307    */
2308   if (search_data->visible &&
2309       !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
2310     return FALSE; 
2311   
2312   search_data->set = TRUE;
2313   search_data->iter = *iter;
2314   
2315   return FALSE; 
2316 }
2317
2318 static gboolean
2319 tree_last (GtkComboBox  *combo,
2320            GtkTreeModel *model,
2321            GtkTreeIter  *last,
2322            gboolean      visible)
2323 {
2324   SearchData search_data;
2325
2326   search_data.combo = combo;
2327   search_data.visible = visible;
2328   search_data.set = FALSE;
2329
2330   gtk_tree_model_foreach (model, tree_last_func, &search_data);
2331   
2332   *last = search_data.iter;
2333
2334   return search_data.set;  
2335 }
2336
2337
2338 static gboolean
2339 tree_first_func (GtkTreeModel *model,
2340                  GtkTreePath  *path,
2341                  GtkTreeIter  *iter,
2342                  gpointer      data)
2343 {
2344   SearchData *search_data = (SearchData *)data;
2345
2346   if (!tree_column_row_is_sensitive (search_data->combo, iter))
2347     return FALSE;
2348   
2349   if (search_data->visible &&
2350       !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
2351     return FALSE;
2352   
2353   search_data->set = TRUE;
2354   search_data->iter = *iter;
2355   
2356   return TRUE;
2357 }
2358
2359 static gboolean
2360 tree_first (GtkComboBox  *combo,
2361             GtkTreeModel *model,
2362             GtkTreeIter  *first,
2363             gboolean      visible)
2364 {
2365   SearchData search_data;
2366   
2367   search_data.combo = combo;
2368   search_data.visible = visible;
2369   search_data.set = FALSE;
2370
2371   gtk_tree_model_foreach (model, tree_first_func, &search_data);
2372   
2373   *first = search_data.iter;
2374
2375   return search_data.set;  
2376 }
2377
2378 static gboolean
2379 gtk_combo_box_scroll_event (GtkWidget          *widget,
2380                             GdkEventScroll     *event)
2381 {
2382   GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
2383   gboolean found;
2384   GtkTreeIter iter;
2385   GtkTreeIter new_iter;
2386
2387   if (!gtk_combo_box_get_active_iter (combo_box, &iter))
2388     return TRUE;
2389   
2390   if (event->direction == GDK_SCROLL_UP)
2391     found = tree_prev (combo_box, combo_box->priv->model, 
2392                        &iter, &new_iter, FALSE);
2393   else
2394     found = tree_next (combo_box, combo_box->priv->model, 
2395                        &iter, &new_iter, FALSE);
2396   
2397   if (found)
2398     gtk_combo_box_set_active_iter (combo_box, &new_iter);
2399
2400   return TRUE;
2401 }
2402
2403 /*
2404  * menu style
2405  */
2406
2407 static void
2408 gtk_combo_box_sync_cells (GtkComboBox   *combo_box,
2409                           GtkCellLayout *cell_layout)
2410 {
2411   GSList *k;
2412
2413   for (k = combo_box->priv->cells; k; k = k->next)
2414     {
2415       GSList *j;
2416       ComboCellInfo *info = (ComboCellInfo *)k->data;
2417
2418       if (info->pack == GTK_PACK_START)
2419         gtk_cell_layout_pack_start (cell_layout,
2420                                     info->cell, info->expand);
2421       else if (info->pack == GTK_PACK_END)
2422         gtk_cell_layout_pack_end (cell_layout,
2423                                   info->cell, info->expand);
2424
2425       gtk_cell_layout_set_cell_data_func (cell_layout,
2426                                           info->cell,
2427                                           combo_cell_data_func, info, NULL);
2428
2429       for (j = info->attributes; j; j = j->next->next)
2430         {
2431           gtk_cell_layout_add_attribute (cell_layout,
2432                                          info->cell,
2433                                          j->data,
2434                                          GPOINTER_TO_INT (j->next->data));
2435         }
2436     }
2437 }
2438
2439 static void
2440 gtk_combo_box_menu_setup (GtkComboBox *combo_box,
2441                           gboolean     add_children)
2442 {
2443   GtkWidget *menu;
2444
2445   if (combo_box->priv->cell_view)
2446     {
2447       combo_box->priv->button = gtk_toggle_button_new ();
2448       gtk_button_set_focus_on_click (GTK_BUTTON (combo_box->priv->button),
2449                                      combo_box->priv->focus_on_click);
2450
2451       g_signal_connect (combo_box->priv->button, "toggled",
2452                         G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
2453       g_signal_connect_after (combo_box->priv->button, 
2454                               "key_press_event",
2455                               G_CALLBACK (gtk_combo_box_key_press), combo_box);
2456       gtk_widget_set_parent (combo_box->priv->button,
2457                              GTK_BIN (combo_box)->child->parent);
2458
2459       combo_box->priv->box = gtk_hbox_new (FALSE, 0);
2460       gtk_container_add (GTK_CONTAINER (combo_box->priv->button), 
2461                          combo_box->priv->box);
2462
2463       combo_box->priv->separator = gtk_vseparator_new ();
2464       gtk_container_add (GTK_CONTAINER (combo_box->priv->box), 
2465                          combo_box->priv->separator);
2466
2467       combo_box->priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
2468       gtk_container_add (GTK_CONTAINER (combo_box->priv->box), 
2469                          combo_box->priv->arrow);
2470
2471       gtk_widget_show_all (combo_box->priv->button);
2472     }
2473   else
2474     {
2475       combo_box->priv->button = gtk_toggle_button_new ();
2476       gtk_button_set_focus_on_click (GTK_BUTTON (combo_box->priv->button),
2477                                      combo_box->priv->focus_on_click);
2478
2479       g_signal_connect (combo_box->priv->button, "toggled",
2480                         G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
2481       g_signal_connect_after (combo_box, "key_press_event",
2482                               G_CALLBACK (gtk_combo_box_key_press), combo_box);
2483       gtk_widget_set_parent (combo_box->priv->button,
2484                              GTK_BIN (combo_box)->child->parent);
2485
2486       combo_box->priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
2487       gtk_container_add (GTK_CONTAINER (combo_box->priv->button),
2488                          combo_box->priv->arrow);
2489       gtk_widget_show_all (combo_box->priv->button);
2490     }
2491
2492   g_signal_connect (combo_box->priv->button, "button_press_event",
2493                     G_CALLBACK (gtk_combo_box_menu_button_press),
2494                     combo_box);
2495   g_signal_connect (combo_box->priv->button, "state_changed",
2496                     G_CALLBACK (gtk_combo_box_button_state_changed), 
2497                     combo_box);
2498
2499   /* create our funky menu */
2500   menu = gtk_menu_new ();
2501   g_signal_connect (menu, "key_press_event",
2502                     G_CALLBACK (gtk_combo_box_menu_key_press), combo_box);
2503   gtk_combo_box_set_popup_widget (combo_box, menu);
2504
2505   /* add items */
2506   if (add_children)
2507     gtk_combo_box_menu_fill (combo_box);
2508
2509   /* the column is needed in tree_column_row_is_sensitive() */
2510   combo_box->priv->column = gtk_tree_view_column_new ();
2511   g_object_ref (combo_box->priv->column);
2512   gtk_object_sink (GTK_OBJECT (combo_box->priv->column));
2513   gtk_combo_box_sync_cells (combo_box, 
2514                             GTK_CELL_LAYOUT (combo_box->priv->column));
2515 }
2516
2517 static void
2518 gtk_combo_box_menu_fill (GtkComboBox *combo_box)
2519 {
2520   GtkWidget *menu;
2521
2522   if (!combo_box->priv->model)
2523     return;
2524
2525   menu = combo_box->priv->popup_widget;
2526
2527   if (combo_box->priv->add_tearoffs)
2528     {
2529       GtkWidget *tearoff = gtk_tearoff_menu_item_new ();
2530
2531       gtk_widget_show (tearoff);
2532       
2533       if (combo_box->priv->wrap_width)
2534         gtk_menu_attach (GTK_MENU (menu), tearoff, 
2535                          0, combo_box->priv->wrap_width, 0, 1);
2536       else
2537         gtk_menu_shell_append (GTK_MENU_SHELL (menu), tearoff);
2538     }
2539   
2540   gtk_combo_box_menu_fill_level (combo_box, menu, NULL);
2541 }
2542
2543 static GtkWidget *
2544 gtk_cell_view_menu_item_new (GtkComboBox  *combo_box,
2545                              GtkTreeModel *model,
2546                              GtkTreeIter  *iter)
2547 {
2548   GtkWidget *cell_view; 
2549   GtkWidget *item;
2550   GtkTreePath *path;
2551   GtkRequisition req;
2552
2553   cell_view = gtk_cell_view_new ();
2554   gtk_cell_view_set_model (GTK_CELL_VIEW (cell_view), model);     
2555   path = gtk_tree_model_get_path (model, iter);
2556   gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (cell_view), path);
2557   gtk_tree_path_free (path);
2558
2559   gtk_combo_box_sync_cells (combo_box, GTK_CELL_LAYOUT (cell_view));
2560   gtk_widget_size_request (cell_view, &req);
2561   gtk_widget_show (cell_view);
2562
2563   item = gtk_menu_item_new ();
2564   gtk_container_add (GTK_CONTAINER (item), cell_view);
2565   
2566   return item;
2567 }
2568  
2569 static void
2570 gtk_combo_box_menu_fill_level (GtkComboBox *combo_box,
2571                                GtkWidget   *menu,
2572                                GtkTreeIter *parent)
2573 {
2574   GtkTreeModel *model = combo_box->priv->model;
2575   GtkWidget *item, *submenu, *subitem, *separator;
2576   GtkTreeIter iter;
2577   gboolean is_separator;
2578   gint i, n_children;
2579   GtkWidget *last;
2580   GtkTreePath *path;
2581   
2582   n_children = gtk_tree_model_iter_n_children (model, parent);
2583   
2584   last = NULL;
2585   for (i = 0; i < n_children; i++)
2586     {
2587       gtk_tree_model_iter_nth_child (model, &iter, parent, i);
2588
2589       if (combo_box->priv->row_separator_func)
2590         is_separator = (*combo_box->priv->row_separator_func) (combo_box->priv->model, &iter,
2591                                                                combo_box->priv->row_separator_data);
2592       else
2593         is_separator = FALSE;
2594       
2595       if (is_separator)
2596         {
2597           item = gtk_separator_menu_item_new ();
2598           path = gtk_tree_model_get_path (model, &iter);
2599           g_object_set_data_full (G_OBJECT (item),
2600                                   I_("gtk-combo-box-item-path"),
2601                                   gtk_tree_row_reference_new (model, path),
2602                                   (GDestroyNotify)gtk_tree_row_reference_free);
2603           gtk_tree_path_free (path);
2604         }
2605       else
2606         {
2607           item = gtk_cell_view_menu_item_new (combo_box, model, &iter);
2608           if (gtk_tree_model_iter_has_child (model, &iter))
2609             {
2610               submenu = gtk_menu_new ();
2611               gtk_widget_show (submenu);
2612               gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu);
2613               
2614               /* Ugly - since menus can only activate leafs, we have to
2615                * duplicate the item inside the submenu.
2616                */
2617               subitem = gtk_cell_view_menu_item_new (combo_box, model, &iter);
2618               separator = gtk_separator_menu_item_new ();
2619               gtk_widget_show (subitem);
2620               gtk_widget_show (separator);
2621               g_signal_connect (subitem, "activate",
2622                                 G_CALLBACK (gtk_combo_box_menu_item_activate),
2623                                 combo_box);
2624               gtk_menu_shell_append (GTK_MENU_SHELL (submenu), subitem);
2625               gtk_menu_shell_append (GTK_MENU_SHELL (submenu), separator);
2626               
2627               gtk_combo_box_menu_fill_level (combo_box, submenu, &iter);
2628             }
2629           else
2630             g_signal_connect (item, "activate",
2631                               G_CALLBACK (gtk_combo_box_menu_item_activate),
2632                               combo_box);
2633         }
2634       
2635       gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2636       if (combo_box->priv->wrap_width && menu == combo_box->priv->popup_widget)
2637         gtk_combo_box_relayout_item (combo_box, item, &iter, last);
2638       gtk_widget_show (item);
2639       
2640       last = item;
2641     }
2642 }
2643
2644 static void
2645 gtk_combo_box_menu_destroy (GtkComboBox *combo_box)
2646 {
2647   g_signal_handlers_disconnect_matched (combo_box->priv->button,
2648                                         G_SIGNAL_MATCH_DATA,
2649                                         0, 0, NULL,
2650                                         gtk_combo_box_menu_button_press, NULL);
2651   g_signal_handlers_disconnect_matched (combo_box->priv->button,
2652                                         G_SIGNAL_MATCH_DATA,
2653                                         0, 0, NULL,
2654                                         gtk_combo_box_button_state_changed, combo_box);
2655
2656   /* unparent will remove our latest ref */
2657   gtk_widget_unparent (combo_box->priv->button);
2658   
2659   combo_box->priv->box = NULL;
2660   combo_box->priv->button = NULL;
2661   combo_box->priv->arrow = NULL;
2662   combo_box->priv->separator = NULL;
2663
2664   g_object_unref (combo_box->priv->column);
2665   combo_box->priv->column = NULL;
2666
2667   /* changing the popup window will unref the menu and the children */
2668 }
2669
2670 /*
2671  * grid
2672  */
2673
2674 static gboolean
2675 menu_occupied (GtkMenu   *menu,
2676                guint      left_attach,
2677                guint      right_attach,
2678                guint      top_attach,
2679                guint      bottom_attach)
2680 {
2681   GList *i;
2682
2683   for (i = GTK_MENU_SHELL (menu)->children; i; i = i->next)
2684     {
2685       guint l, r, b, t;
2686
2687       gtk_container_child_get (GTK_CONTAINER (menu), 
2688                                i->data,
2689                                "left-attach", &l,
2690                                "right-attach", &r,
2691                                "bottom-attach", &b,
2692                                "top-attach", &t,
2693                                NULL);
2694
2695       /* look if this item intersects with the given coordinates */
2696       if (right_attach > l && left_attach < r && bottom_attach > t && top_attach < b)
2697         return TRUE;
2698     }
2699
2700   return FALSE;
2701 }
2702
2703 static void
2704 gtk_combo_box_relayout_item (GtkComboBox *combo_box,
2705                              GtkWidget   *item,
2706                              GtkTreeIter *iter,
2707                              GtkWidget   *last)
2708 {
2709   gint current_col = 0, current_row = 0;
2710   gint rows = 1, cols = 1;
2711   GtkWidget *menu = combo_box->priv->popup_widget;
2712
2713   if (!GTK_IS_MENU_SHELL (menu))
2714     return;
2715   
2716   if (combo_box->priv->col_column == -1 &&
2717       combo_box->priv->row_column == -1 &&
2718       last)
2719     {
2720       gtk_container_child_get (GTK_CONTAINER (menu), 
2721                                last,
2722                                "right_attach", &current_col,
2723                                "top_attach", &current_row,
2724                                NULL);
2725       if (current_col + cols > combo_box->priv->wrap_width)
2726         {
2727           current_col = 0;
2728           current_row++;
2729         }
2730     }
2731   else
2732     {
2733       if (combo_box->priv->col_column != -1)
2734         gtk_tree_model_get (combo_box->priv->model, iter,
2735                             combo_box->priv->col_column, &cols,
2736                             -1);
2737       if (combo_box->priv->row_column != -1)
2738         gtk_tree_model_get (combo_box->priv->model, iter,
2739                             combo_box->priv->row_column, &rows,
2740                             -1);
2741
2742       while (1)
2743         {
2744           if (current_col + cols > combo_box->priv->wrap_width)
2745             {
2746               current_col = 0;
2747               current_row++;
2748             }
2749           
2750           if (!menu_occupied (GTK_MENU (menu), 
2751                               current_col, current_col + cols,
2752                               current_row, current_row + rows))
2753             break;
2754           
2755           current_col++;
2756         }
2757     }
2758
2759   /* set attach props */
2760   gtk_menu_attach (GTK_MENU (menu), item,
2761                    current_col, current_col + cols,
2762                    current_row, current_row + rows);
2763 }
2764
2765 static void
2766 gtk_combo_box_relayout (GtkComboBox *combo_box)
2767 {
2768   GList *list, *j;
2769   GtkWidget *menu;
2770
2771   menu = combo_box->priv->popup_widget;
2772   
2773   /* do nothing unless we are in menu style and realized */
2774   if (combo_box->priv->tree_view || !GTK_IS_MENU_SHELL (menu))
2775     return;
2776   
2777   list = gtk_container_get_children (GTK_CONTAINER (menu));
2778   
2779   for (j = g_list_last (list); j; j = j->prev)
2780     gtk_container_remove (GTK_CONTAINER (menu), j->data);
2781   
2782   gtk_combo_box_menu_fill (combo_box);
2783
2784   g_list_free (list);
2785 }
2786
2787 /* callbacks */
2788 static gboolean
2789 gtk_combo_box_menu_button_press (GtkWidget      *widget,
2790                                  GdkEventButton *event,
2791                                  gpointer        user_data)
2792 {
2793   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
2794
2795   if (GTK_IS_MENU (combo_box->priv->popup_widget) &&
2796       event->type == GDK_BUTTON_PRESS && event->button == 1)
2797     {
2798       if (combo_box->priv->focus_on_click && 
2799           !GTK_WIDGET_HAS_FOCUS (combo_box->priv->button))
2800         gtk_widget_grab_focus (combo_box->priv->button);
2801
2802       gtk_combo_box_menu_popup (combo_box, event->button, event->time);
2803
2804       return TRUE;
2805     }
2806
2807   return FALSE;
2808 }
2809
2810 static void
2811 gtk_combo_box_menu_item_activate (GtkWidget *item,
2812                                   gpointer   user_data)
2813 {
2814   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
2815   GtkWidget *cell_view;
2816   GtkTreePath *path;
2817   GtkTreeIter iter;
2818
2819   cell_view = GTK_BIN (item)->child;
2820
2821   g_return_if_fail (GTK_IS_CELL_VIEW (cell_view));
2822
2823   path = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (cell_view));
2824
2825   if (gtk_tree_model_get_iter (combo_box->priv->model, &iter, path))
2826     gtk_combo_box_set_active_iter (combo_box, &iter);
2827
2828   gtk_tree_path_free (path);
2829
2830   combo_box->priv->editing_canceled = FALSE;
2831 }
2832
2833 static void
2834 gtk_combo_box_model_row_inserted (GtkTreeModel     *model,
2835                                   GtkTreePath      *path,
2836                                   GtkTreeIter      *iter,
2837                                   gpointer          user_data)
2838 {
2839   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
2840
2841   if (combo_box->priv->tree_view)
2842     gtk_combo_box_list_popup_resize (combo_box);
2843   else
2844     gtk_combo_box_menu_row_inserted (model, path, iter, user_data);
2845 }
2846
2847 static void
2848 gtk_combo_box_model_row_deleted (GtkTreeModel     *model,
2849                                  GtkTreePath      *path,
2850                                  gpointer          user_data)
2851 {
2852   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
2853
2854   if (combo_box->priv->cell_view)
2855     {
2856       if (!gtk_tree_row_reference_valid (combo_box->priv->active_row))
2857         gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (combo_box->priv->cell_view), NULL);
2858     }
2859   
2860   if (combo_box->priv->tree_view)
2861     gtk_combo_box_list_popup_resize (combo_box);
2862   else
2863     gtk_combo_box_menu_row_deleted (model, path, user_data);  
2864 }
2865
2866 static void
2867 gtk_combo_box_model_rows_reordered (GtkTreeModel    *model,
2868                                     GtkTreePath     *path,
2869                                     GtkTreeIter     *iter,
2870                                     gint            *new_order,
2871                                     gpointer         user_data)
2872 {
2873   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
2874
2875   gtk_tree_row_reference_reordered (G_OBJECT (user_data), path, iter, new_order);
2876
2877   if (!combo_box->priv->tree_view)
2878     gtk_combo_box_menu_rows_reordered (model, path, iter, new_order, user_data);
2879 }
2880                                                     
2881 static void
2882 gtk_combo_box_model_row_changed (GtkTreeModel     *model,
2883                                  GtkTreePath      *path,
2884                                  GtkTreeIter      *iter,
2885                                  gpointer          user_data)
2886 {
2887   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
2888   GtkTreePath *active_path;
2889
2890   /* FIXME this belongs to GtkCellView */
2891   if (gtk_tree_row_reference_valid (combo_box->priv->active_row))
2892     {
2893       active_path = gtk_tree_row_reference_get_path (combo_box->priv->active_row);
2894       if (gtk_tree_path_compare (path, active_path) == 0 &&
2895           combo_box->priv->cell_view)
2896         gtk_widget_queue_resize (GTK_WIDGET (combo_box->priv->cell_view));
2897       gtk_tree_path_free (active_path);
2898     }
2899       
2900   if (combo_box->priv->tree_view)
2901     gtk_combo_box_list_row_changed (model, path, iter, user_data);
2902   else
2903     gtk_combo_box_menu_row_changed (model, path, iter, user_data);
2904 }
2905
2906 static gboolean
2907 list_popup_resize_idle (gpointer user_data)
2908 {
2909   GtkComboBox *combo_box;
2910   gint x, y, width, height;
2911
2912   GDK_THREADS_ENTER ();
2913
2914   combo_box = GTK_COMBO_BOX (user_data);
2915
2916   if (combo_box->priv->tree_view &&
2917       GTK_WIDGET_MAPPED (combo_box->priv->popup_window))
2918     {
2919       gtk_combo_box_list_position (combo_box, &x, &y, &width, &height);
2920   
2921       gtk_widget_set_size_request (combo_box->priv->popup_window, width, height);
2922       gtk_window_move (GTK_WINDOW (combo_box->priv->popup_window), x, y);
2923     }
2924
2925   combo_box->priv->resize_idle_id = 0;
2926
2927   GDK_THREADS_LEAVE ();
2928
2929   return FALSE;
2930 }
2931
2932 static void
2933 gtk_combo_box_list_popup_resize (GtkComboBox *combo_box)
2934 {
2935   if (!combo_box->priv->resize_idle_id)
2936     combo_box->priv->resize_idle_id = 
2937       g_idle_add (list_popup_resize_idle, combo_box);
2938 }
2939
2940 static void
2941 gtk_combo_box_model_row_expanded (GtkTreeModel     *model,
2942                                   GtkTreePath      *path,
2943                                   GtkTreeIter      *iter,
2944                                   gpointer          user_data)
2945 {
2946   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
2947   
2948   gtk_combo_box_list_popup_resize (combo_box);
2949 }
2950
2951
2952 static GtkWidget *
2953 find_menu_by_path (GtkWidget   *menu,
2954                    GtkTreePath *path,
2955                    gboolean     skip_first)
2956 {
2957   GList *i, *list;
2958   GtkWidget *item;
2959   GtkWidget *submenu;    
2960   GtkTreeRowReference *mref;
2961   GtkTreePath *mpath;
2962   gboolean skip;
2963
2964   list = gtk_container_get_children (GTK_CONTAINER (menu));
2965   skip = skip_first;
2966   item = NULL;
2967   for (i = list; i; i = i->next)
2968     {
2969       if (GTK_IS_SEPARATOR_MENU_ITEM (i->data))
2970         {
2971           mref = g_object_get_data (G_OBJECT (i->data), "gtk-combo-box-item-path");
2972           if (!mref)
2973             continue;
2974           else if (!gtk_tree_row_reference_valid (mref))
2975             mpath = NULL;
2976           else
2977             mpath = gtk_tree_row_reference_get_path (mref);
2978         }
2979       else if (GTK_IS_CELL_VIEW (GTK_BIN (i->data)->child))
2980         {
2981           if (skip)
2982             {
2983               skip = FALSE;
2984               continue;
2985             }
2986
2987           mpath = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (GTK_BIN (i->data)->child));
2988         }
2989       else 
2990         continue;
2991
2992       /* this case is necessary, since the row reference of
2993        * the cell view may already be updated after a deletion
2994        */
2995       if (!mpath)
2996         {
2997           item = i->data;
2998           break;
2999         }
3000       if (gtk_tree_path_compare (mpath, path) == 0)
3001         {
3002           gtk_tree_path_free (mpath);
3003           item = i->data;
3004           break;
3005         }
3006       if (gtk_tree_path_is_ancestor (mpath, path))
3007         {
3008           submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
3009           if (submenu != NULL)
3010             {
3011               gtk_tree_path_free (mpath);
3012               item = find_menu_by_path (submenu, path, TRUE);
3013               break;
3014             }
3015         }
3016       gtk_tree_path_free (mpath);
3017     }
3018   
3019   g_list_free (list);  
3020
3021   return item;
3022 }
3023
3024 #if 0
3025 static void
3026 dump_menu_tree (GtkWidget   *menu, 
3027                 gint         level)
3028 {
3029   GList *i, *list;
3030   GtkWidget *submenu;    
3031   GtkTreePath *path;
3032
3033   list = gtk_container_get_children (GTK_CONTAINER (menu));
3034   for (i = list; i; i = i->next)
3035     {
3036       if (GTK_IS_CELL_VIEW (GTK_BIN (i->data)->child))
3037         {
3038           path = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (GTK_BIN (i->data)->child));
3039           g_print ("%*s%s\n", 2 * level, " ", gtk_tree_path_to_string (path));
3040           gtk_tree_path_free (path);
3041
3042           submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
3043           if (submenu != NULL)
3044             dump_menu_tree (submenu, level + 1);
3045         }
3046     }
3047   
3048   g_list_free (list);  
3049 }
3050 #endif
3051
3052 static void
3053 gtk_combo_box_menu_row_inserted (GtkTreeModel *model,
3054                                  GtkTreePath  *path,
3055                                  GtkTreeIter  *iter,
3056                                  gpointer      user_data)
3057 {
3058   GtkWidget *parent;
3059   GtkWidget *item, *menu, *separator;
3060   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3061   GtkTreePath *ppath;
3062   GtkTreeIter piter;
3063   gint depth, pos;
3064   gboolean is_separator;
3065
3066   if (!combo_box->priv->popup_widget)
3067     return;
3068
3069   depth = gtk_tree_path_get_depth (path);
3070   pos = gtk_tree_path_get_indices (path)[depth - 1];
3071   if (depth > 1)
3072     {
3073       ppath = gtk_tree_path_copy (path);
3074       gtk_tree_path_up (ppath);
3075       parent = find_menu_by_path (combo_box->priv->popup_widget, ppath, FALSE);
3076       gtk_tree_path_free (ppath);
3077
3078       menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (parent));
3079       if (!menu)
3080         {
3081           menu = gtk_menu_new ();
3082           gtk_widget_show (menu);
3083           gtk_menu_item_set_submenu (GTK_MENU_ITEM (parent), menu);
3084           
3085           /* Ugly - since menus can only activate leaves, we have to
3086            * duplicate the item inside the submenu.
3087            */
3088           gtk_tree_model_iter_parent (model, &piter, iter);
3089           item = gtk_cell_view_menu_item_new (combo_box, model, &piter);
3090           separator = gtk_separator_menu_item_new ();
3091           g_signal_connect (item, "activate",
3092                             G_CALLBACK (gtk_combo_box_menu_item_activate),
3093                             combo_box);
3094           gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3095           gtk_menu_shell_append (GTK_MENU_SHELL (menu), separator);
3096           if (cell_view_is_sensitive (GTK_CELL_VIEW (GTK_BIN (item)->child)))
3097             {
3098               gtk_widget_show (item);
3099               gtk_widget_show (separator);
3100             }
3101         }
3102       pos += 2;
3103     }
3104   else
3105     {
3106       menu = combo_box->priv->popup_widget;
3107       if (combo_box->priv->add_tearoffs)
3108         pos += 1;
3109     }
3110   
3111   if (combo_box->priv->row_separator_func)
3112     is_separator = (*combo_box->priv->row_separator_func) (model, iter,
3113                                                            combo_box->priv->row_separator_data);
3114   else
3115     is_separator = FALSE;
3116
3117   if (is_separator)
3118     {
3119       item = gtk_separator_menu_item_new ();
3120       g_object_set_data_full (G_OBJECT (item),
3121                               I_("gtk-combo-box-item-path"),
3122                               gtk_tree_row_reference_new (model, path),
3123                               (GDestroyNotify)gtk_tree_row_reference_free);
3124     }
3125   else
3126     {
3127       item = gtk_cell_view_menu_item_new (combo_box, model, iter);
3128       
3129       g_signal_connect (item, "activate",
3130                         G_CALLBACK (gtk_combo_box_menu_item_activate),
3131                         combo_box);
3132     }
3133
3134   gtk_widget_show (item);
3135   gtk_menu_shell_insert (GTK_MENU_SHELL (menu), item, pos);
3136 }
3137
3138 static void
3139 gtk_combo_box_menu_row_deleted (GtkTreeModel *model,
3140                                 GtkTreePath  *path,
3141                                 gpointer      user_data)
3142 {
3143   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3144   GtkWidget *menu;
3145   GtkWidget *item;
3146
3147   if (!combo_box->priv->popup_widget)
3148     return;
3149
3150   item = find_menu_by_path (combo_box->priv->popup_widget, path, FALSE);
3151   menu = gtk_widget_get_parent (item);
3152   gtk_container_remove (GTK_CONTAINER (menu), item);
3153 }
3154
3155 static void
3156 gtk_combo_box_menu_rows_reordered  (GtkTreeModel     *model,
3157                                     GtkTreePath      *path,
3158                                     GtkTreeIter      *iter,
3159                                     gint             *new_order,
3160                                     gpointer          user_data)
3161 {
3162   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3163
3164   gtk_combo_box_relayout (combo_box);
3165 }
3166                                     
3167 static void
3168 gtk_combo_box_menu_row_changed (GtkTreeModel *model,
3169                                 GtkTreePath  *path,
3170                                 GtkTreeIter  *iter,
3171                                 gpointer      user_data)
3172 {
3173   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3174   GtkWidget *item;
3175   gint width;
3176   gboolean is_separator;
3177
3178   if (!combo_box->priv->popup_widget)
3179     return;
3180
3181   item = find_menu_by_path (combo_box->priv->popup_widget, path, FALSE);
3182
3183   if (combo_box->priv->row_separator_func)
3184     is_separator = (*combo_box->priv->row_separator_func) (model, iter,
3185                                                            combo_box->priv->row_separator_data);
3186   else
3187     is_separator = FALSE;
3188
3189   if (is_separator != GTK_IS_SEPARATOR_MENU_ITEM (item))
3190     {
3191       gtk_combo_box_menu_row_deleted (model, path, combo_box);
3192       gtk_combo_box_menu_row_inserted (model, path, iter, combo_box);
3193     }
3194
3195   if (combo_box->priv->wrap_width
3196       && item->parent == combo_box->priv->popup_widget)
3197     {
3198       GtkWidget *pitem = NULL;
3199       GtkTreePath *prev;
3200
3201       prev = gtk_tree_path_copy (path);
3202
3203       if (gtk_tree_path_prev (prev))
3204         pitem = find_menu_by_path (combo_box->priv->popup_widget, prev, FALSE);
3205
3206       gtk_tree_path_free (prev);
3207
3208       /* unattach item so gtk_combo_box_relayout_item() won't spuriously
3209          move it */
3210       gtk_container_child_set (GTK_CONTAINER (combo_box->priv->popup_widget),
3211                                item, 
3212                                "left-attach", -1, 
3213                                "right-attach", -1,
3214                                "top-attach", -1, 
3215                                "bottom-attach", -1, 
3216                                NULL);
3217
3218       gtk_combo_box_relayout_item (combo_box, item, iter, pitem);
3219     }
3220
3221   width = gtk_combo_box_calc_requested_width (combo_box, path);
3222
3223   if (width > combo_box->priv->width)
3224     {
3225       if (combo_box->priv->cell_view)
3226         {
3227           gtk_widget_set_size_request (combo_box->priv->cell_view, width, -1);
3228           gtk_widget_queue_resize (combo_box->priv->cell_view);
3229         }
3230       combo_box->priv->width = width;
3231     }
3232 }
3233
3234 /*
3235  * list style
3236  */
3237
3238 static void
3239 gtk_combo_box_list_setup (GtkComboBox *combo_box)
3240 {
3241   GtkTreeSelection *sel;
3242
3243   combo_box->priv->button = gtk_toggle_button_new ();
3244   gtk_widget_set_parent (combo_box->priv->button,
3245                          GTK_BIN (combo_box)->child->parent);
3246   g_signal_connect (combo_box->priv->button, "button_press_event",
3247                     G_CALLBACK (gtk_combo_box_list_button_pressed), combo_box);
3248   g_signal_connect (combo_box->priv->button, "toggled",
3249                     G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
3250   g_signal_connect_after (combo_box, "key_press_event",
3251                           G_CALLBACK (gtk_combo_box_key_press), combo_box);
3252
3253   combo_box->priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3254   gtk_container_add (GTK_CONTAINER (combo_box->priv->button),
3255                      combo_box->priv->arrow);
3256   combo_box->priv->separator = NULL;
3257   gtk_widget_show_all (combo_box->priv->button);
3258
3259   if (combo_box->priv->cell_view)
3260     {
3261       gtk_cell_view_set_background_color (GTK_CELL_VIEW (combo_box->priv->cell_view), 
3262                                           &GTK_WIDGET (combo_box)->style->base[GTK_WIDGET_STATE (combo_box)]);
3263
3264       combo_box->priv->box = gtk_event_box_new ();
3265       gtk_event_box_set_visible_window (GTK_EVENT_BOX (combo_box->priv->box), 
3266                                         FALSE);
3267
3268       if (combo_box->priv->has_frame)
3269         {
3270           combo_box->priv->cell_view_frame = gtk_frame_new (NULL);
3271           gtk_frame_set_shadow_type (GTK_FRAME (combo_box->priv->cell_view_frame),
3272                                      GTK_SHADOW_IN);
3273         }
3274       else 
3275         {
3276           combo_box->priv->cell_view_frame = gtk_event_box_new ();
3277           gtk_event_box_set_visible_window (GTK_EVENT_BOX (combo_box->priv->cell_view_frame), 
3278                                             FALSE);
3279         }
3280       
3281       gtk_widget_set_parent (combo_box->priv->cell_view_frame,
3282                              GTK_BIN (combo_box)->child->parent);
3283       gtk_container_add (GTK_CONTAINER (combo_box->priv->cell_view_frame),
3284                          combo_box->priv->box);
3285       gtk_widget_show_all (combo_box->priv->cell_view_frame);
3286
3287       g_signal_connect (combo_box->priv->box, "button_press_event",
3288                         G_CALLBACK (gtk_combo_box_list_button_pressed), 
3289                         combo_box);
3290     }
3291
3292   combo_box->priv->tree_view = gtk_tree_view_new ();
3293   sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (combo_box->priv->tree_view));
3294   gtk_tree_selection_set_mode (sel, GTK_SELECTION_BROWSE);
3295   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (combo_box->priv->tree_view),
3296                                      FALSE);
3297   gtk_tree_view_set_hover_selection (GTK_TREE_VIEW (combo_box->priv->tree_view),
3298                                      TRUE);
3299   if (combo_box->priv->row_separator_func)
3300     gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (combo_box->priv->tree_view), 
3301                                           combo_box->priv->row_separator_func, 
3302                                           combo_box->priv->row_separator_data, 
3303                                           NULL);
3304   if (combo_box->priv->model)
3305     gtk_tree_view_set_model (GTK_TREE_VIEW (combo_box->priv->tree_view),
3306                              combo_box->priv->model);
3307     
3308   combo_box->priv->column = gtk_tree_view_column_new ();
3309   gtk_tree_view_append_column (GTK_TREE_VIEW (combo_box->priv->tree_view),
3310                                combo_box->priv->column);
3311
3312   /* sync up */
3313   gtk_combo_box_sync_cells (combo_box, 
3314                             GTK_CELL_LAYOUT (combo_box->priv->column));
3315
3316   if (gtk_tree_row_reference_valid (combo_box->priv->active_row))
3317     {
3318       GtkTreePath *path;
3319
3320       path = gtk_tree_row_reference_get_path (combo_box->priv->active_row);
3321       gtk_tree_view_set_cursor (GTK_TREE_VIEW (combo_box->priv->tree_view),
3322                                 path, NULL, FALSE);
3323       gtk_tree_path_free (path);
3324     }
3325
3326   /* set sample/popup widgets */
3327   gtk_combo_box_set_popup_widget (combo_box, combo_box->priv->tree_view);
3328
3329   g_signal_connect (combo_box->priv->tree_view, "key_press_event",
3330                     G_CALLBACK (gtk_combo_box_list_key_press),
3331                     combo_box);
3332   g_signal_connect (combo_box->priv->tree_view, "enter_notify_event",
3333                     G_CALLBACK (gtk_combo_box_list_enter_notify),
3334                     combo_box);
3335   g_signal_connect (combo_box->priv->tree_view, "row_expanded",
3336                     G_CALLBACK (gtk_combo_box_model_row_expanded),
3337                     combo_box);
3338   g_signal_connect (combo_box->priv->tree_view, "row_collapsed",
3339                     G_CALLBACK (gtk_combo_box_model_row_expanded),
3340                     combo_box);
3341   g_signal_connect (combo_box->priv->popup_window, "button_press_event",
3342                     G_CALLBACK (gtk_combo_box_list_button_pressed),
3343                     combo_box);
3344   g_signal_connect (combo_box->priv->popup_window, "button_release_event",
3345                     G_CALLBACK (gtk_combo_box_list_button_released),
3346                     combo_box);
3347
3348   gtk_widget_show (combo_box->priv->tree_view);
3349 }
3350
3351 static void
3352 gtk_combo_box_list_destroy (GtkComboBox *combo_box)
3353 {
3354   /* disconnect signals */
3355   g_signal_handlers_disconnect_matched (combo_box->priv->tree_view,
3356                                         G_SIGNAL_MATCH_DATA,
3357                                         0, 0, NULL, NULL, combo_box);
3358   g_signal_handlers_disconnect_matched (combo_box->priv->button,
3359                                         G_SIGNAL_MATCH_DATA,
3360                                         0, 0, NULL,
3361                                         gtk_combo_box_list_button_pressed,
3362                                         NULL);
3363   g_signal_handlers_disconnect_matched (combo_box->priv->popup_window,
3364                                         G_SIGNAL_MATCH_DATA,
3365                                         0, 0, NULL,
3366                                         gtk_combo_box_list_button_pressed,
3367                                         NULL);
3368   g_signal_handlers_disconnect_matched (combo_box->priv->popup_window,
3369                                         G_SIGNAL_MATCH_DATA,
3370                                         0, 0, NULL,
3371                                         gtk_combo_box_list_button_released,
3372                                         NULL);
3373
3374   g_signal_handlers_disconnect_matched (combo_box->priv->popup_window,
3375                                         G_SIGNAL_MATCH_DATA,
3376                                         0, 0, NULL, 
3377                                         gtk_combo_box_child_show,
3378                                         NULL);
3379
3380   g_signal_handlers_disconnect_matched (combo_box->priv->popup_window,
3381                                         G_SIGNAL_MATCH_DATA,
3382                                         0, 0, NULL, 
3383                                         gtk_combo_box_child_hide,
3384                                         NULL);
3385   
3386   if (combo_box->priv->box)
3387     g_signal_handlers_disconnect_matched (combo_box->priv->box,
3388                                           G_SIGNAL_MATCH_DATA,
3389                                           0, 0, NULL,
3390                                           gtk_combo_box_list_button_pressed,
3391                                           NULL);
3392
3393   /* destroy things (unparent will kill the latest ref from us)
3394    * last unref on button will destroy the arrow
3395    */
3396   gtk_widget_unparent (combo_box->priv->button);
3397   combo_box->priv->button = NULL;
3398   combo_box->priv->arrow = NULL;
3399
3400   if (combo_box->priv->cell_view)
3401     {
3402       g_object_set (combo_box->priv->cell_view,
3403                     "background-set", FALSE,
3404                     NULL);
3405     }
3406
3407   if (combo_box->priv->cell_view_frame)
3408     {
3409       gtk_widget_unparent (combo_box->priv->cell_view_frame);
3410       combo_box->priv->cell_view_frame = NULL;
3411       combo_box->priv->box = NULL;
3412     }
3413
3414   if (combo_box->priv->scroll_timer)
3415     {
3416       g_source_remove (combo_box->priv->scroll_timer);
3417       combo_box->priv->scroll_timer = 0;
3418     }
3419
3420   if (combo_box->priv->resize_idle_id)
3421     {
3422       g_source_remove (combo_box->priv->resize_idle_id);
3423       combo_box->priv->resize_idle_id = 0;
3424     }
3425
3426   gtk_widget_destroy (combo_box->priv->tree_view);
3427
3428   combo_box->priv->tree_view = NULL;
3429   combo_box->priv->popup_widget = NULL;
3430 }
3431
3432 /* callbacks */
3433
3434 static gboolean
3435 gtk_combo_box_list_button_pressed (GtkWidget      *widget,
3436                                    GdkEventButton *event,
3437                                    gpointer        data)
3438 {
3439   GtkComboBox *combo_box = GTK_COMBO_BOX (data);
3440
3441   GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *)event);
3442
3443   if (ewidget == combo_box->priv->popup_window)
3444     return TRUE;
3445
3446   if ((ewidget != combo_box->priv->button && ewidget != combo_box->priv->box) ||
3447       gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (combo_box->priv->button)))
3448     return FALSE;
3449
3450   if (combo_box->priv->focus_on_click && 
3451       !GTK_WIDGET_HAS_FOCUS (combo_box->priv->button))
3452     gtk_widget_grab_focus (combo_box->priv->button);
3453
3454   gtk_combo_box_popup (combo_box);
3455
3456   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
3457                                 TRUE);
3458
3459   combo_box->priv->auto_scroll = FALSE;
3460   if (combo_box->priv->scroll_timer == 0)
3461     combo_box->priv->scroll_timer = g_timeout_add (SCROLL_TIME, 
3462                                                    (GSourceFunc) gtk_combo_box_list_scroll_timeout, 
3463                                                    combo_box);
3464
3465   combo_box->priv->popup_in_progress = TRUE;
3466
3467   return TRUE;
3468 }
3469
3470 static gboolean
3471 gtk_combo_box_list_button_released (GtkWidget      *widget,
3472                                     GdkEventButton *event,
3473                                     gpointer        data)
3474 {
3475   gboolean ret;
3476   GtkTreePath *path = NULL;
3477   GtkTreeIter iter;
3478
3479   GtkComboBox *combo_box = GTK_COMBO_BOX (data);
3480
3481   gboolean popup_in_progress = FALSE;
3482
3483   GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *)event);
3484
3485   if (combo_box->priv->popup_in_progress)
3486     {
3487       popup_in_progress = TRUE;
3488       combo_box->priv->popup_in_progress = FALSE;
3489     }
3490
3491   gtk_tree_view_set_hover_expand (GTK_TREE_VIEW (combo_box->priv->tree_view), 
3492                                   FALSE);
3493   if (combo_box->priv->scroll_timer)
3494     {
3495       g_source_remove (combo_box->priv->scroll_timer);
3496       combo_box->priv->scroll_timer = 0;
3497     }
3498
3499   if (ewidget != combo_box->priv->tree_view)
3500     {
3501       if ((ewidget == combo_box->priv->button || 
3502            ewidget == combo_box->priv->box) &&
3503           !popup_in_progress &&
3504           gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (combo_box->priv->button)))
3505         {
3506           gtk_combo_box_popdown (combo_box);
3507           return TRUE;
3508         }
3509
3510       /* released outside treeview */
3511       if (ewidget != combo_box->priv->button && 
3512           ewidget != combo_box->priv->box)
3513         {
3514           gtk_combo_box_popdown (combo_box);
3515
3516           return TRUE;
3517         }
3518
3519       return FALSE;
3520     }
3521
3522   /* select something cool */
3523   ret = gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (combo_box->priv->tree_view),
3524                                        event->x, event->y,
3525                                        &path,
3526                                        NULL, NULL, NULL);
3527
3528   if (!ret)
3529     return TRUE; /* clicked outside window? */
3530
3531   gtk_tree_model_get_iter (combo_box->priv->model, &iter, path);
3532   gtk_tree_path_free (path);
3533
3534   if (tree_column_row_is_sensitive (combo_box, &iter))
3535     gtk_combo_box_set_active_iter (combo_box, &iter);
3536
3537   gtk_combo_box_popdown (combo_box);
3538
3539   return TRUE;
3540 }
3541
3542 static gboolean
3543 gtk_combo_box_key_press (GtkWidget   *widget,
3544                          GdkEventKey *event,
3545                          gpointer     data)
3546 {
3547   GtkComboBox *combo_box = GTK_COMBO_BOX (data);
3548   guint state = event->state & gtk_accelerator_get_default_mod_mask ();
3549   gboolean found;
3550   GtkTreeIter iter;
3551   GtkTreeIter new_iter;
3552
3553   if (combo_box->priv->model == NULL)
3554     return FALSE;
3555
3556   if ((event->keyval == GDK_Down || event->keyval == GDK_KP_Down) && 
3557       state == GDK_MOD1_MASK)
3558     {
3559       gtk_combo_box_popup (combo_box);
3560
3561       return TRUE;
3562     }
3563
3564   if (state != 0)
3565     return FALSE;
3566
3567   switch (event->keyval) 
3568     {
3569     case GDK_Down:
3570     case GDK_KP_Down:
3571       if (gtk_combo_box_get_active_iter (combo_box, &iter))
3572         {
3573           found = tree_next (combo_box, combo_box->priv->model, 
3574                              &iter, &new_iter, FALSE);
3575           break;
3576         }
3577       /* else fall through */
3578     case GDK_Page_Up:
3579     case GDK_KP_Page_Up:
3580     case GDK_Home: 
3581     case GDK_KP_Home:
3582       found = tree_first (combo_box, combo_box->priv->model, &new_iter, FALSE);
3583       break;
3584
3585     case GDK_Up:
3586     case GDK_KP_Up:
3587       if (gtk_combo_box_get_active_iter (combo_box, &iter))
3588         {
3589           found = tree_prev (combo_box, combo_box->priv->model, 
3590                              &iter, &new_iter, FALSE);
3591           break;
3592         }
3593       /* else fall through */      
3594     case GDK_Page_Down:
3595     case GDK_KP_Page_Down:
3596     case GDK_End: 
3597     case GDK_KP_End:
3598       found = tree_last (combo_box, combo_box->priv->model, &new_iter, FALSE);
3599       break;
3600     default:
3601       return FALSE;
3602     }
3603       
3604   if (found)
3605     gtk_combo_box_set_active_iter (combo_box, &new_iter);
3606   
3607   return TRUE;
3608 }
3609
3610 static gboolean
3611 gtk_combo_box_menu_key_press (GtkWidget   *widget,
3612                               GdkEventKey *event,
3613                               gpointer     data)
3614 {
3615   GtkComboBox *combo_box = GTK_COMBO_BOX (data);
3616   guint state = event->state & gtk_accelerator_get_default_mod_mask ();
3617
3618   if ((event->keyval == GDK_Up || event->keyval == GDK_KP_Up) && 
3619       state == GDK_MOD1_MASK)
3620     {
3621       gtk_combo_box_popdown (combo_box);
3622
3623       return TRUE;
3624     }
3625   
3626   return FALSE;
3627 }
3628
3629 static gboolean
3630 gtk_combo_box_list_key_press (GtkWidget   *widget,
3631                               GdkEventKey *event,
3632                               gpointer     data)
3633 {
3634   GtkComboBox *combo_box = GTK_COMBO_BOX (data);
3635   GtkTreeIter iter;
3636   guint state = event->state & gtk_accelerator_get_default_mod_mask ();
3637
3638   if (event->keyval == GDK_Escape ||
3639       ((event->keyval == GDK_Up || event->keyval == GDK_KP_Up) && 
3640        state == GDK_MOD1_MASK))
3641     {
3642       /* reset active item -- this is incredibly lame and ugly */
3643       if (gtk_combo_box_get_active_iter (combo_box, &iter))
3644         gtk_combo_box_set_active_iter (combo_box, &iter);
3645       
3646       gtk_combo_box_popdown (combo_box);
3647       
3648       return TRUE;
3649     }
3650     
3651   if (event->keyval == GDK_Return || event->keyval == GDK_KP_Enter ||
3652       event->keyval == GDK_space || event->keyval == GDK_KP_Space) 
3653   {
3654     GtkTreeIter iter;
3655     GtkTreeModel *model = NULL;
3656     
3657     if (combo_box->priv->model)
3658       {
3659         GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (combo_box->priv->tree_view));
3660     
3661         if (gtk_tree_selection_get_selected (sel, &model, &iter))
3662           gtk_combo_box_set_active_iter (combo_box, &iter);
3663       }
3664
3665     gtk_combo_box_popdown (combo_box);
3666     
3667     return TRUE;
3668   }
3669
3670   return FALSE;
3671 }
3672
3673 static void
3674 gtk_combo_box_list_auto_scroll (GtkComboBox *combo_box,
3675                                 gint         x, 
3676                                 gint         y)
3677 {
3678   GtkWidget *tree_view = combo_box->priv->tree_view;
3679   GtkAdjustment *adj;
3680   gdouble value;
3681
3682   adj = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window));
3683   if (adj && adj->upper - adj->lower > adj->page_size)
3684     {
3685       if (x <= tree_view->allocation.x && 
3686           adj->lower < adj->value)
3687         {
3688           value = adj->value - (tree_view->allocation.x - x + 1);
3689           gtk_adjustment_set_value (adj, CLAMP (value, adj->lower, adj->upper - adj->page_size));
3690         }
3691       else if (x >= tree_view->allocation.x + tree_view->allocation.width &&
3692                adj->upper - adj->page_size > adj->value)
3693         {
3694           value = adj->value + (x - tree_view->allocation.x - tree_view->allocation.width + 1);
3695           gtk_adjustment_set_value (adj, CLAMP (value, 0.0, adj->upper - adj->page_size));
3696         }
3697     }
3698
3699   adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window));
3700   if (adj && adj->upper - adj->lower > adj->page_size)
3701     {
3702       if (y <= tree_view->allocation.y && 
3703           adj->lower < adj->value)
3704         {
3705           value = adj->value - (tree_view->allocation.y - y + 1);
3706           gtk_adjustment_set_value (adj, CLAMP (value, adj->lower, adj->upper - adj->page_size));
3707         }
3708       else if (y >= tree_view->allocation.height &&
3709                adj->upper - adj->page_size > adj->value)
3710         {
3711           value = adj->value + (y - tree_view->allocation.height + 1);
3712           gtk_adjustment_set_value (adj, CLAMP (value, 0.0, adj->upper - adj->page_size));
3713         }
3714     }
3715 }
3716
3717 static gboolean
3718 gtk_combo_box_list_scroll_timeout (GtkComboBox *combo_box)
3719 {
3720   gint x, y;
3721
3722   GDK_THREADS_ENTER ();
3723
3724   if (combo_box->priv->auto_scroll)
3725     {
3726       gdk_window_get_pointer (combo_box->priv->tree_view->window, 
3727                               &x, &y, NULL);
3728       gtk_combo_box_list_auto_scroll (combo_box, x, y);
3729     }
3730
3731   GDK_THREADS_LEAVE ();
3732
3733   return TRUE;
3734 }
3735
3736 static gboolean 
3737 gtk_combo_box_list_enter_notify (GtkWidget        *widget,
3738                                  GdkEventCrossing *event,
3739                                  gpointer          data)
3740 {
3741   GtkComboBox *combo_box = GTK_COMBO_BOX (data);
3742
3743   combo_box->priv->auto_scroll = TRUE;
3744
3745   return TRUE;
3746 }
3747
3748
3749 static void
3750 gtk_combo_box_list_row_changed (GtkTreeModel *model,
3751                                 GtkTreePath  *path,
3752                                 GtkTreeIter  *iter,
3753                                 gpointer      data)
3754 {
3755   GtkComboBox *combo_box = GTK_COMBO_BOX (data);
3756   gint width;
3757
3758   width = gtk_combo_box_calc_requested_width (combo_box, path);
3759
3760   if (width > combo_box->priv->width)
3761     {
3762       if (combo_box->priv->cell_view) 
3763         {
3764           gtk_widget_set_size_request (combo_box->priv->cell_view, width, -1);
3765           gtk_widget_queue_resize (combo_box->priv->cell_view);
3766         }
3767       combo_box->priv->width = width;
3768     }
3769 }
3770
3771 /*
3772  * GtkCellLayout implementation
3773  */
3774
3775 static void
3776 pack_start_recurse (GtkWidget       *menu,
3777                     GtkCellRenderer *cell,
3778                     gboolean         expand)
3779 {
3780   GList *i, *list;
3781   GtkWidget *submenu;    
3782   
3783   list = gtk_container_get_children (GTK_CONTAINER (menu));
3784   for (i = list; i; i = i->next)
3785     {
3786       if (GTK_IS_CELL_LAYOUT (GTK_BIN (i->data)->child))
3787         gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (GTK_BIN (i->data)->child), 
3788                                     cell, expand);
3789
3790       submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
3791       if (submenu != NULL)
3792         pack_start_recurse (submenu, cell, expand);
3793     }
3794
3795   g_list_free (list);
3796 }
3797
3798 static void
3799 gtk_combo_box_cell_layout_pack_start (GtkCellLayout   *layout,
3800                                       GtkCellRenderer *cell,
3801                                       gboolean         expand)
3802 {
3803   ComboCellInfo *info;
3804   GtkComboBox *combo_box;
3805
3806   g_return_if_fail (GTK_IS_COMBO_BOX (layout));
3807   g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
3808
3809   combo_box = GTK_COMBO_BOX (layout);
3810
3811   g_object_ref (cell);
3812   gtk_object_sink (GTK_OBJECT (cell));
3813
3814   info = g_new0 (ComboCellInfo, 1);
3815   info->cell = cell;
3816   info->expand = expand;
3817   info->pack = GTK_PACK_START;
3818
3819   combo_box->priv->cells = g_slist_append (combo_box->priv->cells, info);
3820
3821   if (combo_box->priv->cell_view)
3822     gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box->priv->cell_view),
3823                                 cell, expand);
3824
3825   if (combo_box->priv->column)
3826     gtk_tree_view_column_pack_start (combo_box->priv->column, cell, expand);
3827
3828   if (GTK_IS_MENU (combo_box->priv->popup_widget))
3829     pack_start_recurse (combo_box->priv->popup_widget, cell, expand);
3830 }
3831
3832 static void
3833 pack_end_recurse (GtkWidget       *menu,
3834                   GtkCellRenderer *cell,
3835                   gboolean         expand)
3836 {
3837   GList *i, *list;
3838   GtkWidget *submenu;    
3839   
3840   list = gtk_container_get_children (GTK_CONTAINER (menu));
3841   for (i = list; i; i = i->next)
3842     {
3843       if (GTK_IS_CELL_LAYOUT (GTK_BIN (i->data)->child))
3844         gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (GTK_BIN (i->data)->child), 
3845                                   cell, expand);
3846
3847       submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
3848       if (submenu != NULL)
3849         pack_end_recurse (submenu, cell, expand);
3850     }
3851
3852   g_list_free (list);
3853 }
3854
3855 static void
3856 gtk_combo_box_cell_layout_pack_end (GtkCellLayout   *layout,
3857                                     GtkCellRenderer *cell,
3858                                     gboolean         expand)
3859 {
3860   ComboCellInfo *info;
3861   GtkComboBox *combo_box;
3862
3863   g_return_if_fail (GTK_IS_COMBO_BOX (layout));
3864   g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
3865
3866   combo_box = GTK_COMBO_BOX (layout);
3867
3868   g_object_ref (cell);
3869   gtk_object_sink (GTK_OBJECT (cell));
3870
3871   info = g_new0 (ComboCellInfo, 1);
3872   info->cell = cell;
3873   info->expand = expand;
3874   info->pack = GTK_PACK_END;
3875
3876   combo_box->priv->cells = g_slist_append (combo_box->priv->cells, info);
3877
3878   if (combo_box->priv->cell_view)
3879     gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (combo_box->priv->cell_view),
3880                               cell, expand);
3881
3882   if (combo_box->priv->column)
3883     gtk_tree_view_column_pack_end (combo_box->priv->column, cell, expand);
3884
3885   if (GTK_IS_MENU (combo_box->priv->popup_widget))
3886     pack_end_recurse (combo_box->priv->popup_widget, cell, expand);
3887 }
3888
3889 static void
3890 clear_recurse (GtkWidget *menu)
3891 {
3892   GList *i, *list;
3893   GtkWidget *submenu;    
3894   
3895   list = gtk_container_get_children (GTK_CONTAINER (menu));
3896   for (i = list; i; i = i->next)
3897     {
3898       if (GTK_IS_CELL_LAYOUT (GTK_BIN (i->data)->child))
3899         gtk_cell_layout_clear (GTK_CELL_LAYOUT (GTK_BIN (i->data)->child)); 
3900
3901       submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
3902       if (submenu != NULL)
3903         clear_recurse (submenu);
3904     }
3905
3906   g_list_free (list);
3907 }
3908
3909 static void
3910 gtk_combo_box_cell_layout_clear (GtkCellLayout *layout)
3911 {
3912   GtkComboBox *combo_box;
3913   GSList *i;
3914   
3915   g_return_if_fail (GTK_IS_COMBO_BOX (layout));
3916
3917   combo_box = GTK_COMBO_BOX (layout);
3918  
3919   if (combo_box->priv->cell_view)
3920     gtk_cell_layout_clear (GTK_CELL_LAYOUT (combo_box->priv->cell_view));
3921
3922   if (combo_box->priv->column)
3923     gtk_tree_view_column_clear (combo_box->priv->column);
3924
3925   for (i = combo_box->priv->cells; i; i = i->next)
3926     {
3927      ComboCellInfo *info = (ComboCellInfo *)i->data;
3928
3929       gtk_combo_box_cell_layout_clear_attributes (layout, info->cell);
3930       g_object_unref (info->cell);
3931       g_free (info);
3932       i->data = NULL;
3933     }
3934   g_slist_free (combo_box->priv->cells);
3935   combo_box->priv->cells = NULL;
3936
3937   if (GTK_IS_MENU (combo_box->priv->popup_widget))
3938     clear_recurse (combo_box->priv->popup_widget);
3939 }
3940
3941 static void
3942 add_attribute_recurse (GtkWidget       *menu,
3943                        GtkCellRenderer *cell,
3944                        const gchar     *attribute,
3945                        gint             column)
3946 {
3947   GList *i, *list;
3948   GtkWidget *submenu;    
3949   
3950   list = gtk_container_get_children (GTK_CONTAINER (menu));
3951   for (i = list; i; i = i->next)
3952     {
3953       if (GTK_IS_CELL_LAYOUT (GTK_BIN (i->data)->child))
3954         gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (GTK_BIN (i->data)->child),
3955                                        cell, attribute, column); 
3956
3957       submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
3958       if (submenu != NULL)
3959         add_attribute_recurse (submenu, cell, attribute, column);
3960     }
3961
3962   g_list_free (list);
3963 }
3964                        
3965 static void
3966 gtk_combo_box_cell_layout_add_attribute (GtkCellLayout   *layout,
3967                                          GtkCellRenderer *cell,
3968                                          const gchar     *attribute,
3969                                          gint             column)
3970 {
3971   ComboCellInfo *info;
3972   GtkComboBox *combo_box;
3973
3974   g_return_if_fail (GTK_IS_COMBO_BOX (layout));
3975   g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
3976
3977   combo_box = GTK_COMBO_BOX (layout);
3978
3979   info = gtk_combo_box_get_cell_info (combo_box, cell);
3980
3981   info->attributes = g_slist_prepend (info->attributes,
3982                                       GINT_TO_POINTER (column));
3983   info->attributes = g_slist_prepend (info->attributes,
3984                                       g_strdup (attribute));
3985
3986   if (combo_box->priv->cell_view)
3987     gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box->priv->cell_view),
3988                                    cell, attribute, column);
3989
3990   if (combo_box->priv->column)
3991     gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box->priv->column),
3992                                    cell, attribute, column);
3993
3994   if (GTK_IS_MENU (combo_box->priv->popup_widget))
3995     add_attribute_recurse (combo_box->priv->popup_widget, cell, attribute, column);
3996   gtk_widget_queue_resize (GTK_WIDGET (combo_box));
3997 }
3998
3999 static void
4000 combo_cell_data_func (GtkCellLayout   *cell_layout,
4001                       GtkCellRenderer *cell,
4002                       GtkTreeModel    *tree_model,
4003                       GtkTreeIter     *iter,
4004                       gpointer         data)
4005 {
4006   ComboCellInfo *info = (ComboCellInfo *)data;
4007   GtkWidget *parent = NULL;
4008   
4009   if (!info->func)
4010     return;
4011
4012   (*info->func) (cell_layout, cell, tree_model, iter, info->func_data);
4013   
4014   if (GTK_IS_WIDGET (cell_layout))
4015     parent = gtk_widget_get_parent (GTK_WIDGET (cell_layout));
4016   
4017   if (GTK_IS_MENU_ITEM (parent) && 
4018       gtk_menu_item_get_submenu (GTK_MENU_ITEM (parent)))
4019     g_object_set (cell, "sensitive", TRUE, NULL);
4020 }
4021
4022
4023 static void 
4024 set_cell_data_func_recurse (GtkWidget             *menu,
4025                             GtkCellRenderer       *cell,
4026                             ComboCellInfo         *info)
4027 {
4028   GList *i, *list;
4029   GtkWidget *submenu;    
4030   GtkWidget *cell_view;
4031   
4032  list = gtk_container_get_children (GTK_CONTAINER (menu));
4033   for (i = list; i; i = i->next)
4034     {
4035       cell_view = GTK_BIN (i->data)->child;
4036       if (GTK_IS_CELL_LAYOUT (cell_view))
4037         {
4038           /* Override sensitivity for inner nodes; we don't
4039            * want menuitems with submenus to appear insensitive 
4040            */ 
4041           gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (cell_view), 
4042                                               cell, 
4043                                               combo_cell_data_func, 
4044                                               info, NULL); 
4045           submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4046           if (submenu != NULL)
4047             set_cell_data_func_recurse (submenu, cell, info);
4048         }
4049     }
4050
4051   g_list_free (list);
4052 }
4053
4054 static void
4055 gtk_combo_box_cell_layout_set_cell_data_func (GtkCellLayout         *layout,
4056                                               GtkCellRenderer       *cell,
4057                                               GtkCellLayoutDataFunc  func,
4058                                               gpointer               func_data,
4059                                               GDestroyNotify         destroy)
4060 {
4061   ComboCellInfo *info;
4062   GtkComboBox *combo_box;
4063
4064   g_return_if_fail (GTK_IS_COMBO_BOX (layout));
4065
4066   combo_box = GTK_COMBO_BOX (layout);
4067
4068   info = gtk_combo_box_get_cell_info (combo_box, cell);
4069   g_return_if_fail (info != NULL);
4070   
4071   if (info->destroy)
4072     {
4073       GDestroyNotify d = info->destroy;
4074
4075       info->destroy = NULL;
4076       d (info->func_data);
4077     }
4078
4079   info->func = func;
4080   info->func_data = func_data;
4081   info->destroy = destroy;
4082
4083   if (combo_box->priv->cell_view)
4084     gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo_box->priv->cell_view), cell, func, func_data, NULL);
4085
4086   if (combo_box->priv->column)
4087     gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo_box->priv->column), cell, func, func_data, NULL);
4088
4089   if (GTK_IS_MENU (combo_box->priv->popup_widget))
4090     set_cell_data_func_recurse (combo_box->priv->popup_widget, cell, info);
4091
4092   gtk_widget_queue_resize (GTK_WIDGET (combo_box));
4093 }
4094
4095 static void 
4096 clear_attributes_recurse (GtkWidget             *menu,
4097                           GtkCellRenderer       *cell)
4098 {
4099   GList *i, *list;
4100   GtkWidget *submenu;    
4101   
4102   list = gtk_container_get_children (GTK_CONTAINER (menu));
4103   for (i = list; i; i = i->next)
4104     {
4105       if (GTK_IS_CELL_LAYOUT (GTK_BIN (i->data)->child))
4106         gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (GTK_BIN (i->data)->child),
4107                                             cell); 
4108       
4109       submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4110       if (submenu != NULL)
4111         clear_attributes_recurse (submenu, cell);
4112     }
4113
4114   g_list_free (list);
4115 }
4116
4117 static void
4118 gtk_combo_box_cell_layout_clear_attributes (GtkCellLayout   *layout,
4119                                             GtkCellRenderer *cell)
4120 {
4121   ComboCellInfo *info;
4122   GtkComboBox *combo_box;
4123   GSList *list;
4124
4125   g_return_if_fail (GTK_IS_COMBO_BOX (layout));
4126   g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
4127
4128   combo_box = GTK_COMBO_BOX (layout);
4129
4130   info = gtk_combo_box_get_cell_info (combo_box, cell);
4131   g_return_if_fail (info != NULL);
4132
4133   list = info->attributes;
4134   while (list && list->next)
4135     {
4136       g_free (list->data);
4137       list = list->next->next;
4138     }
4139   g_slist_free (info->attributes);
4140   info->attributes = NULL;
4141
4142   if (combo_box->priv->cell_view)
4143     gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (combo_box->priv->cell_view), cell);
4144
4145   if (combo_box->priv->column)
4146     gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (combo_box->priv->column), cell);
4147
4148   if (GTK_IS_MENU (combo_box->priv->popup_widget))
4149     clear_attributes_recurse (combo_box->priv->popup_widget, cell);
4150
4151   gtk_widget_queue_resize (GTK_WIDGET (combo_box));
4152 }
4153
4154 static void 
4155 reorder_recurse (GtkWidget             *menu,
4156                  GtkCellRenderer       *cell,
4157                  gint                   position)
4158 {
4159   GList *i, *list;
4160   GtkWidget *submenu;    
4161   
4162   list = gtk_container_get_children (GTK_CONTAINER (menu));
4163   for (i = list; i; i = i->next)
4164     {
4165       if (GTK_IS_CELL_LAYOUT (GTK_BIN (i->data)->child))
4166         gtk_cell_layout_reorder (GTK_CELL_LAYOUT (GTK_BIN (i->data)->child),
4167                                  cell, position); 
4168       
4169       submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4170       if (submenu != NULL)
4171         reorder_recurse (submenu, cell, position);
4172     }
4173
4174   g_list_free (list);
4175 }
4176
4177 static void
4178 gtk_combo_box_cell_layout_reorder (GtkCellLayout   *layout,
4179                                    GtkCellRenderer *cell,
4180                                    gint             position)
4181 {
4182   ComboCellInfo *info;
4183   GtkComboBox *combo_box;
4184   GSList *link;
4185
4186   g_return_if_fail (GTK_IS_COMBO_BOX (layout));
4187   g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
4188
4189   combo_box = GTK_COMBO_BOX (layout);
4190
4191   info = gtk_combo_box_get_cell_info (combo_box, cell);
4192
4193   g_return_if_fail (info != NULL);
4194   g_return_if_fail (position >= 0);
4195
4196   link = g_slist_find (combo_box->priv->cells, info);
4197
4198   g_return_if_fail (link != NULL);
4199
4200   combo_box->priv->cells = g_slist_remove_link (combo_box->priv->cells, link);
4201   combo_box->priv->cells = g_slist_insert (combo_box->priv->cells, info,
4202                                            position);
4203
4204   if (combo_box->priv->cell_view)
4205     gtk_cell_layout_reorder (GTK_CELL_LAYOUT (combo_box->priv->cell_view),
4206                              cell, position);
4207
4208   if (combo_box->priv->column)
4209     gtk_cell_layout_reorder (GTK_CELL_LAYOUT (combo_box->priv->column),
4210                              cell, position);
4211
4212   if (GTK_IS_MENU (combo_box->priv->popup_widget))
4213     reorder_recurse (combo_box->priv->popup_widget, cell, position);
4214
4215   gtk_widget_queue_draw (GTK_WIDGET (combo_box));
4216 }
4217
4218 /*
4219  * public API
4220  */
4221
4222 /**
4223  * gtk_combo_box_new:
4224  *
4225  * Creates a new empty #GtkComboBox.
4226  *
4227  * Return value: A new #GtkComboBox.
4228  *
4229  * Since: 2.4
4230  */
4231 GtkWidget *
4232 gtk_combo_box_new (void)
4233 {
4234   return g_object_new (GTK_TYPE_COMBO_BOX, NULL);
4235 }
4236
4237 /**
4238  * gtk_combo_box_new_with_model:
4239  * @model: A #GtkTreeModel.
4240  *
4241  * Creates a new #GtkComboBox with the model initialized to @model.
4242  *
4243  * Return value: A new #GtkComboBox.
4244  *
4245  * Since: 2.4
4246  */
4247 GtkWidget *
4248 gtk_combo_box_new_with_model (GtkTreeModel *model)
4249 {
4250   GtkComboBox *combo_box;
4251
4252   g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL);
4253
4254   combo_box = g_object_new (GTK_TYPE_COMBO_BOX, "model", model, NULL);
4255
4256   return GTK_WIDGET (combo_box);
4257 }
4258
4259 /**
4260  * gtk_combo_box_get_wrap_width:
4261  * @combo_box: A #GtkComboBox.
4262  *
4263  * Returns the wrap width which is used to determine the number
4264  * of columns for the popup menu. If the wrap width is larger than
4265  * 1, the combo box is in table mode.
4266  *
4267  * Returns: the wrap width.
4268  *
4269  * Since: 2.6
4270  */
4271 gint
4272 gtk_combo_box_get_wrap_width (GtkComboBox *combo_box)
4273 {
4274   g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), -1);
4275
4276   return combo_box->priv->wrap_width;
4277 }
4278
4279 /**
4280  * gtk_combo_box_set_wrap_width:
4281  * @combo_box: A #GtkComboBox.
4282  * @width: Preferred number of columns.
4283  *
4284  * Sets the wrap width of @combo_box to be @width. The wrap width is basically
4285  * the preferred number of columns when you want the popup to be layed out
4286  * in a table.
4287  *
4288  * Since: 2.4
4289  */
4290 void
4291 gtk_combo_box_set_wrap_width (GtkComboBox *combo_box,
4292                               gint         width)
4293 {
4294   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4295   g_return_if_fail (width >= 0);
4296
4297   if (width != combo_box->priv->wrap_width)
4298     {
4299       combo_box->priv->wrap_width = width;
4300
4301       gtk_combo_box_check_appearance (combo_box);
4302       gtk_combo_box_relayout (combo_box);
4303       
4304       g_object_notify (G_OBJECT (combo_box), "wrap-width");
4305     }
4306 }
4307
4308 /**
4309  * gtk_combo_box_get_row_span_column:
4310  * @combo_box: A #GtkComboBox.
4311  *
4312  * Returns the column with row span information for @combo_box.
4313  *
4314  * Returns: the row span column.
4315  *
4316  * Since: 2.6
4317  */
4318 gint
4319 gtk_combo_box_get_row_span_column (GtkComboBox *combo_box)
4320 {
4321   g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), -1);
4322
4323   return combo_box->priv->row_column;
4324 }
4325
4326 /**
4327  * gtk_combo_box_set_row_span_column:
4328  * @combo_box: A #GtkComboBox.
4329  * @row_span: A column in the model passed during construction.
4330  *
4331  * Sets the column with row span information for @combo_box to be @row_span.
4332  * The row span column contains integers which indicate how many rows
4333  * an item should span.
4334  *
4335  * Since: 2.4
4336  */
4337 void
4338 gtk_combo_box_set_row_span_column (GtkComboBox *combo_box,
4339                                    gint         row_span)
4340 {
4341   gint col;
4342
4343   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4344
4345   col = gtk_tree_model_get_n_columns (combo_box->priv->model);
4346   g_return_if_fail (row_span >= -1 && row_span < col);
4347
4348   if (row_span != combo_box->priv->row_column)
4349     {
4350       combo_box->priv->row_column = row_span;
4351       
4352       gtk_combo_box_relayout (combo_box);
4353  
4354       g_object_notify (G_OBJECT (combo_box), "row-span-column");
4355     }
4356 }
4357
4358 /**
4359  * gtk_combo_box_get_column_span_column:
4360  * @combo_box: A #GtkComboBox.
4361  *
4362  * Returns the column with column span information for @combo_box.
4363  *
4364  * Returns: the column span column.
4365  *
4366  * Since: 2.6
4367  */
4368 gint
4369 gtk_combo_box_get_column_span_column (GtkComboBox *combo_box)
4370 {
4371   g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), -1);
4372
4373   return combo_box->priv->col_column;
4374 }
4375
4376 /**
4377  * gtk_combo_box_set_column_span_column:
4378  * @combo_box: A #GtkComboBox.
4379  * @column_span: A column in the model passed during construction.
4380  *
4381  * Sets the column with column span information for @combo_box to be
4382  * @column_span. The column span column contains integers which indicate
4383  * how many columns an item should span.
4384  *
4385  * Since: 2.4
4386  */
4387 void
4388 gtk_combo_box_set_column_span_column (GtkComboBox *combo_box,
4389                                       gint         column_span)
4390 {
4391   gint col;
4392
4393   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4394
4395   col = gtk_tree_model_get_n_columns (combo_box->priv->model);
4396   g_return_if_fail (column_span >= -1 && column_span < col);
4397
4398   if (column_span != combo_box->priv->col_column)
4399     {
4400       combo_box->priv->col_column = column_span;
4401       
4402       gtk_combo_box_relayout (combo_box);
4403
4404       g_object_notify (G_OBJECT (combo_box), "column-span-column");
4405     }
4406 }
4407
4408 /**
4409  * gtk_combo_box_get_active:
4410  * @combo_box: A #GtkComboBox.
4411  *
4412  * Returns the index of the currently active item, or -1 if there's no
4413  * active item. If the model is a non-flat treemodel, and the active item 
4414  * is not an immediate child of the root of the tree, this function returns 
4415  * <literal>gtk_tree_path_get_indices (path)[0]</literal>, where 
4416  * <literal>path</literal> is the #GtkTreePath of the active item.
4417  *
4418  * Return value: An integer which is the index of the currently active item, or
4419  * -1 if there's no active item.
4420  *
4421  * Since: 2.4
4422  */
4423 gint
4424 gtk_combo_box_get_active (GtkComboBox *combo_box)
4425 {
4426   gint result;
4427   g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), 0);
4428
4429   if (gtk_tree_row_reference_valid (combo_box->priv->active_row))
4430     {
4431       GtkTreePath *path;
4432
4433       path = gtk_tree_row_reference_get_path (combo_box->priv->active_row);      
4434       result = gtk_tree_path_get_indices (path)[0];
4435       gtk_tree_path_free (path);
4436     }
4437   else
4438     result = -1;
4439
4440   return result;
4441 }
4442
4443 /**
4444  * gtk_combo_box_set_active:
4445  * @combo_box: A #GtkComboBox.
4446  * @index_: An index in the model passed during construction, or -1 to have
4447  * no active item.
4448  *
4449  * Sets the active item of @combo_box to be the item at @index.
4450  *
4451  * Since: 2.4
4452  */
4453 void
4454 gtk_combo_box_set_active (GtkComboBox *combo_box,
4455                           gint         index_)
4456 {
4457   GtkTreePath *path = NULL;
4458   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4459   g_return_if_fail (index_ >= -1);
4460
4461   if (index_ != -1)
4462     path = gtk_tree_path_new_from_indices (index_, -1);
4463    
4464   gtk_combo_box_set_active_internal (combo_box, path);
4465
4466   if (path)
4467     gtk_tree_path_free (path);
4468 }
4469
4470 static void
4471 gtk_combo_box_set_active_internal (GtkComboBox *combo_box,
4472                                    GtkTreePath *path)
4473 {
4474   GtkTreePath *active_path;
4475   gint path_cmp;
4476
4477   if (path && gtk_tree_row_reference_valid (combo_box->priv->active_row))
4478     {
4479       active_path = gtk_tree_row_reference_get_path (combo_box->priv->active_row);
4480       path_cmp = gtk_tree_path_compare (path, active_path);
4481       gtk_tree_path_free (active_path);
4482       if (path_cmp == 0)
4483         return;
4484     }
4485
4486   if (combo_box->priv->active_row)
4487     {
4488       gtk_tree_row_reference_free (combo_box->priv->active_row);
4489       combo_box->priv->active_row = NULL;
4490     }
4491   
4492   if (!path)
4493     {
4494       if (combo_box->priv->tree_view)
4495         gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (combo_box->priv->tree_view)));
4496       else
4497         {
4498           GtkMenu *menu = GTK_MENU (combo_box->priv->popup_widget);
4499
4500           if (GTK_IS_MENU (menu))
4501             gtk_menu_set_active (menu, -1);
4502         }
4503
4504       if (combo_box->priv->cell_view)
4505         gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (combo_box->priv->cell_view), NULL);
4506     }
4507   else
4508     {
4509       combo_box->priv->active_row = 
4510         gtk_tree_row_reference_new (combo_box->priv->model, path);
4511
4512       if (combo_box->priv->tree_view)
4513         {
4514           gtk_tree_view_set_cursor (GTK_TREE_VIEW (combo_box->priv->tree_view), 
4515                                     path, NULL, FALSE);
4516         }
4517       else if (GTK_IS_MENU (combo_box->priv->popup_widget))
4518         {
4519           /* FIXME handle nested menus better */
4520           gtk_menu_set_active (GTK_MENU (combo_box->priv->popup_widget), 
4521                                gtk_tree_path_get_indices (path)[0]);
4522         }
4523
4524       if (combo_box->priv->cell_view)
4525         gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (combo_box->priv->cell_view), 
4526                                          path);
4527     }
4528
4529   g_signal_emit_by_name (combo_box, "changed", NULL, NULL);
4530 }
4531
4532
4533 /**
4534  * gtk_combo_box_get_active_iter:
4535  * @combo_box: A #GtkComboBox
4536  * @iter: The uninitialized #GtkTreeIter.
4537  * 
4538  * Sets @iter to point to the current active item, if it exists.
4539  * 
4540  * Return value: %TRUE, if @iter was set
4541  *
4542  * Since: 2.4
4543  **/
4544 gboolean
4545 gtk_combo_box_get_active_iter (GtkComboBox     *combo_box,
4546                                GtkTreeIter     *iter)
4547 {
4548   GtkTreePath *path;
4549   gboolean result;
4550
4551   g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
4552
4553   if (!gtk_tree_row_reference_valid (combo_box->priv->active_row))
4554     return FALSE;
4555
4556   path = gtk_tree_row_reference_get_path (combo_box->priv->active_row);
4557   result = gtk_tree_model_get_iter (combo_box->priv->model, iter, path);
4558   gtk_tree_path_free (path);
4559
4560   return result;
4561 }
4562
4563 /**
4564  * gtk_combo_box_set_active_iter:
4565  * @combo_box: A #GtkComboBox
4566  * @iter: The #GtkTreeIter.
4567  * 
4568  * Sets the current active item to be the one referenced by @iter. 
4569  * @iter must correspond to a path of depth one.
4570  * 
4571  * Since: 2.4
4572  **/
4573 void
4574 gtk_combo_box_set_active_iter (GtkComboBox     *combo_box,
4575                                GtkTreeIter     *iter)
4576 {
4577   GtkTreePath *path;
4578
4579   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4580
4581   path = gtk_tree_model_get_path (gtk_combo_box_get_model (combo_box), iter);
4582   gtk_combo_box_set_active_internal (combo_box, path);
4583   gtk_tree_path_free (path);
4584 }
4585
4586 /**
4587  * gtk_combo_box_set_model:
4588  * @combo_box: A #GtkComboBox.
4589  * @model: A #GtkTreeModel.
4590  *
4591  * Sets the model used by @combo_box to be @model. Will unset a previously set 
4592  * model (if applicable). If model is %NULL, then it will unset the model.
4593  *
4594  * Note that this function does not clear the cell renderers, you have to 
4595  * call gtk_combo_box_cell_layout_clear() yourself if you need to set up 
4596  * different cell renderers for the new model.
4597  *
4598  * Since: 2.4
4599  */
4600 void
4601 gtk_combo_box_set_model (GtkComboBox  *combo_box,
4602                          GtkTreeModel *model)
4603 {
4604   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4605
4606   if (!model)
4607     {
4608       gtk_combo_box_unset_model (combo_box);
4609       return;
4610     }
4611
4612   g_return_if_fail (GTK_IS_TREE_MODEL (model));
4613
4614   if (model == combo_box->priv->model)
4615     return;
4616   
4617   if (combo_box->priv->model)
4618     gtk_combo_box_unset_model (combo_box);
4619
4620   combo_box->priv->model = model;
4621   g_object_ref (combo_box->priv->model);
4622
4623   combo_box->priv->inserted_id =
4624     g_signal_connect (combo_box->priv->model, "row_inserted",
4625                       G_CALLBACK (gtk_combo_box_model_row_inserted),
4626                       combo_box);
4627   combo_box->priv->deleted_id =
4628     g_signal_connect (combo_box->priv->model, "row_deleted",
4629                       G_CALLBACK (gtk_combo_box_model_row_deleted),
4630                       combo_box);
4631   combo_box->priv->reordered_id =
4632     g_signal_connect (combo_box->priv->model, "rows_reordered",
4633                       G_CALLBACK (gtk_combo_box_model_rows_reordered),
4634                       combo_box);
4635   combo_box->priv->changed_id =
4636     g_signal_connect (combo_box->priv->model, "row_changed",
4637                       G_CALLBACK (gtk_combo_box_model_row_changed),
4638                       combo_box);
4639       
4640   if (combo_box->priv->tree_view)
4641     {
4642       /* list mode */
4643       gtk_tree_view_set_model (GTK_TREE_VIEW (combo_box->priv->tree_view),
4644                                combo_box->priv->model);
4645       gtk_combo_box_list_popup_resize (combo_box);
4646     }
4647   else
4648     {
4649       /* menu mode */
4650       if (combo_box->priv->popup_widget)
4651         gtk_combo_box_menu_fill (combo_box);
4652
4653     }
4654
4655   if (combo_box->priv->cell_view)
4656     gtk_cell_view_set_model (GTK_CELL_VIEW (combo_box->priv->cell_view),
4657                              combo_box->priv->model);
4658 }
4659
4660 /**
4661  * gtk_combo_box_get_model
4662  * @combo_box: A #GtkComboBox.
4663  *
4664  * Returns the #GtkTreeModel which is acting as data source for @combo_box.
4665  *
4666  * Return value: A #GtkTreeModel which was passed during construction.
4667  *
4668  * Since: 2.4
4669  */
4670 GtkTreeModel *
4671 gtk_combo_box_get_model (GtkComboBox *combo_box)
4672 {
4673   g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
4674
4675   return combo_box->priv->model;
4676 }
4677
4678
4679 /* convenience API for simple text combos */
4680
4681 /**
4682  * gtk_combo_box_new_text:
4683  *
4684  * Convenience function which constructs a new text combo box, which is a
4685  * #GtkComboBox just displaying strings. If you use this function to create
4686  * a text combo box, you should only manipulate its data source with the
4687  * following convenience functions: gtk_combo_box_append_text(),
4688  * gtk_combo_box_insert_text(), gtk_combo_box_prepend_text() and
4689  * gtk_combo_box_remove_text().
4690  *
4691  * Return value: A new text combo box.
4692  *
4693  * Since: 2.4
4694  */
4695 GtkWidget *
4696 gtk_combo_box_new_text (void)
4697 {
4698   GtkWidget *combo_box;
4699   GtkCellRenderer *cell;
4700   GtkListStore *store;
4701
4702   store = gtk_list_store_new (1, G_TYPE_STRING);
4703   combo_box = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store));
4704   g_object_unref (store);
4705
4706   cell = gtk_cell_renderer_text_new ();
4707   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), cell, TRUE);
4708   gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), cell,
4709                                   "text", 0,
4710                                   NULL);
4711
4712   return combo_box;
4713 }
4714
4715 /**
4716  * gtk_combo_box_append_text:
4717  * @combo_box: A #GtkComboBox constructed using gtk_combo_box_new_text().
4718  * @text: A string.
4719  *
4720  * Appends @string to the list of strings stored in @combo_box. Note that
4721  * you can only use this function with combo boxes constructed with
4722  * gtk_combo_box_new_text().
4723  *
4724  * Since: 2.4
4725  */
4726 void
4727 gtk_combo_box_append_text (GtkComboBox *combo_box,
4728                            const gchar *text)
4729 {
4730   GtkTreeIter iter;
4731   GtkListStore *store;
4732
4733   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4734   g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model));
4735   g_return_if_fail (text != NULL);
4736
4737   store = GTK_LIST_STORE (combo_box->priv->model);
4738
4739   gtk_list_store_append (store, &iter);
4740   gtk_list_store_set (store, &iter, 0, text, -1);
4741 }
4742
4743 /**
4744  * gtk_combo_box_insert_text:
4745  * @combo_box: A #GtkComboBox constructed using gtk_combo_box_new_text().
4746  * @position: An index to insert @text.
4747  * @text: A string.
4748  *
4749  * Inserts @string at @position in the list of strings stored in @combo_box.
4750  * Note that you can only use this function with combo boxes constructed
4751  * with gtk_combo_box_new_text().
4752  *
4753  * Since: 2.4
4754  */
4755 void
4756 gtk_combo_box_insert_text (GtkComboBox *combo_box,
4757                            gint         position,
4758                            const gchar *text)
4759 {
4760   GtkTreeIter iter;
4761   GtkListStore *store;
4762
4763   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4764   g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model));
4765   g_return_if_fail (position >= 0);
4766   g_return_if_fail (text != NULL);
4767
4768   store = GTK_LIST_STORE (combo_box->priv->model);
4769
4770   gtk_list_store_insert (store, &iter, position);
4771   gtk_list_store_set (store, &iter, 0, text, -1);
4772 }
4773
4774 /**
4775  * gtk_combo_box_prepend_text:
4776  * @combo_box: A #GtkComboBox constructed with gtk_combo_box_new_text().
4777  * @text: A string.
4778  *
4779  * Prepends @string to the list of strings stored in @combo_box. Note that
4780  * you can only use this function with combo boxes constructed with
4781  * gtk_combo_box_new_text().
4782  *
4783  * Since: 2.4
4784  */
4785 void
4786 gtk_combo_box_prepend_text (GtkComboBox *combo_box,
4787                             const gchar *text)
4788 {
4789   GtkTreeIter iter;
4790   GtkListStore *store;
4791
4792   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4793   g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model));
4794   g_return_if_fail (text != NULL);
4795
4796   store = GTK_LIST_STORE (combo_box->priv->model);
4797
4798   gtk_list_store_prepend (store, &iter);
4799   gtk_list_store_set (store, &iter, 0, text, -1);
4800 }
4801
4802 /**
4803  * gtk_combo_box_remove_text:
4804  * @combo_box: A #GtkComboBox constructed with gtk_combo_box_new_text().
4805  * @position: Index of the item to remove.
4806  *
4807  * Removes the string at @position from @combo_box. Note that you can only use
4808  * this function with combo boxes constructed with gtk_combo_box_new_text().
4809  *
4810  * Since: 2.4
4811  */
4812 void
4813 gtk_combo_box_remove_text (GtkComboBox *combo_box,
4814                            gint         position)
4815 {
4816   GtkTreeIter iter;
4817   GtkListStore *store;
4818
4819   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4820   g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model));
4821   g_return_if_fail (position >= 0);
4822
4823   store = GTK_LIST_STORE (combo_box->priv->model);
4824
4825   if (gtk_tree_model_iter_nth_child (combo_box->priv->model, &iter,
4826                                      NULL, position))
4827     gtk_list_store_remove (store, &iter);
4828 }
4829
4830 /**
4831  * gtk_combo_box_get_active_text:
4832  * @combo_box: A #GtkComboBox constructed with gtk_combo_box_new_text().
4833  *
4834  * Returns the currently active string in @combo_box or %NULL if none
4835  * is selected.  Note that you can only use this function with combo
4836  * boxes constructed with gtk_combo_box_new_text() and with 
4837  * #GtkComboBoxEntry<!-- -->s.
4838  *
4839  * Returns: a newly allocated string containing the currently active text.
4840  *
4841  * Since: 2.6
4842  */
4843 gchar *
4844 gtk_combo_box_get_active_text (GtkComboBox *combo_box)
4845 {
4846   GtkComboBoxClass *class;
4847
4848   g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
4849
4850   class = GTK_COMBO_BOX_GET_CLASS (combo_box);
4851
4852   if (class->get_active_text)
4853     return (* class->get_active_text) (combo_box);
4854
4855   return NULL;
4856 }
4857
4858 static gchar *
4859 gtk_combo_box_real_get_active_text (GtkComboBox *combo_box)
4860 {
4861   GtkTreeIter iter;
4862   gchar *text = NULL;
4863
4864   g_return_val_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model), NULL);
4865
4866   if (gtk_combo_box_get_active_iter (combo_box, &iter))
4867     gtk_tree_model_get (combo_box->priv->model, &iter, 
4868                         0, &text, -1);
4869
4870   return text;
4871 }
4872
4873 static gboolean
4874 gtk_combo_box_mnemonic_activate (GtkWidget *widget,
4875                                  gboolean   group_cycling)
4876 {
4877   GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
4878
4879   gtk_widget_grab_focus (combo_box->priv->button);
4880
4881   return TRUE;
4882 }
4883
4884 static void
4885 gtk_combo_box_grab_focus (GtkWidget *widget)
4886 {
4887   GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
4888
4889   gtk_widget_grab_focus (combo_box->priv->button);
4890 }
4891
4892 static void
4893 gtk_combo_box_destroy (GtkObject *object)
4894 {
4895   GtkComboBox *combo_box = GTK_COMBO_BOX (object);
4896
4897   if (combo_box->priv->popup_idle_id > 0)
4898     {
4899       g_source_remove (combo_box->priv->popup_idle_id);
4900       combo_box->priv->popup_idle_id = 0;
4901     }
4902
4903   gtk_combo_box_popdown (combo_box);
4904
4905   if (combo_box->priv->row_separator_destroy)
4906     (* combo_box->priv->row_separator_destroy) (combo_box->priv->row_separator_data);
4907
4908   combo_box->priv->row_separator_func = NULL;
4909   combo_box->priv->row_separator_data = NULL;
4910   combo_box->priv->row_separator_destroy = NULL;
4911
4912   combo_box->priv->destroying = 1;
4913
4914   GTK_OBJECT_CLASS (parent_class)->destroy (object);
4915   combo_box->priv->cell_view = NULL;
4916
4917   combo_box->priv->destroying = 0;
4918 }
4919
4920 static void
4921 gtk_combo_box_finalize (GObject *object)
4922 {
4923   GtkComboBox *combo_box = GTK_COMBO_BOX (object);
4924   GSList *i;
4925   
4926   if (GTK_IS_MENU (combo_box->priv->popup_widget))
4927     {
4928       gtk_combo_box_menu_destroy (combo_box);
4929       gtk_menu_detach (GTK_MENU (combo_box->priv->popup_widget));
4930       combo_box->priv->popup_widget = NULL;
4931     }
4932   
4933   if (GTK_IS_TREE_VIEW (combo_box->priv->tree_view))
4934     gtk_combo_box_list_destroy (combo_box);
4935
4936   if (combo_box->priv->popup_window)
4937     gtk_widget_destroy (combo_box->priv->popup_window);
4938
4939   gtk_combo_box_unset_model (combo_box);
4940
4941   for (i = combo_box->priv->cells; i; i = i->next)
4942     {
4943       ComboCellInfo *info = (ComboCellInfo *)i->data;
4944       GSList *list = info->attributes;
4945
4946       if (info->destroy)
4947         info->destroy (info->func_data);
4948
4949       while (list && list->next)
4950         {
4951           g_free (list->data);
4952           list = list->next->next;
4953         }
4954       g_slist_free (info->attributes);
4955
4956       g_object_unref (info->cell);
4957       g_free (info);
4958     }
4959    g_slist_free (combo_box->priv->cells);
4960
4961    G_OBJECT_CLASS (parent_class)->finalize (object);
4962 }
4963
4964 static gboolean
4965 gtk_cell_editable_key_press (GtkWidget   *widget,
4966                              GdkEventKey *event,
4967                              gpointer     data)
4968 {
4969   GtkComboBox *combo_box = GTK_COMBO_BOX (data);
4970
4971   if (event->keyval == GDK_Escape)
4972     {
4973       combo_box->priv->editing_canceled = TRUE;
4974
4975       gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (combo_box));
4976       gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (combo_box));
4977       
4978       return TRUE;
4979     }
4980   else if (event->keyval == GDK_Return)
4981     {
4982       gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (combo_box));
4983       gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (combo_box));
4984       
4985       return TRUE;
4986     }
4987
4988   return FALSE;
4989 }
4990
4991 static gboolean
4992 popdown_idle (gpointer data)
4993 {
4994   GtkComboBox *combo_box;
4995
4996   GDK_THREADS_ENTER ();
4997
4998   combo_box = GTK_COMBO_BOX (data);
4999   
5000   gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (combo_box));
5001   gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (combo_box));
5002
5003   g_object_unref (combo_box);
5004
5005   GDK_THREADS_LEAVE ();
5006
5007   return FALSE;
5008 }
5009
5010 static void
5011 popdown_handler (GtkWidget *widget,
5012                  gpointer   data)
5013 {
5014   g_idle_add (popdown_idle, g_object_ref (data));
5015 }
5016
5017 static gboolean
5018 popup_idle (gpointer data)
5019 {
5020   GtkComboBox *combo_box;
5021
5022   GDK_THREADS_ENTER ();
5023
5024   combo_box = GTK_COMBO_BOX (data);
5025
5026   if (GTK_IS_MENU (combo_box->priv->popup_widget) &&
5027       combo_box->priv->cell_view)
5028     g_signal_connect_object (combo_box->priv->popup_widget,
5029                              "unmap", G_CALLBACK (popdown_handler),
5030                              combo_box, 0);
5031   
5032   /* we unset this if a menu item is activated */
5033   combo_box->priv->editing_canceled = TRUE;
5034   gtk_combo_box_popup (combo_box);
5035
5036   combo_box->priv->popup_idle_id = 0;
5037
5038   GDK_THREADS_LEAVE ();
5039
5040   return FALSE;
5041 }
5042
5043 static void
5044 gtk_combo_box_start_editing (GtkCellEditable *cell_editable,
5045                              GdkEvent        *event)
5046 {
5047   GtkComboBox *combo_box = GTK_COMBO_BOX (cell_editable);
5048
5049   combo_box->priv->is_cell_renderer = TRUE;
5050
5051   if (combo_box->priv->cell_view)
5052     {
5053       g_signal_connect_object (combo_box->priv->button, "key_press_event",
5054                                G_CALLBACK (gtk_cell_editable_key_press), 
5055                                cell_editable, 0);  
5056
5057       gtk_widget_grab_focus (combo_box->priv->button);
5058     }
5059   else
5060     {
5061       g_signal_connect_object (GTK_BIN (combo_box)->child, "key_press_event",
5062                                G_CALLBACK (gtk_cell_editable_key_press), 
5063                                cell_editable, 0);  
5064
5065       gtk_widget_grab_focus (GTK_WIDGET (GTK_BIN (combo_box)->child));
5066       GTK_WIDGET_UNSET_FLAGS (combo_box->priv->button, GTK_CAN_FOCUS);
5067     }
5068
5069   /* we do the immediate popup only for the optionmenu-like 
5070    * appearance 
5071    */  
5072   if (combo_box->priv->is_cell_renderer && 
5073       combo_box->priv->cell_view && !combo_box->priv->tree_view)
5074     combo_box->priv->popup_idle_id = g_idle_add (popup_idle, combo_box);
5075 }
5076
5077
5078 /**
5079  * gtk_combo_box_get_add_tearoffs:
5080  * @combo_box: a #GtkComboBox
5081  * 
5082  * Gets the current value of the :add-tearoffs property.
5083  * 
5084  * Return value: the current value of the :add-tearoffs property.
5085  **/
5086 gboolean
5087 gtk_combo_box_get_add_tearoffs (GtkComboBox *combo_box)
5088 {
5089   g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
5090
5091   return combo_box->priv->add_tearoffs;
5092 }
5093
5094 /**
5095  * gtk_combo_box_set_add_tearoffs:
5096  * @combo_box: a #GtkComboBox 
5097  * @add_tearoffs: %TRUE to add tearoff menu items
5098  *  
5099  * Sets whether the popup menu should have a tearoff 
5100  * menu item.
5101  *
5102  * Since: 2.6
5103  **/
5104 void
5105 gtk_combo_box_set_add_tearoffs (GtkComboBox *combo_box,
5106                                 gboolean     add_tearoffs)
5107 {
5108   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5109
5110   add_tearoffs = add_tearoffs != FALSE;
5111
5112   if (combo_box->priv->add_tearoffs != add_tearoffs)
5113     {
5114       combo_box->priv->add_tearoffs = add_tearoffs;
5115       gtk_combo_box_check_appearance (combo_box);
5116       gtk_combo_box_relayout (combo_box);
5117       g_object_notify (G_OBJECT (combo_box), "add-tearoffs");
5118     }
5119 }
5120
5121 gboolean
5122 _gtk_combo_box_editing_canceled (GtkComboBox *combo_box)
5123 {
5124   g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), TRUE);
5125
5126   return combo_box->priv->editing_canceled;
5127 }
5128
5129 /**
5130  * gtk_combo_box_get_popup_accessible:
5131  * @combo_box: a #GtkComboBox
5132  *
5133  * Gets the accessible object corresponding to the combo box's popup.
5134  *
5135  * This function is mostly intended for use by accessibility technologies;
5136  * applications should have little use for it.
5137  *
5138  * Returns: the accessible object corresponding to the combo box's popup.
5139  *
5140  * Since: 2.6
5141  **/
5142 AtkObject*
5143 gtk_combo_box_get_popup_accessible (GtkComboBox *combo_box)
5144 {
5145   AtkObject *atk_obj;
5146
5147   g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
5148
5149   if (combo_box->priv->popup_widget)
5150     {
5151       atk_obj = gtk_widget_get_accessible (combo_box->priv->popup_widget);
5152       return atk_obj;
5153     }
5154
5155   return NULL;
5156 }
5157
5158 /**
5159  * gtk_combo_box_get_row_separator_func:
5160  * @combo_box: a #GtkComboBox
5161  * 
5162  * Returns the current row separator function.
5163  * 
5164  * Return value: the current row separator function.
5165  *
5166  * Since: 2.6
5167  **/
5168 GtkTreeViewRowSeparatorFunc 
5169 gtk_combo_box_get_row_separator_func (GtkComboBox *combo_box)
5170 {
5171   g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
5172
5173   return combo_box->priv->row_separator_func;
5174 }
5175
5176 /**
5177  * gtk_combo_box_set_row_separator_func:
5178  * @combo_box: a #GtkComboBox
5179  * @func: a #GtkTreeViewRowSeparatorFunc
5180  * @data: user data to pass to @func, or %NULL
5181  * @destroy: destroy notifier for @data, or %NULL
5182  * 
5183  * Sets the row separator function, which is used to determine
5184  * whether a row should be drawn as a separator. If the row separator
5185  * function is %NULL, no separators are drawn. This is the default value.
5186  *
5187  * Since: 2.6
5188  **/
5189 void
5190 gtk_combo_box_set_row_separator_func (GtkComboBox                 *combo_box,
5191                                       GtkTreeViewRowSeparatorFunc  func,
5192                                       gpointer                     data,
5193                                       GtkDestroyNotify             destroy)
5194 {
5195   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5196
5197   if (combo_box->priv->row_separator_destroy)
5198     (* combo_box->priv->row_separator_destroy) (combo_box->priv->row_separator_data);
5199
5200   combo_box->priv->row_separator_func = func;
5201   combo_box->priv->row_separator_data = data;
5202   combo_box->priv->row_separator_destroy = destroy;
5203
5204   if (combo_box->priv->tree_view)
5205     gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (combo_box->priv->tree_view), 
5206                                           func, data, NULL);
5207
5208   gtk_combo_box_relayout (combo_box);
5209
5210   gtk_widget_queue_draw (GTK_WIDGET (combo_box));
5211 }
5212
5213
5214 /**
5215  * gtk_combo_box_set_focus_on_click:
5216  * @combo: a #GtkComboBox
5217  * @focus_on_click: whether the combo box grabs focus when clicked 
5218  *    with the mouse
5219  * 
5220  * Sets whether the combo box will grab focus when it is clicked with 
5221  * the mouse. Making mouse clicks not grab focus is useful in places 
5222  * like toolbars where you don't want the keyboard focus removed from 
5223  * the main area of the application.
5224  *
5225  * Since: 2.6
5226  **/
5227 void
5228 gtk_combo_box_set_focus_on_click (GtkComboBox *combo_box,
5229                                   gboolean     focus_on_click)
5230 {
5231   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5232   
5233   focus_on_click = focus_on_click != FALSE;
5234
5235   if (combo_box->priv->focus_on_click != focus_on_click)
5236     {
5237       combo_box->priv->focus_on_click = focus_on_click;
5238
5239       if (combo_box->priv->button)
5240         gtk_button_set_focus_on_click (GTK_BUTTON (combo_box->priv->button),
5241                                        focus_on_click);
5242       
5243       g_object_notify (G_OBJECT (combo_box), "focus-on-click");
5244     }
5245 }
5246
5247 /**
5248  * gtk_combo_box_get_focus_on_click:
5249  * @combo: a #GtkComboBox
5250  * 
5251  * Returns whether the combo box grabs focus when it is clicked 
5252  * with the mouse. See gtk_combo_box_set_focus_on_click().
5253  *
5254  * Return value: %TRUE if the combo box grabs focus when it is 
5255  *     clicked with the mouse.
5256  *
5257  * Since: 2.6
5258  **/
5259 gboolean
5260 gtk_combo_box_get_focus_on_click (GtkComboBox *combo_box)
5261 {
5262   g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
5263   
5264   return combo_box->priv->focus_on_click;
5265 }
5266
5267
5268 #define __GTK_COMBO_BOX_C__
5269 #include "gtkaliasdef.c"