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