mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-27 07:56:14 +01:00
e56498ee0b
_GNU_SOURCE must be defined before including any other (system) header, so defining it in glib-unix.h (and hoping no one has included anything else before that) is wrong. And the "#define _USE_GNU" workaround for this problem in gnetworkingprivate.h is even wronger (and still prone to failure anyway due to single-include guards). Fix this by defining _GNU_SOURCE in config.h when building against glibc. In theory this is bad because new releases of glibc may include symbols that conflict with glib symbols, which could then cause compile failures. However, most people only see new releases of glibc when they upgrade their distro, at which point they also generally get new releases of gcc, which have new warnings/errors to clean up anyway. https://bugzilla.gnome.org/show_bug.cgi?id=649201
3026 lines
91 KiB
C
3026 lines
91 KiB
C
/*
|
|
* Copyright © 2009, 2010 Codethink Limited
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the licence, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*
|
|
* Author: Ryan Lortie <desrt@desrt.ca>
|
|
*/
|
|
|
|
/* Prelude {{{1 */
|
|
#include "config.h"
|
|
|
|
#include <glib.h>
|
|
#include <glibintl.h>
|
|
#include <locale.h>
|
|
|
|
#include "gsettings.h"
|
|
|
|
#include "gdelayedsettingsbackend.h"
|
|
#include "gsettingsbackendinternal.h"
|
|
#include "gsettings-mapping.h"
|
|
#include "gio-marshal.h"
|
|
#include "gsettingsschema.h"
|
|
|
|
#include <string.h>
|
|
|
|
|
|
#include "strinfo.c"
|
|
|
|
/**
|
|
* SECTION:gsettings
|
|
* @short_description: High-level API for application settings
|
|
*
|
|
* The #GSettings class provides a convenient API for storing and retrieving
|
|
* application settings.
|
|
*
|
|
* Reads and writes can be considered to be non-blocking. Reading
|
|
* settings with #GSettings is typically extremely fast: on
|
|
* approximately the same order of magnitude (but slower than) a
|
|
* #GHashTable lookup. Writing settings is also extremely fast in terms
|
|
* of time to return to your application, but can be extremely expensive
|
|
* for other threads and other processes. Many settings backends
|
|
* (including dconf) have lazy initialisation which means in the common
|
|
* case of the user using their computer without modifying any settings
|
|
* a lot of work can be avoided. For dconf, the D-Bus service doesn't
|
|
* even need to be started in this case. For this reason, you should
|
|
* only ever modify #GSettings keys in response to explicit user action.
|
|
* Particular care should be paid to ensure that modifications are not
|
|
* made during startup -- for example, when setting the initial value
|
|
* of preferences widgets. The built-in g_settings_bind() functionality
|
|
* is careful not to write settings in response to notify signals as a
|
|
* result of modifications that it makes to widgets.
|
|
*
|
|
* When creating a GSettings instance, you have to specify a schema
|
|
* that describes the keys in your settings and their types and default
|
|
* values, as well as some other information.
|
|
*
|
|
* Normally, a schema has as fixed path that determines where the settings
|
|
* are stored in the conceptual global tree of settings. However, schemas
|
|
* can also be 'relocatable', i.e. not equipped with a fixed path. This is
|
|
* useful e.g. when the schema describes an 'account', and you want to be
|
|
* able to store a arbitrary number of accounts.
|
|
*
|
|
* Unlike other configuration systems (like GConf), GSettings does not
|
|
* restrict keys to basic types like strings and numbers. GSettings stores
|
|
* values as #GVariant, and allows any #GVariantType for keys. Key names
|
|
* are restricted to lowercase characters, numbers and '-'. Furthermore,
|
|
* the names must begin with a lowercase character, must not end
|
|
* with a '-', and must not contain consecutive dashes. Key names can
|
|
* be up to 32 characters long.
|
|
*
|
|
* Similar to GConf, the default values in GSettings schemas can be
|
|
* localized, but the localized values are stored in gettext catalogs
|
|
* and looked up with the domain that is specified in the
|
|
* <tag class="attribute">gettext-domain</tag> attribute of the
|
|
* <tag class="starttag">schemalist</tag> or <tag class="starttag">schema</tag>
|
|
* elements and the category that is specified in the l10n attribute of the
|
|
* <tag class="starttag">key</tag> element.
|
|
*
|
|
* GSettings uses schemas in a compact binary form that is created
|
|
* by the <link linkend="glib-compile-schemas">glib-compile-schemas</link>
|
|
* utility. The input is a schema description in an XML format that can be
|
|
* described by the following DTD:
|
|
* |[<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" parse="text" href="../../../../gio/gschema.dtd"><xi:fallback>FIXME: MISSING XINCLUDE CONTENT</xi:fallback></xi:include>]|
|
|
*
|
|
* glib-compile-schemas expects schema files to have the extension <filename>.gschema.xml</filename>
|
|
*
|
|
* At runtime, schemas are identified by their id (as specified
|
|
* in the <tag class="attribute">id</tag> attribute of the
|
|
* <tag class="starttag">schema</tag> element). The
|
|
* convention for schema ids is to use a dotted name, similar in
|
|
* style to a D-Bus bus name, e.g. "org.gnome.SessionManager". In particular,
|
|
* if the settings are for a specific service that owns a D-Bus bus name,
|
|
* the D-Bus bus name and schema id should match. For schemas which deal
|
|
* with settings not associated with one named application, the id should
|
|
* not use StudlyCaps, e.g. "org.gnome.font-rendering".
|
|
*
|
|
* In addition to #GVariant types, keys can have types that have enumerated
|
|
* types. These can be described by a <tag class="starttag">choice</tag>,
|
|
* <tag class="starttag">enum</tag> or <tag class="starttag">flags</tag> element, see
|
|
* <xref linkend="schema-enumerated"/>. The underlying type of
|
|
* such a key is string, but you can use g_settings_get_enum(),
|
|
* g_settings_set_enum(), g_settings_get_flags(), g_settings_set_flags()
|
|
* access the numeric values corresponding to the string value of enum
|
|
* and flags keys.
|
|
*
|
|
* <example id="schema-default-values"><title>Default values</title>
|
|
* <programlisting><![CDATA[
|
|
* <schemalist>
|
|
* <schema id="org.gtk.Test" path="/tests/" gettext-domain="test">
|
|
*
|
|
* <key name="greeting" type="s">
|
|
* <default l10n="messages">"Hello, earthlings"</default>
|
|
* <summary>A greeting</summary>
|
|
* <description>
|
|
* Greeting of the invading martians
|
|
* </description>
|
|
* </key>
|
|
*
|
|
* <key name="box" type="(ii)">
|
|
* <default>(20,30)</default>
|
|
* </key>
|
|
*
|
|
* </schema>
|
|
* </schemalist>
|
|
* ]]></programlisting></example>
|
|
*
|
|
* <example id="schema-enumerated"><title>Ranges, choices and enumerated types</title>
|
|
* <programlisting><![CDATA[
|
|
* <schemalist>
|
|
*
|
|
* <enum id="myenum">
|
|
* <value nick="first" value="1"/>
|
|
* <value nick="second" value="2"/>
|
|
* </enum>
|
|
*
|
|
* <enum id="myflags">
|
|
* <value nick="flag1" value="1"/>
|
|
* <value nick="flag2" value="2"/>
|
|
* <value nick="flag3" value="4"/>
|
|
* </enum>
|
|
*
|
|
* <schema id="org.gtk.Test">
|
|
*
|
|
* <key name="key-with-range" type="i">
|
|
* <range min="1" max="100"/>
|
|
* <default>10</default>
|
|
* </key>
|
|
*
|
|
* <key name="key-with-choices" type="s">
|
|
* <choices>
|
|
* <choice value='Elisabeth'/>
|
|
* <choice value='Annabeth'/>
|
|
* <choice value='Joe'/>
|
|
* </choices>
|
|
* <aliases>
|
|
* <alias value='Anna' target='Annabeth'/>
|
|
* <alias value='Beth' target='Elisabeth'/>
|
|
* </aliases>
|
|
* <default>'Joe'</default>
|
|
* </key>
|
|
*
|
|
* <key name='enumerated-key' enum='myenum'>
|
|
* <default>'first'</default>
|
|
* </key>
|
|
*
|
|
* <key name='flags-key' flags='myflags'>
|
|
* <default>["flag1",flag2"]</default>
|
|
* </key>
|
|
* </schema>
|
|
* </schemalist>
|
|
* ]]></programlisting></example>
|
|
*
|
|
* <refsect2>
|
|
* <title>Vendor overrides</title>
|
|
* <para>
|
|
* Default values are defined in the schemas that get installed by
|
|
* an application. Sometimes, it is necessary for a vendor or distributor
|
|
* to adjust these defaults. Since patching the XML source for the schema
|
|
* is inconvenient and error-prone,
|
|
* <link linkend="glib-compile-schemas">glib-compile-schemas</link> reads
|
|
* so-called 'vendor override' files. These are keyfiles in the same
|
|
* directory as the XML schema sources which can override default values.
|
|
* The schema id serves as the group name in the key file, and the values
|
|
* are expected in serialized GVariant form, as in the following example:
|
|
* <informalexample><programlisting>
|
|
* [org.gtk.Example]
|
|
* key1='string'
|
|
* key2=1.5
|
|
* </programlisting></informalexample>
|
|
* </para>
|
|
* <para>
|
|
* glib-compile-schemas expects schema files to have the extension
|
|
* <filename>.gschema.override</filename>
|
|
* </para>
|
|
* </refsect2>
|
|
*
|
|
* <refsect2>
|
|
* <title>Binding</title>
|
|
* <para>
|
|
* A very convenient feature of GSettings lets you bind #GObject properties
|
|
* directly to settings, using g_settings_bind(). Once a GObject property
|
|
* has been bound to a setting, changes on either side are automatically
|
|
* propagated to the other side. GSettings handles details like
|
|
* mapping between GObject and GVariant types, and preventing infinite
|
|
* cycles.
|
|
* </para>
|
|
* <para>
|
|
* This makes it very easy to hook up a preferences dialog to the
|
|
* underlying settings. To make this even more convenient, GSettings
|
|
* looks for a boolean property with the name "sensitivity" and
|
|
* automatically binds it to the writability of the bound setting.
|
|
* If this 'magic' gets in the way, it can be suppressed with the
|
|
* #G_SETTINGS_BIND_NO_SENSITIVITY flag.
|
|
* </para>
|
|
* </refsect2>
|
|
**/
|
|
|
|
struct _GSettingsPrivate
|
|
{
|
|
/* where the signals go... */
|
|
GMainContext *main_context;
|
|
|
|
GSettingsBackend *backend;
|
|
GSettingsSchema *schema;
|
|
gchar *schema_name;
|
|
gchar *path;
|
|
|
|
GDelayedSettingsBackend *delayed;
|
|
};
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_SCHEMA,
|
|
PROP_BACKEND,
|
|
PROP_PATH,
|
|
PROP_HAS_UNAPPLIED,
|
|
PROP_DELAY_APPLY
|
|
};
|
|
|
|
enum
|
|
{
|
|
SIGNAL_WRITABLE_CHANGE_EVENT,
|
|
SIGNAL_WRITABLE_CHANGED,
|
|
SIGNAL_CHANGE_EVENT,
|
|
SIGNAL_CHANGED,
|
|
N_SIGNALS
|
|
};
|
|
|
|
static guint g_settings_signals[N_SIGNALS];
|
|
|
|
G_DEFINE_TYPE (GSettings, g_settings, G_TYPE_OBJECT)
|
|
|
|
/* Signals {{{1 */
|
|
static gboolean
|
|
g_settings_real_change_event (GSettings *settings,
|
|
const GQuark *keys,
|
|
gint n_keys)
|
|
{
|
|
gint i;
|
|
|
|
if (keys == NULL)
|
|
keys = g_settings_schema_list (settings->priv->schema, &n_keys);
|
|
|
|
for (i = 0; i < n_keys; i++)
|
|
g_signal_emit (settings, g_settings_signals[SIGNAL_CHANGED],
|
|
keys[i], g_quark_to_string (keys[i]));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
g_settings_real_writable_change_event (GSettings *settings,
|
|
GQuark key)
|
|
{
|
|
const GQuark *keys = &key;
|
|
gint n_keys = 1;
|
|
gint i;
|
|
|
|
if (key == 0)
|
|
keys = g_settings_schema_list (settings->priv->schema, &n_keys);
|
|
|
|
for (i = 0; i < n_keys; i++)
|
|
g_signal_emit (settings, g_settings_signals[SIGNAL_WRITABLE_CHANGED],
|
|
keys[i], g_quark_to_string (keys[i]));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
settings_backend_changed (GObject *target,
|
|
GSettingsBackend *backend,
|
|
const gchar *key,
|
|
gpointer origin_tag)
|
|
{
|
|
GSettings *settings = G_SETTINGS (target);
|
|
gboolean ignore_this;
|
|
gint i;
|
|
|
|
/* We used to assert here:
|
|
*
|
|
* settings->priv->backend == backend
|
|
*
|
|
* but it could be the case that a notification is queued for delivery
|
|
* while someone calls g_settings_delay() (which changes the backend).
|
|
*
|
|
* Since the delay backend would just pass that straight through
|
|
* anyway, it doesn't make sense to try to detect this case.
|
|
* Therefore, we just accept it.
|
|
*/
|
|
|
|
for (i = 0; key[i] == settings->priv->path[i]; i++);
|
|
|
|
if (settings->priv->path[i] == '\0' &&
|
|
g_settings_schema_has_key (settings->priv->schema, key + i))
|
|
{
|
|
GQuark quark;
|
|
|
|
quark = g_quark_from_string (key + i);
|
|
g_signal_emit (settings, g_settings_signals[SIGNAL_CHANGE_EVENT],
|
|
0, &quark, 1, &ignore_this);
|
|
}
|
|
}
|
|
|
|
static void
|
|
settings_backend_path_changed (GObject *target,
|
|
GSettingsBackend *backend,
|
|
const gchar *path,
|
|
gpointer origin_tag)
|
|
{
|
|
GSettings *settings = G_SETTINGS (target);
|
|
gboolean ignore_this;
|
|
|
|
if (g_str_has_prefix (settings->priv->path, path))
|
|
g_signal_emit (settings, g_settings_signals[SIGNAL_CHANGE_EVENT],
|
|
0, NULL, 0, &ignore_this);
|
|
}
|
|
|
|
static void
|
|
settings_backend_keys_changed (GObject *target,
|
|
GSettingsBackend *backend,
|
|
const gchar *path,
|
|
const gchar * const *items,
|
|
gpointer origin_tag)
|
|
{
|
|
GSettings *settings = G_SETTINGS (target);
|
|
gboolean ignore_this;
|
|
gint i;
|
|
|
|
for (i = 0; settings->priv->path[i] &&
|
|
settings->priv->path[i] == path[i]; i++);
|
|
|
|
if (path[i] == '\0')
|
|
{
|
|
GQuark quarks[256];
|
|
gint j, l = 0;
|
|
|
|
for (j = 0; items[j]; j++)
|
|
{
|
|
const gchar *item = items[j];
|
|
gint k;
|
|
|
|
for (k = 0; item[k] == settings->priv->path[i + k]; k++);
|
|
|
|
if (settings->priv->path[i + k] == '\0' &&
|
|
g_settings_schema_has_key (settings->priv->schema, item + k))
|
|
quarks[l++] = g_quark_from_string (item + k);
|
|
|
|
/* "256 quarks ought to be enough for anybody!"
|
|
* If this bites you, I'm sorry. Please file a bug.
|
|
*/
|
|
g_assert (l < 256);
|
|
}
|
|
|
|
if (l > 0)
|
|
g_signal_emit (settings, g_settings_signals[SIGNAL_CHANGE_EVENT],
|
|
0, quarks, l, &ignore_this);
|
|
}
|
|
}
|
|
|
|
static void
|
|
settings_backend_writable_changed (GObject *target,
|
|
GSettingsBackend *backend,
|
|
const gchar *key)
|
|
{
|
|
GSettings *settings = G_SETTINGS (target);
|
|
gboolean ignore_this;
|
|
gint i;
|
|
|
|
for (i = 0; key[i] == settings->priv->path[i]; i++);
|
|
|
|
if (settings->priv->path[i] == '\0' &&
|
|
g_settings_schema_has_key (settings->priv->schema, key + i))
|
|
g_signal_emit (settings, g_settings_signals[SIGNAL_WRITABLE_CHANGE_EVENT],
|
|
0, g_quark_from_string (key + i), &ignore_this);
|
|
}
|
|
|
|
static void
|
|
settings_backend_path_writable_changed (GObject *target,
|
|
GSettingsBackend *backend,
|
|
const gchar *path)
|
|
{
|
|
GSettings *settings = G_SETTINGS (target);
|
|
gboolean ignore_this;
|
|
|
|
if (g_str_has_prefix (settings->priv->path, path))
|
|
g_signal_emit (settings, g_settings_signals[SIGNAL_WRITABLE_CHANGE_EVENT],
|
|
0, (GQuark) 0, &ignore_this);
|
|
}
|
|
|
|
/* Properties, Construction, Destruction {{{1 */
|
|
static void
|
|
g_settings_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GSettings *settings = G_SETTINGS (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_SCHEMA:
|
|
g_assert (settings->priv->schema_name == NULL);
|
|
settings->priv->schema_name = g_value_dup_string (value);
|
|
break;
|
|
|
|
case PROP_PATH:
|
|
settings->priv->path = g_value_dup_string (value);
|
|
break;
|
|
|
|
case PROP_BACKEND:
|
|
settings->priv->backend = g_value_dup_object (value);
|
|
break;
|
|
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
}
|
|
|
|
static void
|
|
g_settings_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GSettings *settings = G_SETTINGS (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_SCHEMA:
|
|
g_value_set_string (value, settings->priv->schema_name);
|
|
break;
|
|
|
|
case PROP_BACKEND:
|
|
g_value_set_object (value, settings->priv->backend);
|
|
break;
|
|
|
|
case PROP_PATH:
|
|
g_value_set_string (value, settings->priv->path);
|
|
break;
|
|
|
|
case PROP_HAS_UNAPPLIED:
|
|
g_value_set_boolean (value, g_settings_get_has_unapplied (settings));
|
|
break;
|
|
|
|
case PROP_DELAY_APPLY:
|
|
g_value_set_boolean (value, settings->priv->delayed != NULL);
|
|
break;
|
|
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
}
|
|
|
|
static const GSettingsListenerVTable listener_vtable = {
|
|
settings_backend_changed,
|
|
settings_backend_path_changed,
|
|
settings_backend_keys_changed,
|
|
settings_backend_writable_changed,
|
|
settings_backend_path_writable_changed
|
|
};
|
|
|
|
static void
|
|
g_settings_constructed (GObject *object)
|
|
{
|
|
GSettings *settings = G_SETTINGS (object);
|
|
const gchar *schema_path;
|
|
|
|
settings->priv->schema = g_settings_schema_new (settings->priv->schema_name);
|
|
schema_path = g_settings_schema_get_path (settings->priv->schema);
|
|
|
|
if (settings->priv->path && schema_path && strcmp (settings->priv->path, schema_path) != 0)
|
|
g_error ("settings object created with schema '%s' and path '%s', but "
|
|
"path '%s' is specified by schema",
|
|
settings->priv->schema_name, settings->priv->path, schema_path);
|
|
|
|
if (settings->priv->path == NULL)
|
|
{
|
|
if (schema_path == NULL)
|
|
g_error ("attempting to create schema '%s' without a path",
|
|
settings->priv->schema_name);
|
|
|
|
settings->priv->path = g_strdup (schema_path);
|
|
}
|
|
|
|
if (settings->priv->backend == NULL)
|
|
settings->priv->backend = g_settings_backend_get_default ();
|
|
|
|
g_settings_backend_watch (settings->priv->backend,
|
|
&listener_vtable, G_OBJECT (settings),
|
|
settings->priv->main_context);
|
|
g_settings_backend_subscribe (settings->priv->backend,
|
|
settings->priv->path);
|
|
}
|
|
|
|
static void
|
|
g_settings_finalize (GObject *object)
|
|
{
|
|
GSettings *settings = G_SETTINGS (object);
|
|
|
|
g_settings_backend_unsubscribe (settings->priv->backend,
|
|
settings->priv->path);
|
|
g_main_context_unref (settings->priv->main_context);
|
|
g_object_unref (settings->priv->backend);
|
|
g_object_unref (settings->priv->schema);
|
|
g_free (settings->priv->schema_name);
|
|
g_free (settings->priv->path);
|
|
|
|
G_OBJECT_CLASS (g_settings_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
g_settings_init (GSettings *settings)
|
|
{
|
|
settings->priv = G_TYPE_INSTANCE_GET_PRIVATE (settings,
|
|
G_TYPE_SETTINGS,
|
|
GSettingsPrivate);
|
|
|
|
settings->priv->main_context = g_main_context_get_thread_default ();
|
|
|
|
if (settings->priv->main_context == NULL)
|
|
settings->priv->main_context = g_main_context_default ();
|
|
|
|
g_main_context_ref (settings->priv->main_context);
|
|
}
|
|
|
|
static void
|
|
g_settings_class_init (GSettingsClass *class)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
|
|
|
class->writable_change_event = g_settings_real_writable_change_event;
|
|
class->change_event = g_settings_real_change_event;
|
|
|
|
object_class->set_property = g_settings_set_property;
|
|
object_class->get_property = g_settings_get_property;
|
|
object_class->constructed = g_settings_constructed;
|
|
object_class->finalize = g_settings_finalize;
|
|
|
|
g_type_class_add_private (object_class, sizeof (GSettingsPrivate));
|
|
|
|
/**
|
|
* GSettings::changed:
|
|
* @settings: the object on which the signal was emitted
|
|
* @key: the name of the key that changed
|
|
*
|
|
* The "changed" signal is emitted when a key has potentially changed.
|
|
* You should call one of the g_settings_get() calls to check the new
|
|
* value.
|
|
*
|
|
* This signal supports detailed connections. You can connect to the
|
|
* detailed signal "changed::x" in order to only receive callbacks
|
|
* when key "x" changes.
|
|
*/
|
|
g_settings_signals[SIGNAL_CHANGED] =
|
|
g_signal_new ("changed", G_TYPE_SETTINGS,
|
|
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
|
|
G_STRUCT_OFFSET (GSettingsClass, changed),
|
|
NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE,
|
|
1, G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE);
|
|
|
|
/**
|
|
* GSettings::change-event:
|
|
* @settings: the object on which the signal was emitted
|
|
* @keys: (array length=n_keys) (element-type GQuark) (allow-none):
|
|
* an array of #GQuark<!-- -->s for the changed keys, or %NULL
|
|
* @n_keys: the length of the @keys array, or 0
|
|
* @returns: %TRUE to stop other handlers from being invoked for the
|
|
* event. FALSE to propagate the event further.
|
|
*
|
|
* The "change-event" signal is emitted once per change event that
|
|
* affects this settings object. You should connect to this signal
|
|
* only if you are interested in viewing groups of changes before they
|
|
* are split out into multiple emissions of the "changed" signal.
|
|
* For most use cases it is more appropriate to use the "changed" signal.
|
|
*
|
|
* In the event that the change event applies to one or more specified
|
|
* keys, @keys will be an array of #GQuark of length @n_keys. In the
|
|
* event that the change event applies to the #GSettings object as a
|
|
* whole (ie: potentially every key has been changed) then @keys will
|
|
* be %NULL and @n_keys will be 0.
|
|
*
|
|
* The default handler for this signal invokes the "changed" signal
|
|
* for each affected key. If any other connected handler returns
|
|
* %TRUE then this default functionality will be supressed.
|
|
*/
|
|
g_settings_signals[SIGNAL_CHANGE_EVENT] =
|
|
g_signal_new ("change-event", G_TYPE_SETTINGS,
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (GSettingsClass, change_event),
|
|
g_signal_accumulator_true_handled, NULL,
|
|
_gio_marshal_BOOL__POINTER_INT,
|
|
G_TYPE_BOOLEAN, 2, G_TYPE_POINTER, G_TYPE_INT);
|
|
|
|
/**
|
|
* GSettings::writable-changed:
|
|
* @settings: the object on which the signal was emitted
|
|
* @key: the key
|
|
*
|
|
* The "writable-changed" signal is emitted when the writability of a
|
|
* key has potentially changed. You should call
|
|
* g_settings_is_writable() in order to determine the new status.
|
|
*
|
|
* This signal supports detailed connections. You can connect to the
|
|
* detailed signal "writable-changed::x" in order to only receive
|
|
* callbacks when the writability of "x" changes.
|
|
*/
|
|
g_settings_signals[SIGNAL_WRITABLE_CHANGED] =
|
|
g_signal_new ("writable-changed", G_TYPE_SETTINGS,
|
|
G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
|
|
G_STRUCT_OFFSET (GSettingsClass, writable_changed),
|
|
NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE,
|
|
1, G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE);
|
|
|
|
/**
|
|
* GSettings::writable-change-event:
|
|
* @settings: the object on which the signal was emitted
|
|
* @key: the quark of the key, or 0
|
|
* @returns: %TRUE to stop other handlers from being invoked for the
|
|
* event. FALSE to propagate the event further.
|
|
*
|
|
* The "writable-change-event" signal is emitted once per writability
|
|
* change event that affects this settings object. You should connect
|
|
* to this signal if you are interested in viewing groups of changes
|
|
* before they are split out into multiple emissions of the
|
|
* "writable-changed" signal. For most use cases it is more
|
|
* appropriate to use the "writable-changed" signal.
|
|
*
|
|
* In the event that the writability change applies only to a single
|
|
* key, @key will be set to the #GQuark for that key. In the event
|
|
* that the writability change affects the entire settings object,
|
|
* @key will be 0.
|
|
*
|
|
* The default handler for this signal invokes the "writable-changed"
|
|
* and "changed" signals for each affected key. This is done because
|
|
* changes in writability might also imply changes in value (if for
|
|
* example, a new mandatory setting is introduced). If any other
|
|
* connected handler returns %TRUE then this default functionality
|
|
* will be supressed.
|
|
*/
|
|
g_settings_signals[SIGNAL_WRITABLE_CHANGE_EVENT] =
|
|
g_signal_new ("writable-change-event", G_TYPE_SETTINGS,
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (GSettingsClass, writable_change_event),
|
|
g_signal_accumulator_true_handled, NULL,
|
|
_gio_marshal_BOOLEAN__UINT, G_TYPE_BOOLEAN, 1, G_TYPE_UINT);
|
|
|
|
/**
|
|
* GSettings:context:
|
|
*
|
|
* The name of the context that the settings are stored in.
|
|
*/
|
|
g_object_class_install_property (object_class, PROP_BACKEND,
|
|
g_param_spec_object ("backend",
|
|
P_("GSettingsBackend"),
|
|
P_("The GSettingsBackend for this settings object"),
|
|
G_TYPE_SETTINGS_BACKEND, G_PARAM_CONSTRUCT_ONLY |
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
/**
|
|
* GSettings:schema:
|
|
*
|
|
* The name of the schema that describes the types of keys
|
|
* for this #GSettings object.
|
|
*/
|
|
g_object_class_install_property (object_class, PROP_SCHEMA,
|
|
g_param_spec_string ("schema",
|
|
P_("Schema name"),
|
|
P_("The name of the schema for this settings object"),
|
|
NULL,
|
|
G_PARAM_CONSTRUCT_ONLY |
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
/**
|
|
* GSettings:path:
|
|
*
|
|
* The path within the backend where the settings are stored.
|
|
*/
|
|
g_object_class_install_property (object_class, PROP_PATH,
|
|
g_param_spec_string ("path",
|
|
P_("Base path"),
|
|
P_("The path within the backend where the settings are"),
|
|
NULL,
|
|
G_PARAM_CONSTRUCT_ONLY |
|
|
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
|
|
|
/**
|
|
* GSettings:has-unapplied:
|
|
*
|
|
* If this property is %TRUE, the #GSettings object has outstanding
|
|
* changes that will be applied when g_settings_apply() is called.
|
|
*/
|
|
g_object_class_install_property (object_class, PROP_HAS_UNAPPLIED,
|
|
g_param_spec_boolean ("has-unapplied",
|
|
P_("Has unapplied changes"),
|
|
P_("TRUE if there are outstanding changes to apply()"),
|
|
FALSE,
|
|
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
|
|
|
/**
|
|
* GSettings:delay-apply:
|
|
*
|
|
* Whether the #GSettings object is in 'delay-apply' mode. See
|
|
* g_settings_delay() for details.
|
|
*
|
|
* Since: 2.28
|
|
*/
|
|
g_object_class_install_property (object_class, PROP_DELAY_APPLY,
|
|
g_param_spec_boolean ("delay-apply",
|
|
P_("Delay-apply mode"),
|
|
P_("Whether this settings object is in 'delay-apply' mode"),
|
|
FALSE,
|
|
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
|
|
}
|
|
|
|
/* Construction (new, new_with_path, etc.) {{{1 */
|
|
/**
|
|
* g_settings_new:
|
|
* @schema: the name of the schema
|
|
* @returns: a new #GSettings object
|
|
*
|
|
* Creates a new #GSettings object with a given schema.
|
|
*
|
|
* Signals on the newly created #GSettings object will be dispatched
|
|
* via the thread-default #GMainContext in effect at the time of the
|
|
* call to g_settings_new(). The new #GSettings will hold a reference
|
|
* on the context. See g_main_context_push_thread_default().
|
|
*
|
|
* Since: 2.26
|
|
*/
|
|
GSettings *
|
|
g_settings_new (const gchar *schema)
|
|
{
|
|
g_return_val_if_fail (schema != NULL, NULL);
|
|
|
|
return g_object_new (G_TYPE_SETTINGS,
|
|
"schema", schema,
|
|
NULL);
|
|
}
|
|
|
|
/**
|
|
* g_settings_new_with_path:
|
|
* @schema: the name of the schema
|
|
* @path: the path to use
|
|
* @returns: a new #GSettings object
|
|
*
|
|
* Creates a new #GSettings object with a given schema and path.
|
|
*
|
|
* You only need to do this if you want to directly create a settings
|
|
* object with a schema that doesn't have a specified path of its own.
|
|
* That's quite rare.
|
|
*
|
|
* It is a programmer error to call this function for a schema that
|
|
* has an explicitly specified path.
|
|
*
|
|
* Since: 2.26
|
|
*/
|
|
GSettings *
|
|
g_settings_new_with_path (const gchar *schema,
|
|
const gchar *path)
|
|
{
|
|
g_return_val_if_fail (schema != NULL, NULL);
|
|
g_return_val_if_fail (path != NULL, NULL);
|
|
|
|
return g_object_new (G_TYPE_SETTINGS,
|
|
"schema", schema,
|
|
"path", path,
|
|
NULL);
|
|
}
|
|
|
|
/**
|
|
* g_settings_new_with_backend:
|
|
* @schema: the name of the schema
|
|
* @backend: the #GSettingsBackend to use
|
|
* @returns: a new #GSettings object
|
|
*
|
|
* Creates a new #GSettings object with a given schema and backend.
|
|
*
|
|
* Creating a #GSettings object with a different backend allows accessing
|
|
* settings from a database other than the usual one. For example, it may make
|
|
* sense to pass a backend corresponding to the "defaults" settings database on
|
|
* the system to get a settings object that modifies the system default
|
|
* settings instead of the settings for this user.
|
|
*
|
|
* Since: 2.26
|
|
*/
|
|
GSettings *
|
|
g_settings_new_with_backend (const gchar *schema,
|
|
GSettingsBackend *backend)
|
|
{
|
|
g_return_val_if_fail (schema != NULL, NULL);
|
|
g_return_val_if_fail (G_IS_SETTINGS_BACKEND (backend), NULL);
|
|
|
|
return g_object_new (G_TYPE_SETTINGS,
|
|
"schema", schema,
|
|
"backend", backend,
|
|
NULL);
|
|
}
|
|
|
|
/**
|
|
* g_settings_new_with_backend_and_path:
|
|
* @schema: the name of the schema
|
|
* @backend: the #GSettingsBackend to use
|
|
* @path: the path to use
|
|
* @returns: a new #GSettings object
|
|
*
|
|
* Creates a new #GSettings object with a given schema, backend and
|
|
* path.
|
|
*
|
|
* This is a mix of g_settings_new_with_backend() and
|
|
* g_settings_new_with_path().
|
|
*
|
|
* Since: 2.26
|
|
*/
|
|
GSettings *
|
|
g_settings_new_with_backend_and_path (const gchar *schema,
|
|
GSettingsBackend *backend,
|
|
const gchar *path)
|
|
{
|
|
g_return_val_if_fail (schema != NULL, NULL);
|
|
g_return_val_if_fail (G_IS_SETTINGS_BACKEND (backend), NULL);
|
|
g_return_val_if_fail (path != NULL, NULL);
|
|
|
|
return g_object_new (G_TYPE_SETTINGS,
|
|
"schema", schema,
|
|
"backend", backend,
|
|
"path", path,
|
|
NULL);
|
|
}
|
|
|
|
/* Internal read/write utilities, enum/flags conversion, validation {{{1 */
|
|
typedef struct
|
|
{
|
|
GSettings *settings;
|
|
const gchar *key;
|
|
|
|
GSettingsSchema *schema;
|
|
|
|
guint is_flags : 1;
|
|
guint is_enum : 1;
|
|
|
|
const guint32 *strinfo;
|
|
gsize strinfo_length;
|
|
|
|
const gchar *unparsed;
|
|
gchar lc_char;
|
|
|
|
const GVariantType *type;
|
|
GVariant *minimum, *maximum;
|
|
GVariant *default_value;
|
|
} GSettingsKeyInfo;
|
|
|
|
static inline void
|
|
endian_fixup (GVariant **value)
|
|
{
|
|
#if G_BYTE_ORDER == G_BIG_ENDIAN
|
|
GVariant *tmp;
|
|
|
|
tmp = g_variant_byteswap (*value);
|
|
g_variant_unref (*value);
|
|
*value = tmp;
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
g_settings_get_key_info (GSettingsKeyInfo *info,
|
|
GSettings *settings,
|
|
const gchar *key)
|
|
{
|
|
GVariantIter *iter;
|
|
GVariant *data;
|
|
guchar code;
|
|
|
|
memset (info, 0, sizeof *info);
|
|
|
|
iter = g_settings_schema_get_value (settings->priv->schema, key);
|
|
|
|
info->default_value = g_variant_iter_next_value (iter);
|
|
endian_fixup (&info->default_value);
|
|
info->type = g_variant_get_type (info->default_value);
|
|
info->settings = g_object_ref (settings);
|
|
info->key = g_intern_string (key);
|
|
|
|
while (g_variant_iter_next (iter, "(y*)", &code, &data))
|
|
{
|
|
switch (code)
|
|
{
|
|
case 'l':
|
|
/* translation requested */
|
|
g_variant_get (data, "(y&s)", &info->lc_char, &info->unparsed);
|
|
break;
|
|
|
|
case 'e':
|
|
/* enumerated types... */
|
|
info->is_enum = TRUE;
|
|
goto choice;
|
|
|
|
case 'f':
|
|
/* flags... */
|
|
info->is_flags = TRUE;
|
|
goto choice;
|
|
|
|
choice: case 'c':
|
|
/* ..., choices, aliases */
|
|
info->strinfo = g_variant_get_fixed_array (data,
|
|
&info->strinfo_length,
|
|
sizeof (guint32));
|
|
break;
|
|
|
|
case 'r':
|
|
g_variant_get (data, "(**)", &info->minimum, &info->maximum);
|
|
endian_fixup (&info->minimum);
|
|
endian_fixup (&info->maximum);
|
|
break;
|
|
|
|
default:
|
|
g_warning ("unknown schema extension '%c'", code);
|
|
break;
|
|
}
|
|
|
|
g_variant_unref (data);
|
|
}
|
|
|
|
g_variant_iter_free (iter);
|
|
}
|
|
|
|
static void
|
|
g_settings_free_key_info (GSettingsKeyInfo *info)
|
|
{
|
|
if (info->minimum)
|
|
g_variant_unref (info->minimum);
|
|
|
|
if (info->maximum)
|
|
g_variant_unref (info->maximum);
|
|
|
|
g_variant_unref (info->default_value);
|
|
g_object_unref (info->settings);
|
|
}
|
|
|
|
static gboolean
|
|
g_settings_write_to_backend (GSettingsKeyInfo *info,
|
|
GVariant *value)
|
|
{
|
|
gboolean success;
|
|
gchar *path;
|
|
|
|
path = g_strconcat (info->settings->priv->path, info->key, NULL);
|
|
success = g_settings_backend_write (info->settings->priv->backend,
|
|
path, value, NULL);
|
|
g_free (path);
|
|
|
|
return success;
|
|
}
|
|
|
|
static gboolean
|
|
g_settings_type_check (GSettingsKeyInfo *info,
|
|
GVariant *value)
|
|
{
|
|
g_return_val_if_fail (value != NULL, FALSE);
|
|
|
|
return g_variant_is_of_type (value, info->type);
|
|
}
|
|
|
|
static gboolean
|
|
g_settings_key_info_range_check (GSettingsKeyInfo *info,
|
|
GVariant *value)
|
|
{
|
|
if (info->minimum == NULL && info->strinfo == NULL)
|
|
return TRUE;
|
|
|
|
if (g_variant_is_container (value))
|
|
{
|
|
gboolean ok = TRUE;
|
|
GVariantIter iter;
|
|
GVariant *child;
|
|
|
|
g_variant_iter_init (&iter, value);
|
|
while (ok && (child = g_variant_iter_next_value (&iter)))
|
|
{
|
|
ok = g_settings_key_info_range_check (info, child);
|
|
g_variant_unref (child);
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
if (info->minimum)
|
|
{
|
|
return g_variant_compare (info->minimum, value) <= 0 &&
|
|
g_variant_compare (value, info->maximum) <= 0;
|
|
}
|
|
|
|
return strinfo_is_string_valid (info->strinfo,
|
|
info->strinfo_length,
|
|
g_variant_get_string (value, NULL));
|
|
}
|
|
|
|
static GVariant *
|
|
g_settings_range_fixup (GSettingsKeyInfo *info,
|
|
GVariant *value)
|
|
{
|
|
const gchar *target;
|
|
|
|
if (g_settings_key_info_range_check (info, value))
|
|
return g_variant_ref (value);
|
|
|
|
if (info->strinfo == NULL)
|
|
return NULL;
|
|
|
|
if (g_variant_is_container (value))
|
|
{
|
|
GVariantBuilder builder;
|
|
GVariantIter iter;
|
|
GVariant *child;
|
|
|
|
g_variant_iter_init (&iter, value);
|
|
g_variant_builder_init (&builder, g_variant_get_type (value));
|
|
|
|
while ((child = g_variant_iter_next_value (&iter)))
|
|
{
|
|
GVariant *fixed;
|
|
|
|
fixed = g_settings_range_fixup (info, child);
|
|
g_variant_unref (child);
|
|
|
|
if (fixed == NULL)
|
|
{
|
|
g_variant_builder_clear (&builder);
|
|
return NULL;
|
|
}
|
|
|
|
g_variant_builder_add_value (&builder, fixed);
|
|
g_variant_unref (fixed);
|
|
}
|
|
|
|
return g_variant_ref_sink (g_variant_builder_end (&builder));
|
|
}
|
|
|
|
target = strinfo_string_from_alias (info->strinfo, info->strinfo_length,
|
|
g_variant_get_string (value, NULL));
|
|
return target ? g_variant_ref_sink (g_variant_new_string (target)) : NULL;
|
|
}
|
|
|
|
static GVariant *
|
|
g_settings_read_from_backend (GSettingsKeyInfo *info)
|
|
{
|
|
GVariant *value;
|
|
GVariant *fixup;
|
|
gchar *path;
|
|
|
|
path = g_strconcat (info->settings->priv->path, info->key, NULL);
|
|
value = g_settings_backend_read (info->settings->priv->backend,
|
|
path, info->type, FALSE);
|
|
g_free (path);
|
|
|
|
if (value != NULL)
|
|
{
|
|
fixup = g_settings_range_fixup (info, value);
|
|
g_variant_unref (value);
|
|
}
|
|
else
|
|
fixup = NULL;
|
|
|
|
return fixup;
|
|
}
|
|
|
|
static GVariant *
|
|
g_settings_get_translated_default (GSettingsKeyInfo *info)
|
|
{
|
|
const gchar *translated;
|
|
GError *error = NULL;
|
|
const gchar *domain;
|
|
GVariant *value;
|
|
|
|
if (info->lc_char == '\0')
|
|
/* translation not requested for this key */
|
|
return NULL;
|
|
|
|
domain = g_settings_schema_get_gettext_domain (info->settings->priv->schema);
|
|
|
|
if (info->lc_char == 't')
|
|
translated = g_dcgettext (domain, info->unparsed, LC_TIME);
|
|
else
|
|
translated = g_dgettext (domain, info->unparsed);
|
|
|
|
if (translated == info->unparsed)
|
|
/* the default value was not translated */
|
|
return NULL;
|
|
|
|
/* try to parse the translation of the unparsed default */
|
|
value = g_variant_parse (info->type, translated, NULL, NULL, &error);
|
|
|
|
if (value == NULL)
|
|
{
|
|
g_warning ("Failed to parse translated string `%s' for "
|
|
"key `%s' in schema `%s': %s", info->unparsed, info->key,
|
|
info->settings->priv->schema_name, error->message);
|
|
g_warning ("Using untranslated default instead.");
|
|
g_error_free (error);
|
|
}
|
|
|
|
else if (!g_settings_key_info_range_check (info, value))
|
|
{
|
|
g_warning ("Translated default `%s' for key `%s' in schema `%s' "
|
|
"is outside of valid range", info->unparsed, info->key,
|
|
info->settings->priv->schema_name);
|
|
g_variant_unref (value);
|
|
value = NULL;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
static gint
|
|
g_settings_to_enum (GSettingsKeyInfo *info,
|
|
GVariant *value)
|
|
{
|
|
gboolean it_worked;
|
|
guint result;
|
|
|
|
it_worked = strinfo_enum_from_string (info->strinfo, info->strinfo_length,
|
|
g_variant_get_string (value, NULL),
|
|
&result);
|
|
|
|
/* 'value' can only come from the backend after being filtered for validity,
|
|
* from the translation after being filtered for validity, or from the schema
|
|
* itself (which the schema compiler checks for validity). If this assertion
|
|
* fails then it's really a bug in GSettings or the schema compiler...
|
|
*/
|
|
g_assert (it_worked);
|
|
|
|
return result;
|
|
}
|
|
|
|
static GVariant *
|
|
g_settings_from_enum (GSettingsKeyInfo *info,
|
|
gint value)
|
|
{
|
|
const gchar *string;
|
|
|
|
string = strinfo_string_from_enum (info->strinfo,
|
|
info->strinfo_length,
|
|
value);
|
|
|
|
if (string == NULL)
|
|
return NULL;
|
|
|
|
return g_variant_new_string (string);
|
|
}
|
|
|
|
static guint
|
|
g_settings_to_flags (GSettingsKeyInfo *info,
|
|
GVariant *value)
|
|
{
|
|
GVariantIter iter;
|
|
const gchar *flag;
|
|
guint result;
|
|
|
|
result = 0;
|
|
g_variant_iter_init (&iter, value);
|
|
while (g_variant_iter_next (&iter, "&s", &flag))
|
|
{
|
|
gboolean it_worked;
|
|
guint flag_value;
|
|
|
|
it_worked = strinfo_enum_from_string (info->strinfo,
|
|
info->strinfo_length,
|
|
flag, &flag_value);
|
|
/* as in g_settings_to_enum() */
|
|
g_assert (it_worked);
|
|
|
|
result |= flag_value;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static GVariant *
|
|
g_settings_from_flags (GSettingsKeyInfo *info,
|
|
guint value)
|
|
{
|
|
GVariantBuilder builder;
|
|
gint i;
|
|
|
|
g_variant_builder_init (&builder, G_VARIANT_TYPE ("as"));
|
|
|
|
for (i = 0; i < 32; i++)
|
|
if (value & (1u << i))
|
|
{
|
|
const gchar *string;
|
|
|
|
string = strinfo_string_from_enum (info->strinfo,
|
|
info->strinfo_length,
|
|
1u << i);
|
|
|
|
if (string == NULL)
|
|
{
|
|
g_variant_builder_clear (&builder);
|
|
return NULL;
|
|
}
|
|
|
|
g_variant_builder_add (&builder, "s", string);
|
|
}
|
|
|
|
return g_variant_builder_end (&builder);
|
|
}
|
|
|
|
/* Public Get/Set API {{{1 (get, get_value, set, set_value, get_mapped) */
|
|
/**
|
|
* g_settings_get_value:
|
|
* @settings: a #GSettings object
|
|
* @key: the key to get the value for
|
|
* @returns: a new #GVariant
|
|
*
|
|
* Gets the value that is stored in @settings for @key.
|
|
*
|
|
* It is a programmer error to give a @key that isn't contained in the
|
|
* schema for @settings.
|
|
*
|
|
* Since: 2.26
|
|
*/
|
|
GVariant *
|
|
g_settings_get_value (GSettings *settings,
|
|
const gchar *key)
|
|
{
|
|
GSettingsKeyInfo info;
|
|
GVariant *value;
|
|
|
|
g_return_val_if_fail (G_IS_SETTINGS (settings), NULL);
|
|
g_return_val_if_fail (key != NULL, NULL);
|
|
|
|
g_settings_get_key_info (&info, settings, key);
|
|
value = g_settings_read_from_backend (&info);
|
|
|
|
if (value == NULL)
|
|
value = g_settings_get_translated_default (&info);
|
|
|
|
if (value == NULL)
|
|
value = g_variant_ref (info.default_value);
|
|
|
|
g_settings_free_key_info (&info);
|
|
|
|
return value;
|
|
}
|
|
|
|
/**
|
|
* g_settings_get_enum:
|
|
* @settings: a #GSettings object
|
|
* @key: the key to get the value for
|
|
* @returns: the enum value
|
|
*
|
|
* Gets the value that is stored in @settings for @key and converts it
|
|
* to the enum value that it represents.
|
|
*
|
|
* In order to use this function the type of the value must be a string
|
|
* and it must be marked in the schema file as an enumerated type.
|
|
*
|
|
* It is a programmer error to give a @key that isn't contained in the
|
|
* schema for @settings or is not marked as an enumerated type.
|
|
*
|
|
* If the value stored in the configuration database is not a valid
|
|
* value for the enumerated type then this function will return the
|
|
* default value.
|
|
*
|
|
* Since: 2.26
|
|
**/
|
|
gint
|
|
g_settings_get_enum (GSettings *settings,
|
|
const gchar *key)
|
|
{
|
|
GSettingsKeyInfo info;
|
|
GVariant *value;
|
|
gint result;
|
|
|
|
g_return_val_if_fail (G_IS_SETTINGS (settings), -1);
|
|
g_return_val_if_fail (key != NULL, -1);
|
|
|
|
g_settings_get_key_info (&info, settings, key);
|
|
|
|
if (!info.is_enum)
|
|
{
|
|
g_critical ("g_settings_get_enum() called on key `%s' which is not "
|
|
"associated with an enumerated type", info.key);
|
|
g_settings_free_key_info (&info);
|
|
return -1;
|
|
}
|
|
|
|
value = g_settings_read_from_backend (&info);
|
|
|
|
if (value == NULL)
|
|
value = g_settings_get_translated_default (&info);
|
|
|
|
if (value == NULL)
|
|
value = g_variant_ref (info.default_value);
|
|
|
|
result = g_settings_to_enum (&info, value);
|
|
g_settings_free_key_info (&info);
|
|
g_variant_unref (value);
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* g_settings_set_enum:
|
|
* @settings: a #GSettings object
|
|
* @key: a key, within @settings
|
|
* @value: an enumerated value
|
|
* @returns: %TRUE, if the set succeeds
|
|
*
|
|
* Looks up the enumerated type nick for @value and writes it to @key,
|
|
* within @settings.
|
|
*
|
|
* It is a programmer error to give a @key that isn't contained in the
|
|
* schema for @settings or is not marked as an enumerated type, or for
|
|
* @value not to be a valid value for the named type.
|
|
*
|
|
* After performing the write, accessing @key directly with
|
|
* g_settings_get_string() will return the 'nick' associated with
|
|
* @value.
|
|
**/
|
|
gboolean
|
|
g_settings_set_enum (GSettings *settings,
|
|
const gchar *key,
|
|
gint value)
|
|
{
|
|
GSettingsKeyInfo info;
|
|
GVariant *variant;
|
|
gboolean success;
|
|
|
|
g_return_val_if_fail (G_IS_SETTINGS (settings), FALSE);
|
|
g_return_val_if_fail (key != NULL, FALSE);
|
|
|
|
g_settings_get_key_info (&info, settings, key);
|
|
|
|
if (!info.is_enum)
|
|
{
|
|
g_critical ("g_settings_set_enum() called on key `%s' which is not "
|
|
"associated with an enumerated type", info.key);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!(variant = g_settings_from_enum (&info, value)))
|
|
{
|
|
g_critical ("g_settings_set_enum(): invalid enum value %d for key `%s' "
|
|
"in schema `%s'. Doing nothing.", value, info.key,
|
|
info.settings->priv->schema_name);
|
|
g_settings_free_key_info (&info);
|
|
return FALSE;
|
|
}
|
|
|
|
success = g_settings_write_to_backend (&info, variant);
|
|
g_settings_free_key_info (&info);
|
|
|
|
return success;
|
|
}
|
|
|
|
/**
|
|
* g_settings_get_flags:
|
|
* @settings: a #GSettings object
|
|
* @key: the key to get the value for
|
|
* @returns: the flags value
|
|
*
|
|
* Gets the value that is stored in @settings for @key and converts it
|
|
* to the flags value that it represents.
|
|
*
|
|
* In order to use this function the type of the value must be an array
|
|
* of strings and it must be marked in the schema file as an flags type.
|
|
*
|
|
* It is a programmer error to give a @key that isn't contained in the
|
|
* schema for @settings or is not marked as a flags type.
|
|
*
|
|
* If the value stored in the configuration database is not a valid
|
|
* value for the flags type then this function will return the default
|
|
* value.
|
|
*
|
|
* Since: 2.26
|
|
**/
|
|
guint
|
|
g_settings_get_flags (GSettings *settings,
|
|
const gchar *key)
|
|
{
|
|
GSettingsKeyInfo info;
|
|
GVariant *value;
|
|
guint result;
|
|
|
|
g_return_val_if_fail (G_IS_SETTINGS (settings), -1);
|
|
g_return_val_if_fail (key != NULL, -1);
|
|
|
|
g_settings_get_key_info (&info, settings, key);
|
|
|
|
if (!info.is_flags)
|
|
{
|
|
g_critical ("g_settings_get_flags() called on key `%s' which is not "
|
|
"associated with a flags type", info.key);
|
|
g_settings_free_key_info (&info);
|
|
return -1;
|
|
}
|
|
|
|
value = g_settings_read_from_backend (&info);
|
|
|
|
if (value == NULL)
|
|
value = g_settings_get_translated_default (&info);
|
|
|
|
if (value == NULL)
|
|
value = g_variant_ref (info.default_value);
|
|
|
|
result = g_settings_to_flags (&info, value);
|
|
g_settings_free_key_info (&info);
|
|
g_variant_unref (value);
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* g_settings_set_flags:
|
|
* @settings: a #GSettings object
|
|
* @key: a key, within @settings
|
|
* @value: a flags value
|
|
* @returns: %TRUE, if the set succeeds
|
|
*
|
|
* Looks up the flags type nicks for the bits specified by @value, puts
|
|
* them in an array of strings and writes the array to @key, withing
|
|
* @settings.
|
|
*
|
|
* It is a programmer error to give a @key that isn't contained in the
|
|
* schema for @settings or is not marked as a flags type, or for @value
|
|
* to contain any bits that are not value for the named type.
|
|
*
|
|
* After performing the write, accessing @key directly with
|
|
* g_settings_get_strv() will return an array of 'nicks'; one for each
|
|
* bit in @value.
|
|
**/
|
|
gboolean
|
|
g_settings_set_flags (GSettings *settings,
|
|
const gchar *key,
|
|
guint value)
|
|
{
|
|
GSettingsKeyInfo info;
|
|
GVariant *variant;
|
|
gboolean success;
|
|
|
|
g_return_val_if_fail (G_IS_SETTINGS (settings), FALSE);
|
|
g_return_val_if_fail (key != NULL, FALSE);
|
|
|
|
g_settings_get_key_info (&info, settings, key);
|
|
|
|
if (!info.is_flags)
|
|
{
|
|
g_critical ("g_settings_set_flags() called on key `%s' which is not "
|
|
"associated with a flags type", info.key);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!(variant = g_settings_from_flags (&info, value)))
|
|
{
|
|
g_critical ("g_settings_set_flags(): invalid flags value 0x%08x "
|
|
"for key `%s' in schema `%s'. Doing nothing.",
|
|
value, info.key, info.settings->priv->schema_name);
|
|
g_settings_free_key_info (&info);
|
|
return FALSE;
|
|
}
|
|
|
|
success = g_settings_write_to_backend (&info, variant);
|
|
g_settings_free_key_info (&info);
|
|
|
|
return success;
|
|
}
|
|
|
|
/**
|
|
* g_settings_set_value:
|
|
* @settings: a #GSettings object
|
|
* @key: the name of the key to set
|
|
* @value: a #GVariant of the correct type
|
|
* @returns: %TRUE if setting the key succeeded,
|
|
* %FALSE if the key was not writable
|
|
*
|
|
* Sets @key in @settings to @value.
|
|
*
|
|
* It is a programmer error to give a @key that isn't contained in the
|
|
* schema for @settings or for @value to have the incorrect type, per
|
|
* the schema.
|
|
*
|
|
* If @value is floating then this function consumes the reference.
|
|
*
|
|
* Since: 2.26
|
|
**/
|
|
gboolean
|
|
g_settings_set_value (GSettings *settings,
|
|
const gchar *key,
|
|
GVariant *value)
|
|
{
|
|
GSettingsKeyInfo info;
|
|
|
|
g_return_val_if_fail (G_IS_SETTINGS (settings), FALSE);
|
|
g_return_val_if_fail (key != NULL, FALSE);
|
|
|
|
g_settings_get_key_info (&info, settings, key);
|
|
|
|
if (!g_settings_type_check (&info, value))
|
|
{
|
|
g_critical ("g_settings_set_value: key '%s' in '%s' expects type '%s', but a GVariant of type '%s' was given",
|
|
key,
|
|
settings->priv->schema_name,
|
|
g_variant_type_peek_string (info.type),
|
|
g_variant_get_type_string (value));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
if (!g_settings_key_info_range_check (&info, value))
|
|
{
|
|
g_warning ("g_settings_set_value: value for key '%s' in schema '%s' "
|
|
"is outside of valid range",
|
|
key,
|
|
settings->priv->schema_name);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
g_settings_free_key_info (&info);
|
|
|
|
return g_settings_write_to_backend (&info, value);
|
|
}
|
|
|
|
/**
|
|
* g_settings_get:
|
|
* @settings: a #GSettings object
|
|
* @key: the key to get the value for
|
|
* @format: a #GVariant format string
|
|
* @...: arguments as per @format
|
|
*
|
|
* Gets the value that is stored at @key in @settings.
|
|
*
|
|
* A convenience function that combines g_settings_get_value() with
|
|
* g_variant_get().
|
|
*
|
|
* It is a programmer error to give a @key that isn't contained in the
|
|
* schema for @settings or for the #GVariantType of @format to mismatch
|
|
* the type given in the schema.
|
|
*
|
|
* Since: 2.26
|
|
*/
|
|
void
|
|
g_settings_get (GSettings *settings,
|
|
const gchar *key,
|
|
const gchar *format,
|
|
...)
|
|
{
|
|
GVariant *value;
|
|
va_list ap;
|
|
|
|
value = g_settings_get_value (settings, key);
|
|
|
|
va_start (ap, format);
|
|
g_variant_get_va (value, format, NULL, &ap);
|
|
va_end (ap);
|
|
|
|
g_variant_unref (value);
|
|
}
|
|
|
|
/**
|
|
* g_settings_set:
|
|
* @settings: a #GSettings object
|
|
* @key: the name of the key to set
|
|
* @format: a #GVariant format string
|
|
* @...: arguments as per @format
|
|
* @returns: %TRUE if setting the key succeeded,
|
|
* %FALSE if the key was not writable
|
|
*
|
|
* Sets @key in @settings to @value.
|
|
*
|
|
* A convenience function that combines g_settings_set_value() with
|
|
* g_variant_new().
|
|
*
|
|
* It is a programmer error to give a @key that isn't contained in the
|
|
* schema for @settings or for the #GVariantType of @format to mismatch
|
|
* the type given in the schema.
|
|
*
|
|
* Since: 2.26
|
|
*/
|
|
gboolean
|
|
g_settings_set (GSettings *settings,
|
|
const gchar *key,
|
|
const gchar *format,
|
|
...)
|
|
{
|
|
GVariant *value;
|
|
va_list ap;
|
|
|
|
va_start (ap, format);
|
|
value = g_variant_new_va (format, NULL, &ap);
|
|
va_end (ap);
|
|
|
|
return g_settings_set_value (settings, key, value);
|
|
}
|
|
|
|
/**
|
|
* g_settings_get_mapped:
|
|
* @settings: a #GSettings object
|
|
* @key: the key to get the value for
|
|
* @mapping: (scope call): the function to map the value in the
|
|
* settings database to the value used by the application
|
|
* @user_data: user data for @mapping
|
|
* @returns: (transfer full): the result, which may be %NULL
|
|
*
|
|
* Gets the value that is stored at @key in @settings, subject to
|
|
* application-level validation/mapping.
|
|
*
|
|
* You should use this function when the application needs to perform
|
|
* some processing on the value of the key (for example, parsing). The
|
|
* @mapping function performs that processing. If the function
|
|
* indicates that the processing was unsuccessful (due to a parse error,
|
|
* for example) then the mapping is tried again with another value.
|
|
|
|
* This allows a robust 'fall back to defaults' behaviour to be
|
|
* implemented somewhat automatically.
|
|
*
|
|
* The first value that is tried is the user's setting for the key. If
|
|
* the mapping function fails to map this value, other values may be
|
|
* tried in an unspecified order (system or site defaults, translated
|
|
* schema default values, untranslated schema default values, etc).
|
|
*
|
|
* If the mapping function fails for all possible values, one additional
|
|
* attempt is made: the mapping function is called with a %NULL value.
|
|
* If the mapping function still indicates failure at this point then
|
|
* the application will be aborted.
|
|
*
|
|
* The result parameter for the @mapping function is pointed to a
|
|
* #gpointer which is initially set to %NULL. The same pointer is given
|
|
* to each invocation of @mapping. The final value of that #gpointer is
|
|
* what is returned by this function. %NULL is valid; it is returned
|
|
* just as any other value would be.
|
|
**/
|
|
gpointer
|
|
g_settings_get_mapped (GSettings *settings,
|
|
const gchar *key,
|
|
GSettingsGetMapping mapping,
|
|
gpointer user_data)
|
|
{
|
|
gpointer result = NULL;
|
|
GSettingsKeyInfo info;
|
|
GVariant *value;
|
|
gboolean okay;
|
|
|
|
g_return_val_if_fail (G_IS_SETTINGS (settings), NULL);
|
|
g_return_val_if_fail (key != NULL, NULL);
|
|
g_return_val_if_fail (mapping != NULL, NULL);
|
|
|
|
g_settings_get_key_info (&info, settings, key);
|
|
|
|
if ((value = g_settings_read_from_backend (&info)))
|
|
{
|
|
okay = mapping (value, &result, user_data);
|
|
g_variant_unref (value);
|
|
if (okay) goto okay;
|
|
}
|
|
|
|
if ((value = g_settings_get_translated_default (&info)))
|
|
{
|
|
okay = mapping (value, &result, user_data);
|
|
g_variant_unref (value);
|
|
if (okay) goto okay;
|
|
}
|
|
|
|
if (mapping (info.default_value, &result, user_data))
|
|
goto okay;
|
|
|
|
if (!mapping (NULL, &result, user_data))
|
|
g_error ("The mapping function given to g_settings_get_mapped() for key "
|
|
"`%s' in schema `%s' returned FALSE when given a NULL value.",
|
|
key, settings->priv->schema_name);
|
|
|
|
okay:
|
|
g_settings_free_key_info (&info);
|
|
|
|
return result;
|
|
}
|
|
|
|
/* Convenience API (get, set_string, int, double, boolean, strv) {{{1 */
|
|
/**
|
|
* g_settings_get_string:
|
|
* @settings: a #GSettings object
|
|
* @key: the key to get the value for
|
|
* @returns: a newly-allocated string
|
|
*
|
|
* Gets the value that is stored at @key in @settings.
|
|
*
|
|
* A convenience variant of g_settings_get() for strings.
|
|
*
|
|
* It is a programmer error to give a @key that isn't specified as
|
|
* having a string type in the schema for @settings.
|
|
*
|
|
* Since: 2.26
|
|
*/
|
|
gchar *
|
|
g_settings_get_string (GSettings *settings,
|
|
const gchar *key)
|
|
{
|
|
GVariant *value;
|
|
gchar *result;
|
|
|
|
value = g_settings_get_value (settings, key);
|
|
result = g_variant_dup_string (value, NULL);
|
|
g_variant_unref (value);
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* g_settings_set_string:
|
|
* @settings: a #GSettings object
|
|
* @key: the name of the key to set
|
|
* @value: the value to set it to
|
|
* @returns: %TRUE if setting the key succeeded,
|
|
* %FALSE if the key was not writable
|
|
*
|
|
* Sets @key in @settings to @value.
|
|
*
|
|
* A convenience variant of g_settings_set() for strings.
|
|
*
|
|
* It is a programmer error to give a @key that isn't specified as
|
|
* having a string type in the schema for @settings.
|
|
*
|
|
* Since: 2.26
|
|
*/
|
|
gboolean
|
|
g_settings_set_string (GSettings *settings,
|
|
const gchar *key,
|
|
const gchar *value)
|
|
{
|
|
return g_settings_set_value (settings, key, g_variant_new_string (value));
|
|
}
|
|
|
|
/**
|
|
* g_settings_get_int:
|
|
* @settings: a #GSettings object
|
|
* @key: the key to get the value for
|
|
* @returns: an integer
|
|
*
|
|
* Gets the value that is stored at @key in @settings.
|
|
*
|
|
* A convenience variant of g_settings_get() for 32-bit integers.
|
|
*
|
|
* It is a programmer error to give a @key that isn't specified as
|
|
* having a int32 type in the schema for @settings.
|
|
*
|
|
* Since: 2.26
|
|
*/
|
|
gint
|
|
g_settings_get_int (GSettings *settings,
|
|
const gchar *key)
|
|
{
|
|
GVariant *value;
|
|
gint result;
|
|
|
|
value = g_settings_get_value (settings, key);
|
|
result = g_variant_get_int32 (value);
|
|
g_variant_unref (value);
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* g_settings_set_int:
|
|
* @settings: a #GSettings object
|
|
* @key: the name of the key to set
|
|
* @value: the value to set it to
|
|
* @returns: %TRUE if setting the key succeeded,
|
|
* %FALSE if the key was not writable
|
|
*
|
|
* Sets @key in @settings to @value.
|
|
*
|
|
* A convenience variant of g_settings_set() for 32-bit integers.
|
|
*
|
|
* It is a programmer error to give a @key that isn't specified as
|
|
* having a int32 type in the schema for @settings.
|
|
*
|
|
* Since: 2.26
|
|
*/
|
|
gboolean
|
|
g_settings_set_int (GSettings *settings,
|
|
const gchar *key,
|
|
gint value)
|
|
{
|
|
return g_settings_set_value (settings, key, g_variant_new_int32 (value));
|
|
}
|
|
|
|
/**
|
|
* g_settings_get_uint:
|
|
* @settings: a #GSettings object
|
|
* @key: the key to get the value for
|
|
* @returns: an unsigned integer
|
|
*
|
|
* Gets the value that is stored at @key in @settings.
|
|
*
|
|
* A convenience variant of g_settings_get() for 32-bit unsigned
|
|
* integers.
|
|
*
|
|
* It is a programmer error to give a @key that isn't specified as
|
|
* having a uint32 type in the schema for @settings.
|
|
*
|
|
* Since: 2.30
|
|
*/
|
|
guint
|
|
g_settings_get_uint (GSettings *settings,
|
|
const gchar *key)
|
|
{
|
|
GVariant *value;
|
|
guint result;
|
|
|
|
value = g_settings_get_value (settings, key);
|
|
result = g_variant_get_uint32 (value);
|
|
g_variant_unref (value);
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* g_settings_set_uint:
|
|
* @settings: a #GSettings object
|
|
* @key: the name of the key to set
|
|
* @value: the value to set it to
|
|
* @returns: %TRUE if setting the key succeeded,
|
|
* %FALSE if the key was not writable
|
|
*
|
|
* Sets @key in @settings to @value.
|
|
*
|
|
* A convenience variant of g_settings_set() for 32-bit unsigned
|
|
* integers.
|
|
*
|
|
* It is a programmer error to give a @key that isn't specified as
|
|
* having a uint32 type in the schema for @settings.
|
|
*
|
|
* Since: 2.30
|
|
*/
|
|
gboolean
|
|
g_settings_set_uint (GSettings *settings,
|
|
const gchar *key,
|
|
guint value)
|
|
{
|
|
return g_settings_set_value (settings, key, g_variant_new_uint32 (value));
|
|
}
|
|
|
|
/**
|
|
* g_settings_get_double:
|
|
* @settings: a #GSettings object
|
|
* @key: the key to get the value for
|
|
* @returns: a double
|
|
*
|
|
* Gets the value that is stored at @key in @settings.
|
|
*
|
|
* A convenience variant of g_settings_get() for doubles.
|
|
*
|
|
* It is a programmer error to give a @key that isn't specified as
|
|
* having a 'double' type in the schema for @settings.
|
|
*
|
|
* Since: 2.26
|
|
*/
|
|
gdouble
|
|
g_settings_get_double (GSettings *settings,
|
|
const gchar *key)
|
|
{
|
|
GVariant *value;
|
|
gdouble result;
|
|
|
|
value = g_settings_get_value (settings, key);
|
|
result = g_variant_get_double (value);
|
|
g_variant_unref (value);
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* g_settings_set_double:
|
|
* @settings: a #GSettings object
|
|
* @key: the name of the key to set
|
|
* @value: the value to set it to
|
|
* @returns: %TRUE if setting the key succeeded,
|
|
* %FALSE if the key was not writable
|
|
*
|
|
* Sets @key in @settings to @value.
|
|
*
|
|
* A convenience variant of g_settings_set() for doubles.
|
|
*
|
|
* It is a programmer error to give a @key that isn't specified as
|
|
* having a 'double' type in the schema for @settings.
|
|
*
|
|
* Since: 2.26
|
|
*/
|
|
gboolean
|
|
g_settings_set_double (GSettings *settings,
|
|
const gchar *key,
|
|
gdouble value)
|
|
{
|
|
return g_settings_set_value (settings, key, g_variant_new_double (value));
|
|
}
|
|
|
|
/**
|
|
* g_settings_get_boolean:
|
|
* @settings: a #GSettings object
|
|
* @key: the key to get the value for
|
|
* @returns: a boolean
|
|
*
|
|
* Gets the value that is stored at @key in @settings.
|
|
*
|
|
* A convenience variant of g_settings_get() for booleans.
|
|
*
|
|
* It is a programmer error to give a @key that isn't specified as
|
|
* having a boolean type in the schema for @settings.
|
|
*
|
|
* Since: 2.26
|
|
*/
|
|
gboolean
|
|
g_settings_get_boolean (GSettings *settings,
|
|
const gchar *key)
|
|
{
|
|
GVariant *value;
|
|
gboolean result;
|
|
|
|
value = g_settings_get_value (settings, key);
|
|
result = g_variant_get_boolean (value);
|
|
g_variant_unref (value);
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* g_settings_set_boolean:
|
|
* @settings: a #GSettings object
|
|
* @key: the name of the key to set
|
|
* @value: the value to set it to
|
|
* @returns: %TRUE if setting the key succeeded,
|
|
* %FALSE if the key was not writable
|
|
*
|
|
* Sets @key in @settings to @value.
|
|
*
|
|
* A convenience variant of g_settings_set() for booleans.
|
|
*
|
|
* It is a programmer error to give a @key that isn't specified as
|
|
* having a boolean type in the schema for @settings.
|
|
*
|
|
* Since: 2.26
|
|
*/
|
|
gboolean
|
|
g_settings_set_boolean (GSettings *settings,
|
|
const gchar *key,
|
|
gboolean value)
|
|
{
|
|
return g_settings_set_value (settings, key, g_variant_new_boolean (value));
|
|
}
|
|
|
|
/**
|
|
* g_settings_get_strv:
|
|
* @settings: a #GSettings object
|
|
* @key: the key to get the value for
|
|
* @returns: (array zero-terminated=1) (transfer full): a
|
|
* newly-allocated, %NULL-terminated array of strings, the value that
|
|
* is stored at @key in @settings.
|
|
*
|
|
* A convenience variant of g_settings_get() for string arrays.
|
|
*
|
|
* It is a programmer error to give a @key that isn't specified as
|
|
* having an array of strings type in the schema for @settings.
|
|
*
|
|
* Since: 2.26
|
|
*/
|
|
gchar **
|
|
g_settings_get_strv (GSettings *settings,
|
|
const gchar *key)
|
|
{
|
|
GVariant *value;
|
|
gchar **result;
|
|
|
|
value = g_settings_get_value (settings, key);
|
|
result = g_variant_dup_strv (value, NULL);
|
|
g_variant_unref (value);
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* g_settings_set_strv:
|
|
* @settings: a #GSettings object
|
|
* @key: the name of the key to set
|
|
* @value: (allow-none) (array zero-terminated=1): the value to set it to, or %NULL
|
|
* @returns: %TRUE if setting the key succeeded,
|
|
* %FALSE if the key was not writable
|
|
*
|
|
* Sets @key in @settings to @value.
|
|
*
|
|
* A convenience variant of g_settings_set() for string arrays. If
|
|
* @value is %NULL, then @key is set to be the empty array.
|
|
*
|
|
* It is a programmer error to give a @key that isn't specified as
|
|
* having an array of strings type in the schema for @settings.
|
|
*
|
|
* Since: 2.26
|
|
*/
|
|
gboolean
|
|
g_settings_set_strv (GSettings *settings,
|
|
const gchar *key,
|
|
const gchar * const *value)
|
|
{
|
|
GVariant *array;
|
|
|
|
if (value != NULL)
|
|
array = g_variant_new_strv (value, -1);
|
|
else
|
|
array = g_variant_new_strv (NULL, 0);
|
|
|
|
return g_settings_set_value (settings, key, array);
|
|
}
|
|
|
|
/* Delayed apply (delay, apply, revert, get_has_unapplied) {{{1 */
|
|
/**
|
|
* g_settings_delay:
|
|
* @settings: a #GSettings object
|
|
*
|
|
* Changes the #GSettings object into 'delay-apply' mode. In this
|
|
* mode, changes to @settings are not immediately propagated to the
|
|
* backend, but kept locally until g_settings_apply() is called.
|
|
*
|
|
* Since: 2.26
|
|
*/
|
|
void
|
|
g_settings_delay (GSettings *settings)
|
|
{
|
|
g_return_if_fail (G_IS_SETTINGS (settings));
|
|
|
|
if (settings->priv->delayed)
|
|
return;
|
|
|
|
settings->priv->delayed =
|
|
g_delayed_settings_backend_new (settings->priv->backend,
|
|
settings,
|
|
settings->priv->main_context);
|
|
g_settings_backend_unwatch (settings->priv->backend, G_OBJECT (settings));
|
|
g_object_unref (settings->priv->backend);
|
|
|
|
settings->priv->backend = G_SETTINGS_BACKEND (settings->priv->delayed);
|
|
g_settings_backend_watch (settings->priv->backend,
|
|
&listener_vtable, G_OBJECT (settings),
|
|
settings->priv->main_context);
|
|
|
|
g_object_notify (G_OBJECT (settings), "delay-apply");
|
|
}
|
|
|
|
/**
|
|
* g_settings_apply:
|
|
* @settings: a #GSettings instance
|
|
*
|
|
* Applies any changes that have been made to the settings. This
|
|
* function does nothing unless @settings is in 'delay-apply' mode;
|
|
* see g_settings_delay(). In the normal case settings are always
|
|
* applied immediately.
|
|
**/
|
|
void
|
|
g_settings_apply (GSettings *settings)
|
|
{
|
|
if (settings->priv->delayed)
|
|
{
|
|
GDelayedSettingsBackend *delayed;
|
|
|
|
delayed = G_DELAYED_SETTINGS_BACKEND (settings->priv->backend);
|
|
g_delayed_settings_backend_apply (delayed);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* g_settings_revert:
|
|
* @settings: a #GSettings instance
|
|
*
|
|
* Reverts all non-applied changes to the settings. This function
|
|
* does nothing unless @settings is in 'delay-apply' mode; see
|
|
* g_settings_delay(). In the normal case settings are always applied
|
|
* immediately.
|
|
*
|
|
* Change notifications will be emitted for affected keys.
|
|
**/
|
|
void
|
|
g_settings_revert (GSettings *settings)
|
|
{
|
|
if (settings->priv->delayed)
|
|
{
|
|
GDelayedSettingsBackend *delayed;
|
|
|
|
delayed = G_DELAYED_SETTINGS_BACKEND (settings->priv->backend);
|
|
g_delayed_settings_backend_revert (delayed);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* g_settings_get_has_unapplied:
|
|
* @settings: a #GSettings object
|
|
* @returns: %TRUE if @settings has unapplied changes
|
|
*
|
|
* Returns whether the #GSettings object has any unapplied
|
|
* changes. This can only be the case if it is in 'delayed-apply' mode.
|
|
*
|
|
* Since: 2.26
|
|
*/
|
|
gboolean
|
|
g_settings_get_has_unapplied (GSettings *settings)
|
|
{
|
|
g_return_val_if_fail (G_IS_SETTINGS (settings), FALSE);
|
|
|
|
return settings->priv->delayed &&
|
|
g_delayed_settings_backend_get_has_unapplied (
|
|
G_DELAYED_SETTINGS_BACKEND (settings->priv->backend));
|
|
}
|
|
|
|
/* Extra API (reset, sync, get_child, is_writable, list_*, ranges) {{{1 */
|
|
/**
|
|
* g_settings_reset:
|
|
* @settings: a #GSettings object
|
|
* @key: the name of a key
|
|
*
|
|
* Resets @key to its default value.
|
|
*
|
|
* This call resets the key, as much as possible, to its default value.
|
|
* That might the value specified in the schema or the one set by the
|
|
* administrator.
|
|
**/
|
|
void
|
|
g_settings_reset (GSettings *settings,
|
|
const gchar *key)
|
|
{
|
|
gchar *path;
|
|
|
|
path = g_strconcat (settings->priv->path, key, NULL);
|
|
g_settings_backend_reset (settings->priv->backend, path, NULL);
|
|
g_free (path);
|
|
}
|
|
|
|
/**
|
|
* g_settings_sync:
|
|
*
|
|
* Ensures that all pending operations for the given are complete for
|
|
* the default backend.
|
|
*
|
|
* Writes made to a #GSettings are handled asynchronously. For this
|
|
* reason, it is very unlikely that the changes have it to disk by the
|
|
* time g_settings_set() returns.
|
|
*
|
|
* This call will block until all of the writes have made it to the
|
|
* backend. Since the mainloop is not running, no change notifications
|
|
* will be dispatched during this call (but some may be queued by the
|
|
* time the call is done).
|
|
**/
|
|
void
|
|
g_settings_sync (void)
|
|
{
|
|
g_settings_backend_sync_default ();
|
|
}
|
|
|
|
/**
|
|
* g_settings_is_writable:
|
|
* @settings: a #GSettings object
|
|
* @name: the name of a key
|
|
* @returns: %TRUE if the key @name is writable
|
|
*
|
|
* Finds out if a key can be written or not
|
|
*
|
|
* Since: 2.26
|
|
*/
|
|
gboolean
|
|
g_settings_is_writable (GSettings *settings,
|
|
const gchar *name)
|
|
{
|
|
gboolean writable;
|
|
gchar *path;
|
|
|
|
g_return_val_if_fail (G_IS_SETTINGS (settings), FALSE);
|
|
|
|
path = g_strconcat (settings->priv->path, name, NULL);
|
|
writable = g_settings_backend_get_writable (settings->priv->backend, path);
|
|
g_free (path);
|
|
|
|
return writable;
|
|
}
|
|
|
|
/**
|
|
* g_settings_get_child:
|
|
* @settings: a #GSettings object
|
|
* @name: the name of the 'child' schema
|
|
* @returns: (transfer full): a 'child' settings object
|
|
*
|
|
* Creates a 'child' settings object which has a base path of
|
|
* <replaceable>base-path</replaceable>/@name, where
|
|
* <replaceable>base-path</replaceable> is the base path of @settings.
|
|
*
|
|
* The schema for the child settings object must have been declared
|
|
* in the schema of @settings using a <tag class="starttag">child</tag> element.
|
|
*
|
|
* Since: 2.26
|
|
*/
|
|
GSettings *
|
|
g_settings_get_child (GSettings *settings,
|
|
const gchar *name)
|
|
{
|
|
const gchar *child_schema;
|
|
gchar *child_path;
|
|
gchar *child_name;
|
|
GSettings *child;
|
|
|
|
g_return_val_if_fail (G_IS_SETTINGS (settings), NULL);
|
|
|
|
child_name = g_strconcat (name, "/", NULL);
|
|
child_schema = g_settings_schema_get_string (settings->priv->schema,
|
|
child_name);
|
|
if (child_schema == NULL)
|
|
g_error ("Schema '%s' has no child '%s'",
|
|
settings->priv->schema_name, name);
|
|
|
|
child_path = g_strconcat (settings->priv->path, child_name, NULL);
|
|
child = g_object_new (G_TYPE_SETTINGS,
|
|
"schema", child_schema,
|
|
"path", child_path,
|
|
NULL);
|
|
g_free (child_path);
|
|
g_free (child_name);
|
|
|
|
return child;
|
|
}
|
|
|
|
/**
|
|
* g_settings_list_keys:
|
|
* @settings: a #GSettings object
|
|
* @returns: (transfer full) (element-type utf8): a list of the keys on @settings
|
|
*
|
|
* Introspects the list of keys on @settings.
|
|
*
|
|
* You should probably not be calling this function from "normal" code
|
|
* (since you should already know what keys are in your schema). This
|
|
* function is intended for introspection reasons.
|
|
*
|
|
* You should free the return value with g_strfreev() when you are done
|
|
* with it.
|
|
*/
|
|
gchar **
|
|
g_settings_list_keys (GSettings *settings)
|
|
{
|
|
const GQuark *keys;
|
|
gchar **strv;
|
|
gint n_keys;
|
|
gint i, j;
|
|
|
|
keys = g_settings_schema_list (settings->priv->schema, &n_keys);
|
|
strv = g_new (gchar *, n_keys + 1);
|
|
for (i = j = 0; i < n_keys; i++)
|
|
{
|
|
const gchar *key = g_quark_to_string (keys[i]);
|
|
|
|
if (!g_str_has_suffix (key, "/"))
|
|
strv[j++] = g_strdup (key);
|
|
}
|
|
strv[j] = NULL;
|
|
|
|
return strv;
|
|
}
|
|
|
|
/**
|
|
* g_settings_list_children:
|
|
* @settings: a #GSettings object
|
|
* @returns: (transfer full) (element-type utf8): a list of the children on @settings
|
|
*
|
|
* Gets the list of children on @settings.
|
|
*
|
|
* The list is exactly the list of strings for which it is not an error
|
|
* to call g_settings_get_child().
|
|
*
|
|
* For GSettings objects that are lists, this value can change at any
|
|
* time and you should connect to the "children-changed" signal to watch
|
|
* for those changes. Note that there is a race condition here: you may
|
|
* request a child after listing it only for it to have been destroyed
|
|
* in the meantime. For this reason, g_settings_get_child() may return
|
|
* %NULL even for a child that was listed by this function.
|
|
*
|
|
* For GSettings objects that are not lists, you should probably not be
|
|
* calling this function from "normal" code (since you should already
|
|
* know what children are in your schema). This function may still be
|
|
* useful there for introspection reasons, however.
|
|
*
|
|
* You should free the return value with g_strfreev() when you are done
|
|
* with it.
|
|
*/
|
|
gchar **
|
|
g_settings_list_children (GSettings *settings)
|
|
{
|
|
const GQuark *keys;
|
|
gchar **strv;
|
|
gint n_keys;
|
|
gint i, j;
|
|
|
|
keys = g_settings_schema_list (settings->priv->schema, &n_keys);
|
|
strv = g_new (gchar *, n_keys + 1);
|
|
for (i = j = 0; i < n_keys; i++)
|
|
{
|
|
const gchar *key = g_quark_to_string (keys[i]);
|
|
|
|
if (g_str_has_suffix (key, "/"))
|
|
{
|
|
gint length = strlen (key);
|
|
|
|
strv[j] = g_memdup (key, length);
|
|
strv[j][length - 1] = '\0';
|
|
j++;
|
|
}
|
|
}
|
|
strv[j] = NULL;
|
|
|
|
return strv;
|
|
}
|
|
|
|
/**
|
|
* g_settings_get_range:
|
|
* @settings: a #GSettings
|
|
* @key: the key to query the range of
|
|
* @returns: a #GVariant describing the range
|
|
*
|
|
* Queries the range of a key.
|
|
*
|
|
* This function will return a #GVariant that fully describes the range
|
|
* of values that are valid for @key.
|
|
*
|
|
* The type of #GVariant returned is <literal>(sv)</literal>. The
|
|
* string describes the type of range restriction in effect. The type
|
|
* and meaning of the value contained in the variant depends on the
|
|
* string.
|
|
*
|
|
* If the string is <literal>'type'</literal> then the variant contains
|
|
* an empty array. The element type of that empty array is the expected
|
|
* type of value and all values of that type are valid.
|
|
*
|
|
* If the string is <literal>'enum'</literal> then the variant contains
|
|
* an array enumerating the possible values. Each item in the array is
|
|
* a possible valid value and no other values are valid.
|
|
*
|
|
* If the string is <literal>'flags'</literal> then the variant contains
|
|
* an array. Each item in the array is a value that may appear zero or
|
|
* one times in an array to be used as the value for this key. For
|
|
* example, if the variant contained the array <literal>['x',
|
|
* 'y']</literal> then the valid values for the key would be
|
|
* <literal>[]</literal>, <literal>['x']</literal>,
|
|
* <literal>['y']</literal>, <literal>['x', 'y']</literal> and
|
|
* <literal>['y', 'x']</literal>.
|
|
*
|
|
* Finally, if the string is <literal>'range'</literal> then the variant
|
|
* contains a pair of like-typed values -- the minimum and maximum
|
|
* permissible values for this key.
|
|
*
|
|
* This information should not be used by normal programs. It is
|
|
* considered to be a hint for introspection purposes. Normal programs
|
|
* should already know what is permitted by their own schema. The
|
|
* format may change in any way in the future -- but particularly, new
|
|
* forms may be added to the possibilities described above.
|
|
*
|
|
* It is a programmer error to give a @key that isn't contained in the
|
|
* schema for @settings.
|
|
*
|
|
* You should free the returned value with g_variant_unref() when it is
|
|
* no longer needed.
|
|
*
|
|
* Since: 2.28
|
|
**/
|
|
GVariant *
|
|
g_settings_get_range (GSettings *settings,
|
|
const gchar *key)
|
|
{
|
|
GSettingsKeyInfo info;
|
|
const gchar *type;
|
|
GVariant *range;
|
|
|
|
g_settings_get_key_info (&info, settings, key);
|
|
|
|
if (info.minimum)
|
|
{
|
|
range = g_variant_new ("(**)", info.minimum, info.maximum);
|
|
type = "range";
|
|
}
|
|
else if (info.strinfo)
|
|
{
|
|
range = strinfo_enumerate (info.strinfo, info.strinfo_length);
|
|
type = info.is_flags ? "flags" : "enum";
|
|
}
|
|
else
|
|
{
|
|
range = g_variant_new_array (info.type, NULL, 0);
|
|
type = "type";
|
|
}
|
|
|
|
g_settings_free_key_info (&info);
|
|
|
|
return g_variant_ref_sink (g_variant_new ("(sv)", type, range));
|
|
}
|
|
|
|
/**
|
|
* g_settings_range_check:
|
|
* @settings: a #GSettings
|
|
* @key: the key to check
|
|
* @value: the value to check
|
|
* @returns: %TRUE if @value is valid for @key
|
|
*
|
|
* Checks if the given @value is of the correct type and within the
|
|
* permitted range for @key.
|
|
*
|
|
* This API is not intended to be used by normal programs -- they should
|
|
* already know what is permitted by their own schemas. This API is
|
|
* meant to be used by programs such as editors or commandline tools.
|
|
*
|
|
* It is a programmer error to give a @key that isn't contained in the
|
|
* schema for @settings.
|
|
*
|
|
* Since: 2.28
|
|
**/
|
|
gboolean
|
|
g_settings_range_check (GSettings *settings,
|
|
const gchar *key,
|
|
GVariant *value)
|
|
{
|
|
GSettingsKeyInfo info;
|
|
gboolean good;
|
|
|
|
g_settings_get_key_info (&info, settings, key);
|
|
good = g_settings_type_check (&info, value) &&
|
|
g_settings_key_info_range_check (&info, value);
|
|
g_settings_free_key_info (&info);
|
|
|
|
return good;
|
|
}
|
|
|
|
/* Binding {{{1 */
|
|
typedef struct
|
|
{
|
|
GSettingsKeyInfo info;
|
|
GObject *object;
|
|
|
|
GSettingsBindGetMapping get_mapping;
|
|
GSettingsBindSetMapping set_mapping;
|
|
gpointer user_data;
|
|
GDestroyNotify destroy;
|
|
|
|
guint writable_handler_id;
|
|
guint property_handler_id;
|
|
const GParamSpec *property;
|
|
guint key_handler_id;
|
|
|
|
/* prevent recursion */
|
|
gboolean running;
|
|
} GSettingsBinding;
|
|
|
|
static void
|
|
g_settings_binding_free (gpointer data)
|
|
{
|
|
GSettingsBinding *binding = data;
|
|
|
|
g_assert (!binding->running);
|
|
|
|
if (binding->writable_handler_id)
|
|
g_signal_handler_disconnect (binding->info.settings,
|
|
binding->writable_handler_id);
|
|
|
|
if (binding->key_handler_id)
|
|
g_signal_handler_disconnect (binding->info.settings,
|
|
binding->key_handler_id);
|
|
|
|
if (g_signal_handler_is_connected (binding->object,
|
|
binding->property_handler_id))
|
|
g_signal_handler_disconnect (binding->object,
|
|
binding->property_handler_id);
|
|
|
|
g_settings_free_key_info (&binding->info);
|
|
|
|
if (binding->destroy)
|
|
binding->destroy (binding->user_data);
|
|
|
|
g_slice_free (GSettingsBinding, binding);
|
|
}
|
|
|
|
static GQuark
|
|
g_settings_binding_quark (const char *property)
|
|
{
|
|
GQuark quark;
|
|
gchar *tmp;
|
|
|
|
tmp = g_strdup_printf ("gsettingsbinding-%s", property);
|
|
quark = g_quark_from_string (tmp);
|
|
g_free (tmp);
|
|
|
|
return quark;
|
|
}
|
|
|
|
static void
|
|
g_settings_binding_key_changed (GSettings *settings,
|
|
const gchar *key,
|
|
gpointer user_data)
|
|
{
|
|
GSettingsBinding *binding = user_data;
|
|
GValue value = { 0, };
|
|
GVariant *variant;
|
|
|
|
g_assert (settings == binding->info.settings);
|
|
g_assert (key == binding->info.key);
|
|
|
|
if (binding->running)
|
|
return;
|
|
|
|
binding->running = TRUE;
|
|
|
|
g_value_init (&value, binding->property->value_type);
|
|
|
|
variant = g_settings_read_from_backend (&binding->info);
|
|
if (variant && !binding->get_mapping (&value, variant, binding->user_data))
|
|
{
|
|
/* silently ignore errors in the user's config database */
|
|
g_variant_unref (variant);
|
|
variant = NULL;
|
|
}
|
|
|
|
if (variant == NULL)
|
|
{
|
|
variant = g_settings_get_translated_default (&binding->info);
|
|
if (variant &&
|
|
!binding->get_mapping (&value, variant, binding->user_data))
|
|
{
|
|
/* flag translation errors with a warning */
|
|
g_warning ("Translated default `%s' for key `%s' in schema `%s' "
|
|
"was rejected by the binding mapping function",
|
|
binding->info.unparsed, binding->info.key,
|
|
binding->info.settings->priv->schema_name);
|
|
g_variant_unref (variant);
|
|
variant = NULL;
|
|
}
|
|
}
|
|
|
|
if (variant == NULL)
|
|
{
|
|
variant = g_variant_ref (binding->info.default_value);
|
|
if (!binding->get_mapping (&value, variant, binding->user_data))
|
|
g_error ("The schema default value for key `%s' in schema `%s' "
|
|
"was rejected by the binding mapping function.",
|
|
binding->info.key,
|
|
binding->info.settings->priv->schema_name);
|
|
}
|
|
|
|
g_object_set_property (binding->object, binding->property->name, &value);
|
|
g_variant_unref (variant);
|
|
g_value_unset (&value);
|
|
|
|
binding->running = FALSE;
|
|
}
|
|
|
|
static void
|
|
g_settings_binding_property_changed (GObject *object,
|
|
const GParamSpec *pspec,
|
|
gpointer user_data)
|
|
{
|
|
GSettingsBinding *binding = user_data;
|
|
GValue value = { 0, };
|
|
GVariant *variant;
|
|
|
|
g_assert (object == binding->object);
|
|
g_assert (pspec == binding->property);
|
|
|
|
if (binding->running)
|
|
return;
|
|
|
|
binding->running = TRUE;
|
|
|
|
g_value_init (&value, pspec->value_type);
|
|
g_object_get_property (object, pspec->name, &value);
|
|
if ((variant = binding->set_mapping (&value, binding->info.type,
|
|
binding->user_data)))
|
|
{
|
|
if (g_variant_is_floating (variant))
|
|
g_variant_ref_sink (variant);
|
|
|
|
if (!g_settings_type_check (&binding->info, variant))
|
|
{
|
|
g_critical ("binding mapping function for key `%s' returned "
|
|
"GVariant of type `%s' when type `%s' was requested",
|
|
binding->info.key, g_variant_get_type_string (variant),
|
|
g_variant_type_dup_string (binding->info.type));
|
|
return;
|
|
}
|
|
|
|
if (!g_settings_key_info_range_check (&binding->info, variant))
|
|
{
|
|
g_critical ("GObject property `%s' on a `%s' object is out of "
|
|
"schema-specified range for key `%s' of `%s': %s",
|
|
binding->property->name,
|
|
g_type_name (binding->property->owner_type),
|
|
binding->info.key,
|
|
binding->info.settings->priv->schema_name,
|
|
g_variant_print (variant, TRUE));
|
|
return;
|
|
}
|
|
|
|
g_settings_write_to_backend (&binding->info, variant);
|
|
g_variant_unref (variant);
|
|
}
|
|
g_value_unset (&value);
|
|
|
|
binding->running = FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
g_settings_bind_invert_boolean_get_mapping (GValue *value,
|
|
GVariant *variant,
|
|
gpointer user_data)
|
|
{
|
|
g_value_set_boolean (value, !g_variant_get_boolean (variant));
|
|
return TRUE;
|
|
}
|
|
|
|
static GVariant *
|
|
g_settings_bind_invert_boolean_set_mapping (const GValue *value,
|
|
const GVariantType *expected_type,
|
|
gpointer user_data)
|
|
{
|
|
return g_variant_new_boolean (!g_value_get_boolean (value));
|
|
}
|
|
|
|
/**
|
|
* g_settings_bind:
|
|
* @settings: a #GSettings object
|
|
* @key: the key to bind
|
|
* @object: (type GObject.Object): a #GObject
|
|
* @property: the name of the property to bind
|
|
* @flags: flags for the binding
|
|
*
|
|
* Create a binding between the @key in the @settings object
|
|
* and the property @property of @object.
|
|
*
|
|
* The binding uses the default GIO mapping functions to map
|
|
* between the settings and property values. These functions
|
|
* handle booleans, numeric types and string types in a
|
|
* straightforward way. Use g_settings_bind_with_mapping() if
|
|
* you need a custom mapping, or map between types that are not
|
|
* supported by the default mapping functions.
|
|
*
|
|
* Unless the @flags include %G_SETTINGS_BIND_NO_SENSITIVITY, this
|
|
* function also establishes a binding between the writability of
|
|
* @key and the "sensitive" property of @object (if @object has
|
|
* a boolean property by that name). See g_settings_bind_writable()
|
|
* for more details about writable bindings.
|
|
*
|
|
* Note that the lifecycle of the binding is tied to the object,
|
|
* and that you can have only one binding per object property.
|
|
* If you bind the same property twice on the same object, the second
|
|
* binding overrides the first one.
|
|
*
|
|
* Since: 2.26
|
|
*/
|
|
void
|
|
g_settings_bind (GSettings *settings,
|
|
const gchar *key,
|
|
gpointer object,
|
|
const gchar *property,
|
|
GSettingsBindFlags flags)
|
|
{
|
|
GSettingsBindGetMapping get_mapping = NULL;
|
|
GSettingsBindSetMapping set_mapping = NULL;
|
|
|
|
if (flags & G_SETTINGS_BIND_INVERT_BOOLEAN)
|
|
{
|
|
get_mapping = g_settings_bind_invert_boolean_get_mapping;
|
|
set_mapping = g_settings_bind_invert_boolean_set_mapping;
|
|
|
|
/* can't pass this flag to g_settings_bind_with_mapping() */
|
|
flags &= ~G_SETTINGS_BIND_INVERT_BOOLEAN;
|
|
}
|
|
|
|
g_settings_bind_with_mapping (settings, key, object, property, flags,
|
|
get_mapping, set_mapping, NULL, NULL);
|
|
}
|
|
|
|
/**
|
|
* g_settings_bind_with_mapping: (skip)
|
|
* @settings: a #GSettings object
|
|
* @key: the key to bind
|
|
* @object: (type GObject.Object): a #GObject
|
|
* @property: the name of the property to bind
|
|
* @flags: flags for the binding
|
|
* @get_mapping: a function that gets called to convert values
|
|
* from @settings to @object, or %NULL to use the default GIO mapping
|
|
* @set_mapping: a function that gets called to convert values
|
|
* from @object to @settings, or %NULL to use the default GIO mapping
|
|
* @user_data: data that gets passed to @get_mapping and @set_mapping
|
|
* @destroy: #GDestroyNotify function for @user_data
|
|
*
|
|
* Create a binding between the @key in the @settings object
|
|
* and the property @property of @object.
|
|
*
|
|
* The binding uses the provided mapping functions to map between
|
|
* settings and property values.
|
|
*
|
|
* Note that the lifecycle of the binding is tied to the object,
|
|
* and that you can have only one binding per object property.
|
|
* If you bind the same property twice on the same object, the second
|
|
* binding overrides the first one.
|
|
*
|
|
* Since: 2.26
|
|
*/
|
|
void
|
|
g_settings_bind_with_mapping (GSettings *settings,
|
|
const gchar *key,
|
|
gpointer object,
|
|
const gchar *property,
|
|
GSettingsBindFlags flags,
|
|
GSettingsBindGetMapping get_mapping,
|
|
GSettingsBindSetMapping set_mapping,
|
|
gpointer user_data,
|
|
GDestroyNotify destroy)
|
|
{
|
|
GSettingsBinding *binding;
|
|
GObjectClass *objectclass;
|
|
gchar *detailed_signal;
|
|
GQuark binding_quark;
|
|
|
|
g_return_if_fail (G_IS_SETTINGS (settings));
|
|
g_return_if_fail (~flags & G_SETTINGS_BIND_INVERT_BOOLEAN);
|
|
|
|
objectclass = G_OBJECT_GET_CLASS (object);
|
|
|
|
binding = g_slice_new0 (GSettingsBinding);
|
|
g_settings_get_key_info (&binding->info, settings, key);
|
|
binding->object = object;
|
|
binding->property = g_object_class_find_property (objectclass, property);
|
|
binding->user_data = user_data;
|
|
binding->destroy = destroy;
|
|
binding->get_mapping = get_mapping ? get_mapping : g_settings_get_mapping;
|
|
binding->set_mapping = set_mapping ? set_mapping : g_settings_set_mapping;
|
|
|
|
if (!(flags & (G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET)))
|
|
flags |= G_SETTINGS_BIND_GET | G_SETTINGS_BIND_SET;
|
|
|
|
if (binding->property == NULL)
|
|
{
|
|
g_critical ("g_settings_bind: no property '%s' on class '%s'",
|
|
property, G_OBJECT_TYPE_NAME (object));
|
|
return;
|
|
}
|
|
|
|
if ((flags & G_SETTINGS_BIND_GET) &&
|
|
(binding->property->flags & G_PARAM_WRITABLE) == 0)
|
|
{
|
|
g_critical ("g_settings_bind: property '%s' on class '%s' is not "
|
|
"writable", property, G_OBJECT_TYPE_NAME (object));
|
|
return;
|
|
}
|
|
if ((flags & G_SETTINGS_BIND_SET) &&
|
|
(binding->property->flags & G_PARAM_READABLE) == 0)
|
|
{
|
|
g_critical ("g_settings_bind: property '%s' on class '%s' is not "
|
|
"readable", property, G_OBJECT_TYPE_NAME (object));
|
|
return;
|
|
}
|
|
|
|
if (get_mapping == g_settings_bind_invert_boolean_get_mapping)
|
|
{
|
|
/* g_settings_bind_invert_boolean_get_mapping() is a private
|
|
* function, so if we are here it means that g_settings_bind() was
|
|
* called with G_SETTINGS_BIND_INVERT_BOOLEAN.
|
|
*
|
|
* Ensure that both sides are boolean.
|
|
*/
|
|
|
|
if (binding->property->value_type != G_TYPE_BOOLEAN)
|
|
{
|
|
g_critical ("g_settings_bind: G_SETTINGS_BIND_INVERT_BOOLEAN "
|
|
"was specified, but property `%s' on type `%s' has "
|
|
"type `%s'", property, G_OBJECT_TYPE_NAME (object),
|
|
g_type_name ((binding->property->value_type)));
|
|
return;
|
|
}
|
|
|
|
if (!g_variant_type_equal (binding->info.type, G_VARIANT_TYPE_BOOLEAN))
|
|
{
|
|
g_critical ("g_settings_bind: G_SETTINGS_BIND_INVERT_BOOLEAN "
|
|
"was specified, but key `%s' on schema `%s' has "
|
|
"type `%s'", key, settings->priv->schema_name,
|
|
g_variant_type_dup_string (binding->info.type));
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
else if (((get_mapping == NULL && (flags & G_SETTINGS_BIND_GET)) ||
|
|
(set_mapping == NULL && (flags & G_SETTINGS_BIND_SET))) &&
|
|
!g_settings_mapping_is_compatible (binding->property->value_type,
|
|
binding->info.type))
|
|
{
|
|
g_critical ("g_settings_bind: property '%s' on class '%s' has type "
|
|
"'%s' which is not compatible with type '%s' of key '%s' "
|
|
"on schema '%s'", property, G_OBJECT_TYPE_NAME (object),
|
|
g_type_name (binding->property->value_type),
|
|
g_variant_type_dup_string (binding->info.type), key,
|
|
settings->priv->schema_name);
|
|
return;
|
|
}
|
|
|
|
if ((flags & G_SETTINGS_BIND_SET) &&
|
|
(~flags & G_SETTINGS_BIND_NO_SENSITIVITY))
|
|
{
|
|
GParamSpec *sensitive;
|
|
|
|
sensitive = g_object_class_find_property (objectclass, "sensitive");
|
|
|
|
if (sensitive && sensitive->value_type == G_TYPE_BOOLEAN &&
|
|
(sensitive->flags & G_PARAM_WRITABLE))
|
|
g_settings_bind_writable (settings, binding->info.key,
|
|
object, "sensitive", FALSE);
|
|
}
|
|
|
|
if (flags & G_SETTINGS_BIND_SET)
|
|
{
|
|
detailed_signal = g_strdup_printf ("notify::%s", property);
|
|
binding->property_handler_id =
|
|
g_signal_connect (object, detailed_signal,
|
|
G_CALLBACK (g_settings_binding_property_changed),
|
|
binding);
|
|
g_free (detailed_signal);
|
|
|
|
if (~flags & G_SETTINGS_BIND_GET)
|
|
g_settings_binding_property_changed (object,
|
|
binding->property,
|
|
binding);
|
|
}
|
|
|
|
if (flags & G_SETTINGS_BIND_GET)
|
|
{
|
|
if (~flags & G_SETTINGS_BIND_GET_NO_CHANGES)
|
|
{
|
|
detailed_signal = g_strdup_printf ("changed::%s", key);
|
|
binding->key_handler_id =
|
|
g_signal_connect (settings, detailed_signal,
|
|
G_CALLBACK (g_settings_binding_key_changed),
|
|
binding);
|
|
g_free (detailed_signal);
|
|
}
|
|
|
|
g_settings_binding_key_changed (settings, binding->info.key, binding);
|
|
}
|
|
|
|
binding_quark = g_settings_binding_quark (property);
|
|
g_object_set_qdata_full (object, binding_quark,
|
|
binding, g_settings_binding_free);
|
|
}
|
|
|
|
/* Writability binding {{{1 */
|
|
typedef struct
|
|
{
|
|
GSettings *settings;
|
|
gpointer object;
|
|
const gchar *key;
|
|
const gchar *property;
|
|
gboolean inverted;
|
|
gulong handler_id;
|
|
} GSettingsWritableBinding;
|
|
|
|
static void
|
|
g_settings_writable_binding_free (gpointer data)
|
|
{
|
|
GSettingsWritableBinding *binding = data;
|
|
|
|
g_signal_handler_disconnect (binding->settings, binding->handler_id);
|
|
g_object_unref (binding->settings);
|
|
g_slice_free (GSettingsWritableBinding, binding);
|
|
}
|
|
|
|
static void
|
|
g_settings_binding_writable_changed (GSettings *settings,
|
|
const gchar *key,
|
|
gpointer user_data)
|
|
{
|
|
GSettingsWritableBinding *binding = user_data;
|
|
gboolean writable;
|
|
|
|
g_assert (settings == binding->settings);
|
|
g_assert (key == binding->key);
|
|
|
|
writable = g_settings_is_writable (settings, key);
|
|
|
|
if (binding->inverted)
|
|
writable = !writable;
|
|
|
|
g_object_set (binding->object, binding->property, writable, NULL);
|
|
}
|
|
|
|
/**
|
|
* g_settings_bind_writable:
|
|
* @settings: a #GSettings object
|
|
* @key: the key to bind
|
|
* @object: (type GObject.Object):a #GObject
|
|
* @property: the name of a boolean property to bind
|
|
* @inverted: whether to 'invert' the value
|
|
*
|
|
* Create a binding between the writability of @key in the
|
|
* @settings object and the property @property of @object.
|
|
* The property must be boolean; "sensitive" or "visible"
|
|
* properties of widgets are the most likely candidates.
|
|
*
|
|
* Writable bindings are always uni-directional; changes of the
|
|
* writability of the setting will be propagated to the object
|
|
* property, not the other way.
|
|
*
|
|
* When the @inverted argument is %TRUE, the binding inverts the
|
|
* value as it passes from the setting to the object, i.e. @property
|
|
* will be set to %TRUE if the key is <emphasis>not</emphasis>
|
|
* writable.
|
|
*
|
|
* Note that the lifecycle of the binding is tied to the object,
|
|
* and that you can have only one binding per object property.
|
|
* If you bind the same property twice on the same object, the second
|
|
* binding overrides the first one.
|
|
*
|
|
* Since: 2.26
|
|
*/
|
|
void
|
|
g_settings_bind_writable (GSettings *settings,
|
|
const gchar *key,
|
|
gpointer object,
|
|
const gchar *property,
|
|
gboolean inverted)
|
|
{
|
|
GSettingsWritableBinding *binding;
|
|
gchar *detailed_signal;
|
|
GParamSpec *pspec;
|
|
|
|
g_return_if_fail (G_IS_SETTINGS (settings));
|
|
|
|
pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (object), property);
|
|
if (pspec == NULL)
|
|
{
|
|
g_critical ("g_settings_bind_writable: no property '%s' on class '%s'",
|
|
property, G_OBJECT_TYPE_NAME (object));
|
|
return;
|
|
}
|
|
if ((pspec->flags & G_PARAM_WRITABLE) == 0)
|
|
{
|
|
g_critical ("g_settings_bind_writable: property '%s' on class '%s' is not writable",
|
|
property, G_OBJECT_TYPE_NAME (object));
|
|
return;
|
|
}
|
|
|
|
binding = g_slice_new (GSettingsWritableBinding);
|
|
binding->settings = g_object_ref (settings);
|
|
binding->object = object;
|
|
binding->key = g_intern_string (key);
|
|
binding->property = g_intern_string (property);
|
|
binding->inverted = inverted;
|
|
|
|
detailed_signal = g_strdup_printf ("writable-changed::%s", key);
|
|
binding->handler_id =
|
|
g_signal_connect (settings, detailed_signal,
|
|
G_CALLBACK (g_settings_binding_writable_changed),
|
|
binding);
|
|
g_free (detailed_signal);
|
|
|
|
g_object_set_qdata_full (object, g_settings_binding_quark (property),
|
|
binding, g_settings_writable_binding_free);
|
|
|
|
g_settings_binding_writable_changed (settings, binding->key, binding);
|
|
}
|
|
|
|
/**
|
|
* g_settings_unbind:
|
|
* @object: the object
|
|
* @property: the property whose binding is removed
|
|
*
|
|
* Removes an existing binding for @property on @object.
|
|
*
|
|
* Note that bindings are automatically removed when the
|
|
* object is finalized, so it is rarely necessary to call this
|
|
* function.
|
|
*
|
|
* Since: 2.26
|
|
*/
|
|
void
|
|
g_settings_unbind (gpointer object,
|
|
const gchar *property)
|
|
{
|
|
GQuark binding_quark;
|
|
|
|
binding_quark = g_settings_binding_quark (property);
|
|
g_object_set_qdata (object, binding_quark, NULL);
|
|
}
|
|
|
|
/* Epilogue {{{1 */
|
|
|
|
/* vim:set foldmethod=marker: */
|