gimpconfig-serialize.c 15.3 KB
Newer Older
1 2
/* LIBGIMP - The GIMP Library
 * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
3
 *
4
 * Object properties serialization routines
5
 * Copyright (C) 2001-2002  Sven Neumann <sven@gimp.org>
6
 *
7
 * This library is free software: you can redistribute it and/or
8 9
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
10
 * version 3 of the License, or (at your option) any later version.
11
 *
12
 * This library is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public
18 19
 * License along with this library.  If not, see
 * <http://www.gnu.org/licenses/>.
20 21 22 23
 */

#include "config.h"

24
#include <cairo.h>
25 26
#include <gegl.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
27

28
#include "libgimpbase/gimpbase.h"
29
#include "libgimpmath/gimpmath.h"
30 31
#include "libgimpcolor/gimpcolor.h"

32 33
#include "gimpconfigtypes.h"

34 35
#include "gimpconfigwriter.h"
#include "gimpconfig-iface.h"
36
#include "gimpconfig-params.h"
37
#include "gimpconfig-path.h"
38
#include "gimpconfig-serialize.h"
39
#include "gimpconfig-utils.h"
40 41


42 43 44 45 46 47 48 49 50
/**
 * SECTION: gimpconfig-serialize
 * @title: GimpConfig-serialize
 * @short_description: Serializing for libgimpconfig.
 *
 * Serializing interface for libgimpconfig.
 **/


51 52 53 54 55
static gboolean  gimp_config_serialize_rgb (const GValue *value,
                                            GString      *str,
                                            gboolean      has_alpha);


56 57
/**
 * gimp_config_serialize_properties:
58
 * @config: a #GimpConfig.
59
 * @writer: a #GimpConfigWriter.
60
 *
61
 * This function writes all object properties to the @writer.
62 63
 *
 * Returns: %TRUE if serialization succeeded, %FALSE otherwise
64
 *
65
 * Since: 2.4
66
 **/
67
gboolean
68
gimp_config_serialize_properties (GimpConfig       *config,
69
                                  GimpConfigWriter *writer)
70
{
71 72 73 74
  GObjectClass  *klass;
  GParamSpec   **property_specs;
  guint          n_property_specs;
  guint          i;
75

76
  g_return_val_if_fail (G_IS_OBJECT (config), FALSE);
77

78
  klass = G_OBJECT_GET_CLASS (config);
79

80
  property_specs = g_object_class_list_properties (klass, &n_property_specs);
81

82 83
  if (! property_specs)
    return TRUE;
84

85 86
  for (i = 0; i < n_property_specs; i++)
    {
Sven Neumann's avatar
Sven Neumann committed
87
      GParamSpec *prop_spec = property_specs[i];
88

89
      if (! (prop_spec->flags & GIMP_CONFIG_PARAM_SERIALIZE))
90 91
        continue;

92
      if (! gimp_config_serialize_property (config, prop_spec, writer))
93
        return FALSE;
94 95 96
    }

  g_free (property_specs);
97 98

  return TRUE;
99 100
}

101
/**
102
 * gimp_config_serialize_changed_properties:
103
 * @config: a #GimpConfig.
104
 * @writer: a #GimpConfigWriter.
105
 *
106
 * This function writes all object properties that have been changed from
107
 * their default values to the @writer.
108 109
 *
 * Returns: %TRUE if serialization succeeded, %FALSE otherwise
110
 *
111
 * Since: 2.4
112
 **/
113
gboolean
114
gimp_config_serialize_changed_properties (GimpConfig       *config,
115
                                          GimpConfigWriter *writer)
