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