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