36 #include "gnc-prefs-p.h" 38 #include <boost/property_tree/ptree.hpp> 39 #include <boost/property_tree/xml_parser.hpp> 42 #include <unordered_map> 44 namespace bpt = boost::property_tree;
46 #define GSET_SCHEMA_PREFIX "org.gnucash.GnuCash" 47 #define GSET_SCHEMA_OLD_PREFIX "org.gnucash" 51 void operator()(GSettings* gsp)
59 using GSettingsPtr = std::unique_ptr<GSettings, GSettingsDeleter>;
61 static std::unordered_map<std::string,GSettingsPtr> schema_hash;
64 static QofLogModule log_module =
"gnc.app-utils.gsettings";
69 static bool gnc_gsettings_is_valid_key(GSettings *settings,
const gchar *key)
72 if (!G_IS_SETTINGS(settings))
75 GSettingsSchema *schema;
76 g_object_get (settings,
"settings-schema", &schema,
nullptr);
80 auto keys = g_settings_schema_list_keys (schema);
81 auto found = (keys && g_strv_contains(keys, key));
83 g_settings_schema_unref (schema);
89 normalize_schema_name (
const gchar *name)
92 return GSET_SCHEMA_PREFIX;
94 if (g_str_has_prefix (name, GSET_SCHEMA_PREFIX) ||
95 (g_str_has_prefix (name, GSET_SCHEMA_OLD_PREFIX)))
98 return std::string{GSET_SCHEMA_PREFIX} +
'.' + name;
101 static GSettings * gnc_gsettings_get_settings_obj (
const gchar *schema_str)
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);
110 auto schema {g_settings_schema_source_lookup(schema_source, full_name,
true)};
111 g_return_val_if_fail(schema,
nullptr);
113 auto gset = g_settings_new_full (schema,
nullptr,
nullptr);
114 DEBUG (
"Created gsettings object %p for schema %s", gset, full_name);
116 if (!G_IS_SETTINGS(gset))
117 PWARN (
"Ignoring attempt to access unknown gsettings schema %s", full_name);
120 g_settings_schema_unref (schema);
125 schema_to_gsettings (
const char *schema,
bool can_retrieve)
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();
135 auto gs_obj = gnc_gsettings_get_settings_obj (schema);
136 if (!G_IS_SETTINGS (gs_obj))
138 PWARN (
"Ignoring attempt to access unknown gsettings schema %s", full_name.c_str());
142 schema_hash[full_name] = GSettingsPtr (gs_obj, g_settings_deleter);
153 return GSET_SCHEMA_PREFIX;
166 g_return_val_if_fail (func, 0);
168 auto gs_obj = schema_to_gsettings (schema,
true);
169 g_return_val_if_fail (G_IS_SETTINGS (gs_obj), 0);
171 auto signal =
static_cast<char *
> (
nullptr);
173 signal = g_strdup (
"changed");
174 else if (gnc_gsettings_is_valid_key(gs_obj, key))
175 signal = g_strconcat (
"changed::", key,
nullptr);
177 auto handlerid = g_signal_connect (gs_obj, signal, G_CALLBACK (func), user_data);
180 g_object_ref (gs_obj);
182 PINFO(
"schema: %s, key: %s, gs_obj: %p, handler_id: %ld",
183 schema, key, gs_obj, handlerid);
193 gnc_gsettings_remove_cb_by_id_internal (GSettings *gs_obj, guint handlerid)
196 g_return_if_fail (G_IS_SETTINGS (gs_obj));
198 g_signal_handler_disconnect (gs_obj, handlerid);
199 g_object_unref (gs_obj);
201 LEAVE (
"Schema: %p, handlerid: %d - removed for handler",
208 gpointer func, gpointer user_data)
211 g_return_if_fail (func);
213 auto gs_obj = schema_to_gsettings (schema,
false);
215 if (!G_IS_SETTINGS (gs_obj))
217 LEAVE (
"No valid GSettings object retrieved from hash table");
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);
223 auto quark = g_quark_from_string (key);
226 guint handler_id = 0;
229 handler_id = g_signal_handler_find (gs_obj, match_type,
230 changed_signal, quark,
nullptr,
235 gnc_gsettings_remove_cb_by_id_internal (gs_obj, handler_id);
238 if (!G_IS_SETTINGS (gs_obj))
241 }
while (handler_id);
243 LEAVE (
"Schema: %s, key: %s - removed %d handlers for 'changed' signal",
244 schema, key, matched);
253 auto gs_obj = schema_to_gsettings (schema,
false);
255 if (!G_IS_SETTINGS (gs_obj))
257 LEAVE (
"No valid GSettings object retrieved from hash table");
261 gnc_gsettings_remove_cb_by_id_internal (gs_obj, handlerid);
263 LEAVE (
"Schema: %p, handlerid: %d - removed for handler",
286 static gboolean gnc_gsettings_enum_bool_mapping_get (GValue *value,
290 g_value_set_boolean (value,
291 !g_strcmp0 ((
const gchar *)user_data,
292 g_variant_get_string (variant,
nullptr)));
297 static GVariant* gnc_gsettings_enum_bool_mapping_set (
const GValue *value,
298 const GVariantType *expected_type,
301 if (g_value_get_boolean (value))
303 return g_variant_new_string ((
const gchar *)user_data);
317 const gchar *property)
319 auto gs_obj = gnc_gsettings_get_settings_obj (schema);
320 g_return_if_fail (G_IS_SETTINGS (gs_obj));
322 if (gnc_gsettings_is_valid_key (gs_obj, key))
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);
334 g_settings_bind (gs_obj, key,
object, property, G_SETTINGS_BIND_DEFAULT);
339 PERR (
"Invalid key %s for schema %s", key, schema);
345 gs_obj_block_handlers ([[maybe_unused]] gpointer key, gpointer gs_obj,
346 [[maybe_unused]] gpointer pointer)
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);
353 gs_obj_unblock_handlers ([[maybe_unused]] gpointer key, gpointer gs_obj,
354 [[maybe_unused]] gpointer pointer)
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);
363 for (
const auto& it : schema_hash)
364 gs_obj_block_handlers (
nullptr, it.second.get(),
nullptr);
372 for (
const auto& it : schema_hash)
373 gs_obj_unblock_handlers (
nullptr, it.second.get(),
nullptr);
382 T gnc_gsettings_get(
const char *schema,
const char *key,
383 auto getter(GSettings*,
const char *)->T, T default_val)
385 auto gs_obj = gnc_gsettings_get_settings_obj (schema);
386 g_return_val_if_fail (G_IS_SETTINGS (gs_obj), default_val);
389 if (gnc_gsettings_is_valid_key (gs_obj, key))
390 val = getter (gs_obj, key);
392 PERR (
"Invalid key %s for schema %s", key, schema);
394 g_object_unref (gs_obj);
401 return gnc_gsettings_get (schema, key, g_settings_get_boolean,
402 static_cast<gboolean>(
false));
408 return gnc_gsettings_get (schema, key, g_settings_get_int, 0);
414 return gnc_gsettings_get (schema, key, g_settings_get_double, 0.0);
420 return gnc_gsettings_get (schema, key, g_settings_get_string,
421 static_cast<gchar *> (
nullptr));
427 return gnc_gsettings_get (schema, key, g_settings_get_enum, 0);
433 return gnc_gsettings_get (schema, key, g_settings_get_value,
434 static_cast<GVariant *> (
nullptr));
440 template<
typename T> gboolean
441 gnc_gsettings_set (
const gchar *schema,
444 gboolean setter(GSettings*,
const char *, T))
446 ENTER(
"schema: %s, key: %s", schema, key);
448 auto gs_obj = gnc_gsettings_get_settings_obj (schema);
449 g_return_val_if_fail (G_IS_SETTINGS (gs_obj),
false);
452 if (gnc_gsettings_is_valid_key (gs_obj, key))
454 result = setter (gs_obj, key, value);
456 PERR (
"Unable to set value for key %s in schema %s", key, schema);
459 PERR (
"Invalid key %s for schema %s", key, schema);
461 g_object_unref (gs_obj);
462 LEAVE(
"result %i", result);
469 return gnc_gsettings_set (schema, key, value, g_settings_set_boolean);
475 return gnc_gsettings_set (schema, key, value, g_settings_set_int);
481 return gnc_gsettings_set (schema, key, value, g_settings_set_double);
487 return gnc_gsettings_set (schema, key, value, g_settings_set_string);
493 return gnc_gsettings_set (schema, key, value, g_settings_set_enum);
499 return gnc_gsettings_set (schema, key, value, g_settings_set_value);
506 auto gs_obj = gnc_gsettings_get_settings_obj (schema);
507 g_return_if_fail (G_IS_SETTINGS (gs_obj));
509 if (gnc_gsettings_is_valid_key (gs_obj, key))
510 g_settings_reset (gs_obj, key);
512 PERR (
"Invalid key %s for schema %s", key, schema);
514 g_object_unref (gs_obj);
520 auto gs_obj = gnc_gsettings_get_settings_obj (schema_str);
525 GSettingsSchema *schema;
526 g_object_get (gs_obj,
"settings-schema", &schema,
nullptr);
529 g_object_unref (gs_obj);
533 auto keys = g_settings_schema_list_keys (schema);
537 for (
auto key = *fkeys; key; key = *++fkeys)
541 g_object_unref (gs_obj);
542 g_settings_schema_unref (schema);
547 gnc_settings_dump_schema_paths (
void)
549 gchar **non_relocatable;
551 auto schema_source {g_settings_schema_source_get_default()};
552 g_return_if_fail(schema_source);
554 g_settings_schema_source_list_schemas (schema_source,
true,
555 &non_relocatable,
nullptr);
557 for (gint i = 0; non_relocatable[i] !=
nullptr; i++)
558 PINFO(
"Schema entry %d is '%s'", i, non_relocatable[i]);
560 g_strfreev (non_relocatable);
571 if (g_strcmp0 (g_getenv (
"GNC_UNINSTALLED"),
"1") == 0)
574 g_free (prefsbackend);
601 gnc_settings_dump_schema_paths ();
607 LEAVE(
"Prefsbackend bind = %p", prefsbackend->bind);
614 g_free (prefsbackend);
619 gnc_gsettings_get_user_value (
const gchar *schema,
622 auto gs_obj = gnc_gsettings_get_settings_obj (schema);
623 g_return_val_if_fail (G_IS_SETTINGS (gs_obj),
nullptr);
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);
629 PERR (
"Invalid key %s for schema %s", key, schema);
631 g_object_unref (gs_obj);
635 using opt_str_vec = boost::optional<std::string>;
638 deprecate_one_key (
const opt_str_vec &oldpath,
const opt_str_vec &oldkey)
640 if (!oldpath || !oldkey )
642 DEBUG (
"Skipping <deprecate> node - missing attribute (old-path or old-key)");
646 PINFO (
"'%s:%s' has been marked deprecated", oldpath->c_str(), oldkey->c_str());
652 migrate_one_key (
const opt_str_vec &oldpath,
const opt_str_vec &oldkey,
653 const opt_str_vec &newpath,
const opt_str_vec &newkey)
655 if (!oldpath || !oldkey || !newpath || !newkey)
657 DEBUG (
"Skipping <migrate> node - missing attribute (old-path, old-key, new-path or new-key)");
661 PINFO (
"Migrating '%s:%s' to '%s:%s'", oldpath->c_str(), oldkey->c_str(),
662 newpath->c_str(), newkey->c_str());
664 auto user_value = gnc_gsettings_get_user_value (oldpath->c_str(), oldkey->c_str());
670 obsolete_one_key (
const opt_str_vec &oldpath,
const opt_str_vec &oldkey)
672 if (!oldpath || !oldkey )
674 DEBUG (
"Skipping <obsolete> node - missing attribute (old-path or old-key)");
678 PINFO (
"Resetting obsolete '%s:%s'", oldpath->c_str(), oldkey->c_str());
683 parse_one_release_node (bpt::ptree &pt)
686 std::for_each (pt.begin(), pt.end(),
687 [] (std::pair<bpt::ptree::key_type, bpt::ptree> node)
689 if (node.first ==
"<xmlattr>")
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"));
704 DEBUG (
"Skipping unknown node <%s>", node.first.c_str());
711 transform_settings (
int old_maj_min,
int cur_maj_min)
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);
719 std::ifstream transform_stream {transform_file};
720 if (!transform_stream.is_open())
722 PWARN(
"Failed to load preferences transformation file '%s'", transform_file.c_str());
728 bpt::read_xml (transform_stream, pt);
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());
737 PWARN (
"Unknown error while parsing GnuCash preferences transformation file.\n");
742 std::for_each (pt.begin(), pt.end(),
743 [&old_maj_min, &cur_maj_min] (std::pair<bpt::ptree::key_type, bpt::ptree> node)
745 if (node.first !=
"release")
747 DEBUG (
"Skipping non-<release> node <%s>", node.first.c_str());
750 auto version = node.second.get_optional<
int> (
"<xmlattr>.version");
753 DEBUG (
"Skipping <release> node - no version attribute found");
756 if (*version <= old_maj_min)
758 DEBUG (
"Skipping <release> node - version %i is less than current compatibility level %i", *version, old_maj_min);
761 if (*version > cur_maj_min)
763 DEBUG (
"Skipping <release> node - version %i is greater than current version level %i", *version, cur_maj_min);
766 DEBUG (
"Retrieved version value '%i'", *version);
768 parse_one_release_node (node.second);
797 ENTER(
"Start of settings transform routine.");
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);
802 auto cur_maj_min = PROJECT_VERSION_MAJOR * 1000 + PROJECT_VERSION_MINOR;
804 if (!ogG_maj_min && !og_maj_min)
807 LEAVE (
"Setting Previous compatibility level to current version: %i", cur_maj_min);
811 auto old_maj_min = 0;
813 old_maj_min =
gnc_gsettings_get_int (GSET_SCHEMA_OLD_PREFIX
"." GNC_PREFS_GROUP_GENERAL, GNC_PREF_VERSION);
816 g_variant_unref (ogG_maj_min);
820 g_variant_unref (og_maj_min);
822 PINFO (
"Previous setting compatibility level: %i, Current version: %i", old_maj_min, cur_maj_min);
824 transform_settings (old_maj_min, cur_maj_min);
827 if (cur_maj_min > old_maj_min)
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.
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.
#define PERR(format, args...)
Log a serious error.
#define ENTER(format, args...)
Print a function entry debugging message.
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.
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. ...
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.
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.
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.
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...