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 void gtk_combo_box_real_move_active (GtkComboBox *combo_box,
338 GtkScrollType scroll);
339 static void gtk_combo_box_real_popup (GtkComboBox *combo_box);
340 static gboolean gtk_combo_box_real_popdown (GtkComboBox *combo_box);
342 /* listening to the model */
343 static void gtk_combo_box_model_row_inserted (GtkTreeModel *model,
347 static void gtk_combo_box_model_row_deleted (GtkTreeModel *model,
350 static void gtk_combo_box_model_rows_reordered (GtkTreeModel *model,
355 static void gtk_combo_box_model_row_changed (GtkTreeModel *model,
359 static void gtk_combo_box_model_row_expanded (GtkTreeModel *model,
365 static void gtk_combo_box_list_position (GtkComboBox *combo_box,
370 static void gtk_combo_box_list_setup (GtkComboBox *combo_box);
371 static void gtk_combo_box_list_destroy (GtkComboBox *combo_box);
373 static gboolean gtk_combo_box_list_button_released (GtkWidget *widget,
374 GdkEventButton *event,
376 static gboolean gtk_combo_box_list_key_press (GtkWidget *widget,
379 static gboolean gtk_combo_box_list_enter_notify (GtkWidget *widget,
380 GdkEventCrossing *event,
382 static void gtk_combo_box_list_auto_scroll (GtkComboBox *combo,
385 static gboolean gtk_combo_box_list_scroll_timeout (GtkComboBox *combo);
386 static gboolean gtk_combo_box_list_button_pressed (GtkWidget *widget,
387 GdkEventButton *event,
390 static gboolean gtk_combo_box_list_select_func (GtkTreeSelection *selection,
393 gboolean path_currently_selected,
396 static void gtk_combo_box_list_row_changed (GtkTreeModel *model,
400 static void gtk_combo_box_list_popup_resize (GtkComboBox *combo_box);
403 static void gtk_combo_box_menu_setup (GtkComboBox *combo_box,
404 gboolean add_children);
405 static void gtk_combo_box_menu_fill (GtkComboBox *combo_box);
406 static void gtk_combo_box_menu_fill_level (GtkComboBox *combo_box,
409 static void gtk_combo_box_update_title (GtkComboBox *combo_box);
410 static void gtk_combo_box_menu_destroy (GtkComboBox *combo_box);
412 static void gtk_combo_box_relayout_item (GtkComboBox *combo_box,
416 static void gtk_combo_box_relayout (GtkComboBox *combo_box);
418 static gboolean gtk_combo_box_menu_button_press (GtkWidget *widget,
419 GdkEventButton *event,
421 static void gtk_combo_box_menu_item_activate (GtkWidget *item,
424 static void gtk_combo_box_update_sensitivity (GtkComboBox *combo_box);
425 static void gtk_combo_box_menu_row_inserted (GtkTreeModel *model,
429 static void gtk_combo_box_menu_row_deleted (GtkTreeModel *model,
432 static void gtk_combo_box_menu_rows_reordered (GtkTreeModel *model,
437 static void gtk_combo_box_menu_row_changed (GtkTreeModel *model,
441 static gboolean gtk_combo_box_menu_key_press (GtkWidget *widget,
444 static void gtk_combo_box_menu_popup (GtkComboBox *combo_box,
446 guint32 activate_time);
447 static GtkWidget *gtk_cell_view_menu_item_new (GtkComboBox *combo_box,
452 static void gtk_combo_box_cell_layout_pack_start (GtkCellLayout *layout,
453 GtkCellRenderer *cell,
455 static void gtk_combo_box_cell_layout_pack_end (GtkCellLayout *layout,
456 GtkCellRenderer *cell,
458 static GList *gtk_combo_box_cell_layout_get_cells (GtkCellLayout *layout);
459 static void gtk_combo_box_cell_layout_clear (GtkCellLayout *layout);
460 static void gtk_combo_box_cell_layout_add_attribute (GtkCellLayout *layout,
461 GtkCellRenderer *cell,
462 const gchar *attribute,
464 static void gtk_combo_box_cell_layout_set_cell_data_func (GtkCellLayout *layout,
465 GtkCellRenderer *cell,
466 GtkCellLayoutDataFunc func,
468 GDestroyNotify destroy);
469 static void gtk_combo_box_cell_layout_clear_attributes (GtkCellLayout *layout,
470 GtkCellRenderer *cell);
471 static void gtk_combo_box_cell_layout_reorder (GtkCellLayout *layout,
472 GtkCellRenderer *cell,
474 static gboolean gtk_combo_box_mnemonic_activate (GtkWidget *widget,
475 gboolean group_cycling);
477 static void gtk_combo_box_sync_cells (GtkComboBox *combo_box,
478 GtkCellLayout *cell_layout);
479 static void combo_cell_data_func (GtkCellLayout *cell_layout,
480 GtkCellRenderer *cell,
481 GtkTreeModel *tree_model,
484 static void gtk_combo_box_child_show (GtkWidget *widget,
485 GtkComboBox *combo_box);
486 static void gtk_combo_box_child_hide (GtkWidget *widget,
487 GtkComboBox *combo_box);
489 /* GtkComboBox:has-entry callbacks */
490 static void gtk_combo_box_entry_contents_changed (GtkEntry *entry,
492 static void gtk_combo_box_entry_active_changed (GtkComboBox *combo_box,
496 /* GtkBuildable method implementation */
497 static GtkBuildableIface *parent_buildable_iface;
499 static void gtk_combo_box_buildable_init (GtkBuildableIface *iface);
500 static gboolean gtk_combo_box_buildable_custom_tag_start (GtkBuildable *buildable,
503 const gchar *tagname,
504 GMarkupParser *parser,
506 static void gtk_combo_box_buildable_custom_tag_end (GtkBuildable *buildable,
509 const gchar *tagname,
511 static GObject *gtk_combo_box_buildable_get_internal_child (GtkBuildable *buildable,
513 const gchar *childname);
516 /* GtkCellEditable method implementations */
517 static void gtk_combo_box_start_editing (GtkCellEditable *cell_editable,
520 static void gtk_combo_box_get_preferred_width (GtkWidget *widget,
523 static void gtk_combo_box_get_preferred_height (GtkWidget *widget,
526 static void gtk_combo_box_get_preferred_width_for_height (GtkWidget *widget,
530 static void gtk_combo_box_get_preferred_height_for_width (GtkWidget *widget,
536 G_DEFINE_TYPE_WITH_CODE (GtkComboBox, gtk_combo_box, GTK_TYPE_BIN,
537 G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
538 gtk_combo_box_cell_layout_init)
539 G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_EDITABLE,
540 gtk_combo_box_cell_editable_init)
541 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
542 gtk_combo_box_buildable_init))
547 gtk_combo_box_class_init (GtkComboBoxClass *klass)
549 GObjectClass *object_class;
550 GtkContainerClass *container_class;
551 GtkWidgetClass *widget_class;
552 GtkBindingSet *binding_set;
554 container_class = (GtkContainerClass *)klass;
555 container_class->forall = gtk_combo_box_forall;
556 container_class->add = gtk_combo_box_add;
557 container_class->remove = gtk_combo_box_remove;
559 widget_class = (GtkWidgetClass *)klass;
560 widget_class->size_allocate = gtk_combo_box_size_allocate;
561 widget_class->draw = gtk_combo_box_draw;
562 widget_class->scroll_event = gtk_combo_box_scroll_event;
563 widget_class->mnemonic_activate = gtk_combo_box_mnemonic_activate;
564 widget_class->grab_focus = gtk_combo_box_grab_focus;
565 widget_class->style_set = gtk_combo_box_style_set;
566 widget_class->state_changed = gtk_combo_box_state_changed;
567 widget_class->get_preferred_width = gtk_combo_box_get_preferred_width;
568 widget_class->get_preferred_height = gtk_combo_box_get_preferred_height;
569 widget_class->get_preferred_height_for_width = gtk_combo_box_get_preferred_height_for_width;
570 widget_class->get_preferred_width_for_height = gtk_combo_box_get_preferred_width_for_height;
571 widget_class->destroy = gtk_combo_box_destroy;
573 object_class = (GObjectClass *)klass;
574 object_class->constructor = gtk_combo_box_constructor;
575 object_class->dispose = gtk_combo_box_dispose;
576 object_class->finalize = gtk_combo_box_finalize;
577 object_class->set_property = gtk_combo_box_set_property;
578 object_class->get_property = gtk_combo_box_get_property;
582 * GtkComboBox::changed:
583 * @widget: the object which received the signal
585 * The changed signal is emitted when the active
586 * item is changed. The can be due to the user selecting
587 * a different item from the list, or due to a
588 * call to gtk_combo_box_set_active_iter().
589 * It will also be emitted while typing into the entry of a combo box
594 combo_box_signals[CHANGED] =
595 g_signal_new (I_("changed"),
596 G_OBJECT_CLASS_TYPE (klass),
598 G_STRUCT_OFFSET (GtkComboBoxClass, changed),
600 g_cclosure_marshal_VOID__VOID,
603 * GtkComboBox::move-active:
604 * @widget: the object that received the signal
605 * @scroll_type: a #GtkScrollType
607 * The ::move-active signal is a
608 * <link linkend="keybinding-signals">keybinding signal</link>
609 * which gets emitted to move the active selection.
613 combo_box_signals[MOVE_ACTIVE] =
614 g_signal_new_class_handler (I_("move-active"),
615 G_OBJECT_CLASS_TYPE (klass),
616 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
617 G_CALLBACK (gtk_combo_box_real_move_active),
619 g_cclosure_marshal_VOID__ENUM,
621 GTK_TYPE_SCROLL_TYPE);
624 * GtkComboBox::popup:
625 * @widget: the object that received the signal
627 * The ::popup signal is a
628 * <link linkend="keybinding-signals">keybinding signal</link>
629 * which gets emitted to popup the combo box list.
631 * The default binding for this signal is Alt+Down.
635 combo_box_signals[POPUP] =
636 g_signal_new_class_handler (I_("popup"),
637 G_OBJECT_CLASS_TYPE (klass),
638 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
639 G_CALLBACK (gtk_combo_box_real_popup),
641 g_cclosure_marshal_VOID__VOID,
644 * GtkComboBox::popdown:
645 * @button: the object which received the signal
647 * The ::popdown signal is a
648 * <link linkend="keybinding-signals">keybinding signal</link>
649 * which gets emitted to popdown the combo box list.
651 * The default bindings for this signal are Alt+Up and Escape.
655 combo_box_signals[POPDOWN] =
656 g_signal_new_class_handler (I_("popdown"),
657 G_OBJECT_CLASS_TYPE (klass),
658 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
659 G_CALLBACK (gtk_combo_box_real_popdown),
661 _gtk_marshal_BOOLEAN__VOID,
665 binding_set = gtk_binding_set_by_class (widget_class);
667 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Down, GDK_MOD1_MASK,
669 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Down, GDK_MOD1_MASK,
672 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Up, GDK_MOD1_MASK,
674 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Up, GDK_MOD1_MASK,
676 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0,
679 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Up, 0,
681 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_UP);
682 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Up, 0,
684 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_UP);
685 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Up, 0,
687 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_UP);
688 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Page_Up, 0,
690 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_UP);
691 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Home, 0,
693 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_START);
694 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Home, 0,
696 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_START);
698 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Down, 0,
700 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_DOWN);
701 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Down, 0,
703 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_DOWN);
704 gtk_binding_entry_add_signal (binding_set, GDK_KEY_Page_Down, 0,
706 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_DOWN);
707 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_Page_Down, 0,
709 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_DOWN);
710 gtk_binding_entry_add_signal (binding_set, GDK_KEY_End, 0,
712 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_END);
713 gtk_binding_entry_add_signal (binding_set, GDK_KEY_KP_End, 0,
715 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_END);
718 g_object_class_override_property (object_class,
719 PROP_EDITING_CANCELED,
725 * The model from which the combo box takes the values shown
730 g_object_class_install_property (object_class,
732 g_param_spec_object ("model",
733 P_("ComboBox model"),
734 P_("The model for the combo box"),
736 GTK_PARAM_READWRITE));
739 * GtkComboBox:wrap-width:
741 * If wrap-width is set to a positive value, the list will be
742 * displayed in multiple columns, the number of columns is
743 * determined by wrap-width.
747 g_object_class_install_property (object_class,
749 g_param_spec_int ("wrap-width",
751 P_("Wrap width for laying out the items in a grid"),
755 GTK_PARAM_READWRITE));
759 * GtkComboBox:row-span-column:
761 * If this is set to a non-negative value, it must be the index of a column
762 * of type %G_TYPE_INT in the model.
764 * The values of that column are used to determine how many rows a value in
765 * the list will span. Therefore, the values in the model column pointed to
766 * by this property must be greater than zero and not larger than wrap-width.
770 g_object_class_install_property (object_class,
771 PROP_ROW_SPAN_COLUMN,
772 g_param_spec_int ("row-span-column",
773 P_("Row span column"),
774 P_("TreeModel column containing the row span values"),
778 GTK_PARAM_READWRITE));
782 * GtkComboBox:column-span-column:
784 * If this is set to a non-negative value, it must be the index of a column
785 * of type %G_TYPE_INT in the model.
787 * The values of that column are used to determine how many columns a value
788 * in the list will span.
792 g_object_class_install_property (object_class,
793 PROP_COLUMN_SPAN_COLUMN,
794 g_param_spec_int ("column-span-column",
795 P_("Column span column"),
796 P_("TreeModel column containing the column span values"),
800 GTK_PARAM_READWRITE));
804 * GtkComboBox:active:
806 * The item which is currently active. If the model is a non-flat treemodel,
807 * and the active item is not an immediate child of the root of the tree,
808 * this property has the value
809 * <literal>gtk_tree_path_get_indices (path)[0]</literal>,
810 * where <literal>path</literal> is the #GtkTreePath of the active item.
814 g_object_class_install_property (object_class,
816 g_param_spec_int ("active",
818 P_("The item which is currently active"),
822 GTK_PARAM_READWRITE));
825 * GtkComboBox:add-tearoffs:
827 * The add-tearoffs property controls whether generated menus
828 * have tearoff menu items.
830 * Note that this only affects menu style combo boxes.
834 g_object_class_install_property (object_class,
836 g_param_spec_boolean ("add-tearoffs",
837 P_("Add tearoffs to menus"),
838 P_("Whether dropdowns should have a tearoff menu item"),
840 GTK_PARAM_READWRITE));
843 * GtkComboBox:has-frame:
845 * The has-frame property controls whether a frame
846 * is drawn around the entry.
850 g_object_class_install_property (object_class,
852 g_param_spec_boolean ("has-frame",
854 P_("Whether the combo box draws a frame around the child"),
856 GTK_PARAM_READWRITE));
858 g_object_class_install_property (object_class,
860 g_param_spec_boolean ("focus-on-click",
861 P_("Focus on click"),
862 P_("Whether the combo box grabs focus when it is clicked with the mouse"),
864 GTK_PARAM_READWRITE));
867 * GtkComboBox:tearoff-title:
869 * A title that may be displayed by the window manager
870 * when the popup is torn-off.
874 g_object_class_install_property (object_class,
876 g_param_spec_string ("tearoff-title",
878 P_("A title that may be displayed by the window manager when the popup is torn-off"),
880 GTK_PARAM_READWRITE));
884 * GtkComboBox:popup-shown:
886 * Whether the combo boxes dropdown is popped up.
887 * Note that this property is mainly useful, because
888 * it allows you to connect to notify::popup-shown.
892 g_object_class_install_property (object_class,
894 g_param_spec_boolean ("popup-shown",
896 P_("Whether the combo's dropdown is shown"),
898 GTK_PARAM_READABLE));
902 * GtkComboBox:button-sensitivity:
904 * Whether the dropdown button is sensitive when
905 * the model is empty.
909 g_object_class_install_property (object_class,
910 PROP_BUTTON_SENSITIVITY,
911 g_param_spec_enum ("button-sensitivity",
912 P_("Button Sensitivity"),
913 P_("Whether the dropdown button is sensitive when the model is empty"),
914 GTK_TYPE_SENSITIVITY_TYPE,
915 GTK_SENSITIVITY_AUTO,
916 GTK_PARAM_READWRITE));
919 * GtkComboBox:has-entry:
921 * Whether the combo box has an entry.
925 g_object_class_install_property (object_class,
927 g_param_spec_boolean ("has-entry",
929 P_("Whether combo box has an entry"),
931 GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
934 * GtkComboBox:entry-text-column:
936 * The column in the combo box's model to associate with strings from the entry
937 * if the combo was created with #GtkComboBox:has-entry = %TRUE.
941 g_object_class_install_property (object_class,
942 PROP_ENTRY_TEXT_COLUMN,
943 g_param_spec_int ("entry-text-column",
944 P_("Entry Text Column"),
945 P_("The column in the combo box's model to associate "
946 "with strings from the entry if the combo was "
947 "created with #GtkComboBox:has-entry = %TRUE"),
949 GTK_PARAM_READWRITE));
952 * GtkComboBox:popup-fixed-width:
954 * Whether the popup's width should be a fixed width matching the
955 * allocated width of the combo box.
959 g_object_class_install_property (object_class,
960 PROP_POPUP_FIXED_WIDTH,
961 g_param_spec_boolean ("popup-fixed-width",
962 P_("Popup Fixed Width"),
963 P_("Whether the popup's width should be a "
964 "fixed width matching the allocated width "
967 GTK_PARAM_READWRITE));
969 gtk_widget_class_install_style_property (widget_class,
970 g_param_spec_boolean ("appears-as-list",
971 P_("Appears as list"),
972 P_("Whether dropdowns should look like lists rather than menus"),
974 GTK_PARAM_READABLE));
977 * GtkComboBox:arrow-size:
979 * Sets the minimum size of the arrow in the combo box. Note
980 * that the arrow size is coupled to the font size, so in case
981 * a larger font is used, the arrow will be larger than set
986 gtk_widget_class_install_style_property (widget_class,
987 g_param_spec_int ("arrow-size",
989 P_("The minimum size of the arrow in the combo box"),
993 GTK_PARAM_READABLE));
996 * GtkComboBox:shadow-type:
998 * Which kind of shadow to draw around the combo box.
1002 gtk_widget_class_install_style_property (widget_class,
1003 g_param_spec_enum ("shadow-type",
1005 P_("Which kind of shadow to draw around the combo box"),
1006 GTK_TYPE_SHADOW_TYPE,
1008 GTK_PARAM_READABLE));
1010 g_type_class_add_private (object_class, sizeof (GtkComboBoxPrivate));
1014 gtk_combo_box_buildable_init (GtkBuildableIface *iface)
1016 parent_buildable_iface = g_type_interface_peek_parent (iface);
1017 iface->add_child = _gtk_cell_layout_buildable_add_child;
1018 iface->custom_tag_start = gtk_combo_box_buildable_custom_tag_start;
1019 iface->custom_tag_end = gtk_combo_box_buildable_custom_tag_end;
1020 iface->get_internal_child = gtk_combo_box_buildable_get_internal_child;
1024 gtk_combo_box_cell_layout_init (GtkCellLayoutIface *iface)
1026 iface->pack_start = gtk_combo_box_cell_layout_pack_start;
1027 iface->pack_end = gtk_combo_box_cell_layout_pack_end;
1028 iface->get_cells = gtk_combo_box_cell_layout_get_cells;
1029 iface->clear = gtk_combo_box_cell_layout_clear;
1030 iface->add_attribute = gtk_combo_box_cell_layout_add_attribute;
1031 iface->set_cell_data_func = gtk_combo_box_cell_layout_set_cell_data_func;
1032 iface->clear_attributes = gtk_combo_box_cell_layout_clear_attributes;
1033 iface->reorder = gtk_combo_box_cell_layout_reorder;
1037 gtk_combo_box_cell_editable_init (GtkCellEditableIface *iface)
1039 iface->start_editing = gtk_combo_box_start_editing;
1043 gtk_combo_box_init (GtkComboBox *combo_box)
1045 GtkComboBoxPrivate *priv;
1047 combo_box->priv = G_TYPE_INSTANCE_GET_PRIVATE (combo_box,
1049 GtkComboBoxPrivate);
1050 priv = combo_box->priv;
1052 priv->cell_view = gtk_cell_view_new ();
1053 gtk_widget_set_parent (priv->cell_view, GTK_WIDGET (combo_box));
1054 _gtk_bin_set_child (GTK_BIN (combo_box), priv->cell_view);
1055 gtk_widget_show (priv->cell_view);
1057 priv->minimum_width = 0;
1058 priv->natural_width = 0;
1060 priv->wrap_width = 0;
1063 priv->active_row = NULL;
1064 priv->col_column = -1;
1065 priv->row_column = -1;
1067 priv->popup_shown = FALSE;
1068 priv->add_tearoffs = FALSE;
1069 priv->has_frame = TRUE;
1070 priv->is_cell_renderer = FALSE;
1071 priv->editing_canceled = FALSE;
1072 priv->auto_scroll = FALSE;
1073 priv->focus_on_click = TRUE;
1074 priv->button_sensitivity = GTK_SENSITIVITY_AUTO;
1075 priv->has_entry = FALSE;
1076 priv->popup_fixed_width = TRUE;
1078 priv->text_column = -1;
1079 priv->text_renderer = NULL;
1081 gtk_combo_box_check_appearance (combo_box);
1085 gtk_combo_box_set_property (GObject *object,
1087 const GValue *value,
1090 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
1095 gtk_combo_box_set_model (combo_box, g_value_get_object (value));
1098 case PROP_WRAP_WIDTH:
1099 gtk_combo_box_set_wrap_width (combo_box, g_value_get_int (value));
1102 case PROP_ROW_SPAN_COLUMN:
1103 gtk_combo_box_set_row_span_column (combo_box, g_value_get_int (value));
1106 case PROP_COLUMN_SPAN_COLUMN:
1107 gtk_combo_box_set_column_span_column (combo_box, g_value_get_int (value));
1111 gtk_combo_box_set_active (combo_box, g_value_get_int (value));
1114 case PROP_ADD_TEAROFFS:
1115 gtk_combo_box_set_add_tearoffs (combo_box, g_value_get_boolean (value));
1118 case PROP_HAS_FRAME:
1119 combo_box->priv->has_frame = g_value_get_boolean (value);
1121 if (combo_box->priv->has_entry)
1125 child = gtk_bin_get_child (GTK_BIN (combo_box));
1127 gtk_entry_set_has_frame (GTK_ENTRY (child),
1128 combo_box->priv->has_frame);
1133 case PROP_FOCUS_ON_CLICK:
1134 gtk_combo_box_set_focus_on_click (combo_box,
1135 g_value_get_boolean (value));
1138 case PROP_TEAROFF_TITLE:
1139 gtk_combo_box_set_title (combo_box, g_value_get_string (value));
1142 case PROP_POPUP_SHOWN:
1143 if (g_value_get_boolean (value))
1144 gtk_combo_box_popup (combo_box);
1146 gtk_combo_box_popdown (combo_box);
1149 case PROP_BUTTON_SENSITIVITY:
1150 gtk_combo_box_set_button_sensitivity (combo_box,
1151 g_value_get_enum (value));
1154 case PROP_POPUP_FIXED_WIDTH:
1155 gtk_combo_box_set_popup_fixed_width (combo_box,
1156 g_value_get_boolean (value));
1159 case PROP_EDITING_CANCELED:
1160 combo_box->priv->editing_canceled = g_value_get_boolean (value);
1163 case PROP_HAS_ENTRY:
1164 combo_box->priv->has_entry = g_value_get_boolean (value);
1167 case PROP_ENTRY_TEXT_COLUMN:
1168 gtk_combo_box_set_entry_text_column (combo_box, g_value_get_int (value));
1172 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1178 gtk_combo_box_get_property (GObject *object,
1183 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
1184 GtkComboBoxPrivate *priv = combo_box->priv;
1189 g_value_set_object (value, combo_box->priv->model);
1192 case PROP_WRAP_WIDTH:
1193 g_value_set_int (value, combo_box->priv->wrap_width);
1196 case PROP_ROW_SPAN_COLUMN:
1197 g_value_set_int (value, combo_box->priv->row_column);
1200 case PROP_COLUMN_SPAN_COLUMN:
1201 g_value_set_int (value, combo_box->priv->col_column);
1205 g_value_set_int (value, gtk_combo_box_get_active (combo_box));
1208 case PROP_ADD_TEAROFFS:
1209 g_value_set_boolean (value, gtk_combo_box_get_add_tearoffs (combo_box));
1212 case PROP_HAS_FRAME:
1213 g_value_set_boolean (value, combo_box->priv->has_frame);
1216 case PROP_FOCUS_ON_CLICK:
1217 g_value_set_boolean (value, combo_box->priv->focus_on_click);
1220 case PROP_TEAROFF_TITLE:
1221 g_value_set_string (value, gtk_combo_box_get_title (combo_box));
1224 case PROP_POPUP_SHOWN:
1225 g_value_set_boolean (value, combo_box->priv->popup_shown);
1228 case PROP_BUTTON_SENSITIVITY:
1229 g_value_set_enum (value, combo_box->priv->button_sensitivity);
1232 case PROP_POPUP_FIXED_WIDTH:
1233 g_value_set_boolean (value, combo_box->priv->popup_fixed_width);
1236 case PROP_EDITING_CANCELED:
1237 g_value_set_boolean (value, priv->editing_canceled);
1240 case PROP_HAS_ENTRY:
1241 g_value_set_boolean (value, priv->has_entry);
1244 case PROP_ENTRY_TEXT_COLUMN:
1245 g_value_set_int (value, priv->text_column);
1249 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1255 gtk_combo_box_state_changed (GtkWidget *widget,
1256 GtkStateType previous)
1258 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1259 GtkComboBoxPrivate *priv = combo_box->priv;
1261 if (gtk_widget_get_realized (widget))
1263 if (priv->tree_view && priv->cell_view)
1264 gtk_cell_view_set_background_color (GTK_CELL_VIEW (priv->cell_view),
1265 >k_widget_get_style (widget)->base[gtk_widget_get_state (widget)]);
1268 gtk_widget_queue_draw (widget);
1272 gtk_combo_box_button_state_changed (GtkWidget *widget,
1273 GtkStateType previous,
1276 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
1277 GtkComboBoxPrivate *priv = combo_box->priv;
1279 if (gtk_widget_get_realized (widget))
1281 if (!priv->tree_view && priv->cell_view)
1283 if ((gtk_widget_get_state (widget) == GTK_STATE_INSENSITIVE) !=
1284 (gtk_widget_get_state (priv->cell_view) == GTK_STATE_INSENSITIVE))
1285 gtk_widget_set_sensitive (priv->cell_view, gtk_widget_get_sensitive (widget));
1287 gtk_widget_set_state (priv->cell_view,
1288 gtk_widget_get_state (widget));
1292 gtk_widget_queue_draw (widget);
1296 gtk_combo_box_check_appearance (GtkComboBox *combo_box)
1298 GtkComboBoxPrivate *priv = combo_box->priv;
1299 gboolean appears_as_list;
1301 /* if wrap_width > 0, then we are in grid-mode and forced to use
1304 if (priv->wrap_width)
1305 appears_as_list = FALSE;
1307 gtk_widget_style_get (GTK_WIDGET (combo_box),
1308 "appears-as-list", &appears_as_list,
1311 if (appears_as_list)
1313 /* Destroy all the menu mode widgets, if they exist. */
1314 if (GTK_IS_MENU (priv->popup_widget))
1315 gtk_combo_box_menu_destroy (combo_box);
1317 /* Create the list mode widgets, if they don't already exist. */
1318 if (!GTK_IS_TREE_VIEW (priv->tree_view))
1319 gtk_combo_box_list_setup (combo_box);
1323 /* Destroy all the list mode widgets, if they exist. */
1324 if (GTK_IS_TREE_VIEW (priv->tree_view))
1325 gtk_combo_box_list_destroy (combo_box);
1327 /* Create the menu mode widgets, if they don't already exist. */
1328 if (!GTK_IS_MENU (priv->popup_widget))
1329 gtk_combo_box_menu_setup (combo_box, TRUE);
1332 gtk_widget_style_get (GTK_WIDGET (combo_box),
1333 "shadow-type", &priv->shadow_type,
1338 gtk_combo_box_style_set (GtkWidget *widget,
1341 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1342 GtkComboBoxPrivate *priv = combo_box->priv;
1345 gtk_combo_box_check_appearance (combo_box);
1347 if (priv->tree_view && priv->cell_view)
1348 gtk_cell_view_set_background_color (GTK_CELL_VIEW (priv->cell_view),
1349 >k_widget_get_style (widget)->base[gtk_widget_get_state (widget)]);
1351 child = gtk_bin_get_child (GTK_BIN (combo_box));
1352 if (GTK_IS_ENTRY (child))
1353 g_object_set (child, "shadow-type",
1354 GTK_SHADOW_NONE == priv->shadow_type ?
1355 GTK_SHADOW_IN : GTK_SHADOW_NONE, NULL);
1359 gtk_combo_box_button_toggled (GtkWidget *widget,
1362 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
1364 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
1366 if (!combo_box->priv->popup_in_progress)
1367 gtk_combo_box_popup (combo_box);
1370 gtk_combo_box_popdown (combo_box);
1374 gtk_combo_box_add (GtkContainer *container,
1377 GtkComboBox *combo_box = GTK_COMBO_BOX (container);
1378 GtkComboBoxPrivate *priv = combo_box->priv;
1380 if (priv->has_entry && !GTK_IS_ENTRY (widget))
1382 g_warning ("Attempting to add a widget with type %s to a GtkComboBox that needs an entry "
1383 "(need an instance of GtkEntry or of a subclass)",
1384 G_OBJECT_TYPE_NAME (widget));
1388 if (priv->cell_view &&
1389 gtk_widget_get_parent (priv->cell_view))
1391 gtk_widget_unparent (priv->cell_view);
1392 _gtk_bin_set_child (GTK_BIN (container), NULL);
1393 gtk_widget_queue_resize (GTK_WIDGET (container));
1396 gtk_widget_set_parent (widget, GTK_WIDGET (container));
1397 _gtk_bin_set_child (GTK_BIN (container), widget);
1399 if (priv->cell_view &&
1400 widget != priv->cell_view)
1402 /* since the cell_view was unparented, it's gone now */
1403 priv->cell_view = NULL;
1405 if (!priv->tree_view && priv->separator)
1407 gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (priv->separator)),
1409 priv->separator = NULL;
1411 gtk_widget_queue_resize (GTK_WIDGET (container));
1413 else if (priv->cell_view_frame)
1415 gtk_widget_unparent (priv->cell_view_frame);
1416 priv->cell_view_frame = NULL;
1421 if (priv->has_entry)
1423 g_signal_connect (widget, "changed",
1424 G_CALLBACK (gtk_combo_box_entry_contents_changed),
1427 gtk_entry_set_has_frame (GTK_ENTRY (widget), priv->has_frame);
1432 gtk_combo_box_remove (GtkContainer *container,
1435 GtkComboBox *combo_box = GTK_COMBO_BOX (container);
1436 GtkComboBoxPrivate *priv = combo_box->priv;
1438 gboolean appears_as_list;
1440 if (priv->has_entry)
1442 GtkWidget *child_widget;
1444 child_widget = gtk_bin_get_child (GTK_BIN (container));
1445 if (widget && widget == child_widget)
1447 g_signal_handlers_disconnect_by_func (widget,
1448 gtk_combo_box_entry_contents_changed,
1453 if (widget == priv->cell_view)
1454 priv->cell_view = NULL;
1456 gtk_widget_unparent (widget);
1457 _gtk_bin_set_child (GTK_BIN (container), NULL);
1459 if (gtk_widget_in_destruction (GTK_WIDGET (combo_box)))
1462 gtk_widget_queue_resize (GTK_WIDGET (container));
1464 if (!priv->tree_view)
1465 appears_as_list = FALSE;
1467 appears_as_list = TRUE;
1469 if (appears_as_list)
1470 gtk_combo_box_list_destroy (combo_box);
1471 else if (GTK_IS_MENU (priv->popup_widget))
1473 gtk_combo_box_menu_destroy (combo_box);
1474 gtk_menu_detach (GTK_MENU (priv->popup_widget));
1475 priv->popup_widget = NULL;
1478 if (!priv->cell_view)
1480 priv->cell_view = gtk_cell_view_new ();
1481 gtk_widget_set_parent (priv->cell_view, GTK_WIDGET (container));
1482 _gtk_bin_set_child (GTK_BIN (container), priv->cell_view);
1484 gtk_widget_show (priv->cell_view);
1485 gtk_cell_view_set_model (GTK_CELL_VIEW (priv->cell_view),
1487 gtk_combo_box_sync_cells (combo_box, GTK_CELL_LAYOUT (priv->cell_view));
1491 if (appears_as_list)
1492 gtk_combo_box_list_setup (combo_box);
1494 gtk_combo_box_menu_setup (combo_box, TRUE);
1496 if (gtk_tree_row_reference_valid (priv->active_row))
1498 path = gtk_tree_row_reference_get_path (priv->active_row);
1499 gtk_combo_box_set_active_internal (combo_box, path);
1500 gtk_tree_path_free (path);
1503 gtk_combo_box_set_active_internal (combo_box, NULL);
1506 static ComboCellInfo *
1507 gtk_combo_box_get_cell_info (GtkComboBox *combo_box,
1508 GtkCellRenderer *cell)
1512 for (i = combo_box->priv->cells; i; i = i->next)
1514 ComboCellInfo *info = (ComboCellInfo *)i->data;
1516 if (info && info->cell == cell)
1524 gtk_combo_box_menu_show (GtkWidget *menu,
1527 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1528 GtkComboBoxPrivate *priv = combo_box->priv;
1530 gtk_combo_box_child_show (menu, user_data);
1532 priv->popup_in_progress = TRUE;
1533 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button),
1535 priv->popup_in_progress = FALSE;
1539 gtk_combo_box_menu_hide (GtkWidget *menu,
1542 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1544 gtk_combo_box_child_hide (menu,user_data);
1546 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
1551 gtk_combo_box_detacher (GtkWidget *widget,
1554 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1555 GtkComboBoxPrivate *priv = combo_box->priv;
1557 g_return_if_fail (priv->popup_widget == (GtkWidget *) menu);
1559 g_signal_handlers_disconnect_by_func (menu->toplevel,
1560 gtk_combo_box_menu_show,
1562 g_signal_handlers_disconnect_by_func (menu->toplevel,
1563 gtk_combo_box_menu_hide,
1566 priv->popup_widget = NULL;
1570 gtk_combo_box_set_popup_widget (GtkComboBox *combo_box,
1573 GtkComboBoxPrivate *priv = combo_box->priv;
1575 if (GTK_IS_MENU (priv->popup_widget))
1577 gtk_menu_detach (GTK_MENU (priv->popup_widget));
1578 priv->popup_widget = NULL;
1580 else if (priv->popup_widget)
1582 gtk_container_remove (GTK_CONTAINER (priv->scrolled_window),
1583 priv->popup_widget);
1584 g_object_unref (priv->popup_widget);
1585 priv->popup_widget = NULL;
1588 if (GTK_IS_MENU (popup))
1590 if (priv->popup_window)
1592 gtk_widget_destroy (priv->popup_window);
1593 priv->popup_window = NULL;
1596 priv->popup_widget = popup;
1599 * Note that we connect to show/hide on the toplevel, not the
1600 * menu itself, since the menu is not shown/hidden when it is
1601 * popped up while torn-off.
1603 g_signal_connect (GTK_MENU (popup)->toplevel, "show",
1604 G_CALLBACK (gtk_combo_box_menu_show), combo_box);
1605 g_signal_connect (GTK_MENU (popup)->toplevel, "hide",
1606 G_CALLBACK (gtk_combo_box_menu_hide), combo_box);
1608 gtk_menu_attach_to_widget (GTK_MENU (popup),
1609 GTK_WIDGET (combo_box),
1610 gtk_combo_box_detacher);
1614 if (!priv->popup_window)
1616 GtkWidget *toplevel;
1618 priv->popup_window = gtk_window_new (GTK_WINDOW_POPUP);
1619 gtk_widget_set_name (priv->popup_window, "gtk-combobox-popup-window");
1621 gtk_window_set_type_hint (GTK_WINDOW (priv->popup_window),
1622 GDK_WINDOW_TYPE_HINT_COMBO);
1624 g_signal_connect (GTK_WINDOW (priv->popup_window),"show",
1625 G_CALLBACK (gtk_combo_box_child_show),
1627 g_signal_connect (GTK_WINDOW (priv->popup_window),"hide",
1628 G_CALLBACK (gtk_combo_box_child_hide),
1631 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (combo_box));
1632 if (GTK_IS_WINDOW (toplevel))
1634 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
1635 GTK_WINDOW (priv->popup_window));
1636 gtk_window_set_transient_for (GTK_WINDOW (priv->popup_window),
1637 GTK_WINDOW (toplevel));
1640 gtk_window_set_resizable (GTK_WINDOW (priv->popup_window), FALSE);
1641 gtk_window_set_screen (GTK_WINDOW (priv->popup_window),
1642 gtk_widget_get_screen (GTK_WIDGET (combo_box)));
1644 priv->scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1646 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
1649 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->scrolled_window),
1652 gtk_widget_show (priv->scrolled_window);
1654 gtk_container_add (GTK_CONTAINER (priv->popup_window),
1655 priv->scrolled_window);
1658 gtk_container_add (GTK_CONTAINER (priv->scrolled_window),
1661 gtk_widget_show (popup);
1662 g_object_ref (popup);
1663 priv->popup_widget = popup;
1668 gtk_combo_box_menu_position_below (GtkMenu *menu,
1674 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1675 GtkAllocation child_allocation;
1681 GdkRectangle monitor;
1683 /* FIXME: is using the size request here broken? */
1684 child = gtk_bin_get_child (GTK_BIN (combo_box));
1688 gtk_widget_get_allocation (child, &child_allocation);
1690 if (!gtk_widget_get_has_window (child))
1692 sx += child_allocation.x;
1693 sy += child_allocation.y;
1696 gdk_window_get_root_coords (gtk_widget_get_window (child),
1699 if (GTK_SHADOW_NONE != combo_box->priv->shadow_type)
1700 sx -= gtk_widget_get_style (GTK_WIDGET (combo_box))->xthickness;
1702 if (combo_box->priv->popup_fixed_width)
1703 gtk_widget_get_preferred_size (GTK_WIDGET (menu), &req, NULL);
1705 gtk_widget_get_preferred_size (GTK_WIDGET (menu), NULL, &req);
1707 if (gtk_widget_get_direction (GTK_WIDGET (combo_box)) == GTK_TEXT_DIR_LTR)
1710 *x = sx + child_allocation.width - req.width;
1713 screen = gtk_widget_get_screen (GTK_WIDGET (combo_box));
1714 monitor_num = gdk_screen_get_monitor_at_window (screen,
1715 gtk_widget_get_window (GTK_WIDGET (combo_box)));
1716 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1720 else if (*x + req.width > monitor.x + monitor.width)
1721 *x = monitor.x + monitor.width - req.width;
1723 if (monitor.y + monitor.height - *y - child_allocation.height >= req.height)
1724 *y += child_allocation.height;
1725 else if (*y - monitor.y >= req.height)
1727 else if (monitor.y + monitor.height - *y - child_allocation.height > *y - monitor.y)
1728 *y += child_allocation.height;
1736 gtk_combo_box_menu_position_over (GtkMenu *menu,
1742 GtkComboBox *combo_box;
1746 GtkAllocation allocation;
1747 GtkAllocation child_allocation;
1754 combo_box = GTK_COMBO_BOX (user_data);
1755 widget = GTK_WIDGET (combo_box);
1757 active = gtk_menu_get_active (GTK_MENU (combo_box->priv->popup_widget));
1759 gtk_widget_get_allocation (widget, &allocation);
1761 menu_xpos = allocation.x;
1762 menu_ypos = allocation.y + allocation.height / 2 - 2;
1764 if (combo_box->priv->popup_fixed_width)
1765 gtk_widget_get_preferred_width (GTK_WIDGET (menu), &menu_width, NULL);
1767 gtk_widget_get_preferred_width (GTK_WIDGET (menu), NULL, &menu_width);
1771 gtk_widget_get_allocation (active, &child_allocation);
1772 menu_ypos -= child_allocation.height / 2;
1775 children = GTK_MENU_SHELL (combo_box->priv->popup_widget)->children;
1778 child = children->data;
1780 if (active == child)
1783 if (gtk_widget_get_visible (child))
1785 gtk_widget_get_allocation (child, &child_allocation);
1787 menu_ypos -= child_allocation.height;
1790 children = children->next;
1793 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
1794 menu_xpos = menu_xpos + allocation.width - menu_width;
1796 gdk_window_get_root_coords (gtk_widget_get_window (widget),
1797 menu_xpos, menu_ypos,
1798 &menu_xpos, &menu_ypos);
1800 /* Clamp the position on screen */
1801 screen_width = gdk_screen_get_width (gtk_widget_get_screen (widget));
1805 else if ((menu_xpos + menu_width) > screen_width)
1806 menu_xpos -= ((menu_xpos + menu_width) - screen_width);
1815 gtk_combo_box_menu_position (GtkMenu *menu,
1821 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1822 GtkComboBoxPrivate *priv = combo_box->priv;
1823 GtkWidget *menu_item;
1825 if (priv->wrap_width > 0 || priv->cell_view == NULL)
1826 gtk_combo_box_menu_position_below (menu, x, y, push_in, user_data);
1829 /* FIXME handle nested menus better */
1830 menu_item = gtk_menu_get_active (GTK_MENU (priv->popup_widget));
1832 gtk_menu_shell_select_item (GTK_MENU_SHELL (priv->popup_widget),
1835 gtk_combo_box_menu_position_over (menu, x, y, push_in, user_data);
1838 if (!gtk_widget_get_visible (GTK_MENU (priv->popup_widget)->toplevel))
1839 gtk_window_set_type_hint (GTK_WINDOW (GTK_MENU (priv->popup_widget)->toplevel),
1840 GDK_WINDOW_TYPE_HINT_COMBO);
1844 gtk_combo_box_list_position (GtkComboBox *combo_box,
1850 GtkComboBoxPrivate *priv = combo_box->priv;
1851 GtkAllocation allocation;
1854 GdkRectangle monitor;
1855 GtkRequisition popup_req;
1856 GtkPolicyType hpolicy, vpolicy;
1859 /* under windows, the drop down list is as wide as the combo box itself.
1861 GtkWidget *widget = GTK_WIDGET (combo_box);
1865 gtk_widget_get_allocation (widget, &allocation);
1867 if (!gtk_widget_get_has_window (widget))
1873 window = gtk_widget_get_window (widget);
1875 gdk_window_get_root_coords (gtk_widget_get_window (widget),
1878 *width = allocation.width;
1880 hpolicy = vpolicy = GTK_POLICY_NEVER;
1881 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
1884 /* XXX This set_size_request call is part of the hack outlined below and can
1885 * go away once height-for-width is implemented on treeviews. */
1886 gtk_widget_set_size_request (priv->tree_view, -1, -1);
1888 if (combo_box->priv->popup_fixed_width)
1890 gtk_widget_get_preferred_size (priv->scrolled_window, &popup_req, NULL);
1892 if (popup_req.width > *width)
1894 hpolicy = GTK_POLICY_ALWAYS;
1895 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
1901 gtk_combo_box_remeasure (combo_box);
1903 if (priv->natural_width > *width)
1905 hpolicy = GTK_POLICY_NEVER;
1906 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
1910 /* XXX Currently we set the size-request on the internal treeview to be
1911 * the natural width of the cells, this hack can go away once our
1912 * treeview does height-for-width properly (i.e. just adjust *width
1913 * here to be the natural width request of the scrolled-window).
1915 * I can't tell why the magic number 5 is needed here (i.e. without it
1916 * treeviews are left ellipsizing) , however it this all should be
1917 * removed with height-for-width treeviews.
1919 gtk_widget_set_size_request (priv->tree_view, priv->natural_width + 5, -1);
1920 gtk_widget_get_preferred_size (priv->scrolled_window, NULL, &popup_req);
1922 *width = popup_req.width;
1926 *height = popup_req.height;
1928 screen = gtk_widget_get_screen (GTK_WIDGET (combo_box));
1929 monitor_num = gdk_screen_get_monitor_at_window (screen, window);
1930 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1932 if (gtk_widget_get_direction (GTK_WIDGET (combo_box)) == GTK_TEXT_DIR_RTL)
1933 *x = *x + allocation.width - *width;
1937 else if (*x + *width > monitor.x + monitor.width)
1938 *x = monitor.x + monitor.width - *width;
1940 if (*y + allocation.height + *height <= monitor.y + monitor.height)
1941 *y += allocation.height;
1942 else if (*y - *height >= monitor.y)
1944 else if (monitor.y + monitor.height - (*y + allocation.height) > *y - monitor.y)
1946 *y += allocation.height;
1947 *height = monitor.y + monitor.height - *y;
1951 *height = *y - monitor.y;
1955 if (popup_req.height > *height)
1957 vpolicy = GTK_POLICY_ALWAYS;
1959 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
1965 cell_view_is_sensitive (GtkCellView *cell_view)
1967 GList *cells, *list;
1970 cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (cell_view));
1973 for (list = cells; list; list = list->next)
1975 g_object_get (list->data, "sensitive", &sensitive, NULL);
1980 g_list_free (cells);
1986 tree_column_row_is_sensitive (GtkComboBox *combo_box,
1989 GtkComboBoxPrivate *priv = combo_box->priv;
1990 GList *cells, *list;
1996 if (priv->row_separator_func)
1998 if (priv->row_separator_func (priv->model, iter,
1999 priv->row_separator_data))
2003 gtk_tree_view_column_cell_set_cell_data (priv->column,
2005 iter, FALSE, FALSE);
2007 cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (priv->column));
2010 for (list = cells; list; list = list->next)
2012 g_object_get (list->data, "sensitive", &sensitive, NULL);
2017 g_list_free (cells);
2023 update_menu_sensitivity (GtkComboBox *combo_box,
2026 GtkComboBoxPrivate *priv = combo_box->priv;
2027 GList *children, *child;
2028 GtkWidget *item, *submenu, *separator;
2029 GtkWidget *cell_view;
2035 children = gtk_container_get_children (GTK_CONTAINER (menu));
2037 for (child = children; child; child = child->next)
2039 item = GTK_WIDGET (child->data);
2040 cell_view = gtk_bin_get_child (GTK_BIN (item));
2042 if (!GTK_IS_CELL_VIEW (cell_view))
2045 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (item));
2046 if (submenu != NULL)
2048 gtk_widget_set_sensitive (item, TRUE);
2049 update_menu_sensitivity (combo_box, submenu);
2053 sensitive = cell_view_is_sensitive (GTK_CELL_VIEW (cell_view));
2055 if (menu != priv->popup_widget && child == children)
2057 separator = GTK_WIDGET (child->next->data);
2058 g_object_set (item, "visible", sensitive, NULL);
2059 g_object_set (separator, "visible", sensitive, NULL);
2062 gtk_widget_set_sensitive (item, sensitive);
2066 g_list_free (children);
2070 gtk_combo_box_menu_popup (GtkComboBox *combo_box,
2072 guint32 activate_time)
2074 GtkComboBoxPrivate *priv = combo_box->priv;
2077 gint width, min_width, nat_width;
2079 update_menu_sensitivity (combo_box, priv->popup_widget);
2082 if (gtk_tree_row_reference_valid (priv->active_row))
2084 path = gtk_tree_row_reference_get_path (priv->active_row);
2085 active_item = gtk_tree_path_get_indices (path)[0];
2086 gtk_tree_path_free (path);
2088 if (priv->add_tearoffs)
2092 /* FIXME handle nested menus better */
2093 gtk_menu_set_active (GTK_MENU (priv->popup_widget), active_item);
2095 if (priv->wrap_width == 0)
2097 GtkAllocation allocation;
2099 gtk_widget_get_allocation (GTK_WIDGET (combo_box), &allocation);
2100 width = allocation.width;
2101 gtk_widget_set_size_request (priv->popup_widget, -1, -1);
2102 gtk_widget_get_preferred_width (priv->popup_widget, &min_width, &nat_width);
2104 if (combo_box->priv->popup_fixed_width)
2105 width = MAX (width, min_width);
2107 width = MAX (width, nat_width);
2109 gtk_widget_set_size_request (priv->popup_widget, width, -1);
2112 gtk_menu_popup (GTK_MENU (priv->popup_widget),
2114 gtk_combo_box_menu_position, combo_box,
2115 button, activate_time);
2119 popup_grab_on_window (GdkWindow *window,
2120 GdkDevice *keyboard,
2122 guint32 activate_time)
2125 gdk_device_grab (keyboard, window,
2126 GDK_OWNERSHIP_WINDOW, TRUE,
2127 GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
2128 NULL, activate_time) != GDK_GRAB_SUCCESS)
2132 gdk_device_grab (pointer, window,
2133 GDK_OWNERSHIP_WINDOW, TRUE,
2134 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
2135 GDK_POINTER_MOTION_MASK,
2136 NULL, activate_time) != GDK_GRAB_SUCCESS)
2139 gdk_device_ungrab (keyboard, activate_time);
2148 * gtk_combo_box_popup:
2149 * @combo_box: a #GtkComboBox
2151 * Pops up the menu or dropdown list of @combo_box.
2153 * This function is mostly intended for use by accessibility technologies;
2154 * applications should have little use for it.
2159 gtk_combo_box_popup (GtkComboBox *combo_box)
2161 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2163 g_signal_emit (combo_box, combo_box_signals[POPUP], 0);
2167 * gtk_combo_box_popup_for_device:
2168 * @combo_box: a #GtkComboBox
2169 * @device: a #GdkDevice
2171 * Pops up the menu or dropdown list of @combo_box, the popup window
2172 * will be grabbed so only @device and its associated pointer/keyboard
2173 * are the only #GdkDevice<!-- -->s able to send events to it.
2178 gtk_combo_box_popup_for_device (GtkComboBox *combo_box,
2181 GtkComboBoxPrivate *priv = combo_box->priv;
2182 gint x, y, width, height;
2183 GtkTreePath *path = NULL, *ppath;
2184 GtkWidget *toplevel;
2185 GdkDevice *keyboard, *pointer;
2188 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2189 g_return_if_fail (GDK_IS_DEVICE (device));
2191 if (!gtk_widget_get_realized (GTK_WIDGET (combo_box)))
2194 if (gtk_widget_get_mapped (priv->popup_widget))
2197 if (priv->grab_pointer && priv->grab_keyboard)
2200 time = gtk_get_current_event_time ();
2202 if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
2205 pointer = gdk_device_get_associated_device (device);
2210 keyboard = gdk_device_get_associated_device (device);
2213 if (GTK_IS_MENU (priv->popup_widget))
2215 gtk_combo_box_menu_popup (combo_box,
2216 priv->activate_button,
2217 priv->activate_time);
2221 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (combo_box));
2222 if (GTK_IS_WINDOW (toplevel))
2223 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
2224 GTK_WINDOW (priv->popup_window));
2226 gtk_widget_show_all (priv->scrolled_window);
2227 gtk_combo_box_list_position (combo_box, &x, &y, &width, &height);
2229 gtk_widget_set_size_request (priv->popup_window, width, height);
2230 gtk_window_move (GTK_WINDOW (priv->popup_window), x, y);
2232 if (gtk_tree_row_reference_valid (priv->active_row))
2234 path = gtk_tree_row_reference_get_path (priv->active_row);
2235 ppath = gtk_tree_path_copy (path);
2236 if (gtk_tree_path_up (ppath))
2237 gtk_tree_view_expand_to_path (GTK_TREE_VIEW (priv->tree_view),
2239 gtk_tree_path_free (ppath);
2241 gtk_tree_view_set_hover_expand (GTK_TREE_VIEW (priv->tree_view),
2245 gtk_widget_show (priv->popup_window);
2249 gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->tree_view),
2251 gtk_tree_path_free (path);
2254 gtk_widget_grab_focus (priv->popup_window);
2255 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button),
2258 if (!gtk_widget_has_focus (priv->tree_view))
2259 gtk_widget_grab_focus (priv->tree_view);
2261 if (!popup_grab_on_window (gtk_widget_get_window (priv->popup_window),
2262 keyboard, pointer, time))
2264 gtk_widget_hide (priv->popup_window);
2268 gtk_device_grab_add (priv->popup_window, pointer, TRUE);
2269 priv->grab_pointer = pointer;
2270 priv->grab_keyboard = keyboard;
2274 gtk_combo_box_real_popup (GtkComboBox *combo_box)
2278 device = gtk_get_current_event_device ();
2282 GdkDeviceManager *device_manager;
2283 GdkDisplay *display;
2286 display = gtk_widget_get_display (GTK_WIDGET (combo_box));
2287 device_manager = gdk_display_get_device_manager (display);
2289 /* No device was set, pick the first master device */
2290 devices = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
2291 device = devices->data;
2292 g_list_free (devices);
2295 gtk_combo_box_popup_for_device (combo_box, device);
2299 gtk_combo_box_real_popdown (GtkComboBox *combo_box)
2301 if (combo_box->priv->popup_shown)
2303 gtk_combo_box_popdown (combo_box);
2311 * gtk_combo_box_popdown:
2312 * @combo_box: a #GtkComboBox
2314 * Hides the menu or dropdown list of @combo_box.
2316 * This function is mostly intended for use by accessibility technologies;
2317 * applications should have little use for it.
2322 gtk_combo_box_popdown (GtkComboBox *combo_box)
2324 GtkComboBoxPrivate *priv = combo_box->priv;
2326 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2328 if (GTK_IS_MENU (priv->popup_widget))
2330 gtk_menu_popdown (GTK_MENU (priv->popup_widget));
2334 if (!gtk_widget_get_realized (GTK_WIDGET (combo_box)))
2337 gtk_device_grab_remove (priv->popup_window, priv->grab_pointer);
2338 gtk_widget_hide (priv->popup_window);
2339 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button),
2342 priv->grab_pointer = NULL;
2343 priv->grab_keyboard = NULL;
2347 gtk_combo_box_update_requested_width (GtkComboBox *combo_box,
2350 GtkComboBoxPrivate *priv = combo_box->priv;
2351 gint padding, min_width, nat_width;
2353 if (priv->cell_view)
2354 gtk_widget_style_get (priv->cell_view,
2355 "focus-line-width", &padding,
2360 /* add some pixels for good measure */
2361 padding += BONUS_PADDING;
2363 if (priv->cell_view)
2364 gtk_cell_view_get_desired_width_of_row (GTK_CELL_VIEW (priv->cell_view),
2365 path, &min_width, &nat_width);
2367 min_width = nat_width = 0;
2369 min_width += padding;
2370 nat_width += padding;
2372 if (min_width > priv->minimum_width || nat_width > priv->natural_width)
2374 priv->minimum_width = MAX (priv->minimum_width, min_width);
2375 priv->natural_width = MAX (priv->natural_width, nat_width);
2377 if (priv->cell_view)
2379 gtk_widget_set_size_request (priv->cell_view, min_width, -1);
2380 gtk_widget_queue_resize (priv->cell_view);
2385 #define GTK_COMBO_BOX_SIZE_ALLOCATE_BUTTON \
2386 gtk_widget_get_preferred_size (combo_box->priv->button, \
2390 child.x = allocation->x + shadow_width; \
2392 child.x = allocation->x + allocation->width - req.width - shadow_width; \
2394 child.y = allocation->y + shadow_height; \
2395 child.width = req.width; \
2396 child.height = allocation->height - 2 * shadow_height; \
2397 child.width = MAX (1, child.width); \
2398 child.height = MAX (1, child.height); \
2400 gtk_widget_size_allocate (combo_box->priv->button, &child);
2403 gtk_combo_box_size_allocate (GtkWidget *widget,
2404 GtkAllocation *allocation)
2406 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
2407 GtkComboBoxPrivate *priv = combo_box->priv;
2408 GtkWidget *child_widget;
2409 gint shadow_width, shadow_height;
2410 gint focus_width, focus_pad;
2411 GtkAllocation child;
2414 gboolean is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
2416 gtk_widget_set_allocation (widget, allocation);
2417 child_widget = gtk_bin_get_child (GTK_BIN (widget));
2419 style = gtk_widget_get_style (widget);
2420 gtk_widget_style_get (widget,
2421 "focus-line-width", &focus_width,
2422 "focus-padding", &focus_pad,
2425 if (GTK_SHADOW_NONE != priv->shadow_type)
2427 shadow_width = style->xthickness;
2428 shadow_height = style->ythickness;
2436 if (!priv->tree_view)
2438 if (priv->cell_view)
2440 gint xthickness, ythickness;
2445 allocation->x += shadow_width;
2446 allocation->y += shadow_height;
2447 allocation->width -= 2 * shadow_width;
2448 allocation->height -= 2 * shadow_height;
2450 gtk_widget_size_allocate (priv->button, allocation);
2452 /* set some things ready */
2453 border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->button));
2454 style = gtk_widget_get_style (priv->button);
2455 xthickness = style->xthickness;
2456 ythickness = style->ythickness;
2458 child.x = allocation->x;
2459 child.y = allocation->y;
2460 width = allocation->width;
2461 child.height = allocation->height;
2463 if (!priv->is_cell_renderer)
2465 child.x += border_width + xthickness + focus_width + focus_pad;
2466 child.y += border_width + ythickness + focus_width + focus_pad;
2467 width -= 2 * (child.x - allocation->x);
2468 child.height -= 2 * (child.y - allocation->y);
2472 /* handle the children */
2473 gtk_widget_get_preferred_size (priv->arrow, &req, NULL);
2474 child.width = req.width;
2476 child.x += width - req.width;
2477 child.width = MAX (1, child.width);
2478 child.height = MAX (1, child.height);
2479 gtk_widget_size_allocate (priv->arrow, &child);
2481 child.x += req.width;
2482 gtk_widget_get_preferred_size (priv->separator, &req, NULL);
2483 child.width = req.width;
2485 child.x -= req.width;
2486 child.width = MAX (1, child.width);
2487 child.height = MAX (1, child.height);
2488 gtk_widget_size_allocate (priv->separator, &child);
2492 child.x += req.width;
2493 child.width = allocation->x + allocation->width
2494 - (border_width + xthickness + focus_width + focus_pad)
2499 child.width = child.x;
2500 child.x = allocation->x
2501 + border_width + xthickness + focus_width + focus_pad;
2502 child.width -= child.x;
2505 if (gtk_widget_get_visible (priv->popup_widget))
2507 gint width, menu_width;
2509 if (priv->wrap_width == 0)
2511 GtkAllocation combo_box_allocation;
2513 gtk_widget_get_allocation (GTK_WIDGET (combo_box), &combo_box_allocation);
2514 width = combo_box_allocation.width;
2515 gtk_widget_set_size_request (priv->popup_widget, -1, -1);
2517 if (combo_box->priv->popup_fixed_width)
2518 gtk_widget_get_preferred_width (priv->popup_widget, &menu_width, NULL);
2520 gtk_widget_get_preferred_width (priv->popup_widget, NULL, &menu_width);
2522 gtk_widget_set_size_request (priv->popup_widget,
2523 MAX (width, menu_width), -1);
2526 /* reposition the menu after giving it a new width */
2527 gtk_menu_reposition (GTK_MENU (priv->popup_widget));
2530 child.width = MAX (1, child.width);
2531 child.height = MAX (1, child.height);
2532 gtk_widget_size_allocate (child_widget, &child);
2536 GTK_COMBO_BOX_SIZE_ALLOCATE_BUTTON
2539 child.x = allocation->x + req.width + shadow_width;
2541 child.x = allocation->x + shadow_width;
2542 child.y = allocation->y + shadow_height;
2543 child.width = allocation->width - req.width - 2 * shadow_width;
2544 child.width = MAX (1, child.width);
2545 child.height = MAX (1, child.height);
2546 gtk_widget_size_allocate (child_widget, &child);
2553 /* Combobox thickness + border-width */
2554 guint border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
2555 int delta_x = shadow_width + border_width;
2556 int delta_y = shadow_height + border_width;
2559 GTK_COMBO_BOX_SIZE_ALLOCATE_BUTTON
2563 child.x = allocation->x + req.width;
2565 child.x = allocation->x;
2567 child.y = allocation->y;
2568 child.width = allocation->width - req.width;
2569 child.height = allocation->height;
2571 if (priv->cell_view_frame)
2575 child.width = MAX (1, child.width - delta_x * 2);
2576 child.height = MAX (1, child.height - delta_y * 2);
2577 gtk_widget_size_allocate (priv->cell_view_frame, &child);
2580 if (priv->has_frame)
2582 border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->cell_view_frame));
2583 style = gtk_widget_get_style (priv->cell_view_frame);
2584 delta_x = border_width + style->xthickness;
2585 delta_y = border_width + style->ythickness;
2589 child.width -= delta_x * 2;
2590 child.height -= delta_y * 2;
2597 child.width -= delta_x * 2;
2598 child.height -= delta_y * 2;
2601 if (gtk_widget_get_visible (priv->popup_window))
2603 gint x, y, width, height;
2604 gtk_combo_box_list_position (combo_box, &x, &y, &width, &height);
2605 gtk_window_move (GTK_WINDOW (priv->popup_window), x, y);
2606 gtk_widget_set_size_request (priv->popup_window, width, height);
2610 child.width = MAX (1, child.width);
2611 child.height = MAX (1, child.height);
2613 gtk_widget_size_allocate (child_widget, &child);
2617 #undef GTK_COMBO_BOX_ALLOCATE_BUTTON
2620 gtk_combo_box_unset_model (GtkComboBox *combo_box)
2622 GtkComboBoxPrivate *priv = combo_box->priv;
2626 g_signal_handler_disconnect (priv->model,
2628 g_signal_handler_disconnect (priv->model,
2630 g_signal_handler_disconnect (priv->model,
2631 priv->reordered_id);
2632 g_signal_handler_disconnect (priv->model,
2637 if (!priv->tree_view)
2639 if (priv->popup_widget)
2640 gtk_container_foreach (GTK_CONTAINER (priv->popup_widget),
2641 (GtkCallback)gtk_widget_destroy, NULL);
2646 g_object_unref (priv->model);
2650 if (priv->active_row)
2652 gtk_tree_row_reference_free (priv->active_row);
2653 priv->active_row = NULL;
2656 if (priv->cell_view)
2657 gtk_cell_view_set_model (GTK_CELL_VIEW (priv->cell_view), NULL);
2661 gtk_combo_box_forall (GtkContainer *container,
2662 gboolean include_internals,
2663 GtkCallback callback,
2664 gpointer callback_data)
2666 GtkComboBox *combo_box = GTK_COMBO_BOX (container);
2667 GtkComboBoxPrivate *priv = combo_box->priv;
2670 if (include_internals)
2673 (* callback) (priv->button, callback_data);
2674 if (priv->cell_view_frame)
2675 (* callback) (priv->cell_view_frame, callback_data);
2678 child = gtk_bin_get_child (GTK_BIN (container));
2680 (* callback) (child, callback_data);
2684 gtk_combo_box_child_show (GtkWidget *widget,
2685 GtkComboBox *combo_box)
2687 GtkComboBoxPrivate *priv = combo_box->priv;
2689 priv->popup_shown = TRUE;
2690 g_object_notify (G_OBJECT (combo_box), "popup-shown");
2694 gtk_combo_box_child_hide (GtkWidget *widget,
2695 GtkComboBox *combo_box)
2697 GtkComboBoxPrivate *priv = combo_box->priv;
2699 priv->popup_shown = FALSE;
2700 g_object_notify (G_OBJECT (combo_box), "popup-shown");
2704 gtk_combo_box_draw (GtkWidget *widget,
2707 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
2708 GtkComboBoxPrivate *priv = combo_box->priv;
2710 if (priv->shadow_type != GTK_SHADOW_NONE)
2712 gtk_paint_shadow (gtk_widget_get_style (widget),
2714 GTK_STATE_NORMAL, priv->shadow_type,
2717 gtk_widget_get_allocated_width (widget),
2718 gtk_widget_get_allocated_height (widget));
2721 gtk_container_propagate_draw (GTK_CONTAINER (widget),
2724 if (priv->tree_view && priv->cell_view_frame)
2726 gtk_container_propagate_draw (GTK_CONTAINER (widget),
2727 priv->cell_view_frame, cr);
2730 gtk_container_propagate_draw (GTK_CONTAINER (widget),
2731 gtk_bin_get_child (GTK_BIN (widget)),
2747 path_visible (GtkTreeView *view,
2753 /* Note that we rely on the fact that collapsed rows don't have nodes
2755 return _gtk_tree_view_find_node (view, path, &tree, &node);
2759 tree_next_func (GtkTreeModel *model,
2764 SearchData *search_data = (SearchData *)data;
2766 if (search_data->found)
2768 if (!tree_column_row_is_sensitive (search_data->combo, iter))
2771 if (search_data->visible &&
2772 !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
2775 search_data->set = TRUE;
2776 search_data->iter = *iter;
2781 if (gtk_tree_path_compare (path, search_data->path) == 0)
2782 search_data->found = TRUE;
2788 tree_next (GtkComboBox *combo,
2789 GtkTreeModel *model,
2794 SearchData search_data;
2796 search_data.combo = combo;
2797 search_data.path = gtk_tree_model_get_path (model, iter);
2798 search_data.visible = visible;
2799 search_data.found = FALSE;
2800 search_data.set = FALSE;
2802 gtk_tree_model_foreach (model, tree_next_func, &search_data);
2804 *next = search_data.iter;
2806 gtk_tree_path_free (search_data.path);
2808 return search_data.set;
2812 tree_prev_func (GtkTreeModel *model,
2817 SearchData *search_data = (SearchData *)data;
2819 if (gtk_tree_path_compare (path, search_data->path) == 0)
2821 search_data->found = TRUE;
2825 if (!tree_column_row_is_sensitive (search_data->combo, iter))
2828 if (search_data->visible &&
2829 !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
2832 search_data->set = TRUE;
2833 search_data->iter = *iter;
2839 tree_prev (GtkComboBox *combo,
2840 GtkTreeModel *model,
2845 SearchData search_data;
2847 search_data.combo = combo;
2848 search_data.path = gtk_tree_model_get_path (model, iter);
2849 search_data.visible = visible;
2850 search_data.found = FALSE;
2851 search_data.set = FALSE;
2853 gtk_tree_model_foreach (model, tree_prev_func, &search_data);
2855 *prev = search_data.iter;
2857 gtk_tree_path_free (search_data.path);
2859 return search_data.set;
2863 tree_last_func (GtkTreeModel *model,
2868 SearchData *search_data = (SearchData *)data;
2870 if (!tree_column_row_is_sensitive (search_data->combo, iter))
2873 /* Note that we rely on the fact that collapsed rows don't have nodes
2875 if (search_data->visible &&
2876 !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
2879 search_data->set = TRUE;
2880 search_data->iter = *iter;
2886 tree_last (GtkComboBox *combo,
2887 GtkTreeModel *model,
2891 SearchData search_data;
2893 search_data.combo = combo;
2894 search_data.visible = visible;
2895 search_data.set = FALSE;
2897 gtk_tree_model_foreach (model, tree_last_func, &search_data);
2899 *last = search_data.iter;
2901 return search_data.set;
2906 tree_first_func (GtkTreeModel *model,
2911 SearchData *search_data = (SearchData *)data;
2913 if (!tree_column_row_is_sensitive (search_data->combo, iter))
2916 if (search_data->visible &&
2917 !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
2920 search_data->set = TRUE;
2921 search_data->iter = *iter;
2927 tree_first (GtkComboBox *combo,
2928 GtkTreeModel *model,
2932 SearchData search_data;
2934 search_data.combo = combo;
2935 search_data.visible = visible;
2936 search_data.set = FALSE;
2938 gtk_tree_model_foreach (model, tree_first_func, &search_data);
2940 *first = search_data.iter;
2942 return search_data.set;
2946 gtk_combo_box_scroll_event (GtkWidget *widget,
2947 GdkEventScroll *event)
2949 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
2952 GtkTreeIter new_iter;
2954 if (!gtk_combo_box_get_active_iter (combo_box, &iter))
2957 if (event->direction == GDK_SCROLL_UP)
2958 found = tree_prev (combo_box, combo_box->priv->model,
2959 &iter, &new_iter, FALSE);
2961 found = tree_next (combo_box, combo_box->priv->model,
2962 &iter, &new_iter, FALSE);
2965 gtk_combo_box_set_active_iter (combo_box, &new_iter);
2975 gtk_combo_box_sync_cells (GtkComboBox *combo_box,
2976 GtkCellLayout *cell_layout)
2978 GtkComboBoxPrivate *priv = combo_box->priv;
2981 for (k = priv->cells; k; k = k->next)
2984 ComboCellInfo *info = (ComboCellInfo *)k->data;
2986 if (info->pack == GTK_PACK_START)
2987 gtk_cell_layout_pack_start (cell_layout,
2988 info->cell, info->expand);
2989 else if (info->pack == GTK_PACK_END)
2990 gtk_cell_layout_pack_end (cell_layout,
2991 info->cell, info->expand);
2993 gtk_cell_layout_set_cell_data_func (cell_layout,
2995 combo_cell_data_func, info, NULL);
2997 for (j = info->attributes; j; j = j->next->next)
2999 gtk_cell_layout_add_attribute (cell_layout,
3002 GPOINTER_TO_INT (j->next->data));
3008 gtk_combo_box_menu_setup (GtkComboBox *combo_box,
3009 gboolean add_children)
3011 GtkComboBoxPrivate *priv = combo_box->priv;
3015 child = gtk_bin_get_child (GTK_BIN (combo_box));
3017 if (priv->cell_view)
3019 priv->button = gtk_toggle_button_new ();
3020 gtk_button_set_focus_on_click (GTK_BUTTON (priv->button),
3021 priv->focus_on_click);
3023 g_signal_connect (priv->button, "toggled",
3024 G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
3025 gtk_widget_set_parent (priv->button,
3026 gtk_widget_get_parent (child));
3028 priv->box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
3029 gtk_container_add (GTK_CONTAINER (priv->button), priv->box);
3031 priv->separator = gtk_separator_new (GTK_ORIENTATION_VERTICAL);
3032 gtk_container_add (GTK_CONTAINER (priv->box), priv->separator);
3034 priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3035 gtk_container_add (GTK_CONTAINER (priv->box), priv->arrow);
3037 gtk_widget_show_all (priv->button);
3041 priv->button = gtk_toggle_button_new ();
3042 gtk_button_set_focus_on_click (GTK_BUTTON (priv->button),
3043 priv->focus_on_click);
3045 g_signal_connect (priv->button, "toggled",
3046 G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
3047 gtk_widget_set_parent (priv->button,
3048 gtk_widget_get_parent (child));
3050 priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3051 gtk_container_add (GTK_CONTAINER (priv->button), priv->arrow);
3052 gtk_widget_show_all (priv->button);
3055 g_signal_connect (priv->button, "button-press-event",
3056 G_CALLBACK (gtk_combo_box_menu_button_press),
3058 g_signal_connect (priv->button, "state-changed",
3059 G_CALLBACK (gtk_combo_box_button_state_changed),
3062 /* create our funky menu */
3063 menu = gtk_menu_new ();
3064 gtk_widget_set_name (menu, "gtk-combobox-popup-menu");
3065 gtk_menu_set_reserve_toggle_size (GTK_MENU (menu), FALSE);
3067 g_signal_connect (menu, "key-press-event",
3068 G_CALLBACK (gtk_combo_box_menu_key_press), combo_box);
3069 gtk_combo_box_set_popup_widget (combo_box, menu);
3073 gtk_combo_box_menu_fill (combo_box);
3075 /* the column is needed in tree_column_row_is_sensitive() */
3076 priv->column = gtk_tree_view_column_new ();
3077 g_object_ref_sink (priv->column);
3078 gtk_combo_box_sync_cells (combo_box, GTK_CELL_LAYOUT (priv->column));
3080 gtk_combo_box_update_title (combo_box);
3081 gtk_combo_box_update_sensitivity (combo_box);
3085 gtk_combo_box_menu_fill (GtkComboBox *combo_box)
3087 GtkComboBoxPrivate *priv = combo_box->priv;
3093 menu = priv->popup_widget;
3095 if (priv->add_tearoffs)
3097 GtkWidget *tearoff = gtk_tearoff_menu_item_new ();
3099 gtk_widget_show (tearoff);
3101 if (priv->wrap_width)
3102 gtk_menu_attach (GTK_MENU (menu), tearoff, 0, priv->wrap_width, 0, 1);
3104 gtk_menu_shell_append (GTK_MENU_SHELL (menu), tearoff);
3107 gtk_combo_box_menu_fill_level (combo_box, menu, NULL);
3111 gtk_cell_view_menu_item_new (GtkComboBox *combo_box,
3112 GtkTreeModel *model,
3115 GtkWidget *cell_view;
3120 cell_view = gtk_cell_view_new ();
3121 item = gtk_menu_item_new ();
3122 gtk_container_add (GTK_CONTAINER (item), cell_view);
3124 gtk_cell_view_set_model (GTK_CELL_VIEW (cell_view), model);
3125 path = gtk_tree_model_get_path (model, iter);
3126 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (cell_view), path);
3127 gtk_tree_path_free (path);
3129 gtk_combo_box_sync_cells (combo_box, GTK_CELL_LAYOUT (cell_view));
3130 gtk_widget_get_preferred_size (cell_view, &req, NULL);
3131 gtk_widget_show (cell_view);
3137 gtk_combo_box_menu_fill_level (GtkComboBox *combo_box,
3139 GtkTreeIter *parent)
3141 GtkComboBoxPrivate *priv = combo_box->priv;
3142 GtkTreeModel *model = priv->model;
3143 GtkWidget *item, *submenu, *subitem, *separator;
3145 gboolean is_separator;
3150 n_children = gtk_tree_model_iter_n_children (model, parent);
3153 for (i = 0; i < n_children; i++)
3155 gtk_tree_model_iter_nth_child (model, &iter, parent, i);
3157 if (priv->row_separator_func)
3158 is_separator = priv->row_separator_func (priv->model, &iter,
3159 priv->row_separator_data);
3161 is_separator = FALSE;
3165 item = gtk_separator_menu_item_new ();
3166 path = gtk_tree_model_get_path (model, &iter);
3167 g_object_set_data_full (G_OBJECT (item),
3168 I_("gtk-combo-box-item-path"),
3169 gtk_tree_row_reference_new (model, path),
3170 (GDestroyNotify)gtk_tree_row_reference_free);
3171 gtk_tree_path_free (path);
3175 item = gtk_cell_view_menu_item_new (combo_box, model, &iter);
3176 if (gtk_tree_model_iter_has_child (model, &iter))
3178 submenu = gtk_menu_new ();
3179 gtk_menu_set_reserve_toggle_size (GTK_MENU (submenu), FALSE);
3180 gtk_widget_show (submenu);
3181 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu);
3183 /* Ugly - since menus can only activate leafs, we have to
3184 * duplicate the item inside the submenu.
3186 subitem = gtk_cell_view_menu_item_new (combo_box, model, &iter);
3187 separator = gtk_separator_menu_item_new ();
3188 gtk_widget_show (subitem);
3189 gtk_widget_show (separator);
3190 g_signal_connect (subitem, "activate",
3191 G_CALLBACK (gtk_combo_box_menu_item_activate),
3193 gtk_menu_shell_append (GTK_MENU_SHELL (submenu), subitem);
3194 gtk_menu_shell_append (GTK_MENU_SHELL (submenu), separator);
3196 gtk_combo_box_menu_fill_level (combo_box, submenu, &iter);
3198 g_signal_connect (item, "activate",
3199 G_CALLBACK (gtk_combo_box_menu_item_activate),
3203 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3204 if (priv->wrap_width && menu == priv->popup_widget)
3205 gtk_combo_box_relayout_item (combo_box, item, &iter, last);
3206 gtk_widget_show (item);
3213 gtk_combo_box_menu_destroy (GtkComboBox *combo_box)
3215 GtkComboBoxPrivate *priv = combo_box->priv;
3217 g_signal_handlers_disconnect_matched (priv->button,
3218 G_SIGNAL_MATCH_DATA,
3220 gtk_combo_box_menu_button_press, NULL);
3221 g_signal_handlers_disconnect_matched (priv->button,
3222 G_SIGNAL_MATCH_DATA,
3224 gtk_combo_box_button_state_changed, combo_box);
3226 /* unparent will remove our latest ref */
3227 gtk_widget_unparent (priv->button);
3230 priv->button = NULL;
3232 priv->separator = NULL;
3234 g_object_unref (priv->column);
3235 priv->column = NULL;
3237 /* changing the popup window will unref the menu and the children */
3245 menu_occupied (GtkMenu *menu,
3249 guint bottom_attach)
3253 for (i = GTK_MENU_SHELL (menu)->children; i; i = i->next)
3257 gtk_container_child_get (GTK_CONTAINER (menu),
3261 "bottom-attach", &b,
3265 /* look if this item intersects with the given coordinates */
3266 if (right_attach > l && left_attach < r && bottom_attach > t && top_attach < b)
3274 gtk_combo_box_relayout_item (GtkComboBox *combo_box,
3279 GtkComboBoxPrivate *priv = combo_box->priv;
3280 gint current_col = 0, current_row = 0;
3281 gint rows = 1, cols = 1;
3282 GtkWidget *menu = priv->popup_widget;
3284 if (!GTK_IS_MENU_SHELL (menu))
3287 if (priv->col_column == -1 &&
3288 priv->row_column == -1 &&
3291 gtk_container_child_get (GTK_CONTAINER (menu),
3293 "right-attach", ¤t_col,
3294 "top-attach", ¤t_row,
3296 if (current_col + cols > priv->wrap_width)
3304 if (priv->col_column != -1)
3305 gtk_tree_model_get (priv->model, iter,
3306 priv->col_column, &cols,
3308 if (priv->row_column != -1)
3309 gtk_tree_model_get (priv->model, iter,
3310 priv->row_column, &rows,
3315 if (current_col + cols > priv->wrap_width)
3321 if (!menu_occupied (GTK_MENU (menu),
3322 current_col, current_col + cols,
3323 current_row, current_row + rows))
3330 /* set attach props */
3331 gtk_menu_attach (GTK_MENU (menu), item,
3332 current_col, current_col + cols,
3333 current_row, current_row + rows);
3337 gtk_combo_box_relayout (GtkComboBox *combo_box)
3342 menu = combo_box->priv->popup_widget;
3344 /* do nothing unless we are in menu style and realized */
3345 if (combo_box->priv->tree_view || !GTK_IS_MENU_SHELL (menu))
3348 list = gtk_container_get_children (GTK_CONTAINER (menu));
3350 for (j = g_list_last (list); j; j = j->prev)
3351 gtk_container_remove (GTK_CONTAINER (menu), j->data);
3353 gtk_combo_box_menu_fill (combo_box);
3360 gtk_combo_box_menu_button_press (GtkWidget *widget,
3361 GdkEventButton *event,
3364 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3365 GtkComboBoxPrivate *priv = combo_box->priv;
3367 if (GTK_IS_MENU (priv->popup_widget) &&
3368 event->type == GDK_BUTTON_PRESS && event->button == 1)
3370 if (priv->focus_on_click &&
3371 !gtk_widget_has_focus (priv->button))
3372 gtk_widget_grab_focus (priv->button);
3374 gtk_combo_box_menu_popup (combo_box, event->button, event->time);
3383 gtk_combo_box_menu_item_activate (GtkWidget *item,
3386 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3387 GtkWidget *cell_view;
3391 cell_view = gtk_bin_get_child (GTK_BIN (item));
3393 g_return_if_fail (GTK_IS_CELL_VIEW (cell_view));
3395 path = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (cell_view));
3397 if (gtk_tree_model_get_iter (combo_box->priv->model, &iter, path))
3399 if (gtk_menu_item_get_submenu (GTK_MENU_ITEM (item)) == NULL)
3400 gtk_combo_box_set_active_iter (combo_box, &iter);
3403 gtk_tree_path_free (path);
3405 g_object_set (combo_box,
3406 "editing-canceled", FALSE,
3411 gtk_combo_box_update_sensitivity (GtkComboBox *combo_box)
3414 gboolean sensitive = TRUE; /* fool code checkers */
3416 if (!combo_box->priv->button)
3419 switch (combo_box->priv->button_sensitivity)
3421 case GTK_SENSITIVITY_ON:
3424 case GTK_SENSITIVITY_OFF:
3427 case GTK_SENSITIVITY_AUTO:
3428 sensitive = combo_box->priv->model &&
3429 gtk_tree_model_get_iter_first (combo_box->priv->model, &iter);
3432 g_assert_not_reached ();
3436 gtk_widget_set_sensitive (combo_box->priv->button, sensitive);
3438 /* In list-mode, we also need to update sensitivity of the event box */
3439 if (GTK_IS_TREE_VIEW (combo_box->priv->tree_view)
3440 && combo_box->priv->cell_view)
3441 gtk_widget_set_sensitive (combo_box->priv->box, sensitive);
3445 gtk_combo_box_model_row_inserted (GtkTreeModel *model,
3450 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3452 if (combo_box->priv->tree_view)
3453 gtk_combo_box_list_popup_resize (combo_box);
3455 gtk_combo_box_menu_row_inserted (model, path, iter, user_data);
3457 gtk_combo_box_update_sensitivity (combo_box);
3461 gtk_combo_box_model_row_deleted (GtkTreeModel *model,
3465 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3466 GtkComboBoxPrivate *priv = combo_box->priv;
3468 if (!gtk_tree_row_reference_valid (priv->active_row))
3470 if (priv->cell_view)
3471 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (priv->cell_view), NULL);
3472 g_signal_emit (combo_box, combo_box_signals[CHANGED], 0);
3475 if (priv->tree_view)
3476 gtk_combo_box_list_popup_resize (combo_box);
3478 gtk_combo_box_menu_row_deleted (model, path, user_data);
3480 gtk_combo_box_update_sensitivity (combo_box);
3484 gtk_combo_box_model_rows_reordered (GtkTreeModel *model,
3490 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3492 gtk_tree_row_reference_reordered (G_OBJECT (user_data), path, iter, new_order);
3494 if (!combo_box->priv->tree_view)
3495 gtk_combo_box_menu_rows_reordered (model, path, iter, new_order, user_data);
3499 gtk_combo_box_model_row_changed (GtkTreeModel *model,
3504 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3505 GtkComboBoxPrivate *priv = combo_box->priv;
3506 GtkTreePath *active_path;
3508 /* FIXME this belongs to GtkCellView */
3509 if (gtk_tree_row_reference_valid (priv->active_row))
3511 active_path = gtk_tree_row_reference_get_path (priv->active_row);
3512 if (gtk_tree_path_compare (path, active_path) == 0 &&
3514 gtk_widget_queue_resize (GTK_WIDGET (priv->cell_view));
3515 gtk_tree_path_free (active_path);
3518 if (priv->tree_view)
3519 gtk_combo_box_list_row_changed (model, path, iter, user_data);
3521 gtk_combo_box_menu_row_changed (model, path, iter, user_data);
3525 list_popup_resize_idle (gpointer user_data)
3527 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3528 GtkComboBoxPrivate *priv = combo_box->priv;
3529 gint x, y, width, height;
3531 if (priv->tree_view && gtk_widget_get_mapped (priv->popup_window))
3533 gtk_combo_box_list_position (combo_box, &x, &y, &width, &height);
3535 gtk_widget_set_size_request (priv->popup_window, width, height);
3536 gtk_window_move (GTK_WINDOW (priv->popup_window), x, y);
3539 priv->resize_idle_id = 0;
3545 gtk_combo_box_list_popup_resize (GtkComboBox *combo_box)
3547 GtkComboBoxPrivate *priv = combo_box->priv;
3549 if (!priv->resize_idle_id)
3550 priv->resize_idle_id =
3551 gdk_threads_add_idle (list_popup_resize_idle, combo_box);
3555 gtk_combo_box_model_row_expanded (GtkTreeModel *model,
3560 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3562 gtk_combo_box_list_popup_resize (combo_box);
3567 find_menu_by_path (GtkWidget *menu,
3569 gboolean skip_first)
3575 GtkTreeRowReference *mref;
3579 list = gtk_container_get_children (GTK_CONTAINER (menu));
3582 for (i = list; i; i = i->next)
3584 child = gtk_bin_get_child (i->data);
3585 if (GTK_IS_SEPARATOR_MENU_ITEM (i->data))
3587 mref = g_object_get_data (G_OBJECT (i->data), "gtk-combo-box-item-path");
3590 else if (!gtk_tree_row_reference_valid (mref))
3593 mpath = gtk_tree_row_reference_get_path (mref);
3595 else if (GTK_IS_CELL_VIEW (child))
3603 mpath = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (child));
3608 /* this case is necessary, since the row reference of
3609 * the cell view may already be updated after a deletion
3616 if (gtk_tree_path_compare (mpath, path) == 0)
3618 gtk_tree_path_free (mpath);
3622 if (gtk_tree_path_is_ancestor (mpath, path))
3624 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
3625 if (submenu != NULL)
3627 gtk_tree_path_free (mpath);
3628 item = find_menu_by_path (submenu, path, TRUE);
3632 gtk_tree_path_free (mpath);
3642 dump_menu_tree (GtkWidget *menu,
3649 list = gtk_container_get_children (GTK_CONTAINER (menu));
3650 for (i = list; i; i = i->next)
3652 if (GTK_IS_CELL_VIEW (GTK_BIN (i->data)->child))
3654 path = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (GTK_BIN (i->data)->child));
3655 g_print ("%*s%s\n", 2 * level, " ", gtk_tree_path_to_string (path));
3656 gtk_tree_path_free (path);
3658 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
3659 if (submenu != NULL)
3660 dump_menu_tree (submenu, level + 1);
3669 gtk_combo_box_menu_row_inserted (GtkTreeModel *model,
3674 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3675 GtkComboBoxPrivate *priv = combo_box->priv;
3677 GtkWidget *item, *menu, *separator;
3681 gboolean is_separator;
3683 if (!priv->popup_widget)
3686 depth = gtk_tree_path_get_depth (path);
3687 pos = gtk_tree_path_get_indices (path)[depth - 1];
3690 ppath = gtk_tree_path_copy (path);
3691 gtk_tree_path_up (ppath);
3692 parent = find_menu_by_path (priv->popup_widget, ppath, FALSE);
3693 gtk_tree_path_free (ppath);
3695 menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (parent));
3698 menu = gtk_menu_new ();
3699 gtk_menu_set_reserve_toggle_size (GTK_MENU (menu), FALSE);
3700 gtk_widget_show (menu);
3701 gtk_menu_item_set_submenu (GTK_MENU_ITEM (parent), menu);
3703 /* Ugly - since menus can only activate leaves, we have to
3704 * duplicate the item inside the submenu.
3706 gtk_tree_model_iter_parent (model, &piter, iter);
3707 item = gtk_cell_view_menu_item_new (combo_box, model, &piter);
3708 separator = gtk_separator_menu_item_new ();
3709 g_signal_connect (item, "activate",
3710 G_CALLBACK (gtk_combo_box_menu_item_activate),
3712 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3713 gtk_menu_shell_append (GTK_MENU_SHELL (menu), separator);
3714 if (cell_view_is_sensitive (GTK_CELL_VIEW (gtk_bin_get_child (GTK_BIN (item)))))
3716 gtk_widget_show (item);
3717 gtk_widget_show (separator);
3724 menu = priv->popup_widget;
3725 if (priv->add_tearoffs)
3729 if (priv->row_separator_func)
3730 is_separator = priv->row_separator_func (model, iter,
3731 priv->row_separator_data);
3733 is_separator = FALSE;
3737 item = gtk_separator_menu_item_new ();
3738 g_object_set_data_full (G_OBJECT (item),
3739 I_("gtk-combo-box-item-path"),
3740 gtk_tree_row_reference_new (model, path),
3741 (GDestroyNotify)gtk_tree_row_reference_free);
3745 item = gtk_cell_view_menu_item_new (combo_box, model, iter);
3747 g_signal_connect (item, "activate",
3748 G_CALLBACK (gtk_combo_box_menu_item_activate),
3752 gtk_widget_show (item);
3753 gtk_menu_shell_insert (GTK_MENU_SHELL (menu), item, pos);
3757 gtk_combo_box_menu_row_deleted (GtkTreeModel *model,
3761 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3762 GtkComboBoxPrivate *priv = combo_box->priv;
3766 if (!priv->popup_widget)
3769 item = find_menu_by_path (priv->popup_widget, path, FALSE);
3770 menu = gtk_widget_get_parent (item);
3771 gtk_container_remove (GTK_CONTAINER (menu), item);
3773 if (gtk_tree_path_get_depth (path) > 1)
3775 GtkTreePath *parent_path;
3779 parent_path = gtk_tree_path_copy (path);
3780 gtk_tree_path_up (parent_path);
3781 gtk_tree_model_get_iter (model, &iter, parent_path);
3783 if (!gtk_tree_model_iter_has_child (model, &iter))
3785 parent = find_menu_by_path (priv->popup_widget,
3786 parent_path, FALSE);
3787 gtk_menu_item_set_submenu (GTK_MENU_ITEM (parent), NULL);
3793 gtk_combo_box_menu_rows_reordered (GtkTreeModel *model,
3799 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3801 gtk_combo_box_relayout (combo_box);
3805 gtk_combo_box_menu_row_changed (GtkTreeModel *model,
3810 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3811 GtkComboBoxPrivate *priv = combo_box->priv;
3813 gboolean is_separator;
3815 if (!priv->popup_widget)
3818 item = find_menu_by_path (priv->popup_widget, path, FALSE);
3820 if (priv->row_separator_func)
3821 is_separator = priv->row_separator_func (model, iter,
3822 priv->row_separator_data);
3824 is_separator = FALSE;
3826 if (is_separator != GTK_IS_SEPARATOR_MENU_ITEM (item))
3828 gtk_combo_box_menu_row_deleted (model, path, combo_box);
3829 gtk_combo_box_menu_row_inserted (model, path, iter, combo_box);
3832 if (priv->wrap_width &&
3833 gtk_widget_get_parent (item) == priv->popup_widget)
3835 GtkWidget *pitem = NULL;
3838 prev = gtk_tree_path_copy (path);
3840 if (gtk_tree_path_prev (prev))
3841 pitem = find_menu_by_path (priv->popup_widget, prev, FALSE);
3843 gtk_tree_path_free (prev);
3845 /* unattach item so gtk_combo_box_relayout_item() won't spuriously
3847 gtk_container_child_set (GTK_CONTAINER (priv->popup_widget),
3852 "bottom-attach", -1,
3855 gtk_combo_box_relayout_item (combo_box, item, iter, pitem);
3858 gtk_combo_box_update_requested_width (combo_box, path);
3866 gtk_combo_box_list_setup (GtkComboBox *combo_box)
3868 GtkComboBoxPrivate *priv = combo_box->priv;
3869 GtkTreeSelection *sel;
3872 GtkWidget *widget = GTK_WIDGET (combo_box);
3874 priv->button = gtk_toggle_button_new ();
3875 child = gtk_bin_get_child (GTK_BIN (combo_box));
3876 gtk_widget_set_parent (priv->button,
3877 gtk_widget_get_parent (child));
3878 g_signal_connect (priv->button, "button-press-event",
3879 G_CALLBACK (gtk_combo_box_list_button_pressed), combo_box);
3880 g_signal_connect (priv->button, "toggled",
3881 G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
3883 priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3884 gtk_container_add (GTK_CONTAINER (priv->button), priv->arrow);
3885 priv->separator = NULL;
3886 gtk_widget_show_all (priv->button);
3888 if (priv->cell_view)
3890 style = gtk_widget_get_style (widget);
3891 gtk_cell_view_set_background_color (GTK_CELL_VIEW (priv->cell_view),
3892 &style->base[gtk_widget_get_state (widget)]);
3894 priv->box = gtk_event_box_new ();
3895 gtk_event_box_set_visible_window (GTK_EVENT_BOX (priv->box),
3898 if (priv->has_frame)
3900 priv->cell_view_frame = gtk_frame_new (NULL);
3901 gtk_frame_set_shadow_type (GTK_FRAME (priv->cell_view_frame),
3906 combo_box->priv->cell_view_frame = gtk_event_box_new ();
3907 gtk_event_box_set_visible_window (GTK_EVENT_BOX (combo_box->priv->cell_view_frame),
3911 gtk_widget_set_parent (priv->cell_view_frame,
3912 gtk_widget_get_parent (child));
3913 gtk_container_add (GTK_CONTAINER (priv->cell_view_frame), priv->box);
3914 gtk_widget_show_all (priv->cell_view_frame);
3916 g_signal_connect (priv->box, "button-press-event",
3917 G_CALLBACK (gtk_combo_box_list_button_pressed),
3921 priv->tree_view = gtk_tree_view_new ();
3922 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));
3923 gtk_tree_selection_set_mode (sel, GTK_SELECTION_BROWSE);
3924 gtk_tree_selection_set_select_function (sel,
3925 gtk_combo_box_list_select_func,
3927 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->tree_view),
3929 gtk_tree_view_set_hover_selection (GTK_TREE_VIEW (priv->tree_view),
3931 if (priv->row_separator_func)
3932 gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (priv->tree_view),
3933 priv->row_separator_func,
3934 priv->row_separator_data,
3937 gtk_tree_view_set_model (GTK_TREE_VIEW (priv->tree_view), priv->model);
3939 priv->column = gtk_tree_view_column_new ();
3940 gtk_tree_view_append_column (GTK_TREE_VIEW (priv->tree_view), priv->column);
3943 gtk_combo_box_sync_cells (combo_box,
3944 GTK_CELL_LAYOUT (priv->column));
3946 if (gtk_tree_row_reference_valid (priv->active_row))
3950 path = gtk_tree_row_reference_get_path (priv->active_row);
3951 gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->tree_view),
3953 gtk_tree_path_free (path);
3956 /* set sample/popup widgets */
3957 gtk_combo_box_set_popup_widget (combo_box, priv->tree_view);
3959 g_signal_connect (priv->tree_view, "key-press-event",
3960 G_CALLBACK (gtk_combo_box_list_key_press),
3962 g_signal_connect (priv->tree_view, "enter-notify-event",
3963 G_CALLBACK (gtk_combo_box_list_enter_notify),
3965 g_signal_connect (priv->tree_view, "row-expanded",
3966 G_CALLBACK (gtk_combo_box_model_row_expanded),
3968 g_signal_connect (priv->tree_view, "row-collapsed",
3969 G_CALLBACK (gtk_combo_box_model_row_expanded),
3971 g_signal_connect (priv->popup_window, "button-press-event",
3972 G_CALLBACK (gtk_combo_box_list_button_pressed),
3974 g_signal_connect (priv->popup_window, "button-release-event",
3975 G_CALLBACK (gtk_combo_box_list_button_released),
3978 gtk_widget_show (priv->tree_view);
3980 gtk_combo_box_update_sensitivity (combo_box);
3984 gtk_combo_box_list_destroy (GtkComboBox *combo_box)
3986 GtkComboBoxPrivate *priv = combo_box->priv;
3988 /* disconnect signals */
3989 g_signal_handlers_disconnect_matched (priv->tree_view,
3990 G_SIGNAL_MATCH_DATA,
3991 0, 0, NULL, NULL, combo_box);
3992 g_signal_handlers_disconnect_matched (priv->button,
3993 G_SIGNAL_MATCH_DATA,
3995 gtk_combo_box_list_button_pressed,
3997 g_signal_handlers_disconnect_matched (priv->popup_window,
3998 G_SIGNAL_MATCH_DATA,
4000 gtk_combo_box_list_button_pressed,
4002 g_signal_handlers_disconnect_matched (priv->popup_window,
4003 G_SIGNAL_MATCH_DATA,
4005 gtk_combo_box_list_button_released,
4008 g_signal_handlers_disconnect_matched (priv->popup_window,
4009 G_SIGNAL_MATCH_DATA,
4011 gtk_combo_box_child_show,
4014 g_signal_handlers_disconnect_matched (priv->popup_window,
4015 G_SIGNAL_MATCH_DATA,
4017 gtk_combo_box_child_hide,
4021 g_signal_handlers_disconnect_matched (priv->box,
4022 G_SIGNAL_MATCH_DATA,
4024 gtk_combo_box_list_button_pressed,
4027 /* destroy things (unparent will kill the latest ref from us)
4028 * last unref on button will destroy the arrow
4030 gtk_widget_unparent (priv->button);
4031 priv->button = NULL;
4034 if (priv->cell_view)
4036 g_object_set (priv->cell_view,
4037 "background-set", FALSE,
4041 if (priv->cell_view_frame)
4043 gtk_widget_unparent (priv->cell_view_frame);
4044 priv->cell_view_frame = NULL;
4048 if (priv->scroll_timer)
4050 g_source_remove (priv->scroll_timer);
4051 priv->scroll_timer = 0;
4054 if (priv->resize_idle_id)
4056 g_source_remove (priv->resize_idle_id);
4057 priv->resize_idle_id = 0;
4060 gtk_widget_destroy (priv->tree_view);
4062 priv->tree_view = NULL;
4063 if (priv->popup_widget)
4065 g_object_unref (priv->popup_widget);
4066 priv->popup_widget = NULL;
4073 gtk_combo_box_list_button_pressed (GtkWidget *widget,
4074 GdkEventButton *event,
4077 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
4078 GtkComboBoxPrivate *priv = combo_box->priv;
4080 GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *)event);
4082 if (ewidget == priv->popup_window)
4085 if ((ewidget != priv->button && ewidget != priv->box) ||
4086 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->button)))
4089 if (priv->focus_on_click &&
4090 !gtk_widget_has_focus (priv->button))
4091 gtk_widget_grab_focus (priv->button);
4093 gtk_combo_box_popup_for_device (combo_box, event->device);
4095 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button), TRUE);
4097 priv->auto_scroll = FALSE;
4098 if (priv->scroll_timer == 0)
4099 priv->scroll_timer = gdk_threads_add_timeout (SCROLL_TIME,
4100 (GSourceFunc) gtk_combo_box_list_scroll_timeout,
4103 priv->popup_in_progress = TRUE;
4109 gtk_combo_box_list_button_released (GtkWidget *widget,
4110 GdkEventButton *event,
4114 GtkTreePath *path = NULL;
4117 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
4118 GtkComboBoxPrivate *priv = combo_box->priv;
4120 gboolean popup_in_progress = FALSE;
4122 GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *)event);
4124 if (priv->popup_in_progress)
4126 popup_in_progress = TRUE;
4127 priv->popup_in_progress = FALSE;
4130 gtk_tree_view_set_hover_expand (GTK_TREE_VIEW (priv->tree_view),
4132 if (priv->scroll_timer)
4134 g_source_remove (priv->scroll_timer);
4135 priv->scroll_timer = 0;
4138 if (ewidget != priv->tree_view)
4140 if ((ewidget == priv->button ||
4141 ewidget == priv->box) &&
4142 !popup_in_progress &&
4143 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->button)))
4145 gtk_combo_box_popdown (combo_box);
4149 /* released outside treeview */
4150 if (ewidget != priv->button && ewidget != priv->box)
4152 gtk_combo_box_popdown (combo_box);
4160 /* select something cool */
4161 ret = gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (priv->tree_view),
4167 return TRUE; /* clicked outside window? */
4169 gtk_tree_model_get_iter (priv->model, &iter, path);
4170 gtk_tree_path_free (path);
4172 gtk_combo_box_popdown (combo_box);
4174 if (tree_column_row_is_sensitive (combo_box, &iter))
4175 gtk_combo_box_set_active_iter (combo_box, &iter);
4181 gtk_combo_box_menu_key_press (GtkWidget *widget,
4185 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
4187 if (!gtk_bindings_activate_event (G_OBJECT (widget), event))
4189 /* The menu hasn't managed the
4190 * event, forward it to the combobox
4192 gtk_bindings_activate_event (G_OBJECT (combo_box), event);
4199 gtk_combo_box_list_key_press (GtkWidget *widget,
4203 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
4206 if (event->keyval == GDK_KEY_Return || event->keyval == GDK_KEY_ISO_Enter || event->keyval == GDK_KEY_KP_Enter ||
4207 event->keyval == GDK_KEY_space || event->keyval == GDK_KEY_KP_Space)
4209 GtkTreeModel *model = NULL;
4211 gtk_combo_box_popdown (combo_box);
4213 if (combo_box->priv->model)
4215 GtkTreeSelection *sel;
4217 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (combo_box->priv->tree_view));
4219 if (gtk_tree_selection_get_selected (sel, &model, &iter))
4220 gtk_combo_box_set_active_iter (combo_box, &iter);
4226 if (!gtk_bindings_activate_event (G_OBJECT (widget), event))
4228 /* The list hasn't managed the
4229 * event, forward it to the combobox
4231 gtk_bindings_activate_event (G_OBJECT (combo_box), event);
4238 gtk_combo_box_list_auto_scroll (GtkComboBox *combo_box,
4243 GtkAllocation allocation;
4244 GtkWidget *tree_view = combo_box->priv->tree_view;
4247 gtk_widget_get_allocation (tree_view, &allocation);
4249 adj = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window));
4250 if (adj && adj->upper - adj->lower > adj->page_size)
4252 if (x <= allocation.x &&
4253 adj->lower < adj->value)
4255 value = adj->value - (allocation.x - x + 1);
4256 gtk_adjustment_set_value (adj, value);
4258 else if (x >= allocation.x + allocation.width &&
4259 adj->upper - adj->page_size > adj->value)
4261 value = adj->value + (x - allocation.x - allocation.width + 1);
4262 gtk_adjustment_set_value (adj, MAX (value, 0.0));
4266 adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window));
4267 if (adj && adj->upper - adj->lower > adj->page_size)
4269 if (y <= allocation.y &&
4270 adj->lower < adj->value)
4272 value = adj->value - (allocation.y - y + 1);
4273 gtk_adjustment_set_value (adj, value);
4275 else if (y >= allocation.height &&
4276 adj->upper - adj->page_size > adj->value)
4278 value = adj->value + (y - allocation.height + 1);
4279 gtk_adjustment_set_value (adj, MAX (value, 0.0));
4285 gtk_combo_box_list_scroll_timeout (GtkComboBox *combo_box)
4287 GtkComboBoxPrivate *priv = combo_box->priv;
4290 if (priv->auto_scroll)
4292 gdk_window_get_device_position (gtk_widget_get_window (priv->tree_view),
4295 gtk_combo_box_list_auto_scroll (combo_box, x, y);
4302 gtk_combo_box_list_enter_notify (GtkWidget *widget,
4303 GdkEventCrossing *event,
4306 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
4308 combo_box->priv->auto_scroll = TRUE;
4314 gtk_combo_box_list_select_func (GtkTreeSelection *selection,
4315 GtkTreeModel *model,
4317 gboolean path_currently_selected,
4321 gboolean sensitive = FALSE;
4323 for (list = selection->tree_view->priv->columns; list && !sensitive; list = list->next)
4325 GList *cells, *cell;
4326 gboolean cell_sensitive, cell_visible;
4328 GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN (list->data);
4330 if (!gtk_tree_view_column_get_visible (column))
4333 gtk_tree_model_get_iter (model, &iter, path);
4334 gtk_tree_view_column_cell_set_cell_data (column, model, &iter,
4337 cell = cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
4340 g_object_get (cell->data,
4341 "sensitive", &cell_sensitive,
4342 "visible", &cell_visible,
4345 if (cell_visible && cell_sensitive)
4350 g_list_free (cells);
4352 sensitive = cell_sensitive;
4359 gtk_combo_box_list_row_changed (GtkTreeModel *model,
4364 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
4366 gtk_combo_box_update_requested_width (combo_box, path);
4370 * GtkCellLayout implementation
4374 pack_start_recurse (GtkWidget *menu,
4375 GtkCellRenderer *cell,
4382 list = gtk_container_get_children (GTK_CONTAINER (menu));
4383 for (i = list; i; i = i->next)
4385 child = gtk_bin_get_child (GTK_BIN (i->data));
4386 if (GTK_IS_CELL_LAYOUT (child))
4387 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (child),
4390 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4391 if (submenu != NULL)
4392 pack_start_recurse (submenu, cell, expand);
4399 gtk_combo_box_cell_layout_pack_start (GtkCellLayout *layout,
4400 GtkCellRenderer *cell,
4403 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4404 ComboCellInfo *info;
4405 GtkComboBoxPrivate *priv;
4407 priv = combo_box->priv;
4409 g_object_ref_sink (cell);
4411 info = g_slice_new0 (ComboCellInfo);
4413 info->expand = expand;
4414 info->pack = GTK_PACK_START;
4416 priv->cells = g_slist_append (priv->cells, info);
4418 if (priv->cell_view)
4420 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->cell_view),
4426 gtk_tree_view_column_pack_start (priv->column, cell, expand);
4428 if (GTK_IS_MENU (priv->popup_widget))
4429 pack_start_recurse (priv->popup_widget, cell, expand);
4433 pack_end_recurse (GtkWidget *menu,
4434 GtkCellRenderer *cell,
4441 list = gtk_container_get_children (GTK_CONTAINER (menu));
4442 for (i = list; i; i = i->next)
4444 child = gtk_bin_get_child (GTK_BIN (i->data));
4445 if (GTK_IS_CELL_LAYOUT (child))
4446 gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (child),
4449 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4450 if (submenu != NULL)
4451 pack_end_recurse (submenu, cell, expand);
4458 gtk_combo_box_cell_layout_pack_end (GtkCellLayout *layout,
4459 GtkCellRenderer *cell,
4462 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4463 ComboCellInfo *info;
4464 GtkComboBoxPrivate *priv;
4466 priv = combo_box->priv;
4468 g_object_ref_sink (cell);
4470 info = g_slice_new0 (ComboCellInfo);
4472 info->expand = expand;
4473 info->pack = GTK_PACK_END;
4475 priv->cells = g_slist_append (priv->cells, info);
4477 if (priv->cell_view)
4478 gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (priv->cell_view),
4482 gtk_tree_view_column_pack_end (priv->column, cell, expand);
4484 if (GTK_IS_MENU (priv->popup_widget))
4485 pack_end_recurse (priv->popup_widget, cell, expand);
4489 gtk_combo_box_cell_layout_get_cells (GtkCellLayout *layout)
4491 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4493 GList *retval = NULL;
4495 for (list = combo_box->priv->cells; list; list = list->next)
4497 ComboCellInfo *info = (ComboCellInfo *)list->data;
4499 retval = g_list_prepend (retval, info->cell);
4502 return g_list_reverse (retval);
4506 clear_recurse (GtkWidget *menu)
4512 list = gtk_container_get_children (GTK_CONTAINER (menu));
4513 for (i = list; i; i = i->next)
4515 child = gtk_bin_get_child (GTK_BIN (i->data));
4516 if (GTK_IS_CELL_LAYOUT (child))
4517 gtk_cell_layout_clear (GTK_CELL_LAYOUT (child));
4519 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4520 if (submenu != NULL)
4521 clear_recurse (submenu);
4528 gtk_combo_box_cell_layout_clear (GtkCellLayout *layout)
4530 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4531 GtkComboBoxPrivate *priv = combo_box->priv;
4534 if (priv->cell_view)
4535 gtk_cell_layout_clear (GTK_CELL_LAYOUT (priv->cell_view));
4538 gtk_tree_view_column_clear (priv->column);
4540 for (i = priv->cells; i; i = i->next)
4542 ComboCellInfo *info = (ComboCellInfo *)i->data;
4544 gtk_combo_box_cell_layout_clear_attributes (layout, info->cell);
4545 g_object_unref (info->cell);
4546 g_slice_free (ComboCellInfo, info);
4549 g_slist_free (priv->cells);
4552 if (GTK_IS_MENU (priv->popup_widget))
4553 clear_recurse (priv->popup_widget);
4557 add_attribute_recurse (GtkWidget *menu,
4558 GtkCellRenderer *cell,
4559 const gchar *attribute,
4566 list = gtk_container_get_children (GTK_CONTAINER (menu));
4567 for (i = list; i; i = i->next)
4569 child = gtk_bin_get_child (GTK_BIN (i->data));
4570 if (GTK_IS_CELL_LAYOUT (child))
4571 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (child),
4572 cell, attribute, column);
4574 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4575 if (submenu != NULL)
4576 add_attribute_recurse (submenu, cell, attribute, column);
4583 gtk_combo_box_cell_layout_add_attribute (GtkCellLayout *layout,
4584 GtkCellRenderer *cell,
4585 const gchar *attribute,
4588 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4589 ComboCellInfo *info;
4591 info = gtk_combo_box_get_cell_info (combo_box, cell);
4592 g_return_if_fail (info != NULL);
4594 info->attributes = g_slist_prepend (info->attributes,
4595 GINT_TO_POINTER (column));
4596 info->attributes = g_slist_prepend (info->attributes,
4597 g_strdup (attribute));
4599 if (combo_box->priv->cell_view)
4600 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box->priv->cell_view),
4601 cell, attribute, column);
4603 if (combo_box->priv->column)
4604 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box->priv->column),
4605 cell, attribute, column);
4607 if (GTK_IS_MENU (combo_box->priv->popup_widget))
4608 add_attribute_recurse (combo_box->priv->popup_widget, cell, attribute, column);
4609 gtk_widget_queue_resize (GTK_WIDGET (combo_box));
4613 combo_cell_data_func (GtkCellLayout *cell_layout,
4614 GtkCellRenderer *cell,
4615 GtkTreeModel *tree_model,
4619 ComboCellInfo *info = (ComboCellInfo *)data;
4620 GtkWidget *parent = NULL;
4625 info->func (cell_layout, cell, tree_model, iter, info->func_data);
4627 if (GTK_IS_WIDGET (cell_layout))
4628 parent = gtk_widget_get_parent (GTK_WIDGET (cell_layout));
4630 if (GTK_IS_MENU_ITEM (parent) &&
4631 gtk_menu_item_get_submenu (GTK_MENU_ITEM (parent)))
4632 g_object_set (cell, "sensitive", TRUE, NULL);
4637 set_cell_data_func_recurse (GtkWidget *menu,
4638 GtkCellRenderer *cell,
4639 ComboCellInfo *info)
4643 GtkWidget *cell_view;
4645 list = gtk_container_get_children (GTK_CONTAINER (menu));
4646 for (i = list; i; i = i->next)
4648 cell_view = gtk_bin_get_child (GTK_BIN (i->data));
4649 if (GTK_IS_CELL_LAYOUT (cell_view))
4651 /* Override sensitivity for inner nodes; we don't
4652 * want menuitems with submenus to appear insensitive
4654 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (cell_view),
4656 combo_cell_data_func,
4658 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4659 if (submenu != NULL)
4660 set_cell_data_func_recurse (submenu, cell, info);
4668 gtk_combo_box_cell_layout_set_cell_data_func (GtkCellLayout *layout,
4669 GtkCellRenderer *cell,
4670 GtkCellLayoutDataFunc func,
4672 GDestroyNotify destroy)
4674 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4675 GtkComboBoxPrivate *priv = combo_box->priv;
4676 ComboCellInfo *info;
4678 info = gtk_combo_box_get_cell_info (combo_box, cell);
4679 g_return_if_fail (info != NULL);
4683 GDestroyNotify d = info->destroy;
4685 info->destroy = NULL;
4686 d (info->func_data);
4690 info->func_data = func_data;
4691 info->destroy = destroy;
4693 if (priv->cell_view)
4694 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (priv->cell_view), cell, func, func_data, NULL);
4697 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (priv->column), cell, func, func_data, NULL);
4699 if (GTK_IS_MENU (priv->popup_widget))
4700 set_cell_data_func_recurse (priv->popup_widget, cell, info);
4702 gtk_widget_queue_resize (GTK_WIDGET (combo_box));
4706 clear_attributes_recurse (GtkWidget *menu,
4707 GtkCellRenderer *cell)
4713 list = gtk_container_get_children (GTK_CONTAINER (menu));
4714 for (i = list; i; i = i->next)
4716 child = gtk_bin_get_child (GTK_BIN (i->data));
4717 if (GTK_IS_CELL_LAYOUT (child))
4718 gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (child), cell);
4720 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4721 if (submenu != NULL)
4722 clear_attributes_recurse (submenu, cell);
4729 gtk_combo_box_cell_layout_clear_attributes (GtkCellLayout *layout,
4730 GtkCellRenderer *cell)
4732 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4733 GtkComboBoxPrivate *priv;
4734 ComboCellInfo *info;
4737 priv = combo_box->priv;
4739 info = gtk_combo_box_get_cell_info (combo_box, cell);
4740 g_return_if_fail (info != NULL);
4742 list = info->attributes;
4743 while (list && list->next)
4745 g_free (list->data);
4746 list = list->next->next;
4748 g_slist_free (info->attributes);
4749 info->attributes = NULL;
4751 if (priv->cell_view)
4752 gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (priv->cell_view), cell);
4755 gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (priv->column), cell);
4757 if (GTK_IS_MENU (priv->popup_widget))
4758 clear_attributes_recurse (priv->popup_widget, cell);
4760 gtk_widget_queue_resize (GTK_WIDGET (combo_box));
4764 reorder_recurse (GtkWidget *menu,
4765 GtkCellRenderer *cell,
4772 list = gtk_container_get_children (GTK_CONTAINER (menu));
4773 for (i = list; i; i = i->next)
4775 child = gtk_bin_get_child (GTK_BIN (i->data));
4776 if (GTK_IS_CELL_LAYOUT (child))
4777 gtk_cell_layout_reorder (GTK_CELL_LAYOUT (child),
4780 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4781 if (submenu != NULL)
4782 reorder_recurse (submenu, cell, position);
4789 gtk_combo_box_cell_layout_reorder (GtkCellLayout *layout,
4790 GtkCellRenderer *cell,
4793 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4794 GtkComboBoxPrivate *priv;
4795 ComboCellInfo *info;
4798 priv = combo_box->priv;
4800 info = gtk_combo_box_get_cell_info (combo_box, cell);
4802 g_return_if_fail (info != NULL);
4803 g_return_if_fail (position >= 0);
4805 link = g_slist_find (priv->cells, info);
4807 g_return_if_fail (link != NULL);
4809 priv->cells = g_slist_delete_link (priv->cells, link);
4810 priv->cells = g_slist_insert (priv->cells, info, position);
4812 if (priv->cell_view)
4813 gtk_cell_layout_reorder (GTK_CELL_LAYOUT (priv->cell_view),
4817 gtk_cell_layout_reorder (GTK_CELL_LAYOUT (priv->column),
4820 if (GTK_IS_MENU (priv->popup_widget))
4821 reorder_recurse (priv->popup_widget, cell, position);
4823 gtk_widget_queue_draw (GTK_WIDGET (combo_box));
4831 * gtk_combo_box_new:
4833 * Creates a new empty #GtkComboBox.
4835 * Return value: A new #GtkComboBox.
4840 gtk_combo_box_new (void)
4842 return g_object_new (GTK_TYPE_COMBO_BOX, NULL);
4846 * gtk_combo_box_new_with_entry:
4848 * Creates a new empty #GtkComboBox with an entry.
4850 * Return value: A new #GtkComboBox.
4853 gtk_combo_box_new_with_entry (void)
4855 return g_object_new (GTK_TYPE_COMBO_BOX, "has-entry", TRUE, NULL);
4859 * gtk_combo_box_new_with_model:
4860 * @model: A #GtkTreeModel.
4862 * Creates a new #GtkComboBox with the model initialized to @model.
4864 * Return value: A new #GtkComboBox.
4869 gtk_combo_box_new_with_model (GtkTreeModel *model)
4871 GtkComboBox *combo_box;
4873 g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL);
4875 combo_box = g_object_new (GTK_TYPE_COMBO_BOX, "model", model, NULL);
4877 return GTK_WIDGET (combo_box);
4881 * gtk_combo_box_new_with_model_and_entry:
4883 * Creates a new empty #GtkComboBox with an entry
4884 * and with the model initialized to @model.
4886 * Return value: A new #GtkComboBox
4889 gtk_combo_box_new_with_model_and_entry (GtkTreeModel *model)
4891 return g_object_new (GTK_TYPE_COMBO_BOX,
4898 * gtk_combo_box_get_wrap_width:
4899 * @combo_box: A #GtkComboBox
4901 * Returns the wrap width which is used to determine the number of columns
4902 * for the popup menu. If the wrap width is larger than 1, the combo box
4905 * Returns: the wrap width.
4910 gtk_combo_box_get_wrap_width (GtkComboBox *combo_box)
4912 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), -1);
4914 return combo_box->priv->wrap_width;
4918 * gtk_combo_box_set_wrap_width:
4919 * @combo_box: A #GtkComboBox
4920 * @width: Preferred number of columns
4922 * Sets the wrap width of @combo_box to be @width. The wrap width is basically
4923 * the preferred number of columns when you want the popup to be layed out
4929 gtk_combo_box_set_wrap_width (GtkComboBox *combo_box,
4932 GtkComboBoxPrivate *priv;
4934 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4935 g_return_if_fail (width >= 0);
4937 priv = combo_box->priv;
4939 if (width != priv->wrap_width)
4941 priv->wrap_width = width;
4943 gtk_combo_box_check_appearance (combo_box);
4944 gtk_combo_box_relayout (combo_box);
4946 g_object_notify (G_OBJECT (combo_box), "wrap-width");
4951 * gtk_combo_box_get_row_span_column:
4952 * @combo_box: A #GtkComboBox
4954 * Returns the column with row span information for @combo_box.
4956 * Returns: the row span column.
4961 gtk_combo_box_get_row_span_column (GtkComboBox *combo_box)
4963 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), -1);
4965 return combo_box->priv->row_column;
4969 * gtk_combo_box_set_row_span_column:
4970 * @combo_box: A #GtkComboBox.
4971 * @row_span: A column in the model passed during construction.
4973 * Sets the column with row span information for @combo_box to be @row_span.
4974 * The row span column contains integers which indicate how many rows
4975 * an item should span.
4980 gtk_combo_box_set_row_span_column (GtkComboBox *combo_box,
4983 GtkComboBoxPrivate *priv;
4986 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4988 priv = combo_box->priv;
4990 col = gtk_tree_model_get_n_columns (priv->model);
4991 g_return_if_fail (row_span >= -1 && row_span < col);
4993 if (row_span != priv->row_column)
4995 priv->row_column = row_span;
4997 gtk_combo_box_relayout (combo_box);
4999 g_object_notify (G_OBJECT (combo_box), "row-span-column");
5004 * gtk_combo_box_get_column_span_column:
5005 * @combo_box: A #GtkComboBox
5007 * Returns the column with column span information for @combo_box.
5009 * Returns: the column span column.
5014 gtk_combo_box_get_column_span_column (GtkComboBox *combo_box)
5016 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), -1);
5018 return combo_box->priv->col_column;
5022 * gtk_combo_box_set_column_span_column:
5023 * @combo_box: A #GtkComboBox
5024 * @column_span: A column in the model passed during construction
5026 * Sets the column with column span information for @combo_box to be
5027 * @column_span. The column span column contains integers which indicate
5028 * how many columns an item should span.
5033 gtk_combo_box_set_column_span_column (GtkComboBox *combo_box,
5036 GtkComboBoxPrivate *priv;
5039 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5041 priv = combo_box->priv;
5043 col = gtk_tree_model_get_n_columns (priv->model);
5044 g_return_if_fail (column_span >= -1 && column_span < col);
5046 if (column_span != priv->col_column)
5048 priv->col_column = column_span;
5050 gtk_combo_box_relayout (combo_box);
5052 g_object_notify (G_OBJECT (combo_box), "column-span-column");
5057 * gtk_combo_box_get_active:
5058 * @combo_box: A #GtkComboBox
5060 * Returns the index of the currently active item, or -1 if there's no
5061 * active item. If the model is a non-flat treemodel, and the active item
5062 * is not an immediate child of the root of the tree, this function returns
5063 * <literal>gtk_tree_path_get_indices (path)[0]</literal>, where
5064 * <literal>path</literal> is the #GtkTreePath of the active item.
5066 * Return value: An integer which is the index of the currently active item,
5067 * or -1 if there's no active item.
5072 gtk_combo_box_get_active (GtkComboBox *combo_box)
5074 GtkComboBoxPrivate *priv;
5077 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), 0);
5079 priv = combo_box->priv;
5081 if (gtk_tree_row_reference_valid (priv->active_row))
5085 path = gtk_tree_row_reference_get_path (priv->active_row);
5086 result = gtk_tree_path_get_indices (path)[0];
5087 gtk_tree_path_free (path);
5096 * gtk_combo_box_set_active:
5097 * @combo_box: A #GtkComboBox
5098 * @index_: An index in the model passed during construction, or -1 to have
5101 * Sets the active item of @combo_box to be the item at @index.
5106 gtk_combo_box_set_active (GtkComboBox *combo_box,
5109 GtkTreePath *path = NULL;
5110 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5111 g_return_if_fail (index_ >= -1);
5113 if (combo_box->priv->model == NULL)
5115 /* Save index, in case the model is set after the index */
5116 combo_box->priv->active = index_;
5122 path = gtk_tree_path_new_from_indices (index_, -1);
5124 gtk_combo_box_set_active_internal (combo_box, path);
5127 gtk_tree_path_free (path);
5131 gtk_combo_box_set_active_internal (GtkComboBox *combo_box,
5134 GtkComboBoxPrivate *priv = combo_box->priv;
5135 GtkTreePath *active_path;
5138 /* Remember whether the initially active row is valid. */
5139 gboolean is_valid_row_reference = gtk_tree_row_reference_valid (priv->active_row);
5141 if (path && is_valid_row_reference)
5143 active_path = gtk_tree_row_reference_get_path (priv->active_row);
5144 path_cmp = gtk_tree_path_compare (path, active_path);
5145 gtk_tree_path_free (active_path);
5150 if (priv->active_row)
5152 gtk_tree_row_reference_free (priv->active_row);
5153 priv->active_row = NULL;
5158 if (priv->tree_view)
5159 gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view)));
5162 GtkMenu *menu = GTK_MENU (priv->popup_widget);
5164 if (GTK_IS_MENU (menu))
5165 gtk_menu_set_active (menu, -1);
5168 if (priv->cell_view)
5169 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (priv->cell_view), NULL);
5172 * Do not emit a "changed" signal when an already invalid selection was
5173 * now set to invalid.
5175 if (!is_valid_row_reference)
5181 gtk_tree_row_reference_new (priv->model, path);
5183 if (priv->tree_view)
5185 gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->tree_view),
5188 else if (GTK_IS_MENU (priv->popup_widget))
5190 /* FIXME handle nested menus better */
5191 gtk_menu_set_active (GTK_MENU (priv->popup_widget),
5192 gtk_tree_path_get_indices (path)[0]);
5195 if (priv->cell_view)
5196 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (priv->cell_view),
5200 g_signal_emit (combo_box, combo_box_signals[CHANGED], 0);
5201 g_object_notify (G_OBJECT (combo_box), "active");
5206 * gtk_combo_box_get_active_iter:
5207 * @combo_box: A #GtkComboBox
5208 * @iter: (out): The uninitialized #GtkTreeIter
5210 * Sets @iter to point to the current active item, if it exists.
5212 * Return value: %TRUE, if @iter was set
5217 gtk_combo_box_get_active_iter (GtkComboBox *combo_box,
5223 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
5225 if (!gtk_tree_row_reference_valid (combo_box->priv->active_row))
5228 path = gtk_tree_row_reference_get_path (combo_box->priv->active_row);
5229 result = gtk_tree_model_get_iter (combo_box->priv->model, iter, path);
5230 gtk_tree_path_free (path);
5236 * gtk_combo_box_set_active_iter:
5237 * @combo_box: A #GtkComboBox
5238 * @iter: (allow-none): The #GtkTreeIter, or %NULL
5240 * Sets the current active item to be the one referenced by @iter, or
5241 * unsets the active item if @iter is %NULL.
5246 gtk_combo_box_set_active_iter (GtkComboBox *combo_box,
5249 GtkTreePath *path = NULL;
5251 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5254 path = gtk_tree_model_get_path (gtk_combo_box_get_model (combo_box), iter);
5256 gtk_combo_box_set_active_internal (combo_box, path);
5257 gtk_tree_path_free (path);
5261 * gtk_combo_box_set_model:
5262 * @combo_box: A #GtkComboBox
5263 * @model: (allow-none): A #GtkTreeModel
5265 * Sets the model used by @combo_box to be @model. Will unset a previously set
5266 * model (if applicable). If model is %NULL, then it will unset the model.
5268 * Note that this function does not clear the cell renderers, you have to
5269 * call gtk_cell_layout_clear() yourself if you need to set up different
5270 * cell renderers for the new model.
5275 gtk_combo_box_set_model (GtkComboBox *combo_box,
5276 GtkTreeModel *model)
5278 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5279 g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
5281 if (model == combo_box->priv->model)
5284 gtk_combo_box_unset_model (combo_box);
5289 combo_box->priv->model = model;
5290 g_object_ref (combo_box->priv->model);
5292 combo_box->priv->inserted_id =
5293 g_signal_connect (combo_box->priv->model, "row-inserted",
5294 G_CALLBACK (gtk_combo_box_model_row_inserted),
5296 combo_box->priv->deleted_id =
5297 g_signal_connect (combo_box->priv->model, "row-deleted",
5298 G_CALLBACK (gtk_combo_box_model_row_deleted),
5300 combo_box->priv->reordered_id =
5301 g_signal_connect (combo_box->priv->model, "rows-reordered",
5302 G_CALLBACK (gtk_combo_box_model_rows_reordered),
5304 combo_box->priv->changed_id =
5305 g_signal_connect (combo_box->priv->model, "row-changed",
5306 G_CALLBACK (gtk_combo_box_model_row_changed),
5309 if (combo_box->priv->tree_view)
5312 gtk_tree_view_set_model (GTK_TREE_VIEW (combo_box->priv->tree_view),
5313 combo_box->priv->model);
5314 gtk_combo_box_list_popup_resize (combo_box);
5319 if (combo_box->priv->popup_widget)
5320 gtk_combo_box_menu_fill (combo_box);
5324 if (combo_box->priv->cell_view)
5325 gtk_cell_view_set_model (GTK_CELL_VIEW (combo_box->priv->cell_view),
5326 combo_box->priv->model);
5328 if (combo_box->priv->active != -1)
5330 /* If an index was set in advance, apply it now */
5331 gtk_combo_box_set_active (combo_box, combo_box->priv->active);
5332 combo_box->priv->active = -1;
5336 gtk_combo_box_update_sensitivity (combo_box);
5338 g_object_notify (G_OBJECT (combo_box), "model");
5342 * gtk_combo_box_get_model:
5343 * @combo_box: A #GtkComboBox
5345 * Returns the #GtkTreeModel which is acting as data source for @combo_box.
5347 * Return value: (transfer none): A #GtkTreeModel which was passed
5348 * during construction.
5353 gtk_combo_box_get_model (GtkComboBox *combo_box)
5355 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
5357 return combo_box->priv->model;
5361 gtk_combo_box_real_move_active (GtkComboBox *combo_box,
5362 GtkScrollType scroll)
5365 GtkTreeIter new_iter;
5366 gboolean active_iter;
5369 if (!combo_box->priv->model)
5371 gtk_widget_error_bell (GTK_WIDGET (combo_box));
5375 active_iter = gtk_combo_box_get_active_iter (combo_box, &iter);
5379 case GTK_SCROLL_STEP_BACKWARD:
5380 case GTK_SCROLL_STEP_UP:
5381 case GTK_SCROLL_STEP_LEFT:
5384 found = tree_prev (combo_box, combo_box->priv->model,
5385 &iter, &new_iter, FALSE);
5388 /* else fall through */
5390 case GTK_SCROLL_PAGE_FORWARD:
5391 case GTK_SCROLL_PAGE_DOWN:
5392 case GTK_SCROLL_PAGE_RIGHT:
5393 case GTK_SCROLL_END:
5394 found = tree_last (combo_box, combo_box->priv->model, &new_iter, FALSE);
5397 case GTK_SCROLL_STEP_FORWARD:
5398 case GTK_SCROLL_STEP_DOWN:
5399 case GTK_SCROLL_STEP_RIGHT:
5402 found = tree_next (combo_box, combo_box->priv->model,
5403 &iter, &new_iter, FALSE);
5406 /* else fall through */
5408 case GTK_SCROLL_PAGE_BACKWARD:
5409 case GTK_SCROLL_PAGE_UP:
5410 case GTK_SCROLL_PAGE_LEFT:
5411 case GTK_SCROLL_START:
5412 found = tree_first (combo_box, combo_box->priv->model, &new_iter, FALSE);
5419 if (found && active_iter)
5421 GtkTreePath *old_path;
5422 GtkTreePath *new_path;
5424 old_path = gtk_tree_model_get_path (combo_box->priv->model, &iter);
5425 new_path = gtk_tree_model_get_path (combo_box->priv->model, &new_iter);
5427 if (gtk_tree_path_compare (old_path, new_path) == 0)
5430 gtk_tree_path_free (old_path);
5431 gtk_tree_path_free (new_path);
5436 gtk_combo_box_set_active_iter (combo_box, &new_iter);
5440 gtk_widget_error_bell (GTK_WIDGET (combo_box));
5445 gtk_combo_box_mnemonic_activate (GtkWidget *widget,
5446 gboolean group_cycling)
5448 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
5450 if (combo_box->priv->has_entry)
5454 child = gtk_bin_get_child (GTK_BIN (combo_box));
5456 gtk_widget_grab_focus (child);
5459 gtk_widget_grab_focus (combo_box->priv->button);
5465 gtk_combo_box_grab_focus (GtkWidget *widget)
5467 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
5469 if (combo_box->priv->has_entry)
5473 child = gtk_bin_get_child (GTK_BIN (combo_box));
5475 gtk_widget_grab_focus (child);
5478 gtk_widget_grab_focus (combo_box->priv->button);
5482 gtk_combo_box_destroy (GtkWidget *widget)
5484 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
5486 if (combo_box->priv->popup_idle_id > 0)
5488 g_source_remove (combo_box->priv->popup_idle_id);
5489 combo_box->priv->popup_idle_id = 0;
5492 gtk_combo_box_popdown (combo_box);
5494 if (combo_box->priv->row_separator_destroy)
5495 combo_box->priv->row_separator_destroy (combo_box->priv->row_separator_data);
5497 combo_box->priv->row_separator_func = NULL;
5498 combo_box->priv->row_separator_data = NULL;
5499 combo_box->priv->row_separator_destroy = NULL;
5501 GTK_WIDGET_CLASS (gtk_combo_box_parent_class)->destroy (widget);
5502 combo_box->priv->cell_view = NULL;
5506 gtk_combo_box_entry_contents_changed (GtkEntry *entry,
5509 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
5512 * Fixes regression reported in bug #574059. The old functionality relied on
5513 * bug #572478. As a bugfix, we now emit the "changed" signal ourselves
5514 * when the selection was already set to -1.
5516 if (gtk_combo_box_get_active(combo_box) == -1)
5517 g_signal_emit_by_name (combo_box, "changed");
5519 gtk_combo_box_set_active (combo_box, -1);
5523 gtk_combo_box_entry_active_changed (GtkComboBox *combo_box,
5526 GtkComboBoxPrivate *priv = combo_box->priv;
5527 GtkTreeModel *model;
5530 if (gtk_combo_box_get_active_iter (combo_box, &iter))
5532 GtkEntry *entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (combo_box)));
5536 GValue value = {0,};
5538 g_signal_handlers_block_by_func (entry,
5539 gtk_combo_box_entry_contents_changed,
5542 model = gtk_combo_box_get_model (combo_box);
5544 gtk_tree_model_get_value (model, &iter,
5545 priv->text_column, &value);
5546 g_object_set_property (G_OBJECT (entry), "text", &value);
5547 g_value_unset (&value);
5549 g_signal_handlers_unblock_by_func (entry,
5550 gtk_combo_box_entry_contents_changed,
5557 gtk_combo_box_constructor (GType type,
5558 guint n_construct_properties,
5559 GObjectConstructParam *construct_properties)
5562 GtkComboBox *combo_box;
5563 GtkComboBoxPrivate *priv;
5565 object = G_OBJECT_CLASS (gtk_combo_box_parent_class)->constructor
5566 (type, n_construct_properties, construct_properties);
5568 combo_box = GTK_COMBO_BOX (object);
5569 priv = combo_box->priv;
5571 if (priv->has_entry)
5575 entry = gtk_entry_new ();
5576 gtk_widget_show (entry);
5577 gtk_container_add (GTK_CONTAINER (combo_box), entry);
5579 priv->text_renderer = gtk_cell_renderer_text_new ();
5580 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box),
5581 priv->text_renderer, TRUE);
5583 gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), -1);
5585 g_signal_connect (combo_box, "changed",
5586 G_CALLBACK (gtk_combo_box_entry_active_changed), NULL);
5594 gtk_combo_box_dispose(GObject* object)
5596 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
5598 if (GTK_IS_MENU (combo_box->priv->popup_widget))
5600 gtk_combo_box_menu_destroy (combo_box);
5601 gtk_menu_detach (GTK_MENU (combo_box->priv->popup_widget));
5602 combo_box->priv->popup_widget = NULL;
5605 G_OBJECT_CLASS (gtk_combo_box_parent_class)->dispose (object);
5609 gtk_combo_box_finalize (GObject *object)
5611 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
5614 if (GTK_IS_TREE_VIEW (combo_box->priv->tree_view))
5615 gtk_combo_box_list_destroy (combo_box);
5617 if (combo_box->priv->popup_window)
5618 gtk_widget_destroy (combo_box->priv->popup_window);
5620 gtk_combo_box_unset_model (combo_box);
5622 for (i = combo_box->priv->cells; i; i = i->next)
5624 ComboCellInfo *info = (ComboCellInfo *)i->data;
5625 GSList *list = info->attributes;
5628 info->destroy (info->func_data);
5630 while (list && list->next)
5632 g_free (list->data);
5633 list = list->next->next;
5635 g_slist_free (info->attributes);
5637 g_object_unref (info->cell);
5638 g_slice_free (ComboCellInfo, info);
5640 g_slist_free (combo_box->priv->cells);
5642 g_free (combo_box->priv->tearoff_title);
5644 G_OBJECT_CLASS (gtk_combo_box_parent_class)->finalize (object);
5649 gtk_cell_editable_key_press (GtkWidget *widget,
5653 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
5655 if (event->keyval == GDK_KEY_Escape)
5657 g_object_set (combo_box,
5658 "editing-canceled", TRUE,
5660 gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (combo_box));
5661 gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (combo_box));
5665 else if (event->keyval == GDK_KEY_Return ||
5666 event->keyval == GDK_KEY_ISO_Enter ||
5667 event->keyval == GDK_KEY_KP_Enter)
5669 gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (combo_box));
5670 gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (combo_box));
5679 popdown_idle (gpointer data)
5681 GtkComboBox *combo_box;
5683 combo_box = GTK_COMBO_BOX (data);
5685 gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (combo_box));
5686 gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (combo_box));
5688 g_object_unref (combo_box);
5694 popdown_handler (GtkWidget *widget,
5697 gdk_threads_add_idle (popdown_idle, g_object_ref (data));
5701 popup_idle (gpointer data)
5703 GtkComboBox *combo_box;
5705 combo_box = GTK_COMBO_BOX (data);
5707 if (GTK_IS_MENU (combo_box->priv->popup_widget) &&
5708 combo_box->priv->cell_view)
5709 g_signal_connect_object (combo_box->priv->popup_widget,
5710 "unmap", G_CALLBACK (popdown_handler),
5713 /* we unset this if a menu item is activated */
5714 g_object_set (combo_box,
5715 "editing-canceled", TRUE,
5717 gtk_combo_box_popup (combo_box);
5719 combo_box->priv->popup_idle_id = 0;
5720 combo_box->priv->activate_button = 0;
5721 combo_box->priv->activate_time = 0;
5727 gtk_combo_box_start_editing (GtkCellEditable *cell_editable,
5730 GtkComboBox *combo_box = GTK_COMBO_BOX (cell_editable);
5733 combo_box->priv->is_cell_renderer = TRUE;
5735 if (combo_box->priv->cell_view)
5737 g_signal_connect_object (combo_box->priv->button, "key-press-event",
5738 G_CALLBACK (gtk_cell_editable_key_press),
5741 gtk_widget_grab_focus (combo_box->priv->button);
5745 child = gtk_bin_get_child (GTK_BIN (combo_box));
5747 g_signal_connect_object (child, "key-press-event",
5748 G_CALLBACK (gtk_cell_editable_key_press),
5751 gtk_widget_grab_focus (child);
5752 gtk_widget_set_can_focus (combo_box->priv->button, FALSE);
5755 /* we do the immediate popup only for the optionmenu-like
5758 if (combo_box->priv->is_cell_renderer &&
5759 combo_box->priv->cell_view && !combo_box->priv->tree_view)
5761 if (event && event->type == GDK_BUTTON_PRESS)
5763 GdkEventButton *event_button = (GdkEventButton *)event;
5765 combo_box->priv->activate_button = event_button->button;
5766 combo_box->priv->activate_time = event_button->time;
5769 combo_box->priv->popup_idle_id =
5770 gdk_threads_add_idle (popup_idle, combo_box);
5776 * gtk_combo_box_get_add_tearoffs:
5777 * @combo_box: a #GtkComboBox
5779 * Gets the current value of the :add-tearoffs property.
5781 * Return value: the current value of the :add-tearoffs property.
5784 gtk_combo_box_get_add_tearoffs (GtkComboBox *combo_box)
5786 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
5788 return combo_box->priv->add_tearoffs;
5792 * gtk_combo_box_set_add_tearoffs:
5793 * @combo_box: a #GtkComboBox
5794 * @add_tearoffs: %TRUE to add tearoff menu items
5796 * Sets whether the popup menu should have a tearoff
5802 gtk_combo_box_set_add_tearoffs (GtkComboBox *combo_box,
5803 gboolean add_tearoffs)
5805 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5807 add_tearoffs = add_tearoffs != FALSE;
5809 if (combo_box->priv->add_tearoffs != add_tearoffs)
5811 combo_box->priv->add_tearoffs = add_tearoffs;
5812 gtk_combo_box_check_appearance (combo_box);
5813 gtk_combo_box_relayout (combo_box);
5814 g_object_notify (G_OBJECT (combo_box), "add-tearoffs");
5819 * gtk_combo_box_get_title:
5820 * @combo_box: a #GtkComboBox
5822 * Gets the current title of the menu in tearoff mode. See
5823 * gtk_combo_box_set_add_tearoffs().
5825 * Returns: the menu's title in tearoff mode. This is an internal copy of the
5826 * string which must not be freed.
5830 G_CONST_RETURN gchar*
5831 gtk_combo_box_get_title (GtkComboBox *combo_box)
5833 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
5835 return combo_box->priv->tearoff_title;
5839 gtk_combo_box_update_title (GtkComboBox *combo_box)
5841 gtk_combo_box_check_appearance (combo_box);
5843 if (combo_box->priv->popup_widget &&
5844 GTK_IS_MENU (combo_box->priv->popup_widget))
5845 gtk_menu_set_title (GTK_MENU (combo_box->priv->popup_widget),
5846 combo_box->priv->tearoff_title);
5850 * gtk_combo_box_set_title:
5851 * @combo_box: a #GtkComboBox
5852 * @title: a title for the menu in tearoff mode
5854 * Sets the menu's title in tearoff mode.
5859 gtk_combo_box_set_title (GtkComboBox *combo_box,
5862 GtkComboBoxPrivate *priv;
5864 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5866 priv = combo_box->priv;
5868 if (strcmp (title ? title : "",
5869 priv->tearoff_title ? priv->tearoff_title : "") != 0)
5871 g_free (priv->tearoff_title);
5872 priv->tearoff_title = g_strdup (title);
5874 gtk_combo_box_update_title (combo_box);
5876 g_object_notify (G_OBJECT (combo_box), "tearoff-title");
5882 * gtk_combo_box_set_popup_fixed_width:
5883 * @combo_box: a #GtkComboBox
5884 * @fixed: whether to use a fixed popup width
5886 * Specifies whether the popup's width should be a fixed width
5887 * matching the allocated width of the combo box.
5892 gtk_combo_box_set_popup_fixed_width (GtkComboBox *combo_box,
5895 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5897 if (combo_box->priv->popup_fixed_width != fixed)
5899 combo_box->priv->popup_fixed_width = fixed;
5901 g_object_notify (G_OBJECT (combo_box), "popup-fixed-width");
5906 * gtk_combo_box_get_popup_fixed_width:
5907 * @combo_box: a #GtkComboBox
5909 * Gets whether the popup uses a fixed width matching
5910 * the allocated width of the combo box.
5912 * Returns: %TRUE if the popup uses a fixed width
5917 gtk_combo_box_get_popup_fixed_width (GtkComboBox *combo_box)
5919 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
5921 return combo_box->priv->popup_fixed_width;
5926 * gtk_combo_box_get_popup_accessible:
5927 * @combo_box: a #GtkComboBox
5929 * Gets the accessible object corresponding to the combo box's popup.
5931 * This function is mostly intended for use by accessibility technologies;
5932 * applications should have little use for it.
5934 * Returns: (transfer none): the accessible object corresponding
5935 * to the combo box's popup.
5940 gtk_combo_box_get_popup_accessible (GtkComboBox *combo_box)
5944 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
5946 if (combo_box->priv->popup_widget)
5948 atk_obj = gtk_widget_get_accessible (combo_box->priv->popup_widget);
5956 * gtk_combo_box_get_row_separator_func:
5957 * @combo_box: a #GtkComboBox
5959 * Returns the current row separator function.
5961 * Return value: the current row separator function.
5965 GtkTreeViewRowSeparatorFunc
5966 gtk_combo_box_get_row_separator_func (GtkComboBox *combo_box)
5968 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
5970 return combo_box->priv->row_separator_func;
5974 * gtk_combo_box_set_row_separator_func:
5975 * @combo_box: a #GtkComboBox
5976 * @func: a #GtkTreeViewRowSeparatorFunc
5977 * @data: (allow-none): user data to pass to @func, or %NULL
5978 * @destroy: (allow-none): destroy notifier for @data, or %NULL
5980 * Sets the row separator function, which is used to determine
5981 * whether a row should be drawn as a separator. If the row separator
5982 * function is %NULL, no separators are drawn. This is the default value.
5987 gtk_combo_box_set_row_separator_func (GtkComboBox *combo_box,
5988 GtkTreeViewRowSeparatorFunc func,
5990 GDestroyNotify destroy)
5992 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5994 if (combo_box->priv->row_separator_destroy)
5995 combo_box->priv->row_separator_destroy (combo_box->priv->row_separator_data);
5997 combo_box->priv->row_separator_func = func;
5998 combo_box->priv->row_separator_data = data;
5999 combo_box->priv->row_separator_destroy = destroy;
6001 if (combo_box->priv->tree_view)
6002 gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (combo_box->priv->tree_view),
6005 gtk_combo_box_relayout (combo_box);
6007 gtk_widget_queue_draw (GTK_WIDGET (combo_box));
6011 * gtk_combo_box_set_button_sensitivity:
6012 * @combo_box: a #GtkComboBox
6013 * @sensitivity: specify the sensitivity of the dropdown button
6015 * Sets whether the dropdown button of the combo box should be
6016 * always sensitive (%GTK_SENSITIVITY_ON), never sensitive (%GTK_SENSITIVITY_OFF)
6017 * or only if there is at least one item to display (%GTK_SENSITIVITY_AUTO).
6022 gtk_combo_box_set_button_sensitivity (GtkComboBox *combo_box,
6023 GtkSensitivityType sensitivity)
6025 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
6027 if (combo_box->priv->button_sensitivity != sensitivity)
6029 combo_box->priv->button_sensitivity = sensitivity;
6030 gtk_combo_box_update_sensitivity (combo_box);
6032 g_object_notify (G_OBJECT (combo_box), "button-sensitivity");
6037 * gtk_combo_box_get_button_sensitivity:
6038 * @combo_box: a #GtkComboBox
6040 * Returns whether the combo box sets the dropdown button
6041 * sensitive or not when there are no items in the model.
6043 * Return Value: %GTK_SENSITIVITY_ON if the dropdown button
6044 * is sensitive when the model is empty, %GTK_SENSITIVITY_OFF
6045 * if the button is always insensitive or
6046 * %GTK_SENSITIVITY_AUTO if it is only sensitive as long as
6047 * the model has one item to be selected.
6052 gtk_combo_box_get_button_sensitivity (GtkComboBox *combo_box)
6054 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
6056 return combo_box->priv->button_sensitivity;
6061 * gtk_combo_box_get_has_entry:
6062 * @combo_box: a #GtkComboBox
6064 * Returns whether the combo box has an entry.
6066 * Return Value: whether there is an entry in @combo_box.
6071 gtk_combo_box_get_has_entry (GtkComboBox *combo_box)
6073 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
6075 return combo_box->priv->has_entry;
6079 * gtk_combo_box_set_entry_text_column:
6080 * @combo_box: A #GtkComboBox
6081 * @text_column: A column in @model to get the strings from for
6082 * the internal entry
6084 * Sets the model column which @combo_box should use to get strings from
6085 * to be @text_column. The column @text_column in the model of @combo_box
6086 * must be of type %G_TYPE_STRING.
6088 * This is only relevant if @combo_box has been created with
6089 * #GtkComboBox:has-entry as %TRUE.
6094 gtk_combo_box_set_entry_text_column (GtkComboBox *combo_box,
6097 GtkComboBoxPrivate *priv = combo_box->priv;
6098 GtkTreeModel *model;
6100 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
6102 model = gtk_combo_box_get_model (combo_box);
6104 g_return_if_fail (text_column >= 0);
6105 g_return_if_fail (model == NULL || text_column < gtk_tree_model_get_n_columns (model));
6107 priv->text_column = text_column;
6109 if (priv->text_renderer != NULL)
6110 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box),
6111 priv->text_renderer,
6112 "text", text_column,
6117 * gtk_combo_box_get_entry_text_column:
6118 * @combo_box: A #GtkComboBox.
6120 * Returns the column which @combo_box is using to get the strings
6121 * from to display in the internal entry.
6123 * Return value: A column in the data source model of @combo_box.
6128 gtk_combo_box_get_entry_text_column (GtkComboBox *combo_box)
6130 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), 0);
6132 return combo_box->priv->text_column;
6136 * gtk_combo_box_set_focus_on_click:
6137 * @combo: a #GtkComboBox
6138 * @focus_on_click: whether the combo box grabs focus when clicked
6141 * Sets whether the combo box will grab focus when it is clicked with
6142 * the mouse. Making mouse clicks not grab focus is useful in places
6143 * like toolbars where you don't want the keyboard focus removed from
6144 * the main area of the application.
6149 gtk_combo_box_set_focus_on_click (GtkComboBox *combo_box,
6150 gboolean focus_on_click)
6152 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
6154 focus_on_click = focus_on_click != FALSE;
6156 if (combo_box->priv->focus_on_click != focus_on_click)
6158 combo_box->priv->focus_on_click = focus_on_click;
6160 if (combo_box->priv->button)
6161 gtk_button_set_focus_on_click (GTK_BUTTON (combo_box->priv->button),
6164 g_object_notify (G_OBJECT (combo_box), "focus-on-click");
6169 * gtk_combo_box_get_focus_on_click:
6170 * @combo: a #GtkComboBox
6172 * Returns whether the combo box grabs focus when it is clicked
6173 * with the mouse. See gtk_combo_box_set_focus_on_click().
6175 * Return value: %TRUE if the combo box grabs focus when it is
6176 * clicked with the mouse.
6181 gtk_combo_box_get_focus_on_click (GtkComboBox *combo_box)
6183 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
6185 return combo_box->priv->focus_on_click;
6190 gtk_combo_box_buildable_custom_tag_start (GtkBuildable *buildable,
6191 GtkBuilder *builder,
6193 const gchar *tagname,
6194 GMarkupParser *parser,
6197 if (parent_buildable_iface->custom_tag_start (buildable, builder, child,
6198 tagname, parser, data))
6201 return _gtk_cell_layout_buildable_custom_tag_start (buildable, builder, child,
6202 tagname, parser, data);
6206 gtk_combo_box_buildable_custom_tag_end (GtkBuildable *buildable,
6207 GtkBuilder *builder,
6209 const gchar *tagname,
6212 if (strcmp (tagname, "attributes") == 0)
6213 _gtk_cell_layout_buildable_custom_tag_end (buildable, builder, child, tagname,
6216 parent_buildable_iface->custom_tag_end (buildable, builder, child, tagname,
6221 gtk_combo_box_buildable_get_internal_child (GtkBuildable *buildable,
6222 GtkBuilder *builder,
6223 const gchar *childname)
6225 GtkComboBox *combo_box = GTK_COMBO_BOX (buildable);
6227 if (combo_box->priv->has_entry && strcmp (childname, "entry") == 0)
6228 return G_OBJECT (gtk_bin_get_child (GTK_BIN (buildable)));
6230 return parent_buildable_iface->get_internal_child (buildable, builder, childname);
6234 gtk_combo_box_remeasure (GtkComboBox *combo_box)
6236 GtkComboBoxPrivate *priv = combo_box->priv;
6241 !gtk_tree_model_get_iter_first (priv->model, &iter))
6244 priv->minimum_width = priv->natural_width = 0;
6246 path = gtk_tree_path_new_from_indices (0, -1);
6250 gint row_min = 0, row_nat = 0;
6252 if (priv->cell_view)
6253 gtk_cell_view_get_desired_width_of_row (GTK_CELL_VIEW (priv->cell_view),
6254 path, &row_min, &row_nat);
6256 priv->minimum_width = MAX (priv->minimum_width, row_min);
6257 priv->natural_width = MAX (priv->natural_width, row_nat);
6259 gtk_tree_path_next (path);
6261 while (gtk_tree_model_iter_next (priv->model, &iter));
6263 gtk_tree_path_free (path);
6268 gtk_combo_box_measure_height_for_width (GtkComboBox *combo_box,
6274 GtkComboBoxPrivate *priv = combo_box->priv;
6277 gint child_min, child_nat;
6279 child = gtk_bin_get_child (GTK_BIN (combo_box));
6281 gtk_widget_get_preferred_height_for_width (child, avail_width,
6282 &child_min, &child_nat);
6285 !gtk_tree_model_get_iter_first (priv->model, &iter))
6288 path = gtk_tree_path_new_from_indices (0, -1);
6292 gint row_min = 0, row_nat = 0;
6294 if (priv->cell_view)
6295 gtk_cell_view_get_desired_height_for_width_of_row (GTK_CELL_VIEW (priv->cell_view),
6297 &row_min, &row_nat);
6299 child_min = MAX (child_min, row_min);
6300 child_nat = MAX (child_nat, row_nat);
6302 gtk_tree_path_next (path);
6304 while (gtk_tree_model_iter_next (priv->model, &iter));
6306 gtk_tree_path_free (path);
6311 *min_height = child_min;
6313 *nat_height = child_nat;
6318 gtk_combo_box_get_preferred_width (GtkWidget *widget,
6322 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
6323 GtkComboBoxPrivate *priv = combo_box->priv;
6325 gint focus_width, focus_pad;
6326 gint font_size, arrow_size;
6327 PangoContext *context;
6328 PangoFontMetrics *metrics;
6329 PangoFontDescription *font_desc;
6331 gint minimum_width, natural_width;
6332 gint child_min, child_nat;
6334 child = gtk_bin_get_child (GTK_BIN (widget));
6337 gtk_widget_get_preferred_width (child, &child_min, &child_nat);
6338 gtk_combo_box_remeasure (combo_box);
6340 child_min = MAX (child_min, priv->minimum_width);
6341 child_nat = MAX (child_nat, priv->natural_width);
6343 gtk_widget_style_get (GTK_WIDGET (widget),
6344 "focus-line-width", &focus_width,
6345 "focus-padding", &focus_pad,
6346 "arrow-size", &arrow_size,
6349 font_desc = gtk_widget_get_style (child)->font_desc;
6350 context = gtk_widget_get_pango_context (GTK_WIDGET (widget));
6351 metrics = pango_context_get_metrics (context, font_desc,
6352 pango_context_get_language (context));
6353 font_size = PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) +
6354 pango_font_metrics_get_descent (metrics));
6355 pango_font_metrics_unref (metrics);
6357 arrow_size = MAX (arrow_size, font_size);
6359 gtk_widget_set_size_request (priv->arrow, arrow_size, arrow_size);
6361 if (!priv->tree_view)
6365 if (priv->cell_view)
6367 gint sep_width, arrow_width;
6368 gint border_width, xthickness, xpad;
6370 border_width = gtk_container_get_border_width (GTK_CONTAINER (combo_box));
6371 xthickness = gtk_widget_get_style (priv->button)->xthickness;
6373 gtk_widget_get_preferred_width (priv->separator, &sep_width, NULL);
6374 gtk_widget_get_preferred_width (priv->arrow, &arrow_width, NULL);
6376 xpad = 2*(border_width + xthickness + focus_width + focus_pad);
6378 minimum_width = child_min + sep_width + arrow_width + xpad;
6379 natural_width = child_nat + sep_width + arrow_width + xpad;
6383 gint but_width, but_nat_width;
6385 gtk_widget_get_preferred_width (priv->button,
6386 &but_width, &but_nat_width);
6388 minimum_width = child_min + but_width;
6389 natural_width = child_nat + but_nat_width;
6395 gint button_width, button_nat_width;
6397 /* sample + frame */
6398 minimum_width = child_min;
6399 natural_width = child_nat;
6401 minimum_width += 2 * focus_width;
6402 natural_width += 2 * focus_width;
6404 if (priv->cell_view_frame)
6406 if (priv->has_frame)
6408 gint border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->cell_view_frame));
6409 gint xpad = 2 * (border_width + gtk_widget_get_style (GTK_WIDGET (priv->cell_view_frame))->xthickness);
6411 minimum_width += xpad;
6412 natural_width += xpad;
6417 gtk_widget_get_preferred_width (priv->button,
6418 &button_width, &button_nat_width);
6420 minimum_width += button_width;
6421 natural_width += button_nat_width;
6424 if (GTK_SHADOW_NONE != priv->shadow_type)
6426 style = gtk_widget_get_style (GTK_WIDGET (widget));
6428 minimum_width += 2 * style->xthickness;
6429 natural_width += 2 * style->xthickness;
6433 *minimum_size = minimum_width;
6436 *natural_size = natural_width;
6440 gtk_combo_box_get_preferred_height (GtkWidget *widget,
6446 /* Combo box is height-for-width only
6447 * (so we always just reserve enough height for the minimum width) */
6448 GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, &min_width, NULL);
6449 GTK_WIDGET_GET_CLASS (widget)->get_preferred_height_for_width (widget, min_width, minimum_size, natural_size);
6453 gtk_combo_box_get_preferred_width_for_height (GtkWidget *widget,
6458 /* Combo box is height-for-width only
6459 * (so we assume we always reserved enough height for the minimum width) */
6460 GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, minimum_size, natural_size);
6465 gtk_combo_box_get_preferred_height_for_width (GtkWidget *widget,
6470 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
6471 GtkComboBoxPrivate *priv = combo_box->priv;
6473 gint focus_width, focus_pad;
6474 gint min_height, nat_height;
6477 gtk_widget_style_get (GTK_WIDGET (widget),
6478 "focus-line-width", &focus_width,
6479 "focus-padding", &focus_pad,
6484 if (GTK_SHADOW_NONE != priv->shadow_type)
6485 size -= gtk_widget_get_style (GTK_WIDGET (widget))->xthickness;
6487 if (!priv->tree_view)
6490 if (priv->cell_view)
6492 GtkStyle *button_style;
6493 /* calculate x/y padding and separator/arrow size */
6494 gint sep_width, arrow_width, sep_height, arrow_height;
6495 gint border_width, xthickness, ythickness, xpad, ypad;
6497 border_width = gtk_container_get_border_width (GTK_CONTAINER (combo_box));
6498 button_style = gtk_widget_get_style (priv->button);
6500 xthickness = button_style->xthickness;
6501 ythickness = button_style->ythickness;
6503 gtk_widget_get_preferred_width (priv->separator, &sep_width, NULL);
6504 gtk_widget_get_preferred_width (priv->arrow, &arrow_width, NULL);
6505 gtk_widget_get_preferred_height_for_width (priv->separator,
6506 sep_width, &sep_height, NULL);
6507 gtk_widget_get_preferred_height_for_width (priv->arrow,
6508 arrow_width, &arrow_height, NULL);
6510 xpad = 2*(border_width + xthickness + focus_width + focus_pad);
6511 ypad = 2*(border_width + ythickness + focus_width + focus_pad);
6513 size -= sep_width + arrow_width + xpad;
6515 gtk_combo_box_measure_height_for_width (combo_box, size, &min_height, &nat_height);
6517 arrow_height = MAX (arrow_height, sep_height);
6518 min_height = MAX (min_height, arrow_height);
6519 nat_height = MAX (nat_height, arrow_height);
6526 /* there is a custom child widget inside (no priv->cell_view) */
6527 gint but_width, but_height;
6529 gtk_widget_get_preferred_width (priv->button, &but_width, NULL);
6530 gtk_widget_get_preferred_height_for_width (priv->button,
6531 but_width, &but_height, NULL);
6535 gtk_combo_box_measure_height_for_width (combo_box, size, &min_height, &nat_height);
6537 min_height = MAX (min_height, but_height);
6538 nat_height = MAX (nat_height, but_height);
6544 gint but_width, but_height;
6545 gint xpad = 0, ypad = 0;
6547 gtk_widget_get_preferred_width (priv->button, &but_width, NULL);
6548 gtk_widget_get_preferred_height_for_width (priv->button,
6549 but_width, &but_height, NULL);
6551 if (priv->cell_view_frame && priv->has_frame)
6553 GtkStyle *cell_style;
6556 border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->cell_view_frame));
6557 cell_style = gtk_widget_get_style (GTK_WIDGET (priv->cell_view_frame));
6559 xpad = 2 * (border_width + cell_style->xthickness);
6560 ypad = 2 * (border_width + cell_style->ythickness);
6564 size -= 2 * focus_width;
6567 gtk_combo_box_measure_height_for_width (combo_box, size, &min_height, &nat_height);
6569 min_height = MAX (min_height, but_height);
6570 nat_height = MAX (nat_height, but_height);
6576 if (GTK_SHADOW_NONE != priv->shadow_type)
6578 style = gtk_widget_get_style (GTK_WIDGET (widget));
6580 min_height += 2 * style->ythickness;
6581 nat_height += 2 * style->ythickness;
6585 *minimum_size = min_height;
6588 *natural_size = nat_height;