diff --git a/ChangeLog b/ChangeLog index 8df4a6f166..84b2925b5e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2008-03-06 Johan Dahlin + + * docs/reference/gtk/tmpl/gtkiconfactory.sgml: + * gtk/gtkbuilder.c: + * gtk/gtkbuilderprivate.h: + * gtk/gtkiconfactory.c: + * tests/buildertest.c: + Implement GtkBuildable on GtkIconFactory, to make + it possible to register custom stock icons. + Fixes #517066 + 2008-03-06 Johan Dahlin Make gtk-doc happy: diff --git a/docs/reference/gtk/tmpl/gtkiconfactory.sgml b/docs/reference/gtk/tmpl/gtkiconfactory.sgml index 88a7229a32..db160c208d 100644 --- a/docs/reference/gtk/tmpl/gtkiconfactory.sgml +++ b/docs/reference/gtk/tmpl/gtkiconfactory.sgml @@ -35,6 +35,77 @@ gtk_widget_render_icon(). These functions take the theme into account when looking up the icon to use for a given stock ID. +GtkIconFactory as GtkBuildable + +GtkIconFactory supports a custom <sources> element, which +can contain multiple <source> elements. +The following attributes are allowed: + + + +stock-id +The stock id of the source, a string. +This attribute is mandatory + + + +filename +The filename of the source, a string. +This attribute is mandatory + + + + +icon-name +The icon name for the source, a string. +This attribute is optional. + + + + +size +Size of the icon, a #GtkIconSize enum value. +This attribute is optional. + + + + +direction +Direction of the source, a #GtkTextDirection enum value. +This attribute is optional. + + + + +state +State of the source, a #GtkStateType enum value. +This attribute is optional. + + + + + + + +A <structname>GtkIconFactory</structname> UI definition fragment. + + + + + + + + + apple-red + True + + + +]]> + + + diff --git a/gtk/gtkbuilder.c b/gtk/gtkbuilder.c index cc96135220..11641035dc 100644 --- a/gtk/gtkbuilder.c +++ b/gtk/gtkbuilder.c @@ -1234,18 +1234,7 @@ gtk_builder_value_from_string_type (GtkBuilder *builder, return FALSE; } - if (g_path_is_absolute (string)) - filename = g_strdup (string); - else - { - gchar *dirname; - - dirname = g_path_get_dirname (builder->priv->filename); - filename = g_build_filename (dirname, string, NULL); - - g_free (dirname); - } - + filename = _gtk_builder_get_absolute_filename (builder, string); pixbuf = gdk_pixbuf_new_from_file (filename, &tmp_error); if (pixbuf == NULL) @@ -1466,6 +1455,26 @@ gtk_builder_error_quark (void) return g_quark_from_static_string ("gtk-builder-error-quark"); } +gchar * +_gtk_builder_get_absolute_filename (GtkBuilder *builder, const gchar *string) +{ + gchar *filename; + gchar *dirname = NULL; + + if (g_path_is_absolute (string)) + return g_strdup (string); + + if (builder->priv->filename && + strcmp (builder->priv->filename, ".") != 0) + dirname = g_path_get_dirname (builder->priv->filename); + else + dirname = g_get_current_dir (); + + filename = g_build_filename (dirname, string, NULL); + g_free (dirname); + + return filename; +} #define __GTK_BUILDER_C__ #include "gtkaliasdef.c" diff --git a/gtk/gtkbuilderprivate.h b/gtk/gtkbuilderprivate.h index 1a45aca236..96f8fd4568 100644 --- a/gtk/gtkbuilderprivate.h +++ b/gtk/gtkbuilderprivate.h @@ -125,5 +125,7 @@ gboolean _gtk_builder_flags_from_string (GType type, gchar * _gtk_builder_parser_translate (const gchar *domain, const gchar *context, const gchar *text); +gchar * _gtk_builder_get_absolute_filename (GtkBuilder *builder, + const gchar *string); #endif /* __GTK_BUILDER_PRIVATE_H__ */ diff --git a/gtk/gtkiconfactory.c b/gtk/gtkiconfactory.c index d8e8bff279..80eb24def2 100644 --- a/gtk/gtkiconfactory.c +++ b/gtk/gtkiconfactory.c @@ -1,6 +1,6 @@ /* GTK - The GIMP Toolkit * Copyright (C) 2000 Red Hat, Inc. - * + * 2008 Johan Dahlin * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either @@ -37,6 +37,8 @@ #include "gtkstock.h" #include "gtkwidget.h" #include "gtkintl.h" +#include "gtkbuildable.h" +#include "gtkbuilderprivate.h" #include "gtkalias.h" @@ -83,6 +85,20 @@ struct _GtkIconSource }; +static void +gtk_icon_factory_buildable_init (GtkBuildableIface *iface); + +static gboolean gtk_icon_factory_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + GMarkupParser *parser, + gpointer *data); +static void gtk_icon_factory_buildable_custom_tag_end (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer *user_data); static void gtk_icon_factory_finalize (GObject *object); static void get_default_icons (GtkIconFactory *icon_factory); static void icon_source_clear (GtkIconSource *source); @@ -96,7 +112,9 @@ static GtkIconSize icon_size_register_intern (const gchar *name, 0, 0, 0, \ any_direction, any_state, any_size } -G_DEFINE_TYPE (GtkIconFactory, gtk_icon_factory, G_TYPE_OBJECT) +G_DEFINE_TYPE_WITH_CODE (GtkIconFactory, gtk_icon_factory, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE, + gtk_icon_factory_buildable_init)) static void gtk_icon_factory_init (GtkIconFactory *factory) @@ -113,6 +131,13 @@ gtk_icon_factory_class_init (GtkIconFactoryClass *klass) object_class->finalize = gtk_icon_factory_finalize; } +static void +gtk_icon_factory_buildable_init (GtkBuildableIface *iface) +{ + iface->custom_tag_start = gtk_icon_factory_buildable_custom_tag_start; + iface->custom_tag_end = gtk_icon_factory_buildable_custom_tag_end; +} + static void free_icon_set (gpointer key, gpointer value, gpointer data) { @@ -2700,6 +2725,204 @@ _gtk_icon_factory_list_ids (void) return ids; } +typedef struct { + GSList *sources; + gboolean in_source; + +} IconFactoryParserData; + +typedef struct { + gchar *stock_id; + gchar *filename; + gchar *icon_name; + GtkTextDirection direction; + GtkIconSize size; + GtkStateType state; +} IconSourceParserData; + +static void +icon_source_start_element (GMarkupParseContext *context, + const gchar *element_name, + const gchar **names, + const gchar **values, + gpointer user_data, + GError **error) +{ + gint i; + gchar *stock_id = NULL; + gchar *filename = NULL; + gchar *icon_name = NULL; + GtkIconSize size = -1; + GtkTextDirection direction = -1; + GtkStateType state = -1; + IconFactoryParserData *parser_data; + IconSourceParserData *source_data; + + parser_data = (IconFactoryParserData*)user_data; + + if (!parser_data->in_source) + { + if (strcmp (element_name, "sources") != 0) + { + g_warning ("Unexpected element %s, expected ", element_name); + return; + } + parser_data->in_source = TRUE; + return; + } + else + { + if (strcmp (element_name, "source") != 0) + { + g_warning ("Unexpected element %s, expected ", element_name); + return; + } + } + + for (i = 0; names[i]; i++) + { + if (strcmp (names[i], "stock-id") == 0) + stock_id = g_strdup (values[i]); + else if (strcmp (names[i], "filename") == 0) + filename = g_strdup (values[i]); + else if (strcmp (names[i], "icon-name") == 0) + icon_name = g_strdup (values[i]); + else if (strcmp (names[i], "size") == 0) + { + if (!_gtk_builder_flags_from_string (GTK_TYPE_ICON_SIZE, + values[i], + &size, + error)) + return; + } + else if (strcmp (names[i], "direction") == 0) + { + if (!_gtk_builder_flags_from_string (GTK_TYPE_TEXT_DIRECTION, + values[i], + &direction, + error)) + return; + } + else if (strcmp (names[i], "state") == 0) + { + if (!_gtk_builder_flags_from_string (GTK_TYPE_STATE_TYPE, + values[i], + &state, + error)) + return; + } + } + + if (!stock_id || !filename) + { + g_warning (" requires a stock_id and a filename"); + return; + } + + source_data = g_slice_new (IconSourceParserData); + source_data->stock_id = stock_id; + source_data->filename = filename; + source_data->icon_name = icon_name; + source_data->size = size; + source_data->direction = direction; + source_data->state = state; + + parser_data->sources = g_slist_prepend (parser_data->sources, source_data); +} + +static const GMarkupParser icon_source_parser = + { + icon_source_start_element, + }; + +static gboolean +gtk_icon_factory_buildable_custom_tag_start (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + GMarkupParser *parser, + gpointer *data) +{ + g_assert (buildable); + + if (strcmp (tagname, "sources") == 0) + { + IconFactoryParserData *parser_data; + + parser_data = g_slice_new0 (IconFactoryParserData); + *parser = icon_source_parser; + *data = parser_data; + return TRUE; + } + return FALSE; +} + +static void +gtk_icon_factory_buildable_custom_tag_end (GtkBuildable *buildable, + GtkBuilder *builder, + GObject *child, + const gchar *tagname, + gpointer *user_data) +{ + GtkIconFactory *icon_factory; + + icon_factory = GTK_ICON_FACTORY (buildable); + + if (strcmp (tagname, "sources") == 0) + { + IconFactoryParserData *parser_data; + GtkIconSource *icon_source; + GtkIconSet *icon_set; + GSList *l; + + parser_data = (IconFactoryParserData*)user_data; + + for (l = parser_data->sources; l; l = l->next) + { + IconSourceParserData *source_data = l->data; + + icon_set = gtk_icon_factory_lookup (icon_factory, source_data->stock_id); + if (!icon_set) + { + icon_set = gtk_icon_set_new (); + gtk_icon_factory_add (icon_factory, source_data->stock_id, icon_set); + } + + icon_source = gtk_icon_source_new (); + + if (source_data->filename) + { + gchar *filename; + filename = _gtk_builder_get_absolute_filename (builder, source_data->filename); + gtk_icon_source_set_filename (icon_source, filename); + g_free (filename); + } + if (source_data->icon_name) + gtk_icon_source_set_icon_name (icon_source, source_data->icon_name); + if (source_data->size != -1) + gtk_icon_source_set_size (icon_source, source_data->size); + if (source_data->direction != -1) + gtk_icon_source_set_direction (icon_source, source_data->direction); + if (source_data->state != -1) + gtk_icon_source_set_state (icon_source, source_data->state); + + /* Inline source_add() to avoid creating a copy */ + g_assert (source->type != GTK_ICON_SOURCE_EMPTY); + icon_set->sources = g_slist_insert_sorted (icon_set->sources, + icon_source, + icon_source_compare); + gtk_icon_set_unref (icon_set); + + g_free (source_data->stock_id); + g_free (source_data->filename); + g_free (source_data->icon_name); + g_slice_free (IconSourceParserData, source_data); + } + g_slist_free (parser_data->sources); + g_slice_free (IconFactoryParserData, parser_data); + } +} + #ifdef G_OS_WIN32 /* DLL ABI stability backward compatibility versions */ diff --git a/tests/buildertest.c b/tests/buildertest.c index 60467d5448..65b90a99e7 100644 --- a/tests/buildertest.c +++ b/tests/buildertest.c @@ -1793,6 +1793,34 @@ test_reference_counting (void) g_object_unref (builder); } +static void +test_icon_factory (void) +{ + GtkBuilder *builder; + const gchar buffer1[] = + "" + " " + " " + " " + " " + " " + ""; + GObject *factory; + GtkIconSet *icon_set; + GtkWidget *image; + + builder = builder_new_from_string (buffer1, -1, NULL); + factory = gtk_builder_get_object (builder, "iconfactory1"); + g_assert (factory != NULL); + + icon_set = gtk_icon_factory_lookup (GTK_ICON_FACTORY (factory), "apple-red"); + g_assert (icon_set != NULL); + + gtk_icon_factory_add_default (GTK_ICON_FACTORY (factory)); + image = gtk_image_new_from_stock ("apple-red", GTK_ICON_SIZE_BUTTON); + g_assert (image != NULL); +} + static void test_file (const gchar *filename) { @@ -1874,5 +1902,7 @@ main (int argc, char **argv) g_test_add_func ("/Builder/Value From String", test_value_from_string); g_test_add_func ("/Builder/Reference Counting", test_reference_counting); g_test_add_func ("/Builder/Window", test_window); + g_test_add_func ("/Builder/IconFactory", test_icon_factory); + return g_test_run(); }