[nautilus-actions] NASettings: configuration monitoring



commit f94afc2325fd5d4a40ea70e524a6778e1d03d8dc
Author: Pierre <pierre vfedora13 virtuals pwi>
Date:   Wed Jan 12 11:37:52 2011 +0100

    NASettings: configuration monitoring

 ChangeLog              |    3 +
 src/core/na-settings.c |  924 ++++++++++++++++++++++++++++--------------------
 src/core/na-settings.h |   13 +-
 3 files changed, 559 insertions(+), 381 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 979e5a9..c36f3ab 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -35,6 +35,9 @@
 
 	* run-autogen.sh: Add sysconfdir argument to autogen.sh
 
+	* src/core/na-settings.c:
+	* src/core/na-settings.h: Configuration monitoring.
+
 	* src/plugin-menu/nautilus-actions.c:
 	Monitor all runtime preferences with only one callback.
 
diff --git a/src/core/na-settings.c b/src/core/na-settings.c
index 4b4f2ef..3421e97 100644
--- a/src/core/na-settings.c
+++ b/src/core/na-settings.c
@@ -33,9 +33,11 @@
 #endif
 
 #include <gio/gio.h>
+#include <stdlib.h>
 #include <string.h>
+#include <strings.h>
 
-#include <api/na-data-types.h>
+#include <api/na-boxed.h>
 #include <api/na-core-utils.h>
 
 #include "na-settings.h"
@@ -46,34 +48,55 @@ struct _NASettingsClassPrivate {
 	void *empty;						/* so that gcc -pedantic is happy */
 };
 
-/* private instance data
+/* The characteristics of a configuration file.
+ * We manage two configuration files:
+ * - the global configuration file handles mandatory preferences;
+ * - the user configuration file handles.. well, user preferences.
  */
