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