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"
56 /* WELCOME, to THE house of evil code */
58 typedef struct _ComboCellInfo ComboCellInfo;
61 GtkCellRenderer *cell;
64 GtkCellLayoutDataFunc func;
66 GDestroyNotify destroy;
72 #define GTK_COMBO_BOX_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_COMBO_BOX, GtkComboBoxPrivate))
74 struct _GtkComboBoxPrivate
82 GtkShadowType shadow_type;
84 GtkTreeRowReference *active_row;
87 GtkTreeViewColumn *column;
90 GtkWidget *cell_view_frame;
97 GtkWidget *popup_widget;
98 GtkWidget *popup_window;
99 GtkWidget *scrolled_window;
106 guint activate_button;
107 guint32 activate_time;
109 guint resize_idle_id;
115 guint popup_in_progress : 1;
116 guint popup_shown : 1;
117 guint add_tearoffs : 1;
119 guint is_cell_renderer : 1;
120 guint editing_canceled : 1;
121 guint auto_scroll : 1;
122 guint focus_on_click : 1;
124 GtkTreeViewRowSeparatorFunc row_separator_func;
125 gpointer row_separator_data;
126 GDestroyNotify row_separator_destroy;
128 gchar *tearoff_title;
131 /* While debugging this evil code, I have learned that
132 * there are actually 4 modes to this widget, which can
133 * be characterized as follows
135 * 1) menu mode, no child added
138 * cell_view -> GtkCellView, regular child
139 * cell_view_frame -> NULL
140 * button -> GtkToggleButton set_parent to combo
141 * arrow -> GtkArrow set_parent to button
142 * separator -> GtkVSepator set_parent to button
143 * popup_widget -> GtkMenu
144 * popup_window -> NULL
145 * scrolled_window -> NULL
147 * 2) menu mode, child added
151 * cell_view_frame -> NULL
152 * button -> GtkToggleButton set_parent to combo
153 * arrow -> GtkArrow, child of button
155 * popup_widget -> GtkMenu
156 * popup_window -> NULL
157 * scrolled_window -> NULL
159 * 3) list mode, no child added
161 * tree_view -> GtkTreeView, child of scrolled_window
162 * cell_view -> GtkCellView, regular child
163 * cell_view_frame -> GtkFrame, set parent to combo
164 * button -> GtkToggleButton, set_parent to combo
165 * arrow -> GtkArrow, child of button
167 * popup_widget -> tree_view
168 * popup_window -> GtkWindow
169 * scrolled_window -> GtkScrolledWindow, child of popup_window
171 * 4) list mode, child added
173 * tree_view -> GtkTreeView, child of scrolled_window
175 * cell_view_frame -> NULL
176 * button -> GtkToggleButton, set_parent to combo
177 * arrow -> GtkArrow, child of button
179 * popup_widget -> tree_view
180 * popup_window -> GtkWindow
181 * scrolled_window -> GtkScrolledWindow, child of popup_window
197 PROP_ROW_SPAN_COLUMN,
198 PROP_COLUMN_SPAN_COLUMN,
207 static guint combo_box_signals[LAST_SIGNAL] = {0,};
209 #define BONUS_PADDING 4
210 #define SCROLL_TIME 100
214 static void gtk_combo_box_cell_layout_init (GtkCellLayoutIface *iface);
215 static void gtk_combo_box_cell_editable_init (GtkCellEditableIface *iface);
216 static void gtk_combo_box_dispose (GObject *object);
217 static void gtk_combo_box_finalize (GObject *object);
218 static void gtk_combo_box_destroy (GtkObject *object);
220 static void gtk_combo_box_set_property (GObject *object,
224 static void gtk_combo_box_get_property (GObject *object,
229 static void gtk_combo_box_state_changed (GtkWidget *widget,
230 GtkStateType previous);
231 static void gtk_combo_box_grab_focus (GtkWidget *widget);
232 static void gtk_combo_box_style_set (GtkWidget *widget,
234 static void gtk_combo_box_button_toggled (GtkWidget *widget,
236 static void gtk_combo_box_button_state_changed (GtkWidget *widget,
237 GtkStateType previous,
239 static void gtk_combo_box_add (GtkContainer *container,
241 static void gtk_combo_box_remove (GtkContainer *container,
244 static ComboCellInfo *gtk_combo_box_get_cell_info (GtkComboBox *combo_box,
245 GtkCellRenderer *cell);
247 static void gtk_combo_box_menu_show (GtkWidget *menu,
249 static void gtk_combo_box_menu_hide (GtkWidget *menu,
252 static void gtk_combo_box_set_popup_widget (GtkComboBox *combo_box,
254 static void gtk_combo_box_menu_position_below (GtkMenu *menu,
259 static void gtk_combo_box_menu_position_over (GtkMenu *menu,
264 static void gtk_combo_box_menu_position (GtkMenu *menu,
270 static gint gtk_combo_box_calc_requested_width (GtkComboBox *combo_box,
272 static void gtk_combo_box_remeasure (GtkComboBox *combo_box);
274 static void gtk_combo_box_unset_model (GtkComboBox *combo_box);
276 static void gtk_combo_box_size_request (GtkWidget *widget,
277 GtkRequisition *requisition);
278 static void gtk_combo_box_size_allocate (GtkWidget *widget,
279 GtkAllocation *allocation);
280 static void gtk_combo_box_forall (GtkContainer *container,
281 gboolean include_internals,
282 GtkCallback callback,
283 gpointer callback_data);
284 static gboolean gtk_combo_box_expose_event (GtkWidget *widget,
285 GdkEventExpose *event);
286 static gboolean gtk_combo_box_scroll_event (GtkWidget *widget,
287 GdkEventScroll *event);
288 static void gtk_combo_box_set_active_internal (GtkComboBox *combo_box,
291 static void gtk_combo_box_check_appearance (GtkComboBox *combo_box);
292 static gchar * gtk_combo_box_real_get_active_text (GtkComboBox *combo_box);
293 static void gtk_combo_box_real_move_active (GtkComboBox *combo_box,
294 GtkScrollType scroll);
295 static void gtk_combo_box_real_popup (GtkComboBox *combo_box);
296 static gboolean gtk_combo_box_real_popdown (GtkComboBox *combo_box);
298 /* listening to the model */
299 static void gtk_combo_box_model_row_inserted (GtkTreeModel *model,
303 static void gtk_combo_box_model_row_deleted (GtkTreeModel *model,
306 static void gtk_combo_box_model_rows_reordered (GtkTreeModel *model,
311 static void gtk_combo_box_model_row_changed (GtkTreeModel *model,
315 static void gtk_combo_box_model_row_expanded (GtkTreeModel *model,
321 static void gtk_combo_box_list_position (GtkComboBox *combo_box,
326 static void gtk_combo_box_list_setup (GtkComboBox *combo_box);
327 static void gtk_combo_box_list_destroy (GtkComboBox *combo_box);
329 static gboolean gtk_combo_box_list_button_released (GtkWidget *widget,
330 GdkEventButton *event,
332 static gboolean gtk_combo_box_list_key_press (GtkWidget *widget,
335 static gboolean gtk_combo_box_list_enter_notify (GtkWidget *widget,
336 GdkEventCrossing *event,
338 static void gtk_combo_box_list_auto_scroll (GtkComboBox *combo,
341 static gboolean gtk_combo_box_list_scroll_timeout (GtkComboBox *combo);
342 static gboolean gtk_combo_box_list_button_pressed (GtkWidget *widget,
343 GdkEventButton *event,
346 static gboolean gtk_combo_box_list_select_func (GtkTreeSelection *selection,
349 gboolean path_currently_selected,
352 static void gtk_combo_box_list_row_changed (GtkTreeModel *model,
356 static void gtk_combo_box_list_popup_resize (GtkComboBox *combo_box);
359 static void gtk_combo_box_menu_setup (GtkComboBox *combo_box,
360 gboolean add_children);
361 static void gtk_combo_box_menu_fill (GtkComboBox *combo_box);
362 static void gtk_combo_box_menu_fill_level (GtkComboBox *combo_box,
365 static void gtk_combo_box_update_title (GtkComboBox *combo_box);
366 static void gtk_combo_box_menu_destroy (GtkComboBox *combo_box);
368 static void gtk_combo_box_relayout_item (GtkComboBox *combo_box,
372 static void gtk_combo_box_relayout (GtkComboBox *combo_box);
374 static gboolean gtk_combo_box_menu_button_press (GtkWidget *widget,
375 GdkEventButton *event,
377 static void gtk_combo_box_menu_item_activate (GtkWidget *item,
379 static void gtk_combo_box_menu_row_inserted (GtkTreeModel *model,
383 static void gtk_combo_box_menu_row_deleted (GtkTreeModel *model,
386 static void gtk_combo_box_menu_rows_reordered (GtkTreeModel *model,
391 static void gtk_combo_box_menu_row_changed (GtkTreeModel *model,
395 static gboolean gtk_combo_box_menu_key_press (GtkWidget *widget,
398 static void gtk_combo_box_menu_popup (GtkComboBox *combo_box,
400 guint32 activate_time);
401 static GtkWidget *gtk_cell_view_menu_item_new (GtkComboBox *combo_box,
406 static void gtk_combo_box_cell_layout_pack_start (GtkCellLayout *layout,
407 GtkCellRenderer *cell,
409 static void gtk_combo_box_cell_layout_pack_end (GtkCellLayout *layout,
410 GtkCellRenderer *cell,
412 static GList *gtk_combo_box_cell_layout_get_cells (GtkCellLayout *layout);
413 static void gtk_combo_box_cell_layout_clear (GtkCellLayout *layout);
414 static void gtk_combo_box_cell_layout_add_attribute (GtkCellLayout *layout,
415 GtkCellRenderer *cell,
416 const gchar *attribute,
418 static void gtk_combo_box_cell_layout_set_cell_data_func (GtkCellLayout *layout,
419 GtkCellRenderer *cell,
420 GtkCellLayoutDataFunc func,
422 GDestroyNotify destroy);
423 static void gtk_combo_box_cell_layout_clear_attributes (GtkCellLayout *layout,
424 GtkCellRenderer *cell);
425 static void gtk_combo_box_cell_layout_reorder (GtkCellLayout *layout,
426 GtkCellRenderer *cell,
428 static gboolean gtk_combo_box_mnemonic_activate (GtkWidget *widget,
429 gboolean group_cycling);
431 static void gtk_combo_box_sync_cells (GtkComboBox *combo_box,
432 GtkCellLayout *cell_layout);
433 static void combo_cell_data_func (GtkCellLayout *cell_layout,
434 GtkCellRenderer *cell,
435 GtkTreeModel *tree_model,
438 static void gtk_combo_box_child_show (GtkWidget *widget,
439 GtkComboBox *combo_box);
440 static void gtk_combo_box_child_hide (GtkWidget *widget,
441 GtkComboBox *combo_box);
443 /* GtkBuildable method implementation */
444 static GtkBuildableIface *parent_buildable_iface;
446 static void gtk_combo_box_buildable_init (GtkBuildableIface *iface);
447 static gboolean gtk_combo_box_buildable_custom_tag_start (GtkBuildable *buildable,
450 const gchar *tagname,
451 GMarkupParser *parser,
453 static void gtk_combo_box_buildable_custom_tag_end (GtkBuildable *buildable,
456 const gchar *tagname,
459 /* GtkCellEditable method implementations */
460 static void gtk_combo_box_start_editing (GtkCellEditable *cell_editable,
464 G_DEFINE_TYPE_WITH_CODE (GtkComboBox, gtk_combo_box, GTK_TYPE_BIN,
465 G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
466 gtk_combo_box_cell_layout_init)
467 G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_EDITABLE,
468 gtk_combo_box_cell_editable_init)
469 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
470 gtk_combo_box_buildable_init))
475 gtk_combo_box_class_init (GtkComboBoxClass *klass)
477 GObjectClass *object_class;
478 GtkObjectClass *gtk_object_class;
479 GtkContainerClass *container_class;
480 GtkWidgetClass *widget_class;
481 GtkBindingSet *binding_set;
483 klass->get_active_text = gtk_combo_box_real_get_active_text;
485 container_class = (GtkContainerClass *)klass;
486 container_class->forall = gtk_combo_box_forall;
487 container_class->add = gtk_combo_box_add;
488 container_class->remove = gtk_combo_box_remove;
490 widget_class = (GtkWidgetClass *)klass;
491 widget_class->size_allocate = gtk_combo_box_size_allocate;
492 widget_class->size_request = gtk_combo_box_size_request;
493 widget_class->expose_event = gtk_combo_box_expose_event;
494 widget_class->scroll_event = gtk_combo_box_scroll_event;
495 widget_class->mnemonic_activate = gtk_combo_box_mnemonic_activate;
496 widget_class->grab_focus = gtk_combo_box_grab_focus;
497 widget_class->style_set = gtk_combo_box_style_set;
498 widget_class->state_changed = gtk_combo_box_state_changed;
500 gtk_object_class = (GtkObjectClass *)klass;
501 gtk_object_class->destroy = gtk_combo_box_destroy;
503 object_class = (GObjectClass *)klass;
504 object_class->dispose = gtk_combo_box_dispose;
505 object_class->finalize = gtk_combo_box_finalize;
506 object_class->set_property = gtk_combo_box_set_property;
507 object_class->get_property = gtk_combo_box_get_property;
511 * GtkComboBox::changed:
512 * @widget: the object which received the signal
514 * The changed signal is emitted when the active
515 * item is changed. The can be due to the user selecting
516 * a different item from the list, or due to a
517 * call to gtk_combo_box_set_active_iter().
518 * It will also be emitted while typing into a GtkComboBoxEntry,
519 * as well as when selecting an item from the GtkComboBoxEntry's list.
523 combo_box_signals[CHANGED] =
524 g_signal_new (I_("changed"),
525 G_OBJECT_CLASS_TYPE (klass),
527 G_STRUCT_OFFSET (GtkComboBoxClass, changed),
529 g_cclosure_marshal_VOID__VOID,
532 * GtkComboBox::move-active:
533 * @widget: the object that received the signal
534 * @scroll_type: a #GtkScrollType
536 * The ::move-active signal is a
537 * <link linkend="keybinding-signals">keybinding signal</link>
538 * which gets emitted to move the active selection.
542 combo_box_signals[MOVE_ACTIVE] =
543 _gtk_binding_signal_new (I_("move-active"),
544 G_OBJECT_CLASS_TYPE (klass),
545 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
546 G_CALLBACK (gtk_combo_box_real_move_active),
548 g_cclosure_marshal_VOID__ENUM,
550 GTK_TYPE_SCROLL_TYPE);
553 * GtkComboBox::popup:
554 * @widget: the object that received the signal
556 * The ::popup signal is a
557 * <link linkend="keybinding-signals">keybinding signal</link>
558 * which gets emitted to popup the combo box list.
560 * The default binding for this signal is Alt+Down.
564 combo_box_signals[POPUP] =
565 _gtk_binding_signal_new (I_("popup"),
566 G_OBJECT_CLASS_TYPE (klass),
567 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
568 G_CALLBACK (gtk_combo_box_real_popup),
570 g_cclosure_marshal_VOID__VOID,
573 * GtkComboBox::popdown:
574 * @button: the object which received the signal
576 * The ::popdown signal is a
577 * <link linkend="keybinding-signals">keybinding signal</link>
578 * which gets emitted to popdown the combo box list.
580 * The default bindings for this signal are Alt+Up and Escape.
584 combo_box_signals[POPDOWN] =
585 _gtk_binding_signal_new (I_("popdown"),
586 G_OBJECT_CLASS_TYPE (klass),
587 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
588 G_CALLBACK (gtk_combo_box_real_popdown),
590 _gtk_marshal_BOOLEAN__VOID,
594 binding_set = gtk_binding_set_by_class (widget_class);
596 gtk_binding_entry_add_signal (binding_set, GDK_Down, GDK_MOD1_MASK,
598 gtk_binding_entry_add_signal (binding_set, GDK_KP_Down, GDK_MOD1_MASK,
601 gtk_binding_entry_add_signal (binding_set, GDK_Up, GDK_MOD1_MASK,
603 gtk_binding_entry_add_signal (binding_set, GDK_KP_Up, GDK_MOD1_MASK,
605 gtk_binding_entry_add_signal (binding_set, GDK_Escape, 0,
608 gtk_binding_entry_add_signal (binding_set, GDK_Up, 0,
610 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_UP);
611 gtk_binding_entry_add_signal (binding_set, GDK_KP_Up, 0,
613 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_UP);
614 gtk_binding_entry_add_signal (binding_set, GDK_Page_Up, 0,
616 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_UP);
617 gtk_binding_entry_add_signal (binding_set, GDK_KP_Page_Up, 0,
619 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_UP);
620 gtk_binding_entry_add_signal (binding_set, GDK_Home, 0,
622 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_START);
623 gtk_binding_entry_add_signal (binding_set, GDK_KP_Home, 0,
625 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_START);
627 gtk_binding_entry_add_signal (binding_set, GDK_Down, 0,
629 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_DOWN);
630 gtk_binding_entry_add_signal (binding_set, GDK_KP_Down, 0,
632 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_DOWN);
633 gtk_binding_entry_add_signal (binding_set, GDK_Page_Down, 0,
635 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_DOWN);
636 gtk_binding_entry_add_signal (binding_set, GDK_KP_Page_Down, 0,
638 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_DOWN);
639 gtk_binding_entry_add_signal (binding_set, GDK_End, 0,
641 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_END);
642 gtk_binding_entry_add_signal (binding_set, GDK_KP_End, 0,
644 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_END);
650 * The model from which the combo box takes the values shown
655 g_object_class_install_property (object_class,
657 g_param_spec_object ("model",
658 P_("ComboBox model"),
659 P_("The model for the combo box"),
661 GTK_PARAM_READWRITE));
664 * GtkComboBox:wrap-width:
666 * If wrap-width is set to a positive value, the list will be
667 * displayed in multiple columns, the number of columns is
668 * determined by wrap-width.
672 g_object_class_install_property (object_class,
674 g_param_spec_int ("wrap-width",
676 P_("Wrap width for laying out the items in a grid"),
680 GTK_PARAM_READWRITE));
684 * GtkComboBox:row-span-column:
686 * If this is set to a non-negative value, it must be the index of a column
687 * of type %G_TYPE_INT in the model.
689 * The values of that column are used to determine how many rows a value in
690 * the list will span. Therefore, the values in the model column pointed to
691 * by this property must be greater than zero and not larger than wrap-width.
695 g_object_class_install_property (object_class,
696 PROP_ROW_SPAN_COLUMN,
697 g_param_spec_int ("row-span-column",
698 P_("Row span column"),
699 P_("TreeModel column containing the row span values"),
703 GTK_PARAM_READWRITE));
707 * GtkComboBox:column-span-column:
709 * If this is set to a non-negative value, it must be the index of a column
710 * of type %G_TYPE_INT in the model.
712 * The values of that column are used to determine how many columns a value
713 * in the list will span.
717 g_object_class_install_property (object_class,
718 PROP_COLUMN_SPAN_COLUMN,
719 g_param_spec_int ("column-span-column",
720 P_("Column span column"),
721 P_("TreeModel column containing the column span values"),
725 GTK_PARAM_READWRITE));
729 * GtkComboBox:active:
731 * The item which is currently active. If the model is a non-flat treemodel,
732 * and the active item is not an immediate child of the root of the tree,
733 * this property has the value
734 * <literal>gtk_tree_path_get_indices (path)[0]</literal>,
735 * where <literal>path</literal> is the #GtkTreePath of the active item.
739 g_object_class_install_property (object_class,
741 g_param_spec_int ("active",
743 P_("The item which is currently active"),
747 GTK_PARAM_READWRITE));
750 * GtkComboBox:add-tearoffs:
752 * The add-tearoffs property controls whether generated menus
753 * have tearoff menu items.
755 * Note that this only affects menu style combo boxes.
759 g_object_class_install_property (object_class,
761 g_param_spec_boolean ("add-tearoffs",
762 P_("Add tearoffs to menus"),
763 P_("Whether dropdowns should have a tearoff menu item"),
765 GTK_PARAM_READWRITE));
768 * GtkComboBox:has-frame:
770 * The has-frame property controls whether a frame
771 * is drawn around the entry.
775 g_object_class_install_property (object_class,
777 g_param_spec_boolean ("has-frame",
779 P_("Whether the combo box draws a frame around the child"),
781 GTK_PARAM_READWRITE));
783 g_object_class_install_property (object_class,
785 g_param_spec_boolean ("focus-on-click",
786 P_("Focus on click"),
787 P_("Whether the combo box grabs focus when it is clicked with the mouse"),
789 GTK_PARAM_READWRITE));
792 * GtkComboBox:tearoff-title:
794 * A title that may be displayed by the window manager
795 * when the popup is torn-off.
799 g_object_class_install_property (object_class,
801 g_param_spec_string ("tearoff-title",
803 P_("A title that may be displayed by the window manager when the popup is torn-off"),
805 GTK_PARAM_READWRITE));
809 * GtkComboBox:popup-shown:
811 * Whether the combo boxes dropdown is popped up.
812 * Note that this property is mainly useful, because
813 * it allows you to connect to notify::popup-shown.
817 g_object_class_install_property (object_class,
819 g_param_spec_boolean ("popup-shown",
821 P_("Whether the combo's dropdown is shown"),
823 GTK_PARAM_READABLE));
825 gtk_widget_class_install_style_property (widget_class,
826 g_param_spec_boolean ("appears-as-list",
827 P_("Appears as list"),
828 P_("Whether dropdowns should look like lists rather than menus"),
830 GTK_PARAM_READABLE));
833 * GtkComboBox:arrow-size:
835 * Sets the minimum size of the arrow in the combo box. Note
836 * that the arrow size is coupled to the font size, so in case
837 * a larger font is used, the arrow will be larger than set
842 gtk_widget_class_install_style_property (widget_class,
843 g_param_spec_int ("arrow-size",
845 P_("The minimum size of the arrow in the combo box"),
849 GTK_PARAM_READABLE));
852 * GtkComboBox:shadow-type:
854 * Which kind of shadow to draw around the combo box.
858 gtk_widget_class_install_style_property (widget_class,
859 g_param_spec_enum ("shadow-type",
861 P_("Which kind of shadow to draw around the combo box"),
862 GTK_TYPE_SHADOW_TYPE,
864 GTK_PARAM_READABLE));
866 g_type_class_add_private (object_class, sizeof (GtkComboBoxPrivate));
870 gtk_combo_box_buildable_init (GtkBuildableIface *iface)
872 parent_buildable_iface = g_type_interface_peek_parent (iface);
873 iface->add_child = _gtk_cell_layout_buildable_add_child;
874 iface->custom_tag_start = gtk_combo_box_buildable_custom_tag_start;
875 iface->custom_tag_end = gtk_combo_box_buildable_custom_tag_end;
879 gtk_combo_box_cell_layout_init (GtkCellLayoutIface *iface)
881 iface->pack_start = gtk_combo_box_cell_layout_pack_start;
882 iface->pack_end = gtk_combo_box_cell_layout_pack_end;
883 iface->get_cells = gtk_combo_box_cell_layout_get_cells;
884 iface->clear = gtk_combo_box_cell_layout_clear;
885 iface->add_attribute = gtk_combo_box_cell_layout_add_attribute;
886 iface->set_cell_data_func = gtk_combo_box_cell_layout_set_cell_data_func;
887 iface->clear_attributes = gtk_combo_box_cell_layout_clear_attributes;
888 iface->reorder = gtk_combo_box_cell_layout_reorder;
892 gtk_combo_box_cell_editable_init (GtkCellEditableIface *iface)
894 iface->start_editing = gtk_combo_box_start_editing;
898 gtk_combo_box_init (GtkComboBox *combo_box)
900 GtkComboBoxPrivate *priv = GTK_COMBO_BOX_GET_PRIVATE (combo_box);
902 priv->cell_view = gtk_cell_view_new ();
903 gtk_widget_set_parent (priv->cell_view, GTK_WIDGET (combo_box));
904 GTK_BIN (combo_box)->child = priv->cell_view;
905 gtk_widget_show (priv->cell_view);
909 priv->wrap_width = 0;
911 priv->active_row = NULL;
912 priv->col_column = -1;
913 priv->row_column = -1;
915 priv->popup_shown = FALSE;
916 priv->add_tearoffs = FALSE;
917 priv->has_frame = TRUE;
918 priv->is_cell_renderer = FALSE;
919 priv->editing_canceled = FALSE;
920 priv->auto_scroll = FALSE;
921 priv->focus_on_click = TRUE;
923 combo_box->priv = priv;
925 gtk_combo_box_check_appearance (combo_box);
929 gtk_combo_box_set_property (GObject *object,
934 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
939 gtk_combo_box_set_model (combo_box, g_value_get_object (value));
942 case PROP_WRAP_WIDTH:
943 gtk_combo_box_set_wrap_width (combo_box, g_value_get_int (value));
946 case PROP_ROW_SPAN_COLUMN:
947 gtk_combo_box_set_row_span_column (combo_box, g_value_get_int (value));
950 case PROP_COLUMN_SPAN_COLUMN:
951 gtk_combo_box_set_column_span_column (combo_box, g_value_get_int (value));
955 gtk_combo_box_set_active (combo_box, g_value_get_int (value));
958 case PROP_ADD_TEAROFFS:
959 gtk_combo_box_set_add_tearoffs (combo_box, g_value_get_boolean (value));
963 combo_box->priv->has_frame = g_value_get_boolean (value);
966 case PROP_FOCUS_ON_CLICK:
967 gtk_combo_box_set_focus_on_click (combo_box,
968 g_value_get_boolean (value));
971 case PROP_TEAROFF_TITLE:
972 gtk_combo_box_set_title (combo_box, g_value_get_string (value));
975 case PROP_POPUP_SHOWN:
976 if (g_value_get_boolean (value))
977 gtk_combo_box_popup (combo_box);
979 gtk_combo_box_popdown (combo_box);
983 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
989 gtk_combo_box_get_property (GObject *object,
994 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
999 g_value_set_object (value, combo_box->priv->model);
1002 case PROP_WRAP_WIDTH:
1003 g_value_set_int (value, combo_box->priv->wrap_width);
1006 case PROP_ROW_SPAN_COLUMN:
1007 g_value_set_int (value, combo_box->priv->row_column);
1010 case PROP_COLUMN_SPAN_COLUMN:
1011 g_value_set_int (value, combo_box->priv->col_column);
1015 g_value_set_int (value, gtk_combo_box_get_active (combo_box));
1018 case PROP_ADD_TEAROFFS:
1019 g_value_set_boolean (value, gtk_combo_box_get_add_tearoffs (combo_box));
1022 case PROP_HAS_FRAME:
1023 g_value_set_boolean (value, combo_box->priv->has_frame);
1026 case PROP_FOCUS_ON_CLICK:
1027 g_value_set_boolean (value, combo_box->priv->focus_on_click);
1030 case PROP_TEAROFF_TITLE:
1031 g_value_set_string (value, gtk_combo_box_get_title (combo_box));
1034 case PROP_POPUP_SHOWN:
1035 g_value_set_boolean (value, combo_box->priv->popup_shown);
1039 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1045 gtk_combo_box_state_changed (GtkWidget *widget,
1046 GtkStateType previous)
1048 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1049 GtkComboBoxPrivate *priv = combo_box->priv;
1051 if (GTK_WIDGET_REALIZED (widget))
1053 if (priv->tree_view && priv->cell_view)
1054 gtk_cell_view_set_background_color (GTK_CELL_VIEW (priv->cell_view),
1055 &widget->style->base[GTK_WIDGET_STATE (widget)]);
1058 gtk_widget_queue_draw (widget);
1062 gtk_combo_box_button_state_changed (GtkWidget *widget,
1063 GtkStateType previous,
1066 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
1067 GtkComboBoxPrivate *priv = combo_box->priv;
1069 if (GTK_WIDGET_REALIZED (widget))
1071 if (!priv->tree_view && priv->cell_view)
1073 if ((GTK_WIDGET_STATE (widget) == GTK_STATE_INSENSITIVE) !=
1074 (GTK_WIDGET_STATE (priv->cell_view) == GTK_STATE_INSENSITIVE))
1075 gtk_widget_set_sensitive (priv->cell_view, GTK_WIDGET_SENSITIVE (widget));
1077 gtk_widget_set_state (priv->cell_view,
1078 GTK_WIDGET_STATE (widget));
1082 gtk_widget_queue_draw (widget);
1086 gtk_combo_box_check_appearance (GtkComboBox *combo_box)
1088 GtkComboBoxPrivate *priv = combo_box->priv;
1089 gboolean appears_as_list;
1091 /* if wrap_width > 0, then we are in grid-mode and forced to use
1094 if (priv->wrap_width)
1095 appears_as_list = FALSE;
1097 gtk_widget_style_get (GTK_WIDGET (combo_box),
1098 "appears-as-list", &appears_as_list,
1101 if (appears_as_list)
1103 /* Destroy all the menu mode widgets, if they exist. */
1104 if (GTK_IS_MENU (priv->popup_widget))
1105 gtk_combo_box_menu_destroy (combo_box);
1107 /* Create the list mode widgets, if they don't already exist. */
1108 if (!GTK_IS_TREE_VIEW (priv->tree_view))
1109 gtk_combo_box_list_setup (combo_box);
1113 /* Destroy all the list mode widgets, if they exist. */
1114 if (GTK_IS_TREE_VIEW (priv->tree_view))
1115 gtk_combo_box_list_destroy (combo_box);
1117 /* Create the menu mode widgets, if they don't already exist. */
1118 if (!GTK_IS_MENU (priv->popup_widget))
1119 gtk_combo_box_menu_setup (combo_box, TRUE);
1122 gtk_widget_style_get (GTK_WIDGET (combo_box),
1123 "shadow-type", &priv->shadow_type,
1128 gtk_combo_box_style_set (GtkWidget *widget,
1131 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1132 GtkComboBoxPrivate *priv = combo_box->priv;
1134 gtk_combo_box_check_appearance (combo_box);
1136 if (priv->tree_view && priv->cell_view)
1137 gtk_cell_view_set_background_color (GTK_CELL_VIEW (priv->cell_view),
1138 &widget->style->base[GTK_WIDGET_STATE (widget)]);
1140 if (GTK_IS_ENTRY (GTK_BIN (combo_box)->child))
1141 g_object_set (GTK_BIN (combo_box)->child, "shadow-type",
1142 GTK_SHADOW_NONE == priv->shadow_type ?
1143 GTK_SHADOW_IN : GTK_SHADOW_NONE, NULL);
1147 gtk_combo_box_button_toggled (GtkWidget *widget,
1150 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
1152 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
1154 if (!combo_box->priv->popup_in_progress)
1155 gtk_combo_box_popup (combo_box);
1158 gtk_combo_box_popdown (combo_box);
1162 gtk_combo_box_add (GtkContainer *container,
1165 GtkComboBox *combo_box = GTK_COMBO_BOX (container);
1166 GtkComboBoxPrivate *priv = combo_box->priv;
1168 if (priv->cell_view && priv->cell_view->parent)
1170 gtk_widget_unparent (priv->cell_view);
1171 GTK_BIN (container)->child = NULL;
1172 gtk_widget_queue_resize (GTK_WIDGET (container));
1175 gtk_widget_set_parent (widget, GTK_WIDGET (container));
1176 GTK_BIN (container)->child = widget;
1178 if (priv->cell_view &&
1179 widget != priv->cell_view)
1181 /* since the cell_view was unparented, it's gone now */
1182 priv->cell_view = NULL;
1184 if (!priv->tree_view && priv->separator)
1186 gtk_container_remove (GTK_CONTAINER (priv->separator->parent),
1188 priv->separator = NULL;
1190 gtk_widget_queue_resize (GTK_WIDGET (container));
1192 else if (priv->cell_view_frame)
1194 gtk_widget_unparent (priv->cell_view_frame);
1195 priv->cell_view_frame = NULL;
1202 gtk_combo_box_remove (GtkContainer *container,
1205 GtkComboBox *combo_box = GTK_COMBO_BOX (container);
1206 GtkComboBoxPrivate *priv = combo_box->priv;
1208 gboolean appears_as_list;
1210 if (widget == priv->cell_view)
1211 priv->cell_view = NULL;
1213 gtk_widget_unparent (widget);
1214 GTK_BIN (container)->child = NULL;
1216 if (GTK_OBJECT_FLAGS (combo_box) & GTK_IN_DESTRUCTION)
1219 gtk_widget_queue_resize (GTK_WIDGET (container));
1221 if (!priv->tree_view)
1222 appears_as_list = FALSE;
1224 appears_as_list = TRUE;
1226 if (appears_as_list)
1227 gtk_combo_box_list_destroy (combo_box);
1228 else if (GTK_IS_MENU (priv->popup_widget))
1230 gtk_combo_box_menu_destroy (combo_box);
1231 gtk_menu_detach (GTK_MENU (priv->popup_widget));
1232 priv->popup_widget = NULL;
1235 if (!priv->cell_view)
1237 priv->cell_view = gtk_cell_view_new ();
1238 gtk_widget_set_parent (priv->cell_view, GTK_WIDGET (container));
1239 GTK_BIN (container)->child = priv->cell_view;
1241 gtk_widget_show (priv->cell_view);
1242 gtk_cell_view_set_model (GTK_CELL_VIEW (priv->cell_view),
1244 gtk_combo_box_sync_cells (combo_box, GTK_CELL_LAYOUT (priv->cell_view));
1248 if (appears_as_list)
1249 gtk_combo_box_list_setup (combo_box);
1251 gtk_combo_box_menu_setup (combo_box, TRUE);
1253 if (gtk_tree_row_reference_valid (priv->active_row))
1255 path = gtk_tree_row_reference_get_path (priv->active_row);
1256 gtk_combo_box_set_active_internal (combo_box, path);
1257 gtk_tree_path_free (path);
1260 gtk_combo_box_set_active_internal (combo_box, NULL);
1263 static ComboCellInfo *
1264 gtk_combo_box_get_cell_info (GtkComboBox *combo_box,
1265 GtkCellRenderer *cell)
1269 for (i = combo_box->priv->cells; i; i = i->next)
1271 ComboCellInfo *info = (ComboCellInfo *)i->data;
1273 if (info && info->cell == cell)
1281 gtk_combo_box_menu_show (GtkWidget *menu,
1284 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1285 GtkComboBoxPrivate *priv = combo_box->priv;
1287 gtk_combo_box_child_show (menu, user_data);
1289 priv->popup_in_progress = TRUE;
1290 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button),
1292 priv->popup_in_progress = FALSE;
1296 gtk_combo_box_menu_hide (GtkWidget *menu,
1299 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1301 gtk_combo_box_child_hide (menu,user_data);
1303 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
1308 gtk_combo_box_detacher (GtkWidget *widget,
1311 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1312 GtkComboBoxPrivate *priv = combo_box->priv;
1314 g_return_if_fail (priv->popup_widget == (GtkWidget *) menu);
1316 g_signal_handlers_disconnect_by_func (menu->toplevel,
1317 gtk_combo_box_menu_show,
1319 g_signal_handlers_disconnect_by_func (menu->toplevel,
1320 gtk_combo_box_menu_hide,
1323 priv->popup_widget = NULL;
1327 gtk_combo_box_set_popup_widget (GtkComboBox *combo_box,
1330 GtkComboBoxPrivate *priv = combo_box->priv;
1332 if (GTK_IS_MENU (priv->popup_widget))
1334 gtk_menu_detach (GTK_MENU (priv->popup_widget));
1335 priv->popup_widget = NULL;
1337 else if (priv->popup_widget)
1339 gtk_container_remove (GTK_CONTAINER (priv->scrolled_window),
1340 priv->popup_widget);
1341 g_object_unref (priv->popup_widget);
1342 priv->popup_widget = NULL;
1345 if (GTK_IS_MENU (popup))
1347 if (priv->popup_window)
1349 gtk_widget_destroy (priv->popup_window);
1350 priv->popup_window = NULL;
1353 priv->popup_widget = popup;
1356 * Note that we connect to show/hide on the toplevel, not the
1357 * menu itself, since the menu is not shown/hidden when it is
1358 * popped up while torn-off.
1360 g_signal_connect (GTK_MENU (popup)->toplevel, "show",
1361 G_CALLBACK (gtk_combo_box_menu_show), combo_box);
1362 g_signal_connect (GTK_MENU (popup)->toplevel, "hide",
1363 G_CALLBACK (gtk_combo_box_menu_hide), combo_box);
1365 gtk_menu_attach_to_widget (GTK_MENU (popup),
1366 GTK_WIDGET (combo_box),
1367 gtk_combo_box_detacher);
1371 if (!priv->popup_window)
1373 GtkWidget *toplevel;
1375 priv->popup_window = gtk_window_new (GTK_WINDOW_POPUP);
1376 gtk_widget_set_name (priv->popup_window, "gtk-combobox-popup-window");
1378 gtk_window_set_type_hint (GTK_WINDOW (priv->popup_window),
1379 GDK_WINDOW_TYPE_HINT_COMBO);
1381 g_signal_connect (GTK_WINDOW (priv->popup_window),"show",
1382 G_CALLBACK (gtk_combo_box_child_show),
1384 g_signal_connect (GTK_WINDOW (priv->popup_window),"hide",
1385 G_CALLBACK (gtk_combo_box_child_hide),
1388 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (combo_box));
1389 if (GTK_IS_WINDOW (toplevel))
1391 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
1392 GTK_WINDOW (priv->popup_window));
1393 gtk_window_set_transient_for (GTK_WINDOW (priv->popup_window),
1394 GTK_WINDOW (toplevel));
1397 gtk_window_set_resizable (GTK_WINDOW (priv->popup_window), FALSE);
1398 gtk_window_set_screen (GTK_WINDOW (priv->popup_window),
1399 gtk_widget_get_screen (GTK_WIDGET (combo_box)));
1401 priv->scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1403 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
1406 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (priv->scrolled_window),
1409 gtk_widget_show (priv->scrolled_window);
1411 gtk_container_add (GTK_CONTAINER (priv->popup_window),
1412 priv->scrolled_window);
1415 gtk_container_add (GTK_CONTAINER (priv->scrolled_window),
1418 gtk_widget_show (popup);
1419 g_object_ref (popup);
1420 priv->popup_widget = popup;
1425 gtk_combo_box_menu_position_below (GtkMenu *menu,
1431 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1437 GdkRectangle monitor;
1439 /* FIXME: is using the size request here broken? */
1440 child = GTK_BIN (combo_box)->child;
1442 gdk_window_get_origin (child->window, &sx, &sy);
1444 if (GTK_WIDGET_NO_WINDOW (child))
1446 sx += child->allocation.x;
1447 sy += child->allocation.y;
1450 if (GTK_SHADOW_NONE != combo_box->priv->shadow_type)
1451 sx -= GTK_WIDGET (combo_box)->style->xthickness;
1453 gtk_widget_size_request (GTK_WIDGET (menu), &req);
1455 if (gtk_widget_get_direction (GTK_WIDGET (combo_box)) == GTK_TEXT_DIR_LTR)
1458 *x = sx + child->allocation.width - req.width;
1461 screen = gtk_widget_get_screen (GTK_WIDGET (combo_box));
1462 monitor_num = gdk_screen_get_monitor_at_window (screen,
1463 GTK_WIDGET (combo_box)->window);
1464 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1468 else if (*x + req.width > monitor.x + monitor.width)
1469 *x = monitor.x + monitor.width - req.width;
1471 if (monitor.y + monitor.height - *y - child->allocation.height >= req.height)
1472 *y += child->allocation.height;
1473 else if (*y - monitor.y >= req.height)
1475 else if (monitor.y + monitor.height - *y - child->allocation.height > *y - monitor.y)
1476 *y += child->allocation.height;
1484 gtk_combo_box_menu_position_over (GtkMenu *menu,
1490 GtkComboBox *combo_box;
1494 GtkRequisition requisition;
1501 combo_box = GTK_COMBO_BOX (user_data);
1502 widget = GTK_WIDGET (combo_box);
1504 gtk_widget_get_child_requisition (GTK_WIDGET (menu), &requisition);
1505 menu_width = requisition.width;
1507 active = gtk_menu_get_active (GTK_MENU (combo_box->priv->popup_widget));
1508 gdk_window_get_origin (widget->window, &menu_xpos, &menu_ypos);
1510 menu_xpos += widget->allocation.x;
1511 menu_ypos += widget->allocation.y + widget->allocation.height / 2 - 2;
1515 gtk_widget_get_child_requisition (active, &requisition);
1516 menu_ypos -= requisition.height / 2;
1519 children = GTK_MENU_SHELL (combo_box->priv->popup_widget)->children;
1522 child = children->data;
1524 if (active == child)
1527 if (GTK_WIDGET_VISIBLE (child))
1529 gtk_widget_get_child_requisition (child, &requisition);
1530 menu_ypos -= requisition.height;
1533 children = children->next;
1536 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
1537 menu_xpos = menu_xpos + widget->allocation.width - menu_width;
1539 /* Clamp the position on screen */
1540 screen_width = gdk_screen_get_width (gtk_widget_get_screen (widget));
1544 else if ((menu_xpos + menu_width) > screen_width)
1545 menu_xpos -= ((menu_xpos + menu_width) - screen_width);
1554 gtk_combo_box_menu_position (GtkMenu *menu,
1560 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1561 GtkComboBoxPrivate *priv = combo_box->priv;
1562 GtkWidget *menu_item;
1565 if (priv->wrap_width > 0 || priv->cell_view == NULL)
1566 gtk_combo_box_menu_position_below (menu, x, y, push_in, user_data);
1569 /* FIXME handle nested menus better */
1570 menu_item = gtk_menu_get_active (GTK_MENU (priv->popup_widget));
1572 gtk_menu_shell_select_item (GTK_MENU_SHELL (priv->popup_widget),
1575 gtk_combo_box_menu_position_over (menu, x, y, push_in, user_data);
1578 gtk_window_set_type_hint (GTK_WINDOW (GTK_MENU (priv->popup_widget)->toplevel),
1579 GDK_WINDOW_TYPE_HINT_COMBO);
1583 gtk_combo_box_list_position (GtkComboBox *combo_box,
1589 GtkComboBoxPrivate *priv = combo_box->priv;
1592 GdkRectangle monitor;
1593 GtkRequisition popup_req;
1594 GtkPolicyType hpolicy, vpolicy;
1596 /* under windows, the drop down list is as wide as the combo box itself.
1598 GtkWidget *sample = GTK_WIDGET (combo_box);
1600 gdk_window_get_origin (sample->window, x, y);
1602 if (GTK_WIDGET_NO_WINDOW (sample))
1604 *x += sample->allocation.x;
1605 *y += sample->allocation.y;
1608 *width = sample->allocation.width;
1610 hpolicy = vpolicy = GTK_POLICY_NEVER;
1611 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
1613 gtk_widget_size_request (priv->scrolled_window, &popup_req);
1615 if (popup_req.width > *width)
1617 hpolicy = GTK_POLICY_ALWAYS;
1618 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
1620 gtk_widget_size_request (priv->scrolled_window, &popup_req);
1623 *height = popup_req.height;
1625 screen = gtk_widget_get_screen (GTK_WIDGET (combo_box));
1626 monitor_num = gdk_screen_get_monitor_at_window (screen,
1627 GTK_WIDGET (combo_box)->window);
1628 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1632 else if (*x + *width > monitor.x + monitor.width)
1633 *x = monitor.x + monitor.width - *width;
1635 if (*y + sample->allocation.height + *height <= monitor.y + monitor.height)
1636 *y += sample->allocation.height;
1637 else if (*y - *height >= monitor.y)
1639 else if (monitor.y + monitor.height - (*y + sample->allocation.height) > *y - monitor.y)
1641 *y += sample->allocation.height;
1642 *height = monitor.y + monitor.height - *y;
1646 *height = *y - monitor.y;
1650 if (popup_req.height > *height)
1652 vpolicy = GTK_POLICY_ALWAYS;
1654 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->scrolled_window),
1660 cell_view_is_sensitive (GtkCellView *cell_view)
1662 GList *cells, *list;
1665 cells = gtk_cell_view_get_cell_renderers (cell_view);
1668 for (list = cells; list; list = list->next)
1670 g_object_get (list->data, "sensitive", &sensitive, NULL);
1675 g_list_free (cells);
1681 tree_column_row_is_sensitive (GtkComboBox *combo_box,
1684 GtkComboBoxPrivate *priv = combo_box->priv;
1685 GList *cells, *list;
1691 if (priv->row_separator_func)
1693 if ((*priv->row_separator_func) (priv->model, iter,
1694 priv->row_separator_data))
1698 gtk_tree_view_column_cell_set_cell_data (priv->column,
1700 iter, FALSE, FALSE);
1702 cells = gtk_tree_view_column_get_cell_renderers (priv->column);
1705 for (list = cells; list; list = list->next)
1707 g_object_get (list->data, "sensitive", &sensitive, NULL);
1712 g_list_free (cells);
1718 update_menu_sensitivity (GtkComboBox *combo_box,
1721 GtkComboBoxPrivate *priv = combo_box->priv;
1722 GList *children, *child;
1723 GtkWidget *item, *submenu, *separator;
1724 GtkWidget *cell_view;
1730 children = gtk_container_get_children (GTK_CONTAINER (menu));
1732 for (child = children; child; child = child->next)
1734 item = GTK_WIDGET (child->data);
1735 cell_view = GTK_BIN (item)->child;
1737 if (!GTK_IS_CELL_VIEW (cell_view))
1740 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (item));
1741 if (submenu != NULL)
1743 gtk_widget_set_sensitive (item, TRUE);
1744 update_menu_sensitivity (combo_box, submenu);
1748 sensitive = cell_view_is_sensitive (GTK_CELL_VIEW (cell_view));
1750 if (menu != priv->popup_widget && child == children)
1752 separator = GTK_WIDGET (child->next->data);
1753 g_object_set (item, "visible", sensitive, NULL);
1754 g_object_set (separator, "visible", sensitive, NULL);
1757 gtk_widget_set_sensitive (item, sensitive);
1761 g_list_free (children);
1765 gtk_combo_box_menu_popup (GtkComboBox *combo_box,
1767 guint32 activate_time)
1769 GtkComboBoxPrivate *priv = combo_box->priv;
1772 GtkRequisition requisition;
1775 update_menu_sensitivity (combo_box, priv->popup_widget);
1778 if (gtk_tree_row_reference_valid (priv->active_row))
1780 path = gtk_tree_row_reference_get_path (priv->active_row);
1781 active_item = gtk_tree_path_get_indices (path)[0];
1782 gtk_tree_path_free (path);
1784 if (priv->add_tearoffs)
1788 /* FIXME handle nested menus better */
1789 gtk_menu_set_active (GTK_MENU (priv->popup_widget), active_item);
1791 if (priv->wrap_width == 0)
1793 width = GTK_WIDGET (combo_box)->allocation.width;
1794 gtk_widget_set_size_request (priv->popup_widget, -1, -1);
1795 gtk_widget_size_request (priv->popup_widget, &requisition);
1797 gtk_widget_set_size_request (priv->popup_widget,
1798 MAX (width, requisition.width), -1);
1801 gtk_menu_popup (GTK_MENU (priv->popup_widget),
1803 gtk_combo_box_menu_position, combo_box,
1804 button, activate_time);
1808 popup_grab_on_window (GdkWindow *window,
1809 guint32 activate_time,
1810 gboolean grab_keyboard)
1812 if ((gdk_pointer_grab (window, TRUE,
1813 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1814 GDK_POINTER_MOTION_MASK,
1815 NULL, NULL, activate_time) == 0))
1817 if (!grab_keyboard ||
1818 gdk_keyboard_grab (window, TRUE,
1819 activate_time) == 0)
1823 gdk_display_pointer_ungrab (gdk_drawable_get_display (window),
1833 * gtk_combo_box_popup:
1834 * @combo_box: a #GtkComboBox
1836 * Pops up the menu or dropdown list of @combo_box.
1838 * This function is mostly intended for use by accessibility technologies;
1839 * applications should have little use for it.
1844 gtk_combo_box_popup (GtkComboBox *combo_box)
1846 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
1848 g_signal_emit (combo_box, combo_box_signals[POPUP], 0);
1852 gtk_combo_box_real_popup (GtkComboBox *combo_box)
1854 GtkComboBoxPrivate *priv = combo_box->priv;
1855 gint x, y, width, height;
1856 GtkTreePath *path = NULL, *ppath;
1857 GtkWidget *toplevel;
1859 if (!GTK_WIDGET_REALIZED (combo_box))
1862 if (GTK_WIDGET_MAPPED (priv->popup_widget))
1865 if (GTK_IS_MENU (priv->popup_widget))
1867 gtk_combo_box_menu_popup (combo_box,
1868 priv->activate_button,
1869 priv->activate_time);
1873 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (combo_box));
1874 if (GTK_IS_WINDOW (toplevel))
1875 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
1876 GTK_WINDOW (priv->popup_window));
1878 gtk_widget_show_all (priv->scrolled_window);
1879 gtk_combo_box_list_position (combo_box, &x, &y, &width, &height);
1881 gtk_widget_set_size_request (priv->popup_window, width, height);
1882 gtk_window_move (GTK_WINDOW (priv->popup_window), x, y);
1884 if (gtk_tree_row_reference_valid (priv->active_row))
1886 path = gtk_tree_row_reference_get_path (priv->active_row);
1887 ppath = gtk_tree_path_copy (path);
1888 if (gtk_tree_path_up (ppath))
1889 gtk_tree_view_expand_to_path (GTK_TREE_VIEW (priv->tree_view),
1891 gtk_tree_path_free (ppath);
1893 gtk_tree_view_set_hover_expand (GTK_TREE_VIEW (priv->tree_view),
1897 gtk_widget_show (priv->popup_window);
1901 gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->tree_view),
1903 gtk_tree_path_free (path);
1906 gtk_widget_grab_focus (priv->popup_window);
1907 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button),
1910 if (!GTK_WIDGET_HAS_FOCUS (priv->tree_view))
1911 gtk_widget_grab_focus (priv->tree_view);
1913 if (!popup_grab_on_window (priv->popup_window->window,
1914 GDK_CURRENT_TIME, TRUE))
1916 gtk_widget_hide (priv->popup_window);
1920 gtk_grab_add (priv->popup_window);
1924 gtk_combo_box_real_popdown (GtkComboBox *combo_box)
1926 if (combo_box->priv->popup_shown)
1928 gtk_combo_box_popdown (combo_box);
1936 * gtk_combo_box_popdown:
1937 * @combo_box: a #GtkComboBox
1939 * Hides the menu or dropdown list of @combo_box.
1941 * This function is mostly intended for use by accessibility technologies;
1942 * applications should have little use for it.
1947 gtk_combo_box_popdown (GtkComboBox *combo_box)
1949 GtkComboBoxPrivate *priv = combo_box->priv;
1951 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
1953 if (GTK_IS_MENU (priv->popup_widget))
1955 gtk_menu_popdown (GTK_MENU (priv->popup_widget));
1959 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (combo_box)))
1962 gtk_grab_remove (priv->popup_window);
1963 gtk_widget_hide_all (priv->popup_window);
1964 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button),
1969 gtk_combo_box_calc_requested_width (GtkComboBox *combo_box,
1972 GtkComboBoxPrivate *priv = combo_box->priv;
1976 if (priv->cell_view)
1977 gtk_widget_style_get (priv->cell_view,
1978 "focus-line-width", &padding,
1983 /* add some pixels for good measure */
1984 padding += BONUS_PADDING;
1986 if (priv->cell_view)
1987 gtk_cell_view_get_size_of_row (GTK_CELL_VIEW (priv->cell_view),
1992 return req.width + padding;
1996 gtk_combo_box_remeasure (GtkComboBox *combo_box)
1998 GtkComboBoxPrivate *priv = combo_box->priv;
2003 !gtk_tree_model_get_iter_first (priv->model, &iter))
2009 path = gtk_tree_path_new_from_indices (0, -1);
2015 if (priv->cell_view)
2016 gtk_cell_view_get_size_of_row (GTK_CELL_VIEW (priv->cell_view),
2024 priv->width = MAX (priv->width, req.width);
2025 priv->height = MAX (priv->height, req.height);
2027 gtk_tree_path_next (path);
2029 while (gtk_tree_model_iter_next (priv->model, &iter));
2031 gtk_tree_path_free (path);
2035 gtk_combo_box_size_request (GtkWidget *widget,
2036 GtkRequisition *requisition)
2039 gint focus_width, focus_pad;
2042 GtkRequisition bin_req;
2043 PangoContext *context;
2044 PangoFontMetrics *metrics;
2045 PangoFontDescription *font_desc;
2047 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
2048 GtkComboBoxPrivate *priv = combo_box->priv;
2051 gtk_widget_size_request (GTK_BIN (widget)->child, &bin_req);
2052 gtk_combo_box_remeasure (combo_box);
2053 bin_req.width = MAX (bin_req.width, priv->width);
2054 bin_req.height = MAX (bin_req.height, priv->height);
2056 gtk_widget_style_get (GTK_WIDGET (widget),
2057 "focus-line-width", &focus_width,
2058 "focus-padding", &focus_pad,
2059 "arrow-size", &arrow_size,
2062 font_desc = GTK_BIN (widget)->child->style->font_desc;
2063 context = gtk_widget_get_pango_context (widget);
2064 metrics = pango_context_get_metrics (context, font_desc,
2065 pango_context_get_language (context));
2066 font_size = PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) +
2067 pango_font_metrics_get_descent (metrics));
2068 pango_font_metrics_unref (metrics);
2070 arrow_size = MAX (arrow_size, font_size);
2072 gtk_widget_set_size_request (priv->arrow, arrow_size, arrow_size);
2074 if (!priv->tree_view)
2078 if (priv->cell_view)
2080 GtkRequisition button_req, sep_req, arrow_req;
2081 gint border_width, xthickness, ythickness;
2083 gtk_widget_size_request (priv->button, &button_req);
2084 border_width = GTK_CONTAINER (combo_box)->border_width;
2085 xthickness = priv->button->style->xthickness;
2086 ythickness = priv->button->style->ythickness;
2088 bin_req.width = MAX (bin_req.width, priv->width);
2089 bin_req.height = MAX (bin_req.height, priv->height);
2091 gtk_widget_size_request (priv->separator, &sep_req);
2092 gtk_widget_size_request (priv->arrow, &arrow_req);
2094 height = MAX (sep_req.height, arrow_req.height);
2095 height = MAX (height, bin_req.height);
2097 width = bin_req.width + sep_req.width + arrow_req.width;
2099 height += 2*(border_width + ythickness + focus_width + focus_pad);
2100 width += 2*(border_width + xthickness + focus_width + focus_pad);
2102 requisition->width = width;
2103 requisition->height = height;
2107 GtkRequisition but_req;
2109 gtk_widget_size_request (priv->button, &but_req);
2111 requisition->width = bin_req.width + but_req.width;
2112 requisition->height = MAX (bin_req.height, but_req.height);
2118 GtkRequisition button_req, frame_req;
2120 /* sample + frame */
2121 *requisition = bin_req;
2123 requisition->width += 2 * focus_width;
2125 if (priv->cell_view_frame)
2127 gtk_widget_size_request (priv->cell_view_frame, &frame_req);
2128 if (priv->has_frame)
2130 requisition->width += 2 *
2131 (GTK_CONTAINER (priv->cell_view_frame)->border_width +
2132 GTK_WIDGET (priv->cell_view_frame)->style->xthickness);
2133 requisition->height += 2 *
2134 (GTK_CONTAINER (priv->cell_view_frame)->border_width +
2135 GTK_WIDGET (priv->cell_view_frame)->style->ythickness);
2140 gtk_widget_size_request (priv->button, &button_req);
2142 requisition->height = MAX (requisition->height, button_req.height);
2143 requisition->width += button_req.width;
2146 if (GTK_SHADOW_NONE != priv->shadow_type)
2148 requisition->height += 2 * widget->style->ythickness;
2149 requisition->width += 2 * widget->style->xthickness;
2153 #define GTK_COMBO_BOX_SIZE_ALLOCATE_BUTTON \
2154 gtk_widget_size_request (combo_box->priv->button, &req); \
2157 child.x = allocation->x + shadow_width; \
2159 child.x = allocation->x + allocation->width - req.width - shadow_width; \
2161 child.y = allocation->y + shadow_height; \
2162 child.width = req.width; \
2163 child.height = allocation->height - 2 * shadow_height; \
2164 child.width = MAX (1, child.width); \
2165 child.height = MAX (1, child.height); \
2167 gtk_widget_size_allocate (combo_box->priv->button, &child);
2170 gtk_combo_box_size_allocate (GtkWidget *widget,
2171 GtkAllocation *allocation)
2173 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
2174 GtkComboBoxPrivate *priv = combo_box->priv;
2175 gint shadow_width, shadow_height;
2176 gint focus_width, focus_pad;
2177 GtkAllocation child;
2179 gboolean is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
2181 widget->allocation = *allocation;
2183 gtk_widget_style_get (GTK_WIDGET (widget),
2184 "focus-line-width", &focus_width,
2185 "focus-padding", &focus_pad,
2188 if (GTK_SHADOW_NONE != priv->shadow_type)
2190 shadow_width = widget->style->xthickness;
2191 shadow_height = widget->style->ythickness;
2199 if (!priv->tree_view)
2201 if (priv->cell_view)
2203 gint border_width, xthickness, ythickness;
2207 allocation->x += shadow_width;
2208 allocation->y += shadow_height;
2209 allocation->width -= 2 * shadow_width;
2210 allocation->height -= 2 * shadow_height;
2212 gtk_widget_size_allocate (priv->button, allocation);
2214 /* set some things ready */
2215 border_width = GTK_CONTAINER (priv->button)->border_width;
2216 xthickness = priv->button->style->xthickness;
2217 ythickness = priv->button->style->ythickness;
2219 child.x = allocation->x;
2220 child.y = allocation->y;
2221 width = allocation->width;
2222 child.height = allocation->height;
2224 if (!priv->is_cell_renderer)
2226 child.x += border_width + xthickness + focus_width + focus_pad;
2227 child.y += border_width + ythickness + focus_width + focus_pad;
2228 width -= 2 * (child.x - allocation->x);
2229 child.height -= 2 * (child.y - allocation->y);
2233 /* handle the children */
2234 gtk_widget_size_request (priv->arrow, &req);
2235 child.width = req.width;
2237 child.x += width - req.width;
2238 child.width = MAX (1, child.width);
2239 child.height = MAX (1, child.height);
2240 gtk_widget_size_allocate (priv->arrow, &child);
2242 child.x += req.width;
2243 gtk_widget_size_request (priv->separator, &req);
2244 child.width = req.width;
2246 child.x -= req.width;
2247 child.width = MAX (1, child.width);
2248 child.height = MAX (1, child.height);
2249 gtk_widget_size_allocate (priv->separator, &child);
2253 child.x += req.width;
2254 child.width = allocation->x + allocation->width
2255 - (border_width + xthickness + focus_width + focus_pad)
2260 child.width = child.x;
2261 child.x = allocation->x
2262 + border_width + xthickness + focus_width + focus_pad;
2263 child.width -= child.x;
2266 child.width = MAX (1, child.width);
2267 child.height = MAX (1, child.height);
2268 gtk_widget_size_allocate (GTK_BIN (widget)->child, &child);
2272 GTK_COMBO_BOX_SIZE_ALLOCATE_BUTTON
2275 child.x = allocation->x + req.width + shadow_width;
2277 child.x = allocation->x + shadow_width;
2278 child.y = allocation->y + shadow_height;
2279 child.width = allocation->width - req.width - 2 * shadow_width;
2280 child.width = MAX (1, child.width);
2281 child.height = MAX (1, child.height);
2282 gtk_widget_size_allocate (GTK_BIN (widget)->child, &child);
2289 /* Combobox thickness + border-width */
2290 int delta_x = shadow_width + GTK_CONTAINER (widget)->border_width;
2291 int delta_y = shadow_height + GTK_CONTAINER (widget)->border_width;
2294 GTK_COMBO_BOX_SIZE_ALLOCATE_BUTTON
2298 child.x = allocation->x + req.width;
2300 child.x = allocation->x;
2302 child.y = allocation->y;
2303 child.width = allocation->width - req.width;
2304 child.height = allocation->height;
2306 if (priv->cell_view_frame)
2310 child.width = MAX (1, child.width - delta_x * 2);
2311 child.height = MAX (1, child.height - delta_y * 2);
2312 gtk_widget_size_allocate (priv->cell_view_frame, &child);
2315 if (priv->has_frame)
2317 delta_x = GTK_CONTAINER (priv->cell_view_frame)->border_width +
2318 GTK_WIDGET (priv->cell_view_frame)->style->xthickness;
2319 delta_y = GTK_CONTAINER (priv->cell_view_frame)->border_width +
2320 GTK_WIDGET (priv->cell_view_frame)->style->ythickness;
2324 child.width -= delta_x * 2;
2325 child.height -= delta_y * 2;
2332 child.width -= delta_x * 2;
2333 child.height -= delta_y * 2;
2336 child.width = MAX (1, child.width);
2337 child.height = MAX (1, child.height);
2339 gtk_widget_size_allocate (GTK_BIN (combo_box)->child, &child);
2343 #undef GTK_COMBO_BOX_ALLOCATE_BUTTON
2346 gtk_combo_box_unset_model (GtkComboBox *combo_box)
2348 GtkComboBoxPrivate *priv = combo_box->priv;
2352 g_signal_handler_disconnect (priv->model,
2354 g_signal_handler_disconnect (priv->model,
2356 g_signal_handler_disconnect (priv->model,
2357 priv->reordered_id);
2358 g_signal_handler_disconnect (priv->model,
2363 if (!priv->tree_view)
2365 if (priv->popup_widget)
2366 gtk_container_foreach (GTK_CONTAINER (priv->popup_widget),
2367 (GtkCallback)gtk_widget_destroy, NULL);
2372 g_object_unref (priv->model);
2376 if (priv->active_row)
2378 gtk_tree_row_reference_free (priv->active_row);
2379 priv->active_row = NULL;
2382 if (priv->cell_view)
2383 gtk_cell_view_set_model (GTK_CELL_VIEW (priv->cell_view), NULL);
2387 gtk_combo_box_forall (GtkContainer *container,
2388 gboolean include_internals,
2389 GtkCallback callback,
2390 gpointer callback_data)
2392 GtkComboBox *combo_box = GTK_COMBO_BOX (container);
2393 GtkComboBoxPrivate *priv = combo_box->priv;
2395 if (include_internals)
2398 (* callback) (priv->button, callback_data);
2399 if (priv->cell_view_frame)
2400 (* callback) (priv->cell_view_frame, callback_data);
2403 if (GTK_BIN (container)->child)
2404 (* callback) (GTK_BIN (container)->child, callback_data);
2408 gtk_combo_box_child_show (GtkWidget *widget,
2409 GtkComboBox *combo_box)
2411 GtkComboBoxPrivate *priv = combo_box->priv;
2413 priv->popup_shown = TRUE;
2414 g_object_notify (G_OBJECT (combo_box), "popup-shown");
2418 gtk_combo_box_child_hide (GtkWidget *widget,
2419 GtkComboBox *combo_box)
2421 GtkComboBoxPrivate *priv = combo_box->priv;
2423 priv->popup_shown = FALSE;
2424 g_object_notify (G_OBJECT (combo_box), "popup-shown");
2428 gtk_combo_box_expose_event (GtkWidget *widget,
2429 GdkEventExpose *event)
2431 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
2432 GtkComboBoxPrivate *priv = combo_box->priv;
2434 if (GTK_WIDGET_DRAWABLE (widget) &&
2435 GTK_SHADOW_NONE != priv->shadow_type)
2437 gtk_paint_shadow (widget->style, widget->window,
2438 GTK_STATE_NORMAL, priv->shadow_type,
2439 NULL, widget, "combobox",
2440 widget->allocation.x, widget->allocation.y,
2441 widget->allocation.width, widget->allocation.height);
2444 gtk_container_propagate_expose (GTK_CONTAINER (widget),
2445 priv->button, event);
2447 if (priv->tree_view && priv->cell_view_frame)
2449 gtk_container_propagate_expose (GTK_CONTAINER (widget),
2450 priv->cell_view_frame, event);
2453 gtk_container_propagate_expose (GTK_CONTAINER (widget),
2454 GTK_BIN (widget)->child, event);
2469 path_visible (GtkTreeView *view,
2475 /* Note that we rely on the fact that collapsed rows don't have nodes
2477 return _gtk_tree_view_find_node (view, path, &tree, &node);
2481 tree_next_func (GtkTreeModel *model,
2486 SearchData *search_data = (SearchData *)data;
2488 if (search_data->found)
2490 if (!tree_column_row_is_sensitive (search_data->combo, iter))
2493 if (search_data->visible &&
2494 !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
2497 search_data->set = TRUE;
2498 search_data->iter = *iter;
2503 if (gtk_tree_path_compare (path, search_data->path) == 0)
2504 search_data->found = TRUE;
2510 tree_next (GtkComboBox *combo,
2511 GtkTreeModel *model,
2516 SearchData search_data;
2518 search_data.combo = combo;
2519 search_data.path = gtk_tree_model_get_path (model, iter);
2520 search_data.visible = visible;
2521 search_data.found = FALSE;
2522 search_data.set = FALSE;
2524 gtk_tree_model_foreach (model, tree_next_func, &search_data);
2526 *next = search_data.iter;
2528 gtk_tree_path_free (search_data.path);
2530 return search_data.set;
2534 tree_prev_func (GtkTreeModel *model,
2539 SearchData *search_data = (SearchData *)data;
2541 if (gtk_tree_path_compare (path, search_data->path) == 0)
2543 search_data->found = TRUE;
2547 if (!tree_column_row_is_sensitive (search_data->combo, iter))
2550 if (search_data->visible &&
2551 !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
2554 search_data->set = TRUE;
2555 search_data->iter = *iter;
2561 tree_prev (GtkComboBox *combo,
2562 GtkTreeModel *model,
2567 SearchData search_data;
2569 search_data.combo = combo;
2570 search_data.path = gtk_tree_model_get_path (model, iter);
2571 search_data.visible = visible;
2572 search_data.found = FALSE;
2573 search_data.set = FALSE;
2575 gtk_tree_model_foreach (model, tree_prev_func, &search_data);
2577 *prev = search_data.iter;
2579 gtk_tree_path_free (search_data.path);
2581 return search_data.set;
2585 tree_last_func (GtkTreeModel *model,
2590 SearchData *search_data = (SearchData *)data;
2592 if (!tree_column_row_is_sensitive (search_data->combo, iter))
2595 /* Note that we rely on the fact that collapsed rows don't have nodes
2597 if (search_data->visible &&
2598 !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
2601 search_data->set = TRUE;
2602 search_data->iter = *iter;
2608 tree_last (GtkComboBox *combo,
2609 GtkTreeModel *model,
2613 SearchData search_data;
2615 search_data.combo = combo;
2616 search_data.visible = visible;
2617 search_data.set = FALSE;
2619 gtk_tree_model_foreach (model, tree_last_func, &search_data);
2621 *last = search_data.iter;
2623 return search_data.set;
2628 tree_first_func (GtkTreeModel *model,
2633 SearchData *search_data = (SearchData *)data;
2635 if (!tree_column_row_is_sensitive (search_data->combo, iter))
2638 if (search_data->visible &&
2639 !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
2642 search_data->set = TRUE;
2643 search_data->iter = *iter;
2649 tree_first (GtkComboBox *combo,
2650 GtkTreeModel *model,
2654 SearchData search_data;
2656 search_data.combo = combo;
2657 search_data.visible = visible;
2658 search_data.set = FALSE;
2660 gtk_tree_model_foreach (model, tree_first_func, &search_data);
2662 *first = search_data.iter;
2664 return search_data.set;
2668 gtk_combo_box_scroll_event (GtkWidget *widget,
2669 GdkEventScroll *event)
2671 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
2674 GtkTreeIter new_iter;
2676 if (!gtk_combo_box_get_active_iter (combo_box, &iter))
2679 if (event->direction == GDK_SCROLL_UP)
2680 found = tree_prev (combo_box, combo_box->priv->model,
2681 &iter, &new_iter, FALSE);
2683 found = tree_next (combo_box, combo_box->priv->model,
2684 &iter, &new_iter, FALSE);
2687 gtk_combo_box_set_active_iter (combo_box, &new_iter);
2697 gtk_combo_box_sync_cells (GtkComboBox *combo_box,
2698 GtkCellLayout *cell_layout)
2700 GtkComboBoxPrivate *priv = combo_box->priv;
2703 for (k = priv->cells; k; k = k->next)
2706 ComboCellInfo *info = (ComboCellInfo *)k->data;
2708 if (info->pack == GTK_PACK_START)
2709 gtk_cell_layout_pack_start (cell_layout,
2710 info->cell, info->expand);
2711 else if (info->pack == GTK_PACK_END)
2712 gtk_cell_layout_pack_end (cell_layout,
2713 info->cell, info->expand);
2715 gtk_cell_layout_set_cell_data_func (cell_layout,
2717 combo_cell_data_func, info, NULL);
2719 for (j = info->attributes; j; j = j->next->next)
2721 gtk_cell_layout_add_attribute (cell_layout,
2724 GPOINTER_TO_INT (j->next->data));
2730 gtk_combo_box_menu_setup (GtkComboBox *combo_box,
2731 gboolean add_children)
2733 GtkComboBoxPrivate *priv = combo_box->priv;
2736 if (priv->cell_view)
2738 priv->button = gtk_toggle_button_new ();
2739 gtk_button_set_focus_on_click (GTK_BUTTON (priv->button),
2740 priv->focus_on_click);
2742 g_signal_connect (priv->button, "toggled",
2743 G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
2744 gtk_widget_set_parent (priv->button,
2745 GTK_BIN (combo_box)->child->parent);
2747 priv->box = gtk_hbox_new (FALSE, 0);
2748 gtk_container_add (GTK_CONTAINER (priv->button), priv->box);
2750 priv->separator = gtk_vseparator_new ();
2751 gtk_container_add (GTK_CONTAINER (priv->box), priv->separator);
2753 priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
2754 gtk_container_add (GTK_CONTAINER (priv->box), priv->arrow);
2756 gtk_widget_show_all (priv->button);
2760 priv->button = gtk_toggle_button_new ();
2761 gtk_button_set_focus_on_click (GTK_BUTTON (priv->button),
2762 priv->focus_on_click);
2764 g_signal_connect (priv->button, "toggled",
2765 G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
2766 gtk_widget_set_parent (priv->button,
2767 GTK_BIN (combo_box)->child->parent);
2769 priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
2770 gtk_container_add (GTK_CONTAINER (priv->button), priv->arrow);
2771 gtk_widget_show_all (priv->button);
2774 g_signal_connect (priv->button, "button_press_event",
2775 G_CALLBACK (gtk_combo_box_menu_button_press),
2777 g_signal_connect (priv->button, "state_changed",
2778 G_CALLBACK (gtk_combo_box_button_state_changed),
2781 /* create our funky menu */
2782 menu = gtk_menu_new ();
2783 gtk_widget_set_name (menu, "gtk-combobox-popup-menu");
2785 g_signal_connect (menu, "key_press_event",
2786 G_CALLBACK (gtk_combo_box_menu_key_press), combo_box);
2787 gtk_combo_box_set_popup_widget (combo_box, menu);
2791 gtk_combo_box_menu_fill (combo_box);
2793 /* the column is needed in tree_column_row_is_sensitive() */
2794 priv->column = gtk_tree_view_column_new ();
2795 g_object_ref_sink (priv->column);
2796 gtk_combo_box_sync_cells (combo_box, GTK_CELL_LAYOUT (priv->column));
2798 gtk_combo_box_update_title (combo_box);
2802 gtk_combo_box_menu_fill (GtkComboBox *combo_box)
2804 GtkComboBoxPrivate *priv = combo_box->priv;
2810 menu = priv->popup_widget;
2812 if (priv->add_tearoffs)
2814 GtkWidget *tearoff = gtk_tearoff_menu_item_new ();
2816 gtk_widget_show (tearoff);
2818 if (priv->wrap_width)
2819 gtk_menu_attach (GTK_MENU (menu), tearoff, 0, priv->wrap_width, 0, 1);
2821 gtk_menu_shell_append (GTK_MENU_SHELL (menu), tearoff);
2824 gtk_combo_box_menu_fill_level (combo_box, menu, NULL);
2828 gtk_cell_view_menu_item_new (GtkComboBox *combo_box,
2829 GtkTreeModel *model,
2832 GtkWidget *cell_view;
2837 cell_view = gtk_cell_view_new ();
2838 item = gtk_menu_item_new ();
2839 gtk_container_add (GTK_CONTAINER (item), cell_view);
2841 gtk_cell_view_set_model (GTK_CELL_VIEW (cell_view), model);
2842 path = gtk_tree_model_get_path (model, iter);
2843 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (cell_view), path);
2844 gtk_tree_path_free (path);
2846 gtk_combo_box_sync_cells (combo_box, GTK_CELL_LAYOUT (cell_view));
2847 gtk_widget_size_request (cell_view, &req);
2848 gtk_widget_show (cell_view);
2854 gtk_combo_box_menu_fill_level (GtkComboBox *combo_box,
2856 GtkTreeIter *parent)
2858 GtkComboBoxPrivate *priv = combo_box->priv;
2859 GtkTreeModel *model = priv->model;
2860 GtkWidget *item, *submenu, *subitem, *separator;
2862 gboolean is_separator;
2867 n_children = gtk_tree_model_iter_n_children (model, parent);
2870 for (i = 0; i < n_children; i++)
2872 gtk_tree_model_iter_nth_child (model, &iter, parent, i);
2874 if (priv->row_separator_func)
2875 is_separator = (*priv->row_separator_func) (priv->model, &iter,
2876 priv->row_separator_data);
2878 is_separator = FALSE;
2882 item = gtk_separator_menu_item_new ();
2883 path = gtk_tree_model_get_path (model, &iter);
2884 g_object_set_data_full (G_OBJECT (item),
2885 I_("gtk-combo-box-item-path"),
2886 gtk_tree_row_reference_new (model, path),
2887 (GDestroyNotify)gtk_tree_row_reference_free);
2888 gtk_tree_path_free (path);
2892 item = gtk_cell_view_menu_item_new (combo_box, model, &iter);
2893 if (gtk_tree_model_iter_has_child (model, &iter))
2895 submenu = gtk_menu_new ();
2896 gtk_widget_show (submenu);
2897 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu);
2899 /* Ugly - since menus can only activate leafs, we have to
2900 * duplicate the item inside the submenu.
2902 subitem = gtk_cell_view_menu_item_new (combo_box, model, &iter);
2903 separator = gtk_separator_menu_item_new ();
2904 gtk_widget_show (subitem);
2905 gtk_widget_show (separator);
2906 g_signal_connect (subitem, "activate",
2907 G_CALLBACK (gtk_combo_box_menu_item_activate),
2909 gtk_menu_shell_append (GTK_MENU_SHELL (submenu), subitem);
2910 gtk_menu_shell_append (GTK_MENU_SHELL (submenu), separator);
2912 gtk_combo_box_menu_fill_level (combo_box, submenu, &iter);
2914 g_signal_connect (item, "activate",
2915 G_CALLBACK (gtk_combo_box_menu_item_activate),
2919 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2920 if (priv->wrap_width && menu == priv->popup_widget)
2921 gtk_combo_box_relayout_item (combo_box, item, &iter, last);
2922 gtk_widget_show (item);
2929 gtk_combo_box_menu_destroy (GtkComboBox *combo_box)
2931 GtkComboBoxPrivate *priv = combo_box->priv;
2933 g_signal_handlers_disconnect_matched (priv->button,
2934 G_SIGNAL_MATCH_DATA,
2936 gtk_combo_box_menu_button_press, NULL);
2937 g_signal_handlers_disconnect_matched (priv->button,
2938 G_SIGNAL_MATCH_DATA,
2940 gtk_combo_box_button_state_changed, combo_box);
2942 /* unparent will remove our latest ref */
2943 gtk_widget_unparent (priv->button);
2946 priv->button = NULL;
2948 priv->separator = NULL;
2950 g_object_unref (priv->column);
2951 priv->column = NULL;
2953 /* changing the popup window will unref the menu and the children */
2961 menu_occupied (GtkMenu *menu,
2965 guint bottom_attach)
2969 for (i = GTK_MENU_SHELL (menu)->children; i; i = i->next)
2973 gtk_container_child_get (GTK_CONTAINER (menu),
2977 "bottom-attach", &b,
2981 /* look if this item intersects with the given coordinates */
2982 if (right_attach > l && left_attach < r && bottom_attach > t && top_attach < b)
2990 gtk_combo_box_relayout_item (GtkComboBox *combo_box,
2995 GtkComboBoxPrivate *priv = combo_box->priv;
2996 gint current_col = 0, current_row = 0;
2997 gint rows = 1, cols = 1;
2998 GtkWidget *menu = priv->popup_widget;
3000 if (!GTK_IS_MENU_SHELL (menu))
3003 if (priv->col_column == -1 &&
3004 priv->row_column == -1 &&
3007 gtk_container_child_get (GTK_CONTAINER (menu),
3009 "right-attach", ¤t_col,
3010 "top-attach", ¤t_row,
3012 if (current_col + cols > priv->wrap_width)
3020 if (priv->col_column != -1)
3021 gtk_tree_model_get (priv->model, iter,
3022 priv->col_column, &cols,
3024 if (priv->row_column != -1)
3025 gtk_tree_model_get (priv->model, iter,
3026 priv->row_column, &rows,
3031 if (current_col + cols > priv->wrap_width)
3037 if (!menu_occupied (GTK_MENU (menu),
3038 current_col, current_col + cols,
3039 current_row, current_row + rows))
3046 /* set attach props */
3047 gtk_menu_attach (GTK_MENU (menu), item,
3048 current_col, current_col + cols,
3049 current_row, current_row + rows);
3053 gtk_combo_box_relayout (GtkComboBox *combo_box)
3058 menu = combo_box->priv->popup_widget;
3060 /* do nothing unless we are in menu style and realized */
3061 if (combo_box->priv->tree_view || !GTK_IS_MENU_SHELL (menu))
3064 list = gtk_container_get_children (GTK_CONTAINER (menu));
3066 for (j = g_list_last (list); j; j = j->prev)
3067 gtk_container_remove (GTK_CONTAINER (menu), j->data);
3069 gtk_combo_box_menu_fill (combo_box);
3076 gtk_combo_box_menu_button_press (GtkWidget *widget,
3077 GdkEventButton *event,
3080 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3081 GtkComboBoxPrivate *priv = combo_box->priv;
3083 if (GTK_IS_MENU (priv->popup_widget) &&
3084 event->type == GDK_BUTTON_PRESS && event->button == 1)
3086 if (priv->focus_on_click &&
3087 !GTK_WIDGET_HAS_FOCUS (priv->button))
3088 gtk_widget_grab_focus (priv->button);
3090 gtk_combo_box_menu_popup (combo_box, event->button, event->time);
3099 gtk_combo_box_menu_item_activate (GtkWidget *item,
3102 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3103 GtkWidget *cell_view;
3107 cell_view = GTK_BIN (item)->child;
3109 g_return_if_fail (GTK_IS_CELL_VIEW (cell_view));
3111 path = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (cell_view));
3113 if (gtk_tree_model_get_iter (combo_box->priv->model, &iter, path))
3115 if (gtk_menu_item_get_submenu (GTK_MENU_ITEM (item)) == NULL)
3116 gtk_combo_box_set_active_iter (combo_box, &iter);
3119 gtk_tree_path_free (path);
3121 combo_box->priv->editing_canceled = FALSE;
3125 gtk_combo_box_model_row_inserted (GtkTreeModel *model,
3130 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3132 if (combo_box->priv->tree_view)
3133 gtk_combo_box_list_popup_resize (combo_box);
3135 gtk_combo_box_menu_row_inserted (model, path, iter, user_data);
3139 gtk_combo_box_model_row_deleted (GtkTreeModel *model,
3143 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3144 GtkComboBoxPrivate *priv = combo_box->priv;
3146 if (!gtk_tree_row_reference_valid (priv->active_row))
3148 if (priv->cell_view)
3149 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (priv->cell_view), NULL);
3150 g_signal_emit (combo_box, combo_box_signals[CHANGED], 0);
3153 if (priv->tree_view)
3154 gtk_combo_box_list_popup_resize (combo_box);
3156 gtk_combo_box_menu_row_deleted (model, path, user_data);
3160 gtk_combo_box_model_rows_reordered (GtkTreeModel *model,
3166 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3168 gtk_tree_row_reference_reordered (G_OBJECT (user_data), path, iter, new_order);
3170 if (!combo_box->priv->tree_view)
3171 gtk_combo_box_menu_rows_reordered (model, path, iter, new_order, user_data);
3175 gtk_combo_box_model_row_changed (GtkTreeModel *model,
3180 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3181 GtkComboBoxPrivate *priv = combo_box->priv;
3182 GtkTreePath *active_path;
3184 /* FIXME this belongs to GtkCellView */
3185 if (gtk_tree_row_reference_valid (priv->active_row))
3187 active_path = gtk_tree_row_reference_get_path (priv->active_row);
3188 if (gtk_tree_path_compare (path, active_path) == 0 &&
3190 gtk_widget_queue_resize (GTK_WIDGET (priv->cell_view));
3191 gtk_tree_path_free (active_path);
3194 if (priv->tree_view)
3195 gtk_combo_box_list_row_changed (model, path, iter, user_data);
3197 gtk_combo_box_menu_row_changed (model, path, iter, user_data);
3201 list_popup_resize_idle (gpointer user_data)
3203 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3204 GtkComboBoxPrivate *priv = combo_box->priv;
3205 gint x, y, width, height;
3207 if (priv->tree_view && GTK_WIDGET_MAPPED (priv->popup_window))
3209 gtk_combo_box_list_position (combo_box, &x, &y, &width, &height);
3211 gtk_widget_set_size_request (priv->popup_window, width, height);
3212 gtk_window_move (GTK_WINDOW (priv->popup_window), x, y);
3215 priv->resize_idle_id = 0;
3221 gtk_combo_box_list_popup_resize (GtkComboBox *combo_box)
3223 GtkComboBoxPrivate *priv = combo_box->priv;
3225 if (!priv->resize_idle_id)
3226 priv->resize_idle_id =
3227 gdk_threads_add_idle (list_popup_resize_idle, combo_box);
3231 gtk_combo_box_model_row_expanded (GtkTreeModel *model,
3236 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3238 gtk_combo_box_list_popup_resize (combo_box);
3243 find_menu_by_path (GtkWidget *menu,
3245 gboolean skip_first)
3250 GtkTreeRowReference *mref;
3254 list = gtk_container_get_children (GTK_CONTAINER (menu));
3257 for (i = list; i; i = i->next)
3259 if (GTK_IS_SEPARATOR_MENU_ITEM (i->data))
3261 mref = g_object_get_data (G_OBJECT (i->data), "gtk-combo-box-item-path");
3264 else if (!gtk_tree_row_reference_valid (mref))
3267 mpath = gtk_tree_row_reference_get_path (mref);
3269 else if (GTK_IS_CELL_VIEW (GTK_BIN (i->data)->child))
3277 mpath = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (GTK_BIN (i->data)->child));
3282 /* this case is necessary, since the row reference of
3283 * the cell view may already be updated after a deletion
3290 if (gtk_tree_path_compare (mpath, path) == 0)
3292 gtk_tree_path_free (mpath);
3296 if (gtk_tree_path_is_ancestor (mpath, path))
3298 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
3299 if (submenu != NULL)
3301 gtk_tree_path_free (mpath);
3302 item = find_menu_by_path (submenu, path, TRUE);
3306 gtk_tree_path_free (mpath);
3316 dump_menu_tree (GtkWidget *menu,
3323 list = gtk_container_get_children (GTK_CONTAINER (menu));
3324 for (i = list; i; i = i->next)
3326 if (GTK_IS_CELL_VIEW (GTK_BIN (i->data)->child))
3328 path = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (GTK_BIN (i->data)->child));
3329 g_print ("%*s%s\n", 2 * level, " ", gtk_tree_path_to_string (path));
3330 gtk_tree_path_free (path);
3332 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
3333 if (submenu != NULL)
3334 dump_menu_tree (submenu, level + 1);
3343 gtk_combo_box_menu_row_inserted (GtkTreeModel *model,
3348 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3349 GtkComboBoxPrivate *priv = combo_box->priv;
3351 GtkWidget *item, *menu, *separator;
3355 gboolean is_separator;
3357 if (!priv->popup_widget)
3360 depth = gtk_tree_path_get_depth (path);
3361 pos = gtk_tree_path_get_indices (path)[depth - 1];
3364 ppath = gtk_tree_path_copy (path);
3365 gtk_tree_path_up (ppath);
3366 parent = find_menu_by_path (priv->popup_widget, ppath, FALSE);
3367 gtk_tree_path_free (ppath);
3369 menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (parent));
3372 menu = gtk_menu_new ();
3373 gtk_widget_show (menu);
3374 gtk_menu_item_set_submenu (GTK_MENU_ITEM (parent), menu);
3376 /* Ugly - since menus can only activate leaves, we have to
3377 * duplicate the item inside the submenu.
3379 gtk_tree_model_iter_parent (model, &piter, iter);
3380 item = gtk_cell_view_menu_item_new (combo_box, model, &piter);
3381 separator = gtk_separator_menu_item_new ();
3382 g_signal_connect (item, "activate",
3383 G_CALLBACK (gtk_combo_box_menu_item_activate),
3385 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3386 gtk_menu_shell_append (GTK_MENU_SHELL (menu), separator);
3387 if (cell_view_is_sensitive (GTK_CELL_VIEW (GTK_BIN (item)->child)))
3389 gtk_widget_show (item);
3390 gtk_widget_show (separator);
3397 menu = priv->popup_widget;
3398 if (priv->add_tearoffs)
3402 if (priv->row_separator_func)
3403 is_separator = (*priv->row_separator_func) (model, iter,
3404 priv->row_separator_data);
3406 is_separator = FALSE;
3410 item = gtk_separator_menu_item_new ();
3411 g_object_set_data_full (G_OBJECT (item),
3412 I_("gtk-combo-box-item-path"),
3413 gtk_tree_row_reference_new (model, path),
3414 (GDestroyNotify)gtk_tree_row_reference_free);
3418 item = gtk_cell_view_menu_item_new (combo_box, model, iter);
3420 g_signal_connect (item, "activate",
3421 G_CALLBACK (gtk_combo_box_menu_item_activate),
3425 gtk_widget_show (item);
3426 gtk_menu_shell_insert (GTK_MENU_SHELL (menu), item, pos);
3430 gtk_combo_box_menu_row_deleted (GtkTreeModel *model,
3434 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3435 GtkComboBoxPrivate *priv = combo_box->priv;
3439 if (!priv->popup_widget)
3442 item = find_menu_by_path (priv->popup_widget, path, FALSE);
3443 menu = gtk_widget_get_parent (item);
3444 gtk_container_remove (GTK_CONTAINER (menu), item);
3446 if (gtk_tree_path_get_depth (path) > 1)
3448 GtkTreePath *parent_path;
3452 parent_path = gtk_tree_path_copy (path);
3453 gtk_tree_path_up (parent_path);
3454 gtk_tree_model_get_iter (model, &iter, parent_path);
3456 if (!gtk_tree_model_iter_has_child (model, &iter))
3458 parent = find_menu_by_path (priv->popup_widget,
3459 parent_path, FALSE);
3460 gtk_menu_item_set_submenu (GTK_MENU_ITEM (parent), NULL);
3466 gtk_combo_box_menu_rows_reordered (GtkTreeModel *model,
3472 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3474 gtk_combo_box_relayout (combo_box);
3478 gtk_combo_box_menu_row_changed (GtkTreeModel *model,
3483 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3484 GtkComboBoxPrivate *priv = combo_box->priv;
3487 gboolean is_separator;
3489 if (!priv->popup_widget)
3492 item = find_menu_by_path (priv->popup_widget, path, FALSE);
3494 if (priv->row_separator_func)
3495 is_separator = (*priv->row_separator_func) (model, iter,
3496 priv->row_separator_data);
3498 is_separator = FALSE;
3500 if (is_separator != GTK_IS_SEPARATOR_MENU_ITEM (item))
3502 gtk_combo_box_menu_row_deleted (model, path, combo_box);
3503 gtk_combo_box_menu_row_inserted (model, path, iter, combo_box);
3506 if (priv->wrap_width && item->parent == priv->popup_widget)
3508 GtkWidget *pitem = NULL;
3511 prev = gtk_tree_path_copy (path);
3513 if (gtk_tree_path_prev (prev))
3514 pitem = find_menu_by_path (priv->popup_widget, prev, FALSE);
3516 gtk_tree_path_free (prev);
3518 /* unattach item so gtk_combo_box_relayout_item() won't spuriously
3520 gtk_container_child_set (GTK_CONTAINER (priv->popup_widget),
3525 "bottom-attach", -1,
3528 gtk_combo_box_relayout_item (combo_box, item, iter, pitem);
3531 width = gtk_combo_box_calc_requested_width (combo_box, path);
3533 if (width > priv->width)
3535 if (priv->cell_view)
3537 gtk_widget_set_size_request (priv->cell_view, width, -1);
3538 gtk_widget_queue_resize (priv->cell_view);
3540 priv->width = width;
3549 gtk_combo_box_list_setup (GtkComboBox *combo_box)
3551 GtkComboBoxPrivate *priv = combo_box->priv;
3552 GtkTreeSelection *sel;
3554 priv->button = gtk_toggle_button_new ();
3555 gtk_widget_set_parent (priv->button,
3556 GTK_BIN (combo_box)->child->parent);
3557 g_signal_connect (priv->button, "button_press_event",
3558 G_CALLBACK (gtk_combo_box_list_button_pressed), combo_box);
3559 g_signal_connect (priv->button, "toggled",
3560 G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
3562 priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3563 gtk_container_add (GTK_CONTAINER (priv->button), priv->arrow);
3564 priv->separator = NULL;
3565 gtk_widget_show_all (priv->button);
3567 if (priv->cell_view)
3569 gtk_cell_view_set_background_color (GTK_CELL_VIEW (priv->cell_view),
3570 >K_WIDGET (combo_box)->style->base[GTK_WIDGET_STATE (combo_box)]);
3572 priv->box = gtk_event_box_new ();
3573 gtk_event_box_set_visible_window (GTK_EVENT_BOX (priv->box),
3576 if (priv->has_frame)
3578 priv->cell_view_frame = gtk_frame_new (NULL);
3579 gtk_frame_set_shadow_type (GTK_FRAME (priv->cell_view_frame),
3584 combo_box->priv->cell_view_frame = gtk_event_box_new ();
3585 gtk_event_box_set_visible_window (GTK_EVENT_BOX (combo_box->priv->cell_view_frame),
3589 gtk_widget_set_parent (priv->cell_view_frame,
3590 GTK_BIN (combo_box)->child->parent);
3591 gtk_container_add (GTK_CONTAINER (priv->cell_view_frame), priv->box);
3592 gtk_widget_show_all (priv->cell_view_frame);
3594 g_signal_connect (priv->box, "button_press_event",
3595 G_CALLBACK (gtk_combo_box_list_button_pressed),
3599 priv->tree_view = gtk_tree_view_new ();
3600 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));
3601 gtk_tree_selection_set_mode (sel, GTK_SELECTION_BROWSE);
3602 gtk_tree_selection_set_select_function (sel,
3603 gtk_combo_box_list_select_func,
3605 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->tree_view),
3607 gtk_tree_view_set_hover_selection (GTK_TREE_VIEW (priv->tree_view),
3609 if (priv->row_separator_func)
3610 gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (priv->tree_view),
3611 priv->row_separator_func,
3612 priv->row_separator_data,
3615 gtk_tree_view_set_model (GTK_TREE_VIEW (priv->tree_view), priv->model);
3617 priv->column = gtk_tree_view_column_new ();
3618 gtk_tree_view_append_column (GTK_TREE_VIEW (priv->tree_view), priv->column);
3621 gtk_combo_box_sync_cells (combo_box,
3622 GTK_CELL_LAYOUT (priv->column));
3624 if (gtk_tree_row_reference_valid (priv->active_row))
3628 path = gtk_tree_row_reference_get_path (priv->active_row);
3629 gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->tree_view),
3631 gtk_tree_path_free (path);
3634 /* set sample/popup widgets */
3635 gtk_combo_box_set_popup_widget (combo_box, priv->tree_view);
3637 g_signal_connect (priv->tree_view, "key_press_event",
3638 G_CALLBACK (gtk_combo_box_list_key_press),
3640 g_signal_connect (priv->tree_view, "enter_notify_event",
3641 G_CALLBACK (gtk_combo_box_list_enter_notify),
3643 g_signal_connect (priv->tree_view, "row_expanded",
3644 G_CALLBACK (gtk_combo_box_model_row_expanded),
3646 g_signal_connect (priv->tree_view, "row_collapsed",
3647 G_CALLBACK (gtk_combo_box_model_row_expanded),
3649 g_signal_connect (priv->popup_window, "button_press_event",
3650 G_CALLBACK (gtk_combo_box_list_button_pressed),
3652 g_signal_connect (priv->popup_window, "button_release_event",
3653 G_CALLBACK (gtk_combo_box_list_button_released),
3656 gtk_widget_show (priv->tree_view);
3660 gtk_combo_box_list_destroy (GtkComboBox *combo_box)
3662 GtkComboBoxPrivate *priv = combo_box->priv;
3664 /* disconnect signals */
3665 g_signal_handlers_disconnect_matched (priv->tree_view,
3666 G_SIGNAL_MATCH_DATA,
3667 0, 0, NULL, NULL, combo_box);
3668 g_signal_handlers_disconnect_matched (priv->button,
3669 G_SIGNAL_MATCH_DATA,
3671 gtk_combo_box_list_button_pressed,
3673 g_signal_handlers_disconnect_matched (priv->popup_window,
3674 G_SIGNAL_MATCH_DATA,
3676 gtk_combo_box_list_button_pressed,
3678 g_signal_handlers_disconnect_matched (priv->popup_window,
3679 G_SIGNAL_MATCH_DATA,
3681 gtk_combo_box_list_button_released,
3684 g_signal_handlers_disconnect_matched (priv->popup_window,
3685 G_SIGNAL_MATCH_DATA,
3687 gtk_combo_box_child_show,
3690 g_signal_handlers_disconnect_matched (priv->popup_window,
3691 G_SIGNAL_MATCH_DATA,
3693 gtk_combo_box_child_hide,
3697 g_signal_handlers_disconnect_matched (priv->box,
3698 G_SIGNAL_MATCH_DATA,
3700 gtk_combo_box_list_button_pressed,
3703 /* destroy things (unparent will kill the latest ref from us)
3704 * last unref on button will destroy the arrow
3706 gtk_widget_unparent (priv->button);
3707 priv->button = NULL;
3710 if (priv->cell_view)
3712 g_object_set (priv->cell_view,
3713 "background-set", FALSE,
3717 if (priv->cell_view_frame)
3719 gtk_widget_unparent (priv->cell_view_frame);
3720 priv->cell_view_frame = NULL;
3724 if (priv->scroll_timer)
3726 g_source_remove (priv->scroll_timer);
3727 priv->scroll_timer = 0;
3730 if (priv->resize_idle_id)
3732 g_source_remove (priv->resize_idle_id);
3733 priv->resize_idle_id = 0;
3736 gtk_widget_destroy (priv->tree_view);
3738 priv->tree_view = NULL;
3739 if (priv->popup_widget)
3741 g_object_unref (priv->popup_widget);
3742 priv->popup_widget = NULL;
3749 gtk_combo_box_list_button_pressed (GtkWidget *widget,
3750 GdkEventButton *event,
3753 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
3754 GtkComboBoxPrivate *priv = combo_box->priv;
3756 GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *)event);
3758 if (ewidget == priv->popup_window)
3761 if ((ewidget != priv->button && ewidget != priv->box) ||
3762 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->button)))
3765 if (priv->focus_on_click &&
3766 !GTK_WIDGET_HAS_FOCUS (priv->button))
3767 gtk_widget_grab_focus (priv->button);
3769 gtk_combo_box_popup (combo_box);
3771 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button), TRUE);
3773 priv->auto_scroll = FALSE;
3774 if (priv->scroll_timer == 0)
3775 priv->scroll_timer = gdk_threads_add_timeout (SCROLL_TIME,
3776 (GSourceFunc) gtk_combo_box_list_scroll_timeout,
3779 priv->popup_in_progress = TRUE;
3785 gtk_combo_box_list_button_released (GtkWidget *widget,
3786 GdkEventButton *event,
3790 GtkTreePath *path = NULL;
3793 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
3794 GtkComboBoxPrivate *priv = combo_box->priv;
3796 gboolean popup_in_progress = FALSE;
3798 GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *)event);
3800 if (priv->popup_in_progress)
3802 popup_in_progress = TRUE;
3803 priv->popup_in_progress = FALSE;
3806 gtk_tree_view_set_hover_expand (GTK_TREE_VIEW (priv->tree_view),
3808 if (priv->scroll_timer)
3810 g_source_remove (priv->scroll_timer);
3811 priv->scroll_timer = 0;
3814 if (ewidget != priv->tree_view)
3816 if ((ewidget == priv->button ||
3817 ewidget == priv->box) &&
3818 !popup_in_progress &&
3819 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->button)))
3821 gtk_combo_box_popdown (combo_box);
3825 /* released outside treeview */
3826 if (ewidget != priv->button && ewidget != priv->box)
3828 gtk_combo_box_popdown (combo_box);
3836 /* select something cool */
3837 ret = gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (priv->tree_view),
3843 return TRUE; /* clicked outside window? */
3845 gtk_tree_model_get_iter (priv->model, &iter, path);
3846 gtk_tree_path_free (path);
3848 gtk_combo_box_popdown (combo_box);
3850 if (tree_column_row_is_sensitive (combo_box, &iter))
3851 gtk_combo_box_set_active_iter (combo_box, &iter);
3857 gtk_combo_box_menu_key_press (GtkWidget *widget,
3861 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
3863 if (!gtk_bindings_activate_event (GTK_OBJECT (widget), event))
3865 /* The menu hasn't managed the
3866 * event, forward it to the combobox
3868 gtk_bindings_activate_event (GTK_OBJECT (combo_box), event);
3875 gtk_combo_box_list_key_press (GtkWidget *widget,
3879 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
3882 if (event->keyval == GDK_Return || event->keyval == GDK_ISO_Enter || event->keyval == GDK_KP_Enter ||
3883 event->keyval == GDK_space || event->keyval == GDK_KP_Space)
3885 GtkTreeModel *model = NULL;
3887 gtk_combo_box_popdown (combo_box);
3889 if (combo_box->priv->model)
3891 GtkTreeSelection *sel;
3893 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (combo_box->priv->tree_view));
3895 if (gtk_tree_selection_get_selected (sel, &model, &iter))
3896 gtk_combo_box_set_active_iter (combo_box, &iter);
3902 if (!gtk_bindings_activate_event (GTK_OBJECT (widget), event))
3904 /* The list hasn't managed the
3905 * event, forward it to the combobox
3907 gtk_bindings_activate_event (GTK_OBJECT (combo_box), event);
3914 gtk_combo_box_list_auto_scroll (GtkComboBox *combo_box,
3918 GtkWidget *tree_view = combo_box->priv->tree_view;
3922 adj = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window));
3923 if (adj && adj->upper - adj->lower > adj->page_size)
3925 if (x <= tree_view->allocation.x &&
3926 adj->lower < adj->value)
3928 value = adj->value - (tree_view->allocation.x - x + 1);
3929 gtk_adjustment_set_value (adj, CLAMP (value, adj->lower, adj->upper - adj->page_size));
3931 else if (x >= tree_view->allocation.x + tree_view->allocation.width &&
3932 adj->upper - adj->page_size > adj->value)
3934 value = adj->value + (x - tree_view->allocation.x - tree_view->allocation.width + 1);
3935 gtk_adjustment_set_value (adj, CLAMP (value, 0.0, adj->upper - adj->page_size));
3939 adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window));
3940 if (adj && adj->upper - adj->lower > adj->page_size)
3942 if (y <= tree_view->allocation.y &&
3943 adj->lower < adj->value)
3945 value = adj->value - (tree_view->allocation.y - y + 1);
3946 gtk_adjustment_set_value (adj, CLAMP (value, adj->lower, adj->upper - adj->page_size));
3948 else if (y >= tree_view->allocation.height &&
3949 adj->upper - adj->page_size > adj->value)
3951 value = adj->value + (y - tree_view->allocation.height + 1);
3952 gtk_adjustment_set_value (adj, CLAMP (value, 0.0, adj->upper - adj->page_size));
3958 gtk_combo_box_list_scroll_timeout (GtkComboBox *combo_box)
3960 GtkComboBoxPrivate *priv = combo_box->priv;
3963 if (priv->auto_scroll)
3965 gdk_window_get_pointer (priv->tree_view->window, &x, &y, NULL);
3966 gtk_combo_box_list_auto_scroll (combo_box, x, y);
3973 gtk_combo_box_list_enter_notify (GtkWidget *widget,
3974 GdkEventCrossing *event,
3977 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
3979 combo_box->priv->auto_scroll = TRUE;
3985 gtk_combo_box_list_select_func (GtkTreeSelection *selection,
3986 GtkTreeModel *model,
3988 gboolean path_currently_selected,
3992 gboolean sensitive = FALSE;
3994 for (list = selection->tree_view->priv->columns; list && !sensitive; list = list->next)
3996 GList *cells, *cell;
3997 gboolean cell_sensitive, cell_visible;
3999 GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN (list->data);
4001 if (!column->visible)
4004 gtk_tree_model_get_iter (model, &iter, path);
4005 gtk_tree_view_column_cell_set_cell_data (column, model, &iter,
4008 cell = cells = gtk_tree_view_column_get_cell_renderers (column);
4011 g_object_get (cell->data,
4012 "sensitive", &cell_sensitive,
4013 "visible", &cell_visible,
4016 if (cell_visible && cell_sensitive)
4021 g_list_free (cells);
4023 sensitive = cell_sensitive;
4030 gtk_combo_box_list_row_changed (GtkTreeModel *model,
4035 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
4036 GtkComboBoxPrivate *priv = combo_box->priv;
4039 width = gtk_combo_box_calc_requested_width (combo_box, path);
4041 if (width > priv->width)
4043 if (priv->cell_view)
4045 gtk_widget_set_size_request (priv->cell_view, width, -1);
4046 gtk_widget_queue_resize (priv->cell_view);
4048 priv->width = width;
4053 * GtkCellLayout implementation
4057 pack_start_recurse (GtkWidget *menu,
4058 GtkCellRenderer *cell,
4064 list = gtk_container_get_children (GTK_CONTAINER (menu));
4065 for (i = list; i; i = i->next)
4067 if (GTK_IS_CELL_LAYOUT (GTK_BIN (i->data)->child))
4068 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (GTK_BIN (i->data)->child),
4071 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4072 if (submenu != NULL)
4073 pack_start_recurse (submenu, cell, expand);
4080 gtk_combo_box_cell_layout_pack_start (GtkCellLayout *layout,
4081 GtkCellRenderer *cell,
4084 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4085 ComboCellInfo *info;
4086 GtkComboBoxPrivate *priv;
4088 priv = combo_box->priv;
4090 g_object_ref_sink (cell);
4092 info = g_new0 (ComboCellInfo, 1);
4094 info->expand = expand;
4095 info->pack = GTK_PACK_START;
4097 priv->cells = g_slist_append (priv->cells, info);
4099 if (priv->cell_view)
4100 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->cell_view),
4104 gtk_tree_view_column_pack_start (priv->column, cell, expand);
4106 if (GTK_IS_MENU (priv->popup_widget))
4107 pack_start_recurse (priv->popup_widget, cell, expand);
4111 pack_end_recurse (GtkWidget *menu,
4112 GtkCellRenderer *cell,
4118 list = gtk_container_get_children (GTK_CONTAINER (menu));
4119 for (i = list; i; i = i->next)
4121 if (GTK_IS_CELL_LAYOUT (GTK_BIN (i->data)->child))
4122 gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (GTK_BIN (i->data)->child),
4125 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4126 if (submenu != NULL)
4127 pack_end_recurse (submenu, cell, expand);
4134 gtk_combo_box_cell_layout_pack_end (GtkCellLayout *layout,
4135 GtkCellRenderer *cell,
4138 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4139 ComboCellInfo *info;
4140 GtkComboBoxPrivate *priv;
4142 priv = combo_box->priv;
4144 g_object_ref_sink (cell);
4146 info = g_new0 (ComboCellInfo, 1);
4148 info->expand = expand;
4149 info->pack = GTK_PACK_END;
4151 priv->cells = g_slist_append (priv->cells, info);
4153 if (priv->cell_view)
4154 gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (priv->cell_view),
4158 gtk_tree_view_column_pack_end (priv->column, cell, expand);
4160 if (GTK_IS_MENU (priv->popup_widget))
4161 pack_end_recurse (priv->popup_widget, cell, expand);
4165 gtk_combo_box_cell_layout_get_cells (GtkCellLayout *layout)
4167 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4169 GList *retval = NULL;
4171 for (list = combo_box->priv->cells; list; list = list->next)
4173 ComboCellInfo *info = (ComboCellInfo *)list->data;
4175 retval = g_list_prepend (retval, info->cell);
4178 return g_list_reverse (retval);
4182 clear_recurse (GtkWidget *menu)
4187 list = gtk_container_get_children (GTK_CONTAINER (menu));
4188 for (i = list; i; i = i->next)
4190 if (GTK_IS_CELL_LAYOUT (GTK_BIN (i->data)->child))
4191 gtk_cell_layout_clear (GTK_CELL_LAYOUT (GTK_BIN (i->data)->child));
4193 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4194 if (submenu != NULL)
4195 clear_recurse (submenu);
4202 gtk_combo_box_cell_layout_clear (GtkCellLayout *layout)
4204 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4205 GtkComboBoxPrivate *priv = combo_box->priv;
4208 if (priv->cell_view)
4209 gtk_cell_layout_clear (GTK_CELL_LAYOUT (priv->cell_view));
4212 gtk_tree_view_column_clear (priv->column);
4214 for (i = priv->cells; i; i = i->next)
4216 ComboCellInfo *info = (ComboCellInfo *)i->data;
4218 gtk_combo_box_cell_layout_clear_attributes (layout, info->cell);
4219 g_object_unref (info->cell);
4223 g_slist_free (priv->cells);
4226 if (GTK_IS_MENU (priv->popup_widget))
4227 clear_recurse (priv->popup_widget);
4231 add_attribute_recurse (GtkWidget *menu,
4232 GtkCellRenderer *cell,
4233 const gchar *attribute,
4239 list = gtk_container_get_children (GTK_CONTAINER (menu));
4240 for (i = list; i; i = i->next)
4242 if (GTK_IS_CELL_LAYOUT (GTK_BIN (i->data)->child))
4243 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (GTK_BIN (i->data)->child),
4244 cell, attribute, column);
4246 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4247 if (submenu != NULL)
4248 add_attribute_recurse (submenu, cell, attribute, column);
4255 gtk_combo_box_cell_layout_add_attribute (GtkCellLayout *layout,
4256 GtkCellRenderer *cell,
4257 const gchar *attribute,
4260 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4261 ComboCellInfo *info;
4263 info = gtk_combo_box_get_cell_info (combo_box, cell);
4264 g_return_if_fail (info != NULL);
4266 info->attributes = g_slist_prepend (info->attributes,
4267 GINT_TO_POINTER (column));
4268 info->attributes = g_slist_prepend (info->attributes,
4269 g_strdup (attribute));
4271 if (combo_box->priv->cell_view)
4272 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box->priv->cell_view),
4273 cell, attribute, column);
4275 if (combo_box->priv->column)
4276 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box->priv->column),
4277 cell, attribute, column);
4279 if (GTK_IS_MENU (combo_box->priv->popup_widget))
4280 add_attribute_recurse (combo_box->priv->popup_widget, cell, attribute, column);
4281 gtk_widget_queue_resize (GTK_WIDGET (combo_box));
4285 combo_cell_data_func (GtkCellLayout *cell_layout,
4286 GtkCellRenderer *cell,
4287 GtkTreeModel *tree_model,
4291 ComboCellInfo *info = (ComboCellInfo *)data;
4292 GtkWidget *parent = NULL;
4297 (*info->func) (cell_layout, cell, tree_model, iter, info->func_data);
4299 if (GTK_IS_WIDGET (cell_layout))
4300 parent = gtk_widget_get_parent (GTK_WIDGET (cell_layout));
4302 if (GTK_IS_MENU_ITEM (parent) &&
4303 gtk_menu_item_get_submenu (GTK_MENU_ITEM (parent)))
4304 g_object_set (cell, "sensitive", TRUE, NULL);
4309 set_cell_data_func_recurse (GtkWidget *menu,
4310 GtkCellRenderer *cell,
4311 ComboCellInfo *info)
4315 GtkWidget *cell_view;
4317 list = gtk_container_get_children (GTK_CONTAINER (menu));
4318 for (i = list; i; i = i->next)
4320 cell_view = GTK_BIN (i->data)->child;
4321 if (GTK_IS_CELL_LAYOUT (cell_view))
4323 /* Override sensitivity for inner nodes; we don't
4324 * want menuitems with submenus to appear insensitive
4326 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (cell_view),
4328 combo_cell_data_func,
4330 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4331 if (submenu != NULL)
4332 set_cell_data_func_recurse (submenu, cell, info);
4340 gtk_combo_box_cell_layout_set_cell_data_func (GtkCellLayout *layout,
4341 GtkCellRenderer *cell,
4342 GtkCellLayoutDataFunc func,
4344 GDestroyNotify destroy)
4346 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4347 GtkComboBoxPrivate *priv = combo_box->priv;
4348 ComboCellInfo *info;
4350 info = gtk_combo_box_get_cell_info (combo_box, cell);
4351 g_return_if_fail (info != NULL);
4355 GDestroyNotify d = info->destroy;
4357 info->destroy = NULL;
4358 d (info->func_data);
4362 info->func_data = func_data;
4363 info->destroy = destroy;
4365 if (priv->cell_view)
4366 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (priv->cell_view), cell, func, func_data, NULL);
4369 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (priv->column), cell, func, func_data, NULL);
4371 if (GTK_IS_MENU (priv->popup_widget))
4372 set_cell_data_func_recurse (priv->popup_widget, cell, info);
4374 gtk_widget_queue_resize (GTK_WIDGET (combo_box));
4378 clear_attributes_recurse (GtkWidget *menu,
4379 GtkCellRenderer *cell)
4384 list = gtk_container_get_children (GTK_CONTAINER (menu));
4385 for (i = list; i; i = i->next)
4387 if (GTK_IS_CELL_LAYOUT (GTK_BIN (i->data)->child))
4388 gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (GTK_BIN (i->data)->child),
4391 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4392 if (submenu != NULL)
4393 clear_attributes_recurse (submenu, cell);
4400 gtk_combo_box_cell_layout_clear_attributes (GtkCellLayout *layout,
4401 GtkCellRenderer *cell)
4403 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4404 GtkComboBoxPrivate *priv;
4405 ComboCellInfo *info;
4408 priv = combo_box->priv;
4410 info = gtk_combo_box_get_cell_info (combo_box, cell);
4411 g_return_if_fail (info != NULL);
4413 list = info->attributes;
4414 while (list && list->next)
4416 g_free (list->data);
4417 list = list->next->next;
4419 g_slist_free (info->attributes);
4420 info->attributes = NULL;
4422 if (priv->cell_view)
4423 gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (priv->cell_view), cell);
4426 gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (priv->column), cell);
4428 if (GTK_IS_MENU (priv->popup_widget))
4429 clear_attributes_recurse (priv->popup_widget, cell);
4431 gtk_widget_queue_resize (GTK_WIDGET (combo_box));
4435 reorder_recurse (GtkWidget *menu,
4436 GtkCellRenderer *cell,
4442 list = gtk_container_get_children (GTK_CONTAINER (menu));
4443 for (i = list; i; i = i->next)
4445 if (GTK_IS_CELL_LAYOUT (GTK_BIN (i->data)->child))
4446 gtk_cell_layout_reorder (GTK_CELL_LAYOUT (GTK_BIN (i->data)->child),
4449 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4450 if (submenu != NULL)
4451 reorder_recurse (submenu, cell, position);
4458 gtk_combo_box_cell_layout_reorder (GtkCellLayout *layout,
4459 GtkCellRenderer *cell,
4462 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
4463 GtkComboBoxPrivate *priv;
4464 ComboCellInfo *info;
4467 priv = combo_box->priv;
4469 info = gtk_combo_box_get_cell_info (combo_box, cell);
4471 g_return_if_fail (info != NULL);
4472 g_return_if_fail (position >= 0);
4474 link = g_slist_find (priv->cells, info);
4476 g_return_if_fail (link != NULL);
4478 priv->cells = g_slist_delete_link (priv->cells, link);
4479 priv->cells = g_slist_insert (priv->cells, info, position);
4481 if (priv->cell_view)
4482 gtk_cell_layout_reorder (GTK_CELL_LAYOUT (priv->cell_view),
4486 gtk_cell_layout_reorder (GTK_CELL_LAYOUT (priv->column),
4489 if (GTK_IS_MENU (priv->popup_widget))
4490 reorder_recurse (priv->popup_widget, cell, position);
4492 gtk_widget_queue_draw (GTK_WIDGET (combo_box));
4500 * gtk_combo_box_new:
4502 * Creates a new empty #GtkComboBox.
4504 * Return value: A new #GtkComboBox.
4509 gtk_combo_box_new (void)
4511 return g_object_new (GTK_TYPE_COMBO_BOX, NULL);
4515 * gtk_combo_box_new_with_model:
4516 * @model: A #GtkTreeModel.
4518 * Creates a new #GtkComboBox with the model initialized to @model.
4520 * Return value: A new #GtkComboBox.
4525 gtk_combo_box_new_with_model (GtkTreeModel *model)
4527 GtkComboBox *combo_box;
4529 g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL);
4531 combo_box = g_object_new (GTK_TYPE_COMBO_BOX, "model", model, NULL);
4533 return GTK_WIDGET (combo_box);
4537 * gtk_combo_box_get_wrap_width:
4538 * @combo_box: A #GtkComboBox
4540 * Returns the wrap width which is used to determine the number of columns
4541 * for the popup menu. If the wrap width is larger than 1, the combo box
4544 * Returns: the wrap width.
4549 gtk_combo_box_get_wrap_width (GtkComboBox *combo_box)
4551 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), -1);
4553 return combo_box->priv->wrap_width;
4557 * gtk_combo_box_set_wrap_width:
4558 * @combo_box: A #GtkComboBox
4559 * @width: Preferred number of columns
4561 * Sets the wrap width of @combo_box to be @width. The wrap width is basically
4562 * the preferred number of columns when you want the popup to be layed out
4568 gtk_combo_box_set_wrap_width (GtkComboBox *combo_box,
4571 GtkComboBoxPrivate *priv;
4573 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4574 g_return_if_fail (width >= 0);
4576 priv = combo_box->priv;
4578 if (width != priv->wrap_width)
4580 priv->wrap_width = width;
4582 gtk_combo_box_check_appearance (combo_box);
4583 gtk_combo_box_relayout (combo_box);
4585 g_object_notify (G_OBJECT (combo_box), "wrap-width");
4590 * gtk_combo_box_get_row_span_column:
4591 * @combo_box: A #GtkComboBox
4593 * Returns the column with row span information for @combo_box.
4595 * Returns: the row span column.
4600 gtk_combo_box_get_row_span_column (GtkComboBox *combo_box)
4602 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), -1);
4604 return combo_box->priv->row_column;
4608 * gtk_combo_box_set_row_span_column:
4609 * @combo_box: A #GtkComboBox.
4610 * @row_span: A column in the model passed during construction.
4612 * Sets the column with row span information for @combo_box to be @row_span.
4613 * The row span column contains integers which indicate how many rows
4614 * an item should span.
4619 gtk_combo_box_set_row_span_column (GtkComboBox *combo_box,
4622 GtkComboBoxPrivate *priv;
4625 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4627 priv = combo_box->priv;
4629 col = gtk_tree_model_get_n_columns (priv->model);
4630 g_return_if_fail (row_span >= -1 && row_span < col);
4632 if (row_span != priv->row_column)
4634 priv->row_column = row_span;
4636 gtk_combo_box_relayout (combo_box);
4638 g_object_notify (G_OBJECT (combo_box), "row-span-column");
4643 * gtk_combo_box_get_column_span_column:
4644 * @combo_box: A #GtkComboBox
4646 * Returns the column with column span information for @combo_box.
4648 * Returns: the column span column.
4653 gtk_combo_box_get_column_span_column (GtkComboBox *combo_box)
4655 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), -1);
4657 return combo_box->priv->col_column;
4661 * gtk_combo_box_set_column_span_column:
4662 * @combo_box: A #GtkComboBox
4663 * @column_span: A column in the model passed during construction
4665 * Sets the column with column span information for @combo_box to be
4666 * @column_span. The column span column contains integers which indicate
4667 * how many columns an item should span.
4672 gtk_combo_box_set_column_span_column (GtkComboBox *combo_box,
4675 GtkComboBoxPrivate *priv;
4678 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4680 priv = combo_box->priv;
4682 col = gtk_tree_model_get_n_columns (priv->model);
4683 g_return_if_fail (column_span >= -1 && column_span < col);
4685 if (column_span != priv->col_column)
4687 priv->col_column = column_span;
4689 gtk_combo_box_relayout (combo_box);
4691 g_object_notify (G_OBJECT (combo_box), "column-span-column");
4696 * gtk_combo_box_get_active:
4697 * @combo_box: A #GtkComboBox
4699 * Returns the index of the currently active item, or -1 if there's no
4700 * active item. If the model is a non-flat treemodel, and the active item
4701 * is not an immediate child of the root of the tree, this function returns
4702 * <literal>gtk_tree_path_get_indices (path)[0]</literal>, where
4703 * <literal>path</literal> is the #GtkTreePath of the active item.
4705 * Return value: An integer which is the index of the currently active item,
4706 * or -1 if there's no active item.
4711 gtk_combo_box_get_active (GtkComboBox *combo_box)
4713 GtkComboBoxPrivate *priv;
4716 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), 0);
4718 priv = combo_box->priv;
4720 if (gtk_tree_row_reference_valid (priv->active_row))
4724 path = gtk_tree_row_reference_get_path (priv->active_row);
4725 result = gtk_tree_path_get_indices (path)[0];
4726 gtk_tree_path_free (path);
4735 * gtk_combo_box_set_active:
4736 * @combo_box: A #GtkComboBox
4737 * @index_: An index in the model passed during construction, or -1 to have
4740 * Sets the active item of @combo_box to be the item at @index.
4745 gtk_combo_box_set_active (GtkComboBox *combo_box,
4748 GtkTreePath *path = NULL;
4749 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4750 g_return_if_fail (index_ >= -1);
4753 path = gtk_tree_path_new_from_indices (index_, -1);
4755 gtk_combo_box_set_active_internal (combo_box, path);
4758 gtk_tree_path_free (path);
4762 gtk_combo_box_set_active_internal (GtkComboBox *combo_box,
4765 GtkComboBoxPrivate *priv = combo_box->priv;
4766 GtkTreePath *active_path;
4769 if (path && gtk_tree_row_reference_valid (priv->active_row))
4771 active_path = gtk_tree_row_reference_get_path (priv->active_row);
4772 path_cmp = gtk_tree_path_compare (path, active_path);
4773 gtk_tree_path_free (active_path);
4778 if (priv->active_row)
4780 gtk_tree_row_reference_free (priv->active_row);
4781 priv->active_row = NULL;
4786 if (priv->tree_view)
4787 gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view)));
4790 GtkMenu *menu = GTK_MENU (priv->popup_widget);
4792 if (GTK_IS_MENU (menu))
4793 gtk_menu_set_active (menu, -1);
4796 if (priv->cell_view)
4797 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (priv->cell_view), NULL);
4802 gtk_tree_row_reference_new (priv->model, path);
4804 if (priv->tree_view)
4806 gtk_tree_view_set_cursor (GTK_TREE_VIEW (priv->tree_view),
4809 else if (GTK_IS_MENU (priv->popup_widget))
4811 /* FIXME handle nested menus better */
4812 gtk_menu_set_active (GTK_MENU (priv->popup_widget),
4813 gtk_tree_path_get_indices (path)[0]);
4816 if (priv->cell_view)
4817 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (priv->cell_view),
4821 g_signal_emit (combo_box, combo_box_signals[CHANGED], 0);
4822 g_object_notify (G_OBJECT (combo_box), "active");
4827 * gtk_combo_box_get_active_iter:
4828 * @combo_box: A #GtkComboBox
4829 * @iter: The uninitialized #GtkTreeIter
4831 * Sets @iter to point to the current active item, if it exists.
4833 * Return value: %TRUE, if @iter was set
4838 gtk_combo_box_get_active_iter (GtkComboBox *combo_box,
4844 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
4846 if (!gtk_tree_row_reference_valid (combo_box->priv->active_row))
4849 path = gtk_tree_row_reference_get_path (combo_box->priv->active_row);
4850 result = gtk_tree_model_get_iter (combo_box->priv->model, iter, path);
4851 gtk_tree_path_free (path);
4857 * gtk_combo_box_set_active_iter:
4858 * @combo_box: A #GtkComboBox
4859 * @iter: The #GtkTreeIter
4861 * Sets the current active item to be the one referenced by @iter.
4862 * @iter must correspond to a path of depth one.
4867 gtk_combo_box_set_active_iter (GtkComboBox *combo_box,
4872 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4874 path = gtk_tree_model_get_path (gtk_combo_box_get_model (combo_box), iter);
4875 gtk_combo_box_set_active_internal (combo_box, path);
4876 gtk_tree_path_free (path);
4880 * gtk_combo_box_set_model:
4881 * @combo_box: A #GtkComboBox
4882 * @model: A #GtkTreeModel
4884 * Sets the model used by @combo_box to be @model. Will unset a previously set
4885 * model (if applicable). If model is %NULL, then it will unset the model.
4887 * Note that this function does not clear the cell renderers, you have to
4888 * call gtk_cell_layout_clear() yourself if you need to set up different
4889 * cell renderers for the new model.
4894 gtk_combo_box_set_model (GtkComboBox *combo_box,
4895 GtkTreeModel *model)
4897 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4898 g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
4900 if (model == combo_box->priv->model)
4903 gtk_combo_box_unset_model (combo_box);
4908 combo_box->priv->model = model;
4909 g_object_ref (combo_box->priv->model);
4911 combo_box->priv->inserted_id =
4912 g_signal_connect (combo_box->priv->model, "row_inserted",
4913 G_CALLBACK (gtk_combo_box_model_row_inserted),
4915 combo_box->priv->deleted_id =
4916 g_signal_connect (combo_box->priv->model, "row_deleted",
4917 G_CALLBACK (gtk_combo_box_model_row_deleted),
4919 combo_box->priv->reordered_id =
4920 g_signal_connect (combo_box->priv->model, "rows_reordered",
4921 G_CALLBACK (gtk_combo_box_model_rows_reordered),
4923 combo_box->priv->changed_id =
4924 g_signal_connect (combo_box->priv->model, "row_changed",
4925 G_CALLBACK (gtk_combo_box_model_row_changed),
4928 if (combo_box->priv->tree_view)
4931 gtk_tree_view_set_model (GTK_TREE_VIEW (combo_box->priv->tree_view),
4932 combo_box->priv->model);
4933 gtk_combo_box_list_popup_resize (combo_box);
4938 if (combo_box->priv->popup_widget)
4939 gtk_combo_box_menu_fill (combo_box);
4943 if (combo_box->priv->cell_view)
4944 gtk_cell_view_set_model (GTK_CELL_VIEW (combo_box->priv->cell_view),
4945 combo_box->priv->model);
4948 g_object_notify (G_OBJECT (combo_box), "model");
4952 * gtk_combo_box_get_model
4953 * @combo_box: A #GtkComboBox
4955 * Returns the #GtkTreeModel which is acting as data source for @combo_box.
4957 * Return value: A #GtkTreeModel which was passed during construction.
4962 gtk_combo_box_get_model (GtkComboBox *combo_box)
4964 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
4966 return combo_box->priv->model;
4970 /* convenience API for simple text combos */
4973 * gtk_combo_box_new_text:
4975 * Convenience function which constructs a new text combo box, which is a
4976 * #GtkComboBox just displaying strings. If you use this function to create
4977 * a text combo box, you should only manipulate its data source with the
4978 * following convenience functions: gtk_combo_box_append_text(),
4979 * gtk_combo_box_insert_text(), gtk_combo_box_prepend_text() and
4980 * gtk_combo_box_remove_text().
4982 * Return value: A new text combo box.
4987 gtk_combo_box_new_text (void)
4989 GtkWidget *combo_box;
4990 GtkCellRenderer *cell;
4991 GtkListStore *store;
4993 store = gtk_list_store_new (1, G_TYPE_STRING);
4994 combo_box = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store));
4995 g_object_unref (store);
4997 cell = gtk_cell_renderer_text_new ();
4998 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), cell, TRUE);
4999 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), cell,
5007 * gtk_combo_box_append_text:
5008 * @combo_box: A #GtkComboBox constructed using gtk_combo_box_new_text()
5011 * Appends @string to the list of strings stored in @combo_box. Note that
5012 * you can only use this function with combo boxes constructed with
5013 * gtk_combo_box_new_text().
5018 gtk_combo_box_append_text (GtkComboBox *combo_box,
5022 GtkListStore *store;
5024 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5025 g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model));
5026 g_return_if_fail (gtk_tree_model_get_column_type (combo_box->priv->model, 0)
5028 g_return_if_fail (text != NULL);
5030 store = GTK_LIST_STORE (combo_box->priv->model);
5032 gtk_list_store_append (store, &iter);
5033 gtk_list_store_set (store, &iter, 0, text, -1);
5037 * gtk_combo_box_insert_text:
5038 * @combo_box: A #GtkComboBox constructed using gtk_combo_box_new_text()
5039 * @position: An index to insert @text
5042 * Inserts @string at @position in the list of strings stored in @combo_box.
5043 * Note that you can only use this function with combo boxes constructed
5044 * with gtk_combo_box_new_text().
5049 gtk_combo_box_insert_text (GtkComboBox *combo_box,
5054 GtkListStore *store;
5056 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5057 g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model));
5058 g_return_if_fail (position >= 0);
5059 g_return_if_fail (gtk_tree_model_get_column_type (combo_box->priv->model, 0)
5061 g_return_if_fail (text != NULL);
5063 store = GTK_LIST_STORE (combo_box->priv->model);
5065 gtk_list_store_insert (store, &iter, position);
5066 gtk_list_store_set (store, &iter, 0, text, -1);
5070 * gtk_combo_box_prepend_text:
5071 * @combo_box: A #GtkComboBox constructed with gtk_combo_box_new_text()
5074 * Prepends @string to the list of strings stored in @combo_box. Note that
5075 * you can only use this function with combo boxes constructed with
5076 * gtk_combo_box_new_text().
5081 gtk_combo_box_prepend_text (GtkComboBox *combo_box,
5085 GtkListStore *store;
5087 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5088 g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model));
5089 g_return_if_fail (gtk_tree_model_get_column_type (combo_box->priv->model, 0)
5091 g_return_if_fail (text != NULL);
5093 store = GTK_LIST_STORE (combo_box->priv->model);
5095 gtk_list_store_prepend (store, &iter);
5096 gtk_list_store_set (store, &iter, 0, text, -1);
5100 * gtk_combo_box_remove_text:
5101 * @combo_box: A #GtkComboBox constructed with gtk_combo_box_new_text()
5102 * @position: Index of the item to remove
5104 * Removes the string at @position from @combo_box. Note that you can only use
5105 * this function with combo boxes constructed with gtk_combo_box_new_text().
5110 gtk_combo_box_remove_text (GtkComboBox *combo_box,
5114 GtkListStore *store;
5116 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5117 g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model));
5118 g_return_if_fail (gtk_tree_model_get_column_type (combo_box->priv->model, 0)
5120 g_return_if_fail (position >= 0);
5122 store = GTK_LIST_STORE (combo_box->priv->model);
5124 if (gtk_tree_model_iter_nth_child (combo_box->priv->model, &iter,
5126 gtk_list_store_remove (store, &iter);
5130 * gtk_combo_box_get_active_text:
5131 * @combo_box: A #GtkComboBox constructed with gtk_combo_box_new_text()
5133 * Returns the currently active string in @combo_box or %NULL if none
5134 * is selected. Note that you can only use this function with combo
5135 * boxes constructed with gtk_combo_box_new_text() and with
5136 * #GtkComboBoxEntry<!-- -->s.
5138 * Returns: a newly allocated string containing the currently active text.
5143 gtk_combo_box_get_active_text (GtkComboBox *combo_box)
5145 GtkComboBoxClass *class;
5147 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
5149 class = GTK_COMBO_BOX_GET_CLASS (combo_box);
5151 if (class->get_active_text)
5152 return (* class->get_active_text) (combo_box);
5158 gtk_combo_box_real_get_active_text (GtkComboBox *combo_box)
5163 g_return_val_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model), NULL);
5164 g_return_val_if_fail (gtk_tree_model_get_column_type (combo_box->priv->model, 0)
5165 == G_TYPE_STRING, NULL);
5167 if (gtk_combo_box_get_active_iter (combo_box, &iter))
5168 gtk_tree_model_get (combo_box->priv->model, &iter,
5175 gtk_combo_box_real_move_active (GtkComboBox *combo_box,
5176 GtkScrollType scroll)
5179 GtkTreeIter new_iter;
5180 gboolean active_iter;
5183 if (!combo_box->priv->model)
5185 gtk_widget_error_bell (GTK_WIDGET (combo_box));
5189 active_iter = gtk_combo_box_get_active_iter (combo_box, &iter);
5193 case GTK_SCROLL_STEP_BACKWARD:
5194 case GTK_SCROLL_STEP_UP:
5195 case GTK_SCROLL_STEP_LEFT:
5198 found = tree_prev (combo_box, combo_box->priv->model,
5199 &iter, &new_iter, FALSE);
5202 /* else fall through */
5204 case GTK_SCROLL_PAGE_FORWARD:
5205 case GTK_SCROLL_PAGE_DOWN:
5206 case GTK_SCROLL_PAGE_RIGHT:
5207 case GTK_SCROLL_END:
5208 found = tree_last (combo_box, combo_box->priv->model, &new_iter, FALSE);
5211 case GTK_SCROLL_STEP_FORWARD:
5212 case GTK_SCROLL_STEP_DOWN:
5213 case GTK_SCROLL_STEP_RIGHT:
5216 found = tree_next (combo_box, combo_box->priv->model,
5217 &iter, &new_iter, FALSE);
5220 /* else fall through */
5222 case GTK_SCROLL_PAGE_BACKWARD:
5223 case GTK_SCROLL_PAGE_UP:
5224 case GTK_SCROLL_PAGE_LEFT:
5225 case GTK_SCROLL_START:
5226 found = tree_first (combo_box, combo_box->priv->model, &new_iter, FALSE);
5233 if (found && active_iter)
5235 GtkTreePath *old_path;
5236 GtkTreePath *new_path;
5238 old_path = gtk_tree_model_get_path (combo_box->priv->model, &iter);
5239 new_path = gtk_tree_model_get_path (combo_box->priv->model, &new_iter);
5241 if (gtk_tree_path_compare (old_path, new_path) == 0)
5244 gtk_tree_path_free (old_path);
5245 gtk_tree_path_free (new_path);
5250 gtk_combo_box_set_active_iter (combo_box, &new_iter);
5254 gtk_widget_error_bell (GTK_WIDGET (combo_box));
5259 gtk_combo_box_mnemonic_activate (GtkWidget *widget,
5260 gboolean group_cycling)
5262 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
5264 gtk_widget_grab_focus (combo_box->priv->button);
5270 gtk_combo_box_grab_focus (GtkWidget *widget)
5272 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
5274 gtk_widget_grab_focus (combo_box->priv->button);
5278 gtk_combo_box_destroy (GtkObject *object)
5280 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
5282 if (combo_box->priv->popup_idle_id > 0)
5284 g_source_remove (combo_box->priv->popup_idle_id);
5285 combo_box->priv->popup_idle_id = 0;
5288 gtk_combo_box_popdown (combo_box);
5290 if (combo_box->priv->row_separator_destroy)
5291 (* combo_box->priv->row_separator_destroy) (combo_box->priv->row_separator_data);
5293 combo_box->priv->row_separator_func = NULL;
5294 combo_box->priv->row_separator_data = NULL;
5295 combo_box->priv->row_separator_destroy = NULL;
5297 GTK_OBJECT_CLASS (gtk_combo_box_parent_class)->destroy (object);
5298 combo_box->priv->cell_view = NULL;
5302 gtk_combo_box_dispose(GObject* object)
5304 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
5306 if (GTK_IS_MENU (combo_box->priv->popup_widget))
5308 gtk_combo_box_menu_destroy (combo_box);
5309 gtk_menu_detach (GTK_MENU (combo_box->priv->popup_widget));
5310 combo_box->priv->popup_widget = NULL;
5313 G_OBJECT_CLASS (gtk_combo_box_parent_class)->dispose (object);
5317 gtk_combo_box_finalize (GObject *object)
5319 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
5322 if (GTK_IS_TREE_VIEW (combo_box->priv->tree_view))
5323 gtk_combo_box_list_destroy (combo_box);
5325 if (combo_box->priv->popup_window)
5326 gtk_widget_destroy (combo_box->priv->popup_window);
5328 gtk_combo_box_unset_model (combo_box);
5330 for (i = combo_box->priv->cells; i; i = i->next)
5332 ComboCellInfo *info = (ComboCellInfo *)i->data;
5333 GSList *list = info->attributes;
5336 info->destroy (info->func_data);
5338 while (list && list->next)
5340 g_free (list->data);
5341 list = list->next->next;
5343 g_slist_free (info->attributes);
5345 g_object_unref (info->cell);
5348 g_slist_free (combo_box->priv->cells);
5350 g_free (combo_box->priv->tearoff_title);
5352 G_OBJECT_CLASS (gtk_combo_box_parent_class)->finalize (object);
5356 gtk_cell_editable_key_press (GtkWidget *widget,
5360 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
5362 if (event->keyval == GDK_Escape)
5364 combo_box->priv->editing_canceled = TRUE;
5366 gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (combo_box));
5367 gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (combo_box));
5371 else if (event->keyval == GDK_Return ||
5372 event->keyval == GDK_ISO_Enter ||
5373 event->keyval == GDK_KP_Enter)
5375 gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (combo_box));
5376 gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (combo_box));
5385 popdown_idle (gpointer data)
5387 GtkComboBox *combo_box;
5389 combo_box = GTK_COMBO_BOX (data);
5391 gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (combo_box));
5392 gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (combo_box));
5394 g_object_unref (combo_box);
5400 popdown_handler (GtkWidget *widget,
5403 gdk_threads_add_idle (popdown_idle, g_object_ref (data));
5407 popup_idle (gpointer data)
5409 GtkComboBox *combo_box;
5411 combo_box = GTK_COMBO_BOX (data);
5413 if (GTK_IS_MENU (combo_box->priv->popup_widget) &&
5414 combo_box->priv->cell_view)
5415 g_signal_connect_object (combo_box->priv->popup_widget,
5416 "unmap", G_CALLBACK (popdown_handler),
5419 /* we unset this if a menu item is activated */
5420 combo_box->priv->editing_canceled = TRUE;
5421 gtk_combo_box_popup (combo_box);
5423 combo_box->priv->popup_idle_id = 0;
5424 combo_box->priv->activate_button = 0;
5425 combo_box->priv->activate_time = 0;
5431 gtk_combo_box_start_editing (GtkCellEditable *cell_editable,
5434 GtkComboBox *combo_box = GTK_COMBO_BOX (cell_editable);
5436 combo_box->priv->is_cell_renderer = TRUE;
5438 if (combo_box->priv->cell_view)
5440 g_signal_connect_object (combo_box->priv->button, "key_press_event",
5441 G_CALLBACK (gtk_cell_editable_key_press),
5444 gtk_widget_grab_focus (combo_box->priv->button);
5448 g_signal_connect_object (GTK_BIN (combo_box)->child, "key_press_event",
5449 G_CALLBACK (gtk_cell_editable_key_press),
5452 gtk_widget_grab_focus (GTK_WIDGET (GTK_BIN (combo_box)->child));
5453 GTK_WIDGET_UNSET_FLAGS (combo_box->priv->button, GTK_CAN_FOCUS);
5456 /* we do the immediate popup only for the optionmenu-like
5459 if (combo_box->priv->is_cell_renderer &&
5460 combo_box->priv->cell_view && !combo_box->priv->tree_view)
5462 if (event && event->type == GDK_BUTTON_PRESS)
5464 GdkEventButton *event_button = (GdkEventButton *)event;
5466 combo_box->priv->activate_button = event_button->button;
5467 combo_box->priv->activate_time = event_button->time;
5470 combo_box->priv->popup_idle_id =
5471 gdk_threads_add_idle (popup_idle, combo_box);
5477 * gtk_combo_box_get_add_tearoffs:
5478 * @combo_box: a #GtkComboBox
5480 * Gets the current value of the :add-tearoffs property.
5482 * Return value: the current value of the :add-tearoffs property.
5485 gtk_combo_box_get_add_tearoffs (GtkComboBox *combo_box)
5487 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
5489 return combo_box->priv->add_tearoffs;
5493 * gtk_combo_box_set_add_tearoffs:
5494 * @combo_box: a #GtkComboBox
5495 * @add_tearoffs: %TRUE to add tearoff menu items
5497 * Sets whether the popup menu should have a tearoff
5503 gtk_combo_box_set_add_tearoffs (GtkComboBox *combo_box,
5504 gboolean add_tearoffs)
5506 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5508 add_tearoffs = add_tearoffs != FALSE;
5510 if (combo_box->priv->add_tearoffs != add_tearoffs)
5512 combo_box->priv->add_tearoffs = add_tearoffs;
5513 gtk_combo_box_check_appearance (combo_box);
5514 gtk_combo_box_relayout (combo_box);
5515 g_object_notify (G_OBJECT (combo_box), "add-tearoffs");
5520 * gtk_combo_box_get_title:
5521 * @combo_box: a #GtkComboBox
5523 * Gets the current title of the menu in tearoff mode. See
5524 * gtk_combo_box_set_add_tearoffs().
5526 * Returns: the menu's title in tearoff mode. This is an internal copy of the
5527 * string which must not be freed.
5531 G_CONST_RETURN gchar*
5532 gtk_combo_box_get_title (GtkComboBox *combo_box)
5534 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
5536 return combo_box->priv->tearoff_title;
5540 gtk_combo_box_update_title (GtkComboBox *combo_box)
5542 gtk_combo_box_check_appearance (combo_box);
5544 if (combo_box->priv->popup_widget &&
5545 GTK_IS_MENU (combo_box->priv->popup_widget))
5546 gtk_menu_set_title (GTK_MENU (combo_box->priv->popup_widget),
5547 combo_box->priv->tearoff_title);
5551 * gtk_combo_box_set_title:
5552 * @combo_box: a #GtkComboBox
5553 * @title: a title for the menu in tearoff mode
5555 * Sets the menu's title in tearoff mode.
5560 gtk_combo_box_set_title (GtkComboBox *combo_box,
5563 GtkComboBoxPrivate *priv;
5565 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5567 priv = combo_box->priv;
5569 if (strcmp (title ? title : "",
5570 priv->tearoff_title ? priv->tearoff_title : "") != 0)
5572 g_free (priv->tearoff_title);
5573 priv->tearoff_title = g_strdup (title);
5575 gtk_combo_box_update_title (combo_box);
5577 g_object_notify (G_OBJECT (combo_box), "tearoff-title");
5582 _gtk_combo_box_editing_canceled (GtkComboBox *combo_box)
5584 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), TRUE);
5586 return combo_box->priv->editing_canceled;
5590 * gtk_combo_box_get_popup_accessible:
5591 * @combo_box: a #GtkComboBox
5593 * Gets the accessible object corresponding to the combo box's popup.
5595 * This function is mostly intended for use by accessibility technologies;
5596 * applications should have little use for it.
5598 * Returns: the accessible object corresponding to the combo box's popup.
5603 gtk_combo_box_get_popup_accessible (GtkComboBox *combo_box)
5607 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
5609 if (combo_box->priv->popup_widget)
5611 atk_obj = gtk_widget_get_accessible (combo_box->priv->popup_widget);
5619 * gtk_combo_box_get_row_separator_func:
5620 * @combo_box: a #GtkComboBox
5622 * Returns the current row separator function.
5624 * Return value: the current row separator function.
5628 GtkTreeViewRowSeparatorFunc
5629 gtk_combo_box_get_row_separator_func (GtkComboBox *combo_box)
5631 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
5633 return combo_box->priv->row_separator_func;
5637 * gtk_combo_box_set_row_separator_func:
5638 * @combo_box: a #GtkComboBox
5639 * @func: a #GtkTreeViewRowSeparatorFunc
5640 * @data: user data to pass to @func, or %NULL
5641 * @destroy: destroy notifier for @data, or %NULL
5643 * Sets the row separator function, which is used to determine
5644 * whether a row should be drawn as a separator. If the row separator
5645 * function is %NULL, no separators are drawn. This is the default value.
5650 gtk_combo_box_set_row_separator_func (GtkComboBox *combo_box,
5651 GtkTreeViewRowSeparatorFunc func,
5653 GDestroyNotify destroy)
5655 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5657 if (combo_box->priv->row_separator_destroy)
5658 (* combo_box->priv->row_separator_destroy) (combo_box->priv->row_separator_data);
5660 combo_box->priv->row_separator_func = func;
5661 combo_box->priv->row_separator_data = data;
5662 combo_box->priv->row_separator_destroy = destroy;
5664 if (combo_box->priv->tree_view)
5665 gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (combo_box->priv->tree_view),
5668 gtk_combo_box_relayout (combo_box);
5670 gtk_widget_queue_draw (GTK_WIDGET (combo_box));
5675 * gtk_combo_box_set_focus_on_click:
5676 * @combo: a #GtkComboBox
5677 * @focus_on_click: whether the combo box grabs focus when clicked
5680 * Sets whether the combo box will grab focus when it is clicked with
5681 * the mouse. Making mouse clicks not grab focus is useful in places
5682 * like toolbars where you don't want the keyboard focus removed from
5683 * the main area of the application.
5688 gtk_combo_box_set_focus_on_click (GtkComboBox *combo_box,
5689 gboolean focus_on_click)
5691 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5693 focus_on_click = focus_on_click != FALSE;
5695 if (combo_box->priv->focus_on_click != focus_on_click)
5697 combo_box->priv->focus_on_click = focus_on_click;
5699 if (combo_box->priv->button)
5700 gtk_button_set_focus_on_click (GTK_BUTTON (combo_box->priv->button),
5703 g_object_notify (G_OBJECT (combo_box), "focus-on-click");
5708 * gtk_combo_box_get_focus_on_click:
5709 * @combo: a #GtkComboBox
5711 * Returns whether the combo box grabs focus when it is clicked
5712 * with the mouse. See gtk_combo_box_set_focus_on_click().
5714 * Return value: %TRUE if the combo box grabs focus when it is
5715 * clicked with the mouse.
5720 gtk_combo_box_get_focus_on_click (GtkComboBox *combo_box)
5722 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
5724 return combo_box->priv->focus_on_click;
5729 gtk_combo_box_buildable_custom_tag_start (GtkBuildable *buildable,
5730 GtkBuilder *builder,
5732 const gchar *tagname,
5733 GMarkupParser *parser,
5736 if (parent_buildable_iface->custom_tag_start (buildable, builder, child,
5737 tagname, parser, data))
5740 return _gtk_cell_layout_buildable_custom_tag_start (buildable, builder, child,
5741 tagname, parser, data);
5745 gtk_combo_box_buildable_custom_tag_end (GtkBuildable *buildable,
5746 GtkBuilder *builder,
5748 const gchar *tagname,
5751 if (strcmp (tagname, "attributes") == 0)
5752 _gtk_cell_layout_buildable_custom_tag_end (buildable, builder, child, tagname,
5755 parent_buildable_iface->custom_tag_end (buildable, builder, child, tagname,
5759 #define __GTK_COMBO_BOX_C__
5760 #include "gtkaliasdef.c"