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