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