-struct _NASettingsPrivate {
-	gboolean  dispose_has_run;
-
-	/* the global configuration file, which handles mandatory preferences
-	 */
-	GKeyFile     *global_conf;
-	GFileMonitor *global_monitor;
-	gulong        global_handler;
-
-	/* the user configuration file
-	 */
-	GKeyFile     *user_conf;
-	GFileMonitor *user_monitor;
-	gulong        user_handler;
+typedef struct {
+	gchar        *fname;
+	GKeyFile     *key_file;
+	GFileMonitor *monitor;
+	gulong        handler;
+}
+	KeyFile;
 
-	/* the registered consumers of monitoring events
-	 * as a list of 'Consumer' structs
-	 */
-	GList        *consumers;
+/* The configuration content is handled as a GList of NAKeyValue structs.
+ * This list is loaded at initialization time, and then compared each
+ * time our file monitors signal us that a change has occured.
+ */
+typedef struct {
+	gchar   *group;
+	gchar   *key;
+	gboolean mandatory;
+	NABoxed *boxed;
+}
+	KeyValue;
+
+/* Each consumer may register a callback function which will be triggered
+ * when a key is modified.
+ * The monitored key may be a real key of the file, but also be a composite
+ * key (e.g. NA_SETTINGS_RUNTIME_IO_PROVIDER_READ_STATUS monitors the
+ * 'readable' key of all i/o providers).
+ * Note that we actually monitor the _user_view_ of the configuration:
+ * e.g. if a key has a mandatory value in global conf, then the same
+ * key in user conf will just be ignored.
+ */
+typedef struct {
+	gchar    *monitored_key;
+	GCallback callback;
+	gpointer  user_data;
+}
+	Consumer;
 
-	/* for each monitoring key, we keep here the last known value
-	 * so that we are able to detect changes when the configuration
-	 * files is 'globally' changed
-	 * as a list of 'Entry' structs
-	 */
-	GList        *entries;
+/* private instance data
+ */
+struct _NASettingsPrivate {
+	gboolean dispose_has_run;
+	KeyFile *mandatory;
+	KeyFile *user;
+	GList   *content;
+	GList   *consumers;
 };
 
 #define GROUP_NACT						"nact"
@@ -85,39 +108,66 @@ struct _NASettingsPrivate {
 typedef struct {
 	const gchar *key;
 	const gchar *group;
-	guint        data_type;				/* picked up from NADataFactory */
+	guint        type;
 	const gchar *default_value;
 }
 	KeyDef;
 
 static const KeyDef st_def_keys[] = {
-	{ NA_SETTINGS_RUNTIME_IO_PROVIDERS_READ_ORDER, GROUP_RUNTIME, NAFD_TYPE_STRING_LIST, "" },
-	{ NA_SETTINGS_RUNTIME_ITEMS_ADD_ABOUT_ITEM,    GROUP_RUNTIME, NAFD_TYPE_BOOLEAN,     "true" },
-	{ NA_SETTINGS_RUNTIME_ITEMS_CREATE_ROOT_MENU,  GROUP_RUNTIME, NAFD_TYPE_BOOLEAN,     "true" },
-	{ NA_SETTINGS_RUNTIME_ITEMS_LEVEL_ZERO_ORDER,  GROUP_RUNTIME, NAFD_TYPE_STRING_LIST, "" },
-	{ NA_SETTINGS_RUNTIME_ITEMS_LIST_ORDER_MODE,   GROUP_RUNTIME, NAFD_TYPE_MAP,         "AscendingOrder" },
+	{ NA_SETTINGS_RUNTIME_IO_PROVIDERS_READ_ORDER,   GROUP_RUNTIME,     NA_BOXED_TYPE_STRING_LIST, "" },
+	{ NA_SETTINGS_RUNTIME_ITEMS_ADD_ABOUT_ITEM,      GROUP_RUNTIME,     NA_BOXED_TYPE_BOOLEAN,     "true" },
+	{ NA_SETTINGS_RUNTIME_ITEMS_CREATE_ROOT_MENU,    GROUP_RUNTIME,     NA_BOXED_TYPE_BOOLEAN,     "true" },
+	{ NA_SETTINGS_RUNTIME_ITEMS_LEVEL_ZERO_ORDER,    GROUP_RUNTIME,     NA_BOXED_TYPE_STRING_LIST, "" },
+	{ NA_SETTINGS_RUNTIME_ITEMS_LIST_ORDER_MODE,     GROUP_RUNTIME,     NA_BOXED_TYPE_STRING,      "AscendingOrder" },
+	{ "assistant-esc-confirm",                       GROUP_NACT,        NA_BOXED_TYPE_BOOLEAN,     "true" },
+	{ "assistant-esc-quit",                          GROUP_NACT,        NA_BOXED_TYPE_BOOLEAN,     "true" },
+	{ "capability-add-capability-dialog-size",       GROUP_NACT,        NA_BOXED_TYPE_UINT_LIST,   "" },
+	{ "command-command-chooser-dialog-size",         GROUP_NACT,        NA_BOXED_TYPE_UINT_LIST,   "" },
+	{ "command-command-chooser-last-folder-uri",     GROUP_NACT,        NA_BOXED_TYPE_STRING,      "" },
+	{ "command-legend-dialog-size",                  GROUP_NACT,        NA_BOXED_TYPE_UINT_LIST,   "" },
+	{ "command-working-dir-chooser-dialog-size",     GROUP_NACT,        NA_BOXED_TYPE_UINT_LIST,   "" },
+	{ "command-working-dir-chooser-last-folder-uri", GROUP_NACT,        NA_BOXED_TYPE_STRING,      "" },
+	{ "environment-show-if-running-dialog-size",     GROUP_NACT,        NA_BOXED_TYPE_UINT_LIST,   "" },
+	{ "environment-show-if-running-last-folder-uri", GROUP_NACT,        NA_BOXED_TYPE_STRING,      "" },
+	{ "environment-try-exec-dialog-size",            GROUP_NACT,        NA_BOXED_TYPE_UINT_LIST,   "" },
+	{ "environment-try-exec-last-folder-uri",        GROUP_NACT,        NA_BOXED_TYPE_STRING,      "" },
+	{ "export-assistant-dialog-size",                GROUP_NACT,        NA_BOXED_TYPE_UINT_LIST,   "" },
+	{ "export-last-folder-uri",                      GROUP_NACT,        NA_BOXED_TYPE_STRING,      "" },
+	{ "export-preferred-format",                     GROUP_NACT,        NA_BOXED_TYPE_STRING,      "Desktop1" },
+	{ "folder-chooser-dialog-size",                  GROUP_NACT,        NA_BOXED_TYPE_UINT_LIST,   "" },
+	{ "folder-last-path",                            GROUP_NACT,        NA_BOXED_TYPE_STRING,      "" },
+	{ "import-ask-user-dialog-size",                 GROUP_NACT,        NA_BOXED_TYPE_UINT_LIST,   "" },
+	{ "import-ask-user-last-mode",                   GROUP_NACT,        NA_BOXED_TYPE_STRING,      "" },
+	{ "import-assistant-dialog-size",                GROUP_NACT,        NA_BOXED_TYPE_UINT_LIST,   "" },
+	{ "import-last-folder-uri",                      GROUP_NACT,        NA_BOXED_TYPE_STRING,      "" },
+	{ "import-mode-keep-last-choice",                GROUP_NACT,        NA_BOXED_TYPE_BOOLEAN,     "false" },
+	{ "import-preferred-mode",                       GROUP_NACT,        NA_BOXED_TYPE_STRING,      "" },
+	{ "io-providers-write-order",                    GROUP_NACT,        NA_BOXED_TYPE_STRING_LIST, "" },
+	{ "item-icon-browse-last-path",                  GROUP_NACT,        NA_BOXED_TYPE_STRING,      "" },
+	{ "item-icon-chooser-dialog-size",               GROUP_NACT,        NA_BOXED_TYPE_UINT_LIST,   "" },
+	{ "main-pane-width",                             GROUP_NACT,        NA_BOXED_TYPE_UINT,        "200" },
+	{ "main-save-auto",                              GROUP_NACT,        NA_BOXED_TYPE_BOOLEAN,     "false" },
+	{ "main-save-period",                            GROUP_NACT,        NA_BOXED_TYPE_UINT,        "5" },
+	{ "main-toolbar-edit-display",                   GROUP_NACT,        NA_BOXED_TYPE_BOOLEAN,     "true" },
+	{ "main-toolbar-file-display",                   GROUP_NACT,        NA_BOXED_TYPE_BOOLEAN,     "true" },
+	{ "main-toolbar-help-display",                   GROUP_NACT,        NA_BOXED_TYPE_BOOLEAN,     "true" },
+	{ "main-toolbar-tools-display",                  GROUP_NACT,        NA_BOXED_TYPE_BOOLEAN,     "false" },
+	{ "main-window-size",                            GROUP_NACT,        NA_BOXED_TYPE_UINT_LIST,   "" },
+	{ "preferences-dialog-size",                     GROUP_NACT,        NA_BOXED_TYPE_UINT_LIST,   "" },
+	{ "relabel-when-duplicate-action",               GROUP_NACT,        NA_BOXED_TYPE_BOOLEAN,     "false" },
+	{ "relabel-when-duplicate-menu",                 GROUP_NACT,        NA_BOXED_TYPE_BOOLEAN,     "false" },
+	{ "relabel-when-duplicate-profile",              GROUP_NACT,        NA_BOXED_TYPE_BOOLEAN,     "false" },
+	{ "scheme-add-scheme-dialog-size",               GROUP_NACT,        NA_BOXED_TYPE_UINT_LIST,   "" },
+	{ "scheme-default-list",                         GROUP_NACT,        NA_BOXED_TYPE_STRING_LIST, "" },
+	{ IO_PROVIDER_READABLE,                          GROUP_IO_PROVIDER, NA_BOXED_TYPE_BOOLEAN,     "true" },
+	{ "writable",                                    GROUP_IO_PROVIDER, NA_BOXED_TYPE_BOOLEAN,     "true" },
 	{ 0 }
 };
 
-typedef void ( *global_fn )( gboolean global, void *user_data );
-
-typedef struct {
-	gchar    *key;
-	GCallback callback;
-	gpointer  user_data;
-}
-	Consumer;
-
-typedef struct {
-	gchar   *group;
-	gchar   *key;
-	void    *value;
-	gboolean global;
-	guint    type;
-}
-	Entry;
-
-static GObjectClass *st_parent_class = NULL;
+static GObjectClass *st_parent_class    = NULL;
+static GTimeVal      st_last_event;
+static guint         st_event_source_id = 0;
+static gint          st_burst_timeout   = 100;		/* burst timeout in msec */
 
 static GType     register_type( void );
 static void      class_init( NASettingsClass *klass );
@@ -125,17 +175,23 @@ static void      instance_init( GTypeInstance *instance, gpointer klass );
 static void      instance_dispose( GObject *object );
 static void      instance_finalize( GObject *object );
 
+static GList    *content_diff( GList *old, GList *new );
+static GList    *content_load( NASettings *settings );
+static GList    *content_load_keys( NASettings *settings, GList *content, KeyFile *key_file, gboolean mandatory );
 static KeyDef   *get_key_def( const gchar *key );
+static GSList   *get_string_list_ex( NASettings *settings, const gchar *group, const gchar *key, gboolean *found, gboolean *global );
+#if 0
 static gchar    *get_string_ex( NASettings *settings, const gchar *group, const gchar *key, const gchar *default_value, gboolean *found, gboolean *global );
-static GSList   *get_string_list_ex( NASettings *settings, const gchar *group, const gchar *key, const gchar *default_value, gboolean *found, gboolean *global );
-static GKeyFile *initialize_settings( NASettings* settings, const gchar *dir, GFileMonitor **monitor, gulong *handler );
-static void      monitor_io_provider_read_status( NASettings *settings );
-static void      monitor_io_provider_read_status_conf( NASettings *settings, GKeyFile *key_file, gboolean global );
-static void      monitor_key( NASettings *settings, const gchar *key );
-static void      monitor_key_add( NASettings *settings, const gchar *group, const gchar *key, gpointer value, gboolean global, guint type );
-static void      on_conf_changed( GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type, NASettings *settings );
+#endif
+static KeyFile  *key_file_new( NASettings *settings, const gchar *dir );
+static void      on_keyfile_changed( GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type, NASettings *settings );
+static gboolean  on_keyfile_changed_timeout( NASettings *settings );
+static KeyValue *peek_key_value_from_content( GList *content, const gchar *group, const gchar *key );
+static KeyValue *read_key_value_from_key_file( GKeyFile *key_file, const gchar *group, const gchar *key, const KeyDef *key_def );
 static void      release_consumer( Consumer *consumer );
-static void      release_entry( Entry *entry );
+static void      release_key_file( KeyFile *key_file );
+static void      release_key_value( KeyValue *value );
+static gulong    time_val_diff( const GTimeVal *recent, const GTimeVal *old );
 
 GType
 na_settings_get_type( void )
@@ -207,12 +263,9 @@ instance_init( GTypeInstance *instance, gpointer klass )
 	self->private = g_new0( NASettingsPrivate, 1 );
 
 	self->private->dispose_has_run = FALSE;
-	self->private->global_conf = NULL;
-	self->private->global_monitor = NULL;
-	self->private->global_handler = 0;
-	self->private->user_conf = NULL;
-	self->private->user_monitor = NULL;
-	self->private->user_handler = 0;
+	self->private->mandatory = NULL;
+	self->private->user = NULL;
+	self->private->content = NULL;
 	self->private->consumers = NULL;
 }
 
@@ -232,23 +285,8 @@ instance_dispose( GObject *object )
 
 		self->private->dispose_has_run = TRUE;
 
-		g_key_file_free( self->private->global_conf );
-		if( self->private->global_monitor ){
-			if( self->private->global_handler ){
-				g_signal_handler_disconnect( self->private->global_monitor, self->private->global_handler );
-			}
-			g_file_monitor_cancel( self->private->global_monitor );
-			g_object_unref( self->private->global_monitor );
-		}
-
-		g_key_file_free( self->private->user_conf );
-		if( self->private->user_monitor ){
-			if( self->private->user_handler ){
-				g_signal_handler_disconnect( self->private->user_monitor, self->private->user_handler );
-			}
-			g_file_monitor_cancel( self->private->user_monitor );
-			g_object_unref( self->private->user_monitor );
-		}
+		release_key_file( self->private->mandatory );
+		release_key_file( self->private->user );
 
 		if( G_OBJECT_CLASS( st_parent_class )->dispose ){
 			G_OBJECT_CLASS( st_parent_class )->dispose( object );
@@ -268,12 +306,12 @@ instance_finalize( GObject *object )
 
 	g_debug( "%s: object=%p", thisfn, ( void * ) object );
 
+	g_list_foreach( self->private->content, ( GFunc ) release_key_value, NULL );
+	g_list_free( self->private->content );
+
 	g_list_foreach( self->private->consumers, ( GFunc ) release_consumer, NULL );
 	g_list_free( self->private->consumers );
 
-	g_list_foreach( self->private->entries, ( GFunc ) release_entry, NULL );
-	g_list_free( self->private->entries );
-
 	g_free( self->private );
 
 	/* chain call to parent class */
@@ -297,19 +335,15 @@ na_settings_new( void )
 	settings = g_object_new( NA_SETTINGS_TYPE, NULL );
 
 	dir = g_build_filename( SYSCONFDIR, "xdg", NULL );
-	settings->private->global_conf = initialize_settings(
-			settings, dir,
-			&settings->private->global_monitor,
-			&settings->private->global_handler );
+	settings->private->mandatory = key_file_new( settings, dir );
 	g_free( dir );
 
 	dir = g_build_filename( g_get_home_dir(), ".config", NULL );
-	settings->private->user_conf = initialize_settings(
-			settings, dir,
-			&settings->private->user_monitor,
-			&settings->private->user_handler );
+	settings->private->user = key_file_new( settings, dir );
 	g_free( dir );
 
+	settings->private->content = content_load( settings );
+
 	return( settings );
 }
 
@@ -327,30 +361,24 @@ na_settings_new( void )
 void
 na_settings_register_key_callback( NASettings *settings, const gchar *key, GCallback callback, gpointer user_data )
 {
+	static const gchar *thisfn = "na_settings_register_key_callback";
+
 	g_return_if_fail( NA_IS_SETTINGS( settings ));
 
 	if( !settings->private->dispose_has_run ){
+		g_debug( "%s: settings=%p, key=%s, callback=%p, user_data=%p",
+				thisfn, ( void * ) settings, key, ( void * ) callback, ( void * ) user_data );
 
 		Consumer *consumer = g_new0( Consumer, 1 );
 
-		consumer->key = g_strdup( key );
+		consumer->monitored_key = g_strdup( key );
 		consumer->callback = callback;
 		consumer->user_data = user_data;
 		settings->private->consumers = g_list_prepend( settings->private->consumers, consumer );
-
-		if( !strcmp( key, NA_SETTINGS_RUNTIME_IO_PROVIDER_READ_STATUS )){
-			monitor_io_provider_read_status( settings );
-
-		} else if( !strcmp( key, NA_SETTINGS_RUNTIME_IO_PROVIDERS_READ_ORDER ) ||
-					!strcmp( key, NA_SETTINGS_RUNTIME_ITEMS_ADD_ABOUT_ITEM ) ||
-					!strcmp( key, NA_SETTINGS_RUNTIME_ITEMS_CREATE_ROOT_MENU ) ||
-					!strcmp( key, NA_SETTINGS_RUNTIME_ITEMS_LEVEL_ZERO_ORDER )){
-			monitor_key( settings, key );
-		}
 	}
-
 }
 
+#if 0
 /**
  * na_settings_register_global_callback:
  * @settings: this #NASettings instance.
@@ -377,6 +405,7 @@ na_settings_register_global_callback( NASettings *settings, GCallback callback,
 	}
 
 }
+#endif
 
 /**
  * na_settings_get_boolean:
@@ -385,20 +414,21 @@ na_settings_register_global_callback( NASettings *settings, GCallback callback,
  * @found: if not %NULL, a pointer to a gboolean in which we will store
  *  whether the searched @key has been found (%TRUE), or if the returned
  *  value comes from default (%FALSE).
- * @global: if not %NULL, a pointer to a gboolean in which we will store
- *  whether the returned value has been readen from global preferences
- *  (%TRUE), or from the user preferences (%FALSE). Global preferences
- *  are usually read-only. When the @key has not been found, @global
- *  is set to %FALSE.
+ * @mandatory: if not %NULL, a pointer to a gboolean in which we will store
+ *  whether the returned value has been readen from mandatory preferences
+ *  (%TRUE), or from the user preferences (%FALSE). When the @key has not
+ *  been found, @mandatory is set to %FALSE.
+ *
+ * This function should only be called for unambiguous keys; the resultat
+ * is otherwise undefined (and rather unpredictable).
  *
  * Returns: the value of the key, of its default value if not found.
  *
  * Since: 3.1.0
  */
 gboolean
-na_settings_get_boolean( NASettings *settings, const gchar *key, gboolean *found, gboolean *global )
+na_settings_get_boolean( NASettings *settings, const gchar *key, gboolean *found, gboolean *mandatory )
 {
-	static const gchar *thisfn = "na_settings_get_boolean";
 	gboolean value;
 	KeyDef *key_def;
 
@@ -408,18 +438,15 @@ na_settings_get_boolean( NASettings *settings, const gchar *key, gboolean *found
 	if( found ){
 		*found = FALSE;
 	}
-	if( global ){
-		*global = FALSE;
+	if( mandatory ){
+		*mandatory = FALSE;
 	}
 
 	if( !settings->private->dispose_has_run ){
 
 		key_def = get_key_def( key );
 		if( key_def ){
-			value = na_settings_get_boolean_ex( settings, key_def->group, key, key_def->default_value, found, global );
-
-		} else {
-			g_warning( "%s: no KeyDef found for key=%s", thisfn, key );
+			value = na_settings_get_boolean_ex( settings, key_def->group, key, found, mandatory );
 		}
 	}
 
@@ -431,27 +458,25 @@ na_settings_get_boolean( NASettings *settings, const gchar *key, gboolean *found
  * @settings: this #NASettings instance.
  * @group: the group where the @key is to be searched for.
  * @key: the key whose value is to be returned.
- * @default_value: as 'true' or 'false', may be %NULL which defaults to %FALSE.
  * @found: if not %NULL, a pointer to a gboolean in which we will store
  *  whether the searched @key has been found (%TRUE), or if the returned
  *  value comes from default (%FALSE).
- * @global: if not %NULL, a pointer to a gboolean in which we will store
- *  whether the returned value has been readen from global preferences
- *  (%TRUE), or from the user preferences (%FALSE). Global preferences
- *  are usually read-only. When the @key has not been found, @global
- *  is set to %FALSE.
+ * @mandatory: if not %NULL, a pointer to a gboolean in which we will store
+ *  whether the returned value has been readen from mandatory preferences
+ *  (%TRUE), or from the user preferences (%FALSE). When the @key has not
+ *  been found, @mandatory is set to %FALSE.
  *
  * Returns: the value of the key, of its default value if not found.
  *
  * Since: 3.1.0
  */
 gboolean
-na_settings_get_boolean_ex( NASettings *settings, const gchar *group, const gchar *key, const gchar *default_value, gboolean *found, gboolean *global )
+na_settings_get_boolean_ex( NASettings *settings, const gchar *group, const gchar *key, gboolean *found, gboolean *mandatory )
 {
-	static const gchar *thisfn = "na_settings_get_boolean_ex";
 	gboolean value;
-	GError *error;
 	gboolean has_entry;
+	KeyDef *key_def;
+	KeyValue *key_value;
 
 	g_return_val_if_fail( NA_IS_SETTINGS( settings ), FALSE );
 
@@ -459,49 +484,39 @@ na_settings_get_boolean_ex( NASettings *settings, const gchar *group, const gcha
 	if( found ){
 		*found = FALSE;
 	}
-	if( global ){
-		*global = FALSE;
+	if( mandatory ){
+		*mandatory = FALSE;
 	}
 
 	if( !settings->private->dispose_has_run ){
 
-		error = NULL;
-		has_entry = TRUE;
-		value = g_key_file_get_boolean( settings->private->global_conf, group, key, &error );
-		if( error ){
+		key_def = get_key_def( key );
+		if( key_def ){
 			has_entry = FALSE;
-			if( error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND && error->code != G_KEY_FILE_ERROR_GROUP_NOT_FOUND ){
-				g_warning( "%s: (global) %s", thisfn, error->message );
-			}
-			g_error_free( error );
-			error = NULL;
-
-		} else {
-			if( found ){
-				*found = TRUE;
-			}
-			if( global ){
-				*global = TRUE;
-			}
-		}
-		if( !has_entry ){
-			value = g_key_file_get_boolean( settings->private->user_conf, group, key, &error );
-			if( error ){
-				if( error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND && error->code != G_KEY_FILE_ERROR_GROUP_NOT_FOUND ){
-					g_warning( "%s: (user) %s", thisfn, error->message );
-				}
-				g_error_free( error );
-				error = NULL;
-			} else {
+			key_value = read_key_value_from_key_file( settings->private->mandatory->key_file, group, key, key_def );
+			if( key_value ){
 				has_entry = TRUE;
 				if( found ){
 					*found = TRUE;
 				}
+				if( mandatory ){
+					*mandatory = TRUE;
+				}
 			}
-		}
-		if( !has_entry ){
-			if( default_value ){
-				value = ( strcmp( default_value, "true" ) == 0 );
+			if( !has_entry ){
+				key_value = read_key_value_from_key_file( settings->private->user->key_file, group, key, key_def );
+				if( key_value ){
+					has_entry = TRUE;
+					if( found ){
+						*found = TRUE;
+					}
+				}
+			}
+			if( has_entry ){
+				value = na_boxed_get_boolean( key_value->boxed );
+				release_key_value( key_value );
+			} else {
+				value = ( key_def->default_value ? ( strcasecmp( key_def->default_value, "true" ) == 0 || atoi( key_def->default_value ) != 0 ) : FALSE );
 			}
 		}
 	}
@@ -516,11 +531,13 @@ na_settings_get_boolean_ex( NASettings *settings, const gchar *group, const gcha
  * @found: if not %NULL, a pointer to a gboolean in which we will store
  *  whether the searched @key has been found (%TRUE), or if the returned
  *  value comes from default (%FALSE).
- * @global: if not %NULL, a pointer to a gboolean in which we will store
- *  whether the returned value has been readen from global preferences
- *  (%TRUE), or from the user preferences (%FALSE). Global preferences
- *  are usually read-only. When the @key has not been found, @global
- *  is set to %FALSE.
+ * @mandatory: if not %NULL, a pointer to a gboolean in which we will store
+ *  whether the returned value has been readen from mandatory preferences
+ *  (%TRUE), or from the user preferences (%FALSE). When the @key has not
+ *  been found, @mandatory is set to %FALSE.
+ *
+ * This function should only be called for unambiguous keys; the resultat
+ * is otherwise undefined (and rather unpredictable).
  *
  * Returns: the value of the key as a newly allocated list of strings.
  * The returned list should be na_core_utils_slist_free() by the caller.
@@ -528,9 +545,8 @@ na_settings_get_boolean_ex( NASettings *settings, const gchar *group, const gcha
  * Since: 3.1.0
  */
 GSList *
-na_settings_get_string_list( NASettings *settings, const gchar *key, gboolean *found, gboolean *global )
+na_settings_get_string_list( NASettings *settings, const gchar *key, gboolean *found, gboolean *mandatory )
 {
-	static const gchar *thisfn = "na_settings_get_string_list";
 	GSList *value;
 	KeyDef *key_def;
 
@@ -540,67 +556,206 @@ na_settings_get_string_list( NASettings *settings, const gchar *key, gboolean *f
 	if( found ){
 		*found = FALSE;
 	}
-	if( global ){
-		*global = FALSE;
+	if( mandatory ){
+		*mandatory = FALSE;
 	}
 
 	if( !settings->private->dispose_has_run ){
 
 		key_def = get_key_def( key );
-
 		if( key_def ){
-			value = get_string_list_ex( settings, key_def->group, key, key_def->default_value, found, global );
-
-		} else {
-			g_warning( "%s: no KeyDef found for key=%s", thisfn, key );
+			value = get_string_list_ex( settings, key_def->group, key, found, mandatory );
 		}
 	}
 
 	return( value );
 }
 
+/*
+ * returns a list of modified KeyValue
+ * - order in the lists is not signifiant
+ * - the mandatory flag is not signifiant
+ * - a key is modified:
+ *   > if it appears in new
+ *   > if it disappears: the value is so reset to its default
+ *   > if the value has been modified
+ *
+ * we return here a new list, with newly allocated KeyValue structs
+ * which hold the new value of each modified key
+ */
+static GList *
+content_diff( GList *old, GList *new )
+{
+	GList *diffs, *io, *in;
+	KeyValue *kold, *knew, *kdiff;
+	gboolean found;
+	KeyDef *key_def;
+
+	diffs = NULL;
+
+	for( io = old ; io ; io = io->next ){
+		kold = ( KeyValue * ) io->data;
+		found = FALSE;
+		for( in = new ; in && !found ; in = in->next ){
+			knew = ( KeyValue * ) in->data;
+			if( !strcmp( kold->group, knew->group ) && !strcmp( kold->key, knew->key )){
+				found = TRUE;
+				if( na_boxed_compare( kold->boxed, knew->boxed ) != 0 ){
+					/* a key has been modified */
+					kdiff = g_new0( KeyValue, 1 );
+					kdiff->group = g_strdup( knew->group );
+					kdiff->key = g_strdup( knew->key );
+					kdiff->mandatory = knew->mandatory;
+					kdiff->boxed = na_boxed_copy( knew->boxed );
+					diffs = g_list_prepend( diffs, kdiff );
+				}
+			}
+		}
+		if( !found ){
+			key_def = get_key_def( kold->key );
+			if( key_def ){
+				/* a key has disappeared */
+				kdiff = g_new0( KeyValue, 1 );
+				kdiff->group = g_strdup( kold->group );
+				kdiff->key = g_strdup( kold->key );
+				kdiff->mandatory = FALSE;
+				kdiff->boxed = na_boxed_new_from_string( key_def->type, key_def->default_value );
+				diffs = g_list_prepend( diffs, kdiff );
+			}
+		}
+	}
+
+	for( in = new ; in ; in = in->next ){
+		knew = ( KeyValue * ) in->data;
+		found = FALSE;
+		for( io = old ; io && !found ; io = io->next ){
+			kold = ( KeyValue * ) io->data;
+			if( !strcmp( kold->group, knew->group ) && !strcmp( kold->key, knew->key )){
+				found = TRUE;
+			}
+		}
+		if( !found ){
+			key_def = get_key_def( knew->key );
+			if( key_def ){
+				/* a key is new */
+				kdiff = g_new0( KeyValue, 1 );
+				kdiff->group = g_strdup( knew->group );
+				kdiff->key = g_strdup( knew->key );
+				kdiff->mandatory = knew->mandatory;
+				kdiff->boxed = na_boxed_copy( knew->boxed );
+				diffs = g_list_prepend( diffs, kdiff );
+			}
+		}
+	}
+
+	return( diffs );
+}
+
+/* load the content of the two configuration files (actually of _the_ configuration)
+ * taking care of not overriding mandatory preferences with user ones
+ */
+static GList *
+content_load( NASettings *settings )
+{
+	GList *content;
+
+	content = content_load_keys( settings, NULL, settings->private->mandatory, TRUE );
+	content = content_load_keys( settings, content, settings->private->user, FALSE );
+
+	return( content );
+}
+
+static GList *
+content_load_keys( NASettings *settings, GList *content, KeyFile *key_file, gboolean mandatory )
+{
+	static const gchar *thisfn = "na_settings_content_load_keys";
+	GError *error;
+	gchar **groups, **ig;
+	gchar **keys, **ik;
+	KeyValue *key_value;
+	KeyDef *key_def;
+
+	error = NULL;
+	if( !g_key_file_load_from_file( key_file->key_file, key_file->fname, G_KEY_FILE_KEEP_COMMENTS, &error )){
+		g_warning( "%s: %s: %s", thisfn, key_file->fname, error->message );
+		g_error_free( error );
+		error = NULL;
+
+	} else {
+		groups = g_key_file_get_groups( key_file->key_file, NULL );
+		ig = groups;
+		while( *ig ){
+			keys = g_key_file_get_keys( key_file->key_file, *ig, NULL, NULL );
+			ik = keys;
+			while( *ik ){
+				key_def = get_key_def( *ik );
+				if( key_def ){
+					key_value = peek_key_value_from_content( content, *ig, *ik );
+					if( !key_value ){
+						key_value = read_key_value_from_key_file( key_file->key_file, *ig, *ik, key_def );
+						if( key_value ){
+							key_value->mandatory = mandatory;
+							content = g_list_prepend( content, key_value );
+						}
+					}
+				}
+				ik++;
+			}
+			g_strfreev( keys );
+			ig++;
+		}
+		g_strfreev( groups );
+	}
+
+	return( content );
+}
+
 static KeyDef *
 get_key_def( const gchar *key )
 {
+	static const gchar *thisfn = "na_settings_get_key_def";
 	KeyDef *found = NULL;
 	KeyDef *idef;
 
 	idef = ( KeyDef * ) st_def_keys;
-	while( idef && !found ){
+	while( idef->key && !found ){
 		if( !strcmp( idef->key, key )){
 			found = idef;
 		}
 		idef++;
 	}
+	if( !found ){
+		g_warning( "%s: no KeyDef found for key=%s", thisfn, key );
+	}
 
 	return( found );
 }
 
 /*
- * get_string_ex:
+ * get_string_list_ex:
  * @settings: this #NASettings instance.
+ * @group: the group name.
  * @key: the key whose value is to be returned.
  * @found: if not %NULL, a pointer to a gboolean in which we will store
  *  whether the searched @key has been found (%TRUE), or if the returned
  *  value comes from default (%FALSE).
- * @global: if not %NULL, a pointer to a gboolean in which we will store
- *  whether the returned value has been readen from global preferences
- *  (%TRUE), or from the user preferences (%FALSE). Global preferences
- *  are usually read-only. When the @key has not been found, @global
- *  is set to %FALSE.
+ * @mandatory: if not %NULL, a pointer to a gboolean in which we will store
+ *  whether the returned value has been readen from mandatory preferences
+ *  (%TRUE), or from the user preferences (%FALSE). When the @key has not
+ *  been found, @mandatory is set to %FALSE.
  *
- * Returns: the value of the key as a newly allocated string which should
- * be g_free() by the caller.
+ * Returns: the value of the key as a newly allocated list of strings.
+ * The returned list should be na_core_utils_slist_free() by the caller.
  *
  * Since: 3.1.0
  */
-static gchar *
-get_string_ex( NASettings *settings, const gchar *group, const gchar *key, const gchar *default_value, gboolean *found, gboolean *global )
+static GSList *
+get_string_list_ex( NASettings *settings, const gchar *group, const gchar *key, gboolean *found, gboolean *mandatory )
 {
-	static const gchar *thisfn = "na_settings_get_string_ex";
-	gchar *value;
-	GError *error;
+	GSList *value;
 	gboolean has_entry;
+	KeyDef *key_def;
+	KeyValue *key_value;
 
 	g_return_val_if_fail( NA_IS_SETTINGS( settings ), NULL );
 
@@ -608,55 +763,48 @@ get_string_ex( NASettings *settings, const gchar *group, const gchar *key, const
 	if( found ){
 		*found = FALSE;
 	}
-	if( global ){
-		*global = FALSE;
+	if( mandatory ){
+		*mandatory = FALSE;
 	}
 
 	if( !settings->private->dispose_has_run ){
-		error = NULL;
-		has_entry = TRUE;
-		value = g_key_file_get_string( settings->private->global_conf, group, key, &error );
-		if( error ){
+		key_def = get_key_def( key );
+		if( key_def ){
 			has_entry = FALSE;
-			if( error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND && error->code != G_KEY_FILE_ERROR_GROUP_NOT_FOUND ){
-				g_warning( "%s: (global) %s", thisfn, error->message );
-			}
-			g_error_free( error );
-			error = NULL;
-
-		} else {
-			if( found ){
-				*found = TRUE;
-			}
-			if( global ){
-				*global = TRUE;
-			}
-		}
-		if( !has_entry ){
-			value = g_key_file_get_string( settings->private->user_conf, group, key, &error );
-			if( error ){
-				if( error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND && error->code != G_KEY_FILE_ERROR_GROUP_NOT_FOUND ){
-					g_warning( "%s: (user) %s", thisfn, error->message );
-				}
-				g_error_free( error );
-				error = NULL;
-			} else {
+			key_value = read_key_value_from_key_file( settings->private->mandatory->key_file, group, key, key_def );
+			if( key_value ){
 				has_entry = TRUE;
 				if( found ){
 					*found = TRUE;
 				}
+				if( mandatory ){
+					*mandatory = TRUE;
+				}
+			}
+			if( !has_entry ){
+				key_value = read_key_value_from_key_file( settings->private->user->key_file, group, key, key_def );
+				if( key_value ){
+					has_entry = TRUE;
+					if( found ){
+						*found = TRUE;
+					}
+				}
+			}
+			if( has_entry ){
+				value = na_boxed_get_string_list( key_value->boxed );
+				release_key_value( key_value );
+			} else {
+				value = g_slist_append( NULL, g_strdup( key_def->default_value ));
 			}
-		}
-		if( !has_entry ){
-			value = g_strdup( default_value );
 		}
 	}
 
 	return( value );
 }
 
+#if 0
 /*
- * get_string_list_ex:
+ * get_string_ex:
  * @settings: this #NASettings instance.
  * @key: the key whose value is to be returned.
  * @found: if not %NULL, a pointer to a gboolean in which we will store
@@ -668,17 +816,16 @@ get_string_ex( NASettings *settings, const gchar *group, const gchar *key, const
  *  are usually read-only. When the @key has not been found, @global
  *  is set to %FALSE.
  *
- * Returns: the value of the key as a newly allocated list of strings.
- * The returned list should be na_core_utils_slist_free() by the caller.
+ * Returns: the value of the key as a newly allocated string which should
+ * be g_free() by the caller.
  *
  * Since: 3.1.0
  */
-static GSList *
-get_string_list_ex( NASettings *settings, const gchar *group, const gchar *key, const gchar *default_value, gboolean *found, gboolean *global )
+static gchar *
+get_string_ex( NASettings *settings, const gchar *group, const gchar *key, const gchar *default_value, gboolean *found, gboolean *global )
 {
-	static const gchar *thisfn = "na_settings_get_string_list_ex";
-	GSList *value;
-	gchar **array;
+	static const gchar *thisfn = "na_settings_get_string_ex";
+	gchar *value;
 	GError *error;
 	gboolean has_entry;
 
@@ -695,7 +842,7 @@ get_string_list_ex( NASettings *settings, const gchar *group, const gchar *key,
 	if( !settings->private->dispose_has_run ){
 		error = NULL;
 		has_entry = TRUE;
-		array = g_key_file_get_string_list( settings->private->global_conf, group, key, NULL, &error );
+		value = g_key_file_get_string( settings->private->global_conf, group, key, &error );
 		if( error ){
 			has_entry = FALSE;
 			if( error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND && error->code != G_KEY_FILE_ERROR_GROUP_NOT_FOUND ){
@@ -713,7 +860,7 @@ get_string_list_ex( NASettings *settings, const gchar *group, const gchar *key,
 			}
 		}
 		if( !has_entry ){
-			array = g_key_file_get_string_list( settings->private->user_conf, group, key, NULL, &error );
+			value = g_key_file_get_string( settings->private->user_conf, group, key, &error );
 			if( error ){
 				if( error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND && error->code != G_KEY_FILE_ERROR_GROUP_NOT_FOUND ){
 					g_warning( "%s: (user) %s", thisfn, error->message );
@@ -728,202 +875,197 @@ get_string_list_ex( NASettings *settings, const gchar *group, const gchar *key,
 			}
 		}
 		if( !has_entry ){
-			value = g_slist_append( NULL, g_strdup( default_value ));
-		} else {
-			value = na_core_utils_slist_from_array(( const gchar ** ) array );
-			g_strfreev( array );
+			value = g_strdup( default_value );
 		}
 	}
 
 	return( value );
 }
+#endif
 
 /*
  * called from na_settings_new
  * allocate and load the key files for global and user preferences
  */
-static GKeyFile *
-initialize_settings( NASettings* settings, const gchar *dir, GFileMonitor **monitor, gulong *handler )
+static KeyFile *
+key_file_new( NASettings *settings, const gchar *dir )
 {
-	static const gchar *thisfn = "na_settings_initialize_settings";
-	GKeyFile *key_file;
+	static const gchar *thisfn = "na_settings_key_file_new";
+	KeyFile *key_file;
 	GError *error;
-	gchar *path;
 	GFile *file;
 
-	key_file = g_key_file_new();
+	key_file = g_new0( KeyFile, 1 );
 
-	error = NULL;
-	path = g_strdup_printf( "%s/%s.conf", dir, PACKAGE );
-	if( !g_key_file_load_from_file( key_file, path, G_KEY_FILE_KEEP_COMMENTS, &error )){
-		g_warning( "%s: %s", thisfn, error->message );
-		g_error_free( error );
-		error = NULL;
-	}
+	key_file->key_file = g_key_file_new();
+	key_file->fname = g_strdup_printf( "%s/%s.conf", dir, PACKAGE );
 
-	file = g_file_new_for_path( path );
-	*monitor = g_file_monitor_file( file, 0, NULL, &error );
+	error = NULL;
+	file = g_file_new_for_path( key_file->fname );
+	key_file->monitor = g_file_monitor_file( file, 0, NULL, &error );
 	if( error ){
-		g_warning( "%s: %s", thisfn, error->message );
+		g_warning( "%s: %s: %s", thisfn, key_file->fname, error->message );
 		g_error_free( error );
 		error = NULL;
-
 	} else {
-		*handler = g_signal_connect( *monitor, "changed", ( GCallback ) on_conf_changed, settings );
+		key_file->handler = g_signal_connect( key_file->monitor, "changed", ( GCallback ) on_keyfile_changed, settings );
 	}
 	g_object_unref( file );
 
-	g_free( path );
 	return( key_file );
 }
 
 /*
- * this is a fake key, as we actually monitor the 'readable' status
- * of all io-providers
- * add to the list of monitored keys the found io-providers
+ * one of the two monitored configuration files have changed on the disk
+ * we do not try to identify which keys have actually change
+ * instead we trigger each registered consumer for the 'global' event
+ *
+ * consumers which register for the 'global_conf' event are recorded
+ * with a NULL key
  */
 static void
-monitor_io_provider_read_status( NASettings *settings )
+on_keyfile_changed( GFileMonitor *monitor,
+		GFile *file, GFile *other_file, GFileMonitorEvent event_type, NASettings *settings )
 {
-	monitor_io_provider_read_status_conf( settings, settings->private->global_conf, TRUE );
+	g_return_if_fail( NA_IS_SETTINGS( settings ));
 
-	monitor_io_provider_read_status_conf( settings, settings->private->user_conf, FALSE );
-}
+	if( !settings->private->dispose_has_run ){
 
-static void
-monitor_io_provider_read_status_conf( NASettings *settings, GKeyFile *key_file, gboolean global )
-{
-	gchar **array;
-	gchar **idx;
-	gboolean readable;
-	gboolean found;
+		/* set a timeout to notify consumers at the end of the serie */
+		g_get_current_time( &st_last_event );
 
-	array = g_key_file_get_groups( key_file, NULL );
-	if( array ){
-		idx = array;
-		while( idx ){
-			if( g_str_has_prefix( *idx, "io-provider " )){
-				readable = na_settings_get_boolean_ex( settings, *idx, IO_PROVIDER_READABLE, NULL, &found, NULL );
-				if( found ){
-					monitor_key_add( settings, *idx, IO_PROVIDER_READABLE, GUINT_TO_POINTER(( guint ) readable ), global, NAFD_TYPE_BOOLEAN );
-				}
-			}
-			idx++;
+		if( !st_event_source_id ){
+			st_event_source_id =
+				g_timeout_add( st_burst_timeout, ( GSourceFunc ) on_keyfile_changed_timeout, settings );
 		}
-		g_strfreev( array );
 	}
 }
 
-static void
-monitor_key( NASettings *settings, const gchar *key )
+static gboolean
+on_keyfile_changed_timeout( NASettings *settings )
 {
-	static const gchar *thisfn = "na_settings_monitor_key";
-	KeyDef *key_def;
-	gpointer value;
-	gboolean found, global;
-
-	found = FALSE;
-	key_def = get_key_def( key );
-	if( key_def ){
-		switch( key_def->data_type ){
-
-			case NAFD_TYPE_STRING:
-			case NAFD_TYPE_MAP:
-				value = get_string_ex( settings, key_def->group, key, NULL, &found, &global );
-				break;
-
-			case NAFD_TYPE_BOOLEAN:
-				value = GUINT_TO_POINTER( na_settings_get_boolean_ex( settings, key_def->group, key, NULL, &found, &global ));
-				break;
-
-			case NAFD_TYPE_STRING_LIST:
-				value = get_string_list_ex( settings, key_def->group, key, NULL, &found, &global );
-				break;
-
-			case NAFD_TYPE_LOCALE_STRING:
-			case NAFD_TYPE_POINTER:
-			case NAFD_TYPE_UINT:
-				g_warning( "%s: unmanaged data type: %s", thisfn, na_data_types_get_label( key_def->data_type ));
-				break;
-
-			default:
-				g_warning( "%s: unknown data type: %d", thisfn, key_def->data_type );
+	static const gchar *thisfn = "na_settings_on_keyfile_changed_timeout";
+	GList *new_content;
+	GList *modifs;
+	GList *ic, *im;
+	Consumer *consumer;
+	KeyValue *changed;
+	gchar *group_prefix, *key;
+	GTimeVal now;
+	gulong diff;
+	gulong timeout_usec = 1000*st_burst_timeout;
+
+	g_get_current_time( &now );
+	diff = time_val_diff( &now, &st_last_event );
+	if( diff < timeout_usec ){
+		return( TRUE );
+	}
+
+	/* last individual notification is older that the st_burst_timeout
+	 * we may so suppose that the burst is terminated
+	 */
+	new_content = content_load( settings );
+	modifs = content_diff( settings->private->content, new_content );
+	g_debug( "%s: %d modifs found", thisfn, g_list_length( modifs ));
+
+	for( ic = settings->private->consumers ; ic ; ic = ic->next ){
+		consumer = ( Consumer * ) ic->data;
+
+		group_prefix = NULL;
+		if( !strcmp( consumer->monitored_key, NA_SETTINGS_RUNTIME_IO_PROVIDER_READ_STATUS )){
+			group_prefix = g_strdup_printf( "%s ", GROUP_IO_PROVIDER );
+			key = IO_PROVIDER_READABLE;
+		} else {
+			key = consumer->monitored_key;
 		}
 
-	} else {
-		g_warning( "%s: no KeyDef found for key=%s", thisfn, key );
-	}
+		for( im = modifs ; im ; im = im->next ){
+			changed = ( KeyValue * ) im->data;
+			if(( !group_prefix || g_str_has_prefix( changed->group, group_prefix )) && !strcmp( changed->key, key )){
+				( *( NASettingsKeyCallback ) consumer->callback )( changed->group, changed->key, na_boxed_get_pointer( changed->boxed ), changed->mandatory, consumer->user_data );
+			}
+		}
 
-	if( found ){
-		monitor_key_add( settings, key_def->group, key, value, global, key_def->data_type );
+		g_free( group_prefix );
 	}
+
+	g_list_foreach( settings->private->content, ( GFunc ) release_key_value, NULL );
+	g_list_free( settings->private->content );
+	settings->private->content = new_content;
+
+	g_list_foreach( modifs, ( GFunc ) release_key_value, NULL );
+	g_list_free( modifs );
+
+	st_event_source_id = 0;
+	return( FALSE );
 }
 
-/* add the key if not already monitored
- */
-static void
-monitor_key_add( NASettings *settings, const gchar *group, const gchar *key, gpointer value, gboolean global, guint type )
+static KeyValue *
+peek_key_value_from_content( GList *content, const gchar *group, const gchar *key )
 {
-	GList *it;
-	gboolean found;
-	Entry *entry;
+	KeyValue *value, *found;
+	GList *ic;
 
-	for( it=settings->private->entries, found=FALSE ; it && !found ; it=it->next ){
-		entry = ( Entry * ) it->data;
-		if( !strcmp( entry->group, group ) &&
-			!strcmp( entry->key, key ) &&
-			entry->global == global ){
-				found = TRUE;
+	found = NULL;
+	for( ic = content ; ic && !found ; ic = ic->next ){
+		value = ( KeyValue * ) ic->data;
+		if( !strcmp( value->group, group ) && !strcmp( value->key, key )){
+			found = value;
 		}
 	}
 
-	if( !found ){
-		entry = g_new0( Entry, 1 );
-		entry->group = g_strdup( group );
-		entry->key = g_strdup( key );
-		entry->value = na_data_types_copy( value, type );
-		entry->global = global;
-		entry->type = type;
-
-		settings->private->entries = g_list_prepend( settings->private->entries, entry );
-	}
+	return( found );
 }
 
-/*
- * one of the two monitored configuration files have changed on the disk
- * we do not try to identify which keys have actually change
- * instead we trigger each registered consumer for the 'global' event
- *
- * consumers which register for the 'global_conf' event are recorded
- * with a NULL key
- */
-static void
-on_conf_changed( GFileMonitor *monitor,
-		GFile *file, GFile *other_file, GFileMonitorEvent event_type, NASettings *settings )
+static KeyValue *
+read_key_value_from_key_file( GKeyFile *key_file, const gchar *group, const gchar *key, const KeyDef *key_def )
 {
-	GList *ic;
-	Consumer *consumer;
-	gchar *path;
-	GFile *prefix;
-	gboolean global;
-
-	g_return_if_fail( NA_IS_SETTINGS( settings ));
+	static const gchar *thisfn = "na_settings_read_key_value_from_key_file";
+	KeyValue *value;
+	gchar *str;
+	GError *error;
 
-	if( !settings->private->dispose_has_run ){
+	value = NULL;
+	error = NULL;
 
-		path = g_build_filename( SYSCONFDIR, "xdg", NULL );
-		prefix = g_file_new_for_path( path );
-		global = g_file_has_prefix( file, prefix );
-		g_object_unref( prefix );
-		g_free( path );
+	switch( key_def->type ){
 
-		for( ic = settings->private->consumers ; ic ; ic = ic->next ){
-			consumer = ( Consumer * ) ic->data;
-			if( !consumer->key ){
-				( *( global_fn ) consumer->callback )( global, consumer->user_data );
+		case NA_BOXED_TYPE_STRING:
+		case NA_BOXED_TYPE_STRING_LIST:
+		case NA_BOXED_TYPE_UINT:
+		case NA_BOXED_TYPE_UINT_LIST:
+		case NA_BOXED_TYPE_BOOLEAN:
+			str = g_key_file_get_string( key_file, group, key, &error );
+			if( error ){
+				if( error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND && error->code != G_KEY_FILE_ERROR_GROUP_NOT_FOUND ){
+					g_warning( "%s: %s", thisfn, error->message );
+				}
+				g_error_free( error );
+			} else {
+				value = g_new0( KeyValue, 1 );
+				value->group = g_strdup( group );
+				value->key = g_strdup( key );
+				switch( key_def->type ){
+					case NA_BOXED_TYPE_STRING:
+					case NA_BOXED_TYPE_UINT:
+					case NA_BOXED_TYPE_BOOLEAN:
+						value->boxed = na_boxed_new_from_string( key_def->type, str );
+						break;
+					case NA_BOXED_TYPE_STRING_LIST:
+					case NA_BOXED_TYPE_UINT_LIST:
+						value->boxed = na_boxed_new_from_string_with_sep( key_def->type, str, ";" );
+						break;
+				}
 			}
-		}
+			g_free( str );
+			break;
+
+		default:
+			g_warning( "%s: unmanaged boxed type: %d", thisfn, key_def->type );
+			break;
 	}
+
+	return( value );
 }
 
 /*
@@ -933,19 +1075,49 @@ on_conf_changed( GFileMonitor *monitor,
 static void
 release_consumer( Consumer *consumer )
 {
-	g_free( consumer->key );
+	g_free( consumer->monitored_key );
 	g_free( consumer );
 }
 
 /*
+ * called from instance_dispose
+ * release the opened and monitored GKeyFiles
+ */
+static void
+release_key_file( KeyFile *key_file )
+{
+	g_key_file_free( key_file->key_file );
+	if( key_file->monitor ){
+		if( key_file->handler ){
+			g_signal_handler_disconnect( key_file->monitor, key_file->handler );
+		}
+		g_file_monitor_cancel( key_file->monitor );
+		g_object_unref( key_file->monitor );
+	}
+	g_free( key_file->fname );
+	g_free( key_file );
+}
+
+/*
  * called from instance_finalize
- * release the list of monitored entries
+ * release a KeyValue struct
  */
 static void
-release_entry( Entry *entry )
+release_key_value( KeyValue *value )
+{
+	g_free( value->group );
+	g_free( value->key );
+	na_boxed_free( value->boxed );
+	g_free( value );
+}
+
+/*
+ * returns the difference in microseconds.
+ */
+static gulong
+time_val_diff( const GTimeVal *recent, const GTimeVal *old )
 {
-	g_free( entry->group );
-	g_free( entry->key );
-	na_data_types_free( entry->value, entry->type );
-	g_free( entry );
+	gulong microsec = 1000000 * ( recent->tv_sec - old->tv_sec );
+	microsec += recent->tv_usec  - old->tv_usec;
+	return( microsec );
 }
diff --git a/src/core/na-settings.h b/src/core/na-settings.h
index 0d882fb..97a93a7 100644
--- a/src/core/na-settings.h
+++ b/src/core/na-settings.h
@@ -81,21 +81,24 @@ typedef struct {
 
 GType na_settings_get_type( void );
 
-#define NA_SETTINGS_RUNTIME_IO_PROVIDER_READ_STATUS		"io-provider-read-status-fake-key"
+/* monitorable keys
+ */
+#define NA_SETTINGS_RUNTIME_IO_PROVIDER_READ_STATUS		"io-provider-read-status-composite-key"
 #define NA_SETTINGS_RUNTIME_IO_PROVIDERS_READ_ORDER		"io-providers-read-order"
 #define NA_SETTINGS_RUNTIME_ITEMS_ADD_ABOUT_ITEM		"items-add-about-item"
 #define NA_SETTINGS_RUNTIME_ITEMS_CREATE_ROOT_MENU		"items-create-root-menu"
 #define NA_SETTINGS_RUNTIME_ITEMS_LEVEL_ZERO_ORDER		"items-level-zero-order"
 #define NA_SETTINGS_RUNTIME_ITEMS_LIST_ORDER_MODE		"items-list-order-mode"
 
+typedef void ( *NASettingsKeyCallback )( const gchar *group, const gchar *key, gconstpointer new_value, gboolean mandatory, void *user_data );
+
 NASettings *na_settings_new                     ( void );
 
 void        na_settings_register_key_callback   ( NASettings *settings, const gchar *key, GCallback callback, gpointer user_data );
-void        na_settings_register_global_callback( NASettings *settings, GCallback callback, gpointer user_data );
 
-gboolean    na_settings_get_boolean             ( NASettings *settings, const gchar *key, gboolean *found, gboolean *global );
-gboolean    na_settings_get_boolean_ex          ( NASettings *settings, const gchar *group, const gchar *key, const gchar *default_value, gboolean *found, gboolean *global );
-GSList     *na_settings_get_string_list         ( NASettings *settings, const gchar *key, gboolean *found, gboolean *global );
+gboolean    na_settings_get_boolean             ( NASettings *settings, const gchar *key, gboolean *found, gboolean *mandatory );
+gboolean    na_settings_get_boolean_ex          ( NASettings *settings, const gchar *group, const gchar *key, gboolean *found, gboolean *mandatory );
+GSList     *na_settings_get_string_list         ( NASettings *settings, const gchar *key, gboolean *found, gboolean *mandatory );
 
 G_END_DECLS
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]