gemblemedicon.c 11.7 KB
Newer Older
1 2 3 4 5 6 7 8 9
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */

/* GIO - GLib Input, Output and Streaming Library
 * 
 * Copyright (C) 2006-2007 Red Hat, Inc.
 *
 * 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
10
 * version 2.1 of the License, or (at your option) any later version.
11 12 13 14 15 16 17
 *
 * 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
18
 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 20
 *
 * Author: Matthias Clasen <mclasen@redhat.com>
Matthias Clasen's avatar
Matthias Clasen committed
21
 *         Clemens N. Buss <cebuzz@gmail.com>
22 23 24 25 26 27 28 29
 */

#include <config.h>

#include <string.h>

#include "gemblemedicon.h"
#include "glibintl.h"
30
#include "gioerror.h"
31 32 33 34 35 36


/**
 * SECTION:gemblemedicon
 * @short_description: Icon with emblems
 * @include: gio/gio.h
Matthias Clasen's avatar
Matthias Clasen committed
37
 * @see_also: #GIcon, #GLoadableIcon, #GThemedIcon, #GEmblem
38 39
 *
 * #GEmblemedIcon is an implementation of #GIcon that supports
Matthias Clasen's avatar
Matthias Clasen committed
40 41
 * adding an emblem to an icon. Adding multiple emblems to an
 * icon is ensured via g_emblemed_icon_add_emblem(). 
42 43
 *
 * Note that #GEmblemedIcon allows no control over the position
Matthias Clasen's avatar
Matthias Clasen committed
44
 * of the emblems. See also #GEmblem for more information.
45 46
 **/

47 48 49 50
enum {
  PROP_GICON = 1,
  NUM_PROPERTIES
};
51

52
struct _GEmblemedIconPrivate {
53
  GIcon *icon;
Matthias Clasen's avatar
Matthias Clasen committed
54
  GList *emblems;
55 56
};

57 58 59
static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };

static void g_emblemed_icon_icon_iface_init (GIconIface *iface);
60 61

G_DEFINE_TYPE_WITH_CODE (GEmblemedIcon, g_emblemed_icon, G_TYPE_OBJECT,
62
                         G_ADD_PRIVATE (GEmblemedIcon)
Matthias Clasen's avatar
Matthias Clasen committed
63 64
                         G_IMPLEMENT_INTERFACE (G_TYPE_ICON,
                         g_emblemed_icon_icon_iface_init))
65 66 67 68 69 70 71 72 73


static void
g_emblemed_icon_finalize (GObject *object)
{
  GEmblemedIcon *emblemed;

  emblemed = G_EMBLEMED_ICON (object);

74
  g_clear_object (&emblemed->priv->icon);
75
  g_list_free_full (emblemed->priv->emblems, g_object_unref);
76 77 78 79

  (*G_OBJECT_CLASS (g_emblemed_icon_parent_class)->finalize) (object);
}

