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