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