1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
2  *
3  * Copyright (C) 2014-2017 Richard Hughes <richard@hughsie.com>
4  *
5  * SPDX-License-Identifier: LGPL-2.1+
6  */
7 
8 /**
9  * SECTION:as-icon
10  * @short_description: Object representing a single icon used in a screenshot.
11  * @include: appstream-glib.h
12  * @stability: Stable
13  *
14  * Screenshot may have multiple versions of an icon in different resolutions
15  * or aspect ratios. This object allows access to the location and size of a
16  * single icon.
17  *
18  * See also: #AsScreenshot
19  */
20 
21 #include "config.h"
22 
23 #include "as-icon-private.h"
24 #include "as-node-private.h"
25 #include "as-ref-string.h"
26 #include "as-utils-private.h"
27 #include "as-yaml.h"
28 
29 typedef struct
30 {
31 	AsIconKind		 kind;
32 	AsRefString		*name;
33 	AsRefString		*url;
34 	AsRefString		*filename;
35 	AsRefString		*prefix;
36 	AsRefString		*prefix_private;
37 	gboolean		 prepend_size;
38 	guint			 width;
39 	guint			 height;
40 	guint			 scale;
41 	GdkPixbuf		*pixbuf;
42 	GBytes			*data;
43 } AsIconPrivate;
44 
G_DEFINE_TYPE_WITH_PRIVATE(AsIcon,as_icon,G_TYPE_OBJECT)45 G_DEFINE_TYPE_WITH_PRIVATE (AsIcon, as_icon, G_TYPE_OBJECT)
46 
47 #define GET_PRIVATE(o) (as_icon_get_instance_private (o))
48 
49 /**
50  * as_icon_error_quark:
51  *
52  * Return value: An error quark.
53  *
54  * Since: 0.3.1
55  **/
56 G_DEFINE_QUARK (as-icon-error-quark, as_icon_error)
57 
58 static void
59 as_icon_finalize (GObject *object)
60 {
61 	AsIcon *icon = AS_ICON (object);
62 	AsIconPrivate *priv = GET_PRIVATE (icon);
63 
64 	if (priv->pixbuf != NULL)
65 		g_object_unref (priv->pixbuf);
66 	if (priv->data != NULL)
67 		g_bytes_unref (priv->data);
68 	if (priv->name != NULL)
69 		as_ref_string_unref (priv->name);
70 	if (priv->url != NULL)
71 		as_ref_string_unref (priv->url);
72 	if (priv->filename != NULL)
73 		as_ref_string_unref (priv->filename);
74 	if (priv->prefix != NULL)
75 		as_ref_string_unref (priv->prefix);
76 	if (priv->prefix_private != NULL)
77 		as_ref_string_unref (priv->prefix_private);
78 
79 	G_OBJECT_CLASS (as_icon_parent_class)->finalize (object);
80 }
81 
82 static void
as_icon_init(AsIcon * icon)83 as_icon_init (AsIcon *icon)
84 {
85 }
86 
87 static void
as_icon_class_init(AsIconClass * klass)88 as_icon_class_init (AsIconClass *klass)
89 {
90 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
91 	object_class->finalize = as_icon_finalize;
92 }
93 
94 
95 /**
96  * as_icon_kind_to_string:
97  * @icon_kind: the @AsIconKind.
98  *
99  * Converts the enumerated value to an text representation.
100  *
101  * Returns: string version of @icon_kind
102  *
103  * Since: 0.1.0
104  **/
105 const gchar *
as_icon_kind_to_string(AsIconKind icon_kind)106 as_icon_kind_to_string (AsIconKind icon_kind)
107 {
108 	if (icon_kind == AS_ICON_KIND_CACHED)
109 		return "cached";
110 	if (icon_kind == AS_ICON_KIND_STOCK)
111 		return "stock";
112 	if (icon_kind == AS_ICON_KIND_REMOTE)
113 		return "remote";
114 	if (icon_kind == AS_ICON_KIND_EMBEDDED)
115 		return "embedded";
116 	if (icon_kind == AS_ICON_KIND_LOCAL)
117 		return "local";
118 	return "unknown";
119 }
120 
121 /**
122  * as_icon_kind_from_string:
123  * @icon_kind: the string.
124  *
125  * Converts the text representation to an enumerated value.
126  *
127  * Returns: a #AsIconKind or %AS_ICON_KIND_UNKNOWN for unknown
128  *
129  * Since: 0.1.0
130  **/
131 AsIconKind
as_icon_kind_from_string(const gchar * icon_kind)132 as_icon_kind_from_string (const gchar *icon_kind)
133 {
134 	if (g_strcmp0 (icon_kind, "cached") == 0)
135 		return AS_ICON_KIND_CACHED;
136 	if (g_strcmp0 (icon_kind, "stock") == 0)
137 		return AS_ICON_KIND_STOCK;
138 	if (g_strcmp0 (icon_kind, "remote") == 0)
139 		return AS_ICON_KIND_REMOTE;
140 	if (g_strcmp0 (icon_kind, "embedded") == 0)
141 		return AS_ICON_KIND_EMBEDDED;
142 	if (g_strcmp0 (icon_kind, "local") == 0)
143 		return AS_ICON_KIND_LOCAL;
144 	return AS_ICON_KIND_UNKNOWN;
145 }
146 
147 /**
148  * as_icon_get_name:
149  * @icon: a #AsIcon instance.
150  *
151  * Gets the name of the icon, e.g. "epiphany.png"
152  *
153  * Returns: the basename of the icon
154  *
155  * Since: 0.3.1
156  **/
157 const gchar *
as_icon_get_name(AsIcon * icon)158 as_icon_get_name (AsIcon *icon)
159 {
160 	AsIconPrivate *priv = GET_PRIVATE (icon);
161 	g_return_val_if_fail (AS_IS_ICON (icon), NULL);
162 	return priv->name;
163 }
164 
165 /**
166  * as_icon_get_url:
167  * @icon: a #AsIcon instance.
168  *
169  * Gets the full qualified URL for the icon, usually pointing at some mirror.
170  * NOTE: This is only set for icons of type %AS_ICON_KIND_REMOTE
171  *
172  * Returns: the fully qualified URL
173  *
174  * Since: 0.3.2
175  **/
176 const gchar *
as_icon_get_url(AsIcon * icon)177 as_icon_get_url (AsIcon *icon)
178 {
179 	AsIconPrivate *priv = GET_PRIVATE (icon);
180 	g_return_val_if_fail (AS_IS_ICON (icon), NULL);
181 	return priv->url;
182 }
183 
184 /**
185  * as_icon_get_filename:
186  * @icon: a #AsIcon instance.
187  *
188  * Gets the absolute path on disk of the icon.
189  * NOTE: This is only set for icons of type %AS_ICON_KIND_LOCAL
190  *
191  * Returns: the absolute filename on disk
192  *
193  * Since: 0.3.2
194  **/
195 const gchar *
as_icon_get_filename(AsIcon * icon)196 as_icon_get_filename (AsIcon *icon)
197 {
198 	AsIconPrivate *priv = GET_PRIVATE (icon);
199 	g_return_val_if_fail (AS_IS_ICON (icon), NULL);
200 	return priv->filename;
201 }
202 
203 /**
204  * as_icon_get_prefix:
205  * @icon: a #AsIcon instance.
206  *
207  * Gets the suggested prefix of the icon.
208  *
209  * Returns: filename
210  *
211  * Since: 0.1.6
212  **/
213 const gchar *
as_icon_get_prefix(AsIcon * icon)214 as_icon_get_prefix (AsIcon *icon)
215 {
216 	AsIconPrivate *priv = GET_PRIVATE (icon);
217 
218 	g_return_val_if_fail (AS_IS_ICON (icon), NULL);
219 
220 	/* only use the size if the metadata has width and height */
221 	if (priv->prepend_size && priv->prefix_private == NULL) {
222 		g_autofree gchar *sz = NULL;
223 		if (priv->scale > 1) {
224 			sz = g_strdup_printf ("%s/%ux%u@%u",
225 					      priv->prefix,
226 					      priv->width,
227 					      priv->height,
228 					      priv->scale);
229 		} else {
230 			sz = g_strdup_printf ("%s/%ux%u",
231 					      priv->prefix,
232 					      priv->width,
233 					      priv->height);
234 		}
235 		as_ref_string_assign_safe (&priv->prefix_private, sz);
236 	}
237 
238 	if (priv->prefix_private != NULL)
239 		return priv->prefix_private;
240 	return priv->prefix;
241 }
242 
243 /**
244  * as_icon_get_width:
245  * @icon: a #AsIcon instance.
246  *
247  * Gets the icon width.
248  *
249  * Returns: width in pixels
250  *
251  * Since: 0.3.1
252  **/
253 guint
as_icon_get_width(AsIcon * icon)254 as_icon_get_width (AsIcon *icon)
255 {
256 	AsIconPrivate *priv = GET_PRIVATE (icon);
257 	g_return_val_if_fail (AS_IS_ICON (icon), 0);
258 	return priv->width;
259 }
260 
261 /**
262  * as_icon_get_height:
263  * @icon: a #AsIcon instance.
264  *
265  * Gets the icon height.
266  *
267  * Returns: height in pixels
268  *
269  * Since: 0.3.1
270  **/
271 guint
as_icon_get_height(AsIcon * icon)272 as_icon_get_height (AsIcon *icon)
273 {
274 	AsIconPrivate *priv = GET_PRIVATE (icon);
275 	g_return_val_if_fail (AS_IS_ICON (icon), 0);
276 	return priv->height;
277 }
278 
279 /**
280  * as_icon_get_scale:
281  * @icon: a #AsIcon instance.
282  *
283  * Gets the icon scale.
284  *
285  * Returns: scale factor
286  *
287  * Since: 0.6.13
288  **/
289 guint
as_icon_get_scale(AsIcon * icon)290 as_icon_get_scale (AsIcon *icon)
291 {
292 	AsIconPrivate *priv = GET_PRIVATE (icon);
293 	g_return_val_if_fail (AS_IS_ICON (icon), 0);
294 	return priv->scale;
295 }
296 
297 /**
298  * as_icon_get_kind:
299  * @icon: a #AsIcon instance.
300  *
301  * Gets the icon kind.
302  *
303  * Returns: the #AsIconKind
304  *
305  * Since: 0.3.1
306  **/
307 AsIconKind
as_icon_get_kind(AsIcon * icon)308 as_icon_get_kind (AsIcon *icon)
309 {
310 	AsIconPrivate *priv = GET_PRIVATE (icon);
311 	g_return_val_if_fail (AS_IS_ICON (icon), AS_ICON_KIND_UNKNOWN);
312 	return priv->kind;
313 }
314 
315 /**
316  * as_icon_get_pixbuf:
317  * @icon: a #AsIcon instance.
318  *
319  * Gets the icon pixbuf if set.
320  *
321  * Returns: (transfer none): the #GdkPixbuf, or %NULL
322  *
323  * Since: 0.3.1
324  **/
325 GdkPixbuf *
as_icon_get_pixbuf(AsIcon * icon)326 as_icon_get_pixbuf (AsIcon *icon)
327 {
328 	AsIconPrivate *priv = GET_PRIVATE (icon);
329 	g_return_val_if_fail (AS_IS_ICON (icon), NULL);
330 	return priv->pixbuf;
331 }
332 
333 /**
334  * as_icon_get_data:
335  * @icon: a #AsIcon instance.
336  *
337  * Gets the icon data if set.
338  *
339  * Returns: (transfer none): the #GBytes, or %NULL
340  *
341  * Since: 0.3.1
342  **/
343 GBytes *
as_icon_get_data(AsIcon * icon)344 as_icon_get_data (AsIcon *icon)
345 {
346 	AsIconPrivate *priv = GET_PRIVATE (icon);
347 	g_return_val_if_fail (AS_IS_ICON (icon), NULL);
348 	return priv->data;
349 }
350 
351 /**
352  * as_icon_set_name:
353  * @icon: a #AsIcon instance.
354  * @name: the icon name, e.g. "gimp.png"
355  *
356  * Sets the basename to use for the icon.
357  *
358  * Since: 0.3.1
359  **/
360 void
as_icon_set_name(AsIcon * icon,const gchar * name)361 as_icon_set_name (AsIcon *icon, const gchar *name)
362 {
363 	AsIconPrivate *priv = GET_PRIVATE (icon);
364 	g_return_if_fail (AS_IS_ICON (icon));
365 	as_ref_string_assign_safe (&priv->name, name);
366 }
367 
368 /**
369  * as_icon_set_prefix:
370  * @icon: a #AsIcon instance.
371  * @prefix: the new filename prefix.
372  *
373  * Sets the icon prefix filename.
374  *
375  * Since: 0.1.6
376  **/
377 void
as_icon_set_prefix(AsIcon * icon,const gchar * prefix)378 as_icon_set_prefix (AsIcon *icon, const gchar *prefix)
379 {
380 	AsIconPrivate *priv = GET_PRIVATE (icon);
381 	g_return_if_fail (AS_IS_ICON (icon));
382 	as_ref_string_assign_safe (&priv->prefix, prefix);
383 }
384 
385 void
as_icon_set_prefix_rstr(AsIcon * icon,AsRefString * rstr)386 as_icon_set_prefix_rstr (AsIcon *icon, AsRefString *rstr)
387 {
388 	AsIconPrivate *priv = GET_PRIVATE (icon);
389 	g_return_if_fail (AS_IS_ICON (icon));
390 	as_ref_string_assign (&priv->prefix, rstr);
391 }
392 
393 /**
394  * as_icon_set_url:
395  * @icon: a #AsIcon instance.
396  * @url: the new icon URL.
397  *
398  * Sets the icon URL.
399  *
400  * Since: 0.3.2
401  **/
402 void
as_icon_set_url(AsIcon * icon,const gchar * url)403 as_icon_set_url (AsIcon *icon, const gchar *url)
404 {
405 	AsIconPrivate *priv = GET_PRIVATE (icon);
406 	g_return_if_fail (AS_IS_ICON (icon));
407 	as_ref_string_assign_safe (&priv->url, url);
408 }
409 
410 /**
411  * as_icon_set_filename:
412  * @icon: a #AsIcon instance.
413  * @filename: the new icon URL.
414  *
415  * Sets the icon absolute filename.
416  *
417  * Since: 0.3.2
418  **/
419 void
as_icon_set_filename(AsIcon * icon,const gchar * filename)420 as_icon_set_filename (AsIcon *icon, const gchar *filename)
421 {
422 	AsIconPrivate *priv = GET_PRIVATE (icon);
423 	g_return_if_fail (AS_IS_ICON (icon));
424 	as_ref_string_assign_safe (&priv->filename, filename);
425 }
426 
427 /**
428  * as_icon_set_width:
429  * @icon: a #AsIcon instance.
430  * @width: the width in pixels.
431  *
432  * Sets the icon width.
433  *
434  * Since: 0.3.1
435  **/
436 void
as_icon_set_width(AsIcon * icon,guint width)437 as_icon_set_width (AsIcon *icon, guint width)
438 {
439 	AsIconPrivate *priv = GET_PRIVATE (icon);
440 	g_return_if_fail (AS_IS_ICON (icon));
441 	priv->width = width;
442 }
443 
444 /**
445  * as_icon_set_height:
446  * @icon: a #AsIcon instance.
447  * @height: the height in pixels.
448  *
449  * Sets the icon height.
450  *
451  * Since: 0.3.1
452  **/
453 void
as_icon_set_height(AsIcon * icon,guint height)454 as_icon_set_height (AsIcon *icon, guint height)
455 {
456 	AsIconPrivate *priv = GET_PRIVATE (icon);
457 	g_return_if_fail (AS_IS_ICON (icon));
458 	priv->height = height;
459 }
460 
461 /**
462  * as_icon_set_scale:
463  * @icon: a #AsIcon instance.
464  * @scale: the scale as a factor.
465  *
466  * Sets the icon scale.
467  *
468  * Since: 0.6.13
469  **/
470 void
as_icon_set_scale(AsIcon * icon,guint scale)471 as_icon_set_scale (AsIcon *icon, guint scale)
472 {
473 	AsIconPrivate *priv = GET_PRIVATE (icon);
474 	g_return_if_fail (AS_IS_ICON (icon));
475 	priv->scale = scale;
476 }
477 
478 /**
479  * as_icon_set_kind:
480  * @icon: a #AsIcon instance.
481  * @kind: the #AsIconKind, e.g. %AS_ICON_KIND_STOCK.
482  *
483  * Sets the icon kind.
484  *
485  * Since: 0.3.1
486  **/
487 void
as_icon_set_kind(AsIcon * icon,AsIconKind kind)488 as_icon_set_kind (AsIcon *icon, AsIconKind kind)
489 {
490 	AsIconPrivate *priv = GET_PRIVATE (icon);
491 	g_return_if_fail (AS_IS_ICON (icon));
492 	priv->kind = kind;
493 }
494 
495 /**
496  * as_icon_set_pixbuf:
497  * @icon: a #AsIcon instance.
498  * @pixbuf: the #GdkPixbuf, or %NULL
499  *
500  * Sets the icon pixbuf.
501  *
502  * Since: 0.3.1
503  **/
504 void
as_icon_set_pixbuf(AsIcon * icon,GdkPixbuf * pixbuf)505 as_icon_set_pixbuf (AsIcon *icon, GdkPixbuf *pixbuf)
506 {
507 	AsIconPrivate *priv = GET_PRIVATE (icon);
508 	g_return_if_fail (AS_IS_ICON (icon));
509 	g_set_object (&priv->pixbuf, pixbuf);
510 	if (pixbuf != NULL) {
511 		priv->width = (guint) gdk_pixbuf_get_width (pixbuf);
512 		priv->height = (guint) gdk_pixbuf_get_height (pixbuf);
513 	}
514 }
515 
516 /**
517  * as_icon_set_data:
518  * @icon: a #AsIcon instance.
519  * @data: the #GBytes, or %NULL
520  *
521  * Sets the icon data.
522  *
523  * Since: 0.3.1
524  **/
525 void
as_icon_set_data(AsIcon * icon,GBytes * data)526 as_icon_set_data (AsIcon *icon, GBytes *data)
527 {
528 	AsIconPrivate *priv = GET_PRIVATE (icon);
529 
530 	g_return_if_fail (AS_IS_ICON (icon));
531 
532 	if (priv->data != NULL)
533 		g_bytes_unref (priv->data);
534 	if (data == NULL) {
535 		priv->data = NULL;
536 		return;
537 	}
538 	priv->data = g_bytes_ref (data);
539 }
540 
541 static GNode *
as_icon_node_insert_embedded(AsIcon * icon,GNode * parent,AsNodeContext * ctx)542 as_icon_node_insert_embedded (AsIcon *icon, GNode *parent, AsNodeContext *ctx)
543 {
544 	AsIconPrivate *priv = GET_PRIVATE (icon);
545 	GNode *n;
546 	g_autofree gchar *data = NULL;
547 
548 	/* embedded icon */
549 	n = as_node_insert (parent, "icon", NULL, 0,
550 			    "type", as_icon_kind_to_string (priv->kind),
551 			    NULL);
552 	as_node_add_attribute_as_uint (n, "width", priv->width);
553 	as_node_add_attribute_as_uint (n, "height", priv->height);
554 	if (priv->scale > 1)
555 		as_node_add_attribute_as_uint (n, "scale", priv->scale);
556 	as_node_insert (n, "name", priv->name, 0, NULL);
557 	data = g_base64_encode (g_bytes_get_data (priv->data, NULL),
558 				g_bytes_get_size (priv->data));
559 	as_node_insert (n, "filecontent", data,
560 			AS_NODE_INSERT_FLAG_BASE64_ENCODED, NULL);
561 	return n;
562 }
563 
564 /**
565  * as_icon_node_insert: (skip)
566  * @icon: a #AsIcon instance.
567  * @parent: the parent #GNode to use..
568  * @ctx: the #AsNodeContext
569  *
570  * Inserts the icon into the DOM tree.
571  *
572  * Returns: (transfer none): A populated #GNode
573  *
574  * Since: 0.3.1
575  **/
576 GNode *
as_icon_node_insert(AsIcon * icon,GNode * parent,AsNodeContext * ctx)577 as_icon_node_insert (AsIcon *icon, GNode *parent, AsNodeContext *ctx)
578 {
579 	AsIconPrivate *priv = GET_PRIVATE (icon);
580 	GNode *n;
581 
582 	g_return_val_if_fail (AS_IS_ICON (icon), NULL);
583 
584 	/* embedded icon */
585 	if (priv->kind == AS_ICON_KIND_EMBEDDED)
586 		return as_icon_node_insert_embedded (icon, parent, ctx);
587 
588 	/* other icons */
589 	switch (priv->kind) {
590 	case AS_ICON_KIND_REMOTE:
591 		n = as_node_insert (parent, "icon", priv->url, 0,
592 				    "type", as_icon_kind_to_string (priv->kind),
593 				    NULL);
594 		break;
595 	case AS_ICON_KIND_LOCAL:
596 		if (priv->filename != NULL) {
597 			n = as_node_insert (parent, "icon", priv->filename, 0,
598 					    "type", as_icon_kind_to_string (priv->kind),
599 					    NULL);
600 		} else {
601 			n = as_node_insert (parent, "icon", priv->name, 0,
602 					    "type", as_icon_kind_to_string (priv->kind),
603 					    NULL);
604 		}
605 		break;
606 	default:
607 		n = as_node_insert (parent, "icon", priv->name, 0, NULL);
608 		if (priv->kind != AS_ICON_KIND_UNKNOWN) {
609 			as_node_add_attribute (n, "type",
610 					       as_icon_kind_to_string (priv->kind));
611 		}
612 		break;
613 	}
614 	if (priv->kind == AS_ICON_KIND_CACHED) {
615 		if (priv->width > 0)
616 			as_node_add_attribute_as_uint (n, "width", priv->width);
617 		if (priv->height > 0)
618 			as_node_add_attribute_as_uint (n, "height", priv->height);
619 		if (priv->scale > 1)
620 			as_node_add_attribute_as_uint (n, "scale", priv->scale);
621 	}
622 	return n;
623 }
624 
625 static gboolean
as_icon_node_parse_embedded(AsIcon * icon,GNode * n,GError ** error)626 as_icon_node_parse_embedded (AsIcon *icon, GNode *n, GError **error)
627 {
628 	AsIconPrivate *priv = GET_PRIVATE (icon);
629 	GNode *c;
630 	gsize size;
631 	g_autofree guchar *data = NULL;
632 	g_autoptr(GdkPixbuf) pixbuf = NULL;
633 	g_autoptr(GInputStream) stream = NULL;
634 
635 	/* get the icon name */
636 	c = as_node_find (n, "name");
637 	if (c == NULL) {
638 		g_set_error_literal (error,
639 				     AS_ICON_ERROR,
640 				     AS_ICON_ERROR_FAILED,
641 				     "embedded icons needs <name>");
642 		return FALSE;
643 	}
644 	as_ref_string_assign (&priv->name, as_node_get_data_as_refstr (c));
645 
646 	/* parse the Base64 data */
647 	c = as_node_find (n, "filecontent");
648 	if (c == NULL) {
649 		g_set_error_literal (error,
650 				     AS_ICON_ERROR,
651 				     AS_ICON_ERROR_FAILED,
652 				     "embedded icons needs <filecontent>");
653 		return FALSE;
654 	}
655 	data = g_base64_decode (as_node_get_data (c), &size);
656 	stream = g_memory_input_stream_new_from_data (data, (gssize) size, NULL);
657 	if (stream == NULL) {
658 		g_set_error_literal (error,
659 				     AS_ICON_ERROR,
660 				     AS_ICON_ERROR_FAILED,
661 				     "failed to load embedded data");
662 		return FALSE;
663 	}
664 
665 	/* load the image */
666 	pixbuf = gdk_pixbuf_new_from_stream (stream, NULL, error);
667 	if (pixbuf == NULL)
668 		return FALSE;
669 	as_icon_set_pixbuf (icon, pixbuf);
670 
671 	/* save the raw data */
672 	if (priv->data != NULL)
673 		g_bytes_unref (priv->data);
674 	priv->data = g_bytes_new (data, size);
675 
676 	return TRUE;
677 }
678 
679 /**
680  * as_icon_node_parse:
681  * @icon: a #AsIcon instance.
682  * @node: a #GNode.
683  * @ctx: a #AsNodeContext.
684  * @error: A #GError or %NULL.
685  *
686  * Populates the object from a DOM node.
687  *
688  * Returns: %TRUE for success
689  *
690  * Since: 0.3.1
691  **/
692 gboolean
as_icon_node_parse(AsIcon * icon,GNode * node,AsNodeContext * ctx,GError ** error)693 as_icon_node_parse (AsIcon *icon, GNode *node,
694 		    AsNodeContext *ctx, GError **error)
695 {
696 	AsIconPrivate *priv = GET_PRIVATE (icon);
697 	AsRefString *str;
698 	guint size;
699 
700 	g_return_val_if_fail (AS_IS_ICON (icon), FALSE);
701 
702 	str = as_node_get_attribute_as_refstr (node, "type");
703 	as_icon_set_kind (icon, as_icon_kind_from_string (str));
704 	switch (priv->kind) {
705 	case AS_ICON_KIND_EMBEDDED:
706 		if (!as_icon_node_parse_embedded (icon, node, error))
707 			return FALSE;
708 		break;
709 	default:
710 
711 		/* preserve the URL for remote icons */
712 		str = as_node_get_data_as_refstr (node);
713 		if (str == NULL) {
714 			g_set_error (error,
715 				     AS_ICON_ERROR,
716 				     AS_ICON_ERROR_FAILED,
717 				     "no data for icon of type %s",
718 				     as_icon_kind_to_string (priv->kind));
719 			return FALSE;
720 		}
721 		if (priv->kind == AS_ICON_KIND_REMOTE)
722 			as_ref_string_assign (&priv->url, str);
723 		else if (priv->kind == AS_ICON_KIND_LOCAL)
724 			as_ref_string_assign (&priv->filename, str);
725 
726 		/* store the name without any prefix */
727 		if (g_strstr_len (str, -1, "/") == NULL) {
728 			as_ref_string_assign (&priv->name, str);
729 		} else {
730 			g_autofree gchar *basename = NULL;
731 			basename = g_path_get_basename (str);
732 			as_icon_set_name (icon, basename);
733 		}
734 
735 		/* width is optional, assume 64px if missing */
736 		priv->prepend_size = TRUE;
737 		size = as_node_get_attribute_as_uint (node, "width");
738 		if (size == G_MAXUINT) {
739 			size = 64;
740 			priv->prepend_size = FALSE;
741 		}
742 		priv->width = size;
743 
744 		/* height is optional, assume 64px if missing */
745 		size = as_node_get_attribute_as_uint (node, "height");
746 		if (size == G_MAXUINT) {
747 			size = 64;
748 			priv->prepend_size = FALSE;
749 		}
750 		priv->height = size;
751 
752 		/* scale is optional, assume 1 if missing */
753 		size = as_node_get_attribute_as_uint (node, "scale");
754 		if (size == G_MAXUINT)
755 			size = 1;
756 		priv->scale = size;
757 		break;
758 	}
759 
760 	return TRUE;
761 }
762 
763 /**
764  * as_icon_node_parse_dep11:
765  * @icon: a #AsIcon instance.
766  * @node: a #GNode.
767  * @ctx: a #AsNodeContext.
768  * @error: A #GError or %NULL.
769  *
770  * Populates the object from a DEP-11 node.
771  *
772  * Returns: %TRUE for success
773  *
774  * Since: 0.3.1
775  **/
776 gboolean
as_icon_node_parse_dep11(AsIcon * icon,GNode * node,AsNodeContext * ctx,GError ** error)777 as_icon_node_parse_dep11 (AsIcon *icon, GNode *node,
778 			  AsNodeContext *ctx, GError **error)
779 {
780 	GNode *n;
781 	AsIconPrivate *priv = GET_PRIVATE (icon);
782 
783 	g_return_val_if_fail (AS_IS_ICON (icon), FALSE);
784 
785 	for (n = node->children; n != NULL; n = n->next) {
786 		const gchar *key;
787 		guint size;
788 
789 		key = as_yaml_node_get_key (n);
790 		if (g_strcmp0 (key, "width") == 0) {
791 			size = as_yaml_node_get_value_as_uint (n);
792 			if (size == G_MAXUINT)
793 				size = 64;
794 			priv->width = size;
795 		} else if (g_strcmp0 (key, "height") == 0) {
796 			size = as_yaml_node_get_value_as_uint (n);
797 			if (size == G_MAXUINT)
798 				size = 64;
799 			priv->height = size;
800 		} else if (g_strcmp0 (key, "scale") == 0) {
801 			size = as_yaml_node_get_value_as_uint (n);
802 			if (size == G_MAXUINT)
803 				size = 1;
804 			priv->scale = size;
805 		} else {
806 			if (priv->kind == AS_ICON_KIND_REMOTE) {
807 				if (g_strcmp0 (key, "url") == 0) {
808 					const gchar *media_baseurl;
809 					media_baseurl = as_node_context_get_media_base_url (ctx);
810 					if (media_baseurl == NULL) {
811 						/* no baseurl, we can just set the value as URL */
812 						as_icon_set_url (icon, as_yaml_node_get_value (n));
813 					} else {
814 						/* handle the media baseurl */
815 						g_autofree gchar *url = NULL;
816 						url = g_build_filename (media_baseurl,
817 									as_yaml_node_get_value (n),
818 									NULL);
819 						as_icon_set_url (icon, url);
820 					}
821 				}
822 			} else {
823 				if (g_strcmp0 (key, "name") == 0) {
824 					const gchar *icon_name;
825 					icon_name = as_yaml_node_get_value (n);
826 
827 					if (g_str_has_prefix (icon_name, "/"))
828 						as_icon_set_filename (icon, icon_name);
829 					else
830 						as_icon_set_name (icon, icon_name);
831 				}
832 			}
833 		}
834 	}
835 
836 	return TRUE;
837 }
838 
839 /**
840  * as_icon_load:
841  * @icon: a #AsIcon instance.
842  * @flags: a #AsIconLoadFlags, e.g. %AS_ICON_LOAD_FLAG_SEARCH_SIZE
843  * @error: A #GError or %NULL.
844  *
845  * Loads the icon into a local pixbuf.
846  *
847  * Returns: %TRUE for success
848  *
849  * Since: 0.3.1
850  **/
851 gboolean
as_icon_load(AsIcon * icon,AsIconLoadFlags flags,GError ** error)852 as_icon_load (AsIcon *icon, AsIconLoadFlags flags, GError **error)
853 {
854 	AsIconPrivate *priv = GET_PRIVATE (icon);
855 	g_autofree gchar *fn_fallback = NULL;
856 	g_autoptr(GdkPixbuf) pixbuf = NULL;
857 
858 	g_return_val_if_fail (AS_IS_ICON (icon), FALSE);
859 
860 	/* absolute filename */
861 	if (priv->kind == AS_ICON_KIND_LOCAL) {
862 		if (priv->filename == NULL) {
863 			g_set_error (error,
864 				     AS_ICON_ERROR,
865 				     AS_ICON_ERROR_FAILED,
866 				     "unable to load '%s' as no filename set",
867 				     priv->name);
868 			return FALSE;
869 		}
870 		pixbuf = gdk_pixbuf_new_from_file_at_size (priv->filename,
871 							   (gint) priv->width,
872 							   (gint) priv->height,
873 							   error);
874 		if (pixbuf == NULL)
875 			return FALSE;
876 		as_icon_set_pixbuf (icon, pixbuf);
877 		return TRUE;
878 	}
879 
880 	/* not set */
881 	if (priv->prefix == NULL) {
882 		g_set_error (error,
883 			     AS_ICON_ERROR,
884 			     AS_ICON_ERROR_FAILED,
885 			     "unable to load '%s' as no prefix set",
886 			     priv->name);
887 		return FALSE;
888 	}
889 
890 	/* try getting a pixbuf of the right size */
891 	if (flags & AS_ICON_LOAD_FLAG_SEARCH_SIZE) {
892 		guint widths[] = { priv->width, 64, 128, 0 };
893 		guint height[] = { priv->height, 64, 128, 0 };
894 		guint i;
895 		for (i = 0; widths[i] != 0; i++) {
896 			g_autofree gchar *fn_size = NULL;
897 			g_autofree gchar *size_str = NULL;
898 			size_str = g_strdup_printf ("%ux%u", widths[i], height[i]);
899 			fn_size = g_build_filename (priv->prefix, size_str, priv->name, NULL);
900 			if (g_file_test (fn_size, G_FILE_TEST_EXISTS)) {
901 				pixbuf = gdk_pixbuf_new_from_file (fn_size, error);
902 				if (pixbuf == NULL)
903 					return FALSE;
904 				as_icon_set_pixbuf (icon, pixbuf);
905 				return TRUE;
906 			}
907 		}
908 	}
909 
910 	/* fall back to the old location */
911 	fn_fallback = g_build_filename (priv->prefix, priv->name, NULL);
912 	pixbuf = gdk_pixbuf_new_from_file (fn_fallback, error);
913 	if (pixbuf == NULL)
914 		return FALSE;
915 	as_icon_set_pixbuf (icon, pixbuf);
916 	return TRUE;
917 }
918 
919 /**
920  * as_icon_convert_to_kind:
921  * @icon: a #AsIcon instance.
922  * @kind: a %AsIconKind, e.g. #AS_ICON_KIND_EMBEDDED
923  * @error: A #GError or %NULL.
924  *
925  * Converts the icon from one kind to another.
926  *
927  * Returns: %TRUE for success
928  *
929  * Since: 0.3.1
930  **/
931 gboolean
as_icon_convert_to_kind(AsIcon * icon,AsIconKind kind,GError ** error)932 as_icon_convert_to_kind (AsIcon *icon, AsIconKind kind, GError **error)
933 {
934 	AsIconPrivate *priv = GET_PRIVATE (icon);
935 
936 	g_return_val_if_fail (AS_IS_ICON (icon), FALSE);
937 
938 	/* these can't be converted */
939 	if (priv->kind == AS_ICON_KIND_STOCK ||
940 	    priv->kind == AS_ICON_KIND_REMOTE)
941 		return TRUE;
942 
943 	/* no change */
944 	if (priv->kind == kind)
945 		return TRUE;
946 
947 	/* cached -> embedded */
948 	if (priv->kind == AS_ICON_KIND_CACHED && kind == AS_ICON_KIND_EMBEDDED) {
949 		gsize data_size;
950 		g_autoptr(GBytes) tmp = NULL;
951 		g_autofree gchar *data = NULL;
952 
953 		/* load the pixbuf and save it to a PNG buffer */
954 		if (priv->pixbuf == NULL) {
955 			if (!as_icon_load (icon, AS_ICON_LOAD_FLAG_SEARCH_SIZE, error))
956 				return FALSE;
957 		}
958 		if (!gdk_pixbuf_save_to_buffer (priv->pixbuf, &data, &data_size,
959 						"png", error, NULL))
960 			return FALSE;
961 
962 		/* set the PNG buffer to a blob of data */
963 		tmp = g_bytes_new (data, data_size);
964 		as_icon_set_data (icon, tmp);
965 		as_icon_set_kind (icon, kind);
966 		return TRUE;
967 	}
968 
969 	/* cached -> embedded */
970 	if (priv->kind == AS_ICON_KIND_EMBEDDED && kind == AS_ICON_KIND_CACHED) {
971 		g_autofree gchar *size_str = NULL;
972 		g_autofree gchar *path = NULL;
973 		g_autofree gchar *fn = NULL;
974 
975 		/* ensure the parent path exists */
976 		size_str = g_strdup_printf ("%ux%u", priv->width, priv->height);
977 		path = g_build_filename (priv->prefix, size_str, NULL);
978 		if (g_mkdir_with_parents (path, 0700) != 0) {
979 			g_set_error (error,
980 				     AS_ICON_ERROR,
981 				     AS_ICON_ERROR_FAILED,
982 				     "Failed to create: %s", path);
983 			return FALSE;
984 		}
985 
986 		/* save the pixbuf */
987 		fn = g_build_filename (path, priv->name, NULL);
988 		if (!gdk_pixbuf_save (priv->pixbuf, fn, "png", error, NULL))
989 			return FALSE;
990 		as_icon_set_kind (icon, kind);
991 		return TRUE;
992 	}
993 
994 	/* not supported */
995 	g_set_error (error,
996 		     AS_ICON_ERROR,
997 		     AS_ICON_ERROR_FAILED,
998 		     "converting %s to %s is not supported",
999 		     as_icon_kind_to_string (priv->kind),
1000 		     as_icon_kind_to_string (kind));
1001 	return FALSE;
1002 }
1003 
1004 /**
1005  * as_icon_new:
1006  *
1007  * Creates a new #AsIcon.
1008  *
1009  * Returns: (transfer full): a #AsIcon
1010  *
1011  * Since: 0.3.1
1012  **/
1013 AsIcon *
as_icon_new(void)1014 as_icon_new (void)
1015 {
1016 	AsIcon *icon;
1017 	icon = g_object_new (AS_TYPE_ICON, NULL);
1018 	return AS_ICON (icon);
1019 }
1020