gimpconfig-utils.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  1. /* LIBGIMP - The GIMP Library
  2. * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
  3. *
  4. * Utility functions for GimpConfig.
  5. * Copyright (C) 2001-2003 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 <gio/gio.h>
  23. #include "libgimpbase/gimpbase.h"
  24. #include "gimpconfigtypes.h"
  25. #include "gimpconfigwriter.h"
  26. #include "gimpconfig-iface.h"
  27. #include "gimpconfig-params.h"
  28. #include "gimpconfig-utils.h"
  29. /**
  30. * SECTION: gimpconfig-utils
  31. * @title: GimpConfig-utils
  32. * @short_description: Miscellaneous utility functions for libgimpconfig.
  33. *
  34. * Miscellaneous utility functions for libgimpconfig.
  35. **/
  36. static gboolean
  37. gimp_config_diff_property (GObject *a,
  38. GObject *b,
  39. GParamSpec *prop_spec)
  40. {
  41. GValue a_value = G_VALUE_INIT;
  42. GValue b_value = G_VALUE_INIT;
  43. gboolean retval = FALSE;
  44. g_value_init (&a_value, prop_spec->value_type);
  45. g_value_init (&b_value, prop_spec->value_type);
  46. g_object_get_property (a, prop_spec->name, &a_value);
  47. g_object_get_property (b, prop_spec->name, &b_value);
  48. /* TODO: temporary hack to handle case of NULL GeglColor in a param value.
  49. * This got fixed in commit c0477bcb0 which should be available for GEGL
  50. * 0.4.50. In the meantime, this will do.
  51. */
  52. if (GEGL_IS_PARAM_SPEC_COLOR (prop_spec) &&
  53. (! g_value_get_object (&a_value) ||
  54. ! g_value_get_object (&b_value)))
  55. {
  56. retval = (g_value_get_object (&a_value) != g_value_get_object (&b_value));
  57. }
  58. else if (g_param_values_cmp (prop_spec, &a_value, &b_value))
  59. {
  60. if ((prop_spec->flags & GIMP_CONFIG_PARAM_AGGREGATE) &&
  61. G_IS_PARAM_SPEC_OBJECT (prop_spec) &&
  62. g_type_interface_peek (g_type_class_peek (prop_spec->value_type),
  63. GIMP_TYPE_CONFIG))
  64. {
  65. if (! gimp_config_is_equal_to (g_value_get_object (&a_value),
  66. g_value_get_object (&b_value)))
  67. {
  68. retval = TRUE;
  69. }
  70. }
  71. else
  72. {
  73. retval = TRUE;
  74. }
  75. }
  76. g_value_unset (&a_value);
  77. g_value_unset (&b_value);
  78. return retval;
  79. }
  80. static GList *
  81. gimp_config_diff_same (GObject *a,
  82. GObject *b,
  83. GParamFlags flags)
  84. {
  85. GParamSpec **param_specs;
  86. guint n_param_specs;
  87. guint i;
  88. GList *list = NULL;
  89. param_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (a),
  90. &n_param_specs);
  91. for (i = 0; i < n_param_specs; i++)
  92. {
  93. GParamSpec *prop_spec = param_specs[i];
  94. if (! flags || ((prop_spec->flags & flags) == flags))
  95. {
  96. if (gimp_config_diff_property (a, b, prop_spec))
  97. list = g_list_prepend (list, prop_spec);
  98. }
  99. }
  100. g_free (param_specs);
  101. return list;
  102. }
  103. static GList *
  104. gimp_config_diff_other (GObject *a,
  105. GObject *b,
  106. GParamFlags flags)
  107. {
  108. GParamSpec **param_specs;
  109. guint n_param_specs;
  110. guint i;
  111. GList *list = NULL;
  112. param_specs = g_object_class_list_properties (G_OBJECT_GET_CLASS (a),
  113. &n_param_specs);
  114. for (i = 0; i < n_param_specs; i++)
  115. {
  116. GParamSpec *a_spec = param_specs[i];
  117. GParamSpec *b_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (b),
  118. a_spec->name);
  119. if (b_spec &&
  120. (a_spec->value_type == b_spec->value_type) &&
  121. (! flags || (a_spec->flags & b_spec->flags & flags) == flags))
  122. {
  123. if (gimp_config_diff_property (a, b, b_spec))
  124. list = g_list_prepend (list, b_spec);
  125. }
  126. }
  127. g_free (param_specs);
  128. return list;
  129. }
  130. /**
  131. * gimp_config_diff:
  132. * @a: a #GObject
  133. * @b: another #GObject object
  134. * @flags: a mask of GParamFlags
  135. *
  136. * Compares all properties of @a and @b that have all @flags set. If
  137. * @flags is 0, all properties are compared.
  138. *
  139. * If the two objects are not of the same type, only properties that
  140. * exist in both object classes and are of the same value_type are
  141. * compared.
  142. *
  143. * Returns: (transfer container) (element-type GParamSpec): a GList of differing GParamSpecs.
  144. *
  145. * Since: 2.4
  146. **/
  147. GList *
  148. gimp_config_diff (GObject *a,
  149. GObject *b,
  150. GParamFlags flags)
  151. {
  152. GList *diff;
  153. g_return_val_if_fail (G_IS_OBJECT (a), NULL);
  154. g_return_val_if_fail (G_IS_OBJECT (b), NULL);
  155. if (G_TYPE_FROM_INSTANCE (a) == G_TYPE_FROM_INSTANCE (b))
  156. diff = gimp_config_diff_same (a, b, flags);
  157. else
  158. diff = gimp_config_diff_other (a, b, flags);
  159. return g_list_reverse (diff);
  160. }
  161. /**
  162. * gimp_config_sync:
  163. * @src: a #GObject
  164. * @dest: another #GObject
  165. * @flags: a mask of GParamFlags
  166. *
  167. * Compares all read- and write-able properties from @src and @dest
  168. * that have all @flags set. Differing values are then copied from
  169. * @src to @dest. If @flags is 0, all differing read/write properties.
  170. *
  171. * Properties marked as "construct-only" are not touched.
  172. *
  173. * If the two objects are not of the same type, only properties that
  174. * exist in both object classes and are of the same value_type are
  175. * synchronized
  176. *
  177. * Returns: %TRUE if @dest was modified, %FALSE otherwise
  178. *
  179. * Since: 2.4
  180. **/
  181. gboolean
  182. gimp_config_sync (GObject *src,
  183. GObject *dest,
  184. GParamFlags flags)
  185. {
  186. GList *diff;
  187. GList *list;
  188. g_return_val_if_fail (G_IS_OBJECT (src), FALSE);
  189. g_return_val_if_fail (G_IS_OBJECT (dest), FALSE);
  190. /* we use the internal versions here for a number of reasons:
  191. * - it saves a g_list_reverse()
  192. * - it avoids duplicated parameter checks
  193. */
  194. if (G_TYPE_FROM_INSTANCE (src) == G_TYPE_FROM_INSTANCE (dest))
  195. diff = gimp_config_diff_same (src, dest, (flags | G_PARAM_READWRITE));
  196. else
  197. diff = gimp_config_diff_other (src, dest, flags);
  198. if (!diff)
  199. return FALSE;
  200. g_object_freeze_notify (G_OBJECT (dest));
  201. for (list = diff; list; list = list->next)
  202. {
  203. GParamSpec *prop_spec = list->data;
  204. if (! (prop_spec->flags & G_PARAM_CONSTRUCT_ONLY))
  205. {
  206. GValue value = G_VALUE_INIT;
  207. g_value_init (&value, prop_spec->value_type);
  208. g_object_get_property (src, prop_spec->name, &value);
  209. g_object_set_property (dest, prop_spec->name, &value);
  210. g_value_unset (&value);
  211. }
  212. }
  213. g_object_thaw_notify (G_OBJECT (dest));
  214. g_list_free (diff);
  215. return TRUE;
  216. }
  217. /**
  218. * gimp_config_reset_properties:
  219. * @object: a #GObject
  220. *
  221. * Resets all writable properties of @object to the default values as
  222. * defined in their #GParamSpec. Properties marked as "construct-only"
  223. * are not touched.
  224. *
  225. * If you want to reset a #GimpConfig object, please use gimp_config_reset().
  226. *
  227. * Since: 2.4
  228. **/
  229. void
  230. gimp_config_reset_properties (GObject *object)
  231. {
  232. GObjectClass *klass;
  233. GParamSpec **property_specs;
  234. guint n_property_specs;
  235. guint i;
  236. g_return_if_fail (G_IS_OBJECT (object));
  237. klass = G_OBJECT_GET_CLASS (object);
  238. property_specs = g_object_class_list_properties (klass, &n_property_specs);
  239. if (!property_specs)
  240. return;
  241. g_object_freeze_notify (object);
  242. for (i = 0; i < n_property_specs; i++)
  243. {
  244. GParamSpec *prop_spec;
  245. GValue value = G_VALUE_INIT;
  246. prop_spec = property_specs[i];
  247. if ((prop_spec->flags & G_PARAM_WRITABLE) &&
  248. ! (prop_spec->flags & G_PARAM_CONSTRUCT_ONLY))
  249. {
  250. if (G_IS_PARAM_SPEC_OBJECT (prop_spec))
  251. {
  252. if ((prop_spec->flags & GIMP_CONFIG_PARAM_SERIALIZE) &&
  253. (prop_spec->flags & GIMP_CONFIG_PARAM_AGGREGATE) &&
  254. g_type_interface_peek (g_type_class_peek (prop_spec->value_type),
  255. GIMP_TYPE_CONFIG))
  256. {
  257. g_value_init (&value, prop_spec->value_type);
  258. g_object_get_property (object, prop_spec->name, &value);
  259. gimp_config_reset (g_value_get_object (&value));
  260. g_value_unset (&value);
  261. }
  262. }
  263. else
  264. {
  265. GValue default_value = G_VALUE_INIT;
  266. g_value_init (&default_value, prop_spec->value_type);
  267. g_value_init (&value, prop_spec->value_type);
  268. g_param_value_set_default (prop_spec, &default_value);
  269. g_object_get_property (object, prop_spec->name, &value);
  270. if (g_param_values_cmp (prop_spec, &default_value, &value))
  271. {
  272. g_object_set_property (object, prop_spec->name,
  273. &default_value);
  274. }
  275. g_value_unset (&value);
  276. g_value_unset (&default_value);
  277. }
  278. }
  279. }
  280. g_object_thaw_notify (object);
  281. g_free (property_specs);
  282. }
  283. /**
  284. * gimp_config_reset_property:
  285. * @object: a #GObject
  286. * @property_name: name of the property to reset
  287. *
  288. * Resets the property named @property_name to its default value. The
  289. * property must be writable and must not be marked as "construct-only".
  290. *
  291. * Since: 2.4
  292. **/
  293. void
  294. gimp_config_reset_property (GObject *object,
  295. const gchar *property_name)
  296. {
  297. GObjectClass *klass;
  298. GParamSpec *prop_spec;
  299. g_return_if_fail (G_IS_OBJECT (object));
  300. g_return_if_fail (property_name != NULL);
  301. klass = G_OBJECT_GET_CLASS (object);
  302. prop_spec = g_object_class_find_property (klass, property_name);
  303. if (!prop_spec)
  304. return;
  305. if ((prop_spec->flags & G_PARAM_WRITABLE) &&
  306. ! (prop_spec->flags & G_PARAM_CONSTRUCT_ONLY))
  307. {
  308. GValue value = G_VALUE_INIT;
  309. if (G_IS_PARAM_SPEC_OBJECT (prop_spec))
  310. {
  311. if ((prop_spec->flags & GIMP_CONFIG_PARAM_SERIALIZE) &&
  312. (prop_spec->flags & GIMP_CONFIG_PARAM_AGGREGATE) &&
  313. g_type_interface_peek (g_type_class_peek (prop_spec->value_type),
  314. GIMP_TYPE_CONFIG))
  315. {
  316. g_value_init (&value, prop_spec->value_type);
  317. g_object_get_property (object, prop_spec->name, &value);
  318. gimp_config_reset (g_value_get_object (&value));
  319. g_value_unset (&value);
  320. }
  321. }
  322. else
  323. {
  324. g_value_init (&value, prop_spec->value_type);
  325. g_param_value_set_default (prop_spec, &value);
  326. g_object_set_property (object, prop_spec->name, &value);
  327. g_value_unset (&value);
  328. }
  329. }
  330. }
  331. /*
  332. * GimpConfig string utilities
  333. */
  334. /**
  335. * gimp_config_string_append_escaped:
  336. * @string: pointer to a #GString
  337. * @val: a string to append or %NULL
  338. *
  339. * Escapes and quotes @val and appends it to @string. The escape
  340. * algorithm is different from the one used by g_strescape() since it
  341. * leaves non-ASCII characters intact and thus preserves UTF-8
  342. * strings. Only control characters and quotes are being escaped.
  343. *
  344. * Since: 2.4
  345. **/
  346. void
  347. gimp_config_string_append_escaped (GString *string,
  348. const gchar *val)
  349. {
  350. g_return_if_fail (string != NULL);
  351. if (val)
  352. {
  353. const guchar *p;
  354. gchar buf[4] = { '\\', 0, 0, 0 };
  355. gint len;
  356. g_string_append_c (string, '\"');
  357. for (p = (const guchar *) val, len = 0; *p; p++)
  358. {
  359. if (*p < ' ' || *p == '\\' || *p == '\"')
  360. {
  361. g_string_append_len (string, val, len);
  362. len = 2;
  363. switch (*p)
  364. {
  365. case '\b':
  366. buf[1] = 'b';
  367. break;
  368. case '\f':
  369. buf[1] = 'f';
  370. break;
  371. case '\n':
  372. buf[1] = 'n';
  373. break;
  374. case '\r':
  375. buf[1] = 'r';
  376. break;
  377. case '\t':
  378. buf[1] = 't';
  379. break;
  380. case '\\':
  381. case '"':
  382. buf[1] = *p;
  383. break;
  384. default:
  385. len = 4;
  386. buf[1] = '0' + (((*p) >> 6) & 07);
  387. buf[2] = '0' + (((*p) >> 3) & 07);
  388. buf[3] = '0' + ((*p) & 07);
  389. break;
  390. }
  391. g_string_append_len (string, buf, len);
  392. val = (const gchar *) p + 1;
  393. len = 0;
  394. }
  395. else
  396. {
  397. len++;
  398. }
  399. }
  400. g_string_append_len (string, val, len);
  401. g_string_append_c (string, '\"');
  402. }
  403. else
  404. {
  405. g_string_append_len (string, "\"\"", 2);
  406. }
  407. }