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