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