14 typedef struct W_UserDefaults
{
17 WMPropList
*appDomain
;
19 WMPropList
*searchListArray
;
20 WMPropList
**searchList
; /* cache for searchListArray */
26 char *path
; /* where is db located */
28 time_t timestamp
; /* last modification time */
30 struct W_UserDefaults
*next
;
34 static UserDefaults
*sharedUserDefaults
= NULL
;
36 char *WMUserDefaultsDidChangeNotification
= "WMUserDefaultsDidChangeNotification";
38 static void synchronizeUserDefaults(void *foo
);
40 extern char *WMGetApplicationName();
42 #define DEFAULTS_DIR "/Defaults"
44 /* Check defaults database for changes every this many milliseconds */
45 /* XXX: this is shared with src/ stuff, put it in some common header */
46 #define UD_SYNC_INTERVAL 2000
49 char *wusergnusteppath()
51 static char *path
= NULL
;
55 gspath
= getenv("GNUSTEP_USER_ROOT");
57 gspath
= wexpandpath(gspath
);
59 pathlen
= strlen(gspath
) + 4;
60 path
= wmalloc(pathlen
);
61 if (wstrlcpy(path
, gspath
, pathlen
) >= pathlen
) {
68 char *h
= wgethomedir();
71 pathlen
= strlen(h
) + 8 /* /GNUstep */ + 1;
72 path
= wmalloc(pathlen
);
73 if (wstrlcpy(path
, h
, pathlen
) >= pathlen
||
74 wstrlcat(path
, "/GNUstep", pathlen
) >= pathlen
) {
83 char *wdefaultspathfordomain(char *domain
)
89 gspath
= wusergnusteppath();
90 slen
= strlen(gspath
) + strlen(DEFAULTS_DIR
) + strlen(domain
) + 4;
93 if (wstrlcpy(path
, gspath
, slen
) >= slen
||
94 wstrlcat(path
, DEFAULTS_DIR
, slen
) >= slen
||
95 wstrlcat(path
, "/", slen
) >= slen
||
96 wstrlcat(path
, domain
, slen
) >= slen
) {
104 /* XXX: doesn't quite belong to *user*defaults.c */
105 #ifndef GLOBAL_DEFAULTS_SUBDIR
106 #define GLOBAL_DEFAULTS_SUBDIR "WindowMaker"
108 char *wglobaldefaultspathfordomain(const char *domain
)
113 len
= strlen( SYSCONFDIR
) + strlen( GLOBAL_DEFAULTS_SUBDIR
) + strlen(domain
) + 3;
115 snprintf(t
, len
, "%s/%s/%s", SYSCONFDIR
, GLOBAL_DEFAULTS_SUBDIR
, domain
);
121 saveDefaultsChanges(void)
123 /* save the user defaults databases */
124 synchronizeUserDefaults(NULL
);
127 /* set to save changes in defaults when program is exited */
128 static void registerSaveOnExit(void)
130 static Bool registeredSaveOnExit
= False
;
132 if (!registeredSaveOnExit
) {
133 atexit(saveDefaultsChanges
);
134 registeredSaveOnExit
= True
;
138 static void synchronizeUserDefaults(void *foo
)
140 UserDefaults
*database
= sharedUserDefaults
;
143 if (!database
->dontSync
)
144 WMSynchronizeUserDefaults(database
);
145 database
= database
->next
;
150 static void addSynchronizeTimerHandler(void)
152 static Bool initialized
= False
;
155 WMAddPersistentTimerHandler(UD_SYNC_INTERVAL
,
156 synchronizeUserDefaults
, NULL
);
162 void WMEnableUDPeriodicSynchronization(WMUserDefaults
* database
, Bool enable
)
164 database
->dontSync
= !enable
;
167 void WMSynchronizeUserDefaults(WMUserDefaults
* database
)
169 Bool fileIsNewer
= False
, release
= False
, notify
= False
;
170 WMPropList
*plF
, *key
;
174 if (!database
->path
) {
175 path
= wdefaultspathfordomain(WMGetApplicationName());
178 path
= database
->path
;
181 if (stat(path
, &stbuf
) >= 0 && stbuf
.st_mtime
> database
->timestamp
)
184 if (database
->appDomain
&& (database
->dirty
|| fileIsNewer
)) {
185 if (database
->dirty
&& fileIsNewer
) {
186 plF
= WMReadPropListFromFile(path
);
188 plF
= WMMergePLDictionaries(plF
, database
->appDomain
, False
);
189 WMReleasePropList(database
->appDomain
);
190 database
->appDomain
= plF
;
191 key
= database
->searchList
[0];
192 WMPutInPLDictionary(database
->defaults
, key
, plF
);
195 /* something happened with the file. just overwrite it */
196 wwarning(_("cannot read domain from file '%s' when syncing"), path
);
197 WMWritePropListToFile(database
->appDomain
, path
);
199 } else if (database
->dirty
) {
200 WMWritePropListToFile(database
->appDomain
, path
);
201 } else if (fileIsNewer
) {
202 plF
= WMReadPropListFromFile(path
);
204 WMReleasePropList(database
->appDomain
);
205 database
->appDomain
= plF
;
206 key
= database
->searchList
[0];
207 WMPutInPLDictionary(database
->defaults
, key
, plF
);
210 /* something happened with the file. just overwrite it */
211 wwarning(_("cannot read domain from file '%s' when syncing"), path
);
212 WMWritePropListToFile(database
->appDomain
, path
);
218 if (stat(path
, &stbuf
) >= 0)
219 database
->timestamp
= stbuf
.st_mtime
;
222 WMPostNotificationName(WMUserDefaultsDidChangeNotification
, database
, NULL
);
231 void WMSaveUserDefaults(WMUserDefaults
* database
)
233 if (database
->appDomain
) {
236 Bool release
= False
;
238 if (!database
->path
) {
239 path
= wdefaultspathfordomain(WMGetApplicationName());
242 path
= database
->path
;
244 WMWritePropListToFile(database
->appDomain
, path
);
246 if (stat(path
, &stbuf
) >= 0)
247 database
->timestamp
= stbuf
.st_mtime
;
253 WMUserDefaults
*WMGetStandardUserDefaults(void)
255 WMUserDefaults
*defaults
;
262 if (sharedUserDefaults
) {
263 defaults
= sharedUserDefaults
;
265 /* path == NULL only for StandardUserDefaults db */
266 if (defaults
->path
== NULL
)
268 defaults
= defaults
->next
;
272 /* we didn't found the database we are looking for. Go read it. XXX: wtf? */
273 defaults
= wmalloc(sizeof(WMUserDefaults
));
274 defaults
->defaults
= WMCreatePLDictionary(NULL
, NULL
);
275 defaults
->searchList
= wmalloc(sizeof(WMPropList
*) * 3);
277 /* application domain */
278 key
= WMCreatePLString(WMGetApplicationName());
279 defaults
->searchList
[0] = key
;
281 /* temporary kluge. wmaker handles synchronization itself */
282 if (strcmp(WMGetApplicationName(), "WindowMaker") == 0) {
283 defaults
->dontSync
= 1;
286 path
= wdefaultspathfordomain(WMGetFromPLString(key
));
288 if (stat(path
, &stbuf
) >= 0)
289 defaults
->timestamp
= stbuf
.st_mtime
;
291 domain
= WMReadPropListFromFile(path
);
294 domain
= WMCreatePLDictionary(NULL
, NULL
);
299 defaults
->appDomain
= domain
;
302 WMPutInPLDictionary(defaults
->defaults
, key
, domain
);
305 key
= WMCreatePLString("WMGLOBAL");
306 defaults
->searchList
[1] = key
;
308 path
= wdefaultspathfordomain(WMGetFromPLString(key
));
310 domain
= WMReadPropListFromFile(path
);
315 domain
= WMCreatePLDictionary(NULL
, NULL
);
318 WMPutInPLDictionary(defaults
->defaults
, key
, domain
);
321 defaults
->searchList
[2] = NULL
;
323 defaults
->searchListArray
= WMCreatePLArray(NULL
, NULL
);
326 while (defaults
->searchList
[i
]) {
327 WMAddToPLArray(defaults
->searchListArray
, defaults
->searchList
[i
]);
331 if (sharedUserDefaults
)
332 defaults
->next
= sharedUserDefaults
;
333 sharedUserDefaults
= defaults
;
336 addSynchronizeTimerHandler();
338 registerSaveOnExit();
343 WMUserDefaults
*WMGetDefaultsFromPath(char *path
)
345 WMUserDefaults
*defaults
;
352 assert(path
!= NULL
);
354 if (sharedUserDefaults
) {
355 defaults
= sharedUserDefaults
;
357 if (defaults
->path
&& strcmp(defaults
->path
, path
) == 0)
359 defaults
= defaults
->next
;
363 /* we didn't found the database we are looking for. Go read it. XXX wtf? */
364 defaults
= wmalloc(sizeof(WMUserDefaults
));
365 defaults
->defaults
= WMCreatePLDictionary(NULL
, NULL
);
366 defaults
->searchList
= wmalloc(sizeof(WMPropList
*) * 2);
368 /* the domain we want, go in the first position */
369 name
= strrchr(path
, '/');
375 key
= WMCreatePLString(name
);
376 defaults
->searchList
[0] = key
;
378 if (stat(path
, &stbuf
) >= 0)
379 defaults
->timestamp
= stbuf
.st_mtime
;
381 domain
= WMReadPropListFromFile(path
);
384 domain
= WMCreatePLDictionary(NULL
, NULL
);
386 defaults
->path
= wstrdup(path
);
388 defaults
->appDomain
= domain
;
391 WMPutInPLDictionary(defaults
->defaults
, key
, domain
);
394 defaults
->searchList
[1] = NULL
;
396 defaults
->searchListArray
= WMCreatePLArray(NULL
, NULL
);
399 while (defaults
->searchList
[i
]) {
400 WMAddToPLArray(defaults
->searchListArray
, defaults
->searchList
[i
]);
404 if (sharedUserDefaults
)
405 defaults
->next
= sharedUserDefaults
;
406 sharedUserDefaults
= defaults
;
409 addSynchronizeTimerHandler();
411 registerSaveOnExit();
416 /* Returns a WMPropList array with all keys in the user defaults database.
417 * Free the array with WMReleasePropList() when no longer needed,
418 * but do not free the elements of the array! They're just references. */
419 WMPropList
*WMGetUDKeys(WMUserDefaults
* database
)
421 return WMGetPLDictionaryKeys(database
->appDomain
);
424 WMPropList
*WMGetUDObjectForKey(WMUserDefaults
* database
, char *defaultName
)
426 WMPropList
*domainName
, *domain
;
427 WMPropList
*object
= NULL
;
428 WMPropList
*key
= WMCreatePLString(defaultName
);
431 while (database
->searchList
[i
] && !object
) {
432 domainName
= database
->searchList
[i
];
433 domain
= WMGetFromPLDictionary(database
->defaults
, domainName
);
435 object
= WMGetFromPLDictionary(domain
, key
);
439 WMReleasePropList(key
);
444 void WMSetUDObjectForKey(WMUserDefaults
* database
, WMPropList
* object
, char *defaultName
)
446 WMPropList
*key
= WMCreatePLString(defaultName
);
450 WMPutInPLDictionary(database
->appDomain
, key
, object
);
451 WMReleasePropList(key
);
454 void WMRemoveUDObjectForKey(WMUserDefaults
* database
, char *defaultName
)
456 WMPropList
*key
= WMCreatePLString(defaultName
);
460 WMRemoveFromPLDictionary(database
->appDomain
, key
);
462 WMReleasePropList(key
);
465 char *WMGetUDStringForKey(WMUserDefaults
* database
, char *defaultName
)
469 val
= WMGetUDObjectForKey(database
, defaultName
);
474 if (!WMIsPLString(val
))
477 return WMGetFromPLString(val
);
480 int WMGetUDIntegerForKey(WMUserDefaults
* database
, char *defaultName
)
486 val
= WMGetUDObjectForKey(database
, defaultName
);
491 if (!WMIsPLString(val
))
494 str
= WMGetFromPLString(val
);
498 if (sscanf(str
, "%i", &value
) != 1)
504 float WMGetUDFloatForKey(WMUserDefaults
* database
, char *defaultName
)
510 val
= WMGetUDObjectForKey(database
, defaultName
);
512 if (!val
|| !WMIsPLString(val
))
515 if (!(str
= WMGetFromPLString(val
)))
518 if (sscanf(str
, "%f", &value
) != 1)
524 Bool
WMGetUDBoolForKey(WMUserDefaults
* database
, char *defaultName
)
530 val
= WMGetUDObjectForKey(database
, defaultName
);
535 if (!WMIsPLString(val
))
538 str
= WMGetFromPLString(val
);
542 if (sscanf(str
, "%i", &value
) == 1 && value
!= 0)
545 if (strcasecmp(str
, "YES") == 0)
548 if (strcasecmp(str
, "Y") == 0)
554 void WMSetUDIntegerForKey(WMUserDefaults
* database
, int value
, char *defaultName
)
559 sprintf(buffer
, "%i", value
);
560 object
= WMCreatePLString(buffer
);
562 WMSetUDObjectForKey(database
, object
, defaultName
);
563 WMReleasePropList(object
);
566 void WMSetUDStringForKey(WMUserDefaults
* database
, char *value
, char *defaultName
)
570 object
= WMCreatePLString(value
);
572 WMSetUDObjectForKey(database
, object
, defaultName
);
573 WMReleasePropList(object
);
576 void WMSetUDFloatForKey(WMUserDefaults
* database
, float value
, char *defaultName
)
581 sprintf(buffer
, "%f", value
);
582 object
= WMCreatePLString(buffer
);
584 WMSetUDObjectForKey(database
, object
, defaultName
);
585 WMReleasePropList(object
);
588 void WMSetUDBoolForKey(WMUserDefaults
* database
, Bool value
, char *defaultName
)
590 static WMPropList
*yes
= NULL
, *no
= NULL
;
593 yes
= WMCreatePLString("YES");
594 no
= WMCreatePLString("NO");
597 WMSetUDObjectForKey(database
, value
? yes
: no
, defaultName
);
600 WMPropList
*WMGetUDSearchList(WMUserDefaults
* database
)
602 return database
->searchListArray
;
605 void WMSetUDSearchList(WMUserDefaults
* database
, WMPropList
* list
)
609 if (database
->searchList
) {
611 while (database
->searchList
[i
]) {
612 WMReleasePropList(database
->searchList
[i
]);
615 wfree(database
->searchList
);
617 if (database
->searchListArray
) {
618 WMReleasePropList(database
->searchListArray
);
621 c
= WMGetPropListItemCount(list
);
622 database
->searchList
= wmalloc(sizeof(WMPropList
*) * (c
+ 1));
624 for (i
= 0; i
< c
; i
++) {
625 database
->searchList
[i
] = WMGetFromPLArray(list
, i
);
627 database
->searchList
[c
] = NULL
;
629 database
->searchListArray
= WMDeepCopyPropList(list
);