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