80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
static void
g_emblemed_icon_set_property (GObject  *object,
                              guint property_id,
                              const GValue *value,
                              GParamSpec *pspec)
{
  GEmblemedIcon *self = G_EMBLEMED_ICON (object);

  switch (property_id)
    {
    case PROP_GICON:
      self->priv->icon = g_value_dup_object (value);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}

static void
g_emblemed_icon_get_property (GObject  *object,
                              guint property_id,
                              GValue *value,
                              GParamSpec *pspec)
{
  GEmblemedIcon *self = G_EMBLEMED_ICON (object);

  switch (property_id)
    {
    case PROP_GICON:
      g_value_set_object (value, self->priv->icon);
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}

118 119 120 121
static void
g_emblemed_icon_class_init (GEmblemedIconClass *klass)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
122

123
  gobject_class->finalize = g_emblemed_icon_finalize;
124 125 126 127 128 129 130 131 132 133 134
  gobject_class->set_property = g_emblemed_icon_set_property;
  gobject_class->get_property = g_emblemed_icon_get_property;

  properties[PROP_GICON] =
    g_param_spec_object ("gicon",
                         P_("The base GIcon"),
                         P_("The GIcon to attach emblems to"),
                         G_TYPE_ICON,
                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);

  g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
135 136 137 138 139
}

static void
g_emblemed_icon_init (GEmblemedIcon *emblemed)
{
140
  emblemed->priv = g_emblemed_icon_get_instance_private (emblemed);
141 142 143 144
}

/**
 * g_emblemed_icon_new:
Matthias Clasen's avatar
Matthias Clasen committed
145
 * @icon: a #GIcon
146
 * @emblem: (nullable): a #GEmblem, or %NULL
147
 *
Matthias Clasen's avatar
Matthias Clasen committed
148
 * Creates a new emblemed icon for @icon with the emblem @emblem.
149
 *
150
 * Returns: (transfer full) (type GEmblemedIcon): a new #GIcon
151 152 153 154
 *
 * Since: 2.18
 **/
GIcon *
Matthias Clasen's avatar
Matthias Clasen committed
155 156
g_emblemed_icon_new (GIcon   *icon,
                     GEmblem *emblem)
157 158
{
  GEmblemedIcon *emblemed;
Matthias Clasen's avatar
Matthias Clasen committed
159 160 161
  
  g_return_val_if_fail (G_IS_ICON (icon), NULL);
  g_return_val_if_fail (!G_IS_EMBLEM (icon), NULL);
162

163 164 165
  emblemed = G_EMBLEMED_ICON (g_object_new (G_TYPE_EMBLEMED_ICON,
                                            "gicon", icon,
                                            NULL));
166 167 168

  if (emblem != NULL)
    g_emblemed_icon_add_emblem (emblemed, emblem);
169 170 171 172

  return G_ICON (emblemed);
}

Matthias Clasen's avatar
Matthias Clasen committed
173

174 175
/**
 * g_emblemed_icon_get_icon:
Matthias Clasen's avatar
Matthias Clasen committed
176
 * @emblemed: a #GEmblemedIcon
177
 *
Matthias Clasen's avatar
Matthias Clasen committed
178
 * Gets the main icon for @emblemed.
179
 *
180
 * Returns: (transfer none): a #GIcon that is owned by @emblemed
181 182 183 184
 *
 * Since: 2.18
 **/
GIcon *
Matthias Clasen's avatar
Matthias Clasen committed
185
g_emblemed_icon_get_icon (GEmblemedIcon *emblemed)
186
{
Matthias Clasen's avatar
Matthias Clasen committed
187
  g_return_val_if_fail (G_IS_EMBLEMED_ICON (emblemed), NULL);
188

189
  return emblemed->priv->icon;
190 191 192
}

/**
Matthias Clasen's avatar
Matthias Clasen committed
193 194
 * g_emblemed_icon_get_emblems:
 * @emblemed: a #GEmblemedIcon
195
 *
Matthias Clasen's avatar
Matthias Clasen committed
196
 * Gets the list of emblems for the @icon.
197
 *
198
 * Returns: (element-type Gio.Emblem) (transfer none): a #GList of
199
 *     #GEmblems that is owned by @emblemed
200 201 202
 *
 * Since: 2.18
 **/
Matthias Clasen's avatar
Matthias Clasen committed
203 204 205 206 207 208

GList *
g_emblemed_icon_get_emblems (GEmblemedIcon *emblemed)
{
  g_return_val_if_fail (G_IS_EMBLEMED_ICON (emblemed), NULL);

209
  return emblemed->priv->emblems;
Matthias Clasen's avatar
Matthias Clasen committed
210 211
}

212 213 214 215
/**
 * g_emblemed_icon_clear_emblems:
 * @emblemed: a #GEmblemedIcon
 *
Cosimo Cecchi's avatar
Cosimo Cecchi committed
216
 * Removes all the emblems from @icon.
217 218 219
 *
 * Since: 2.28
 **/
220 221 222 223 224
void
g_emblemed_icon_clear_emblems (GEmblemedIcon *emblemed)
{
  g_return_if_fail (G_IS_EMBLEMED_ICON (emblemed));

225
  if (emblemed->priv->emblems == NULL)
226 227
    return;

228 229
  g_list_free_full (emblemed->priv->emblems, g_object_unref);
  emblemed->priv->emblems = NULL;
230 231
}

232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
static gint
g_emblem_comp (GEmblem *a,
               GEmblem *b)
{
  guint hash_a = g_icon_hash (G_ICON (a));
  guint hash_b = g_icon_hash (G_ICON (b));

  if(hash_a < hash_b)
    return -1;

  if(hash_a == hash_b)
    return 0;

  return 1;
}
Matthias Clasen's avatar
Matthias Clasen committed
247 248 249 250 251 252

/**
 * g_emblemed_icon_add_emblem:
 * @emblemed: a #GEmblemedIcon
 * @emblem: a #GEmblem
 *
253
 * Adds @emblem to the #GList of #GEmblems.
Matthias Clasen's avatar
Matthias Clasen committed
254 255 256 257 258 259
 *
 * Since: 2.18
 **/
void 
g_emblemed_icon_add_emblem (GEmblemedIcon *emblemed,
                            GEmblem       *emblem)
260
{
Matthias Clasen's avatar
Matthias Clasen committed
261 262
  g_return_if_fail (G_IS_EMBLEMED_ICON (emblemed));
  g_return_if_fail (G_IS_EMBLEM (emblem));
263

Matthias Clasen's avatar
Matthias Clasen committed
264
  g_object_ref (emblem);
265 266
  emblemed->priv->emblems = g_list_insert_sorted (emblemed->priv->emblems, emblem,
                                                  (GCompareFunc) g_emblem_comp);
267 268 269 270 271 272
}

static guint
g_emblemed_icon_hash (GIcon *icon)
{
  GEmblemedIcon *emblemed = G_EMBLEMED_ICON (icon);
Matthias Clasen's avatar
Matthias Clasen committed
273
  GList *list;
274
  guint hash = g_icon_hash (emblemed->priv->icon);
275

276
  for (list = emblemed->priv->emblems; list != NULL; list = list->next)
Matthias Clasen's avatar
Matthias Clasen committed
277
    hash ^= g_icon_hash (G_ICON (list->data));
278 279 280 281 282 283 284 285 286 287

  return hash;
}

static gboolean
g_emblemed_icon_equal (GIcon *icon1,
                       GIcon *icon2)
{
  GEmblemedIcon *emblemed1 = G_EMBLEMED_ICON (icon1);
  GEmblemedIcon *emblemed2 = G_EMBLEMED_ICON (icon2);
Matthias Clasen's avatar
Matthias Clasen committed
288 289
  GList *list1, *list2;

290
  if (!g_icon_equal (emblemed1->priv->icon, emblemed2->priv->icon))
Matthias Clasen's avatar
Matthias Clasen committed
291 292
    return FALSE;

293 294
  list1 = emblemed1->priv->emblems;
  list2 = emblemed2->priv->emblems;
Matthias Clasen's avatar
Matthias Clasen committed
295 296 297 298 299 300 301 302 303 304 305

  while (list1 && list2)
  {
    if (!g_icon_equal (G_ICON (list1->data), G_ICON (list2->data)))
        return FALSE;
    
    list1 = list1->next;
    list2 = list2->next;
  }
  
  return list1 == NULL && list2 == NULL;
306 307
}

308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
static gboolean
g_emblemed_icon_to_tokens (GIcon *icon,
                           GPtrArray *tokens,
                           gint  *out_version)
{
  GEmblemedIcon *emblemed_icon = G_EMBLEMED_ICON (icon);
  GList *l;
  char *s;

  /* GEmblemedIcons are encoded as
   *
   *   <encoded_icon> [<encoded_emblem_icon>]*
   */

  g_return_val_if_fail (out_version != NULL, FALSE);

  *out_version = 0;

326
  s = g_icon_to_string (emblemed_icon->priv->icon);
327 328 329 330 331
  if (s == NULL)
    return FALSE;

  g_ptr_array_add (tokens, s);

332
  for (l = emblemed_icon->priv->emblems; l != NULL; l = l->next)
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
    {
      GIcon *emblem_icon = G_ICON (l->data);

      s = g_icon_to_string (emblem_icon);
      if (s == NULL)
        return FALSE;
      
      g_ptr_array_add (tokens, s);
    }

  return TRUE;
}

static GIcon *
g_emblemed_icon_from_tokens (gchar  **tokens,
                             gint     num_tokens,
                             gint     version,
                             GError **error)
{
  GEmblemedIcon *emblemed_icon;
  int n;

  emblemed_icon = NULL;

  if (version != 0)
    {
      g_set_error (error,
                   G_IO_ERROR,
                   G_IO_ERROR_INVALID_ARGUMENT,
362
                   _("Can’t handle version %d of GEmblemedIcon encoding"),
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377
                   version);
      goto fail;
    }

  if (num_tokens < 1)
    {
      g_set_error (error,
                   G_IO_ERROR,
                   G_IO_ERROR_INVALID_ARGUMENT,
                   _("Malformed number of tokens (%d) in GEmblemedIcon encoding"),
                   num_tokens);
      goto fail;
    }

  emblemed_icon = g_object_new (G_TYPE_EMBLEMED_ICON, NULL);
378 379
  emblemed_icon->priv->icon = g_icon_new_for_string (tokens[0], error);
  if (emblemed_icon->priv->icon == NULL)
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399
    goto fail;

  for (n = 1; n < num_tokens; n++)
    {
      GIcon *emblem;

      emblem = g_icon_new_for_string (tokens[n], error);
      if (emblem == NULL)
        goto fail;

      if (!G_IS_EMBLEM (emblem))
        {
          g_set_error_literal (error,
                               G_IO_ERROR,
                               G_IO_ERROR_INVALID_ARGUMENT,
                               _("Expected a GEmblem for GEmblemedIcon"));
          g_object_unref (emblem);
          goto fail;
        }

400
      emblemed_icon->priv->emblems = g_list_append (emblemed_icon->priv->emblems, emblem);
401 402 403 404 405 406 407 408 409 410
    }

  return G_ICON (emblemed_icon);

 fail:
  if (emblemed_icon != NULL)
    g_object_unref (emblemed_icon);
  return NULL;
}

411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458
static GVariant *
g_emblemed_icon_serialize (GIcon *icon)
{
  GEmblemedIcon *emblemed_icon = G_EMBLEMED_ICON (icon);
  GVariantBuilder builder;
  GVariant *icon_data;
  GList *node;

  icon_data = g_icon_serialize (emblemed_icon->priv->icon);
  if (!icon_data)
    return NULL;

  g_variant_builder_init (&builder, G_VARIANT_TYPE ("(va(va{sv}))"));

  g_variant_builder_add (&builder, "v", icon_data);
  g_variant_unref (icon_data);

  g_variant_builder_open (&builder, G_VARIANT_TYPE ("a(va{sv})"));
  for (node = emblemed_icon->priv->emblems; node != NULL; node = node->next)
    {
      icon_data = g_icon_serialize (node->data);
      if (icon_data)
        {
          /* We know how emblems serialise, so do a tweak here to
           * reduce some of the variant wrapping and redundant storage
           * of 'emblem' over and again...
           */
          if (g_variant_is_of_type (icon_data, G_VARIANT_TYPE ("(sv)")))
            {
              const gchar *name;
              GVariant *content;

              g_variant_get (icon_data, "(&sv)", &name, &content);

              if (g_str_equal (name, "emblem") && g_variant_is_of_type (content, G_VARIANT_TYPE ("(va{sv})")))
                g_variant_builder_add (&builder, "@(va{sv})", content);

              g_variant_unref (content);
            }

          g_variant_unref (icon_data);
        }
    }
  g_variant_builder_close (&builder);

  return g_variant_new ("(sv)", "emblemed", g_variant_builder_end (&builder));
}

459 460 461 462 463
static void
g_emblemed_icon_icon_iface_init (GIconIface *iface)
{
  iface->hash = g_emblemed_icon_hash;
  iface->equal = g_emblemed_icon_equal;
464 465
  iface->to_tokens = g_emblemed_icon_to_tokens;
  iface->from_tokens = g_emblemed_icon_from_tokens;
466
  iface->serialize = g_emblemed_icon_serialize;
467
}