mirror of
				https://gitlab.gnome.org/GNOME/glib.git
				synced 2025-10-31 00:12:19 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			519 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			XML
		
	
	
	
	
	
			
		
		
	
	
			519 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			XML
		
	
	
	
	
	
|   <chapter>
 | |
|     <title>Migrating from GConf to GSettings</title>
 | |
| 
 | |
|     <section>
 | |
|       <title>Before you start</title>
 | |
| 
 | |
|       <para>
 | |
|         Converting individual applications and their settings from GConf to
 | |
|         GSettings can be done at will. But desktop-wide settings like font or
 | |
|         theme settings often have consumers in multiple modules. Therefore,
 | |
|         some consideration has to go into making sure that all users of a setting
 | |
|         are converted to GSettings at the same time or that the program
 | |
|         responsible for configuring that setting continues to update the value in
 | |
|         both places.
 | |
|       </para>
 | |
|       <para>
 | |
|         It is always a good idea to have a look at how others have handled
 | |
|         similar problems before.  An examplaric conversion can be found e.g.
 | |
|         in the <ulink url="http://git.gnome.org/browse/gnome-utils/log/?h=gsettings-tutorial">gsettings-tutorial</ulink> branch of gnome-utils.
 | |
|       </para>
 | |
|     </section>
 | |
| 
 | |
|     <section>
 | |
|       <title>Conceptual differences</title>
 | |
| 
 | |
|       <para>
 | |
|         Conceptually, GConf and GSettings are fairly similar. Both
 | |
|         have a concept of pluggable backends. Both keep information
 | |
|         about keys and their types in schemas. Both have a concept of
 | |
|         mandatory values, which lets you implement lock-down.
 | |
|       </para>
 | |
|       <para>
 | |
|         There are some differences in the approach to schemas. GConf
 | |
|         installs the schemas into the database and has API to handle
 | |
|         schema information (gconf_client_get_default_from_schema(),
 | |
|         gconf_value_get_schema(), etc). GSettings on the other hand
 | |
|         assumes that an application knows its own schemas, and does
 | |
|         not provide API to handle schema information at runtime.
 | |
|         GSettings is also more strict about requiring a schema whenever
 | |
|         you want to read or write a key. To deal with more free-form
 | |
|         information that would appear in schema-less entries in GConf,
 | |
|         GSettings allows for schemas to be 'relocatable'.
 | |
|       </para>
 | |
|       <para>
 | |
|         One difference in the way applications interact with their
 | |
|         settings is that with GConf you interact with a tree of
 | |
