gimpconfig-serialize.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667
  1. /* LIBGIMP - The GIMP Library
  2. * 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. * modify it under the terms of the GNU Lesser General Public
  9. * 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. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Library General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this library. If not, see
  19. * <https://www.gnu.org/licenses/>.
  20. */
  21. #include "config.h"
  22. #include <cairo.h>
  23. #include <gegl.h>
  24. #include <gdk-pixbuf/gdk-pixbuf.h>
  25. #include "libgimpbase/gimpbase.h"
  26. #include "libgimpmath/gimpmath.h"
  27. #include "libgimpcolor/gimpcolor.h"
  28. #include "gimpconfigtypes.h"
  29. #include "gimpconfigwriter.h"
  30. #include "gimpconfig-array.h"
  31. #include "gimpconfig-iface.h"
  32. #include "gimpconfig-params.h"
  33. #include "gimpconfig-path.h"
  34. #include "gimpconfig-serialize.h"
  35. #include "gimpconfig-utils.h"
  36. /**
  37. * SECTION: gimpconfig-serialize
  38. * @title: GimpConfig-serialize
  39. * @short_description: Serializing for libgimpconfig.
  40. *
  41. * Serializing interface for libgimpconfig.
  42. **/
  43. /**
  44. * gimp_config_serialize_properties:
  45. * @config: a #GimpConfig.
  46. * @writer: a #GimpConfigWriter.
  47. *
  48. * This function writes all object properties to the @writer.
  49. *
  50. * Returns: %TRUE if serialization succeeded, %FALSE otherwise
  51. *
  52. * Since: 2.4
  53. **/
  54. gboolean
  55. gimp_config_serialize_properties (GimpConfig *config,
  56. GimpConfigWriter *writer)
  57. {
  58. GObjectClass *klass;
  59. GParamSpec **property_specs;
  60. guint n_property_specs;
  61. guint i;
  62. gboolean success = TRUE;
  63. g_return_val_if_fail (G_IS_OBJECT (config), FALSE);
  64. klass = G_OBJECT_GET_CLASS (config);
  65. property_specs = g_object_class_list_properties (klass, &n_property_specs);
  66. if (! property_specs)
  67. return success;
  68. for (i = 0; i < n_property_specs; i++)
  69. {
  70. GParamSpec *prop_spec = property_specs[i];
  71. if (! (prop_spec->flags & GIMP_CONFIG_PARAM_SERIALIZE))
  72. continue;
  73. /* Some properties may fail writing, which shouldn't break serializing
  74. * more properties, yet final result would be a (partial) failure.
  75. */
  76. if (! gimp_config_serialize_property (config, prop_spec, writer))
  77. success = FALSE;
  78. }
  79. g_free (property_specs);
  80. return success;
  81. }
  82. /**
  83. * gimp_config_serialize_changed_properties:
  84. * @config: a #GimpConfig.
  85. * @writer: a #GimpConfigWriter.
  86. *
  87. * This function writes all object properties that have been changed from
  88. * their default values to the @writer.
  89. *
  90. * Returns: %TRUE if serialization succeeded, %FALSE otherwise
  91. *
  92. * Since: 2.4
  93. **/
  94. gboolean
  95. gimp_config_serialize_changed_properties (GimpConfig *config,
  96. GimpConfigWriter *writer)
  97. {
  98. GObjectClass *klass;
  99. GParamSpec **property_specs;
  100. guint n_property_specs;
  101. guint i;
  102. GValue value = G_VALUE_INIT;
  103. gboolean success = TRUE;
  104. g_return_val_if_fail (G_IS_OBJECT (config), FALSE);
  105. klass = G_OBJECT_GET_CLASS (config);
  106. property_specs = g_object_class_list_properties (klass, &n_property_specs);
  107. if (! property_specs)
  108. return success;
  109. for (i = 0; i < n_property_specs; i++)
  110. {
  111. GParamSpec *prop_spec = property_specs[i];
  112. if (! (prop_spec->flags & GIMP_CONFIG_PARAM_SERIALIZE))
  113. continue;
  114. g_value_init (&value, prop_spec->value_type);
  115. g_object_get_property (G_OBJECT (config), prop_spec->name, &value);
  116. if (! g_param_value_defaults (prop_spec, &value))
  117. {
  118. /* Some properties may fail writing, which shouldn't break serializing
  119. * more properties, yet final result would be a (partial) failure.
  120. */
  121. if (! gimp_config_serialize_property (config, prop_spec, writer))
  122. success = FALSE;
  123. }
  124. g_value_unset (&value);
  125. }
  126. g_free (property_specs);
  127. return success;
  128. }
  129. /**
  130. * gimp_config_serialize_property:
  131. * @config: a #GimpConfig.
  132. * @param_spec: a #GParamSpec.
  133. * @writer: a #GimpConfigWriter.
  134. *
  135. * This function serializes a single object property to the @writer.
  136. *
  137. * Returns: %TRUE if serialization succeeded, %FALSE otherwise
  138. *
  139. * Since: 2.4
  140. **/
  141. gboolean
  142. gimp_config_serialize_property (GimpConfig *config,
  143. GParamSpec *param_spec,
  144. GimpConfigWriter *writer)
  145. {
  146. GimpConfigInterface *config_iface = NULL;
  147. GimpConfigInterface *parent_iface = NULL;
  148. GValue value = G_VALUE_INIT;
  149. gboolean success = FALSE;
  150. if (! (param_spec->flags & GIMP_CONFIG_PARAM_SERIALIZE))
  151. return FALSE;
  152. if (param_spec->flags & GIMP_CONFIG_PARAM_IGNORE)
  153. return TRUE;
  154. g_value_init (&value, param_spec->value_type);
  155. g_object_get_property (G_OBJECT (config), param_spec->name, &value);
  156. if (param_spec->flags & GIMP_CONFIG_PARAM_DEFAULTS &&
  157. g_param_value_defaults (param_spec, &value))
  158. {
  159. g_value_unset (&value);
  160. return TRUE;
  161. }
  162. if (G_TYPE_IS_OBJECT (param_spec->owner_type))
  163. {
  164. GTypeClass *owner_class = g_type_class_peek (param_spec->owner_type);
  165. config_iface = g_type_interface_peek (owner_class, GIMP_TYPE_CONFIG);
  166. /* We must call serialize_property() *only* if the *exact* class
  167. * which implements it is param_spec->owner_type's class.
  168. *
  169. * Therefore, we ask param_spec->owner_type's immediate parent class
  170. * for it's GimpConfigInterface and check if we get a different
  171. * pointer.
  172. *
  173. * (if the pointers are the same, param_spec->owner_type's
  174. * GimpConfigInterface is inherited from one of it's parent classes
  175. * and thus not able to handle param_spec->owner_type's properties).
  176. */
  177. if (config_iface)
  178. {
  179. GTypeClass *owner_parent_class;
  180. owner_parent_class = g_type_class_peek_parent (owner_class);
  181. parent_iface = g_type_interface_peek (owner_parent_class,
  182. GIMP_TYPE_CONFIG);
  183. }
  184. }
  185. if (config_iface &&
  186. config_iface != parent_iface && /* see comment above */
  187. config_iface->serialize_property &&
  188. config_iface->serialize_property (config,
  189. param_spec->param_id,
  190. (const GValue *) &value,
  191. param_spec,
  192. writer))
  193. {
  194. success = TRUE;
  195. }
  196. /* If there is no serialize_property() method *or* if it returned
  197. * FALSE, continue with the default implementation
  198. */
  199. if (! success)
  200. {
  201. if (G_VALUE_TYPE (&value) == GIMP_TYPE_PARASITE)
  202. {
  203. GimpParasite *parasite = g_value_get_boxed (&value);
  204. gimp_config_writer_open (writer, param_spec->name);
  205. if (parasite)
  206. {
  207. const gchar *name;
  208. gconstpointer data;
  209. guint32 data_length;
  210. gulong flags;
  211. name = gimp_parasite_get_name (parasite);
  212. gimp_config_writer_string (writer, name);
  213. flags = gimp_parasite_get_flags (parasite);
  214. data = gimp_parasite_get_data (parasite, &data_length);
  215. gimp_config_writer_printf (writer, "%lu %u", flags, data_length);
  216. gimp_config_writer_data (writer, data_length, data);
  217. success = TRUE;
  218. }
  219. if (success)
  220. gimp_config_writer_close (writer);
  221. else
  222. gimp_config_writer_revert (writer);
  223. }
  224. else if (G_VALUE_TYPE (&value) == G_TYPE_BYTES)
  225. {
  226. GBytes *bytes = g_value_get_boxed (&value);
  227. gimp_config_writer_open (writer, param_spec->name);
  228. if (bytes)
  229. {
  230. gconstpointer data;
  231. gsize data_length;
  232. data = g_bytes_get_data (bytes, &data_length);
  233. gimp_config_writer_printf (writer, "%" G_GSIZE_FORMAT, data_length);
  234. gimp_config_writer_data (writer, data_length, data);
  235. }
  236. else
  237. {
  238. gimp_config_writer_printf (writer, "%s", "NULL");
  239. }
  240. success = TRUE;
  241. gimp_config_writer_close (writer);
  242. }
  243. else if (GIMP_VALUE_HOLDS_COLOR (&value))
  244. {
  245. GeglColor *color = g_value_get_object (&value);
  246. gboolean free_color = FALSE;
  247. gimp_config_writer_open (writer, param_spec->name);
  248. if (color)
  249. {
  250. const gchar *encoding;
  251. const Babl *format = gegl_color_get_format (color);
  252. const Babl *space;
  253. GBytes *bytes;
  254. gconstpointer data;
  255. gsize data_length;
  256. int profile_length = 0;
  257. gimp_config_writer_open (writer, "color");
  258. if (babl_format_is_palette (format))
  259. {
  260. guint8 pixel[40];
  261. /* As a special case, we don't want to serialize
  262. * palette colors, because they are just too much
  263. * dependent on external data and cannot be
  264. * deserialized back safely. So we convert them first.
  265. */
  266. free_color = TRUE;
  267. color = gegl_color_duplicate (color);
  268. format = babl_format_with_space ("R'G'B'A u8", format);
  269. gegl_color_get_pixel (color, format, pixel);
  270. gegl_color_set_pixel (color, format, pixel);
  271. }
  272. encoding = babl_format_get_encoding (format);
  273. gimp_config_writer_string (writer, encoding);
  274. bytes = gegl_color_get_bytes (color, format);
  275. data = g_bytes_get_data (bytes, &data_length);
  276. gimp_config_writer_printf (writer, "%" G_GSIZE_FORMAT, data_length);
  277. gimp_config_writer_data (writer, data_length, data);
  278. space = babl_format_get_space (format);
  279. if (space != babl_space ("sRGB"))
  280. {
  281. guint8 *profile_data;
  282. profile_data = (guint8 *) babl_space_get_icc (babl_format_get_space (format),
  283. &profile_length);
  284. gimp_config_writer_printf (writer, "%u", profile_length);
  285. if (profile_data)
  286. gimp_config_writer_data (writer, profile_length, profile_data);
  287. }
  288. else
  289. {
  290. gimp_config_writer_printf (writer, "%u", profile_length);
  291. }
  292. g_bytes_unref (bytes);
  293. gimp_config_writer_close (writer);
  294. }
  295. else
  296. {
  297. gimp_config_writer_printf (writer, "%s", "NULL");
  298. }
  299. success = TRUE;
  300. gimp_config_writer_close (writer);
  301. if (free_color)
  302. g_object_unref (color);
  303. }
  304. else if (G_VALUE_HOLDS_OBJECT (&value) &&
  305. G_VALUE_TYPE (&value) != G_TYPE_FILE)
  306. {
  307. GimpConfigInterface *config_iface = NULL;
  308. GimpConfig *prop_object;
  309. prop_object = g_value_get_object (&value);
  310. if (prop_object)
  311. config_iface = GIMP_CONFIG_GET_IFACE (prop_object);
  312. else
  313. success = TRUE;
  314. if (config_iface)
  315. {
  316. gimp_config_writer_open (writer, param_spec->name);
  317. /* if the object property is not GIMP_CONFIG_PARAM_AGGREGATE,
  318. * deserializing will need to know the exact type
  319. * in order to create the object
  320. */
  321. if (! (param_spec->flags & GIMP_CONFIG_PARAM_AGGREGATE))
  322. {
  323. GType object_type = G_TYPE_FROM_INSTANCE (prop_object);
  324. gimp_config_writer_string (writer, g_type_name (object_type));
  325. }
  326. success = config_iface->serialize (prop_object, writer, NULL);
  327. if (success)
  328. gimp_config_writer_close (writer);
  329. else
  330. gimp_config_writer_revert (writer);
  331. }
  332. }
  333. else
  334. {
  335. GString *str = g_string_new (NULL);
  336. if (G_VALUE_TYPE (&value) == G_TYPE_STRV)
  337. {
  338. success = gimp_config_serialize_strv (&value, str);
  339. }
  340. else
  341. {
  342. success = gimp_config_serialize_value (&value, str, TRUE);
  343. }
  344. if (success)
  345. {
  346. gimp_config_writer_open (writer, param_spec->name);
  347. gimp_config_writer_print (writer, str->str, str->len);
  348. gimp_config_writer_close (writer);
  349. }
  350. g_string_free (str, TRUE);
  351. }
  352. if (! success)
  353. {
  354. /* don't warn for empty string properties */
  355. if (G_VALUE_HOLDS_STRING (&value))
  356. {
  357. success = TRUE;
  358. }
  359. else
  360. {
  361. g_warning ("couldn't serialize property %s::%s of type %s",
  362. g_type_name (G_TYPE_FROM_INSTANCE (config)),
  363. param_spec->name,
  364. g_type_name (param_spec->value_type));
  365. }
  366. }
  367. }
  368. g_value_unset (&value);
  369. return success;
  370. }
  371. /**
  372. * gimp_config_serialize_property_by_name:
  373. * @config: a #GimpConfig.
  374. * @prop_name: the property's name.
  375. * @writer: a #GimpConfigWriter.
  376. *
  377. * This function serializes a single object property to the @writer.
  378. *
  379. * Returns: %TRUE if serialization succeeded, %FALSE otherwise
  380. *
  381. * Since: 2.6
  382. **/
  383. gboolean
  384. gimp_config_serialize_property_by_name (GimpConfig *config,
  385. const gchar *prop_name,
  386. GimpConfigWriter *writer)
  387. {
  388. GParamSpec *pspec;
  389. pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (config),
  390. prop_name);
  391. if (! pspec)
  392. return FALSE;
  393. return gimp_config_serialize_property (config, pspec, writer);
  394. }
  395. /**
  396. * gimp_config_serialize_value:
  397. * @value: a #GValue.
  398. * @str: a #GString.
  399. * @escaped: whether to escape string values.
  400. *
  401. * This utility function appends a string representation of #GValue to @str.
  402. *
  403. * Returns: %TRUE if serialization succeeded, %FALSE otherwise.
  404. *
  405. * Since: 2.4
  406. **/
  407. gboolean
  408. gimp_config_serialize_value (const GValue *value,
  409. GString *str,
  410. gboolean escaped)
  411. {
  412. if (G_VALUE_HOLDS_BOOLEAN (value))
  413. {
  414. gboolean bool;
  415. bool = g_value_get_boolean (value);
  416. g_string_append (str, bool ? "yes" : "no");
  417. return TRUE;
  418. }
  419. if (G_VALUE_HOLDS_ENUM (value))
  420. {
  421. GEnumClass *enum_class = g_type_class_peek (G_VALUE_TYPE (value));
  422. GEnumValue *enum_value = g_enum_get_value (enum_class,
  423. g_value_get_enum (value));
  424. if (enum_value && enum_value->value_nick)
  425. {
  426. g_string_append (str, enum_value->value_nick);
  427. return TRUE;
  428. }
  429. else
  430. {
  431. g_warning ("Couldn't get nick for enum_value of %s",
  432. G_ENUM_CLASS_TYPE_NAME (enum_class));
  433. return FALSE;
  434. }
  435. }
  436. if (G_VALUE_HOLDS_STRING (value))
  437. {
  438. const gchar *cstr = g_value_get_string (value);
  439. if (!cstr)
  440. return FALSE;
  441. if (escaped)
  442. gimp_config_string_append_escaped (str, cstr);
  443. else
  444. g_string_append (str, cstr);
  445. return TRUE;
  446. }
  447. if (G_VALUE_HOLDS_DOUBLE (value) || G_VALUE_HOLDS_FLOAT (value))
  448. {
  449. gdouble v_double;
  450. gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
  451. if (G_VALUE_HOLDS_DOUBLE (value))
  452. v_double = g_value_get_double (value);
  453. else
  454. v_double = (gdouble) g_value_get_float (value);
  455. g_ascii_dtostr (buf, sizeof (buf), v_double);
  456. g_string_append (str, buf);
  457. return TRUE;
  458. }
  459. if (GIMP_VALUE_HOLDS_MATRIX2 (value))
  460. {
  461. GimpMatrix2 *trafo;
  462. trafo = g_value_get_boxed (value);
  463. if (trafo)
  464. {
  465. gchar buf[4][G_ASCII_DTOSTR_BUF_SIZE];
  466. gint i, j, k;
  467. for (i = 0, k = 0; i < 2; i++)
  468. for (j = 0; j < 2; j++, k++)
  469. g_ascii_dtostr (buf[k], G_ASCII_DTOSTR_BUF_SIZE,
  470. trafo->coeff[i][j]);
  471. g_string_append_printf (str, "(matrix %s %s %s %s)",
  472. buf[0], buf[1], buf[2], buf[3]);
  473. }
  474. else
  475. {
  476. g_string_append (str, "(matrix 1.0 1.0 1.0 1.0)");
  477. }
  478. return TRUE;
  479. }
  480. if (G_VALUE_TYPE (value) == GIMP_TYPE_VALUE_ARRAY)
  481. {
  482. GimpValueArray *array;
  483. array = g_value_get_boxed (value);
  484. if (array)
  485. {
  486. gint length = gimp_value_array_length (array);
  487. gint i;
  488. g_string_append_printf (str, "%d", length);
  489. for (i = 0; i < length; i++)
  490. {
  491. g_string_append (str, " ");
  492. if (! gimp_config_serialize_value (gimp_value_array_index (array,
  493. i),
  494. str, TRUE))
  495. return FALSE;
  496. }
  497. }
  498. else
  499. {
  500. g_string_append (str, "0");
  501. }
  502. return TRUE;
  503. }
  504. if (G_VALUE_TYPE (value) == G_TYPE_FILE)
  505. {
  506. GFile *file = g_value_get_object (value);
  507. if (file)
  508. {
  509. gchar *path = g_file_get_path (file);
  510. gchar *unexpand = NULL;
  511. if (path)
  512. {
  513. unexpand = gimp_config_path_unexpand (path, TRUE, NULL);
  514. g_free (path);
  515. }
  516. if (unexpand)
  517. {
  518. if (escaped)
  519. gimp_config_string_append_escaped (str, unexpand);
  520. else
  521. g_string_append (str, unexpand);
  522. g_free (unexpand);
  523. }
  524. else
  525. {
  526. g_string_append (str, "NULL");
  527. }
  528. }
  529. else
  530. {
  531. g_string_append (str, "NULL");
  532. }
  533. return TRUE;
  534. }
  535. if (g_value_type_transformable (G_VALUE_TYPE (value), G_TYPE_STRING))
  536. {
  537. GValue tmp_value = G_VALUE_INIT;
  538. g_value_init (&tmp_value, G_TYPE_STRING);
  539. g_value_transform (value, &tmp_value);
  540. g_string_append (str, g_value_get_string (&tmp_value));
  541. g_value_unset (&tmp_value);
  542. return TRUE;
  543. }
  544. return FALSE;
  545. }