14 #include "userdefaults.h"
17 typedef struct W_UserDefaults
{
20 WMPropList
*appDomain
;
22 WMPropList
*searchListArray
;
23 WMPropList
**searchList
; /* cache for searchListArray */
29 char *path
; /* where is db located */
31 time_t timestamp
; /* last modification time */
33 struct W_UserDefaults
*next
;
37 static UserDefaults
*sharedUserDefaults
= NULL
;
39 char *WMUserDefaultsDidChangeNotification
= "WMUserDefaultsDidChangeNotification";
41 static void synchronizeUserDefaults(void *foo
);
43 #define DEFAULTS_DIR "/Defaults"
45 /* Check defaults database for changes every this many milliseconds */
46 /* XXX: this is shared with src/ stuff, put it in some common header */
47 #define UD_SYNC_INTERVAL 2000
50 const char *wusergnusteppath()
52 static const char subdir
[] = "/GNUstep";
53 static char *path
= NULL
;
58 /* Value have been already computed, re-use it */
61 gspath
= getenv("GNUSTEP_USER_ROOT");
63 gspath
= wexpandpath(gspath
);
68 wwarning(_("variable GNUSTEP_USER_ROOT defined with invalid path, not used"));
76 path
= wmalloc(pathlen
+ sizeof(subdir
));
78 strcpy(path
+ pathlen
, subdir
);
83 char *wdefaultspathfordomain(const char *domain
)
89 gspath
= wusergnusteppath();
90 slen
= strlen(gspath
) + strlen(DEFAULTS_DIR
) + strlen(domain
) + 4;
94 strcat(path
, DEFAULTS_DIR
);
101 /* XXX: doesn't quite belong to *user*defaults.c */
102 #ifndef GLOBAL_DEFAULTS_SUBDIR
103 #define GLOBAL_DEFAULTS_SUBDIR "WindowMaker"
105 char *wglobaldefaultspathfordomain(const char *domain
)
110 len
= strlen( SYSCONFDIR
) + strlen( GLOBAL_DEFAULTS_SUBDIR
) + strlen(domain
) + 3;
112 snprintf(t
, len
, "%s/%s/%s", SYSCONFDIR
, GLOBAL_DEFAULTS_SUBDIR
, domain
);
117 void w_save_defaults_changes(void)
119 if (WMApplication
.applicationName
== NULL
) {
121 * This means that the user has properly exited by calling the
122 * function 'WMReleaseApplication' (which has already called us)
123 * but we're being called again by the fallback 'atexit' method
124 * (the legacy way of saving changes on exit which is kept for
125 * application that would forget to call 'WMReleaseApplication')
130 /* save the user defaults databases */
131 synchronizeUserDefaults(NULL
);
134 /* set to save changes in defaults when program is exited */
135 static void registerSaveOnExit(void)
137 static Bool registeredSaveOnExit
= False
;
139 if (!registeredSaveOnExit
) {
140 atexit(w_save_defaults_changes
);
141 registeredSaveOnExit
= True
;
145 static void synchronizeUserDefaults(void *foo
)
147 UserDefaults
*database
= sharedUserDefaults
;
149 /* Parameter not used, but tell the compiler that it is ok */
153 if (!database
->dontSync
)
154 WMSynchronizeUserDefaults(database
);
155 database
= database
->next
;
160 static void addSynchronizeTimerHandler(void)
162 static Bool initialized
= False
;
165 WMAddPersistentTimerHandler(UD_SYNC_INTERVAL
,
166 synchronizeUserDefaults
, NULL
);
172 void WMEnableUDPeriodicSynchronization(WMUserDefaults
* database
, Bool enable
)
174 database
->dontSync
= !enable
;
177 void WMSynchronizeUserDefaults(WMUserDefaults
* database
)
179 Bool fileIsNewer
= False
, release
= False
, notify
= False
;
180 WMPropList
*plF
, *key
;
184 if (!database
->path
) {
185 path
= wdefaultspathfordomain(WMGetApplicationName());
188 path
= database
->path
;
191 if (stat(path
, &stbuf
) >= 0 && stbuf
.st_mtime
> database
->timestamp
)
194 if (database
->appDomain
&& (database
->dirty
|| fileIsNewer
)) {
195 if (database
->dirty
&& fileIsNewer
) {
196 plF
= WMReadPropListFromFile(path
);
198 plF
= WMMergePLDictionaries(plF
, database
->appDomain
, False
);
199 WMReleasePropList(database
->appDomain
);
200 database
->appDomain
= plF
;
201 key
= database
->searchList
[0];
202 WMPutInPLDictionary(database
->defaults
, key
, plF
);
205 /* something happened with the file. just overwrite it */
206 wwarning(_("cannot read domain from file '%s' when syncing"), path
);
207 WMWritePropListToFile(database
->appDomain
, path
);
209 } else if (database
->dirty
) {
210 WMWritePropListToFile(database
->appDomain
, path
);
211 } else if (fileIsNewer
) {
212 plF
= WMReadPropListFromFile(path
);
214 WMReleasePropList(database
->appDomain
);
215 database
->appDomain
= plF
;
216 key
= database
->searchList
[0];
217 WMPutInPLDictionary(database
->defaults
, key
, plF
);
220 /* something happened with the file. just overwrite it */
221 wwarning(_("cannot read domain from file '%s' when syncing"), path
);
222 WMWritePropListToFile(database
->appDomain
, path
);
228 if (stat(path
, &stbuf
) >= 0)
229 database
->timestamp
= stbuf
.st_mtime
;
232 WMPostNotificationName(WMUserDefaultsDidChangeNotification
, database
, NULL
);
241 void WMSaveUserDefaults(WMUserDefaults
* database
)
243 if (database
->appDomain
) {
246 Bool release
= False
;
248 if (!database
->path
) {
249 path
= wdefaultspathfordomain(WMGetApplicationName());
252 path
= database
->path
;
254 WMWritePropListToFile(database
->appDomain
, path
);
256 if (stat(path
, &stbuf
) >= 0)
257 database
->timestamp
= stbuf
.st_mtime
;
263 WMUserDefaults
*WMGetStandardUserDefaults(void)
265 WMUserDefaults
*defaults
;
272 if (sharedUserDefaults
) {
273 defaults
= sharedUserDefaults
;
275 /* path == NULL only for StandardUserDefaults db */
276 if (defaults
->path
== NULL
)
278 defaults
= defaults
->next
;
282 /* we didn't found the database we are looking for. Go read it. XXX: wtf? */
283 defaults
= wmalloc(sizeof(WMUserDefaults
));
284 defaults
->defaults
= WMCreatePLDictionary(NULL
, NULL
);
285 defaults
->searchList
= wmalloc(sizeof(WMPropList
*) * 3);
287 /* application domain */
288 key
= WMCreatePLString(WMGetApplicationName());
289 defaults
->searchList
[0] = key
;
291 /* temporary kluge. wmaker handles synchronization itself */
292 if (strcmp(WMGetApplicationName(), "WindowMaker") == 0) {
293 defaults
->dontSync
= 1;
296 path
= wdefaultspathfordomain(WMGetFromPLString(key
));
298 if (stat(path
, &stbuf
) >= 0)
299 defaults
->timestamp
= stbuf
.st_mtime
;
301 domain
= WMReadPropListFromFile(path
);
304 domain
= WMCreatePLDictionary(NULL
, NULL
);
309 defaults
->appDomain
= domain
;
312 WMPutInPLDictionary(defaults
->defaults
, key
, domain
);
315 key
= WMCreatePLString("WMGLOBAL");
316 defaults
->searchList
[1] = key
;
318 path
= wdefaultspathfordomain(WMGetFromPLString(key
));
320 domain
= WMReadPropListFromFile(path
);
325 domain
= WMCreatePLDictionary(NULL
, NULL
);
328 WMPutInPLDictionary(defaults
->defaults
, key
, domain
);
331 defaults
->searchList
[2] = NULL
;
333 defaults
->searchListArray
= WMCreatePLArray(NULL
, NULL
);
336 while (defaults
->searchList
[i
]) {
337 WMAddToPLArray(defaults
->searchListArray
, defaults
->searchList
[i
]);
341 if (sharedUserDefaults
)
342 defaults
->next
= sharedUserDefaults
;
343 sharedUserDefaults
= defaults
;
346 addSynchronizeTimerHandler();
348 registerSaveOnExit();
353 WMUserDefaults
*WMGetDefaultsFromPath(const char *path
)
355 WMUserDefaults
*defaults
;
362 assert(path
!= NULL
);
364 if (sharedUserDefaults
) {
365 defaults
= sharedUserDefaults
;
367 if (defaults
->path
&& strcmp(defaults
->path
, path
) == 0)
369 defaults
= defaults
->next
;
373 /* we didn't found the database we are looking for. Go read it. XXX wtf? */
374 defaults
= wmalloc(sizeof(WMUserDefaults
));
375 defaults
->defaults
= WMCreatePLDictionary(NULL
, NULL
);
376 defaults
->searchList
= wmalloc(sizeof(WMPropList
*) * 2);
378 /* the domain we want, go in the first position */
379 name
= strrchr(path
, '/');
385 key
= WMCreatePLString(name
);
386 defaults
->searchList
[0] = key
;
388 if (stat(path
, &stbuf
) >= 0)
389 defaults
->timestamp
= stbuf
.st_mtime
;
391 domain
= WMReadPropListFromFile(path
);
394 domain
= WMCreatePLDictionary(NULL
, NULL
);
396 defaults
->path
= wstrdup(path
);
398 defaults
->appDomain
= domain
;
401 WMPutInPLDictionary(defaults
->defaults
, key
, domain
);
404 defaults
->searchList
[1] = NULL
;
406 defaults
->searchListArray
= WMCreatePLArray(NULL
, NULL
);
409 while (defaults
->searchList
[i
]) {
410 WMAddToPLArray(defaults
->searchListArray
, defaults
->searchList
[i
]);
414 if (sharedUserDefaults
)
415 defaults
->next
= sharedUserDefaults
;
416 sharedUserDefaults
= defaults
;
419 addSynchronizeTimerHandler();
421 registerSaveOnExit();
426 /* Returns a WMPropList array with all keys in the user defaults database.
427 * Free the array with WMReleasePropList() when no longer needed,
428 * but do not free the elements of the array! They're just references. */
429 WMPropList
*WMGetUDKeys(WMUserDefaults
* database
)
431 return WMGetPLDictionaryKeys(database
->appDomain
);
434 WMPropList
*WMGetUDObjectForKey(WMUserDefaults
* database
, const char *defaultName
)
436 WMPropList
*domainName
, *domain
;
437 WMPropList
*object
= NULL
;
438 WMPropList
*key
= WMCreatePLString(defaultName
);
441 while (database
->searchList
[i
] && !object
) {
442 domainName
= database
->searchList
[i
];
443 domain
= WMGetFromPLDictionary(database
->defaults
, domainName
);
445 object
= WMGetFromPLDictionary(domain
, key
);
449 WMReleasePropList(key
);
454 void WMSetUDObjectForKey(WMUserDefaults
* database
, WMPropList
* object
, const char *defaultName
)
456 WMPropList
*key
= WMCreatePLString(defaultName
);
460 WMPutInPLDictionary(database
->appDomain
, key
, object
);
461 WMReleasePropList(key
);
464 void WMRemoveUDObjectForKey(WMUserDefaults
* database
, const char *defaultName
)
466 WMPropList
*key
= WMCreatePLString(defaultName
);
470 WMRemoveFromPLDictionary(database
->appDomain
, key
);
472 WMReleasePropList(key
);
475 char *WMGetUDStringForKey(WMUserDefaults
* database
, const char *defaultName
)
479 val
= WMGetUDObjectForKey(database
, defaultName
);
484 if (!WMIsPLString(val
))
487 return WMGetFromPLString(val
);
490 int WMGetUDIntegerForKey(WMUserDefaults
* database
, const char *defaultName
)
496 val
= WMGetUDObjectForKey(database
, defaultName
);
501 if (!WMIsPLString(val
))
504 str
= WMGetFromPLString(val
);
508 if (sscanf(str
, "%i", &value
) != 1)
514 float WMGetUDFloatForKey(WMUserDefaults
* database
, const char *defaultName
)
520 val
= WMGetUDObjectForKey(database
, defaultName
);
522 if (!val
|| !WMIsPLString(val
))
525 if (!(str
= WMGetFromPLString(val
)))
528 if (sscanf(str
, "%f", &value
) != 1)
534 Bool
WMGetUDBoolForKey(WMUserDefaults
* database
, const char *defaultName
)
540 val
= WMGetUDObjectForKey(database
, defaultName
);
545 if (!WMIsPLString(val
))
548 str
= WMGetFromPLString(val
);
552 if (sscanf(str
, "%i", &value
) == 1 && value
!= 0)
555 if (strcasecmp(str
, "YES") == 0)
558 if (strcasecmp(str
, "Y") == 0)
564 void WMSetUDIntegerForKey(WMUserDefaults
* database
, int value
, const char *defaultName
)
569 sprintf(buffer
, "%i", value
);
570 object
= WMCreatePLString(buffer
);
572 WMSetUDObjectForKey(database
, object
, defaultName
);
573 WMReleasePropList(object
);
576 void WMSetUDStringForKey(WMUserDefaults
* database
, const char *value
, const char *defaultName
)
580 object
= WMCreatePLString(value
);
582 WMSetUDObjectForKey(database
, object
, defaultName
);
583 WMReleasePropList(object
);
586 void WMSetUDFloatForKey(WMUserDefaults
* database
, float value
, const char *defaultName
)
591 sprintf(buffer
, "%f", value
);
592 object
= WMCreatePLString(buffer
);
594 WMSetUDObjectForKey(database
, object
, defaultName
);
595 WMReleasePropList(object
);
598 void WMSetUDBoolForKey(WMUserDefaults
* database
, Bool value
, const char *defaultName
)
600 static WMPropList
*yes
= NULL
, *no
= NULL
;
603 yes
= WMCreatePLString("YES");
604 no
= WMCreatePLString("NO");
607 WMSetUDObjectForKey(database
, value
? yes
: no
, defaultName
);
610 WMPropList
*WMGetUDSearchList(WMUserDefaults
* database
)
612 return database
->searchListArray
;
615 void WMSetUDSearchList(WMUserDefaults
* database
, WMPropList
* list
)
619 if (database
->searchList
) {
621 while (database
->searchList
[i
]) {
622 WMReleasePropList(database
->searchList
[i
]);
625 wfree(database
->searchList
);
627 if (database
->searchListArray
) {
628 WMReleasePropList(database
->searchListArray
);
631 c
= WMGetPropListItemCount(list
);
632 database
->searchList
= wmalloc(sizeof(WMPropList
*) * (c
+ 1));
634 for (i
= 0; i
< c
; i
++) {
635 database
->searchList
[i
] = WMGetFromPLArray(list
, i
);
637 database
->searchList
[c
] = NULL
;
639 database
->searchListArray
= WMDeepCopyPropList(list
);