|         settings (ie the keys you pass to functions when reading
 | |
|         or writing values are actually paths with the actual name
 | |
|         of the key as the last element. With GSettings, you create
 | |
|         a GSettings object which has an implicit prefix that determines
 | |
|         where the settings get stored in the global tree of settings,
 | |
|         but the keys you pass when reading or writing values are just
 | |
|         the key names, not the full path.
 | |
|       </para>
 | |
|     </section>
 | |
| 
 | |
|     <section>
 | |
|       <title>GConfClient (and GConfBridge) API conversion</title>
 | |
| 
 | |
|       <para>
 | |
|         Most people use GConf via the high-level #GConfClient API.
 | |
|         The corresponding API is the #GSettings object. While not
 | |
|         every GConfClient function has a direct GSettings equivalent,
 | |
|         many do:
 | |
|         <table id="gconf-client-vs-gsettings">
 | |
|           <tgroup cols="2">
 | |
|             <thead>
 | |
|               <row><entry>GConfClient</entry><entry>GSettings</entry></row>
 | |
|             </thead>
 | |
|             <tbody>
 | |
|               <row><entry>gconf_client_get_default()</entry><entry>no direct equivalent,
 | |
|                                                                    instead you call g_settings_new() for the schemas you use</entry></row>
 | |
|               <row><entry>gconf_client_set()</entry><entry>g_settings_set()</entry></row>
 | |
|               <row><entry>gconf_client_get()</entry><entry>g_settings_get()</entry></row>
 | |
|               <row><entry>gconf_client_get_bool()</entry><entry>g_settings_get_boolean()</entry></row>
 | |
|               <row><entry>gconf_client_set_bool()</entry><entry>g_settings_set_boolean()</entry></row>
 | |
|               <row><entry>gconf_client_get_int()</entry><entry>g_settings_get_int()</entry></row>
 | |
|               <row><entry>gconf_client_set_int()</entry><entry>g_settings_set_int()</entry></row>
 | |
|               <row><entry>gconf_client_get_float()</entry><entry>g_settings_get_double()</entry></row>
 | |
|               <row><entry>gconf_client_set_float()</entry><entry>g_settings_set_double()</entry></row>
 | |
|               <row><entry>gconf_client_get_string()</entry><entry>g_settings_get_string()</entry></row>
 | |
|               <row><entry>gconf_client_set_string()</entry><entry>g_settings_set_string()</entry></row>
 | |
|               <row><entry>gconf_client_get_list()</entry><entry>for string lists, see g_settings_get_strv(), else see g_settings_get_value() and #GVariant API</entry></row>
 | |
|               <row><entry>gconf_client_set_list()</entry><entry>for string lists, see g_settings_set_strv(), else see g_settings_set_value() and #GVariant API</entry></row>
 | |
|               <row><entry>gconf_entry_get_is_writable()</entry><entry>g_settings_is_writable()</entry></row>
 | |
|               <row><entry>gconf_client_notify_add()</entry><entry>not required, the #GSettings::changed signal is emitted automatically</entry></row>
 | |
|               <row><entry>gconf_client_add_dir()</entry><entry>not required, each GSettings instance automatically watches all keys in its path</entry></row>
 | |
|               <row><entry>#GConfChangeSet</entry><entry>g_settings_delay(), g_settings_apply()</entry></row>
 | |
|               <row><entry>gconf_client_get_default_from_schema()</entry><entry>no equivalent, applications are expected to know their schema</entry></row>
 | |
|               <row><entry>gconf_client_all_entries()</entry><entry>no equivalent, applications are expected to know their schema, and GSettings does not allow schema-less entries</entry></row>
 | |
|               <row><entry>gconf_client_get_without_default()</entry><entry>no equivalent</entry></row>
 | |
|               <row><entry>gconf_bridge_bind_property()</entry><entry>g_settings_bind()</entry></row>
 | |
|               <row><entry>gconf_bridge_bind_property_full()</entry><entry>g_settings_bind_with_mapping()</entry></row>
 | |
|             </tbody>
 | |
|           </tgroup>
 | |
|         </table>
 | |
|       </para>
 | |
|       <para>
 | |
|         GConfBridge was a third-party library that used GConf to bind an object property
 | |
|         to a particular configuration key. GSettings offers this service itself.
 | |
|       </para>
 | |
|       <para>
 | |
|         There is a pattern that is sometimes used for GConf, where a setting can have
 | |
|         explicit 'value A', explicit 'value B' or 'use the system default'. With GConf,
 | |
|         'use the system default' is sometimes implemented by unsetting the user value.
 | |
|       </para>
 | |
|       <para>
 | |
|         This is not possible in GSettings, since it does not have API to determine if a value
 | |
|         is the default and does not let you unset values. The recommended way (and much
 | |
|         clearer) way in which this can be implemented in GSettings is to have a separate
 | |
|         'use-system-default' boolean setting.
 | |
|       </para>
 | |
|     </section>
 | |
| 
 | |
|     <section>
 | |
|       <title>Change notification</title>
 | |
| 
 | |
|       <para>
 | |
|         GConf requires you to call gconf_client_add_dir() and
 | |
|         gconf_client_notify_add() to get change notification. With
 | |
|         GSettings, this is not necessary; signals get emitted automatically
 | |
|         for every change.
 | |
|       </para>
 | |
|       <para>
 | |
|         The #GSettings::changed signal is emitted for each changed key.
 | |
|         There is also a #GSettings::change-event signal that you can handle
 | |
|         if you need to see groups of keys that get changed at the same time.
 | |
|       </para>
 | |
|       <para>
 | |
|         GSettings also notifies you about changes in writability of keys,
 | |
|         with the #GSettings::writable-changed signal (and the
 | |
|         #GSettings::writable-change-event signal).
 | |
|       </para>
 | |
|     </section>
 | |
| 
 | |
|     <section><title>Change sets</title>
 | |
|       <para>
 | |
|         GConf has a a concept of a set of changes which can be applied or reverted
 | |
|         at once: #GConfChangeSet (GConf doesn't actually apply changes atomically,
 | |
|         which is one of its shortcomings).
 | |
