]> Pileus Git - ~andy/gtk/blob - gtk/gtkcombobox.c
Return FALSE, so we don't flush repeatedly. (#319151, Alexander Larsson)
[~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   guint resize_idle_id;
108
109   gint width;
110   GSList *cells;
111
112   guint popup_in_progress : 1;
113   guint destroying : 1;
114   guint add_tearoffs : 1;
115   guint has_frame : 1;
116   guint is_cell_renderer : 1;
117   guint editing_canceled : 1;
118   guint auto_scroll : 1;
119   guint focus_on_click : 1;
120
121   GtkTreeViewRowSeparatorFunc row_separator_func;
122   gpointer                    row_separator_data;
123   GtkDestroyNotify            row_separator_destroy;
124 };
125
126 /* While debugging this evil code, I have learned that
127  * there are actually 4 modes to this widget, which can
128  * be characterized as follows
129  * 
130  * 1) menu mode, no child added
131  *
132  * tree_view -> NULL
133  * cell_view -> GtkCellView, regular child
134  * cell_view_frame -> NULL
135  * button -> GtkToggleButton set_parent to combo
136  * arrow -> GtkArrow set_parent to button
137  * separator -> GtkVSepator set_parent to button
138  * popup_widget -> GtkMenu
139  * popup_window -> NULL
140  * popup_frame -> NULL
141  * scrolled_window -> NULL
142  *
143  * 2) menu mode, child added
144  * 
145  * tree_view -> NULL
146  * cell_view -> NULL 
147  * cell_view_frame -> NULL
148  * button -> GtkToggleButton set_parent to combo
149  * arrow -> GtkArrow, child of button
150  * separator -> NULL
151  * popup_widget -> GtkMenu
152  * popup_window -> NULL
153  * popup_frame -> NULL
154  * scrolled_window -> NULL
155  *
156  * 3) list mode, no child added
157  * 
158  * tree_view -> GtkTreeView, child of popup_frame
159  * cell_view -> GtkCellView, regular child
160  * cell_view_frame -> GtkFrame, set parent to combo
161  * button -> GtkToggleButton, set_parent to combo
162  * arrow -> GtkArrow, child of button
163  * separator -> NULL
164  * popup_widget -> tree_view
165  * popup_window -> GtkWindow
166  * popup_frame -> GtkFrame, child of popup_window
167  * scrolled_window -> GtkScrolledWindow, child of popup_frame
168  *
169  * 4) list mode, child added
170  *
171  * tree_view -> GtkTreeView, child of popup_frame
172  * cell_view -> NULL
173  * cell_view_frame -> NULL
174  * button -> GtkToggleButton, set_parent to combo
175  * arrow -> GtkArrow, child of button
176  * separator -> NULL
177  * popup_widget -> tree_view
178  * popup_window -> GtkWindow
179  * popup_frame -> GtkFrame, child of popup_window
180  * scrolled_window -> GtkScrolledWindow, child of popup_frame
181  * 
182  */
183
184 enum {
185   CHANGED,
186   LAST_SIGNAL
187 };
188
189 enum {
190   PROP_0,
191   PROP_MODEL,
192   PROP_WRAP_WIDTH,
193   PROP_ROW_SPAN_COLUMN,
194   PROP_COLUMN_SPAN_COLUMN,
195   PROP_ACTIVE,
196   PROP_ADD_TEAROFFS,
197   PROP_HAS_FRAME,
198   PROP_FOCUS_ON_CLICK
199 };
200
201 static GtkBinClass *parent_class = NULL;
202 static guint combo_box_signals[LAST_SIGNAL] = {0,};
203
204 #define BONUS_PADDING 4
205 #define SCROLL_TIME  100
206
207 /* common */
208 static void     gtk_combo_box_class_init           (GtkComboBoxClass *klass);
209 static void     gtk_combo_box_cell_layout_init     (GtkCellLayoutIface *iface);
210 static void     gtk_combo_box_cell_editable_init   (GtkCellEditableIface *iface);
211 static void     gtk_combo_box_init                 (GtkComboBox      *combo_box);
212 static void     gtk_combo_box_finalize             (GObject          *object);
213 static void     gtk_combo_box_destroy              (GtkObject        *object);
214
215 static void     gtk_combo_box_set_property         (GObject         *object,
216                                                     guint            prop_id,
217                                                     const GValue    *value,
218                                                     GParamSpec      *spec);
219 static void     gtk_combo_box_get_property         (GObject         *object,
220                                                     guint            prop_id,
221                                                     GValue          *value,
222                                                     GParamSpec      *spec);
223
224 static void     gtk_combo_box_state_changed        (GtkWidget        *widget,
225                                                     GtkStateType      previous);
226 static void     gtk_combo_box_grab_focus           (GtkWidget       *widget);
227 static void     gtk_combo_box_style_set            (GtkWidget       *widget,
228                                                     GtkStyle        *previous);
229 static void     gtk_combo_box_button_toggled       (GtkWidget       *widget,
230                                                     gpointer         data);
231 static void     gtk_combo_box_button_state_changed (GtkWidget       *widget,
232                                                     GtkStateType     previous,
233                                                     gpointer         data);
234 static void     gtk_combo_box_add                  (GtkContainer    *container,
235                                                     GtkWidget       *widget);
236 static void     gtk_combo_box_remove               (GtkContainer    *container,
237                                                     GtkWidget       *widget);
238
239 static ComboCellInfo *gtk_combo_box_get_cell_info  (GtkComboBox      *combo_box,
240                                                     GtkCellRenderer  *cell);
241
242 static void     gtk_combo_box_menu_show            (GtkWidget        *menu,
243                                                     gpointer          user_data);
244 static void     gtk_combo_box_menu_hide            (GtkWidget        *menu,
245                                                     gpointer          user_data);
246
247 static void     gtk_combo_box_set_popup_widget     (GtkComboBox      *combo_box,
248                                                     GtkWidget        *popup);
249 static void     gtk_combo_box_menu_position_below  (GtkMenu          *menu,
250                                                     gint             *x,
251                                                     gint             *y,
252                                                     gint             *push_in,
253                                                     gpointer          user_data);
254 static void     gtk_combo_box_menu_position_over   (GtkMenu          *menu,
255                                                     gint             *x,
256                                                     gint             *y,
257                                                     gint             *push_in,
258                                                     gpointer          user_data);
259 static void     gtk_combo_box_menu_position        (GtkMenu          *menu,
260                                                     gint             *x,
261                                                     gint             *y,
262                                                     gint             *push_in,
263                                                     gpointer          user_data);
264
265 static gint     gtk_combo_box_calc_requested_width (GtkComboBox      *combo_box,
266                                                     GtkTreePath      *path);
267 static void     gtk_combo_box_remeasure            (GtkComboBox      *combo_box);
268
269 static void     gtk_combo_box_unset_model          (GtkComboBox      *combo_box);
270
271 static void     gtk_combo_box_size_request         (GtkWidget        *widget,
272                                                     GtkRequisition   *requisition);
273 static void     gtk_combo_box_size_allocate        (GtkWidget        *widget,
274                                                     GtkAllocation    *allocation);
275 static void     gtk_combo_box_forall               (GtkContainer     *container,
276                                                     gboolean          include_internals,
277                                                     GtkCallback       callback,
278                                                     gpointer          callback_data);
279 static gboolean gtk_combo_box_expose_event         (GtkWidget        *widget,
280                                                     GdkEventExpose   *event);
281 static gboolean gtk_combo_box_scroll_event         (GtkWidget        *widget,
282                                                     GdkEventScroll   *event);
283 static void     gtk_combo_box_set_active_internal  (GtkComboBox      *combo_box,
284                                                     GtkTreePath      *path);
285 static gboolean gtk_combo_box_key_press            (GtkWidget        *widget,
286                                                     GdkEventKey      *event,
287                                                     gpointer          data);
288
289 static void     gtk_combo_box_check_appearance     (GtkComboBox      *combo_box);
290 static gchar *  gtk_combo_box_real_get_active_text (GtkComboBox      *combo_box);
291
292 /* listening to the model */
293 static void     gtk_combo_box_model_row_inserted   (GtkTreeModel     *model,
294                                                     GtkTreePath      *path,
295                                                     GtkTreeIter      *iter,
296                                                     gpointer          user_data);
297 static void     gtk_combo_box_model_row_deleted    (GtkTreeModel     *model,
298                                                     GtkTreePath      *path,
299                                                     gpointer          user_data);
300 static void     gtk_combo_box_model_rows_reordered (GtkTreeModel     *model,
301                                                     GtkTreePath      *path,
302                                                     GtkTreeIter      *iter,
303                                                     gint             *new_order,
304                                                     gpointer          user_data);
305 static void     gtk_combo_box_model_row_changed    (GtkTreeModel     *model,
306                                                     GtkTreePath      *path,
307                                                     GtkTreeIter      *iter,
308                                                     gpointer          data);
309 static void     gtk_combo_box_model_row_expanded   (GtkTreeModel     *model,
310                                                     GtkTreePath      *path,
311                                                     GtkTreeIter      *iter,
312                                                     gpointer          data);
313
314 /* list */
315 static void     gtk_combo_box_list_position        (GtkComboBox      *combo_box, 
316                                                     gint             *x, 
317                                                     gint             *y, 
318                                                     gint             *width,
319                                                     gint             *height);
320 static void     gtk_combo_box_list_setup           (GtkComboBox      *combo_box);
321 static void     gtk_combo_box_list_destroy         (GtkComboBox      *combo_box);
322
323 static gboolean gtk_combo_box_list_button_released (GtkWidget        *widget,
324                                                     GdkEventButton   *event,
325                                                     gpointer          data);
326 static gboolean gtk_combo_box_list_key_press       (GtkWidget        *widget,
327                                                     GdkEventKey      *event,
328                                                     gpointer          data);
329 static gboolean gtk_combo_box_list_enter_notify    (GtkWidget        *widget,
330                                                     GdkEventCrossing *event,
331                                                     gpointer          data);
332 static void     gtk_combo_box_list_auto_scroll     (GtkComboBox   *combo,
333                                                     gint           x,
334                                                     gint           y);
335 static gboolean gtk_combo_box_list_scroll_timeout  (GtkComboBox   *combo);
336 static gboolean gtk_combo_box_list_button_pressed  (GtkWidget        *widget,
337                                                     GdkEventButton   *event,
338                                                     gpointer          data);
339
340 static void     gtk_combo_box_list_row_changed     (GtkTreeModel     *model,
341                                                     GtkTreePath      *path,
342                                                     GtkTreeIter      *iter,
343                                                     gpointer          data);
344 static void     gtk_combo_box_list_popup_resize    (GtkComboBox      *combo_box);
345
346 /* menu */
347 static void     gtk_combo_box_menu_setup           (GtkComboBox      *combo_box,
348                                                     gboolean          add_children);
349 static void     gtk_combo_box_menu_fill            (GtkComboBox      *combo_box);
350 static void     gtk_combo_box_menu_fill_level      (GtkComboBox      *combo_box,
351                                                     GtkWidget        *menu,
352                                                     GtkTreeIter      *iter);
353 static void     gtk_combo_box_menu_destroy         (GtkComboBox      *combo_box);
354
355 static void     gtk_combo_box_relayout_item        (GtkComboBox      *combo_box,
356                                                     GtkWidget        *item,
357                                                     GtkTreeIter      *iter,
358                                                     GtkWidget        *last);
359 static void     gtk_combo_box_relayout             (GtkComboBox      *combo_box);
360
361 static gboolean gtk_combo_box_menu_button_press    (GtkWidget        *widget,
362                                                     GdkEventButton   *event,
363                                                     gpointer          user_data);
364 static void     gtk_combo_box_menu_item_activate   (GtkWidget        *item,
365                                                     gpointer          user_data);
366 static void     gtk_combo_box_menu_row_inserted    (GtkTreeModel     *model,
367                                                     GtkTreePath      *path,
368                                                     GtkTreeIter      *iter,
369                                                     gpointer          user_data);
370 static void     gtk_combo_box_menu_row_deleted     (GtkTreeModel     *model,
371                                                     GtkTreePath      *path,
372                                                     gpointer          user_data);
373 static void     gtk_combo_box_menu_rows_reordered  (GtkTreeModel     *model,
374                                                     GtkTreePath      *path,
375                                                     GtkTreeIter      *iter,
376                                                     gint             *new_order,
377                                                     gpointer          user_data);
378 static void     gtk_combo_box_menu_row_changed     (GtkTreeModel     *model,
379                                                     GtkTreePath      *path,
380                                                     GtkTreeIter      *iter,
381                                                     gpointer          data);
382 static gboolean gtk_combo_box_menu_key_press       (GtkWidget        *widget,
383                                                     GdkEventKey      *event,
384                                                     gpointer          data);
385 static void     gtk_combo_box_menu_popup           (GtkComboBox      *combo_box,
386                                                     guint             button, 
387                                                     guint32           activate_time);
388 static GtkWidget *gtk_cell_view_menu_item_new      (GtkComboBox      *combo_box,
389                                                     GtkTreeModel     *model,
390                                                     GtkTreeIter      *iter);
391
392 /* cell layout */
393 static void     gtk_combo_box_cell_layout_pack_start         (GtkCellLayout         *layout,
394                                                               GtkCellRenderer       *cell,
395                                                               gboolean               expand);
396 static void     gtk_combo_box_cell_layout_pack_end           (GtkCellLayout         *layout,
397                                                               GtkCellRenderer       *cell,
398                                                               gboolean               expand);
399 static void     gtk_combo_box_cell_layout_clear              (GtkCellLayout         *layout);
400 static void     gtk_combo_box_cell_layout_add_attribute      (GtkCellLayout         *layout,
401                                                               GtkCellRenderer       *cell,
402                                                               const gchar           *attribute,
403                                                               gint                   column);
404 static void     gtk_combo_box_cell_layout_set_cell_data_func (GtkCellLayout         *layout,
405                                                               GtkCellRenderer       *cell,
406                                                               GtkCellLayoutDataFunc  func,
407                                                               gpointer               func_data,
408                                                               GDestroyNotify         destroy);
409 static void     gtk_combo_box_cell_layout_clear_attributes   (GtkCellLayout         *layout,
410                                                               GtkCellRenderer       *cell);
411 static void     gtk_combo_box_cell_layout_reorder            (GtkCellLayout         *layout,
412                                                               GtkCellRenderer       *cell,
413                                                               gint                   position);
414 static gboolean gtk_combo_box_mnemonic_activate              (GtkWidget    *widget,
415                                                               gboolean      group_cycling);
416
417 static void     gtk_combo_box_sync_cells                     (GtkComboBox   *combo_box,
418                                                               GtkCellLayout *cell_layout);
419 static void     combo_cell_data_func                         (GtkCellLayout   *cell_layout,
420                                                               GtkCellRenderer *cell,
421                                                               GtkTreeModel    *tree_model,
422                                                               GtkTreeIter     *iter,
423                                                               gpointer         data);
424
425 /* GtkCellEditable method implementations */
426 static void gtk_combo_box_start_editing (GtkCellEditable *cell_editable,
427                                          GdkEvent        *event);
428
429
430 GType
431 gtk_combo_box_get_type (void)
432 {
433   static GType combo_box_type = 0;
434
435   if (!combo_box_type)
436     {
437       static const GTypeInfo combo_box_info =
438         {
439           sizeof (GtkComboBoxClass),
440           NULL, /* base_init */
441           NULL, /* base_finalize */
442           (GClassInitFunc) gtk_combo_box_class_init,
443           NULL, /* class_finalize */
444           NULL, /* class_data */
445           sizeof (GtkComboBox),
446           0,
447           (GInstanceInitFunc) gtk_combo_box_init
448         };
449
450       static const GInterfaceInfo cell_layout_info =
451         {
452           (GInterfaceInitFunc) gtk_combo_box_cell_layout_init,
453           NULL,
454           NULL
455         };
456
457       static const GInterfaceInfo cell_editable_info =
458         {
459           (GInterfaceInitFunc) gtk_combo_box_cell_editable_init,
460           NULL,
461           NULL
462        };
463
464       combo_box_type = g_type_register_static (GTK_TYPE_BIN,
465                                                I_("GtkComboBox"),
466                                                &combo_box_info,
467                                                0);
468
469       g_type_add_interface_static (combo_box_type,
470                                    GTK_TYPE_CELL_LAYOUT,
471                                    &cell_layout_info);
472
473
474       g_type_add_interface_static (combo_box_type,
475                                    GTK_TYPE_CELL_EDITABLE,
476                                    &cell_editable_info);
477       
478
479     }
480
481   return combo_box_type;
482 }
483
484 /* common */
485 static void
486 gtk_combo_box_class_init (GtkComboBoxClass *klass)
487 {
488   GObjectClass *object_class;
489   GtkObjectClass *gtk_object_class;
490   GtkContainerClass *container_class;
491   GtkWidgetClass *widget_class;
492
493   klass->get_active_text = gtk_combo_box_real_get_active_text;
494
495   container_class = (GtkContainerClass *)klass;
496   container_class->forall = gtk_combo_box_forall;
497   container_class->add = gtk_combo_box_add;
498   container_class->remove = gtk_combo_box_remove;
499
500   widget_class = (GtkWidgetClass *)klass;
501   widget_class->size_allocate = gtk_combo_box_size_allocate;
502   widget_class->size_request = gtk_combo_box_size_request;
503   widget_class->expose_event = gtk_combo_box_expose_event;
504   widget_class->scroll_event = gtk_combo_box_scroll_event;
505   widget_class->mnemonic_activate = gtk_combo_box_mnemonic_activate;
506   widget_class->grab_focus = gtk_combo_box_grab_focus;
507   widget_class->style_set = gtk_combo_box_style_set;
508   widget_class->state_changed = gtk_combo_box_state_changed;
509
510   gtk_object_class = (GtkObjectClass *)klass;
511   gtk_object_class->destroy = gtk_combo_box_destroy;
512
513   object_class = (GObjectClass *)klass;
514   object_class->finalize = gtk_combo_box_finalize;
515   object_class->set_property = gtk_combo_box_set_property;
516   object_class->get_property = gtk_combo_box_get_property;
517
518   parent_class = g_type_class_peek_parent (klass);
519
520   /* signals */
521   /**
522    * GtkComboBox::changed:
523    * @widget: the object which received the signal
524    * 
525    * The changed signal gets emitted when the active
526    * item is changed. The can be due to the user selecting
527    * a different item from the list, or due to a 
528    * call to gtk_combo_box_set_active_iter().
529    *
530    * Since: 2.4
531    */
532   combo_box_signals[CHANGED] =
533     g_signal_new (I_("changed"),
534                   G_OBJECT_CLASS_TYPE (klass),
535                   G_SIGNAL_RUN_LAST,
536                   G_STRUCT_OFFSET (GtkComboBoxClass, changed),
537                   NULL, NULL,
538                   g_cclosure_marshal_VOID__VOID,
539                   G_TYPE_NONE, 0);
540
541   /* properties */
542   /**
543    * GtkComboBox:model:
544    *
545    * The model from which the combo box takes the values shown
546    * in the list. 
547    *
548    * Since: 2.4
549    */
550   g_object_class_install_property (object_class,
551                                    PROP_MODEL,
552                                    g_param_spec_object ("model",
553                                                         P_("ComboBox model"),
554                                                         P_("The model for the combo box"),
555                                                         GTK_TYPE_TREE_MODEL,
556                                                         GTK_PARAM_READWRITE));
557
558   /**
559    * GtkComboBox:wrap-width:
560    *
561    * If wrap-width is set to a positive value, the list will be
562    * displayed in multiple columns, the number of columns is
563    * determined by wrap-width.
564    *
565    * Since: 2.4
566    */
567   g_object_class_install_property (object_class,
568                                    PROP_WRAP_WIDTH,
569                                    g_param_spec_int ("wrap-width",
570                                                      P_("Wrap width"),
571                                                      P_("Wrap width for laying out the items in a grid"),
572                                                      0,
573                                                      G_MAXINT,
574                                                      0,
575                                                      GTK_PARAM_READWRITE));
576
577
578   /**
579    * GtkComboBox:row-span-column:
580    *
581    * If this is set to a non-negative value, it must be the index of a column 
582    * of type %G_TYPE_INT in the model. 
583    *
584    * The values of that column are used to determine how many rows a value 
585    * in the list will span. Therefore, the values in the model column pointed 
586    * to by this property must be greater than zero and not larger than wrap-width.
587    *
588    * Since: 2.4
589    */
590   g_object_class_install_property (object_class,
591                                    PROP_ROW_SPAN_COLUMN,
592                                    g_param_spec_int ("row-span-column",
593                                                      P_("Row span column"),
594                                                      P_("TreeModel column containing the row span values"),
595                                                      -1,
596                                                      G_MAXINT,
597                                                      -1,
598                                                      GTK_PARAM_READWRITE));
599
600
601   /**
602    * GtkComboBox:column-span-column:
603    *
604    * If this is set to a non-negative value, it must be the index of a column 
605    * of type %G_TYPE_INT in the model. 
606    *
607    * The values of that column are used to determine how many columns a value 
608    * in the list will span. 
609    *
610    * Since: 2.4
611    */
612   g_object_class_install_property (object_class,
613                                    PROP_COLUMN_SPAN_COLUMN,
614                                    g_param_spec_int ("column-span-column",
615                                                      P_("Column span column"),
616                                                      P_("TreeModel column containing the column span values"),
617                                                      -1,
618                                                      G_MAXINT,
619                                                      -1,
620                                                      GTK_PARAM_READWRITE));
621
622
623   /**
624    * GtkComboBox:active:
625    *
626    * The item which is currently active. If the model is a non-flat treemodel,
627    * and the active item is not an immediate child of the root of the tree,
628    * this property has the value <literal>gtk_tree_path_get_indices (path)[0]</literal>,
629    * where <literal>path</literal> is the #GtkTreePath of the active item.
630    *
631    * Since: 2.4
632    */
633   g_object_class_install_property (object_class,
634                                    PROP_ACTIVE,
635                                    g_param_spec_int ("active",
636                                                      P_("Active item"),
637                                                      P_("The item which is currently active"),
638                                                      -1,
639                                                      G_MAXINT,
640                                                      -1,
641                                                      GTK_PARAM_READWRITE));
642
643   /**
644    * GtkComboBox:add-tearoffs:
645    *
646    * The add-tearoffs property controls whether generated menus 
647    * have tearoff menu items. 
648    *
649    * Note that this only affects menu style combo boxes.
650    *
651    * Since: 2.6
652    */
653   g_object_class_install_property (object_class,
654                                    PROP_ADD_TEAROFFS,
655                                    g_param_spec_boolean ("add-tearoffs",
656                                                          P_("Add tearoffs to menus"),
657                                                          P_("Whether dropdowns should have a tearoff menu item"),
658                                                          FALSE,
659                                                          GTK_PARAM_READWRITE));
660   
661   /**
662    * GtkComboBox:has-frame:
663    *
664    * The has-frame property controls whether a frame
665    * is drawn around the entry.
666    *
667    * Since: 2.6
668    */
669   g_object_class_install_property (object_class,
670                                    PROP_HAS_FRAME,
671                                    g_param_spec_boolean ("has-frame",
672                                                          P_("Has Frame"),
673                                                          P_("Whether the combo box draws a frame around the child"),
674                                                          TRUE,
675                                                          GTK_PARAM_READWRITE));
676   
677   g_object_class_install_property (object_class,
678                                    PROP_FOCUS_ON_CLICK,
679                                    g_param_spec_boolean ("focus-on-click",
680                                                          P_("Focus on click"),
681                                                          P_("Whether the combo box grabs focus when it is clicked with the mouse"),
682                                                          TRUE,
683                                                          GTK_PARAM_READWRITE));
684
685   gtk_widget_class_install_style_property (widget_class,
686                                            g_param_spec_boolean ("appears-as-list",
687                                                                  P_("Appears as list"),
688                                                                  P_("Whether dropdowns should look like lists rather than menus"),
689                                                                  FALSE,
690                                                                  GTK_PARAM_READABLE));
691
692   g_type_class_add_private (object_class, sizeof (GtkComboBoxPrivate));
693 }
694
695 static void
696 gtk_combo_box_cell_layout_init (GtkCellLayoutIface *iface)
697 {
698   iface->pack_start = gtk_combo_box_cell_layout_pack_start;
699   iface->pack_end = gtk_combo_box_cell_layout_pack_end;
700   iface->clear = gtk_combo_box_cell_layout_clear;
701   iface->add_attribute = gtk_combo_box_cell_layout_add_attribute;
702   iface->set_cell_data_func = gtk_combo_box_cell_layout_set_cell_data_func;
703   iface->clear_attributes = gtk_combo_box_cell_layout_clear_attributes;
704   iface->reorder = gtk_combo_box_cell_layout_reorder;
705 }
706
707 static void
708 gtk_combo_box_cell_editable_init (GtkCellEditableIface *iface)
709 {
710   iface->start_editing = gtk_combo_box_start_editing;
711 }
712
713 static void
714 gtk_combo_box_init (GtkComboBox *combo_box)
715 {
716   combo_box->priv = GTK_COMBO_BOX_GET_PRIVATE (combo_box);
717
718   combo_box->priv->cell_view = gtk_cell_view_new ();
719   gtk_widget_set_parent (combo_box->priv->cell_view, GTK_WIDGET (combo_box));
720   GTK_BIN (combo_box)->child = combo_box->priv->cell_view;
721   gtk_widget_show (combo_box->priv->cell_view);
722
723   combo_box->priv->width = 0;
724   combo_box->priv->wrap_width = 0;
725
726   combo_box->priv->active_row = NULL;
727   combo_box->priv->col_column = -1;
728   combo_box->priv->row_column = -1;
729
730   combo_box->priv->add_tearoffs = FALSE;
731   combo_box->priv->has_frame = TRUE;
732   combo_box->priv->is_cell_renderer = FALSE;
733   combo_box->priv->editing_canceled = FALSE;
734   combo_box->priv->auto_scroll = FALSE;
735   combo_box->priv->focus_on_click = TRUE;
736 }
737
738 static void
739 gtk_combo_box_set_property (GObject      *object,
740                             guint         prop_id,
741                             const GValue *value,
742                             GParamSpec   *pspec)
743 {
744   GtkComboBox *combo_box = GTK_COMBO_BOX (object);
745
746   switch (prop_id)
747     {
748       case PROP_MODEL:
749         gtk_combo_box_set_model (combo_box, g_value_get_object (value));
750         break;
751
752       case PROP_WRAP_WIDTH:
753         gtk_combo_box_set_wrap_width (combo_box, g_value_get_int (value));
754         break;
755
756       case PROP_ROW_SPAN_COLUMN:
757         gtk_combo_box_set_row_span_column (combo_box, g_value_get_int (value));
758         break;
759
760       case PROP_COLUMN_SPAN_COLUMN:
761         gtk_combo_box_set_column_span_column (combo_box, g_value_get_int (value));
762         break;
763
764       case PROP_ACTIVE:
765         gtk_combo_box_set_active (combo_box, g_value_get_int (value));
766         break;
767
768       case PROP_ADD_TEAROFFS:
769         gtk_combo_box_set_add_tearoffs (combo_box, g_value_get_boolean (value));
770         break;
771
772       case PROP_HAS_FRAME:
773         combo_box->priv->has_frame = g_value_get_boolean (value);
774         break;
775
776       case PROP_FOCUS_ON_CLICK:
777         gtk_combo_box_set_focus_on_click (combo_box, 
778                                           g_value_get_boolean (value));
779         break;
780
781       default:
782         break;
783     }
784 }
785
786 static void
787 gtk_combo_box_get_property (GObject    *object,
788                             guint       prop_id,
789                             GValue     *value,
790                             GParamSpec *pspec)
791 {
792   GtkComboBox *combo_box = GTK_COMBO_BOX (object);
793
794   switch (prop_id)
795     {
796       case PROP_MODEL:
797         g_value_set_object (value, combo_box->priv->model);
798         break;
799
800       case PROP_WRAP_WIDTH:
801         g_value_set_int (value, combo_box->priv->wrap_width);
802         break;
803
804       case PROP_ROW_SPAN_COLUMN:
805         g_value_set_int (value, combo_box->priv->row_column);
806         break;
807
808       case PROP_COLUMN_SPAN_COLUMN:
809         g_value_set_int (value, combo_box->priv->col_column);
810         break;
811
812       case PROP_ACTIVE:
813         g_value_set_int (value, gtk_combo_box_get_active (combo_box));
814         break;
815
816       case PROP_ADD_TEAROFFS:
817         g_value_set_boolean (value, gtk_combo_box_get_add_tearoffs (combo_box));
818         break;
819
820       case PROP_HAS_FRAME:
821         g_value_set_boolean (value, combo_box->priv->has_frame);
822         break;
823
824       case PROP_FOCUS_ON_CLICK:
825         g_value_set_boolean (value, combo_box->priv->focus_on_click);
826         break;
827
828       default:
829         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
830         break;
831     }
832 }
833
834 static void
835 gtk_combo_box_state_changed (GtkWidget    *widget,
836                              GtkStateType  previous)
837 {
838   GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
839
840   if (GTK_WIDGET_REALIZED (widget))
841     {
842       if (combo_box->priv->tree_view && combo_box->priv->cell_view)
843         gtk_cell_view_set_background_color (GTK_CELL_VIEW (combo_box->priv->cell_view), 
844                                             &widget->style->base[GTK_WIDGET_STATE (widget)]);
845     }
846
847   gtk_widget_queue_draw (widget);
848 }
849
850 static void
851 gtk_combo_box_button_state_changed (GtkWidget    *widget,
852                                     GtkStateType  previous,
853                                     gpointer      data)
854 {
855   GtkComboBox *combo_box = GTK_COMBO_BOX (data);
856
857   if (GTK_WIDGET_REALIZED (widget))
858     {
859       if (!combo_box->priv->tree_view && combo_box->priv->cell_view)
860         {
861           if ((GTK_WIDGET_STATE (widget) == GTK_STATE_INSENSITIVE) !=
862               (GTK_WIDGET_STATE (combo_box->priv->cell_view) == GTK_STATE_INSENSITIVE))
863             gtk_widget_set_sensitive (combo_box->priv->cell_view, GTK_WIDGET_SENSITIVE (widget));
864           
865           gtk_widget_set_state (combo_box->priv->cell_view, 
866                                 GTK_WIDGET_STATE (widget));
867           
868         }
869     }
870
871   gtk_widget_queue_draw (widget);
872 }
873
874 static void
875 gtk_combo_box_check_appearance (GtkComboBox *combo_box)
876 {
877   gboolean appears_as_list;
878
879   /* if wrap_width > 0, then we are in grid-mode and forced to use
880    * unix style
881    */
882   if (combo_box->priv->wrap_width)
883     appears_as_list = FALSE;
884   else
885     gtk_widget_style_get (GTK_WIDGET (combo_box),
886                           "appears-as-list", &appears_as_list,
887                           NULL);
888
889   if (appears_as_list)
890     {
891       /* Destroy all the menu mode widgets, if they exist. */
892       if (GTK_IS_MENU (combo_box->priv->popup_widget))
893         gtk_combo_box_menu_destroy (combo_box);
894
895       /* Create the list mode widgets, if they don't already exist. */
896       if (!GTK_IS_TREE_VIEW (combo_box->priv->tree_view))
897         gtk_combo_box_list_setup (combo_box);
898     }
899   else
900     {
901       /* Destroy all the list mode widgets, if they exist. */
902       if (GTK_IS_TREE_VIEW (combo_box->priv->tree_view))
903         gtk_combo_box_list_destroy (combo_box);
904
905       /* Create the menu mode widgets, if they don't already exist. */
906       if (!GTK_IS_MENU (combo_box->priv->popup_widget))
907         gtk_combo_box_menu_setup (combo_box, TRUE);
908     }
909 }
910
911 static void
912 gtk_combo_box_style_set (GtkWidget *widget,
913                          GtkStyle  *previous)
914 {
915   GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
916
917   gtk_combo_box_check_appearance (combo_box);
918
919   if (combo_box->priv->tree_view && combo_box->priv->cell_view)
920     gtk_cell_view_set_background_color (GTK_CELL_VIEW (combo_box->priv->cell_view), 
921                                         &widget->style->base[GTK_WIDGET_STATE (widget)]);
922 }
923
924 static void
925 gtk_combo_box_button_toggled (GtkWidget *widget,
926                               gpointer   data)
927 {
928   GtkComboBox *combo_box = GTK_COMBO_BOX (data);
929
930   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
931     {
932       if (!combo_box->priv->popup_in_progress)
933         gtk_combo_box_popup (combo_box);
934     }
935   else
936     gtk_combo_box_popdown (combo_box);
937 }
938
939 static void
940 gtk_combo_box_add (GtkContainer *container,
941                    GtkWidget    *widget)
942 {
943   GtkComboBox *combo_box = GTK_COMBO_BOX (container);
944
945   if (combo_box->priv->cell_view && combo_box->priv->cell_view->parent)
946     {
947       gtk_widget_unparent (combo_box->priv->cell_view);
948       GTK_BIN (container)->child = NULL;
949       gtk_widget_queue_resize (GTK_WIDGET (container));
950     }
951   
952   gtk_widget_set_parent (widget, GTK_WIDGET (container));
953   GTK_BIN (container)->child = widget;
954
955   if (combo_box->priv->cell_view &&
956       widget != combo_box->priv->cell_view)
957     {
958       /* since the cell_view was unparented, it's gone now */
959       combo_box->priv->cell_view = NULL;
960
961       if (!combo_box->priv->tree_view && combo_box->priv->separator)
962         {
963           gtk_container_remove (GTK_CONTAINER (combo_box->priv->separator->parent),
964                                 combo_box->priv->separator);
965           combo_box->priv->separator = NULL;
966
967           gtk_widget_queue_resize (GTK_WIDGET (container));
968         }
969       else if (combo_box->priv->cell_view_frame)
970         {
971           gtk_widget_unparent (combo_box->priv->cell_view_frame);
972           combo_box->priv->cell_view_frame = NULL;
973           combo_box->priv->box = NULL;
974         }
975     }
976 }
977
978 static void
979 gtk_combo_box_remove (GtkContainer *container,
980                       GtkWidget    *widget)
981 {
982   GtkComboBox *combo_box = GTK_COMBO_BOX (container);
983   GtkTreePath *path;
984   gboolean appears_as_list;
985
986   gtk_widget_unparent (widget);
987   GTK_BIN (container)->child = NULL;
988
989   if (combo_box->priv->destroying)
990     return;
991
992   gtk_widget_queue_resize (GTK_WIDGET (container));
993
994   if (!combo_box->priv->tree_view)
995     appears_as_list = FALSE;
996   else
997     appears_as_list = TRUE;
998   
999   if (appears_as_list)
1000     gtk_combo_box_list_destroy (combo_box);
1001   else if (GTK_IS_MENU (combo_box->priv->popup_widget))
1002     {
1003       gtk_combo_box_menu_destroy (combo_box);
1004       gtk_menu_detach (GTK_MENU (combo_box->priv->popup_widget));
1005       combo_box->priv->popup_widget = NULL;
1006     }
1007
1008   if (!combo_box->priv->cell_view)
1009     {
1010       combo_box->priv->cell_view = gtk_cell_view_new ();
1011       gtk_widget_set_parent (combo_box->priv->cell_view, GTK_WIDGET (container));
1012       GTK_BIN (container)->child = combo_box->priv->cell_view;
1013       
1014       gtk_widget_show (combo_box->priv->cell_view);
1015       gtk_cell_view_set_model (GTK_CELL_VIEW (combo_box->priv->cell_view),
1016                                combo_box->priv->model);
1017       gtk_combo_box_sync_cells (combo_box, GTK_CELL_LAYOUT (combo_box->priv->cell_view));
1018     }
1019
1020
1021   if (appears_as_list)
1022     gtk_combo_box_list_setup (combo_box); 
1023   else
1024     gtk_combo_box_menu_setup (combo_box, TRUE);
1025
1026   if (gtk_tree_row_reference_valid (combo_box->priv->active_row))
1027     {
1028       path = gtk_tree_row_reference_get_path (combo_box->priv->active_row);
1029       gtk_combo_box_set_active_internal (combo_box, path);
1030       gtk_tree_path_free (path);
1031     }
1032   else
1033     gtk_combo_box_set_active_internal (combo_box, NULL);
1034 }
1035
1036 static ComboCellInfo *
1037 gtk_combo_box_get_cell_info (GtkComboBox     *combo_box,
1038                              GtkCellRenderer *cell)
1039 {
1040   GSList *i;
1041
1042   for (i = combo_box->priv->cells; i; i = i->next)
1043     {
1044       ComboCellInfo *info = (ComboCellInfo *)i->data;
1045
1046       if (info && info->cell == cell)
1047         return info;
1048     }
1049
1050   return NULL;
1051 }
1052
1053 static void
1054 gtk_combo_box_menu_show (GtkWidget *menu,
1055                          gpointer   user_data)
1056 {
1057   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1058
1059   combo_box->priv->popup_in_progress = TRUE;
1060   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
1061                                 TRUE);
1062   combo_box->priv->popup_in_progress = FALSE;
1063 }
1064
1065 static void
1066 gtk_combo_box_menu_hide (GtkWidget *menu,
1067                          gpointer   user_data)
1068 {
1069   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1070
1071   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
1072                                 FALSE);
1073 }
1074
1075 static void
1076 gtk_combo_box_detacher (GtkWidget *widget,
1077                         GtkMenu   *menu)
1078 {
1079   GtkComboBox *combo_box;
1080
1081   g_return_if_fail (GTK_IS_COMBO_BOX (widget));
1082
1083   combo_box = GTK_COMBO_BOX (widget);
1084   g_return_if_fail (combo_box->priv->popup_widget == (GtkWidget*) menu);
1085
1086   g_signal_handlers_disconnect_by_func (menu->toplevel,
1087                                         gtk_combo_box_menu_show,
1088                                         combo_box);
1089   g_signal_handlers_disconnect_by_func (menu->toplevel,
1090                                         gtk_combo_box_menu_hide,
1091                                         combo_box);
1092   
1093   combo_box->priv->popup_widget = NULL;
1094 }
1095
1096 static void
1097 gtk_combo_box_set_popup_widget (GtkComboBox *combo_box,
1098                                 GtkWidget   *popup)
1099 {
1100   if (GTK_IS_MENU (combo_box->priv->popup_widget))
1101     {
1102       gtk_menu_detach (GTK_MENU (combo_box->priv->popup_widget));
1103       combo_box->priv->popup_widget = NULL;
1104     }
1105   else if (combo_box->priv->popup_widget)
1106     {
1107       gtk_container_remove (GTK_CONTAINER (combo_box->priv->popup_frame),
1108                             combo_box->priv->popup_widget);
1109       g_object_unref (combo_box->priv->popup_widget);
1110       combo_box->priv->popup_widget = NULL;
1111     }
1112
1113   if (GTK_IS_MENU (popup))
1114     {
1115       if (combo_box->priv->popup_window)
1116         {
1117           gtk_widget_destroy (combo_box->priv->popup_window);
1118           combo_box->priv->popup_window = NULL;
1119           combo_box->priv->popup_frame = NULL;
1120         }
1121
1122       combo_box->priv->popup_widget = popup;
1123
1124       /* 
1125        * Note that we connect to show/hide on the toplevel, not the
1126        * menu itself, since the menu is not shown/hidden when it is
1127        * popped up while torn-off.
1128        */
1129       g_signal_connect (GTK_MENU (popup)->toplevel, "show",
1130                         G_CALLBACK (gtk_combo_box_menu_show), combo_box);
1131       g_signal_connect (GTK_MENU (popup)->toplevel, "hide",
1132                         G_CALLBACK (gtk_combo_box_menu_hide), combo_box);
1133
1134       gtk_menu_attach_to_widget (GTK_MENU (popup),
1135                                  GTK_WIDGET (combo_box),
1136                                  gtk_combo_box_detacher);
1137     }
1138   else
1139     {
1140       if (!combo_box->priv->popup_window)
1141         {
1142           GtkWidget *toplevel;
1143           
1144           combo_box->priv->popup_window = gtk_window_new (GTK_WINDOW_POPUP);
1145
1146           toplevel = gtk_widget_get_toplevel (GTK_WIDGET (combo_box));
1147           if (GTK_IS_WINDOW (toplevel))
1148             gtk_window_group_add_window (_gtk_window_get_group (GTK_WINDOW (toplevel)), 
1149                                          GTK_WINDOW (combo_box->priv->popup_window));
1150
1151           gtk_window_set_resizable (GTK_WINDOW (combo_box->priv->popup_window), FALSE);
1152           gtk_window_set_screen (GTK_WINDOW (combo_box->priv->popup_window),
1153                                  gtk_widget_get_screen (GTK_WIDGET (combo_box)));
1154
1155           combo_box->priv->popup_frame = gtk_frame_new (NULL);
1156           gtk_frame_set_shadow_type (GTK_FRAME (combo_box->priv->popup_frame),
1157                                      GTK_SHADOW_ETCHED_IN);
1158           gtk_container_add (GTK_CONTAINER (combo_box->priv->popup_window),
1159                              combo_box->priv->popup_frame);
1160
1161           gtk_widget_show (combo_box->priv->popup_frame);
1162
1163           combo_box->priv->scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1164           
1165           gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window),
1166                                           GTK_POLICY_NEVER,
1167                                           GTK_POLICY_NEVER);
1168           gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window),
1169                                                GTK_SHADOW_NONE);
1170
1171           gtk_widget_show (combo_box->priv->scrolled_window);
1172           
1173           gtk_container_add (GTK_CONTAINER (combo_box->priv->popup_frame),
1174                              combo_box->priv->scrolled_window);
1175         }
1176
1177       gtk_container_add (GTK_CONTAINER (combo_box->priv->scrolled_window),
1178                          popup);
1179
1180       gtk_widget_show (popup);
1181       g_object_ref (popup);
1182       combo_box->priv->popup_widget = popup;
1183     }
1184 }
1185
1186 static void
1187 gtk_combo_box_menu_position_below (GtkMenu  *menu,
1188                                    gint     *x,
1189                                    gint     *y,
1190                                    gint     *push_in,
1191                                    gpointer  user_data)
1192 {
1193   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1194   gint sx, sy;
1195   GtkWidget *child;
1196   GtkRequisition req;
1197   GdkScreen *screen;
1198   gint monitor_num;
1199   GdkRectangle monitor;
1200   
1201   /* FIXME: is using the size request here broken? */
1202    child = GTK_BIN (combo_box)->child;
1203    
1204    gdk_window_get_origin (child->window, &sx, &sy);
1205    
1206    if (GTK_WIDGET_NO_WINDOW (child))
1207       {
1208         sx += child->allocation.x;
1209         sy += child->allocation.y;
1210       }
1211
1212    gtk_widget_size_request (GTK_WIDGET (menu), &req);
1213
1214    if (gtk_widget_get_direction (GTK_WIDGET (combo_box)) == GTK_TEXT_DIR_LTR)
1215      *x = sx;
1216    else
1217      *x = sx + child->allocation.width - req.width;
1218    *y = sy;
1219
1220   screen = gtk_widget_get_screen (GTK_WIDGET (combo_box));
1221   monitor_num = gdk_screen_get_monitor_at_window (screen, 
1222                                                   GTK_WIDGET (combo_box)->window);
1223   gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1224   
1225   if (*x < monitor.x)
1226     *x = monitor.x;
1227   else if (*x + req.width > monitor.x + monitor.width)
1228     *x = monitor.x + monitor.width - req.width;
1229   
1230   if (monitor.y + monitor.height - *y - child->allocation.height >= req.height)
1231     *y += child->allocation.height;
1232   else if (*y - monitor.y >= req.height)
1233     *y -= req.height;
1234   else if (monitor.y + monitor.height - *y - child->allocation.height > *y - monitor.y) 
1235     *y += child->allocation.height;
1236   else
1237     *y -= req.height;
1238
1239    *push_in = FALSE;
1240 }
1241
1242 static void
1243 gtk_combo_box_menu_position_over (GtkMenu  *menu,
1244                                   gint     *x,
1245                                   gint     *y,
1246                                   gboolean *push_in,
1247                                   gpointer  user_data)
1248 {
1249   GtkComboBox *combo_box;
1250   GtkWidget *active;
1251   GtkWidget *child;
1252   GtkWidget *widget;
1253   GtkRequisition requisition;
1254   GList *children;
1255   gint screen_width;
1256   gint menu_xpos;
1257   gint menu_ypos;
1258   gint menu_width;
1259
1260   g_return_if_fail (GTK_IS_COMBO_BOX (user_data));
1261   
1262   combo_box = GTK_COMBO_BOX (user_data);
1263   widget = GTK_WIDGET (combo_box);
1264
1265   gtk_widget_get_child_requisition (GTK_WIDGET (menu), &requisition);
1266   menu_width = requisition.width;
1267
1268   active = gtk_menu_get_active (GTK_MENU (combo_box->priv->popup_widget));
1269   gdk_window_get_origin (widget->window, &menu_xpos, &menu_ypos);
1270
1271   menu_xpos += widget->allocation.x;
1272   menu_ypos += widget->allocation.y + widget->allocation.height / 2 - 2;
1273
1274   if (active != NULL)
1275     {
1276       gtk_widget_get_child_requisition (active, &requisition);
1277       menu_ypos -= requisition.height / 2;
1278     }
1279
1280   children = GTK_MENU_SHELL (combo_box->priv->popup_widget)->children;
1281   while (children)
1282     {
1283       child = children->data;
1284
1285       if (active == child)
1286         break;
1287
1288       if (GTK_WIDGET_VISIBLE (child))
1289         {
1290           gtk_widget_get_child_requisition (child, &requisition);
1291           menu_ypos -= requisition.height;
1292         }
1293
1294       children = children->next;
1295     }
1296
1297   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
1298     menu_xpos = menu_xpos + widget->allocation.width - menu_width;
1299
1300   /* Clamp the position on screen */
1301   screen_width = gdk_screen_get_width (gtk_widget_get_screen (widget));
1302   
1303   if (menu_xpos < 0)
1304     menu_xpos = 0;
1305   else if ((menu_xpos + menu_width) > screen_width)
1306     menu_xpos -= ((menu_xpos + menu_width) - screen_width);
1307
1308   *x = menu_xpos;
1309   *y = menu_ypos;
1310
1311   *push_in = TRUE;
1312 }
1313
1314 static void
1315 gtk_combo_box_menu_position (GtkMenu  *menu,
1316                              gint     *x,
1317                              gint     *y,
1318                              gint     *push_in,
1319                              gpointer  user_data)
1320 {
1321   GtkComboBox *combo_box;
1322   GtkWidget *menu_item;
1323
1324   combo_box = GTK_COMBO_BOX (user_data);
1325
1326   if (combo_box->priv->wrap_width > 0 || combo_box->priv->cell_view == NULL)    
1327     gtk_combo_box_menu_position_below (menu, x, y, push_in, user_data);
1328   else
1329     {
1330       /* FIXME handle nested menus better */
1331       menu_item = gtk_menu_get_active (GTK_MENU (combo_box->priv->popup_widget));
1332       if (menu_item)
1333         gtk_menu_shell_select_item (GTK_MENU_SHELL (combo_box->priv->popup_widget), 
1334                                     menu_item);
1335
1336       gtk_combo_box_menu_position_over (menu, x, y, push_in, user_data);
1337     }
1338
1339 }
1340
1341 static void
1342 gtk_combo_box_list_position (GtkComboBox *combo_box, 
1343                              gint        *x, 
1344                              gint        *y, 
1345                              gint        *width,
1346                              gint        *height)
1347 {
1348   GtkWidget *sample;
1349   GdkScreen *screen;
1350   gint monitor_num;
1351   GdkRectangle monitor;
1352   GtkRequisition popup_req;
1353   GtkPolicyType hpolicy, vpolicy;
1354   
1355   sample = GTK_BIN (combo_box)->child;
1356
1357   gdk_window_get_origin (sample->window, x, y);
1358
1359   if (GTK_WIDGET_NO_WINDOW (sample))
1360     {
1361       *x += sample->allocation.x;
1362       *y += sample->allocation.y;
1363     }
1364   
1365   *width = sample->allocation.width;
1366   
1367   if (combo_box->priv->cell_view_frame && combo_box->priv->has_frame)
1368     {
1369        *x -= GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
1370              GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness;
1371        *width += 2 * (GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
1372             GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness);
1373     }
1374
1375   hpolicy = vpolicy = GTK_POLICY_NEVER;
1376   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window),
1377                                   hpolicy, vpolicy);
1378   gtk_widget_size_request (combo_box->priv->popup_frame, &popup_req);
1379
1380   if (popup_req.width > *width)
1381     {
1382       hpolicy = GTK_POLICY_ALWAYS;
1383       gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window),
1384                                       hpolicy, vpolicy);
1385       gtk_widget_size_request (combo_box->priv->popup_frame, &popup_req);
1386     }
1387
1388   *height = popup_req.height;
1389
1390   screen = gtk_widget_get_screen (GTK_WIDGET (combo_box));
1391   monitor_num = gdk_screen_get_monitor_at_window (screen, 
1392                                                   GTK_WIDGET (combo_box)->window);
1393   gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1394
1395   if (*x < monitor.x)
1396     *x = monitor.x;
1397   else if (*x + *width > monitor.x + monitor.width)
1398     *x = monitor.x + monitor.width - *width;
1399   
1400   if (*y + sample->allocation.height + *height <= monitor.y + monitor.height)
1401     *y += sample->allocation.height;
1402   else if (*y - *height >= monitor.y)
1403     *y -= *height;
1404   else if (monitor.y + monitor.height - (*y + sample->allocation.height) > *y - monitor.y)
1405     {
1406       *y += sample->allocation.height;
1407       *height = monitor.y + monitor.height - *y;
1408     }
1409   else 
1410     {
1411       *height = *y - monitor.y;
1412       *y = monitor.y;
1413     }
1414
1415   if (popup_req.height > *height)
1416     {
1417       vpolicy = GTK_POLICY_ALWAYS;
1418       
1419       gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window),
1420                                       hpolicy, vpolicy);
1421     }
1422
1423
1424 static gboolean
1425 cell_view_is_sensitive (GtkCellView *cell_view)
1426 {
1427   GList *cells, *list;
1428   gboolean sensitive;
1429   
1430   cells = gtk_cell_view_get_cell_renderers (cell_view);
1431
1432   sensitive = FALSE;
1433   list = cells;
1434   while (list)
1435     {
1436       g_object_get (list->data, "sensitive", &sensitive, NULL);
1437       
1438       if (sensitive)
1439         break;
1440
1441       list = list->next;
1442     }
1443   g_list_free (cells);
1444
1445   return sensitive;
1446 }
1447
1448 static gboolean
1449 tree_column_row_is_sensitive (GtkComboBox *combo_box,
1450                               GtkTreeIter *iter)
1451 {
1452   GList *cells, *list;
1453   gboolean sensitive;
1454
1455   if (!combo_box->priv->column)
1456     return TRUE;
1457
1458   if (combo_box->priv->row_separator_func)
1459     {
1460       if ((*combo_box->priv->row_separator_func) (combo_box->priv->model, iter,
1461                                                   combo_box->priv->row_separator_data))
1462         return FALSE;
1463     }
1464
1465   gtk_tree_view_column_cell_set_cell_data (combo_box->priv->column,
1466                                            combo_box->priv->model,
1467                                            iter, FALSE, FALSE);
1468
1469   cells = gtk_tree_view_column_get_cell_renderers (combo_box->priv->column);
1470
1471   sensitive = FALSE;
1472   list = cells;
1473   while (list)
1474     {
1475       g_object_get (list->data, "sensitive", &sensitive, NULL);
1476       
1477       if (sensitive)
1478         break;
1479
1480       list = list->next;
1481     }
1482   g_list_free (cells);
1483
1484   return sensitive;
1485 }
1486
1487 static void
1488 update_menu_sensitivity (GtkComboBox *combo_box,
1489                          GtkWidget   *menu)
1490 {
1491   GList *children, *child;
1492   GtkWidget *item, *submenu, *separator;
1493   GtkWidget *cell_view;
1494   gboolean sensitive;
1495
1496   if (!combo_box->priv->model)
1497     return;
1498
1499   children = gtk_container_get_children (GTK_CONTAINER (menu));
1500
1501   for (child = children; child; child = child->next)
1502     {
1503       item = GTK_WIDGET (child->data);
1504       cell_view = GTK_BIN (item)->child;
1505
1506       if (!GTK_IS_CELL_VIEW (cell_view))
1507         continue;
1508       
1509       submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (item));
1510       if (submenu != NULL)
1511         {
1512           gtk_widget_set_sensitive (item, TRUE);
1513           update_menu_sensitivity (combo_box, submenu);
1514         }
1515       else
1516         {
1517           sensitive = cell_view_is_sensitive (GTK_CELL_VIEW (cell_view));
1518
1519           if (menu != combo_box->priv->popup_widget && child == children)
1520             {
1521               separator = GTK_WIDGET (child->next->data);
1522               g_object_set (item, "visible", sensitive, NULL);
1523               g_object_set (separator, "visible", sensitive, NULL);
1524             }
1525           else
1526             gtk_widget_set_sensitive (item, sensitive);
1527         }
1528     }
1529
1530   g_list_free (children);
1531 }
1532
1533 static void 
1534 gtk_combo_box_menu_popup (GtkComboBox *combo_box,
1535                           guint        button, 
1536                           guint32      activate_time)
1537 {
1538   GtkTreePath *path;
1539   gint active_item;
1540   GtkRequisition requisition;
1541   gint width;
1542   
1543   update_menu_sensitivity (combo_box, combo_box->priv->popup_widget);
1544
1545   active_item = -1;
1546   if (gtk_tree_row_reference_valid (combo_box->priv->active_row))
1547     {
1548       path = gtk_tree_row_reference_get_path (combo_box->priv->active_row);
1549       active_item = gtk_tree_path_get_indices (path)[0];
1550       gtk_tree_path_free (path);
1551       
1552       if (combo_box->priv->add_tearoffs)
1553         active_item++;
1554     }
1555
1556   /* FIXME handle nested menus better */
1557   gtk_menu_set_active (GTK_MENU (combo_box->priv->popup_widget), active_item);
1558   
1559   if (combo_box->priv->wrap_width == 0)
1560     {
1561       width = GTK_WIDGET (combo_box)->allocation.width;
1562       gtk_widget_set_size_request (combo_box->priv->popup_widget, -1, -1);
1563       gtk_widget_size_request (combo_box->priv->popup_widget, &requisition);
1564       
1565       gtk_widget_set_size_request (combo_box->priv->popup_widget,
1566                                    MAX (width, requisition.width), -1);
1567     }
1568   
1569   gtk_menu_popup (GTK_MENU (combo_box->priv->popup_widget),
1570                   NULL, NULL,
1571                   gtk_combo_box_menu_position, combo_box,
1572                   button, activate_time);
1573 }
1574
1575 static gboolean
1576 popup_grab_on_window (GdkWindow *window,
1577                       guint32    activate_time,
1578                       gboolean   grab_keyboard)
1579 {
1580   if ((gdk_pointer_grab (window, TRUE,
1581                          GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1582                          GDK_POINTER_MOTION_MASK,
1583                          NULL, NULL, activate_time) == 0))
1584     {
1585       if (!grab_keyboard ||
1586           gdk_keyboard_grab (window, TRUE,
1587                              activate_time) == 0)
1588         return TRUE;
1589       else
1590         {
1591           gdk_display_pointer_ungrab (gdk_drawable_get_display (window),
1592                                       activate_time);
1593           return FALSE;
1594         }
1595     }
1596
1597   return FALSE;
1598 }
1599
1600 /**
1601  * gtk_combo_box_popup:
1602  * @combo_box: a #GtkComboBox
1603  * 
1604  * Pops up the menu or dropdown list of @combo_box. 
1605  *
1606  * This function is mostly intended for use by accessibility technologies;
1607  * applications should have little use for it.
1608  *
1609  * Since: 2.4
1610  **/
1611 void
1612 gtk_combo_box_popup (GtkComboBox *combo_box)
1613 {
1614   gint x, y, width, height;
1615   GtkTreePath *path, *ppath;
1616   GtkWidget *toplevel;
1617
1618   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
1619
1620   if (!GTK_WIDGET_REALIZED (combo_box))
1621     return;
1622
1623   if (GTK_WIDGET_MAPPED (combo_box->priv->popup_widget))
1624     return;
1625
1626   if (GTK_IS_MENU (combo_box->priv->popup_widget))
1627     {
1628       gtk_combo_box_menu_popup (combo_box, 0, 0);
1629       return;
1630     }
1631
1632   toplevel = gtk_widget_get_toplevel (GTK_WIDGET (combo_box));
1633   if (GTK_IS_WINDOW (toplevel))
1634     gtk_window_group_add_window (_gtk_window_get_group (GTK_WINDOW (toplevel)), 
1635                                  GTK_WINDOW (combo_box->priv->popup_window));
1636
1637   gtk_widget_show_all (combo_box->priv->popup_frame);
1638   gtk_combo_box_list_position (combo_box, &x, &y, &width, &height);
1639   
1640   gtk_widget_set_size_request (combo_box->priv->popup_window, width, height);  
1641   gtk_window_move (GTK_WINDOW (combo_box->priv->popup_window), x, y);
1642
1643   if (gtk_tree_row_reference_valid (combo_box->priv->active_row))
1644     {
1645       path = gtk_tree_row_reference_get_path (combo_box->priv->active_row);
1646       ppath = gtk_tree_path_copy (path);
1647       if (gtk_tree_path_up (ppath))
1648         gtk_tree_view_expand_to_path (GTK_TREE_VIEW (combo_box->priv->tree_view),
1649                                       ppath);
1650       gtk_tree_view_set_cursor (GTK_TREE_VIEW (combo_box->priv->tree_view),
1651                                 path, NULL, FALSE);
1652       gtk_tree_path_free (path);
1653       gtk_tree_path_free (ppath);
1654     }
1655   gtk_tree_view_set_hover_expand (GTK_TREE_VIEW (combo_box->priv->tree_view), 
1656                                   TRUE);
1657   
1658   /* popup */
1659   gtk_widget_show (combo_box->priv->popup_window);
1660
1661   gtk_widget_grab_focus (combo_box->priv->popup_window);
1662   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
1663                                 TRUE);
1664
1665   if (!GTK_WIDGET_HAS_FOCUS (combo_box->priv->tree_view))
1666     gtk_widget_grab_focus (combo_box->priv->tree_view);
1667
1668   if (!popup_grab_on_window (combo_box->priv->popup_window->window,
1669                              GDK_CURRENT_TIME, TRUE))
1670     {
1671       gtk_widget_hide (combo_box->priv->popup_window);
1672       return;
1673     }
1674
1675   gtk_grab_add (combo_box->priv->popup_window);
1676 }
1677
1678 /**
1679  * gtk_combo_box_popdown:
1680  * @combo_box: a #GtkComboBox
1681  * 
1682  * Hides the menu or dropdown list of @combo_box.
1683  *
1684  * This function is mostly intended for use by accessibility technologies;
1685  * applications should have little use for it.
1686  *
1687  * Since: 2.4
1688  **/
1689 void
1690 gtk_combo_box_popdown (GtkComboBox *combo_box)
1691 {
1692   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
1693
1694   if (GTK_IS_MENU (combo_box->priv->popup_widget))
1695     {
1696       gtk_menu_popdown (GTK_MENU (combo_box->priv->popup_widget));
1697       return;
1698     }
1699
1700   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (combo_box)))
1701     return;
1702
1703   gtk_grab_remove (combo_box->priv->popup_window);
1704   gtk_widget_hide_all (combo_box->priv->popup_window);
1705   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
1706                                 FALSE);
1707 }
1708
1709 static gint
1710 gtk_combo_box_calc_requested_width (GtkComboBox *combo_box,
1711                                     GtkTreePath *path)
1712 {
1713   gint padding;
1714   GtkRequisition req;
1715
1716   if (combo_box->priv->cell_view)
1717     gtk_widget_style_get (combo_box->priv->cell_view,
1718                           "focus-line-width", &padding,
1719                           NULL);
1720   else
1721     padding = 0;
1722
1723   /* add some pixels for good measure */
1724   padding += BONUS_PADDING;
1725
1726   if (combo_box->priv->cell_view)
1727     gtk_cell_view_get_size_of_row (GTK_CELL_VIEW (combo_box->priv->cell_view),
1728                                    path, &req);
1729   else
1730     req.width = 0;
1731
1732   return req.width + padding;
1733 }
1734
1735 static void
1736 gtk_combo_box_remeasure (GtkComboBox *combo_box)
1737 {
1738   GtkTreeIter iter;
1739   GtkTreePath *path;
1740
1741   if (!combo_box->priv->model ||
1742       !gtk_tree_model_get_iter_first (combo_box->priv->model, &iter))
1743     return;
1744
1745   combo_box->priv->width = 0;
1746
1747   path = gtk_tree_path_new_from_indices (0, -1);
1748
1749   do
1750     {
1751       GtkRequisition req;
1752
1753       if (combo_box->priv->cell_view)
1754         gtk_cell_view_get_size_of_row (GTK_CELL_VIEW (combo_box->priv->cell_view), 
1755                                        path, &req);
1756       else
1757         req.width = 0;
1758
1759       combo_box->priv->width = MAX (combo_box->priv->width, req.width);
1760
1761       gtk_tree_path_next (path);
1762     }
1763   while (gtk_tree_model_iter_next (combo_box->priv->model, &iter));
1764
1765   gtk_tree_path_free (path);
1766 }
1767
1768 static void
1769 gtk_combo_box_size_request (GtkWidget      *widget,
1770                             GtkRequisition *requisition)
1771 {
1772   gint width, height;
1773   gint focus_width, focus_pad;
1774   GtkRequisition bin_req;
1775
1776   GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1777
1778   gtk_combo_box_check_appearance (combo_box);
1779
1780   /* common */
1781   gtk_widget_size_request (GTK_BIN (widget)->child, &bin_req);
1782   gtk_combo_box_remeasure (combo_box);
1783   bin_req.width = MAX (bin_req.width, combo_box->priv->width);
1784
1785   gtk_widget_style_get (GTK_WIDGET (widget),
1786                         "focus-line-width", &focus_width,
1787                         "focus-padding", &focus_pad,
1788                         NULL);
1789
1790   if (!combo_box->priv->tree_view)
1791     {
1792       /* menu mode */
1793
1794       if (combo_box->priv->cell_view)
1795         {
1796           GtkRequisition button_req, sep_req, arrow_req;
1797           gint border_width, xthickness, ythickness;
1798
1799           gtk_widget_size_request (combo_box->priv->button, &button_req);
1800           border_width = GTK_CONTAINER (combo_box)->border_width;
1801           xthickness = combo_box->priv->button->style->xthickness;
1802           ythickness = combo_box->priv->button->style->ythickness;
1803
1804           bin_req.width = MAX (bin_req.width, combo_box->priv->width);
1805
1806           gtk_widget_size_request (combo_box->priv->separator, &sep_req);
1807           gtk_widget_size_request (combo_box->priv->arrow, &arrow_req);
1808
1809           height = MAX (sep_req.height, arrow_req.height);
1810           height = MAX (height, bin_req.height);
1811
1812           width = bin_req.width + sep_req.width + arrow_req.width;
1813
1814           height += 2*(border_width + ythickness + focus_width + focus_pad);
1815           width  += 2*(border_width + xthickness + focus_width + focus_pad);
1816
1817           requisition->width = width;
1818           requisition->height = height;
1819         }
1820       else
1821         {
1822           GtkRequisition but_req;
1823
1824           gtk_widget_size_request (combo_box->priv->button, &but_req);
1825
1826           requisition->width = bin_req.width + but_req.width;
1827           requisition->height = MAX (bin_req.height, but_req.height);
1828         }
1829     }
1830   else
1831     {
1832       /* list mode */
1833       GtkRequisition button_req, frame_req;
1834
1835       /* sample + frame */
1836       *requisition = bin_req;
1837
1838       requisition->width += 2 * focus_width;
1839       
1840       if (combo_box->priv->cell_view_frame)
1841         {
1842           gtk_widget_size_request (combo_box->priv->cell_view_frame, &frame_req);
1843           if (combo_box->priv->has_frame)
1844             {
1845               requisition->width += 2 *
1846                 (GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
1847                  GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness);
1848               requisition->height += 2 *
1849                 (GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
1850                  GTK_WIDGET (combo_box->priv->cell_view_frame)->style->ythickness);
1851             }
1852         }
1853
1854       /* the button */
1855       gtk_widget_size_request (combo_box->priv->button, &button_req);
1856
1857       requisition->height = MAX (requisition->height, button_req.height);
1858       requisition->width += button_req.width;
1859     }
1860 }
1861
1862 static void
1863 gtk_combo_box_size_allocate (GtkWidget     *widget,
1864                              GtkAllocation *allocation)
1865 {
1866   GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1867   gint focus_width, focus_pad;
1868   GtkAllocation child;
1869   GtkRequisition req;
1870   gboolean is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
1871
1872   gtk_combo_box_check_appearance (combo_box);
1873
1874   widget->allocation = *allocation;
1875
1876   gtk_widget_style_get (GTK_WIDGET (widget),
1877                         "focus-line-width", &focus_width,
1878                         "focus-padding", &focus_pad,
1879                         NULL);
1880
1881   if (!combo_box->priv->tree_view)
1882     {
1883       if (combo_box->priv->cell_view)
1884         {
1885           gint border_width, xthickness, ythickness;
1886           gint width;
1887
1888           /* menu mode */
1889           gtk_widget_size_allocate (combo_box->priv->button, allocation);
1890
1891           /* set some things ready */
1892           border_width = GTK_CONTAINER (combo_box->priv->button)->border_width;
1893           xthickness = combo_box->priv->button->style->xthickness;
1894           ythickness = combo_box->priv->button->style->ythickness;
1895
1896           child.x = allocation->x;
1897           child.y = allocation->y;
1898           width = allocation->width;
1899           child.height = allocation->height;
1900
1901           if (!combo_box->priv->is_cell_renderer)
1902             {
1903               child.x += border_width + xthickness + focus_width + focus_pad;
1904               child.y += border_width + ythickness + focus_width + focus_pad;
1905               width -= 2 * (child.x - allocation->x);
1906               child.height -= 2 * (child.y - allocation->y);
1907             }
1908
1909
1910           /* handle the children */
1911           gtk_widget_size_request (combo_box->priv->arrow, &req);
1912           child.width = req.width;
1913           if (!is_rtl)
1914             child.x += width - req.width;
1915           child.width = MAX (1, child.width);
1916           child.height = MAX (1, child.height);
1917           gtk_widget_size_allocate (combo_box->priv->arrow, &child);
1918           if (is_rtl)
1919             child.x += req.width;
1920           gtk_widget_size_request (combo_box->priv->separator, &req);
1921           child.width = req.width;
1922           if (!is_rtl)
1923             child.x -= req.width;
1924           child.width = MAX (1, child.width);
1925           child.height = MAX (1, child.height);
1926           gtk_widget_size_allocate (combo_box->priv->separator, &child);
1927
1928           if (is_rtl)
1929             {
1930               child.x += req.width;
1931               child.width = allocation->x + allocation->width 
1932                 - (border_width + xthickness + focus_width + focus_pad) 
1933                 - child.x;
1934             }
1935           else 
1936             {
1937               child.width = child.x;
1938               child.x = allocation->x 
1939                 + border_width + xthickness + focus_width + focus_pad;
1940               child.width -= child.x;
1941             }
1942
1943           child.width = MAX (1, child.width);
1944           child.height = MAX (1, child.height);
1945           gtk_widget_size_allocate (GTK_BIN (widget)->child, &child);
1946         }
1947       else
1948         {
1949           gtk_widget_size_request (combo_box->priv->button, &req);
1950           if (is_rtl)
1951             child.x = allocation->x;
1952           else
1953             child.x = allocation->x + allocation->width - req.width;
1954           child.y = allocation->y;
1955           child.width = req.width;
1956           child.height = allocation->height;
1957           child.width = MAX (1, child.width);
1958           child.height = MAX (1, child.height);
1959           gtk_widget_size_allocate (combo_box->priv->button, &child);
1960
1961           if (is_rtl)
1962             child.x = allocation->x + req.width;
1963           else
1964             child.x = allocation->x;
1965           child.y = allocation->y;
1966           child.width = allocation->width - req.width;
1967           child.width = MAX (1, child.width);
1968           child.height = MAX (1, child.height);
1969           gtk_widget_size_allocate (GTK_BIN (widget)->child, &child);
1970         }
1971     }
1972   else
1973     {
1974       /* list mode */
1975
1976       /* button */
1977       gtk_widget_size_request (combo_box->priv->button, &req);
1978       if (is_rtl)
1979         child.x = allocation->x;
1980       else
1981         child.x = allocation->x + allocation->width - req.width;
1982       child.y = allocation->y;
1983       child.width = req.width;
1984       child.height = allocation->height;
1985       child.width = MAX (1, child.width);
1986       child.height = MAX (1, child.height);
1987       gtk_widget_size_allocate (combo_box->priv->button, &child);
1988
1989       /* frame */
1990       if (is_rtl)
1991         child.x = allocation->x + req.width;
1992       else
1993         child.x = allocation->x;
1994       child.y = allocation->y;
1995       child.width = allocation->width - req.width;
1996       child.height = allocation->height;
1997
1998       if (combo_box->priv->cell_view_frame)
1999         {
2000           child.width = MAX (1, child.width);
2001           child.height = MAX (1, child.height);
2002           gtk_widget_size_allocate (combo_box->priv->cell_view_frame, &child);
2003
2004           /* the sample */
2005           if (combo_box->priv->has_frame)
2006             {
2007               child.x +=
2008                 GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
2009                 GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness;
2010               child.y +=
2011                 GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
2012                 GTK_WIDGET (combo_box->priv->cell_view_frame)->style->ythickness;
2013               child.width -= 2 * (
2014                                   GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
2015                                   GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness);
2016               child.height -= 2 * (
2017                                    GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
2018                                    GTK_WIDGET (combo_box->priv->cell_view_frame)->style->ythickness);
2019             }
2020         }
2021       
2022       child.width = MAX (1, child.width);
2023       child.height = MAX (1, child.height);
2024       gtk_widget_size_allocate (GTK_BIN (combo_box)->child, &child);
2025     }
2026 }
2027
2028 static void
2029 gtk_combo_box_unset_model (GtkComboBox *combo_box)
2030 {
2031   if (combo_box->priv->model)
2032     {
2033       g_signal_handler_disconnect (combo_box->priv->model,
2034                                    combo_box->priv->inserted_id);
2035       g_signal_handler_disconnect (combo_box->priv->model,
2036                                    combo_box->priv->deleted_id);
2037       g_signal_handler_disconnect (combo_box->priv->model,
2038                                    combo_box->priv->reordered_id);
2039       g_signal_handler_disconnect (combo_box->priv->model,
2040                                    combo_box->priv->changed_id);
2041     }
2042
2043   /* menu mode */
2044   if (!combo_box->priv->tree_view)
2045     {
2046       if (combo_box->priv->popup_widget)
2047         gtk_container_foreach (GTK_CONTAINER (combo_box->priv->popup_widget),
2048                                (GtkCallback)gtk_widget_destroy, NULL);
2049     }
2050
2051   if (combo_box->priv->model)
2052     {
2053       g_object_unref (combo_box->priv->model);
2054       combo_box->priv->model = NULL;
2055     }
2056
2057   if (combo_box->priv->active_row)
2058     {
2059       gtk_tree_row_reference_free (combo_box->priv->active_row);
2060       combo_box->priv->active_row = NULL;
2061     }
2062
2063   if (combo_box->priv->cell_view)
2064     gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (combo_box->priv->cell_view), NULL);
2065 }
2066
2067 static void
2068 gtk_combo_box_forall (GtkContainer *container,
2069                       gboolean      include_internals,
2070                       GtkCallback   callback,
2071                       gpointer      callback_data)
2072 {
2073   GtkComboBox *combo_box = GTK_COMBO_BOX (container);
2074
2075   if (include_internals)
2076     {
2077       if (combo_box->priv->button)
2078         (* callback) (combo_box->priv->button, callback_data);
2079       if (combo_box->priv->cell_view_frame)
2080         (* callback) (combo_box->priv->cell_view_frame, callback_data);
2081     }
2082
2083   if (GTK_BIN (container)->child)
2084     (* callback) (GTK_BIN (container)->child, callback_data);
2085 }
2086
2087 static gboolean
2088 gtk_combo_box_expose_event (GtkWidget      *widget,
2089                             GdkEventExpose *event)
2090 {
2091   GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
2092
2093   if (!combo_box->priv->tree_view)
2094     {
2095       gtk_container_propagate_expose (GTK_CONTAINER (widget),
2096                                       combo_box->priv->button, event);
2097     }
2098   else
2099     {
2100       gtk_container_propagate_expose (GTK_CONTAINER (widget),
2101                                       combo_box->priv->button, event);
2102
2103       if (combo_box->priv->cell_view_frame)
2104         gtk_container_propagate_expose (GTK_CONTAINER (widget),
2105                                         combo_box->priv->cell_view_frame, event);
2106     }
2107
2108   gtk_container_propagate_expose (GTK_CONTAINER (widget),
2109                                   GTK_BIN (widget)->child, event);
2110
2111   return FALSE;
2112 }
2113
2114 typedef struct {
2115   GtkComboBox *combo;
2116   GtkTreePath *path;
2117   GtkTreeIter iter;
2118   gboolean found;
2119   gboolean set;
2120   gboolean visible;
2121 } SearchData;
2122
2123 static gboolean
2124 path_visible (GtkTreeView *view,
2125               GtkTreePath *path)
2126 {
2127   GtkRBTree *tree;
2128   GtkRBNode *node;
2129   
2130   /* Note that we rely on the fact that collapsed rows don't have nodes 
2131    */
2132   return _gtk_tree_view_find_node (view, path, &tree, &node);
2133 }
2134
2135 static gboolean
2136 tree_next_func (GtkTreeModel *model,
2137                 GtkTreePath  *path,
2138                 GtkTreeIter  *iter,
2139                 gpointer      data)
2140 {
2141   SearchData *search_data = (SearchData *)data;
2142
2143   if (search_data->found) 
2144     {
2145       if (!tree_column_row_is_sensitive (search_data->combo, iter))
2146         return FALSE;
2147       
2148       if (search_data->visible &&
2149           !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
2150         return FALSE;
2151
2152       search_data->set = TRUE;
2153       search_data->iter = *iter;
2154
2155       return TRUE;
2156     }
2157  
2158   if (gtk_tree_path_compare (path, search_data->path) == 0)
2159     search_data->found = TRUE;
2160   
2161   return FALSE;
2162 }
2163
2164 static gboolean
2165 tree_next (GtkComboBox  *combo,
2166            GtkTreeModel *model,
2167            GtkTreeIter  *iter,
2168            GtkTreeIter  *next,
2169            gboolean      visible)
2170 {
2171   SearchData search_data;
2172
2173   search_data.combo = combo;
2174   search_data.path = gtk_tree_model_get_path (model, iter);
2175   search_data.visible = visible;
2176   search_data.found = FALSE;
2177   search_data.set = FALSE;
2178
2179   gtk_tree_model_foreach (model, tree_next_func, &search_data);
2180   
2181   *next = search_data.iter;
2182
2183   gtk_tree_path_free (search_data.path);
2184
2185   return search_data.set;
2186 }
2187
2188 static gboolean
2189 tree_prev_func (GtkTreeModel *model,
2190                 GtkTreePath  *path,
2191                 GtkTreeIter  *iter,
2192                 gpointer      data)
2193 {
2194   SearchData *search_data = (SearchData *)data;
2195
2196   if (gtk_tree_path_compare (path, search_data->path) == 0)
2197     {
2198       search_data->found = TRUE;
2199       return TRUE;
2200     }
2201   
2202   if (!tree_column_row_is_sensitive (search_data->combo, iter))
2203     return FALSE;
2204       
2205   if (search_data->visible &&
2206       !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
2207     return FALSE; 
2208   
2209   search_data->set = TRUE;
2210   search_data->iter = *iter;
2211   
2212   return FALSE; 
2213 }
2214
2215 static gboolean
2216 tree_prev (GtkComboBox  *combo,
2217            GtkTreeModel *model,
2218            GtkTreeIter  *iter,
2219            GtkTreeIter  *prev,
2220            gboolean      visible)
2221 {
2222   SearchData search_data;
2223
2224   search_data.combo = combo;
2225   search_data.path = gtk_tree_model_get_path (model, iter);
2226   search_data.visible = visible;
2227   search_data.found = FALSE;
2228   search_data.set = FALSE;
2229
2230   gtk_tree_model_foreach (model, tree_prev_func, &search_data);
2231   
2232   *prev = search_data.iter;
2233
2234   gtk_tree_path_free (search_data.path);
2235
2236   return search_data.set;
2237 }
2238
2239 static gboolean
2240 tree_last_func (GtkTreeModel *model,
2241                 GtkTreePath  *path,
2242                 GtkTreeIter  *iter,
2243                 gpointer      data)
2244 {
2245   SearchData *search_data = (SearchData *)data;
2246
2247   if (!tree_column_row_is_sensitive (search_data->combo, iter))
2248     return FALSE;
2249       
2250   /* Note that we rely on the fact that collapsed rows don't have nodes 
2251    */
2252   if (search_data->visible &&
2253       !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
2254     return FALSE; 
2255   
2256   search_data->set = TRUE;
2257   search_data->iter = *iter;
2258   
2259   return FALSE; 
2260 }
2261
2262 static gboolean
2263 tree_last (GtkComboBox  *combo,
2264            GtkTreeModel *model,
2265            GtkTreeIter  *last,
2266            gboolean      visible)
2267 {
2268   SearchData search_data;
2269
2270   search_data.combo = combo;
2271   search_data.visible = visible;
2272   search_data.set = FALSE;
2273
2274   gtk_tree_model_foreach (model, tree_last_func, &search_data);
2275   
2276   *last = search_data.iter;
2277
2278   return search_data.set;  
2279 }
2280
2281
2282 static gboolean
2283 tree_first_func (GtkTreeModel *model,
2284                  GtkTreePath  *path,
2285                  GtkTreeIter  *iter,
2286                  gpointer      data)
2287 {
2288   SearchData *search_data = (SearchData *)data;
2289
2290   if (!tree_column_row_is_sensitive (search_data->combo, iter))
2291     return FALSE;
2292   
2293   if (search_data->visible &&
2294       !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
2295     return FALSE;
2296   
2297   search_data->set = TRUE;
2298   search_data->iter = *iter;
2299   
2300   return TRUE;
2301 }
2302
2303 static gboolean
2304 tree_first (GtkComboBox  *combo,
2305             GtkTreeModel *model,
2306             GtkTreeIter  *first,
2307             gboolean      visible)
2308 {
2309   SearchData search_data;
2310   
2311   search_data.combo = combo;
2312   search_data.visible = visible;
2313   search_data.set = FALSE;
2314
2315   gtk_tree_model_foreach (model, tree_first_func, &search_data);
2316   
2317   *first = search_data.iter;
2318
2319   return search_data.set;  
2320 }
2321
2322 static gboolean
2323 gtk_combo_box_scroll_event (GtkWidget          *widget,
2324                             GdkEventScroll     *event)
2325 {
2326   GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
2327   gboolean found;
2328   GtkTreeIter iter;
2329   GtkTreeIter new_iter;
2330
2331   if (!gtk_combo_box_get_active_iter (combo_box, &iter))
2332     return TRUE;
2333   
2334   if (event->direction == GDK_SCROLL_UP)
2335     found = tree_prev (combo_box, combo_box->priv->model, 
2336                        &iter, &new_iter, FALSE);
2337   else
2338     found = tree_next (combo_box, combo_box->priv->model, 
2339                        &iter, &new_iter, FALSE);
2340   
2341   if (found)
2342     gtk_combo_box_set_active_iter (combo_box, &new_iter);
2343
2344   return TRUE;
2345 }
2346
2347 /*
2348  * menu style
2349  */
2350
2351 static void
2352 gtk_combo_box_sync_cells (GtkComboBox   *combo_box,
2353                           GtkCellLayout *cell_layout)
2354 {
2355   GSList *k;
2356
2357   for (k = combo_box->priv->cells; k; k = k->next)
2358     {
2359       GSList *j;
2360       ComboCellInfo *info = (ComboCellInfo *)k->data;
2361
2362       if (info->pack == GTK_PACK_START)
2363         gtk_cell_layout_pack_start (cell_layout,
2364                                     info->cell, info->expand);
2365       else if (info->pack == GTK_PACK_END)
2366         gtk_cell_layout_pack_end (cell_layout,
2367                                   info->cell, info->expand);
2368
2369       gtk_cell_layout_set_cell_data_func (cell_layout,
2370                                           info->cell,
2371                                           combo_cell_data_func, info, NULL);
2372
2373       for (j = info->attributes; j; j = j->next->next)
2374         {
2375           gtk_cell_layout_add_attribute (cell_layout,
2376                                          info->cell,
2377                                          j->data,
2378                                          GPOINTER_TO_INT (j->next->data));
2379         }
2380     }
2381 }
2382
2383 static void
2384 gtk_combo_box_menu_setup (GtkComboBox *combo_box,
2385                           gboolean     add_children)
2386 {
2387   GtkWidget *menu;
2388
2389   if (combo_box->priv->cell_view)
2390     {
2391       combo_box->priv->button = gtk_toggle_button_new ();
2392       gtk_button_set_focus_on_click (GTK_BUTTON (combo_box->priv->button),
2393                                      combo_box->priv->focus_on_click);
2394
2395       g_signal_connect (combo_box->priv->button, "toggled",
2396                         G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
2397       g_signal_connect_after (combo_box->priv->button, 
2398                               "key_press_event",
2399                               G_CALLBACK (gtk_combo_box_key_press), combo_box);
2400       gtk_widget_set_parent (combo_box->priv->button,
2401                              GTK_BIN (combo_box)->child->parent);
2402
2403       combo_box->priv->box = gtk_hbox_new (FALSE, 0);
2404       gtk_container_add (GTK_CONTAINER (combo_box->priv->button), 
2405                          combo_box->priv->box);
2406
2407       combo_box->priv->separator = gtk_vseparator_new ();
2408       gtk_container_add (GTK_CONTAINER (combo_box->priv->box), 
2409                          combo_box->priv->separator);
2410
2411       combo_box->priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
2412       gtk_container_add (GTK_CONTAINER (combo_box->priv->box), 
2413                          combo_box->priv->arrow);
2414
2415       gtk_widget_show_all (combo_box->priv->button);
2416     }
2417   else
2418     {
2419       combo_box->priv->button = gtk_toggle_button_new ();
2420       gtk_button_set_focus_on_click (GTK_BUTTON (combo_box->priv->button),
2421                                      combo_box->priv->focus_on_click);
2422
2423       g_signal_connect (combo_box->priv->button, "toggled",
2424                         G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
2425       g_signal_connect_after (combo_box, "key_press_event",
2426                               G_CALLBACK (gtk_combo_box_key_press), combo_box);
2427       gtk_widget_set_parent (combo_box->priv->button,
2428                              GTK_BIN (combo_box)->child->parent);
2429
2430       combo_box->priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
2431       gtk_container_add (GTK_CONTAINER (combo_box->priv->button),
2432                          combo_box->priv->arrow);
2433       gtk_widget_show_all (combo_box->priv->button);
2434     }
2435
2436   g_signal_connect (combo_box->priv->button, "button_press_event",
2437                     G_CALLBACK (gtk_combo_box_menu_button_press),
2438                     combo_box);
2439   g_signal_connect (combo_box->priv->button, "state_changed",
2440                     G_CALLBACK (gtk_combo_box_button_state_changed), 
2441                     combo_box);
2442
2443   /* create our funky menu */
2444   menu = gtk_menu_new ();
2445   g_signal_connect (menu, "key_press_event",
2446                     G_CALLBACK (gtk_combo_box_menu_key_press), combo_box);
2447   gtk_combo_box_set_popup_widget (combo_box, menu);
2448
2449   /* add items */
2450   if (add_children)
2451     gtk_combo_box_menu_fill (combo_box);
2452
2453   /* the column is needed in tree_column_row_is_sensitive() */
2454   combo_box->priv->column = gtk_tree_view_column_new ();
2455   g_object_ref (combo_box->priv->column);
2456   gtk_object_sink (GTK_OBJECT (combo_box->priv->column));
2457   gtk_combo_box_sync_cells (combo_box, 
2458                             GTK_CELL_LAYOUT (combo_box->priv->column));
2459 }
2460
2461 static void
2462 gtk_combo_box_menu_fill (GtkComboBox *combo_box)
2463 {
2464   GtkWidget *menu;
2465
2466   if (!combo_box->priv->model)
2467     return;
2468
2469   menu = combo_box->priv->popup_widget;
2470
2471   if (combo_box->priv->add_tearoffs)
2472     {
2473       GtkWidget *tearoff = gtk_tearoff_menu_item_new ();
2474
2475       gtk_widget_show (tearoff);
2476       
2477       if (combo_box->priv->wrap_width)
2478         gtk_menu_attach (GTK_MENU (menu), tearoff, 
2479                          0, combo_box->priv->wrap_width, 0, 1);
2480       else
2481         gtk_menu_shell_append (GTK_MENU_SHELL (menu), tearoff);
2482     }
2483   
2484   gtk_combo_box_menu_fill_level (combo_box, menu, NULL);
2485 }
2486
2487 static GtkWidget *
2488 gtk_cell_view_menu_item_new (GtkComboBox  *combo_box,
2489                              GtkTreeModel *model,
2490                              GtkTreeIter  *iter)
2491 {
2492   GtkWidget *cell_view; 
2493   GtkWidget *item;
2494   GtkTreePath *path;
2495   GtkRequisition req;
2496
2497   cell_view = gtk_cell_view_new ();
2498   gtk_cell_view_set_model (GTK_CELL_VIEW (cell_view), model);     
2499   path = gtk_tree_model_get_path (model, iter);
2500   gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (cell_view), path);
2501   gtk_tree_path_free (path);
2502
2503   gtk_combo_box_sync_cells (combo_box, GTK_CELL_LAYOUT (cell_view));
2504   gtk_widget_size_request (cell_view, &req);
2505   gtk_widget_show (cell_view);
2506
2507   item = gtk_menu_item_new ();
2508   gtk_container_add (GTK_CONTAINER (item), cell_view);
2509   
2510   return item;
2511 }
2512  
2513 static void
2514 gtk_combo_box_menu_fill_level (GtkComboBox *combo_box,
2515                                GtkWidget   *menu,
2516                                GtkTreeIter *parent)
2517 {
2518   GtkTreeModel *model = combo_box->priv->model;
2519   GtkWidget *item, *submenu, *subitem, *separator;
2520   GtkTreeIter iter;
2521   gboolean is_separator;
2522   gint i, n_children;
2523   GtkWidget *last;
2524   GtkTreePath *path;
2525   
2526   n_children = gtk_tree_model_iter_n_children (model, parent);
2527   
2528   last = NULL;
2529   for (i = 0; i < n_children; i++)
2530     {
2531       gtk_tree_model_iter_nth_child (model, &iter, parent, i);
2532
2533       if (combo_box->priv->row_separator_func)
2534         is_separator = (*combo_box->priv->row_separator_func) (combo_box->priv->model, &iter,
2535                                                                combo_box->priv->row_separator_data);
2536       else
2537         is_separator = FALSE;
2538       
2539       if (is_separator)
2540         {
2541           item = gtk_separator_menu_item_new ();
2542           path = gtk_tree_model_get_path (model, &iter);
2543           g_object_set_data_full (G_OBJECT (item),
2544                                   I_("gtk-combo-box-item-path"),
2545                                   gtk_tree_row_reference_new (model, path),
2546                                   (GDestroyNotify)gtk_tree_row_reference_free);
2547           gtk_tree_path_free (path);
2548         }
2549       else
2550         {
2551           item = gtk_cell_view_menu_item_new (combo_box, model, &iter);
2552           if (gtk_tree_model_iter_has_child (model, &iter))
2553             {
2554               submenu = gtk_menu_new ();
2555               gtk_widget_show (submenu);
2556               gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu);
2557               
2558               /* Ugly - since menus can only activate leafs, we have to
2559                * duplicate the item inside the submenu.
2560                */
2561               subitem = gtk_cell_view_menu_item_new (combo_box, model, &iter);
2562               separator = gtk_separator_menu_item_new ();
2563               gtk_widget_show (subitem);
2564               gtk_widget_show (separator);
2565               g_signal_connect (subitem, "activate",
2566                                 G_CALLBACK (gtk_combo_box_menu_item_activate),
2567                                 combo_box);
2568               gtk_menu_shell_append (GTK_MENU_SHELL (submenu), subitem);
2569               gtk_menu_shell_append (GTK_MENU_SHELL (submenu), separator);
2570               
2571               gtk_combo_box_menu_fill_level (combo_box, submenu, &iter);
2572             }
2573           else
2574             g_signal_connect (item, "activate",
2575                               G_CALLBACK (gtk_combo_box_menu_item_activate),
2576                               combo_box);
2577         }
2578       
2579       gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2580       if (combo_box->priv->wrap_width && menu == combo_box->priv->popup_widget)
2581         gtk_combo_box_relayout_item (combo_box, item, &iter, last);
2582       gtk_widget_show (item);
2583       
2584       last = item;
2585     }
2586 }
2587
2588 static void
2589 gtk_combo_box_menu_destroy (GtkComboBox *combo_box)
2590 {
2591   g_signal_handlers_disconnect_matched (combo_box->priv->button,
2592                                         G_SIGNAL_MATCH_DATA,
2593                                         0, 0, NULL,
2594                                         gtk_combo_box_menu_button_press, NULL);
2595   g_signal_handlers_disconnect_matched (combo_box->priv->button,
2596                                         G_SIGNAL_MATCH_DATA,
2597                                         0, 0, NULL,
2598                                         gtk_combo_box_button_state_changed, combo_box);
2599
2600   /* unparent will remove our latest ref */
2601   gtk_widget_unparent (combo_box->priv->button);
2602   
2603   combo_box->priv->box = NULL;
2604   combo_box->priv->button = NULL;
2605   combo_box->priv->arrow = NULL;
2606   combo_box->priv->separator = NULL;
2607
2608   g_object_unref (combo_box->priv->column);
2609   combo_box->priv->column = NULL;
2610
2611   /* changing the popup window will unref the menu and the children */
2612 }
2613
2614 /*
2615  * grid
2616  */
2617
2618 static gboolean
2619 menu_occupied (GtkMenu   *menu,
2620                guint      left_attach,
2621                guint      right_attach,
2622                guint      top_attach,
2623                guint      bottom_attach)
2624 {
2625   GList *i;
2626
2627   for (i = GTK_MENU_SHELL (menu)->children; i; i = i->next)
2628     {
2629       guint l, r, b, t;
2630
2631       gtk_container_child_get (GTK_CONTAINER (menu), 
2632                                i->data,
2633                                "left-attach", &l,
2634                                "right-attach", &r,
2635                                "bottom-attach", &b,
2636                                "top-attach", &t,
2637                                NULL);
2638
2639       /* look if this item intersects with the given coordinates */
2640       if (right_attach > l && left_attach < r && bottom_attach > t && top_attach < b)
2641         return TRUE;
2642     }
2643
2644   return FALSE;
2645 }
2646
2647 static void
2648 gtk_combo_box_relayout_item (GtkComboBox *combo_box,
2649                              GtkWidget   *item,
2650                              GtkTreeIter *iter,
2651                              GtkWidget   *last)
2652 {
2653   gint current_col = 0, current_row = 0;
2654   gint rows = 1, cols = 1;
2655   GtkWidget *menu = combo_box->priv->popup_widget;
2656
2657   if (!GTK_IS_MENU_SHELL (menu))
2658     return;
2659   
2660   if (combo_box->priv->col_column == -1 &&
2661       combo_box->priv->row_column == -1 &&
2662       last)
2663     {
2664       gtk_container_child_get (GTK_CONTAINER (menu), 
2665                                last,
2666                                "right_attach", &current_col,
2667                                "top_attach", &current_row,
2668                                NULL);
2669       if (current_col + cols > combo_box->priv->wrap_width)
2670         {
2671           current_col = 0;
2672           current_row++;
2673         }
2674     }
2675   else
2676     {
2677       if (combo_box->priv->col_column != -1)
2678         gtk_tree_model_get (combo_box->priv->model, iter,
2679                             combo_box->priv->col_column, &cols,
2680                             -1);
2681       if (combo_box->priv->row_column != -1)
2682         gtk_tree_model_get (combo_box->priv->model, iter,
2683                             combo_box->priv->row_column, &rows,
2684                             -1);
2685
2686       while (1)
2687         {
2688           if (current_col + cols > combo_box->priv->wrap_width)
2689             {
2690               current_col = 0;
2691               current_row++;
2692             }
2693           
2694           if (!menu_occupied (GTK_MENU (menu), 
2695                               current_col, current_col + cols,
2696                               current_row, current_row + rows))
2697             break;
2698           
2699           current_col++;
2700         }
2701     }
2702
2703   /* set attach props */
2704   gtk_menu_attach (GTK_MENU (menu), item,
2705                    current_col, current_col + cols,
2706                    current_row, current_row + rows);
2707 }
2708
2709 static void
2710 gtk_combo_box_relayout (GtkComboBox *combo_box)
2711 {
2712   GList *list, *j;
2713   GtkWidget *menu;
2714
2715   menu = combo_box->priv->popup_widget;
2716   
2717   /* do nothing unless we are in menu style and realized */
2718   if (combo_box->priv->tree_view || !GTK_IS_MENU_SHELL (menu))
2719     return;
2720   
2721   list = gtk_container_get_children (GTK_CONTAINER (menu));
2722   
2723   for (j = g_list_last (list); j; j = j->prev)
2724     gtk_container_remove (GTK_CONTAINER (menu), j->data);
2725   
2726   gtk_combo_box_menu_fill (combo_box);
2727
2728   g_list_free (list);
2729 }
2730
2731 /* callbacks */
2732 static gboolean
2733 gtk_combo_box_menu_button_press (GtkWidget      *widget,
2734                                  GdkEventButton *event,
2735                                  gpointer        user_data)
2736 {
2737   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
2738
2739   if (GTK_IS_MENU (combo_box->priv->popup_widget) &&
2740       event->type == GDK_BUTTON_PRESS && event->button == 1)
2741     {
2742       if (combo_box->priv->focus_on_click && 
2743           !GTK_WIDGET_HAS_FOCUS (combo_box->priv->button))
2744         gtk_widget_grab_focus (combo_box->priv->button);
2745
2746       gtk_combo_box_menu_popup (combo_box, event->button, event->time);
2747
2748       return TRUE;
2749     }
2750
2751   return FALSE;
2752 }
2753
2754 static void
2755 gtk_combo_box_menu_item_activate (GtkWidget *item,
2756                                   gpointer   user_data)
2757 {
2758   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
2759   GtkWidget *cell_view;
2760   GtkTreePath *path;
2761   GtkTreeIter iter;
2762
2763   cell_view = GTK_BIN (item)->child;
2764
2765   g_return_if_fail (GTK_IS_CELL_VIEW (cell_view));
2766
2767   path = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (cell_view));
2768
2769   if (gtk_tree_model_get_iter (combo_box->priv->model, &iter, path))
2770     gtk_combo_box_set_active_iter (combo_box, &iter);
2771
2772   gtk_tree_path_free (path);
2773
2774   combo_box->priv->editing_canceled = FALSE;
2775 }
2776
2777 static void
2778 gtk_combo_box_model_row_inserted (GtkTreeModel     *model,
2779                                   GtkTreePath      *path,
2780                                   GtkTreeIter      *iter,
2781                                   gpointer          user_data)
2782 {
2783   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
2784
2785   if (combo_box->priv->tree_view)
2786     gtk_combo_box_list_popup_resize (combo_box);
2787   else
2788     gtk_combo_box_menu_row_inserted (model, path, iter, user_data);
2789 }
2790
2791 static void
2792 gtk_combo_box_model_row_deleted (GtkTreeModel     *model,
2793                                  GtkTreePath      *path,
2794                                  gpointer          user_data)
2795 {
2796   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
2797
2798   if (combo_box->priv->cell_view)
2799     {
2800       if (!gtk_tree_row_reference_valid (combo_box->priv->active_row))
2801         gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (combo_box->priv->cell_view), NULL);
2802     }
2803   
2804   if (combo_box->priv->tree_view)
2805     gtk_combo_box_list_popup_resize (combo_box);
2806   else
2807     gtk_combo_box_menu_row_deleted (model, path, user_data);  
2808 }
2809
2810 static void
2811 gtk_combo_box_model_rows_reordered (GtkTreeModel    *model,
2812                                     GtkTreePath     *path,
2813                                     GtkTreeIter     *iter,
2814                                     gint            *new_order,
2815                                     gpointer         user_data)
2816 {
2817   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
2818
2819   gtk_tree_row_reference_reordered (G_OBJECT (user_data), path, iter, new_order);
2820
2821   if (!combo_box->priv->tree_view)
2822     gtk_combo_box_menu_rows_reordered (model, path, iter, new_order, user_data);
2823 }
2824                                                     
2825 static void
2826 gtk_combo_box_model_row_changed (GtkTreeModel     *model,
2827                                  GtkTreePath      *path,
2828                                  GtkTreeIter      *iter,
2829                                  gpointer          user_data)
2830 {
2831   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
2832   GtkTreePath *active_path;
2833
2834   /* FIXME this belongs to GtkCellView */
2835   if (gtk_tree_row_reference_valid (combo_box->priv->active_row))
2836     {
2837       active_path = gtk_tree_row_reference_get_path (combo_box->priv->active_row);
2838       if (gtk_tree_path_compare (path, active_path) == 0 &&
2839           combo_box->priv->cell_view)
2840         gtk_widget_queue_resize (GTK_WIDGET (combo_box->priv->cell_view));
2841       gtk_tree_path_free (active_path);
2842     }
2843       
2844   if (combo_box->priv->tree_view)
2845     gtk_combo_box_list_row_changed (model, path, iter, user_data);
2846   else
2847     gtk_combo_box_menu_row_changed (model, path, iter, user_data);
2848 }
2849
2850 static gboolean
2851 list_popup_resize_idle (gpointer user_data)
2852 {
2853   GtkComboBox *combo_box;
2854   gint x, y, width, height;
2855
2856   GDK_THREADS_ENTER ();
2857
2858   combo_box = GTK_COMBO_BOX (user_data);
2859
2860   if (combo_box->priv->tree_view &&
2861       GTK_WIDGET_MAPPED (combo_box->priv->popup_window))
2862     {
2863       gtk_combo_box_list_position (combo_box, &x, &y, &width, &height);
2864   
2865       gtk_widget_set_size_request (combo_box->priv->popup_window, width, height);
2866       gtk_window_move (GTK_WINDOW (combo_box->priv->popup_window), x, y);
2867     }
2868
2869   combo_box->priv->resize_idle_id = 0;
2870
2871   GDK_THREADS_LEAVE ();
2872
2873   return FALSE;
2874 }
2875
2876 static void
2877 gtk_combo_box_list_popup_resize (GtkComboBox *combo_box)
2878 {
2879   if (!combo_box->priv->resize_idle_id)
2880     combo_box->priv->resize_idle_id = 
2881       g_idle_add (list_popup_resize_idle, combo_box);
2882 }
2883
2884 static void
2885 gtk_combo_box_model_row_expanded (GtkTreeModel     *model,
2886                                   GtkTreePath      *path,
2887                                   GtkTreeIter      *iter,
2888                                   gpointer          user_data)
2889 {
2890   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
2891   
2892   gtk_combo_box_list_popup_resize (combo_box);
2893 }
2894
2895
2896 static GtkWidget *
2897 find_menu_by_path (GtkWidget   *menu,
2898                    GtkTreePath *path,
2899                    gboolean     skip_first)
2900 {
2901   GList *i, *list;
2902   GtkWidget *item;
2903   GtkWidget *submenu;    
2904   GtkTreeRowReference *mref;
2905   GtkTreePath *mpath;
2906   gboolean skip;
2907
2908   list = gtk_container_get_children (GTK_CONTAINER (menu));
2909   skip = skip_first;
2910   item = NULL;
2911   for (i = list; i; i = i->next)
2912     {
2913       if (GTK_IS_SEPARATOR_MENU_ITEM (i->data))
2914         {
2915           mref = g_object_get_data (G_OBJECT (i->data), "gtk-combo-box-item-path");
2916           if (!mref)
2917             continue;
2918           else if (!gtk_tree_row_reference_valid (mref))
2919             mpath = NULL;
2920           else
2921             mpath = gtk_tree_row_reference_get_path (mref);
2922         }
2923       else if (GTK_IS_CELL_VIEW (GTK_BIN (i->data)->child))
2924         {
2925           if (skip)
2926             {
2927               skip = FALSE;
2928               continue;
2929             }
2930
2931           mpath = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (GTK_BIN (i->data)->child));
2932         }
2933       else 
2934         continue;
2935
2936       /* this case is necessary, since the row reference of
2937        * the cell view may already be updated after a deletion
2938        */
2939       if (!mpath)
2940         {
2941           item = i->data;
2942           break;
2943         }
2944       if (gtk_tree_path_compare (mpath, path) == 0)
2945         {
2946           gtk_tree_path_free (mpath);
2947           item = i->data;
2948           break;
2949         }
2950       if (gtk_tree_path_is_ancestor (mpath, path))
2951         {
2952           submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
2953           if (submenu != NULL)
2954             {
2955               gtk_tree_path_free (mpath);
2956               item = find_menu_by_path (submenu, path, TRUE);
2957               break;
2958             }
2959         }
2960       gtk_tree_path_free (mpath);
2961     }
2962   
2963   g_list_free (list);  
2964
2965   return item;
2966 }
2967
2968 #if 0
2969 static void
2970 dump_menu_tree (GtkWidget   *menu, 
2971                 gint         level)
2972 {
2973   GList *i, *list;
2974   GtkWidget *submenu;    
2975   GtkTreePath *path;
2976
2977   list = gtk_container_get_children (GTK_CONTAINER (menu));
2978   for (i = list; i; i = i->next)
2979     {
2980       if (GTK_IS_CELL_VIEW (GTK_BIN (i->data)->child))
2981         {
2982           path = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (GTK_BIN (i->data)->child));
2983           g_print ("%*s%s\n", 2 * level, " ", gtk_tree_path_to_string (path));
2984           gtk_tree_path_free (path);
2985
2986           submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
2987           if (submenu != NULL)
2988             dump_menu_tree (submenu, level + 1);
2989         }
2990     }
2991   
2992   g_list_free (list);  
2993 }
2994 #endif
2995
2996 static void
2997 gtk_combo_box_menu_row_inserted (GtkTreeModel *model,
2998                                  GtkTreePath  *path,
2999                                  GtkTreeIter  *iter,
3000                                  gpointer      user_data)
3001 {
3002   GtkWidget *parent;
3003   GtkWidget *item, *menu, *separator;
3004   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3005   GtkTreePath *ppath;
3006   GtkTreeIter piter;
3007   gint depth, pos;
3008   gboolean is_separator;
3009
3010   if (!combo_box->priv->popup_widget)
3011     return;
3012
3013   depth = gtk_tree_path_get_depth (path);
3014   pos = gtk_tree_path_get_indices (path)[depth - 1];
3015   if (depth > 1)
3016     {
3017       ppath = gtk_tree_path_copy (path);
3018       gtk_tree_path_up (ppath);
3019       parent = find_menu_by_path (combo_box->priv->popup_widget, ppath, FALSE);
3020       gtk_tree_path_free (ppath);
3021
3022       menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (parent));
3023       if (!menu)
3024         {
3025           menu = gtk_menu_new ();
3026           gtk_widget_show (menu);
3027           gtk_menu_item_set_submenu (GTK_MENU_ITEM (parent), menu);
3028           
3029           /* Ugly - since menus can only activate leaves, we have to
3030            * duplicate the item inside the submenu.
3031            */
3032           gtk_tree_model_iter_parent (model, &piter, iter);
3033           item = gtk_cell_view_menu_item_new (combo_box, model, &piter);
3034           separator = gtk_separator_menu_item_new ();
3035           g_signal_connect (item, "activate",
3036                             G_CALLBACK (gtk_combo_box_menu_item_activate),
3037                             combo_box);
3038           gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3039           gtk_menu_shell_append (GTK_MENU_SHELL (menu), separator);
3040           if (cell_view_is_sensitive (GTK_CELL_VIEW (GTK_BIN (item)->child)))
3041             {
3042               gtk_widget_show (item);
3043               gtk_widget_show (separator);
3044             }
3045         }
3046       pos += 2;
3047     }
3048   else
3049     {
3050       menu = combo_box->priv->popup_widget;
3051       if (combo_box->priv->add_tearoffs)
3052         pos += 1;
3053     }
3054   
3055   if (combo_box->priv->row_separator_func)
3056     is_separator = (*combo_box->priv->row_separator_func) (model, iter,
3057                                                            combo_box->priv->row_separator_data);
3058   else
3059     is_separator = FALSE;
3060
3061   if (is_separator)
3062     {
3063       item = gtk_separator_menu_item_new ();
3064       g_object_set_data_full (G_OBJECT (item),
3065                               I_("gtk-combo-box-item-path"),
3066                               gtk_tree_row_reference_new (model, path),
3067                               (GDestroyNotify)gtk_tree_row_reference_free);
3068     }
3069   else
3070     {
3071       item = gtk_cell_view_menu_item_new (combo_box, model, iter);
3072       
3073       g_signal_connect (item, "activate",
3074                         G_CALLBACK (gtk_combo_box_menu_item_activate),
3075                         combo_box);
3076     }
3077
3078   gtk_widget_show (item);
3079   gtk_menu_shell_insert (GTK_MENU_SHELL (menu), item, pos);
3080 }
3081
3082 static void
3083 gtk_combo_box_menu_row_deleted (GtkTreeModel *model,
3084                                 GtkTreePath  *path,
3085                                 gpointer      user_data)
3086 {
3087   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3088   GtkWidget *menu;
3089   GtkWidget *item;
3090
3091   if (!combo_box->priv->popup_widget)
3092     return;
3093
3094   item = find_menu_by_path (combo_box->priv->popup_widget, path, FALSE);
3095   menu = gtk_widget_get_parent (item);
3096   gtk_container_remove (GTK_CONTAINER (menu), item);
3097 }
3098
3099 static void
3100 gtk_combo_box_menu_rows_reordered  (GtkTreeModel     *model,
3101                                     GtkTreePath      *path,
3102                                     GtkTreeIter      *iter,
3103                                     gint             *new_order,
3104                                     gpointer          user_data)
3105 {
3106   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3107
3108   gtk_combo_box_relayout (combo_box);
3109 }
3110                                     
3111 static void
3112 gtk_combo_box_menu_row_changed (GtkTreeModel *model,
3113                                 GtkTreePath  *path,
3114                                 GtkTreeIter  *iter,
3115                                 gpointer      user_data)
3116 {
3117   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3118   GtkWidget *item;
3119   gint width;
3120   gboolean is_separator;
3121
3122   if (!combo_box->priv->popup_widget)
3123     return;
3124
3125   item = find_menu_by_path (combo_box->priv->popup_widget, path, FALSE);
3126
3127   if (combo_box->priv->row_separator_func)
3128     is_separator = (*combo_box->priv->row_separator_func) (model, iter,
3129                                                            combo_box->priv->row_separator_data);
3130   else
3131     is_separator = FALSE;
3132
3133   if (is_separator != GTK_IS_SEPARATOR_MENU_ITEM (item))
3134     {
3135       gtk_combo_box_menu_row_deleted (model, path, combo_box);
3136       gtk_combo_box_menu_row_inserted (model, path, iter, combo_box);
3137     }
3138
3139   if (combo_box->priv->wrap_width
3140       && item->parent == combo_box->priv->popup_widget)
3141     {
3142       GtkWidget *pitem = NULL;
3143       GtkTreePath *prev;
3144
3145       prev = gtk_tree_path_copy (path);
3146
3147       if (gtk_tree_path_prev (prev))
3148         pitem = find_menu_by_path (combo_box->priv->popup_widget, prev, FALSE);
3149
3150       gtk_tree_path_free (prev);
3151
3152       /* unattach item so gtk_combo_box_relayout_item() won't spuriously
3153          move it */
3154       gtk_container_child_set (GTK_CONTAINER (combo_box->priv->popup_widget),
3155                                item, 
3156                                "left-attach", -1, 
3157                                "right-attach", -1,
3158                                "top-attach", -1, 
3159                                "bottom-attach", -1, 
3160                                NULL);
3161
3162       gtk_combo_box_relayout_item (combo_box, item, iter, pitem);
3163     }
3164
3165   width = gtk_combo_box_calc_requested_width (combo_box, path);
3166
3167   if (width > combo_box->priv->width)
3168     {
3169       if (combo_box->priv->cell_view)
3170         {
3171           gtk_widget_set_size_request (combo_box->priv->cell_view, width, -1);
3172           gtk_widget_queue_resize (combo_box->priv->cell_view);
3173         }
3174       combo_box->priv->width = width;
3175     }
3176 }
3177
3178 /*
3179  * list style
3180  */
3181
3182 static void
3183 gtk_combo_box_list_setup (GtkComboBox *combo_box)
3184 {
3185   GtkTreeSelection *sel;
3186
3187   combo_box->priv->button = gtk_toggle_button_new ();
3188   gtk_widget_set_parent (combo_box->priv->button,
3189                          GTK_BIN (combo_box)->child->parent);
3190   g_signal_connect (combo_box->priv->button, "button_press_event",
3191                     G_CALLBACK (gtk_combo_box_list_button_pressed), combo_box);
3192   g_signal_connect (combo_box->priv->button, "toggled",
3193                     G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
3194   g_signal_connect_after (combo_box, "key_press_event",
3195                           G_CALLBACK (gtk_combo_box_key_press), combo_box);
3196
3197   combo_box->priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3198   gtk_container_add (GTK_CONTAINER (combo_box->priv->button),
3199                      combo_box->priv->arrow);
3200   combo_box->priv->separator = NULL;
3201   gtk_widget_show_all (combo_box->priv->button);
3202
3203   if (combo_box->priv->cell_view)
3204     {
3205       gtk_cell_view_set_background_color (GTK_CELL_VIEW (combo_box->priv->cell_view), 
3206                                           &GTK_WIDGET (combo_box)->style->base[GTK_WIDGET_STATE (combo_box)]);
3207
3208       combo_box->priv->box = gtk_event_box_new ();
3209       gtk_event_box_set_visible_window (GTK_EVENT_BOX (combo_box->priv->box), 
3210                                         FALSE);
3211
3212       if (combo_box->priv->has_frame)
3213         {
3214           combo_box->priv->cell_view_frame = gtk_frame_new (NULL);
3215           gtk_frame_set_shadow_type (GTK_FRAME (combo_box->priv->cell_view_frame),
3216                                      GTK_SHADOW_IN);
3217         }
3218       else 
3219         {
3220           combo_box->priv->cell_view_frame = gtk_event_box_new ();
3221           gtk_event_box_set_visible_window (GTK_EVENT_BOX (combo_box->priv->cell_view_frame), 
3222                                             FALSE);
3223         }
3224       
3225       gtk_widget_set_parent (combo_box->priv->cell_view_frame,
3226                              GTK_BIN (combo_box)->child->parent);
3227       gtk_container_add (GTK_CONTAINER (combo_box->priv->cell_view_frame),
3228                          combo_box->priv->box);
3229       gtk_widget_show_all (combo_box->priv->cell_view_frame);
3230
3231       g_signal_connect (combo_box->priv->box, "button_press_event",
3232                         G_CALLBACK (gtk_combo_box_list_button_pressed), 
3233                         combo_box);
3234     }
3235
3236   combo_box->priv->tree_view = gtk_tree_view_new ();
3237   sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (combo_box->priv->tree_view));
3238   gtk_tree_selection_set_mode (sel, GTK_SELECTION_BROWSE);
3239   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (combo_box->priv->tree_view),
3240                                      FALSE);
3241   gtk_tree_view_set_hover_selection (GTK_TREE_VIEW (combo_box->priv->tree_view),
3242                                      TRUE);
3243   if (combo_box->priv->row_separator_func)
3244     gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (combo_box->priv->tree_view), 
3245                                           combo_box->priv->row_separator_func, 
3246                                           combo_box->priv->row_separator_data, 
3247                                           NULL);
3248   if (combo_box->priv->model)
3249     gtk_tree_view_set_model (GTK_TREE_VIEW (combo_box->priv->tree_view),
3250                              combo_box->priv->model);
3251     
3252   combo_box->priv->column = gtk_tree_view_column_new ();
3253   gtk_tree_view_append_column (GTK_TREE_VIEW (combo_box->priv->tree_view),
3254                                combo_box->priv->column);
3255
3256   /* sync up */
3257   gtk_combo_box_sync_cells (combo_box, 
3258                             GTK_CELL_LAYOUT (combo_box->priv->column));
3259
3260   if (gtk_tree_row_reference_valid (combo_box->priv->active_row))
3261     {
3262       GtkTreePath *path;
3263
3264       path = gtk_tree_row_reference_get_path (combo_box->priv->active_row);
3265       gtk_tree_view_set_cursor (GTK_TREE_VIEW (combo_box->priv->tree_view),
3266                                 path, NULL, FALSE);
3267       gtk_tree_path_free (path);
3268     }
3269
3270   /* set sample/popup widgets */
3271   gtk_combo_box_set_popup_widget (combo_box, combo_box->priv->tree_view);
3272
3273   g_signal_connect (combo_box->priv->tree_view, "key_press_event",
3274                     G_CALLBACK (gtk_combo_box_list_key_press),
3275                     combo_box);
3276   g_signal_connect (combo_box->priv->tree_view, "enter_notify_event",
3277                     G_CALLBACK (gtk_combo_box_list_enter_notify),
3278                     combo_box);
3279   g_signal_connect (combo_box->priv->tree_view, "row_expanded",
3280                     G_CALLBACK (gtk_combo_box_model_row_expanded),
3281                     combo_box);
3282   g_signal_connect (combo_box->priv->tree_view, "row_collapsed",
3283                     G_CALLBACK (gtk_combo_box_model_row_expanded),
3284                     combo_box);
3285   g_signal_connect (combo_box->priv->popup_window, "button_press_event",
3286                     G_CALLBACK (gtk_combo_box_list_button_pressed),
3287                     combo_box);
3288   g_signal_connect (combo_box->priv->popup_window, "button_release_event",
3289                     G_CALLBACK (gtk_combo_box_list_button_released),
3290                     combo_box);
3291
3292   gtk_widget_show (combo_box->priv->tree_view);
3293 }
3294
3295 static void
3296 gtk_combo_box_list_destroy (GtkComboBox *combo_box)
3297 {
3298   /* disconnect signals */
3299   g_signal_handlers_disconnect_matched (combo_box->priv->tree_view,
3300                                         G_SIGNAL_MATCH_DATA,
3301                                         0, 0, NULL, NULL, combo_box);
3302   g_signal_handlers_disconnect_matched (combo_box->priv->button,
3303                                         G_SIGNAL_MATCH_DATA,
3304                                         0, 0, NULL,
3305                                         gtk_combo_box_list_button_pressed,
3306                                         NULL);
3307   g_signal_handlers_disconnect_matched (combo_box->priv->popup_window,
3308                                         G_SIGNAL_MATCH_DATA,
3309                                         0, 0, NULL,
3310                                         gtk_combo_box_list_button_pressed,
3311                                         NULL);
3312   g_signal_handlers_disconnect_matched (combo_box->priv->popup_window,
3313                                         G_SIGNAL_MATCH_DATA,
3314                                         0, 0, NULL,
3315                                         gtk_combo_box_list_button_released,
3316                                         NULL);
3317   if (combo_box->priv->box)
3318     g_signal_handlers_disconnect_matched (combo_box->priv->box,
3319                                           G_SIGNAL_MATCH_DATA,
3320                                           0, 0, NULL,
3321                                           gtk_combo_box_list_button_pressed,
3322                                           NULL);
3323
3324   /* destroy things (unparent will kill the latest ref from us)
3325    * last unref on button will destroy the arrow
3326    */
3327   gtk_widget_unparent (combo_box->priv->button);
3328   combo_box->priv->button = NULL;
3329   combo_box->priv->arrow = NULL;
3330
3331   if (combo_box->priv->cell_view)
3332     {
3333       g_object_set (combo_box->priv->cell_view,
3334                     "background-set", FALSE,
3335                     NULL);
3336     }
3337
3338   if (combo_box->priv->cell_view_frame)
3339     {
3340       gtk_widget_unparent (combo_box->priv->cell_view_frame);
3341       combo_box->priv->cell_view_frame = NULL;
3342       combo_box->priv->box = NULL;
3343     }
3344
3345   if (combo_box->priv->scroll_timer)
3346     {
3347       g_source_remove (combo_box->priv->scroll_timer);
3348       combo_box->priv->scroll_timer = 0;
3349     }
3350
3351   if (combo_box->priv->resize_idle_id)
3352     {
3353       g_source_remove (combo_box->priv->resize_idle_id);
3354       combo_box->priv->resize_idle_id = 0;
3355     }
3356
3357   gtk_widget_destroy (combo_box->priv->tree_view);
3358
3359   combo_box->priv->tree_view = NULL;
3360   combo_box->priv->popup_widget = NULL;
3361 }
3362
3363 /* callbacks */
3364
3365 static gboolean
3366 gtk_combo_box_list_button_pressed (GtkWidget      *widget,
3367                                    GdkEventButton *event,
3368                                    gpointer        data)
3369 {
3370   GtkComboBox *combo_box = GTK_COMBO_BOX (data);
3371
3372   GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *)event);
3373
3374   if (ewidget == combo_box->priv->popup_window)
3375     return TRUE;
3376
3377   if ((ewidget != combo_box->priv->button && ewidget != combo_box->priv->box) ||
3378       gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (combo_box->priv->button)))
3379     return FALSE;
3380
3381   if (combo_box->priv->focus_on_click && 
3382       !GTK_WIDGET_HAS_FOCUS (combo_box->priv->button))
3383     gtk_widget_grab_focus (combo_box->priv->button);
3384
3385   gtk_combo_box_popup (combo_box);
3386
3387   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
3388                                 TRUE);
3389
3390   combo_box->priv->auto_scroll = FALSE;
3391   if (combo_box->priv->scroll_timer == 0)
3392     combo_box->priv->scroll_timer = g_timeout_add (SCROLL_TIME, 
3393                                                    (GSourceFunc) gtk_combo_box_list_scroll_timeout, 
3394                                                    combo_box);
3395
3396   combo_box->priv->popup_in_progress = TRUE;
3397
3398   return TRUE;
3399 }
3400
3401 static gboolean
3402 gtk_combo_box_list_button_released (GtkWidget      *widget,
3403                                     GdkEventButton *event,
3404                                     gpointer        data)
3405 {
3406   gboolean ret;
3407   GtkTreePath *path = NULL;
3408   GtkTreeIter iter;
3409
3410   GtkComboBox *combo_box = GTK_COMBO_BOX (data);
3411
3412   gboolean popup_in_progress = FALSE;
3413
3414   GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *)event);
3415
3416   if (combo_box->priv->popup_in_progress)
3417     {
3418       popup_in_progress = TRUE;
3419       combo_box->priv->popup_in_progress = FALSE;
3420     }
3421
3422   gtk_tree_view_set_hover_expand (GTK_TREE_VIEW (combo_box->priv->tree_view), 
3423                                   FALSE);
3424   if (combo_box->priv->scroll_timer)
3425     {
3426       g_source_remove (combo_box->priv->scroll_timer);
3427       combo_box->priv->scroll_timer = 0;
3428     }
3429
3430   if (ewidget != combo_box->priv->tree_view)
3431     {
3432       if ((ewidget == combo_box->priv->button || 
3433            ewidget == combo_box->priv->box) &&
3434           !popup_in_progress &&
3435           gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (combo_box->priv->button)))
3436         {
3437           gtk_combo_box_popdown (combo_box);
3438           return TRUE;
3439         }
3440
3441       /* released outside treeview */
3442       if (ewidget != combo_box->priv->button && 
3443           ewidget != combo_box->priv->box)
3444         {
3445           gtk_combo_box_popdown (combo_box);
3446
3447           return TRUE;
3448         }
3449
3450       return FALSE;
3451     }
3452
3453   /* select something cool */
3454   ret = gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (combo_box->priv->tree_view),
3455                                        event->x, event->y,
3456                                        &path,
3457                                        NULL, NULL, NULL);
3458
3459   if (!ret)
3460     return TRUE; /* clicked outside window? */
3461
3462   gtk_tree_model_get_iter (combo_box->priv->model, &iter, path);
3463   gtk_tree_path_free (path);
3464
3465   if (tree_column_row_is_sensitive (combo_box, &iter))
3466     gtk_combo_box_set_active_iter (combo_box, &iter);
3467
3468   gtk_combo_box_popdown (combo_box);
3469
3470   return TRUE;
3471 }
3472
3473 static gboolean
3474 gtk_combo_box_key_press (GtkWidget   *widget,
3475                          GdkEventKey *event,
3476                          gpointer     data)
3477 {
3478   GtkComboBox *combo_box = GTK_COMBO_BOX (data);
3479   guint state = event->state & gtk_accelerator_get_default_mod_mask ();
3480   gboolean found;
3481   GtkTreeIter iter;
3482   GtkTreeIter new_iter;
3483
3484   if (combo_box->priv->model == NULL)
3485     return FALSE;
3486
3487   if ((event->keyval == GDK_Down || event->keyval == GDK_KP_Down) && 
3488       state == GDK_MOD1_MASK)
3489     {
3490       gtk_combo_box_popup (combo_box);
3491
3492       return TRUE;
3493     }
3494
3495   if (state != 0)
3496     return FALSE;
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   combo_box->priv->popup_idle_id = 0;
4968
4969   GDK_THREADS_LEAVE ();
4970
4971   return FALSE;
4972 }
4973
4974 static void
4975 gtk_combo_box_start_editing (GtkCellEditable *cell_editable,
4976                              GdkEvent        *event)
4977 {
4978   GtkComboBox *combo_box = GTK_COMBO_BOX (cell_editable);
4979
4980   combo_box->priv->is_cell_renderer = TRUE;
4981
4982   if (combo_box->priv->cell_view)
4983     {
4984       g_signal_connect_object (combo_box->priv->button, "key_press_event",
4985                                G_CALLBACK (gtk_cell_editable_key_press), 
4986                                cell_editable, 0);  
4987
4988       gtk_widget_grab_focus (combo_box->priv->button);
4989     }
4990   else
4991     {
4992       g_signal_connect_object (GTK_BIN (combo_box)->child, "key_press_event",
4993                                G_CALLBACK (gtk_cell_editable_key_press), 
4994                                cell_editable, 0);  
4995
4996       gtk_widget_grab_focus (GTK_WIDGET (GTK_BIN (combo_box)->child));
4997       GTK_WIDGET_UNSET_FLAGS (combo_box->priv->button, GTK_CAN_FOCUS);
4998     }
4999
5000   /* we do the immediate popup only for the optionmenu-like 
5001    * appearance 
5002    */  
5003   if (combo_box->priv->is_cell_renderer && 
5004       combo_box->priv->cell_view && !combo_box->priv->tree_view)
5005     combo_box->priv->popup_idle_id = g_idle_add (popup_idle, combo_box);
5006 }
5007
5008
5009 /**
5010  * gtk_combo_box_get_add_tearoffs:
5011  * @combo_box: a #GtkComboBox
5012  * 
5013  * Gets the current value of the :add-tearoffs property.
5014  * 
5015  * Return value: the current value of the :add-tearoffs property.
5016  **/
5017 gboolean
5018 gtk_combo_box_get_add_tearoffs (GtkComboBox *combo_box)
5019 {
5020   g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
5021
5022   return combo_box->priv->add_tearoffs;
5023 }
5024
5025 /**
5026  * gtk_combo_box_set_add_tearoffs:
5027  * @combo_box: a #GtkComboBox 
5028  * @add_tearoffs: %TRUE to add tearoff menu items
5029  *  
5030  * Sets whether the popup menu should have a tearoff 
5031  * menu item.
5032  *
5033  * Since: 2.6
5034  **/
5035 void
5036 gtk_combo_box_set_add_tearoffs (GtkComboBox *combo_box,
5037                                 gboolean     add_tearoffs)
5038 {
5039   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5040
5041   add_tearoffs = add_tearoffs != FALSE;
5042
5043   if (combo_box->priv->add_tearoffs != add_tearoffs)
5044     {
5045       combo_box->priv->add_tearoffs = add_tearoffs;
5046       gtk_combo_box_check_appearance (combo_box);
5047       gtk_combo_box_relayout (combo_box);
5048       g_object_notify (G_OBJECT (combo_box), "add-tearoffs");
5049     }
5050 }
5051
5052 gboolean
5053 _gtk_combo_box_editing_canceled (GtkComboBox *combo_box)
5054 {
5055   g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), TRUE);
5056
5057   return combo_box->priv->editing_canceled;
5058 }
5059
5060 /**
5061  * gtk_combo_box_get_popup_accessible:
5062  * @combo_box: a #GtkComboBox
5063  *
5064  * Gets the accessible object corresponding to the combo box's popup.
5065  *
5066  * This function is mostly intended for use by accessibility technologies;
5067  * applications should have little use for it.
5068  *
5069  * Returns: the accessible object corresponding to the combo box's popup.
5070  *
5071  * Since: 2.6
5072  **/
5073 AtkObject*
5074 gtk_combo_box_get_popup_accessible (GtkComboBox *combo_box)
5075 {
5076   AtkObject *atk_obj;
5077
5078   g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
5079
5080   if (combo_box->priv->popup_widget)
5081     {
5082       atk_obj = gtk_widget_get_accessible (combo_box->priv->popup_widget);
5083       return atk_obj;
5084     }
5085
5086   return NULL;
5087 }
5088
5089 /**
5090  * gtk_combo_box_get_row_separator_func:
5091  * @combo_box: a #GtkComboBox
5092  * 
5093  * Returns the current row separator function.
5094  * 
5095  * Return value: the current row separator function.
5096  *
5097  * Since: 2.6
5098  **/
5099 GtkTreeViewRowSeparatorFunc 
5100 gtk_combo_box_get_row_separator_func (GtkComboBox *combo_box)
5101 {
5102   g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
5103
5104   return combo_box->priv->row_separator_func;
5105 }
5106
5107 /**
5108  * gtk_combo_box_set_row_separator_func:
5109  * @combo_box: a #GtkComboBox
5110  * @func: a #GtkTreeViewRowSeparatorFunc
5111  * @data: user data to pass to @func, or %NULL
5112  * @destroy: destroy notifier for @data, or %NULL
5113  * 
5114  * Sets the row separator function, which is used to determine
5115  * whether a row should be drawn as a separator. If the row separator
5116  * function is %NULL, no separators are drawn. This is the default value.
5117  *
5118  * Since: 2.6
5119  **/
5120 void
5121 gtk_combo_box_set_row_separator_func (GtkComboBox                 *combo_box,
5122                                       GtkTreeViewRowSeparatorFunc  func,
5123                                       gpointer                     data,
5124                                       GtkDestroyNotify             destroy)
5125 {
5126   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5127
5128   if (combo_box->priv->row_separator_destroy)
5129     (* combo_box->priv->row_separator_destroy) (combo_box->priv->row_separator_data);
5130
5131   combo_box->priv->row_separator_func = func;
5132   combo_box->priv->row_separator_data = data;
5133   combo_box->priv->row_separator_destroy = destroy;
5134
5135   if (combo_box->priv->tree_view)
5136     gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (combo_box->priv->tree_view), 
5137                                           func, data, NULL);
5138
5139   gtk_combo_box_relayout (combo_box);
5140
5141   gtk_widget_queue_draw (GTK_WIDGET (combo_box));
5142 }
5143
5144
5145 /**
5146  * gtk_combo_box_set_focus_on_click:
5147  * @combo: a #GtkComboBox
5148  * @focus_on_click: whether the combo box grabs focus when clicked 
5149  *    with the mouse
5150  * 
5151  * Sets whether the combo box will grab focus when it is clicked with 
5152  * the mouse. Making mouse clicks not grab focus is useful in places 
5153  * like toolbars where you don't want the keyboard focus removed from 
5154  * the main area of the application.
5155  *
5156  * Since: 2.6
5157  **/
5158 void
5159 gtk_combo_box_set_focus_on_click (GtkComboBox *combo_box,
5160                                   gboolean     focus_on_click)
5161 {
5162   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5163   
5164   focus_on_click = focus_on_click != FALSE;
5165
5166   if (combo_box->priv->focus_on_click != focus_on_click)
5167     {
5168       combo_box->priv->focus_on_click = focus_on_click;
5169
5170       if (combo_box->priv->button)
5171         gtk_button_set_focus_on_click (GTK_BUTTON (combo_box->priv->button),
5172                                        focus_on_click);
5173       
5174       g_object_notify (G_OBJECT (combo_box), "focus-on-click");
5175     }
5176 }
5177
5178 /**
5179  * gtk_combo_box_get_focus_on_click:
5180  * @combo: a #GtkComboBox
5181  * 
5182  * Returns whether the combo box grabs focus when it is clicked 
5183  * with the mouse. See gtk_combo_box_set_focus_on_click().
5184  *
5185  * Return value: %TRUE if the combo box grabs focus when it is 
5186  *     clicked with the mouse.
5187  *
5188  * Since: 2.6
5189  **/
5190 gboolean
5191 gtk_combo_box_get_focus_on_click (GtkComboBox *combo_box)
5192 {
5193   g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
5194   
5195   return combo_box->priv->focus_on_click;
5196 }
5197
5198
5199 #define __GTK_COMBO_BOX_C__
5200 #include "gtkaliasdef.c"