545 lines
14 KiB
C
545 lines
14 KiB
C
/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
|
|
|
|
#include <string.h>
|
|
|
|
struct _GOConfNode {
|
|
gchar *path;
|
|
gchar *id;
|
|
gchar *key;
|
|
GSettings *settings;
|
|
};
|
|
|
|
static GHashTable *installed_schemas, *closures;
|
|
void
|
|
_go_conf_init (void)
|
|
{
|
|
char const * const *schemas = g_settings_list_schemas ();
|
|
char const * const *cur = schemas;
|
|
installed_schemas = g_hash_table_new (g_str_hash, g_str_equal);
|
|
while (*cur) {
|
|
g_hash_table_insert (installed_schemas, (gpointer) *cur, GUINT_TO_POINTER (TRUE));
|
|
cur++;
|
|
}
|
|
closures = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) go_conf_closure_free);
|
|
}
|
|
|
|
void
|
|
_go_conf_shutdown (void)
|
|
{
|
|
g_hash_table_destroy (installed_schemas);
|
|
g_hash_table_destroy (closures);
|
|
}
|
|
|
|
static gchar *
|
|
go_conf_format_id (gchar const *key)
|
|
{
|
|
char *cur, *res;
|
|
if (!key)
|
|
return NULL;
|
|
res = g_strdup (key);
|
|
/* replace all slashes by dots */
|
|
cur = res;
|
|
while ((cur = strchr (cur, '/')) && *cur)
|
|
*cur = '.';
|
|
/* replace all underscores by hyphens */
|
|
cur = res;
|
|
while ((cur = strchr (cur, '_')) && *cur)
|
|
*cur = '-';
|
|
cur = res;
|
|
while (*cur) {
|
|
*cur = g_ascii_tolower (*cur);
|
|
cur++;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
GOConfNode *
|
|
go_conf_get_node (GOConfNode *parent, gchar const *key)
|
|
{
|
|
GOConfNode *node;
|
|
|
|
char *formatted = go_conf_format_id (key);
|
|
node = g_new0 (GOConfNode, 1);
|
|
if (parent) {
|
|
if (key && !parent->key) {
|
|
node->path = g_strconcat (parent->path, "/", key, NULL);
|
|
node->id = g_strconcat (parent->id, ".", formatted, NULL);
|
|
} else {
|
|
node->path = g_strdup (parent->path);
|
|
node->id = g_strdup (parent->id);
|
|
node->key = g_strdup (key? key: parent->key);
|
|
}
|
|
} else {
|
|
if (key[0] == '/') {
|
|
node->path = g_strdup (key);
|
|
node->id = g_strconcat ("org.gnome", formatted, NULL);
|
|
} else {
|
|
node->path = g_strconcat ("/apps/", key, NULL);
|
|
node->id = g_strconcat ("org.gnome.", formatted, NULL);
|
|
}
|
|
}
|
|
node->settings = g_hash_table_lookup (installed_schemas, node->id)? g_settings_new (node->id): NULL;
|
|
g_free (formatted);
|
|
if (!node->settings) {
|
|
char *last_dot = strrchr (node->id, '.');
|
|
*last_dot = 0;
|
|
node->settings = g_hash_table_lookup (installed_schemas, node->id)? g_settings_new (node->id): NULL;
|
|
if (node->settings)
|
|
node->key = g_strdup (last_dot + 1);
|
|
else {
|
|
go_conf_free_node (node);
|
|
node = NULL;
|
|
}
|
|
}
|
|
return node;
|
|
}
|
|
|
|
void
|
|
go_conf_free_node (GOConfNode *node)
|
|
{
|
|
if (node) {
|
|
if (node->settings)
|
|
g_object_unref (node->settings);
|
|
g_free (node->path);
|
|
g_free (node->id);
|
|
g_free (node->key);
|
|
g_free (node);
|
|
}
|
|
}
|
|
|
|
void
|
|
go_conf_sync (GOConfNode *node)
|
|
{
|
|
g_settings_sync ();
|
|
}
|
|
|
|
void
|
|
go_conf_set_bool (GOConfNode *node, gchar const *key, gboolean val)
|
|
{
|
|
GOConfNode *real_node = go_conf_get_node (node, key);
|
|
if (!real_node) {
|
|
d (g_warning ("Unable to set key '%s'", key));
|
|
return;
|
|
}
|
|
g_settings_set_boolean (real_node->settings, real_node->key, val);
|
|
go_conf_free_node (real_node);
|
|
}
|
|
|
|
void
|
|
go_conf_set_int (GOConfNode *node, gchar const *key, gint val)
|
|
{
|
|
GOConfNode *real_node = go_conf_get_node (node, key);
|
|
if (!real_node) {
|
|
d (g_warning ("Unable to set key '%s'", key));
|
|
return;
|
|
}
|
|
g_settings_set_int (real_node->settings, real_node->key, val);
|
|
go_conf_free_node (real_node);
|
|
}
|
|
|
|
void
|
|
go_conf_set_double (GOConfNode *node, gchar const *key, gdouble val)
|
|
{
|
|
GOConfNode *real_node = go_conf_get_node (node, key);
|
|
if (!real_node) {
|
|
d (g_warning ("Unable to set key '%s'", key));
|
|
return;
|
|
}
|
|
g_settings_set_double (real_node->settings, real_node->key, val);
|
|
go_conf_free_node (real_node);
|
|
}
|
|
|
|
void
|
|
go_conf_set_string (GOConfNode *node, gchar const *key, gchar const *str)
|
|
{
|
|
GOConfNode *real_node = go_conf_get_node (node, key);
|
|
if (!real_node) {
|
|
d (g_warning ("Unable to set key '%s'", key));
|
|
return;
|
|
}
|
|
g_settings_set_string (real_node->settings, real_node->key, str);
|
|
go_conf_free_node (real_node);
|
|
}
|
|
|
|
void
|
|
go_conf_set_str_list (GOConfNode *node, gchar const *key, GSList *list)
|
|
{
|
|
GOConfNode *real_node = go_conf_get_node (node, key);
|
|
gssize n = g_slist_length (list);
|
|
char const ** strs;
|
|
GSList *ptr = list;
|
|
unsigned i = 0;
|
|
|
|
if (!real_node) {
|
|
d (g_warning ("Unable to set key '%s'", key));
|
|
return;
|
|
}
|
|
strs = g_new (char const*, n + 1);
|
|
for (; ptr; ptr = ptr->next)
|
|
strs[i++] = (char const *) ptr->data;
|
|
strs[n] = NULL;
|
|
g_settings_set_strv (real_node->settings, real_node->key, strs);
|
|
g_free (strs);
|
|
go_conf_free_node (real_node);
|
|
}
|
|
|
|
static GVariant *
|
|
go_conf_get (GOConfNode *node, gchar const *key, GVariantType const *t)
|
|
{
|
|
GVariant *res = g_settings_get_value (node->settings, key);
|
|
if (res == NULL) {
|
|
d (g_warning ("Unable to load key '%s'", key));
|
|
return NULL;
|
|
}
|
|
|
|
if (!g_variant_is_of_type (res, t)) {
|
|
char *tstr = g_variant_type_dup_string (t);
|
|
g_warning ("Expected `%s' got `%s' for key %s",
|
|
tstr,
|
|
g_variant_get_type_string (res),
|
|
key);
|
|
g_free (tstr);
|
|
g_variant_unref (res);
|
|
return NULL;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
gboolean
|
|
go_conf_load_bool (GOConfNode *node, gchar const *key, gboolean default_val)
|
|
{
|
|
gboolean res;
|
|
GVariant *val = NULL;
|
|
if (node) {
|
|
if (key && !strchr (key, '/') && !strchr (key, '.'))
|
|
val = go_conf_get (node, key, G_VARIANT_TYPE_BOOLEAN);
|
|
else if (node->key)
|
|
val = go_conf_get (node, node->key, G_VARIANT_TYPE_BOOLEAN);
|
|
}
|
|
if (val == NULL) {
|
|
GOConfNode *real_node = go_conf_get_node (node, key);
|
|
val = real_node? go_conf_get (real_node, real_node->key, G_VARIANT_TYPE_BOOLEAN): NULL;
|
|
go_conf_free_node (real_node);
|
|
}
|
|
|
|
if (val != NULL) {
|
|
res = g_variant_get_boolean (val);
|
|
g_variant_unref (val);
|
|
} else {
|
|
d (g_warning ("Using default value '%s'", default_val ? "true" : "false"));
|
|
return default_val;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
gint
|
|
go_conf_load_int (GOConfNode *node, gchar const *key, gint minima, gint maxima, gint default_val)
|
|
{
|
|
gint res = -1;
|
|
GVariant *val = NULL;
|
|
if (node) {
|
|
if (key && !strchr (key, '/') && !strchr (key, '.'))
|
|
val = go_conf_get (node, key, G_VARIANT_TYPE_INT32);
|
|
else if (node->key)
|
|
val = go_conf_get (node, node->key, G_VARIANT_TYPE_INT32);
|
|
}
|
|
if (val == NULL) {
|
|
GOConfNode *real_node = go_conf_get_node (node, key);
|
|
val = real_node? go_conf_get (real_node, real_node->key, G_VARIANT_TYPE_INT32): NULL;
|
|
go_conf_free_node (real_node);
|
|
}
|
|
if (val != NULL) {
|
|
res = g_variant_get_int32 (val);
|
|
g_variant_unref (val);
|
|
if (res < minima || maxima < res) {
|
|
g_warning ("Invalid value '%d' for %s. If should be >= %d and <= %d",
|
|
res, key, minima, maxima);
|
|
val = NULL;
|
|
}
|
|
} else {
|
|
d (g_warning ("Using default value '%d'", default_val));
|
|
return default_val;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
gdouble
|
|
go_conf_load_double (GOConfNode *node, gchar const *key,
|
|
gdouble minima, gdouble maxima, gdouble default_val)
|
|
{
|
|
gdouble res = -1;
|
|
GVariant *val = NULL;
|
|
if (node) {
|
|
if (key && !strchr (key, '/') && !strchr (key, '.'))
|
|
val = go_conf_get (node, key, G_VARIANT_TYPE_DOUBLE);
|
|
else if (node->key)
|
|
val = go_conf_get (node, node->key, G_VARIANT_TYPE_DOUBLE);
|
|
}
|
|
if (val == NULL) {
|
|
GOConfNode *real_node = go_conf_get_node (node, key);
|
|
val = real_node? go_conf_get (real_node, real_node->key, G_VARIANT_TYPE_DOUBLE): NULL;
|
|
go_conf_free_node (real_node);
|
|
}
|
|
if (val != NULL) {
|
|
res = g_variant_get_double (val);
|
|
g_variant_unref (val);
|
|
if (res < minima || maxima < res) {
|
|
g_warning ("Invalid value '%g' for %s. If should be >= %g and <= %g",
|
|
res, key, minima, maxima);
|
|
val = NULL;
|
|
}
|
|
} else {
|
|
d (g_warning ("Using default value '%g'", default_val));
|
|
return default_val;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
gchar *
|
|
go_conf_load_string (GOConfNode *node, gchar const *key)
|
|
{
|
|
char *res = NULL;
|
|
if (node) {
|
|
if (key && !strchr (key, '/') && !strchr (key, '.'))
|
|
res = g_settings_get_string (node->settings, key);
|
|
else if (node->key)
|
|
res = g_settings_get_string (node->settings, node->key);
|
|
}
|
|
if (res == NULL) {
|
|
GOConfNode *real_node = go_conf_get_node (node, key);
|
|
res = (real_node)? g_settings_get_string (real_node->settings, real_node->key): NULL;
|
|
go_conf_free_node (real_node);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
GSList *
|
|
go_conf_load_str_list (GOConfNode *node, gchar const *key)
|
|
{
|
|
char **strs = NULL, **ptr;
|
|
GSList *list = NULL;
|
|
|
|
if (node) {
|
|
if (key && !strchr (key, '/') && !strchr (key, '.'))
|
|
strs = g_settings_get_strv (node->settings, key);
|
|
else if (node->key)
|
|
strs = g_settings_get_strv (node->settings, node->key);
|
|
}
|
|
if (strs == NULL) {
|
|
GOConfNode *real_node = go_conf_get_node (node, key);
|
|
strs = real_node? g_settings_get_strv (node->settings, real_node->key): NULL;
|
|
go_conf_free_node (real_node);
|
|
}
|
|
if (strs) {
|
|
for (ptr = strs; *ptr; ptr++)
|
|
list = g_slist_prepend (list, g_strdup (*ptr));
|
|
g_strfreev (strs);
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
gchar *
|
|
go_conf_get_short_desc (GOConfNode *node, gchar const *key)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
gchar *
|
|
go_conf_get_long_desc (GOConfNode *node, gchar const *key)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
gchar *
|
|
go_conf_get_value_as_str (GOConfNode *node, gchar const *key)
|
|
{
|
|
gchar *value_string;
|
|
GVariant *value = NULL;
|
|
|
|
if (node) {
|
|
if (key && !strchr (key, '/') && !strchr (key, '.'))
|
|
value = g_settings_get_value (node->settings, key);
|
|
else if (node->key)
|
|
value = g_settings_get_value (node->settings, node->key);
|
|
}
|
|
if (value == NULL) {
|
|
GOConfNode *real_node = go_conf_get_node (node, key);
|
|
value = real_node? g_settings_get_value (real_node->settings, real_node->key): NULL;
|
|
go_conf_free_node (real_node);
|
|
}
|
|
switch (g_variant_classify (value)) {
|
|
case 's':
|
|
value_string = g_strdup (g_variant_get_string (value, NULL));
|
|
break;
|
|
case 'i':
|
|
value_string = g_strdup_printf ("%i", g_variant_get_int32 (value));
|
|
break;
|
|
case 'd':
|
|
value_string = g_strdup_printf ("%f", g_variant_get_double (value));
|
|
break;
|
|
case 'b':
|
|
value_string = g_strdup (go_locale_boolean_name (g_variant_get_boolean (value)));
|
|
break;
|
|
default:
|
|
value_string = g_strdup ("ERROR FIXME");
|
|
break;
|
|
}
|
|
|
|
return value_string;
|
|
}
|
|
|
|
gboolean
|
|
go_conf_get_bool (GOConfNode *node, gchar const *key)
|
|
{
|
|
gboolean res, failed = node == NULL;
|
|
if (!failed) {
|
|
if (key && !strchr (key, '/') && !strchr (key, '.'))
|
|
res = g_settings_get_boolean (node->settings, key);
|
|
else if (node->key)
|
|
res = g_settings_get_boolean (node->settings, node->key);
|
|
else
|
|
failed = TRUE;
|
|
}
|
|
if (failed) {
|
|
GOConfNode *real_node = go_conf_get_node (node, key);
|
|
res = (real_node)? g_settings_get_boolean (real_node->settings, real_node->key): FALSE;
|
|
go_conf_free_node (real_node);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
gint
|
|
go_conf_get_int (GOConfNode *node, gchar const *key)
|
|
{
|
|
GOConfNode *real_node = go_conf_get_node (node, key);
|
|
gint res = (real_node)? g_settings_get_int (real_node->settings, real_node->key): 0;
|
|
go_conf_free_node (real_node);
|
|
return res;
|
|
}
|
|
|
|
gdouble
|
|
go_conf_get_double (GOConfNode *node, gchar const *key)
|
|
{
|
|
GOConfNode *real_node = go_conf_get_node (node, key);
|
|
gdouble res = (real_node)? g_settings_get_double (real_node->settings, real_node->key): 0.;
|
|
go_conf_free_node (real_node);
|
|
return res;
|
|
}
|
|
|
|
gchar *
|
|
go_conf_get_string (GOConfNode *node, gchar const *key)
|
|
{
|
|
GOConfNode *real_node = go_conf_get_node (node, key);
|
|
gchar *res = (real_node)? g_settings_get_string (real_node->settings, real_node->key): NULL;
|
|
go_conf_free_node (real_node);
|
|
return res;
|
|
}
|
|
|
|
GSList *
|
|
go_conf_get_str_list (GOConfNode *node, gchar const *key)
|
|
{
|
|
return go_conf_load_str_list (node, key);
|
|
}
|
|
|
|
#if 0
|
|
gboolean
|
|
go_conf_set_value_from_str (GOConfNode *node, gchar const *key, gchar const *val_str)
|
|
{
|
|
switch (go_conf_node_get_key_type (node, key)) {
|
|
case G_TYPE_STRING:
|
|
go_conf_set_string (node, key, val_str);
|
|
break;
|
|
case G_TYPE_FLOAT: {
|
|
GODateConventions const *conv = NULL; /* workbook_date_conv (state->wb); */
|
|
GnmValue *value = format_match_number (val_str, NULL, conv);
|
|
if (value != NULL) {
|
|
gnm_float the_float = value_get_as_float (value);
|
|
go_conf_set_double (node, key, the_float);
|
|
}
|
|
value_release (value);
|
|
break;
|
|
}
|
|
case G_TYPE_INT: {
|
|
GODateConventions const *conv = NULL; /* workbook_date_conv (state->wb); */
|
|
GnmValue *value = format_match_number (val_str, NULL, conv);
|
|
if (value != NULL) {
|
|
gint the_int = value_get_as_int (value);
|
|
go_conf_set_int (node, key, the_int);
|
|
}
|
|
value_release (value);
|
|
break;
|
|
}
|
|
case G_TYPE_BOOLEAN: {
|
|
GODateConventions const *conv = NULL; /* workbook_date_conv (state->wb); */
|
|
GnmValue *value = format_match_number (val_str, NULL, conv);
|
|
gboolean err, the_bool;
|
|
if (value != NULL) {
|
|
err = FALSE;
|
|
the_bool = value_get_as_bool (value, &err);
|
|
go_conf_set_bool (node, key, the_bool);
|
|
}
|
|
value_release (value);
|
|
break;
|
|
}
|
|
default:
|
|
g_warning ("Unsupported gconf type in preference dialog");
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
|
|
void
|
|
go_conf_remove_monitor (guint monitor_id)
|
|
{
|
|
GOConfClosure *cls = g_hash_table_lookup (closures, GUINT_TO_POINTER (monitor_id));
|
|
if (cls) {
|
|
g_signal_handler_disconnect (cls->node->settings, monitor_id);
|
|
g_hash_table_remove (closures, GUINT_TO_POINTER (monitor_id));
|
|
} else
|
|
g_warning ("unkown GOConfMonitor id.");
|
|
}
|
|
|
|
static void
|
|
cb_key_changed (GSettings *settings,
|
|
char *key,
|
|
GOConfClosure *cls)
|
|
{
|
|
char *real_key;
|
|
if (cls->key) {
|
|
if (strcmp (cls->key, key))
|
|
return; /* not the watched key */
|
|
real_key = g_strdup (cls->real_key);
|
|
} else
|
|
real_key = g_strconcat (cls->real_key, "/", key, NULL);
|
|
cls->monitor (cls->node, real_key , cls->data);
|
|
g_free (real_key);
|
|
}
|
|
|
|
guint
|
|
go_conf_add_monitor (GOConfNode *node, G_GNUC_UNUSED gchar const *key,
|
|
GOConfMonitorFunc monitor, gpointer data)
|
|
{
|
|
guint ret;
|
|
GOConfClosure *cls;
|
|
|
|
g_return_val_if_fail (node || key, 0);
|
|
g_return_val_if_fail (monitor != NULL, 0);
|
|
|
|
cls = g_new (GOConfClosure, 1);
|
|
cls->monitor = monitor;
|
|
cls->node = node;
|
|
cls->data = data;
|
|
cls->key = g_strdup (key? key: node->key);
|
|
cls->real_key = (key)? g_strconcat (node->path, '/', key, NULL): g_strdup (node->path);
|
|
ret = g_signal_connect
|
|
(node->settings,
|
|
"changed", G_CALLBACK (cb_key_changed),
|
|
cls);
|
|
|
|
g_hash_table_insert (closures, GUINT_TO_POINTER (ret), cls);
|
|
return ret;
|
|
}
|