]> Pileus Git - ~andy/gtk/blob - gtk/gtkcombobox.c
Merge branch 'notebooks-without-mouse-scrolling'
[~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_size_request_get_size (GTK_SIZE_REQUEST (menu),
1533                              &req, NULL);
1534
1535   if (gtk_widget_get_direction (GTK_WIDGET (combo_box)) == GTK_TEXT_DIR_LTR)
1536     *x = sx;
1537   else
1538     *x = sx + child_allocation.width - req.width;
1539   *y = sy;
1540
1541   screen = gtk_widget_get_screen (GTK_WIDGET (combo_box));
1542   monitor_num = gdk_screen_get_monitor_at_window (screen, 
1543                                                   gtk_widget_get_window (GTK_WIDGET (combo_box)));
1544   gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1545   
1546   if (*x < monitor.x)
1547     *x = monitor.x;
1548   else if (*x + req.width > monitor.x + monitor.width)
1549     *x = monitor.x + monitor.width - req.width;
1550   
1551   if (monitor.y + monitor.height - *y - child_allocation.height >= req.height)
1552     *y += child_allocation.height;
1553   else if (*y - monitor.y >= req.height)
1554     *y -= req.height;
1555   else if (monitor.y + monitor.height - *y - child_allocation.height > *y - monitor.y)
1556     *y += child_allocation.height;
1557   else
1558     *y -= req.height;
1559
1560    *push_in = FALSE;
1561 }
1562
1563 static void
1564 gtk_combo_box_menu_position_over (GtkMenu  *menu,
1565                                   gint     *x,
1566                                   gint     *y,
1567                                   gboolean *push_in,
1568                                   gpointer  user_data)
1569 {
1570   GtkComboBox    *combo_box;
1571   GtkWidget      *active;
1572   GtkWidget      *child;
1573   GtkWidget      *widget;
1574   GtkAllocation   allocation;
1575   GtkAllocation   child_allocation;
1576   GList          *children;
1577   gint            screen_width;
1578   gint            menu_xpos;
1579   gint            menu_ypos;
1580   gint            menu_width;
1581
1582   combo_box = GTK_COMBO_BOX (user_data);
1583   widget = GTK_WIDGET (combo_box);
1584
1585   active = gtk_menu_get_active (GTK_MENU (combo_box->priv->popup_widget));
1586
1587   gtk_widget_get_allocation (widget, &allocation);
1588
1589   menu_xpos = allocation.x;
1590   menu_ypos = allocation.y + allocation.height / 2 - 2;
1591
1592   gtk_size_request_get_width (GTK_SIZE_REQUEST (menu), &menu_width, NULL);
1593
1594   if (active != NULL)
1595     {
1596       gtk_widget_get_allocation (active, &child_allocation);
1597       menu_ypos -= child_allocation.height / 2;
1598     }
1599
1600   children = GTK_MENU_SHELL (combo_box->priv->popup_widget)->children;
1601   while (children)
1602     {
1603       child = children->data;
1604
1605       if (active == child)
1606         break;
1607
1608       if (gtk_widget_get_visible (child))
1609         {
1610           gtk_widget_get_allocation (child, &child_allocation);
1611
1612           menu_ypos -= child_allocation.height;
1613         }
1614
1615       children = children->next;
1616     }
1617
1618   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
1619     menu_xpos = menu_xpos + allocation.width - menu_width;
1620
1621   gdk_window_get_root_coords (gtk_widget_get_window (widget),
1622                               menu_xpos, menu_ypos,
1623                               &menu_xpos, &menu_ypos);
1624
1625   /* Clamp the position on screen */
1626   screen_width = gdk_screen_get_width (gtk_widget_get_screen (widget));
1627   
1628   if (menu_xpos < 0)
1629     menu_xpos = 0;
1630   else if ((menu_xpos + menu_width) > screen_width)
1631     menu_xpos -= ((menu_xpos + menu_width) - screen_width);
1632
1633   *x = menu_xpos;
1634   *y = menu_ypos;
1635
1636   *push_in = TRUE;
1637 }
1638
1639 static void
1640 gtk_combo_box_menu_position (GtkMenu  *menu,
1641                              gint     *x,
1642                              gint     *y,
1643                              gint     *push_in,
1644                              gpointer  user_data)
1645 {
1646   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1647   GtkComboBoxPrivate *priv = combo_box->priv;
1648   GtkWidget *menu_item;
1649
1650
1651   if (priv->wrap_width > 0 || priv->cell_view == NULL)  
1652     gtk_combo_box_menu_position_below (menu, x, y, push_in, user_data);
1653   else
1654     {
1655       /* FIXME handle nested menus better */
1656       menu_item = gtk_menu_get_active (GTK_MENU (priv->popup_widget));
1657       if (menu_item)
1658         gtk_menu_shell_select_item (GTK_MENU_SHELL (priv->popup_widget), 
1659                                     menu_item);
1660
1661       gtk_combo_box_menu_position_over (menu, x, y, push_in, user_data);
1662     }
1663
1664   if (!gtk_widget_get_visible (GTK_MENU (priv->popup_widget)->toplevel))
1665     gtk_window_set_type_hint (GTK_WINDOW (GTK_MENU (priv->popup_widget)->toplevel),
1666                               GDK_WINDOW_TYPE_HINT_COMBO);
1667 }
1668
1669 static void
1670 gtk_combo_box_list_position (GtkComboBox *combo_box, 
1671                              gint        *x, 
1672                              gint        *y, 
1673                              gint        *width,
1674                              gint        *height)
1675 {
1676   GtkComboBoxPrivate *priv = combo_box->priv;
1677   GtkAllocation allocation;
1678   GdkScreen *screen;
1679   gint monitor_num;
1680   GdkRectangle monitor;
1681   GtkRequisition popup_req;
1682   GtkPolicyType hpolicy, vpolicy;
1683   GdkWindow *window;
1684
1685   /* under windows, the drop down list is as wide as the combo box itself.
1686      see bug #340204 */
1687   GtkWidget *widget = GTK_WIDGET (combo_box);
1688
1689   *x = *y = 0;
1690
1691   gtk_widget_get_allocation (widget, &allocation);
1692
1693   if (!gtk_widget_get_has_window (widget))
1694     {
1695       *x += allocation.x;
1696       *y += allocation.y;
1697     }
1698
1699   window = gtk_widget_get_window (widget);
1700
1701   gdk_window_get_root_coords (gtk_widget_get_window (widget),
1702                               *x, *y, x, y);
1703
1704   *width = allocation.width;
1705
1706   hpolicy = vpolicy = GTK_POLICY_NEVER;
1707   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
1708                                   hpolicy, vpolicy);
1709   gtk_size_request_get_size (GTK_SIZE_REQUEST (priv->scrolled_window),
1710                              &popup_req, NULL);
1711
1712   if (popup_req.width > *width)
1713     {
1714       hpolicy = GTK_POLICY_ALWAYS;
1715       gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
1716                                       hpolicy, vpolicy);
1717       gtk_size_request_get_size (GTK_SIZE_REQUEST (priv->scrolled_window),
1718                                  &popup_req, NULL);
1719     }
1720
1721   *height = popup_req.height;
1722
1723   screen = gtk_widget_get_screen (GTK_WIDGET (combo_box));
1724   monitor_num = gdk_screen_get_monitor_at_window (screen, window);
1725   gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1726
1727   if (*x < monitor.x)
1728     *x = monitor.x;
1729   else if (*x + *width > monitor.x + monitor.width)
1730     *x = monitor.x + monitor.width - *width;
1731   
1732   if (*y + allocation.height + *height <= monitor.y + monitor.height)
1733     *y += allocation.height;
1734   else if (*y - *height >= monitor.y)
1735     *y -= *height;
1736   else if (monitor.y + monitor.height - (*y + allocation.height) > *y - monitor.y)
1737     {
1738       *y += allocation.height;
1739       *height = monitor.y + monitor.height - *y;
1740     }
1741   else 
1742     {
1743       *height = *y - monitor.y;
1744       *y = monitor.y;
1745     }
1746
1747   if (popup_req.height > *height)
1748     {
1749       vpolicy = GTK_POLICY_ALWAYS;
1750       
1751       gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
1752                                       hpolicy, vpolicy);
1753     }
1754
1755
1756 static gboolean
1757 cell_view_is_sensitive (GtkCellView *cell_view)
1758 {
1759   GList *cells, *list;
1760   gboolean sensitive;
1761   
1762   cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (cell_view));
1763
1764   sensitive = FALSE;
1765   for (list = cells; list; list = list->next)
1766     {
1767       g_object_get (list->data, "sensitive", &sensitive, NULL);
1768       
1769       if (sensitive)
1770         break;
1771     }
1772   g_list_free (cells);
1773
1774   return sensitive;
1775 }
1776
1777 static gboolean
1778 tree_column_row_is_sensitive (GtkComboBox *combo_box,
1779                               GtkTreeIter *iter)
1780 {
1781   GtkComboBoxPrivate *priv = combo_box->priv;
1782   GList *cells, *list;
1783   gboolean sensitive;
1784
1785   if (!priv->column)
1786     return TRUE;
1787
1788   if (priv->row_separator_func)
1789     {
1790       if (priv->row_separator_func (priv->model, iter,
1791                                     priv->row_separator_data))
1792         return FALSE;
1793     }
1794
1795   gtk_tree_view_column_cell_set_cell_data (priv->column,
1796                                            priv->model,
1797                                            iter, FALSE, FALSE);
1798
1799   cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (priv->column));
1800
1801   sensitive = FALSE;
1802   for (list = cells; list; list = list->next)
1803     {
1804       g_object_get (list->data, "sensitive", &sensitive, NULL);
1805       
1806       if (sensitive)
1807         break;
1808     }
1809   g_list_free (cells);
1810
1811   return sensitive;
1812 }
1813
1814 static void
1815 update_menu_sensitivity (GtkComboBox *combo_box,
1816                          GtkWidget   *menu)
1817 {
1818   GtkComboBoxPrivate *priv = combo_box->priv;
1819   GList *children, *child;
1820   GtkWidget *item, *submenu, *separator;
1821   GtkWidget *cell_view;
1822   gboolean sensitive;
1823
1824   if (!priv->model)
1825     return;
1826
1827   children = gtk_container_get_children (GTK_CONTAINER (menu));
1828
1829   for (child = children; child; child = child->next)
1830     {
1831       item = GTK_WIDGET (child->data);
1832       cell_view = gtk_bin_get_child (GTK_BIN (item));
1833
1834       if (!GTK_IS_CELL_VIEW (cell_view))
1835         continue;
1836       
1837       submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (item));
1838       if (submenu != NULL)
1839         {
1840           gtk_widget_set_sensitive (item, TRUE);
1841           update_menu_sensitivity (combo_box, submenu);
1842         }
1843       else
1844         {
1845           sensitive = cell_view_is_sensitive (GTK_CELL_VIEW (cell_view));
1846
1847           if (menu != priv->popup_widget && child == children)
1848             {
1849               separator = GTK_WIDGET (child->next->data);
1850               g_object_set (item, "visible", sensitive, NULL);
1851               g_object_set (separator, "visible", sensitive, NULL);
1852             }
1853           else
1854             gtk_widget_set_sensitive (item, sensitive);
1855         }
1856     }
1857
1858   g_list_free (children);
1859 }
1860
1861 static void 
1862 gtk_combo_box_menu_popup (GtkComboBox *combo_box,
1863                           guint        button, 
1864                           guint32      activate_time)
1865 {
1866   GtkComboBoxPrivate *priv = combo_box->priv;
1867   GtkTreePath *path;
1868   gint active_item;
1869   gint width, min_width;
1870   
1871   update_menu_sensitivity (combo_box, priv->popup_widget);
1872
1873   active_item = -1;
1874   if (gtk_tree_row_reference_valid (priv->active_row))
1875     {
1876       path = gtk_tree_row_reference_get_path (priv->active_row);
1877       active_item = gtk_tree_path_get_indices (path)[0];
1878       gtk_tree_path_free (path);
1879       
1880       if (priv->add_tearoffs)
1881         active_item++;
1882     }
1883
1884   /* FIXME handle nested menus better */
1885   gtk_menu_set_active (GTK_MENU (priv->popup_widget), active_item);
1886   
1887   if (priv->wrap_width == 0)
1888     {
1889       GtkAllocation allocation;
1890
1891       gtk_widget_get_allocation (GTK_WIDGET (combo_box), &allocation);
1892       width = allocation.width;
1893       gtk_widget_set_size_request (priv->popup_widget, -1, -1);
1894       gtk_size_request_get_width (GTK_SIZE_REQUEST (priv->popup_widget), &min_width, NULL);
1895       
1896       gtk_widget_set_size_request (priv->popup_widget,
1897                                    MAX (width, min_width), -1);
1898     }
1899   
1900   gtk_menu_popup (GTK_MENU (priv->popup_widget),
1901                   NULL, NULL,
1902                   gtk_combo_box_menu_position, combo_box,
1903                   button, activate_time);
1904 }
1905
1906 static gboolean
1907 popup_grab_on_window (GdkWindow *window,
1908                       GdkDevice *keyboard,
1909                       GdkDevice *pointer,
1910                       guint32    activate_time)
1911 {
1912   if (keyboard &&
1913       gdk_device_grab (keyboard, window,
1914                        GDK_OWNERSHIP_WINDOW, TRUE,
1915                        GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
1916                        NULL, activate_time) != GDK_GRAB_SUCCESS)
1917     return FALSE;
1918
1919   if (pointer &&
1920       gdk_device_grab (pointer, window,
1921                        GDK_OWNERSHIP_WINDOW, TRUE,
1922                        GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1923                        GDK_POINTER_MOTION_MASK,
1924                        NULL, activate_time) != GDK_GRAB_SUCCESS)
1925     {
1926       if (keyboard)
1927         gdk_device_ungrab (keyboard, activate_time);
1928
1929       return FALSE;
1930     }
1931
1932   return TRUE;
1933 }
1934
1935 /**
1936  * gtk_combo_box_popup:
1937  * @combo_box: a #GtkComboBox
1938  * 
1939  * Pops up the menu or dropdown list of @combo_box. 
1940  *
1941  * This function is mostly intended for use by accessibility technologies;
1942  * applications should have little use for it.
1943  *
1944  * Since: 2.4
1945  */
1946 void
1947 gtk_combo_box_popup (GtkComboBox *combo_box)
1948 {
1949   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
1950
1951   g_signal_emit (combo_box, combo_box_signals[POPUP], 0);
1952 }
1953
1954 /**
1955  * gtk_combo_box_popup_for_device:
1956  * @combo_box: a #GtkComboBox
1957  * @device: a #GdkDevice
1958  *
1959  * Pops up the menu or dropdown list of @combo_box, the popup window
1960  * will be grabbed so only @device and its associated pointer/keyboard
1961  * are the only #GdkDevice<!-- -->s able to send events to it.
1962  *
1963  * Since: 3.0
1964  **/
1965 void
1966 gtk_combo_box_popup_for_device (GtkComboBox *combo_box,
1967                                 GdkDevice   *device)
1968 {
1969   GtkComboBoxPrivate *priv = combo_box->priv;
1970   gint x, y, width, height;
1971   GtkTreePath *path = NULL, *ppath;
1972   GtkWidget *toplevel;
1973   GdkDevice *keyboard, *pointer;
1974   guint32 time;
1975
1976   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
1977   g_return_if_fail (GDK_IS_DEVICE (device));
1978
1979   if (!gtk_widget_get_realized (GTK_WIDGET (combo_box)))
1980     return;
1981
1982   if (gtk_widget_get_mapped (priv->popup_widget))
1983     return;
1984
1985   if (priv->grab_pointer && priv->grab_keyboard)
1986     return;
1987
1988   time = gtk_get_current_event_time ();
1989
1990   if (device->source == GDK_SOURCE_KEYBOARD)
1991     {
1992       keyboard = device;
1993       pointer = gdk_device_get_associated_device (device);
1994     }
1995   else
1996     {
1997       pointer = device;
1998       keyboard = gdk_device_get_associated_device (device);
1999     }
2000
2001   if (GTK_IS_MENU (priv->popup_widget))
2002     {
2003       gtk_combo_box_menu_popup (combo_box, 
2004                                 priv->activate_button,
2005                                 priv->activate_time);
2006       return;
2007     }
2008
2009   toplevel = gtk_widget_get_toplevel (GTK_WIDGET (combo_box));
2010   if (GTK_IS_WINDOW (toplevel))
2011     gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)), 
2012                                  GTK_WINDOW (priv->popup_window));
2013
2014   gtk_widget_show_all (priv->scrolled_window);
2015   gtk_combo_box_list_position (combo_box, &x, &y, &width, &height);
2016   
2017   gtk_widget_set_size_request (priv->popup_window, width, height);  
2018   gtk_window_move (GTK_WINDOW (priv->popup_window), x, y);
2019
2020   if (gtk_tree_row_reference_valid (priv->active_row))
2021     {
2022       path = gtk_tree_row_reference_get_path (priv->active_row);
2023       ppath = gtk_tree_path_copy (path);
2024       if (gtk_tree_path_up (ppath))
2025         gtk_tree_view_expand_to_path (GTK_TREE_VIEW (priv->tree_view),
2026                                       ppath);
2027       gtk_tree_path_free (ppath);
2028     }
2029   gtk_tree_view_set_hover_expand (GTK_TREE_VIEW (priv->tree_view), 
2030                                   TRUE);
2031   
2032   /* popup */
2033   gtk_widget_show (priv->popup_window);
2034
2035   if (path)
2036     {
2037       gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->tree_view),
2038                                 path, NULL, FALSE);
2039       gtk_tree_path_free (path);
2040     }
2041
2042   gtk_widget_grab_focus (priv->popup_window);
2043   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button),
2044                                 TRUE);
2045
2046   if (!gtk_widget_has_focus (priv->tree_view))
2047     gtk_widget_grab_focus (priv->tree_view);
2048
2049   if (!popup_grab_on_window (gtk_widget_get_window (priv->popup_window),
2050                              keyboard, pointer, time))
2051     {
2052       gtk_widget_hide (priv->popup_window);
2053       return;
2054     }
2055
2056   gtk_device_grab_add (priv->popup_window, pointer, TRUE);
2057   priv->grab_pointer = pointer;
2058   priv->grab_keyboard = keyboard;
2059 }
2060
2061 static void
2062 gtk_combo_box_real_popup (GtkComboBox *combo_box)
2063 {
2064   GdkDevice *device;
2065
2066   device = gtk_get_current_event_device ();
2067
2068   if (!device)
2069     {
2070       GdkDeviceManager *device_manager;
2071       GdkDisplay *display;
2072       GList *devices;
2073
2074       display = gtk_widget_get_display (GTK_WIDGET (combo_box));
2075       device_manager = gdk_display_get_device_manager (display);
2076
2077       /* No device was set, pick the first master device */
2078       devices = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
2079       device = devices->data;
2080       g_list_free (devices);
2081     }
2082
2083   gtk_combo_box_popup_for_device (combo_box, device);
2084 }
2085
2086 static gboolean
2087 gtk_combo_box_real_popdown (GtkComboBox *combo_box)
2088 {
2089   if (combo_box->priv->popup_shown)
2090     {
2091       gtk_combo_box_popdown (combo_box);
2092       return TRUE;
2093     }
2094
2095   return FALSE;
2096 }
2097
2098 /**
2099  * gtk_combo_box_popdown:
2100  * @combo_box: a #GtkComboBox
2101  * 
2102  * Hides the menu or dropdown list of @combo_box.
2103  *
2104  * This function is mostly intended for use by accessibility technologies;
2105  * applications should have little use for it.
2106  *
2107  * Since: 2.4
2108  */
2109 void
2110 gtk_combo_box_popdown (GtkComboBox *combo_box)
2111 {
2112   GtkComboBoxPrivate *priv = combo_box->priv;
2113
2114   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2115
2116   if (GTK_IS_MENU (priv->popup_widget))
2117     {
2118       gtk_menu_popdown (GTK_MENU (priv->popup_widget));
2119       return;
2120     }
2121
2122   if (!gtk_widget_get_realized (GTK_WIDGET (combo_box)))
2123     return;
2124
2125   gtk_device_grab_remove (priv->popup_window, priv->grab_pointer);
2126   gtk_widget_hide_all (priv->popup_window);
2127   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button),
2128                                 FALSE);
2129
2130   priv->grab_pointer = NULL;
2131   priv->grab_keyboard = NULL;
2132 }
2133
2134 static void
2135 gtk_combo_box_update_requested_width (GtkComboBox *combo_box,
2136                                       GtkTreePath *path)
2137 {
2138   GtkComboBoxPrivate *priv = combo_box->priv;
2139   gint                padding, min_width, nat_width;
2140
2141   if (priv->cell_view)
2142     gtk_widget_style_get (priv->cell_view,
2143                           "focus-line-width", &padding,
2144                           NULL);
2145   else
2146     padding = 0;
2147
2148   /* add some pixels for good measure */
2149   padding += BONUS_PADDING;
2150
2151   if (priv->cell_view)
2152     gtk_cell_view_get_desired_width_of_row (GTK_CELL_VIEW (priv->cell_view),
2153                                             path, &min_width, &nat_width);
2154   else
2155     min_width = nat_width = 0;
2156
2157   min_width += padding;
2158   nat_width += padding;
2159
2160   if (min_width > priv->minimum_width || nat_width > priv->natural_width)
2161     {
2162       priv->minimum_width = MAX (priv->minimum_width, min_width);
2163       priv->natural_width = MAX (priv->natural_width, nat_width);
2164
2165       if (priv->cell_view)
2166         {
2167           gtk_widget_set_size_request (priv->cell_view, min_width, -1);
2168           gtk_widget_queue_resize (priv->cell_view);
2169         }
2170     }
2171 }
2172
2173 #define GTK_COMBO_BOX_SIZE_ALLOCATE_BUTTON                                      \
2174   gtk_size_request_get_size (GTK_SIZE_REQUEST (combo_box->priv->button),        \
2175                              &req, NULL);                                       \
2176                                                                                 \
2177   if (is_rtl)                                                                   \
2178     child.x = allocation->x + shadow_width;                                     \
2179   else                                                                          \
2180     child.x = allocation->x + allocation->width - req.width - shadow_width;     \
2181                                                                                 \
2182   child.y = allocation->y + shadow_height;                                      \
2183   child.width = req.width;                                                      \
2184   child.height = allocation->height - 2 * shadow_height;                        \
2185   child.width = MAX (1, child.width);                                           \
2186   child.height = MAX (1, child.height);                                         \
2187                                                                                 \
2188   gtk_widget_size_allocate (combo_box->priv->button, &child);
2189
2190 static void
2191 gtk_combo_box_size_allocate (GtkWidget     *widget,
2192                              GtkAllocation *allocation)
2193 {
2194   GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
2195   GtkComboBoxPrivate *priv = combo_box->priv;
2196   GtkWidget *child_widget;
2197   gint shadow_width, shadow_height;
2198   gint focus_width, focus_pad;
2199   GtkAllocation child;
2200   GtkRequisition req;
2201   GtkStyle *style;
2202   gboolean is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
2203
2204   gtk_widget_set_allocation (widget, allocation);
2205   child_widget = gtk_bin_get_child (GTK_BIN (widget));
2206
2207   style = gtk_widget_get_style (widget);
2208   gtk_widget_style_get (widget,
2209                         "focus-line-width", &focus_width,
2210                         "focus-padding", &focus_pad,
2211                         NULL);
2212
2213   if (GTK_SHADOW_NONE != priv->shadow_type)
2214     {
2215       shadow_width = style->xthickness;
2216       shadow_height = style->ythickness;
2217     }
2218   else
2219     {
2220       shadow_width = 0;
2221       shadow_height = 0;
2222     }
2223
2224   if (!priv->tree_view)
2225     {
2226       if (priv->cell_view)
2227         {
2228           gint xthickness, ythickness;
2229           gint width;
2230           guint border_width;
2231
2232           /* menu mode */
2233           allocation->x += shadow_width;
2234           allocation->y += shadow_height;
2235           allocation->width -= 2 * shadow_width;
2236           allocation->height -= 2 * shadow_height;
2237
2238           gtk_widget_size_allocate (priv->button, allocation);
2239
2240           /* set some things ready */
2241           border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->button));
2242           style = gtk_widget_get_style (priv->button);
2243           xthickness = style->xthickness;
2244           ythickness = style->ythickness;
2245
2246           child.x = allocation->x;
2247           child.y = allocation->y;
2248           width = allocation->width;
2249           child.height = allocation->height;
2250
2251           if (!priv->is_cell_renderer)
2252             {
2253               child.x += border_width + xthickness + focus_width + focus_pad;
2254               child.y += border_width + ythickness + focus_width + focus_pad;
2255               width -= 2 * (child.x - allocation->x);
2256               child.height -= 2 * (child.y - allocation->y);
2257             }
2258
2259
2260           /* handle the children */
2261           gtk_size_request_get_size (GTK_SIZE_REQUEST (priv->arrow),
2262                                      &req, NULL);
2263           child.width = req.width;
2264           if (!is_rtl)
2265             child.x += width - req.width;
2266           child.width = MAX (1, child.width);
2267           child.height = MAX (1, child.height);
2268           gtk_widget_size_allocate (priv->arrow, &child);
2269           if (is_rtl)
2270             child.x += req.width;
2271           gtk_size_request_get_size (GTK_SIZE_REQUEST (priv->separator),
2272                                      &req, NULL);
2273           child.width = req.width;
2274           if (!is_rtl)
2275             child.x -= req.width;
2276           child.width = MAX (1, child.width);
2277           child.height = MAX (1, child.height);
2278           gtk_widget_size_allocate (priv->separator, &child);
2279
2280           if (is_rtl)
2281             {
2282               child.x += req.width;
2283               child.width = allocation->x + allocation->width 
2284                 - (border_width + xthickness + focus_width + focus_pad) 
2285                 - child.x;
2286             }
2287           else 
2288             {
2289               child.width = child.x;
2290               child.x = allocation->x 
2291                 + border_width + xthickness + focus_width + focus_pad;
2292               child.width -= child.x;
2293             }
2294
2295           if (gtk_widget_get_visible (priv->popup_widget))
2296             {
2297               gint width, min_width;
2298
2299               if (priv->wrap_width == 0)
2300                 {
2301                   GtkAllocation combo_box_allocation;
2302
2303                   gtk_widget_get_allocation (GTK_WIDGET (combo_box), &combo_box_allocation);
2304                   width = combo_box_allocation.width;
2305                   gtk_widget_set_size_request (priv->popup_widget, -1, -1);
2306                   gtk_size_request_get_width (GTK_SIZE_REQUEST (priv->popup_widget), &min_width, NULL);
2307                   gtk_widget_set_size_request (priv->popup_widget,
2308                     MAX (width, min_width), -1);
2309                }
2310
2311               /* reposition the menu after giving it a new width */
2312               gtk_menu_reposition (GTK_MENU (priv->popup_widget));
2313             }
2314
2315           child.width = MAX (1, child.width);
2316           child.height = MAX (1, child.height);
2317           gtk_widget_size_allocate (child_widget, &child);
2318         }
2319       else
2320         {
2321           GTK_COMBO_BOX_SIZE_ALLOCATE_BUTTON
2322
2323           if (is_rtl)
2324             child.x = allocation->x + req.width + shadow_width;
2325           else
2326             child.x = allocation->x + shadow_width;
2327           child.y = allocation->y + shadow_height;
2328           child.width = allocation->width - req.width - 2 * shadow_width;
2329           child.width = MAX (1, child.width);
2330           child.height = MAX (1, child.height);
2331           gtk_widget_size_allocate (child_widget, &child);
2332         }
2333     }
2334   else
2335     {
2336       /* list mode */
2337
2338       /* Combobox thickness + border-width */
2339       guint border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
2340       int delta_x = shadow_width + border_width;
2341       int delta_y = shadow_height + border_width;
2342
2343       /* button */
2344       GTK_COMBO_BOX_SIZE_ALLOCATE_BUTTON
2345
2346       /* frame */
2347       if (is_rtl)
2348         child.x = allocation->x + req.width;
2349       else
2350         child.x = allocation->x;
2351
2352       child.y = allocation->y;
2353       child.width = allocation->width - req.width;
2354       child.height = allocation->height;
2355
2356       if (priv->cell_view_frame)
2357         {
2358           child.x += delta_x;
2359           child.y += delta_y;
2360           child.width = MAX (1, child.width - delta_x * 2);
2361           child.height = MAX (1, child.height - delta_y * 2);
2362           gtk_widget_size_allocate (priv->cell_view_frame, &child);
2363
2364           /* the sample */
2365           if (priv->has_frame)
2366             {
2367               border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->cell_view_frame));
2368               style = gtk_widget_get_style (priv->cell_view_frame);
2369               delta_x = border_width + style->xthickness;
2370               delta_y = border_width + style->ythickness;
2371
2372               child.x += delta_x;
2373               child.y += delta_y;
2374               child.width -= delta_x * 2;
2375               child.height -= delta_y * 2;
2376             }
2377         }
2378       else
2379         {
2380           child.x += delta_x;
2381           child.y += delta_y;
2382           child.width -= delta_x * 2;
2383           child.height -= delta_y * 2;
2384         }
2385
2386       if (gtk_widget_get_visible (priv->popup_window))
2387         {
2388           gint x, y, width, height;
2389           gtk_combo_box_list_position (combo_box, &x, &y, &width, &height);
2390           gtk_window_move (GTK_WINDOW (priv->popup_window), x, y);
2391           gtk_widget_set_size_request (priv->popup_window, width, height);
2392         }
2393
2394       
2395       child.width = MAX (1, child.width);
2396       child.height = MAX (1, child.height);
2397       
2398       gtk_widget_size_allocate (child_widget, &child);
2399     }
2400 }
2401
2402 #undef GTK_COMBO_BOX_ALLOCATE_BUTTON
2403
2404 static void
2405 gtk_combo_box_unset_model (GtkComboBox *combo_box)
2406 {
2407   GtkComboBoxPrivate *priv = combo_box->priv;
2408
2409   if (priv->model)
2410     {
2411       g_signal_handler_disconnect (priv->model,
2412                                    priv->inserted_id);
2413       g_signal_handler_disconnect (priv->model,
2414                                    priv->deleted_id);
2415       g_signal_handler_disconnect (priv->model,
2416                                    priv->reordered_id);
2417       g_signal_handler_disconnect (priv->model,
2418                                    priv->changed_id);
2419     }
2420
2421   /* menu mode */
2422   if (!priv->tree_view)
2423     {
2424       if (priv->popup_widget)
2425         gtk_container_foreach (GTK_CONTAINER (priv->popup_widget),
2426                                (GtkCallback)gtk_widget_destroy, NULL);
2427     }
2428
2429   if (priv->model)
2430     {
2431       g_object_unref (priv->model);
2432       priv->model = NULL;
2433     }
2434
2435   if (priv->active_row)
2436     {
2437       gtk_tree_row_reference_free (priv->active_row);
2438       priv->active_row = NULL;
2439     }
2440
2441   if (priv->cell_view)
2442     gtk_cell_view_set_model (GTK_CELL_VIEW (priv->cell_view), NULL);
2443 }
2444
2445 static void
2446 gtk_combo_box_forall (GtkContainer *container,
2447                       gboolean      include_internals,
2448                       GtkCallback   callback,
2449                       gpointer      callback_data)
2450 {
2451   GtkComboBox *combo_box = GTK_COMBO_BOX (container);
2452   GtkComboBoxPrivate *priv = combo_box->priv;
2453   GtkWidget *child;
2454
2455   if (include_internals)
2456     {
2457       if (priv->button)
2458         (* callback) (priv->button, callback_data);
2459       if (priv->cell_view_frame)
2460         (* callback) (priv->cell_view_frame, callback_data);
2461     }
2462
2463   child = gtk_bin_get_child (GTK_BIN (container));
2464   if (child)
2465     (* callback) (child, callback_data);
2466 }
2467
2468 static void 
2469 gtk_combo_box_child_show (GtkWidget *widget,
2470                           GtkComboBox *combo_box)
2471 {
2472   GtkComboBoxPrivate *priv = combo_box->priv;
2473
2474   priv->popup_shown = TRUE;
2475   g_object_notify (G_OBJECT (combo_box), "popup-shown");
2476 }
2477
2478 static void 
2479 gtk_combo_box_child_hide (GtkWidget *widget,
2480                           GtkComboBox *combo_box)
2481 {
2482   GtkComboBoxPrivate *priv = combo_box->priv;
2483
2484   priv->popup_shown = FALSE;
2485   g_object_notify (G_OBJECT (combo_box), "popup-shown");
2486 }
2487
2488 static gboolean
2489 gtk_combo_box_expose_event (GtkWidget      *widget,
2490                             GdkEventExpose *event)
2491 {
2492   GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
2493   GtkComboBoxPrivate *priv = combo_box->priv;
2494
2495   if (gtk_widget_is_drawable (widget) &&
2496       GTK_SHADOW_NONE != priv->shadow_type)
2497     {
2498       GtkAllocation allocation;
2499
2500       gtk_widget_get_allocation (widget, &allocation);
2501
2502       gtk_paint_shadow (gtk_widget_get_style (widget),
2503                         gtk_widget_get_window (widget),
2504                         GTK_STATE_NORMAL, priv->shadow_type,
2505                         NULL, widget, "combobox",
2506                         allocation.x, allocation.y,
2507                         allocation.width, allocation.height);
2508     }
2509
2510   gtk_container_propagate_expose (GTK_CONTAINER (widget),
2511                                   priv->button, event);
2512
2513   if (priv->tree_view && priv->cell_view_frame)
2514     {
2515       gtk_container_propagate_expose (GTK_CONTAINER (widget),
2516                                       priv->cell_view_frame, event);
2517     }
2518
2519   gtk_container_propagate_expose (GTK_CONTAINER (widget),
2520                                   gtk_bin_get_child (GTK_BIN (widget)),
2521                                   event);
2522
2523   return FALSE;
2524 }
2525
2526 typedef struct {
2527   GtkComboBox *combo;
2528   GtkTreePath *path;
2529   GtkTreeIter iter;
2530   gboolean found;
2531   gboolean set;
2532   gboolean visible;
2533 } SearchData;
2534
2535 static gboolean
2536 path_visible (GtkTreeView *view,
2537               GtkTreePath *path)
2538 {
2539   GtkRBTree *tree;
2540   GtkRBNode *node;
2541   
2542   /* Note that we rely on the fact that collapsed rows don't have nodes 
2543    */
2544   return _gtk_tree_view_find_node (view, path, &tree, &node);
2545 }
2546
2547 static gboolean
2548 tree_next_func (GtkTreeModel *model,
2549                 GtkTreePath  *path,
2550                 GtkTreeIter  *iter,
2551                 gpointer      data)
2552 {
2553   SearchData *search_data = (SearchData *)data;
2554
2555   if (search_data->found) 
2556     {
2557       if (!tree_column_row_is_sensitive (search_data->combo, iter))
2558         return FALSE;
2559       
2560       if (search_data->visible &&
2561           !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
2562         return FALSE;
2563
2564       search_data->set = TRUE;
2565       search_data->iter = *iter;
2566
2567       return TRUE;
2568     }
2569  
2570   if (gtk_tree_path_compare (path, search_data->path) == 0)
2571     search_data->found = TRUE;
2572   
2573   return FALSE;
2574 }
2575
2576 static gboolean
2577 tree_next (GtkComboBox  *combo,
2578            GtkTreeModel *model,
2579            GtkTreeIter  *iter,
2580            GtkTreeIter  *next,
2581            gboolean      visible)
2582 {
2583   SearchData search_data;
2584
2585   search_data.combo = combo;
2586   search_data.path = gtk_tree_model_get_path (model, iter);
2587   search_data.visible = visible;
2588   search_data.found = FALSE;
2589   search_data.set = FALSE;
2590
2591   gtk_tree_model_foreach (model, tree_next_func, &search_data);
2592   
2593   *next = search_data.iter;
2594
2595   gtk_tree_path_free (search_data.path);
2596
2597   return search_data.set;
2598 }
2599
2600 static gboolean
2601 tree_prev_func (GtkTreeModel *model,
2602                 GtkTreePath  *path,
2603                 GtkTreeIter  *iter,
2604                 gpointer      data)
2605 {
2606   SearchData *search_data = (SearchData *)data;
2607
2608   if (gtk_tree_path_compare (path, search_data->path) == 0)
2609     {
2610       search_data->found = TRUE;
2611       return TRUE;
2612     }
2613   
2614   if (!tree_column_row_is_sensitive (search_data->combo, iter))
2615     return FALSE;
2616       
2617   if (search_data->visible &&
2618       !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
2619     return FALSE; 
2620   
2621   search_data->set = TRUE;
2622   search_data->iter = *iter;
2623   
2624   return FALSE; 
2625 }
2626
2627 static gboolean
2628 tree_prev (GtkComboBox  *combo,
2629            GtkTreeModel *model,
2630            GtkTreeIter  *iter,
2631            GtkTreeIter  *prev,
2632            gboolean      visible)
2633 {
2634   SearchData search_data;
2635
2636   search_data.combo = combo;
2637   search_data.path = gtk_tree_model_get_path (model, iter);
2638   search_data.visible = visible;
2639   search_data.found = FALSE;
2640   search_data.set = FALSE;
2641
2642   gtk_tree_model_foreach (model, tree_prev_func, &search_data);
2643   
2644   *prev = search_data.iter;
2645
2646   gtk_tree_path_free (search_data.path);
2647
2648   return search_data.set;
2649 }
2650
2651 static gboolean
2652 tree_last_func (GtkTreeModel *model,
2653                 GtkTreePath  *path,
2654                 GtkTreeIter  *iter,
2655                 gpointer      data)
2656 {
2657   SearchData *search_data = (SearchData *)data;
2658
2659   if (!tree_column_row_is_sensitive (search_data->combo, iter))
2660     return FALSE;
2661       
2662   /* Note that we rely on the fact that collapsed rows don't have nodes 
2663    */
2664   if (search_data->visible &&
2665       !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
2666     return FALSE; 
2667   
2668   search_data->set = TRUE;
2669   search_data->iter = *iter;
2670   
2671   return FALSE; 
2672 }
2673
2674 static gboolean
2675 tree_last (GtkComboBox  *combo,
2676            GtkTreeModel *model,
2677            GtkTreeIter  *last,
2678            gboolean      visible)
2679 {
2680   SearchData search_data;
2681
2682   search_data.combo = combo;
2683   search_data.visible = visible;
2684   search_data.set = FALSE;
2685
2686   gtk_tree_model_foreach (model, tree_last_func, &search_data);
2687   
2688   *last = search_data.iter;
2689
2690   return search_data.set;  
2691 }
2692
2693
2694 static gboolean
2695 tree_first_func (GtkTreeModel *model,
2696                  GtkTreePath  *path,
2697                  GtkTreeIter  *iter,
2698                  gpointer      data)
2699 {
2700   SearchData *search_data = (SearchData *)data;
2701
2702   if (!tree_column_row_is_sensitive (search_data->combo, iter))
2703     return FALSE;
2704   
2705   if (search_data->visible &&
2706       !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
2707     return FALSE;
2708   
2709   search_data->set = TRUE;
2710   search_data->iter = *iter;
2711   
2712   return TRUE;
2713 }
2714
2715 static gboolean
2716 tree_first (GtkComboBox  *combo,
2717             GtkTreeModel *model,
2718             GtkTreeIter  *first,
2719             gboolean      visible)
2720 {
2721   SearchData search_data;
2722   
2723   search_data.combo = combo;
2724   search_data.visible = visible;
2725   search_data.set = FALSE;
2726
2727   gtk_tree_model_foreach (model, tree_first_func, &search_data);
2728   
2729   *first = search_data.iter;
2730
2731   return search_data.set;  
2732 }
2733
2734 static gboolean
2735 gtk_combo_box_scroll_event (GtkWidget          *widget,
2736                             GdkEventScroll     *event)
2737 {
2738   GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
2739   gboolean found;
2740   GtkTreeIter iter;
2741   GtkTreeIter new_iter;
2742
2743   if (!gtk_combo_box_get_active_iter (combo_box, &iter))
2744     return TRUE;
2745   
2746   if (event->direction == GDK_SCROLL_UP)
2747     found = tree_prev (combo_box, combo_box->priv->model, 
2748                        &iter, &new_iter, FALSE);
2749   else
2750     found = tree_next (combo_box, combo_box->priv->model, 
2751                        &iter, &new_iter, FALSE);
2752   
2753   if (found)
2754     gtk_combo_box_set_active_iter (combo_box, &new_iter);
2755
2756   return TRUE;
2757 }
2758
2759 /*
2760  * menu style
2761  */
2762
2763 static void
2764 gtk_combo_box_sync_cells (GtkComboBox   *combo_box,
2765                           GtkCellLayout *cell_layout)
2766 {
2767   GtkComboBoxPrivate *priv = combo_box->priv;
2768   GSList *k;
2769
2770   for (k = priv->cells; k; k = k->next)
2771     {
2772       GSList *j;
2773       ComboCellInfo *info = (ComboCellInfo *)k->data;
2774
2775       if (info->pack == GTK_PACK_START)
2776         gtk_cell_layout_pack_start (cell_layout,
2777                                     info->cell, info->expand);
2778       else if (info->pack == GTK_PACK_END)
2779         gtk_cell_layout_pack_end (cell_layout,
2780                                   info->cell, info->expand);
2781
2782       gtk_cell_layout_set_cell_data_func (cell_layout,
2783                                           info->cell,
2784                                           combo_cell_data_func, info, NULL);
2785
2786       for (j = info->attributes; j; j = j->next->next)
2787         {
2788           gtk_cell_layout_add_attribute (cell_layout,
2789                                          info->cell,
2790                                          j->data,
2791                                          GPOINTER_TO_INT (j->next->data));
2792         }
2793     }
2794 }
2795
2796 static void
2797 gtk_combo_box_menu_setup (GtkComboBox *combo_box,
2798                           gboolean     add_children)
2799 {
2800   GtkComboBoxPrivate *priv = combo_box->priv;
2801   GtkWidget *child;
2802   GtkWidget *menu;
2803
2804   child = gtk_bin_get_child (GTK_BIN (combo_box));
2805
2806   if (priv->cell_view)
2807     {
2808       priv->button = gtk_toggle_button_new ();
2809       gtk_button_set_focus_on_click (GTK_BUTTON (priv->button),
2810                                      priv->focus_on_click);
2811
2812       g_signal_connect (priv->button, "toggled",
2813                         G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
2814       gtk_widget_set_parent (priv->button,
2815                              gtk_widget_get_parent (child));
2816
2817       priv->box = gtk_hbox_new (FALSE, 0);
2818       gtk_container_add (GTK_CONTAINER (priv->button), priv->box);
2819
2820       priv->separator = gtk_vseparator_new ();
2821       gtk_container_add (GTK_CONTAINER (priv->box), priv->separator);
2822
2823       priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
2824       gtk_container_add (GTK_CONTAINER (priv->box), priv->arrow);
2825
2826       gtk_widget_show_all (priv->button);
2827     }
2828   else
2829     {
2830       priv->button = gtk_toggle_button_new ();
2831       gtk_button_set_focus_on_click (GTK_BUTTON (priv->button),
2832                                      priv->focus_on_click);
2833
2834       g_signal_connect (priv->button, "toggled",
2835                         G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
2836       gtk_widget_set_parent (priv->button,
2837                              gtk_widget_get_parent (child));
2838
2839       priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
2840       gtk_container_add (GTK_CONTAINER (priv->button), priv->arrow);
2841       gtk_widget_show_all (priv->button);
2842     }
2843
2844   g_signal_connect (priv->button, "button-press-event",
2845                     G_CALLBACK (gtk_combo_box_menu_button_press),
2846                     combo_box);
2847   g_signal_connect (priv->button, "state-changed",
2848                     G_CALLBACK (gtk_combo_box_button_state_changed), 
2849                     combo_box);
2850
2851   /* create our funky menu */
2852   menu = gtk_menu_new ();
2853   gtk_widget_set_name (menu, "gtk-combobox-popup-menu");
2854   gtk_menu_set_reserve_toggle_size (GTK_MENU (menu), FALSE);
2855   
2856   g_signal_connect (menu, "key-press-event",
2857                     G_CALLBACK (gtk_combo_box_menu_key_press), combo_box);
2858   gtk_combo_box_set_popup_widget (combo_box, menu);
2859
2860   /* add items */
2861   if (add_children)
2862     gtk_combo_box_menu_fill (combo_box);
2863
2864   /* the column is needed in tree_column_row_is_sensitive() */
2865   priv->column = gtk_tree_view_column_new ();
2866   g_object_ref_sink (priv->column);
2867   gtk_combo_box_sync_cells (combo_box, GTK_CELL_LAYOUT (priv->column));
2868
2869   gtk_combo_box_update_title (combo_box);
2870   gtk_combo_box_update_sensitivity (combo_box);
2871 }
2872
2873 static void
2874 gtk_combo_box_menu_fill (GtkComboBox *combo_box)
2875 {
2876   GtkComboBoxPrivate *priv = combo_box->priv;
2877   GtkWidget *menu;
2878
2879   if (!priv->model)
2880     return;
2881
2882   menu = priv->popup_widget;
2883
2884   if (priv->add_tearoffs)
2885     {
2886       GtkWidget *tearoff = gtk_tearoff_menu_item_new ();
2887
2888       gtk_widget_show (tearoff);
2889       
2890       if (priv->wrap_width)
2891         gtk_menu_attach (GTK_MENU (menu), tearoff, 0, priv->wrap_width, 0, 1);
2892       else
2893         gtk_menu_shell_append (GTK_MENU_SHELL (menu), tearoff);
2894     }
2895   
2896   gtk_combo_box_menu_fill_level (combo_box, menu, NULL);
2897 }
2898
2899 static GtkWidget *
2900 gtk_cell_view_menu_item_new (GtkComboBox  *combo_box,
2901                              GtkTreeModel *model,
2902                              GtkTreeIter  *iter)
2903 {
2904   GtkWidget *cell_view; 
2905   GtkWidget *item;
2906   GtkTreePath *path;
2907   GtkRequisition req;
2908
2909   cell_view = gtk_cell_view_new ();
2910   item = gtk_menu_item_new ();
2911   gtk_container_add (GTK_CONTAINER (item), cell_view);
2912
2913   gtk_cell_view_set_model (GTK_CELL_VIEW (cell_view), model);     
2914   path = gtk_tree_model_get_path (model, iter);
2915   gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (cell_view), path);
2916   gtk_tree_path_free (path);
2917
2918   gtk_combo_box_sync_cells (combo_box, GTK_CELL_LAYOUT (cell_view));
2919   gtk_size_request_get_size (GTK_SIZE_REQUEST (cell_view),
2920                              &req, NULL);
2921   gtk_widget_show (cell_view);
2922   
2923   return item;
2924 }
2925  
2926 static void
2927 gtk_combo_box_menu_fill_level (GtkComboBox *combo_box,
2928                                GtkWidget   *menu,
2929                                GtkTreeIter *parent)
2930 {
2931   GtkComboBoxPrivate *priv = combo_box->priv;
2932   GtkTreeModel *model = priv->model;
2933   GtkWidget *item, *submenu, *subitem, *separator;
2934   GtkTreeIter iter;
2935   gboolean is_separator;
2936   gint i, n_children;
2937   GtkWidget *last;
2938   GtkTreePath *path;
2939   
2940   n_children = gtk_tree_model_iter_n_children (model, parent);
2941   
2942   last = NULL;
2943   for (i = 0; i < n_children; i++)
2944     {
2945       gtk_tree_model_iter_nth_child (model, &iter, parent, i);
2946
2947       if (priv->row_separator_func)
2948         is_separator = priv->row_separator_func (priv->model, &iter,
2949                                                  priv->row_separator_data);
2950       else
2951         is_separator = FALSE;
2952       
2953       if (is_separator)
2954         {
2955           item = gtk_separator_menu_item_new ();
2956           path = gtk_tree_model_get_path (model, &iter);
2957           g_object_set_data_full (G_OBJECT (item),
2958                                   I_("gtk-combo-box-item-path"),
2959                                   gtk_tree_row_reference_new (model, path),
2960                                   (GDestroyNotify)gtk_tree_row_reference_free);
2961           gtk_tree_path_free (path);
2962         }
2963       else
2964         {
2965           item = gtk_cell_view_menu_item_new (combo_box, model, &iter);
2966           if (gtk_tree_model_iter_has_child (model, &iter))
2967             {
2968               submenu = gtk_menu_new ();
2969               gtk_menu_set_reserve_toggle_size (GTK_MENU (submenu), FALSE);
2970               gtk_widget_show (submenu);
2971               gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu);
2972               
2973               /* Ugly - since menus can only activate leafs, we have to
2974                * duplicate the item inside the submenu.
2975                */
2976               subitem = gtk_cell_view_menu_item_new (combo_box, model, &iter);
2977               separator = gtk_separator_menu_item_new ();
2978               gtk_widget_show (subitem);
2979               gtk_widget_show (separator);
2980               g_signal_connect (subitem, "activate",
2981                                 G_CALLBACK (gtk_combo_box_menu_item_activate),
2982                                 combo_box);
2983               gtk_menu_shell_append (GTK_MENU_SHELL (submenu), subitem);
2984               gtk_menu_shell_append (GTK_MENU_SHELL (submenu), separator);
2985               
2986               gtk_combo_box_menu_fill_level (combo_box, submenu, &iter);
2987             }
2988           g_signal_connect (item, "activate",
2989                             G_CALLBACK (gtk_combo_box_menu_item_activate),
2990                             combo_box);
2991         }
2992       
2993       gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2994       if (priv->wrap_width && menu == priv->popup_widget)
2995         gtk_combo_box_relayout_item (combo_box, item, &iter, last);
2996       gtk_widget_show (item);
2997       
2998       last = item;
2999     }
3000 }
3001
3002 static void
3003 gtk_combo_box_menu_destroy (GtkComboBox *combo_box)
3004 {
3005   GtkComboBoxPrivate *priv = combo_box->priv;
3006
3007   g_signal_handlers_disconnect_matched (priv->button,
3008                                         G_SIGNAL_MATCH_DATA,
3009                                         0, 0, NULL,
3010                                         gtk_combo_box_menu_button_press, NULL);
3011   g_signal_handlers_disconnect_matched (priv->button,
3012                                         G_SIGNAL_MATCH_DATA,
3013                                         0, 0, NULL,
3014                                         gtk_combo_box_button_state_changed, combo_box);
3015
3016   /* unparent will remove our latest ref */
3017   gtk_widget_unparent (priv->button);
3018   
3019   priv->box = NULL;
3020   priv->button = NULL;
3021   priv->arrow = NULL;
3022   priv->separator = NULL;
3023
3024   g_object_unref (priv->column);
3025   priv->column = NULL;
3026
3027   /* changing the popup window will unref the menu and the children */
3028 }
3029
3030 /*
3031  * grid
3032  */
3033
3034 static gboolean
3035 menu_occupied (GtkMenu   *menu,
3036                guint      left_attach,
3037                guint      right_attach,
3038                guint      top_attach,
3039                guint      bottom_attach)
3040 {
3041   GList *i;
3042
3043   for (i = GTK_MENU_SHELL (menu)->children; i; i = i->next)
3044     {
3045       guint l, r, b, t;
3046
3047       gtk_container_child_get (GTK_CONTAINER (menu), 
3048                                i->data,
3049                                "left-attach", &l,
3050                                "right-attach", &r,
3051                                "bottom-attach", &b,
3052                                "top-attach", &t,
3053                                NULL);
3054
3055       /* look if this item intersects with the given coordinates */
3056       if (right_attach > l && left_attach < r && bottom_attach > t && top_attach < b)
3057         return TRUE;
3058     }
3059
3060   return FALSE;
3061 }
3062
3063 static void
3064 gtk_combo_box_relayout_item (GtkComboBox *combo_box,
3065                              GtkWidget   *item,
3066                              GtkTreeIter *iter,
3067                              GtkWidget   *last)
3068 {
3069   GtkComboBoxPrivate *priv = combo_box->priv;
3070   gint current_col = 0, current_row = 0;
3071   gint rows = 1, cols = 1;
3072   GtkWidget *menu = priv->popup_widget;
3073
3074   if (!GTK_IS_MENU_SHELL (menu))
3075     return;
3076   
3077   if (priv->col_column == -1 &&
3078       priv->row_column == -1 &&
3079       last)
3080     {
3081       gtk_container_child_get (GTK_CONTAINER (menu), 
3082                                last,
3083                                "right-attach", &current_col,
3084                                "top-attach", &current_row,
3085                                NULL);
3086       if (current_col + cols > priv->wrap_width)
3087         {
3088           current_col = 0;
3089           current_row++;
3090         }
3091     }
3092   else
3093     {
3094       if (priv->col_column != -1)
3095         gtk_tree_model_get (priv->model, iter,
3096                             priv->col_column, &cols,
3097                             -1);
3098       if (priv->row_column != -1)
3099         gtk_tree_model_get (priv->model, iter,
3100                             priv->row_column, &rows,
3101                             -1);
3102
3103       while (1)
3104         {
3105           if (current_col + cols > priv->wrap_width)
3106             {
3107               current_col = 0;
3108               current_row++;
3109             }
3110           
3111           if (!menu_occupied (GTK_MENU (menu), 
3112                               current_col, current_col + cols,
3113                               current_row, current_row + rows))
3114             break;
3115           
3116           current_col++;
3117         }
3118     }
3119
3120   /* set attach props */
3121   gtk_menu_attach (GTK_MENU (menu), item,
3122                    current_col, current_col + cols,
3123                    current_row, current_row + rows);
3124 }
3125
3126 static void
3127 gtk_combo_box_relayout (GtkComboBox *combo_box)
3128 {
3129   GList *list, *j;
3130   GtkWidget *menu;
3131
3132   menu = combo_box->priv->popup_widget;
3133   
3134   /* do nothing unless we are in menu style and realized */
3135   if (combo_box->priv->tree_view || !GTK_IS_MENU_SHELL (menu))
3136     return;
3137   
3138   list = gtk_container_get_children (GTK_CONTAINER (menu));
3139   
3140   for (j = g_list_last (list); j; j = j->prev)
3141     gtk_container_remove (GTK_CONTAINER (menu), j->data);
3142   
3143   gtk_combo_box_menu_fill (combo_box);
3144
3145   g_list_free (list);
3146 }
3147
3148 /* callbacks */
3149 static gboolean
3150 gtk_combo_box_menu_button_press (GtkWidget      *widget,
3151                                  GdkEventButton *event,
3152                                  gpointer        user_data)
3153 {
3154   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3155   GtkComboBoxPrivate *priv = combo_box->priv;
3156
3157   if (GTK_IS_MENU (priv->popup_widget) &&
3158       event->type == GDK_BUTTON_PRESS && event->button == 1)
3159     {
3160       if (priv->focus_on_click && 
3161           !gtk_widget_has_focus (priv->button))
3162         gtk_widget_grab_focus (priv->button);
3163
3164       gtk_combo_box_menu_popup (combo_box, event->button, event->time);
3165
3166       return TRUE;
3167     }
3168
3169   return FALSE;
3170 }
3171
3172 static void
3173 gtk_combo_box_menu_item_activate (GtkWidget *item,
3174                                   gpointer   user_data)
3175 {
3176   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3177   GtkWidget *cell_view;
3178   GtkTreePath *path;
3179   GtkTreeIter iter;
3180
3181   cell_view = gtk_bin_get_child (GTK_BIN (item));
3182
3183   g_return_if_fail (GTK_IS_CELL_VIEW (cell_view));
3184
3185   path = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (cell_view));
3186
3187   if (gtk_tree_model_get_iter (combo_box->priv->model, &iter, path))
3188   {
3189     if (gtk_menu_item_get_submenu (GTK_MENU_ITEM (item)) == NULL)
3190       gtk_combo_box_set_active_iter (combo_box, &iter);
3191   }
3192
3193   gtk_tree_path_free (path);
3194
3195   g_object_set (combo_box,
3196                 "editing-canceled", FALSE,
3197                 NULL);
3198 }
3199
3200 static void
3201 gtk_combo_box_update_sensitivity (GtkComboBox *combo_box)
3202 {
3203   GtkTreeIter iter;
3204   gboolean sensitive = TRUE; /* fool code checkers */
3205
3206   if (!combo_box->priv->button)
3207     return;
3208
3209   switch (combo_box->priv->button_sensitivity)
3210     {
3211       case GTK_SENSITIVITY_ON:
3212         sensitive = TRUE;
3213         break;
3214       case GTK_SENSITIVITY_OFF:
3215         sensitive = FALSE;
3216         break;
3217       case GTK_SENSITIVITY_AUTO:
3218         sensitive = combo_box->priv->model &&
3219                     gtk_tree_model_get_iter_first (combo_box->priv->model, &iter);
3220         break;
3221       default:
3222         g_assert_not_reached ();
3223         break;
3224     }
3225
3226   gtk_widget_set_sensitive (combo_box->priv->button, sensitive);
3227
3228   /* In list-mode, we also need to update sensitivity of the event box */
3229   if (GTK_IS_TREE_VIEW (combo_box->priv->tree_view)
3230       && combo_box->priv->cell_view)
3231     gtk_widget_set_sensitive (combo_box->priv->box, sensitive);
3232 }
3233
3234 static void
3235 gtk_combo_box_model_row_inserted (GtkTreeModel     *model,
3236                                   GtkTreePath      *path,
3237                                   GtkTreeIter      *iter,
3238                                   gpointer          user_data)
3239 {
3240   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3241
3242   if (combo_box->priv->tree_view)
3243     gtk_combo_box_list_popup_resize (combo_box);
3244   else
3245     gtk_combo_box_menu_row_inserted (model, path, iter, user_data);
3246
3247   gtk_combo_box_update_sensitivity (combo_box);
3248 }
3249
3250 static void
3251 gtk_combo_box_model_row_deleted (GtkTreeModel     *model,
3252                                  GtkTreePath      *path,
3253                                  gpointer          user_data)
3254 {
3255   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3256   GtkComboBoxPrivate *priv = combo_box->priv;
3257
3258   if (!gtk_tree_row_reference_valid (priv->active_row))
3259     {
3260       if (priv->cell_view)
3261         gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (priv->cell_view), NULL);
3262       g_signal_emit (combo_box, combo_box_signals[CHANGED], 0);
3263     }
3264   
3265   if (priv->tree_view)
3266     gtk_combo_box_list_popup_resize (combo_box);
3267   else
3268     gtk_combo_box_menu_row_deleted (model, path, user_data);  
3269
3270   gtk_combo_box_update_sensitivity (combo_box);
3271 }
3272
3273 static void
3274 gtk_combo_box_model_rows_reordered (GtkTreeModel    *model,
3275                                     GtkTreePath     *path,
3276                                     GtkTreeIter     *iter,
3277                                     gint            *new_order,
3278                                     gpointer         user_data)
3279 {
3280   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3281
3282   gtk_tree_row_reference_reordered (G_OBJECT (user_data), path, iter, new_order);
3283
3284   if (!combo_box->priv->tree_view)
3285     gtk_combo_box_menu_rows_reordered (model, path, iter, new_order, user_data);
3286 }
3287                                                     
3288 static void
3289 gtk_combo_box_model_row_changed (GtkTreeModel     *model,
3290                                  GtkTreePath      *path,
3291                                  GtkTreeIter      *iter,
3292                                  gpointer          user_data)
3293 {
3294   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3295   GtkComboBoxPrivate *priv = combo_box->priv;
3296   GtkTreePath *active_path;
3297
3298   /* FIXME this belongs to GtkCellView */
3299   if (gtk_tree_row_reference_valid (priv->active_row))
3300     {
3301       active_path = gtk_tree_row_reference_get_path (priv->active_row);
3302       if (gtk_tree_path_compare (path, active_path) == 0 &&
3303           priv->cell_view)
3304         gtk_widget_queue_resize (GTK_WIDGET (priv->cell_view));
3305       gtk_tree_path_free (active_path);
3306     }
3307       
3308   if (priv->tree_view)
3309     gtk_combo_box_list_row_changed (model, path, iter, user_data);
3310   else
3311     gtk_combo_box_menu_row_changed (model, path, iter, user_data);
3312 }
3313
3314 static gboolean
3315 list_popup_resize_idle (gpointer user_data)
3316 {
3317   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3318   GtkComboBoxPrivate *priv = combo_box->priv;
3319   gint x, y, width, height;
3320
3321   if (priv->tree_view && gtk_widget_get_mapped (priv->popup_window))
3322     {
3323       gtk_combo_box_list_position (combo_box, &x, &y, &width, &height);
3324   
3325       gtk_widget_set_size_request (priv->popup_window, width, height);
3326       gtk_window_move (GTK_WINDOW (priv->popup_window), x, y);
3327     }
3328
3329   priv->resize_idle_id = 0;
3330
3331   return FALSE;
3332 }
3333
3334 static void
3335 gtk_combo_box_list_popup_resize (GtkComboBox *combo_box)
3336 {
3337   GtkComboBoxPrivate *priv = combo_box->priv;
3338
3339   if (!priv->resize_idle_id)
3340     priv->resize_idle_id = 
3341       gdk_threads_add_idle (list_popup_resize_idle, combo_box);
3342 }
3343
3344 static void
3345 gtk_combo_box_model_row_expanded (GtkTreeModel     *model,
3346                                   GtkTreePath      *path,
3347                                   GtkTreeIter      *iter,
3348                                   gpointer          user_data)
3349 {
3350   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3351   
3352   gtk_combo_box_list_popup_resize (combo_box);
3353 }
3354
3355
3356 static GtkWidget *
3357 find_menu_by_path (GtkWidget   *menu,
3358                    GtkTreePath *path,
3359                    gboolean     skip_first)
3360 {
3361   GList *i, *list;
3362   GtkWidget *child;
3363   GtkWidget *item;
3364   GtkWidget *submenu;    
3365   GtkTreeRowReference *mref;
3366   GtkTreePath *mpath;
3367   gboolean skip;
3368
3369   list = gtk_container_get_children (GTK_CONTAINER (menu));
3370   skip = skip_first;
3371   item = NULL;
3372   for (i = list; i; i = i->next)
3373     {
3374       child = gtk_bin_get_child (i->data);
3375       if (GTK_IS_SEPARATOR_MENU_ITEM (i->data))
3376         {
3377           mref = g_object_get_data (G_OBJECT (i->data), "gtk-combo-box-item-path");
3378           if (!mref)
3379             continue;
3380           else if (!gtk_tree_row_reference_valid (mref))
3381             mpath = NULL;
3382           else
3383             mpath = gtk_tree_row_reference_get_path (mref);
3384         }
3385       else if (GTK_IS_CELL_VIEW (child))
3386         {
3387           if (skip)
3388             {
3389               skip = FALSE;
3390               continue;
3391             }
3392
3393           mpath = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (child));
3394         }
3395       else 
3396         continue;
3397
3398       /* this case is necessary, since the row reference of
3399        * the cell view may already be updated after a deletion
3400        */
3401       if (!mpath)
3402         {
3403           item = i->data;
3404           break;
3405         }
3406       if (gtk_tree_path_compare (mpath, path) == 0)
3407         {
3408           gtk_tree_path_free (mpath);
3409           item = i->data;
3410           break;
3411         }
3412       if (gtk_tree_path_is_ancestor (mpath, path))
3413         {
3414           submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
3415           if (submenu != NULL)
3416             {
3417               gtk_tree_path_free (mpath);
3418               item = find_menu_by_path (submenu, path, TRUE);
3419               break;
3420             }
3421         }
3422       gtk_tree_path_free (mpath);
3423     }
3424   
3425   g_list_free (list);  
3426
3427   return item;
3428 }
3429
3430 #if 0
3431 static void
3432 dump_menu_tree (GtkWidget   *menu, 
3433                 gint         level)
3434 {
3435   GList *i, *list;
3436   GtkWidget *submenu;    
3437   GtkTreePath *path;
3438
3439   list = gtk_container_get_children (GTK_CONTAINER (menu));
3440   for (i = list; i; i = i->next)
3441     {
3442       if (GTK_IS_CELL_VIEW (GTK_BIN (i->data)->child))
3443         {
3444           path = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (GTK_BIN (i->data)->child));
3445           g_print ("%*s%s\n", 2 * level, " ", gtk_tree_path_to_string (path));
3446           gtk_tree_path_free (path);
3447
3448           submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
3449           if (submenu != NULL)
3450             dump_menu_tree (submenu, level + 1);
3451         }
3452     }
3453   
3454   g_list_free (list);  
3455 }
3456 #endif
3457
3458 static void
3459 gtk_combo_box_menu_row_inserted (GtkTreeModel *model,
3460                                  GtkTreePath  *path,
3461                                  GtkTreeIter  *iter,
3462                                  gpointer      user_data)
3463 {
3464   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3465   GtkComboBoxPrivate *priv = combo_box->priv;
3466   GtkWidget *parent;
3467   GtkWidget *item, *menu, *separator;
3468   GtkTreePath *ppath;
3469   GtkTreeIter piter;
3470   gint depth, pos;
3471   gboolean is_separator;
3472
3473   if (!priv->popup_widget)
3474     return;
3475
3476   depth = gtk_tree_path_get_depth (path);
3477   pos = gtk_tree_path_get_indices (path)[depth - 1];
3478   if (depth > 1)
3479     {
3480       ppath = gtk_tree_path_copy (path);
3481       gtk_tree_path_up (ppath);
3482       parent = find_menu_by_path (priv->popup_widget, ppath, FALSE);
3483       gtk_tree_path_free (ppath);
3484
3485       menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (parent));
3486       if (!menu)
3487         {
3488           menu = gtk_menu_new ();
3489           gtk_menu_set_reserve_toggle_size (GTK_MENU (menu), FALSE);
3490           gtk_widget_show (menu);
3491           gtk_menu_item_set_submenu (GTK_MENU_ITEM (parent), menu);
3492           
3493           /* Ugly - since menus can only activate leaves, we have to
3494            * duplicate the item inside the submenu.
3495            */
3496           gtk_tree_model_iter_parent (model, &piter, iter);
3497           item = gtk_cell_view_menu_item_new (combo_box, model, &piter);
3498           separator = gtk_separator_menu_item_new ();
3499           g_signal_connect (item, "activate",
3500                             G_CALLBACK (gtk_combo_box_menu_item_activate),
3501                             combo_box);
3502           gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3503           gtk_menu_shell_append (GTK_MENU_SHELL (menu), separator);
3504           if (cell_view_is_sensitive (GTK_CELL_VIEW (gtk_bin_get_child (GTK_BIN (item)))))
3505             {
3506               gtk_widget_show (item);
3507               gtk_widget_show (separator);
3508             }
3509         }
3510       pos += 2;
3511     }
3512   else
3513     {
3514       menu = priv->popup_widget;
3515       if (priv->add_tearoffs)
3516         pos += 1;
3517     }
3518   
3519   if (priv->row_separator_func)
3520     is_separator = priv->row_separator_func (model, iter,
3521                                              priv->row_separator_data);
3522   else
3523     is_separator = FALSE;
3524
3525   if (is_separator)
3526     {
3527       item = gtk_separator_menu_item_new ();
3528       g_object_set_data_full (G_OBJECT (item),
3529                               I_("gtk-combo-box-item-path"),
3530                               gtk_tree_row_reference_new (model, path),
3531                               (GDestroyNotify)gtk_tree_row_reference_free);
3532     }
3533   else
3534     {
3535       item = gtk_cell_view_menu_item_new (combo_box, model, iter);
3536       
3537       g_signal_connect (item, "activate",
3538                         G_CALLBACK (gtk_combo_box_menu_item_activate),
3539                         combo_box);
3540     }
3541
3542   gtk_widget_show (item);
3543   gtk_menu_shell_insert (GTK_MENU_SHELL (menu), item, pos);
3544 }
3545
3546 static void
3547 gtk_combo_box_menu_row_deleted (GtkTreeModel *model,
3548                                 GtkTreePath  *path,
3549                                 gpointer      user_data)
3550 {
3551   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3552   GtkComboBoxPrivate *priv = combo_box->priv;
3553   GtkWidget *menu;
3554   GtkWidget *item;
3555
3556   if (!priv->popup_widget)
3557     return;
3558
3559   item = find_menu_by_path (priv->popup_widget, path, FALSE);
3560   menu = gtk_widget_get_parent (item);
3561   gtk_container_remove (GTK_CONTAINER (menu), item);
3562
3563   if (gtk_tree_path_get_depth (path) > 1)
3564     {
3565       GtkTreePath *parent_path;
3566       GtkTreeIter iter;
3567       GtkWidget *parent;
3568
3569       parent_path = gtk_tree_path_copy (path);
3570       gtk_tree_path_up (parent_path);
3571       gtk_tree_model_get_iter (model, &iter, parent_path);
3572
3573       if (!gtk_tree_model_iter_has_child (model, &iter))
3574         {
3575           parent = find_menu_by_path (priv->popup_widget, 
3576                                       parent_path, FALSE);
3577           gtk_menu_item_set_submenu (GTK_MENU_ITEM (parent), NULL);
3578         }
3579     }
3580 }
3581
3582 static void
3583 gtk_combo_box_menu_rows_reordered  (GtkTreeModel     *model,
3584                                     GtkTreePath      *path,
3585                                     GtkTreeIter      *iter,
3586                                     gint             *new_order,
3587                                     gpointer          user_data)
3588 {
3589   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3590
3591   gtk_combo_box_relayout (combo_box);
3592 }
3593                                     
3594 static void
3595 gtk_combo_box_menu_row_changed (GtkTreeModel *model,
3596                                 GtkTreePath  *path,
3597                                 GtkTreeIter  *iter,
3598                                 gpointer      user_data)
3599 {
3600   GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3601   GtkComboBoxPrivate *priv = combo_box->priv;
3602   GtkWidget *item;
3603   gboolean is_separator;
3604
3605   if (!priv->popup_widget)
3606     return;
3607
3608   item = find_menu_by_path (priv->popup_widget, path, FALSE);
3609
3610   if (priv->row_separator_func)
3611     is_separator = priv->row_separator_func (model, iter,
3612                                              priv->row_separator_data);
3613   else
3614     is_separator = FALSE;
3615
3616   if (is_separator != GTK_IS_SEPARATOR_MENU_ITEM (item))
3617     {
3618       gtk_combo_box_menu_row_deleted (model, path, combo_box);
3619       gtk_combo_box_menu_row_inserted (model, path, iter, combo_box);
3620     }
3621
3622   if (priv->wrap_width &&
3623       gtk_widget_get_parent (item) == priv->popup_widget)
3624     {
3625       GtkWidget *pitem = NULL;
3626       GtkTreePath *prev;
3627
3628       prev = gtk_tree_path_copy (path);
3629
3630       if (gtk_tree_path_prev (prev))
3631         pitem = find_menu_by_path (priv->popup_widget, prev, FALSE);
3632
3633       gtk_tree_path_free (prev);
3634
3635       /* unattach item so gtk_combo_box_relayout_item() won't spuriously
3636          move it */
3637       gtk_container_child_set (GTK_CONTAINER (priv->popup_widget),
3638                                item, 
3639                                "left-attach", -1, 
3640                                "right-attach", -1,
3641                                "top-attach", -1, 
3642                                "bottom-attach", -1, 
3643                                NULL);
3644
3645       gtk_combo_box_relayout_item (combo_box, item, iter, pitem);
3646     }
3647
3648   gtk_combo_box_update_requested_width (combo_box, path);
3649 }
3650
3651 /*
3652  * list style
3653  */
3654
3655 static void
3656 gtk_combo_box_list_setup (GtkComboBox *combo_box)
3657 {
3658   GtkComboBoxPrivate *priv = combo_box->priv;
3659   GtkTreeSelection *sel;
3660   GtkStyle *style;
3661   GtkWidget *child;
3662   GtkWidget *widget = GTK_WIDGET (combo_box);
3663
3664   priv->button = gtk_toggle_button_new ();
3665   child = gtk_bin_get_child (GTK_BIN (combo_box));
3666   gtk_widget_set_parent (priv->button,
3667                          gtk_widget_get_parent (child));
3668   g_signal_connect (priv->button, "button-press-event",
3669                     G_CALLBACK (gtk_combo_box_list_button_pressed), combo_box);
3670   g_signal_connect (priv->button, "toggled",
3671                     G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
3672
3673   priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3674   gtk_container_add (GTK_CONTAINER (priv->button), priv->arrow);
3675   priv->separator = NULL;
3676   gtk_widget_show_all (priv->button);
3677
3678   if (priv->cell_view)
3679     {
3680       style = gtk_widget_get_style (widget);
3681       gtk_cell_view_set_background_color (GTK_CELL_VIEW (priv->cell_view),
3682                                           &style->base[gtk_widget_get_state (widget)]);
3683
3684       priv->box = gtk_event_box_new ();
3685       gtk_event_box_set_visible_window (GTK_EVENT_BOX (priv->box), 
3686                                         FALSE);
3687
3688       if (priv->has_frame)
3689         {
3690           priv->cell_view_frame = gtk_frame_new (NULL);
3691           gtk_frame_set_shadow_type (GTK_FRAME (priv->cell_view_frame),
3692                                      GTK_SHADOW_IN);
3693         }
3694       else 
3695         {
3696           combo_box->priv->cell_view_frame = gtk_event_box_new ();
3697           gtk_event_box_set_visible_window (GTK_EVENT_BOX (combo_box->priv->cell_view_frame), 
3698                                             FALSE);
3699         }
3700
3701       gtk_widget_set_parent (priv->cell_view_frame,
3702                              gtk_widget_get_parent (child));
3703       gtk_container_add (GTK_CONTAINER (priv->cell_view_frame), priv->box);
3704       gtk_widget_show_all (priv->cell_view_frame);
3705
3706       g_signal_connect (priv->box, "button-press-event",
3707                         G_CALLBACK (gtk_combo_box_list_button_pressed), 
3708                         combo_box);
3709     }
3710
3711   priv->tree_view = gtk_tree_view_new ();
3712   sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));
3713   gtk_tree_selection_set_mode (sel, GTK_SELECTION_BROWSE);
3714   gtk_tree_selection_set_select_function (sel,
3715                                           gtk_combo_box_list_select_func,
3716                                           NULL, NULL);
3717   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->tree_view),
3718                                      FALSE);
3719   gtk_tree_view_set_hover_selection (GTK_TREE_VIEW (priv->tree_view),
3720                                      TRUE);
3721   if (priv->row_separator_func)
3722     gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (priv->tree_view), 
3723                                           priv->row_separator_func, 
3724                                           priv->row_separator_data, 
3725                                           NULL);
3726   if (priv->model)
3727     gtk_tree_view_set_model (GTK_TREE_VIEW (priv->tree_view), priv->model);
3728     
3729   priv->column = gtk_tree_view_column_new ();
3730   gtk_tree_view_append_column (GTK_TREE_VIEW (priv->tree_view), priv->column);
3731
3732   /* sync up */
3733   gtk_combo_box_sync_cells (combo_box, 
3734                             GTK_CELL_LAYOUT (priv->column));
3735
3736   if (gtk_tree_row_reference_valid (priv->active_row))
3737     {
3738       GtkTreePath *path;
3739
3740       path = gtk_tree_row_reference_get_path (priv->active_row);
3741       gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->tree_view),
3742                                 path, NULL, FALSE);
3743       gtk_tree_path_free (path);
3744     }
3745
3746   /* set sample/popup widgets */
3747   gtk_combo_box_set_popup_widget (combo_box, priv->tree_view);
3748
3749   g_signal_connect (priv->tree_view, "key-press-event",
3750                     G_CALLBACK (gtk_combo_box_list_key_press),
3751                     combo_box);
3752   g_signal_connect (priv->tree_view, "enter-notify-event",
3753                     G_CALLBACK (gtk_combo_box_list_enter_notify),
3754                     combo_box);
3755   g_signal_connect (priv->tree_view, "row-expanded",
3756                     G_CALLBACK (gtk_combo_box_model_row_expanded),
3757                     combo_box);
3758   g_signal_connect (priv->tree_view, "row-collapsed",
3759                     G_CALLBACK (gtk_combo_box_model_row_expanded),
3760                     combo_box);
3761   g_signal_connect (priv->popup_window, "button-press-event",
3762                     G_CALLBACK (gtk_combo_box_list_button_pressed),
3763                     combo_box);
3764   g_signal_connect (priv->popup_window, "button-release-event",
3765                     G_CALLBACK (gtk_combo_box_list_button_released),
3766                     combo_box);
3767
3768   gtk_widget_show (priv->tree_view);
3769
3770   gtk_combo_box_update_sensitivity (combo_box);
3771 }
3772
3773 static void
3774 gtk_combo_box_list_destroy (GtkComboBox *combo_box)
3775 {
3776   GtkComboBoxPrivate *priv = combo_box->priv;
3777
3778   /* disconnect signals */
3779   g_signal_handlers_disconnect_matched (priv->tree_view,
3780                                         G_SIGNAL_MATCH_DATA,
3781                                         0, 0, NULL, NULL, combo_box);
3782   g_signal_handlers_disconnect_matched (priv->button,
3783                                         G_SIGNAL_MATCH_DATA,
3784                                         0, 0, NULL,
3785                                         gtk_combo_box_list_button_pressed,
3786                                         NULL);
3787   g_signal_handlers_disconnect_matched (priv->popup_window,
3788                                         G_SIGNAL_MATCH_DATA,
3789                                         0, 0, NULL,
3790                                         gtk_combo_box_list_button_pressed,
3791                                         NULL);
3792   g_signal_handlers_disconnect_matched (priv->popup_window,
3793                                         G_SIGNAL_MATCH_DATA,
3794                                         0, 0, NULL,
3795                                         gtk_combo_box_list_button_released,
3796                                         NULL);
3797
3798   g_signal_handlers_disconnect_matched (priv->popup_window,
3799                                         G_SIGNAL_MATCH_DATA,
3800                                         0, 0, NULL, 
3801                                         gtk_combo_box_child_show,
3802                                         NULL);
3803
3804   g_signal_handlers_disconnect_matched (priv->popup_window,
3805                                         G_SIGNAL_MATCH_DATA,
3806                                         0, 0, NULL, 
3807                                         gtk_combo_box_child_hide,
3808                                         NULL);
3809   
3810   if (priv->box)
3811     g_signal_handlers_disconnect_matched (priv->box,
3812                                           G_SIGNAL_MATCH_DATA,
3813                                           0, 0, NULL,
3814                                           gtk_combo_box_list_button_pressed,
3815                                           NULL);
3816
3817   /* destroy things (unparent will kill the latest ref from us)
3818    * last unref on button will destroy the arrow
3819    */
3820   gtk_widget_unparent (priv->button);
3821   priv->button = NULL;
3822   priv->arrow = NULL;
3823
3824   if (priv->cell_view)
3825     {
3826       g_object_set (priv->cell_view,
3827                     "background-set", FALSE,
3828                     NULL);
3829     }
3830
3831   if (priv->cell_view_frame)
3832     {
3833       gtk_widget_unparent (priv->cell_view_frame);
3834       priv->cell_view_frame = NULL;
3835       priv->box = NULL;
3836     }
3837
3838   if (priv->scroll_timer)
3839     {
3840       g_source_remove (priv->scroll_timer);
3841       priv->scroll_timer = 0;
3842     }
3843
3844   if (priv->resize_idle_id)
3845     {
3846       g_source_remove (priv->resize_idle_id);
3847       priv->resize_idle_id = 0;
3848     }
3849
3850   gtk_widget_destroy (priv->tree_view);
3851
3852   priv->tree_view = NULL;
3853   if (priv->popup_widget)
3854     {
3855       g_object_unref (priv->popup_widget);
3856       priv->popup_widget = NULL;
3857     }
3858 }
3859
3860 /* callbacks */
3861
3862 static gboolean
3863 gtk_combo_box_list_button_pressed (GtkWidget      *widget,
3864                                    GdkEventButton *event,
3865                                    gpointer        data)
3866 {
3867   GtkComboBox *combo_box = GTK_COMBO_BOX (data);
3868   GtkComboBoxPrivate *priv = combo_box->priv;
3869
3870   GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *)event);
3871
3872   if (ewidget == priv->popup_window)
3873     return TRUE;
3874
3875   if ((ewidget != priv->button && ewidget != priv->box) ||
3876       gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->button)))
3877     return FALSE;
3878
3879   if (priv->focus_on_click && 
3880       !gtk_widget_has_focus (priv->button))
3881     gtk_widget_grab_focus (priv->button);
3882
3883   gtk_combo_box_popup_for_device (combo_box, event->device);
3884
3885   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button), TRUE);
3886
3887   priv->auto_scroll = FALSE;
3888   if (priv->scroll_timer == 0)
3889     priv->scroll_timer = gdk_threads_add_timeout (SCROLL_TIME, 
3890                                                   (GSourceFunc) gtk_combo_box_list_scroll_timeout, 
3891                                                    combo_box);
3892
3893   priv->popup_in_progress = TRUE;
3894
3895   return TRUE;
3896 }
3897
3898 static gboolean
3899 gtk_combo_box_list_button_released (GtkWidget      *widget,
3900                                     GdkEventButton *event,
3901                                     gpointer        data)
3902 {
3903   gboolean ret;
3904   GtkTreePath *path = NULL;
3905   GtkTreeIter iter;
3906
3907   GtkComboBox *combo_box = GTK_COMBO_BOX (data);
3908   GtkComboBoxPrivate *priv = combo_box->priv;
3909
3910   gboolean popup_in_progress = FALSE;
3911
3912   GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *)event);
3913
3914   if (priv->popup_in_progress)
3915     {
3916       popup_in_progress = TRUE;
3917       priv->popup_in_progress = FALSE;
3918     }
3919
3920   gtk_tree_view_set_hover_expand (GTK_TREE_VIEW (priv->tree_view), 
3921                                   FALSE);
3922   if (priv->scroll_timer)
3923     {
3924       g_source_remove (priv->scroll_timer);
3925       priv->scroll_timer = 0;
3926     }
3927
3928   if (ewidget != priv->tree_view)
3929     {
3930       if ((ewidget == priv->button || 
3931            ewidget == priv->box) &&
3932           !popup_in_progress &&
3933           gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->button)))
3934         {
3935           gtk_combo_box_popdown (combo_box);
3936           return TRUE;
3937         }
3938
3939       /* released outside treeview */
3940       if (ewidget != priv->button && ewidget != priv->box)
3941         {
3942           gtk_combo_box_popdown (combo_box);
3943
3944           return TRUE;
3945         }
3946
3947       return FALSE;
3948     }
3949
3950   /* select something cool */
3951   ret = gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (priv->tree_view),
3952                                        event->x, event->y,
3953                                        &path,
3954                                        NULL, NULL, NULL);
3955
3956   if (!ret)
3957     return TRUE; /* clicked outside window? */
3958
3959   gtk_tree_model_get_iter (priv->model, &iter, path);
3960   gtk_tree_path_free (path);
3961
3962   gtk_combo_box_popdown (combo_box);
3963
3964   if (tree_column_row_is_sensitive (combo_box, &iter))
3965     gtk_combo_box_set_active_iter (combo_box, &iter);
3966
3967   return TRUE;
3968 }
3969
3970 static gboolean
3971 gtk_combo_box_menu_key_press (GtkWidget   *widget,
3972                               GdkEventKey *event,
3973                               gpointer     data)
3974 {
3975   GtkComboBox *combo_box = GTK_COMBO_BOX (data);
3976
3977   if (!gtk_bindings_activate_event (GTK_OBJECT (widget), event))
3978     {
3979       /* The menu hasn't managed the
3980        * event, forward it to the combobox
3981        */
3982       gtk_bindings_activate_event (GTK_OBJECT (combo_box), event);
3983     }
3984
3985   return TRUE;
3986 }
3987
3988 static gboolean
3989 gtk_combo_box_list_key_press (GtkWidget   *widget,
3990                               GdkEventKey *event,
3991                               gpointer     data)
3992 {
3993   GtkComboBox *combo_box = GTK_COMBO_BOX (data);
3994   GtkTreeIter iter;
3995
3996   if (event->keyval == GDK_KEY_Return || event->keyval == GDK_KEY_ISO_Enter || event->keyval == GDK_KEY_KP_Enter ||
3997       event->keyval == GDK_KEY_space || event->keyval == GDK_KEY_KP_Space) 
3998   {
3999     GtkTreeModel *model = NULL;
4000     
4001     gtk_combo_box_popdown (combo_box);
4002     
4003     if (combo_box->priv->model)
4004       {
4005         GtkTreeSelection *sel;
4006
4007         sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (combo_box->priv->tree_view));
4008
4009         if (gtk_tree_selection_get_selected (sel, &model, &iter))
4010           gtk_combo_box_set_active_iter (combo_box, &iter);
4011       }
4012     
4013     return TRUE;
4014   }
4015
4016   if (!gtk_bindings_activate_event (GTK_OBJECT (widget), event))
4017     {
4018       /* The list hasn't managed the
4019        * event, forward it to the combobox
4020        */
4021       gtk_bindings_activate_event (GTK_OBJECT (combo_box), event);
4022     }
4023
4024   return TRUE;
4025 }
4026
4027 static void
4028 gtk_combo_box_list_auto_scroll (GtkComboBox *combo_box,
4029                                 gint         x, 
4030                                 gint         y)
4031 {
4032   GtkAdjustment *adj;
4033   GtkAllocation allocation;
4034   GtkWidget *tree_view = combo_box->priv->tree_view;
4035   gdouble value;
4036
4037   gtk_widget_get_allocation (tree_view, &allocation);
4038
4039   adj = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window));
4040   if (adj && adj->upper - adj->lower > adj->page_size)
4041     {
4042       if (x <= allocation.x &&
4043           adj->lower < adj->value)
4044         {
4045           value = adj->value - (allocation.x - x + 1);
4046           gtk_adjustment_set_value (adj, value);
4047         }
4048       else if (x >= allocation.x + allocation.width &&
4049                adj->upper - adj->page_size > adj->value)
4050         {
4051           value = adj->value + (x - allocation.x - allocation.width + 1);
4052           gtk_adjustment_set_value (adj, MAX (value, 0.0));
4053         }
4054     }
4055
4056   adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window));
4057   if (adj && adj->upper - adj->lower > adj->page_size)
4058     {
4059       if (y <= allocation.y &&
4060           adj->lower < adj->value)
4061         {
4062           value = adj->value - (allocation.y - y + 1);
4063           gtk_adjustment_set_value (adj, value);
4064         }
4065       else if (y >= allocation.height &&
4066                adj->upper - adj->page_size > adj->value)
4067         {
4068           value = adj->value + (y - allocation.height + 1);
4069           gtk_adjustment_set_value (adj, MAX (value, 0.0));
4070         }
4071     }
4072 }
4073
4074 static gboolean
4075 gtk_combo_box_list_scroll_timeout (GtkComboBox *combo_box)
4076 {
4077   GtkComboBoxPrivate *priv = combo_box->priv;
4078   gint x, y;
4079
4080   if (priv->auto_scroll)
4081     {
4082       gdk_window_get_device_position (gtk_widget_get_window (priv->tree_view),
4083                                       priv->grab_pointer,
4084                                       &x, &y, NULL);
4085       gtk_combo_box_list_auto_scroll (combo_box, x, y);
4086     }
4087
4088   return TRUE;
4089 }
4090
4091 static gboolean 
4092 gtk_combo_box_list_enter_notify (GtkWidget        *widget,
4093                                  GdkEventCrossing *event,
4094                                  gpointer          data)
4095 {
4096   GtkComboBox *combo_box = GTK_COMBO_BOX (data);
4097
4098   combo_box->priv->auto_scroll = TRUE;
4099
4100   return TRUE;
4101 }
4102
4103 static gboolean
4104 gtk_combo_box_list_select_func (GtkTreeSelection *selection,
4105                                 GtkTreeModel     *model,
4106                                 GtkTreePath      *path,
4107                                 gboolean          path_currently_selected,
4108                                 gpointer          data)
4109 {
4110   GList *list;
4111   gboolean sensitive = FALSE;
4112
4113   for (list = selection->tree_view->priv->columns; list && !sensitive; list = list->next)
4114     {
4115       GList *cells, *cell;
4116       gboolean cell_sensitive, cell_visible;
4117       GtkTreeIter iter;
4118       GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN (list->data);
4119
4120       if (!column->visible)
4121         continue;
4122
4123       gtk_tree_model_get_iter (model, &iter, path);
4124       gtk_tree_view_column_cell_set_cell_data (column, model, &iter,
4125                                                FALSE, FALSE);
4126
4127       cell = cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
4128       while (cell)
4129         {
4130           g_object_get (cell->data,
4131                         "sensitive", &cell_sensitive,
4132                         "visible", &cell_visible,
4133                         NULL);
4134
4135           if (cell_visible && cell_sensitive)
4136             break;
4137
4138           cell = cell->next;
4139         }
4140       g_list_free (cells);
4141
4142       sensitive = cell_sensitive;
4143     }
4144
4145   return sensitive;
4146 }
4147
4148 static void
4149 gtk_combo_box_list_row_changed (GtkTreeModel *model,
4150                                 GtkTreePath  *path,
4151                                 GtkTreeIter  *iter,
4152                                 gpointer      data)
4153 {
4154   GtkComboBox *combo_box = GTK_COMBO_BOX (data);
4155
4156   gtk_combo_box_update_requested_width (combo_box, path);
4157 }
4158
4159 /*
4160  * GtkCellLayout implementation
4161  */
4162
4163 static void
4164 pack_start_recurse (GtkWidget       *menu,
4165                     GtkCellRenderer *cell,
4166                     gboolean         expand)
4167 {
4168   GList *i, *list;
4169   GtkWidget *child;
4170   GtkWidget *submenu;    
4171   
4172   list = gtk_container_get_children (GTK_CONTAINER (menu));
4173   for (i = list; i; i = i->next)
4174     {
4175       child = gtk_bin_get_child (GTK_BIN (i->data));
4176       if (GTK_IS_CELL_LAYOUT (child))
4177         gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (child),
4178                                     cell, expand);
4179
4180       submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4181       if (submenu != NULL)
4182         pack_start_recurse (submenu, cell, expand);
4183     }
4184
4185   g_list_free (list);
4186 }
4187
4188 static void
4189 gtk_combo_box_cell_layout_pack_start (GtkCellLayout   *layout,
4190                                       GtkCellRenderer *cell,
4191                                       gboolean         expand)
4192 {
4193   GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4194   ComboCellInfo *info;
4195   GtkComboBoxPrivate *priv;
4196
4197   priv = combo_box->priv;
4198
4199   g_object_ref_sink (cell);
4200
4201   info = g_slice_new0 (ComboCellInfo);
4202   info->cell = cell;
4203   info->expand = expand;
4204   info->pack = GTK_PACK_START;
4205
4206   priv->cells = g_slist_append (priv->cells, info);
4207
4208   if (priv->cell_view)
4209     {
4210       gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->cell_view),
4211                                   cell, expand);
4212
4213     }
4214
4215   if (priv->column)
4216     gtk_tree_view_column_pack_start (priv->column, cell, expand);
4217
4218   if (GTK_IS_MENU (priv->popup_widget))
4219     pack_start_recurse (priv->popup_widget, cell, expand);
4220 }
4221
4222 static void
4223 pack_end_recurse (GtkWidget       *menu,
4224                   GtkCellRenderer *cell,
4225                   gboolean         expand)
4226 {
4227   GList *i, *list;
4228   GtkWidget *child;
4229   GtkWidget *submenu;    
4230   
4231   list = gtk_container_get_children (GTK_CONTAINER (menu));
4232   for (i = list; i; i = i->next)
4233     {
4234       child = gtk_bin_get_child (GTK_BIN (i->data));
4235       if (GTK_IS_CELL_LAYOUT (child))
4236         gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (child),
4237                                   cell, expand);
4238
4239       submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4240       if (submenu != NULL)
4241         pack_end_recurse (submenu, cell, expand);
4242     }
4243
4244   g_list_free (list);
4245 }
4246
4247 static void
4248 gtk_combo_box_cell_layout_pack_end (GtkCellLayout   *layout,
4249                                     GtkCellRenderer *cell,
4250                                     gboolean         expand)
4251 {
4252   GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4253   ComboCellInfo *info;
4254   GtkComboBoxPrivate *priv;
4255
4256   priv = combo_box->priv;
4257
4258   g_object_ref_sink (cell);
4259
4260   info = g_slice_new0 (ComboCellInfo);
4261   info->cell = cell;
4262   info->expand = expand;
4263   info->pack = GTK_PACK_END;
4264
4265   priv->cells = g_slist_append (priv->cells, info);
4266
4267   if (priv->cell_view)
4268     gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (priv->cell_view),
4269                               cell, expand);
4270
4271   if (priv->column)
4272     gtk_tree_view_column_pack_end (priv->column, cell, expand);
4273
4274   if (GTK_IS_MENU (priv->popup_widget))
4275     pack_end_recurse (priv->popup_widget, cell, expand);
4276 }
4277
4278 static GList *
4279 gtk_combo_box_cell_layout_get_cells (GtkCellLayout *layout)
4280 {
4281   GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4282   GSList *list;
4283   GList *retval = NULL;
4284
4285   for (list = combo_box->priv->cells; list; list = list->next)
4286     {
4287       ComboCellInfo *info = (ComboCellInfo *)list->data;
4288
4289       retval = g_list_prepend (retval, info->cell);
4290     }
4291
4292   return g_list_reverse (retval);
4293 }
4294
4295 static void
4296 clear_recurse (GtkWidget *menu)
4297 {
4298   GList *i, *list;
4299   GtkWidget *child;
4300   GtkWidget *submenu;    
4301   
4302   list = gtk_container_get_children (GTK_CONTAINER (menu));
4303   for (i = list; i; i = i->next)
4304     {
4305       child = gtk_bin_get_child (GTK_BIN (i->data));
4306       if (GTK_IS_CELL_LAYOUT (child))
4307         gtk_cell_layout_clear (GTK_CELL_LAYOUT (child));
4308
4309       submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4310       if (submenu != NULL)
4311         clear_recurse (submenu);
4312     }
4313
4314   g_list_free (list);
4315 }
4316
4317 static void
4318 gtk_combo_box_cell_layout_clear (GtkCellLayout *layout)
4319 {
4320   GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4321   GtkComboBoxPrivate *priv = combo_box->priv;
4322   GSList *i;
4323   
4324   if (priv->cell_view)
4325     gtk_cell_layout_clear (GTK_CELL_LAYOUT (priv->cell_view));
4326
4327   if (priv->column)
4328     gtk_tree_view_column_clear (priv->column);
4329
4330   for (i = priv->cells; i; i = i->next)
4331     {
4332       ComboCellInfo *info = (ComboCellInfo *)i->data;
4333
4334       gtk_combo_box_cell_layout_clear_attributes (layout, info->cell);
4335       g_object_unref (info->cell);
4336       g_slice_free (ComboCellInfo, info);
4337       i->data = NULL;
4338     }
4339   g_slist_free (priv->cells);
4340   priv->cells = NULL;
4341
4342   if (GTK_IS_MENU (priv->popup_widget))
4343     clear_recurse (priv->popup_widget);
4344 }
4345
4346 static void
4347 add_attribute_recurse (GtkWidget       *menu,
4348                        GtkCellRenderer *cell,
4349                        const gchar     *attribute,
4350                        gint             column)
4351 {
4352   GList *i, *list;
4353   GtkWidget *child;
4354   GtkWidget *submenu;    
4355
4356   list = gtk_container_get_children (GTK_CONTAINER (menu));
4357   for (i = list; i; i = i->next)
4358     {
4359       child = gtk_bin_get_child (GTK_BIN (i->data));
4360       if (GTK_IS_CELL_LAYOUT (child))
4361         gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (child),
4362                                        cell, attribute, column);
4363
4364       submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4365       if (submenu != NULL)
4366         add_attribute_recurse (submenu, cell, attribute, column);
4367     }
4368
4369   g_list_free (list);
4370 }
4371                        
4372 static void
4373 gtk_combo_box_cell_layout_add_attribute (GtkCellLayout   *layout,
4374                                          GtkCellRenderer *cell,
4375                                          const gchar     *attribute,
4376                                          gint             column)
4377 {
4378   GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4379   ComboCellInfo *info;
4380
4381   info = gtk_combo_box_get_cell_info (combo_box, cell);
4382   g_return_if_fail (info != NULL);
4383
4384   info->attributes = g_slist_prepend (info->attributes,
4385                                       GINT_TO_POINTER (column));
4386   info->attributes = g_slist_prepend (info->attributes,
4387                                       g_strdup (attribute));
4388
4389   if (combo_box->priv->cell_view)
4390     gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box->priv->cell_view),
4391                                    cell, attribute, column);
4392
4393   if (combo_box->priv->column)
4394     gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box->priv->column),
4395                                    cell, attribute, column);
4396
4397   if (GTK_IS_MENU (combo_box->priv->popup_widget))
4398     add_attribute_recurse (combo_box->priv->popup_widget, cell, attribute, column);
4399   gtk_widget_queue_resize (GTK_WIDGET (combo_box));
4400 }
4401
4402 static void
4403 combo_cell_data_func (GtkCellLayout   *cell_layout,
4404                       GtkCellRenderer *cell,
4405                       GtkTreeModel    *tree_model,
4406                       GtkTreeIter     *iter,
4407                       gpointer         data)
4408 {
4409   ComboCellInfo *info = (ComboCellInfo *)data;
4410   GtkWidget *parent = NULL;
4411   
4412   if (!info->func)
4413     return;
4414
4415   info->func (cell_layout, cell, tree_model, iter, info->func_data);
4416
4417   if (GTK_IS_WIDGET (cell_layout))
4418     parent = gtk_widget_get_parent (GTK_WIDGET (cell_layout));
4419   
4420   if (GTK_IS_MENU_ITEM (parent) && 
4421       gtk_menu_item_get_submenu (GTK_MENU_ITEM (parent)))
4422     g_object_set (cell, "sensitive", TRUE, NULL);
4423 }
4424
4425
4426 static void 
4427 set_cell_data_func_recurse (GtkWidget       *menu,
4428                             GtkCellRenderer *cell,
4429                             ComboCellInfo   *info)
4430 {
4431   GList *i, *list;
4432   GtkWidget *submenu;    
4433   GtkWidget *cell_view;
4434   
4435  list = gtk_container_get_children (GTK_CONTAINER (menu));
4436   for (i = list; i; i = i->next)
4437     {
4438       cell_view = gtk_bin_get_child (GTK_BIN (i->data));
4439       if (GTK_IS_CELL_LAYOUT (cell_view))
4440         {
4441           /* Override sensitivity for inner nodes; we don't
4442            * want menuitems with submenus to appear insensitive 
4443            */ 
4444           gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (cell_view), 
4445                                               cell, 
4446                                               combo_cell_data_func, 
4447                                               info, NULL); 
4448           submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4449           if (submenu != NULL)
4450             set_cell_data_func_recurse (submenu, cell, info);
4451         }
4452     }
4453
4454   g_list_free (list);
4455 }
4456
4457 static void
4458 gtk_combo_box_cell_layout_set_cell_data_func (GtkCellLayout         *layout,
4459                                               GtkCellRenderer       *cell,
4460                                               GtkCellLayoutDataFunc  func,
4461                                               gpointer               func_data,
4462                                               GDestroyNotify         destroy)
4463 {
4464   GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4465   GtkComboBoxPrivate *priv = combo_box->priv;
4466   ComboCellInfo *info;
4467
4468   info = gtk_combo_box_get_cell_info (combo_box, cell);
4469   g_return_if_fail (info != NULL);
4470   
4471   if (info->destroy)
4472     {
4473       GDestroyNotify d = info->destroy;
4474
4475       info->destroy = NULL;
4476       d (info->func_data);
4477     }
4478
4479   info->func = func;
4480   info->func_data = func_data;
4481   info->destroy = destroy;
4482
4483   if (priv->cell_view)
4484     gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (priv->cell_view), cell, func, func_data, NULL);
4485
4486   if (priv->column)
4487     gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (priv->column), cell, func, func_data, NULL);
4488
4489   if (GTK_IS_MENU (priv->popup_widget))
4490     set_cell_data_func_recurse (priv->popup_widget, cell, info);
4491
4492   gtk_widget_queue_resize (GTK_WIDGET (combo_box));
4493 }
4494
4495 static void 
4496 clear_attributes_recurse (GtkWidget       *menu,
4497                           GtkCellRenderer *cell)
4498 {
4499   GList *i, *list;
4500   GtkWidget *submenu;
4501   GtkWidget *child;
4502
4503   list = gtk_container_get_children (GTK_CONTAINER (menu));
4504   for (i = list; i; i = i->next)
4505     {
4506       child = gtk_bin_get_child (GTK_BIN (i->data));
4507       if (GTK_IS_CELL_LAYOUT (child))
4508         gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (child), cell);
4509
4510       submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4511       if (submenu != NULL)
4512         clear_attributes_recurse (submenu, cell);
4513     }
4514
4515   g_list_free (list);
4516 }
4517
4518 static void
4519 gtk_combo_box_cell_layout_clear_attributes (GtkCellLayout   *layout,
4520                                             GtkCellRenderer *cell)
4521 {
4522   GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4523   GtkComboBoxPrivate *priv;
4524   ComboCellInfo *info;
4525   GSList *list;
4526
4527   priv = combo_box->priv;
4528
4529   info = gtk_combo_box_get_cell_info (combo_box, cell);
4530   g_return_if_fail (info != NULL);
4531
4532   list = info->attributes;
4533   while (list && list->next)
4534     {
4535       g_free (list->data);
4536       list = list->next->next;
4537     }
4538   g_slist_free (info->attributes);
4539   info->attributes = NULL;
4540
4541   if (priv->cell_view)
4542     gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (priv->cell_view), cell);
4543
4544   if (priv->column)
4545     gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (priv->column), cell);
4546
4547   if (GTK_IS_MENU (priv->popup_widget))
4548     clear_attributes_recurse (priv->popup_widget, cell);
4549
4550   gtk_widget_queue_resize (GTK_WIDGET (combo_box));
4551 }
4552
4553 static void 
4554 reorder_recurse (GtkWidget             *menu,
4555                  GtkCellRenderer       *cell,
4556                  gint                   position)
4557 {
4558   GList *i, *list;
4559   GtkWidget *child;
4560   GtkWidget *submenu;    
4561
4562   list = gtk_container_get_children (GTK_CONTAINER (menu));
4563   for (i = list; i; i = i->next)
4564     {
4565       child = gtk_bin_get_child (GTK_BIN (i->data));
4566       if (GTK_IS_CELL_LAYOUT (child))
4567         gtk_cell_layout_reorder (GTK_CELL_LAYOUT (child),
4568                                  cell, position);
4569
4570       submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4571       if (submenu != NULL)
4572         reorder_recurse (submenu, cell, position);
4573     }
4574
4575   g_list_free (list);
4576 }
4577
4578 static void
4579 gtk_combo_box_cell_layout_reorder (GtkCellLayout   *layout,
4580                                    GtkCellRenderer *cell,
4581                                    gint             position)
4582 {
4583   GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4584   GtkComboBoxPrivate *priv;
4585   ComboCellInfo *info;
4586   GSList *link;
4587
4588   priv = combo_box->priv;
4589
4590   info = gtk_combo_box_get_cell_info (combo_box, cell);
4591
4592   g_return_if_fail (info != NULL);
4593   g_return_if_fail (position >= 0);
4594
4595   link = g_slist_find (priv->cells, info);
4596
4597   g_return_if_fail (link != NULL);
4598
4599   priv->cells = g_slist_delete_link (priv->cells, link);
4600   priv->cells = g_slist_insert (priv->cells, info, position);
4601
4602   if (priv->cell_view)
4603     gtk_cell_layout_reorder (GTK_CELL_LAYOUT (priv->cell_view),
4604                              cell, position);
4605
4606   if (priv->column)
4607     gtk_cell_layout_reorder (GTK_CELL_LAYOUT (priv->column),
4608                              cell, position);
4609
4610   if (GTK_IS_MENU (priv->popup_widget))
4611     reorder_recurse (priv->popup_widget, cell, position);
4612
4613   gtk_widget_queue_draw (GTK_WIDGET (combo_box));
4614 }
4615
4616 /*
4617  * public API
4618  */
4619
4620 /**
4621  * gtk_combo_box_new:
4622  *
4623  * Creates a new empty #GtkComboBox.
4624  *
4625  * Return value: A new #GtkComboBox.
4626  *
4627  * Since: 2.4
4628  */
4629 GtkWidget *
4630 gtk_combo_box_new (void)
4631 {
4632   return g_object_new (GTK_TYPE_COMBO_BOX, NULL);
4633 }
4634
4635 /**
4636  * gtk_combo_box_new_with_model:
4637  * @model: A #GtkTreeModel.
4638  *
4639  * Creates a new #GtkComboBox with the model initialized to @model.
4640  *
4641  * Return value: A new #GtkComboBox.
4642  *
4643  * Since: 2.4
4644  */
4645 GtkWidget *
4646 gtk_combo_box_new_with_model (GtkTreeModel *model)
4647 {
4648   GtkComboBox *combo_box;
4649
4650   g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL);
4651
4652   combo_box = g_object_new (GTK_TYPE_COMBO_BOX, "model", model, NULL);
4653
4654   return GTK_WIDGET (combo_box);
4655 }
4656
4657 /**
4658  * gtk_combo_box_get_wrap_width:
4659  * @combo_box: A #GtkComboBox
4660  *
4661  * Returns the wrap width which is used to determine the number of columns 
4662  * for the popup menu. If the wrap width is larger than 1, the combo box 
4663  * is in table mode.
4664  *
4665  * Returns: the wrap width.
4666  *
4667  * Since: 2.6
4668  */
4669 gint
4670 gtk_combo_box_get_wrap_width (GtkComboBox *combo_box)
4671 {
4672   g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), -1);
4673
4674   return combo_box->priv->wrap_width;
4675 }
4676
4677 /**
4678  * gtk_combo_box_set_wrap_width:
4679  * @combo_box: A #GtkComboBox
4680  * @width: Preferred number of columns
4681  *
4682  * Sets the wrap width of @combo_box to be @width. The wrap width is basically
4683  * the preferred number of columns when you want the popup to be layed out
4684  * in a table.
4685  *
4686  * Since: 2.4
4687  */
4688 void
4689 gtk_combo_box_set_wrap_width (GtkComboBox *combo_box,
4690                               gint         width)
4691 {
4692   GtkComboBoxPrivate *priv;
4693
4694   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4695   g_return_if_fail (width >= 0);
4696
4697   priv = combo_box->priv;
4698
4699   if (width != priv->wrap_width)
4700     {
4701       priv->wrap_width = width;
4702
4703       gtk_combo_box_check_appearance (combo_box);
4704       gtk_combo_box_relayout (combo_box);
4705       
4706       g_object_notify (G_OBJECT (combo_box), "wrap-width");
4707     }
4708 }
4709
4710 /**
4711  * gtk_combo_box_get_row_span_column:
4712  * @combo_box: A #GtkComboBox
4713  *
4714  * Returns the column with row span information for @combo_box.
4715  *
4716  * Returns: the row span column.
4717  *
4718  * Since: 2.6
4719  */
4720 gint
4721 gtk_combo_box_get_row_span_column (GtkComboBox *combo_box)
4722 {
4723   g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), -1);
4724
4725   return combo_box->priv->row_column;
4726 }
4727
4728 /**
4729  * gtk_combo_box_set_row_span_column:
4730  * @combo_box: A #GtkComboBox.
4731  * @row_span: A column in the model passed during construction.
4732  *
4733  * Sets the column with row span information for @combo_box to be @row_span.
4734  * The row span column contains integers which indicate how many rows
4735  * an item should span.
4736  *
4737  * Since: 2.4
4738  */
4739 void
4740 gtk_combo_box_set_row_span_column (GtkComboBox *combo_box,
4741                                    gint         row_span)
4742 {
4743   GtkComboBoxPrivate *priv;
4744   gint col;
4745
4746   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4747
4748   priv = combo_box->priv;
4749
4750   col = gtk_tree_model_get_n_columns (priv->model);
4751   g_return_if_fail (row_span >= -1 && row_span < col);
4752
4753   if (row_span != priv->row_column)
4754     {
4755       priv->row_column = row_span;
4756       
4757       gtk_combo_box_relayout (combo_box);
4758  
4759       g_object_notify (G_OBJECT (combo_box), "row-span-column");
4760     }
4761 }
4762
4763 /**
4764  * gtk_combo_box_get_column_span_column:
4765  * @combo_box: A #GtkComboBox
4766  *
4767  * Returns the column with column span information for @combo_box.
4768  *
4769  * Returns: the column span column.
4770  *
4771  * Since: 2.6
4772  */
4773 gint
4774 gtk_combo_box_get_column_span_column (GtkComboBox *combo_box)
4775 {
4776   g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), -1);
4777
4778   return combo_box->priv->col_column;
4779 }
4780
4781 /**
4782  * gtk_combo_box_set_column_span_column:
4783  * @combo_box: A #GtkComboBox
4784  * @column_span: A column in the model passed during construction
4785  *
4786  * Sets the column with column span information for @combo_box to be
4787  * @column_span. The column span column contains integers which indicate
4788  * how many columns an item should span.
4789  *
4790  * Since: 2.4
4791  */
4792 void
4793 gtk_combo_box_set_column_span_column (GtkComboBox *combo_box,
4794                                       gint         column_span)
4795 {
4796   GtkComboBoxPrivate *priv;
4797   gint col;
4798
4799   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4800
4801   priv = combo_box->priv;
4802
4803   col = gtk_tree_model_get_n_columns (priv->model);
4804   g_return_if_fail (column_span >= -1 && column_span < col);
4805
4806   if (column_span != priv->col_column)
4807     {
4808       priv->col_column = column_span;
4809       
4810       gtk_combo_box_relayout (combo_box);
4811
4812       g_object_notify (G_OBJECT (combo_box), "column-span-column");
4813     }
4814 }
4815
4816 /**
4817  * gtk_combo_box_get_active:
4818  * @combo_box: A #GtkComboBox
4819  *
4820  * Returns the index of the currently active item, or -1 if there's no
4821  * active item. If the model is a non-flat treemodel, and the active item 
4822  * is not an immediate child of the root of the tree, this function returns 
4823  * <literal>gtk_tree_path_get_indices (path)[0]</literal>, where 
4824  * <literal>path</literal> is the #GtkTreePath of the active item.
4825  *
4826  * Return value: An integer which is the index of the currently active item, 
4827  *     or -1 if there's no active item.
4828  *
4829  * Since: 2.4
4830  */
4831 gint
4832 gtk_combo_box_get_active (GtkComboBox *combo_box)
4833 {
4834   GtkComboBoxPrivate *priv;
4835   gint result;
4836
4837   g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), 0);
4838
4839   priv = combo_box->priv;
4840
4841   if (gtk_tree_row_reference_valid (priv->active_row))
4842     {
4843       GtkTreePath *path;
4844
4845       path = gtk_tree_row_reference_get_path (priv->active_row);      
4846       result = gtk_tree_path_get_indices (path)[0];
4847       gtk_tree_path_free (path);
4848     }
4849   else
4850     result = -1;
4851
4852   return result;
4853 }
4854
4855 /**
4856  * gtk_combo_box_set_active:
4857  * @combo_box: A #GtkComboBox
4858  * @index_: An index in the model passed during construction, or -1 to have
4859  * no active item
4860  *
4861  * Sets the active item of @combo_box to be the item at @index.
4862  *
4863  * Since: 2.4
4864  */
4865 void
4866 gtk_combo_box_set_active (GtkComboBox *combo_box,
4867                           gint         index_)
4868 {
4869   GtkTreePath *path = NULL;
4870   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4871   g_return_if_fail (index_ >= -1);
4872
4873   if (combo_box->priv->model == NULL)
4874     {
4875       /* Save index, in case the model is set after the index */
4876       combo_box->priv->active = index_;
4877       if (index_ != -1)
4878         return;
4879     }
4880
4881   if (index_ != -1)
4882     path = gtk_tree_path_new_from_indices (index_, -1);
4883    
4884   gtk_combo_box_set_active_internal (combo_box, path);
4885
4886   if (path)
4887     gtk_tree_path_free (path);
4888 }
4889
4890 static void
4891 gtk_combo_box_set_active_internal (GtkComboBox *combo_box,
4892                                    GtkTreePath *path)
4893 {
4894   GtkComboBoxPrivate *priv = combo_box->priv;
4895   GtkTreePath *active_path;
4896   gint path_cmp;
4897
4898   /* Remember whether the initially active row is valid. */
4899   gboolean is_valid_row_reference = gtk_tree_row_reference_valid (priv->active_row);
4900
4901   if (path && is_valid_row_reference)
4902     {
4903       active_path = gtk_tree_row_reference_get_path (priv->active_row);
4904       path_cmp = gtk_tree_path_compare (path, active_path);
4905       gtk_tree_path_free (active_path);
4906       if (path_cmp == 0)
4907         return;
4908     }
4909
4910   if (priv->active_row)
4911     {
4912       gtk_tree_row_reference_free (priv->active_row);
4913       priv->active_row = NULL;
4914     }
4915   
4916   if (!path)
4917     {
4918       if (priv->tree_view)
4919         gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view)));
4920       else
4921         {
4922           GtkMenu *menu = GTK_MENU (priv->popup_widget);
4923
4924           if (GTK_IS_MENU (menu))
4925             gtk_menu_set_active (menu, -1);
4926         }
4927
4928       if (priv->cell_view)
4929         gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (priv->cell_view), NULL);
4930
4931       /*
4932        *  Do not emit a "changed" signal when an already invalid selection was
4933        *  now set to invalid.
4934        */
4935       if (!is_valid_row_reference)
4936         return;
4937     }
4938   else
4939     {
4940       priv->active_row = 
4941         gtk_tree_row_reference_new (priv->model, path);
4942
4943       if (priv->tree_view)
4944         {
4945           gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->tree_view), 
4946                                     path, NULL, FALSE);
4947         }
4948       else if (GTK_IS_MENU (priv->popup_widget))
4949         {
4950           /* FIXME handle nested menus better */
4951           gtk_menu_set_active (GTK_MENU (priv->popup_widget), 
4952                                gtk_tree_path_get_indices (path)[0]);
4953         }
4954
4955       if (priv->cell_view)
4956         gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (priv->cell_view), 
4957                                          path);
4958     }
4959
4960   g_signal_emit (combo_box, combo_box_signals[CHANGED], 0);
4961   g_object_notify (G_OBJECT (combo_box), "active");
4962 }
4963
4964
4965 /**
4966  * gtk_combo_box_get_active_iter:
4967  * @combo_box: A #GtkComboBox
4968  * @iter: (out): The uninitialized #GtkTreeIter
4969  * 
4970  * Sets @iter to point to the current active item, if it exists.
4971  * 
4972  * Return value: %TRUE, if @iter was set
4973  *
4974  * Since: 2.4
4975  */
4976 gboolean
4977 gtk_combo_box_get_active_iter (GtkComboBox     *combo_box,
4978                                GtkTreeIter     *iter)
4979 {
4980   GtkTreePath *path;
4981   gboolean result;
4982
4983   g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
4984
4985   if (!gtk_tree_row_reference_valid (combo_box->priv->active_row))
4986     return FALSE;
4987
4988   path = gtk_tree_row_reference_get_path (combo_box->priv->active_row);
4989   result = gtk_tree_model_get_iter (combo_box->priv->model, iter, path);
4990   gtk_tree_path_free (path);
4991
4992   return result;
4993 }
4994
4995 /**
4996  * gtk_combo_box_set_active_iter:
4997  * @combo_box: A #GtkComboBox
4998  * @iter: (allow-none): The #GtkTreeIter, or %NULL
4999  * 
5000  * Sets the current active item to be the one referenced by @iter, or
5001  * unsets the active item if @iter is %NULL.
5002  * 
5003  * Since: 2.4
5004  */
5005 void
5006 gtk_combo_box_set_active_iter (GtkComboBox     *combo_box,
5007                                GtkTreeIter     *iter)
5008 {
5009   GtkTreePath *path = NULL;
5010
5011   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5012
5013   if (iter)
5014     path = gtk_tree_model_get_path (gtk_combo_box_get_model (combo_box), iter);
5015
5016   gtk_combo_box_set_active_internal (combo_box, path);
5017   gtk_tree_path_free (path);
5018 }
5019
5020 /**
5021  * gtk_combo_box_set_model:
5022  * @combo_box: A #GtkComboBox
5023  * @model: (allow-none): A #GtkTreeModel
5024  *
5025  * Sets the model used by @combo_box to be @model. Will unset a previously set
5026  * model (if applicable). If model is %NULL, then it will unset the model.
5027  *
5028  * Note that this function does not clear the cell renderers, you have to 
5029  * call gtk_cell_layout_clear() yourself if you need to set up different 
5030  * cell renderers for the new model.
5031  *
5032  * Since: 2.4
5033  */
5034 void
5035 gtk_combo_box_set_model (GtkComboBox  *combo_box,
5036                          GtkTreeModel *model)
5037 {
5038   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5039   g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
5040
5041   if (model == combo_box->priv->model)
5042     return;
5043   
5044   gtk_combo_box_unset_model (combo_box);
5045
5046   if (model == NULL)
5047     goto out;
5048
5049   combo_box->priv->model = model;
5050   g_object_ref (combo_box->priv->model);
5051
5052   combo_box->priv->inserted_id =
5053     g_signal_connect (combo_box->priv->model, "row-inserted",
5054                       G_CALLBACK (gtk_combo_box_model_row_inserted),
5055                       combo_box);
5056   combo_box->priv->deleted_id =
5057     g_signal_connect (combo_box->priv->model, "row-deleted",
5058                       G_CALLBACK (gtk_combo_box_model_row_deleted),
5059                       combo_box);
5060   combo_box->priv->reordered_id =
5061     g_signal_connect (combo_box->priv->model, "rows-reordered",
5062                       G_CALLBACK (gtk_combo_box_model_rows_reordered),
5063                       combo_box);
5064   combo_box->priv->changed_id =
5065     g_signal_connect (combo_box->priv->model, "row-changed",
5066                       G_CALLBACK (gtk_combo_box_model_row_changed),
5067                       combo_box);
5068       
5069   if (combo_box->priv->tree_view)
5070     {
5071       /* list mode */
5072       gtk_tree_view_set_model (GTK_TREE_VIEW (combo_box->priv->tree_view),
5073                                combo_box->priv->model);
5074       gtk_combo_box_list_popup_resize (combo_box);
5075     }
5076   else
5077     {
5078       /* menu mode */
5079       if (combo_box->priv->popup_widget)
5080         gtk_combo_box_menu_fill (combo_box);
5081
5082     }
5083
5084   if (combo_box->priv->cell_view)
5085     gtk_cell_view_set_model (GTK_CELL_VIEW (combo_box->priv->cell_view),
5086                              combo_box->priv->model);
5087
5088   if (combo_box->priv->active != -1)
5089     {
5090       /* If an index was set in advance, apply it now */
5091       gtk_combo_box_set_active (combo_box, combo_box->priv->active);
5092       combo_box->priv->active = -1;
5093     }
5094
5095 out:
5096   gtk_combo_box_update_sensitivity (combo_box);
5097
5098   g_object_notify (G_OBJECT (combo_box), "model");
5099 }
5100
5101 /**
5102  * gtk_combo_box_get_model:
5103  * @combo_box: A #GtkComboBox
5104  *
5105  * Returns the #GtkTreeModel which is acting as data source for @combo_box.
5106  *
5107  * Return value: (transfer none): A #GtkTreeModel which was passed
5108  *     during construction.
5109  *
5110  * Since: 2.4
5111  */
5112 GtkTreeModel *
5113 gtk_combo_box_get_model (GtkComboBox *combo_box)
5114 {
5115   g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
5116
5117   return combo_box->priv->model;
5118 }
5119
5120
5121 /* convenience API for simple text combos */
5122
5123 /**
5124  * gtk_combo_box_new_text:
5125  *
5126  * Convenience function which constructs a new text combo box, which is a
5127  * #GtkComboBox just displaying strings. If you use this function to create
5128  * a text combo box, you should only manipulate its data source with the
5129  * following convenience functions: gtk_combo_box_append_text(),
5130  * gtk_combo_box_insert_text(), gtk_combo_box_prepend_text() and
5131  * gtk_combo_box_remove_text().
5132  *
5133  * Return value: (transfer none): A new text combo box.
5134  *
5135  * Since: 2.4
5136  */
5137 GtkWidget *
5138 gtk_combo_box_new_text (void)
5139 {
5140   GtkWidget *combo_box;
5141   GtkCellRenderer *cell;
5142   GtkListStore *store;
5143
5144   store = gtk_list_store_new (1, G_TYPE_STRING);
5145   combo_box = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store));
5146   g_object_unref (store);
5147
5148   cell = gtk_cell_renderer_text_new ();
5149   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), cell, TRUE);
5150   gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), cell,
5151                                   "text", 0,
5152                                   NULL);
5153
5154   return combo_box;
5155 }
5156
5157 /**
5158  * gtk_combo_box_append_text:
5159  * @combo_box: A #GtkComboBox constructed using gtk_combo_box_new_text()
5160  * @text: A string
5161  *
5162  * Appends @string to the list of strings stored in @combo_box. Note that
5163  * you can only use this function with combo boxes constructed with
5164  * gtk_combo_box_new_text().
5165  *
5166  * Since: 2.4
5167  */
5168 void
5169 gtk_combo_box_append_text (GtkComboBox *combo_box,
5170                            const gchar *text)
5171 {
5172   GtkTreeIter iter;
5173   GtkListStore *store;
5174
5175   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5176   g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model));
5177   g_return_if_fail (gtk_tree_model_get_column_type (combo_box->priv->model, 0)
5178                     == G_TYPE_STRING);
5179   g_return_if_fail (text != NULL);
5180
5181   store = GTK_LIST_STORE (combo_box->priv->model);
5182
5183   gtk_list_store_append (store, &iter);
5184   gtk_list_store_set (store, &iter, 0, text, -1);
5185 }
5186
5187 /**
5188  * gtk_combo_box_insert_text:
5189  * @combo_box: A #GtkComboBox constructed using gtk_combo_box_new_text()
5190  * @position: An index to insert @text
5191  * @text: A string
5192  *
5193  * Inserts @string at @position in the list of strings stored in @combo_box.
5194  * Note that you can only use this function with combo boxes constructed
5195  * with gtk_combo_box_new_text().
5196  *
5197  * Since: 2.4
5198  */
5199 void
5200 gtk_combo_box_insert_text (GtkComboBox *combo_box,
5201                            gint         position,
5202                            const gchar *text)
5203 {
5204   GtkTreeIter iter;
5205   GtkListStore *store;
5206
5207   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5208   g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model));
5209   g_return_if_fail (position >= 0);
5210   g_return_if_fail (gtk_tree_model_get_column_type (combo_box->priv->model, 0)
5211                     == G_TYPE_STRING);
5212   g_return_if_fail (text != NULL);
5213
5214   store = GTK_LIST_STORE (combo_box->priv->model);
5215
5216   gtk_list_store_insert (store, &iter, position);
5217   gtk_list_store_set (store, &iter, 0, text, -1);
5218 }
5219
5220 /**
5221  * gtk_combo_box_prepend_text:
5222  * @combo_box: A #GtkComboBox constructed with gtk_combo_box_new_text()
5223  * @text: A string
5224  *
5225  * Prepends @string to the list of strings stored in @combo_box. Note that
5226  * you can only use this function with combo boxes constructed with
5227  * gtk_combo_box_new_text().
5228  *
5229  * Since: 2.4
5230  */
5231 void
5232 gtk_combo_box_prepend_text (GtkComboBox *combo_box,
5233                             const gchar *text)
5234 {
5235   GtkTreeIter iter;
5236   GtkListStore *store;
5237
5238   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5239   g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model));
5240   g_return_if_fail (gtk_tree_model_get_column_type (combo_box->priv->model, 0)
5241                     == G_TYPE_STRING);
5242   g_return_if_fail (text != NULL);
5243
5244   store = GTK_LIST_STORE (combo_box->priv->model);
5245
5246   gtk_list_store_prepend (store, &iter);
5247   gtk_list_store_set (store, &iter, 0, text, -1);
5248 }
5249
5250 /**
5251  * gtk_combo_box_remove_text:
5252  * @combo_box: A #GtkComboBox constructed with gtk_combo_box_new_text()
5253  * @position: Index of the item to remove
5254  *
5255  * Removes the string at @position from @combo_box. Note that you can only use
5256  * this function with combo boxes constructed with gtk_combo_box_new_text().
5257  *
5258  * Since: 2.4
5259  */
5260 void
5261 gtk_combo_box_remove_text (GtkComboBox *combo_box,
5262                            gint         position)
5263 {
5264   GtkTreeIter iter;
5265   GtkListStore *store;
5266
5267   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5268   g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model));
5269   g_return_if_fail (gtk_tree_model_get_column_type (combo_box->priv->model, 0)
5270                     == G_TYPE_STRING);
5271   g_return_if_fail (position >= 0);
5272
5273   store = GTK_LIST_STORE (combo_box->priv->model);
5274
5275   if (gtk_tree_model_iter_nth_child (combo_box->priv->model, &iter,
5276                                      NULL, position))
5277     gtk_list_store_remove (store, &iter);
5278 }
5279
5280 /**
5281  * gtk_combo_box_get_active_text:
5282  * @combo_box: A #GtkComboBox constructed with gtk_combo_box_new_text()
5283  *
5284  * Returns the currently active string in @combo_box or %NULL if none
5285  * is selected.  Note that you can only use this function with combo
5286  * boxes constructed with gtk_combo_box_new_text() and with
5287  * #GtkComboBoxEntry<!-- -->s.
5288  *
5289  * Returns: a newly allocated string containing the currently active text.
5290  *     Must be freed with g_free().
5291  *
5292  * Since: 2.6
5293  */
5294 gchar *
5295 gtk_combo_box_get_active_text (GtkComboBox *combo_box)
5296 {
5297   GtkComboBoxClass *class;
5298
5299   g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
5300
5301   class = GTK_COMBO_BOX_GET_CLASS (combo_box);
5302
5303   if (class->get_active_text)
5304     return class->get_active_text (combo_box);
5305
5306   return NULL;
5307 }
5308
5309 static gchar *
5310 gtk_combo_box_real_get_active_text (GtkComboBox *combo_box)
5311 {
5312   GtkTreeIter iter;
5313   gchar *text = NULL;
5314
5315   g_return_val_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model), NULL);
5316   g_return_val_if_fail (gtk_tree_model_get_column_type (combo_box->priv->model, 0)
5317                         == G_TYPE_STRING, NULL);
5318
5319   if (gtk_combo_box_get_active_iter (combo_box, &iter))
5320     gtk_tree_model_get (combo_box->priv->model, &iter, 
5321                         0, &text, -1);
5322
5323   return text;
5324 }
5325
5326 static void
5327 gtk_combo_box_real_move_active (GtkComboBox   *combo_box,
5328                                 GtkScrollType  scroll)
5329 {
5330   GtkTreeIter iter;
5331   GtkTreeIter new_iter;
5332   gboolean    active_iter;
5333   gboolean    found;
5334
5335   if (!combo_box->priv->model)
5336     {
5337       gtk_widget_error_bell (GTK_WIDGET (combo_box));
5338       return;
5339     }
5340
5341   active_iter = gtk_combo_box_get_active_iter (combo_box, &iter);
5342
5343   switch (scroll)
5344     {
5345     case GTK_SCROLL_STEP_BACKWARD:
5346     case GTK_SCROLL_STEP_UP:
5347     case GTK_SCROLL_STEP_LEFT:
5348       if (active_iter)
5349         {
5350           found = tree_prev (combo_box, combo_box->priv->model,
5351                              &iter, &new_iter, FALSE);
5352           break;
5353         }
5354       /* else fall through */
5355
5356     case GTK_SCROLL_PAGE_FORWARD:
5357     case GTK_SCROLL_PAGE_DOWN:
5358     case GTK_SCROLL_PAGE_RIGHT:
5359     case GTK_SCROLL_END:
5360       found = tree_last (combo_box, combo_box->priv->model, &new_iter, FALSE);
5361       break;
5362
5363     case GTK_SCROLL_STEP_FORWARD:
5364     case GTK_SCROLL_STEP_DOWN:
5365     case GTK_SCROLL_STEP_RIGHT:
5366       if (active_iter)
5367         {
5368           found = tree_next (combo_box, combo_box->priv->model,
5369                              &iter, &new_iter, FALSE);
5370           break;
5371         }
5372       /* else fall through */
5373
5374     case GTK_SCROLL_PAGE_BACKWARD:
5375     case GTK_SCROLL_PAGE_UP:
5376     case GTK_SCROLL_PAGE_LEFT:
5377     case GTK_SCROLL_START:
5378       found = tree_first (combo_box, combo_box->priv->model, &new_iter, FALSE);
5379       break;
5380
5381     default:
5382       return;
5383     }
5384
5385   if (found && active_iter)
5386     {
5387       GtkTreePath *old_path;
5388       GtkTreePath *new_path;
5389
5390       old_path = gtk_tree_model_get_path (combo_box->priv->model, &iter);
5391       new_path = gtk_tree_model_get_path (combo_box->priv->model, &new_iter);
5392
5393       if (gtk_tree_path_compare (old_path, new_path) == 0)
5394         found = FALSE;
5395
5396       gtk_tree_path_free (old_path);
5397       gtk_tree_path_free (new_path);
5398     }
5399
5400   if (found)
5401     {
5402       gtk_combo_box_set_active_iter (combo_box, &new_iter);
5403     }
5404   else
5405     {
5406       gtk_widget_error_bell (GTK_WIDGET (combo_box));
5407     }
5408 }
5409
5410 static gboolean
5411 gtk_combo_box_mnemonic_activate (GtkWidget *widget,
5412                                  gboolean   group_cycling)
5413 {
5414   GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
5415
5416   gtk_widget_grab_focus (combo_box->priv->button);
5417
5418   return TRUE;
5419 }
5420
5421 static void
5422 gtk_combo_box_grab_focus (GtkWidget *widget)
5423 {
5424   GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
5425
5426   gtk_widget_grab_focus (combo_box->priv->button);
5427 }
5428
5429 static void
5430 gtk_combo_box_destroy (GtkObject *object)
5431 {
5432   GtkComboBox *combo_box = GTK_COMBO_BOX (object);
5433
5434   if (combo_box->priv->popup_idle_id > 0)
5435     {
5436       g_source_remove (combo_box->priv->popup_idle_id);
5437       combo_box->priv->popup_idle_id = 0;
5438     }
5439
5440   gtk_combo_box_popdown (combo_box);
5441
5442   if (combo_box->priv->row_separator_destroy)
5443     combo_box->priv->row_separator_destroy (combo_box->priv->row_separator_data);
5444
5445   combo_box->priv->row_separator_func = NULL;
5446   combo_box->priv->row_separator_data = NULL;
5447   combo_box->priv->row_separator_destroy = NULL;
5448
5449   GTK_OBJECT_CLASS (gtk_combo_box_parent_class)->destroy (object);
5450   combo_box->priv->cell_view = NULL;
5451 }
5452
5453 static void
5454 gtk_combo_box_dispose(GObject* object)
5455 {
5456   GtkComboBox *combo_box = GTK_COMBO_BOX (object);
5457
5458   if (GTK_IS_MENU (combo_box->priv->popup_widget))
5459     {
5460       gtk_combo_box_menu_destroy (combo_box);
5461       gtk_menu_detach (GTK_MENU (combo_box->priv->popup_widget));
5462       combo_box->priv->popup_widget = NULL;
5463     }
5464
5465   G_OBJECT_CLASS (gtk_combo_box_parent_class)->dispose (object);
5466 }
5467
5468 static void
5469 gtk_combo_box_finalize (GObject *object)
5470 {
5471   GtkComboBox *combo_box = GTK_COMBO_BOX (object);
5472   GSList *i;
5473   
5474   if (GTK_IS_TREE_VIEW (combo_box->priv->tree_view))
5475     gtk_combo_box_list_destroy (combo_box);
5476
5477   if (combo_box->priv->popup_window)
5478     gtk_widget_destroy (combo_box->priv->popup_window);
5479
5480   gtk_combo_box_unset_model (combo_box);
5481
5482   for (i = combo_box->priv->cells; i; i = i->next)
5483     {
5484       ComboCellInfo *info = (ComboCellInfo *)i->data;
5485       GSList *list = info->attributes;
5486
5487       if (info->destroy)
5488         info->destroy (info->func_data);
5489
5490       while (list && list->next)
5491         {
5492           g_free (list->data);
5493           list = list->next->next;
5494         }
5495       g_slist_free (info->attributes);
5496
5497       g_object_unref (info->cell);
5498       g_slice_free (ComboCellInfo, info);
5499     }
5500    g_slist_free (combo_box->priv->cells);
5501
5502    g_free (combo_box->priv->tearoff_title);
5503
5504    G_OBJECT_CLASS (gtk_combo_box_parent_class)->finalize (object);
5505 }
5506
5507 static gboolean
5508 gtk_cell_editable_key_press (GtkWidget   *widget,
5509                              GdkEventKey *event,
5510                              gpointer     data)
5511 {
5512   GtkComboBox *combo_box = GTK_COMBO_BOX (data);
5513
5514   if (event->keyval == GDK_KEY_Escape)
5515     {
5516       g_object_set (combo_box,
5517                     "editing-canceled", TRUE,
5518                     NULL);
5519       gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (combo_box));
5520       gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (combo_box));
5521       
5522       return TRUE;
5523     }
5524   else if (event->keyval == GDK_KEY_Return ||
5525            event->keyval == GDK_KEY_ISO_Enter ||
5526            event->keyval == GDK_KEY_KP_Enter)
5527     {
5528       gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (combo_box));
5529       gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (combo_box));
5530       
5531       return TRUE;
5532     }
5533
5534   return FALSE;
5535 }
5536
5537 static gboolean
5538 popdown_idle (gpointer data)
5539 {
5540   GtkComboBox *combo_box;
5541
5542   combo_box = GTK_COMBO_BOX (data);
5543   
5544   gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (combo_box));
5545   gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (combo_box));
5546
5547   g_object_unref (combo_box);
5548
5549   return FALSE;
5550 }
5551
5552 static void
5553 popdown_handler (GtkWidget *widget,
5554                  gpointer   data)
5555 {
5556   gdk_threads_add_idle (popdown_idle, g_object_ref (data));
5557 }
5558
5559 static gboolean
5560 popup_idle (gpointer data)
5561 {
5562   GtkComboBox *combo_box;
5563
5564   combo_box = GTK_COMBO_BOX (data);
5565
5566   if (GTK_IS_MENU (combo_box->priv->popup_widget) &&
5567       combo_box->priv->cell_view)
5568     g_signal_connect_object (combo_box->priv->popup_widget,
5569                              "unmap", G_CALLBACK (popdown_handler),
5570                              combo_box, 0);
5571   
5572   /* we unset this if a menu item is activated */
5573   g_object_set (combo_box,
5574                 "editing-canceled", TRUE,
5575                 NULL);
5576   gtk_combo_box_popup (combo_box);
5577
5578   combo_box->priv->popup_idle_id = 0;
5579   combo_box->priv->activate_button = 0;
5580   combo_box->priv->activate_time = 0;
5581
5582   return FALSE;
5583 }
5584
5585 static void
5586 gtk_combo_box_start_editing (GtkCellEditable *cell_editable,
5587                              GdkEvent        *event)
5588 {
5589   GtkComboBox *combo_box = GTK_COMBO_BOX (cell_editable);
5590   GtkWidget *child;
5591
5592   combo_box->priv->is_cell_renderer = TRUE;
5593
5594   if (combo_box->priv->cell_view)
5595     {
5596       g_signal_connect_object (combo_box->priv->button, "key-press-event",
5597                                G_CALLBACK (gtk_cell_editable_key_press), 
5598                                cell_editable, 0);  
5599
5600       gtk_widget_grab_focus (combo_box->priv->button);
5601     }
5602   else
5603     {
5604       child = gtk_bin_get_child (GTK_BIN (combo_box));
5605
5606       g_signal_connect_object (child, "key-press-event",
5607                                G_CALLBACK (gtk_cell_editable_key_press), 
5608                                cell_editable, 0);  
5609
5610       gtk_widget_grab_focus (child);
5611       gtk_widget_set_can_focus (combo_box->priv->button, FALSE);
5612     }
5613
5614   /* we do the immediate popup only for the optionmenu-like 
5615    * appearance 
5616    */  
5617   if (combo_box->priv->is_cell_renderer && 
5618       combo_box->priv->cell_view && !combo_box->priv->tree_view)
5619     {
5620       if (event && event->type == GDK_BUTTON_PRESS)
5621         {
5622           GdkEventButton *event_button = (GdkEventButton *)event;
5623
5624           combo_box->priv->activate_button = event_button->button;
5625           combo_box->priv->activate_time = event_button->time;
5626         }
5627
5628       combo_box->priv->popup_idle_id = 
5629           gdk_threads_add_idle (popup_idle, combo_box);
5630     }
5631 }
5632
5633
5634 /**
5635  * gtk_combo_box_get_add_tearoffs:
5636  * @combo_box: a #GtkComboBox
5637  * 
5638  * Gets the current value of the :add-tearoffs property.
5639  * 
5640  * Return value: the current value of the :add-tearoffs property.
5641  */
5642 gboolean
5643 gtk_combo_box_get_add_tearoffs (GtkComboBox *combo_box)
5644 {
5645   g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
5646
5647   return combo_box->priv->add_tearoffs;
5648 }
5649
5650 /**
5651  * gtk_combo_box_set_add_tearoffs:
5652  * @combo_box: a #GtkComboBox 
5653  * @add_tearoffs: %TRUE to add tearoff menu items
5654  *  
5655  * Sets whether the popup menu should have a tearoff 
5656  * menu item.
5657  *
5658  * Since: 2.6
5659  */
5660 void
5661 gtk_combo_box_set_add_tearoffs (GtkComboBox *combo_box,
5662                                 gboolean     add_tearoffs)
5663 {
5664   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5665
5666   add_tearoffs = add_tearoffs != FALSE;
5667
5668   if (combo_box->priv->add_tearoffs != add_tearoffs)
5669     {
5670       combo_box->priv->add_tearoffs = add_tearoffs;
5671       gtk_combo_box_check_appearance (combo_box);
5672       gtk_combo_box_relayout (combo_box);
5673       g_object_notify (G_OBJECT (combo_box), "add-tearoffs");
5674     }
5675 }
5676
5677 /**
5678  * gtk_combo_box_get_title:
5679  * @combo_box: a #GtkComboBox
5680  *
5681  * Gets the current title of the menu in tearoff mode. See
5682  * gtk_combo_box_set_add_tearoffs().
5683  *
5684  * Returns: the menu's title in tearoff mode. This is an internal copy of the
5685  * string which must not be freed.
5686  *
5687  * Since: 2.10
5688  */
5689 G_CONST_RETURN gchar*
5690 gtk_combo_box_get_title (GtkComboBox *combo_box)
5691 {
5692   g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
5693   
5694   return combo_box->priv->tearoff_title;
5695 }
5696
5697 static void
5698 gtk_combo_box_update_title (GtkComboBox *combo_box)
5699 {
5700   gtk_combo_box_check_appearance (combo_box);
5701   
5702   if (combo_box->priv->popup_widget && 
5703       GTK_IS_MENU (combo_box->priv->popup_widget))
5704     gtk_menu_set_title (GTK_MENU (combo_box->priv->popup_widget), 
5705                         combo_box->priv->tearoff_title);
5706 }
5707
5708 /**
5709  * gtk_combo_box_set_title:
5710  * @combo_box: a #GtkComboBox 
5711  * @title: a title for the menu in tearoff mode
5712  *  
5713  * Sets the menu's title in tearoff mode.
5714  *
5715  * Since: 2.10
5716  */
5717 void
5718 gtk_combo_box_set_title (GtkComboBox *combo_box,
5719                          const gchar *title)
5720 {
5721   GtkComboBoxPrivate *priv;
5722
5723   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5724
5725   priv = combo_box->priv;
5726
5727   if (strcmp (title ? title : "", 
5728               priv->tearoff_title ? priv->tearoff_title : "") != 0)
5729     {
5730       g_free (priv->tearoff_title);
5731       priv->tearoff_title = g_strdup (title);
5732
5733       gtk_combo_box_update_title (combo_box);
5734
5735       g_object_notify (G_OBJECT (combo_box), "tearoff-title");
5736     }
5737 }
5738
5739 /**
5740  * gtk_combo_box_get_popup_accessible:
5741  * @combo_box: a #GtkComboBox
5742  *
5743  * Gets the accessible object corresponding to the combo box's popup.
5744  *
5745  * This function is mostly intended for use by accessibility technologies;
5746  * applications should have little use for it.
5747  *
5748  * Returns: (transfer none): the accessible object corresponding
5749  *     to the combo box's popup.
5750  *
5751  * Since: 2.6
5752  */
5753 AtkObject*
5754 gtk_combo_box_get_popup_accessible (GtkComboBox *combo_box)
5755 {
5756   AtkObject *atk_obj;
5757
5758   g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
5759
5760   if (combo_box->priv->popup_widget)
5761     {
5762       atk_obj = gtk_widget_get_accessible (combo_box->priv->popup_widget);
5763       return atk_obj;
5764     }
5765
5766   return NULL;
5767 }
5768
5769 /**
5770  * gtk_combo_box_get_row_separator_func:
5771  * @combo_box: a #GtkComboBox
5772  * 
5773  * Returns the current row separator function.
5774  * 
5775  * Return value: the current row separator function.
5776  *
5777  * Since: 2.6
5778  */
5779 GtkTreeViewRowSeparatorFunc 
5780 gtk_combo_box_get_row_separator_func (GtkComboBox *combo_box)
5781 {
5782   g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
5783
5784   return combo_box->priv->row_separator_func;
5785 }
5786
5787 /**
5788  * gtk_combo_box_set_row_separator_func:
5789  * @combo_box: a #GtkComboBox
5790  * @func: a #GtkTreeViewRowSeparatorFunc
5791  * @data: (allow-none): user data to pass to @func, or %NULL
5792  * @destroy: (allow-none): destroy notifier for @data, or %NULL
5793  * 
5794  * Sets the row separator function, which is used to determine
5795  * whether a row should be drawn as a separator. If the row separator
5796  * function is %NULL, no separators are drawn. This is the default value.
5797  *
5798  * Since: 2.6
5799  */
5800 void
5801 gtk_combo_box_set_row_separator_func (GtkComboBox                 *combo_box,
5802                                       GtkTreeViewRowSeparatorFunc  func,
5803                                       gpointer                     data,
5804                                       GDestroyNotify               destroy)
5805 {
5806   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5807
5808   if (combo_box->priv->row_separator_destroy)
5809     combo_box->priv->row_separator_destroy (combo_box->priv->row_separator_data);
5810
5811   combo_box->priv->row_separator_func = func;
5812   combo_box->priv->row_separator_data = data;
5813   combo_box->priv->row_separator_destroy = destroy;
5814
5815   if (combo_box->priv->tree_view)
5816     gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (combo_box->priv->tree_view), 
5817                                           func, data, NULL);
5818
5819   gtk_combo_box_relayout (combo_box);
5820
5821   gtk_widget_queue_draw (GTK_WIDGET (combo_box));
5822 }
5823
5824 /**
5825  * gtk_combo_box_set_button_sensitivity:
5826  * @combo_box: a #GtkComboBox
5827  * @sensitivity: specify the sensitivity of the dropdown button
5828  *
5829  * Sets whether the dropdown button of the combo box should be
5830  * always sensitive (%GTK_SENSITIVITY_ON), never sensitive (%GTK_SENSITIVITY_OFF)
5831  * or only if there is at least one item to display (%GTK_SENSITIVITY_AUTO).
5832  *
5833  * Since: 2.14
5834  **/
5835 void
5836 gtk_combo_box_set_button_sensitivity (GtkComboBox        *combo_box,
5837                                       GtkSensitivityType  sensitivity)
5838 {
5839   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5840
5841   if (combo_box->priv->button_sensitivity != sensitivity)
5842     {
5843       combo_box->priv->button_sensitivity = sensitivity;
5844       gtk_combo_box_update_sensitivity (combo_box);
5845
5846       g_object_notify (G_OBJECT (combo_box), "button-sensitivity");
5847     }
5848 }
5849
5850 /**
5851  * gtk_combo_box_get_button_sensitivity:
5852  * @combo_box: a #GtkComboBox
5853  *
5854  * Returns whether the combo box sets the dropdown button
5855  * sensitive or not when there are no items in the model.
5856  *
5857  * Return Value: %GTK_SENSITIVITY_ON if the dropdown button
5858  *    is sensitive when the model is empty, %GTK_SENSITIVITY_OFF
5859  *    if the button is always insensitive or
5860  *    %GTK_SENSITIVITY_AUTO if it is only sensitive as long as
5861  *    the model has one item to be selected.
5862  *
5863  * Since: 2.14
5864  **/
5865 GtkSensitivityType
5866 gtk_combo_box_get_button_sensitivity (GtkComboBox *combo_box)
5867 {
5868   g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
5869
5870   return combo_box->priv->button_sensitivity;
5871 }
5872
5873
5874 /**
5875  * gtk_combo_box_set_focus_on_click:
5876  * @combo: a #GtkComboBox
5877  * @focus_on_click: whether the combo box grabs focus when clicked 
5878  *    with the mouse
5879  * 
5880  * Sets whether the combo box will grab focus when it is clicked with 
5881  * the mouse. Making mouse clicks not grab focus is useful in places 
5882  * like toolbars where you don't want the keyboard focus removed from 
5883  * the main area of the application.
5884  *
5885  * Since: 2.6
5886  */
5887 void
5888 gtk_combo_box_set_focus_on_click (GtkComboBox *combo_box,
5889                                   gboolean     focus_on_click)
5890 {
5891   g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5892   
5893   focus_on_click = focus_on_click != FALSE;
5894
5895   if (combo_box->priv->focus_on_click != focus_on_click)
5896     {
5897       combo_box->priv->focus_on_click = focus_on_click;
5898
5899       if (combo_box->priv->button)
5900         gtk_button_set_focus_on_click (GTK_BUTTON (combo_box->priv->button),
5901                                        focus_on_click);
5902       
5903       g_object_notify (G_OBJECT (combo_box), "focus-on-click");
5904     }
5905 }
5906
5907 /**
5908  * gtk_combo_box_get_focus_on_click:
5909  * @combo: a #GtkComboBox
5910  * 
5911  * Returns whether the combo box grabs focus when it is clicked 
5912  * with the mouse. See gtk_combo_box_set_focus_on_click().
5913  *
5914  * Return value: %TRUE if the combo box grabs focus when it is 
5915  *     clicked with the mouse.
5916  *
5917  * Since: 2.6
5918  */
5919 gboolean
5920 gtk_combo_box_get_focus_on_click (GtkComboBox *combo_box)
5921 {
5922   g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
5923   
5924   return combo_box->priv->focus_on_click;
5925 }
5926
5927
5928 static gboolean
5929 gtk_combo_box_buildable_custom_tag_start (GtkBuildable  *buildable,
5930                                           GtkBuilder    *builder,
5931                                           GObject       *child,
5932                                           const gchar   *tagname,
5933                                           GMarkupParser *parser,
5934                                           gpointer      *data)
5935 {
5936   if (parent_buildable_iface->custom_tag_start (buildable, builder, child,
5937                                                 tagname, parser, data))
5938     return TRUE;
5939
5940   return _gtk_cell_layout_buildable_custom_tag_start (buildable, builder, child,
5941                                                       tagname, parser, data);
5942 }
5943
5944 static void
5945 gtk_combo_box_buildable_custom_tag_end (GtkBuildable *buildable,
5946                                         GtkBuilder   *builder,
5947                                         GObject      *child,
5948                                         const gchar  *tagname,
5949                                         gpointer     *data)
5950 {
5951   if (strcmp (tagname, "attributes") == 0)
5952     _gtk_cell_layout_buildable_custom_tag_end (buildable, builder, child, tagname,
5953                                                data);
5954   else
5955     parent_buildable_iface->custom_tag_end (buildable, builder, child, tagname,
5956                                             data);
5957 }
5958
5959
5960 static void
5961 gtk_combo_box_size_request_init (GtkSizeRequestIface *iface)
5962 {
5963   iface->get_width            = gtk_combo_box_get_width;
5964   iface->get_height           = gtk_combo_box_get_height;
5965   iface->get_height_for_width = gtk_combo_box_get_height_for_width;
5966   iface->get_width_for_height = gtk_combo_box_get_width_for_height;
5967 }
5968
5969 static void
5970 gtk_combo_box_remeasure (GtkComboBox *combo_box)
5971 {
5972   GtkComboBoxPrivate *priv = combo_box->priv;
5973   GtkTreeIter iter;
5974   GtkTreePath *path;
5975
5976   if (!priv->model ||
5977       !gtk_tree_model_get_iter_first (priv->model, &iter))
5978     return;
5979
5980   priv->minimum_width = priv->natural_width = 0;
5981
5982   path = gtk_tree_path_new_from_indices (0, -1);
5983
5984   do
5985     {
5986       gint row_min = 0, row_nat = 0;
5987
5988       if (priv->cell_view)
5989         gtk_cell_view_get_desired_width_of_row (GTK_CELL_VIEW (priv->cell_view), 
5990                                                 path, &row_min, &row_nat);
5991
5992       priv->minimum_width = MAX (priv->minimum_width, row_min);
5993       priv->natural_width = MAX (priv->natural_width, row_nat);
5994
5995       gtk_tree_path_next (path);
5996     }
5997   while (gtk_tree_model_iter_next (priv->model, &iter));
5998
5999   gtk_tree_path_free (path);
6000 }
6001
6002
6003 static void
6004 gtk_combo_box_measure_height_for_width (GtkComboBox *combo_box,
6005                                         gint         avail_width, 
6006                                         gint        *min_height, 
6007                                         gint        *nat_height)
6008 {
6009   GtkWidget             *child;
6010   GtkComboBoxPrivate    *priv = combo_box->priv;
6011   GtkTreeIter            iter;
6012   GtkTreePath           *path;
6013   gint                   child_min, child_nat;
6014
6015   child = gtk_bin_get_child (GTK_BIN (combo_box));
6016
6017   gtk_size_request_get_height_for_width (GTK_SIZE_REQUEST (child), avail_width,
6018                                          &child_min, &child_nat);
6019
6020   if (!priv->model ||
6021       !gtk_tree_model_get_iter_first (priv->model, &iter))
6022     goto out;
6023
6024   path = gtk_tree_path_new_from_indices (0, -1);
6025
6026   do
6027     {
6028       gint row_min = 0, row_nat = 0;
6029
6030       if (priv->cell_view)
6031         gtk_cell_view_get_desired_height_for_width_of_row (GTK_CELL_VIEW (priv->cell_view), 
6032                                                            path, avail_width, 
6033                                                            &row_min, &row_nat);
6034
6035       child_min = MAX (child_min, row_min);
6036       child_nat = MAX (child_nat, row_nat);
6037
6038       gtk_tree_path_next (path);
6039     }
6040   while (gtk_tree_model_iter_next (priv->model, &iter));
6041
6042   gtk_tree_path_free (path);
6043
6044  out:
6045
6046   if (min_height)
6047     *min_height = child_min;
6048   if (nat_height)
6049     *nat_height = child_nat;
6050 }
6051
6052
6053 static void     
6054 gtk_combo_box_get_width (GtkSizeRequest      *widget,
6055                          gint                *minimum_size,
6056                          gint                *natural_size)
6057 {
6058   GtkComboBox           *combo_box = GTK_COMBO_BOX (widget);
6059   GtkComboBoxPrivate    *priv = combo_box->priv;
6060   GtkStyle              *style;
6061   gint                   focus_width, focus_pad;
6062   gint                   font_size, arrow_size;
6063   PangoContext          *context;
6064   PangoFontMetrics      *metrics;
6065   PangoFontDescription  *font_desc;
6066   GtkWidget             *child;
6067   gint                   minimum_width, natural_width;
6068   gint                   child_min, child_nat;
6069
6070   child = gtk_bin_get_child (GTK_BIN (widget));
6071  
6072   /* common */
6073   gtk_size_request_get_width (GTK_SIZE_REQUEST (child), &child_min, &child_nat);
6074   gtk_combo_box_remeasure (combo_box);
6075
6076   child_min  = MAX (child_min,  priv->minimum_width);
6077   child_nat  = MAX (child_nat,  priv->natural_width);
6078
6079   gtk_widget_style_get (GTK_WIDGET (widget),
6080                         "focus-line-width", &focus_width,
6081                         "focus-padding", &focus_pad,
6082                         "arrow-size", &arrow_size,
6083                         NULL);
6084
6085   font_desc = gtk_widget_get_style (child)->font_desc;
6086   context = gtk_widget_get_pango_context (GTK_WIDGET (widget));
6087   metrics = pango_context_get_metrics (context, font_desc,
6088                                        pango_context_get_language (context));
6089   font_size = PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) +
6090                             pango_font_metrics_get_descent (metrics));
6091   pango_font_metrics_unref (metrics);
6092
6093   arrow_size = MAX (arrow_size, font_size);
6094
6095   gtk_widget_set_size_request (priv->arrow, arrow_size, arrow_size);
6096
6097   if (!priv->tree_view)
6098     {
6099       /* menu mode */
6100           
6101       if (priv->cell_view)
6102         {
6103           gint sep_width, arrow_width;
6104           gint border_width, xthickness, xpad;
6105
6106           border_width = gtk_container_get_border_width (GTK_CONTAINER (combo_box));
6107           xthickness   = gtk_widget_get_style (priv->button)->xthickness;
6108
6109           gtk_size_request_get_width (GTK_SIZE_REQUEST (priv->separator), &sep_width, NULL);
6110           gtk_size_request_get_width (GTK_SIZE_REQUEST (priv->arrow), &arrow_width, NULL);
6111
6112           xpad = 2*(border_width + xthickness + focus_width + focus_pad);
6113
6114           minimum_width  = child_min + sep_width + arrow_width + xpad;
6115           natural_width  = child_nat + sep_width + arrow_width + xpad;
6116         }
6117       else
6118         {
6119           gint but_width, but_nat_width;
6120
6121           gtk_size_request_get_width (GTK_SIZE_REQUEST (priv->button), 
6122                                       &but_width, &but_nat_width);
6123
6124           minimum_width  = child_min + but_width;
6125           natural_width  = child_nat + but_nat_width;
6126         }
6127     }
6128   else
6129     {
6130       /* list mode */
6131       gint button_width, button_nat_width;
6132
6133       /* sample + frame */
6134       minimum_width = child_min;
6135       natural_width = child_nat;
6136
6137       minimum_width += 2 * focus_width;
6138       natural_width += 2 * focus_width;
6139       
6140       if (priv->cell_view_frame)
6141         {
6142           if (priv->has_frame)
6143             {
6144               gint border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->cell_view_frame));
6145               gint xpad         = 2 * (border_width + gtk_widget_get_style (GTK_WIDGET (priv->cell_view_frame))->xthickness);
6146
6147               minimum_width  += xpad;
6148               natural_width  += xpad;
6149             }
6150         }
6151
6152       /* the button */
6153       gtk_size_request_get_width (GTK_SIZE_REQUEST (priv->button), 
6154                                   &button_width, &button_nat_width);
6155
6156       minimum_width += button_width;
6157       natural_width += button_nat_width;
6158     }
6159
6160   if (GTK_SHADOW_NONE != priv->shadow_type)
6161     {
6162       style = gtk_widget_get_style (GTK_WIDGET (widget));
6163
6164       minimum_width  += 2 * style->xthickness;
6165       natural_width  += 2 * style->xthickness;
6166     }
6167
6168   if (minimum_size)
6169     *minimum_size = minimum_width;
6170
6171   if (natural_size)
6172     *natural_size = natural_width;
6173 }
6174
6175 static void
6176 gtk_combo_box_get_height (GtkSizeRequest      *widget,
6177                           gint                *minimum_size,
6178                           gint                *natural_size)
6179
6180   gint min_width;
6181
6182   /* Combo box is height-for-width only 
6183    * (so we always just reserve enough height for the minimum width) */
6184   GTK_SIZE_REQUEST_GET_IFACE (widget)->get_width (widget, &min_width, NULL);
6185   GTK_SIZE_REQUEST_GET_IFACE (widget)->get_height_for_width (widget, min_width, minimum_size, natural_size);
6186 }
6187
6188 static void
6189 gtk_combo_box_get_width_for_height (GtkSizeRequest        *widget,
6190                                     gint                   avail_size,
6191                                     gint                  *minimum_size,
6192                                     gint                  *natural_size)
6193 {
6194   /* Combo box is height-for-width only 
6195    * (so we assume we always reserved enough height for the minimum width) */
6196   GTK_SIZE_REQUEST_GET_IFACE (widget)->get_width (widget, minimum_size, natural_size);
6197 }
6198
6199
6200 static void
6201 gtk_combo_box_get_height_for_width (GtkSizeRequest        *widget,
6202                                     gint                   avail_size,
6203                                     gint                  *minimum_size,
6204                                     gint                  *natural_size)
6205 {
6206   GtkComboBox           *combo_box = GTK_COMBO_BOX (widget);
6207   GtkComboBoxPrivate    *priv = combo_box->priv;
6208   GtkStyle              *style;
6209   gint                   focus_width, focus_pad;
6210   gint                   min_height, nat_height;
6211   gint                   size;
6212
6213   gtk_widget_style_get (GTK_WIDGET (widget),
6214                         "focus-line-width", &focus_width,
6215                         "focus-padding", &focus_pad,
6216                         NULL);
6217
6218   size = avail_size;
6219
6220   if (GTK_SHADOW_NONE != priv->shadow_type)
6221     size -= gtk_widget_get_style (GTK_WIDGET (widget))->xthickness;
6222
6223   if (!priv->tree_view)
6224     {
6225       /* menu mode */
6226       if (priv->cell_view)
6227         {
6228           GtkStyle *button_style;
6229           /* calculate x/y padding and separator/arrow size */
6230           gint sep_width, arrow_width, sep_height, arrow_height;
6231           gint border_width, xthickness, ythickness, xpad, ypad;
6232
6233           border_width = gtk_container_get_border_width (GTK_CONTAINER (combo_box));
6234           button_style = gtk_widget_get_style (priv->button);
6235
6236           xthickness = button_style->xthickness;
6237           ythickness = button_style->ythickness;
6238
6239           gtk_size_request_get_width (GTK_SIZE_REQUEST (priv->separator), &sep_width, NULL);
6240           gtk_size_request_get_width (GTK_SIZE_REQUEST (priv->arrow), &arrow_width, NULL);
6241           gtk_size_request_get_height_for_width (GTK_SIZE_REQUEST (priv->separator), 
6242                                                  sep_width, &sep_height, NULL);
6243           gtk_size_request_get_height_for_width (GTK_SIZE_REQUEST (priv->arrow), 
6244                                                  arrow_width, &arrow_height, NULL);
6245
6246           xpad = 2*(border_width + xthickness + focus_width + focus_pad);
6247           ypad = 2*(border_width + ythickness + focus_width + focus_pad);
6248
6249           size -= sep_width + arrow_width + xpad;
6250
6251           gtk_combo_box_measure_height_for_width (combo_box, size, &min_height, &nat_height);
6252
6253           arrow_height = MAX (arrow_height, sep_height);
6254           min_height = MAX (min_height, arrow_height);
6255           nat_height = MAX (nat_height, arrow_height);
6256
6257           min_height += ypad;
6258           nat_height += ypad;
6259         }
6260       else
6261         {
6262           /* there is a custom child widget inside (no priv->cell_view) */
6263           gint but_width, but_height;
6264
6265           gtk_size_request_get_width (GTK_SIZE_REQUEST (priv->button), &but_width, NULL);
6266           gtk_size_request_get_height_for_width (GTK_SIZE_REQUEST (priv->button), 
6267                                                  but_width, &but_height, NULL);
6268
6269           size -= but_width;
6270
6271           gtk_combo_box_measure_height_for_width (combo_box, size, &min_height, &nat_height);
6272           
6273           min_height = MAX (min_height, but_height);
6274           nat_height = MAX (nat_height, but_height);
6275         }
6276     }
6277   else
6278     {
6279       /* list mode */
6280       gint but_width, but_height;
6281       gint xpad = 0, ypad = 0;
6282
6283       gtk_size_request_get_width (GTK_SIZE_REQUEST (priv->button), &but_width, NULL);
6284       gtk_size_request_get_height_for_width (GTK_SIZE_REQUEST (priv->button), 
6285                                              but_width, &but_height, NULL);
6286       
6287       if (priv->cell_view_frame && priv->has_frame)
6288         {
6289           GtkStyle *cell_style;
6290           gint border_width;
6291
6292           border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->cell_view_frame));
6293           cell_style = gtk_widget_get_style (GTK_WIDGET (priv->cell_view_frame));
6294
6295           xpad = 2 * (border_width + cell_style->xthickness);
6296           ypad = 2 * (border_width + cell_style->ythickness);
6297         }
6298
6299       size -= but_width;
6300       size -= 2 * focus_width;
6301       size -= xpad;
6302
6303       gtk_combo_box_measure_height_for_width (combo_box, size, &min_height, &nat_height);
6304           
6305       min_height = MAX (min_height, but_height);
6306       nat_height = MAX (nat_height, but_height);
6307
6308       min_height += ypad;
6309       nat_height += ypad;
6310     }
6311
6312   if (GTK_SHADOW_NONE != priv->shadow_type)
6313     {
6314       style = gtk_widget_get_style (GTK_WIDGET (widget));
6315
6316       min_height += 2 * style->ythickness;
6317       nat_height += 2 * style->ythickness;
6318     }
6319
6320   if (minimum_size)
6321     *minimum_size = min_height;
6322
6323   if (natural_size)
6324     *natural_size = nat_height;
6325 }