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
);
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 const char *wusergnusteppath(void)
51 static const char subdir
[] = "/" GSUSER_SUBDIR
;
52 static char *path
= NULL
;
58 /* Value have been already computed, re-use it */
61 gspath
= GETENV("WMAKER_USER_ROOT");
63 gspath
= wexpandpath(gspath
);
68 wwarning(_("variable WMAKER_USER_ROOT defined with invalid path, not used"));
76 path
= wmalloc(pathlen
+ sizeof(subdir
));
78 strcpy(path
+ pathlen
, subdir
);
83 const char *wuserdatapath(void)
85 static char *path
= NULL
;
88 /* Value have been already computed, re-use it */
91 path
= wstrconcat(wusergnusteppath(), "/" USERDATA_SUBDIR
);
96 char *wdefaultspathfordomain(const char *domain
)
102 gspath
= wusergnusteppath();
103 slen
= strlen(gspath
) + strlen("/" DEFAULTS_SUBDIR
"/") + strlen(domain
) + 1;
104 path
= wmalloc(slen
);
106 strcpy(path
, gspath
);
107 strcat(path
, "/" DEFAULTS_SUBDIR
"/");
108 strcat(path
, domain
);
113 /* XXX: doesn't quite belong to *user*defaults.c */
114 char *wglobaldefaultspathfordomain(const char *domain
)
116 return wstrconcat(PKGCONFDIR
"/", domain
);
119 void w_save_defaults_changes(void)
121 if (WMApplication
.applicationName
== NULL
) {
123 * This means that the user has properly exited by calling the
124 * function 'WMReleaseApplication' (which has already called us)
125 * but we're being called again by the fallback 'atexit' method
126 * (the legacy way of saving changes on exit which is kept for
127 * application that would forget to call 'WMReleaseApplication')
132 /* save the user defaults databases */
133 synchronizeUserDefaults(NULL
);
136 /* set to save changes in defaults when program is exited */
137 static void registerSaveOnExit(void)
139 static Bool registeredSaveOnExit
= False
;
141 if (!registeredSaveOnExit
) {
142 atexit(w_save_defaults_changes
);
143 registeredSaveOnExit
= True
;
147 static void synchronizeUserDefaults(void *foo
)
149 UserDefaults
*database
= sharedUserDefaults
;
151 /* Parameter not used, but tell the compiler that it is ok */
155 if (!database
->dontSync
)
156 WMSynchronizeUserDefaults(database
);
157 database
= database
->next
;
162 static void addSynchronizeTimerHandler(void)
164 static Bool initialized
= False
;
167 WMAddPersistentTimerHandler(UD_SYNC_INTERVAL
,
168 synchronizeUserDefaults
, NULL
);
174 void WMEnableUDPeriodicSynchronization(WMUserDefaults
* database
, Bool enable
)
176 database
->dontSync
= !enable
;
179 void WMSynchronizeUserDefaults(WMUserDefaults
* database
)
181 Bool fileIsNewer
= False
, release
= False
, notify
= False
;
182 WMPropList
*plF
, *key
;
186 if (!database
->path
) {
187 path
= wdefaultspathfordomain(WMGetApplicationName());
190 path
= database
->path
;
193 if (stat(path
, &stbuf
) >= 0 && stbuf
.st_mtime
> database
->timestamp
)
196 if (database
->appDomain
&& (database
->dirty
|| fileIsNewer
)) {
197 if (database
->dirty
&& fileIsNewer
) {
198 plF
= WMReadPropListFromFile(path
);
200 plF
= WMMergePLDictionaries(plF
, database
->appDomain
, False
);
201 WMReleasePropList(database
->appDomain
);
202 database
->appDomain
= plF
;
203 key
= database
->searchList
[0];
204 WMPutInPLDictionary(database
->defaults
, key
, plF
);
207 /* something happened with the file. just overwrite it */
208 wwarning(_("cannot read domain from file '%s' when syncing"), path
);
209 WMWritePropListToFile(database
->appDomain
, path
);
211 } else if (database
->dirty
) {
212 WMWritePropListToFile(database
->appDomain
, path
);
213 } else if (fileIsNewer
) {
214 plF
= WMReadPropListFromFile(path
);
216 WMReleasePropList(database
->appDomain
);
217 database
->appDomain
= plF
;
218 key
= database
->searchList
[0];
219 WMPutInPLDictionary(database
->defaults
, key
, plF
);
222 /* something happened with the file. just overwrite it */
223 wwarning(_("cannot read domain from file '%s' when syncing"), path
);
224 WMWritePropListToFile(database
->appDomain
, path
);
230 if (stat(path
, &stbuf
) >= 0)
231 database
->timestamp
= stbuf
.st_mtime
;
234 WMPostNotificationName(WMUserDefaultsDidChangeNotification
, database
, NULL
);
243 void WMSaveUserDefaults(WMUserDefaults
* database
)
245 if (database
->appDomain
) {
248 Bool release
= False
;
250 if (!database
->path
) {
251 path
= wdefaultspathfordomain(WMGetApplicationName());
254 path
= database
->path
;
256 WMWritePropListToFile(database
->appDomain
, path
);
258 if (stat(path
, &stbuf
) >= 0)
259 database
->timestamp
= stbuf
.st_mtime
;
265 WMUserDefaults
*WMGetStandardUserDefaults(void)
267 WMUserDefaults
*defaults
;
274 if (sharedUserDefaults
) {
275 defaults
= sharedUserDefaults
;
277 /* path == NULL only for StandardUserDefaults db */
278 if (defaults
->path
== NULL
)
280 defaults
= defaults
->next
;
284 /* we didn't found the database we are looking for. Go read it. XXX: wtf? */
285 defaults
= wmalloc(sizeof(WMUserDefaults
));
286 defaults
->defaults
= WMCreatePLDictionary(NULL
, NULL
);
287 defaults
->searchList
= wmalloc(sizeof(WMPropList
*) * 3);
289 /* application domain */
290 key
= WMCreatePLString(WMGetApplicationName());
291 defaults
->searchList
[0] = key
;
293 /* temporary kluge. wmaker handles synchronization itself */
294 if (strcmp(WMGetApplicationName(), "WindowMaker") == 0) {
295 defaults
->dontSync
= 1;
298 path
= wdefaultspathfordomain(WMGetFromPLString(key
));
300 if (stat(path
, &stbuf
) >= 0)
301 defaults
->timestamp
= stbuf
.st_mtime
;
303 domain
= WMReadPropListFromFile(path
);
306 domain
= WMCreatePLDictionary(NULL
, NULL
);
310 defaults
->appDomain
= domain
;
313 WMPutInPLDictionary(defaults
->defaults
, key
, domain
);
316 key
= WMCreatePLString("WMGLOBAL");
317 defaults
->searchList
[1] = key
;
319 path
= wdefaultspathfordomain(WMGetFromPLString(key
));
321 domain
= WMReadPropListFromFile(path
);
326 domain
= WMCreatePLDictionary(NULL
, NULL
);
329 WMPutInPLDictionary(defaults
->defaults
, key
, domain
);
332 defaults
->searchList
[2] = NULL
;
334 defaults
->searchListArray
= WMCreatePLArray(NULL
, NULL
);
337 while (defaults
->searchList
[i
]) {
338 WMAddToPLArray(defaults
->searchListArray
, defaults
->searchList
[i
]);
342 if (sharedUserDefaults
)
343 defaults
->next
= sharedUserDefaults
;
344 sharedUserDefaults
= defaults
;
347 addSynchronizeTimerHandler();
349 registerSaveOnExit();
354 WMUserDefaults
*WMGetDefaultsFromPath(const char *path
)
356 WMUserDefaults
*defaults
;
363 assert(path
!= NULL
);
365 if (sharedUserDefaults
) {
366 defaults
= sharedUserDefaults
;
368 if (defaults
->path
&& strcmp(defaults
->path
, path
) == 0)
370 defaults
= defaults
->next
;
374 /* we didn't found the database we are looking for. Go read it. XXX wtf? */
375 defaults
= wmalloc(sizeof(WMUserDefaults
));
376 defaults
->defaults
= WMCreatePLDictionary(NULL
, NULL
);
377 defaults
->searchList
= wmalloc(sizeof(WMPropList
*) * 2);
379 /* the domain we want, go in the first position */
380 name
= strrchr(path
, '/');
386 key
= WMCreatePLString(name
);
387 defaults
->searchList
[0] = key
;
389 if (stat(path
, &stbuf
) >= 0)
390 defaults
->timestamp
= stbuf
.st_mtime
;
392 domain
= WMReadPropListFromFile(path
);
395 domain
= WMCreatePLDictionary(NULL
, NULL
);
397 defaults
->path
= wstrdup(path
);
399 defaults
->appDomain
= domain
;
402 WMPutInPLDictionary(defaults
->defaults
, key
, domain
);
405 defaults
->searchList
[1] = NULL
;
407 defaults
->searchListArray
= WMCreatePLArray(NULL
, NULL
);
410 while (defaults
->searchList
[i
]) {
411 WMAddToPLArray(defaults
->searchListArray
, defaults
->searchList
[i
]);
415 if (sharedUserDefaults
)
416 defaults
->next
= sharedUserDefaults
;
417 sharedUserDefaults
= defaults
;
420 addSynchronizeTimerHandler();
422 registerSaveOnExit();
427 /* Returns a WMPropList array with all keys in the user defaults database.
428 * Free the array with WMReleasePropList() when no longer needed,
429 * but do not free the elements of the array! They're just references. */
430 WMPropList
*WMGetUDKeys(WMUserDefaults
* database
)
432 return WMGetPLDictionaryKeys(database
->appDomain
);
435 WMPropList
*WMGetUDObjectForKey(WMUserDefaults
* database
, const char *defaultName
)
437 WMPropList
*domainName
, *domain
;
438 WMPropList
*object
= NULL
;
439 WMPropList
*key
= WMCreatePLString(defaultName
);
442 while (database
->searchList
[i
] && !object
) {
443 domainName
= database
->searchList
[i
];
444 domain
= WMGetFromPLDictionary(database
->defaults
, domainName
);
446 object
= WMGetFromPLDictionary(domain
, key
);
450 WMReleasePropList(key
);
455 void WMSetUDObjectForKey(WMUserDefaults
* database
, WMPropList
* object
, const char *defaultName
)
457 WMPropList
*key
= WMCreatePLString(defaultName
);
461 WMPutInPLDictionary(database
->appDomain
, key
, object
);
462 WMReleasePropList(key
);
465 void WMRemoveUDObjectForKey(WMUserDefaults
* database
, const char *defaultName
)
467 WMPropList
*key
= WMCreatePLString(defaultName
);
471 WMRemoveFromPLDictionary(database
->appDomain
, key
);
473 WMReleasePropList(key
);
476 char *WMGetUDStringForKey(WMUserDefaults
* database
, const char *defaultName
)
480 val
= WMGetUDObjectForKey(database
, defaultName
);
485 if (!WMIsPLString(val
))
488 return WMGetFromPLString(val
);
491 int WMGetUDIntegerForKey(WMUserDefaults
* database
, const char *defaultName
)
497 val
= WMGetUDObjectForKey(database
, defaultName
);
502 if (!WMIsPLString(val
))
505 str
= WMGetFromPLString(val
);
509 if (sscanf(str
, "%i", &value
) != 1)
515 float WMGetUDFloatForKey(WMUserDefaults
* database
, const char *defaultName
)
521 val
= WMGetUDObjectForKey(database
, defaultName
);
523 if (!val
|| !WMIsPLString(val
))
526 if (!(str
= WMGetFromPLString(val
)))
529 if (sscanf(str
, "%f", &value
) != 1)
535 Bool
WMGetUDBoolForKey(WMUserDefaults
* database
, const char *defaultName
)
541 val
= WMGetUDObjectForKey(database
, defaultName
);
546 if (!WMIsPLString(val
))
549 str
= WMGetFromPLString(val
);
553 if (sscanf(str
, "%i", &value
) == 1 && value
!= 0)
556 if (strcasecmp(str
, "YES") == 0)
559 if (strcasecmp(str
, "Y") == 0)
565 void WMSetUDIntegerForKey(WMUserDefaults
* database
, int value
, const char *defaultName
)
570 sprintf(buffer
, "%i", value
);
571 object
= WMCreatePLString(buffer
);
573 WMSetUDObjectForKey(database
, object
, defaultName
);
574 WMReleasePropList(object
);
577 void WMSetUDStringForKey(WMUserDefaults
* database
, const char *value
, const char *defaultName
)
581 object
= WMCreatePLString(value
);
583 WMSetUDObjectForKey(database
, object
, defaultName
);
584 WMReleasePropList(object
);
587 void WMSetUDFloatForKey(WMUserDefaults
* database
, float value
, const char *defaultName
)
592 sprintf(buffer
, "%f", (double)value
);
593 object
= WMCreatePLString(buffer
);
595 WMSetUDObjectForKey(database
, object
, defaultName
);
596 WMReleasePropList(object
);
599 void WMSetUDBoolForKey(WMUserDefaults
* database
, Bool value
, const char *defaultName
)
601 static WMPropList
*yes
= NULL
, *no
= NULL
;
604 yes
= WMCreatePLString("YES");
605 no
= WMCreatePLString("NO");
608 WMSetUDObjectForKey(database
, value
? yes
: no
, defaultName
);
611 WMPropList
*WMGetUDSearchList(WMUserDefaults
* database
)
613 return database
->searchListArray
;
616 void WMSetUDSearchList(WMUserDefaults
* database
, WMPropList
* list
)
620 if (database
->searchList
) {
622 while (database
->searchList
[i
]) {
623 WMReleasePropList(database
->searchList
[i
]);
626 wfree(database
->searchList
);
628 if (database
->searchListArray
) {
629 WMReleasePropList(database
->searchListArray
);
632 c
= WMGetPropListItemCount(list
);
633 database
->searchList
= wmalloc(sizeof(WMPropList
*) * (c
+ 1));
635 for (i
= 0; i
< c
; i
++) {
636 database
->searchList
[i
] = WMGetFromPLArray(list
, i
);
638 database
->searchList
[c
] = NULL
;
640 database
->searchListArray
= WMDeepCopyPropList(list
);