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