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