116 117 118 119 120
{
  GObjectClass  *klass;
  GParamSpec   **property_specs;
  guint          n_property_specs;
  guint          i;
121
  GValue         value = G_VALUE_INIT;
122

123
  g_return_val_if_fail (G_IS_OBJECT (config), FALSE);
124

125
  klass = G_OBJECT_GET_CLASS (config);
126 127 128 129 130 131 132 133 134 135

  property_specs = g_object_class_list_properties (klass, &n_property_specs);

  if (! property_specs)
    return TRUE;

  for (i = 0; i < n_property_specs; i++)
    {
      GParamSpec *prop_spec = property_specs[i];

136
      if (! (prop_spec->flags & GIMP_CONFIG_PARAM_SERIALIZE))
137 138 139
        continue;

      g_value_init (&value, prop_spec->value_type);
140
      g_object_get_property (G_OBJECT (config), prop_spec->name, &value);
141 142 143

      if (! g_param_value_defaults (prop_spec, &value))
        {
144
          if (! gimp_config_serialize_property (config, prop_spec, writer))
145
            return FALSE;
146 147 148 149 150 151 152 153 154 155
        }

      g_value_unset (&value);
    }

  g_free (property_specs);

  return TRUE;
}

156
/**
Michael Natterer's avatar
Michael Natterer committed
157 158 159 160
 * gimp_config_serialize_property:
 * @config:     a #GimpConfig.
 * @param_spec: a #GParamSpec.
 * @writer:     a #GimpConfigWriter.
161 162 163 164 165
 *
 * This function serializes a single object property to the @writer.
 *
 * Returns: %TRUE if serialization succeeded, %FALSE otherwise
 *
166
 * Since: 2.4
167
 **/
Sven Neumann's avatar
Sven Neumann committed
168
gboolean
169
gimp_config_serialize_property (GimpConfig       *config,
170 171
                                GParamSpec       *param_spec,
                                GimpConfigWriter *writer)
