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