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