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