GnuCash  4.11-243-g1cac132214+
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 extern "C" {
32 #include <stdio.h>
33 #include <string.h>
34 #include "gnc-gsettings.h"
35 #include "gnc-path.h"
36 #include "qof.h"
37 #include "gnc-prefs-p.h"
38 }
39 
40 #include <boost/property_tree/ptree.hpp>
41 #include <boost/property_tree/xml_parser.hpp>
42 #include <fstream>
43 #include <iostream>
44 
45 namespace bpt = boost::property_tree;
46 
47 #define GSET_SCHEMA_PREFIX "org.gnucash.GnuCash"
48 #define GSET_SCHEMA_OLD_PREFIX "org.gnucash"
49 #define CLIENT_TAG "%s-%s-client"
50 #define NOTIFY_TAG "%s-%s-notify_id"
51 
52 static GHashTable *schema_hash = NULL;
53 static const gchar *gsettings_prefix;
54 
55 static GHashTable *registered_handlers_hash = NULL;
56 
57 /* This static indicates the debugging module that this .o belongs to. */
58 static QofLogModule log_module = "gnc.app-utils.gsettings";
59 
60 /************************************************************/
61 /* Internal helper functions */
62 /************************************************************/
63 static gboolean gnc_gsettings_is_valid_key(GSettings *settings, const gchar *key)
64 {
65  gchar **keys = NULL;
66  gint i = 0;
67  gboolean found = FALSE;
68  GSettingsSchema *schema;
69 
70  // Check if the key is valid key within settings
71  if (!G_IS_SETTINGS(settings))
72  return FALSE;
73 
74  g_object_get (settings, "settings-schema", &schema, NULL);
75  if (!schema)
76  return FALSE;
77 
78  keys = g_settings_schema_list_keys (schema);
79  while (keys && keys[i])
80  {
81  if (!g_strcmp0(key, keys[i]))
82  {
83  found = TRUE;
84  break;
85  }
86  i++;
87  }
88  g_strfreev(keys);
89 
90  return found;
91 }
92 
93 static GSettings * gnc_gsettings_get_settings_ptr (const gchar *schema_str)
94 {
95  GSettings *gset = NULL;
96  gchar *full_name = gnc_gsettings_normalize_schema_name (schema_str);
97 
98  ENTER("");
99  if (!schema_hash)
100  schema_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
101 
102  gset = static_cast<GSettings*> (g_hash_table_lookup (schema_hash, full_name));
103  DEBUG ("Looking for schema %s returned gsettings %p", full_name, gset);
104 
105  if (!gset)
106  {
107  auto schema_source {g_settings_schema_source_get_default()};
108  auto schema {g_settings_schema_source_lookup(schema_source, full_name,
109  FALSE)};
110  gset = g_settings_new_full (schema, nullptr, nullptr);
111  DEBUG ("Created gsettings object %p for schema %s", gset, full_name);
112  if (G_IS_SETTINGS(gset))
113  g_hash_table_insert (schema_hash, full_name, gset);
114  else
115  PWARN ("Ignoring attempt to access unknown gsettings schema %s", full_name);
116  }
117  else
118  {
119  g_free(full_name);
120  }
121  LEAVE("");
122  return gset;
123 }
124 
125 static void
126 handlers_hash_block_helper (gpointer key, gpointer settings_ptr, gpointer pointer)
127 {
128  g_signal_handler_block (settings_ptr, (gulong)key); // block signal_handler
129  PINFO("Block handler_id %ld for settings_ptr %p", (gulong)key, settings_ptr);
130 }
131 
132 static void
133 handlers_hash_unblock_helper (gpointer key, gpointer settings_ptr, gpointer pointer)
134 {
135  g_signal_handler_unblock (settings_ptr, (gulong)key); // unblock signal_handler
136  PINFO("UnBlock handler_id %ld for settings_ptr %p", (gulong)key, settings_ptr);
137 }
138 
139 /************************************************************/
140 /* GSettings Utilities */
141 /************************************************************/
142 
143 const gchar *
145 {
146  return GSET_SCHEMA_PREFIX;
147 }
148 
149 gchar *
151 {
152  if (!name)
153  {
154  /* Need to return a newly allocated string */
155  return g_strdup(GSET_SCHEMA_PREFIX);
156  }
157  if (g_str_has_prefix (name, GSET_SCHEMA_PREFIX) ||
158  (g_str_has_prefix (name, GSET_SCHEMA_OLD_PREFIX)))
159  {
160  /* Need to return a newly allocated string */
161  return g_strdup(name);
162  }
163 
164  return g_strjoin(".", GSET_SCHEMA_PREFIX, name, NULL);
165 }
166 
167 
168 /************************************************************/
169 /* Change notification */
170 /************************************************************/
171 
172 gulong
173 gnc_gsettings_register_cb (const gchar *schema,
174  const gchar *key,
175  gpointer func,
176  gpointer user_data)
177 {
178  gulong retval = 0;
179  gchar *signal = NULL;
180 
181  GSettings *settings_ptr = gnc_gsettings_get_settings_ptr (schema);
182 
183  ENTER("");
184  g_return_val_if_fail (G_IS_SETTINGS (settings_ptr), retval);
185  g_return_val_if_fail (func, retval);
186 
187  if ((!key) || (*key == '\0'))
188  signal = g_strdup ("changed");
189  else
190  {
191  if (gnc_gsettings_is_valid_key(settings_ptr, key))
192  signal = g_strconcat ("changed::", key, NULL);
193  }
194 
195  retval = g_signal_connect (settings_ptr, signal, G_CALLBACK (func), user_data);
196 
197  if (!registered_handlers_hash)
198  registered_handlers_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
199 
200  if (retval != 0)
201  {
202  g_hash_table_insert (registered_handlers_hash,
203  GINT_TO_POINTER(retval), settings_ptr); //key, value
204 
205  PINFO("schema: %s, key: %s, settings_ptr: %p, handler_id: %ld",
206  schema, key, settings_ptr, retval);
207  }
208  g_free (signal);
209 
210  LEAVE("");
211  return retval;
212 }
213 
214 
215 void
216 gnc_gsettings_remove_cb_by_func (const gchar *schema,
217  const gchar *key,
218  gpointer func,
219  gpointer user_data)
220 {
221  gint matched = 0;
222  GQuark quark = 0;
223  gulong handler_id = 0;
224 
225  GSettings *settings_ptr = gnc_gsettings_get_settings_ptr (schema);
226  g_return_if_fail (G_IS_SETTINGS (settings_ptr));
227  g_return_if_fail (func);
228 
229  ENTER ();
230 
231  if ((key) && (gnc_gsettings_is_valid_key(settings_ptr, key)))
232  quark = g_quark_from_string (key);
233 
234  handler_id = g_signal_handler_find (
235  settings_ptr,
236  static_cast<GSignalMatchType> (G_SIGNAL_MATCH_DETAIL | G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA),
237  g_signal_lookup ("changed", G_TYPE_SETTINGS), /* signal_id */
238  quark, /* signal_detail */
239  NULL, /* closure */
240  func, /* callback function */
241  user_data);
242 
243  while (handler_id)
244  {
245  matched ++;
246  gnc_gsettings_remove_cb_by_id (schema, handler_id);
247 
248  handler_id = g_signal_handler_find (
249  settings_ptr,
250  static_cast<GSignalMatchType> (G_SIGNAL_MATCH_DETAIL | G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA),
251  g_signal_lookup ("changed", G_TYPE_SETTINGS), /* signal_id */
252  quark, /* signal_detail */
253  NULL, /* closure */
254  func, /* callback function */
255  user_data);
256  }
257 
258  LEAVE ("Schema: %s, key: %s, hashtable size: %d - removed %d handlers for 'changed' signal",
259  schema, key, g_hash_table_size (registered_handlers_hash), matched);
260 }
261 
262 
263 void
264 gnc_gsettings_remove_cb_by_id (const gchar *schema,
265  guint handlerid)
266 {
267  GSettings *settings_ptr = gnc_gsettings_get_settings_ptr (schema);
268  g_return_if_fail (G_IS_SETTINGS (settings_ptr));
269 
270  ENTER ();
271 
272  g_signal_handler_disconnect (settings_ptr, handlerid);
273 
274  // remove the handlerid from the registerered_handlers_hash
275  g_hash_table_remove (registered_handlers_hash, GINT_TO_POINTER(handlerid));
276 
277  // destroy hash table if size is 0
278  if (g_hash_table_size (registered_handlers_hash) == 0)
279  {
280  g_hash_table_destroy (registered_handlers_hash);
281  PINFO ("All registered preference callbacks removed");
282  }
283 
284  LEAVE ("Schema: %s, handlerid: %d, hashtable size: %d - removed for handler",
285  schema, handlerid, g_hash_table_size (registered_handlers_hash));
286 }
287 
288 
289 guint
290 gnc_gsettings_register_any_cb (const gchar *schema,
291  gpointer func,
292  gpointer user_data)
293 {
294  return gnc_gsettings_register_cb (schema, NULL, func, user_data);
295 }
296 
297 
298 void
300  gpointer func,
301  gpointer user_data)
302 {
303  gnc_gsettings_remove_cb_by_func (schema, NULL, func, user_data);
304 }
305 
306 
307 void gnc_gsettings_bind (const gchar *schema,
308  /*@ null @*/ const gchar *key,
309  gpointer object,
310  const gchar *property)
311 {
312  GSettings *settings_ptr = gnc_gsettings_get_settings_ptr (schema);
313  g_return_if_fail (G_IS_SETTINGS (settings_ptr));
314 
315  if (gnc_gsettings_is_valid_key (settings_ptr, key))
316  g_settings_bind (settings_ptr, key, object, property, G_SETTINGS_BIND_DEFAULT);
317  else
318  {
319  PERR ("Invalid key %s for schema %s", key, schema);
320  }
321 }
322 
323 /************************************************************/
324 /* Getters/Setters */
325 /************************************************************/
326 
327 gboolean
328 gnc_gsettings_get_bool (const gchar *schema,
329  const gchar *key)
330 {
331  GSettings *settings_ptr = gnc_gsettings_get_settings_ptr (schema);
332  g_return_val_if_fail (G_IS_SETTINGS (settings_ptr), FALSE);
333 
334  if (gnc_gsettings_is_valid_key (settings_ptr, key))
335  return g_settings_get_boolean (settings_ptr, key);
336  else
337  {
338  PERR ("Invalid key %s for schema %s", key, schema);
339  return FALSE;
340  }
341 }
342 
343 gboolean
344 gnc_gsettings_set_bool (const gchar *schema,
345  const gchar *key,
346  gboolean value)
347 {
348  gboolean result = FALSE;
349  GSettings *settings_ptr = gnc_gsettings_get_settings_ptr (schema);
350  g_return_val_if_fail (G_IS_SETTINGS (settings_ptr), FALSE);
351 
352  ENTER("schema: %s, key: %s", schema, key);
353  if (gnc_gsettings_is_valid_key (settings_ptr, key))
354  {
355  result = g_settings_set_boolean (settings_ptr, key, value);
356  if (!result)
357  PERR ("Unable to set value for key %s in schema %s", key, schema);
358  }
359  else
360  PERR ("Invalid key %s for schema %s", key, schema);
361 
362  LEAVE("result %i", result);
363  return result;
364 }
365 
366 gint
367 gnc_gsettings_get_int (const gchar *schema,
368  const gchar *key)
369 {
370  GSettings *settings_ptr = gnc_gsettings_get_settings_ptr (schema);
371  g_return_val_if_fail (G_IS_SETTINGS (settings_ptr), 0);
372 
373  if (gnc_gsettings_is_valid_key (settings_ptr, key))
374  return g_settings_get_int (settings_ptr, key);
375  else
376  {
377  PERR ("Invalid key %s for schema %s", key, schema);
378  return 0;
379  }
380 }
381 
382 gboolean
383 gnc_gsettings_set_int (const gchar *schema,
384  const gchar *key,
385  gint value)
386 {
387  gboolean result = FALSE;
388  GSettings *settings_ptr = gnc_gsettings_get_settings_ptr (schema);
389  g_return_val_if_fail (G_IS_SETTINGS (settings_ptr), FALSE);
390 
391  if (gnc_gsettings_is_valid_key (settings_ptr, key))
392  {
393  result = g_settings_set_int (settings_ptr, key, value);
394  if (!result)
395  PERR ("Unable to set value for key %s in schema %s", key, schema);
396  }
397  else
398  PERR ("Invalid key %s for schema %s", key, schema);
399 
400  return result;
401 }
402 
403 gdouble
404 gnc_gsettings_get_float (const gchar *schema,
405  const gchar *key)
406 {
407  GSettings *settings_ptr = gnc_gsettings_get_settings_ptr (schema);
408  g_return_val_if_fail (G_IS_SETTINGS (settings_ptr), 0);
409 
410  if (gnc_gsettings_is_valid_key (settings_ptr, key))
411  return g_settings_get_double (settings_ptr, key);
412  else
413  {
414  PERR ("Invalid key %s for schema %s", key, schema);
415  return 0;
416  }
417 }
418 
419 gboolean
420 gnc_gsettings_set_float (const gchar *schema,
421  const gchar *key,
422  gdouble value)
423 {
424  gboolean result = FALSE;
425  GSettings *settings_ptr = gnc_gsettings_get_settings_ptr (schema);
426  g_return_val_if_fail (G_IS_SETTINGS (settings_ptr), FALSE);
427 
428  if (gnc_gsettings_is_valid_key (settings_ptr, key))
429  {
430  result = g_settings_set_double (settings_ptr, key, value);
431  if (!result)
432  PERR ("Unable to set value for key %s in schema %s", key, schema);
433  }
434  else
435  PERR ("Invalid key %s for schema %s", key, schema);
436 
437  return result;
438 }
439 
440 gchar *
441 gnc_gsettings_get_string (const gchar *schema,
442  const gchar *key)
443 {
444  GSettings *settings_ptr = gnc_gsettings_get_settings_ptr (schema);
445  g_return_val_if_fail (G_IS_SETTINGS (settings_ptr), NULL);
446 
447  if (gnc_gsettings_is_valid_key (settings_ptr, key))
448  return g_settings_get_string (settings_ptr, key);
449  else
450  {
451  PERR ("Invalid key %s for schema %s", key, schema);
452  return NULL;
453  }
454 }
455 
456 gboolean
457 gnc_gsettings_set_string (const gchar *schema,
458  const gchar *key,
459  const gchar *value)
460 {
461  gboolean result = FALSE;
462  GSettings *settings_ptr = gnc_gsettings_get_settings_ptr (schema);
463  g_return_val_if_fail (G_IS_SETTINGS (settings_ptr), FALSE);
464 
465  ENTER("schema: %s, key: %s", schema, key);
466  if (gnc_gsettings_is_valid_key (settings_ptr, key))
467  {
468  result = g_settings_set_string (settings_ptr, key, value);
469  if (!result)
470  PERR ("Unable to set value for key %s in schema %s", key, schema);
471  }
472  else
473  PERR ("Invalid key %s for schema %s", key, schema);
474 
475  LEAVE("result %i", result);
476  return result;
477 }
478 
479 gint
480 gnc_gsettings_get_enum (const gchar *schema,
481  const gchar *key)
482 {
483  GSettings *settings_ptr = gnc_gsettings_get_settings_ptr (schema);
484  g_return_val_if_fail (G_IS_SETTINGS (settings_ptr), 0);
485 
486  if (gnc_gsettings_is_valid_key (settings_ptr, key))
487  return g_settings_get_enum (settings_ptr, key);
488  else
489  {
490  PERR ("Invalid key %s for schema %s", key, schema);
491  return 0;
492  }
493 }
494 
495 gboolean
496 gnc_gsettings_set_enum (const gchar *schema,
497  const gchar *key,
498  gint value)
499 {
500  gboolean result = FALSE;
501  GSettings *settings_ptr = gnc_gsettings_get_settings_ptr (schema);
502  g_return_val_if_fail (G_IS_SETTINGS (settings_ptr), FALSE);
503 
504  if (gnc_gsettings_is_valid_key (settings_ptr, key))
505  {
506  result = g_settings_set_enum (settings_ptr, key, value);
507  if (!result)
508  PERR ("Unable to set value for key %s in schema %s", key, schema);
509  }
510  else
511  PERR ("Invalid key %s for schema %s", key, schema);
512 
513  return result;
514 }
515 
516 GVariant *
517 gnc_gsettings_get_value (const gchar *schema,
518  const gchar *key)
519 {
520  GSettings *settings_ptr = gnc_gsettings_get_settings_ptr (schema);
521  g_return_val_if_fail (G_IS_SETTINGS (settings_ptr), NULL);
522 
523  if (gnc_gsettings_is_valid_key (settings_ptr, key))
524  return g_settings_get_value (settings_ptr, key);
525  else
526  {
527  PERR ("Invalid key %s for schema %s", key, schema);
528  return NULL;
529  }
530 }
531 
532 gboolean
533 gnc_gsettings_set_value (const gchar *schema,
534  const gchar *key,
535  GVariant *value)
536 {
537  gboolean result = FALSE;
538  GSettings *settings_ptr = gnc_gsettings_get_settings_ptr (schema);
539  g_return_val_if_fail (G_IS_SETTINGS (settings_ptr), FALSE);
540 
541  if (gnc_gsettings_is_valid_key (settings_ptr, key))
542  {
543  result = g_settings_set_value (settings_ptr, key, value);
544  if (!result)
545  PERR ("Unable to set value for key %s in schema %s", key, schema);
546  }
547  else
548  PERR ("Invalid key %s for schema %s", key, schema);
549 
550  return result;
551 }
552 
553 void
554 gnc_gsettings_reset (const gchar *schema,
555  const gchar *key)
556 {
557  GSettings *settings_ptr = gnc_gsettings_get_settings_ptr (schema);
558  g_return_if_fail (G_IS_SETTINGS (settings_ptr));
559 
560  if (gnc_gsettings_is_valid_key (settings_ptr, key))
561  g_settings_reset (settings_ptr, key);
562  else
563  PERR ("Invalid key %s for schema %s", key, schema);
564 }
565 
566 void
567 gnc_gsettings_reset_schema (const gchar *schema_str)
568 {
569  gchar **keys;
570  gint counter = 0;
571  GSettingsSchema *schema;
572  GSettings *settings = gnc_gsettings_get_settings_ptr (schema_str);
573 
574  if (!settings)
575  return;
576 
577  g_object_get (settings, "settings-schema", &schema, NULL);
578  if (!schema)
579  return;
580 
581  keys = g_settings_schema_list_keys (schema);
582  if (!keys)
583  return;
584 
585  while (keys[counter])
586  {
587  gnc_gsettings_reset (schema_str, keys[counter]);
588  counter++;
589  }
590 
591  g_strfreev (keys);
592 }
593 
595 {
596  ENTER("");
597 
598  /* The gsettings backend only works in an installed environment.
599  * When called from the source environment (for testing purposes)
600  * simply return.
601  */
602  if (g_strcmp0 (g_getenv ("GNC_UNINSTALLED"), "1") == 0)
603  return;
604 
605  if (prefsbackend)
606  g_free (prefsbackend);
607 
608  prefsbackend = g_new0 (PrefsBackend, 1);
609 
610  prefsbackend->register_cb = gnc_gsettings_register_cb;
611  prefsbackend->remove_cb_by_func = gnc_gsettings_remove_cb_by_func;
612  prefsbackend->remove_cb_by_id = gnc_gsettings_remove_cb_by_id;
613  prefsbackend->register_group_cb = gnc_gsettings_register_any_cb;
614  prefsbackend->remove_group_cb_by_func = gnc_gsettings_remove_any_cb_by_func;
615  prefsbackend->bind = gnc_gsettings_bind;
616  prefsbackend->get_bool = gnc_gsettings_get_bool;
617  prefsbackend->get_int = gnc_gsettings_get_int;
618  prefsbackend->get_float = gnc_gsettings_get_float;
619  prefsbackend->get_string = gnc_gsettings_get_string;
620  prefsbackend->get_enum = gnc_gsettings_get_enum;
621  prefsbackend->get_value = gnc_gsettings_get_value;
622  prefsbackend->set_bool = gnc_gsettings_set_bool;
623  prefsbackend->set_int = gnc_gsettings_set_int;
624  prefsbackend->set_float = gnc_gsettings_set_float;
625  prefsbackend->set_string = gnc_gsettings_set_string;
626  prefsbackend->set_enum = gnc_gsettings_set_enum;
627  prefsbackend->set_value = gnc_gsettings_set_value;
628  prefsbackend->reset = gnc_gsettings_reset;
629  prefsbackend->reset_group = gnc_gsettings_reset_schema;
630  prefsbackend->block_all = gnc_gsettings_block_all;
631  prefsbackend->unblock_all = gnc_gsettings_unblock_all;
632 
633  /* Run any data model changes for the backend before it's used
634  * by anyone */
636 
637  LEAVE("Prefsbackend bind = %p", prefsbackend->bind);
638 }
639 
640 
641 
642 static GVariant *
643 gnc_gsettings_get_user_value (const gchar *schema,
644  const gchar *key)
645 {
646  GSettings *settings_ptr = gnc_gsettings_get_settings_ptr (schema);
647  g_return_val_if_fail (G_IS_SETTINGS (settings_ptr), NULL);
648 
649  if (gnc_gsettings_is_valid_key (settings_ptr, key))
650  return g_settings_get_user_value (settings_ptr, key);
651  else
652  {
653  PERR ("Invalid key %s for schema %s", key, schema);
654  return NULL;
655  }
656 }
657 
658 using opt_str_vec = boost::optional<std::string>;
659 
660 static void
661 deprecate_one_key (const opt_str_vec &oldpath, const opt_str_vec &oldkey)
662 {
663  if (!oldpath || !oldkey )
664  {
665  DEBUG ("Skipping <deprecate> node - missing attribute (old-path or old-key)");
666  return;
667  }
668 
669  PINFO ("'%s:%s' has been marked deprecated", oldpath->c_str(), oldkey->c_str());
670  /* This does nothing really, but is a reminder for future maintainers
671  * to mark this pref as obsolete in the next major release. */
672 }
673 
674 static void
675 migrate_one_key (const opt_str_vec &oldpath, const opt_str_vec &oldkey,
676  const opt_str_vec &newpath, const opt_str_vec &newkey)
677 {
678  if (!oldpath || !oldkey || !newpath || !newkey)
679  {
680  DEBUG ("Skipping <migrate> node - missing attribute (old-path, old-key, new-path or new-key)");
681  return;
682  }
683 
684  PINFO ("Migrating '%s:%s' to '%s:%s'", oldpath->c_str(), oldkey->c_str(),
685  newpath->c_str(), newkey->c_str());
686 
687  auto user_value = gnc_gsettings_get_user_value (oldpath->c_str(), oldkey->c_str());
688  if (user_value)
689  gnc_gsettings_set_value (newpath->c_str(), newkey->c_str(), user_value);
690 }
691 
692 static void
693 obsolete_one_key (const opt_str_vec &oldpath, const opt_str_vec &oldkey)
694 {
695  if (!oldpath || !oldkey )
696  {
697  DEBUG ("Skipping <obsolete> node - missing attribute (old-path or old-key)");
698  return;
699  }
700 
701  PINFO ("Resetting obsolete '%s:%s'", oldpath->c_str(), oldkey->c_str());
702  gnc_gsettings_reset (oldpath->c_str(), oldkey->c_str());
703 }
704 
705 static void
706 parse_one_release_node (bpt::ptree &pt)
707 {
708  /* loop over top-level property tree */
709  std::for_each (pt.begin(), pt.end(),
710  [] (std::pair<bpt::ptree::key_type, bpt::ptree> node)
711  {
712  if (node.first == "<xmlattr>")
713  return;
714  else if (node.first == "deprecate")
715  deprecate_one_key (node.second.get_optional<std::string> ("<xmlattr>.old-path"),
716  node.second.get_optional<std::string> ("<xmlattr>.old-key"));
717  else if (node.first == "migrate")
718  migrate_one_key (node.second.get_optional<std::string> ("<xmlattr>.old-path"),
719  node.second.get_optional<std::string> ("<xmlattr>.old-key"),
720  node.second.get_optional<std::string> ("<xmlattr>.new-path"),
721  node.second.get_optional<std::string> ("<xmlattr>.new-key"));
722  else if (node.first == "obsolete")
723  obsolete_one_key (node.second.get_optional<std::string> ("<xmlattr>.old-path"),
724  node.second.get_optional<std::string> ("<xmlattr>.old-key"));
725  else
726  {
727  DEBUG ("Skipping unknown node <%s>", node.first.c_str());
728  return;
729  }
730  });
731 }
732 
733 static void
734 transform_settings (int old_maj_min)
735 {
736  bpt::ptree pt;
737 
738  auto pkg_data_dir = gnc_path_get_pkgdatadir();
739  auto transform_file = std::string (pkg_data_dir) + "/pref_transformations.xml";
740  g_free (pkg_data_dir);
741 
742  std::ifstream transform_stream {transform_file};
743  if (!transform_stream.is_open())
744  {
745  PWARN("Failed to load preferences transformation file '%s'", transform_file.c_str());
746  return;
747  }
748 
749  try
750  {
751  bpt::read_xml (transform_stream, pt);
752  }
753  catch (bpt::xml_parser_error &e) {
754  PWARN ("Failed to parse GnuCash preferences transformation file.\n");
755  PWARN ("Error message:\n");
756  PWARN ("%s\n", e.what());
757  return;
758  }
759  catch (...) {
760  PWARN ("Unknown error while parsing GnuCash preferences transformation file.\n");
761  return;
762  }
763 
764  /* loop over top-level property tree */
765  std::for_each (pt.begin(), pt.end(),
766  [&old_maj_min] (std::pair<bpt::ptree::key_type, bpt::ptree> node)
767  {
768  if (node.first != "release")
769  {
770  DEBUG ("Skipping non-<release> node <%s>", node.first.c_str());
771  return;
772  }
773  auto version = node.second.get_optional<int> ("<xmlattr>.version");
774  if (!version)
775  {
776  DEBUG ("Skipping <release> node - no version attribute found");
777  return;
778  }
779  if (*version <= old_maj_min)
780  {
781  DEBUG ("Skipping <release> node - version %i is less than current compatibility level %i", *version, old_maj_min);
782  return;
783  }
784  DEBUG ("Retrieved version value '%i'", *version);
785 
786  parse_one_release_node (node.second);
787  });
788 }
789 
791 {
792  /* This routine will conditionally execute conversion rules from
793  * prefs_transformations.xml to adapt the user's existing preferences to
794  * the current preferences schema. The rules in this file are versioned and
795  * only rules still relevant to the user's existing preferences and for
796  * this version of GnuCash will be executed.
797  *
798  * Starting with GnuCash 4.7 the code expects all preferences to be stored
799  * under prefix org.gnucash instead of org.gnucash.GnuCash, including our
800  * GNC_PREF_VERSION setting.
801  * As the logic to determine whether or not settings conversions are needed
802  * depends on this preference, we have to test for its value in two
803  * locations:
804  * - if GNC_PREF_VERSION is not set under old nor new prefix
805  * => GnuCash has never run before so no conversion run necessary
806  * - if GNC_PREF_VERSION is set under old prefix and not new prefix
807  * => user's preferences weren't moved yet from old to new prefix. Use old
808  * prefix GNC_PREF_VERSION to determine which conversions may be needed
809  * - if GNC_PREF_VERSION is set under both prefixes
810  * => ignore old prefix and use new prefix GNC_PREF_VERSION to determine
811  * which conversions may be needed.
812  * Sometime in the future (GnuCash 6.0) the old prefix will be fully removed
813  * and the test will be simplified to only check in the new prefix.
814  */
815  ENTER("Start of settings transform routine.");
816 
817  auto ogG_maj_min = gnc_gsettings_get_user_value (GNC_PREFS_GROUP_GENERAL, GNC_PREF_VERSION);
818  auto og_maj_min = gnc_gsettings_get_user_value (GSET_SCHEMA_OLD_PREFIX "." GNC_PREFS_GROUP_GENERAL, GNC_PREF_VERSION);
819 
820  if (!ogG_maj_min && !og_maj_min)
821  {
822  LEAVE("");
823  return;
824  }
825 
826  auto old_maj_min = 0;
827  if (!ogG_maj_min)
828  old_maj_min = gnc_gsettings_get_int (GSET_SCHEMA_OLD_PREFIX "." GNC_PREFS_GROUP_GENERAL, GNC_PREF_VERSION);
829  else
830  {
831  g_variant_unref (ogG_maj_min);
832  old_maj_min = gnc_gsettings_get_int (GNC_PREFS_GROUP_GENERAL, GNC_PREF_VERSION);
833  }
834  if (og_maj_min)
835  g_variant_unref (og_maj_min);
836 
837  PINFO ("Previous setting compatibility level: %i", old_maj_min);
838 
839  transform_settings (old_maj_min);
840 
841  /* Only write current version if it's more recent than what was set */
842  auto cur_maj_min = PROJECT_VERSION_MAJOR * 1000 + PROJECT_VERSION_MINOR;
843  if (cur_maj_min > old_maj_min)
844  gnc_gsettings_set_int (GNC_PREFS_GROUP_GENERAL, GNC_PREF_VERSION, cur_maj_min);
845 
846  LEAVE("");
847 }
848 
849 
851 {
852  PINFO("block registered_handlers_hash list size is %d",
853  g_hash_table_size (registered_handlers_hash));
854  g_hash_table_foreach (registered_handlers_hash,
855  handlers_hash_block_helper, NULL);
856 }
857 
858 
860 {
861  PINFO("unblock registered_handlers_hash list size is %d",
862  g_hash_table_size (registered_handlers_hash));
863  g_hash_table_foreach (registered_handlers_hash,
864  handlers_hash_unblock_helper, NULL);
865 }
gchar * gnc_gsettings_normalize_schema_name(const gchar *name)
Convert a partial schema name into a complete gsettings schema name.
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
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.
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...