Sven Neumann's avatar
Sven Neumann committed
172
{
173
  GimpConfigInterface *config_iface = NULL;
174
  GimpConfigInterface *parent_iface = NULL;
175 176
  GValue               value        = G_VALUE_INIT;
  gboolean             success      = FALSE;
Sven Neumann's avatar
Sven Neumann committed
177

178
  if (! (param_spec->flags & GIMP_CONFIG_PARAM_SERIALIZE))
Sven Neumann's avatar
Sven Neumann committed
179 180
    return FALSE;

181
  if (param_spec->flags & GIMP_CONFIG_PARAM_IGNORE)
182 183
    return TRUE;

Sven Neumann's avatar
Sven Neumann committed
184
  g_value_init (&value, param_spec->value_type);
185
  g_object_get_property (G_OBJECT (config), param_spec->name, &value);
Sven Neumann's avatar
Sven Neumann committed
186

187
  if (param_spec->flags & GIMP_CONFIG_PARAM_DEFAULTS &&
188 189 190 191 192 193
      g_param_value_defaults (param_spec, &value))
    {
      g_value_unset (&value);
      return TRUE;
    }

194
  if (G_TYPE_IS_OBJECT (param_spec->owner_type))
195
    {
196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
      GTypeClass *owner_class = g_type_class_peek (param_spec->owner_type);

      config_iface = g_type_interface_peek (owner_class, GIMP_TYPE_CONFIG);

      /*  We must call serialize_property() *only* if the *exact* class
       *  which implements it is param_spec->owner_type's class.
       *
       *  Therefore, we ask param_spec->owner_type's immediate parent class
       *  for it's GimpConfigInterface and check if we get a different
       *  pointer.
       *
       *  (if the pointers are the same, param_spec->owner_type's
       *   GimpConfigInterface is inherited from one of it's parent classes
       *   and thus not able to handle param_spec->owner_type's properties).
       */
      if (config_iface)
        {
          GTypeClass *owner_parent_class;
214

215
          owner_parent_class = g_type_class_peek_parent (owner_class);
216

217 218 219
          parent_iface = g_type_interface_peek (owner_parent_class,
                                                GIMP_TYPE_CONFIG);
        }
220 221
    }

Sven Neumann's avatar
Sven Neumann committed
222
  if (config_iface                     &&
223
      config_iface != parent_iface     && /* see comment above */
224
      config_iface->serialize_property &&
225
      config_iface->serialize_property (config,
226 227 228
                                        param_spec->param_id,
                                        (const GValue *) &value,
                                        param_spec,
229
                                        writer))
Sven Neumann's avatar
Sven Neumann committed
230
    {
231
      success = TRUE;
Sven Neumann's avatar
Sven Neumann committed
232
    }
233 234 235 236 237

  /*  If there is no serialize_property() method *or* if it returned
   *  FALSE, continue with the default implementation
   */

238
  if (! success)
Sven Neumann's avatar
Sven Neumann committed
239
    {
240 241
      if (G_VALUE_HOLDS_OBJECT (&value) &&
          G_VALUE_TYPE (&value) != G_TYPE_FILE)
242
        {
243 244
          GimpConfigInterface *config_iface = NULL;
          GimpConfig          *prop_object;
245 246 247 248

          prop_object = g_value_get_object (&value);

          if (prop_object)
249
            config_iface = GIMP_CONFIG_GET_INTERFACE (prop_object);
250 251 252
          else
            success = TRUE;

253
          if (config_iface)
254 255 256
            {
              gimp_config_writer_open (writer, param_spec->name);

257
              /*  if the object property is not GIMP_CONFIG_PARAM_AGGREGATE,
258 259 260
               *  deserializing will need to know the exact type
               *  in order to create the object
               */
261
              if (! (param_spec->flags & GIMP_CONFIG_PARAM_AGGREGATE))
262 263 264 265 266 267
                {
                  GType object_type = G_TYPE_FROM_INSTANCE (prop_object);

                  gimp_config_writer_string (writer, g_type_name (object_type));
                }

268
              success = config_iface->serialize (prop_object, writer, NULL);
269 270 271 272 273 274

              if (success)
                gimp_config_writer_close (writer);
              else
                gimp_config_writer_revert (writer);
            }
275
        }
276
      else
277
        {
Sven Neumann's avatar
Sven Neumann committed
278
          GString *str = g_string_new (NULL);
279

280 281
          if (GIMP_VALUE_HOLDS_RGB (&value))
            {
282
              gboolean has_alpha = gimp_param_spec_rgb_has_alpha (param_spec);
283 284 285 286 287 288 289

              success = gimp_config_serialize_rgb (&value, str, has_alpha);
            }
          else
            {
              success = gimp_config_serialize_value (&value, str, TRUE);
            }
290

Sven Neumann's avatar
Sven Neumann committed
291
          if (success)
292 293 294 295 296
            {
              gimp_config_writer_open (writer, param_spec->name);
              gimp_config_writer_print (writer, str->str, str->len);
              gimp_config_writer_close (writer);
            }
297

Sven Neumann's avatar
Sven Neumann committed
298 299
          g_string_free (str, TRUE);
        }
300

301 302 303 304 305 306 307 308 309 310
      if (! success)
        {
          /* don't warn for empty string properties */
          if (G_VALUE_HOLDS_STRING (&value))
            {
              success = TRUE;
            }
          else
            {
              g_warning ("couldn't serialize property %s::%s of type %s",
311
                         g_type_name (G_TYPE_FROM_INSTANCE (config)),
312
                         param_spec->name,
313 314 315
                         g_type_name (param_spec->value_type));
            }
        }
Sven Neumann's avatar
Sven Neumann committed
316 317 318 319
    }

  g_value_unset (&value);

320
  return success;
Sven Neumann's avatar
Sven Neumann committed
321 322
}

323 324 325 326 327 328 329 330 331 332
/**
 * gimp_config_serialize_property_by_name:
 * @config:    a #GimpConfig.
 * @prop_name: the property's name.
 * @writer:    a #GimpConfigWriter.
 *
 * This function serializes a single object property to the @writer.
 *
 * Returns: %TRUE if serialization succeeded, %FALSE otherwise
 *
333
 * Since: 2.6
334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350
 **/
