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