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