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