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