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, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
21 #include "gtkcombobox.h"
24 #include "gtkbindings.h"
25 #include "gtkcelllayout.h"
26 #include "gtkcellrenderertext.h"
27 #include "gtkcellview.h"
28 #include "gtkeventbox.h"
31 #include "gtkliststore.h"
33 #include "gtkmenuprivate.h"
34 #include "gtkmenushellprivate.h"
35 #include "gtkscrolledwindow.h"
36 #include "gtkseparatormenuitem.h"
37 #include "gtktearoffmenuitem.h"
38 #include "gtktogglebutton.h"
39 #include "gtktreeselection.h"
40 #include "gtkvseparator.h"
41 #include "gtkwindow.h"
42 #include "gtkcellareabox.h"
43 #include "gtktreemenu.h"
44 #include "gtkprivate.h"
45 #include "gtkentryprivate.h"
47 #include <gdk/gdkkeysyms.h>
49 #include <gobject/gvaluecollector.h>
54 #include "gtkmarshalers.h"
57 #include "gtktreeprivate.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 * For a simple list of textual choices, the model-view API of GtkComboBox
82 * can be a bit overwhelming. In this case, #GtkComboBoxText offers a
87 /* WELCOME, to THE house of evil code */
88 /* While debugging this evil code, I have learned that
89 * there are actually 4 modes to this widget, which can
90 * be characterized as follows
92 * 1) menu mode, no child added
95 * cell_view -> GtkCellView, regular child
96 * cell_view_frame -> NULL
97 * button -> GtkToggleButton set_parent to combo
98 * arrow -> GtkArrow set_parent to button
99 * separator -> GtkVSepator set_parent to button
100 * popup_widget -> GtkMenu
101 * popup_window -> NULL
102 * scrolled_window -> NULL
104 * 2) menu mode, child added
108 * cell_view_frame -> NULL
109 * button -> GtkToggleButton set_parent to combo
110 * arrow -> GtkArrow, child of button
112 * popup_widget -> GtkMenu
113 * popup_window -> NULL
114 * scrolled_window -> NULL
116 * 3) list mode, no child added
118 * tree_view -> GtkTreeView, child of scrolled_window
119 * cell_view -> GtkCellView, regular child
120 * cell_view_frame -> GtkFrame, set parent to combo
121 * button -> GtkToggleButton, set_parent to combo
122 * arrow -> GtkArrow, child of button
124 * popup_widget -> tree_view
125 * popup_window -> GtkWindow
126 * scrolled_window -> GtkScrolledWindow, child of popup_window
128 * 4) list mode, child added
130 * tree_view -> GtkTreeView, child of scrolled_window
132 * cell_view_frame -> NULL
133 * button -> GtkToggleButton, set_parent to combo
134 * arrow -> GtkArrow, child of button
136 * popup_widget -> tree_view
137 * popup_window -> GtkWindow
138 * scrolled_window -> GtkScrolledWindow, child of popup_window
143 static GObject *gtk_combo_box_constructor (GType type,
144 guint n_construct_properties,
145 GObjectConstructParam *construct_properties);
146 static void gtk_combo_box_dispose (GObject *object);
147 static void gtk_combo_box_finalize (GObject *object);
148 static void gtk_combo_box_set_property (GObject *object,
152 static void gtk_combo_box_get_property (GObject *object,
158 static void gtk_combo_box_destroy (GtkWidget *widget);
159 static void gtk_combo_box_state_changed (GtkWidget *widget,
160 GtkStateType previous);
161 static void gtk_combo_box_grab_focus (GtkWidget *widget);
162 static void gtk_combo_box_style_updated (GtkWidget *widget);
163 static void gtk_combo_box_size_allocate (GtkWidget *widget,
164 GtkAllocation *allocation);
165 static void gtk_combo_box_get_preferred_width (GtkWidget *widget,
168 static void gtk_combo_box_get_preferred_height (GtkWidget *widget,
171 static void gtk_combo_box_get_preferred_width_for_height (GtkWidget *widget,
175 static void gtk_combo_box_get_preferred_height_for_width (GtkWidget *widget,
179 static gboolean gtk_combo_box_draw (GtkWidget *widget,
181 static gboolean gtk_combo_box_scroll_event (GtkWidget *widget,
182 GdkEventScroll *event);
184 /* GtkContainerClass */
185 static void gtk_combo_box_add (GtkContainer *container,
187 static void gtk_combo_box_remove (GtkContainer *container,
189 static void gtk_combo_box_forall (GtkContainer *container,
190 gboolean include_internals,
191 GtkCallback callback,
192 gpointer callback_data);
194 /* GtkComboBoxClass (binding handlers) */
195 static void gtk_combo_box_real_move_active (GtkComboBox *combo_box,
196 GtkScrollType scroll);
197 static void gtk_combo_box_real_popup (GtkComboBox *combo_box);
198 static gboolean gtk_combo_box_real_popdown (GtkComboBox *combo_box);
201 /* GtkTreeModel signals */
202 static void gtk_combo_box_model_row_inserted (GtkTreeModel *model,
206 static void gtk_combo_box_model_row_deleted (GtkTreeModel *model,
210 static void gtk_combo_box_menu_show (GtkWidget *menu,
212 static void gtk_combo_box_menu_hide (GtkWidget *menu,
215 static void gtk_combo_box_set_popup_widget (GtkComboBox *combo_box,
218 static void gtk_combo_box_unset_model (GtkComboBox *combo_box);
219 static void gtk_combo_box_button_toggled (GtkWidget *widget,
221 static void gtk_combo_box_button_state_changed (GtkWidget *widget,
222 GtkStateType previous,
225 static void gtk_combo_box_set_active_internal (GtkComboBox *combo_box,
228 static void gtk_combo_box_check_appearance (GtkComboBox *combo_box);
230 static void gtk_combo_box_model_row_expanded (GtkTreeModel *model,
236 static void gtk_combo_box_list_position (GtkComboBox *combo_box,
241 static void gtk_combo_box_list_setup (GtkComboBox *combo_box);
242 static void gtk_combo_box_list_destroy (GtkComboBox *combo_box);
244 static gboolean gtk_combo_box_list_button_released (GtkWidget *widget,
245 GdkEventButton *event,
247 static gboolean gtk_combo_box_list_key_press (GtkWidget *widget,
250 static gboolean gtk_combo_box_list_enter_notify (GtkWidget *widget,
251 GdkEventCrossing *event,
253 static void gtk_combo_box_list_auto_scroll (GtkComboBox *combo,
256 static gboolean gtk_combo_box_list_scroll_timeout (GtkComboBox *combo);
257 static gboolean gtk_combo_box_list_button_pressed (GtkWidget *widget,
258 GdkEventButton *event,
261 static gboolean gtk_combo_box_list_select_func (GtkTreeSelection *selection,
264 gboolean path_currently_selected,
267 static void gtk_combo_box_list_popup_resize (GtkComboBox *combo_box);
270 static void gtk_combo_box_menu_setup (GtkComboBox *combo_box,
271 gboolean add_children);
272 static void gtk_combo_box_menu_destroy (GtkComboBox *combo_box);
273 static void gtk_combo_box_update_title (GtkComboBox *combo_box);
274 static gboolean gtk_combo_box_menu_button_press (GtkWidget *widget,
275 GdkEventButton *event,
277 static void gtk_combo_box_menu_activate (GtkWidget *menu,
279 GtkComboBox *combo_box);
280 static gboolean gtk_combo_box_menu_key_press (GtkWidget *widget,
283 static void gtk_combo_box_menu_popup (GtkComboBox *combo_box,
285 guint32 activate_time);
287 static void gtk_combo_box_menu_position_below (GtkMenu *menu,
292 static void gtk_combo_box_menu_position_over (GtkMenu *menu,
297 static void gtk_combo_box_menu_position (GtkMenu *menu,
303 static gboolean gtk_combo_box_mnemonic_activate (GtkWidget *widget,
304 gboolean group_cycling);
306 static void gtk_combo_box_child_show (GtkWidget *widget,
307 GtkComboBox *combo_box);
308 static void gtk_combo_box_child_hide (GtkWidget *widget,
309 GtkComboBox *combo_box);
311 /* GtkComboBox:has-entry callbacks */
312 static void gtk_combo_box_entry_contents_changed (GtkEntry *entry,
314 static void gtk_combo_box_entry_active_changed (GtkComboBox *combo_box,
316 /* GtkBuildableIface */
317 static void gtk_combo_box_buildable_init (GtkBuildableIface *iface);
318 static gboolean gtk_combo_box_buildable_custom_tag_start (GtkBuildable *buildable,
321 const gchar *tagname,
322 GMarkupParser *parser,
324 static void gtk_combo_box_buildable_custom_tag_end (GtkBuildable *buildable,
327 const gchar *tagname,
329 static GObject *gtk_combo_box_buildable_get_internal_child (GtkBuildable *buildable,
331 const gchar *childname);
333 /* GtkCellEditable */
334 static void gtk_combo_box_cell_editable_init (GtkCellEditableIface *iface);
335 static void gtk_combo_box_start_editing (GtkCellEditable *cell_editable,
338 /* GtkCellLayoutIface */
339 static void gtk_combo_box_cell_layout_init (GtkCellLayoutIface *iface);
340 static GtkCellArea *gtk_combo_box_cell_layout_get_area (GtkCellLayout *layout);
343 struct _GtkComboBoxPrivate
347 /* The cell area shared with the treeview or treemenu */
355 GtkShadowType shadow_type;
357 gint active; /* Only temporary */
358 GtkTreeRowReference *active_row;
361 /* The cellview displayed on the button */
362 GtkWidget *cell_view;
363 GtkWidget *cell_view_frame;
365 /* The treeview & column for list mode */
366 GtkWidget *tree_view;
367 GtkTreeViewColumn *column;
372 GtkWidget *separator;
374 GtkWidget *popup_widget;
375 GtkWidget *popup_window;
376 GtkWidget *scrolled_window;
381 guint activate_button;
382 guint32 activate_time;
384 guint resize_idle_id;
386 /* For "has-entry" specific behavior we track
387 * an automated cell renderer and text column */
389 GtkCellRenderer *text_renderer;
391 guint in_construction : 1;
392 guint popup_in_progress : 1;
393 guint popup_shown : 1;
394 guint add_tearoffs : 1;
396 guint is_cell_renderer : 1;
397 guint editing_canceled : 1;
398 guint auto_scroll : 1;
399 guint focus_on_click : 1;
400 guint button_sensitivity : 2;
402 guint popup_fixed_width : 1;
404 GtkTreeViewRowSeparatorFunc row_separator_func;
405 gpointer row_separator_data;
406 GDestroyNotify row_separator_destroy;
408 GdkDevice *grab_pointer;
409 GdkDevice *grab_keyboard;
411 gchar *tearoff_title;
427 PROP_ROW_SPAN_COLUMN,
428 PROP_COLUMN_SPAN_COLUMN,
435 PROP_BUTTON_SENSITIVITY,
436 PROP_EDITING_CANCELED,
438 PROP_ENTRY_TEXT_COLUMN,
439 PROP_POPUP_FIXED_WIDTH,
445 #define BONUS_PADDING 4
446 #define SCROLL_TIME 100
448 static guint combo_box_signals[LAST_SIGNAL] = {0,};
451 /* GtkBuildable method implementation */
452 static GtkBuildableIface *parent_buildable_iface;
455 G_DEFINE_TYPE_WITH_CODE (GtkComboBox, gtk_combo_box, GTK_TYPE_BIN,
456 G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
457 gtk_combo_box_cell_layout_init)
458 G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_EDITABLE,
459 gtk_combo_box_cell_editable_init)
460 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
461 gtk_combo_box_buildable_init))
466 gtk_combo_box_class_init (GtkComboBoxClass *klass)
468 GObjectClass *object_class;
469 GtkContainerClass *container_class;
470 GtkWidgetClass *widget_class;
471 GtkBindingSet *binding_set;
473 object_class = (GObjectClass *)klass;
474 object_class->constructor = gtk_combo_box_constructor;
475 object_class->dispose = gtk_combo_box_dispose;
476 object_class->finalize = gtk_combo_box_finalize;
477 object_class->set_property = gtk_combo_box_set_property;
478 object_class->get_property = gtk_combo_box_get_property;
480 widget_class = (GtkWidgetClass *)klass;
481 widget_class->draw = gtk_combo_box_draw;
482 widget_class->scroll_event = gtk_combo_box_scroll_event;
483 widget_class->mnemonic_activate = gtk_combo_box_mnemonic_activate;
484 widget_class->grab_focus = gtk_combo_box_grab_focus;
485 widget_class->style_updated = gtk_combo_box_style_updated;
486 widget_class->state_changed = gtk_combo_box_state_changed;
487 widget_class->size_allocate = gtk_combo_box_size_allocate;
488 widget_class->get_preferred_width = gtk_combo_box_get_preferred_width;
489 widget_class->get_preferred_height = gtk_combo_box_get_preferred_height;
490 widget_class->get_preferred_height_for_width = gtk_combo_box_get_preferred_height_for_width;
491 widget_class->get_preferred_width_for_height = gtk_combo_box_get_preferred_width_for_height;
492 widget_class->destroy = gtk_combo_box_destroy;
494 container_class = (GtkContainerClass *)klass;
495 container_class->forall = gtk_combo_box_forall;
496 container_class->add = gtk_combo_box_add;
497 container_class->remove = gtk_combo_box_remove;
501 * GtkComboBox::changed:
502 * @widget: the object which received the signal
504 * The changed signal is emitted when the active
505 * item is changed. The can be due to the user selecting
506 * a different item from the list, or due to a
507 * call to gtk_combo_box_set_active_iter().
508 * It will also be emitted while typing into the entry of a combo box
513 combo_box_signals[CHANGED] =
514 g_signal_new (I_("changed"),
515 G_OBJECT_CLASS_TYPE (klass),
517 G_STRUCT_OFFSET (GtkComboBoxClass, changed),
519 g_cclosure_marshal_VOID__VOID,
522 * GtkComboBox::move-active:
523 * @widget: the object that received the signal
524 * @scroll_type: a #GtkScrollType
526 * The ::move-active signal is a
527 * <link linkend="keybinding-signals">keybinding signal</link>
528 * which gets emitted to move the active selection.
532 combo_box_signals[MOVE_ACTIVE] =
533 g_signal_new_class_handler (I_("move-active"),
534 G_OBJECT_CLASS_TYPE (klass),
535 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
536 G_CALLBACK (gtk_combo_box_real_move_active),
538 g_cclosure_marshal_VOID__ENUM,
540 GTK_TYPE_SCROLL_TYPE);
543 * GtkComboBox::popup:
544 * @widget: the object that received the signal
546 * The ::popup signal is a
547 * <link linkend="keybinding-signals">keybinding signal</link>
548 * which gets emitted to popup the combo box list.
550 * The default binding for this signal is Alt+Down.
554 combo_box_signals[POPUP] =
555 g_signal_new_class_handler (I_("popup"),
556 G_OBJECT_CLASS_TYPE (klass),
557 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
558 G_CALLBACK (gtk_combo_box_real_popup),
560 g_cclosure_marshal_VOID__VOID,
563 * GtkComboBox::popdown:
564 * @button: the object which received the signal
566 * The ::popdown signal is a
567 * <link linkend="keybinding-signals">keybinding signal</link>
568 * which gets emitted to popdown the combo box list.
570 * The default bindings for this signal are Alt+Up and Escape.
574 combo_box_signals[POPDOWN] =
575 g_signal_new_class_handler (I_("popdown"),
576 G_OBJECT_CLASS_TYPE (klass),
577 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
578 G_CALLBACK (gtk_combo_box_real_popdown),
580 _gtk_marshal_BOOLEAN__VOID,
584 binding_set = gtk_binding_set_by_class (widget_class);
586 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Down, GDK_MOD1_MASK,
588 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Down, GDK_MOD1_MASK,
591 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Up, GDK_MOD1_MASK,
593 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Up, GDK_MOD1_MASK,
595 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0,
598 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Up, 0,
600 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_UP);
601 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Up, 0,
603 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_UP);
604 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Up, 0,
606 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_UP);
607 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Page_Up, 0,
609 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_UP);
610 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Home, 0,
612 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_START);
613 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Home, 0,
615 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_START);
617 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Down, 0,
619 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_DOWN);
620 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Down, 0,
622 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_DOWN);
623 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Down, 0,
625 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_DOWN);
626 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Page_Down, 0,
628 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_DOWN);
629 gtk_binding_entry_add_signal (binding_set, GDK_KEY_End, 0,
631 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_END);
632 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_End, 0,
634 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_END);
637 g_object_class_override_property (object_class,
638 PROP_EDITING_CANCELED,
644 * The model from which the combo box takes the values shown
649 g_object_class_install_property (object_class,
651 g_param_spec_object ("model",
652 P_("ComboBox model"),
653 P_("The model for the combo box"),
655 GTK_PARAM_READWRITE));
658 * GtkComboBox:wrap-width:
660 * If wrap-width is set to a positive value, the list will be
661 * displayed in multiple columns, the number of columns is
662 * determined by wrap-width.
666 g_object_class_install_property (object_class,
668 g_param_spec_int ("wrap-width",
670 P_("Wrap width for laying out the items in a grid"),
674 GTK_PARAM_READWRITE));
678 * GtkComboBox:row-span-column:
680 * If this is set to a non-negative value, it must be the index of a column
681 * of type %G_TYPE_INT in the model.
683 * The values of that column are used to determine how many rows a value in
684 * the list will span. Therefore, the values in the model column pointed to
685 * by this property must be greater than zero and not larger than wrap-width.
689 g_object_class_install_property (object_class,
690 PROP_ROW_SPAN_COLUMN,
691 g_param_spec_int ("row-span-column",
692 P_("Row span column"),
693 P_("TreeModel column containing the row span values"),
697 GTK_PARAM_READWRITE));
701 * GtkComboBox:column-span-column:
703 * If this is set to a non-negative value, it must be the index of a column
704 * of type %G_TYPE_INT in the model.
706 * The values of that column are used to determine how many columns a value
707 * in the list will span.
711 g_object_class_install_property (object_class,
712 PROP_COLUMN_SPAN_COLUMN,
713 g_param_spec_int ("column-span-column",
714 P_("Column span column"),
715 P_("TreeModel column containing the column span values"),
719 GTK_PARAM_READWRITE));
723 * GtkComboBox:active:
725 * The item which is currently active. If the model is a non-flat treemodel,
726 * and the active item is not an immediate child of the root of the tree,
727 * this property has the value
728 * <literal>gtk_tree_path_get_indices (path)[0]</literal>,
729 * where <literal>path</literal> is the #GtkTreePath of the active item.
733 g_object_class_install_property (object_class,
735 g_param_spec_int ("active",
737 P_("The item which is currently active"),
741 GTK_PARAM_READWRITE));
744 * GtkComboBox:add-tearoffs:
746 * The add-tearoffs property controls whether generated menus
747 * have tearoff menu items.
749 * Note that this only affects menu style combo boxes.
753 g_object_class_install_property (object_class,
755 g_param_spec_boolean ("add-tearoffs",
756 P_("Add tearoffs to menus"),
757 P_("Whether dropdowns should have a tearoff menu item"),
759 GTK_PARAM_READWRITE));
762 * GtkComboBox:has-frame:
764 * The has-frame property controls whether a frame
765 * is drawn around the entry.
769 g_object_class_install_property (object_class,
771 g_param_spec_boolean ("has-frame",
773 P_("Whether the combo box draws a frame around the child"),
775 GTK_PARAM_READWRITE));
777 g_object_class_install_property (object_class,
779 g_param_spec_boolean ("focus-on-click",
780 P_("Focus on click"),
781 P_("Whether the combo box grabs focus when it is clicked with the mouse"),
783 GTK_PARAM_READWRITE));
786 * GtkComboBox:tearoff-title:
788 * A title that may be displayed by the window manager
789 * when the popup is torn-off.
793 g_object_class_install_property (object_class,
795 g_param_spec_string ("tearoff-title",
797 P_("A title that may be displayed by the window manager when the popup is torn-off"),
799 GTK_PARAM_READWRITE));
803 * GtkComboBox:popup-shown:
805 * Whether the combo boxes dropdown is popped up.
806 * Note that this property is mainly useful, because
807 * it allows you to connect to notify::popup-shown.
811 g_object_class_install_property (object_class,
813 g_param_spec_boolean ("popup-shown",
815 P_("Whether the combo's dropdown is shown"),
817 GTK_PARAM_READABLE));
821 * GtkComboBox:button-sensitivity:
823 * Whether the dropdown button is sensitive when
824 * the model is empty.
828 g_object_class_install_property (object_class,
829 PROP_BUTTON_SENSITIVITY,
830 g_param_spec_enum ("button-sensitivity",
831 P_("Button Sensitivity"),
832 P_("Whether the dropdown button is sensitive when the model is empty"),
833 GTK_TYPE_SENSITIVITY_TYPE,
834 GTK_SENSITIVITY_AUTO,
835 GTK_PARAM_READWRITE));
838 * GtkComboBox:has-entry:
840 * Whether the combo box has an entry.
844 g_object_class_install_property (object_class,
846 g_param_spec_boolean ("has-entry",
848 P_("Whether combo box has an entry"),
850 GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
853 * GtkComboBox:entry-text-column:
855 * The column in the combo box's model to associate with strings from the entry
856 * if the combo was created with #GtkComboBox:has-entry = %TRUE.
860 g_object_class_install_property (object_class,
861 PROP_ENTRY_TEXT_COLUMN,
862 g_param_spec_int ("entry-text-column",
863 P_("Entry Text Column"),
864 P_("The column in the combo box's model to associate "
865 "with strings from the entry if the combo was "
866 "created with #GtkComboBox:has-entry = %TRUE"),
868 GTK_PARAM_READWRITE));
871 * GtkComboBox:id-column:
873 * The column in the combo box's model that provides string
874 * IDs for the values in the model, if != -1.
878 g_object_class_install_property (object_class,
880 g_param_spec_int ("id-column",
882 P_("The column in the combo box's model that provides "
883 "string IDs for the values in the model"),
885 GTK_PARAM_READWRITE));
888 * GtkComboBox:active-id:
890 * The value of the ID column of the active row.
894 g_object_class_install_property (object_class,
896 g_param_spec_string ("active-id",
898 P_("The value of the id column "
899 "for the active row"),
900 NULL, GTK_PARAM_READWRITE));
902 * GtkComboBox:cell-area:
904 * The #GtkCellArea used to layout cell renderers for this combo box.
908 g_object_class_install_property (object_class,
910 g_param_spec_object ("cell-area",
912 P_("The GtkCellArea used to layout cells"),
914 GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
917 * GtkComboBox:popup-fixed-width:
919 * Whether the popup's width should be a fixed width matching the
920 * allocated width of the combo box.
924 g_object_class_install_property (object_class,
925 PROP_POPUP_FIXED_WIDTH,
926 g_param_spec_boolean ("popup-fixed-width",
927 P_("Popup Fixed Width"),
928 P_("Whether the popup's width should be a "
929 "fixed width matching the allocated width "
932 GTK_PARAM_READWRITE));
934 gtk_widget_class_install_style_property (widget_class,
935 g_param_spec_boolean ("appears-as-list",
936 P_("Appears as list"),
937 P_("Whether dropdowns should look like lists rather than menus"),
939 GTK_PARAM_READABLE));
942 * GtkComboBox:arrow-size:
944 * Sets the minimum size of the arrow in the combo box. Note
945 * that the arrow size is coupled to the font size, so in case
946 * a larger font is used, the arrow will be larger than set
951 gtk_widget_class_install_style_property (widget_class,
952 g_param_spec_int ("arrow-size",
954 P_("The minimum size of the arrow in the combo box"),
958 GTK_PARAM_READABLE));
961 * GtkComboBox:shadow-type:
963 * Which kind of shadow to draw around the combo box.
967 gtk_widget_class_install_style_property (widget_class,
968 g_param_spec_enum ("shadow-type",
970 P_("Which kind of shadow to draw around the combo box"),
971 GTK_TYPE_SHADOW_TYPE,
973 GTK_PARAM_READABLE));
975 g_type_class_add_private (object_class, sizeof (GtkComboBoxPrivate));
979 gtk_combo_box_init (GtkComboBox *combo_box)
981 GtkComboBoxPrivate *priv;
983 combo_box->priv = G_TYPE_INSTANCE_GET_PRIVATE (combo_box,
986 priv = combo_box->priv;
988 priv->wrap_width = 0;
991 priv->active_row = NULL;
992 priv->col_column = -1;
993 priv->row_column = -1;
995 priv->popup_shown = FALSE;
996 priv->add_tearoffs = FALSE;
997 priv->has_frame = TRUE;
998 priv->is_cell_renderer = FALSE;
999 priv->editing_canceled = FALSE;
1000 priv->auto_scroll = FALSE;
1001 priv->focus_on_click = TRUE;
1002 priv->button_sensitivity = GTK_SENSITIVITY_AUTO;
1003 priv->has_entry = FALSE;
1004 priv->popup_fixed_width = TRUE;
1006 priv->text_column = -1;
1007 priv->text_renderer = NULL;
1008 priv->id_column = -1;
1011 /******************************************************
1013 ******************************************************/
1015 gtk_combo_box_constructor (GType type,
1016 guint n_construct_properties,
1017 GObjectConstructParam *construct_properties)
1020 GtkComboBox *combo_box;
1021 GtkComboBoxPrivate *priv;
1022 GtkStyleContext *context;
1024 object = G_OBJECT_CLASS (gtk_combo_box_parent_class)->constructor
1025 (type, n_construct_properties, construct_properties);
1027 combo_box = GTK_COMBO_BOX (object);
1028 priv = combo_box->priv;
1032 GtkCellArea *area = gtk_cell_area_box_new ();
1034 priv->area = g_object_ref_sink (area);
1037 priv->cell_view = gtk_cell_view_new_with_context (priv->area, NULL);
1038 gtk_cell_view_set_fit_model (GTK_CELL_VIEW (priv->cell_view), TRUE);
1039 gtk_cell_view_set_model (GTK_CELL_VIEW (priv->cell_view), priv->model);
1040 gtk_widget_set_parent (priv->cell_view, GTK_WIDGET (combo_box));
1041 _gtk_bin_set_child (GTK_BIN (combo_box), priv->cell_view);
1042 gtk_widget_show (priv->cell_view);
1044 gtk_combo_box_check_appearance (combo_box);
1046 context = gtk_widget_get_style_context (GTK_WIDGET (combo_box));
1047 gtk_style_context_add_class (context, GTK_STYLE_CLASS_BUTTON);
1049 if (priv->has_entry)
1053 entry = gtk_entry_new ();
1054 gtk_widget_show (entry);
1055 gtk_container_add (GTK_CONTAINER (combo_box), entry);
1057 priv->text_renderer = gtk_cell_renderer_text_new ();
1058 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box),
1059 priv->text_renderer, TRUE);
1061 gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), -1);
1063 g_signal_connect (combo_box, "changed",
1064 G_CALLBACK (gtk_combo_box_entry_active_changed), NULL);
1071 gtk_combo_box_dispose(GObject* object)
1073 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
1075 if (GTK_IS_MENU (combo_box->priv->popup_widget))
1077 gtk_combo_box_menu_destroy (combo_box);
1078 gtk_menu_detach (GTK_MENU (combo_box->priv->popup_widget));
1079 combo_box->priv->popup_widget = NULL;
1082 if (combo_box->priv->area)
1084 g_object_unref (combo_box->priv->area);
1085 combo_box->priv->area = NULL;
1088 G_OBJECT_CLASS (gtk_combo_box_parent_class)->dispose (object);
1092 gtk_combo_box_finalize (GObject *object)
1094 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
1096 if (GTK_IS_TREE_VIEW (combo_box->priv->tree_view))
1097 gtk_combo_box_list_destroy (combo_box);
1099 if (combo_box->priv->popup_window)
1100 gtk_widget_destroy (combo_box->priv->popup_window);
1102 gtk_combo_box_unset_model (combo_box);
1104 g_free (combo_box->priv->tearoff_title);
1106 G_OBJECT_CLASS (gtk_combo_box_parent_class)->finalize (object);
1110 gtk_combo_box_set_property (GObject *object,
1112 const GValue *value,
1115 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
1121 gtk_combo_box_set_model (combo_box, g_value_get_object (value));
1124 case PROP_WRAP_WIDTH:
1125 gtk_combo_box_set_wrap_width (combo_box, g_value_get_int (value));
1128 case PROP_ROW_SPAN_COLUMN:
1129 gtk_combo_box_set_row_span_column (combo_box, g_value_get_int (value));
1132 case PROP_COLUMN_SPAN_COLUMN:
1133 gtk_combo_box_set_column_span_column (combo_box, g_value_get_int (value));
1137 gtk_combo_box_set_active (combo_box, g_value_get_int (value));
1140 case PROP_ADD_TEAROFFS:
1141 gtk_combo_box_set_add_tearoffs (combo_box, g_value_get_boolean (value));
1144 case PROP_HAS_FRAME:
1145 combo_box->priv->has_frame = g_value_get_boolean (value);
1147 if (combo_box->priv->has_entry)
1151 child = gtk_bin_get_child (GTK_BIN (combo_box));
1153 gtk_entry_set_has_frame (GTK_ENTRY (child),
1154 combo_box->priv->has_frame);
1159 case PROP_FOCUS_ON_CLICK:
1160 gtk_combo_box_set_focus_on_click (combo_box,
1161 g_value_get_boolean (value));
1164 case PROP_TEAROFF_TITLE:
1165 gtk_combo_box_set_title (combo_box, g_value_get_string (value));
1168 case PROP_POPUP_SHOWN:
1169 if (g_value_get_boolean (value))
1170 gtk_combo_box_popup (combo_box);
1172 gtk_combo_box_popdown (combo_box);
1175 case PROP_BUTTON_SENSITIVITY:
1176 gtk_combo_box_set_button_sensitivity (combo_box,
1177 g_value_get_enum (value));
1180 case PROP_POPUP_FIXED_WIDTH:
1181 gtk_combo_box_set_popup_fixed_width (combo_box,
1182 g_value_get_boolean (value));
1185 case PROP_EDITING_CANCELED:
1186 combo_box->priv->editing_canceled = g_value_get_boolean (value);
1189 case PROP_HAS_ENTRY:
1190 combo_box->priv->has_entry = g_value_get_boolean (value);
1193 case PROP_ENTRY_TEXT_COLUMN:
1194 gtk_combo_box_set_entry_text_column (combo_box, g_value_get_int (value));
1197 case PROP_ID_COLUMN:
1198 gtk_combo_box_set_id_column (combo_box, g_value_get_int (value));
1201 case PROP_ACTIVE_ID:
1202 gtk_combo_box_set_active_id (combo_box, g_value_get_string (value));
1205 case PROP_CELL_AREA:
1206 /* Construct-only, can only be assigned once */
1207 area = g_value_get_object (value);
1210 combo_box->priv->area = g_object_ref_sink (area);
1214 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1220 gtk_combo_box_get_property (GObject *object,
1225 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
1226 GtkComboBoxPrivate *priv = combo_box->priv;
1231 g_value_set_object (value, combo_box->priv->model);
1234 case PROP_WRAP_WIDTH:
1235 g_value_set_int (value, combo_box->priv->wrap_width);
1238 case PROP_ROW_SPAN_COLUMN:
1239 g_value_set_int (value, combo_box->priv->row_column);
1242 case PROP_COLUMN_SPAN_COLUMN:
1243 g_value_set_int (value, combo_box->priv->col_column);
1247 g_value_set_int (value, gtk_combo_box_get_active (combo_box));
1250 case PROP_ADD_TEAROFFS:
1251 g_value_set_boolean (value, gtk_combo_box_get_add_tearoffs (combo_box));
1254 case PROP_HAS_FRAME:
1255 g_value_set_boolean (value, combo_box->priv->has_frame);
1258 case PROP_FOCUS_ON_CLICK:
1259 g_value_set_boolean (value, combo_box->priv->focus_on_click);
1262 case PROP_TEAROFF_TITLE:
1263 g_value_set_string (value, gtk_combo_box_get_title (combo_box));
1266 case PROP_POPUP_SHOWN:
1267 g_value_set_boolean (value, combo_box->priv->popup_shown);
1270 case PROP_BUTTON_SENSITIVITY:
1271 g_value_set_enum (value, combo_box->priv->button_sensitivity);
1274 case PROP_POPUP_FIXED_WIDTH:
1275 g_value_set_boolean (value, combo_box->priv->popup_fixed_width);
1278 case PROP_EDITING_CANCELED:
1279 g_value_set_boolean (value, priv->editing_canceled);
1282 case PROP_HAS_ENTRY:
1283 g_value_set_boolean (value, priv->has_entry);
1286 case PROP_ENTRY_TEXT_COLUMN:
1287 g_value_set_int (value, priv->text_column);
1290 case PROP_ID_COLUMN:
1291 g_value_set_int (value, priv->id_column);
1294 case PROP_ACTIVE_ID:
1295 g_value_set_string (value, gtk_combo_box_get_active_id (combo_box));
1298 case PROP_CELL_AREA:
1299 g_value_set_object (value, priv->area);
1303 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1308 /******************************************************
1310 ******************************************************/
1312 gtk_combo_box_draw (GtkWidget *widget,
1315 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1316 GtkComboBoxPrivate *priv = combo_box->priv;
1318 if (priv->shadow_type != GTK_SHADOW_NONE)
1320 GtkStyleContext *context;
1321 GtkStateFlags state;
1323 context = gtk_widget_get_style_context (widget);
1324 state = gtk_widget_get_state_flags (widget);
1325 gtk_style_context_set_state (context, state);
1327 gtk_render_background (context, cr, 0, 0,
1328 gtk_widget_get_allocated_width (widget),
1329 gtk_widget_get_allocated_height (widget));
1330 gtk_render_frame (context, cr, 0, 0,
1331 gtk_widget_get_allocated_width (widget),
1332 gtk_widget_get_allocated_height (widget));
1335 gtk_container_propagate_draw (GTK_CONTAINER (widget),
1338 if (priv->tree_view && priv->cell_view_frame)
1340 gtk_container_propagate_draw (GTK_CONTAINER (widget),
1341 priv->cell_view_frame, cr);
1344 gtk_container_propagate_draw (GTK_CONTAINER (widget),
1345 gtk_bin_get_child (GTK_BIN (widget)),
1362 path_visible (GtkTreeView *view,
1368 /* Note that we rely on the fact that collapsed rows don't have nodes
1370 return _gtk_tree_view_find_node (view, path, &tree, &node);
1374 tree_column_row_is_sensitive (GtkComboBox *combo_box,
1377 GtkComboBoxPrivate *priv = combo_box->priv;
1378 GList *cells, *list;
1384 if (priv->row_separator_func)
1386 if (priv->row_separator_func (priv->model, iter,
1387 priv->row_separator_data))
1391 gtk_tree_view_column_cell_set_cell_data (priv->column,
1393 iter, FALSE, FALSE);
1395 cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (priv->column));
1398 for (list = cells; list; list = list->next)
1400 g_object_get (list->data, "sensitive", &sensitive, NULL);
1405 g_list_free (cells);
1411 tree_next_func (GtkTreeModel *model,
1416 SearchData *search_data = (SearchData *)data;
1418 if (search_data->found)
1420 if (!tree_column_row_is_sensitive (search_data->combo, iter))
1423 if (search_data->visible &&
1424 !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
1427 search_data->set = TRUE;
1428 search_data->iter = *iter;
1433 if (gtk_tree_path_compare (path, search_data->path) == 0)
1434 search_data->found = TRUE;
1440 tree_next (GtkComboBox *combo,
1441 GtkTreeModel *model,
1446 SearchData search_data;
1448 search_data.combo = combo;
1449 search_data.path = gtk_tree_model_get_path (model, iter);
1450 search_data.visible = visible;
1451 search_data.found = FALSE;
1452 search_data.set = FALSE;
1454 gtk_tree_model_foreach (model, tree_next_func, &search_data);
1456 *next = search_data.iter;
1458 gtk_tree_path_free (search_data.path);
1460 return search_data.set;
1464 tree_prev_func (GtkTreeModel *model,
1469 SearchData *search_data = (SearchData *)data;
1471 if (gtk_tree_path_compare (path, search_data->path) == 0)
1473 search_data->found = TRUE;
1477 if (!tree_column_row_is_sensitive (search_data->combo, iter))
1480 if (search_data->visible &&
1481 !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
1484 search_data->set = TRUE;
1485 search_data->iter = *iter;
1491 tree_prev (GtkComboBox *combo,
1492 GtkTreeModel *model,
1497 SearchData search_data;
1499 search_data.combo = combo;
1500 search_data.path = gtk_tree_model_get_path (model, iter);
1501 search_data.visible = visible;
1502 search_data.found = FALSE;
1503 search_data.set = FALSE;
1505 gtk_tree_model_foreach (model, tree_prev_func, &search_data);
1507 *prev = search_data.iter;
1509 gtk_tree_path_free (search_data.path);
1511 return search_data.set;
1515 tree_last_func (GtkTreeModel *model,
1520 SearchData *search_data = (SearchData *)data;
1522 if (!tree_column_row_is_sensitive (search_data->combo, iter))
1525 /* Note that we rely on the fact that collapsed rows don't have nodes
1527 if (search_data->visible &&
1528 !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
1531 search_data->set = TRUE;
1532 search_data->iter = *iter;
1538 tree_last (GtkComboBox *combo,
1539 GtkTreeModel *model,
1543 SearchData search_data;
1545 search_data.combo = combo;
1546 search_data.visible = visible;
1547 search_data.set = FALSE;
1549 gtk_tree_model_foreach (model, tree_last_func, &search_data);
1551 *last = search_data.iter;
1553 return search_data.set;
1558 tree_first_func (GtkTreeModel *model,
1563 SearchData *search_data = (SearchData *)data;
1565 if (!tree_column_row_is_sensitive (search_data->combo, iter))
1568 if (search_data->visible &&
1569 !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
1572 search_data->set = TRUE;
1573 search_data->iter = *iter;
1579 tree_first (GtkComboBox *combo,
1580 GtkTreeModel *model,
1584 SearchData search_data;
1586 search_data.combo = combo;
1587 search_data.visible = visible;
1588 search_data.set = FALSE;
1590 gtk_tree_model_foreach (model, tree_first_func, &search_data);
1592 *first = search_data.iter;
1594 return search_data.set;
1598 gtk_combo_box_scroll_event (GtkWidget *widget,
1599 GdkEventScroll *event)
1601 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1604 GtkTreeIter new_iter;
1606 if (!gtk_combo_box_get_active_iter (combo_box, &iter))
1609 if (event->direction == GDK_SCROLL_UP)
1610 found = tree_prev (combo_box, combo_box->priv->model,
1611 &iter, &new_iter, FALSE);
1613 found = tree_next (combo_box, combo_box->priv->model,
1614 &iter, &new_iter, FALSE);
1617 gtk_combo_box_set_active_iter (combo_box, &new_iter);
1624 gtk_combo_box_mnemonic_activate (GtkWidget *widget,
1625 gboolean group_cycling)
1627 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1629 if (combo_box->priv->has_entry)
1633 child = gtk_bin_get_child (GTK_BIN (combo_box));
1635 gtk_widget_grab_focus (child);
1638 gtk_widget_grab_focus (combo_box->priv->button);
1644 gtk_combo_box_grab_focus (GtkWidget *widget)
1646 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1648 if (combo_box->priv->has_entry)
1652 child = gtk_bin_get_child (GTK_BIN (combo_box));
1654 gtk_widget_grab_focus (child);
1657 gtk_widget_grab_focus (combo_box->priv->button);
1661 gtk_combo_box_state_changed (GtkWidget *widget,
1662 GtkStateType previous)
1664 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1665 GtkComboBoxPrivate *priv = combo_box->priv;
1667 if (gtk_widget_get_realized (widget))
1669 if (priv->tree_view && priv->cell_view)
1671 GtkStyleContext *context;
1672 GtkStateFlags state;
1675 context = gtk_widget_get_style_context (widget);
1676 state = gtk_widget_get_state_flags (widget);
1678 gtk_style_context_get (context, state,
1679 "background-color", &color,
1682 gtk_cell_view_set_background_rgba (GTK_CELL_VIEW (priv->cell_view), color);
1683 gdk_rgba_free (color);
1687 gtk_widget_queue_draw (widget);
1691 gtk_combo_box_button_state_changed (GtkWidget *widget,
1692 GtkStateType previous,
1695 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
1696 GtkComboBoxPrivate *priv = combo_box->priv;
1698 if (gtk_widget_get_realized (widget))
1700 if (!priv->tree_view && priv->cell_view)
1702 if ((gtk_widget_get_state (widget) == GTK_STATE_INSENSITIVE) !=
1703 (gtk_widget_get_state (priv->cell_view) == GTK_STATE_INSENSITIVE))
1704 gtk_widget_set_sensitive (priv->cell_view, gtk_widget_get_sensitive (widget));
1706 gtk_widget_set_state (priv->cell_view,
1707 gtk_widget_get_state (widget));
1711 gtk_widget_queue_draw (widget);
1715 gtk_combo_box_check_appearance (GtkComboBox *combo_box)
1717 GtkComboBoxPrivate *priv = combo_box->priv;
1718 gboolean appears_as_list;
1720 /* if wrap_width > 0, then we are in grid-mode and forced to use
1723 if (priv->wrap_width)
1724 appears_as_list = FALSE;
1726 gtk_widget_style_get (GTK_WIDGET (combo_box),
1727 "appears-as-list", &appears_as_list,
1730 if (appears_as_list)
1732 /* Destroy all the menu mode widgets, if they exist. */
1733 if (GTK_IS_MENU (priv->popup_widget))
1734 gtk_combo_box_menu_destroy (combo_box);
1736 /* Create the list mode widgets, if they don't already exist. */
1737 if (!GTK_IS_TREE_VIEW (priv->tree_view))
1738 gtk_combo_box_list_setup (combo_box);
1742 /* Destroy all the list mode widgets, if they exist. */
1743 if (GTK_IS_TREE_VIEW (priv->tree_view))
1744 gtk_combo_box_list_destroy (combo_box);
1746 /* Create the menu mode widgets, if they don't already exist. */
1747 if (!GTK_IS_MENU (priv->popup_widget))
1748 gtk_combo_box_menu_setup (combo_box, TRUE);
1751 gtk_widget_style_get (GTK_WIDGET (combo_box),
1752 "shadow-type", &priv->shadow_type,
1757 gtk_combo_box_style_updated (GtkWidget *widget)
1759 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1760 GtkComboBoxPrivate *priv = combo_box->priv;
1763 gtk_combo_box_check_appearance (combo_box);
1765 if (priv->tree_view && priv->cell_view)
1767 GtkStyleContext *context;
1770 context = gtk_widget_get_style_context (widget);
1771 gtk_style_context_get (context, 0,
1772 "background-color", &color,
1775 gtk_cell_view_set_background_rgba (GTK_CELL_VIEW (priv->cell_view), color);
1776 gdk_rgba_free (color);
1779 child = gtk_bin_get_child (GTK_BIN (combo_box));
1780 if (GTK_IS_ENTRY (child))
1781 g_object_set (child, "shadow-type",
1782 GTK_SHADOW_NONE == priv->shadow_type ?
1783 GTK_SHADOW_IN : GTK_SHADOW_NONE, NULL);
1787 gtk_combo_box_destroy (GtkWidget *widget)
1789 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1791 if (combo_box->priv->popup_idle_id > 0)
1793 g_source_remove (combo_box->priv->popup_idle_id);
1794 combo_box->priv->popup_idle_id = 0;
1797 gtk_combo_box_popdown (combo_box);
1799 if (combo_box->priv->row_separator_destroy)
1800 combo_box->priv->row_separator_destroy (combo_box->priv->row_separator_data);
1802 combo_box->priv->row_separator_func = NULL;
1803 combo_box->priv->row_separator_data = NULL;
1804 combo_box->priv->row_separator_destroy = NULL;
1806 GTK_WIDGET_CLASS (gtk_combo_box_parent_class)->destroy (widget);
1807 combo_box->priv->cell_view = NULL;
1811 get_widget_border (GtkWidget *widget,
1814 GtkStyleContext *context;
1815 GtkBorder *border_width;
1817 context = gtk_widget_get_style_context (widget);
1819 gtk_style_context_get (context,
1820 gtk_widget_get_state_flags (widget),
1821 "border-width", &border_width,
1824 *border = *border_width;
1825 gtk_border_free (border_width);
1829 gtk_combo_box_get_preferred_width (GtkWidget *widget,
1833 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1834 GtkComboBoxPrivate *priv = combo_box->priv;
1835 gint focus_width, focus_pad;
1836 gint font_size, arrow_size;
1837 PangoContext *context;
1838 PangoFontMetrics *metrics;
1839 PangoFontDescription *font_desc;
1841 gint minimum_width = 0, natural_width = 0;
1842 gint child_min, child_nat;
1843 GtkStyleContext *style_context;
1844 GtkStateFlags state;
1847 child = gtk_bin_get_child (GTK_BIN (widget));
1850 gtk_widget_get_preferred_width (child, &child_min, &child_nat);
1852 gtk_widget_style_get (GTK_WIDGET (widget),
1853 "focus-line-width", &focus_width,
1854 "focus-padding", &focus_pad,
1855 "arrow-size", &arrow_size,
1858 style_context = gtk_widget_get_style_context (widget);
1859 state = gtk_widget_get_state_flags (widget);
1861 gtk_style_context_get (style_context, state,
1863 "border-width", &border,
1866 context = gtk_widget_get_pango_context (GTK_WIDGET (widget));
1867 metrics = pango_context_get_metrics (context, font_desc,
1868 pango_context_get_language (context));
1869 font_size = PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) +
1870 pango_font_metrics_get_descent (metrics));
1871 pango_font_metrics_unref (metrics);
1872 pango_font_description_free (font_desc);
1874 arrow_size = MAX (arrow_size, font_size);
1876 gtk_widget_set_size_request (priv->arrow, arrow_size, arrow_size);
1878 if (!priv->tree_view)
1881 if (priv->cell_view)
1883 gint sep_width, arrow_width;
1884 gint border_width, xpad;
1885 GtkBorder button_border;
1887 border_width = gtk_container_get_border_width (GTK_CONTAINER (combo_box));
1888 get_widget_border (priv->button, &button_border);
1890 gtk_widget_get_preferred_width (priv->separator, &sep_width, NULL);
1891 gtk_widget_get_preferred_width (priv->arrow, &arrow_width, NULL);
1893 xpad = 2 * (border_width + focus_width + focus_pad) +
1894 button_border.left + button_border.right;
1896 minimum_width = child_min + sep_width + arrow_width + xpad;
1897 natural_width = child_nat + sep_width + arrow_width + xpad;
1901 gint but_width, but_nat_width;
1903 gtk_widget_get_preferred_width (priv->button,
1904 &but_width, &but_nat_width);
1906 minimum_width = child_min + but_width;
1907 natural_width = child_nat + but_nat_width;
1913 gint button_width, button_nat_width;
1915 /* sample + frame */
1916 minimum_width = child_min;
1917 natural_width = child_nat;
1919 minimum_width += 2 * focus_width;
1920 natural_width += 2 * focus_width;
1922 if (priv->cell_view_frame)
1924 if (priv->has_frame)
1926 gint border_width, xpad;
1927 GtkBorder frame_border;
1929 border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->cell_view_frame));
1930 get_widget_border (priv->cell_view_frame, &frame_border);
1931 xpad = (2 * border_width) + frame_border.left + frame_border.right;
1933 minimum_width += xpad;
1934 natural_width += xpad;
1939 gtk_widget_get_preferred_width (priv->button,
1940 &button_width, &button_nat_width);
1942 minimum_width += button_width;
1943 natural_width += button_nat_width;
1946 minimum_width += border->left + border->right;
1947 natural_width += border->left + border->right;
1948 gtk_border_free (border);
1951 *minimum_size = minimum_width;
1954 *natural_size = natural_width;
1958 gtk_combo_box_get_preferred_height (GtkWidget *widget,
1964 /* Combo box is height-for-width only
1965 * (so we always just reserve enough height for the minimum width) */
1966 GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, &min_width, NULL);
1967 GTK_WIDGET_GET_CLASS (widget)->get_preferred_height_for_width (widget, min_width, minimum_size, natural_size);
1971 gtk_combo_box_get_preferred_width_for_height (GtkWidget *widget,
1976 /* Combo box is height-for-width only
1977 * (so we assume we always reserved enough height for the minimum width) */
1978 GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, minimum_size, natural_size);
1983 gtk_combo_box_get_preferred_height_for_width (GtkWidget *widget,
1988 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1989 GtkComboBoxPrivate *priv = combo_box->priv;
1990 gint focus_width, focus_pad;
1991 gint min_height = 0, nat_height = 0;
1996 gtk_widget_style_get (GTK_WIDGET (widget),
1997 "focus-line-width", &focus_width,
1998 "focus-padding", &focus_pad,
2001 child = gtk_bin_get_child (GTK_BIN (widget));
2003 get_widget_border (widget, &border);
2004 size = avail_size - border.left;
2006 if (!priv->tree_view)
2009 if (priv->cell_view)
2011 /* calculate x/y padding and separator/arrow size */
2012 gint sep_width, arrow_width, sep_height, arrow_height;
2013 gint border_width, xpad, ypad;
2014 GtkBorder button_border;
2016 border_width = gtk_container_get_border_width (GTK_CONTAINER (combo_box));
2017 get_widget_border (priv->button, &button_border);
2019 gtk_widget_get_preferred_width (priv->separator, &sep_width, NULL);
2020 gtk_widget_get_preferred_width (priv->arrow, &arrow_width, NULL);
2021 gtk_widget_get_preferred_height_for_width (priv->separator,
2022 sep_width, &sep_height, NULL);
2023 gtk_widget_get_preferred_height_for_width (priv->arrow,
2024 arrow_width, &arrow_height, NULL);
2026 xpad = 2 * (border_width + focus_width + focus_pad) +
2027 button_border.left + button_border.right;
2028 ypad = 2 * (border_width + focus_width + focus_pad) +
2029 button_border.top + button_border.bottom;
2031 size -= sep_width + arrow_width + xpad;
2033 /* Get height-for-width of the child widget, usually a GtkCellArea calculating
2034 * and fitting the whole treemodel */
2035 gtk_widget_get_preferred_height_for_width (child, size, &min_height, &nat_height);
2037 arrow_height = MAX (arrow_height, sep_height);
2038 min_height = MAX (min_height, arrow_height);
2039 nat_height = MAX (nat_height, arrow_height);
2046 /* there is a custom child widget inside (no priv->cell_view) */
2047 gint but_width, but_height;
2049 gtk_widget_get_preferred_width (priv->button, &but_width, NULL);
2050 gtk_widget_get_preferred_height_for_width (priv->button,
2051 but_width, &but_height, NULL);
2055 /* Get height-for-width of the child widget, usually a GtkCellArea calculating
2056 * and fitting the whole treemodel */
2057 gtk_widget_get_preferred_height_for_width (child, size, &min_height, &nat_height);
2059 min_height = MAX (min_height, but_height);
2060 nat_height = MAX (nat_height, but_height);
2066 gint but_width, but_height;
2067 gint xpad = 0, ypad = 0;
2069 gtk_widget_get_preferred_width (priv->button, &but_width, NULL);
2070 gtk_widget_get_preferred_height_for_width (priv->button,
2071 but_width, &but_height, NULL);
2073 if (priv->cell_view_frame && priv->has_frame)
2075 GtkBorder frame_border;
2078 border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->cell_view_frame));
2079 get_widget_border (GTK_WIDGET (priv->cell_view_frame), &frame_border);
2081 xpad = (2 * border_width) + border.left + frame_border.right;
2082 ypad = (2 * border_width) + border.top + frame_border.bottom;
2086 size -= 2 * focus_width;
2089 /* Get height-for-width of the child widget, usually a GtkCellArea calculating
2090 * and fitting the whole treemodel */
2091 gtk_widget_get_preferred_height_for_width (child, size, &min_height, &nat_height);
2093 min_height = MAX (min_height, but_height);
2094 nat_height = MAX (nat_height, but_height);
2100 min_height += border.top + border.bottom;
2101 nat_height += border.top + border.bottom;
2104 *minimum_size = min_height;
2107 *natural_size = nat_height;
2110 #define GTK_COMBO_BOX_SIZE_ALLOCATE_BUTTON \
2111 gtk_widget_get_preferred_size (combo_box->priv->button, \
2115 child.x = allocation->x + border.right; \
2117 child.x = allocation->x + allocation->width - req.width - border.left; \
2119 child.y = allocation->y + border.top; \
2120 child.width = req.width; \
2121 child.height = allocation->height - (border.top + border.bottom); \
2122 child.width = MAX (1, child.width); \
2123 child.height = MAX (1, child.height); \
2125 gtk_widget_size_allocate (combo_box->priv->button, &child);
2129 gtk_combo_box_size_allocate (GtkWidget *widget,
2130 GtkAllocation *allocation)
2132 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
2133 GtkComboBoxPrivate *priv = combo_box->priv;
2134 GtkWidget *child_widget;
2135 gint focus_width, focus_pad;
2136 GtkAllocation child;
2138 gboolean is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
2141 gtk_widget_set_allocation (widget, allocation);
2142 child_widget = gtk_bin_get_child (GTK_BIN (widget));
2143 get_widget_border (widget, &border);
2145 gtk_widget_style_get (widget,
2146 "focus-line-width", &focus_width,
2147 "focus-padding", &focus_pad,
2150 if (!priv->tree_view)
2152 if (priv->cell_view)
2154 GtkBorder button_border;
2159 allocation->x += border.left;
2160 allocation->y += border.top;
2161 allocation->width -= border.left + border.right;
2162 allocation->height -= border.top + border.bottom;
2164 gtk_widget_size_allocate (priv->button, allocation);
2166 /* set some things ready */
2167 border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->button));
2168 get_widget_border (priv->button, &button_border);
2170 child.x = allocation->x;
2171 child.y = allocation->y;
2172 width = allocation->width;
2173 child.height = allocation->height;
2175 if (!priv->is_cell_renderer)
2177 child.x += border_width + button_border.left + focus_width + focus_pad;
2178 child.y += border_width + button_border.top + focus_width + focus_pad;
2179 width -= (2 * (border_width + focus_width + focus_pad)) +
2180 button_border.left + button_border.right;
2181 child.height -= (2 * (border_width + focus_width + focus_pad)) +
2182 button_border.top + button_border.bottom;
2185 /* handle the children */
2186 gtk_widget_get_preferred_size (priv->arrow, &req, NULL);
2187 child.width = req.width;
2189 child.x += width - req.width;
2190 child.width = MAX (1, child.width);
2191 child.height = MAX (1, child.height);
2192 gtk_widget_size_allocate (priv->arrow, &child);
2194 child.x += req.width;
2195 gtk_widget_get_preferred_size (priv->separator, &req, NULL);
2196 child.width = req.width;
2198 child.x -= req.width;
2199 child.width = MAX (1, child.width);
2200 child.height = MAX (1, child.height);
2201 gtk_widget_size_allocate (priv->separator, &child);
2205 child.x += req.width;
2206 child.width = allocation->x + allocation->width
2207 - (border_width + button_border.right + focus_width + focus_pad)
2212 child.width = child.x;
2213 child.x = allocation->x
2214 + border_width + button_border.left + focus_width + focus_pad;
2215 child.width -= child.x;
2218 if (gtk_widget_get_visible (priv->popup_widget))
2220 gint width, menu_width;
2222 if (priv->wrap_width == 0)
2224 GtkAllocation combo_box_allocation;
2226 gtk_widget_get_allocation (GTK_WIDGET (combo_box), &combo_box_allocation);
2227 width = combo_box_allocation.width;
2228 gtk_widget_set_size_request (priv->popup_widget, -1, -1);
2230 if (combo_box->priv->popup_fixed_width)
2231 gtk_widget_get_preferred_width (priv->popup_widget, &menu_width, NULL);
2233 gtk_widget_get_preferred_width (priv->popup_widget, NULL, &menu_width);
2235 gtk_widget_set_size_request (priv->popup_widget,
2236 MAX (width, menu_width), -1);
2239 /* reposition the menu after giving it a new width */
2240 gtk_menu_reposition (GTK_MENU (priv->popup_widget));
2243 child.width = MAX (1, child.width);
2244 child.height = MAX (1, child.height);
2245 gtk_widget_size_allocate (child_widget, &child);
2249 GTK_COMBO_BOX_SIZE_ALLOCATE_BUTTON
2252 child.x = allocation->x + req.width + border.right;
2254 child.x = allocation->x + border.left;
2255 child.y = allocation->y + border.top;
2256 child.width = allocation->width - req.width - (border.left + border.right);
2257 child.width = MAX (1, child.width);
2258 child.height = MAX (1, child.height);
2259 gtk_widget_size_allocate (child_widget, &child);
2265 guint border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
2268 GTK_COMBO_BOX_SIZE_ALLOCATE_BUTTON
2272 child.x = allocation->x + req.width;
2274 child.x = allocation->x;
2276 child.y = allocation->y;
2277 child.width = allocation->width - req.width;
2278 child.height = allocation->height;
2280 if (priv->cell_view_frame)
2282 child.x += border.left + border_width;
2283 child.y += border.top + border_width;
2284 child.width = MAX (1, child.width - (2 * border_width) - (border.left + border.right));
2285 child.height = MAX (1, child.height - (2 * border_width) - (border.top + border.bottom));
2286 gtk_widget_size_allocate (priv->cell_view_frame, &child);
2289 if (priv->has_frame)
2291 GtkBorder frame_border;
2293 border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->cell_view_frame));
2294 get_widget_border (priv->cell_view_frame, &frame_border);
2296 child.x += border_width + frame_border.left;
2297 child.y += border_width + frame_border.right;
2298 child.width -= (2 * border_width) + frame_border.left + frame_border.right;
2299 child.height -= (2 * border_width) + frame_border.top + frame_border.bottom;
2304 child.x += border.left + border_width;
2305 child.y += border.top + border_width;
2306 child.width -= (2 * border_width) - (border.left + border.right);
2307 child.height -= (2 * border_width) - (border.top + border.bottom);
2310 if (gtk_widget_get_visible (priv->popup_window))
2312 gint x, y, width, height;
2313 gtk_combo_box_list_position (combo_box, &x, &y, &width, &height);
2314 gtk_window_move (GTK_WINDOW (priv->popup_window), x, y);
2315 gtk_widget_set_size_request (priv->popup_window, width, height);
2319 child.width = MAX (1, child.width);
2320 child.height = MAX (1, child.height);
2322 gtk_widget_size_allocate (child_widget, &child);
2326 #undef GTK_COMBO_BOX_ALLOCATE_BUTTON
2328 /******************************************************
2329 * GtkContainerClass *
2330 ******************************************************/
2333 gtk_combo_box_add (GtkContainer *container,
2336 GtkComboBox *combo_box = GTK_COMBO_BOX (container);
2337 GtkComboBoxPrivate *priv = combo_box->priv;
2339 if (priv->has_entry && !GTK_IS_ENTRY (widget))
2341 g_warning ("Attempting to add a widget with type %s to a GtkComboBox that needs an entry "
2342 "(need an instance of GtkEntry or of a subclass)",
2343 G_OBJECT_TYPE_NAME (widget));
2347 if (priv->cell_view &&
2348 gtk_widget_get_parent (priv->cell_view))
2350 gtk_container_remove (container, priv->cell_view);
2351 gtk_widget_queue_resize (GTK_WIDGET (container));
2354 gtk_widget_set_parent (widget, GTK_WIDGET (container));
2355 _gtk_bin_set_child (GTK_BIN (container), widget);
2357 if (priv->cell_view &&
2358 widget != priv->cell_view)
2360 /* since the cell_view was unparented, it's gone now */
2361 priv->cell_view = NULL;
2363 if (!priv->tree_view && priv->separator)
2365 gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (priv->separator)),
2367 priv->separator = NULL;
2369 gtk_widget_queue_resize (GTK_WIDGET (container));
2371 else if (priv->cell_view_frame)
2373 gtk_widget_unparent (priv->cell_view_frame);
2374 priv->cell_view_frame = NULL;
2379 if (priv->has_entry)
2381 /* this flag is a hack to tell the entry to fill its allocation.
2383 _gtk_entry_set_is_cell_renderer (GTK_ENTRY (widget), TRUE);
2385 g_signal_connect (widget, "changed",
2386 G_CALLBACK (gtk_combo_box_entry_contents_changed),
2389 gtk_entry_set_has_frame (GTK_ENTRY (widget), priv->has_frame);
2394 gtk_combo_box_remove (GtkContainer *container,
2397 GtkComboBox *combo_box = GTK_COMBO_BOX (container);
2398 GtkComboBoxPrivate *priv = combo_box->priv;
2400 gboolean appears_as_list;
2402 if (priv->has_entry)
2404 GtkWidget *child_widget;
2406 child_widget = gtk_bin_get_child (GTK_BIN (container));
2407 if (widget && widget == child_widget && GTK_IS_ENTRY (widget))
2409 g_signal_handlers_disconnect_by_func (widget,
2410 gtk_combo_box_entry_contents_changed,
2413 _gtk_entry_set_is_cell_renderer (GTK_ENTRY (widget), FALSE);
2417 if (widget == priv->cell_view)
2418 priv->cell_view = NULL;
2420 GTK_CONTAINER_CLASS (gtk_combo_box_parent_class)->remove (container, widget);
2422 if (gtk_widget_in_destruction (GTK_WIDGET (combo_box)))
2425 gtk_widget_queue_resize (GTK_WIDGET (container));
2427 if (!priv->tree_view)
2428 appears_as_list = FALSE;
2430 appears_as_list = TRUE;
2432 if (appears_as_list)
2433 gtk_combo_box_list_destroy (combo_box);
2434 else if (GTK_IS_MENU (priv->popup_widget))
2436 gtk_combo_box_menu_destroy (combo_box);
2437 gtk_menu_detach (GTK_MENU (priv->popup_widget));
2438 priv->popup_widget = NULL;
2441 if (!priv->cell_view)
2443 priv->cell_view = gtk_cell_view_new_with_context (priv->area, NULL);
2444 gtk_cell_view_set_fit_model (GTK_CELL_VIEW (priv->cell_view), TRUE);
2446 GTK_CONTAINER_CLASS (gtk_combo_box_parent_class)->add (container, priv->cell_view);
2448 gtk_widget_show (priv->cell_view);
2449 gtk_cell_view_set_model (GTK_CELL_VIEW (priv->cell_view), priv->model);
2452 if (appears_as_list)
2453 gtk_combo_box_list_setup (combo_box);
2455 gtk_combo_box_menu_setup (combo_box, TRUE);
2457 if (gtk_tree_row_reference_valid (priv->active_row))
2459 path = gtk_tree_row_reference_get_path (priv->active_row);
2460 gtk_combo_box_set_active_internal (combo_box, path);
2461 gtk_tree_path_free (path);
2464 gtk_combo_box_set_active_internal (combo_box, NULL);
2468 gtk_combo_box_forall (GtkContainer *container,
2469 gboolean include_internals,
2470 GtkCallback callback,
2471 gpointer callback_data)
2473 GtkComboBox *combo_box = GTK_COMBO_BOX (container);
2474 GtkComboBoxPrivate *priv = combo_box->priv;
2477 if (include_internals)
2480 (* callback) (priv->button, callback_data);
2481 if (priv->cell_view_frame)
2482 (* callback) (priv->cell_view_frame, callback_data);
2485 child = gtk_bin_get_child (GTK_BIN (container));
2487 (* callback) (child, callback_data);
2491 /******************************************************
2492 * GtkComboBoxClass (binding methods) *
2493 ******************************************************/
2495 gtk_combo_box_real_move_active (GtkComboBox *combo_box,
2496 GtkScrollType scroll)
2499 GtkTreeIter new_iter;
2500 gboolean active_iter;
2503 if (!combo_box->priv->model)
2505 gtk_widget_error_bell (GTK_WIDGET (combo_box));
2509 active_iter = gtk_combo_box_get_active_iter (combo_box, &iter);
2513 case GTK_SCROLL_STEP_BACKWARD:
2514 case GTK_SCROLL_STEP_UP:
2515 case GTK_SCROLL_STEP_LEFT:
2518 found = tree_prev (combo_box, combo_box->priv->model,
2519 &iter, &new_iter, FALSE);
2522 /* else fall through */
2524 case GTK_SCROLL_PAGE_FORWARD:
2525 case GTK_SCROLL_PAGE_DOWN:
2526 case GTK_SCROLL_PAGE_RIGHT:
2527 case GTK_SCROLL_END:
2528 found = tree_last (combo_box, combo_box->priv->model, &new_iter, FALSE);
2531 case GTK_SCROLL_STEP_FORWARD:
2532 case GTK_SCROLL_STEP_DOWN:
2533 case GTK_SCROLL_STEP_RIGHT:
2536 found = tree_next (combo_box, combo_box->priv->model,
2537 &iter, &new_iter, FALSE);
2540 /* else fall through */
2542 case GTK_SCROLL_PAGE_BACKWARD:
2543 case GTK_SCROLL_PAGE_UP:
2544 case GTK_SCROLL_PAGE_LEFT:
2545 case GTK_SCROLL_START:
2546 found = tree_first (combo_box, combo_box->priv->model, &new_iter, FALSE);
2553 if (found && active_iter)
2555 GtkTreePath *old_path;
2556 GtkTreePath *new_path;
2558 old_path = gtk_tree_model_get_path (combo_box->priv->model, &iter);
2559 new_path = gtk_tree_model_get_path (combo_box->priv->model, &new_iter);
2561 if (gtk_tree_path_compare (old_path, new_path) == 0)
2564 gtk_tree_path_free (old_path);
2565 gtk_tree_path_free (new_path);
2570 gtk_combo_box_set_active_iter (combo_box, &new_iter);
2574 gtk_widget_error_bell (GTK_WIDGET (combo_box));
2579 gtk_combo_box_real_popup (GtkComboBox *combo_box)
2583 device = gtk_get_current_event_device ();
2587 GdkDeviceManager *device_manager;
2588 GdkDisplay *display;
2591 display = gtk_widget_get_display (GTK_WIDGET (combo_box));
2592 device_manager = gdk_display_get_device_manager (display);
2594 /* No device was set, pick the first master device */
2595 devices = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
2596 device = devices->data;
2597 g_list_free (devices);
2600 gtk_combo_box_popup_for_device (combo_box, device);
2604 gtk_combo_box_real_popdown (GtkComboBox *combo_box)
2606 if (combo_box->priv->popup_shown)
2608 gtk_combo_box_popdown (combo_box);
2615 /******************************************************
2616 * GtkTreeModel callbacks *
2617 ******************************************************/
2619 gtk_combo_box_model_row_inserted (GtkTreeModel *model,
2624 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
2626 if (combo_box->priv->tree_view)
2627 gtk_combo_box_list_popup_resize (combo_box);
2631 gtk_combo_box_model_row_deleted (GtkTreeModel *model,
2635 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
2636 GtkComboBoxPrivate *priv = combo_box->priv;
2638 if (!gtk_tree_row_reference_valid (priv->active_row))
2640 if (priv->cell_view)
2641 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (priv->cell_view), NULL);
2642 g_signal_emit (combo_box, combo_box_signals[CHANGED], 0);
2645 if (priv->tree_view)
2646 gtk_combo_box_list_popup_resize (combo_box);
2651 gtk_combo_box_button_toggled (GtkWidget *widget,
2654 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
2656 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
2658 if (!combo_box->priv->popup_in_progress)
2659 gtk_combo_box_popup (combo_box);
2662 gtk_combo_box_popdown (combo_box);
2666 gtk_combo_box_menu_show (GtkWidget *menu,
2669 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
2670 GtkComboBoxPrivate *priv = combo_box->priv;
2672 gtk_combo_box_child_show (menu, user_data);
2674 priv->popup_in_progress = TRUE;
2675 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button),
2677 priv->popup_in_progress = FALSE;
2681 gtk_combo_box_menu_hide (GtkWidget *menu,
2684 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
2686 gtk_combo_box_child_hide (menu,user_data);
2688 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
2693 gtk_combo_box_detacher (GtkWidget *widget,
2696 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
2697 GtkComboBoxPrivate *priv = combo_box->priv;
2699 g_return_if_fail (priv->popup_widget == (GtkWidget *) menu);
2701 g_signal_handlers_disconnect_by_func (menu->toplevel,
2702 gtk_combo_box_menu_show,
2704 g_signal_handlers_disconnect_by_func (menu->toplevel,
2705 gtk_combo_box_menu_hide,
2708 priv->popup_widget = NULL;
2712 gtk_combo_box_set_popup_widget (GtkComboBox *combo_box,
2715 GtkComboBoxPrivate *priv = combo_box->priv;
2717 if (GTK_IS_MENU (priv->popup_widget))
2719 gtk_menu_detach (GTK_MENU (priv->popup_widget));
2720 priv->popup_widget = NULL;
2722 else if (priv->popup_widget)
2724 gtk_container_remove (GTK_CONTAINER (priv->scrolled_window),
2725 priv->popup_widget);
2726 g_object_unref (priv->popup_widget);
2727 priv->popup_widget = NULL;
2730 if (GTK_IS_MENU (popup))
2732 if (priv->popup_window)
2734 gtk_widget_destroy (priv->popup_window);
2735 priv->popup_window = NULL;
2738 priv->popup_widget = popup;
2741 * Note that we connect to show/hide on the toplevel, not the
2742 * menu itself, since the menu is not shown/hidden when it is
2743 * popped up while torn-off.
2745 g_signal_connect (GTK_MENU (popup)->toplevel, "show",
2746 G_CALLBACK (gtk_combo_box_menu_show), combo_box);
2747 g_signal_connect (GTK_MENU (popup)->toplevel, "hide",
2748 G_CALLBACK (gtk_combo_box_menu_hide), combo_box);
2750 gtk_menu_attach_to_widget (GTK_MENU (popup),
2751 GTK_WIDGET (combo_box),
2752 gtk_combo_box_detacher);
2756 if (!priv->popup_window)
2758 GtkWidget *toplevel;
2760 priv->popup_window = gtk_window_new (GTK_WINDOW_POPUP);
2761 gtk_widget_set_name (priv->popup_window, "gtk-combobox-popup-window");
2763 gtk_window_set_type_hint (GTK_WINDOW (priv->popup_window),
2764 GDK_WINDOW_TYPE_HINT_COMBO);
2766 g_signal_connect (GTK_WINDOW (priv->popup_window),"show",
2767 G_CALLBACK (gtk_combo_box_child_show),
2769 g_signal_connect (GTK_WINDOW (priv->popup_window),"hide",
2770 G_CALLBACK (gtk_combo_box_child_hide),
2773 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (combo_box));
2774 if (GTK_IS_WINDOW (toplevel))
2776 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
2777 GTK_WINDOW (priv->popup_window));
2778 gtk_window_set_transient_for (GTK_WINDOW (priv->popup_window),
2779 GTK_WINDOW (toplevel));
2782 gtk_window_set_resizable (GTK_WINDOW (priv->popup_window), FALSE);
2783 gtk_window_set_screen (GTK_WINDOW (priv->popup_window),
2784 gtk_widget_get_screen (GTK_WIDGET (combo_box)));
2786 priv->scrolled_window = gtk_scrolled_window_new (NULL, NULL);
2788 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
2791 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->scrolled_window),
2794 gtk_widget_show (priv->scrolled_window);
2796 gtk_container_add (GTK_CONTAINER (priv->popup_window),
2797 priv->scrolled_window);
2800 gtk_container_add (GTK_CONTAINER (priv->scrolled_window),
2803 gtk_widget_show (popup);
2804 g_object_ref (popup);
2805 priv->popup_widget = popup;
2810 gtk_combo_box_menu_position_below (GtkMenu *menu,
2816 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
2817 GtkAllocation child_allocation;
2823 GdkRectangle monitor;
2826 /* FIXME: is using the size request here broken? */
2827 child = gtk_bin_get_child (GTK_BIN (combo_box));
2831 gtk_widget_get_allocation (child, &child_allocation);
2833 if (!gtk_widget_get_has_window (child))
2835 sx += child_allocation.x;
2836 sy += child_allocation.y;
2839 gdk_window_get_root_coords (gtk_widget_get_window (child),
2842 get_widget_border (GTK_WIDGET (combo_box), &border);
2845 if (combo_box->priv->popup_fixed_width)
2846 gtk_widget_get_preferred_size (GTK_WIDGET (menu), &req, NULL);
2848 gtk_widget_get_preferred_size (GTK_WIDGET (menu), NULL, &req);
2850 if (gtk_widget_get_direction (GTK_WIDGET (combo_box)) == GTK_TEXT_DIR_LTR)
2853 *x = sx + child_allocation.width - req.width;
2856 screen = gtk_widget_get_screen (GTK_WIDGET (combo_box));
2857 monitor_num = gdk_screen_get_monitor_at_window (screen,
2858 gtk_widget_get_window (GTK_WIDGET (combo_box)));
2859 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
2863 else if (*x + req.width > monitor.x + monitor.width)
2864 *x = monitor.x + monitor.width - req.width;
2866 if (monitor.y + monitor.height - *y - child_allocation.height >= req.height)
2867 *y += child_allocation.height;
2868 else if (*y - monitor.y >= req.height)
2870 else if (monitor.y + monitor.height - *y - child_allocation.height > *y - monitor.y)
2871 *y += child_allocation.height;
2879 gtk_combo_box_menu_position_over (GtkMenu *menu,
2885 GtkComboBox *combo_box;
2889 GtkAllocation allocation;
2890 GtkAllocation child_allocation;
2897 combo_box = GTK_COMBO_BOX (user_data);
2898 widget = GTK_WIDGET (combo_box);
2900 active = gtk_menu_get_active (GTK_MENU (combo_box->priv->popup_widget));
2902 gtk_widget_get_allocation (widget, &allocation);
2904 menu_xpos = allocation.x;
2905 menu_ypos = allocation.y + allocation.height / 2 - 2;
2907 if (combo_box->priv->popup_fixed_width)
2908 gtk_widget_get_preferred_width (GTK_WIDGET (menu), &menu_width, NULL);
2910 gtk_widget_get_preferred_width (GTK_WIDGET (menu), NULL, &menu_width);
2914 gtk_widget_get_allocation (active, &child_allocation);
2915 menu_ypos -= child_allocation.height / 2;
2918 children = GTK_MENU_SHELL (combo_box->priv->popup_widget)->children;
2921 child = children->data;
2923 if (active == child)
2926 if (gtk_widget_get_visible (child))
2928 gtk_widget_get_allocation (child, &child_allocation);
2930 menu_ypos -= child_allocation.height;
2933 children = children->next;
2936 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
2937 menu_xpos = menu_xpos + allocation.width - menu_width;
2939 gdk_window_get_root_coords (gtk_widget_get_window (widget),
2940 menu_xpos, menu_ypos,
2941 &menu_xpos, &menu_ypos);
2943 /* Clamp the position on screen */
2944 screen_width = gdk_screen_get_width (gtk_widget_get_screen (widget));
2948 else if ((menu_xpos + menu_width) > screen_width)
2949 menu_xpos -= ((menu_xpos + menu_width) - screen_width);
2958 gtk_combo_box_menu_position (GtkMenu *menu,
2964 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
2965 GtkComboBoxPrivate *priv = combo_box->priv;
2966 GtkWidget *menu_item;
2968 if (priv->wrap_width > 0 || priv->cell_view == NULL)
2969 gtk_combo_box_menu_position_below (menu, x, y, push_in, user_data);
2972 /* FIXME handle nested menus better */
2973 menu_item = gtk_menu_get_active (GTK_MENU (priv->popup_widget));
2975 gtk_menu_shell_select_item (GTK_MENU_SHELL (priv->popup_widget),
2978 gtk_combo_box_menu_position_over (menu, x, y, push_in, user_data);
2981 if (!gtk_widget_get_visible (GTK_MENU (priv->popup_widget)->toplevel))
2982 gtk_window_set_type_hint (GTK_WINDOW (GTK_MENU (priv->popup_widget)->toplevel),
2983 GDK_WINDOW_TYPE_HINT_COMBO);
2987 gtk_combo_box_list_position (GtkComboBox *combo_box,
2993 GtkComboBoxPrivate *priv = combo_box->priv;
2994 GtkAllocation allocation;
2997 GdkRectangle monitor;
2998 GtkRequisition popup_req;
2999 GtkPolicyType hpolicy, vpolicy;
3002 /* under windows, the drop down list is as wide as the combo box itself.
3004 GtkWidget *widget = GTK_WIDGET (combo_box);
3008 gtk_widget_get_allocation (widget, &allocation);
3010 if (!gtk_widget_get_has_window (widget))
3016 window = gtk_widget_get_window (widget);
3018 gdk_window_get_root_coords (gtk_widget_get_window (widget),
3021 *width = allocation.width;
3023 hpolicy = vpolicy = GTK_POLICY_NEVER;
3024 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
3027 if (combo_box->priv->popup_fixed_width)
3029 gtk_widget_get_preferred_size (priv->scrolled_window, &popup_req, NULL);
3031 if (popup_req.width > *width)
3033 hpolicy = GTK_POLICY_ALWAYS;
3034 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
3040 /* This code depends on treeviews properly reporting their natural width */
3041 gtk_widget_get_preferred_size (priv->scrolled_window, NULL, &popup_req);
3043 if (popup_req.width > *width)
3045 hpolicy = GTK_POLICY_NEVER;
3046 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
3049 *width = popup_req.width;
3053 *height = popup_req.height;
3055 screen = gtk_widget_get_screen (GTK_WIDGET (combo_box));
3056 monitor_num = gdk_screen_get_monitor_at_window (screen, window);
3057 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
3059 if (gtk_widget_get_direction (GTK_WIDGET (combo_box)) == GTK_TEXT_DIR_RTL)
3060 *x = *x + allocation.width - *width;
3064 else if (*x + *width > monitor.x + monitor.width)
3065 *x = monitor.x + monitor.width - *width;
3067 if (*y + allocation.height + *height <= monitor.y + monitor.height)
3068 *y += allocation.height;
3069 else if (*y - *height >= monitor.y)
3071 else if (monitor.y + monitor.height - (*y + allocation.height) > *y - monitor.y)
3073 *y += allocation.height;
3074 *height = monitor.y + monitor.height - *y;
3078 *height = *y - monitor.y;
3082 if (popup_req.height > *height)
3084 vpolicy = GTK_POLICY_ALWAYS;
3086 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
3092 gtk_combo_box_menu_popup (GtkComboBox *combo_box,
3094 guint32 activate_time)
3096 GtkComboBoxPrivate *priv = combo_box->priv;
3099 gint width, min_width, nat_width;
3102 if (gtk_tree_row_reference_valid (priv->active_row))
3104 path = gtk_tree_row_reference_get_path (priv->active_row);
3105 active_item = gtk_tree_path_get_indices (path)[0];
3106 gtk_tree_path_free (path);
3108 if (priv->add_tearoffs)
3112 /* FIXME handle nested menus better */
3113 gtk_menu_set_active (GTK_MENU (priv->popup_widget), active_item);
3115 if (priv->wrap_width == 0)
3117 GtkAllocation allocation;
3119 gtk_widget_get_allocation (GTK_WIDGET (combo_box), &allocation);
3120 width = allocation.width;
3121 gtk_widget_set_size_request (priv->popup_widget, -1, -1);
3122 gtk_widget_get_preferred_width (priv->popup_widget, &min_width, &nat_width);
3124 if (combo_box->priv->popup_fixed_width)
3125 width = MAX (width, min_width);
3127 width = MAX (width, nat_width);
3129 gtk_widget_set_size_request (priv->popup_widget, width, -1);
3132 gtk_menu_popup (GTK_MENU (priv->popup_widget),
3134 gtk_combo_box_menu_position, combo_box,
3135 button, activate_time);
3139 popup_grab_on_window (GdkWindow *window,
3140 GdkDevice *keyboard,
3142 guint32 activate_time)
3145 gdk_device_grab (keyboard, window,
3146 GDK_OWNERSHIP_WINDOW, TRUE,
3147 GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
3148 NULL, activate_time) != GDK_GRAB_SUCCESS)
3152 gdk_device_grab (pointer, window,
3153 GDK_OWNERSHIP_WINDOW, TRUE,
3154 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
3155 GDK_POINTER_MOTION_MASK,
3156 NULL, activate_time) != GDK_GRAB_SUCCESS)
3159 gdk_device_ungrab (keyboard, activate_time);
3168 gtk_combo_box_unset_model (GtkComboBox *combo_box)
3170 GtkComboBoxPrivate *priv = combo_box->priv;
3174 g_signal_handler_disconnect (priv->model,
3176 g_signal_handler_disconnect (priv->model,
3179 g_object_unref (priv->model);
3183 if (priv->active_row)
3185 gtk_tree_row_reference_free (priv->active_row);
3186 priv->active_row = NULL;
3189 if (priv->cell_view)
3190 gtk_cell_view_set_model (GTK_CELL_VIEW (priv->cell_view), NULL);
3194 gtk_combo_box_child_show (GtkWidget *widget,
3195 GtkComboBox *combo_box)
3197 GtkComboBoxPrivate *priv = combo_box->priv;
3199 priv->popup_shown = TRUE;
3200 g_object_notify (G_OBJECT (combo_box), "popup-shown");
3204 gtk_combo_box_child_hide (GtkWidget *widget,
3205 GtkComboBox *combo_box)
3207 GtkComboBoxPrivate *priv = combo_box->priv;
3209 priv->popup_shown = FALSE;
3210 g_object_notify (G_OBJECT (combo_box), "popup-shown");
3218 gtk_combo_box_row_separator_func (GtkTreeModel *model,
3222 GtkComboBoxPrivate *priv = combo->priv;
3224 if (priv->row_separator_func)
3225 return priv->row_separator_func (model, iter, priv->row_separator_data);
3231 gtk_combo_box_header_func (GtkTreeModel *model,
3235 /* Every submenu has a selectable header, however we
3236 * can expose a method to make that configurable by
3237 * the user (like row_separator_func is done) */
3242 gtk_combo_box_menu_activate (GtkWidget *menu,
3244 GtkComboBox *combo_box)
3248 if (gtk_tree_model_get_iter_from_string (combo_box->priv->model, &iter, path))
3249 gtk_combo_box_set_active_iter (combo_box, &iter);
3251 g_object_set (combo_box,
3252 "editing-canceled", FALSE,
3257 gtk_combo_box_menu_setup (GtkComboBox *combo_box,
3258 gboolean add_children)
3260 GtkComboBoxPrivate *priv = combo_box->priv;
3264 child = gtk_bin_get_child (GTK_BIN (combo_box));
3266 if (priv->cell_view)
3268 priv->button = gtk_toggle_button_new ();
3269 gtk_button_set_focus_on_click (GTK_BUTTON (priv->button),
3270 priv->focus_on_click);
3272 g_signal_connect (priv->button, "toggled",
3273 G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
3274 gtk_widget_set_parent (priv->button,
3275 gtk_widget_get_parent (child));
3277 priv->box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
3278 gtk_container_add (GTK_CONTAINER (priv->button), priv->box);
3280 priv->separator = gtk_separator_new (GTK_ORIENTATION_VERTICAL);
3281 gtk_container_add (GTK_CONTAINER (priv->box), priv->separator);
3283 priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3284 gtk_container_add (GTK_CONTAINER (priv->box), priv->arrow);
3286 gtk_widget_show_all (priv->button);
3290 priv->button = gtk_toggle_button_new ();
3291 gtk_button_set_focus_on_click (GTK_BUTTON (priv->button),
3292 priv->focus_on_click);
3294 g_signal_connect (priv->button, "toggled",
3295 G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
3296 gtk_widget_set_parent (priv->button,
3297 gtk_widget_get_parent (child));
3299 priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3300 gtk_container_add (GTK_CONTAINER (priv->button), priv->arrow);
3301 gtk_widget_show_all (priv->button);
3304 g_signal_connect (priv->button, "button-press-event",
3305 G_CALLBACK (gtk_combo_box_menu_button_press),
3307 g_signal_connect (priv->button, "state-changed",
3308 G_CALLBACK (gtk_combo_box_button_state_changed),
3311 /* create our funky menu */
3312 menu = gtk_tree_menu_new_with_area (priv->area);
3313 gtk_tree_menu_set_model (GTK_TREE_MENU (menu), priv->model);
3315 gtk_tree_menu_set_wrap_width (GTK_TREE_MENU (menu), priv->wrap_width);
3316 gtk_tree_menu_set_row_span_column (GTK_TREE_MENU (menu), priv->row_column);
3317 gtk_tree_menu_set_column_span_column (GTK_TREE_MENU (menu), priv->col_column);
3318 gtk_tree_menu_set_tearoff (GTK_TREE_MENU (menu),
3319 combo_box->priv->add_tearoffs);
3321 g_signal_connect (menu, "menu-activate",
3322 G_CALLBACK (gtk_combo_box_menu_activate), combo_box);
3324 /* Chain our row_separator_func through */
3325 gtk_tree_menu_set_row_separator_func (GTK_TREE_MENU (menu),
3326 (GtkTreeViewRowSeparatorFunc)gtk_combo_box_row_separator_func,
3329 gtk_tree_menu_set_header_func (GTK_TREE_MENU (menu),
3330 (GtkTreeMenuHeaderFunc)gtk_combo_box_header_func,
3333 gtk_widget_set_name (menu, "gtk-combobox-popup-menu");
3335 g_signal_connect (menu, "key-press-event",
3336 G_CALLBACK (gtk_combo_box_menu_key_press), combo_box);
3337 gtk_combo_box_set_popup_widget (combo_box, menu);
3339 gtk_combo_box_update_title (combo_box);
3343 gtk_combo_box_menu_destroy (GtkComboBox *combo_box)
3345 GtkComboBoxPrivate *priv = combo_box->priv;
3347 g_signal_handlers_disconnect_matched (priv->button,
3348 G_SIGNAL_MATCH_DATA,
3350 gtk_combo_box_menu_button_press, NULL);
3351 g_signal_handlers_disconnect_matched (priv->button,
3352 G_SIGNAL_MATCH_DATA,
3354 gtk_combo_box_button_state_changed, combo_box);
3355 g_signal_handlers_disconnect_matched (priv->popup_widget,
3356 G_SIGNAL_MATCH_DATA,
3358 gtk_combo_box_menu_activate, combo_box);
3360 /* unparent will remove our latest ref */
3361 gtk_widget_unparent (priv->button);
3364 priv->button = NULL;
3366 priv->separator = NULL;
3368 priv->column = NULL;
3370 /* changing the popup window will unref the menu and the children */
3375 gtk_combo_box_menu_button_press (GtkWidget *widget,
3376 GdkEventButton *event,
3379 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3380 GtkComboBoxPrivate *priv = combo_box->priv;
3382 if (GTK_IS_MENU (priv->popup_widget) &&
3383 event->type == GDK_BUTTON_PRESS && event->button == 1)
3385 if (priv->focus_on_click &&
3386 !gtk_widget_has_focus (priv->button))
3387 gtk_widget_grab_focus (priv->button);
3389 gtk_combo_box_menu_popup (combo_box, event->button, event->time);
3398 gtk_combo_box_update_sensitivity (GtkComboBox *combo_box)
3401 gboolean sensitive = TRUE; /* fool code checkers */
3403 if (!combo_box->priv->button)
3406 switch (combo_box->priv->button_sensitivity)
3408 case GTK_SENSITIVITY_ON:
3411 case GTK_SENSITIVITY_OFF:
3414 case GTK_SENSITIVITY_AUTO:
3415 sensitive = combo_box->priv->model &&
3416 gtk_tree_model_get_iter_first (combo_box->priv->model, &iter);
3419 g_assert_not_reached ();
3423 gtk_widget_set_sensitive (combo_box->priv->button, sensitive);
3425 /* In list-mode, we also need to update sensitivity of the event box */
3426 if (GTK_IS_TREE_VIEW (combo_box->priv->tree_view)
3427 && combo_box->priv->cell_view)
3428 gtk_widget_set_sensitive (combo_box->priv->box, sensitive);
3432 list_popup_resize_idle (gpointer user_data)
3434 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3435 GtkComboBoxPrivate *priv = combo_box->priv;
3436 gint x, y, width, height;
3438 if (priv->tree_view && gtk_widget_get_mapped (priv->popup_window))
3440 gtk_combo_box_list_position (combo_box, &x, &y, &width, &height);
3442 gtk_widget_set_size_request (priv->popup_window, width, height);
3443 gtk_window_move (GTK_WINDOW (priv->popup_window), x, y);
3446 priv->resize_idle_id = 0;
3452 gtk_combo_box_list_popup_resize (GtkComboBox *combo_box)
3454 GtkComboBoxPrivate *priv = combo_box->priv;
3456 if (!priv->resize_idle_id)
3457 priv->resize_idle_id =
3458 gdk_threads_add_idle (list_popup_resize_idle, combo_box);
3462 gtk_combo_box_model_row_expanded (GtkTreeModel *model,
3467 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3469 gtk_combo_box_list_popup_resize (combo_box);
3477 gtk_combo_box_list_setup (GtkComboBox *combo_box)
3479 GtkComboBoxPrivate *priv = combo_box->priv;
3480 GtkTreeSelection *sel;
3482 GtkWidget *widget = GTK_WIDGET (combo_box);
3484 priv->button = gtk_toggle_button_new ();
3485 child = gtk_bin_get_child (GTK_BIN (combo_box));
3486 gtk_widget_set_parent (priv->button,
3487 gtk_widget_get_parent (child));
3488 g_signal_connect (priv->button, "button-press-event",
3489 G_CALLBACK (gtk_combo_box_list_button_pressed), combo_box);
3490 g_signal_connect (priv->button, "toggled",
3491 G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
3493 priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3494 gtk_container_add (GTK_CONTAINER (priv->button), priv->arrow);
3495 priv->separator = NULL;
3496 gtk_widget_show_all (priv->button);
3498 if (priv->cell_view)
3500 GtkStyleContext *context;
3501 GtkStateFlags state;
3504 context = gtk_widget_get_style_context (widget);
3505 state = gtk_widget_get_state_flags (widget);
3507 gtk_style_context_get (context, state,
3508 "background-color", &color,
3511 gtk_cell_view_set_background_rgba (GTK_CELL_VIEW (priv->cell_view), color);
3512 gdk_rgba_free (color);
3514 priv->box = gtk_event_box_new ();
3515 gtk_event_box_set_visible_window (GTK_EVENT_BOX (priv->box),
3518 if (priv->has_frame)
3520 priv->cell_view_frame = gtk_frame_new (NULL);
3521 gtk_frame_set_shadow_type (GTK_FRAME (priv->cell_view_frame),
3526 combo_box->priv->cell_view_frame = gtk_event_box_new ();
3527 gtk_event_box_set_visible_window (GTK_EVENT_BOX (combo_box->priv->cell_view_frame),
3531 gtk_widget_set_parent (priv->cell_view_frame,
3532 gtk_widget_get_parent (child));
3533 gtk_container_add (GTK_CONTAINER (priv->cell_view_frame), priv->box);
3534 gtk_widget_show_all (priv->cell_view_frame);
3536 g_signal_connect (priv->box, "button-press-event",
3537 G_CALLBACK (gtk_combo_box_list_button_pressed),
3541 priv->tree_view = gtk_tree_view_new ();
3542 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));
3543 gtk_tree_selection_set_mode (sel, GTK_SELECTION_BROWSE);
3544 gtk_tree_selection_set_select_function (sel,
3545 gtk_combo_box_list_select_func,
3547 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->tree_view),
3549 gtk_tree_view_set_hover_selection (GTK_TREE_VIEW (priv->tree_view),
3552 gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (priv->tree_view),
3553 (GtkTreeViewRowSeparatorFunc)gtk_combo_box_row_separator_func,
3556 gtk_tree_view_set_model (GTK_TREE_VIEW (priv->tree_view), priv->model);
3558 priv->column = gtk_tree_view_column_new ();
3559 gtk_tree_view_append_column (GTK_TREE_VIEW (priv->tree_view), priv->column);
3561 if (gtk_tree_row_reference_valid (priv->active_row))
3565 path = gtk_tree_row_reference_get_path (priv->active_row);
3566 gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->tree_view),
3568 gtk_tree_path_free (path);
3571 /* set sample/popup widgets */
3572 gtk_combo_box_set_popup_widget (combo_box, priv->tree_view);
3574 g_signal_connect (priv->tree_view, "key-press-event",
3575 G_CALLBACK (gtk_combo_box_list_key_press),
3577 g_signal_connect (priv->tree_view, "enter-notify-event",
3578 G_CALLBACK (gtk_combo_box_list_enter_notify),
3580 g_signal_connect (priv->tree_view, "row-expanded",
3581 G_CALLBACK (gtk_combo_box_model_row_expanded),
3583 g_signal_connect (priv->tree_view, "row-collapsed",
3584 G_CALLBACK (gtk_combo_box_model_row_expanded),
3586 g_signal_connect (priv->popup_window, "button-press-event",
3587 G_CALLBACK (gtk_combo_box_list_button_pressed),
3589 g_signal_connect (priv->popup_window, "button-release-event",
3590 G_CALLBACK (gtk_combo_box_list_button_released),
3593 gtk_widget_show (priv->tree_view);
3595 gtk_combo_box_update_sensitivity (combo_box);
3599 gtk_combo_box_list_destroy (GtkComboBox *combo_box)
3601 GtkComboBoxPrivate *priv = combo_box->priv;
3603 /* disconnect signals */
3604 g_signal_handlers_disconnect_matched (priv->tree_view,
3605 G_SIGNAL_MATCH_DATA,
3606 0, 0, NULL, NULL, combo_box);
3607 g_signal_handlers_disconnect_matched (priv->button,
3608 G_SIGNAL_MATCH_DATA,
3610 gtk_combo_box_list_button_pressed,
3612 g_signal_handlers_disconnect_matched (priv->popup_window,
3613 G_SIGNAL_MATCH_DATA,
3615 gtk_combo_box_list_button_pressed,
3617 g_signal_handlers_disconnect_matched (priv->popup_window,
3618 G_SIGNAL_MATCH_DATA,
3620 gtk_combo_box_list_button_released,
3623 g_signal_handlers_disconnect_matched (priv->popup_window,
3624 G_SIGNAL_MATCH_DATA,
3626 gtk_combo_box_child_show,
3629 g_signal_handlers_disconnect_matched (priv->popup_window,
3630 G_SIGNAL_MATCH_DATA,
3632 gtk_combo_box_child_hide,
3636 g_signal_handlers_disconnect_matched (priv->box,
3637 G_SIGNAL_MATCH_DATA,
3639 gtk_combo_box_list_button_pressed,
3642 /* destroy things (unparent will kill the latest ref from us)
3643 * last unref on button will destroy the arrow
3645 gtk_widget_unparent (priv->button);
3646 priv->button = NULL;
3649 if (priv->cell_view)
3651 g_object_set (priv->cell_view,
3652 "background-set", FALSE,
3656 if (priv->cell_view_frame)
3658 gtk_widget_unparent (priv->cell_view_frame);
3659 priv->cell_view_frame = NULL;
3663 if (priv->scroll_timer)
3665 g_source_remove (priv->scroll_timer);
3666 priv->scroll_timer = 0;
3669 if (priv->resize_idle_id)
3671 g_source_remove (priv->resize_idle_id);
3672 priv->resize_idle_id = 0;
3675 gtk_widget_destroy (priv->tree_view);
3677 priv->tree_view = NULL;
3678 if (priv->popup_widget)
3680 g_object_unref (priv->popup_widget);
3681 priv->popup_widget = NULL;
3688 gtk_combo_box_list_button_pressed (GtkWidget *widget,
3689 GdkEventButton *event,
3692 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
3693 GtkComboBoxPrivate *priv = combo_box->priv;
3695 GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *)event);
3697 if (ewidget == priv->popup_window)
3700 if ((ewidget != priv->button && ewidget != priv->box) ||
3701 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->button)))
3704 if (priv->focus_on_click &&
3705 !gtk_widget_has_focus (priv->button))
3706 gtk_widget_grab_focus (priv->button);
3708 gtk_combo_box_popup_for_device (combo_box, event->device);
3710 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button), TRUE);
3712 priv->auto_scroll = FALSE;
3713 if (priv->scroll_timer == 0)
3714 priv->scroll_timer = gdk_threads_add_timeout (SCROLL_TIME,
3715 (GSourceFunc) gtk_combo_box_list_scroll_timeout,
3718 priv->popup_in_progress = TRUE;
3724 gtk_combo_box_list_button_released (GtkWidget *widget,
3725 GdkEventButton *event,
3729 GtkTreePath *path = NULL;
3732 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
3733 GtkComboBoxPrivate *priv = combo_box->priv;
3735 gboolean popup_in_progress = FALSE;
3737 GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *)event);
3739 if (priv->popup_in_progress)
3741 popup_in_progress = TRUE;
3742 priv->popup_in_progress = FALSE;
3745 gtk_tree_view_set_hover_expand (GTK_TREE_VIEW (priv->tree_view),
3747 if (priv->scroll_timer)
3749 g_source_remove (priv->scroll_timer);
3750 priv->scroll_timer = 0;
3753 if (ewidget != priv->tree_view)
3755 if ((ewidget == priv->button ||
3756 ewidget == priv->box) &&
3757 !popup_in_progress &&
3758 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->button)))
3760 gtk_combo_box_popdown (combo_box);
3764 /* released outside treeview */
3765 if (ewidget != priv->button && ewidget != priv->box)
3767 gtk_combo_box_popdown (combo_box);
3775 /* select something cool */
3776 ret = gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (priv->tree_view),
3782 return TRUE; /* clicked outside window? */
3784 gtk_tree_model_get_iter (priv->model, &iter, path);
3785 gtk_tree_path_free (path);
3787 gtk_combo_box_popdown (combo_box);
3789 if (tree_column_row_is_sensitive (combo_box, &iter))
3790 gtk_combo_box_set_active_iter (combo_box, &iter);
3796 gtk_combo_box_menu_key_press (GtkWidget *widget,
3800 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
3802 if (!gtk_bindings_activate_event (G_OBJECT (widget), event))
3804 /* The menu hasn't managed the
3805 * event, forward it to the combobox
3807 gtk_bindings_activate_event (G_OBJECT (combo_box), event);
3814 gtk_combo_box_list_key_press (GtkWidget *widget,
3818 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
3821 if (event->keyval == GDK_KEY_Return || event->keyval == GDK_KEY_ISO_Enter || event->keyval == GDK_KEY_KP_Enter ||
3822 event->keyval == GDK_KEY_space || event->keyval == GDK_KEY_KP_Space)
3824 GtkTreeModel *model = NULL;
3826 gtk_combo_box_popdown (combo_box);
3828 if (combo_box->priv->model)
3830 GtkTreeSelection *sel;
3832 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (combo_box->priv->tree_view));
3834 if (gtk_tree_selection_get_selected (sel, &model, &iter))
3835 gtk_combo_box_set_active_iter (combo_box, &iter);
3841 if (!gtk_bindings_activate_event (G_OBJECT (widget), event))
3843 /* The list hasn't managed the
3844 * event, forward it to the combobox
3846 gtk_bindings_activate_event (G_OBJECT (combo_box), event);
3853 gtk_combo_box_list_auto_scroll (GtkComboBox *combo_box,
3858 GtkAllocation allocation;
3859 GtkWidget *tree_view = combo_box->priv->tree_view;
3862 gtk_widget_get_allocation (tree_view, &allocation);
3864 adj = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window));
3865 if (adj && adj->upper - adj->lower > adj->page_size)
3867 if (x <= allocation.x &&
3868 adj->lower < adj->value)
3870 value = adj->value - (allocation.x - x + 1);
3871 gtk_adjustment_set_value (adj, value);
3873 else if (x >= allocation.x + allocation.width &&
3874 adj->upper - adj->page_size > adj->value)
3876 value = adj->value + (x - allocation.x - allocation.width + 1);
3877 gtk_adjustment_set_value (adj, MAX (value, 0.0));
3881 adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window));
3882 if (adj && adj->upper - adj->lower > adj->page_size)
3884 if (y <= allocation.y &&
3885 adj->lower < adj->value)
3887 value = adj->value - (allocation.y - y + 1);
3888 gtk_adjustment_set_value (adj, value);
3890 else if (y >= allocation.height &&
3891 adj->upper - adj->page_size > adj->value)
3893 value = adj->value + (y - allocation.height + 1);
3894 gtk_adjustment_set_value (adj, MAX (value, 0.0));
3900 gtk_combo_box_list_scroll_timeout (GtkComboBox *combo_box)
3902 GtkComboBoxPrivate *priv = combo_box->priv;
3905 if (priv->auto_scroll)
3907 gdk_window_get_device_position (gtk_widget_get_window (priv->tree_view),
3910 gtk_combo_box_list_auto_scroll (combo_box, x, y);
3917 gtk_combo_box_list_enter_notify (GtkWidget *widget,
3918 GdkEventCrossing *event,
3921 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
3923 combo_box->priv->auto_scroll = TRUE;
3929 gtk_combo_box_list_select_func (GtkTreeSelection *selection,
3930 GtkTreeModel *model,
3932 gboolean path_currently_selected,
3935 GList *list, *columns;
3936 gboolean sensitive = FALSE;
3938 columns = gtk_tree_view_get_columns (gtk_tree_selection_get_tree_view (selection));
3940 for (list = columns; list && !sensitive; list = list->next)
3942 GList *cells, *cell;
3943 gboolean cell_sensitive, cell_visible;
3945 GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN (list->data);
3947 if (!gtk_tree_view_column_get_visible (column))
3950 gtk_tree_model_get_iter (model, &iter, path);
3951 gtk_tree_view_column_cell_set_cell_data (column, model, &iter,
3954 cell = cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
3957 g_object_get (cell->data,
3958 "sensitive", &cell_sensitive,
3959 "visible", &cell_visible,
3962 if (cell_visible && cell_sensitive)
3967 g_list_free (cells);
3969 sensitive = cell_sensitive;
3972 g_list_free (columns);
3979 * GtkCellEditable implementation
3983 gtk_combo_box_cell_editable_init (GtkCellEditableIface *iface)
3985 iface->start_editing = gtk_combo_box_start_editing;
3989 gtk_cell_editable_key_press (GtkWidget *widget,
3993 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
3995 if (event->keyval == GDK_KEY_Escape)
3997 g_object_set (combo_box,
3998 "editing-canceled", TRUE,
4000 gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (combo_box));
4001 gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (combo_box));
4005 else if (event->keyval == GDK_KEY_Return ||
4006 event->keyval == GDK_KEY_ISO_Enter ||
4007 event->keyval == GDK_KEY_KP_Enter)
4009 gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (combo_box));
4010 gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (combo_box));
4019 popdown_idle (gpointer data)
4021 GtkComboBox *combo_box;
4023 combo_box = GTK_COMBO_BOX (data);
4025 gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (combo_box));
4026 gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (combo_box));
4028 g_object_unref (combo_box);
4034 popdown_handler (GtkWidget *widget,
4037 gdk_threads_add_idle (popdown_idle, g_object_ref (data));
4041 popup_idle (gpointer data)
4043 GtkComboBox *combo_box;
4045 combo_box = GTK_COMBO_BOX (data);
4047 if (GTK_IS_MENU (combo_box->priv->popup_widget) &&
4048 combo_box->priv->cell_view)
4049 g_signal_connect_object (combo_box->priv->popup_widget,
4050 "unmap", G_CALLBACK (popdown_handler),
4053 /* we unset this if a menu item is activated */
4054 g_object_set (combo_box,
4055 "editing-canceled", TRUE,
4057 gtk_combo_box_popup (combo_box);
4059 combo_box->priv->popup_idle_id = 0;
4060 combo_box->priv->activate_button = 0;
4061 combo_box->priv->activate_time = 0;
4067 gtk_combo_box_start_editing (GtkCellEditable *cell_editable,
4070 GtkComboBox *combo_box = GTK_COMBO_BOX (cell_editable);
4073 combo_box->priv->is_cell_renderer = TRUE;
4075 if (combo_box->priv->cell_view)
4077 g_signal_connect_object (combo_box->priv->button, "key-press-event",
4078 G_CALLBACK (gtk_cell_editable_key_press),
4081 gtk_widget_grab_focus (combo_box->priv->button);
4085 child = gtk_bin_get_child (GTK_BIN (combo_box));
4087 g_signal_connect_object (child, "key-press-event",
4088 G_CALLBACK (gtk_cell_editable_key_press),
4091 gtk_widget_grab_focus (child);
4092 gtk_widget_set_can_focus (combo_box->priv->button, FALSE);
4095 /* we do the immediate popup only for the optionmenu-like
4098 if (combo_box->priv->is_cell_renderer &&
4099 combo_box->priv->cell_view && !combo_box->priv->tree_view)
4101 if (event && event->type == GDK_BUTTON_PRESS)
4103 GdkEventButton *event_button = (GdkEventButton *)event;
4105 combo_box->priv->activate_button = event_button->button;
4106 combo_box->priv->activate_time = event_button->time;
4109 combo_box->priv->popup_idle_id =
4110 gdk_threads_add_idle (popup_idle, combo_box);
4116 * GtkCellLayout implementation
4120 gtk_combo_box_cell_layout_init (GtkCellLayoutIface *iface)
4122 iface->get_area = gtk_combo_box_cell_layout_get_area;
4125 static GtkCellArea *
4126 gtk_combo_box_cell_layout_get_area (GtkCellLayout *layout)
4128 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4129 GtkComboBoxPrivate *priv;
4131 priv = combo_box->priv;
4137 * GtkBuildable implementation
4141 gtk_combo_box_buildable_init (GtkBuildableIface *iface)
4143 parent_buildable_iface = g_type_interface_peek_parent (iface);
4144 iface->add_child = _gtk_cell_layout_buildable_add_child;
4145 iface->custom_tag_start = gtk_combo_box_buildable_custom_tag_start;
4146 iface->custom_tag_end = gtk_combo_box_buildable_custom_tag_end;
4147 iface->get_internal_child = gtk_combo_box_buildable_get_internal_child;
4151 gtk_combo_box_buildable_custom_tag_start (GtkBuildable *buildable,
4152 GtkBuilder *builder,
4154 const gchar *tagname,
4155 GMarkupParser *parser,
4158 if (parent_buildable_iface->custom_tag_start (buildable, builder, child,
4159 tagname, parser, data))
4162 return _gtk_cell_layout_buildable_custom_tag_start (buildable, builder, child,
4163 tagname, parser, data);
4167 gtk_combo_box_buildable_custom_tag_end (GtkBuildable *buildable,
4168 GtkBuilder *builder,
4170 const gchar *tagname,
4173 if (_gtk_cell_layout_buildable_custom_tag_end (buildable, builder, child, tagname,
4177 parent_buildable_iface->custom_tag_end (buildable, builder, child, tagname,
4182 gtk_combo_box_buildable_get_internal_child (GtkBuildable *buildable,
4183 GtkBuilder *builder,
4184 const gchar *childname)
4186 GtkComboBox *combo_box = GTK_COMBO_BOX (buildable);
4188 if (combo_box->priv->has_entry && strcmp (childname, "entry") == 0)
4189 return G_OBJECT (gtk_bin_get_child (GTK_BIN (buildable)));
4191 return parent_buildable_iface->get_internal_child (buildable, builder, childname);
4199 * gtk_combo_box_new:
4201 * Creates a new empty #GtkComboBox.
4203 * Return value: A new #GtkComboBox.
4208 gtk_combo_box_new (void)
4210 return g_object_new (GTK_TYPE_COMBO_BOX, NULL);
4214 * gtk_combo_box_new_with_entry:
4216 * Creates a new empty #GtkComboBox with an entry.
4218 * Return value: A new #GtkComboBox.
4221 gtk_combo_box_new_with_entry (void)
4223 return g_object_new (GTK_TYPE_COMBO_BOX, "has-entry", TRUE, NULL);
4227 * gtk_combo_box_new_with_model:
4228 * @model: A #GtkTreeModel.
4230 * Creates a new #GtkComboBox with the model initialized to @model.
4232 * Return value: A new #GtkComboBox.
4237 gtk_combo_box_new_with_model (GtkTreeModel *model)
4239 GtkComboBox *combo_box;
4241 g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL);
4243 combo_box = g_object_new (GTK_TYPE_COMBO_BOX, "model", model, NULL);
4245 return GTK_WIDGET (combo_box);
4249 * gtk_combo_box_new_with_model_and_entry:
4250 * @model: A #GtkTreeModel
4252 * Creates a new empty #GtkComboBox with an entry
4253 * and with the model initialized to @model.
4255 * Return value: A new #GtkComboBox
4258 gtk_combo_box_new_with_model_and_entry (GtkTreeModel *model)
4260 return g_object_new (GTK_TYPE_COMBO_BOX,
4267 * gtk_combo_box_get_wrap_width:
4268 * @combo_box: A #GtkComboBox
4270 * Returns the wrap width which is used to determine the number of columns
4271 * for the popup menu. If the wrap width is larger than 1, the combo box
4274 * Returns: the wrap width.
4279 gtk_combo_box_get_wrap_width (GtkComboBox *combo_box)
4281 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), -1);
4283 return combo_box->priv->wrap_width;
4287 * gtk_combo_box_set_wrap_width:
4288 * @combo_box: A #GtkComboBox
4289 * @width: Preferred number of columns
4291 * Sets the wrap width of @combo_box to be @width. The wrap width is basically
4292 * the preferred number of columns when you want the popup to be layed out
4298 gtk_combo_box_set_wrap_width (GtkComboBox *combo_box,
4301 GtkComboBoxPrivate *priv;
4303 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4304 g_return_if_fail (width >= 0);
4306 priv = combo_box->priv;
4308 if (width != priv->wrap_width)
4310 priv->wrap_width = width;
4312 gtk_combo_box_check_appearance (combo_box);
4314 if (GTK_IS_TREE_MENU (priv->popup_widget))
4315 gtk_tree_menu_set_wrap_width (GTK_TREE_MENU (priv->popup_widget), priv->wrap_width);
4317 g_object_notify (G_OBJECT (combo_box), "wrap-width");
4322 * gtk_combo_box_get_row_span_column:
4323 * @combo_box: A #GtkComboBox
4325 * Returns the column with row span information for @combo_box.
4327 * Returns: the row span column.
4332 gtk_combo_box_get_row_span_column (GtkComboBox *combo_box)
4334 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), -1);
4336 return combo_box->priv->row_column;
4340 * gtk_combo_box_set_row_span_column:
4341 * @combo_box: A #GtkComboBox.
4342 * @row_span: A column in the model passed during construction.
4344 * Sets the column with row span information for @combo_box to be @row_span.
4345 * The row span column contains integers which indicate how many rows
4346 * an item should span.
4351 gtk_combo_box_set_row_span_column (GtkComboBox *combo_box,
4354 GtkComboBoxPrivate *priv;
4357 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4359 priv = combo_box->priv;
4361 col = gtk_tree_model_get_n_columns (priv->model);
4362 g_return_if_fail (row_span >= -1 && row_span < col);
4364 if (row_span != priv->row_column)
4366 priv->row_column = row_span;
4368 if (GTK_IS_TREE_MENU (priv->popup_widget))
4369 gtk_tree_menu_set_row_span_column (GTK_TREE_MENU (priv->popup_widget), priv->row_column);
4371 g_object_notify (G_OBJECT (combo_box), "row-span-column");
4376 * gtk_combo_box_get_column_span_column:
4377 * @combo_box: A #GtkComboBox
4379 * Returns the column with column span information for @combo_box.
4381 * Returns: the column span column.
4386 gtk_combo_box_get_column_span_column (GtkComboBox *combo_box)
4388 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), -1);
4390 return combo_box->priv->col_column;
4394 * gtk_combo_box_set_column_span_column:
4395 * @combo_box: A #GtkComboBox
4396 * @column_span: A column in the model passed during construction
4398 * Sets the column with column span information for @combo_box to be
4399 * @column_span. The column span column contains integers which indicate
4400 * how many columns an item should span.
4405 gtk_combo_box_set_column_span_column (GtkComboBox *combo_box,
4408 GtkComboBoxPrivate *priv;
4411 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4413 priv = combo_box->priv;
4415 col = gtk_tree_model_get_n_columns (priv->model);
4416 g_return_if_fail (column_span >= -1 && column_span < col);
4418 if (column_span != priv->col_column)
4420 priv->col_column = column_span;
4422 if (GTK_IS_TREE_MENU (priv->popup_widget))
4423 gtk_tree_menu_set_column_span_column (GTK_TREE_MENU (priv->popup_widget), priv->col_column);
4425 g_object_notify (G_OBJECT (combo_box), "column-span-column");
4430 * gtk_combo_box_get_active:
4431 * @combo_box: A #GtkComboBox
4433 * Returns the index of the currently active item, or -1 if there's no
4434 * active item. If the model is a non-flat treemodel, and the active item
4435 * is not an immediate child of the root of the tree, this function returns
4436 * <literal>gtk_tree_path_get_indices (path)[0]</literal>, where
4437 * <literal>path</literal> is the #GtkTreePath of the active item.
4439 * Return value: An integer which is the index of the currently active item,
4440 * or -1 if there's no active item.
4445 gtk_combo_box_get_active (GtkComboBox *combo_box)
4447 GtkComboBoxPrivate *priv;
4450 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), 0);
4452 priv = combo_box->priv;
4454 if (gtk_tree_row_reference_valid (priv->active_row))
4458 path = gtk_tree_row_reference_get_path (priv->active_row);
4459 result = gtk_tree_path_get_indices (path)[0];
4460 gtk_tree_path_free (path);
4469 * gtk_combo_box_set_active:
4470 * @combo_box: A #GtkComboBox
4471 * @index_: An index in the model passed during construction, or -1 to have
4474 * Sets the active item of @combo_box to be the item at @index.
4479 gtk_combo_box_set_active (GtkComboBox *combo_box,
4482 GtkTreePath *path = NULL;
4483 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4484 g_return_if_fail (index_ >= -1);
4486 if (combo_box->priv->model == NULL)
4488 /* Save index, in case the model is set after the index */
4489 combo_box->priv->active = index_;
4495 path = gtk_tree_path_new_from_indices (index_, -1);
4497 gtk_combo_box_set_active_internal (combo_box, path);
4500 gtk_tree_path_free (path);
4504 gtk_combo_box_set_active_internal (GtkComboBox *combo_box,
4507 GtkComboBoxPrivate *priv = combo_box->priv;
4508 GtkTreePath *active_path;
4511 /* Remember whether the initially active row is valid. */
4512 gboolean is_valid_row_reference = gtk_tree_row_reference_valid (priv->active_row);
4514 if (path && is_valid_row_reference)
4516 active_path = gtk_tree_row_reference_get_path (priv->active_row);
4517 path_cmp = gtk_tree_path_compare (path, active_path);
4518 gtk_tree_path_free (active_path);
4523 if (priv->active_row)
4525 gtk_tree_row_reference_free (priv->active_row);
4526 priv->active_row = NULL;
4531 if (priv->tree_view)
4532 gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view)));
4535 GtkMenu *menu = GTK_MENU (priv->popup_widget);
4537 if (GTK_IS_MENU (menu))
4538 gtk_menu_set_active (menu, -1);
4541 if (priv->cell_view)
4542 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (priv->cell_view), NULL);
4545 * Do not emit a "changed" signal when an already invalid selection was
4546 * now set to invalid.
4548 if (!is_valid_row_reference)
4554 gtk_tree_row_reference_new (priv->model, path);
4556 if (priv->tree_view)
4558 gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->tree_view),
4561 else if (GTK_IS_MENU (priv->popup_widget))
4563 /* FIXME handle nested menus better */
4564 gtk_menu_set_active (GTK_MENU (priv->popup_widget),
4565 gtk_tree_path_get_indices (path)[0]);
4568 if (priv->cell_view)
4569 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (priv->cell_view),
4573 g_signal_emit (combo_box, combo_box_signals[CHANGED], 0);
4574 g_object_notify (G_OBJECT (combo_box), "active");
4575 if (combo_box->priv->id_column >= 0)
4576 g_object_notify (G_OBJECT (combo_box), "active-id");
4581 * gtk_combo_box_get_active_iter:
4582 * @combo_box: A #GtkComboBox
4583 * @iter: (out): The uninitialized #GtkTreeIter
4585 * Sets @iter to point to the current active item, if it exists.
4587 * Return value: %TRUE, if @iter was set
4592 gtk_combo_box_get_active_iter (GtkComboBox *combo_box,
4598 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
4600 if (!gtk_tree_row_reference_valid (combo_box->priv->active_row))
4603 path = gtk_tree_row_reference_get_path (combo_box->priv->active_row);
4604 result = gtk_tree_model_get_iter (combo_box->priv->model, iter, path);
4605 gtk_tree_path_free (path);
4611 * gtk_combo_box_set_active_iter:
4612 * @combo_box: A #GtkComboBox
4613 * @iter: (allow-none): The #GtkTreeIter, or %NULL
4615 * Sets the current active item to be the one referenced by @iter, or
4616 * unsets the active item if @iter is %NULL.
4621 gtk_combo_box_set_active_iter (GtkComboBox *combo_box,
4624 GtkTreePath *path = NULL;
4626 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4629 path = gtk_tree_model_get_path (gtk_combo_box_get_model (combo_box), iter);
4631 gtk_combo_box_set_active_internal (combo_box, path);
4632 gtk_tree_path_free (path);
4636 * gtk_combo_box_set_model:
4637 * @combo_box: A #GtkComboBox
4638 * @model: (allow-none): A #GtkTreeModel
4640 * Sets the model used by @combo_box to be @model. Will unset a previously set
4641 * model (if applicable). If model is %NULL, then it will unset the model.
4643 * Note that this function does not clear the cell renderers, you have to
4644 * call gtk_cell_layout_clear() yourself if you need to set up different
4645 * cell renderers for the new model.
4650 gtk_combo_box_set_model (GtkComboBox *combo_box,
4651 GtkTreeModel *model)
4653 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4654 g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
4656 if (model == combo_box->priv->model)
4659 gtk_combo_box_unset_model (combo_box);
4664 combo_box->priv->model = model;
4665 g_object_ref (combo_box->priv->model);
4667 combo_box->priv->inserted_id =
4668 g_signal_connect (combo_box->priv->model, "row-inserted",
4669 G_CALLBACK (gtk_combo_box_model_row_inserted),
4671 combo_box->priv->deleted_id =
4672 g_signal_connect (combo_box->priv->model, "row-deleted",
4673 G_CALLBACK (gtk_combo_box_model_row_deleted),
4676 if (combo_box->priv->tree_view)
4679 gtk_tree_view_set_model (GTK_TREE_VIEW (combo_box->priv->tree_view),
4680 combo_box->priv->model);
4681 gtk_combo_box_list_popup_resize (combo_box);
4684 if (GTK_IS_TREE_MENU (combo_box->priv->popup_widget))
4687 gtk_tree_menu_set_model (GTK_TREE_MENU (combo_box->priv->popup_widget),
4688 combo_box->priv->model);
4691 if (combo_box->priv->cell_view)
4692 gtk_cell_view_set_model (GTK_CELL_VIEW (combo_box->priv->cell_view),
4693 combo_box->priv->model);
4695 if (combo_box->priv->active != -1)
4697 /* If an index was set in advance, apply it now */
4698 gtk_combo_box_set_active (combo_box, combo_box->priv->active);
4699 combo_box->priv->active = -1;
4703 g_object_notify (G_OBJECT (combo_box), "model");
4707 * gtk_combo_box_get_model:
4708 * @combo_box: A #GtkComboBox
4710 * Returns the #GtkTreeModel which is acting as data source for @combo_box.
4712 * Return value: (transfer none): A #GtkTreeModel which was passed
4713 * during construction.
4718 gtk_combo_box_get_model (GtkComboBox *combo_box)
4720 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
4722 return combo_box->priv->model;
4726 gtk_combo_box_entry_contents_changed (GtkEntry *entry,
4729 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
4732 * Fixes regression reported in bug #574059. The old functionality relied on
4733 * bug #572478. As a bugfix, we now emit the "changed" signal ourselves
4734 * when the selection was already set to -1.
4736 if (gtk_combo_box_get_active(combo_box) == -1)
4737 g_signal_emit_by_name (combo_box, "changed");
4739 gtk_combo_box_set_active (combo_box, -1);
4743 gtk_combo_box_entry_active_changed (GtkComboBox *combo_box,
4746 GtkComboBoxPrivate *priv = combo_box->priv;
4747 GtkTreeModel *model;
4750 if (gtk_combo_box_get_active_iter (combo_box, &iter))
4752 GtkEntry *entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (combo_box)));
4756 GValue value = {0,};
4758 g_signal_handlers_block_by_func (entry,
4759 gtk_combo_box_entry_contents_changed,
4762 model = gtk_combo_box_get_model (combo_box);
4764 gtk_tree_model_get_value (model, &iter,
4765 priv->text_column, &value);
4766 g_object_set_property (G_OBJECT (entry), "text", &value);
4767 g_value_unset (&value);
4769 g_signal_handlers_unblock_by_func (entry,
4770 gtk_combo_box_entry_contents_changed,
4777 * gtk_combo_box_get_add_tearoffs:
4778 * @combo_box: a #GtkComboBox
4780 * Gets the current value of the :add-tearoffs property.
4782 * Return value: the current value of the :add-tearoffs property.
4785 gtk_combo_box_get_add_tearoffs (GtkComboBox *combo_box)
4787 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
4789 return combo_box->priv->add_tearoffs;
4793 * gtk_combo_box_set_add_tearoffs:
4794 * @combo_box: a #GtkComboBox
4795 * @add_tearoffs: %TRUE to add tearoff menu items
4797 * Sets whether the popup menu should have a tearoff
4803 gtk_combo_box_set_add_tearoffs (GtkComboBox *combo_box,
4804 gboolean add_tearoffs)
4806 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4808 add_tearoffs = add_tearoffs != FALSE;
4810 if (combo_box->priv->add_tearoffs != add_tearoffs)
4812 combo_box->priv->add_tearoffs = add_tearoffs;
4814 if (GTK_IS_TREE_MENU (combo_box->priv->popup_widget))
4817 gtk_tree_menu_set_tearoff (GTK_TREE_MENU (combo_box->priv->popup_widget),
4818 combo_box->priv->add_tearoffs);
4822 g_object_notify (G_OBJECT (combo_box), "add-tearoffs");
4827 * gtk_combo_box_get_title:
4828 * @combo_box: a #GtkComboBox
4830 * Gets the current title of the menu in tearoff mode. See
4831 * gtk_combo_box_set_add_tearoffs().
4833 * Returns: the menu's title in tearoff mode. This is an internal copy of the
4834 * string which must not be freed.
4838 G_CONST_RETURN gchar*
4839 gtk_combo_box_get_title (GtkComboBox *combo_box)
4841 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
4843 return combo_box->priv->tearoff_title;
4847 gtk_combo_box_update_title (GtkComboBox *combo_box)
4849 gtk_combo_box_check_appearance (combo_box);
4851 if (combo_box->priv->popup_widget &&
4852 GTK_IS_MENU (combo_box->priv->popup_widget))
4853 gtk_menu_set_title (GTK_MENU (combo_box->priv->popup_widget),
4854 combo_box->priv->tearoff_title);
4858 * gtk_combo_box_set_title:
4859 * @combo_box: a #GtkComboBox
4860 * @title: a title for the menu in tearoff mode
4862 * Sets the menu's title in tearoff mode.
4867 gtk_combo_box_set_title (GtkComboBox *combo_box,
4870 GtkComboBoxPrivate *priv;
4872 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4874 priv = combo_box->priv;
4876 if (strcmp (title ? title : "",
4877 priv->tearoff_title ? priv->tearoff_title : "") != 0)
4879 g_free (priv->tearoff_title);
4880 priv->tearoff_title = g_strdup (title);
4882 gtk_combo_box_update_title (combo_box);
4884 g_object_notify (G_OBJECT (combo_box), "tearoff-title");
4890 * gtk_combo_box_set_popup_fixed_width:
4891 * @combo_box: a #GtkComboBox
4892 * @fixed: whether to use a fixed popup width
4894 * Specifies whether the popup's width should be a fixed width
4895 * matching the allocated width of the combo box.
4900 gtk_combo_box_set_popup_fixed_width (GtkComboBox *combo_box,
4903 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4905 if (combo_box->priv->popup_fixed_width != fixed)
4907 combo_box->priv->popup_fixed_width = fixed;
4909 g_object_notify (G_OBJECT (combo_box), "popup-fixed-width");
4914 * gtk_combo_box_get_popup_fixed_width:
4915 * @combo_box: a #GtkComboBox
4917 * Gets whether the popup uses a fixed width matching
4918 * the allocated width of the combo box.
4920 * Returns: %TRUE if the popup uses a fixed width
4925 gtk_combo_box_get_popup_fixed_width (GtkComboBox *combo_box)
4927 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
4929 return combo_box->priv->popup_fixed_width;
4934 * gtk_combo_box_get_popup_accessible:
4935 * @combo_box: a #GtkComboBox
4937 * Gets the accessible object corresponding to the combo box's popup.
4939 * This function is mostly intended for use by accessibility technologies;
4940 * applications should have little use for it.
4942 * Returns: (transfer none): the accessible object corresponding
4943 * to the combo box's popup.
4948 gtk_combo_box_get_popup_accessible (GtkComboBox *combo_box)
4952 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
4954 if (combo_box->priv->popup_widget)
4956 atk_obj = gtk_widget_get_accessible (combo_box->priv->popup_widget);
4964 * gtk_combo_box_get_row_separator_func:
4965 * @combo_box: a #GtkComboBox
4967 * Returns the current row separator function.
4969 * Return value: the current row separator function.
4973 GtkTreeViewRowSeparatorFunc
4974 gtk_combo_box_get_row_separator_func (GtkComboBox *combo_box)
4976 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
4978 return combo_box->priv->row_separator_func;
4982 * gtk_combo_box_set_row_separator_func:
4983 * @combo_box: a #GtkComboBox
4984 * @func: a #GtkTreeViewRowSeparatorFunc
4985 * @data: (allow-none): user data to pass to @func, or %NULL
4986 * @destroy: (allow-none): destroy notifier for @data, or %NULL
4988 * Sets the row separator function, which is used to determine
4989 * whether a row should be drawn as a separator. If the row separator
4990 * function is %NULL, no separators are drawn. This is the default value.
4995 gtk_combo_box_set_row_separator_func (GtkComboBox *combo_box,
4996 GtkTreeViewRowSeparatorFunc func,
4998 GDestroyNotify destroy)
5000 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5002 if (combo_box->priv->row_separator_destroy)
5003 combo_box->priv->row_separator_destroy (combo_box->priv->row_separator_data);
5005 combo_box->priv->row_separator_func = func;
5006 combo_box->priv->row_separator_data = data;
5007 combo_box->priv->row_separator_destroy = destroy;
5009 /* Provoke the underlying treeview/menu to rebuild themselves with the new separator func */
5010 if (combo_box->priv->tree_view)
5012 gtk_tree_view_set_model (GTK_TREE_VIEW (combo_box->priv->tree_view), NULL);
5013 gtk_tree_view_set_model (GTK_TREE_VIEW (combo_box->priv->tree_view), combo_box->priv->model);
5016 if (GTK_IS_TREE_MENU (combo_box->priv->popup_widget))
5018 gtk_tree_menu_set_model (GTK_TREE_MENU (combo_box->priv->popup_widget), NULL);
5019 gtk_tree_menu_set_model (GTK_TREE_MENU (combo_box->priv->popup_widget), combo_box->priv->model);
5022 gtk_widget_queue_draw (GTK_WIDGET (combo_box));
5026 * gtk_combo_box_set_button_sensitivity:
5027 * @combo_box: a #GtkComboBox
5028 * @sensitivity: specify the sensitivity of the dropdown button
5030 * Sets whether the dropdown button of the combo box should be
5031 * always sensitive (%GTK_SENSITIVITY_ON), never sensitive (%GTK_SENSITIVITY_OFF)
5032 * or only if there is at least one item to display (%GTK_SENSITIVITY_AUTO).
5037 gtk_combo_box_set_button_sensitivity (GtkComboBox *combo_box,
5038 GtkSensitivityType sensitivity)
5040 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5042 if (combo_box->priv->button_sensitivity != sensitivity)
5044 combo_box->priv->button_sensitivity = sensitivity;
5045 gtk_combo_box_update_sensitivity (combo_box);
5047 g_object_notify (G_OBJECT (combo_box), "button-sensitivity");
5052 * gtk_combo_box_get_button_sensitivity:
5053 * @combo_box: a #GtkComboBox
5055 * Returns whether the combo box sets the dropdown button
5056 * sensitive or not when there are no items in the model.
5058 * Return Value: %GTK_SENSITIVITY_ON if the dropdown button
5059 * is sensitive when the model is empty, %GTK_SENSITIVITY_OFF
5060 * if the button is always insensitive or
5061 * %GTK_SENSITIVITY_AUTO if it is only sensitive as long as
5062 * the model has one item to be selected.
5067 gtk_combo_box_get_button_sensitivity (GtkComboBox *combo_box)
5069 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
5071 return combo_box->priv->button_sensitivity;
5076 * gtk_combo_box_get_has_entry:
5077 * @combo_box: a #GtkComboBox
5079 * Returns whether the combo box has an entry.
5081 * Return Value: whether there is an entry in @combo_box.
5086 gtk_combo_box_get_has_entry (GtkComboBox *combo_box)
5088 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
5090 return combo_box->priv->has_entry;
5094 * gtk_combo_box_set_entry_text_column:
5095 * @combo_box: A #GtkComboBox
5096 * @text_column: A column in @model to get the strings from for
5097 * the internal entry
5099 * Sets the model column which @combo_box should use to get strings from
5100 * to be @text_column. The column @text_column in the model of @combo_box
5101 * must be of type %G_TYPE_STRING.
5103 * This is only relevant if @combo_box has been created with
5104 * #GtkComboBox:has-entry as %TRUE.
5109 gtk_combo_box_set_entry_text_column (GtkComboBox *combo_box,
5112 GtkComboBoxPrivate *priv = combo_box->priv;
5113 GtkTreeModel *model;
5115 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5117 model = gtk_combo_box_get_model (combo_box);
5119 g_return_if_fail (text_column >= 0);
5120 g_return_if_fail (model == NULL || text_column < gtk_tree_model_get_n_columns (model));
5122 priv->text_column = text_column;
5124 if (priv->text_renderer != NULL)
5125 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box),
5126 priv->text_renderer,
5127 "text", text_column,
5132 * gtk_combo_box_get_entry_text_column:
5133 * @combo_box: A #GtkComboBox.
5135 * Returns the column which @combo_box is using to get the strings
5136 * from to display in the internal entry.
5138 * Return value: A column in the data source model of @combo_box.
5143 gtk_combo_box_get_entry_text_column (GtkComboBox *combo_box)
5145 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), 0);
5147 return combo_box->priv->text_column;
5151 * gtk_combo_box_set_focus_on_click:
5152 * @combo: a #GtkComboBox
5153 * @focus_on_click: whether the combo box grabs focus when clicked
5156 * Sets whether the combo box will grab focus when it is clicked with
5157 * the mouse. Making mouse clicks not grab focus is useful in places
5158 * like toolbars where you don't want the keyboard focus removed from
5159 * the main area of the application.
5164 gtk_combo_box_set_focus_on_click (GtkComboBox *combo_box,
5165 gboolean focus_on_click)
5167 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5169 focus_on_click = focus_on_click != FALSE;
5171 if (combo_box->priv->focus_on_click != focus_on_click)
5173 combo_box->priv->focus_on_click = focus_on_click;
5175 if (combo_box->priv->button)
5176 gtk_button_set_focus_on_click (GTK_BUTTON (combo_box->priv->button),
5179 g_object_notify (G_OBJECT (combo_box), "focus-on-click");
5184 * gtk_combo_box_get_focus_on_click:
5185 * @combo: a #GtkComboBox
5187 * Returns whether the combo box grabs focus when it is clicked
5188 * with the mouse. See gtk_combo_box_set_focus_on_click().
5190 * Return value: %TRUE if the combo box grabs focus when it is
5191 * clicked with the mouse.
5196 gtk_combo_box_get_focus_on_click (GtkComboBox *combo_box)
5198 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
5200 return combo_box->priv->focus_on_click;
5205 * gtk_combo_box_popup:
5206 * @combo_box: a #GtkComboBox
5208 * Pops up the menu or dropdown list of @combo_box.
5210 * This function is mostly intended for use by accessibility technologies;
5211 * applications should have little use for it.
5216 gtk_combo_box_popup (GtkComboBox *combo_box)
5218 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5220 g_signal_emit (combo_box, combo_box_signals[POPUP], 0);
5224 * gtk_combo_box_popup_for_device:
5225 * @combo_box: a #GtkComboBox
5226 * @device: a #GdkDevice
5228 * Pops up the menu or dropdown list of @combo_box, the popup window
5229 * will be grabbed so only @device and its associated pointer/keyboard
5230 * are the only #GdkDevice<!-- -->s able to send events to it.
5235 gtk_combo_box_popup_for_device (GtkComboBox *combo_box,
5238 GtkComboBoxPrivate *priv = combo_box->priv;
5239 gint x, y, width, height;
5240 GtkTreePath *path = NULL, *ppath;
5241 GtkWidget *toplevel;
5242 GdkDevice *keyboard, *pointer;
5245 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5246 g_return_if_fail (GDK_IS_DEVICE (device));
5248 if (!gtk_widget_get_realized (GTK_WIDGET (combo_box)))
5251 if (gtk_widget_get_mapped (priv->popup_widget))
5254 if (priv->grab_pointer && priv->grab_keyboard)
5257 time = gtk_get_current_event_time ();
5259 if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
5262 pointer = gdk_device_get_associated_device (device);
5267 keyboard = gdk_device_get_associated_device (device);
5270 if (GTK_IS_MENU (priv->popup_widget))
5272 gtk_combo_box_menu_popup (combo_box,
5273 priv->activate_button,
5274 priv->activate_time);
5278 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (combo_box));
5279 if (GTK_IS_WINDOW (toplevel))
5280 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
5281 GTK_WINDOW (priv->popup_window));
5283 gtk_widget_show_all (priv->scrolled_window);
5284 gtk_combo_box_list_position (combo_box, &x, &y, &width, &height);
5286 gtk_widget_set_size_request (priv->popup_window, width, height);
5287 gtk_window_move (GTK_WINDOW (priv->popup_window), x, y);
5289 if (gtk_tree_row_reference_valid (priv->active_row))
5291 path = gtk_tree_row_reference_get_path (priv->active_row);
5292 ppath = gtk_tree_path_copy (path);
5293 if (gtk_tree_path_up (ppath))
5294 gtk_tree_view_expand_to_path (GTK_TREE_VIEW (priv->tree_view),
5296 gtk_tree_path_free (ppath);
5298 gtk_tree_view_set_hover_expand (GTK_TREE_VIEW (priv->tree_view),
5302 gtk_widget_show (priv->popup_window);
5306 gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->tree_view),
5308 gtk_tree_path_free (path);
5311 gtk_widget_grab_focus (priv->popup_window);
5312 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button),
5315 if (!gtk_widget_has_focus (priv->tree_view))
5316 gtk_widget_grab_focus (priv->tree_view);
5318 if (!popup_grab_on_window (gtk_widget_get_window (priv->popup_window),
5319 keyboard, pointer, time))
5321 gtk_widget_hide (priv->popup_window);
5325 gtk_device_grab_add (priv->popup_window, pointer, TRUE);
5326 priv->grab_pointer = pointer;
5327 priv->grab_keyboard = keyboard;
5331 * gtk_combo_box_popdown:
5332 * @combo_box: a #GtkComboBox
5334 * Hides the menu or dropdown list of @combo_box.
5336 * This function is mostly intended for use by accessibility technologies;
5337 * applications should have little use for it.
5342 gtk_combo_box_popdown (GtkComboBox *combo_box)
5344 GtkComboBoxPrivate *priv = combo_box->priv;
5346 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5348 if (GTK_IS_MENU (priv->popup_widget))
5350 gtk_menu_popdown (GTK_MENU (priv->popup_widget));
5354 if (!gtk_widget_get_realized (GTK_WIDGET (combo_box)))
5357 gtk_device_grab_remove (priv->popup_window, priv->grab_pointer);
5358 gtk_widget_hide (priv->popup_window);
5359 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button),
5362 priv->grab_pointer = NULL;
5363 priv->grab_keyboard = NULL;
5367 * gtk_combo_box_set_id_column:
5368 * @combo_box: A #GtkComboBox
5369 * @id_column: A column in @model to get string IDs for values from
5371 * Sets the model column which @combo_box should use to get string IDs
5372 * for values from. The column @id_column in the model of @combo_box
5373 * must be of type %G_TYPE_STRING.
5378 gtk_combo_box_set_id_column (GtkComboBox *combo_box,
5381 GtkComboBoxPrivate *priv = combo_box->priv;
5382 GtkTreeModel *model;
5384 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5386 if (id_column != priv->id_column)
5388 model = gtk_combo_box_get_model (combo_box);
5390 g_return_if_fail (id_column >= 0);
5391 g_return_if_fail (model == NULL ||
5392 id_column < gtk_tree_model_get_n_columns (model));
5394 priv->id_column = id_column;
5396 g_object_notify (G_OBJECT (combo_box), "id-column");
5397 g_object_notify (G_OBJECT (combo_box), "active-id");
5402 * gtk_combo_box_get_id_column:
5403 * @combo_box: A #GtkComboBox
5405 * Returns the column which @combo_box is using to get string IDs
5408 * Return value: A column in the data source model of @combo_box.
5413 gtk_combo_box_get_id_column (GtkComboBox *combo_box)
5415 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), 0);
5417 return combo_box->priv->id_column;
5421 * gtk_combo_box_get_active_id:
5422 * @combo_box: a #GtkComboBox
5424 * Returns the ID of the active row of @combo_box. This value is taken
5425 * from the active row and the column specified by the 'id-column'
5426 * property of @combo_box (see gtk_combo_box_set_id_column()).
5428 * The returned value is an interned string which means that you can
5429 * compare the pointer by value to other interned strings and that you
5432 * If the 'id-column' property of @combo_box is not set or if no row is
5433 * selected then %NULL is returned.
5435 * Return value: the ID of the active row, or %NULL
5440 gtk_combo_box_get_active_id (GtkComboBox *combo_box)
5442 GtkTreeModel *model;
5446 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), 0);
5448 column = combo_box->priv->id_column;
5453 model = gtk_combo_box_get_model (combo_box);
5454 g_return_val_if_fail (gtk_tree_model_get_column_type (model, column) ==
5455 G_TYPE_STRING, NULL);
5457 if (gtk_combo_box_get_active_iter (combo_box, &iter))
5459 const gchar *interned;
5462 gtk_tree_model_get (model, &iter, column, &id, -1);
5463 interned = g_intern_string (id);
5473 * gtk_combo_box_set_active_id:
5474 * @combo_box: a #GtkComboBox
5475 * @active_id: the ID of the row to select
5477 * Changes the active row of @combo_box to the one that has an ID equal to @id.
5479 * If the 'id-column' property of @combo_box is unset or if no row has
5480 * the given ID then nothing happens.
5485 gtk_combo_box_set_active_id (GtkComboBox *combo_box,
5486 const gchar *active_id)
5488 GtkTreeModel *model;
5492 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5494 column = combo_box->priv->id_column;
5499 model = gtk_combo_box_get_model (combo_box);
5500 g_return_if_fail (gtk_tree_model_get_column_type (model, column) ==
5503 if (gtk_tree_model_get_iter_first (model, &iter))
5508 gtk_tree_model_get (model, &iter, column, &id, -1);
5509 match = strcmp (id, active_id) == 0;
5514 gtk_combo_box_set_active_iter (combo_box, &iter);
5517 } while (gtk_tree_model_iter_next (model, &iter));