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"
22 #include "gtkcellareabox.h"
23 #include "gtktreemenu.h"
25 #include "gtkbindings.h"
26 #include "gtkcelllayout.h"
27 #include "gtkcellrenderertext.h"
28 #include "gtkcellview.h"
29 #include "gtkeventbox.h"
32 #include "gtkliststore.h"
34 #include "gtkmenuprivate.h"
35 #include "gtkmenushellprivate.h"
36 #include "gtkscrolledwindow.h"
37 #include "gtkseparatormenuitem.h"
38 #include "gtktearoffmenuitem.h"
39 #include "gtktogglebutton.h"
40 #include "gtktreeselection.h"
41 #include "gtkseparator.h"
42 #include "gtkwindow.h"
43 #include "gtktypebuiltins.h"
44 #include "gtkprivate.h"
46 #include <gobject/gvaluecollector.h>
51 #include "gtkmarshalers.h"
54 #include "gtkentryprivate.h"
55 #include "gtktreeprivate.h"
60 * @Short_description: A widget used to choose from a list of items
62 * @See_also: #GtkComboBoxText, #GtkTreeModel, #GtkCellRenderer
64 * A GtkComboBox is a widget that allows the user to choose from a list of
65 * valid choices. The GtkComboBox displays the selected choice. When
66 * activated, the GtkComboBox displays a popup which allows the user to
67 * make a new choice. The style in which the selected value is displayed,
68 * and the style of the popup is determined by the current theme. It may
69 * be similar to a Windows-style combo box.
71 * The GtkComboBox uses the model-view pattern; the list of valid choices
72 * is specified in the form of a tree model, and the display of the choices
73 * can be adapted to the data in the model by using cell renderers, as you
74 * would in a tree view. This is possible since GtkComboBox implements the
75 * #GtkCellLayout interface. The tree model holding the valid choices is
76 * not restricted to a flat list, it can be a real tree, and the popup will
77 * reflect the tree structure.
79 * To allow the user to enter values not in the model, the 'has-entry'
80 * property allows the GtkComboBox to contain a #GtkEntry. This entry
81 * can be accessed by calling gtk_bin_get_child() on the combo box.
83 * For a simple list of textual choices, the model-view API of GtkComboBox
84 * can be a bit overwhelming. In this case, #GtkComboBoxText offers a
85 * simple alternative. Both GtkComboBox and #GtkComboBoxText can contain
90 /* WELCOME, to THE house of evil code */
91 struct _GtkComboBoxPrivate
101 GtkShadowType shadow_type;
103 gint active; /* Only temporary */
104 GtkTreeRowReference *active_row;
106 GtkWidget *tree_view;
108 GtkWidget *cell_view;
109 GtkWidget *cell_view_frame;
114 GtkWidget *separator;
116 GtkWidget *popup_widget;
117 GtkWidget *popup_window;
118 GtkWidget *scrolled_window;
125 guint activate_button;
126 guint32 activate_time;
128 guint resize_idle_id;
130 /* For "has-entry" specific behavior we track
131 * an automated cell renderer and text column
134 GtkCellRenderer *text_renderer;
138 guint popup_in_progress : 1;
139 guint popup_shown : 1;
140 guint add_tearoffs : 1;
142 guint is_cell_renderer : 1;
143 guint editing_canceled : 1;
144 guint auto_scroll : 1;
145 guint focus_on_click : 1;
146 guint button_sensitivity : 2;
148 guint popup_fixed_width : 1;
150 GtkTreeViewRowSeparatorFunc row_separator_func;
151 gpointer row_separator_data;
152 GDestroyNotify row_separator_destroy;
154 GdkDevice *grab_pointer;
155 GdkDevice *grab_keyboard;
157 gchar *tearoff_title;
160 /* While debugging this evil code, I have learned that
161 * there are actually 4 modes to this widget, which can
162 * be characterized as follows
164 * 1) menu mode, no child added
167 * cell_view -> GtkCellView, regular child
168 * cell_view_frame -> NULL
169 * button -> GtkToggleButton set_parent to combo
170 * arrow -> GtkArrow set_parent to button
171 * separator -> GtkVSepator set_parent to button
172 * popup_widget -> GtkMenu
173 * popup_window -> NULL
174 * scrolled_window -> NULL
176 * 2) menu mode, child added
180 * cell_view_frame -> NULL
181 * button -> GtkToggleButton set_parent to combo
182 * arrow -> GtkArrow, child of button
184 * popup_widget -> GtkMenu
185 * popup_window -> NULL
186 * scrolled_window -> NULL
188 * 3) list mode, no child added
190 * tree_view -> GtkTreeView, child of scrolled_window
191 * cell_view -> GtkCellView, regular child
192 * cell_view_frame -> GtkFrame, set parent to combo
193 * button -> GtkToggleButton, set_parent to combo
194 * arrow -> GtkArrow, child of button
196 * popup_widget -> tree_view
197 * popup_window -> GtkWindow
198 * scrolled_window -> GtkScrolledWindow, child of popup_window
200 * 4) list mode, child added
202 * tree_view -> GtkTreeView, child of scrolled_window
204 * cell_view_frame -> NULL
205 * button -> GtkToggleButton, set_parent to combo
206 * arrow -> GtkArrow, child of button
208 * popup_widget -> tree_view
209 * popup_window -> GtkWindow
210 * scrolled_window -> GtkScrolledWindow, child of popup_window
226 PROP_ROW_SPAN_COLUMN,
227 PROP_COLUMN_SPAN_COLUMN,
234 PROP_BUTTON_SENSITIVITY,
235 PROP_EDITING_CANCELED,
237 PROP_ENTRY_TEXT_COLUMN,
238 PROP_POPUP_FIXED_WIDTH,
244 static guint combo_box_signals[LAST_SIGNAL] = {0,};
246 #define BONUS_PADDING 4
247 #define SCROLL_TIME 100
251 static void gtk_combo_box_cell_layout_init (GtkCellLayoutIface *iface);
252 static void gtk_combo_box_cell_editable_init (GtkCellEditableIface *iface);
253 static GObject *gtk_combo_box_constructor (GType type,
254 guint n_construct_properties,
255 GObjectConstructParam *construct_properties);
256 static void gtk_combo_box_dispose (GObject *object);
257 static void gtk_combo_box_finalize (GObject *object);
258 static void gtk_combo_box_destroy (GtkWidget *widget);
260 static void gtk_combo_box_set_property (GObject *object,
264 static void gtk_combo_box_get_property (GObject *object,
269 static void gtk_combo_box_state_flags_changed (GtkWidget *widget,
270 GtkStateFlags previous);
271 static void gtk_combo_box_grab_focus (GtkWidget *widget);
272 static void gtk_combo_box_style_updated (GtkWidget *widget);
273 static void gtk_combo_box_button_toggled (GtkWidget *widget,
275 static void gtk_combo_box_button_state_flags_changed (GtkWidget *widget,
276 GtkStateFlags previous,
278 static void gtk_combo_box_add (GtkContainer *container,
280 static void gtk_combo_box_remove (GtkContainer *container,
283 static void gtk_combo_box_menu_show (GtkWidget *menu,
285 static void gtk_combo_box_menu_hide (GtkWidget *menu,
288 static void gtk_combo_box_set_popup_widget (GtkComboBox *combo_box,
290 static void gtk_combo_box_menu_position_below (GtkMenu *menu,
295 static void gtk_combo_box_menu_position_over (GtkMenu *menu,
300 static void gtk_combo_box_menu_position (GtkMenu *menu,
306 static void gtk_combo_box_unset_model (GtkComboBox *combo_box);
308 static void gtk_combo_box_size_allocate (GtkWidget *widget,
309 GtkAllocation *allocation);
310 static void gtk_combo_box_forall (GtkContainer *container,
311 gboolean include_internals,
312 GtkCallback callback,
313 gpointer callback_data);
314 static gboolean gtk_combo_box_draw (GtkWidget *widget,
316 static gboolean gtk_combo_box_scroll_event (GtkWidget *widget,
317 GdkEventScroll *event);
318 static void gtk_combo_box_set_active_internal (GtkComboBox *combo_box,
321 static void gtk_combo_box_check_appearance (GtkComboBox *combo_box);
322 static void gtk_combo_box_real_move_active (GtkComboBox *combo_box,
323 GtkScrollType scroll);
324 static void gtk_combo_box_real_popup (GtkComboBox *combo_box);
325 static gboolean gtk_combo_box_real_popdown (GtkComboBox *combo_box);
327 /* listening to the model */
328 static void gtk_combo_box_model_row_inserted (GtkTreeModel *model,
332 static void gtk_combo_box_model_row_deleted (GtkTreeModel *model,
335 static void gtk_combo_box_model_rows_reordered (GtkTreeModel *model,
340 static void gtk_combo_box_model_row_changed (GtkTreeModel *model,
344 static void gtk_combo_box_model_row_expanded (GtkTreeModel *model,
350 static void gtk_combo_box_list_position (GtkComboBox *combo_box,
355 static void gtk_combo_box_list_setup (GtkComboBox *combo_box);
356 static void gtk_combo_box_list_destroy (GtkComboBox *combo_box);
358 static gboolean gtk_combo_box_list_button_released (GtkWidget *widget,
359 GdkEventButton *event,
361 static gboolean gtk_combo_box_list_key_press (GtkWidget *widget,
364 static gboolean gtk_combo_box_list_enter_notify (GtkWidget *widget,
365 GdkEventCrossing *event,
367 static void gtk_combo_box_list_auto_scroll (GtkComboBox *combo,
370 static gboolean gtk_combo_box_list_scroll_timeout (GtkComboBox *combo);
371 static gboolean gtk_combo_box_list_button_pressed (GtkWidget *widget,
372 GdkEventButton *event,
375 static gboolean gtk_combo_box_list_select_func (GtkTreeSelection *selection,
378 gboolean path_currently_selected,
381 static void gtk_combo_box_list_row_changed (GtkTreeModel *model,
385 static void gtk_combo_box_list_popup_resize (GtkComboBox *combo_box);
388 static void gtk_combo_box_menu_setup (GtkComboBox *combo_box,
389 gboolean add_children);
390 static void gtk_combo_box_update_title (GtkComboBox *combo_box);
391 static void gtk_combo_box_menu_destroy (GtkComboBox *combo_box);
394 static gboolean gtk_combo_box_menu_button_press (GtkWidget *widget,
395 GdkEventButton *event,
397 static void gtk_combo_box_menu_activate (GtkWidget *menu,
399 GtkComboBox *combo_box);
400 static void gtk_combo_box_update_sensitivity (GtkComboBox *combo_box);
401 static gboolean gtk_combo_box_menu_key_press (GtkWidget *widget,
404 static void gtk_combo_box_menu_popup (GtkComboBox *combo_box,
406 guint32 activate_time);
409 static GtkCellArea *gtk_combo_box_cell_layout_get_area (GtkCellLayout *cell_layout);
411 static gboolean gtk_combo_box_mnemonic_activate (GtkWidget *widget,
412 gboolean group_cycling);
414 static void gtk_combo_box_child_show (GtkWidget *widget,
415 GtkComboBox *combo_box);
416 static void gtk_combo_box_child_hide (GtkWidget *widget,
417 GtkComboBox *combo_box);
419 /* GtkComboBox:has-entry callbacks */
420 static void gtk_combo_box_entry_contents_changed (GtkEntry *entry,
422 static void gtk_combo_box_entry_active_changed (GtkComboBox *combo_box,
426 /* GtkBuildable method implementation */
427 static GtkBuildableIface *parent_buildable_iface;
429 static void gtk_combo_box_buildable_init (GtkBuildableIface *iface);
430 static gboolean gtk_combo_box_buildable_custom_tag_start (GtkBuildable *buildable,
433 const gchar *tagname,
434 GMarkupParser *parser,
436 static void gtk_combo_box_buildable_custom_tag_end (GtkBuildable *buildable,
439 const gchar *tagname,
441 static GObject *gtk_combo_box_buildable_get_internal_child (GtkBuildable *buildable,
443 const gchar *childname);
446 /* GtkCellEditable method implementations */
447 static void gtk_combo_box_start_editing (GtkCellEditable *cell_editable,
450 static void gtk_combo_box_get_preferred_width (GtkWidget *widget,
453 static void gtk_combo_box_get_preferred_height (GtkWidget *widget,
456 static void gtk_combo_box_get_preferred_width_for_height (GtkWidget *widget,
460 static void gtk_combo_box_get_preferred_height_for_width (GtkWidget *widget,
466 G_DEFINE_TYPE_WITH_CODE (GtkComboBox, gtk_combo_box, GTK_TYPE_BIN,
467 G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
468 gtk_combo_box_cell_layout_init)
469 G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_EDITABLE,
470 gtk_combo_box_cell_editable_init)
471 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
472 gtk_combo_box_buildable_init))
477 gtk_combo_box_class_init (GtkComboBoxClass *klass)
479 GObjectClass *object_class;
480 GtkContainerClass *container_class;
481 GtkWidgetClass *widget_class;
482 GtkBindingSet *binding_set;
484 container_class = (GtkContainerClass *)klass;
485 container_class->forall = gtk_combo_box_forall;
486 container_class->add = gtk_combo_box_add;
487 container_class->remove = gtk_combo_box_remove;
489 widget_class = (GtkWidgetClass *)klass;
490 widget_class->size_allocate = gtk_combo_box_size_allocate;
491 widget_class->draw = gtk_combo_box_draw;
492 widget_class->scroll_event = gtk_combo_box_scroll_event;
493 widget_class->mnemonic_activate = gtk_combo_box_mnemonic_activate;
494 widget_class->grab_focus = gtk_combo_box_grab_focus;
495 widget_class->style_updated = gtk_combo_box_style_updated;
496 widget_class->state_flags_changed = gtk_combo_box_state_flags_changed;
497 widget_class->get_preferred_width = gtk_combo_box_get_preferred_width;
498 widget_class->get_preferred_height = gtk_combo_box_get_preferred_height;
499 widget_class->get_preferred_height_for_width = gtk_combo_box_get_preferred_height_for_width;
500 widget_class->get_preferred_width_for_height = gtk_combo_box_get_preferred_width_for_height;
501 widget_class->destroy = gtk_combo_box_destroy;
503 object_class = (GObjectClass *)klass;
504 object_class->constructor = gtk_combo_box_constructor;
505 object_class->dispose = gtk_combo_box_dispose;
506 object_class->finalize = gtk_combo_box_finalize;
507 object_class->set_property = gtk_combo_box_set_property;
508 object_class->get_property = gtk_combo_box_get_property;
512 * GtkComboBox::changed:
513 * @widget: the object which received the signal
515 * The changed signal is emitted when the active
516 * item is changed. The can be due to the user selecting
517 * a different item from the list, or due to a
518 * call to gtk_combo_box_set_active_iter().
519 * It will also be emitted while typing into the entry of a combo box
524 combo_box_signals[CHANGED] =
525 g_signal_new (I_("changed"),
526 G_OBJECT_CLASS_TYPE (klass),
528 G_STRUCT_OFFSET (GtkComboBoxClass, changed),
530 g_cclosure_marshal_VOID__VOID,
533 * GtkComboBox::move-active:
534 * @widget: the object that received the signal
535 * @scroll_type: a #GtkScrollType
537 * The ::move-active signal is a
538 * <link linkend="keybinding-signals">keybinding signal</link>
539 * which gets emitted to move the active selection.
543 combo_box_signals[MOVE_ACTIVE] =
544 g_signal_new_class_handler (I_("move-active"),
545 G_OBJECT_CLASS_TYPE (klass),
546 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
547 G_CALLBACK (gtk_combo_box_real_move_active),
549 g_cclosure_marshal_VOID__ENUM,
551 GTK_TYPE_SCROLL_TYPE);
554 * GtkComboBox::popup:
555 * @widget: the object that received the signal
557 * The ::popup signal is a
558 * <link linkend="keybinding-signals">keybinding signal</link>
559 * which gets emitted to popup the combo box list.
561 * The default binding for this signal is Alt+Down.
565 combo_box_signals[POPUP] =
566 g_signal_new_class_handler (I_("popup"),
567 G_OBJECT_CLASS_TYPE (klass),
568 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
569 G_CALLBACK (gtk_combo_box_real_popup),
571 g_cclosure_marshal_VOID__VOID,
574 * GtkComboBox::popdown:
575 * @button: the object which received the signal
577 * The ::popdown signal is a
578 * <link linkend="keybinding-signals">keybinding signal</link>
579 * which gets emitted to popdown the combo box list.
581 * The default bindings for this signal are Alt+Up and Escape.
585 combo_box_signals[POPDOWN] =
586 g_signal_new_class_handler (I_("popdown"),
587 G_OBJECT_CLASS_TYPE (klass),
588 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
589 G_CALLBACK (gtk_combo_box_real_popdown),
591 _gtk_marshal_BOOLEAN__VOID,
595 binding_set = gtk_binding_set_by_class (widget_class);
597 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Down, GDK_MOD1_MASK,
599 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Down, GDK_MOD1_MASK,
602 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Up, GDK_MOD1_MASK,
604 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Up, GDK_MOD1_MASK,
606 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0,
609 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Up, 0,
611 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_UP);
612 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Up, 0,
614 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_UP);
615 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Up, 0,
617 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_UP);
618 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Page_Up, 0,
620 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_UP);
621 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Home, 0,
623 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_START);
624 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Home, 0,
626 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_START);
628 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Down, 0,
630 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_DOWN);
631 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Down, 0,
633 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_DOWN);
634 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Down, 0,
636 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_DOWN);
637 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Page_Down, 0,
639 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_DOWN);
640 gtk_binding_entry_add_signal (binding_set, GDK_KEY_End, 0,
642 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_END);
643 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_End, 0,
645 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_END);
648 g_object_class_override_property (object_class,
649 PROP_EDITING_CANCELED,
655 * The model from which the combo box takes the values shown
660 g_object_class_install_property (object_class,
662 g_param_spec_object ("model",
663 P_("ComboBox model"),
664 P_("The model for the combo box"),
666 GTK_PARAM_READWRITE));
669 * GtkComboBox:wrap-width:
671 * If wrap-width is set to a positive value, the list will be
672 * displayed in multiple columns, the number of columns is
673 * determined by wrap-width.
677 g_object_class_install_property (object_class,
679 g_param_spec_int ("wrap-width",
681 P_("Wrap width for laying out the items in a grid"),
685 GTK_PARAM_READWRITE));
689 * GtkComboBox:row-span-column:
691 * If this is set to a non-negative value, it must be the index of a column
692 * of type %G_TYPE_INT in the model.
694 * The values of that column are used to determine how many rows a value in
695 * the list will span. Therefore, the values in the model column pointed to
696 * by this property must be greater than zero and not larger than wrap-width.
700 g_object_class_install_property (object_class,
701 PROP_ROW_SPAN_COLUMN,
702 g_param_spec_int ("row-span-column",
703 P_("Row span column"),
704 P_("TreeModel column containing the row span values"),
708 GTK_PARAM_READWRITE));
712 * GtkComboBox:column-span-column:
714 * If this is set to a non-negative value, it must be the index of a column
715 * of type %G_TYPE_INT in the model.
717 * The values of that column are used to determine how many columns a value
718 * in the list will span.
722 g_object_class_install_property (object_class,
723 PROP_COLUMN_SPAN_COLUMN,
724 g_param_spec_int ("column-span-column",
725 P_("Column span column"),
726 P_("TreeModel column containing the column span values"),
730 GTK_PARAM_READWRITE));
734 * GtkComboBox:active:
736 * The item which is currently active. If the model is a non-flat treemodel,
737 * and the active item is not an immediate child of the root of the tree,
738 * this property has the value
739 * <literal>gtk_tree_path_get_indices (path)[0]</literal>,
740 * where <literal>path</literal> is the #GtkTreePath of the active item.
744 g_object_class_install_property (object_class,
746 g_param_spec_int ("active",
748 P_("The item which is currently active"),
752 GTK_PARAM_READWRITE));
755 * GtkComboBox:add-tearoffs:
757 * The add-tearoffs property controls whether generated menus
758 * have tearoff menu items.
760 * Note that this only affects menu style combo boxes.
764 g_object_class_install_property (object_class,
766 g_param_spec_boolean ("add-tearoffs",
767 P_("Add tearoffs to menus"),
768 P_("Whether dropdowns should have a tearoff menu item"),
770 GTK_PARAM_READWRITE));
773 * GtkComboBox:has-frame:
775 * The has-frame property controls whether a frame
776 * is drawn around the entry.
780 g_object_class_install_property (object_class,
782 g_param_spec_boolean ("has-frame",
784 P_("Whether the combo box draws a frame around the child"),
786 GTK_PARAM_READWRITE));
788 g_object_class_install_property (object_class,
790 g_param_spec_boolean ("focus-on-click",
791 P_("Focus on click"),
792 P_("Whether the combo box grabs focus when it is clicked with the mouse"),
794 GTK_PARAM_READWRITE));
797 * GtkComboBox:tearoff-title:
799 * A title that may be displayed by the window manager
800 * when the popup is torn-off.
804 g_object_class_install_property (object_class,
806 g_param_spec_string ("tearoff-title",
808 P_("A title that may be displayed by the window manager when the popup is torn-off"),
810 GTK_PARAM_READWRITE));
814 * GtkComboBox:popup-shown:
816 * Whether the combo boxes dropdown is popped up.
817 * Note that this property is mainly useful, because
818 * it allows you to connect to notify::popup-shown.
822 g_object_class_install_property (object_class,
824 g_param_spec_boolean ("popup-shown",
826 P_("Whether the combo's dropdown is shown"),
828 GTK_PARAM_READABLE));
832 * GtkComboBox:button-sensitivity:
834 * Whether the dropdown button is sensitive when
835 * the model is empty.
839 g_object_class_install_property (object_class,
840 PROP_BUTTON_SENSITIVITY,
841 g_param_spec_enum ("button-sensitivity",
842 P_("Button Sensitivity"),
843 P_("Whether the dropdown button is sensitive when the model is empty"),
844 GTK_TYPE_SENSITIVITY_TYPE,
845 GTK_SENSITIVITY_AUTO,
846 GTK_PARAM_READWRITE));
849 * GtkComboBox:has-entry:
851 * Whether the combo box has an entry.
855 g_object_class_install_property (object_class,
857 g_param_spec_boolean ("has-entry",
859 P_("Whether combo box has an entry"),
861 GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
864 * GtkComboBox:entry-text-column:
866 * The column in the combo box's model to associate with strings from the entry
867 * if the combo was created with #GtkComboBox:has-entry = %TRUE.
871 g_object_class_install_property (object_class,
872 PROP_ENTRY_TEXT_COLUMN,
873 g_param_spec_int ("entry-text-column",
874 P_("Entry Text Column"),
875 P_("The column in the combo box's model to associate "
876 "with strings from the entry if the combo was "
877 "created with #GtkComboBox:has-entry = %TRUE"),
879 GTK_PARAM_READWRITE));
882 * GtkComboBox:id-column:
884 * The column in the combo box's model that provides string
885 * IDs for the values in the model, if != -1.
889 g_object_class_install_property (object_class,
891 g_param_spec_int ("id-column",
893 P_("The column in the combo box's model that provides "
894 "string IDs for the values in the model"),
896 GTK_PARAM_READWRITE));
899 * GtkComboBox:active-id:
901 * The value of the ID column of the active row.
905 g_object_class_install_property (object_class,
907 g_param_spec_string ("active-id",
909 P_("The value of the id column "
910 "for the active row"),
911 NULL, GTK_PARAM_READWRITE));
914 * GtkComboBox:popup-fixed-width:
916 * Whether the popup's width should be a fixed width matching the
917 * allocated width of the combo box.
921 g_object_class_install_property (object_class,
922 PROP_POPUP_FIXED_WIDTH,
923 g_param_spec_boolean ("popup-fixed-width",
924 P_("Popup Fixed Width"),
925 P_("Whether the popup's width should be a "
926 "fixed width matching the allocated width "
929 GTK_PARAM_READWRITE));
932 * GtkComboBox:cell-area:
934 * The #GtkCellArea used to layout cell renderers for this combo box.
936 * If no area is specified when creating the combo box with gtk_combo_box_new_with_area()
937 * a horizontally oriented #GtkCellAreaBox will be used.
941 g_object_class_install_property (object_class,
943 g_param_spec_object ("cell-area",
945 P_("The GtkCellArea used to layout cells"),
947 GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
949 gtk_widget_class_install_style_property (widget_class,
950 g_param_spec_boolean ("appears-as-list",
951 P_("Appears as list"),
952 P_("Whether dropdowns should look like lists rather than menus"),
954 GTK_PARAM_READABLE));
957 * GtkComboBox:arrow-size:
959 * Sets the minimum size of the arrow in the combo box. Note
960 * that the arrow size is coupled to the font size, so in case
961 * a larger font is used, the arrow will be larger than set
966 gtk_widget_class_install_style_property (widget_class,
967 g_param_spec_int ("arrow-size",
969 P_("The minimum size of the arrow in the combo box"),
973 GTK_PARAM_READABLE));
976 * GtkComboBox:arrow-scaling:
978 * Sets the amount of space used up by the combobox arrow,
979 * proportional to the font size.
983 gtk_widget_class_install_style_property (widget_class,
984 g_param_spec_float ("arrow-scaling",
986 P_("The amount of space used by the arrow"),
990 GTK_PARAM_READABLE));
993 * GtkComboBox:shadow-type:
995 * Which kind of shadow to draw around the combo box.
999 gtk_widget_class_install_style_property (widget_class,
1000 g_param_spec_enum ("shadow-type",
1002 P_("Which kind of shadow to draw around the combo box"),
1003 GTK_TYPE_SHADOW_TYPE,
1005 GTK_PARAM_READABLE));
1007 g_type_class_add_private (object_class, sizeof (GtkComboBoxPrivate));
1011 gtk_combo_box_buildable_init (GtkBuildableIface *iface)
1013 parent_buildable_iface = g_type_interface_peek_parent (iface);
1014 iface->add_child = _gtk_cell_layout_buildable_add_child;
1015 iface->custom_tag_start = gtk_combo_box_buildable_custom_tag_start;
1016 iface->custom_tag_end = gtk_combo_box_buildable_custom_tag_end;
1017 iface->get_internal_child = gtk_combo_box_buildable_get_internal_child;
1021 gtk_combo_box_cell_layout_init (GtkCellLayoutIface *iface)
1023 iface->get_area = gtk_combo_box_cell_layout_get_area;
1027 gtk_combo_box_cell_editable_init (GtkCellEditableIface *iface)
1029 iface->start_editing = gtk_combo_box_start_editing;
1033 gtk_combo_box_init (GtkComboBox *combo_box)
1035 GtkComboBoxPrivate *priv;
1037 combo_box->priv = G_TYPE_INSTANCE_GET_PRIVATE (combo_box,
1039 GtkComboBoxPrivate);
1040 priv = combo_box->priv;
1042 priv->wrap_width = 0;
1045 priv->active_row = NULL;
1046 priv->col_column = -1;
1047 priv->row_column = -1;
1049 priv->popup_shown = FALSE;
1050 priv->add_tearoffs = FALSE;
1051 priv->has_frame = TRUE;
1052 priv->is_cell_renderer = FALSE;
1053 priv->editing_canceled = FALSE;
1054 priv->auto_scroll = FALSE;
1055 priv->focus_on_click = TRUE;
1056 priv->button_sensitivity = GTK_SENSITIVITY_AUTO;
1057 priv->has_entry = FALSE;
1058 priv->popup_fixed_width = TRUE;
1060 priv->text_column = -1;
1061 priv->text_renderer = NULL;
1062 priv->id_column = -1;
1066 gtk_combo_box_set_property (GObject *object,
1068 const GValue *value,
1071 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
1072 GtkComboBoxPrivate *priv = combo_box->priv;
1078 gtk_combo_box_set_model (combo_box, g_value_get_object (value));
1081 case PROP_WRAP_WIDTH:
1082 gtk_combo_box_set_wrap_width (combo_box, g_value_get_int (value));
1085 case PROP_ROW_SPAN_COLUMN:
1086 gtk_combo_box_set_row_span_column (combo_box, g_value_get_int (value));
1089 case PROP_COLUMN_SPAN_COLUMN:
1090 gtk_combo_box_set_column_span_column (combo_box, g_value_get_int (value));
1094 gtk_combo_box_set_active (combo_box, g_value_get_int (value));
1097 case PROP_ADD_TEAROFFS:
1098 gtk_combo_box_set_add_tearoffs (combo_box, g_value_get_boolean (value));
1101 case PROP_HAS_FRAME:
1102 priv->has_frame = g_value_get_boolean (value);
1104 if (priv->has_entry)
1108 child = gtk_bin_get_child (GTK_BIN (combo_box));
1110 gtk_entry_set_has_frame (GTK_ENTRY (child), priv->has_frame);
1115 case PROP_FOCUS_ON_CLICK:
1116 gtk_combo_box_set_focus_on_click (combo_box,
1117 g_value_get_boolean (value));
1120 case PROP_TEAROFF_TITLE:
1121 gtk_combo_box_set_title (combo_box, g_value_get_string (value));
1124 case PROP_POPUP_SHOWN:
1125 if (g_value_get_boolean (value))
1126 gtk_combo_box_popup (combo_box);
1128 gtk_combo_box_popdown (combo_box);
1131 case PROP_BUTTON_SENSITIVITY:
1132 gtk_combo_box_set_button_sensitivity (combo_box,
1133 g_value_get_enum (value));
1136 case PROP_POPUP_FIXED_WIDTH:
1137 gtk_combo_box_set_popup_fixed_width (combo_box,
1138 g_value_get_boolean (value));
1141 case PROP_EDITING_CANCELED:
1142 priv->editing_canceled = g_value_get_boolean (value);
1145 case PROP_HAS_ENTRY:
1146 priv->has_entry = g_value_get_boolean (value);
1149 case PROP_ENTRY_TEXT_COLUMN:
1150 gtk_combo_box_set_entry_text_column (combo_box, g_value_get_int (value));
1153 case PROP_ID_COLUMN:
1154 gtk_combo_box_set_id_column (combo_box, g_value_get_int (value));
1157 case PROP_ACTIVE_ID:
1158 gtk_combo_box_set_active_id (combo_box, g_value_get_string (value));
1161 case PROP_CELL_AREA:
1162 /* Construct-only, can only be assigned once */
1163 area = g_value_get_object (value);
1166 if (priv->area != NULL)
1168 g_warning ("cell-area has already been set, ignoring construct property");
1169 g_object_ref_sink (area);
1170 g_object_unref (area);
1173 priv->area = g_object_ref_sink (area);
1178 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1184 gtk_combo_box_get_property (GObject *object,
1189 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
1190 GtkComboBoxPrivate *priv = combo_box->priv;
1195 g_value_set_object (value, combo_box->priv->model);
1198 case PROP_WRAP_WIDTH:
1199 g_value_set_int (value, combo_box->priv->wrap_width);
1202 case PROP_ROW_SPAN_COLUMN:
1203 g_value_set_int (value, combo_box->priv->row_column);
1206 case PROP_COLUMN_SPAN_COLUMN:
1207 g_value_set_int (value, combo_box->priv->col_column);
1211 g_value_set_int (value, gtk_combo_box_get_active (combo_box));
1214 case PROP_ADD_TEAROFFS:
1215 g_value_set_boolean (value, gtk_combo_box_get_add_tearoffs (combo_box));
1218 case PROP_HAS_FRAME:
1219 g_value_set_boolean (value, combo_box->priv->has_frame);
1222 case PROP_FOCUS_ON_CLICK:
1223 g_value_set_boolean (value, combo_box->priv->focus_on_click);
1226 case PROP_TEAROFF_TITLE:
1227 g_value_set_string (value, gtk_combo_box_get_title (combo_box));
1230 case PROP_POPUP_SHOWN:
1231 g_value_set_boolean (value, combo_box->priv->popup_shown);
1234 case PROP_BUTTON_SENSITIVITY:
1235 g_value_set_enum (value, combo_box->priv->button_sensitivity);
1238 case PROP_POPUP_FIXED_WIDTH:
1239 g_value_set_boolean (value, combo_box->priv->popup_fixed_width);
1242 case PROP_EDITING_CANCELED:
1243 g_value_set_boolean (value, priv->editing_canceled);
1246 case PROP_HAS_ENTRY:
1247 g_value_set_boolean (value, priv->has_entry);
1250 case PROP_ENTRY_TEXT_COLUMN:
1251 g_value_set_int (value, priv->text_column);
1254 case PROP_ID_COLUMN:
1255 g_value_set_int (value, priv->id_column);
1258 case PROP_ACTIVE_ID:
1259 g_value_set_string (value, gtk_combo_box_get_active_id (combo_box));
1262 case PROP_CELL_AREA:
1263 g_value_set_object (value, priv->area);
1267 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1273 gtk_combo_box_state_flags_changed (GtkWidget *widget,
1274 GtkStateFlags previous)
1276 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1277 GtkComboBoxPrivate *priv = combo_box->priv;
1279 if (gtk_widget_get_realized (widget))
1281 if (priv->tree_view && priv->cell_view)
1283 GtkStyleContext *context;
1284 GtkStateFlags state;
1287 context = gtk_widget_get_style_context (widget);
1288 state = gtk_widget_get_state_flags (widget);
1289 gtk_style_context_get_background_color (context, state, &color);
1291 gtk_cell_view_set_background_rgba (GTK_CELL_VIEW (priv->cell_view), &color);
1295 gtk_widget_queue_draw (widget);
1299 gtk_combo_box_button_state_flags_changed (GtkWidget *widget,
1300 GtkStateFlags previous,
1303 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
1304 GtkComboBoxPrivate *priv = combo_box->priv;
1306 if (gtk_widget_get_realized (widget))
1308 if (!priv->tree_view && priv->cell_view)
1309 gtk_widget_set_state_flags (priv->cell_view,
1310 gtk_widget_get_state_flags (widget),
1314 gtk_widget_queue_draw (widget);
1318 gtk_combo_box_check_appearance (GtkComboBox *combo_box)
1320 GtkComboBoxPrivate *priv = combo_box->priv;
1321 gboolean appears_as_list;
1323 /* if wrap_width > 0, then we are in grid-mode and forced to use
1326 if (priv->wrap_width)
1327 appears_as_list = FALSE;
1329 gtk_widget_style_get (GTK_WIDGET (combo_box),
1330 "appears-as-list", &appears_as_list,
1333 if (appears_as_list)
1335 /* Destroy all the menu mode widgets, if they exist. */
1336 if (GTK_IS_MENU (priv->popup_widget))
1337 gtk_combo_box_menu_destroy (combo_box);
1339 /* Create the list mode widgets, if they don't already exist. */
1340 if (!GTK_IS_TREE_VIEW (priv->tree_view))
1341 gtk_combo_box_list_setup (combo_box);
1345 /* Destroy all the list mode widgets, if they exist. */
1346 if (GTK_IS_TREE_VIEW (priv->tree_view))
1347 gtk_combo_box_list_destroy (combo_box);
1349 /* Create the menu mode widgets, if they don't already exist. */
1350 if (!GTK_IS_MENU (priv->popup_widget))
1351 gtk_combo_box_menu_setup (combo_box, TRUE);
1354 gtk_widget_style_get (GTK_WIDGET (combo_box),
1355 "shadow-type", &priv->shadow_type,
1360 gtk_combo_box_style_updated (GtkWidget *widget)
1362 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1363 GtkComboBoxPrivate *priv = combo_box->priv;
1366 GTK_WIDGET_CLASS (gtk_combo_box_parent_class)->style_updated (widget);
1368 gtk_combo_box_check_appearance (combo_box);
1370 if (priv->tree_view && priv->cell_view)
1372 GtkStyleContext *context;
1373 GtkStateFlags state;
1376 context = gtk_widget_get_style_context (widget);
1377 state = gtk_widget_get_state_flags (widget);
1378 gtk_style_context_get_background_color (context, state, &color);
1380 gtk_cell_view_set_background_rgba (GTK_CELL_VIEW (priv->cell_view), &color);
1383 child = gtk_bin_get_child (GTK_BIN (combo_box));
1384 if (GTK_IS_ENTRY (child))
1385 g_object_set (child, "shadow-type",
1386 GTK_SHADOW_NONE == priv->shadow_type ?
1387 GTK_SHADOW_IN : GTK_SHADOW_NONE, NULL);
1391 gtk_combo_box_button_toggled (GtkWidget *widget,
1394 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
1396 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
1398 if (!combo_box->priv->popup_in_progress)
1399 gtk_combo_box_popup (combo_box);
1402 gtk_combo_box_popdown (combo_box);
1406 gtk_combo_box_add (GtkContainer *container,
1409 GtkComboBox *combo_box = GTK_COMBO_BOX (container);
1410 GtkComboBoxPrivate *priv = combo_box->priv;
1412 if (priv->has_entry && !GTK_IS_ENTRY (widget))
1414 g_warning ("Attempting to add a widget with type %s to a GtkComboBox that needs an entry "
1415 "(need an instance of GtkEntry or of a subclass)",
1416 G_OBJECT_TYPE_NAME (widget));
1420 if (priv->cell_view &&
1421 gtk_widget_get_parent (priv->cell_view))
1423 gtk_widget_unparent (priv->cell_view);
1424 _gtk_bin_set_child (GTK_BIN (container), NULL);
1425 gtk_widget_queue_resize (GTK_WIDGET (container));
1428 gtk_widget_set_parent (widget, GTK_WIDGET (container));
1429 _gtk_bin_set_child (GTK_BIN (container), widget);
1431 if (priv->cell_view &&
1432 widget != priv->cell_view)
1434 /* since the cell_view was unparented, it's gone now */
1435 priv->cell_view = NULL;
1437 if (!priv->tree_view && priv->separator)
1439 gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (priv->separator)),
1441 priv->separator = NULL;
1443 gtk_widget_queue_resize (GTK_WIDGET (container));
1445 else if (priv->cell_view_frame)
1447 gtk_widget_unparent (priv->cell_view_frame);
1448 priv->cell_view_frame = NULL;
1453 if (priv->has_entry)
1455 /* this flag is a hack to tell the entry to fill its allocation.
1457 _gtk_entry_set_is_cell_renderer (GTK_ENTRY (widget), TRUE);
1459 g_signal_connect (widget, "changed",
1460 G_CALLBACK (gtk_combo_box_entry_contents_changed),
1463 gtk_entry_set_has_frame (GTK_ENTRY (widget), priv->has_frame);
1468 gtk_combo_box_remove (GtkContainer *container,
1471 GtkComboBox *combo_box = GTK_COMBO_BOX (container);
1472 GtkComboBoxPrivate *priv = combo_box->priv;
1474 gboolean appears_as_list;
1476 if (priv->has_entry)
1478 GtkWidget *child_widget;
1480 child_widget = gtk_bin_get_child (GTK_BIN (container));
1481 if (widget && widget == child_widget)
1483 g_signal_handlers_disconnect_by_func (widget,
1484 gtk_combo_box_entry_contents_changed,
1486 _gtk_entry_set_is_cell_renderer (GTK_ENTRY (widget), FALSE);
1490 if (widget == priv->cell_view)
1491 priv->cell_view = NULL;
1493 gtk_widget_unparent (widget);
1494 _gtk_bin_set_child (GTK_BIN (container), NULL);
1496 if (gtk_widget_in_destruction (GTK_WIDGET (combo_box)))
1499 gtk_widget_queue_resize (GTK_WIDGET (container));
1501 if (!priv->tree_view)
1502 appears_as_list = FALSE;
1504 appears_as_list = TRUE;
1506 if (appears_as_list)
1507 gtk_combo_box_list_destroy (combo_box);
1508 else if (GTK_IS_MENU (priv->popup_widget))
1510 gtk_combo_box_menu_destroy (combo_box);
1511 gtk_menu_detach (GTK_MENU (priv->popup_widget));
1512 priv->popup_widget = NULL;
1515 if (!priv->cell_view)
1517 priv->cell_view = gtk_cell_view_new ();
1518 gtk_widget_set_parent (priv->cell_view, GTK_WIDGET (container));
1519 _gtk_bin_set_child (GTK_BIN (container), priv->cell_view);
1521 gtk_widget_show (priv->cell_view);
1522 gtk_cell_view_set_model (GTK_CELL_VIEW (priv->cell_view),
1527 if (appears_as_list)
1528 gtk_combo_box_list_setup (combo_box);
1530 gtk_combo_box_menu_setup (combo_box, TRUE);
1532 if (gtk_tree_row_reference_valid (priv->active_row))
1534 path = gtk_tree_row_reference_get_path (priv->active_row);
1535 gtk_combo_box_set_active_internal (combo_box, path);
1536 gtk_tree_path_free (path);
1539 gtk_combo_box_set_active_internal (combo_box, NULL);
1543 gtk_combo_box_menu_show (GtkWidget *menu,
1546 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1547 GtkComboBoxPrivate *priv = combo_box->priv;
1549 gtk_combo_box_child_show (menu, user_data);
1551 priv->popup_in_progress = TRUE;
1552 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button),
1554 priv->popup_in_progress = FALSE;
1558 gtk_combo_box_menu_hide (GtkWidget *menu,
1561 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1563 gtk_combo_box_child_hide (menu,user_data);
1565 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
1570 gtk_combo_box_detacher (GtkWidget *widget,
1573 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1574 GtkComboBoxPrivate *priv = combo_box->priv;
1576 g_return_if_fail (priv->popup_widget == (GtkWidget *) menu);
1578 g_signal_handlers_disconnect_by_func (menu->priv->toplevel,
1579 gtk_combo_box_menu_show,
1581 g_signal_handlers_disconnect_by_func (menu->priv->toplevel,
1582 gtk_combo_box_menu_hide,
1585 priv->popup_widget = NULL;
1589 gtk_combo_box_set_popup_widget (GtkComboBox *combo_box,
1592 GtkComboBoxPrivate *priv = combo_box->priv;
1594 if (GTK_IS_MENU (priv->popup_widget))
1596 gtk_menu_detach (GTK_MENU (priv->popup_widget));
1597 priv->popup_widget = NULL;
1599 else if (priv->popup_widget)
1601 gtk_container_remove (GTK_CONTAINER (priv->scrolled_window),
1602 priv->popup_widget);
1603 g_object_unref (priv->popup_widget);
1604 priv->popup_widget = NULL;
1607 if (GTK_IS_MENU (popup))
1609 if (priv->popup_window)
1611 gtk_widget_destroy (priv->popup_window);
1612 priv->popup_window = NULL;
1615 priv->popup_widget = popup;
1618 * Note that we connect to show/hide on the toplevel, not the
1619 * menu itself, since the menu is not shown/hidden when it is
1620 * popped up while torn-off.
1622 g_signal_connect (GTK_MENU (popup)->priv->toplevel, "show",
1623 G_CALLBACK (gtk_combo_box_menu_show), combo_box);
1624 g_signal_connect (GTK_MENU (popup)->priv->toplevel, "hide",
1625 G_CALLBACK (gtk_combo_box_menu_hide), combo_box);
1627 gtk_menu_attach_to_widget (GTK_MENU (popup),
1628 GTK_WIDGET (combo_box),
1629 gtk_combo_box_detacher);
1633 if (!priv->popup_window)
1635 GtkWidget *toplevel;
1637 priv->popup_window = gtk_window_new (GTK_WINDOW_POPUP);
1638 gtk_widget_set_name (priv->popup_window, "gtk-combobox-popup-window");
1640 gtk_window_set_type_hint (GTK_WINDOW (priv->popup_window),
1641 GDK_WINDOW_TYPE_HINT_COMBO);
1643 g_signal_connect (GTK_WINDOW (priv->popup_window),"show",
1644 G_CALLBACK (gtk_combo_box_child_show),
1646 g_signal_connect (GTK_WINDOW (priv->popup_window),"hide",
1647 G_CALLBACK (gtk_combo_box_child_hide),
1650 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (combo_box));
1651 if (GTK_IS_WINDOW (toplevel))
1653 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
1654 GTK_WINDOW (priv->popup_window));
1655 gtk_window_set_transient_for (GTK_WINDOW (priv->popup_window),
1656 GTK_WINDOW (toplevel));
1659 gtk_window_set_resizable (GTK_WINDOW (priv->popup_window), FALSE);
1660 gtk_window_set_screen (GTK_WINDOW (priv->popup_window),
1661 gtk_widget_get_screen (GTK_WIDGET (combo_box)));
1663 priv->scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1665 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
1668 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->scrolled_window),
1671 gtk_widget_show (priv->scrolled_window);
1673 gtk_container_add (GTK_CONTAINER (priv->popup_window),
1674 priv->scrolled_window);
1677 gtk_container_add (GTK_CONTAINER (priv->scrolled_window),
1680 gtk_widget_show (popup);
1681 g_object_ref (popup);
1682 priv->popup_widget = popup;
1687 get_widget_padding (GtkWidget *widget,
1690 GtkStyleContext *context;
1691 GtkStateFlags state;
1693 context = gtk_widget_get_style_context (widget);
1694 state = gtk_style_context_get_state (context);
1696 gtk_style_context_get_padding (context, state, padding);
1700 gtk_combo_box_menu_position_below (GtkMenu *menu,
1706 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1707 GtkAllocation child_allocation;
1713 GdkRectangle monitor;
1716 /* FIXME: is using the size request here broken? */
1717 child = gtk_bin_get_child (GTK_BIN (combo_box));
1721 gtk_widget_get_allocation (child, &child_allocation);
1723 if (!gtk_widget_get_has_window (child))
1725 sx += child_allocation.x;
1726 sy += child_allocation.y;
1729 gdk_window_get_root_coords (gtk_widget_get_window (child),
1731 get_widget_padding (GTK_WIDGET (combo_box), &padding);
1734 if (combo_box->priv->popup_fixed_width)
1735 gtk_widget_get_preferred_size (GTK_WIDGET (menu), &req, NULL);
1737 gtk_widget_get_preferred_size (GTK_WIDGET (menu), NULL, &req);
1739 if (gtk_widget_get_direction (GTK_WIDGET (combo_box)) == GTK_TEXT_DIR_LTR)
1742 *x = sx + child_allocation.width - req.width;
1745 screen = gtk_widget_get_screen (GTK_WIDGET (combo_box));
1746 monitor_num = gdk_screen_get_monitor_at_window (screen,
1747 gtk_widget_get_window (GTK_WIDGET (combo_box)));
1748 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1752 else if (*x + req.width > monitor.x + monitor.width)
1753 *x = monitor.x + monitor.width - req.width;
1755 if (monitor.y + monitor.height - *y - child_allocation.height >= req.height)
1756 *y += child_allocation.height;
1757 else if (*y - monitor.y >= req.height)
1759 else if (monitor.y + monitor.height - *y - child_allocation.height > *y - monitor.y)
1760 *y += child_allocation.height;
1768 gtk_combo_box_menu_position_over (GtkMenu *menu,
1774 GtkComboBox *combo_box;
1778 GtkAllocation allocation;
1779 GtkAllocation child_allocation;
1786 combo_box = GTK_COMBO_BOX (user_data);
1787 widget = GTK_WIDGET (combo_box);
1789 active = gtk_menu_get_active (GTK_MENU (combo_box->priv->popup_widget));
1791 gtk_widget_get_allocation (widget, &allocation);
1793 menu_xpos = allocation.x;
1794 menu_ypos = allocation.y + allocation.height / 2 - 2;
1796 if (combo_box->priv->popup_fixed_width)
1797 gtk_widget_get_preferred_width (GTK_WIDGET (menu), &menu_width, NULL);
1799 gtk_widget_get_preferred_width (GTK_WIDGET (menu), NULL, &menu_width);
1803 gtk_widget_get_allocation (active, &child_allocation);
1804 menu_ypos -= child_allocation.height / 2;
1807 children = GTK_MENU_SHELL (combo_box->priv->popup_widget)->priv->children;
1810 child = children->data;
1812 if (active == child)
1815 if (gtk_widget_get_visible (child))
1817 gtk_widget_get_allocation (child, &child_allocation);
1819 menu_ypos -= child_allocation.height;
1822 children = children->next;
1825 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
1826 menu_xpos = menu_xpos + allocation.width - menu_width;
1828 gdk_window_get_root_coords (gtk_widget_get_window (widget),
1829 menu_xpos, menu_ypos,
1830 &menu_xpos, &menu_ypos);
1832 /* Clamp the position on screen */
1833 screen_width = gdk_screen_get_width (gtk_widget_get_screen (widget));
1837 else if ((menu_xpos + menu_width) > screen_width)
1838 menu_xpos -= ((menu_xpos + menu_width) - screen_width);
1847 gtk_combo_box_menu_position (GtkMenu *menu,
1853 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1854 GtkComboBoxPrivate *priv = combo_box->priv;
1855 GtkWidget *menu_item;
1857 if (priv->wrap_width > 0 || priv->cell_view == NULL)
1858 gtk_combo_box_menu_position_below (menu, x, y, push_in, user_data);
1861 /* FIXME handle nested menus better */
1862 menu_item = gtk_menu_get_active (GTK_MENU (priv->popup_widget));
1864 gtk_menu_shell_select_item (GTK_MENU_SHELL (priv->popup_widget),
1867 gtk_combo_box_menu_position_over (menu, x, y, push_in, user_data);
1870 if (!gtk_widget_get_visible (GTK_MENU (priv->popup_widget)->priv->toplevel))
1871 gtk_window_set_type_hint (GTK_WINDOW (GTK_MENU (priv->popup_widget)->priv->toplevel),
1872 GDK_WINDOW_TYPE_HINT_COMBO);
1876 gtk_combo_box_list_position (GtkComboBox *combo_box,
1882 GtkComboBoxPrivate *priv = combo_box->priv;
1883 GtkAllocation allocation;
1886 GdkRectangle monitor;
1887 GtkRequisition popup_req;
1888 GtkPolicyType hpolicy, vpolicy;
1891 /* under windows, the drop down list is as wide as the combo box itself.
1893 GtkWidget *widget = GTK_WIDGET (combo_box);
1897 gtk_widget_get_allocation (widget, &allocation);
1899 if (!gtk_widget_get_has_window (widget))
1905 window = gtk_widget_get_window (widget);
1907 gdk_window_get_root_coords (gtk_widget_get_window (widget),
1910 *width = allocation.width;
1912 hpolicy = vpolicy = GTK_POLICY_NEVER;
1913 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
1916 if (combo_box->priv->popup_fixed_width)
1918 gtk_widget_get_preferred_size (priv->scrolled_window, &popup_req, NULL);
1920 if (popup_req.width > *width)
1922 hpolicy = GTK_POLICY_ALWAYS;
1923 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
1929 /* XXX This code depends on treeviews properly reporting their natural width
1930 * list-mode menus won't fill up to their natural width until then */
1931 gtk_widget_get_preferred_size (priv->scrolled_window, NULL, &popup_req);
1933 if (popup_req.width > *width)
1935 hpolicy = GTK_POLICY_NEVER;
1936 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
1939 *width = popup_req.width;
1943 *height = popup_req.height;
1945 screen = gtk_widget_get_screen (GTK_WIDGET (combo_box));
1946 monitor_num = gdk_screen_get_monitor_at_window (screen, window);
1947 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1949 if (gtk_widget_get_direction (GTK_WIDGET (combo_box)) == GTK_TEXT_DIR_RTL)
1950 *x = *x + allocation.width - *width;
1954 else if (*x + *width > monitor.x + monitor.width)
1955 *x = monitor.x + monitor.width - *width;
1957 if (*y + allocation.height + *height <= monitor.y + monitor.height)
1958 *y += allocation.height;
1959 else if (*y - *height >= monitor.y)
1961 else if (monitor.y + monitor.height - (*y + allocation.height) > *y - monitor.y)
1963 *y += allocation.height;
1964 *height = monitor.y + monitor.height - *y;
1968 *height = *y - monitor.y;
1972 if (popup_req.height > *height)
1974 vpolicy = GTK_POLICY_ALWAYS;
1976 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
1982 cell_layout_is_sensitive (GtkCellLayout *layout)
1984 GList *cells, *list;
1987 cells = gtk_cell_layout_get_cells (layout);
1990 for (list = cells; list; list = list->next)
1992 g_object_get (list->data, "sensitive", &sensitive, NULL);
1997 g_list_free (cells);
2003 cell_is_sensitive (GtkCellRenderer *cell,
2006 gboolean *sensitive = data;
2008 g_object_get (cell, "sensitive", sensitive, NULL);
2014 tree_column_row_is_sensitive (GtkComboBox *combo_box,
2017 GtkComboBoxPrivate *priv = combo_box->priv;
2019 if (priv->row_separator_func)
2021 if (priv->row_separator_func (priv->model, iter,
2022 priv->row_separator_data))
2030 gtk_cell_area_apply_attributes (priv->area, priv->model, iter, FALSE, FALSE);
2034 gtk_cell_area_foreach (priv->area, cell_is_sensitive, &sensitive);
2043 update_menu_sensitivity (GtkComboBox *combo_box,
2046 GtkComboBoxPrivate *priv = combo_box->priv;
2047 GList *children, *child;
2048 GtkWidget *item, *submenu, *separator;
2049 GtkWidget *cell_view;
2055 children = gtk_container_get_children (GTK_CONTAINER (menu));
2057 for (child = children; child; child = child->next)
2059 item = GTK_WIDGET (child->data);
2060 cell_view = gtk_bin_get_child (GTK_BIN (item));
2062 if (!GTK_IS_CELL_VIEW (cell_view))
2065 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (item));
2066 if (submenu != NULL)
2068 gtk_widget_set_sensitive (item, TRUE);
2069 update_menu_sensitivity (combo_box, submenu);
2073 sensitive = cell_layout_is_sensitive (GTK_CELL_LAYOUT (cell_view));
2075 if (menu != priv->popup_widget && child == children)
2077 separator = GTK_WIDGET (child->next->data);
2078 g_object_set (item, "visible", sensitive, NULL);
2079 g_object_set (separator, "visible", sensitive, NULL);
2082 gtk_widget_set_sensitive (item, sensitive);
2086 g_list_free (children);
2090 gtk_combo_box_menu_popup (GtkComboBox *combo_box,
2092 guint32 activate_time)
2094 GtkComboBoxPrivate *priv = combo_box->priv;
2097 gint width, min_width, nat_width;
2099 update_menu_sensitivity (combo_box, priv->popup_widget);
2102 if (gtk_tree_row_reference_valid (priv->active_row))
2104 path = gtk_tree_row_reference_get_path (priv->active_row);
2105 active_item = gtk_tree_path_get_indices (path)[0];
2106 gtk_tree_path_free (path);
2108 if (priv->add_tearoffs)
2112 /* FIXME handle nested menus better */
2113 gtk_menu_set_active (GTK_MENU (priv->popup_widget), active_item);
2115 if (priv->wrap_width == 0)
2117 GtkAllocation allocation;
2119 gtk_widget_get_allocation (GTK_WIDGET (combo_box), &allocation);
2120 width = allocation.width;
2121 gtk_widget_set_size_request (priv->popup_widget, -1, -1);
2122 gtk_widget_get_preferred_width (priv->popup_widget, &min_width, &nat_width);
2124 if (combo_box->priv->popup_fixed_width)
2125 width = MAX (width, min_width);
2127 width = MAX (width, nat_width);
2129 gtk_widget_set_size_request (priv->popup_widget, width, -1);
2132 gtk_menu_popup (GTK_MENU (priv->popup_widget),
2134 gtk_combo_box_menu_position, combo_box,
2135 button, activate_time);
2139 popup_grab_on_window (GdkWindow *window,
2140 GdkDevice *keyboard,
2142 guint32 activate_time)
2145 gdk_device_grab (keyboard, window,
2146 GDK_OWNERSHIP_WINDOW, TRUE,
2147 GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
2148 NULL, activate_time) != GDK_GRAB_SUCCESS)
2152 gdk_device_grab (pointer, window,
2153 GDK_OWNERSHIP_WINDOW, TRUE,
2154 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
2155 GDK_POINTER_MOTION_MASK,
2156 NULL, activate_time) != GDK_GRAB_SUCCESS)
2159 gdk_device_ungrab (keyboard, activate_time);
2168 * gtk_combo_box_popup:
2169 * @combo_box: a #GtkComboBox
2171 * Pops up the menu or dropdown list of @combo_box.
2173 * This function is mostly intended for use by accessibility technologies;
2174 * applications should have little use for it.
2179 gtk_combo_box_popup (GtkComboBox *combo_box)
2181 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2183 g_signal_emit (combo_box, combo_box_signals[POPUP], 0);
2187 * gtk_combo_box_popup_for_device:
2188 * @combo_box: a #GtkComboBox
2189 * @device: a #GdkDevice
2191 * Pops up the menu or dropdown list of @combo_box, the popup window
2192 * will be grabbed so only @device and its associated pointer/keyboard
2193 * are the only #GdkDevice<!-- -->s able to send events to it.
2198 gtk_combo_box_popup_for_device (GtkComboBox *combo_box,
2201 GtkComboBoxPrivate *priv = combo_box->priv;
2202 gint x, y, width, height;
2203 GtkTreePath *path = NULL, *ppath;
2204 GtkWidget *toplevel;
2205 GdkDevice *keyboard, *pointer;
2208 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2209 g_return_if_fail (GDK_IS_DEVICE (device));
2211 if (!gtk_widget_get_realized (GTK_WIDGET (combo_box)))
2214 if (gtk_widget_get_mapped (priv->popup_widget))
2217 if (priv->grab_pointer && priv->grab_keyboard)
2220 time = gtk_get_current_event_time ();
2222 if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
2225 pointer = gdk_device_get_associated_device (device);
2230 keyboard = gdk_device_get_associated_device (device);
2233 if (GTK_IS_MENU (priv->popup_widget))
2235 gtk_combo_box_menu_popup (combo_box,
2236 priv->activate_button,
2237 priv->activate_time);
2241 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (combo_box));
2242 if (GTK_IS_WINDOW (toplevel))
2243 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
2244 GTK_WINDOW (priv->popup_window));
2246 gtk_widget_show_all (priv->scrolled_window);
2247 gtk_combo_box_list_position (combo_box, &x, &y, &width, &height);
2249 gtk_widget_set_size_request (priv->popup_window, width, height);
2250 gtk_window_move (GTK_WINDOW (priv->popup_window), x, y);
2252 if (gtk_tree_row_reference_valid (priv->active_row))
2254 path = gtk_tree_row_reference_get_path (priv->active_row);
2255 ppath = gtk_tree_path_copy (path);
2256 if (gtk_tree_path_up (ppath))
2257 gtk_tree_view_expand_to_path (GTK_TREE_VIEW (priv->tree_view),
2259 gtk_tree_path_free (ppath);
2261 gtk_tree_view_set_hover_expand (GTK_TREE_VIEW (priv->tree_view),
2265 gtk_widget_show (priv->popup_window);
2269 gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->tree_view),
2271 gtk_tree_path_free (path);
2274 gtk_widget_grab_focus (priv->popup_window);
2275 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button),
2278 if (!gtk_widget_has_focus (priv->tree_view))
2279 gtk_widget_grab_focus (priv->tree_view);
2281 if (!popup_grab_on_window (gtk_widget_get_window (priv->popup_window),
2282 keyboard, pointer, time))
2284 gtk_widget_hide (priv->popup_window);
2288 gtk_device_grab_add (priv->popup_window, pointer, TRUE);
2289 priv->grab_pointer = pointer;
2290 priv->grab_keyboard = keyboard;
2294 gtk_combo_box_real_popup (GtkComboBox *combo_box)
2298 device = gtk_get_current_event_device ();
2302 GdkDeviceManager *device_manager;
2303 GdkDisplay *display;
2306 display = gtk_widget_get_display (GTK_WIDGET (combo_box));
2307 device_manager = gdk_display_get_device_manager (display);
2309 /* No device was set, pick the first master device */
2310 devices = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
2311 device = devices->data;
2312 g_list_free (devices);
2315 gtk_combo_box_popup_for_device (combo_box, device);
2319 gtk_combo_box_real_popdown (GtkComboBox *combo_box)
2321 if (combo_box->priv->popup_shown)
2323 gtk_combo_box_popdown (combo_box);
2331 * gtk_combo_box_popdown:
2332 * @combo_box: a #GtkComboBox
2334 * Hides the menu or dropdown list of @combo_box.
2336 * This function is mostly intended for use by accessibility technologies;
2337 * applications should have little use for it.
2342 gtk_combo_box_popdown (GtkComboBox *combo_box)
2344 GtkComboBoxPrivate *priv = combo_box->priv;
2346 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2348 if (GTK_IS_MENU (priv->popup_widget))
2350 gtk_menu_popdown (GTK_MENU (priv->popup_widget));
2354 if (!gtk_widget_get_realized (GTK_WIDGET (combo_box)))
2357 gtk_device_grab_remove (priv->popup_window, priv->grab_pointer);
2358 gtk_widget_hide (priv->popup_window);
2359 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button),
2362 priv->grab_pointer = NULL;
2363 priv->grab_keyboard = NULL;
2366 #define GTK_COMBO_BOX_SIZE_ALLOCATE_BUTTON \
2367 gtk_widget_get_preferred_size (combo_box->priv->button, \
2371 child.x = allocation->x + padding.right; \
2373 child.x = allocation->x + allocation->width - req.width - padding.left; \
2375 child.y = allocation->y + padding.top; \
2376 child.width = req.width; \
2377 child.height = allocation->height - (padding.top + padding.bottom); \
2378 child.width = MAX (1, child.width); \
2379 child.height = MAX (1, child.height); \
2381 gtk_widget_size_allocate (combo_box->priv->button, &child);
2385 gtk_combo_box_size_allocate (GtkWidget *widget,
2386 GtkAllocation *allocation)
2388 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
2389 GtkComboBoxPrivate *priv = combo_box->priv;
2390 GtkWidget *child_widget;
2391 gint focus_width, focus_pad;
2392 GtkAllocation child;
2394 gboolean is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
2397 gtk_widget_set_allocation (widget, allocation);
2398 child_widget = gtk_bin_get_child (GTK_BIN (widget));
2399 get_widget_padding (widget, &padding);
2401 gtk_widget_style_get (widget,
2402 "focus-line-width", &focus_width,
2403 "focus-padding", &focus_pad,
2406 if (!priv->tree_view)
2408 if (priv->cell_view)
2410 GtkBorder button_padding;
2414 gtk_widget_size_allocate (priv->button, allocation);
2417 allocation->x += padding.left;
2418 allocation->y += padding.top;
2419 allocation->width -= padding.left + padding.right;
2420 allocation->height -= padding.top + padding.bottom;
2422 /* set some things ready */
2423 border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->button));
2424 get_widget_padding (priv->button, &button_padding);
2426 child.x = allocation->x;
2427 child.y = allocation->y;
2428 width = allocation->width;
2429 child.height = allocation->height;
2431 if (!priv->is_cell_renderer)
2433 child.x += border_width + button_padding.left + focus_width + focus_pad;
2434 child.y += border_width + button_padding.top + focus_width + focus_pad;
2435 width -= (2 * (border_width + focus_width + focus_pad)) +
2436 button_padding.left + button_padding.right;
2437 child.height -= (2 * (border_width + focus_width + focus_pad)) +
2438 button_padding.top + button_padding.bottom;
2441 /* handle the children */
2442 gtk_widget_get_preferred_size (priv->arrow, &req, NULL);
2443 child.width = req.width;
2445 child.x += width - req.width - button_padding.right;
2446 child.width = MAX (1, child.width);
2447 child.height = MAX (1, child.height);
2448 gtk_widget_size_allocate (priv->arrow, &child);
2450 child.x += req.width;
2451 gtk_widget_get_preferred_size (priv->separator, &req, NULL);
2452 child.width = req.width;
2454 child.x -= req.width;
2455 child.width = MAX (1, child.width);
2456 child.height = MAX (1, child.height);
2457 gtk_widget_size_allocate (priv->separator, &child);
2461 child.x += req.width;
2462 child.width = allocation->x + allocation->width
2463 - (border_width + focus_width + focus_pad)
2468 child.width = child.x;
2469 child.x = allocation->x
2470 + border_width + button_padding.left + focus_width + focus_pad;
2471 child.width -= child.x;
2474 if (gtk_widget_get_visible (priv->popup_widget))
2476 gint width, menu_width;
2478 if (priv->wrap_width == 0)
2480 GtkAllocation combo_box_allocation;
2482 gtk_widget_get_allocation (GTK_WIDGET (combo_box), &combo_box_allocation);
2483 width = combo_box_allocation.width;
2484 gtk_widget_set_size_request (priv->popup_widget, -1, -1);
2486 if (combo_box->priv->popup_fixed_width)
2487 gtk_widget_get_preferred_width (priv->popup_widget, &menu_width, NULL);
2489 gtk_widget_get_preferred_width (priv->popup_widget, NULL, &menu_width);
2491 gtk_widget_set_size_request (priv->popup_widget,
2492 MAX (width, menu_width), -1);
2495 /* reposition the menu after giving it a new width */
2496 gtk_menu_reposition (GTK_MENU (priv->popup_widget));
2499 child.width = MAX (1, child.width);
2500 child.height = MAX (1, child.height);
2501 gtk_widget_size_allocate (child_widget, &child);
2505 GTK_COMBO_BOX_SIZE_ALLOCATE_BUTTON
2508 child.x = allocation->x + req.width + padding.right;
2510 child.x = allocation->x + padding.left;
2511 child.y = allocation->y + padding.top;
2512 child.width = allocation->width - req.width - (padding.left + padding.right);
2513 child.width = MAX (1, child.width);
2514 child.height = MAX (1, child.height);
2515 gtk_widget_size_allocate (child_widget, &child);
2521 guint border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
2524 GTK_COMBO_BOX_SIZE_ALLOCATE_BUTTON
2528 child.x = allocation->x + req.width;
2530 child.x = allocation->x;
2532 child.y = allocation->y;
2533 child.width = allocation->width - req.width;
2534 child.height = allocation->height;
2536 if (priv->cell_view_frame)
2538 child.x += padding.left + border_width;
2539 child.y += padding.top + border_width;
2540 child.width = MAX (1, child.width - (2 * border_width) - (padding.left + padding.right));
2541 child.height = MAX (1, child.height - (2 * border_width) - (padding.top + padding.bottom));
2542 gtk_widget_size_allocate (priv->cell_view_frame, &child);
2545 if (priv->has_frame)
2547 GtkBorder frame_padding;
2549 border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->cell_view_frame));
2550 get_widget_padding (priv->cell_view_frame, &frame_padding);
2552 child.x += border_width + frame_padding.left;
2553 child.y += border_width + frame_padding.right;
2554 child.width -= (2 * border_width) + frame_padding.left + frame_padding.right;
2555 child.height -= (2 * border_width) + frame_padding.top + frame_padding.bottom;
2560 child.x += padding.left + border_width;
2561 child.y += padding.top + border_width;
2562 child.width -= (2 * border_width) - (padding.left + padding.right);
2563 child.height -= (2 * border_width) - (padding.top + padding.bottom);
2566 if (gtk_widget_get_visible (priv->popup_window))
2568 gint x, y, width, height;
2569 gtk_combo_box_list_position (combo_box, &x, &y, &width, &height);
2570 gtk_window_move (GTK_WINDOW (priv->popup_window), x, y);
2571 gtk_widget_set_size_request (priv->popup_window, width, height);
2574 child.width = MAX (1, child.width);
2575 child.height = MAX (1, child.height);
2576 gtk_widget_size_allocate (child_widget, &child);
2580 #undef GTK_COMBO_BOX_ALLOCATE_BUTTON
2583 gtk_combo_box_unset_model (GtkComboBox *combo_box)
2585 GtkComboBoxPrivate *priv = combo_box->priv;
2589 g_signal_handler_disconnect (priv->model,
2591 g_signal_handler_disconnect (priv->model,
2593 g_signal_handler_disconnect (priv->model,
2594 priv->reordered_id);
2595 g_signal_handler_disconnect (priv->model,
2601 g_object_unref (priv->model);
2605 if (priv->active_row)
2607 gtk_tree_row_reference_free (priv->active_row);
2608 priv->active_row = NULL;
2611 if (priv->cell_view)
2612 gtk_cell_view_set_model (GTK_CELL_VIEW (priv->cell_view), NULL);
2616 gtk_combo_box_forall (GtkContainer *container,
2617 gboolean include_internals,
2618 GtkCallback callback,
2619 gpointer callback_data)
2621 GtkComboBox *combo_box = GTK_COMBO_BOX (container);
2622 GtkComboBoxPrivate *priv = combo_box->priv;
2625 if (include_internals)
2628 (* callback) (priv->button, callback_data);
2629 if (priv->cell_view_frame)
2630 (* callback) (priv->cell_view_frame, callback_data);
2633 child = gtk_bin_get_child (GTK_BIN (container));
2635 (* callback) (child, callback_data);
2639 gtk_combo_box_child_show (GtkWidget *widget,
2640 GtkComboBox *combo_box)
2642 GtkComboBoxPrivate *priv = combo_box->priv;
2644 priv->popup_shown = TRUE;
2645 g_object_notify (G_OBJECT (combo_box), "popup-shown");
2649 gtk_combo_box_child_hide (GtkWidget *widget,
2650 GtkComboBox *combo_box)
2652 GtkComboBoxPrivate *priv = combo_box->priv;
2654 priv->popup_shown = FALSE;
2655 g_object_notify (G_OBJECT (combo_box), "popup-shown");
2659 gtk_combo_box_draw (GtkWidget *widget,
2662 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
2663 GtkComboBoxPrivate *priv = combo_box->priv;
2665 if (priv->shadow_type != GTK_SHADOW_NONE)
2667 GtkStyleContext *context;
2668 GtkStateFlags state;
2670 context = gtk_widget_get_style_context (widget);
2671 state = gtk_widget_get_state_flags (widget);
2672 gtk_style_context_set_state (context, state);
2674 gtk_render_background (context, cr, 0, 0,
2675 gtk_widget_get_allocated_width (widget),
2676 gtk_widget_get_allocated_height (widget));
2677 gtk_render_frame (context, cr, 0, 0,
2678 gtk_widget_get_allocated_width (widget),
2679 gtk_widget_get_allocated_height (widget));
2682 gtk_container_propagate_draw (GTK_CONTAINER (widget),
2685 if (priv->tree_view && priv->cell_view_frame)
2687 gtk_container_propagate_draw (GTK_CONTAINER (widget),
2688 priv->cell_view_frame, cr);
2691 gtk_container_propagate_draw (GTK_CONTAINER (widget),
2692 gtk_bin_get_child (GTK_BIN (widget)),
2708 path_visible (GtkTreeView *view,
2714 /* Note that we rely on the fact that collapsed rows don't have nodes
2716 return _gtk_tree_view_find_node (view, path, &tree, &node);
2720 tree_next_func (GtkTreeModel *model,
2725 SearchData *search_data = (SearchData *)data;
2727 if (search_data->found)
2729 if (!tree_column_row_is_sensitive (search_data->combo, iter))
2732 if (search_data->visible &&
2733 !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
2736 search_data->set = TRUE;
2737 search_data->iter = *iter;
2742 if (gtk_tree_path_compare (path, search_data->path) == 0)
2743 search_data->found = TRUE;
2749 tree_next (GtkComboBox *combo,
2750 GtkTreeModel *model,
2755 SearchData search_data;
2757 search_data.combo = combo;
2758 search_data.path = gtk_tree_model_get_path (model, iter);
2759 search_data.visible = visible;
2760 search_data.found = FALSE;
2761 search_data.set = FALSE;
2763 gtk_tree_model_foreach (model, tree_next_func, &search_data);
2765 *next = search_data.iter;
2767 gtk_tree_path_free (search_data.path);
2769 return search_data.set;
2773 tree_prev_func (GtkTreeModel *model,
2778 SearchData *search_data = (SearchData *)data;
2780 if (gtk_tree_path_compare (path, search_data->path) == 0)
2782 search_data->found = TRUE;
2786 if (!tree_column_row_is_sensitive (search_data->combo, iter))
2789 if (search_data->visible &&
2790 !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
2793 search_data->set = TRUE;
2794 search_data->iter = *iter;
2800 tree_prev (GtkComboBox *combo,
2801 GtkTreeModel *model,
2806 SearchData search_data;
2808 search_data.combo = combo;
2809 search_data.path = gtk_tree_model_get_path (model, iter);
2810 search_data.visible = visible;
2811 search_data.found = FALSE;
2812 search_data.set = FALSE;
2814 gtk_tree_model_foreach (model, tree_prev_func, &search_data);
2816 *prev = search_data.iter;
2818 gtk_tree_path_free (search_data.path);
2820 return search_data.set;
2824 tree_last_func (GtkTreeModel *model,
2829 SearchData *search_data = (SearchData *)data;
2831 if (!tree_column_row_is_sensitive (search_data->combo, iter))
2834 /* Note that we rely on the fact that collapsed rows don't have nodes
2836 if (search_data->visible &&
2837 !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
2840 search_data->set = TRUE;
2841 search_data->iter = *iter;
2847 tree_last (GtkComboBox *combo,
2848 GtkTreeModel *model,
2852 SearchData search_data;
2854 search_data.combo = combo;
2855 search_data.visible = visible;
2856 search_data.set = FALSE;
2858 gtk_tree_model_foreach (model, tree_last_func, &search_data);
2860 *last = search_data.iter;
2862 return search_data.set;
2867 tree_first_func (GtkTreeModel *model,
2872 SearchData *search_data = (SearchData *)data;
2874 if (!tree_column_row_is_sensitive (search_data->combo, iter))
2877 if (search_data->visible &&
2878 !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
2881 search_data->set = TRUE;
2882 search_data->iter = *iter;
2888 tree_first (GtkComboBox *combo,
2889 GtkTreeModel *model,
2893 SearchData search_data;
2895 search_data.combo = combo;
2896 search_data.visible = visible;
2897 search_data.set = FALSE;
2899 gtk_tree_model_foreach (model, tree_first_func, &search_data);
2901 *first = search_data.iter;
2903 return search_data.set;
2907 gtk_combo_box_scroll_event (GtkWidget *widget,
2908 GdkEventScroll *event)
2910 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
2913 GtkTreeIter new_iter;
2915 if (!gtk_combo_box_get_active_iter (combo_box, &iter))
2918 if (event->direction == GDK_SCROLL_UP)
2919 found = tree_prev (combo_box, combo_box->priv->model,
2920 &iter, &new_iter, FALSE);
2922 found = tree_next (combo_box, combo_box->priv->model,
2923 &iter, &new_iter, FALSE);
2926 gtk_combo_box_set_active_iter (combo_box, &new_iter);
2935 gtk_combo_box_row_separator_func (GtkTreeModel *model,
2939 GtkComboBoxPrivate *priv = combo->priv;
2941 if (priv->row_separator_func)
2942 return priv->row_separator_func (model, iter, priv->row_separator_data);
2948 gtk_combo_box_header_func (GtkTreeModel *model,
2952 /* Every submenu has a selectable header, however we
2953 * can expose a method to make that configurable by
2954 * the user (like row_separator_func is done) */
2959 gtk_combo_box_menu_setup (GtkComboBox *combo_box,
2960 gboolean add_children)
2962 GtkComboBoxPrivate *priv = combo_box->priv;
2966 child = gtk_bin_get_child (GTK_BIN (combo_box));
2968 if (priv->cell_view)
2970 priv->button = gtk_toggle_button_new ();
2971 gtk_button_set_focus_on_click (GTK_BUTTON (priv->button),
2972 priv->focus_on_click);
2974 g_signal_connect (priv->button, "toggled",
2975 G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
2976 gtk_widget_set_parent (priv->button,
2977 gtk_widget_get_parent (child));
2979 priv->box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
2980 gtk_container_add (GTK_CONTAINER (priv->button), priv->box);
2982 priv->separator = gtk_separator_new (GTK_ORIENTATION_VERTICAL);
2983 gtk_container_add (GTK_CONTAINER (priv->box), priv->separator);
2985 priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
2986 gtk_container_add (GTK_CONTAINER (priv->box), priv->arrow);
2988 gtk_widget_show_all (priv->button);
2992 priv->button = gtk_toggle_button_new ();
2993 gtk_button_set_focus_on_click (GTK_BUTTON (priv->button),
2994 priv->focus_on_click);
2996 g_signal_connect (priv->button, "toggled",
2997 G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
2998 gtk_widget_set_parent (priv->button,
2999 gtk_widget_get_parent (child));
3001 priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3002 gtk_container_add (GTK_CONTAINER (priv->button), priv->arrow);
3003 gtk_widget_show_all (priv->button);
3006 g_signal_connect (priv->button, "button-press-event",
3007 G_CALLBACK (gtk_combo_box_menu_button_press),
3009 g_signal_connect (priv->button, "state-flags-changed",
3010 G_CALLBACK (gtk_combo_box_button_state_flags_changed),
3013 /* create our funky menu */
3014 menu = _gtk_tree_menu_new_with_area (priv->area);
3015 gtk_widget_set_name (menu, "gtk-combobox-popup-menu");
3017 _gtk_tree_menu_set_model (GTK_TREE_MENU (menu), priv->model);
3019 _gtk_tree_menu_set_wrap_width (GTK_TREE_MENU (menu), priv->wrap_width);
3020 _gtk_tree_menu_set_row_span_column (GTK_TREE_MENU (menu), priv->row_column);
3021 _gtk_tree_menu_set_column_span_column (GTK_TREE_MENU (menu), priv->col_column);
3022 _gtk_tree_menu_set_tearoff (GTK_TREE_MENU (menu),
3023 combo_box->priv->add_tearoffs);
3025 g_signal_connect (menu, "menu-activate",
3026 G_CALLBACK (gtk_combo_box_menu_activate), combo_box);
3028 /* Chain our row_separator_func through */
3029 _gtk_tree_menu_set_row_separator_func (GTK_TREE_MENU (menu),
3030 (GtkTreeViewRowSeparatorFunc)gtk_combo_box_row_separator_func,
3033 _gtk_tree_menu_set_header_func (GTK_TREE_MENU (menu),
3034 (GtkTreeMenuHeaderFunc)gtk_combo_box_header_func,
3037 g_signal_connect (menu, "key-press-event",
3038 G_CALLBACK (gtk_combo_box_menu_key_press), combo_box);
3039 gtk_combo_box_set_popup_widget (combo_box, menu);
3041 gtk_combo_box_update_title (combo_box);
3045 gtk_combo_box_menu_destroy (GtkComboBox *combo_box)
3047 GtkComboBoxPrivate *priv = combo_box->priv;
3049 g_signal_handlers_disconnect_matched (priv->button,
3050 G_SIGNAL_MATCH_DATA,
3052 gtk_combo_box_menu_button_press, NULL);
3053 g_signal_handlers_disconnect_matched (priv->button,
3054 G_SIGNAL_MATCH_DATA,
3056 gtk_combo_box_button_state_flags_changed, combo_box);
3057 g_signal_handlers_disconnect_matched (priv->popup_widget,
3058 G_SIGNAL_MATCH_DATA,
3060 gtk_combo_box_menu_activate, combo_box);
3062 /* unparent will remove our latest ref */
3063 gtk_widget_unparent (priv->button);
3066 priv->button = NULL;
3068 priv->separator = NULL;
3070 /* changing the popup window will unref the menu and the children */
3075 gtk_combo_box_menu_button_press (GtkWidget *widget,
3076 GdkEventButton *event,
3079 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3080 GtkComboBoxPrivate *priv = combo_box->priv;
3082 if (GTK_IS_MENU (priv->popup_widget) &&
3083 event->type == GDK_BUTTON_PRESS && event->button == 1)
3085 if (priv->focus_on_click &&
3086 !gtk_widget_has_focus (priv->button))
3087 gtk_widget_grab_focus (priv->button);
3089 gtk_combo_box_menu_popup (combo_box, event->button, event->time);
3098 gtk_combo_box_menu_activate (GtkWidget *menu,
3100 GtkComboBox *combo_box)
3104 if (gtk_tree_model_get_iter_from_string (combo_box->priv->model, &iter, path))
3105 gtk_combo_box_set_active_iter (combo_box, &iter);
3107 g_object_set (combo_box,
3108 "editing-canceled", FALSE,
3114 gtk_combo_box_update_sensitivity (GtkComboBox *combo_box)
3117 gboolean sensitive = TRUE; /* fool code checkers */
3119 if (!combo_box->priv->button)
3122 switch (combo_box->priv->button_sensitivity)
3124 case GTK_SENSITIVITY_ON:
3127 case GTK_SENSITIVITY_OFF:
3130 case GTK_SENSITIVITY_AUTO:
3131 sensitive = combo_box->priv->model &&
3132 gtk_tree_model_get_iter_first (combo_box->priv->model, &iter);
3135 g_assert_not_reached ();
3139 gtk_widget_set_sensitive (combo_box->priv->button, sensitive);
3141 /* In list-mode, we also need to update sensitivity of the event box */
3142 if (GTK_IS_TREE_VIEW (combo_box->priv->tree_view)
3143 && combo_box->priv->cell_view)
3144 gtk_widget_set_sensitive (combo_box->priv->box, sensitive);
3148 gtk_combo_box_model_row_inserted (GtkTreeModel *model,
3153 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3155 if (combo_box->priv->tree_view)
3156 gtk_combo_box_list_popup_resize (combo_box);
3158 gtk_combo_box_update_sensitivity (combo_box);
3162 gtk_combo_box_model_row_deleted (GtkTreeModel *model,
3166 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3167 GtkComboBoxPrivate *priv = combo_box->priv;
3169 if (!gtk_tree_row_reference_valid (priv->active_row))
3171 if (priv->cell_view)
3172 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (priv->cell_view), NULL);
3173 g_signal_emit (combo_box, combo_box_signals[CHANGED], 0);
3176 if (priv->tree_view)
3177 gtk_combo_box_list_popup_resize (combo_box);
3179 gtk_combo_box_update_sensitivity (combo_box);
3183 gtk_combo_box_model_rows_reordered (GtkTreeModel *model,
3189 gtk_tree_row_reference_reordered (G_OBJECT (user_data), path, iter, new_order);
3193 gtk_combo_box_model_row_changed (GtkTreeModel *model,
3198 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3199 GtkComboBoxPrivate *priv = combo_box->priv;
3200 GtkTreePath *active_path;
3202 /* FIXME this belongs to GtkCellView */
3203 if (gtk_tree_row_reference_valid (priv->active_row))
3205 active_path = gtk_tree_row_reference_get_path (priv->active_row);
3206 if (gtk_tree_path_compare (path, active_path) == 0 &&
3208 gtk_widget_queue_resize (GTK_WIDGET (priv->cell_view));
3209 gtk_tree_path_free (active_path);
3212 if (priv->tree_view)
3213 gtk_combo_box_list_row_changed (model, path, iter, user_data);
3217 list_popup_resize_idle (gpointer user_data)
3219 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3220 GtkComboBoxPrivate *priv = combo_box->priv;
3221 gint x, y, width, height;
3223 if (priv->tree_view && gtk_widget_get_mapped (priv->popup_window))
3225 gtk_combo_box_list_position (combo_box, &x, &y, &width, &height);
3227 gtk_widget_set_size_request (priv->popup_window, width, height);
3228 gtk_window_move (GTK_WINDOW (priv->popup_window), x, y);
3231 priv->resize_idle_id = 0;
3237 gtk_combo_box_list_popup_resize (GtkComboBox *combo_box)
3239 GtkComboBoxPrivate *priv = combo_box->priv;
3241 if (!priv->resize_idle_id)
3242 priv->resize_idle_id =
3243 gdk_threads_add_idle (list_popup_resize_idle, combo_box);
3247 gtk_combo_box_model_row_expanded (GtkTreeModel *model,
3252 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3254 gtk_combo_box_list_popup_resize (combo_box);
3263 gtk_combo_box_list_setup (GtkComboBox *combo_box)
3265 GtkComboBoxPrivate *priv = combo_box->priv;
3266 GtkTreeSelection *sel;
3268 GtkWidget *widget = GTK_WIDGET (combo_box);
3270 priv->button = gtk_toggle_button_new ();
3271 child = gtk_bin_get_child (GTK_BIN (combo_box));
3272 gtk_widget_set_parent (priv->button,
3273 gtk_widget_get_parent (child));
3274 g_signal_connect (priv->button, "button-press-event",
3275 G_CALLBACK (gtk_combo_box_list_button_pressed), combo_box);
3276 g_signal_connect (priv->button, "toggled",
3277 G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
3279 priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3280 gtk_container_add (GTK_CONTAINER (priv->button), priv->arrow);
3281 priv->separator = NULL;
3282 gtk_widget_show_all (priv->button);
3284 if (priv->cell_view)
3286 GtkStyleContext *context;
3287 GtkStateFlags state;
3290 context = gtk_widget_get_style_context (widget);
3291 state = gtk_widget_get_state_flags (widget);
3292 gtk_style_context_get_background_color (context, state, &color);
3294 gtk_cell_view_set_background_rgba (GTK_CELL_VIEW (priv->cell_view), &color);
3296 priv->box = gtk_event_box_new ();
3297 gtk_event_box_set_visible_window (GTK_EVENT_BOX (priv->box),
3300 if (priv->has_frame)
3302 priv->cell_view_frame = gtk_frame_new (NULL);
3303 gtk_frame_set_shadow_type (GTK_FRAME (priv->cell_view_frame),
3308 combo_box->priv->cell_view_frame = gtk_event_box_new ();
3309 gtk_event_box_set_visible_window (GTK_EVENT_BOX (combo_box->priv->cell_view_frame),
3313 gtk_widget_set_parent (priv->cell_view_frame,
3314 gtk_widget_get_parent (child));
3315 gtk_container_add (GTK_CONTAINER (priv->cell_view_frame), priv->box);
3316 gtk_widget_show_all (priv->cell_view_frame);
3318 g_signal_connect (priv->box, "button-press-event",
3319 G_CALLBACK (gtk_combo_box_list_button_pressed),
3323 priv->tree_view = gtk_tree_view_new ();
3324 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));
3325 gtk_tree_selection_set_mode (sel, GTK_SELECTION_BROWSE);
3326 gtk_tree_selection_set_select_function (sel,
3327 gtk_combo_box_list_select_func,
3329 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->tree_view),
3331 gtk_tree_view_set_hover_selection (GTK_TREE_VIEW (priv->tree_view),
3334 gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (priv->tree_view),
3335 (GtkTreeViewRowSeparatorFunc)gtk_combo_box_row_separator_func,
3339 gtk_tree_view_set_model (GTK_TREE_VIEW (priv->tree_view), priv->model);
3341 gtk_tree_view_append_column (GTK_TREE_VIEW (priv->tree_view),
3342 gtk_tree_view_column_new_with_area (priv->area));
3344 if (gtk_tree_row_reference_valid (priv->active_row))
3348 path = gtk_tree_row_reference_get_path (priv->active_row);
3349 gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->tree_view),
3351 gtk_tree_path_free (path);
3354 /* set sample/popup widgets */
3355 gtk_combo_box_set_popup_widget (combo_box, priv->tree_view);
3357 g_signal_connect (priv->tree_view, "key-press-event",
3358 G_CALLBACK (gtk_combo_box_list_key_press),
3360 g_signal_connect (priv->tree_view, "enter-notify-event",
3361 G_CALLBACK (gtk_combo_box_list_enter_notify),
3363 g_signal_connect (priv->tree_view, "row-expanded",
3364 G_CALLBACK (gtk_combo_box_model_row_expanded),
3366 g_signal_connect (priv->tree_view, "row-collapsed",
3367 G_CALLBACK (gtk_combo_box_model_row_expanded),
3369 g_signal_connect (priv->popup_window, "button-press-event",
3370 G_CALLBACK (gtk_combo_box_list_button_pressed),
3372 g_signal_connect (priv->popup_window, "button-release-event",
3373 G_CALLBACK (gtk_combo_box_list_button_released),
3376 gtk_widget_show (priv->tree_view);
3378 gtk_combo_box_update_sensitivity (combo_box);
3382 gtk_combo_box_list_destroy (GtkComboBox *combo_box)
3384 GtkComboBoxPrivate *priv = combo_box->priv;
3386 /* disconnect signals */
3387 g_signal_handlers_disconnect_matched (priv->tree_view,
3388 G_SIGNAL_MATCH_DATA,
3389 0, 0, NULL, NULL, combo_box);
3390 g_signal_handlers_disconnect_matched (priv->button,
3391 G_SIGNAL_MATCH_DATA,
3393 gtk_combo_box_list_button_pressed,
3395 g_signal_handlers_disconnect_matched (priv->popup_window,
3396 G_SIGNAL_MATCH_DATA,
3398 gtk_combo_box_list_button_pressed,
3400 g_signal_handlers_disconnect_matched (priv->popup_window,
3401 G_SIGNAL_MATCH_DATA,
3403 gtk_combo_box_list_button_released,
3406 g_signal_handlers_disconnect_matched (priv->popup_window,
3407 G_SIGNAL_MATCH_DATA,
3409 gtk_combo_box_child_show,
3412 g_signal_handlers_disconnect_matched (priv->popup_window,
3413 G_SIGNAL_MATCH_DATA,
3415 gtk_combo_box_child_hide,
3419 g_signal_handlers_disconnect_matched (priv->box,
3420 G_SIGNAL_MATCH_DATA,
3422 gtk_combo_box_list_button_pressed,
3425 /* destroy things (unparent will kill the latest ref from us)
3426 * last unref on button will destroy the arrow
3428 gtk_widget_unparent (priv->button);
3429 priv->button = NULL;
3432 if (priv->cell_view)
3434 g_object_set (priv->cell_view,
3435 "background-set", FALSE,
3439 if (priv->cell_view_frame)
3441 gtk_widget_unparent (priv->cell_view_frame);
3442 priv->cell_view_frame = NULL;
3446 if (priv->scroll_timer)
3448 g_source_remove (priv->scroll_timer);
3449 priv->scroll_timer = 0;
3452 if (priv->resize_idle_id)
3454 g_source_remove (priv->resize_idle_id);
3455 priv->resize_idle_id = 0;
3458 gtk_widget_destroy (priv->tree_view);
3460 priv->tree_view = NULL;
3461 if (priv->popup_widget)
3463 g_object_unref (priv->popup_widget);
3464 priv->popup_widget = NULL;
3471 gtk_combo_box_list_button_pressed (GtkWidget *widget,
3472 GdkEventButton *event,
3475 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
3476 GtkComboBoxPrivate *priv = combo_box->priv;
3478 GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *)event);
3480 if (ewidget == priv->popup_window)
3483 if ((ewidget != priv->button && ewidget != priv->box) ||
3484 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->button)))
3487 if (priv->focus_on_click &&
3488 !gtk_widget_has_focus (priv->button))
3489 gtk_widget_grab_focus (priv->button);
3491 gtk_combo_box_popup_for_device (combo_box, event->device);
3493 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button), TRUE);
3495 priv->auto_scroll = FALSE;
3496 if (priv->scroll_timer == 0)
3497 priv->scroll_timer = gdk_threads_add_timeout (SCROLL_TIME,
3498 (GSourceFunc) gtk_combo_box_list_scroll_timeout,
3501 priv->popup_in_progress = TRUE;
3507 gtk_combo_box_list_button_released (GtkWidget *widget,
3508 GdkEventButton *event,
3512 GtkTreePath *path = NULL;
3515 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
3516 GtkComboBoxPrivate *priv = combo_box->priv;
3518 gboolean popup_in_progress = FALSE;
3520 GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *)event);
3522 if (priv->popup_in_progress)
3524 popup_in_progress = TRUE;
3525 priv->popup_in_progress = FALSE;
3528 gtk_tree_view_set_hover_expand (GTK_TREE_VIEW (priv->tree_view),
3530 if (priv->scroll_timer)
3532 g_source_remove (priv->scroll_timer);
3533 priv->scroll_timer = 0;
3536 if (ewidget != priv->tree_view)
3538 if ((ewidget == priv->button ||
3539 ewidget == priv->box) &&
3540 !popup_in_progress &&
3541 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->button)))
3543 gtk_combo_box_popdown (combo_box);
3547 /* released outside treeview */
3548 if (ewidget != priv->button && ewidget != priv->box)
3550 gtk_combo_box_popdown (combo_box);
3558 /* select something cool */
3559 ret = gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (priv->tree_view),
3565 return TRUE; /* clicked outside window? */
3567 gtk_tree_model_get_iter (priv->model, &iter, path);
3568 gtk_tree_path_free (path);
3570 gtk_combo_box_popdown (combo_box);
3572 if (tree_column_row_is_sensitive (combo_box, &iter))
3573 gtk_combo_box_set_active_iter (combo_box, &iter);
3579 gtk_combo_box_menu_key_press (GtkWidget *widget,
3583 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
3585 if (!gtk_bindings_activate_event (G_OBJECT (widget), event))
3587 /* The menu hasn't managed the
3588 * event, forward it to the combobox
3590 gtk_bindings_activate_event (G_OBJECT (combo_box), event);
3597 gtk_combo_box_list_key_press (GtkWidget *widget,
3601 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
3604 if (event->keyval == GDK_KEY_Return || event->keyval == GDK_KEY_ISO_Enter || event->keyval == GDK_KEY_KP_Enter ||
3605 event->keyval == GDK_KEY_space || event->keyval == GDK_KEY_KP_Space)
3607 GtkTreeModel *model = NULL;
3609 gtk_combo_box_popdown (combo_box);
3611 if (combo_box->priv->model)
3613 GtkTreeSelection *sel;
3615 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (combo_box->priv->tree_view));
3617 if (gtk_tree_selection_get_selected (sel, &model, &iter))
3618 gtk_combo_box_set_active_iter (combo_box, &iter);
3624 if (!gtk_bindings_activate_event (G_OBJECT (widget), event))
3626 /* The list hasn't managed the
3627 * event, forward it to the combobox
3629 gtk_bindings_activate_event (G_OBJECT (combo_box), event);
3636 gtk_combo_box_list_auto_scroll (GtkComboBox *combo_box,
3641 GtkAllocation allocation;
3642 GtkWidget *tree_view = combo_box->priv->tree_view;
3645 gtk_widget_get_allocation (tree_view, &allocation);
3647 adj = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window));
3648 if (adj && gtk_adjustment_get_upper (adj) - gtk_adjustment_get_lower (adj) > gtk_adjustment_get_page_size (adj))
3650 if (x <= allocation.x &&
3651 gtk_adjustment_get_lower (adj) < gtk_adjustment_get_value (adj))
3653 value = gtk_adjustment_get_value (adj) - (allocation.x - x + 1);
3654 gtk_adjustment_set_value (adj, value);
3656 else if (x >= allocation.x + allocation.width &&
3657 gtk_adjustment_get_upper (adj) - gtk_adjustment_get_page_size (adj) > gtk_adjustment_get_value (adj))
3659 value = gtk_adjustment_get_value (adj) + (x - allocation.x - allocation.width + 1);
3660 gtk_adjustment_set_value (adj, MAX (value, 0.0));
3664 adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window));
3665 if (adj && gtk_adjustment_get_upper (adj) - gtk_adjustment_get_lower (adj) > gtk_adjustment_get_page_size (adj))
3667 if (y <= allocation.y &&
3668 gtk_adjustment_get_lower (adj) < gtk_adjustment_get_value (adj))
3670 value = gtk_adjustment_get_value (adj) - (allocation.y - y + 1);
3671 gtk_adjustment_set_value (adj, value);
3673 else if (y >= allocation.height &&
3674 gtk_adjustment_get_upper (adj) - gtk_adjustment_get_page_size (adj) > gtk_adjustment_get_value (adj))
3676 value = gtk_adjustment_get_value (adj) + (y - allocation.height + 1);
3677 gtk_adjustment_set_value (adj, MAX (value, 0.0));
3683 gtk_combo_box_list_scroll_timeout (GtkComboBox *combo_box)
3685 GtkComboBoxPrivate *priv = combo_box->priv;
3688 if (priv->auto_scroll)
3690 gdk_window_get_device_position (gtk_widget_get_window (priv->tree_view),
3693 gtk_combo_box_list_auto_scroll (combo_box, x, y);
3700 gtk_combo_box_list_enter_notify (GtkWidget *widget,
3701 GdkEventCrossing *event,
3704 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
3706 combo_box->priv->auto_scroll = TRUE;
3712 gtk_combo_box_list_select_func (GtkTreeSelection *selection,
3713 GtkTreeModel *model,
3715 gboolean path_currently_selected,
3718 GList *list, *columns;
3719 gboolean sensitive = FALSE;
3721 columns = gtk_tree_view_get_columns (gtk_tree_selection_get_tree_view (selection));
3723 for (list = columns; list && !sensitive; list = list->next)
3725 GList *cells, *cell;
3726 gboolean cell_sensitive, cell_visible;
3728 GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN (list->data);
3730 if (!gtk_tree_view_column_get_visible (column))
3733 gtk_tree_model_get_iter (model, &iter, path);
3734 gtk_tree_view_column_cell_set_cell_data (column, model, &iter,
3737 cell = cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
3740 g_object_get (cell->data,
3741 "sensitive", &cell_sensitive,
3742 "visible", &cell_visible,
3745 if (cell_visible && cell_sensitive)
3750 g_list_free (cells);
3752 sensitive = cell_sensitive;
3755 g_list_free (columns);
3761 gtk_combo_box_list_row_changed (GtkTreeModel *model,
3766 /* XXX Do nothing ? */
3770 * GtkCellLayout implementation
3772 static GtkCellArea *
3773 gtk_combo_box_cell_layout_get_area (GtkCellLayout *cell_layout)
3775 GtkComboBox *combo = GTK_COMBO_BOX (cell_layout);
3776 GtkComboBoxPrivate *priv = combo->priv;
3778 if (G_UNLIKELY (!priv->area))
3780 priv->area = gtk_cell_area_box_new ();
3781 g_object_ref_sink (priv->area);
3792 * gtk_combo_box_new:
3794 * Creates a new empty #GtkComboBox.
3796 * Return value: A new #GtkComboBox.
3801 gtk_combo_box_new (void)
3803 return g_object_new (GTK_TYPE_COMBO_BOX, NULL);
3807 * gtk_combo_box_new_with_area:
3808 * @area: the #GtkCellArea to use to layout cell renderers
3810 * Creates a new empty #GtkComboBox using @area to layout cells.
3812 * Return value: A new #GtkComboBox.
3815 gtk_combo_box_new_with_area (GtkCellArea *area)
3817 return g_object_new (GTK_TYPE_COMBO_BOX, "cell-area", area, NULL);
3821 * gtk_combo_box_new_with_area_and_entry:
3822 * @area: the #GtkCellArea to use to layout cell renderers
3824 * Creates a new empty #GtkComboBox with an entry.
3826 * The new combo box will use @area to layout cells.
3828 * Return value: A new #GtkComboBox.
3831 gtk_combo_box_new_with_area_and_entry (GtkCellArea *area)
3833 return g_object_new (GTK_TYPE_COMBO_BOX,
3841 * gtk_combo_box_new_with_entry:
3843 * Creates a new empty #GtkComboBox with an entry.
3845 * Return value: A new #GtkComboBox.
3848 gtk_combo_box_new_with_entry (void)
3850 return g_object_new (GTK_TYPE_COMBO_BOX, "has-entry", TRUE, NULL);
3854 * gtk_combo_box_new_with_model:
3855 * @model: A #GtkTreeModel.
3857 * Creates a new #GtkComboBox with the model initialized to @model.
3859 * Return value: A new #GtkComboBox.
3864 gtk_combo_box_new_with_model (GtkTreeModel *model)
3866 GtkComboBox *combo_box;
3868 g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL);
3870 combo_box = g_object_new (GTK_TYPE_COMBO_BOX, "model", model, NULL);
3872 return GTK_WIDGET (combo_box);
3876 * gtk_combo_box_new_with_model_and_entry:
3877 * @model: A #GtkTreeModel
3879 * Creates a new empty #GtkComboBox with an entry
3880 * and with the model initialized to @model.
3882 * Return value: A new #GtkComboBox
3885 gtk_combo_box_new_with_model_and_entry (GtkTreeModel *model)
3887 return g_object_new (GTK_TYPE_COMBO_BOX,
3894 * gtk_combo_box_get_wrap_width:
3895 * @combo_box: A #GtkComboBox
3897 * Returns the wrap width which is used to determine the number of columns
3898 * for the popup menu. If the wrap width is larger than 1, the combo box
3901 * Returns: the wrap width.
3906 gtk_combo_box_get_wrap_width (GtkComboBox *combo_box)
3908 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), -1);
3910 return combo_box->priv->wrap_width;
3914 * gtk_combo_box_set_wrap_width:
3915 * @combo_box: A #GtkComboBox
3916 * @width: Preferred number of columns
3918 * Sets the wrap width of @combo_box to be @width. The wrap width is basically
3919 * the preferred number of columns when you want the popup to be layed out
3925 gtk_combo_box_set_wrap_width (GtkComboBox *combo_box,
3928 GtkComboBoxPrivate *priv;
3930 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
3931 g_return_if_fail (width >= 0);
3933 priv = combo_box->priv;
3935 if (width != priv->wrap_width)
3937 priv->wrap_width = width;
3939 gtk_combo_box_check_appearance (combo_box);
3941 if (GTK_IS_TREE_MENU (priv->popup_widget))
3942 _gtk_tree_menu_set_wrap_width (GTK_TREE_MENU (priv->popup_widget), priv->wrap_width);
3944 g_object_notify (G_OBJECT (combo_box), "wrap-width");
3949 * gtk_combo_box_get_row_span_column:
3950 * @combo_box: A #GtkComboBox
3952 * Returns the column with row span information for @combo_box.
3954 * Returns: the row span column.
3959 gtk_combo_box_get_row_span_column (GtkComboBox *combo_box)
3961 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), -1);
3963 return combo_box->priv->row_column;
3967 * gtk_combo_box_set_row_span_column:
3968 * @combo_box: A #GtkComboBox.
3969 * @row_span: A column in the model passed during construction.
3971 * Sets the column with row span information for @combo_box to be @row_span.
3972 * The row span column contains integers which indicate how many rows
3973 * an item should span.
3978 gtk_combo_box_set_row_span_column (GtkComboBox *combo_box,
3981 GtkComboBoxPrivate *priv;
3984 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
3986 priv = combo_box->priv;
3988 col = gtk_tree_model_get_n_columns (priv->model);
3989 g_return_if_fail (row_span >= -1 && row_span < col);
3991 if (row_span != priv->row_column)
3993 priv->row_column = row_span;
3995 if (GTK_IS_TREE_MENU (priv->popup_widget))
3996 _gtk_tree_menu_set_row_span_column (GTK_TREE_MENU (priv->popup_widget), priv->row_column);
3998 g_object_notify (G_OBJECT (combo_box), "row-span-column");
4003 * gtk_combo_box_get_column_span_column:
4004 * @combo_box: A #GtkComboBox
4006 * Returns the column with column span information for @combo_box.
4008 * Returns: the column span column.
4013 gtk_combo_box_get_column_span_column (GtkComboBox *combo_box)
4015 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), -1);
4017 return combo_box->priv->col_column;
4021 * gtk_combo_box_set_column_span_column:
4022 * @combo_box: A #GtkComboBox
4023 * @column_span: A column in the model passed during construction
4025 * Sets the column with column span information for @combo_box to be
4026 * @column_span. The column span column contains integers which indicate
4027 * how many columns an item should span.
4032 gtk_combo_box_set_column_span_column (GtkComboBox *combo_box,
4035 GtkComboBoxPrivate *priv;
4038 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4040 priv = combo_box->priv;
4042 col = gtk_tree_model_get_n_columns (priv->model);
4043 g_return_if_fail (column_span >= -1 && column_span < col);
4045 if (column_span != priv->col_column)
4047 priv->col_column = column_span;
4049 if (GTK_IS_TREE_MENU (priv->popup_widget))
4050 _gtk_tree_menu_set_column_span_column (GTK_TREE_MENU (priv->popup_widget), priv->col_column);
4052 g_object_notify (G_OBJECT (combo_box), "column-span-column");
4057 * gtk_combo_box_get_active:
4058 * @combo_box: A #GtkComboBox
4060 * Returns the index of the currently active item, or -1 if there's no
4061 * active item. If the model is a non-flat treemodel, and the active item
4062 * is not an immediate child of the root of the tree, this function returns
4063 * <literal>gtk_tree_path_get_indices (path)[0]</literal>, where
4064 * <literal>path</literal> is the #GtkTreePath of the active item.
4066 * Return value: An integer which is the index of the currently active item,
4067 * or -1 if there's no active item.
4072 gtk_combo_box_get_active (GtkComboBox *combo_box)
4074 GtkComboBoxPrivate *priv;
4077 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), 0);
4079 priv = combo_box->priv;
4081 if (gtk_tree_row_reference_valid (priv->active_row))
4085 path = gtk_tree_row_reference_get_path (priv->active_row);
4086 result = gtk_tree_path_get_indices (path)[0];
4087 gtk_tree_path_free (path);
4096 * gtk_combo_box_set_active:
4097 * @combo_box: A #GtkComboBox
4098 * @index_: An index in the model passed during construction, or -1 to have
4101 * Sets the active item of @combo_box to be the item at @index.
4106 gtk_combo_box_set_active (GtkComboBox *combo_box,
4109 GtkTreePath *path = NULL;
4110 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4111 g_return_if_fail (index_ >= -1);
4113 if (combo_box->priv->model == NULL)
4115 /* Save index, in case the model is set after the index */
4116 combo_box->priv->active = index_;
4122 path = gtk_tree_path_new_from_indices (index_, -1);
4124 gtk_combo_box_set_active_internal (combo_box, path);
4127 gtk_tree_path_free (path);
4131 gtk_combo_box_set_active_internal (GtkComboBox *combo_box,
4134 GtkComboBoxPrivate *priv = combo_box->priv;
4135 GtkTreePath *active_path;
4138 /* Remember whether the initially active row is valid. */
4139 gboolean is_valid_row_reference = gtk_tree_row_reference_valid (priv->active_row);
4141 if (path && is_valid_row_reference)
4143 active_path = gtk_tree_row_reference_get_path (priv->active_row);
4144 path_cmp = gtk_tree_path_compare (path, active_path);
4145 gtk_tree_path_free (active_path);
4150 if (priv->active_row)
4152 gtk_tree_row_reference_free (priv->active_row);
4153 priv->active_row = NULL;
4158 if (priv->tree_view)
4159 gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view)));
4162 GtkMenu *menu = GTK_MENU (priv->popup_widget);
4164 if (GTK_IS_MENU (menu))
4165 gtk_menu_set_active (menu, -1);
4168 if (priv->cell_view)
4169 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (priv->cell_view), NULL);
4172 * Do not emit a "changed" signal when an already invalid selection was
4173 * now set to invalid.
4175 if (!is_valid_row_reference)
4181 gtk_tree_row_reference_new (priv->model, path);
4183 if (priv->tree_view)
4185 gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->tree_view),
4188 else if (GTK_IS_MENU (priv->popup_widget))
4190 /* FIXME handle nested menus better */
4191 gtk_menu_set_active (GTK_MENU (priv->popup_widget),
4192 gtk_tree_path_get_indices (path)[0]);
4195 if (priv->cell_view)
4196 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (priv->cell_view),
4200 g_signal_emit (combo_box, combo_box_signals[CHANGED], 0);
4201 g_object_notify (G_OBJECT (combo_box), "active");
4202 if (combo_box->priv->id_column >= 0)
4203 g_object_notify (G_OBJECT (combo_box), "active-id");
4208 * gtk_combo_box_get_active_iter:
4209 * @combo_box: A #GtkComboBox
4210 * @iter: (out): The uninitialized #GtkTreeIter
4212 * Sets @iter to point to the current active item, if it exists.
4214 * Return value: %TRUE, if @iter was set
4219 gtk_combo_box_get_active_iter (GtkComboBox *combo_box,
4225 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
4227 if (!gtk_tree_row_reference_valid (combo_box->priv->active_row))
4230 path = gtk_tree_row_reference_get_path (combo_box->priv->active_row);
4231 result = gtk_tree_model_get_iter (combo_box->priv->model, iter, path);
4232 gtk_tree_path_free (path);
4238 * gtk_combo_box_set_active_iter:
4239 * @combo_box: A #GtkComboBox
4240 * @iter: (allow-none): The #GtkTreeIter, or %NULL
4242 * Sets the current active item to be the one referenced by @iter, or
4243 * unsets the active item if @iter is %NULL.
4248 gtk_combo_box_set_active_iter (GtkComboBox *combo_box,
4251 GtkTreePath *path = NULL;
4253 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4256 path = gtk_tree_model_get_path (gtk_combo_box_get_model (combo_box), iter);
4258 gtk_combo_box_set_active_internal (combo_box, path);
4259 gtk_tree_path_free (path);
4263 * gtk_combo_box_set_model:
4264 * @combo_box: A #GtkComboBox
4265 * @model: (allow-none): A #GtkTreeModel
4267 * Sets the model used by @combo_box to be @model. Will unset a previously set
4268 * model (if applicable). If model is %NULL, then it will unset the model.
4270 * Note that this function does not clear the cell renderers, you have to
4271 * call gtk_cell_layout_clear() yourself if you need to set up different
4272 * cell renderers for the new model.
4277 gtk_combo_box_set_model (GtkComboBox *combo_box,
4278 GtkTreeModel *model)
4280 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4281 g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
4283 if (model == combo_box->priv->model)
4286 gtk_combo_box_unset_model (combo_box);
4291 combo_box->priv->model = model;
4292 g_object_ref (combo_box->priv->model);
4294 combo_box->priv->inserted_id =
4295 g_signal_connect (combo_box->priv->model, "row-inserted",
4296 G_CALLBACK (gtk_combo_box_model_row_inserted),
4298 combo_box->priv->deleted_id =
4299 g_signal_connect (combo_box->priv->model, "row-deleted",
4300 G_CALLBACK (gtk_combo_box_model_row_deleted),
4302 combo_box->priv->reordered_id =
4303 g_signal_connect (combo_box->priv->model, "rows-reordered",
4304 G_CALLBACK (gtk_combo_box_model_rows_reordered),
4306 combo_box->priv->changed_id =
4307 g_signal_connect (combo_box->priv->model, "row-changed",
4308 G_CALLBACK (gtk_combo_box_model_row_changed),
4311 if (combo_box->priv->tree_view)
4314 gtk_tree_view_set_model (GTK_TREE_VIEW (combo_box->priv->tree_view),
4315 combo_box->priv->model);
4316 gtk_combo_box_list_popup_resize (combo_box);
4319 if (GTK_IS_TREE_MENU (combo_box->priv->popup_widget))
4322 _gtk_tree_menu_set_model (GTK_TREE_MENU (combo_box->priv->popup_widget),
4323 combo_box->priv->model);
4326 if (combo_box->priv->cell_view)
4327 gtk_cell_view_set_model (GTK_CELL_VIEW (combo_box->priv->cell_view),
4328 combo_box->priv->model);
4330 if (combo_box->priv->active != -1)
4332 /* If an index was set in advance, apply it now */
4333 gtk_combo_box_set_active (combo_box, combo_box->priv->active);
4334 combo_box->priv->active = -1;
4338 gtk_combo_box_update_sensitivity (combo_box);
4340 g_object_notify (G_OBJECT (combo_box), "model");
4344 * gtk_combo_box_get_model:
4345 * @combo_box: A #GtkComboBox
4347 * Returns the #GtkTreeModel which is acting as data source for @combo_box.
4349 * Return value: (transfer none): A #GtkTreeModel which was passed
4350 * during construction.
4355 gtk_combo_box_get_model (GtkComboBox *combo_box)
4357 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
4359 return combo_box->priv->model;
4363 gtk_combo_box_real_move_active (GtkComboBox *combo_box,
4364 GtkScrollType scroll)
4367 GtkTreeIter new_iter;
4368 gboolean active_iter;
4371 if (!combo_box->priv->model)
4373 gtk_widget_error_bell (GTK_WIDGET (combo_box));
4377 active_iter = gtk_combo_box_get_active_iter (combo_box, &iter);
4381 case GTK_SCROLL_STEP_BACKWARD:
4382 case GTK_SCROLL_STEP_UP:
4383 case GTK_SCROLL_STEP_LEFT:
4386 found = tree_prev (combo_box, combo_box->priv->model,
4387 &iter, &new_iter, FALSE);
4390 /* else fall through */
4392 case GTK_SCROLL_PAGE_FORWARD:
4393 case GTK_SCROLL_PAGE_DOWN:
4394 case GTK_SCROLL_PAGE_RIGHT:
4395 case GTK_SCROLL_END:
4396 found = tree_last (combo_box, combo_box->priv->model, &new_iter, FALSE);
4399 case GTK_SCROLL_STEP_FORWARD:
4400 case GTK_SCROLL_STEP_DOWN:
4401 case GTK_SCROLL_STEP_RIGHT:
4404 found = tree_next (combo_box, combo_box->priv->model,
4405 &iter, &new_iter, FALSE);
4408 /* else fall through */
4410 case GTK_SCROLL_PAGE_BACKWARD:
4411 case GTK_SCROLL_PAGE_UP:
4412 case GTK_SCROLL_PAGE_LEFT:
4413 case GTK_SCROLL_START:
4414 found = tree_first (combo_box, combo_box->priv->model, &new_iter, FALSE);
4421 if (found && active_iter)
4423 GtkTreePath *old_path;
4424 GtkTreePath *new_path;
4426 old_path = gtk_tree_model_get_path (combo_box->priv->model, &iter);
4427 new_path = gtk_tree_model_get_path (combo_box->priv->model, &new_iter);
4429 if (gtk_tree_path_compare (old_path, new_path) == 0)
4432 gtk_tree_path_free (old_path);
4433 gtk_tree_path_free (new_path);
4438 gtk_combo_box_set_active_iter (combo_box, &new_iter);
4442 gtk_widget_error_bell (GTK_WIDGET (combo_box));
4447 gtk_combo_box_mnemonic_activate (GtkWidget *widget,
4448 gboolean group_cycling)
4450 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
4452 if (combo_box->priv->has_entry)
4456 child = gtk_bin_get_child (GTK_BIN (combo_box));
4458 gtk_widget_grab_focus (child);
4461 gtk_widget_grab_focus (combo_box->priv->button);
4467 gtk_combo_box_grab_focus (GtkWidget *widget)
4469 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
4471 if (combo_box->priv->has_entry)
4475 child = gtk_bin_get_child (GTK_BIN (combo_box));
4477 gtk_widget_grab_focus (child);
4480 gtk_widget_grab_focus (combo_box->priv->button);
4484 gtk_combo_box_destroy (GtkWidget *widget)
4486 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
4488 if (combo_box->priv->popup_idle_id > 0)
4490 g_source_remove (combo_box->priv->popup_idle_id);
4491 combo_box->priv->popup_idle_id = 0;
4494 gtk_combo_box_popdown (combo_box);
4496 if (combo_box->priv->row_separator_destroy)
4497 combo_box->priv->row_separator_destroy (combo_box->priv->row_separator_data);
4499 combo_box->priv->row_separator_func = NULL;
4500 combo_box->priv->row_separator_data = NULL;
4501 combo_box->priv->row_separator_destroy = NULL;
4503 GTK_WIDGET_CLASS (gtk_combo_box_parent_class)->destroy (widget);
4504 combo_box->priv->cell_view = NULL;
4508 gtk_combo_box_entry_contents_changed (GtkEntry *entry,
4511 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
4514 * Fixes regression reported in bug #574059. The old functionality relied on
4515 * bug #572478. As a bugfix, we now emit the "changed" signal ourselves
4516 * when the selection was already set to -1.
4518 if (gtk_combo_box_get_active(combo_box) == -1)
4519 g_signal_emit_by_name (combo_box, "changed");
4521 gtk_combo_box_set_active (combo_box, -1);
4525 gtk_combo_box_entry_active_changed (GtkComboBox *combo_box,
4528 GtkComboBoxPrivate *priv = combo_box->priv;
4529 GtkTreeModel *model;
4532 if (gtk_combo_box_get_active_iter (combo_box, &iter))
4534 GtkEntry *entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (combo_box)));
4538 GValue value = {0,};
4540 g_signal_handlers_block_by_func (entry,
4541 gtk_combo_box_entry_contents_changed,
4544 model = gtk_combo_box_get_model (combo_box);
4546 gtk_tree_model_get_value (model, &iter,
4547 priv->text_column, &value);
4548 g_object_set_property (G_OBJECT (entry), "text", &value);
4549 g_value_unset (&value);
4551 g_signal_handlers_unblock_by_func (entry,
4552 gtk_combo_box_entry_contents_changed,
4559 gtk_combo_box_constructor (GType type,
4560 guint n_construct_properties,
4561 GObjectConstructParam *construct_properties)
4564 GtkComboBox *combo_box;
4565 GtkComboBoxPrivate *priv;
4567 object = G_OBJECT_CLASS (gtk_combo_box_parent_class)->constructor
4568 (type, n_construct_properties, construct_properties);
4570 combo_box = GTK_COMBO_BOX (object);
4571 priv = combo_box->priv;
4575 priv->area = gtk_cell_area_box_new ();
4576 g_object_ref_sink (priv->area);
4579 priv->cell_view = gtk_cell_view_new_with_context (priv->area, NULL);
4580 gtk_cell_view_set_fit_model (GTK_CELL_VIEW (priv->cell_view), TRUE);
4581 gtk_cell_view_set_model (GTK_CELL_VIEW (priv->cell_view), priv->model);
4582 gtk_widget_set_parent (priv->cell_view, GTK_WIDGET (combo_box));
4583 _gtk_bin_set_child (GTK_BIN (combo_box), priv->cell_view);
4584 gtk_widget_show (priv->cell_view);
4586 gtk_combo_box_check_appearance (combo_box);
4588 if (priv->has_entry)
4592 entry = gtk_entry_new ();
4593 gtk_widget_show (entry);
4594 gtk_container_add (GTK_CONTAINER (combo_box), entry);
4596 priv->text_renderer = gtk_cell_renderer_text_new ();
4597 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box),
4598 priv->text_renderer, TRUE);
4600 gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), -1);
4602 g_signal_connect (combo_box, "changed",
4603 G_CALLBACK (gtk_combo_box_entry_active_changed), NULL);
4611 gtk_combo_box_dispose(GObject* object)
4613 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
4615 if (GTK_IS_MENU (combo_box->priv->popup_widget))
4617 gtk_combo_box_menu_destroy (combo_box);
4618 gtk_menu_detach (GTK_MENU (combo_box->priv->popup_widget));
4619 combo_box->priv->popup_widget = NULL;
4622 if (combo_box->priv->area)
4624 g_object_unref (combo_box->priv->area);
4625 combo_box->priv->area = NULL;
4628 if (GTK_IS_TREE_VIEW (combo_box->priv->tree_view))
4629 gtk_combo_box_list_destroy (combo_box);
4631 if (combo_box->priv->popup_window)
4633 gtk_widget_destroy (combo_box->priv->popup_window);
4634 combo_box->priv->popup_window = NULL;
4637 gtk_combo_box_unset_model (combo_box);
4639 G_OBJECT_CLASS (gtk_combo_box_parent_class)->dispose (object);
4643 gtk_combo_box_finalize (GObject *object)
4645 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
4647 g_free (combo_box->priv->tearoff_title);
4649 G_OBJECT_CLASS (gtk_combo_box_parent_class)->finalize (object);
4653 gtk_cell_editable_key_press (GtkWidget *widget,
4657 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
4659 if (event->keyval == GDK_KEY_Escape)
4661 g_object_set (combo_box,
4662 "editing-canceled", TRUE,
4664 gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (combo_box));
4665 gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (combo_box));
4669 else if (event->keyval == GDK_KEY_Return ||
4670 event->keyval == GDK_KEY_ISO_Enter ||
4671 event->keyval == GDK_KEY_KP_Enter)
4673 gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (combo_box));
4674 gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (combo_box));
4683 popdown_idle (gpointer data)
4685 GtkComboBox *combo_box;
4687 combo_box = GTK_COMBO_BOX (data);
4689 gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (combo_box));
4690 gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (combo_box));
4692 g_object_unref (combo_box);
4698 popdown_handler (GtkWidget *widget,
4701 gdk_threads_add_idle (popdown_idle, g_object_ref (data));
4705 popup_idle (gpointer data)
4707 GtkComboBox *combo_box;
4709 combo_box = GTK_COMBO_BOX (data);
4711 if (GTK_IS_MENU (combo_box->priv->popup_widget) &&
4712 combo_box->priv->cell_view)
4713 g_signal_connect_object (combo_box->priv->popup_widget,
4714 "unmap", G_CALLBACK (popdown_handler),
4717 /* we unset this if a menu item is activated */
4718 g_object_set (combo_box,
4719 "editing-canceled", TRUE,
4721 gtk_combo_box_popup (combo_box);
4723 combo_box->priv->popup_idle_id = 0;
4724 combo_box->priv->activate_button = 0;
4725 combo_box->priv->activate_time = 0;
4731 gtk_combo_box_start_editing (GtkCellEditable *cell_editable,
4734 GtkComboBox *combo_box = GTK_COMBO_BOX (cell_editable);
4737 combo_box->priv->is_cell_renderer = TRUE;
4739 if (combo_box->priv->cell_view)
4741 g_signal_connect_object (combo_box->priv->button, "key-press-event",
4742 G_CALLBACK (gtk_cell_editable_key_press),
4745 gtk_widget_grab_focus (combo_box->priv->button);
4749 child = gtk_bin_get_child (GTK_BIN (combo_box));
4751 g_signal_connect_object (child, "key-press-event",
4752 G_CALLBACK (gtk_cell_editable_key_press),
4755 gtk_widget_grab_focus (child);
4756 gtk_widget_set_can_focus (combo_box->priv->button, FALSE);
4759 /* we do the immediate popup only for the optionmenu-like
4762 if (combo_box->priv->is_cell_renderer &&
4763 combo_box->priv->cell_view && !combo_box->priv->tree_view)
4765 if (event && event->type == GDK_BUTTON_PRESS)
4767 GdkEventButton *event_button = (GdkEventButton *)event;
4769 combo_box->priv->activate_button = event_button->button;
4770 combo_box->priv->activate_time = event_button->time;
4773 combo_box->priv->popup_idle_id =
4774 gdk_threads_add_idle (popup_idle, combo_box);
4780 * gtk_combo_box_get_add_tearoffs:
4781 * @combo_box: a #GtkComboBox
4783 * Gets the current value of the :add-tearoffs property.
4785 * Return value: the current value of the :add-tearoffs property.
4788 gtk_combo_box_get_add_tearoffs (GtkComboBox *combo_box)
4790 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
4792 return combo_box->priv->add_tearoffs;
4796 * gtk_combo_box_set_add_tearoffs:
4797 * @combo_box: a #GtkComboBox
4798 * @add_tearoffs: %TRUE to add tearoff menu items
4800 * Sets whether the popup menu should have a tearoff
4806 gtk_combo_box_set_add_tearoffs (GtkComboBox *combo_box,
4807 gboolean add_tearoffs)
4809 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4811 add_tearoffs = add_tearoffs != FALSE;
4813 if (combo_box->priv->add_tearoffs != add_tearoffs)
4815 combo_box->priv->add_tearoffs = add_tearoffs;
4816 gtk_combo_box_check_appearance (combo_box);
4818 if (GTK_IS_TREE_MENU (combo_box->priv->popup_widget))
4819 _gtk_tree_menu_set_tearoff (GTK_TREE_MENU (combo_box->priv->popup_widget),
4820 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.
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: (skip)
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_buildable_custom_tag_start (GtkBuildable *buildable,
5206 GtkBuilder *builder,
5208 const gchar *tagname,
5209 GMarkupParser *parser,
5212 if (parent_buildable_iface->custom_tag_start (buildable, builder, child,
5213 tagname, parser, data))
5216 return _gtk_cell_layout_buildable_custom_tag_start (buildable, builder, child,
5217 tagname, parser, data);
5221 gtk_combo_box_buildable_custom_tag_end (GtkBuildable *buildable,
5222 GtkBuilder *builder,
5224 const gchar *tagname,
5227 if (!_gtk_cell_layout_buildable_custom_tag_end (buildable, builder, child, tagname, data))
5228 parent_buildable_iface->custom_tag_end (buildable, builder, child, tagname, data);
5232 gtk_combo_box_buildable_get_internal_child (GtkBuildable *buildable,
5233 GtkBuilder *builder,
5234 const gchar *childname)
5236 GtkComboBox *combo_box = GTK_COMBO_BOX (buildable);
5238 if (combo_box->priv->has_entry && strcmp (childname, "entry") == 0)
5239 return G_OBJECT (gtk_bin_get_child (GTK_BIN (buildable)));
5241 return parent_buildable_iface->get_internal_child (buildable, builder, childname);
5245 gtk_combo_box_get_preferred_width (GtkWidget *widget,
5249 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
5250 GtkComboBoxPrivate *priv = combo_box->priv;
5251 gint focus_width, focus_pad;
5252 gint font_size, arrow_size;
5253 PangoContext *context;
5254 PangoFontMetrics *metrics;
5255 const PangoFontDescription *font_desc;
5257 gint minimum_width = 0, natural_width = 0;
5258 gint child_min, child_nat;
5259 GtkStyleContext *style_context;
5260 GtkStateFlags state;
5262 gfloat arrow_scaling;
5264 child = gtk_bin_get_child (GTK_BIN (widget));
5267 gtk_widget_get_preferred_width (child, &child_min, &child_nat);
5269 gtk_widget_style_get (GTK_WIDGET (widget),
5270 "focus-line-width", &focus_width,
5271 "focus-padding", &focus_pad,
5272 "arrow-size", &arrow_size,
5273 "arrow-scaling", &arrow_scaling,
5276 style_context = gtk_widget_get_style_context (widget);
5277 state = gtk_widget_get_state_flags (widget);
5279 get_widget_padding (widget, &padding);
5280 font_desc = gtk_style_context_get_font (style_context, state);
5282 context = gtk_widget_get_pango_context (GTK_WIDGET (widget));
5283 metrics = pango_context_get_metrics (context, font_desc,
5284 pango_context_get_language (context));
5285 font_size = PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) +
5286 pango_font_metrics_get_descent (metrics));
5287 pango_font_metrics_unref (metrics);
5289 arrow_size = MAX (arrow_size, font_size) * arrow_scaling;
5291 gtk_widget_set_size_request (priv->arrow, arrow_size, arrow_size);
5293 if (!priv->tree_view)
5296 if (priv->cell_view)
5298 gint sep_width, arrow_width;
5299 gint border_width, xpad;
5300 GtkBorder button_padding;
5302 border_width = gtk_container_get_border_width (GTK_CONTAINER (combo_box));
5303 get_widget_padding (priv->button, &button_padding);
5305 gtk_widget_get_preferred_width (priv->separator, &sep_width, NULL);
5306 gtk_widget_get_preferred_width (priv->arrow, &arrow_width, NULL);
5308 xpad = 2 * (border_width + focus_width + focus_pad) +
5309 button_padding.left + button_padding.right + padding.left + padding.right;
5311 minimum_width = child_min + sep_width + arrow_width + xpad;
5312 natural_width = child_nat + sep_width + arrow_width + xpad;
5316 gint but_width, but_nat_width;
5318 gtk_widget_get_preferred_width (priv->button,
5319 &but_width, &but_nat_width);
5321 minimum_width = child_min + but_width;
5322 natural_width = child_nat + but_nat_width;
5328 gint button_width, button_nat_width;
5330 /* sample + frame */
5331 minimum_width = child_min;
5332 natural_width = child_nat;
5334 minimum_width += 2 * focus_width;
5335 natural_width += 2 * focus_width;
5337 if (priv->cell_view_frame)
5339 if (priv->has_frame)
5341 gint border_width, xpad;
5342 GtkBorder frame_padding;
5344 border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->cell_view_frame));
5345 get_widget_padding (priv->cell_view_frame, &frame_padding);
5346 xpad = (2 * border_width) + frame_padding.left + frame_padding.right;
5348 minimum_width += xpad;
5349 natural_width += xpad;
5354 gtk_widget_get_preferred_width (priv->button,
5355 &button_width, &button_nat_width);
5357 minimum_width += button_width;
5358 natural_width += button_nat_width;
5361 minimum_width += padding.left + padding.right;
5362 natural_width += padding.left + padding.right;
5365 *minimum_size = minimum_width;
5368 *natural_size = natural_width;
5372 gtk_combo_box_get_preferred_height (GtkWidget *widget,
5378 /* Combo box is height-for-width only
5379 * (so we always just reserve enough height for the minimum width) */
5380 GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, &min_width, NULL);
5381 GTK_WIDGET_GET_CLASS (widget)->get_preferred_height_for_width (widget, min_width, minimum_size, natural_size);
5385 gtk_combo_box_get_preferred_width_for_height (GtkWidget *widget,
5390 /* Combo box is height-for-width only
5391 * (so we assume we always reserved enough height for the minimum width) */
5392 GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, minimum_size, natural_size);
5397 gtk_combo_box_get_preferred_height_for_width (GtkWidget *widget,
5402 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
5403 GtkComboBoxPrivate *priv = combo_box->priv;
5404 gint focus_width, focus_pad;
5405 gint min_height = 0, nat_height = 0;
5410 gtk_widget_style_get (GTK_WIDGET (widget),
5411 "focus-line-width", &focus_width,
5412 "focus-padding", &focus_pad,
5415 child = gtk_bin_get_child (GTK_BIN (widget));
5417 get_widget_padding (widget, &padding);
5420 if (!priv->tree_view)
5423 if (priv->cell_view)
5425 /* calculate x/y padding and separator/arrow size */
5426 gint sep_width, arrow_width, sep_height, arrow_height;
5427 gint border_width, xpad, ypad;
5428 GtkBorder button_padding;
5430 border_width = gtk_container_get_border_width (GTK_CONTAINER (combo_box));
5431 get_widget_padding (priv->button, &button_padding);
5433 gtk_widget_get_preferred_width (priv->separator, &sep_width, NULL);
5434 gtk_widget_get_preferred_width (priv->arrow, &arrow_width, NULL);
5435 gtk_widget_get_preferred_height_for_width (priv->separator,
5436 sep_width, &sep_height, NULL);
5437 gtk_widget_get_preferred_height_for_width (priv->arrow,
5438 arrow_width, &arrow_height, NULL);
5440 xpad = 2 * (border_width + focus_width + focus_pad) +
5441 button_padding.left + button_padding.right;
5442 ypad = 2 * (border_width + focus_width + focus_pad) +
5443 button_padding.top + button_padding.bottom;
5445 size -= sep_width + arrow_width + xpad;
5447 /* Get height-for-width of the child widget, usually a GtkCellArea calculating
5448 * and fitting the whole treemodel */
5449 gtk_widget_get_preferred_height_for_width (child, size, &min_height, &nat_height);
5451 arrow_height = MAX (arrow_height, sep_height);
5452 min_height = MAX (min_height, arrow_height);
5453 nat_height = MAX (nat_height, arrow_height);
5460 /* there is a custom child widget inside (no priv->cell_view) */
5461 gint but_width, but_height;
5463 gtk_widget_get_preferred_width (priv->button, &but_width, NULL);
5464 gtk_widget_get_preferred_height_for_width (priv->button,
5465 but_width, &but_height, NULL);
5469 /* Get height-for-width of the child widget, usually a GtkCellArea calculating
5470 * and fitting the whole treemodel */
5471 gtk_widget_get_preferred_height_for_width (child, size, &min_height, &nat_height);
5473 min_height = MAX (min_height, but_height);
5474 nat_height = MAX (nat_height, but_height);
5480 gint but_width, but_height;
5481 gint xpad = 0, ypad = 0;
5483 gtk_widget_get_preferred_width (priv->button, &but_width, NULL);
5484 gtk_widget_get_preferred_height_for_width (priv->button,
5485 but_width, &but_height, NULL);
5487 if (priv->cell_view_frame && priv->has_frame)
5489 GtkBorder frame_padding;
5492 border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->cell_view_frame));
5493 get_widget_padding (GTK_WIDGET (priv->cell_view_frame), &frame_padding);
5495 xpad = (2 * border_width) + padding.left + frame_padding.right;
5496 ypad = (2 * border_width) + padding.top + frame_padding.bottom;
5500 size -= 2 * focus_width;
5503 /* Get height-for-width of the child widget, usually a GtkCellArea calculating
5504 * and fitting the whole treemodel */
5505 gtk_widget_get_preferred_height_for_width (child, size, &min_height, &nat_height);
5507 min_height = MAX (min_height, but_height);
5508 nat_height = MAX (nat_height, but_height);
5514 min_height += padding.top + padding.bottom;
5515 nat_height += padding.top + padding.bottom;
5518 *minimum_size = min_height;
5521 *natural_size = nat_height;
5525 * gtk_combo_box_set_id_column:
5526 * @combo_box: A #GtkComboBox
5527 * @id_column: A column in @model to get string IDs for values from
5529 * Sets the model column which @combo_box should use to get string IDs
5530 * for values from. The column @id_column in the model of @combo_box
5531 * must be of type %G_TYPE_STRING.
5536 gtk_combo_box_set_id_column (GtkComboBox *combo_box,
5539 GtkComboBoxPrivate *priv = combo_box->priv;
5540 GtkTreeModel *model;
5542 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5544 if (id_column != priv->id_column)
5546 model = gtk_combo_box_get_model (combo_box);
5548 g_return_if_fail (id_column >= 0);
5549 g_return_if_fail (model == NULL ||
5550 id_column < gtk_tree_model_get_n_columns (model));
5552 priv->id_column = id_column;
5554 g_object_notify (G_OBJECT (combo_box), "id-column");
5555 g_object_notify (G_OBJECT (combo_box), "active-id");
5560 * gtk_combo_box_get_id_column:
5561 * @combo_box: A #GtkComboBox
5563 * Returns the column which @combo_box is using to get string IDs
5566 * Return value: A column in the data source model of @combo_box.
5571 gtk_combo_box_get_id_column (GtkComboBox *combo_box)
5573 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), 0);
5575 return combo_box->priv->id_column;
5579 * gtk_combo_box_get_active_id:
5580 * @combo_box: a #GtkComboBox
5582 * Returns the ID of the active row of @combo_box. This value is taken
5583 * from the active row and the column specified by the #GtkComboBox:id-column
5584 * property of @combo_box (see gtk_combo_box_set_id_column()).
5586 * The returned value is an interned string which means that you can
5587 * compare the pointer by value to other interned strings and that you
5590 * If the #GtkComboBox:id-column property of @combo_box is not set, or if
5591 * no row is active, or if the active row has a %NULL ID value, then %NULL
5594 * Return value: the ID of the active row, or %NULL
5599 gtk_combo_box_get_active_id (GtkComboBox *combo_box)
5601 GtkTreeModel *model;
5605 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
5607 column = combo_box->priv->id_column;
5612 model = gtk_combo_box_get_model (combo_box);
5613 g_return_val_if_fail (gtk_tree_model_get_column_type (model, column) ==
5614 G_TYPE_STRING, NULL);
5616 if (gtk_combo_box_get_active_iter (combo_box, &iter))
5618 const gchar *interned;
5621 gtk_tree_model_get (model, &iter, column, &id, -1);
5622 interned = g_intern_string (id);
5632 * gtk_combo_box_set_active_id:
5633 * @combo_box: a #GtkComboBox
5634 * @active_id: (allow-none): the ID of the row to select, or %NULL
5636 * Changes the active row of @combo_box to the one that has an ID equal to
5637 * @active_id, or unsets the active row if @active_id is %NULL. Rows having
5638 * a %NULL ID string cannot be made active by this function.
5640 * If the #GtkComboBox:id-column property of @combo_box is unset or if no
5641 * row has the given ID then the function does nothing and returns %FALSE.
5643 * Returns: %TRUE if a row with a matching ID was found. If a %NULL
5644 * @active_id was given to unset the active row, the function
5645 * always returns %TRUE.
5650 gtk_combo_box_set_active_id (GtkComboBox *combo_box,
5651 const gchar *active_id)
5653 GtkTreeModel *model;
5655 gboolean match = FALSE;
5658 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
5660 if (active_id == NULL)
5662 gtk_combo_box_set_active (combo_box, -1);
5663 return TRUE; /* active row was successfully unset */
5666 column = combo_box->priv->id_column;
5671 model = gtk_combo_box_get_model (combo_box);
5672 g_return_val_if_fail (gtk_tree_model_get_column_type (model, column) ==
5673 G_TYPE_STRING, FALSE);
5675 if (gtk_tree_model_get_iter_first (model, &iter))
5679 gtk_tree_model_get (model, &iter, column, &id, -1);
5681 match = strcmp (id, active_id) == 0;
5686 gtk_combo_box_set_active_iter (combo_box, &iter);
5689 } while (gtk_tree_model_iter_next (model, &iter));