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