summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Hughes <richard@hughsie.com>2014-10-01 14:10:48 +0100
committerRichard Hughes <richard@hughsie.com>2014-10-01 15:16:41 +0100
commit475e8d18f2154763ccad6bdeb0cef5c33526e9e8 (patch)
treea84f297ac8692ea5d02b5cedd039d696c8630adb
parent82a68844ae87ca36019cd95fcdbcde15dbfa1a46 (diff)
downloadappstream-glib-475e8d18f2154763ccad6bdeb0cef5c33526e9e8.tar.gz
Add AsIcon as a new abstract icon object
-rw-r--r--docs/api/appstream-glib-docs.sgml1
-rw-r--r--libappstream-glib/Makefile.am5
-rw-r--r--libappstream-glib/appstream-glib.h1
-rw-r--r--libappstream-glib/as-enums.c48
-rw-r--r--libappstream-glib/as-enums.h23
-rw-r--r--libappstream-glib/as-icon-private.h49
-rw-r--r--libappstream-glib/as-icon.c618
-rw-r--r--libappstream-glib/as-icon.h134
-rw-r--r--libappstream-glib/as-self-test.c133
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);