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