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