2 * Copyright (C) 2002, 2003 Kristian Rietveld <kris@gtk.org>
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
9 * This program 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 * General Public License for more details.
14 * You should have received a copy of the GNU General Public
15 * License along with this program; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
20 #include <gtk/gtkcombobox.h>
21 #include <gtk/gtkcelllayout.h>
22 #include <gtk/gtkcellview.h>
23 #include <gtk/gtkcellviewmenuitem.h>
25 #include <gtk/gtktreeselection.h>
26 #include <gtk/gtkframe.h>
27 #include <gtk/gtktogglebutton.h>
28 #include <gtk/gtkvseparator.h>
29 #include <gtk/gtkarrow.h>
30 #include <gtk/gtkmenu.h>
31 #include <gtk/gtkmain.h>
32 #include <gtk/gtkeventbox.h>
33 #include <gtk/gtkcellrenderertext.h>
34 #include <gtk/gtkbindings.h>
35 #include <gtk/gtkliststore.h>
36 #include <gtk/gtkwindow.h>
38 #include <gdk/gdkkeysyms.h>
41 #include <gobject/gvaluecollector.h>
45 #include "gtkmarshalers.h"
49 /* WELCOME, to THE house of evil code */
52 typedef struct _ComboCellInfo ComboCellInfo;
55 GtkCellRenderer *cell;
58 GtkCellLayoutDataFunc func;
60 GDestroyNotify destroy;
66 struct _GtkComboBoxPrivate
78 GtkTreeViewColumn *column;
84 GtkWidget *cell_view_frame;
90 GtkWidget *popup_widget;
91 GtkWidget *popup_window;
92 GtkWidget *popup_frame;
102 guint popup_in_progress : 1;
114 PROP_ROW_SPAN_COLUMN,
115 PROP_COLUMN_SPAN_COLUMN,
119 static GtkBinClass *parent_class = NULL;
120 static guint combo_box_signals[LAST_SIGNAL] = {0,};
122 #define BONUS_PADDING 4
126 static void gtk_combo_box_class_init (GtkComboBoxClass *klass);
127 static void gtk_combo_box_cell_layout_init (GtkCellLayoutIface *iface);
128 static void gtk_combo_box_init (GtkComboBox *combo_box);
130 static void gtk_combo_box_set_property (GObject *object,
134 static void gtk_combo_box_get_property (GObject *object,
139 static void gtk_combo_box_set_model (GtkComboBox *combo_box,
140 GtkTreeModel *model);
142 static void gtk_combo_box_style_set (GtkWidget *widget,
143 GtkStyle *previous_style,
145 static void gtk_combo_box_button_toggled (GtkWidget *widget,
147 static void gtk_combo_box_add (GtkContainer *container,
150 static ComboCellInfo *gtk_combo_box_get_cell_info (GtkComboBox *combo_box,
151 GtkCellRenderer *cell);
153 static void gtk_combo_box_menu_show (GtkWidget *menu,
155 static void gtk_combo_box_menu_hide (GtkWidget *menu,
158 static void gtk_combo_box_set_popup_widget (GtkComboBox *combo_box,
160 static void gtk_combo_box_menu_position (GtkMenu *menu,
165 static void gtk_combo_box_popup (GtkComboBox *combo_box);
166 static void gtk_combo_box_popdown (GtkComboBox *combo_box);
168 static gint gtk_combo_box_calc_requested_width (GtkComboBox *combo_box,
170 static void gtk_combo_box_remeasure (GtkComboBox *combo_box);
172 static void gtk_combo_box_size_request (GtkWidget *widget,
173 GtkRequisition *requisition);
174 static void gtk_combo_box_size_allocate (GtkWidget *widget,
175 GtkAllocation *allocation);
176 static void gtk_combo_box_forall (GtkContainer *container,
177 gboolean include_internals,
178 GtkCallback callback,
179 gpointer callback_data);
180 static gboolean gtk_combo_box_expose_event (GtkWidget *widget,
181 GdkEventExpose *event);
184 static void gtk_combo_box_list_setup (GtkComboBox *combo_box);
185 static void gtk_combo_box_list_destroy (GtkComboBox *combo_box);
187 static gboolean gtk_combo_box_list_button_released (GtkWidget *widget,
188 GdkEventButton *event,
190 static gboolean gtk_combo_box_list_key_press (GtkWidget *widget,
193 static gboolean gtk_combo_box_list_button_pressed (GtkWidget *widget,
194 GdkEventButton *event,
197 static void gtk_combo_box_list_row_changed (GtkTreeModel *model,
203 static void gtk_combo_box_menu_setup (GtkComboBox *combo_box,
204 gboolean add_childs);
205 static void gtk_combo_box_menu_destroy (GtkComboBox *combo_box);
207 static void gtk_combo_box_item_get_size (GtkComboBox *combo_box,
211 static void gtk_combo_box_relayout_item (GtkComboBox *combo_box,
213 static void gtk_combo_box_relayout (GtkComboBox *combo_box);
215 static gboolean gtk_combo_box_menu_button_press (GtkWidget *widget,
216 GdkEventButton *event,
218 static void gtk_combo_box_menu_item_activate (GtkWidget *item,
220 static void gtk_combo_box_menu_row_inserted (GtkTreeModel *model,
224 static void gtk_combo_box_menu_row_deleted (GtkTreeModel *model,
227 static void gtk_combo_box_menu_row_changed (GtkTreeModel *model,
233 static void gtk_combo_box_cell_layout_pack_start (GtkCellLayout *layout,
234 GtkCellRenderer *cell,
236 static void gtk_combo_box_cell_layout_pack_end (GtkCellLayout *layout,
237 GtkCellRenderer *cell,
239 static void gtk_combo_box_cell_layout_clear (GtkCellLayout *layout);
240 static void gtk_combo_box_cell_layout_add_attribute (GtkCellLayout *layout,
241 GtkCellRenderer *cell,
242 const gchar *attribute,
244 static void gtk_combo_box_cell_layout_set_cell_data_func (GtkCellLayout *layout,
245 GtkCellRenderer *cell,
246 GtkCellLayoutDataFunc func,
248 GDestroyNotify destroy);
249 static void gtk_combo_box_cell_layout_clear_attributes (GtkCellLayout *layout,
250 GtkCellRenderer *cell);
254 gtk_combo_box_get_type (void)
256 static GType combo_box_type = 0;
260 static const GTypeInfo combo_box_info =
262 sizeof (GtkComboBoxClass),
263 NULL, /* base_init */
264 NULL, /* base_finalize */
265 (GClassInitFunc) gtk_combo_box_class_init,
266 NULL, /* class_finalize */
267 NULL, /* class_data */
268 sizeof (GtkComboBox),
270 (GInstanceInitFunc) gtk_combo_box_init
273 static const GInterfaceInfo cell_layout_info =
275 (GInterfaceInitFunc) gtk_combo_box_cell_layout_init,
280 combo_box_type = g_type_register_static (GTK_TYPE_BIN,
285 g_type_add_interface_static (combo_box_type,
286 GTK_TYPE_CELL_LAYOUT,
290 return combo_box_type;
295 gtk_combo_box_class_init (GtkComboBoxClass *klass)
297 GObjectClass *object_class;
298 GtkBindingSet *binding_set;
299 GtkContainerClass *container_class;
300 GtkWidgetClass *widget_class;
302 binding_set = gtk_binding_set_by_class (klass);
304 container_class = (GtkContainerClass *)klass;
305 container_class->forall = gtk_combo_box_forall;
306 container_class->add = gtk_combo_box_add;
308 widget_class = (GtkWidgetClass *)klass;
309 widget_class->size_allocate = gtk_combo_box_size_allocate;
310 widget_class->size_request = gtk_combo_box_size_request;
311 widget_class->expose_event = gtk_combo_box_expose_event;
313 object_class = (GObjectClass *)klass;
314 object_class->set_property = gtk_combo_box_set_property;
315 object_class->get_property = gtk_combo_box_get_property;
317 parent_class = g_type_class_peek_parent (klass);
320 combo_box_signals[CHANGED] =
321 g_signal_new ("changed",
322 G_OBJECT_CLASS_TYPE (klass),
324 G_STRUCT_OFFSET (GtkComboBoxClass, changed),
326 g_cclosure_marshal_VOID__VOID,
330 g_object_class_install_property (object_class,
332 g_param_spec_object ("model",
334 _("The model for the combo box"),
336 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
338 g_object_class_install_property (object_class,
340 g_param_spec_int ("wrap_width",
342 _("Wrap width for layouting the items in a grid"),
348 g_object_class_install_property (object_class,
349 PROP_ROW_SPAN_COLUMN,
350 g_param_spec_int ("row_span_column",
351 _("Row span column"),
352 _("TreeModel column containing the row span values"),
358 g_object_class_install_property (object_class,
359 PROP_COLUMN_SPAN_COLUMN,
360 g_param_spec_int ("column_span_column",
361 _("Column span column"),
362 _("TreeModel column containing the column span values"),
368 g_object_class_install_property (object_class,
370 g_param_spec_int ("active",
372 _("The item which is currently active"),
378 gtk_widget_class_install_style_property (widget_class,
379 g_param_spec_boolean ("appearance",
380 _("ComboBox appareance"),
381 _("ComboBox appearance, where TRUE means Windows-style."),
385 g_type_class_add_private (object_class, sizeof (GtkComboBoxPrivate));
389 gtk_combo_box_cell_layout_init (GtkCellLayoutIface *iface)
391 iface->pack_start = gtk_combo_box_cell_layout_pack_start;
392 iface->pack_end = gtk_combo_box_cell_layout_pack_end;
393 iface->clear = gtk_combo_box_cell_layout_clear;
394 iface->add_attribute = gtk_combo_box_cell_layout_add_attribute;
395 iface->set_cell_data_func = gtk_combo_box_cell_layout_set_cell_data_func;
396 iface->clear_attributes = gtk_combo_box_cell_layout_clear_attributes;
400 gtk_combo_box_init (GtkComboBox *combo_box)
402 combo_box->priv = GTK_COMBO_BOX_GET_PRIVATE (combo_box);
404 g_signal_connect (combo_box, "style_set",
405 G_CALLBACK (gtk_combo_box_style_set), NULL);
407 combo_box->priv->cell_view = gtk_cell_view_new ();
408 gtk_container_add (GTK_CONTAINER (combo_box), combo_box->priv->cell_view);
409 gtk_widget_show (combo_box->priv->cell_view);
411 combo_box->priv->measurer = gtk_cell_view_new ();
413 combo_box->priv->width = 0;
414 combo_box->priv->wrap_width = 0;
416 combo_box->priv->active_item = -1;
417 combo_box->priv->col_column = -1;
418 combo_box->priv->row_column = -1;
422 gtk_combo_box_set_property (GObject *object,
427 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
432 gtk_combo_box_set_model (combo_box, g_value_get_object (value));
435 case PROP_WRAP_WIDTH:
436 gtk_combo_box_set_wrap_width (combo_box, g_value_get_int (value));
439 case PROP_ROW_SPAN_COLUMN:
440 gtk_combo_box_set_row_span_column (combo_box, g_value_get_int (value));
443 case PROP_COLUMN_SPAN_COLUMN:
444 gtk_combo_box_set_column_span_column (combo_box, g_value_get_int (value));
448 gtk_combo_box_set_active (combo_box, g_value_get_int (value));
457 gtk_combo_box_get_property (GObject *object,
462 GtkComboBox *combo_box = GTK_COMBO_BOX (object);
467 g_value_set_object (value, combo_box->priv->model);
470 case PROP_WRAP_WIDTH:
471 g_value_set_int (value, combo_box->priv->wrap_width);
474 case PROP_ROW_SPAN_COLUMN:
475 g_value_set_int (value, combo_box->priv->row_column);
478 case PROP_COLUMN_SPAN_COLUMN:
479 g_value_set_int (value, combo_box->priv->col_column);
483 g_value_set_int (value, gtk_combo_box_get_active (combo_box));
487 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
493 gtk_combo_box_set_model (GtkComboBox *combo_box,
496 if (combo_box->priv->model)
499 combo_box->priv->model = model;
500 g_object_ref (G_OBJECT (combo_box->priv->model));
502 if (combo_box->priv->cell_view)
503 gtk_cell_view_set_model (GTK_CELL_VIEW (combo_box->priv->cell_view),
504 combo_box->priv->model);
505 gtk_cell_view_set_model (GTK_CELL_VIEW (combo_box->priv->measurer),
506 combo_box->priv->model);
510 gtk_combo_box_style_set (GtkWidget *widget,
511 GtkStyle *previous_style,
515 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
517 /* if wrap_width > 0, then we are in grid-mode and forced to use
520 if (combo_box->priv->wrap_width)
523 gtk_widget_style_get (widget,
524 "appearance", &appearance,
527 /* TRUE is windows style */
530 if (GTK_IS_MENU (combo_box->priv->popup_widget))
531 gtk_combo_box_menu_destroy (combo_box);
532 gtk_combo_box_list_setup (combo_box);
536 if (GTK_IS_TREE_VIEW (combo_box->priv->tree_view))
537 gtk_combo_box_list_destroy (combo_box);
538 gtk_combo_box_menu_setup (combo_box, TRUE);
543 gtk_combo_box_button_toggled (GtkWidget *widget,
546 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
548 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
550 if (!combo_box->priv->popup_in_progress)
551 gtk_combo_box_popup (combo_box);
554 gtk_combo_box_popdown (combo_box);
558 gtk_combo_box_add (GtkContainer *container,
561 GtkComboBox *combo_box = GTK_COMBO_BOX (container);
563 if (combo_box->priv->cell_view && combo_box->priv->cell_view->parent)
565 gtk_container_remove (container, combo_box->priv->cell_view);
566 (* GTK_CONTAINER_CLASS (parent_class)->add) (container, widget);
570 (* GTK_CONTAINER_CLASS (parent_class)->add) (container, widget);
573 if (combo_box->priv->cell_view &&
574 widget != combo_box->priv->cell_view)
576 /* since the cell_view was unparented, it's gone now */
577 combo_box->priv->cell_view = NULL;
579 if (!combo_box->priv->tree_view && combo_box->priv->separator)
581 gtk_widget_unparent (combo_box->priv->separator);
583 g_object_ref (G_OBJECT (combo_box->priv->arrow));
584 gtk_widget_unparent (combo_box->priv->arrow);
585 gtk_container_add (GTK_CONTAINER (combo_box->priv->button),
586 combo_box->priv->arrow);
587 g_object_unref (G_OBJECT (combo_box->priv->arrow));
589 gtk_widget_queue_resize (GTK_WIDGET (container));
591 else if (combo_box->priv->cell_view_frame)
593 gtk_widget_unparent (combo_box->priv->cell_view_frame);
594 combo_box->priv->cell_view_frame = NULL;
599 static ComboCellInfo *
600 gtk_combo_box_get_cell_info (GtkComboBox *combo_box,
601 GtkCellRenderer *cell)
605 for (i = combo_box->priv->cells; i; i = i->next)
607 ComboCellInfo *info = (ComboCellInfo *)i->data;
609 if (info->cell == cell)
617 gtk_combo_box_menu_show (GtkWidget *menu,
620 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
622 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
624 combo_box->priv->popup_in_progress = FALSE;
628 gtk_combo_box_menu_hide (GtkWidget *menu,
631 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
633 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
638 gtk_combo_box_set_popup_widget (GtkComboBox *combo_box,
641 if (GTK_IS_MENU (combo_box->priv->popup_widget))
642 combo_box->priv->popup_widget = NULL;
643 else if (combo_box->priv->popup_widget)
645 gtk_container_remove (GTK_CONTAINER (combo_box->priv->popup_frame),
646 combo_box->priv->popup_widget);
647 g_object_unref (G_OBJECT (combo_box->priv->popup_widget));
648 combo_box->priv->popup_widget = NULL;
651 if (GTK_IS_MENU (popup))
653 if (combo_box->priv->popup_window)
655 gtk_widget_destroy (combo_box->priv->popup_window);
656 combo_box->priv->popup_window = combo_box->priv->popup_frame = NULL;
659 combo_box->priv->popup_widget = popup;
661 g_signal_connect (popup, "show",
662 G_CALLBACK (gtk_combo_box_menu_show), combo_box);
663 g_signal_connect (popup, "hide",
664 G_CALLBACK (gtk_combo_box_menu_hide), combo_box);
666 /* FIXME: need to attach to widget? */
670 if (!combo_box->priv->popup_window)
672 combo_box->priv->popup_window = gtk_window_new (GTK_WINDOW_POPUP);
674 combo_box->priv->popup_frame = gtk_frame_new (NULL);
675 gtk_frame_set_shadow_type (GTK_FRAME (combo_box->priv->popup_frame),
677 gtk_container_add (GTK_CONTAINER (combo_box->priv->popup_window),
678 combo_box->priv->popup_frame);
679 gtk_widget_show (combo_box->priv->popup_frame);
682 gtk_container_add (GTK_CONTAINER (combo_box->priv->popup_frame),
684 gtk_widget_show (popup);
685 g_object_ref (G_OBJECT (popup));
686 combo_box->priv->popup_widget = popup;
691 gtk_combo_box_menu_position (GtkMenu *menu,
700 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
702 /* FIXME: is using the size request here broken? */
703 child = GTK_BIN (combo_box)->child;
705 gdk_window_get_origin (child->window, &sx, &sy);
707 gtk_widget_size_request (GTK_WIDGET (menu), &req);
709 *x = sx + child->allocation.width - req.width;
710 *y = sy + child->allocation.height;
712 if (GTK_WIDGET_NO_WINDOW (child))
714 *x += child->allocation.x;
715 *y += child->allocation.y;
722 gtk_combo_box_popup (GtkComboBox *combo_box)
724 gint x, y, width, height;
727 if (GTK_WIDGET_MAPPED (combo_box->priv->popup_widget))
730 if (GTK_IS_MENU (combo_box->priv->popup_widget))
732 if (combo_box->priv->active_item != -1)
736 childs = gtk_container_get_children (GTK_CONTAINER (combo_box->priv->popup_widget));
737 gtk_menu_shell_select_item (GTK_MENU_SHELL (combo_box->priv->popup_widget),
738 g_list_nth_data (childs, combo_box->priv->active_item));
739 g_list_free (childs);
742 gtk_menu_popup (GTK_MENU (combo_box->priv->popup_widget),
744 gtk_combo_box_menu_position, combo_box,
750 sample = GTK_BIN (combo_box)->child;
752 width = sample->allocation.width;
753 height = sample->allocation.height;
755 gdk_window_get_origin (sample->window,
757 gtk_widget_set_size_request (combo_box->priv->popup_window,
760 if (GTK_WIDGET_NO_WINDOW (sample))
762 x += sample->allocation.x;
763 y += sample->allocation.y;
766 gtk_window_move (GTK_WINDOW (combo_box->priv->popup_window),
770 gtk_widget_show_all (combo_box->priv->popup_window);
772 gtk_widget_grab_focus (combo_box->priv->popup_window);
773 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
776 if (!GTK_WIDGET_HAS_FOCUS (combo_box->priv->tree_view))
778 gdk_keyboard_grab (combo_box->priv->popup_window->window,
779 FALSE, GDK_CURRENT_TIME);
780 gtk_widget_grab_focus (combo_box->priv->tree_view);
785 gtk_combo_box_popdown (GtkComboBox *combo_box)
787 if (GTK_IS_MENU (combo_box->priv->popup_widget))
789 gtk_menu_popdown (GTK_MENU (combo_box->priv->popup_widget));
793 gtk_widget_hide_all (combo_box->priv->popup_window);
794 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
799 gtk_combo_box_calc_requested_width (GtkComboBox *combo_box,
805 gtk_widget_style_get (combo_box->priv->measurer,
806 "focus-line-width", &padding,
809 /* add some pixels for good measure */
810 padding += BONUS_PADDING;
812 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (combo_box->priv->measurer),
815 /* nasty trick to get around the sizegroup's size request caching */
816 (* GTK_WIDGET_GET_CLASS (combo_box->priv->measurer)->size_request) (combo_box->priv->measurer, &req);
818 return req.width + padding;
822 gtk_combo_box_remeasure (GtkComboBox *combo_box)
828 if (!gtk_tree_model_get_iter_first (combo_box->priv->model, &iter))
831 path = gtk_tree_path_new_from_indices (0, -1);
833 gtk_widget_style_get (combo_box->priv->measurer,
834 "focus-line-width", &padding,
837 /* add some pixels for good measure */
838 padding += BONUS_PADDING;
844 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (combo_box->priv->measurer),
847 /* nasty trick to get around the sizegroup's size request caching */
848 (* GTK_WIDGET_GET_CLASS (combo_box->priv->measurer)->size_request) (combo_box->priv->measurer, &req);
850 combo_box->priv->width = MAX (combo_box->priv->width,
851 req.width + padding);
853 gtk_tree_path_next (path);
855 while (gtk_tree_model_iter_next (combo_box->priv->model, &iter));
857 gtk_tree_path_free (path);
859 if (combo_box->priv->cell_view)
861 gtk_widget_set_size_request (combo_box->priv->cell_view,
862 combo_box->priv->width, -1);
863 gtk_widget_queue_resize (combo_box->priv->cell_view);
868 gtk_combo_box_size_request (GtkWidget *widget,
869 GtkRequisition *requisition)
872 GtkRequisition bin_req;
874 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
877 gtk_widget_size_request (GTK_BIN (widget)->child, &bin_req);
879 if (!combo_box->priv->tree_view)
883 if (combo_box->priv->cell_view)
885 GtkRequisition sep_req, arrow_req;
886 gint border_width, xthickness, ythickness;
888 border_width = GTK_CONTAINER (combo_box->priv->button)->border_width;
889 xthickness = combo_box->priv->button->style->xthickness;
890 ythickness = combo_box->priv->button->style->ythickness;
892 bin_req.width = MAX (bin_req.width, combo_box->priv->width);
895 gtk_widget_set_size_request (combo_box->priv->separator,
897 gtk_widget_size_request (combo_box->priv->separator, &sep_req);
900 gtk_widget_set_size_request (combo_box->priv->arrow,
902 gtk_widget_size_request (combo_box->priv->arrow, &arrow_req);
904 height = MAX (sep_req.height, arrow_req.height);
905 height = MAX (height, bin_req.height);
907 width = bin_req.width + sep_req.width + arrow_req.width;
909 height += border_width + 1 + xthickness * 2 + 4;
910 width += border_width + 1 + ythickness * 2 + 4;
912 gtk_widget_set_size_request (combo_box->priv->button, width, height);
913 gtk_widget_size_request (combo_box->priv->button, requisition);
917 GtkRequisition but_req;
919 gtk_widget_set_size_request (combo_box->priv->button,
921 gtk_widget_size_request (combo_box->priv->button, &but_req);
923 requisition->width = bin_req.width + but_req.width;
924 requisition->height = MAX (bin_req.height, but_req.height);
930 GtkRequisition button_req;
933 *requisition = bin_req;
935 if (combo_box->priv->cell_view_frame)
937 requisition->width +=
938 (GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
939 GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness) * 2;
940 requisition->height +=
941 (GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
942 GTK_WIDGET (combo_box->priv->cell_view_frame)->style->ythickness) * 2;
946 gtk_widget_set_size_request (combo_box->priv->button,
947 -1, requisition->height);
948 gtk_widget_size_request (combo_box->priv->button, &button_req);
950 requisition->height = MAX (requisition->height, button_req.height);
951 requisition->width += button_req.width;
956 gtk_combo_box_size_allocate (GtkWidget *widget,
957 GtkAllocation *allocation)
959 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
963 widget->allocation = *allocation;
965 if (!combo_box->priv->tree_view)
967 if (combo_box->priv->cell_view)
969 gint border_width, xthickness, ythickness;
973 gtk_widget_size_allocate (combo_box->priv->button, allocation);
975 /* set some things ready */
976 border_width = GTK_CONTAINER (combo_box->priv->button)->border_width;
977 xthickness = combo_box->priv->button->style->xthickness;
978 ythickness = combo_box->priv->button->style->ythickness;
980 child.x = allocation->x + border_width + 1 + xthickness + 2;
981 child.y = allocation->y + border_width + 1 + ythickness + 2;
983 width = allocation->width - (border_width + 1 + ythickness * 2 + 4);
985 /* handle the childs */
986 gtk_widget_size_request (combo_box->priv->arrow, &req);
987 child.width = req.width;
988 child.height = req.height;
989 child.x += width - req.width;
990 gtk_widget_size_allocate (combo_box->priv->arrow, &child);
992 gtk_widget_size_request (combo_box->priv->separator, &req);
993 child.width = req.width;
994 child.height = req.height;
995 child.x -= req.width;
996 gtk_widget_size_allocate (combo_box->priv->separator, &child);
998 child.width = child.x;
999 child.x = allocation->x + border_width + 1 + xthickness + 2;
1000 child.width -= child.x;
1002 gtk_widget_size_request (GTK_BIN (widget)->child, &req);
1003 child.height = req.height;
1004 gtk_widget_size_allocate (GTK_BIN (widget)->child, &child);
1008 gtk_widget_size_request (combo_box->priv->button, &req);
1009 child.x = allocation->x + allocation->width - req.width;
1010 child.y = allocation->y;
1011 child.width = req.width;
1012 child.height = req.height;
1013 gtk_widget_size_allocate (combo_box->priv->button, &child);
1015 child.x = allocation->x;
1016 child.y = allocation->y;
1017 child.width = allocation->width - req.width;
1018 gtk_widget_size_allocate (GTK_BIN (widget)->child, &child);
1026 gtk_widget_size_request (combo_box->priv->button, &req);
1027 child.x = allocation->x + allocation->width - req.width;
1028 child.y = allocation->y;
1029 child.width = req.width;
1030 child.height = req.height;
1031 gtk_widget_size_allocate (combo_box->priv->button, &child);
1034 child.x = allocation->x;
1035 child.y = allocation->y;
1036 child.width = allocation->width - req.width;
1038 if (combo_box->priv->cell_view_frame)
1040 gtk_widget_size_allocate (combo_box->priv->cell_view_frame, &child);
1044 GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
1045 GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness;
1047 GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
1048 GTK_WIDGET (combo_box->priv->cell_view_frame)->style->ythickness;
1050 GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
1051 GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness;
1053 GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
1054 GTK_WIDGET (combo_box->priv->cell_view_frame)->style->ythickness;
1057 gtk_widget_size_allocate (GTK_BIN (combo_box)->child, &child);
1062 gtk_combo_box_forall (GtkContainer *container,
1063 gboolean include_internals,
1064 GtkCallback callback,
1065 gpointer callback_data)
1067 GtkComboBox *combo_box = GTK_COMBO_BOX (container);
1069 if (include_internals)
1071 if (!combo_box->priv->tree_view)
1073 if (combo_box->priv->cell_view && combo_box->priv->button)
1075 (* callback) (combo_box->priv->button, callback_data);
1076 (* callback) (combo_box->priv->separator, callback_data);
1077 (* callback) (combo_box->priv->arrow, callback_data);
1079 else if (combo_box->priv->arrow)
1081 (* callback) (combo_box->priv->button, callback_data);
1082 (* callback) (combo_box->priv->arrow, callback_data);
1087 (* callback) (combo_box->priv->button, callback_data);
1088 if (combo_box->priv->cell_view_frame)
1089 (* callback) (combo_box->priv->cell_view_frame, callback_data);
1093 if (GTK_BIN (container)->child)
1094 (* callback) (GTK_BIN (container)->child, callback_data);
1098 gtk_combo_box_expose_event (GtkWidget *widget,
1099 GdkEventExpose *event)
1101 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1103 if (!combo_box->priv->tree_view)
1105 gtk_container_propagate_expose (GTK_CONTAINER (widget),
1106 combo_box->priv->button, event);
1108 if (combo_box->priv->separator)
1110 gtk_container_propagate_expose (GTK_CONTAINER (combo_box->priv->button),
1111 combo_box->priv->separator, event);
1113 /* if not in this case, arrow gets its expose event from button */
1114 gtk_container_propagate_expose (GTK_CONTAINER (combo_box->priv->button),
1115 combo_box->priv->arrow, event);
1120 gtk_container_propagate_expose (GTK_CONTAINER (widget),
1121 combo_box->priv->button, event);
1123 if (combo_box->priv->cell_view_frame)
1124 gtk_container_propagate_expose (GTK_CONTAINER (widget),
1125 combo_box->priv->cell_view_frame, event);
1128 gtk_container_propagate_expose (GTK_CONTAINER (widget),
1129 GTK_BIN (widget)->child, event);
1139 cell_view_sync_cells (GtkComboBox *combo_box,
1140 GtkCellView *cell_view)
1144 for (k = combo_box->priv->cells; k; k = k->next)
1147 ComboCellInfo *info = (ComboCellInfo *)k->data;
1149 if (info->pack == GTK_PACK_START)
1150 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (cell_view),
1151 info->cell, info->expand);
1152 else if (info->pack == GTK_PACK_END)
1153 gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (cell_view),
1154 info->cell, info->expand);
1156 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (cell_view),
1158 info->func, info->func_data, NULL);
1160 for (j = info->attributes; j; j = j->next->next)
1162 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (cell_view),
1165 GPOINTER_TO_INT (j->next->data));
1171 gtk_combo_box_menu_setup (GtkComboBox *combo_box,
1172 gboolean add_childs)
1178 if (combo_box->priv->cell_view)
1180 combo_box->priv->button = gtk_toggle_button_new ();
1181 g_signal_connect (combo_box->priv->button, "toggled",
1182 G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
1183 gtk_widget_set_parent (combo_box->priv->button,
1184 GTK_BIN (combo_box)->child->parent);
1186 combo_box->priv->separator = gtk_vseparator_new ();
1187 gtk_widget_set_parent (combo_box->priv->separator,
1188 combo_box->priv->button);
1189 gtk_widget_show (combo_box->priv->separator);
1191 combo_box->priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
1192 gtk_widget_set_parent (combo_box->priv->arrow, combo_box->priv->button);
1193 gtk_widget_show (combo_box->priv->arrow);
1195 gtk_widget_show_all (combo_box->priv->button);
1197 if (GTK_WIDGET_MAPPED (GTK_BIN (combo_box)->child))
1199 /* I have no clue why, but we need to manually map in this case. */
1200 gtk_widget_map (combo_box->priv->separator);
1201 gtk_widget_map (combo_box->priv->arrow);
1206 combo_box->priv->button = gtk_toggle_button_new ();
1207 g_signal_connect (combo_box->priv->button, "toggled",
1208 G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
1209 gtk_widget_set_parent (combo_box->priv->button,
1210 GTK_BIN (combo_box)->child->parent);
1212 combo_box->priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
1213 gtk_container_add (GTK_CONTAINER (combo_box->priv->button),
1214 combo_box->priv->arrow);
1215 gtk_widget_show_all (combo_box->priv->button);
1218 combo_box->priv->inserted_id =
1219 g_signal_connect (combo_box->priv->model, "row_inserted",
1220 G_CALLBACK (gtk_combo_box_menu_row_inserted),
1222 combo_box->priv->deleted_id =
1223 g_signal_connect (combo_box->priv->model, "row_deleted",
1224 G_CALLBACK (gtk_combo_box_menu_row_deleted),
1226 combo_box->priv->changed_id =
1227 g_signal_connect (combo_box->priv->model, "row_changed",
1228 G_CALLBACK (gtk_combo_box_menu_row_changed),
1231 g_signal_connect (combo_box->priv->button, "button_press_event",
1232 G_CALLBACK (gtk_combo_box_menu_button_press),
1235 /* create our funky menu */
1236 box = gtk_menu_new ();
1237 gtk_combo_box_set_popup_widget (combo_box, box);
1243 items = gtk_tree_model_iter_n_children (combo_box->priv->model, NULL);
1245 for (i = 0; i < items; i++)
1249 path = gtk_tree_path_new_from_indices (i, -1);
1250 tmp = gtk_cell_view_menu_item_new_from_model (combo_box->priv->model,
1252 g_signal_connect (tmp, "activate",
1253 G_CALLBACK (gtk_combo_box_menu_item_activate),
1256 cell_view_sync_cells (combo_box, GTK_CELL_VIEW (GTK_BIN (tmp)->child));
1258 gtk_menu_shell_append (GTK_MENU_SHELL (box), tmp);
1259 gtk_widget_show (tmp);
1261 gtk_tree_path_free (path);
1266 gtk_combo_box_menu_destroy (GtkComboBox *combo_box)
1268 /* disconnect signal handlers */
1269 g_signal_handler_disconnect (combo_box->priv->model,
1270 combo_box->priv->inserted_id);
1271 g_signal_handler_disconnect (combo_box->priv->model,
1272 combo_box->priv->deleted_id);
1273 g_signal_handler_disconnect (combo_box->priv->model,
1274 combo_box->priv->changed_id);
1276 g_signal_handlers_disconnect_matched (combo_box->priv->button,
1277 G_SIGNAL_MATCH_DATA,
1279 gtk_combo_box_menu_button_press, NULL);
1281 combo_box->priv->inserted_id =
1282 combo_box->priv->deleted_id =
1283 combo_box->priv->changed_id = -1;
1285 /* unparent will remove our latest ref */
1286 if (combo_box->priv->cell_view)
1288 gtk_widget_unparent (combo_box->priv->arrow);
1289 gtk_widget_unparent (combo_box->priv->separator);
1290 gtk_widget_unparent (combo_box->priv->button);
1294 /* will destroy the arrow too */
1295 gtk_widget_unparent (combo_box->priv->button);
1298 /* changing the popup window will unref the menu and the childs */
1306 gtk_combo_box_item_get_size (GtkComboBox *combo_box,
1313 gtk_tree_model_iter_nth_child (combo_box->priv->model, &iter, NULL, index);
1317 if (combo_box->priv->col_column == -1)
1320 gtk_tree_model_get (combo_box->priv->model, &iter,
1321 combo_box->priv->col_column, cols,
1327 if (combo_box->priv->row_column == -1)
1330 gtk_tree_model_get (combo_box->priv->model, &iter,
1331 combo_box->priv->row_column, rows,
1337 menu_occupied (GtkMenu *menu,
1341 guint bottom_attach)
1345 g_return_val_if_fail (GTK_IS_MENU (menu), TRUE);
1346 g_return_val_if_fail (left_attach < right_attach, TRUE);
1347 g_return_val_if_fail (top_attach < bottom_attach, TRUE);
1349 for (i = GTK_MENU_SHELL (menu)->children; i; i = i->next)
1352 gboolean h_intersect = FALSE;
1353 gboolean v_intersect = FALSE;
1355 gtk_container_child_get (GTK_CONTAINER (menu), i->data,
1358 "bottom_attach", &b,
1362 /* look if this item intersects with the given coordinates */
1363 h_intersect = left_attach <= l && l <= right_attach;
1364 h_intersect &= left_attach <= r && r <= right_attach;
1366 v_intersect = top_attach <= t && t <= bottom_attach;
1367 v_intersect &= top_attach <= b && b <= bottom_attach;
1369 if (h_intersect && v_intersect)
1377 gtk_combo_box_relayout_item (GtkComboBox *combo_box,
1380 gint current_col = 0, current_row = 0;
1386 menu = combo_box->priv->popup_widget;
1387 if (!GTK_IS_MENU_SHELL (menu))
1390 list = gtk_container_get_children (GTK_CONTAINER (menu));
1391 item = g_list_nth_data (list, index);
1393 gtk_combo_box_item_get_size (combo_box, index, &cols, &rows);
1395 /* look for a good spot */
1398 if (current_col + cols > combo_box->priv->wrap_width)
1404 if (!menu_occupied (GTK_MENU (menu),
1405 current_col, current_col + cols,
1406 current_row, current_row + rows))
1412 /* set attach props */
1413 gtk_menu_attach (GTK_MENU (menu), item,
1414 current_col, current_col + cols,
1415 current_row, current_row + rows);
1419 gtk_combo_box_relayout (GtkComboBox *combo_box)
1425 /* ensure we are in menu style */
1426 if (combo_box->priv->tree_view)
1427 gtk_combo_box_list_destroy (combo_box);
1429 menu = combo_box->priv->popup_widget;
1431 if (!GTK_IS_MENU_SHELL (menu))
1433 gtk_combo_box_menu_setup (combo_box, FALSE);
1434 menu = combo_box->priv->popup_widget;
1437 /* get rid of all children */
1438 g_return_if_fail (GTK_IS_MENU_SHELL (menu));
1440 list = gtk_container_get_children (GTK_CONTAINER (menu));
1442 for (j = g_list_last (list); j; j = j->prev)
1443 gtk_container_remove (GTK_CONTAINER (menu), j->data);
1448 items = gtk_tree_model_iter_n_children (combo_box->priv->model, NULL);
1450 for (i = 0; i < items; i++)
1455 path = gtk_tree_path_new_from_indices (i, -1);
1456 tmp = gtk_cell_view_menu_item_new_from_model (combo_box->priv->model,
1459 g_signal_connect (tmp, "activate",
1460 G_CALLBACK (gtk_combo_box_menu_item_activate),
1463 cell_view_sync_cells (combo_box, GTK_CELL_VIEW (GTK_BIN (tmp)->child));
1465 gtk_menu_shell_insert (GTK_MENU_SHELL (menu), tmp, i);
1467 if (combo_box->priv->wrap_width)
1468 gtk_combo_box_relayout_item (combo_box, i);
1470 gtk_widget_show (tmp);
1472 gtk_tree_path_free (path);
1478 gtk_combo_box_menu_button_press (GtkWidget *widget,
1479 GdkEventButton *event,
1482 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1484 if (! GTK_IS_MENU (combo_box->priv->popup_widget))
1487 if (event->type == GDK_BUTTON_PRESS && event->button == 1)
1489 combo_box->priv->popup_in_progress = TRUE;
1490 gtk_menu_popup (GTK_MENU (combo_box->priv->popup_widget),
1492 gtk_combo_box_menu_position, combo_box,
1493 event->button, event->time);
1502 gtk_combo_box_menu_item_activate (GtkWidget *item,
1507 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1509 menu = combo_box->priv->popup_widget;
1510 g_return_if_fail (GTK_IS_MENU (menu));
1512 index = g_list_index (GTK_MENU_SHELL (menu)->children, item);
1514 gtk_combo_box_set_active (combo_box, index);
1518 gtk_combo_box_menu_row_inserted (GtkTreeModel *model,
1525 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1527 menu = combo_box->priv->popup_widget;
1528 g_return_if_fail (GTK_IS_MENU (menu));
1530 item = gtk_cell_view_menu_item_new_from_model (model, path);
1531 g_signal_connect (item, "activate",
1532 G_CALLBACK (gtk_combo_box_menu_item_activate),
1535 cell_view_sync_cells (combo_box, GTK_CELL_VIEW (GTK_BIN (item)->child));
1537 gtk_menu_shell_insert (GTK_MENU_SHELL (menu), item,
1538 gtk_tree_path_get_indices (path)[0]);
1539 gtk_widget_show (item);
1543 gtk_combo_box_menu_row_deleted (GtkTreeModel *model,
1550 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1552 index = gtk_tree_path_get_indices (path)[0];
1553 items = gtk_tree_model_iter_n_children (model, NULL);
1555 if (gtk_combo_box_get_active (combo_box) == index)
1556 gtk_combo_box_set_active (combo_box, index + 1 % items);
1558 menu = combo_box->priv->popup_widget;
1559 g_return_if_fail (GTK_IS_MENU (menu));
1561 item = g_list_nth_data (GTK_MENU_SHELL (menu)->children, index);
1562 g_return_if_fail (GTK_IS_MENU_ITEM (item));
1564 gtk_container_remove (GTK_CONTAINER (menu), item);
1568 gtk_combo_box_menu_row_changed (GtkTreeModel *model,
1573 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1576 if (combo_box->priv->wrap_width)
1577 gtk_combo_box_relayout_item (combo_box,
1578 gtk_tree_path_get_indices (path)[0]);
1580 width = gtk_combo_box_calc_requested_width (combo_box, path);
1582 if (width > combo_box->priv->width)
1584 gtk_widget_set_size_request (combo_box->priv->cell_view, width, -1);
1585 gtk_widget_queue_resize (combo_box->priv->cell_view);
1586 combo_box->priv->width = width;
1595 gtk_combo_box_list_setup (GtkComboBox *combo_box)
1598 GtkTreeSelection *sel;
1600 combo_box->priv->button = gtk_toggle_button_new ();
1601 gtk_widget_set_parent (combo_box->priv->button,
1602 GTK_BIN (combo_box)->child->parent);
1603 g_signal_connect (combo_box->priv->button, "button_press_event",
1604 G_CALLBACK (gtk_combo_box_list_button_pressed), combo_box);
1605 g_signal_connect (combo_box->priv->button, "toggled",
1606 G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
1608 combo_box->priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
1609 gtk_container_add (GTK_CONTAINER (combo_box->priv->button),
1610 combo_box->priv->arrow);
1611 gtk_widget_show_all (combo_box->priv->button);
1613 if (combo_box->priv->cell_view)
1615 combo_box->priv->cell_view_frame = gtk_frame_new (NULL);
1616 gtk_widget_set_parent (combo_box->priv->cell_view_frame,
1617 GTK_BIN (combo_box)->child->parent);
1618 gtk_frame_set_shadow_type (GTK_FRAME (combo_box->priv->cell_view_frame),
1621 g_object_set (G_OBJECT (combo_box->priv->cell_view),
1622 "background", "white",
1623 "background_set", TRUE,
1626 gtk_widget_show (combo_box->priv->cell_view_frame);
1629 combo_box->priv->tree_view = gtk_tree_view_new ();
1630 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (combo_box->priv->tree_view));
1631 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1632 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (combo_box->priv->tree_view),
1635 g_signal_connect (combo_box->priv->tree_view, "button_press_event",
1636 G_CALLBACK (gtk_combo_box_list_button_pressed),
1638 g_signal_connect (combo_box->priv->tree_view, "button_release_event",
1639 G_CALLBACK (gtk_combo_box_list_button_released),
1641 g_signal_connect (combo_box->priv->tree_view, "key_press_event",
1642 G_CALLBACK (gtk_combo_box_list_key_press),
1645 combo_box->priv->column = gtk_tree_view_column_new ();
1646 gtk_tree_view_append_column (GTK_TREE_VIEW (combo_box->priv->tree_view),
1647 combo_box->priv->column);
1649 /* set the models */
1650 gtk_tree_view_set_model (GTK_TREE_VIEW (combo_box->priv->tree_view),
1651 combo_box->priv->model);
1653 combo_box->priv->changed_id =
1654 g_signal_connect (combo_box->priv->model, "row_changed",
1655 G_CALLBACK (gtk_combo_box_list_row_changed),
1659 for (i = combo_box->priv->cells; i; i = i->next)
1662 ComboCellInfo *info = (ComboCellInfo *)i->data;
1664 if (info->pack == GTK_PACK_START)
1665 gtk_tree_view_column_pack_start (combo_box->priv->column,
1666 info->cell, info->expand);
1667 else if (info->pack == GTK_PACK_END)
1668 gtk_tree_view_column_pack_end (combo_box->priv->column,
1669 info->cell, info->expand);
1671 for (j = info->attributes; j; j = j->next->next)
1673 gtk_tree_view_column_add_attribute (combo_box->priv->column,
1676 GPOINTER_TO_INT (j->next->data));
1680 if (combo_box->priv->active_item != -1)
1684 path = gtk_tree_path_new_from_indices (combo_box->priv->active_item, -1);
1687 gtk_tree_view_set_cursor (GTK_TREE_VIEW (combo_box->priv->tree_view),
1689 gtk_tree_path_free (path);
1693 /* set sample/popup widgets */
1694 gtk_combo_box_set_popup_widget (GTK_COMBO_BOX (combo_box),
1695 combo_box->priv->tree_view);
1697 gtk_widget_show (combo_box->priv->tree_view);
1701 gtk_combo_box_list_destroy (GtkComboBox *combo_box)
1703 /* disconnect signals */
1704 g_signal_handler_disconnect (combo_box->priv->model,
1705 combo_box->priv->changed_id);
1706 combo_box->priv->changed_id = -1;
1708 g_signal_handlers_disconnect_matched (combo_box->priv->tree_view,
1709 G_SIGNAL_MATCH_DATA,
1710 0, 0, NULL, NULL, combo_box);
1711 g_signal_handlers_disconnect_matched (combo_box->priv->button,
1712 G_SIGNAL_MATCH_DATA,
1714 gtk_combo_box_list_button_pressed,
1717 /* destroy things (unparent will kill the latest ref from us)
1718 * last unref on button will destroy the arrow
1720 gtk_widget_unparent (combo_box->priv->button);
1722 if (combo_box->priv->cell_view)
1724 g_object_set (G_OBJECT (combo_box->priv->cell_view),
1725 "background_set", FALSE,
1728 gtk_widget_unparent (combo_box->priv->cell_view_frame);
1731 gtk_widget_destroy (combo_box->priv->tree_view);
1732 combo_box->priv->tree_view = NULL;
1733 combo_box->priv->popup_widget = NULL;
1738 gtk_combo_box_list_remove_grabs (GtkComboBox *combo_box)
1740 if (GTK_WIDGET_HAS_GRAB (combo_box->priv->tree_view))
1741 gtk_grab_remove (combo_box->priv->tree_view);
1743 if (GTK_WIDGET_HAS_GRAB (combo_box->priv->popup_window))
1745 gtk_grab_remove (combo_box->priv->popup_window);
1746 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1751 gtk_combo_box_list_button_pressed (GtkWidget *widget,
1752 GdkEventButton *event,
1755 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
1757 GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *)event);
1759 if (ewidget == combo_box->priv->tree_view)
1762 if ((ewidget != combo_box->priv->button) ||
1763 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (combo_box->priv->button)))
1766 gtk_combo_box_popup (combo_box);
1768 gtk_grab_add (combo_box->priv->popup_window);
1769 gdk_pointer_grab (combo_box->priv->popup_window->window, TRUE,
1770 GDK_BUTTON_PRESS_MASK |
1771 GDK_BUTTON_RELEASE_MASK |
1772 GDK_POINTER_MOTION_MASK,
1773 NULL, NULL, GDK_CURRENT_TIME);
1775 gtk_grab_add (combo_box->priv->tree_view);
1777 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
1780 combo_box->priv->popup_in_progress = TRUE;
1786 gtk_combo_box_list_button_released (GtkWidget *widget,
1787 GdkEventButton *event,
1791 GtkTreePath *path = NULL;
1793 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
1795 gboolean popup_in_progress = FALSE;
1797 GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *)event);
1799 if (combo_box->priv->popup_in_progress)
1801 popup_in_progress = TRUE;
1802 combo_box->priv->popup_in_progress = FALSE;
1805 if (ewidget != combo_box->priv->tree_view)
1807 if (ewidget == combo_box->priv->button &&
1808 !popup_in_progress &&
1809 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (combo_box->priv->button)))
1811 gtk_combo_box_list_remove_grabs (combo_box);
1812 gtk_combo_box_popdown (combo_box);
1816 /* released outside treeview */
1817 if (ewidget != combo_box->priv->button)
1819 gtk_combo_box_list_remove_grabs (combo_box);
1820 gtk_combo_box_popdown (combo_box);
1829 gtk_combo_box_list_remove_grabs (combo_box);
1831 /* select something cool */
1832 ret = gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
1838 return TRUE; /* clicked outside window? */
1840 gtk_combo_box_set_active (combo_box, gtk_tree_path_get_indices (path)[0]);
1841 gtk_combo_box_popdown (combo_box);
1843 gtk_tree_path_free (path);
1849 gtk_combo_box_list_key_press (GtkWidget *widget,
1853 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
1855 if ((event->keyval == GDK_Return || event->keyval == GDK_KP_Enter ||
1856 event->keyval == GDK_space || event->keyval == GDK_KP_Space) ||
1857 event->keyval == GDK_Escape)
1859 if (event->keyval != GDK_Escape)
1863 GtkTreeModel *model = NULL;
1864 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (combo_box->priv->tree_view));
1866 ret = gtk_tree_selection_get_selected (sel, &model, &iter);
1871 path = gtk_tree_model_get_path (model, &iter);
1874 gtk_combo_box_set_active (combo_box, gtk_tree_path_get_indices (path)[0]);
1875 gtk_tree_path_free (path);
1880 /* reset active item -- this is incredibly lame and ugly */
1881 gtk_combo_box_set_active (combo_box,
1882 gtk_combo_box_get_active (combo_box));
1884 gtk_combo_box_list_remove_grabs (combo_box);
1885 gtk_combo_box_popdown (combo_box);
1894 gtk_combo_box_list_row_changed (GtkTreeModel *model,
1899 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
1902 width = gtk_combo_box_calc_requested_width (combo_box, path);
1904 if (width > combo_box->priv->width)
1906 gtk_widget_set_size_request (combo_box->priv->cell_view, width, -1);
1907 gtk_widget_queue_resize (combo_box->priv->cell_view);
1908 combo_box->priv->width = width;
1913 * GtkCellLayout implementation
1916 gtk_combo_box_cell_layout_pack_start (GtkCellLayout *layout,
1917 GtkCellRenderer *cell,
1920 ComboCellInfo *info;
1921 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
1924 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
1925 g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
1927 info = g_new0 (ComboCellInfo, 1);
1929 info->expand = expand;
1930 info->pack = GTK_PACK_START;
1932 combo_box->priv->cells = g_slist_append (combo_box->priv->cells, info);
1934 if (combo_box->priv->cell_view)
1935 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box->priv->cell_view),
1938 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box->priv->measurer),
1941 if (combo_box->priv->column)
1942 gtk_tree_view_column_pack_start (combo_box->priv->column, cell, expand);
1944 menu = combo_box->priv->popup_widget;
1945 if (GTK_IS_MENU (menu))
1949 list = gtk_container_get_children (GTK_CONTAINER (menu));
1950 for (i = list; i; i = i->next)
1954 if (GTK_IS_CELL_VIEW_MENU_ITEM (i->data))
1955 view = GTK_CELL_VIEW (GTK_BIN (i->data)->child);
1957 view = GTK_CELL_VIEW (i->data);
1959 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (view), cell, expand);
1966 gtk_combo_box_cell_layout_pack_end (GtkCellLayout *layout,
1967 GtkCellRenderer *cell,
1970 ComboCellInfo *info;
1971 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
1974 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
1975 g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
1977 info = g_new0 (ComboCellInfo, 1);
1979 info->expand = expand;
1980 info->pack = GTK_PACK_END;
1982 if (combo_box->priv->cell_view)
1983 gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (combo_box->priv->cell_view),
1986 gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (combo_box->priv->measurer),
1989 if (combo_box->priv->column)
1990 gtk_tree_view_column_pack_end (combo_box->priv->column, cell, expand);
1992 menu = combo_box->priv->popup_widget;
1993 if (GTK_IS_MENU (menu))
1997 list = gtk_container_get_children (GTK_CONTAINER (menu));
1998 for (i = list; i; i = i->next)
2002 if (GTK_IS_CELL_VIEW_MENU_ITEM (i->data))
2003 view = GTK_CELL_VIEW (GTK_BIN (i->data)->child);
2005 view = GTK_CELL_VIEW (i->data);
2007 gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (view), cell, expand);
2014 gtk_combo_box_cell_layout_clear (GtkCellLayout *layout)
2017 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
2019 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2021 if (combo_box->priv->cell_view)
2022 gtk_cell_layout_clear (GTK_CELL_LAYOUT (combo_box->priv->cell_view));
2024 if (combo_box->priv->measurer)
2025 gtk_cell_layout_clear (GTK_CELL_LAYOUT (combo_box->priv->measurer));
2027 if (combo_box->priv->column)
2028 gtk_tree_view_column_clear (combo_box->priv->column);
2030 menu = combo_box->priv->popup_widget;
2031 if (GTK_IS_MENU (menu))
2035 list = gtk_container_get_children (GTK_CONTAINER (menu));
2036 for (i = list; i; i = i->next)
2040 if (GTK_IS_CELL_VIEW_MENU_ITEM (i->data))
2041 view = GTK_CELL_VIEW (GTK_BIN (i->data)->child);
2043 view = GTK_CELL_VIEW (i->data);
2045 gtk_cell_layout_clear (GTK_CELL_LAYOUT (view));
2051 gtk_combo_box_cell_layout_add_attribute (GtkCellLayout *layout,
2052 GtkCellRenderer *cell,
2053 const gchar *attribute,
2056 ComboCellInfo *info;
2057 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
2060 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2061 g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
2063 info = gtk_combo_box_get_cell_info (combo_box, cell);
2065 info->attributes = g_slist_prepend (info->attributes,
2066 GINT_TO_POINTER (column));
2067 info->attributes = g_slist_prepend (info->attributes,
2068 g_strdup (attribute));
2070 if (combo_box->priv->cell_view)
2071 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box->priv->cell_view),
2072 cell, attribute, column);
2074 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box->priv->measurer),
2075 cell, attribute, column);
2077 if (combo_box->priv->column)
2078 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box->priv->column),
2079 cell, attribute, column);
2081 menu = combo_box->priv->popup_widget;
2082 if (GTK_IS_MENU (menu))
2086 list = gtk_container_get_children (GTK_CONTAINER (menu));
2087 for (i = list; i; i = i->next)
2091 if (GTK_IS_CELL_VIEW_MENU_ITEM (i->data))
2092 view = GTK_CELL_VIEW (GTK_BIN (i->data)->child);
2094 view = GTK_CELL_VIEW (i->data);
2096 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (view), cell,
2102 gtk_combo_box_remeasure (combo_box);
2106 gtk_combo_box_cell_layout_set_cell_data_func (GtkCellLayout *layout,
2107 GtkCellRenderer *cell,
2108 GtkCellLayoutDataFunc func,
2110 GDestroyNotify destroy)
2112 ComboCellInfo *info;
2113 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
2116 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2118 info = gtk_combo_box_get_cell_info (combo_box, cell);
2119 g_return_if_fail (info != NULL);
2123 GDestroyNotify d = info->destroy;
2125 info->destroy = NULL;
2126 d (info->func_data);
2130 info->func_data = func_data;
2131 info->destroy = destroy;
2133 if (combo_box->priv->cell_view)
2134 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo_box->priv->cell_view), cell, func, func_data, NULL);
2136 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo_box->priv->measurer), cell, func, func_data, NULL);
2138 if (combo_box->priv->column)
2139 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo_box->priv->column), cell, func, func_data, NULL);
2141 menu = combo_box->priv->popup_widget;
2142 if (GTK_IS_MENU (menu))
2146 list = gtk_container_get_children (GTK_CONTAINER (menu));
2147 for (i = list; i; i = i->next)
2151 if (GTK_IS_CELL_VIEW_MENU_ITEM (i->data))
2152 view = GTK_CELL_VIEW (GTK_BIN (i->data)->child);
2154 view = GTK_CELL_VIEW (i->data);
2156 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (view), cell,
2157 func, func_data, NULL);
2162 gtk_combo_box_remeasure (combo_box);
2166 gtk_combo_box_cell_layout_clear_attributes (GtkCellLayout *layout,
2167 GtkCellRenderer *cell)
2169 ComboCellInfo *info;
2170 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
2174 g_return_if_fail (GTK_IS_COMBO_BOX (layout));
2175 g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
2177 info = gtk_combo_box_get_cell_info (combo_box, cell);
2178 g_return_if_fail (info != NULL);
2180 list = info->attributes;
2181 while (list && list->next)
2183 g_free (list->data);
2184 list = list->next->next;
2186 g_slist_free (list);
2188 info->attributes = NULL;
2190 if (combo_box->priv->cell_view)
2191 gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (combo_box->priv->cell_view), cell);
2193 gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (combo_box->priv->measurer), cell);
2195 if (combo_box->priv->column)
2196 gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (combo_box->priv->column), cell);
2198 menu = combo_box->priv->popup_widget;
2199 if (GTK_IS_MENU (menu))
2203 list = gtk_container_get_children (GTK_CONTAINER (menu));
2204 for (i = list; i; i = i->next)
2208 if (GTK_IS_CELL_VIEW_MENU_ITEM (i->data))
2209 view = GTK_CELL_VIEW (GTK_BIN (i->data)->child);
2211 view = GTK_CELL_VIEW (i->data);
2213 gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (view), cell);
2218 gtk_combo_box_remeasure (combo_box);
2226 * gtk_combo_box_new:
2227 * @model: A #GtkTreeModel.
2229 * Creates a new #GtkComboBox with the model initialized to @model.
2231 * Return value: A new #GtkComboBox.
2236 gtk_combo_box_new (GtkTreeModel *model)
2238 GtkComboBox *combo_box;
2240 g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL);
2242 combo_box = GTK_COMBO_BOX (g_object_new (gtk_combo_box_get_type (),
2246 return GTK_WIDGET (combo_box);
2250 * gtk_combo_box_set_wrap_width:
2251 * @combo_box: A #GtkComboBox.
2252 * @width: Preferred number of columns.
2254 * Sets the wrap width of @combo_box to be @width. The wrap width is basically
2255 * the preferred number of columns when you want to the popup to be layed out
2261 gtk_combo_box_set_wrap_width (GtkComboBox *combo_box,
2264 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2265 g_return_if_fail (width > 0);
2267 combo_box->priv->wrap_width = width;
2269 gtk_combo_box_relayout (combo_box);
2273 * gtk_combo_box_set_row_span_column:
2274 * @combo_box: A #GtkComboBox.
2275 * @row_span: A column in the model passed during construction.
2277 * Sets the column with row span information for @combo_box to be @row_span.
2278 * The row span column contains integers which indicate how many rows
2279 * an item should span.
2284 gtk_combo_box_set_row_span_column (GtkComboBox *combo_box,
2289 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2291 col = gtk_tree_model_get_n_columns (combo_box->priv->model);
2292 g_return_if_fail (row_span >= 0 && row_span < col);
2294 combo_box->priv->row_column = row_span;
2296 gtk_combo_box_relayout (combo_box);
2300 * gtk_combo_box_set_column_span_column:
2301 * @combo_box: A #GtkComboBox.
2302 * @column_span: A column in the model passed during construction.
2304 * Sets the column with column span information for @combo_box to be
2305 * @column_span. The column span column contains integers which indicate
2306 * how many columns an item should span.
2311 gtk_combo_box_set_column_span_column (GtkComboBox *combo_box,
2316 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2318 col = gtk_tree_model_get_n_columns (combo_box->priv->model);
2319 g_return_if_fail (column_span >= 0 && column_span < col);
2321 combo_box->priv->col_column = column_span;
2323 gtk_combo_box_relayout (combo_box);
2327 * gtk_combo_box_get_active:
2328 * @combo_box: A #GtkComboBox.
2330 * Returns the index of the currently active item.
2332 * Return value: An integer which is the index of the currently active item.
2337 gtk_combo_box_get_active (GtkComboBox *combo_box)
2339 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), 0);
2341 return combo_box->priv->active_item;
2345 * gtk_combo_box_set_active:
2346 * @combo_box: A #GtkComboBox.
2347 * @index: An index in the model passed during construction.
2349 * Sets the active item of @combo_box to be the item at @index.
2354 gtk_combo_box_set_active (GtkComboBox *combo_box,
2359 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2360 /* -1 means "no item selected" */
2361 g_return_if_fail (index >= -1);
2363 if (combo_box->priv->active_item == index)
2366 combo_box->priv->active_item = index;
2370 if (combo_box->priv->tree_view)
2371 gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (combo_box->priv->tree_view)));
2374 GtkMenu *menu = GTK_MENU (combo_box->priv->popup_widget);
2376 if (GTK_IS_MENU (menu))
2377 gtk_menu_set_active (menu, -1);
2380 if (combo_box->priv->cell_view)
2381 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (combo_box->priv->cell_view), NULL);
2385 path = gtk_tree_path_new_from_indices (index, -1);
2387 if (combo_box->priv->tree_view)
2388 gtk_tree_view_set_cursor (GTK_TREE_VIEW (combo_box->priv->tree_view), path, NULL, FALSE);
2391 GtkMenu *menu = GTK_MENU (combo_box->priv->popup_widget);
2393 if (GTK_IS_MENU (menu))
2394 gtk_menu_set_active (GTK_MENU (menu), index);
2397 if (combo_box->priv->cell_view)
2398 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (combo_box->priv->cell_view), path);
2400 gtk_tree_path_free (path);
2403 g_signal_emit_by_name (combo_box, "changed", NULL, NULL);
2407 * gtk_combo_box_get_model
2408 * @combo_box: A #GtkComboBox.
2410 * Returns the #GtkTreeModel which is acting as data source for @combo_box.
2412 * Return value: A #GtkTreeModel which was passed during construction.
2417 gtk_combo_box_get_model (GtkComboBox *combo_box)
2419 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
2421 return combo_box->priv->model;
2425 /* convenience API for simple text combos */
2428 * gtk_combo_box_new_text:
2430 * Convenience function which constructs a new text combo box, which is a
2431 * #GtkComboBox just displaying strings. If you use this function to create
2432 * a text combo box, you only want to manipulate it's data source with the
2433 * following convenience functions: gtk_combo_box_append_text(),
2434 * gtk_combo_box_insert_text() and gtk_combo_box_prepend_text().
2436 * Return value: A new text combo box.
2441 gtk_combo_box_new_text (void)
2443 GtkWidget *combo_box;
2444 GtkCellRenderer *cell;
2445 GtkListStore *store;
2447 store = gtk_list_store_new (1, G_TYPE_STRING);
2449 combo_box = gtk_combo_box_new (GTK_TREE_MODEL (store));
2451 cell = gtk_cell_renderer_text_new ();
2452 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), cell, TRUE);
2453 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), cell,
2461 * gtk_combo_box_append_text:
2462 * @combo_box: A #GtkComboBox constructed using gtk_combo_box_new_text().
2465 * Appends @string to the list of strings stored in @combo_box. Note that
2466 * you can only use this function with combo boxes constructed with
2467 * gtk_combo_box_new_text().
2472 gtk_combo_box_append_text (GtkComboBox *combo_box,
2476 GtkListStore *store;
2478 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2479 g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model));
2480 g_return_if_fail (text != NULL);
2482 store = GTK_LIST_STORE (combo_box->priv->model);
2484 gtk_list_store_append (store, &iter);
2485 gtk_list_store_set (store, &iter, 0, text, -1);
2489 * gtk_combo_box_insert_text:
2490 * @combo_box: A #GtkComboBox constructed using gtk_combo_box_new_text().
2491 * @position: An index to insert @text.
2494 * Inserts @string at @position in the list of strings stored in @combo_box.
2495 * Note that you can only use this function with combo boxes constructed
2496 * with gtk_combo_box_new_text().
2501 gtk_combo_box_insert_text (GtkComboBox *combo_box,
2506 GtkListStore *store;
2508 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2509 g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model));
2510 g_return_if_fail (position >= 0);
2511 g_return_if_fail (text != NULL);
2513 store = GTK_LIST_STORE (combo_box->priv->model);
2515 gtk_list_store_insert (store, &iter, position);
2516 gtk_list_store_set (store, &iter, 0, text, -1);
2520 * gtk_combo_box_prepend_text:
2521 * @combo_box: A #GtkComboBox constructed with gtk_combo_box_new_text().
2524 * Prepends @string to the list of strings stored in @combo_box. Note that
2525 * you can only use this function with combo boxes constructed with
2526 * gtk_combo_box_new_text().
2531 gtk_combo_box_prepend_text (GtkComboBox *combo_box,
2535 GtkListStore *store;
2537 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2538 g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model));
2539 g_return_if_fail (text != NULL);
2541 store = GTK_LIST_STORE (combo_box->priv->model);
2543 gtk_list_store_prepend (store, &iter);
2544 gtk_list_store_set (store, &iter, 0, text, -1);