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