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));
501 gtk_cell_view_set_model (GTK_CELL_VIEW (combo_box->priv->cell_view),
502 combo_box->priv->model);
503 gtk_cell_view_set_model (GTK_CELL_VIEW (combo_box->priv->measurer),
504 combo_box->priv->model);
508 gtk_combo_box_style_set (GtkWidget *widget,
509 GtkStyle *previous_style,
513 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
515 /* if wrap_width > 0, then we are in grid-mode and forced to use
518 if (combo_box->priv->wrap_width)
521 gtk_widget_style_get (widget,
522 "appearance", &appearance,
525 /* TRUE is windows style */
528 if (GTK_IS_MENU (combo_box->priv->popup_widget))
529 gtk_combo_box_menu_destroy (combo_box);
530 gtk_combo_box_list_setup (combo_box);
534 if (GTK_IS_TREE_VIEW (combo_box->priv->tree_view))
535 gtk_combo_box_list_destroy (combo_box);
536 gtk_combo_box_menu_setup (combo_box, TRUE);
541 gtk_combo_box_button_toggled (GtkWidget *widget,
544 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
546 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
548 if (!combo_box->priv->popup_in_progress)
549 gtk_combo_box_popup (combo_box);
552 gtk_combo_box_popdown (combo_box);
556 gtk_combo_box_add (GtkContainer *container,
559 GtkComboBox *combo_box = GTK_COMBO_BOX (container);
561 if (combo_box->priv->cell_view && combo_box->priv->cell_view->parent)
563 gtk_container_remove (container, combo_box->priv->cell_view);
564 (* GTK_CONTAINER_CLASS (parent_class)->add) (container, widget);
568 (* GTK_CONTAINER_CLASS (parent_class)->add) (container, widget);
571 if (combo_box->priv->cell_view &&
572 widget != combo_box->priv->cell_view)
574 /* since the cell_view was unparented, it's gone now */
575 combo_box->priv->cell_view = NULL;
577 if (!combo_box->priv->tree_view && combo_box->priv->separator)
579 gtk_widget_unparent (combo_box->priv->separator);
581 g_object_ref (G_OBJECT (combo_box->priv->arrow));
582 gtk_widget_unparent (combo_box->priv->arrow);
583 gtk_container_add (GTK_CONTAINER (combo_box->priv->button),
584 combo_box->priv->arrow);
585 g_object_unref (G_OBJECT (combo_box->priv->arrow));
587 gtk_widget_queue_resize (GTK_WIDGET (container));
589 else if (combo_box->priv->cell_view_frame)
591 gtk_widget_unparent (combo_box->priv->cell_view_frame);
592 combo_box->priv->cell_view_frame = NULL;
597 static ComboCellInfo *
598 gtk_combo_box_get_cell_info (GtkComboBox *combo_box,
599 GtkCellRenderer *cell)
603 for (i = combo_box->priv->cells; i; i = i->next)
605 ComboCellInfo *info = (ComboCellInfo *)i->data;
607 if (info->cell == cell)
615 gtk_combo_box_menu_show (GtkWidget *menu,
618 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
620 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
622 combo_box->priv->popup_in_progress = FALSE;
626 gtk_combo_box_menu_hide (GtkWidget *menu,
629 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
631 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
636 gtk_combo_box_set_popup_widget (GtkComboBox *combo_box,
639 if (GTK_IS_MENU (combo_box->priv->popup_widget))
640 combo_box->priv->popup_widget = NULL;
641 else if (combo_box->priv->popup_widget)
643 gtk_container_remove (GTK_CONTAINER (combo_box->priv->popup_frame),
644 combo_box->priv->popup_widget);
645 g_object_unref (G_OBJECT (combo_box->priv->popup_widget));
646 combo_box->priv->popup_widget = NULL;
649 if (GTK_IS_MENU (popup))
651 if (combo_box->priv->popup_window)
653 gtk_widget_destroy (combo_box->priv->popup_window);
654 combo_box->priv->popup_window = combo_box->priv->popup_frame = NULL;
657 combo_box->priv->popup_widget = popup;
659 g_signal_connect (popup, "show",
660 G_CALLBACK (gtk_combo_box_menu_show), combo_box);
661 g_signal_connect (popup, "hide",
662 G_CALLBACK (gtk_combo_box_menu_hide), combo_box);
664 /* FIXME: need to attach to widget? */
668 if (!combo_box->priv->popup_window)
670 combo_box->priv->popup_window = gtk_window_new (GTK_WINDOW_POPUP);
672 combo_box->priv->popup_frame = gtk_frame_new (NULL);
673 gtk_frame_set_shadow_type (GTK_FRAME (combo_box->priv->popup_frame),
675 gtk_container_add (GTK_CONTAINER (combo_box->priv->popup_window),
676 combo_box->priv->popup_frame);
677 gtk_widget_show (combo_box->priv->popup_frame);
680 gtk_container_add (GTK_CONTAINER (combo_box->priv->popup_frame),
682 gtk_widget_show (popup);
683 g_object_ref (G_OBJECT (popup));
684 combo_box->priv->popup_widget = popup;
689 gtk_combo_box_menu_position (GtkMenu *menu,
698 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
700 /* FIXME: is using the size request here broken? */
701 child = GTK_BIN (combo_box)->child;
703 gdk_window_get_origin (child->window, &sx, &sy);
705 gtk_widget_size_request (GTK_WIDGET (menu), &req);
707 *x = sx + child->allocation.width - req.width;
708 *y = sy + child->allocation.height;
710 if (GTK_WIDGET_NO_WINDOW (child))
712 *x += child->allocation.x;
713 *y += child->allocation.y;
720 gtk_combo_box_popup (GtkComboBox *combo_box)
722 gint x, y, width, height;
725 if (GTK_WIDGET_MAPPED (combo_box->priv->popup_widget))
728 if (GTK_IS_MENU (combo_box->priv->popup_widget))
730 if (combo_box->priv->active_item != -1)
734 childs = gtk_container_get_children (GTK_CONTAINER (combo_box->priv->popup_widget));
735 gtk_menu_shell_select_item (GTK_MENU_SHELL (combo_box->priv->popup_widget),
736 g_list_nth_data (childs, combo_box->priv->active_item));
737 g_list_free (childs);
740 gtk_menu_popup (GTK_MENU (combo_box->priv->popup_widget),
742 gtk_combo_box_menu_position, combo_box,
748 sample = GTK_BIN (combo_box)->child;
750 width = sample->allocation.width;
751 height = sample->allocation.height;
753 gdk_window_get_origin (sample->window,
755 gtk_widget_set_size_request (combo_box->priv->popup_window,
758 if (GTK_WIDGET_NO_WINDOW (sample))
760 x += sample->allocation.x;
761 y += sample->allocation.y;
764 gtk_window_move (GTK_WINDOW (combo_box->priv->popup_window),
768 gtk_widget_show_all (combo_box->priv->popup_window);
770 gtk_widget_grab_focus (combo_box->priv->popup_window);
771 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
774 if (!GTK_WIDGET_HAS_FOCUS (combo_box->priv->tree_view))
776 gdk_keyboard_grab (combo_box->priv->popup_window->window,
777 FALSE, GDK_CURRENT_TIME);
778 gtk_widget_grab_focus (combo_box->priv->tree_view);
783 gtk_combo_box_popdown (GtkComboBox *combo_box)
785 if (GTK_IS_MENU (combo_box->priv->popup_widget))
787 gtk_menu_popdown (GTK_MENU (combo_box->priv->popup_widget));
791 gtk_widget_hide_all (combo_box->priv->popup_window);
792 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
797 gtk_combo_box_calc_requested_width (GtkComboBox *combo_box,
803 gtk_widget_style_get (combo_box->priv->measurer,
804 "focus-line-width", &padding,
807 /* add some pixels for good measure */
808 padding += BONUS_PADDING;
810 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (combo_box->priv->measurer),
813 /* nasty trick to get around the sizegroup's size request caching */
814 (* GTK_WIDGET_GET_CLASS (combo_box->priv->measurer)->size_request) (combo_box->priv->measurer, &req);
816 return req.width + padding;
820 gtk_combo_box_remeasure (GtkComboBox *combo_box)
826 if (!gtk_tree_model_get_iter_first (combo_box->priv->model, &iter))
829 path = gtk_tree_path_new_from_indices (0, -1);
831 gtk_widget_style_get (combo_box->priv->measurer,
832 "focus-line-width", &padding,
835 /* add some pixels for good measure */
836 padding += BONUS_PADDING;
842 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (combo_box->priv->measurer),
845 /* nasty trick to get around the sizegroup's size request caching */
846 (* GTK_WIDGET_GET_CLASS (combo_box->priv->measurer)->size_request) (combo_box->priv->measurer, &req);
848 combo_box->priv->width = MAX (combo_box->priv->width,
849 req.width + padding);
851 gtk_tree_path_next (path);
853 while (gtk_tree_model_iter_next (combo_box->priv->model, &iter));
855 gtk_tree_path_free (path);
857 if (combo_box->priv->cell_view)
859 gtk_widget_set_size_request (combo_box->priv->cell_view,
860 combo_box->priv->width, -1);
861 gtk_widget_queue_resize (combo_box->priv->cell_view);
866 gtk_combo_box_size_request (GtkWidget *widget,
867 GtkRequisition *requisition)
870 GtkRequisition bin_req;
872 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
875 gtk_widget_size_request (GTK_BIN (widget)->child, &bin_req);
877 if (!combo_box->priv->tree_view)
881 if (combo_box->priv->cell_view)
883 GtkRequisition sep_req, arrow_req;
884 gint border_width, xthickness, ythickness;
886 border_width = GTK_CONTAINER (combo_box->priv->button)->border_width;
887 xthickness = combo_box->priv->button->style->xthickness;
888 ythickness = combo_box->priv->button->style->ythickness;
890 bin_req.width = MAX (bin_req.width, combo_box->priv->width);
893 gtk_widget_set_size_request (combo_box->priv->separator,
895 gtk_widget_size_request (combo_box->priv->separator, &sep_req);
898 gtk_widget_set_size_request (combo_box->priv->arrow,
900 gtk_widget_size_request (combo_box->priv->arrow, &arrow_req);
902 height = MAX (sep_req.height, arrow_req.height);
903 height = MAX (height, bin_req.height);
905 width = bin_req.width + sep_req.width + arrow_req.width;
907 height += border_width + 1 + xthickness * 2 + 4;
908 width += border_width + 1 + ythickness * 2 + 4;
910 gtk_widget_set_size_request (combo_box->priv->button, width, height);
911 gtk_widget_size_request (combo_box->priv->button, requisition);
915 GtkRequisition but_req;
917 gtk_widget_set_size_request (combo_box->priv->button,
919 gtk_widget_size_request (combo_box->priv->button, &but_req);
921 requisition->width = bin_req.width + but_req.width;
922 requisition->height = MAX (bin_req.height, but_req.height);
928 GtkRequisition button_req;
931 *requisition = bin_req;
933 if (combo_box->priv->cell_view_frame)
935 requisition->width +=
936 (GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
937 GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness) * 2;
938 requisition->height +=
939 (GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
940 GTK_WIDGET (combo_box->priv->cell_view_frame)->style->ythickness) * 2;
944 gtk_widget_set_size_request (combo_box->priv->button,
945 -1, requisition->height);
946 gtk_widget_size_request (combo_box->priv->button, &button_req);
948 requisition->height = MAX (requisition->height, button_req.height);
949 requisition->width += button_req.width;
954 gtk_combo_box_size_allocate (GtkWidget *widget,
955 GtkAllocation *allocation)
957 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
961 widget->allocation = *allocation;
963 if (!combo_box->priv->tree_view)
965 if (combo_box->priv->cell_view)
967 gint border_width, xthickness, ythickness;
971 gtk_widget_size_allocate (combo_box->priv->button, allocation);
973 /* set some things ready */
974 border_width = GTK_CONTAINER (combo_box->priv->button)->border_width;
975 xthickness = combo_box->priv->button->style->xthickness;
976 ythickness = combo_box->priv->button->style->ythickness;
978 child.x = allocation->x + border_width + 1 + xthickness + 2;
979 child.y = allocation->y + border_width + 1 + ythickness + 2;
981 width = allocation->width - (border_width + 1 + ythickness * 2 + 4);
983 /* handle the childs */
984 gtk_widget_size_request (combo_box->priv->arrow, &req);
985 child.width = req.width;
986 child.height = req.height;
987 child.x += width - req.width;
988 gtk_widget_size_allocate (combo_box->priv->arrow, &child);
990 gtk_widget_size_request (combo_box->priv->separator, &req);
991 child.width = req.width;
992 child.height = req.height;
993 child.x -= req.width;
994 gtk_widget_size_allocate (combo_box->priv->separator, &child);
996 child.width = child.x;
997 child.x = allocation->x + border_width + 1 + xthickness + 2;
998 child.width -= child.x;
1000 gtk_widget_size_request (GTK_BIN (widget)->child, &req);
1001 child.height = req.height;
1002 gtk_widget_size_allocate (GTK_BIN (widget)->child, &child);
1006 gtk_widget_size_request (combo_box->priv->button, &req);
1007 child.x = allocation->x + allocation->width - req.width;
1008 child.y = allocation->y;
1009 child.width = req.width;
1010 child.height = req.height;
1011 gtk_widget_size_allocate (combo_box->priv->button, &child);
1013 child.x = allocation->x;
1014 child.y = allocation->y;
1015 child.width = allocation->width - req.width;
1016 gtk_widget_size_allocate (GTK_BIN (widget)->child, &child);
1024 gtk_widget_size_request (combo_box->priv->button, &req);
1025 child.x = allocation->x + allocation->width - req.width;
1026 child.y = allocation->y;
1027 child.width = req.width;
1028 child.height = req.height;
1029 gtk_widget_size_allocate (combo_box->priv->button, &child);
1032 child.x = allocation->x;
1033 child.y = allocation->y;
1034 child.width = allocation->width - req.width;
1036 if (combo_box->priv->cell_view_frame)
1038 gtk_widget_size_allocate (combo_box->priv->cell_view_frame, &child);
1042 GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
1043 GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness;
1045 GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
1046 GTK_WIDGET (combo_box->priv->cell_view_frame)->style->ythickness;
1048 GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
1049 GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness;
1051 GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width +
1052 GTK_WIDGET (combo_box->priv->cell_view_frame)->style->ythickness;
1055 gtk_widget_size_allocate (GTK_BIN (combo_box)->child, &child);
1060 gtk_combo_box_forall (GtkContainer *container,
1061 gboolean include_internals,
1062 GtkCallback callback,
1063 gpointer callback_data)
1065 GtkComboBox *combo_box = GTK_COMBO_BOX (container);
1067 if (include_internals)
1069 if (!combo_box->priv->tree_view)
1071 if (combo_box->priv->cell_view && combo_box->priv->button)
1073 (* callback) (combo_box->priv->button, callback_data);
1074 (* callback) (combo_box->priv->separator, callback_data);
1075 (* callback) (combo_box->priv->arrow, callback_data);
1077 else if (combo_box->priv->arrow)
1079 (* callback) (combo_box->priv->button, callback_data);
1080 (* callback) (combo_box->priv->arrow, callback_data);
1085 (* callback) (combo_box->priv->button, callback_data);
1086 if (combo_box->priv->cell_view_frame)
1087 (* callback) (combo_box->priv->cell_view_frame, callback_data);
1091 if (GTK_BIN (container)->child)
1092 (* callback) (GTK_BIN (container)->child, callback_data);
1096 gtk_combo_box_expose_event (GtkWidget *widget,
1097 GdkEventExpose *event)
1099 GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
1101 if (!combo_box->priv->tree_view)
1103 gtk_container_propagate_expose (GTK_CONTAINER (widget),
1104 combo_box->priv->button, event);
1106 if (combo_box->priv->separator)
1108 gtk_container_propagate_expose (GTK_CONTAINER (combo_box->priv->button),
1109 combo_box->priv->separator, event);
1111 /* if not in this case, arrow gets its expose event from button */
1112 gtk_container_propagate_expose (GTK_CONTAINER (combo_box->priv->button),
1113 combo_box->priv->arrow, event);
1118 gtk_container_propagate_expose (GTK_CONTAINER (widget),
1119 combo_box->priv->button, event);
1121 if (combo_box->priv->cell_view_frame)
1122 gtk_container_propagate_expose (GTK_CONTAINER (widget),
1123 combo_box->priv->cell_view_frame, event);
1126 gtk_container_propagate_expose (GTK_CONTAINER (widget),
1127 GTK_BIN (widget)->child, event);
1137 cell_view_sync_cells (GtkComboBox *combo_box,
1138 GtkCellView *cell_view)
1142 for (k = combo_box->priv->cells; k; k = k->next)
1145 ComboCellInfo *info = (ComboCellInfo *)k->data;
1147 if (info->pack == GTK_PACK_START)
1148 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (cell_view),
1149 info->cell, info->expand);
1150 else if (info->pack == GTK_PACK_END)
1151 gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (cell_view),
1152 info->cell, info->expand);
1154 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (cell_view),
1156 info->func, info->func_data, NULL);
1158 for (j = info->attributes; j; j = j->next->next)
1160 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (cell_view),
1163 GPOINTER_TO_INT (j->next->data));
1169 gtk_combo_box_menu_setup (GtkComboBox *combo_box,
1170 gboolean add_childs)
1176 if (combo_box->priv->cell_view)
1178 combo_box->priv->button = gtk_toggle_button_new ();
1179 g_signal_connect (combo_box->priv->button, "toggled",
1180 G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
1181 gtk_widget_set_parent (combo_box->priv->button,
1182 GTK_BIN (combo_box)->child->parent);
1184 combo_box->priv->separator = gtk_vseparator_new ();
1185 gtk_widget_set_parent (combo_box->priv->separator,
1186 combo_box->priv->button);
1187 gtk_widget_show (combo_box->priv->separator);
1189 combo_box->priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
1190 gtk_widget_set_parent (combo_box->priv->arrow, combo_box->priv->button);
1191 gtk_widget_show (combo_box->priv->arrow);
1193 gtk_widget_show_all (combo_box->priv->button);
1195 if (GTK_WIDGET_MAPPED (GTK_BIN (combo_box)->child))
1197 /* I have no clue why, but we need to manually map in this case. */
1198 gtk_widget_map (combo_box->priv->separator);
1199 gtk_widget_map (combo_box->priv->arrow);
1204 combo_box->priv->button = gtk_toggle_button_new ();
1205 g_signal_connect (combo_box->priv->button, "toggled",
1206 G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
1207 gtk_widget_set_parent (combo_box->priv->button,
1208 GTK_BIN (combo_box)->child->parent);
1210 combo_box->priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
1211 gtk_container_add (GTK_CONTAINER (combo_box->priv->button),
1212 combo_box->priv->arrow);
1213 gtk_widget_show_all (combo_box->priv->button);
1216 combo_box->priv->inserted_id =
1217 g_signal_connect (combo_box->priv->model, "row_inserted",
1218 G_CALLBACK (gtk_combo_box_menu_row_inserted),
1220 combo_box->priv->deleted_id =
1221 g_signal_connect (combo_box->priv->model, "row_deleted",
1222 G_CALLBACK (gtk_combo_box_menu_row_deleted),
1224 combo_box->priv->changed_id =
1225 g_signal_connect (combo_box->priv->model, "row_changed",
1226 G_CALLBACK (gtk_combo_box_menu_row_changed),
1229 g_signal_connect (combo_box->priv->button, "button_press_event",
1230 G_CALLBACK (gtk_combo_box_menu_button_press),
1233 /* create our funky menu */
1234 box = gtk_menu_new ();
1235 gtk_combo_box_set_popup_widget (combo_box, box);
1241 items = gtk_tree_model_iter_n_children (combo_box->priv->model, NULL);
1243 for (i = 0; i < items; i++)
1247 path = gtk_tree_path_new_from_indices (i, -1);
1248 tmp = gtk_cell_view_menu_item_new_from_model (combo_box->priv->model,
1250 g_signal_connect (tmp, "activate",
1251 G_CALLBACK (gtk_combo_box_menu_item_activate),
1254 cell_view_sync_cells (combo_box, GTK_CELL_VIEW (GTK_BIN (tmp)->child));
1256 gtk_menu_shell_append (GTK_MENU_SHELL (box), tmp);
1257 gtk_widget_show (tmp);
1259 gtk_tree_path_free (path);
1264 gtk_combo_box_menu_destroy (GtkComboBox *combo_box)
1266 /* disconnect signal handlers */
1267 g_signal_handler_disconnect (combo_box->priv->model,
1268 combo_box->priv->inserted_id);
1269 g_signal_handler_disconnect (combo_box->priv->model,
1270 combo_box->priv->deleted_id);
1271 g_signal_handler_disconnect (combo_box->priv->model,
1272 combo_box->priv->changed_id);
1274 g_signal_handlers_disconnect_matched (combo_box->priv->button,
1275 G_SIGNAL_MATCH_DATA,
1277 gtk_combo_box_menu_button_press, NULL);
1279 combo_box->priv->inserted_id =
1280 combo_box->priv->deleted_id =
1281 combo_box->priv->changed_id = -1;
1283 /* unparent will remove our latest ref */
1284 if (combo_box->priv->cell_view)
1286 gtk_widget_unparent (combo_box->priv->arrow);
1287 gtk_widget_unparent (combo_box->priv->separator);
1288 gtk_widget_unparent (combo_box->priv->button);
1292 /* will destroy the arrow too */
1293 gtk_widget_unparent (combo_box->priv->button);
1296 /* changing the popup window will unref the menu and the childs */
1304 gtk_combo_box_item_get_size (GtkComboBox *combo_box,
1311 gtk_tree_model_iter_nth_child (combo_box->priv->model, &iter, NULL, index);
1315 if (combo_box->priv->col_column == -1)
1318 gtk_tree_model_get (combo_box->priv->model, &iter,
1319 combo_box->priv->col_column, cols,
1325 if (combo_box->priv->row_column == -1)
1328 gtk_tree_model_get (combo_box->priv->model, &iter,
1329 combo_box->priv->row_column, rows,
1335 menu_occupied (GtkMenu *menu,
1339 guint bottom_attach)
1343 g_return_val_if_fail (GTK_IS_MENU (menu), TRUE);
1344 g_return_val_if_fail (left_attach < right_attach, TRUE);
1345 g_return_val_if_fail (top_attach < bottom_attach, TRUE);
1347 for (i = GTK_MENU_SHELL (menu)->children; i; i = i->next)
1350 gboolean h_intersect = FALSE;
1351 gboolean v_intersect = FALSE;
1353 gtk_container_child_get (GTK_CONTAINER (menu), i->data,
1356 "bottom_attach", &b,
1360 /* look if this item intersects with the given coordinates */
1361 h_intersect = left_attach <= l && l <= right_attach;
1362 h_intersect &= left_attach <= r && r <= right_attach;
1364 v_intersect = top_attach <= t && t <= bottom_attach;
1365 v_intersect &= top_attach <= b && b <= bottom_attach;
1367 if (h_intersect && v_intersect)
1375 gtk_combo_box_relayout_item (GtkComboBox *combo_box,
1378 gint current_col = 0, current_row = 0;
1384 menu = combo_box->priv->popup_widget;
1385 if (!GTK_IS_MENU_SHELL (menu))
1388 list = gtk_container_get_children (GTK_CONTAINER (menu));
1389 item = g_list_nth_data (list, index);
1391 gtk_combo_box_item_get_size (combo_box, index, &cols, &rows);
1393 /* look for a good spot */
1396 if (current_col + cols > combo_box->priv->wrap_width)
1402 if (!menu_occupied (GTK_MENU (menu),
1403 current_col, current_col + cols,
1404 current_row, current_row + rows))
1410 /* set attach props */
1411 gtk_menu_attach (GTK_MENU (menu), item,
1412 current_col, current_col + cols,
1413 current_row, current_row + rows);
1417 gtk_combo_box_relayout (GtkComboBox *combo_box)
1423 /* ensure we are in menu style */
1424 if (combo_box->priv->tree_view)
1425 gtk_combo_box_list_destroy (combo_box);
1427 menu = combo_box->priv->popup_widget;
1429 if (!GTK_IS_MENU_SHELL (menu))
1431 gtk_combo_box_menu_setup (combo_box, FALSE);
1432 menu = combo_box->priv->popup_widget;
1435 /* get rid of all children */
1436 g_return_if_fail (GTK_IS_MENU_SHELL (menu));
1438 list = gtk_container_get_children (GTK_CONTAINER (menu));
1440 for (j = g_list_last (list); j; j = j->prev)
1441 gtk_container_remove (GTK_CONTAINER (menu), j->data);
1446 items = gtk_tree_model_iter_n_children (combo_box->priv->model, NULL);
1448 for (i = 0; i < items; i++)
1453 path = gtk_tree_path_new_from_indices (i, -1);
1454 tmp = gtk_cell_view_menu_item_new_from_model (combo_box->priv->model,
1457 g_signal_connect (tmp, "activate",
1458 G_CALLBACK (gtk_combo_box_menu_item_activate),
1461 cell_view_sync_cells (combo_box, GTK_CELL_VIEW (GTK_BIN (tmp)->child));
1463 gtk_menu_shell_insert (GTK_MENU_SHELL (menu), tmp, i);
1465 if (combo_box->priv->wrap_width)
1466 gtk_combo_box_relayout_item (combo_box, i);
1468 gtk_widget_show (tmp);
1470 gtk_tree_path_free (path);
1476 gtk_combo_box_menu_button_press (GtkWidget *widget,
1477 GdkEventButton *event,
1480 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1482 if (! GTK_IS_MENU (combo_box->priv->popup_widget))
1485 if (event->type == GDK_BUTTON_PRESS && event->button == 1)
1487 combo_box->priv->popup_in_progress = TRUE;
1488 gtk_menu_popup (GTK_MENU (combo_box->priv->popup_widget),
1490 gtk_combo_box_menu_position, combo_box,
1491 event->button, event->time);
1500 gtk_combo_box_menu_item_activate (GtkWidget *item,
1505 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1507 menu = combo_box->priv->popup_widget;
1508 g_return_if_fail (GTK_IS_MENU (menu));
1510 index = g_list_index (GTK_MENU_SHELL (menu)->children, item);
1512 gtk_combo_box_set_active (combo_box, index);
1516 gtk_combo_box_menu_row_inserted (GtkTreeModel *model,
1523 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1525 menu = combo_box->priv->popup_widget;
1526 g_return_if_fail (GTK_IS_MENU (menu));
1528 item = gtk_cell_view_menu_item_new_from_model (model, path);
1529 g_signal_connect (item, "activate",
1530 G_CALLBACK (gtk_combo_box_menu_item_activate),
1533 cell_view_sync_cells (combo_box, GTK_CELL_VIEW (GTK_BIN (item)->child));
1535 gtk_menu_shell_insert (GTK_MENU_SHELL (menu), item,
1536 gtk_tree_path_get_indices (path)[0]);
1537 gtk_widget_show (item);
1541 gtk_combo_box_menu_row_deleted (GtkTreeModel *model,
1548 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1550 index = gtk_tree_path_get_indices (path)[0];
1551 items = gtk_tree_model_iter_n_children (model, NULL);
1553 if (gtk_combo_box_get_active (combo_box) == index)
1554 gtk_combo_box_set_active (combo_box, index + 1 % items);
1556 menu = combo_box->priv->popup_widget;
1557 g_return_if_fail (GTK_IS_MENU (menu));
1559 item = g_list_nth_data (GTK_MENU_SHELL (menu)->children, index);
1560 g_return_if_fail (GTK_IS_MENU_ITEM (item));
1562 gtk_container_remove (GTK_CONTAINER (menu), item);
1566 gtk_combo_box_menu_row_changed (GtkTreeModel *model,
1571 GtkComboBox *combo_box = GTK_COMBO_BOX (user_data);
1574 if (combo_box->priv->wrap_width)
1575 gtk_combo_box_relayout_item (combo_box,
1576 gtk_tree_path_get_indices (path)[0]);
1578 width = gtk_combo_box_calc_requested_width (combo_box, path);
1580 if (width > combo_box->priv->width)
1582 gtk_widget_set_size_request (combo_box->priv->cell_view, width, -1);
1583 gtk_widget_queue_resize (combo_box->priv->cell_view);
1584 combo_box->priv->width = width;
1593 gtk_combo_box_list_setup (GtkComboBox *combo_box)
1596 GtkTreeSelection *sel;
1598 combo_box->priv->button = gtk_toggle_button_new ();
1599 gtk_widget_set_parent (combo_box->priv->button,
1600 GTK_BIN (combo_box)->child->parent);
1601 g_signal_connect (combo_box->priv->button, "button_press_event",
1602 G_CALLBACK (gtk_combo_box_list_button_pressed), combo_box);
1603 g_signal_connect (combo_box->priv->button, "toggled",
1604 G_CALLBACK (gtk_combo_box_button_toggled), combo_box);
1606 combo_box->priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
1607 gtk_container_add (GTK_CONTAINER (combo_box->priv->button),
1608 combo_box->priv->arrow);
1609 gtk_widget_show_all (combo_box->priv->button);
1611 if (combo_box->priv->cell_view)
1613 combo_box->priv->cell_view_frame = gtk_frame_new (NULL);
1614 gtk_widget_set_parent (combo_box->priv->cell_view_frame,
1615 GTK_BIN (combo_box)->child->parent);
1616 gtk_frame_set_shadow_type (GTK_FRAME (combo_box->priv->cell_view_frame),
1619 g_object_set (G_OBJECT (combo_box->priv->cell_view),
1620 "background", "white",
1621 "background_set", TRUE,
1624 gtk_widget_show (combo_box->priv->cell_view_frame);
1627 combo_box->priv->tree_view = gtk_tree_view_new ();
1628 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (combo_box->priv->tree_view));
1629 gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE);
1630 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (combo_box->priv->tree_view),
1633 g_signal_connect (combo_box->priv->tree_view, "button_press_event",
1634 G_CALLBACK (gtk_combo_box_list_button_pressed),
1636 g_signal_connect (combo_box->priv->tree_view, "button_release_event",
1637 G_CALLBACK (gtk_combo_box_list_button_released),
1639 g_signal_connect (combo_box->priv->tree_view, "key_press_event",
1640 G_CALLBACK (gtk_combo_box_list_key_press),
1643 combo_box->priv->column = gtk_tree_view_column_new ();
1644 gtk_tree_view_append_column (GTK_TREE_VIEW (combo_box->priv->tree_view),
1645 combo_box->priv->column);
1647 /* set the models */
1648 gtk_tree_view_set_model (GTK_TREE_VIEW (combo_box->priv->tree_view),
1649 combo_box->priv->model);
1651 combo_box->priv->changed_id =
1652 g_signal_connect (combo_box->priv->model, "row_changed",
1653 G_CALLBACK (gtk_combo_box_list_row_changed),
1657 for (i = combo_box->priv->cells; i; i = i->next)
1660 ComboCellInfo *info = (ComboCellInfo *)i->data;
1662 if (info->pack == GTK_PACK_START)
1663 gtk_tree_view_column_pack_start (combo_box->priv->column,
1664 info->cell, info->expand);
1665 else if (info->pack == GTK_PACK_END)
1666 gtk_tree_view_column_pack_end (combo_box->priv->column,
1667 info->cell, info->expand);
1669 for (j = info->attributes; j; j = j->next->next)
1671 gtk_tree_view_column_add_attribute (combo_box->priv->column,
1674 GPOINTER_TO_INT (j->next->data));
1678 if (combo_box->priv->active_item != -1)
1682 path = gtk_tree_path_new_from_indices (combo_box->priv->active_item, -1);
1685 gtk_tree_view_set_cursor (GTK_TREE_VIEW (combo_box->priv->tree_view),
1687 gtk_tree_path_free (path);
1691 /* set sample/popup widgets */
1692 gtk_combo_box_set_popup_widget (GTK_COMBO_BOX (combo_box),
1693 combo_box->priv->tree_view);
1695 gtk_widget_show (combo_box->priv->tree_view);
1699 gtk_combo_box_list_destroy (GtkComboBox *combo_box)
1701 /* disconnect signals */
1702 g_signal_handler_disconnect (combo_box->priv->model,
1703 combo_box->priv->changed_id);
1704 combo_box->priv->changed_id = -1;
1706 g_signal_handlers_disconnect_matched (combo_box->priv->tree_view,
1707 G_SIGNAL_MATCH_DATA,
1708 0, 0, NULL, NULL, combo_box);
1709 g_signal_handlers_disconnect_matched (combo_box->priv->button,
1710 G_SIGNAL_MATCH_DATA,
1712 gtk_combo_box_list_button_pressed,
1715 /* destroy things (unparent will kill the latest ref from us)
1716 * last unref on button will destroy the arrow
1718 gtk_widget_unparent (combo_box->priv->button);
1720 if (combo_box->priv->cell_view)
1722 g_object_set (G_OBJECT (combo_box->priv->cell_view),
1723 "background_set", FALSE,
1726 gtk_widget_unparent (combo_box->priv->cell_view_frame);
1729 gtk_widget_destroy (combo_box->priv->tree_view);
1730 combo_box->priv->tree_view = NULL;
1731 combo_box->priv->popup_widget = NULL;
1736 gtk_combo_box_list_remove_grabs (GtkComboBox *combo_box)
1738 if (GTK_WIDGET_HAS_GRAB (combo_box->priv->tree_view))
1739 gtk_grab_remove (combo_box->priv->tree_view);
1741 if (GTK_WIDGET_HAS_GRAB (combo_box->priv->popup_window))
1743 gtk_grab_remove (combo_box->priv->popup_window);
1744 gdk_pointer_ungrab (GDK_CURRENT_TIME);
1749 gtk_combo_box_list_button_pressed (GtkWidget *widget,
1750 GdkEventButton *event,
1753 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
1755 GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *)event);
1757 if (ewidget == combo_box->priv->tree_view)
1760 if ((ewidget != combo_box->priv->button) ||
1761 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (combo_box->priv->button)))
1764 gtk_combo_box_popup (combo_box);
1766 gtk_grab_add (combo_box->priv->popup_window);
1767 gdk_pointer_grab (combo_box->priv->popup_window->window, TRUE,
1768 GDK_BUTTON_PRESS_MASK |
1769 GDK_BUTTON_RELEASE_MASK |
1770 GDK_POINTER_MOTION_MASK,
1771 NULL, NULL, GDK_CURRENT_TIME);
1773 gtk_grab_add (combo_box->priv->tree_view);
1775 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button),
1778 combo_box->priv->popup_in_progress = TRUE;
1784 gtk_combo_box_list_button_released (GtkWidget *widget,
1785 GdkEventButton *event,
1789 GtkTreePath *path = NULL;
1791 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
1793 gboolean popup_in_progress = FALSE;
1795 GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *)event);
1797 if (combo_box->priv->popup_in_progress)
1799 popup_in_progress = TRUE;
1800 combo_box->priv->popup_in_progress = FALSE;
1803 if (ewidget != combo_box->priv->tree_view)
1805 if (ewidget == combo_box->priv->button &&
1806 !popup_in_progress &&
1807 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (combo_box->priv->button)))
1809 gtk_combo_box_list_remove_grabs (combo_box);
1810 gtk_combo_box_popdown (combo_box);
1814 /* released outside treeview */
1815 if (ewidget != combo_box->priv->button)
1817 gtk_combo_box_list_remove_grabs (combo_box);
1818 gtk_combo_box_popdown (combo_box);
1827 gtk_combo_box_list_remove_grabs (combo_box);
1829 /* select something cool */
1830 ret = gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget),
1836 return TRUE; /* clicked outside window? */
1838 gtk_combo_box_set_active (combo_box, gtk_tree_path_get_indices (path)[0]);
1839 gtk_combo_box_popdown (combo_box);
1841 gtk_tree_path_free (path);
1847 gtk_combo_box_list_key_press (GtkWidget *widget,
1851 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
1853 if ((event->keyval == GDK_Return || event->keyval == GDK_KP_Enter ||
1854 event->keyval == GDK_space || event->keyval == GDK_KP_Space) ||
1855 event->keyval == GDK_Escape)
1857 if (event->keyval != GDK_Escape)
1861 GtkTreeModel *model = NULL;
1862 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (combo_box->priv->tree_view));
1864 ret = gtk_tree_selection_get_selected (sel, &model, &iter);
1869 path = gtk_tree_model_get_path (model, &iter);
1872 gtk_combo_box_set_active (combo_box, gtk_tree_path_get_indices (path)[0]);
1873 gtk_tree_path_free (path);
1878 /* reset active item -- this is incredibly lame and ugly */
1879 gtk_combo_box_set_active (combo_box,
1880 gtk_combo_box_get_active (combo_box));
1882 gtk_combo_box_list_remove_grabs (combo_box);
1883 gtk_combo_box_popdown (combo_box);
1892 gtk_combo_box_list_row_changed (GtkTreeModel *model,
1897 GtkComboBox *combo_box = GTK_COMBO_BOX (data);
1900 width = gtk_combo_box_calc_requested_width (combo_box, path);
1902 if (width > combo_box->priv->width)
1904 gtk_widget_set_size_request (combo_box->priv->cell_view, width, -1);
1905 gtk_widget_queue_resize (combo_box->priv->cell_view);
1906 combo_box->priv->width = width;
1911 * GtkCellLayout implementation
1914 gtk_combo_box_cell_layout_pack_start (GtkCellLayout *layout,
1915 GtkCellRenderer *cell,
1918 ComboCellInfo *info;
1919 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
1922 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
1923 g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
1925 info = g_new0 (ComboCellInfo, 1);
1927 info->expand = expand;
1928 info->pack = GTK_PACK_START;
1930 combo_box->priv->cells = g_slist_append (combo_box->priv->cells, info);
1932 if (combo_box->priv->cell_view)
1933 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box->priv->cell_view),
1936 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box->priv->measurer),
1939 if (combo_box->priv->column)
1940 gtk_tree_view_column_pack_start (combo_box->priv->column, cell, expand);
1942 menu = combo_box->priv->popup_widget;
1943 if (GTK_IS_MENU (menu))
1947 list = gtk_container_get_children (GTK_CONTAINER (menu));
1948 for (i = list; i; i = i->next)
1952 if (GTK_IS_CELL_VIEW_MENU_ITEM (i->data))
1953 view = GTK_CELL_VIEW (GTK_BIN (i->data)->child);
1955 view = GTK_CELL_VIEW (i->data);
1957 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (view), cell, expand);
1964 gtk_combo_box_cell_layout_pack_end (GtkCellLayout *layout,
1965 GtkCellRenderer *cell,
1968 ComboCellInfo *info;
1969 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
1972 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
1973 g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
1975 info = g_new0 (ComboCellInfo, 1);
1977 info->expand = expand;
1978 info->pack = GTK_PACK_END;
1980 if (combo_box->priv->cell_view)
1981 gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (combo_box->priv->cell_view),
1984 gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (combo_box->priv->measurer),
1987 if (combo_box->priv->column)
1988 gtk_tree_view_column_pack_end (combo_box->priv->column, cell, expand);
1990 menu = combo_box->priv->popup_widget;
1991 if (GTK_IS_MENU (menu))
1995 list = gtk_container_get_children (GTK_CONTAINER (menu));
1996 for (i = list; i; i = i->next)
2000 if (GTK_IS_CELL_VIEW_MENU_ITEM (i->data))
2001 view = GTK_CELL_VIEW (GTK_BIN (i->data)->child);
2003 view = GTK_CELL_VIEW (i->data);
2005 gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (view), cell, expand);
2012 gtk_combo_box_cell_layout_clear (GtkCellLayout *layout)
2015 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
2017 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2019 if (combo_box->priv->cell_view)
2020 gtk_cell_layout_clear (GTK_CELL_LAYOUT (combo_box->priv->cell_view));
2022 if (combo_box->priv->measurer)
2023 gtk_cell_layout_clear (GTK_CELL_LAYOUT (combo_box->priv->measurer));
2025 if (combo_box->priv->column)
2026 gtk_tree_view_column_clear (combo_box->priv->column);
2028 menu = combo_box->priv->popup_widget;
2029 if (GTK_IS_MENU (menu))
2033 list = gtk_container_get_children (GTK_CONTAINER (menu));
2034 for (i = list; i; i = i->next)
2038 if (GTK_IS_CELL_VIEW_MENU_ITEM (i->data))
2039 view = GTK_CELL_VIEW (GTK_BIN (i->data)->child);
2041 view = GTK_CELL_VIEW (i->data);
2043 gtk_cell_layout_clear (GTK_CELL_LAYOUT (view));
2049 gtk_combo_box_cell_layout_add_attribute (GtkCellLayout *layout,
2050 GtkCellRenderer *cell,
2051 const gchar *attribute,
2054 ComboCellInfo *info;
2055 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
2058 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2059 g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
2061 info = gtk_combo_box_get_cell_info (combo_box, cell);
2063 info->attributes = g_slist_prepend (info->attributes,
2064 GINT_TO_POINTER (column));
2065 info->attributes = g_slist_prepend (info->attributes,
2066 g_strdup (attribute));
2068 if (combo_box->priv->cell_view)
2069 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box->priv->cell_view),
2070 cell, attribute, column);
2072 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box->priv->measurer),
2073 cell, attribute, column);
2075 if (combo_box->priv->column)
2076 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combo_box->priv->column),
2077 cell, attribute, column);
2079 menu = combo_box->priv->popup_widget;
2080 if (GTK_IS_MENU (menu))
2084 list = gtk_container_get_children (GTK_CONTAINER (menu));
2085 for (i = list; i; i = i->next)
2089 if (GTK_IS_CELL_VIEW_MENU_ITEM (i->data))
2090 view = GTK_CELL_VIEW (GTK_BIN (i->data)->child);
2092 view = GTK_CELL_VIEW (i->data);
2094 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (view), cell,
2100 gtk_combo_box_remeasure (combo_box);
2104 gtk_combo_box_cell_layout_set_cell_data_func (GtkCellLayout *layout,
2105 GtkCellRenderer *cell,
2106 GtkCellLayoutDataFunc func,
2108 GDestroyNotify destroy)
2110 ComboCellInfo *info;
2111 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
2114 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2116 info = gtk_combo_box_get_cell_info (combo_box, cell);
2117 g_return_if_fail (info != NULL);
2121 GDestroyNotify d = info->destroy;
2123 info->destroy = NULL;
2124 d (info->func_data);
2128 info->func_data = func_data;
2129 info->destroy = destroy;
2131 if (combo_box->priv->cell_view)
2132 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo_box->priv->cell_view), cell, func, func_data, NULL);
2134 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo_box->priv->measurer), cell, func, func_data, NULL);
2136 if (combo_box->priv->column)
2137 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (combo_box->priv->column), cell, func, func_data, NULL);
2139 menu = combo_box->priv->popup_widget;
2140 if (GTK_IS_MENU (menu))
2144 list = gtk_container_get_children (GTK_CONTAINER (menu));
2145 for (i = list; i; i = i->next)
2149 if (GTK_IS_CELL_VIEW_MENU_ITEM (i->data))
2150 view = GTK_CELL_VIEW (GTK_BIN (i->data)->child);
2152 view = GTK_CELL_VIEW (i->data);
2154 gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (view), cell,
2155 func, func_data, NULL);
2160 gtk_combo_box_remeasure (combo_box);
2164 gtk_combo_box_cell_layout_clear_attributes (GtkCellLayout *layout,
2165 GtkCellRenderer *cell)
2167 ComboCellInfo *info;
2168 GtkComboBox *combo_box = GTK_COMBO_BOX (layout);
2172 g_return_if_fail (GTK_IS_COMBO_BOX (layout));
2173 g_return_if_fail (GTK_IS_CELL_RENDERER (cell));
2175 info = gtk_combo_box_get_cell_info (combo_box, cell);
2176 g_return_if_fail (info != NULL);
2178 list = info->attributes;
2179 while (list && list->next)
2181 g_free (list->data);
2182 list = list->next->next;
2184 g_slist_free (list);
2186 info->attributes = NULL;
2188 if (combo_box->priv->cell_view)
2189 gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (combo_box->priv->cell_view), cell);
2191 gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (combo_box->priv->measurer), cell);
2193 if (combo_box->priv->column)
2194 gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (combo_box->priv->column), cell);
2196 menu = combo_box->priv->popup_widget;
2197 if (GTK_IS_MENU (menu))
2201 list = gtk_container_get_children (GTK_CONTAINER (menu));
2202 for (i = list; i; i = i->next)
2206 if (GTK_IS_CELL_VIEW_MENU_ITEM (i->data))
2207 view = GTK_CELL_VIEW (GTK_BIN (i->data)->child);
2209 view = GTK_CELL_VIEW (i->data);
2211 gtk_cell_layout_clear_attributes (GTK_CELL_LAYOUT (view), cell);
2216 gtk_combo_box_remeasure (combo_box);
2224 * gtk_combo_box_new:
2225 * @model: A #GtkTreeModel.
2227 * Creates a new #GtkComboBox with the model initialized to @model.
2229 * Return value: A new #GtkComboBox.
2234 gtk_combo_box_new (GtkTreeModel *model)
2236 GtkComboBox *combo_box;
2238 g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL);
2240 combo_box = GTK_COMBO_BOX (g_object_new (gtk_combo_box_get_type (),
2244 return GTK_WIDGET (combo_box);
2248 * gtk_combo_box_set_wrap_width:
2249 * @combo_box: A #GtkComboBox.
2250 * @width: Preferred number of columns.
2252 * Sets the wrap width of @combo_box to be @width. The wrap width is basically
2253 * the preferred number of columns when you want to the popup to be layed out
2259 gtk_combo_box_set_wrap_width (GtkComboBox *combo_box,
2262 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2263 g_return_if_fail (width > 0);
2265 combo_box->priv->wrap_width = width;
2267 gtk_combo_box_relayout (combo_box);
2271 * gtk_combo_box_set_row_span_column:
2272 * @combo_box: A #GtkComboBox.
2273 * @row_span: A column in the model passed during construction.
2275 * Sets the column with row span information for @combo_box to be @row_span.
2276 * The row span column contains integers which indicate how many rows
2277 * an item should span.
2282 gtk_combo_box_set_row_span_column (GtkComboBox *combo_box,
2287 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2289 col = gtk_tree_model_get_n_columns (combo_box->priv->model);
2290 g_return_if_fail (row_span >= 0 && row_span < col);
2292 combo_box->priv->row_column = row_span;
2294 gtk_combo_box_relayout (combo_box);
2298 * gtk_combo_box_set_column_span_column:
2299 * @combo_box: A #GtkComboBox.
2300 * @column_span: A column in the model passed during construction.
2302 * Sets the column with column span information for @combo_box to be
2303 * @column_span. The column span column contains integers which indicate
2304 * how many columns an item should span.
2309 gtk_combo_box_set_column_span_column (GtkComboBox *combo_box,
2314 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2316 col = gtk_tree_model_get_n_columns (combo_box->priv->model);
2317 g_return_if_fail (column_span >= 0 && column_span < col);
2319 combo_box->priv->col_column = column_span;
2321 gtk_combo_box_relayout (combo_box);
2325 * gtk_combo_box_get_active:
2326 * @combo_box: A #GtkComboBox.
2328 * Returns the index of the currently active item.
2330 * Return value: An integer which is the index of the currently active item.
2335 gtk_combo_box_get_active (GtkComboBox *combo_box)
2337 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), 0);
2339 return combo_box->priv->active_item;
2343 * gtk_combo_box_set_active:
2344 * @combo_box: A #GtkComboBox.
2345 * @index: An index in the model passed during construction.
2347 * Sets the active item of @combo_box to be the item at @index.
2352 gtk_combo_box_set_active (GtkComboBox *combo_box,
2357 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2358 /* -1 means "no item selected" */
2359 g_return_if_fail (index >= -1);
2361 if (combo_box->priv->active_item == index)
2364 combo_box->priv->active_item = index;
2368 if (combo_box->priv->tree_view)
2369 gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (combo_box->priv->tree_view)));
2372 GtkMenu *menu = GTK_MENU (combo_box->priv->popup_widget);
2374 if (GTK_IS_MENU (menu))
2375 gtk_menu_set_active (menu, -1);
2378 if (combo_box->priv->cell_view)
2379 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (combo_box->priv->cell_view), NULL);
2383 path = gtk_tree_path_new_from_indices (index, -1);
2385 if (combo_box->priv->tree_view)
2386 gtk_tree_view_set_cursor (GTK_TREE_VIEW (combo_box->priv->tree_view), path, NULL, FALSE);
2389 GtkMenu *menu = GTK_MENU (combo_box->priv->popup_widget);
2391 if (GTK_IS_MENU (menu))
2392 gtk_menu_set_active (GTK_MENU (menu), index);
2395 if (combo_box->priv->cell_view)
2396 gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (combo_box->priv->cell_view), path);
2398 gtk_tree_path_free (path);
2401 g_signal_emit_by_name (combo_box, "changed", NULL, NULL);
2405 * gtk_combo_box_get_model
2406 * @combo_box: A #GtkComboBox.
2408 * Returns the #GtkTreeModel which is acting as data source for @combo_box.
2410 * Return value: A #GtkTreeModel which was passed during construction.
2415 gtk_combo_box_get_model (GtkComboBox *combo_box)
2417 g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL);
2419 return combo_box->priv->model;
2423 /* convenience API for simple text combos */
2426 * gtk_combo_box_new_text:
2428 * Convenience function which constructs a new text combo box, which is a
2429 * #GtkComboBox just displaying strings. If you use this function to create
2430 * a text combo box, you only want to manipulate it's data source with the
2431 * following convenience functions: gtk_combo_box_append_text(),
2432 * gtk_combo_box_insert_text() and gtk_combo_box_prepend_text().
2434 * Return value: A new text combo box.
2439 gtk_combo_box_new_text (void)
2441 GtkWidget *combo_box;
2442 GtkCellRenderer *cell;
2443 GtkListStore *store;
2445 store = gtk_list_store_new (1, G_TYPE_STRING);
2447 combo_box = gtk_combo_box_new (GTK_TREE_MODEL (store));
2449 cell = gtk_cell_renderer_text_new ();
2450 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), cell, TRUE);
2451 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), cell,
2459 * gtk_combo_box_append_text:
2460 * @combo_box: A #GtkComboBox constructed using gtk_combo_box_new_text().
2463 * Appends @string to the list of strings stored in @combo_box. Note that
2464 * you can only use this function with combo boxes constructed with
2465 * gtk_combo_box_new_text().
2470 gtk_combo_box_append_text (GtkComboBox *combo_box,
2474 GtkListStore *store;
2476 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2477 g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model));
2478 g_return_if_fail (text != NULL);
2480 store = GTK_LIST_STORE (combo_box->priv->model);
2482 gtk_list_store_append (store, &iter);
2483 gtk_list_store_set (store, &iter, 0, text, -1);
2487 * gtk_combo_box_insert_text:
2488 * @combo_box: A #GtkComboBox constructed using gtk_combo_box_new_text().
2489 * @position: An index to insert @text.
2492 * Inserts @string at @position in the list of strings stored in @combo_box.
2493 * Note that you can only use this function with combo boxes constructed
2494 * with gtk_combo_box_new_text().
2499 gtk_combo_box_insert_text (GtkComboBox *combo_box,
2504 GtkListStore *store;
2506 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2507 g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model));
2508 g_return_if_fail (position >= 0);
2509 g_return_if_fail (text != NULL);
2511 store = GTK_LIST_STORE (combo_box->priv->model);
2513 gtk_list_store_insert (store, &iter, position);
2514 gtk_list_store_set (store, &iter, 0, text, -1);
2518 * gtk_combo_box_prepend_text:
2519 * @combo_box: A #GtkComboBox constructed with gtk_combo_box_new_text().
2522 * Prepends @string to the list of strings stored in @combo_box. Note that
2523 * you can only use this function with combo boxes constructed with
2524 * gtk_combo_box_new_text().
2529 gtk_combo_box_prepend_text (GtkComboBox *combo_box,
2533 GtkListStore *store;
2535 g_return_if_fail (GTK_IS_COMBO_BOX (combo_box));
2536 g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model));
2537 g_return_if_fail (text != NULL);
2539 store = GTK_LIST_STORE (combo_box->priv->model);
2541 gtk_list_store_prepend (store, &iter);
2542 gtk_list_store_set (store, &iter, 0, text, -1);