diff --git a/ChangeLog b/ChangeLog index e6acdd0240..ceb5917d10 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,17 @@ +Fri Sep 26 23:49:44 2003 Kristian Rietveld + + Landing the new ComboBox. Note that only gtkcombobox.h and + gtkcomboboxentry.h are public. + + * gtk/gtkcellview.[ch], gtk/gtkcellviewmenuitem.[ch], + gtk/gtkcombobox.[ch], gtk/gtkcomboboxentry.[ch]: new files. + + * gtk/Makefile.am, gtk/gtk.h: adding the new files. + + * tests/testcombo.c: and a new test. + + * tests/Makefile.am: adding the new test. + 2003-09-25 Tor Lillqvist * demos/gtk-demo/main.c: Don't use hardcoded DEMOCODEDIR on Windows. diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index e6acdd0240..ceb5917d10 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -1,3 +1,17 @@ +Fri Sep 26 23:49:44 2003 Kristian Rietveld + + Landing the new ComboBox. Note that only gtkcombobox.h and + gtkcomboboxentry.h are public. + + * gtk/gtkcellview.[ch], gtk/gtkcellviewmenuitem.[ch], + gtk/gtkcombobox.[ch], gtk/gtkcomboboxentry.[ch]: new files. + + * gtk/Makefile.am, gtk/gtk.h: adding the new files. + + * tests/testcombo.c: and a new test. + + * tests/Makefile.am: adding the new test. + 2003-09-25 Tor Lillqvist * demos/gtk-demo/main.c: Don't use hardcoded DEMOCODEDIR on Windows. diff --git a/ChangeLog.pre-2-4 b/ChangeLog.pre-2-4 index e6acdd0240..ceb5917d10 100644 --- a/ChangeLog.pre-2-4 +++ b/ChangeLog.pre-2-4 @@ -1,3 +1,17 @@ +Fri Sep 26 23:49:44 2003 Kristian Rietveld + + Landing the new ComboBox. Note that only gtkcombobox.h and + gtkcomboboxentry.h are public. + + * gtk/gtkcellview.[ch], gtk/gtkcellviewmenuitem.[ch], + gtk/gtkcombobox.[ch], gtk/gtkcomboboxentry.[ch]: new files. + + * gtk/Makefile.am, gtk/gtk.h: adding the new files. + + * tests/testcombo.c: and a new test. + + * tests/Makefile.am: adding the new test. + 2003-09-25 Tor Lillqvist * demos/gtk-demo/main.c: Don't use hardcoded DEMOCODEDIR on Windows. diff --git a/ChangeLog.pre-2-6 b/ChangeLog.pre-2-6 index e6acdd0240..ceb5917d10 100644 --- a/ChangeLog.pre-2-6 +++ b/ChangeLog.pre-2-6 @@ -1,3 +1,17 @@ +Fri Sep 26 23:49:44 2003 Kristian Rietveld + + Landing the new ComboBox. Note that only gtkcombobox.h and + gtkcomboboxentry.h are public. + + * gtk/gtkcellview.[ch], gtk/gtkcellviewmenuitem.[ch], + gtk/gtkcombobox.[ch], gtk/gtkcomboboxentry.[ch]: new files. + + * gtk/Makefile.am, gtk/gtk.h: adding the new files. + + * tests/testcombo.c: and a new test. + + * tests/Makefile.am: adding the new test. + 2003-09-25 Tor Lillqvist * demos/gtk-demo/main.c: Don't use hardcoded DEMOCODEDIR on Windows. diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8 index e6acdd0240..ceb5917d10 100644 --- a/ChangeLog.pre-2-8 +++ b/ChangeLog.pre-2-8 @@ -1,3 +1,17 @@ +Fri Sep 26 23:49:44 2003 Kristian Rietveld + + Landing the new ComboBox. Note that only gtkcombobox.h and + gtkcomboboxentry.h are public. + + * gtk/gtkcellview.[ch], gtk/gtkcellviewmenuitem.[ch], + gtk/gtkcombobox.[ch], gtk/gtkcomboboxentry.[ch]: new files. + + * gtk/Makefile.am, gtk/gtk.h: adding the new files. + + * tests/testcombo.c: and a new test. + + * tests/Makefile.am: adding the new test. + 2003-09-25 Tor Lillqvist * demos/gtk-demo/main.c: Don't use hardcoded DEMOCODEDIR on Windows. diff --git a/gtk/Makefile.am b/gtk/Makefile.am index c0bcf177e8..646894e614 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -118,6 +118,8 @@ gtk_public_h_sources = \ gtkcolorsel.h \ gtkcolorseldialog.h \ gtkcombo.h \ + gtkcombobox.h \ + gtkcomboboxentry.h \ gtkcontainer.h \ gtkctree.h \ gtkcurve.h \ @@ -249,6 +251,8 @@ gtk_public_h_sources = \ # GTK+ header files that don't get installed gtk_private_h_sources = \ + gtkcellview.h \ + gtkcellviewmenuitem.h \ gtkentryprivate.h \ gtkrbtree.h \ gtktextbtree.h \ @@ -290,6 +294,8 @@ gtk_c_sources = \ gtkcellrendererpixbuf.c \ gtkcellrenderertext.c \ gtkcellrenderertoggle.c \ + gtkcellview.c \ + gtkcellviewmenuitem.c \ gtkcheckbutton.c \ gtkcheckmenuitem.c \ gtkclipboard.c \ @@ -298,6 +304,8 @@ gtk_c_sources = \ gtkcolorsel.c \ gtkcolorseldialog.c \ gtkcombo.c \ + gtkcombobox.c \ + gtkcomboboxentry.c \ gtkcontainer.c \ gtkctree.c \ gtkcurve.c \ diff --git a/gtk/gtk.h b/gtk/gtk.h index cbb0de3577..034040720b 100644 --- a/gtk/gtk.h +++ b/gtk/gtk.h @@ -58,6 +58,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/gtk/gtkcellview.c b/gtk/gtkcellview.c new file mode 100644 index 0000000000..448851f59b --- /dev/null +++ b/gtk/gtkcellview.c @@ -0,0 +1,835 @@ +/* gtkellview.c + * Copyright (C) 2002, 2003 Kristian Rietveld + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include + +typedef struct _GtkCellViewCellInfo GtkCellViewCellInfo; +struct _GtkCellViewCellInfo +{ + GtkCellRenderer *cell; + + gint requested_width; + gint real_width; + guint expand : 1; + guint pack : 1; + + GSList *attributes; +}; + +struct _GtkCellViewPrivate +{ + GtkTreeModel *model; + GtkTreeRowReference *displayed_row; + GList *cell_list; + gint spacing; + + GdkColor background; + gboolean background_set; +}; + + +static void gtk_cell_view_class_init (GtkCellViewClass *klass); +static void gtk_cell_view_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec); +static void gtk_cell_view_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec); +static void gtk_cell_view_init (GtkCellView *cellview); +static void gtk_cell_view_finalize (GObject *object); +static void gtk_cell_view_style_set (GtkWidget *widget, + GtkStyle *previous_style); +static void gtk_cell_view_size_request (GtkWidget *widget, + GtkRequisition *requisition); +static void gtk_cell_view_size_allocate (GtkWidget *widget, + GtkAllocation *allocation); +static gboolean gtk_cell_view_expose (GtkWidget *widget, + GdkEventExpose *event); +static void gtk_cell_view_set_attributesv (GtkCellView *cellview, + GtkCellRenderer *renderer, + va_list args); +static void gtk_cell_view_set_valuesv (GtkCellView *cellview, + GtkCellRenderer *renderer, + va_list args); +static void gtk_cell_view_set_cell_data (GtkCellView *cellview); + + +enum +{ + PROP_0, + PROP_BACKGROUND, + PROP_BACKGROUND_GDK, + PROP_BACKGROUND_SET +}; + +static GtkObjectClass *parent_class = NULL; + + +GType +gtk_cell_view_get_type (void) +{ + static GType cell_view_type = 0; + + if (!cell_view_type) + { + static const GTypeInfo cell_view_info = + { + sizeof (GtkCellViewClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) gtk_cell_view_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GtkCellView), + 0, + (GInstanceInitFunc) gtk_cell_view_init + }; + + cell_view_type = g_type_register_static (GTK_TYPE_WIDGET, "GtkCellView", + &cell_view_info, 0); + } + + return cell_view_type; +} + +static void +gtk_cell_view_class_init (GtkCellViewClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + gobject_class->get_property = gtk_cell_view_get_property; + gobject_class->set_property = gtk_cell_view_set_property; + gobject_class->finalize = gtk_cell_view_finalize; + + widget_class->expose_event = gtk_cell_view_expose; + widget_class->size_allocate = gtk_cell_view_size_allocate; + widget_class->size_request = gtk_cell_view_size_request; + widget_class->style_set = gtk_cell_view_style_set; + + /* properties */ + g_object_class_install_property (gobject_class, + PROP_BACKGROUND, + g_param_spec_string ("background", + _("Background color name"), + _("Background color as a string"), + NULL, + G_PARAM_WRITABLE)); + g_object_class_install_property (gobject_class, + PROP_BACKGROUND_GDK, + g_param_spec_boxed ("background_gdk", + _("Background color"), + _("Background color as a GdkColor"), + GDK_TYPE_COLOR, + G_PARAM_READABLE | G_PARAM_WRITABLE)); + +#define ADD_SET_PROP(propname, propval, nick, blurb) g_object_class_install_property (gobject_class, propval, g_param_spec_boolean (propname, nick, blurb, FALSE, G_PARAM_READABLE | G_PARAM_WRITABLE)) + + ADD_SET_PROP ("background_set", PROP_BACKGROUND_SET, + _("Background set"), + _("Whether this tag affects the background color")); + + g_type_class_add_private (gobject_class, sizeof (GtkCellViewPrivate)); +} + +static void +gtk_cell_view_get_property (GObject *object, + guint param_id, + GValue *value, + GParamSpec *pspec) +{ + GtkCellView *view = GTK_CELL_VIEW (object); + + switch (param_id) + { + case PROP_BACKGROUND_GDK: + { + GdkColor color; + + color = view->priv->background; + + g_value_set_boxed (value, &color); + } + break; + case PROP_BACKGROUND_SET: + g_value_set_boolean (value, view->priv->background_set); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +gtk_cell_view_set_property (GObject *object, + guint param_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkCellView *view = GTK_CELL_VIEW (object); + + switch (param_id) + { + case PROP_BACKGROUND: + { + GdkColor color; + + if (!g_value_get_string (value)) + gtk_cell_view_set_background_color (view, NULL); + else if (gdk_color_parse (g_value_get_string (value), &color)) + gtk_cell_view_set_background_color (view, &color); + else + g_warning ("Don't know color `%s'", g_value_get_string (value)); + + g_object_notify (object, "background_gdk"); + } + break; + case PROP_BACKGROUND_GDK: + gtk_cell_view_set_background_color (view, g_value_get_boxed (value)); + break; + case PROP_BACKGROUND_SET: + view->priv->background_set = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); + break; + } +} + +static void +gtk_cell_view_init (GtkCellView *cellview) +{ + GTK_WIDGET_SET_FLAGS (cellview, GTK_NO_WINDOW); + + cellview->priv = GTK_CELL_VIEW_GET_PRIVATE (cellview); +} + +static void +gtk_cell_view_style_set (GtkWidget *widget, + GtkStyle *previous_style) +{ + if (previous_style && GTK_WIDGET_REALIZED (widget)) + gdk_window_set_background (widget->window, + &widget->style->base[GTK_WIDGET_STATE (widget)]); +} + +static void +gtk_cell_view_finalize (GObject *object) +{ + GtkCellView *cellview = GTK_CELL_VIEW (object); + + if (cellview->priv->cell_list) + { + g_list_foreach (cellview->priv->cell_list, (GFunc)g_free, NULL); + g_list_free (cellview->priv->cell_list); + } + cellview->priv->cell_list = NULL; + + if (G_OBJECT_CLASS (parent_class)->finalize) + (* G_OBJECT_CLASS (parent_class)->finalize) (object); +} + +static void +gtk_cell_view_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ + GList *i; + gboolean first_cell = TRUE; + GtkCellView *cellview; + + cellview = GTK_CELL_VIEW (widget); + + requisition->width = 0; + requisition->height = 0; + + if (cellview->priv->displayed_row) + gtk_cell_view_set_cell_data (cellview); + + for (i = cellview->priv->cell_list; i; i = i->next) + { + gint width, height; + GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)i->data; + + if (!info->cell->visible) + continue; + + if (!first_cell) + requisition->width += cellview->priv->spacing; + + gtk_cell_renderer_get_size (info->cell, widget, NULL, NULL, NULL, + &width, &height); + + info->requested_width = width; + requisition->width += width; + requisition->height = MAX (requisition->height, height); + + first_cell = FALSE; + } +} + +static void +gtk_cell_view_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + GList *i; + gint expand_cell_count = 0; + gint full_requested_width = 0; + gint extra_space; + GtkCellView *cellview; + + widget->allocation = *allocation; + + cellview = GTK_CELL_VIEW (widget); + + /* checking how much extra space we have */ + for (i = cellview->priv->cell_list; i; i = i->next) + { + GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)i->data; + + if (!info->cell->visible) + continue; + + if (info->expand) + expand_cell_count++; + + full_requested_width += info->requested_width; + } + + extra_space = widget->allocation.width - full_requested_width; + if (extra_space < 0) + extra_space = 0; + else if (extra_space > 0 && expand_cell_count > 0) + extra_space /= expand_cell_count; + + /* iterate list for PACK_START cells */ + for (i = cellview->priv->cell_list; i; i = i->next) + { + GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)i->data; + + if (info->pack == GTK_PACK_END) + continue; + + if (!info->cell->visible) + continue; + + info->real_width = info->requested_width + (info->expand?extra_space:0); + } + + /* iterate list for PACK_END cells */ + for (i = cellview->priv->cell_list; i; i = i->next) + { + GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)i->data; + + if (info->pack == GTK_PACK_START) + continue; + + if (!info->cell->visible) + continue; + + info->real_width = info->requested_width + (info->expand?extra_space:0); + } +} + +static gboolean +gtk_cell_view_expose (GtkWidget *widget, + GdkEventExpose *event) +{ + GList *i; + GtkCellView *cellview; + GdkRectangle area; + + cellview = GTK_CELL_VIEW (widget); + + if (! GTK_WIDGET_DRAWABLE (widget)) + return FALSE; + + /* "blank" background */ + if (cellview->priv->background_set) + { + GdkGC *gc; + + gc = gdk_gc_new (GTK_WIDGET (cellview)->window); + gdk_gc_set_rgb_fg_color (gc, &cellview->priv->background); + + gdk_draw_rectangle (GTK_WIDGET (cellview)->window, + gc, + TRUE, + + /*0, 0,*/ + widget->allocation.x, + widget->allocation.y, + + widget->allocation.width, + widget->allocation.height); + + g_object_unref (G_OBJECT (gc)); + } + + /* set cell data (if applicable) */ + if (cellview->priv->displayed_row) + gtk_cell_view_set_cell_data (cellview); + + /* render cells */ + area = widget->allocation; + + /* we draw on our very own window, initialize x and y to zero */ + area.x = widget->allocation.x; + area.y = widget->allocation.y; + + /* PACK_START */ + for (i = cellview->priv->cell_list; i; i = i->next) + { + GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)i->data; + + if (info->pack == GTK_PACK_END) + continue; + + if (!info->cell->visible) + continue; + + area.width = info->real_width; + + gtk_cell_renderer_render (info->cell, + event->window, + widget, + /* FIXME! */ + &area, &area, &event->area, 0); + + area.x += info->real_width; + } + + /* PACK_END */ + for (i = cellview->priv->cell_list; i; i = i->next) + { + GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)i->data; + + if (info->pack == GTK_PACK_START) + continue; + + if (!info->cell->visible) + continue; + + area.width = info->real_width; + + gtk_cell_renderer_render (info->cell, + widget->window, + widget, + /* FIXME ! */ + &area, &area, &event->area, 0); + area.x += info->real_width; + } + + return FALSE; +} + +GtkWidget * +gtk_cell_view_new (void) +{ + GtkCellView *cellview; + + cellview = GTK_CELL_VIEW (g_object_new (gtk_cell_view_get_type (), NULL)); + + return GTK_WIDGET (cellview); +} + +GtkWidget * +gtk_cell_view_new_with_text (const gchar *text) +{ + GtkCellView *cellview; + GtkCellRenderer *renderer; + GValue value = {0, }; + + cellview = GTK_CELL_VIEW (gtk_cell_view_new ()); + + renderer = gtk_cell_renderer_text_new (); + gtk_cell_view_pack_start (cellview, renderer, TRUE); + + g_value_init (&value, G_TYPE_STRING); + g_value_set_string (&value, text); + gtk_cell_view_set_values (cellview, renderer, "text", &value, NULL); + g_value_unset (&value); + + return GTK_WIDGET (cellview); +} + +GtkWidget * +gtk_cell_view_new_with_markup (const gchar *markup) +{ + GtkCellView *cellview; + GtkCellRenderer *renderer; + GValue value = {0, }; + + cellview = GTK_CELL_VIEW (gtk_cell_view_new ()); + + renderer = gtk_cell_renderer_text_new (); + gtk_cell_view_pack_start (cellview, renderer, TRUE); + + g_value_init (&value, G_TYPE_STRING); + g_value_set_string (&value, markup); + gtk_cell_view_set_values (cellview, renderer, "markup", &value, NULL); + g_value_unset (&value); + + return GTK_WIDGET (cellview); +} + +GtkWidget * +gtk_cell_view_new_with_pixbuf (GdkPixbuf *pixbuf) +{ + GtkCellView *cellview; + GtkCellRenderer *renderer; + GValue value = {0, }; + + cellview = GTK_CELL_VIEW (gtk_cell_view_new ()); + + renderer = gtk_cell_renderer_pixbuf_new (); + gtk_cell_view_pack_start (cellview, renderer, TRUE); + + g_value_init (&value, GDK_TYPE_PIXBUF); + g_value_set_object (&value, pixbuf); + gtk_cell_view_set_values (cellview, renderer, "pixbuf", &value, NULL); + g_value_unset (&value); + + return GTK_WIDGET (cellview); +} + +static GtkCellViewCellInfo * +gtk_cell_view_get_cell_info (GtkCellView *cellview, + GtkCellRenderer *renderer) +{ + GList *i; + + for (i = cellview->priv->cell_list; i; i = i->next) + { + GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)i->data; + + if (info->cell == renderer) + return info; + } + + return NULL; +} + +void +gtk_cell_view_pack_start (GtkCellView *cellview, + GtkCellRenderer *renderer, + gboolean expand) +{ + GtkCellViewCellInfo *info; + + g_return_if_fail (GTK_IS_CELL_VIEW (cellview)); + g_return_if_fail (GTK_IS_CELL_RENDERER (renderer)); + g_return_if_fail (!gtk_cell_view_get_cell_info (cellview, renderer)); + + g_object_ref (G_OBJECT (renderer)); + gtk_object_sink (GTK_OBJECT (renderer)); + + info = g_new0 (GtkCellViewCellInfo, 1); + info->cell = renderer; + info->expand = expand ? TRUE : FALSE; + info->pack = GTK_PACK_START; + + cellview->priv->cell_list = g_list_append (cellview->priv->cell_list, info); +} + +void +gtk_cell_view_pack_end (GtkCellView *cellview, + GtkCellRenderer *renderer, + gboolean expand) +{ + GtkCellViewCellInfo *info; + + g_return_if_fail (GTK_IS_CELL_VIEW (cellview)); + g_return_if_fail (GTK_IS_CELL_RENDERER (renderer)); + g_return_if_fail (!gtk_cell_view_get_cell_info (cellview, renderer)); + + g_object_ref (G_OBJECT (renderer)); + gtk_object_sink (GTK_OBJECT (renderer)); + + info = g_new0 (GtkCellViewCellInfo, 1); + info->cell = renderer; + info->expand = expand ? TRUE : FALSE; + info->pack = GTK_PACK_END; + + cellview->priv->cell_list = g_list_append (cellview->priv->cell_list, info); +} + +void +gtk_cell_view_add_attribute (GtkCellView *cellview, + GtkCellRenderer *renderer, + const gchar *attribute, + gint column) +{ + GtkCellViewCellInfo *info; + + g_return_if_fail (GTK_IS_CELL_VIEW (cellview)); + info = gtk_cell_view_get_cell_info (cellview, renderer); + g_return_if_fail (info != NULL); + + info->attributes = g_slist_prepend (info->attributes, + GINT_TO_POINTER (column)); + info->attributes = g_slist_prepend (info->attributes, + g_strdup (attribute)); +} + +void +gtk_cell_view_clear (GtkCellView *cellview) +{ + GList *i; + + for (i = cellview->priv->cell_list; i; i = i->next) + { + GtkCellViewCellInfo *info = (GtkCellViewCellInfo *)i->data; + + gtk_cell_view_clear_attributes (cellview, info->cell); + g_object_unref (G_OBJECT (info->cell)); + g_free (info); + } + + g_list_free (cellview->priv->cell_list); + cellview->priv->cell_list = NULL; +} + +void +gtk_cell_view_clear_attributes (GtkCellView *cellview, + GtkCellRenderer *renderer) +{ + GtkCellViewCellInfo *info; + GSList *list; + + g_return_if_fail (GTK_IS_CELL_VIEW (cellview)); + g_return_if_fail (GTK_IS_CELL_RENDERER (renderer)); + + info = gtk_cell_view_get_cell_info (cellview, renderer); + + list = info->attributes; + + while (list && list->next) + { + g_free (list->data); + list = list->next->next; + } + g_slist_free (list); + + info->attributes = NULL; +} + +static void +gtk_cell_view_set_attributesv (GtkCellView *cellview, + GtkCellRenderer *renderer, + va_list args) +{ + gchar *attribute; + gint column; + + attribute = va_arg (args, gchar *); + + gtk_cell_view_clear_attributes (cellview, renderer); + + while (attribute) + { + column = va_arg (args, gint); + gtk_cell_view_add_attribute (cellview, renderer, attribute, column); + attribute = va_arg (args, gchar *); + } +} + +void +gtk_cell_view_set_attributes (GtkCellView *cellview, + GtkCellRenderer *renderer, + ...) +{ + va_list args; + + g_return_if_fail (GTK_IS_CELL_VIEW (cellview)); + g_return_if_fail (GTK_IS_CELL_RENDERER (renderer)); + g_return_if_fail (gtk_cell_view_get_cell_info (cellview, renderer)); + + va_start (args, renderer); + gtk_cell_view_set_attributesv (cellview, renderer, args); + va_end (args); +} + +void +gtk_cell_view_set_value (GtkCellView *cellview, + GtkCellRenderer *renderer, + gchar *property, + GValue *value) +{ + g_return_if_fail (GTK_IS_CELL_VIEW (cellview)); + g_return_if_fail (GTK_IS_CELL_RENDERER (renderer)); + + g_object_set_property (G_OBJECT (renderer), property, value); + + /* force redraw */ + gtk_widget_queue_draw (GTK_WIDGET (cellview)); +} + +static void +gtk_cell_view_set_valuesv (GtkCellView *cellview, + GtkCellRenderer *renderer, + va_list args) +{ + gchar *attribute; + GValue *value; + + attribute = va_arg (args, gchar *); + + while (attribute) + { + value = va_arg (args, GValue *); + gtk_cell_view_set_value (cellview, renderer, attribute, value); + attribute = va_arg (args, gchar *); + } +} + +void +gtk_cell_view_set_values (GtkCellView *cellview, + GtkCellRenderer *renderer, + ...) +{ + va_list args; + + g_return_if_fail (GTK_IS_CELL_VIEW (cellview)); + g_return_if_fail (GTK_IS_CELL_RENDERER (renderer)); + g_return_if_fail (gtk_cell_view_get_cell_info (cellview, renderer)); + + va_start (args, renderer); + gtk_cell_view_set_valuesv (cellview, renderer, args); + va_end (args); +} + +void +gtk_cell_view_set_model (GtkCellView *cellview, + GtkTreeModel *model) +{ + g_return_if_fail (GTK_IS_CELL_VIEW (cellview)); + g_return_if_fail (GTK_IS_TREE_MODEL (model)); + + if (cellview->priv->model) + { + if (cellview->priv->displayed_row) + gtk_tree_row_reference_free (cellview->priv->displayed_row); + cellview->priv->displayed_row = NULL; + + g_object_unref (G_OBJECT (cellview->priv->model)); + cellview->priv->model = NULL; + } + + cellview->priv->model = model; + + if (cellview->priv->model) + g_object_ref (G_OBJECT (cellview->priv->model)); +} + +static void +gtk_cell_view_set_cell_data (GtkCellView *cellview) +{ + GList *i; + GtkTreeIter iter; + GtkTreePath *path; + + g_return_if_fail (cellview->priv->displayed_row != NULL); + + path = gtk_tree_row_reference_get_path (cellview->priv->displayed_row); + gtk_tree_model_get_iter (cellview->priv->model, &iter, path); + gtk_tree_path_free (path); + + for (i = cellview->priv->cell_list; i; i = i->next) + { + GSList *j; + GtkCellViewCellInfo *info = i->data; + + for (j = info->attributes; j && j->next; j = j->next->next) + { + gchar *property = j->data; + gint column = GPOINTER_TO_INT (j->next->data); + GValue value = {0, }; + + gtk_tree_model_get_value (cellview->priv->model, &iter, + column, &value); + g_object_set_property (G_OBJECT (info->cell), + property, &value); + g_value_unset (&value); + } + } +} + +void +gtk_cell_view_set_displayed_row (GtkCellView *cellview, + GtkTreePath *path) +{ + g_return_if_fail (GTK_IS_CELL_VIEW (cellview)); + g_return_if_fail (GTK_IS_TREE_MODEL (cellview->priv->model)); + g_return_if_fail (path != NULL); + + if (cellview->priv->displayed_row) + gtk_tree_row_reference_free (cellview->priv->displayed_row); + + cellview->priv->displayed_row = + gtk_tree_row_reference_new (cellview->priv->model, path); + + /* force redraw */ + gtk_widget_queue_draw (GTK_WIDGET (cellview)); +} + +GtkTreePath * +gtk_cell_view_get_displayed_row (GtkCellView *cellview) +{ + g_return_val_if_fail (GTK_IS_CELL_VIEW (cellview), NULL); + + if (!cellview->priv->displayed_row) + return NULL; + + return gtk_tree_row_reference_get_path (cellview->priv->displayed_row); +} + +void +gtk_cell_view_set_background_color (GtkCellView *view, + GdkColor *color) +{ + g_return_if_fail (GTK_IS_CELL_VIEW (view)); + + if (color) + { + if (!view->priv->background_set) + { + view->priv->background_set = TRUE; + g_object_notify (G_OBJECT (view), "background_set"); + } + + view->priv->background = *color; + } + else + { + if (view->priv->background_set) + { + view->priv->background_set = FALSE; + g_object_notify (G_OBJECT (view), "background_set"); + } + } +} diff --git a/gtk/gtkcellview.h b/gtk/gtkcellview.h new file mode 100644 index 0000000000..523e921cc4 --- /dev/null +++ b/gtk/gtkcellview.h @@ -0,0 +1,96 @@ +/* gtkcellview.h + * Copyright (C) 2002, 2003 Kristian Rietveld + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GTK_CELL_VIEW_H__ +#define __GTK_CELL_VIEW_H__ + +#include +#include +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_CELL_VIEW (gtk_cell_view_get_type ()) +#define GTK_CELL_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_VIEW, GtkCellView)) +#define GTK_CELL_VIEW_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST ((vtable), GTK_TYPE_CELL_VIEW, GtkCellViewClass)) +#define GTK_IS_CELL_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_VIEW)) +#define GTK_IS_CELL_VIEW_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), GTK_TYPE_CELL_VIEW)) +#define GTK_CELL_VIEW_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), GTK_TYPE_CELL_VIEW, GtkCellViewClass)) +#define GTK_CELL_VIEW_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_CELL_VIEW, GtkCellViewPrivate)) + +typedef struct _GtkCellView GtkCellView; +typedef struct _GtkCellViewClass GtkCellViewClass; +typedef struct _GtkCellViewPrivate GtkCellViewPrivate; + +struct _GtkCellView +{ + GtkWidget parent_instance; + + /*< private >*/ + GtkCellViewPrivate *priv; +}; + +struct _GtkCellViewClass +{ + GtkWidgetClass parent_class; +}; + +GType gtk_cell_view_get_type (void); +GtkWidget *gtk_cell_view_new (void); +GtkWidget *gtk_cell_view_new_with_text (const gchar *text); +GtkWidget *gtk_cell_view_new_with_markup (const gchar *markup); +GtkWidget *gtk_cell_view_new_with_pixbuf (GdkPixbuf *pixbuf); + + +void gtk_cell_view_pack_start (GtkCellView *cell_view, + GtkCellRenderer *renderer, + gboolean expand); +void gtk_cell_view_pack_end (GtkCellView *cell_view, + GtkCellRenderer *renderer, + gboolean expand); +void gtk_cell_view_clear (GtkCellView *cell_view); +void gtk_cell_view_add_attribute (GtkCellView *cell_view, + GtkCellRenderer *renderer, + const gchar *attribute, + gint column); +void gtk_cell_view_clear_attributes (GtkCellView *cell_view, + GtkCellRenderer *attribute); +void gtk_cell_view_set_attributes (GtkCellView *cell_view, + GtkCellRenderer *renderer, + ...); +void gtk_cell_view_set_value (GtkCellView *cell_view, + GtkCellRenderer *renderer, + gchar *property, + GValue *value); +void gtk_cell_view_set_values (GtkCellView *cell_view, + GtkCellRenderer *renderer, + ...); + +void gtk_cell_view_set_model (GtkCellView *cell_view, + GtkTreeModel *model); +void gtk_cell_view_set_displayed_row (GtkCellView *cell_view, + GtkTreePath *path); +GtkTreePath *gtk_cell_view_get_displayed_row (GtkCellView *cell_view); + +void gtk_cell_view_set_background_color (GtkCellView *cell_view, + GdkColor *color); + +G_END_DECLS + +#endif /* __GTK_CELL_VIEW_H__ */ diff --git a/gtk/gtkcellviewmenuitem.c b/gtk/gtkcellviewmenuitem.c new file mode 100644 index 0000000000..d69435f7f3 --- /dev/null +++ b/gtk/gtkcellviewmenuitem.c @@ -0,0 +1,146 @@ +/* gtkcellviewmenuitem.c + * Copyright (C) 2003 Kristian Rietveld + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include + +struct _GtkCellViewMenuItemPrivate +{ + GtkWidget *cell_view; +}; + +static void gtk_cell_view_menu_item_init (GtkCellViewMenuItem *item); +static void gtk_cell_view_menu_item_class_init (GtkCellViewMenuItemClass *klass); + + +GType +gtk_cell_view_menu_item_get_type (void) +{ + static GType cell_view_menu_item_type = 0; + + if (!cell_view_menu_item_type) + { + static const GTypeInfo cell_view_menu_item_info = + { + sizeof (GtkCellViewMenuItemClass), + NULL, + NULL, + (GClassInitFunc) gtk_cell_view_menu_item_class_init, + NULL, + NULL, + sizeof (GtkCellViewMenuItem), + 0, + (GInstanceInitFunc) gtk_cell_view_menu_item_init + }; + + cell_view_menu_item_type = + g_type_register_static (GTK_TYPE_MENU_ITEM, "GtkCellViewMenuItem", + &cell_view_menu_item_info, 0); + } + + return cell_view_menu_item_type; +} + +static void +gtk_cell_view_menu_item_class_init (GtkCellViewMenuItemClass *klass) +{ + g_type_class_add_private ((GObjectClass *)klass, + sizeof (GtkCellViewMenuItemPrivate)); +} + +static void +gtk_cell_view_menu_item_init (GtkCellViewMenuItem *item) +{ + item->priv = GTK_CELL_VIEW_MENU_ITEM_GET_PRIVATE (item); +} + +GtkWidget * +gtk_cell_view_menu_item_new (void) +{ + GtkCellViewMenuItem *item; + + item = g_object_new (GTK_TYPE_CELL_VIEW_MENU_ITEM, NULL); + + item->priv->cell_view = gtk_cell_view_new (); + gtk_container_add (GTK_CONTAINER (item), item->priv->cell_view); + gtk_widget_show (item->priv->cell_view); + + return GTK_WIDGET (item); +} + +GtkWidget * +gtk_cell_view_menu_item_new_with_pixbuf (GdkPixbuf *pixbuf) +{ + GtkCellViewMenuItem *item; + + item = g_object_new (GTK_TYPE_CELL_VIEW_MENU_ITEM, NULL); + + item->priv->cell_view = gtk_cell_view_new_with_pixbuf (pixbuf); + gtk_container_add (GTK_CONTAINER (item), item->priv->cell_view); + gtk_widget_show (item->priv->cell_view); + + return GTK_WIDGET (item); +} + +GtkWidget * +gtk_cell_view_menu_item_new_with_text (const gchar *text) +{ + GtkCellViewMenuItem *item; + + item = g_object_new (GTK_TYPE_CELL_VIEW_MENU_ITEM, NULL); + + item->priv->cell_view = gtk_cell_view_new_with_text (text); + gtk_container_add (GTK_CONTAINER (item), item->priv->cell_view); + gtk_widget_show (item->priv->cell_view); + + return GTK_WIDGET (item); +} + +GtkWidget * +gtk_cell_view_menu_item_new_with_markup (const gchar *markup) +{ + GtkCellViewMenuItem *item; + + item = g_object_new (GTK_TYPE_CELL_VIEW_MENU_ITEM, NULL); + + item->priv->cell_view = gtk_cell_view_new_with_markup (markup); + gtk_container_add (GTK_CONTAINER (item), item->priv->cell_view); + gtk_widget_show (item->priv->cell_view); + + return GTK_WIDGET (item); +} + +GtkWidget * +gtk_cell_view_menu_item_new_from_model (GtkTreeModel *model, + GtkTreePath *path) +{ + GtkCellViewMenuItem *item; + + item = g_object_new (GTK_TYPE_CELL_VIEW_MENU_ITEM, NULL); + + item->priv->cell_view = gtk_cell_view_new (); + gtk_container_add (GTK_CONTAINER (item), item->priv->cell_view); + + gtk_cell_view_set_model (GTK_CELL_VIEW (item->priv->cell_view), model); + gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (item->priv->cell_view), path); + + gtk_widget_show (item->priv->cell_view); + + return GTK_WIDGET (item); +} diff --git a/gtk/gtkcellviewmenuitem.h b/gtk/gtkcellviewmenuitem.h new file mode 100644 index 0000000000..539fabc1da --- /dev/null +++ b/gtk/gtkcellviewmenuitem.h @@ -0,0 +1,68 @@ +/* gtkcellviewmenuitem.h + * Copyright (C) 2003 Kristian Rietveld + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GTK_CELL_VIEW_MENU_ITEM_H__ +#define __GTK_CELL_VIEW_MENU_ITEM_H__ + +#include +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_CELL_VIEW_MENU_ITEM (gtk_cell_view_menu_item_get_type ()) +#define GTK_CELL_VIEW_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_VIEW_MENU_ITEM, GtkCellViewMenuItem)) +#define GTK_CELL_VIEW_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_CELL_VIEW_MENU_ITEM, GtkCellViewMenuItemClass)) +#define GTK_IS_CELL_VIEW_MENU_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_VIEW_MENU_ITEM)) +#define GTK_IS_CELL_VIEW_MENU_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_CELL_VIEW_MENU_ITEM)) +#define GTK_CELL_VIEW_MENU_ITEM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CELL_VIEW_MENU_ITEM, GtkCellViewMenuItemClass)) +#define GTK_CELL_VIEW_MENU_ITEM_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_CELL_VIEW_MENU_ITEM, GtkCellViewMenuItemPrivate)) + + +typedef struct _GtkCellViewMenuItem GtkCellViewMenuItem; +typedef struct _GtkCellViewMenuItemClass GtkCellViewMenuItemClass; +typedef struct _GtkCellViewMenuItemPrivate GtkCellViewMenuItemPrivate; + +struct _GtkCellViewMenuItem +{ + GtkMenuItem parent_instance; + + /*< private >*/ + GtkCellViewMenuItemPrivate *priv; +}; + +struct _GtkCellViewMenuItemClass +{ + GtkMenuItemClass parent_class; +}; + + +GType gtk_cell_view_menu_item_get_type (void); +GtkWidget *gtk_cell_view_menu_item_new (void); + +GtkWidget *gtk_cell_view_menu_item_new_with_pixbuf (GdkPixbuf *pixbuf); +GtkWidget *gtk_cell_view_menu_item_new_with_text (const gchar *text); +GtkWidget *gtk_cell_view_menu_item_new_with_markup (const gchar *markup); + +GtkWidget *gtk_cell_view_menu_item_new_from_model (GtkTreeModel *model, + GtkTreePath *path); + + +G_END_DECLS + +#endif /* __GTK_CELL_VIEW_MENU_ITEM_H__ */ diff --git a/gtk/gtkcombobox.c b/gtk/gtkcombobox.c new file mode 100644 index 0000000000..9bbf551155 --- /dev/null +++ b/gtk/gtkcombobox.c @@ -0,0 +1,2271 @@ +/* gtkcombobox.c + * Copyright (C) 2002, 2003 Kristian Rietveld + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include "gtkmarshalers.h" +#include "gtkintl.h" + + +/* WELCOME, to THE house of evil code */ + + +typedef struct _ComboCellInfo ComboCellInfo; +struct _ComboCellInfo +{ + GtkCellRenderer *cell; + GSList *attributes; + + guint expand : 1; + guint pack : 1; +}; + +struct _GtkComboBoxPrivate +{ + GtkTreeModel *model; + + gint col_column; + gint row_column; + + gint wrap_width; + + gint active_item; + + GtkWidget *tree_view; + GtkTreeViewColumn *column; + + GtkWidget *cell_view; + GtkWidget *measurer; + + GtkWidget *hbox; + GtkWidget *cell_view_frame; + + GtkWidget *button; + GtkWidget *arrow; + GtkWidget *separator; + + GtkWidget *popup_widget; + GtkWidget *popup_window; + GtkWidget *popup_frame; + + guint inserted_id; + guint deleted_id; + + gint width; + GSList *cells; + + guint changed_id; + + guint popup_in_progress : 1; +}; + +enum { + CHANGED, + LAST_SIGNAL +}; + +enum { + PROP_0, + PROP_MODEL, + PROP_WRAP_WIDTH, + PROP_ROW_SPAN_COLUMN, + PROP_COLUMN_SPAN_COLUMN, + PROP_ACTIVE +}; + +static GtkBinClass *parent_class = NULL; +static guint combo_box_signals[LAST_SIGNAL] = {0,}; + +#define BONUS_PADDING 4 + + +/* common */ +static void gtk_combo_box_class_init (GtkComboBoxClass *klass); +static void gtk_combo_box_init (GtkComboBox *combo_box); + +static void gtk_combo_box_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *spec); +static void gtk_combo_box_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *spec); + +static void gtk_combo_box_set_model (GtkComboBox *combo_box, + GtkTreeModel *model); + +static void gtk_combo_box_style_set (GtkWidget *widget, + GtkStyle *previous_style, + gpointer data); +static void gtk_combo_box_button_toggled (GtkWidget *widget, + gpointer data); +static void gtk_combo_box_add (GtkContainer *container, + GtkWidget *widget); + +static ComboCellInfo *gtk_combo_box_get_cell_info (GtkComboBox *combo_box, + GtkCellRenderer *cell); + +static void gtk_combo_box_menu_show (GtkWidget *menu, + gpointer user_data); +static void gtk_combo_box_menu_hide (GtkWidget *menu, + gpointer user_data); + +static void gtk_combo_box_set_popup_widget (GtkComboBox *combo_box, + GtkWidget *popup); +static void gtk_combo_box_menu_position (GtkMenu *menu, + gint *x, + gint *y, + gint *push_in, + gpointer user_data); +static void gtk_combo_box_popup (GtkComboBox *combo_box); +static void gtk_combo_box_popdown (GtkComboBox *combo_box); + +static gint gtk_combo_box_calc_requested_width (GtkComboBox *combo_box, + GtkTreePath *path); +static void gtk_combo_box_remeasure (GtkComboBox *combo_box); + +static void gtk_combo_box_size_request (GtkWidget *widget, + GtkRequisition *requisition); +static void gtk_combo_box_size_allocate (GtkWidget *widget, + GtkAllocation *allocation); +static void gtk_combo_box_forall (GtkContainer *container, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data); +static gboolean gtk_combo_box_expose_event (GtkWidget *widget, + GdkEventExpose *event); + +/* list */ +static void gtk_combo_box_list_setup (GtkComboBox *combo_box); +static void gtk_combo_box_list_destroy (GtkComboBox *combo_box); + +static gboolean gtk_combo_box_list_button_released (GtkWidget *widget, + GdkEventButton *event, + gpointer data); +static gboolean gtk_combo_box_list_key_press (GtkWidget *widget, + GdkEventKey *event, + gpointer data); +static gboolean gtk_combo_box_list_button_pressed (GtkWidget *widget, + GdkEventButton *event, + gpointer data); + +static void gtk_combo_box_list_row_changed (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data); + +/* menu */ +static void gtk_combo_box_menu_setup (GtkComboBox *combo_box, + gboolean add_childs); +static void gtk_combo_box_menu_destroy (GtkComboBox *combo_box); + +static void gtk_combo_box_item_get_size (GtkComboBox *combo_box, + gint index, + gint *cols, + gint *rows); +static void gtk_combo_box_relayout_item (GtkComboBox *combo_box, + gint index); +static void gtk_combo_box_relayout (GtkComboBox *combo_box); + +static gboolean gtk_combo_box_menu_button_press (GtkWidget *widget, + GdkEventButton *event, + gpointer user_data); +static void gtk_combo_box_menu_item_activate (GtkWidget *item, + gpointer user_data); +static void gtk_combo_box_menu_row_inserted (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer user_data); +static void gtk_combo_box_menu_row_deleted (GtkTreeModel *model, + GtkTreePath *path, + gpointer user_data); +static void gtk_combo_box_menu_row_changed (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data); + + +GType +gtk_combo_box_get_type (void) +{ + static GType combo_box_type = 0; + + if (!combo_box_type) + { + static const GTypeInfo combo_box_info = + { + sizeof (GtkComboBoxClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) gtk_combo_box_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GtkComboBox), + 0, + (GInstanceInitFunc) gtk_combo_box_init + }; + + combo_box_type = g_type_register_static (GTK_TYPE_BIN, + "GtkComboBox", + &combo_box_info, + 0); + } + + return combo_box_type; +} + +/* common */ +static void +gtk_combo_box_class_init (GtkComboBoxClass *klass) +{ + GObjectClass *object_class; + GtkBindingSet *binding_set; + GtkContainerClass *container_class; + GtkWidgetClass *widget_class; + + binding_set = gtk_binding_set_by_class (klass); + + container_class = (GtkContainerClass *)klass; + container_class->forall = gtk_combo_box_forall; + container_class->add = gtk_combo_box_add; + + widget_class = (GtkWidgetClass *)klass; + widget_class->size_allocate = gtk_combo_box_size_allocate; + widget_class->size_request = gtk_combo_box_size_request; + widget_class->expose_event = gtk_combo_box_expose_event; + + object_class = (GObjectClass *)klass; + object_class->set_property = gtk_combo_box_set_property; + object_class->get_property = gtk_combo_box_get_property; + + parent_class = g_type_class_peek_parent (klass); + + /* signals */ + combo_box_signals[CHANGED] = + g_signal_new ("changed", + G_OBJECT_CLASS_TYPE (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkComboBoxClass, changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + /* properties */ + g_object_class_install_property (object_class, + PROP_MODEL, + g_param_spec_object ("model", + _("ComboBox model"), + _("The model for the combo box"), + GTK_TYPE_TREE_MODEL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + g_object_class_install_property (object_class, + PROP_WRAP_WIDTH, + g_param_spec_int ("wrap_width", + _("Wrap width"), + _("Wrap width for layouting the items in a grid"), + 0, + G_MAXINT, + 0, + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_ROW_SPAN_COLUMN, + g_param_spec_int ("row_span_column", + _("Row span column"), + _("TreeModel column containing the row span values"), + 0, + G_MAXINT, + 0, + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_COLUMN_SPAN_COLUMN, + g_param_spec_int ("column_span_column", + _("Column span column"), + _("TreeModel column containing the column span values"), + 0, + G_MAXINT, + 0, + G_PARAM_READWRITE)); + + g_object_class_install_property (object_class, + PROP_ACTIVE, + g_param_spec_int ("active", + _("Active item"), + _("The item which is currently active"), + 0, + G_MAXINT, + 0, + G_PARAM_READWRITE)); + + gtk_widget_class_install_style_property (widget_class, + g_param_spec_boolean ("appearance", + _("ComboBox appareance"), + _("ComboBox appearance, where TRUE means Windows-style."), + FALSE, + G_PARAM_READWRITE)); + + g_type_class_add_private (object_class, sizeof (GtkComboBoxPrivate)); +} + +static void +gtk_combo_box_init (GtkComboBox *combo_box) +{ + combo_box->priv = GTK_COMBO_BOX_GET_PRIVATE (combo_box); + + g_signal_connect (combo_box, "style_set", + G_CALLBACK (gtk_combo_box_style_set), NULL); + + combo_box->priv->cell_view = gtk_cell_view_new (); + gtk_container_add (GTK_CONTAINER (combo_box), combo_box->priv->cell_view); + + combo_box->priv->measurer = gtk_cell_view_new (); + + combo_box->priv->width = 0; + combo_box->priv->wrap_width = 0; + + combo_box->priv->active_item = -1; + combo_box->priv->col_column = -1; + combo_box->priv->row_column = -1; +} + +static void +gtk_combo_box_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkComboBox *combo_box = GTK_COMBO_BOX (object); + + switch (prop_id) + { + case PROP_MODEL: + gtk_combo_box_set_model (combo_box, g_value_get_object (value)); + break; + + case PROP_WRAP_WIDTH: + gtk_combo_box_set_wrap_width (combo_box, g_value_get_int (value)); + break; + + case PROP_ROW_SPAN_COLUMN: + gtk_combo_box_set_row_span_column (combo_box, g_value_get_int (value)); + break; + + case PROP_COLUMN_SPAN_COLUMN: + gtk_combo_box_set_column_span_column (combo_box, g_value_get_int (value)); + break; + + case PROP_ACTIVE: + gtk_combo_box_set_active (combo_box, g_value_get_int (value)); + break; + + default: + break; + } +} + +static void +gtk_combo_box_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkComboBox *combo_box = GTK_COMBO_BOX (object); + + switch (prop_id) + { + case PROP_MODEL: + g_value_set_object (value, combo_box->priv->model); + break; + + case PROP_WRAP_WIDTH: + g_value_set_int (value, combo_box->priv->wrap_width); + break; + + case PROP_ROW_SPAN_COLUMN: + g_value_set_int (value, combo_box->priv->row_column); + break; + + case PROP_COLUMN_SPAN_COLUMN: + g_value_set_int (value, combo_box->priv->col_column); + break; + + case PROP_ACTIVE: + g_value_set_int (value, gtk_combo_box_get_active (combo_box)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_combo_box_set_model (GtkComboBox *combo_box, + GtkTreeModel *model) +{ + if (combo_box->priv->model) + return; + + combo_box->priv->model = model; + g_object_ref (G_OBJECT (combo_box->priv->model)); + gtk_cell_view_set_model (GTK_CELL_VIEW (combo_box->priv->cell_view), + combo_box->priv->model); + gtk_cell_view_set_model (GTK_CELL_VIEW (combo_box->priv->measurer), + combo_box->priv->model); +} + +static void +gtk_combo_box_style_set (GtkWidget *widget, + GtkStyle *previous_style, + gpointer data) +{ + gboolean appearance; + GtkComboBox *combo_box = GTK_COMBO_BOX (widget); + + /* if wrap_width > 0, then we are in grid-mode and forced to use + * unix style + */ + if (combo_box->priv->wrap_width) + return; + + gtk_widget_style_get (widget, + "appearance", &appearance, + NULL); + + /* TRUE is windows style */ + if (appearance) + { + if (GTK_IS_MENU (combo_box->priv->popup_widget)) + gtk_combo_box_menu_destroy (combo_box); + gtk_combo_box_list_setup (combo_box); + } + else + { + if (GTK_IS_TREE_VIEW (combo_box->priv->tree_view)) + gtk_combo_box_list_destroy (combo_box); + gtk_combo_box_menu_setup (combo_box, TRUE); + } +} + +static void +gtk_combo_box_button_toggled (GtkWidget *widget, + gpointer data) +{ + GtkComboBox *combo_box = GTK_COMBO_BOX (data); + + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))) + { + if (!combo_box->priv->popup_in_progress) + gtk_combo_box_popup (combo_box); + } + else + gtk_combo_box_popdown (combo_box); +} + +static void +gtk_combo_box_add (GtkContainer *container, + GtkWidget *widget) +{ + GtkComboBox *combo_box = GTK_COMBO_BOX (container); + + if (combo_box->priv->cell_view && combo_box->priv->cell_view->parent) + { + gtk_container_remove (container, combo_box->priv->cell_view); + (* GTK_CONTAINER_CLASS (parent_class)->add) (container, widget); + } + else + { + (* GTK_CONTAINER_CLASS (parent_class)->add) (container, widget); + } + + if (combo_box->priv->cell_view && + widget != combo_box->priv->cell_view) + { + /* since the cell_view was unparented, it's gone now */ + combo_box->priv->cell_view = NULL; + + if (!combo_box->priv->tree_view && combo_box->priv->separator) + { + gtk_widget_unparent (combo_box->priv->separator); + + g_object_ref (G_OBJECT (combo_box->priv->arrow)); + gtk_widget_unparent (combo_box->priv->arrow); + gtk_container_add (GTK_CONTAINER (combo_box->priv->button), + combo_box->priv->arrow); + g_object_unref (G_OBJECT (combo_box->priv->arrow)); + + gtk_widget_queue_resize (GTK_WIDGET (container)); + } + else if (combo_box->priv->cell_view_frame) + { + gtk_widget_unparent (combo_box->priv->cell_view_frame); + combo_box->priv->cell_view_frame = NULL; + } + } +} + +static ComboCellInfo * +gtk_combo_box_get_cell_info (GtkComboBox *combo_box, + GtkCellRenderer *cell) +{ + GSList *i; + + for (i = combo_box->priv->cells; i; i = i->next) + { + ComboCellInfo *info = (ComboCellInfo *)i->data; + + if (info->cell == cell) + return info; + } + + return NULL; +} + +static void +gtk_combo_box_menu_show (GtkWidget *menu, + gpointer user_data) +{ + GtkComboBox *combo_box = GTK_COMBO_BOX (user_data); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button), + TRUE); +} + +static void +gtk_combo_box_menu_hide (GtkWidget *menu, + gpointer user_data) +{ + GtkComboBox *combo_box = GTK_COMBO_BOX (user_data); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button), + FALSE); +} + +static void +gtk_combo_box_set_popup_widget (GtkComboBox *combo_box, + GtkWidget *popup) +{ + if (GTK_IS_MENU (combo_box->priv->popup_widget)) + combo_box->priv->popup_widget = NULL; + else if (combo_box->priv->popup_widget) + { + gtk_container_remove (GTK_CONTAINER (combo_box->priv->popup_frame), + combo_box->priv->popup_widget); + g_object_unref (G_OBJECT (combo_box->priv->popup_widget)); + combo_box->priv->popup_widget = NULL; + } + + if (GTK_IS_MENU (popup)) + { + if (combo_box->priv->popup_window) + { + gtk_widget_destroy (combo_box->priv->popup_window); + combo_box->priv->popup_window = combo_box->priv->popup_frame = NULL; + } + + combo_box->priv->popup_widget = popup; + + g_signal_connect (popup, "show", + G_CALLBACK (gtk_combo_box_menu_show), combo_box); + g_signal_connect (popup, "hide", + G_CALLBACK (gtk_combo_box_menu_hide), combo_box); + + /* FIXME: need to attach to widget? */ + } + else + { + if (!combo_box->priv->popup_window) + { + combo_box->priv->popup_window = gtk_window_new (GTK_WINDOW_POPUP); + + combo_box->priv->popup_frame = gtk_frame_new (NULL); + gtk_frame_set_shadow_type (GTK_FRAME (combo_box->priv->popup_frame), + GTK_SHADOW_NONE); + gtk_container_add (GTK_CONTAINER (combo_box->priv->popup_window), + combo_box->priv->popup_frame); + gtk_widget_show (combo_box->priv->popup_frame); + } + + gtk_container_add (GTK_CONTAINER (combo_box->priv->popup_frame), + popup); + gtk_widget_show (popup); + g_object_ref (G_OBJECT (popup)); + combo_box->priv->popup_widget = popup; + } +} + +static void +gtk_combo_box_menu_position (GtkMenu *menu, + gint *x, + gint *y, + gint *push_in, + gpointer user_data) +{ + gint sx, sy; + GtkWidget *child; + GtkRequisition req; + GtkComboBox *combo_box = GTK_COMBO_BOX (user_data); + + /* FIXME: is using the size request here broken? */ + child = GTK_BIN (combo_box)->child; + + gdk_window_get_origin (child->window, &sx, &sy); + + gtk_widget_size_request (GTK_WIDGET (menu), &req); + + *x = sx + child->allocation.width - req.width; + *y = sy + child->allocation.height; + + if (GTK_WIDGET_NO_WINDOW (child)) + { + *x += child->allocation.x; + *y += child->allocation.y; + } + + *push_in = TRUE; +} + +static void +gtk_combo_box_popup (GtkComboBox *combo_box) +{ + gint x, y, width, height; + GtkWidget *sample; + + if (GTK_WIDGET_MAPPED (combo_box->priv->popup_widget)) + return; + + if (GTK_IS_MENU (combo_box->priv->popup_widget)) + { + if (combo_box->priv->active_item != -1) + { + GList *childs; + + childs = gtk_container_get_children (GTK_CONTAINER (combo_box->priv->popup_widget)); + gtk_menu_shell_select_item (GTK_MENU_SHELL (combo_box->priv->popup_widget), + g_list_nth_data (childs, combo_box->priv->active_item)); + g_list_free (childs); + } + + gtk_menu_popup (GTK_MENU (combo_box->priv->popup_widget), + NULL, NULL, + gtk_combo_box_menu_position, combo_box, + 0, 0); + return; + } + + /* size it */ + sample = GTK_BIN (combo_box)->child; + + width = sample->allocation.width; + height = sample->allocation.height; + + gdk_window_get_origin (sample->window, + &x, &y); + gtk_widget_set_size_request (combo_box->priv->popup_window, + width, -1); + + if (GTK_WIDGET_NO_WINDOW (sample)) + { + x += sample->allocation.x; + y += sample->allocation.y; + } + + gtk_window_move (GTK_WINDOW (combo_box->priv->popup_window), + x, y + height); + + /* popup */ + gtk_widget_show_all (combo_box->priv->popup_window); + + gtk_widget_grab_focus (combo_box->priv->popup_window); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button), + TRUE); + + if (!GTK_WIDGET_HAS_FOCUS (combo_box->priv->tree_view)) + { + gdk_keyboard_grab (combo_box->priv->popup_window->window, + FALSE, GDK_CURRENT_TIME); + gtk_widget_grab_focus (combo_box->priv->tree_view); + } +} + +static void +gtk_combo_box_popdown (GtkComboBox *combo_box) +{ + if (GTK_IS_MENU (combo_box->priv->popup_widget)) + { + gtk_menu_popdown (GTK_MENU (combo_box->priv->popup_widget)); + return; + } + + gtk_widget_hide_all (combo_box->priv->popup_window); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button), + FALSE); +} + +static gint +gtk_combo_box_calc_requested_width (GtkComboBox *combo_box, + GtkTreePath *path) +{ + gint padding; + GtkRequisition req; + + gtk_widget_style_get (combo_box->priv->measurer, + "focus-line-width", &padding, + NULL); + + /* add some pixels for good measure */ + padding += BONUS_PADDING; + + gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (combo_box->priv->measurer), + path); + + /* nasty trick to get around the sizegroup's size request caching */ + (* GTK_WIDGET_GET_CLASS (combo_box->priv->measurer)->size_request) (combo_box->priv->measurer, &req); + + return req.width + padding; +} + +static void +gtk_combo_box_remeasure (GtkComboBox *combo_box) +{ + GtkTreeIter iter; + GtkTreePath *path; + gint padding = 0; + + if (!gtk_tree_model_get_iter_first (combo_box->priv->model, &iter)) + return; + + path = gtk_tree_path_new_from_indices (0, -1); + + gtk_widget_style_get (combo_box->priv->measurer, + "focus-line-width", &padding, + NULL); + + /* add some pixels for good measure */ + padding += BONUS_PADDING; + + do + { + GtkRequisition req; + + gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (combo_box->priv->measurer), + path); + + /* nasty trick to get around the sizegroup's size request caching */ + (* GTK_WIDGET_GET_CLASS (combo_box->priv->measurer)->size_request) (combo_box->priv->measurer, &req); + + combo_box->priv->width = MAX (combo_box->priv->width, + req.width + padding); + + gtk_tree_path_next (path); + } + while (gtk_tree_model_iter_next (combo_box->priv->model, &iter)); + + gtk_tree_path_free (path); + + if (combo_box->priv->cell_view) + { + gtk_widget_set_size_request (combo_box->priv->cell_view, + combo_box->priv->width, -1); + gtk_widget_queue_resize (combo_box->priv->cell_view); + } +} + +static void +gtk_combo_box_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ + gint width, height; + GtkRequisition bin_req; + + GtkComboBox *combo_box = GTK_COMBO_BOX (widget); + + /* common */ + gtk_widget_size_request (GTK_BIN (widget)->child, &bin_req); + + if (!combo_box->priv->tree_view) + { + /* menu mode */ + + if (combo_box->priv->cell_view) + { + GtkRequisition sep_req, arrow_req; + gint border_width, xthickness, ythickness; + + border_width = GTK_CONTAINER (combo_box->priv->button)->border_width; + xthickness = combo_box->priv->button->style->xthickness; + ythickness = combo_box->priv->button->style->ythickness; + + bin_req.width = MAX (bin_req.width, combo_box->priv->width); + + /* separator */ + gtk_widget_set_size_request (combo_box->priv->separator, + -1, bin_req.height); + gtk_widget_size_request (combo_box->priv->separator, &sep_req); + + /* arrow */ + gtk_widget_set_size_request (combo_box->priv->arrow, + -1, bin_req.height); + gtk_widget_size_request (combo_box->priv->arrow, &arrow_req); + + height = MAX (sep_req.height, arrow_req.height); + height = MAX (height, bin_req.height); + + width = bin_req.width + sep_req.width + arrow_req.width; + + height += border_width + 1 + xthickness * 2 + 4; + width += border_width + 1 + ythickness * 2 + 4; + + gtk_widget_set_size_request (combo_box->priv->button, width, height); + gtk_widget_size_request (combo_box->priv->button, requisition); + } + else + { + GtkRequisition but_req; + + gtk_widget_set_size_request (combo_box->priv->button, + -1, bin_req.height); + gtk_widget_size_request (combo_box->priv->button, &but_req); + + requisition->width = bin_req.width + but_req.width; + requisition->height = MAX (bin_req.height, but_req.height); + } + } + else + { + /* list mode */ + GtkRequisition button_req; + + /* sample + frame */ + *requisition = bin_req; + + if (combo_box->priv->cell_view_frame) + { + requisition->width += + (GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width + + GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness) * 2; + requisition->height += + (GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width + + GTK_WIDGET (combo_box->priv->cell_view_frame)->style->ythickness) * 2; + } + + /* the button */ + gtk_widget_set_size_request (combo_box->priv->button, + -1, requisition->height); + gtk_widget_size_request (combo_box->priv->button, &button_req); + + requisition->height = MAX (requisition->height, button_req.height); + requisition->width += button_req.width; + } +} + +static void +gtk_combo_box_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + GtkComboBox *combo_box = GTK_COMBO_BOX (widget); + GtkAllocation child; + GtkRequisition req; + + widget->allocation = *allocation; + + if (!combo_box->priv->tree_view) + { + if (combo_box->priv->cell_view) + { + gint border_width, xthickness, ythickness; + gint width; + + /* menu mode */ + gtk_widget_size_allocate (combo_box->priv->button, allocation); + + /* set some things ready */ + border_width = GTK_CONTAINER (combo_box->priv->button)->border_width; + xthickness = combo_box->priv->button->style->xthickness; + ythickness = combo_box->priv->button->style->ythickness; + + child.x = allocation->x + border_width + 1 + xthickness + 2; + child.y = allocation->y + border_width + 1 + ythickness + 2; + + width = allocation->width - (border_width + 1 + ythickness * 2 + 4); + + /* handle the childs */ + gtk_widget_size_request (combo_box->priv->arrow, &req); + child.width = req.width; + child.height = req.height; + child.x += width - req.width; + gtk_widget_size_allocate (combo_box->priv->arrow, &child); + + gtk_widget_size_request (combo_box->priv->separator, &req); + child.width = req.width; + child.height = req.height; + child.x -= req.width; + gtk_widget_size_allocate (combo_box->priv->separator, &child); + + child.width = child.x; + child.x = allocation->x + border_width + 1 + xthickness + 2; + child.width -= child.x; + + gtk_widget_size_request (GTK_BIN (widget)->child, &req); + child.height = req.height; + gtk_widget_size_allocate (GTK_BIN (widget)->child, &child); + } + else + { + gtk_widget_size_request (combo_box->priv->button, &req); + child.x = allocation->x + allocation->width - req.width; + child.y = allocation->y; + child.width = req.width; + child.height = req.height; + gtk_widget_size_allocate (combo_box->priv->button, &child); + + child.x = allocation->x; + child.y = allocation->y; + child.width = allocation->width - req.width; + gtk_widget_size_allocate (GTK_BIN (widget)->child, &child); + } + } + else + { + /* list mode */ + + /* button */ + gtk_widget_size_request (combo_box->priv->button, &req); + child.x = allocation->x + allocation->width - req.width; + child.y = allocation->y; + child.width = req.width; + child.height = req.height; + gtk_widget_size_allocate (combo_box->priv->button, &child); + + /* frame */ + child.x = allocation->x; + child.y = allocation->y; + child.width = allocation->width - req.width; + + if (combo_box->priv->cell_view_frame) + { + gtk_widget_size_allocate (combo_box->priv->cell_view_frame, &child); + + /* the sample */ + child.x += + GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width + + GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness; + child.y += + GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width + + GTK_WIDGET (combo_box->priv->cell_view_frame)->style->ythickness; + child.width -= + GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width + + GTK_WIDGET (combo_box->priv->cell_view_frame)->style->xthickness; + child.height -= + GTK_CONTAINER (combo_box->priv->cell_view_frame)->border_width + + GTK_WIDGET (combo_box->priv->cell_view_frame)->style->ythickness; + } + + gtk_widget_size_allocate (GTK_BIN (combo_box)->child, &child); + } +} + +static void +gtk_combo_box_forall (GtkContainer *container, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data) +{ + GtkComboBox *combo_box = GTK_COMBO_BOX (container); + + if (include_internals) + { + if (!combo_box->priv->tree_view) + { + if (combo_box->priv->cell_view && combo_box->priv->button) + { + (* callback) (combo_box->priv->button, callback_data); + (* callback) (combo_box->priv->separator, callback_data); + (* callback) (combo_box->priv->arrow, callback_data); + } + else if (combo_box->priv->arrow) + { + (* callback) (combo_box->priv->button, callback_data); + (* callback) (combo_box->priv->arrow, callback_data); + } + } + else + { + (* callback) (combo_box->priv->button, callback_data); + if (combo_box->priv->cell_view_frame) + (* callback) (combo_box->priv->cell_view_frame, callback_data); + } + } + + if (GTK_BIN (container)->child) + (* callback) (GTK_BIN (container)->child, callback_data); +} + +static gboolean +gtk_combo_box_expose_event (GtkWidget *widget, + GdkEventExpose *event) +{ + GtkComboBox *combo_box = GTK_COMBO_BOX (widget); + + if (!combo_box->priv->tree_view) + { + gtk_container_propagate_expose (GTK_CONTAINER (widget), + combo_box->priv->button, event); + + if (combo_box->priv->separator) + { + gtk_container_propagate_expose (GTK_CONTAINER (combo_box->priv->button), + combo_box->priv->separator, event); + + /* if not in this case, arrow gets its expose event from button */ + gtk_container_propagate_expose (GTK_CONTAINER (combo_box->priv->button), + combo_box->priv->arrow, event); + } + } + else + { + gtk_container_propagate_expose (GTK_CONTAINER (widget), + combo_box->priv->button, event); + + if (combo_box->priv->cell_view_frame) + gtk_container_propagate_expose (GTK_CONTAINER (widget), + combo_box->priv->cell_view_frame, event); + } + + gtk_container_propagate_expose (GTK_CONTAINER (widget), + GTK_BIN (widget)->child, event); + + return FALSE; +} + +/* + * menu style + */ + +static void +cell_view_sync_cells (GtkComboBox *combo_box, + GtkCellView *cell_view) +{ + GSList *k; + + for (k = combo_box->priv->cells; k; k = k->next) + { + GSList *j; + ComboCellInfo *info = (ComboCellInfo *)k->data; + + if (info->pack == GTK_PACK_START) + gtk_cell_view_pack_start (cell_view, + info->cell, info->expand); + else if (info->pack == GTK_PACK_END) + gtk_cell_view_pack_end (cell_view, + info->cell, info->expand); + + for (j = info->attributes; j; j = j->next->next) + { + gtk_cell_view_add_attribute (cell_view, + info->cell, + j->data, + GPOINTER_TO_INT (j->next->data)); + } + } +} + +static void +gtk_combo_box_menu_setup (GtkComboBox *combo_box, + gboolean add_childs) +{ + gint i, items; + GtkWidget *box; + GtkWidget *tmp; + + if (combo_box->priv->cell_view) + { + combo_box->priv->button = gtk_toggle_button_new (); + g_signal_connect (combo_box->priv->button, "toggled", + G_CALLBACK (gtk_combo_box_button_toggled), combo_box); + gtk_widget_set_parent (combo_box->priv->button, + GTK_BIN (combo_box)->child->parent); + + combo_box->priv->separator = gtk_vseparator_new (); + gtk_widget_set_parent (combo_box->priv->separator, + combo_box->priv->button); + gtk_widget_show (combo_box->priv->separator); + + combo_box->priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE); + gtk_widget_set_parent (combo_box->priv->arrow, combo_box->priv->button); + gtk_widget_show (combo_box->priv->arrow); + + gtk_widget_show_all (combo_box->priv->button); + + if (GTK_WIDGET_MAPPED (GTK_BIN (combo_box)->child)) + { + /* I have no clue why, but we need to manually map in this case. */ + gtk_widget_map (combo_box->priv->separator); + gtk_widget_map (combo_box->priv->arrow); + } + } + else + { + combo_box->priv->button = gtk_toggle_button_new (); + g_signal_connect (combo_box->priv->button, "toggled", + G_CALLBACK (gtk_combo_box_button_toggled), combo_box); + gtk_widget_set_parent (combo_box->priv->button, + GTK_BIN (combo_box)->child->parent); + + combo_box->priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE); + gtk_container_add (GTK_CONTAINER (combo_box->priv->button), + combo_box->priv->arrow); + gtk_widget_show_all (combo_box->priv->button); + } + + combo_box->priv->inserted_id = + g_signal_connect (combo_box->priv->model, "row_inserted", + G_CALLBACK (gtk_combo_box_menu_row_inserted), + combo_box); + combo_box->priv->deleted_id = + g_signal_connect (combo_box->priv->model, "row_deleted", + G_CALLBACK (gtk_combo_box_menu_row_deleted), + combo_box); + combo_box->priv->changed_id = + g_signal_connect (combo_box->priv->model, "row_changed", + G_CALLBACK (gtk_combo_box_menu_row_changed), + combo_box); + + g_signal_connect (combo_box->priv->button, "button_press_event", + G_CALLBACK (gtk_combo_box_menu_button_press), + combo_box); + + /* create our funky menu */ + box = gtk_menu_new (); + gtk_combo_box_set_popup_widget (combo_box, box); + + /* add items */ + if (!add_childs) + return; + + items = gtk_tree_model_iter_n_children (combo_box->priv->model, NULL); + + for (i = 0; i < items; i++) + { + GtkTreePath *path; + + path = gtk_tree_path_new_from_indices (i, -1); + tmp = gtk_cell_view_menu_item_new_from_model (combo_box->priv->model, + path); + g_signal_connect (tmp, "activate", + G_CALLBACK (gtk_combo_box_menu_item_activate), + combo_box); + + cell_view_sync_cells (combo_box, GTK_CELL_VIEW (GTK_BIN (tmp)->child)); + + gtk_menu_shell_append (GTK_MENU_SHELL (box), tmp); + gtk_widget_show (tmp); + + gtk_tree_path_free (path); + } +} + +static void +gtk_combo_box_menu_destroy (GtkComboBox *combo_box) +{ + /* disconnect signal handlers */ + g_signal_handler_disconnect (combo_box->priv->model, + combo_box->priv->inserted_id); + g_signal_handler_disconnect (combo_box->priv->model, + combo_box->priv->deleted_id); + g_signal_handler_disconnect (combo_box->priv->model, + combo_box->priv->changed_id); + + g_signal_handlers_disconnect_matched (combo_box->priv->button, + G_SIGNAL_MATCH_DATA, + 0, 0, NULL, + gtk_combo_box_menu_button_press, NULL); + + combo_box->priv->inserted_id = + combo_box->priv->deleted_id = + combo_box->priv->changed_id = -1; + + /* unparent will remove our latest ref */ + if (combo_box->priv->cell_view) + { + gtk_widget_unparent (combo_box->priv->arrow); + gtk_widget_unparent (combo_box->priv->separator); + gtk_widget_unparent (combo_box->priv->button); + } + else + { + /* will destroy the arrow too */ + gtk_widget_unparent (combo_box->priv->button); + } + + /* changing the popup window will unref the menu and the childs */ +} + +/* + * grid + */ + +static void +gtk_combo_box_item_get_size (GtkComboBox *combo_box, + gint index, + gint *cols, + gint *rows) +{ + GtkTreeIter iter; + + gtk_tree_model_iter_nth_child (combo_box->priv->model, &iter, NULL, index); + + if (cols) + { + if (combo_box->priv->col_column == -1) + *cols = 1; + else + gtk_tree_model_get (combo_box->priv->model, &iter, + combo_box->priv->col_column, cols, + -1); + } + + if (rows) + { + if (combo_box->priv->row_column == -1) + *rows = 1; + else + gtk_tree_model_get (combo_box->priv->model, &iter, + combo_box->priv->row_column, rows, + -1); + } +} + +static gboolean +menu_occupied (GtkMenu *menu, + guint left_attach, + guint right_attach, + guint top_attach, + guint bottom_attach) +{ + GList *i; + + g_return_val_if_fail (GTK_IS_MENU (menu), TRUE); + g_return_val_if_fail (left_attach < right_attach, TRUE); + g_return_val_if_fail (top_attach < bottom_attach, TRUE); + + for (i = GTK_MENU_SHELL (menu)->children; i; i = i->next) + { + guint l, r, b, t; + gboolean h_intersect = FALSE; + gboolean v_intersect = FALSE; + + gtk_container_child_get (GTK_CONTAINER (menu), i->data, + "left_attach", &l, + "right_attach", &r, + "bottom_attach", &b, + "top_attach", &t, + NULL); + + /* look if this item intersects with the given coordinates */ + h_intersect = left_attach <= l && l <= right_attach; + h_intersect &= left_attach <= r && r <= right_attach; + + v_intersect = top_attach <= t && t <= bottom_attach; + v_intersect &= top_attach <= b && b <= bottom_attach; + + if (h_intersect && v_intersect) + return TRUE; + } + + return FALSE; +} + +static void +gtk_combo_box_relayout_item (GtkComboBox *combo_box, + gint index) +{ + gint current_col = 0, current_row = 0; + gint rows, cols; + GList *list; + GtkWidget *item; + GtkWidget *menu; + + menu = combo_box->priv->popup_widget; + if (!GTK_IS_MENU_SHELL (menu)) + return; + + list = gtk_container_get_children (GTK_CONTAINER (menu)); + item = g_list_nth_data (list, index); + + gtk_combo_box_item_get_size (combo_box, index, &cols, &rows); + + /* look for a good spot */ + while (1) + { + if (current_col + cols > combo_box->priv->wrap_width) + { + current_col = 0; + current_row++; + } + + if (!menu_occupied (GTK_MENU (menu), + current_col, current_col + cols, + current_row, current_row + rows)) + break; + + current_col++; + } + + /* set attach props */ + gtk_menu_attach (GTK_MENU (menu), item, + current_col, current_col + cols, + current_row, current_row + rows); +} + +static void +gtk_combo_box_relayout (GtkComboBox *combo_box) +{ + gint i, items; + GList *list, *j; + GtkWidget *menu; + + /* ensure we are in menu style */ + if (combo_box->priv->tree_view) + gtk_combo_box_list_destroy (combo_box); + + menu = combo_box->priv->popup_widget; + + if (!GTK_IS_MENU_SHELL (menu)) + { + gtk_combo_box_menu_setup (combo_box, FALSE); + menu = combo_box->priv->popup_widget; + } + + /* get rid of all children */ + g_return_if_fail (GTK_IS_MENU_SHELL (menu)); + + list = gtk_container_get_children (GTK_CONTAINER (menu)); + + for (j = g_list_last (list); j; j = j->prev) + gtk_container_remove (GTK_CONTAINER (menu), j->data); + + g_list_free (j); + + /* and relayout */ + items = gtk_tree_model_iter_n_children (combo_box->priv->model, NULL); + + for (i = 0; i < items; i++) + { + GtkWidget *tmp; + GtkTreePath *path; + + path = gtk_tree_path_new_from_indices (i, -1); + tmp = gtk_cell_view_menu_item_new_from_model (combo_box->priv->model, + path); + + g_signal_connect (tmp, "activate", + G_CALLBACK (gtk_combo_box_menu_item_activate), + combo_box); + + cell_view_sync_cells (combo_box, GTK_CELL_VIEW (GTK_BIN (tmp)->child)); + + gtk_menu_shell_insert (GTK_MENU_SHELL (menu), tmp, i); + + if (combo_box->priv->wrap_width) + gtk_combo_box_relayout_item (combo_box, i); + + gtk_widget_show (tmp); + + gtk_tree_path_free (path); + } +} + +/* callbacks */ +static gboolean +gtk_combo_box_menu_button_press (GtkWidget *widget, + GdkEventButton *event, + gpointer user_data) +{ + GtkComboBox *combo_box = GTK_COMBO_BOX (user_data); + + if (! GTK_IS_MENU (combo_box->priv->popup_widget)) + return FALSE; + + if (event->type == GDK_BUTTON_PRESS && event->button == 1) + { + gtk_menu_popup (GTK_MENU (combo_box->priv->popup_widget), + NULL, NULL, + gtk_combo_box_menu_position, combo_box, + event->button, event->time); + + return TRUE; + } + + return FALSE; +} + +static void +gtk_combo_box_menu_item_activate (GtkWidget *item, + gpointer user_data) +{ + gint index; + GtkWidget *menu; + GtkComboBox *combo_box = GTK_COMBO_BOX (user_data); + + menu = combo_box->priv->popup_widget; + g_return_if_fail (GTK_IS_MENU (menu)); + + index = g_list_index (GTK_MENU_SHELL (menu)->children, item); + + gtk_combo_box_set_active (combo_box, index); +} + +static void +gtk_combo_box_menu_row_inserted (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer user_data) +{ + GtkWidget *menu; + GtkWidget *item; + GtkComboBox *combo_box = GTK_COMBO_BOX (user_data); + + menu = combo_box->priv->popup_widget; + g_return_if_fail (GTK_IS_MENU (menu)); + + item = gtk_cell_view_menu_item_new_from_model (model, path); + g_signal_connect (item, "activate", + G_CALLBACK (gtk_combo_box_menu_item_activate), + combo_box); + + cell_view_sync_cells (combo_box, GTK_CELL_VIEW (GTK_BIN (item)->child)); + + gtk_menu_shell_insert (GTK_MENU_SHELL (menu), item, + gtk_tree_path_get_indices (path)[0]); + gtk_widget_show (item); +} + +static void +gtk_combo_box_menu_row_deleted (GtkTreeModel *model, + GtkTreePath *path, + gpointer user_data) +{ + gint index, items; + GtkWidget *menu; + GtkWidget *item; + GtkComboBox *combo_box = GTK_COMBO_BOX (user_data); + + index = gtk_tree_path_get_indices (path)[0]; + items = gtk_tree_model_iter_n_children (model, NULL); + + if (gtk_combo_box_get_active (combo_box) == index) + gtk_combo_box_set_active (combo_box, index + 1 % items); + + menu = combo_box->priv->popup_widget; + g_return_if_fail (GTK_IS_MENU (menu)); + + item = g_list_nth_data (GTK_MENU_SHELL (menu)->children, index); + g_return_if_fail (GTK_IS_MENU_ITEM (item)); + + gtk_container_remove (GTK_CONTAINER (menu), item); +} + +static void +gtk_combo_box_menu_row_changed (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer user_data) +{ + GtkComboBox *combo_box = GTK_COMBO_BOX (user_data); + gint width; + + if (combo_box->priv->wrap_width) + gtk_combo_box_relayout_item (combo_box, + gtk_tree_path_get_indices (path)[0]); + + width = gtk_combo_box_calc_requested_width (combo_box, path); + + if (width > combo_box->priv->width) + { + gtk_widget_set_size_request (combo_box->priv->cell_view, width, -1); + gtk_widget_queue_resize (combo_box->priv->cell_view); + combo_box->priv->width = width; + } +} + +/* + * list style + */ + +static void +gtk_combo_box_list_setup (GtkComboBox *combo_box) +{ + GSList *i; + GtkTreeSelection *sel; + + combo_box->priv->button = gtk_toggle_button_new (); + gtk_widget_set_parent (combo_box->priv->button, + GTK_BIN (combo_box)->child->parent); + g_signal_connect (combo_box->priv->button, "button_press_event", + G_CALLBACK (gtk_combo_box_list_button_pressed), combo_box); + g_signal_connect (combo_box->priv->button, "toggled", + G_CALLBACK (gtk_combo_box_button_toggled), combo_box); + + combo_box->priv->arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE); + gtk_container_add (GTK_CONTAINER (combo_box->priv->button), + combo_box->priv->arrow); + gtk_widget_show_all (combo_box->priv->button); + + if (combo_box->priv->cell_view) + { + combo_box->priv->cell_view_frame = gtk_frame_new (NULL); + gtk_widget_set_parent (combo_box->priv->cell_view_frame, + GTK_BIN (combo_box)->child->parent); + gtk_frame_set_shadow_type (GTK_FRAME (combo_box->priv->cell_view_frame), + GTK_SHADOW_IN); + + g_object_set (G_OBJECT (combo_box->priv->cell_view), + "background", "white", + "background_set", TRUE, + NULL); + + gtk_widget_show (combo_box->priv->cell_view_frame); + } + + combo_box->priv->tree_view = gtk_tree_view_new (); + sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (combo_box->priv->tree_view)); + gtk_tree_selection_set_mode (sel, GTK_SELECTION_SINGLE); + gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (combo_box->priv->tree_view), + FALSE); + + g_signal_connect (combo_box->priv->tree_view, "button_press_event", + G_CALLBACK (gtk_combo_box_list_button_pressed), + combo_box); + g_signal_connect (combo_box->priv->tree_view, "button_release_event", + G_CALLBACK (gtk_combo_box_list_button_released), + combo_box); + g_signal_connect (combo_box->priv->tree_view, "key_press_event", + G_CALLBACK (gtk_combo_box_list_key_press), + combo_box); + + combo_box->priv->column = gtk_tree_view_column_new (); + gtk_tree_view_append_column (GTK_TREE_VIEW (combo_box->priv->tree_view), + combo_box->priv->column); + + /* set the models */ + gtk_tree_view_set_model (GTK_TREE_VIEW (combo_box->priv->tree_view), + combo_box->priv->model); + + combo_box->priv->changed_id = + g_signal_connect (combo_box->priv->model, "row_changed", + G_CALLBACK (gtk_combo_box_list_row_changed), + combo_box); + + /* sync up */ + for (i = combo_box->priv->cells; i; i = i->next) + { + GSList *j; + ComboCellInfo *info = (ComboCellInfo *)i->data; + + if (info->pack == GTK_PACK_START) + gtk_tree_view_column_pack_start (combo_box->priv->column, + info->cell, info->expand); + else if (info->pack == GTK_PACK_END) + gtk_tree_view_column_pack_end (combo_box->priv->column, + info->cell, info->expand); + + for (j = info->attributes; j; j = j->next->next) + { + gtk_tree_view_column_add_attribute (combo_box->priv->column, + info->cell, + j->data, + GPOINTER_TO_INT (j->next->data)); + } + } + + if (combo_box->priv->active_item != -1) + { + GtkTreePath *path; + + path = gtk_tree_path_new_from_indices (combo_box->priv->active_item, -1); + if (path) + { + gtk_tree_view_set_cursor (GTK_TREE_VIEW (combo_box->priv->tree_view), + path, NULL, FALSE); + gtk_tree_path_free (path); + } + } + + /* set sample/popup widgets */ + gtk_combo_box_set_popup_widget (GTK_COMBO_BOX (combo_box), + combo_box->priv->tree_view); + + gtk_widget_show (combo_box->priv->tree_view); +} + +static void +gtk_combo_box_list_destroy (GtkComboBox *combo_box) +{ + /* disconnect signals */ + g_signal_handler_disconnect (combo_box->priv->model, + combo_box->priv->changed_id); + combo_box->priv->changed_id = -1; + + g_signal_handlers_disconnect_matched (combo_box->priv->tree_view, + G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, combo_box); + g_signal_handlers_disconnect_matched (combo_box->priv->button, + G_SIGNAL_MATCH_DATA, + 0, 0, NULL, + gtk_combo_box_list_button_pressed, + NULL); + + /* destroy things (unparent will kill the latest ref from us) + * last unref on button will destroy the arrow + */ + gtk_widget_unparent (combo_box->priv->button); + + if (combo_box->priv->cell_view) + { + g_object_set (G_OBJECT (combo_box->priv->cell_view), + "background_set", FALSE, + NULL); + + gtk_widget_unparent (combo_box->priv->cell_view_frame); + } + + gtk_widget_destroy (combo_box->priv->tree_view); + combo_box->priv->tree_view = NULL; + combo_box->priv->popup_widget = NULL; +} + +/* callbacks */ +static void +gtk_combo_box_list_remove_grabs (GtkComboBox *combo_box) +{ + if (GTK_WIDGET_HAS_GRAB (combo_box->priv->tree_view)) + gtk_grab_remove (combo_box->priv->tree_view); + + if (GTK_WIDGET_HAS_GRAB (combo_box->priv->popup_window)) + { + gtk_grab_remove (combo_box->priv->popup_window); + gdk_pointer_ungrab (GDK_CURRENT_TIME); + } +} + +static gboolean +gtk_combo_box_list_button_pressed (GtkWidget *widget, + GdkEventButton *event, + gpointer data) +{ + GtkComboBox *combo_box = GTK_COMBO_BOX (data); + + GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *)event); + + if (ewidget == combo_box->priv->tree_view) + return TRUE; + + if ((ewidget != combo_box->priv->button) || + gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (combo_box->priv->button))) + return FALSE; + + gtk_combo_box_popup (combo_box); + + gtk_grab_add (combo_box->priv->popup_window); + gdk_pointer_grab (combo_box->priv->popup_window->window, TRUE, + GDK_BUTTON_PRESS_MASK | + GDK_BUTTON_RELEASE_MASK | + GDK_POINTER_MOTION_MASK, + NULL, NULL, GDK_CURRENT_TIME); + + gtk_grab_add (combo_box->priv->tree_view); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (combo_box->priv->button), + TRUE); + + combo_box->priv->popup_in_progress = TRUE; + + return TRUE; +} + +static gboolean +gtk_combo_box_list_button_released (GtkWidget *widget, + GdkEventButton *event, + gpointer data) +{ + gboolean ret; + GtkTreePath *path = NULL; + + GtkComboBox *combo_box = GTK_COMBO_BOX (data); + + gboolean popup_in_progress = FALSE; + + GtkWidget *ewidget = gtk_get_event_widget ((GdkEvent *)event); + + if (combo_box->priv->popup_in_progress) + { + popup_in_progress = TRUE; + combo_box->priv->popup_in_progress = FALSE; + } + + if (ewidget != combo_box->priv->tree_view) + { + if (ewidget == combo_box->priv->button && + !popup_in_progress && + gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (combo_box->priv->button))) + { + gtk_combo_box_list_remove_grabs (combo_box); + gtk_combo_box_popdown (combo_box); + return TRUE; + } + + /* released outside treeview */ + if (ewidget != combo_box->priv->button) + { + gtk_combo_box_list_remove_grabs (combo_box); + gtk_combo_box_popdown (combo_box); + + return TRUE; + } + + return FALSE; + } + + /* drop grabs */ + gtk_combo_box_list_remove_grabs (combo_box); + + /* select something cool */ + ret = gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget), + event->x, event->y, + &path, + NULL, NULL, NULL); + + if (!ret) + return TRUE; /* clicked outside window? */ + + gtk_combo_box_set_active (combo_box, gtk_tree_path_get_indices (path)[0]); + gtk_combo_box_popdown (combo_box); + + gtk_tree_path_free (path); + + return TRUE; +} + +static gboolean +gtk_combo_box_list_key_press (GtkWidget *widget, + GdkEventKey *event, + gpointer data) +{ + GtkComboBox *combo_box = GTK_COMBO_BOX (data); + + if ((event->keyval == GDK_Return || event->keyval == GDK_KP_Enter || + event->keyval == GDK_space || event->keyval == GDK_KP_Space) || + event->keyval == GDK_Escape) + { + if (event->keyval != GDK_Escape) + { + gboolean ret; + GtkTreeIter iter; + GtkTreeModel *model = NULL; + GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (combo_box->priv->tree_view)); + + ret = gtk_tree_selection_get_selected (sel, &model, &iter); + if (ret) + { + GtkTreePath *path; + + path = gtk_tree_model_get_path (model, &iter); + if (path) + { + gtk_combo_box_set_active (combo_box, gtk_tree_path_get_indices (path)[0]); + gtk_tree_path_free (path); + } + } + } + else + /* reset active item -- this is incredibly lame and ugly */ + gtk_combo_box_set_active (combo_box, + gtk_combo_box_get_active (combo_box)); + + gtk_combo_box_list_remove_grabs (combo_box); + gtk_combo_box_popdown (combo_box); + + return TRUE; + } + + return FALSE; +} + +static void +gtk_combo_box_list_row_changed (GtkTreeModel *model, + GtkTreePath *path, + GtkTreeIter *iter, + gpointer data) +{ + GtkComboBox *combo_box = GTK_COMBO_BOX (data); + gint width; + + width = gtk_combo_box_calc_requested_width (combo_box, path); + + if (width > combo_box->priv->width) + { + gtk_widget_set_size_request (combo_box->priv->cell_view, width, -1); + gtk_widget_queue_resize (combo_box->priv->cell_view); + combo_box->priv->width = width; + } +} + +/* + * public API + */ + +GtkWidget * +gtk_combo_box_new (GtkTreeModel *model) +{ + GtkComboBox *combo_box; + + g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL); + + combo_box = GTK_COMBO_BOX (g_object_new (gtk_combo_box_get_type (), + "model", model, + NULL)); + + return GTK_WIDGET (combo_box); +} + +void +gtk_combo_box_pack_start (GtkComboBox *combo_box, + GtkCellRenderer *cell, + gboolean expand) +{ + ComboCellInfo *info; + GtkWidget *menu; + + g_return_if_fail (GTK_IS_COMBO_BOX (combo_box)); + g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); + + info = g_new0 (ComboCellInfo, 1); + info->cell = cell; + info->expand = expand; + info->pack = GTK_PACK_START; + + combo_box->priv->cells = g_slist_append (combo_box->priv->cells, info); + + if (combo_box->priv->cell_view) + gtk_cell_view_pack_start (GTK_CELL_VIEW (combo_box->priv->cell_view), + cell, expand); + + gtk_cell_view_pack_start (GTK_CELL_VIEW (combo_box->priv->measurer), + cell, expand); + + if (combo_box->priv->column) + gtk_tree_view_column_pack_start (combo_box->priv->column, cell, expand); + + menu = combo_box->priv->popup_widget; + if (GTK_IS_MENU (menu)) + { + GList *i, *list; + + list = gtk_container_get_children (GTK_CONTAINER (menu)); + for (i = list; i; i = i->next) + { + GtkCellView *view; + + if (GTK_IS_CELL_VIEW_MENU_ITEM (i->data)) + view = GTK_CELL_VIEW (GTK_BIN (i->data)->child); + else + view = GTK_CELL_VIEW (i->data); + + gtk_cell_view_pack_start (view, cell, expand); + } + g_list_free (list); + } +} + +void +gtk_combo_box_pack_end (GtkComboBox *combo_box, + GtkCellRenderer *cell, + gboolean expand) +{ + ComboCellInfo *info; + GtkWidget *menu; + + g_return_if_fail (GTK_IS_COMBO_BOX (combo_box)); + g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); + + info = g_new0 (ComboCellInfo, 1); + info->cell = cell; + info->expand = expand; + info->pack = GTK_PACK_END; + + if (combo_box->priv->cell_view) + gtk_cell_view_pack_end (GTK_CELL_VIEW (combo_box->priv->cell_view), + cell, expand); + + gtk_cell_view_pack_end (GTK_CELL_VIEW (combo_box->priv->measurer), + cell, expand); + + if (combo_box->priv->column) + gtk_tree_view_column_pack_end (combo_box->priv->column, cell, expand); + + menu = combo_box->priv->popup_widget; + if (GTK_IS_MENU (menu)) + { + GList *i, *list; + + list = gtk_container_get_children (GTK_CONTAINER (menu)); + for (i = list; i; i = i->next) + { + GtkCellView *view; + + if (GTK_IS_CELL_VIEW_MENU_ITEM (i->data)) + view = GTK_CELL_VIEW (GTK_BIN (i->data)->child); + else + view = GTK_CELL_VIEW (i->data); + + gtk_cell_view_pack_end (view, cell, expand); + } + g_list_free (list); + } +} + +void +gtk_combo_box_set_attributes (GtkComboBox *combo_box, + GtkCellRenderer *cell, + ...) +{ + va_list args; + gchar *attribute; + gint column; + gint attributes = 0; + ComboCellInfo *info; + GtkWidget *menu; + + g_return_if_fail (GTK_IS_COMBO_BOX (combo_box)); + g_return_if_fail (GTK_IS_CELL_RENDERER (cell)); + + info = gtk_combo_box_get_cell_info (combo_box, cell); + + va_start (args, cell); + + attribute = va_arg (args, gchar *); + + while (attribute) + { + column = va_arg (args, gint); + + info->attributes = g_slist_prepend (info->attributes, + GINT_TO_POINTER (column)); + info->attributes = g_slist_prepend (info->attributes, + g_strdup (attribute)); + + attributes++; + + if (combo_box->priv->cell_view) + gtk_cell_view_add_attribute (GTK_CELL_VIEW (combo_box->priv->cell_view), + cell, attribute, column); + + gtk_cell_view_add_attribute (GTK_CELL_VIEW (combo_box->priv->measurer), + cell, attribute, column); + + if (combo_box->priv->column) + gtk_tree_view_column_add_attribute (combo_box->priv->column, cell, + attribute, column); + + attribute = va_arg (args, gchar *); + } + + va_end (args); + + menu = combo_box->priv->popup_widget; + if (GTK_IS_MENU (menu)) + { + GList *i, *list; + + list = gtk_container_get_children (GTK_CONTAINER (menu)); + for (i = list; i; i = i->next) + { + gint k; + GSList *j; + GtkCellView *view; + + if (GTK_IS_CELL_VIEW_MENU_ITEM (i->data)) + view = GTK_CELL_VIEW (GTK_BIN (i->data)->child); + else + view = GTK_CELL_VIEW (i->data); + + for (j = info->attributes, k = 0; k < attributes; k++) + { + gtk_cell_view_add_attribute (view, cell, + (gchar *)j->data, + GPOINTER_TO_INT (j->next->data)); + j = j->next->next; + } + } + g_list_free (list); + } + + gtk_combo_box_remeasure (combo_box); +} + +void +gtk_combo_box_clear (GtkComboBox *combo_box) +{ + GtkWidget *menu; + + g_return_if_fail (GTK_IS_COMBO_BOX (combo_box)); + + if (combo_box->priv->cell_view) + gtk_cell_view_clear (GTK_CELL_VIEW (combo_box->priv->cell_view)); + + if (combo_box->priv->measurer) + gtk_cell_view_clear (GTK_CELL_VIEW (combo_box->priv->measurer)); + + if (combo_box->priv->column) + gtk_tree_view_column_clear (combo_box->priv->column); + + menu = combo_box->priv->popup_widget; + if (GTK_IS_MENU (menu)) + { + GList *i, *list; + + list = gtk_container_get_children (GTK_CONTAINER (menu)); + for (i = list; i; i = i->next) + { + GtkCellView *view; + + if (GTK_IS_CELL_VIEW_MENU_ITEM (i->data)) + view = GTK_CELL_VIEW (GTK_BIN (i->data)->child); + else + view = GTK_CELL_VIEW (i->data); + + gtk_cell_view_clear (view); + } + } +} + +void +gtk_combo_box_set_wrap_width (GtkComboBox *combo_box, + gint width) +{ + g_return_if_fail (GTK_IS_COMBO_BOX (combo_box)); + g_return_if_fail (width > 0); + + combo_box->priv->wrap_width = width; + + gtk_combo_box_relayout (combo_box); +} + +void +gtk_combo_box_set_row_span_column (GtkComboBox *combo_box, + gint row_span) +{ + gint col; + + g_return_if_fail (GTK_IS_COMBO_BOX (combo_box)); + + col = gtk_tree_model_get_n_columns (combo_box->priv->model); + g_return_if_fail (row_span >= 0 && row_span < col); + + combo_box->priv->row_column = row_span; + + gtk_combo_box_relayout (combo_box); +} + +void +gtk_combo_box_set_column_span_column (GtkComboBox *combo_box, + gint column_span) +{ + gint col; + + g_return_if_fail (GTK_IS_COMBO_BOX (combo_box)); + + col = gtk_tree_model_get_n_columns (combo_box->priv->model); + g_return_if_fail (column_span >= 0 && column_span < col); + + combo_box->priv->col_column = column_span; + + gtk_combo_box_relayout (combo_box); +} + +gint +gtk_combo_box_get_active (GtkComboBox *combo_box) +{ + g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), 0); + + return combo_box->priv->active_item; +} + +void +gtk_combo_box_set_active (GtkComboBox *combo_box, + gint index) +{ + GtkTreePath *path; + + g_return_if_fail (GTK_IS_COMBO_BOX (combo_box)); + /* -1 means "no item selected" */ + g_return_if_fail (index >= -1); + + if (combo_box->priv->active_item == index) + return; + + combo_box->priv->active_item = index; + + if (index == -1) + { + if (combo_box->priv->tree_view) + gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (GTK_TREE_VIEW (combo_box->priv->tree_view))); + else + { + GtkMenu *menu = GTK_MENU (combo_box->priv->popup_widget); + + if (GTK_IS_MENU (menu)) + gtk_menu_set_active (menu, -1); + } + + if (combo_box->priv->cell_view) + gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (combo_box->priv->cell_view), NULL); + } + else + { + path = gtk_tree_path_new_from_indices (index, -1); + + if (combo_box->priv->tree_view) + gtk_tree_view_set_cursor (GTK_TREE_VIEW (combo_box->priv->tree_view), path, NULL, FALSE); + else + { + GtkMenu *menu = GTK_MENU (combo_box->priv->popup_widget); + + if (GTK_IS_MENU (menu)) + gtk_menu_set_active (GTK_MENU (menu), index); + } + + if (combo_box->priv->cell_view) + gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (combo_box->priv->cell_view), path); + + gtk_tree_path_free (path); + } + + g_signal_emit_by_name (combo_box, "changed", NULL, NULL); +} + +GtkTreeModel * +gtk_combo_box_get_model (GtkComboBox *combo_box) +{ + g_return_val_if_fail (GTK_IS_COMBO_BOX (combo_box), NULL); + + return combo_box->priv->model; +} + + +GtkWidget * +gtk_combo_box_new_text (void) +{ + GtkWidget *combo_box; + GtkCellRenderer *cell; + GtkListStore *store; + + store = gtk_list_store_new (1, G_TYPE_STRING); + + combo_box = gtk_combo_box_new (GTK_TREE_MODEL (store)); + + cell = gtk_cell_renderer_text_new (); + gtk_combo_box_pack_start (GTK_COMBO_BOX (combo_box), + cell, TRUE); + gtk_combo_box_set_attributes (GTK_COMBO_BOX (combo_box), + cell, + "text", 0, + NULL); + + return combo_box; +} + +void +gtk_combo_box_append_text (GtkComboBox *combo_box, + const gchar *text) +{ + GtkTreeIter iter; + GtkListStore *store; + + g_return_if_fail (GTK_IS_COMBO_BOX (combo_box)); + g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model)); + g_return_if_fail (text != NULL); + + store = GTK_LIST_STORE (combo_box->priv->model); + + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, 0, text, -1); +} + +void +gtk_combo_box_insert_text (GtkComboBox *combo_box, + gint position, + const gchar *text) +{ + GtkTreeIter iter; + GtkListStore *store; + + g_return_if_fail (GTK_IS_COMBO_BOX (combo_box)); + g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model)); + g_return_if_fail (position >= 0); + g_return_if_fail (text != NULL); + + store = GTK_LIST_STORE (combo_box->priv->model); + + gtk_list_store_insert (store, &iter, position); + gtk_list_store_set (store, &iter, 0, text, -1); +} + +void +gtk_combo_box_prepend_text (GtkComboBox *combo_box, + const gchar *text) +{ + GtkTreeIter iter; + GtkListStore *store; + + g_return_if_fail (GTK_IS_COMBO_BOX (combo_box)); + g_return_if_fail (GTK_IS_LIST_STORE (combo_box->priv->model)); + g_return_if_fail (text != NULL); + + store = GTK_LIST_STORE (combo_box->priv->model); + + gtk_list_store_prepend (store, &iter); + gtk_list_store_set (store, &iter, 0, text, -1); +} diff --git a/gtk/gtkcombobox.h b/gtk/gtkcombobox.h new file mode 100644 index 0000000000..b724f7a9d6 --- /dev/null +++ b/gtk/gtkcombobox.h @@ -0,0 +1,102 @@ +/* gtkcombobox.h + * Copyright (C) 2002, 2003 Kristian Rietveld + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GTK_COMBO_BOX_H__ +#define __GTK_COMBO_BOX_H__ + +#include +#include +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_COMBO_BOX (gtk_combo_box_get_type ()) +#define GTK_COMBO_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_COMBO_BOX, GtkComboBox)) +#define GTK_COMBO_BOX_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST ((vtable), GTK_TYPE_COMBO_BOX, GtkComboBoxClass)) +#define GTK_IS_COMBO_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_COMBO_BOX)) +#define GTK_IS_COMBO_BOX_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), GTK_TYPE_COMBO_BOX)) +#define GTK_COMBO_BOX_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), GTK_TYPE_COMBO_BOX, GtkComboBoxClass)) +#define GTK_COMBO_BOX_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_COMBO_BOX, GtkComboBoxPrivate)) + +typedef struct _GtkComboBox GtkComboBox; +typedef struct _GtkComboBoxClass GtkComboBoxClass; +typedef struct _GtkComboBoxPrivate GtkComboBoxPrivate; + +struct _GtkComboBox +{ + GtkBin parent_instance; + + /*< private >*/ + GtkComboBoxPrivate *priv; +}; + +struct _GtkComboBoxClass +{ + GtkBinClass parent_class; + + /* signals */ + void (* changed) (GtkComboBox *combo_box); +}; + + +/* construction */ +GType gtk_combo_box_get_type (void); +GtkWidget *gtk_combo_box_new (GtkTreeModel *model); + +/* manipulate list of cell renderers */ +void gtk_combo_box_pack_start (GtkComboBox *combo_box, + GtkCellRenderer *cell, + gboolean expand); +void gtk_combo_box_pack_end (GtkComboBox *combo_box, + GtkCellRenderer *cell, + gboolean expand); +void gtk_combo_box_set_attributes (GtkComboBox *combo_box, + GtkCellRenderer *cell, + ...); +void gtk_combo_box_clear (GtkComboBox *combo_box); + +/* grids */ +void gtk_combo_box_set_wrap_width (GtkComboBox *combo_box, + gint width); +void gtk_combo_box_set_row_span_column (GtkComboBox *combo_box, + gint row_span); +void gtk_combo_box_set_column_span_column (GtkComboBox *combo_box, + gint column_span); + +/* get/set active item */ +gint gtk_combo_box_get_active (GtkComboBox *combo_box); +void gtk_combo_box_set_active (GtkComboBox *combo_box, + gint index); + +/* getters and setters */ +GtkTreeModel *gtk_combo_box_get_model (GtkComboBox *combo_box); + +/* convenience -- text */ +GtkWidget *gtk_combo_box_new_text (void); +void gtk_combo_box_append_text (GtkComboBox *combo_box, + const gchar *text); +void gtk_combo_box_insert_text (GtkComboBox *combo_box, + gint position, + const gchar *text); +void gtk_combo_box_prepend_text (GtkComboBox *combo_box, + const gchar *text); + +G_END_DECLS + +#endif /* __GTK_COMBO_BOX_H__ */ diff --git a/gtk/gtkcomboboxentry.c b/gtk/gtkcomboboxentry.c new file mode 100644 index 0000000000..7d29916a37 --- /dev/null +++ b/gtk/gtkcomboboxentry.c @@ -0,0 +1,177 @@ +/* gtkcomboboxentry.c + * Copyright (C) 2002, 2003 Kristian Rietveld + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include + +#include +#include + +struct _GtkComboBoxEntryPrivate +{ + GtkWidget *entry; + + gint text_column; +}; + +static void gtk_combo_box_entry_class_init (GtkComboBoxEntryClass *klass); +static void gtk_combo_box_entry_init (GtkComboBoxEntry *entry_box); + +static void gtk_combo_box_entry_active_changed (GtkComboBox *combo_box, + gpointer user_data); +static void gtk_combo_box_entry_contents_changed (GtkEntry *entry, + gpointer user_data); + + +GType +gtk_combo_box_entry_get_type (void) +{ + static GType combo_box_entry_type = 0; + + if (!combo_box_entry_type) + { + static const GTypeInfo combo_box_entry_info = + { + sizeof (GtkComboBoxEntryClass), + NULL, /* base_init */ + NULL, /* base_finalize */ + (GClassInitFunc) gtk_combo_box_entry_class_init, + NULL, /* class_finalize */ + NULL, /* class_data */ + sizeof (GtkComboBoxEntry), + 0, + (GInstanceInitFunc) gtk_combo_box_entry_init + }; + + combo_box_entry_type = g_type_register_static (GTK_TYPE_COMBO_BOX, + "GtkComboBoxEntry", + &combo_box_entry_info, + 0); + } + + return combo_box_entry_type; +} + +static void +gtk_combo_box_entry_class_init (GtkComboBoxEntryClass *klass) +{ + g_type_class_add_private ((GObjectClass *) klass, + sizeof (GtkComboBoxEntryPrivate)); +} + +static void +gtk_combo_box_entry_init (GtkComboBoxEntry *entry_box) +{ + entry_box->priv = GTK_COMBO_BOX_ENTRY_GET_PRIVATE (entry_box); +} + +static void +gtk_combo_box_entry_active_changed (GtkComboBox *combo_box, + gpointer user_data) +{ + gint index; + GtkComboBoxEntry *entry_box = GTK_COMBO_BOX_ENTRY (combo_box); + + index = gtk_combo_box_get_active (combo_box); + + g_signal_handlers_block_by_func (entry_box->priv->entry, + gtk_combo_box_entry_contents_changed, + combo_box); + + if (index < 0) + gtk_entry_set_text (GTK_ENTRY (entry_box->priv->entry), ""); + else + { + gchar *str = NULL; + GtkTreeIter iter; + GtkTreeModel *model; + + model = gtk_combo_box_get_model (combo_box); + + gtk_tree_model_iter_nth_child (model, &iter, NULL, index); + gtk_tree_model_get (model, &iter, + entry_box->priv->text_column, &str, + -1); + + gtk_entry_set_text (GTK_ENTRY (entry_box->priv->entry), str); + + g_free (str); + } + + g_signal_handlers_unblock_by_func (entry_box->priv->entry, + gtk_combo_box_entry_contents_changed, + combo_box); +} + +static void +gtk_combo_box_entry_contents_changed (GtkEntry *entry, + gpointer user_data) +{ + GtkComboBox *combo_box = GTK_COMBO_BOX (user_data); + + g_signal_handlers_block_by_func (combo_box, + gtk_combo_box_entry_active_changed, + NULL); + gtk_combo_box_set_active (combo_box, -1); + g_signal_handlers_unblock_by_func (combo_box, + gtk_combo_box_entry_active_changed, + NULL); +} + +/* public API */ +GtkWidget * +gtk_combo_box_entry_new (GtkTreeModel *model, + gint text_column) +{ + GtkWidget *ret; + GtkCellRenderer *renderer; + + g_return_val_if_fail (GTK_IS_TREE_MODEL (model), NULL); + g_return_val_if_fail (text_column >= 0, NULL); + g_return_val_if_fail (text_column < gtk_tree_model_get_n_columns (model), NULL); + + ret = g_object_new (gtk_combo_box_entry_get_type (), + "model", model, + NULL); + + GTK_COMBO_BOX_ENTRY (ret)->priv->entry = gtk_entry_new (); + gtk_container_add (GTK_CONTAINER (ret), + GTK_COMBO_BOX_ENTRY (ret)->priv->entry); + + GTK_COMBO_BOX_ENTRY (ret)->priv->text_column = text_column; + renderer = gtk_cell_renderer_text_new (); + gtk_combo_box_pack_start (GTK_COMBO_BOX (ret), renderer, TRUE); + gtk_combo_box_set_attributes (GTK_COMBO_BOX (ret), renderer, + "text", text_column, + NULL); + + g_signal_connect (GTK_COMBO_BOX_ENTRY (ret)->priv->entry, "changed", + G_CALLBACK (gtk_combo_box_entry_contents_changed), ret); + g_signal_connect (ret, "changed", + G_CALLBACK (gtk_combo_box_entry_active_changed), NULL); + + return ret; +} + +gint +gtk_combo_box_entry_get_text_column (GtkComboBoxEntry *entry_box) +{ + g_return_val_if_fail (GTK_IS_COMBO_BOX_ENTRY (entry_box), 0); + + return entry_box->priv->text_column; +} diff --git a/gtk/gtkcomboboxentry.h b/gtk/gtkcomboboxentry.h new file mode 100644 index 0000000000..8e4c0db4f1 --- /dev/null +++ b/gtk/gtkcomboboxentry.h @@ -0,0 +1,63 @@ +/* gtkcomboboxentry.h + * Copyright (C) 2002, 2003 Kristian Rietveld + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __GTK_COMBO_BOX_ENTRY_H__ +#define __GTK_COMBO_BOX_ENTRY_H__ + +#include +#include + +G_BEGIN_DECLS + +#define GTK_TYPE_COMBO_BOX_ENTRY (gtk_combo_box_entry_get_type ()) +#define GTK_COMBO_BOX_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_COMBO_BOX_ENTRY, GtkComboBoxEntry)) +#define GTK_COMBO_BOX_ENTRY_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST ((vtable), GTK_TYPE_COMBO_BOX_ENTRY, GtkComboBoxEntryClass)) +#define GTK_IS_COMBO_BOX_ENTRY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_COMBO_BOX_ENTRY)) +#define GTK_IS_COMBO_BOX_ENTRY_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE ((vtable), GTK_TYPE_COMBO_BOX_ENTRY)) +#define GTK_COMBO_BOX_ENTRY_GET_CLASS(inst) (G_TYPE_INSTANCE_GET_CLASS ((inst), GTK_TYPE_COMBO_BOX_ENTRY, GtkComboBoxEntryClass)) +#define GTK_COMBO_BOX_ENTRY_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_COMBO_BOX_ENTRY, GtkComboBoxEntryPrivate)) + +typedef struct _GtkComboBoxEntry GtkComboBoxEntry; +typedef struct _GtkComboBoxEntryClass GtkComboBoxEntryClass; +typedef struct _GtkComboBoxEntryPrivate GtkComboBoxEntryPrivate; + +struct _GtkComboBoxEntry +{ + GtkComboBox parent_instance; + + /*< private >*/ + GtkComboBoxEntryPrivate *priv; +}; + +struct _GtkComboBoxEntryClass +{ + GtkComboBoxClass parent_class; +}; + + +GType gtk_combo_box_entry_get_type (void); +GtkWidget *gtk_combo_box_entry_new (GtkTreeModel *model, + gint text_column); + +gint gtk_combo_box_entry_get_text_column (GtkComboBoxEntry *entry_box); + + +G_END_DECLS + +#endif /* __GTK_COMBO_BOX_ENTRY_H__ */ diff --git a/tests/Makefile.am b/tests/Makefile.am index 367777581a..a669fce39f 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -29,6 +29,7 @@ endif noinst_PROGRAMS = \ simple \ testcalendar \ + testcombo \ testdnd \ testgtk \ testicontheme \ @@ -61,6 +62,7 @@ noinst_PROGRAMS = \ simple_DEPENDENCIES = $(TEST_DEPS) testicontheme_DEPENDENCIES = $(TEST_DEPS) testcalendar_DEPENDENCIES = $(TEST_DEPS) +testcombo_DEPENDENCIES = $(TEST_DEPS) testdnd_DEPENDENCIES = $(TEST_DEPS) testgtk_DEPENDENCIES = $(TEST_DEPS) testinput_DEPENDENCIES = $(TEST_DEPS) @@ -86,6 +88,7 @@ testactions_DEPENDENCIES = $(TEST_DEPS) simple_LDADD = $(LDADDS) testcalendar_LDADD = $(LDADDS) +testcombo_LDADD = $(LDADDS) testdnd_LDADD = $(LDADDS) testgtk_LDADD = $(LDADDS) testicontheme_LDADD = $(LDADDS) diff --git a/tests/testcombo.c b/tests/testcombo.c new file mode 100644 index 0000000000..f3ff6f8385 --- /dev/null +++ b/tests/testcombo.c @@ -0,0 +1,313 @@ +#include +#include +#include + +#include +#include + +/** + * oh yes, this test app surely has a lot of ugly code + */ + +/* grid combo demo */ +static GdkPixbuf * +create_color_pixbuf (const char *color) +{ + GdkPixbuf *pixbuf; + GdkColor col; + + int x; + int num; + int rowstride; + guchar *pixels, *p; + + if (!gdk_color_parse (color, &col)) + return NULL; + + pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, + FALSE, 8, + 16, 16); + + rowstride = gdk_pixbuf_get_rowstride (pixbuf); + p = pixels = gdk_pixbuf_get_pixels (pixbuf); + + num = gdk_pixbuf_get_width (pixbuf) * + gdk_pixbuf_get_height (pixbuf); + + for (x = 0; x < num; x++) { + p[0] = col.red / 65535 * 255; + p[1] = col.green / 65535 * 255; + p[2] = col.blue / 65535 * 255; + p += 3; + } + + return pixbuf; +} + +static GtkWidget * +create_combo_box_grid_demo () +{ + GtkWidget *combo; + GtkTreeIter iter; + GtkCellRenderer *cell = gtk_cell_renderer_pixbuf_new (); + GtkListStore *store; + + store = gtk_list_store_new (1, GDK_TYPE_PIXBUF); + + combo = gtk_combo_box_new (GTK_TREE_MODEL (store)); + gtk_combo_box_pack_start (GTK_COMBO_BOX (combo), + cell, TRUE); + gtk_combo_box_set_attributes (GTK_COMBO_BOX (combo), + cell, "pixbuf", 0, NULL); + gtk_combo_box_set_wrap_width (GTK_COMBO_BOX (combo), + 3); + + /* first row */ + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + 0, create_color_pixbuf ("red"), + -1); + + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + 0, create_color_pixbuf ("green"), + -1); + + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + 0, create_color_pixbuf ("blue"), + -1); + + /* second row */ + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + 0, create_color_pixbuf ("yellow"), + -1); + + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + 0, create_color_pixbuf ("black"), + -1); + + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + 0, create_color_pixbuf ("white"), + -1); + + /* third row */ + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + 0, create_color_pixbuf ("gray"), + -1); + + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + 0, create_color_pixbuf ("snow"), + -1); + + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + 0, create_color_pixbuf ("magenta"), + -1); + + gtk_combo_box_set_active (GTK_COMBO_BOX (combo), 0); + + return combo; +} + +/* blaat */ +static GtkTreeModel * +create_blaat () +{ + GdkPixbuf *pixbuf; + GtkWidget *cellview; + GtkTreeIter iter; + GtkListStore *store; + + cellview = gtk_cell_view_new (); + + store = gtk_list_store_new (2, GDK_TYPE_PIXBUF, G_TYPE_STRING); + + pixbuf = gtk_widget_render_icon (cellview, GTK_STOCK_DIALOG_WARNING, + GTK_ICON_SIZE_BUTTON, NULL); + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + 0, pixbuf, + 1, "gtk-stock-dialog-warning", + -1); + + pixbuf = gtk_widget_render_icon (cellview, GTK_STOCK_STOP, + GTK_ICON_SIZE_BUTTON, NULL); + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + 0, pixbuf, + 1, "gtk-stock-stop", + -1); + + pixbuf = gtk_widget_render_icon (cellview, GTK_STOCK_NEW, + GTK_ICON_SIZE_BUTTON, NULL); + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + 0, pixbuf, + 1, "gtk-stock-new", + -1); + + pixbuf = gtk_widget_render_icon (cellview, GTK_STOCK_CLEAR, + GTK_ICON_SIZE_BUTTON, NULL); + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, + 0, pixbuf, + 1, "gtk-stock-clear", + -1); + + gtk_widget_destroy (cellview); + + return GTK_TREE_MODEL (store); +} + +static GtkTreeModel * +create_text_store () +{ + GtkTreeIter iter; + GtkListStore *store; + + store = GTK_LIST_STORE (gtk_list_store_new (1, G_TYPE_STRING)); + + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, 0, "dum de dum", -1); + + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, 0, "la la la", -1); + + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, 0, "la la la dum de dum la la la la la la boom de da la la", -1); + + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, 0, "bloop", -1); + + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, 0, "bleep", -1); + + gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, 0, "klaas", -1); + + return GTK_TREE_MODEL (store); +} + +int +main (int argc, char **argv) +{ + GtkWidget *window, *cellview, *mainbox; + GtkWidget *combobox, *comboboxtext, *comboboxgrid; + GtkWidget *tmp, *boom; + GtkCellRenderer *renderer; + GdkPixbuf *pixbuf; + GValue value = {0, }; + + gtk_init (&argc, &argv); + + window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_container_set_border_width (GTK_CONTAINER (window), 5); + g_signal_connect (window, "delete_event", gtk_main_quit, NULL); + + mainbox = gtk_vbox_new (FALSE, 2); + gtk_container_add (GTK_CONTAINER (window), mainbox); + + + /* GtkCellView */ + tmp = gtk_frame_new ("GtkCellView"); + gtk_box_pack_start (GTK_BOX (mainbox), tmp, FALSE, FALSE, 0); + + boom = gtk_vbox_new (FALSE, 0); + gtk_container_set_border_width (GTK_CONTAINER (boom), 5); + gtk_container_add (GTK_CONTAINER (tmp), boom); + + cellview = gtk_cell_view_new (); + renderer = gtk_cell_renderer_pixbuf_new (); + pixbuf = gtk_widget_render_icon (cellview, GTK_STOCK_DIALOG_WARNING, + GTK_ICON_SIZE_BUTTON, NULL); + + gtk_cell_view_pack_start (GTK_CELL_VIEW (cellview), + renderer, + FALSE); + g_value_init (&value, GDK_TYPE_PIXBUF); + g_value_set_instance (&value, pixbuf); + gtk_cell_view_set_values (GTK_CELL_VIEW (cellview), + renderer, + "pixbuf", &value, + NULL); + g_value_unset (&value); + + renderer = gtk_cell_renderer_text_new (); + gtk_cell_view_pack_start (GTK_CELL_VIEW (cellview), + renderer, + TRUE); + g_value_init (&value, G_TYPE_STRING); + g_value_set_string (&value, "la la la"); + gtk_cell_view_set_values (GTK_CELL_VIEW (cellview), + renderer, + "text", &value, + NULL); + g_value_unset (&value); + gtk_container_add (GTK_CONTAINER (boom), cellview); + + + /* GtkComboBox */ + tmp = gtk_frame_new ("GtkComboBox"); + gtk_box_pack_start (GTK_BOX (mainbox), tmp, FALSE, FALSE, 0); + + boom = gtk_vbox_new (FALSE, 0); + gtk_container_set_border_width (GTK_CONTAINER (boom), 5); + gtk_container_add (GTK_CONTAINER (tmp), boom); + + combobox = gtk_combo_box_new (create_blaat ()); + gtk_container_add (GTK_CONTAINER (boom), combobox); + + renderer = gtk_cell_renderer_pixbuf_new (); + gtk_combo_box_pack_start (GTK_COMBO_BOX (combobox), + renderer, + FALSE); + gtk_combo_box_set_attributes (GTK_COMBO_BOX (combobox), renderer, "pixbuf", 0, NULL); + + renderer = gtk_cell_renderer_text_new (); + gtk_combo_box_pack_start (GTK_COMBO_BOX (combobox), + renderer, + TRUE); + gtk_combo_box_set_attributes (GTK_COMBO_BOX (combobox), renderer, "text", 1, NULL); + + gtk_combo_box_set_active (GTK_COMBO_BOX (combobox), 1); + + + /* GtkComboBox (grid mode) */ + tmp = gtk_frame_new ("GtkComboBox (grid mode)"); + gtk_box_pack_start (GTK_BOX (mainbox), tmp, FALSE, FALSE, 0); + + boom = gtk_vbox_new (FALSE, 0); + gtk_container_set_border_width (GTK_CONTAINER (boom), 5); + gtk_container_add (GTK_CONTAINER (tmp), boom); + + comboboxgrid = create_combo_box_grid_demo (); + gtk_box_pack_start (GTK_BOX (boom), comboboxgrid, FALSE, FALSE, 0); + + + /* GtkComboBoxEntry */ + tmp = gtk_frame_new ("GtkComboBoxEntry"); + gtk_box_pack_start (GTK_BOX (mainbox), tmp, FALSE, FALSE, 0); + + boom = gtk_vbox_new (FALSE, 0); + gtk_container_set_border_width (GTK_CONTAINER (boom), 5); + gtk_container_add (GTK_CONTAINER (tmp), boom); + + comboboxtext = gtk_combo_box_entry_new (create_text_store (), 0); + gtk_container_add (GTK_CONTAINER (boom), comboboxtext); + + /* done */ + gtk_widget_show_all (window); + + gtk_main (); + + return 0; +} + +/* vim:expandtab + */