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