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