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 "gtktreeprivate.h"
58 * @Short_description: A widget used to choose from a list of items
60 * @See_also: #GtkComboBoxText, #GtkTreeModel, #GtkCellRenderer
62 * A GtkComboBox is a widget that allows the user to choose from a list of
63 * valid choices. The GtkComboBox displays the selected choice. When
64 * activated, the GtkComboBox displays a popup which allows the user to
65 * make a new choice. The style in which the selected value is displayed,
66 * and the style of the popup is determined by the current theme. It may
67 * be similar to a Windows-style combo box.
69 * The GtkComboBox uses the model-view pattern; the list of valid choices
70 * is specified in the form of a tree model, and the display of the choices
71 * can be adapted to the data in the model by using cell renderers, as you
72 * would in a tree view. This is possible since GtkComboBox implements the
73 * #GtkCellLayout interface. The tree model holding the valid choices is
74 * not restricted to a flat list, it can be a real tree, and the popup will
75 * reflect the tree structure.
77 * For a simple list of textual choices, the model-view API of GtkComboBox
78 * can be a bit overwhelming. In this case, #GtkComboBoxText offers a
83 /* WELCOME, to THE house of evil code */
85 typedef struct _ComboCellInfo ComboCellInfo;
88 GtkCellRenderer *cell;
91 GtkCellLayoutDataFunc func;
93 GDestroyNotify destroy;
100 struct _GtkComboBoxPrivate
108 GtkShadowType shadow_type;
110 gint active; /* Only temporary */
111 GtkTreeRowReference *active_row;
113 GtkWidget *tree_view;
114 GtkTreeViewColumn *column;
116 GtkWidget *cell_view;
117 GtkWidget *cell_view_frame;
122 GtkWidget *separator;
124 GtkWidget *popup_widget;
125 GtkWidget *popup_window;
126 GtkWidget *scrolled_window;
133 guint activate_button;
134 guint32 activate_time;
136 guint resize_idle_id;
141 /* For "has-entry" specific behavior we track
142 * an automated cell renderer and text column */
144 GtkCellRenderer *text_renderer;
150 guint popup_in_progress : 1;
151 guint popup_shown : 1;
152 guint add_tearoffs : 1;
154 guint is_cell_renderer : 1;
155 guint editing_canceled : 1;
156 guint auto_scroll : 1;
157 guint focus_on_click : 1;
158 guint button_sensitivity : 2;
160 guint popup_fixed_width : 1;
162 GtkTreeViewRowSeparatorFunc row_separator_func;
163 gpointer row_separator_data;
164 GDestroyNotify row_separator_destroy;
166 GdkDevice *grab_pointer;
167 GdkDevice *grab_keyboard;
169 gchar *tearoff_title;
172 /* While debugging this evil code, I have learned that
173 * there are actually 4 modes to this widget, which can
174 * be characterized as follows
176 * 1) menu mode, no child added
179 * cell_view -> GtkCellView, regular child
180 * cell_view_frame -> NULL
181 * button -> GtkToggleButton set_parent to combo
182 * arrow -> GtkArrow set_parent to button
183 * separator -> GtkVSepator set_parent to button
184 * popup_widget -> GtkMenu
185 * popup_window -> NULL
186 * scrolled_window -> NULL
188 * 2) menu mode, child added
192 * cell_view_frame -> NULL
193 * button -> GtkToggleButton set_parent to combo
194 * arrow -> GtkArrow, child of button
196 * popup_widget -> GtkMenu
197 * popup_window -> NULL
198 * scrolled_window -> NULL
200 * 3) list mode, no child added
202 * tree_view -> GtkTreeView, child of scrolled_window
203 * cell_view -> GtkCellView, regular child
204 * cell_view_frame -> GtkFrame, set parent to combo
205 * button -> GtkToggleButton, set_parent to combo
206 * arrow -> GtkArrow, child of button
208 * popup_widget -> tree_view
209 * popup_window -> GtkWindow
210 * scrolled_window -> GtkScrolledWindow, child of popup_window
212 * 4) list mode, child added
214 * tree_view -> GtkTreeView, child of scrolled_window
216 * cell_view_frame -> NULL
217 * button -> GtkToggleButton, set_parent to combo
218 * arrow -> GtkArrow, child of button
220 * popup_widget -> tree_view
221 * popup_window -> GtkWindow
222 * scrolled_window -> GtkScrolledWindow, child of popup_window
238 PROP_ROW_SPAN_COLUMN,
239 PROP_COLUMN_SPAN_COLUMN,
246 PROP_BUTTON_SENSITIVITY,
247 PROP_EDITING_CANCELED,
249 PROP_ENTRY_TEXT_COLUMN,
250 PROP_POPUP_FIXED_WIDTH,
255 static guint combo_box_signals[LAST_SIGNAL] = {0,};
257 #define BONUS_PADDING 4
258 #define SCROLL_TIME 100
262 static void gtk_combo_box_cell_layout_init (GtkCellLayoutIface *iface);
263 static void gtk_combo_box_cell_editable_init (GtkCellEditableIface *iface);
264 static GObject *gtk_combo_box_constructor (GType type,
265 guint n_construct_properties,
266 GObjectConstructParam *construct_properties);
267 static void gtk_combo_box_dispose (GObject *object);
268 static void gtk_combo_box_finalize (GObject *object);
269 static void gtk_combo_box_destroy (GtkWidget *widget);
271 static void gtk_combo_box_set_property (GObject *object,
275 static void gtk_combo_box_get_property (GObject *object,
280 static void gtk_combo_box_state_changed (GtkWidget *widget,
281 GtkStateType previous);
282 static void gtk_combo_box_grab_focus (GtkWidget *widget);
283 static void gtk_combo_box_style_updated (GtkWidget *widget);
284 static void gtk_combo_box_button_toggled (GtkWidget *widget,
286 static void gtk_combo_box_button_state_flags_changed (GtkWidget *widget,
287 GtkStateFlags previous,
289 static void gtk_combo_box_add (GtkContainer *container,
291 static void gtk_combo_box_remove (GtkContainer *container,
294 static ComboCellInfo *gtk_combo_box_get_cell_info (GtkComboBox *combo_box,
295 GtkCellRenderer *cell);
297 static void gtk_combo_box_menu_show (GtkWidget *menu,
299 static void gtk_combo_box_menu_hide (GtkWidget *menu,
302 static void gtk_combo_box_set_popup_widget (GtkComboBox *combo_box,
304 static void gtk_combo_box_menu_position_below (GtkMenu *menu,
309 static void gtk_combo_box_menu_position_over (GtkMenu *menu,
314 static void gtk_combo_box_menu_position (GtkMenu *menu,
320 static void gtk_combo_box_update_requested_width(GtkComboBox *combo_box,
322 static void gtk_combo_box_remeasure (GtkComboBox *combo_box);
324 static void gtk_combo_box_unset_model (GtkComboBox *combo_box);
326 static void gtk_combo_box_size_allocate (GtkWidget *widget,
327 GtkAllocation *allocation);
328 static void gtk_combo_box_forall (GtkContainer *container,
329 gboolean include_internals,
330 GtkCallback callback,
331 gpointer callback_data);
332 static gboolean gtk_combo_box_draw (GtkWidget *widget,
334 static gboolean gtk_combo_box_scroll_event (GtkWidget *widget,
335 GdkEventScroll *event);
336 static void gtk_combo_box_set_active_internal (GtkComboBox *combo_box,
339 static void gtk_combo_box_check_appearance (GtkComboBox *combo_box);
340 static void gtk_combo_box_real_move_active (GtkComboBox *combo_box,
341 GtkScrollType scroll);
342 static void gtk_combo_box_real_popup (GtkComboBox *combo_box);
343 static gboolean gtk_combo_box_real_popdown (GtkComboBox *combo_box);
345 /* listening to the model */
346 static void gtk_combo_box_model_row_inserted (GtkTreeModel *model,
350 static void gtk_combo_box_model_row_deleted (GtkTreeModel *model,
353 static void gtk_combo_box_model_rows_reordered (GtkTreeModel *model,
358 static void gtk_combo_box_model_row_changed (GtkTreeModel *model,
362 static void gtk_combo_box_model_row_expanded (GtkTreeModel *model,
368 static void gtk_combo_box_list_position (GtkComboBox *combo_box,
373 static void gtk_combo_box_list_setup (GtkComboBox *combo_box);
374 static void gtk_combo_box_list_destroy (GtkComboBox *combo_box);
376 static gboolean gtk_combo_box_list_button_released (GtkWidget *widget,
377 GdkEventButton *event,
379 static gboolean gtk_combo_box_list_key_press (GtkWidget *widget,
382 static gboolean gtk_combo_box_list_enter_notify (GtkWidget *widget,
383 GdkEventCrossing *event,
385 static void gtk_combo_box_list_auto_scroll (GtkComboBox *combo,
388 static gboolean gtk_combo_box_list_scroll_timeout (GtkComboBox *combo);
389 static gboolean gtk_combo_box_list_button_pressed (GtkWidget *widget,
390 GdkEventButton *event,
393 static gboolean gtk_combo_box_list_select_func (GtkTreeSelection *selection,
396 gboolean path_currently_selected,
399 static void gtk_combo_box_list_row_changed (GtkTreeModel *model,
403 static void gtk_combo_box_list_popup_resize (GtkComboBox *combo_box);
406 static void gtk_combo_box_menu_setup (GtkComboBox *combo_box,
407 gboolean add_children);
408 static void gtk_combo_box_menu_fill (GtkComboBox *combo_box);
409 static void gtk_combo_box_menu_fill_level (GtkComboBox *combo_box,
412 static void gtk_combo_box_update_title (GtkComboBox *combo_box);
413 static void gtk_combo_box_menu_destroy (GtkComboBox *combo_box);
415 static void gtk_combo_box_relayout_item (GtkComboBox *combo_box,
419 static void gtk_combo_box_relayout (GtkComboBox *combo_box);
421 static gboolean gtk_combo_box_menu_button_press (GtkWidget *widget,
422 GdkEventButton *event,
424 static void gtk_combo_box_menu_item_activate (GtkWidget *item,
427 static void gtk_combo_box_update_sensitivity (GtkComboBox *combo_box);
428 static void gtk_combo_box_menu_row_inserted (GtkTreeModel *model,
432 static void gtk_combo_box_menu_row_deleted (GtkTreeModel *model,
435 static void gtk_combo_box_menu_rows_reordered (GtkTreeModel *model,
440 static void gtk_combo_box_menu_row_changed (GtkTreeModel *model,
444 static gboolean gtk_combo_box_menu_key_press (GtkWidget *widget,
447 static void gtk_combo_box_menu_popup (GtkComboBox *combo_box,
449 guint32 activate_time);
450 static GtkWidget *gtk_cell_view_menu_item_new (GtkComboBox *combo_box,
455 static void gtk_combo_box_cell_layout_pack_start (GtkCellLayout *layout,
456 GtkCellRenderer *cell,
458 static void gtk_combo_box_cell_layout_pack_end (GtkCellLayout *layout,
459 GtkCellRenderer *cell,
461 static GList *gtk_combo_box_cell_layout_get_cells (GtkCellLayout *layout);
462 static void gtk_combo_box_cell_layout_clear (GtkCellLayout *layout);
463 static void gtk_combo_box_cell_layout_add_attribute (GtkCellLayout *layout,
464 GtkCellRenderer *cell,
465 const gchar *attribute,
467 static void gtk_combo_box_cell_layout_set_cell_data_func (GtkCellLayout *layout,
468 GtkCellRenderer *cell,
469 GtkCellLayoutDataFunc func,
471 GDestroyNotify destroy);
472 static void gtk_combo_box_cell_layout_clear_attributes (GtkCellLayout *layout,
473 GtkCellRenderer *cell);
474 static void gtk_combo_box_cell_layout_reorder (GtkCellLayout *layout,
475 GtkCellRenderer *cell,
477 static gboolean gtk_combo_box_mnemonic_activate (GtkWidget *widget,
478 gboolean group_cycling);
480 static void gtk_combo_box_sync_cells (GtkComboBox *combo_box,
481 GtkCellLayout *cell_layout);
482 static void combo_cell_data_func (GtkCellLayout *cell_layout,
483 GtkCellRenderer *cell,
484 GtkTreeModel *tree_model,
487 static void gtk_combo_box_child_show (GtkWidget *widget,
488 GtkComboBox *combo_box);
489 static void gtk_combo_box_child_hide (GtkWidget *widget,
490 GtkComboBox *combo_box);
492 /* GtkComboBox:has-entry callbacks */
493 static void gtk_combo_box_entry_contents_changed (GtkEntry *entry,
495 static void gtk_combo_box_entry_active_changed (GtkComboBox *combo_box,
499 /* GtkBuildable method implementation */
500 static GtkBuildableIface *parent_buildable_iface;
502 static void gtk_combo_box_buildable_init (GtkBuildableIface *iface);
503 static gboolean gtk_combo_box_buildable_custom_tag_start (GtkBuildable *buildable,
506 const gchar *tagname,
507 GMarkupParser *parser,
509 static void gtk_combo_box_buildable_custom_tag_end (GtkBuildable *buildable,
512 const gchar *tagname,
514 static GObject *gtk_combo_box_buildable_get_internal_child (GtkBuildable *buildable,
516 const gchar *childname);
519 /* GtkCellEditable method implementations */
520 static void gtk_combo_box_start_editing (GtkCellEditable *cell_editable,
523 static void gtk_combo_box_get_preferred_width (GtkWidget *widget,
526 static void gtk_combo_box_get_preferred_height (GtkWidget *widget,
529 static void gtk_combo_box_get_preferred_width_for_height (GtkWidget *widget,
533 static void gtk_combo_box_get_preferred_height_for_width (GtkWidget *widget,
539 G_DEFINE_TYPE_WITH_CODE (GtkComboBox, gtk_combo_box, GTK_TYPE_BIN,
540 G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
541 gtk_combo_box_cell_layout_init)
542 G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_EDITABLE,
543 gtk_combo_box_cell_editable_init)
544 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
545 gtk_combo_box_buildable_init))
550 gtk_combo_box_class_init (GtkComboBoxClass *klass)
552 GObjectClass *object_class;
553 GtkContainerClass *container_class;
554 GtkWidgetClass *widget_class;
555 GtkBindingSet *binding_set;
557 container_class = (GtkContainerClass *)klass;
558 container_class->forall = gtk_combo_box_forall;
559 container_class->add = gtk_combo_box_add;
560 container_class->remove = gtk_combo_box_remove;
562 widget_class = (GtkWidgetClass *)klass;
563 widget_class->size_allocate = gtk_combo_box_size_allocate;
564 widget_class->draw = gtk_combo_box_draw;
565 widget_class->scroll_event = gtk_combo_box_scroll_event;
566 widget_class->mnemonic_activate = gtk_combo_box_mnemonic_activate;
567 widget_class->grab_focus = gtk_combo_box_grab_focus;
568 widget_class->style_updated = gtk_combo_box_style_updated;
569 widget_class->state_changed = gtk_combo_box_state_changed;
570 widget_class->get_preferred_width = gtk_combo_box_get_preferred_width;
571 widget_class->get_preferred_height = gtk_combo_box_get_preferred_height;
572 widget_class->get_preferred_height_for_width = gtk_combo_box_get_preferred_height_for_width;
573 widget_class->get_preferred_width_for_height = gtk_combo_box_get_preferred_width_for_height;
574 widget_class->destroy = gtk_combo_box_destroy;
576 object_class = (GObjectClass *)klass;
577 object_class->constructor = gtk_combo_box_constructor;
578 object_class->dispose = gtk_combo_box_dispose;
579 object_class->finalize = gtk_combo_box_finalize;
580 object_class->set_property = gtk_combo_box_set_property;
581 object_class->get_property = gtk_combo_box_get_property;
585 * GtkComboBox::changed:
586 * @widget: the object which received the signal
588 * The changed signal is emitted when the active
589 * item is changed. The can be due to the user selecting
590 * a different item from the list, or due to a
591 * call to gtk_combo_box_set_active_iter().
592 * It will also be emitted while typing into the entry of a combo box
597 combo_box_signals[CHANGED] =
598 g_signal_new (I_("changed"),
599 G_OBJECT_CLASS_TYPE (klass),
601 G_STRUCT_OFFSET (GtkComboBoxClass, changed),
603 g_cclosure_marshal_VOID__VOID,
606 * GtkComboBox::move-active:
607 * @widget: the object that received the signal
608 * @scroll_type: a #GtkScrollType
610 * The ::move-active signal is a
611 * <link linkend="keybinding-signals">keybinding signal</link>
612 * which gets emitted to move the active selection.
616 combo_box_signals[MOVE_ACTIVE] =
617 g_signal_new_class_handler (I_("move-active"),
618 G_OBJECT_CLASS_TYPE (klass),
619 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
620 G_CALLBACK (gtk_combo_box_real_move_active),
622 g_cclosure_marshal_VOID__ENUM,
624 GTK_TYPE_SCROLL_TYPE);
627 * GtkComboBox::popup:
628 * @widget: the object that received the signal
630 * The ::popup signal is a
631 * <link linkend="keybinding-signals">keybinding signal</link>
632 * which gets emitted to popup the combo box list.
634 * The default binding for this signal is Alt+Down.
638 combo_box_signals[POPUP] =
639 g_signal_new_class_handler (I_("popup"),
640 G_OBJECT_CLASS_TYPE (klass),
641 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
642 G_CALLBACK (gtk_combo_box_real_popup),
644 g_cclosure_marshal_VOID__VOID,
647 * GtkComboBox::popdown:
648 * @button: the object which received the signal
650 * The ::popdown signal is a
651 * <link linkend="keybinding-signals">keybinding signal</link>
652 * which gets emitted to popdown the combo box list.
654 * The default bindings for this signal are Alt+Up and Escape.
658 combo_box_signals[POPDOWN] =
659 g_signal_new_class_handler (I_("popdown"),
660 G_OBJECT_CLASS_TYPE (klass),
661 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
662 G_CALLBACK (gtk_combo_box_real_popdown),
664 _gtk_marshal_BOOLEAN__VOID,
668 binding_set = gtk_binding_set_by_class (widget_class);
670 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Down, GDK_MOD1_MASK,
672 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Down, GDK_MOD1_MASK,
675 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Up, GDK_MOD1_MASK,
677 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Up, GDK_MOD1_MASK,
679 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0,
682 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Up, 0,
684 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_UP);
685 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Up, 0,
687 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_UP);
688 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Up, 0,
690 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_UP);
691 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Page_Up, 0,
693 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_UP);
694 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Home, 0,
696 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_START);
697 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Home, 0,
699 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_START);
701 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Down, 0,
703 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_DOWN);
704 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Down, 0,
706 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_DOWN);
707 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Down, 0,
709 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_DOWN);
710 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Page_Down, 0,
712 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_DOWN);
713 gtk_binding_entry_add_signal (binding_set, GDK_KEY_End, 0,
715 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_END);
716 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_End, 0,
718 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_END);
721 g_object_class_override_property (object_class,
722 PROP_EDITING_CANCELED,
728 * The model from which the combo box takes the values shown
733 g_object_class_install_property (object_class,
735 g_param_spec_object ("model",
736 P_("ComboBox model"),
737 P_("The model for the combo box"),
739 GTK_PARAM_READWRITE));
742 * GtkComboBox:wrap-width:
744 * If wrap-width is set to a positive value, the list will be
745 * displayed in multiple columns, the number of columns is
746 * determined by wrap-width.
750 g_object_class_install_property (object_class,
752 g_param_spec_int ("wrap-width",
754 P_("Wrap width for laying out the items in a grid"),
758 GTK_PARAM_READWRITE));
762 * GtkComboBox:row-span-column:
764 * If this is set to a non-negative value, it must be the index of a column
765 * of type %G_TYPE_INT in the model.
767 * The values of that column are used to determine how many rows a value in
768 * the list will span. Therefore, the values in the model column pointed to
769 * by this property must be greater than zero and not larger than wrap-width.
773 g_object_class_install_property (object_class,
774 PROP_ROW_SPAN_COLUMN,
775 g_param_spec_int ("row-span-column",
776 P_("Row span column"),
777 P_("TreeModel column containing the row span values"),
781 GTK_PARAM_READWRITE));
785 * GtkComboBox:column-span-column:
787 * If this is set to a non-negative value, it must be the index of a column
788 * of type %G_TYPE_INT in the model.
790 * The values of that column are used to determine how many columns a value
791 * in the list will span.
795 g_object_class_install_property (object_class,
796 PROP_COLUMN_SPAN_COLUMN,
797 g_param_spec_int ("column-span-column",
798 P_("Column span column"),
799 P_("TreeModel column containing the column span values"),
803 GTK_PARAM_READWRITE));
807 * GtkComboBox:active:
809 * The item which is currently active. If the model is a non-flat treemodel,
810 * and the active item is not an immediate child of the root of the tree,
811 * this property has the value
812 * <literal>gtk_tree_path_get_indices (path)[0]</literal>,
813 * where <literal>path</literal> is the #GtkTreePath of the active item.
817 g_object_class_install_property (object_class,
819 g_param_spec_int ("active",
821 P_("The item which is currently active"),
825 GTK_PARAM_READWRITE));
828 * GtkComboBox:add-tearoffs:
830 * The add-tearoffs property controls whether generated menus
831 * have tearoff menu items.
833 * Note that this only affects menu style combo boxes.
837 g_object_class_install_property (object_class,
839 g_param_spec_boolean ("add-tearoffs",
840 P_("Add tearoffs to menus"),
841 P_("Whether dropdowns should have a tearoff menu item"),
843 GTK_PARAM_READWRITE));
846 * GtkComboBox:has-frame:
848 * The has-frame property controls whether a frame
849 * is drawn around the entry.
853 g_object_class_install_property (object_class,
855 g_param_spec_boolean ("has-frame",
857 P_("Whether the combo box draws a frame around the child"),
859 GTK_PARAM_READWRITE));
861 g_object_class_install_property (object_class,
863 g_param_spec_boolean ("focus-on-click",
864 P_("Focus on click"),
865 P_("Whether the combo box grabs focus when it is clicked with the mouse"),
867 GTK_PARAM_READWRITE));
870 * GtkComboBox:tearoff-title:
872 * A title that may be displayed by the window manager
873 * when the popup is torn-off.
877 g_object_class_install_property (object_class,
879 g_param_spec_string ("tearoff-title",
881 P_("A title that may be displayed by the window manager when the popup is torn-off"),
883 GTK_PARAM_READWRITE));
887 * GtkComboBox:popup-shown:
889 * Whether the combo boxes dropdown is popped up.
890 * Note that this property is mainly useful, because
891 * it allows you to connect to notify::popup-shown.
895 g_object_class_install_property (object_class,
897 g_param_spec_boolean ("popup-shown",
899 P_("Whether the combo's dropdown is shown"),
901 GTK_PARAM_READABLE));
905 * GtkComboBox:button-sensitivity:
907 * Whether the dropdown button is sensitive when
908 * the model is empty.
912 g_object_class_install_property (object_class,
913 PROP_BUTTON_SENSITIVITY,
914 g_param_spec_enum ("button-sensitivity",
915 P_("Button Sensitivity"),
916 P_("Whether the dropdown button is sensitive when the model is empty"),
917 GTK_TYPE_SENSITIVITY_TYPE,
918 GTK_SENSITIVITY_AUTO,
919 GTK_PARAM_READWRITE));
922 * GtkComboBox:has-entry:
924 * Whether the combo box has an entry.
928 g_object_class_install_property (object_class,
930 g_param_spec_boolean ("has-entry",
932 P_("Whether combo box has an entry"),
934 GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
937 * GtkComboBox:entry-text-column:
939 * The column in the combo box's model to associate with strings from the entry
940 * if the combo was created with #GtkComboBox:has-entry = %TRUE.
944 g_object_class_install_property (object_class,
945 PROP_ENTRY_TEXT_COLUMN,
946 g_param_spec_int ("entry-text-column",
947 P_("Entry Text Column"),
948 P_("The column in the combo box's model to associate "
949 "with strings from the entry if the combo was "
950 "created with #GtkComboBox:has-entry = %TRUE"),
952 GTK_PARAM_READWRITE));
955 * GtkComboBox:id-column:
957 * The column in the combo box's model that provides string
958 * IDs for the values in the model, if != -1.
962 g_object_class_install_property (object_class,
964 g_param_spec_int ("id-column",
966 P_("The column in the combo box's model that provides "
967 "string IDs for the values in the model"),
969 GTK_PARAM_READWRITE));
972 * GtkComboBox:active-id:
974 * The value of the ID column of the active row.
978 g_object_class_install_property (object_class,
980 g_param_spec_string ("active-id",
982 P_("The value of the id column "
983 "for the active row"),
984 NULL, GTK_PARAM_READWRITE));
987 * GtkComboBox:popup-fixed-width:
989 * Whether the popup's width should be a fixed width matching the
990 * allocated width of the combo box.
994 g_object_class_install_property (object_class,
995 PROP_POPUP_FIXED_WIDTH,
996 g_param_spec_boolean ("popup-fixed-width",
997 P_("Popup Fixed Width"),
998 P_("Whether the popup's width should be a "
999 "fixed width matching the allocated width "
1000 "of the combo box"),
1002 GTK_PARAM_READWRITE));
1004 gtk_widget_class_install_style_property (widget_class,
1005 g_param_spec_boolean ("appears-as-list",
1006 P_("Appears as list"),
1007 P_("Whether dropdowns should look like lists rather than menus"),
1009 GTK_PARAM_READABLE));
1012 * GtkComboBox:arrow-size:
1014 * Sets the minimum size of the arrow in the combo box. Note
1015 * that the arrow size is coupled to the font size, so in case
1016 * a larger font is used, the arrow will be larger than set
1021 gtk_widget_class_install_style_property (widget_class,
1022 g_param_spec_int ("arrow-size",
1024 P_("The minimum size of the arrow in the combo box"),
1028 GTK_PARAM_READABLE));
1031 * GtkComboBox:shadow-type:
1033 * Which kind of shadow to draw around the combo box.
1037 gtk_widget_class_install_style_property (widget_class,
1038 g_param_spec_enum ("shadow-type",
1040 P_("Which kind of shadow to draw around the combo box"),
1041 GTK_TYPE_SHADOW_TYPE,
1043 GTK_PARAM_READABLE));
1045 g_type_class_add_private (object_class, sizeof (GtkComboBoxPrivate));
1049 gtk_combo_box_buildable_init (GtkBuildableIface *iface)
1051 parent_buildable_iface = g_type_interface_peek_parent (iface);
1052 iface->add_child = _gtk_cell_layout_buildable_add_child;
1053 iface->custom_tag_start = gtk_combo_box_buildable_custom_tag_start;
1054 iface->custom_tag_end = gtk_combo_box_buildable_custom_tag_end;
1055 iface->get_internal_child = gtk_combo_box_buildable_get_internal_child;
1059 gtk_combo_box_cell_layout_init (GtkCellLayoutIface *iface)
1061 iface->pack_start = gtk_combo_box_cell_layout_pack_start;
1062 iface->pack_end = gtk_combo_box_cell_layout_pack_end;
1063 iface->get_cells = gtk_combo_box_cell_layout_get_cells;
1064 iface->clear = gtk_combo_box_cell_layout_clear;
1065 iface->add_attribute = gtk_combo_box_cell_layout_add_attribute;
1066 iface->set_cell_data_func = gtk_combo_box_cell_layout_set_cell_data_func;
1067 iface->clear_attributes = gtk_combo_box_cell_layout_clear_attributes;
1068 iface->reorder = gtk_combo_box_cell_layout_reorder;
1072 gtk_combo_box_cell_editable_init (GtkCellEditableIface *iface)
1074 iface->start_editing = gtk_combo_box_start_editing;
1078 gtk_combo_box_init (GtkComboBox *combo_box)
1080 GtkComboBoxPrivate *priv;
1081 GtkStyleContext *context;
1083 combo_box->priv = G_TYPE_INSTANCE_GET_PRIVATE (combo_box,
1085 GtkComboBoxPrivate);
1086 priv = combo_box->priv;
1088 priv->cell_view = gtk_cell_view_new ();
1089 gtk_widget_set_parent (priv->cell_view, GTK_WIDGET (combo_box));
1090 _gtk_bin_set_child (GTK_BIN (combo_box), priv->cell_view);
1091 gtk_widget_show (priv->cell_view);
1093 priv->minimum_width = 0;
1094 priv->natural_width = 0;
1096 priv->wrap_width = 0;
1099 priv->active_row = NULL;
1100 priv->col_column = -1;
1101 priv->row_column = -1;
1103 priv->popup_shown = FALSE;
1104 priv->add_tearoffs = FALSE;
1105 priv->has_frame = TRUE;
1106 priv->is_cell_renderer = FALSE;
1107 priv->editing_canceled = FALSE;
1108 priv->auto_scroll = FALSE;
1109 priv->focus_on_click = TRUE;
1110 priv->button_sensitivity = GTK_SENSITIVITY_AUTO;
1111 priv->has_entry = FALSE;
1112 priv->popup_fixed_width = TRUE;
1114 priv->text_column = -1;
1115 priv->text_renderer = NULL;
1116 priv->id_column = -1;
1118 gtk_combo_box_check_appearance (combo_box);
1120 context = gtk_widget_get_style_context (GTK_WIDGET (combo_box));
1121 gtk_style_context_add_class (context, GTK_STYLE_CLASS_BUTTON);
1125 gtk_combo_box_set_property (GObject *object,
1127 const GValue *value,
1130 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
1135 gtk_combo_box_set_model (combo_box, g_value_get_object (value));
1138 case PROP_WRAP_WIDTH:
1139 gtk_combo_box_set_wrap_width (combo_box, g_value_get_int (value));
1142 case PROP_ROW_SPAN_COLUMN:
1143 gtk_combo_box_set_row_span_column (combo_box, g_value_get_int (value));
1146 case PROP_COLUMN_SPAN_COLUMN:
1147 gtk_combo_box_set_column_span_column (combo_box, g_value_get_int (value));
1151 gtk_combo_box_set_active (combo_box, g_value_get_int (value));
1154 case PROP_ADD_TEAROFFS:
1155 gtk_combo_box_set_add_tearoffs (combo_box, g_value_get_boolean (value));
1158 case PROP_HAS_FRAME:
1159 combo_box->priv->has_frame = g_value_get_boolean (value);
1161 if (combo_box->priv->has_entry)
1165 child = gtk_bin_get_child (GTK_BIN (combo_box));
1167 gtk_entry_set_has_frame (GTK_ENTRY (child),
1168 combo_box->priv->has_frame);
1173 case PROP_FOCUS_ON_CLICK:
1174 gtk_combo_box_set_focus_on_click (combo_box,
1175 g_value_get_boolean (value));
1178 case PROP_TEAROFF_TITLE:
1179 gtk_combo_box_set_title (combo_box, g_value_get_string (value));
1182 case PROP_POPUP_SHOWN:
1183 if (g_value_get_boolean (value))
1184 gtk_combo_box_popup (combo_box);
1186 gtk_combo_box_popdown (combo_box);
1189 case PROP_BUTTON_SENSITIVITY:
1190 gtk_combo_box_set_button_sensitivity (combo_box,
1191 g_value_get_enum (value));
1194 case PROP_POPUP_FIXED_WIDTH:
1195 gtk_combo_box_set_popup_fixed_width (combo_box,
1196 g_value_get_boolean (value));
1199 case PROP_EDITING_CANCELED:
1200 combo_box->priv->editing_canceled = g_value_get_boolean (value);
1203 case PROP_HAS_ENTRY:
1204 combo_box->priv->has_entry = g_value_get_boolean (value);
1207 case PROP_ENTRY_TEXT_COLUMN:
1208 gtk_combo_box_set_entry_text_column (combo_box, g_value_get_int (value));
1211 case PROP_ID_COLUMN:
1212 gtk_combo_box_set_id_column (combo_box, g_value_get_int (value));
1215 case PROP_ACTIVE_ID:
1216 gtk_combo_box_set_active_id (combo_box, g_value_get_string (value));
1220 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1226 gtk_combo_box_get_property (GObject *object,
1231 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
1232 GtkComboBoxPrivate *priv = combo_box->priv;
1237 g_value_set_object (value, combo_box->priv->model);
1240 case PROP_WRAP_WIDTH:
1241 g_value_set_int (value, combo_box->priv->wrap_width);
1244 case PROP_ROW_SPAN_COLUMN:
1245 g_value_set_int (value, combo_box->priv->row_column);
1248 case PROP_COLUMN_SPAN_COLUMN:
1249 g_value_set_int (value, combo_box->priv->col_column);
1253 g_value_set_int (value, gtk_combo_box_get_active (combo_box));
1256 case PROP_ADD_TEAROFFS:
1257 g_value_set_boolean (value, gtk_combo_box_get_add_tearoffs (combo_box));
1260 case PROP_HAS_FRAME:
1261 g_value_set_boolean (value, combo_box->priv->has_frame);
1264 case PROP_FOCUS_ON_CLICK:
1265 g_value_set_boolean (value, combo_box->priv->focus_on_click);
1268 case PROP_TEAROFF_TITLE:
1269 g_value_set_string (value, gtk_combo_box_get_title (combo_box));
1272 case PROP_POPUP_SHOWN:
1273 g_value_set_boolean (value, combo_box->priv->popup_shown);
1276 case PROP_BUTTON_SENSITIVITY:
1277 g_value_set_enum (value, combo_box->priv->button_sensitivity);
1280 case PROP_POPUP_FIXED_WIDTH:
1281 g_value_set_boolean (value, combo_box->priv->popup_fixed_width);
1284 case PROP_EDITING_CANCELED:
1285 g_value_set_boolean (value, priv->editing_canceled);
1288 case PROP_HAS_ENTRY:
1289 g_value_set_boolean (value, priv->has_entry);
1292 case PROP_ENTRY_TEXT_COLUMN:
1293 g_value_set_int (value, priv->text_column);
1296 case PROP_ID_COLUMN:
1297 g_value_set_int (value, priv->id_column);
1300 case PROP_ACTIVE_ID:
1301 g_value_set_string (value, gtk_combo_box_get_active_id (combo_box));
1305 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1311 gtk_combo_box_state_changed (GtkWidget *widget,
1312 GtkStateType previous)
1314 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1315 GtkComboBoxPrivate *priv = combo_box->priv;
1317 if (gtk_widget_get_realized (widget))
1319 if (priv->tree_view && priv->cell_view)
1321 GtkStyleContext *context;
1322 GtkStateFlags state;
1325 context = gtk_widget_get_style_context (widget);
1326 state = gtk_widget_get_state_flags (widget);
1328 gtk_style_context_get (context, state,
1329 "background-color", &color,
1332 gtk_cell_view_set_background_rgba (GTK_CELL_VIEW (priv->cell_view), color);
1333 gdk_rgba_free (color);
1337 gtk_widget_queue_draw (widget);
1341 gtk_combo_box_button_state_flags_changed (GtkWidget *widget,
1342 GtkStateFlags previous,
1345 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
1346 GtkComboBoxPrivate *priv = combo_box->priv;
1348 if (gtk_widget_get_realized (widget))
1350 if (!priv->tree_view && priv->cell_view)
1351 gtk_widget_set_state_flags (priv->cell_view,
1352 gtk_widget_get_state_flags (widget),
1356 gtk_widget_queue_draw (widget);
1360 gtk_combo_box_check_appearance (GtkComboBox *combo_box)
1362 GtkComboBoxPrivate *priv = combo_box->priv;
1363 gboolean appears_as_list;
1365 /* if wrap_width > 0, then we are in grid-mode and forced to use
1368 if (priv->wrap_width)
1369 appears_as_list = FALSE;
1371 gtk_widget_style_get (GTK_WIDGET (combo_box),
1372 "appears-as-list", &appears_as_list,
1375 if (appears_as_list)
1377 /* Destroy all the menu mode widgets, if they exist. */
1378 if (GTK_IS_MENU (priv->popup_widget))
1379 gtk_combo_box_menu_destroy (combo_box);
1381 /* Create the list mode widgets, if they don't already exist. */
1382 if (!GTK_IS_TREE_VIEW (priv->tree_view))
1383 gtk_combo_box_list_setup (combo_box);
1387 /* Destroy all the list mode widgets, if they exist. */
1388 if (GTK_IS_TREE_VIEW (priv->tree_view))
1389 gtk_combo_box_list_destroy (combo_box);
1391 /* Create the menu mode widgets, if they don't already exist. */
1392 if (!GTK_IS_MENU (priv->popup_widget))
1393 gtk_combo_box_menu_setup (combo_box, TRUE);
1396 gtk_widget_style_get (GTK_WIDGET (combo_box),
1397 "shadow-type", &priv->shadow_type,
1402 gtk_combo_box_style_updated (GtkWidget *widget)
1404 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1405 GtkComboBoxPrivate *priv = combo_box->priv;
1408 gtk_combo_box_check_appearance (combo_box);
1410 if (priv->tree_view && priv->cell_view)
1412 GtkStyleContext *context;
1415 context = gtk_widget_get_style_context (widget);
1416 gtk_style_context_get (context, 0,
1417 "background-color", &color,
1420 gtk_cell_view_set_background_rgba (GTK_CELL_VIEW (priv->cell_view),
1423 gdk_rgba_free (color);
1426 child = gtk_bin_get_child (GTK_BIN (combo_box));
1427 if (GTK_IS_ENTRY (child))
1428 g_object_set (child, "shadow-type",
1429 GTK_SHADOW_NONE == priv->shadow_type ?
1430 GTK_SHADOW_IN : GTK_SHADOW_NONE, NULL);
1434 gtk_combo_box_button_toggled (GtkWidget *widget,
1437 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
1439 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
1441 if (!combo_box->priv->popup_in_progress)
1442 gtk_combo_box_popup (combo_box);
1445 gtk_combo_box_popdown (combo_box);
1449 gtk_combo_box_add (GtkContainer *container,
1452 GtkComboBox *combo_box = GTK_COMBO_BOX (container);
1453 GtkComboBoxPrivate *priv = combo_box->priv;
1455 if (priv->has_entry && !GTK_IS_ENTRY (widget))
1457 g_warning ("Attempting to add a widget with type %s to a GtkComboBox that needs an entry "
1458 "(need an instance of GtkEntry or of a subclass)",
1459 G_OBJECT_TYPE_NAME (widget));
1463 if (priv->cell_view &&
1464 gtk_widget_get_parent (priv->cell_view))
1466 gtk_widget_unparent (priv->cell_view);
1467 _gtk_bin_set_child (GTK_BIN (container), NULL);
1468 gtk_widget_queue_resize (GTK_WIDGET (container));
1471 gtk_widget_set_parent (widget, GTK_WIDGET (container));
1472 _gtk_bin_set_child (GTK_BIN (container), widget);
1474 if (priv->cell_view &&
1475 widget != priv->cell_view)
1477 /* since the cell_view was unparented, it's gone now */
1478 priv->cell_view = NULL;
1480 if (!priv->tree_view && priv->separator)
1482 gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (priv->separator)),
1484 priv->separator = NULL;
1486 gtk_widget_queue_resize (GTK_WIDGET (container));
1488 else if (priv->cell_view_frame)
1490 gtk_widget_unparent (priv->cell_view_frame);
1491 priv->cell_view_frame = NULL;
1496 if (priv->has_entry)
1498 g_signal_connect (widget, "changed",
1499 G_CALLBACK (gtk_combo_box_entry_contents_changed),
1502 gtk_entry_set_has_frame (GTK_ENTRY (widget), priv->has_frame);
1507 gtk_combo_box_remove (GtkContainer *container,
1510 GtkComboBox *combo_box = GTK_COMBO_BOX (container);
1511 GtkComboBoxPrivate *priv = combo_box->priv;
1513 gboolean appears_as_list;
1515 if (priv->has_entry)
1517 GtkWidget *child_widget;
1519 child_widget = gtk_bin_get_child (GTK_BIN (container));
1520 if (widget && widget == child_widget)
1522 g_signal_handlers_disconnect_by_func (widget,
1523 gtk_combo_box_entry_contents_changed,
1528 if (widget == priv->cell_view)
1529 priv->cell_view = NULL;
1531 gtk_widget_unparent (widget);
1532 _gtk_bin_set_child (GTK_BIN (container), NULL);
1534 if (gtk_widget_in_destruction (GTK_WIDGET (combo_box)))
1537 gtk_widget_queue_resize (GTK_WIDGET (container));
1539 if (!priv->tree_view)
1540 appears_as_list = FALSE;
1542 appears_as_list = TRUE;
1544 if (appears_as_list)
1545 gtk_combo_box_list_destroy (combo_box);
1546 else if (GTK_IS_MENU (priv->popup_widget))
1548 gtk_combo_box_menu_destroy (combo_box);
1549 gtk_menu_detach (GTK_MENU (priv->popup_widget));
1550 priv->popup_widget = NULL;
1553 if (!priv->cell_view)
1555 priv->cell_view = gtk_cell_view_new ();
1556 gtk_widget_set_parent (priv->cell_view, GTK_WIDGET (container));
1557 _gtk_bin_set_child (GTK_BIN (container), priv->cell_view);
1559 gtk_widget_show (priv->cell_view);
1560 gtk_cell_view_set_model (GTK_CELL_VIEW (priv->cell_view),
1562 gtk_combo_box_sync_cells (combo_box, GTK_CELL_LAYOUT (priv->cell_view));
1566 if (appears_as_list)
1567 gtk_combo_box_list_setup (combo_box);
1569 gtk_combo_box_menu_setup (combo_box, TRUE);
1571 if (gtk_tree_row_reference_valid (priv->active_row))
1573 path = gtk_tree_row_reference_get_path (priv->active_row);
1574 gtk_combo_box_set_active_internal (combo_box, path);
1575 gtk_tree_path_free (path);
1578 gtk_combo_box_set_active_internal (combo_box, NULL);
1581 static ComboCellInfo *
1582 gtk_combo_box_get_cell_info (GtkComboBox *combo_box,
1583 GtkCellRenderer *cell)
1587 for (i = combo_box->priv->cells; i; i = i->next)
1589 ComboCellInfo *info = (ComboCellInfo *)i->data;
1591 if (info && info->cell == cell)
1599 gtk_combo_box_menu_show (GtkWidget *menu,
1602 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1603 GtkComboBoxPrivate *priv = combo_box->priv;
1605 gtk_combo_box_child_show (menu, user_data);
1607 priv->popup_in_progress = TRUE;
1608 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button),
1610 priv->popup_in_progress = FALSE;
1614 gtk_combo_box_menu_hide (GtkWidget *menu,
1617 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1619 gtk_combo_box_child_hide (menu,user_data);
1621 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
1626 gtk_combo_box_detacher (GtkWidget *widget,
1629 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1630 GtkComboBoxPrivate *priv = combo_box->priv;
1632 g_return_if_fail (priv->popup_widget == (GtkWidget *) menu);
1634 g_signal_handlers_disconnect_by_func (menu->toplevel,
1635 gtk_combo_box_menu_show,
1637 g_signal_handlers_disconnect_by_func (menu->toplevel,
1638 gtk_combo_box_menu_hide,
1641 priv->popup_widget = NULL;
1645 gtk_combo_box_set_popup_widget (GtkComboBox *combo_box,
1648 GtkComboBoxPrivate *priv = combo_box->priv;
1650 if (GTK_IS_MENU (priv->popup_widget))
1652 gtk_menu_detach (GTK_MENU (priv->popup_widget));
1653 priv->popup_widget = NULL;
1655 else if (priv->popup_widget)
1657 gtk_container_remove (GTK_CONTAINER (priv->scrolled_window),
1658 priv->popup_widget);
1659 g_object_unref (priv->popup_widget);
1660 priv->popup_widget = NULL;
1663 if (GTK_IS_MENU (popup))
1665 if (priv->popup_window)
1667 gtk_widget_destroy (priv->popup_window);
1668 priv->popup_window = NULL;
1671 priv->popup_widget = popup;
1674 * Note that we connect to show/hide on the toplevel, not the
1675 * menu itself, since the menu is not shown/hidden when it is
1676 * popped up while torn-off.
1678 g_signal_connect (GTK_MENU (popup)->toplevel, "show",
1679 G_CALLBACK (gtk_combo_box_menu_show), combo_box);
1680 g_signal_connect (GTK_MENU (popup)->toplevel, "hide",
1681 G_CALLBACK (gtk_combo_box_menu_hide), combo_box);
1683 gtk_menu_attach_to_widget (GTK_MENU (popup),
1684 GTK_WIDGET (combo_box),
1685 gtk_combo_box_detacher);
1689 if (!priv->popup_window)
1691 GtkWidget *toplevel;
1693 priv->popup_window = gtk_window_new (GTK_WINDOW_POPUP);
1694 gtk_widget_set_name (priv->popup_window, "gtk-combobox-popup-window");
1696 gtk_window_set_type_hint (GTK_WINDOW (priv->popup_window),
1697 GDK_WINDOW_TYPE_HINT_COMBO);
1699 g_signal_connect (GTK_WINDOW (priv->popup_window),"show",
1700 G_CALLBACK (gtk_combo_box_child_show),
1702 g_signal_connect (GTK_WINDOW (priv->popup_window),"hide",
1703 G_CALLBACK (gtk_combo_box_child_hide),
1706 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (combo_box));
1707 if (GTK_IS_WINDOW (toplevel))
1709 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
1710 GTK_WINDOW (priv->popup_window));
1711 gtk_window_set_transient_for (GTK_WINDOW (priv->popup_window),
1712 GTK_WINDOW (toplevel));
1715 gtk_window_set_resizable (GTK_WINDOW (priv->popup_window), FALSE);
1716 gtk_window_set_screen (GTK_WINDOW (priv->popup_window),
1717 gtk_widget_get_screen (GTK_WIDGET (combo_box)));
1719 priv->scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1721 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
1724 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->scrolled_window),
1727 gtk_widget_show (priv->scrolled_window);
1729 gtk_container_add (GTK_CONTAINER (priv->popup_window),
1730 priv->scrolled_window);
1733 gtk_container_add (GTK_CONTAINER (priv->scrolled_window),
1736 gtk_widget_show (popup);
1737 g_object_ref (popup);
1738 priv->popup_widget = popup;
1743 get_widget_border_thickness (GtkWidget *widget)
1745 GtkStyleContext *context;
1748 context = gtk_widget_get_style_context (widget);
1750 gtk_style_context_get (context,
1751 gtk_widget_get_state_flags (widget),
1752 "border-width", &thickness,
1758 gtk_combo_box_menu_position_below (GtkMenu *menu,
1764 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1765 GtkAllocation child_allocation;
1771 GdkRectangle monitor;
1773 /* FIXME: is using the size request here broken? */
1774 child = gtk_bin_get_child (GTK_BIN (combo_box));
1778 gtk_widget_get_allocation (child, &child_allocation);
1780 if (!gtk_widget_get_has_window (child))
1782 sx += child_allocation.x;
1783 sy += child_allocation.y;
1786 gdk_window_get_root_coords (gtk_widget_get_window (child),
1789 if (GTK_SHADOW_NONE != combo_box->priv->shadow_type)
1790 sx -= get_widget_border_thickness (GTK_WIDGET (combo_box));
1792 if (combo_box->priv->popup_fixed_width)
1793 gtk_widget_get_preferred_size (GTK_WIDGET (menu), &req, NULL);
1795 gtk_widget_get_preferred_size (GTK_WIDGET (menu), NULL, &req);
1797 if (gtk_widget_get_direction (GTK_WIDGET (combo_box)) == GTK_TEXT_DIR_LTR)
1800 *x = sx + child_allocation.width - req.width;
1803 screen = gtk_widget_get_screen (GTK_WIDGET (combo_box));
1804 monitor_num = gdk_screen_get_monitor_at_window (screen,
1805 gtk_widget_get_window (GTK_WIDGET (combo_box)));
1806 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1810 else if (*x + req.width > monitor.x + monitor.width)
1811 *x = monitor.x + monitor.width - req.width;
1813 if (monitor.y + monitor.height - *y - child_allocation.height >= req.height)
1814 *y += child_allocation.height;
1815 else if (*y - monitor.y >= req.height)
1817 else if (monitor.y + monitor.height - *y - child_allocation.height > *y - monitor.y)
1818 *y += child_allocation.height;
1826 gtk_combo_box_menu_position_over (GtkMenu *menu,
1832 GtkComboBox *combo_box;
1836 GtkAllocation allocation;
1837 GtkAllocation child_allocation;
1844 combo_box = GTK_COMBO_BOX (user_data);
1845 widget = GTK_WIDGET (combo_box);
1847 active = gtk_menu_get_active (GTK_MENU (combo_box->priv->popup_widget));
1849 gtk_widget_get_allocation (widget, &allocation);
1851 menu_xpos = allocation.x;
1852 menu_ypos = allocation.y + allocation.height / 2 - 2;
1854 if (combo_box->priv->popup_fixed_width)
1855 gtk_widget_get_preferred_width (GTK_WIDGET (menu), &menu_width, NULL);
1857 gtk_widget_get_preferred_width (GTK_WIDGET (menu), NULL, &menu_width);
1861 gtk_widget_get_allocation (active, &child_allocation);
1862 menu_ypos -= child_allocation.height / 2;
1865 children = GTK_MENU_SHELL (combo_box->priv->popup_widget)->children;
1868 child = children->data;
1870 if (active == child)
1873 if (gtk_widget_get_visible (child))
1875 gtk_widget_get_allocation (child, &child_allocation);
1877 menu_ypos -= child_allocation.height;
1880 children = children->next;
1883 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
1884 menu_xpos = menu_xpos + allocation.width - menu_width;
1886 gdk_window_get_root_coords (gtk_widget_get_window (widget),
1887 menu_xpos, menu_ypos,
1888 &menu_xpos, &menu_ypos);
1890 /* Clamp the position on screen */
1891 screen_width = gdk_screen_get_width (gtk_widget_get_screen (widget));
1895 else if ((menu_xpos + menu_width) > screen_width)
1896 menu_xpos -= ((menu_xpos + menu_width) - screen_width);
1905 gtk_combo_box_menu_position (GtkMenu *menu,
1911 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1912 GtkComboBoxPrivate *priv = combo_box->priv;
1913 GtkWidget *menu_item;
1915 if (priv->wrap_width > 0 || priv->cell_view == NULL)
1916 gtk_combo_box_menu_position_below (menu, x, y, push_in, user_data);
1919 /* FIXME handle nested menus better */
1920 menu_item = gtk_menu_get_active (GTK_MENU (priv->popup_widget));
1922 gtk_menu_shell_select_item (GTK_MENU_SHELL (priv->popup_widget),
1925 gtk_combo_box_menu_position_over (menu, x, y, push_in, user_data);
1928 if (!gtk_widget_get_visible (GTK_MENU (priv->popup_widget)->toplevel))
1929 gtk_window_set_type_hint (GTK_WINDOW (GTK_MENU (priv->popup_widget)->toplevel),
1930 GDK_WINDOW_TYPE_HINT_COMBO);
1934 gtk_combo_box_list_position (GtkComboBox *combo_box,
1940 GtkComboBoxPrivate *priv = combo_box->priv;
1941 GtkAllocation allocation;
1944 GdkRectangle monitor;
1945 GtkRequisition popup_req;
1946 GtkPolicyType hpolicy, vpolicy;
1949 /* under windows, the drop down list is as wide as the combo box itself.
1951 GtkWidget *widget = GTK_WIDGET (combo_box);
1955 gtk_widget_get_allocation (widget, &allocation);
1957 if (!gtk_widget_get_has_window (widget))
1963 window = gtk_widget_get_window (widget);
1965 gdk_window_get_root_coords (gtk_widget_get_window (widget),
1968 *width = allocation.width;
1970 hpolicy = vpolicy = GTK_POLICY_NEVER;
1971 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
1974 /* XXX This set_size_request call is part of the hack outlined below and can
1975 * go away once height-for-width is implemented on treeviews. */
1976 gtk_widget_set_size_request (priv->tree_view, -1, -1);
1978 if (combo_box->priv->popup_fixed_width)
1980 gtk_widget_get_preferred_size (priv->scrolled_window, &popup_req, NULL);
1982 if (popup_req.width > *width)
1984 hpolicy = GTK_POLICY_ALWAYS;
1985 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
1991 gtk_combo_box_remeasure (combo_box);
1993 if (priv->natural_width > *width)
1995 hpolicy = GTK_POLICY_NEVER;
1996 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
2000 /* XXX Currently we set the size-request on the internal treeview to be
2001 * the natural width of the cells, this hack can go away once our
2002 * treeview does height-for-width properly (i.e. just adjust *width
2003 * here to be the natural width request of the scrolled-window).
2005 * I can't tell why the magic number 5 is needed here (i.e. without it
2006 * treeviews are left ellipsizing) , however it this all should be
2007 * removed with height-for-width treeviews.
2009 gtk_widget_set_size_request (priv->tree_view, priv->natural_width + 5, -1);
2010 gtk_widget_get_preferred_size (priv->scrolled_window, NULL, &popup_req);
2012 *width = popup_req.width;
2016 *height = popup_req.height;
2018 screen = gtk_widget_get_screen (GTK_WIDGET (combo_box));
2019 monitor_num = gdk_screen_get_monitor_at_window (screen, window);
2020 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
2022 if (gtk_widget_get_direction (GTK_WIDGET (combo_box)) == GTK_TEXT_DIR_RTL)
2023 *x = *x + allocation.width - *width;
2027 else if (*x + *width > monitor.x + monitor.width)
2028 *x = monitor.x + monitor.width - *width;
2030 if (*y + allocation.height + *height <= monitor.y + monitor.height)
2031 *y += allocation.height;
2032 else if (*y - *height >= monitor.y)
2034 else if (monitor.y + monitor.height - (*y + allocation.height) > *y - monitor.y)
2036 *y += allocation.height;
2037 *height = monitor.y + monitor.height - *y;
2041 *height = *y - monitor.y;
2045 if (popup_req.height > *height)
2047 vpolicy = GTK_POLICY_ALWAYS;
2049 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
2055 cell_view_is_sensitive (GtkCellView *cell_view)
2057 GList *cells, *list;
2060 cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (cell_view));
2063 for (list = cells; list; list = list->next)
2065 g_object_get (list->data, "sensitive", &sensitive, NULL);
2070 g_list_free (cells);
2076 tree_column_row_is_sensitive (GtkComboBox *combo_box,
2079 GtkComboBoxPrivate *priv = combo_box->priv;
2080 GList *cells, *list;
2086 if (priv->row_separator_func)
2088 if (priv->row_separator_func (priv->model, iter,
2089 priv->row_separator_data))
2093 gtk_tree_view_column_cell_set_cell_data (priv->column,
2095 iter, FALSE, FALSE);
2097 cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (priv->column));
2100 for (list = cells; list; list = list->next)
2102 g_object_get (list->data, "sensitive", &sensitive, NULL);
2107 g_list_free (cells);
2113 update_menu_sensitivity (GtkComboBox *combo_box,
2116 GtkComboBoxPrivate *priv = combo_box->priv;
2117 GList *children, *child;
2118 GtkWidget *item, *submenu, *separator;
2119 GtkWidget *cell_view;
2125 children = gtk_container_get_children (GTK_CONTAINER (menu));
2127 for (child = children; child; child = child->next)
2129 item = GTK_WIDGET (child->data);
2130 cell_view = gtk_bin_get_child (GTK_BIN (item));
2132 if (!GTK_IS_CELL_VIEW (cell_view))
2135 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (item));
2136 if (submenu != NULL)
2138 gtk_widget_set_sensitive (item, TRUE);
2139 update_menu_sensitivity (combo_box, submenu);
2143 sensitive = cell_view_is_sensitive (GTK_CELL_VIEW (cell_view));
2145 if (menu != priv->popup_widget && child == children)
2147 separator = GTK_WIDGET (child->next->data);
2148 g_object_set (item, "visible", sensitive, NULL);
2149 g_object_set (separator, "visible", sensitive, NULL);
2152 gtk_widget_set_sensitive (item, sensitive);
2156 g_list_free (children);
2160 gtk_combo_box_menu_popup (GtkComboBox *combo_box,
2162 guint32 activate_time)
2164 GtkComboBoxPrivate *priv = combo_box->priv;
2167 gint width, min_width, nat_width;
2169 update_menu_sensitivity (combo_box, priv->popup_widget);
2172 if (gtk_tree_row_reference_valid (priv->active_row))
2174 path = gtk_tree_row_reference_get_path (priv->active_row);
2175 active_item = gtk_tree_path_get_indices (path)[0];
2176 gtk_tree_path_free (path);
2178 if (priv->add_tearoffs)
2182 /* FIXME handle nested menus better */
2183 gtk_menu_set_active (GTK_MENU (priv->popup_widget), active_item);
2185 if (priv->wrap_width == 0)
2187 GtkAllocation allocation;
2189 gtk_widget_get_allocation (GTK_WIDGET (combo_box), &allocation);
2190 width = allocation.width;
2191 gtk_widget_set_size_request (priv->popup_widget, -1, -1);
2192 gtk_widget_get_preferred_width (priv->popup_widget, &min_width, &nat_width);
2194 if (combo_box->priv->popup_fixed_width)
2195 width = MAX (width, min_width);
2197 width = MAX (width, nat_width);
2199 gtk_widget_set_size_request (priv->popup_widget, width, -1);
2202 gtk_menu_popup (GTK_MENU (priv->popup_widget),
2204 gtk_combo_box_menu_position, combo_box,
2205 button, activate_time);
2209 popup_grab_on_window (GdkWindow *window,
2210 GdkDevice *keyboard,
2212 guint32 activate_time)
2215 gdk_device_grab (keyboard, window,
2216 GDK_OWNERSHIP_WINDOW, TRUE,
2217 GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
2218 NULL, activate_time) != GDK_GRAB_SUCCESS)
2222 gdk_device_grab (pointer, window,
2223 GDK_OWNERSHIP_WINDOW, TRUE,
2224 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
2225 GDK_POINTER_MOTION_MASK,
2226 NULL, activate_time) != GDK_GRAB_SUCCESS)
2229 gdk_device_ungrab (keyboard, activate_time);
2238 * gtk_combo_box_popup:
2239 * @combo_box: a #GtkComboBox
2241 * Pops up the menu or dropdown list of @combo_box.
2243 * This function is mostly intended for use by accessibility technologies;
2244 * applications should have little use for it.
2249 gtk_combo_box_popup (GtkComboBox *combo_box)
2251 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2253 g_signal_emit (combo_box, combo_box_signals[POPUP], 0);
2257 * gtk_combo_box_popup_for_device:
2258 * @combo_box: a #GtkComboBox
2259 * @device: a #GdkDevice
2261 * Pops up the menu or dropdown list of @combo_box, the popup window
2262 * will be grabbed so only @device and its associated pointer/keyboard
2263 * are the only #GdkDevice<!-- -->s able to send events to it.
2268 gtk_combo_box_popup_for_device (GtkComboBox *combo_box,
2271 GtkComboBoxPrivate *priv = combo_box->priv;
2272 gint x, y, width, height;
2273 GtkTreePath *path = NULL, *ppath;
2274 GtkWidget *toplevel;
2275 GdkDevice *keyboard, *pointer;
2278 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2279 g_return_if_fail (GDK_IS_DEVICE (device));
2281 if (!gtk_widget_get_realized (GTK_WIDGET (combo_box)))
2284 if (gtk_widget_get_mapped (priv->popup_widget))
2287 if (priv->grab_pointer && priv->grab_keyboard)
2290 time = gtk_get_current_event_time ();
2292 if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
2295 pointer = gdk_device_get_associated_device (device);
2300 keyboard = gdk_device_get_associated_device (device);
2303 if (GTK_IS_MENU (priv->popup_widget))
2305 gtk_combo_box_menu_popup (combo_box,
2306 priv->activate_button,
2307 priv->activate_time);
2311 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (combo_box));
2312 if (GTK_IS_WINDOW (toplevel))
2313 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
2314 GTK_WINDOW (priv->popup_window));
2316 gtk_widget_show_all (priv->scrolled_window);
2317 gtk_combo_box_list_position (combo_box, &x, &y, &width, &height);
2319 gtk_widget_set_size_request (priv->popup_window, width, height);
2320 gtk_window_move (GTK_WINDOW (priv->popup_window), x, y);
2322 if (gtk_tree_row_reference_valid (priv->active_row))
2324 path = gtk_tree_row_reference_get_path (priv->active_row);
2325 ppath = gtk_tree_path_copy (path);
2326 if (gtk_tree_path_up (ppath))
2327 gtk_tree_view_expand_to_path (GTK_TREE_VIEW (priv->tree_view),
2329 gtk_tree_path_free (ppath);
2331 gtk_tree_view_set_hover_expand (GTK_TREE_VIEW (priv->tree_view),
2335 gtk_widget_show (priv->popup_window);
2339 gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->tree_view),
2341 gtk_tree_path_free (path);
2344 gtk_widget_grab_focus (priv->popup_window);
2345 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button),
2348 if (!gtk_widget_has_focus (priv->tree_view))
2349 gtk_widget_grab_focus (priv->tree_view);
2351 if (!popup_grab_on_window (gtk_widget_get_window (priv->popup_window),
2352 keyboard, pointer, time))
2354 gtk_widget_hide (priv->popup_window);
2358 gtk_device_grab_add (priv->popup_window, pointer, TRUE);
2359 priv->grab_pointer = pointer;
2360 priv->grab_keyboard = keyboard;
2364 gtk_combo_box_real_popup (GtkComboBox *combo_box)
2368 device = gtk_get_current_event_device ();
2372 GdkDeviceManager *device_manager;
2373 GdkDisplay *display;
2376 display = gtk_widget_get_display (GTK_WIDGET (combo_box));
2377 device_manager = gdk_display_get_device_manager (display);
2379 /* No device was set, pick the first master device */
2380 devices = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
2381 device = devices->data;
2382 g_list_free (devices);
2385 gtk_combo_box_popup_for_device (combo_box, device);
2389 gtk_combo_box_real_popdown (GtkComboBox *combo_box)
2391 if (combo_box->priv->popup_shown)
2393 gtk_combo_box_popdown (combo_box);
2401 * gtk_combo_box_popdown:
2402 * @combo_box: a #GtkComboBox
2404 * Hides the menu or dropdown list of @combo_box.
2406 * This function is mostly intended for use by accessibility technologies;
2407 * applications should have little use for it.
2412 gtk_combo_box_popdown (GtkComboBox *combo_box)
2414 GtkComboBoxPrivate *priv = combo_box->priv;
2416 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2418 if (GTK_IS_MENU (priv->popup_widget))
2420 gtk_menu_popdown (GTK_MENU (priv->popup_widget));
2424 if (!gtk_widget_get_realized (GTK_WIDGET (combo_box)))
2427 gtk_device_grab_remove (priv->popup_window, priv->grab_pointer);
2428 gtk_widget_hide (priv->popup_window);
2429 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button),
2432 priv->grab_pointer = NULL;
2433 priv->grab_keyboard = NULL;
2437 gtk_combo_box_update_requested_width (GtkComboBox *combo_box,
2440 GtkComboBoxPrivate *priv = combo_box->priv;
2441 gint padding, min_width, nat_width;
2443 if (priv->cell_view)
2444 gtk_widget_style_get (priv->cell_view,
2445 "focus-line-width", &padding,
2450 /* add some pixels for good measure */
2451 padding += BONUS_PADDING;
2453 if (priv->cell_view)
2454 gtk_cell_view_get_desired_width_of_row (GTK_CELL_VIEW (priv->cell_view),
2455 path, &min_width, &nat_width);
2457 min_width = nat_width = 0;
2459 min_width += padding;
2460 nat_width += padding;
2462 if (min_width > priv->minimum_width || nat_width > priv->natural_width)
2464 priv->minimum_width = MAX (priv->minimum_width, min_width);
2465 priv->natural_width = MAX (priv->natural_width, nat_width);
2467 if (priv->cell_view)
2469 gtk_widget_set_size_request (priv->cell_view, min_width, -1);
2470 gtk_widget_queue_resize (priv->cell_view);
2475 #define GTK_COMBO_BOX_SIZE_ALLOCATE_BUTTON \
2476 gtk_widget_get_preferred_size (combo_box->priv->button, \
2480 child.x = allocation->x + shadow_width; \
2482 child.x = allocation->x + allocation->width - req.width - shadow_width; \
2484 child.y = allocation->y + shadow_height; \
2485 child.width = req.width; \
2486 child.height = allocation->height - 2 * shadow_height; \
2487 child.width = MAX (1, child.width); \
2488 child.height = MAX (1, child.height); \
2490 gtk_widget_size_allocate (combo_box->priv->button, &child);
2493 gtk_combo_box_size_allocate (GtkWidget *widget,
2494 GtkAllocation *allocation)
2496 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
2497 GtkComboBoxPrivate *priv = combo_box->priv;
2498 GtkWidget *child_widget;
2499 gint shadow_width, shadow_height;
2500 gint focus_width, focus_pad;
2501 GtkAllocation child;
2503 gboolean is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
2505 gtk_widget_set_allocation (widget, allocation);
2506 child_widget = gtk_bin_get_child (GTK_BIN (widget));
2508 gtk_widget_style_get (widget,
2509 "focus-line-width", &focus_width,
2510 "focus-padding", &focus_pad,
2513 if (GTK_SHADOW_NONE != priv->shadow_type)
2514 shadow_width = shadow_height = get_widget_border_thickness (widget);
2521 if (!priv->tree_view)
2523 if (priv->cell_view)
2525 gint xthickness, ythickness;
2530 allocation->x += shadow_width;
2531 allocation->y += shadow_height;
2532 allocation->width -= 2 * shadow_width;
2533 allocation->height -= 2 * shadow_height;
2535 gtk_widget_size_allocate (priv->button, allocation);
2537 /* set some things ready */
2538 border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->button));
2539 xthickness = ythickness = get_widget_border_thickness (priv->button);
2541 child.x = allocation->x;
2542 child.y = allocation->y;
2543 width = allocation->width;
2544 child.height = allocation->height;
2546 if (!priv->is_cell_renderer)
2548 child.x += border_width + xthickness + focus_width + focus_pad;
2549 child.y += border_width + ythickness + focus_width + focus_pad;
2550 width -= 2 * (child.x - allocation->x);
2551 child.height -= 2 * (child.y - allocation->y);
2555 /* handle the children */
2556 gtk_widget_get_preferred_size (priv->arrow, &req, NULL);
2557 child.width = req.width;
2559 child.x += width - req.width;
2560 child.width = MAX (1, child.width);
2561 child.height = MAX (1, child.height);
2562 gtk_widget_size_allocate (priv->arrow, &child);
2564 child.x += req.width;
2565 gtk_widget_get_preferred_size (priv->separator, &req, NULL);
2566 child.width = req.width;
2568 child.x -= req.width;
2569 child.width = MAX (1, child.width);
2570 child.height = MAX (1, child.height);
2571 gtk_widget_size_allocate (priv->separator, &child);
2575 child.x += req.width;
2576 child.width = allocation->x + allocation->width
2577 - (border_width + xthickness + focus_width + focus_pad)
2582 child.width = child.x;
2583 child.x = allocation->x
2584 + border_width + xthickness + focus_width + focus_pad;
2585 child.width -= child.x;
2588 if (gtk_widget_get_visible (priv->popup_widget))
2590 gint width, menu_width;
2592 if (priv->wrap_width == 0)
2594 GtkAllocation combo_box_allocation;
2596 gtk_widget_get_allocation (GTK_WIDGET (combo_box), &combo_box_allocation);
2597 width = combo_box_allocation.width;
2598 gtk_widget_set_size_request (priv->popup_widget, -1, -1);
2600 if (combo_box->priv->popup_fixed_width)
2601 gtk_widget_get_preferred_width (priv->popup_widget, &menu_width, NULL);
2603 gtk_widget_get_preferred_width (priv->popup_widget, NULL, &menu_width);
2605 gtk_widget_set_size_request (priv->popup_widget,
2606 MAX (width, menu_width), -1);
2609 /* reposition the menu after giving it a new width */
2610 gtk_menu_reposition (GTK_MENU (priv->popup_widget));
2613 child.width = MAX (1, child.width);
2614 child.height = MAX (1, child.height);
2615 gtk_widget_size_allocate (child_widget, &child);
2619 GTK_COMBO_BOX_SIZE_ALLOCATE_BUTTON
2622 child.x = allocation->x + req.width + shadow_width;
2624 child.x = allocation->x + shadow_width;
2625 child.y = allocation->y + shadow_height;
2626 child.width = allocation->width - req.width - 2 * shadow_width;
2627 child.width = MAX (1, child.width);
2628 child.height = MAX (1, child.height);
2629 gtk_widget_size_allocate (child_widget, &child);
2636 /* Combobox thickness + border-width */
2637 guint border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
2638 int delta_x = shadow_width + border_width;
2639 int delta_y = shadow_height + border_width;
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)
2658 child.width = MAX (1, child.width - delta_x * 2);
2659 child.height = MAX (1, child.height - delta_y * 2);
2660 gtk_widget_size_allocate (priv->cell_view_frame, &child);
2663 if (priv->has_frame)
2665 border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->cell_view_frame));
2666 delta_x = delta_y = border_width + get_widget_border_thickness (priv->cell_view_frame);
2670 child.width -= delta_x * 2;
2671 child.height -= delta_y * 2;
2678 child.width -= delta_x * 2;
2679 child.height -= delta_y * 2;
2682 if (gtk_widget_get_visible (priv->popup_window))
2684 gint x, y, width, height;
2685 gtk_combo_box_list_position (combo_box, &x, &y, &width, &height);
2686 gtk_window_move (GTK_WINDOW (priv->popup_window), x, y);
2687 gtk_widget_set_size_request (priv->popup_window, width, height);
2691 child.width = MAX (1, child.width);
2692 child.height = MAX (1, child.height);
2694 gtk_widget_size_allocate (child_widget, &child);
2698 #undef GTK_COMBO_BOX_ALLOCATE_BUTTON
2701 gtk_combo_box_unset_model (GtkComboBox *combo_box)
2703 GtkComboBoxPrivate *priv = combo_box->priv;
2707 g_signal_handler_disconnect (priv->model,
2709 g_signal_handler_disconnect (priv->model,
2711 g_signal_handler_disconnect (priv->model,
2712 priv->reordered_id);
2713 g_signal_handler_disconnect (priv->model,
2718 if (!priv->tree_view)
2720 if (priv->popup_widget)
2721 gtk_container_foreach (GTK_CONTAINER (priv->popup_widget),
2722 (GtkCallback)gtk_widget_destroy, NULL);
2727 g_object_unref (priv->model);
2731 if (priv->active_row)
2733 gtk_tree_row_reference_free (priv->active_row);
2734 priv->active_row = NULL;
2737 if (priv->cell_view)
2738 gtk_cell_view_set_model (GTK_CELL_VIEW (priv->cell_view), NULL);
2742 gtk_combo_box_forall (GtkContainer *container,
2743 gboolean include_internals,
2744 GtkCallback callback,
2745 gpointer callback_data)
2747 GtkComboBox *combo_box = GTK_COMBO_BOX (container);
2748 GtkComboBoxPrivate *priv = combo_box->priv;
2751 if (include_internals)
2754 (* callback) (priv->button, callback_data);
2755 if (priv->cell_view_frame)
2756 (* callback) (priv->cell_view_frame, callback_data);
2759 child = gtk_bin_get_child (GTK_BIN (container));
2761 (* callback) (child, callback_data);
2765 gtk_combo_box_child_show (GtkWidget *widget,
2766 GtkComboBox *combo_box)
2768 GtkComboBoxPrivate *priv = combo_box->priv;
2770 priv->popup_shown = TRUE;
2771 g_object_notify (G_OBJECT (combo_box), "popup-shown");
2775 gtk_combo_box_child_hide (GtkWidget *widget,
2776 GtkComboBox *combo_box)
2778 GtkComboBoxPrivate *priv = combo_box->priv;
2780 priv->popup_shown = FALSE;
2781 g_object_notify (G_OBJECT (combo_box), "popup-shown");
2785 gtk_combo_box_draw (GtkWidget *widget,
2788 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
2789 GtkComboBoxPrivate *priv = combo_box->priv;
2791 if (priv->shadow_type != GTK_SHADOW_NONE)
2793 GtkStyleContext *context;
2794 GtkStateFlags state;
2796 context = gtk_widget_get_style_context (widget);
2797 state = gtk_widget_get_state_flags (widget);
2798 gtk_style_context_set_state (context, state);
2800 gtk_render_background (context, cr, 0, 0,
2801 gtk_widget_get_allocated_width (widget),
2802 gtk_widget_get_allocated_height (widget));
2803 gtk_render_frame (context, cr, 0, 0,
2804 gtk_widget_get_allocated_width (widget),
2805 gtk_widget_get_allocated_height (widget));
2808 gtk_container_propagate_draw (GTK_CONTAINER (widget),
2811 if (priv->tree_view && priv->cell_view_frame)
2813 gtk_container_propagate_draw (GTK_CONTAINER (widget),
2814 priv->cell_view_frame, cr);
2817 gtk_container_propagate_draw (GTK_CONTAINER (widget),
2818 gtk_bin_get_child (GTK_BIN (widget)),
2834 path_visible (GtkTreeView *view,
2840 /* Note that we rely on the fact that collapsed rows don't have nodes
2842 return _gtk_tree_view_find_node (view, path, &tree, &node);
2846 tree_next_func (GtkTreeModel *model,
2851 SearchData *search_data = (SearchData *)data;
2853 if (search_data->found)
2855 if (!tree_column_row_is_sensitive (search_data->combo, iter))
2858 if (search_data->visible &&
2859 !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
2862 search_data->set = TRUE;
2863 search_data->iter = *iter;
2868 if (gtk_tree_path_compare (path, search_data->path) == 0)
2869 search_data->found = TRUE;
2875 tree_next (GtkComboBox *combo,
2876 GtkTreeModel *model,
2881 SearchData search_data;
2883 search_data.combo = combo;
2884 search_data.path = gtk_tree_model_get_path (model, iter);
2885 search_data.visible = visible;
2886 search_data.found = FALSE;
2887 search_data.set = FALSE;
2889 gtk_tree_model_foreach (model, tree_next_func, &search_data);
2891 *next = search_data.iter;
2893 gtk_tree_path_free (search_data.path);
2895 return search_data.set;
2899 tree_prev_func (GtkTreeModel *model,
2904 SearchData *search_data = (SearchData *)data;
2906 if (gtk_tree_path_compare (path, search_data->path) == 0)
2908 search_data->found = TRUE;
2912 if (!tree_column_row_is_sensitive (search_data->combo, iter))
2915 if (search_data->visible &&
2916 !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
2919 search_data->set = TRUE;
2920 search_data->iter = *iter;
2926 tree_prev (GtkComboBox *combo,
2927 GtkTreeModel *model,
2932 SearchData search_data;
2934 search_data.combo = combo;
2935 search_data.path = gtk_tree_model_get_path (model, iter);
2936 search_data.visible = visible;
2937 search_data.found = FALSE;
2938 search_data.set = FALSE;
2940 gtk_tree_model_foreach (model, tree_prev_func, &search_data);
2942 *prev = search_data.iter;
2944 gtk_tree_path_free (search_data.path);
2946 return search_data.set;
2950 tree_last_func (GtkTreeModel *model,
2955 SearchData *search_data = (SearchData *)data;
2957 if (!tree_column_row_is_sensitive (search_data->combo, iter))
2960 /* Note that we rely on the fact that collapsed rows don't have nodes
2962 if (search_data->visible &&
2963 !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
2966 search_data->set = TRUE;
2967 search_data->iter = *iter;
2973 tree_last (GtkComboBox *combo,
2974 GtkTreeModel *model,
2978 SearchData search_data;
2980 search_data.combo = combo;
2981 search_data.visible = visible;
2982 search_data.set = FALSE;
2984 gtk_tree_model_foreach (model, tree_last_func, &search_data);
2986 *last = search_data.iter;
2988 return search_data.set;
2993 tree_first_func (GtkTreeModel *model,
2998 SearchData *search_data = (SearchData *)data;
3000 if (!tree_column_row_is_sensitive (search_data->combo, iter))
3003 if (search_data->visible &&
3004 !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
3007 search_data->set = TRUE;
3008 search_data->iter = *iter;
3014 tree_first (GtkComboBox *combo,
3015 GtkTreeModel *model,
3019 SearchData search_data;
3021 search_data.combo = combo;
3022 search_data.visible = visible;
3023 search_data.set = FALSE;
3025 gtk_tree_model_foreach (model, tree_first_func, &search_data);
3027 *first = search_data.iter;
3029 return search_data.set;
3033 gtk_combo_box_scroll_event (GtkWidget *widget,
3034 GdkEventScroll *event)
3036 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
3039 GtkTreeIter new_iter;
3041 if (!gtk_combo_box_get_active_iter (combo_box, &iter))
3044 if (event->direction == GDK_SCROLL_UP)
3045 found = tree_prev (combo_box, combo_box->priv->model,
3046 &iter, &new_iter, FALSE);
3048 found = tree_next (combo_box, combo_box->priv->model,
3049 &iter, &new_iter, FALSE);
3052 gtk_combo_box_set_active_iter (combo_box, &new_iter);
3062 gtk_combo_box_sync_cells (GtkComboBox *combo_box,
3063 GtkCellLayout *cell_layout)
3065 GtkComboBoxPrivate *priv = combo_box->priv;
3068 for (k = priv->cells; k; k = k->next)
3071 ComboCellInfo *info = (ComboCellInfo *)k->data;
3073 if (info->pack == GTK_PACK_START)
3074 gtk_cell_layout_pack_start (cell_layout,
3075 info->cell, info->expand);
3076 else if (info->pack == GTK_PACK_END)
3077 gtk_cell_layout_pack_end (cell_layout,
3078 info->cell, info->expand);
3080 gtk_cell_layout_set_cell_data_func (cell_layout,
3082 combo_cell_data_func, info, NULL);
3084 for (j = info->attributes; j; j = j->next->next)
3086 gtk_cell_layout_add_attribute (cell_layout,
3089 GPOINTER_TO_INT (j->next->data));
3095 gtk_combo_box_menu_setup (GtkComboBox *combo_box,
3096 gboolean add_children)
3098 GtkComboBoxPrivate *priv = combo_box->priv;
3102 child = gtk_bin_get_child (GTK_BIN (combo_box));
3104 if (priv->cell_view)
3106 priv->button = gtk_toggle_button_new ();
3107 gtk_button_set_focus_on_click (GTK_BUTTON (priv->button),
3108 priv->focus_on_click);
3110 g_signal_connect (priv->button, "toggled",
3111 G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
3112 gtk_widget_set_parent (priv->button,
3113 gtk_widget_get_parent (child));
3115 priv->box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
3116 gtk_container_add (GTK_CONTAINER (priv->button), priv->box);
3118 priv->separator = gtk_separator_new (GTK_ORIENTATION_VERTICAL);
3119 gtk_container_add (GTK_CONTAINER (priv->box), priv->separator);
3121 priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3122 gtk_container_add (GTK_CONTAINER (priv->box), priv->arrow);
3124 gtk_widget_show_all (priv->button);
3128 priv->button = gtk_toggle_button_new ();
3129 gtk_button_set_focus_on_click (GTK_BUTTON (priv->button),
3130 priv->focus_on_click);
3132 g_signal_connect (priv->button, "toggled",
3133 G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
3134 gtk_widget_set_parent (priv->button,
3135 gtk_widget_get_parent (child));
3137 priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3138 gtk_container_add (GTK_CONTAINER (priv->button), priv->arrow);
3139 gtk_widget_show_all (priv->button);
3142 g_signal_connect (priv->button, "button-press-event",
3143 G_CALLBACK (gtk_combo_box_menu_button_press),
3145 g_signal_connect (priv->button, "state-flags-changed",
3146 G_CALLBACK (gtk_combo_box_button_state_flags_changed),
3149 /* create our funky menu */
3150 menu = gtk_menu_new ();
3151 gtk_widget_set_name (menu, "gtk-combobox-popup-menu");
3152 gtk_menu_set_reserve_toggle_size (GTK_MENU (menu), FALSE);
3154 g_signal_connect (menu, "key-press-event",
3155 G_CALLBACK (gtk_combo_box_menu_key_press), combo_box);
3156 gtk_combo_box_set_popup_widget (combo_box, menu);
3160 gtk_combo_box_menu_fill (combo_box);
3162 /* the column is needed in tree_column_row_is_sensitive() */
3163 priv->column = gtk_tree_view_column_new ();
3164 g_object_ref_sink (priv->column);
3165 gtk_combo_box_sync_cells (combo_box, GTK_CELL_LAYOUT (priv->column));
3167 gtk_combo_box_update_title (combo_box);
3168 gtk_combo_box_update_sensitivity (combo_box);
3172 gtk_combo_box_menu_fill (GtkComboBox *combo_box)
3174 GtkComboBoxPrivate *priv = combo_box->priv;
3180 menu = priv->popup_widget;
3182 if (priv->add_tearoffs)
3184 GtkWidget *tearoff = gtk_tearoff_menu_item_new ();
3186 gtk_widget_show (tearoff);
3188 if (priv->wrap_width)
3189 gtk_menu_attach (GTK_MENU (menu), tearoff, 0, priv->wrap_width, 0, 1);
3191 gtk_menu_shell_append (GTK_MENU_SHELL (menu), tearoff);
3194 gtk_combo_box_menu_fill_level (combo_box, menu, NULL);
3198 gtk_cell_view_menu_item_new (GtkComboBox *combo_box,
3199 GtkTreeModel *model,
3202 GtkWidget *cell_view;
3207 cell_view = gtk_cell_view_new ();
3208 item = gtk_menu_item_new ();
3209 gtk_container_add (GTK_CONTAINER (item), cell_view);
3211 gtk_cell_view_set_model (GTK_CELL_VIEW (cell_view), model);
3212 path = gtk_tree_model_get_path (model, iter);
3213 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (cell_view), path);
3214 gtk_tree_path_free (path);
3216 gtk_combo_box_sync_cells (combo_box, GTK_CELL_LAYOUT (cell_view));
3217 gtk_widget_get_preferred_size (cell_view, &req, NULL);
3218 gtk_widget_show (cell_view);
3224 gtk_combo_box_menu_fill_level (GtkComboBox *combo_box,
3226 GtkTreeIter *parent)
3228 GtkComboBoxPrivate *priv = combo_box->priv;
3229 GtkTreeModel *model = priv->model;
3230 GtkWidget *item, *submenu, *subitem, *separator;
3232 gboolean is_separator;
3237 n_children = gtk_tree_model_iter_n_children (model, parent);
3240 for (i = 0; i < n_children; i++)
3242 gtk_tree_model_iter_nth_child (model, &iter, parent, i);
3244 if (priv->row_separator_func)
3245 is_separator = priv->row_separator_func (priv->model, &iter,
3246 priv->row_separator_data);
3248 is_separator = FALSE;
3252 item = gtk_separator_menu_item_new ();
3253 path = gtk_tree_model_get_path (model, &iter);
3254 g_object_set_data_full (G_OBJECT (item),
3255 I_("gtk-combo-box-item-path"),
3256 gtk_tree_row_reference_new (model, path),
3257 (GDestroyNotify)gtk_tree_row_reference_free);
3258 gtk_tree_path_free (path);
3262 item = gtk_cell_view_menu_item_new (combo_box, model, &iter);
3263 if (gtk_tree_model_iter_has_child (model, &iter))
3265 submenu = gtk_menu_new ();
3266 gtk_menu_set_reserve_toggle_size (GTK_MENU (submenu), FALSE);
3267 gtk_widget_show (submenu);
3268 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu);
3270 /* Ugly - since menus can only activate leafs, we have to
3271 * duplicate the item inside the submenu.
3273 subitem = gtk_cell_view_menu_item_new (combo_box, model, &iter);
3274 separator = gtk_separator_menu_item_new ();
3275 gtk_widget_show (subitem);
3276 gtk_widget_show (separator);
3277 g_signal_connect (subitem, "activate",
3278 G_CALLBACK (gtk_combo_box_menu_item_activate),
3280 gtk_menu_shell_append (GTK_MENU_SHELL (submenu), subitem);
3281 gtk_menu_shell_append (GTK_MENU_SHELL (submenu), separator);
3283 gtk_combo_box_menu_fill_level (combo_box, submenu, &iter);
3285 g_signal_connect (item, "activate",
3286 G_CALLBACK (gtk_combo_box_menu_item_activate),
3290 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3291 if (priv->wrap_width && menu == priv->popup_widget)
3292 gtk_combo_box_relayout_item (combo_box, item, &iter, last);
3293 gtk_widget_show (item);
3300 gtk_combo_box_menu_destroy (GtkComboBox *combo_box)
3302 GtkComboBoxPrivate *priv = combo_box->priv;
3304 g_signal_handlers_disconnect_matched (priv->button,
3305 G_SIGNAL_MATCH_DATA,
3307 gtk_combo_box_menu_button_press, NULL);
3308 g_signal_handlers_disconnect_matched (priv->button,
3309 G_SIGNAL_MATCH_DATA,
3311 gtk_combo_box_button_state_flags_changed, combo_box);
3313 /* unparent will remove our latest ref */
3314 gtk_widget_unparent (priv->button);
3317 priv->button = NULL;
3319 priv->separator = NULL;
3321 g_object_unref (priv->column);
3322 priv->column = NULL;
3324 /* changing the popup window will unref the menu and the children */
3332 menu_occupied (GtkMenu *menu,
3336 guint bottom_attach)
3340 for (i = GTK_MENU_SHELL (menu)->children; i; i = i->next)
3344 gtk_container_child_get (GTK_CONTAINER (menu),
3348 "bottom-attach", &b,
3352 /* look if this item intersects with the given coordinates */
3353 if (right_attach > l && left_attach < r && bottom_attach > t && top_attach < b)
3361 gtk_combo_box_relayout_item (GtkComboBox *combo_box,
3366 GtkComboBoxPrivate *priv = combo_box->priv;
3367 gint current_col = 0, current_row = 0;
3368 gint rows = 1, cols = 1;
3369 GtkWidget *menu = priv->popup_widget;
3371 if (!GTK_IS_MENU_SHELL (menu))
3374 if (priv->col_column == -1 &&
3375 priv->row_column == -1 &&
3378 gtk_container_child_get (GTK_CONTAINER (menu),
3380 "right-attach", ¤t_col,
3381 "top-attach", ¤t_row,
3383 if (current_col + cols > priv->wrap_width)
3391 if (priv->col_column != -1)
3392 gtk_tree_model_get (priv->model, iter,
3393 priv->col_column, &cols,
3395 if (priv->row_column != -1)
3396 gtk_tree_model_get (priv->model, iter,
3397 priv->row_column, &rows,
3402 if (current_col + cols > priv->wrap_width)
3408 if (!menu_occupied (GTK_MENU (menu),
3409 current_col, current_col + cols,
3410 current_row, current_row + rows))
3417 /* set attach props */
3418 gtk_menu_attach (GTK_MENU (menu), item,
3419 current_col, current_col + cols,
3420 current_row, current_row + rows);
3424 gtk_combo_box_relayout (GtkComboBox *combo_box)
3429 menu = combo_box->priv->popup_widget;
3431 /* do nothing unless we are in menu style and realized */
3432 if (combo_box->priv->tree_view || !GTK_IS_MENU_SHELL (menu))
3435 list = gtk_container_get_children (GTK_CONTAINER (menu));
3437 for (j = g_list_last (list); j; j = j->prev)
3438 gtk_container_remove (GTK_CONTAINER (menu), j->data);
3440 gtk_combo_box_menu_fill (combo_box);
3447 gtk_combo_box_menu_button_press (GtkWidget *widget,
3448 GdkEventButton *event,
3451 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3452 GtkComboBoxPrivate *priv = combo_box->priv;
3454 if (GTK_IS_MENU (priv->popup_widget) &&
3455 event->type == GDK_BUTTON_PRESS && event->button == 1)
3457 if (priv->focus_on_click &&
3458 !gtk_widget_has_focus (priv->button))
3459 gtk_widget_grab_focus (priv->button);
3461 gtk_combo_box_menu_popup (combo_box, event->button, event->time);
3470 gtk_combo_box_menu_item_activate (GtkWidget *item,
3473 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3474 GtkWidget *cell_view;
3478 cell_view = gtk_bin_get_child (GTK_BIN (item));
3480 g_return_if_fail (GTK_IS_CELL_VIEW (cell_view));
3482 path = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (cell_view));
3484 if (gtk_tree_model_get_iter (combo_box->priv->model, &iter, path))
3486 if (gtk_menu_item_get_submenu (GTK_MENU_ITEM (item)) == NULL)
3487 gtk_combo_box_set_active_iter (combo_box, &iter);
3490 gtk_tree_path_free (path);
3492 g_object_set (combo_box,
3493 "editing-canceled", FALSE,
3498 gtk_combo_box_update_sensitivity (GtkComboBox *combo_box)
3501 gboolean sensitive = TRUE; /* fool code checkers */
3503 if (!combo_box->priv->button)
3506 switch (combo_box->priv->button_sensitivity)
3508 case GTK_SENSITIVITY_ON:
3511 case GTK_SENSITIVITY_OFF:
3514 case GTK_SENSITIVITY_AUTO:
3515 sensitive = combo_box->priv->model &&
3516 gtk_tree_model_get_iter_first (combo_box->priv->model, &iter);
3519 g_assert_not_reached ();
3523 gtk_widget_set_sensitive (combo_box->priv->button, sensitive);
3525 /* In list-mode, we also need to update sensitivity of the event box */
3526 if (GTK_IS_TREE_VIEW (combo_box->priv->tree_view)
3527 && combo_box->priv->cell_view)
3528 gtk_widget_set_sensitive (combo_box->priv->box, sensitive);
3532 gtk_combo_box_model_row_inserted (GtkTreeModel *model,
3537 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3539 if (combo_box->priv->tree_view)
3540 gtk_combo_box_list_popup_resize (combo_box);
3542 gtk_combo_box_menu_row_inserted (model, path, iter, user_data);
3544 gtk_combo_box_update_sensitivity (combo_box);
3548 gtk_combo_box_model_row_deleted (GtkTreeModel *model,
3552 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3553 GtkComboBoxPrivate *priv = combo_box->priv;
3555 if (!gtk_tree_row_reference_valid (priv->active_row))
3557 if (priv->cell_view)
3558 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (priv->cell_view), NULL);
3559 g_signal_emit (combo_box, combo_box_signals[CHANGED], 0);
3562 if (priv->tree_view)
3563 gtk_combo_box_list_popup_resize (combo_box);
3565 gtk_combo_box_menu_row_deleted (model, path, user_data);
3567 gtk_combo_box_update_sensitivity (combo_box);
3571 gtk_combo_box_model_rows_reordered (GtkTreeModel *model,
3577 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3579 gtk_tree_row_reference_reordered (G_OBJECT (user_data), path, iter, new_order);
3581 if (!combo_box->priv->tree_view)
3582 gtk_combo_box_menu_rows_reordered (model, path, iter, new_order, user_data);
3586 gtk_combo_box_model_row_changed (GtkTreeModel *model,
3591 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3592 GtkComboBoxPrivate *priv = combo_box->priv;
3593 GtkTreePath *active_path;
3595 /* FIXME this belongs to GtkCellView */
3596 if (gtk_tree_row_reference_valid (priv->active_row))
3598 active_path = gtk_tree_row_reference_get_path (priv->active_row);
3599 if (gtk_tree_path_compare (path, active_path) == 0 &&
3601 gtk_widget_queue_resize (GTK_WIDGET (priv->cell_view));
3602 gtk_tree_path_free (active_path);
3605 if (priv->tree_view)
3606 gtk_combo_box_list_row_changed (model, path, iter, user_data);
3608 gtk_combo_box_menu_row_changed (model, path, iter, user_data);
3612 list_popup_resize_idle (gpointer user_data)
3614 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3615 GtkComboBoxPrivate *priv = combo_box->priv;
3616 gint x, y, width, height;
3618 if (priv->tree_view && gtk_widget_get_mapped (priv->popup_window))
3620 gtk_combo_box_list_position (combo_box, &x, &y, &width, &height);
3622 gtk_widget_set_size_request (priv->popup_window, width, height);
3623 gtk_window_move (GTK_WINDOW (priv->popup_window), x, y);
3626 priv->resize_idle_id = 0;
3632 gtk_combo_box_list_popup_resize (GtkComboBox *combo_box)
3634 GtkComboBoxPrivate *priv = combo_box->priv;
3636 if (!priv->resize_idle_id)
3637 priv->resize_idle_id =
3638 gdk_threads_add_idle (list_popup_resize_idle, combo_box);
3642 gtk_combo_box_model_row_expanded (GtkTreeModel *model,
3647 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3649 gtk_combo_box_list_popup_resize (combo_box);
3654 find_menu_by_path (GtkWidget *menu,
3656 gboolean skip_first)
3662 GtkTreeRowReference *mref;
3666 list = gtk_container_get_children (GTK_CONTAINER (menu));
3669 for (i = list; i; i = i->next)
3671 child = gtk_bin_get_child (i->data);
3672 if (GTK_IS_SEPARATOR_MENU_ITEM (i->data))
3674 mref = g_object_get_data (G_OBJECT (i->data), "gtk-combo-box-item-path");
3677 else if (!gtk_tree_row_reference_valid (mref))
3680 mpath = gtk_tree_row_reference_get_path (mref);
3682 else if (GTK_IS_CELL_VIEW (child))
3690 mpath = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (child));
3695 /* this case is necessary, since the row reference of
3696 * the cell view may already be updated after a deletion
3703 if (gtk_tree_path_compare (mpath, path) == 0)
3705 gtk_tree_path_free (mpath);
3709 if (gtk_tree_path_is_ancestor (mpath, path))
3711 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
3712 if (submenu != NULL)
3714 gtk_tree_path_free (mpath);
3715 item = find_menu_by_path (submenu, path, TRUE);
3719 gtk_tree_path_free (mpath);
3729 dump_menu_tree (GtkWidget *menu,
3736 list = gtk_container_get_children (GTK_CONTAINER (menu));
3737 for (i = list; i; i = i->next)
3739 if (GTK_IS_CELL_VIEW (GTK_BIN (i->data)->child))
3741 path = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (GTK_BIN (i->data)->child));
3742 g_print ("%*s%s\n", 2 * level, " ", gtk_tree_path_to_string (path));
3743 gtk_tree_path_free (path);
3745 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
3746 if (submenu != NULL)
3747 dump_menu_tree (submenu, level + 1);
3756 gtk_combo_box_menu_row_inserted (GtkTreeModel *model,
3761 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3762 GtkComboBoxPrivate *priv = combo_box->priv;
3764 GtkWidget *item, *menu, *separator;
3768 gboolean is_separator;
3770 if (!priv->popup_widget)
3773 depth = gtk_tree_path_get_depth (path);
3774 pos = gtk_tree_path_get_indices (path)[depth - 1];
3777 ppath = gtk_tree_path_copy (path);
3778 gtk_tree_path_up (ppath);
3779 parent = find_menu_by_path (priv->popup_widget, ppath, FALSE);
3780 gtk_tree_path_free (ppath);
3782 menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (parent));
3785 menu = gtk_menu_new ();
3786 gtk_menu_set_reserve_toggle_size (GTK_MENU (menu), FALSE);
3787 gtk_widget_show (menu);
3788 gtk_menu_item_set_submenu (GTK_MENU_ITEM (parent), menu);
3790 /* Ugly - since menus can only activate leaves, we have to
3791 * duplicate the item inside the submenu.
3793 gtk_tree_model_iter_parent (model, &piter, iter);
3794 item = gtk_cell_view_menu_item_new (combo_box, model, &piter);
3795 separator = gtk_separator_menu_item_new ();
3796 g_signal_connect (item, "activate",
3797 G_CALLBACK (gtk_combo_box_menu_item_activate),
3799 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3800 gtk_menu_shell_append (GTK_MENU_SHELL (menu), separator);
3801 if (cell_view_is_sensitive (GTK_CELL_VIEW (gtk_bin_get_child (GTK_BIN (item)))))
3803 gtk_widget_show (item);
3804 gtk_widget_show (separator);
3811 menu = priv->popup_widget;
3812 if (priv->add_tearoffs)
3816 if (priv->row_separator_func)
3817 is_separator = priv->row_separator_func (model, iter,
3818 priv->row_separator_data);
3820 is_separator = FALSE;
3824 item = gtk_separator_menu_item_new ();
3825 g_object_set_data_full (G_OBJECT (item),
3826 I_("gtk-combo-box-item-path"),
3827 gtk_tree_row_reference_new (model, path),
3828 (GDestroyNotify)gtk_tree_row_reference_free);
3832 item = gtk_cell_view_menu_item_new (combo_box, model, iter);
3834 g_signal_connect (item, "activate",
3835 G_CALLBACK (gtk_combo_box_menu_item_activate),
3839 gtk_widget_show (item);
3840 gtk_menu_shell_insert (GTK_MENU_SHELL (menu), item, pos);
3844 gtk_combo_box_menu_row_deleted (GtkTreeModel *model,
3848 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3849 GtkComboBoxPrivate *priv = combo_box->priv;
3853 if (!priv->popup_widget)
3856 item = find_menu_by_path (priv->popup_widget, path, FALSE);
3857 menu = gtk_widget_get_parent (item);
3858 gtk_container_remove (GTK_CONTAINER (menu), item);
3860 if (gtk_tree_path_get_depth (path) > 1)
3862 GtkTreePath *parent_path;
3866 parent_path = gtk_tree_path_copy (path);
3867 gtk_tree_path_up (parent_path);
3868 gtk_tree_model_get_iter (model, &iter, parent_path);
3870 if (!gtk_tree_model_iter_has_child (model, &iter))
3872 parent = find_menu_by_path (priv->popup_widget,
3873 parent_path, FALSE);
3874 gtk_menu_item_set_submenu (GTK_MENU_ITEM (parent), NULL);
3880 gtk_combo_box_menu_rows_reordered (GtkTreeModel *model,
3886 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3888 gtk_combo_box_relayout (combo_box);
3892 gtk_combo_box_menu_row_changed (GtkTreeModel *model,
3897 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3898 GtkComboBoxPrivate *priv = combo_box->priv;
3900 gboolean is_separator;
3902 if (!priv->popup_widget)
3905 item = find_menu_by_path (priv->popup_widget, path, FALSE);
3907 if (priv->row_separator_func)
3908 is_separator = priv->row_separator_func (model, iter,
3909 priv->row_separator_data);
3911 is_separator = FALSE;
3913 if (is_separator != GTK_IS_SEPARATOR_MENU_ITEM (item))
3915 gtk_combo_box_menu_row_deleted (model, path, combo_box);
3916 gtk_combo_box_menu_row_inserted (model, path, iter, combo_box);
3919 if (priv->wrap_width &&
3920 gtk_widget_get_parent (item) == priv->popup_widget)
3922 GtkWidget *pitem = NULL;
3925 prev = gtk_tree_path_copy (path);
3927 if (gtk_tree_path_prev (prev))
3928 pitem = find_menu_by_path (priv->popup_widget, prev, FALSE);
3930 gtk_tree_path_free (prev);
3932 /* unattach item so gtk_combo_box_relayout_item() won't spuriously
3934 gtk_container_child_set (GTK_CONTAINER (priv->popup_widget),
3939 "bottom-attach", -1,
3942 gtk_combo_box_relayout_item (combo_box, item, iter, pitem);
3945 gtk_combo_box_update_requested_width (combo_box, path);
3953 gtk_combo_box_list_setup (GtkComboBox *combo_box)
3955 GtkComboBoxPrivate *priv = combo_box->priv;
3956 GtkTreeSelection *sel;
3958 GtkWidget *widget = GTK_WIDGET (combo_box);
3960 priv->button = gtk_toggle_button_new ();
3961 child = gtk_bin_get_child (GTK_BIN (combo_box));
3962 gtk_widget_set_parent (priv->button,
3963 gtk_widget_get_parent (child));
3964 g_signal_connect (priv->button, "button-press-event",
3965 G_CALLBACK (gtk_combo_box_list_button_pressed), combo_box);
3966 g_signal_connect (priv->button, "toggled",
3967 G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
3969 priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3970 gtk_container_add (GTK_CONTAINER (priv->button), priv->arrow);
3971 priv->separator = NULL;
3972 gtk_widget_show_all (priv->button);
3974 if (priv->cell_view)
3976 GtkStyleContext *context;
3977 GtkStateFlags state;
3980 context = gtk_widget_get_style_context (widget);
3981 state = gtk_widget_get_state_flags (widget);
3983 gtk_style_context_get (context, state,
3984 "background-color", &color,
3987 gtk_cell_view_set_background_rgba (GTK_CELL_VIEW (priv->cell_view), color);
3988 gdk_rgba_free (color);
3990 priv->box = gtk_event_box_new ();
3991 gtk_event_box_set_visible_window (GTK_EVENT_BOX (priv->box),
3994 if (priv->has_frame)
3996 priv->cell_view_frame = gtk_frame_new (NULL);
3997 gtk_frame_set_shadow_type (GTK_FRAME (priv->cell_view_frame),
4002 combo_box->priv->cell_view_frame = gtk_event_box_new ();
4003 gtk_event_box_set_visible_window (GTK_EVENT_BOX (combo_box->priv->cell_view_frame),
4007 gtk_widget_set_parent (priv->cell_view_frame,
4008 gtk_widget_get_parent (child));
4009 gtk_container_add (GTK_CONTAINER (priv->cell_view_frame), priv->box);
4010 gtk_widget_show_all (priv->cell_view_frame);
4012 g_signal_connect (priv->box, "button-press-event",
4013 G_CALLBACK (gtk_combo_box_list_button_pressed),
4017 priv->tree_view = gtk_tree_view_new ();
4018 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));
4019 gtk_tree_selection_set_mode (sel, GTK_SELECTION_BROWSE);
4020 gtk_tree_selection_set_select_function (sel,
4021 gtk_combo_box_list_select_func,
4023 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->tree_view),
4025 gtk_tree_view_set_hover_selection (GTK_TREE_VIEW (priv->tree_view),
4027 if (priv->row_separator_func)
4028 gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (priv->tree_view),
4029 priv->row_separator_func,
4030 priv->row_separator_data,
4033 gtk_tree_view_set_model (GTK_TREE_VIEW (priv->tree_view), priv->model);
4035 priv->column = gtk_tree_view_column_new ();
4036 gtk_tree_view_append_column (GTK_TREE_VIEW (priv->tree_view), priv->column);
4039 gtk_combo_box_sync_cells (combo_box,
4040 GTK_CELL_LAYOUT (priv->column));
4042 if (gtk_tree_row_reference_valid (priv->active_row))
4046 path = gtk_tree_row_reference_get_path (priv->active_row);
4047 gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->tree_view),
4049 gtk_tree_path_free (path);
4052 /* set sample/popup widgets */
4053 gtk_combo_box_set_popup_widget (combo_box, priv->tree_view);
4055 g_signal_connect (priv->tree_view, "key-press-event",
4056 G_CALLBACK (gtk_combo_box_list_key_press),
4058 g_signal_connect (priv->tree_view, "enter-notify-event",
4059 G_CALLBACK (gtk_combo_box_list_enter_notify),
4061 g_signal_connect (priv->tree_view, "row-expanded",
4062 G_CALLBACK (gtk_combo_box_model_row_expanded),
4064 g_signal_connect (priv->tree_view, "row-collapsed",
4065 G_CALLBACK (gtk_combo_box_model_row_expanded),
4067 g_signal_connect (priv->popup_window, "button-press-event",
4068 G_CALLBACK (gtk_combo_box_list_button_pressed),
4070 g_signal_connect (priv->popup_window, "button-release-event",
4071 G_CALLBACK (gtk_combo_box_list_button_released),
4074 gtk_widget_show (priv->tree_view);
4076 gtk_combo_box_update_sensitivity (combo_box);
4080 gtk_combo_box_list_destroy (GtkComboBox *combo_box)
4082 GtkComboBoxPrivate *priv = combo_box->priv;
4084 /* disconnect signals */
4085 g_signal_handlers_disconnect_matched (priv->tree_view,
4086 G_SIGNAL_MATCH_DATA,
4087 0, 0, NULL, NULL, combo_box);
4088 g_signal_handlers_disconnect_matched (priv->button,
4089 G_SIGNAL_MATCH_DATA,
4091 gtk_combo_box_list_button_pressed,
4093 g_signal_handlers_disconnect_matched (priv->popup_window,
4094 G_SIGNAL_MATCH_DATA,
4096 gtk_combo_box_list_button_pressed,
4098 g_signal_handlers_disconnect_matched (priv->popup_window,
4099 G_SIGNAL_MATCH_DATA,
4101 gtk_combo_box_list_button_released,
4104 g_signal_handlers_disconnect_matched (priv->popup_window,
4105 G_SIGNAL_MATCH_DATA,
4107 gtk_combo_box_child_show,
4110 g_signal_handlers_disconnect_matched (priv->popup_window,
4111 G_SIGNAL_MATCH_DATA,
4113 gtk_combo_box_child_hide,
4117 g_signal_handlers_disconnect_matched (priv->box,
4118 G_SIGNAL_MATCH_DATA,
4120 gtk_combo_box_list_button_pressed,
4123 /* destroy things (unparent will kill the latest ref from us)
4124 * last unref on button will destroy the arrow
4126 gtk_widget_unparent (priv->button);
4127 priv->button = NULL;
4130 if (priv->cell_view)
4132 g_object_set (priv->cell_view,
4133 "background-set", FALSE,
4137 if (priv->cell_view_frame)
4139 gtk_widget_unparent (priv->cell_view_frame);
4140 priv->cell_view_frame = NULL;
4144 if (priv->scroll_timer)
4146 g_source_remove (priv->scroll_timer);
4147 priv->scroll_timer = 0;
4150 if (priv->resize_idle_id)
4152 g_source_remove (priv->resize_idle_id);
4153 priv->resize_idle_id = 0;
4156 gtk_widget_destroy (priv->tree_view);
4158 priv->tree_view = NULL;
4159 if (priv->popup_widget)
4161 g_object_unref (priv->popup_widget);
4162 priv->popup_widget = NULL;
4169 gtk_combo_box_list_button_pressed (GtkWidget *widget,
4170 GdkEventButton *event,
4173 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
4174 GtkComboBoxPrivate *priv = combo_box->priv;
4176 GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *)event);
4178 if (ewidget == priv->popup_window)
4181 if ((ewidget != priv->button && ewidget != priv->box) ||
4182 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->button)))
4185 if (priv->focus_on_click &&
4186 !gtk_widget_has_focus (priv->button))
4187 gtk_widget_grab_focus (priv->button);
4189 gtk_combo_box_popup_for_device (combo_box, event->device);
4191 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button), TRUE);
4193 priv->auto_scroll = FALSE;
4194 if (priv->scroll_timer == 0)
4195 priv->scroll_timer = gdk_threads_add_timeout (SCROLL_TIME,
4196 (GSourceFunc) gtk_combo_box_list_scroll_timeout,
4199 priv->popup_in_progress = TRUE;
4205 gtk_combo_box_list_button_released (GtkWidget *widget,
4206 GdkEventButton *event,
4210 GtkTreePath *path = NULL;
4213 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
4214 GtkComboBoxPrivate *priv = combo_box->priv;
4216 gboolean popup_in_progress = FALSE;
4218 GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *)event);
4220 if (priv->popup_in_progress)
4222 popup_in_progress = TRUE;
4223 priv->popup_in_progress = FALSE;
4226 gtk_tree_view_set_hover_expand (GTK_TREE_VIEW (priv->tree_view),
4228 if (priv->scroll_timer)
4230 g_source_remove (priv->scroll_timer);
4231 priv->scroll_timer = 0;
4234 if (ewidget != priv->tree_view)
4236 if ((ewidget == priv->button ||
4237 ewidget == priv->box) &&
4238 !popup_in_progress &&
4239 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->button)))
4241 gtk_combo_box_popdown (combo_box);
4245 /* released outside treeview */
4246 if (ewidget != priv->button && ewidget != priv->box)
4248 gtk_combo_box_popdown (combo_box);
4256 /* select something cool */
4257 ret = gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (priv->tree_view),
4263 return TRUE; /* clicked outside window? */
4265 gtk_tree_model_get_iter (priv->model, &iter, path);
4266 gtk_tree_path_free (path);
4268 gtk_combo_box_popdown (combo_box);
4270 if (tree_column_row_is_sensitive (combo_box, &iter))
4271 gtk_combo_box_set_active_iter (combo_box, &iter);
4277 gtk_combo_box_menu_key_press (GtkWidget *widget,
4281 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
4283 if (!gtk_bindings_activate_event (G_OBJECT (widget), event))
4285 /* The menu hasn't managed the
4286 * event, forward it to the combobox
4288 gtk_bindings_activate_event (G_OBJECT (combo_box), event);
4295 gtk_combo_box_list_key_press (GtkWidget *widget,
4299 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
4302 if (event->keyval == GDK_KEY_Return || event->keyval == GDK_KEY_ISO_Enter || event->keyval == GDK_KEY_KP_Enter ||
4303 event->keyval == GDK_KEY_space || event->keyval == GDK_KEY_KP_Space)
4305 GtkTreeModel *model = NULL;
4307 gtk_combo_box_popdown (combo_box);
4309 if (combo_box->priv->model)
4311 GtkTreeSelection *sel;
4313 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (combo_box->priv->tree_view));
4315 if (gtk_tree_selection_get_selected (sel, &model, &iter))
4316 gtk_combo_box_set_active_iter (combo_box, &iter);
4322 if (!gtk_bindings_activate_event (G_OBJECT (widget), event))
4324 /* The list hasn't managed the
4325 * event, forward it to the combobox
4327 gtk_bindings_activate_event (G_OBJECT (combo_box), event);
4334 gtk_combo_box_list_auto_scroll (GtkComboBox *combo_box,
4339 GtkAllocation allocation;
4340 GtkWidget *tree_view = combo_box->priv->tree_view;
4343 gtk_widget_get_allocation (tree_view, &allocation);
4345 adj = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window));
4346 if (adj && adj->upper - adj->lower > adj->page_size)
4348 if (x <= allocation.x &&
4349 adj->lower < adj->value)
4351 value = adj->value - (allocation.x - x + 1);
4352 gtk_adjustment_set_value (adj, value);
4354 else if (x >= allocation.x + allocation.width &&
4355 adj->upper - adj->page_size > adj->value)
4357 value = adj->value + (x - allocation.x - allocation.width + 1);
4358 gtk_adjustment_set_value (adj, MAX (value, 0.0));
4362 adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window));
4363 if (adj && adj->upper - adj->lower > adj->page_size)
4365 if (y <= allocation.y &&
4366 adj->lower < adj->value)
4368 value = adj->value - (allocation.y - y + 1);
4369 gtk_adjustment_set_value (adj, value);
4371 else if (y >= allocation.height &&
4372 adj->upper - adj->page_size > adj->value)
4374 value = adj->value + (y - allocation.height + 1);
4375 gtk_adjustment_set_value (adj, MAX (value, 0.0));
4381 gtk_combo_box_list_scroll_timeout (GtkComboBox *combo_box)
4383 GtkComboBoxPrivate *priv = combo_box->priv;
4386 if (priv->auto_scroll)
4388 gdk_window_get_device_position (gtk_widget_get_window (priv->tree_view),
4391 gtk_combo_box_list_auto_scroll (combo_box, x, y);
4398 gtk_combo_box_list_enter_notify (GtkWidget *widget,
4399 GdkEventCrossing *event,
4402 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
4404 combo_box->priv->auto_scroll = TRUE;
4410 gtk_combo_box_list_select_func (GtkTreeSelection *selection,
4411 GtkTreeModel *model,
4413 gboolean path_currently_selected,
4417 gboolean sensitive = FALSE;
4419 for (list = selection->tree_view->priv->columns; list && !sensitive; list = list->next)
4421 GList *cells, *cell;
4422 gboolean cell_sensitive, cell_visible;
4424 GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN (list->data);
4426 if (!gtk_tree_view_column_get_visible (column))
4429 gtk_tree_model_get_iter (model, &iter, path);
4430 gtk_tree_view_column_cell_set_cell_data (column, model, &iter,
4433 cell = cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
4436 g_object_get (cell->data,
4437 "sensitive", &cell_sensitive,
4438 "visible", &cell_visible,
4441 if (cell_visible && cell_sensitive)
4446 g_list_free (cells);
4448 sensitive = cell_sensitive;
4455 gtk_combo_box_list_row_changed (GtkTreeModel *model,
4460 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
4462 gtk_combo_box_update_requested_width (combo_box, path);
4466 * GtkCellLayout implementation
4470 pack_start_recurse (GtkWidget *menu,
4471 GtkCellRenderer *cell,
4478 list = gtk_container_get_children (GTK_CONTAINER (menu));
4479 for (i = list; i; i = i->next)
4481 child = gtk_bin_get_child (GTK_BIN (i->data));
4482 if (GTK_IS_CELL_LAYOUT (child))
4483 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (child),
4486 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4487 if (submenu != NULL)
4488 pack_start_recurse (submenu, cell, expand);
4495 gtk_combo_box_cell_layout_pack_start (GtkCellLayout *layout,
4496 GtkCellRenderer *cell,
4499 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4500 ComboCellInfo *info;
4501 GtkComboBoxPrivate *priv;
4503 priv = combo_box->priv;
4505 g_object_ref_sink (cell);
4507 info = g_slice_new0 (ComboCellInfo);
4509 info->expand = expand;
4510 info->pack = GTK_PACK_START;
4512 priv->cells = g_slist_append (priv->cells, info);
4514 if (priv->cell_view)
4516 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->cell_view),
4522 gtk_tree_view_column_pack_start (priv->column, cell, expand);
4524 if (GTK_IS_MENU (priv->popup_widget))
4525 pack_start_recurse (priv->popup_widget, cell, expand);
4529 pack_end_recurse (GtkWidget *menu,
4530 GtkCellRenderer *cell,
4537 list = gtk_container_get_children (GTK_CONTAINER (menu));
4538 for (i = list; i; i = i->next)
4540 child = gtk_bin_get_child (GTK_BIN (i->data));
4541 if (GTK_IS_CELL_LAYOUT (child))
4542 gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (child),
4545 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4546 if (submenu != NULL)
4547 pack_end_recurse (submenu, cell, expand);
4554 gtk_combo_box_cell_layout_pack_end (GtkCellLayout *layout,
4555 GtkCellRenderer *cell,
4558 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4559 ComboCellInfo *info;
4560 GtkComboBoxPrivate *priv;
4562 priv = combo_box->priv;
4564 g_object_ref_sink (cell);
4566 info = g_slice_new0 (ComboCellInfo);
4568 info->expand = expand;
4569 info->pack = GTK_PACK_END;
4571 priv->cells = g_slist_append (priv->cells, info);
4573 if (priv->cell_view)
4574 gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (priv->cell_view),
4578 gtk_tree_view_column_pack_end (priv->column, cell, expand);
4580 if (GTK_IS_MENU (priv->popup_widget))
4581 pack_end_recurse (priv->popup_widget, cell, expand);
4585 gtk_combo_box_cell_layout_get_cells (GtkCellLayout *layout)
4587 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4589 GList *retval = NULL;
4591 for (list = combo_box->priv->cells; list; list = list->next)
4593 ComboCellInfo *info = (ComboCellInfo *)list->data;
4595 retval = g_list_prepend (retval, info->cell);
4598 return g_list_reverse (retval);
4602 clear_recurse (GtkWidget *menu)
4608 list = gtk_container_get_children (GTK_CONTAINER (menu));
4609 for (i = list; i; i = i->next)
4611 child = gtk_bin_get_child (GTK_BIN (i->data));
4612 if (GTK_IS_CELL_LAYOUT (child))
4613 gtk_cell_layout_clear (GTK_CELL_LAYOUT (child));
4615 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4616 if (submenu != NULL)
4617 clear_recurse (submenu);
4624 gtk_combo_box_cell_layout_clear (GtkCellLayout *layout)
4626 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4627 GtkComboBoxPrivate *priv = combo_box->priv;
4630 if (priv->cell_view)
4631 gtk_cell_layout_clear (GTK_CELL_LAYOUT (priv->cell_view));
4634 gtk_tree_view_column_clear (priv->column);
4636 for (i = priv->cells; i; i = i->next)
4638 ComboCellInfo *info = (ComboCellInfo *)i->data;
4640 gtk_combo_box_cell_layout_clear_attributes (layout, info->cell);
4641 g_object_unref (info->cell);
4642 g_slice_free (ComboCellInfo, info);
4645 g_slist_free (priv->cells);
4648 if (GTK_IS_MENU (priv->popup_widget))
4649 clear_recurse (priv->popup_widget);
4653 add_attribute_recurse (GtkWidget *menu,
4654 GtkCellRenderer *cell,
4655 const gchar *attribute,
4662 list = gtk_container_get_children (GTK_CONTAINER (menu));
4663 for (i = list; i; i = i->next)
4665 child = gtk_bin_get_child (GTK_BIN (i->data));
4666 if (GTK_IS_CELL_LAYOUT (child))
4667 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (child),
4668 cell, attribute, column);
4670 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4671 if (submenu != NULL)
4672 add_attribute_recurse (submenu, cell, attribute, column);
4679 gtk_combo_box_cell_layout_add_attribute (GtkCellLayout *layout,
4680 GtkCellRenderer *cell,
4681 const gchar *attribute,
4684 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4685 ComboCellInfo *info;
4687 info = gtk_combo_box_get_cell_info (combo_box, cell);
4688 g_return_if_fail (info != NULL);
4690 info->attributes = g_slist_prepend (info->attributes,
4691 GINT_TO_POINTER (column));
4692 info->attributes = g_slist_prepend (info->attributes,
4693 g_strdup (attribute));
4695 if (combo_box->priv->cell_view)
4696 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box->priv->cell_view),
4697 cell, attribute, column);
4699 if (combo_box->priv->column)
4700 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box->priv->column),
4701 cell, attribute, column);
4703 if (GTK_IS_MENU (combo_box->priv->popup_widget))
4704 add_attribute_recurse (combo_box->priv->popup_widget, cell, attribute, column);
4705 gtk_widget_queue_resize (GTK_WIDGET (combo_box));
4709 combo_cell_data_func (GtkCellLayout *cell_layout,
4710 GtkCellRenderer *cell,
4711 GtkTreeModel *tree_model,
4715 ComboCellInfo *info = (ComboCellInfo *)data;
4716 GtkWidget *parent = NULL;
4721 info->func (cell_layout, cell, tree_model, iter, info->func_data);
4723 if (GTK_IS_WIDGET (cell_layout))
4724 parent = gtk_widget_get_parent (GTK_WIDGET (cell_layout));
4726 if (GTK_IS_MENU_ITEM (parent) &&
4727 gtk_menu_item_get_submenu (GTK_MENU_ITEM (parent)))
4728 g_object_set (cell, "sensitive", TRUE, NULL);
4733 set_cell_data_func_recurse (GtkWidget *menu,
4734 GtkCellRenderer *cell,
4735 ComboCellInfo *info)
4739 GtkWidget *cell_view;
4741 list = gtk_container_get_children (GTK_CONTAINER (menu));
4742 for (i = list; i; i = i->next)
4744 cell_view = gtk_bin_get_child (GTK_BIN (i->data));
4745 if (GTK_IS_CELL_LAYOUT (cell_view))
4747 /* Override sensitivity for inner nodes; we don't
4748 * want menuitems with submenus to appear insensitive
4750 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (cell_view),
4752 combo_cell_data_func,
4754 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4755 if (submenu != NULL)
4756 set_cell_data_func_recurse (submenu, cell, info);
4764 gtk_combo_box_cell_layout_set_cell_data_func (GtkCellLayout *layout,
4765 GtkCellRenderer *cell,
4766 GtkCellLayoutDataFunc func,
4768 GDestroyNotify destroy)
4770 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4771 GtkComboBoxPrivate *priv = combo_box->priv;
4772 ComboCellInfo *info;
4774 info = gtk_combo_box_get_cell_info (combo_box, cell);
4775 g_return_if_fail (info != NULL);
4779 GDestroyNotify d = info->destroy;
4781 info->destroy = NULL;
4782 d (info->func_data);
4786 info->func_data = func_data;
4787 info->destroy = destroy;
4789 if (priv->cell_view)
4790 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (priv->cell_view), cell, func, func_data, NULL);
4793 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (priv->column), cell, func, func_data, NULL);
4795 if (GTK_IS_MENU (priv->popup_widget))
4796 set_cell_data_func_recurse (priv->popup_widget, cell, info);
4798 gtk_widget_queue_resize (GTK_WIDGET (combo_box));
4802 clear_attributes_recurse (GtkWidget *menu,
4803 GtkCellRenderer *cell)
4809 list = gtk_container_get_children (GTK_CONTAINER (menu));
4810 for (i = list; i; i = i->next)
4812 child = gtk_bin_get_child (GTK_BIN (i->data));
4813 if (GTK_IS_CELL_LAYOUT (child))
4814 gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (child), cell);
4816 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4817 if (submenu != NULL)
4818 clear_attributes_recurse (submenu, cell);
4825 gtk_combo_box_cell_layout_clear_attributes (GtkCellLayout *layout,
4826 GtkCellRenderer *cell)
4828 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4829 GtkComboBoxPrivate *priv;
4830 ComboCellInfo *info;
4833 priv = combo_box->priv;
4835 info = gtk_combo_box_get_cell_info (combo_box, cell);
4836 g_return_if_fail (info != NULL);
4838 list = info->attributes;
4839 while (list && list->next)
4841 g_free (list->data);
4842 list = list->next->next;
4844 g_slist_free (info->attributes);
4845 info->attributes = NULL;
4847 if (priv->cell_view)
4848 gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (priv->cell_view), cell);
4851 gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (priv->column), cell);
4853 if (GTK_IS_MENU (priv->popup_widget))
4854 clear_attributes_recurse (priv->popup_widget, cell);
4856 gtk_widget_queue_resize (GTK_WIDGET (combo_box));
4860 reorder_recurse (GtkWidget *menu,
4861 GtkCellRenderer *cell,
4868 list = gtk_container_get_children (GTK_CONTAINER (menu));
4869 for (i = list; i; i = i->next)
4871 child = gtk_bin_get_child (GTK_BIN (i->data));
4872 if (GTK_IS_CELL_LAYOUT (child))
4873 gtk_cell_layout_reorder (GTK_CELL_LAYOUT (child),
4876 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4877 if (submenu != NULL)
4878 reorder_recurse (submenu, cell, position);
4885 gtk_combo_box_cell_layout_reorder (GtkCellLayout *layout,
4886 GtkCellRenderer *cell,
4889 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4890 GtkComboBoxPrivate *priv;
4891 ComboCellInfo *info;
4894 priv = combo_box->priv;
4896 info = gtk_combo_box_get_cell_info (combo_box, cell);
4898 g_return_if_fail (info != NULL);
4899 g_return_if_fail (position >= 0);
4901 link = g_slist_find (priv->cells, info);
4903 g_return_if_fail (link != NULL);
4905 priv->cells = g_slist_delete_link (priv->cells, link);
4906 priv->cells = g_slist_insert (priv->cells, info, position);
4908 if (priv->cell_view)
4909 gtk_cell_layout_reorder (GTK_CELL_LAYOUT (priv->cell_view),
4913 gtk_cell_layout_reorder (GTK_CELL_LAYOUT (priv->column),
4916 if (GTK_IS_MENU (priv->popup_widget))
4917 reorder_recurse (priv->popup_widget, cell, position);
4919 gtk_widget_queue_draw (GTK_WIDGET (combo_box));
4927 * gtk_combo_box_new:
4929 * Creates a new empty #GtkComboBox.
4931 * Return value: A new #GtkComboBox.
4936 gtk_combo_box_new (void)
4938 return g_object_new (GTK_TYPE_COMBO_BOX, NULL);
4942 * gtk_combo_box_new_with_entry:
4944 * Creates a new empty #GtkComboBox with an entry.
4946 * Return value: A new #GtkComboBox.
4949 gtk_combo_box_new_with_entry (void)
4951 return g_object_new (GTK_TYPE_COMBO_BOX, "has-entry", TRUE, NULL);
4955 * gtk_combo_box_new_with_model:
4956 * @model: A #GtkTreeModel.
4958 * Creates a new #GtkComboBox with the model initialized to @model.
4960 * Return value: A new #GtkComboBox.
4965 gtk_combo_box_new_with_model (GtkTreeModel *model)
4967 GtkComboBox *combo_box;
4969 g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL);
4971 combo_box = g_object_new (GTK_TYPE_COMBO_BOX, "model", model, NULL);
4973 return GTK_WIDGET (combo_box);
4977 * gtk_combo_box_new_with_model_and_entry:
4978 * @model: A #GtkTreeModel
4980 * Creates a new empty #GtkComboBox with an entry
4981 * and with the model initialized to @model.
4983 * Return value: A new #GtkComboBox
4986 gtk_combo_box_new_with_model_and_entry (GtkTreeModel *model)
4988 return g_object_new (GTK_TYPE_COMBO_BOX,
4995 * gtk_combo_box_get_wrap_width:
4996 * @combo_box: A #GtkComboBox
4998 * Returns the wrap width which is used to determine the number of columns
4999 * for the popup menu. If the wrap width is larger than 1, the combo box
5002 * Returns: the wrap width.
5007 gtk_combo_box_get_wrap_width (GtkComboBox *combo_box)
5009 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), -1);
5011 return combo_box->priv->wrap_width;
5015 * gtk_combo_box_set_wrap_width:
5016 * @combo_box: A #GtkComboBox
5017 * @width: Preferred number of columns
5019 * Sets the wrap width of @combo_box to be @width. The wrap width is basically
5020 * the preferred number of columns when you want the popup to be layed out
5026 gtk_combo_box_set_wrap_width (GtkComboBox *combo_box,
5029 GtkComboBoxPrivate *priv;
5031 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5032 g_return_if_fail (width >= 0);
5034 priv = combo_box->priv;
5036 if (width != priv->wrap_width)
5038 priv->wrap_width = width;
5040 gtk_combo_box_check_appearance (combo_box);
5041 gtk_combo_box_relayout (combo_box);
5043 g_object_notify (G_OBJECT (combo_box), "wrap-width");
5048 * gtk_combo_box_get_row_span_column:
5049 * @combo_box: A #GtkComboBox
5051 * Returns the column with row span information for @combo_box.
5053 * Returns: the row span column.
5058 gtk_combo_box_get_row_span_column (GtkComboBox *combo_box)
5060 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), -1);
5062 return combo_box->priv->row_column;
5066 * gtk_combo_box_set_row_span_column:
5067 * @combo_box: A #GtkComboBox.
5068 * @row_span: A column in the model passed during construction.
5070 * Sets the column with row span information for @combo_box to be @row_span.
5071 * The row span column contains integers which indicate how many rows
5072 * an item should span.
5077 gtk_combo_box_set_row_span_column (GtkComboBox *combo_box,
5080 GtkComboBoxPrivate *priv;
5083 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5085 priv = combo_box->priv;
5087 col = gtk_tree_model_get_n_columns (priv->model);
5088 g_return_if_fail (row_span >= -1 && row_span < col);
5090 if (row_span != priv->row_column)
5092 priv->row_column = row_span;
5094 gtk_combo_box_relayout (combo_box);
5096 g_object_notify (G_OBJECT (combo_box), "row-span-column");
5101 * gtk_combo_box_get_column_span_column:
5102 * @combo_box: A #GtkComboBox
5104 * Returns the column with column span information for @combo_box.
5106 * Returns: the column span column.
5111 gtk_combo_box_get_column_span_column (GtkComboBox *combo_box)
5113 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), -1);
5115 return combo_box->priv->col_column;
5119 * gtk_combo_box_set_column_span_column:
5120 * @combo_box: A #GtkComboBox
5121 * @column_span: A column in the model passed during construction
5123 * Sets the column with column span information for @combo_box to be
5124 * @column_span. The column span column contains integers which indicate
5125 * how many columns an item should span.
5130 gtk_combo_box_set_column_span_column (GtkComboBox *combo_box,
5133 GtkComboBoxPrivate *priv;
5136 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5138 priv = combo_box->priv;
5140 col = gtk_tree_model_get_n_columns (priv->model);
5141 g_return_if_fail (column_span >= -1 && column_span < col);
5143 if (column_span != priv->col_column)
5145 priv->col_column = column_span;
5147 gtk_combo_box_relayout (combo_box);
5149 g_object_notify (G_OBJECT (combo_box), "column-span-column");
5154 * gtk_combo_box_get_active:
5155 * @combo_box: A #GtkComboBox
5157 * Returns the index of the currently active item, or -1 if there's no
5158 * active item. If the model is a non-flat treemodel, and the active item
5159 * is not an immediate child of the root of the tree, this function returns
5160 * <literal>gtk_tree_path_get_indices (path)[0]</literal>, where
5161 * <literal>path</literal> is the #GtkTreePath of the active item.
5163 * Return value: An integer which is the index of the currently active item,
5164 * or -1 if there's no active item.
5169 gtk_combo_box_get_active (GtkComboBox *combo_box)
5171 GtkComboBoxPrivate *priv;
5174 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), 0);
5176 priv = combo_box->priv;
5178 if (gtk_tree_row_reference_valid (priv->active_row))
5182 path = gtk_tree_row_reference_get_path (priv->active_row);
5183 result = gtk_tree_path_get_indices (path)[0];
5184 gtk_tree_path_free (path);
5193 * gtk_combo_box_set_active:
5194 * @combo_box: A #GtkComboBox
5195 * @index_: An index in the model passed during construction, or -1 to have
5198 * Sets the active item of @combo_box to be the item at @index.
5203 gtk_combo_box_set_active (GtkComboBox *combo_box,
5206 GtkTreePath *path = NULL;
5207 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5208 g_return_if_fail (index_ >= -1);
5210 if (combo_box->priv->model == NULL)
5212 /* Save index, in case the model is set after the index */
5213 combo_box->priv->active = index_;
5219 path = gtk_tree_path_new_from_indices (index_, -1);
5221 gtk_combo_box_set_active_internal (combo_box, path);
5224 gtk_tree_path_free (path);
5228 gtk_combo_box_set_active_internal (GtkComboBox *combo_box,
5231 GtkComboBoxPrivate *priv = combo_box->priv;
5232 GtkTreePath *active_path;
5235 /* Remember whether the initially active row is valid. */
5236 gboolean is_valid_row_reference = gtk_tree_row_reference_valid (priv->active_row);
5238 if (path && is_valid_row_reference)
5240 active_path = gtk_tree_row_reference_get_path (priv->active_row);
5241 path_cmp = gtk_tree_path_compare (path, active_path);
5242 gtk_tree_path_free (active_path);
5247 if (priv->active_row)
5249 gtk_tree_row_reference_free (priv->active_row);
5250 priv->active_row = NULL;
5255 if (priv->tree_view)
5256 gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view)));
5259 GtkMenu *menu = GTK_MENU (priv->popup_widget);
5261 if (GTK_IS_MENU (menu))
5262 gtk_menu_set_active (menu, -1);
5265 if (priv->cell_view)
5266 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (priv->cell_view), NULL);
5269 * Do not emit a "changed" signal when an already invalid selection was
5270 * now set to invalid.
5272 if (!is_valid_row_reference)
5278 gtk_tree_row_reference_new (priv->model, path);
5280 if (priv->tree_view)
5282 gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->tree_view),
5285 else if (GTK_IS_MENU (priv->popup_widget))
5287 /* FIXME handle nested menus better */
5288 gtk_menu_set_active (GTK_MENU (priv->popup_widget),
5289 gtk_tree_path_get_indices (path)[0]);
5292 if (priv->cell_view)
5293 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (priv->cell_view),
5297 g_signal_emit (combo_box, combo_box_signals[CHANGED], 0);
5298 g_object_notify (G_OBJECT (combo_box), "active");
5299 if (combo_box->priv->id_column >= 0)
5300 g_object_notify (G_OBJECT (combo_box), "active-id");
5305 * gtk_combo_box_get_active_iter:
5306 * @combo_box: A #GtkComboBox
5307 * @iter: (out): The uninitialized #GtkTreeIter
5309 * Sets @iter to point to the current active item, if it exists.
5311 * Return value: %TRUE, if @iter was set
5316 gtk_combo_box_get_active_iter (GtkComboBox *combo_box,
5322 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
5324 if (!gtk_tree_row_reference_valid (combo_box->priv->active_row))
5327 path = gtk_tree_row_reference_get_path (combo_box->priv->active_row);
5328 result = gtk_tree_model_get_iter (combo_box->priv->model, iter, path);
5329 gtk_tree_path_free (path);
5335 * gtk_combo_box_set_active_iter:
5336 * @combo_box: A #GtkComboBox
5337 * @iter: (allow-none): The #GtkTreeIter, or %NULL
5339 * Sets the current active item to be the one referenced by @iter, or
5340 * unsets the active item if @iter is %NULL.
5345 gtk_combo_box_set_active_iter (GtkComboBox *combo_box,
5348 GtkTreePath *path = NULL;
5350 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5353 path = gtk_tree_model_get_path (gtk_combo_box_get_model (combo_box), iter);
5355 gtk_combo_box_set_active_internal (combo_box, path);
5356 gtk_tree_path_free (path);
5360 * gtk_combo_box_set_model:
5361 * @combo_box: A #GtkComboBox
5362 * @model: (allow-none): A #GtkTreeModel
5364 * Sets the model used by @combo_box to be @model. Will unset a previously set
5365 * model (if applicable). If model is %NULL, then it will unset the model.
5367 * Note that this function does not clear the cell renderers, you have to
5368 * call gtk_cell_layout_clear() yourself if you need to set up different
5369 * cell renderers for the new model.
5374 gtk_combo_box_set_model (GtkComboBox *combo_box,
5375 GtkTreeModel *model)
5377 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5378 g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
5380 if (model == combo_box->priv->model)
5383 gtk_combo_box_unset_model (combo_box);
5388 combo_box->priv->model = model;
5389 g_object_ref (combo_box->priv->model);
5391 combo_box->priv->inserted_id =
5392 g_signal_connect (combo_box->priv->model, "row-inserted",
5393 G_CALLBACK (gtk_combo_box_model_row_inserted),
5395 combo_box->priv->deleted_id =
5396 g_signal_connect (combo_box->priv->model, "row-deleted",
5397 G_CALLBACK (gtk_combo_box_model_row_deleted),
5399 combo_box->priv->reordered_id =
5400 g_signal_connect (combo_box->priv->model, "rows-reordered",
5401 G_CALLBACK (gtk_combo_box_model_rows_reordered),
5403 combo_box->priv->changed_id =
5404 g_signal_connect (combo_box->priv->model, "row-changed",
5405 G_CALLBACK (gtk_combo_box_model_row_changed),
5408 if (combo_box->priv->tree_view)
5411 gtk_tree_view_set_model (GTK_TREE_VIEW (combo_box->priv->tree_view),
5412 combo_box->priv->model);
5413 gtk_combo_box_list_popup_resize (combo_box);
5418 if (combo_box->priv->popup_widget)
5419 gtk_combo_box_menu_fill (combo_box);
5423 if (combo_box->priv->cell_view)
5424 gtk_cell_view_set_model (GTK_CELL_VIEW (combo_box->priv->cell_view),
5425 combo_box->priv->model);
5427 if (combo_box->priv->active != -1)
5429 /* If an index was set in advance, apply it now */
5430 gtk_combo_box_set_active (combo_box, combo_box->priv->active);
5431 combo_box->priv->active = -1;
5435 gtk_combo_box_update_sensitivity (combo_box);
5437 g_object_notify (G_OBJECT (combo_box), "model");
5441 * gtk_combo_box_get_model:
5442 * @combo_box: A #GtkComboBox
5444 * Returns the #GtkTreeModel which is acting as data source for @combo_box.
5446 * Return value: (transfer none): A #GtkTreeModel which was passed
5447 * during construction.
5452 gtk_combo_box_get_model (GtkComboBox *combo_box)
5454 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
5456 return combo_box->priv->model;
5460 gtk_combo_box_real_move_active (GtkComboBox *combo_box,
5461 GtkScrollType scroll)
5464 GtkTreeIter new_iter;
5465 gboolean active_iter;
5468 if (!combo_box->priv->model)
5470 gtk_widget_error_bell (GTK_WIDGET (combo_box));
5474 active_iter = gtk_combo_box_get_active_iter (combo_box, &iter);
5478 case GTK_SCROLL_STEP_BACKWARD:
5479 case GTK_SCROLL_STEP_UP:
5480 case GTK_SCROLL_STEP_LEFT:
5483 found = tree_prev (combo_box, combo_box->priv->model,
5484 &iter, &new_iter, FALSE);
5487 /* else fall through */
5489 case GTK_SCROLL_PAGE_FORWARD:
5490 case GTK_SCROLL_PAGE_DOWN:
5491 case GTK_SCROLL_PAGE_RIGHT:
5492 case GTK_SCROLL_END:
5493 found = tree_last (combo_box, combo_box->priv->model, &new_iter, FALSE);
5496 case GTK_SCROLL_STEP_FORWARD:
5497 case GTK_SCROLL_STEP_DOWN:
5498 case GTK_SCROLL_STEP_RIGHT:
5501 found = tree_next (combo_box, combo_box->priv->model,
5502 &iter, &new_iter, FALSE);
5505 /* else fall through */
5507 case GTK_SCROLL_PAGE_BACKWARD:
5508 case GTK_SCROLL_PAGE_UP:
5509 case GTK_SCROLL_PAGE_LEFT:
5510 case GTK_SCROLL_START:
5511 found = tree_first (combo_box, combo_box->priv->model, &new_iter, FALSE);
5518 if (found && active_iter)
5520 GtkTreePath *old_path;
5521 GtkTreePath *new_path;
5523 old_path = gtk_tree_model_get_path (combo_box->priv->model, &iter);
5524 new_path = gtk_tree_model_get_path (combo_box->priv->model, &new_iter);
5526 if (gtk_tree_path_compare (old_path, new_path) == 0)
5529 gtk_tree_path_free (old_path);
5530 gtk_tree_path_free (new_path);
5535 gtk_combo_box_set_active_iter (combo_box, &new_iter);
5539 gtk_widget_error_bell (GTK_WIDGET (combo_box));
5544 gtk_combo_box_mnemonic_activate (GtkWidget *widget,
5545 gboolean group_cycling)
5547 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
5549 if (combo_box->priv->has_entry)
5553 child = gtk_bin_get_child (GTK_BIN (combo_box));
5555 gtk_widget_grab_focus (child);
5558 gtk_widget_grab_focus (combo_box->priv->button);
5564 gtk_combo_box_grab_focus (GtkWidget *widget)
5566 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
5568 if (combo_box->priv->has_entry)
5572 child = gtk_bin_get_child (GTK_BIN (combo_box));
5574 gtk_widget_grab_focus (child);
5577 gtk_widget_grab_focus (combo_box->priv->button);
5581 gtk_combo_box_destroy (GtkWidget *widget)
5583 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
5585 if (combo_box->priv->popup_idle_id > 0)
5587 g_source_remove (combo_box->priv->popup_idle_id);
5588 combo_box->priv->popup_idle_id = 0;
5591 gtk_combo_box_popdown (combo_box);
5593 if (combo_box->priv->row_separator_destroy)
5594 combo_box->priv->row_separator_destroy (combo_box->priv->row_separator_data);
5596 combo_box->priv->row_separator_func = NULL;
5597 combo_box->priv->row_separator_data = NULL;
5598 combo_box->priv->row_separator_destroy = NULL;
5600 GTK_WIDGET_CLASS (gtk_combo_box_parent_class)->destroy (widget);
5601 combo_box->priv->cell_view = NULL;
5605 gtk_combo_box_entry_contents_changed (GtkEntry *entry,
5608 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
5611 * Fixes regression reported in bug #574059. The old functionality relied on
5612 * bug #572478. As a bugfix, we now emit the "changed" signal ourselves
5613 * when the selection was already set to -1.
5615 if (gtk_combo_box_get_active(combo_box) == -1)
5616 g_signal_emit_by_name (combo_box, "changed");
5618 gtk_combo_box_set_active (combo_box, -1);
5622 gtk_combo_box_entry_active_changed (GtkComboBox *combo_box,
5625 GtkComboBoxPrivate *priv = combo_box->priv;
5626 GtkTreeModel *model;
5629 if (gtk_combo_box_get_active_iter (combo_box, &iter))
5631 GtkEntry *entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (combo_box)));
5635 GValue value = {0,};
5637 g_signal_handlers_block_by_func (entry,
5638 gtk_combo_box_entry_contents_changed,
5641 model = gtk_combo_box_get_model (combo_box);
5643 gtk_tree_model_get_value (model, &iter,
5644 priv->text_column, &value);
5645 g_object_set_property (G_OBJECT (entry), "text", &value);
5646 g_value_unset (&value);
5648 g_signal_handlers_unblock_by_func (entry,
5649 gtk_combo_box_entry_contents_changed,
5656 gtk_combo_box_constructor (GType type,
5657 guint n_construct_properties,
5658 GObjectConstructParam *construct_properties)
5661 GtkComboBox *combo_box;
5662 GtkComboBoxPrivate *priv;
5664 object = G_OBJECT_CLASS (gtk_combo_box_parent_class)->constructor
5665 (type, n_construct_properties, construct_properties);
5667 combo_box = GTK_COMBO_BOX (object);
5668 priv = combo_box->priv;
5670 if (priv->has_entry)
5674 entry = gtk_entry_new ();
5675 gtk_widget_show (entry);
5676 gtk_container_add (GTK_CONTAINER (combo_box), entry);
5678 priv->text_renderer = gtk_cell_renderer_text_new ();
5679 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box),
5680 priv->text_renderer, TRUE);
5682 gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), -1);
5684 g_signal_connect (combo_box, "changed",
5685 G_CALLBACK (gtk_combo_box_entry_active_changed), NULL);
5693 gtk_combo_box_dispose(GObject* object)
5695 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
5697 if (GTK_IS_MENU (combo_box->priv->popup_widget))
5699 gtk_combo_box_menu_destroy (combo_box);
5700 gtk_menu_detach (GTK_MENU (combo_box->priv->popup_widget));
5701 combo_box->priv->popup_widget = NULL;
5704 G_OBJECT_CLASS (gtk_combo_box_parent_class)->dispose (object);
5708 gtk_combo_box_finalize (GObject *object)
5710 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
5713 if (GTK_IS_TREE_VIEW (combo_box->priv->tree_view))
5714 gtk_combo_box_list_destroy (combo_box);
5716 if (combo_box->priv->popup_window)
5717 gtk_widget_destroy (combo_box->priv->popup_window);
5719 gtk_combo_box_unset_model (combo_box);
5721 for (i = combo_box->priv->cells; i; i = i->next)
5723 ComboCellInfo *info = (ComboCellInfo *)i->data;
5724 GSList *list = info->attributes;
5727 info->destroy (info->func_data);
5729 while (list && list->next)
5731 g_free (list->data);
5732 list = list->next->next;
5734 g_slist_free (info->attributes);
5736 g_object_unref (info->cell);
5737 g_slice_free (ComboCellInfo, info);
5739 g_slist_free (combo_box->priv->cells);
5741 g_free (combo_box->priv->tearoff_title);
5743 G_OBJECT_CLASS (gtk_combo_box_parent_class)->finalize (object);
5748 gtk_cell_editable_key_press (GtkWidget *widget,
5752 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
5754 if (event->keyval == GDK_KEY_Escape)
5756 g_object_set (combo_box,
5757 "editing-canceled", TRUE,
5759 gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (combo_box));
5760 gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (combo_box));
5764 else if (event->keyval == GDK_KEY_Return ||
5765 event->keyval == GDK_KEY_ISO_Enter ||
5766 event->keyval == GDK_KEY_KP_Enter)
5768 gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (combo_box));
5769 gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (combo_box));
5778 popdown_idle (gpointer data)
5780 GtkComboBox *combo_box;
5782 combo_box = GTK_COMBO_BOX (data);
5784 gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (combo_box));
5785 gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (combo_box));
5787 g_object_unref (combo_box);
5793 popdown_handler (GtkWidget *widget,
5796 gdk_threads_add_idle (popdown_idle, g_object_ref (data));
5800 popup_idle (gpointer data)
5802 GtkComboBox *combo_box;
5804 combo_box = GTK_COMBO_BOX (data);
5806 if (GTK_IS_MENU (combo_box->priv->popup_widget) &&
5807 combo_box->priv->cell_view)
5808 g_signal_connect_object (combo_box->priv->popup_widget,
5809 "unmap", G_CALLBACK (popdown_handler),
5812 /* we unset this if a menu item is activated */
5813 g_object_set (combo_box,
5814 "editing-canceled", TRUE,
5816 gtk_combo_box_popup (combo_box);
5818 combo_box->priv->popup_idle_id = 0;
5819 combo_box->priv->activate_button = 0;
5820 combo_box->priv->activate_time = 0;
5826 gtk_combo_box_start_editing (GtkCellEditable *cell_editable,
5829 GtkComboBox *combo_box = GTK_COMBO_BOX (cell_editable);
5832 combo_box->priv->is_cell_renderer = TRUE;
5834 if (combo_box->priv->cell_view)
5836 g_signal_connect_object (combo_box->priv->button, "key-press-event",
5837 G_CALLBACK (gtk_cell_editable_key_press),
5840 gtk_widget_grab_focus (combo_box->priv->button);
5844 child = gtk_bin_get_child (GTK_BIN (combo_box));
5846 g_signal_connect_object (child, "key-press-event",
5847 G_CALLBACK (gtk_cell_editable_key_press),
5850 gtk_widget_grab_focus (child);
5851 gtk_widget_set_can_focus (combo_box->priv->button, FALSE);
5854 /* we do the immediate popup only for the optionmenu-like
5857 if (combo_box->priv->is_cell_renderer &&
5858 combo_box->priv->cell_view && !combo_box->priv->tree_view)
5860 if (event && event->type == GDK_BUTTON_PRESS)
5862 GdkEventButton *event_button = (GdkEventButton *)event;
5864 combo_box->priv->activate_button = event_button->button;
5865 combo_box->priv->activate_time = event_button->time;
5868 combo_box->priv->popup_idle_id =
5869 gdk_threads_add_idle (popup_idle, combo_box);
5875 * gtk_combo_box_get_add_tearoffs:
5876 * @combo_box: a #GtkComboBox
5878 * Gets the current value of the :add-tearoffs property.
5880 * Return value: the current value of the :add-tearoffs property.
5883 gtk_combo_box_get_add_tearoffs (GtkComboBox *combo_box)
5885 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
5887 return combo_box->priv->add_tearoffs;
5891 * gtk_combo_box_set_add_tearoffs:
5892 * @combo_box: a #GtkComboBox
5893 * @add_tearoffs: %TRUE to add tearoff menu items
5895 * Sets whether the popup menu should have a tearoff
5901 gtk_combo_box_set_add_tearoffs (GtkComboBox *combo_box,
5902 gboolean add_tearoffs)
5904 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5906 add_tearoffs = add_tearoffs != FALSE;
5908 if (combo_box->priv->add_tearoffs != add_tearoffs)
5910 combo_box->priv->add_tearoffs = add_tearoffs;
5911 gtk_combo_box_check_appearance (combo_box);
5912 gtk_combo_box_relayout (combo_box);
5913 g_object_notify (G_OBJECT (combo_box), "add-tearoffs");
5918 * gtk_combo_box_get_title:
5919 * @combo_box: a #GtkComboBox
5921 * Gets the current title of the menu in tearoff mode. See
5922 * gtk_combo_box_set_add_tearoffs().
5924 * Returns: the menu's title in tearoff mode. This is an internal copy of the
5925 * string which must not be freed.
5929 G_CONST_RETURN gchar*
5930 gtk_combo_box_get_title (GtkComboBox *combo_box)
5932 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
5934 return combo_box->priv->tearoff_title;
5938 gtk_combo_box_update_title (GtkComboBox *combo_box)
5940 gtk_combo_box_check_appearance (combo_box);
5942 if (combo_box->priv->popup_widget &&
5943 GTK_IS_MENU (combo_box->priv->popup_widget))
5944 gtk_menu_set_title (GTK_MENU (combo_box->priv->popup_widget),
5945 combo_box->priv->tearoff_title);
5949 * gtk_combo_box_set_title:
5950 * @combo_box: a #GtkComboBox
5951 * @title: a title for the menu in tearoff mode
5953 * Sets the menu's title in tearoff mode.
5958 gtk_combo_box_set_title (GtkComboBox *combo_box,
5961 GtkComboBoxPrivate *priv;
5963 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5965 priv = combo_box->priv;
5967 if (strcmp (title ? title : "",
5968 priv->tearoff_title ? priv->tearoff_title : "") != 0)
5970 g_free (priv->tearoff_title);
5971 priv->tearoff_title = g_strdup (title);
5973 gtk_combo_box_update_title (combo_box);
5975 g_object_notify (G_OBJECT (combo_box), "tearoff-title");
5981 * gtk_combo_box_set_popup_fixed_width:
5982 * @combo_box: a #GtkComboBox
5983 * @fixed: whether to use a fixed popup width
5985 * Specifies whether the popup's width should be a fixed width
5986 * matching the allocated width of the combo box.
5991 gtk_combo_box_set_popup_fixed_width (GtkComboBox *combo_box,
5994 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5996 if (combo_box->priv->popup_fixed_width != fixed)
5998 combo_box->priv->popup_fixed_width = fixed;
6000 g_object_notify (G_OBJECT (combo_box), "popup-fixed-width");
6005 * gtk_combo_box_get_popup_fixed_width:
6006 * @combo_box: a #GtkComboBox
6008 * Gets whether the popup uses a fixed width matching
6009 * the allocated width of the combo box.
6011 * Returns: %TRUE if the popup uses a fixed width
6016 gtk_combo_box_get_popup_fixed_width (GtkComboBox *combo_box)
6018 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
6020 return combo_box->priv->popup_fixed_width;
6025 * gtk_combo_box_get_popup_accessible:
6026 * @combo_box: a #GtkComboBox
6028 * Gets the accessible object corresponding to the combo box's popup.
6030 * This function is mostly intended for use by accessibility technologies;
6031 * applications should have little use for it.
6033 * Returns: (transfer none): the accessible object corresponding
6034 * to the combo box's popup.
6039 gtk_combo_box_get_popup_accessible (GtkComboBox *combo_box)
6043 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
6045 if (combo_box->priv->popup_widget)
6047 atk_obj = gtk_widget_get_accessible (combo_box->priv->popup_widget);
6055 * gtk_combo_box_get_row_separator_func:
6056 * @combo_box: a #GtkComboBox
6058 * Returns the current row separator function.
6060 * Return value: the current row separator function.
6064 GtkTreeViewRowSeparatorFunc
6065 gtk_combo_box_get_row_separator_func (GtkComboBox *combo_box)
6067 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
6069 return combo_box->priv->row_separator_func;
6073 * gtk_combo_box_set_row_separator_func:
6074 * @combo_box: a #GtkComboBox
6075 * @func: a #GtkTreeViewRowSeparatorFunc
6076 * @data: (allow-none): user data to pass to @func, or %NULL
6077 * @destroy: (allow-none): destroy notifier for @data, or %NULL
6079 * Sets the row separator function, which is used to determine
6080 * whether a row should be drawn as a separator. If the row separator
6081 * function is %NULL, no separators are drawn. This is the default value.
6086 gtk_combo_box_set_row_separator_func (GtkComboBox *combo_box,
6087 GtkTreeViewRowSeparatorFunc func,
6089 GDestroyNotify destroy)
6091 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
6093 if (combo_box->priv->row_separator_destroy)
6094 combo_box->priv->row_separator_destroy (combo_box->priv->row_separator_data);
6096 combo_box->priv->row_separator_func = func;
6097 combo_box->priv->row_separator_data = data;
6098 combo_box->priv->row_separator_destroy = destroy;
6100 if (combo_box->priv->tree_view)
6101 gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (combo_box->priv->tree_view),
6104 gtk_combo_box_relayout (combo_box);
6106 gtk_widget_queue_draw (GTK_WIDGET (combo_box));
6110 * gtk_combo_box_set_button_sensitivity:
6111 * @combo_box: a #GtkComboBox
6112 * @sensitivity: specify the sensitivity of the dropdown button
6114 * Sets whether the dropdown button of the combo box should be
6115 * always sensitive (%GTK_SENSITIVITY_ON), never sensitive (%GTK_SENSITIVITY_OFF)
6116 * or only if there is at least one item to display (%GTK_SENSITIVITY_AUTO).
6121 gtk_combo_box_set_button_sensitivity (GtkComboBox *combo_box,
6122 GtkSensitivityType sensitivity)
6124 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
6126 if (combo_box->priv->button_sensitivity != sensitivity)
6128 combo_box->priv->button_sensitivity = sensitivity;
6129 gtk_combo_box_update_sensitivity (combo_box);
6131 g_object_notify (G_OBJECT (combo_box), "button-sensitivity");
6136 * gtk_combo_box_get_button_sensitivity:
6137 * @combo_box: a #GtkComboBox
6139 * Returns whether the combo box sets the dropdown button
6140 * sensitive or not when there are no items in the model.
6142 * Return Value: %GTK_SENSITIVITY_ON if the dropdown button
6143 * is sensitive when the model is empty, %GTK_SENSITIVITY_OFF
6144 * if the button is always insensitive or
6145 * %GTK_SENSITIVITY_AUTO if it is only sensitive as long as
6146 * the model has one item to be selected.
6151 gtk_combo_box_get_button_sensitivity (GtkComboBox *combo_box)
6153 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
6155 return combo_box->priv->button_sensitivity;
6160 * gtk_combo_box_get_has_entry:
6161 * @combo_box: a #GtkComboBox
6163 * Returns whether the combo box has an entry.
6165 * Return Value: whether there is an entry in @combo_box.
6170 gtk_combo_box_get_has_entry (GtkComboBox *combo_box)
6172 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
6174 return combo_box->priv->has_entry;
6178 * gtk_combo_box_set_entry_text_column:
6179 * @combo_box: A #GtkComboBox
6180 * @text_column: A column in @model to get the strings from for
6181 * the internal entry
6183 * Sets the model column which @combo_box should use to get strings from
6184 * to be @text_column. The column @text_column in the model of @combo_box
6185 * must be of type %G_TYPE_STRING.
6187 * This is only relevant if @combo_box has been created with
6188 * #GtkComboBox:has-entry as %TRUE.
6193 gtk_combo_box_set_entry_text_column (GtkComboBox *combo_box,
6196 GtkComboBoxPrivate *priv = combo_box->priv;
6197 GtkTreeModel *model;
6199 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
6201 model = gtk_combo_box_get_model (combo_box);
6203 g_return_if_fail (text_column >= 0);
6204 g_return_if_fail (model == NULL || text_column < gtk_tree_model_get_n_columns (model));
6206 priv->text_column = text_column;
6208 if (priv->text_renderer != NULL)
6209 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box),
6210 priv->text_renderer,
6211 "text", text_column,
6216 * gtk_combo_box_get_entry_text_column:
6217 * @combo_box: A #GtkComboBox.
6219 * Returns the column which @combo_box is using to get the strings
6220 * from to display in the internal entry.
6222 * Return value: A column in the data source model of @combo_box.
6227 gtk_combo_box_get_entry_text_column (GtkComboBox *combo_box)
6229 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), 0);
6231 return combo_box->priv->text_column;
6235 * gtk_combo_box_set_focus_on_click:
6236 * @combo: a #GtkComboBox
6237 * @focus_on_click: whether the combo box grabs focus when clicked
6240 * Sets whether the combo box will grab focus when it is clicked with
6241 * the mouse. Making mouse clicks not grab focus is useful in places
6242 * like toolbars where you don't want the keyboard focus removed from
6243 * the main area of the application.
6248 gtk_combo_box_set_focus_on_click (GtkComboBox *combo_box,
6249 gboolean focus_on_click)
6251 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
6253 focus_on_click = focus_on_click != FALSE;
6255 if (combo_box->priv->focus_on_click != focus_on_click)
6257 combo_box->priv->focus_on_click = focus_on_click;
6259 if (combo_box->priv->button)
6260 gtk_button_set_focus_on_click (GTK_BUTTON (combo_box->priv->button),
6263 g_object_notify (G_OBJECT (combo_box), "focus-on-click");
6268 * gtk_combo_box_get_focus_on_click:
6269 * @combo: a #GtkComboBox
6271 * Returns whether the combo box grabs focus when it is clicked
6272 * with the mouse. See gtk_combo_box_set_focus_on_click().
6274 * Return value: %TRUE if the combo box grabs focus when it is
6275 * clicked with the mouse.
6280 gtk_combo_box_get_focus_on_click (GtkComboBox *combo_box)
6282 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
6284 return combo_box->priv->focus_on_click;
6289 gtk_combo_box_buildable_custom_tag_start (GtkBuildable *buildable,
6290 GtkBuilder *builder,
6292 const gchar *tagname,
6293 GMarkupParser *parser,
6296 if (parent_buildable_iface->custom_tag_start (buildable, builder, child,
6297 tagname, parser, data))
6300 return _gtk_cell_layout_buildable_custom_tag_start (buildable, builder, child,
6301 tagname, parser, data);
6305 gtk_combo_box_buildable_custom_tag_end (GtkBuildable *buildable,
6306 GtkBuilder *builder,
6308 const gchar *tagname,
6311 if (strcmp (tagname, "attributes") == 0)
6312 _gtk_cell_layout_buildable_custom_tag_end (buildable, builder, child, tagname,
6315 parent_buildable_iface->custom_tag_end (buildable, builder, child, tagname,
6320 gtk_combo_box_buildable_get_internal_child (GtkBuildable *buildable,
6321 GtkBuilder *builder,
6322 const gchar *childname)
6324 GtkComboBox *combo_box = GTK_COMBO_BOX (buildable);
6326 if (combo_box->priv->has_entry && strcmp (childname, "entry") == 0)
6327 return G_OBJECT (gtk_bin_get_child (GTK_BIN (buildable)));
6329 return parent_buildable_iface->get_internal_child (buildable, builder, childname);
6333 gtk_combo_box_remeasure (GtkComboBox *combo_box)
6335 GtkComboBoxPrivate *priv = combo_box->priv;
6340 !gtk_tree_model_get_iter_first (priv->model, &iter))
6343 priv->minimum_width = priv->natural_width = 0;
6345 path = gtk_tree_path_new_from_indices (0, -1);
6349 gint row_min = 0, row_nat = 0;
6351 if (priv->cell_view)
6352 gtk_cell_view_get_desired_width_of_row (GTK_CELL_VIEW (priv->cell_view),
6353 path, &row_min, &row_nat);
6355 priv->minimum_width = MAX (priv->minimum_width, row_min);
6356 priv->natural_width = MAX (priv->natural_width, row_nat);
6358 gtk_tree_path_next (path);
6360 while (gtk_tree_model_iter_next (priv->model, &iter));
6362 gtk_tree_path_free (path);
6367 gtk_combo_box_measure_height_for_width (GtkComboBox *combo_box,
6373 GtkComboBoxPrivate *priv = combo_box->priv;
6376 gint child_min, child_nat;
6378 child = gtk_bin_get_child (GTK_BIN (combo_box));
6380 gtk_widget_get_preferred_height_for_width (child, avail_width,
6381 &child_min, &child_nat);
6384 !gtk_tree_model_get_iter_first (priv->model, &iter))
6387 path = gtk_tree_path_new_from_indices (0, -1);
6391 gint row_min = 0, row_nat = 0;
6393 if (priv->cell_view)
6394 gtk_cell_view_get_desired_height_for_width_of_row (GTK_CELL_VIEW (priv->cell_view),
6396 &row_min, &row_nat);
6398 child_min = MAX (child_min, row_min);
6399 child_nat = MAX (child_nat, row_nat);
6401 gtk_tree_path_next (path);
6403 while (gtk_tree_model_iter_next (priv->model, &iter));
6405 gtk_tree_path_free (path);
6410 *min_height = child_min;
6412 *nat_height = child_nat;
6417 gtk_combo_box_get_preferred_width (GtkWidget *widget,
6421 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
6422 GtkComboBoxPrivate *priv = combo_box->priv;
6423 gint focus_width, focus_pad;
6424 gint font_size, arrow_size;
6425 PangoContext *context;
6426 PangoFontMetrics *metrics;
6427 PangoFontDescription *font_desc;
6429 gint minimum_width, natural_width;
6430 gint child_min, child_nat;
6431 GtkStyleContext *style_context;
6432 GtkStateFlags state;
6434 child = gtk_bin_get_child (GTK_BIN (widget));
6437 gtk_widget_get_preferred_width (child, &child_min, &child_nat);
6438 gtk_combo_box_remeasure (combo_box);
6440 child_min = MAX (child_min, priv->minimum_width);
6441 child_nat = MAX (child_nat, priv->natural_width);
6443 gtk_widget_style_get (GTK_WIDGET (widget),
6444 "focus-line-width", &focus_width,
6445 "focus-padding", &focus_pad,
6446 "arrow-size", &arrow_size,
6449 style_context = gtk_widget_get_style_context (widget);
6450 state = gtk_widget_get_state_flags (widget);
6452 gtk_style_context_get (style_context, state,
6456 context = gtk_widget_get_pango_context (GTK_WIDGET (widget));
6457 metrics = pango_context_get_metrics (context, font_desc,
6458 pango_context_get_language (context));
6459 font_size = PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) +
6460 pango_font_metrics_get_descent (metrics));
6461 pango_font_metrics_unref (metrics);
6462 pango_font_description_free (font_desc);
6464 arrow_size = MAX (arrow_size, font_size);
6466 gtk_widget_set_size_request (priv->arrow, arrow_size, arrow_size);
6468 if (!priv->tree_view)
6472 if (priv->cell_view)
6474 gint sep_width, arrow_width;
6475 gint border_width, xthickness, xpad;
6477 border_width = gtk_container_get_border_width (GTK_CONTAINER (combo_box));
6478 xthickness = get_widget_border_thickness (priv->button);
6480 gtk_widget_get_preferred_width (priv->separator, &sep_width, NULL);
6481 gtk_widget_get_preferred_width (priv->arrow, &arrow_width, NULL);
6483 xpad = 2*(border_width + xthickness + focus_width + focus_pad);
6485 minimum_width = child_min + sep_width + arrow_width + xpad;
6486 natural_width = child_nat + sep_width + arrow_width + xpad;
6490 gint but_width, but_nat_width;
6492 gtk_widget_get_preferred_width (priv->button,
6493 &but_width, &but_nat_width);
6495 minimum_width = child_min + but_width;
6496 natural_width = child_nat + but_nat_width;
6502 gint button_width, button_nat_width;
6504 /* sample + frame */
6505 minimum_width = child_min;
6506 natural_width = child_nat;
6508 minimum_width += 2 * focus_width;
6509 natural_width += 2 * focus_width;
6511 if (priv->cell_view_frame)
6513 if (priv->has_frame)
6515 gint border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->cell_view_frame));
6516 gint xpad = 2 * (border_width + get_widget_border_thickness (priv->cell_view_frame));
6518 minimum_width += xpad;
6519 natural_width += xpad;
6524 gtk_widget_get_preferred_width (priv->button,
6525 &button_width, &button_nat_width);
6527 minimum_width += button_width;
6528 natural_width += button_nat_width;
6531 if (GTK_SHADOW_NONE != priv->shadow_type)
6535 thickness = get_widget_border_thickness (GTK_WIDGET (widget));
6536 minimum_width += 2 * thickness;
6537 natural_width += 2 * thickness;
6541 *minimum_size = minimum_width;
6544 *natural_size = natural_width;
6548 gtk_combo_box_get_preferred_height (GtkWidget *widget,
6554 /* Combo box is height-for-width only
6555 * (so we always just reserve enough height for the minimum width) */
6556 GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, &min_width, NULL);
6557 GTK_WIDGET_GET_CLASS (widget)->get_preferred_height_for_width (widget, min_width, minimum_size, natural_size);
6561 gtk_combo_box_get_preferred_width_for_height (GtkWidget *widget,
6566 /* Combo box is height-for-width only
6567 * (so we assume we always reserved enough height for the minimum width) */
6568 GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, minimum_size, natural_size);
6573 gtk_combo_box_get_preferred_height_for_width (GtkWidget *widget,
6578 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
6579 GtkComboBoxPrivate *priv = combo_box->priv;
6580 gint focus_width, focus_pad;
6581 gint min_height, nat_height;
6584 gtk_widget_style_get (GTK_WIDGET (widget),
6585 "focus-line-width", &focus_width,
6586 "focus-padding", &focus_pad,
6591 if (GTK_SHADOW_NONE != priv->shadow_type)
6592 size -= get_widget_border_thickness (widget);
6594 if (!priv->tree_view)
6597 if (priv->cell_view)
6599 /* calculate x/y padding and separator/arrow size */
6600 gint sep_width, arrow_width, sep_height, arrow_height;
6601 gint border_width, xthickness, ythickness, xpad, ypad;
6603 border_width = gtk_container_get_border_width (GTK_CONTAINER (combo_box));
6604 xthickness = ythickness = get_widget_border_thickness (priv->button);
6606 gtk_widget_get_preferred_width (priv->separator, &sep_width, NULL);
6607 gtk_widget_get_preferred_width (priv->arrow, &arrow_width, NULL);
6608 gtk_widget_get_preferred_height_for_width (priv->separator,
6609 sep_width, &sep_height, NULL);
6610 gtk_widget_get_preferred_height_for_width (priv->arrow,
6611 arrow_width, &arrow_height, NULL);
6613 xpad = 2*(border_width + xthickness + focus_width + focus_pad);
6614 ypad = 2*(border_width + ythickness + focus_width + focus_pad);
6616 size -= sep_width + arrow_width + xpad;
6618 gtk_combo_box_measure_height_for_width (combo_box, size, &min_height, &nat_height);
6620 arrow_height = MAX (arrow_height, sep_height);
6621 min_height = MAX (min_height, arrow_height);
6622 nat_height = MAX (nat_height, arrow_height);
6629 /* there is a custom child widget inside (no priv->cell_view) */
6630 gint but_width, but_height;
6632 gtk_widget_get_preferred_width (priv->button, &but_width, NULL);
6633 gtk_widget_get_preferred_height_for_width (priv->button,
6634 but_width, &but_height, NULL);
6638 gtk_combo_box_measure_height_for_width (combo_box, size, &min_height, &nat_height);
6640 min_height = MAX (min_height, but_height);
6641 nat_height = MAX (nat_height, but_height);
6647 gint but_width, but_height;
6648 gint xpad = 0, ypad = 0;
6650 gtk_widget_get_preferred_width (priv->button, &but_width, NULL);
6651 gtk_widget_get_preferred_height_for_width (priv->button,
6652 but_width, &but_height, NULL);
6654 if (priv->cell_view_frame && priv->has_frame)
6659 border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->cell_view_frame));
6660 thickness = get_widget_border_thickness (GTK_WIDGET (priv->cell_view_frame));
6662 xpad = 2 * (border_width + thickness);
6663 ypad = 2 * (border_width + thickness);
6667 size -= 2 * focus_width;
6670 gtk_combo_box_measure_height_for_width (combo_box, size, &min_height, &nat_height);
6672 min_height = MAX (min_height, but_height);
6673 nat_height = MAX (nat_height, but_height);
6679 if (GTK_SHADOW_NONE != priv->shadow_type)
6683 thickness = get_widget_border_thickness (widget);
6684 min_height += 2 * thickness;
6685 nat_height += 2 * thickness;
6689 *minimum_size = min_height;
6692 *natural_size = nat_height;
6696 * gtk_combo_box_set_id_column:
6697 * @combo_box: A #GtkComboBox
6698 * @id_column: A column in @model to get string IDs for values from
6700 * Sets the model column which @combo_box should use to get string IDs
6701 * for values from. The column @id_column in the model of @combo_box
6702 * must be of type %G_TYPE_STRING.
6707 gtk_combo_box_set_id_column (GtkComboBox *combo_box,
6710 GtkComboBoxPrivate *priv = combo_box->priv;
6711 GtkTreeModel *model;
6713 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
6715 if (id_column != priv->id_column)
6717 model = gtk_combo_box_get_model (combo_box);
6719 g_return_if_fail (id_column >= 0);
6720 g_return_if_fail (model == NULL ||
6721 id_column < gtk_tree_model_get_n_columns (model));
6723 priv->id_column = id_column;
6725 g_object_notify (G_OBJECT (combo_box), "id-column");
6726 g_object_notify (G_OBJECT (combo_box), "active-id");
6731 * gtk_combo_box_get_id_column:
6732 * @combo_box: A #GtkComboBox
6734 * Returns the column which @combo_box is using to get string IDs
6737 * Return value: A column in the data source model of @combo_box.
6742 gtk_combo_box_get_id_column (GtkComboBox *combo_box)
6744 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), 0);
6746 return combo_box->priv->id_column;
6750 * gtk_combo_box_get_active_id:
6751 * @combo_box: a #GtkComboBox
6753 * Returns the ID of the active row of @combo_box. This value is taken
6754 * from the active row and the column specified by the 'id-column'
6755 * property of @combo_box (see gtk_combo_box_set_id_column()).
6757 * The returned value is an interned string which means that you can
6758 * compare the pointer by value to other interned strings and that you
6761 * If the 'id-column' property of @combo_box is not set or if no row is
6762 * selected then %NULL is returned.
6764 * Return value: the ID of the active row, or %NULL
6769 gtk_combo_box_get_active_id (GtkComboBox *combo_box)
6771 GtkTreeModel *model;
6775 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), 0);
6777 column = combo_box->priv->id_column;
6782 model = gtk_combo_box_get_model (combo_box);
6783 g_return_val_if_fail (gtk_tree_model_get_column_type (model, column) ==
6784 G_TYPE_STRING, NULL);
6786 if (gtk_combo_box_get_active_iter (combo_box, &iter))
6788 const gchar *interned;
6791 gtk_tree_model_get (model, &iter, column, &id, -1);
6792 interned = g_intern_string (id);
6802 * gtk_combo_box_set_active_id:
6803 * @combo_box: a #GtkComboBox
6804 * @active_id: the ID of the row to select
6806 * Changes the active row of @combo_box to the one that has an ID equal to @id.
6808 * If the 'id-column' property of @combo_box is unset or if no row has
6809 * the given ID then nothing happens.
6814 gtk_combo_box_set_active_id (GtkComboBox *combo_box,
6815 const gchar *active_id)
6817 GtkTreeModel *model;
6821 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
6823 column = combo_box->priv->id_column;
6828 model = gtk_combo_box_get_model (combo_box);
6829 g_return_if_fail (gtk_tree_model_get_column_type (model, column) ==
6832 if (gtk_tree_model_get_iter_first (model, &iter))
6837 gtk_tree_model_get (model, &iter, column, &id, -1);
6838 match = strcmp (id, active_id) == 0;
6843 gtk_combo_box_set_active_iter (combo_box, &iter);
6846 } while (gtk_tree_model_iter_next (model, &iter));