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