2 * Copyright (C) 2002, 2003 Kristian Rietveld <kris@gtk.org>
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.
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.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
20 #include "gtkcombobox.h"
22 #include "gtkadjustment.h"
23 #include "gtkcellareabox.h"
24 #include "gtktreemenu.h"
26 #include "gtkbindings.h"
27 #include "gtkcelllayout.h"
28 #include "gtkcellrenderertext.h"
29 #include "gtkcellview.h"
30 #include "gtkeventbox.h"
33 #include "gtkliststore.h"
35 #include "gtkmenuprivate.h"
36 #include "gtkmenushellprivate.h"
37 #include "gtkscrolledwindow.h"
38 #include "gtkseparatormenuitem.h"
39 #include "deprecated/gtktearoffmenuitem.h"
40 #include "gtktogglebutton.h"
41 #include "gtktreeselection.h"
42 #include "gtkseparator.h"
43 #include "gtkwindow.h"
44 #include "gtktypebuiltins.h"
45 #include "gtkprivate.h"
47 #include <gobject/gvaluecollector.h>
52 #include "gtkmarshalers.h"
55 #include "gtkentryprivate.h"
56 #include "gtktreeprivate.h"
57 #include "a11y/gtkcomboboxaccessible.h"
62 * @Short_description: A widget used to choose from a list of items
64 * @See_also: #GtkComboBoxText, #GtkTreeModel, #GtkCellRenderer
66 * A GtkComboBox is a widget that allows the user to choose from a list of
67 * valid choices. The GtkComboBox displays the selected choice. When
68 * activated, the GtkComboBox displays a popup which allows the user to
69 * make a new choice. The style in which the selected value is displayed,
70 * and the style of the popup is determined by the current theme. It may
71 * be similar to a Windows-style combo box.
73 * The GtkComboBox uses the model-view pattern; the list of valid choices
74 * is specified in the form of a tree model, and the display of the choices
75 * can be adapted to the data in the model by using cell renderers, as you
76 * would in a tree view. This is possible since GtkComboBox implements the
77 * #GtkCellLayout interface. The tree model holding the valid choices is
78 * not restricted to a flat list, it can be a real tree, and the popup will
79 * reflect the tree structure.
81 * To allow the user to enter values not in the model, the 'has-entry'
82 * property allows the GtkComboBox to contain a #GtkEntry. This entry
83 * can be accessed by calling gtk_bin_get_child() on the combo box.
85 * For a simple list of textual choices, the model-view API of GtkComboBox
86 * can be a bit overwhelming. In this case, #GtkComboBoxText offers a
87 * simple alternative. Both GtkComboBox and #GtkComboBoxText can contain
92 /* WELCOME, to THE house of evil code */
93 struct _GtkComboBoxPrivate
103 GtkShadowType shadow_type;
105 gint active; /* Only temporary */
106 GtkTreeRowReference *active_row;
108 GtkWidget *tree_view;
110 GtkWidget *cell_view;
111 GtkWidget *cell_view_frame;
116 GtkWidget *separator;
118 GtkWidget *popup_widget;
119 GtkWidget *popup_window;
120 GtkWidget *scrolled_window;
127 guint activate_button;
128 guint32 activate_time;
130 guint resize_idle_id;
132 /* For "has-entry" specific behavior we track
133 * an automated cell renderer and text column
136 GtkCellRenderer *text_renderer;
140 guint popup_in_progress : 1;
141 guint popup_shown : 1;
142 guint add_tearoffs : 1;
144 guint is_cell_renderer : 1;
145 guint editing_canceled : 1;
146 guint auto_scroll : 1;
147 guint focus_on_click : 1;
148 guint button_sensitivity : 2;
150 guint popup_fixed_width : 1;
152 GtkTreeViewRowSeparatorFunc row_separator_func;
153 gpointer row_separator_data;
154 GDestroyNotify row_separator_destroy;
156 GdkDevice *grab_pointer;
157 GdkDevice *grab_keyboard;
159 gchar *tearoff_title;
162 /* While debugging this evil code, I have learned that
163 * there are actually 4 modes to this widget, which can
164 * be characterized as follows
166 * 1) menu mode, no child added
169 * cell_view -> GtkCellView, regular child
170 * cell_view_frame -> NULL
171 * button -> GtkToggleButton set_parent to combo
172 * arrow -> GtkArrow set_parent to button
173 * separator -> GtkVSepator set_parent to button
174 * popup_widget -> GtkMenu
175 * popup_window -> NULL
176 * scrolled_window -> NULL
178 * 2) menu mode, child added
182 * cell_view_frame -> NULL
183 * button -> GtkToggleButton set_parent to combo
184 * arrow -> GtkArrow, child of button
186 * popup_widget -> GtkMenu
187 * popup_window -> NULL
188 * scrolled_window -> NULL
190 * 3) list mode, no child added
192 * tree_view -> GtkTreeView, child of scrolled_window
193 * cell_view -> GtkCellView, regular child
194 * cell_view_frame -> GtkFrame, set parent to combo
195 * button -> GtkToggleButton, set_parent to combo
196 * arrow -> GtkArrow, child of button
198 * popup_widget -> tree_view
199 * popup_window -> GtkWindow
200 * scrolled_window -> GtkScrolledWindow, child of popup_window
202 * 4) list mode, child added
204 * tree_view -> GtkTreeView, child of scrolled_window
206 * cell_view_frame -> NULL
207 * button -> GtkToggleButton, set_parent to combo
208 * arrow -> GtkArrow, child of button
210 * popup_widget -> tree_view
211 * popup_window -> GtkWindow
212 * scrolled_window -> GtkScrolledWindow, child of popup_window
229 PROP_ROW_SPAN_COLUMN,
230 PROP_COLUMN_SPAN_COLUMN,
237 PROP_BUTTON_SENSITIVITY,
238 PROP_EDITING_CANCELED,
240 PROP_ENTRY_TEXT_COLUMN,
241 PROP_POPUP_FIXED_WIDTH,
247 static guint combo_box_signals[LAST_SIGNAL] = {0,};
249 #define SCROLL_TIME 100
253 static void gtk_combo_box_cell_layout_init (GtkCellLayoutIface *iface);
254 static void gtk_combo_box_cell_editable_init (GtkCellEditableIface *iface);
255 static GObject *gtk_combo_box_constructor (GType type,
256 guint n_construct_properties,
257 GObjectConstructParam *construct_properties);
258 static void gtk_combo_box_dispose (GObject *object);
259 static void gtk_combo_box_finalize (GObject *object);
260 static void gtk_combo_box_destroy (GtkWidget *widget);
262 static void gtk_combo_box_set_property (GObject *object,
266 static void gtk_combo_box_get_property (GObject *object,
271 static void gtk_combo_box_state_flags_changed (GtkWidget *widget,
272 GtkStateFlags previous);
273 static void gtk_combo_box_grab_focus (GtkWidget *widget);
274 static void gtk_combo_box_style_updated (GtkWidget *widget);
275 static void gtk_combo_box_button_toggled (GtkWidget *widget,
277 static void gtk_combo_box_button_state_flags_changed (GtkWidget *widget,
278 GtkStateFlags previous,
280 static void gtk_combo_box_add (GtkContainer *container,
282 static void gtk_combo_box_remove (GtkContainer *container,
285 static void gtk_combo_box_menu_show (GtkWidget *menu,
287 static void gtk_combo_box_menu_hide (GtkWidget *menu,
290 static void gtk_combo_box_set_popup_widget (GtkComboBox *combo_box,
292 static void gtk_combo_box_menu_position_below (GtkMenu *menu,
297 static void gtk_combo_box_menu_position_over (GtkMenu *menu,
302 static void gtk_combo_box_menu_position (GtkMenu *menu,
308 static void gtk_combo_box_unset_model (GtkComboBox *combo_box);
310 static void gtk_combo_box_size_allocate (GtkWidget *widget,
311 GtkAllocation *allocation);
312 static void gtk_combo_box_forall (GtkContainer *container,
313 gboolean include_internals,
314 GtkCallback callback,
315 gpointer callback_data);
316 static gboolean gtk_combo_box_draw (GtkWidget *widget,
318 static gboolean gtk_combo_box_scroll_event (GtkWidget *widget,
319 GdkEventScroll *event);
320 static void gtk_combo_box_set_active_internal (GtkComboBox *combo_box,
323 static void gtk_combo_box_check_appearance (GtkComboBox *combo_box);
324 static void gtk_combo_box_real_move_active (GtkComboBox *combo_box,
325 GtkScrollType scroll);
326 static void gtk_combo_box_real_popup (GtkComboBox *combo_box);
327 static gboolean gtk_combo_box_real_popdown (GtkComboBox *combo_box);
329 /* listening to the model */
330 static void gtk_combo_box_model_row_inserted (GtkTreeModel *model,
334 static void gtk_combo_box_model_row_deleted (GtkTreeModel *model,
337 static void gtk_combo_box_model_rows_reordered (GtkTreeModel *model,
342 static void gtk_combo_box_model_row_changed (GtkTreeModel *model,
346 static void gtk_combo_box_model_row_expanded (GtkTreeModel *model,
352 static void gtk_combo_box_list_position (GtkComboBox *combo_box,
357 static void gtk_combo_box_list_setup (GtkComboBox *combo_box);
358 static void gtk_combo_box_list_destroy (GtkComboBox *combo_box);
360 static gboolean gtk_combo_box_list_button_released (GtkWidget *widget,
361 GdkEventButton *event,
363 static gboolean gtk_combo_box_list_key_press (GtkWidget *widget,
366 static gboolean gtk_combo_box_list_enter_notify (GtkWidget *widget,
367 GdkEventCrossing *event,
369 static void gtk_combo_box_list_auto_scroll (GtkComboBox *combo,
372 static gboolean gtk_combo_box_list_scroll_timeout (GtkComboBox *combo);
373 static gboolean gtk_combo_box_list_button_pressed (GtkWidget *widget,
374 GdkEventButton *event,
377 static gboolean gtk_combo_box_list_select_func (GtkTreeSelection *selection,
380 gboolean path_currently_selected,
383 static void gtk_combo_box_list_row_changed (GtkTreeModel *model,
387 static void gtk_combo_box_list_popup_resize (GtkComboBox *combo_box);
390 static void gtk_combo_box_menu_setup (GtkComboBox *combo_box,
391 gboolean add_children);
392 static void gtk_combo_box_update_title (GtkComboBox *combo_box);
393 static void gtk_combo_box_menu_destroy (GtkComboBox *combo_box);
396 static gboolean gtk_combo_box_menu_button_press (GtkWidget *widget,
397 GdkEventButton *event,
399 static void gtk_combo_box_menu_activate (GtkWidget *menu,
401 GtkComboBox *combo_box);
402 static void gtk_combo_box_update_sensitivity (GtkComboBox *combo_box);
403 static gboolean gtk_combo_box_menu_key_press (GtkWidget *widget,
406 static void gtk_combo_box_menu_popup (GtkComboBox *combo_box,
408 guint32 activate_time);
411 static GtkCellArea *gtk_combo_box_cell_layout_get_area (GtkCellLayout *cell_layout);
413 static gboolean gtk_combo_box_mnemonic_activate (GtkWidget *widget,
414 gboolean group_cycling);
416 static void gtk_combo_box_child_show (GtkWidget *widget,
417 GtkComboBox *combo_box);
418 static void gtk_combo_box_child_hide (GtkWidget *widget,
419 GtkComboBox *combo_box);
421 /* GtkComboBox:has-entry callbacks */
422 static void gtk_combo_box_entry_contents_changed (GtkEntry *entry,
424 static void gtk_combo_box_entry_active_changed (GtkComboBox *combo_box,
426 static gchar *gtk_combo_box_format_entry_text (GtkComboBox *combo_box,
429 /* GtkBuildable method implementation */
430 static GtkBuildableIface *parent_buildable_iface;
432 static void gtk_combo_box_buildable_init (GtkBuildableIface *iface);
433 static gboolean gtk_combo_box_buildable_custom_tag_start (GtkBuildable *buildable,
436 const gchar *tagname,
437 GMarkupParser *parser,
439 static void gtk_combo_box_buildable_custom_tag_end (GtkBuildable *buildable,
442 const gchar *tagname,
444 static GObject *gtk_combo_box_buildable_get_internal_child (GtkBuildable *buildable,
446 const gchar *childname);
449 /* GtkCellEditable method implementations */
450 static void gtk_combo_box_start_editing (GtkCellEditable *cell_editable,
453 static void gtk_combo_box_get_preferred_width (GtkWidget *widget,
456 static void gtk_combo_box_get_preferred_height (GtkWidget *widget,
459 static void gtk_combo_box_get_preferred_width_for_height (GtkWidget *widget,
463 static void gtk_combo_box_get_preferred_height_for_width (GtkWidget *widget,
467 static GtkWidgetPath *gtk_combo_box_get_path_for_child (GtkContainer *container,
469 static void gtk_combo_box_direction_changed (GtkWidget *widget,
470 GtkTextDirection previous_direction);
472 G_DEFINE_TYPE_WITH_CODE (GtkComboBox, gtk_combo_box, GTK_TYPE_BIN,
473 G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
474 gtk_combo_box_cell_layout_init)
475 G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_EDITABLE,
476 gtk_combo_box_cell_editable_init)
477 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
478 gtk_combo_box_buildable_init))
483 gtk_combo_box_class_init (GtkComboBoxClass *klass)
485 GObjectClass *object_class;
486 GtkContainerClass *container_class;
487 GtkWidgetClass *widget_class;
488 GtkBindingSet *binding_set;
490 container_class = (GtkContainerClass *)klass;
491 container_class->forall = gtk_combo_box_forall;
492 container_class->add = gtk_combo_box_add;
493 container_class->remove = gtk_combo_box_remove;
494 container_class->get_path_for_child = gtk_combo_box_get_path_for_child;
496 gtk_container_class_handle_border_width (container_class);
498 widget_class = (GtkWidgetClass *)klass;
499 widget_class->size_allocate = gtk_combo_box_size_allocate;
500 widget_class->draw = gtk_combo_box_draw;
501 widget_class->scroll_event = gtk_combo_box_scroll_event;
502 widget_class->mnemonic_activate = gtk_combo_box_mnemonic_activate;
503 widget_class->grab_focus = gtk_combo_box_grab_focus;
504 widget_class->style_updated = gtk_combo_box_style_updated;
505 widget_class->state_flags_changed = gtk_combo_box_state_flags_changed;
506 widget_class->get_preferred_width = gtk_combo_box_get_preferred_width;
507 widget_class->get_preferred_height = gtk_combo_box_get_preferred_height;
508 widget_class->get_preferred_height_for_width = gtk_combo_box_get_preferred_height_for_width;
509 widget_class->get_preferred_width_for_height = gtk_combo_box_get_preferred_width_for_height;
510 widget_class->destroy = gtk_combo_box_destroy;
511 widget_class->direction_changed = gtk_combo_box_direction_changed;
513 object_class = (GObjectClass *)klass;
514 object_class->constructor = gtk_combo_box_constructor;
515 object_class->dispose = gtk_combo_box_dispose;
516 object_class->finalize = gtk_combo_box_finalize;
517 object_class->set_property = gtk_combo_box_set_property;
518 object_class->get_property = gtk_combo_box_get_property;
520 klass->format_entry_text = gtk_combo_box_format_entry_text;
524 * GtkComboBox::changed:
525 * @widget: the object which received the signal
527 * The changed signal is emitted when the active
528 * item is changed. The can be due to the user selecting
529 * a different item from the list, or due to a
530 * call to gtk_combo_box_set_active_iter().
531 * It will also be emitted while typing into the entry of a combo box
536 combo_box_signals[CHANGED] =
537 g_signal_new (I_("changed"),
538 G_OBJECT_CLASS_TYPE (klass),
540 G_STRUCT_OFFSET (GtkComboBoxClass, changed),
542 g_cclosure_marshal_VOID__VOID,
545 * GtkComboBox::move-active:
546 * @widget: the object that received the signal
547 * @scroll_type: a #GtkScrollType
549 * The ::move-active signal is a
550 * <link linkend="keybinding-signals">keybinding signal</link>
551 * which gets emitted to move the active selection.
555 combo_box_signals[MOVE_ACTIVE] =
556 g_signal_new_class_handler (I_("move-active"),
557 G_OBJECT_CLASS_TYPE (klass),
558 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
559 G_CALLBACK (gtk_combo_box_real_move_active),
561 g_cclosure_marshal_VOID__ENUM,
563 GTK_TYPE_SCROLL_TYPE);
566 * GtkComboBox::popup:
567 * @widget: the object that received the signal
569 * The ::popup signal is a
570 * <link linkend="keybinding-signals">keybinding signal</link>
571 * which gets emitted to popup the combo box list.
573 * The default binding for this signal is Alt+Down.
577 combo_box_signals[POPUP] =
578 g_signal_new_class_handler (I_("popup"),
579 G_OBJECT_CLASS_TYPE (klass),
580 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
581 G_CALLBACK (gtk_combo_box_real_popup),
583 g_cclosure_marshal_VOID__VOID,
586 * GtkComboBox::popdown:
587 * @button: the object which received the signal
589 * The ::popdown signal is a
590 * <link linkend="keybinding-signals">keybinding signal</link>
591 * which gets emitted to popdown the combo box list.
593 * The default bindings for this signal are Alt+Up and Escape.
597 combo_box_signals[POPDOWN] =
598 g_signal_new_class_handler (I_("popdown"),
599 G_OBJECT_CLASS_TYPE (klass),
600 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
601 G_CALLBACK (gtk_combo_box_real_popdown),
603 _gtk_marshal_BOOLEAN__VOID,
607 * GtkComboBox::format-entry-text:
608 * @combo: the object which received the signal
609 * @path: the GtkTreePath string from the combo box's current model to format text for
611 * For combo boxes that are created with an entry (See GtkComboBox:has-entry).
613 * A signal which allows you to change how the text displayed in a combo box's
614 * entry is displayed.
616 * Connect a signal handler which returns an allocated string representing
617 * @path. That string will then be used to set the text in the combo box's entry.
618 * The default signal handler uses the text from the GtkComboBox::entry-text-column
621 * Here's an example signal handler which fetches data from the model and
622 * displays it in the entry.
625 * format_entry_text_callback (GtkComboBox *combo,
627 * gpointer user_data)
630 * GtkTreeModel model;
633 * model = gtk_combo_box_get_model (combo);
635 * gtk_tree_model_get_iter_from_string (model, &iter, path);
636 * gtk_tree_model_get (model, &iter,
637 * THE_DOUBLE_VALUE_COLUMN, &value,
640 * return g_strdup_printf ("%g", value);
644 * Return value: (transfer full): a newly allocated string representing @path
645 * for the current GtkComboBox model.
649 combo_box_signals[FORMAT_ENTRY_TEXT] =
650 g_signal_new (I_("format-entry-text"),
651 G_TYPE_FROM_CLASS (klass),
653 G_STRUCT_OFFSET (GtkComboBoxClass, format_entry_text),
654 _gtk_single_string_accumulator, NULL,
655 _gtk_marshal_STRING__STRING,
656 G_TYPE_STRING, 1, G_TYPE_STRING);
659 binding_set = gtk_binding_set_by_class (widget_class);
661 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Down, GDK_MOD1_MASK,
663 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Down, GDK_MOD1_MASK,
666 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Up, GDK_MOD1_MASK,
668 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Up, GDK_MOD1_MASK,
670 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0,
673 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Up, 0,
675 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_UP);
676 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Up, 0,
678 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_UP);
679 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Up, 0,
681 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_UP);
682 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Page_Up, 0,
684 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_UP);
685 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Home, 0,
687 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_START);
688 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Home, 0,
690 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_START);
692 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Down, 0,
694 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_DOWN);
695 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Down, 0,
697 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_DOWN);
698 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Down, 0,
700 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_DOWN);
701 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Page_Down, 0,
703 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_DOWN);
704 gtk_binding_entry_add_signal (binding_set, GDK_KEY_End, 0,
706 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_END);
707 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_End, 0,
709 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_END);
712 g_object_class_override_property (object_class,
713 PROP_EDITING_CANCELED,
719 * The model from which the combo box takes the values shown
724 g_object_class_install_property (object_class,
726 g_param_spec_object ("model",
727 P_("ComboBox model"),
728 P_("The model for the combo box"),
730 GTK_PARAM_READWRITE));
733 * GtkComboBox:wrap-width:
735 * If wrap-width is set to a positive value, the list will be
736 * displayed in multiple columns, the number of columns is
737 * determined by wrap-width.
741 g_object_class_install_property (object_class,
743 g_param_spec_int ("wrap-width",
745 P_("Wrap width for laying out the items in a grid"),
749 GTK_PARAM_READWRITE));
753 * GtkComboBox:row-span-column:
755 * If this is set to a non-negative value, it must be the index of a column
756 * of type %G_TYPE_INT in the model.
758 * The values of that column are used to determine how many rows a value in
759 * the list will span. Therefore, the values in the model column pointed to
760 * by this property must be greater than zero and not larger than wrap-width.
764 g_object_class_install_property (object_class,
765 PROP_ROW_SPAN_COLUMN,
766 g_param_spec_int ("row-span-column",
767 P_("Row span column"),
768 P_("TreeModel column containing the row span values"),
772 GTK_PARAM_READWRITE));
776 * GtkComboBox:column-span-column:
778 * If this is set to a non-negative value, it must be the index of a column
779 * of type %G_TYPE_INT in the model.
781 * The values of that column are used to determine how many columns a value
782 * in the list will span.
786 g_object_class_install_property (object_class,
787 PROP_COLUMN_SPAN_COLUMN,
788 g_param_spec_int ("column-span-column",
789 P_("Column span column"),
790 P_("TreeModel column containing the column span values"),
794 GTK_PARAM_READWRITE));
798 * GtkComboBox:active:
800 * The item which is currently active. If the model is a non-flat treemodel,
801 * and the active item is not an immediate child of the root of the tree,
802 * this property has the value
803 * <literal>gtk_tree_path_get_indices (path)[0]</literal>,
804 * where <literal>path</literal> is the #GtkTreePath of the active item.
808 g_object_class_install_property (object_class,
810 g_param_spec_int ("active",
812 P_("The item which is currently active"),
816 GTK_PARAM_READWRITE));
819 * GtkComboBox:add-tearoffs:
821 * The add-tearoffs property controls whether generated menus
822 * have tearoff menu items.
824 * Note that this only affects menu style combo boxes.
828 g_object_class_install_property (object_class,
830 g_param_spec_boolean ("add-tearoffs",
831 P_("Add tearoffs to menus"),
832 P_("Whether dropdowns should have a tearoff menu item"),
834 GTK_PARAM_READWRITE));
837 * GtkComboBox:has-frame:
839 * The has-frame property controls whether a frame
840 * is drawn around the entry.
844 g_object_class_install_property (object_class,
846 g_param_spec_boolean ("has-frame",
848 P_("Whether the combo box draws a frame around the child"),
850 GTK_PARAM_READWRITE));
852 g_object_class_install_property (object_class,
854 g_param_spec_boolean ("focus-on-click",
855 P_("Focus on click"),
856 P_("Whether the combo box grabs focus when it is clicked with the mouse"),
858 GTK_PARAM_READWRITE));
861 * GtkComboBox:tearoff-title:
863 * A title that may be displayed by the window manager
864 * when the popup is torn-off.
868 g_object_class_install_property (object_class,
870 g_param_spec_string ("tearoff-title",
872 P_("A title that may be displayed by the window manager when the popup is torn-off"),
874 GTK_PARAM_READWRITE));
878 * GtkComboBox:popup-shown:
880 * Whether the combo boxes dropdown is popped up.
881 * Note that this property is mainly useful, because
882 * it allows you to connect to notify::popup-shown.
886 g_object_class_install_property (object_class,
888 g_param_spec_boolean ("popup-shown",
890 P_("Whether the combo's dropdown is shown"),
892 GTK_PARAM_READABLE));
896 * GtkComboBox:button-sensitivity:
898 * Whether the dropdown button is sensitive when
899 * the model is empty.
903 g_object_class_install_property (object_class,
904 PROP_BUTTON_SENSITIVITY,
905 g_param_spec_enum ("button-sensitivity",
906 P_("Button Sensitivity"),
907 P_("Whether the dropdown button is sensitive when the model is empty"),
908 GTK_TYPE_SENSITIVITY_TYPE,
909 GTK_SENSITIVITY_AUTO,
910 GTK_PARAM_READWRITE));
913 * GtkComboBox:has-entry:
915 * Whether the combo box has an entry.
919 g_object_class_install_property (object_class,
921 g_param_spec_boolean ("has-entry",
923 P_("Whether combo box has an entry"),
925 GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
928 * GtkComboBox:entry-text-column:
930 * The column in the combo box's model to associate with strings from the entry
931 * if the combo was created with #GtkComboBox:has-entry = %TRUE.
935 g_object_class_install_property (object_class,
936 PROP_ENTRY_TEXT_COLUMN,
937 g_param_spec_int ("entry-text-column",
938 P_("Entry Text Column"),
939 P_("The column in the combo box's model to associate "
940 "with strings from the entry if the combo was "
941 "created with #GtkComboBox:has-entry = %TRUE"),
943 GTK_PARAM_READWRITE));
946 * GtkComboBox:id-column:
948 * The column in the combo box's model that provides string
949 * IDs for the values in the model, if != -1.
953 g_object_class_install_property (object_class,
955 g_param_spec_int ("id-column",
957 P_("The column in the combo box's model that provides "
958 "string IDs for the values in the model"),
960 GTK_PARAM_READWRITE));
963 * GtkComboBox:active-id:
965 * The value of the ID column of the active row.
969 g_object_class_install_property (object_class,
971 g_param_spec_string ("active-id",
973 P_("The value of the id column "
974 "for the active row"),
975 NULL, GTK_PARAM_READWRITE));
978 * GtkComboBox:popup-fixed-width:
980 * Whether the popup's width should be a fixed width matching the
981 * allocated width of the combo box.
985 g_object_class_install_property (object_class,
986 PROP_POPUP_FIXED_WIDTH,
987 g_param_spec_boolean ("popup-fixed-width",
988 P_("Popup Fixed Width"),
989 P_("Whether the popup's width should be a "
990 "fixed width matching the allocated width "
993 GTK_PARAM_READWRITE));
996 * GtkComboBox:cell-area:
998 * The #GtkCellArea used to layout cell renderers for this combo box.
1000 * If no area is specified when creating the combo box with gtk_combo_box_new_with_area()
1001 * a horizontally oriented #GtkCellAreaBox will be used.
1005 g_object_class_install_property (object_class,
1007 g_param_spec_object ("cell-area",
1009 P_("The GtkCellArea used to layout cells"),
1011 GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
1013 gtk_widget_class_install_style_property (widget_class,
1014 g_param_spec_boolean ("appears-as-list",
1015 P_("Appears as list"),
1016 P_("Whether dropdowns should look like lists rather than menus"),
1018 GTK_PARAM_READABLE));
1021 * GtkComboBox:arrow-size:
1023 * Sets the minimum size of the arrow in the combo box. Note
1024 * that the arrow size is coupled to the font size, so in case
1025 * a larger font is used, the arrow will be larger than set
1030 gtk_widget_class_install_style_property (widget_class,
1031 g_param_spec_int ("arrow-size",
1033 P_("The minimum size of the arrow in the combo box"),
1037 GTK_PARAM_READABLE));
1040 * GtkComboBox:arrow-scaling:
1042 * Sets the amount of space used up by the combobox arrow,
1043 * proportional to the font size.
1047 gtk_widget_class_install_style_property (widget_class,
1048 g_param_spec_float ("arrow-scaling",
1049 P_("Arrow Scaling"),
1050 P_("The amount of space used by the arrow"),
1054 GTK_PARAM_READABLE));
1057 * GtkComboBox:shadow-type:
1059 * Which kind of shadow to draw around the combo box.
1063 gtk_widget_class_install_style_property (widget_class,
1064 g_param_spec_enum ("shadow-type",
1066 P_("Which kind of shadow to draw around the combo box"),
1067 GTK_TYPE_SHADOW_TYPE,
1069 GTK_PARAM_READABLE));
1071 g_type_class_add_private (object_class, sizeof (GtkComboBoxPrivate));
1073 gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_COMBO_BOX_ACCESSIBLE);
1077 gtk_combo_box_buildable_init (GtkBuildableIface *iface)
1079 parent_buildable_iface = g_type_interface_peek_parent (iface);
1080 iface->add_child = _gtk_cell_layout_buildable_add_child;
1081 iface->custom_tag_start = gtk_combo_box_buildable_custom_tag_start;
1082 iface->custom_tag_end = gtk_combo_box_buildable_custom_tag_end;
1083 iface->get_internal_child = gtk_combo_box_buildable_get_internal_child;
1087 gtk_combo_box_cell_layout_init (GtkCellLayoutIface *iface)
1089 iface->get_area = gtk_combo_box_cell_layout_get_area;
1093 gtk_combo_box_cell_editable_init (GtkCellEditableIface *iface)
1095 iface->start_editing = gtk_combo_box_start_editing;
1099 gtk_combo_box_init (GtkComboBox *combo_box)
1101 GtkComboBoxPrivate *priv;
1103 combo_box->priv = G_TYPE_INSTANCE_GET_PRIVATE (combo_box,
1105 GtkComboBoxPrivate);
1106 priv = combo_box->priv;
1108 priv->wrap_width = 0;
1111 priv->active_row = NULL;
1112 priv->col_column = -1;
1113 priv->row_column = -1;
1115 priv->popup_shown = FALSE;
1116 priv->add_tearoffs = FALSE;
1117 priv->has_frame = TRUE;
1118 priv->is_cell_renderer = FALSE;
1119 priv->editing_canceled = FALSE;
1120 priv->auto_scroll = FALSE;
1121 priv->focus_on_click = TRUE;
1122 priv->button_sensitivity = GTK_SENSITIVITY_AUTO;
1123 priv->has_entry = FALSE;
1124 priv->popup_fixed_width = TRUE;
1126 priv->text_column = -1;
1127 priv->text_renderer = NULL;
1128 priv->id_column = -1;
1132 gtk_combo_box_set_property (GObject *object,
1134 const GValue *value,
1137 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
1138 GtkComboBoxPrivate *priv = combo_box->priv;
1144 gtk_combo_box_set_model (combo_box, g_value_get_object (value));
1147 case PROP_WRAP_WIDTH:
1148 gtk_combo_box_set_wrap_width (combo_box, g_value_get_int (value));
1151 case PROP_ROW_SPAN_COLUMN:
1152 gtk_combo_box_set_row_span_column (combo_box, g_value_get_int (value));
1155 case PROP_COLUMN_SPAN_COLUMN:
1156 gtk_combo_box_set_column_span_column (combo_box, g_value_get_int (value));
1160 gtk_combo_box_set_active (combo_box, g_value_get_int (value));
1163 case PROP_ADD_TEAROFFS:
1164 gtk_combo_box_set_add_tearoffs (combo_box, g_value_get_boolean (value));
1167 case PROP_HAS_FRAME:
1168 priv->has_frame = g_value_get_boolean (value);
1170 if (priv->has_entry)
1174 child = gtk_bin_get_child (GTK_BIN (combo_box));
1176 gtk_entry_set_has_frame (GTK_ENTRY (child), priv->has_frame);
1181 case PROP_FOCUS_ON_CLICK:
1182 gtk_combo_box_set_focus_on_click (combo_box,
1183 g_value_get_boolean (value));
1186 case PROP_TEAROFF_TITLE:
1187 gtk_combo_box_set_title (combo_box, g_value_get_string (value));
1190 case PROP_POPUP_SHOWN:
1191 if (g_value_get_boolean (value))
1192 gtk_combo_box_popup (combo_box);
1194 gtk_combo_box_popdown (combo_box);
1197 case PROP_BUTTON_SENSITIVITY:
1198 gtk_combo_box_set_button_sensitivity (combo_box,
1199 g_value_get_enum (value));
1202 case PROP_POPUP_FIXED_WIDTH:
1203 gtk_combo_box_set_popup_fixed_width (combo_box,
1204 g_value_get_boolean (value));
1207 case PROP_EDITING_CANCELED:
1208 priv->editing_canceled = g_value_get_boolean (value);
1211 case PROP_HAS_ENTRY:
1212 priv->has_entry = g_value_get_boolean (value);
1215 case PROP_ENTRY_TEXT_COLUMN:
1216 gtk_combo_box_set_entry_text_column (combo_box, g_value_get_int (value));
1219 case PROP_ID_COLUMN:
1220 gtk_combo_box_set_id_column (combo_box, g_value_get_int (value));
1223 case PROP_ACTIVE_ID:
1224 gtk_combo_box_set_active_id (combo_box, g_value_get_string (value));
1227 case PROP_CELL_AREA:
1228 /* Construct-only, can only be assigned once */
1229 area = g_value_get_object (value);
1232 if (priv->area != NULL)
1234 g_warning ("cell-area has already been set, ignoring construct property");
1235 g_object_ref_sink (area);
1236 g_object_unref (area);
1239 priv->area = g_object_ref_sink (area);
1244 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1250 gtk_combo_box_get_property (GObject *object,
1255 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
1256 GtkComboBoxPrivate *priv = combo_box->priv;
1261 g_value_set_object (value, combo_box->priv->model);
1264 case PROP_WRAP_WIDTH:
1265 g_value_set_int (value, combo_box->priv->wrap_width);
1268 case PROP_ROW_SPAN_COLUMN:
1269 g_value_set_int (value, combo_box->priv->row_column);
1272 case PROP_COLUMN_SPAN_COLUMN:
1273 g_value_set_int (value, combo_box->priv->col_column);
1277 g_value_set_int (value, gtk_combo_box_get_active (combo_box));
1280 case PROP_ADD_TEAROFFS:
1281 g_value_set_boolean (value, gtk_combo_box_get_add_tearoffs (combo_box));
1284 case PROP_HAS_FRAME:
1285 g_value_set_boolean (value, combo_box->priv->has_frame);
1288 case PROP_FOCUS_ON_CLICK:
1289 g_value_set_boolean (value, combo_box->priv->focus_on_click);
1292 case PROP_TEAROFF_TITLE:
1293 g_value_set_string (value, gtk_combo_box_get_title (combo_box));
1296 case PROP_POPUP_SHOWN:
1297 g_value_set_boolean (value, combo_box->priv->popup_shown);
1300 case PROP_BUTTON_SENSITIVITY:
1301 g_value_set_enum (value, combo_box->priv->button_sensitivity);
1304 case PROP_POPUP_FIXED_WIDTH:
1305 g_value_set_boolean (value, combo_box->priv->popup_fixed_width);
1308 case PROP_EDITING_CANCELED:
1309 g_value_set_boolean (value, priv->editing_canceled);
1312 case PROP_HAS_ENTRY:
1313 g_value_set_boolean (value, priv->has_entry);
1316 case PROP_ENTRY_TEXT_COLUMN:
1317 g_value_set_int (value, priv->text_column);
1320 case PROP_ID_COLUMN:
1321 g_value_set_int (value, priv->id_column);
1324 case PROP_ACTIVE_ID:
1325 g_value_set_string (value, gtk_combo_box_get_active_id (combo_box));
1328 case PROP_CELL_AREA:
1329 g_value_set_object (value, priv->area);
1333 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1339 gtk_combo_box_state_flags_changed (GtkWidget *widget,
1340 GtkStateFlags previous)
1342 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1343 GtkComboBoxPrivate *priv = combo_box->priv;
1345 if (gtk_widget_get_realized (widget))
1347 if (priv->tree_view && priv->cell_view)
1349 GtkStyleContext *context;
1350 GtkStateFlags state;
1353 context = gtk_widget_get_style_context (widget);
1354 state = gtk_widget_get_state_flags (widget);
1355 gtk_style_context_get_background_color (context, state, &color);
1357 gtk_cell_view_set_background_rgba (GTK_CELL_VIEW (priv->cell_view), &color);
1361 gtk_widget_queue_draw (widget);
1365 gtk_combo_box_button_state_flags_changed (GtkWidget *widget,
1366 GtkStateFlags previous,
1369 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
1370 GtkComboBoxPrivate *priv = combo_box->priv;
1372 if (gtk_widget_get_realized (widget))
1374 if (!priv->tree_view && priv->cell_view)
1375 gtk_widget_set_state_flags (priv->cell_view,
1376 gtk_widget_get_state_flags (widget),
1380 gtk_widget_queue_draw (widget);
1384 gtk_combo_box_invalidate_order (GtkComboBox *combo_box)
1386 gtk_container_forall (GTK_CONTAINER (combo_box),
1387 (GtkCallback) gtk_widget_reset_style,
1392 gtk_combo_box_direction_changed (GtkWidget *widget,
1393 GtkTextDirection previous_direction)
1395 gtk_combo_box_invalidate_order (GTK_COMBO_BOX (widget));
1398 static GtkWidgetPath *
1399 gtk_combo_box_get_path_for_child (GtkContainer *container,
1402 GtkComboBoxPrivate *priv = GTK_COMBO_BOX (container)->priv;
1403 GtkWidgetPath *path;
1405 gboolean found = FALSE;
1406 GList *visible_children, *l;
1407 GtkWidgetPath *sibling_path;
1410 path = gtk_widget_path_copy (gtk_widget_get_path (GTK_WIDGET (container)));
1412 if (gtk_widget_get_visible (child))
1414 visible_children = NULL;
1416 if (priv->button && gtk_widget_get_visible (priv->button))
1417 visible_children = g_list_prepend (visible_children, priv->button);
1419 if (priv->cell_view_frame && gtk_widget_get_visible (priv->cell_view_frame))
1420 visible_children = g_list_prepend (visible_children, priv->cell_view_frame);
1422 widget = gtk_bin_get_child (GTK_BIN (container));
1423 if (widget && gtk_widget_get_visible (widget))
1424 visible_children = g_list_prepend (visible_children, widget);
1426 if (gtk_widget_get_direction (GTK_WIDGET (container)) == GTK_TEXT_DIR_RTL)
1427 visible_children = g_list_reverse (visible_children);
1431 for (l = visible_children; l; l = l->next)
1435 if (widget == child)
1447 sibling_path = gtk_widget_path_new ();
1449 for (l = visible_children; l; l = l->next)
1450 gtk_widget_path_append_for_widget (sibling_path, l->data);
1452 gtk_widget_path_append_with_siblings (path, sibling_path, pos);
1454 g_list_free (visible_children);
1455 gtk_widget_path_unref (sibling_path);
1459 gtk_widget_path_append_for_widget (path, child);
1466 gtk_combo_box_check_appearance (GtkComboBox *combo_box)
1468 GtkComboBoxPrivate *priv = combo_box->priv;
1469 gboolean appears_as_list;
1471 /* if wrap_width > 0, then we are in grid-mode and forced to use
1474 if (priv->wrap_width)
1475 appears_as_list = FALSE;
1477 gtk_widget_style_get (GTK_WIDGET (combo_box),
1478 "appears-as-list", &appears_as_list,
1481 if (appears_as_list)
1483 /* Destroy all the menu mode widgets, if they exist. */
1484 if (GTK_IS_MENU (priv->popup_widget))
1485 gtk_combo_box_menu_destroy (combo_box);
1487 /* Create the list mode widgets, if they don't already exist. */
1488 if (!GTK_IS_TREE_VIEW (priv->tree_view))
1489 gtk_combo_box_list_setup (combo_box);
1493 /* Destroy all the list mode widgets, if they exist. */
1494 if (GTK_IS_TREE_VIEW (priv->tree_view))
1495 gtk_combo_box_list_destroy (combo_box);
1497 /* Create the menu mode widgets, if they don't already exist. */
1498 if (!GTK_IS_MENU (priv->popup_widget))
1499 gtk_combo_box_menu_setup (combo_box, TRUE);
1502 gtk_widget_style_get (GTK_WIDGET (combo_box),
1503 "shadow-type", &priv->shadow_type,
1508 gtk_combo_box_style_updated (GtkWidget *widget)
1510 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1511 GtkComboBoxPrivate *priv = combo_box->priv;
1514 GTK_WIDGET_CLASS (gtk_combo_box_parent_class)->style_updated (widget);
1516 gtk_combo_box_check_appearance (combo_box);
1518 if (priv->tree_view && priv->cell_view)
1520 GtkStyleContext *context;
1521 GtkStateFlags state;
1524 context = gtk_widget_get_style_context (widget);
1525 state = gtk_widget_get_state_flags (widget);
1526 gtk_style_context_get_background_color (context, state, &color);
1528 gtk_cell_view_set_background_rgba (GTK_CELL_VIEW (priv->cell_view), &color);
1531 child = gtk_bin_get_child (GTK_BIN (combo_box));
1532 if (GTK_IS_ENTRY (child))
1533 g_object_set (child, "shadow-type",
1534 GTK_SHADOW_NONE == priv->shadow_type ?
1535 GTK_SHADOW_IN : GTK_SHADOW_NONE, NULL);
1539 gtk_combo_box_button_toggled (GtkWidget *widget,
1542 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
1544 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
1546 if (!combo_box->priv->popup_in_progress)
1547 gtk_combo_box_popup (combo_box);
1550 gtk_combo_box_popdown (combo_box);
1554 gtk_combo_box_add (GtkContainer *container,
1557 GtkComboBox *combo_box = GTK_COMBO_BOX (container);
1558 GtkComboBoxPrivate *priv = combo_box->priv;
1560 if (priv->has_entry && !GTK_IS_ENTRY (widget))
1562 g_warning ("Attempting to add a widget with type %s to a GtkComboBox that needs an entry "
1563 "(need an instance of GtkEntry or of a subclass)",
1564 G_OBJECT_TYPE_NAME (widget));
1568 if (priv->cell_view &&
1569 gtk_widget_get_parent (priv->cell_view))
1571 gtk_widget_unparent (priv->cell_view);
1572 _gtk_bin_set_child (GTK_BIN (container), NULL);
1573 gtk_widget_queue_resize (GTK_WIDGET (container));
1576 gtk_widget_set_parent (widget, GTK_WIDGET (container));
1577 _gtk_bin_set_child (GTK_BIN (container), widget);
1579 if (priv->cell_view &&
1580 widget != priv->cell_view)
1582 /* since the cell_view was unparented, it's gone now */
1583 priv->cell_view = NULL;
1585 if (!priv->tree_view && priv->separator)
1587 gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (priv->separator)),
1589 priv->separator = NULL;
1591 gtk_widget_queue_resize (GTK_WIDGET (container));
1593 else if (priv->cell_view_frame)
1595 gtk_widget_unparent (priv->cell_view_frame);
1596 priv->cell_view_frame = NULL;
1601 if (priv->has_entry)
1603 /* this flag is a hack to tell the entry to fill its allocation.
1605 _gtk_entry_set_is_cell_renderer (GTK_ENTRY (widget), TRUE);
1607 g_signal_connect (widget, "changed",
1608 G_CALLBACK (gtk_combo_box_entry_contents_changed),
1611 gtk_entry_set_has_frame (GTK_ENTRY (widget), priv->has_frame);
1616 gtk_combo_box_remove (GtkContainer *container,
1619 GtkComboBox *combo_box = GTK_COMBO_BOX (container);
1620 GtkComboBoxPrivate *priv = combo_box->priv;
1622 gboolean appears_as_list;
1624 if (priv->has_entry)
1626 GtkWidget *child_widget;
1628 child_widget = gtk_bin_get_child (GTK_BIN (container));
1629 if (widget && widget == child_widget)
1631 g_signal_handlers_disconnect_by_func (widget,
1632 gtk_combo_box_entry_contents_changed,
1634 _gtk_entry_set_is_cell_renderer (GTK_ENTRY (widget), FALSE);
1638 if (widget == priv->cell_view)
1639 priv->cell_view = NULL;
1641 gtk_widget_unparent (widget);
1642 _gtk_bin_set_child (GTK_BIN (container), NULL);
1644 if (gtk_widget_in_destruction (GTK_WIDGET (combo_box)))
1647 gtk_widget_queue_resize (GTK_WIDGET (container));
1649 if (!priv->tree_view)
1650 appears_as_list = FALSE;
1652 appears_as_list = TRUE;
1654 if (appears_as_list)
1655 gtk_combo_box_list_destroy (combo_box);
1656 else if (GTK_IS_MENU (priv->popup_widget))
1658 gtk_combo_box_menu_destroy (combo_box);
1659 gtk_menu_detach (GTK_MENU (priv->popup_widget));
1660 priv->popup_widget = NULL;
1663 if (!priv->cell_view)
1665 priv->cell_view = gtk_cell_view_new ();
1666 gtk_widget_set_parent (priv->cell_view, GTK_WIDGET (container));
1667 _gtk_bin_set_child (GTK_BIN (container), priv->cell_view);
1669 gtk_widget_show (priv->cell_view);
1670 gtk_cell_view_set_model (GTK_CELL_VIEW (priv->cell_view),
1675 if (appears_as_list)
1676 gtk_combo_box_list_setup (combo_box);
1678 gtk_combo_box_menu_setup (combo_box, TRUE);
1680 if (gtk_tree_row_reference_valid (priv->active_row))
1682 path = gtk_tree_row_reference_get_path (priv->active_row);
1683 gtk_combo_box_set_active_internal (combo_box, path);
1684 gtk_tree_path_free (path);
1687 gtk_combo_box_set_active_internal (combo_box, NULL);
1691 gtk_combo_box_menu_show (GtkWidget *menu,
1694 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1695 GtkComboBoxPrivate *priv = combo_box->priv;
1697 gtk_combo_box_child_show (menu, user_data);
1699 priv->popup_in_progress = TRUE;
1700 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button),
1702 priv->popup_in_progress = FALSE;
1706 gtk_combo_box_menu_hide (GtkWidget *menu,
1709 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1711 gtk_combo_box_child_hide (menu,user_data);
1713 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
1718 gtk_combo_box_detacher (GtkWidget *widget,
1721 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1722 GtkComboBoxPrivate *priv = combo_box->priv;
1724 g_return_if_fail (priv->popup_widget == (GtkWidget *) menu);
1726 g_signal_handlers_disconnect_by_func (menu->priv->toplevel,
1727 gtk_combo_box_menu_show,
1729 g_signal_handlers_disconnect_by_func (menu->priv->toplevel,
1730 gtk_combo_box_menu_hide,
1733 priv->popup_widget = NULL;
1737 gtk_combo_box_set_popup_widget (GtkComboBox *combo_box,
1740 GtkComboBoxPrivate *priv = combo_box->priv;
1742 if (GTK_IS_MENU (priv->popup_widget))
1744 gtk_menu_detach (GTK_MENU (priv->popup_widget));
1745 priv->popup_widget = NULL;
1747 else if (priv->popup_widget)
1749 gtk_container_remove (GTK_CONTAINER (priv->scrolled_window),
1750 priv->popup_widget);
1751 g_object_unref (priv->popup_widget);
1752 priv->popup_widget = NULL;
1755 if (GTK_IS_MENU (popup))
1757 if (priv->popup_window)
1759 gtk_widget_destroy (priv->popup_window);
1760 priv->popup_window = NULL;
1763 priv->popup_widget = popup;
1766 * Note that we connect to show/hide on the toplevel, not the
1767 * menu itself, since the menu is not shown/hidden when it is
1768 * popped up while torn-off.
1770 g_signal_connect (GTK_MENU (popup)->priv->toplevel, "show",
1771 G_CALLBACK (gtk_combo_box_menu_show), combo_box);
1772 g_signal_connect (GTK_MENU (popup)->priv->toplevel, "hide",
1773 G_CALLBACK (gtk_combo_box_menu_hide), combo_box);
1775 gtk_menu_attach_to_widget (GTK_MENU (popup),
1776 GTK_WIDGET (combo_box),
1777 gtk_combo_box_detacher);
1781 if (!priv->popup_window)
1783 GtkWidget *toplevel;
1785 priv->popup_window = gtk_window_new (GTK_WINDOW_POPUP);
1786 gtk_widget_set_name (priv->popup_window, "gtk-combobox-popup-window");
1788 gtk_window_set_type_hint (GTK_WINDOW (priv->popup_window),
1789 GDK_WINDOW_TYPE_HINT_COMBO);
1791 g_signal_connect (GTK_WINDOW (priv->popup_window),"show",
1792 G_CALLBACK (gtk_combo_box_child_show),
1794 g_signal_connect (GTK_WINDOW (priv->popup_window),"hide",
1795 G_CALLBACK (gtk_combo_box_child_hide),
1798 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (combo_box));
1799 if (GTK_IS_WINDOW (toplevel))
1801 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
1802 GTK_WINDOW (priv->popup_window));
1803 gtk_window_set_transient_for (GTK_WINDOW (priv->popup_window),
1804 GTK_WINDOW (toplevel));
1807 gtk_window_set_resizable (GTK_WINDOW (priv->popup_window), FALSE);
1808 gtk_window_set_screen (GTK_WINDOW (priv->popup_window),
1809 gtk_widget_get_screen (GTK_WIDGET (combo_box)));
1811 priv->scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1813 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
1816 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->scrolled_window),
1819 gtk_widget_show (priv->scrolled_window);
1821 gtk_container_add (GTK_CONTAINER (priv->popup_window),
1822 priv->scrolled_window);
1825 gtk_container_add (GTK_CONTAINER (priv->scrolled_window),
1828 gtk_widget_show (popup);
1829 g_object_ref (popup);
1830 priv->popup_widget = popup;
1835 get_widget_padding_and_border (GtkWidget *widget,
1838 GtkStyleContext *context;
1839 GtkStateFlags state;
1842 context = gtk_widget_get_style_context (widget);
1843 state = gtk_style_context_get_state (context);
1845 gtk_style_context_get_padding (context, state, padding);
1846 gtk_style_context_get_border (context, state, &tmp);
1848 padding->top += tmp.top;
1849 padding->right += tmp.right;
1850 padding->bottom += tmp.bottom;
1851 padding->left += tmp.left;
1855 gtk_combo_box_menu_position_below (GtkMenu *menu,
1861 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1862 GtkAllocation child_allocation;
1868 GdkRectangle monitor;
1871 /* FIXME: is using the size request here broken? */
1872 child = gtk_bin_get_child (GTK_BIN (combo_box));
1876 gtk_widget_get_allocation (child, &child_allocation);
1878 if (!gtk_widget_get_has_window (child))
1880 sx += child_allocation.x;
1881 sy += child_allocation.y;
1884 gdk_window_get_root_coords (gtk_widget_get_window (child),
1886 get_widget_padding_and_border (GTK_WIDGET (combo_box), &padding);
1888 if (gtk_widget_get_direction (GTK_WIDGET (combo_box)) == GTK_TEXT_DIR_RTL)
1893 if (combo_box->priv->popup_fixed_width)
1894 gtk_widget_get_preferred_size (GTK_WIDGET (menu), &req, NULL);
1896 gtk_widget_get_preferred_size (GTK_WIDGET (menu), NULL, &req);
1898 if (gtk_widget_get_direction (GTK_WIDGET (combo_box)) == GTK_TEXT_DIR_LTR)
1901 *x = sx + child_allocation.width - req.width;
1904 screen = gtk_widget_get_screen (GTK_WIDGET (combo_box));
1905 monitor_num = gdk_screen_get_monitor_at_window (screen,
1906 gtk_widget_get_window (GTK_WIDGET (combo_box)));
1907 gdk_screen_get_monitor_workarea (screen, monitor_num, &monitor);
1911 else if (*x + req.width > monitor.x + monitor.width)
1912 *x = monitor.x + monitor.width - req.width;
1914 if (monitor.y + monitor.height - *y - child_allocation.height >= req.height)
1915 *y += child_allocation.height;
1916 else if (*y - monitor.y >= req.height)
1918 else if (monitor.y + monitor.height - *y - child_allocation.height > *y - monitor.y)
1919 *y += child_allocation.height;
1927 gtk_combo_box_menu_position_over (GtkMenu *menu,
1933 GtkComboBox *combo_box;
1937 GtkAllocation allocation;
1938 GtkAllocation child_allocation;
1945 combo_box = GTK_COMBO_BOX (user_data);
1946 widget = GTK_WIDGET (combo_box);
1948 active = gtk_menu_get_active (GTK_MENU (combo_box->priv->popup_widget));
1950 gtk_widget_get_allocation (widget, &allocation);
1952 menu_xpos = allocation.x;
1953 menu_ypos = allocation.y + allocation.height / 2 - 2;
1955 if (combo_box->priv->popup_fixed_width)
1956 gtk_widget_get_preferred_width (GTK_WIDGET (menu), &menu_width, NULL);
1958 gtk_widget_get_preferred_width (GTK_WIDGET (menu), NULL, &menu_width);
1962 gtk_widget_get_allocation (active, &child_allocation);
1963 menu_ypos -= child_allocation.height / 2;
1966 children = GTK_MENU_SHELL (combo_box->priv->popup_widget)->priv->children;
1969 child = children->data;
1971 if (active == child)
1974 if (gtk_widget_get_visible (child))
1976 gtk_widget_get_allocation (child, &child_allocation);
1978 menu_ypos -= child_allocation.height;
1981 children = children->next;
1984 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
1985 menu_xpos = menu_xpos + allocation.width - menu_width;
1987 gdk_window_get_root_coords (gtk_widget_get_window (widget),
1988 menu_xpos, menu_ypos,
1989 &menu_xpos, &menu_ypos);
1991 /* Clamp the position on screen */
1992 screen_width = gdk_screen_get_width (gtk_widget_get_screen (widget));
1996 else if ((menu_xpos + menu_width) > screen_width)
1997 menu_xpos -= ((menu_xpos + menu_width) - screen_width);
2006 gtk_combo_box_menu_position (GtkMenu *menu,
2012 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
2013 GtkComboBoxPrivate *priv = combo_box->priv;
2014 GtkWidget *menu_item;
2016 if (priv->wrap_width > 0 || priv->cell_view == NULL)
2017 gtk_combo_box_menu_position_below (menu, x, y, push_in, user_data);
2020 /* FIXME handle nested menus better */
2021 menu_item = gtk_menu_get_active (GTK_MENU (priv->popup_widget));
2023 gtk_menu_shell_select_item (GTK_MENU_SHELL (priv->popup_widget),
2026 gtk_combo_box_menu_position_over (menu, x, y, push_in, user_data);
2029 if (!gtk_widget_get_visible (GTK_MENU (priv->popup_widget)->priv->toplevel))
2030 gtk_window_set_type_hint (GTK_WINDOW (GTK_MENU (priv->popup_widget)->priv->toplevel),
2031 GDK_WINDOW_TYPE_HINT_COMBO);
2035 gtk_combo_box_list_position (GtkComboBox *combo_box,
2041 GtkComboBoxPrivate *priv = combo_box->priv;
2042 GtkAllocation allocation;
2045 GdkRectangle monitor;
2046 GtkRequisition popup_req;
2047 GtkPolicyType hpolicy, vpolicy;
2050 /* under windows, the drop down list is as wide as the combo box itself.
2052 GtkWidget *widget = GTK_WIDGET (combo_box);
2056 gtk_widget_get_allocation (widget, &allocation);
2058 if (!gtk_widget_get_has_window (widget))
2064 window = gtk_widget_get_window (widget);
2066 gdk_window_get_root_coords (gtk_widget_get_window (widget),
2069 *width = allocation.width;
2071 hpolicy = vpolicy = GTK_POLICY_NEVER;
2072 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
2075 if (combo_box->priv->popup_fixed_width)
2077 gtk_widget_get_preferred_size (priv->scrolled_window, &popup_req, NULL);
2079 if (popup_req.width > *width)
2081 hpolicy = GTK_POLICY_ALWAYS;
2082 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
2088 /* XXX This code depends on treeviews properly reporting their natural width
2089 * list-mode menus won't fill up to their natural width until then */
2090 gtk_widget_get_preferred_size (priv->scrolled_window, NULL, &popup_req);
2092 if (popup_req.width > *width)
2094 hpolicy = GTK_POLICY_NEVER;
2095 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
2098 *width = popup_req.width;
2102 *height = popup_req.height;
2104 screen = gtk_widget_get_screen (GTK_WIDGET (combo_box));
2105 monitor_num = gdk_screen_get_monitor_at_window (screen, window);
2106 gdk_screen_get_monitor_workarea (screen, monitor_num, &monitor);
2108 if (gtk_widget_get_direction (GTK_WIDGET (combo_box)) == GTK_TEXT_DIR_RTL)
2109 *x = *x + allocation.width - *width;
2113 else if (*x + *width > monitor.x + monitor.width)
2114 *x = monitor.x + monitor.width - *width;
2116 if (*y + allocation.height + *height <= monitor.y + monitor.height)
2117 *y += allocation.height;
2118 else if (*y - *height >= monitor.y)
2120 else if (monitor.y + monitor.height - (*y + allocation.height) > *y - monitor.y)
2122 *y += allocation.height;
2123 *height = monitor.y + monitor.height - *y;
2127 *height = *y - monitor.y;
2131 if (popup_req.height > *height)
2133 vpolicy = GTK_POLICY_ALWAYS;
2135 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
2141 cell_layout_is_sensitive (GtkCellLayout *layout)
2143 GList *cells, *list;
2146 cells = gtk_cell_layout_get_cells (layout);
2149 for (list = cells; list; list = list->next)
2151 g_object_get (list->data, "sensitive", &sensitive, NULL);
2156 g_list_free (cells);
2162 cell_is_sensitive (GtkCellRenderer *cell,
2165 gboolean *sensitive = data;
2167 g_object_get (cell, "sensitive", sensitive, NULL);
2173 tree_column_row_is_sensitive (GtkComboBox *combo_box,
2176 GtkComboBoxPrivate *priv = combo_box->priv;
2178 if (priv->row_separator_func)
2180 if (priv->row_separator_func (priv->model, iter,
2181 priv->row_separator_data))
2189 gtk_cell_area_apply_attributes (priv->area, priv->model, iter, FALSE, FALSE);
2193 gtk_cell_area_foreach (priv->area, cell_is_sensitive, &sensitive);
2202 update_menu_sensitivity (GtkComboBox *combo_box,
2205 GtkComboBoxPrivate *priv = combo_box->priv;
2206 GList *children, *child;
2207 GtkWidget *item, *submenu, *separator;
2208 GtkWidget *cell_view;
2214 children = gtk_container_get_children (GTK_CONTAINER (menu));
2216 for (child = children; child; child = child->next)
2218 item = GTK_WIDGET (child->data);
2219 cell_view = gtk_bin_get_child (GTK_BIN (item));
2221 if (!GTK_IS_CELL_VIEW (cell_view))
2224 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (item));
2225 if (submenu != NULL)
2227 gtk_widget_set_sensitive (item, TRUE);
2228 update_menu_sensitivity (combo_box, submenu);
2232 sensitive = cell_layout_is_sensitive (GTK_CELL_LAYOUT (cell_view));
2234 if (menu != priv->popup_widget && child == children)
2236 separator = GTK_WIDGET (child->next->data);
2237 g_object_set (item, "visible", sensitive, NULL);
2238 g_object_set (separator, "visible", sensitive, NULL);
2241 gtk_widget_set_sensitive (item, sensitive);
2245 g_list_free (children);
2249 gtk_combo_box_menu_popup (GtkComboBox *combo_box,
2251 guint32 activate_time)
2253 GtkComboBoxPrivate *priv = combo_box->priv;
2256 gint width, min_width, nat_width;
2258 update_menu_sensitivity (combo_box, priv->popup_widget);
2261 if (gtk_tree_row_reference_valid (priv->active_row))
2263 path = gtk_tree_row_reference_get_path (priv->active_row);
2264 active_item = gtk_tree_path_get_indices (path)[0];
2265 gtk_tree_path_free (path);
2267 if (priv->add_tearoffs)
2271 /* FIXME handle nested menus better */
2272 gtk_menu_set_active (GTK_MENU (priv->popup_widget), active_item);
2274 if (priv->wrap_width == 0)
2276 GtkAllocation allocation;
2278 gtk_widget_get_allocation (GTK_WIDGET (combo_box), &allocation);
2279 width = allocation.width;
2280 gtk_widget_set_size_request (priv->popup_widget, -1, -1);
2281 gtk_widget_get_preferred_width (priv->popup_widget, &min_width, &nat_width);
2283 if (combo_box->priv->popup_fixed_width)
2284 width = MAX (width, min_width);
2286 width = MAX (width, nat_width);
2288 gtk_widget_set_size_request (priv->popup_widget, width, -1);
2291 gtk_menu_popup (GTK_MENU (priv->popup_widget),
2293 gtk_combo_box_menu_position, combo_box,
2294 button, activate_time);
2298 popup_grab_on_window (GdkWindow *window,
2299 GdkDevice *keyboard,
2301 guint32 activate_time)
2304 gdk_device_grab (keyboard, window,
2305 GDK_OWNERSHIP_WINDOW, TRUE,
2306 GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
2307 NULL, activate_time) != GDK_GRAB_SUCCESS)
2311 gdk_device_grab (pointer, window,
2312 GDK_OWNERSHIP_WINDOW, TRUE,
2313 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
2314 GDK_POINTER_MOTION_MASK,
2315 NULL, activate_time) != GDK_GRAB_SUCCESS)
2318 gdk_device_ungrab (keyboard, activate_time);
2327 * gtk_combo_box_popup:
2328 * @combo_box: a #GtkComboBox
2330 * Pops up the menu or dropdown list of @combo_box.
2332 * This function is mostly intended for use by accessibility technologies;
2333 * applications should have little use for it.
2338 gtk_combo_box_popup (GtkComboBox *combo_box)
2340 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2342 g_signal_emit (combo_box, combo_box_signals[POPUP], 0);
2346 * gtk_combo_box_popup_for_device:
2347 * @combo_box: a #GtkComboBox
2348 * @device: a #GdkDevice
2350 * Pops up the menu or dropdown list of @combo_box, the popup window
2351 * will be grabbed so only @device and its associated pointer/keyboard
2352 * are the only #GdkDevice<!-- -->s able to send events to it.
2357 gtk_combo_box_popup_for_device (GtkComboBox *combo_box,
2360 GtkComboBoxPrivate *priv = combo_box->priv;
2361 gint x, y, width, height;
2362 GtkTreePath *path = NULL, *ppath;
2363 GtkWidget *toplevel;
2364 GdkDevice *keyboard, *pointer;
2367 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2368 g_return_if_fail (GDK_IS_DEVICE (device));
2370 if (!gtk_widget_get_realized (GTK_WIDGET (combo_box)))
2373 if (gtk_widget_get_mapped (priv->popup_widget))
2376 if (priv->grab_pointer && priv->grab_keyboard)
2379 time = gtk_get_current_event_time ();
2381 if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
2384 pointer = gdk_device_get_associated_device (device);
2389 keyboard = gdk_device_get_associated_device (device);
2392 if (GTK_IS_MENU (priv->popup_widget))
2394 gtk_combo_box_menu_popup (combo_box,
2395 priv->activate_button,
2396 priv->activate_time);
2400 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (combo_box));
2401 if (GTK_IS_WINDOW (toplevel))
2402 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
2403 GTK_WINDOW (priv->popup_window));
2405 gtk_widget_show_all (priv->scrolled_window);
2406 gtk_combo_box_list_position (combo_box, &x, &y, &width, &height);
2408 gtk_widget_set_size_request (priv->popup_window, width, height);
2409 gtk_window_move (GTK_WINDOW (priv->popup_window), x, y);
2411 if (gtk_tree_row_reference_valid (priv->active_row))
2413 path = gtk_tree_row_reference_get_path (priv->active_row);
2414 ppath = gtk_tree_path_copy (path);
2415 if (gtk_tree_path_up (ppath))
2416 gtk_tree_view_expand_to_path (GTK_TREE_VIEW (priv->tree_view),
2418 gtk_tree_path_free (ppath);
2420 gtk_tree_view_set_hover_expand (GTK_TREE_VIEW (priv->tree_view),
2424 gtk_widget_show (priv->popup_window);
2428 gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->tree_view),
2430 gtk_tree_path_free (path);
2433 gtk_widget_grab_focus (priv->popup_window);
2434 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button),
2437 if (!gtk_widget_has_focus (priv->tree_view))
2438 gtk_widget_grab_focus (priv->tree_view);
2440 if (!popup_grab_on_window (gtk_widget_get_window (priv->popup_window),
2441 keyboard, pointer, time))
2443 gtk_widget_hide (priv->popup_window);
2447 gtk_device_grab_add (priv->popup_window, pointer, TRUE);
2448 priv->grab_pointer = pointer;
2449 priv->grab_keyboard = keyboard;
2453 gtk_combo_box_real_popup (GtkComboBox *combo_box)
2457 device = gtk_get_current_event_device ();
2461 GdkDeviceManager *device_manager;
2462 GdkDisplay *display;
2465 display = gtk_widget_get_display (GTK_WIDGET (combo_box));
2466 device_manager = gdk_display_get_device_manager (display);
2468 /* No device was set, pick the first master device */
2469 devices = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
2470 device = devices->data;
2471 g_list_free (devices);
2474 gtk_combo_box_popup_for_device (combo_box, device);
2478 gtk_combo_box_real_popdown (GtkComboBox *combo_box)
2480 if (combo_box->priv->popup_shown)
2482 gtk_combo_box_popdown (combo_box);
2490 * gtk_combo_box_popdown:
2491 * @combo_box: a #GtkComboBox
2493 * Hides the menu or dropdown list of @combo_box.
2495 * This function is mostly intended for use by accessibility technologies;
2496 * applications should have little use for it.
2501 gtk_combo_box_popdown (GtkComboBox *combo_box)
2503 GtkComboBoxPrivate *priv = combo_box->priv;
2505 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2507 if (GTK_IS_MENU (priv->popup_widget))
2509 gtk_menu_popdown (GTK_MENU (priv->popup_widget));
2513 if (!gtk_widget_get_realized (GTK_WIDGET (combo_box)))
2516 gtk_device_grab_remove (priv->popup_window, priv->grab_pointer);
2517 gtk_widget_hide (priv->popup_window);
2518 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button),
2521 priv->grab_pointer = NULL;
2522 priv->grab_keyboard = NULL;
2525 #define GTK_COMBO_BOX_SIZE_ALLOCATE_BUTTON \
2526 GtkAllocation button_allocation; \
2527 gtk_widget_get_preferred_size (combo_box->priv->button, \
2531 button_allocation.x = allocation->x; \
2533 button_allocation.x = allocation->x + allocation->width \
2536 button_allocation.y = allocation->y; \
2537 button_allocation.width = MAX (1, req.width); \
2538 button_allocation.height = allocation->height; \
2539 button_allocation.height = MAX (1, button_allocation.height); \
2541 gtk_widget_size_allocate (combo_box->priv->button, \
2542 &button_allocation);
2546 gtk_combo_box_size_allocate (GtkWidget *widget,
2547 GtkAllocation *allocation)
2549 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
2550 GtkComboBoxPrivate *priv = combo_box->priv;
2551 GtkWidget *child_widget;
2552 GtkAllocation child;
2554 gboolean is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
2557 gtk_widget_set_allocation (widget, allocation);
2558 child_widget = gtk_bin_get_child (GTK_BIN (widget));
2559 get_widget_padding_and_border (widget, &padding);
2561 allocation->x += padding.left;
2562 allocation->y += padding.top;
2563 allocation->width -= padding.left + padding.right;
2564 allocation->height -= padding.top + padding.bottom;
2566 if (!priv->tree_view)
2568 if (priv->cell_view)
2570 GtkBorder button_padding;
2574 border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->button));
2575 get_widget_padding_and_border (priv->button, &button_padding);
2577 /* menu mode; child_widget is priv->cell_view.
2578 * Allocate the button to the full combobox allocation (minus the
2581 gtk_widget_size_allocate (priv->button, allocation);
2583 child.x = allocation->x;
2584 child.y = allocation->y;
2585 width = allocation->width;
2586 child.height = allocation->height;
2588 if (!priv->is_cell_renderer)
2590 /* restrict allocation of the child into the button box
2591 * if we're not in cell renderer mode.
2593 child.x += border_width + button_padding.left;
2594 child.y += border_width + button_padding.top;
2595 width -= 2 * border_width +
2596 button_padding.left + button_padding.right;
2597 child.height -= 2 * border_width +
2598 button_padding.top + button_padding.bottom;
2601 /* allocate the box containing the separator and the arrow */
2602 gtk_widget_get_preferred_size (priv->box, &req, NULL);
2603 child.width = req.width;
2605 child.x += width - req.width;
2606 child.width = MAX (1, child.width);
2607 child.height = MAX (1, child.height);
2608 gtk_widget_size_allocate (priv->box, &child);
2612 child.x += req.width;
2613 child.width = allocation->x + allocation->width
2614 - border_width - child.x - button_padding.right;
2618 child.width = child.x;
2619 child.x = allocation->x
2620 + border_width + button_padding.left;
2621 child.width -= child.x;
2624 if (gtk_widget_get_visible (priv->popup_widget))
2626 gint width, menu_width;
2628 if (priv->wrap_width == 0)
2630 GtkAllocation combo_box_allocation;
2632 gtk_widget_get_allocation (GTK_WIDGET (combo_box), &combo_box_allocation);
2633 width = combo_box_allocation.width;
2634 gtk_widget_set_size_request (priv->popup_widget, -1, -1);
2636 if (combo_box->priv->popup_fixed_width)
2637 gtk_widget_get_preferred_width (priv->popup_widget, &menu_width, NULL);
2639 gtk_widget_get_preferred_width (priv->popup_widget, NULL, &menu_width);
2641 gtk_widget_set_size_request (priv->popup_widget,
2642 MAX (width, menu_width), -1);
2645 /* reposition the menu after giving it a new width */
2646 gtk_menu_reposition (GTK_MENU (priv->popup_widget));
2649 child.width = MAX (1, child.width);
2650 child.height = MAX (1, child.height);
2651 gtk_widget_size_allocate (child_widget, &child);
2655 /* menu mode; child_widget has been set with gtk_container_add().
2656 * E.g. it might be a GtkEntry if priv->has_entry is TRUE.
2657 * Allocate the button at the far end, according to the direction
2660 GTK_COMBO_BOX_SIZE_ALLOCATE_BUTTON
2662 /* After the macro, button_allocation has the button allocation rect */
2665 child.x = button_allocation.x + button_allocation.width;
2667 child.x = allocation->x;
2669 child.y = allocation->y;
2670 child.width = allocation->width - button_allocation.width;
2671 child.height = button_allocation.height;
2673 child.width = MAX (1, child.width);
2675 gtk_widget_size_allocate (child_widget, &child);
2680 /* list mode; child_widget might be either priv->cell_view or a child
2681 * added with gtk_container_add().
2684 /* After the macro, button_allocation has the button allocation rect */
2685 GTK_COMBO_BOX_SIZE_ALLOCATE_BUTTON
2688 child.x = button_allocation.x + button_allocation.width;
2690 child.x = allocation->x;
2692 child.y = allocation->y;
2693 child.width = allocation->width - button_allocation.width;
2694 child.height = button_allocation.height;
2696 if (priv->cell_view_frame)
2698 gtk_widget_size_allocate (priv->cell_view_frame, &child);
2700 /* restrict allocation of the child into the frame box if it's present */
2701 if (priv->has_frame)
2703 GtkBorder frame_padding;
2706 border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->cell_view_frame));
2707 get_widget_padding_and_border (priv->cell_view_frame, &frame_padding);
2709 child.x += border_width + frame_padding.left;
2710 child.y += border_width + frame_padding.right;
2711 child.width -= (2 * border_width) + frame_padding.left + frame_padding.right;
2712 child.height -= (2 * border_width) + frame_padding.top + frame_padding.bottom;
2716 if (gtk_widget_get_visible (priv->popup_window))
2718 gint x, y, width, height;
2719 gtk_combo_box_list_position (combo_box, &x, &y, &width, &height);
2720 gtk_window_move (GTK_WINDOW (priv->popup_window), x, y);
2721 gtk_widget_set_size_request (priv->popup_window, width, height);
2724 /* allocate the child */
2725 child.width = MAX (1, child.width);
2726 child.height = MAX (1, child.height);
2727 gtk_widget_size_allocate (child_widget, &child);
2731 #undef GTK_COMBO_BOX_ALLOCATE_BUTTON
2734 gtk_combo_box_unset_model (GtkComboBox *combo_box)
2736 GtkComboBoxPrivate *priv = combo_box->priv;
2740 g_signal_handler_disconnect (priv->model,
2742 g_signal_handler_disconnect (priv->model,
2744 g_signal_handler_disconnect (priv->model,
2745 priv->reordered_id);
2746 g_signal_handler_disconnect (priv->model,
2752 g_object_unref (priv->model);
2756 if (priv->active_row)
2758 gtk_tree_row_reference_free (priv->active_row);
2759 priv->active_row = NULL;
2762 if (priv->cell_view)
2763 gtk_cell_view_set_model (GTK_CELL_VIEW (priv->cell_view), NULL);
2767 gtk_combo_box_forall (GtkContainer *container,
2768 gboolean include_internals,
2769 GtkCallback callback,
2770 gpointer callback_data)
2772 GtkComboBox *combo_box = GTK_COMBO_BOX (container);
2773 GtkComboBoxPrivate *priv = combo_box->priv;
2776 if (include_internals)
2779 (* callback) (priv->button, callback_data);
2780 if (priv->cell_view_frame)
2781 (* callback) (priv->cell_view_frame, callback_data);
2784 child = gtk_bin_get_child (GTK_BIN (container));
2786 (* callback) (child, callback_data);
2790 gtk_combo_box_child_show (GtkWidget *widget,
2791 GtkComboBox *combo_box)
2793 GtkComboBoxPrivate *priv = combo_box->priv;
2795 priv->popup_shown = TRUE;
2796 g_object_notify (G_OBJECT (combo_box), "popup-shown");
2800 gtk_combo_box_child_hide (GtkWidget *widget,
2801 GtkComboBox *combo_box)
2803 GtkComboBoxPrivate *priv = combo_box->priv;
2805 priv->popup_shown = FALSE;
2806 g_object_notify (G_OBJECT (combo_box), "popup-shown");
2810 gtk_combo_box_draw (GtkWidget *widget,
2813 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
2814 GtkComboBoxPrivate *priv = combo_box->priv;
2816 if (priv->shadow_type != GTK_SHADOW_NONE)
2818 GtkStyleContext *context;
2820 context = gtk_widget_get_style_context (widget);
2822 gtk_render_background (context, cr, 0, 0,
2823 gtk_widget_get_allocated_width (widget),
2824 gtk_widget_get_allocated_height (widget));
2825 gtk_render_frame (context, cr, 0, 0,
2826 gtk_widget_get_allocated_width (widget),
2827 gtk_widget_get_allocated_height (widget));
2830 gtk_container_propagate_draw (GTK_CONTAINER (widget),
2833 if (priv->tree_view && priv->cell_view_frame)
2835 gtk_container_propagate_draw (GTK_CONTAINER (widget),
2836 priv->cell_view_frame, cr);
2839 gtk_container_propagate_draw (GTK_CONTAINER (widget),
2840 gtk_bin_get_child (GTK_BIN (widget)),
2856 path_visible (GtkTreeView *view,
2862 /* Note that we rely on the fact that collapsed rows don't have nodes
2864 return _gtk_tree_view_find_node (view, path, &tree, &node);
2868 tree_next_func (GtkTreeModel *model,
2873 SearchData *search_data = (SearchData *)data;
2875 if (search_data->found)
2877 if (!tree_column_row_is_sensitive (search_data->combo, iter))
2880 if (search_data->visible &&
2881 !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
2884 search_data->set = TRUE;
2885 search_data->iter = *iter;
2890 if (gtk_tree_path_compare (path, search_data->path) == 0)
2891 search_data->found = TRUE;
2897 tree_next (GtkComboBox *combo,
2898 GtkTreeModel *model,
2903 SearchData search_data;
2905 search_data.combo = combo;
2906 search_data.path = gtk_tree_model_get_path (model, iter);
2907 search_data.visible = visible;
2908 search_data.found = FALSE;
2909 search_data.set = FALSE;
2911 gtk_tree_model_foreach (model, tree_next_func, &search_data);
2913 *next = search_data.iter;
2915 gtk_tree_path_free (search_data.path);
2917 return search_data.set;
2921 tree_prev_func (GtkTreeModel *model,
2926 SearchData *search_data = (SearchData *)data;
2928 if (gtk_tree_path_compare (path, search_data->path) == 0)
2930 search_data->found = TRUE;
2934 if (!tree_column_row_is_sensitive (search_data->combo, iter))
2937 if (search_data->visible &&
2938 !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
2941 search_data->set = TRUE;
2942 search_data->iter = *iter;
2948 tree_prev (GtkComboBox *combo,
2949 GtkTreeModel *model,
2954 SearchData search_data;
2956 search_data.combo = combo;
2957 search_data.path = gtk_tree_model_get_path (model, iter);
2958 search_data.visible = visible;
2959 search_data.found = FALSE;
2960 search_data.set = FALSE;
2962 gtk_tree_model_foreach (model, tree_prev_func, &search_data);
2964 *prev = search_data.iter;
2966 gtk_tree_path_free (search_data.path);
2968 return search_data.set;
2972 tree_last_func (GtkTreeModel *model,
2977 SearchData *search_data = (SearchData *)data;
2979 if (!tree_column_row_is_sensitive (search_data->combo, iter))
2982 /* Note that we rely on the fact that collapsed rows don't have nodes
2984 if (search_data->visible &&
2985 !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
2988 search_data->set = TRUE;
2989 search_data->iter = *iter;
2995 tree_last (GtkComboBox *combo,
2996 GtkTreeModel *model,
3000 SearchData search_data;
3002 search_data.combo = combo;
3003 search_data.visible = visible;
3004 search_data.set = FALSE;
3006 gtk_tree_model_foreach (model, tree_last_func, &search_data);
3008 *last = search_data.iter;
3010 return search_data.set;
3015 tree_first_func (GtkTreeModel *model,
3020 SearchData *search_data = (SearchData *)data;
3022 if (!tree_column_row_is_sensitive (search_data->combo, iter))
3025 if (search_data->visible &&
3026 !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
3029 search_data->set = TRUE;
3030 search_data->iter = *iter;
3036 tree_first (GtkComboBox *combo,
3037 GtkTreeModel *model,
3041 SearchData search_data;
3043 search_data.combo = combo;
3044 search_data.visible = visible;
3045 search_data.set = FALSE;
3047 gtk_tree_model_foreach (model, tree_first_func, &search_data);
3049 *first = search_data.iter;
3051 return search_data.set;
3055 gtk_combo_box_scroll_event (GtkWidget *widget,
3056 GdkEventScroll *event)
3058 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
3061 GtkTreeIter new_iter;
3063 if (!gtk_combo_box_get_active_iter (combo_box, &iter))
3066 if (event->direction == GDK_SCROLL_UP)
3067 found = tree_prev (combo_box, combo_box->priv->model,
3068 &iter, &new_iter, FALSE);
3070 found = tree_next (combo_box, combo_box->priv->model,
3071 &iter, &new_iter, FALSE);
3074 gtk_combo_box_set_active_iter (combo_box, &new_iter);
3083 gtk_combo_box_row_separator_func (GtkTreeModel *model,
3087 GtkComboBoxPrivate *priv = combo->priv;
3089 if (priv->row_separator_func)
3090 return priv->row_separator_func (model, iter, priv->row_separator_data);
3096 gtk_combo_box_header_func (GtkTreeModel *model,
3100 /* Every submenu has a selectable header, however we
3101 * can expose a method to make that configurable by
3102 * the user (like row_separator_func is done) */
3107 gtk_combo_box_menu_setup (GtkComboBox *combo_box,
3108 gboolean add_children)
3110 GtkComboBoxPrivate *priv = combo_box->priv;
3114 child = gtk_bin_get_child (GTK_BIN (combo_box));
3116 if (priv->cell_view)
3118 priv->button = gtk_toggle_button_new ();
3119 gtk_button_set_focus_on_click (GTK_BUTTON (priv->button),
3120 priv->focus_on_click);
3122 g_signal_connect (priv->button, "toggled",
3123 G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
3124 gtk_widget_set_parent (priv->button,
3125 gtk_widget_get_parent (child));
3127 priv->box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
3128 gtk_container_add (GTK_CONTAINER (priv->button), priv->box);
3130 priv->separator = gtk_separator_new (GTK_ORIENTATION_VERTICAL);
3131 gtk_container_add (GTK_CONTAINER (priv->box), priv->separator);
3133 priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3134 gtk_container_add (GTK_CONTAINER (priv->box), priv->arrow);
3136 gtk_widget_show_all (priv->button);
3140 priv->button = gtk_toggle_button_new ();
3141 gtk_button_set_focus_on_click (GTK_BUTTON (priv->button),
3142 priv->focus_on_click);
3144 g_signal_connect (priv->button, "toggled",
3145 G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
3146 gtk_widget_set_parent (priv->button,
3147 gtk_widget_get_parent (child));
3149 priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3150 gtk_container_add (GTK_CONTAINER (priv->button), priv->arrow);
3151 gtk_widget_show_all (priv->button);
3154 g_signal_connect (priv->button, "button-press-event",
3155 G_CALLBACK (gtk_combo_box_menu_button_press),
3157 g_signal_connect (priv->button, "state-flags-changed",
3158 G_CALLBACK (gtk_combo_box_button_state_flags_changed),
3161 /* create our funky menu */
3162 menu = _gtk_tree_menu_new_with_area (priv->area);
3163 gtk_widget_set_name (menu, "gtk-combobox-popup-menu");
3165 _gtk_tree_menu_set_model (GTK_TREE_MENU (menu), priv->model);
3167 _gtk_tree_menu_set_wrap_width (GTK_TREE_MENU (menu), priv->wrap_width);
3168 _gtk_tree_menu_set_row_span_column (GTK_TREE_MENU (menu), priv->row_column);
3169 _gtk_tree_menu_set_column_span_column (GTK_TREE_MENU (menu), priv->col_column);
3170 _gtk_tree_menu_set_tearoff (GTK_TREE_MENU (menu),
3171 combo_box->priv->add_tearoffs);
3173 g_signal_connect (menu, "menu-activate",
3174 G_CALLBACK (gtk_combo_box_menu_activate), combo_box);
3176 /* Chain our row_separator_func through */
3177 _gtk_tree_menu_set_row_separator_func (GTK_TREE_MENU (menu),
3178 (GtkTreeViewRowSeparatorFunc)gtk_combo_box_row_separator_func,
3181 _gtk_tree_menu_set_header_func (GTK_TREE_MENU (menu),
3182 (GtkTreeMenuHeaderFunc)gtk_combo_box_header_func,
3185 g_signal_connect (menu, "key-press-event",
3186 G_CALLBACK (gtk_combo_box_menu_key_press), combo_box);
3187 gtk_combo_box_set_popup_widget (combo_box, menu);
3189 gtk_combo_box_update_title (combo_box);
3193 gtk_combo_box_menu_destroy (GtkComboBox *combo_box)
3195 GtkComboBoxPrivate *priv = combo_box->priv;
3197 g_signal_handlers_disconnect_matched (priv->button,
3198 G_SIGNAL_MATCH_DATA,
3200 gtk_combo_box_menu_button_press, NULL);
3201 g_signal_handlers_disconnect_matched (priv->button,
3202 G_SIGNAL_MATCH_DATA,
3204 gtk_combo_box_button_state_flags_changed, combo_box);
3205 g_signal_handlers_disconnect_matched (priv->popup_widget,
3206 G_SIGNAL_MATCH_DATA,
3208 gtk_combo_box_menu_activate, combo_box);
3210 /* unparent will remove our latest ref */
3211 gtk_widget_unparent (priv->button);
3214 priv->button = NULL;
3216 priv->separator = NULL;
3218 /* changing the popup window will unref the menu and the children */
3223 gtk_combo_box_menu_button_press (GtkWidget *widget,
3224 GdkEventButton *event,
3227 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3228 GtkComboBoxPrivate *priv = combo_box->priv;
3230 if (GTK_IS_MENU (priv->popup_widget) &&
3231 event->type == GDK_BUTTON_PRESS && event->button == GDK_BUTTON_PRIMARY)
3233 if (priv->focus_on_click &&
3234 !gtk_widget_has_focus (priv->button))
3235 gtk_widget_grab_focus (priv->button);
3237 gtk_combo_box_menu_popup (combo_box, event->button, event->time);
3246 gtk_combo_box_menu_activate (GtkWidget *menu,
3248 GtkComboBox *combo_box)
3252 if (gtk_tree_model_get_iter_from_string (combo_box->priv->model, &iter, path))
3253 gtk_combo_box_set_active_iter (combo_box, &iter);
3255 g_object_set (combo_box,
3256 "editing-canceled", FALSE,
3262 gtk_combo_box_update_sensitivity (GtkComboBox *combo_box)
3265 gboolean sensitive = TRUE; /* fool code checkers */
3267 if (!combo_box->priv->button)
3270 switch (combo_box->priv->button_sensitivity)
3272 case GTK_SENSITIVITY_ON:
3275 case GTK_SENSITIVITY_OFF:
3278 case GTK_SENSITIVITY_AUTO:
3279 sensitive = combo_box->priv->model &&
3280 gtk_tree_model_get_iter_first (combo_box->priv->model, &iter);
3283 g_assert_not_reached ();
3287 gtk_widget_set_sensitive (combo_box->priv->button, sensitive);
3289 /* In list-mode, we also need to update sensitivity of the event box */
3290 if (GTK_IS_TREE_VIEW (combo_box->priv->tree_view)
3291 && combo_box->priv->cell_view)
3292 gtk_widget_set_sensitive (combo_box->priv->box, sensitive);
3296 gtk_combo_box_model_row_inserted (GtkTreeModel *model,
3301 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3303 if (combo_box->priv->tree_view)
3304 gtk_combo_box_list_popup_resize (combo_box);
3306 gtk_combo_box_update_sensitivity (combo_box);
3310 gtk_combo_box_model_row_deleted (GtkTreeModel *model,
3314 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3315 GtkComboBoxPrivate *priv = combo_box->priv;
3317 if (!gtk_tree_row_reference_valid (priv->active_row))
3319 if (priv->cell_view)
3320 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (priv->cell_view), NULL);
3321 g_signal_emit (combo_box, combo_box_signals[CHANGED], 0);
3324 if (priv->tree_view)
3325 gtk_combo_box_list_popup_resize (combo_box);
3327 gtk_combo_box_update_sensitivity (combo_box);
3331 gtk_combo_box_model_rows_reordered (GtkTreeModel *model,
3337 gtk_tree_row_reference_reordered (G_OBJECT (user_data), path, iter, new_order);
3341 gtk_combo_box_model_row_changed (GtkTreeModel *model,
3346 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3347 GtkComboBoxPrivate *priv = combo_box->priv;
3348 GtkTreePath *active_path;
3350 /* FIXME this belongs to GtkCellView */
3351 if (gtk_tree_row_reference_valid (priv->active_row))
3353 active_path = gtk_tree_row_reference_get_path (priv->active_row);
3354 if (gtk_tree_path_compare (path, active_path) == 0 &&
3356 gtk_widget_queue_resize (GTK_WIDGET (priv->cell_view));
3357 gtk_tree_path_free (active_path);
3360 if (priv->tree_view)
3361 gtk_combo_box_list_row_changed (model, path, iter, user_data);
3365 list_popup_resize_idle (gpointer user_data)
3367 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3368 GtkComboBoxPrivate *priv = combo_box->priv;
3369 gint x, y, width, height;
3371 if (priv->tree_view && gtk_widget_get_mapped (priv->popup_window))
3373 gtk_combo_box_list_position (combo_box, &x, &y, &width, &height);
3375 gtk_widget_set_size_request (priv->popup_window, width, height);
3376 gtk_window_move (GTK_WINDOW (priv->popup_window), x, y);
3379 priv->resize_idle_id = 0;
3385 gtk_combo_box_list_popup_resize (GtkComboBox *combo_box)
3387 GtkComboBoxPrivate *priv = combo_box->priv;
3389 if (!priv->resize_idle_id)
3390 priv->resize_idle_id =
3391 gdk_threads_add_idle (list_popup_resize_idle, combo_box);
3395 gtk_combo_box_model_row_expanded (GtkTreeModel *model,
3400 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3402 gtk_combo_box_list_popup_resize (combo_box);
3411 gtk_combo_box_list_setup (GtkComboBox *combo_box)
3413 GtkComboBoxPrivate *priv = combo_box->priv;
3414 GtkTreeSelection *sel;
3416 GtkWidget *widget = GTK_WIDGET (combo_box);
3418 priv->button = gtk_toggle_button_new ();
3419 child = gtk_bin_get_child (GTK_BIN (combo_box));
3420 gtk_widget_set_parent (priv->button,
3421 gtk_widget_get_parent (child));
3422 g_signal_connect (priv->button, "button-press-event",
3423 G_CALLBACK (gtk_combo_box_list_button_pressed), combo_box);
3424 g_signal_connect (priv->button, "toggled",
3425 G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
3427 priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3428 gtk_container_add (GTK_CONTAINER (priv->button), priv->arrow);
3429 priv->separator = NULL;
3430 gtk_widget_show_all (priv->button);
3432 if (priv->cell_view)
3434 GtkStyleContext *context;
3435 GtkStateFlags state;
3438 context = gtk_widget_get_style_context (widget);
3439 state = gtk_widget_get_state_flags (widget);
3440 gtk_style_context_get_background_color (context, state, &color);
3442 gtk_cell_view_set_background_rgba (GTK_CELL_VIEW (priv->cell_view), &color);
3444 priv->box = gtk_event_box_new ();
3445 gtk_event_box_set_visible_window (GTK_EVENT_BOX (priv->box),
3448 if (priv->has_frame)
3450 priv->cell_view_frame = gtk_frame_new (NULL);
3451 gtk_frame_set_shadow_type (GTK_FRAME (priv->cell_view_frame),
3456 combo_box->priv->cell_view_frame = gtk_event_box_new ();
3457 gtk_event_box_set_visible_window (GTK_EVENT_BOX (combo_box->priv->cell_view_frame),
3461 gtk_widget_set_parent (priv->cell_view_frame,
3462 gtk_widget_get_parent (child));
3463 gtk_container_add (GTK_CONTAINER (priv->cell_view_frame), priv->box);
3464 gtk_widget_show_all (priv->cell_view_frame);
3466 g_signal_connect (priv->box, "button-press-event",
3467 G_CALLBACK (gtk_combo_box_list_button_pressed),
3471 priv->tree_view = gtk_tree_view_new ();
3472 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));
3473 gtk_tree_selection_set_mode (sel, GTK_SELECTION_BROWSE);
3474 gtk_tree_selection_set_select_function (sel,
3475 gtk_combo_box_list_select_func,
3477 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->tree_view),
3479 gtk_tree_view_set_hover_selection (GTK_TREE_VIEW (priv->tree_view),
3482 gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (priv->tree_view),
3483 (GtkTreeViewRowSeparatorFunc)gtk_combo_box_row_separator_func,
3487 gtk_tree_view_set_model (GTK_TREE_VIEW (priv->tree_view), priv->model);
3489 gtk_tree_view_append_column (GTK_TREE_VIEW (priv->tree_view),
3490 gtk_tree_view_column_new_with_area (priv->area));
3492 if (gtk_tree_row_reference_valid (priv->active_row))
3496 path = gtk_tree_row_reference_get_path (priv->active_row);
3497 gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->tree_view),
3499 gtk_tree_path_free (path);
3502 /* set sample/popup widgets */
3503 gtk_combo_box_set_popup_widget (combo_box, priv->tree_view);
3505 g_signal_connect (priv->tree_view, "key-press-event",
3506 G_CALLBACK (gtk_combo_box_list_key_press),
3508 g_signal_connect (priv->tree_view, "enter-notify-event",
3509 G_CALLBACK (gtk_combo_box_list_enter_notify),
3511 g_signal_connect (priv->tree_view, "row-expanded",
3512 G_CALLBACK (gtk_combo_box_model_row_expanded),
3514 g_signal_connect (priv->tree_view, "row-collapsed",
3515 G_CALLBACK (gtk_combo_box_model_row_expanded),
3517 g_signal_connect (priv->popup_window, "button-press-event",
3518 G_CALLBACK (gtk_combo_box_list_button_pressed),
3520 g_signal_connect (priv->popup_window, "button-release-event",
3521 G_CALLBACK (gtk_combo_box_list_button_released),
3524 gtk_widget_show (priv->tree_view);
3526 gtk_combo_box_update_sensitivity (combo_box);
3530 gtk_combo_box_list_destroy (GtkComboBox *combo_box)
3532 GtkComboBoxPrivate *priv = combo_box->priv;
3534 /* disconnect signals */
3535 g_signal_handlers_disconnect_matched (priv->tree_view,
3536 G_SIGNAL_MATCH_DATA,
3537 0, 0, NULL, NULL, combo_box);
3538 g_signal_handlers_disconnect_matched (priv->button,
3539 G_SIGNAL_MATCH_DATA,
3541 gtk_combo_box_list_button_pressed,
3543 g_signal_handlers_disconnect_matched (priv->popup_window,
3544 G_SIGNAL_MATCH_DATA,
3546 gtk_combo_box_list_button_pressed,
3548 g_signal_handlers_disconnect_matched (priv->popup_window,
3549 G_SIGNAL_MATCH_DATA,
3551 gtk_combo_box_list_button_released,
3554 g_signal_handlers_disconnect_matched (priv->popup_window,
3555 G_SIGNAL_MATCH_DATA,
3557 gtk_combo_box_child_show,
3560 g_signal_handlers_disconnect_matched (priv->popup_window,
3561 G_SIGNAL_MATCH_DATA,
3563 gtk_combo_box_child_hide,
3567 g_signal_handlers_disconnect_matched (priv->box,
3568 G_SIGNAL_MATCH_DATA,
3570 gtk_combo_box_list_button_pressed,
3573 /* destroy things (unparent will kill the latest ref from us)
3574 * last unref on button will destroy the arrow
3576 gtk_widget_unparent (priv->button);
3577 priv->button = NULL;
3580 if (priv->cell_view)
3582 g_object_set (priv->cell_view,
3583 "background-set", FALSE,
3587 if (priv->cell_view_frame)
3589 gtk_widget_unparent (priv->cell_view_frame);
3590 priv->cell_view_frame = NULL;
3594 if (priv->scroll_timer)
3596 g_source_remove (priv->scroll_timer);
3597 priv->scroll_timer = 0;
3600 if (priv->resize_idle_id)
3602 g_source_remove (priv->resize_idle_id);
3603 priv->resize_idle_id = 0;
3606 gtk_widget_destroy (priv->tree_view);
3608 priv->tree_view = NULL;
3609 if (priv->popup_widget)
3611 g_object_unref (priv->popup_widget);
3612 priv->popup_widget = NULL;
3619 gtk_combo_box_list_button_pressed (GtkWidget *widget,
3620 GdkEventButton *event,
3623 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
3624 GtkComboBoxPrivate *priv = combo_box->priv;
3626 GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *)event);
3628 if (ewidget == priv->popup_window)
3631 if ((ewidget != priv->button && ewidget != priv->box) ||
3632 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->button)))
3635 if (priv->focus_on_click &&
3636 !gtk_widget_has_focus (priv->button))
3637 gtk_widget_grab_focus (priv->button);
3639 gtk_combo_box_popup_for_device (combo_box, event->device);
3641 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button), TRUE);
3643 priv->auto_scroll = FALSE;
3644 if (priv->scroll_timer == 0)
3645 priv->scroll_timer = gdk_threads_add_timeout (SCROLL_TIME,
3646 (GSourceFunc) gtk_combo_box_list_scroll_timeout,
3649 priv->popup_in_progress = TRUE;
3655 gtk_combo_box_list_button_released (GtkWidget *widget,
3656 GdkEventButton *event,
3660 GtkTreePath *path = NULL;
3663 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
3664 GtkComboBoxPrivate *priv = combo_box->priv;
3666 gboolean popup_in_progress = FALSE;
3668 GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *)event);
3670 if (priv->popup_in_progress)
3672 popup_in_progress = TRUE;
3673 priv->popup_in_progress = FALSE;
3676 gtk_tree_view_set_hover_expand (GTK_TREE_VIEW (priv->tree_view),
3678 if (priv->scroll_timer)
3680 g_source_remove (priv->scroll_timer);
3681 priv->scroll_timer = 0;
3684 if (ewidget != priv->tree_view)
3686 if ((ewidget == priv->button ||
3687 ewidget == priv->box) &&
3688 !popup_in_progress &&
3689 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->button)))
3691 gtk_combo_box_popdown (combo_box);
3695 /* released outside treeview */
3696 if (ewidget != priv->button && ewidget != priv->box)
3698 gtk_combo_box_popdown (combo_box);
3706 /* select something cool */
3707 ret = gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (priv->tree_view),
3713 return TRUE; /* clicked outside window? */
3715 gtk_tree_model_get_iter (priv->model, &iter, path);
3716 gtk_tree_path_free (path);
3718 gtk_combo_box_popdown (combo_box);
3720 if (tree_column_row_is_sensitive (combo_box, &iter))
3721 gtk_combo_box_set_active_iter (combo_box, &iter);
3727 gtk_combo_box_menu_key_press (GtkWidget *widget,
3731 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
3733 if (!gtk_bindings_activate_event (G_OBJECT (widget), event))
3735 /* The menu hasn't managed the
3736 * event, forward it to the combobox
3738 gtk_bindings_activate_event (G_OBJECT (combo_box), event);
3745 gtk_combo_box_list_key_press (GtkWidget *widget,
3749 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
3752 if (event->keyval == GDK_KEY_Return || event->keyval == GDK_KEY_ISO_Enter || event->keyval == GDK_KEY_KP_Enter ||
3753 event->keyval == GDK_KEY_space || event->keyval == GDK_KEY_KP_Space)
3755 GtkTreeModel *model = NULL;
3757 gtk_combo_box_popdown (combo_box);
3759 if (combo_box->priv->model)
3761 GtkTreeSelection *sel;
3763 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (combo_box->priv->tree_view));
3765 if (gtk_tree_selection_get_selected (sel, &model, &iter))
3766 gtk_combo_box_set_active_iter (combo_box, &iter);
3772 if (!gtk_bindings_activate_event (G_OBJECT (widget), event))
3774 /* The list hasn't managed the
3775 * event, forward it to the combobox
3777 gtk_bindings_activate_event (G_OBJECT (combo_box), event);
3784 gtk_combo_box_list_auto_scroll (GtkComboBox *combo_box,
3789 GtkAllocation allocation;
3790 GtkWidget *tree_view = combo_box->priv->tree_view;
3793 gtk_widget_get_allocation (tree_view, &allocation);
3795 adj = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window));
3796 if (adj && gtk_adjustment_get_upper (adj) - gtk_adjustment_get_lower (adj) > gtk_adjustment_get_page_size (adj))
3798 if (x <= allocation.x &&
3799 gtk_adjustment_get_lower (adj) < gtk_adjustment_get_value (adj))
3801 value = gtk_adjustment_get_value (adj) - (allocation.x - x + 1);
3802 gtk_adjustment_set_value (adj, value);
3804 else if (x >= allocation.x + allocation.width &&
3805 gtk_adjustment_get_upper (adj) - gtk_adjustment_get_page_size (adj) > gtk_adjustment_get_value (adj))
3807 value = gtk_adjustment_get_value (adj) + (x - allocation.x - allocation.width + 1);
3808 gtk_adjustment_set_value (adj, MAX (value, 0.0));
3812 adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window));
3813 if (adj && gtk_adjustment_get_upper (adj) - gtk_adjustment_get_lower (adj) > gtk_adjustment_get_page_size (adj))
3815 if (y <= allocation.y &&
3816 gtk_adjustment_get_lower (adj) < gtk_adjustment_get_value (adj))
3818 value = gtk_adjustment_get_value (adj) - (allocation.y - y + 1);
3819 gtk_adjustment_set_value (adj, value);
3821 else if (y >= allocation.height &&
3822 gtk_adjustment_get_upper (adj) - gtk_adjustment_get_page_size (adj) > gtk_adjustment_get_value (adj))
3824 value = gtk_adjustment_get_value (adj) + (y - allocation.height + 1);
3825 gtk_adjustment_set_value (adj, MAX (value, 0.0));
3831 gtk_combo_box_list_scroll_timeout (GtkComboBox *combo_box)
3833 GtkComboBoxPrivate *priv = combo_box->priv;
3836 if (priv->auto_scroll)
3838 gdk_window_get_device_position (gtk_widget_get_window (priv->tree_view),
3841 gtk_combo_box_list_auto_scroll (combo_box, x, y);
3848 gtk_combo_box_list_enter_notify (GtkWidget *widget,
3849 GdkEventCrossing *event,
3852 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
3854 combo_box->priv->auto_scroll = TRUE;
3860 gtk_combo_box_list_select_func (GtkTreeSelection *selection,
3861 GtkTreeModel *model,
3863 gboolean path_currently_selected,
3866 GList *list, *columns;
3867 gboolean sensitive = FALSE;
3869 columns = gtk_tree_view_get_columns (gtk_tree_selection_get_tree_view (selection));
3871 for (list = columns; list && !sensitive; list = list->next)
3873 GList *cells, *cell;
3874 gboolean cell_sensitive, cell_visible;
3876 GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN (list->data);
3878 if (!gtk_tree_view_column_get_visible (column))
3881 gtk_tree_model_get_iter (model, &iter, path);
3882 gtk_tree_view_column_cell_set_cell_data (column, model, &iter,
3885 cell = cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
3888 g_object_get (cell->data,
3889 "sensitive", &cell_sensitive,
3890 "visible", &cell_visible,
3893 if (cell_visible && cell_sensitive)
3902 g_list_free (cells);
3905 g_list_free (columns);
3911 gtk_combo_box_list_row_changed (GtkTreeModel *model,
3916 /* XXX Do nothing ? */
3920 * GtkCellLayout implementation
3922 static GtkCellArea *
3923 gtk_combo_box_cell_layout_get_area (GtkCellLayout *cell_layout)
3925 GtkComboBox *combo = GTK_COMBO_BOX (cell_layout);
3926 GtkComboBoxPrivate *priv = combo->priv;
3928 if (G_UNLIKELY (!priv->area))
3930 priv->area = gtk_cell_area_box_new ();
3931 g_object_ref_sink (priv->area);
3942 * gtk_combo_box_new:
3944 * Creates a new empty #GtkComboBox.
3946 * Return value: A new #GtkComboBox.
3951 gtk_combo_box_new (void)
3953 return g_object_new (GTK_TYPE_COMBO_BOX, NULL);
3957 * gtk_combo_box_new_with_area:
3958 * @area: the #GtkCellArea to use to layout cell renderers
3960 * Creates a new empty #GtkComboBox using @area to layout cells.
3962 * Return value: A new #GtkComboBox.
3965 gtk_combo_box_new_with_area (GtkCellArea *area)
3967 return g_object_new (GTK_TYPE_COMBO_BOX, "cell-area", area, NULL);
3971 * gtk_combo_box_new_with_area_and_entry:
3972 * @area: the #GtkCellArea to use to layout cell renderers
3974 * Creates a new empty #GtkComboBox with an entry.
3976 * The new combo box will use @area to layout cells.
3978 * Return value: A new #GtkComboBox.
3981 gtk_combo_box_new_with_area_and_entry (GtkCellArea *area)
3983 return g_object_new (GTK_TYPE_COMBO_BOX,
3991 * gtk_combo_box_new_with_entry:
3993 * Creates a new empty #GtkComboBox with an entry.
3995 * Return value: A new #GtkComboBox.
3998 gtk_combo_box_new_with_entry (void)
4000 return g_object_new (GTK_TYPE_COMBO_BOX, "has-entry", TRUE, NULL);
4004 * gtk_combo_box_new_with_model:
4005 * @model: A #GtkTreeModel.
4007 * Creates a new #GtkComboBox with the model initialized to @model.
4009 * Return value: A new #GtkComboBox.
4014 gtk_combo_box_new_with_model (GtkTreeModel *model)
4016 GtkComboBox *combo_box;
4018 g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL);
4020 combo_box = g_object_new (GTK_TYPE_COMBO_BOX, "model", model, NULL);
4022 return GTK_WIDGET (combo_box);
4026 * gtk_combo_box_new_with_model_and_entry:
4027 * @model: A #GtkTreeModel
4029 * Creates a new empty #GtkComboBox with an entry
4030 * and with the model initialized to @model.
4032 * Return value: A new #GtkComboBox
4035 gtk_combo_box_new_with_model_and_entry (GtkTreeModel *model)
4037 return g_object_new (GTK_TYPE_COMBO_BOX,
4044 * gtk_combo_box_get_wrap_width:
4045 * @combo_box: A #GtkComboBox
4047 * Returns the wrap width which is used to determine the number of columns
4048 * for the popup menu. If the wrap width is larger than 1, the combo box
4051 * Returns: the wrap width.
4056 gtk_combo_box_get_wrap_width (GtkComboBox *combo_box)
4058 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), -1);
4060 return combo_box->priv->wrap_width;
4064 * gtk_combo_box_set_wrap_width:
4065 * @combo_box: A #GtkComboBox
4066 * @width: Preferred number of columns
4068 * Sets the wrap width of @combo_box to be @width. The wrap width is basically
4069 * the preferred number of columns when you want the popup to be layed out
4075 gtk_combo_box_set_wrap_width (GtkComboBox *combo_box,
4078 GtkComboBoxPrivate *priv;
4080 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4081 g_return_if_fail (width >= 0);
4083 priv = combo_box->priv;
4085 if (width != priv->wrap_width)
4087 priv->wrap_width = width;
4089 gtk_combo_box_check_appearance (combo_box);
4091 if (GTK_IS_TREE_MENU (priv->popup_widget))
4092 _gtk_tree_menu_set_wrap_width (GTK_TREE_MENU (priv->popup_widget), priv->wrap_width);
4094 g_object_notify (G_OBJECT (combo_box), "wrap-width");
4099 * gtk_combo_box_get_row_span_column:
4100 * @combo_box: A #GtkComboBox
4102 * Returns the column with row span information for @combo_box.
4104 * Returns: the row span column.
4109 gtk_combo_box_get_row_span_column (GtkComboBox *combo_box)
4111 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), -1);
4113 return combo_box->priv->row_column;
4117 * gtk_combo_box_set_row_span_column:
4118 * @combo_box: A #GtkComboBox.
4119 * @row_span: A column in the model passed during construction.
4121 * Sets the column with row span information for @combo_box to be @row_span.
4122 * The row span column contains integers which indicate how many rows
4123 * an item should span.
4128 gtk_combo_box_set_row_span_column (GtkComboBox *combo_box,
4131 GtkComboBoxPrivate *priv;
4134 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4136 priv = combo_box->priv;
4138 col = gtk_tree_model_get_n_columns (priv->model);
4139 g_return_if_fail (row_span >= -1 && row_span < col);
4141 if (row_span != priv->row_column)
4143 priv->row_column = row_span;
4145 if (GTK_IS_TREE_MENU (priv->popup_widget))
4146 _gtk_tree_menu_set_row_span_column (GTK_TREE_MENU (priv->popup_widget), priv->row_column);
4148 g_object_notify (G_OBJECT (combo_box), "row-span-column");
4153 * gtk_combo_box_get_column_span_column:
4154 * @combo_box: A #GtkComboBox
4156 * Returns the column with column span information for @combo_box.
4158 * Returns: the column span column.
4163 gtk_combo_box_get_column_span_column (GtkComboBox *combo_box)
4165 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), -1);
4167 return combo_box->priv->col_column;
4171 * gtk_combo_box_set_column_span_column:
4172 * @combo_box: A #GtkComboBox
4173 * @column_span: A column in the model passed during construction
4175 * Sets the column with column span information for @combo_box to be
4176 * @column_span. The column span column contains integers which indicate
4177 * how many columns an item should span.
4182 gtk_combo_box_set_column_span_column (GtkComboBox *combo_box,
4185 GtkComboBoxPrivate *priv;
4188 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4190 priv = combo_box->priv;
4192 col = gtk_tree_model_get_n_columns (priv->model);
4193 g_return_if_fail (column_span >= -1 && column_span < col);
4195 if (column_span != priv->col_column)
4197 priv->col_column = column_span;
4199 if (GTK_IS_TREE_MENU (priv->popup_widget))
4200 _gtk_tree_menu_set_column_span_column (GTK_TREE_MENU (priv->popup_widget), priv->col_column);
4202 g_object_notify (G_OBJECT (combo_box), "column-span-column");
4207 * gtk_combo_box_get_active:
4208 * @combo_box: A #GtkComboBox
4210 * Returns the index of the currently active item, or -1 if there's no
4211 * active item. If the model is a non-flat treemodel, and the active item
4212 * is not an immediate child of the root of the tree, this function returns
4213 * <literal>gtk_tree_path_get_indices (path)[0]</literal>, where
4214 * <literal>path</literal> is the #GtkTreePath of the active item.
4216 * Return value: An integer which is the index of the currently active item,
4217 * or -1 if there's no active item.
4222 gtk_combo_box_get_active (GtkComboBox *combo_box)
4224 GtkComboBoxPrivate *priv;
4227 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), 0);
4229 priv = combo_box->priv;
4231 if (gtk_tree_row_reference_valid (priv->active_row))
4235 path = gtk_tree_row_reference_get_path (priv->active_row);
4236 result = gtk_tree_path_get_indices (path)[0];
4237 gtk_tree_path_free (path);
4246 * gtk_combo_box_set_active:
4247 * @combo_box: A #GtkComboBox
4248 * @index_: An index in the model passed during construction, or -1 to have
4251 * Sets the active item of @combo_box to be the item at @index.
4256 gtk_combo_box_set_active (GtkComboBox *combo_box,
4259 GtkTreePath *path = NULL;
4260 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4261 g_return_if_fail (index_ >= -1);
4263 if (combo_box->priv->model == NULL)
4265 /* Save index, in case the model is set after the index */
4266 combo_box->priv->active = index_;
4272 path = gtk_tree_path_new_from_indices (index_, -1);
4274 gtk_combo_box_set_active_internal (combo_box, path);
4277 gtk_tree_path_free (path);
4281 gtk_combo_box_set_active_internal (GtkComboBox *combo_box,
4284 GtkComboBoxPrivate *priv = combo_box->priv;
4285 GtkTreePath *active_path;
4288 /* Remember whether the initially active row is valid. */
4289 gboolean is_valid_row_reference = gtk_tree_row_reference_valid (priv->active_row);
4291 if (path && is_valid_row_reference)
4293 active_path = gtk_tree_row_reference_get_path (priv->active_row);
4294 path_cmp = gtk_tree_path_compare (path, active_path);
4295 gtk_tree_path_free (active_path);
4300 if (priv->active_row)
4302 gtk_tree_row_reference_free (priv->active_row);
4303 priv->active_row = NULL;
4308 if (priv->tree_view)
4309 gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view)));
4312 GtkMenu *menu = GTK_MENU (priv->popup_widget);
4314 if (GTK_IS_MENU (menu))
4315 gtk_menu_set_active (menu, -1);
4318 if (priv->cell_view)
4319 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (priv->cell_view), NULL);
4322 * Do not emit a "changed" signal when an already invalid selection was
4323 * now set to invalid.
4325 if (!is_valid_row_reference)
4331 gtk_tree_row_reference_new (priv->model, path);
4333 if (priv->tree_view)
4335 gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->tree_view),
4338 else if (GTK_IS_MENU (priv->popup_widget))
4340 /* FIXME handle nested menus better */
4341 gtk_menu_set_active (GTK_MENU (priv->popup_widget),
4342 gtk_tree_path_get_indices (path)[0]);
4345 if (priv->cell_view)
4346 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (priv->cell_view),
4350 g_signal_emit (combo_box, combo_box_signals[CHANGED], 0);
4351 g_object_notify (G_OBJECT (combo_box), "active");
4352 if (combo_box->priv->id_column >= 0)
4353 g_object_notify (G_OBJECT (combo_box), "active-id");
4358 * gtk_combo_box_get_active_iter:
4359 * @combo_box: A #GtkComboBox
4360 * @iter: (out): The uninitialized #GtkTreeIter
4362 * Sets @iter to point to the current active item, if it exists.
4364 * Return value: %TRUE, if @iter was set
4369 gtk_combo_box_get_active_iter (GtkComboBox *combo_box,
4375 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
4377 if (!gtk_tree_row_reference_valid (combo_box->priv->active_row))
4380 path = gtk_tree_row_reference_get_path (combo_box->priv->active_row);
4381 result = gtk_tree_model_get_iter (combo_box->priv->model, iter, path);
4382 gtk_tree_path_free (path);
4388 * gtk_combo_box_set_active_iter:
4389 * @combo_box: A #GtkComboBox
4390 * @iter: (allow-none): The #GtkTreeIter, or %NULL
4392 * Sets the current active item to be the one referenced by @iter, or
4393 * unsets the active item if @iter is %NULL.
4398 gtk_combo_box_set_active_iter (GtkComboBox *combo_box,
4401 GtkTreePath *path = NULL;
4403 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4406 path = gtk_tree_model_get_path (gtk_combo_box_get_model (combo_box), iter);
4408 gtk_combo_box_set_active_internal (combo_box, path);
4409 gtk_tree_path_free (path);
4413 * gtk_combo_box_set_model:
4414 * @combo_box: A #GtkComboBox
4415 * @model: (allow-none): A #GtkTreeModel
4417 * Sets the model used by @combo_box to be @model. Will unset a previously set
4418 * model (if applicable). If model is %NULL, then it will unset the model.
4420 * Note that this function does not clear the cell renderers, you have to
4421 * call gtk_cell_layout_clear() yourself if you need to set up different
4422 * cell renderers for the new model.
4427 gtk_combo_box_set_model (GtkComboBox *combo_box,
4428 GtkTreeModel *model)
4430 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4431 g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
4433 if (model == combo_box->priv->model)
4436 gtk_combo_box_unset_model (combo_box);
4441 combo_box->priv->model = model;
4442 g_object_ref (combo_box->priv->model);
4444 combo_box->priv->inserted_id =
4445 g_signal_connect (combo_box->priv->model, "row-inserted",
4446 G_CALLBACK (gtk_combo_box_model_row_inserted),
4448 combo_box->priv->deleted_id =
4449 g_signal_connect (combo_box->priv->model, "row-deleted",
4450 G_CALLBACK (gtk_combo_box_model_row_deleted),
4452 combo_box->priv->reordered_id =
4453 g_signal_connect (combo_box->priv->model, "rows-reordered",
4454 G_CALLBACK (gtk_combo_box_model_rows_reordered),
4456 combo_box->priv->changed_id =
4457 g_signal_connect (combo_box->priv->model, "row-changed",
4458 G_CALLBACK (gtk_combo_box_model_row_changed),
4461 if (combo_box->priv->tree_view)
4464 gtk_tree_view_set_model (GTK_TREE_VIEW (combo_box->priv->tree_view),
4465 combo_box->priv->model);
4466 gtk_combo_box_list_popup_resize (combo_box);
4469 if (GTK_IS_TREE_MENU (combo_box->priv->popup_widget))
4472 _gtk_tree_menu_set_model (GTK_TREE_MENU (combo_box->priv->popup_widget),
4473 combo_box->priv->model);
4476 if (combo_box->priv->cell_view)
4477 gtk_cell_view_set_model (GTK_CELL_VIEW (combo_box->priv->cell_view),
4478 combo_box->priv->model);
4480 if (combo_box->priv->active != -1)
4482 /* If an index was set in advance, apply it now */
4483 gtk_combo_box_set_active (combo_box, combo_box->priv->active);
4484 combo_box->priv->active = -1;
4488 gtk_combo_box_update_sensitivity (combo_box);
4490 g_object_notify (G_OBJECT (combo_box), "model");
4494 * gtk_combo_box_get_model:
4495 * @combo_box: A #GtkComboBox
4497 * Returns the #GtkTreeModel which is acting as data source for @combo_box.
4499 * Return value: (transfer none): A #GtkTreeModel which was passed
4500 * during construction.
4505 gtk_combo_box_get_model (GtkComboBox *combo_box)
4507 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
4509 return combo_box->priv->model;
4513 gtk_combo_box_real_move_active (GtkComboBox *combo_box,
4514 GtkScrollType scroll)
4517 GtkTreeIter new_iter;
4518 gboolean active_iter;
4521 if (!combo_box->priv->model)
4523 gtk_widget_error_bell (GTK_WIDGET (combo_box));
4527 active_iter = gtk_combo_box_get_active_iter (combo_box, &iter);
4531 case GTK_SCROLL_STEP_BACKWARD:
4532 case GTK_SCROLL_STEP_UP:
4533 case GTK_SCROLL_STEP_LEFT:
4536 found = tree_prev (combo_box, combo_box->priv->model,
4537 &iter, &new_iter, FALSE);
4540 /* else fall through */
4542 case GTK_SCROLL_PAGE_FORWARD:
4543 case GTK_SCROLL_PAGE_DOWN:
4544 case GTK_SCROLL_PAGE_RIGHT:
4545 case GTK_SCROLL_END:
4546 found = tree_last (combo_box, combo_box->priv->model, &new_iter, FALSE);
4549 case GTK_SCROLL_STEP_FORWARD:
4550 case GTK_SCROLL_STEP_DOWN:
4551 case GTK_SCROLL_STEP_RIGHT:
4554 found = tree_next (combo_box, combo_box->priv->model,
4555 &iter, &new_iter, FALSE);
4558 /* else fall through */
4560 case GTK_SCROLL_PAGE_BACKWARD:
4561 case GTK_SCROLL_PAGE_UP:
4562 case GTK_SCROLL_PAGE_LEFT:
4563 case GTK_SCROLL_START:
4564 found = tree_first (combo_box, combo_box->priv->model, &new_iter, FALSE);
4571 if (found && active_iter)
4573 GtkTreePath *old_path;
4574 GtkTreePath *new_path;
4576 old_path = gtk_tree_model_get_path (combo_box->priv->model, &iter);
4577 new_path = gtk_tree_model_get_path (combo_box->priv->model, &new_iter);
4579 if (gtk_tree_path_compare (old_path, new_path) == 0)
4582 gtk_tree_path_free (old_path);
4583 gtk_tree_path_free (new_path);
4588 gtk_combo_box_set_active_iter (combo_box, &new_iter);
4592 gtk_widget_error_bell (GTK_WIDGET (combo_box));
4597 gtk_combo_box_mnemonic_activate (GtkWidget *widget,
4598 gboolean group_cycling)
4600 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
4602 if (combo_box->priv->has_entry)
4606 child = gtk_bin_get_child (GTK_BIN (combo_box));
4608 gtk_widget_grab_focus (child);
4611 gtk_widget_grab_focus (combo_box->priv->button);
4617 gtk_combo_box_grab_focus (GtkWidget *widget)
4619 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
4621 if (combo_box->priv->has_entry)
4625 child = gtk_bin_get_child (GTK_BIN (combo_box));
4627 gtk_widget_grab_focus (child);
4630 gtk_widget_grab_focus (combo_box->priv->button);
4634 gtk_combo_box_destroy (GtkWidget *widget)
4636 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
4638 if (combo_box->priv->popup_idle_id > 0)
4640 g_source_remove (combo_box->priv->popup_idle_id);
4641 combo_box->priv->popup_idle_id = 0;
4644 gtk_combo_box_popdown (combo_box);
4646 if (combo_box->priv->row_separator_destroy)
4647 combo_box->priv->row_separator_destroy (combo_box->priv->row_separator_data);
4649 combo_box->priv->row_separator_func = NULL;
4650 combo_box->priv->row_separator_data = NULL;
4651 combo_box->priv->row_separator_destroy = NULL;
4653 GTK_WIDGET_CLASS (gtk_combo_box_parent_class)->destroy (widget);
4654 combo_box->priv->cell_view = NULL;
4658 gtk_combo_box_entry_contents_changed (GtkEntry *entry,
4661 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
4664 * Fixes regression reported in bug #574059. The old functionality relied on
4665 * bug #572478. As a bugfix, we now emit the "changed" signal ourselves
4666 * when the selection was already set to -1.
4668 if (gtk_combo_box_get_active(combo_box) == -1)
4669 g_signal_emit_by_name (combo_box, "changed");
4671 gtk_combo_box_set_active (combo_box, -1);
4675 gtk_combo_box_entry_active_changed (GtkComboBox *combo_box,
4678 GtkTreeModel *model;
4681 if (gtk_combo_box_get_active_iter (combo_box, &iter))
4683 GtkEntry *entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (combo_box)));
4691 model = gtk_combo_box_get_model (combo_box);
4692 path = gtk_tree_model_get_path (model, &iter);
4693 path_str = gtk_tree_path_to_string (path);
4695 g_signal_handlers_block_by_func (entry,
4696 gtk_combo_box_entry_contents_changed,
4700 g_signal_emit (combo_box, combo_box_signals[FORMAT_ENTRY_TEXT], 0,
4703 gtk_entry_set_text (entry, text);
4705 g_signal_handlers_unblock_by_func (entry,
4706 gtk_combo_box_entry_contents_changed,
4709 gtk_tree_path_free (path);
4717 gtk_combo_box_format_entry_text (GtkComboBox *combo_box,
4720 GtkComboBoxPrivate *priv = combo_box->priv;
4721 GtkTreeModel *model;
4725 if (priv->text_column >= 0)
4727 model = gtk_combo_box_get_model (combo_box);
4728 gtk_tree_model_get_iter_from_string (model, &iter, path);
4730 gtk_tree_model_get (model, &iter,
4731 priv->text_column, &text,
4740 gtk_combo_box_constructor (GType type,
4741 guint n_construct_properties,
4742 GObjectConstructParam *construct_properties)
4745 GtkComboBox *combo_box;
4746 GtkComboBoxPrivate *priv;
4748 object = G_OBJECT_CLASS (gtk_combo_box_parent_class)->constructor
4749 (type, n_construct_properties, construct_properties);
4751 combo_box = GTK_COMBO_BOX (object);
4752 priv = combo_box->priv;
4756 priv->area = gtk_cell_area_box_new ();
4757 g_object_ref_sink (priv->area);
4760 priv->cell_view = gtk_cell_view_new_with_context (priv->area, NULL);
4761 gtk_cell_view_set_fit_model (GTK_CELL_VIEW (priv->cell_view), TRUE);
4762 gtk_cell_view_set_model (GTK_CELL_VIEW (priv->cell_view), priv->model);
4763 gtk_widget_set_parent (priv->cell_view, GTK_WIDGET (combo_box));
4764 _gtk_bin_set_child (GTK_BIN (combo_box), priv->cell_view);
4765 gtk_widget_show (priv->cell_view);
4767 gtk_combo_box_check_appearance (combo_box);
4769 if (priv->has_entry)
4772 GtkStyleContext *context;
4774 entry = gtk_entry_new ();
4775 gtk_widget_show (entry);
4776 gtk_container_add (GTK_CONTAINER (combo_box), entry);
4778 context = gtk_widget_get_style_context (GTK_WIDGET (combo_box));
4779 gtk_style_context_add_class (context, GTK_STYLE_CLASS_COMBOBOX_ENTRY);
4781 priv->text_renderer = gtk_cell_renderer_text_new ();
4782 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box),
4783 priv->text_renderer, TRUE);
4785 gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), -1);
4787 g_signal_connect (combo_box, "changed",
4788 G_CALLBACK (gtk_combo_box_entry_active_changed), NULL);
4796 gtk_combo_box_dispose(GObject* object)
4798 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
4800 if (GTK_IS_MENU (combo_box->priv->popup_widget))
4802 gtk_combo_box_menu_destroy (combo_box);
4803 gtk_menu_detach (GTK_MENU (combo_box->priv->popup_widget));
4804 combo_box->priv->popup_widget = NULL;
4807 if (combo_box->priv->area)
4809 g_object_unref (combo_box->priv->area);
4810 combo_box->priv->area = NULL;
4813 if (GTK_IS_TREE_VIEW (combo_box->priv->tree_view))
4814 gtk_combo_box_list_destroy (combo_box);
4816 if (combo_box->priv->popup_window)
4818 gtk_widget_destroy (combo_box->priv->popup_window);
4819 combo_box->priv->popup_window = NULL;
4822 gtk_combo_box_unset_model (combo_box);
4824 G_OBJECT_CLASS (gtk_combo_box_parent_class)->dispose (object);
4828 gtk_combo_box_finalize (GObject *object)
4830 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
4832 g_free (combo_box->priv->tearoff_title);
4834 G_OBJECT_CLASS (gtk_combo_box_parent_class)->finalize (object);
4838 gtk_cell_editable_key_press (GtkWidget *widget,
4842 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
4844 if (event->keyval == GDK_KEY_Escape)
4846 g_object_set (combo_box,
4847 "editing-canceled", TRUE,
4849 gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (combo_box));
4850 gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (combo_box));
4854 else if (event->keyval == GDK_KEY_Return ||
4855 event->keyval == GDK_KEY_ISO_Enter ||
4856 event->keyval == GDK_KEY_KP_Enter)
4858 gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (combo_box));
4859 gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (combo_box));
4868 popdown_idle (gpointer data)
4870 GtkComboBox *combo_box;
4872 combo_box = GTK_COMBO_BOX (data);
4874 gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (combo_box));
4875 gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (combo_box));
4877 g_object_unref (combo_box);
4883 popdown_handler (GtkWidget *widget,
4886 gdk_threads_add_idle (popdown_idle, g_object_ref (data));
4890 popup_idle (gpointer data)
4892 GtkComboBox *combo_box;
4894 combo_box = GTK_COMBO_BOX (data);
4896 if (GTK_IS_MENU (combo_box->priv->popup_widget) &&
4897 combo_box->priv->cell_view)
4898 g_signal_connect_object (combo_box->priv->popup_widget,
4899 "unmap", G_CALLBACK (popdown_handler),
4902 /* we unset this if a menu item is activated */
4903 g_object_set (combo_box,
4904 "editing-canceled", TRUE,
4906 gtk_combo_box_popup (combo_box);
4908 combo_box->priv->popup_idle_id = 0;
4909 combo_box->priv->activate_button = 0;
4910 combo_box->priv->activate_time = 0;
4916 gtk_combo_box_start_editing (GtkCellEditable *cell_editable,
4919 GtkComboBox *combo_box = GTK_COMBO_BOX (cell_editable);
4922 combo_box->priv->is_cell_renderer = TRUE;
4924 if (combo_box->priv->cell_view)
4926 g_signal_connect_object (combo_box->priv->button, "key-press-event",
4927 G_CALLBACK (gtk_cell_editable_key_press),
4930 gtk_widget_grab_focus (combo_box->priv->button);
4934 child = gtk_bin_get_child (GTK_BIN (combo_box));
4936 g_signal_connect_object (child, "key-press-event",
4937 G_CALLBACK (gtk_cell_editable_key_press),
4940 gtk_widget_grab_focus (child);
4941 gtk_widget_set_can_focus (combo_box->priv->button, FALSE);
4944 /* we do the immediate popup only for the optionmenu-like
4947 if (combo_box->priv->is_cell_renderer &&
4948 combo_box->priv->cell_view && !combo_box->priv->tree_view)
4950 if (event && event->type == GDK_BUTTON_PRESS)
4952 GdkEventButton *event_button = (GdkEventButton *)event;
4954 combo_box->priv->activate_button = event_button->button;
4955 combo_box->priv->activate_time = event_button->time;
4958 combo_box->priv->popup_idle_id =
4959 gdk_threads_add_idle (popup_idle, combo_box);
4965 * gtk_combo_box_get_add_tearoffs:
4966 * @combo_box: a #GtkComboBox
4968 * Gets the current value of the :add-tearoffs property.
4970 * Return value: the current value of the :add-tearoffs property.
4973 gtk_combo_box_get_add_tearoffs (GtkComboBox *combo_box)
4975 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
4977 return combo_box->priv->add_tearoffs;
4981 * gtk_combo_box_set_add_tearoffs:
4982 * @combo_box: a #GtkComboBox
4983 * @add_tearoffs: %TRUE to add tearoff menu items
4985 * Sets whether the popup menu should have a tearoff
4991 gtk_combo_box_set_add_tearoffs (GtkComboBox *combo_box,
4992 gboolean add_tearoffs)
4994 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4996 add_tearoffs = add_tearoffs != FALSE;
4998 if (combo_box->priv->add_tearoffs != add_tearoffs)
5000 combo_box->priv->add_tearoffs = add_tearoffs;
5001 gtk_combo_box_check_appearance (combo_box);
5003 if (GTK_IS_TREE_MENU (combo_box->priv->popup_widget))
5004 _gtk_tree_menu_set_tearoff (GTK_TREE_MENU (combo_box->priv->popup_widget),
5005 combo_box->priv->add_tearoffs);
5007 g_object_notify (G_OBJECT (combo_box), "add-tearoffs");
5012 * gtk_combo_box_get_title:
5013 * @combo_box: a #GtkComboBox
5015 * Gets the current title of the menu in tearoff mode. See
5016 * gtk_combo_box_set_add_tearoffs().
5018 * Returns: the menu's title in tearoff mode. This is an internal copy of the
5019 * string which must not be freed.
5024 gtk_combo_box_get_title (GtkComboBox *combo_box)
5026 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
5028 return combo_box->priv->tearoff_title;
5032 gtk_combo_box_update_title (GtkComboBox *combo_box)
5034 gtk_combo_box_check_appearance (combo_box);
5036 if (combo_box->priv->popup_widget &&
5037 GTK_IS_MENU (combo_box->priv->popup_widget))
5038 gtk_menu_set_title (GTK_MENU (combo_box->priv->popup_widget),
5039 combo_box->priv->tearoff_title);
5043 * gtk_combo_box_set_title:
5044 * @combo_box: a #GtkComboBox
5045 * @title: a title for the menu in tearoff mode
5047 * Sets the menu's title in tearoff mode.
5052 gtk_combo_box_set_title (GtkComboBox *combo_box,
5055 GtkComboBoxPrivate *priv;
5057 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5059 priv = combo_box->priv;
5061 if (strcmp (title ? title : "",
5062 priv->tearoff_title ? priv->tearoff_title : "") != 0)
5064 g_free (priv->tearoff_title);
5065 priv->tearoff_title = g_strdup (title);
5067 gtk_combo_box_update_title (combo_box);
5069 g_object_notify (G_OBJECT (combo_box), "tearoff-title");
5075 * gtk_combo_box_set_popup_fixed_width:
5076 * @combo_box: a #GtkComboBox
5077 * @fixed: whether to use a fixed popup width
5079 * Specifies whether the popup's width should be a fixed width
5080 * matching the allocated width of the combo box.
5085 gtk_combo_box_set_popup_fixed_width (GtkComboBox *combo_box,
5088 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5090 if (combo_box->priv->popup_fixed_width != fixed)
5092 combo_box->priv->popup_fixed_width = fixed;
5094 g_object_notify (G_OBJECT (combo_box), "popup-fixed-width");
5099 * gtk_combo_box_get_popup_fixed_width:
5100 * @combo_box: a #GtkComboBox
5102 * Gets whether the popup uses a fixed width matching
5103 * the allocated width of the combo box.
5105 * Returns: %TRUE if the popup uses a fixed width
5110 gtk_combo_box_get_popup_fixed_width (GtkComboBox *combo_box)
5112 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
5114 return combo_box->priv->popup_fixed_width;
5119 * gtk_combo_box_get_popup_accessible:
5120 * @combo_box: a #GtkComboBox
5122 * Gets the accessible object corresponding to the combo box's popup.
5124 * This function is mostly intended for use by accessibility technologies;
5125 * applications should have little use for it.
5127 * Returns: (transfer none): the accessible object corresponding
5128 * to the combo box's popup.
5133 gtk_combo_box_get_popup_accessible (GtkComboBox *combo_box)
5137 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
5139 if (combo_box->priv->popup_widget)
5141 atk_obj = gtk_widget_get_accessible (combo_box->priv->popup_widget);
5149 * gtk_combo_box_get_row_separator_func: (skip)
5150 * @combo_box: a #GtkComboBox
5152 * Returns the current row separator function.
5154 * Return value: the current row separator function.
5158 GtkTreeViewRowSeparatorFunc
5159 gtk_combo_box_get_row_separator_func (GtkComboBox *combo_box)
5161 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
5163 return combo_box->priv->row_separator_func;
5167 * gtk_combo_box_set_row_separator_func:
5168 * @combo_box: a #GtkComboBox
5169 * @func: a #GtkTreeViewRowSeparatorFunc
5170 * @data: (allow-none): user data to pass to @func, or %NULL
5171 * @destroy: (allow-none): destroy notifier for @data, or %NULL
5173 * Sets the row separator function, which is used to determine
5174 * whether a row should be drawn as a separator. If the row separator
5175 * function is %NULL, no separators are drawn. This is the default value.
5180 gtk_combo_box_set_row_separator_func (GtkComboBox *combo_box,
5181 GtkTreeViewRowSeparatorFunc func,
5183 GDestroyNotify destroy)
5185 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5187 if (combo_box->priv->row_separator_destroy)
5188 combo_box->priv->row_separator_destroy (combo_box->priv->row_separator_data);
5190 combo_box->priv->row_separator_func = func;
5191 combo_box->priv->row_separator_data = data;
5192 combo_box->priv->row_separator_destroy = destroy;
5194 /* Provoke the underlying treeview/menu to rebuild themselves with the new separator func */
5195 if (combo_box->priv->tree_view)
5197 gtk_tree_view_set_model (GTK_TREE_VIEW (combo_box->priv->tree_view), NULL);
5198 gtk_tree_view_set_model (GTK_TREE_VIEW (combo_box->priv->tree_view), combo_box->priv->model);
5201 if (GTK_IS_TREE_MENU (combo_box->priv->popup_widget))
5203 _gtk_tree_menu_set_model (GTK_TREE_MENU (combo_box->priv->popup_widget), NULL);
5204 _gtk_tree_menu_set_model (GTK_TREE_MENU (combo_box->priv->popup_widget), combo_box->priv->model);
5207 gtk_widget_queue_draw (GTK_WIDGET (combo_box));
5211 * gtk_combo_box_set_button_sensitivity:
5212 * @combo_box: a #GtkComboBox
5213 * @sensitivity: specify the sensitivity of the dropdown button
5215 * Sets whether the dropdown button of the combo box should be
5216 * always sensitive (%GTK_SENSITIVITY_ON), never sensitive (%GTK_SENSITIVITY_OFF)
5217 * or only if there is at least one item to display (%GTK_SENSITIVITY_AUTO).
5222 gtk_combo_box_set_button_sensitivity (GtkComboBox *combo_box,
5223 GtkSensitivityType sensitivity)
5225 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5227 if (combo_box->priv->button_sensitivity != sensitivity)
5229 combo_box->priv->button_sensitivity = sensitivity;
5230 gtk_combo_box_update_sensitivity (combo_box);
5232 g_object_notify (G_OBJECT (combo_box), "button-sensitivity");
5237 * gtk_combo_box_get_button_sensitivity:
5238 * @combo_box: a #GtkComboBox
5240 * Returns whether the combo box sets the dropdown button
5241 * sensitive or not when there are no items in the model.
5243 * Return Value: %GTK_SENSITIVITY_ON if the dropdown button
5244 * is sensitive when the model is empty, %GTK_SENSITIVITY_OFF
5245 * if the button is always insensitive or
5246 * %GTK_SENSITIVITY_AUTO if it is only sensitive as long as
5247 * the model has one item to be selected.
5252 gtk_combo_box_get_button_sensitivity (GtkComboBox *combo_box)
5254 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
5256 return combo_box->priv->button_sensitivity;
5261 * gtk_combo_box_get_has_entry:
5262 * @combo_box: a #GtkComboBox
5264 * Returns whether the combo box has an entry.
5266 * Return Value: whether there is an entry in @combo_box.
5271 gtk_combo_box_get_has_entry (GtkComboBox *combo_box)
5273 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
5275 return combo_box->priv->has_entry;
5279 * gtk_combo_box_set_entry_text_column:
5280 * @combo_box: A #GtkComboBox
5281 * @text_column: A column in @model to get the strings from for
5282 * the internal entry
5284 * Sets the model column which @combo_box should use to get strings from
5285 * to be @text_column. The column @text_column in the model of @combo_box
5286 * must be of type %G_TYPE_STRING.
5288 * This is only relevant if @combo_box has been created with
5289 * #GtkComboBox:has-entry as %TRUE.
5294 gtk_combo_box_set_entry_text_column (GtkComboBox *combo_box,
5297 GtkComboBoxPrivate *priv = combo_box->priv;
5298 GtkTreeModel *model;
5300 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5302 model = gtk_combo_box_get_model (combo_box);
5304 g_return_if_fail (text_column >= 0);
5305 g_return_if_fail (model == NULL || text_column < gtk_tree_model_get_n_columns (model));
5307 priv->text_column = text_column;
5309 if (priv->text_renderer != NULL)
5310 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box),
5311 priv->text_renderer,
5312 "text", text_column,
5317 * gtk_combo_box_get_entry_text_column:
5318 * @combo_box: A #GtkComboBox.
5320 * Returns the column which @combo_box is using to get the strings
5321 * from to display in the internal entry.
5323 * Return value: A column in the data source model of @combo_box.
5328 gtk_combo_box_get_entry_text_column (GtkComboBox *combo_box)
5330 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), 0);
5332 return combo_box->priv->text_column;
5336 * gtk_combo_box_set_focus_on_click:
5337 * @combo: a #GtkComboBox
5338 * @focus_on_click: whether the combo box grabs focus when clicked
5341 * Sets whether the combo box will grab focus when it is clicked with
5342 * the mouse. Making mouse clicks not grab focus is useful in places
5343 * like toolbars where you don't want the keyboard focus removed from
5344 * the main area of the application.
5349 gtk_combo_box_set_focus_on_click (GtkComboBox *combo_box,
5350 gboolean focus_on_click)
5352 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5354 focus_on_click = focus_on_click != FALSE;
5356 if (combo_box->priv->focus_on_click != focus_on_click)
5358 combo_box->priv->focus_on_click = focus_on_click;
5360 if (combo_box->priv->button)
5361 gtk_button_set_focus_on_click (GTK_BUTTON (combo_box->priv->button),
5364 g_object_notify (G_OBJECT (combo_box), "focus-on-click");
5369 * gtk_combo_box_get_focus_on_click:
5370 * @combo: a #GtkComboBox
5372 * Returns whether the combo box grabs focus when it is clicked
5373 * with the mouse. See gtk_combo_box_set_focus_on_click().
5375 * Return value: %TRUE if the combo box grabs focus when it is
5376 * clicked with the mouse.
5381 gtk_combo_box_get_focus_on_click (GtkComboBox *combo_box)
5383 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
5385 return combo_box->priv->focus_on_click;
5390 gtk_combo_box_buildable_custom_tag_start (GtkBuildable *buildable,
5391 GtkBuilder *builder,
5393 const gchar *tagname,
5394 GMarkupParser *parser,
5397 if (parent_buildable_iface->custom_tag_start (buildable, builder, child,
5398 tagname, parser, data))
5401 return _gtk_cell_layout_buildable_custom_tag_start (buildable, builder, child,
5402 tagname, parser, data);
5406 gtk_combo_box_buildable_custom_tag_end (GtkBuildable *buildable,
5407 GtkBuilder *builder,
5409 const gchar *tagname,
5412 if (!_gtk_cell_layout_buildable_custom_tag_end (buildable, builder, child, tagname, data))
5413 parent_buildable_iface->custom_tag_end (buildable, builder, child, tagname, data);
5417 gtk_combo_box_buildable_get_internal_child (GtkBuildable *buildable,
5418 GtkBuilder *builder,
5419 const gchar *childname)
5421 GtkComboBox *combo_box = GTK_COMBO_BOX (buildable);
5423 if (combo_box->priv->has_entry && strcmp (childname, "entry") == 0)
5424 return G_OBJECT (gtk_bin_get_child (GTK_BIN (buildable)));
5426 return parent_buildable_iface->get_internal_child (buildable, builder, childname);
5430 gtk_combo_box_get_preferred_width (GtkWidget *widget,
5434 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
5435 GtkComboBoxPrivate *priv = combo_box->priv;
5436 gint font_size, arrow_size;
5437 PangoContext *context;
5438 PangoFontMetrics *metrics;
5439 const PangoFontDescription *font_desc;
5441 gint minimum_width = 0, natural_width = 0;
5442 gint child_min, child_nat;
5443 GtkStyleContext *style_context;
5444 GtkStateFlags state;
5446 gfloat arrow_scaling;
5448 child = gtk_bin_get_child (GTK_BIN (widget));
5451 gtk_widget_get_preferred_width (child, &child_min, &child_nat);
5453 gtk_widget_style_get (GTK_WIDGET (widget),
5454 "arrow-size", &arrow_size,
5455 "arrow-scaling", &arrow_scaling,
5458 style_context = gtk_widget_get_style_context (widget);
5459 state = gtk_widget_get_state_flags (widget);
5461 get_widget_padding_and_border (widget, &padding);
5462 font_desc = gtk_style_context_get_font (style_context, state);
5464 context = gtk_widget_get_pango_context (GTK_WIDGET (widget));
5465 metrics = pango_context_get_metrics (context, font_desc,
5466 pango_context_get_language (context));
5467 font_size = PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) +
5468 pango_font_metrics_get_descent (metrics));
5469 pango_font_metrics_unref (metrics);
5471 arrow_size = MAX (arrow_size, font_size) * arrow_scaling;
5473 gtk_widget_set_size_request (priv->arrow, arrow_size, arrow_size);
5475 if (!priv->tree_view)
5478 if (priv->cell_view)
5480 gint box_width, xpad;
5481 GtkBorder button_padding;
5483 get_widget_padding_and_border (priv->button, &button_padding);
5485 gtk_widget_get_preferred_width (priv->box, &box_width, NULL);
5486 xpad = button_padding.left + button_padding.right + padding.left + padding.right;
5488 minimum_width = child_min + box_width + xpad;
5489 natural_width = child_nat + box_width + xpad;
5493 gint but_width, but_nat_width;
5495 gtk_widget_get_preferred_width (priv->button,
5496 &but_width, &but_nat_width);
5498 minimum_width = child_min + but_width;
5499 natural_width = child_nat + but_nat_width;
5505 gint button_width, button_nat_width;
5507 /* sample + frame */
5508 minimum_width = child_min;
5509 natural_width = child_nat;
5511 if (priv->cell_view_frame)
5513 if (priv->has_frame)
5515 gint border_width, xpad;
5516 GtkBorder frame_padding;
5518 border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->cell_view_frame));
5519 get_widget_padding_and_border (priv->cell_view_frame, &frame_padding);
5520 xpad = (2 * border_width) + frame_padding.left + frame_padding.right;
5522 minimum_width += xpad;
5523 natural_width += xpad;
5528 gtk_widget_get_preferred_width (priv->button,
5529 &button_width, &button_nat_width);
5531 minimum_width += button_width;
5532 natural_width += button_nat_width;
5535 minimum_width += padding.left + padding.right;
5536 natural_width += padding.left + padding.right;
5539 *minimum_size = minimum_width;
5542 *natural_size = natural_width;
5546 gtk_combo_box_get_preferred_height (GtkWidget *widget,
5552 /* Combo box is height-for-width only
5553 * (so we always just reserve enough height for the minimum width) */
5554 GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, &min_width, NULL);
5555 GTK_WIDGET_GET_CLASS (widget)->get_preferred_height_for_width (widget, min_width, minimum_size, natural_size);
5559 gtk_combo_box_get_preferred_width_for_height (GtkWidget *widget,
5564 /* Combo box is height-for-width only
5565 * (so we assume we always reserved enough height for the minimum width) */
5566 GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, minimum_size, natural_size);
5571 gtk_combo_box_get_preferred_height_for_width (GtkWidget *widget,
5576 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
5577 GtkComboBoxPrivate *priv = combo_box->priv;
5578 gint min_height = 0, nat_height = 0;
5583 child = gtk_bin_get_child (GTK_BIN (widget));
5585 get_widget_padding_and_border (widget, &padding);
5588 if (!priv->tree_view)
5591 if (priv->cell_view)
5593 /* calculate x/y padding and separator/arrow size */
5594 gint box_width, box_height;
5596 GtkBorder button_padding;
5598 get_widget_padding_and_border (priv->button, &button_padding);
5600 gtk_widget_get_preferred_width (priv->box, &box_width, NULL);
5601 gtk_widget_get_preferred_height_for_width (priv->box,
5602 box_width, &box_height, NULL);
5604 xpad = button_padding.left + button_padding.right;
5605 ypad = button_padding.top + button_padding.bottom;
5607 size -= box_width + xpad;
5609 /* Get height-for-width of the child widget, usually a GtkCellArea calculating
5610 * and fitting the whole treemodel */
5611 gtk_widget_get_preferred_height_for_width (child, size, &min_height, &nat_height);
5613 min_height = MAX (min_height, box_height);
5614 nat_height = MAX (nat_height, box_height);
5621 /* there is a custom child widget inside (no priv->cell_view) */
5622 gint but_width, but_height;
5624 gtk_widget_get_preferred_width (priv->button, &but_width, NULL);
5625 gtk_widget_get_preferred_height_for_width (priv->button,
5626 but_width, &but_height, NULL);
5630 /* Get height-for-width of the child widget, usually a GtkCellArea calculating
5631 * and fitting the whole treemodel */
5632 gtk_widget_get_preferred_height_for_width (child, size, &min_height, &nat_height);
5634 min_height = MAX (min_height, but_height);
5635 nat_height = MAX (nat_height, but_height);
5641 gint but_width, but_height;
5642 gint xpad = 0, ypad = 0;
5644 gtk_widget_get_preferred_width (priv->button, &but_width, NULL);
5645 gtk_widget_get_preferred_height_for_width (priv->button,
5646 but_width, &but_height, NULL);
5648 if (priv->cell_view_frame && priv->has_frame)
5650 GtkBorder frame_padding;
5653 border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->cell_view_frame));
5654 get_widget_padding_and_border (GTK_WIDGET (priv->cell_view_frame), &frame_padding);
5656 xpad = (2 * border_width) + padding.left + frame_padding.right;
5657 ypad = (2 * border_width) + padding.top + frame_padding.bottom;
5663 /* Get height-for-width of the child widget, usually a GtkCellArea calculating
5664 * and fitting the whole treemodel */
5665 gtk_widget_get_preferred_height_for_width (child, size, &min_height, &nat_height);
5667 min_height = MAX (min_height, but_height);
5668 nat_height = MAX (nat_height, but_height);
5674 min_height += padding.top + padding.bottom;
5675 nat_height += padding.top + padding.bottom;
5678 *minimum_size = min_height;
5681 *natural_size = nat_height;
5685 * gtk_combo_box_set_id_column:
5686 * @combo_box: A #GtkComboBox
5687 * @id_column: A column in @model to get string IDs for values from
5689 * Sets the model column which @combo_box should use to get string IDs
5690 * for values from. The column @id_column in the model of @combo_box
5691 * must be of type %G_TYPE_STRING.
5696 gtk_combo_box_set_id_column (GtkComboBox *combo_box,
5699 GtkComboBoxPrivate *priv = combo_box->priv;
5700 GtkTreeModel *model;
5702 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5704 if (id_column != priv->id_column)
5706 model = gtk_combo_box_get_model (combo_box);
5708 g_return_if_fail (id_column >= 0);
5709 g_return_if_fail (model == NULL ||
5710 id_column < gtk_tree_model_get_n_columns (model));
5712 priv->id_column = id_column;
5714 g_object_notify (G_OBJECT (combo_box), "id-column");
5715 g_object_notify (G_OBJECT (combo_box), "active-id");
5720 * gtk_combo_box_get_id_column:
5721 * @combo_box: A #GtkComboBox
5723 * Returns the column which @combo_box is using to get string IDs
5726 * Return value: A column in the data source model of @combo_box.
5731 gtk_combo_box_get_id_column (GtkComboBox *combo_box)
5733 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), 0);
5735 return combo_box->priv->id_column;
5739 * gtk_combo_box_get_active_id:
5740 * @combo_box: a #GtkComboBox
5742 * Returns the ID of the active row of @combo_box. This value is taken
5743 * from the active row and the column specified by the #GtkComboBox:id-column
5744 * property of @combo_box (see gtk_combo_box_set_id_column()).
5746 * The returned value is an interned string which means that you can
5747 * compare the pointer by value to other interned strings and that you
5750 * If the #GtkComboBox:id-column property of @combo_box is not set, or if
5751 * no row is active, or if the active row has a %NULL ID value, then %NULL
5754 * Return value: the ID of the active row, or %NULL
5759 gtk_combo_box_get_active_id (GtkComboBox *combo_box)
5761 GtkTreeModel *model;
5765 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
5767 column = combo_box->priv->id_column;
5772 model = gtk_combo_box_get_model (combo_box);
5773 g_return_val_if_fail (gtk_tree_model_get_column_type (model, column) ==
5774 G_TYPE_STRING, NULL);
5776 if (gtk_combo_box_get_active_iter (combo_box, &iter))
5778 const gchar *interned;
5781 gtk_tree_model_get (model, &iter, column, &id, -1);
5782 interned = g_intern_string (id);
5792 * gtk_combo_box_set_active_id:
5793 * @combo_box: a #GtkComboBox
5794 * @active_id: (allow-none): the ID of the row to select, or %NULL
5796 * Changes the active row of @combo_box to the one that has an ID equal to
5797 * @active_id, or unsets the active row if @active_id is %NULL. Rows having
5798 * a %NULL ID string cannot be made active by this function.
5800 * If the #GtkComboBox:id-column property of @combo_box is unset or if no
5801 * row has the given ID then the function does nothing and returns %FALSE.
5803 * Returns: %TRUE if a row with a matching ID was found. If a %NULL
5804 * @active_id was given to unset the active row, the function
5805 * always returns %TRUE.
5810 gtk_combo_box_set_active_id (GtkComboBox *combo_box,
5811 const gchar *active_id)
5813 GtkTreeModel *model;
5815 gboolean match = FALSE;
5818 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
5820 if (active_id == NULL)
5822 gtk_combo_box_set_active (combo_box, -1);
5823 return TRUE; /* active row was successfully unset */
5826 column = combo_box->priv->id_column;
5831 model = gtk_combo_box_get_model (combo_box);
5832 g_return_val_if_fail (gtk_tree_model_get_column_type (model, column) ==
5833 G_TYPE_STRING, FALSE);
5835 if (gtk_tree_model_get_iter_first (model, &iter))
5839 gtk_tree_model_get (model, &iter, column, &id, -1);
5841 match = strcmp (id, active_id) == 0;
5846 gtk_combo_box_set_active_iter (combo_box, &iter);
5849 } while (gtk_tree_model_iter_next (model, &iter));