|       </para>
 | |
|       <para>
 | |
|         Instead of a separate object to represent a change set, GSettings has a
 | |
|         'delayed-apply' mode, which can be turned on for a GSettings object by
 | |
|         calling g_settings_delay(). In this mode, changes done to the GSettings
 | |
|         object are not applied - they are still visible when calling g_settings_get()
 | |
|         <emphasis>on the same object</emphasis>, but not to other GSettings instances
 | |
|         or even other processes.
 | |
|       </para>
 | |
|       <para>
 | |
|         To apply the pending changes all at once (GSettings <emphasis>does</emphasis>
 | |
|         atomicity here), call g_settings_apply(). To revert the pending changes,
 | |
|         call g_settings_revert() or just drop the reference to the #GSettings object.
 | |
|       </para>
 | |
|     </section>
 | |
| 
 | |
|     <section>
 | |
|       <title>Schema conversion</title>
 | |
| 
 | |
|       <para>
 | |
|         If you are porting your application from GConf, most likely you already
 | |
|         have a GConf schema. GConf comes with a commandline tool
 | |
|         gsettings-schema-convert that can help with the task of converting
 | |
|         a GConf schema into an equivalent GSettings schema. The tool is not
 | |
|         perfect and may need assistence in some cases.
 | |
|       </para>
 | |
|       <example><title>An example for using gsettings-schema-convert</title>
 | |
|         <para>Running <userinput>gsettings-schema-convert --gconf --xml --schema-id "org.gnome.font-rendering" --output org.gnome.font-rendering.gschema.xml destop_gnome_font_rendering.schemas</userinput> on the following <filename>desktop_gnome_font_rendering.schemas</filename> file:
 | |
|         <programlisting>
 | |
| <![CDATA[
 | |
| <?xml version="1.0"?>
 | |
| <gconfschemafile>
 | |
|     <schemalist>
 | |
|         <schema>
 | |
|             <key>/schemas/desktop/gnome/font_rendering/dpi</key>
 | |
|             <applyto>/desktop/gnome/font_rendering/dpi</applyto>
 | |
|             <owner>gnome</owner>
 | |
|             <type>int</type>
 | |
|             <default>96</default>
 | |
|             <locale name="C">
 | |
|                 <short>DPI</short>
 | |
|                 <long>The resolution used for converting font sizes to pixel sizes, in dots per inch.</long>
 | |
|             </locale>
 | |
|         </schema>
 | |
|     </schemalist>
 | |
| </gconfschemafile>
 | |
| ]]>
 | |
| </programlisting>
 | |
| produces a <filename>org.gnome.font-rendering.gschema.xml</filename> file with the following content:
 | |
| <programlisting>
 | |
| <![CDATA[
 | |
| <schemalist>
 | |
|   <schema id="org.gnome.font-rendering" path="/desktop/gnome/font_rendering/">
 | |
|     <key name="dpi" type="i">
 | |
|       <default>96</default>
 | |
|       <summary>DPI</summary>
 | |
|       <description>The resolution used for converting font sizes to pixel sizes, in dots per inch.</description>
 | |
|     </key>
 | |
|   </schema>
 | |
| </schemalist>
 | |
| ]]>
 | |
