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 /* this flag is a hack to tell the entry to fill its allocation.
1425 GTK_ENTRY (widget)->is_cell_renderer = TRUE;
1427 g_signal_connect (widget, "changed",
1428 G_CALLBACK (gtk_combo_box_entry_contents_changed),
1431 gtk_entry_set_has_frame (GTK_ENTRY (widget), priv->has_frame);
1436 gtk_combo_box_remove (GtkContainer *container,
1439 GtkComboBox *combo_box = GTK_COMBO_BOX (container);
1440 GtkComboBoxPrivate *priv = combo_box->priv;
1442 gboolean appears_as_list;
1444 if (priv->has_entry)
1446 GtkWidget *child_widget;
1448 child_widget = gtk_bin_get_child (GTK_BIN (container));
1449 if (widget && widget == child_widget)
1451 g_signal_handlers_disconnect_by_func (widget,
1452 gtk_combo_box_entry_contents_changed,
1454 GTK_ENTRY (widget)->is_cell_renderer = FALSE;
1458 if (widget == priv->cell_view)
1459 priv->cell_view = NULL;
1461 gtk_widget_unparent (widget);
1462 _gtk_bin_set_child (GTK_BIN (container), NULL);
1464 if (gtk_widget_in_destruction (GTK_WIDGET (combo_box)))
1467 gtk_widget_queue_resize (GTK_WIDGET (container));
1469 if (!priv->tree_view)
1470 appears_as_list = FALSE;
1472 appears_as_list = TRUE;
1474 if (appears_as_list)
1475 gtk_combo_box_list_destroy (combo_box);
1476 else if (GTK_IS_MENU (priv->popup_widget))
1478 gtk_combo_box_menu_destroy (combo_box);
1479 gtk_menu_detach (GTK_MENU (priv->popup_widget));
1480 priv->popup_widget = NULL;
1483 if (!priv->cell_view)
1485 priv->cell_view = gtk_cell_view_new ();
1486 gtk_widget_set_parent (priv->cell_view, GTK_WIDGET (container));
1487 _gtk_bin_set_child (GTK_BIN (container), priv->cell_view);
1489 gtk_widget_show (priv->cell_view);
1490 gtk_cell_view_set_model (GTK_CELL_VIEW (priv->cell_view),
1492 gtk_combo_box_sync_cells (combo_box, GTK_CELL_LAYOUT (priv->cell_view));
1496 if (appears_as_list)
1497 gtk_combo_box_list_setup (combo_box);
1499 gtk_combo_box_menu_setup (combo_box, TRUE);
1501 if (gtk_tree_row_reference_valid (priv->active_row))
1503 path = gtk_tree_row_reference_get_path (priv->active_row);
1504 gtk_combo_box_set_active_internal (combo_box, path);
1505 gtk_tree_path_free (path);
1508 gtk_combo_box_set_active_internal (combo_box, NULL);
1511 static ComboCellInfo *
1512 gtk_combo_box_get_cell_info (GtkComboBox *combo_box,
1513 GtkCellRenderer *cell)
1517 for (i = combo_box->priv->cells; i; i = i->next)
1519 ComboCellInfo *info = (ComboCellInfo *)i->data;
1521 if (info && info->cell == cell)
1529 gtk_combo_box_menu_show (GtkWidget *menu,
1532 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1533 GtkComboBoxPrivate *priv = combo_box->priv;
1535 gtk_combo_box_child_show (menu, user_data);
1537 priv->popup_in_progress = TRUE;
1538 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button),
1540 priv->popup_in_progress = FALSE;
1544 gtk_combo_box_menu_hide (GtkWidget *menu,
1547 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1549 gtk_combo_box_child_hide (menu,user_data);
1551 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
1556 gtk_combo_box_detacher (GtkWidget *widget,
1559 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1560 GtkComboBoxPrivate *priv = combo_box->priv;
1562 g_return_if_fail (priv->popup_widget == (GtkWidget *) menu);
1564 g_signal_handlers_disconnect_by_func (menu->toplevel,
1565 gtk_combo_box_menu_show,
1567 g_signal_handlers_disconnect_by_func (menu->toplevel,
1568 gtk_combo_box_menu_hide,
1571 priv->popup_widget = NULL;
1575 gtk_combo_box_set_popup_widget (GtkComboBox *combo_box,
1578 GtkComboBoxPrivate *priv = combo_box->priv;
1580 if (GTK_IS_MENU (priv->popup_widget))
1582 gtk_menu_detach (GTK_MENU (priv->popup_widget));
1583 priv->popup_widget = NULL;
1585 else if (priv->popup_widget)
1587 gtk_container_remove (GTK_CONTAINER (priv->scrolled_window),
1588 priv->popup_widget);
1589 g_object_unref (priv->popup_widget);
1590 priv->popup_widget = NULL;
1593 if (GTK_IS_MENU (popup))
1595 if (priv->popup_window)
1597 gtk_widget_destroy (priv->popup_window);
1598 priv->popup_window = NULL;
1601 priv->popup_widget = popup;
1604 * Note that we connect to show/hide on the toplevel, not the
1605 * menu itself, since the menu is not shown/hidden when it is
1606 * popped up while torn-off.
1608 g_signal_connect (GTK_MENU (popup)->toplevel, "show",
1609 G_CALLBACK (gtk_combo_box_menu_show), combo_box);
1610 g_signal_connect (GTK_MENU (popup)->toplevel, "hide",
1611 G_CALLBACK (gtk_combo_box_menu_hide), combo_box);
1613 gtk_menu_attach_to_widget (GTK_MENU (popup),
1614 GTK_WIDGET (combo_box),
1615 gtk_combo_box_detacher);
1619 if (!priv->popup_window)
1621 GtkWidget *toplevel;
1623 priv->popup_window = gtk_window_new (GTK_WINDOW_POPUP);
1624 gtk_widget_set_name (priv->popup_window, "gtk-combobox-popup-window");
1626 gtk_window_set_type_hint (GTK_WINDOW (priv->popup_window),
1627 GDK_WINDOW_TYPE_HINT_COMBO);
1629 g_signal_connect (GTK_WINDOW (priv->popup_window),"show",
1630 G_CALLBACK (gtk_combo_box_child_show),
1632 g_signal_connect (GTK_WINDOW (priv->popup_window),"hide",
1633 G_CALLBACK (gtk_combo_box_child_hide),
1636 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (combo_box));
1637 if (GTK_IS_WINDOW (toplevel))
1639 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
1640 GTK_WINDOW (priv->popup_window));
1641 gtk_window_set_transient_for (GTK_WINDOW (priv->popup_window),
1642 GTK_WINDOW (toplevel));
1645 gtk_window_set_resizable (GTK_WINDOW (priv->popup_window), FALSE);
1646 gtk_window_set_screen (GTK_WINDOW (priv->popup_window),
1647 gtk_widget_get_screen (GTK_WIDGET (combo_box)));
1649 priv->scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1651 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
1654 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->scrolled_window),
1657 gtk_widget_show (priv->scrolled_window);
1659 gtk_container_add (GTK_CONTAINER (priv->popup_window),
1660 priv->scrolled_window);
1663 gtk_container_add (GTK_CONTAINER (priv->scrolled_window),
1666 gtk_widget_show (popup);
1667 g_object_ref (popup);
1668 priv->popup_widget = popup;
1673 gtk_combo_box_menu_position_below (GtkMenu *menu,
1679 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1680 GtkAllocation child_allocation;
1686 GdkRectangle monitor;
1688 /* FIXME: is using the size request here broken? */
1689 child = gtk_bin_get_child (GTK_BIN (combo_box));
1693 gtk_widget_get_allocation (child, &child_allocation);
1695 if (!gtk_widget_get_has_window (child))
1697 sx += child_allocation.x;
1698 sy += child_allocation.y;
1701 gdk_window_get_root_coords (gtk_widget_get_window (child),
1704 if (GTK_SHADOW_NONE != combo_box->priv->shadow_type)
1705 sx -= gtk_widget_get_style (GTK_WIDGET (combo_box))->xthickness;
1707 if (combo_box->priv->popup_fixed_width)
1708 gtk_widget_get_preferred_size (GTK_WIDGET (menu), &req, NULL);
1710 gtk_widget_get_preferred_size (GTK_WIDGET (menu), NULL, &req);
1712 if (gtk_widget_get_direction (GTK_WIDGET (combo_box)) == GTK_TEXT_DIR_LTR)
1715 *x = sx + child_allocation.width - req.width;
1718 screen = gtk_widget_get_screen (GTK_WIDGET (combo_box));
1719 monitor_num = gdk_screen_get_monitor_at_window (screen,
1720 gtk_widget_get_window (GTK_WIDGET (combo_box)));
1721 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1725 else if (*x + req.width > monitor.x + monitor.width)
1726 *x = monitor.x + monitor.width - req.width;
1728 if (monitor.y + monitor.height - *y - child_allocation.height >= req.height)
1729 *y += child_allocation.height;
1730 else if (*y - monitor.y >= req.height)
1732 else if (monitor.y + monitor.height - *y - child_allocation.height > *y - monitor.y)
1733 *y += child_allocation.height;
1741 gtk_combo_box_menu_position_over (GtkMenu *menu,
1747 GtkComboBox *combo_box;
1751 GtkAllocation allocation;
1752 GtkAllocation child_allocation;
1759 combo_box = GTK_COMBO_BOX (user_data);
1760 widget = GTK_WIDGET (combo_box);
1762 active = gtk_menu_get_active (GTK_MENU (combo_box->priv->popup_widget));
1764 gtk_widget_get_allocation (widget, &allocation);
1766 menu_xpos = allocation.x;
1767 menu_ypos = allocation.y + allocation.height / 2 - 2;
1769 if (combo_box->priv->popup_fixed_width)
1770 gtk_widget_get_preferred_width (GTK_WIDGET (menu), &menu_width, NULL);
1772 gtk_widget_get_preferred_width (GTK_WIDGET (menu), NULL, &menu_width);
1776 gtk_widget_get_allocation (active, &child_allocation);
1777 menu_ypos -= child_allocation.height / 2;
1780 children = GTK_MENU_SHELL (combo_box->priv->popup_widget)->children;
1783 child = children->data;
1785 if (active == child)
1788 if (gtk_widget_get_visible (child))
1790 gtk_widget_get_allocation (child, &child_allocation);
1792 menu_ypos -= child_allocation.height;
1795 children = children->next;
1798 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
1799 menu_xpos = menu_xpos + allocation.width - menu_width;
1801 gdk_window_get_root_coords (gtk_widget_get_window (widget),
1802 menu_xpos, menu_ypos,
1803 &menu_xpos, &menu_ypos);
1805 /* Clamp the position on screen */
1806 screen_width = gdk_screen_get_width (gtk_widget_get_screen (widget));
1810 else if ((menu_xpos + menu_width) > screen_width)
1811 menu_xpos -= ((menu_xpos + menu_width) - screen_width);
1820 gtk_combo_box_menu_position (GtkMenu *menu,
1826 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1827 GtkComboBoxPrivate *priv = combo_box->priv;
1828 GtkWidget *menu_item;
1830 if (priv->wrap_width > 0 || priv->cell_view == NULL)
1831 gtk_combo_box_menu_position_below (menu, x, y, push_in, user_data);
1834 /* FIXME handle nested menus better */
1835 menu_item = gtk_menu_get_active (GTK_MENU (priv->popup_widget));
1837 gtk_menu_shell_select_item (GTK_MENU_SHELL (priv->popup_widget),
1840 gtk_combo_box_menu_position_over (menu, x, y, push_in, user_data);
1843 if (!gtk_widget_get_visible (GTK_MENU (priv->popup_widget)->toplevel))
1844 gtk_window_set_type_hint (GTK_WINDOW (GTK_MENU (priv->popup_widget)->toplevel),
1845 GDK_WINDOW_TYPE_HINT_COMBO);
1849 gtk_combo_box_list_position (GtkComboBox *combo_box,
1855 GtkComboBoxPrivate *priv = combo_box->priv;
1856 GtkAllocation allocation;
1859 GdkRectangle monitor;
1860 GtkRequisition popup_req;
1861 GtkPolicyType hpolicy, vpolicy;
1864 /* under windows, the drop down list is as wide as the combo box itself.
1866 GtkWidget *widget = GTK_WIDGET (combo_box);
1870 gtk_widget_get_allocation (widget, &allocation);
1872 if (!gtk_widget_get_has_window (widget))
1878 window = gtk_widget_get_window (widget);
1880 gdk_window_get_root_coords (gtk_widget_get_window (widget),
1883 *width = allocation.width;
1885 hpolicy = vpolicy = GTK_POLICY_NEVER;
1886 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
1889 /* XXX This set_size_request call is part of the hack outlined below and can
1890 * go away once height-for-width is implemented on treeviews. */
1891 gtk_widget_set_size_request (priv->tree_view, -1, -1);
1893 if (combo_box->priv->popup_fixed_width)
1895 gtk_widget_get_preferred_size (priv->scrolled_window, &popup_req, NULL);
1897 if (popup_req.width > *width)
1899 hpolicy = GTK_POLICY_ALWAYS;
1900 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
1906 gtk_combo_box_remeasure (combo_box);
1908 if (priv->natural_width > *width)
1910 hpolicy = GTK_POLICY_NEVER;
1911 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
1915 /* XXX Currently we set the size-request on the internal treeview to be
1916 * the natural width of the cells, this hack can go away once our
1917 * treeview does height-for-width properly (i.e. just adjust *width
1918 * here to be the natural width request of the scrolled-window).
1920 * I can't tell why the magic number 5 is needed here (i.e. without it
1921 * treeviews are left ellipsizing) , however it this all should be
1922 * removed with height-for-width treeviews.
1924 gtk_widget_set_size_request (priv->tree_view, priv->natural_width + 5, -1);
1925 gtk_widget_get_preferred_size (priv->scrolled_window, NULL, &popup_req);
1927 *width = popup_req.width;
1931 *height = popup_req.height;
1933 screen = gtk_widget_get_screen (GTK_WIDGET (combo_box));
1934 monitor_num = gdk_screen_get_monitor_at_window (screen, window);
1935 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1937 if (gtk_widget_get_direction (GTK_WIDGET (combo_box)) == GTK_TEXT_DIR_RTL)
1938 *x = *x + allocation.width - *width;
1942 else if (*x + *width > monitor.x + monitor.width)
1943 *x = monitor.x + monitor.width - *width;
1945 if (*y + allocation.height + *height <= monitor.y + monitor.height)
1946 *y += allocation.height;
1947 else if (*y - *height >= monitor.y)
1949 else if (monitor.y + monitor.height - (*y + allocation.height) > *y - monitor.y)
1951 *y += allocation.height;
1952 *height = monitor.y + monitor.height - *y;
1956 *height = *y - monitor.y;
1960 if (popup_req.height > *height)
1962 vpolicy = GTK_POLICY_ALWAYS;
1964 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
1970 cell_view_is_sensitive (GtkCellView *cell_view)
1972 GList *cells, *list;
1975 cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (cell_view));
1978 for (list = cells; list; list = list->next)
1980 g_object_get (list->data, "sensitive", &sensitive, NULL);
1985 g_list_free (cells);
1991 tree_column_row_is_sensitive (GtkComboBox *combo_box,
1994 GtkComboBoxPrivate *priv = combo_box->priv;
1995 GList *cells, *list;
2001 if (priv->row_separator_func)
2003 if (priv->row_separator_func (priv->model, iter,
2004 priv->row_separator_data))
2008 gtk_tree_view_column_cell_set_cell_data (priv->column,
2010 iter, FALSE, FALSE);
2012 cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (priv->column));
2015 for (list = cells; list; list = list->next)
2017 g_object_get (list->data, "sensitive", &sensitive, NULL);
2022 g_list_free (cells);
2028 update_menu_sensitivity (GtkComboBox *combo_box,
2031 GtkComboBoxPrivate *priv = combo_box->priv;
2032 GList *children, *child;
2033 GtkWidget *item, *submenu, *separator;
2034 GtkWidget *cell_view;
2040 children = gtk_container_get_children (GTK_CONTAINER (menu));
2042 for (child = children; child; child = child->next)
2044 item = GTK_WIDGET (child->data);
2045 cell_view = gtk_bin_get_child (GTK_BIN (item));
2047 if (!GTK_IS_CELL_VIEW (cell_view))
2050 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (item));
2051 if (submenu != NULL)
2053 gtk_widget_set_sensitive (item, TRUE);
2054 update_menu_sensitivity (combo_box, submenu);
2058 sensitive = cell_view_is_sensitive (GTK_CELL_VIEW (cell_view));
2060 if (menu != priv->popup_widget && child == children)
2062 separator = GTK_WIDGET (child->next->data);
2063 g_object_set (item, "visible", sensitive, NULL);
2064 g_object_set (separator, "visible", sensitive, NULL);
2067 gtk_widget_set_sensitive (item, sensitive);
2071 g_list_free (children);
2075 gtk_combo_box_menu_popup (GtkComboBox *combo_box,
2077 guint32 activate_time)
2079 GtkComboBoxPrivate *priv = combo_box->priv;
2082 gint width, min_width, nat_width;
2084 update_menu_sensitivity (combo_box, priv->popup_widget);
2087 if (gtk_tree_row_reference_valid (priv->active_row))
2089 path = gtk_tree_row_reference_get_path (priv->active_row);
2090 active_item = gtk_tree_path_get_indices (path)[0];
2091 gtk_tree_path_free (path);
2093 if (priv->add_tearoffs)
2097 /* FIXME handle nested menus better */
2098 gtk_menu_set_active (GTK_MENU (priv->popup_widget), active_item);
2100 if (priv->wrap_width == 0)
2102 GtkAllocation allocation;
2104 gtk_widget_get_allocation (GTK_WIDGET (combo_box), &allocation);
2105 width = allocation.width;
2106 gtk_widget_set_size_request (priv->popup_widget, -1, -1);
2107 gtk_widget_get_preferred_width (priv->popup_widget, &min_width, &nat_width);
2109 if (combo_box->priv->popup_fixed_width)
2110 width = MAX (width, min_width);
2112 width = MAX (width, nat_width);
2114 gtk_widget_set_size_request (priv->popup_widget, width, -1);
2117 gtk_menu_popup (GTK_MENU (priv->popup_widget),
2119 gtk_combo_box_menu_position, combo_box,
2120 button, activate_time);
2124 popup_grab_on_window (GdkWindow *window,
2125 GdkDevice *keyboard,
2127 guint32 activate_time)
2130 gdk_device_grab (keyboard, window,
2131 GDK_OWNERSHIP_WINDOW, TRUE,
2132 GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
2133 NULL, activate_time) != GDK_GRAB_SUCCESS)
2137 gdk_device_grab (pointer, window,
2138 GDK_OWNERSHIP_WINDOW, TRUE,
2139 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
2140 GDK_POINTER_MOTION_MASK,
2141 NULL, activate_time) != GDK_GRAB_SUCCESS)
2144 gdk_device_ungrab (keyboard, activate_time);
2153 * gtk_combo_box_popup:
2154 * @combo_box: a #GtkComboBox
2156 * Pops up the menu or dropdown list of @combo_box.
2158 * This function is mostly intended for use by accessibility technologies;
2159 * applications should have little use for it.
2164 gtk_combo_box_popup (GtkComboBox *combo_box)
2166 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2168 g_signal_emit (combo_box, combo_box_signals[POPUP], 0);
2172 * gtk_combo_box_popup_for_device:
2173 * @combo_box: a #GtkComboBox
2174 * @device: a #GdkDevice
2176 * Pops up the menu or dropdown list of @combo_box, the popup window
2177 * will be grabbed so only @device and its associated pointer/keyboard
2178 * are the only #GdkDevice<!-- -->s able to send events to it.
2183 gtk_combo_box_popup_for_device (GtkComboBox *combo_box,
2186 GtkComboBoxPrivate *priv = combo_box->priv;
2187 gint x, y, width, height;
2188 GtkTreePath *path = NULL, *ppath;
2189 GtkWidget *toplevel;
2190 GdkDevice *keyboard, *pointer;
2193 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2194 g_return_if_fail (GDK_IS_DEVICE (device));
2196 if (!gtk_widget_get_realized (GTK_WIDGET (combo_box)))
2199 if (gtk_widget_get_mapped (priv->popup_widget))
2202 if (priv->grab_pointer && priv->grab_keyboard)
2205 time = gtk_get_current_event_time ();
2207 if (device->source == GDK_SOURCE_KEYBOARD)
2210 pointer = gdk_device_get_associated_device (device);
2215 keyboard = gdk_device_get_associated_device (device);
2218 if (GTK_IS_MENU (priv->popup_widget))
2220 gtk_combo_box_menu_popup (combo_box,
2221 priv->activate_button,
2222 priv->activate_time);
2226 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (combo_box));
2227 if (GTK_IS_WINDOW (toplevel))
2228 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
2229 GTK_WINDOW (priv->popup_window));
2231 gtk_widget_show_all (priv->scrolled_window);
2232 gtk_combo_box_list_position (combo_box, &x, &y, &width, &height);
2234 gtk_widget_set_size_request (priv->popup_window, width, height);
2235 gtk_window_move (GTK_WINDOW (priv->popup_window), x, y);
2237 if (gtk_tree_row_reference_valid (priv->active_row))
2239 path = gtk_tree_row_reference_get_path (priv->active_row);
2240 ppath = gtk_tree_path_copy (path);
2241 if (gtk_tree_path_up (ppath))
2242 gtk_tree_view_expand_to_path (GTK_TREE_VIEW (priv->tree_view),
2244 gtk_tree_path_free (ppath);
2246 gtk_tree_view_set_hover_expand (GTK_TREE_VIEW (priv->tree_view),
2250 gtk_widget_show (priv->popup_window);
2254 gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->tree_view),
2256 gtk_tree_path_free (path);
2259 gtk_widget_grab_focus (priv->popup_window);
2260 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button),
2263 if (!gtk_widget_has_focus (priv->tree_view))
2264 gtk_widget_grab_focus (priv->tree_view);
2266 if (!popup_grab_on_window (gtk_widget_get_window (priv->popup_window),
2267 keyboard, pointer, time))
2269 gtk_widget_hide (priv->popup_window);
2273 gtk_device_grab_add (priv->popup_window, pointer, TRUE);
2274 priv->grab_pointer = pointer;
2275 priv->grab_keyboard = keyboard;
2279 gtk_combo_box_real_popup (GtkComboBox *combo_box)
2283 device = gtk_get_current_event_device ();
2287 GdkDeviceManager *device_manager;
2288 GdkDisplay *display;
2291 display = gtk_widget_get_display (GTK_WIDGET (combo_box));
2292 device_manager = gdk_display_get_device_manager (display);
2294 /* No device was set, pick the first master device */
2295 devices = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER);
2296 device = devices->data;
2297 g_list_free (devices);
2300 gtk_combo_box_popup_for_device (combo_box, device);
2304 gtk_combo_box_real_popdown (GtkComboBox *combo_box)
2306 if (combo_box->priv->popup_shown)
2308 gtk_combo_box_popdown (combo_box);
2316 * gtk_combo_box_popdown:
2317 * @combo_box: a #GtkComboBox
2319 * Hides the menu or dropdown list of @combo_box.
2321 * This function is mostly intended for use by accessibility technologies;
2322 * applications should have little use for it.
2327 gtk_combo_box_popdown (GtkComboBox *combo_box)
2329 GtkComboBoxPrivate *priv = combo_box->priv;
2331 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2333 if (GTK_IS_MENU (priv->popup_widget))
2335 gtk_menu_popdown (GTK_MENU (priv->popup_widget));
2339 if (!gtk_widget_get_realized (GTK_WIDGET (combo_box)))
2342 gtk_device_grab_remove (priv->popup_window, priv->grab_pointer);
2343 gtk_widget_hide (priv->popup_window);
2344 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button),
2347 priv->grab_pointer = NULL;
2348 priv->grab_keyboard = NULL;
2352 gtk_combo_box_update_requested_width (GtkComboBox *combo_box,
2355 GtkComboBoxPrivate *priv = combo_box->priv;
2356 gint padding, min_width, nat_width;
2358 if (priv->cell_view)
2359 gtk_widget_style_get (priv->cell_view,
2360 "focus-line-width", &padding,
2365 /* add some pixels for good measure */
2366 padding += BONUS_PADDING;
2368 if (priv->cell_view)
2369 gtk_cell_view_get_desired_width_of_row (GTK_CELL_VIEW (priv->cell_view),
2370 path, &min_width, &nat_width);
2372 min_width = nat_width = 0;
2374 min_width += padding;
2375 nat_width += padding;
2377 if (min_width > priv->minimum_width || nat_width > priv->natural_width)
2379 priv->minimum_width = MAX (priv->minimum_width, min_width);
2380 priv->natural_width = MAX (priv->natural_width, nat_width);
2382 if (priv->cell_view)
2384 gtk_widget_set_size_request (priv->cell_view, min_width, -1);
2385 gtk_widget_queue_resize (priv->cell_view);
2390 #define GTK_COMBO_BOX_SIZE_ALLOCATE_BUTTON \
2391 gtk_widget_get_preferred_size (combo_box->priv->button, \
2395 child.x = allocation->x + shadow_width; \
2397 child.x = allocation->x + allocation->width - req.width - shadow_width; \
2399 child.y = allocation->y + shadow_height; \
2400 child.width = req.width; \
2401 child.height = allocation->height - 2 * shadow_height; \
2402 child.width = MAX (1, child.width); \
2403 child.height = MAX (1, child.height); \
2405 gtk_widget_size_allocate (combo_box->priv->button, &child);
2408 gtk_combo_box_size_allocate (GtkWidget *widget,
2409 GtkAllocation *allocation)
2411 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
2412 GtkComboBoxPrivate *priv = combo_box->priv;
2413 GtkWidget *child_widget;
2414 gint shadow_width, shadow_height;
2415 gint focus_width, focus_pad;
2416 GtkAllocation child;
2419 gboolean is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
2421 gtk_widget_set_allocation (widget, allocation);
2422 child_widget = gtk_bin_get_child (GTK_BIN (widget));
2424 style = gtk_widget_get_style (widget);
2425 gtk_widget_style_get (widget,
2426 "focus-line-width", &focus_width,
2427 "focus-padding", &focus_pad,
2430 if (GTK_SHADOW_NONE != priv->shadow_type)
2432 shadow_width = style->xthickness;
2433 shadow_height = style->ythickness;
2441 if (!priv->tree_view)
2443 if (priv->cell_view)
2445 gint xthickness, ythickness;
2450 allocation->x += shadow_width;
2451 allocation->y += shadow_height;
2452 allocation->width -= 2 * shadow_width;
2453 allocation->height -= 2 * shadow_height;
2455 gtk_widget_size_allocate (priv->button, allocation);
2457 /* set some things ready */
2458 border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->button));
2459 style = gtk_widget_get_style (priv->button);
2460 xthickness = style->xthickness;
2461 ythickness = style->ythickness;
2463 child.x = allocation->x;
2464 child.y = allocation->y;
2465 width = allocation->width;
2466 child.height = allocation->height;
2468 if (!priv->is_cell_renderer)
2470 child.x += border_width + xthickness + focus_width + focus_pad;
2471 child.y += border_width + ythickness + focus_width + focus_pad;
2472 width -= 2 * (child.x - allocation->x);
2473 child.height -= 2 * (child.y - allocation->y);
2477 /* handle the children */
2478 gtk_widget_get_preferred_size (priv->arrow, &req, NULL);
2479 child.width = req.width;
2481 child.x += width - req.width;
2482 child.width = MAX (1, child.width);
2483 child.height = MAX (1, child.height);
2484 gtk_widget_size_allocate (priv->arrow, &child);
2486 child.x += req.width;
2487 gtk_widget_get_preferred_size (priv->separator, &req, NULL);
2488 child.width = req.width;
2490 child.x -= req.width;
2491 child.width = MAX (1, child.width);
2492 child.height = MAX (1, child.height);
2493 gtk_widget_size_allocate (priv->separator, &child);
2497 child.x += req.width;
2498 child.width = allocation->x + allocation->width
2499 - (border_width + xthickness + focus_width + focus_pad)
2504 child.width = child.x;
2505 child.x = allocation->x
2506 + border_width + xthickness + focus_width + focus_pad;
2507 child.width -= child.x;
2510 if (gtk_widget_get_visible (priv->popup_widget))
2512 gint width, menu_width;
2514 if (priv->wrap_width == 0)
2516 GtkAllocation combo_box_allocation;
2518 gtk_widget_get_allocation (GTK_WIDGET (combo_box), &combo_box_allocation);
2519 width = combo_box_allocation.width;
2520 gtk_widget_set_size_request (priv->popup_widget, -1, -1);
2522 if (combo_box->priv->popup_fixed_width)
2523 gtk_widget_get_preferred_width (priv->popup_widget, &menu_width, NULL);
2525 gtk_widget_get_preferred_width (priv->popup_widget, NULL, &menu_width);
2527 gtk_widget_set_size_request (priv->popup_widget,
2528 MAX (width, menu_width), -1);
2531 /* reposition the menu after giving it a new width */
2532 gtk_menu_reposition (GTK_MENU (priv->popup_widget));
2535 child.width = MAX (1, child.width);
2536 child.height = MAX (1, child.height);
2537 gtk_widget_size_allocate (child_widget, &child);
2541 GTK_COMBO_BOX_SIZE_ALLOCATE_BUTTON
2544 child.x = allocation->x + req.width + shadow_width;
2546 child.x = allocation->x + shadow_width;
2547 child.y = allocation->y + shadow_height;
2548 child.width = allocation->width - req.width - 2 * shadow_width;
2549 child.width = MAX (1, child.width);
2550 child.height = MAX (1, child.height);
2551 gtk_widget_size_allocate (child_widget, &child);
2558 /* Combobox thickness + border-width */
2559 guint border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
2560 int delta_x = shadow_width + border_width;
2561 int delta_y = shadow_height + border_width;
2564 GTK_COMBO_BOX_SIZE_ALLOCATE_BUTTON
2568 child.x = allocation->x + req.width;
2570 child.x = allocation->x;
2572 child.y = allocation->y;
2573 child.width = allocation->width - req.width;
2574 child.height = allocation->height;
2576 if (priv->cell_view_frame)
2580 child.width = MAX (1, child.width - delta_x * 2);
2581 child.height = MAX (1, child.height - delta_y * 2);
2582 gtk_widget_size_allocate (priv->cell_view_frame, &child);
2585 if (priv->has_frame)
2587 border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->cell_view_frame));
2588 style = gtk_widget_get_style (priv->cell_view_frame);
2589 delta_x = border_width + style->xthickness;
2590 delta_y = border_width + style->ythickness;
2594 child.width -= delta_x * 2;
2595 child.height -= delta_y * 2;
2602 child.width -= delta_x * 2;
2603 child.height -= delta_y * 2;
2606 if (gtk_widget_get_visible (priv->popup_window))
2608 gint x, y, width, height;
2609 gtk_combo_box_list_position (combo_box, &x, &y, &width, &height);
2610 gtk_window_move (GTK_WINDOW (priv->popup_window), x, y);
2611 gtk_widget_set_size_request (priv->popup_window, width, height);
2615 child.width = MAX (1, child.width);
2616 child.height = MAX (1, child.height);
2618 gtk_widget_size_allocate (child_widget, &child);
2622 #undef GTK_COMBO_BOX_ALLOCATE_BUTTON
2625 gtk_combo_box_unset_model (GtkComboBox *combo_box)
2627 GtkComboBoxPrivate *priv = combo_box->priv;
2631 g_signal_handler_disconnect (priv->model,
2633 g_signal_handler_disconnect (priv->model,
2635 g_signal_handler_disconnect (priv->model,
2636 priv->reordered_id);
2637 g_signal_handler_disconnect (priv->model,
2642 if (!priv->tree_view)
2644 if (priv->popup_widget)
2645 gtk_container_foreach (GTK_CONTAINER (priv->popup_widget),
2646 (GtkCallback)gtk_widget_destroy, NULL);
2651 g_object_unref (priv->model);
2655 if (priv->active_row)
2657 gtk_tree_row_reference_free (priv->active_row);
2658 priv->active_row = NULL;
2661 if (priv->cell_view)
2662 gtk_cell_view_set_model (GTK_CELL_VIEW (priv->cell_view), NULL);
2666 gtk_combo_box_forall (GtkContainer *container,
2667 gboolean include_internals,
2668 GtkCallback callback,
2669 gpointer callback_data)
2671 GtkComboBox *combo_box = GTK_COMBO_BOX (container);
2672 GtkComboBoxPrivate *priv = combo_box->priv;
2675 if (include_internals)
2678 (* callback) (priv->button, callback_data);
2679 if (priv->cell_view_frame)
2680 (* callback) (priv->cell_view_frame, callback_data);
2683 child = gtk_bin_get_child (GTK_BIN (container));
2685 (* callback) (child, callback_data);
2689 gtk_combo_box_child_show (GtkWidget *widget,
2690 GtkComboBox *combo_box)
2692 GtkComboBoxPrivate *priv = combo_box->priv;
2694 priv->popup_shown = TRUE;
2695 g_object_notify (G_OBJECT (combo_box), "popup-shown");
2699 gtk_combo_box_child_hide (GtkWidget *widget,
2700 GtkComboBox *combo_box)
2702 GtkComboBoxPrivate *priv = combo_box->priv;
2704 priv->popup_shown = FALSE;
2705 g_object_notify (G_OBJECT (combo_box), "popup-shown");
2709 gtk_combo_box_draw (GtkWidget *widget,
2712 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
2713 GtkComboBoxPrivate *priv = combo_box->priv;
2715 if (priv->shadow_type != GTK_SHADOW_NONE)
2717 gtk_paint_shadow (gtk_widget_get_style (widget),
2719 GTK_STATE_NORMAL, priv->shadow_type,
2722 gtk_widget_get_allocated_width (widget),
2723 gtk_widget_get_allocated_height (widget));
2726 gtk_container_propagate_draw (GTK_CONTAINER (widget),
2729 if (priv->tree_view && priv->cell_view_frame)
2731 gtk_container_propagate_draw (GTK_CONTAINER (widget),
2732 priv->cell_view_frame, cr);
2735 gtk_container_propagate_draw (GTK_CONTAINER (widget),
2736 gtk_bin_get_child (GTK_BIN (widget)),
2752 path_visible (GtkTreeView *view,
2758 /* Note that we rely on the fact that collapsed rows don't have nodes
2760 return _gtk_tree_view_find_node (view, path, &tree, &node);
2764 tree_next_func (GtkTreeModel *model,
2769 SearchData *search_data = (SearchData *)data;
2771 if (search_data->found)
2773 if (!tree_column_row_is_sensitive (search_data->combo, iter))
2776 if (search_data->visible &&
2777 !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
2780 search_data->set = TRUE;
2781 search_data->iter = *iter;
2786 if (gtk_tree_path_compare (path, search_data->path) == 0)
2787 search_data->found = TRUE;
2793 tree_next (GtkComboBox *combo,
2794 GtkTreeModel *model,
2799 SearchData search_data;
2801 search_data.combo = combo;
2802 search_data.path = gtk_tree_model_get_path (model, iter);
2803 search_data.visible = visible;
2804 search_data.found = FALSE;
2805 search_data.set = FALSE;
2807 gtk_tree_model_foreach (model, tree_next_func, &search_data);
2809 *next = search_data.iter;
2811 gtk_tree_path_free (search_data.path);
2813 return search_data.set;
2817 tree_prev_func (GtkTreeModel *model,
2822 SearchData *search_data = (SearchData *)data;
2824 if (gtk_tree_path_compare (path, search_data->path) == 0)
2826 search_data->found = TRUE;
2830 if (!tree_column_row_is_sensitive (search_data->combo, iter))
2833 if (search_data->visible &&
2834 !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
2837 search_data->set = TRUE;
2838 search_data->iter = *iter;
2844 tree_prev (GtkComboBox *combo,
2845 GtkTreeModel *model,
2850 SearchData search_data;
2852 search_data.combo = combo;
2853 search_data.path = gtk_tree_model_get_path (model, iter);
2854 search_data.visible = visible;
2855 search_data.found = FALSE;
2856 search_data.set = FALSE;
2858 gtk_tree_model_foreach (model, tree_prev_func, &search_data);
2860 *prev = search_data.iter;
2862 gtk_tree_path_free (search_data.path);
2864 return search_data.set;
2868 tree_last_func (GtkTreeModel *model,
2873 SearchData *search_data = (SearchData *)data;
2875 if (!tree_column_row_is_sensitive (search_data->combo, iter))
2878 /* Note that we rely on the fact that collapsed rows don't have nodes
2880 if (search_data->visible &&
2881 !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
2884 search_data->set = TRUE;
2885 search_data->iter = *iter;
2891 tree_last (GtkComboBox *combo,
2892 GtkTreeModel *model,
2896 SearchData search_data;
2898 search_data.combo = combo;
2899 search_data.visible = visible;
2900 search_data.set = FALSE;
2902 gtk_tree_model_foreach (model, tree_last_func, &search_data);
2904 *last = search_data.iter;
2906 return search_data.set;
2911 tree_first_func (GtkTreeModel *model,
2916 SearchData *search_data = (SearchData *)data;
2918 if (!tree_column_row_is_sensitive (search_data->combo, iter))
2921 if (search_data->visible &&
2922 !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
2925 search_data->set = TRUE;
2926 search_data->iter = *iter;
2932 tree_first (GtkComboBox *combo,
2933 GtkTreeModel *model,
2937 SearchData search_data;
2939 search_data.combo = combo;
2940 search_data.visible = visible;
2941 search_data.set = FALSE;
2943 gtk_tree_model_foreach (model, tree_first_func, &search_data);
2945 *first = search_data.iter;
2947 return search_data.set;
2951 gtk_combo_box_scroll_event (GtkWidget *widget,
2952 GdkEventScroll *event)
2954 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
2957 GtkTreeIter new_iter;
2959 if (!gtk_combo_box_get_active_iter (combo_box, &iter))
2962 if (event->direction == GDK_SCROLL_UP)
2963 found = tree_prev (combo_box, combo_box->priv->model,
2964 &iter, &new_iter, FALSE);
2966 found = tree_next (combo_box, combo_box->priv->model,
2967 &iter, &new_iter, FALSE);
2970 gtk_combo_box_set_active_iter (combo_box, &new_iter);
2980 gtk_combo_box_sync_cells (GtkComboBox *combo_box,
2981 GtkCellLayout *cell_layout)
2983 GtkComboBoxPrivate *priv = combo_box->priv;
2986 for (k = priv->cells; k; k = k->next)
2989 ComboCellInfo *info = (ComboCellInfo *)k->data;
2991 if (info->pack == GTK_PACK_START)
2992 gtk_cell_layout_pack_start (cell_layout,
2993 info->cell, info->expand);
2994 else if (info->pack == GTK_PACK_END)
2995 gtk_cell_layout_pack_end (cell_layout,
2996 info->cell, info->expand);
2998 gtk_cell_layout_set_cell_data_func (cell_layout,
3000 combo_cell_data_func, info, NULL);
3002 for (j = info->attributes; j; j = j->next->next)
3004 gtk_cell_layout_add_attribute (cell_layout,
3007 GPOINTER_TO_INT (j->next->data));
3013 gtk_combo_box_menu_setup (GtkComboBox *combo_box,
3014 gboolean add_children)
3016 GtkComboBoxPrivate *priv = combo_box->priv;
3020 child = gtk_bin_get_child (GTK_BIN (combo_box));
3022 if (priv->cell_view)
3024 priv->button = gtk_toggle_button_new ();
3025 gtk_button_set_focus_on_click (GTK_BUTTON (priv->button),
3026 priv->focus_on_click);
3028 g_signal_connect (priv->button, "toggled",
3029 G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
3030 gtk_widget_set_parent (priv->button,
3031 gtk_widget_get_parent (child));
3033 priv->box = gtk_hbox_new (FALSE, 0);
3034 gtk_container_add (GTK_CONTAINER (priv->button), priv->box);
3036 priv->separator = gtk_vseparator_new ();
3037 gtk_container_add (GTK_CONTAINER (priv->box), priv->separator);
3039 priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3040 gtk_container_add (GTK_CONTAINER (priv->box), priv->arrow);
3042 gtk_widget_show_all (priv->button);
3046 priv->button = gtk_toggle_button_new ();
3047 gtk_button_set_focus_on_click (GTK_BUTTON (priv->button),
3048 priv->focus_on_click);
3050 g_signal_connect (priv->button, "toggled",
3051 G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
3052 gtk_widget_set_parent (priv->button,
3053 gtk_widget_get_parent (child));
3055 priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3056 gtk_container_add (GTK_CONTAINER (priv->button), priv->arrow);
3057 gtk_widget_show_all (priv->button);
3060 g_signal_connect (priv->button, "button-press-event",
3061 G_CALLBACK (gtk_combo_box_menu_button_press),
3063 g_signal_connect (priv->button, "state-changed",
3064 G_CALLBACK (gtk_combo_box_button_state_changed),
3067 /* create our funky menu */
3068 menu = gtk_menu_new ();
3069 gtk_widget_set_name (menu, "gtk-combobox-popup-menu");
3070 gtk_menu_set_reserve_toggle_size (GTK_MENU (menu), FALSE);
3072 g_signal_connect (menu, "key-press-event",
3073 G_CALLBACK (gtk_combo_box_menu_key_press), combo_box);
3074 gtk_combo_box_set_popup_widget (combo_box, menu);
3078 gtk_combo_box_menu_fill (combo_box);
3080 /* the column is needed in tree_column_row_is_sensitive() */
3081 priv->column = gtk_tree_view_column_new ();
3082 g_object_ref_sink (priv->column);
3083 gtk_combo_box_sync_cells (combo_box, GTK_CELL_LAYOUT (priv->column));
3085 gtk_combo_box_update_title (combo_box);
3086 gtk_combo_box_update_sensitivity (combo_box);
3090 gtk_combo_box_menu_fill (GtkComboBox *combo_box)
3092 GtkComboBoxPrivate *priv = combo_box->priv;
3098 menu = priv->popup_widget;
3100 if (priv->add_tearoffs)
3102 GtkWidget *tearoff = gtk_tearoff_menu_item_new ();
3104 gtk_widget_show (tearoff);
3106 if (priv->wrap_width)
3107 gtk_menu_attach (GTK_MENU (menu), tearoff, 0, priv->wrap_width, 0, 1);
3109 gtk_menu_shell_append (GTK_MENU_SHELL (menu), tearoff);
3112 gtk_combo_box_menu_fill_level (combo_box, menu, NULL);
3116 gtk_cell_view_menu_item_new (GtkComboBox *combo_box,
3117 GtkTreeModel *model,
3120 GtkWidget *cell_view;
3125 cell_view = gtk_cell_view_new ();
3126 item = gtk_menu_item_new ();
3127 gtk_container_add (GTK_CONTAINER (item), cell_view);
3129 gtk_cell_view_set_model (GTK_CELL_VIEW (cell_view), model);
3130 path = gtk_tree_model_get_path (model, iter);
3131 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (cell_view), path);
3132 gtk_tree_path_free (path);
3134 gtk_combo_box_sync_cells (combo_box, GTK_CELL_LAYOUT (cell_view));
3135 gtk_widget_get_preferred_size (cell_view, &req, NULL);
3136 gtk_widget_show (cell_view);
3142 gtk_combo_box_menu_fill_level (GtkComboBox *combo_box,
3144 GtkTreeIter *parent)
3146 GtkComboBoxPrivate *priv = combo_box->priv;
3147 GtkTreeModel *model = priv->model;
3148 GtkWidget *item, *submenu, *subitem, *separator;
3150 gboolean is_separator;
3155 n_children = gtk_tree_model_iter_n_children (model, parent);
3158 for (i = 0; i < n_children; i++)
3160 gtk_tree_model_iter_nth_child (model, &iter, parent, i);
3162 if (priv->row_separator_func)
3163 is_separator = priv->row_separator_func (priv->model, &iter,
3164 priv->row_separator_data);
3166 is_separator = FALSE;
3170 item = gtk_separator_menu_item_new ();
3171 path = gtk_tree_model_get_path (model, &iter);
3172 g_object_set_data_full (G_OBJECT (item),
3173 I_("gtk-combo-box-item-path"),
3174 gtk_tree_row_reference_new (model, path),
3175 (GDestroyNotify)gtk_tree_row_reference_free);
3176 gtk_tree_path_free (path);
3180 item = gtk_cell_view_menu_item_new (combo_box, model, &iter);
3181 if (gtk_tree_model_iter_has_child (model, &iter))
3183 submenu = gtk_menu_new ();
3184 gtk_menu_set_reserve_toggle_size (GTK_MENU (submenu), FALSE);
3185 gtk_widget_show (submenu);
3186 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu);
3188 /* Ugly - since menus can only activate leafs, we have to
3189 * duplicate the item inside the submenu.
3191 subitem = gtk_cell_view_menu_item_new (combo_box, model, &iter);
3192 separator = gtk_separator_menu_item_new ();
3193 gtk_widget_show (subitem);
3194 gtk_widget_show (separator);
3195 g_signal_connect (subitem, "activate",
3196 G_CALLBACK (gtk_combo_box_menu_item_activate),
3198 gtk_menu_shell_append (GTK_MENU_SHELL (submenu), subitem);
3199 gtk_menu_shell_append (GTK_MENU_SHELL (submenu), separator);
3201 gtk_combo_box_menu_fill_level (combo_box, submenu, &iter);
3203 g_signal_connect (item, "activate",
3204 G_CALLBACK (gtk_combo_box_menu_item_activate),
3208 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3209 if (priv->wrap_width && menu == priv->popup_widget)
3210 gtk_combo_box_relayout_item (combo_box, item, &iter, last);
3211 gtk_widget_show (item);
3218 gtk_combo_box_menu_destroy (GtkComboBox *combo_box)
3220 GtkComboBoxPrivate *priv = combo_box->priv;
3222 g_signal_handlers_disconnect_matched (priv->button,
3223 G_SIGNAL_MATCH_DATA,
3225 gtk_combo_box_menu_button_press, NULL);
3226 g_signal_handlers_disconnect_matched (priv->button,
3227 G_SIGNAL_MATCH_DATA,
3229 gtk_combo_box_button_state_changed, combo_box);
3231 /* unparent will remove our latest ref */
3232 gtk_widget_unparent (priv->button);
3235 priv->button = NULL;
3237 priv->separator = NULL;
3239 g_object_unref (priv->column);
3240 priv->column = NULL;
3242 /* changing the popup window will unref the menu and the children */
3250 menu_occupied (GtkMenu *menu,
3254 guint bottom_attach)
3258 for (i = GTK_MENU_SHELL (menu)->children; i; i = i->next)
3262 gtk_container_child_get (GTK_CONTAINER (menu),
3266 "bottom-attach", &b,
3270 /* look if this item intersects with the given coordinates */
3271 if (right_attach > l && left_attach < r && bottom_attach > t && top_attach < b)
3279 gtk_combo_box_relayout_item (GtkComboBox *combo_box,
3284 GtkComboBoxPrivate *priv = combo_box->priv;
3285 gint current_col = 0, current_row = 0;
3286 gint rows = 1, cols = 1;
3287 GtkWidget *menu = priv->popup_widget;
3289 if (!GTK_IS_MENU_SHELL (menu))
3292 if (priv->col_column == -1 &&
3293 priv->row_column == -1 &&
3296 gtk_container_child_get (GTK_CONTAINER (menu),
3298 "right-attach", ¤t_col,
3299 "top-attach", ¤t_row,
3301 if (current_col + cols > priv->wrap_width)
3309 if (priv->col_column != -1)
3310 gtk_tree_model_get (priv->model, iter,
3311 priv->col_column, &cols,
3313 if (priv->row_column != -1)
3314 gtk_tree_model_get (priv->model, iter,
3315 priv->row_column, &rows,
3320 if (current_col + cols > priv->wrap_width)
3326 if (!menu_occupied (GTK_MENU (menu),
3327 current_col, current_col + cols,
3328 current_row, current_row + rows))
3335 /* set attach props */
3336 gtk_menu_attach (GTK_MENU (menu), item,
3337 current_col, current_col + cols,
3338 current_row, current_row + rows);
3342 gtk_combo_box_relayout (GtkComboBox *combo_box)
3347 menu = combo_box->priv->popup_widget;
3349 /* do nothing unless we are in menu style and realized */
3350 if (combo_box->priv->tree_view || !GTK_IS_MENU_SHELL (menu))
3353 list = gtk_container_get_children (GTK_CONTAINER (menu));
3355 for (j = g_list_last (list); j; j = j->prev)
3356 gtk_container_remove (GTK_CONTAINER (menu), j->data);
3358 gtk_combo_box_menu_fill (combo_box);
3365 gtk_combo_box_menu_button_press (GtkWidget *widget,
3366 GdkEventButton *event,
3369 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3370 GtkComboBoxPrivate *priv = combo_box->priv;
3372 if (GTK_IS_MENU (priv->popup_widget) &&
3373 event->type == GDK_BUTTON_PRESS && event->button == 1)
3375 if (priv->focus_on_click &&
3376 !gtk_widget_has_focus (priv->button))
3377 gtk_widget_grab_focus (priv->button);
3379 gtk_combo_box_menu_popup (combo_box, event->button, event->time);
3388 gtk_combo_box_menu_item_activate (GtkWidget *item,
3391 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3392 GtkWidget *cell_view;
3396 cell_view = gtk_bin_get_child (GTK_BIN (item));
3398 g_return_if_fail (GTK_IS_CELL_VIEW (cell_view));
3400 path = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (cell_view));
3402 if (gtk_tree_model_get_iter (combo_box->priv->model, &iter, path))
3404 if (gtk_menu_item_get_submenu (GTK_MENU_ITEM (item)) == NULL)
3405 gtk_combo_box_set_active_iter (combo_box, &iter);
3408 gtk_tree_path_free (path);
3410 g_object_set (combo_box,
3411 "editing-canceled", FALSE,
3416 gtk_combo_box_update_sensitivity (GtkComboBox *combo_box)
3419 gboolean sensitive = TRUE; /* fool code checkers */
3421 if (!combo_box->priv->button)
3424 switch (combo_box->priv->button_sensitivity)
3426 case GTK_SENSITIVITY_ON:
3429 case GTK_SENSITIVITY_OFF:
3432 case GTK_SENSITIVITY_AUTO:
3433 sensitive = combo_box->priv->model &&
3434 gtk_tree_model_get_iter_first (combo_box->priv->model, &iter);
3437 g_assert_not_reached ();
3441 gtk_widget_set_sensitive (combo_box->priv->button, sensitive);
3443 /* In list-mode, we also need to update sensitivity of the event box */
3444 if (GTK_IS_TREE_VIEW (combo_box->priv->tree_view)
3445 && combo_box->priv->cell_view)
3446 gtk_widget_set_sensitive (combo_box->priv->box, sensitive);
3450 gtk_combo_box_model_row_inserted (GtkTreeModel *model,
3455 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3457 if (combo_box->priv->tree_view)
3458 gtk_combo_box_list_popup_resize (combo_box);
3460 gtk_combo_box_menu_row_inserted (model, path, iter, user_data);
3462 gtk_combo_box_update_sensitivity (combo_box);
3466 gtk_combo_box_model_row_deleted (GtkTreeModel *model,
3470 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3471 GtkComboBoxPrivate *priv = combo_box->priv;
3473 if (!gtk_tree_row_reference_valid (priv->active_row))
3475 if (priv->cell_view)
3476 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (priv->cell_view), NULL);
3477 g_signal_emit (combo_box, combo_box_signals[CHANGED], 0);
3480 if (priv->tree_view)
3481 gtk_combo_box_list_popup_resize (combo_box);
3483 gtk_combo_box_menu_row_deleted (model, path, user_data);
3485 gtk_combo_box_update_sensitivity (combo_box);
3489 gtk_combo_box_model_rows_reordered (GtkTreeModel *model,
3495 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3497 gtk_tree_row_reference_reordered (G_OBJECT (user_data), path, iter, new_order);
3499 if (!combo_box->priv->tree_view)
3500 gtk_combo_box_menu_rows_reordered (model, path, iter, new_order, user_data);
3504 gtk_combo_box_model_row_changed (GtkTreeModel *model,
3509 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3510 GtkComboBoxPrivate *priv = combo_box->priv;
3511 GtkTreePath *active_path;
3513 /* FIXME this belongs to GtkCellView */
3514 if (gtk_tree_row_reference_valid (priv->active_row))
3516 active_path = gtk_tree_row_reference_get_path (priv->active_row);
3517 if (gtk_tree_path_compare (path, active_path) == 0 &&
3519 gtk_widget_queue_resize (GTK_WIDGET (priv->cell_view));
3520 gtk_tree_path_free (active_path);
3523 if (priv->tree_view)
3524 gtk_combo_box_list_row_changed (model, path, iter, user_data);
3526 gtk_combo_box_menu_row_changed (model, path, iter, user_data);
3530 list_popup_resize_idle (gpointer user_data)
3532 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3533 GtkComboBoxPrivate *priv = combo_box->priv;
3534 gint x, y, width, height;
3536 if (priv->tree_view && gtk_widget_get_mapped (priv->popup_window))
3538 gtk_combo_box_list_position (combo_box, &x, &y, &width, &height);
3540 gtk_widget_set_size_request (priv->popup_window, width, height);
3541 gtk_window_move (GTK_WINDOW (priv->popup_window), x, y);
3544 priv->resize_idle_id = 0;
3550 gtk_combo_box_list_popup_resize (GtkComboBox *combo_box)
3552 GtkComboBoxPrivate *priv = combo_box->priv;
3554 if (!priv->resize_idle_id)
3555 priv->resize_idle_id =
3556 gdk_threads_add_idle (list_popup_resize_idle, combo_box);
3560 gtk_combo_box_model_row_expanded (GtkTreeModel *model,
3565 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3567 gtk_combo_box_list_popup_resize (combo_box);
3572 find_menu_by_path (GtkWidget *menu,
3574 gboolean skip_first)
3580 GtkTreeRowReference *mref;
3584 list = gtk_container_get_children (GTK_CONTAINER (menu));
3587 for (i = list; i; i = i->next)
3589 child = gtk_bin_get_child (i->data);
3590 if (GTK_IS_SEPARATOR_MENU_ITEM (i->data))
3592 mref = g_object_get_data (G_OBJECT (i->data), "gtk-combo-box-item-path");
3595 else if (!gtk_tree_row_reference_valid (mref))
3598 mpath = gtk_tree_row_reference_get_path (mref);
3600 else if (GTK_IS_CELL_VIEW (child))
3608 mpath = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (child));
3613 /* this case is necessary, since the row reference of
3614 * the cell view may already be updated after a deletion
3621 if (gtk_tree_path_compare (mpath, path) == 0)
3623 gtk_tree_path_free (mpath);
3627 if (gtk_tree_path_is_ancestor (mpath, path))
3629 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
3630 if (submenu != NULL)
3632 gtk_tree_path_free (mpath);
3633 item = find_menu_by_path (submenu, path, TRUE);
3637 gtk_tree_path_free (mpath);
3647 dump_menu_tree (GtkWidget *menu,
3654 list = gtk_container_get_children (GTK_CONTAINER (menu));
3655 for (i = list; i; i = i->next)
3657 if (GTK_IS_CELL_VIEW (GTK_BIN (i->data)->child))
3659 path = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (GTK_BIN (i->data)->child));
3660 g_print ("%*s%s\n", 2 * level, " ", gtk_tree_path_to_string (path));
3661 gtk_tree_path_free (path);
3663 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
3664 if (submenu != NULL)
3665 dump_menu_tree (submenu, level + 1);
3674 gtk_combo_box_menu_row_inserted (GtkTreeModel *model,
3679 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3680 GtkComboBoxPrivate *priv = combo_box->priv;
3682 GtkWidget *item, *menu, *separator;
3686 gboolean is_separator;
3688 if (!priv->popup_widget)
3691 depth = gtk_tree_path_get_depth (path);
3692 pos = gtk_tree_path_get_indices (path)[depth - 1];
3695 ppath = gtk_tree_path_copy (path);
3696 gtk_tree_path_up (ppath);
3697 parent = find_menu_by_path (priv->popup_widget, ppath, FALSE);
3698 gtk_tree_path_free (ppath);
3700 menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (parent));
3703 menu = gtk_menu_new ();
3704 gtk_menu_set_reserve_toggle_size (GTK_MENU (menu), FALSE);
3705 gtk_widget_show (menu);
3706 gtk_menu_item_set_submenu (GTK_MENU_ITEM (parent), menu);
3708 /* Ugly - since menus can only activate leaves, we have to
3709 * duplicate the item inside the submenu.
3711 gtk_tree_model_iter_parent (model, &piter, iter);
3712 item = gtk_cell_view_menu_item_new (combo_box, model, &piter);
3713 separator = gtk_separator_menu_item_new ();
3714 g_signal_connect (item, "activate",
3715 G_CALLBACK (gtk_combo_box_menu_item_activate),
3717 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3718 gtk_menu_shell_append (GTK_MENU_SHELL (menu), separator);
3719 if (cell_view_is_sensitive (GTK_CELL_VIEW (gtk_bin_get_child (GTK_BIN (item)))))
3721 gtk_widget_show (item);
3722 gtk_widget_show (separator);
3729 menu = priv->popup_widget;
3730 if (priv->add_tearoffs)
3734 if (priv->row_separator_func)
3735 is_separator = priv->row_separator_func (model, iter,
3736 priv->row_separator_data);
3738 is_separator = FALSE;
3742 item = gtk_separator_menu_item_new ();
3743 g_object_set_data_full (G_OBJECT (item),
3744 I_("gtk-combo-box-item-path"),
3745 gtk_tree_row_reference_new (model, path),
3746 (GDestroyNotify)gtk_tree_row_reference_free);
3750 item = gtk_cell_view_menu_item_new (combo_box, model, iter);
3752 g_signal_connect (item, "activate",
3753 G_CALLBACK (gtk_combo_box_menu_item_activate),
3757 gtk_widget_show (item);
3758 gtk_menu_shell_insert (GTK_MENU_SHELL (menu), item, pos);
3762 gtk_combo_box_menu_row_deleted (GtkTreeModel *model,
3766 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3767 GtkComboBoxPrivate *priv = combo_box->priv;
3771 if (!priv->popup_widget)
3774 item = find_menu_by_path (priv->popup_widget, path, FALSE);
3775 menu = gtk_widget_get_parent (item);
3776 gtk_container_remove (GTK_CONTAINER (menu), item);
3778 if (gtk_tree_path_get_depth (path) > 1)
3780 GtkTreePath *parent_path;
3784 parent_path = gtk_tree_path_copy (path);
3785 gtk_tree_path_up (parent_path);
3786 gtk_tree_model_get_iter (model, &iter, parent_path);
3788 if (!gtk_tree_model_iter_has_child (model, &iter))
3790 parent = find_menu_by_path (priv->popup_widget,
3791 parent_path, FALSE);
3792 gtk_menu_item_set_submenu (GTK_MENU_ITEM (parent), NULL);
3798 gtk_combo_box_menu_rows_reordered (GtkTreeModel *model,
3804 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3806 gtk_combo_box_relayout (combo_box);
3810 gtk_combo_box_menu_row_changed (GtkTreeModel *model,
3815 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3816 GtkComboBoxPrivate *priv = combo_box->priv;
3818 gboolean is_separator;
3820 if (!priv->popup_widget)
3823 item = find_menu_by_path (priv->popup_widget, path, FALSE);
3825 if (priv->row_separator_func)
3826 is_separator = priv->row_separator_func (model, iter,
3827 priv->row_separator_data);
3829 is_separator = FALSE;
3831 if (is_separator != GTK_IS_SEPARATOR_MENU_ITEM (item))
3833 gtk_combo_box_menu_row_deleted (model, path, combo_box);
3834 gtk_combo_box_menu_row_inserted (model, path, iter, combo_box);
3837 if (priv->wrap_width &&
3838 gtk_widget_get_parent (item) == priv->popup_widget)
3840 GtkWidget *pitem = NULL;
3843 prev = gtk_tree_path_copy (path);
3845 if (gtk_tree_path_prev (prev))
3846 pitem = find_menu_by_path (priv->popup_widget, prev, FALSE);
3848 gtk_tree_path_free (prev);
3850 /* unattach item so gtk_combo_box_relayout_item() won't spuriously
3852 gtk_container_child_set (GTK_CONTAINER (priv->popup_widget),
3857 "bottom-attach", -1,
3860 gtk_combo_box_relayout_item (combo_box, item, iter, pitem);
3863 gtk_combo_box_update_requested_width (combo_box, path);
3871 gtk_combo_box_list_setup (GtkComboBox *combo_box)
3873 GtkComboBoxPrivate *priv = combo_box->priv;
3874 GtkTreeSelection *sel;
3877 GtkWidget *widget = GTK_WIDGET (combo_box);
3879 priv->button = gtk_toggle_button_new ();
3880 child = gtk_bin_get_child (GTK_BIN (combo_box));
3881 gtk_widget_set_parent (priv->button,
3882 gtk_widget_get_parent (child));
3883 g_signal_connect (priv->button, "button-press-event",
3884 G_CALLBACK (gtk_combo_box_list_button_pressed), combo_box);
3885 g_signal_connect (priv->button, "toggled",
3886 G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
3888 priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3889 gtk_container_add (GTK_CONTAINER (priv->button), priv->arrow);
3890 priv->separator = NULL;
3891 gtk_widget_show_all (priv->button);
3893 if (priv->cell_view)
3895 style = gtk_widget_get_style (widget);
3896 gtk_cell_view_set_background_color (GTK_CELL_VIEW (priv->cell_view),
3897 &style->base[gtk_widget_get_state (widget)]);
3899 priv->box = gtk_event_box_new ();
3900 gtk_event_box_set_visible_window (GTK_EVENT_BOX (priv->box),
3903 if (priv->has_frame)
3905 priv->cell_view_frame = gtk_frame_new (NULL);
3906 gtk_frame_set_shadow_type (GTK_FRAME (priv->cell_view_frame),
3911 combo_box->priv->cell_view_frame = gtk_event_box_new ();
3912 gtk_event_box_set_visible_window (GTK_EVENT_BOX (combo_box->priv->cell_view_frame),
3916 gtk_widget_set_parent (priv->cell_view_frame,
3917 gtk_widget_get_parent (child));
3918 gtk_container_add (GTK_CONTAINER (priv->cell_view_frame), priv->box);
3919 gtk_widget_show_all (priv->cell_view_frame);
3921 g_signal_connect (priv->box, "button-press-event",
3922 G_CALLBACK (gtk_combo_box_list_button_pressed),
3926 priv->tree_view = gtk_tree_view_new ();
3927 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));
3928 gtk_tree_selection_set_mode (sel, GTK_SELECTION_BROWSE);
3929 gtk_tree_selection_set_select_function (sel,
3930 gtk_combo_box_list_select_func,
3932 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->tree_view),
3934 gtk_tree_view_set_hover_selection (GTK_TREE_VIEW (priv->tree_view),
3936 if (priv->row_separator_func)
3937 gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (priv->tree_view),
3938 priv->row_separator_func,
3939 priv->row_separator_data,
3942 gtk_tree_view_set_model (GTK_TREE_VIEW (priv->tree_view), priv->model);
3944 priv->column = gtk_tree_view_column_new ();
3945 gtk_tree_view_append_column (GTK_TREE_VIEW (priv->tree_view), priv->column);
3948 gtk_combo_box_sync_cells (combo_box,
3949 GTK_CELL_LAYOUT (priv->column));
3951 if (gtk_tree_row_reference_valid (priv->active_row))
3955 path = gtk_tree_row_reference_get_path (priv->active_row);
3956 gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->tree_view),
3958 gtk_tree_path_free (path);
3961 /* set sample/popup widgets */
3962 gtk_combo_box_set_popup_widget (combo_box, priv->tree_view);
3964 g_signal_connect (priv->tree_view, "key-press-event",
3965 G_CALLBACK (gtk_combo_box_list_key_press),
3967 g_signal_connect (priv->tree_view, "enter-notify-event",
3968 G_CALLBACK (gtk_combo_box_list_enter_notify),
3970 g_signal_connect (priv->tree_view, "row-expanded",
3971 G_CALLBACK (gtk_combo_box_model_row_expanded),
3973 g_signal_connect (priv->tree_view, "row-collapsed",
3974 G_CALLBACK (gtk_combo_box_model_row_expanded),
3976 g_signal_connect (priv->popup_window, "button-press-event",
3977 G_CALLBACK (gtk_combo_box_list_button_pressed),
3979 g_signal_connect (priv->popup_window, "button-release-event",
3980 G_CALLBACK (gtk_combo_box_list_button_released),
3983 gtk_widget_show (priv->tree_view);
3985 gtk_combo_box_update_sensitivity (combo_box);
3989 gtk_combo_box_list_destroy (GtkComboBox *combo_box)
3991 GtkComboBoxPrivate *priv = combo_box->priv;
3993 /* disconnect signals */
3994 g_signal_handlers_disconnect_matched (priv->tree_view,
3995 G_SIGNAL_MATCH_DATA,
3996 0, 0, NULL, NULL, combo_box);
3997 g_signal_handlers_disconnect_matched (priv->button,
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_pressed,
4007 g_signal_handlers_disconnect_matched (priv->popup_window,
4008 G_SIGNAL_MATCH_DATA,
4010 gtk_combo_box_list_button_released,
4013 g_signal_handlers_disconnect_matched (priv->popup_window,
4014 G_SIGNAL_MATCH_DATA,
4016 gtk_combo_box_child_show,
4019 g_signal_handlers_disconnect_matched (priv->popup_window,
4020 G_SIGNAL_MATCH_DATA,
4022 gtk_combo_box_child_hide,
4026 g_signal_handlers_disconnect_matched (priv->box,
4027 G_SIGNAL_MATCH_DATA,
4029 gtk_combo_box_list_button_pressed,
4032 /* destroy things (unparent will kill the latest ref from us)
4033 * last unref on button will destroy the arrow
4035 gtk_widget_unparent (priv->button);
4036 priv->button = NULL;
4039 if (priv->cell_view)
4041 g_object_set (priv->cell_view,
4042 "background-set", FALSE,
4046 if (priv->cell_view_frame)
4048 gtk_widget_unparent (priv->cell_view_frame);
4049 priv->cell_view_frame = NULL;
4053 if (priv->scroll_timer)
4055 g_source_remove (priv->scroll_timer);
4056 priv->scroll_timer = 0;
4059 if (priv->resize_idle_id)
4061 g_source_remove (priv->resize_idle_id);
4062 priv->resize_idle_id = 0;
4065 gtk_widget_destroy (priv->tree_view);
4067 priv->tree_view = NULL;
4068 if (priv->popup_widget)
4070 g_object_unref (priv->popup_widget);
4071 priv->popup_widget = NULL;
4078 gtk_combo_box_list_button_pressed (GtkWidget *widget,
4079 GdkEventButton *event,
4082 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
4083 GtkComboBoxPrivate *priv = combo_box->priv;
4085 GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *)event);
4087 if (ewidget == priv->popup_window)
4090 if ((ewidget != priv->button && ewidget != priv->box) ||
4091 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->button)))
4094 if (priv->focus_on_click &&
4095 !gtk_widget_has_focus (priv->button))
4096 gtk_widget_grab_focus (priv->button);
4098 gtk_combo_box_popup_for_device (combo_box, event->device);
4100 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button), TRUE);
4102 priv->auto_scroll = FALSE;
4103 if (priv->scroll_timer == 0)
4104 priv->scroll_timer = gdk_threads_add_timeout (SCROLL_TIME,
4105 (GSourceFunc) gtk_combo_box_list_scroll_timeout,
4108 priv->popup_in_progress = TRUE;
4114 gtk_combo_box_list_button_released (GtkWidget *widget,
4115 GdkEventButton *event,
4119 GtkTreePath *path = NULL;
4122 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
4123 GtkComboBoxPrivate *priv = combo_box->priv;
4125 gboolean popup_in_progress = FALSE;
4127 GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *)event);
4129 if (priv->popup_in_progress)
4131 popup_in_progress = TRUE;
4132 priv->popup_in_progress = FALSE;
4135 gtk_tree_view_set_hover_expand (GTK_TREE_VIEW (priv->tree_view),
4137 if (priv->scroll_timer)
4139 g_source_remove (priv->scroll_timer);
4140 priv->scroll_timer = 0;
4143 if (ewidget != priv->tree_view)
4145 if ((ewidget == priv->button ||
4146 ewidget == priv->box) &&
4147 !popup_in_progress &&
4148 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->button)))
4150 gtk_combo_box_popdown (combo_box);
4154 /* released outside treeview */
4155 if (ewidget != priv->button && ewidget != priv->box)
4157 gtk_combo_box_popdown (combo_box);
4165 /* select something cool */
4166 ret = gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (priv->tree_view),
4172 return TRUE; /* clicked outside window? */
4174 gtk_tree_model_get_iter (priv->model, &iter, path);
4175 gtk_tree_path_free (path);
4177 gtk_combo_box_popdown (combo_box);
4179 if (tree_column_row_is_sensitive (combo_box, &iter))
4180 gtk_combo_box_set_active_iter (combo_box, &iter);
4186 gtk_combo_box_menu_key_press (GtkWidget *widget,
4190 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
4192 if (!gtk_bindings_activate_event (G_OBJECT (widget), event))
4194 /* The menu hasn't managed the
4195 * event, forward it to the combobox
4197 gtk_bindings_activate_event (G_OBJECT (combo_box), event);
4204 gtk_combo_box_list_key_press (GtkWidget *widget,
4208 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
4211 if (event->keyval == GDK_KEY_Return || event->keyval == GDK_KEY_ISO_Enter || event->keyval == GDK_KEY_KP_Enter ||
4212 event->keyval == GDK_KEY_space || event->keyval == GDK_KEY_KP_Space)
4214 GtkTreeModel *model = NULL;
4216 gtk_combo_box_popdown (combo_box);
4218 if (combo_box->priv->model)
4220 GtkTreeSelection *sel;
4222 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (combo_box->priv->tree_view));
4224 if (gtk_tree_selection_get_selected (sel, &model, &iter))
4225 gtk_combo_box_set_active_iter (combo_box, &iter);
4231 if (!gtk_bindings_activate_event (G_OBJECT (widget), event))
4233 /* The list hasn't managed the
4234 * event, forward it to the combobox
4236 gtk_bindings_activate_event (G_OBJECT (combo_box), event);
4243 gtk_combo_box_list_auto_scroll (GtkComboBox *combo_box,
4248 GtkAllocation allocation;
4249 GtkWidget *tree_view = combo_box->priv->tree_view;
4252 gtk_widget_get_allocation (tree_view, &allocation);
4254 adj = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window));
4255 if (adj && adj->upper - adj->lower > adj->page_size)
4257 if (x <= allocation.x &&
4258 adj->lower < adj->value)
4260 value = adj->value - (allocation.x - x + 1);
4261 gtk_adjustment_set_value (adj, value);
4263 else if (x >= allocation.x + allocation.width &&
4264 adj->upper - adj->page_size > adj->value)
4266 value = adj->value + (x - allocation.x - allocation.width + 1);
4267 gtk_adjustment_set_value (adj, MAX (value, 0.0));
4271 adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window));
4272 if (adj && adj->upper - adj->lower > adj->page_size)
4274 if (y <= allocation.y &&
4275 adj->lower < adj->value)
4277 value = adj->value - (allocation.y - y + 1);
4278 gtk_adjustment_set_value (adj, value);
4280 else if (y >= allocation.height &&
4281 adj->upper - adj->page_size > adj->value)
4283 value = adj->value + (y - allocation.height + 1);
4284 gtk_adjustment_set_value (adj, MAX (value, 0.0));
4290 gtk_combo_box_list_scroll_timeout (GtkComboBox *combo_box)
4292 GtkComboBoxPrivate *priv = combo_box->priv;
4295 if (priv->auto_scroll)
4297 gdk_window_get_device_position (gtk_widget_get_window (priv->tree_view),
4300 gtk_combo_box_list_auto_scroll (combo_box, x, y);
4307 gtk_combo_box_list_enter_notify (GtkWidget *widget,
4308 GdkEventCrossing *event,
4311 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
4313 combo_box->priv->auto_scroll = TRUE;
4319 gtk_combo_box_list_select_func (GtkTreeSelection *selection,
4320 GtkTreeModel *model,
4322 gboolean path_currently_selected,
4326 gboolean sensitive = FALSE;
4328 for (list = selection->tree_view->priv->columns; list && !sensitive; list = list->next)
4330 GList *cells, *cell;
4331 gboolean cell_sensitive, cell_visible;
4333 GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN (list->data);
4335 if (!column->visible)
4338 gtk_tree_model_get_iter (model, &iter, path);
4339 gtk_tree_view_column_cell_set_cell_data (column, model, &iter,
4342 cell = cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
4345 g_object_get (cell->data,
4346 "sensitive", &cell_sensitive,
4347 "visible", &cell_visible,
4350 if (cell_visible && cell_sensitive)
4355 g_list_free (cells);
4357 sensitive = cell_sensitive;
4364 gtk_combo_box_list_row_changed (GtkTreeModel *model,
4369 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
4371 gtk_combo_box_update_requested_width (combo_box, path);
4375 * GtkCellLayout implementation
4379 pack_start_recurse (GtkWidget *menu,
4380 GtkCellRenderer *cell,
4387 list = gtk_container_get_children (GTK_CONTAINER (menu));
4388 for (i = list; i; i = i->next)
4390 child = gtk_bin_get_child (GTK_BIN (i->data));
4391 if (GTK_IS_CELL_LAYOUT (child))
4392 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (child),
4395 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4396 if (submenu != NULL)
4397 pack_start_recurse (submenu, cell, expand);
4404 gtk_combo_box_cell_layout_pack_start (GtkCellLayout *layout,
4405 GtkCellRenderer *cell,
4408 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4409 ComboCellInfo *info;
4410 GtkComboBoxPrivate *priv;
4412 priv = combo_box->priv;
4414 g_object_ref_sink (cell);
4416 info = g_slice_new0 (ComboCellInfo);
4418 info->expand = expand;
4419 info->pack = GTK_PACK_START;
4421 priv->cells = g_slist_append (priv->cells, info);
4423 if (priv->cell_view)
4425 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->cell_view),
4431 gtk_tree_view_column_pack_start (priv->column, cell, expand);
4433 if (GTK_IS_MENU (priv->popup_widget))
4434 pack_start_recurse (priv->popup_widget, cell, expand);
4438 pack_end_recurse (GtkWidget *menu,
4439 GtkCellRenderer *cell,
4446 list = gtk_container_get_children (GTK_CONTAINER (menu));
4447 for (i = list; i; i = i->next)
4449 child = gtk_bin_get_child (GTK_BIN (i->data));
4450 if (GTK_IS_CELL_LAYOUT (child))
4451 gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (child),
4454 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4455 if (submenu != NULL)
4456 pack_end_recurse (submenu, cell, expand);
4463 gtk_combo_box_cell_layout_pack_end (GtkCellLayout *layout,
4464 GtkCellRenderer *cell,
4467 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4468 ComboCellInfo *info;
4469 GtkComboBoxPrivate *priv;
4471 priv = combo_box->priv;
4473 g_object_ref_sink (cell);
4475 info = g_slice_new0 (ComboCellInfo);
4477 info->expand = expand;
4478 info->pack = GTK_PACK_END;
4480 priv->cells = g_slist_append (priv->cells, info);
4482 if (priv->cell_view)
4483 gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (priv->cell_view),
4487 gtk_tree_view_column_pack_end (priv->column, cell, expand);
4489 if (GTK_IS_MENU (priv->popup_widget))
4490 pack_end_recurse (priv->popup_widget, cell, expand);
4494 gtk_combo_box_cell_layout_get_cells (GtkCellLayout *layout)
4496 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4498 GList *retval = NULL;
4500 for (list = combo_box->priv->cells; list; list = list->next)
4502 ComboCellInfo *info = (ComboCellInfo *)list->data;
4504 retval = g_list_prepend (retval, info->cell);
4507 return g_list_reverse (retval);
4511 clear_recurse (GtkWidget *menu)
4517 list = gtk_container_get_children (GTK_CONTAINER (menu));
4518 for (i = list; i; i = i->next)
4520 child = gtk_bin_get_child (GTK_BIN (i->data));
4521 if (GTK_IS_CELL_LAYOUT (child))
4522 gtk_cell_layout_clear (GTK_CELL_LAYOUT (child));
4524 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4525 if (submenu != NULL)
4526 clear_recurse (submenu);
4533 gtk_combo_box_cell_layout_clear (GtkCellLayout *layout)
4535 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4536 GtkComboBoxPrivate *priv = combo_box->priv;
4539 if (priv->cell_view)
4540 gtk_cell_layout_clear (GTK_CELL_LAYOUT (priv->cell_view));
4543 gtk_tree_view_column_clear (priv->column);
4545 for (i = priv->cells; i; i = i->next)
4547 ComboCellInfo *info = (ComboCellInfo *)i->data;
4549 gtk_combo_box_cell_layout_clear_attributes (layout, info->cell);
4550 g_object_unref (info->cell);
4551 g_slice_free (ComboCellInfo, info);
4554 g_slist_free (priv->cells);
4557 if (GTK_IS_MENU (priv->popup_widget))
4558 clear_recurse (priv->popup_widget);
4562 add_attribute_recurse (GtkWidget *menu,
4563 GtkCellRenderer *cell,
4564 const gchar *attribute,
4571 list = gtk_container_get_children (GTK_CONTAINER (menu));
4572 for (i = list; i; i = i->next)
4574 child = gtk_bin_get_child (GTK_BIN (i->data));
4575 if (GTK_IS_CELL_LAYOUT (child))
4576 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (child),
4577 cell, attribute, column);
4579 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4580 if (submenu != NULL)
4581 add_attribute_recurse (submenu, cell, attribute, column);
4588 gtk_combo_box_cell_layout_add_attribute (GtkCellLayout *layout,
4589 GtkCellRenderer *cell,
4590 const gchar *attribute,
4593 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4594 ComboCellInfo *info;
4596 info = gtk_combo_box_get_cell_info (combo_box, cell);
4597 g_return_if_fail (info != NULL);
4599 info->attributes = g_slist_prepend (info->attributes,
4600 GINT_TO_POINTER (column));
4601 info->attributes = g_slist_prepend (info->attributes,
4602 g_strdup (attribute));
4604 if (combo_box->priv->cell_view)
4605 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box->priv->cell_view),
4606 cell, attribute, column);
4608 if (combo_box->priv->column)
4609 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box->priv->column),
4610 cell, attribute, column);
4612 if (GTK_IS_MENU (combo_box->priv->popup_widget))
4613 add_attribute_recurse (combo_box->priv->popup_widget, cell, attribute, column);
4614 gtk_widget_queue_resize (GTK_WIDGET (combo_box));
4618 combo_cell_data_func (GtkCellLayout *cell_layout,
4619 GtkCellRenderer *cell,
4620 GtkTreeModel *tree_model,
4624 ComboCellInfo *info = (ComboCellInfo *)data;
4625 GtkWidget *parent = NULL;
4630 info->func (cell_layout, cell, tree_model, iter, info->func_data);
4632 if (GTK_IS_WIDGET (cell_layout))
4633 parent = gtk_widget_get_parent (GTK_WIDGET (cell_layout));
4635 if (GTK_IS_MENU_ITEM (parent) &&
4636 gtk_menu_item_get_submenu (GTK_MENU_ITEM (parent)))
4637 g_object_set (cell, "sensitive", TRUE, NULL);
4642 set_cell_data_func_recurse (GtkWidget *menu,
4643 GtkCellRenderer *cell,
4644 ComboCellInfo *info)
4648 GtkWidget *cell_view;
4650 list = gtk_container_get_children (GTK_CONTAINER (menu));
4651 for (i = list; i; i = i->next)
4653 cell_view = gtk_bin_get_child (GTK_BIN (i->data));
4654 if (GTK_IS_CELL_LAYOUT (cell_view))
4656 /* Override sensitivity for inner nodes; we don't
4657 * want menuitems with submenus to appear insensitive
4659 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (cell_view),
4661 combo_cell_data_func,
4663 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4664 if (submenu != NULL)
4665 set_cell_data_func_recurse (submenu, cell, info);
4673 gtk_combo_box_cell_layout_set_cell_data_func (GtkCellLayout *layout,
4674 GtkCellRenderer *cell,
4675 GtkCellLayoutDataFunc func,
4677 GDestroyNotify destroy)
4679 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4680 GtkComboBoxPrivate *priv = combo_box->priv;
4681 ComboCellInfo *info;
4683 info = gtk_combo_box_get_cell_info (combo_box, cell);
4684 g_return_if_fail (info != NULL);
4688 GDestroyNotify d = info->destroy;
4690 info->destroy = NULL;
4691 d (info->func_data);
4695 info->func_data = func_data;
4696 info->destroy = destroy;
4698 if (priv->cell_view)
4699 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (priv->cell_view), cell, func, func_data, NULL);
4702 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (priv->column), cell, func, func_data, NULL);
4704 if (GTK_IS_MENU (priv->popup_widget))
4705 set_cell_data_func_recurse (priv->popup_widget, cell, info);
4707 gtk_widget_queue_resize (GTK_WIDGET (combo_box));
4711 clear_attributes_recurse (GtkWidget *menu,
4712 GtkCellRenderer *cell)
4718 list = gtk_container_get_children (GTK_CONTAINER (menu));
4719 for (i = list; i; i = i->next)
4721 child = gtk_bin_get_child (GTK_BIN (i->data));
4722 if (GTK_IS_CELL_LAYOUT (child))
4723 gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (child), cell);
4725 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4726 if (submenu != NULL)
4727 clear_attributes_recurse (submenu, cell);
4734 gtk_combo_box_cell_layout_clear_attributes (GtkCellLayout *layout,
4735 GtkCellRenderer *cell)
4737 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4738 GtkComboBoxPrivate *priv;
4739 ComboCellInfo *info;
4742 priv = combo_box->priv;
4744 info = gtk_combo_box_get_cell_info (combo_box, cell);
4745 g_return_if_fail (info != NULL);
4747 list = info->attributes;
4748 while (list && list->next)
4750 g_free (list->data);
4751 list = list->next->next;
4753 g_slist_free (info->attributes);
4754 info->attributes = NULL;
4756 if (priv->cell_view)
4757 gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (priv->cell_view), cell);
4760 gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (priv->column), cell);
4762 if (GTK_IS_MENU (priv->popup_widget))
4763 clear_attributes_recurse (priv->popup_widget, cell);
4765 gtk_widget_queue_resize (GTK_WIDGET (combo_box));
4769 reorder_recurse (GtkWidget *menu,
4770 GtkCellRenderer *cell,
4777 list = gtk_container_get_children (GTK_CONTAINER (menu));
4778 for (i = list; i; i = i->next)
4780 child = gtk_bin_get_child (GTK_BIN (i->data));
4781 if (GTK_IS_CELL_LAYOUT (child))
4782 gtk_cell_layout_reorder (GTK_CELL_LAYOUT (child),
4785 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4786 if (submenu != NULL)
4787 reorder_recurse (submenu, cell, position);
4794 gtk_combo_box_cell_layout_reorder (GtkCellLayout *layout,
4795 GtkCellRenderer *cell,
4798 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4799 GtkComboBoxPrivate *priv;
4800 ComboCellInfo *info;
4803 priv = combo_box->priv;
4805 info = gtk_combo_box_get_cell_info (combo_box, cell);
4807 g_return_if_fail (info != NULL);
4808 g_return_if_fail (position >= 0);
4810 link = g_slist_find (priv->cells, info);
4812 g_return_if_fail (link != NULL);
4814 priv->cells = g_slist_delete_link (priv->cells, link);
4815 priv->cells = g_slist_insert (priv->cells, info, position);
4817 if (priv->cell_view)
4818 gtk_cell_layout_reorder (GTK_CELL_LAYOUT (priv->cell_view),
4822 gtk_cell_layout_reorder (GTK_CELL_LAYOUT (priv->column),
4825 if (GTK_IS_MENU (priv->popup_widget))
4826 reorder_recurse (priv->popup_widget, cell, position);
4828 gtk_widget_queue_draw (GTK_WIDGET (combo_box));
4836 * gtk_combo_box_new:
4838 * Creates a new empty #GtkComboBox.
4840 * Return value: A new #GtkComboBox.
4845 gtk_combo_box_new (void)
4847 return g_object_new (GTK_TYPE_COMBO_BOX, NULL);
4851 * gtk_combo_box_new_with_entry:
4853 * Creates a new empty #GtkComboBox with an entry.
4855 * Return value: A new #GtkComboBox.
4858 gtk_combo_box_new_with_entry (void)
4860 return g_object_new (GTK_TYPE_COMBO_BOX, "has-entry", TRUE, NULL);
4864 * gtk_combo_box_new_with_model:
4865 * @model: A #GtkTreeModel.
4867 * Creates a new #GtkComboBox with the model initialized to @model.
4869 * Return value: A new #GtkComboBox.
4874 gtk_combo_box_new_with_model (GtkTreeModel *model)
4876 GtkComboBox *combo_box;
4878 g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL);
4880 combo_box = g_object_new (GTK_TYPE_COMBO_BOX, "model", model, NULL);
4882 return GTK_WIDGET (combo_box);
4886 * gtk_combo_box_get_wrap_width:
4887 * @combo_box: A #GtkComboBox
4889 * Returns the wrap width which is used to determine the number of columns
4890 * for the popup menu. If the wrap width is larger than 1, the combo box
4893 * Returns: the wrap width.
4898 gtk_combo_box_get_wrap_width (GtkComboBox *combo_box)
4900 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), -1);
4902 return combo_box->priv->wrap_width;
4906 * gtk_combo_box_set_wrap_width:
4907 * @combo_box: A #GtkComboBox
4908 * @width: Preferred number of columns
4910 * Sets the wrap width of @combo_box to be @width. The wrap width is basically
4911 * the preferred number of columns when you want the popup to be layed out
4917 gtk_combo_box_set_wrap_width (GtkComboBox *combo_box,
4920 GtkComboBoxPrivate *priv;
4922 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4923 g_return_if_fail (width >= 0);
4925 priv = combo_box->priv;
4927 if (width != priv->wrap_width)
4929 priv->wrap_width = width;
4931 gtk_combo_box_check_appearance (combo_box);
4932 gtk_combo_box_relayout (combo_box);
4934 g_object_notify (G_OBJECT (combo_box), "wrap-width");
4939 * gtk_combo_box_get_row_span_column:
4940 * @combo_box: A #GtkComboBox
4942 * Returns the column with row span information for @combo_box.
4944 * Returns: the row span column.
4949 gtk_combo_box_get_row_span_column (GtkComboBox *combo_box)
4951 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), -1);
4953 return combo_box->priv->row_column;
4957 * gtk_combo_box_set_row_span_column:
4958 * @combo_box: A #GtkComboBox.
4959 * @row_span: A column in the model passed during construction.
4961 * Sets the column with row span information for @combo_box to be @row_span.
4962 * The row span column contains integers which indicate how many rows
4963 * an item should span.
4968 gtk_combo_box_set_row_span_column (GtkComboBox *combo_box,
4971 GtkComboBoxPrivate *priv;
4974 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4976 priv = combo_box->priv;
4978 col = gtk_tree_model_get_n_columns (priv->model);
4979 g_return_if_fail (row_span >= -1 && row_span < col);
4981 if (row_span != priv->row_column)
4983 priv->row_column = row_span;
4985 gtk_combo_box_relayout (combo_box);
4987 g_object_notify (G_OBJECT (combo_box), "row-span-column");
4992 * gtk_combo_box_get_column_span_column:
4993 * @combo_box: A #GtkComboBox
4995 * Returns the column with column span information for @combo_box.
4997 * Returns: the column span column.
5002 gtk_combo_box_get_column_span_column (GtkComboBox *combo_box)
5004 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), -1);
5006 return combo_box->priv->col_column;
5010 * gtk_combo_box_set_column_span_column:
5011 * @combo_box: A #GtkComboBox
5012 * @column_span: A column in the model passed during construction
5014 * Sets the column with column span information for @combo_box to be
5015 * @column_span. The column span column contains integers which indicate
5016 * how many columns an item should span.
5021 gtk_combo_box_set_column_span_column (GtkComboBox *combo_box,
5024 GtkComboBoxPrivate *priv;
5027 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5029 priv = combo_box->priv;
5031 col = gtk_tree_model_get_n_columns (priv->model);
5032 g_return_if_fail (column_span >= -1 && column_span < col);
5034 if (column_span != priv->col_column)
5036 priv->col_column = column_span;
5038 gtk_combo_box_relayout (combo_box);
5040 g_object_notify (G_OBJECT (combo_box), "column-span-column");
5045 * gtk_combo_box_get_active:
5046 * @combo_box: A #GtkComboBox
5048 * Returns the index of the currently active item, or -1 if there's no
5049 * active item. If the model is a non-flat treemodel, and the active item
5050 * is not an immediate child of the root of the tree, this function returns
5051 * <literal>gtk_tree_path_get_indices (path)[0]</literal>, where
5052 * <literal>path</literal> is the #GtkTreePath of the active item.
5054 * Return value: An integer which is the index of the currently active item,
5055 * or -1 if there's no active item.
5060 gtk_combo_box_get_active (GtkComboBox *combo_box)
5062 GtkComboBoxPrivate *priv;
5065 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), 0);
5067 priv = combo_box->priv;
5069 if (gtk_tree_row_reference_valid (priv->active_row))
5073 path = gtk_tree_row_reference_get_path (priv->active_row);
5074 result = gtk_tree_path_get_indices (path)[0];
5075 gtk_tree_path_free (path);
5084 * gtk_combo_box_set_active:
5085 * @combo_box: A #GtkComboBox
5086 * @index_: An index in the model passed during construction, or -1 to have
5089 * Sets the active item of @combo_box to be the item at @index.
5094 gtk_combo_box_set_active (GtkComboBox *combo_box,
5097 GtkTreePath *path = NULL;
5098 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5099 g_return_if_fail (index_ >= -1);
5101 if (combo_box->priv->model == NULL)
5103 /* Save index, in case the model is set after the index */
5104 combo_box->priv->active = index_;
5110 path = gtk_tree_path_new_from_indices (index_, -1);
5112 gtk_combo_box_set_active_internal (combo_box, path);
5115 gtk_tree_path_free (path);
5119 gtk_combo_box_set_active_internal (GtkComboBox *combo_box,
5122 GtkComboBoxPrivate *priv = combo_box->priv;
5123 GtkTreePath *active_path;
5126 /* Remember whether the initially active row is valid. */
5127 gboolean is_valid_row_reference = gtk_tree_row_reference_valid (priv->active_row);
5129 if (path && is_valid_row_reference)
5131 active_path = gtk_tree_row_reference_get_path (priv->active_row);
5132 path_cmp = gtk_tree_path_compare (path, active_path);
5133 gtk_tree_path_free (active_path);
5138 if (priv->active_row)
5140 gtk_tree_row_reference_free (priv->active_row);
5141 priv->active_row = NULL;
5146 if (priv->tree_view)
5147 gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view)));
5150 GtkMenu *menu = GTK_MENU (priv->popup_widget);
5152 if (GTK_IS_MENU (menu))
5153 gtk_menu_set_active (menu, -1);
5156 if (priv->cell_view)
5157 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (priv->cell_view), NULL);
5160 * Do not emit a "changed" signal when an already invalid selection was
5161 * now set to invalid.
5163 if (!is_valid_row_reference)
5169 gtk_tree_row_reference_new (priv->model, path);
5171 if (priv->tree_view)
5173 gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->tree_view),
5176 else if (GTK_IS_MENU (priv->popup_widget))
5178 /* FIXME handle nested menus better */
5179 gtk_menu_set_active (GTK_MENU (priv->popup_widget),
5180 gtk_tree_path_get_indices (path)[0]);
5183 if (priv->cell_view)
5184 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (priv->cell_view),
5188 g_signal_emit (combo_box, combo_box_signals[CHANGED], 0);
5189 g_object_notify (G_OBJECT (combo_box), "active");
5194 * gtk_combo_box_get_active_iter:
5195 * @combo_box: A #GtkComboBox
5196 * @iter: (out): The uninitialized #GtkTreeIter
5198 * Sets @iter to point to the current active item, if it exists.
5200 * Return value: %TRUE, if @iter was set
5205 gtk_combo_box_get_active_iter (GtkComboBox *combo_box,
5211 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
5213 if (!gtk_tree_row_reference_valid (combo_box->priv->active_row))
5216 path = gtk_tree_row_reference_get_path (combo_box->priv->active_row);
5217 result = gtk_tree_model_get_iter (combo_box->priv->model, iter, path);
5218 gtk_tree_path_free (path);
5224 * gtk_combo_box_set_active_iter:
5225 * @combo_box: A #GtkComboBox
5226 * @iter: (allow-none): The #GtkTreeIter, or %NULL
5228 * Sets the current active item to be the one referenced by @iter, or
5229 * unsets the active item if @iter is %NULL.
5234 gtk_combo_box_set_active_iter (GtkComboBox *combo_box,
5237 GtkTreePath *path = NULL;
5239 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5242 path = gtk_tree_model_get_path (gtk_combo_box_get_model (combo_box), iter);
5244 gtk_combo_box_set_active_internal (combo_box, path);
5245 gtk_tree_path_free (path);
5249 * gtk_combo_box_set_model:
5250 * @combo_box: A #GtkComboBox
5251 * @model: (allow-none): A #GtkTreeModel
5253 * Sets the model used by @combo_box to be @model. Will unset a previously set
5254 * model (if applicable). If model is %NULL, then it will unset the model.
5256 * Note that this function does not clear the cell renderers, you have to
5257 * call gtk_cell_layout_clear() yourself if you need to set up different
5258 * cell renderers for the new model.
5263 gtk_combo_box_set_model (GtkComboBox *combo_box,
5264 GtkTreeModel *model)
5266 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5267 g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
5269 if (model == combo_box->priv->model)
5272 gtk_combo_box_unset_model (combo_box);
5277 combo_box->priv->model = model;
5278 g_object_ref (combo_box->priv->model);
5280 combo_box->priv->inserted_id =
5281 g_signal_connect (combo_box->priv->model, "row-inserted",
5282 G_CALLBACK (gtk_combo_box_model_row_inserted),
5284 combo_box->priv->deleted_id =
5285 g_signal_connect (combo_box->priv->model, "row-deleted",
5286 G_CALLBACK (gtk_combo_box_model_row_deleted),
5288 combo_box->priv->reordered_id =
5289 g_signal_connect (combo_box->priv->model, "rows-reordered",
5290 G_CALLBACK (gtk_combo_box_model_rows_reordered),
5292 combo_box->priv->changed_id =
5293 g_signal_connect (combo_box->priv->model, "row-changed",
5294 G_CALLBACK (gtk_combo_box_model_row_changed),
5297 if (combo_box->priv->tree_view)
5300 gtk_tree_view_set_model (GTK_TREE_VIEW (combo_box->priv->tree_view),
5301 combo_box->priv->model);
5302 gtk_combo_box_list_popup_resize (combo_box);
5307 if (combo_box->priv->popup_widget)
5308 gtk_combo_box_menu_fill (combo_box);
5312 if (combo_box->priv->cell_view)
5313 gtk_cell_view_set_model (GTK_CELL_VIEW (combo_box->priv->cell_view),
5314 combo_box->priv->model);
5316 if (combo_box->priv->active != -1)
5318 /* If an index was set in advance, apply it now */
5319 gtk_combo_box_set_active (combo_box, combo_box->priv->active);
5320 combo_box->priv->active = -1;
5324 gtk_combo_box_update_sensitivity (combo_box);
5326 g_object_notify (G_OBJECT (combo_box), "model");
5330 * gtk_combo_box_get_model:
5331 * @combo_box: A #GtkComboBox
5333 * Returns the #GtkTreeModel which is acting as data source for @combo_box.
5335 * Return value: (transfer none): A #GtkTreeModel which was passed
5336 * during construction.
5341 gtk_combo_box_get_model (GtkComboBox *combo_box)
5343 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
5345 return combo_box->priv->model;
5349 gtk_combo_box_real_move_active (GtkComboBox *combo_box,
5350 GtkScrollType scroll)
5353 GtkTreeIter new_iter;
5354 gboolean active_iter;
5357 if (!combo_box->priv->model)
5359 gtk_widget_error_bell (GTK_WIDGET (combo_box));
5363 active_iter = gtk_combo_box_get_active_iter (combo_box, &iter);
5367 case GTK_SCROLL_STEP_BACKWARD:
5368 case GTK_SCROLL_STEP_UP:
5369 case GTK_SCROLL_STEP_LEFT:
5372 found = tree_prev (combo_box, combo_box->priv->model,
5373 &iter, &new_iter, FALSE);
5376 /* else fall through */
5378 case GTK_SCROLL_PAGE_FORWARD:
5379 case GTK_SCROLL_PAGE_DOWN:
5380 case GTK_SCROLL_PAGE_RIGHT:
5381 case GTK_SCROLL_END:
5382 found = tree_last (combo_box, combo_box->priv->model, &new_iter, FALSE);
5385 case GTK_SCROLL_STEP_FORWARD:
5386 case GTK_SCROLL_STEP_DOWN:
5387 case GTK_SCROLL_STEP_RIGHT:
5390 found = tree_next (combo_box, combo_box->priv->model,
5391 &iter, &new_iter, FALSE);
5394 /* else fall through */
5396 case GTK_SCROLL_PAGE_BACKWARD:
5397 case GTK_SCROLL_PAGE_UP:
5398 case GTK_SCROLL_PAGE_LEFT:
5399 case GTK_SCROLL_START:
5400 found = tree_first (combo_box, combo_box->priv->model, &new_iter, FALSE);
5407 if (found && active_iter)
5409 GtkTreePath *old_path;
5410 GtkTreePath *new_path;
5412 old_path = gtk_tree_model_get_path (combo_box->priv->model, &iter);
5413 new_path = gtk_tree_model_get_path (combo_box->priv->model, &new_iter);
5415 if (gtk_tree_path_compare (old_path, new_path) == 0)
5418 gtk_tree_path_free (old_path);
5419 gtk_tree_path_free (new_path);
5424 gtk_combo_box_set_active_iter (combo_box, &new_iter);
5428 gtk_widget_error_bell (GTK_WIDGET (combo_box));
5433 gtk_combo_box_mnemonic_activate (GtkWidget *widget,
5434 gboolean group_cycling)
5436 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
5438 if (combo_box->priv->has_entry)
5442 child = gtk_bin_get_child (GTK_BIN (combo_box));
5444 gtk_widget_grab_focus (child);
5447 gtk_widget_grab_focus (combo_box->priv->button);
5453 gtk_combo_box_grab_focus (GtkWidget *widget)
5455 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
5457 if (combo_box->priv->has_entry)
5461 child = gtk_bin_get_child (GTK_BIN (combo_box));
5463 gtk_widget_grab_focus (child);
5466 gtk_widget_grab_focus (combo_box->priv->button);
5470 gtk_combo_box_destroy (GtkWidget *widget)
5472 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
5474 if (combo_box->priv->popup_idle_id > 0)
5476 g_source_remove (combo_box->priv->popup_idle_id);
5477 combo_box->priv->popup_idle_id = 0;
5480 gtk_combo_box_popdown (combo_box);
5482 if (combo_box->priv->row_separator_destroy)
5483 combo_box->priv->row_separator_destroy (combo_box->priv->row_separator_data);
5485 combo_box->priv->row_separator_func = NULL;
5486 combo_box->priv->row_separator_data = NULL;
5487 combo_box->priv->row_separator_destroy = NULL;
5489 GTK_WIDGET_CLASS (gtk_combo_box_parent_class)->destroy (widget);
5490 combo_box->priv->cell_view = NULL;
5494 gtk_combo_box_entry_contents_changed (GtkEntry *entry,
5497 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
5500 * Fixes regression reported in bug #574059. The old functionality relied on
5501 * bug #572478. As a bugfix, we now emit the "changed" signal ourselves
5502 * when the selection was already set to -1.
5504 if (gtk_combo_box_get_active(combo_box) == -1)
5505 g_signal_emit_by_name (combo_box, "changed");
5507 gtk_combo_box_set_active (combo_box, -1);
5511 gtk_combo_box_entry_active_changed (GtkComboBox *combo_box,
5514 GtkComboBoxPrivate *priv = combo_box->priv;
5515 GtkTreeModel *model;
5518 if (gtk_combo_box_get_active_iter (combo_box, &iter))
5520 GtkEntry *entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (combo_box)));
5524 GValue value = {0,};
5526 g_signal_handlers_block_by_func (entry,
5527 gtk_combo_box_entry_contents_changed,
5530 model = gtk_combo_box_get_model (combo_box);
5532 gtk_tree_model_get_value (model, &iter,
5533 priv->text_column, &value);
5534 g_object_set_property (G_OBJECT (entry), "text", &value);
5535 g_value_unset (&value);
5537 g_signal_handlers_unblock_by_func (entry,
5538 gtk_combo_box_entry_contents_changed,
5545 gtk_combo_box_constructor (GType type,
5546 guint n_construct_properties,
5547 GObjectConstructParam *construct_properties)
5550 GtkComboBox *combo_box;
5551 GtkComboBoxPrivate *priv;
5553 object = G_OBJECT_CLASS (gtk_combo_box_parent_class)->constructor
5554 (type, n_construct_properties, construct_properties);
5556 combo_box = GTK_COMBO_BOX (object);
5557 priv = combo_box->priv;
5559 if (priv->has_entry)
5563 entry = gtk_entry_new ();
5564 gtk_widget_show (entry);
5565 gtk_container_add (GTK_CONTAINER (combo_box), entry);
5567 priv->text_renderer = gtk_cell_renderer_text_new ();
5568 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box),
5569 priv->text_renderer, TRUE);
5571 gtk_combo_box_set_active (GTK_COMBO_BOX (combo_box), -1);
5573 g_signal_connect (combo_box, "changed",
5574 G_CALLBACK (gtk_combo_box_entry_active_changed), NULL);
5582 gtk_combo_box_dispose(GObject* object)
5584 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
5586 if (GTK_IS_MENU (combo_box->priv->popup_widget))
5588 gtk_combo_box_menu_destroy (combo_box);
5589 gtk_menu_detach (GTK_MENU (combo_box->priv->popup_widget));
5590 combo_box->priv->popup_widget = NULL;
5593 G_OBJECT_CLASS (gtk_combo_box_parent_class)->dispose (object);
5597 gtk_combo_box_finalize (GObject *object)
5599 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
5602 if (GTK_IS_TREE_VIEW (combo_box->priv->tree_view))
5603 gtk_combo_box_list_destroy (combo_box);
5605 if (combo_box->priv->popup_window)
5606 gtk_widget_destroy (combo_box->priv->popup_window);
5608 gtk_combo_box_unset_model (combo_box);
5610 for (i = combo_box->priv->cells; i; i = i->next)
5612 ComboCellInfo *info = (ComboCellInfo *)i->data;
5613 GSList *list = info->attributes;
5616 info->destroy (info->func_data);
5618 while (list && list->next)
5620 g_free (list->data);
5621 list = list->next->next;
5623 g_slist_free (info->attributes);
5625 g_object_unref (info->cell);
5626 g_slice_free (ComboCellInfo, info);
5628 g_slist_free (combo_box->priv->cells);
5630 g_free (combo_box->priv->tearoff_title);
5632 G_OBJECT_CLASS (gtk_combo_box_parent_class)->finalize (object);
5637 gtk_cell_editable_key_press (GtkWidget *widget,
5641 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
5643 if (event->keyval == GDK_KEY_Escape)
5645 g_object_set (combo_box,
5646 "editing-canceled", TRUE,
5648 gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (combo_box));
5649 gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (combo_box));
5653 else if (event->keyval == GDK_KEY_Return ||
5654 event->keyval == GDK_KEY_ISO_Enter ||
5655 event->keyval == GDK_KEY_KP_Enter)
5657 gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (combo_box));
5658 gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (combo_box));
5667 popdown_idle (gpointer data)
5669 GtkComboBox *combo_box;
5671 combo_box = GTK_COMBO_BOX (data);
5673 gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (combo_box));
5674 gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (combo_box));
5676 g_object_unref (combo_box);
5682 popdown_handler (GtkWidget *widget,
5685 gdk_threads_add_idle (popdown_idle, g_object_ref (data));
5689 popup_idle (gpointer data)
5691 GtkComboBox *combo_box;
5693 combo_box = GTK_COMBO_BOX (data);
5695 if (GTK_IS_MENU (combo_box->priv->popup_widget) &&
5696 combo_box->priv->cell_view)
5697 g_signal_connect_object (combo_box->priv->popup_widget,
5698 "unmap", G_CALLBACK (popdown_handler),
5701 /* we unset this if a menu item is activated */
5702 g_object_set (combo_box,
5703 "editing-canceled", TRUE,
5705 gtk_combo_box_popup (combo_box);
5707 combo_box->priv->popup_idle_id = 0;
5708 combo_box->priv->activate_button = 0;
5709 combo_box->priv->activate_time = 0;
5715 gtk_combo_box_start_editing (GtkCellEditable *cell_editable,
5718 GtkComboBox *combo_box = GTK_COMBO_BOX (cell_editable);
5721 combo_box->priv->is_cell_renderer = TRUE;
5723 if (combo_box->priv->cell_view)
5725 g_signal_connect_object (combo_box->priv->button, "key-press-event",
5726 G_CALLBACK (gtk_cell_editable_key_press),
5729 gtk_widget_grab_focus (combo_box->priv->button);
5733 child = gtk_bin_get_child (GTK_BIN (combo_box));
5735 g_signal_connect_object (child, "key-press-event",
5736 G_CALLBACK (gtk_cell_editable_key_press),
5739 gtk_widget_grab_focus (child);
5740 gtk_widget_set_can_focus (combo_box->priv->button, FALSE);
5743 /* we do the immediate popup only for the optionmenu-like
5746 if (combo_box->priv->is_cell_renderer &&
5747 combo_box->priv->cell_view && !combo_box->priv->tree_view)
5749 if (event && event->type == GDK_BUTTON_PRESS)
5751 GdkEventButton *event_button = (GdkEventButton *)event;
5753 combo_box->priv->activate_button = event_button->button;
5754 combo_box->priv->activate_time = event_button->time;
5757 combo_box->priv->popup_idle_id =
5758 gdk_threads_add_idle (popup_idle, combo_box);
5764 * gtk_combo_box_get_add_tearoffs:
5765 * @combo_box: a #GtkComboBox
5767 * Gets the current value of the :add-tearoffs property.
5769 * Return value: the current value of the :add-tearoffs property.
5772 gtk_combo_box_get_add_tearoffs (GtkComboBox *combo_box)
5774 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
5776 return combo_box->priv->add_tearoffs;
5780 * gtk_combo_box_set_add_tearoffs:
5781 * @combo_box: a #GtkComboBox
5782 * @add_tearoffs: %TRUE to add tearoff menu items
5784 * Sets whether the popup menu should have a tearoff
5790 gtk_combo_box_set_add_tearoffs (GtkComboBox *combo_box,
5791 gboolean add_tearoffs)
5793 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5795 add_tearoffs = add_tearoffs != FALSE;
5797 if (combo_box->priv->add_tearoffs != add_tearoffs)
5799 combo_box->priv->add_tearoffs = add_tearoffs;
5800 gtk_combo_box_check_appearance (combo_box);
5801 gtk_combo_box_relayout (combo_box);
5802 g_object_notify (G_OBJECT (combo_box), "add-tearoffs");
5807 * gtk_combo_box_get_title:
5808 * @combo_box: a #GtkComboBox
5810 * Gets the current title of the menu in tearoff mode. See
5811 * gtk_combo_box_set_add_tearoffs().
5813 * Returns: the menu's title in tearoff mode. This is an internal copy of the
5814 * string which must not be freed.
5818 G_CONST_RETURN gchar*
5819 gtk_combo_box_get_title (GtkComboBox *combo_box)
5821 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
5823 return combo_box->priv->tearoff_title;
5827 gtk_combo_box_update_title (GtkComboBox *combo_box)
5829 gtk_combo_box_check_appearance (combo_box);
5831 if (combo_box->priv->popup_widget &&
5832 GTK_IS_MENU (combo_box->priv->popup_widget))
5833 gtk_menu_set_title (GTK_MENU (combo_box->priv->popup_widget),
5834 combo_box->priv->tearoff_title);
5838 * gtk_combo_box_set_title:
5839 * @combo_box: a #GtkComboBox
5840 * @title: a title for the menu in tearoff mode
5842 * Sets the menu's title in tearoff mode.
5847 gtk_combo_box_set_title (GtkComboBox *combo_box,
5850 GtkComboBoxPrivate *priv;
5852 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5854 priv = combo_box->priv;
5856 if (strcmp (title ? title : "",
5857 priv->tearoff_title ? priv->tearoff_title : "") != 0)
5859 g_free (priv->tearoff_title);
5860 priv->tearoff_title = g_strdup (title);
5862 gtk_combo_box_update_title (combo_box);
5864 g_object_notify (G_OBJECT (combo_box), "tearoff-title");
5870 * gtk_combo_box_set_popup_fixed_width:
5871 * @combo_box: a #GtkComboBox
5872 * @fixed: whether to use a fixed popup width
5874 * Specifies whether the popup's width should be a fixed width
5875 * matching the allocated width of the combo box.
5880 gtk_combo_box_set_popup_fixed_width (GtkComboBox *combo_box,
5883 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5885 if (combo_box->priv->popup_fixed_width != fixed)
5887 combo_box->priv->popup_fixed_width = fixed;
5889 g_object_notify (G_OBJECT (combo_box), "popup-fixed-width");
5894 * gtk_combo_box_get_popup_fixed_width:
5895 * @combo_box: a #GtkComboBox
5897 * Gets whether the popup uses a fixed width matching
5898 * the allocated width of the combo box.
5900 * Returns: %TRUE if the popup uses a fixed width
5905 gtk_combo_box_get_popup_fixed_width (GtkComboBox *combo_box)
5907 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
5909 return combo_box->priv->popup_fixed_width;
5914 * gtk_combo_box_get_popup_accessible:
5915 * @combo_box: a #GtkComboBox
5917 * Gets the accessible object corresponding to the combo box's popup.
5919 * This function is mostly intended for use by accessibility technologies;
5920 * applications should have little use for it.
5922 * Returns: (transfer none): the accessible object corresponding
5923 * to the combo box's popup.
5928 gtk_combo_box_get_popup_accessible (GtkComboBox *combo_box)
5932 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
5934 if (combo_box->priv->popup_widget)
5936 atk_obj = gtk_widget_get_accessible (combo_box->priv->popup_widget);
5944 * gtk_combo_box_get_row_separator_func:
5945 * @combo_box: a #GtkComboBox
5947 * Returns the current row separator function.
5949 * Return value: the current row separator function.
5953 GtkTreeViewRowSeparatorFunc
5954 gtk_combo_box_get_row_separator_func (GtkComboBox *combo_box)
5956 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
5958 return combo_box->priv->row_separator_func;
5962 * gtk_combo_box_set_row_separator_func:
5963 * @combo_box: a #GtkComboBox
5964 * @func: a #GtkTreeViewRowSeparatorFunc
5965 * @data: (allow-none): user data to pass to @func, or %NULL
5966 * @destroy: (allow-none): destroy notifier for @data, or %NULL
5968 * Sets the row separator function, which is used to determine
5969 * whether a row should be drawn as a separator. If the row separator
5970 * function is %NULL, no separators are drawn. This is the default value.
5975 gtk_combo_box_set_row_separator_func (GtkComboBox *combo_box,
5976 GtkTreeViewRowSeparatorFunc func,
5978 GDestroyNotify destroy)
5980 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5982 if (combo_box->priv->row_separator_destroy)
5983 combo_box->priv->row_separator_destroy (combo_box->priv->row_separator_data);
5985 combo_box->priv->row_separator_func = func;
5986 combo_box->priv->row_separator_data = data;
5987 combo_box->priv->row_separator_destroy = destroy;
5989 if (combo_box->priv->tree_view)
5990 gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (combo_box->priv->tree_view),
5993 gtk_combo_box_relayout (combo_box);
5995 gtk_widget_queue_draw (GTK_WIDGET (combo_box));
5999 * gtk_combo_box_set_button_sensitivity:
6000 * @combo_box: a #GtkComboBox
6001 * @sensitivity: specify the sensitivity of the dropdown button
6003 * Sets whether the dropdown button of the combo box should be
6004 * always sensitive (%GTK_SENSITIVITY_ON), never sensitive (%GTK_SENSITIVITY_OFF)
6005 * or only if there is at least one item to display (%GTK_SENSITIVITY_AUTO).
6010 gtk_combo_box_set_button_sensitivity (GtkComboBox *combo_box,
6011 GtkSensitivityType sensitivity)
6013 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
6015 if (combo_box->priv->button_sensitivity != sensitivity)
6017 combo_box->priv->button_sensitivity = sensitivity;
6018 gtk_combo_box_update_sensitivity (combo_box);
6020 g_object_notify (G_OBJECT (combo_box), "button-sensitivity");
6025 * gtk_combo_box_get_button_sensitivity:
6026 * @combo_box: a #GtkComboBox
6028 * Returns whether the combo box sets the dropdown button
6029 * sensitive or not when there are no items in the model.
6031 * Return Value: %GTK_SENSITIVITY_ON if the dropdown button
6032 * is sensitive when the model is empty, %GTK_SENSITIVITY_OFF
6033 * if the button is always insensitive or
6034 * %GTK_SENSITIVITY_AUTO if it is only sensitive as long as
6035 * the model has one item to be selected.
6040 gtk_combo_box_get_button_sensitivity (GtkComboBox *combo_box)
6042 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
6044 return combo_box->priv->button_sensitivity;
6049 * gtk_combo_box_get_has_entry:
6050 * @combo_box: a #GtkComboBox
6052 * Returns whether the combo box has an entry.
6054 * Return Value: whether there is an entry in @combo_box.
6059 gtk_combo_box_get_has_entry (GtkComboBox *combo_box)
6061 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
6063 return combo_box->priv->has_entry;
6067 * gtk_combo_box_set_entry_text_column:
6068 * @combo_box: A #GtkComboBox
6069 * @text_column: A column in @model to get the strings from for
6070 * the internal entry
6072 * Sets the model column which @combo_box should use to get strings from
6073 * to be @text_column. The column @text_column in the model of @combo_box
6074 * must be of type %G_TYPE_STRING.
6076 * This is only relevant if @combo_box has been created with
6077 * #GtkComboBox:has-entry as %TRUE.
6082 gtk_combo_box_set_entry_text_column (GtkComboBox *combo_box,
6085 GtkComboBoxPrivate *priv = combo_box->priv;
6086 GtkTreeModel *model;
6088 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
6090 model = gtk_combo_box_get_model (combo_box);
6092 g_return_if_fail (text_column >= 0);
6093 g_return_if_fail (model == NULL || text_column < gtk_tree_model_get_n_columns (model));
6095 priv->text_column = text_column;
6097 if (priv->text_renderer != NULL)
6098 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box),
6099 priv->text_renderer,
6100 "text", text_column,
6105 * gtk_combo_box_get_entry_text_column:
6106 * @combo_box: A #GtkComboBox.
6108 * Returns the column which @combo_box is using to get the strings
6109 * from to display in the internal entry.
6111 * Return value: A column in the data source model of @combo_box.
6116 gtk_combo_box_get_entry_text_column (GtkComboBox *combo_box)
6118 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), 0);
6120 return combo_box->priv->text_column;
6124 * gtk_combo_box_set_focus_on_click:
6125 * @combo: a #GtkComboBox
6126 * @focus_on_click: whether the combo box grabs focus when clicked
6129 * Sets whether the combo box will grab focus when it is clicked with
6130 * the mouse. Making mouse clicks not grab focus is useful in places
6131 * like toolbars where you don't want the keyboard focus removed from
6132 * the main area of the application.
6137 gtk_combo_box_set_focus_on_click (GtkComboBox *combo_box,
6138 gboolean focus_on_click)
6140 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
6142 focus_on_click = focus_on_click != FALSE;
6144 if (combo_box->priv->focus_on_click != focus_on_click)
6146 combo_box->priv->focus_on_click = focus_on_click;
6148 if (combo_box->priv->button)
6149 gtk_button_set_focus_on_click (GTK_BUTTON (combo_box->priv->button),
6152 g_object_notify (G_OBJECT (combo_box), "focus-on-click");
6157 * gtk_combo_box_get_focus_on_click:
6158 * @combo: a #GtkComboBox
6160 * Returns whether the combo box grabs focus when it is clicked
6161 * with the mouse. See gtk_combo_box_set_focus_on_click().
6163 * Return value: %TRUE if the combo box grabs focus when it is
6164 * clicked with the mouse.
6169 gtk_combo_box_get_focus_on_click (GtkComboBox *combo_box)
6171 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
6173 return combo_box->priv->focus_on_click;
6178 gtk_combo_box_buildable_custom_tag_start (GtkBuildable *buildable,
6179 GtkBuilder *builder,
6181 const gchar *tagname,
6182 GMarkupParser *parser,
6185 if (parent_buildable_iface->custom_tag_start (buildable, builder, child,
6186 tagname, parser, data))
6189 return _gtk_cell_layout_buildable_custom_tag_start (buildable, builder, child,
6190 tagname, parser, data);
6194 gtk_combo_box_buildable_custom_tag_end (GtkBuildable *buildable,
6195 GtkBuilder *builder,
6197 const gchar *tagname,
6200 if (strcmp (tagname, "attributes") == 0)
6201 _gtk_cell_layout_buildable_custom_tag_end (buildable, builder, child, tagname,
6204 parent_buildable_iface->custom_tag_end (buildable, builder, child, tagname,
6209 gtk_combo_box_buildable_get_internal_child (GtkBuildable *buildable,
6210 GtkBuilder *builder,
6211 const gchar *childname)
6213 GtkComboBox *combo_box = GTK_COMBO_BOX (buildable);
6215 if (combo_box->priv->has_entry && strcmp (childname, "entry") == 0)
6216 return G_OBJECT (gtk_bin_get_child (GTK_BIN (buildable)));
6218 return parent_buildable_iface->get_internal_child (buildable, builder, childname);
6222 gtk_combo_box_remeasure (GtkComboBox *combo_box)
6224 GtkComboBoxPrivate *priv = combo_box->priv;
6229 !gtk_tree_model_get_iter_first (priv->model, &iter))
6232 priv->minimum_width = priv->natural_width = 0;
6234 path = gtk_tree_path_new_from_indices (0, -1);
6238 gint row_min = 0, row_nat = 0;
6240 if (priv->cell_view)
6241 gtk_cell_view_get_desired_width_of_row (GTK_CELL_VIEW (priv->cell_view),
6242 path, &row_min, &row_nat);
6244 priv->minimum_width = MAX (priv->minimum_width, row_min);
6245 priv->natural_width = MAX (priv->natural_width, row_nat);
6247 gtk_tree_path_next (path);
6249 while (gtk_tree_model_iter_next (priv->model, &iter));
6251 gtk_tree_path_free (path);
6256 gtk_combo_box_measure_height_for_width (GtkComboBox *combo_box,
6262 GtkComboBoxPrivate *priv = combo_box->priv;
6265 gint child_min, child_nat;
6267 child = gtk_bin_get_child (GTK_BIN (combo_box));
6269 gtk_widget_get_preferred_height_for_width (child, avail_width,
6270 &child_min, &child_nat);
6273 !gtk_tree_model_get_iter_first (priv->model, &iter))
6276 path = gtk_tree_path_new_from_indices (0, -1);
6280 gint row_min = 0, row_nat = 0;
6282 if (priv->cell_view)
6283 gtk_cell_view_get_desired_height_for_width_of_row (GTK_CELL_VIEW (priv->cell_view),
6285 &row_min, &row_nat);
6287 child_min = MAX (child_min, row_min);
6288 child_nat = MAX (child_nat, row_nat);
6290 gtk_tree_path_next (path);
6292 while (gtk_tree_model_iter_next (priv->model, &iter));
6294 gtk_tree_path_free (path);
6299 *min_height = child_min;
6301 *nat_height = child_nat;
6306 gtk_combo_box_get_preferred_width (GtkWidget *widget,
6310 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
6311 GtkComboBoxPrivate *priv = combo_box->priv;
6313 gint focus_width, focus_pad;
6314 gint font_size, arrow_size;
6315 PangoContext *context;
6316 PangoFontMetrics *metrics;
6317 PangoFontDescription *font_desc;
6319 gint minimum_width, natural_width;
6320 gint child_min, child_nat;
6322 child = gtk_bin_get_child (GTK_BIN (widget));
6325 gtk_widget_get_preferred_width (child, &child_min, &child_nat);
6326 gtk_combo_box_remeasure (combo_box);
6328 child_min = MAX (child_min, priv->minimum_width);
6329 child_nat = MAX (child_nat, priv->natural_width);
6331 gtk_widget_style_get (GTK_WIDGET (widget),
6332 "focus-line-width", &focus_width,
6333 "focus-padding", &focus_pad,
6334 "arrow-size", &arrow_size,
6337 font_desc = gtk_widget_get_style (child)->font_desc;
6338 context = gtk_widget_get_pango_context (GTK_WIDGET (widget));
6339 metrics = pango_context_get_metrics (context, font_desc,
6340 pango_context_get_language (context));
6341 font_size = PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) +
6342 pango_font_metrics_get_descent (metrics));
6343 pango_font_metrics_unref (metrics);
6345 arrow_size = MAX (arrow_size, font_size);
6347 gtk_widget_set_size_request (priv->arrow, arrow_size, arrow_size);
6349 if (!priv->tree_view)
6353 if (priv->cell_view)
6355 gint sep_width, arrow_width;
6356 gint border_width, xthickness, xpad;
6358 border_width = gtk_container_get_border_width (GTK_CONTAINER (combo_box));
6359 xthickness = gtk_widget_get_style (priv->button)->xthickness;
6361 gtk_widget_get_preferred_width (priv->separator, &sep_width, NULL);
6362 gtk_widget_get_preferred_width (priv->arrow, &arrow_width, NULL);
6364 xpad = 2*(border_width + xthickness + focus_width + focus_pad);
6366 minimum_width = child_min + sep_width + arrow_width + xpad;
6367 natural_width = child_nat + sep_width + arrow_width + xpad;
6371 gint but_width, but_nat_width;
6373 gtk_widget_get_preferred_width (priv->button,
6374 &but_width, &but_nat_width);
6376 minimum_width = child_min + but_width;
6377 natural_width = child_nat + but_nat_width;
6383 gint button_width, button_nat_width;
6385 /* sample + frame */
6386 minimum_width = child_min;
6387 natural_width = child_nat;
6389 minimum_width += 2 * focus_width;
6390 natural_width += 2 * focus_width;
6392 if (priv->cell_view_frame)
6394 if (priv->has_frame)
6396 gint border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->cell_view_frame));
6397 gint xpad = 2 * (border_width + gtk_widget_get_style (GTK_WIDGET (priv->cell_view_frame))->xthickness);
6399 minimum_width += xpad;
6400 natural_width += xpad;
6405 gtk_widget_get_preferred_width (priv->button,
6406 &button_width, &button_nat_width);
6408 minimum_width += button_width;
6409 natural_width += button_nat_width;
6412 if (GTK_SHADOW_NONE != priv->shadow_type)
6414 style = gtk_widget_get_style (GTK_WIDGET (widget));
6416 minimum_width += 2 * style->xthickness;
6417 natural_width += 2 * style->xthickness;
6421 *minimum_size = minimum_width;
6424 *natural_size = natural_width;
6428 gtk_combo_box_get_preferred_height (GtkWidget *widget,
6434 /* Combo box is height-for-width only
6435 * (so we always just reserve enough height for the minimum width) */
6436 GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, &min_width, NULL);
6437 GTK_WIDGET_GET_CLASS (widget)->get_preferred_height_for_width (widget, min_width, minimum_size, natural_size);
6441 gtk_combo_box_get_preferred_width_for_height (GtkWidget *widget,
6446 /* Combo box is height-for-width only
6447 * (so we assume we always reserved enough height for the minimum width) */
6448 GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, minimum_size, natural_size);
6453 gtk_combo_box_get_preferred_height_for_width (GtkWidget *widget,
6458 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
6459 GtkComboBoxPrivate *priv = combo_box->priv;
6461 gint focus_width, focus_pad;
6462 gint min_height, nat_height;
6465 gtk_widget_style_get (GTK_WIDGET (widget),
6466 "focus-line-width", &focus_width,
6467 "focus-padding", &focus_pad,
6472 if (GTK_SHADOW_NONE != priv->shadow_type)
6473 size -= gtk_widget_get_style (GTK_WIDGET (widget))->xthickness;
6475 if (!priv->tree_view)
6478 if (priv->cell_view)
6480 GtkStyle *button_style;
6481 /* calculate x/y padding and separator/arrow size */
6482 gint sep_width, arrow_width, sep_height, arrow_height;
6483 gint border_width, xthickness, ythickness, xpad, ypad;
6485 border_width = gtk_container_get_border_width (GTK_CONTAINER (combo_box));
6486 button_style = gtk_widget_get_style (priv->button);
6488 xthickness = button_style->xthickness;
6489 ythickness = button_style->ythickness;
6491 gtk_widget_get_preferred_width (priv->separator, &sep_width, NULL);
6492 gtk_widget_get_preferred_width (priv->arrow, &arrow_width, NULL);
6493 gtk_widget_get_preferred_height_for_width (priv->separator,
6494 sep_width, &sep_height, NULL);
6495 gtk_widget_get_preferred_height_for_width (priv->arrow,
6496 arrow_width, &arrow_height, NULL);
6498 xpad = 2*(border_width + xthickness + focus_width + focus_pad);
6499 ypad = 2*(border_width + ythickness + focus_width + focus_pad);
6501 size -= sep_width + arrow_width + xpad;
6503 gtk_combo_box_measure_height_for_width (combo_box, size, &min_height, &nat_height);
6505 arrow_height = MAX (arrow_height, sep_height);
6506 min_height = MAX (min_height, arrow_height);
6507 nat_height = MAX (nat_height, arrow_height);
6514 /* there is a custom child widget inside (no priv->cell_view) */
6515 gint but_width, but_height;
6517 gtk_widget_get_preferred_width (priv->button, &but_width, NULL);
6518 gtk_widget_get_preferred_height_for_width (priv->button,
6519 but_width, &but_height, NULL);
6523 gtk_combo_box_measure_height_for_width (combo_box, size, &min_height, &nat_height);
6525 min_height = MAX (min_height, but_height);
6526 nat_height = MAX (nat_height, but_height);
6532 gint but_width, but_height;
6533 gint xpad = 0, ypad = 0;
6535 gtk_widget_get_preferred_width (priv->button, &but_width, NULL);
6536 gtk_widget_get_preferred_height_for_width (priv->button,
6537 but_width, &but_height, NULL);
6539 if (priv->cell_view_frame && priv->has_frame)
6541 GtkStyle *cell_style;
6544 border_width = gtk_container_get_border_width (GTK_CONTAINER (priv->cell_view_frame));
6545 cell_style = gtk_widget_get_style (GTK_WIDGET (priv->cell_view_frame));
6547 xpad = 2 * (border_width + cell_style->xthickness);
6548 ypad = 2 * (border_width + cell_style->ythickness);
6552 size -= 2 * focus_width;
6555 gtk_combo_box_measure_height_for_width (combo_box, size, &min_height, &nat_height);
6557 min_height = MAX (min_height, but_height);
6558 nat_height = MAX (nat_height, but_height);
6564 if (GTK_SHADOW_NONE != priv->shadow_type)
6566 style = gtk_widget_get_style (GTK_WIDGET (widget));
6568 min_height += 2 * style->ythickness;
6569 nat_height += 2 * style->ythickness;
6573 *minimum_size = min_height;
6576 *natural_size = nat_height;