mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-26 02:01:03 +00:00
User defaults language/setup restructuring
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@32454 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
aa9300f786
commit
f169caa814
6 changed files with 319 additions and 335 deletions
17
ChangeLog
17
ChangeLog
|
@ -1,3 +1,20 @@
|
||||||
|
2011-03-05 Richard Frith-Macdonald <rfm@gnu.org>
|
||||||
|
|
||||||
|
* Source/NSNumberFormatter.m: Make error text compatible with OSX
|
||||||
|
* Source/NSSpellServer.m: Use NSLanguages user default
|
||||||
|
* Source/NSBundle.m: Use NSLanguages user default
|
||||||
|
* Source/NSUserDefaults.m:
|
||||||
|
Revert recent attempt to avoid deadlocks (broke thread-safety).
|
||||||
|
Rewrite shared defaults initialisation and user language management
|
||||||
|
to break the complicated interrelationshp between methods which
|
||||||
|
made the initialisation fragile and incomprehensible.
|
||||||
|
Language information derived from operating system or environment
|
||||||
|
variables (rather than the defaults database) is now stored in a
|
||||||
|
new volatile domain.
|
||||||
|
The +userLanguages method just returns the NSLanguages default,
|
||||||
|
and +setUserLanguages: now sets NSLanguages in the new volatile domain.
|
||||||
|
Simplify setup of shared user defaults to avoid deadlocks.
|
||||||
|
|
||||||
2011-03-04 Richard Frith-Macdonald <rfm@gnu.org>
|
2011-03-04 Richard Frith-Macdonald <rfm@gnu.org>
|
||||||
|
|
||||||
* Source/NSOperation.m: retain operation while finishing so that it
|
* Source/NSOperation.m: retain operation while finishing so that it
|
||||||
|
|
|
@ -1685,7 +1685,8 @@ IF_NO_GC(
|
||||||
NSEnumerator *enumerate;
|
NSEnumerator *enumerate;
|
||||||
|
|
||||||
array = [NSMutableArray arrayWithCapacity: 8];
|
array = [NSMutableArray arrayWithCapacity: 8];
|
||||||
languages = [NSUserDefaults userLanguages];
|
languages = [[NSUserDefaults standardUserDefaults]
|
||||||
|
stringArrayForKey: @"NSLanguages"];
|
||||||
|
|
||||||
primary = [rootPath stringByAppendingPathComponent: @"Resources"];
|
primary = [rootPath stringByAppendingPathComponent: @"Resources"];
|
||||||
[array addObject: _bundle_resource_path(primary, subPath, nil)];
|
[array addObject: _bundle_resource_path(primary, subPath, nil)];
|
||||||
|
@ -1945,7 +1946,8 @@ IF_NO_GC(
|
||||||
+ (NSArray *) preferredLocalizationsFromArray: (NSArray *)localizationsArray
|
+ (NSArray *) preferredLocalizationsFromArray: (NSArray *)localizationsArray
|
||||||
{
|
{
|
||||||
return [self preferredLocalizationsFromArray: localizationsArray
|
return [self preferredLocalizationsFromArray: localizationsArray
|
||||||
forPreferences: [NSUserDefaults userLanguages]];
|
forPreferences: [[NSUserDefaults standardUserDefaults]
|
||||||
|
stringArrayForKey: @"NSLanguages"]];
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (NSArray *) preferredLocalizationsFromArray: (NSArray *)localizationsArray
|
+ (NSArray *) preferredLocalizationsFromArray: (NSArray *)localizationsArray
|
||||||
|
|
|
@ -543,8 +543,10 @@ static NSRecursiveLock *classLock = nil;
|
||||||
NSMutableArray *mArray;
|
NSMutableArray *mArray;
|
||||||
NSUInteger cnt;
|
NSUInteger cnt;
|
||||||
NSUInteger idx = 0;
|
NSUInteger idx = 0;
|
||||||
NSArray *languages = [NSUserDefaults userLanguages];
|
NSArray *languages;
|
||||||
|
|
||||||
|
languages = [[NSUserDefaults standardUserDefaults]
|
||||||
|
stringArrayForKey: @"NSLanguages"];
|
||||||
if (languages == nil)
|
if (languages == nil)
|
||||||
return [NSArray arrayWithObject: @"en"];
|
return [NSArray arrayWithObject: @"en"];
|
||||||
|
|
||||||
|
|
|
@ -419,7 +419,7 @@ static NSUInteger _defaultBehavior = NSNumberFormatterBehavior10_4;
|
||||||
{
|
{
|
||||||
if (0 != error)
|
if (0 != error)
|
||||||
{
|
{
|
||||||
*error = _(@"floating point values not allowed");
|
*error = _(@"Floating Point not allowed");
|
||||||
}
|
}
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,9 +81,12 @@ GSSpellServerName(NSString *vendor, NSString *language)
|
||||||
// Non-private Instance methods
|
// Non-private Instance methods
|
||||||
- (id) init
|
- (id) init
|
||||||
{
|
{
|
||||||
NSArray *userLanguages = [NSUserDefaults userLanguages];
|
NSArray *userPreference;
|
||||||
NSString *currentLanguage = [userLanguages objectAtIndex: 0];
|
NSString *currentLanguage;
|
||||||
|
|
||||||
|
userPreference = [[NSUserDefaults standardUserDefaults]
|
||||||
|
stringArrayForKey: @"NSLanguages"];
|
||||||
|
currentLanguage = [userPreference objectAtIndex: 0];
|
||||||
if ((self = [super init]) != nil)
|
if ((self = [super init]) != nil)
|
||||||
{
|
{
|
||||||
_delegate = nil;
|
_delegate = nil;
|
||||||
|
|
|
@ -86,14 +86,19 @@ static Class NSNumberClass;
|
||||||
static Class NSMutableDictionaryClass;
|
static Class NSMutableDictionaryClass;
|
||||||
static Class NSStringClass;
|
static Class NSStringClass;
|
||||||
|
|
||||||
|
static NSString *GSPrimaryDomain = @"GSPrimaryDomain";
|
||||||
static NSString *defaultsFile = @".GNUstepDefaults";
|
static NSString *defaultsFile = @".GNUstepDefaults";
|
||||||
|
|
||||||
static NSUserDefaults *sharedDefaults = nil;
|
static NSUserDefaults *sharedDefaults = nil;
|
||||||
static NSMutableString *processName = nil;
|
static NSMutableString *processName = nil;
|
||||||
static NSMutableArray *userLanguages = nil;
|
|
||||||
static BOOL invalidatedLanguages = NO;
|
|
||||||
static NSRecursiveLock *classLock = nil;
|
static NSRecursiveLock *classLock = nil;
|
||||||
|
|
||||||
|
/* Flag to say whether the sharedDefaults variable has been set up by a
|
||||||
|
* call to the +standardUserDefaults method. If this is YES but the variable
|
||||||
|
* is nil then there was a problem initialising the shared object and we
|
||||||
|
* have no defaults available.
|
||||||
|
*/
|
||||||
|
static BOOL hasSharedDefaults = NO;
|
||||||
/*
|
/*
|
||||||
* Caching some defaults.
|
* Caching some defaults.
|
||||||
*/
|
*/
|
||||||
|
@ -173,6 +178,76 @@ writeDictionary(NSDictionary *dict, NSString *file)
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static NSMutableArray *
|
||||||
|
newLanguages(NSArray *oldNames)
|
||||||
|
{
|
||||||
|
NSMutableArray *newNames;
|
||||||
|
NSEnumerator *enumerator;
|
||||||
|
NSString *language;
|
||||||
|
NSString *locale = nil;
|
||||||
|
|
||||||
|
#ifdef HAVE_LOCALE_H
|
||||||
|
#ifdef LC_MESSAGES
|
||||||
|
locale = GSSetLocale(LC_MESSAGES, nil);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
newNames = [NSMutableArray arrayWithCapacity: 5];
|
||||||
|
|
||||||
|
if (oldNames == nil && locale != nil)
|
||||||
|
{
|
||||||
|
NSString *locLang = GSLanguageFromLocale(locale);
|
||||||
|
|
||||||
|
if (nil != locLang)
|
||||||
|
{
|
||||||
|
oldNames = [NSArray arrayWithObject: locLang];
|
||||||
|
}
|
||||||
|
#ifdef __MINGW__
|
||||||
|
if (oldNames == nil)
|
||||||
|
{
|
||||||
|
/* Check for language as the first part of the locale string */
|
||||||
|
NSRange under = [locale rangeOfString: @"_"];
|
||||||
|
|
||||||
|
if (under.location)
|
||||||
|
{
|
||||||
|
oldNames = [NSArray arrayWithObject:
|
||||||
|
[locale substringToIndex: under.location]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
if (oldNames == nil)
|
||||||
|
{
|
||||||
|
NSString *env;
|
||||||
|
|
||||||
|
env = [[[NSProcessInfo processInfo] environment]
|
||||||
|
objectForKey: @"LANGUAGES"];
|
||||||
|
if (env != nil)
|
||||||
|
{
|
||||||
|
oldNames = [env componentsSeparatedByString: @";"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enumerator = [oldNames objectEnumerator];
|
||||||
|
while (nil != (language = [enumerator nextObject]))
|
||||||
|
{
|
||||||
|
language = [language stringByTrimmingSpaces];
|
||||||
|
if ([language length] > 0 && NO == [newNames containsObject: language])
|
||||||
|
{
|
||||||
|
[newNames addObject: language];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if "English" is included. We do this to make sure all the
|
||||||
|
* required language constants are set somewhere if they aren't set
|
||||||
|
* in the default language.
|
||||||
|
*/
|
||||||
|
if (NO == [newNames containsObject: @"English"])
|
||||||
|
{
|
||||||
|
[newNames addObject: @"English"];
|
||||||
|
}
|
||||||
|
return newNames;
|
||||||
|
}
|
||||||
|
|
||||||
/*************************************************************************
|
/*************************************************************************
|
||||||
*** Local method definitions
|
*** Local method definitions
|
||||||
*************************************************************************/
|
*************************************************************************/
|
||||||
|
@ -207,6 +282,12 @@ writeDictionary(NSDictionary *dict, NSString *file)
|
||||||
* looks through the various domains in a particular order.
|
* looks through the various domains in a particular order.
|
||||||
* </p>
|
* </p>
|
||||||
* <deflist>
|
* <deflist>
|
||||||
|
* <term><code>GSPrimaryDomain</code> ... volatile</term>
|
||||||
|
* <desc>
|
||||||
|
* Contains values set at runtime and intended to supercede any values
|
||||||
|
* set in other domains. This should be used with great care since it
|
||||||
|
* overrides values which may have been set explicitly by the user.
|
||||||
|
* </desc>
|
||||||
* <term><code>NSArgumentDomain</code> ... volatile</term>
|
* <term><code>NSArgumentDomain</code> ... volatile</term>
|
||||||
* <desc>
|
* <desc>
|
||||||
* Contains defaults read from the arguments provided
|
* Contains defaults read from the arguments provided
|
||||||
|
@ -220,8 +301,10 @@ writeDictionary(NSDictionary *dict, NSString *file)
|
||||||
* </desc>
|
* </desc>
|
||||||
* <term>Application (name of the current process) ... persistent</term>
|
* <term>Application (name of the current process) ... persistent</term>
|
||||||
* <desc>
|
* <desc>
|
||||||
* Contains application specific defaults,
|
* Contains application specific defaults, such as window positions.
|
||||||
* such as window positions.</desc>
|
* This is the domain used by the -setObject:forKey: method and is
|
||||||
|
* the domain normally used when setting preferences for an application.
|
||||||
|
* </desc>
|
||||||
* <term><code>NSGlobalDomain</code> ... persistent</term>
|
* <term><code>NSGlobalDomain</code> ... persistent</term>
|
||||||
* <desc>
|
* <desc>
|
||||||
* Global defaults applicable to all applications.
|
* Global defaults applicable to all applications.
|
||||||
|
@ -280,8 +363,6 @@ writeDictionary(NSDictionary *dict, NSString *file)
|
||||||
*/
|
*/
|
||||||
@implementation NSUserDefaults: NSObject
|
@implementation NSUserDefaults: NSObject
|
||||||
|
|
||||||
static BOOL setSharedDefaults = NO; /* Flag to prevent infinite recursion */
|
|
||||||
|
|
||||||
+ (void) initialize
|
+ (void) initialize
|
||||||
{
|
{
|
||||||
if (self == [NSUserDefaults class])
|
if (self == [NSUserDefaults class])
|
||||||
|
@ -305,12 +386,14 @@ static BOOL setSharedDefaults = NO; /* Flag to prevent infinite recursion */
|
||||||
|
|
||||||
+ (void) resetStandardUserDefaults
|
+ (void) resetStandardUserDefaults
|
||||||
{
|
{
|
||||||
|
NSDictionary *regDefs;
|
||||||
|
|
||||||
[classLock lock];
|
[classLock lock];
|
||||||
NS_DURING
|
NS_DURING
|
||||||
{
|
{
|
||||||
if (sharedDefaults != nil)
|
regDefs = [sharedDefaults volatileDomainForName: @"NSRegistrationDomain"];
|
||||||
|
if (nil != sharedDefaults)
|
||||||
{
|
{
|
||||||
NSDictionary *regDefs;
|
|
||||||
|
|
||||||
/* To ensure that we don't try to synchronise the old defaults to disk
|
/* To ensure that we don't try to synchronise the old defaults to disk
|
||||||
* after creating the new ones, remove as housekeeping notification
|
* after creating the new ones, remove as housekeeping notification
|
||||||
|
@ -323,22 +406,9 @@ static BOOL setSharedDefaults = NO; /* Flag to prevent infinite recursion */
|
||||||
*/
|
*/
|
||||||
[sharedDefaults synchronize];
|
[sharedDefaults synchronize];
|
||||||
DESTROY(sharedDefaults->_changedDomains);
|
DESTROY(sharedDefaults->_changedDomains);
|
||||||
|
|
||||||
regDefs = RETAIN([sharedDefaults->_tempDomains
|
|
||||||
objectForKey: NSRegistrationDomain]);
|
|
||||||
setSharedDefaults = NO;
|
|
||||||
DESTROY(sharedDefaults);
|
DESTROY(sharedDefaults);
|
||||||
if (regDefs != nil)
|
|
||||||
{
|
|
||||||
[self standardUserDefaults];
|
|
||||||
if (sharedDefaults != nil)
|
|
||||||
{
|
|
||||||
[sharedDefaults->_tempDomains setObject: regDefs
|
|
||||||
forKey: NSRegistrationDomain];
|
|
||||||
}
|
|
||||||
RELEASE(regDefs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
hasSharedDefaults = NO;
|
||||||
[classLock unlock];
|
[classLock unlock];
|
||||||
}
|
}
|
||||||
NS_HANDLER
|
NS_HANDLER
|
||||||
|
@ -347,6 +417,15 @@ static BOOL setSharedDefaults = NO; /* Flag to prevent infinite recursion */
|
||||||
[localException raise];
|
[localException raise];
|
||||||
}
|
}
|
||||||
NS_ENDHANDLER
|
NS_ENDHANDLER
|
||||||
|
if (nil != regDefs)
|
||||||
|
{
|
||||||
|
[self standardUserDefaults];
|
||||||
|
if (sharedDefaults != nil)
|
||||||
|
{
|
||||||
|
[sharedDefaults->_tempDomains setObject: regDefs
|
||||||
|
forKey: NSRegistrationDomain];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create a locale dictionary when we have absolutely no information
|
/* Create a locale dictionary when we have absolutely no information
|
||||||
|
@ -457,65 +536,36 @@ static BOOL setSharedDefaults = NO; /* Flag to prevent infinite recursion */
|
||||||
|
|
||||||
+ (NSUserDefaults*) standardUserDefaults
|
+ (NSUserDefaults*) standardUserDefaults
|
||||||
{
|
{
|
||||||
|
NSUserDefaults *defs;
|
||||||
BOOL added_lang, added_locale;
|
BOOL added_lang, added_locale;
|
||||||
|
BOOL setup;
|
||||||
id lang;
|
id lang;
|
||||||
|
NSArray *nL;
|
||||||
NSArray *uL;
|
NSArray *uL;
|
||||||
NSEnumerator *enumerator;
|
NSEnumerator *enumerator;
|
||||||
|
|
||||||
/*
|
/* If the shared instance is already available ... return it.
|
||||||
* Calling +standardUserDefaults and +userLanguages is horribly interrelated.
|
|
||||||
* Messing with the order of assignments and calls within these methods can
|
|
||||||
* break things very easily ... take care.
|
|
||||||
*
|
|
||||||
* If +standardUserDefaults is called first, it sets up initial information
|
|
||||||
* in sharedDefaults then calls +userLanguages to get language information.
|
|
||||||
* +userLanguages then calls +standardUserDefaults to read NSLanguages
|
|
||||||
* and returns the language information.
|
|
||||||
* +standardUserDefaults then sets up language domains and loads localisation
|
|
||||||
* information before returning.
|
|
||||||
*
|
|
||||||
* If +userLanguages is called first, it calls +standardUserDefaults
|
|
||||||
* to obtain the NSLanguages array.
|
|
||||||
* +standardUserDefaults loads basic information initialising the
|
|
||||||
* sharedDefaults variable, then calls back to +userLanguages to set up
|
|
||||||
* its search list.
|
|
||||||
* +userLanguages calls +standardUserDefaults again to get NSLanguages.
|
|
||||||
* +standardUserDefaults returns the partially initialised sharedDefaults.
|
|
||||||
* +userLanguages uses this to create and return the user languages.
|
|
||||||
* +standardUserDefaults uses the languages to update the search list
|
|
||||||
* and load in localisation information then returns sharedDefaults.
|
|
||||||
* +userLanguages uses this to rebuild language information and return it.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Return the sharedDefaults without locking in the simple case.
|
|
||||||
* We need to lock and check again before CREATING sharedDefaults if it doesn't exist,
|
|
||||||
* so that two threads can't create it at once (or call resetStandardUserDefaults at
|
|
||||||
* the same time).
|
|
||||||
* By not locking here, we avoid a deadlock that can occur between classLock and _lock. */
|
|
||||||
if (setSharedDefaults == YES)
|
|
||||||
{
|
|
||||||
IF_NO_GC([sharedDefaults retain];)
|
|
||||||
return AUTORELEASE(sharedDefaults);
|
|
||||||
}
|
|
||||||
|
|
||||||
[classLock lock];
|
[classLock lock];
|
||||||
|
defs = [sharedDefaults retain];
|
||||||
/*
|
setup = hasSharedDefaults;
|
||||||
* NB. The use of the setSharedDefaults flag ensures that a recursive
|
|
||||||
* call to this method is quietly suppressed ... so we get a more
|
|
||||||
* manageable problem.
|
|
||||||
*/
|
|
||||||
if (setSharedDefaults == YES)
|
|
||||||
{
|
|
||||||
IF_NO_GC([sharedDefaults retain];)
|
|
||||||
[classLock unlock];
|
[classLock unlock];
|
||||||
return AUTORELEASE(sharedDefaults);
|
if (YES == setup)
|
||||||
|
{
|
||||||
|
return [defs autorelease];
|
||||||
}
|
}
|
||||||
setSharedDefaults = YES;
|
|
||||||
|
|
||||||
NS_DURING
|
NS_DURING
|
||||||
{
|
{
|
||||||
// Create new sharedDefaults (NOTE: Not added to the autorelease pool!)
|
/* Create new NSUserDefaults (NOTE: Not added to the autorelease pool!)
|
||||||
|
* NB. The following code avoids deadlocks by creating a minimally
|
||||||
|
* initialised instance, locking that instance, locking the class-wide
|
||||||
|
* lock, installing the instance as the new shared defaults, unlocking
|
||||||
|
* the class wide lock, completing the setup of the instance, and then
|
||||||
|
* unlocking the instance. This means we already have the shared
|
||||||
|
* instance locked ourselves at the point when it first becomes
|
||||||
|
* visible to other threads.
|
||||||
|
*/
|
||||||
#if defined(__MINGW__)
|
#if defined(__MINGW__)
|
||||||
{
|
{
|
||||||
NSString *path = GSDefaultsRootForUser(NSUserName());
|
NSString *path = GSDefaultsRootForUser(NSUserName());
|
||||||
|
@ -523,43 +573,76 @@ static BOOL setSharedDefaults = NO; /* Flag to prevent infinite recursion */
|
||||||
|
|
||||||
if (r.length > 0)
|
if (r.length > 0)
|
||||||
{
|
{
|
||||||
sharedDefaults = [[NSUserDefaultsWin32 alloc] init];
|
defs = [[NSUserDefaultsWin32 alloc] init];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
sharedDefaults = [[self alloc] init];
|
defs = [[self alloc] init];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
sharedDefaults = [[self alloc] init];
|
defs = [[self alloc] init];
|
||||||
#endif
|
#endif
|
||||||
if (sharedDefaults == nil)
|
|
||||||
|
/* Install the new defaults as the shared copy, but lock it so that
|
||||||
|
* we can complete setup without other threads interfering.
|
||||||
|
*/
|
||||||
|
if (nil != defs)
|
||||||
|
{
|
||||||
|
[defs->_lock lock];
|
||||||
|
[classLock lock];
|
||||||
|
if (NO == hasSharedDefaults)
|
||||||
|
{
|
||||||
|
hasSharedDefaults = YES;
|
||||||
|
sharedDefaults = [defs retain];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Already set up by another thread.
|
||||||
|
*/
|
||||||
|
[defs->_lock unlock];
|
||||||
|
[defs release];
|
||||||
|
defs = nil;
|
||||||
|
}
|
||||||
|
[classLock unlock];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nil == defs)
|
||||||
{
|
{
|
||||||
NSLog(@"WARNING - unable to create shared user defaults!\n");
|
NSLog(@"WARNING - unable to create shared user defaults!\n");
|
||||||
[classLock unlock];
|
|
||||||
NS_VALRETURN(nil);
|
NS_VALRETURN(nil);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set up search list (excluding language list, which we don't know yet)
|
* Set up search list (excluding language list, which we don't know yet)
|
||||||
*/
|
*/
|
||||||
[sharedDefaults->_searchList addObject: NSArgumentDomain];
|
[defs->_searchList addObject: GSPrimaryDomain];
|
||||||
[sharedDefaults->_searchList addObject: processName];
|
[defs->_searchList addObject: NSArgumentDomain];
|
||||||
[sharedDefaults->_searchList addObject: NSGlobalDomain];
|
[defs->_searchList addObject: processName];
|
||||||
[sharedDefaults->_searchList addObject: GSConfigDomain];
|
[defs->_searchList addObject: NSGlobalDomain];
|
||||||
[sharedDefaults->_searchList addObject: NSRegistrationDomain];
|
[defs->_searchList addObject: GSConfigDomain];
|
||||||
|
[defs->_searchList addObject: NSRegistrationDomain];
|
||||||
|
|
||||||
|
/* Load persistent data into the new instance.
|
||||||
|
*/
|
||||||
|
[defs synchronize];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Look up user languages list and insert language specific domains
|
* Look up user languages list and insert language specific domains
|
||||||
* into search list before NSRegistrationDomain
|
* into search list before NSRegistrationDomain
|
||||||
*/
|
*/
|
||||||
uL = [self userLanguages];
|
uL = [defs stringArrayForKey: @"NSLanguages"];
|
||||||
enumerator = [uL objectEnumerator];
|
nL = newLanguages(uL);
|
||||||
|
if (NO == [uL isEqual: nL])
|
||||||
|
{
|
||||||
|
[self setUserLanguages: nL];
|
||||||
|
}
|
||||||
|
enumerator = [nL objectEnumerator];
|
||||||
while ((lang = [enumerator nextObject]))
|
while ((lang = [enumerator nextObject]))
|
||||||
{
|
{
|
||||||
unsigned index = [sharedDefaults->_searchList count] - 1;
|
unsigned index = [defs->_searchList count] - 1;
|
||||||
|
|
||||||
[sharedDefaults->_searchList insertObject: lang atIndex: index];
|
[defs->_searchList insertObject: lang atIndex: index];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Set up language constants */
|
/* Set up language constants */
|
||||||
|
@ -623,7 +706,7 @@ static BOOL setSharedDefaults = NO; /* Flag to prevent infinite recursion */
|
||||||
}
|
}
|
||||||
if (dict != nil)
|
if (dict != nil)
|
||||||
{
|
{
|
||||||
[sharedDefaults setVolatileDomain: dict forName: lang];
|
[defs setVolatileDomain: dict forName: lang];
|
||||||
added_lang = YES;
|
added_lang = YES;
|
||||||
}
|
}
|
||||||
else if (added_locale == NO)
|
else if (added_locale == NO)
|
||||||
|
@ -663,8 +746,7 @@ static BOOL setSharedDefaults = NO; /* Flag to prevent infinite recursion */
|
||||||
dict = GSDomainFromDefaultLocale ();
|
dict = GSDomainFromDefaultLocale ();
|
||||||
if (dict != nil)
|
if (dict != nil)
|
||||||
{
|
{
|
||||||
[sharedDefaults setVolatileDomain: dict
|
[defs setVolatileDomain: dict forName: lang];
|
||||||
forName: lang];
|
|
||||||
|
|
||||||
/* We do not set added_lang to YES here
|
/* We do not set added_lang to YES here
|
||||||
* because we want the basic hardcoded defaults
|
* because we want the basic hardcoded defaults
|
||||||
|
@ -684,154 +766,49 @@ static BOOL setSharedDefaults = NO; /* Flag to prevent infinite recursion */
|
||||||
* We need to use hard-coded defaults.
|
* We need to use hard-coded defaults.
|
||||||
*/
|
*/
|
||||||
/* FIXME - should we set this as volatile domain for English ? */
|
/* FIXME - should we set this as volatile domain for English ? */
|
||||||
[sharedDefaults registerDefaults: [self _unlocalizedDefaults]];
|
[defs registerDefaults: [self _unlocalizedDefaults]];
|
||||||
}
|
}
|
||||||
IF_NO_GC([sharedDefaults retain];)
|
|
||||||
updateCache(sharedDefaults);
|
updateCache(sharedDefaults);
|
||||||
[classLock unlock];
|
[defs->_lock unlock];
|
||||||
}
|
}
|
||||||
NS_HANDLER
|
NS_HANDLER
|
||||||
{
|
{
|
||||||
[classLock unlock];
|
[defs->_lock unlock];
|
||||||
|
[defs release];
|
||||||
[localException raise];
|
[localException raise];
|
||||||
}
|
}
|
||||||
NS_ENDHANDLER
|
NS_ENDHANDLER
|
||||||
return AUTORELEASE(sharedDefaults);
|
return [defs autorelease];
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (NSArray*) userLanguages
|
+ (NSArray*) userLanguages
|
||||||
{
|
{
|
||||||
NSArray *result = nil;
|
return [[NSUserDefaults standardUserDefaults]
|
||||||
|
|
||||||
/*
|
|
||||||
* Calling +standardUserDefaults and +userLanguages is horribly interrelated.
|
|
||||||
* Messing with the order of assignments and calls within these methods can
|
|
||||||
* break things very easily ... take care.
|
|
||||||
*
|
|
||||||
* If +standardUserDefaults is called first, it sets up initial information
|
|
||||||
* in sharedDefaults then calls +userLanguages to get language information.
|
|
||||||
* +userLanguages then calls +standardUserDefaults to read NSLanguages
|
|
||||||
* and returns the language information.
|
|
||||||
* +standardUserDefaults then sets up language domains and loads localisation
|
|
||||||
* information before returning.
|
|
||||||
*
|
|
||||||
* If +userLanguages is called first, it calls +standardUserDefaults
|
|
||||||
* to obtain the NSLanguages array.
|
|
||||||
* +standardUserDefaults loads basic information initialising the
|
|
||||||
* sharedDefaults variable, then calls back to +userLanguages to set up
|
|
||||||
* its search list.
|
|
||||||
* +userLanguages calls +standardUserDefaults again to get NSLanguages.
|
|
||||||
* +standardUserDefaults returns the partially initialised sharedDefaults.
|
|
||||||
* +userLanguages uses this to create and return the user languages.
|
|
||||||
* +standardUserDefaults uses the languages to update the search list
|
|
||||||
* and load in localisation information then returns sharedDefaults.
|
|
||||||
* +userLanguages uses this to rebuild language information and return it.
|
|
||||||
*/
|
|
||||||
|
|
||||||
[classLock lock];
|
|
||||||
NS_DURING
|
|
||||||
{
|
|
||||||
if (invalidatedLanguages == YES)
|
|
||||||
{
|
|
||||||
invalidatedLanguages = NO;
|
|
||||||
DESTROY(userLanguages);
|
|
||||||
}
|
|
||||||
if (userLanguages == nil)
|
|
||||||
{
|
|
||||||
NSArray *currLang = nil;
|
|
||||||
NSString *locale = nil;
|
|
||||||
|
|
||||||
#ifdef HAVE_LOCALE_H
|
|
||||||
#ifdef LC_MESSAGES
|
|
||||||
locale = GSSetLocale(LC_MESSAGES, nil);
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
currLang = [[NSUserDefaults standardUserDefaults]
|
|
||||||
stringArrayForKey: @"NSLanguages"];
|
stringArrayForKey: @"NSLanguages"];
|
||||||
|
|
||||||
userLanguages = [[NSMutableArray alloc] initWithCapacity: 5];
|
|
||||||
|
|
||||||
if (currLang == nil && locale != nil && GSLanguageFromLocale(locale))
|
|
||||||
{
|
|
||||||
currLang = [NSArray arrayWithObject: GSLanguageFromLocale(locale)];
|
|
||||||
}
|
|
||||||
#ifdef __MINGW__
|
|
||||||
if (currLang == nil && locale != nil)
|
|
||||||
{
|
|
||||||
/* Check for language as the first part of the locale string */
|
|
||||||
NSRange under = [locale rangeOfString: @"_"];
|
|
||||||
if (under.location)
|
|
||||||
currLang = [NSArray arrayWithObject:
|
|
||||||
[locale substringToIndex: under.location]];
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (currLang == nil)
|
|
||||||
{
|
|
||||||
NSString *env;
|
|
||||||
|
|
||||||
env = [[[NSProcessInfo processInfo] environment]
|
|
||||||
objectForKey: @"LANGUAGES"];
|
|
||||||
if (env != nil)
|
|
||||||
{
|
|
||||||
currLang = [env componentsSeparatedByString: @";"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currLang != nil)
|
|
||||||
{
|
|
||||||
NSMutableArray *a = [currLang mutableCopy];
|
|
||||||
unsigned c = [a count];
|
|
||||||
|
|
||||||
while (c-- > 0)
|
|
||||||
{
|
|
||||||
NSString *s = [[a objectAtIndex: c] stringByTrimmingSpaces];
|
|
||||||
|
|
||||||
if ([s length] == 0)
|
|
||||||
{
|
|
||||||
[a removeObjectAtIndex: c];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
[a replaceObjectAtIndex: c withObject: s];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
[userLanguages addObjectsFromArray: a];
|
|
||||||
RELEASE(a);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if "English" is included. We do this to make sure all the
|
|
||||||
required language constants are set somewhere if they aren't set
|
|
||||||
in the default language */
|
|
||||||
if ([userLanguages containsObject: @"English"] == NO)
|
|
||||||
{
|
|
||||||
[userLanguages addObject: @"English"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result = RETAIN(userLanguages);
|
|
||||||
[classLock unlock];
|
|
||||||
}
|
|
||||||
NS_HANDLER
|
|
||||||
{
|
|
||||||
[classLock unlock];
|
|
||||||
[localException raise];
|
|
||||||
}
|
|
||||||
NS_ENDHANDLER
|
|
||||||
return AUTORELEASE(result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (void) setUserLanguages: (NSArray*)languages
|
+ (void) setUserLanguages: (NSArray*)languages
|
||||||
{
|
{
|
||||||
NSMutableDictionary *globDict;
|
NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
|
||||||
|
NSMutableDictionary *dict;
|
||||||
|
|
||||||
globDict = [[[self standardUserDefaults]
|
dict = [[defs volatileDomainForName: GSPrimaryDomain] mutableCopy];
|
||||||
persistentDomainForName: NSGlobalDomain] mutableCopy];
|
|
||||||
if (languages == nil) // Remove the entry
|
if (languages == nil) // Remove the entry
|
||||||
[globDict removeObjectForKey: @"NSLanguages"];
|
{
|
||||||
|
[dict removeObjectForKey: @"NSLanguages"];
|
||||||
|
}
|
||||||
else
|
else
|
||||||
[globDict setObject: languages forKey: @"NSLanguages"];
|
{
|
||||||
[[self standardUserDefaults]
|
if (nil == dict)
|
||||||
setPersistentDomain: globDict forName: NSGlobalDomain];
|
{
|
||||||
RELEASE(globDict);
|
dict = [NSMutableDictionary new];
|
||||||
|
}
|
||||||
|
languages = newLanguages(languages);
|
||||||
|
[dict setObject: languages forKey: @"NSLanguages"];
|
||||||
|
}
|
||||||
|
[defs removeVolatileDomainForName: GSPrimaryDomain];
|
||||||
|
[defs setVolatileDomain: dict forName: GSPrimaryDomain];
|
||||||
|
[dict release];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id) init
|
- (id) init
|
||||||
|
@ -918,27 +895,6 @@ static BOOL setSharedDefaults = NO; /* Flag to prevent infinite recursion */
|
||||||
{
|
{
|
||||||
// Initialize _persDomains from the archived user defaults (persistent)
|
// Initialize _persDomains from the archived user defaults (persistent)
|
||||||
_persDomains = [[NSMutableDictionaryClass alloc] initWithCapacity: 10];
|
_persDomains = [[NSMutableDictionaryClass alloc] initWithCapacity: 10];
|
||||||
if ([self synchronize] == NO)
|
|
||||||
{
|
|
||||||
DESTROY(self);
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check and if not existent add the Application and the Global domains
|
|
||||||
if ([_persDomains objectForKey: processName] == nil)
|
|
||||||
{
|
|
||||||
[_persDomains
|
|
||||||
setObject: [NSMutableDictionaryClass dictionaryWithCapacity: 10]
|
|
||||||
forKey: processName];
|
|
||||||
[self __changePersistentDomain: processName];
|
|
||||||
}
|
|
||||||
if ([_persDomains objectForKey: NSGlobalDomain] == nil)
|
|
||||||
{
|
|
||||||
[_persDomains
|
|
||||||
setObject: [NSMutableDictionaryClass dictionaryWithCapacity: 10]
|
|
||||||
forKey: NSGlobalDomain];
|
|
||||||
[self __changePersistentDomain: NSGlobalDomain];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create volatile defaults and add the Argument and the Registration domains
|
// Create volatile defaults and add the Argument and the Registration domains
|
||||||
|
@ -1007,7 +963,6 @@ static BOOL setSharedDefaults = NO; /* Flag to prevent infinite recursion */
|
||||||
NS_DURING
|
NS_DURING
|
||||||
{
|
{
|
||||||
DESTROY(_dictionaryRep);
|
DESTROY(_dictionaryRep);
|
||||||
if (self == sharedDefaults) invalidatedLanguages = YES;
|
|
||||||
[_searchList removeObject: aName];
|
[_searchList removeObject: aName];
|
||||||
index = [_searchList indexOfObject: processName];
|
index = [_searchList indexOfObject: processName];
|
||||||
index = (index == NSNotFound) ? 0 : (index + 1);
|
index = (index == NSNotFound) ? 0 : (index + 1);
|
||||||
|
@ -1357,7 +1312,6 @@ static BOOL isPlistObject(id o)
|
||||||
NS_DURING
|
NS_DURING
|
||||||
{
|
{
|
||||||
DESTROY(_dictionaryRep);
|
DESTROY(_dictionaryRep);
|
||||||
if (self == sharedDefaults) invalidatedLanguages = YES;
|
|
||||||
RELEASE(_searchList);
|
RELEASE(_searchList);
|
||||||
_searchList = [newList mutableCopy];
|
_searchList = [newList mutableCopy];
|
||||||
[_lock unlock];
|
[_lock unlock];
|
||||||
|
@ -1705,56 +1659,43 @@ NSLog(@"Creating empty user defaults database");
|
||||||
- (BOOL) synchronize
|
- (BOOL) synchronize
|
||||||
{
|
{
|
||||||
NSMutableDictionary *newDict;
|
NSMutableDictionary *newDict;
|
||||||
|
NSDate *saved;
|
||||||
BOOL wasLocked;
|
BOOL wasLocked;
|
||||||
|
BOOL result = YES;
|
||||||
|
|
||||||
[_lock lock];
|
[_lock lock];
|
||||||
|
saved = _lastSync;
|
||||||
|
_lastSync = [NSDate new]; // Record timestamp of this sync.
|
||||||
NS_DURING
|
NS_DURING
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* If we haven't changed anything, we only need to synchronise if
|
* If we haven't changed anything, we only need to synchronise if
|
||||||
* the on-disk database has been changed by someone else.
|
* the on-disk database has been changed by someone else.
|
||||||
*/
|
*/
|
||||||
|
if (_changedDomains != nil
|
||||||
if (_changedDomains == nil)
|
|| YES == [self wantToReadDefaultsSince: saved])
|
||||||
{
|
{
|
||||||
if ([self wantToReadDefaultsSince: _lastSync] == NO)
|
|
||||||
{
|
|
||||||
[_lock unlock];
|
|
||||||
NS_VALRETURN(YES);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DESTROY(_dictionaryRep);
|
DESTROY(_dictionaryRep);
|
||||||
if (self == sharedDefaults)
|
|
||||||
{
|
|
||||||
invalidatedLanguages = YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ([self lockDefaultsFile: &wasLocked] == NO)
|
if ([self lockDefaultsFile: &wasLocked] == NO)
|
||||||
{
|
{
|
||||||
[_lock unlock];
|
result = NO;
|
||||||
NS_VALRETURN(NO);
|
|
||||||
}
|
}
|
||||||
|
else if (nil == (newDict = [self readDefaults]))
|
||||||
newDict = [self readDefaults];
|
|
||||||
|
|
||||||
if (newDict == nil)
|
|
||||||
{
|
{
|
||||||
if (wasLocked == NO)
|
if (wasLocked == NO)
|
||||||
{
|
{
|
||||||
[self unlockDefaultsFile];
|
[self unlockDefaultsFile];
|
||||||
}
|
}
|
||||||
[_lock unlock];
|
result = NO;
|
||||||
NS_VALRETURN(NO);
|
|
||||||
}
|
}
|
||||||
|
else if (_changedDomains != nil)
|
||||||
if (_changedDomains != nil)
|
|
||||||
{ // Synchronize both dictionaries
|
{ // Synchronize both dictionaries
|
||||||
NSEnumerator *enumerator = [_changedDomains objectEnumerator];
|
NSEnumerator *enumerator;
|
||||||
NSString *domainName;
|
NSString *domainName;
|
||||||
NSDictionary *domain;
|
NSDictionary *domain;
|
||||||
NSDictionary *oldData = AUTORELEASE([newDict copy]);
|
NSDictionary *oldData = AUTORELEASE([newDict copy]);
|
||||||
|
|
||||||
|
enumerator = [_changedDomains objectEnumerator];
|
||||||
DESTROY(_changedDomains); // Retained by enumerator.
|
DESTROY(_changedDomains); // Retained by enumerator.
|
||||||
while ((domainName = [enumerator nextObject]) != nil)
|
while ((domainName = [enumerator nextObject]) != nil)
|
||||||
{
|
{
|
||||||
|
@ -1775,14 +1716,11 @@ NSLog(@"Creating empty user defaults database");
|
||||||
{
|
{
|
||||||
[self unlockDefaultsFile];
|
[self unlockDefaultsFile];
|
||||||
}
|
}
|
||||||
[_lock unlock];
|
result = NO;
|
||||||
NS_VALRETURN(NO);
|
|
||||||
}
|
}
|
||||||
ASSIGN(_lastSync, [NSDateClass date]);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ASSIGN(_lastSync, [NSDateClass date]);
|
|
||||||
if ([_persDomains isEqual: newDict] == NO)
|
if ([_persDomains isEqual: newDict] == NO)
|
||||||
{
|
{
|
||||||
ASSIGN(_persDomains, newDict);
|
ASSIGN(_persDomains, newDict);
|
||||||
|
@ -1792,20 +1730,47 @@ NSLog(@"Creating empty user defaults database");
|
||||||
object: self];
|
object: self];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wasLocked == NO)
|
if (wasLocked == NO)
|
||||||
{
|
{
|
||||||
[self unlockDefaultsFile];
|
[self unlockDefaultsFile];
|
||||||
}
|
}
|
||||||
[_lock unlock];
|
}
|
||||||
}
|
}
|
||||||
NS_HANDLER
|
NS_HANDLER
|
||||||
{
|
{
|
||||||
|
[_lastSync release];
|
||||||
|
_lastSync = saved;
|
||||||
[_lock unlock];
|
[_lock unlock];
|
||||||
[localException raise];
|
[localException raise];
|
||||||
}
|
}
|
||||||
NS_ENDHANDLER
|
NS_ENDHANDLER
|
||||||
return YES;
|
|
||||||
|
if (YES == result)
|
||||||
|
{
|
||||||
|
[saved release];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
[_lastSync release];
|
||||||
|
_lastSync = saved;
|
||||||
|
}
|
||||||
|
// Check and if not existent add the Application and the Global domains
|
||||||
|
if ([_persDomains objectForKey: processName] == nil)
|
||||||
|
{
|
||||||
|
[_persDomains
|
||||||
|
setObject: [NSMutableDictionaryClass dictionaryWithCapacity: 10]
|
||||||
|
forKey: processName];
|
||||||
|
[self __changePersistentDomain: processName];
|
||||||
|
}
|
||||||
|
if ([_persDomains objectForKey: NSGlobalDomain] == nil)
|
||||||
|
{
|
||||||
|
[_persDomains
|
||||||
|
setObject: [NSMutableDictionaryClass dictionaryWithCapacity: 10]
|
||||||
|
forKey: NSGlobalDomain];
|
||||||
|
[self __changePersistentDomain: NSGlobalDomain];
|
||||||
|
}
|
||||||
|
[_lock unlock];
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1815,7 +1780,6 @@ NSLog(@"Creating empty user defaults database");
|
||||||
NS_DURING
|
NS_DURING
|
||||||
{
|
{
|
||||||
DESTROY(_dictionaryRep);
|
DESTROY(_dictionaryRep);
|
||||||
if (self == sharedDefaults) invalidatedLanguages = YES;
|
|
||||||
[_tempDomains removeObjectForKey: domainName];
|
[_tempDomains removeObjectForKey: domainName];
|
||||||
[_lock unlock];
|
[_lock unlock];
|
||||||
}
|
}
|
||||||
|
@ -1849,7 +1813,6 @@ NSLog(@"Creating empty user defaults database");
|
||||||
}
|
}
|
||||||
|
|
||||||
DESTROY(_dictionaryRep);
|
DESTROY(_dictionaryRep);
|
||||||
if (self == sharedDefaults) invalidatedLanguages = YES;
|
|
||||||
domain = [domain mutableCopy];
|
domain = [domain mutableCopy];
|
||||||
[_tempDomains setObject: domain forKey: domainName];
|
[_tempDomains setObject: domain forKey: domainName];
|
||||||
RELEASE(domain);
|
RELEASE(domain);
|
||||||
|
@ -1968,7 +1931,6 @@ NSLog(@"Creating empty user defaults database");
|
||||||
[_tempDomains setObject: regDefs forKey: NSRegistrationDomain];
|
[_tempDomains setObject: regDefs forKey: NSRegistrationDomain];
|
||||||
}
|
}
|
||||||
DESTROY(_dictionaryRep);
|
DESTROY(_dictionaryRep);
|
||||||
if (self == sharedDefaults) invalidatedLanguages = YES;
|
|
||||||
[regDefs addEntriesFromDictionary: newVals];
|
[regDefs addEntriesFromDictionary: newVals];
|
||||||
[_lock unlock];
|
[_lock unlock];
|
||||||
}
|
}
|
||||||
|
@ -1991,7 +1953,6 @@ NSLog(@"Creating empty user defaults database");
|
||||||
NS_DURING
|
NS_DURING
|
||||||
{
|
{
|
||||||
DESTROY(_dictionaryRep);
|
DESTROY(_dictionaryRep);
|
||||||
if (self == sharedDefaults) invalidatedLanguages = YES;
|
|
||||||
[_searchList removeObject: aName];
|
[_searchList removeObject: aName];
|
||||||
[_lock unlock];
|
[_lock unlock];
|
||||||
}
|
}
|
||||||
|
@ -2111,7 +2072,6 @@ NSLog(@"Creating empty user defaults database");
|
||||||
NS_DURING
|
NS_DURING
|
||||||
{
|
{
|
||||||
DESTROY(_dictionaryRep);
|
DESTROY(_dictionaryRep);
|
||||||
if (self == sharedDefaults) invalidatedLanguages = YES;
|
|
||||||
if (_changedDomains == nil)
|
if (_changedDomains == nil)
|
||||||
{
|
{
|
||||||
_changedDomains = [[NSMutableArray alloc] initWithObjects: &domainName
|
_changedDomains = [[NSMutableArray alloc] initWithObjects: &domainName
|
||||||
|
|
Loading…
Reference in a new issue