diff --git a/libgnucash/app-utils/gnc-gsettings.cpp b/libgnucash/app-utils/gnc-gsettings.cpp index 17b25114be..9e16f07ec1 100644 --- a/libgnucash/app-utils/gnc-gsettings.cpp +++ b/libgnucash/app-utils/gnc-gsettings.cpp @@ -41,6 +41,7 @@ extern "C" { #include #include #include +#include namespace bpt = boost::property_tree; @@ -57,6 +58,10 @@ static GHashTable *registered_handlers_hash = NULL; /* This static indicates the debugging module that this .o belongs to. */ static QofLogModule log_module = "gnc.app-utils.gsettings"; +using pref_id = std::pair; + +static std::map oldkeys_map; + /************************************************************/ /* Internal helper functions */ /************************************************************/ @@ -313,7 +318,9 @@ void gnc_gsettings_bind (const gchar *schema, g_return_if_fail (G_IS_SETTINGS (settings_ptr)); if (gnc_gsettings_is_valid_key (settings_ptr, key)) + { g_settings_bind (settings_ptr, key, object, property, G_SETTINGS_BIND_DEFAULT); + } else { PERR ("Invalid key %s for schema %s", key, schema); @@ -687,6 +694,10 @@ migrate_one_key (const opt_str_vec &oldpath, const opt_str_vec &oldkey, auto user_value = gnc_gsettings_get_user_value (oldpath->c_str(), oldkey->c_str()); if (user_value) gnc_gsettings_set_value (newpath->c_str(), newkey->c_str(), user_value); + + /* Add old preference to oldkeys_map so we can keep it in sync with its replacement */ + oldkeys_map.emplace (std::make_pair(*oldpath, *oldkey), + std::make_pair(*newpath, *newkey)); } static void @@ -700,6 +711,9 @@ obsolete_one_key (const opt_str_vec &oldpath, const opt_str_vec &oldkey) PINFO ("Resetting obsolete '%s:%s'", oldpath->c_str(), oldkey->c_str()); gnc_gsettings_reset (oldpath->c_str(), oldkey->c_str()); + + /* Removve old preference to oldkeys_map. It's been reset we don't want to keep it in synch any more */ + oldkeys_map.erase (std::make_pair(*oldpath, *oldkey)); } static void @@ -723,10 +737,39 @@ parse_one_release_node (bpt::ptree &pt) obsolete_one_key (node.second.get_optional (".old-path"), node.second.get_optional (".old-key")); else - { DEBUG ("Skipping unknown node <%s>", node.first.c_str()); + }); +} + +static void +update_oldkeys_only (bpt::ptree &pt) +{ + /* handles oldkey tracking for release nodes that don't require full processing + * any more (when the preference db compatibility level is higher than what's in + * this release node) + * But even for those nodes we need to extract old preference ids to potentially + * keep them in sync with their replacements + */ + + std::for_each (pt.begin(), pt.end(), + [] (std::pair node) + { + auto oldpath = node.second.get_optional (".old-path"); + auto oldkey = node.second.get_optional (".old-key"); + auto newpath = node.second.get_optional (".new-path"); + auto newkey = node.second.get_optional (".new-key"); + + if ((node.first == "") || (node.first == "deprecate")) return; - } + else if (node.first == "migrate") + /* Add old preference to oldkeys_map so we can keep it in sync with its replacement */ + oldkeys_map.emplace (std::make_pair(*oldpath, *oldkey), + std::make_pair(*newpath, *newkey)); + else if (node.first == "obsolete") + /* Removve old preference to oldkeys_map. It's been reset we don't want to keep it in synch any more */ + oldkeys_map.erase (std::make_pair(*oldpath, *oldkey)); + else + DEBUG ("Skipping unknown node <%s>", node.first.c_str()); }); } @@ -751,13 +794,13 @@ transform_settings (int old_maj_min) bpt::read_xml (transform_stream, pt); } catch (bpt::xml_parser_error &e) { - PWARN ("Failed to parse GnuCash preferences transformation file.\n"); - PWARN ("Error message:\n"); - PWARN ("%s\n", e.what()); + PWARN ("Failed to parse GnuCash preferences transformation file."); + PWARN ("Error message:"); + PWARN ("%s", e.what()); return; } catch (...) { - PWARN ("Unknown error while parsing GnuCash preferences transformation file.\n"); + PWARN ("Unknown error while parsing GnuCash preferences transformation file."); return; } @@ -776,15 +819,36 @@ transform_settings (int old_maj_min) DEBUG ("Skipping node - no version attribute found"); return; } + if (*version <= old_maj_min) { - DEBUG ("Skipping node - version %i is less than current compatibility level %i", *version, old_maj_min); - return; + DEBUG ("Already processed node with version %i (current compatibility level %i). Extracting old preferences only.", + *version, old_maj_min); + update_oldkeys_only (node.second); + } + else + { + DEBUG ("Found node with version %i (current compatibility level %i). Processing child nodes.", + *version, old_maj_min); + parse_one_release_node (node.second); } - DEBUG ("Retrieved version value '%i'", *version); - - parse_one_release_node (node.second); }); + + /* oldkeys_map is generated oldkey->newkey for efficiency reasons but for + * subesquent use we need newkey->oldkey. So let's swap keys and values now. */ + std::map tmp_map; + std::for_each (oldkeys_map.begin(), oldkeys_map.end(), + [&tmp_map] (auto map_it) + { + tmp_map.emplace (map_it.second, map_it.first); + DEBUG ("Added new pref-> old_pref mapping for %s:%s -> %s:%s", + map_it.second.first.c_str(), + map_it.second.second.c_str(), + map_it.first.first.c_str(), + map_it.first.second.c_str()); + }); + oldkeys_map = tmp_map; + } void gnc_gsettings_version_upgrade (void)