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