| </programlisting>
 | |
| </para>
 | |
|       </example>
 | |
| 
 | |
|       <para>
 | |
|        GSettings schemas are identified at runtime by their id (as specified
 | |
|        in the XML source file). It is recommended to use a dotted name as schema
 | |
|        id, similar in style to a D-Bus bus name, e.g. "org.gnome.SessionManager".
 | |
|        In cases where the settings are general and not specific to one application,
 | |
|        the id should not use StudlyCaps, e.g. "org.gnome.font-rendering".
 | |
|        The filename used for the XML schema source is immaterial, but
 | |
|        schema compiler expects the files to have the extension
 | |
|        <filename>.gschema.xml</filename>. It is recommended to simply
 | |
|        use the schema id as the filename, followed by this extension,
 | |
|        e.g. <filename>org.gnome.SessionManager.gschema.xml</filename>.
 | |
|       </para>
 | |
| 
 | |
|       <para>
 | |
|         The XML source file for your GSettings schema needs to get installed
 | |
|         into <filename>$datadir/glib-2.0/schemas</filename>, and needs to be
 | |
|         compiled into a binary form. At runtime, GSettings looks for compiled
 | |
|         schemas in the <filename>glib-2.0/schemas</filename> subdirectories
 | |
|         of all <envar>XDG_DATA_DIRS</envar> directories, so if you install
 | |
|         your schema in a different location, you need to set the
 | |
|         <envar>XDG_DATA_DIRS</envar> environment variable appropriately.
 | |
|       </para>
 | |
|       <para>
 | |
|         Schemas are compiled into binary form by the
 | |
|         <link linkend="glib-compile-schemas">glib-compile-schemas</link> utility.
 | |
|         GIO provides a <literal>glib_compile_schemas</literal>
 | |
|         variable for the schema compiler.
 | |
|       </para>
 | |
|       <para>
 | |
|         You can ignore all of this by using the provided m4 macros.  To
 | |
|         do this, add to your <filename>configure.ac</filename>:
 | |
| <programlisting>
 | |
| GLIB_GSETTINGS
 | |
| </programlisting>
 | |
|         The corresponding <filename>Makefile.am</filename> fragment looks like
 | |
|         this:
 | |
| <programlisting>
 | |
| # gsettings_SCHEMAS is a list of all the schemas you want to install
 | |
| gsettings_SCHEMAS = my.app.gschema.xml
 | |
| 
 | |
| # include the appropriate makefile rules for schema handling
 | |
| @GSETTINGS_RULES@
 | |
| </programlisting>
 | |
|       </para>
 | |
| 
 | |
|       <para>
 | |
|         This is not sufficient on its own.  You need to mention what the source
 | |
|         of the <filename>my.app.gschema.xml</filename> file is.  If the schema
 | |
|         file is distributed directly with your project's tarball then a mention
 | |
|         in <varname>EXTRA_DIST</varname> is appropriate.  If the schema file is
 | |
|         generated from another source then you will need the appropriate rule
 | |
|         for that, plus probably an item in <varname>EXTRA_DIST</varname> for the
 | |
|         source files used by that rule.
 | |
|       </para>
 | |
| 
 | |
|       <para>
 | |
|         One possible pitfall in doing schema conversion is that the default
 | |
|         values in GSettings schemas are parsed by the #GVariant parser.
 | |
|         This means that strings need to include quotes in the XML.  Also note
 | |
|         that the types are now specified as #GVariant type strings.
 | |
|         <programlisting>
 | |
| <![CDATA[
 | |
| <type>string</type>
 | |
| <default>rgb</default>
 | |
| ]]>
 | |
|         </programlisting>
 | |
|         becomes
 | |
|         <programlisting>
 | |
