#include #include #include #include #include #include #include "../src/config.h" #include "WINGs.h" #include typedef struct W_UserDefaults { proplist_t defaults; proplist_t appDomain; proplist_t searchListArray; proplist_t *searchList; /* cache for searchListArray */ char dirty; char dontSync; char *path; /* where is db located */ time_t timestamp; /* last modification time */ struct W_UserDefaults *next; } UserDefaults; static UserDefaults *sharedUserDefaults = NULL; char *WMUserDefaultsDidChangeNotification = "WMUserDefaultsDidChangeNotification"; extern char *WMGetApplicationName(); #define DEFAULTS_DIR "/Defaults" #define UD_SYNC_INTERVAL 2000 char* wusergnusteppath() { static char *path = NULL; char *gspath; int pathlen; if (!path) { gspath = getenv("GNUSTEP_USER_ROOT"); if (gspath) { gspath = wexpandpath(gspath); pathlen = strlen(gspath) + 4; path = wmalloc(pathlen); strcpy(path, gspath); wfree(gspath); } else { pathlen = strlen(wgethomedir()) + 10; path = wmalloc(pathlen); strcpy(path, wgethomedir()); strcat(path, "/GNUstep"); } } return path; } char* wdefaultspathfordomain(char *domain) { char *path; char *gspath; gspath = wusergnusteppath(); path = wmalloc(strlen(gspath)+strlen(DEFAULTS_DIR)+strlen(domain)+4); strcpy(path, gspath); strcat(path, DEFAULTS_DIR); strcat(path, "/"); strcat(path, domain); return path; } static void #ifndef HAVE_ATEXIT saveDefaultsChanges(int foo, void *bar) #else saveDefaultsChanges(void) #endif { /* save the user defaults databases */ UserDefaults *tmp = sharedUserDefaults; while (tmp) { WMSynchronizeUserDefaults(tmp); tmp = tmp->next; } } /* set to save changes in defaults when program is exited */ static void registerSaveOnExit(void) { static Bool registeredSaveOnExit = False; if (!registeredSaveOnExit) { #ifndef HAVE_ATEXIT on_exit(saveDefaultsChanges, (void*)NULL); #else atexit(saveDefaultsChanges); #endif registeredSaveOnExit = True; } } static void synchronizeUserDefaults(void *foo) { UserDefaults *database = sharedUserDefaults; while (database) { if (!database->dontSync) WMSynchronizeUserDefaults(database); database = database->next; } WMAddTimerHandler(UD_SYNC_INTERVAL, synchronizeUserDefaults, NULL); } static void addSynchronizeTimerHandler(void) { static Bool initialized = False; if (!initialized) { WMAddTimerHandler(UD_SYNC_INTERVAL, synchronizeUserDefaults, NULL); initialized = True; } } void WMEnableUDPeriodicSynchronization(WMUserDefaults *database, Bool enable) { database->dontSync = !enable; } void WMSynchronizeUserDefaults(WMUserDefaults *database) { Bool fileIsNewer = False, release = False; char *path; struct stat stbuf; if (!database->path) { path = wdefaultspathfordomain(WMGetApplicationName()); release = True; } else { path = database->path; } if (stat(path, &stbuf) >= 0 && stbuf.st_mtime > database->timestamp) fileIsNewer = True; if (database->appDomain && (database->dirty || fileIsNewer)) { /*fprintf(stderr, "syncing: %s %d %d\n", path, database->dirty, fileIsNewer);*/ PLShallowSynchronize(database->appDomain); database->dirty = 0; if (stat(path, &stbuf) >= 0) database->timestamp = stbuf.st_mtime; if (fileIsNewer) { WMPostNotificationName(WMUserDefaultsDidChangeNotification, database, NULL); } } if (release) wfree(path); } void WMSaveUserDefaults(WMUserDefaults *database) { if (database->appDomain) { struct stat stbuf; char *path; Bool release = False; PLSave(database->appDomain, YES); database->dirty = 0; if (!database->path) { path = wdefaultspathfordomain(WMGetApplicationName()); release = True; } else { path = database->path; } if (stat(path, &stbuf) >= 0) database->timestamp = stbuf.st_mtime; if (release) wfree(path); } } WMUserDefaults* WMGetStandardUserDefaults(void) { WMUserDefaults *defaults; proplist_t domain; proplist_t key; struct stat stbuf; char *path; int i; if (sharedUserDefaults) { defaults = sharedUserDefaults; while (defaults) { /* Trick, path == NULL only for StandardUserDefaults db */ if (defaults->path == NULL) return defaults; defaults = defaults->next; } } /* we didn't found the database we are looking for. Go read it. */ defaults = wmalloc(sizeof(WMUserDefaults)); memset(defaults, 0, sizeof(WMUserDefaults)); defaults->defaults = PLMakeDictionaryFromEntries(NULL, NULL, NULL); defaults->searchList = wmalloc(sizeof(proplist_t)*3); /* application domain */ key = PLMakeString(WMGetApplicationName()); defaults->searchList[0] = key; /* temporary kluge */ if (strcmp(WMGetApplicationName(), "WindowMaker")==0) { domain = NULL; path = NULL; } else { path = wdefaultspathfordomain(PLGetString(key)); if (stat(path, &stbuf) >= 0) defaults->timestamp = stbuf.st_mtime; domain = PLGetProplistWithPath(path); } if (!domain) { proplist_t p; domain = PLMakeDictionaryFromEntries(NULL, NULL, NULL); if (path) { p = PLMakeString(path); PLSetFilename(domain, p); PLRelease(p); } } if (path) wfree(path); defaults->appDomain = domain; if (domain) PLInsertDictionaryEntry(defaults->defaults, key, domain); PLRelease(key); /* global domain */ key = PLMakeString("WMGLOBAL"); defaults->searchList[1] = key; path = wdefaultspathfordomain(PLGetString(key)); domain = PLGetProplistWithPath(path); wfree(path); if (!domain) domain = PLMakeDictionaryFromEntries(NULL, NULL, NULL); if (domain) PLInsertDictionaryEntry(defaults->defaults, key, domain); PLRelease(key); /* terminate list */ defaults->searchList[2] = NULL; defaults->searchListArray = PLMakeArrayFromElements(NULL,NULL); i = 0; while (defaults->searchList[i]) { PLAppendArrayElement(defaults->searchListArray, defaults->searchList[i]); i++; } if (sharedUserDefaults) defaults->next = sharedUserDefaults; sharedUserDefaults = defaults; addSynchronizeTimerHandler(); registerSaveOnExit(); return defaults; } WMUserDefaults* WMGetDefaultsFromPath(char *path) { WMUserDefaults *defaults; proplist_t domain; proplist_t key; struct stat stbuf; char *name; int i; assert(path != NULL); if (sharedUserDefaults) { defaults = sharedUserDefaults; while (defaults) { if (defaults->path && strcmp(defaults->path, path) == 0) return defaults; defaults = defaults->next; } } /* we didn't found the database we are looking for. Go read it. */ defaults = wmalloc(sizeof(WMUserDefaults)); memset(defaults, 0, sizeof(WMUserDefaults)); defaults->defaults = PLMakeDictionaryFromEntries(NULL, NULL, NULL); defaults->searchList = wmalloc(sizeof(proplist_t)*2); /* the domain we want, go in the first position */ name = strrchr(path, '/'); if (!name) name = path; else name++; key = PLMakeString(name); defaults->searchList[0] = key; if (stat(path, &stbuf) >= 0) defaults->timestamp = stbuf.st_mtime; domain = PLGetProplistWithPath(path); if (!domain) { proplist_t p; domain = PLMakeDictionaryFromEntries(NULL, NULL, NULL); p = PLMakeString(path); PLSetFilename(domain, p); PLRelease(p); } defaults->path = wstrdup(path); defaults->appDomain = domain; if (domain) PLInsertDictionaryEntry(defaults->defaults, key, domain); PLRelease(key); /* terminate list */ defaults->searchList[1] = NULL; defaults->searchListArray = PLMakeArrayFromElements(NULL,NULL); i = 0; while (defaults->searchList[i]) { PLAppendArrayElement(defaults->searchListArray, defaults->searchList[i]); i++; } if (sharedUserDefaults) defaults->next = sharedUserDefaults; sharedUserDefaults = defaults; addSynchronizeTimerHandler(); registerSaveOnExit(); return defaults; } /* Returns a PLArray with all keys in the user defaults database. * Free the returned array with PLRelease() when no longer needed, * but do not free the elements of the array! They're just references. */ proplist_t WMGetUDAllKeys(WMUserDefaults *database) { return PLGetAllDictionaryKeys(database->appDomain); } proplist_t WMGetUDObjectForKey(WMUserDefaults *database, char *defaultName) { proplist_t domainName, domain; proplist_t object = NULL; proplist_t key = PLMakeString(defaultName); int i = 0; while (database->searchList[i] && !object) { domainName = database->searchList[i]; domain = PLGetDictionaryEntry(database->defaults, domainName); if (domain) { object = PLGetDictionaryEntry(domain, key); } i++; } PLRelease(key); return object; } void WMSetUDObjectForKey(WMUserDefaults *database, proplist_t object, char *defaultName) { proplist_t key = PLMakeString(defaultName); database->dirty = 1; PLInsertDictionaryEntry(database->appDomain, key, object); PLRelease(key); } void WMRemoveUDObjectForKey(WMUserDefaults *database, char *defaultName) { proplist_t key = PLMakeString(defaultName); database->dirty = 1; PLRemoveDictionaryEntry(database->appDomain, key); PLRelease(key); } char* WMGetUDStringForKey(WMUserDefaults *database, char *defaultName) { proplist_t val; val = WMGetUDObjectForKey(database, defaultName); if (!val) return NULL; if (!PLIsString(val)) return NULL; return PLGetString(val); } int WMGetUDIntegerForKey(WMUserDefaults *database, char *defaultName) { proplist_t val; char *str; int value; val = WMGetUDObjectForKey(database, defaultName); if (!val) return 0; if (!PLIsString(val)) return 0; str = PLGetString(val); if (!str) return 0; if (sscanf(str, "%i", &value)!=1) return 0; return value; } float WMGetUDFloatForKey(WMUserDefaults *database, char *defaultName) { proplist_t val; char *str; float value; val = WMGetUDObjectForKey(database, defaultName); if (!val || !PLIsString(val)) return 0.0; if (!(str = PLGetString(val))) return 0.0; if (sscanf(str, "%f", &value)!=1) return 0.0; return value; } Bool WMGetUDBoolForKey(WMUserDefaults *database, char *defaultName) { proplist_t val; int value; char *str; val = WMGetUDObjectForKey(database, defaultName); if (!val) return False; if (!PLIsString(val)) return False; str = PLGetString(val); if (!str) return False; if (sscanf(str, "%i", &value)==1 && value!=0) return True; if (strcasecmp(str, "YES")==0) return True; if (strcasecmp(str, "Y")==0) return True; return False; } void WMSetUDIntegerForKey(WMUserDefaults *database, int value, char *defaultName) { proplist_t object; char buffer[128]; sprintf(buffer, "%i", value); object = PLMakeString(buffer); WMSetUDObjectForKey(database, object, defaultName); PLRelease(object); } void WMSetUDStringForKey(WMUserDefaults *database, char *value, char *defaultName) { proplist_t object; object = PLMakeString(value); WMSetUDObjectForKey(database, object, defaultName); PLRelease(object); } void WMSetUDFloatForKey(WMUserDefaults *database, float value, char *defaultName) { proplist_t object; char buffer[128]; sprintf(buffer, "%f", value); object = PLMakeString(buffer); WMSetUDObjectForKey(database, object, defaultName); PLRelease(object); } void WMSetUDBoolForKey(WMUserDefaults *database, Bool value, char *defaultName) { static proplist_t yes = NULL, no = NULL; if (!yes) { yes = PLMakeString("YES"); no = PLMakeString("NO"); } WMSetUDObjectForKey(database, value ? yes : no, defaultName); } proplist_t WMGetUDSearchList(WMUserDefaults *database) { return database->searchListArray; } void WMSetUDSearchList(WMUserDefaults *database, proplist_t list) { int i, c; if (database->searchList) { i = 0; while (database->searchList[i]) { PLRelease(database->searchList[i]); i++; } wfree(database->searchList); } if (database->searchListArray) { PLRelease(database->searchListArray); } c = PLGetNumberOfElements(list); database->searchList = wmalloc(sizeof(proplist_t)*(c+1)); for (i=0; isearchList[i] = PLGetArrayElement(list, i); } database->searchListArray = PLDeepCopy(list); }