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