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