| <![CDATA[
 | |
| <key name="rgba-order" type="s">
 | |
|   <default>'rgb'</default> <!-- note quotes -->
 | |
| </key>
 | |
| ]]>
 | |
|         </programlisting>
 | |
|       </para>
 | |
|       <para>
 | |
|         Another possible complication is that GConf specifies full paths
 | |
|         for each key, while a GSettings schema has a 'path' attribute that
 | |
|         contains the prefix for all the keys in the schema, and individual
 | |
|         keys just have a simple name. So
 | |
|         <programlisting>
 | |
| <![CDATA[
 | |
| <key>/schemas/desktop/gnome/font_rendering/antialiasing</key>
 | |
| ]]>
 | |
|         </programlisting>
 | |
|         becomes
 | |
|         <programlisting>
 | |
| <![CDATA[
 | |
| <schema id="org.gnome.font" path="/desktop/gnome/font_rendering/">
 | |
|   <key name="antialiasing" type="s">
 | |
| ]]>
 | |
|         </programlisting>
 | |
|       </para>
 | |
|       <para>
 | |
|         Default values can be localized in both GConf and GSettings schemas,
 | |
|         but GSettings uses gettext for the localization. You can specify
 | |
|         the gettext domain to use in the <tag class="attribute">gettext-domain</tag>
 | |
|         attribute. Therefore, when converting localized defaults in GConf,
 | |
|         <programlisting>
 | |
| <![CDATA[
 | |
| <key>/schemas/apps/my_app/font_size</key>
 | |
|   <locale name="C">
 | |
|     <default>18</default>
 | |
|   </locale>
 | |
|   <locale name="be">
 | |
|     <default>24</default>
 | |
|   </locale>
 | |
| </key>
 | |
| ]]>
 | |
|         </programlisting>
 | |
|         becomes
 | |
|         <programlisting>
 | |
| <![CDATA[
 | |
| <schema id="..." gettext-domain="your-domain">
 | |
|  ...
 | |
| <key name="font-size" type="i">
 | |
|   <default l10n="messages" context="font_size">18</default>
 | |
| </key>
 | |
| ]]>
 | |
|         </programlisting>
 | |
|       </para>
 | |
|       <para>
 | |
|         GSettings uses gettext for translation of default values.
 | |
|         The string that is translated is exactly the string that appears
 | |
|         inside of the <tag class='starttag'>default</tag> element.  This
 | |
|         includes the quotation marks that appear around strings.
 | |
|         Default values must be marked with the <varname>l10n</varname>
 | |
|         attribute in the <tag class='starttag'>default</tag> tag, which
 | |
|         should be set as equal to <literal>'messages'</literal> or
 | |
|         <literal>'time'</literal> depending on the desired category.  An
 | |
|         optional translation context can also be specified with the
 | |
|         <varname>context</varname> attribute, as in the example.  This
 | |
|         is usually recommended, since the string "<literal>18</literal>"
 | |
|         is not particularly easy to translate without context.  The
 | |
|         translated version of the default value should be stored in the
 | |
|         specified <varname>gettext-domain</varname>.  Care must be taken
 | |
|         during translation to ensure that all translated values remain
 | |
|         syntactically valid; mistakes here will cause runtime errors.
 | |
|       </para>
 | |
|       <para>
 | |
|         GSettings schemas have optional <tag class="starttag">summary</tag> and
 | |
|         <tag class="starttag">description</tag> elements for each key which
 | |
|         correspond to the <tag class="starttag">short</tag> and
 | |
|         <tag class="starttag">long</tag> elements in the GConf schema and
 | |
|         will be used in similar ways by a future gsettings-editor, so you
 | |
|         should use the same conventions for them: The summary is just a short
 | |
|         label with no punctuation, the description can be one or more complete
 | |
|         sentences. If multiple paragraphs are desired for the description, the
 | |
|         paragraphs should be separated by a completely empty line.
 | |
