diff options
author | Richard Hughes <richard@hughsie.com> | 2014-10-01 14:10:48 +0100 |
---|---|---|
committer | Richard Hughes <richard@hughsie.com> | 2014-10-01 15:16:41 +0100 |
commit | 475e8d18f2154763ccad6bdeb0cef5c33526e9e8 (patch) | |
tree | a84f297ac8692ea5d02b5cedd039d696c8630adb | |
parent | 82a68844ae87ca36019cd95fcdbcde15dbfa1a46 (diff) | |
download | appstream-glib-475e8d18f2154763ccad6bdeb0cef5c33526e9e8.tar.gz |
Add AsIcon as a new abstract icon object
-rw-r--r-- | docs/api/appstream-glib-docs.sgml | 1 | ||||
-rw-r--r-- | libappstream-glib/Makefile.am | 5 | ||||
-rw-r--r-- | libappstream-glib/appstream-glib.h | 1 | ||||
-rw-r--r-- | libappstream-glib/as-enums.c | 48 | ||||
-rw-r--r-- | libappstream-glib/as-enums.h | 23 | ||||
-rw-r--r-- | libappstream-glib/as-icon-private.h | 49 | ||||
-rw-r--r-- | libappstream-glib/as-icon.c | 618 | ||||
-rw-r--r-- | libappstream-glib/as-icon.h | 134 | ||||
-rw-r--r-- | libappstream-glib/as-self-test.c | 133 |
9 files changed, 941 insertions, 71 deletions
diff --git a/docs/api/appstream-glib-docs.sgml b/docs/api/appstream-glib-docs.sgml index 7a0c0fb..3d5fe88 100644 --- a/docs/api/appstream-glib-docs.sgml +++ b/docs/api/appstream-glib-docs.sgml @@ -34,6 +34,7 @@ </para> </partintro> <xi:include href="xml/as-app.xml"/> + <xi:include href="xml/as-icon.xml"/> <xi:include href="xml/as-image.xml"/> <xi:include href="xml/as-release.xml"/> <xi:include href="xml/as-provide.xml"/> diff --git a/libappstream-glib/Makefile.am b/libappstream-glib/Makefile.am index 28e6e8d..f798a97 100644 --- a/libappstream-glib/Makefile.am +++ b/libappstream-glib/Makefile.am @@ -68,6 +68,7 @@ libappstream_glib_include_HEADERS = \ appstream-glib.h \ as-app.h \ as-enums.h \ + as-icon.h \ as-image.h \ as-node.h \ as-problem.h \ @@ -85,6 +86,8 @@ libappstream_glib_la_SOURCES = \ as-app-validate.c \ as-cleanup.h \ as-enums.c \ + as-icon.c \ + as-icon-private.h \ as-image.c \ as-image-private.h \ as-node.c \ @@ -150,6 +153,8 @@ introspection_sources = \ as-app.h \ as-enums.c \ as-enums.h \ + as-icon.c \ + as-icon.h \ as-image.c \ as-image.h \ as-node.c \ diff --git a/libappstream-glib/appstream-glib.h b/libappstream-glib/appstream-glib.h index b15db6d..e2e1144 100644 --- a/libappstream-glib/appstream-glib.h +++ b/libappstream-glib/appstream-glib.h @@ -26,6 +26,7 @@ #include <as-app.h> #include <as-enums.h> +#include <as-icon.h> #include <as-image.h> #include <as-node.h> #include <as-problem.h> diff --git a/libappstream-glib/as-enums.c b/libappstream-glib/as-enums.c index 5f7fe53..4a78787 100644 --- a/libappstream-glib/as-enums.c +++ b/libappstream-glib/as-enums.c @@ -93,54 +93,6 @@ as_id_kind_from_string (const gchar *id_kind) } /** - * as_icon_kind_to_string: - * @icon_kind: the @AsIconKind. - * - * Converts the enumerated value to an text representation. - * - * Returns: string version of @icon_kind - * - * Since: 0.1.0 - **/ -const gchar * -as_icon_kind_to_string (AsIconKind icon_kind) -{ - if (icon_kind == AS_ICON_KIND_CACHED) - return "cached"; - if (icon_kind == AS_ICON_KIND_STOCK) - return "stock"; - if (icon_kind == AS_ICON_KIND_REMOTE) - return "remote"; - if (icon_kind == AS_ICON_KIND_EMBEDDED) - return "embedded"; - return "unknown"; -} - -/** - * as_icon_kind_from_string: - * @icon_kind: the string. - * - * Converts the text representation to an enumerated value. - * - * Returns: a #AsIconKind or %AS_ICON_KIND_UNKNOWN for unknown - * - * Since: 0.1.0 - **/ -AsIconKind -as_icon_kind_from_string (const gchar *icon_kind) -{ - if (g_strcmp0 (icon_kind, "cached") == 0) - return AS_ICON_KIND_CACHED; - if (g_strcmp0 (icon_kind, "stock") == 0) - return AS_ICON_KIND_STOCK; - if (g_strcmp0 (icon_kind, "remote") == 0) - return AS_ICON_KIND_REMOTE; - if (g_strcmp0 (icon_kind, "embedded") == 0) - return AS_ICON_KIND_EMBEDDED; - return AS_ICON_KIND_UNKNOWN; -} - -/** * as_url_kind_to_string: * @url_kind: the @AsUrlKind. * diff --git a/libappstream-glib/as-enums.h b/libappstream-glib/as-enums.h index 050ae8b..ac8010a 100644 --- a/libappstream-glib/as-enums.h +++ b/libappstream-glib/as-enums.h @@ -57,26 +57,6 @@ typedef enum { } AsIdKind; /** - * AsIconKind: - * @AS_ICON_KIND_UNKNOWN: Type invalid or not known - * @AS_ICON_KIND_STOCK: Stock icon or present in the generic icon theme - * @AS_ICON_KIND_CACHED: An icon shipped with the AppStream metadata - * @AS_ICON_KIND_REMOTE: An icon referenced by a remote URL - * @AS_ICON_KIND_EMBEDDED: An embedded Base64 icon - * - * The icon type. - **/ -typedef enum { - AS_ICON_KIND_UNKNOWN, /* Since: 0.1.0 */ - AS_ICON_KIND_STOCK, /* Since: 0.1.0 */ - AS_ICON_KIND_CACHED, /* Since: 0.1.0 */ - AS_ICON_KIND_REMOTE, /* Since: 0.1.0 */ - AS_ICON_KIND_EMBEDDED, /* Since: 0.3.1 */ - /*< private >*/ - AS_ICON_KIND_LAST -} AsIconKind; - -/** * AsUrlKind: * @AS_URL_KIND_UNKNOWN: Type invalid or not known * @AS_URL_KIND_HOMEPAGE: Application project homepage @@ -129,9 +109,6 @@ typedef enum { const gchar *as_id_kind_to_string (AsIdKind id_kind); AsIdKind as_id_kind_from_string (const gchar *id_kind); -const gchar *as_icon_kind_to_string (AsIconKind icon_kind); -AsIconKind as_icon_kind_from_string (const gchar *icon_kind); - const gchar *as_url_kind_to_string (AsUrlKind url_kind); AsUrlKind as_url_kind_from_string (const gchar *url_kind); diff --git a/libappstream-glib/as-icon-private.h b/libappstream-glib/as-icon-private.h new file mode 100644 index 0000000..01ed19a --- /dev/null +++ b/libappstream-glib/as-icon-private.h @@ -0,0 +1,49 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2014 Richard Hughes <richard@hughsie.com> + * + * Licensed under the GNU Lesser General Public License Version 2.1 + * + * 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 + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#if !defined (__APPSTREAM_GLIB_PRIVATE_H) && !defined (AS_COMPILATION) +#error "Only <appstream-glib.h> can be included directly." +#endif + +#ifndef __AS_ICON_PRIVATE_H +#define __AS_ICON_PRIVATE_H + +#include "as-icon.h" + +G_BEGIN_DECLS + +GBytes *as_icon_get_data (AsIcon *icon); +void as_icon_set_data (AsIcon *icon, + GBytes *data); + +GNode *as_icon_node_insert (AsIcon *icon, + GNode *parent, + gdouble api_version); +gboolean as_icon_node_parse (AsIcon *icon, + GNode *node, + GError **error); +gboolean as_icon_node_parse_dep11 (AsIcon *icon, + GNode *node, + GError **error); + +G_END_DECLS + +#endif /* __AS_ICON_PRIVATE_H */ diff --git a/libappstream-glib/as-icon.c b/libappstream-glib/as-icon.c new file mode 100644 index 0000000..00bfbdd --- /dev/null +++ b/libappstream-glib/as-icon.c @@ -0,0 +1,618 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2014 Richard Hughes <richard@hughsie.com> + * + * Licensed under the GNU Lesser General Public License Version 2.1 + * + * 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 + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * SECTION:as-icon + * @short_description: Object representing a single icon used in a screenshot. + * @include: appstream-glib.h + * @stability: Stable + * + * Screenshot may have multiple versions of an icon in different resolutions + * or aspect ratios. This object allows access to the location and size of a + * single icon. + * + * See also: #AsScreenshot + */ + +#include "config.h" + +#include "as-cleanup.h" +#include "as-icon-private.h" +#include "as-node-private.h" +#include "as-utils-private.h" +#include "as-yaml.h" + +typedef struct _AsIconPrivate AsIconPrivate; +struct _AsIconPrivate +{ + AsIconKind kind; + gchar *name; + gchar *prefix; + guint width; + guint height; + GdkPixbuf *pixbuf; + GBytes *data; +}; + +G_DEFINE_TYPE_WITH_PRIVATE (AsIcon, as_icon, G_TYPE_OBJECT) + +#define GET_PRIVATE(o) (as_icon_get_instance_private (o)) + +/** + * as_icon_finalize: + **/ +static void +as_icon_finalize (GObject *object) +{ + AsIcon *icon = AS_ICON (object); + AsIconPrivate *priv = GET_PRIVATE (icon); + + if (priv->pixbuf != NULL) + g_object_unref (priv->pixbuf); + if (priv->data != NULL) + g_bytes_unref (priv->data); + g_free (priv->name); + g_free (priv->prefix); + + G_OBJECT_CLASS (as_icon_parent_class)->finalize (object); +} + +/** + * as_icon_init: + **/ +static void +as_icon_init (AsIcon *icon) +{ +} + +/** + * as_icon_class_init: + **/ +static void +as_icon_class_init (AsIconClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + object_class->finalize = as_icon_finalize; +} + + +/** + * as_icon_kind_to_string: + * @icon_kind: the @AsIconKind. + * + * Converts the enumerated value to an text representation. + * + * Returns: string version of @icon_kind + * + * Since: 0.1.0 + **/ +const gchar * +as_icon_kind_to_string (AsIconKind icon_kind) +{ + if (icon_kind == AS_ICON_KIND_CACHED) + return "cached"; + if (icon_kind == AS_ICON_KIND_STOCK) + return "stock"; + if (icon_kind == AS_ICON_KIND_REMOTE) + return "remote"; + if (icon_kind == AS_ICON_KIND_EMBEDDED) + return "embedded"; + return "unknown"; +} + +/** + * as_icon_kind_from_string: + * @icon_kind: the string. + * + * Converts the text representation to an enumerated value. + * + * Returns: a #AsIconKind or %AS_ICON_KIND_UNKNOWN for unknown + * + * Since: 0.1.0 + **/ +AsIconKind +as_icon_kind_from_string (const gchar *icon_kind) +{ + if (g_strcmp0 (icon_kind, "cached") == 0) + return AS_ICON_KIND_CACHED; + if (g_strcmp0 (icon_kind, "stock") == 0) + return AS_ICON_KIND_STOCK; + if (g_strcmp0 (icon_kind, "remote") == 0) + return AS_ICON_KIND_REMOTE; + if (g_strcmp0 (icon_kind, "embedded") == 0) + return AS_ICON_KIND_EMBEDDED; + return AS_ICON_KIND_UNKNOWN; +} + +/** + * as_icon_get_name: + * @icon: a #AsIcon instance. + * + * Gets the full qualified URL for the icon, usually pointing at some mirror. + * + * Returns: URL + * + * Since: 0.3.1 + **/ +const gchar * +as_icon_get_name (AsIcon *icon) +{ + AsIconPrivate *priv = GET_PRIVATE (icon); + return priv->name; +} + +/** + * as_icon_get_prefix: + * @icon: a #AsIcon instance. + * + * Gets the suggested prefix the icon, including file extension. + * + * Returns: filename + * + * Since: 0.1.6 + **/ +const gchar * +as_icon_get_prefix (AsIcon *icon) +{ + AsIconPrivate *priv = GET_PRIVATE (icon); + return priv->prefix; +} + +/** + * as_icon_get_width: + * @icon: a #AsIcon instance. + * + * Gets the icon width. + * + * Returns: width in pixels + * + * Since: 0.3.1 + **/ +guint +as_icon_get_width (AsIcon *icon) +{ + AsIconPrivate *priv = GET_PRIVATE (icon); + return priv->width; +} + +/** + * as_icon_get_height: + * @icon: a #AsIcon instance. + * + * Gets the icon height. + * + * Returns: height in pixels + * + * Since: 0.3.1 + **/ +guint +as_icon_get_height (AsIcon *icon) +{ + AsIconPrivate *priv = GET_PRIVATE (icon); + return priv->height; +} + +/** + * as_icon_get_kind: + * @icon: a #AsIcon instance. + * + * Gets the icon kind. + * + * Returns: the #AsIconKind + * + * Since: 0.3.1 + **/ +AsIconKind +as_icon_get_kind (AsIcon *icon) +{ + AsIconPrivate *priv = GET_PRIVATE (icon); + return priv->kind; +} + +/** + * as_icon_get_pixbuf: + * @icon: a #AsIcon instance. + * + * Gets the icon pixbuf if set. + * + * Returns: (transfer none): the #GdkPixbuf, or %NULL + * + * Since: 0.3.1 + **/ +GdkPixbuf * +as_icon_get_pixbuf (AsIcon *icon) +{ + AsIconPrivate *priv = GET_PRIVATE (icon); + return priv->pixbuf; +} + +/** + * as_icon_get_data: + * @icon: a #AsIcon instance. + * + * Gets the icon data if set. + * + * Returns: (transfer none): the #GBytes, or %NULL + * + * Since: 0.3.1 + **/ +GBytes * +as_icon_get_data (AsIcon *icon) +{ + AsIconPrivate *priv = GET_PRIVATE (icon); + return priv->data; +} + +/** + * as_icon_set_name: + * @icon: a #AsIcon instance. + * @name: the URL. + * @name_len: the size of @name, or -1 if %NULL-terminated. + * + * Sets the fully-qualified mirror URL to use for the icon. + * + * Since: 0.3.1 + **/ +void +as_icon_set_name (AsIcon *icon, const gchar *name, gssize name_len) +{ + AsIconPrivate *priv = GET_PRIVATE (icon); + g_free (priv->name); + priv->name = as_strndup (name, name_len); +} + +/** + * as_icon_set_prefix: + * @icon: a #AsIcon instance. + * @prefix: the new filename prefix. + * + * Sets the icon prefix filename. + * + * Since: 0.1.6 + **/ +void +as_icon_set_prefix (AsIcon *icon, const gchar *prefix) +{ + AsIconPrivate *priv = GET_PRIVATE (icon); + g_free (priv->prefix); + priv->prefix = g_strdup (prefix); +} + +/** + * as_icon_set_width: + * @icon: a #AsIcon instance. + * @width: the width in pixels. + * + * Sets the icon width. + * + * Since: 0.3.1 + **/ +void +as_icon_set_width (AsIcon *icon, guint width) +{ + AsIconPrivate *priv = GET_PRIVATE (icon); + priv->width = width; +} + +/** + * as_icon_set_height: + * @icon: a #AsIcon instance. + * @height: the height in pixels. + * + * Sets the icon height. + * + * Since: 0.3.1 + **/ +void +as_icon_set_height (AsIcon *icon, guint height) +{ + AsIconPrivate *priv = GET_PRIVATE (icon); + priv->height = height; +} + +/** + * as_icon_set_kind: + * @icon: a #AsIcon instance. + * @kind: the #AsIconKind, e.g. %AS_ICON_KIND_STOCK. + * + * Sets the icon kind. + * + * Since: 0.3.1 + **/ +void +as_icon_set_kind (AsIcon *icon, AsIconKind kind) +{ + AsIconPrivate *priv = GET_PRIVATE (icon); + priv->kind = kind; +} + +/** + * as_icon_set_pixbuf: + * @icon: a #AsIcon instance. + * @pixbuf: the #GdkPixbuf, or %NULL + * + * Sets the icon pixbuf. + * + * Since: 0.3.1 + **/ +void +as_icon_set_pixbuf (AsIcon *icon, GdkPixbuf *pixbuf) +{ + AsIconPrivate *priv = GET_PRIVATE (icon); + + if (priv->pixbuf != NULL) + g_object_unref (priv->pixbuf); + if (pixbuf == NULL) { + priv->pixbuf = NULL; + return; + } + priv->pixbuf = g_object_ref (pixbuf); + priv->width = gdk_pixbuf_get_width (pixbuf); + priv->height = gdk_pixbuf_get_height (pixbuf); +} + +/** + * as_icon_set_data: + * @icon: a #AsIcon instance. + * @data: the #GBytes, or %NULL + * + * Sets the icon data. + * + * Since: 0.3.1 + **/ +void +as_icon_set_data (AsIcon *icon, GBytes *data) +{ + AsIconPrivate *priv = GET_PRIVATE (icon); + + if (priv->data != NULL) + g_bytes_unref (priv->data); + if (data == NULL) { + priv->data = NULL; + return; + } + priv->data = g_bytes_ref (data); +} + +/** + * as_icon_node_insert: (skip) + * @icon: a #AsIcon instance. + * @parent: the parent #GNode to use.. + * @api_version: the AppStream API version + * + * Inserts the icon into the DOM tree. + * + * Returns: (transfer none): A populated #GNode + * + * Since: 0.3.1 + **/ +GNode * +as_icon_node_insert (AsIcon *icon, GNode *parent, gdouble api_version) +{ + AsIconPrivate *priv = GET_PRIVATE (icon); + GNode *n; + _cleanup_free_ gchar *data = NULL; + + /* normal icon */ + if (priv->kind != AS_ICON_KIND_EMBEDDED) { + n = as_node_insert (parent, "icon", priv->name, 0, + "type", as_icon_kind_to_string (priv->kind), + NULL); + return n; + } + + /* embedded icon */ + n = as_node_insert (parent, "icon", NULL, 0, + "type", as_icon_kind_to_string (priv->kind), + NULL); + as_node_insert (n, "name", priv->name, 0, NULL); + data = g_base64_encode (g_bytes_get_data (priv->data, NULL), + g_bytes_get_size (priv->data)); + as_node_insert (n, "filecontent", data, 0, NULL); + return n; +} + +/** + * as_icon_node_parse_embedded: + **/ +static gboolean +as_icon_node_parse_embedded (AsIcon *icon, GNode *n, GError **error) +{ + AsIconPrivate *priv = GET_PRIVATE (icon); + GNode *c; + gsize size; + _cleanup_free_ guchar *data = NULL; + _cleanup_object_unref_ GdkPixbuf *pixbuf = NULL; + _cleanup_object_unref_ GInputStream *stream = NULL; + + /* get the icon name */ + c = as_node_find (n, "name"); + if (c == NULL) { + g_set_error_literal (error, + AS_NODE_ERROR, + AS_NODE_ERROR_FAILED, + "embedded icons needs <name>"); + return FALSE; + } + g_free (priv->name); + priv->name = as_node_take_data (c); + + /* parse the Base64 data */ + c = as_node_find (n, "filecontent"); + if (c == NULL) { + g_set_error_literal (error, + AS_NODE_ERROR, + AS_NODE_ERROR_FAILED, + "embedded icons needs <filecontent>"); + return FALSE; + } + data = g_base64_decode (as_node_get_data (c), &size); + stream = g_memory_input_stream_new_from_data (data, (gssize) size, NULL); + if (stream == NULL) { + g_set_error_literal (error, + AS_NODE_ERROR, + AS_NODE_ERROR_FAILED, + "failed to load embedded data"); + return FALSE; + } + + /* load the image */ + pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, error); + if (pixbuf == NULL) + return FALSE; + as_icon_set_pixbuf (icon, pixbuf); + + /* save the raw data */ + if (priv->data != NULL) + g_bytes_unref (priv->data); + priv->data = g_bytes_new (data, size); + + return TRUE; +} + +/** + * as_icon_node_parse: + * @icon: a #AsIcon instance. + * @node: a #GNode. + * @error: A #GError or %NULL. + * + * Populates the object from a DOM node. + * + * Returns: %TRUE for success + * + * Since: 0.3.1 + **/ +gboolean +as_icon_node_parse (AsIcon *icon, GNode *node, GError **error) +{ + AsIconPrivate *priv = GET_PRIVATE (icon); + const gchar *tmp; + + tmp = as_node_get_attribute (node, "type"); + as_icon_set_kind (icon, as_icon_kind_from_string (tmp)); + switch (priv->kind) { + case AS_ICON_KIND_EMBEDDED: + if (!as_icon_node_parse_embedded (icon, node, error)) + return FALSE; + break; + default: + g_free (priv->name); + priv->name = as_node_take_data (node); + /* FIXME: we assume this */ + priv->width = 64; + priv->height = 64; + break; + } + + return TRUE; +} + +/** + * as_icon_node_parse_dep11: + * @icon: a #AsIcon instance. + * @node: a #GNode. + * @error: A #GError or %NULL. + * + * Populates the object from a DEP-11 node. + * + * Returns: %TRUE for success + * + * Since: 0.3.1 + **/ +gboolean +as_icon_node_parse_dep11 (AsIcon *im, GNode *node, GError **error) +{ + if (g_strcmp0 (as_yaml_node_get_key (node), "cached") != 0) + return TRUE; + as_icon_set_name (im, as_yaml_node_get_value (node), -1); + as_icon_set_kind (im, AS_ICON_KIND_CACHED); + return TRUE; +} + +/** + * as_icon_load: + * @icon: a #AsIcon instance. + * @flags: a #AsIconLoadFlags, e.g. %AS_ICON_LOAD_FLAG_SEARCH_SIZE + * @error: A #GError or %NULL. + * + * Loads the icon into a local pixbuf. + * + * Returns: %TRUE for success + * + * Since: 0.3.1 + **/ +gboolean +as_icon_load (AsIcon *icon, AsIconLoadFlags flags, GError **error) +{ + AsIconPrivate *priv = GET_PRIVATE (icon); + _cleanup_free_ gchar *fn_fallback = NULL; + _cleanup_free_ gchar *fn_size = NULL; + _cleanup_free_ gchar *size_str = NULL; + _cleanup_object_unref_ GdkPixbuf *pixbuf = NULL; + + /* not set */ + if (priv->prefix == NULL) { + g_set_error (error, + AS_NODE_ERROR, + AS_NODE_ERROR_FAILED, + "unable to load '%s' as no prefix set", + priv->name); + return FALSE; + } + + /* try getting a pixbuf of the right size */ + if (flags & AS_ICON_LOAD_FLAG_SEARCH_SIZE) { + size_str = g_strdup_printf ("%ix%i", priv->width, priv->height); + fn_size = g_build_filename (priv->prefix, size_str, priv->name, NULL); + if (g_file_test (fn_size, G_FILE_TEST_EXISTS)) { + pixbuf = gdk_pixbuf_new_from_file (fn_size, error); + if (pixbuf == NULL) + return FALSE; + as_icon_set_pixbuf (icon, pixbuf); + return TRUE; + } + } + + /* fall back to the old location */ + fn_fallback = g_build_filename (priv->prefix, priv->name, NULL); + pixbuf = gdk_pixbuf_new_from_file (fn_fallback, error); + if (pixbuf == NULL) + return FALSE; + as_icon_set_pixbuf (icon, pixbuf); + return TRUE; +} + +/** + * as_icon_new: + * + * Creates a new #AsIcon. + * + * Returns: (transfer full): a #AsIcon + * + * Since: 0.3.1 + **/ +AsIcon * +as_icon_new (void) +{ + AsIcon *icon; + icon = g_object_new (AS_TYPE_ICON, NULL); + return AS_ICON (icon); +} diff --git a/libappstream-glib/as-icon.h b/libappstream-glib/as-icon.h new file mode 100644 index 0000000..f5188fe --- /dev/null +++ b/libappstream-glib/as-icon.h @@ -0,0 +1,134 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2014 Richard Hughes <richard@hughsie.com> + * + * Licensed under the GNU Lesser General Public License Version 2.1 + * + * 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 + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#if !defined (__APPSTREAM_GLIB_H) && !defined (AS_COMPILATION) +#error "Only <appstream-glib.h> can be included directly." +#endif + +#ifndef __AS_ICON_H +#define __AS_ICON_H + +#include <glib-object.h> +#include <gdk-pixbuf/gdk-pixbuf.h> + +#define AS_TYPE_ICON (as_icon_get_type()) +#define AS_ICON(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), AS_TYPE_ICON, AsIcon)) +#define AS_ICON_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST((cls), AS_TYPE_ICON, AsIconClass)) +#define AS_IS_ICON(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), AS_TYPE_ICON)) +#define AS_IS_ICON_CLASS(cls) (G_TYPE_CHECK_CLASS_TYPE((cls), AS_TYPE_ICON)) +#define AS_ICON_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), AS_TYPE_ICON, AsIconClass)) + +G_BEGIN_DECLS + +typedef struct _AsIcon AsIcon; +typedef struct _AsIconClass AsIconClass; + +struct _AsIcon +{ + GObject parent; +}; + +struct _AsIconClass +{ + GObjectClass parent_class; + /*< private >*/ + void (*_as_reserved1) (void); + void (*_as_reserved2) (void); + void (*_as_reserved3) (void); + void (*_as_reserved4) (void); + void (*_as_reserved5) (void); + void (*_as_reserved6) (void); + void (*_as_reserved7) (void); + void (*_as_reserved8) (void); +}; + +/** + * AsIconKind: + * @AS_ICON_KIND_UNKNOWN: Type invalid or not known + * @AS_ICON_KIND_STOCK: Stock icon or present in the generic icon theme + * @AS_ICON_KIND_CACHED: An icon shipped with the AppStream metadata + * @AS_ICON_KIND_REMOTE: An icon referenced by a remote URL + * @AS_ICON_KIND_EMBEDDED: An embedded Base64 icon + * + * The icon type. + **/ +typedef enum { + AS_ICON_KIND_UNKNOWN, /* Since: 0.1.0 */ + AS_ICON_KIND_STOCK, /* Since: 0.1.0 */ + AS_ICON_KIND_CACHED, /* Since: 0.1.0 */ + AS_ICON_KIND_REMOTE, /* Since: 0.1.0 */ + AS_ICON_KIND_EMBEDDED, /* Since: 0.3.1 */ + /*< private >*/ + AS_ICON_KIND_LAST +} AsIconKind; + +/** + * AsIconLoadFlags: + * @AS_ICON_LOAD_FLAG_NONE: No extra flags to use + * @AS_ICON_LOAD_FLAG_SEARCH_SIZE: Search first in a size-specific directory + * + * The flags to use when loading icons. + **/ +typedef enum { + AS_ICON_LOAD_FLAG_NONE = 0, /* Since: 0.3.1 */ + AS_ICON_LOAD_FLAG_SEARCH_SIZE = 1, /* Since: 0.3.1 */ + /*< private >*/ + AS_ICON_LOAD_FLAG_LAST +} AsIconLoadFlags; + +GType as_icon_get_type (void); +AsIcon *as_icon_new (void); + +/* helpers */ +const gchar *as_icon_kind_to_string (AsIconKind icon_kind); +AsIconKind as_icon_kind_from_string (const gchar *icon_kind); + +/* getters */ +const gchar *as_icon_get_name (AsIcon *icon); +const gchar *as_icon_get_prefix (AsIcon *icon); +guint as_icon_get_width (AsIcon *icon); +guint as_icon_get_height (AsIcon *icon); +AsIconKind as_icon_get_kind (AsIcon *icon); +GdkPixbuf *as_icon_get_pixbuf (AsIcon *icon); + +/* setters */ +void as_icon_set_name (AsIcon *icon, + const gchar *name, + gssize name_len); +void as_icon_set_prefix (AsIcon *icon, + const gchar *prefix); +void as_icon_set_width (AsIcon *icon, + guint width); +void as_icon_set_height (AsIcon *icon, + guint height); +void as_icon_set_kind (AsIcon *icon, + AsIconKind kind); +void as_icon_set_pixbuf (AsIcon *icon, + GdkPixbuf *pixbuf); + +/* object methods */ +gboolean as_icon_load (AsIcon *icon, + AsIconLoadFlags flags, + GError **error); + +G_END_DECLS + +#endif /* __AS_ICON_H */ diff --git a/libappstream-glib/as-self-test.c b/libappstream-glib/as-self-test.c index 09248bd..1bb8548 100644 --- a/libappstream-glib/as-self-test.c +++ b/libappstream-glib/as-self-test.c @@ -27,6 +27,7 @@ #include "as-app-private.h" #include "as-cleanup.h" #include "as-enums.h" +#include "as-icon-private.h" #include "as-image-private.h" #include "as-node-private.h" #include "as-problem.h" @@ -385,6 +386,136 @@ as_test_image_resize_func (void) } static void +as_test_icon_func (void) +{ + GError *error = NULL; + GNode *n; + GNode *root; + GString *xml; + const gchar *src = "<icon type=\"cached\">app.png</icon>"; + gboolean ret; + _cleanup_free_ gchar *prefix = NULL; + _cleanup_object_unref_ AsIcon *icon = NULL; + _cleanup_object_unref_ GdkPixbuf *pixbuf = NULL; + + icon = as_icon_new (); + + /* to object */ + root = as_node_from_xml (src, -1, 0, &error); + g_assert_no_error (error); + g_assert (root != NULL); + n = as_node_find (root, "icon"); + g_assert (n != NULL); + ret = as_icon_node_parse (icon, n, &error); + g_assert_no_error (error); + g_assert (ret); + as_node_unref (root); + + /* verify */ + g_assert_cmpint (as_icon_get_kind (icon), ==, AS_ICON_KIND_CACHED); + g_assert_cmpstr (as_icon_get_name (icon), ==, "app.png"); + g_assert_cmpint (as_icon_get_height (icon), ==, 64); + g_assert_cmpint (as_icon_get_width (icon), ==, 64); + g_assert (as_icon_get_pixbuf (icon) == NULL); + g_assert (as_icon_get_data (icon) == NULL); + + /* back to node */ + root = as_node_new (); + n = as_icon_node_insert (icon, root, 0.4); + xml = as_node_to_xml (n, AS_NODE_TO_XML_FLAG_NONE); + g_assert_cmpstr (xml->str, ==, src); + g_string_free (xml, TRUE); + as_node_unref (root); +} + +static void +as_test_icon_embedded_func (void) +{ + GError *error = NULL; + GNode *n; + GNode *root; + GString *xml; + const gchar *src = +"<icon type=\"embedded\"><name>app.png</name>" +"<filecontent>" +"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" +"</filecontent>" +"</icon>"; + gboolean ret; + _cleanup_object_unref_ AsIcon *icon = NULL; + _cleanup_object_unref_ GdkPixbuf *pixbuf = NULL; + + icon = as_icon_new (); + + /* to object */ + root = as_node_from_xml (src, -1, 0, &error); + g_assert_no_error (error); + g_assert (root != NULL); + n = as_node_find (root, "icon"); + g_assert (n != NULL); + ret = as_icon_node_parse (icon, n, &error); + g_assert_no_error (error); + g_assert (ret); + as_node_unref (root); + + /* verify */ + g_assert_cmpint (as_icon_get_kind (icon), ==, AS_ICON_KIND_EMBEDDED); + g_assert_cmpstr (as_icon_get_name (icon), ==, "app.png"); + g_assert_cmpint (as_icon_get_height (icon), ==, 32); + g_assert_cmpint (as_icon_get_width (icon), ==, 32); + g_assert (as_icon_get_data (icon) != NULL); + g_assert (as_icon_get_pixbuf (icon) != NULL); + + /* back to node */ + root = as_node_new (); + n = as_icon_node_insert (icon, root, 0.4); + xml = as_node_to_xml (n, AS_NODE_TO_XML_FLAG_NONE); + g_assert_cmpstr (xml->str, ==, src); + g_string_free (xml, TRUE); + as_node_unref (root); +} + +static void as_test_image_func (void) { GError *error = NULL; @@ -2757,6 +2888,8 @@ main (int argc, char **argv) g_test_add_func ("/AppStream/provide", as_test_provide_func); g_test_add_func ("/AppStream/release", as_test_release_func); g_test_add_func ("/AppStream/release{description}", as_test_release_desc_func); + g_test_add_func ("/AppStream/icon", as_test_icon_func); + g_test_add_func ("/AppStream/icon{embedded}", as_test_icon_embedded_func); g_test_add_func ("/AppStream/image", as_test_image_func); g_test_add_func ("/AppStream/image{resize}", as_test_image_resize_func); g_test_add_func ("/AppStream/image{alpha}", as_test_image_alpha_func); |