LCOV - code coverage report
Current view: top level - libgnucash/app-utils - gnc-gsettings.cpp (source / functions) Coverage Total Hit
Test: gnucash.info Lines: 1.1 % 367 4
Test Date: 2026-11-02 18:19:43 Functions: 1.9 % 53 1
Legend: Lines:     hit not hit

            Line data    Source code
       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              : 
      49              : struct GSettingsDeleter
      50              : {
      51            0 :     void operator()(GSettings* gsp)
      52              :     {
      53            0 :         g_object_unref(gsp);
      54            0 :     }
      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            0 : static bool gnc_gsettings_is_valid_key(GSettings *settings, const gchar *key)
      70              : {
      71              :     // Check if the key is valid key within settings
      72            0 :     if (!G_IS_SETTINGS(settings))
      73            0 :         return false;
      74              : 
      75              :     GSettingsSchema *schema;
      76            0 :     g_object_get (settings, "settings-schema", &schema, nullptr);
      77            0 :     if (!schema)
      78            0 :         return false;
      79              : 
      80            0 :     auto keys = g_settings_schema_list_keys (schema);
      81            0 :     auto found = (keys && g_strv_contains(keys, key));
      82            0 :     g_strfreev (keys);
      83            0 :     g_settings_schema_unref (schema);
      84              : 
      85            0 :     return found;
      86              : }
      87              : 
      88              : static std::string
      89            0 : normalize_schema_name (const gchar *name)
      90              : {
      91            0 :     if (!name)
      92            0 :         return GSET_SCHEMA_PREFIX;
      93              : 
      94            0 :     if (g_str_has_prefix (name, GSET_SCHEMA_PREFIX) ||
      95            0 :        (g_str_has_prefix (name, GSET_SCHEMA_OLD_PREFIX)))
      96            0 :         return name;
      97              : 
      98            0 :     return std::string{GSET_SCHEMA_PREFIX} + '.' + name;
      99              : }
     100              : 
     101            0 : static GSettings * gnc_gsettings_get_settings_obj (const gchar *schema_str)
     102              : {
     103            0 :     ENTER("");
     104              : 
     105            0 :     auto full_name_str = normalize_schema_name (schema_str);
     106            0 :     auto full_name = full_name_str.c_str();
     107            0 :     auto schema_source {g_settings_schema_source_get_default()};
     108            0 :     g_return_val_if_fail(schema_source, nullptr);
     109              : 
     110            0 :     auto schema {g_settings_schema_source_lookup(schema_source, full_name, true)};
     111            0 :     g_return_val_if_fail(schema, nullptr);
     112              : 
     113            0 :     auto gset = g_settings_new_full (schema, nullptr, nullptr);
     114            0 :     DEBUG ("Created gsettings object %p for schema %s", gset, full_name);
     115              : 
     116            0 :     if (!G_IS_SETTINGS(gset))
     117            0 :         PWARN ("Ignoring attempt to access unknown gsettings schema %s", full_name);
     118              : 
     119            0 :     LEAVE("");
     120            0 :     g_settings_schema_unref (schema);
     121            0 :     return gset;
     122            0 : }
     123              : 
     124              : static GSettings*
     125            0 : schema_to_gsettings (const char *schema, bool can_retrieve)
     126              : {
     127            0 :     auto full_name = normalize_schema_name (schema);
     128            0 :     auto iter = schema_hash.find (full_name);
     129            0 :     if (iter != schema_hash.end())
     130            0 :         return iter->second.get();
     131              : 
     132            0 :     if (!can_retrieve)
     133            0 :         return nullptr;
     134              : 
     135            0 :     auto gs_obj = gnc_gsettings_get_settings_obj (schema);
     136            0 :     if (!G_IS_SETTINGS (gs_obj))
     137              :     {
     138            0 :         PWARN ("Ignoring attempt to access unknown gsettings schema %s", full_name.c_str());
     139            0 :         return nullptr;
     140              :     }
     141              : 
     142            0 :     schema_hash[full_name] = GSettingsPtr (gs_obj, g_settings_deleter);
     143            0 :     return gs_obj;
     144            0 : }
     145              : 
     146              : /************************************************************/
     147              : /*                      GSettings Utilities                 */
     148              : /************************************************************/
     149              : 
     150              : const gchar *
     151            0 : gnc_gsettings_get_prefix (void)
     152              : {
     153            0 :     return GSET_SCHEMA_PREFIX;
     154              : }
     155              : 
     156              : /************************************************************/
     157              : /*                   Change notification                    */
     158              : /************************************************************/
     159              : 
     160              : gulong
     161            0 : gnc_gsettings_register_cb (const gchar *schema, const gchar *key,
     162              :                            gpointer func,
     163              :                            gpointer user_data)
     164              : {
     165            0 :     ENTER("");
     166            0 :     g_return_val_if_fail (func, 0);
     167              : 
     168            0 :     auto gs_obj = schema_to_gsettings (schema, true);
     169            0 :     g_return_val_if_fail (G_IS_SETTINGS (gs_obj), 0);
     170              : 
     171            0 :     auto signal = static_cast<char *> (nullptr);
     172            0 :     if (!(key && *key))
     173            0 :         signal = g_strdup ("changed");
     174            0 :     else if (gnc_gsettings_is_valid_key(gs_obj, key))
     175            0 :         signal = g_strconcat ("changed::", key, nullptr);
     176              : 
     177            0 :     auto handlerid = g_signal_connect (gs_obj, signal, G_CALLBACK (func), user_data);
     178            0 :     if (handlerid)
     179              :     {
     180            0 :         g_object_ref (gs_obj);
     181              : 
     182            0 :         PINFO("schema: %s, key: %s, gs_obj: %p, handler_id: %ld",
     183              :                schema, key, gs_obj, handlerid);
     184              :     }
     185            0 :     g_free (signal);
     186              : 
     187            0 :     LEAVE("");
     188            0 :     return handlerid;
     189              : }
     190              : 
     191              : 
     192              : static void
     193            0 : gnc_gsettings_remove_cb_by_id_internal (GSettings *gs_obj, guint handlerid)
     194              : {
     195            0 :     ENTER ();
     196            0 :     g_return_if_fail (G_IS_SETTINGS (gs_obj));
     197              : 
     198            0 :     g_signal_handler_disconnect (gs_obj, handlerid);
     199            0 :     g_object_unref (gs_obj);
     200              : 
     201            0 :     LEAVE ("Schema: %p, handlerid: %d - removed for handler",
     202              :            gs_obj, handlerid);
     203              : }
     204              : 
     205              : 
     206              : void
     207            0 : gnc_gsettings_remove_cb_by_func (const gchar *schema, const gchar *key,
     208              :                                  gpointer func, gpointer user_data)
     209              : {
     210            0 :     ENTER ();
     211            0 :     g_return_if_fail (func);
     212              : 
     213            0 :     auto gs_obj = schema_to_gsettings (schema, false);
     214              : 
     215            0 :     if (!G_IS_SETTINGS (gs_obj))
     216              :     {
     217            0 :         LEAVE ("No valid GSettings object retrieved from hash table");
     218            0 :         return;
     219              :     }
     220              : 
     221            0 :     auto match_type = static_cast<GSignalMatchType> (G_SIGNAL_MATCH_DETAIL |  G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA);
     222            0 :     auto changed_signal = g_signal_lookup ("changed", G_TYPE_SETTINGS); /* signal_id */
     223            0 :     auto quark = g_quark_from_string (key); /* signal_detail */
     224              : 
     225            0 :     auto matched = 0;
     226            0 :     guint handler_id = 0;
     227              :     do
     228              :     {
     229            0 :         handler_id = g_signal_handler_find (gs_obj, match_type,
     230              :                                             changed_signal, quark, nullptr,
     231              :                                             func, user_data);
     232            0 :         if (handler_id)
     233              :         {
     234            0 :             matched ++;
     235            0 :             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            0 :             if (!G_IS_SETTINGS (gs_obj))
     239            0 :                 handler_id = 0;
     240              :         }
     241            0 :     } while (handler_id);
     242              : 
     243            0 :     LEAVE ("Schema: %s, key: %s - removed %d handlers for 'changed' signal",
     244              :             schema, key, matched);
     245              : }
     246              : 
     247              : 
     248              : void
     249            0 : gnc_gsettings_remove_cb_by_id (const gchar *schema, guint handlerid)
     250              : {
     251            0 :     ENTER ();
     252              : 
     253            0 :     auto gs_obj = schema_to_gsettings (schema, false);
     254              : 
     255            0 :     if (!G_IS_SETTINGS (gs_obj))
     256              :     {
     257            0 :         LEAVE ("No valid GSettings object retrieved from hash table");
     258            0 :         return;
     259              :     }
     260              : 
     261            0 :     gnc_gsettings_remove_cb_by_id_internal (gs_obj, handlerid);
     262              : 
     263            0 :     LEAVE ("Schema: %p, handlerid: %d - removed for handler",
     264              :             gs_obj, handlerid);
     265              : }
     266              : 
     267              : 
     268              : guint
     269            0 : gnc_gsettings_register_any_cb (const gchar *schema,
     270              :                                gpointer func,
     271              :                                gpointer user_data)
     272              : {
     273            0 :     return gnc_gsettings_register_cb (schema, nullptr, func, user_data);
     274              : }
     275              : 
     276              : 
     277              : void
     278            0 : gnc_gsettings_remove_any_cb_by_func (const gchar *schema,
     279              :                                      gpointer func,
     280              :                                      gpointer user_data)
     281              : {
     282            0 :     gnc_gsettings_remove_cb_by_func (schema, nullptr, func, user_data);
     283            0 : }
     284              : 
     285              : 
     286            0 : static gboolean gnc_gsettings_enum_bool_mapping_get (GValue *value,
     287              :                                                      GVariant *variant,
     288              :                                                      gpointer user_data)
     289              : {
     290            0 :     g_value_set_boolean (value,
     291            0 :         !g_strcmp0 ((const gchar *)user_data,
     292              :             g_variant_get_string (variant, nullptr)));
     293              : 
     294            0 :     return true;
     295              : }
     296              : 
     297            0 : static GVariant* gnc_gsettings_enum_bool_mapping_set (const GValue *value,
     298              :                                                       const GVariantType *expected_type,
     299              :                                                       gpointer user_data)
     300              : {
     301            0 :     if (g_value_get_boolean (value))
     302              :     {
     303            0 :         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            0 :         return nullptr;
     310              :     }
     311              : }
     312              : 
     313            0 : void 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            0 :     auto gs_obj = gnc_gsettings_get_settings_obj (schema);
     320            0 :     g_return_if_fail (G_IS_SETTINGS (gs_obj));
     321              : 
     322            0 :     if (gnc_gsettings_is_valid_key (gs_obj, key))
     323              :     {
     324            0 :         if (value)
     325              :         {
     326            0 :             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            0 :                                           g_strdup (value), g_free);
     331              :         }
     332              :         else
     333              :         {
     334            0 :             g_settings_bind (gs_obj, key, object, property, G_SETTINGS_BIND_DEFAULT);
     335              :         }
     336              :     }
     337              :     else
     338              :     {
     339            0 :         PERR ("Invalid key %s for schema %s", key, schema);
     340              :     }
     341              : }
     342              : 
     343              : 
     344              : static void
     345            0 : gs_obj_block_handlers ([[maybe_unused]] gpointer key, gpointer gs_obj,
     346              :                        [[maybe_unused]] gpointer pointer)
     347              : {
     348            0 :     g_signal_handlers_block_matched (gs_obj, G_SIGNAL_MATCH_CLOSURE, 0, 0, nullptr, nullptr, nullptr);
     349            0 :     PINFO("Block all handlers for GSettings object %p", gs_obj);
     350            0 : }
     351              : 
     352              : static void
     353            0 : gs_obj_unblock_handlers ([[maybe_unused]] gpointer key, gpointer gs_obj,
     354              :                          [[maybe_unused]] gpointer pointer)
     355              : {
     356            0 :     g_signal_handlers_unblock_matched (gs_obj, G_SIGNAL_MATCH_CLOSURE, 0, 0, nullptr, nullptr, nullptr);
     357            0 :     PINFO("Unblock all handlers for GSettings object %p", gs_obj);
     358            0 : }
     359              : 
     360            0 : void gnc_gsettings_block_all (void)
     361              : {
     362            0 :     ENTER ();
     363            0 :     for (const auto& it : schema_hash)
     364            0 :         gs_obj_block_handlers (nullptr, it.second.get(), nullptr);
     365            0 :     LEAVE();
     366            0 : }
     367              : 
     368              : 
     369            0 : void gnc_gsettings_unblock_all (void)
     370              : {
     371            0 :     ENTER ();
     372            0 :     for (const auto& it : schema_hash)
     373            0 :         gs_obj_unblock_handlers (nullptr, it.second.get(), nullptr);
     374            0 :     LEAVE();
     375            0 : }
     376              : 
     377              : 
     378              : /************************************************************/
     379              : /* Getters                                                  */
     380              : /************************************************************/
     381              : template<typename T>
     382            0 : T gnc_gsettings_get(const char *schema, const char *key,
     383              :                     auto getter(GSettings*, const char *)->T, T default_val)
     384              : {
     385            0 :     auto gs_obj = gnc_gsettings_get_settings_obj (schema);
     386            0 :     g_return_val_if_fail (G_IS_SETTINGS (gs_obj), default_val);
     387              : 
     388            0 :     T val = default_val;
     389            0 :     if (gnc_gsettings_is_valid_key (gs_obj, key))
     390            0 :         val = getter (gs_obj, key);
     391              :     else
     392            0 :         PERR ("Invalid key %s for schema %s", key, schema);
     393              : 
     394            0 :     g_object_unref (gs_obj);
     395            0 :     return val;
     396              : }
     397              : 
     398              : gboolean
     399            0 : gnc_gsettings_get_bool (const gchar *schema, const gchar *key)
     400              : {
     401            0 :     return gnc_gsettings_get (schema, key, g_settings_get_boolean,
     402            0 :                               static_cast<gboolean>(false));
     403              : }
     404              : 
     405              : gint
     406            0 : gnc_gsettings_get_int (const gchar *schema, const gchar *key)
     407              : {
     408            0 :     return gnc_gsettings_get (schema, key, g_settings_get_int, 0);
     409              : }
     410              : 
     411              : gdouble
     412            0 : gnc_gsettings_get_float (const gchar *schema, const gchar *key)
     413              : {
     414            0 :     return gnc_gsettings_get (schema, key, g_settings_get_double, 0.0);
     415              : }
     416              : 
     417              : gchar *
     418            0 : gnc_gsettings_get_string (const gchar *schema, const gchar *key)
     419              : {
     420            0 :     return gnc_gsettings_get (schema, key, g_settings_get_string,
     421            0 :                               static_cast<gchar *> (nullptr));
     422              : }
     423              : 
     424              : gint
     425            0 : gnc_gsettings_get_enum (const gchar *schema, const gchar *key)
     426              : {
     427            0 :     return gnc_gsettings_get (schema, key, g_settings_get_enum, 0);
     428              : }
     429              : 
     430              : GVariant *
     431            0 : gnc_gsettings_get_value (const gchar *schema, const gchar *key)
     432              : {
     433            0 :     return gnc_gsettings_get (schema, key, g_settings_get_value,
     434            0 :                               static_cast<GVariant *> (nullptr));
     435              : }
     436              : 
     437              : /************************************************************/
     438              : /* Setters                                                  */
     439              : /************************************************************/
     440              : template<typename T> gboolean
     441            0 : gnc_gsettings_set (const gchar *schema,
     442              :                    const gchar *key,
     443              :                    T value,
     444              :                    gboolean setter(GSettings*, const char *, T))
     445              : {
     446            0 :     ENTER("schema: %s, key: %s", schema, key);
     447              : 
     448            0 :     auto gs_obj = gnc_gsettings_get_settings_obj (schema);
     449            0 :     g_return_val_if_fail (G_IS_SETTINGS (gs_obj), false);
     450              : 
     451            0 :     auto result = false;
     452            0 :     if (gnc_gsettings_is_valid_key (gs_obj, key))
     453              :     {
     454            0 :         result = setter (gs_obj, key, value);
     455            0 :         if (!result)
     456            0 :             PERR ("Unable to set value for key %s in schema %s", key, schema);
     457              :     }
     458              :     else
     459            0 :         PERR ("Invalid key %s for schema %s", key, schema);
     460              : 
     461            0 :     g_object_unref (gs_obj);
     462            0 :     LEAVE("result %i", result);
     463            0 :     return result;
     464              : }
     465              : 
     466              : gboolean
     467            0 : gnc_gsettings_set_bool (const gchar *schema, const gchar *key, gboolean value)
     468              : {
     469            0 :     return gnc_gsettings_set (schema, key, value, g_settings_set_boolean);
     470              : }
     471              : 
     472              : gboolean
     473            0 : gnc_gsettings_set_int (const gchar *schema, const gchar *key, gint value)
     474              : {
     475            0 :     return gnc_gsettings_set (schema, key, value, g_settings_set_int);
     476              : }
     477              : 
     478              : gboolean
     479            0 : gnc_gsettings_set_float (const gchar *schema, const gchar *key, gdouble value)
     480              : {
     481            0 :     return gnc_gsettings_set (schema, key, value, g_settings_set_double);
     482              : }
     483              : 
     484              : gboolean
     485            0 : gnc_gsettings_set_string (const gchar *schema, const gchar *key, const gchar *value)
     486              : {
     487            0 :     return gnc_gsettings_set (schema, key, value, g_settings_set_string);
     488              : }
     489              : 
     490              : gboolean
     491            0 : gnc_gsettings_set_enum (const gchar *schema, const gchar *key, gint value)
     492              : {
     493            0 :     return gnc_gsettings_set (schema, key, value, g_settings_set_enum);
     494              : }
     495              : 
     496              : gboolean
     497            0 : gnc_gsettings_set_value (const gchar *schema, const gchar *key, GVariant *value)
     498              : {
     499            0 :     return gnc_gsettings_set (schema, key, value, g_settings_set_value);
     500              : }
     501              : 
     502              : void
     503            0 : gnc_gsettings_reset (const gchar *schema,
     504              :                      const gchar *key)
     505              : {
     506            0 :     auto gs_obj = gnc_gsettings_get_settings_obj (schema);
     507            0 :     g_return_if_fail (G_IS_SETTINGS (gs_obj));
     508              : 
     509            0 :     if (gnc_gsettings_is_valid_key (gs_obj, key))
     510            0 :         g_settings_reset (gs_obj, key);
     511              :     else
     512            0 :         PERR ("Invalid key %s for schema %s", key, schema);
     513              : 
     514            0 :     g_object_unref (gs_obj);
     515              : }
     516              : 
     517              : void
     518            0 : gnc_gsettings_reset_schema (const gchar *schema_str)
     519              : {
     520            0 :     auto gs_obj = gnc_gsettings_get_settings_obj (schema_str);
     521              : 
     522            0 :     if (!gs_obj)
     523            0 :         return;
     524              : 
     525              :     GSettingsSchema *schema;
     526            0 :     g_object_get (gs_obj, "settings-schema", &schema, nullptr);
     527            0 :     if (!schema)
     528              :     {
     529            0 :         g_object_unref (gs_obj);
     530            0 :         return;
     531              :     }
     532              : 
     533            0 :     auto keys = g_settings_schema_list_keys (schema);
     534            0 :     if (keys)
     535              :     {
     536            0 :         auto fkeys = keys;
     537            0 :         for (auto key = *fkeys; key; key = *++fkeys)
     538            0 :             gnc_gsettings_reset (schema_str, key);
     539              :     }
     540              : 
     541            0 :     g_object_unref (gs_obj);
     542            0 :     g_settings_schema_unref (schema);
     543            0 :     g_strfreev (keys);
     544              : }
     545              : 
     546              : static void
     547            0 : gnc_settings_dump_schema_paths (void)
     548              : {
     549              :     gchar **non_relocatable;
     550              : 
     551            0 :     auto schema_source {g_settings_schema_source_get_default()};
     552            0 :     g_return_if_fail(schema_source);
     553              : 
     554            0 :     g_settings_schema_source_list_schemas (schema_source, true,
     555              :                                            &non_relocatable, nullptr);
     556              : 
     557            0 :     for (gint i = 0; non_relocatable[i] != nullptr; i++)
     558            0 :         PINFO("Schema entry %d is '%s'", i, non_relocatable[i]);
     559              : 
     560            0 :     g_strfreev (non_relocatable);
     561              : }
     562              : 
     563            1 : void gnc_gsettings_load_backend (void)
     564              : {
     565            1 :     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            1 :     if (g_strcmp0 (g_getenv ("GNC_UNINSTALLED"), "1") == 0)
     572            1 :         return;
     573              : 
     574            0 :     g_free (prefsbackend);
     575            0 :     prefsbackend = g_new0 (PrefsBackend, 1);
     576              : 
     577            0 :     prefsbackend->register_cb = gnc_gsettings_register_cb;
     578            0 :     prefsbackend->remove_cb_by_func = gnc_gsettings_remove_cb_by_func;
     579            0 :     prefsbackend->remove_cb_by_id = gnc_gsettings_remove_cb_by_id;
     580            0 :     prefsbackend->register_group_cb = gnc_gsettings_register_any_cb;
     581            0 :     prefsbackend->remove_group_cb_by_func = gnc_gsettings_remove_any_cb_by_func;
     582            0 :     prefsbackend->bind = gnc_gsettings_bind;
     583            0 :     prefsbackend->get_bool = gnc_gsettings_get_bool;
     584            0 :     prefsbackend->get_int = gnc_gsettings_get_int;
     585            0 :     prefsbackend->get_float = gnc_gsettings_get_float;
     586            0 :     prefsbackend->get_string = gnc_gsettings_get_string;
     587            0 :     prefsbackend->get_enum = gnc_gsettings_get_enum;
     588            0 :     prefsbackend->get_value = gnc_gsettings_get_value;
     589            0 :     prefsbackend->set_bool = gnc_gsettings_set_bool;
     590            0 :     prefsbackend->set_int = gnc_gsettings_set_int;
     591            0 :     prefsbackend->set_float = gnc_gsettings_set_float;
     592            0 :     prefsbackend->set_string = gnc_gsettings_set_string;
     593            0 :     prefsbackend->set_enum = gnc_gsettings_set_enum;
     594            0 :     prefsbackend->set_value = gnc_gsettings_set_value;
     595            0 :     prefsbackend->reset = gnc_gsettings_reset;
     596            0 :     prefsbackend->reset_group = gnc_gsettings_reset_schema;
     597            0 :     prefsbackend->block_all = gnc_gsettings_block_all;
     598            0 :     prefsbackend->unblock_all = gnc_gsettings_unblock_all;
     599              : 
     600            0 :     if (qof_log_check (log_module, QOF_LOG_DEBUG))
     601            0 :         gnc_settings_dump_schema_paths ();
     602              : 
     603              :     /* Run any data model changes for the backend before it's used
     604              :      * by anyone */
     605            0 :     gnc_gsettings_version_upgrade();
     606              : 
     607            0 :     LEAVE("Prefsbackend bind = %p", prefsbackend->bind);
     608              : }
     609              : 
     610              : void
     611            0 : gnc_gsettings_shutdown (void)
     612              : {
     613            0 :     schema_hash.clear();
     614            0 :     g_free (prefsbackend);
     615            0 : }
     616              : 
     617              : 
     618              : static GVariant *
     619            0 : gnc_gsettings_get_user_value (const gchar *schema,
     620              :                               const gchar *key)
     621              : {
     622            0 :     auto gs_obj = gnc_gsettings_get_settings_obj (schema);
     623            0 :     g_return_val_if_fail (G_IS_SETTINGS (gs_obj), nullptr);
     624              : 
     625            0 :     auto val = static_cast<GVariant *> (nullptr);
     626            0 :     if (gnc_gsettings_is_valid_key (gs_obj, key))
     627            0 :         val = g_settings_get_user_value (gs_obj, key);
     628              :     else
     629            0 :         PERR ("Invalid key %s for schema %s", key, schema);
     630              : 
     631            0 :     g_object_unref (gs_obj);
     632            0 :     return val;
     633              : }
     634              : 
     635              : using opt_str_vec = boost::optional<std::string>;
     636              : 
     637              : static void
     638            0 : deprecate_one_key (const opt_str_vec &oldpath, const opt_str_vec &oldkey)
     639              : {
     640            0 :     if (!oldpath || !oldkey )
     641              :     {
     642            0 :         DEBUG ("Skipping <deprecate> node - missing attribute (old-path or old-key)");
     643            0 :         return;
     644              :     }
     645              : 
     646            0 :     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              : 
     651              : static void
     652            0 : migrate_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            0 :     if (!oldpath || !oldkey || !newpath || !newkey)
     656              :     {
     657            0 :         DEBUG ("Skipping <migrate> node - missing attribute (old-path, old-key, new-path or new-key)");
     658            0 :         return;
     659              :     }
     660              : 
     661            0 :     PINFO ("Migrating '%s:%s' to '%s:%s'", oldpath->c_str(), oldkey->c_str(),
     662              :            newpath->c_str(), newkey->c_str());
     663              : 
     664            0 :     auto user_value = gnc_gsettings_get_user_value (oldpath->c_str(), oldkey->c_str());
     665            0 :     if (user_value)
     666            0 :         gnc_gsettings_set_value (newpath->c_str(), newkey->c_str(), user_value);
     667              : }
     668              : 
     669              : static void
     670            0 : obsolete_one_key (const opt_str_vec &oldpath, const opt_str_vec &oldkey)
     671              : {
     672            0 :     if (!oldpath || !oldkey )
     673              :     {
     674            0 :         DEBUG ("Skipping <obsolete> node - missing attribute (old-path or old-key)");
     675            0 :         return;
     676              :     }
     677              : 
     678            0 :     PINFO ("Resetting obsolete '%s:%s'", oldpath->c_str(), oldkey->c_str());
     679            0 :     gnc_gsettings_reset (oldpath->c_str(), oldkey->c_str());
     680              : }
     681              : 
     682              : static void
     683            0 : parse_one_release_node (bpt::ptree &pt)
     684              : {
     685              :     /* loop over top-level property tree */
     686            0 :     std::for_each (pt.begin(), pt.end(),
     687            0 :             [] (std::pair<bpt::ptree::key_type, bpt::ptree> node)
     688              :             {
     689            0 :                 if (node.first == "<xmlattr>")
     690            0 :                     return;
     691            0 :                 else if (node.first == "deprecate")
     692            0 :                     deprecate_one_key (node.second.get_optional<std::string> ("<xmlattr>.old-path"),
     693            0 :                                        node.second.get_optional<std::string> ("<xmlattr>.old-key"));
     694            0 :                 else if (node.first == "migrate")
     695            0 :                     migrate_one_key (node.second.get_optional<std::string> ("<xmlattr>.old-path"),
     696            0 :                                      node.second.get_optional<std::string> ("<xmlattr>.old-key"),
     697            0 :                                      node.second.get_optional<std::string> ("<xmlattr>.new-path"),
     698            0 :                                      node.second.get_optional<std::string> ("<xmlattr>.new-key"));
     699            0 :                 else if (node.first == "obsolete")
     700            0 :                     obsolete_one_key (node.second.get_optional<std::string> ("<xmlattr>.old-path"),
     701            0 :                                       node.second.get_optional<std::string> ("<xmlattr>.old-key"));
     702              :                 else
     703              :                 {
     704            0 :                     DEBUG ("Skipping unknown node <%s>", node.first.c_str());
     705            0 :                     return;
     706              :                 }
     707              :             });
     708            0 : }
     709              : 
     710              : static void
     711            0 : transform_settings (int old_maj_min, int cur_maj_min)
     712              : {
     713            0 :     bpt::ptree pt;
     714              : 
     715            0 :     auto pkg_data_dir = gnc_path_get_pkgdatadir();
     716            0 :     auto transform_file = std::string (pkg_data_dir) + "/pref_transformations.xml";
     717            0 :     g_free (pkg_data_dir);
     718              : 
     719            0 :     std::ifstream transform_stream {transform_file};
     720            0 :     if (!transform_stream.is_open())
     721              :     {
     722            0 :         PWARN("Failed to load preferences transformation file '%s'", transform_file.c_str());
     723            0 :         return;
     724              :     }
     725              : 
     726              :     try
     727              :     {
     728            0 :         bpt::read_xml (transform_stream, pt);
     729              :     }
     730            0 :     catch (bpt::xml_parser_error &e) {
     731            0 :         PWARN ("Failed to parse GnuCash preferences transformation file.\n");
     732            0 :         PWARN ("Error message:\n");
     733            0 :         PWARN ("%s\n", e.what());
     734            0 :         return;
     735            0 :     }
     736            0 :     catch (...) {
     737            0 :         PWARN ("Unknown error while parsing GnuCash preferences transformation file.\n");
     738            0 :         return;
     739            0 :     }
     740              : 
     741              :     /* loop over top-level property tree */
     742            0 :     std::for_each (pt.begin(), pt.end(),
     743            0 :             [&old_maj_min, &cur_maj_min] (std::pair<bpt::ptree::key_type, bpt::ptree> node)
     744              :             {
     745            0 :                 if (node.first != "release")
     746              :                 {
     747            0 :                     DEBUG ("Skipping non-<release> node <%s>", node.first.c_str());
     748            0 :                     return;
     749              :                 }
     750            0 :                 auto version = node.second.get_optional<int> ("<xmlattr>.version");
     751            0 :                 if (!version)
     752              :                 {
     753            0 :                     DEBUG ("Skipping <release> node - no version attribute found");
     754            0 :                     return;
     755              :                 }
     756            0 :                 if (*version <= old_maj_min)
     757              :                 {
     758            0 :                     DEBUG ("Skipping <release> node - version %i is less than current compatibility level %i", *version, old_maj_min);
     759            0 :                     return;
     760              :                 }
     761            0 :                 if (*version > cur_maj_min)
     762              :                 {
     763            0 :                     DEBUG ("Skipping <release> node - version %i is greater than current version level %i", *version, cur_maj_min);
     764            0 :                     return;
     765              :                 }
     766            0 :                 DEBUG ("Retrieved version value '%i'", *version);
     767              : 
     768            0 :                 parse_one_release_node (node.second);
     769              :             });
     770            0 : }
     771              : 
     772            0 : void gnc_gsettings_version_upgrade (void)
     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            0 :     ENTER("Start of settings transform routine.");
     798              : 
     799            0 :     auto ogG_maj_min = gnc_gsettings_get_user_value (GNC_PREFS_GROUP_GENERAL, GNC_PREF_VERSION);
     800            0 :     auto og_maj_min = gnc_gsettings_get_user_value (GSET_SCHEMA_OLD_PREFIX "." GNC_PREFS_GROUP_GENERAL, GNC_PREF_VERSION);
     801              : 
     802            0 :     auto cur_maj_min = PROJECT_VERSION_MAJOR * 1000 + PROJECT_VERSION_MINOR;
     803              : 
     804            0 :     if (!ogG_maj_min && !og_maj_min) // new install
     805              :     {
     806            0 :         gnc_gsettings_set_int (GNC_PREFS_GROUP_GENERAL, GNC_PREF_VERSION, cur_maj_min);
     807            0 :         LEAVE ("Setting Previous compatibility level to current version: %i", cur_maj_min);
     808            0 :         return;
     809              :     }
     810              : 
     811            0 :     auto old_maj_min = 0;
     812            0 :     if (!ogG_maj_min) // old preference location
     813            0 :         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            0 :         g_variant_unref (ogG_maj_min);
     817            0 :         old_maj_min = gnc_gsettings_get_int (GNC_PREFS_GROUP_GENERAL, GNC_PREF_VERSION);
     818              :     }
     819            0 :     if (og_maj_min)
     820            0 :         g_variant_unref (og_maj_min);
     821              : 
     822            0 :     PINFO ("Previous setting compatibility level: %i, Current version: %i", old_maj_min, cur_maj_min);
     823              : 
     824            0 :     transform_settings (old_maj_min, cur_maj_min);
     825              : 
     826              :     /* Only write current version if it's more recent than what was set */
     827            0 :     if (cur_maj_min > old_maj_min)
     828            0 :         gnc_gsettings_set_int (GNC_PREFS_GROUP_GENERAL, GNC_PREF_VERSION, cur_maj_min);
     829              : 
     830            0 :     LEAVE("");
     831              : }
        

Generated by: LCOV version 2.0-1