GnuCash  5.6-150-g038405b370+
gnc-gsettings.cpp
1 /********************************************************************\
2  * gnc-gsettings.c -- utility functions for storing/retrieving *
3  * data in the GSettings database for GnuCash *
4  * Copyright (C) 2013 Geert Janssens <geert@kobaltwit.be> *
5  * *
6  * This program is free software; you can redistribute it and/or *
7  * modify it under the terms of the GNU General Public License as *
8  * published by the Free Software Foundation; either version 2 of *
9  * the License, or (at your option) any later version. *
10  * *
11  * This program is distributed in the hope that it will be useful, *
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14  * GNU General Public License for more details. *
15  * *
16  * You should have received a copy of the GNU General Public License*
17  * along with this program; if not, contact: *
18  * *
19  * Free Software Foundation Voice: +1-617-542-5942 *
20  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
21  * Boston, MA 02110-1301, USA gnu@gnu.org *
22  * *
23 \********************************************************************/
24 
25 #include <config.h>
26 
27 
28 #include <gio/gio.h>
29 #include <glib.h>
30 
31 #include <stdio.h>
32 #include <string.h>
33 #include "gnc-gsettings.h"
34 #include "gnc-path.h"
35 #include "qof.h"
36 #include "gnc-prefs-p.h"
37 
38 #include <boost/property_tree/ptree.hpp>
39 #include <boost/property_tree/xml_parser.hpp>
40 #include <fstream>
41 #include <iostream>
42 #include <unordered_map>
43 
44 namespace bpt = boost::property_tree;
45 
46 #define GSET_SCHEMA_PREFIX "org.gnucash.GnuCash"
47 #define GSET_SCHEMA_OLD_PREFIX "org.gnucash"
48 
50 {
51  void operator()(GSettings* gsp)
52  {
53  g_object_unref(gsp);
54  }
55 };
56 
57 static GSettingsDeleter g_settings_deleter;
58 
59 using GSettingsPtr = std::unique_ptr<GSettings, GSettingsDeleter>;
60 
61 static std::unordered_map<std::string,GSettingsPtr> schema_hash;
62 
63 /* This static indicates the debugging module that this .o belongs to. */
64 static QofLogModule log_module = "gnc.app-utils.gsettings";
65 
66 /************************************************************/
67 /* Internal helper functions */
68 /************************************************************/
69 static bool gnc_gsettings_is_valid_key(GSettings *settings, const gchar *key)
70 {
71  // Check if the key is valid key within settings
72  if (!G_IS_SETTINGS(settings))
73  return false;
74 
75  GSettingsSchema *schema;
76  g_object_get (settings, "settings-schema", &schema, nullptr);
77  if (!schema)
78  return false;
79 
80  auto keys = g_settings_schema_list_keys (schema);
81  auto found = (keys && g_strv_contains(keys, key));
82  g_strfreev (keys);
83  g_settings_schema_unref (schema);
84 
85  return found;
86 }
87 
88 static std::string
89 normalize_schema_name (const gchar *name)
90 {
91  if (!name)
92  return GSET_SCHEMA_PREFIX;
93 
94  if (g_str_has_prefix (name, GSET_SCHEMA_PREFIX) ||
95  (g_str_has_prefix (name, GSET_SCHEMA_OLD_PREFIX)))
96  return name;
97 
98  return std::string{GSET_SCHEMA_PREFIX} + '.' + name;
99 }
100 
101 static GSettings * gnc_gsettings_get_settings_obj (const gchar *schema_str)
102 {
103  ENTER("");
104 
105  auto full_name_str = normalize_schema_name (schema_str);
106  auto full_name = full_name_str.c_str();
107  auto schema_source {g_settings_schema_source_get_default()};
108  auto schema {g_settings_schema_source_lookup(schema_source, full_name, true)};
109  auto gset = g_settings_new_full (schema, nullptr, nullptr);
110  DEBUG ("Created gsettings object %p for schema %s", gset, full_name);
111 
112  if (!G_IS_SETTINGS(gset))
113  PWARN ("Ignoring attempt to access unknown gsettings schema %s", full_name);
114 
115  LEAVE("");
116  g_settings_schema_unref (schema);
117  return gset;
118 }
119 
120 static GSettings*
121 schema_to_gsettings (const char *schema, bool can_retrieve)
122 {
123  auto full_name = normalize_schema_name (schema);
124  auto iter = schema_hash.find (full_name);
125  if (iter != schema_hash.end())
126  return iter->second.get();
127 
128  if (!can_retrieve)
129  return nullptr;
130 
131  auto gs_obj = gnc_gsettings_get_settings_obj (schema);
132  if (!G_IS_SETTINGS (gs_obj))
133  {
134  PWARN ("Ignoring attempt to access unknown gsettings schema %s", full_name.c_str());
135  return nullptr;
136  }
137 
138  schema_hash[full_name] = GSettingsPtr (gs_obj, g_settings_deleter);
139  return gs_obj;
140 }
141 
142 /************************************************************/
143 /* GSettings Utilities */
144 /************************************************************/
145 
146 const gchar *
148 {
149  return GSET_SCHEMA_PREFIX;
150 }
151 
152 /************************************************************/
153 /* Change notification */
154 /************************************************************/
155 
156 gulong
157 gnc_gsettings_register_cb (const gchar *schema, const gchar *key,
158  gpointer func,
159  gpointer user_data)
160 {
161  ENTER("");
162  g_return_val_if_fail (func, 0);
163 
164  auto gs_obj = schema_to_gsettings (schema, true);
165  g_return_val_if_fail (G_IS_SETTINGS (gs_obj), 0);
166 
167  auto signal = static_cast<char *> (nullptr);
168  if (!(key && *key))
169  signal = g_strdup ("changed");
170  else if (gnc_gsettings_is_valid_key(gs_obj, key))
171  signal = g_strconcat ("changed::", key, nullptr);
172 
173  auto handlerid = g_signal_connect (gs_obj, signal, G_CALLBACK (func), user_data);
174  if (handlerid)
175  {
176  g_object_ref (gs_obj);
177 
178  PINFO("schema: %s, key: %s, gs_obj: %p, handler_id: %ld",
179  schema, key, gs_obj, handlerid);
180  }
181  g_free (signal);
182 
183  LEAVE("");
184  return handlerid;
185 }
186 
187 
188 static void
189 gnc_gsettings_remove_cb_by_id_internal (GSettings *gs_obj, guint handlerid)
190 {
191  ENTER ();
192  g_return_if_fail (G_IS_SETTINGS (gs_obj));
193 
194  g_signal_handler_disconnect (gs_obj, handlerid);
195  g_object_unref (gs_obj);
196 
197  LEAVE ("Schema: %p, handlerid: %d - removed for handler",
198  gs_obj, handlerid);
199 }
200 
201 
202 void
203 gnc_gsettings_remove_cb_by_func (const gchar *schema, const gchar *key,
204  gpointer func, gpointer user_data)
205 {
206  ENTER ();
207  g_return_if_fail (func);
208 
209  auto gs_obj = schema_to_gsettings (schema, false);
210 
211  if (!G_IS_SETTINGS (gs_obj))
212  {
213  LEAVE ("No valid GSettings object retrieved from hash table");
214  return;
215  }
216 
217  auto match_type = static_cast<GSignalMatchType> (G_SIGNAL_MATCH_DETAIL | G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA);
218  auto changed_signal = g_signal_lookup ("changed", G_TYPE_SETTINGS); /* signal_id */
219  auto quark = g_quark_from_string (key); /* signal_detail */
220 
221  auto matched = 0;
222  guint handler_id = 0;
223  do
224  {
225  handler_id = g_signal_handler_find (gs_obj, match_type,
226  changed_signal, quark, nullptr,
227  func, user_data);
228  if (handler_id)
229  {
230  matched ++;
231  gnc_gsettings_remove_cb_by_id_internal (gs_obj, handler_id);
232 
233  // Previous function will invalidate object if there is only one handler
234  if (!G_IS_SETTINGS (gs_obj))
235  handler_id = 0;
236  }
237  } while (handler_id);
238 
239  LEAVE ("Schema: %s, key: %s - removed %d handlers for 'changed' signal",
240  schema, key, matched);
241 }
242 
243 
244 void
245 gnc_gsettings_remove_cb_by_id (const gchar *schema, guint handlerid)
246 {
247  ENTER ();
248 
249  auto gs_obj = schema_to_gsettings (schema, false);
250 
251  if (!G_IS_SETTINGS (gs_obj))
252  {
253  LEAVE ("No valid GSettings object retrieved from hash table");
254  return;
255  }
256 
257  gnc_gsettings_remove_cb_by_id_internal (gs_obj, handlerid);
258 
259  LEAVE ("Schema: %p, handlerid: %d - removed for handler",
260  gs_obj, handlerid);
261 }
262 
263 
264 guint
265 gnc_gsettings_register_any_cb (const gchar *schema,
266  gpointer func,
267  gpointer user_data)
268 {
269  return gnc_gsettings_register_cb (schema, nullptr, func, user_data);
270 }
271 
272 
273 void
275  gpointer func,
276  gpointer user_data)
277 {
278  gnc_gsettings_remove_cb_by_func (schema, nullptr, func, user_data);
279 }
280 
281 
282 void gnc_gsettings_bind (const gchar *schema,
283  /*@ null @*/ const gchar *key,
284  gpointer object,
285  const gchar *property)
286 {
287  auto gs_obj = gnc_gsettings_get_settings_obj (schema);
288  g_return_if_fail (G_IS_SETTINGS (gs_obj));
289 
290  if (gnc_gsettings_is_valid_key (gs_obj, key))
291  g_settings_bind (gs_obj, key, object, property, G_SETTINGS_BIND_DEFAULT);
292  else
293  PERR ("Invalid key %s for schema %s", key, schema);
294 }
295 
296 
297 static void
298 gs_obj_block_handlers ([[maybe_unused]] gpointer key, gpointer gs_obj,
299  [[maybe_unused]] gpointer pointer)
300 {
301  g_signal_handlers_block_matched (gs_obj, G_SIGNAL_MATCH_CLOSURE, 0, 0, nullptr, nullptr, nullptr);
302  PINFO("Block all handlers for GSettings object %p", gs_obj);
303 }
304 
305 static void
306 gs_obj_unblock_handlers ([[maybe_unused]] gpointer key, gpointer gs_obj,
307  [[maybe_unused]] gpointer pointer)
308 {
309  g_signal_handlers_unblock_matched (gs_obj, G_SIGNAL_MATCH_CLOSURE, 0, 0, nullptr, nullptr, nullptr);
310  PINFO("Unblock all handlers for GSettings object %p", gs_obj);
311 }
312 
314 {
315  ENTER ();
316  for (const auto& it : schema_hash)
317  gs_obj_block_handlers (nullptr, it.second.get(), nullptr);
318  LEAVE();
319 }
320 
321 
323 {
324  ENTER ();
325  for (const auto& it : schema_hash)
326  gs_obj_unblock_handlers (nullptr, it.second.get(), nullptr);
327  LEAVE();
328 }
329 
330 
331 /************************************************************/
332 /* Getters */
333 /************************************************************/
334 template<typename T>
335 T gnc_gsettings_get(const char *schema, const char *key,
336  auto getter(GSettings*, const char *)->T, T default_val)
337 {
338  auto gs_obj = gnc_gsettings_get_settings_obj (schema);
339  g_return_val_if_fail (G_IS_SETTINGS (gs_obj), default_val);
340 
341  T val = default_val;
342  if (gnc_gsettings_is_valid_key (gs_obj, key))
343  val = getter (gs_obj, key);
344  else
345  PERR ("Invalid key %s for schema %s", key, schema);
346 
347  g_object_unref (gs_obj);
348  return val;
349 }
350 
351 gboolean
352 gnc_gsettings_get_bool (const gchar *schema, const gchar *key)
353 {
354  return gnc_gsettings_get (schema, key, g_settings_get_boolean,
355  static_cast<gboolean>(false));
356 }
357 
358 gint
359 gnc_gsettings_get_int (const gchar *schema, const gchar *key)
360 {
361  return gnc_gsettings_get (schema, key, g_settings_get_int, 0);
362 }
363 
364 gdouble
365 gnc_gsettings_get_float (const gchar *schema, const gchar *key)
366 {
367  return gnc_gsettings_get (schema, key, g_settings_get_double, 0.0);
368 }
369 
370 gchar *
371 gnc_gsettings_get_string (const gchar *schema, const gchar *key)
372 {
373  return gnc_gsettings_get (schema, key, g_settings_get_string,
374  static_cast<gchar *> (nullptr));
375 }
376 
377 gint
378 gnc_gsettings_get_enum (const gchar *schema, const gchar *key)
379 {
380  return gnc_gsettings_get (schema, key, g_settings_get_enum, 0);
381 }
382 
383 GVariant *
384 gnc_gsettings_get_value (const gchar *schema, const gchar *key)
385 {
386  return gnc_gsettings_get (schema, key, g_settings_get_value,
387  static_cast<GVariant *> (nullptr));
388 }
389 
390 /************************************************************/
391 /* Setters */
392 /************************************************************/
393 template<typename T> gboolean
394 gnc_gsettings_set (const gchar *schema,
395  const gchar *key,
396  T value,
397  gboolean setter(GSettings*, const char *, T))
398 {
399  ENTER("schema: %s, key: %s", schema, key);
400 
401  auto gs_obj = gnc_gsettings_get_settings_obj (schema);
402  g_return_val_if_fail (G_IS_SETTINGS (gs_obj), false);
403 
404  auto result = false;
405  if (gnc_gsettings_is_valid_key (gs_obj, key))
406  {
407  result = setter (gs_obj, key, value);
408  if (!result)
409  PERR ("Unable to set value for key %s in schema %s", key, schema);
410  }
411  else
412  PERR ("Invalid key %s for schema %s", key, schema);
413 
414  g_object_unref (gs_obj);
415  LEAVE("result %i", result);
416  return result;
417 }
418 
419 gboolean
420 gnc_gsettings_set_bool (const gchar *schema, const gchar *key, gboolean value)
421 {
422  return gnc_gsettings_set (schema, key, value, g_settings_set_boolean);
423 }
424 
425 gboolean
426 gnc_gsettings_set_int (const gchar *schema, const gchar *key, gint value)
427 {
428  return gnc_gsettings_set (schema, key, value, g_settings_set_int);
429 }
430 
431 gboolean
432 gnc_gsettings_set_float (const gchar *schema, const gchar *key, gdouble value)
433 {
434  return gnc_gsettings_set (schema, key, value, g_settings_set_double);
435 }
436 
437 gboolean
438 gnc_gsettings_set_string (const gchar *schema, const gchar *key, const gchar *value)
439 {
440  return gnc_gsettings_set (schema, key, value, g_settings_set_string);
441 }
442 
443 gboolean
444 gnc_gsettings_set_enum (const gchar *schema, const gchar *key, gint value)
445 {
446  return gnc_gsettings_set (schema, key, value, g_settings_set_enum);
447 }
448 
449 gboolean
450 gnc_gsettings_set_value (const gchar *schema, const gchar *key, GVariant *value)
451 {
452  return gnc_gsettings_set (schema, key, value, g_settings_set_value);
453 }
454 
455 void
456 gnc_gsettings_reset (const gchar *schema,
457  const gchar *key)
458 {
459  auto gs_obj = gnc_gsettings_get_settings_obj (schema);
460  g_return_if_fail (G_IS_SETTINGS (gs_obj));
461 
462  if (gnc_gsettings_is_valid_key (gs_obj, key))
463  g_settings_reset (gs_obj, key);
464  else
465  PERR ("Invalid key %s for schema %s", key, schema);
466 
467  g_object_unref (gs_obj);
468 }
469 
470 void
471 gnc_gsettings_reset_schema (const gchar *schema_str)
472 {
473  auto gs_obj = gnc_gsettings_get_settings_obj (schema_str);
474 
475  if (!gs_obj)
476  return;
477 
478  GSettingsSchema *schema;
479  g_object_get (gs_obj, "settings-schema", &schema, nullptr);
480  if (!schema)
481  {
482  g_object_unref (gs_obj);
483  return;
484  }
485 
486  auto keys = g_settings_schema_list_keys (schema);
487  if (keys)
488  {
489  auto fkeys = keys;
490  for (auto key = *fkeys; key; key = *++fkeys)
491  gnc_gsettings_reset (schema_str, key);
492  }
493 
494  g_object_unref (gs_obj);
495  g_settings_schema_unref (schema);
496  g_strfreev (keys);
497 }
498 
499 static void
500 gnc_settings_dump_schema_paths (void)
501 {
502  gchar **non_relocatable;
503 
504  auto schema_source {g_settings_schema_source_get_default()};
505  g_settings_schema_source_list_schemas (schema_source, true,
506  &non_relocatable, nullptr);
507 
508  for (gint i = 0; non_relocatable[i] != nullptr; i++)
509  PINFO("Schema entry %d is '%s'", i, non_relocatable[i]);
510 
511  g_strfreev (non_relocatable);
512 }
513 
515 {
516  ENTER("");
517 
518  /* The gsettings backend only works in an installed environment.
519  * When called from the source environment (for testing purposes)
520  * simply return.
521  */
522  if (g_strcmp0 (g_getenv ("GNC_UNINSTALLED"), "1") == 0)
523  return;
524 
525  g_free (prefsbackend);
526  prefsbackend = g_new0 (PrefsBackend, 1);
527 
528  prefsbackend->register_cb = gnc_gsettings_register_cb;
529  prefsbackend->remove_cb_by_func = gnc_gsettings_remove_cb_by_func;
530  prefsbackend->remove_cb_by_id = gnc_gsettings_remove_cb_by_id;
531  prefsbackend->register_group_cb = gnc_gsettings_register_any_cb;
532  prefsbackend->remove_group_cb_by_func = gnc_gsettings_remove_any_cb_by_func;
533  prefsbackend->bind = gnc_gsettings_bind;
534  prefsbackend->get_bool = gnc_gsettings_get_bool;
535  prefsbackend->get_int = gnc_gsettings_get_int;
536  prefsbackend->get_float = gnc_gsettings_get_float;
537  prefsbackend->get_string = gnc_gsettings_get_string;
538  prefsbackend->get_enum = gnc_gsettings_get_enum;
539  prefsbackend->get_value = gnc_gsettings_get_value;
540  prefsbackend->set_bool = gnc_gsettings_set_bool;
541  prefsbackend->set_int = gnc_gsettings_set_int;
542  prefsbackend->set_float = gnc_gsettings_set_float;
543  prefsbackend->set_string = gnc_gsettings_set_string;
544  prefsbackend->set_enum = gnc_gsettings_set_enum;
545  prefsbackend->set_value = gnc_gsettings_set_value;
546  prefsbackend->reset = gnc_gsettings_reset;
547  prefsbackend->reset_group = gnc_gsettings_reset_schema;
548  prefsbackend->block_all = gnc_gsettings_block_all;
549  prefsbackend->unblock_all = gnc_gsettings_unblock_all;
550 
551  if (qof_log_check (log_module, QOF_LOG_DEBUG))
552  gnc_settings_dump_schema_paths ();
553 
554  /* Run any data model changes for the backend before it's used
555  * by anyone */
557 
558  LEAVE("Prefsbackend bind = %p", prefsbackend->bind);
559 }
560 
561 void
563 {
564  schema_hash.clear();
565  g_free (prefsbackend);
566 }
567 
568 
569 static GVariant *
570 gnc_gsettings_get_user_value (const gchar *schema,
571  const gchar *key)
572 {
573  auto gs_obj = gnc_gsettings_get_settings_obj (schema);
574  g_return_val_if_fail (G_IS_SETTINGS (gs_obj), nullptr);
575 
576  auto val = static_cast<GVariant *> (nullptr);
577  if (gnc_gsettings_is_valid_key (gs_obj, key))
578  val = g_settings_get_user_value (gs_obj, key);
579  else
580  PERR ("Invalid key %s for schema %s", key, schema);
581 
582  g_object_unref (gs_obj);
583  return val;
584 }
585 
586 using opt_str_vec = boost::optional<std::string>;
587 
588 static void
589 deprecate_one_key (const opt_str_vec &oldpath, const opt_str_vec &oldkey)
590 {
591  if (!oldpath || !oldkey )
592  {
593  DEBUG ("Skipping <deprecate> node - missing attribute (old-path or old-key)");
594  return;
595  }
596 
597  PINFO ("'%s:%s' has been marked deprecated", oldpath->c_str(), oldkey->c_str());
598  /* This does nothing really, but is a reminder for future maintainers
599  * to mark this pref as obsolete in the next major release. */
600 }
601 
602 static void
603 migrate_one_key (const opt_str_vec &oldpath, const opt_str_vec &oldkey,
604  const opt_str_vec &newpath, const opt_str_vec &newkey)
605 {
606  if (!oldpath || !oldkey || !newpath || !newkey)
607  {
608  DEBUG ("Skipping <migrate> node - missing attribute (old-path, old-key, new-path or new-key)");
609  return;
610  }
611 
612  PINFO ("Migrating '%s:%s' to '%s:%s'", oldpath->c_str(), oldkey->c_str(),
613  newpath->c_str(), newkey->c_str());
614 
615  auto user_value = gnc_gsettings_get_user_value (oldpath->c_str(), oldkey->c_str());
616  if (user_value)
617  gnc_gsettings_set_value (newpath->c_str(), newkey->c_str(), user_value);
618 }
619 
620 static void
621 obsolete_one_key (const opt_str_vec &oldpath, const opt_str_vec &oldkey)
622 {
623  if (!oldpath || !oldkey )
624  {
625  DEBUG ("Skipping <obsolete> node - missing attribute (old-path or old-key)");
626  return;
627  }
628 
629  PINFO ("Resetting obsolete '%s:%s'", oldpath->c_str(), oldkey->c_str());
630  gnc_gsettings_reset (oldpath->c_str(), oldkey->c_str());
631 }
632 
633 static void
634 parse_one_release_node (bpt::ptree &pt)
635 {
636  /* loop over top-level property tree */
637  std::for_each (pt.begin(), pt.end(),
638  [] (std::pair<bpt::ptree::key_type, bpt::ptree> node)
639  {
640  if (node.first == "<xmlattr>")
641  return;
642  else if (node.first == "deprecate")
643  deprecate_one_key (node.second.get_optional<std::string> ("<xmlattr>.old-path"),
644  node.second.get_optional<std::string> ("<xmlattr>.old-key"));
645  else if (node.first == "migrate")
646  migrate_one_key (node.second.get_optional<std::string> ("<xmlattr>.old-path"),
647  node.second.get_optional<std::string> ("<xmlattr>.old-key"),
648  node.second.get_optional<std::string> ("<xmlattr>.new-path"),
649  node.second.get_optional<std::string> ("<xmlattr>.new-key"));
650  else if (node.first == "obsolete")
651  obsolete_one_key (node.second.get_optional<std::string> ("<xmlattr>.old-path"),
652  node.second.get_optional<std::string> ("<xmlattr>.old-key"));
653  else
654  {
655  DEBUG ("Skipping unknown node <%s>", node.first.c_str());
656  return;
657  }
658  });
659 }
660 
661 static void
662 transform_settings (int old_maj_min, int cur_maj_min)
663 {
664  bpt::ptree pt;
665 
666  auto pkg_data_dir = gnc_path_get_pkgdatadir();
667  auto transform_file = std::string (pkg_data_dir) + "/pref_transformations.xml";
668  g_free (pkg_data_dir);
669 
670  std::ifstream transform_stream {transform_file};
671  if (!transform_stream.is_open())
672  {
673  PWARN("Failed to load preferences transformation file '%s'", transform_file.c_str());
674  return;
675  }
676 
677  try
678  {
679  bpt::read_xml (transform_stream, pt);
680  }
681  catch (bpt::xml_parser_error &e) {
682  PWARN ("Failed to parse GnuCash preferences transformation file.\n");
683  PWARN ("Error message:\n");
684  PWARN ("%s\n", e.what());
685  return;
686  }
687  catch (...) {
688  PWARN ("Unknown error while parsing GnuCash preferences transformation file.\n");
689  return;
690  }
691 
692  /* loop over top-level property tree */
693  std::for_each (pt.begin(), pt.end(),
694  [&old_maj_min, &cur_maj_min] (std::pair<bpt::ptree::key_type, bpt::ptree> node)
695  {
696  if (node.first != "release")
697  {
698  DEBUG ("Skipping non-<release> node <%s>", node.first.c_str());
699  return;
700  }
701  auto version = node.second.get_optional<int> ("<xmlattr>.version");
702  if (!version)
703  {
704  DEBUG ("Skipping <release> node - no version attribute found");
705  return;
706  }
707  if (*version <= old_maj_min)
708  {
709  DEBUG ("Skipping <release> node - version %i is less than current compatibility level %i", *version, old_maj_min);
710  return;
711  }
712  if (*version > cur_maj_min)
713  {
714  DEBUG ("Skipping <release> node - version %i is greater than current version level %i", *version, cur_maj_min);
715  return;
716  }
717  DEBUG ("Retrieved version value '%i'", *version);
718 
719  parse_one_release_node (node.second);
720  });
721 }
722 
724 {
725  /* This routine will conditionally execute conversion rules from
726  * prefs_transformations.xml to adapt the user's existing preferences to
727  * the current preferences schema. The rules in this file are versioned and
728  * only rules still relevant to the user's existing preferences and for
729  * this version of GnuCash will be executed.
730  *
731  * Starting with GnuCash 4.7 the code expects all preferences to be stored
732  * under prefix org.gnucash.GnuCash instead of org.gnucash, including our
733  * GNC_PREF_VERSION setting.
734  * As the logic to determine whether or not settings conversions are needed
735  * depends on this preference, we have to test for its value in two
736  * locations:
737  * - if GNC_PREF_VERSION is not set under old nor new prefix
738  * => GnuCash has never run before so no conversion run necessary
739  * - if GNC_PREF_VERSION is set under old prefix and not new prefix
740  * => user's preferences weren't moved yet from old to new prefix. Use old
741  * prefix GNC_PREF_VERSION to determine which conversions may be needed
742  * - if GNC_PREF_VERSION is set under both prefixes
743  * => ignore old prefix and use new prefix GNC_PREF_VERSION to determine
744  * which conversions may be needed.
745  * Sometime in the future (GnuCash 6.0) the old prefix will be fully removed
746  * and the test will be simplified to only check in the new prefix.
747  */
748  ENTER("Start of settings transform routine.");
749 
750  auto ogG_maj_min = gnc_gsettings_get_user_value (GNC_PREFS_GROUP_GENERAL, GNC_PREF_VERSION);
751  auto og_maj_min = gnc_gsettings_get_user_value (GSET_SCHEMA_OLD_PREFIX "." GNC_PREFS_GROUP_GENERAL, GNC_PREF_VERSION);
752 
753  auto cur_maj_min = PROJECT_VERSION_MAJOR * 1000 + PROJECT_VERSION_MINOR;
754 
755  if (!ogG_maj_min && !og_maj_min) // new install
756  {
757  gnc_gsettings_set_int (GNC_PREFS_GROUP_GENERAL, GNC_PREF_VERSION, cur_maj_min);
758  LEAVE ("Setting Previous compatibility level to current version: %i", cur_maj_min);
759  return;
760  }
761 
762  auto old_maj_min = 0;
763  if (!ogG_maj_min) // old preference location
764  old_maj_min = gnc_gsettings_get_int (GSET_SCHEMA_OLD_PREFIX "." GNC_PREFS_GROUP_GENERAL, GNC_PREF_VERSION);
765  else // new preference location
766  {
767  g_variant_unref (ogG_maj_min);
768  old_maj_min = gnc_gsettings_get_int (GNC_PREFS_GROUP_GENERAL, GNC_PREF_VERSION);
769  }
770  if (og_maj_min)
771  g_variant_unref (og_maj_min);
772 
773  PINFO ("Previous setting compatibility level: %i, Current version: %i", old_maj_min, cur_maj_min);
774 
775  transform_settings (old_maj_min, cur_maj_min);
776 
777  /* Only write current version if it's more recent than what was set */
778  if (cur_maj_min > old_maj_min)
779  gnc_gsettings_set_int (GNC_PREFS_GROUP_GENERAL, GNC_PREF_VERSION, cur_maj_min);
780 
781  LEAVE("");
782 }
gboolean gnc_gsettings_set_int(const gchar *schema, const gchar *key, gint value)
Store an integer value into GSettings.
gboolean gnc_gsettings_set_float(const gchar *schema, const gchar *key, gdouble value)
Store a float value into GSettings.
#define PINFO(format, args...)
Print an informational note.
Definition: qoflog.h:256
gint gnc_gsettings_get_int(const gchar *schema, const gchar *key)
Get an integer value from GSettings.
#define DEBUG(format, args...)
Print a debugging message.
Definition: qoflog.h:264
void gnc_gsettings_bind(const gchar *schema, const gchar *key, gpointer object, const gchar *property)
Bind a setting to a g_object property.
#define PERR(format, args...)
Log a serious error.
Definition: qoflog.h:244
#define ENTER(format, args...)
Print a function entry debugging message.
Definition: qoflog.h:272
void gnc_gsettings_remove_any_cb_by_func(const gchar *schema, gpointer func, gpointer user_data)
Remove a function that was registered for a callback when any key in the given settings schema change...
gboolean gnc_gsettings_set_enum(const gchar *schema, const gchar *key, gint value)
Store an enum value into GSettings.
#define PWARN(format, args...)
Log a warning.
Definition: qoflog.h:250
gboolean qof_log_check(QofLogModule domain, QofLogLevel level)
Check to see if the given log_module is configured to log at the given log_level. ...
Definition: qoflog.cpp:324
GVariant * gnc_gsettings_get_value(const gchar *schema, const gchar *key)
Get an arbitrary combination of values from GSettings.
guint gnc_gsettings_register_any_cb(const gchar *schema, gpointer func, gpointer user_data)
Register a callback for when any key in the settings schema is changed.
void gnc_gsettings_unblock_all(void)
UnBlock all prefs callbacks, used while preference dialog is loaded.
void gnc_gsettings_reset_schema(const gchar *schema_str)
Reset all keys in a schema to their default values in GSettings.
void gnc_gsettings_shutdown(void)
Free the GSettings resources.
gdouble gnc_gsettings_get_float(const gchar *schema, const gchar *key)
Get an float value from GSettings.
void gnc_gsettings_reset(const gchar *schema, const gchar *key)
Reset a key to its default value in GSettings.
gchar * gnc_gsettings_get_string(const gchar *schema, const gchar *key)
Get a string value from GSettings.
gint gnc_gsettings_get_enum(const gchar *schema, const gchar *key)
Get an enum value from GSettings.
gboolean gnc_gsettings_set_bool(const gchar *schema, const gchar *key, gboolean value)
Store a boolean value into GSettings.
void gnc_gsettings_load_backend(void)
Configure gsettings as the backend for the gnucash preferences api.
void gnc_gsettings_version_upgrade(void)
Check whether we need to adjust the user settings to a newer version.
void gnc_gsettings_block_all(void)
Block all prefs callbacks, used while preference dialog is loaded.
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282
gboolean gnc_gsettings_get_bool(const gchar *schema, const gchar *key)
Get a boolean value from GSettings.
const gchar * gnc_gsettings_get_prefix(void)
Get the default gsettings schema prefix.
void gnc_gsettings_remove_cb_by_id(const gchar *schema, guint handlerid)
Remove a function that was registered for a callback when a specific key in the settings schema chang...
gboolean gnc_gsettings_set_value(const gchar *schema, const gchar *key, GVariant *value)
Store an arbitrary combination of values into GSettings.
gulong gnc_gsettings_register_cb(const char *schema, const gchar *key, gpointer func, gpointer user_data)
Register a callback for when a specific key in the settings schema is changed.
GSettings helper routines.
gboolean gnc_gsettings_set_string(const gchar *schema, const gchar *key, const gchar *value)
Store a string into GSettings.
void gnc_gsettings_remove_cb_by_func(const gchar *schema, const gchar *key, gpointer func, gpointer user_data)
Remove a function that was registered for a callback when a specific key in the settings schema chang...