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