2 * Copyright (C) 2002, 2003 Kristian Rietveld <kris@gtk.org>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
21 #include "gtkcombobox.h"
24 #include "gtkbindings.h"
25 #include "gtkcelllayout.h"
26 #include "gtkcellrenderertext.h"
27 #include "gtkcellview.h"
28 #include "gtkeventbox.h"
31 #include "gtkliststore.h"
34 #include "gtkscrolledwindow.h"
35 #include "gtkseparatormenuitem.h"
36 #include "gtktearoffmenuitem.h"
37 #include "gtktogglebutton.h"
38 #include "gtktreeselection.h"
39 #include "gtkvseparator.h"
40 #include "gtkwindow.h"
41 #include "gtkprivate.h"
43 #include <gdk/gdkkeysyms.h>
45 #include <gobject/gvaluecollector.h>
50 #include "gtkmarshalers.h"
53 #include "gtkentryprivate.h"
54 #include "gtktreeprivate.h"
59 * @Short_description: A widget used to choose from a list of items
61 * @See_also: #GtkComboBoxText, #GtkTreeModel, #GtkCellRenderer
63 * A GtkComboBox is a widget that allows the user to choose from a list of
64 * valid choices. The GtkComboBox displays the selected choice. When
65 * activated, the GtkComboBox displays a popup which allows the user to
66 * make a new choice. The style in which the selected value is displayed,
67 * and the style of the popup is determined by the current theme. It may
68 * be similar to a Windows-style combo box.
70 * The GtkComboBox uses the model-view pattern; the list of valid choices
71 * is specified in the form of a tree model, and the display of the choices
72 * can be adapted to the data in the model by using cell renderers, as you
73 * would in a tree view. This is possible since GtkComboBox implements the
74 * #GtkCellLayout interface. The tree model holding the valid choices is
75 * not restricted to a flat list, it can be a real tree, and the popup will
76 * reflect the tree structure.
78 * For a simple list of textual choices, the model-view API of GtkComboBox
79 * can be a bit overwhelming. In this case, #GtkComboBoxText offers a
84 /* WELCOME, to THE house of evil code */
86 typedef struct _ComboCellInfo ComboCellInfo;
89 GtkCellRenderer *cell;
92 GtkCellLayoutDataFunc func;
94 GDestroyNotify destroy;
101 struct _GtkComboBoxPrivate
109 GtkShadowType shadow_type;
111 gint active; /* Only temporary */
112 GtkTreeRowReference *active_row;
114 GtkWidget *tree_view;
115 GtkTreeViewColumn *column;
117 GtkWidget *cell_view;
118 GtkWidget *cell_view_frame;
123 GtkWidget *separator;
125 GtkWidget *popup_widget;
126 GtkWidget *popup_window;
127 GtkWidget *scrolled_window;
134 guint activate_button;
135 guint32 activate_time;
137 guint resize_idle_id;
142 /* For "has-entry" specific behavior we track
143 * an automated cell renderer and text column */
145 GtkCellRenderer *text_renderer;
151 guint popup_in_progress : 1;
152 guint popup_shown : 1;
153 guint add_tearoffs : 1;
155 guint is_cell_renderer : 1;
156 guint editing_canceled : 1;
157 guint auto_scroll : 1;
158 guint focus_on_click : 1;
159 guint button_sensitivity : 2;
161 guint popup_fixed_width : 1;
163 GtkTreeViewRowSeparatorFunc row_separator_func;
164 gpointer row_separator_data;
165 GDestroyNotify row_separator_destroy;
167 GdkDevice *grab_pointer;
168 GdkDevice *grab_keyboard;
170 gchar *tearoff_title;
173 /* While debugging this evil code, I have learned that
174 * there are actually 4 modes to this widget, which can
175 * be characterized as follows
177 * 1) menu mode, no child added
180 * cell_view -> GtkCellView, regular child
181 * cell_view_frame -> NULL
182 * button -> GtkToggleButton set_parent to combo
183 * arrow -> GtkArrow set_parent to button
184 * separator -> GtkVSepator set_parent to button
185 * popup_widget -> GtkMenu
186 * popup_window -> NULL
187 * scrolled_window -> NULL
189 * 2) menu mode, child added
193 * cell_view_frame -> NULL
194 * button -> GtkToggleButton set_parent to combo
195 * arrow -> GtkArrow, child of button
197 * popup_widget -> GtkMenu
198 * popup_window -> NULL
199 * scrolled_window -> NULL
201 * 3) list mode, no child added
203 * tree_view -> GtkTreeView, child of scrolled_window
204 * cell_view -> GtkCellView, regular child
205 * cell_view_frame -> GtkFrame, set parent to combo
206 * button -> GtkToggleButton, set_parent to combo
207 * arrow -> GtkArrow, child of button
209 * popup_widget -> tree_view
210 * popup_window -> GtkWindow
211 * scrolled_window -> GtkScrolledWindow, child of popup_window
213 * 4) list mode, child added
215 * tree_view -> GtkTreeView, child of scrolled_window
217 * cell_view_frame -> NULL
218 * button -> GtkToggleButton, set_parent to combo
219 * arrow -> GtkArrow, child of button
221 * popup_widget -> tree_view
222 * popup_window -> GtkWindow
223 * scrolled_window -> GtkScrolledWindow, child of popup_window
239 PROP_ROW_SPAN_COLUMN,
240 PROP_COLUMN_SPAN_COLUMN,
247 PROP_BUTTON_SENSITIVITY,
248 PROP_EDITING_CANCELED,
250 PROP_ENTRY_TEXT_COLUMN,
251 PROP_POPUP_FIXED_WIDTH,
256 static guint combo_box_signals[LAST_SIGNAL] = {0,};
258 #define BONUS_PADDING 4
259 #define SCROLL_TIME 100
263 static void gtk_combo_box_cell_layout_init (GtkCellLayoutIface *iface);
264 static void gtk_combo_box_cell_editable_init (GtkCellEditableIface *iface);
265 static GObject *gtk_combo_box_constructor (GType type,
266 guint n_construct_properties,
267 GObjectConstructParam *construct_properties);
268 static void gtk_combo_box_dispose (GObject *object);
269 static void gtk_combo_box_finalize (GObject *object);
270 static void gtk_combo_box_destroy (GtkWidget *widget);
272 static void gtk_combo_box_set_property (GObject *object,
276 static void gtk_combo_box_get_property (GObject *object,
281 static void gtk_combo_box_state_changed (GtkWidget *widget,
282 GtkStateType previous);
283 static void gtk_combo_box_grab_focus (GtkWidget *widget);
284 static void gtk_combo_box_style_updated (GtkWidget *widget);
285 static void gtk_combo_box_button_toggled (GtkWidget *widget,
287 static void gtk_combo_box_button_state_flags_changed (GtkWidget *widget,
288 GtkStateFlags previous,
290 static void gtk_combo_box_add (GtkContainer *container,
292 static void gtk_combo_box_remove (GtkContainer *container,
295 static ComboCellInfo *gtk_combo_box_get_cell_info (GtkComboBox *combo_box,
296 GtkCellRenderer *cell);
298 static void gtk_combo_box_menu_show (GtkWidget *menu,
300 static void gtk_combo_box_menu_hide (GtkWidget *menu,
303 static void gtk_combo_box_set_popup_widget (GtkComboBox *combo_box,
305 static void gtk_combo_box_menu_position_below (GtkMenu *menu,
310 static void gtk_combo_box_menu_position_over (GtkMenu *menu,
315 static void gtk_combo_box_menu_position (GtkMenu *menu,
321 static void gtk_combo_box_update_requested_width(GtkComboBox *combo_box,
323 static void gtk_combo_box_remeasure (GtkComboBox *combo_box);
325 static void gtk_combo_box_unset_model (GtkComboBox *combo_box);
327 static void gtk_combo_box_size_allocate (GtkWidget *widget,
328 GtkAllocation *allocation);
329 static void gtk_combo_box_forall (GtkContainer *container,
330 gboolean include_internals,
331 GtkCallback callback,
332 gpointer callback_data);
333 static gboolean gtk_combo_box_draw (GtkWidget *widget,
335 static gboolean gtk_combo_box_scroll_event (GtkWidget *widget,
336 GdkEventScroll *event);
337 static void gtk_combo_box_set_active_internal (GtkComboBox *combo_box,
340 static void gtk_combo_box_check_appearance (GtkComboBox *combo_box);
341 static void gtk_combo_box_real_move_active (GtkComboBox *combo_box,
342 GtkScrollType scroll);
343 static void gtk_combo_box_real_popup (GtkComboBox *combo_box);
344 static gboolean gtk_combo_box_real_popdown (GtkComboBox *combo_box);
346 /* listening to the model */
347 static void gtk_combo_box_model_row_inserted (GtkTreeModel *model,
351 static void gtk_combo_box_model_row_deleted (GtkTreeModel *model,
354 static void gtk_combo_box_model_rows_reordered (GtkTreeModel *model,
359 static void gtk_combo_box_model_row_changed (GtkTreeModel *model,
363 static void gtk_combo_box_model_row_expanded (GtkTreeModel *model,
369 static void gtk_combo_box_list_position (GtkComboBox *combo_box,
374 static void gtk_combo_box_list_setup (GtkComboBox *combo_box);
375 static void gtk_combo_box_list_destroy (GtkComboBox *combo_box);
377 static gboolean gtk_combo_box_list_button_released (GtkWidget *widget,
378 GdkEventButton *event,
380 static gboolean gtk_combo_box_list_key_press (GtkWidget *widget,
383 static gboolean gtk_combo_box_list_enter_notify (GtkWidget *widget,
384 GdkEventCrossing *event,
386 static void gtk_combo_box_list_auto_scroll (GtkComboBox *combo,
389 static gboolean gtk_combo_box_list_scroll_timeout (GtkComboBox *combo);
390 static gboolean gtk_combo_box_list_button_pressed (GtkWidget *widget,
391 GdkEventButton *event,
394 static gboolean gtk_combo_box_list_select_func (GtkTreeSelection *selection,
397 gboolean path_currently_selected,
400 static void gtk_combo_box_list_row_changed (GtkTreeModel *model,
404 static void gtk_combo_box_list_popup_resize (GtkComboBox *combo_box);
407 static void gtk_combo_box_menu_setup (GtkComboBox *combo_box,
408 gboolean add_children);
409 static void gtk_combo_box_menu_fill (GtkComboBox *combo_box);
410 static void gtk_combo_box_menu_fill_level (GtkComboBox *combo_box,
413 static void gtk_combo_box_update_title (GtkComboBox *combo_box);
414 static void gtk_combo_box_menu_destroy (GtkComboBox *combo_box);
416 static void gtk_combo_box_relayout_item (GtkComboBox *combo_box,
420 static void gtk_combo_box_relayout (GtkComboBox *combo_box);
422 static gboolean gtk_combo_box_menu_button_press (GtkWidget *widget,
423 GdkEventButton *event,
425 static void gtk_combo_box_menu_item_activate (GtkWidget *item,
428 static void gtk_combo_box_update_sensitivity (GtkComboBox *combo_box);
429 static void gtk_combo_box_menu_row_inserted (GtkTreeModel *model,
433 static void gtk_combo_box_menu_row_deleted (GtkTreeModel *model,
436 static void gtk_combo_box_menu_rows_reordered (GtkTreeModel *model,
441 static void gtk_combo_box_menu_row_changed (GtkTreeModel *model,
445 static gboolean gtk_combo_box_menu_key_press (GtkWidget *widget,
448 static void gtk_combo_box_menu_popup (GtkComboBox *combo_box,
450 guint32 activate_time);
451 static GtkWidget *gtk_cell_view_menu_item_new (GtkComboBox *combo_box,
456 static void gtk_combo_box_cell_layout_pack_start (GtkCellLayout *layout,
457 GtkCellRenderer *cell,
459 static void gtk_combo_box_cell_layout_pack_end (GtkCellLayout *layout,
460 GtkCellRenderer *cell,
462 static GList *gtk_combo_box_cell_layout_get_cells (GtkCellLayout *layout);
463 static void gtk_combo_box_cell_layout_clear (GtkCellLayout *layout);
464 static void gtk_combo_box_cell_layout_add_attribute (GtkCellLayout *layout,
465 GtkCellRenderer *cell,
466 const gchar *attribute,
468 static void gtk_combo_box_cell_layout_set_cell_data_func (GtkCellLayout *layout,
469 GtkCellRenderer *cell,
470 GtkCellLayoutDataFunc func,
472 GDestroyNotify destroy);
473 static void gtk_combo_box_cell_layout_clear_attributes (GtkCellLayout *layout,
474 GtkCellRenderer *cell);
475 static void gtk_combo_box_cell_layout_reorder (GtkCellLayout *layout,
476 GtkCellRenderer *cell,
478 static gboolean gtk_combo_box_mnemonic_activate (GtkWidget *widget,
479 gboolean group_cycling);
481 static void gtk_combo_box_sync_cells (GtkComboBox *combo_box,
482 GtkCellLayout *cell_layout);
483 static void combo_cell_data_func (GtkCellLayout *cell_layout,
484 GtkCellRenderer *cell,
485 GtkTreeModel *tree_model,
488 static void gtk_combo_box_child_show (GtkWidget *widget,
489 GtkComboBox *combo_box);
490 static void gtk_combo_box_child_hide (GtkWidget *widget,
491 GtkComboBox *combo_box);
493 /* GtkComboBox:has-entry callbacks */
494 static void gtk_combo_box_entry_contents_changed (GtkEntry *entry,
496 static void gtk_combo_box_entry_active_changed (GtkComboBox *combo_box,
500 /* GtkBuildable method implementation */
501 static GtkBuildableIface *parent_buildable_iface;
503 static void gtk_combo_box_buildable_init (GtkBuildableIface *iface);
504 static gboolean gtk_combo_box_buildable_custom_tag_start (GtkBuildable *buildable,
507 const gchar *tagname,
508 GMarkupParser *parser,
510 static void gtk_combo_box_buildable_custom_tag_end (GtkBuildable *buildable,
513 const gchar *tagname,
515 static GObject *gtk_combo_box_buildable_get_internal_child (GtkBuildable *buildable,
517 const gchar *childname);
520 /* GtkCellEditable method implementations */
521 static void gtk_combo_box_start_editing (GtkCellEditable *cell_editable,
524 static void gtk_combo_box_get_preferred_width (GtkWidget *widget,
527 static void gtk_combo_box_get_preferred_height (GtkWidget *widget,
530 static void gtk_combo_box_get_preferred_width_for_height (GtkWidget *widget,
534 static void gtk_combo_box_get_preferred_height_for_width (GtkWidget *widget,
540 G_DEFINE_TYPE_WITH_CODE (GtkComboBox, gtk_combo_box, GTK_TYPE_BIN,
541 G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
542 gtk_combo_box_cell_layout_init)
543 G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_EDITABLE,
544 gtk_combo_box_cell_editable_init)
545 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
546 gtk_combo_box_buildable_init))
551 gtk_combo_box_class_init (GtkComboBoxClass *klass)
553 GObjectClass *object_class;
554 GtkContainerClass *container_class;
555 GtkWidgetClass *widget_class;
556 GtkBindingSet *binding_set;
558 container_class = (GtkContainerClass *)klass;
559 container_class->forall = gtk_combo_box_forall;
560 container_class->add = gtk_combo_box_add;
561 container_class->remove = gtk_combo_box_remove;
563 widget_class = (GtkWidgetClass *)klass;
564 widget_class->size_allocate = gtk_combo_box_size_allocate;
565 widget_class->draw = gtk_combo_box_draw;
566 widget_class->scroll_event = gtk_combo_box_scroll_event;
567 widget_class->mnemonic_activate = gtk_combo_box_mnemonic_activate;
568 widget_class->grab_focus = gtk_combo_box_grab_focus;
569 widget_class->style_updated = gtk_combo_box_style_updated;
570 widget_class->state_changed = gtk_combo_box_state_changed;
571 widget_class->get_preferred_width = gtk_combo_box_get_preferred_width;
572 widget_class->get_preferred_height = gtk_combo_box_get_preferred_height;
573 widget_class->get_preferred_height_for_width = gtk_combo_box_get_preferred_height_for_width;
574 widget_class->get_preferred_width_for_height = gtk_combo_box_get_preferred_width_for_height;
575 widget_class->destroy = gtk_combo_box_destroy;
577 object_class = (GObjectClass *)klass;
578 object_class->constructor = gtk_combo_box_constructor;
579 object_class->dispose = gtk_combo_box_dispose;
580 object_class->finalize = gtk_combo_box_finalize;
581 object_class->set_property = gtk_combo_box_set_property;
582 object_class->get_property = gtk_combo_box_get_property;
586 * GtkComboBox::changed:
587 * @widget: the object which received the signal
589 * The changed signal is emitted when the active
590 * item is changed. The can be due to the user selecting
591 * a different item from the list, or due to a
592 * call to gtk_combo_box_set_active_iter().
593 * It will also be emitted while typing into the entry of a combo box
598 combo_box_signals[CHANGED] =
599 g_signal_new (I_("changed"),
600 G_OBJECT_CLASS_TYPE (klass),
602 G_STRUCT_OFFSET (GtkComboBoxClass, changed),
604 g_cclosure_marshal_VOID__VOID,
607 * GtkComboBox::move-active:
608 * @widget: the object that received the signal
609 * @scroll_type: a #GtkScrollType
611 * The ::move-active signal is a
612 * <link linkend="keybinding-signals">keybinding signal</link>
613 * which gets emitted to move the active selection.
617 combo_box_signals[MOVE_ACTIVE] =
618 g_signal_new_class_handler (I_("move-active"),
619 G_OBJECT_CLASS_TYPE (klass),
620 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
621 G_CALLBACK (gtk_combo_box_real_move_active),
623 g_cclosure_marshal_VOID__ENUM,
625 GTK_TYPE_SCROLL_TYPE);
628 * GtkComboBox::popup:
629 * @widget: the object that received the signal
631 * The ::popup signal is a
632 * <link linkend="keybinding-signals">keybinding signal</link>
633 * which gets emitted to popup the combo box list.
635 * The default binding for this signal is Alt+Down.
639 combo_box_signals[POPUP] =
640 g_signal_new_class_handler (I_("popup"),
641 G_OBJECT_CLASS_TYPE (klass),
642 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
643 G_CALLBACK (gtk_combo_box_real_popup),
645 g_cclosure_marshal_VOID__VOID,
648 * GtkComboBox::popdown:
649 * @button: the object which received the signal
651 * The ::popdown signal is a
652 * <link linkend="keybinding-signals">keybinding signal</link>
653 * which gets emitted to popdown the combo box list.
655 * The default bindings for this signal are Alt+Up and Escape.
659 combo_box_signals[POPDOWN] =
660 g_signal_new_class_handler (I_("popdown"),
661 G_OBJECT_CLASS_TYPE (klass),
662 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
663 G_CALLBACK (gtk_combo_box_real_popdown),
665 _gtk_marshal_BOOLEAN__VOID,
669 binding_set = gtk_binding_set_by_class (widget_class);
671 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Down, GDK_MOD1_MASK,
673 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Down, GDK_MOD1_MASK,
676 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Up, GDK_MOD1_MASK,
678 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Up, GDK_MOD1_MASK,
680 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0,
683 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Up, 0,
685 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_UP);
686 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Up, 0,
688 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_UP);
689 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Up, 0,
691 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_UP);
692 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Page_Up, 0,
694 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_UP);
695 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Home, 0,
697 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_START);
698 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Home, 0,
700 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_START);
702 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Down, 0,
704 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_DOWN);
705 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Down, 0,
707 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_DOWN);
708 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Down, 0,
710 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_DOWN);
711 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Page_Down, 0,
713 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_DOWN);
714 gtk_binding_entry_add_signal (binding_set, GDK_KEY_End, 0,
716 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_END);
717 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_End, 0,
719 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_END);
722 g_object_class_override_property (object_class,
723 PROP_EDITING_CANCELED,
729 * The model from which the combo box takes the values shown
734 g_object_class_install_property (object_class,
736 g_param_spec_object ("model",
737 P_("ComboBox model"),
738 P_("The model for the combo box"),
740 GTK_PARAM_READWRITE));
743 * GtkComboBox:wrap-width:
745 * If wrap-width is set to a positive value, the list will be
746 * displayed in multiple columns, the number of columns is
747 * determined by wrap-width.
751 g_object_class_install_property (object_class,
753 g_param_spec_int ("wrap-width",
755 P_("Wrap width for laying out the items in a grid"),
759 GTK_PARAM_READWRITE));
763 * GtkComboBox:row-span-column:
765 * If this is set to a non-negative value, it must be the index of a column
766 * of type %G_TYPE_INT in the model.
768 * The values of that column are used to determine how many rows a value in
769 * the list will span. Therefore, the values in the model column pointed to
770 * by this property must be greater than zero and not larger than wrap-width.
774 g_object_class_install_property (object_class,
775 PROP_ROW_SPAN_COLUMN,
776 g_param_spec_int ("row-span-column",
777 P_("Row span column"),
778 P_("TreeModel column containing the row span values"),
782 GTK_PARAM_READWRITE));
786 * GtkComboBox:column-span-column:
788 * If this is set to a non-negative value, it must be the index of a column
789 * of type %G_TYPE_INT in the model.
791 * The values of that column are used to determine how many columns a value
792 * in the list will span.
796 g_object_class_install_property (object_class,
797 PROP_COLUMN_SPAN_COLUMN,
798 g_param_spec_int ("column-span-column",
799 P_("Column span column"),
800 P_("TreeModel column containing the column span values"),
804 GTK_PARAM_READWRITE));
808 * GtkComboBox:active:
810 * The item which is currently active. If the model is a non-flat treemodel,
811 * and the active item is not an immediate child of the root of the tree,
812 * this property has the value
813 * <literal>gtk_tree_path_get_indices (path)[0]</literal>,
814 * where <literal>path</literal> is the #GtkTreePath of the active item.
818 g_object_class_install_property (object_class,
820 g_param_spec_int ("active",
822 P_("The item which is currently active"),
826 GTK_PARAM_READWRITE));
829 * GtkComboBox:add-tearoffs:
831 * The add-tearoffs property controls whether generated menus
832 * have tearoff menu items.
834 * Note that this only affects menu style combo boxes.
838 g_object_class_install_property (object_class,
840 g_param_spec_boolean ("add-tearoffs",
841 P_("Add tearoffs to menus"),
842 P_("Whether dropdowns should have a tearoff menu item"),
844 GTK_PARAM_READWRITE));
847 * GtkComboBox:has-frame:
849 * The has-frame property controls whether a frame
850 * is drawn around the entry.
854 g_object_class_install_property (object_class,
856 g_param_spec_boolean ("has-frame",
858 P_("Whether the combo box draws a frame around the child"),
860 GTK_PARAM_READWRITE));
862 g_object_class_install_property (object_class,
864 g_param_spec_boolean ("focus-on-click",
865 P_("Focus on click"),
866 P_("Whether the combo box grabs focus when it is clicked with the mouse"),
868 GTK_PARAM_READWRITE));
871 * GtkComboBox:tearoff-title:
873 * A title that may be displayed by the window manager
874 * when the popup is torn-off.
878 g_object_class_install_property (object_class,
880 g_param_spec_string ("tearoff-title",
882 P_("A title that may be displayed by the window manager when the popup is torn-off"),
884 GTK_PARAM_READWRITE));
888 * GtkComboBox:popup-shown:
890 * Whether the combo boxes dropdown is popped up.
891 * Note that this property is mainly useful, because
892 * it allows you to connect to notify::popup-shown.
896 g_object_class_install_property (object_class,
898 g_param_spec_boolean ("popup-shown",
900 P_("Whether the combo's dropdown is shown"),
902 GTK_PARAM_READABLE));
906 * GtkComboBox:button-sensitivity:
908 * Whether the dropdown button is sensitive when
909 * the model is empty.
913 g_object_class_install_property (object_class,
914 PROP_BUTTON_SENSITIVITY,
915 g_param_spec_enum ("button-sensitivity",
916 P_("Button Sensitivity"),
917 P_("Whether the dropdown button is sensitive when the model is empty"),
918 GTK_TYPE_SENSITIVITY_TYPE,
919 GTK_SENSITIVITY_AUTO,
920 GTK_PARAM_READWRITE));
923 * GtkComboBox:has-entry:
925 * Whether the combo box has an entry.
929 g_object_class_install_property (object_class,
931 g_param_spec_boolean ("has-entry",
933 P_("Whether combo box has an entry"),
935 GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
938 * GtkComboBox:entry-text-column:
940 * The column in the combo box's model to associate with strings from the entry
941 * if the combo was created with #GtkComboBox:has-entry = %TRUE.
945 g_object_class_install_property (object_class,
946 PROP_ENTRY_TEXT_COLUMN,
947 g_param_spec_int ("entry-text-column",
948 P_("Entry Text Column"),
949 P_("The column in the combo box's model to associate "
950 "with strings from the entry if the combo was "
951 "created with #GtkComboBox:has-entry = %TRUE"),
953 GTK_PARAM_READWRITE));
956 * GtkComboBox:id-column:
958 * The column in the combo box's model that provides string
959 * IDs for the values in the model, if != -1.
963 g_object_class_install_property (object_class,
965 g_param_spec_int ("id-column",
967 P_("The column in the combo box's model that provides "
968 "string IDs for the values in the model"),
970 GTK_PARAM_READWRITE));
973 * GtkComboBox:active-id:
975 * The value of the ID column of the active row.
979 g_object_class_install_property (object_class,
981 g_param_spec_string ("active-id",
983 P_("The value of the id column "
984 "for the active row"),
985 NULL, GTK_PARAM_READWRITE));
988 * GtkComboBox:popup-fixed-width:
990 * Whether the popup's width should be a fixed width matching the
991 * allocated width of the combo box.
995 g_object_class_install_property (object_class,
996 PROP_POPUP_FIXED_WIDTH,
997 g_param_spec_boolean ("popup-fixed-width",
998 P_("Popup Fixed Width"),
999 P_("Whether the popup's width should be a "
1000 "fixed width matching the allocated width "
1001 "of the combo box"),
1003 GTK_PARAM_READWRITE));
1005 gtk_widget_class_install_style_property (widget_class,
1006 g_param_spec_boolean ("appears-as-list",
1007 P_("Appears as list"),
1008 P_("Whether dropdowns should look like lists rather than menus"),
1010 GTK_PARAM_READABLE));
1013 * GtkComboBox:arrow-size:
1015 * Sets the minimum size of the arrow in the combo box. Note
1016 * that the arrow size is coupled to the font size, so in case
1017 * a larger font is used, the arrow will be larger than set
1022 gtk_widget_class_install_style_property (widget_class,
1023 g_param_spec_int ("arrow-size",
1025 P_("The minimum size of the arrow in the combo box"),
1029 GTK_PARAM_READABLE));
1032 * GtkComboBox:shadow-type:
1034 * Which kind of shadow to draw around the combo box.
1038 gtk_widget_class_install_style_property (widget_class,
1039 g_param_spec_enum ("shadow-type",
1041 P_("Which kind of shadow to draw around the combo box"),
1042 GTK_TYPE_SHADOW_TYPE,
1044 GTK_PARAM_READABLE));
1046 g_type_class_add_private (object_class, sizeof (GtkComboBoxPrivate));
1050 gtk_combo_box_buildable_init (GtkBuildableIface *iface)
1052 parent_buildable_iface = g_type_interface_peek_parent (iface);
1053 iface->add_child = _gtk_cell_layout_buildable_add_child;
1054 iface->custom_tag_start = gtk_combo_box_buildable_custom_tag_start;
1055 iface->custom_tag_end = gtk_combo_box_buildable_custom_tag_end;
1056 iface->get_internal_child = gtk_combo_box_buildable_get_internal_child;
1060 gtk_combo_box_cell_layout_init (GtkCellLayoutIface *iface)
1062 iface->pack_start = gtk_combo_box_cell_layout_pack_start;
1063 iface->pack_end = gtk_combo_box_cell_layout_pack_end;
1064 iface->get_cells = gtk_combo_box_cell_layout_get_cells;
1065 iface->clear = gtk_combo_box_cell_layout_clear;
1066 iface->add_attribute = gtk_combo_box_cell_layout_add_attribute;
1067 iface->set_cell_data_func = gtk_combo_box_cell_layout_set_cell_data_func;
1068 iface->clear_attributes = gtk_combo_box_cell_layout_clear_attributes;
1069 iface->reorder = gtk_combo_box_cell_layout_reorder;
1073 gtk_combo_box_cell_editable_init (GtkCellEditableIface *iface)
1075 iface->start_editing = gtk_combo_box_start_editing;
1079 gtk_combo_box_init (GtkComboBox *combo_box)
1081 GtkComboBoxPrivate *priv;
1082 GtkStyleContext *context;
1084 combo_box->priv = G_TYPE_INSTANCE_GET_PRIVATE (combo_box,
1086 GtkComboBoxPrivate);
1087 priv = combo_box->priv;
1089 priv->cell_view = gtk_cell_view_new ();
1090 gtk_widget_set_parent (priv->cell_view, GTK_WIDGET (combo_box));
1091 _gtk_bin_set_child (GTK_BIN (combo_box), priv->cell_view);
1092 gtk_widget_show (priv->cell_view);
1094 priv->minimum_width = 0;
1095 priv->natural_width = 0;
1097 priv->wrap_width = 0;
1100 priv->active_row = NULL;
1101 priv->col_column = -1;
1102 priv->row_column = -1;
1104 priv->popup_shown = FALSE;
1105 priv->add_tearoffs = FALSE;
1106 priv->has_frame = TRUE;
1107 priv->is_cell_renderer = FALSE;
1108 priv->editing_canceled = FALSE;
1109 priv->auto_scroll = FALSE;
1110 priv->focus_on_click = TRUE;
1111 priv->button_sensitivity = GTK_SENSITIVITY_AUTO;
1112 priv->has_entry = FALSE;
1113 priv->popup_fixed_width = TRUE;
1115 priv->text_column = -1;
1116 priv->text_renderer = NULL;
1117 priv->id_column = -1;
1119 gtk_combo_box_check_appearance (combo_box);
1121 context = gtk_widget_get_style_context (GTK_WIDGET (combo_box));
1122 gtk_style_context_add_class (context, GTK_STYLE_CLASS_BUTTON);
1126 gtk_combo_box_set_property (GObject *object,
1128 const GValue *value,
1131 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
1136 gtk_combo_box_set_model (combo_box, g_value_get_object (value));
1139 case PROP_WRAP_WIDTH:
1140 gtk_combo_box_set_wrap_width (combo_box, g_value_get_int (value));
1143 case PROP_ROW_SPAN_COLUMN:
1144 gtk_combo_box_set_row_span_column (combo_box, g_value_get_int (value));
1147 case PROP_COLUMN_SPAN_COLUMN:
1148 gtk_combo_box_set_column_span_column (combo_box, g_value_get_int (value));
1152 gtk_combo_box_set_active (combo_box, g_value_get_int (value));
1155 case PROP_ADD_TEAROFFS:
1156 gtk_combo_box_set_add_tearoffs (combo_box, g_value_get_boolean (value));
1159 case PROP_HAS_FRAME:
1160 combo_box->priv->has_frame = g_value_get_boolean (value);
1162 if (combo_box->priv->has_entry)
1166 child = gtk_bin_get_child (GTK_BIN (combo_box));
1168 gtk_entry_set_has_frame (GTK_ENTRY (child),
1169 combo_box->priv->has_frame);
1174 case PROP_FOCUS_ON_CLICK:
1175 gtk_combo_box_set_focus_on_click (combo_box,
1176 g_value_get_boolean (value));
1179 case PROP_TEAROFF_TITLE:
1180 gtk_combo_box_set_title (combo_box, g_value_get_string (value));
1183 case PROP_POPUP_SHOWN:
1184 if (g_value_get_boolean (value))
1185 gtk_combo_box_popup (combo_box);
1187 gtk_combo_box_popdown (combo_box);
1190 case PROP_BUTTON_SENSITIVITY:
1191 gtk_combo_box_set_button_sensitivity (combo_box,
1192 g_value_get_enum (value));
1195 case PROP_POPUP_FIXED_WIDTH:
1196 gtk_combo_box_set_popup_fixed_width (combo_box,
1197 g_value_get_boolean (value));
1200 case PROP_EDITING_CANCELED:
1201 combo_box->priv->editing_canceled = g_value_get_boolean (value);
1204 case PROP_HAS_ENTRY:
1205 combo_box->priv->has_entry = g_value_get_boolean (value);
1208 case PROP_ENTRY_TEXT_COLUMN:
1209 gtk_combo_box_set_entry_text_column (combo_box, g_value_get_int (value));
1212 case PROP_ID_COLUMN:
1213 gtk_combo_box_set_id_column (combo_box, g_value_get_int (value));
1216 case PROP_ACTIVE_ID:
1217 gtk_combo_box_set_active_id (combo_box, g_value_get_string (value));
1221 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1227 gtk_combo_box_get_property (GObject *object,
1232 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
1233 GtkComboBoxPrivate *priv = combo_box->priv;
1238 g_value_set_object (value, combo_box->priv->model);
1241 case PROP_WRAP_WIDTH:
1242 g_value_set_int (value, combo_box->priv->wrap_width);
1245 case PROP_ROW_SPAN_COLUMN:
1246 g_value_set_int (value, combo_box->priv->row_column);
1249 case PROP_COLUMN_SPAN_COLUMN:
1250 g_value_set_int (value, combo_box->priv->col_column);
1254 g_value_set_int (value, gtk_combo_box_get_active (combo_box));
1257 case PROP_ADD_TEAROFFS:
1258 g_value_set_boolean (value, gtk_combo_box_get_add_tearoffs (combo_box));
1261 case PROP_HAS_FRAME:
1262 g_value_set_boolean (value, combo_box->priv->has_frame);
1265 case PROP_FOCUS_ON_CLICK:
1266 g_value_set_boolean (value, combo_box->priv->focus_on_click);
1269 case PROP_TEAROFF_TITLE:
1270 g_value_set_string (value, gtk_combo_box_get_title (combo_box));
1273 case PROP_POPUP_SHOWN:
1274 g_value_set_boolean (value, combo_box->priv->popup_shown);
1277 case PROP_BUTTON_SENSITIVITY:
1278 g_value_set_enum (value, combo_box->priv->button_sensitivity);
1281 case PROP_POPUP_FIXED_WIDTH:
1282 g_value_set_boolean (value, combo_box->priv->popup_fixed_width);
1285 case PROP_EDITING_CANCELED:
1286 g_value_set_boolean (value, priv->editing_canceled);
1289 case PROP_HAS_ENTRY:
1290 g_value_set_boolean (value, priv->has_entry);
1293 case PROP_ENTRY_TEXT_COLUMN:
1294 g_value_set_int (value, priv->text_column);
1297 case PROP_ID_COLUMN:
1298 g_value_set_int (value, priv->id_column);
1301 case PROP_ACTIVE_ID:
1302 g_value_set_string (value, gtk_combo_box_get_active_id (combo_box));
1306 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1312 gtk_combo_box_state_changed (GtkWidget *widget,
1313 GtkStateType previous)
1315 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1316 GtkComboBoxPrivate *priv = combo_box->priv;
1318 if (gtk_widget_get_realized (widget))
1320 if (priv->tree_view && priv->cell_view)
1322 GtkStyleContext *context;
1323 GtkStateFlags state;
1326 context = gtk_widget_get_style_context (widget);
1327 state = gtk_widget_get_state_flags (widget);
1329 gtk_style_context_get (context, state,
1330 "background-color", &color,
1333 gtk_cell_view_set_background_rgba (GTK_CELL_VIEW (priv->cell_view), color);
1334 gdk_rgba_free (color);
1338 gtk_widget_queue_draw (widget);
1342 gtk_combo_box_button_state_flags_changed (GtkWidget *widget,
1343 GtkStateFlags previous,
1346 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
1347 GtkComboBoxPrivate *priv = combo_box->priv;
1349 if (gtk_widget_get_realized (widget))
1351 if (!priv->tree_view && priv->cell_view)
1352 gtk_widget_set_state_flags (priv->cell_view,
1353 gtk_widget_get_state_flags (widget),
1357 gtk_widget_queue_draw (widget);
1361 gtk_combo_box_check_appearance (GtkComboBox *combo_box)
1363 GtkComboBoxPrivate *priv = combo_box->priv;
1364 gboolean appears_as_list;
1366 /* if wrap_width > 0, then we are in grid-mode and forced to use
1369 if (priv->wrap_width)
1370 appears_as_list = FALSE;
1372 gtk_widget_style_get (GTK_WIDGET (combo_box),
1373 "appears-as-list", &appears_as_list,
1376 if (appears_as_list)
1378 /* Destroy all the menu mode widgets, if they exist. */
1379 if (GTK_IS_MENU (priv->popup_widget))
1380 gtk_combo_box_menu_destroy (combo_box);
1382 /* Create the list mode widgets, if they don't already exist. */
1383 if (!GTK_IS_TREE_VIEW (priv->tree_view))
1384 gtk_combo_box_list_setup (combo_box);
1388 /* Destroy all the list mode widgets, if they exist. */
1389 if (GTK_IS_TREE_VIEW (priv->tree_view))
1390 gtk_combo_box_list_destroy (combo_box);
1392 /* Create the menu mode widgets, if they don't already exist. */
1393 if (!GTK_IS_MENU (priv->popup_widget))
1394 gtk_combo_box_menu_setup (combo_box, TRUE);
1397 gtk_widget_style_get (GTK_WIDGET (combo_box),
1398 "shadow-type", &priv->shadow_type,
1403 gtk_combo_box_style_updated (GtkWidget *widget)
1405 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1406 GtkComboBoxPrivate *priv = combo_box->priv;
1409 gtk_combo_box_check_appearance (combo_box);
1411 if (priv->tree_view && priv->cell_view)
1413 GtkStyleContext *context;
1416 context = gtk_widget_get_style_context (widget);
1417 gtk_style_context_get (context, 0,
1418 "background-color", &color,
1421 gtk_cell_view_set_background_rgba (GTK_CELL_VIEW (priv->cell_view),
1424 gdk_rgba_free (color);
1427 child = gtk_bin_get_child (GTK_BIN (combo_box));
1428 if (GTK_IS_ENTRY (child))
1429 g_object_set (child, "shadow-type",
1430 GTK_SHADOW_NONE == priv->shadow_type ?
1431 GTK_SHADOW_IN : GTK_SHADOW_NONE, NULL);
1435 gtk_combo_box_button_toggled (GtkWidget *widget,
1438 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
1440 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
1442 if (!combo_box->priv->popup_in_progress)
1443 gtk_combo_box_popup (combo_box);
1446 gtk_combo_box_popdown (combo_box);
1450 gtk_combo_box_add (GtkContainer *container,
1453 GtkComboBox *combo_box = GTK_COMBO_BOX (container);
1454 GtkComboBoxPrivate *priv = combo_box->priv;
1456 if (priv->has_entry && !GTK_IS_ENTRY (widget))
1458 g_warning ("Attempting to add a widget with type %s to a GtkComboBox that needs an entry "
1459 "(need an instance of GtkEntry or of a subclass)",
1460 G_OBJECT_TYPE_NAME (widget));
1464 if (priv->cell_view &&
1465 gtk_widget_get_parent (priv->cell_view))
1467 gtk_widget_unparent (priv->cell_view);
1468 _gtk_bin_set_child (GTK_BIN (container), NULL);
1469 gtk_widget_queue_resize (GTK_WIDGET (container));
1472 gtk_widget_set_parent (widget, GTK_WIDGET (container));
1473 _gtk_bin_set_child (GTK_BIN (container), widget);
1475 if (priv->cell_view &&
1476 widget != priv->cell_view)
1478 /* since the cell_view was unparented, it's gone now */
1479 priv->cell_view = NULL;
1481 if (!priv->tree_view && priv->separator)
1483 gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (priv->separator)),
1485 priv->separator = NULL;
1487 gtk_widget_queue_resize (GTK_WIDGET (container));
1489 else if (priv->cell_view_frame)
1491 gtk_widget_unparent (priv->cell_view_frame);
1492 priv->cell_view_frame = NULL;
1497 if (priv->has_entry)
1499 /* this flag is a hack to tell the entry to fill its allocation.
1501 _gtk_entry_set_is_cell_renderer (GTK_ENTRY (widget), TRUE);
1503 g_signal_connect (widget, "changed",
1504 G_CALLBACK (gtk_combo_box_entry_contents_changed),
1507 gtk_entry_set_has_frame (GTK_ENTRY (widget), priv->has_frame);
1512 gtk_combo_box_remove (GtkContainer *container,
1515 GtkComboBox *combo_box = GTK_COMBO_BOX (container);
1516 GtkComboBoxPrivate *priv = combo_box->priv;
1518 gboolean appears_as_list;
1520 if (priv->has_entry)
1522 GtkWidget *child_widget;
1524 child_widget = gtk_bin_get_child (GTK_BIN (container));
1525 if (widget && widget == child_widget)
1527 g_signal_handlers_disconnect_by_func (widget,
1528 gtk_combo_box_entry_contents_changed,
1530 _gtk_entry_set_is_cell_renderer (GTK_ENTRY (widget), FALSE);
1534 if (widget == priv->cell_view)
1535 priv->cell_view = NULL;
1537 gtk_widget_unparent (widget);
1538 _gtk_bin_set_child (GTK_BIN (container), NULL);
1540 if (gtk_widget_in_destruction (GTK_WIDGET (combo_box)))
1543 gtk_widget_queue_resize (GTK_WIDGET (container));
1545 if (!priv->tree_view)
1546 appears_as_list = FALSE;
1548 appears_as_list = TRUE;
1550 if (appears_as_list)
1551 gtk_combo_box_list_destroy (combo_box);
1552 else if (GTK_IS_MENU (priv->popup_widget))
1554 gtk_combo_box_menu_destroy (combo_box);
1555 gtk_menu_detach (GTK_MENU (priv->popup_widget));
1556 priv->popup_widget = NULL;
1559 if (!priv->cell_view)
1561 priv->cell_view = gtk_cell_view_new ();
1562 gtk_widget_set_parent (priv->cell_view, GTK_WIDGET (container));
1563 _gtk_bin_set_child (GTK_BIN (container), priv->cell_view);
1565 gtk_widget_show (priv->cell_view);
1566 gtk_cell_view_set_model (GTK_CELL_VIEW (priv->cell_view),
1568 gtk_combo_box_sync_cells (combo_box, GTK_CELL_LAYOUT (priv->cell_view));
1572 if (appears_as_list)
1573 gtk_combo_box_list_setup (combo_box);
1575 gtk_combo_box_menu_setup (combo_box, TRUE);
1577 if (gtk_tree_row_reference_valid (priv->active_row))
1579 path = gtk_tree_row_reference_get_path (priv->active_row);
1580 gtk_combo_box_set_active_internal (combo_box, path);
1581 gtk_tree_path_free (path);
1584 gtk_combo_box_set_active_internal (combo_box, NULL);
1587 static ComboCellInfo *
1588 gtk_combo_box_get_cell_info (GtkComboBox *combo_box,
1589 GtkCellRenderer *cell)
1593 for (i = combo_box->priv->cells; i; i = i->next)
1595 ComboCellInfo *info = (ComboCellInfo *)i->data;
1597 if (info && info->cell == cell)
1605 gtk_combo_box_menu_show (GtkWidget *menu,
1608 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1609 GtkComboBoxPrivate *priv = combo_box->priv;
1611 gtk_combo_box_child_show (menu, user_data);
1613 priv->popup_in_progress = TRUE;
1614 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button),
1616 priv->popup_in_progress = FALSE;
1620 gtk_combo_box_menu_hide (GtkWidget *menu,
1623 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1625 gtk_combo_box_child_hide (menu,user_data);
1627 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
1632 gtk_combo_box_detacher (GtkWidget *widget,
1635 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1636 GtkComboBoxPrivate *priv = combo_box->priv;
1638 g_return_if_fail (priv->popup_widget == (GtkWidget *) menu);
1640 g_signal_handlers_disconnect_by_func (menu->toplevel,
1641 gtk_combo_box_menu_show,
1643 g_signal_handlers_disconnect_by_func (menu->toplevel,
1644 gtk_combo_box_menu_hide,
1647 priv->popup_widget = NULL;
1651 gtk_combo_box_set_popup_widget (GtkComboBox *combo_box,
1654 GtkComboBoxPrivate *priv = combo_box->priv;
1656 if (GTK_IS_MENU (priv->popup_widget))
1658 gtk_menu_detach (GTK_MENU (priv->popup_widget));
1659 priv->popup_widget = NULL;
1661 else if (priv->popup_widget)
1663 gtk_container_remove (GTK_CONTAINER (priv->scrolled_window),
1664 priv->popup_widget);
1665 g_object_unref (priv->popup_widget);
1666 priv->popup_widget = NULL;
1669 if (GTK_IS_MENU (popup))
1671 if (priv->popup_window)
1673 gtk_widget_destroy (priv->popup_window);
1674 priv->popup_window = NULL;
1677 priv->popup_widget = popup;
1680 * Note that we connect to show/hide on the toplevel, not the
1681 * menu itself, since the menu is not shown/hidden when it is
1682 * popped up while torn-off.
1684 g_signal_connect (GTK_MENU (popup)->toplevel, "show",
1685 G_CALLBACK (gtk_combo_box_menu_show), combo_box);
1686 g_signal_connect (GTK_MENU (popup)->toplevel, "hide",
1687 G_CALLBACK (gtk_combo_box_menu_hide), combo_box);
1689 gtk_menu_attach_to_widget (GTK_MENU (popup),
1690 GTK_WIDGET (combo_box),
1691 gtk_combo_box_detacher);
1695 if (!priv->popup_window)
1697 GtkWidget *toplevel;
1699 priv->popup_window = gtk_window_new (GTK_WINDOW_POPUP);
1700 gtk_widget_set_name (priv->popup_window, "gtk-combobox-popup-window");
1702 gtk_window_set_type_hint (GTK_WINDOW (priv->popup_window),
1703 GDK_WINDOW_TYPE_HINT_COMBO);
1705 g_signal_connect (GTK_WINDOW (priv->popup_window),"show",
1706 G_CALLBACK (gtk_combo_box_child_show),
1708 g_signal_connect (GTK_WINDOW (priv->popup_window),"hide",
1709 G_CALLBACK (gtk_combo_box_child_hide),
1712 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (combo_box));
1713 if (GTK_IS_WINDOW (toplevel))
1715 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
1716 GTK_WINDOW (priv->popup_window));
1717 gtk_window_set_transient_for (GTK_WINDOW (priv->popup_window),
1718 GTK_WINDOW (toplevel));
1721 gtk_window_set_resizable (GTK_WINDOW (priv->popup_window), FALSE);
1722 gtk_window_set_screen (GTK_WINDOW (priv->popup_window),
1723 gtk_widget_get_screen (GTK_WIDGET (combo_box)));
1725 priv->scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1727 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
1730 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->scrolled_window),
1733 gtk_widget_show (priv->scrolled_window);
1735 gtk_container_add (GTK_CONTAINER (priv->popup_window),
1736 priv->scrolled_window);
1739 gtk_container_add (GTK_CONTAINER (priv->scrolled_window),
1742 gtk_widget_show (popup);
1743 g_object_ref (popup);
1744 priv->popup_widget = popup;
1749 get_widget_border (GtkWidget *widget,
1752 GtkStyleContext *context;
1753 GtkBorder *border_width;
1755 context = gtk_widget_get_style_context (widget);
1757 gtk_style_context_get (context,
1758 gtk_widget_get_state_flags (widget),
1759 "border-width", &border_width,
1762 *border = *border_width;
1763 gtk_border_free (border_width);
1767 gtk_combo_box_menu_position_below (GtkMenu *menu,
1773 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1774 GtkAllocation child_allocation;
1780 GdkRectangle monitor;
1783 /* FIXME: is using the size request here broken? */
1784 child = gtk_bin_get_child (GTK_BIN (combo_box));
1788 gtk_widget_get_allocation (child, &child_allocation);
1790 if (!gtk_widget_get_has_window (child))
1792 sx += child_allocation.x;
1793 sy += child_allocation.y;
1796 gdk_window_get_root_coords (gtk_widget_get_window (child),
1798 get_widget_border (GTK_WIDGET (combo_box), &border);
1801 if (combo_box->priv->popup_fixed_width)
1802 gtk_widget_get_preferred_size (GTK_WIDGET (menu), &req, NULL);
1804 gtk_widget_get_preferred_size (GTK_WIDGET (menu), NULL, &req);
1806 if (gtk_widget_get_direction (GTK_WIDGET (combo_box)) == GTK_TEXT_DIR_LTR)
1809 *x = sx + child_allocation.width - req.width;
1812 screen = gtk_widget_get_screen (GTK_WIDGET (combo_box));
1813 monitor_num = gdk_screen_get_monitor_at_window (screen,
1814 gtk_widget_get_window (GTK_WIDGET (combo_box)));
1815 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1819 else if (*x + req.width > monitor.x + monitor.width)
1820 *x = monitor.x + monitor.width - req.width;
1822 if (monitor.y + monitor.height - *y - child_allocation.height >= req.height)
1823 *y += child_allocation.height;
1824 else if (*y - monitor.y >= req.height)
1826 else if (monitor.y + monitor.height - *y - child_allocation.height > *y - monitor.y)
1827 *y += child_allocation.height;
1835 gtk_combo_box_menu_position_over (GtkMenu *menu,
1841 GtkComboBox *combo_box;
1845 GtkAllocation allocation;
1846 GtkAllocation child_allocation;
1853 combo_box = GTK_COMBO_BOX (user_data);
1854 widget = GTK_WIDGET (combo_box);
1856 active = gtk_menu_get_active (GTK_MENU (combo_box->priv->popup_widget));
1858 gtk_widget_get_allocation (widget, &allocation);
1860 menu_xpos = allocation.x;
1861 menu_ypos = allocation.y + allocation.height / 2 - 2;
1863 if (combo_box->priv->popup_fixed_width)
1864 gtk_widget_get_preferred_width (GTK_WIDGET (menu), &menu_width, NULL);
1866 gtk_widget_get_preferred_width (GTK_WIDGET (menu), NULL, &menu_width);
1870 gtk_widget_get_allocation (active, &child_allocation);
1871 menu_ypos -= child_allocation.height / 2;
1874 children = GTK_MENU_SHELL (combo_box->priv->popup_widget)->children;
1877 child = children->data;
1879 if (active == child)
1882 if (gtk_widget_get_visible (child))
1884 gtk_widget_get_allocation (child, &child_allocation);
1886 menu_ypos -= child_allocation.height;
1889 children = children->next;
1892 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
1893 menu_xpos = menu_xpos + allocation.width - menu_width;
1895 gdk_window_get_root_coords (gtk_widget_get_window (widget),
1896 menu_xpos, menu_ypos,
1897 &menu_xpos, &menu_ypos);
1899 /* Clamp the position on screen */
1900 screen_width = gdk_screen_get_width (gtk_widget_get_screen (widget));
1904 else if ((menu_xpos + menu_width) > screen_width)
1905 menu_xpos -= ((menu_xpos + menu_width) - screen_width);
1914 gtk_combo_box_menu_position (GtkMenu *menu,
1920 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1921 GtkComboBoxPrivate *priv = combo_box->priv;
1922 GtkWidget *menu_item;
1924 if (priv->wrap_width > 0 || priv->cell_view == NULL)
1925 gtk_combo_box_menu_position_below (menu, x, y, push_in, user_data);
1928 /* FIXME handle nested menus better */
1929 menu_item = gtk_menu_get_active (GTK_MENU (priv->popup_widget));
1931 gtk_menu_shell_select_item (GTK_MENU_SHELL (priv->popup_widget),
1934 gtk_combo_box_menu_position_over (menu, x, y, push_in, user_data);
1937 if (!gtk_widget_get_visible (GTK_MENU (priv->popup_widget)->toplevel))
1938 gtk_window_set_type_hint (GTK_WINDOW (GTK_MENU (priv->popup_widget)->toplevel),
1939 GDK_WINDOW_TYPE_HINT_COMBO);
1943 gtk_combo_box_list_position (GtkComboBox *combo_box,
1949 GtkComboBoxPrivate *priv = combo_box->priv;
1950 GtkAllocation allocation;
1953 GdkRectangle monitor;
1954 GtkRequisition popup_req;
1955 GtkPolicyType hpolicy, vpolicy;
1958 /* under windows, the drop down list is as wide as the combo box itself.
1960 GtkWidget *widget = GTK_WIDGET (combo_box);
1964 gtk_widget_get_allocation (widget, &allocation);
1966 if (!gtk_widget_get_has_window (widget))
1972 window = gtk_widget_get_window (widget);
1974 gdk_window_get_root_coords (gtk_widget_get_window (widget),
1977 *width = allocation.width;
1979 hpolicy = vpolicy = GTK_POLICY_NEVER;
1980 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
1983 /* XXX This set_size_request call is part of the hack outlined below and can
1984 * go away once height-for-width is implemented on treeviews. */
1985 gtk_widget_set_size_request (priv->tree_view, -1, -1);
1987 if (combo_box->priv->popup_fixed_width)
1989 gtk_widget_get_preferred_size (priv->scrolled_window, &popup_req, NULL);
1991 if (popup_req.width > *width)
1993 hpolicy = GTK_POLICY_ALWAYS;
1994 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
2000 gtk_combo_box_remeasure (combo_box);
2002 if (priv->natural_width > *width)
2004 hpolicy = GTK_POLICY_NEVER;
2005 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
2009 /* XXX Currently we set the size-request on the internal treeview to be
2010 * the natural width of the cells, this hack can go away once our
2011 * treeview does height-for-width properly (i.e. just adjust *width
2012 * here to be the natural width request of the scrolled-window).
2014 * I can't tell why the magic number 5 is needed here (i.e. without it
2015 * treeviews are left ellipsizing) , however it this all should be
2016 * removed with height-for-width treeviews.
2018 gtk_widget_set_size_request (priv->tree_view, priv->natural_width + 5, -1);
2019 gtk_widget_get_preferred_size (priv->scrolled_window, NULL, &popup_req);
2021 *width = popup_req.width;
2025 *height = popup_req.height;
2027 screen = gtk_widget_get_screen (GTK_WIDGET (combo_box));
2028 monitor_num = gdk_screen_get_monitor_at_window (screen, window);
2029 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
2031 if (gtk_widget_get_direction (GTK_WIDGET (combo_box)) == GTK_TEXT_DIR_RTL)
2032 *x = *x + allocation.width - *width;
2036 else if (*x + *width > monitor.x + monitor.width)
2037 *x = monitor.x + monitor.width - *width;
2039 if (*y + allocation.height + *height <= monitor.y + monitor.height)
2040 *y += allocation.height;
2041 else if (*y - *height >= monitor.y)
2043 else if (monitor.y + monitor.height - (*y + allocation.height) > *y - monitor.y)
2045 *y += allocation.height;
2046 *height = monitor.y + monitor.height - *y;
2050 *height = *y - monitor.y;
2054 if (popup_req.height > *height)
2056 vpolicy = GTK_POLICY_ALWAYS;
2058 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
2064 cell_view_is_sensitive (GtkCellView *cell_view)
2066 GList *cells, *list;
2069 cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (cell_view));
2072 for (list = cells; list; list = list->next)
2074 g_object_get (list->data, "sensitive", &sensitive, NULL);
2079 g_list_free (cells);
2085 tree_column_row_is_sensitive (GtkComboBox *combo_box,
2088 GtkComboBoxPrivate *priv = combo_box->priv;
2089 GList *cells, *list;
2095 if (priv->row_separator_func)
2097 if (priv->row_separator_func (priv->model, iter,
2098 priv->row_separator_data))
2102 gtk_tree_view_column_cell_set_cell_data (priv->column,
2104 iter, FALSE, FALSE);
2106 cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (priv->column));
2109 for (list = cells; list; list = list->next)
2111 g_object_get (list->data, "sensitive", &sensitive, NULL);
2116 g_list_free (cells);
2122 update_menu_sensitivity (GtkComboBox *combo_box,
2125 GtkComboBoxPrivate *priv = combo_box->priv;
2126 GList *children, *child;
2127 GtkWidget *item, *submenu, *separator;
2128 GtkWidget *cell_view;
2134 children = gtk_container_get_children (GTK_CONTAINER (menu));
2136 for (child = children; child; child = child->next)
2138 item = GTK_WIDGET (child->data);
2139 cell_view = gtk_bin_get_child (GTK_BIN (item));
2141 if (!GTK_IS_CELL_VIEW (cell_view))
2144 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (item));
2145 if (submenu != NULL)
2147 gtk_widget_set_sensitive (item, TRUE);
2148 update_menu_sensitivity (combo_box, submenu);
2152 sensitive = cell_view_is_sensitive (GTK_CELL_VIEW (cell_view));
2154 if (menu != priv->popup_widget && child == children)
2156 separator = GTK_WIDGET (child->next->data);
2157 g_object_set (item, "visible", sensitive, NULL);
2158 g_object_set (separator, "visible", sensitive, NULL);
2161 gtk_widget_set_sensitive (item, sensitive);
2165 g_list_free (children);
2169 gtk_combo_box_menu_popup (GtkComboBox *combo_box,
2171 guint32 activate_time)
2173 GtkComboBoxPrivate *priv = combo_box->priv;
2176 gint width, min_width, nat_width;
2178 update_menu_sensitivity (combo_box, priv->popup_widget);
2181 if (gtk_tree_row_reference_valid (priv->active_row))
2183 path = gtk_tree_row_reference_get_path (priv->active_row);
2184 active_item = gtk_tree_path_get_indices (path)[0];
2185 gtk_tree_path_free (path);
2187 if (priv->add_tearoffs)
2191 /* FIXME handle nested menus better */
2192 gtk_menu_set_active (GTK_MENU (priv->popup_widget), active_item);
2194 if (priv->wrap_width == 0)
2196 GtkAllocation allocation;
2198 gtk_widget_get_allocation (GTK_WIDGET (combo_box), &allocation);
2199 width = allocation.width;
2200 gtk_widget_set_size_request (priv->popup_widget, -1, -1);
2201 gtk_widget_get_preferred_width (priv->popup_widget, &min_width, &nat_width);
2203 if (combo_box->priv->popup_fixed_width)
2204 width = MAX (width, min_width);
2206 width = MAX (width, nat_width);
2208 gtk_widget_set_size_request (priv->popup_widget, width, -1);
2211 gtk_menu_popup (GTK_MENU (priv->popup_widget),
2213 gtk_combo_box_menu_position, combo_box,
2214 button, activate_time);
2218 popup_grab_on_window (GdkWindow *window,
2219 GdkDevice *keyboard,
2221 guint32 activate_time)
2224 gdk_device_grab (keyboard, window,
2225 GDK_OWNERSHIP_WINDOW, TRUE,
2226 GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
2227 NULL, activate_time) != GDK_GRAB_SUCCESS)
2231 gdk_device_grab (pointer, window,
2232 GDK_OWNERSHIP_WINDOW, TRUE,
2233 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
2234 GDK_POINTER_MOTION_MASK,
2235 NULL, activate_time) != GDK_GRAB_SUCCESS)
2238 gdk_device_ungrab (keyboard, activate_time);
2247 * gtk_combo_box_popup:
2248 * @combo_box: a #GtkComboBox
2250 * Pops up the menu or dropdown list of @combo_box.
2252 * This function is mostly intended for use by accessibility technologies;
2253 * applications should have little use for it.
2258 gtk_combo_box_popup (GtkComboBox *combo_box)
2260 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2262 g_signal_emit (combo_box, combo_box_signals[POPUP], 0);
2266 * gtk_combo_box_popup_for_device:
2267 * @combo_box: a #GtkComboBox
2268 * @device: a #GdkDevice
2270 * Pops up the menu or dropdown list of @combo_box, the popup window
2271 * will be grabbed so only @device and its associated pointer/keyboard
2272 * are the only #GdkDevice<!-- -->s able to send events to it.
2277 gtk_combo_box_popup_for_device (GtkComboBox *combo_box,
2280 GtkComboBoxPrivate *priv = combo_box->priv;
2281 gint x, y, width, height;
2282 GtkTreePath *path = NULL, *ppath;
2283 GtkWidget *toplevel;
2284 GdkDevice *keyboard, *pointer;
2287 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2288 g_return_if_fail (GDK_IS_DEVICE (device));
2290 if (!gtk_widget_get_realized (GTK_WIDGET (combo_box)))
2293 if (gtk_widget_get_mapped (priv->popup_widget))
2296 if (priv->grab_pointer && priv->grab_keyboard)
2299 time = gtk_get_current_event_time ();
2301 if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
2304 pointer = gdk_device_get_associated_device (device);
2309 keyboard = gdk_device_get_associated_device (device);
2312 if (GTK_IS_MENU (priv->popup_widget))
2314 gtk_combo_box_menu_popup (combo_box,
2315 priv->activate_button,
2316 priv->activate_time);
2320 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (combo_box));
2321 if (GTK_IS_WINDOW (toplevel))
2322 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
2323 GTK_WINDOW (priv->popup_window));
2325 gtk_widget_show_all (priv->scrolled_window);
2326 gtk_combo_box_list_position (combo_box, &x, &y, &width, &height);
2328 gtk_widget_set_size_request (priv->popup_window, width, height);
2329 gtk_window_move (GTK_WINDOW (priv->popup_window), x, y);
2331 if (gtk_tree_row_reference_valid (priv->active_row))
2333 path = gtk_tree_row_reference_get_path (priv->active_row);
2334 ppath = gtk_tree_path_copy (path);
2335 if (gtk_tree_path_up (ppath))
2336 gtk_tree_view_expand_to_path (GTK_TREE_VIEW (priv->tree_view),
2338 gtk_tree_path_free (ppath);
2340 gtk_tree_view_set_hover_expand (GTK_TREE_VIEW (priv->tree_view),
2344 gtk_widget_show (priv->popup_window);
2348 gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->tree_view),
2350 gtk_tree_path_free (path);
2353 gtk_widget_grab_focus (priv->popup_window);
2354 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button),
2357 if (!gtk_widget_has_focus (priv->tree_view))
2358 gtk_widget_grab_focus (priv->tree_view);
2360 if (!popup_grab_on_window (gtk_widget_get_window (priv->popup_window),
2361 keyboard, pointer, time))
2363 gtk_widget_hide (priv->popup_window);
2367 gtk_device_grab_add (priv->popup_window, pointer, TRUE);
2368 priv->grab_pointer = pointer;
2369 priv->grab_keyboard = keyboard;
2373 gtk_combo_box_real_popup (GtkComboBox *combo_box)
2377 device = gtk_get_current_event_device ();
2381 GdkDeviceManager *device_manager;
2382 GdkDisplay *display;
2385 display = gtk_widget_get_display (GTK_WIDGET (combo_box));
2386 device_manager = gdk_display_get_device_manager (display);
2388 /* No device was set, pick the first master device */
2389 devices = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
2390 device = devices->data;
2391 g_list_free (devices);
2394 gtk_combo_box_popup_for_device (combo_box, device);
2398 gtk_combo_box_real_popdown (GtkComboBox *combo_box)
2400 if (combo_box->priv->popup_shown)
2402 gtk_combo_box_popdown (combo_box);
2410 * gtk_combo_box_popdown:
2411 * @combo_box: a #GtkComboBox
2413 * Hides the menu or dropdown list of @combo_box.
2415 * This function is mostly intended for use by accessibility technologies;
2416 * applications should have little use for it.
2421 gtk_combo_box_popdown (GtkComboBox *combo_box)
2423 GtkComboBoxPrivate *priv = combo_box->priv;
2425 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2427 if (GTK_IS_MENU (priv->popup_widget))
2429 gtk_menu_popdown (GTK_MENU (priv->popup_widget));
2433 if (!gtk_widget_get_realized (GTK_WIDGET (combo_box)))
2436 gtk_device_grab_remove (priv->popup_window, priv->grab_pointer);
2437 gtk_widget_hide (priv->popup_window);
2438 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button),
2441 priv->grab_pointer = NULL;
2442 priv->grab_keyboard = NULL;
2446 gtk_combo_box_update_requested_width (GtkComboBox *combo_box,
2449 GtkComboBoxPrivate *priv = combo_box->priv;
2450 gint padding, min_width, nat_width;
2452 if (priv->cell_view)
2453 gtk_widget_style_get (priv->cell_view,
2454 "focus-line-width", &padding,
2459 /* add some pixels for good measure */
2460 padding += BONUS_PADDING;
2462 if (priv->cell_view)
2463 gtk_cell_view_get_desired_width_of_row (GTK_CELL_VIEW (priv->cell_view),
2464 path, &min_width, &nat_width);
2466 min_width = nat_width = 0;
2468 min_width += padding;
2469 nat_width += padding;
2471 if (min_width > priv->minimum_width || nat_width > priv->natural_width)
2473 priv->minimum_width = MAX (priv->minimum_width, min_width);
2474 priv->natural_width = MAX (priv->natural_width, nat_width);
2476 if (priv->cell_view)
2478 gtk_widget_set_size_request (priv->cell_view, min_width, -1);
2479 gtk_widget_queue_resize (priv->cell_view);
2484 #define GTK_COMBO_BOX_SIZE_ALLOCATE_BUTTON \
2485 gtk_widget_get_preferred_size (combo_box->priv->button, \
2489 child.x = allocation->x + border.right; \
2491 child.x = allocation->x + allocation->width - req.width - border.left; \
2493 child.y = allocation->y + border.top; \
2494 child.width = req.width; \
2495 child.height = allocation->height - (border.top + border.bottom); \
2496 child.width = MAX (1, child.width); \
2497 child.height = MAX (1, child.height); \
2499 gtk_widget_size_allocate (combo_box->priv->button, &child);
2502 gtk_combo_box_size_allocate (GtkWidget *widget,
2503 GtkAllocation *allocation)
2505 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
2506 GtkComboBoxPrivate *priv = combo_box->priv;
2507 GtkWidget *child_widget;
2508 gint focus_width, focus_pad;
2509 GtkAllocation child;
2512 gboolean is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
2514 gtk_widget_set_allocation (widget, allocation);
2515 child_widget = gtk_bin_get_child (GTK_BIN (widget));
2516 get_widget_border (widget, &border);
2518 gtk_widget_style_get (widget,
2519 "focus-line-width", &focus_width,
2520 "focus-padding", &focus_pad,
2523 if (!priv->tree_view)
2525 if (priv->cell_view)
2527 GtkBorder button_border;
2532 allocation->x += border.left;
2533 allocation->y += border.top;
2534 allocation->width -= border.left + border.right;
2535 allocation->height -= border.top + border.bottom;
2537 gtk_widget_size_allocate (priv->button, allocation);
2539 /* set some things ready */
2540 border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->button));
2541 get_widget_border (priv->button, &button_border);
2543 child.x = allocation->x;
2544 child.y = allocation->y;
2545 width = allocation->width;
2546 child.height = allocation->height;
2548 if (!priv->is_cell_renderer)
2550 child.x += border_width + button_border.left + focus_width + focus_pad;
2551 child.y += border_width + button_border.top + focus_width + focus_pad;
2552 width -= (2 * (border_width + focus_width + focus_pad)) +
2553 button_border.left + button_border.right;
2554 child.height -= (2 * (border_width + focus_width + focus_pad)) +
2555 button_border.top + button_border.bottom;
2559 /* handle the children */
2560 gtk_widget_get_preferred_size (priv->arrow, &req, NULL);
2561 child.width = req.width;
2563 child.x += width - req.width;
2564 child.width = MAX (1, child.width);
2565 child.height = MAX (1, child.height);
2566 gtk_widget_size_allocate (priv->arrow, &child);
2568 child.x += req.width;
2569 gtk_widget_get_preferred_size (priv->separator, &req, NULL);
2570 child.width = req.width;
2572 child.x -= req.width;
2573 child.width = MAX (1, child.width);
2574 child.height = MAX (1, child.height);
2575 gtk_widget_size_allocate (priv->separator, &child);
2579 child.x += req.width;
2580 child.width = allocation->x + allocation->width
2581 - (border_width + button_border.right + focus_width + focus_pad)
2586 child.width = child.x;
2587 child.x = allocation->x
2588 + border_width + button_border.left + focus_width + focus_pad;
2589 child.width -= child.x;
2592 if (gtk_widget_get_visible (priv->popup_widget))
2594 gint width, menu_width;
2596 if (priv->wrap_width == 0)
2598 GtkAllocation combo_box_allocation;
2600 gtk_widget_get_allocation (GTK_WIDGET (combo_box), &combo_box_allocation);
2601 width = combo_box_allocation.width;
2602 gtk_widget_set_size_request (priv->popup_widget, -1, -1);
2604 if (combo_box->priv->popup_fixed_width)
2605 gtk_widget_get_preferred_width (priv->popup_widget, &menu_width, NULL);
2607 gtk_widget_get_preferred_width (priv->popup_widget, NULL, &menu_width);
2609 gtk_widget_set_size_request (priv->popup_widget,
2610 MAX (width, menu_width), -1);
2613 /* reposition the menu after giving it a new width */
2614 gtk_menu_reposition (GTK_MENU (priv->popup_widget));
2617 child.width = MAX (1, child.width);
2618 child.height = MAX (1, child.height);
2619 gtk_widget_size_allocate (child_widget, &child);
2623 GTK_COMBO_BOX_SIZE_ALLOCATE_BUTTON
2626 child.x = allocation->x + req.width + border.right;
2628 child.x = allocation->x + border.left;
2629 child.y = allocation->y + border.top;
2630 child.width = allocation->width - req.width - (border.left + border.right);
2631 child.width = MAX (1, child.width);
2632 child.height = MAX (1, child.height);
2633 gtk_widget_size_allocate (child_widget, &child);
2639 guint border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
2642 GTK_COMBO_BOX_SIZE_ALLOCATE_BUTTON
2646 child.x = allocation->x + req.width;
2648 child.x = allocation->x;
2650 child.y = allocation->y;
2651 child.width = allocation->width - req.width;
2652 child.height = allocation->height;
2654 if (priv->cell_view_frame)
2656 child.x += border.left + border_width;
2657 child.y += border.top + border_width;
2658 child.width = MAX (1, child.width - (2 * border_width) - (border.left + border.right));
2659 child.height = MAX (1, child.height - (2 * border_width) - (border.top + border.bottom));
2660 gtk_widget_size_allocate (priv->cell_view_frame, &child);
2663 if (priv->has_frame)
2665 GtkBorder frame_border;
2667 border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->cell_view_frame));
2668 get_widget_border (priv->cell_view_frame, &frame_border);
2670 child.x += border_width + frame_border.left;
2671 child.y += border_width + frame_border.right;
2672 child.width -= (2 * border_width) + frame_border.left + frame_border.right;
2673 child.height -= (2 * border_width) + frame_border.top + frame_border.bottom;
2678 child.x += border.left + border_width;
2679 child.y += border.top + border_width;
2680 child.width -= (2 * border_width) - (border.left + border.right);
2681 child.height -= (2 * border_width) - (border.top + border.bottom);
2684 if (gtk_widget_get_visible (priv->popup_window))
2686 gint x, y, width, height;
2687 gtk_combo_box_list_position (combo_box, &x, &y, &width, &height);
2688 gtk_window_move (GTK_WINDOW (priv->popup_window), x, y);
2689 gtk_widget_set_size_request (priv->popup_window, width, height);
2693 child.width = MAX (1, child.width);
2694 child.height = MAX (1, child.height);
2696 gtk_widget_size_allocate (child_widget, &child);
2700 #undef GTK_COMBO_BOX_ALLOCATE_BUTTON
2703 gtk_combo_box_unset_model (GtkComboBox *combo_box)
2705 GtkComboBoxPrivate *priv = combo_box->priv;
2709 g_signal_handler_disconnect (priv->model,
2711 g_signal_handler_disconnect (priv->model,
2713 g_signal_handler_disconnect (priv->model,
2714 priv->reordered_id);
2715 g_signal_handler_disconnect (priv->model,
2720 if (!priv->tree_view)
2722 if (priv->popup_widget)
2723 gtk_container_foreach (GTK_CONTAINER (priv->popup_widget),
2724 (GtkCallback)gtk_widget_destroy, NULL);
2729 g_object_unref (priv->model);
2733 if (priv->active_row)
2735 gtk_tree_row_reference_free (priv->active_row);
2736 priv->active_row = NULL;
2739 if (priv->cell_view)
2740 gtk_cell_view_set_model (GTK_CELL_VIEW (priv->cell_view), NULL);
2744 gtk_combo_box_forall (GtkContainer *container,
2745 gboolean include_internals,
2746 GtkCallback callback,
2747 gpointer callback_data)
2749 GtkComboBox *combo_box = GTK_COMBO_BOX (container);
2750 GtkComboBoxPrivate *priv = combo_box->priv;
2753 if (include_internals)
2756 (* callback) (priv->button, callback_data);
2757 if (priv->cell_view_frame)
2758 (* callback) (priv->cell_view_frame, callback_data);
2761 child = gtk_bin_get_child (GTK_BIN (container));
2763 (* callback) (child, callback_data);
2767 gtk_combo_box_child_show (GtkWidget *widget,
2768 GtkComboBox *combo_box)
2770 GtkComboBoxPrivate *priv = combo_box->priv;
2772 priv->popup_shown = TRUE;
2773 g_object_notify (G_OBJECT (combo_box), "popup-shown");
2777 gtk_combo_box_child_hide (GtkWidget *widget,
2778 GtkComboBox *combo_box)
2780 GtkComboBoxPrivate *priv = combo_box->priv;
2782 priv->popup_shown = FALSE;
2783 g_object_notify (G_OBJECT (combo_box), "popup-shown");
2787 gtk_combo_box_draw (GtkWidget *widget,
2790 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
2791 GtkComboBoxPrivate *priv = combo_box->priv;
2793 if (priv->shadow_type != GTK_SHADOW_NONE)
2795 GtkStyleContext *context;
2796 GtkStateFlags state;
2798 context = gtk_widget_get_style_context (widget);
2799 state = gtk_widget_get_state_flags (widget);
2800 gtk_style_context_set_state (context, state);
2802 gtk_render_background (context, cr, 0, 0,
2803 gtk_widget_get_allocated_width (widget),
2804 gtk_widget_get_allocated_height (widget));
2805 gtk_render_frame (context, cr, 0, 0,
2806 gtk_widget_get_allocated_width (widget),
2807 gtk_widget_get_allocated_height (widget));
2810 gtk_container_propagate_draw (GTK_CONTAINER (widget),
2813 if (priv->tree_view && priv->cell_view_frame)
2815 gtk_container_propagate_draw (GTK_CONTAINER (widget),
2816 priv->cell_view_frame, cr);
2819 gtk_container_propagate_draw (GTK_CONTAINER (widget),
2820 gtk_bin_get_child (GTK_BIN (widget)),
2836 path_visible (GtkTreeView *view,
2842 /* Note that we rely on the fact that collapsed rows don't have nodes
2844 return _gtk_tree_view_find_node (view, path, &tree, &node);
2848 tree_next_func (GtkTreeModel *model,
2853 SearchData *search_data = (SearchData *)data;
2855 if (search_data->found)
2857 if (!tree_column_row_is_sensitive (search_data->combo, iter))
2860 if (search_data->visible &&
2861 !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
2864 search_data->set = TRUE;
2865 search_data->iter = *iter;
2870 if (gtk_tree_path_compare (path, search_data->path) == 0)
2871 search_data->found = TRUE;
2877 tree_next (GtkComboBox *combo,
2878 GtkTreeModel *model,
2883 SearchData search_data;
2885 search_data.combo = combo;
2886 search_data.path = gtk_tree_model_get_path (model, iter);
2887 search_data.visible = visible;
2888 search_data.found = FALSE;
2889 search_data.set = FALSE;
2891 gtk_tree_model_foreach (model, tree_next_func, &search_data);
2893 *next = search_data.iter;
2895 gtk_tree_path_free (search_data.path);
2897 return search_data.set;
2901 tree_prev_func (GtkTreeModel *model,
2906 SearchData *search_data = (SearchData *)data;
2908 if (gtk_tree_path_compare (path, search_data->path) == 0)
2910 search_data->found = TRUE;
2914 if (!tree_column_row_is_sensitive (search_data->combo, iter))
2917 if (search_data->visible &&
2918 !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
2921 search_data->set = TRUE;
2922 search_data->iter = *iter;
2928 tree_prev (GtkComboBox *combo,
2929 GtkTreeModel *model,
2934 SearchData search_data;
2936 search_data.combo = combo;
2937 search_data.path = gtk_tree_model_get_path (model, iter);
2938 search_data.visible = visible;
2939 search_data.found = FALSE;
2940 search_data.set = FALSE;
2942 gtk_tree_model_foreach (model, tree_prev_func, &search_data);
2944 *prev = search_data.iter;
2946 gtk_tree_path_free (search_data.path);
2948 return search_data.set;
2952 tree_last_func (GtkTreeModel *model,
2957 SearchData *search_data = (SearchData *)data;
2959 if (!tree_column_row_is_sensitive (search_data->combo, iter))
2962 /* Note that we rely on the fact that collapsed rows don't have nodes
2964 if (search_data->visible &&
2965 !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
2968 search_data->set = TRUE;
2969 search_data->iter = *iter;
2975 tree_last (GtkComboBox *combo,
2976 GtkTreeModel *model,
2980 SearchData search_data;
2982 search_data.combo = combo;
2983 search_data.visible = visible;
2984 search_data.set = FALSE;
2986 gtk_tree_model_foreach (model, tree_last_func, &search_data);
2988 *last = search_data.iter;
2990 return search_data.set;
2995 tree_first_func (GtkTreeModel *model,
3000 SearchData *search_data = (SearchData *)data;
3002 if (!tree_column_row_is_sensitive (search_data->combo, iter))
3005 if (search_data->visible &&
3006 !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
3009 search_data->set = TRUE;
3010 search_data->iter = *iter;
3016 tree_first (GtkComboBox *combo,
3017 GtkTreeModel *model,
3021 SearchData search_data;
3023 search_data.combo = combo;
3024 search_data.visible = visible;
3025 search_data.set = FALSE;
3027 gtk_tree_model_foreach (model, tree_first_func, &search_data);
3029 *first = search_data.iter;
3031 return search_data.set;
3035 gtk_combo_box_scroll_event (GtkWidget *widget,
3036 GdkEventScroll *event)
3038 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
3041 GtkTreeIter new_iter;
3043 if (!gtk_combo_box_get_active_iter (combo_box, &iter))
3046 if (event->direction == GDK_SCROLL_UP)
3047 found = tree_prev (combo_box, combo_box->priv->model,
3048 &iter, &new_iter, FALSE);
3050 found = tree_next (combo_box, combo_box->priv->model,
3051 &iter, &new_iter, FALSE);
3054 gtk_combo_box_set_active_iter (combo_box, &new_iter);
3064 gtk_combo_box_sync_cells (GtkComboBox *combo_box,
3065 GtkCellLayout *cell_layout)
3067 GtkComboBoxPrivate *priv = combo_box->priv;
3070 for (k = priv->cells; k; k = k->next)
3073 ComboCellInfo *info = (ComboCellInfo *)k->data;
3075 if (info->pack == GTK_PACK_START)
3076 gtk_cell_layout_pack_start (cell_layout,
3077 info->cell, info->expand);
3078 else if (info->pack == GTK_PACK_END)
3079 gtk_cell_layout_pack_end (cell_layout,
3080 info->cell, info->expand);
3082 gtk_cell_layout_set_cell_data_func (cell_layout,
3084 combo_cell_data_func, info, NULL);
3086 for (j = info->attributes; j; j = j->next->next)
3088 gtk_cell_layout_add_attribute (cell_layout,
3091 GPOINTER_TO_INT (j->next->data));
3097 gtk_combo_box_menu_setup (GtkComboBox *combo_box,
3098 gboolean add_children)
3100 GtkComboBoxPrivate *priv = combo_box->priv;
3104 child = gtk_bin_get_child (GTK_BIN (combo_box));
3106 if (priv->cell_view)
3108 priv->button = gtk_toggle_button_new ();
3109 gtk_button_set_focus_on_click (GTK_BUTTON (priv->button),
3110 priv->focus_on_click);
3112 g_signal_connect (priv->button, "toggled",
3113 G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
3114 gtk_widget_set_parent (priv->button,
3115 gtk_widget_get_parent (child));
3117 priv->box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
3118 gtk_container_add (GTK_CONTAINER (priv->button), priv->box);
3120 priv->separator = gtk_separator_new (GTK_ORIENTATION_VERTICAL);
3121 gtk_container_add (GTK_CONTAINER (priv->box), priv->separator);
3123 priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3124 gtk_container_add (GTK_CONTAINER (priv->box), priv->arrow);
3126 gtk_widget_show_all (priv->button);
3130 priv->button = gtk_toggle_button_new ();
3131 gtk_button_set_focus_on_click (GTK_BUTTON (priv->button),
3132 priv->focus_on_click);
3134 g_signal_connect (priv->button, "toggled",
3135 G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
3136 gtk_widget_set_parent (priv->button,
3137 gtk_widget_get_parent (child));
3139 priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3140 gtk_container_add (GTK_CONTAINER (priv->button), priv->arrow);
3141 gtk_widget_show_all (priv->button);
3144 g_signal_connect (priv->button, "button-press-event",
3145 G_CALLBACK (gtk_combo_box_menu_button_press),
3147 g_signal_connect (priv->button, "state-flags-changed",
3148 G_CALLBACK (gtk_combo_box_button_state_flags_changed),
3151 /* create our funky menu */
3152 menu = gtk_menu_new ();
3153 gtk_widget_set_name (menu, "gtk-combobox-popup-menu");
3154 gtk_menu_set_reserve_toggle_size (GTK_MENU (menu), FALSE);
3156 g_signal_connect (menu, "key-press-event",
3157 G_CALLBACK (gtk_combo_box_menu_key_press), combo_box);
3158 gtk_combo_box_set_popup_widget (combo_box, menu);
3162 gtk_combo_box_menu_fill (combo_box);
3164 /* the column is needed in tree_column_row_is_sensitive() */
3165 priv->column = gtk_tree_view_column_new ();
3166 g_object_ref_sink (priv->column);
3167 gtk_combo_box_sync_cells (combo_box, GTK_CELL_LAYOUT (priv->column));
3169 gtk_combo_box_update_title (combo_box);
3170 gtk_combo_box_update_sensitivity (combo_box);
3174 gtk_combo_box_menu_fill (GtkComboBox *combo_box)
3176 GtkComboBoxPrivate *priv = combo_box->priv;
3182 menu = priv->popup_widget;
3184 if (priv->add_tearoffs)
3186 GtkWidget *tearoff = gtk_tearoff_menu_item_new ();
3188 gtk_widget_show (tearoff);
3190 if (priv->wrap_width)
3191 gtk_menu_attach (GTK_MENU (menu), tearoff, 0, priv->wrap_width, 0, 1);
3193 gtk_menu_shell_append (GTK_MENU_SHELL (menu), tearoff);
3196 gtk_combo_box_menu_fill_level (combo_box, menu, NULL);
3200 gtk_cell_view_menu_item_new (GtkComboBox *combo_box,
3201 GtkTreeModel *model,
3204 GtkWidget *cell_view;
3209 cell_view = gtk_cell_view_new ();
3210 item = gtk_menu_item_new ();
3211 gtk_container_add (GTK_CONTAINER (item), cell_view);
3213 gtk_cell_view_set_model (GTK_CELL_VIEW (cell_view), model);
3214 path = gtk_tree_model_get_path (model, iter);
3215 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (cell_view), path);
3216 gtk_tree_path_free (path);
3218 gtk_combo_box_sync_cells (combo_box, GTK_CELL_LAYOUT (cell_view));
3219 gtk_widget_get_preferred_size (cell_view, &req, NULL);
3220 gtk_widget_show (cell_view);
3226 gtk_combo_box_menu_fill_level (GtkComboBox *combo_box,
3228 GtkTreeIter *parent)
3230 GtkComboBoxPrivate *priv = combo_box->priv;
3231 GtkTreeModel *model = priv->model;
3232 GtkWidget *item, *submenu, *subitem, *separator;
3234 gboolean is_separator;
3239 n_children = gtk_tree_model_iter_n_children (model, parent);
3242 for (i = 0; i < n_children; i++)
3244 gtk_tree_model_iter_nth_child (model, &iter, parent, i);
3246 if (priv->row_separator_func)
3247 is_separator = priv->row_separator_func (priv->model, &iter,
3248 priv->row_separator_data);
3250 is_separator = FALSE;
3254 item = gtk_separator_menu_item_new ();
3255 path = gtk_tree_model_get_path (model, &iter);
3256 g_object_set_data_full (G_OBJECT (item),
3257 I_("gtk-combo-box-item-path"),
3258 gtk_tree_row_reference_new (model, path),
3259 (GDestroyNotify)gtk_tree_row_reference_free);
3260 gtk_tree_path_free (path);
3264 item = gtk_cell_view_menu_item_new (combo_box, model, &iter);
3265 if (gtk_tree_model_iter_has_child (model, &iter))
3267 submenu = gtk_menu_new ();
3268 gtk_menu_set_reserve_toggle_size (GTK_MENU (submenu), FALSE);
3269 gtk_widget_show (submenu);
3270 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu);
3272 /* Ugly - since menus can only activate leafs, we have to
3273 * duplicate the item inside the submenu.
3275 subitem = gtk_cell_view_menu_item_new (combo_box, model, &iter);
3276 separator = gtk_separator_menu_item_new ();
3277 gtk_widget_show (subitem);
3278 gtk_widget_show (separator);
3279 g_signal_connect (subitem, "activate",
3280 G_CALLBACK (gtk_combo_box_menu_item_activate),
3282 gtk_menu_shell_append (GTK_MENU_SHELL (submenu), subitem);
3283 gtk_menu_shell_append (GTK_MENU_SHELL (submenu), separator);
3285 gtk_combo_box_menu_fill_level (combo_box, submenu, &iter);
3287 g_signal_connect (item, "activate",
3288 G_CALLBACK (gtk_combo_box_menu_item_activate),
3292 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3293 if (priv->wrap_width && menu == priv->popup_widget)
3294 gtk_combo_box_relayout_item (combo_box, item, &iter, last);
3295 gtk_widget_show (item);
3302 gtk_combo_box_menu_destroy (GtkComboBox *combo_box)
3304 GtkComboBoxPrivate *priv = combo_box->priv;
3306 g_signal_handlers_disconnect_matched (priv->button,
3307 G_SIGNAL_MATCH_DATA,
3309 gtk_combo_box_menu_button_press, NULL);
3310 g_signal_handlers_disconnect_matched (priv->button,
3311 G_SIGNAL_MATCH_DATA,
3313 gtk_combo_box_button_state_flags_changed, combo_box);
3315 /* unparent will remove our latest ref */
3316 gtk_widget_unparent (priv->button);
3319 priv->button = NULL;
3321 priv->separator = NULL;
3323 g_object_unref (priv->column);
3324 priv->column = NULL;
3326 /* changing the popup window will unref the menu and the children */
3334 menu_occupied (GtkMenu *menu,
3338 guint bottom_attach)
3342 for (i = GTK_MENU_SHELL (menu)->children; i; i = i->next)
3346 gtk_container_child_get (GTK_CONTAINER (menu),
3350 "bottom-attach", &b,
3354 /* look if this item intersects with the given coordinates */
3355 if (right_attach > l && left_attach < r && bottom_attach > t && top_attach < b)
3363 gtk_combo_box_relayout_item (GtkComboBox *combo_box,
3368 GtkComboBoxPrivate *priv = combo_box->priv;
3369 gint current_col = 0, current_row = 0;
3370 gint rows = 1, cols = 1;
3371 GtkWidget *menu = priv->popup_widget;
3373 if (!GTK_IS_MENU_SHELL (menu))
3376 if (priv->col_column == -1 &&
3377 priv->row_column == -1 &&
3380 gtk_container_child_get (GTK_CONTAINER (menu),
3382 "right-attach", ¤t_col,
3383 "top-attach", ¤t_row,
3385 if (current_col + cols > priv->wrap_width)
3393 if (priv->col_column != -1)
3394 gtk_tree_model_get (priv->model, iter,
3395 priv->col_column, &cols,
3397 if (priv->row_column != -1)
3398 gtk_tree_model_get (priv->model, iter,
3399 priv->row_column, &rows,
3404 if (current_col + cols > priv->wrap_width)
3410 if (!menu_occupied (GTK_MENU (menu),
3411 current_col, current_col + cols,
3412 current_row, current_row + rows))
3419 /* set attach props */
3420 gtk_menu_attach (GTK_MENU (menu), item,
3421 current_col, current_col + cols,
3422 current_row, current_row + rows);
3426 gtk_combo_box_relayout (GtkComboBox *combo_box)
3431 menu = combo_box->priv->popup_widget;
3433 /* do nothing unless we are in menu style and realized */
3434 if (combo_box->priv->tree_view || !GTK_IS_MENU_SHELL (menu))
3437 list = gtk_container_get_children (GTK_CONTAINER (menu));
3439 for (j = g_list_last (list); j; j = j->prev)
3440 gtk_container_remove (GTK_CONTAINER (menu), j->data);
3442 gtk_combo_box_menu_fill (combo_box);
3449 gtk_combo_box_menu_button_press (GtkWidget *widget,
3450 GdkEventButton *event,
3453 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3454 GtkComboBoxPrivate *priv = combo_box->priv;
3456 if (GTK_IS_MENU (priv->popup_widget) &&
3457 event->type == GDK_BUTTON_PRESS && event->button == 1)
3459 if (priv->focus_on_click &&
3460 !gtk_widget_has_focus (priv->button))
3461 gtk_widget_grab_focus (priv->button);
3463 gtk_combo_box_menu_popup (combo_box, event->button, event->time);
3472 gtk_combo_box_menu_item_activate (GtkWidget *item,
3475 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3476 GtkWidget *cell_view;
3480 cell_view = gtk_bin_get_child (GTK_BIN (item));
3482 g_return_if_fail (GTK_IS_CELL_VIEW (cell_view));
3484 path = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (cell_view));
3486 if (gtk_tree_model_get_iter (combo_box->priv->model, &iter, path))
3488 if (gtk_menu_item_get_submenu (GTK_MENU_ITEM (item)) == NULL)
3489 gtk_combo_box_set_active_iter (combo_box, &iter);
3492 gtk_tree_path_free (path);
3494 g_object_set (combo_box,
3495 "editing-canceled", FALSE,
3500 gtk_combo_box_update_sensitivity (GtkComboBox *combo_box)
3503 gboolean sensitive = TRUE; /* fool code checkers */
3505 if (!combo_box->priv->button)
3508 switch (combo_box->priv->button_sensitivity)
3510 case GTK_SENSITIVITY_ON:
3513 case GTK_SENSITIVITY_OFF:
3516 case GTK_SENSITIVITY_AUTO:
3517 sensitive = combo_box->priv->model &&
3518 gtk_tree_model_get_iter_first (combo_box->priv->model, &iter);
3521 g_assert_not_reached ();
3525 gtk_widget_set_sensitive (combo_box->priv->button, sensitive);
3527 /* In list-mode, we also need to update sensitivity of the event box */
3528 if (GTK_IS_TREE_VIEW (combo_box->priv->tree_view)
3529 && combo_box->priv->cell_view)
3530 gtk_widget_set_sensitive (combo_box->priv->box, sensitive);
3534 gtk_combo_box_model_row_inserted (GtkTreeModel *model,
3539 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3541 if (combo_box->priv->tree_view)
3542 gtk_combo_box_list_popup_resize (combo_box);
3544 gtk_combo_box_menu_row_inserted (model, path, iter, user_data);
3546 gtk_combo_box_update_sensitivity (combo_box);
3550 gtk_combo_box_model_row_deleted (GtkTreeModel *model,
3554 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3555 GtkComboBoxPrivate *priv = combo_box->priv;
3557 if (!gtk_tree_row_reference_valid (priv->active_row))
3559 if (priv->cell_view)
3560 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (priv->cell_view), NULL);
3561 g_signal_emit (combo_box, combo_box_signals[CHANGED], 0);
3564 if (priv->tree_view)
3565 gtk_combo_box_list_popup_resize (combo_box);
3567 gtk_combo_box_menu_row_deleted (model, path, user_data);
3569 gtk_combo_box_update_sensitivity (combo_box);
3573 gtk_combo_box_model_rows_reordered (GtkTreeModel *model,
3579 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3581 gtk_tree_row_reference_reordered (G_OBJECT (user_data), path, iter, new_order);
3583 if (!combo_box->priv->tree_view)
3584 gtk_combo_box_menu_rows_reordered (model, path, iter, new_order, user_data);
3588 gtk_combo_box_model_row_changed (GtkTreeModel *model,
3593 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3594 GtkComboBoxPrivate *priv = combo_box->priv;
3595 GtkTreePath *active_path;
3597 /* FIXME this belongs to GtkCellView */
3598 if (gtk_tree_row_reference_valid (priv->active_row))
3600 active_path = gtk_tree_row_reference_get_path (priv->active_row);
3601 if (gtk_tree_path_compare (path, active_path) == 0 &&
3603 gtk_widget_queue_resize (GTK_WIDGET (priv->cell_view));
3604 gtk_tree_path_free (active_path);
3607 if (priv->tree_view)
3608 gtk_combo_box_list_row_changed (model, path, iter, user_data);
3610 gtk_combo_box_menu_row_changed (model, path, iter, user_data);
3614 list_popup_resize_idle (gpointer user_data)
3616 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3617 GtkComboBoxPrivate *priv = combo_box->priv;
3618 gint x, y, width, height;
3620 if (priv->tree_view && gtk_widget_get_mapped (priv->popup_window))
3622 gtk_combo_box_list_position (combo_box, &x, &y, &width, &height);
3624 gtk_widget_set_size_request (priv->popup_window, width, height);
3625 gtk_window_move (GTK_WINDOW (priv->popup_window), x, y);
3628 priv->resize_idle_id = 0;
3634 gtk_combo_box_list_popup_resize (GtkComboBox *combo_box)
3636 GtkComboBoxPrivate *priv = combo_box->priv;
3638 if (!priv->resize_idle_id)
3639 priv->resize_idle_id =
3640 gdk_threads_add_idle (list_popup_resize_idle, combo_box);
3644 gtk_combo_box_model_row_expanded (GtkTreeModel *model,
3649 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3651 gtk_combo_box_list_popup_resize (combo_box);
3656 find_menu_by_path (GtkWidget *menu,
3658 gboolean skip_first)
3664 GtkTreeRowReference *mref;
3668 list = gtk_container_get_children (GTK_CONTAINER (menu));
3671 for (i = list; i; i = i->next)
3673 child = gtk_bin_get_child (i->data);
3674 if (GTK_IS_SEPARATOR_MENU_ITEM (i->data))
3676 mref = g_object_get_data (G_OBJECT (i->data), "gtk-combo-box-item-path");
3679 else if (!gtk_tree_row_reference_valid (mref))
3682 mpath = gtk_tree_row_reference_get_path (mref);
3684 else if (GTK_IS_CELL_VIEW (child))
3692 mpath = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (child));
3697 /* this case is necessary, since the row reference of
3698 * the cell view may already be updated after a deletion
3705 if (gtk_tree_path_compare (mpath, path) == 0)
3707 gtk_tree_path_free (mpath);
3711 if (gtk_tree_path_is_ancestor (mpath, path))
3713 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
3714 if (submenu != NULL)
3716 gtk_tree_path_free (mpath);
3717 item = find_menu_by_path (submenu, path, TRUE);
3721 gtk_tree_path_free (mpath);
3731 dump_menu_tree (GtkWidget *menu,
3738 list = gtk_container_get_children (GTK_CONTAINER (menu));
3739 for (i = list; i; i = i->next)
3741 if (GTK_IS_CELL_VIEW (GTK_BIN (i->data)->child))
3743 path = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (GTK_BIN (i->data)->child));
3744 g_print ("%*s%s\n", 2 * level, " ", gtk_tree_path_to_string (path));
3745 gtk_tree_path_free (path);
3747 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
3748 if (submenu != NULL)
3749 dump_menu_tree (submenu, level + 1);
3758 gtk_combo_box_menu_row_inserted (GtkTreeModel *model,
3763 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3764 GtkComboBoxPrivate *priv = combo_box->priv;
3766 GtkWidget *item, *menu, *separator;
3770 gboolean is_separator;
3772 if (!priv->popup_widget)
3775 depth = gtk_tree_path_get_depth (path);
3776 pos = gtk_tree_path_get_indices (path)[depth - 1];
3779 ppath = gtk_tree_path_copy (path);
3780 gtk_tree_path_up (ppath);
3781 parent = find_menu_by_path (priv->popup_widget, ppath, FALSE);
3782 gtk_tree_path_free (ppath);
3784 menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (parent));
3787 menu = gtk_menu_new ();
3788 gtk_menu_set_reserve_toggle_size (GTK_MENU (menu), FALSE);
3789 gtk_widget_show (menu);
3790 gtk_menu_item_set_submenu (GTK_MENU_ITEM (parent), menu);
3792 /* Ugly - since menus can only activate leaves, we have to
3793 * duplicate the item inside the submenu.
3795 gtk_tree_model_iter_parent (model, &piter, iter);
3796 item = gtk_cell_view_menu_item_new (combo_box, model, &piter);
3797 separator = gtk_separator_menu_item_new ();
3798 g_signal_connect (item, "activate",
3799 G_CALLBACK (gtk_combo_box_menu_item_activate),
3801 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3802 gtk_menu_shell_append (GTK_MENU_SHELL (menu), separator);
3803 if (cell_view_is_sensitive (GTK_CELL_VIEW (gtk_bin_get_child (GTK_BIN (item)))))
3805 gtk_widget_show (item);
3806 gtk_widget_show (separator);
3813 menu = priv->popup_widget;
3814 if (priv->add_tearoffs)
3818 if (priv->row_separator_func)
3819 is_separator = priv->row_separator_func (model, iter,
3820 priv->row_separator_data);
3822 is_separator = FALSE;
3826 item = gtk_separator_menu_item_new ();
3827 g_object_set_data_full (G_OBJECT (item),
3828 I_("gtk-combo-box-item-path"),
3829 gtk_tree_row_reference_new (model, path),
3830 (GDestroyNotify)gtk_tree_row_reference_free);
3834 item = gtk_cell_view_menu_item_new (combo_box, model, iter);
3836 g_signal_connect (item, "activate",
3837 G_CALLBACK (gtk_combo_box_menu_item_activate),
3841 gtk_widget_show (item);
3842 gtk_menu_shell_insert (GTK_MENU_SHELL (menu), item, pos);
3846 gtk_combo_box_menu_row_deleted (GtkTreeModel *model,
3850 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3851 GtkComboBoxPrivate *priv = combo_box->priv;
3855 if (!priv->popup_widget)
3858 item = find_menu_by_path (priv->popup_widget, path, FALSE);
3859 menu = gtk_widget_get_parent (item);
3860 gtk_container_remove (GTK_CONTAINER (menu), item);
3862 if (gtk_tree_path_get_depth (path) > 1)
3864 GtkTreePath *parent_path;
3868 parent_path = gtk_tree_path_copy (path);
3869 gtk_tree_path_up (parent_path);
3870 gtk_tree_model_get_iter (model, &iter, parent_path);
3872 if (!gtk_tree_model_iter_has_child (model, &iter))
3874 parent = find_menu_by_path (priv->popup_widget,
3875 parent_path, FALSE);
3876 gtk_menu_item_set_submenu (GTK_MENU_ITEM (parent), NULL);
3882 gtk_combo_box_menu_rows_reordered (GtkTreeModel *model,
3888 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3890 gtk_combo_box_relayout (combo_box);
3894 gtk_combo_box_menu_row_changed (GtkTreeModel *model,
3899 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3900 GtkComboBoxPrivate *priv = combo_box->priv;
3902 gboolean is_separator;
3904 if (!priv->popup_widget)
3907 item = find_menu_by_path (priv->popup_widget, path, FALSE);
3909 if (priv->row_separator_func)
3910 is_separator = priv->row_separator_func (model, iter,
3911 priv->row_separator_data);
3913 is_separator = FALSE;
3915 if (is_separator != GTK_IS_SEPARATOR_MENU_ITEM (item))
3917 gtk_combo_box_menu_row_deleted (model, path, combo_box);
3918 gtk_combo_box_menu_row_inserted (model, path, iter, combo_box);
3921 if (priv->wrap_width &&
3922 gtk_widget_get_parent (item) == priv->popup_widget)
3924 GtkWidget *pitem = NULL;
3927 prev = gtk_tree_path_copy (path);
3929 if (gtk_tree_path_prev (prev))
3930 pitem = find_menu_by_path (priv->popup_widget, prev, FALSE);
3932 gtk_tree_path_free (prev);
3934 /* unattach item so gtk_combo_box_relayout_item() won't spuriously
3936 gtk_container_child_set (GTK_CONTAINER (priv->popup_widget),
3941 "bottom-attach", -1,
3944 gtk_combo_box_relayout_item (combo_box, item, iter, pitem);
3947 gtk_combo_box_update_requested_width (combo_box, path);
3955 gtk_combo_box_list_setup (GtkComboBox *combo_box)
3957 GtkComboBoxPrivate *priv = combo_box->priv;
3958 GtkTreeSelection *sel;
3960 GtkWidget *widget = GTK_WIDGET (combo_box);
3962 priv->button = gtk_toggle_button_new ();
3963 child = gtk_bin_get_child (GTK_BIN (combo_box));
3964 gtk_widget_set_parent (priv->button,
3965 gtk_widget_get_parent (child));
3966 g_signal_connect (priv->button, "button-press-event",
3967 G_CALLBACK (gtk_combo_box_list_button_pressed), combo_box);
3968 g_signal_connect (priv->button, "toggled",
3969 G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
3971 priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3972 gtk_container_add (GTK_CONTAINER (priv->button), priv->arrow);
3973 priv->separator = NULL;
3974 gtk_widget_show_all (priv->button);
3976 if (priv->cell_view)
3978 GtkStyleContext *context;
3979 GtkStateFlags state;
3982 context = gtk_widget_get_style_context (widget);
3983 state = gtk_widget_get_state_flags (widget);
3985 gtk_style_context_get (context, state,
3986 "background-color", &color,
3989 gtk_cell_view_set_background_rgba (GTK_CELL_VIEW (priv->cell_view), color);
3990 gdk_rgba_free (color);
3992 priv->box = gtk_event_box_new ();
3993 gtk_event_box_set_visible_window (GTK_EVENT_BOX (priv->box),
3996 if (priv->has_frame)
3998 priv->cell_view_frame = gtk_frame_new (NULL);
3999 gtk_frame_set_shadow_type (GTK_FRAME (priv->cell_view_frame),
4004 combo_box->priv->cell_view_frame = gtk_event_box_new ();
4005 gtk_event_box_set_visible_window (GTK_EVENT_BOX (combo_box->priv->cell_view_frame),
4009 gtk_widget_set_parent (priv->cell_view_frame,
4010 gtk_widget_get_parent (child));
4011 gtk_container_add (GTK_CONTAINER (priv->cell_view_frame), priv->box);
4012 gtk_widget_show_all (priv->cell_view_frame);
4014 g_signal_connect (priv->box, "button-press-event",
4015 G_CALLBACK (gtk_combo_box_list_button_pressed),
4019 priv->tree_view = gtk_tree_view_new ();
4020 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));
4021 gtk_tree_selection_set_mode (sel, GTK_SELECTION_BROWSE);
4022 gtk_tree_selection_set_select_function (sel,
4023 gtk_combo_box_list_select_func,
4025 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->tree_view),
4027 gtk_tree_view_set_hover_selection (GTK_TREE_VIEW (priv->tree_view),
4029 if (priv->row_separator_func)
4030 gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (priv->tree_view),
4031 priv->row_separator_func,
4032 priv->row_separator_data,
4035 gtk_tree_view_set_model (GTK_TREE_VIEW (priv->tree_view), priv->model);
4037 priv->column = gtk_tree_view_column_new ();
4038 gtk_tree_view_append_column (GTK_TREE_VIEW (priv->tree_view), priv->column);
4041 gtk_combo_box_sync_cells (combo_box,
4042 GTK_CELL_LAYOUT (priv->column));
4044 if (gtk_tree_row_reference_valid (priv->active_row))
4048 path = gtk_tree_row_reference_get_path (priv->active_row);
4049 gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->tree_view),
4051 gtk_tree_path_free (path);
4054 /* set sample/popup widgets */
4055 gtk_combo_box_set_popup_widget (combo_box, priv->tree_view);
4057 g_signal_connect (priv->tree_view, "key-press-event",
4058 G_CALLBACK (gtk_combo_box_list_key_press),
4060 g_signal_connect (priv->tree_view, "enter-notify-event",
4061 G_CALLBACK (gtk_combo_box_list_enter_notify),
4063 g_signal_connect (priv->tree_view, "row-expanded",
4064 G_CALLBACK (gtk_combo_box_model_row_expanded),
4066 g_signal_connect (priv->tree_view, "row-collapsed",
4067 G_CALLBACK (gtk_combo_box_model_row_expanded),
4069 g_signal_connect (priv->popup_window, "button-press-event",
4070 G_CALLBACK (gtk_combo_box_list_button_pressed),
4072 g_signal_connect (priv->popup_window, "button-release-event",
4073 G_CALLBACK (gtk_combo_box_list_button_released),
4076 gtk_widget_show (priv->tree_view);
4078 gtk_combo_box_update_sensitivity (combo_box);
4082 gtk_combo_box_list_destroy (GtkComboBox *combo_box)
4084 GtkComboBoxPrivate *priv = combo_box->priv;
4086 /* disconnect signals */
4087 g_signal_handlers_disconnect_matched (priv->tree_view,
4088 G_SIGNAL_MATCH_DATA,
4089 0, 0, NULL, NULL, combo_box);
4090 g_signal_handlers_disconnect_matched (priv->button,
4091 G_SIGNAL_MATCH_DATA,
4093 gtk_combo_box_list_button_pressed,
4095 g_signal_handlers_disconnect_matched (priv->popup_window,
4096 G_SIGNAL_MATCH_DATA,
4098 gtk_combo_box_list_button_pressed,
4100 g_signal_handlers_disconnect_matched (priv->popup_window,
4101 G_SIGNAL_MATCH_DATA,
4103 gtk_combo_box_list_button_released,
4106 g_signal_handlers_disconnect_matched (priv->popup_window,
4107 G_SIGNAL_MATCH_DATA,
4109 gtk_combo_box_child_show,
4112 g_signal_handlers_disconnect_matched (priv->popup_window,
4113 G_SIGNAL_MATCH_DATA,
4115 gtk_combo_box_child_hide,
4119 g_signal_handlers_disconnect_matched (priv->box,
4120 G_SIGNAL_MATCH_DATA,
4122 gtk_combo_box_list_button_pressed,
4125 /* destroy things (unparent will kill the latest ref from us)
4126 * last unref on button will destroy the arrow
4128 gtk_widget_unparent (priv->button);
4129 priv->button = NULL;
4132 if (priv->cell_view)
4134 g_object_set (priv->cell_view,
4135 "background-set", FALSE,
4139 if (priv->cell_view_frame)
4141 gtk_widget_unparent (priv->cell_view_frame);
4142 priv->cell_view_frame = NULL;
4146 if (priv->scroll_timer)
4148 g_source_remove (priv->scroll_timer);
4149 priv->scroll_timer = 0;
4152 if (priv->resize_idle_id)
4154 g_source_remove (priv->resize_idle_id);
4155 priv->resize_idle_id = 0;
4158 gtk_widget_destroy (priv->tree_view);
4160 priv->tree_view = NULL;
4161 if (priv->popup_widget)
4163 g_object_unref (priv->popup_widget);
4164 priv->popup_widget = NULL;
4171 gtk_combo_box_list_button_pressed (GtkWidget *widget,
4172 GdkEventButton *event,
4175 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
4176 GtkComboBoxPrivate *priv = combo_box->priv;
4178 GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *)event);
4180 if (ewidget == priv->popup_window)
4183 if ((ewidget != priv->button && ewidget != priv->box) ||
4184 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->button)))
4187 if (priv->focus_on_click &&
4188 !gtk_widget_has_focus (priv->button))
4189 gtk_widget_grab_focus (priv->button);
4191 gtk_combo_box_popup_for_device (combo_box, event->device);
4193 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button), TRUE);
4195 priv->auto_scroll = FALSE;
4196 if (priv->scroll_timer == 0)
4197 priv->scroll_timer = gdk_threads_add_timeout (SCROLL_TIME,
4198 (GSourceFunc) gtk_combo_box_list_scroll_timeout,
4201 priv->popup_in_progress = TRUE;
4207 gtk_combo_box_list_button_released (GtkWidget *widget,
4208 GdkEventButton *event,
4212 GtkTreePath *path = NULL;
4215 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
4216 GtkComboBoxPrivate *priv = combo_box->priv;
4218 gboolean popup_in_progress = FALSE;
4220 GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *)event);
4222 if (priv->popup_in_progress)
4224 popup_in_progress = TRUE;
4225 priv->popup_in_progress = FALSE;
4228 gtk_tree_view_set_hover_expand (GTK_TREE_VIEW (priv->tree_view),
4230 if (priv->scroll_timer)
4232 g_source_remove (priv->scroll_timer);
4233 priv->scroll_timer = 0;
4236 if (ewidget != priv->tree_view)
4238 if ((ewidget == priv->button ||
4239 ewidget == priv->box) &&
4240 !popup_in_progress &&
4241 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->button)))
4243 gtk_combo_box_popdown (combo_box);
4247 /* released outside treeview */
4248 if (ewidget != priv->button && ewidget != priv->box)
4250 gtk_combo_box_popdown (combo_box);
4258 /* select something cool */
4259 ret = gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (priv->tree_view),
4265 return TRUE; /* clicked outside window? */
4267 gtk_tree_model_get_iter (priv->model, &iter, path);
4268 gtk_tree_path_free (path);
4270 gtk_combo_box_popdown (combo_box);
4272 if (tree_column_row_is_sensitive (combo_box, &iter))
4273 gtk_combo_box_set_active_iter (combo_box, &iter);
4279 gtk_combo_box_menu_key_press (GtkWidget *widget,
4283 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
4285 if (!gtk_bindings_activate_event (G_OBJECT (widget), event))
4287 /* The menu hasn't managed the
4288 * event, forward it to the combobox
4290 gtk_bindings_activate_event (G_OBJECT (combo_box), event);
4297 gtk_combo_box_list_key_press (GtkWidget *widget,
4301 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
4304 if (event->keyval == GDK_KEY_Return || event->keyval == GDK_KEY_ISO_Enter || event->keyval == GDK_KEY_KP_Enter ||
4305 event->keyval == GDK_KEY_space || event->keyval == GDK_KEY_KP_Space)
4307 GtkTreeModel *model = NULL;
4309 gtk_combo_box_popdown (combo_box);
4311 if (combo_box->priv->model)
4313 GtkTreeSelection *sel;
4315 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (combo_box->priv->tree_view));
4317 if (gtk_tree_selection_get_selected (sel, &model, &iter))
4318 gtk_combo_box_set_active_iter (combo_box, &iter);
4324 if (!gtk_bindings_activate_event (G_OBJECT (widget), event))
4326 /* The list hasn't managed the
4327 * event, forward it to the combobox
4329 gtk_bindings_activate_event (G_OBJECT (combo_box), event);
4336 gtk_combo_box_list_auto_scroll (GtkComboBox *combo_box,
4341 GtkAllocation allocation;
4342 GtkWidget *tree_view = combo_box->priv->tree_view;
4345 gtk_widget_get_allocation (tree_view, &allocation);
4347 adj = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window));
4348 if (adj && adj->upper - adj->lower > adj->page_size)
4350 if (x <= allocation.x &&
4351 adj->lower < adj->value)
4353 value = adj->value - (allocation.x - x + 1);
4354 gtk_adjustment_set_value (adj, value);
4356 else if (x >= allocation.x + allocation.width &&
4357 adj->upper - adj->page_size > adj->value)
4359 value = adj->value + (x - allocation.x - allocation.width + 1);
4360 gtk_adjustment_set_value (adj, MAX (value, 0.0));
4364 adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window));
4365 if (adj && adj->upper - adj->lower > adj->page_size)
4367 if (y <= allocation.y &&
4368 adj->lower < adj->value)
4370 value = adj->value - (allocation.y - y + 1);
4371 gtk_adjustment_set_value (adj, value);
4373 else if (y >= allocation.height &&
4374 adj->upper - adj->page_size > adj->value)
4376 value = adj->value + (y - allocation.height + 1);
4377 gtk_adjustment_set_value (adj, MAX (value, 0.0));
4383 gtk_combo_box_list_scroll_timeout (GtkComboBox *combo_box)
4385 GtkComboBoxPrivate *priv = combo_box->priv;
4388 if (priv->auto_scroll)
4390 gdk_window_get_device_position (gtk_widget_get_window (priv->tree_view),
4393 gtk_combo_box_list_auto_scroll (combo_box, x, y);
4400 gtk_combo_box_list_enter_notify (GtkWidget *widget,
4401 GdkEventCrossing *event,
4404 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
4406 combo_box->priv->auto_scroll = TRUE;
4412 gtk_combo_box_list_select_func (GtkTreeSelection *selection,
4413 GtkTreeModel *model,
4415 gboolean path_currently_selected,
4418 GList *list, *columns;
4419 gboolean sensitive = FALSE;
4421 columns = gtk_tree_view_get_columns (selection->tree_view);
4423 for (list = columns; list && !sensitive; list = list->next)
4425 GList *cells, *cell;
4426 gboolean cell_sensitive, cell_visible;
4428 GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN (list->data);
4430 if (!gtk_tree_view_column_get_visible (column))
4433 gtk_tree_model_get_iter (model, &iter, path);
4434 gtk_tree_view_column_cell_set_cell_data (column, model, &iter,
4437 cell = cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
4440 g_object_get (cell->data,
4441 "sensitive", &cell_sensitive,
4442 "visible", &cell_visible,
4445 if (cell_visible && cell_sensitive)
4450 g_list_free (cells);
4452 sensitive = cell_sensitive;
4455 g_list_free (columns);
4461 gtk_combo_box_list_row_changed (GtkTreeModel *model,
4466 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
4468 gtk_combo_box_update_requested_width (combo_box, path);
4472 * GtkCellLayout implementation
4476 pack_start_recurse (GtkWidget *menu,
4477 GtkCellRenderer *cell,
4484 list = gtk_container_get_children (GTK_CONTAINER (menu));
4485 for (i = list; i; i = i->next)
4487 child = gtk_bin_get_child (GTK_BIN (i->data));
4488 if (GTK_IS_CELL_LAYOUT (child))
4489 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (child),
4492 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4493 if (submenu != NULL)
4494 pack_start_recurse (submenu, cell, expand);
4501 gtk_combo_box_cell_layout_pack_start (GtkCellLayout *layout,
4502 GtkCellRenderer *cell,
4505 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4506 ComboCellInfo *info;
4507 GtkComboBoxPrivate *priv;
4509 priv = combo_box->priv;
4511 g_object_ref_sink (cell);
4513 info = g_slice_new0 (ComboCellInfo);
4515 info->expand = expand;
4516 info->pack = GTK_PACK_START;
4518 priv->cells = g_slist_append (priv->cells, info);
4520 if (priv->cell_view)
4522 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->cell_view),
4528 gtk_tree_view_column_pack_start (priv->column, cell, expand);
4530 if (GTK_IS_MENU (priv->popup_widget))
4531 pack_start_recurse (priv->popup_widget, cell, expand);
4535 pack_end_recurse (GtkWidget *menu,
4536 GtkCellRenderer *cell,
4543 list = gtk_container_get_children (GTK_CONTAINER (menu));
4544 for (i = list; i; i = i->next)
4546 child = gtk_bin_get_child (GTK_BIN (i->data));
4547 if (GTK_IS_CELL_LAYOUT (child))
4548 gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (child),
4551 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4552 if (submenu != NULL)
4553 pack_end_recurse (submenu, cell, expand);
4560 gtk_combo_box_cell_layout_pack_end (GtkCellLayout *layout,
4561 GtkCellRenderer *cell,
4564 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4565 ComboCellInfo *info;
4566 GtkComboBoxPrivate *priv;
4568 priv = combo_box->priv;
4570 g_object_ref_sink (cell);
4572 info = g_slice_new0 (ComboCellInfo);
4574 info->expand = expand;
4575 info->pack = GTK_PACK_END;
4577 priv->cells = g_slist_append (priv->cells, info);
4579 if (priv->cell_view)
4580 gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (priv->cell_view),
4584 gtk_tree_view_column_pack_end (priv->column, cell, expand);
4586 if (GTK_IS_MENU (priv->popup_widget))
4587 pack_end_recurse (priv->popup_widget, cell, expand);
4591 gtk_combo_box_cell_layout_get_cells (GtkCellLayout *layout)
4593 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4595 GList *retval = NULL;
4597 for (list = combo_box->priv->cells; list; list = list->next)
4599 ComboCellInfo *info = (ComboCellInfo *)list->data;
4601 retval = g_list_prepend (retval, info->cell);
4604 return g_list_reverse (retval);
4608 clear_recurse (GtkWidget *menu)
4614 list = gtk_container_get_children (GTK_CONTAINER (menu));
4615 for (i = list; i; i = i->next)
4617 child = gtk_bin_get_child (GTK_BIN (i->data));
4618 if (GTK_IS_CELL_LAYOUT (child))
4619 gtk_cell_layout_clear (GTK_CELL_LAYOUT (child));
4621 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4622 if (submenu != NULL)
4623 clear_recurse (submenu);
4630 gtk_combo_box_cell_layout_clear (GtkCellLayout *layout)
4632 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4633 GtkComboBoxPrivate *priv = combo_box->priv;
4636 if (priv->cell_view)
4637 gtk_cell_layout_clear (GTK_CELL_LAYOUT (priv->cell_view));
4640 gtk_tree_view_column_clear (priv->column);
4642 for (i = priv->cells; i; i = i->next)
4644 ComboCellInfo *info = (ComboCellInfo *)i->data;
4646 gtk_combo_box_cell_layout_clear_attributes (layout, info->cell);
4647 g_object_unref (info->cell);
4648 g_slice_free (ComboCellInfo, info);
4651 g_slist_free (priv->cells);
4654 if (GTK_IS_MENU (priv->popup_widget))
4655 clear_recurse (priv->popup_widget);
4659 add_attribute_recurse (GtkWidget *menu,
4660 GtkCellRenderer *cell,
4661 const gchar *attribute,
4668 list = gtk_container_get_children (GTK_CONTAINER (menu));
4669 for (i = list; i; i = i->next)
4671 child = gtk_bin_get_child (GTK_BIN (i->data));
4672 if (GTK_IS_CELL_LAYOUT (child))
4673 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (child),
4674 cell, attribute, column);
4676 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4677 if (submenu != NULL)
4678 add_attribute_recurse (submenu, cell, attribute, column);
4685 gtk_combo_box_cell_layout_add_attribute (GtkCellLayout *layout,
4686 GtkCellRenderer *cell,
4687 const gchar *attribute,
4690 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4691 ComboCellInfo *info;
4693 info = gtk_combo_box_get_cell_info (combo_box, cell);
4694 g_return_if_fail (info != NULL);
4696 info->attributes = g_slist_prepend (info->attributes,
4697 GINT_TO_POINTER (column));
4698 info->attributes = g_slist_prepend (info->attributes,
4699 g_strdup (attribute));
4701 if (combo_box->priv->cell_view)
4702 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box->priv->cell_view),
4703 cell, attribute, column);
4705 if (combo_box->priv->column)
4706 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box->priv->column),
4707 cell, attribute, column);
4709 if (GTK_IS_MENU (combo_box->priv->popup_widget))
4710 add_attribute_recurse (combo_box->priv->popup_widget, cell, attribute, column);
4711 gtk_widget_queue_resize (GTK_WIDGET (combo_box));
4715 combo_cell_data_func (GtkCellLayout *cell_layout,
4716 GtkCellRenderer *cell,
4717 GtkTreeModel *tree_model,
4721 ComboCellInfo *info = (ComboCellInfo *)data;
4722 GtkWidget *parent = NULL;
4727 info->func (cell_layout, cell, tree_model, iter, info->func_data);
4729 if (GTK_IS_WIDGET (cell_layout))
4730 parent = gtk_widget_get_parent (GTK_WIDGET (cell_layout));
4732 if (GTK_IS_MENU_ITEM (parent) &&
4733 gtk_menu_item_get_submenu (GTK_MENU_ITEM (parent)))
4734 g_object_set (cell, "sensitive", TRUE, NULL);
4739 set_cell_data_func_recurse (GtkWidget *menu,
4740 GtkCellRenderer *cell,
4741 ComboCellInfo *info)
4745 GtkWidget *cell_view;
4747 list = gtk_container_get_children (GTK_CONTAINER (menu));
4748 for (i = list; i; i = i->next)
4750 cell_view = gtk_bin_get_child (GTK_BIN (i->data));
4751 if (GTK_IS_CELL_LAYOUT (cell_view))
4753 /* Override sensitivity for inner nodes; we don't
4754 * want menuitems with submenus to appear insensitive
4756 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (cell_view),
4758 combo_cell_data_func,
4760 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4761 if (submenu != NULL)
4762 set_cell_data_func_recurse (submenu, cell, info);
4770 gtk_combo_box_cell_layout_set_cell_data_func (GtkCellLayout *layout,
4771 GtkCellRenderer *cell,
4772 GtkCellLayoutDataFunc func,
4774 GDestroyNotify destroy)
4776 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4777 GtkComboBoxPrivate *priv = combo_box->priv;
4778 ComboCellInfo *info;
4780 info = gtk_combo_box_get_cell_info (combo_box, cell);
4781 g_return_if_fail (info != NULL);
4785 GDestroyNotify d = info->destroy;
4787 info->destroy = NULL;
4788 d (info->func_data);
4792 info->func_data = func_data;
4793 info->destroy = destroy;
4795 if (priv->cell_view)
4796 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (priv->cell_view), cell, func, func_data, NULL);
4799 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (priv->column), cell, func, func_data, NULL);
4801 if (GTK_IS_MENU (priv->popup_widget))
4802 set_cell_data_func_recurse (priv->popup_widget, cell, info);
4804 gtk_widget_queue_resize (GTK_WIDGET (combo_box));
4808 clear_attributes_recurse (GtkWidget *menu,
4809 GtkCellRenderer *cell)
4815 list = gtk_container_get_children (GTK_CONTAINER (menu));
4816 for (i = list; i; i = i->next)
4818 child = gtk_bin_get_child (GTK_BIN (i->data));
4819 if (GTK_IS_CELL_LAYOUT (child))
4820 gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (child), cell);
4822 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4823 if (submenu != NULL)
4824 clear_attributes_recurse (submenu, cell);
4831 gtk_combo_box_cell_layout_clear_attributes (GtkCellLayout *layout,
4832 GtkCellRenderer *cell)
4834 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4835 GtkComboBoxPrivate *priv;
4836 ComboCellInfo *info;
4839 priv = combo_box->priv;
4841 info = gtk_combo_box_get_cell_info (combo_box, cell);
4842 g_return_if_fail (info != NULL);
4844 list = info->attributes;
4845 while (list && list->next)
4847 g_free (list->data);
4848 list = list->next->next;
4850 g_slist_free (info->attributes);
4851 info->attributes = NULL;
4853 if (priv->cell_view)
4854 gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (priv->cell_view), cell);
4857 gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (priv->column), cell);
4859 if (GTK_IS_MENU (priv->popup_widget))
4860 clear_attributes_recurse (priv->popup_widget, cell);
4862 gtk_widget_queue_resize (GTK_WIDGET (combo_box));
4866 reorder_recurse (GtkWidget *menu,
4867 GtkCellRenderer *cell,
4874 list = gtk_container_get_children (GTK_CONTAINER (menu));
4875 for (i = list; i; i = i->next)
4877 child = gtk_bin_get_child (GTK_BIN (i->data));
4878 if (GTK_IS_CELL_LAYOUT (child))
4879 gtk_cell_layout_reorder (GTK_CELL_LAYOUT (child),
4882 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4883 if (submenu != NULL)
4884 reorder_recurse (submenu, cell, position);
4891 gtk_combo_box_cell_layout_reorder (GtkCellLayout *layout,
4892 GtkCellRenderer *cell,
4895 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4896 GtkComboBoxPrivate *priv;
4897 ComboCellInfo *info;
4900 priv = combo_box->priv;
4902 info = gtk_combo_box_get_cell_info (combo_box, cell);
4904 g_return_if_fail (info != NULL);
4905 g_return_if_fail (position >= 0);
4907 link = g_slist_find (priv->cells, info);
4909 g_return_if_fail (link != NULL);
4911 priv->cells = g_slist_delete_link (priv->cells, link);
4912 priv->cells = g_slist_insert (priv->cells, info, position);
4914 if (priv->cell_view)
4915 gtk_cell_layout_reorder (GTK_CELL_LAYOUT (priv->cell_view),
4919 gtk_cell_layout_reorder (GTK_CELL_LAYOUT (priv->column),
4922 if (GTK_IS_MENU (priv->popup_widget))
4923 reorder_recurse (priv->popup_widget, cell, position);
4925 gtk_widget_queue_draw (GTK_WIDGET (combo_box));
4933 * gtk_combo_box_new:
4935 * Creates a new empty #GtkComboBox.
4937 * Return value: A new #GtkComboBox.
4942 gtk_combo_box_new (void)
4944 return g_object_new (GTK_TYPE_COMBO_BOX, NULL);
4948 * gtk_combo_box_new_with_entry:
4950 * Creates a new empty #GtkComboBox with an entry.
4952 * Return value: A new #GtkComboBox.
4955 gtk_combo_box_new_with_entry (void)
4957 return g_object_new (GTK_TYPE_COMBO_BOX, "has-entry", TRUE, NULL);
4961 * gtk_combo_box_new_with_model:
4962 * @model: A #GtkTreeModel.
4964 * Creates a new #GtkComboBox with the model initialized to @model.
4966 * Return value: A new #GtkComboBox.
4971 gtk_combo_box_new_with_model (GtkTreeModel *model)
4973 GtkComboBox *combo_box;
4975 g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL);
4977 combo_box = g_object_new (GTK_TYPE_COMBO_BOX, "model", model, NULL);
4979 return GTK_WIDGET (combo_box);
4983 * gtk_combo_box_new_with_model_and_entry:
4984 * @model: A #GtkTreeModel
4986 * Creates a new empty #GtkComboBox with an entry
4987 * and with the model initialized to @model.
4989 * Return value: A new #GtkComboBox
4992 gtk_combo_box_new_with_model_and_entry (GtkTreeModel *model)
4994 return g_object_new (GTK_TYPE_COMBO_BOX,
5001 * gtk_combo_box_get_wrap_width:
5002 * @combo_box: A #GtkComboBox
5004 * Returns the wrap width which is used to determine the number of columns
5005 * for the popup menu. If the wrap width is larger than 1, the combo box
5008 * Returns: the wrap width.
5013 gtk_combo_box_get_wrap_width (GtkComboBox *combo_box)
5015 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), -1);
5017 return combo_box->priv->wrap_width;
5021 * gtk_combo_box_set_wrap_width:
5022 * @combo_box: A #GtkComboBox
5023 * @width: Preferred number of columns
5025 * Sets the wrap width of @combo_box to be @width. The wrap width is basically
5026 * the preferred number of columns when you want the popup to be layed out
5032 gtk_combo_box_set_wrap_width (GtkComboBox *combo_box,
5035 GtkComboBoxPrivate *priv;
5037 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5038 g_return_if_fail (width >= 0);
5040 priv = combo_box->priv;
5042 if (width != priv->wrap_width)
5044 priv->wrap_width = width;
5046 gtk_combo_box_check_appearance (combo_box);
5047 gtk_combo_box_relayout (combo_box);
5049 g_object_notify (G_OBJECT (combo_box), "wrap-width");
5054 * gtk_combo_box_get_row_span_column:
5055 * @combo_box: A #GtkComboBox
5057 * Returns the column with row span information for @combo_box.
5059 * Returns: the row span column.
5064 gtk_combo_box_get_row_span_column (GtkComboBox *combo_box)
5066 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), -1);
5068 return combo_box->priv->row_column;
5072 * gtk_combo_box_set_row_span_column:
5073 * @combo_box: A #GtkComboBox.
5074 * @row_span: A column in the model passed during construction.
5076 * Sets the column with row span information for @combo_box to be @row_span.
5077 * The row span column contains integers which indicate how many rows
5078 * an item should span.
5083 gtk_combo_box_set_row_span_column (GtkComboBox *combo_box,
5086 GtkComboBoxPrivate *priv;
5089 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5091 priv = combo_box->priv;
5093 col = gtk_tree_model_get_n_columns (priv->model);
5094 g_return_if_fail (row_span >= -1 && row_span < col);
5096 if (row_span != priv->row_column)
5098 priv->row_column = row_span;
5100 gtk_combo_box_relayout (combo_box);
5102 g_object_notify (G_OBJECT (combo_box), "row-span-column");
5107 * gtk_combo_box_get_column_span_column:
5108 * @combo_box: A #GtkComboBox
5110 * Returns the column with column span information for @combo_box.
5112 * Returns: the column span column.
5117 gtk_combo_box_get_column_span_column (GtkComboBox *combo_box)
5119 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), -1);
5121 return combo_box->priv->col_column;
5125 * gtk_combo_box_set_column_span_column:
5126 * @combo_box: A #GtkComboBox
5127 * @column_span: A column in the model passed during construction
5129 * Sets the column with column span information for @combo_box to be
5130 * @column_span. The column span column contains integers which indicate
5131 * how many columns an item should span.
5136 gtk_combo_box_set_column_span_column (GtkComboBox *combo_box,
5139 GtkComboBoxPrivate *priv;
5142 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5144 priv = combo_box->priv;
5146 col = gtk_tree_model_get_n_columns (priv->model);
5147 g_return_if_fail (column_span >= -1 && column_span < col);
5149 if (column_span != priv->col_column)
5151 priv->col_column = column_span;
5153 gtk_combo_box_relayout (combo_box);
5155 g_object_notify (G_OBJECT (combo_box), "column-span-column");
5160 * gtk_combo_box_get_active:
5161 * @combo_box: A #GtkComboBox
5163 * Returns the index of the currently active item, or -1 if there's no
5164 * active item. If the model is a non-flat treemodel, and the active item
5165 * is not an immediate child of the root of the tree, this function returns
5166 * <literal>gtk_tree_path_get_indices (path)[0]</literal>, where
5167 * <literal>path</literal> is the #GtkTreePath of the active item.
5169 * Return value: An integer which is the index of the currently active item,
5170 * or -1 if there's no active item.
5175 gtk_combo_box_get_active (GtkComboBox *combo_box)
5177 GtkComboBoxPrivate *priv;
5180 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), 0);
5182 priv = combo_box->priv;
5184 if (gtk_tree_row_reference_valid (priv->active_row))
5188 path = gtk_tree_row_reference_get_path (priv->active_row);
5189 result = gtk_tree_path_get_indices (path)[0];
5190 gtk_tree_path_free (path);
5199 * gtk_combo_box_set_active:
5200 * @combo_box: A #GtkComboBox
5201 * @index_: An index in the model passed during construction, or -1 to have
5204 * Sets the active item of @combo_box to be the item at @index.
5209 gtk_combo_box_set_active (GtkComboBox *combo_box,
5212 GtkTreePath *path = NULL;
5213 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5214 g_return_if_fail (index_ >= -1);
5216 if (combo_box->priv->model == NULL)
5218 /* Save index, in case the model is set after the index */
5219 combo_box->priv->active = index_;
5225 path = gtk_tree_path_new_from_indices (index_, -1);
5227 gtk_combo_box_set_active_internal (combo_box, path);
5230 gtk_tree_path_free (path);
5234 gtk_combo_box_set_active_internal (GtkComboBox *combo_box,
5237 GtkComboBoxPrivate *priv = combo_box->priv;
5238 GtkTreePath *active_path;
5241 /* Remember whether the initially active row is valid. */
5242 gboolean is_valid_row_reference = gtk_tree_row_reference_valid (priv->active_row);
5244 if (path && is_valid_row_reference)
5246 active_path = gtk_tree_row_reference_get_path (priv->active_row);
5247 path_cmp = gtk_tree_path_compare (path, active_path);
5248 gtk_tree_path_free (active_path);
5253 if (priv->active_row)
5255 gtk_tree_row_reference_free (priv->active_row);
5256 priv->active_row = NULL;
5261 if (priv->tree_view)
5262 gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view)));
5265 GtkMenu *menu = GTK_MENU (priv->popup_widget);
5267 if (GTK_IS_MENU (menu))
5268 gtk_menu_set_active (menu, -1);
5271 if (priv->cell_view)
5272 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (priv->cell_view), NULL);
5275 * Do not emit a "changed" signal when an already invalid selection was
5276 * now set to invalid.
5278 if (!is_valid_row_reference)
5284 gtk_tree_row_reference_new (priv->model, path);
5286 if (priv->tree_view)
5288 gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->tree_view),
5291 else if (GTK_IS_MENU (priv->popup_widget))
5293 /* FIXME handle nested menus better */
5294 gtk_menu_set_active (GTK_MENU (priv->popup_widget),
5295 gtk_tree_path_get_indices (path)[0]);
5298 if (priv->cell_view)
5299 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (priv->cell_view),
5303 g_signal_emit (combo_box, combo_box_signals[CHANGED], 0);
5304 g_object_notify (G_OBJECT (combo_box), "active");
5305 if (combo_box->priv->id_column >= 0)
5306 g_object_notify (G_OBJECT (combo_box), "active-id");
5311 * gtk_combo_box_get_active_iter:
5312 * @combo_box: A #GtkComboBox
5313 * @iter: (out): The uninitialized #GtkTreeIter
5315 * Sets @iter to point to the current active item, if it exists.
5317 * Return value: %TRUE, if @iter was set
5322 gtk_combo_box_get_active_iter (GtkComboBox *combo_box,
5328 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
5330 if (!gtk_tree_row_reference_valid (combo_box->priv->active_row))
5333 path = gtk_tree_row_reference_get_path (combo_box->priv->active_row);
5334 result = gtk_tree_model_get_iter (combo_box->priv->model, iter, path);
5335 gtk_tree_path_free (path);
5341 * gtk_combo_box_set_active_iter:
5342 * @combo_box: A #GtkComboBox
5343 * @iter: (allow-none): The #GtkTreeIter, or %NULL
5345 * Sets the current active item to be the one referenced by @iter, or
5346 * unsets the active item if @iter is %NULL.
5351 gtk_combo_box_set_active_iter (GtkComboBox *combo_box,
5354 GtkTreePath *path = NULL;
5356 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5359 path = gtk_tree_model_get_path (gtk_combo_box_get_model (combo_box), iter);
5361 gtk_combo_box_set_active_internal (combo_box, path);
5362 gtk_tree_path_free (path);
5366 * gtk_combo_box_set_model:
5367 * @combo_box: A #GtkComboBox
5368 * @model: (allow-none): A #GtkTreeModel
5370 * Sets the model used by @combo_box to be @model. Will unset a previously set
5371 * model (if applicable). If model is %NULL, then it will unset the model.
5373 * Note that this function does not clear the cell renderers, you have to
5374 * call gtk_cell_layout_clear() yourself if you need to set up different
5375 * cell renderers for the new model.
5380 gtk_combo_box_set_model (GtkComboBox *combo_box,
5381 GtkTreeModel *model)
5383 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5384 g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
5386 if (model == combo_box->priv->model)
5389 gtk_combo_box_unset_model (combo_box);
5394 combo_box->priv->model = model;
5395 g_object_ref (combo_box->priv->model);
5397 combo_box->priv->inserted_id =
5398 g_signal_connect (combo_box->priv->model, "row-inserted",
5399 G_CALLBACK (gtk_combo_box_model_row_inserted),
5401 combo_box->priv->deleted_id =
5402 g_signal_connect (combo_box->priv->model, "row-deleted",
5403 G_CALLBACK (gtk_combo_box_model_row_deleted),
5405 combo_box->priv->reordered_id =
5406 g_signal_connect (combo_box->priv->model, "rows-reordered",
5407 G_CALLBACK (gtk_combo_box_model_rows_reordered),
5409 combo_box->priv->changed_id =
5410 g_signal_connect (combo_box->priv->model, "row-changed",
5411 G_CALLBACK (gtk_combo_box_model_row_changed),
5414 if (combo_box->priv->tree_view)
5417 gtk_tree_view_set_model (GTK_TREE_VIEW (combo_box->priv->tree_view),
5418 combo_box->priv->model);
5419 gtk_combo_box_list_popup_resize (combo_box);
5424 if (combo_box->priv->popup_widget)
5425 gtk_combo_box_menu_fill (combo_box);
5429 if (combo_box->priv->cell_view)
5430 gtk_cell_view_set_model (GTK_CELL_VIEW (combo_box->priv->cell_view),
5431 combo_box->priv->model);
5433 if (combo_box->priv->active != -1)
5435 /* If an index was set in advance, apply it now */
5436 gtk_combo_box_set_active (combo_box, combo_box->priv->active);
5437 combo_box->priv->active = -1;
5441 gtk_combo_box_update_sensitivity (combo_box);
5443 g_object_notify (G_OBJECT (combo_box), "model");
5447 * gtk_combo_box_get_model:
5448 * @combo_box: A #GtkComboBox
5450 * Returns the #GtkTreeModel which is acting as data source for @combo_box.
5452 * Return value: (transfer none): A #GtkTreeModel which was passed
5453 * during construction.
5458 gtk_combo_box_get_model (GtkComboBox *combo_box)
5460 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
5462 return combo_box->priv->model;
5466 gtk_combo_box_real_move_active (GtkComboBox *combo_box,
5467 GtkScrollType scroll)
5470 GtkTreeIter new_iter;
5471 gboolean active_iter;
5474 if (!combo_box->priv->model)
5476 gtk_widget_error_bell (GTK_WIDGET (combo_box));
5480 active_iter = gtk_combo_box_get_active_iter (combo_box, &iter);
5484 case GTK_SCROLL_STEP_BACKWARD:
5485 case GTK_SCROLL_STEP_UP:
5486 case GTK_SCROLL_STEP_LEFT:
5489 found = tree_prev (combo_box, combo_box->priv->model,
5490 &iter, &new_iter, FALSE);
5493 /* else fall through */
5495 case GTK_SCROLL_PAGE_FORWARD:
5496 case GTK_SCROLL_PAGE_DOWN:
5497 case GTK_SCROLL_PAGE_RIGHT:
5498 case GTK_SCROLL_END:
5499 found = tree_last (combo_box, combo_box->priv->model, &new_iter, FALSE);
5502 case GTK_SCROLL_STEP_FORWARD:
5503 case GTK_SCROLL_STEP_DOWN:
5504 case GTK_SCROLL_STEP_RIGHT:
5507 found = tree_next (combo_box, combo_box->priv->model,
5508 &iter, &new_iter, FALSE);
5511 /* else fall through */
5513 case GTK_SCROLL_PAGE_BACKWARD:
5514 case GTK_SCROLL_PAGE_UP:
5515 case GTK_SCROLL_PAGE_LEFT:
5516 case GTK_SCROLL_START:
5517 found = tree_first (combo_box, combo_box->priv->model, &new_iter, FALSE);
5524 if (found && active_iter)
5526 GtkTreePath *old_path;
5527 GtkTreePath *new_path;
5529 old_path = gtk_tree_model_get_path (combo_box->priv->model, &iter);
5530 new_path = gtk_tree_model_get_path (combo_box->priv->model, &new_iter);
5532 if (gtk_tree_path_compare (old_path, new_path) == 0)
5535 gtk_tree_path_free (old_path);
5536 gtk_tree_path_free (new_path);
5541 gtk_combo_box_set_active_iter (combo_box, &new_iter);
5545 gtk_widget_error_bell (GTK_WIDGET (combo_box));
5550 gtk_combo_box_mnemonic_activate (GtkWidget *widget,
5551 gboolean group_cycling)
5553 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
5555 if (combo_box->priv->has_entry)
5559 child = gtk_bin_get_child (GTK_BIN (combo_box));
5561 gtk_widget_grab_focus (child);
5564 gtk_widget_grab_focus (combo_box->priv->button);
5570 gtk_combo_box_grab_focus (GtkWidget *widget)
5572 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
5574 if (combo_box->priv->has_entry)
5578 child = gtk_bin_get_child (GTK_BIN (combo_box));
5580 gtk_widget_grab_focus (child);
5583 gtk_widget_grab_focus (combo_box->priv->button);
5587 gtk_combo_box_destroy (GtkWidget *widget)
5589 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
5591 if (combo_box->priv->popup_idle_id > 0)
5593 g_source_remove (combo_box->priv->popup_idle_id);
5594 combo_box->priv->popup_idle_id = 0;
5597 gtk_combo_box_popdown (combo_box);
5599 if (combo_box->priv->row_separator_destroy)
5600 combo_box->priv->row_separator_destroy (combo_box->priv->row_separator_data);
5602 combo_box->priv->row_separator_func = NULL;
5603 combo_box->priv->row_separator_data = NULL;
5604 combo_box->priv->row_separator_destroy = NULL;
5606 GTK_WIDGET_CLASS (gtk_combo_box_parent_class)->destroy (widget);
5607 combo_box->priv->cell_view = NULL;
5611 gtk_combo_box_entry_contents_changed (GtkEntry *entry,
5614 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
5617 * Fixes regression reported in bug #574059. The old functionality relied on
5618 * bug #572478. As a bugfix, we now emit the "changed" signal ourselves
5619 * when the selection was already set to -1.
5621 if (gtk_combo_box_get_active(combo_box) == -1)
5622 g_signal_emit_by_name (combo_box, "changed");
5624 gtk_combo_box_set_active (combo_box, -1);
5628 gtk_combo_box_entry_active_changed (GtkComboBox *combo_box,
5631 GtkComboBoxPrivate *priv = combo_box->priv;
5632 GtkTreeModel *model;
5635 if (gtk_combo_box_get_active_iter (combo_box, &iter))
5637 GtkEntry *entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (combo_box)));
5641 GValue value = {0,};
5643 g_signal_handlers_block_by_func (entry,
5644 gtk_combo_box_entry_contents_changed,
5647 model = gtk_combo_box_get_model (combo_box);
5649 gtk_tree_model_get_value (model, &iter,
5650 priv->text_column, &value);
5651 g_object_set_property (G_OBJECT (entry), "text", &value);
5652 g_value_unset (&value);
5654 g_signal_handlers_unblock_by_func (entry,
5655 gtk_combo_box_entry_contents_changed,
5662 gtk_combo_box_constructor (GType type,
5663 guint n_construct_properties,
5664 GObjectConstructParam *construct_properties)
5667 GtkComboBox *combo_box;
5668 GtkComboBoxPrivate *priv;
5670 object = G_OBJECT_CLASS (gtk_combo_box_parent_class)->constructor
5671 (type, n_construct_properties, construct_properties);
5673 combo_box = GTK_COMBO_BOX (object);
5674 priv = combo_box->priv;
5676 if (priv->has_entry)
5680 entry = gtk_entry_new ();
5681 gtk_widget_show (entry);
5682 gtk_container_add (GTK_CONTAINER (combo_box), entry);
5684 priv->text_renderer = gtk_cell_renderer_text_new ();
5685 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box),
5686 priv->text_renderer, TRUE);
5688 gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), -1);
5690 g_signal_connect (combo_box, "changed",
5691 G_CALLBACK (gtk_combo_box_entry_active_changed), NULL);
5699 gtk_combo_box_dispose(GObject* object)
5701 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
5703 if (GTK_IS_MENU (combo_box->priv->popup_widget))
5705 gtk_combo_box_menu_destroy (combo_box);
5706 gtk_menu_detach (GTK_MENU (combo_box->priv->popup_widget));
5707 combo_box->priv->popup_widget = NULL;
5710 G_OBJECT_CLASS (gtk_combo_box_parent_class)->dispose (object);
5714 gtk_combo_box_finalize (GObject *object)
5716 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
5719 if (GTK_IS_TREE_VIEW (combo_box->priv->tree_view))
5720 gtk_combo_box_list_destroy (combo_box);
5722 if (combo_box->priv->popup_window)
5723 gtk_widget_destroy (combo_box->priv->popup_window);
5725 gtk_combo_box_unset_model (combo_box);
5727 for (i = combo_box->priv->cells; i; i = i->next)
5729 ComboCellInfo *info = (ComboCellInfo *)i->data;
5730 GSList *list = info->attributes;
5733 info->destroy (info->func_data);
5735 while (list && list->next)
5737 g_free (list->data);
5738 list = list->next->next;
5740 g_slist_free (info->attributes);
5742 g_object_unref (info->cell);
5743 g_slice_free (ComboCellInfo, info);
5745 g_slist_free (combo_box->priv->cells);
5747 g_free (combo_box->priv->tearoff_title);
5749 G_OBJECT_CLASS (gtk_combo_box_parent_class)->finalize (object);
5754 gtk_cell_editable_key_press (GtkWidget *widget,
5758 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
5760 if (event->keyval == GDK_KEY_Escape)
5762 g_object_set (combo_box,
5763 "editing-canceled", TRUE,
5765 gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (combo_box));
5766 gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (combo_box));
5770 else if (event->keyval == GDK_KEY_Return ||
5771 event->keyval == GDK_KEY_ISO_Enter ||
5772 event->keyval == GDK_KEY_KP_Enter)
5774 gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (combo_box));
5775 gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (combo_box));
5784 popdown_idle (gpointer data)
5786 GtkComboBox *combo_box;
5788 combo_box = GTK_COMBO_BOX (data);
5790 gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (combo_box));
5791 gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (combo_box));
5793 g_object_unref (combo_box);
5799 popdown_handler (GtkWidget *widget,
5802 gdk_threads_add_idle (popdown_idle, g_object_ref (data));
5806 popup_idle (gpointer data)
5808 GtkComboBox *combo_box;
5810 combo_box = GTK_COMBO_BOX (data);
5812 if (GTK_IS_MENU (combo_box->priv->popup_widget) &&
5813 combo_box->priv->cell_view)
5814 g_signal_connect_object (combo_box->priv->popup_widget,
5815 "unmap", G_CALLBACK (popdown_handler),
5818 /* we unset this if a menu item is activated */
5819 g_object_set (combo_box,
5820 "editing-canceled", TRUE,
5822 gtk_combo_box_popup (combo_box);
5824 combo_box->priv->popup_idle_id = 0;
5825 combo_box->priv->activate_button = 0;
5826 combo_box->priv->activate_time = 0;
5832 gtk_combo_box_start_editing (GtkCellEditable *cell_editable,
5835 GtkComboBox *combo_box = GTK_COMBO_BOX (cell_editable);
5838 combo_box->priv->is_cell_renderer = TRUE;
5840 if (combo_box->priv->cell_view)
5842 g_signal_connect_object (combo_box->priv->button, "key-press-event",
5843 G_CALLBACK (gtk_cell_editable_key_press),
5846 gtk_widget_grab_focus (combo_box->priv->button);
5850 child = gtk_bin_get_child (GTK_BIN (combo_box));
5852 g_signal_connect_object (child, "key-press-event",
5853 G_CALLBACK (gtk_cell_editable_key_press),
5856 gtk_widget_grab_focus (child);
5857 gtk_widget_set_can_focus (combo_box->priv->button, FALSE);
5860 /* we do the immediate popup only for the optionmenu-like
5863 if (combo_box->priv->is_cell_renderer &&
5864 combo_box->priv->cell_view && !combo_box->priv->tree_view)
5866 if (event && event->type == GDK_BUTTON_PRESS)
5868 GdkEventButton *event_button = (GdkEventButton *)event;
5870 combo_box->priv->activate_button = event_button->button;
5871 combo_box->priv->activate_time = event_button->time;
5874 combo_box->priv->popup_idle_id =
5875 gdk_threads_add_idle (popup_idle, combo_box);
5881 * gtk_combo_box_get_add_tearoffs:
5882 * @combo_box: a #GtkComboBox
5884 * Gets the current value of the :add-tearoffs property.
5886 * Return value: the current value of the :add-tearoffs property.
5889 gtk_combo_box_get_add_tearoffs (GtkComboBox *combo_box)
5891 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
5893 return combo_box->priv->add_tearoffs;
5897 * gtk_combo_box_set_add_tearoffs:
5898 * @combo_box: a #GtkComboBox
5899 * @add_tearoffs: %TRUE to add tearoff menu items
5901 * Sets whether the popup menu should have a tearoff
5907 gtk_combo_box_set_add_tearoffs (GtkComboBox *combo_box,
5908 gboolean add_tearoffs)
5910 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5912 add_tearoffs = add_tearoffs != FALSE;
5914 if (combo_box->priv->add_tearoffs != add_tearoffs)
5916 combo_box->priv->add_tearoffs = add_tearoffs;
5917 gtk_combo_box_check_appearance (combo_box);
5918 gtk_combo_box_relayout (combo_box);
5919 g_object_notify (G_OBJECT (combo_box), "add-tearoffs");
5924 * gtk_combo_box_get_title:
5925 * @combo_box: a #GtkComboBox
5927 * Gets the current title of the menu in tearoff mode. See
5928 * gtk_combo_box_set_add_tearoffs().
5930 * Returns: the menu's title in tearoff mode. This is an internal copy of the
5931 * string which must not be freed.
5935 G_CONST_RETURN gchar*
5936 gtk_combo_box_get_title (GtkComboBox *combo_box)
5938 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
5940 return combo_box->priv->tearoff_title;
5944 gtk_combo_box_update_title (GtkComboBox *combo_box)
5946 gtk_combo_box_check_appearance (combo_box);
5948 if (combo_box->priv->popup_widget &&
5949 GTK_IS_MENU (combo_box->priv->popup_widget))
5950 gtk_menu_set_title (GTK_MENU (combo_box->priv->popup_widget),
5951 combo_box->priv->tearoff_title);
5955 * gtk_combo_box_set_title:
5956 * @combo_box: a #GtkComboBox
5957 * @title: a title for the menu in tearoff mode
5959 * Sets the menu's title in tearoff mode.
5964 gtk_combo_box_set_title (GtkComboBox *combo_box,
5967 GtkComboBoxPrivate *priv;
5969 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5971 priv = combo_box->priv;
5973 if (strcmp (title ? title : "",
5974 priv->tearoff_title ? priv->tearoff_title : "") != 0)
5976 g_free (priv->tearoff_title);
5977 priv->tearoff_title = g_strdup (title);
5979 gtk_combo_box_update_title (combo_box);
5981 g_object_notify (G_OBJECT (combo_box), "tearoff-title");
5987 * gtk_combo_box_set_popup_fixed_width:
5988 * @combo_box: a #GtkComboBox
5989 * @fixed: whether to use a fixed popup width
5991 * Specifies whether the popup's width should be a fixed width
5992 * matching the allocated width of the combo box.
5997 gtk_combo_box_set_popup_fixed_width (GtkComboBox *combo_box,
6000 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
6002 if (combo_box->priv->popup_fixed_width != fixed)
6004 combo_box->priv->popup_fixed_width = fixed;
6006 g_object_notify (G_OBJECT (combo_box), "popup-fixed-width");
6011 * gtk_combo_box_get_popup_fixed_width:
6012 * @combo_box: a #GtkComboBox
6014 * Gets whether the popup uses a fixed width matching
6015 * the allocated width of the combo box.
6017 * Returns: %TRUE if the popup uses a fixed width
6022 gtk_combo_box_get_popup_fixed_width (GtkComboBox *combo_box)
6024 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
6026 return combo_box->priv->popup_fixed_width;
6031 * gtk_combo_box_get_popup_accessible:
6032 * @combo_box: a #GtkComboBox
6034 * Gets the accessible object corresponding to the combo box's popup.
6036 * This function is mostly intended for use by accessibility technologies;
6037 * applications should have little use for it.
6039 * Returns: (transfer none): the accessible object corresponding
6040 * to the combo box's popup.
6045 gtk_combo_box_get_popup_accessible (GtkComboBox *combo_box)
6049 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
6051 if (combo_box->priv->popup_widget)
6053 atk_obj = gtk_widget_get_accessible (combo_box->priv->popup_widget);
6061 * gtk_combo_box_get_row_separator_func:
6062 * @combo_box: a #GtkComboBox
6064 * Returns the current row separator function.
6066 * Return value: the current row separator function.
6070 GtkTreeViewRowSeparatorFunc
6071 gtk_combo_box_get_row_separator_func (GtkComboBox *combo_box)
6073 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
6075 return combo_box->priv->row_separator_func;
6079 * gtk_combo_box_set_row_separator_func:
6080 * @combo_box: a #GtkComboBox
6081 * @func: a #GtkTreeViewRowSeparatorFunc
6082 * @data: (allow-none): user data to pass to @func, or %NULL
6083 * @destroy: (allow-none): destroy notifier for @data, or %NULL
6085 * Sets the row separator function, which is used to determine
6086 * whether a row should be drawn as a separator. If the row separator
6087 * function is %NULL, no separators are drawn. This is the default value.
6092 gtk_combo_box_set_row_separator_func (GtkComboBox *combo_box,
6093 GtkTreeViewRowSeparatorFunc func,
6095 GDestroyNotify destroy)
6097 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
6099 if (combo_box->priv->row_separator_destroy)
6100 combo_box->priv->row_separator_destroy (combo_box->priv->row_separator_data);
6102 combo_box->priv->row_separator_func = func;
6103 combo_box->priv->row_separator_data = data;
6104 combo_box->priv->row_separator_destroy = destroy;
6106 if (combo_box->priv->tree_view)
6107 gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (combo_box->priv->tree_view),
6110 gtk_combo_box_relayout (combo_box);
6112 gtk_widget_queue_draw (GTK_WIDGET (combo_box));
6116 * gtk_combo_box_set_button_sensitivity:
6117 * @combo_box: a #GtkComboBox
6118 * @sensitivity: specify the sensitivity of the dropdown button
6120 * Sets whether the dropdown button of the combo box should be
6121 * always sensitive (%GTK_SENSITIVITY_ON), never sensitive (%GTK_SENSITIVITY_OFF)
6122 * or only if there is at least one item to display (%GTK_SENSITIVITY_AUTO).
6127 gtk_combo_box_set_button_sensitivity (GtkComboBox *combo_box,
6128 GtkSensitivityType sensitivity)
6130 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
6132 if (combo_box->priv->button_sensitivity != sensitivity)
6134 combo_box->priv->button_sensitivity = sensitivity;
6135 gtk_combo_box_update_sensitivity (combo_box);
6137 g_object_notify (G_OBJECT (combo_box), "button-sensitivity");
6142 * gtk_combo_box_get_button_sensitivity:
6143 * @combo_box: a #GtkComboBox
6145 * Returns whether the combo box sets the dropdown button
6146 * sensitive or not when there are no items in the model.
6148 * Return Value: %GTK_SENSITIVITY_ON if the dropdown button
6149 * is sensitive when the model is empty, %GTK_SENSITIVITY_OFF
6150 * if the button is always insensitive or
6151 * %GTK_SENSITIVITY_AUTO if it is only sensitive as long as
6152 * the model has one item to be selected.
6157 gtk_combo_box_get_button_sensitivity (GtkComboBox *combo_box)
6159 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
6161 return combo_box->priv->button_sensitivity;
6166 * gtk_combo_box_get_has_entry:
6167 * @combo_box: a #GtkComboBox
6169 * Returns whether the combo box has an entry.
6171 * Return Value: whether there is an entry in @combo_box.
6176 gtk_combo_box_get_has_entry (GtkComboBox *combo_box)
6178 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
6180 return combo_box->priv->has_entry;
6184 * gtk_combo_box_set_entry_text_column:
6185 * @combo_box: A #GtkComboBox
6186 * @text_column: A column in @model to get the strings from for
6187 * the internal entry
6189 * Sets the model column which @combo_box should use to get strings from
6190 * to be @text_column. The column @text_column in the model of @combo_box
6191 * must be of type %G_TYPE_STRING.
6193 * This is only relevant if @combo_box has been created with
6194 * #GtkComboBox:has-entry as %TRUE.
6199 gtk_combo_box_set_entry_text_column (GtkComboBox *combo_box,
6202 GtkComboBoxPrivate *priv = combo_box->priv;
6203 GtkTreeModel *model;
6205 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
6207 model = gtk_combo_box_get_model (combo_box);
6209 g_return_if_fail (text_column >= 0);
6210 g_return_if_fail (model == NULL || text_column < gtk_tree_model_get_n_columns (model));
6212 priv->text_column = text_column;
6214 if (priv->text_renderer != NULL)
6215 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box),
6216 priv->text_renderer,
6217 "text", text_column,
6222 * gtk_combo_box_get_entry_text_column:
6223 * @combo_box: A #GtkComboBox.
6225 * Returns the column which @combo_box is using to get the strings
6226 * from to display in the internal entry.
6228 * Return value: A column in the data source model of @combo_box.
6233 gtk_combo_box_get_entry_text_column (GtkComboBox *combo_box)
6235 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), 0);
6237 return combo_box->priv->text_column;
6241 * gtk_combo_box_set_focus_on_click:
6242 * @combo: a #GtkComboBox
6243 * @focus_on_click: whether the combo box grabs focus when clicked
6246 * Sets whether the combo box will grab focus when it is clicked with
6247 * the mouse. Making mouse clicks not grab focus is useful in places
6248 * like toolbars where you don't want the keyboard focus removed from
6249 * the main area of the application.
6254 gtk_combo_box_set_focus_on_click (GtkComboBox *combo_box,
6255 gboolean focus_on_click)
6257 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
6259 focus_on_click = focus_on_click != FALSE;
6261 if (combo_box->priv->focus_on_click != focus_on_click)
6263 combo_box->priv->focus_on_click = focus_on_click;
6265 if (combo_box->priv->button)
6266 gtk_button_set_focus_on_click (GTK_BUTTON (combo_box->priv->button),
6269 g_object_notify (G_OBJECT (combo_box), "focus-on-click");
6274 * gtk_combo_box_get_focus_on_click:
6275 * @combo: a #GtkComboBox
6277 * Returns whether the combo box grabs focus when it is clicked
6278 * with the mouse. See gtk_combo_box_set_focus_on_click().
6280 * Return value: %TRUE if the combo box grabs focus when it is
6281 * clicked with the mouse.
6286 gtk_combo_box_get_focus_on_click (GtkComboBox *combo_box)
6288 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
6290 return combo_box->priv->focus_on_click;
6295 gtk_combo_box_buildable_custom_tag_start (GtkBuildable *buildable,
6296 GtkBuilder *builder,
6298 const gchar *tagname,
6299 GMarkupParser *parser,
6302 if (parent_buildable_iface->custom_tag_start (buildable, builder, child,
6303 tagname, parser, data))
6306 return _gtk_cell_layout_buildable_custom_tag_start (buildable, builder, child,
6307 tagname, parser, data);
6311 gtk_combo_box_buildable_custom_tag_end (GtkBuildable *buildable,
6312 GtkBuilder *builder,
6314 const gchar *tagname,
6317 if (strcmp (tagname, "attributes") == 0)
6318 _gtk_cell_layout_buildable_custom_tag_end (buildable, builder, child, tagname,
6321 parent_buildable_iface->custom_tag_end (buildable, builder, child, tagname,
6326 gtk_combo_box_buildable_get_internal_child (GtkBuildable *buildable,
6327 GtkBuilder *builder,
6328 const gchar *childname)
6330 GtkComboBox *combo_box = GTK_COMBO_BOX (buildable);
6332 if (combo_box->priv->has_entry && strcmp (childname, "entry") == 0)
6333 return G_OBJECT (gtk_bin_get_child (GTK_BIN (buildable)));
6335 return parent_buildable_iface->get_internal_child (buildable, builder, childname);
6339 gtk_combo_box_remeasure (GtkComboBox *combo_box)
6341 GtkComboBoxPrivate *priv = combo_box->priv;
6346 !gtk_tree_model_get_iter_first (priv->model, &iter))
6349 priv->minimum_width = priv->natural_width = 0;
6351 path = gtk_tree_path_new_from_indices (0, -1);
6355 gint row_min = 0, row_nat = 0;
6357 if (priv->cell_view)
6358 gtk_cell_view_get_desired_width_of_row (GTK_CELL_VIEW (priv->cell_view),
6359 path, &row_min, &row_nat);
6361 priv->minimum_width = MAX (priv->minimum_width, row_min);
6362 priv->natural_width = MAX (priv->natural_width, row_nat);
6364 gtk_tree_path_next (path);
6366 while (gtk_tree_model_iter_next (priv->model, &iter));
6368 gtk_tree_path_free (path);
6373 gtk_combo_box_measure_height_for_width (GtkComboBox *combo_box,
6379 GtkComboBoxPrivate *priv = combo_box->priv;
6382 gint child_min, child_nat;
6384 child = gtk_bin_get_child (GTK_BIN (combo_box));
6386 gtk_widget_get_preferred_height_for_width (child, avail_width,
6387 &child_min, &child_nat);
6390 !gtk_tree_model_get_iter_first (priv->model, &iter))
6393 path = gtk_tree_path_new_from_indices (0, -1);
6397 gint row_min = 0, row_nat = 0;
6399 if (priv->cell_view)
6400 gtk_cell_view_get_desired_height_for_width_of_row (GTK_CELL_VIEW (priv->cell_view),
6402 &row_min, &row_nat);
6404 child_min = MAX (child_min, row_min);
6405 child_nat = MAX (child_nat, row_nat);
6407 gtk_tree_path_next (path);
6409 while (gtk_tree_model_iter_next (priv->model, &iter));
6411 gtk_tree_path_free (path);
6416 *min_height = child_min;
6418 *nat_height = child_nat;
6423 gtk_combo_box_get_preferred_width (GtkWidget *widget,
6427 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
6428 GtkComboBoxPrivate *priv = combo_box->priv;
6429 gint focus_width, focus_pad;
6430 gint font_size, arrow_size;
6431 PangoContext *context;
6432 PangoFontMetrics *metrics;
6433 PangoFontDescription *font_desc;
6435 gint minimum_width, natural_width;
6436 gint child_min, child_nat;
6437 GtkStyleContext *style_context;
6438 GtkStateFlags state;
6441 child = gtk_bin_get_child (GTK_BIN (widget));
6444 gtk_widget_get_preferred_width (child, &child_min, &child_nat);
6445 gtk_combo_box_remeasure (combo_box);
6447 child_min = MAX (child_min, priv->minimum_width);
6448 child_nat = MAX (child_nat, priv->natural_width);
6450 gtk_widget_style_get (GTK_WIDGET (widget),
6451 "focus-line-width", &focus_width,
6452 "focus-padding", &focus_pad,
6453 "arrow-size", &arrow_size,
6456 style_context = gtk_widget_get_style_context (widget);
6457 state = gtk_widget_get_state_flags (widget);
6459 gtk_style_context_get (style_context, state,
6461 "border-width", &border,
6464 context = gtk_widget_get_pango_context (GTK_WIDGET (widget));
6465 metrics = pango_context_get_metrics (context, font_desc,
6466 pango_context_get_language (context));
6467 font_size = PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) +
6468 pango_font_metrics_get_descent (metrics));
6469 pango_font_metrics_unref (metrics);
6470 pango_font_description_free (font_desc);
6472 arrow_size = MAX (arrow_size, font_size);
6474 gtk_widget_set_size_request (priv->arrow, arrow_size, arrow_size);
6476 if (!priv->tree_view)
6480 if (priv->cell_view)
6482 gint sep_width, arrow_width;
6483 gint border_width, xpad;
6484 GtkBorder button_border;
6486 border_width = gtk_container_get_border_width (GTK_CONTAINER (combo_box));
6487 get_widget_border (priv->button, &button_border);
6489 gtk_widget_get_preferred_width (priv->separator, &sep_width, NULL);
6490 gtk_widget_get_preferred_width (priv->arrow, &arrow_width, NULL);
6492 xpad = 2 * (border_width + focus_width + focus_pad) +
6493 button_border.left + button_border.right;
6495 minimum_width = child_min + sep_width + arrow_width + xpad;
6496 natural_width = child_nat + sep_width + arrow_width + xpad;
6500 gint but_width, but_nat_width;
6502 gtk_widget_get_preferred_width (priv->button,
6503 &but_width, &but_nat_width);
6505 minimum_width = child_min + but_width;
6506 natural_width = child_nat + but_nat_width;
6512 gint button_width, button_nat_width;
6514 /* sample + frame */
6515 minimum_width = child_min;
6516 natural_width = child_nat;
6518 minimum_width += 2 * focus_width;
6519 natural_width += 2 * focus_width;
6521 if (priv->cell_view_frame)
6523 if (priv->has_frame)
6525 gint border_width, xpad;
6526 GtkBorder frame_border;
6528 border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->cell_view_frame));
6529 get_widget_border (priv->cell_view_frame, &frame_border);
6530 xpad = (2 * border_width) + frame_border.left + frame_border.right;
6532 minimum_width += xpad;
6533 natural_width += xpad;
6538 gtk_widget_get_preferred_width (priv->button,
6539 &button_width, &button_nat_width);
6541 minimum_width += button_width;
6542 natural_width += button_nat_width;
6545 minimum_width += border->left + border->right;
6546 natural_width += border->left + border->right;
6547 gtk_border_free (border);
6550 *minimum_size = minimum_width;
6553 *natural_size = natural_width;
6557 gtk_combo_box_get_preferred_height (GtkWidget *widget,
6563 /* Combo box is height-for-width only
6564 * (so we always just reserve enough height for the minimum width) */
6565 GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, &min_width, NULL);
6566 GTK_WIDGET_GET_CLASS (widget)->get_preferred_height_for_width (widget, min_width, minimum_size, natural_size);
6570 gtk_combo_box_get_preferred_width_for_height (GtkWidget *widget,
6575 /* Combo box is height-for-width only
6576 * (so we assume we always reserved enough height for the minimum width) */
6577 GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, minimum_size, natural_size);
6582 gtk_combo_box_get_preferred_height_for_width (GtkWidget *widget,
6587 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
6588 GtkComboBoxPrivate *priv = combo_box->priv;
6589 gint focus_width, focus_pad;
6590 gint min_height, nat_height;
6594 gtk_widget_style_get (GTK_WIDGET (widget),
6595 "focus-line-width", &focus_width,
6596 "focus-padding", &focus_pad,
6599 get_widget_border (widget, &border);
6600 size = avail_size - border.left;
6602 if (!priv->tree_view)
6605 if (priv->cell_view)
6607 /* calculate x/y padding and separator/arrow size */
6608 gint sep_width, arrow_width, sep_height, arrow_height;
6609 gint border_width, xpad, ypad;
6610 GtkBorder button_border;
6612 border_width = gtk_container_get_border_width (GTK_CONTAINER (combo_box));
6613 get_widget_border (priv->button, &button_border);
6615 gtk_widget_get_preferred_width (priv->separator, &sep_width, NULL);
6616 gtk_widget_get_preferred_width (priv->arrow, &arrow_width, NULL);
6617 gtk_widget_get_preferred_height_for_width (priv->separator,
6618 sep_width, &sep_height, NULL);
6619 gtk_widget_get_preferred_height_for_width (priv->arrow,
6620 arrow_width, &arrow_height, NULL);
6622 xpad = 2 * (border_width + focus_width + focus_pad) +
6623 button_border.left + button_border.right;
6624 ypad = 2 * (border_width + focus_width + focus_pad) +
6625 button_border.top + button_border.bottom;
6627 size -= sep_width + arrow_width + xpad;
6629 gtk_combo_box_measure_height_for_width (combo_box, size, &min_height, &nat_height);
6631 arrow_height = MAX (arrow_height, sep_height);
6632 min_height = MAX (min_height, arrow_height);
6633 nat_height = MAX (nat_height, arrow_height);
6640 /* there is a custom child widget inside (no priv->cell_view) */
6641 gint but_width, but_height;
6643 gtk_widget_get_preferred_width (priv->button, &but_width, NULL);
6644 gtk_widget_get_preferred_height_for_width (priv->button,
6645 but_width, &but_height, NULL);
6649 gtk_combo_box_measure_height_for_width (combo_box, size, &min_height, &nat_height);
6651 min_height = MAX (min_height, but_height);
6652 nat_height = MAX (nat_height, but_height);
6658 gint but_width, but_height;
6659 gint xpad = 0, ypad = 0;
6661 gtk_widget_get_preferred_width (priv->button, &but_width, NULL);
6662 gtk_widget_get_preferred_height_for_width (priv->button,
6663 but_width, &but_height, NULL);
6665 if (priv->cell_view_frame && priv->has_frame)
6667 GtkBorder frame_border;
6670 border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->cell_view_frame));
6671 get_widget_border (GTK_WIDGET (priv->cell_view_frame), &frame_border);
6673 xpad = (2 * border_width) + border.left + frame_border.right;
6674 ypad = (2 * border_width) + border.top + frame_border.bottom;
6678 size -= 2 * focus_width;
6681 gtk_combo_box_measure_height_for_width (combo_box, size, &min_height, &nat_height);
6683 min_height = MAX (min_height, but_height);
6684 nat_height = MAX (nat_height, but_height);
6690 min_height += border.top + border.bottom;
6691 nat_height += border.top + border.bottom;
6694 *minimum_size = min_height;
6697 *natural_size = nat_height;
6701 * gtk_combo_box_set_id_column:
6702 * @combo_box: A #GtkComboBox
6703 * @id_column: A column in @model to get string IDs for values from
6705 * Sets the model column which @combo_box should use to get string IDs
6706 * for values from. The column @id_column in the model of @combo_box
6707 * must be of type %G_TYPE_STRING.
6712 gtk_combo_box_set_id_column (GtkComboBox *combo_box,
6715 GtkComboBoxPrivate *priv = combo_box->priv;
6716 GtkTreeModel *model;
6718 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
6720 if (id_column != priv->id_column)
6722 model = gtk_combo_box_get_model (combo_box);
6724 g_return_if_fail (id_column >= 0);
6725 g_return_if_fail (model == NULL ||
6726 id_column < gtk_tree_model_get_n_columns (model));
6728 priv->id_column = id_column;
6730 g_object_notify (G_OBJECT (combo_box), "id-column");
6731 g_object_notify (G_OBJECT (combo_box), "active-id");
6736 * gtk_combo_box_get_id_column:
6737 * @combo_box: A #GtkComboBox
6739 * Returns the column which @combo_box is using to get string IDs
6742 * Return value: A column in the data source model of @combo_box.
6747 gtk_combo_box_get_id_column (GtkComboBox *combo_box)
6749 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), 0);
6751 return combo_box->priv->id_column;
6755 * gtk_combo_box_get_active_id:
6756 * @combo_box: a #GtkComboBox
6758 * Returns the ID of the active row of @combo_box. This value is taken
6759 * from the active row and the column specified by the 'id-column'
6760 * property of @combo_box (see gtk_combo_box_set_id_column()).
6762 * The returned value is an interned string which means that you can
6763 * compare the pointer by value to other interned strings and that you
6766 * If the 'id-column' property of @combo_box is not set or if no row is
6767 * selected then %NULL is returned.
6769 * Return value: the ID of the active row, or %NULL
6774 gtk_combo_box_get_active_id (GtkComboBox *combo_box)
6776 GtkTreeModel *model;
6780 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), 0);
6782 column = combo_box->priv->id_column;
6787 model = gtk_combo_box_get_model (combo_box);
6788 g_return_val_if_fail (gtk_tree_model_get_column_type (model, column) ==
6789 G_TYPE_STRING, NULL);
6791 if (gtk_combo_box_get_active_iter (combo_box, &iter))
6793 const gchar *interned;
6796 gtk_tree_model_get (model, &iter, column, &id, -1);
6797 interned = g_intern_string (id);
6807 * gtk_combo_box_set_active_id:
6808 * @combo_box: a #GtkComboBox
6809 * @active_id: the ID of the row to select
6811 * Changes the active row of @combo_box to the one that has an ID equal to @id.
6813 * If the 'id-column' property of @combo_box is unset or if no row has
6814 * the given ID then nothing happens.
6819 gtk_combo_box_set_active_id (GtkComboBox *combo_box,
6820 const gchar *active_id)
6822 GtkTreeModel *model;
6826 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
6828 column = combo_box->priv->id_column;
6833 model = gtk_combo_box_get_model (combo_box);
6834 g_return_if_fail (gtk_tree_model_get_column_type (model, column) ==
6837 if (gtk_tree_model_get_iter_first (model, &iter))
6842 gtk_tree_model_get (model, &iter, column, &id, -1);
6843 match = strcmp (id, active_id) == 0;
6848 gtk_combo_box_set_active_iter (combo_box, &iter);
6851 } while (gtk_tree_model_iter_next (model, &iter));