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