gboolean
gimp_config_serialize_property_by_name (GimpConfig       *config,
                                        const gchar      *prop_name,
                                        GimpConfigWriter *writer)
{
  GParamSpec *pspec;

  pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (config),
                                        prop_name);

  if (! pspec)
    return FALSE;

  return gimp_config_serialize_property (config, pspec, writer);
}

351 352 353
/**
 * gimp_config_serialize_value:
 * @value: a #GValue.
354
 * @str: a #GString.
Sven Neumann's avatar
Sven Neumann committed
355
 * @escaped: whether to escape string values.
356
 *
357
 * This utility function appends a string representation of #GValue to @str.
358
 *
359
 * Return value: %TRUE if serialization succeeded, %FALSE otherwise.
360
 *
361
 * Since: 2.4
362
 **/
363 364
gboolean
gimp_config_serialize_value (const GValue *value,
Sven Neumann's avatar
Sven Neumann committed
365 366
                             GString      *str,
                             gboolean      escaped)
367 368 369 370
{
  if (G_VALUE_HOLDS_BOOLEAN (value))
    {
      gboolean bool;
371

372
      bool = g_value_get_boolean (value);
373
      g_string_append (str, bool ? "yes" : "no");
374 375
      return TRUE;
    }
376

377 378
  if (G_VALUE_HOLDS_ENUM (value))
    {
379 380 381
      GEnumClass *enum_class = g_type_class_peek (G_VALUE_TYPE (value));
      GEnumValue *enum_value = g_enum_get_value (enum_class,
                                                 g_value_get_enum (value));
382 383

      if (enum_value && enum_value->value_nick)
384
        {
385 386
          g_string_append (str, enum_value->value_nick);
          return TRUE;
387
        }
388 389
      else
        {
390
          g_warning ("Couldn't get nick for enum_value of %s",
391 392 393 394
                     G_ENUM_CLASS_TYPE_NAME (enum_class));
          return FALSE;
        }
    }
395

396 397 398
  if (G_VALUE_HOLDS_STRING (value))
    {
      const gchar *cstr = g_value_get_string (value);
399

400 401 402
      if (!cstr)
        return FALSE;

Sven Neumann's avatar
Sven Neumann committed
403
      if (escaped)
404
        gimp_config_string_append_escaped (str, cstr);
Sven Neumann's avatar
Sven Neumann committed
405
      else
406 407
        g_string_append (str, cstr);

408
      return TRUE;
409
    }
410

411 412 413 414 415 416 417 418 419 420 421 422 423 424 425
  if (G_VALUE_HOLDS_DOUBLE (value) || G_VALUE_HOLDS_FLOAT (value))
    {
      gdouble v_double;
      gchar   buf[G_ASCII_DTOSTR_BUF_SIZE];

      if (G_VALUE_HOLDS_DOUBLE (value))
        v_double = g_value_get_double (value);
      else
        v_double = (gdouble) g_value_get_float (value);

      g_ascii_formatd (buf, sizeof (buf), "%f", v_double);
      g_string_append (str, buf);
      return TRUE;
    }

426
  if (GIMP_VALUE_HOLDS_RGB (value))
427
    {
428
      return gimp_config_serialize_rgb (value, str, TRUE);
429 430
    }

431 432 433 434 435 436
  if (GIMP_VALUE_HOLDS_MATRIX2 (value))
    {
      GimpMatrix2 *trafo;

      trafo = g_value_get_boxed (value);

437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453
      if (trafo)
        {
          gchar buf[4][G_ASCII_DTOSTR_BUF_SIZE];
          gint  i, j, k;

          for (i = 0, k = 0; i < 2; i++)
            for (j = 0; j < 2; j++, k++)
              g_ascii_formatd (buf[k],
                               G_ASCII_DTOSTR_BUF_SIZE, "%f", trafo->coeff[i][j]);

          g_string_append_printf (str, "(matrix %s %s %s %s)",
                                  buf[0], buf[1], buf[2], buf[3]);
        }
      else
        {
          g_string_append (str, "(matrix 1.0 1.0 1.0 1.0)");
        }
454 455 456 457

      return TRUE;
    }

458
  if (G_VALUE_TYPE (value) == GIMP_TYPE_VALUE_ARRAY)
459
    {
460
      GimpValueArray *array;
461 462 463 464 465

      array = g_value_get_boxed (value);

      if (array)
        {
466
          gint length = gimp_value_array_length (array);
467 468
          gint i;

469
          g_string_append_printf (str, "%d", length);
470

471
          for (i = 0; i < length; i++)
472 473 474
            {
              g_string_append (str, " ");

475 476
              if (! gimp_config_serialize_value (gimp_value_array_index (array,
                                                                         i),
477
                                                 str, TRUE))
478 479 480 481 482
                return FALSE;
            }
        }
      else
        {
483
          g_string_append (str, "0");
484 485
        }

486 487 488
      return TRUE;
    }

489 490 491 492 493 494
  if (G_VALUE_TYPE (value) == G_TYPE_FILE)
    {
      GFile *file = g_value_get_object (value);

      if (file)
        {
495 496
          gchar *path     = g_file_get_path (file);
          gchar *unexpand = NULL;
497

498 499 500 501 502 503 504 505 506 507 508 509
          if (path)
            {
              unexpand = gimp_config_path_unexpand (path, TRUE, NULL);
              g_free (path);
            }

          if (unexpand)
            {
              if (escaped)
                gimp_config_string_append_escaped (str, unexpand);
              else
                g_string_append (str, unexpand);
510

511 512 513 514 515 516
              g_free (unexpand);
            }
          else
            {
              g_string_append (str, "NULL");
            }
517 518 519 520 521 522 523 524 525
        }
      else
        {
          g_string_append (str, "NULL");
        }

      return TRUE;
    }

526 527
  if (g_value_type_transformable (G_VALUE_TYPE (value), G_TYPE_STRING))
    {
528
      GValue  tmp_value = G_VALUE_INIT;
529

530 531 532 533 534 535 536 537
      g_value_init (&tmp_value, G_TYPE_STRING);
      g_value_transform (value, &tmp_value);

      g_string_append (str, g_value_get_string (&tmp_value));

      g_value_unset (&tmp_value);
      return TRUE;
    }
538

539 540
  return FALSE;
}
541 542 543 544 545 546 547 548 549 550

static gboolean
gimp_config_serialize_rgb (const GValue *value,
                           GString      *str,
                           gboolean      has_alpha)
{
  GimpRGB *rgb;

  rgb = g_value_get_boxed (value);

551
  if (rgb)
552
    {
553
      gchar buf[4][G_ASCII_DTOSTR_BUF_SIZE];
554

555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572
      g_ascii_formatd (buf[0], G_ASCII_DTOSTR_BUF_SIZE, "%f", rgb->r);
      g_ascii_formatd (buf[1], G_ASCII_DTOSTR_BUF_SIZE, "%f", rgb->g);
      g_ascii_formatd (buf[2], G_ASCII_DTOSTR_BUF_SIZE, "%f", rgb->b);

      if (has_alpha)
        {
          g_ascii_formatd (buf[3], G_ASCII_DTOSTR_BUF_SIZE, "%f", rgb->a);

          g_string_append_printf (str, "(color-rgba %s %s %s %s)",
                                  buf[0], buf[1], buf[2], buf[3]);
        }
      else
        {
          g_string_append_printf (str, "(color-rgb %s %s %s)",
                                  buf[0], buf[1], buf[2]);
        }

      return TRUE;
573 574
    }

575
  return FALSE;
576
}