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.
20 #include "gtkcombobox.h"
23 #include "gtkbindings.h"
24 #include "gtkcelllayout.h"
25 #include "gtkcellrenderertext.h"
26 #include "gtkcellview.h"
27 #include "gtkcellviewmenuitem.h"
28 #include "gtkeventbox.h"
30 #include "gtkliststore.h"
33 #include "gtktogglebutton.h"
34 #include "gtktreeselection.h"
35 #include "gtkvseparator.h"
36 #include "gtkwindow.h"
38 #include <gdk/gdkkeysyms.h>
40 #include <gobject/gvaluecollector.h>
45 #include "gtkmarshalers.h"
49 /* WELCOME, to THE house of evil code */
51 typedef struct _ComboCellInfo ComboCellInfo;
54 GtkCellRenderer *cell;
57 GtkCellLayoutDataFunc func;
59 GDestroyNotify destroy;
65 #define GTK_COMBO_BOX_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_COMBO_BOX, GtkComboBoxPrivate))
67 struct _GtkComboBoxPrivate
79 GtkTreeViewColumn *column;
82 GtkWidget *cell_view_frame;
88 GtkWidget *popup_widget;
89 GtkWidget *popup_window;
90 GtkWidget *popup_frame;
100 guint popup_in_progress : 1;
103 /* While debugging this evil code, I have learned that
104 * there are actually 4 modes to this widget, which can
105 * be characterized as follows
107 * 1) menu mode, no child added
110 * cell_view -> GtkCellView, regular child
111 * cell_view_frame -> NULL
112 * button -> GtkToggleButton set_parent to combo
113 * arrow -> GtkArrow set_parent to button
114 * separator -> GtkVSepator set_parent to button
115 * popup_widget -> GtkMenu
116 * popup_window -> NULL
117 * popup_frame -> NULL
119 * 2) menu mode, child added
123 * cell_view_frame -> NULL
124 * button -> GtkToggleButton set_parent to combo
125 * arrow -> GtkArrow, child of button
127 * popup_widget -> GtkMenu
128 * popup_window -> NULL
129 * popup_frame -> NULL
131 * 3) list mode, no child added
133 * tree_view -> GtkTreeView, child of popup_frame
134 * cell_view -> GtkCellView, regular child
135 * cell_view_frame -> GtkFrame, set parent to combo
136 * button -> GtkToggleButton, set_parent to combo
137 * arrow -> GtkArrow, child of button
139 * popup_widget -> tree_view
140 * popup_window -> GtkWindow
141 * popup_frame -> GtkFrame, child of popup_window
143 * 4) list mode, child added
145 * tree_view -> GtkTreeView, child of popup_frame
147 * cell_view_frame -> NULL
148 * button -> GtkToggleButton, set_parent to combo
149 * arrow -> GtkArrow, child of button
151 * popup_widget -> tree_view
152 * popup_window -> GtkWindow
153 * popup_frame -> GtkFrame, child of popup_window
166 PROP_ROW_SPAN_COLUMN,
167 PROP_COLUMN_SPAN_COLUMN,
171 static GtkBinClass *parent_class = NULL;
172 static guint combo_box_signals[LAST_SIGNAL] = {0,};
174 #define BONUS_PADDING 4
178 static void gtk_combo_box_class_init (GtkComboBoxClass *klass);
179 static void gtk_combo_box_cell_layout_init (GtkCellLayoutIface *iface);
180 static void gtk_combo_box_init (GtkComboBox *combo_box);
181 static void gtk_combo_box_finalize (GObject *object);
182 static void gtk_combo_box_destroy (GtkObject *object);
184 static void gtk_combo_box_set_property (GObject *object,
188 static void gtk_combo_box_get_property (GObject *object,
193 static void gtk_combo_box_style_set (GtkWidget *widget,
195 static void gtk_combo_box_button_toggled (GtkWidget *widget,
197 static void gtk_combo_box_add (GtkContainer *container,
200 static ComboCellInfo *gtk_combo_box_get_cell_info (GtkComboBox *combo_box,
201 GtkCellRenderer *cell);
203 static void gtk_combo_box_menu_show (GtkWidget *menu,
205 static void gtk_combo_box_menu_hide (GtkWidget *menu,
208 static void gtk_combo_box_set_popup_widget (GtkComboBox *combo_box,
210 static void gtk_combo_box_menu_position (GtkMenu *menu,
216 static gint gtk_combo_box_calc_requested_width (GtkComboBox *combo_box,
218 static void gtk_combo_box_remeasure (GtkComboBox *combo_box);
220 static void gtk_combo_box_unset_model (GtkComboBox *combo_box);
221 static void gtk_combo_box_set_model_internal (GtkComboBox *combo_box);
223 static void gtk_combo_box_size_request (GtkWidget *widget,
224 GtkRequisition *requisition);
225 static void gtk_combo_box_size_allocate (GtkWidget *widget,
226 GtkAllocation *allocation);
227 static void gtk_combo_box_forall (GtkContainer *container,
228 gboolean include_internals,
229 GtkCallback callback,
230 gpointer callback_data);
231 static gboolean gtk_combo_box_expose_event (GtkWidget *widget,
232 GdkEventExpose *event);
233 static gboolean gtk_combo_box_scroll_event (GtkWidget *widget,
234 GdkEventScroll *event);
236 /* listening to the model */
237 static void gtk_combo_box_model_row_inserted (GtkTreeModel *model,
241 static void gtk_combo_box_model_row_deleted (GtkTreeModel *model,
244 static void gtk_combo_box_model_rows_reordered (GtkTreeModel *model,
249 static void gtk_combo_box_model_row_changed (GtkTreeModel *model,
255 static void gtk_combo_box_list_position (GtkComboBox *combo_box,
260 static void gtk_combo_box_list_setup (GtkComboBox *combo_box);
261 static void gtk_combo_box_list_destroy (GtkComboBox *combo_box);
263 static void gtk_combo_box_list_remove_grabs (GtkComboBox *combo_box);
265 static gboolean gtk_combo_box_list_button_released (GtkWidget *widget,
266 GdkEventButton *event,
268 static gboolean gtk_combo_box_list_key_press (GtkWidget *widget,
271 static gboolean gtk_combo_box_list_button_pressed (GtkWidget *widget,
272 GdkEventButton *event,
275 static void gtk_combo_box_list_row_changed (GtkTreeModel *model,
281 static void gtk_combo_box_menu_setup (GtkComboBox *combo_box,
282 gboolean add_childs);
283 static void gtk_combo_box_menu_fill (GtkComboBox *combo_box);
284 static void gtk_combo_box_menu_destroy (GtkComboBox *combo_box);
286 static void gtk_combo_box_item_get_size (GtkComboBox *combo_box,
290 static void gtk_combo_box_relayout_item (GtkComboBox *combo_box,
292 static void gtk_combo_box_relayout (GtkComboBox *combo_box);
294 static gboolean gtk_combo_box_menu_button_press (GtkWidget *widget,
295 GdkEventButton *event,
297 static void gtk_combo_box_menu_item_activate (GtkWidget *item,
299 static void gtk_combo_box_menu_row_inserted (GtkTreeModel *model,
303 static void gtk_combo_box_menu_row_deleted (GtkTreeModel *model,
306 static void gtk_combo_box_menu_rows_reordered (GtkTreeModel *model,
311 static void gtk_combo_box_menu_row_changed (GtkTreeModel *model,
317 static void gtk_combo_box_cell_layout_pack_start (GtkCellLayout *layout,
318 GtkCellRenderer *cell,
320 static void gtk_combo_box_cell_layout_pack_end (GtkCellLayout *layout,
321 GtkCellRenderer *cell,
323 static void gtk_combo_box_cell_layout_clear (GtkCellLayout *layout);
324 static void gtk_combo_box_cell_layout_add_attribute (GtkCellLayout *layout,
325 GtkCellRenderer *cell,
326 const gchar *attribute,
328 static void gtk_combo_box_cell_layout_set_cell_data_func (GtkCellLayout *layout,
329 GtkCellRenderer *cell,
330 GtkCellLayoutDataFunc func,
332 GDestroyNotify destroy);
333 static void gtk_combo_box_cell_layout_clear_attributes (GtkCellLayout *layout,
334 GtkCellRenderer *cell);
335 static void gtk_combo_box_cell_layout_reorder (GtkCellLayout *layout,
336 GtkCellRenderer *cell,
338 static gboolean gtk_combo_box_mnemonic_activate (GtkWidget *widget,
339 gboolean group_cycling);
343 gtk_combo_box_get_type (void)
345 static GType combo_box_type = 0;
349 static const GTypeInfo combo_box_info =
351 sizeof (GtkComboBoxClass),
352 NULL, /* base_init */
353 NULL, /* base_finalize */
354 (GClassInitFunc) gtk_combo_box_class_init,
355 NULL, /* class_finalize */
356 NULL, /* class_data */
357 sizeof (GtkComboBox),
359 (GInstanceInitFunc) gtk_combo_box_init
362 static const GInterfaceInfo cell_layout_info =
364 (GInterfaceInitFunc) gtk_combo_box_cell_layout_init,
369 combo_box_type = g_type_register_static (GTK_TYPE_BIN,
374 g_type_add_interface_static (combo_box_type,
375 GTK_TYPE_CELL_LAYOUT,
379 return combo_box_type;
384 gtk_combo_box_class_init (GtkComboBoxClass *klass)
386 GObjectClass *object_class;
387 GtkBindingSet *binding_set;
388 GtkObjectClass *gtk_object_class;
389 GtkContainerClass *container_class;
390 GtkWidgetClass *widget_class;
392 binding_set = gtk_binding_set_by_class (klass);
394 container_class = (GtkContainerClass *)klass;
395 container_class->forall = gtk_combo_box_forall;
396 container_class->add = gtk_combo_box_add;
398 widget_class = (GtkWidgetClass *)klass;
399 widget_class->size_allocate = gtk_combo_box_size_allocate;
400 widget_class->size_request = gtk_combo_box_size_request;
401 widget_class->expose_event = gtk_combo_box_expose_event;
402 widget_class->scroll_event = gtk_combo_box_scroll_event;
403 widget_class->mnemonic_activate = gtk_combo_box_mnemonic_activate;
404 widget_class->style_set = gtk_combo_box_style_set;
406 gtk_object_class = (GtkObjectClass *)klass;
407 gtk_object_class->destroy = gtk_combo_box_destroy;
409 object_class = (GObjectClass *)klass;
410 object_class->finalize = gtk_combo_box_finalize;
411 object_class->set_property = gtk_combo_box_set_property;
412 object_class->get_property = gtk_combo_box_get_property;
414 parent_class = g_type_class_peek_parent (klass);
417 combo_box_signals[CHANGED] =
418 g_signal_new ("changed",
419 G_OBJECT_CLASS_TYPE (klass),
421 G_STRUCT_OFFSET (GtkComboBoxClass, changed),
423 g_cclosure_marshal_VOID__VOID,
427 g_object_class_install_property (object_class,
429 g_param_spec_object ("model",
430 P_("ComboBox model"),
431 P_("The model for the combo box"),
435 g_object_class_install_property (object_class,
437 g_param_spec_int ("wrap_width",
439 P_("Wrap width for layouting the items in a grid"),
445 g_object_class_install_property (object_class,
446 PROP_ROW_SPAN_COLUMN,
447 g_param_spec_int ("row_span_column",
448 P_("Row span column"),
449 P_("TreeModel column containing the row span values"),
455 g_object_class_install_property (object_class,
456 PROP_COLUMN_SPAN_COLUMN,
457 g_param_spec_int ("column_span_column",
458 P_("Column span column"),
459 P_("TreeModel column containing the column span values"),
465 g_object_class_install_property (object_class,
467 g_param_spec_int ("active",
469 P_("The item which is currently active"),
475 gtk_widget_class_install_style_property (widget_class,
476 g_param_spec_boolean ("appears-as-list",
477 P_("Appears as list"),
478 P_("Whether combobox dropdowns should look like lists rather than menus"),
482 g_type_class_add_private (object_class, sizeof (GtkComboBoxPrivate));
486 gtk_combo_box_cell_layout_init (GtkCellLayoutIface *iface)
488 iface->pack_start = gtk_combo_box_cell_layout_pack_start;
489 iface->pack_end = gtk_combo_box_cell_layout_pack_end;
490 iface->clear = gtk_combo_box_cell_layout_clear;
491 iface->add_attribute = gtk_combo_box_cell_layout_add_attribute;
492 iface->set_cell_data_func = gtk_combo_box_cell_layout_set_cell_data_func;
493 iface->clear_attributes = gtk_combo_box_cell_layout_clear_attributes;
494 iface->reorder = gtk_combo_box_cell_layout_reorder;
498 gtk_combo_box_init (GtkComboBox *combo_box)
500 combo_box->priv = GTK_COMBO_BOX_GET_PRIVATE (combo_box);
502 combo_box->priv->cell_view = gtk_cell_view_new ();
503 gtk_container_add (GTK_CONTAINER (combo_box), combo_box->priv->cell_view);
504 gtk_widget_show (combo_box->priv->cell_view);
506 combo_box->priv->width = 0;
507 combo_box->priv->wrap_width = 0;
509 combo_box->priv->active_item = -1;
510 combo_box->priv->col_column = -1;
511 combo_box->priv->row_column = -1;
515 gtk_combo_box_set_property (GObject *object,
520 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
525 gtk_combo_box_set_model (combo_box, g_value_get_object (value));
528 case PROP_WRAP_WIDTH:
529 gtk_combo_box_set_wrap_width (combo_box, g_value_get_int (value));
532 case PROP_ROW_SPAN_COLUMN:
533 gtk_combo_box_set_row_span_column (combo_box, g_value_get_int (value));
536 case PROP_COLUMN_SPAN_COLUMN:
537 gtk_combo_box_set_column_span_column (combo_box, g_value_get_int (value));
541 gtk_combo_box_set_active (combo_box, g_value_get_int (value));
550 gtk_combo_box_get_property (GObject *object,
555 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
560 g_value_set_object (value, combo_box->priv->model);
563 case PROP_WRAP_WIDTH:
564 g_value_set_int (value, combo_box->priv->wrap_width);
567 case PROP_ROW_SPAN_COLUMN:
568 g_value_set_int (value, combo_box->priv->row_column);
571 case PROP_COLUMN_SPAN_COLUMN:
572 g_value_set_int (value, combo_box->priv->col_column);
576 g_value_set_int (value, gtk_combo_box_get_active (combo_box));
580 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
586 gtk_combo_box_style_set (GtkWidget *widget,
590 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
592 gtk_widget_queue_resize (widget);
594 /* if wrap_width > 0, then we are in grid-mode and forced to use
597 if (combo_box->priv->wrap_width)
600 gtk_widget_style_get (widget,
601 "appears-as-list", &appearance,
604 /* TRUE is windows style */
607 /* Destroy all the menu mode widgets, if they exist. */
608 if (GTK_IS_MENU (combo_box->priv->popup_widget))
609 gtk_combo_box_menu_destroy (combo_box);
611 /* Create the list mode widgets, if they don't already exist. */
612 if (!GTK_IS_TREE_VIEW (combo_box->priv->tree_view))
613 gtk_combo_box_list_setup (combo_box);
617 /* Destroy all the list mode widgets, if they exist. */
618 if (GTK_IS_TREE_VIEW (combo_box->priv->tree_view))
619 gtk_combo_box_list_destroy (combo_box);
621 /* Create the menu mode widgets, if they don't already exist. */
622 if (!GTK_IS_MENU (combo_box->priv->popup_widget))
623 gtk_combo_box_menu_setup (combo_box, TRUE);
628 gtk_combo_box_button_toggled (GtkWidget *widget,
631 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
633 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
635 if (!combo_box->priv->popup_in_progress)
636 gtk_combo_box_popup (combo_box);
639 gtk_combo_box_popdown (combo_box);
643 gtk_combo_box_add (GtkContainer *container,
646 GtkComboBox *combo_box = GTK_COMBO_BOX (container);
648 if (combo_box->priv->cell_view && combo_box->priv->cell_view->parent)
649 gtk_container_remove (container, combo_box->priv->cell_view);
651 (* GTK_CONTAINER_CLASS (parent_class)->add) (container, widget);
653 if (combo_box->priv->cell_view &&
654 widget != combo_box->priv->cell_view)
656 /* since the cell_view was unparented, it's gone now */
657 combo_box->priv->cell_view = NULL;
659 if (!combo_box->priv->tree_view && combo_box->priv->separator)
661 gtk_widget_unparent (combo_box->priv->separator);
662 combo_box->priv->separator = NULL;
664 g_object_ref (G_OBJECT (combo_box->priv->arrow));
665 gtk_widget_unparent (combo_box->priv->arrow);
666 gtk_container_add (GTK_CONTAINER (combo_box->priv->button),
667 combo_box->priv->arrow);
668 g_object_unref (G_OBJECT (combo_box->priv->arrow));
670 gtk_widget_queue_resize (GTK_WIDGET (container));
672 else if (combo_box->priv->cell_view_frame)
674 gtk_widget_unparent (combo_box->priv->cell_view_frame);
675 combo_box->priv->cell_view_frame = NULL;
680 static ComboCellInfo *
681 gtk_combo_box_get_cell_info (GtkComboBox *combo_box,
682 GtkCellRenderer *cell)
686 for (i = combo_box->priv->cells; i; i = i->next)
688 ComboCellInfo *info = (ComboCellInfo *)i->data;
690 if (info && info->cell == cell)
698 gtk_combo_box_menu_show (GtkWidget *menu,
701 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
703 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
705 combo_box->priv->popup_in_progress = FALSE;
709 gtk_combo_box_menu_hide (GtkWidget *menu,
712 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
714 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
719 gtk_combo_box_detacher (GtkWidget *widget,
722 GtkComboBox *combo_box;
724 g_return_if_fail (GTK_IS_COMBO_BOX (widget));
726 combo_box = GTK_COMBO_BOX (widget);
727 g_return_if_fail (combo_box->priv->popup_widget == (GtkWidget*) menu);
729 g_signal_handlers_disconnect_by_func (menu,
730 gtk_combo_box_menu_show,
732 g_signal_handlers_disconnect_by_func (menu,
733 gtk_combo_box_menu_hide,
736 combo_box->priv->popup_widget = NULL;
740 gtk_combo_box_set_popup_widget (GtkComboBox *combo_box,
743 if (GTK_IS_MENU (combo_box->priv->popup_widget))
745 gtk_menu_detach (GTK_MENU (combo_box->priv->popup_widget));
746 combo_box->priv->popup_widget = NULL;
748 else if (combo_box->priv->popup_widget)
750 gtk_container_remove (GTK_CONTAINER (combo_box->priv->popup_frame),
751 combo_box->priv->popup_widget);
752 g_object_unref (G_OBJECT (combo_box->priv->popup_widget));
753 combo_box->priv->popup_widget = NULL;
756 if (GTK_IS_MENU (popup))
758 if (combo_box->priv->popup_window)
760 gtk_widget_destroy (combo_box->priv->popup_window);
761 combo_box->priv->popup_window = NULL;
762 combo_box->priv->popup_frame = NULL;
765 combo_box->priv->popup_widget = popup;
767 g_signal_connect (popup, "show",
768 G_CALLBACK (gtk_combo_box_menu_show), combo_box);
769 g_signal_connect (popup, "hide",
770 G_CALLBACK (gtk_combo_box_menu_hide), combo_box);
772 gtk_menu_attach_to_widget (GTK_MENU (popup),
773 GTK_WIDGET (combo_box),
774 gtk_combo_box_detacher);
778 if (!combo_box->priv->popup_window)
780 combo_box->priv->popup_window = gtk_window_new (GTK_WINDOW_POPUP);
781 gtk_window_set_resizable (GTK_WINDOW (combo_box->priv->popup_window), FALSE);
782 gtk_window_set_screen (GTK_WINDOW (combo_box->priv->popup_window),
783 gtk_widget_get_screen (GTK_WIDGET (combo_box)));
785 combo_box->priv->popup_frame = gtk_frame_new (NULL);
786 gtk_frame_set_shadow_type (GTK_FRAME (combo_box->priv->popup_frame),
787 GTK_SHADOW_ETCHED_IN);
788 gtk_container_add (GTK_CONTAINER (combo_box->priv->popup_window),
789 combo_box->priv->popup_frame);
791 gtk_widget_show (combo_box->priv->popup_frame);
794 gtk_container_add (GTK_CONTAINER (combo_box->priv->popup_frame),
796 gtk_widget_show (popup);
797 g_object_ref (G_OBJECT (popup));
798 combo_box->priv->popup_widget = popup;
803 gtk_combo_box_menu_position (GtkMenu *menu,
812 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
814 /* FIXME: is using the size request here broken? */
815 child = GTK_BIN (combo_box)->child;
817 gdk_window_get_origin (child->window, &sx, &sy);
819 gtk_widget_size_request (GTK_WIDGET (menu), &req);
821 if (gtk_widget_get_direction (GTK_WIDGET (combo_box)) == GTK_TEXT_DIR_RTL)
824 *x = sx + child->allocation.width - req.width;
825 *y = sy + child->allocation.height;
827 if (GTK_WIDGET_NO_WINDOW (child))
829 *x += child->allocation.x;
830 *y += child->allocation.y;
837 gtk_combo_box_list_position (GtkComboBox *combo_box,
846 GdkRectangle monitor;
847 GtkRequisition popup_req;
849 sample = GTK_BIN (combo_box)->child;
851 *width = sample->allocation.width;
852 gtk_widget_size_request (combo_box->priv->popup_window, &popup_req);
853 *height = popup_req.height;
855 gdk_window_get_origin (sample->window, x, y);
857 if (combo_box->priv->cell_view_frame)
859 *x -= GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
860 GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness;
861 *width += 2 * (GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
862 GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness);
865 if (GTK_WIDGET_NO_WINDOW (sample))
867 *x += sample->allocation.x;
868 *y += sample->allocation.y;
871 screen = gtk_widget_get_screen (GTK_WIDGET (combo_box));
872 monitor_num = gdk_screen_get_monitor_at_window (screen,
873 GTK_WIDGET (combo_box)->window);
874 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
878 else if (*x + *width > monitor.x + monitor.width)
879 *x = monitor.x + monitor.width - *width;
881 if (*y + sample->allocation.height + *height <= monitor.y + monitor.height)
882 *y += sample->allocation.height;
888 * gtk_combo_box_popup:
889 * @combo_box: a #GtkComboBox
891 * Pops up the menu or dropdown list of @combo_box.
893 * This function is mostly intended for use by accessibility technologies;
894 * applications should have little use for it.
899 gtk_combo_box_popup (GtkComboBox *combo_box)
901 gint x, y, width, height;
903 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
905 if (GTK_WIDGET_MAPPED (combo_box->priv->popup_widget))
908 if (GTK_IS_MENU (combo_box->priv->popup_widget))
910 if (combo_box->priv->active_item != -1)
914 childs = gtk_container_get_children (GTK_CONTAINER (combo_box->priv->popup_widget));
915 gtk_menu_shell_select_item (GTK_MENU_SHELL (combo_box->priv->popup_widget),
916 g_list_nth_data (childs, combo_box->priv->active_item));
917 g_list_free (childs);
920 gtk_menu_popup (GTK_MENU (combo_box->priv->popup_widget),
922 gtk_combo_box_menu_position, combo_box,
927 gtk_widget_show_all (combo_box->priv->popup_frame);
928 gtk_combo_box_list_position (combo_box, &x, &y, &width, &height);
930 gtk_widget_set_size_request (combo_box->priv->popup_window, width, -1);
931 gtk_window_move (GTK_WINDOW (combo_box->priv->popup_window), x, y);
934 gtk_widget_show (combo_box->priv->popup_window);
936 gtk_widget_grab_focus (combo_box->priv->popup_window);
937 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
940 if (!GTK_WIDGET_HAS_FOCUS (combo_box->priv->tree_view))
942 gdk_keyboard_grab (combo_box->priv->popup_window->window,
943 FALSE, GDK_CURRENT_TIME);
944 gtk_widget_grab_focus (combo_box->priv->tree_view);
947 gtk_grab_add (combo_box->priv->popup_window);
948 gdk_pointer_grab (combo_box->priv->popup_window->window, TRUE,
949 GDK_BUTTON_PRESS_MASK |
950 GDK_BUTTON_RELEASE_MASK |
951 GDK_POINTER_MOTION_MASK,
952 NULL, NULL, GDK_CURRENT_TIME);
954 gtk_grab_add (combo_box->priv->tree_view);
958 * gtk_combo_box_popdown:
959 * @combo_box: a #GtkComboBox
961 * Hides the menu or dropdown list of @combo_box.
963 * This function is mostly intended for use by accessibility technologies;
964 * applications should have little use for it.
969 gtk_combo_box_popdown (GtkComboBox *combo_box)
971 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
973 if (GTK_IS_MENU (combo_box->priv->popup_widget))
975 gtk_menu_popdown (GTK_MENU (combo_box->priv->popup_widget));
979 gtk_combo_box_list_remove_grabs (combo_box);
980 gtk_widget_hide_all (combo_box->priv->popup_window);
981 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
986 gtk_combo_box_calc_requested_width (GtkComboBox *combo_box,
992 if (combo_box->priv->cell_view)
993 gtk_widget_style_get (combo_box->priv->cell_view,
994 "focus-line-width", &padding,
999 /* add some pixels for good measure */
1000 padding += BONUS_PADDING;
1002 if (combo_box->priv->cell_view)
1003 gtk_cell_view_get_size_of_row (GTK_CELL_VIEW (combo_box->priv->cell_view),
1008 return req.width + padding;
1012 gtk_combo_box_remeasure (GtkComboBox *combo_box)
1018 if (!gtk_tree_model_get_iter_first (combo_box->priv->model, &iter))
1021 combo_box->priv->width = 0;
1023 path = gtk_tree_path_new_from_indices (0, -1);
1025 if (combo_box->priv->cell_view)
1026 gtk_widget_style_get (combo_box->priv->cell_view,
1027 "focus-line-width", &padding,
1032 /* add some pixels for good measure */
1033 padding += BONUS_PADDING;
1039 if (combo_box->priv->cell_view)
1040 gtk_cell_view_get_size_of_row (GTK_CELL_VIEW (combo_box->priv->cell_view),
1045 combo_box->priv->width = MAX (combo_box->priv->width,
1046 req.width + padding);
1048 gtk_tree_path_next (path);
1050 while (gtk_tree_model_iter_next (combo_box->priv->model, &iter));
1052 gtk_tree_path_free (path);
1056 gtk_combo_box_size_request (GtkWidget *widget,
1057 GtkRequisition *requisition)
1060 GtkRequisition bin_req;
1062 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1065 gtk_widget_size_request (GTK_BIN (widget)->child, &bin_req);
1066 gtk_combo_box_remeasure (combo_box);
1067 bin_req.width = MAX (bin_req.width, combo_box->priv->width);
1069 if (!combo_box->priv->tree_view)
1073 if (combo_box->priv->cell_view)
1075 GtkRequisition sep_req, arrow_req;
1076 gint border_width, xthickness, ythickness;
1078 border_width = GTK_CONTAINER (combo_box->priv->button)->border_width;
1079 xthickness = combo_box->priv->button->style->xthickness;
1080 ythickness = combo_box->priv->button->style->ythickness;
1082 bin_req.width = MAX (bin_req.width, combo_box->priv->width);
1084 gtk_widget_size_request (combo_box->priv->separator, &sep_req);
1085 gtk_widget_size_request (combo_box->priv->arrow, &arrow_req);
1087 height = MAX (sep_req.height, arrow_req.height);
1088 height = MAX (height, bin_req.height);
1090 width = bin_req.width + sep_req.width + arrow_req.width;
1092 height += border_width + 1 + xthickness * 2 + 4;
1093 width += border_width + 1 + ythickness * 2 + 4;
1095 requisition->width = width;
1096 requisition->height = height;
1100 GtkRequisition but_req;
1102 gtk_widget_size_request (combo_box->priv->button, &but_req);
1104 requisition->width = bin_req.width + but_req.width;
1105 requisition->height = MAX (bin_req.height, but_req.height);
1111 GtkRequisition button_req;
1113 /* sample + frame */
1114 *requisition = bin_req;
1116 if (combo_box->priv->cell_view_frame)
1118 requisition->width += 2 *
1119 (GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
1120 GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness);
1121 requisition->height += 2 *
1122 (GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
1123 GTK_WIDGET (combo_box->priv->cell_view_frame)->style->ythickness);
1127 gtk_widget_size_request (combo_box->priv->button, &button_req);
1129 requisition->height = MAX (requisition->height, button_req.height);
1130 requisition->width += button_req.width;
1135 gtk_combo_box_size_allocate (GtkWidget *widget,
1136 GtkAllocation *allocation)
1138 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1139 GtkAllocation child;
1141 gboolean is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
1143 widget->allocation = *allocation;
1145 if (!combo_box->priv->tree_view)
1147 if (combo_box->priv->cell_view)
1149 gint border_width, xthickness, ythickness;
1153 gtk_widget_size_allocate (combo_box->priv->button, allocation);
1155 /* set some things ready */
1156 border_width = GTK_CONTAINER (combo_box->priv->button)->border_width;
1157 xthickness = combo_box->priv->button->style->xthickness;
1158 ythickness = combo_box->priv->button->style->ythickness;
1160 child.x = allocation->x + border_width + 1 + xthickness + 2;
1161 child.y = allocation->y + border_width + 1 + ythickness + 2;
1163 width = allocation->width - (border_width + 1 + xthickness * 2 + 4);
1165 /* handle the childs */
1166 gtk_widget_size_request (combo_box->priv->arrow, &req);
1167 child.width = req.width;
1168 child.height = allocation->height - 2 * (child.y - allocation->y);
1170 child.x += width - req.width;
1171 gtk_widget_size_allocate (combo_box->priv->arrow, &child);
1173 child.x += req.width;
1174 gtk_widget_size_request (combo_box->priv->separator, &req);
1175 child.width = req.width;
1177 child.x -= req.width;
1178 gtk_widget_size_allocate (combo_box->priv->separator, &child);
1182 child.x += req.width;
1183 child.width = allocation->x + allocation->width
1184 - (border_width + 1 + xthickness + 2) - child.x;
1188 child.width = child.x;
1189 child.x = allocation->x + border_width + 1 + xthickness + 2;
1190 child.width -= child.x;
1193 gtk_widget_size_allocate (GTK_BIN (widget)->child, &child);
1197 gtk_widget_size_request (combo_box->priv->button, &req);
1199 child.x = allocation->x;
1201 child.x = allocation->x + allocation->width - req.width;
1202 child.y = allocation->y;
1203 child.width = req.width;
1204 child.height = allocation->height;
1205 gtk_widget_size_allocate (combo_box->priv->button, &child);
1208 child.x = allocation->x + req.width;
1210 child.x = allocation->x;
1211 child.y = allocation->y;
1212 child.width = allocation->width - req.width;
1213 gtk_widget_size_allocate (GTK_BIN (widget)->child, &child);
1221 gtk_widget_size_request (combo_box->priv->button, &req);
1223 child.x = allocation->x;
1225 child.x = allocation->x + allocation->width - req.width;
1226 child.y = allocation->y;
1227 child.width = req.width;
1228 child.height = allocation->height;
1229 gtk_widget_size_allocate (combo_box->priv->button, &child);
1233 child.x = allocation->x + req.width;
1235 child.x = allocation->x;
1236 child.y = allocation->y;
1237 child.width = allocation->width - req.width;
1238 child.height = allocation->height;
1240 if (combo_box->priv->cell_view_frame)
1242 gtk_widget_size_allocate (combo_box->priv->cell_view_frame, &child);
1246 GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
1247 GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness;
1249 GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
1250 GTK_WIDGET (combo_box->priv->cell_view_frame)->style->ythickness;
1251 child.width -= 2 * (
1252 GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
1253 GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness);
1254 child.height -= 2 * (
1255 GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
1256 GTK_WIDGET (combo_box->priv->cell_view_frame)->style->ythickness);
1259 gtk_widget_size_allocate (GTK_BIN (combo_box)->child, &child);
1264 gtk_combo_box_unset_model (GtkComboBox *combo_box)
1266 if (combo_box->priv->inserted_id != -1)
1268 g_signal_handler_disconnect (combo_box->priv->model,
1269 combo_box->priv->inserted_id);
1270 combo_box->priv->inserted_id = -1;
1272 if (combo_box->priv->deleted_id != -1)
1274 g_signal_handler_disconnect (combo_box->priv->model,
1275 combo_box->priv->deleted_id);
1276 combo_box->priv->deleted_id = -1;
1278 if (combo_box->priv->reordered_id != -1)
1280 g_signal_handler_disconnect (combo_box->priv->model,
1281 combo_box->priv->reordered_id);
1282 combo_box->priv->reordered_id = -1;
1284 if (combo_box->priv->changed_id != -1)
1286 g_signal_handler_disconnect (combo_box->priv->model,
1287 combo_box->priv->changed_id);
1288 combo_box->priv->changed_id = -1;
1292 if (!combo_box->priv->tree_view)
1294 if (combo_box->priv->popup_widget)
1295 gtk_container_foreach (GTK_CONTAINER (combo_box->priv->popup_widget),
1296 (GtkCallback)gtk_widget_destroy, NULL);
1301 gtk_combo_box_set_model_internal (GtkComboBox *combo_box)
1303 combo_box->priv->inserted_id =
1304 g_signal_connect (combo_box->priv->model, "row_inserted",
1305 G_CALLBACK (gtk_combo_box_model_row_inserted),
1307 combo_box->priv->deleted_id =
1308 g_signal_connect (combo_box->priv->model, "row_deleted",
1309 G_CALLBACK (gtk_combo_box_model_row_deleted),
1311 combo_box->priv->reordered_id =
1312 g_signal_connect (combo_box->priv->model, "rows_reordered",
1313 G_CALLBACK (gtk_combo_box_model_rows_reordered),
1315 combo_box->priv->changed_id =
1316 g_signal_connect (combo_box->priv->model, "row_changed",
1317 G_CALLBACK (gtk_combo_box_model_row_changed),
1320 if (combo_box->priv->tree_view)
1323 gtk_tree_view_set_model (GTK_TREE_VIEW (combo_box->priv->tree_view),
1324 combo_box->priv->model);
1329 gtk_combo_box_forall (GtkContainer *container,
1330 gboolean include_internals,
1331 GtkCallback callback,
1332 gpointer callback_data)
1334 GtkComboBox *combo_box = GTK_COMBO_BOX (container);
1336 if (include_internals)
1338 if (combo_box->priv->button)
1339 (* callback) (combo_box->priv->button, callback_data);
1340 if (combo_box->priv->separator)
1341 (* callback) (combo_box->priv->separator, callback_data);
1342 if (combo_box->priv->arrow)
1343 (* callback) (combo_box->priv->arrow, callback_data);
1344 if (combo_box->priv->cell_view_frame)
1345 (* callback) (combo_box->priv->cell_view_frame, callback_data);
1348 if (GTK_BIN (container)->child)
1349 (* callback) (GTK_BIN (container)->child, callback_data);
1353 gtk_combo_box_expose_event (GtkWidget *widget,
1354 GdkEventExpose *event)
1356 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1358 if (!combo_box->priv->tree_view)
1360 gtk_container_propagate_expose (GTK_CONTAINER (widget),
1361 combo_box->priv->button, event);
1363 if (combo_box->priv->separator)
1365 gtk_container_propagate_expose (GTK_CONTAINER (combo_box->priv->button),
1366 combo_box->priv->separator, event);
1368 /* if not in this case, arrow gets its expose event from button */
1369 gtk_container_propagate_expose (GTK_CONTAINER (combo_box->priv->button),
1370 combo_box->priv->arrow, event);
1375 gtk_container_propagate_expose (GTK_CONTAINER (widget),
1376 combo_box->priv->button, event);
1378 if (combo_box->priv->cell_view_frame)
1379 gtk_container_propagate_expose (GTK_CONTAINER (widget),
1380 combo_box->priv->cell_view_frame, event);
1383 gtk_container_propagate_expose (GTK_CONTAINER (widget),
1384 GTK_BIN (widget)->child, event);
1390 gtk_combo_box_scroll_event (GtkWidget *widget,
1391 GdkEventScroll *event)
1393 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1397 index = gtk_combo_box_get_active (combo_box);
1401 items = gtk_tree_model_iter_n_children (combo_box->priv->model, NULL);
1403 if (event->direction == GDK_SCROLL_UP)
1408 gtk_combo_box_set_active (combo_box, CLAMP (index, 0, items - 1));
1419 cell_view_sync_cells (GtkComboBox *combo_box,
1420 GtkCellView *cell_view)
1424 for (k = combo_box->priv->cells; k; k = k->next)
1427 ComboCellInfo *info = (ComboCellInfo *)k->data;
1429 if (info->pack == GTK_PACK_START)
1430 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (cell_view),
1431 info->cell, info->expand);
1432 else if (info->pack == GTK_PACK_END)
1433 gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (cell_view),
1434 info->cell, info->expand);
1436 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (cell_view),
1438 info->func, info->func_data, NULL);
1440 for (j = info->attributes; j; j = j->next->next)
1442 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (cell_view),
1445 GPOINTER_TO_INT (j->next->data));
1451 gtk_combo_box_menu_setup (GtkComboBox *combo_box,
1452 gboolean add_childs)
1456 /* Unset any existing model. */
1457 gtk_combo_box_unset_model (combo_box);
1459 if (combo_box->priv->cell_view)
1461 combo_box->priv->button = gtk_toggle_button_new ();
1462 g_signal_connect (combo_box->priv->button, "toggled",
1463 G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
1464 gtk_widget_set_parent (combo_box->priv->button,
1465 GTK_BIN (combo_box)->child->parent);
1467 combo_box->priv->separator = gtk_vseparator_new ();
1468 gtk_widget_set_parent (combo_box->priv->separator,
1469 combo_box->priv->button);
1470 gtk_widget_show (combo_box->priv->separator);
1472 combo_box->priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
1473 gtk_widget_set_parent (combo_box->priv->arrow, combo_box->priv->button);
1474 gtk_widget_show (combo_box->priv->arrow);
1476 gtk_widget_show_all (combo_box->priv->button);
1478 if (GTK_WIDGET_MAPPED (GTK_BIN (combo_box)->child))
1480 /* I have no clue why, but we need to manually map in this case. */
1481 gtk_widget_map (combo_box->priv->separator);
1482 gtk_widget_map (combo_box->priv->arrow);
1487 combo_box->priv->button = gtk_toggle_button_new ();
1488 g_signal_connect (combo_box->priv->button, "toggled",
1489 G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
1490 gtk_widget_set_parent (combo_box->priv->button,
1491 GTK_BIN (combo_box)->child->parent);
1493 combo_box->priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
1494 gtk_container_add (GTK_CONTAINER (combo_box->priv->button),
1495 combo_box->priv->arrow);
1496 gtk_widget_show_all (combo_box->priv->button);
1499 g_signal_connect (combo_box->priv->button, "button_press_event",
1500 G_CALLBACK (gtk_combo_box_menu_button_press),
1503 /* create our funky menu */
1504 box = gtk_menu_new ();
1505 gtk_combo_box_set_popup_widget (combo_box, box);
1507 /* set the models */
1508 gtk_combo_box_set_model_internal (combo_box);
1512 gtk_combo_box_menu_fill (combo_box);
1517 gtk_combo_box_menu_fill (GtkComboBox *combo_box)
1523 if (!combo_box->priv->model)
1526 items = gtk_tree_model_iter_n_children (combo_box->priv->model, NULL);
1527 menu = combo_box->priv->popup_widget;
1529 for (i = 0; i < items; i++)
1533 path = gtk_tree_path_new_from_indices (i, -1);
1534 tmp = gtk_cell_view_menu_item_new_from_model (combo_box->priv->model,
1536 g_signal_connect (tmp, "activate",
1537 G_CALLBACK (gtk_combo_box_menu_item_activate),
1540 cell_view_sync_cells (combo_box,
1541 GTK_CELL_VIEW (GTK_BIN (tmp)->child));
1543 gtk_menu_shell_append (GTK_MENU_SHELL (menu), tmp);
1544 gtk_widget_show (tmp);
1546 gtk_tree_path_free (path);
1551 gtk_combo_box_menu_destroy (GtkComboBox *combo_box)
1553 /* disconnect signal handlers */
1554 gtk_combo_box_unset_model (combo_box);
1556 g_signal_handlers_disconnect_matched (combo_box->priv->button,
1557 G_SIGNAL_MATCH_DATA,
1559 gtk_combo_box_menu_button_press, NULL);
1561 /* unparent will remove our latest ref */
1562 if (combo_box->priv->cell_view)
1564 gtk_widget_unparent (combo_box->priv->arrow);
1565 combo_box->priv->arrow = NULL;
1567 gtk_widget_unparent (combo_box->priv->separator);
1568 combo_box->priv->separator = NULL;
1570 gtk_widget_unparent (combo_box->priv->button);
1571 combo_box->priv->button = NULL;
1575 /* will destroy the arrow too */
1576 gtk_widget_unparent (combo_box->priv->button);
1578 combo_box->priv->button = NULL;
1579 combo_box->priv->arrow = NULL;
1582 /* changing the popup window will unref the menu and the childs */
1590 gtk_combo_box_item_get_size (GtkComboBox *combo_box,
1597 gtk_tree_model_iter_nth_child (combo_box->priv->model, &iter, NULL, index);
1601 if (combo_box->priv->col_column == -1)
1604 gtk_tree_model_get (combo_box->priv->model, &iter,
1605 combo_box->priv->col_column, cols,
1611 if (combo_box->priv->row_column == -1)
1614 gtk_tree_model_get (combo_box->priv->model, &iter,
1615 combo_box->priv->row_column, rows,
1621 menu_occupied (GtkMenu *menu,
1625 guint bottom_attach)
1629 g_return_val_if_fail (GTK_IS_MENU (menu), TRUE);
1630 g_return_val_if_fail (left_attach < right_attach, TRUE);
1631 g_return_val_if_fail (top_attach < bottom_attach, TRUE);
1633 for (i = GTK_MENU_SHELL (menu)->children; i; i = i->next)
1636 gboolean h_intersect = FALSE;
1637 gboolean v_intersect = FALSE;
1639 gtk_container_child_get (GTK_CONTAINER (menu), i->data,
1642 "bottom_attach", &b,
1646 /* look if this item intersects with the given coordinates */
1647 h_intersect = left_attach <= l && l <= right_attach;
1648 h_intersect &= left_attach <= r && r <= right_attach;
1650 v_intersect = top_attach <= t && t <= bottom_attach;
1651 v_intersect &= top_attach <= b && b <= bottom_attach;
1653 if (h_intersect && v_intersect)
1661 gtk_combo_box_relayout_item (GtkComboBox *combo_box,
1664 gint current_col = 0, current_row = 0;
1670 menu = combo_box->priv->popup_widget;
1671 if (!GTK_IS_MENU_SHELL (menu))
1674 list = gtk_container_get_children (GTK_CONTAINER (menu));
1675 item = g_list_nth_data (list, index);
1678 gtk_combo_box_item_get_size (combo_box, index, &cols, &rows);
1680 /* look for a good spot */
1683 if (current_col + cols > combo_box->priv->wrap_width)
1689 if (!menu_occupied (GTK_MENU (menu),
1690 current_col, current_col + cols,
1691 current_row, current_row + rows))
1697 /* set attach props */
1698 gtk_menu_attach (GTK_MENU (menu), item,
1699 current_col, current_col + cols,
1700 current_row, current_row + rows);
1704 gtk_combo_box_relayout (GtkComboBox *combo_box)
1710 /* ensure we are in menu style */
1711 if (combo_box->priv->tree_view)
1712 gtk_combo_box_list_destroy (combo_box);
1714 menu = combo_box->priv->popup_widget;
1716 if (!GTK_IS_MENU_SHELL (menu))
1718 gtk_combo_box_menu_setup (combo_box, FALSE);
1719 menu = combo_box->priv->popup_widget;
1722 /* get rid of all children */
1723 g_return_if_fail (GTK_IS_MENU_SHELL (menu));
1725 list = gtk_container_get_children (GTK_CONTAINER (menu));
1727 for (j = g_list_last (list); j; j = j->prev)
1728 gtk_container_remove (GTK_CONTAINER (menu), j->data);
1733 items = gtk_tree_model_iter_n_children (combo_box->priv->model, NULL);
1735 for (i = 0; i < items; i++)
1740 path = gtk_tree_path_new_from_indices (i, -1);
1741 tmp = gtk_cell_view_menu_item_new_from_model (combo_box->priv->model,
1744 g_signal_connect (tmp, "activate",
1745 G_CALLBACK (gtk_combo_box_menu_item_activate),
1748 cell_view_sync_cells (combo_box, GTK_CELL_VIEW (GTK_BIN (tmp)->child));
1750 gtk_menu_shell_insert (GTK_MENU_SHELL (menu), tmp, i);
1752 if (combo_box->priv->wrap_width)
1753 gtk_combo_box_relayout_item (combo_box, i);
1755 gtk_widget_show (tmp);
1757 gtk_tree_path_free (path);
1763 gtk_combo_box_menu_button_press (GtkWidget *widget,
1764 GdkEventButton *event,
1767 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1769 if (! GTK_IS_MENU (combo_box->priv->popup_widget))
1772 if (event->type == GDK_BUTTON_PRESS && event->button == 1)
1774 combo_box->priv->popup_in_progress = TRUE;
1775 gtk_menu_popup (GTK_MENU (combo_box->priv->popup_widget),
1777 gtk_combo_box_menu_position, combo_box,
1778 event->button, event->time);
1787 gtk_combo_box_menu_item_activate (GtkWidget *item,
1792 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1794 menu = combo_box->priv->popup_widget;
1795 g_return_if_fail (GTK_IS_MENU (menu));
1797 index = g_list_index (GTK_MENU_SHELL (menu)->children, item);
1799 gtk_combo_box_set_active (combo_box, index);
1803 gtk_combo_box_model_row_inserted (GtkTreeModel *model,
1808 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1809 gint index = gtk_tree_path_get_indices (path)[0];
1810 gint items = gtk_tree_model_iter_n_children (model, NULL);
1812 if (combo_box->priv->active_item >= index)
1813 combo_box->priv->active_item++;
1815 if (!combo_box->priv->tree_view)
1816 gtk_combo_box_menu_row_inserted (model, path, iter, user_data);
1819 gtk_combo_box_set_active (combo_box, 0);
1823 gtk_combo_box_model_row_deleted (GtkTreeModel *model,
1827 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1828 gint index = gtk_tree_path_get_indices (path)[0];
1830 if (!combo_box->priv->tree_view)
1831 gtk_combo_box_menu_row_deleted (model, path, user_data);
1833 if (index == combo_box->priv->active_item)
1835 gint items = gtk_tree_model_iter_n_children (model, NULL);
1838 gtk_combo_box_set_active (combo_box, -1);
1839 else if (index == items)
1840 gtk_combo_box_set_active (combo_box, index - 1);
1842 gtk_combo_box_set_active (combo_box, index);
1844 else if (combo_box->priv->active_item > index)
1845 combo_box->priv->active_item--;
1849 gtk_combo_box_model_rows_reordered (GtkTreeModel *model,
1855 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1856 gint items = gtk_tree_model_iter_n_children (model, NULL);
1859 combo_box->priv->active_item = new_order[combo_box->priv->active_item];
1861 if (!combo_box->priv->tree_view)
1862 gtk_combo_box_menu_rows_reordered (model, path, iter, new_order, user_data);
1867 gtk_combo_box_model_row_changed (GtkTreeModel *model,
1872 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1873 gint index = gtk_tree_path_get_indices (path)[0];
1875 if (index == combo_box->priv->active_item &&
1876 combo_box->priv->cell_view)
1877 gtk_widget_queue_resize (GTK_WIDGET (combo_box->priv->cell_view));
1879 if (combo_box->priv->tree_view)
1880 gtk_combo_box_list_row_changed (model, path, iter, user_data);
1882 gtk_combo_box_menu_row_changed (model, path, iter, user_data);
1887 gtk_combo_box_menu_row_inserted (GtkTreeModel *model,
1894 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1896 if (!combo_box->priv->popup_widget)
1899 menu = combo_box->priv->popup_widget;
1900 g_return_if_fail (GTK_IS_MENU (menu));
1902 item = gtk_cell_view_menu_item_new_from_model (model, path);
1903 g_signal_connect (item, "activate",
1904 G_CALLBACK (gtk_combo_box_menu_item_activate),
1907 cell_view_sync_cells (combo_box, GTK_CELL_VIEW (GTK_BIN (item)->child));
1909 gtk_menu_shell_insert (GTK_MENU_SHELL (menu), item,
1910 gtk_tree_path_get_indices (path)[0]);
1911 gtk_widget_show (item);
1915 gtk_combo_box_menu_row_deleted (GtkTreeModel *model,
1922 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1924 if (!combo_box->priv->popup_widget)
1927 index = gtk_tree_path_get_indices (path)[0];
1929 menu = combo_box->priv->popup_widget;
1930 g_return_if_fail (GTK_IS_MENU (menu));
1932 item = g_list_nth_data (GTK_MENU_SHELL (menu)->children, index);
1933 g_return_if_fail (GTK_IS_MENU_ITEM (item));
1935 gtk_container_remove (GTK_CONTAINER (menu), item);
1939 gtk_combo_box_menu_rows_reordered (GtkTreeModel *model,
1945 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1947 gtk_combo_box_relayout (combo_box);
1951 gtk_combo_box_menu_row_changed (GtkTreeModel *model,
1956 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1959 if (!combo_box->priv->popup_widget)
1962 if (combo_box->priv->wrap_width)
1963 gtk_combo_box_relayout_item (combo_box,
1964 gtk_tree_path_get_indices (path)[0]);
1966 width = gtk_combo_box_calc_requested_width (combo_box, path);
1968 if (width > combo_box->priv->width)
1970 if (combo_box->priv->cell_view)
1972 gtk_widget_set_size_request (combo_box->priv->cell_view, width, -1);
1973 gtk_widget_queue_resize (combo_box->priv->cell_view);
1975 combo_box->priv->width = width;
1984 gtk_combo_box_list_setup (GtkComboBox *combo_box)
1987 GtkTreeSelection *sel;
1989 /* Unset any existing model. */
1990 gtk_combo_box_unset_model (combo_box);
1992 combo_box->priv->button = gtk_toggle_button_new ();
1993 gtk_widget_set_parent (combo_box->priv->button,
1994 GTK_BIN (combo_box)->child->parent);
1995 g_signal_connect (combo_box->priv->button, "button_press_event",
1996 G_CALLBACK (gtk_combo_box_list_button_pressed), combo_box);
1997 g_signal_connect (combo_box->priv->button, "toggled",
1998 G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
2000 combo_box->priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
2001 gtk_container_add (GTK_CONTAINER (combo_box->priv->button),
2002 combo_box->priv->arrow);
2003 combo_box->priv->separator = NULL;
2004 gtk_widget_show_all (combo_box->priv->button);
2006 if (combo_box->priv->cell_view)
2008 combo_box->priv->cell_view_frame = gtk_frame_new (NULL);
2009 gtk_widget_set_parent (combo_box->priv->cell_view_frame,
2010 GTK_BIN (combo_box)->child->parent);
2011 gtk_frame_set_shadow_type (GTK_FRAME (combo_box->priv->cell_view_frame),
2014 g_object_set (G_OBJECT (combo_box->priv->cell_view),
2015 "background", "white",
2016 "background_set", TRUE,
2019 gtk_widget_show (combo_box->priv->cell_view_frame);
2022 combo_box->priv->tree_view = gtk_tree_view_new ();
2023 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (combo_box->priv->tree_view));
2024 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
2025 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (combo_box->priv->tree_view),
2028 g_signal_connect (combo_box->priv->tree_view, "button_press_event",
2029 G_CALLBACK (gtk_combo_box_list_button_pressed),
2031 g_signal_connect (combo_box->priv->tree_view, "button_release_event",
2032 G_CALLBACK (gtk_combo_box_list_button_released),
2034 g_signal_connect (combo_box->priv->tree_view, "key_press_event",
2035 G_CALLBACK (gtk_combo_box_list_key_press),
2038 combo_box->priv->column = gtk_tree_view_column_new ();
2039 gtk_tree_view_append_column (GTK_TREE_VIEW (combo_box->priv->tree_view),
2040 combo_box->priv->column);
2042 /* set the models */
2043 gtk_combo_box_set_model_internal (combo_box);
2046 for (i = combo_box->priv->cells; i; i = i->next)
2049 ComboCellInfo *info = (ComboCellInfo *)i->data;
2051 if (info->pack == GTK_PACK_START)
2052 gtk_tree_view_column_pack_start (combo_box->priv->column,
2053 info->cell, info->expand);
2054 else if (info->pack == GTK_PACK_END)
2055 gtk_tree_view_column_pack_end (combo_box->priv->column,
2056 info->cell, info->expand);
2058 for (j = info->attributes; j; j = j->next->next)
2060 gtk_tree_view_column_add_attribute (combo_box->priv->column,
2063 GPOINTER_TO_INT (j->next->data));
2067 if (combo_box->priv->active_item != -1)
2071 path = gtk_tree_path_new_from_indices (combo_box->priv->active_item, -1);
2074 gtk_tree_view_set_cursor (GTK_TREE_VIEW (combo_box->priv->tree_view),
2076 gtk_tree_path_free (path);
2080 /* set sample/popup widgets */
2081 gtk_combo_box_set_popup_widget (combo_box, combo_box->priv->tree_view);
2083 gtk_widget_show (combo_box->priv->tree_view);
2087 gtk_combo_box_list_destroy (GtkComboBox *combo_box)
2089 /* disconnect signals */
2090 gtk_combo_box_unset_model (combo_box);
2092 g_signal_handlers_disconnect_matched (combo_box->priv->tree_view,
2093 G_SIGNAL_MATCH_DATA,
2094 0, 0, NULL, NULL, combo_box);
2095 g_signal_handlers_disconnect_matched (combo_box->priv->button,
2096 G_SIGNAL_MATCH_DATA,
2098 gtk_combo_box_list_button_pressed,
2101 /* destroy things (unparent will kill the latest ref from us)
2102 * last unref on button will destroy the arrow
2104 gtk_widget_unparent (combo_box->priv->button);
2105 combo_box->priv->button = NULL;
2106 combo_box->priv->arrow = NULL;
2108 if (combo_box->priv->cell_view)
2110 g_object_set (G_OBJECT (combo_box->priv->cell_view),
2111 "background_set", FALSE,
2114 gtk_widget_unparent (combo_box->priv->cell_view_frame);
2115 combo_box->priv->cell_view_frame = NULL;
2118 gtk_widget_destroy (combo_box->priv->tree_view);
2120 combo_box->priv->tree_view = NULL;
2121 combo_box->priv->popup_widget = NULL;
2126 gtk_combo_box_list_remove_grabs (GtkComboBox *combo_box)
2128 if (GTK_WIDGET_HAS_GRAB (combo_box->priv->tree_view))
2129 gtk_grab_remove (combo_box->priv->tree_view);
2131 if (GTK_WIDGET_HAS_GRAB (combo_box->priv->popup_window))
2133 gtk_grab_remove (combo_box->priv->popup_window);
2134 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
2135 gdk_pointer_ungrab (GDK_CURRENT_TIME);
2140 gtk_combo_box_list_button_pressed (GtkWidget *widget,
2141 GdkEventButton *event,
2144 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
2146 GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *)event);
2148 if (ewidget == combo_box->priv->tree_view)
2151 if ((ewidget != combo_box->priv->button) ||
2152 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (combo_box->priv->button)))
2155 gtk_combo_box_popup (combo_box);
2157 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
2160 combo_box->priv->popup_in_progress = TRUE;
2166 gtk_combo_box_list_button_released (GtkWidget *widget,
2167 GdkEventButton *event,
2171 GtkTreePath *path = NULL;
2173 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
2175 gboolean popup_in_progress = FALSE;
2177 GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *)event);
2179 if (combo_box->priv->popup_in_progress)
2181 popup_in_progress = TRUE;
2182 combo_box->priv->popup_in_progress = FALSE;
2185 if (ewidget != combo_box->priv->tree_view)
2187 if (ewidget == combo_box->priv->button &&
2188 !popup_in_progress &&
2189 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (combo_box->priv->button)))
2191 gtk_combo_box_popdown (combo_box);
2195 /* released outside treeview */
2196 if (ewidget != combo_box->priv->button)
2198 gtk_combo_box_popdown (combo_box);
2206 /* select something cool */
2207 ret = gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
2213 return TRUE; /* clicked outside window? */
2215 gtk_combo_box_set_active (combo_box, gtk_tree_path_get_indices (path)[0]);
2216 gtk_combo_box_popdown (combo_box);
2218 gtk_tree_path_free (path);
2224 gtk_combo_box_list_key_press (GtkWidget *widget,
2228 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
2230 if ((event->keyval == GDK_Return || event->keyval == GDK_KP_Enter ||
2231 event->keyval == GDK_space || event->keyval == GDK_KP_Space) ||
2232 event->keyval == GDK_Escape)
2234 if (event->keyval != GDK_Escape)
2238 GtkTreeModel *model = NULL;
2239 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (combo_box->priv->tree_view));
2241 ret = gtk_tree_selection_get_selected (sel, &model, &iter);
2246 path = gtk_tree_model_get_path (model, &iter);
2249 gtk_combo_box_set_active (combo_box, gtk_tree_path_get_indices (path)[0]);
2250 gtk_tree_path_free (path);
2255 /* reset active item -- this is incredibly lame and ugly */
2256 gtk_combo_box_set_active (combo_box,
2257 gtk_combo_box_get_active (combo_box));
2259 gtk_combo_box_popdown (combo_box);
2268 gtk_combo_box_list_row_changed (GtkTreeModel *model,
2273 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
2276 width = gtk_combo_box_calc_requested_width (combo_box, path);
2278 if (width > combo_box->priv->width)
2280 if (combo_box->priv->cell_view)
2282 gtk_widget_set_size_request (combo_box->priv->cell_view, width, -1);
2283 gtk_widget_queue_resize (combo_box->priv->cell_view);
2285 combo_box->priv->width = width;
2290 * GtkCellLayout implementation
2293 gtk_combo_box_cell_layout_pack_start (GtkCellLayout *layout,
2294 GtkCellRenderer *cell,
2297 ComboCellInfo *info;
2298 GtkComboBox *combo_box;
2301 g_return_if_fail (GTK_IS_COMBO_BOX (layout));
2302 g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
2304 combo_box = GTK_COMBO_BOX (layout);
2306 g_object_ref (G_OBJECT (cell));
2307 gtk_object_sink (GTK_OBJECT (cell));
2309 info = g_new0 (ComboCellInfo, 1);
2311 info->expand = expand;
2312 info->pack = GTK_PACK_START;
2314 combo_box->priv->cells = g_slist_append (combo_box->priv->cells, info);
2316 if (combo_box->priv->cell_view)
2317 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box->priv->cell_view),
2320 if (combo_box->priv->column)
2321 gtk_tree_view_column_pack_start (combo_box->priv->column, cell, expand);
2323 menu = combo_box->priv->popup_widget;
2324 if (GTK_IS_MENU (menu))
2328 list = gtk_container_get_children (GTK_CONTAINER (menu));
2329 for (i = list; i; i = i->next)
2333 if (GTK_IS_CELL_VIEW_MENU_ITEM (i->data))
2334 view = GTK_CELL_VIEW (GTK_BIN (i->data)->child);
2336 view = GTK_CELL_VIEW (i->data);
2338 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (view), cell, expand);
2345 gtk_combo_box_cell_layout_pack_end (GtkCellLayout *layout,
2346 GtkCellRenderer *cell,
2349 ComboCellInfo *info;
2350 GtkComboBox *combo_box;
2353 g_return_if_fail (GTK_IS_COMBO_BOX (layout));
2354 g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
2356 combo_box = GTK_COMBO_BOX (layout);
2358 g_object_ref (G_OBJECT (cell));
2359 gtk_object_sink (GTK_OBJECT (cell));
2361 info = g_new0 (ComboCellInfo, 1);
2363 info->expand = expand;
2364 info->pack = GTK_PACK_END;
2366 if (combo_box->priv->cell_view)
2367 gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (combo_box->priv->cell_view),
2370 if (combo_box->priv->column)
2371 gtk_tree_view_column_pack_end (combo_box->priv->column, cell, expand);
2373 menu = combo_box->priv->popup_widget;
2374 if (GTK_IS_MENU (menu))
2378 list = gtk_container_get_children (GTK_CONTAINER (menu));
2379 for (i = list; i; i = i->next)
2383 if (GTK_IS_CELL_VIEW_MENU_ITEM (i->data))
2384 view = GTK_CELL_VIEW (GTK_BIN (i->data)->child);
2386 view = GTK_CELL_VIEW (i->data);
2388 gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (view), cell, expand);
2395 gtk_combo_box_cell_layout_clear (GtkCellLayout *layout)
2398 GtkComboBox *combo_box;
2401 g_return_if_fail (GTK_IS_COMBO_BOX (layout));
2403 combo_box = GTK_COMBO_BOX (layout);
2405 if (combo_box->priv->cell_view)
2406 gtk_cell_layout_clear (GTK_CELL_LAYOUT (combo_box->priv->cell_view));
2408 if (combo_box->priv->column)
2409 gtk_tree_view_column_clear (combo_box->priv->column);
2411 for (i = combo_box->priv->cells; i; i = i->next)
2413 ComboCellInfo *info = (ComboCellInfo *)i->data;
2415 gtk_combo_box_cell_layout_clear_attributes (layout, info->cell);
2416 g_object_unref (G_OBJECT (info->cell));
2420 g_slist_free (combo_box->priv->cells);
2421 combo_box->priv->cells = NULL;
2423 menu = combo_box->priv->popup_widget;
2424 if (GTK_IS_MENU (menu))
2428 list = gtk_container_get_children (GTK_CONTAINER (menu));
2429 for (i = list; i; i = i->next)
2433 if (GTK_IS_CELL_VIEW_MENU_ITEM (i->data))
2434 view = GTK_CELL_VIEW (GTK_BIN (i->data)->child);
2436 view = GTK_CELL_VIEW (i->data);
2438 gtk_cell_layout_clear (GTK_CELL_LAYOUT (view));
2445 gtk_combo_box_cell_layout_add_attribute (GtkCellLayout *layout,
2446 GtkCellRenderer *cell,
2447 const gchar *attribute,
2450 ComboCellInfo *info;
2451 GtkComboBox *combo_box;
2454 g_return_if_fail (GTK_IS_COMBO_BOX (layout));
2455 g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
2457 combo_box = GTK_COMBO_BOX (layout);
2459 info = gtk_combo_box_get_cell_info (combo_box, cell);
2461 info->attributes = g_slist_prepend (info->attributes,
2462 GINT_TO_POINTER (column));
2463 info->attributes = g_slist_prepend (info->attributes,
2464 g_strdup (attribute));
2466 if (combo_box->priv->cell_view)
2467 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box->priv->cell_view),
2468 cell, attribute, column);
2470 if (combo_box->priv->column)
2471 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box->priv->column),
2472 cell, attribute, column);
2474 menu = combo_box->priv->popup_widget;
2475 if (GTK_IS_MENU (menu))
2479 list = gtk_container_get_children (GTK_CONTAINER (menu));
2480 for (i = list; i; i = i->next)
2484 if (GTK_IS_CELL_VIEW_MENU_ITEM (i->data))
2485 view = GTK_CELL_VIEW (GTK_BIN (i->data)->child);
2487 view = GTK_CELL_VIEW (i->data);
2489 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (view), cell,
2495 gtk_widget_queue_resize (GTK_WIDGET (combo_box));
2499 gtk_combo_box_cell_layout_set_cell_data_func (GtkCellLayout *layout,
2500 GtkCellRenderer *cell,
2501 GtkCellLayoutDataFunc func,
2503 GDestroyNotify destroy)
2505 ComboCellInfo *info;
2506 GtkComboBox *combo_box;
2509 g_return_if_fail (GTK_IS_COMBO_BOX (layout));
2511 combo_box = GTK_COMBO_BOX (layout);
2513 info = gtk_combo_box_get_cell_info (combo_box, cell);
2514 g_return_if_fail (info != NULL);
2518 GDestroyNotify d = info->destroy;
2520 info->destroy = NULL;
2521 d (info->func_data);
2525 info->func_data = func_data;
2526 info->destroy = destroy;
2528 if (combo_box->priv->cell_view)
2529 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo_box->priv->cell_view), cell, func, func_data, NULL);
2531 if (combo_box->priv->column)
2532 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo_box->priv->column), cell, func, func_data, NULL);
2534 menu = combo_box->priv->popup_widget;
2535 if (GTK_IS_MENU (menu))
2539 list = gtk_container_get_children (GTK_CONTAINER (menu));
2540 for (i = list; i; i = i->next)
2544 if (GTK_IS_CELL_VIEW_MENU_ITEM (i->data))
2545 view = GTK_CELL_VIEW (GTK_BIN (i->data)->child);
2547 view = GTK_CELL_VIEW (i->data);
2549 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (view), cell,
2550 func, func_data, NULL);
2555 gtk_widget_queue_resize (GTK_WIDGET (combo_box));
2559 gtk_combo_box_cell_layout_clear_attributes (GtkCellLayout *layout,
2560 GtkCellRenderer *cell)
2562 ComboCellInfo *info;
2563 GtkComboBox *combo_box;
2567 g_return_if_fail (GTK_IS_COMBO_BOX (layout));
2568 g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
2570 combo_box = GTK_COMBO_BOX (layout);
2572 info = gtk_combo_box_get_cell_info (combo_box, cell);
2573 g_return_if_fail (info != NULL);
2575 list = info->attributes;
2576 while (list && list->next)
2578 g_free (list->data);
2579 list = list->next->next;
2581 g_slist_free (info->attributes);
2582 info->attributes = NULL;
2584 if (combo_box->priv->cell_view)
2585 gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (combo_box->priv->cell_view), cell);
2587 if (combo_box->priv->column)
2588 gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (combo_box->priv->column), cell);
2590 menu = combo_box->priv->popup_widget;
2591 if (GTK_IS_MENU (menu))
2595 list = gtk_container_get_children (GTK_CONTAINER (menu));
2596 for (i = list; i; i = i->next)
2600 if (GTK_IS_CELL_VIEW_MENU_ITEM (i->data))
2601 view = GTK_CELL_VIEW (GTK_BIN (i->data)->child);
2603 view = GTK_CELL_VIEW (i->data);
2605 gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (view), cell);
2610 gtk_widget_queue_resize (GTK_WIDGET (combo_box));
2614 gtk_combo_box_cell_layout_reorder (GtkCellLayout *layout,
2615 GtkCellRenderer *cell,
2618 ComboCellInfo *info;
2619 GtkComboBox *combo_box;
2623 g_return_if_fail (GTK_IS_COMBO_BOX (layout));
2624 g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
2626 combo_box = GTK_COMBO_BOX (layout);
2628 info = gtk_combo_box_get_cell_info (combo_box, cell);
2630 g_return_if_fail (info != NULL);
2631 g_return_if_fail (position >= 0);
2633 link = g_slist_find (combo_box->priv->cells, info);
2635 g_return_if_fail (link != NULL);
2637 combo_box->priv->cells = g_slist_remove_link (combo_box->priv->cells, link);
2638 combo_box->priv->cells = g_slist_insert (combo_box->priv->cells, info,
2641 if (combo_box->priv->cell_view)
2642 gtk_cell_layout_reorder (GTK_CELL_LAYOUT (combo_box->priv->cell_view),
2645 if (combo_box->priv->column)
2646 gtk_cell_layout_reorder (GTK_CELL_LAYOUT (combo_box->priv->column),
2649 menu = combo_box->priv->popup_widget;
2650 if (GTK_IS_MENU (menu))
2654 list = gtk_container_get_children (GTK_CONTAINER (menu));
2655 for (i = list; i; i = i->next)
2659 if (GTK_IS_CELL_VIEW_MENU_ITEM (i->data))
2660 view = GTK_CELL_VIEW (GTK_BIN (i->data)->child);
2662 view = GTK_CELL_VIEW (i->data);
2664 gtk_cell_layout_reorder (GTK_CELL_LAYOUT (view), cell, position);
2669 gtk_widget_queue_draw (GTK_WIDGET (combo_box));
2677 * gtk_combo_box_new:
2679 * Creates a new empty #GtkComboBox.
2681 * Return value: A new #GtkComboBox.
2686 gtk_combo_box_new (void)
2688 return GTK_WIDGET (g_object_new (GTK_TYPE_COMBO_BOX, NULL));
2692 * gtk_combo_box_new_with_model:
2693 * @model: A #GtkTreeModel.
2695 * Creates a new #GtkComboBox with the model initialized to @model.
2697 * Return value: A new #GtkComboBox.
2702 gtk_combo_box_new_with_model (GtkTreeModel *model)
2704 GtkComboBox *combo_box;
2706 g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL);
2708 combo_box = GTK_COMBO_BOX (g_object_new (GTK_TYPE_COMBO_BOX,
2712 return GTK_WIDGET (combo_box);
2716 * gtk_combo_box_set_wrap_width:
2717 * @combo_box: A #GtkComboBox.
2718 * @width: Preferred number of columns.
2720 * Sets the wrap width of @combo_box to be @width. The wrap width is basically
2721 * the preferred number of columns when you want to the popup to be layed out
2727 gtk_combo_box_set_wrap_width (GtkComboBox *combo_box,
2730 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2731 g_return_if_fail (width >= 0);
2733 if (width != combo_box->priv->wrap_width)
2735 combo_box->priv->wrap_width = width;
2737 gtk_combo_box_relayout (combo_box);
2738 gtk_combo_box_style_set (GTK_WIDGET (combo_box), NULL);
2740 g_object_notify (G_OBJECT (combo_box), "wrap_width");
2745 * gtk_combo_box_set_row_span_column:
2746 * @combo_box: A #GtkComboBox.
2747 * @row_span: A column in the model passed during construction.
2749 * Sets the column with row span information for @combo_box to be @row_span.
2750 * The row span column contains integers which indicate how many rows
2751 * an item should span.
2756 gtk_combo_box_set_row_span_column (GtkComboBox *combo_box,
2761 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2763 col = gtk_tree_model_get_n_columns (combo_box->priv->model);
2764 g_return_if_fail (row_span >= 0 && row_span < col);
2766 if (row_span != combo_box->priv->row_column)
2768 combo_box->priv->row_column = row_span;
2770 gtk_combo_box_relayout (combo_box);
2772 g_object_notify (G_OBJECT (combo_box), "row_span_column");
2777 * gtk_combo_box_set_column_span_column:
2778 * @combo_box: A #GtkComboBox.
2779 * @column_span: A column in the model passed during construction.
2781 * Sets the column with column span information for @combo_box to be
2782 * @column_span. The column span column contains integers which indicate
2783 * how many columns an item should span.
2788 gtk_combo_box_set_column_span_column (GtkComboBox *combo_box,
2793 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2795 col = gtk_tree_model_get_n_columns (combo_box->priv->model);
2796 g_return_if_fail (column_span >= 0 && column_span < col);
2798 if (column_span != combo_box->priv->col_column)
2800 combo_box->priv->col_column = column_span;
2802 gtk_combo_box_relayout (combo_box);
2804 g_object_notify (G_OBJECT (combo_box), "column_span_column");
2809 * gtk_combo_box_get_active:
2810 * @combo_box: A #GtkComboBox.
2812 * Returns the index of the currently active item, or -1 if there's no
2815 * Return value: An integer which is the index of the currently active item, or
2816 * -1 if there's no active item.
2821 gtk_combo_box_get_active (GtkComboBox *combo_box)
2823 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), 0);
2825 return combo_box->priv->active_item;
2829 * gtk_combo_box_set_active:
2830 * @combo_box: A #GtkComboBox.
2831 * @index: An index in the model passed during construction, or -1 to have
2834 * Sets the active item of @combo_box to be the item at @index.
2839 gtk_combo_box_set_active (GtkComboBox *combo_box,
2844 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2845 /* -1 means "no item selected" */
2846 g_return_if_fail (index >= -1);
2848 if (combo_box->priv->active_item == index)
2851 combo_box->priv->active_item = index;
2855 if (combo_box->priv->tree_view)
2856 gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (combo_box->priv->tree_view)));
2859 GtkMenu *menu = GTK_MENU (combo_box->priv->popup_widget);
2861 if (GTK_IS_MENU (menu))
2862 gtk_menu_set_active (menu, -1);
2865 if (combo_box->priv->cell_view)
2866 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (combo_box->priv->cell_view), NULL);
2870 path = gtk_tree_path_new_from_indices (index, -1);
2872 if (combo_box->priv->tree_view)
2873 gtk_tree_view_set_cursor (GTK_TREE_VIEW (combo_box->priv->tree_view), path, NULL, FALSE);
2876 GtkMenu *menu = GTK_MENU (combo_box->priv->popup_widget);
2878 if (GTK_IS_MENU (menu))
2879 gtk_menu_set_active (GTK_MENU (menu), index);
2882 if (combo_box->priv->cell_view)
2883 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (combo_box->priv->cell_view), path);
2885 gtk_tree_path_free (path);
2888 g_signal_emit_by_name (combo_box, "changed", NULL, NULL);
2893 * gtk_combo_box_get_active_iter:
2894 * @combo_box: A #GtkComboBox
2895 * @iter: The uninitialized #GtkTreeIter.
2897 * Sets @iter to point to the current active item, if it exists.
2899 * Return value: %TRUE, if @iter was set
2904 gtk_combo_box_get_active_iter (GtkComboBox *combo_box,
2911 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), FALSE);
2913 active = gtk_combo_box_get_active (combo_box);
2917 path = gtk_tree_path_new_from_indices (active, -1);
2918 retval = gtk_tree_model_get_iter (gtk_combo_box_get_model (combo_box),
2920 gtk_tree_path_free (path);
2926 * gtk_combo_box_set_active_iter:
2927 * @combo_box: A #GtkComboBox
2928 * @iter: The #GtkTreeIter.
2930 * Sets the current active item to be the one referenced by @iter.
2931 * @iter must correspond to a path of depth one.
2936 gtk_combo_box_set_active_iter (GtkComboBox *combo_box,
2941 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2943 path = gtk_tree_model_get_path (gtk_combo_box_get_model (combo_box), iter);
2944 g_return_if_fail (path != NULL);
2945 g_return_if_fail (gtk_tree_path_get_depth (path) == 1);
2947 gtk_combo_box_set_active (combo_box, gtk_tree_path_get_indices (path)[0]);
2948 gtk_tree_path_free (path);
2952 * gtk_combo_box_set_model:
2953 * @combo_box: A #GtkComboBox.
2954 * @model: A #GtkTreeModel.
2956 * Sets the model used by @combo_box to be @model. Will unset a
2957 * previously set model (if applicable).
2962 gtk_combo_box_set_model (GtkComboBox *combo_box,
2963 GtkTreeModel *model)
2965 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2966 g_return_if_fail (GTK_IS_TREE_MODEL (model));
2968 if (combo_box->priv->model)
2970 gtk_combo_box_unset_model (combo_box);
2971 g_object_unref (G_OBJECT (combo_box->priv->model));
2974 combo_box->priv->model = model;
2975 g_object_ref (G_OBJECT (combo_box->priv->model));
2977 gtk_combo_box_set_model_internal (combo_box);
2978 if (!combo_box->priv->tree_view && combo_box->priv->popup_widget)
2979 gtk_combo_box_menu_fill (combo_box);
2981 if (combo_box->priv->cell_view)
2982 gtk_cell_view_set_model (GTK_CELL_VIEW (combo_box->priv->cell_view),
2983 combo_box->priv->model);
2987 * gtk_combo_box_get_model
2988 * @combo_box: A #GtkComboBox.
2990 * Returns the #GtkTreeModel which is acting as data source for @combo_box.
2992 * Return value: A #GtkTreeModel which was passed during construction.
2997 gtk_combo_box_get_model (GtkComboBox *combo_box)
2999 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
3001 return combo_box->priv->model;
3005 /* convenience API for simple text combos */
3008 * gtk_combo_box_new_text:
3010 * Convenience function which constructs a new text combo box, which is a
3011 * #GtkComboBox just displaying strings. If you use this function to create
3012 * a text combo box, you should only manipulate its data source with the
3013 * following convenience functions: gtk_combo_box_append_text(),
3014 * gtk_combo_box_insert_text(), gtk_combo_box_prepend_text() and
3015 * gtk_combo_box_remove_text().
3017 * Return value: A new text combo box.
3022 gtk_combo_box_new_text (void)
3024 GtkWidget *combo_box;
3025 GtkCellRenderer *cell;
3026 GtkListStore *store;
3028 store = gtk_list_store_new (1, G_TYPE_STRING);
3030 combo_box = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store));
3032 cell = gtk_cell_renderer_text_new ();
3033 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), cell, TRUE);
3034 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), cell,
3042 * gtk_combo_box_append_text:
3043 * @combo_box: A #GtkComboBox constructed using gtk_combo_box_new_text().
3046 * Appends @string to the list of strings stored in @combo_box. Note that
3047 * you can only use this function with combo boxes constructed with
3048 * gtk_combo_box_new_text().
3053 gtk_combo_box_append_text (GtkComboBox *combo_box,
3057 GtkListStore *store;
3059 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
3060 g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model));
3061 g_return_if_fail (text != NULL);
3063 store = GTK_LIST_STORE (combo_box->priv->model);
3065 gtk_list_store_append (store, &iter);
3066 gtk_list_store_set (store, &iter, 0, text, -1);
3070 * gtk_combo_box_insert_text:
3071 * @combo_box: A #GtkComboBox constructed using gtk_combo_box_new_text().
3072 * @position: An index to insert @text.
3075 * Inserts @string at @position in the list of strings stored in @combo_box.
3076 * Note that you can only use this function with combo boxes constructed
3077 * with gtk_combo_box_new_text().
3082 gtk_combo_box_insert_text (GtkComboBox *combo_box,
3087 GtkListStore *store;
3089 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
3090 g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model));
3091 g_return_if_fail (position >= 0);
3092 g_return_if_fail (text != NULL);
3094 store = GTK_LIST_STORE (combo_box->priv->model);
3096 gtk_list_store_insert (store, &iter, position);
3097 gtk_list_store_set (store, &iter, 0, text, -1);
3101 * gtk_combo_box_prepend_text:
3102 * @combo_box: A #GtkComboBox constructed with gtk_combo_box_new_text().
3105 * Prepends @string to the list of strings stored in @combo_box. Note that
3106 * you can only use this function with combo boxes constructed with
3107 * gtk_combo_box_new_text().
3112 gtk_combo_box_prepend_text (GtkComboBox *combo_box,
3116 GtkListStore *store;
3118 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
3119 g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model));
3120 g_return_if_fail (text != NULL);
3122 store = GTK_LIST_STORE (combo_box->priv->model);
3124 gtk_list_store_prepend (store, &iter);
3125 gtk_list_store_set (store, &iter, 0, text, -1);
3129 * gtk_combo_box_remove_text:
3130 * @combo_box: A #GtkComboBox constructed with gtk_combo_box_new_text().
3131 * @position: Index of the item to remove.
3133 * Removes the string at @position from @combo_box. Note that you can only use
3134 * this function with combo boxes constructed with gtk_combo_box_new_text().
3139 gtk_combo_box_remove_text (GtkComboBox *combo_box,
3143 GtkListStore *store;
3145 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
3146 g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model));
3147 g_return_if_fail (position >= 0);
3149 store = GTK_LIST_STORE (combo_box->priv->model);
3151 if (gtk_tree_model_iter_nth_child (combo_box->priv->model, &iter,
3153 gtk_list_store_remove (store, &iter);
3158 gtk_combo_box_mnemonic_activate (GtkWidget *widget,
3159 gboolean group_cycling)
3161 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
3163 gtk_widget_grab_focus (combo_box->priv->button);
3169 gtk_combo_box_destroy (GtkObject *object)
3171 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
3173 GTK_OBJECT_CLASS (parent_class)->destroy (object);
3175 combo_box->priv->cell_view = NULL;
3179 gtk_combo_box_finalize (GObject *object)
3181 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
3184 gtk_combo_box_unset_model (combo_box);
3186 if (GTK_IS_MENU (combo_box->priv->popup_widget))
3187 gtk_combo_box_menu_destroy (combo_box);
3189 if (GTK_IS_TREE_VIEW (combo_box->priv->tree_view))
3190 gtk_combo_box_list_destroy (combo_box);
3192 if (combo_box->priv->popup_window)
3193 gtk_widget_destroy (combo_box->priv->popup_window);
3195 if (combo_box->priv->model)
3196 g_object_unref (combo_box->priv->model);
3198 for (i = combo_box->priv->cells; i; i = i->next)
3200 ComboCellInfo *info = (ComboCellInfo *)i->data;
3201 GSList *list = info->attributes;
3204 info->destroy (info->func_data);
3206 while (list && list->next)
3208 g_free (list->data);
3209 list = list->next->next;
3211 g_slist_free (info->attributes);
3213 g_object_unref (G_OBJECT (info->cell));
3216 g_slist_free (combo_box->priv->cells);
3218 G_OBJECT_CLASS (parent_class)->finalize (object);