|       </para>
 | |
|       <para>
 | |
|         Translations for these strings will also be handled
 | |
|         via gettext, so you should arrange for these strings to be
 | |
|         extracted into your gettext catalog. One way to do that is to use
 | |
|         intltool. For that, you use <tag class="starttag">_summary</tag>
 | |
|         and <tag class="starttag">_description</tag> elements in a
 | |
|         .gschema.xml.in file and use
 | |
|         <literal>@<!-- -->INTLTOOL_XML_NOMERGE_RULE<!-- -->@</literal>
 | |
|         in your Makefile.am to produce the .gschema.xml file. The
 | |
|         <literal>NOMERGE</literal> part of the rule instructs intltool
 | |
|         to extract translatable strings, but not merge the translations
 | |
|         back into the generated xml file.
 | |
|       </para>
 | |
|       <para>
 | |
|         GSettings is a bit more restrictive about key names than GConf. Key
 | |
|         names in GSettings can be at most 32 characters long, and must only
 | |
|         consist of lowercase characters, numbers and dashes, with no
 | |
|         consecutive dashes. The first character must not be a number or dash,
 | |
|         and the last character cannot be '-'.
 | |
|       </para>
 | |
|       <para>
 | |
|         If you are using the GConf backend for GSettings during the
 | |
|         transition, you may want to keep your key names the same they
 | |
|         were in GConf, so that existing settings in the users GConf
 | |
|         database are preserved. You can achieve this by using the
 | |
|         <option>--allow-any-name</option> with the
 | |
|         <link linkend="glib-compile-schemas">glib-compile-schemas</link> schema
 | |
|         compiler. Note that this option is only meant
 | |
|         to ease the process of porting your application, allowing parts
 | |
|         of your application to continue to access GConf and parts to use
 | |
|         GSettings. By the time you have finished porting your application
 | |
|         you must ensure that all key names are valid.
 | |
|       </para>
 | |
|     </section>
 | |
| 
 | |
|     <section><title>Data conversion</title>
 | |
|       <para>
 | |
|         GConf comes with a GSettings backend that can be used to
 | |
|         facility the transition to the GSettings API until you are
 | |
|         ready to make the jump to a different backend (most likely
 | |
|         dconf). To use it, you need to set the <envar>GSETTINGS_BACKEND</envar>
 | |
|         to 'gconf', e.g. by using
 | |
| <programlisting>
 | |
|   g_setenv ("GSETTINGS_BACKEND", "gconf", TRUE);
 | |
| </programlisting>
 | |
|         early on in your program. Note that this backend is meant purely
 | |
|         as a transition tool, and should not be used in production.
 | |
|       </para>
 | |
|       <para>
 | |
|         GConf also comes with a utility called
 | |
|         <command>gsettings-data-convert</command>, which is designed to help
 | |
|         with the task of migrating user settings from GConf into another
 | |
|         GSettings backend. It can be run manually, but it is designed to be
 | |
|         executed automatically, every time a user logs in. It keeps track of
 | |
|         the data migrations that it has already done, and it is harmless to
 | |
|         run it more than once.
 | |
|       </para>
 | |
|       <para>
 | |
|         To make use of this utility, you must install a keyfile in the
 | |
|         directory <filename>/usr/share/GConf/gsettings</filename> which
 | |
|         lists the GSettings keys and GConf paths to map to each other, for
 | |
|         each schema that you want to migrate user data for.
 | |
|       </para>
 | |
|       <para>
 | |
|         Here is an example:
 | |
|         <programlisting>
 | |
| <![CDATA[
 | |
| [org.gnome.fonts]
 | |
| antialiasing = /desktop/gnome/font_rendering/antialiasing
 | |
| dpi = /desktop/gnome/font_rendering/dpi
 | |
| hinting = /desktop/gnome/font_rendering/hinting
 | |
| rgba-order = /desktop/gnome/font_rendering/rgba_order
 | |
| 
 | |
| [apps.myapp:/path/to/myapps/]
 | |
| some-odd-key1 = /apps/myapp/some_ODD-key1
 | |
| ]]>
 | |
