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