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