Fix string errors reported by Christian Kirbach
[nautilus-actions.git] / src / core / na-settings.c
blob9f9c6e5e553bfaaf006a237b11fff9620049ba4b
1 /*
2 * Nautilus-Actions
3 * A Nautilus extension which offers configurable context menu actions.
5 * Copyright (C) 2005 The GNOME Foundation
6 * Copyright (C) 2006, 2007, 2008 Frederic Ruaudel and others (see AUTHORS)
7 * Copyright (C) 2009, 2010, 2011 Pierre Wieser and others (see AUTHORS)
9 * This Program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of
12 * the License, or (at your option) any later version.
14 * This Program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public
20 * License along with this Library; see the file COPYING. If not,
21 * write to the Free Software Foundation, Inc., 59 Temple Place,
22 * Suite 330, Boston, MA 02111-1307, USA.
24 * Authors:
25 * Frederic Ruaudel <grumz@grumz.net>
26 * Rodrigo Moya <rodrigo@gnome-db.org>
27 * Pierre Wieser <pwieser@trychlos.org>
28 * ... and many others (see AUTHORS)
31 #ifdef HAVE_CONFIG_H
32 #include <config.h>
33 #endif
35 #include <gio/gio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <strings.h>
40 #include <api/na-boxed.h>
41 #include <api/na-core-utils.h>
42 #include <api/na-timeout.h>
44 #include "na-settings.h"
46 /* private class data
48 struct _NASettingsClassPrivate {
49 void *empty; /* so that gcc -pedantic is happy */
52 /* The characteristics of a configuration file.
53 * We manage two configuration files:
54 * - the global configuration file handles mandatory preferences;
55 * - the user configuration file handles.. well, user preferences.
57 typedef struct {
58 gchar *fname;
59 GKeyFile *key_file;
60 GFileMonitor *monitor;
61 gulong handler;
63 KeyFile;
65 /* The configuration content is handled as a GList of NAKeyValue structs.
66 * This list is loaded at initialization time, and then compared each
67 * time our file monitors signal us that a change has occured.
69 typedef struct {
70 gchar *group;
71 gchar *key;
72 gboolean mandatory;
73 NABoxed *boxed;
75 KeyValue;
77 /* Each consumer may register a callback function which will be triggered
78 * when a key is modified.
79 * The monitored key may be a real key of the file, but also be a composite
80 * key (e.g. NA_IPREFS_IO_PROVIDERS_READ_STATUS monitors the 'readable'
81 * key of all i/o providers).
82 * Note that we actually monitor the _user_view_ of the configuration:
83 * e.g. if a key has a mandatory value in global conf, then the same
84 * key in user conf will just be ignored.
86 typedef struct {
87 gchar *monitored_key;
88 GCallback callback;
89 gpointer user_data;
91 Consumer;
93 /* private instance data
95 struct _NASettingsPrivate {
96 gboolean dispose_has_run;
97 KeyFile *mandatory;
98 KeyFile *user;
99 GList *content;
100 GList *consumers;
101 NATimeout timeout;
104 #define GROUP_NACT "nact"
105 #define GROUP_RUNTIME "runtime"
107 typedef struct {
108 const gchar *key;
109 const gchar *group;
110 guint type;
111 const gchar *default_value;
113 KeyDef;
115 static const KeyDef st_def_keys[] = {
116 { NA_IPREFS_ADMIN_PREFERENCES_LOCKED, GROUP_NACT, NA_BOXED_TYPE_BOOLEAN, "false" },
117 { NA_IPREFS_ADMIN_IO_PROVIDERS_LOCKED, GROUP_RUNTIME, NA_BOXED_TYPE_BOOLEAN, "false" },
118 { NA_IPREFS_ASSISTANT_ESC_CONFIRM, GROUP_NACT, NA_BOXED_TYPE_BOOLEAN, "true" },
119 { NA_IPREFS_ASSISTANT_ESC_QUIT, GROUP_NACT, NA_BOXED_TYPE_BOOLEAN, "true" },
120 { NA_IPREFS_CAPABILITY_ADD_CAPABILITY_WSP, GROUP_NACT, NA_BOXED_TYPE_UINT_LIST, "" },
121 { NA_IPREFS_COMMAND_CHOOSER_WSP, GROUP_NACT, NA_BOXED_TYPE_UINT_LIST, "" },
122 { NA_IPREFS_COMMAND_CHOOSER_URI, GROUP_NACT, NA_BOXED_TYPE_STRING, "file:///bin" },
123 { NA_IPREFS_COMMAND_LEGEND_WSP, GROUP_NACT, NA_BOXED_TYPE_UINT_LIST, "" },
124 { NA_IPREFS_WORKING_DIR_WSP, GROUP_NACT, NA_BOXED_TYPE_UINT_LIST, "" },
125 { NA_IPREFS_WORKING_DIR_URI, GROUP_NACT, NA_BOXED_TYPE_STRING, "file:///" },
126 { NA_IPREFS_SHOW_IF_RUNNING_WSP, GROUP_NACT, NA_BOXED_TYPE_UINT_LIST, "" },
127 { NA_IPREFS_SHOW_IF_RUNNING_URI, GROUP_NACT, NA_BOXED_TYPE_STRING, "file:///bin" },
128 { NA_IPREFS_TRY_EXEC_WSP, GROUP_NACT, NA_BOXED_TYPE_UINT_LIST, "" },
129 { NA_IPREFS_TRY_EXEC_URI, GROUP_NACT, NA_BOXED_TYPE_STRING, "file:///bin" },
130 { NA_IPREFS_EXPORT_ASK_USER_WSP, GROUP_NACT, NA_BOXED_TYPE_UINT_LIST, "" },
131 { NA_IPREFS_EXPORT_ASK_USER_LAST_FORMAT, GROUP_NACT, NA_BOXED_TYPE_STRING, NA_IPREFS_DEFAULT_EXPORT_FORMAT },
132 { NA_IPREFS_EXPORT_ASK_USER_KEEP_LAST_CHOICE, GROUP_NACT, NA_BOXED_TYPE_BOOLEAN, "false" },
133 { NA_IPREFS_EXPORT_ASSISTANT_WSP, GROUP_NACT, NA_BOXED_TYPE_UINT_LIST, "" },
134 { NA_IPREFS_EXPORT_ASSISTANT_URI, GROUP_NACT, NA_BOXED_TYPE_STRING, "file:///tmp" },
135 { NA_IPREFS_EXPORT_PREFERRED_FORMAT, GROUP_NACT, NA_BOXED_TYPE_STRING, NA_IPREFS_DEFAULT_EXPORT_FORMAT },
136 { NA_IPREFS_FOLDER_CHOOSER_WSP, GROUP_NACT, NA_BOXED_TYPE_UINT_LIST, "" },
137 { NA_IPREFS_FOLDER_CHOOSER_URI, GROUP_NACT, NA_BOXED_TYPE_STRING, "file:///" },
138 { NA_IPREFS_IMPORT_ASK_USER_WSP, GROUP_NACT, NA_BOXED_TYPE_UINT_LIST, "" },
139 { NA_IPREFS_IMPORT_ASK_USER_LAST_MODE, GROUP_NACT, NA_BOXED_TYPE_STRING, NA_IPREFS_DEFAULT_IMPORT_MODE },
140 { NA_IPREFS_IMPORT_ASSISTANT_WSP, GROUP_NACT, NA_BOXED_TYPE_UINT_LIST, "" },
141 { NA_IPREFS_IMPORT_ASSISTANT_URI, GROUP_NACT, NA_BOXED_TYPE_STRING, "file:///tmp" },
142 { NA_IPREFS_IMPORT_ASK_USER_KEEP_LAST_CHOICE, GROUP_NACT, NA_BOXED_TYPE_BOOLEAN, "false" },
143 { NA_IPREFS_IMPORT_PREFERRED_MODE, GROUP_NACT, NA_BOXED_TYPE_STRING, NA_IPREFS_DEFAULT_IMPORT_MODE },
144 { NA_IPREFS_IO_PROVIDERS_WRITE_ORDER, GROUP_NACT, NA_BOXED_TYPE_STRING_LIST, "" },
145 { NA_IPREFS_ICON_CHOOSER_URI, GROUP_NACT, NA_BOXED_TYPE_STRING, "file:///" },
146 { NA_IPREFS_ICON_CHOOSER_PANED, GROUP_NACT, NA_BOXED_TYPE_UINT, "200" },
147 { NA_IPREFS_ICON_CHOOSER_WSP, GROUP_NACT, NA_BOXED_TYPE_UINT_LIST, "" },
148 { NA_IPREFS_ITEMS_ADD_ABOUT_ITEM, GROUP_RUNTIME, NA_BOXED_TYPE_BOOLEAN, "true" },
149 { NA_IPREFS_ITEMS_CREATE_ROOT_MENU, GROUP_RUNTIME, NA_BOXED_TYPE_BOOLEAN, "true" },
150 { NA_IPREFS_ITEMS_LEVEL_ZERO_ORDER, GROUP_RUNTIME, NA_BOXED_TYPE_STRING_LIST, "" },
151 { NA_IPREFS_ITEMS_LIST_ORDER_MODE, GROUP_RUNTIME, NA_BOXED_TYPE_STRING, NA_IPREFS_DEFAULT_LIST_ORDER_MODE },
152 { NA_IPREFS_MAIN_PANED, GROUP_NACT, NA_BOXED_TYPE_UINT, "200" },
153 { NA_IPREFS_MAIN_SAVE_AUTO, GROUP_NACT, NA_BOXED_TYPE_BOOLEAN, "false" },
154 { NA_IPREFS_MAIN_SAVE_PERIOD, GROUP_NACT, NA_BOXED_TYPE_UINT, "5" },
155 { NA_IPREFS_MAIN_TOOLBAR_EDIT_DISPLAY, GROUP_NACT, NA_BOXED_TYPE_BOOLEAN, "true" },
156 { NA_IPREFS_MAIN_TOOLBAR_FILE_DISPLAY, GROUP_NACT, NA_BOXED_TYPE_BOOLEAN, "true" },
157 { NA_IPREFS_MAIN_TOOLBAR_HELP_DISPLAY, GROUP_NACT, NA_BOXED_TYPE_BOOLEAN, "true" },
158 { NA_IPREFS_MAIN_TOOLBAR_TOOLS_DISPLAY, GROUP_NACT, NA_BOXED_TYPE_BOOLEAN, "false" },
159 { NA_IPREFS_MAIN_WINDOW_WSP, GROUP_NACT, NA_BOXED_TYPE_UINT_LIST, "" },
160 { NA_IPREFS_PREFERENCES_WSP, GROUP_NACT, NA_BOXED_TYPE_UINT_LIST, "" },
161 { NA_IPREFS_RELABEL_DUPLICATE_ACTION, GROUP_NACT, NA_BOXED_TYPE_BOOLEAN, "false" },
162 { NA_IPREFS_RELABEL_DUPLICATE_MENU, GROUP_NACT, NA_BOXED_TYPE_BOOLEAN, "false" },
163 { NA_IPREFS_RELABEL_DUPLICATE_PROFILE, GROUP_NACT, NA_BOXED_TYPE_BOOLEAN, "false" },
164 { NA_IPREFS_SCHEME_ADD_SCHEME_WSP, GROUP_NACT, NA_BOXED_TYPE_UINT_LIST, "" },
165 { NA_IPREFS_SCHEME_DEFAULT_LIST, GROUP_NACT, NA_BOXED_TYPE_STRING_LIST, "" },
166 { NA_IPREFS_IO_PROVIDER_READABLE, NA_IPREFS_IO_PROVIDER_GROUP, NA_BOXED_TYPE_BOOLEAN, "true" },
167 { NA_IPREFS_IO_PROVIDER_WRITABLE, NA_IPREFS_IO_PROVIDER_GROUP, NA_BOXED_TYPE_BOOLEAN, "true" },
168 { 0 }
171 static GObjectClass *st_parent_class = NULL;
172 static gint st_burst_timeout = 100; /* burst timeout in msec */
174 static GType register_type( void );
175 static void class_init( NASettingsClass *klass );
176 static void instance_init( GTypeInstance *instance, gpointer klass );
177 static void instance_dispose( GObject *object );
178 static void instance_finalize( GObject *object );
180 static GList *content_diff( GList *old, GList *new );
181 static GList *content_load( NASettings *settings );
182 static GList *content_load_keys( NASettings *settings, GList *content, KeyFile *key_file, gboolean mandatory );
183 static KeyDef *get_key_def( const gchar *key );
184 static KeyFile *key_file_new( NASettings *settings, const gchar *dir );
185 static void on_keyfile_changed( GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type, NASettings *settings );
186 static void on_keyfile_changed_timeout( NASettings *settings );
187 static KeyValue *peek_key_value_from_content( GList *content, const gchar *group, const gchar *key );
188 static KeyValue *read_key_value( NASettings *settings, const gchar *group, const gchar *key, gboolean *found, gboolean *mandatory );
189 static KeyValue *read_key_value_from_key_file( GKeyFile *key_file, const gchar *group, const gchar *key, const KeyDef *key_def );
190 static void release_consumer( Consumer *consumer );
191 static void release_key_file( KeyFile *key_file );
192 static void release_key_value( KeyValue *value );
193 static gboolean set_key_value( NASettings *settings, const gchar *group, const gchar *key, const gchar *string );
194 static gboolean write_user_key_file( NASettings *settings );
196 GType
197 na_settings_get_type( void )
199 static GType object_type = 0;
201 if( !object_type ){
202 object_type = register_type();
205 return( object_type );
208 static GType
209 register_type( void )
211 static const gchar *thisfn = "na_settings_register_type";
212 GType type;
214 static GTypeInfo info = {
215 sizeof( NASettingsClass ),
216 ( GBaseInitFunc ) NULL,
217 ( GBaseFinalizeFunc ) NULL,
218 ( GClassInitFunc ) class_init,
219 NULL,
220 NULL,
221 sizeof( NASettings ),
223 ( GInstanceInitFunc ) instance_init
226 g_debug( "%s", thisfn );
228 type = g_type_register_static( G_TYPE_OBJECT, "NASettings", &info, 0 );
230 return( type );
233 static void
234 class_init( NASettingsClass *klass )
236 static const gchar *thisfn = "na_settings_class_init";
237 GObjectClass *object_class;
239 g_debug( "%s: klass=%p", thisfn, ( void * ) klass );
241 st_parent_class = g_type_class_peek_parent( klass );
243 object_class = G_OBJECT_CLASS( klass );
244 object_class->dispose = instance_dispose;
245 object_class->finalize = instance_finalize;
247 klass->private = g_new0( NASettingsClassPrivate, 1 );
250 static void
251 instance_init( GTypeInstance *instance, gpointer klass )
253 static const gchar *thisfn = "na_settings_instance_init";
254 NASettings *self;
256 g_return_if_fail( NA_IS_SETTINGS( instance ));
258 g_debug( "%s: instance=%p (%s), klass=%p",
259 thisfn, ( void * ) instance, G_OBJECT_TYPE_NAME( instance ), ( void * ) klass );
261 self = NA_SETTINGS( instance );
263 self->private = g_new0( NASettingsPrivate, 1 );
265 self->private->dispose_has_run = FALSE;
266 self->private->mandatory = NULL;
267 self->private->user = NULL;
268 self->private->content = NULL;
269 self->private->consumers = NULL;
271 self->private->timeout.timeout = st_burst_timeout;
272 self->private->timeout.handler = ( NATimeoutFunc ) on_keyfile_changed_timeout;
273 self->private->timeout.user_data = self;
274 self->private->timeout.source_id = 0;
277 static void
278 instance_dispose( GObject *object )
280 static const gchar *thisfn = "na_settings_instance_dispose";
281 NASettings *self;
283 g_return_if_fail( NA_IS_SETTINGS( object ));
285 self = NA_SETTINGS( object );
287 if( !self->private->dispose_has_run ){
289 g_debug( "%s: object=%p (%s)", thisfn, ( void * ) object, G_OBJECT_TYPE_NAME( object ));
291 self->private->dispose_has_run = TRUE;
293 release_key_file( self->private->mandatory );
294 release_key_file( self->private->user );
296 if( G_OBJECT_CLASS( st_parent_class )->dispose ){
297 G_OBJECT_CLASS( st_parent_class )->dispose( object );
302 static void
303 instance_finalize( GObject *object )
305 static const gchar *thisfn = "na_settings_instance_finalize";
306 NASettings *self;
308 g_return_if_fail( NA_IS_SETTINGS( object ));
310 g_debug( "%s: object=%p (%s)", thisfn, ( void * ) object, G_OBJECT_TYPE_NAME( object ));
312 self = NA_SETTINGS( object );
314 g_list_foreach( self->private->content, ( GFunc ) release_key_value, NULL );
315 g_list_free( self->private->content );
317 g_list_foreach( self->private->consumers, ( GFunc ) release_consumer, NULL );
318 g_list_free( self->private->consumers );
320 g_free( self->private );
322 /* chain call to parent class */
323 if( G_OBJECT_CLASS( st_parent_class )->finalize ){
324 G_OBJECT_CLASS( st_parent_class )->finalize( object );
329 * na_settings_new:
331 * Returns: a new #NASettings object which should be g_object_unref()
332 * by the caller.
334 NASettings *
335 na_settings_new( void )
337 static const gchar *thisfn = "na_settings_new";
338 NASettings *settings;
339 gchar *dir;
341 settings = g_object_new( NA_SETTINGS_TYPE, NULL );
343 dir = g_build_filename( SYSCONFDIR, "xdg", NULL );
344 g_debug( "%s: reading mandatory configuration", thisfn );
345 settings->private->mandatory = key_file_new( settings, dir );
346 g_free( dir );
348 dir = g_build_filename( g_get_home_dir(), ".config", NULL );
349 g_debug( "%s: reading user configuration", thisfn );
350 settings->private->user = key_file_new( settings, dir );
351 g_free( dir );
353 settings->private->content = content_load( settings );
355 return( settings );
359 * na_settings_register_key_callback:
360 * @settings: this #NASettings instance.
361 * @key: the key to be monitored.
362 * @callback: the function to be called when the value of the key changes.
363 * @user_data: data to be passed to the @callback function.
365 * Registers a new consumer of the monitoring of the @key.
367 * Since: 3.1.0
369 void
370 na_settings_register_key_callback( NASettings *settings, const gchar *key, GCallback callback, gpointer user_data )
372 static const gchar *thisfn = "na_settings_register_key_callback";
374 g_return_if_fail( NA_IS_SETTINGS( settings ));
376 if( !settings->private->dispose_has_run ){
377 g_debug( "%s: settings=%p, key=%s, callback=%p, user_data=%p",
378 thisfn, ( void * ) settings, key, ( void * ) callback, ( void * ) user_data );
380 Consumer *consumer = g_new0( Consumer, 1 );
382 consumer->monitored_key = g_strdup( key );
383 consumer->callback = callback;
384 consumer->user_data = user_data;
385 settings->private->consumers = g_list_prepend( settings->private->consumers, consumer );
389 #if 0
391 * na_settings_register_global_callback:
392 * @settings: this #NASettings instance.
393 * @callback: the function to be called when the value of the key changes.
394 * @user_data: data to be passed to the @callback function.
396 * Registers a new consumer of the monitoring of the configuration files.
398 * Since: 3.1.0
400 void
401 na_settings_register_global_callback( NASettings *settings, GCallback callback, gpointer user_data )
403 g_return_if_fail( NA_IS_SETTINGS( settings ));
405 if( !settings->private->dispose_has_run ){
407 Consumer *consumer = g_new0( Consumer, 1 );
409 consumer->key = NULL;
410 consumer->callback = callback;
411 consumer->user_data = user_data;
412 settings->private->consumers = g_list_prepend( settings->private->consumers, consumer );
416 #endif
419 * na_settings_get_boolean:
420 * @settings: this #NASettings instance.
421 * @key: the key whose value is to be returned.
422 * @found: if not %NULL, a pointer to a gboolean in which we will store
423 * whether the searched @key has been found (%TRUE), or if the returned
424 * value comes from default (%FALSE).
425 * @mandatory: if not %NULL, a pointer to a gboolean in which we will store
426 * whether the returned value has been read from mandatory preferences
427 * (%TRUE), or from the user preferences (%FALSE). When the @key has not
428 * been found, @mandatory is set to %FALSE.
430 * This function should only be called for unambiguous keys; the resultat
431 * is otherwise undefined (and rather unpredictable).
433 * Returns: the value of the key, of its default value if not found.
435 * Since: 3.1.0
437 gboolean
438 na_settings_get_boolean( NASettings *settings, const gchar *key, gboolean *found, gboolean *mandatory )
440 return( na_settings_get_boolean_ex( settings, NULL, key, found, mandatory ));
444 * na_settings_get_boolean_ex:
445 * @settings: this #NASettings instance.
446 * @group: the group where the @key is to be searched for. May be %NULL.
447 * @key: the key whose value is to be returned.
448 * @found: if not %NULL, a pointer to a gboolean in which we will store
449 * whether the searched @key has been found (%TRUE), or if the returned
450 * value comes from default (%FALSE).
451 * @mandatory: if not %NULL, a pointer to a gboolean in which we will store
452 * whether the returned value has been read from mandatory preferences
453 * (%TRUE), or from the user preferences (%FALSE). When the @key has not
454 * been found, @mandatory is set to %FALSE.
456 * Returns: the value of the key, of its default value if not found.
458 * Since: 3.1.0
460 gboolean
461 na_settings_get_boolean_ex( NASettings *settings, const gchar *group, const gchar *key, gboolean *found, gboolean *mandatory )
463 gboolean value;
464 KeyValue *key_value;
465 KeyDef *key_def;
467 value = FALSE;
468 key_value = read_key_value( settings, group, key, found, mandatory );
470 if( key_value ){
471 value = na_boxed_get_boolean( key_value->boxed );
472 release_key_value( key_value );
474 } else {
475 key_def = get_key_def( key );
476 if( key_def ){
477 value = ( key_def->default_value ? ( strcasecmp( key_def->default_value, "true" ) == 0 || atoi( key_def->default_value ) != 0 ) : FALSE );
481 return( value );
485 * na_settings_get_string:
486 * @settings: this #NASettings instance.
487 * @key: the key whose value is to be returned.
488 * @found: if not %NULL, a pointer to a gboolean in which we will store
489 * whether the searched @key has been found (%TRUE), or if the returned
490 * value comes from default (%FALSE).
491 * @mandatory: if not %NULL, a pointer to a gboolean in which we will store
492 * whether the returned value has been read from mandatory preferences
493 * (%TRUE), or from the user preferences (%FALSE). When the @key has not
494 * been found, @mandatory is set to %FALSE.
496 * This function should only be called for unambiguous keys; the resultat
497 * is otherwise undefined (and rather unpredictable).
499 * Returns: the value of the key as a newly allocated string, which should
500 * be g_free() by the caller.
502 * Since: 3.1.0
504 gchar *
505 na_settings_get_string( NASettings *settings, const gchar *key, gboolean *found, gboolean *mandatory )
507 gchar *value;
508 KeyValue *key_value;
509 KeyDef *key_def;
511 value = NULL;
512 key_value = read_key_value( settings, NULL, key, found, mandatory );
514 if( key_value ){
515 value = na_boxed_get_string( key_value->boxed );
516 release_key_value( key_value );
518 } else {
519 key_def = get_key_def( key );
520 if( key_def && key_def->default_value ){
521 value = g_strdup( key_def->default_value );
525 return( value );
529 * na_settings_get_string_list:
530 * @settings: this #NASettings instance.
531 * @key: the key whose value is to be returned.
532 * @found: if not %NULL, a pointer to a gboolean in which we will store
533 * whether the searched @key has been found (%TRUE), or if the returned
534 * value comes from default (%FALSE).
535 * @mandatory: if not %NULL, a pointer to a gboolean in which we will store
536 * whether the returned value has been read from mandatory preferences
537 * (%TRUE), or from the user preferences (%FALSE). When the @key has not
538 * been found, @mandatory is set to %FALSE.
540 * This function should only be called for unambiguous keys; the resultat
541 * is otherwise undefined (and rather unpredictable).
543 * Returns: the value of the key as a newly allocated list of strings.
544 * The returned list should be na_core_utils_slist_free() by the caller.
546 * Since: 3.1.0
548 GSList *
549 na_settings_get_string_list( NASettings *settings, const gchar *key, gboolean *found, gboolean *mandatory )
551 GSList *value;
552 KeyValue *key_value;
553 KeyDef *key_def;
555 value = NULL;
556 key_value = read_key_value( settings, NULL, key, found, mandatory );
558 if( key_value ){
559 value = na_boxed_get_string_list( key_value->boxed );
560 release_key_value( key_value );
562 } else {
563 key_def = get_key_def( key );
564 if( key_def && key_def->default_value && strlen( key_def->default_value )){
565 value = g_slist_append( NULL, g_strdup( key_def->default_value ));
569 return( value );
573 * na_settings_get_uint:
574 * @settings: this #NASettings instance.
575 * @key: the key whose value is to be returned.
576 * @found: if not %NULL, a pointer to a gboolean in which we will store
577 * whether the searched @key has been found (%TRUE), or if the returned
578 * value comes from default (%FALSE).
579 * @mandatory: if not %NULL, a pointer to a gboolean in which we will store
580 * whether the returned value has been read from mandatory preferences
581 * (%TRUE), or from the user preferences (%FALSE). When the @key has not
582 * been found, @mandatory is set to %FALSE.
584 * This function should only be called for unambiguous keys; the resultat
585 * is otherwise undefined (and rather unpredictable).
587 * Returns: the value of the key.
589 * Since: 3.1.0
591 guint
592 na_settings_get_uint( NASettings *settings, const gchar *key, gboolean *found, gboolean *mandatory )
594 guint value;
595 KeyDef *key_def;
596 KeyValue *key_value;
598 value = 0;
599 key_value = read_key_value( settings, NULL, key, found, mandatory );
601 if( key_value ){
602 value = na_boxed_get_uint( key_value->boxed );
603 release_key_value( key_value );
605 } else {
606 key_def = get_key_def( key );
607 if( key_def && key_def->default_value ){
608 value = atoi( key_def->default_value );
612 return( value );
616 * na_settings_get_uint_list:
617 * @settings: this #NASettings instance.
618 * @key: the key whose value is to be returned.
619 * @found: if not %NULL, a pointer to a gboolean in which we will store
620 * whether the searched @key has been found (%TRUE), or if the returned
621 * value comes from default (%FALSE).
622 * @mandatory: if not %NULL, a pointer to a gboolean in which we will store
623 * whether the returned value has been read from mandatory preferences
624 * (%TRUE), or from the user preferences (%FALSE). When the @key has not
625 * been found, @mandatory is set to %FALSE.
627 * This function should only be called for unambiguous keys; the resultat
628 * is otherwise undefined (and rather unpredictable).
630 * Returns: the value of the key as a newly allocated list of uints.
631 * The returned list should be g_list_free() by the caller.
633 * Since: 3.1.0
635 GList *
636 na_settings_get_uint_list( NASettings *settings, const gchar *key, gboolean *found, gboolean *mandatory )
638 GList *value;
639 KeyDef *key_def;
640 KeyValue *key_value;
642 value = NULL;
643 key_value = read_key_value( settings, NULL, key, found, mandatory );
645 if( key_value ){
646 value = na_boxed_get_uint_list( key_value->boxed );
647 release_key_value( key_value );
649 } else {
650 key_def = get_key_def( key );
651 if( key_def && key_def->default_value ){
652 value = g_list_append( NULL, GUINT_TO_POINTER( atoi( key_def->default_value )));
656 return( value );
660 * na_settings_set_boolean:
661 * @settings: this #NASettings instance.
662 * @key: the key whose value is to be returned.
663 * @value: the boolean to be written.
665 * This function writes @value as a user preference.
667 * This function should only be called for unambiguous keys; the resultat
668 * is otherwise undefined (and rather unpredictable).
670 * Returns: %TRUE is the writing has been successfull, %FALSE else.
672 * Since: 3.1.0
674 gboolean
675 na_settings_set_boolean( NASettings *settings, const gchar *key, gboolean value )
677 gchar *string;
678 gboolean ok;
680 string = g_strdup_printf( "%s", value ? "true" : "false" );
681 ok = set_key_value( settings, NULL, key, string );
682 g_free( string );
684 return( ok );
688 * na_settings_set_boolean_ex:
689 * @settings: this #NASettings instance.
690 * @group: the group in the keyed file;
691 * @key: the key whose value is to be returned.
692 * @value: the boolean to be written.
694 * This function writes @value as a user preference.
696 * Returns: %TRUE is the writing has been successfull, %FALSE else.
698 * Since: 3.1.0
700 gboolean
701 na_settings_set_boolean_ex( NASettings *settings, const gchar *group, const gchar *key, gboolean value )
703 gchar *string;
704 gboolean ok;
706 string = g_strdup_printf( "%s", value ? "true" : "false" );
707 ok = set_key_value( settings, group, key, string );
708 g_free( string );
710 return( ok );
714 * na_settings_set_string:
715 * @settings: this #NASettings instance.
716 * @key: the key whose value is to be returned.
717 * @value: the string to be written.
719 * This function writes @value as a user preference.
721 * This function should only be called for unambiguous keys; the resultat
722 * is otherwise undefined (and rather unpredictable).
724 * Returns: %TRUE is the writing has been successfull, %FALSE else.
726 * Since: 3.1.0
728 gboolean
729 na_settings_set_string( NASettings *settings, const gchar *key, const gchar *value )
731 return( set_key_value( settings, NULL, key, value ));
735 * na_settings_set_string_list:
736 * @settings: this #NASettings instance.
737 * @key: the key whose value is to be returned.
738 * @value: the list of strings to be written.
740 * This function writes @value as a user preference.
742 * This function should only be called for unambiguous keys; the resultat
743 * is otherwise undefined (and rather unpredictable).
745 * Returns: %TRUE is the writing has been successfull, %FALSE else.
747 * Since: 3.1.0
749 gboolean
750 na_settings_set_string_list( NASettings *settings, const gchar *key, const GSList *value )
752 GString *string;
753 const GSList *it;
754 gboolean ok;
756 string = g_string_new( "" );
757 for( it = value ; it ; it = it->next ){
758 g_string_append_printf( string, "%s;", ( const gchar * ) it->data );
760 ok = set_key_value( settings, NULL, key, string->str );
761 g_string_free( string, TRUE );
763 return( ok );
767 * na_settings_set_uint:
768 * @settings: this #NASettings instance.
769 * @key: the key whose value is to be returned.
770 * @value: the unsigned integer to be written.
772 * This function writes @value as a user preference.
774 * This function should only be called for unambiguous keys; the resultat
775 * is otherwise undefined (and rather unpredictable).
777 * Returns: %TRUE is the writing has been successfull, %FALSE else.
779 * Since: 3.1.0
781 gboolean
782 na_settings_set_uint( NASettings *settings, const gchar *key, guint value )
784 gchar *string;
785 gboolean ok;
787 string = g_strdup_printf( "%u", value );
788 ok = set_key_value( settings, NULL, key, string );
789 g_free( string );
791 return( ok );
795 * na_settings_set_uint_list:
796 * @settings: this #NASettings instance.
797 * @key: the key whose value is to be returned.
798 * @value: the list of unsigned integers to be written.
800 * This function writes @value as a user preference.
802 * This function should only be called for unambiguous keys; the resultat
803 * is otherwise undefined (and rather unpredictable).
805 * Returns: %TRUE is the writing has been successfull, %FALSE else.
807 * Since: 3.1.0
809 gboolean
810 na_settings_set_uint_list( NASettings *settings, const gchar *key, const GList *value )
812 GString *string;
813 const GList *it;
814 gboolean ok;
816 string = g_string_new( "" );
817 for( it = value ; it ; it = it->next ){
818 g_string_append_printf( string, "%u;", GPOINTER_TO_UINT( it->data ));
820 ok = set_key_value( settings, NULL, key, string->str );
821 g_string_free( string, TRUE );
823 return( ok );
827 * na_settings_get_groups:
828 * @settings: this #NASettings instance.
830 * Returns: the list of groups in the configuration; this list should be
831 * na_core_utils_slist_free() by the caller.
833 * This function participates to a rather bad hack to obtain the list of
834 * known i/o providers from preferences. We do not care of returning unique
835 * or sorted group names.
837 * Since: 3.1.0
839 GSList *
840 na_settings_get_groups( NASettings *settings )
842 GSList *groups;
843 gchar **array;
845 groups = NULL;
847 g_return_val_if_fail( NA_IS_SETTINGS( settings ), NULL );
849 if( !settings->private->dispose_has_run ){
851 array = g_key_file_get_groups( settings->private->mandatory->key_file, NULL );
852 if( array ){
853 groups = na_core_utils_slist_from_array(( const gchar ** ) array );
854 g_strfreev( array );
857 array = g_key_file_get_groups( settings->private->user->key_file, NULL );
858 if( array ){
859 groups = g_slist_concat( groups, na_core_utils_slist_from_array(( const gchar ** ) array ));
860 g_strfreev( array );
864 return( groups );
868 * returns a list of modified KeyValue
869 * - order in the lists is not signifiant
870 * - the mandatory flag is not signifiant
871 * - a key is modified:
872 * > if it appears in new
873 * > if it disappears: the value is so reset to its default
874 * > if the value has been modified
876 * we return here a new list, with newly allocated KeyValue structs
877 * which hold the new value of each modified key
879 static GList *
880 content_diff( GList *old, GList *new )
882 GList *diffs, *io, *in;
883 KeyValue *kold, *knew, *kdiff;
884 gboolean found;
885 KeyDef *key_def;
887 diffs = NULL;
889 for( io = old ; io ; io = io->next ){
890 kold = ( KeyValue * ) io->data;
891 found = FALSE;
892 for( in = new ; in && !found ; in = in->next ){
893 knew = ( KeyValue * ) in->data;
894 if( !strcmp( kold->group, knew->group ) && !strcmp( kold->key, knew->key )){
895 found = TRUE;
896 if( na_boxed_compare( kold->boxed, knew->boxed ) != 0 ){
897 /* a key has been modified */
898 kdiff = g_new0( KeyValue, 1 );
899 kdiff->group = g_strdup( knew->group );
900 kdiff->key = g_strdup( knew->key );
901 kdiff->mandatory = knew->mandatory;
902 kdiff->boxed = na_boxed_copy( knew->boxed );
903 diffs = g_list_prepend( diffs, kdiff );
907 if( !found ){
908 key_def = get_key_def( kold->key );
909 if( key_def ){
910 /* a key has disappeared */
911 kdiff = g_new0( KeyValue, 1 );
912 kdiff->group = g_strdup( kold->group );
913 kdiff->key = g_strdup( kold->key );
914 kdiff->mandatory = FALSE;
915 kdiff->boxed = na_boxed_new_from_string( key_def->type, key_def->default_value );
916 diffs = g_list_prepend( diffs, kdiff );
921 for( in = new ; in ; in = in->next ){
922 knew = ( KeyValue * ) in->data;
923 found = FALSE;
924 for( io = old ; io && !found ; io = io->next ){
925 kold = ( KeyValue * ) io->data;
926 if( !strcmp( kold->group, knew->group ) && !strcmp( kold->key, knew->key )){
927 found = TRUE;
930 if( !found ){
931 key_def = get_key_def( knew->key );
932 if( key_def ){
933 /* a key is new */
934 kdiff = g_new0( KeyValue, 1 );
935 kdiff->group = g_strdup( knew->group );
936 kdiff->key = g_strdup( knew->key );
937 kdiff->mandatory = knew->mandatory;
938 kdiff->boxed = na_boxed_copy( knew->boxed );
939 diffs = g_list_prepend( diffs, kdiff );
944 return( diffs );
947 /* load the content of the two configuration files (actually of _the_ configuration)
948 * taking care of not overriding mandatory preferences with user ones
950 static GList *
951 content_load( NASettings *settings )
953 GList *content;
955 content = content_load_keys( settings, NULL, settings->private->mandatory, TRUE );
956 content = content_load_keys( settings, content, settings->private->user, FALSE );
958 return( content );
961 static GList *
962 content_load_keys( NASettings *settings, GList *content, KeyFile *key_file, gboolean mandatory )
964 static const gchar *thisfn = "na_settings_content_load_keys";
965 GError *error;
966 gchar **groups, **ig;
967 gchar **keys, **ik;
968 KeyValue *key_value;
969 KeyDef *key_def;
971 error = NULL;
972 if( !g_key_file_load_from_file( key_file->key_file, key_file->fname, G_KEY_FILE_KEEP_COMMENTS, &error )){
973 if( error->code != G_FILE_ERROR_NOENT ){
974 g_warning( "%s: %s (%d) %s", thisfn, key_file->fname, error->code, error->message );
976 g_error_free( error );
977 error = NULL;
979 } else {
980 groups = g_key_file_get_groups( key_file->key_file, NULL );
981 ig = groups;
982 while( *ig ){
983 keys = g_key_file_get_keys( key_file->key_file, *ig, NULL, NULL );
984 ik = keys;
985 while( *ik ){
986 key_def = get_key_def( *ik );
987 if( key_def ){
988 key_value = peek_key_value_from_content( content, *ig, *ik );
989 if( !key_value ){
990 key_value = read_key_value_from_key_file( key_file->key_file, *ig, *ik, key_def );
991 if( key_value ){
992 key_value->mandatory = mandatory;
993 content = g_list_prepend( content, key_value );
997 ik++;
999 g_strfreev( keys );
1000 ig++;
1002 g_strfreev( groups );
1005 return( content );
1008 static KeyDef *
1009 get_key_def( const gchar *key )
1011 static const gchar *thisfn = "na_settings_get_key_def";
1012 KeyDef *found = NULL;
1013 KeyDef *idef;
1015 idef = ( KeyDef * ) st_def_keys;
1016 while( idef->key && !found ){
1017 if( !strcmp( idef->key, key )){
1018 found = idef;
1020 idef++;
1022 if( !found ){
1023 g_warning( "%s: no KeyDef found for key=%s", thisfn, key );
1026 return( found );
1030 * called from na_settings_new
1031 * allocate and load the key files for global and user preferences
1033 static KeyFile *
1034 key_file_new( NASettings *settings, const gchar *dir )
1036 static const gchar *thisfn = "na_settings_key_file_new";
1037 KeyFile *key_file;
1038 GError *error;
1039 GFile *file;
1041 key_file = g_new0( KeyFile, 1 );
1043 key_file->key_file = g_key_file_new();
1044 key_file->fname = g_strdup_printf( "%s/%s.conf", dir, PACKAGE );
1046 error = NULL;
1047 file = g_file_new_for_path( key_file->fname );
1048 key_file->monitor = g_file_monitor_file( file, 0, NULL, &error );
1049 if( error ){
1050 g_warning( "%s: %s: %s", thisfn, key_file->fname, error->message );
1051 g_error_free( error );
1052 error = NULL;
1053 } else {
1054 key_file->handler = g_signal_connect( key_file->monitor, "changed", ( GCallback ) on_keyfile_changed, settings );
1056 g_object_unref( file );
1058 return( key_file );
1062 * one of the two monitored configuration files have changed on the disk
1063 * we do not try to identify which keys have actually change
1064 * instead we trigger each registered consumer for the 'global' event
1066 * consumers which register for the 'global_conf' event are recorded
1067 * with a NULL key
1069 static void
1070 on_keyfile_changed( GFileMonitor *monitor,
1071 GFile *file, GFile *other_file, GFileMonitorEvent event_type, NASettings *settings )
1073 g_return_if_fail( NA_IS_SETTINGS( settings ));
1075 if( !settings->private->dispose_has_run ){
1077 na_timeout_event( &settings->private->timeout );
1081 static void
1082 on_keyfile_changed_timeout( NASettings *settings )
1084 static const gchar *thisfn = "na_settings_on_keyfile_changed_timeout";
1085 GList *new_content;
1086 GList *modifs;
1087 GList *ic, *im;
1088 gchar *value;
1089 Consumer *consumer;
1090 KeyValue *changed;
1091 gchar *group_prefix, *key;
1093 /* last individual notification is older that the st_burst_timeout
1094 * we may so suppose that the burst is terminated
1096 new_content = content_load( settings );
1097 modifs = content_diff( settings->private->content, new_content );
1098 g_debug( "%s: %d found update(s)", thisfn, g_list_length( modifs ));
1099 for( im = modifs ; im ; im = im->next ){
1100 changed = ( KeyValue * ) im->data;
1101 value = na_boxed_get_string( changed->boxed );
1102 g_debug( "%s: key=%s, value=%s", thisfn, changed->key, value );
1103 g_free( value );
1106 for( ic = settings->private->consumers ; ic ; ic = ic->next ){
1107 consumer = ( Consumer * ) ic->data;
1109 group_prefix = NULL;
1110 if( !strcmp( consumer->monitored_key, NA_IPREFS_IO_PROVIDERS_READ_STATUS )){
1111 group_prefix = g_strdup_printf( "%s ", NA_IPREFS_IO_PROVIDER_GROUP );
1112 key = NA_IPREFS_IO_PROVIDER_READABLE;
1113 } else {
1114 key = consumer->monitored_key;
1117 for( im = modifs ; im ; im = im->next ){
1118 changed = ( KeyValue * ) im->data;
1119 if(( !group_prefix || g_str_has_prefix( changed->group, group_prefix )) && !strcmp( changed->key, key )){
1120 ( *( NASettingsKeyCallback ) consumer->callback )( changed->group, changed->key, na_boxed_get_pointer( changed->boxed ), changed->mandatory, consumer->user_data );
1124 g_free( group_prefix );
1127 g_list_foreach( settings->private->content, ( GFunc ) release_key_value, NULL );
1128 g_list_free( settings->private->content );
1129 settings->private->content = new_content;
1131 g_list_foreach( modifs, ( GFunc ) release_key_value, NULL );
1132 g_list_free( modifs );
1135 static KeyValue *
1136 peek_key_value_from_content( GList *content, const gchar *group, const gchar *key )
1138 KeyValue *value, *found;
1139 GList *ic;
1141 found = NULL;
1142 for( ic = content ; ic && !found ; ic = ic->next ){
1143 value = ( KeyValue * ) ic->data;
1144 if( !strcmp( value->group, group ) && !strcmp( value->key, key )){
1145 found = value;
1149 return( found );
1152 /* group may be NULL
1154 static KeyValue *
1155 read_key_value( NASettings *settings, const gchar *group, const gchar *key, gboolean *found, gboolean *mandatory )
1157 KeyDef *key_def;
1158 gboolean has_entry;
1159 KeyValue *key_value;
1161 g_return_val_if_fail( NA_IS_SETTINGS( settings ), NULL );
1163 key_value = NULL;
1164 if( found ){
1165 *found = FALSE;
1167 if( mandatory ){
1168 *mandatory = FALSE;
1171 if( !settings->private->dispose_has_run ){
1173 key_def = get_key_def( key );
1174 if( key_def ){
1175 has_entry = FALSE;
1176 key_value = read_key_value_from_key_file( settings->private->mandatory->key_file, group ? group : key_def->group, key, key_def );
1177 if( key_value ){
1178 has_entry = TRUE;
1179 if( found ){
1180 *found = TRUE;
1182 if( mandatory ){
1183 *mandatory = TRUE;
1186 if( !has_entry ){
1187 key_value = read_key_value_from_key_file( settings->private->user->key_file, group ? group : key_def->group, key, key_def );
1188 if( key_value ){
1189 has_entry = TRUE;
1190 if( found ){
1191 *found = TRUE;
1198 return( key_value );
1201 static KeyValue *
1202 read_key_value_from_key_file( GKeyFile *key_file, const gchar *group, const gchar *key, const KeyDef *key_def )
1204 static const gchar *thisfn = "na_settings_read_key_value_from_key_file";
1205 KeyValue *value;
1206 gchar *str;
1207 GError *error;
1209 value = NULL;
1210 error = NULL;
1212 switch( key_def->type ){
1214 case NA_BOXED_TYPE_STRING:
1215 case NA_BOXED_TYPE_STRING_LIST:
1216 case NA_BOXED_TYPE_UINT:
1217 case NA_BOXED_TYPE_UINT_LIST:
1218 case NA_BOXED_TYPE_BOOLEAN:
1219 str = g_key_file_get_string( key_file, group, key, &error );
1220 if( error ){
1221 if( error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND && error->code != G_KEY_FILE_ERROR_GROUP_NOT_FOUND ){
1222 g_warning( "%s: %s", thisfn, error->message );
1224 g_error_free( error );
1226 /* key exists, but may be empty */
1227 } else {
1228 value = g_new0( KeyValue, 1 );
1229 value->group = g_strdup( group );
1230 value->key = g_strdup( key );
1231 switch( key_def->type ){
1232 case NA_BOXED_TYPE_STRING:
1233 case NA_BOXED_TYPE_UINT:
1234 case NA_BOXED_TYPE_BOOLEAN:
1235 value->boxed = na_boxed_new_from_string( key_def->type, str );
1236 break;
1237 case NA_BOXED_TYPE_STRING_LIST:
1238 case NA_BOXED_TYPE_UINT_LIST:
1239 value->boxed = na_boxed_new_from_string_with_sep( key_def->type, str, ";" );
1240 break;
1243 g_free( str );
1244 break;
1246 default:
1247 g_warning( "%s: unmanaged boxed type: %d", thisfn, key_def->type );
1248 break;
1251 return( value );
1255 * called from instance_finalize
1256 * release the list of registered consumers
1258 static void
1259 release_consumer( Consumer *consumer )
1261 g_free( consumer->monitored_key );
1262 g_free( consumer );
1266 * called from instance_dispose
1267 * release the opened and monitored GKeyFiles
1269 static void
1270 release_key_file( KeyFile *key_file )
1272 g_key_file_free( key_file->key_file );
1273 if( key_file->monitor ){
1274 if( key_file->handler ){
1275 g_signal_handler_disconnect( key_file->monitor, key_file->handler );
1277 g_file_monitor_cancel( key_file->monitor );
1278 g_object_unref( key_file->monitor );
1280 g_free( key_file->fname );
1281 g_free( key_file );
1285 * called from instance_finalize
1286 * release a KeyValue struct
1288 static void
1289 release_key_value( KeyValue *value )
1291 g_free( value->group );
1292 g_free( value->key );
1293 na_boxed_free( value->boxed );
1294 g_free( value );
1297 static gboolean
1298 set_key_value( NASettings *settings, const gchar *group, const gchar *key, const gchar *string )
1300 KeyDef *key_def;
1301 const gchar *wgroup;
1302 gboolean ok;
1304 g_return_val_if_fail( NA_IS_SETTINGS( settings ), FALSE );
1306 ok = FALSE;
1308 if( !settings->private->dispose_has_run ){
1310 wgroup = group;
1311 if( !wgroup ){
1312 key_def = get_key_def( key );
1313 if( key_def ){
1314 wgroup = key_def->group;
1317 if( wgroup ){
1318 g_key_file_set_string( settings->private->user->key_file, wgroup, key, string );
1319 ok = write_user_key_file( settings );
1323 return( ok );
1326 static gboolean
1327 write_user_key_file( NASettings *settings )
1329 static const gchar *thisfn = "na_settings_write_user_key_file";
1330 gchar *data;
1331 GFile *file;
1332 GFileOutputStream *stream;
1333 GError *error;
1334 gsize length;
1336 error = NULL;
1337 data = g_key_file_to_data( settings->private->user->key_file, &length, NULL );
1338 file = g_file_new_for_path( settings->private->user->fname );
1340 stream = g_file_replace( file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &error );
1341 if( error ){
1342 g_warning( "%s: g_file_replace: %s", thisfn, error->message );
1343 g_error_free( error );
1344 if( stream ){
1345 g_object_unref( stream );
1347 g_object_unref( file );
1348 g_free( data );
1349 return( FALSE );
1352 g_output_stream_write( G_OUTPUT_STREAM( stream ), data, length, NULL, &error );
1353 if( error ){
1354 g_warning( "%s: g_output_stream_write: %s", thisfn, error->message );
1355 g_error_free( error );
1356 g_object_unref( stream );
1357 g_object_unref( file );
1358 g_free( data );
1359 return( FALSE );
1362 g_output_stream_close( G_OUTPUT_STREAM( stream ), NULL, &error );
1363 if( error ){
1364 g_warning( "%s: g_output_stream_close: %s", thisfn, error->message );
1365 g_error_free( error );
1366 g_object_unref( stream );
1367 g_object_unref( file );
1368 g_free( data );
1369 return( FALSE );
1372 g_object_unref( stream );
1373 g_object_unref( file );
1374 g_free( data );
1376 return( TRUE );