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