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