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;
148 guint popup_in_progress : 1;
149 guint popup_shown : 1;
150 guint add_tearoffs : 1;
152 guint is_cell_renderer : 1;
153 guint editing_canceled : 1;
154 guint auto_scroll : 1;
155 guint focus_on_click : 1;
156 guint button_sensitivity : 2;
158 guint popup_fixed_width : 1;
160 GtkTreeViewRowSeparatorFunc row_separator_func;
161 gpointer row_separator_data;
162 GDestroyNotify row_separator_destroy;
164 GdkDevice *grab_pointer;
165 GdkDevice *grab_keyboard;
167 gchar *tearoff_title;
170 /* While debugging this evil code, I have learned that
171 * there are actually 4 modes to this widget, which can
172 * be characterized as follows
174 * 1) menu mode, no child added
177 * cell_view -> GtkCellView, regular child
178 * cell_view_frame -> NULL
179 * button -> GtkToggleButton set_parent to combo
180 * arrow -> GtkArrow set_parent to button
181 * separator -> GtkVSepator set_parent to button
182 * popup_widget -> GtkMenu
183 * popup_window -> NULL
184 * scrolled_window -> NULL
186 * 2) menu mode, child added
190 * cell_view_frame -> NULL
191 * button -> GtkToggleButton set_parent to combo
192 * arrow -> GtkArrow, child of button
194 * popup_widget -> GtkMenu
195 * popup_window -> NULL
196 * scrolled_window -> NULL
198 * 3) list mode, no child added
200 * tree_view -> GtkTreeView, child of scrolled_window
201 * cell_view -> GtkCellView, regular child
202 * cell_view_frame -> GtkFrame, set parent to combo
203 * button -> GtkToggleButton, set_parent to combo
204 * arrow -> GtkArrow, child of button
206 * popup_widget -> tree_view
207 * popup_window -> GtkWindow
208 * scrolled_window -> GtkScrolledWindow, child of popup_window
210 * 4) list mode, child added
212 * tree_view -> GtkTreeView, child of scrolled_window
214 * cell_view_frame -> NULL
215 * button -> GtkToggleButton, set_parent to combo
216 * arrow -> GtkArrow, child of button
218 * popup_widget -> tree_view
219 * popup_window -> GtkWindow
220 * scrolled_window -> GtkScrolledWindow, child of popup_window
236 PROP_ROW_SPAN_COLUMN,
237 PROP_COLUMN_SPAN_COLUMN,
244 PROP_BUTTON_SENSITIVITY,
245 PROP_EDITING_CANCELED,
247 PROP_ENTRY_TEXT_COLUMN,
248 PROP_POPUP_FIXED_WIDTH
251 static guint combo_box_signals[LAST_SIGNAL] = {0,};
253 #define BONUS_PADDING 4
254 #define SCROLL_TIME 100
258 static void gtk_combo_box_cell_layout_init (GtkCellLayoutIface *iface);
259 static void gtk_combo_box_cell_editable_init (GtkCellEditableIface *iface);
260 static GObject *gtk_combo_box_constructor (GType type,
261 guint n_construct_properties,
262 GObjectConstructParam *construct_properties);
263 static void gtk_combo_box_dispose (GObject *object);
264 static void gtk_combo_box_finalize (GObject *object);
265 static void gtk_combo_box_destroy (GtkWidget *widget);
267 static void gtk_combo_box_set_property (GObject *object,
271 static void gtk_combo_box_get_property (GObject *object,
276 static void gtk_combo_box_state_changed (GtkWidget *widget,
277 GtkStateType previous);
278 static void gtk_combo_box_grab_focus (GtkWidget *widget);
279 static void gtk_combo_box_style_set (GtkWidget *widget,
281 static void gtk_combo_box_button_toggled (GtkWidget *widget,
283 static void gtk_combo_box_button_state_changed (GtkWidget *widget,
284 GtkStateType previous,
286 static void gtk_combo_box_add (GtkContainer *container,
288 static void gtk_combo_box_remove (GtkContainer *container,
291 static ComboCellInfo *gtk_combo_box_get_cell_info (GtkComboBox *combo_box,
292 GtkCellRenderer *cell);
294 static void gtk_combo_box_menu_show (GtkWidget *menu,
296 static void gtk_combo_box_menu_hide (GtkWidget *menu,
299 static void gtk_combo_box_set_popup_widget (GtkComboBox *combo_box,
301 static void gtk_combo_box_menu_position_below (GtkMenu *menu,
306 static void gtk_combo_box_menu_position_over (GtkMenu *menu,
311 static void gtk_combo_box_menu_position (GtkMenu *menu,
317 static void gtk_combo_box_update_requested_width(GtkComboBox *combo_box,
319 static void gtk_combo_box_remeasure (GtkComboBox *combo_box);
321 static void gtk_combo_box_unset_model (GtkComboBox *combo_box);
323 static void gtk_combo_box_size_allocate (GtkWidget *widget,
324 GtkAllocation *allocation);
325 static void gtk_combo_box_forall (GtkContainer *container,
326 gboolean include_internals,
327 GtkCallback callback,
328 gpointer callback_data);
329 static gboolean gtk_combo_box_draw (GtkWidget *widget,
331 static gboolean gtk_combo_box_scroll_event (GtkWidget *widget,
332 GdkEventScroll *event);
333 static void gtk_combo_box_set_active_internal (GtkComboBox *combo_box,
336 static void gtk_combo_box_check_appearance (GtkComboBox *combo_box);
337 static gchar * gtk_combo_box_real_get_active_text (GtkComboBox *combo_box);
338 static void gtk_combo_box_real_move_active (GtkComboBox *combo_box,
339 GtkScrollType scroll);
340 static void gtk_combo_box_real_popup (GtkComboBox *combo_box);
341 static gboolean gtk_combo_box_real_popdown (GtkComboBox *combo_box);
343 /* listening to the model */
344 static void gtk_combo_box_model_row_inserted (GtkTreeModel *model,
348 static void gtk_combo_box_model_row_deleted (GtkTreeModel *model,
351 static void gtk_combo_box_model_rows_reordered (GtkTreeModel *model,
356 static void gtk_combo_box_model_row_changed (GtkTreeModel *model,
360 static void gtk_combo_box_model_row_expanded (GtkTreeModel *model,
366 static void gtk_combo_box_list_position (GtkComboBox *combo_box,
371 static void gtk_combo_box_list_setup (GtkComboBox *combo_box);
372 static void gtk_combo_box_list_destroy (GtkComboBox *combo_box);
374 static gboolean gtk_combo_box_list_button_released (GtkWidget *widget,
375 GdkEventButton *event,
377 static gboolean gtk_combo_box_list_key_press (GtkWidget *widget,
380 static gboolean gtk_combo_box_list_enter_notify (GtkWidget *widget,
381 GdkEventCrossing *event,
383 static void gtk_combo_box_list_auto_scroll (GtkComboBox *combo,
386 static gboolean gtk_combo_box_list_scroll_timeout (GtkComboBox *combo);
387 static gboolean gtk_combo_box_list_button_pressed (GtkWidget *widget,
388 GdkEventButton *event,
391 static gboolean gtk_combo_box_list_select_func (GtkTreeSelection *selection,
394 gboolean path_currently_selected,
397 static void gtk_combo_box_list_row_changed (GtkTreeModel *model,
401 static void gtk_combo_box_list_popup_resize (GtkComboBox *combo_box);
404 static void gtk_combo_box_menu_setup (GtkComboBox *combo_box,
405 gboolean add_children);
406 static void gtk_combo_box_menu_fill (GtkComboBox *combo_box);
407 static void gtk_combo_box_menu_fill_level (GtkComboBox *combo_box,
410 static void gtk_combo_box_update_title (GtkComboBox *combo_box);
411 static void gtk_combo_box_menu_destroy (GtkComboBox *combo_box);
413 static void gtk_combo_box_relayout_item (GtkComboBox *combo_box,
417 static void gtk_combo_box_relayout (GtkComboBox *combo_box);
419 static gboolean gtk_combo_box_menu_button_press (GtkWidget *widget,
420 GdkEventButton *event,
422 static void gtk_combo_box_menu_item_activate (GtkWidget *item,
425 static void gtk_combo_box_update_sensitivity (GtkComboBox *combo_box);
426 static void gtk_combo_box_menu_row_inserted (GtkTreeModel *model,
430 static void gtk_combo_box_menu_row_deleted (GtkTreeModel *model,
433 static void gtk_combo_box_menu_rows_reordered (GtkTreeModel *model,
438 static void gtk_combo_box_menu_row_changed (GtkTreeModel *model,
442 static gboolean gtk_combo_box_menu_key_press (GtkWidget *widget,
445 static void gtk_combo_box_menu_popup (GtkComboBox *combo_box,
447 guint32 activate_time);
448 static GtkWidget *gtk_cell_view_menu_item_new (GtkComboBox *combo_box,
453 static void gtk_combo_box_cell_layout_pack_start (GtkCellLayout *layout,
454 GtkCellRenderer *cell,
456 static void gtk_combo_box_cell_layout_pack_end (GtkCellLayout *layout,
457 GtkCellRenderer *cell,
459 static GList *gtk_combo_box_cell_layout_get_cells (GtkCellLayout *layout);
460 static void gtk_combo_box_cell_layout_clear (GtkCellLayout *layout);
461 static void gtk_combo_box_cell_layout_add_attribute (GtkCellLayout *layout,
462 GtkCellRenderer *cell,
463 const gchar *attribute,
465 static void gtk_combo_box_cell_layout_set_cell_data_func (GtkCellLayout *layout,
466 GtkCellRenderer *cell,
467 GtkCellLayoutDataFunc func,
469 GDestroyNotify destroy);
470 static void gtk_combo_box_cell_layout_clear_attributes (GtkCellLayout *layout,
471 GtkCellRenderer *cell);
472 static void gtk_combo_box_cell_layout_reorder (GtkCellLayout *layout,
473 GtkCellRenderer *cell,
475 static gboolean gtk_combo_box_mnemonic_activate (GtkWidget *widget,
476 gboolean group_cycling);
478 static void gtk_combo_box_sync_cells (GtkComboBox *combo_box,
479 GtkCellLayout *cell_layout);
480 static void combo_cell_data_func (GtkCellLayout *cell_layout,
481 GtkCellRenderer *cell,
482 GtkTreeModel *tree_model,
485 static void gtk_combo_box_child_show (GtkWidget *widget,
486 GtkComboBox *combo_box);
487 static void gtk_combo_box_child_hide (GtkWidget *widget,
488 GtkComboBox *combo_box);
490 /* GtkComboBox:has-entry callbacks */
491 static void gtk_combo_box_entry_contents_changed (GtkEntry *entry,
493 static void gtk_combo_box_entry_active_changed (GtkComboBox *combo_box,
497 /* GtkBuildable method implementation */
498 static GtkBuildableIface *parent_buildable_iface;
500 static void gtk_combo_box_buildable_init (GtkBuildableIface *iface);
501 static gboolean gtk_combo_box_buildable_custom_tag_start (GtkBuildable *buildable,
504 const gchar *tagname,
505 GMarkupParser *parser,
507 static void gtk_combo_box_buildable_custom_tag_end (GtkBuildable *buildable,
510 const gchar *tagname,
512 static GObject *gtk_combo_box_buildable_get_internal_child (GtkBuildable *buildable,
514 const gchar *childname);
517 /* GtkCellEditable method implementations */
518 static void gtk_combo_box_start_editing (GtkCellEditable *cell_editable,
521 static void gtk_combo_box_get_preferred_width (GtkWidget *widget,
524 static void gtk_combo_box_get_preferred_height (GtkWidget *widget,
527 static void gtk_combo_box_get_preferred_width_for_height (GtkWidget *widget,
531 static void gtk_combo_box_get_preferred_height_for_width (GtkWidget *widget,
537 G_DEFINE_TYPE_WITH_CODE (GtkComboBox, gtk_combo_box, GTK_TYPE_BIN,
538 G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
539 gtk_combo_box_cell_layout_init)
540 G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_EDITABLE,
541 gtk_combo_box_cell_editable_init)
542 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
543 gtk_combo_box_buildable_init))
548 gtk_combo_box_class_init (GtkComboBoxClass *klass)
550 GObjectClass *object_class;
551 GtkContainerClass *container_class;
552 GtkWidgetClass *widget_class;
553 GtkBindingSet *binding_set;
555 klass->get_active_text = gtk_combo_box_real_get_active_text;
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_set = gtk_combo_box_style_set;
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 a GtkComboBoxEntry,
593 * as well as when selecting an item from the GtkComboBoxEntry's list.
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:popup-fixed-width:
957 * Whether the popup's width should be a fixed width matching the
958 * allocated width of the combo box.
962 g_object_class_install_property (object_class,
963 PROP_POPUP_FIXED_WIDTH,
964 g_param_spec_boolean ("popup-fixed-width",
965 P_("Popup Fixed Width"),
966 P_("Whether the popup's width should be a "
967 "fixed width matching the allocated width "
970 GTK_PARAM_READWRITE));
972 gtk_widget_class_install_style_property (widget_class,
973 g_param_spec_boolean ("appears-as-list",
974 P_("Appears as list"),
975 P_("Whether dropdowns should look like lists rather than menus"),
977 GTK_PARAM_READABLE));
980 * GtkComboBox:arrow-size:
982 * Sets the minimum size of the arrow in the combo box. Note
983 * that the arrow size is coupled to the font size, so in case
984 * a larger font is used, the arrow will be larger than set
989 gtk_widget_class_install_style_property (widget_class,
990 g_param_spec_int ("arrow-size",
992 P_("The minimum size of the arrow in the combo box"),
996 GTK_PARAM_READABLE));
999 * GtkComboBox:shadow-type:
1001 * Which kind of shadow to draw around the combo box.
1005 gtk_widget_class_install_style_property (widget_class,
1006 g_param_spec_enum ("shadow-type",
1008 P_("Which kind of shadow to draw around the combo box"),
1009 GTK_TYPE_SHADOW_TYPE,
1011 GTK_PARAM_READABLE));
1013 g_type_class_add_private (object_class, sizeof (GtkComboBoxPrivate));
1017 gtk_combo_box_buildable_init (GtkBuildableIface *iface)
1019 parent_buildable_iface = g_type_interface_peek_parent (iface);
1020 iface->add_child = _gtk_cell_layout_buildable_add_child;
1021 iface->custom_tag_start = gtk_combo_box_buildable_custom_tag_start;
1022 iface->custom_tag_end = gtk_combo_box_buildable_custom_tag_end;
1023 iface->get_internal_child = gtk_combo_box_buildable_get_internal_child;
1027 gtk_combo_box_cell_layout_init (GtkCellLayoutIface *iface)
1029 iface->pack_start = gtk_combo_box_cell_layout_pack_start;
1030 iface->pack_end = gtk_combo_box_cell_layout_pack_end;
1031 iface->get_cells = gtk_combo_box_cell_layout_get_cells;
1032 iface->clear = gtk_combo_box_cell_layout_clear;
1033 iface->add_attribute = gtk_combo_box_cell_layout_add_attribute;
1034 iface->set_cell_data_func = gtk_combo_box_cell_layout_set_cell_data_func;
1035 iface->clear_attributes = gtk_combo_box_cell_layout_clear_attributes;
1036 iface->reorder = gtk_combo_box_cell_layout_reorder;
1040 gtk_combo_box_cell_editable_init (GtkCellEditableIface *iface)
1042 iface->start_editing = gtk_combo_box_start_editing;
1046 gtk_combo_box_init (GtkComboBox *combo_box)
1048 GtkComboBoxPrivate *priv;
1050 combo_box->priv = G_TYPE_INSTANCE_GET_PRIVATE (combo_box,
1052 GtkComboBoxPrivate);
1053 priv = combo_box->priv;
1055 priv->cell_view = gtk_cell_view_new ();
1056 gtk_widget_set_parent (priv->cell_view, GTK_WIDGET (combo_box));
1057 _gtk_bin_set_child (GTK_BIN (combo_box), priv->cell_view);
1058 gtk_widget_show (priv->cell_view);
1060 priv->minimum_width = 0;
1061 priv->natural_width = 0;
1063 priv->wrap_width = 0;
1066 priv->active_row = NULL;
1067 priv->col_column = -1;
1068 priv->row_column = -1;
1070 priv->popup_shown = FALSE;
1071 priv->add_tearoffs = FALSE;
1072 priv->has_frame = TRUE;
1073 priv->is_cell_renderer = FALSE;
1074 priv->editing_canceled = FALSE;
1075 priv->auto_scroll = FALSE;
1076 priv->focus_on_click = TRUE;
1077 priv->button_sensitivity = GTK_SENSITIVITY_AUTO;
1078 priv->has_entry = FALSE;
1079 priv->popup_fixed_width = TRUE;
1081 priv->text_column = -1;
1082 priv->text_renderer = NULL;
1084 gtk_combo_box_check_appearance (combo_box);
1088 gtk_combo_box_set_property (GObject *object,
1090 const GValue *value,
1093 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
1098 gtk_combo_box_set_model (combo_box, g_value_get_object (value));
1101 case PROP_WRAP_WIDTH:
1102 gtk_combo_box_set_wrap_width (combo_box, g_value_get_int (value));
1105 case PROP_ROW_SPAN_COLUMN:
1106 gtk_combo_box_set_row_span_column (combo_box, g_value_get_int (value));
1109 case PROP_COLUMN_SPAN_COLUMN:
1110 gtk_combo_box_set_column_span_column (combo_box, g_value_get_int (value));
1114 gtk_combo_box_set_active (combo_box, g_value_get_int (value));
1117 case PROP_ADD_TEAROFFS:
1118 gtk_combo_box_set_add_tearoffs (combo_box, g_value_get_boolean (value));
1121 case PROP_HAS_FRAME:
1122 combo_box->priv->has_frame = g_value_get_boolean (value);
1124 if (combo_box->priv->has_entry)
1128 child = gtk_bin_get_child (GTK_BIN (combo_box));
1130 gtk_entry_set_has_frame (GTK_ENTRY (child),
1131 combo_box->priv->has_frame);
1136 case PROP_FOCUS_ON_CLICK:
1137 gtk_combo_box_set_focus_on_click (combo_box,
1138 g_value_get_boolean (value));
1141 case PROP_TEAROFF_TITLE:
1142 gtk_combo_box_set_title (combo_box, g_value_get_string (value));
1145 case PROP_POPUP_SHOWN:
1146 if (g_value_get_boolean (value))
1147 gtk_combo_box_popup (combo_box);
1149 gtk_combo_box_popdown (combo_box);
1152 case PROP_BUTTON_SENSITIVITY:
1153 gtk_combo_box_set_button_sensitivity (combo_box,
1154 g_value_get_enum (value));
1157 case PROP_POPUP_FIXED_WIDTH:
1158 gtk_combo_box_set_popup_fixed_width (combo_box,
1159 g_value_get_boolean (value));
1162 case PROP_EDITING_CANCELED:
1163 combo_box->priv->editing_canceled = g_value_get_boolean (value);
1166 case PROP_HAS_ENTRY:
1167 combo_box->priv->has_entry = g_value_get_boolean (value);
1170 case PROP_ENTRY_TEXT_COLUMN:
1171 gtk_combo_box_set_entry_text_column (combo_box, g_value_get_int (value));
1175 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1181 gtk_combo_box_get_property (GObject *object,
1186 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
1187 GtkComboBoxPrivate *priv = combo_box->priv;
1192 g_value_set_object (value, combo_box->priv->model);
1195 case PROP_WRAP_WIDTH:
1196 g_value_set_int (value, combo_box->priv->wrap_width);
1199 case PROP_ROW_SPAN_COLUMN:
1200 g_value_set_int (value, combo_box->priv->row_column);
1203 case PROP_COLUMN_SPAN_COLUMN:
1204 g_value_set_int (value, combo_box->priv->col_column);
1208 g_value_set_int (value, gtk_combo_box_get_active (combo_box));
1211 case PROP_ADD_TEAROFFS:
1212 g_value_set_boolean (value, gtk_combo_box_get_add_tearoffs (combo_box));
1215 case PROP_HAS_FRAME:
1216 g_value_set_boolean (value, combo_box->priv->has_frame);
1219 case PROP_FOCUS_ON_CLICK:
1220 g_value_set_boolean (value, combo_box->priv->focus_on_click);
1223 case PROP_TEAROFF_TITLE:
1224 g_value_set_string (value, gtk_combo_box_get_title (combo_box));
1227 case PROP_POPUP_SHOWN:
1228 g_value_set_boolean (value, combo_box->priv->popup_shown);
1231 case PROP_BUTTON_SENSITIVITY:
1232 g_value_set_enum (value, combo_box->priv->button_sensitivity);
1235 case PROP_POPUP_FIXED_WIDTH:
1236 g_value_set_boolean (value, combo_box->priv->popup_fixed_width);
1239 case PROP_EDITING_CANCELED:
1240 g_value_set_boolean (value, priv->editing_canceled);
1243 case PROP_HAS_ENTRY:
1244 g_value_set_boolean (value, priv->has_entry);
1247 case PROP_ENTRY_TEXT_COLUMN:
1248 g_value_set_int (value, priv->text_column);
1252 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1258 gtk_combo_box_state_changed (GtkWidget *widget,
1259 GtkStateType previous)
1261 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1262 GtkComboBoxPrivate *priv = combo_box->priv;
1264 if (gtk_widget_get_realized (widget))
1266 if (priv->tree_view && priv->cell_view)
1267 gtk_cell_view_set_background_color (GTK_CELL_VIEW (priv->cell_view),
1268 >k_widget_get_style (widget)->base[gtk_widget_get_state (widget)]);
1271 gtk_widget_queue_draw (widget);
1275 gtk_combo_box_button_state_changed (GtkWidget *widget,
1276 GtkStateType previous,
1279 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
1280 GtkComboBoxPrivate *priv = combo_box->priv;
1282 if (gtk_widget_get_realized (widget))
1284 if (!priv->tree_view && priv->cell_view)
1286 if ((gtk_widget_get_state (widget) == GTK_STATE_INSENSITIVE) !=
1287 (gtk_widget_get_state (priv->cell_view) == GTK_STATE_INSENSITIVE))
1288 gtk_widget_set_sensitive (priv->cell_view, gtk_widget_get_sensitive (widget));
1290 gtk_widget_set_state (priv->cell_view,
1291 gtk_widget_get_state (widget));
1295 gtk_widget_queue_draw (widget);
1299 gtk_combo_box_check_appearance (GtkComboBox *combo_box)
1301 GtkComboBoxPrivate *priv = combo_box->priv;
1302 gboolean appears_as_list;
1304 /* if wrap_width > 0, then we are in grid-mode and forced to use
1307 if (priv->wrap_width)
1308 appears_as_list = FALSE;
1310 gtk_widget_style_get (GTK_WIDGET (combo_box),
1311 "appears-as-list", &appears_as_list,
1314 if (appears_as_list)
1316 /* Destroy all the menu mode widgets, if they exist. */
1317 if (GTK_IS_MENU (priv->popup_widget))
1318 gtk_combo_box_menu_destroy (combo_box);
1320 /* Create the list mode widgets, if they don't already exist. */
1321 if (!GTK_IS_TREE_VIEW (priv->tree_view))
1322 gtk_combo_box_list_setup (combo_box);
1326 /* Destroy all the list mode widgets, if they exist. */
1327 if (GTK_IS_TREE_VIEW (priv->tree_view))
1328 gtk_combo_box_list_destroy (combo_box);
1330 /* Create the menu mode widgets, if they don't already exist. */
1331 if (!GTK_IS_MENU (priv->popup_widget))
1332 gtk_combo_box_menu_setup (combo_box, TRUE);
1335 gtk_widget_style_get (GTK_WIDGET (combo_box),
1336 "shadow-type", &priv->shadow_type,
1341 gtk_combo_box_style_set (GtkWidget *widget,
1344 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1345 GtkComboBoxPrivate *priv = combo_box->priv;
1348 gtk_combo_box_check_appearance (combo_box);
1350 if (priv->tree_view && priv->cell_view)
1351 gtk_cell_view_set_background_color (GTK_CELL_VIEW (priv->cell_view),
1352 >k_widget_get_style (widget)->base[gtk_widget_get_state (widget)]);
1354 child = gtk_bin_get_child (GTK_BIN (combo_box));
1355 if (GTK_IS_ENTRY (child))
1356 g_object_set (child, "shadow-type",
1357 GTK_SHADOW_NONE == priv->shadow_type ?
1358 GTK_SHADOW_IN : GTK_SHADOW_NONE, NULL);
1362 gtk_combo_box_button_toggled (GtkWidget *widget,
1365 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
1367 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
1369 if (!combo_box->priv->popup_in_progress)
1370 gtk_combo_box_popup (combo_box);
1373 gtk_combo_box_popdown (combo_box);
1377 gtk_combo_box_add (GtkContainer *container,
1380 GtkComboBox *combo_box = GTK_COMBO_BOX (container);
1381 GtkComboBoxPrivate *priv = combo_box->priv;
1383 if (priv->has_entry && !GTK_IS_ENTRY (widget))
1385 g_warning ("Attempting to add a widget with type %s to a GtkComboBox that needs an entry "
1386 "(need an instance of GtkEntry or of a subclass)",
1387 G_OBJECT_TYPE_NAME (widget));
1391 if (priv->cell_view &&
1392 gtk_widget_get_parent (priv->cell_view))
1394 gtk_widget_unparent (priv->cell_view);
1395 _gtk_bin_set_child (GTK_BIN (container), NULL);
1396 gtk_widget_queue_resize (GTK_WIDGET (container));
1399 gtk_widget_set_parent (widget, GTK_WIDGET (container));
1400 _gtk_bin_set_child (GTK_BIN (container), widget);
1402 if (priv->cell_view &&
1403 widget != priv->cell_view)
1405 /* since the cell_view was unparented, it's gone now */
1406 priv->cell_view = NULL;
1408 if (!priv->tree_view && priv->separator)
1410 gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (priv->separator)),
1412 priv->separator = NULL;
1414 gtk_widget_queue_resize (GTK_WIDGET (container));
1416 else if (priv->cell_view_frame)
1418 gtk_widget_unparent (priv->cell_view_frame);
1419 priv->cell_view_frame = NULL;
1424 if (priv->has_entry)
1426 /* this flag is a hack to tell the entry to fill its allocation.
1428 GTK_ENTRY (widget)->is_cell_renderer = TRUE;
1430 g_signal_connect (widget, "changed",
1431 G_CALLBACK (gtk_combo_box_entry_contents_changed),
1434 gtk_entry_set_has_frame (GTK_ENTRY (widget), priv->has_frame);
1439 gtk_combo_box_remove (GtkContainer *container,
1442 GtkComboBox *combo_box = GTK_COMBO_BOX (container);
1443 GtkComboBoxPrivate *priv = combo_box->priv;
1445 gboolean appears_as_list;
1447 if (priv->has_entry)
1449 GtkWidget *child_widget;
1451 child_widget = gtk_bin_get_child (GTK_BIN (container));
1452 if (widget && widget == child_widget)
1454 g_signal_handlers_disconnect_by_func (widget,
1455 gtk_combo_box_entry_contents_changed,
1457 GTK_ENTRY (widget)->is_cell_renderer = FALSE;
1461 if (widget == priv->cell_view)
1462 priv->cell_view = NULL;
1464 gtk_widget_unparent (widget);
1465 _gtk_bin_set_child (GTK_BIN (container), NULL);
1467 if (gtk_widget_in_destruction (GTK_WIDGET (combo_box)))
1470 gtk_widget_queue_resize (GTK_WIDGET (container));
1472 if (!priv->tree_view)
1473 appears_as_list = FALSE;
1475 appears_as_list = TRUE;
1477 if (appears_as_list)
1478 gtk_combo_box_list_destroy (combo_box);
1479 else if (GTK_IS_MENU (priv->popup_widget))
1481 gtk_combo_box_menu_destroy (combo_box);
1482 gtk_menu_detach (GTK_MENU (priv->popup_widget));
1483 priv->popup_widget = NULL;
1486 if (!priv->cell_view)
1488 priv->cell_view = gtk_cell_view_new ();
1489 gtk_widget_set_parent (priv->cell_view, GTK_WIDGET (container));
1490 _gtk_bin_set_child (GTK_BIN (container), priv->cell_view);
1492 gtk_widget_show (priv->cell_view);
1493 gtk_cell_view_set_model (GTK_CELL_VIEW (priv->cell_view),
1495 gtk_combo_box_sync_cells (combo_box, GTK_CELL_LAYOUT (priv->cell_view));
1499 if (appears_as_list)
1500 gtk_combo_box_list_setup (combo_box);
1502 gtk_combo_box_menu_setup (combo_box, TRUE);
1504 if (gtk_tree_row_reference_valid (priv->active_row))
1506 path = gtk_tree_row_reference_get_path (priv->active_row);
1507 gtk_combo_box_set_active_internal (combo_box, path);
1508 gtk_tree_path_free (path);
1511 gtk_combo_box_set_active_internal (combo_box, NULL);
1514 static ComboCellInfo *
1515 gtk_combo_box_get_cell_info (GtkComboBox *combo_box,
1516 GtkCellRenderer *cell)
1520 for (i = combo_box->priv->cells; i; i = i->next)
1522 ComboCellInfo *info = (ComboCellInfo *)i->data;
1524 if (info && info->cell == cell)
1532 gtk_combo_box_menu_show (GtkWidget *menu,
1535 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1536 GtkComboBoxPrivate *priv = combo_box->priv;
1538 gtk_combo_box_child_show (menu, user_data);
1540 priv->popup_in_progress = TRUE;
1541 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button),
1543 priv->popup_in_progress = FALSE;
1547 gtk_combo_box_menu_hide (GtkWidget *menu,
1550 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1552 gtk_combo_box_child_hide (menu,user_data);
1554 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
1559 gtk_combo_box_detacher (GtkWidget *widget,
1562 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1563 GtkComboBoxPrivate *priv = combo_box->priv;
1565 g_return_if_fail (priv->popup_widget == (GtkWidget *) menu);
1567 g_signal_handlers_disconnect_by_func (menu->toplevel,
1568 gtk_combo_box_menu_show,
1570 g_signal_handlers_disconnect_by_func (menu->toplevel,
1571 gtk_combo_box_menu_hide,
1574 priv->popup_widget = NULL;
1578 gtk_combo_box_set_popup_widget (GtkComboBox *combo_box,
1581 GtkComboBoxPrivate *priv = combo_box->priv;
1583 if (GTK_IS_MENU (priv->popup_widget))
1585 gtk_menu_detach (GTK_MENU (priv->popup_widget));
1586 priv->popup_widget = NULL;
1588 else if (priv->popup_widget)
1590 gtk_container_remove (GTK_CONTAINER (priv->scrolled_window),
1591 priv->popup_widget);
1592 g_object_unref (priv->popup_widget);
1593 priv->popup_widget = NULL;
1596 if (GTK_IS_MENU (popup))
1598 if (priv->popup_window)
1600 gtk_widget_destroy (priv->popup_window);
1601 priv->popup_window = NULL;
1604 priv->popup_widget = popup;
1607 * Note that we connect to show/hide on the toplevel, not the
1608 * menu itself, since the menu is not shown/hidden when it is
1609 * popped up while torn-off.
1611 g_signal_connect (GTK_MENU (popup)->toplevel, "show",
1612 G_CALLBACK (gtk_combo_box_menu_show), combo_box);
1613 g_signal_connect (GTK_MENU (popup)->toplevel, "hide",
1614 G_CALLBACK (gtk_combo_box_menu_hide), combo_box);
1616 gtk_menu_attach_to_widget (GTK_MENU (popup),
1617 GTK_WIDGET (combo_box),
1618 gtk_combo_box_detacher);
1622 if (!priv->popup_window)
1624 GtkWidget *toplevel;
1626 priv->popup_window = gtk_window_new (GTK_WINDOW_POPUP);
1627 gtk_widget_set_name (priv->popup_window, "gtk-combobox-popup-window");
1629 gtk_window_set_type_hint (GTK_WINDOW (priv->popup_window),
1630 GDK_WINDOW_TYPE_HINT_COMBO);
1632 g_signal_connect (GTK_WINDOW (priv->popup_window),"show",
1633 G_CALLBACK (gtk_combo_box_child_show),
1635 g_signal_connect (GTK_WINDOW (priv->popup_window),"hide",
1636 G_CALLBACK (gtk_combo_box_child_hide),
1639 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (combo_box));
1640 if (GTK_IS_WINDOW (toplevel))
1642 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
1643 GTK_WINDOW (priv->popup_window));
1644 gtk_window_set_transient_for (GTK_WINDOW (priv->popup_window),
1645 GTK_WINDOW (toplevel));
1648 gtk_window_set_resizable (GTK_WINDOW (priv->popup_window), FALSE);
1649 gtk_window_set_screen (GTK_WINDOW (priv->popup_window),
1650 gtk_widget_get_screen (GTK_WIDGET (combo_box)));
1652 priv->scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1654 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
1657 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->scrolled_window),
1660 gtk_widget_show (priv->scrolled_window);
1662 gtk_container_add (GTK_CONTAINER (priv->popup_window),
1663 priv->scrolled_window);
1666 gtk_container_add (GTK_CONTAINER (priv->scrolled_window),
1669 gtk_widget_show (popup);
1670 g_object_ref (popup);
1671 priv->popup_widget = popup;
1676 gtk_combo_box_menu_position_below (GtkMenu *menu,
1682 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1683 GtkAllocation child_allocation;
1689 GdkRectangle monitor;
1691 /* FIXME: is using the size request here broken? */
1692 child = gtk_bin_get_child (GTK_BIN (combo_box));
1696 gtk_widget_get_allocation (child, &child_allocation);
1698 if (!gtk_widget_get_has_window (child))
1700 sx += child_allocation.x;
1701 sy += child_allocation.y;
1704 gdk_window_get_root_coords (gtk_widget_get_window (child),
1707 if (GTK_SHADOW_NONE != combo_box->priv->shadow_type)
1708 sx -= gtk_widget_get_style (GTK_WIDGET (combo_box))->xthickness;
1710 if (combo_box->priv->popup_fixed_width)
1711 gtk_widget_get_preferred_size (GTK_WIDGET (menu), &req, NULL);
1713 gtk_widget_get_preferred_size (GTK_WIDGET (menu), NULL, &req);
1715 if (gtk_widget_get_direction (GTK_WIDGET (combo_box)) == GTK_TEXT_DIR_LTR)
1718 *x = sx + child_allocation.width - req.width;
1721 screen = gtk_widget_get_screen (GTK_WIDGET (combo_box));
1722 monitor_num = gdk_screen_get_monitor_at_window (screen,
1723 gtk_widget_get_window (GTK_WIDGET (combo_box)));
1724 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1728 else if (*x + req.width > monitor.x + monitor.width)
1729 *x = monitor.x + monitor.width - req.width;
1731 if (monitor.y + monitor.height - *y - child_allocation.height >= req.height)
1732 *y += child_allocation.height;
1733 else if (*y - monitor.y >= req.height)
1735 else if (monitor.y + monitor.height - *y - child_allocation.height > *y - monitor.y)
1736 *y += child_allocation.height;
1744 gtk_combo_box_menu_position_over (GtkMenu *menu,
1750 GtkComboBox *combo_box;
1754 GtkAllocation allocation;
1755 GtkAllocation child_allocation;
1762 combo_box = GTK_COMBO_BOX (user_data);
1763 widget = GTK_WIDGET (combo_box);
1765 active = gtk_menu_get_active (GTK_MENU (combo_box->priv->popup_widget));
1767 gtk_widget_get_allocation (widget, &allocation);
1769 menu_xpos = allocation.x;
1770 menu_ypos = allocation.y + allocation.height / 2 - 2;
1772 if (combo_box->priv->popup_fixed_width)
1773 gtk_widget_get_preferred_width (GTK_WIDGET (menu), &menu_width, NULL);
1775 gtk_widget_get_preferred_width (GTK_WIDGET (menu), NULL, &menu_width);
1779 gtk_widget_get_allocation (active, &child_allocation);
1780 menu_ypos -= child_allocation.height / 2;
1783 children = GTK_MENU_SHELL (combo_box->priv->popup_widget)->children;
1786 child = children->data;
1788 if (active == child)
1791 if (gtk_widget_get_visible (child))
1793 gtk_widget_get_allocation (child, &child_allocation);
1795 menu_ypos -= child_allocation.height;
1798 children = children->next;
1801 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
1802 menu_xpos = menu_xpos + allocation.width - menu_width;
1804 gdk_window_get_root_coords (gtk_widget_get_window (widget),
1805 menu_xpos, menu_ypos,
1806 &menu_xpos, &menu_ypos);
1808 /* Clamp the position on screen */
1809 screen_width = gdk_screen_get_width (gtk_widget_get_screen (widget));
1813 else if ((menu_xpos + menu_width) > screen_width)
1814 menu_xpos -= ((menu_xpos + menu_width) - screen_width);
1823 gtk_combo_box_menu_position (GtkMenu *menu,
1829 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1830 GtkComboBoxPrivate *priv = combo_box->priv;
1831 GtkWidget *menu_item;
1833 if (priv->wrap_width > 0 || priv->cell_view == NULL)
1834 gtk_combo_box_menu_position_below (menu, x, y, push_in, user_data);
1837 /* FIXME handle nested menus better */
1838 menu_item = gtk_menu_get_active (GTK_MENU (priv->popup_widget));
1840 gtk_menu_shell_select_item (GTK_MENU_SHELL (priv->popup_widget),
1843 gtk_combo_box_menu_position_over (menu, x, y, push_in, user_data);
1846 if (!gtk_widget_get_visible (GTK_MENU (priv->popup_widget)->toplevel))
1847 gtk_window_set_type_hint (GTK_WINDOW (GTK_MENU (priv->popup_widget)->toplevel),
1848 GDK_WINDOW_TYPE_HINT_COMBO);
1852 gtk_combo_box_list_position (GtkComboBox *combo_box,
1858 GtkComboBoxPrivate *priv = combo_box->priv;
1859 GtkAllocation allocation;
1862 GdkRectangle monitor;
1863 GtkRequisition popup_req;
1864 GtkPolicyType hpolicy, vpolicy;
1867 /* under windows, the drop down list is as wide as the combo box itself.
1869 GtkWidget *widget = GTK_WIDGET (combo_box);
1873 gtk_widget_get_allocation (widget, &allocation);
1875 if (!gtk_widget_get_has_window (widget))
1881 window = gtk_widget_get_window (widget);
1883 gdk_window_get_root_coords (gtk_widget_get_window (widget),
1886 *width = allocation.width;
1888 hpolicy = vpolicy = GTK_POLICY_NEVER;
1889 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
1892 /* XXX This set_size_request call is part of the hack outlined below and can
1893 * go away once height-for-width is implemented on treeviews. */
1894 gtk_widget_set_size_request (priv->tree_view, -1, -1);
1896 if (combo_box->priv->popup_fixed_width)
1898 gtk_widget_get_preferred_size (priv->scrolled_window, &popup_req, NULL);
1900 if (popup_req.width > *width)
1902 hpolicy = GTK_POLICY_ALWAYS;
1903 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
1909 gtk_combo_box_remeasure (combo_box);
1911 if (priv->natural_width > *width)
1913 hpolicy = GTK_POLICY_NEVER;
1914 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
1918 /* XXX Currently we set the size-request on the internal treeview to be
1919 * the natural width of the cells, this hack can go away once our
1920 * treeview does height-for-width properly (i.e. just adjust *width
1921 * here to be the natural width request of the scrolled-window).
1923 * I can't tell why the magic number 5 is needed here (i.e. without it
1924 * treeviews are left ellipsizing) , however it this all should be
1925 * removed with height-for-width treeviews.
1927 gtk_widget_set_size_request (priv->tree_view, priv->natural_width + 5, -1);
1928 gtk_widget_get_preferred_size (priv->scrolled_window, NULL, &popup_req);
1930 *width = popup_req.width;
1934 *height = popup_req.height;
1936 screen = gtk_widget_get_screen (GTK_WIDGET (combo_box));
1937 monitor_num = gdk_screen_get_monitor_at_window (screen, window);
1938 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1940 if (gtk_widget_get_direction (GTK_WIDGET (combo_box)) == GTK_TEXT_DIR_RTL)
1941 *x = *x + allocation.width - *width;
1945 else if (*x + *width > monitor.x + monitor.width)
1946 *x = monitor.x + monitor.width - *width;
1948 if (*y + allocation.height + *height <= monitor.y + monitor.height)
1949 *y += allocation.height;
1950 else if (*y - *height >= monitor.y)
1952 else if (monitor.y + monitor.height - (*y + allocation.height) > *y - monitor.y)
1954 *y += allocation.height;
1955 *height = monitor.y + monitor.height - *y;
1959 *height = *y - monitor.y;
1963 if (popup_req.height > *height)
1965 vpolicy = GTK_POLICY_ALWAYS;
1967 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
1973 cell_view_is_sensitive (GtkCellView *cell_view)
1975 GList *cells, *list;
1978 cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (cell_view));
1981 for (list = cells; list; list = list->next)
1983 g_object_get (list->data, "sensitive", &sensitive, NULL);
1988 g_list_free (cells);
1994 tree_column_row_is_sensitive (GtkComboBox *combo_box,
1997 GtkComboBoxPrivate *priv = combo_box->priv;
1998 GList *cells, *list;
2004 if (priv->row_separator_func)
2006 if (priv->row_separator_func (priv->model, iter,
2007 priv->row_separator_data))
2011 gtk_tree_view_column_cell_set_cell_data (priv->column,
2013 iter, FALSE, FALSE);
2015 cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (priv->column));
2018 for (list = cells; list; list = list->next)
2020 g_object_get (list->data, "sensitive", &sensitive, NULL);
2025 g_list_free (cells);
2031 update_menu_sensitivity (GtkComboBox *combo_box,
2034 GtkComboBoxPrivate *priv = combo_box->priv;
2035 GList *children, *child;
2036 GtkWidget *item, *submenu, *separator;
2037 GtkWidget *cell_view;
2043 children = gtk_container_get_children (GTK_CONTAINER (menu));
2045 for (child = children; child; child = child->next)
2047 item = GTK_WIDGET (child->data);
2048 cell_view = gtk_bin_get_child (GTK_BIN (item));
2050 if (!GTK_IS_CELL_VIEW (cell_view))
2053 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (item));
2054 if (submenu != NULL)
2056 gtk_widget_set_sensitive (item, TRUE);
2057 update_menu_sensitivity (combo_box, submenu);
2061 sensitive = cell_view_is_sensitive (GTK_CELL_VIEW (cell_view));
2063 if (menu != priv->popup_widget && child == children)
2065 separator = GTK_WIDGET (child->next->data);
2066 g_object_set (item, "visible", sensitive, NULL);
2067 g_object_set (separator, "visible", sensitive, NULL);
2070 gtk_widget_set_sensitive (item, sensitive);
2074 g_list_free (children);
2078 gtk_combo_box_menu_popup (GtkComboBox *combo_box,
2080 guint32 activate_time)
2082 GtkComboBoxPrivate *priv = combo_box->priv;
2085 gint width, min_width, nat_width;
2087 update_menu_sensitivity (combo_box, priv->popup_widget);
2090 if (gtk_tree_row_reference_valid (priv->active_row))
2092 path = gtk_tree_row_reference_get_path (priv->active_row);
2093 active_item = gtk_tree_path_get_indices (path)[0];
2094 gtk_tree_path_free (path);
2096 if (priv->add_tearoffs)
2100 /* FIXME handle nested menus better */
2101 gtk_menu_set_active (GTK_MENU (priv->popup_widget), active_item);
2103 if (priv->wrap_width == 0)
2105 GtkAllocation allocation;
2107 gtk_widget_get_allocation (GTK_WIDGET (combo_box), &allocation);
2108 width = allocation.width;
2109 gtk_widget_set_size_request (priv->popup_widget, -1, -1);
2110 gtk_widget_get_preferred_width (priv->popup_widget, &min_width, &nat_width);
2112 if (combo_box->priv->popup_fixed_width)
2113 width = MAX (width, min_width);
2115 width = MAX (width, nat_width);
2117 gtk_widget_set_size_request (priv->popup_widget, width, -1);
2120 gtk_menu_popup (GTK_MENU (priv->popup_widget),
2122 gtk_combo_box_menu_position, combo_box,
2123 button, activate_time);
2127 popup_grab_on_window (GdkWindow *window,
2128 GdkDevice *keyboard,
2130 guint32 activate_time)
2133 gdk_device_grab (keyboard, window,
2134 GDK_OWNERSHIP_WINDOW, TRUE,
2135 GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
2136 NULL, activate_time) != GDK_GRAB_SUCCESS)
2140 gdk_device_grab (pointer, window,
2141 GDK_OWNERSHIP_WINDOW, TRUE,
2142 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
2143 GDK_POINTER_MOTION_MASK,
2144 NULL, activate_time) != GDK_GRAB_SUCCESS)
2147 gdk_device_ungrab (keyboard, activate_time);
2156 * gtk_combo_box_popup:
2157 * @combo_box: a #GtkComboBox
2159 * Pops up the menu or dropdown list of @combo_box.
2161 * This function is mostly intended for use by accessibility technologies;
2162 * applications should have little use for it.
2167 gtk_combo_box_popup (GtkComboBox *combo_box)
2169 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2171 g_signal_emit (combo_box, combo_box_signals[POPUP], 0);
2175 * gtk_combo_box_popup_for_device:
2176 * @combo_box: a #GtkComboBox
2177 * @device: a #GdkDevice
2179 * Pops up the menu or dropdown list of @combo_box, the popup window
2180 * will be grabbed so only @device and its associated pointer/keyboard
2181 * are the only #GdkDevice<!-- -->s able to send events to it.
2186 gtk_combo_box_popup_for_device (GtkComboBox *combo_box,
2189 GtkComboBoxPrivate *priv = combo_box->priv;
2190 gint x, y, width, height;
2191 GtkTreePath *path = NULL, *ppath;
2192 GtkWidget *toplevel;
2193 GdkDevice *keyboard, *pointer;
2196 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2197 g_return_if_fail (GDK_IS_DEVICE (device));
2199 if (!gtk_widget_get_realized (GTK_WIDGET (combo_box)))
2202 if (gtk_widget_get_mapped (priv->popup_widget))
2205 if (priv->grab_pointer && priv->grab_keyboard)
2208 time = gtk_get_current_event_time ();
2210 if (device->source == GDK_SOURCE_KEYBOARD)
2213 pointer = gdk_device_get_associated_device (device);
2218 keyboard = gdk_device_get_associated_device (device);
2221 if (GTK_IS_MENU (priv->popup_widget))
2223 gtk_combo_box_menu_popup (combo_box,
2224 priv->activate_button,
2225 priv->activate_time);
2229 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (combo_box));
2230 if (GTK_IS_WINDOW (toplevel))
2231 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
2232 GTK_WINDOW (priv->popup_window));
2234 gtk_widget_show_all (priv->scrolled_window);
2235 gtk_combo_box_list_position (combo_box, &x, &y, &width, &height);
2237 gtk_widget_set_size_request (priv->popup_window, width, height);
2238 gtk_window_move (GTK_WINDOW (priv->popup_window), x, y);
2240 if (gtk_tree_row_reference_valid (priv->active_row))
2242 path = gtk_tree_row_reference_get_path (priv->active_row);
2243 ppath = gtk_tree_path_copy (path);
2244 if (gtk_tree_path_up (ppath))
2245 gtk_tree_view_expand_to_path (GTK_TREE_VIEW (priv->tree_view),
2247 gtk_tree_path_free (ppath);
2249 gtk_tree_view_set_hover_expand (GTK_TREE_VIEW (priv->tree_view),
2253 gtk_widget_show (priv->popup_window);
2257 gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->tree_view),
2259 gtk_tree_path_free (path);
2262 gtk_widget_grab_focus (priv->popup_window);
2263 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button),
2266 if (!gtk_widget_has_focus (priv->tree_view))
2267 gtk_widget_grab_focus (priv->tree_view);
2269 if (!popup_grab_on_window (gtk_widget_get_window (priv->popup_window),
2270 keyboard, pointer, time))
2272 gtk_widget_hide (priv->popup_window);
2276 gtk_device_grab_add (priv->popup_window, pointer, TRUE);
2277 priv->grab_pointer = pointer;
2278 priv->grab_keyboard = keyboard;
2282 gtk_combo_box_real_popup (GtkComboBox *combo_box)
2286 device = gtk_get_current_event_device ();
2290 GdkDeviceManager *device_manager;
2291 GdkDisplay *display;
2294 display = gtk_widget_get_display (GTK_WIDGET (combo_box));
2295 device_manager = gdk_display_get_device_manager (display);
2297 /* No device was set, pick the first master device */
2298 devices = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
2299 device = devices->data;
2300 g_list_free (devices);
2303 gtk_combo_box_popup_for_device (combo_box, device);
2307 gtk_combo_box_real_popdown (GtkComboBox *combo_box)
2309 if (combo_box->priv->popup_shown)
2311 gtk_combo_box_popdown (combo_box);
2319 * gtk_combo_box_popdown:
2320 * @combo_box: a #GtkComboBox
2322 * Hides the menu or dropdown list of @combo_box.
2324 * This function is mostly intended for use by accessibility technologies;
2325 * applications should have little use for it.
2330 gtk_combo_box_popdown (GtkComboBox *combo_box)
2332 GtkComboBoxPrivate *priv = combo_box->priv;
2334 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2336 if (GTK_IS_MENU (priv->popup_widget))
2338 gtk_menu_popdown (GTK_MENU (priv->popup_widget));
2342 if (!gtk_widget_get_realized (GTK_WIDGET (combo_box)))
2345 gtk_device_grab_remove (priv->popup_window, priv->grab_pointer);
2346 gtk_widget_hide (priv->popup_window);
2347 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button),
2350 priv->grab_pointer = NULL;
2351 priv->grab_keyboard = NULL;
2355 gtk_combo_box_update_requested_width (GtkComboBox *combo_box,
2358 GtkComboBoxPrivate *priv = combo_box->priv;
2359 gint padding, min_width, nat_width;
2361 if (priv->cell_view)
2362 gtk_widget_style_get (priv->cell_view,
2363 "focus-line-width", &padding,
2368 /* add some pixels for good measure */
2369 padding += BONUS_PADDING;
2371 if (priv->cell_view)
2372 gtk_cell_view_get_desired_width_of_row (GTK_CELL_VIEW (priv->cell_view),
2373 path, &min_width, &nat_width);
2375 min_width = nat_width = 0;
2377 min_width += padding;
2378 nat_width += padding;
2380 if (min_width > priv->minimum_width || nat_width > priv->natural_width)
2382 priv->minimum_width = MAX (priv->minimum_width, min_width);
2383 priv->natural_width = MAX (priv->natural_width, nat_width);
2385 if (priv->cell_view)
2387 gtk_widget_set_size_request (priv->cell_view, min_width, -1);
2388 gtk_widget_queue_resize (priv->cell_view);
2393 #define GTK_COMBO_BOX_SIZE_ALLOCATE_BUTTON \
2394 gtk_widget_get_preferred_size (combo_box->priv->button, \
2398 child.x = allocation->x + shadow_width; \
2400 child.x = allocation->x + allocation->width - req.width - shadow_width; \
2402 child.y = allocation->y + shadow_height; \
2403 child.width = req.width; \
2404 child.height = allocation->height - 2 * shadow_height; \
2405 child.width = MAX (1, child.width); \
2406 child.height = MAX (1, child.height); \
2408 gtk_widget_size_allocate (combo_box->priv->button, &child);
2411 gtk_combo_box_size_allocate (GtkWidget *widget,
2412 GtkAllocation *allocation)
2414 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
2415 GtkComboBoxPrivate *priv = combo_box->priv;
2416 GtkWidget *child_widget;
2417 gint shadow_width, shadow_height;
2418 gint focus_width, focus_pad;
2419 GtkAllocation child;
2422 gboolean is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
2424 gtk_widget_set_allocation (widget, allocation);
2425 child_widget = gtk_bin_get_child (GTK_BIN (widget));
2427 style = gtk_widget_get_style (widget);
2428 gtk_widget_style_get (widget,
2429 "focus-line-width", &focus_width,
2430 "focus-padding", &focus_pad,
2433 if (GTK_SHADOW_NONE != priv->shadow_type)
2435 shadow_width = style->xthickness;
2436 shadow_height = style->ythickness;
2444 if (!priv->tree_view)
2446 if (priv->cell_view)
2448 gint xthickness, ythickness;
2453 allocation->x += shadow_width;
2454 allocation->y += shadow_height;
2455 allocation->width -= 2 * shadow_width;
2456 allocation->height -= 2 * shadow_height;
2458 gtk_widget_size_allocate (priv->button, allocation);
2460 /* set some things ready */
2461 border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->button));
2462 style = gtk_widget_get_style (priv->button);
2463 xthickness = style->xthickness;
2464 ythickness = style->ythickness;
2466 child.x = allocation->x;
2467 child.y = allocation->y;
2468 width = allocation->width;
2469 child.height = allocation->height;
2471 if (!priv->is_cell_renderer)
2473 child.x += border_width + xthickness + focus_width + focus_pad;
2474 child.y += border_width + ythickness + focus_width + focus_pad;
2475 width -= 2 * (child.x - allocation->x);
2476 child.height -= 2 * (child.y - allocation->y);
2480 /* handle the children */
2481 gtk_widget_get_preferred_size (priv->arrow, &req, NULL);
2482 child.width = req.width;
2484 child.x += width - req.width;
2485 child.width = MAX (1, child.width);
2486 child.height = MAX (1, child.height);
2487 gtk_widget_size_allocate (priv->arrow, &child);
2489 child.x += req.width;
2490 gtk_widget_get_preferred_size (priv->separator, &req, NULL);
2491 child.width = req.width;
2493 child.x -= req.width;
2494 child.width = MAX (1, child.width);
2495 child.height = MAX (1, child.height);
2496 gtk_widget_size_allocate (priv->separator, &child);
2500 child.x += req.width;
2501 child.width = allocation->x + allocation->width
2502 - (border_width + xthickness + focus_width + focus_pad)
2507 child.width = child.x;
2508 child.x = allocation->x
2509 + border_width + xthickness + focus_width + focus_pad;
2510 child.width -= child.x;
2513 if (gtk_widget_get_visible (priv->popup_widget))
2515 gint width, menu_width;
2517 if (priv->wrap_width == 0)
2519 GtkAllocation combo_box_allocation;
2521 gtk_widget_get_allocation (GTK_WIDGET (combo_box), &combo_box_allocation);
2522 width = combo_box_allocation.width;
2523 gtk_widget_set_size_request (priv->popup_widget, -1, -1);
2525 if (combo_box->priv->popup_fixed_width)
2526 gtk_widget_get_preferred_width (priv->popup_widget, &menu_width, NULL);
2528 gtk_widget_get_preferred_width (priv->popup_widget, NULL, &menu_width);
2530 gtk_widget_set_size_request (priv->popup_widget,
2531 MAX (width, menu_width), -1);
2534 /* reposition the menu after giving it a new width */
2535 gtk_menu_reposition (GTK_MENU (priv->popup_widget));
2538 child.width = MAX (1, child.width);
2539 child.height = MAX (1, child.height);
2540 gtk_widget_size_allocate (child_widget, &child);
2544 GTK_COMBO_BOX_SIZE_ALLOCATE_BUTTON
2547 child.x = allocation->x + req.width + shadow_width;
2549 child.x = allocation->x + shadow_width;
2550 child.y = allocation->y + shadow_height;
2551 child.width = allocation->width - req.width - 2 * shadow_width;
2552 child.width = MAX (1, child.width);
2553 child.height = MAX (1, child.height);
2554 gtk_widget_size_allocate (child_widget, &child);
2561 /* Combobox thickness + border-width */
2562 guint border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
2563 int delta_x = shadow_width + border_width;
2564 int delta_y = shadow_height + border_width;
2567 GTK_COMBO_BOX_SIZE_ALLOCATE_BUTTON
2571 child.x = allocation->x + req.width;
2573 child.x = allocation->x;
2575 child.y = allocation->y;
2576 child.width = allocation->width - req.width;
2577 child.height = allocation->height;
2579 if (priv->cell_view_frame)
2583 child.width = MAX (1, child.width - delta_x * 2);
2584 child.height = MAX (1, child.height - delta_y * 2);
2585 gtk_widget_size_allocate (priv->cell_view_frame, &child);
2588 if (priv->has_frame)
2590 border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->cell_view_frame));
2591 style = gtk_widget_get_style (priv->cell_view_frame);
2592 delta_x = border_width + style->xthickness;
2593 delta_y = border_width + style->ythickness;
2597 child.width -= delta_x * 2;
2598 child.height -= delta_y * 2;
2605 child.width -= delta_x * 2;
2606 child.height -= delta_y * 2;
2609 if (gtk_widget_get_visible (priv->popup_window))
2611 gint x, y, width, height;
2612 gtk_combo_box_list_position (combo_box, &x, &y, &width, &height);
2613 gtk_window_move (GTK_WINDOW (priv->popup_window), x, y);
2614 gtk_widget_set_size_request (priv->popup_window, width, height);
2618 child.width = MAX (1, child.width);
2619 child.height = MAX (1, child.height);
2621 gtk_widget_size_allocate (child_widget, &child);
2625 #undef GTK_COMBO_BOX_ALLOCATE_BUTTON
2628 gtk_combo_box_unset_model (GtkComboBox *combo_box)
2630 GtkComboBoxPrivate *priv = combo_box->priv;
2634 g_signal_handler_disconnect (priv->model,
2636 g_signal_handler_disconnect (priv->model,
2638 g_signal_handler_disconnect (priv->model,
2639 priv->reordered_id);
2640 g_signal_handler_disconnect (priv->model,
2645 if (!priv->tree_view)
2647 if (priv->popup_widget)
2648 gtk_container_foreach (GTK_CONTAINER (priv->popup_widget),
2649 (GtkCallback)gtk_widget_destroy, NULL);
2654 g_object_unref (priv->model);
2658 if (priv->active_row)
2660 gtk_tree_row_reference_free (priv->active_row);
2661 priv->active_row = NULL;
2664 if (priv->cell_view)
2665 gtk_cell_view_set_model (GTK_CELL_VIEW (priv->cell_view), NULL);
2669 gtk_combo_box_forall (GtkContainer *container,
2670 gboolean include_internals,
2671 GtkCallback callback,
2672 gpointer callback_data)
2674 GtkComboBox *combo_box = GTK_COMBO_BOX (container);
2675 GtkComboBoxPrivate *priv = combo_box->priv;
2678 if (include_internals)
2681 (* callback) (priv->button, callback_data);
2682 if (priv->cell_view_frame)
2683 (* callback) (priv->cell_view_frame, callback_data);
2686 child = gtk_bin_get_child (GTK_BIN (container));
2688 (* callback) (child, callback_data);
2692 gtk_combo_box_child_show (GtkWidget *widget,
2693 GtkComboBox *combo_box)
2695 GtkComboBoxPrivate *priv = combo_box->priv;
2697 priv->popup_shown = TRUE;
2698 g_object_notify (G_OBJECT (combo_box), "popup-shown");
2702 gtk_combo_box_child_hide (GtkWidget *widget,
2703 GtkComboBox *combo_box)
2705 GtkComboBoxPrivate *priv = combo_box->priv;
2707 priv->popup_shown = FALSE;
2708 g_object_notify (G_OBJECT (combo_box), "popup-shown");
2712 gtk_combo_box_draw (GtkWidget *widget,
2715 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
2716 GtkComboBoxPrivate *priv = combo_box->priv;
2718 if (priv->shadow_type != GTK_SHADOW_NONE)
2720 gtk_paint_shadow (gtk_widget_get_style (widget),
2722 GTK_STATE_NORMAL, priv->shadow_type,
2725 gtk_widget_get_allocated_width (widget),
2726 gtk_widget_get_allocated_height (widget));
2729 gtk_container_propagate_draw (GTK_CONTAINER (widget),
2732 if (priv->tree_view && priv->cell_view_frame)
2734 gtk_container_propagate_draw (GTK_CONTAINER (widget),
2735 priv->cell_view_frame, cr);
2738 gtk_container_propagate_draw (GTK_CONTAINER (widget),
2739 gtk_bin_get_child (GTK_BIN (widget)),
2755 path_visible (GtkTreeView *view,
2761 /* Note that we rely on the fact that collapsed rows don't have nodes
2763 return _gtk_tree_view_find_node (view, path, &tree, &node);
2767 tree_next_func (GtkTreeModel *model,
2772 SearchData *search_data = (SearchData *)data;
2774 if (search_data->found)
2776 if (!tree_column_row_is_sensitive (search_data->combo, iter))
2779 if (search_data->visible &&
2780 !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
2783 search_data->set = TRUE;
2784 search_data->iter = *iter;
2789 if (gtk_tree_path_compare (path, search_data->path) == 0)
2790 search_data->found = TRUE;
2796 tree_next (GtkComboBox *combo,
2797 GtkTreeModel *model,
2802 SearchData search_data;
2804 search_data.combo = combo;
2805 search_data.path = gtk_tree_model_get_path (model, iter);
2806 search_data.visible = visible;
2807 search_data.found = FALSE;
2808 search_data.set = FALSE;
2810 gtk_tree_model_foreach (model, tree_next_func, &search_data);
2812 *next = search_data.iter;
2814 gtk_tree_path_free (search_data.path);
2816 return search_data.set;
2820 tree_prev_func (GtkTreeModel *model,
2825 SearchData *search_data = (SearchData *)data;
2827 if (gtk_tree_path_compare (path, search_data->path) == 0)
2829 search_data->found = TRUE;
2833 if (!tree_column_row_is_sensitive (search_data->combo, iter))
2836 if (search_data->visible &&
2837 !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
2840 search_data->set = TRUE;
2841 search_data->iter = *iter;
2847 tree_prev (GtkComboBox *combo,
2848 GtkTreeModel *model,
2853 SearchData search_data;
2855 search_data.combo = combo;
2856 search_data.path = gtk_tree_model_get_path (model, iter);
2857 search_data.visible = visible;
2858 search_data.found = FALSE;
2859 search_data.set = FALSE;
2861 gtk_tree_model_foreach (model, tree_prev_func, &search_data);
2863 *prev = search_data.iter;
2865 gtk_tree_path_free (search_data.path);
2867 return search_data.set;
2871 tree_last_func (GtkTreeModel *model,
2876 SearchData *search_data = (SearchData *)data;
2878 if (!tree_column_row_is_sensitive (search_data->combo, iter))
2881 /* Note that we rely on the fact that collapsed rows don't have nodes
2883 if (search_data->visible &&
2884 !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
2887 search_data->set = TRUE;
2888 search_data->iter = *iter;
2894 tree_last (GtkComboBox *combo,
2895 GtkTreeModel *model,
2899 SearchData search_data;
2901 search_data.combo = combo;
2902 search_data.visible = visible;
2903 search_data.set = FALSE;
2905 gtk_tree_model_foreach (model, tree_last_func, &search_data);
2907 *last = search_data.iter;
2909 return search_data.set;
2914 tree_first_func (GtkTreeModel *model,
2919 SearchData *search_data = (SearchData *)data;
2921 if (!tree_column_row_is_sensitive (search_data->combo, iter))
2924 if (search_data->visible &&
2925 !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
2928 search_data->set = TRUE;
2929 search_data->iter = *iter;
2935 tree_first (GtkComboBox *combo,
2936 GtkTreeModel *model,
2940 SearchData search_data;
2942 search_data.combo = combo;
2943 search_data.visible = visible;
2944 search_data.set = FALSE;
2946 gtk_tree_model_foreach (model, tree_first_func, &search_data);
2948 *first = search_data.iter;
2950 return search_data.set;
2954 gtk_combo_box_scroll_event (GtkWidget *widget,
2955 GdkEventScroll *event)
2957 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
2960 GtkTreeIter new_iter;
2962 if (!gtk_combo_box_get_active_iter (combo_box, &iter))
2965 if (event->direction == GDK_SCROLL_UP)
2966 found = tree_prev (combo_box, combo_box->priv->model,
2967 &iter, &new_iter, FALSE);
2969 found = tree_next (combo_box, combo_box->priv->model,
2970 &iter, &new_iter, FALSE);
2973 gtk_combo_box_set_active_iter (combo_box, &new_iter);
2983 gtk_combo_box_sync_cells (GtkComboBox *combo_box,
2984 GtkCellLayout *cell_layout)
2986 GtkComboBoxPrivate *priv = combo_box->priv;
2989 for (k = priv->cells; k; k = k->next)
2992 ComboCellInfo *info = (ComboCellInfo *)k->data;
2994 if (info->pack == GTK_PACK_START)
2995 gtk_cell_layout_pack_start (cell_layout,
2996 info->cell, info->expand);
2997 else if (info->pack == GTK_PACK_END)
2998 gtk_cell_layout_pack_end (cell_layout,
2999 info->cell, info->expand);
3001 gtk_cell_layout_set_cell_data_func (cell_layout,
3003 combo_cell_data_func, info, NULL);
3005 for (j = info->attributes; j; j = j->next->next)
3007 gtk_cell_layout_add_attribute (cell_layout,
3010 GPOINTER_TO_INT (j->next->data));
3016 gtk_combo_box_menu_setup (GtkComboBox *combo_box,
3017 gboolean add_children)
3019 GtkComboBoxPrivate *priv = combo_box->priv;
3023 child = gtk_bin_get_child (GTK_BIN (combo_box));
3025 if (priv->cell_view)
3027 priv->button = gtk_toggle_button_new ();
3028 gtk_button_set_focus_on_click (GTK_BUTTON (priv->button),
3029 priv->focus_on_click);
3031 g_signal_connect (priv->button, "toggled",
3032 G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
3033 gtk_widget_set_parent (priv->button,
3034 gtk_widget_get_parent (child));
3036 priv->box = gtk_hbox_new (FALSE, 0);
3037 gtk_container_add (GTK_CONTAINER (priv->button), priv->box);
3039 priv->separator = gtk_vseparator_new ();
3040 gtk_container_add (GTK_CONTAINER (priv->box), priv->separator);
3042 priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3043 gtk_container_add (GTK_CONTAINER (priv->box), priv->arrow);
3045 gtk_widget_show_all (priv->button);
3049 priv->button = gtk_toggle_button_new ();
3050 gtk_button_set_focus_on_click (GTK_BUTTON (priv->button),
3051 priv->focus_on_click);
3053 g_signal_connect (priv->button, "toggled",
3054 G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
3055 gtk_widget_set_parent (priv->button,
3056 gtk_widget_get_parent (child));
3058 priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3059 gtk_container_add (GTK_CONTAINER (priv->button), priv->arrow);
3060 gtk_widget_show_all (priv->button);
3063 g_signal_connect (priv->button, "button-press-event",
3064 G_CALLBACK (gtk_combo_box_menu_button_press),
3066 g_signal_connect (priv->button, "state-changed",
3067 G_CALLBACK (gtk_combo_box_button_state_changed),
3070 /* create our funky menu */
3071 menu = gtk_menu_new ();
3072 gtk_widget_set_name (menu, "gtk-combobox-popup-menu");
3073 gtk_menu_set_reserve_toggle_size (GTK_MENU (menu), FALSE);
3075 g_signal_connect (menu, "key-press-event",
3076 G_CALLBACK (gtk_combo_box_menu_key_press), combo_box);
3077 gtk_combo_box_set_popup_widget (combo_box, menu);
3081 gtk_combo_box_menu_fill (combo_box);
3083 /* the column is needed in tree_column_row_is_sensitive() */
3084 priv->column = gtk_tree_view_column_new ();
3085 g_object_ref_sink (priv->column);
3086 gtk_combo_box_sync_cells (combo_box, GTK_CELL_LAYOUT (priv->column));
3088 gtk_combo_box_update_title (combo_box);
3089 gtk_combo_box_update_sensitivity (combo_box);
3093 gtk_combo_box_menu_fill (GtkComboBox *combo_box)
3095 GtkComboBoxPrivate *priv = combo_box->priv;
3101 menu = priv->popup_widget;
3103 if (priv->add_tearoffs)
3105 GtkWidget *tearoff = gtk_tearoff_menu_item_new ();
3107 gtk_widget_show (tearoff);
3109 if (priv->wrap_width)
3110 gtk_menu_attach (GTK_MENU (menu), tearoff, 0, priv->wrap_width, 0, 1);
3112 gtk_menu_shell_append (GTK_MENU_SHELL (menu), tearoff);
3115 gtk_combo_box_menu_fill_level (combo_box, menu, NULL);
3119 gtk_cell_view_menu_item_new (GtkComboBox *combo_box,
3120 GtkTreeModel *model,
3123 GtkWidget *cell_view;
3128 cell_view = gtk_cell_view_new ();
3129 item = gtk_menu_item_new ();
3130 gtk_container_add (GTK_CONTAINER (item), cell_view);
3132 gtk_cell_view_set_model (GTK_CELL_VIEW (cell_view), model);
3133 path = gtk_tree_model_get_path (model, iter);
3134 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (cell_view), path);
3135 gtk_tree_path_free (path);
3137 gtk_combo_box_sync_cells (combo_box, GTK_CELL_LAYOUT (cell_view));
3138 gtk_widget_get_preferred_size (cell_view, &req, NULL);
3139 gtk_widget_show (cell_view);
3145 gtk_combo_box_menu_fill_level (GtkComboBox *combo_box,
3147 GtkTreeIter *parent)
3149 GtkComboBoxPrivate *priv = combo_box->priv;
3150 GtkTreeModel *model = priv->model;
3151 GtkWidget *item, *submenu, *subitem, *separator;
3153 gboolean is_separator;
3158 n_children = gtk_tree_model_iter_n_children (model, parent);
3161 for (i = 0; i < n_children; i++)
3163 gtk_tree_model_iter_nth_child (model, &iter, parent, i);
3165 if (priv->row_separator_func)
3166 is_separator = priv->row_separator_func (priv->model, &iter,
3167 priv->row_separator_data);
3169 is_separator = FALSE;
3173 item = gtk_separator_menu_item_new ();
3174 path = gtk_tree_model_get_path (model, &iter);
3175 g_object_set_data_full (G_OBJECT (item),
3176 I_("gtk-combo-box-item-path"),
3177 gtk_tree_row_reference_new (model, path),
3178 (GDestroyNotify)gtk_tree_row_reference_free);
3179 gtk_tree_path_free (path);
3183 item = gtk_cell_view_menu_item_new (combo_box, model, &iter);
3184 if (gtk_tree_model_iter_has_child (model, &iter))
3186 submenu = gtk_menu_new ();
3187 gtk_menu_set_reserve_toggle_size (GTK_MENU (submenu), FALSE);
3188 gtk_widget_show (submenu);
3189 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu);
3191 /* Ugly - since menus can only activate leafs, we have to
3192 * duplicate the item inside the submenu.
3194 subitem = gtk_cell_view_menu_item_new (combo_box, model, &iter);
3195 separator = gtk_separator_menu_item_new ();
3196 gtk_widget_show (subitem);
3197 gtk_widget_show (separator);
3198 g_signal_connect (subitem, "activate",
3199 G_CALLBACK (gtk_combo_box_menu_item_activate),
3201 gtk_menu_shell_append (GTK_MENU_SHELL (submenu), subitem);
3202 gtk_menu_shell_append (GTK_MENU_SHELL (submenu), separator);
3204 gtk_combo_box_menu_fill_level (combo_box, submenu, &iter);
3206 g_signal_connect (item, "activate",
3207 G_CALLBACK (gtk_combo_box_menu_item_activate),
3211 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3212 if (priv->wrap_width && menu == priv->popup_widget)
3213 gtk_combo_box_relayout_item (combo_box, item, &iter, last);
3214 gtk_widget_show (item);
3221 gtk_combo_box_menu_destroy (GtkComboBox *combo_box)
3223 GtkComboBoxPrivate *priv = combo_box->priv;
3225 g_signal_handlers_disconnect_matched (priv->button,
3226 G_SIGNAL_MATCH_DATA,
3228 gtk_combo_box_menu_button_press, NULL);
3229 g_signal_handlers_disconnect_matched (priv->button,
3230 G_SIGNAL_MATCH_DATA,
3232 gtk_combo_box_button_state_changed, combo_box);
3234 /* unparent will remove our latest ref */
3235 gtk_widget_unparent (priv->button);
3238 priv->button = NULL;
3240 priv->separator = NULL;
3242 g_object_unref (priv->column);
3243 priv->column = NULL;
3245 /* changing the popup window will unref the menu and the children */
3253 menu_occupied (GtkMenu *menu,
3257 guint bottom_attach)
3261 for (i = GTK_MENU_SHELL (menu)->children; i; i = i->next)
3265 gtk_container_child_get (GTK_CONTAINER (menu),
3269 "bottom-attach", &b,
3273 /* look if this item intersects with the given coordinates */
3274 if (right_attach > l && left_attach < r && bottom_attach > t && top_attach < b)
3282 gtk_combo_box_relayout_item (GtkComboBox *combo_box,
3287 GtkComboBoxPrivate *priv = combo_box->priv;
3288 gint current_col = 0, current_row = 0;
3289 gint rows = 1, cols = 1;
3290 GtkWidget *menu = priv->popup_widget;
3292 if (!GTK_IS_MENU_SHELL (menu))
3295 if (priv->col_column == -1 &&
3296 priv->row_column == -1 &&
3299 gtk_container_child_get (GTK_CONTAINER (menu),
3301 "right-attach", ¤t_col,
3302 "top-attach", ¤t_row,
3304 if (current_col + cols > priv->wrap_width)
3312 if (priv->col_column != -1)
3313 gtk_tree_model_get (priv->model, iter,
3314 priv->col_column, &cols,
3316 if (priv->row_column != -1)
3317 gtk_tree_model_get (priv->model, iter,
3318 priv->row_column, &rows,
3323 if (current_col + cols > priv->wrap_width)
3329 if (!menu_occupied (GTK_MENU (menu),
3330 current_col, current_col + cols,
3331 current_row, current_row + rows))
3338 /* set attach props */
3339 gtk_menu_attach (GTK_MENU (menu), item,
3340 current_col, current_col + cols,
3341 current_row, current_row + rows);
3345 gtk_combo_box_relayout (GtkComboBox *combo_box)
3350 menu = combo_box->priv->popup_widget;
3352 /* do nothing unless we are in menu style and realized */
3353 if (combo_box->priv->tree_view || !GTK_IS_MENU_SHELL (menu))
3356 list = gtk_container_get_children (GTK_CONTAINER (menu));
3358 for (j = g_list_last (list); j; j = j->prev)
3359 gtk_container_remove (GTK_CONTAINER (menu), j->data);
3361 gtk_combo_box_menu_fill (combo_box);
3368 gtk_combo_box_menu_button_press (GtkWidget *widget,
3369 GdkEventButton *event,
3372 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3373 GtkComboBoxPrivate *priv = combo_box->priv;
3375 if (GTK_IS_MENU (priv->popup_widget) &&
3376 event->type == GDK_BUTTON_PRESS && event->button == 1)
3378 if (priv->focus_on_click &&
3379 !gtk_widget_has_focus (priv->button))
3380 gtk_widget_grab_focus (priv->button);
3382 gtk_combo_box_menu_popup (combo_box, event->button, event->time);
3391 gtk_combo_box_menu_item_activate (GtkWidget *item,
3394 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3395 GtkWidget *cell_view;
3399 cell_view = gtk_bin_get_child (GTK_BIN (item));
3401 g_return_if_fail (GTK_IS_CELL_VIEW (cell_view));
3403 path = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (cell_view));
3405 if (gtk_tree_model_get_iter (combo_box->priv->model, &iter, path))
3407 if (gtk_menu_item_get_submenu (GTK_MENU_ITEM (item)) == NULL)
3408 gtk_combo_box_set_active_iter (combo_box, &iter);
3411 gtk_tree_path_free (path);
3413 g_object_set (combo_box,
3414 "editing-canceled", FALSE,
3419 gtk_combo_box_update_sensitivity (GtkComboBox *combo_box)
3422 gboolean sensitive = TRUE; /* fool code checkers */
3424 if (!combo_box->priv->button)
3427 switch (combo_box->priv->button_sensitivity)
3429 case GTK_SENSITIVITY_ON:
3432 case GTK_SENSITIVITY_OFF:
3435 case GTK_SENSITIVITY_AUTO:
3436 sensitive = combo_box->priv->model &&
3437 gtk_tree_model_get_iter_first (combo_box->priv->model, &iter);
3440 g_assert_not_reached ();
3444 gtk_widget_set_sensitive (combo_box->priv->button, sensitive);
3446 /* In list-mode, we also need to update sensitivity of the event box */
3447 if (GTK_IS_TREE_VIEW (combo_box->priv->tree_view)
3448 && combo_box->priv->cell_view)
3449 gtk_widget_set_sensitive (combo_box->priv->box, sensitive);
3453 gtk_combo_box_model_row_inserted (GtkTreeModel *model,
3458 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3460 if (combo_box->priv->tree_view)
3461 gtk_combo_box_list_popup_resize (combo_box);
3463 gtk_combo_box_menu_row_inserted (model, path, iter, user_data);
3465 gtk_combo_box_update_sensitivity (combo_box);
3469 gtk_combo_box_model_row_deleted (GtkTreeModel *model,
3473 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3474 GtkComboBoxPrivate *priv = combo_box->priv;
3476 if (!gtk_tree_row_reference_valid (priv->active_row))
3478 if (priv->cell_view)
3479 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (priv->cell_view), NULL);
3480 g_signal_emit (combo_box, combo_box_signals[CHANGED], 0);
3483 if (priv->tree_view)
3484 gtk_combo_box_list_popup_resize (combo_box);
3486 gtk_combo_box_menu_row_deleted (model, path, user_data);
3488 gtk_combo_box_update_sensitivity (combo_box);
3492 gtk_combo_box_model_rows_reordered (GtkTreeModel *model,
3498 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3500 gtk_tree_row_reference_reordered (G_OBJECT (user_data), path, iter, new_order);
3502 if (!combo_box->priv->tree_view)
3503 gtk_combo_box_menu_rows_reordered (model, path, iter, new_order, user_data);
3507 gtk_combo_box_model_row_changed (GtkTreeModel *model,
3512 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3513 GtkComboBoxPrivate *priv = combo_box->priv;
3514 GtkTreePath *active_path;
3516 /* FIXME this belongs to GtkCellView */
3517 if (gtk_tree_row_reference_valid (priv->active_row))
3519 active_path = gtk_tree_row_reference_get_path (priv->active_row);
3520 if (gtk_tree_path_compare (path, active_path) == 0 &&
3522 gtk_widget_queue_resize (GTK_WIDGET (priv->cell_view));
3523 gtk_tree_path_free (active_path);
3526 if (priv->tree_view)
3527 gtk_combo_box_list_row_changed (model, path, iter, user_data);
3529 gtk_combo_box_menu_row_changed (model, path, iter, user_data);
3533 list_popup_resize_idle (gpointer user_data)
3535 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3536 GtkComboBoxPrivate *priv = combo_box->priv;
3537 gint x, y, width, height;
3539 if (priv->tree_view && gtk_widget_get_mapped (priv->popup_window))
3541 gtk_combo_box_list_position (combo_box, &x, &y, &width, &height);
3543 gtk_widget_set_size_request (priv->popup_window, width, height);
3544 gtk_window_move (GTK_WINDOW (priv->popup_window), x, y);
3547 priv->resize_idle_id = 0;
3553 gtk_combo_box_list_popup_resize (GtkComboBox *combo_box)
3555 GtkComboBoxPrivate *priv = combo_box->priv;
3557 if (!priv->resize_idle_id)
3558 priv->resize_idle_id =
3559 gdk_threads_add_idle (list_popup_resize_idle, combo_box);
3563 gtk_combo_box_model_row_expanded (GtkTreeModel *model,
3568 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3570 gtk_combo_box_list_popup_resize (combo_box);
3575 find_menu_by_path (GtkWidget *menu,
3577 gboolean skip_first)
3583 GtkTreeRowReference *mref;
3587 list = gtk_container_get_children (GTK_CONTAINER (menu));
3590 for (i = list; i; i = i->next)
3592 child = gtk_bin_get_child (i->data);
3593 if (GTK_IS_SEPARATOR_MENU_ITEM (i->data))
3595 mref = g_object_get_data (G_OBJECT (i->data), "gtk-combo-box-item-path");
3598 else if (!gtk_tree_row_reference_valid (mref))
3601 mpath = gtk_tree_row_reference_get_path (mref);
3603 else if (GTK_IS_CELL_VIEW (child))
3611 mpath = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (child));
3616 /* this case is necessary, since the row reference of
3617 * the cell view may already be updated after a deletion
3624 if (gtk_tree_path_compare (mpath, path) == 0)
3626 gtk_tree_path_free (mpath);
3630 if (gtk_tree_path_is_ancestor (mpath, path))
3632 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
3633 if (submenu != NULL)
3635 gtk_tree_path_free (mpath);
3636 item = find_menu_by_path (submenu, path, TRUE);
3640 gtk_tree_path_free (mpath);
3650 dump_menu_tree (GtkWidget *menu,
3657 list = gtk_container_get_children (GTK_CONTAINER (menu));
3658 for (i = list; i; i = i->next)
3660 if (GTK_IS_CELL_VIEW (GTK_BIN (i->data)->child))
3662 path = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (GTK_BIN (i->data)->child));
3663 g_print ("%*s%s\n", 2 * level, " ", gtk_tree_path_to_string (path));
3664 gtk_tree_path_free (path);
3666 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
3667 if (submenu != NULL)
3668 dump_menu_tree (submenu, level + 1);
3677 gtk_combo_box_menu_row_inserted (GtkTreeModel *model,
3682 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3683 GtkComboBoxPrivate *priv = combo_box->priv;
3685 GtkWidget *item, *menu, *separator;
3689 gboolean is_separator;
3691 if (!priv->popup_widget)
3694 depth = gtk_tree_path_get_depth (path);
3695 pos = gtk_tree_path_get_indices (path)[depth - 1];
3698 ppath = gtk_tree_path_copy (path);
3699 gtk_tree_path_up (ppath);
3700 parent = find_menu_by_path (priv->popup_widget, ppath, FALSE);
3701 gtk_tree_path_free (ppath);
3703 menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (parent));
3706 menu = gtk_menu_new ();
3707 gtk_menu_set_reserve_toggle_size (GTK_MENU (menu), FALSE);
3708 gtk_widget_show (menu);
3709 gtk_menu_item_set_submenu (GTK_MENU_ITEM (parent), menu);
3711 /* Ugly - since menus can only activate leaves, we have to
3712 * duplicate the item inside the submenu.
3714 gtk_tree_model_iter_parent (model, &piter, iter);
3715 item = gtk_cell_view_menu_item_new (combo_box, model, &piter);
3716 separator = gtk_separator_menu_item_new ();
3717 g_signal_connect (item, "activate",
3718 G_CALLBACK (gtk_combo_box_menu_item_activate),
3720 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3721 gtk_menu_shell_append (GTK_MENU_SHELL (menu), separator);
3722 if (cell_view_is_sensitive (GTK_CELL_VIEW (gtk_bin_get_child (GTK_BIN (item)))))
3724 gtk_widget_show (item);
3725 gtk_widget_show (separator);
3732 menu = priv->popup_widget;
3733 if (priv->add_tearoffs)
3737 if (priv->row_separator_func)
3738 is_separator = priv->row_separator_func (model, iter,
3739 priv->row_separator_data);
3741 is_separator = FALSE;
3745 item = gtk_separator_menu_item_new ();
3746 g_object_set_data_full (G_OBJECT (item),
3747 I_("gtk-combo-box-item-path"),
3748 gtk_tree_row_reference_new (model, path),
3749 (GDestroyNotify)gtk_tree_row_reference_free);
3753 item = gtk_cell_view_menu_item_new (combo_box, model, iter);
3755 g_signal_connect (item, "activate",
3756 G_CALLBACK (gtk_combo_box_menu_item_activate),
3760 gtk_widget_show (item);
3761 gtk_menu_shell_insert (GTK_MENU_SHELL (menu), item, pos);
3765 gtk_combo_box_menu_row_deleted (GtkTreeModel *model,
3769 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3770 GtkComboBoxPrivate *priv = combo_box->priv;
3774 if (!priv->popup_widget)
3777 item = find_menu_by_path (priv->popup_widget, path, FALSE);
3778 menu = gtk_widget_get_parent (item);
3779 gtk_container_remove (GTK_CONTAINER (menu), item);
3781 if (gtk_tree_path_get_depth (path) > 1)
3783 GtkTreePath *parent_path;
3787 parent_path = gtk_tree_path_copy (path);
3788 gtk_tree_path_up (parent_path);
3789 gtk_tree_model_get_iter (model, &iter, parent_path);
3791 if (!gtk_tree_model_iter_has_child (model, &iter))
3793 parent = find_menu_by_path (priv->popup_widget,
3794 parent_path, FALSE);
3795 gtk_menu_item_set_submenu (GTK_MENU_ITEM (parent), NULL);
3801 gtk_combo_box_menu_rows_reordered (GtkTreeModel *model,
3807 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3809 gtk_combo_box_relayout (combo_box);
3813 gtk_combo_box_menu_row_changed (GtkTreeModel *model,
3818 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3819 GtkComboBoxPrivate *priv = combo_box->priv;
3821 gboolean is_separator;
3823 if (!priv->popup_widget)
3826 item = find_menu_by_path (priv->popup_widget, path, FALSE);
3828 if (priv->row_separator_func)
3829 is_separator = priv->row_separator_func (model, iter,
3830 priv->row_separator_data);
3832 is_separator = FALSE;
3834 if (is_separator != GTK_IS_SEPARATOR_MENU_ITEM (item))
3836 gtk_combo_box_menu_row_deleted (model, path, combo_box);
3837 gtk_combo_box_menu_row_inserted (model, path, iter, combo_box);
3840 if (priv->wrap_width &&
3841 gtk_widget_get_parent (item) == priv->popup_widget)
3843 GtkWidget *pitem = NULL;
3846 prev = gtk_tree_path_copy (path);
3848 if (gtk_tree_path_prev (prev))
3849 pitem = find_menu_by_path (priv->popup_widget, prev, FALSE);
3851 gtk_tree_path_free (prev);
3853 /* unattach item so gtk_combo_box_relayout_item() won't spuriously
3855 gtk_container_child_set (GTK_CONTAINER (priv->popup_widget),
3860 "bottom-attach", -1,
3863 gtk_combo_box_relayout_item (combo_box, item, iter, pitem);
3866 gtk_combo_box_update_requested_width (combo_box, path);
3874 gtk_combo_box_list_setup (GtkComboBox *combo_box)
3876 GtkComboBoxPrivate *priv = combo_box->priv;
3877 GtkTreeSelection *sel;
3880 GtkWidget *widget = GTK_WIDGET (combo_box);
3882 priv->button = gtk_toggle_button_new ();
3883 child = gtk_bin_get_child (GTK_BIN (combo_box));
3884 gtk_widget_set_parent (priv->button,
3885 gtk_widget_get_parent (child));
3886 g_signal_connect (priv->button, "button-press-event",
3887 G_CALLBACK (gtk_combo_box_list_button_pressed), combo_box);
3888 g_signal_connect (priv->button, "toggled",
3889 G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
3891 priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3892 gtk_container_add (GTK_CONTAINER (priv->button), priv->arrow);
3893 priv->separator = NULL;
3894 gtk_widget_show_all (priv->button);
3896 if (priv->cell_view)
3898 style = gtk_widget_get_style (widget);
3899 gtk_cell_view_set_background_color (GTK_CELL_VIEW (priv->cell_view),
3900 &style->base[gtk_widget_get_state (widget)]);
3902 priv->box = gtk_event_box_new ();
3903 gtk_event_box_set_visible_window (GTK_EVENT_BOX (priv->box),
3906 if (priv->has_frame)
3908 priv->cell_view_frame = gtk_frame_new (NULL);
3909 gtk_frame_set_shadow_type (GTK_FRAME (priv->cell_view_frame),
3914 combo_box->priv->cell_view_frame = gtk_event_box_new ();
3915 gtk_event_box_set_visible_window (GTK_EVENT_BOX (combo_box->priv->cell_view_frame),
3919 gtk_widget_set_parent (priv->cell_view_frame,
3920 gtk_widget_get_parent (child));
3921 gtk_container_add (GTK_CONTAINER (priv->cell_view_frame), priv->box);
3922 gtk_widget_show_all (priv->cell_view_frame);
3924 g_signal_connect (priv->box, "button-press-event",
3925 G_CALLBACK (gtk_combo_box_list_button_pressed),
3929 priv->tree_view = gtk_tree_view_new ();
3930 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));
3931 gtk_tree_selection_set_mode (sel, GTK_SELECTION_BROWSE);
3932 gtk_tree_selection_set_select_function (sel,
3933 gtk_combo_box_list_select_func,
3935 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->tree_view),
3937 gtk_tree_view_set_hover_selection (GTK_TREE_VIEW (priv->tree_view),
3939 if (priv->row_separator_func)
3940 gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (priv->tree_view),
3941 priv->row_separator_func,
3942 priv->row_separator_data,
3945 gtk_tree_view_set_model (GTK_TREE_VIEW (priv->tree_view), priv->model);
3947 priv->column = gtk_tree_view_column_new ();
3948 gtk_tree_view_append_column (GTK_TREE_VIEW (priv->tree_view), priv->column);
3951 gtk_combo_box_sync_cells (combo_box,
3952 GTK_CELL_LAYOUT (priv->column));
3954 if (gtk_tree_row_reference_valid (priv->active_row))
3958 path = gtk_tree_row_reference_get_path (priv->active_row);
3959 gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->tree_view),
3961 gtk_tree_path_free (path);
3964 /* set sample/popup widgets */
3965 gtk_combo_box_set_popup_widget (combo_box, priv->tree_view);
3967 g_signal_connect (priv->tree_view, "key-press-event",
3968 G_CALLBACK (gtk_combo_box_list_key_press),
3970 g_signal_connect (priv->tree_view, "enter-notify-event",
3971 G_CALLBACK (gtk_combo_box_list_enter_notify),
3973 g_signal_connect (priv->tree_view, "row-expanded",
3974 G_CALLBACK (gtk_combo_box_model_row_expanded),
3976 g_signal_connect (priv->tree_view, "row-collapsed",
3977 G_CALLBACK (gtk_combo_box_model_row_expanded),
3979 g_signal_connect (priv->popup_window, "button-press-event",
3980 G_CALLBACK (gtk_combo_box_list_button_pressed),
3982 g_signal_connect (priv->popup_window, "button-release-event",
3983 G_CALLBACK (gtk_combo_box_list_button_released),
3986 gtk_widget_show (priv->tree_view);
3988 gtk_combo_box_update_sensitivity (combo_box);
3992 gtk_combo_box_list_destroy (GtkComboBox *combo_box)
3994 GtkComboBoxPrivate *priv = combo_box->priv;
3996 /* disconnect signals */
3997 g_signal_handlers_disconnect_matched (priv->tree_view,
3998 G_SIGNAL_MATCH_DATA,
3999 0, 0, NULL, NULL, combo_box);
4000 g_signal_handlers_disconnect_matched (priv->button,
4001 G_SIGNAL_MATCH_DATA,
4003 gtk_combo_box_list_button_pressed,
4005 g_signal_handlers_disconnect_matched (priv->popup_window,
4006 G_SIGNAL_MATCH_DATA,
4008 gtk_combo_box_list_button_pressed,
4010 g_signal_handlers_disconnect_matched (priv->popup_window,
4011 G_SIGNAL_MATCH_DATA,
4013 gtk_combo_box_list_button_released,
4016 g_signal_handlers_disconnect_matched (priv->popup_window,
4017 G_SIGNAL_MATCH_DATA,
4019 gtk_combo_box_child_show,
4022 g_signal_handlers_disconnect_matched (priv->popup_window,
4023 G_SIGNAL_MATCH_DATA,
4025 gtk_combo_box_child_hide,
4029 g_signal_handlers_disconnect_matched (priv->box,
4030 G_SIGNAL_MATCH_DATA,
4032 gtk_combo_box_list_button_pressed,
4035 /* destroy things (unparent will kill the latest ref from us)
4036 * last unref on button will destroy the arrow
4038 gtk_widget_unparent (priv->button);
4039 priv->button = NULL;
4042 if (priv->cell_view)
4044 g_object_set (priv->cell_view,
4045 "background-set", FALSE,
4049 if (priv->cell_view_frame)
4051 gtk_widget_unparent (priv->cell_view_frame);
4052 priv->cell_view_frame = NULL;
4056 if (priv->scroll_timer)
4058 g_source_remove (priv->scroll_timer);
4059 priv->scroll_timer = 0;
4062 if (priv->resize_idle_id)
4064 g_source_remove (priv->resize_idle_id);
4065 priv->resize_idle_id = 0;
4068 gtk_widget_destroy (priv->tree_view);
4070 priv->tree_view = NULL;
4071 if (priv->popup_widget)
4073 g_object_unref (priv->popup_widget);
4074 priv->popup_widget = NULL;
4081 gtk_combo_box_list_button_pressed (GtkWidget *widget,
4082 GdkEventButton *event,
4085 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
4086 GtkComboBoxPrivate *priv = combo_box->priv;
4088 GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *)event);
4090 if (ewidget == priv->popup_window)
4093 if ((ewidget != priv->button && ewidget != priv->box) ||
4094 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->button)))
4097 if (priv->focus_on_click &&
4098 !gtk_widget_has_focus (priv->button))
4099 gtk_widget_grab_focus (priv->button);
4101 gtk_combo_box_popup_for_device (combo_box, event->device);
4103 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button), TRUE);
4105 priv->auto_scroll = FALSE;
4106 if (priv->scroll_timer == 0)
4107 priv->scroll_timer = gdk_threads_add_timeout (SCROLL_TIME,
4108 (GSourceFunc) gtk_combo_box_list_scroll_timeout,
4111 priv->popup_in_progress = TRUE;
4117 gtk_combo_box_list_button_released (GtkWidget *widget,
4118 GdkEventButton *event,
4122 GtkTreePath *path = NULL;
4125 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
4126 GtkComboBoxPrivate *priv = combo_box->priv;
4128 gboolean popup_in_progress = FALSE;
4130 GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *)event);
4132 if (priv->popup_in_progress)
4134 popup_in_progress = TRUE;
4135 priv->popup_in_progress = FALSE;
4138 gtk_tree_view_set_hover_expand (GTK_TREE_VIEW (priv->tree_view),
4140 if (priv->scroll_timer)
4142 g_source_remove (priv->scroll_timer);
4143 priv->scroll_timer = 0;
4146 if (ewidget != priv->tree_view)
4148 if ((ewidget == priv->button ||
4149 ewidget == priv->box) &&
4150 !popup_in_progress &&
4151 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->button)))
4153 gtk_combo_box_popdown (combo_box);
4157 /* released outside treeview */
4158 if (ewidget != priv->button && ewidget != priv->box)
4160 gtk_combo_box_popdown (combo_box);
4168 /* select something cool */
4169 ret = gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (priv->tree_view),
4175 return TRUE; /* clicked outside window? */
4177 gtk_tree_model_get_iter (priv->model, &iter, path);
4178 gtk_tree_path_free (path);
4180 gtk_combo_box_popdown (combo_box);
4182 if (tree_column_row_is_sensitive (combo_box, &iter))
4183 gtk_combo_box_set_active_iter (combo_box, &iter);
4189 gtk_combo_box_menu_key_press (GtkWidget *widget,
4193 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
4195 if (!gtk_bindings_activate_event (G_OBJECT (widget), event))
4197 /* The menu hasn't managed the
4198 * event, forward it to the combobox
4200 gtk_bindings_activate_event (G_OBJECT (combo_box), event);
4207 gtk_combo_box_list_key_press (GtkWidget *widget,
4211 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
4214 if (event->keyval == GDK_KEY_Return || event->keyval == GDK_KEY_ISO_Enter || event->keyval == GDK_KEY_KP_Enter ||
4215 event->keyval == GDK_KEY_space || event->keyval == GDK_KEY_KP_Space)
4217 GtkTreeModel *model = NULL;
4219 gtk_combo_box_popdown (combo_box);
4221 if (combo_box->priv->model)
4223 GtkTreeSelection *sel;
4225 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (combo_box->priv->tree_view));
4227 if (gtk_tree_selection_get_selected (sel, &model, &iter))
4228 gtk_combo_box_set_active_iter (combo_box, &iter);
4234 if (!gtk_bindings_activate_event (G_OBJECT (widget), event))
4236 /* The list hasn't managed the
4237 * event, forward it to the combobox
4239 gtk_bindings_activate_event (G_OBJECT (combo_box), event);
4246 gtk_combo_box_list_auto_scroll (GtkComboBox *combo_box,
4251 GtkAllocation allocation;
4252 GtkWidget *tree_view = combo_box->priv->tree_view;
4255 gtk_widget_get_allocation (tree_view, &allocation);
4257 adj = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window));
4258 if (adj && adj->upper - adj->lower > adj->page_size)
4260 if (x <= allocation.x &&
4261 adj->lower < adj->value)
4263 value = adj->value - (allocation.x - x + 1);
4264 gtk_adjustment_set_value (adj, value);
4266 else if (x >= allocation.x + allocation.width &&
4267 adj->upper - adj->page_size > adj->value)
4269 value = adj->value + (x - allocation.x - allocation.width + 1);
4270 gtk_adjustment_set_value (adj, MAX (value, 0.0));
4274 adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window));
4275 if (adj && adj->upper - adj->lower > adj->page_size)
4277 if (y <= allocation.y &&
4278 adj->lower < adj->value)
4280 value = adj->value - (allocation.y - y + 1);
4281 gtk_adjustment_set_value (adj, value);
4283 else if (y >= allocation.height &&
4284 adj->upper - adj->page_size > adj->value)
4286 value = adj->value + (y - allocation.height + 1);
4287 gtk_adjustment_set_value (adj, MAX (value, 0.0));
4293 gtk_combo_box_list_scroll_timeout (GtkComboBox *combo_box)
4295 GtkComboBoxPrivate *priv = combo_box->priv;
4298 if (priv->auto_scroll)
4300 gdk_window_get_device_position (gtk_widget_get_window (priv->tree_view),
4303 gtk_combo_box_list_auto_scroll (combo_box, x, y);
4310 gtk_combo_box_list_enter_notify (GtkWidget *widget,
4311 GdkEventCrossing *event,
4314 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
4316 combo_box->priv->auto_scroll = TRUE;
4322 gtk_combo_box_list_select_func (GtkTreeSelection *selection,
4323 GtkTreeModel *model,
4325 gboolean path_currently_selected,
4329 gboolean sensitive = FALSE;
4331 for (list = selection->tree_view->priv->columns; list && !sensitive; list = list->next)
4333 GList *cells, *cell;
4334 gboolean cell_sensitive, cell_visible;
4336 GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN (list->data);
4338 if (!column->visible)
4341 gtk_tree_model_get_iter (model, &iter, path);
4342 gtk_tree_view_column_cell_set_cell_data (column, model, &iter,
4345 cell = cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
4348 g_object_get (cell->data,
4349 "sensitive", &cell_sensitive,
4350 "visible", &cell_visible,
4353 if (cell_visible && cell_sensitive)
4358 g_list_free (cells);
4360 sensitive = cell_sensitive;
4367 gtk_combo_box_list_row_changed (GtkTreeModel *model,
4372 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
4374 gtk_combo_box_update_requested_width (combo_box, path);
4378 * GtkCellLayout implementation
4382 pack_start_recurse (GtkWidget *menu,
4383 GtkCellRenderer *cell,
4390 list = gtk_container_get_children (GTK_CONTAINER (menu));
4391 for (i = list; i; i = i->next)
4393 child = gtk_bin_get_child (GTK_BIN (i->data));
4394 if (GTK_IS_CELL_LAYOUT (child))
4395 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (child),
4398 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4399 if (submenu != NULL)
4400 pack_start_recurse (submenu, cell, expand);
4407 gtk_combo_box_cell_layout_pack_start (GtkCellLayout *layout,
4408 GtkCellRenderer *cell,
4411 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4412 ComboCellInfo *info;
4413 GtkComboBoxPrivate *priv;
4415 priv = combo_box->priv;
4417 g_object_ref_sink (cell);
4419 info = g_slice_new0 (ComboCellInfo);
4421 info->expand = expand;
4422 info->pack = GTK_PACK_START;
4424 priv->cells = g_slist_append (priv->cells, info);
4426 if (priv->cell_view)
4428 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->cell_view),
4434 gtk_tree_view_column_pack_start (priv->column, cell, expand);
4436 if (GTK_IS_MENU (priv->popup_widget))
4437 pack_start_recurse (priv->popup_widget, cell, expand);
4441 pack_end_recurse (GtkWidget *menu,
4442 GtkCellRenderer *cell,
4449 list = gtk_container_get_children (GTK_CONTAINER (menu));
4450 for (i = list; i; i = i->next)
4452 child = gtk_bin_get_child (GTK_BIN (i->data));
4453 if (GTK_IS_CELL_LAYOUT (child))
4454 gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (child),
4457 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4458 if (submenu != NULL)
4459 pack_end_recurse (submenu, cell, expand);
4466 gtk_combo_box_cell_layout_pack_end (GtkCellLayout *layout,
4467 GtkCellRenderer *cell,
4470 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4471 ComboCellInfo *info;
4472 GtkComboBoxPrivate *priv;
4474 priv = combo_box->priv;
4476 g_object_ref_sink (cell);
4478 info = g_slice_new0 (ComboCellInfo);
4480 info->expand = expand;
4481 info->pack = GTK_PACK_END;
4483 priv->cells = g_slist_append (priv->cells, info);
4485 if (priv->cell_view)
4486 gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (priv->cell_view),
4490 gtk_tree_view_column_pack_end (priv->column, cell, expand);
4492 if (GTK_IS_MENU (priv->popup_widget))
4493 pack_end_recurse (priv->popup_widget, cell, expand);
4497 gtk_combo_box_cell_layout_get_cells (GtkCellLayout *layout)
4499 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4501 GList *retval = NULL;
4503 for (list = combo_box->priv->cells; list; list = list->next)
4505 ComboCellInfo *info = (ComboCellInfo *)list->data;
4507 retval = g_list_prepend (retval, info->cell);
4510 return g_list_reverse (retval);
4514 clear_recurse (GtkWidget *menu)
4520 list = gtk_container_get_children (GTK_CONTAINER (menu));
4521 for (i = list; i; i = i->next)
4523 child = gtk_bin_get_child (GTK_BIN (i->data));
4524 if (GTK_IS_CELL_LAYOUT (child))
4525 gtk_cell_layout_clear (GTK_CELL_LAYOUT (child));
4527 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4528 if (submenu != NULL)
4529 clear_recurse (submenu);
4536 gtk_combo_box_cell_layout_clear (GtkCellLayout *layout)
4538 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4539 GtkComboBoxPrivate *priv = combo_box->priv;
4542 if (priv->cell_view)
4543 gtk_cell_layout_clear (GTK_CELL_LAYOUT (priv->cell_view));
4546 gtk_tree_view_column_clear (priv->column);
4548 for (i = priv->cells; i; i = i->next)
4550 ComboCellInfo *info = (ComboCellInfo *)i->data;
4552 gtk_combo_box_cell_layout_clear_attributes (layout, info->cell);
4553 g_object_unref (info->cell);
4554 g_slice_free (ComboCellInfo, info);
4557 g_slist_free (priv->cells);
4560 if (GTK_IS_MENU (priv->popup_widget))
4561 clear_recurse (priv->popup_widget);
4565 add_attribute_recurse (GtkWidget *menu,
4566 GtkCellRenderer *cell,
4567 const gchar *attribute,
4574 list = gtk_container_get_children (GTK_CONTAINER (menu));
4575 for (i = list; i; i = i->next)
4577 child = gtk_bin_get_child (GTK_BIN (i->data));
4578 if (GTK_IS_CELL_LAYOUT (child))
4579 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (child),
4580 cell, attribute, column);
4582 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4583 if (submenu != NULL)
4584 add_attribute_recurse (submenu, cell, attribute, column);
4591 gtk_combo_box_cell_layout_add_attribute (GtkCellLayout *layout,
4592 GtkCellRenderer *cell,
4593 const gchar *attribute,
4596 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4597 ComboCellInfo *info;
4599 info = gtk_combo_box_get_cell_info (combo_box, cell);
4600 g_return_if_fail (info != NULL);
4602 info->attributes = g_slist_prepend (info->attributes,
4603 GINT_TO_POINTER (column));
4604 info->attributes = g_slist_prepend (info->attributes,
4605 g_strdup (attribute));
4607 if (combo_box->priv->cell_view)
4608 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box->priv->cell_view),
4609 cell, attribute, column);
4611 if (combo_box->priv->column)
4612 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box->priv->column),
4613 cell, attribute, column);
4615 if (GTK_IS_MENU (combo_box->priv->popup_widget))
4616 add_attribute_recurse (combo_box->priv->popup_widget, cell, attribute, column);
4617 gtk_widget_queue_resize (GTK_WIDGET (combo_box));
4621 combo_cell_data_func (GtkCellLayout *cell_layout,
4622 GtkCellRenderer *cell,
4623 GtkTreeModel *tree_model,
4627 ComboCellInfo *info = (ComboCellInfo *)data;
4628 GtkWidget *parent = NULL;
4633 info->func (cell_layout, cell, tree_model, iter, info->func_data);
4635 if (GTK_IS_WIDGET (cell_layout))
4636 parent = gtk_widget_get_parent (GTK_WIDGET (cell_layout));
4638 if (GTK_IS_MENU_ITEM (parent) &&
4639 gtk_menu_item_get_submenu (GTK_MENU_ITEM (parent)))
4640 g_object_set (cell, "sensitive", TRUE, NULL);
4645 set_cell_data_func_recurse (GtkWidget *menu,
4646 GtkCellRenderer *cell,
4647 ComboCellInfo *info)
4651 GtkWidget *cell_view;
4653 list = gtk_container_get_children (GTK_CONTAINER (menu));
4654 for (i = list; i; i = i->next)
4656 cell_view = gtk_bin_get_child (GTK_BIN (i->data));
4657 if (GTK_IS_CELL_LAYOUT (cell_view))
4659 /* Override sensitivity for inner nodes; we don't
4660 * want menuitems with submenus to appear insensitive
4662 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (cell_view),
4664 combo_cell_data_func,
4666 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4667 if (submenu != NULL)
4668 set_cell_data_func_recurse (submenu, cell, info);
4676 gtk_combo_box_cell_layout_set_cell_data_func (GtkCellLayout *layout,
4677 GtkCellRenderer *cell,
4678 GtkCellLayoutDataFunc func,
4680 GDestroyNotify destroy)
4682 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4683 GtkComboBoxPrivate *priv = combo_box->priv;
4684 ComboCellInfo *info;
4686 info = gtk_combo_box_get_cell_info (combo_box, cell);
4687 g_return_if_fail (info != NULL);
4691 GDestroyNotify d = info->destroy;
4693 info->destroy = NULL;
4694 d (info->func_data);
4698 info->func_data = func_data;
4699 info->destroy = destroy;
4701 if (priv->cell_view)
4702 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (priv->cell_view), cell, func, func_data, NULL);
4705 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (priv->column), cell, func, func_data, NULL);
4707 if (GTK_IS_MENU (priv->popup_widget))
4708 set_cell_data_func_recurse (priv->popup_widget, cell, info);
4710 gtk_widget_queue_resize (GTK_WIDGET (combo_box));
4714 clear_attributes_recurse (GtkWidget *menu,
4715 GtkCellRenderer *cell)
4721 list = gtk_container_get_children (GTK_CONTAINER (menu));
4722 for (i = list; i; i = i->next)
4724 child = gtk_bin_get_child (GTK_BIN (i->data));
4725 if (GTK_IS_CELL_LAYOUT (child))
4726 gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (child), cell);
4728 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4729 if (submenu != NULL)
4730 clear_attributes_recurse (submenu, cell);
4737 gtk_combo_box_cell_layout_clear_attributes (GtkCellLayout *layout,
4738 GtkCellRenderer *cell)
4740 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4741 GtkComboBoxPrivate *priv;
4742 ComboCellInfo *info;
4745 priv = combo_box->priv;
4747 info = gtk_combo_box_get_cell_info (combo_box, cell);
4748 g_return_if_fail (info != NULL);
4750 list = info->attributes;
4751 while (list && list->next)
4753 g_free (list->data);
4754 list = list->next->next;
4756 g_slist_free (info->attributes);
4757 info->attributes = NULL;
4759 if (priv->cell_view)
4760 gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (priv->cell_view), cell);
4763 gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (priv->column), cell);
4765 if (GTK_IS_MENU (priv->popup_widget))
4766 clear_attributes_recurse (priv->popup_widget, cell);
4768 gtk_widget_queue_resize (GTK_WIDGET (combo_box));
4772 reorder_recurse (GtkWidget *menu,
4773 GtkCellRenderer *cell,
4780 list = gtk_container_get_children (GTK_CONTAINER (menu));
4781 for (i = list; i; i = i->next)
4783 child = gtk_bin_get_child (GTK_BIN (i->data));
4784 if (GTK_IS_CELL_LAYOUT (child))
4785 gtk_cell_layout_reorder (GTK_CELL_LAYOUT (child),
4788 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4789 if (submenu != NULL)
4790 reorder_recurse (submenu, cell, position);
4797 gtk_combo_box_cell_layout_reorder (GtkCellLayout *layout,
4798 GtkCellRenderer *cell,
4801 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4802 GtkComboBoxPrivate *priv;
4803 ComboCellInfo *info;
4806 priv = combo_box->priv;
4808 info = gtk_combo_box_get_cell_info (combo_box, cell);
4810 g_return_if_fail (info != NULL);
4811 g_return_if_fail (position >= 0);
4813 link = g_slist_find (priv->cells, info);
4815 g_return_if_fail (link != NULL);
4817 priv->cells = g_slist_delete_link (priv->cells, link);
4818 priv->cells = g_slist_insert (priv->cells, info, position);
4820 if (priv->cell_view)
4821 gtk_cell_layout_reorder (GTK_CELL_LAYOUT (priv->cell_view),
4825 gtk_cell_layout_reorder (GTK_CELL_LAYOUT (priv->column),
4828 if (GTK_IS_MENU (priv->popup_widget))
4829 reorder_recurse (priv->popup_widget, cell, position);
4831 gtk_widget_queue_draw (GTK_WIDGET (combo_box));
4839 * gtk_combo_box_new:
4841 * Creates a new empty #GtkComboBox.
4843 * Return value: A new #GtkComboBox.
4848 gtk_combo_box_new (void)
4850 return g_object_new (GTK_TYPE_COMBO_BOX, NULL);
4854 * gtk_combo_box_new_with_entry:
4856 * Creates a new empty #GtkComboBox with an entry.
4858 * Return value: A new #GtkComboBox.
4861 gtk_combo_box_new_with_entry (void)
4863 return g_object_new (GTK_TYPE_COMBO_BOX, "has-entry", TRUE, NULL);
4867 * gtk_combo_box_new_with_model:
4868 * @model: A #GtkTreeModel.
4870 * Creates a new #GtkComboBox with the model initialized to @model.
4872 * Return value: A new #GtkComboBox.
4877 gtk_combo_box_new_with_model (GtkTreeModel *model)
4879 GtkComboBox *combo_box;
4881 g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL);
4883 combo_box = g_object_new (GTK_TYPE_COMBO_BOX, "model", model, NULL);
4885 return GTK_WIDGET (combo_box);
4889 * gtk_combo_box_get_wrap_width:
4890 * @combo_box: A #GtkComboBox
4892 * Returns the wrap width which is used to determine the number of columns
4893 * for the popup menu. If the wrap width is larger than 1, the combo box
4896 * Returns: the wrap width.
4901 gtk_combo_box_get_wrap_width (GtkComboBox *combo_box)
4903 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), -1);
4905 return combo_box->priv->wrap_width;
4909 * gtk_combo_box_set_wrap_width:
4910 * @combo_box: A #GtkComboBox
4911 * @width: Preferred number of columns
4913 * Sets the wrap width of @combo_box to be @width. The wrap width is basically
4914 * the preferred number of columns when you want the popup to be layed out
4920 gtk_combo_box_set_wrap_width (GtkComboBox *combo_box,
4923 GtkComboBoxPrivate *priv;
4925 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4926 g_return_if_fail (width >= 0);
4928 priv = combo_box->priv;
4930 if (width != priv->wrap_width)
4932 priv->wrap_width = width;
4934 gtk_combo_box_check_appearance (combo_box);
4935 gtk_combo_box_relayout (combo_box);
4937 g_object_notify (G_OBJECT (combo_box), "wrap-width");
4942 * gtk_combo_box_get_row_span_column:
4943 * @combo_box: A #GtkComboBox
4945 * Returns the column with row span information for @combo_box.
4947 * Returns: the row span column.
4952 gtk_combo_box_get_row_span_column (GtkComboBox *combo_box)
4954 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), -1);
4956 return combo_box->priv->row_column;
4960 * gtk_combo_box_set_row_span_column:
4961 * @combo_box: A #GtkComboBox.
4962 * @row_span: A column in the model passed during construction.
4964 * Sets the column with row span information for @combo_box to be @row_span.
4965 * The row span column contains integers which indicate how many rows
4966 * an item should span.
4971 gtk_combo_box_set_row_span_column (GtkComboBox *combo_box,
4974 GtkComboBoxPrivate *priv;
4977 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4979 priv = combo_box->priv;
4981 col = gtk_tree_model_get_n_columns (priv->model);
4982 g_return_if_fail (row_span >= -1 && row_span < col);
4984 if (row_span != priv->row_column)
4986 priv->row_column = row_span;
4988 gtk_combo_box_relayout (combo_box);
4990 g_object_notify (G_OBJECT (combo_box), "row-span-column");
4995 * gtk_combo_box_get_column_span_column:
4996 * @combo_box: A #GtkComboBox
4998 * Returns the column with column span information for @combo_box.
5000 * Returns: the column span column.
5005 gtk_combo_box_get_column_span_column (GtkComboBox *combo_box)
5007 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), -1);
5009 return combo_box->priv->col_column;
5013 * gtk_combo_box_set_column_span_column:
5014 * @combo_box: A #GtkComboBox
5015 * @column_span: A column in the model passed during construction
5017 * Sets the column with column span information for @combo_box to be
5018 * @column_span. The column span column contains integers which indicate
5019 * how many columns an item should span.
5024 gtk_combo_box_set_column_span_column (GtkComboBox *combo_box,
5027 GtkComboBoxPrivate *priv;
5030 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5032 priv = combo_box->priv;
5034 col = gtk_tree_model_get_n_columns (priv->model);
5035 g_return_if_fail (column_span >= -1 && column_span < col);
5037 if (column_span != priv->col_column)
5039 priv->col_column = column_span;
5041 gtk_combo_box_relayout (combo_box);
5043 g_object_notify (G_OBJECT (combo_box), "column-span-column");
5048 * gtk_combo_box_get_active:
5049 * @combo_box: A #GtkComboBox
5051 * Returns the index of the currently active item, or -1 if there's no
5052 * active item. If the model is a non-flat treemodel, and the active item
5053 * is not an immediate child of the root of the tree, this function returns
5054 * <literal>gtk_tree_path_get_indices (path)[0]</literal>, where
5055 * <literal>path</literal> is the #GtkTreePath of the active item.
5057 * Return value: An integer which is the index of the currently active item,
5058 * or -1 if there's no active item.
5063 gtk_combo_box_get_active (GtkComboBox *combo_box)
5065 GtkComboBoxPrivate *priv;
5068 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), 0);
5070 priv = combo_box->priv;
5072 if (gtk_tree_row_reference_valid (priv->active_row))
5076 path = gtk_tree_row_reference_get_path (priv->active_row);
5077 result = gtk_tree_path_get_indices (path)[0];
5078 gtk_tree_path_free (path);
5087 * gtk_combo_box_set_active:
5088 * @combo_box: A #GtkComboBox
5089 * @index_: An index in the model passed during construction, or -1 to have
5092 * Sets the active item of @combo_box to be the item at @index.
5097 gtk_combo_box_set_active (GtkComboBox *combo_box,
5100 GtkTreePath *path = NULL;
5101 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5102 g_return_if_fail (index_ >= -1);
5104 if (combo_box->priv->model == NULL)
5106 /* Save index, in case the model is set after the index */
5107 combo_box->priv->active = index_;
5113 path = gtk_tree_path_new_from_indices (index_, -1);
5115 gtk_combo_box_set_active_internal (combo_box, path);
5118 gtk_tree_path_free (path);
5122 gtk_combo_box_set_active_internal (GtkComboBox *combo_box,
5125 GtkComboBoxPrivate *priv = combo_box->priv;
5126 GtkTreePath *active_path;
5129 /* Remember whether the initially active row is valid. */
5130 gboolean is_valid_row_reference = gtk_tree_row_reference_valid (priv->active_row);
5132 if (path && is_valid_row_reference)
5134 active_path = gtk_tree_row_reference_get_path (priv->active_row);
5135 path_cmp = gtk_tree_path_compare (path, active_path);
5136 gtk_tree_path_free (active_path);
5141 if (priv->active_row)
5143 gtk_tree_row_reference_free (priv->active_row);
5144 priv->active_row = NULL;
5149 if (priv->tree_view)
5150 gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view)));
5153 GtkMenu *menu = GTK_MENU (priv->popup_widget);
5155 if (GTK_IS_MENU (menu))
5156 gtk_menu_set_active (menu, -1);
5159 if (priv->cell_view)
5160 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (priv->cell_view), NULL);
5163 * Do not emit a "changed" signal when an already invalid selection was
5164 * now set to invalid.
5166 if (!is_valid_row_reference)
5172 gtk_tree_row_reference_new (priv->model, path);
5174 if (priv->tree_view)
5176 gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->tree_view),
5179 else if (GTK_IS_MENU (priv->popup_widget))
5181 /* FIXME handle nested menus better */
5182 gtk_menu_set_active (GTK_MENU (priv->popup_widget),
5183 gtk_tree_path_get_indices (path)[0]);
5186 if (priv->cell_view)
5187 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (priv->cell_view),
5191 g_signal_emit (combo_box, combo_box_signals[CHANGED], 0);
5192 g_object_notify (G_OBJECT (combo_box), "active");
5197 * gtk_combo_box_get_active_iter:
5198 * @combo_box: A #GtkComboBox
5199 * @iter: (out): The uninitialized #GtkTreeIter
5201 * Sets @iter to point to the current active item, if it exists.
5203 * Return value: %TRUE, if @iter was set
5208 gtk_combo_box_get_active_iter (GtkComboBox *combo_box,
5214 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
5216 if (!gtk_tree_row_reference_valid (combo_box->priv->active_row))
5219 path = gtk_tree_row_reference_get_path (combo_box->priv->active_row);
5220 result = gtk_tree_model_get_iter (combo_box->priv->model, iter, path);
5221 gtk_tree_path_free (path);
5227 * gtk_combo_box_set_active_iter:
5228 * @combo_box: A #GtkComboBox
5229 * @iter: (allow-none): The #GtkTreeIter, or %NULL
5231 * Sets the current active item to be the one referenced by @iter, or
5232 * unsets the active item if @iter is %NULL.
5237 gtk_combo_box_set_active_iter (GtkComboBox *combo_box,
5240 GtkTreePath *path = NULL;
5242 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5245 path = gtk_tree_model_get_path (gtk_combo_box_get_model (combo_box), iter);
5247 gtk_combo_box_set_active_internal (combo_box, path);
5248 gtk_tree_path_free (path);
5252 * gtk_combo_box_set_model:
5253 * @combo_box: A #GtkComboBox
5254 * @model: (allow-none): A #GtkTreeModel
5256 * Sets the model used by @combo_box to be @model. Will unset a previously set
5257 * model (if applicable). If model is %NULL, then it will unset the model.
5259 * Note that this function does not clear the cell renderers, you have to
5260 * call gtk_cell_layout_clear() yourself if you need to set up different
5261 * cell renderers for the new model.
5266 gtk_combo_box_set_model (GtkComboBox *combo_box,
5267 GtkTreeModel *model)
5269 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5270 g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
5272 if (model == combo_box->priv->model)
5275 gtk_combo_box_unset_model (combo_box);
5280 combo_box->priv->model = model;
5281 g_object_ref (combo_box->priv->model);
5283 combo_box->priv->inserted_id =
5284 g_signal_connect (combo_box->priv->model, "row-inserted",
5285 G_CALLBACK (gtk_combo_box_model_row_inserted),
5287 combo_box->priv->deleted_id =
5288 g_signal_connect (combo_box->priv->model, "row-deleted",
5289 G_CALLBACK (gtk_combo_box_model_row_deleted),
5291 combo_box->priv->reordered_id =
5292 g_signal_connect (combo_box->priv->model, "rows-reordered",
5293 G_CALLBACK (gtk_combo_box_model_rows_reordered),
5295 combo_box->priv->changed_id =
5296 g_signal_connect (combo_box->priv->model, "row-changed",
5297 G_CALLBACK (gtk_combo_box_model_row_changed),
5300 if (combo_box->priv->tree_view)
5303 gtk_tree_view_set_model (GTK_TREE_VIEW (combo_box->priv->tree_view),
5304 combo_box->priv->model);
5305 gtk_combo_box_list_popup_resize (combo_box);
5310 if (combo_box->priv->popup_widget)
5311 gtk_combo_box_menu_fill (combo_box);
5315 if (combo_box->priv->cell_view)
5316 gtk_cell_view_set_model (GTK_CELL_VIEW (combo_box->priv->cell_view),
5317 combo_box->priv->model);
5319 if (combo_box->priv->active != -1)
5321 /* If an index was set in advance, apply it now */
5322 gtk_combo_box_set_active (combo_box, combo_box->priv->active);
5323 combo_box->priv->active = -1;
5327 gtk_combo_box_update_sensitivity (combo_box);
5329 g_object_notify (G_OBJECT (combo_box), "model");
5333 * gtk_combo_box_get_model:
5334 * @combo_box: A #GtkComboBox
5336 * Returns the #GtkTreeModel which is acting as data source for @combo_box.
5338 * Return value: (transfer none): A #GtkTreeModel which was passed
5339 * during construction.
5344 gtk_combo_box_get_model (GtkComboBox *combo_box)
5346 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
5348 return combo_box->priv->model;
5352 /* convenience API for simple text combos */
5355 * gtk_combo_box_new_text:
5357 * Convenience function which constructs a new text combo box, which is a
5358 * #GtkComboBox just displaying strings. If you use this function to create
5359 * a text combo box, you should only manipulate its data source with the
5360 * following convenience functions: gtk_combo_box_append_text(),
5361 * gtk_combo_box_insert_text(), gtk_combo_box_prepend_text() and
5362 * gtk_combo_box_remove_text().
5364 * Return value: (transfer none): A new text combo box.
5368 * Deprecated: 2.24: Use #GtkComboBoxText
5371 gtk_combo_box_new_text (void)
5373 GtkWidget *combo_box;
5374 GtkCellRenderer *cell;
5375 GtkListStore *store;
5377 store = gtk_list_store_new (1, G_TYPE_STRING);
5378 combo_box = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store));
5379 g_object_unref (store);
5381 cell = gtk_cell_renderer_text_new ();
5382 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), cell, TRUE);
5383 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), cell,
5391 * gtk_combo_box_append_text:
5392 * @combo_box: A #GtkComboBox constructed using gtk_combo_box_new_text()
5395 * Appends @string to the list of strings stored in @combo_box. Note that
5396 * you can only use this function with combo boxes constructed with
5397 * gtk_combo_box_new_text().
5401 * Deprecated: 2.24: Use #GtkComboBoxText
5404 gtk_combo_box_append_text (GtkComboBox *combo_box,
5408 GtkListStore *store;
5410 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5411 g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model));
5412 g_return_if_fail (gtk_tree_model_get_column_type (combo_box->priv->model, 0)
5414 g_return_if_fail (text != NULL);
5416 store = GTK_LIST_STORE (combo_box->priv->model);
5418 gtk_list_store_append (store, &iter);
5419 gtk_list_store_set (store, &iter, 0, text, -1);
5423 * gtk_combo_box_insert_text:
5424 * @combo_box: A #GtkComboBox constructed using gtk_combo_box_new_text()
5425 * @position: An index to insert @text
5428 * Inserts @string at @position in the list of strings stored in @combo_box.
5429 * Note that you can only use this function with combo boxes constructed
5430 * with gtk_combo_box_new_text().
5434 * Deprecated: 2.24: Use #GtkComboBoxText
5437 gtk_combo_box_insert_text (GtkComboBox *combo_box,
5442 GtkListStore *store;
5444 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5445 g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model));
5446 g_return_if_fail (position >= 0);
5447 g_return_if_fail (gtk_tree_model_get_column_type (combo_box->priv->model, 0)
5449 g_return_if_fail (text != NULL);
5451 store = GTK_LIST_STORE (combo_box->priv->model);
5453 gtk_list_store_insert (store, &iter, position);
5454 gtk_list_store_set (store, &iter, 0, text, -1);
5458 * gtk_combo_box_prepend_text:
5459 * @combo_box: A #GtkComboBox constructed with gtk_combo_box_new_text()
5462 * Prepends @string to the list of strings stored in @combo_box. Note that
5463 * you can only use this function with combo boxes constructed with
5464 * gtk_combo_box_new_text().
5468 * Deprecated: 2.24: Use #GtkComboBoxText
5471 gtk_combo_box_prepend_text (GtkComboBox *combo_box,
5475 GtkListStore *store;
5477 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5478 g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model));
5479 g_return_if_fail (gtk_tree_model_get_column_type (combo_box->priv->model, 0)
5481 g_return_if_fail (text != NULL);
5483 store = GTK_LIST_STORE (combo_box->priv->model);
5485 gtk_list_store_prepend (store, &iter);
5486 gtk_list_store_set (store, &iter, 0, text, -1);
5490 * gtk_combo_box_remove_text:
5491 * @combo_box: A #GtkComboBox constructed with gtk_combo_box_new_text()
5492 * @position: Index of the item to remove
5494 * Removes the string at @position from @combo_box. Note that you can only use
5495 * this function with combo boxes constructed with gtk_combo_box_new_text().
5499 * Deprecated: 2.24: Use #GtkComboBoxText
5502 gtk_combo_box_remove_text (GtkComboBox *combo_box,
5506 GtkListStore *store;
5508 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5509 g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model));
5510 g_return_if_fail (gtk_tree_model_get_column_type (combo_box->priv->model, 0)
5512 g_return_if_fail (position >= 0);
5514 store = GTK_LIST_STORE (combo_box->priv->model);
5516 if (gtk_tree_model_iter_nth_child (combo_box->priv->model, &iter,
5518 gtk_list_store_remove (store, &iter);
5522 * gtk_combo_box_get_active_text:
5523 * @combo_box: A #GtkComboBox constructed with gtk_combo_box_new_text()
5525 * Returns the currently active string in @combo_box or %NULL if none
5528 * Returns: a newly allocated string containing the currently active text.
5529 * Must be freed with g_free().
5533 * Deprecated: 2.24: Use #GtkComboBoxText
5536 gtk_combo_box_get_active_text (GtkComboBox *combo_box)
5538 GtkComboBoxClass *class;
5540 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
5542 class = GTK_COMBO_BOX_GET_CLASS (combo_box);
5544 if (class->get_active_text)
5545 return class->get_active_text (combo_box);
5551 gtk_combo_box_real_get_active_text (GtkComboBox *combo_box)
5556 if (combo_box->priv->has_entry)
5558 GtkBin *combo = GTK_BIN (combo_box);
5561 child = gtk_bin_get_child (combo);
5563 return g_strdup (gtk_entry_get_text (GTK_ENTRY (child)));
5569 g_return_val_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model), NULL);
5570 g_return_val_if_fail (gtk_tree_model_get_column_type (combo_box->priv->model, 0)
5571 == G_TYPE_STRING, NULL);
5573 if (gtk_combo_box_get_active_iter (combo_box, &iter))
5574 gtk_tree_model_get (combo_box->priv->model, &iter,
5582 gtk_combo_box_real_move_active (GtkComboBox *combo_box,
5583 GtkScrollType scroll)
5586 GtkTreeIter new_iter;
5587 gboolean active_iter;
5590 if (!combo_box->priv->model)
5592 gtk_widget_error_bell (GTK_WIDGET (combo_box));
5596 active_iter = gtk_combo_box_get_active_iter (combo_box, &iter);
5600 case GTK_SCROLL_STEP_BACKWARD:
5601 case GTK_SCROLL_STEP_UP:
5602 case GTK_SCROLL_STEP_LEFT:
5605 found = tree_prev (combo_box, combo_box->priv->model,
5606 &iter, &new_iter, FALSE);
5609 /* else fall through */
5611 case GTK_SCROLL_PAGE_FORWARD:
5612 case GTK_SCROLL_PAGE_DOWN:
5613 case GTK_SCROLL_PAGE_RIGHT:
5614 case GTK_SCROLL_END:
5615 found = tree_last (combo_box, combo_box->priv->model, &new_iter, FALSE);
5618 case GTK_SCROLL_STEP_FORWARD:
5619 case GTK_SCROLL_STEP_DOWN:
5620 case GTK_SCROLL_STEP_RIGHT:
5623 found = tree_next (combo_box, combo_box->priv->model,
5624 &iter, &new_iter, FALSE);
5627 /* else fall through */
5629 case GTK_SCROLL_PAGE_BACKWARD:
5630 case GTK_SCROLL_PAGE_UP:
5631 case GTK_SCROLL_PAGE_LEFT:
5632 case GTK_SCROLL_START:
5633 found = tree_first (combo_box, combo_box->priv->model, &new_iter, FALSE);
5640 if (found && active_iter)
5642 GtkTreePath *old_path;
5643 GtkTreePath *new_path;
5645 old_path = gtk_tree_model_get_path (combo_box->priv->model, &iter);
5646 new_path = gtk_tree_model_get_path (combo_box->priv->model, &new_iter);
5648 if (gtk_tree_path_compare (old_path, new_path) == 0)
5651 gtk_tree_path_free (old_path);
5652 gtk_tree_path_free (new_path);
5657 gtk_combo_box_set_active_iter (combo_box, &new_iter);
5661 gtk_widget_error_bell (GTK_WIDGET (combo_box));
5666 gtk_combo_box_mnemonic_activate (GtkWidget *widget,
5667 gboolean group_cycling)
5669 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
5671 if (combo_box->priv->has_entry)
5675 child = gtk_bin_get_child (GTK_BIN (combo_box));
5677 gtk_widget_grab_focus (child);
5680 gtk_widget_grab_focus (combo_box->priv->button);
5686 gtk_combo_box_grab_focus (GtkWidget *widget)
5688 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
5690 if (combo_box->priv->has_entry)
5694 child = gtk_bin_get_child (GTK_BIN (combo_box));
5696 gtk_widget_grab_focus (child);
5699 gtk_widget_grab_focus (combo_box->priv->button);
5703 gtk_combo_box_destroy (GtkWidget *widget)
5705 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
5707 if (combo_box->priv->popup_idle_id > 0)
5709 g_source_remove (combo_box->priv->popup_idle_id);
5710 combo_box->priv->popup_idle_id = 0;
5713 gtk_combo_box_popdown (combo_box);
5715 if (combo_box->priv->row_separator_destroy)
5716 combo_box->priv->row_separator_destroy (combo_box->priv->row_separator_data);
5718 combo_box->priv->row_separator_func = NULL;
5719 combo_box->priv->row_separator_data = NULL;
5720 combo_box->priv->row_separator_destroy = NULL;
5722 GTK_WIDGET_CLASS (gtk_combo_box_parent_class)->destroy (widget);
5723 combo_box->priv->cell_view = NULL;
5727 gtk_combo_box_entry_contents_changed (GtkEntry *entry,
5730 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
5733 * Fixes regression reported in bug #574059. The old functionality relied on
5734 * bug #572478. As a bugfix, we now emit the "changed" signal ourselves
5735 * when the selection was already set to -1.
5737 if (gtk_combo_box_get_active(combo_box) == -1)
5738 g_signal_emit_by_name (combo_box, "changed");
5740 gtk_combo_box_set_active (combo_box, -1);
5744 gtk_combo_box_entry_active_changed (GtkComboBox *combo_box,
5747 GtkComboBoxPrivate *priv = combo_box->priv;
5748 GtkTreeModel *model;
5751 if (gtk_combo_box_get_active_iter (combo_box, &iter))
5753 GtkEntry *entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (combo_box)));
5757 GValue value = {0,};
5759 g_signal_handlers_block_by_func (entry,
5760 gtk_combo_box_entry_contents_changed,
5763 model = gtk_combo_box_get_model (combo_box);
5765 gtk_tree_model_get_value (model, &iter,
5766 priv->text_column, &value);
5767 g_object_set_property (G_OBJECT (entry), "text", &value);
5768 g_value_unset (&value);
5770 g_signal_handlers_unblock_by_func (entry,
5771 gtk_combo_box_entry_contents_changed,
5778 gtk_combo_box_constructor (GType type,
5779 guint n_construct_properties,
5780 GObjectConstructParam *construct_properties)
5783 GtkComboBox *combo_box;
5784 GtkComboBoxPrivate *priv;
5786 object = G_OBJECT_CLASS (gtk_combo_box_parent_class)->constructor
5787 (type, n_construct_properties, construct_properties);
5789 combo_box = GTK_COMBO_BOX (object);
5790 priv = combo_box->priv;
5792 if (priv->has_entry)
5796 entry = gtk_entry_new ();
5797 gtk_widget_show (entry);
5798 gtk_container_add (GTK_CONTAINER (combo_box), entry);
5800 priv->text_renderer = gtk_cell_renderer_text_new ();
5801 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box),
5802 priv->text_renderer, TRUE);
5804 gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), -1);
5806 g_signal_connect (combo_box, "changed",
5807 G_CALLBACK (gtk_combo_box_entry_active_changed), NULL);
5815 gtk_combo_box_dispose(GObject* object)
5817 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
5819 if (GTK_IS_MENU (combo_box->priv->popup_widget))
5821 gtk_combo_box_menu_destroy (combo_box);
5822 gtk_menu_detach (GTK_MENU (combo_box->priv->popup_widget));
5823 combo_box->priv->popup_widget = NULL;
5826 G_OBJECT_CLASS (gtk_combo_box_parent_class)->dispose (object);
5830 gtk_combo_box_finalize (GObject *object)
5832 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
5835 if (GTK_IS_TREE_VIEW (combo_box->priv->tree_view))
5836 gtk_combo_box_list_destroy (combo_box);
5838 if (combo_box->priv->popup_window)
5839 gtk_widget_destroy (combo_box->priv->popup_window);
5841 gtk_combo_box_unset_model (combo_box);
5843 for (i = combo_box->priv->cells; i; i = i->next)
5845 ComboCellInfo *info = (ComboCellInfo *)i->data;
5846 GSList *list = info->attributes;
5849 info->destroy (info->func_data);
5851 while (list && list->next)
5853 g_free (list->data);
5854 list = list->next->next;
5856 g_slist_free (info->attributes);
5858 g_object_unref (info->cell);
5859 g_slice_free (ComboCellInfo, info);
5861 g_slist_free (combo_box->priv->cells);
5863 g_free (combo_box->priv->tearoff_title);
5865 G_OBJECT_CLASS (gtk_combo_box_parent_class)->finalize (object);
5870 gtk_cell_editable_key_press (GtkWidget *widget,
5874 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
5876 if (event->keyval == GDK_KEY_Escape)
5878 g_object_set (combo_box,
5879 "editing-canceled", TRUE,
5881 gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (combo_box));
5882 gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (combo_box));
5886 else if (event->keyval == GDK_KEY_Return ||
5887 event->keyval == GDK_KEY_ISO_Enter ||
5888 event->keyval == GDK_KEY_KP_Enter)
5890 gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (combo_box));
5891 gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (combo_box));
5900 popdown_idle (gpointer data)
5902 GtkComboBox *combo_box;
5904 combo_box = GTK_COMBO_BOX (data);
5906 gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (combo_box));
5907 gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (combo_box));
5909 g_object_unref (combo_box);
5915 popdown_handler (GtkWidget *widget,
5918 gdk_threads_add_idle (popdown_idle, g_object_ref (data));
5922 popup_idle (gpointer data)
5924 GtkComboBox *combo_box;
5926 combo_box = GTK_COMBO_BOX (data);
5928 if (GTK_IS_MENU (combo_box->priv->popup_widget) &&
5929 combo_box->priv->cell_view)
5930 g_signal_connect_object (combo_box->priv->popup_widget,
5931 "unmap", G_CALLBACK (popdown_handler),
5934 /* we unset this if a menu item is activated */
5935 g_object_set (combo_box,
5936 "editing-canceled", TRUE,
5938 gtk_combo_box_popup (combo_box);
5940 combo_box->priv->popup_idle_id = 0;
5941 combo_box->priv->activate_button = 0;
5942 combo_box->priv->activate_time = 0;
5948 gtk_combo_box_start_editing (GtkCellEditable *cell_editable,
5951 GtkComboBox *combo_box = GTK_COMBO_BOX (cell_editable);
5954 combo_box->priv->is_cell_renderer = TRUE;
5956 if (combo_box->priv->cell_view)
5958 g_signal_connect_object (combo_box->priv->button, "key-press-event",
5959 G_CALLBACK (gtk_cell_editable_key_press),
5962 gtk_widget_grab_focus (combo_box->priv->button);
5966 child = gtk_bin_get_child (GTK_BIN (combo_box));
5968 g_signal_connect_object (child, "key-press-event",
5969 G_CALLBACK (gtk_cell_editable_key_press),
5972 gtk_widget_grab_focus (child);
5973 gtk_widget_set_can_focus (combo_box->priv->button, FALSE);
5976 /* we do the immediate popup only for the optionmenu-like
5979 if (combo_box->priv->is_cell_renderer &&
5980 combo_box->priv->cell_view && !combo_box->priv->tree_view)
5982 if (event && event->type == GDK_BUTTON_PRESS)
5984 GdkEventButton *event_button = (GdkEventButton *)event;
5986 combo_box->priv->activate_button = event_button->button;
5987 combo_box->priv->activate_time = event_button->time;
5990 combo_box->priv->popup_idle_id =
5991 gdk_threads_add_idle (popup_idle, combo_box);
5997 * gtk_combo_box_get_add_tearoffs:
5998 * @combo_box: a #GtkComboBox
6000 * Gets the current value of the :add-tearoffs property.
6002 * Return value: the current value of the :add-tearoffs property.
6005 gtk_combo_box_get_add_tearoffs (GtkComboBox *combo_box)
6007 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
6009 return combo_box->priv->add_tearoffs;
6013 * gtk_combo_box_set_add_tearoffs:
6014 * @combo_box: a #GtkComboBox
6015 * @add_tearoffs: %TRUE to add tearoff menu items
6017 * Sets whether the popup menu should have a tearoff
6023 gtk_combo_box_set_add_tearoffs (GtkComboBox *combo_box,
6024 gboolean add_tearoffs)
6026 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
6028 add_tearoffs = add_tearoffs != FALSE;
6030 if (combo_box->priv->add_tearoffs != add_tearoffs)
6032 combo_box->priv->add_tearoffs = add_tearoffs;
6033 gtk_combo_box_check_appearance (combo_box);
6034 gtk_combo_box_relayout (combo_box);
6035 g_object_notify (G_OBJECT (combo_box), "add-tearoffs");
6040 * gtk_combo_box_get_title:
6041 * @combo_box: a #GtkComboBox
6043 * Gets the current title of the menu in tearoff mode. See
6044 * gtk_combo_box_set_add_tearoffs().
6046 * Returns: the menu's title in tearoff mode. This is an internal copy of the
6047 * string which must not be freed.
6051 G_CONST_RETURN gchar*
6052 gtk_combo_box_get_title (GtkComboBox *combo_box)
6054 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
6056 return combo_box->priv->tearoff_title;
6060 gtk_combo_box_update_title (GtkComboBox *combo_box)
6062 gtk_combo_box_check_appearance (combo_box);
6064 if (combo_box->priv->popup_widget &&
6065 GTK_IS_MENU (combo_box->priv->popup_widget))
6066 gtk_menu_set_title (GTK_MENU (combo_box->priv->popup_widget),
6067 combo_box->priv->tearoff_title);
6071 * gtk_combo_box_set_title:
6072 * @combo_box: a #GtkComboBox
6073 * @title: a title for the menu in tearoff mode
6075 * Sets the menu's title in tearoff mode.
6080 gtk_combo_box_set_title (GtkComboBox *combo_box,
6083 GtkComboBoxPrivate *priv;
6085 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
6087 priv = combo_box->priv;
6089 if (strcmp (title ? title : "",
6090 priv->tearoff_title ? priv->tearoff_title : "") != 0)
6092 g_free (priv->tearoff_title);
6093 priv->tearoff_title = g_strdup (title);
6095 gtk_combo_box_update_title (combo_box);
6097 g_object_notify (G_OBJECT (combo_box), "tearoff-title");
6103 * gtk_combo_box_set_popup_fixed_width:
6104 * @combo_box: a #GtkComboBox
6105 * @fixed: whether to use a fixed popup width
6107 * Specifies whether the popup's width should be a fixed width matching
6108 * the allocated width of the combo box.
6113 gtk_combo_box_set_popup_fixed_width (GtkComboBox *combo_box,
6116 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
6118 if (combo_box->priv->popup_fixed_width != fixed)
6120 combo_box->priv->popup_fixed_width = fixed;
6122 g_object_notify (G_OBJECT (combo_box), "popup-fixed-width");
6127 * gtk_combo_box_get_popup_fixed_width:
6128 * @combo_box: a #GtkComboBox
6130 * Gets whether the popup uses a fixed width matching
6131 * the allocated width of the combo box.
6136 gtk_combo_box_get_popup_fixed_width (GtkComboBox *combo_box)
6138 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
6140 return combo_box->priv->popup_fixed_width;
6145 * gtk_combo_box_get_popup_accessible:
6146 * @combo_box: a #GtkComboBox
6148 * Gets the accessible object corresponding to the combo box's popup.
6150 * This function is mostly intended for use by accessibility technologies;
6151 * applications should have little use for it.
6153 * Returns: (transfer none): the accessible object corresponding
6154 * to the combo box's popup.
6159 gtk_combo_box_get_popup_accessible (GtkComboBox *combo_box)
6163 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
6165 if (combo_box->priv->popup_widget)
6167 atk_obj = gtk_widget_get_accessible (combo_box->priv->popup_widget);
6175 * gtk_combo_box_get_row_separator_func:
6176 * @combo_box: a #GtkComboBox
6178 * Returns the current row separator function.
6180 * Return value: the current row separator function.
6184 GtkTreeViewRowSeparatorFunc
6185 gtk_combo_box_get_row_separator_func (GtkComboBox *combo_box)
6187 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
6189 return combo_box->priv->row_separator_func;
6193 * gtk_combo_box_set_row_separator_func:
6194 * @combo_box: a #GtkComboBox
6195 * @func: a #GtkTreeViewRowSeparatorFunc
6196 * @data: (allow-none): user data to pass to @func, or %NULL
6197 * @destroy: (allow-none): destroy notifier for @data, or %NULL
6199 * Sets the row separator function, which is used to determine
6200 * whether a row should be drawn as a separator. If the row separator
6201 * function is %NULL, no separators are drawn. This is the default value.
6206 gtk_combo_box_set_row_separator_func (GtkComboBox *combo_box,
6207 GtkTreeViewRowSeparatorFunc func,
6209 GDestroyNotify destroy)
6211 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
6213 if (combo_box->priv->row_separator_destroy)
6214 combo_box->priv->row_separator_destroy (combo_box->priv->row_separator_data);
6216 combo_box->priv->row_separator_func = func;
6217 combo_box->priv->row_separator_data = data;
6218 combo_box->priv->row_separator_destroy = destroy;
6220 if (combo_box->priv->tree_view)
6221 gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (combo_box->priv->tree_view),
6224 gtk_combo_box_relayout (combo_box);
6226 gtk_widget_queue_draw (GTK_WIDGET (combo_box));
6230 * gtk_combo_box_set_button_sensitivity:
6231 * @combo_box: a #GtkComboBox
6232 * @sensitivity: specify the sensitivity of the dropdown button
6234 * Sets whether the dropdown button of the combo box should be
6235 * always sensitive (%GTK_SENSITIVITY_ON), never sensitive (%GTK_SENSITIVITY_OFF)
6236 * or only if there is at least one item to display (%GTK_SENSITIVITY_AUTO).
6241 gtk_combo_box_set_button_sensitivity (GtkComboBox *combo_box,
6242 GtkSensitivityType sensitivity)
6244 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
6246 if (combo_box->priv->button_sensitivity != sensitivity)
6248 combo_box->priv->button_sensitivity = sensitivity;
6249 gtk_combo_box_update_sensitivity (combo_box);
6251 g_object_notify (G_OBJECT (combo_box), "button-sensitivity");
6256 * gtk_combo_box_get_button_sensitivity:
6257 * @combo_box: a #GtkComboBox
6259 * Returns whether the combo box sets the dropdown button
6260 * sensitive or not when there are no items in the model.
6262 * Return Value: %GTK_SENSITIVITY_ON if the dropdown button
6263 * is sensitive when the model is empty, %GTK_SENSITIVITY_OFF
6264 * if the button is always insensitive or
6265 * %GTK_SENSITIVITY_AUTO if it is only sensitive as long as
6266 * the model has one item to be selected.
6271 gtk_combo_box_get_button_sensitivity (GtkComboBox *combo_box)
6273 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
6275 return combo_box->priv->button_sensitivity;
6280 * gtk_combo_box_get_has_entry:
6281 * @combo_box: a #GtkComboBox
6283 * Returns whether the combo box has an entry.
6285 * Return Value: whether there is an entry in @combo_box.
6290 gtk_combo_box_get_has_entry (GtkComboBox *combo_box)
6292 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
6294 return combo_box->priv->has_entry;
6298 * gtk_combo_box_set_entry_text_column:
6299 * @combo_box: A #GtkComboBox
6300 * @text_column: A column in @model to get the strings from for
6301 * the internal entry
6303 * Sets the model column which @combo_box should use to get strings from
6304 * to be @text_column. The column @text_column in the model of @combo_box
6305 * must be of type %G_TYPE_STRING.
6307 * This is only relevant if @combo_box has been created with
6308 * #GtkComboBox:has-entry as %TRUE.
6313 gtk_combo_box_set_entry_text_column (GtkComboBox *combo_box,
6316 GtkComboBoxPrivate *priv = combo_box->priv;
6317 GtkTreeModel *model;
6319 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
6321 model = gtk_combo_box_get_model (combo_box);
6323 g_return_if_fail (text_column >= 0);
6324 g_return_if_fail (model == NULL || text_column < gtk_tree_model_get_n_columns (model));
6326 priv->text_column = text_column;
6328 if (priv->text_renderer != NULL)
6329 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box),
6330 priv->text_renderer,
6331 "text", text_column,
6336 * gtk_combo_box_get_entry_text_column:
6337 * @combo_box: A #GtkComboBox.
6339 * Returns the column which @combo_box is using to get the strings
6340 * from to display in the internal entry.
6342 * Return value: A column in the data source model of @combo_box.
6347 gtk_combo_box_get_entry_text_column (GtkComboBox *combo_box)
6349 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), 0);
6351 return combo_box->priv->text_column;
6355 * gtk_combo_box_set_focus_on_click:
6356 * @combo: a #GtkComboBox
6357 * @focus_on_click: whether the combo box grabs focus when clicked
6360 * Sets whether the combo box will grab focus when it is clicked with
6361 * the mouse. Making mouse clicks not grab focus is useful in places
6362 * like toolbars where you don't want the keyboard focus removed from
6363 * the main area of the application.
6368 gtk_combo_box_set_focus_on_click (GtkComboBox *combo_box,
6369 gboolean focus_on_click)
6371 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
6373 focus_on_click = focus_on_click != FALSE;
6375 if (combo_box->priv->focus_on_click != focus_on_click)
6377 combo_box->priv->focus_on_click = focus_on_click;
6379 if (combo_box->priv->button)
6380 gtk_button_set_focus_on_click (GTK_BUTTON (combo_box->priv->button),
6383 g_object_notify (G_OBJECT (combo_box), "focus-on-click");
6388 * gtk_combo_box_get_focus_on_click:
6389 * @combo: a #GtkComboBox
6391 * Returns whether the combo box grabs focus when it is clicked
6392 * with the mouse. See gtk_combo_box_set_focus_on_click().
6394 * Return value: %TRUE if the combo box grabs focus when it is
6395 * clicked with the mouse.
6400 gtk_combo_box_get_focus_on_click (GtkComboBox *combo_box)
6402 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
6404 return combo_box->priv->focus_on_click;
6409 gtk_combo_box_buildable_custom_tag_start (GtkBuildable *buildable,
6410 GtkBuilder *builder,
6412 const gchar *tagname,
6413 GMarkupParser *parser,
6416 if (parent_buildable_iface->custom_tag_start (buildable, builder, child,
6417 tagname, parser, data))
6420 return _gtk_cell_layout_buildable_custom_tag_start (buildable, builder, child,
6421 tagname, parser, data);
6425 gtk_combo_box_buildable_custom_tag_end (GtkBuildable *buildable,
6426 GtkBuilder *builder,
6428 const gchar *tagname,
6431 if (strcmp (tagname, "attributes") == 0)
6432 _gtk_cell_layout_buildable_custom_tag_end (buildable, builder, child, tagname,
6435 parent_buildable_iface->custom_tag_end (buildable, builder, child, tagname,
6440 gtk_combo_box_buildable_get_internal_child (GtkBuildable *buildable,
6441 GtkBuilder *builder,
6442 const gchar *childname)
6444 GtkComboBox *combo_box = GTK_COMBO_BOX (buildable);
6446 if (combo_box->priv->has_entry && strcmp (childname, "entry") == 0)
6447 return G_OBJECT (gtk_bin_get_child (GTK_BIN (buildable)));
6449 return parent_buildable_iface->get_internal_child (buildable, builder, childname);
6453 gtk_combo_box_remeasure (GtkComboBox *combo_box)
6455 GtkComboBoxPrivate *priv = combo_box->priv;
6460 !gtk_tree_model_get_iter_first (priv->model, &iter))
6463 priv->minimum_width = priv->natural_width = 0;
6465 path = gtk_tree_path_new_from_indices (0, -1);
6469 gint row_min = 0, row_nat = 0;
6471 if (priv->cell_view)
6472 gtk_cell_view_get_desired_width_of_row (GTK_CELL_VIEW (priv->cell_view),
6473 path, &row_min, &row_nat);
6475 priv->minimum_width = MAX (priv->minimum_width, row_min);
6476 priv->natural_width = MAX (priv->natural_width, row_nat);
6478 gtk_tree_path_next (path);
6480 while (gtk_tree_model_iter_next (priv->model, &iter));
6482 gtk_tree_path_free (path);
6487 gtk_combo_box_measure_height_for_width (GtkComboBox *combo_box,
6493 GtkComboBoxPrivate *priv = combo_box->priv;
6496 gint child_min, child_nat;
6498 child = gtk_bin_get_child (GTK_BIN (combo_box));
6500 gtk_widget_get_preferred_height_for_width (child, avail_width,
6501 &child_min, &child_nat);
6504 !gtk_tree_model_get_iter_first (priv->model, &iter))
6507 path = gtk_tree_path_new_from_indices (0, -1);
6511 gint row_min = 0, row_nat = 0;
6513 if (priv->cell_view)
6514 gtk_cell_view_get_desired_height_for_width_of_row (GTK_CELL_VIEW (priv->cell_view),
6516 &row_min, &row_nat);
6518 child_min = MAX (child_min, row_min);
6519 child_nat = MAX (child_nat, row_nat);
6521 gtk_tree_path_next (path);
6523 while (gtk_tree_model_iter_next (priv->model, &iter));
6525 gtk_tree_path_free (path);
6530 *min_height = child_min;
6532 *nat_height = child_nat;
6537 gtk_combo_box_get_preferred_width (GtkWidget *widget,
6541 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
6542 GtkComboBoxPrivate *priv = combo_box->priv;
6544 gint focus_width, focus_pad;
6545 gint font_size, arrow_size;
6546 PangoContext *context;
6547 PangoFontMetrics *metrics;
6548 PangoFontDescription *font_desc;
6550 gint minimum_width, natural_width;
6551 gint child_min, child_nat;
6553 child = gtk_bin_get_child (GTK_BIN (widget));
6556 gtk_widget_get_preferred_width (child, &child_min, &child_nat);
6557 gtk_combo_box_remeasure (combo_box);
6559 child_min = MAX (child_min, priv->minimum_width);
6560 child_nat = MAX (child_nat, priv->natural_width);
6562 gtk_widget_style_get (GTK_WIDGET (widget),
6563 "focus-line-width", &focus_width,
6564 "focus-padding", &focus_pad,
6565 "arrow-size", &arrow_size,
6568 font_desc = gtk_widget_get_style (child)->font_desc;
6569 context = gtk_widget_get_pango_context (GTK_WIDGET (widget));
6570 metrics = pango_context_get_metrics (context, font_desc,
6571 pango_context_get_language (context));
6572 font_size = PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) +
6573 pango_font_metrics_get_descent (metrics));
6574 pango_font_metrics_unref (metrics);
6576 arrow_size = MAX (arrow_size, font_size);
6578 gtk_widget_set_size_request (priv->arrow, arrow_size, arrow_size);
6580 if (!priv->tree_view)
6584 if (priv->cell_view)
6586 gint sep_width, arrow_width;
6587 gint border_width, xthickness, xpad;
6589 border_width = gtk_container_get_border_width (GTK_CONTAINER (combo_box));
6590 xthickness = gtk_widget_get_style (priv->button)->xthickness;
6592 gtk_widget_get_preferred_width (priv->separator, &sep_width, NULL);
6593 gtk_widget_get_preferred_width (priv->arrow, &arrow_width, NULL);
6595 xpad = 2*(border_width + xthickness + focus_width + focus_pad);
6597 minimum_width = child_min + sep_width + arrow_width + xpad;
6598 natural_width = child_nat + sep_width + arrow_width + xpad;
6602 gint but_width, but_nat_width;
6604 gtk_widget_get_preferred_width (priv->button,
6605 &but_width, &but_nat_width);
6607 minimum_width = child_min + but_width;
6608 natural_width = child_nat + but_nat_width;
6614 gint button_width, button_nat_width;
6616 /* sample + frame */
6617 minimum_width = child_min;
6618 natural_width = child_nat;
6620 minimum_width += 2 * focus_width;
6621 natural_width += 2 * focus_width;
6623 if (priv->cell_view_frame)
6625 if (priv->has_frame)
6627 gint border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->cell_view_frame));
6628 gint xpad = 2 * (border_width + gtk_widget_get_style (GTK_WIDGET (priv->cell_view_frame))->xthickness);
6630 minimum_width += xpad;
6631 natural_width += xpad;
6636 gtk_widget_get_preferred_width (priv->button,
6637 &button_width, &button_nat_width);
6639 minimum_width += button_width;
6640 natural_width += button_nat_width;
6643 if (GTK_SHADOW_NONE != priv->shadow_type)
6645 style = gtk_widget_get_style (GTK_WIDGET (widget));
6647 minimum_width += 2 * style->xthickness;
6648 natural_width += 2 * style->xthickness;
6652 *minimum_size = minimum_width;
6655 *natural_size = natural_width;
6659 gtk_combo_box_get_preferred_height (GtkWidget *widget,
6665 /* Combo box is height-for-width only
6666 * (so we always just reserve enough height for the minimum width) */
6667 GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, &min_width, NULL);
6668 GTK_WIDGET_GET_CLASS (widget)->get_preferred_height_for_width (widget, min_width, minimum_size, natural_size);
6672 gtk_combo_box_get_preferred_width_for_height (GtkWidget *widget,
6677 /* Combo box is height-for-width only
6678 * (so we assume we always reserved enough height for the minimum width) */
6679 GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, minimum_size, natural_size);
6684 gtk_combo_box_get_preferred_height_for_width (GtkWidget *widget,
6689 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
6690 GtkComboBoxPrivate *priv = combo_box->priv;
6692 gint focus_width, focus_pad;
6693 gint min_height, nat_height;
6696 gtk_widget_style_get (GTK_WIDGET (widget),
6697 "focus-line-width", &focus_width,
6698 "focus-padding", &focus_pad,
6703 if (GTK_SHADOW_NONE != priv->shadow_type)
6704 size -= gtk_widget_get_style (GTK_WIDGET (widget))->xthickness;
6706 if (!priv->tree_view)
6709 if (priv->cell_view)
6711 GtkStyle *button_style;
6712 /* calculate x/y padding and separator/arrow size */
6713 gint sep_width, arrow_width, sep_height, arrow_height;
6714 gint border_width, xthickness, ythickness, xpad, ypad;
6716 border_width = gtk_container_get_border_width (GTK_CONTAINER (combo_box));
6717 button_style = gtk_widget_get_style (priv->button);
6719 xthickness = button_style->xthickness;
6720 ythickness = button_style->ythickness;
6722 gtk_widget_get_preferred_width (priv->separator, &sep_width, NULL);
6723 gtk_widget_get_preferred_width (priv->arrow, &arrow_width, NULL);
6724 gtk_widget_get_preferred_height_for_width (priv->separator,
6725 sep_width, &sep_height, NULL);
6726 gtk_widget_get_preferred_height_for_width (priv->arrow,
6727 arrow_width, &arrow_height, NULL);
6729 xpad = 2*(border_width + xthickness + focus_width + focus_pad);
6730 ypad = 2*(border_width + ythickness + focus_width + focus_pad);
6732 size -= sep_width + arrow_width + xpad;
6734 gtk_combo_box_measure_height_for_width (combo_box, size, &min_height, &nat_height);
6736 arrow_height = MAX (arrow_height, sep_height);
6737 min_height = MAX (min_height, arrow_height);
6738 nat_height = MAX (nat_height, arrow_height);
6745 /* there is a custom child widget inside (no priv->cell_view) */
6746 gint but_width, but_height;
6748 gtk_widget_get_preferred_width (priv->button, &but_width, NULL);
6749 gtk_widget_get_preferred_height_for_width (priv->button,
6750 but_width, &but_height, NULL);
6754 gtk_combo_box_measure_height_for_width (combo_box, size, &min_height, &nat_height);
6756 min_height = MAX (min_height, but_height);
6757 nat_height = MAX (nat_height, but_height);
6763 gint but_width, but_height;
6764 gint xpad = 0, ypad = 0;
6766 gtk_widget_get_preferred_width (priv->button, &but_width, NULL);
6767 gtk_widget_get_preferred_height_for_width (priv->button,
6768 but_width, &but_height, NULL);
6770 if (priv->cell_view_frame && priv->has_frame)
6772 GtkStyle *cell_style;
6775 border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->cell_view_frame));
6776 cell_style = gtk_widget_get_style (GTK_WIDGET (priv->cell_view_frame));
6778 xpad = 2 * (border_width + cell_style->xthickness);
6779 ypad = 2 * (border_width + cell_style->ythickness);
6783 size -= 2 * focus_width;
6786 gtk_combo_box_measure_height_for_width (combo_box, size, &min_height, &nat_height);
6788 min_height = MAX (min_height, but_height);
6789 nat_height = MAX (nat_height, but_height);
6795 if (GTK_SHADOW_NONE != priv->shadow_type)
6797 style = gtk_widget_get_style (GTK_WIDGET (widget));
6799 min_height += 2 * style->ythickness;
6800 nat_height += 2 * style->ythickness;
6804 *minimum_size = min_height;
6807 *natural_size = nat_height;