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