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