1420 lines
32 KiB
C
1420 lines
32 KiB
C
/*
|
|
* mooutils/mooprefs.c
|
|
*
|
|
* Copyright (C) 2004-2006 by Yevgen Muntyan <muntyan@math.tamu.edu>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* See COPYING file that comes with this distribution.
|
|
*/
|
|
|
|
#include "mooutils/mooprefs.h"
|
|
#include "mooutils/moocompat.h"
|
|
#include "mooutils/eggregex.h"
|
|
#include "mooutils/moomarshals.h"
|
|
#include "mooutils/moomarkup.h"
|
|
#include "mooutils/mooutils-fs.h"
|
|
#include "mooutils/mooutils-gobject.h"
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
#define PREFS_ROOT "Prefs"
|
|
/* #define DEBUG_READWRITE 1 */
|
|
|
|
#if !GLIB_CHECK_VERSION(2,4,0)
|
|
#define g_value_take_string g_value_set_string_take_ownership
|
|
#endif
|
|
|
|
|
|
struct _MooPrefsPrivate {
|
|
GHashTable *data; /* char* -> Item* */
|
|
MooMarkupDoc *xml;
|
|
GList *closures;
|
|
GHashTable *closures_map; /* guint -> closures list link */
|
|
guint last_notify_id;
|
|
};
|
|
|
|
|
|
typedef struct {
|
|
GType type;
|
|
GValue value;
|
|
GValue default_value;
|
|
} PrefsItem;
|
|
|
|
|
|
static void prefs_new_key (MooPrefs *prefs,
|
|
const char *key,
|
|
GType value_type,
|
|
const GValue *default_value);
|
|
static void prefs_new_key_from_string (MooPrefs *prefs,
|
|
const char *key,
|
|
const char *value);
|
|
static void prefs_delete_key (MooPrefs *prefs,
|
|
const char *key);
|
|
static GType prefs_get_key_type (MooPrefs *prefs,
|
|
const char *key);
|
|
|
|
static const GValue *prefs_get (MooPrefs *prefs,
|
|
const char *key);
|
|
static const GValue *prefs_get_default (MooPrefs *prefs,
|
|
const char *key);
|
|
static void prefs_set (MooPrefs *prefs,
|
|
const char *key,
|
|
const GValue *value);
|
|
static void prefs_set_default (MooPrefs *prefs,
|
|
const char *key,
|
|
const GValue *value);
|
|
static PrefsItem *prefs_get_item (MooPrefs *prefs,
|
|
const char *key);
|
|
static void prefs_emit_notify (MooPrefs *prefs,
|
|
const char *key,
|
|
const GValue *value);
|
|
|
|
static PrefsItem *item_new (GType type,
|
|
const GValue *value,
|
|
const GValue *default_value);
|
|
static void item_free (PrefsItem *item);
|
|
static void item_set_type (PrefsItem *item,
|
|
GType type);
|
|
static const GValue *item_value (PrefsItem *item);
|
|
static const GValue *item_default_value (PrefsItem *item);
|
|
static void item_set (PrefsItem *item,
|
|
const GValue *value);
|
|
static void item_set_default (PrefsItem *item,
|
|
const GValue *value);
|
|
|
|
static void moo_prefs_finalize (GObject *object);
|
|
static void moo_prefs_new_key_from_string (const char *key,
|
|
const char *value);
|
|
|
|
|
|
/* MOO_TYPE_PREFS */
|
|
G_DEFINE_TYPE (MooPrefs, moo_prefs, G_TYPE_OBJECT)
|
|
|
|
|
|
static MooPrefs*
|
|
instance (void)
|
|
{
|
|
static MooPrefs *p = NULL;
|
|
|
|
if (!p)
|
|
p = g_object_new (MOO_TYPE_PREFS, NULL);
|
|
|
|
return p;
|
|
}
|
|
|
|
|
|
/***************************************************************************/
|
|
/* MooPrefs
|
|
*/
|
|
|
|
static void
|
|
moo_prefs_class_init (MooPrefsClass *klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
gobject_class->finalize = moo_prefs_finalize;
|
|
}
|
|
|
|
|
|
static void
|
|
moo_prefs_init (MooPrefs *prefs)
|
|
{
|
|
prefs->priv = g_new0 (MooPrefsPrivate, 1);
|
|
|
|
prefs->priv->data =
|
|
g_hash_table_new_full (g_str_hash, g_str_equal,
|
|
(GDestroyNotify) g_free,
|
|
(GDestroyNotify) item_free);
|
|
|
|
prefs->priv->closures_map = g_hash_table_new (g_direct_hash, NULL);
|
|
}
|
|
|
|
|
|
static void
|
|
moo_prefs_finalize (GObject *obj)
|
|
{
|
|
MooPrefs *prefs = MOO_PREFS (obj);
|
|
|
|
g_hash_table_destroy (prefs->priv->data);
|
|
g_free (prefs->priv);
|
|
prefs->priv = NULL;
|
|
|
|
G_OBJECT_CLASS(moo_prefs_parent_class)->finalize (obj);
|
|
}
|
|
|
|
|
|
char*
|
|
moo_prefs_make_key (const char *first_comp,
|
|
...)
|
|
{
|
|
char *key;
|
|
va_list var_args;
|
|
|
|
g_return_val_if_fail (first_comp != NULL, NULL);
|
|
|
|
va_start (var_args, first_comp);
|
|
key = moo_prefs_make_keyv (first_comp, var_args);
|
|
va_end (var_args);
|
|
|
|
return key;
|
|
}
|
|
|
|
|
|
char*
|
|
moo_prefs_make_keyv (const char *first_comp,
|
|
va_list var_args)
|
|
{
|
|
const char *comp;
|
|
GString *key = NULL;
|
|
|
|
g_return_val_if_fail (first_comp != NULL, NULL);
|
|
|
|
comp = first_comp;
|
|
|
|
while (comp)
|
|
{
|
|
if (!key)
|
|
{
|
|
key = g_string_new (comp);
|
|
}
|
|
else
|
|
{
|
|
g_string_append_c (key, '/');
|
|
g_string_append (key, comp);
|
|
}
|
|
|
|
comp = va_arg (var_args, const char*);
|
|
}
|
|
|
|
return g_string_free (key, FALSE);
|
|
}
|
|
|
|
|
|
MooMarkupDoc*
|
|
moo_prefs_get_markup (void)
|
|
{
|
|
MooPrefs *prefs = instance ();
|
|
|
|
if (!prefs->priv->xml)
|
|
prefs->priv->xml = moo_markup_doc_new ("Prefs");
|
|
|
|
return prefs->priv->xml;
|
|
}
|
|
|
|
|
|
void
|
|
moo_prefs_new_key (const char *key,
|
|
GType value_type,
|
|
const GValue *default_value)
|
|
{
|
|
g_return_if_fail (key != NULL);
|
|
g_return_if_fail (moo_value_type_supported (value_type));
|
|
g_return_if_fail (default_value != NULL);
|
|
g_return_if_fail (G_VALUE_TYPE (default_value) == value_type);
|
|
|
|
prefs_new_key (instance(), key, value_type, default_value);
|
|
}
|
|
|
|
|
|
GType
|
|
moo_prefs_get_key_type (const char *key)
|
|
{
|
|
g_return_val_if_fail (key != NULL, G_TYPE_NONE);
|
|
return prefs_get_key_type (instance(), key);
|
|
}
|
|
|
|
|
|
gboolean
|
|
moo_prefs_key_registered (const char *key)
|
|
{
|
|
g_return_val_if_fail (key != NULL, FALSE);
|
|
return prefs_get_item (instance(), key) != NULL;
|
|
}
|
|
|
|
|
|
const GValue*
|
|
moo_prefs_get (const char *key)
|
|
{
|
|
g_return_val_if_fail (key != NULL, NULL);
|
|
return prefs_get (instance(), key);
|
|
}
|
|
|
|
|
|
const GValue*
|
|
moo_prefs_get_default (const char *key)
|
|
{
|
|
g_return_val_if_fail (key != NULL, NULL);
|
|
return prefs_get_default (instance(), key);
|
|
}
|
|
|
|
|
|
void
|
|
moo_prefs_set (const char *key,
|
|
const GValue *value)
|
|
{
|
|
g_return_if_fail (key != NULL);
|
|
g_return_if_fail (value != NULL);
|
|
prefs_set (instance(), key, value);
|
|
}
|
|
|
|
|
|
void
|
|
moo_prefs_set_default (const char *key,
|
|
const GValue *value)
|
|
{
|
|
g_return_if_fail (key != NULL);
|
|
g_return_if_fail (value != NULL);
|
|
prefs_set_default (instance(), key, value);
|
|
}
|
|
|
|
|
|
void
|
|
moo_prefs_delete_key (const char *key)
|
|
{
|
|
g_return_if_fail (key != NULL);
|
|
prefs_delete_key (instance(), key);
|
|
}
|
|
|
|
|
|
static void
|
|
moo_prefs_new_key_from_string (const char *key,
|
|
const char *value)
|
|
{
|
|
g_return_if_fail (key != NULL);
|
|
g_return_if_fail (value != NULL);
|
|
prefs_new_key_from_string (instance(), key, value);
|
|
}
|
|
|
|
|
|
/***************************************************************************/
|
|
/* Prefs
|
|
*/
|
|
|
|
|
|
static void
|
|
prefs_new_key (MooPrefs *prefs,
|
|
const char *key,
|
|
GType type,
|
|
const GValue *default_value)
|
|
{
|
|
PrefsItem *item;
|
|
|
|
g_return_if_fail (key && key[0]);
|
|
g_return_if_fail (g_utf8_validate (key, -1, NULL));
|
|
g_return_if_fail (moo_value_type_supported (type));
|
|
g_return_if_fail (G_IS_VALUE (default_value));
|
|
g_return_if_fail (G_VALUE_TYPE (default_value) == type);
|
|
|
|
item = prefs_get_item (prefs, key);
|
|
|
|
if (!item)
|
|
{
|
|
item = item_new (type, default_value, default_value);
|
|
g_hash_table_insert (prefs->priv->data, g_strdup (key), item);
|
|
}
|
|
else
|
|
{
|
|
item_set_type (item, type);
|
|
item_set_default (item, default_value);
|
|
}
|
|
|
|
prefs_emit_notify (prefs, key, item_value (item));
|
|
}
|
|
|
|
|
|
static void
|
|
prefs_new_key_from_string (MooPrefs *prefs,
|
|
const char *key,
|
|
const char *value)
|
|
{
|
|
PrefsItem *item;
|
|
GValue val, default_val;
|
|
|
|
g_return_if_fail (key && key[0]);
|
|
g_return_if_fail (g_utf8_validate (key, -1, NULL));
|
|
|
|
item = prefs_get_item (prefs, key);
|
|
|
|
val.g_type = 0;
|
|
g_value_init (&val, G_TYPE_STRING);
|
|
g_value_set_static_string (&val, value);
|
|
default_val.g_type = 0;
|
|
g_value_init (&default_val, G_TYPE_STRING);
|
|
|
|
if (!item)
|
|
{
|
|
prefs_new_key (prefs, key, G_TYPE_STRING, &default_val);
|
|
item = prefs_get_item (prefs, key);
|
|
item_set (item, &val);
|
|
}
|
|
else
|
|
{
|
|
moo_value_convert (&val, &item->value);
|
|
}
|
|
|
|
g_value_unset (&val);
|
|
g_value_unset (&default_val);
|
|
prefs_emit_notify (prefs, key, item_value (item));
|
|
}
|
|
|
|
|
|
static GType
|
|
prefs_get_key_type (MooPrefs *prefs,
|
|
const char *key)
|
|
{
|
|
PrefsItem *item;
|
|
|
|
g_return_val_if_fail (key != NULL, G_TYPE_NONE);
|
|
|
|
item = prefs_get_item (prefs, key);
|
|
g_return_val_if_fail (item != NULL, G_TYPE_NONE);
|
|
|
|
return item->type;
|
|
}
|
|
|
|
|
|
static const GValue*
|
|
prefs_get (MooPrefs *prefs,
|
|
const char *key)
|
|
{
|
|
PrefsItem *item;
|
|
|
|
g_return_val_if_fail (key != NULL, NULL);
|
|
|
|
item = prefs_get_item (prefs, key);
|
|
return item ? item_value (item) : NULL;
|
|
}
|
|
|
|
|
|
static const GValue*
|
|
prefs_get_default (MooPrefs *prefs,
|
|
const char *key)
|
|
{
|
|
PrefsItem *item;
|
|
|
|
g_return_val_if_fail (key != NULL, NULL);
|
|
|
|
item = prefs_get_item (prefs, key);
|
|
g_return_val_if_fail (item != NULL, NULL);
|
|
|
|
return item_default_value (item);
|
|
}
|
|
|
|
|
|
static void
|
|
prefs_set (MooPrefs *prefs,
|
|
const char *key,
|
|
const GValue *value)
|
|
{
|
|
PrefsItem *item;
|
|
|
|
g_return_if_fail (key != NULL);
|
|
g_return_if_fail (G_IS_VALUE (value));
|
|
|
|
item = prefs_get_item (prefs, key);
|
|
|
|
if (!item)
|
|
{
|
|
g_warning ("key '%s' not registered", key);
|
|
return;
|
|
}
|
|
|
|
item_set (item, value);
|
|
prefs_emit_notify (prefs, key, item_value (item));
|
|
}
|
|
|
|
|
|
static void
|
|
prefs_set_default (MooPrefs *prefs,
|
|
const char *key,
|
|
const GValue *value)
|
|
{
|
|
PrefsItem *item;
|
|
|
|
g_return_if_fail (key != NULL);
|
|
g_return_if_fail (G_IS_VALUE (value));
|
|
|
|
item = prefs_get_item (prefs, key);
|
|
g_return_if_fail (item != NULL);
|
|
|
|
item_set_default (item, value);
|
|
}
|
|
|
|
|
|
static void
|
|
prefs_delete_key (MooPrefs *prefs,
|
|
const char *key)
|
|
{
|
|
PrefsItem *item;
|
|
|
|
g_return_if_fail (key != NULL);
|
|
|
|
item = prefs_get_item (prefs, key);
|
|
|
|
if (!item)
|
|
return;
|
|
|
|
g_hash_table_remove (prefs->priv->data, key);
|
|
prefs_emit_notify (prefs, key, NULL);
|
|
}
|
|
|
|
|
|
|
|
static PrefsItem*
|
|
prefs_get_item (MooPrefs *prefs,
|
|
const char *key)
|
|
{
|
|
g_return_val_if_fail (key != NULL, NULL);
|
|
return g_hash_table_lookup (prefs->priv->data, key);
|
|
}
|
|
|
|
|
|
/***************************************************************************/
|
|
/* PrefsItem
|
|
*/
|
|
|
|
static PrefsItem*
|
|
item_new (GType type,
|
|
const GValue *value,
|
|
const GValue *default_value)
|
|
{
|
|
PrefsItem *item;
|
|
|
|
g_return_val_if_fail (moo_value_type_supported (type), NULL);
|
|
g_return_val_if_fail (value && default_value, NULL);
|
|
g_return_val_if_fail (G_VALUE_TYPE (value) == type, NULL);
|
|
g_return_val_if_fail (G_VALUE_TYPE (default_value) == type, NULL);
|
|
|
|
item = g_new0 (PrefsItem, 1);
|
|
|
|
item->type = type;
|
|
|
|
g_value_init (&item->value, type);
|
|
g_value_copy (value, &item->value);
|
|
g_value_init (&item->default_value, type);
|
|
g_value_copy (default_value, &item->default_value);
|
|
|
|
return item;
|
|
}
|
|
|
|
|
|
static void
|
|
item_set_type (PrefsItem *item,
|
|
GType type)
|
|
{
|
|
g_return_if_fail (item != NULL);
|
|
g_return_if_fail (moo_value_type_supported (type));
|
|
|
|
if (type != item->type)
|
|
{
|
|
item->type = type;
|
|
moo_value_change_type (&item->value, type);
|
|
moo_value_change_type (&item->default_value, type);
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
item_free (PrefsItem *item)
|
|
{
|
|
if (item)
|
|
{
|
|
item->type = 0;
|
|
g_value_unset (&item->value);
|
|
g_value_unset (&item->default_value);
|
|
g_free (item);
|
|
}
|
|
}
|
|
|
|
|
|
static const GValue*
|
|
item_value (PrefsItem *item)
|
|
{
|
|
g_return_val_if_fail (item != NULL, NULL);
|
|
return &item->value;
|
|
}
|
|
|
|
|
|
static const GValue*
|
|
item_default_value (PrefsItem *item)
|
|
{
|
|
g_return_val_if_fail (item != NULL, NULL);
|
|
return &item->default_value;
|
|
}
|
|
|
|
|
|
static void
|
|
item_set (PrefsItem *item,
|
|
const GValue *value)
|
|
{
|
|
g_return_if_fail (item != NULL);
|
|
g_return_if_fail (G_IS_VALUE (value));
|
|
g_return_if_fail (item->type == G_VALUE_TYPE (value));
|
|
g_value_copy (value, &item->value);
|
|
}
|
|
|
|
|
|
static void
|
|
item_set_default (PrefsItem *item,
|
|
const GValue *value)
|
|
{
|
|
g_return_if_fail (item != NULL);
|
|
g_return_if_fail (G_IS_VALUE (value));
|
|
g_return_if_fail (item->type == G_VALUE_TYPE (value));
|
|
g_value_copy (value, &item->default_value);
|
|
}
|
|
|
|
|
|
/***************************************************************************/
|
|
/* PrefsNotify
|
|
*/
|
|
|
|
typedef union {
|
|
char *key;
|
|
EggRegex *regex;
|
|
} Pattern;
|
|
|
|
typedef struct {
|
|
guint id;
|
|
MooPrefsMatchType type;
|
|
Pattern pattern;
|
|
guint prefix_len;
|
|
MooPrefsNotify callback;
|
|
gpointer data;
|
|
GDestroyNotify notify;
|
|
guint blocked : 1;
|
|
} Closure;
|
|
|
|
static void pattern_free (Pattern p,
|
|
MooPrefsMatchType type);
|
|
|
|
static Closure *closure_new (MooPrefs *prefs,
|
|
const char *pattern,
|
|
MooPrefsMatchType match_type,
|
|
MooPrefsNotify callback,
|
|
gpointer data,
|
|
GDestroyNotify notify);
|
|
static void closure_free (Closure *closure);
|
|
static gboolean closure_match (Closure *closure,
|
|
const char *key);
|
|
static void closure_invoke (Closure *closure,
|
|
const char *key,
|
|
const GValue *value);
|
|
|
|
|
|
static void
|
|
prefs_emit_notify (MooPrefs *prefs,
|
|
const char *key,
|
|
const GValue *value)
|
|
{
|
|
GList *l;
|
|
|
|
g_object_ref (prefs);
|
|
|
|
for (l = prefs->priv->closures; l != NULL; l = l->next)
|
|
{
|
|
Closure *closure = l->data;
|
|
if (!closure->blocked && closure_match (closure, key))
|
|
closure_invoke (closure, key, value);
|
|
}
|
|
|
|
g_object_unref (prefs);
|
|
}
|
|
|
|
|
|
guint
|
|
moo_prefs_notify_connect (const char *pattern,
|
|
MooPrefsMatchType match_type,
|
|
MooPrefsNotify callback,
|
|
gpointer data,
|
|
GDestroyNotify notify)
|
|
{
|
|
Closure *closure;
|
|
MooPrefs *prefs = instance ();
|
|
|
|
g_return_val_if_fail (pattern != NULL, 0);
|
|
g_return_val_if_fail (match_type == MOO_PREFS_MATCH_KEY ||
|
|
match_type == MOO_PREFS_MATCH_PREFIX ||
|
|
match_type == MOO_PREFS_MATCH_REGEX, 0);
|
|
g_return_val_if_fail (callback != NULL, 0);
|
|
|
|
closure = closure_new (prefs, pattern, match_type, callback, data, notify);
|
|
g_return_val_if_fail (closure != NULL, 0);
|
|
|
|
prefs->priv->closures = g_list_prepend (prefs->priv->closures, closure);
|
|
g_hash_table_insert (prefs->priv->closures_map,
|
|
GUINT_TO_POINTER (closure->id),
|
|
prefs->priv->closures);
|
|
|
|
return closure->id;
|
|
}
|
|
|
|
|
|
static Closure*
|
|
closure_new (MooPrefs *prefs,
|
|
const char *pattern,
|
|
MooPrefsMatchType match_type,
|
|
MooPrefsNotify callback,
|
|
gpointer data,
|
|
GDestroyNotify notify)
|
|
{
|
|
EggRegex *regex;
|
|
Closure *closure;
|
|
GError *err = NULL;
|
|
|
|
closure = g_new (Closure, 1);
|
|
closure->type = match_type;
|
|
closure->callback = callback;
|
|
closure->data = data;
|
|
closure->notify = notify;
|
|
closure->blocked = FALSE;
|
|
|
|
closure->id = ++prefs->priv->last_notify_id;
|
|
|
|
switch (match_type) {
|
|
case MOO_PREFS_MATCH_REGEX:
|
|
regex = egg_regex_new (pattern, EGG_REGEX_EXTENDED, 0, &err);
|
|
if (err)
|
|
{
|
|
g_warning ("%s: %s", G_STRLOC, err->message);
|
|
g_error_free (err);
|
|
egg_regex_free (regex);
|
|
g_free (closure);
|
|
return NULL;
|
|
}
|
|
egg_regex_optimize (regex, &err);
|
|
if (err)
|
|
{
|
|
g_warning ("%s: %s", G_STRLOC, err->message);
|
|
g_error_free (err);
|
|
}
|
|
|
|
closure->pattern.regex = regex;
|
|
break;
|
|
|
|
case MOO_PREFS_MATCH_PREFIX:
|
|
closure->pattern.key = g_strdup (pattern);
|
|
closure->prefix_len = strlen (pattern);
|
|
break;
|
|
|
|
case MOO_PREFS_MATCH_KEY:
|
|
closure->pattern.key = g_strdup (pattern);
|
|
break;
|
|
|
|
default:
|
|
g_assert_not_reached ();
|
|
}
|
|
|
|
return closure;
|
|
}
|
|
|
|
|
|
static void
|
|
closure_free (Closure *closure)
|
|
{
|
|
if (closure)
|
|
{
|
|
if (closure->notify)
|
|
closure->notify (closure->data);
|
|
pattern_free (closure->pattern, closure->type);
|
|
g_free (closure);
|
|
}
|
|
}
|
|
|
|
|
|
static gboolean
|
|
closure_match (Closure *closure,
|
|
const char *key)
|
|
{
|
|
switch (closure->type) {
|
|
case MOO_PREFS_MATCH_KEY:
|
|
return !strcmp (key, closure->pattern.key);
|
|
|
|
case MOO_PREFS_MATCH_PREFIX:
|
|
if (closure->prefix_len)
|
|
return !strncmp (key, closure->pattern.key,
|
|
closure->prefix_len);
|
|
else
|
|
return TRUE;
|
|
|
|
case MOO_PREFS_MATCH_REGEX:
|
|
egg_regex_clear (closure->pattern.regex);
|
|
return egg_regex_match (closure->pattern.regex,
|
|
key, -1, 0) > 0;
|
|
|
|
default:
|
|
g_return_val_if_reached (FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
closure_invoke (Closure *closure,
|
|
const char *key,
|
|
const GValue *value)
|
|
{
|
|
closure->callback (key, value, closure->data);
|
|
}
|
|
|
|
|
|
static void
|
|
pattern_free (Pattern p,
|
|
MooPrefsMatchType type)
|
|
{
|
|
if (!p.key) return;
|
|
if (type == MOO_PREFS_MATCH_REGEX)
|
|
egg_regex_free (p.regex);
|
|
else
|
|
g_free (p.key);
|
|
}
|
|
|
|
|
|
static Closure*
|
|
find_closure (MooPrefs *prefs,
|
|
guint id)
|
|
{
|
|
GList *l;
|
|
|
|
l = g_hash_table_lookup (prefs->priv->closures_map,
|
|
GUINT_TO_POINTER (id));
|
|
if (l)
|
|
return l->data;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
|
|
gboolean
|
|
moo_prefs_notify_block (guint id)
|
|
{
|
|
Closure *c;
|
|
|
|
g_return_val_if_fail (id != 0, FALSE);
|
|
|
|
c = find_closure (instance(), id);
|
|
g_return_val_if_fail (c != NULL, FALSE);
|
|
|
|
c->blocked = TRUE;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
gboolean
|
|
moo_prefs_notify_unblock (guint id)
|
|
{
|
|
Closure *c;
|
|
|
|
g_return_val_if_fail (id != 0, FALSE);
|
|
|
|
c = find_closure (instance(), id);
|
|
g_return_val_if_fail (c != NULL, FALSE);
|
|
|
|
c->blocked = FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
gboolean
|
|
moo_prefs_notify_disconnect (guint id)
|
|
{
|
|
GList *l;
|
|
MooPrefs *prefs = instance ();
|
|
|
|
g_return_val_if_fail (id != 0, FALSE);
|
|
|
|
l = g_hash_table_lookup (prefs->priv->closures_map,
|
|
GUINT_TO_POINTER (id));
|
|
g_return_val_if_fail (l != NULL, FALSE);
|
|
|
|
g_hash_table_remove (prefs->priv->closures_map,
|
|
GUINT_TO_POINTER (id));
|
|
|
|
closure_free (l->data);
|
|
prefs->priv->closures =
|
|
g_list_delete_link (prefs->priv->closures, l);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/***************************************************************************/
|
|
/* Loading abd saving
|
|
*/
|
|
|
|
static void
|
|
process_element (MooMarkupElement *elm)
|
|
{
|
|
MooMarkupNode *child;
|
|
gboolean dir = FALSE;
|
|
|
|
if (elm->parent->type == MOO_MARKUP_DOC_NODE)
|
|
{
|
|
dir = TRUE;
|
|
}
|
|
else
|
|
{
|
|
for (child = elm->children; child != NULL; child = child->next)
|
|
if (child->type == MOO_MARKUP_ELEMENT_NODE)
|
|
dir = TRUE;
|
|
}
|
|
|
|
if (!dir)
|
|
{
|
|
char *path;
|
|
|
|
path = moo_markup_element_get_path (elm);
|
|
|
|
if (strlen (path) < strlen (PREFS_ROOT) + 2)
|
|
{
|
|
g_free (path);
|
|
g_return_if_reached ();
|
|
}
|
|
else
|
|
{
|
|
const char *key = path + strlen (PREFS_ROOT) + 1;
|
|
moo_prefs_new_key_from_string (key, elm->content);
|
|
|
|
#ifdef DEBUG_READWRITE
|
|
g_print ("key: '%s', val: '%s'\n", key, elm->content);
|
|
#endif
|
|
|
|
g_free (path);
|
|
return;
|
|
}
|
|
}
|
|
|
|
for (child = elm->children; child != NULL; child = child->next)
|
|
if (child->type == MOO_MARKUP_ELEMENT_NODE)
|
|
process_element (MOO_MARKUP_ELEMENT (child));
|
|
}
|
|
|
|
|
|
gboolean
|
|
moo_prefs_load (const char *file,
|
|
GError **error)
|
|
{
|
|
MooMarkupDoc *xml;
|
|
MooMarkupNode *root;
|
|
MooPrefs *prefs;
|
|
|
|
prefs = instance ();
|
|
|
|
g_return_val_if_fail (file != NULL, FALSE);
|
|
|
|
if (!g_file_test (file, G_FILE_TEST_EXISTS))
|
|
return TRUE;
|
|
|
|
xml = moo_markup_parse_file (file, error);
|
|
|
|
if (!xml)
|
|
return FALSE;
|
|
|
|
if (prefs->priv->xml)
|
|
{
|
|
g_warning ("%s: implement me", G_STRLOC);
|
|
moo_markup_doc_unref (prefs->priv->xml);
|
|
}
|
|
|
|
prefs->priv->xml = xml;
|
|
|
|
root = moo_markup_get_root_element (xml, PREFS_ROOT);
|
|
|
|
if (!root)
|
|
return TRUE;
|
|
|
|
process_element (MOO_MARKUP_ELEMENT (root));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
typedef struct {
|
|
MooMarkupDoc *xml;
|
|
MooMarkupNode *root;
|
|
} Stuff;
|
|
|
|
|
|
static void
|
|
write_item (const char *key,
|
|
PrefsItem *item,
|
|
Stuff *stuff)
|
|
{
|
|
const char *string = NULL;
|
|
|
|
g_return_if_fail (key != NULL && key[0] != 0);
|
|
g_return_if_fail (item != NULL && stuff != NULL);
|
|
g_return_if_fail (moo_value_type_supported (item->type));
|
|
|
|
if (moo_value_equal (item_value (item), item_default_value (item)))
|
|
{
|
|
#ifdef DEBUG_READWRITE
|
|
g_print ("skipping '%s'\n", key);
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
string = moo_value_convert_to_string (item_value (item));
|
|
g_return_if_fail (string != NULL);
|
|
|
|
if (!stuff->root)
|
|
stuff->root =
|
|
moo_markup_create_root_element (stuff->xml, PREFS_ROOT);
|
|
|
|
g_return_if_fail (stuff->root != NULL);
|
|
|
|
moo_markup_create_text_element (stuff->root, key, string);
|
|
|
|
#ifdef DEBUG_READWRITE
|
|
g_print ("writing key: '%s', val: '%s'\n", key, string);
|
|
#endif
|
|
}
|
|
|
|
|
|
static void
|
|
sync_xml (void)
|
|
{
|
|
MooPrefs *prefs = instance ();
|
|
MooMarkupDoc *xml;
|
|
MooMarkupNode *root;
|
|
Stuff stuff;
|
|
|
|
if (!prefs->priv->xml)
|
|
prefs->priv->xml = moo_markup_doc_new ("Prefs");
|
|
|
|
xml = prefs->priv->xml;
|
|
root = moo_markup_get_root_element (xml, PREFS_ROOT);
|
|
|
|
if (root)
|
|
moo_markup_delete_node (root);
|
|
|
|
stuff.xml = xml;
|
|
stuff.root = NULL;
|
|
|
|
g_hash_table_foreach (prefs->priv->data,
|
|
(GHFunc) write_item,
|
|
&stuff);
|
|
}
|
|
|
|
|
|
gboolean
|
|
moo_prefs_save (const char *file,
|
|
GError **error)
|
|
{
|
|
MooPrefs *prefs = instance ();
|
|
MooMarkupDoc *xml;
|
|
MooMarkupNode *node;
|
|
gboolean empty;
|
|
|
|
g_return_val_if_fail (file != NULL, FALSE);
|
|
|
|
sync_xml ();
|
|
|
|
xml = prefs->priv->xml;
|
|
|
|
g_return_val_if_fail (xml != NULL, FALSE);
|
|
|
|
empty = TRUE;
|
|
for (node = xml->children; empty && node != NULL; node = node->next)
|
|
if (MOO_MARKUP_IS_ELEMENT (node))
|
|
empty = FALSE;
|
|
|
|
if (empty)
|
|
{
|
|
if (g_file_test (file, G_FILE_TEST_EXISTS))
|
|
if (m_unlink (file))
|
|
g_critical ("%s: %s", G_STRLOC,
|
|
g_strerror (errno));
|
|
return TRUE;
|
|
}
|
|
|
|
return moo_markup_save_pretty (xml, file, 2, error);
|
|
}
|
|
|
|
|
|
/***************************************************************************/
|
|
/* Helpers
|
|
*/
|
|
|
|
void
|
|
moo_prefs_new_key_bool (const char *key,
|
|
gboolean default_val)
|
|
{
|
|
GValue val;
|
|
|
|
g_return_if_fail (key != NULL);
|
|
|
|
val.g_type = 0;
|
|
g_value_init (&val, G_TYPE_BOOLEAN);
|
|
g_value_set_boolean (&val, default_val);
|
|
|
|
moo_prefs_new_key (key, G_TYPE_BOOLEAN, &val);
|
|
}
|
|
|
|
|
|
void
|
|
moo_prefs_new_key_int (const char *key,
|
|
int default_val)
|
|
{
|
|
GValue val;
|
|
|
|
g_return_if_fail (key != NULL);
|
|
|
|
val.g_type = 0;
|
|
g_value_init (&val, G_TYPE_INT);
|
|
g_value_set_int (&val, default_val);
|
|
|
|
moo_prefs_new_key (key, G_TYPE_INT, &val);
|
|
}
|
|
|
|
|
|
void
|
|
moo_prefs_new_key_enum (const char *key,
|
|
GType enum_type,
|
|
int default_val)
|
|
{
|
|
GValue val;
|
|
|
|
g_return_if_fail (key != NULL);
|
|
|
|
val.g_type = 0;
|
|
g_value_init (&val, enum_type);
|
|
g_value_set_enum (&val, default_val);
|
|
|
|
moo_prefs_new_key (key, enum_type, &val);
|
|
}
|
|
|
|
|
|
void
|
|
moo_prefs_new_key_string (const char *key,
|
|
const char *default_val)
|
|
{
|
|
static GValue val;
|
|
|
|
g_return_if_fail (key != NULL);
|
|
|
|
val.g_type = 0;
|
|
g_value_init (&val, G_TYPE_STRING);
|
|
|
|
g_value_set_static_string (&val, default_val);
|
|
moo_prefs_new_key (key, G_TYPE_STRING, &val);
|
|
|
|
g_value_unset (&val);
|
|
}
|
|
|
|
|
|
void
|
|
moo_prefs_new_key_color (const char *key,
|
|
const GdkColor *default_val)
|
|
{
|
|
static GValue val;
|
|
|
|
g_return_if_fail (key != NULL);
|
|
|
|
if (!G_IS_VALUE (&val))
|
|
g_value_init (&val, GDK_TYPE_COLOR);
|
|
|
|
g_value_set_boxed (&val, default_val);
|
|
moo_prefs_new_key (key, GDK_TYPE_COLOR, &val);
|
|
}
|
|
|
|
|
|
const char*
|
|
moo_prefs_get_string (const char *key)
|
|
{
|
|
const GValue *val;
|
|
|
|
val = moo_prefs_get (key);
|
|
g_return_val_if_fail (val != NULL, NULL);
|
|
|
|
return g_value_get_string (val);
|
|
}
|
|
|
|
|
|
const char*
|
|
moo_prefs_get_filename (const char *key)
|
|
{
|
|
const char *utf8_val;
|
|
static char *val = NULL;
|
|
GError *error = NULL;
|
|
|
|
utf8_val = moo_prefs_get_string (key);
|
|
|
|
if (!utf8_val)
|
|
return NULL;
|
|
|
|
g_free (val);
|
|
val = g_filename_from_utf8 (utf8_val, -1, NULL, NULL, &error);
|
|
|
|
if (!val)
|
|
{
|
|
g_warning ("%s: could not convert '%s' to filename encoding",
|
|
G_STRLOC, utf8_val);
|
|
g_warning ("%s: %s", G_STRLOC, error->message);
|
|
g_error_free (error);
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
|
|
gboolean
|
|
moo_prefs_get_bool (const char *key)
|
|
{
|
|
const GValue *value;
|
|
|
|
g_return_val_if_fail (key != NULL, FALSE);
|
|
|
|
value = moo_prefs_get (key);
|
|
g_return_val_if_fail (value != NULL, FALSE);
|
|
|
|
return g_value_get_boolean (value);
|
|
}
|
|
|
|
|
|
gdouble
|
|
moo_prefs_get_number (const char *key)
|
|
{
|
|
const GValue *value;
|
|
|
|
g_return_val_if_fail (key != NULL, 0);
|
|
|
|
value = moo_prefs_get (key);
|
|
g_return_val_if_fail (value != NULL, 0);
|
|
|
|
return moo_value_convert_to_double (value);
|
|
}
|
|
|
|
|
|
const GdkColor*
|
|
moo_prefs_get_color (const char *key)
|
|
{
|
|
const GValue *value;
|
|
|
|
g_return_val_if_fail (key != NULL, NULL);
|
|
|
|
value = moo_prefs_get (key);
|
|
g_return_val_if_fail (value != NULL, NULL);
|
|
|
|
g_return_val_if_fail (G_VALUE_TYPE (value) == GDK_TYPE_COLOR, NULL);
|
|
return g_value_get_boxed (value);
|
|
}
|
|
|
|
|
|
int
|
|
moo_prefs_get_int (const char *key)
|
|
{
|
|
const GValue *value;
|
|
|
|
g_return_val_if_fail (key != NULL, 0);
|
|
|
|
value = moo_prefs_get (key);
|
|
g_return_val_if_fail (value != NULL, 0);
|
|
|
|
return g_value_get_int (value);
|
|
}
|
|
|
|
|
|
int
|
|
moo_prefs_get_enum (const char *key)
|
|
{
|
|
const GValue *value;
|
|
|
|
g_return_val_if_fail (key != NULL, 0);
|
|
|
|
value = moo_prefs_get (key);
|
|
g_return_val_if_fail (value != NULL, 0);
|
|
|
|
return g_value_get_enum (value);
|
|
}
|
|
|
|
|
|
void
|
|
moo_prefs_set_string (const char *key,
|
|
const char *val)
|
|
{
|
|
GValue gval;
|
|
|
|
g_return_if_fail (key != NULL);
|
|
|
|
gval.g_type = 0;
|
|
g_value_init (&gval, G_TYPE_STRING);
|
|
g_value_set_static_string (&gval, val);
|
|
moo_prefs_set (key, &gval);
|
|
g_value_unset (&gval);
|
|
}
|
|
|
|
|
|
void
|
|
moo_prefs_set_filename (const char *key,
|
|
const char *val)
|
|
{
|
|
char *utf8_val;
|
|
|
|
g_return_if_fail (key != NULL);
|
|
|
|
if (!val)
|
|
return moo_prefs_set_string (key, NULL);
|
|
|
|
utf8_val = g_filename_display_name (val);
|
|
|
|
if (!utf8_val)
|
|
{
|
|
g_warning ("%s: could not convert '%s' to utf8", G_STRLOC, val);
|
|
return;
|
|
}
|
|
|
|
moo_prefs_set_string (key, utf8_val);
|
|
g_free (utf8_val);
|
|
}
|
|
|
|
|
|
void
|
|
moo_prefs_set_number (const char *key,
|
|
double val)
|
|
{
|
|
GValue gval, double_val;
|
|
GType type;
|
|
|
|
g_return_if_fail (key != NULL);
|
|
g_return_if_fail (moo_prefs_key_registered (key));
|
|
|
|
type = moo_prefs_get_key_type (key);
|
|
|
|
gval.g_type = 0;
|
|
double_val.g_type = 0;
|
|
g_value_init (&gval, type);
|
|
g_value_init (&double_val, G_TYPE_DOUBLE);
|
|
|
|
g_value_set_double (&double_val, val);
|
|
moo_value_convert (&double_val, &gval);
|
|
moo_prefs_set (key, &gval);
|
|
}
|
|
|
|
|
|
void
|
|
moo_prefs_set_int (const char *key,
|
|
int val)
|
|
{
|
|
static GValue gval;
|
|
|
|
g_return_if_fail (key != NULL);
|
|
|
|
if (!G_IS_VALUE (&gval))
|
|
g_value_init (&gval, G_TYPE_INT);
|
|
|
|
g_value_set_int (&gval, val);
|
|
moo_prefs_set (key, &gval);
|
|
}
|
|
|
|
|
|
void
|
|
moo_prefs_set_bool (const char *key,
|
|
gboolean val)
|
|
{
|
|
static GValue gval;
|
|
|
|
g_return_if_fail (key != NULL);
|
|
|
|
if (!G_IS_VALUE (&gval))
|
|
g_value_init (&gval, G_TYPE_BOOLEAN);
|
|
|
|
g_value_set_boolean (&gval, val);
|
|
moo_prefs_set (key, &gval);
|
|
}
|
|
|
|
|
|
void
|
|
moo_prefs_set_color (const char *key,
|
|
const GdkColor *val)
|
|
{
|
|
static GValue gval;
|
|
|
|
g_return_if_fail (key != NULL);
|
|
|
|
if (!G_IS_VALUE (&gval))
|
|
g_value_init (&gval, GDK_TYPE_COLOR);
|
|
|
|
g_value_set_boxed (&gval, val);
|
|
moo_prefs_set (key, &gval);
|
|
}
|
|
|
|
|
|
void
|
|
moo_prefs_set_enum (const char *key,
|
|
int value)
|
|
{
|
|
GValue gval;
|
|
GType type;
|
|
|
|
g_return_if_fail (key != NULL);
|
|
|
|
type = moo_prefs_get_key_type (key);
|
|
g_return_if_fail (G_TYPE_IS_ENUM (type));
|
|
|
|
gval.g_type = 0;
|
|
g_value_init (&gval, type);
|
|
g_value_set_enum (&gval, value);
|
|
|
|
moo_prefs_set (key, &gval);
|
|
}
|
|
|
|
|
|
GType
|
|
moo_prefs_match_type_get_type (void)
|
|
{
|
|
static GType type = 0;
|
|
|
|
if (!type)
|
|
{
|
|
static const GFlagsValue values[] = {
|
|
{ MOO_PREFS_MATCH_KEY, (char*)"MOO_PREFS_MATCH_KEY", (char*)"match-key" },
|
|
{ MOO_PREFS_MATCH_PREFIX, (char*)"MOO_PREFS_MATCH_PREFIX", (char*)"match-prefix" },
|
|
{ MOO_PREFS_MATCH_REGEX, (char*)"MOO_PREFS_MATCH_REGEX", (char*)"match-regex" },
|
|
{ 0, NULL, NULL }
|
|
};
|
|
type = g_flags_register_static ("MooPrefsMatchType", values);
|
|
}
|
|
|
|
return type;
|
|
}
|
|
|
|
|
|
static void
|
|
add_key (const char *key,
|
|
G_GNUC_UNUSED PrefsItem *item,
|
|
GPtrArray *array)
|
|
{
|
|
g_ptr_array_add (array, g_strdup (key));
|
|
}
|
|
|
|
char**
|
|
moo_prefs_list_keys (guint *n_keys)
|
|
{
|
|
MooPrefs *prefs = instance ();
|
|
GPtrArray *array;
|
|
|
|
array = g_ptr_array_new ();
|
|
|
|
g_hash_table_foreach (prefs->priv->data,
|
|
(GHFunc) add_key,
|
|
array);
|
|
|
|
if (!array->len)
|
|
{
|
|
g_ptr_array_free (array, TRUE);
|
|
if (n_keys)
|
|
*n_keys = 0;
|
|
return NULL;
|
|
}
|
|
else
|
|
{
|
|
if (n_keys)
|
|
*n_keys = array->len;
|
|
g_ptr_array_add (array, NULL);
|
|
return (char**) g_ptr_array_free (array, FALSE);
|
|
}
|
|
}
|