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