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