Branch data Line data Source code
1 : : /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 : :
3 : : /* GIO - GLib Input, Output and Streaming Library
4 : : *
5 : : * Copyright (C) 2006-2007 Red Hat, Inc.
6 : : *
7 : : * SPDX-License-Identifier: LGPL-2.1-or-later
8 : : *
9 : : * This library is free software; you can redistribute it and/or
10 : : * modify it under the terms of the GNU Lesser General Public
11 : : * License as published by the Free Software Foundation; either
12 : : * version 2.1 of the License, or (at your option) any later version.
13 : : *
14 : : * This library is distributed in the hope that it will be useful,
15 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 : : * Lesser General Public License for more details.
18 : : *
19 : : * You should have received a copy of the GNU Lesser General
20 : : * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
21 : : *
22 : : * Author: Matthias Clasen <mclasen@redhat.com>
23 : : * Clemens N. Buss <cebuzz@gmail.com>
24 : : */
25 : :
26 : : #include <config.h>
27 : :
28 : : #include <string.h>
29 : :
30 : : #include "gemblemedicon.h"
31 : : #include "glibintl.h"
32 : : #include "gioerror.h"
33 : :
34 : :
35 : : /**
36 : : * GEmblemedIcon:
37 : : *
38 : : * `GEmblemedIcon` is an implementation of [iface@Gio.Icon] that supports
39 : : * adding an emblem to an icon. Adding multiple emblems to an
40 : : * icon is ensured via [method@Gio.EmblemedIcon.add_emblem].
41 : : *
42 : : * Note that `GEmblemedIcon` allows no control over the position
43 : : * of the emblems. See also [class@Gio.Emblem] for more information.
44 : : **/
45 : :
46 : : enum {
47 : : PROP_GICON = 1,
48 : : NUM_PROPERTIES
49 : : };
50 : :
51 : : struct _GEmblemedIconPrivate {
52 : : GIcon *icon;
53 : : GList *emblems;
54 : : };
55 : :
56 : : static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
57 : :
58 : : static void g_emblemed_icon_icon_iface_init (GIconIface *iface);
59 : :
60 : 98 : G_DEFINE_TYPE_WITH_CODE (GEmblemedIcon, g_emblemed_icon, G_TYPE_OBJECT,
61 : : G_ADD_PRIVATE (GEmblemedIcon)
62 : : G_IMPLEMENT_INTERFACE (G_TYPE_ICON,
63 : : g_emblemed_icon_icon_iface_init))
64 : :
65 : :
66 : : static void
67 : 8 : g_emblemed_icon_finalize (GObject *object)
68 : : {
69 : : GEmblemedIcon *emblemed;
70 : :
71 : 8 : emblemed = G_EMBLEMED_ICON (object);
72 : :
73 : 8 : g_clear_object (&emblemed->priv->icon);
74 : 8 : g_list_free_full (emblemed->priv->emblems, g_object_unref);
75 : :
76 : 8 : (*G_OBJECT_CLASS (g_emblemed_icon_parent_class)->finalize) (object);
77 : 8 : }
78 : :
79 : : static void
80 : 8 : g_emblemed_icon_set_property (GObject *object,
81 : : guint property_id,
82 : : const GValue *value,
83 : : GParamSpec *pspec)
84 : : {
85 : 8 : GEmblemedIcon *self = G_EMBLEMED_ICON (object);
86 : :
87 : 8 : switch (property_id)
88 : : {
89 : 8 : case PROP_GICON:
90 : 8 : self->priv->icon = g_value_dup_object (value);
91 : 8 : break;
92 : 0 : default:
93 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
94 : 0 : break;
95 : : }
96 : 8 : }
97 : :
98 : : static void
99 : 2 : g_emblemed_icon_get_property (GObject *object,
100 : : guint property_id,
101 : : GValue *value,
102 : : GParamSpec *pspec)
103 : : {
104 : 2 : GEmblemedIcon *self = G_EMBLEMED_ICON (object);
105 : :
106 : 2 : switch (property_id)
107 : : {
108 : 2 : case PROP_GICON:
109 : 2 : g_value_set_object (value, self->priv->icon);
110 : 2 : break;
111 : 0 : default:
112 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
113 : 0 : break;
114 : : }
115 : 2 : }
116 : :
117 : : static void
118 : 3 : g_emblemed_icon_class_init (GEmblemedIconClass *klass)
119 : : {
120 : 3 : GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
121 : :
122 : 3 : gobject_class->finalize = g_emblemed_icon_finalize;
123 : 3 : gobject_class->set_property = g_emblemed_icon_set_property;
124 : 3 : gobject_class->get_property = g_emblemed_icon_get_property;
125 : :
126 : : /**
127 : : * GEmblemedIcon:gicon:
128 : : *
129 : : * The [iface@Gio.Icon] to attach emblems to.
130 : : *
131 : : * Since: 2.18
132 : : */
133 : 3 : properties[PROP_GICON] =
134 : 3 : g_param_spec_object ("gicon", NULL, NULL,
135 : : G_TYPE_ICON,
136 : : G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
137 : :
138 : 3 : g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
139 : 3 : }
140 : :
141 : : static void
142 : 8 : g_emblemed_icon_init (GEmblemedIcon *emblemed)
143 : : {
144 : 8 : emblemed->priv = g_emblemed_icon_get_instance_private (emblemed);
145 : 8 : }
146 : :
147 : : /**
148 : : * g_emblemed_icon_new:
149 : : * @icon: a #GIcon
150 : : * @emblem: (nullable): a #GEmblem, or %NULL
151 : : *
152 : : * Creates a new emblemed icon for @icon with the emblem @emblem.
153 : : *
154 : : * Returns: (transfer full) (type GEmblemedIcon): a new #GIcon
155 : : *
156 : : * Since: 2.18
157 : : **/
158 : : GIcon *
159 : 6 : g_emblemed_icon_new (GIcon *icon,
160 : : GEmblem *emblem)
161 : : {
162 : : GEmblemedIcon *emblemed;
163 : :
164 : 6 : g_return_val_if_fail (G_IS_ICON (icon), NULL);
165 : 6 : g_return_val_if_fail (!G_IS_EMBLEM (icon), NULL);
166 : :
167 : 6 : emblemed = G_EMBLEMED_ICON (g_object_new (G_TYPE_EMBLEMED_ICON,
168 : : "gicon", icon,
169 : : NULL));
170 : :
171 : 6 : if (emblem != NULL)
172 : 4 : g_emblemed_icon_add_emblem (emblemed, emblem);
173 : :
174 : 6 : return G_ICON (emblemed);
175 : : }
176 : :
177 : :
178 : : /**
179 : : * g_emblemed_icon_get_icon:
180 : : * @emblemed: a #GEmblemedIcon
181 : : *
182 : : * Gets the main icon for @emblemed.
183 : : *
184 : : * Returns: (transfer none): a #GIcon that is owned by @emblemed
185 : : *
186 : : * Since: 2.18
187 : : **/
188 : : GIcon *
189 : 1 : g_emblemed_icon_get_icon (GEmblemedIcon *emblemed)
190 : : {
191 : 1 : g_return_val_if_fail (G_IS_EMBLEMED_ICON (emblemed), NULL);
192 : :
193 : 1 : return emblemed->priv->icon;
194 : : }
195 : :
196 : : /**
197 : : * g_emblemed_icon_get_emblems:
198 : : * @emblemed: a #GEmblemedIcon
199 : : *
200 : : * Gets the list of emblems for the @icon.
201 : : *
202 : : * Returns: (element-type Gio.Emblem) (transfer none): a #GList of
203 : : * #GEmblems that is owned by @emblemed
204 : : *
205 : : * Since: 2.18
206 : : **/
207 : :
208 : : GList *
209 : 3 : g_emblemed_icon_get_emblems (GEmblemedIcon *emblemed)
210 : : {
211 : 3 : g_return_val_if_fail (G_IS_EMBLEMED_ICON (emblemed), NULL);
212 : :
213 : 3 : return emblemed->priv->emblems;
214 : : }
215 : :
216 : : /**
217 : : * g_emblemed_icon_clear_emblems:
218 : : * @emblemed: a #GEmblemedIcon
219 : : *
220 : : * Removes all the emblems from @icon.
221 : : *
222 : : * Since: 2.28
223 : : **/
224 : : void
225 : 1 : g_emblemed_icon_clear_emblems (GEmblemedIcon *emblemed)
226 : : {
227 : 1 : g_return_if_fail (G_IS_EMBLEMED_ICON (emblemed));
228 : :
229 : 1 : if (emblemed->priv->emblems == NULL)
230 : 0 : return;
231 : :
232 : 1 : g_list_free_full (emblemed->priv->emblems, g_object_unref);
233 : 1 : emblemed->priv->emblems = NULL;
234 : : }
235 : :
236 : : static gint
237 : 5 : g_emblem_comp (GEmblem *a,
238 : : GEmblem *b)
239 : : {
240 : 5 : guint hash_a = g_icon_hash (G_ICON (a));
241 : 5 : guint hash_b = g_icon_hash (G_ICON (b));
242 : :
243 : 5 : if(hash_a < hash_b)
244 : 0 : return -1;
245 : :
246 : 5 : if(hash_a == hash_b)
247 : 0 : return 0;
248 : :
249 : 5 : return 1;
250 : : }
251 : :
252 : : /**
253 : : * g_emblemed_icon_add_emblem:
254 : : * @emblemed: a #GEmblemedIcon
255 : : * @emblem: a #GEmblem
256 : : *
257 : : * Adds @emblem to the #GList of #GEmblems.
258 : : *
259 : : * Since: 2.18
260 : : **/
261 : : void
262 : 11 : g_emblemed_icon_add_emblem (GEmblemedIcon *emblemed,
263 : : GEmblem *emblem)
264 : : {
265 : 11 : g_return_if_fail (G_IS_EMBLEMED_ICON (emblemed));
266 : 11 : g_return_if_fail (G_IS_EMBLEM (emblem));
267 : :
268 : 11 : g_object_ref (emblem);
269 : 11 : emblemed->priv->emblems = g_list_insert_sorted (emblemed->priv->emblems, emblem,
270 : : (GCompareFunc) g_emblem_comp);
271 : : }
272 : :
273 : : static guint
274 : 3 : g_emblemed_icon_hash (GIcon *icon)
275 : : {
276 : 3 : GEmblemedIcon *emblemed = G_EMBLEMED_ICON (icon);
277 : : GList *list;
278 : 3 : guint hash = g_icon_hash (emblemed->priv->icon);
279 : :
280 : 7 : for (list = emblemed->priv->emblems; list != NULL; list = list->next)
281 : 4 : hash ^= g_icon_hash (G_ICON (list->data));
282 : :
283 : 3 : return hash;
284 : : }
285 : :
286 : : static gboolean
287 : 4 : g_emblemed_icon_equal (GIcon *icon1,
288 : : GIcon *icon2)
289 : : {
290 : 4 : GEmblemedIcon *emblemed1 = G_EMBLEMED_ICON (icon1);
291 : 4 : GEmblemedIcon *emblemed2 = G_EMBLEMED_ICON (icon2);
292 : : GList *list1, *list2;
293 : :
294 : 4 : if (!g_icon_equal (emblemed1->priv->icon, emblemed2->priv->icon))
295 : 0 : return FALSE;
296 : :
297 : 4 : list1 = emblemed1->priv->emblems;
298 : 4 : list2 = emblemed2->priv->emblems;
299 : :
300 : 11 : while (list1 && list2)
301 : : {
302 : 7 : if (!g_icon_equal (G_ICON (list1->data), G_ICON (list2->data)))
303 : 0 : return FALSE;
304 : :
305 : 7 : list1 = list1->next;
306 : 7 : list2 = list2->next;
307 : : }
308 : :
309 : 4 : return list1 == NULL && list2 == NULL;
310 : : }
311 : :
312 : : static gboolean
313 : 1 : g_emblemed_icon_to_tokens (GIcon *icon,
314 : : GPtrArray *tokens,
315 : : gint *out_version)
316 : : {
317 : 1 : GEmblemedIcon *emblemed_icon = G_EMBLEMED_ICON (icon);
318 : : GList *l;
319 : : char *s;
320 : :
321 : : /* GEmblemedIcons are encoded as
322 : : *
323 : : * <encoded_icon> [<encoded_emblem_icon>]*
324 : : */
325 : :
326 : 1 : g_return_val_if_fail (out_version != NULL, FALSE);
327 : :
328 : 1 : *out_version = 0;
329 : :
330 : 1 : s = g_icon_to_string (emblemed_icon->priv->icon);
331 : 1 : if (s == NULL)
332 : 0 : return FALSE;
333 : :
334 : 1 : g_ptr_array_add (tokens, s);
335 : :
336 : 3 : for (l = emblemed_icon->priv->emblems; l != NULL; l = l->next)
337 : : {
338 : 2 : GIcon *emblem_icon = G_ICON (l->data);
339 : :
340 : 2 : s = g_icon_to_string (emblem_icon);
341 : 2 : if (s == NULL)
342 : 0 : return FALSE;
343 : :
344 : 2 : g_ptr_array_add (tokens, s);
345 : : }
346 : :
347 : 1 : return TRUE;
348 : : }
349 : :
350 : : static GIcon *
351 : 1 : g_emblemed_icon_from_tokens (gchar **tokens,
352 : : gint num_tokens,
353 : : gint version,
354 : : GError **error)
355 : : {
356 : : GEmblemedIcon *emblemed_icon;
357 : : int n;
358 : :
359 : 1 : emblemed_icon = NULL;
360 : :
361 : 1 : if (version != 0)
362 : : {
363 : 0 : g_set_error (error,
364 : : G_IO_ERROR,
365 : : G_IO_ERROR_INVALID_ARGUMENT,
366 : : _("Can’t handle version %d of GEmblemedIcon encoding"),
367 : : version);
368 : 0 : goto fail;
369 : : }
370 : :
371 : 1 : if (num_tokens < 1)
372 : : {
373 : 0 : g_set_error (error,
374 : : G_IO_ERROR,
375 : : G_IO_ERROR_INVALID_ARGUMENT,
376 : : _("Malformed number of tokens (%d) in GEmblemedIcon encoding"),
377 : : num_tokens);
378 : 0 : goto fail;
379 : : }
380 : :
381 : 1 : emblemed_icon = g_object_new (G_TYPE_EMBLEMED_ICON, NULL);
382 : 1 : emblemed_icon->priv->icon = g_icon_new_for_string (tokens[0], error);
383 : 1 : if (emblemed_icon->priv->icon == NULL)
384 : 0 : goto fail;
385 : :
386 : 3 : for (n = 1; n < num_tokens; n++)
387 : : {
388 : : GIcon *emblem;
389 : :
390 : 2 : emblem = g_icon_new_for_string (tokens[n], error);
391 : 2 : if (emblem == NULL)
392 : 0 : goto fail;
393 : :
394 : 2 : if (!G_IS_EMBLEM (emblem))
395 : : {
396 : 0 : g_set_error_literal (error,
397 : : G_IO_ERROR,
398 : : G_IO_ERROR_INVALID_ARGUMENT,
399 : : _("Expected a GEmblem for GEmblemedIcon"));
400 : 0 : g_object_unref (emblem);
401 : 0 : goto fail;
402 : : }
403 : :
404 : 2 : emblemed_icon->priv->emblems = g_list_append (emblemed_icon->priv->emblems, emblem);
405 : : }
406 : :
407 : 1 : return G_ICON (emblemed_icon);
408 : :
409 : 0 : fail:
410 : 0 : if (emblemed_icon != NULL)
411 : 0 : g_object_unref (emblemed_icon);
412 : 0 : return NULL;
413 : : }
414 : :
415 : : static GVariant *
416 : 2 : g_emblemed_icon_serialize (GIcon *icon)
417 : : {
418 : 2 : GEmblemedIcon *emblemed_icon = G_EMBLEMED_ICON (icon);
419 : : GVariantBuilder builder;
420 : : GVariant *icon_data;
421 : : GList *node;
422 : :
423 : 2 : icon_data = g_icon_serialize (emblemed_icon->priv->icon);
424 : 2 : if (!icon_data)
425 : 0 : return NULL;
426 : :
427 : 2 : g_variant_builder_init_static (&builder, G_VARIANT_TYPE ("(va(va{sv}))"));
428 : :
429 : 2 : g_variant_builder_add (&builder, "v", icon_data);
430 : 2 : g_variant_unref (icon_data);
431 : :
432 : 2 : g_variant_builder_open (&builder, G_VARIANT_TYPE ("a(va{sv})"));
433 : 6 : for (node = emblemed_icon->priv->emblems; node != NULL; node = node->next)
434 : : {
435 : 4 : icon_data = g_icon_serialize (node->data);
436 : 4 : if (icon_data)
437 : : {
438 : : /* We know how emblems serialize, so do a tweak here to
439 : : * reduce some of the variant wrapping and redundant storage
440 : : * of 'emblem' over and again...
441 : : */
442 : 4 : if (g_variant_is_of_type (icon_data, G_VARIANT_TYPE ("(sv)")))
443 : : {
444 : : const gchar *name;
445 : : GVariant *content;
446 : :
447 : 4 : g_variant_get (icon_data, "(&sv)", &name, &content);
448 : :
449 : 4 : if (g_str_equal (name, "emblem") && g_variant_is_of_type (content, G_VARIANT_TYPE ("(va{sv})")))
450 : 4 : g_variant_builder_add (&builder, "@(va{sv})", content);
451 : :
452 : 4 : g_variant_unref (content);
453 : : }
454 : :
455 : 4 : g_variant_unref (icon_data);
456 : : }
457 : : }
458 : 2 : g_variant_builder_close (&builder);
459 : :
460 : 2 : return g_variant_new ("(sv)", "emblemed", g_variant_builder_end (&builder));
461 : : }
462 : :
463 : : static void
464 : 3 : g_emblemed_icon_icon_iface_init (GIconIface *iface)
465 : : {
466 : 3 : iface->hash = g_emblemed_icon_hash;
467 : 3 : iface->equal = g_emblemed_icon_equal;
468 : 3 : iface->to_tokens = g_emblemed_icon_to_tokens;
469 : 3 : iface->from_tokens = g_emblemed_icon_from_tokens;
470 : 3 : iface->serialize = g_emblemed_icon_serialize;
471 : 3 : }
|