|         </programlisting>
 | |
|         The last key demonstrates that it may be necessary to modify the key
 | |
|         name to comply with stricter GSettings key name rules. Of course,
 | |
|         that means your application must use the new key names when looking
 | |
|         up settings in GSettings.
 | |
|       </para>
 | |
|       <para>
 | |
|         The last group in the example also shows how to handle the case
 | |
|         of 'relocatable' schemas, which don't have a fixed path. You can
 | |
|         specify the path to use in the group name, separated by a colon.
 | |
|       </para>
 | |
|       <para>
 | |
|         There are some limitations: <command>gsettings-data-convert</command>
 | |
|         does not do any transformation of the values. And it does not handle
 | |
|         complex GConf types other than lists of strings or integers.
 | |
|       </para>
 | |
|       <para>
 | |
|         Don't forget to require GConf 2.31.1 or newer in your configure
 | |
|         script if you are making use of the GConf backend or the conversion
 | |
|         utility.
 | |
|       </para>
 | |
| 
 | |
|       <para>
 | |
|         If, as an application developer, you are interested in manually
 | |
|         ensuring that <command>gsettings-data-convert</command> has been
 | |
|         invoked (for example, to deal with the case where the user is
 | |
|         logged in during a distribution upgrade or for non-XDG desktop
 | |
|         environments which do not run the command as an autostart) you
 | |
|         may invoke it manually during your program initialisation.  This
 | |
|         is not recommended for all application authors -- it is your
 | |
|         choice if this use case concerns you enough.
 | |
|       </para>
 | |
|       <para>
 | |
|         Internally, <command>gsettings-data-convert</command> uses a
 | |
|         keyfile to track which settings have been migrated.  The
 | |
|         following code fragment will check that keyfile to see if your
 | |
|         data conversion script has been run yet and, if not, will
 | |
|         attempt to invoke the tool to run it.  You should adapt it to
 | |
|         your application as you see fit.
 | |
|       </para>
 | |
|       <para>
 | |
|         <programlisting>
 | |
| <![CDATA[
 | |
| static void
 | |
| ensure_migrated (const gchar *name)
 | |
| {
 | |
|   gboolean needed = TRUE;
 | |
|   GKeyFile *kf;
 | |
|   gchar **list;
 | |
|   gsize i, n;
 | |
| 
 | |
|   kf = g_key_file_new ();
 | |
| 
 | |
|   g_key_file_load_from_data_dirs (kf, "gsettings-data-convert",
 | |
|                                   NULL, G_KEY_FILE_NONE, NULL);
 | |
|   list = g_key_file_get_string_list (kf, "State", "converted", &n, NULL);
 | |
| 
 | |
|   if (list)
 | |
|     {
 | |
|       for (i = 0; i < n; i++)
 | |
|         if (strcmp (list[i], name) == 0)
 | |
|           {
 | |
|             needed = FALSE;
 | |
|             break;
 | |
|           }
 | |
| 
 | |
|       g_strfreev (list);
 | |
|     }
 | |
| 
 | |
|   g_key_file_free (kf);
 | |
| 
 | |
|   if (needed)
 | |
|     g_spawn_command_line_sync ("gsettings-data-convert",
 | |
|                                NULL, NULL, NULL, NULL);
 | |
| }
 | |
| 
 | |
| ]]>
 | |
|         </programlisting>
 | |
|       </para>
 | |
|       <para>
 | |
|         Although there is the possibility that the
 | |
|         <command>gsettings-data-convert</command> script will end up
 | |
|         running multiple times concurrently with this approach, it is
 | |
|         believed that this is safe.
 | |
|       </para>
 | |
|     </section>
 | |
|   </chapter>
 |