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