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