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