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 *popup_frame;
100 GtkWidget *scrolled_window;
108 guint resize_idle_id;
114 guint popup_in_progress : 1;
115 guint popup_shown : 1;
116 guint add_tearoffs : 1;
118 guint is_cell_renderer : 1;
119 guint editing_canceled : 1;
120 guint auto_scroll : 1;
121 guint focus_on_click : 1;
123 GtkTreeViewRowSeparatorFunc row_separator_func;
124 gpointer row_separator_data;
125 GtkDestroyNotify row_separator_destroy;
127 gchar *tearoff_title;
130 /* While debugging this evil code, I have learned that
131 * there are actually 4 modes to this widget, which can
132 * be characterized as follows
134 * 1) menu mode, no child added
137 * cell_view -> GtkCellView, regular child
138 * cell_view_frame -> NULL
139 * button -> GtkToggleButton set_parent to combo
140 * arrow -> GtkArrow set_parent to button
141 * separator -> GtkVSepator set_parent to button
142 * popup_widget -> GtkMenu
143 * popup_window -> NULL
144 * popup_frame -> 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 * popup_frame -> NULL
158 * scrolled_window -> NULL
160 * 3) list mode, no child added
162 * tree_view -> GtkTreeView, child of popup_frame
163 * cell_view -> GtkCellView, regular child
164 * cell_view_frame -> GtkFrame, set parent to combo
165 * button -> GtkToggleButton, set_parent to combo
166 * arrow -> GtkArrow, child of button
168 * popup_widget -> tree_view
169 * popup_window -> GtkWindow
170 * popup_frame -> GtkFrame, child of popup_window
171 * scrolled_window -> GtkScrolledWindow, child of popup_frame
173 * 4) list mode, child added
175 * tree_view -> GtkTreeView, child of popup_frame
177 * cell_view_frame -> NULL
178 * button -> GtkToggleButton, set_parent to combo
179 * arrow -> GtkArrow, child of button
181 * popup_widget -> tree_view
182 * popup_window -> GtkWindow
183 * popup_frame -> GtkFrame, child of popup_window
184 * scrolled_window -> GtkScrolledWindow, child of popup_frame
199 PROP_ROW_SPAN_COLUMN,
200 PROP_COLUMN_SPAN_COLUMN,
209 static guint combo_box_signals[LAST_SIGNAL] = {0,};
211 #define BONUS_PADDING 4
212 #define SCROLL_TIME 100
216 static void gtk_combo_box_cell_layout_init (GtkCellLayoutIface *iface);
217 static void gtk_combo_box_cell_editable_init (GtkCellEditableIface *iface);
218 static void gtk_combo_box_finalize (GObject *object);
219 static void gtk_combo_box_destroy (GtkObject *object);
221 static void gtk_combo_box_set_property (GObject *object,
225 static void gtk_combo_box_get_property (GObject *object,
230 static void gtk_combo_box_state_changed (GtkWidget *widget,
231 GtkStateType previous);
232 static void gtk_combo_box_grab_focus (GtkWidget *widget);
233 static void gtk_combo_box_style_set (GtkWidget *widget,
235 static void gtk_combo_box_button_toggled (GtkWidget *widget,
237 static void gtk_combo_box_button_state_changed (GtkWidget *widget,
238 GtkStateType previous,
240 static void gtk_combo_box_add (GtkContainer *container,
242 static void gtk_combo_box_remove (GtkContainer *container,
245 static ComboCellInfo *gtk_combo_box_get_cell_info (GtkComboBox *combo_box,
246 GtkCellRenderer *cell);
248 static void gtk_combo_box_menu_show (GtkWidget *menu,
250 static void gtk_combo_box_menu_hide (GtkWidget *menu,
253 static void gtk_combo_box_set_popup_widget (GtkComboBox *combo_box,
255 static void gtk_combo_box_menu_position_below (GtkMenu *menu,
260 static void gtk_combo_box_menu_position_over (GtkMenu *menu,
265 static void gtk_combo_box_menu_position (GtkMenu *menu,
271 static gint gtk_combo_box_calc_requested_width (GtkComboBox *combo_box,
273 static void gtk_combo_box_remeasure (GtkComboBox *combo_box);
275 static void gtk_combo_box_unset_model (GtkComboBox *combo_box);
277 static void gtk_combo_box_size_request (GtkWidget *widget,
278 GtkRequisition *requisition);
279 static void gtk_combo_box_size_allocate (GtkWidget *widget,
280 GtkAllocation *allocation);
281 static void gtk_combo_box_forall (GtkContainer *container,
282 gboolean include_internals,
283 GtkCallback callback,
284 gpointer callback_data);
285 static gboolean gtk_combo_box_expose_event (GtkWidget *widget,
286 GdkEventExpose *event);
287 static gboolean gtk_combo_box_scroll_event (GtkWidget *widget,
288 GdkEventScroll *event);
289 static void gtk_combo_box_set_active_internal (GtkComboBox *combo_box,
292 static void gtk_combo_box_check_appearance (GtkComboBox *combo_box);
293 static gchar * gtk_combo_box_real_get_active_text (GtkComboBox *combo_box);
294 static void gtk_combo_box_real_move_active (GtkComboBox *combo_box,
295 GtkScrollType scroll);
296 static void gtk_combo_box_real_popup (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 void gtk_combo_box_cell_layout_clear (GtkCellLayout *layout);
413 static void gtk_combo_box_cell_layout_add_attribute (GtkCellLayout *layout,
414 GtkCellRenderer *cell,
415 const gchar *attribute,
417 static void gtk_combo_box_cell_layout_set_cell_data_func (GtkCellLayout *layout,
418 GtkCellRenderer *cell,
419 GtkCellLayoutDataFunc func,
421 GDestroyNotify destroy);
422 static void gtk_combo_box_cell_layout_clear_attributes (GtkCellLayout *layout,
423 GtkCellRenderer *cell);
424 static void gtk_combo_box_cell_layout_reorder (GtkCellLayout *layout,
425 GtkCellRenderer *cell,
427 static gboolean gtk_combo_box_mnemonic_activate (GtkWidget *widget,
428 gboolean group_cycling);
430 static void gtk_combo_box_sync_cells (GtkComboBox *combo_box,
431 GtkCellLayout *cell_layout);
432 static void combo_cell_data_func (GtkCellLayout *cell_layout,
433 GtkCellRenderer *cell,
434 GtkTreeModel *tree_model,
437 static void gtk_combo_box_child_show (GtkWidget *widget,
438 GtkComboBox *combo_box);
439 static void gtk_combo_box_child_hide (GtkWidget *widget,
440 GtkComboBox *combo_box);
443 /* GtkCellEditable method implementations */
444 static void gtk_combo_box_start_editing (GtkCellEditable *cell_editable,
448 G_DEFINE_TYPE_WITH_CODE (GtkComboBox, gtk_combo_box, GTK_TYPE_BIN,
449 G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
450 gtk_combo_box_cell_layout_init)
451 G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_EDITABLE,
452 gtk_combo_box_cell_editable_init))
456 gtk_combo_box_class_init (GtkComboBoxClass *klass)
458 GObjectClass *object_class;
459 GtkObjectClass *gtk_object_class;
460 GtkContainerClass *container_class;
461 GtkWidgetClass *widget_class;
462 GtkBindingSet *binding_set;
464 klass->get_active_text = gtk_combo_box_real_get_active_text;
466 container_class = (GtkContainerClass *)klass;
467 container_class->forall = gtk_combo_box_forall;
468 container_class->add = gtk_combo_box_add;
469 container_class->remove = gtk_combo_box_remove;
471 widget_class = (GtkWidgetClass *)klass;
472 widget_class->size_allocate = gtk_combo_box_size_allocate;
473 widget_class->size_request = gtk_combo_box_size_request;
474 widget_class->expose_event = gtk_combo_box_expose_event;
475 widget_class->scroll_event = gtk_combo_box_scroll_event;
476 widget_class->mnemonic_activate = gtk_combo_box_mnemonic_activate;
477 widget_class->grab_focus = gtk_combo_box_grab_focus;
478 widget_class->style_set = gtk_combo_box_style_set;
479 widget_class->state_changed = gtk_combo_box_state_changed;
481 gtk_object_class = (GtkObjectClass *)klass;
482 gtk_object_class->destroy = gtk_combo_box_destroy;
484 object_class = (GObjectClass *)klass;
485 object_class->finalize = gtk_combo_box_finalize;
486 object_class->set_property = gtk_combo_box_set_property;
487 object_class->get_property = gtk_combo_box_get_property;
491 * GtkComboBox::changed:
492 * @widget: the object which received the signal
494 * The changed signal is emitted when the active
495 * item is changed. The can be due to the user selecting
496 * a different item from the list, or due to a
497 * call to gtk_combo_box_set_active_iter().
498 * It will also be emitted while typing into a GtkComboBoxEntry,
499 * as well as when selecting an item from the GtkComboBoxEntry's list.
503 combo_box_signals[CHANGED] =
504 g_signal_new (I_("changed"),
505 G_OBJECT_CLASS_TYPE (klass),
507 G_STRUCT_OFFSET (GtkComboBoxClass, changed),
509 g_cclosure_marshal_VOID__VOID,
512 combo_box_signals[MOVE_ACTIVE] =
513 _gtk_binding_signal_new (I_("move-active"),
514 G_OBJECT_CLASS_TYPE (klass),
515 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
516 G_CALLBACK (gtk_combo_box_real_move_active),
518 g_cclosure_marshal_VOID__ENUM,
520 GTK_TYPE_SCROLL_TYPE);
522 combo_box_signals[POPUP] =
523 _gtk_binding_signal_new (I_("popup"),
524 G_OBJECT_CLASS_TYPE (klass),
525 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
526 G_CALLBACK (gtk_combo_box_real_popup),
528 g_cclosure_marshal_VOID__VOID,
532 binding_set = gtk_binding_set_by_class (widget_class);
534 gtk_binding_entry_add_signal (binding_set, GDK_Down, GDK_MOD1_MASK,
537 gtk_binding_entry_add_signal (binding_set, GDK_Up, 0,
539 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_UP);
540 gtk_binding_entry_add_signal (binding_set, GDK_KP_Up, 0,
542 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_UP);
543 gtk_binding_entry_add_signal (binding_set, GDK_Page_Up, 0,
545 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_UP);
546 gtk_binding_entry_add_signal (binding_set, GDK_KP_Page_Up, 0,
548 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_UP);
549 gtk_binding_entry_add_signal (binding_set, GDK_Home, 0,
551 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_START);
552 gtk_binding_entry_add_signal (binding_set, GDK_KP_Home, 0,
554 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_START);
556 gtk_binding_entry_add_signal (binding_set, GDK_Down, 0,
558 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_DOWN);
559 gtk_binding_entry_add_signal (binding_set, GDK_KP_Down, 0,
561 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_DOWN);
562 gtk_binding_entry_add_signal (binding_set, GDK_Page_Down, 0,
564 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_DOWN);
565 gtk_binding_entry_add_signal (binding_set, GDK_KP_Page_Down, 0,
567 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_DOWN);
568 gtk_binding_entry_add_signal (binding_set, GDK_End, 0,
570 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_END);
571 gtk_binding_entry_add_signal (binding_set, GDK_KP_End, 0,
573 GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_END);
579 * The model from which the combo box takes the values shown
584 g_object_class_install_property (object_class,
586 g_param_spec_object ("model",
587 P_("ComboBox model"),
588 P_("The model for the combo box"),
590 GTK_PARAM_READWRITE));
593 * GtkComboBox:wrap-width:
595 * If wrap-width is set to a positive value, the list will be
596 * displayed in multiple columns, the number of columns is
597 * determined by wrap-width.
601 g_object_class_install_property (object_class,
603 g_param_spec_int ("wrap-width",
605 P_("Wrap width for laying out the items in a grid"),
609 GTK_PARAM_READWRITE));
613 * GtkComboBox:row-span-column:
615 * If this is set to a non-negative value, it must be the index of a column
616 * of type %G_TYPE_INT in the model.
618 * The values of that column are used to determine how many rows a value
619 * in the list will span. Therefore, the values in the model column pointed
620 * to by this property must be greater than zero and not larger than wrap-width.
624 g_object_class_install_property (object_class,
625 PROP_ROW_SPAN_COLUMN,
626 g_param_spec_int ("row-span-column",
627 P_("Row span column"),
628 P_("TreeModel column containing the row span values"),
632 GTK_PARAM_READWRITE));
636 * GtkComboBox:column-span-column:
638 * If this is set to a non-negative value, it must be the index of a column
639 * of type %G_TYPE_INT in the model.
641 * The values of that column are used to determine how many columns a value
642 * in the list will span.
646 g_object_class_install_property (object_class,
647 PROP_COLUMN_SPAN_COLUMN,
648 g_param_spec_int ("column-span-column",
649 P_("Column span column"),
650 P_("TreeModel column containing the column span values"),
654 GTK_PARAM_READWRITE));
658 * GtkComboBox:active:
660 * The item which is currently active. If the model is a non-flat treemodel,
661 * and the active item is not an immediate child of the root of the tree,
662 * this property has the value <literal>gtk_tree_path_get_indices (path)[0]</literal>,
663 * where <literal>path</literal> is the #GtkTreePath of the active item.
667 g_object_class_install_property (object_class,
669 g_param_spec_int ("active",
671 P_("The item which is currently active"),
675 GTK_PARAM_READWRITE));
678 * GtkComboBox:add-tearoffs:
680 * The add-tearoffs property controls whether generated menus
681 * have tearoff menu items.
683 * Note that this only affects menu style combo boxes.
687 g_object_class_install_property (object_class,
689 g_param_spec_boolean ("add-tearoffs",
690 P_("Add tearoffs to menus"),
691 P_("Whether dropdowns should have a tearoff menu item"),
693 GTK_PARAM_READWRITE));
696 * GtkComboBox:has-frame:
698 * The has-frame property controls whether a frame
699 * is drawn around the entry.
703 g_object_class_install_property (object_class,
705 g_param_spec_boolean ("has-frame",
707 P_("Whether the combo box draws a frame around the child"),
709 GTK_PARAM_READWRITE));
711 g_object_class_install_property (object_class,
713 g_param_spec_boolean ("focus-on-click",
714 P_("Focus on click"),
715 P_("Whether the combo box grabs focus when it is clicked with the mouse"),
717 GTK_PARAM_READWRITE));
720 * GtkComboBox:tearoff-title:
722 * A title that may be displayed by the window manager
723 * when the popup is torn-off.
727 g_object_class_install_property (object_class,
729 g_param_spec_string ("tearoff-title",
731 P_("A title that may be displayed by the window manager when the popup is torn-off"),
733 GTK_PARAM_READWRITE));
737 * GtkComboBox:popup-shown:
739 * Whether the combo boxes dropdown is popped up.
740 * Note that this property is mainly useful, because
741 * it allows you to connect to notify::popup-shown.
745 g_object_class_install_property (object_class,
747 g_param_spec_boolean ("popup-shown",
749 P_("Whether the combo's dropdown is shown"),
751 GTK_PARAM_READABLE));
753 gtk_widget_class_install_style_property (widget_class,
754 g_param_spec_boolean ("appears-as-list",
755 P_("Appears as list"),
756 P_("Whether dropdowns should look like lists rather than menus"),
758 GTK_PARAM_READABLE));
761 * GtkComboBox:arrow-size:
763 * Sets the minimum size of the arrow in the combo box. Note
764 * that the arrow size is coupled to the font size, so in case
765 * a larger font is used, the arrow will be larger than set
770 gtk_widget_class_install_style_property (widget_class,
771 g_param_spec_int ("arrow-size",
773 P_("The minimum size of the arrow in the combo box"),
777 GTK_PARAM_READABLE));
780 * GtkComboBox:shadow-type:
782 * Which kind of shadow to draw around the combo box.
786 gtk_widget_class_install_style_property (widget_class,
787 g_param_spec_enum ("shadow-type",
789 P_("Which kind of shadow to draw around the combo box"),
790 GTK_TYPE_SHADOW_TYPE,
792 GTK_PARAM_READABLE));
794 g_type_class_add_private (object_class, sizeof (GtkComboBoxPrivate));
798 gtk_combo_box_cell_layout_init (GtkCellLayoutIface *iface)
800 iface->pack_start = gtk_combo_box_cell_layout_pack_start;
801 iface->pack_end = gtk_combo_box_cell_layout_pack_end;
802 iface->clear = gtk_combo_box_cell_layout_clear;
803 iface->add_attribute = gtk_combo_box_cell_layout_add_attribute;
804 iface->set_cell_data_func = gtk_combo_box_cell_layout_set_cell_data_func;
805 iface->clear_attributes = gtk_combo_box_cell_layout_clear_attributes;
806 iface->reorder = gtk_combo_box_cell_layout_reorder;
810 gtk_combo_box_cell_editable_init (GtkCellEditableIface *iface)
812 iface->start_editing = gtk_combo_box_start_editing;
816 gtk_combo_box_init (GtkComboBox *combo_box)
818 combo_box->priv = GTK_COMBO_BOX_GET_PRIVATE (combo_box);
820 combo_box->priv->cell_view = gtk_cell_view_new ();
821 gtk_widget_set_parent (combo_box->priv->cell_view, GTK_WIDGET (combo_box));
822 GTK_BIN (combo_box)->child = combo_box->priv->cell_view;
823 gtk_widget_show (combo_box->priv->cell_view);
825 combo_box->priv->width = 0;
826 combo_box->priv->height = 0;
827 combo_box->priv->wrap_width = 0;
829 combo_box->priv->active_row = NULL;
830 combo_box->priv->col_column = -1;
831 combo_box->priv->row_column = -1;
833 combo_box->priv->popup_shown = FALSE;
834 combo_box->priv->add_tearoffs = FALSE;
835 combo_box->priv->has_frame = TRUE;
836 combo_box->priv->is_cell_renderer = FALSE;
837 combo_box->priv->editing_canceled = FALSE;
838 combo_box->priv->auto_scroll = FALSE;
839 combo_box->priv->focus_on_click = TRUE;
841 gtk_combo_box_check_appearance (combo_box);
845 gtk_combo_box_set_property (GObject *object,
850 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
855 gtk_combo_box_set_model (combo_box, g_value_get_object (value));
858 case PROP_WRAP_WIDTH:
859 gtk_combo_box_set_wrap_width (combo_box, g_value_get_int (value));
862 case PROP_ROW_SPAN_COLUMN:
863 gtk_combo_box_set_row_span_column (combo_box, g_value_get_int (value));
866 case PROP_COLUMN_SPAN_COLUMN:
867 gtk_combo_box_set_column_span_column (combo_box, g_value_get_int (value));
871 gtk_combo_box_set_active (combo_box, g_value_get_int (value));
874 case PROP_ADD_TEAROFFS:
875 gtk_combo_box_set_add_tearoffs (combo_box, g_value_get_boolean (value));
879 combo_box->priv->has_frame = g_value_get_boolean (value);
882 case PROP_FOCUS_ON_CLICK:
883 gtk_combo_box_set_focus_on_click (combo_box,
884 g_value_get_boolean (value));
887 case PROP_TEAROFF_TITLE:
888 gtk_combo_box_set_title (combo_box, g_value_get_string (value));
891 case PROP_POPUP_SHOWN:
892 if (g_value_get_boolean (value))
894 gtk_combo_box_popup (combo_box);
898 gtk_combo_box_popdown (combo_box);
908 gtk_combo_box_get_property (GObject *object,
913 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
918 g_value_set_object (value, combo_box->priv->model);
921 case PROP_WRAP_WIDTH:
922 g_value_set_int (value, combo_box->priv->wrap_width);
925 case PROP_ROW_SPAN_COLUMN:
926 g_value_set_int (value, combo_box->priv->row_column);
929 case PROP_COLUMN_SPAN_COLUMN:
930 g_value_set_int (value, combo_box->priv->col_column);
934 g_value_set_int (value, gtk_combo_box_get_active (combo_box));
937 case PROP_ADD_TEAROFFS:
938 g_value_set_boolean (value, gtk_combo_box_get_add_tearoffs (combo_box));
942 g_value_set_boolean (value, combo_box->priv->has_frame);
945 case PROP_FOCUS_ON_CLICK:
946 g_value_set_boolean (value, combo_box->priv->focus_on_click);
949 case PROP_TEAROFF_TITLE:
950 g_value_set_string (value, gtk_combo_box_get_title (combo_box));
953 case PROP_POPUP_SHOWN:
954 g_value_set_boolean (value, combo_box->priv->popup_shown);
958 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
964 gtk_combo_box_state_changed (GtkWidget *widget,
965 GtkStateType previous)
967 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
969 if (GTK_WIDGET_REALIZED (widget))
971 if (combo_box->priv->tree_view && combo_box->priv->cell_view)
972 gtk_cell_view_set_background_color (GTK_CELL_VIEW (combo_box->priv->cell_view),
973 &widget->style->base[GTK_WIDGET_STATE (widget)]);
976 gtk_widget_queue_draw (widget);
980 gtk_combo_box_button_state_changed (GtkWidget *widget,
981 GtkStateType previous,
984 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
986 if (GTK_WIDGET_REALIZED (widget))
988 if (!combo_box->priv->tree_view && combo_box->priv->cell_view)
990 if ((GTK_WIDGET_STATE (widget) == GTK_STATE_INSENSITIVE) !=
991 (GTK_WIDGET_STATE (combo_box->priv->cell_view) == GTK_STATE_INSENSITIVE))
992 gtk_widget_set_sensitive (combo_box->priv->cell_view, GTK_WIDGET_SENSITIVE (widget));
994 gtk_widget_set_state (combo_box->priv->cell_view,
995 GTK_WIDGET_STATE (widget));
1000 gtk_widget_queue_draw (widget);
1004 gtk_combo_box_check_appearance (GtkComboBox *combo_box)
1006 gboolean appears_as_list;
1008 /* if wrap_width > 0, then we are in grid-mode and forced to use
1011 if (combo_box->priv->wrap_width)
1012 appears_as_list = FALSE;
1014 gtk_widget_style_get (GTK_WIDGET (combo_box),
1015 "appears-as-list", &appears_as_list,
1018 if (appears_as_list)
1020 /* Destroy all the menu mode widgets, if they exist. */
1021 if (GTK_IS_MENU (combo_box->priv->popup_widget))
1022 gtk_combo_box_menu_destroy (combo_box);
1024 /* Create the list mode widgets, if they don't already exist. */
1025 if (!GTK_IS_TREE_VIEW (combo_box->priv->tree_view))
1026 gtk_combo_box_list_setup (combo_box);
1030 /* Destroy all the list mode widgets, if they exist. */
1031 if (GTK_IS_TREE_VIEW (combo_box->priv->tree_view))
1032 gtk_combo_box_list_destroy (combo_box);
1034 /* Create the menu mode widgets, if they don't already exist. */
1035 if (!GTK_IS_MENU (combo_box->priv->popup_widget))
1036 gtk_combo_box_menu_setup (combo_box, TRUE);
1039 gtk_widget_style_get (GTK_WIDGET (combo_box),
1040 "shadow-type", &combo_box->priv->shadow_type,
1045 gtk_combo_box_style_set (GtkWidget *widget,
1048 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1050 gtk_combo_box_check_appearance (combo_box);
1052 if (combo_box->priv->tree_view && combo_box->priv->cell_view)
1053 gtk_cell_view_set_background_color (GTK_CELL_VIEW (combo_box->priv->cell_view),
1054 &widget->style->base[GTK_WIDGET_STATE (widget)]);
1056 if (GTK_IS_ENTRY (GTK_BIN (combo_box)->child))
1057 g_object_set (GTK_BIN (combo_box)->child, "shadow-type",
1058 GTK_SHADOW_NONE == combo_box->priv->shadow_type ?
1059 GTK_SHADOW_IN : GTK_SHADOW_NONE, NULL);
1063 gtk_combo_box_button_toggled (GtkWidget *widget,
1066 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
1068 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
1070 if (!combo_box->priv->popup_in_progress)
1071 gtk_combo_box_popup (combo_box);
1074 gtk_combo_box_popdown (combo_box);
1078 gtk_combo_box_add (GtkContainer *container,
1081 GtkComboBox *combo_box = GTK_COMBO_BOX (container);
1083 if (combo_box->priv->cell_view && combo_box->priv->cell_view->parent)
1085 gtk_widget_unparent (combo_box->priv->cell_view);
1086 GTK_BIN (container)->child = NULL;
1087 gtk_widget_queue_resize (GTK_WIDGET (container));
1090 gtk_widget_set_parent (widget, GTK_WIDGET (container));
1091 GTK_BIN (container)->child = widget;
1093 if (combo_box->priv->cell_view &&
1094 widget != combo_box->priv->cell_view)
1096 /* since the cell_view was unparented, it's gone now */
1097 combo_box->priv->cell_view = NULL;
1099 if (!combo_box->priv->tree_view && combo_box->priv->separator)
1101 gtk_container_remove (GTK_CONTAINER (combo_box->priv->separator->parent),
1102 combo_box->priv->separator);
1103 combo_box->priv->separator = NULL;
1105 gtk_widget_queue_resize (GTK_WIDGET (container));
1107 else if (combo_box->priv->cell_view_frame)
1109 gtk_widget_unparent (combo_box->priv->cell_view_frame);
1110 combo_box->priv->cell_view_frame = NULL;
1111 combo_box->priv->box = NULL;
1117 gtk_combo_box_remove (GtkContainer *container,
1120 GtkComboBox *combo_box = GTK_COMBO_BOX (container);
1122 gboolean appears_as_list;
1124 if (widget == combo_box->priv->cell_view)
1125 combo_box->priv->cell_view = NULL;
1127 gtk_widget_unparent (widget);
1128 GTK_BIN (container)->child = NULL;
1130 if (GTK_OBJECT_FLAGS (combo_box) & GTK_IN_DESTRUCTION)
1133 gtk_widget_queue_resize (GTK_WIDGET (container));
1135 if (!combo_box->priv->tree_view)
1136 appears_as_list = FALSE;
1138 appears_as_list = TRUE;
1140 if (appears_as_list)
1141 gtk_combo_box_list_destroy (combo_box);
1142 else if (GTK_IS_MENU (combo_box->priv->popup_widget))
1144 gtk_combo_box_menu_destroy (combo_box);
1145 gtk_menu_detach (GTK_MENU (combo_box->priv->popup_widget));
1146 combo_box->priv->popup_widget = NULL;
1149 if (!combo_box->priv->cell_view)
1151 combo_box->priv->cell_view = gtk_cell_view_new ();
1152 gtk_widget_set_parent (combo_box->priv->cell_view, GTK_WIDGET (container));
1153 GTK_BIN (container)->child = combo_box->priv->cell_view;
1155 gtk_widget_show (combo_box->priv->cell_view);
1156 gtk_cell_view_set_model (GTK_CELL_VIEW (combo_box->priv->cell_view),
1157 combo_box->priv->model);
1158 gtk_combo_box_sync_cells (combo_box, GTK_CELL_LAYOUT (combo_box->priv->cell_view));
1162 if (appears_as_list)
1163 gtk_combo_box_list_setup (combo_box);
1165 gtk_combo_box_menu_setup (combo_box, TRUE);
1167 if (gtk_tree_row_reference_valid (combo_box->priv->active_row))
1169 path = gtk_tree_row_reference_get_path (combo_box->priv->active_row);
1170 gtk_combo_box_set_active_internal (combo_box, path);
1171 gtk_tree_path_free (path);
1174 gtk_combo_box_set_active_internal (combo_box, NULL);
1177 static ComboCellInfo *
1178 gtk_combo_box_get_cell_info (GtkComboBox *combo_box,
1179 GtkCellRenderer *cell)
1183 for (i = combo_box->priv->cells; i; i = i->next)
1185 ComboCellInfo *info = (ComboCellInfo *)i->data;
1187 if (info && info->cell == cell)
1195 gtk_combo_box_menu_show (GtkWidget *menu,
1198 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1200 gtk_combo_box_child_show (menu, user_data);
1202 combo_box->priv->popup_in_progress = TRUE;
1203 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
1205 combo_box->priv->popup_in_progress = FALSE;
1209 gtk_combo_box_menu_hide (GtkWidget *menu,
1212 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1214 gtk_combo_box_child_hide(menu,user_data);
1216 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
1221 gtk_combo_box_detacher (GtkWidget *widget,
1224 GtkComboBox *combo_box;
1226 g_return_if_fail (GTK_IS_COMBO_BOX (widget));
1228 combo_box = GTK_COMBO_BOX (widget);
1229 g_return_if_fail (combo_box->priv->popup_widget == (GtkWidget*) menu);
1231 g_signal_handlers_disconnect_by_func (menu->toplevel,
1232 gtk_combo_box_menu_show,
1234 g_signal_handlers_disconnect_by_func (menu->toplevel,
1235 gtk_combo_box_menu_hide,
1238 combo_box->priv->popup_widget = NULL;
1242 gtk_combo_box_set_popup_widget (GtkComboBox *combo_box,
1245 if (GTK_IS_MENU (combo_box->priv->popup_widget))
1247 gtk_menu_detach (GTK_MENU (combo_box->priv->popup_widget));
1248 combo_box->priv->popup_widget = NULL;
1250 else if (combo_box->priv->popup_widget)
1252 gtk_container_remove (GTK_CONTAINER (combo_box->priv->popup_frame),
1253 combo_box->priv->popup_widget);
1254 g_object_unref (combo_box->priv->popup_widget);
1255 combo_box->priv->popup_widget = NULL;
1258 if (GTK_IS_MENU (popup))
1260 if (combo_box->priv->popup_window)
1262 gtk_widget_destroy (combo_box->priv->popup_window);
1263 combo_box->priv->popup_window = NULL;
1264 combo_box->priv->popup_frame = NULL;
1267 combo_box->priv->popup_widget = popup;
1270 * Note that we connect to show/hide on the toplevel, not the
1271 * menu itself, since the menu is not shown/hidden when it is
1272 * popped up while torn-off.
1274 g_signal_connect (GTK_MENU (popup)->toplevel, "show",
1275 G_CALLBACK (gtk_combo_box_menu_show), combo_box);
1276 g_signal_connect (GTK_MENU (popup)->toplevel, "hide",
1277 G_CALLBACK (gtk_combo_box_menu_hide), combo_box);
1279 gtk_menu_attach_to_widget (GTK_MENU (popup),
1280 GTK_WIDGET (combo_box),
1281 gtk_combo_box_detacher);
1285 if (!combo_box->priv->popup_window)
1287 GtkWidget *toplevel;
1289 combo_box->priv->popup_window = gtk_window_new (GTK_WINDOW_POPUP);
1290 gtk_widget_set_name (combo_box->priv->popup_window, "gtk-combobox-popup-window");
1292 gtk_window_set_type_hint (GTK_WINDOW (combo_box->priv->popup_window),
1293 GDK_WINDOW_TYPE_HINT_COMBO);
1295 g_signal_connect (GTK_WINDOW(combo_box->priv->popup_window),"show",
1296 G_CALLBACK (gtk_combo_box_child_show),
1298 g_signal_connect (GTK_WINDOW(combo_box->priv->popup_window),"hide",
1299 G_CALLBACK (gtk_combo_box_child_hide),
1302 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (combo_box));
1303 if (GTK_IS_WINDOW (toplevel))
1305 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
1306 GTK_WINDOW (combo_box->priv->popup_window));
1307 gtk_window_set_transient_for (GTK_WINDOW (combo_box->priv->popup_window),
1308 GTK_WINDOW (toplevel));
1311 gtk_window_set_resizable (GTK_WINDOW (combo_box->priv->popup_window), FALSE);
1312 gtk_window_set_screen (GTK_WINDOW (combo_box->priv->popup_window),
1313 gtk_widget_get_screen (GTK_WIDGET (combo_box)));
1315 combo_box->priv->popup_frame = gtk_frame_new (NULL);
1316 gtk_frame_set_shadow_type (GTK_FRAME (combo_box->priv->popup_frame),
1318 gtk_container_add (GTK_CONTAINER (combo_box->priv->popup_window),
1319 combo_box->priv->popup_frame);
1321 gtk_widget_show (combo_box->priv->popup_frame);
1323 combo_box->priv->scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1325 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window),
1328 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window),
1331 gtk_widget_show (combo_box->priv->scrolled_window);
1333 gtk_container_add (GTK_CONTAINER (combo_box->priv->popup_frame),
1334 combo_box->priv->scrolled_window);
1337 gtk_container_add (GTK_CONTAINER (combo_box->priv->scrolled_window),
1340 gtk_widget_show (popup);
1341 g_object_ref (popup);
1342 combo_box->priv->popup_widget = popup;
1347 gtk_combo_box_menu_position_below (GtkMenu *menu,
1353 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1359 GdkRectangle monitor;
1361 /* FIXME: is using the size request here broken? */
1362 child = GTK_BIN (combo_box)->child;
1364 gdk_window_get_origin (child->window, &sx, &sy);
1366 if (GTK_WIDGET_NO_WINDOW (child))
1368 sx += child->allocation.x;
1369 sy += child->allocation.y;
1372 if (GTK_SHADOW_NONE != combo_box->priv->shadow_type)
1373 sx -= GTK_WIDGET (combo_box)->style->xthickness;
1375 gtk_widget_size_request (GTK_WIDGET (menu), &req);
1377 if (gtk_widget_get_direction (GTK_WIDGET (combo_box)) == GTK_TEXT_DIR_LTR)
1380 *x = sx + child->allocation.width - req.width;
1383 screen = gtk_widget_get_screen (GTK_WIDGET (combo_box));
1384 monitor_num = gdk_screen_get_monitor_at_window (screen,
1385 GTK_WIDGET (combo_box)->window);
1386 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1390 else if (*x + req.width > monitor.x + monitor.width)
1391 *x = monitor.x + monitor.width - req.width;
1393 if (monitor.y + monitor.height - *y - child->allocation.height >= req.height)
1394 *y += child->allocation.height;
1395 else if (*y - monitor.y >= req.height)
1397 else if (monitor.y + monitor.height - *y - child->allocation.height > *y - monitor.y)
1398 *y += child->allocation.height;
1406 gtk_combo_box_menu_position_over (GtkMenu *menu,
1412 GtkComboBox *combo_box;
1416 GtkRequisition requisition;
1423 g_return_if_fail (GTK_IS_COMBO_BOX (user_data));
1425 combo_box = GTK_COMBO_BOX (user_data);
1426 widget = GTK_WIDGET (combo_box);
1428 gtk_widget_get_child_requisition (GTK_WIDGET (menu), &requisition);
1429 menu_width = requisition.width;
1431 active = gtk_menu_get_active (GTK_MENU (combo_box->priv->popup_widget));
1432 gdk_window_get_origin (widget->window, &menu_xpos, &menu_ypos);
1434 menu_xpos += widget->allocation.x;
1435 menu_ypos += widget->allocation.y + widget->allocation.height / 2 - 2;
1439 gtk_widget_get_child_requisition (active, &requisition);
1440 menu_ypos -= requisition.height / 2;
1443 children = GTK_MENU_SHELL (combo_box->priv->popup_widget)->children;
1446 child = children->data;
1448 if (active == child)
1451 if (GTK_WIDGET_VISIBLE (child))
1453 gtk_widget_get_child_requisition (child, &requisition);
1454 menu_ypos -= requisition.height;
1457 children = children->next;
1460 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
1461 menu_xpos = menu_xpos + widget->allocation.width - menu_width;
1463 /* Clamp the position on screen */
1464 screen_width = gdk_screen_get_width (gtk_widget_get_screen (widget));
1468 else if ((menu_xpos + menu_width) > screen_width)
1469 menu_xpos -= ((menu_xpos + menu_width) - screen_width);
1478 gtk_combo_box_menu_position (GtkMenu *menu,
1484 GtkComboBox *combo_box;
1485 GtkWidget *menu_item;
1487 combo_box = GTK_COMBO_BOX (user_data);
1489 if (combo_box->priv->wrap_width > 0 || combo_box->priv->cell_view == NULL)
1490 gtk_combo_box_menu_position_below (menu, x, y, push_in, user_data);
1493 /* FIXME handle nested menus better */
1494 menu_item = gtk_menu_get_active (GTK_MENU (combo_box->priv->popup_widget));
1496 gtk_menu_shell_select_item (GTK_MENU_SHELL (combo_box->priv->popup_widget),
1499 gtk_combo_box_menu_position_over (menu, x, y, push_in, user_data);
1505 gtk_combo_box_list_position (GtkComboBox *combo_box,
1513 GdkRectangle monitor;
1514 GtkRequisition popup_req;
1515 GtkPolicyType hpolicy, vpolicy;
1517 /* under windows, the drop down list is as wide as the combo box itself.
1519 GtkWidget *sample = GTK_WIDGET (combo_box);
1521 gdk_window_get_origin (sample->window, x, y);
1523 if (GTK_WIDGET_NO_WINDOW (sample))
1525 *x += sample->allocation.x;
1526 *y += sample->allocation.y;
1529 *width = sample->allocation.width;
1531 if (combo_box->priv->cell_view_frame && combo_box->priv->has_frame)
1533 *x -= GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
1534 GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness;
1535 *width += 2 * (GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
1536 GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness);
1539 hpolicy = vpolicy = GTK_POLICY_NEVER;
1540 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window),
1542 gtk_widget_size_request (combo_box->priv->popup_frame, &popup_req);
1544 if (popup_req.width > *width)
1546 hpolicy = GTK_POLICY_ALWAYS;
1547 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window),
1549 gtk_widget_size_request (combo_box->priv->popup_frame, &popup_req);
1552 *height = popup_req.height;
1554 screen = gtk_widget_get_screen (GTK_WIDGET (combo_box));
1555 monitor_num = gdk_screen_get_monitor_at_window (screen,
1556 GTK_WIDGET (combo_box)->window);
1557 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
1561 else if (*x + *width > monitor.x + monitor.width)
1562 *x = monitor.x + monitor.width - *width;
1564 if (*y + sample->allocation.height + *height <= monitor.y + monitor.height)
1565 *y += sample->allocation.height;
1566 else if (*y - *height >= monitor.y)
1568 else if (monitor.y + monitor.height - (*y + sample->allocation.height) > *y - monitor.y)
1570 *y += sample->allocation.height;
1571 *height = monitor.y + monitor.height - *y;
1575 *height = *y - monitor.y;
1579 if (popup_req.height > *height)
1581 vpolicy = GTK_POLICY_ALWAYS;
1583 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window),
1589 cell_view_is_sensitive (GtkCellView *cell_view)
1591 GList *cells, *list;
1594 cells = gtk_cell_view_get_cell_renderers (cell_view);
1600 g_object_get (list->data, "sensitive", &sensitive, NULL);
1607 g_list_free (cells);
1613 tree_column_row_is_sensitive (GtkComboBox *combo_box,
1616 GList *cells, *list;
1619 if (!combo_box->priv->column)
1622 if (combo_box->priv->row_separator_func)
1624 if ((*combo_box->priv->row_separator_func) (combo_box->priv->model, iter,
1625 combo_box->priv->row_separator_data))
1629 gtk_tree_view_column_cell_set_cell_data (combo_box->priv->column,
1630 combo_box->priv->model,
1631 iter, FALSE, FALSE);
1633 cells = gtk_tree_view_column_get_cell_renderers (combo_box->priv->column);
1639 g_object_get (list->data, "sensitive", &sensitive, NULL);
1646 g_list_free (cells);
1652 update_menu_sensitivity (GtkComboBox *combo_box,
1655 GList *children, *child;
1656 GtkWidget *item, *submenu, *separator;
1657 GtkWidget *cell_view;
1660 if (!combo_box->priv->model)
1663 children = gtk_container_get_children (GTK_CONTAINER (menu));
1665 for (child = children; child; child = child->next)
1667 item = GTK_WIDGET (child->data);
1668 cell_view = GTK_BIN (item)->child;
1670 if (!GTK_IS_CELL_VIEW (cell_view))
1673 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (item));
1674 if (submenu != NULL)
1676 gtk_widget_set_sensitive (item, TRUE);
1677 update_menu_sensitivity (combo_box, submenu);
1681 sensitive = cell_view_is_sensitive (GTK_CELL_VIEW (cell_view));
1683 if (menu != combo_box->priv->popup_widget && child == children)
1685 separator = GTK_WIDGET (child->next->data);
1686 g_object_set (item, "visible", sensitive, NULL);
1687 g_object_set (separator, "visible", sensitive, NULL);
1690 gtk_widget_set_sensitive (item, sensitive);
1694 g_list_free (children);
1698 gtk_combo_box_menu_popup (GtkComboBox *combo_box,
1700 guint32 activate_time)
1704 GtkRequisition requisition;
1707 update_menu_sensitivity (combo_box, combo_box->priv->popup_widget);
1710 if (gtk_tree_row_reference_valid (combo_box->priv->active_row))
1712 path = gtk_tree_row_reference_get_path (combo_box->priv->active_row);
1713 active_item = gtk_tree_path_get_indices (path)[0];
1714 gtk_tree_path_free (path);
1716 if (combo_box->priv->add_tearoffs)
1720 /* FIXME handle nested menus better */
1721 gtk_menu_set_active (GTK_MENU (combo_box->priv->popup_widget), active_item);
1723 if (combo_box->priv->wrap_width == 0)
1725 width = GTK_WIDGET (combo_box)->allocation.width;
1726 gtk_widget_set_size_request (combo_box->priv->popup_widget, -1, -1);
1727 gtk_widget_size_request (combo_box->priv->popup_widget, &requisition);
1729 gtk_widget_set_size_request (combo_box->priv->popup_widget,
1730 MAX (width, requisition.width), -1);
1733 gtk_menu_popup (GTK_MENU (combo_box->priv->popup_widget),
1735 gtk_combo_box_menu_position, combo_box,
1736 button, activate_time);
1740 popup_grab_on_window (GdkWindow *window,
1741 guint32 activate_time,
1742 gboolean grab_keyboard)
1744 if ((gdk_pointer_grab (window, TRUE,
1745 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1746 GDK_POINTER_MOTION_MASK,
1747 NULL, NULL, activate_time) == 0))
1749 if (!grab_keyboard ||
1750 gdk_keyboard_grab (window, TRUE,
1751 activate_time) == 0)
1755 gdk_display_pointer_ungrab (gdk_drawable_get_display (window),
1765 * gtk_combo_box_popup:
1766 * @combo_box: a #GtkComboBox
1768 * Pops up the menu or dropdown list of @combo_box.
1770 * This function is mostly intended for use by accessibility technologies;
1771 * applications should have little use for it.
1776 gtk_combo_box_popup (GtkComboBox *combo_box)
1778 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
1780 g_signal_emit (combo_box, combo_box_signals[POPUP], 0);
1784 gtk_combo_box_real_popup (GtkComboBox *combo_box)
1786 gint x, y, width, height;
1787 GtkTreePath *path = NULL, *ppath;
1788 GtkWidget *toplevel;
1790 if (!GTK_WIDGET_REALIZED (combo_box))
1793 if (GTK_WIDGET_MAPPED (combo_box->priv->popup_widget))
1796 if (GTK_IS_MENU (combo_box->priv->popup_widget))
1798 gtk_combo_box_menu_popup (combo_box, 0, 0);
1802 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (combo_box));
1803 if (GTK_IS_WINDOW (toplevel))
1804 gtk_window_group_add_window (gtk_window_get_group (GTK_WINDOW (toplevel)),
1805 GTK_WINDOW (combo_box->priv->popup_window));
1807 gtk_widget_show_all (combo_box->priv->popup_frame);
1808 gtk_combo_box_list_position (combo_box, &x, &y, &width, &height);
1810 gtk_widget_set_size_request (combo_box->priv->popup_window, width, height);
1811 gtk_window_move (GTK_WINDOW (combo_box->priv->popup_window), x, y);
1813 if (gtk_tree_row_reference_valid (combo_box->priv->active_row))
1815 path = gtk_tree_row_reference_get_path (combo_box->priv->active_row);
1816 ppath = gtk_tree_path_copy (path);
1817 if (gtk_tree_path_up (ppath))
1818 gtk_tree_view_expand_to_path (GTK_TREE_VIEW (combo_box->priv->tree_view),
1820 gtk_tree_path_free (ppath);
1822 gtk_tree_view_set_hover_expand (GTK_TREE_VIEW (combo_box->priv->tree_view),
1826 gtk_widget_show (combo_box->priv->popup_window);
1830 gtk_tree_view_set_cursor (GTK_TREE_VIEW (combo_box->priv->tree_view),
1832 gtk_tree_path_free (path);
1835 gtk_widget_grab_focus (combo_box->priv->popup_window);
1836 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
1839 if (!GTK_WIDGET_HAS_FOCUS (combo_box->priv->tree_view))
1840 gtk_widget_grab_focus (combo_box->priv->tree_view);
1842 if (!popup_grab_on_window (combo_box->priv->popup_window->window,
1843 GDK_CURRENT_TIME, TRUE))
1845 gtk_widget_hide (combo_box->priv->popup_window);
1849 gtk_grab_add (combo_box->priv->popup_window);
1853 * gtk_combo_box_popdown:
1854 * @combo_box: a #GtkComboBox
1856 * Hides the menu or dropdown list of @combo_box.
1858 * This function is mostly intended for use by accessibility technologies;
1859 * applications should have little use for it.
1864 gtk_combo_box_popdown (GtkComboBox *combo_box)
1866 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
1868 if (GTK_IS_MENU (combo_box->priv->popup_widget))
1870 gtk_menu_popdown (GTK_MENU (combo_box->priv->popup_widget));
1874 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (combo_box)))
1877 gtk_grab_remove (combo_box->priv->popup_window);
1878 gtk_widget_hide_all (combo_box->priv->popup_window);
1879 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
1884 gtk_combo_box_calc_requested_width (GtkComboBox *combo_box,
1890 if (combo_box->priv->cell_view)
1891 gtk_widget_style_get (combo_box->priv->cell_view,
1892 "focus-line-width", &padding,
1897 /* add some pixels for good measure */
1898 padding += BONUS_PADDING;
1900 if (combo_box->priv->cell_view)
1901 gtk_cell_view_get_size_of_row (GTK_CELL_VIEW (combo_box->priv->cell_view),
1906 return req.width + padding;
1910 gtk_combo_box_remeasure (GtkComboBox *combo_box)
1915 if (!combo_box->priv->model ||
1916 !gtk_tree_model_get_iter_first (combo_box->priv->model, &iter))
1919 combo_box->priv->width = 0;
1920 combo_box->priv->height = 0;
1922 path = gtk_tree_path_new_from_indices (0, -1);
1928 if (combo_box->priv->cell_view)
1929 gtk_cell_view_get_size_of_row (GTK_CELL_VIEW (combo_box->priv->cell_view),
1937 combo_box->priv->width = MAX (combo_box->priv->width, req.width);
1938 combo_box->priv->height = MAX (combo_box->priv->height, req.height);
1940 gtk_tree_path_next (path);
1942 while (gtk_tree_model_iter_next (combo_box->priv->model, &iter));
1944 gtk_tree_path_free (path);
1948 gtk_combo_box_size_request (GtkWidget *widget,
1949 GtkRequisition *requisition)
1952 gint focus_width, focus_pad;
1955 GtkRequisition bin_req;
1956 PangoContext *context;
1957 PangoFontMetrics *metrics;
1958 PangoFontDescription *font_desc;
1960 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1963 gtk_widget_size_request (GTK_BIN (widget)->child, &bin_req);
1964 gtk_combo_box_remeasure (combo_box);
1965 bin_req.width = MAX (bin_req.width, combo_box->priv->width);
1966 bin_req.height = MAX (bin_req.height, combo_box->priv->height);
1968 gtk_widget_style_get (GTK_WIDGET (widget),
1969 "focus-line-width", &focus_width,
1970 "focus-padding", &focus_pad,
1971 "arrow-size", &arrow_size,
1974 font_desc = GTK_BIN (widget)->child->style->font_desc;
1975 context = gtk_widget_get_pango_context (widget);
1976 metrics = pango_context_get_metrics (context, font_desc,
1977 pango_context_get_language (context));
1978 font_size = PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) +
1979 pango_font_metrics_get_descent (metrics));
1980 pango_font_metrics_unref (metrics);
1982 arrow_size = MAX (arrow_size, font_size);
1984 gtk_widget_set_size_request (combo_box->priv->arrow, arrow_size, arrow_size);
1986 if (!combo_box->priv->tree_view)
1990 if (combo_box->priv->cell_view)
1992 GtkRequisition button_req, sep_req, arrow_req;
1993 gint border_width, xthickness, ythickness;
1995 gtk_widget_size_request (combo_box->priv->button, &button_req);
1996 border_width = GTK_CONTAINER (combo_box)->border_width;
1997 xthickness = combo_box->priv->button->style->xthickness;
1998 ythickness = combo_box->priv->button->style->ythickness;
2000 bin_req.width = MAX (bin_req.width, combo_box->priv->width);
2001 bin_req.height = MAX (bin_req.height, combo_box->priv->height);
2003 gtk_widget_size_request (combo_box->priv->separator, &sep_req);
2004 gtk_widget_size_request (combo_box->priv->arrow, &arrow_req);
2006 height = MAX (sep_req.height, arrow_req.height);
2007 height = MAX (height, bin_req.height);
2009 width = bin_req.width + sep_req.width + arrow_req.width;
2011 height += 2*(border_width + ythickness + focus_width + focus_pad);
2012 width += 2*(border_width + xthickness + focus_width + focus_pad);
2014 requisition->width = width;
2015 requisition->height = height;
2019 GtkRequisition but_req;
2021 gtk_widget_size_request (combo_box->priv->button, &but_req);
2023 requisition->width = bin_req.width + but_req.width;
2024 requisition->height = MAX (bin_req.height, but_req.height);
2030 GtkRequisition button_req, frame_req;
2032 /* sample + frame */
2033 *requisition = bin_req;
2035 requisition->width += 2 * focus_width;
2037 if (combo_box->priv->cell_view_frame)
2039 gtk_widget_size_request (combo_box->priv->cell_view_frame, &frame_req);
2040 if (combo_box->priv->has_frame)
2042 requisition->width += 2 *
2043 (GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
2044 GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness);
2045 requisition->height += 2 *
2046 (GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
2047 GTK_WIDGET (combo_box->priv->cell_view_frame)->style->ythickness);
2052 gtk_widget_size_request (combo_box->priv->button, &button_req);
2054 requisition->height = MAX (requisition->height, button_req.height);
2055 requisition->width += button_req.width;
2058 if (GTK_SHADOW_NONE != combo_box->priv->shadow_type)
2060 requisition->height += 2 * widget->style->ythickness;
2061 requisition->width += 2 * widget->style->xthickness;
2065 #define GTK_COMBO_BOX_SIZE_ALLOCATE_BUTTON \
2066 gtk_widget_size_request (combo_box->priv->button, &req); \
2069 child.x = allocation->x + shadow_width; \
2071 child.x = allocation->x + allocation->width - req.width - shadow_width; \
2073 child.y = allocation->y + shadow_height; \
2074 child.width = req.width; \
2075 child.height = allocation->height - 2 * shadow_height; \
2076 child.width = MAX (1, child.width); \
2077 child.height = MAX (1, child.height); \
2079 gtk_widget_size_allocate (combo_box->priv->button, &child);
2082 gtk_combo_box_size_allocate (GtkWidget *widget,
2083 GtkAllocation *allocation)
2085 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
2086 gint shadow_width, shadow_height;
2087 gint focus_width, focus_pad;
2088 GtkAllocation child;
2090 gboolean is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
2092 widget->allocation = *allocation;
2094 gtk_widget_style_get (GTK_WIDGET (widget),
2095 "focus-line-width", &focus_width,
2096 "focus-padding", &focus_pad,
2099 if (GTK_SHADOW_NONE != combo_box->priv->shadow_type)
2101 shadow_width = widget->style->xthickness;
2102 shadow_height = widget->style->ythickness;
2110 if (!combo_box->priv->tree_view)
2112 if (combo_box->priv->cell_view)
2114 gint border_width, xthickness, ythickness;
2118 allocation->x += shadow_width;
2119 allocation->y += shadow_height;
2120 allocation->width -= 2 * shadow_width;
2121 allocation->height -= 2 * shadow_height;
2123 gtk_widget_size_allocate (combo_box->priv->button, allocation);
2125 /* set some things ready */
2126 border_width = GTK_CONTAINER (combo_box->priv->button)->border_width;
2127 xthickness = combo_box->priv->button->style->xthickness;
2128 ythickness = combo_box->priv->button->style->ythickness;
2130 child.x = allocation->x;
2131 child.y = allocation->y;
2132 width = allocation->width;
2133 child.height = allocation->height;
2135 if (!combo_box->priv->is_cell_renderer)
2137 child.x += border_width + xthickness + focus_width + focus_pad;
2138 child.y += border_width + ythickness + focus_width + focus_pad;
2139 width -= 2 * (child.x - allocation->x);
2140 child.height -= 2 * (child.y - allocation->y);
2144 /* handle the children */
2145 gtk_widget_size_request (combo_box->priv->arrow, &req);
2146 child.width = req.width;
2148 child.x += width - req.width;
2149 child.width = MAX (1, child.width);
2150 child.height = MAX (1, child.height);
2151 gtk_widget_size_allocate (combo_box->priv->arrow, &child);
2153 child.x += req.width;
2154 gtk_widget_size_request (combo_box->priv->separator, &req);
2155 child.width = req.width;
2157 child.x -= req.width;
2158 child.width = MAX (1, child.width);
2159 child.height = MAX (1, child.height);
2160 gtk_widget_size_allocate (combo_box->priv->separator, &child);
2164 child.x += req.width;
2165 child.width = allocation->x + allocation->width
2166 - (border_width + xthickness + focus_width + focus_pad)
2171 child.width = child.x;
2172 child.x = allocation->x
2173 + border_width + xthickness + focus_width + focus_pad;
2174 child.width -= child.x;
2177 child.width = MAX (1, child.width);
2178 child.height = MAX (1, child.height);
2179 gtk_widget_size_allocate (GTK_BIN (widget)->child, &child);
2183 GTK_COMBO_BOX_SIZE_ALLOCATE_BUTTON
2186 child.x = allocation->x + req.width + shadow_width;
2188 child.x = allocation->x + shadow_width;
2189 child.y = allocation->y + shadow_height;
2190 child.width = allocation->width - req.width - 2 * shadow_width;
2191 child.width = MAX (1, child.width);
2192 child.height = MAX (1, child.height);
2193 gtk_widget_size_allocate (GTK_BIN (widget)->child, &child);
2201 GTK_COMBO_BOX_SIZE_ALLOCATE_BUTTON
2205 child.x = allocation->x + req.width;
2207 child.x = allocation->x;
2208 child.y = allocation->y;
2209 child.width = allocation->width - req.width;
2210 child.height = allocation->height;
2212 if (combo_box->priv->cell_view_frame)
2214 child.width = MAX (1, child.width);
2215 child.height = MAX (1, child.height);
2216 gtk_widget_size_allocate (combo_box->priv->cell_view_frame, &child);
2219 if (combo_box->priv->has_frame)
2222 GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
2223 GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness;
2225 GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
2226 GTK_WIDGET (combo_box->priv->cell_view_frame)->style->ythickness;
2227 child.width -= 2 * (
2228 GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
2229 GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness);
2230 child.height -= 2 * (
2231 GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
2232 GTK_WIDGET (combo_box->priv->cell_view_frame)->style->ythickness);
2236 child.width = MAX (1, child.width);
2237 child.height = MAX (1, child.height);
2238 gtk_widget_size_allocate (GTK_BIN (combo_box)->child, &child);
2242 #undef GTK_COMBO_BOX_ALLOCATE_BUTTON
2245 gtk_combo_box_unset_model (GtkComboBox *combo_box)
2247 if (combo_box->priv->model)
2249 g_signal_handler_disconnect (combo_box->priv->model,
2250 combo_box->priv->inserted_id);
2251 g_signal_handler_disconnect (combo_box->priv->model,
2252 combo_box->priv->deleted_id);
2253 g_signal_handler_disconnect (combo_box->priv->model,
2254 combo_box->priv->reordered_id);
2255 g_signal_handler_disconnect (combo_box->priv->model,
2256 combo_box->priv->changed_id);
2260 if (!combo_box->priv->tree_view)
2262 if (combo_box->priv->popup_widget)
2263 gtk_container_foreach (GTK_CONTAINER (combo_box->priv->popup_widget),
2264 (GtkCallback)gtk_widget_destroy, NULL);
2267 if (combo_box->priv->model)
2269 g_object_unref (combo_box->priv->model);
2270 combo_box->priv->model = NULL;
2273 if (combo_box->priv->active_row)
2275 gtk_tree_row_reference_free (combo_box->priv->active_row);
2276 combo_box->priv->active_row = NULL;
2279 if (combo_box->priv->cell_view)
2280 gtk_cell_view_set_model (GTK_CELL_VIEW (combo_box->priv->cell_view), NULL);
2284 gtk_combo_box_forall (GtkContainer *container,
2285 gboolean include_internals,
2286 GtkCallback callback,
2287 gpointer callback_data)
2289 GtkComboBox *combo_box = GTK_COMBO_BOX (container);
2291 if (include_internals)
2293 if (combo_box->priv->button)
2294 (* callback) (combo_box->priv->button, callback_data);
2295 if (combo_box->priv->cell_view_frame)
2296 (* callback) (combo_box->priv->cell_view_frame, callback_data);
2299 if (GTK_BIN (container)->child)
2300 (* callback) (GTK_BIN (container)->child, callback_data);
2304 gtk_combo_box_child_show (GtkWidget *widget,
2305 GtkComboBox *combo_box)
2307 GtkComboBoxPrivate *priv = combo_box->priv;
2309 priv->popup_shown = TRUE;
2310 g_object_notify (G_OBJECT (combo_box), "popup-shown");
2314 gtk_combo_box_child_hide (GtkWidget *widget,
2315 GtkComboBox *combo_box)
2317 GtkComboBoxPrivate *priv = combo_box->priv;
2319 priv->popup_shown = FALSE;
2320 g_object_notify (G_OBJECT (combo_box), "popup-shown");
2324 gtk_combo_box_expose_event (GtkWidget *widget,
2325 GdkEventExpose *event)
2327 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
2329 if (GTK_WIDGET_DRAWABLE (widget) &&
2330 GTK_SHADOW_NONE != combo_box->priv->shadow_type)
2332 gtk_paint_shadow (widget->style, widget->window,
2333 GTK_STATE_NORMAL, combo_box->priv->shadow_type,
2334 NULL, widget, "combobox",
2335 widget->allocation.x, widget->allocation.y,
2336 widget->allocation.width, widget->allocation.height);
2339 gtk_container_propagate_expose (GTK_CONTAINER (widget),
2340 combo_box->priv->button, event);
2342 if (combo_box->priv->tree_view &&
2343 combo_box->priv->cell_view_frame)
2345 gtk_container_propagate_expose (GTK_CONTAINER (widget),
2346 combo_box->priv->cell_view_frame, event);
2349 gtk_container_propagate_expose (GTK_CONTAINER (widget),
2350 GTK_BIN (widget)->child, event);
2365 path_visible (GtkTreeView *view,
2371 /* Note that we rely on the fact that collapsed rows don't have nodes
2373 return _gtk_tree_view_find_node (view, path, &tree, &node);
2377 tree_next_func (GtkTreeModel *model,
2382 SearchData *search_data = (SearchData *)data;
2384 if (search_data->found)
2386 if (!tree_column_row_is_sensitive (search_data->combo, iter))
2389 if (search_data->visible &&
2390 !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
2393 search_data->set = TRUE;
2394 search_data->iter = *iter;
2399 if (gtk_tree_path_compare (path, search_data->path) == 0)
2400 search_data->found = TRUE;
2406 tree_next (GtkComboBox *combo,
2407 GtkTreeModel *model,
2412 SearchData search_data;
2414 search_data.combo = combo;
2415 search_data.path = gtk_tree_model_get_path (model, iter);
2416 search_data.visible = visible;
2417 search_data.found = FALSE;
2418 search_data.set = FALSE;
2420 gtk_tree_model_foreach (model, tree_next_func, &search_data);
2422 *next = search_data.iter;
2424 gtk_tree_path_free (search_data.path);
2426 return search_data.set;
2430 tree_prev_func (GtkTreeModel *model,
2435 SearchData *search_data = (SearchData *)data;
2437 if (gtk_tree_path_compare (path, search_data->path) == 0)
2439 search_data->found = TRUE;
2443 if (!tree_column_row_is_sensitive (search_data->combo, iter))
2446 if (search_data->visible &&
2447 !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
2450 search_data->set = TRUE;
2451 search_data->iter = *iter;
2457 tree_prev (GtkComboBox *combo,
2458 GtkTreeModel *model,
2463 SearchData search_data;
2465 search_data.combo = combo;
2466 search_data.path = gtk_tree_model_get_path (model, iter);
2467 search_data.visible = visible;
2468 search_data.found = FALSE;
2469 search_data.set = FALSE;
2471 gtk_tree_model_foreach (model, tree_prev_func, &search_data);
2473 *prev = search_data.iter;
2475 gtk_tree_path_free (search_data.path);
2477 return search_data.set;
2481 tree_last_func (GtkTreeModel *model,
2486 SearchData *search_data = (SearchData *)data;
2488 if (!tree_column_row_is_sensitive (search_data->combo, iter))
2491 /* Note that we rely on the fact that collapsed rows don't have nodes
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;
2504 tree_last (GtkComboBox *combo,
2505 GtkTreeModel *model,
2509 SearchData search_data;
2511 search_data.combo = combo;
2512 search_data.visible = visible;
2513 search_data.set = FALSE;
2515 gtk_tree_model_foreach (model, tree_last_func, &search_data);
2517 *last = search_data.iter;
2519 return search_data.set;
2524 tree_first_func (GtkTreeModel *model,
2529 SearchData *search_data = (SearchData *)data;
2531 if (!tree_column_row_is_sensitive (search_data->combo, iter))
2534 if (search_data->visible &&
2535 !path_visible (GTK_TREE_VIEW (search_data->combo->priv->tree_view), path))
2538 search_data->set = TRUE;
2539 search_data->iter = *iter;
2545 tree_first (GtkComboBox *combo,
2546 GtkTreeModel *model,
2550 SearchData search_data;
2552 search_data.combo = combo;
2553 search_data.visible = visible;
2554 search_data.set = FALSE;
2556 gtk_tree_model_foreach (model, tree_first_func, &search_data);
2558 *first = search_data.iter;
2560 return search_data.set;
2564 gtk_combo_box_scroll_event (GtkWidget *widget,
2565 GdkEventScroll *event)
2567 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
2570 GtkTreeIter new_iter;
2572 if (!gtk_combo_box_get_active_iter (combo_box, &iter))
2575 if (event->direction == GDK_SCROLL_UP)
2576 found = tree_prev (combo_box, combo_box->priv->model,
2577 &iter, &new_iter, FALSE);
2579 found = tree_next (combo_box, combo_box->priv->model,
2580 &iter, &new_iter, FALSE);
2583 gtk_combo_box_set_active_iter (combo_box, &new_iter);
2593 gtk_combo_box_sync_cells (GtkComboBox *combo_box,
2594 GtkCellLayout *cell_layout)
2598 for (k = combo_box->priv->cells; k; k = k->next)
2601 ComboCellInfo *info = (ComboCellInfo *)k->data;
2603 if (info->pack == GTK_PACK_START)
2604 gtk_cell_layout_pack_start (cell_layout,
2605 info->cell, info->expand);
2606 else if (info->pack == GTK_PACK_END)
2607 gtk_cell_layout_pack_end (cell_layout,
2608 info->cell, info->expand);
2610 gtk_cell_layout_set_cell_data_func (cell_layout,
2612 combo_cell_data_func, info, NULL);
2614 for (j = info->attributes; j; j = j->next->next)
2616 gtk_cell_layout_add_attribute (cell_layout,
2619 GPOINTER_TO_INT (j->next->data));
2625 gtk_combo_box_menu_setup (GtkComboBox *combo_box,
2626 gboolean add_children)
2630 if (combo_box->priv->cell_view)
2632 combo_box->priv->button = gtk_toggle_button_new ();
2633 gtk_button_set_focus_on_click (GTK_BUTTON (combo_box->priv->button),
2634 combo_box->priv->focus_on_click);
2636 g_signal_connect (combo_box->priv->button, "toggled",
2637 G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
2638 gtk_widget_set_parent (combo_box->priv->button,
2639 GTK_BIN (combo_box)->child->parent);
2641 combo_box->priv->box = gtk_hbox_new (FALSE, 0);
2642 gtk_container_add (GTK_CONTAINER (combo_box->priv->button),
2643 combo_box->priv->box);
2645 combo_box->priv->separator = gtk_vseparator_new ();
2646 gtk_container_add (GTK_CONTAINER (combo_box->priv->box),
2647 combo_box->priv->separator);
2649 combo_box->priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
2650 gtk_container_add (GTK_CONTAINER (combo_box->priv->box),
2651 combo_box->priv->arrow);
2653 gtk_widget_show_all (combo_box->priv->button);
2657 combo_box->priv->button = gtk_toggle_button_new ();
2658 gtk_button_set_focus_on_click (GTK_BUTTON (combo_box->priv->button),
2659 combo_box->priv->focus_on_click);
2661 g_signal_connect (combo_box->priv->button, "toggled",
2662 G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
2663 gtk_widget_set_parent (combo_box->priv->button,
2664 GTK_BIN (combo_box)->child->parent);
2666 combo_box->priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
2667 gtk_container_add (GTK_CONTAINER (combo_box->priv->button),
2668 combo_box->priv->arrow);
2669 gtk_widget_show_all (combo_box->priv->button);
2672 g_signal_connect (combo_box->priv->button, "button_press_event",
2673 G_CALLBACK (gtk_combo_box_menu_button_press),
2675 g_signal_connect (combo_box->priv->button, "state_changed",
2676 G_CALLBACK (gtk_combo_box_button_state_changed),
2679 /* create our funky menu */
2680 menu = gtk_menu_new ();
2681 gtk_widget_set_name (menu, "gtk-combobox-popup-menu");
2683 g_signal_connect (menu, "key_press_event",
2684 G_CALLBACK (gtk_combo_box_menu_key_press), combo_box);
2685 gtk_combo_box_set_popup_widget (combo_box, menu);
2689 gtk_combo_box_menu_fill (combo_box);
2691 /* the column is needed in tree_column_row_is_sensitive() */
2692 combo_box->priv->column = gtk_tree_view_column_new ();
2693 g_object_ref_sink (combo_box->priv->column);
2694 gtk_combo_box_sync_cells (combo_box,
2695 GTK_CELL_LAYOUT (combo_box->priv->column));
2697 gtk_combo_box_update_title (combo_box);
2701 gtk_combo_box_menu_fill (GtkComboBox *combo_box)
2705 if (!combo_box->priv->model)
2708 menu = combo_box->priv->popup_widget;
2710 if (combo_box->priv->add_tearoffs)
2712 GtkWidget *tearoff = gtk_tearoff_menu_item_new ();
2714 gtk_widget_show (tearoff);
2716 if (combo_box->priv->wrap_width)
2717 gtk_menu_attach (GTK_MENU (menu), tearoff,
2718 0, combo_box->priv->wrap_width, 0, 1);
2720 gtk_menu_shell_append (GTK_MENU_SHELL (menu), tearoff);
2723 gtk_combo_box_menu_fill_level (combo_box, menu, NULL);
2727 gtk_cell_view_menu_item_new (GtkComboBox *combo_box,
2728 GtkTreeModel *model,
2731 GtkWidget *cell_view;
2736 cell_view = gtk_cell_view_new ();
2737 item = gtk_menu_item_new ();
2738 gtk_container_add (GTK_CONTAINER (item), cell_view);
2740 gtk_cell_view_set_model (GTK_CELL_VIEW (cell_view), model);
2741 path = gtk_tree_model_get_path (model, iter);
2742 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (cell_view), path);
2743 gtk_tree_path_free (path);
2745 gtk_combo_box_sync_cells (combo_box, GTK_CELL_LAYOUT (cell_view));
2746 gtk_widget_size_request (cell_view, &req);
2747 gtk_widget_show (cell_view);
2753 gtk_combo_box_menu_fill_level (GtkComboBox *combo_box,
2755 GtkTreeIter *parent)
2757 GtkTreeModel *model = combo_box->priv->model;
2758 GtkWidget *item, *submenu, *subitem, *separator;
2760 gboolean is_separator;
2765 n_children = gtk_tree_model_iter_n_children (model, parent);
2768 for (i = 0; i < n_children; i++)
2770 gtk_tree_model_iter_nth_child (model, &iter, parent, i);
2772 if (combo_box->priv->row_separator_func)
2773 is_separator = (*combo_box->priv->row_separator_func) (combo_box->priv->model, &iter,
2774 combo_box->priv->row_separator_data);
2776 is_separator = FALSE;
2780 item = gtk_separator_menu_item_new ();
2781 path = gtk_tree_model_get_path (model, &iter);
2782 g_object_set_data_full (G_OBJECT (item),
2783 I_("gtk-combo-box-item-path"),
2784 gtk_tree_row_reference_new (model, path),
2785 (GDestroyNotify)gtk_tree_row_reference_free);
2786 gtk_tree_path_free (path);
2790 item = gtk_cell_view_menu_item_new (combo_box, model, &iter);
2791 if (gtk_tree_model_iter_has_child (model, &iter))
2793 submenu = gtk_menu_new ();
2794 gtk_widget_show (submenu);
2795 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu);
2797 /* Ugly - since menus can only activate leafs, we have to
2798 * duplicate the item inside the submenu.
2800 subitem = gtk_cell_view_menu_item_new (combo_box, model, &iter);
2801 separator = gtk_separator_menu_item_new ();
2802 gtk_widget_show (subitem);
2803 gtk_widget_show (separator);
2804 g_signal_connect (subitem, "activate",
2805 G_CALLBACK (gtk_combo_box_menu_item_activate),
2807 gtk_menu_shell_append (GTK_MENU_SHELL (submenu), subitem);
2808 gtk_menu_shell_append (GTK_MENU_SHELL (submenu), separator);
2810 gtk_combo_box_menu_fill_level (combo_box, submenu, &iter);
2812 g_signal_connect (item, "activate",
2813 G_CALLBACK (gtk_combo_box_menu_item_activate),
2817 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
2818 if (combo_box->priv->wrap_width && menu == combo_box->priv->popup_widget)
2819 gtk_combo_box_relayout_item (combo_box, item, &iter, last);
2820 gtk_widget_show (item);
2827 gtk_combo_box_menu_destroy (GtkComboBox *combo_box)
2829 g_signal_handlers_disconnect_matched (combo_box->priv->button,
2830 G_SIGNAL_MATCH_DATA,
2832 gtk_combo_box_menu_button_press, NULL);
2833 g_signal_handlers_disconnect_matched (combo_box->priv->button,
2834 G_SIGNAL_MATCH_DATA,
2836 gtk_combo_box_button_state_changed, combo_box);
2838 /* unparent will remove our latest ref */
2839 gtk_widget_unparent (combo_box->priv->button);
2841 combo_box->priv->box = NULL;
2842 combo_box->priv->button = NULL;
2843 combo_box->priv->arrow = NULL;
2844 combo_box->priv->separator = NULL;
2846 g_object_unref (combo_box->priv->column);
2847 combo_box->priv->column = NULL;
2849 /* changing the popup window will unref the menu and the children */
2857 menu_occupied (GtkMenu *menu,
2861 guint bottom_attach)
2865 for (i = GTK_MENU_SHELL (menu)->children; i; i = i->next)
2869 gtk_container_child_get (GTK_CONTAINER (menu),
2873 "bottom-attach", &b,
2877 /* look if this item intersects with the given coordinates */
2878 if (right_attach > l && left_attach < r && bottom_attach > t && top_attach < b)
2886 gtk_combo_box_relayout_item (GtkComboBox *combo_box,
2891 gint current_col = 0, current_row = 0;
2892 gint rows = 1, cols = 1;
2893 GtkWidget *menu = combo_box->priv->popup_widget;
2895 if (!GTK_IS_MENU_SHELL (menu))
2898 if (combo_box->priv->col_column == -1 &&
2899 combo_box->priv->row_column == -1 &&
2902 gtk_container_child_get (GTK_CONTAINER (menu),
2904 "right-attach", ¤t_col,
2905 "top-attach", ¤t_row,
2907 if (current_col + cols > combo_box->priv->wrap_width)
2915 if (combo_box->priv->col_column != -1)
2916 gtk_tree_model_get (combo_box->priv->model, iter,
2917 combo_box->priv->col_column, &cols,
2919 if (combo_box->priv->row_column != -1)
2920 gtk_tree_model_get (combo_box->priv->model, iter,
2921 combo_box->priv->row_column, &rows,
2926 if (current_col + cols > combo_box->priv->wrap_width)
2932 if (!menu_occupied (GTK_MENU (menu),
2933 current_col, current_col + cols,
2934 current_row, current_row + rows))
2941 /* set attach props */
2942 gtk_menu_attach (GTK_MENU (menu), item,
2943 current_col, current_col + cols,
2944 current_row, current_row + rows);
2948 gtk_combo_box_relayout (GtkComboBox *combo_box)
2953 menu = combo_box->priv->popup_widget;
2955 /* do nothing unless we are in menu style and realized */
2956 if (combo_box->priv->tree_view || !GTK_IS_MENU_SHELL (menu))
2959 list = gtk_container_get_children (GTK_CONTAINER (menu));
2961 for (j = g_list_last (list); j; j = j->prev)
2962 gtk_container_remove (GTK_CONTAINER (menu), j->data);
2964 gtk_combo_box_menu_fill (combo_box);
2971 gtk_combo_box_menu_button_press (GtkWidget *widget,
2972 GdkEventButton *event,
2975 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
2977 if (GTK_IS_MENU (combo_box->priv->popup_widget) &&
2978 event->type == GDK_BUTTON_PRESS && event->button == 1)
2980 if (combo_box->priv->focus_on_click &&
2981 !GTK_WIDGET_HAS_FOCUS (combo_box->priv->button))
2982 gtk_widget_grab_focus (combo_box->priv->button);
2984 gtk_combo_box_menu_popup (combo_box, event->button, event->time);
2993 gtk_combo_box_menu_item_activate (GtkWidget *item,
2996 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
2997 GtkWidget *cell_view;
3001 cell_view = GTK_BIN (item)->child;
3003 g_return_if_fail (GTK_IS_CELL_VIEW (cell_view));
3005 path = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (cell_view));
3007 if (gtk_tree_model_get_iter (combo_box->priv->model, &iter, path))
3009 if (gtk_menu_item_get_submenu (GTK_MENU_ITEM (item)) == NULL)
3010 gtk_combo_box_set_active_iter (combo_box, &iter);
3013 gtk_tree_path_free (path);
3015 combo_box->priv->editing_canceled = FALSE;
3019 gtk_combo_box_model_row_inserted (GtkTreeModel *model,
3024 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3026 if (combo_box->priv->tree_view)
3027 gtk_combo_box_list_popup_resize (combo_box);
3029 gtk_combo_box_menu_row_inserted (model, path, iter, user_data);
3033 gtk_combo_box_model_row_deleted (GtkTreeModel *model,
3037 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3039 if (combo_box->priv->cell_view)
3041 if (!gtk_tree_row_reference_valid (combo_box->priv->active_row))
3042 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (combo_box->priv->cell_view), NULL);
3045 if (combo_box->priv->tree_view)
3046 gtk_combo_box_list_popup_resize (combo_box);
3048 gtk_combo_box_menu_row_deleted (model, path, user_data);
3052 gtk_combo_box_model_rows_reordered (GtkTreeModel *model,
3058 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3060 gtk_tree_row_reference_reordered (G_OBJECT (user_data), path, iter, new_order);
3062 if (!combo_box->priv->tree_view)
3063 gtk_combo_box_menu_rows_reordered (model, path, iter, new_order, user_data);
3067 gtk_combo_box_model_row_changed (GtkTreeModel *model,
3072 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3073 GtkTreePath *active_path;
3075 /* FIXME this belongs to GtkCellView */
3076 if (gtk_tree_row_reference_valid (combo_box->priv->active_row))
3078 active_path = gtk_tree_row_reference_get_path (combo_box->priv->active_row);
3079 if (gtk_tree_path_compare (path, active_path) == 0 &&
3080 combo_box->priv->cell_view)
3081 gtk_widget_queue_resize (GTK_WIDGET (combo_box->priv->cell_view));
3082 gtk_tree_path_free (active_path);
3085 if (combo_box->priv->tree_view)
3086 gtk_combo_box_list_row_changed (model, path, iter, user_data);
3088 gtk_combo_box_menu_row_changed (model, path, iter, user_data);
3092 list_popup_resize_idle (gpointer user_data)
3094 GtkComboBox *combo_box;
3095 gint x, y, width, height;
3097 combo_box = GTK_COMBO_BOX (user_data);
3099 if (combo_box->priv->tree_view &&
3100 GTK_WIDGET_MAPPED (combo_box->priv->popup_window))
3102 gtk_combo_box_list_position (combo_box, &x, &y, &width, &height);
3104 gtk_widget_set_size_request (combo_box->priv->popup_window, width, height);
3105 gtk_window_move (GTK_WINDOW (combo_box->priv->popup_window), x, y);
3108 combo_box->priv->resize_idle_id = 0;
3114 gtk_combo_box_list_popup_resize (GtkComboBox *combo_box)
3116 if (!combo_box->priv->resize_idle_id)
3117 combo_box->priv->resize_idle_id =
3118 gdk_threads_add_idle (list_popup_resize_idle, combo_box);
3122 gtk_combo_box_model_row_expanded (GtkTreeModel *model,
3127 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3129 gtk_combo_box_list_popup_resize (combo_box);
3134 find_menu_by_path (GtkWidget *menu,
3136 gboolean skip_first)
3141 GtkTreeRowReference *mref;
3145 list = gtk_container_get_children (GTK_CONTAINER (menu));
3148 for (i = list; i; i = i->next)
3150 if (GTK_IS_SEPARATOR_MENU_ITEM (i->data))
3152 mref = g_object_get_data (G_OBJECT (i->data), "gtk-combo-box-item-path");
3155 else if (!gtk_tree_row_reference_valid (mref))
3158 mpath = gtk_tree_row_reference_get_path (mref);
3160 else if (GTK_IS_CELL_VIEW (GTK_BIN (i->data)->child))
3168 mpath = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (GTK_BIN (i->data)->child));
3173 /* this case is necessary, since the row reference of
3174 * the cell view may already be updated after a deletion
3181 if (gtk_tree_path_compare (mpath, path) == 0)
3183 gtk_tree_path_free (mpath);
3187 if (gtk_tree_path_is_ancestor (mpath, path))
3189 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
3190 if (submenu != NULL)
3192 gtk_tree_path_free (mpath);
3193 item = find_menu_by_path (submenu, path, TRUE);
3197 gtk_tree_path_free (mpath);
3207 dump_menu_tree (GtkWidget *menu,
3214 list = gtk_container_get_children (GTK_CONTAINER (menu));
3215 for (i = list; i; i = i->next)
3217 if (GTK_IS_CELL_VIEW (GTK_BIN (i->data)->child))
3219 path = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (GTK_BIN (i->data)->child));
3220 g_print ("%*s%s\n", 2 * level, " ", gtk_tree_path_to_string (path));
3221 gtk_tree_path_free (path);
3223 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
3224 if (submenu != NULL)
3225 dump_menu_tree (submenu, level + 1);
3234 gtk_combo_box_menu_row_inserted (GtkTreeModel *model,
3240 GtkWidget *item, *menu, *separator;
3241 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3245 gboolean is_separator;
3247 if (!combo_box->priv->popup_widget)
3250 depth = gtk_tree_path_get_depth (path);
3251 pos = gtk_tree_path_get_indices (path)[depth - 1];
3254 ppath = gtk_tree_path_copy (path);
3255 gtk_tree_path_up (ppath);
3256 parent = find_menu_by_path (combo_box->priv->popup_widget, ppath, FALSE);
3257 gtk_tree_path_free (ppath);
3259 menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (parent));
3262 menu = gtk_menu_new ();
3263 gtk_widget_show (menu);
3264 gtk_menu_item_set_submenu (GTK_MENU_ITEM (parent), menu);
3266 /* Ugly - since menus can only activate leaves, we have to
3267 * duplicate the item inside the submenu.
3269 gtk_tree_model_iter_parent (model, &piter, iter);
3270 item = gtk_cell_view_menu_item_new (combo_box, model, &piter);
3271 separator = gtk_separator_menu_item_new ();
3272 g_signal_connect (item, "activate",
3273 G_CALLBACK (gtk_combo_box_menu_item_activate),
3275 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
3276 gtk_menu_shell_append (GTK_MENU_SHELL (menu), separator);
3277 if (cell_view_is_sensitive (GTK_CELL_VIEW (GTK_BIN (item)->child)))
3279 gtk_widget_show (item);
3280 gtk_widget_show (separator);
3287 menu = combo_box->priv->popup_widget;
3288 if (combo_box->priv->add_tearoffs)
3292 if (combo_box->priv->row_separator_func)
3293 is_separator = (*combo_box->priv->row_separator_func) (model, iter,
3294 combo_box->priv->row_separator_data);
3296 is_separator = FALSE;
3300 item = gtk_separator_menu_item_new ();
3301 g_object_set_data_full (G_OBJECT (item),
3302 I_("gtk-combo-box-item-path"),
3303 gtk_tree_row_reference_new (model, path),
3304 (GDestroyNotify)gtk_tree_row_reference_free);
3308 item = gtk_cell_view_menu_item_new (combo_box, model, iter);
3310 g_signal_connect (item, "activate",
3311 G_CALLBACK (gtk_combo_box_menu_item_activate),
3315 gtk_widget_show (item);
3316 gtk_menu_shell_insert (GTK_MENU_SHELL (menu), item, pos);
3320 gtk_combo_box_menu_row_deleted (GtkTreeModel *model,
3324 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3328 if (!combo_box->priv->popup_widget)
3331 item = find_menu_by_path (combo_box->priv->popup_widget, path, FALSE);
3332 menu = gtk_widget_get_parent (item);
3333 gtk_container_remove (GTK_CONTAINER (menu), item);
3335 if (gtk_tree_path_get_depth (path) > 1)
3337 GtkTreePath *parent_path;
3341 parent_path = gtk_tree_path_copy (path);
3342 gtk_tree_path_up (parent_path);
3343 gtk_tree_model_get_iter (model, &iter, parent_path);
3345 if (!gtk_tree_model_iter_has_child (model, &iter))
3347 parent = find_menu_by_path (combo_box->priv->popup_widget,
3348 parent_path, FALSE);
3349 gtk_menu_item_remove_submenu (GTK_MENU_ITEM (parent));
3355 gtk_combo_box_menu_rows_reordered (GtkTreeModel *model,
3361 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3363 gtk_combo_box_relayout (combo_box);
3367 gtk_combo_box_menu_row_changed (GtkTreeModel *model,
3372 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
3375 gboolean is_separator;
3377 if (!combo_box->priv->popup_widget)
3380 item = find_menu_by_path (combo_box->priv->popup_widget, path, FALSE);
3382 if (combo_box->priv->row_separator_func)
3383 is_separator = (*combo_box->priv->row_separator_func) (model, iter,
3384 combo_box->priv->row_separator_data);
3386 is_separator = FALSE;
3388 if (is_separator != GTK_IS_SEPARATOR_MENU_ITEM (item))
3390 gtk_combo_box_menu_row_deleted (model, path, combo_box);
3391 gtk_combo_box_menu_row_inserted (model, path, iter, combo_box);
3394 if (combo_box->priv->wrap_width
3395 && item->parent == combo_box->priv->popup_widget)
3397 GtkWidget *pitem = NULL;
3400 prev = gtk_tree_path_copy (path);
3402 if (gtk_tree_path_prev (prev))
3403 pitem = find_menu_by_path (combo_box->priv->popup_widget, prev, FALSE);
3405 gtk_tree_path_free (prev);
3407 /* unattach item so gtk_combo_box_relayout_item() won't spuriously
3409 gtk_container_child_set (GTK_CONTAINER (combo_box->priv->popup_widget),
3414 "bottom-attach", -1,
3417 gtk_combo_box_relayout_item (combo_box, item, iter, pitem);
3420 width = gtk_combo_box_calc_requested_width (combo_box, path);
3422 if (width > combo_box->priv->width)
3424 if (combo_box->priv->cell_view)
3426 gtk_widget_set_size_request (combo_box->priv->cell_view, width, -1);
3427 gtk_widget_queue_resize (combo_box->priv->cell_view);
3429 combo_box->priv->width = width;
3438 gtk_combo_box_list_setup (GtkComboBox *combo_box)
3440 GtkTreeSelection *sel;
3442 combo_box->priv->button = gtk_toggle_button_new ();
3443 gtk_widget_set_parent (combo_box->priv->button,
3444 GTK_BIN (combo_box)->child->parent);
3445 g_signal_connect (combo_box->priv->button, "button_press_event",
3446 G_CALLBACK (gtk_combo_box_list_button_pressed), combo_box);
3447 g_signal_connect (combo_box->priv->button, "toggled",
3448 G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
3450 combo_box->priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
3451 gtk_container_add (GTK_CONTAINER (combo_box->priv->button),
3452 combo_box->priv->arrow);
3453 combo_box->priv->separator = NULL;
3454 gtk_widget_show_all (combo_box->priv->button);
3456 if (combo_box->priv->cell_view)
3458 gtk_cell_view_set_background_color (GTK_CELL_VIEW (combo_box->priv->cell_view),
3459 >K_WIDGET (combo_box)->style->base[GTK_WIDGET_STATE (combo_box)]);
3461 combo_box->priv->box = gtk_event_box_new ();
3462 gtk_event_box_set_visible_window (GTK_EVENT_BOX (combo_box->priv->box),
3465 if (combo_box->priv->has_frame)
3467 combo_box->priv->cell_view_frame = gtk_frame_new (NULL);
3468 gtk_frame_set_shadow_type (GTK_FRAME (combo_box->priv->cell_view_frame),
3473 combo_box->priv->cell_view_frame = gtk_event_box_new ();
3474 gtk_event_box_set_visible_window (GTK_EVENT_BOX (combo_box->priv->cell_view_frame),
3478 gtk_widget_set_parent (combo_box->priv->cell_view_frame,
3479 GTK_BIN (combo_box)->child->parent);
3480 gtk_container_add (GTK_CONTAINER (combo_box->priv->cell_view_frame),
3481 combo_box->priv->box);
3482 gtk_widget_show_all (combo_box->priv->cell_view_frame);
3484 g_signal_connect (combo_box->priv->box, "button_press_event",
3485 G_CALLBACK (gtk_combo_box_list_button_pressed),
3489 combo_box->priv->tree_view = gtk_tree_view_new ();
3490 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (combo_box->priv->tree_view));
3491 gtk_tree_selection_set_mode (sel, GTK_SELECTION_BROWSE);
3492 gtk_tree_selection_set_select_function (sel,
3493 gtk_combo_box_list_select_func,
3495 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (combo_box->priv->tree_view),
3497 gtk_tree_view_set_hover_selection (GTK_TREE_VIEW (combo_box->priv->tree_view),
3499 if (combo_box->priv->row_separator_func)
3500 gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (combo_box->priv->tree_view),
3501 combo_box->priv->row_separator_func,
3502 combo_box->priv->row_separator_data,
3504 if (combo_box->priv->model)
3505 gtk_tree_view_set_model (GTK_TREE_VIEW (combo_box->priv->tree_view),
3506 combo_box->priv->model);
3508 combo_box->priv->column = gtk_tree_view_column_new ();
3509 gtk_tree_view_append_column (GTK_TREE_VIEW (combo_box->priv->tree_view),
3510 combo_box->priv->column);
3513 gtk_combo_box_sync_cells (combo_box,
3514 GTK_CELL_LAYOUT (combo_box->priv->column));
3516 if (gtk_tree_row_reference_valid (combo_box->priv->active_row))
3520 path = gtk_tree_row_reference_get_path (combo_box->priv->active_row);
3521 gtk_tree_view_set_cursor (GTK_TREE_VIEW (combo_box->priv->tree_view),
3523 gtk_tree_path_free (path);
3526 /* set sample/popup widgets */
3527 gtk_combo_box_set_popup_widget (combo_box, combo_box->priv->tree_view);
3529 g_signal_connect (combo_box->priv->tree_view, "key_press_event",
3530 G_CALLBACK (gtk_combo_box_list_key_press),
3532 g_signal_connect (combo_box->priv->tree_view, "enter_notify_event",
3533 G_CALLBACK (gtk_combo_box_list_enter_notify),
3535 g_signal_connect (combo_box->priv->tree_view, "row_expanded",
3536 G_CALLBACK (gtk_combo_box_model_row_expanded),
3538 g_signal_connect (combo_box->priv->tree_view, "row_collapsed",
3539 G_CALLBACK (gtk_combo_box_model_row_expanded),
3541 g_signal_connect (combo_box->priv->popup_window, "button_press_event",
3542 G_CALLBACK (gtk_combo_box_list_button_pressed),
3544 g_signal_connect (combo_box->priv->popup_window, "button_release_event",
3545 G_CALLBACK (gtk_combo_box_list_button_released),
3548 gtk_widget_show (combo_box->priv->tree_view);
3552 gtk_combo_box_list_destroy (GtkComboBox *combo_box)
3554 /* disconnect signals */
3555 g_signal_handlers_disconnect_matched (combo_box->priv->tree_view,
3556 G_SIGNAL_MATCH_DATA,
3557 0, 0, NULL, NULL, combo_box);
3558 g_signal_handlers_disconnect_matched (combo_box->priv->button,
3559 G_SIGNAL_MATCH_DATA,
3561 gtk_combo_box_list_button_pressed,
3563 g_signal_handlers_disconnect_matched (combo_box->priv->popup_window,
3564 G_SIGNAL_MATCH_DATA,
3566 gtk_combo_box_list_button_pressed,
3568 g_signal_handlers_disconnect_matched (combo_box->priv->popup_window,
3569 G_SIGNAL_MATCH_DATA,
3571 gtk_combo_box_list_button_released,
3574 g_signal_handlers_disconnect_matched (combo_box->priv->popup_window,
3575 G_SIGNAL_MATCH_DATA,
3577 gtk_combo_box_child_show,
3580 g_signal_handlers_disconnect_matched (combo_box->priv->popup_window,
3581 G_SIGNAL_MATCH_DATA,
3583 gtk_combo_box_child_hide,
3586 if (combo_box->priv->box)
3587 g_signal_handlers_disconnect_matched (combo_box->priv->box,
3588 G_SIGNAL_MATCH_DATA,
3590 gtk_combo_box_list_button_pressed,
3593 /* destroy things (unparent will kill the latest ref from us)
3594 * last unref on button will destroy the arrow
3596 gtk_widget_unparent (combo_box->priv->button);
3597 combo_box->priv->button = NULL;
3598 combo_box->priv->arrow = NULL;
3600 if (combo_box->priv->cell_view)
3602 g_object_set (combo_box->priv->cell_view,
3603 "background-set", FALSE,
3607 if (combo_box->priv->cell_view_frame)
3609 gtk_widget_unparent (combo_box->priv->cell_view_frame);
3610 combo_box->priv->cell_view_frame = NULL;
3611 combo_box->priv->box = NULL;
3614 if (combo_box->priv->scroll_timer)
3616 g_source_remove (combo_box->priv->scroll_timer);
3617 combo_box->priv->scroll_timer = 0;
3620 if (combo_box->priv->resize_idle_id)
3622 g_source_remove (combo_box->priv->resize_idle_id);
3623 combo_box->priv->resize_idle_id = 0;
3626 gtk_widget_destroy (combo_box->priv->tree_view);
3628 combo_box->priv->tree_view = NULL;
3629 if (combo_box->priv->popup_widget)
3631 g_object_unref (combo_box->priv->popup_widget);
3632 combo_box->priv->popup_widget = NULL;
3639 gtk_combo_box_list_button_pressed (GtkWidget *widget,
3640 GdkEventButton *event,
3643 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
3645 GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *)event);
3647 if (ewidget == combo_box->priv->popup_window)
3650 if ((ewidget != combo_box->priv->button && ewidget != combo_box->priv->box) ||
3651 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (combo_box->priv->button)))
3654 if (combo_box->priv->focus_on_click &&
3655 !GTK_WIDGET_HAS_FOCUS (combo_box->priv->button))
3656 gtk_widget_grab_focus (combo_box->priv->button);
3658 gtk_combo_box_popup (combo_box);
3660 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
3663 combo_box->priv->auto_scroll = FALSE;
3664 if (combo_box->priv->scroll_timer == 0)
3665 combo_box->priv->scroll_timer = gdk_threads_add_timeout (SCROLL_TIME,
3666 (GSourceFunc) gtk_combo_box_list_scroll_timeout,
3669 combo_box->priv->popup_in_progress = TRUE;
3675 gtk_combo_box_list_button_released (GtkWidget *widget,
3676 GdkEventButton *event,
3680 GtkTreePath *path = NULL;
3683 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
3685 gboolean popup_in_progress = FALSE;
3687 GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *)event);
3689 if (combo_box->priv->popup_in_progress)
3691 popup_in_progress = TRUE;
3692 combo_box->priv->popup_in_progress = FALSE;
3695 gtk_tree_view_set_hover_expand (GTK_TREE_VIEW (combo_box->priv->tree_view),
3697 if (combo_box->priv->scroll_timer)
3699 g_source_remove (combo_box->priv->scroll_timer);
3700 combo_box->priv->scroll_timer = 0;
3703 if (ewidget != combo_box->priv->tree_view)
3705 if ((ewidget == combo_box->priv->button ||
3706 ewidget == combo_box->priv->box) &&
3707 !popup_in_progress &&
3708 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (combo_box->priv->button)))
3710 gtk_combo_box_popdown (combo_box);
3714 /* released outside treeview */
3715 if (ewidget != combo_box->priv->button &&
3716 ewidget != combo_box->priv->box)
3718 gtk_combo_box_popdown (combo_box);
3726 /* select something cool */
3727 ret = gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (combo_box->priv->tree_view),
3733 return TRUE; /* clicked outside window? */
3735 gtk_tree_model_get_iter (combo_box->priv->model, &iter, path);
3736 gtk_tree_path_free (path);
3738 gtk_combo_box_popdown (combo_box);
3740 if (tree_column_row_is_sensitive (combo_box, &iter))
3741 gtk_combo_box_set_active_iter (combo_box, &iter);
3747 gtk_combo_box_menu_key_press (GtkWidget *widget,
3751 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
3752 guint state = event->state & gtk_accelerator_get_default_mod_mask ();
3754 if ((event->keyval == GDK_Up || event->keyval == GDK_KP_Up) &&
3755 state == GDK_MOD1_MASK)
3757 gtk_combo_box_popdown (combo_box);
3766 gtk_combo_box_list_key_press (GtkWidget *widget,
3770 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
3772 guint state = event->state & gtk_accelerator_get_default_mod_mask ();
3774 if (event->keyval == GDK_Escape ||
3775 ((event->keyval == GDK_Up || event->keyval == GDK_KP_Up) &&
3776 state == GDK_MOD1_MASK))
3778 gtk_combo_box_popdown (combo_box);
3780 /* reset active item -- this is incredibly lame and ugly */
3781 if (gtk_combo_box_get_active_iter (combo_box, &iter))
3782 gtk_combo_box_set_active_iter (combo_box, &iter);
3787 if (event->keyval == GDK_Return || event->keyval == GDK_KP_Enter ||
3788 event->keyval == GDK_space || event->keyval == GDK_KP_Space)
3790 GtkTreeModel *model = NULL;
3792 gtk_combo_box_popdown (combo_box);
3794 if (combo_box->priv->model)
3796 GtkTreeSelection *sel;
3798 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (combo_box->priv->tree_view));
3800 if (gtk_tree_selection_get_selected (sel, &model, &iter))
3801 gtk_combo_box_set_active_iter (combo_box, &iter);
3811 gtk_combo_box_list_auto_scroll (GtkComboBox *combo_box,
3815 GtkWidget *tree_view = combo_box->priv->tree_view;
3819 adj = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window));
3820 if (adj && adj->upper - adj->lower > adj->page_size)
3822 if (x <= tree_view->allocation.x &&
3823 adj->lower < adj->value)
3825 value = adj->value - (tree_view->allocation.x - x + 1);
3826 gtk_adjustment_set_value (adj, CLAMP (value, adj->lower, adj->upper - adj->page_size));
3828 else if (x >= tree_view->allocation.x + tree_view->allocation.width &&
3829 adj->upper - adj->page_size > adj->value)
3831 value = adj->value + (x - tree_view->allocation.x - tree_view->allocation.width + 1);
3832 gtk_adjustment_set_value (adj, CLAMP (value, 0.0, adj->upper - adj->page_size));
3836 adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (combo_box->priv->scrolled_window));
3837 if (adj && adj->upper - adj->lower > adj->page_size)
3839 if (y <= tree_view->allocation.y &&
3840 adj->lower < adj->value)
3842 value = adj->value - (tree_view->allocation.y - y + 1);
3843 gtk_adjustment_set_value (adj, CLAMP (value, adj->lower, adj->upper - adj->page_size));
3845 else if (y >= tree_view->allocation.height &&
3846 adj->upper - adj->page_size > adj->value)
3848 value = adj->value + (y - tree_view->allocation.height + 1);
3849 gtk_adjustment_set_value (adj, CLAMP (value, 0.0, adj->upper - adj->page_size));
3855 gtk_combo_box_list_scroll_timeout (GtkComboBox *combo_box)
3859 if (combo_box->priv->auto_scroll)
3861 gdk_window_get_pointer (combo_box->priv->tree_view->window,
3863 gtk_combo_box_list_auto_scroll (combo_box, x, y);
3870 gtk_combo_box_list_enter_notify (GtkWidget *widget,
3871 GdkEventCrossing *event,
3874 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
3876 combo_box->priv->auto_scroll = TRUE;
3882 gtk_combo_box_list_select_func (GtkTreeSelection *selection,
3883 GtkTreeModel *model,
3885 gboolean path_currently_selected,
3889 gboolean sensitive = FALSE;
3891 for (list = selection->tree_view->priv->columns; list && !sensitive; list = list->next)
3893 GList *cells, *cell;
3894 gboolean cell_sensitive, cell_visible;
3896 GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN (list->data);
3898 if (!column->visible)
3901 gtk_tree_model_get_iter (model, &iter, path);
3902 gtk_tree_view_column_cell_set_cell_data (column, model, &iter,
3905 cell = cells = gtk_tree_view_column_get_cell_renderers (column);
3908 g_object_get (cell->data,
3909 "sensitive", &cell_sensitive,
3910 "visible", &cell_visible,
3913 if (cell_visible && cell_sensitive)
3918 g_list_free (cells);
3920 sensitive = cell_sensitive;
3927 gtk_combo_box_list_row_changed (GtkTreeModel *model,
3932 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
3935 width = gtk_combo_box_calc_requested_width (combo_box, path);
3937 if (width > combo_box->priv->width)
3939 if (combo_box->priv->cell_view)
3941 gtk_widget_set_size_request (combo_box->priv->cell_view, width, -1);
3942 gtk_widget_queue_resize (combo_box->priv->cell_view);
3944 combo_box->priv->width = width;
3949 * GtkCellLayout implementation
3953 pack_start_recurse (GtkWidget *menu,
3954 GtkCellRenderer *cell,
3960 list = gtk_container_get_children (GTK_CONTAINER (menu));
3961 for (i = list; i; i = i->next)
3963 if (GTK_IS_CELL_LAYOUT (GTK_BIN (i->data)->child))
3964 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (GTK_BIN (i->data)->child),
3967 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
3968 if (submenu != NULL)
3969 pack_start_recurse (submenu, cell, expand);
3976 gtk_combo_box_cell_layout_pack_start (GtkCellLayout *layout,
3977 GtkCellRenderer *cell,
3980 ComboCellInfo *info;
3981 GtkComboBox *combo_box;
3983 g_return_if_fail (GTK_IS_COMBO_BOX (layout));
3984 g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
3986 combo_box = GTK_COMBO_BOX (layout);
3988 g_object_ref_sink (cell);
3990 info = g_new0 (ComboCellInfo, 1);
3992 info->expand = expand;
3993 info->pack = GTK_PACK_START;
3995 combo_box->priv->cells = g_slist_append (combo_box->priv->cells, info);
3997 if (combo_box->priv->cell_view)
3998 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box->priv->cell_view),
4001 if (combo_box->priv->column)
4002 gtk_tree_view_column_pack_start (combo_box->priv->column, cell, expand);
4004 if (GTK_IS_MENU (combo_box->priv->popup_widget))
4005 pack_start_recurse (combo_box->priv->popup_widget, cell, expand);
4009 pack_end_recurse (GtkWidget *menu,
4010 GtkCellRenderer *cell,
4016 list = gtk_container_get_children (GTK_CONTAINER (menu));
4017 for (i = list; i; i = i->next)
4019 if (GTK_IS_CELL_LAYOUT (GTK_BIN (i->data)->child))
4020 gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (GTK_BIN (i->data)->child),
4023 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4024 if (submenu != NULL)
4025 pack_end_recurse (submenu, cell, expand);
4032 gtk_combo_box_cell_layout_pack_end (GtkCellLayout *layout,
4033 GtkCellRenderer *cell,
4036 ComboCellInfo *info;
4037 GtkComboBox *combo_box;
4039 g_return_if_fail (GTK_IS_COMBO_BOX (layout));
4040 g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
4042 combo_box = GTK_COMBO_BOX (layout);
4044 g_object_ref_sink (cell);
4046 info = g_new0 (ComboCellInfo, 1);
4048 info->expand = expand;
4049 info->pack = GTK_PACK_END;
4051 combo_box->priv->cells = g_slist_append (combo_box->priv->cells, info);
4053 if (combo_box->priv->cell_view)
4054 gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (combo_box->priv->cell_view),
4057 if (combo_box->priv->column)
4058 gtk_tree_view_column_pack_end (combo_box->priv->column, cell, expand);
4060 if (GTK_IS_MENU (combo_box->priv->popup_widget))
4061 pack_end_recurse (combo_box->priv->popup_widget, cell, expand);
4065 clear_recurse (GtkWidget *menu)
4070 list = gtk_container_get_children (GTK_CONTAINER (menu));
4071 for (i = list; i; i = i->next)
4073 if (GTK_IS_CELL_LAYOUT (GTK_BIN (i->data)->child))
4074 gtk_cell_layout_clear (GTK_CELL_LAYOUT (GTK_BIN (i->data)->child));
4076 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4077 if (submenu != NULL)
4078 clear_recurse (submenu);
4085 gtk_combo_box_cell_layout_clear (GtkCellLayout *layout)
4087 GtkComboBox *combo_box;
4090 g_return_if_fail (GTK_IS_COMBO_BOX (layout));
4092 combo_box = GTK_COMBO_BOX (layout);
4094 if (combo_box->priv->cell_view)
4095 gtk_cell_layout_clear (GTK_CELL_LAYOUT (combo_box->priv->cell_view));
4097 if (combo_box->priv->column)
4098 gtk_tree_view_column_clear (combo_box->priv->column);
4100 for (i = combo_box->priv->cells; i; i = i->next)
4102 ComboCellInfo *info = (ComboCellInfo *)i->data;
4104 gtk_combo_box_cell_layout_clear_attributes (layout, info->cell);
4105 g_object_unref (info->cell);
4109 g_slist_free (combo_box->priv->cells);
4110 combo_box->priv->cells = NULL;
4112 if (GTK_IS_MENU (combo_box->priv->popup_widget))
4113 clear_recurse (combo_box->priv->popup_widget);
4117 add_attribute_recurse (GtkWidget *menu,
4118 GtkCellRenderer *cell,
4119 const gchar *attribute,
4125 list = gtk_container_get_children (GTK_CONTAINER (menu));
4126 for (i = list; i; i = i->next)
4128 if (GTK_IS_CELL_LAYOUT (GTK_BIN (i->data)->child))
4129 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (GTK_BIN (i->data)->child),
4130 cell, attribute, column);
4132 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4133 if (submenu != NULL)
4134 add_attribute_recurse (submenu, cell, attribute, column);
4141 gtk_combo_box_cell_layout_add_attribute (GtkCellLayout *layout,
4142 GtkCellRenderer *cell,
4143 const gchar *attribute,
4146 ComboCellInfo *info;
4147 GtkComboBox *combo_box;
4149 g_return_if_fail (GTK_IS_COMBO_BOX (layout));
4150 g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
4152 combo_box = GTK_COMBO_BOX (layout);
4154 info = gtk_combo_box_get_cell_info (combo_box, cell);
4156 info->attributes = g_slist_prepend (info->attributes,
4157 GINT_TO_POINTER (column));
4158 info->attributes = g_slist_prepend (info->attributes,
4159 g_strdup (attribute));
4161 if (combo_box->priv->cell_view)
4162 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box->priv->cell_view),
4163 cell, attribute, column);
4165 if (combo_box->priv->column)
4166 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box->priv->column),
4167 cell, attribute, column);
4169 if (GTK_IS_MENU (combo_box->priv->popup_widget))
4170 add_attribute_recurse (combo_box->priv->popup_widget, cell, attribute, column);
4171 gtk_widget_queue_resize (GTK_WIDGET (combo_box));
4175 combo_cell_data_func (GtkCellLayout *cell_layout,
4176 GtkCellRenderer *cell,
4177 GtkTreeModel *tree_model,
4181 ComboCellInfo *info = (ComboCellInfo *)data;
4182 GtkWidget *parent = NULL;
4187 (*info->func) (cell_layout, cell, tree_model, iter, info->func_data);
4189 if (GTK_IS_WIDGET (cell_layout))
4190 parent = gtk_widget_get_parent (GTK_WIDGET (cell_layout));
4192 if (GTK_IS_MENU_ITEM (parent) &&
4193 gtk_menu_item_get_submenu (GTK_MENU_ITEM (parent)))
4194 g_object_set (cell, "sensitive", TRUE, NULL);
4199 set_cell_data_func_recurse (GtkWidget *menu,
4200 GtkCellRenderer *cell,
4201 ComboCellInfo *info)
4205 GtkWidget *cell_view;
4207 list = gtk_container_get_children (GTK_CONTAINER (menu));
4208 for (i = list; i; i = i->next)
4210 cell_view = GTK_BIN (i->data)->child;
4211 if (GTK_IS_CELL_LAYOUT (cell_view))
4213 /* Override sensitivity for inner nodes; we don't
4214 * want menuitems with submenus to appear insensitive
4216 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (cell_view),
4218 combo_cell_data_func,
4220 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4221 if (submenu != NULL)
4222 set_cell_data_func_recurse (submenu, cell, info);
4230 gtk_combo_box_cell_layout_set_cell_data_func (GtkCellLayout *layout,
4231 GtkCellRenderer *cell,
4232 GtkCellLayoutDataFunc func,
4234 GDestroyNotify destroy)
4236 ComboCellInfo *info;
4237 GtkComboBox *combo_box;
4239 g_return_if_fail (GTK_IS_COMBO_BOX (layout));
4241 combo_box = GTK_COMBO_BOX (layout);
4243 info = gtk_combo_box_get_cell_info (combo_box, cell);
4244 g_return_if_fail (info != NULL);
4248 GDestroyNotify d = info->destroy;
4250 info->destroy = NULL;
4251 d (info->func_data);
4255 info->func_data = func_data;
4256 info->destroy = destroy;
4258 if (combo_box->priv->cell_view)
4259 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo_box->priv->cell_view), cell, func, func_data, NULL);
4261 if (combo_box->priv->column)
4262 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo_box->priv->column), cell, func, func_data, NULL);
4264 if (GTK_IS_MENU (combo_box->priv->popup_widget))
4265 set_cell_data_func_recurse (combo_box->priv->popup_widget, cell, info);
4267 gtk_widget_queue_resize (GTK_WIDGET (combo_box));
4271 clear_attributes_recurse (GtkWidget *menu,
4272 GtkCellRenderer *cell)
4277 list = gtk_container_get_children (GTK_CONTAINER (menu));
4278 for (i = list; i; i = i->next)
4280 if (GTK_IS_CELL_LAYOUT (GTK_BIN (i->data)->child))
4281 gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (GTK_BIN (i->data)->child),
4284 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4285 if (submenu != NULL)
4286 clear_attributes_recurse (submenu, cell);
4293 gtk_combo_box_cell_layout_clear_attributes (GtkCellLayout *layout,
4294 GtkCellRenderer *cell)
4296 ComboCellInfo *info;
4297 GtkComboBox *combo_box;
4300 g_return_if_fail (GTK_IS_COMBO_BOX (layout));
4301 g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
4303 combo_box = GTK_COMBO_BOX (layout);
4305 info = gtk_combo_box_get_cell_info (combo_box, cell);
4306 g_return_if_fail (info != NULL);
4308 list = info->attributes;
4309 while (list && list->next)
4311 g_free (list->data);
4312 list = list->next->next;
4314 g_slist_free (info->attributes);
4315 info->attributes = NULL;
4317 if (combo_box->priv->cell_view)
4318 gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (combo_box->priv->cell_view), cell);
4320 if (combo_box->priv->column)
4321 gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (combo_box->priv->column), cell);
4323 if (GTK_IS_MENU (combo_box->priv->popup_widget))
4324 clear_attributes_recurse (combo_box->priv->popup_widget, cell);
4326 gtk_widget_queue_resize (GTK_WIDGET (combo_box));
4330 reorder_recurse (GtkWidget *menu,
4331 GtkCellRenderer *cell,
4337 list = gtk_container_get_children (GTK_CONTAINER (menu));
4338 for (i = list; i; i = i->next)
4340 if (GTK_IS_CELL_LAYOUT (GTK_BIN (i->data)->child))
4341 gtk_cell_layout_reorder (GTK_CELL_LAYOUT (GTK_BIN (i->data)->child),
4344 submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (i->data));
4345 if (submenu != NULL)
4346 reorder_recurse (submenu, cell, position);
4353 gtk_combo_box_cell_layout_reorder (GtkCellLayout *layout,
4354 GtkCellRenderer *cell,
4357 ComboCellInfo *info;
4358 GtkComboBox *combo_box;
4361 g_return_if_fail (GTK_IS_COMBO_BOX (layout));
4362 g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
4364 combo_box = GTK_COMBO_BOX (layout);
4366 info = gtk_combo_box_get_cell_info (combo_box, cell);
4368 g_return_if_fail (info != NULL);
4369 g_return_if_fail (position >= 0);
4371 link = g_slist_find (combo_box->priv->cells, info);
4373 g_return_if_fail (link != NULL);
4375 combo_box->priv->cells = g_slist_remove_link (combo_box->priv->cells, link);
4376 combo_box->priv->cells = g_slist_insert (combo_box->priv->cells, info,
4379 if (combo_box->priv->cell_view)
4380 gtk_cell_layout_reorder (GTK_CELL_LAYOUT (combo_box->priv->cell_view),
4383 if (combo_box->priv->column)
4384 gtk_cell_layout_reorder (GTK_CELL_LAYOUT (combo_box->priv->column),
4387 if (GTK_IS_MENU (combo_box->priv->popup_widget))
4388 reorder_recurse (combo_box->priv->popup_widget, cell, position);
4390 gtk_widget_queue_draw (GTK_WIDGET (combo_box));
4398 * gtk_combo_box_new:
4400 * Creates a new empty #GtkComboBox.
4402 * Return value: A new #GtkComboBox.
4407 gtk_combo_box_new (void)
4409 return g_object_new (GTK_TYPE_COMBO_BOX, NULL);
4413 * gtk_combo_box_new_with_model:
4414 * @model: A #GtkTreeModel.
4416 * Creates a new #GtkComboBox with the model initialized to @model.
4418 * Return value: A new #GtkComboBox.
4423 gtk_combo_box_new_with_model (GtkTreeModel *model)
4425 GtkComboBox *combo_box;
4427 g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL);
4429 combo_box = g_object_new (GTK_TYPE_COMBO_BOX, "model", model, NULL);
4431 return GTK_WIDGET (combo_box);
4435 * gtk_combo_box_get_wrap_width:
4436 * @combo_box: A #GtkComboBox.
4438 * Returns the wrap width which is used to determine the number
4439 * of columns for the popup menu. If the wrap width is larger than
4440 * 1, the combo box is in table mode.
4442 * Returns: the wrap width.
4447 gtk_combo_box_get_wrap_width (GtkComboBox *combo_box)
4449 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), -1);
4451 return combo_box->priv->wrap_width;
4455 * gtk_combo_box_set_wrap_width:
4456 * @combo_box: A #GtkComboBox.
4457 * @width: Preferred number of columns.
4459 * Sets the wrap width of @combo_box to be @width. The wrap width is basically
4460 * the preferred number of columns when you want the popup to be layed out
4466 gtk_combo_box_set_wrap_width (GtkComboBox *combo_box,
4469 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4470 g_return_if_fail (width >= 0);
4472 if (width != combo_box->priv->wrap_width)
4474 combo_box->priv->wrap_width = width;
4476 gtk_combo_box_check_appearance (combo_box);
4477 gtk_combo_box_relayout (combo_box);
4479 g_object_notify (G_OBJECT (combo_box), "wrap-width");
4484 * gtk_combo_box_get_row_span_column:
4485 * @combo_box: A #GtkComboBox.
4487 * Returns the column with row span information for @combo_box.
4489 * Returns: the row span column.
4494 gtk_combo_box_get_row_span_column (GtkComboBox *combo_box)
4496 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), -1);
4498 return combo_box->priv->row_column;
4502 * gtk_combo_box_set_row_span_column:
4503 * @combo_box: A #GtkComboBox.
4504 * @row_span: A column in the model passed during construction.
4506 * Sets the column with row span information for @combo_box to be @row_span.
4507 * The row span column contains integers which indicate how many rows
4508 * an item should span.
4513 gtk_combo_box_set_row_span_column (GtkComboBox *combo_box,
4518 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4520 col = gtk_tree_model_get_n_columns (combo_box->priv->model);
4521 g_return_if_fail (row_span >= -1 && row_span < col);
4523 if (row_span != combo_box->priv->row_column)
4525 combo_box->priv->row_column = row_span;
4527 gtk_combo_box_relayout (combo_box);
4529 g_object_notify (G_OBJECT (combo_box), "row-span-column");
4534 * gtk_combo_box_get_column_span_column:
4535 * @combo_box: A #GtkComboBox.
4537 * Returns the column with column span information for @combo_box.
4539 * Returns: the column span column.
4544 gtk_combo_box_get_column_span_column (GtkComboBox *combo_box)
4546 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), -1);
4548 return combo_box->priv->col_column;
4552 * gtk_combo_box_set_column_span_column:
4553 * @combo_box: A #GtkComboBox.
4554 * @column_span: A column in the model passed during construction.
4556 * Sets the column with column span information for @combo_box to be
4557 * @column_span. The column span column contains integers which indicate
4558 * how many columns an item should span.
4563 gtk_combo_box_set_column_span_column (GtkComboBox *combo_box,
4568 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4570 col = gtk_tree_model_get_n_columns (combo_box->priv->model);
4571 g_return_if_fail (column_span >= -1 && column_span < col);
4573 if (column_span != combo_box->priv->col_column)
4575 combo_box->priv->col_column = column_span;
4577 gtk_combo_box_relayout (combo_box);
4579 g_object_notify (G_OBJECT (combo_box), "column-span-column");
4584 * gtk_combo_box_get_active:
4585 * @combo_box: A #GtkComboBox.
4587 * Returns the index of the currently active item, or -1 if there's no
4588 * active item. If the model is a non-flat treemodel, and the active item
4589 * is not an immediate child of the root of the tree, this function returns
4590 * <literal>gtk_tree_path_get_indices (path)[0]</literal>, where
4591 * <literal>path</literal> is the #GtkTreePath of the active item.
4593 * Return value: An integer which is the index of the currently active item, or
4594 * -1 if there's no active item.
4599 gtk_combo_box_get_active (GtkComboBox *combo_box)
4602 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), 0);
4604 if (gtk_tree_row_reference_valid (combo_box->priv->active_row))
4608 path = gtk_tree_row_reference_get_path (combo_box->priv->active_row);
4609 result = gtk_tree_path_get_indices (path)[0];
4610 gtk_tree_path_free (path);
4619 * gtk_combo_box_set_active:
4620 * @combo_box: A #GtkComboBox.
4621 * @index_: An index in the model passed during construction, or -1 to have
4624 * Sets the active item of @combo_box to be the item at @index.
4629 gtk_combo_box_set_active (GtkComboBox *combo_box,
4632 GtkTreePath *path = NULL;
4633 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4634 g_return_if_fail (index_ >= -1);
4637 path = gtk_tree_path_new_from_indices (index_, -1);
4639 gtk_combo_box_set_active_internal (combo_box, path);
4642 gtk_tree_path_free (path);
4646 gtk_combo_box_set_active_internal (GtkComboBox *combo_box,
4649 GtkTreePath *active_path;
4652 if (path && gtk_tree_row_reference_valid (combo_box->priv->active_row))
4654 active_path = gtk_tree_row_reference_get_path (combo_box->priv->active_row);
4655 path_cmp = gtk_tree_path_compare (path, active_path);
4656 gtk_tree_path_free (active_path);
4661 if (combo_box->priv->active_row)
4663 gtk_tree_row_reference_free (combo_box->priv->active_row);
4664 combo_box->priv->active_row = NULL;
4669 if (combo_box->priv->tree_view)
4670 gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (combo_box->priv->tree_view)));
4673 GtkMenu *menu = GTK_MENU (combo_box->priv->popup_widget);
4675 if (GTK_IS_MENU (menu))
4676 gtk_menu_set_active (menu, -1);
4679 if (combo_box->priv->cell_view)
4680 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (combo_box->priv->cell_view), NULL);
4684 combo_box->priv->active_row =
4685 gtk_tree_row_reference_new (combo_box->priv->model, path);
4687 if (combo_box->priv->tree_view)
4689 gtk_tree_view_set_cursor (GTK_TREE_VIEW (combo_box->priv->tree_view),
4692 else if (GTK_IS_MENU (combo_box->priv->popup_widget))
4694 /* FIXME handle nested menus better */
4695 gtk_menu_set_active (GTK_MENU (combo_box->priv->popup_widget),
4696 gtk_tree_path_get_indices (path)[0]);
4699 if (combo_box->priv->cell_view)
4700 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (combo_box->priv->cell_view),
4704 g_signal_emit (combo_box, combo_box_signals[CHANGED], 0);
4705 g_object_notify (G_OBJECT (combo_box), "active");
4710 * gtk_combo_box_get_active_iter:
4711 * @combo_box: A #GtkComboBox
4712 * @iter: The uninitialized #GtkTreeIter.
4714 * Sets @iter to point to the current active item, if it exists.
4716 * Return value: %TRUE, if @iter was set
4721 gtk_combo_box_get_active_iter (GtkComboBox *combo_box,
4727 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
4729 if (!gtk_tree_row_reference_valid (combo_box->priv->active_row))
4732 path = gtk_tree_row_reference_get_path (combo_box->priv->active_row);
4733 result = gtk_tree_model_get_iter (combo_box->priv->model, iter, path);
4734 gtk_tree_path_free (path);
4740 * gtk_combo_box_set_active_iter:
4741 * @combo_box: A #GtkComboBox
4742 * @iter: The #GtkTreeIter.
4744 * Sets the current active item to be the one referenced by @iter.
4745 * @iter must correspond to a path of depth one.
4750 gtk_combo_box_set_active_iter (GtkComboBox *combo_box,
4755 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4757 path = gtk_tree_model_get_path (gtk_combo_box_get_model (combo_box), iter);
4758 gtk_combo_box_set_active_internal (combo_box, path);
4759 gtk_tree_path_free (path);
4763 * gtk_combo_box_set_model:
4764 * @combo_box: A #GtkComboBox.
4765 * @model: A #GtkTreeModel.
4767 * Sets the model used by @combo_box to be @model. Will unset a previously set
4768 * model (if applicable). If model is %NULL, then it will unset the model.
4770 * Note that this function does not clear the cell renderers, you have to
4771 * call gtk_combo_box_cell_layout_clear() yourself if you need to set up
4772 * different cell renderers for the new model.
4777 gtk_combo_box_set_model (GtkComboBox *combo_box,
4778 GtkTreeModel *model)
4780 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4781 g_return_if_fail (model == NULL || GTK_IS_TREE_MODEL (model));
4783 if (model == combo_box->priv->model)
4786 gtk_combo_box_unset_model (combo_box);
4791 combo_box->priv->model = model;
4792 g_object_ref (combo_box->priv->model);
4794 combo_box->priv->inserted_id =
4795 g_signal_connect (combo_box->priv->model, "row_inserted",
4796 G_CALLBACK (gtk_combo_box_model_row_inserted),
4798 combo_box->priv->deleted_id =
4799 g_signal_connect (combo_box->priv->model, "row_deleted",
4800 G_CALLBACK (gtk_combo_box_model_row_deleted),
4802 combo_box->priv->reordered_id =
4803 g_signal_connect (combo_box->priv->model, "rows_reordered",
4804 G_CALLBACK (gtk_combo_box_model_rows_reordered),
4806 combo_box->priv->changed_id =
4807 g_signal_connect (combo_box->priv->model, "row_changed",
4808 G_CALLBACK (gtk_combo_box_model_row_changed),
4811 if (combo_box->priv->tree_view)
4814 gtk_tree_view_set_model (GTK_TREE_VIEW (combo_box->priv->tree_view),
4815 combo_box->priv->model);
4816 gtk_combo_box_list_popup_resize (combo_box);
4821 if (combo_box->priv->popup_widget)
4822 gtk_combo_box_menu_fill (combo_box);
4826 if (combo_box->priv->cell_view)
4827 gtk_cell_view_set_model (GTK_CELL_VIEW (combo_box->priv->cell_view),
4828 combo_box->priv->model);
4832 * gtk_combo_box_get_model
4833 * @combo_box: A #GtkComboBox.
4835 * Returns the #GtkTreeModel which is acting as data source for @combo_box.
4837 * Return value: A #GtkTreeModel which was passed during construction.
4842 gtk_combo_box_get_model (GtkComboBox *combo_box)
4844 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
4846 return combo_box->priv->model;
4850 /* convenience API for simple text combos */
4853 * gtk_combo_box_new_text:
4855 * Convenience function which constructs a new text combo box, which is a
4856 * #GtkComboBox just displaying strings. If you use this function to create
4857 * a text combo box, you should only manipulate its data source with the
4858 * following convenience functions: gtk_combo_box_append_text(),
4859 * gtk_combo_box_insert_text(), gtk_combo_box_prepend_text() and
4860 * gtk_combo_box_remove_text().
4862 * Return value: A new text combo box.
4867 gtk_combo_box_new_text (void)
4869 GtkWidget *combo_box;
4870 GtkCellRenderer *cell;
4871 GtkListStore *store;
4873 store = gtk_list_store_new (1, G_TYPE_STRING);
4874 combo_box = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store));
4875 g_object_unref (store);
4877 cell = gtk_cell_renderer_text_new ();
4878 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), cell, TRUE);
4879 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), cell,
4887 * gtk_combo_box_append_text:
4888 * @combo_box: A #GtkComboBox constructed using gtk_combo_box_new_text().
4891 * Appends @string to the list of strings stored in @combo_box. Note that
4892 * you can only use this function with combo boxes constructed with
4893 * gtk_combo_box_new_text().
4898 gtk_combo_box_append_text (GtkComboBox *combo_box,
4902 GtkListStore *store;
4904 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4905 g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model));
4906 g_return_if_fail (text != NULL);
4908 store = GTK_LIST_STORE (combo_box->priv->model);
4910 gtk_list_store_append (store, &iter);
4911 gtk_list_store_set (store, &iter, 0, text, -1);
4915 * gtk_combo_box_insert_text:
4916 * @combo_box: A #GtkComboBox constructed using gtk_combo_box_new_text().
4917 * @position: An index to insert @text.
4920 * Inserts @string at @position in the list of strings stored in @combo_box.
4921 * Note that you can only use this function with combo boxes constructed
4922 * with gtk_combo_box_new_text().
4927 gtk_combo_box_insert_text (GtkComboBox *combo_box,
4932 GtkListStore *store;
4934 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4935 g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model));
4936 g_return_if_fail (position >= 0);
4937 g_return_if_fail (text != NULL);
4939 store = GTK_LIST_STORE (combo_box->priv->model);
4941 gtk_list_store_insert (store, &iter, position);
4942 gtk_list_store_set (store, &iter, 0, text, -1);
4946 * gtk_combo_box_prepend_text:
4947 * @combo_box: A #GtkComboBox constructed with gtk_combo_box_new_text().
4950 * Prepends @string to the list of strings stored in @combo_box. Note that
4951 * you can only use this function with combo boxes constructed with
4952 * gtk_combo_box_new_text().
4957 gtk_combo_box_prepend_text (GtkComboBox *combo_box,
4961 GtkListStore *store;
4963 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4964 g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model));
4965 g_return_if_fail (text != NULL);
4967 store = GTK_LIST_STORE (combo_box->priv->model);
4969 gtk_list_store_prepend (store, &iter);
4970 gtk_list_store_set (store, &iter, 0, text, -1);
4974 * gtk_combo_box_remove_text:
4975 * @combo_box: A #GtkComboBox constructed with gtk_combo_box_new_text().
4976 * @position: Index of the item to remove.
4978 * Removes the string at @position from @combo_box. Note that you can only use
4979 * this function with combo boxes constructed with gtk_combo_box_new_text().
4984 gtk_combo_box_remove_text (GtkComboBox *combo_box,
4988 GtkListStore *store;
4990 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
4991 g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model));
4992 g_return_if_fail (position >= 0);
4994 store = GTK_LIST_STORE (combo_box->priv->model);
4996 if (gtk_tree_model_iter_nth_child (combo_box->priv->model, &iter,
4998 gtk_list_store_remove (store, &iter);
5002 * gtk_combo_box_get_active_text:
5003 * @combo_box: A #GtkComboBox constructed with gtk_combo_box_new_text().
5005 * Returns the currently active string in @combo_box or %NULL if none
5006 * is selected. Note that you can only use this function with combo
5007 * boxes constructed with gtk_combo_box_new_text() and with
5008 * #GtkComboBoxEntry<!-- -->s.
5010 * Returns: a newly allocated string containing the currently active text.
5015 gtk_combo_box_get_active_text (GtkComboBox *combo_box)
5017 GtkComboBoxClass *class;
5019 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
5021 class = GTK_COMBO_BOX_GET_CLASS (combo_box);
5023 if (class->get_active_text)
5024 return (* class->get_active_text) (combo_box);
5030 gtk_combo_box_real_get_active_text (GtkComboBox *combo_box)
5035 g_return_val_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model), NULL);
5037 if (gtk_combo_box_get_active_iter (combo_box, &iter))
5038 gtk_tree_model_get (combo_box->priv->model, &iter,
5045 gtk_combo_box_real_move_active (GtkComboBox *combo_box,
5046 GtkScrollType scroll)
5049 GtkTreeIter new_iter;
5050 gboolean active_iter;
5053 if (!combo_box->priv->model)
5055 gtk_widget_error_bell (GTK_WIDGET (combo_box));
5059 active_iter = gtk_combo_box_get_active_iter (combo_box, &iter);
5063 case GTK_SCROLL_STEP_BACKWARD:
5064 case GTK_SCROLL_STEP_UP:
5065 case GTK_SCROLL_STEP_LEFT:
5068 found = tree_prev (combo_box, combo_box->priv->model,
5069 &iter, &new_iter, FALSE);
5072 /* else fall through */
5074 case GTK_SCROLL_PAGE_FORWARD:
5075 case GTK_SCROLL_PAGE_DOWN:
5076 case GTK_SCROLL_PAGE_RIGHT:
5077 case GTK_SCROLL_END:
5078 found = tree_last (combo_box, combo_box->priv->model, &new_iter, FALSE);
5081 case GTK_SCROLL_STEP_FORWARD:
5082 case GTK_SCROLL_STEP_DOWN:
5083 case GTK_SCROLL_STEP_RIGHT:
5086 found = tree_next (combo_box, combo_box->priv->model,
5087 &iter, &new_iter, FALSE);
5090 /* else fall through */
5092 case GTK_SCROLL_PAGE_BACKWARD:
5093 case GTK_SCROLL_PAGE_UP:
5094 case GTK_SCROLL_PAGE_LEFT:
5095 case GTK_SCROLL_START:
5096 found = tree_first (combo_box, combo_box->priv->model, &new_iter, FALSE);
5103 if (found && active_iter)
5105 GtkTreePath *old_path;
5106 GtkTreePath *new_path;
5108 old_path = gtk_tree_model_get_path (combo_box->priv->model, &iter);
5109 new_path = gtk_tree_model_get_path (combo_box->priv->model, &new_iter);
5111 if (gtk_tree_path_compare (old_path, new_path) == 0)
5114 gtk_tree_path_free (old_path);
5115 gtk_tree_path_free (new_path);
5120 gtk_combo_box_set_active_iter (combo_box, &new_iter);
5124 gtk_widget_error_bell (GTK_WIDGET (combo_box));
5129 gtk_combo_box_mnemonic_activate (GtkWidget *widget,
5130 gboolean group_cycling)
5132 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
5134 gtk_widget_grab_focus (combo_box->priv->button);
5140 gtk_combo_box_grab_focus (GtkWidget *widget)
5142 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
5144 gtk_widget_grab_focus (combo_box->priv->button);
5148 gtk_combo_box_destroy (GtkObject *object)
5150 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
5152 if (combo_box->priv->popup_idle_id > 0)
5154 g_source_remove (combo_box->priv->popup_idle_id);
5155 combo_box->priv->popup_idle_id = 0;
5158 gtk_combo_box_popdown (combo_box);
5160 if (combo_box->priv->row_separator_destroy)
5161 (* combo_box->priv->row_separator_destroy) (combo_box->priv->row_separator_data);
5163 combo_box->priv->row_separator_func = NULL;
5164 combo_box->priv->row_separator_data = NULL;
5165 combo_box->priv->row_separator_destroy = NULL;
5167 GTK_OBJECT_CLASS (gtk_combo_box_parent_class)->destroy (object);
5168 combo_box->priv->cell_view = NULL;
5172 gtk_combo_box_finalize (GObject *object)
5174 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
5177 if (GTK_IS_MENU (combo_box->priv->popup_widget))
5179 gtk_combo_box_menu_destroy (combo_box);
5180 gtk_menu_detach (GTK_MENU (combo_box->priv->popup_widget));
5181 combo_box->priv->popup_widget = NULL;
5184 if (GTK_IS_TREE_VIEW (combo_box->priv->tree_view))
5185 gtk_combo_box_list_destroy (combo_box);
5187 if (combo_box->priv->popup_window)
5188 gtk_widget_destroy (combo_box->priv->popup_window);
5190 gtk_combo_box_unset_model (combo_box);
5192 for (i = combo_box->priv->cells; i; i = i->next)
5194 ComboCellInfo *info = (ComboCellInfo *)i->data;
5195 GSList *list = info->attributes;
5198 info->destroy (info->func_data);
5200 while (list && list->next)
5202 g_free (list->data);
5203 list = list->next->next;
5205 g_slist_free (info->attributes);
5207 g_object_unref (info->cell);
5210 g_slist_free (combo_box->priv->cells);
5212 g_free (combo_box->priv->tearoff_title);
5214 G_OBJECT_CLASS (gtk_combo_box_parent_class)->finalize (object);
5218 gtk_cell_editable_key_press (GtkWidget *widget,
5222 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
5224 if (event->keyval == GDK_Escape)
5226 combo_box->priv->editing_canceled = TRUE;
5228 gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (combo_box));
5229 gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (combo_box));
5233 else if (event->keyval == GDK_Return)
5235 gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (combo_box));
5236 gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (combo_box));
5245 popdown_idle (gpointer data)
5247 GtkComboBox *combo_box;
5249 combo_box = GTK_COMBO_BOX (data);
5251 gtk_cell_editable_editing_done (GTK_CELL_EDITABLE (combo_box));
5252 gtk_cell_editable_remove_widget (GTK_CELL_EDITABLE (combo_box));
5254 g_object_unref (combo_box);
5260 popdown_handler (GtkWidget *widget,
5263 gdk_threads_add_idle (popdown_idle, g_object_ref (data));
5267 popup_idle (gpointer data)
5269 GtkComboBox *combo_box;
5271 combo_box = GTK_COMBO_BOX (data);
5273 if (GTK_IS_MENU (combo_box->priv->popup_widget) &&
5274 combo_box->priv->cell_view)
5275 g_signal_connect_object (combo_box->priv->popup_widget,
5276 "unmap", G_CALLBACK (popdown_handler),
5279 /* we unset this if a menu item is activated */
5280 combo_box->priv->editing_canceled = TRUE;
5281 gtk_combo_box_popup (combo_box);
5283 combo_box->priv->popup_idle_id = 0;
5289 gtk_combo_box_start_editing (GtkCellEditable *cell_editable,
5292 GtkComboBox *combo_box = GTK_COMBO_BOX (cell_editable);
5294 combo_box->priv->is_cell_renderer = TRUE;
5296 if (combo_box->priv->cell_view)
5298 g_signal_connect_object (combo_box->priv->button, "key_press_event",
5299 G_CALLBACK (gtk_cell_editable_key_press),
5302 gtk_widget_grab_focus (combo_box->priv->button);
5306 g_signal_connect_object (GTK_BIN (combo_box)->child, "key_press_event",
5307 G_CALLBACK (gtk_cell_editable_key_press),
5310 gtk_widget_grab_focus (GTK_WIDGET (GTK_BIN (combo_box)->child));
5311 GTK_WIDGET_UNSET_FLAGS (combo_box->priv->button, GTK_CAN_FOCUS);
5314 /* we do the immediate popup only for the optionmenu-like
5317 if (combo_box->priv->is_cell_renderer &&
5318 combo_box->priv->cell_view && !combo_box->priv->tree_view)
5319 combo_box->priv->popup_idle_id = gdk_threads_add_idle (popup_idle, combo_box);
5324 * gtk_combo_box_get_add_tearoffs:
5325 * @combo_box: a #GtkComboBox
5327 * Gets the current value of the :add-tearoffs property.
5329 * Return value: the current value of the :add-tearoffs property.
5332 gtk_combo_box_get_add_tearoffs (GtkComboBox *combo_box)
5334 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
5336 return combo_box->priv->add_tearoffs;
5340 * gtk_combo_box_set_add_tearoffs:
5341 * @combo_box: a #GtkComboBox
5342 * @add_tearoffs: %TRUE to add tearoff menu items
5344 * Sets whether the popup menu should have a tearoff
5350 gtk_combo_box_set_add_tearoffs (GtkComboBox *combo_box,
5351 gboolean add_tearoffs)
5353 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5355 add_tearoffs = add_tearoffs != FALSE;
5357 if (combo_box->priv->add_tearoffs != add_tearoffs)
5359 combo_box->priv->add_tearoffs = add_tearoffs;
5360 gtk_combo_box_check_appearance (combo_box);
5361 gtk_combo_box_relayout (combo_box);
5362 g_object_notify (G_OBJECT (combo_box), "add-tearoffs");
5367 * gtk_combo_box_get_title:
5368 * @combo_box: a #GtkComboBox
5370 * Gets the current title of the menu in tearoff mode. See
5371 * gtk_combo_box_set_add_tearoffs().
5373 * Returns: the menu's title in tearoff mode. This is an internal copy of the
5374 * string which must not be freed.
5378 G_CONST_RETURN gchar*
5379 gtk_combo_box_get_title (GtkComboBox *combo_box)
5381 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
5383 return combo_box->priv->tearoff_title;
5387 gtk_combo_box_update_title (GtkComboBox *combo_box)
5389 gtk_combo_box_check_appearance (combo_box);
5391 if (combo_box->priv->popup_widget &&
5392 GTK_IS_MENU (combo_box->priv->popup_widget))
5393 gtk_menu_set_title (GTK_MENU (combo_box->priv->popup_widget),
5394 combo_box->priv->tearoff_title);
5398 * gtk_combo_box_set_title:
5399 * @combo_box: a #GtkComboBox
5400 * @title: a title for the menu in tearoff mode.
5402 * Sets the menu's title in tearoff mode.
5407 gtk_combo_box_set_title (GtkComboBox *combo_box,
5410 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5412 if (strcmp (title ? title : "",
5413 combo_box->priv->tearoff_title ? combo_box->priv->tearoff_title : "") != 0)
5415 g_free (combo_box->priv->tearoff_title);
5416 combo_box->priv->tearoff_title = g_strdup (title);
5418 gtk_combo_box_update_title (combo_box);
5420 g_object_notify (G_OBJECT (combo_box), "tearoff-title");
5425 _gtk_combo_box_editing_canceled (GtkComboBox *combo_box)
5427 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), TRUE);
5429 return combo_box->priv->editing_canceled;
5433 * gtk_combo_box_get_popup_accessible:
5434 * @combo_box: a #GtkComboBox
5436 * Gets the accessible object corresponding to the combo box's popup.
5438 * This function is mostly intended for use by accessibility technologies;
5439 * applications should have little use for it.
5441 * Returns: the accessible object corresponding to the combo box's popup.
5446 gtk_combo_box_get_popup_accessible (GtkComboBox *combo_box)
5450 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
5452 if (combo_box->priv->popup_widget)
5454 atk_obj = gtk_widget_get_accessible (combo_box->priv->popup_widget);
5462 * gtk_combo_box_get_row_separator_func:
5463 * @combo_box: a #GtkComboBox
5465 * Returns the current row separator function.
5467 * Return value: the current row separator function.
5471 GtkTreeViewRowSeparatorFunc
5472 gtk_combo_box_get_row_separator_func (GtkComboBox *combo_box)
5474 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
5476 return combo_box->priv->row_separator_func;
5480 * gtk_combo_box_set_row_separator_func:
5481 * @combo_box: a #GtkComboBox
5482 * @func: a #GtkTreeViewRowSeparatorFunc
5483 * @data: user data to pass to @func, or %NULL
5484 * @destroy: destroy notifier for @data, or %NULL
5486 * Sets the row separator function, which is used to determine
5487 * whether a row should be drawn as a separator. If the row separator
5488 * function is %NULL, no separators are drawn. This is the default value.
5493 gtk_combo_box_set_row_separator_func (GtkComboBox *combo_box,
5494 GtkTreeViewRowSeparatorFunc func,
5496 GtkDestroyNotify destroy)
5498 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5500 if (combo_box->priv->row_separator_destroy)
5501 (* combo_box->priv->row_separator_destroy) (combo_box->priv->row_separator_data);
5503 combo_box->priv->row_separator_func = func;
5504 combo_box->priv->row_separator_data = data;
5505 combo_box->priv->row_separator_destroy = destroy;
5507 if (combo_box->priv->tree_view)
5508 gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (combo_box->priv->tree_view),
5511 gtk_combo_box_relayout (combo_box);
5513 gtk_widget_queue_draw (GTK_WIDGET (combo_box));
5518 * gtk_combo_box_set_focus_on_click:
5519 * @combo: a #GtkComboBox
5520 * @focus_on_click: whether the combo box grabs focus when clicked
5523 * Sets whether the combo box will grab focus when it is clicked with
5524 * the mouse. Making mouse clicks not grab focus is useful in places
5525 * like toolbars where you don't want the keyboard focus removed from
5526 * the main area of the application.
5531 gtk_combo_box_set_focus_on_click (GtkComboBox *combo_box,
5532 gboolean focus_on_click)
5534 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
5536 focus_on_click = focus_on_click != FALSE;
5538 if (combo_box->priv->focus_on_click != focus_on_click)
5540 combo_box->priv->focus_on_click = focus_on_click;
5542 if (combo_box->priv->button)
5543 gtk_button_set_focus_on_click (GTK_BUTTON (combo_box->priv->button),
5546 g_object_notify (G_OBJECT (combo_box), "focus-on-click");
5551 * gtk_combo_box_get_focus_on_click:
5552 * @combo: a #GtkComboBox
5554 * Returns whether the combo box grabs focus when it is clicked
5555 * with the mouse. See gtk_combo_box_set_focus_on_click().
5557 * Return value: %TRUE if the combo box grabs focus when it is
5558 * clicked with the mouse.
5563 gtk_combo_box_get_focus_on_click (GtkComboBox *combo_box)
5565 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
5567 return combo_box->priv->focus_on_click;
5571 #define __GTK_COMBO_BOX_C__
5572 #include "gtkaliasdef.c"