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