From 0608b15caa35831a16aa7e5fd1490df24dd01784 Mon Sep 17 00:00:00 2001 From: "gtm.daemon" Date: Wed, 2 Jun 2010 17:36:03 +0000 Subject: [Author: dmaclach] Cleans up builds of GTM on Snow Leopard with gcc 4.2. Cleans up iPhone configs Adds libgcov for Snow Leopard Fixes up some small bugs. R=thomasvl DELTA=2028 (972 added, 990 deleted, 66 changed) --- AppKit/GTMCarbonEvent.m | 1 + AppKit/GTMGetURLHandlerTest.m | 2 +- AppKit/GTMGoogleSearch.h | 144 ++++++ AppKit/GTMGoogleSearch.m | 536 +++++++++++++++++++++ AppKit/GTMGoogleSearchTest.m | 208 ++++++++ AppKit/GTMHotKeyTextFieldTest.m | 8 +- AppKit/GTMNSImage+Scaling.h | 6 +- AppKit/GTMNSImage+ScalingTest.m | 20 +- AppKit/GTMNSImage+SearchCache.h | 2 + ...lizerAndLayoutTweakerTest4-0.10_4_SDK.10.6.tiff | Bin 0 -> 30128 bytes ...lizerAndLayoutTweakerTest4-1.10_4_SDK.10.6.tiff | Bin 0 -> 42058 bytes ...lizerAndLayoutTweakerTest4-2.10_4_SDK.10.6.tiff | Bin 0 -> 28444 bytes 12 files changed, 919 insertions(+), 8 deletions(-) create mode 100644 AppKit/GTMGoogleSearch.h create mode 100644 AppKit/GTMGoogleSearch.m create mode 100644 AppKit/GTMGoogleSearchTest.m create mode 100644 AppKit/TestData/GTMUILocalizerAndLayoutTweakerTest4-0.10_4_SDK.10.6.tiff create mode 100644 AppKit/TestData/GTMUILocalizerAndLayoutTweakerTest4-1.10_4_SDK.10.6.tiff create mode 100644 AppKit/TestData/GTMUILocalizerAndLayoutTweakerTest4-2.10_4_SDK.10.6.tiff (limited to 'AppKit') diff --git a/AppKit/GTMCarbonEvent.m b/AppKit/GTMCarbonEvent.m index 479e49e..28bfdb9 100644 --- a/AppKit/GTMCarbonEvent.m +++ b/AppKit/GTMCarbonEvent.m @@ -17,6 +17,7 @@ // #import "GTMCarbonEvent.h" +#import #import "GTMObjectSingleton.h" #import "GTMDebugSelectorValidation.h" #import "GTMTypeCasting.h" diff --git a/AppKit/GTMGetURLHandlerTest.m b/AppKit/GTMGetURLHandlerTest.m index 940d90f..a639176 100644 --- a/AppKit/GTMGetURLHandlerTest.m +++ b/AppKit/GTMGetURLHandlerTest.m @@ -51,7 +51,7 @@ static BOOL sURLHandlerWasHit; + (BOOL)gtm_openURL:(NSURL*)url { sURLHandlerWasHit = !sURLHandlerWasHit; - return sURLHandlerWasHit; + return YES; } - (void)testURLCall { diff --git a/AppKit/GTMGoogleSearch.h b/AppKit/GTMGoogleSearch.h new file mode 100644 index 0000000..06b8d51 --- /dev/null +++ b/AppKit/GTMGoogleSearch.h @@ -0,0 +1,144 @@ +// +// GTMGoogleSearch.h +// +// Copyright 2006-2009 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +#import + +// Key for Info.plist for default global search args +#define GTMGoogleSearchClientAppArgsKey @"GTMGoogleSearchClientAppArgs" + +// Types to pass in to searchForURL:ofType:arguments +// and performQuery:ofType:arguments +#define GTMGoogleSearchFroogle @"products" +#define GTMGoogleSearchGroups @"groups" +#define GTMGoogleSearchImages @"images" +#define GTMGoogleSearchLocal @"local" +#define GTMGoogleSearchNews @"news" +#define GTMGoogleSearchFinance @"finance" +#define GTMGoogleSearchBooks @"books" +#define GTMGoogleSearchWeb @"search" + +// iPhone doesn't support distributed notifications, so this controls whether +// or not we enable them in this class. +#define GTM_GOOGLE_SEARCH_SUPPORTS_DISTRIBUTED_NOTIFICATIONS GTM_MACOS_SDK + +// Composes URLs and searches for google properties in the correct language +// and domain. +@interface GTMGoogleSearch : NSObject { + // the cached values + NSString *allAppsCachedDomain_; + NSString *allAppsCachedLanguage_; + NSString *curAppCachedDomain_; + NSString *curAppCachedLanguage_; + NSDictionary *globalSearchArguments_; +} + +// +// +sharedInstance +// +// fetches the common shared object for accessing this users preference +// ++ (GTMGoogleSearch*)sharedInstance; + +// +// searchURLFor:ofType:arguments: +// +// creates a search url of type |type| for |queryText| using the user's +// preferred domain and language settings. |args| is a set of arguments +// that will be added into your query, and you can use it to complement +// or override settings stored in globalSearchArguments. +// example dictionary to do an I'm feeling lucky search would be: +// [NSDictionary dictionaryWithObject:@"1" key:@"btnI"]; +// If queryText is nil, no query will be put in. +// Arguments passed in in args must be properly URL escaped. +// If you want to remove one of the arguments that will be included in the +// global search arguments, set the object for the key you want to remove to +// [NSNull null]. +- (NSString*)searchURLFor:(NSString *)queryText + ofType:(NSString *)type + arguments:(NSDictionary *)args; + +// +// performQuery:ofType:arguments: +// +// Asks NSWorkspace to open up a query for an url created by passing +// the args to searchURLFor:ofType:arguments: above. +// +- (BOOL)performQuery:(NSString *)queryText + ofType:(NSString *)type + arguments:(NSDictionary *)localArgs; + +// Global search arguments are initially picked up from your main bundle +// info.plist if there is a dictionary entry at the top level with the key +// "GTMGoogleSearchClientAppArgs". This dictionary should be a map of strings +// to strings where they are the args you want passed to all Google searches. +// You can override these with your localArgs when you actually perform the +// search if you wish. +// This arguments will affect all searches. Arguments must be properly URL +// escaped. +- (void)setGlobalSearchArguments:(NSDictionary *)args; + +// Returns the global search arguments. +- (NSDictionary *)globalSearchArguments; + +// +// -preferredDomainAndLanguage:areCurrentAppOnly +// +// fetches the user's preferred domain and language, and whether the values +// that were grabbed were from the anyapplication domain, or from the current +// application domain. You may pass in nil for |language| if you don't want +// a language back, and you may pass in NULL for |currentAppOnly| if you don't +// care about where it came from. +// +- (void)preferredDomain:(NSString **)domain + language:(NSString **)language + areCurrentAppOnly:(BOOL*)currentAppOnly; + +// +// -updatePreferredDomain:language:currentApplicationOnly: +// +// updated the users preferred domain and language to copies of |domain| and +// |language| respectively. |domain| can't be nil or an empty string, but +// |language| can't be nil, but can be an empty string to signify no language +// pref. If |currentAppOnly| is YES, only updates the preferred settings for the +// current app, otherwise updates them for all apps. +// +- (void)updatePreferredDomain:(NSString *)domain + language:(NSString *)language + currentApplicationOnly:(BOOL)currentAppOnly; + +// +// -clearPreferredDomainAndLanguageForCurrentApplication +// +// clears the setting for the current applications preferred domain and +// language so future fetches will get the system level ones. +// +- (void)clearPreferredDomainAndLanguageForCurrentApplication; + +// +// -clearPreferredDomainAndLanguageForAllApps +// +// clears the "AllApps" setting for preferred domain and language so future +// fetches end up having to use the default. Odds are this is only +// used by the unittests. +// NOTE: this doesn't do anything to any setting that's set in an individual +// apps preferences, so those settings will still override inplace of the +// "all apps" value (or default). +// +- (void)clearPreferredDomainAndLanguageForAllApps; + +@end diff --git a/AppKit/GTMGoogleSearch.m b/AppKit/GTMGoogleSearch.m new file mode 100644 index 0000000..9eb095f --- /dev/null +++ b/AppKit/GTMGoogleSearch.m @@ -0,0 +1,536 @@ +// +// GTMGoogleSearch.m +// +// Copyright 2006-2009 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +#import "GTMGoogleSearch.h" +#import "GTMObjectSingleton.h" +#import "GTMGarbageCollection.h" + +#if GTM_IPHONE_SDK +#import +#else +#import +#endif // GTM_IPHONE_SDK + +typedef struct { + NSString *language; + NSString *country; + // we don't include a language, we'll use what we get from the OS + NSString *defaultDomain; +} LanguageDefaultInfo; + +// +// this is a seed mapping from languages to domains for google search. +// this doesn't have to be complete, as it is just a seed. +// +// +static LanguageDefaultInfo kLanguageListDefaultMappingTable[] = { + // order is important, first match is taken + // if country is |nil|, then only language has to match + { @"en", @"US", @"com" }, // english - united states + { @"en", @"GB", @"co.uk" }, // english - united kingdom + { @"en", @"CA", @"ca" }, // english - canada + { @"en", @"AU", @"com.au" }, // english - australia + { @"en", @"NZ", @"com" }, // english - new zealand + { @"en", @"IE", @"ie" }, // english - ireland + { @"en", @"IN", @"co.in" }, // english - india + { @"en", @"PH", @"com.ph" }, // english - philippines + { @"en", @"SG", @"com.sg" }, // english - singapore + { @"en", @"ZA", @"co.za" }, // english - south africa + { @"en", @"IL", @"co.il" }, // english - israel + { @"en", nil , @"com" }, // english (catch all) + { @"fr", @"CA", @"ca" }, // french - canada + { @"fr", @"CH", @"ch" }, // french - switzerland + { @"fr", nil , @"fr" }, // france + { @"it", nil , @"it" }, // italy + { @"de", @"AT", @"at" }, // german - austria + { @"de", nil , @"de" }, // germany + { @"es", @"MX", @"com.mx" }, // spanish - mexico + { @"es", @"AR", @"com.ar" }, // spanish - argentina + { @"es", @"CL", @"cl" }, // spanish - chile + { @"es", @"CO", @"com.co" }, // spanish - colombia + { @"es", @"PE", @"com.pe" }, // spanish - peru + { @"es", @"VE", @"co.ve" }, // venezuela + { @"es", nil , @"es" }, // spain + { @"zh", @"TW", @"com.tw" }, // taiwan + { @"zh", @"HK", @"com.hk" }, // hong kong + { @"zh", nil , @"cn" }, // chinese (catch all) + { @"ja", nil , @"co.jp" }, // japan + { @"ko", nil , @"co.kr" }, // korea + { @"nl", @"BE", @"be" }, // dutch - belgium + { @"nl", nil , @"nl" }, // (dutch) netherlands + { @"ru", nil , @"ru" }, // russia + { @"pt", @"BZ", @"com.br"}, // portuguese - brazil + { @"pt", nil , @"pt" }, // portugal + { @"sv", nil , @"se" }, // sweden + { @"nn", nil , @"no" }, // norway (two variants) + { @"nb", nil , @"no" }, // norway (two variants) + { @"da", nil , @"dk" }, // denmark + { @"fi", nil , @"fi" }, // finland + { @"bg", nil , @"bg" }, // bulgaria + { @"hr", nil , @"hr" }, // croatia + { @"cx", nil , @"cz" }, // czech republic + { @"el", nil , @"gr" }, // greece + { @"hu", nil , @"co.hu" }, // hungary + { @"ro", nil , @"ro" }, // romania + { @"sk", nil , @"sk" }, // slovakia + { @"sl", nil , @"si" }, // slovenia + { @"tr", nil , @"com.tr" }, // turkey + { @"my", nil , @"com.my" }, // malaysia + { @"th", nil , @"co.th" }, // thailand + { @"uk", nil , @"com.ua" }, // ukraine + { @"vi", nil , @"com.vn" }, // vietnam + { @"af", nil , @"com.za" }, // south africa (afrikaans) + { @"hi", nil , @"co.in" }, // india (hindi) + { @"id", nil , @"co.id" }, // indonesia + { @"pl", nil , @"pl" }, // poland +}; + +// the notification we use for syncing up instances in different processes +static NSString *const kNotificationName + = @"com.google.GoogleSearchAllApps.prefsWritten"; + +// this is the bundle id we use for the pref file used for all apps +static CFStringRef const kAllAppsBuildIdentifier + = CFSTR("com.google.GoogleSearchAllApps"); + +static CFStringRef const kPreferredDomainPrefKey + = CFSTR("com.google.PreferredDomain"); +static CFStringRef const kPreferredLanguagePrefKey + = CFSTR("com.google.PreferredLanguage"); + +static NSString *const kDefaultDomain = @"com"; +static NSString *const kDefaultLanguage = @"en"; + +#define SEARCH_URL_TEMPLATE @"http://www.google.%@/%@?%@" + +@interface GTMGoogleSearch (PrivateMethods) +- (void)defaultDomain:(NSString**)preferedDomain + language:(NSString**)preferredLanguage; +- (void)reloadAllAppCachedValues:(NSNotification*)notification; +- (void)updateAllAppsDomain:(NSString*)domain language:(NSString*)language; +@end + + +@implementation GTMGoogleSearch + +GTMOBJECT_SINGLETON_BOILERPLATE(GTMGoogleSearch, sharedInstance); + +- (id)init { + self = [super init]; + if (self != nil) { +#if GTM_GOOGLE_SEARCH_SUPPORTS_DISTRIBUTED_NOTIFICATIONS + // register for the notification + NSDistributedNotificationCenter *distCenter = + [NSDistributedNotificationCenter defaultCenter]; + [distCenter addObserver:self + selector:@selector(reloadAllAppCachedValues:) + name:kNotificationName + object:nil]; +#endif // GTM_GOOGLE_SEARCH_SUPPORTS_DISTRIBUTED_NOTIFICATIONS + // load the allApps value + [self reloadAllAppCachedValues:nil]; + + // load the cur app value + CFStringRef domain + = CFPreferencesCopyValue(kPreferredDomainPrefKey, + kCFPreferencesCurrentApplication, + kCFPreferencesCurrentUser, + kCFPreferencesAnyHost); + CFStringRef lang = CFPreferencesCopyValue(kPreferredLanguagePrefKey, + kCFPreferencesCurrentApplication, + kCFPreferencesCurrentUser, + kCFPreferencesAnyHost); + + // make sure we got values for both and domain is not empty + if (domain && CFStringGetLength(domain) == 0) { + CFRelease(domain); + domain = nil; + if (lang) { + CFRelease(lang); + lang = nil; + } + } + + curAppCachedDomain_ = GTMNSMakeCollectable(domain); + curAppCachedLanguage_ = GTMNSMakeCollectable(lang); + + NSBundle *bundle = [NSBundle mainBundle]; + + NSDictionary *appArgs + = [bundle objectForInfoDictionaryKey:GTMGoogleSearchClientAppArgsKey]; + globalSearchArguments_ = [appArgs retain]; + } + return self; +} + +#if GTM_GOOGLE_SEARCH_SUPPORTS_DISTRIBUTED_NOTIFICATIONS +- (void)finalize { + [[NSDistributedNotificationCenter defaultCenter] removeObject:self]; + [super finalize]; +} +#endif // GTM_GOOGLE_SEARCH_SUPPORTS_DISTRIBUTED_NOTIFICATIONS + +- (void)dealloc { +#if GTM_GOOGLE_SEARCH_SUPPORTS_DISTRIBUTED_NOTIFICATIONS + [[NSDistributedNotificationCenter defaultCenter] removeObject:self]; +#endif // GTM_GOOGLE_SEARCH_SUPPORTS_DISTRIBUTED_NOTIFICATIONS + [allAppsCachedDomain_ release]; + [allAppsCachedLanguage_ release]; + [curAppCachedDomain_ release]; + [curAppCachedLanguage_ release]; + [globalSearchArguments_ release]; + [super dealloc]; +} + +- (void)preferredDomain:(NSString **)domain + language:(NSString**)language + areCurrentAppOnly:(BOOL*)currentAppOnly { + BOOL localCurrentAppOnly = YES; + NSString *localDomain = curAppCachedDomain_; + NSString *localLanguage = curAppCachedLanguage_; + + // if either one wasn't there, drop both, and use any app if we can + if (!localDomain || !localLanguage) { + localCurrentAppOnly = NO; + localDomain = allAppsCachedDomain_; + localLanguage = allAppsCachedLanguage_; + + // if we didn't get anything from the prefs, go with the defaults + if (!localDomain || !localLanguage) { + // if either one wasn't there, drop both, and use defaults + [self defaultDomain:&localDomain language:&localLanguage]; + } + } + if (!localDomain || !localLanguage) { + _GTMDevLog(@"GTMGoogleSearch: Failed to get the preferred domain/language " + @"from prefs or defaults"); + } + if (language) { + *language = [[localLanguage retain] autorelease]; + } + if (domain) { + *domain = [[localDomain retain] autorelease]; + } + if (currentAppOnly) { + *currentAppOnly = localCurrentAppOnly; + } +} + +- (void)updatePreferredDomain:(NSString*)domain + language:(NSString*)language + currentApplicationOnly:(BOOL)currentAppOnly { + // valid inputs? + if (!domain || ![domain length] || !language) { + return; + } + + if (currentAppOnly) { + // if they are the same, don't do anything + if ((domain == nil && curAppCachedDomain_ == nil && + language == nil && curAppCachedLanguage_ == nil) || + ([domain isEqualToString:curAppCachedDomain_] && + [language isEqualToString:curAppCachedLanguage_])) { + return; + } + + // save them out + CFPreferencesSetValue(kPreferredDomainPrefKey, + (CFStringRef)domain, + kCFPreferencesCurrentApplication, + kCFPreferencesCurrentUser, + kCFPreferencesAnyHost); + CFPreferencesSetValue(kPreferredLanguagePrefKey, + (CFStringRef)language, + kCFPreferencesCurrentApplication, + kCFPreferencesCurrentUser, + kCFPreferencesAnyHost); + CFPreferencesSynchronize(kCFPreferencesCurrentApplication, + kCFPreferencesCurrentUser, + kCFPreferencesAnyHost); + // update our locals + [curAppCachedDomain_ release]; + [curAppCachedLanguage_ release]; + curAppCachedDomain_ = [domain copy]; + curAppCachedLanguage_ = [language copy]; + } else { + // Set the "any application" values + [self updateAllAppsDomain:domain language:language]; + + // Clear the current application values (if there were any) + [self clearPreferredDomainAndLanguageForCurrentApplication]; + } +} + +- (void)clearPreferredDomainAndLanguageForCurrentApplication { + // flush what's in the file + CFPreferencesSetValue(kPreferredDomainPrefKey, + NULL, + kCFPreferencesCurrentApplication, + kCFPreferencesCurrentUser, + kCFPreferencesAnyHost); + CFPreferencesSetValue(kPreferredLanguagePrefKey, + NULL, + kCFPreferencesCurrentApplication, + kCFPreferencesCurrentUser, + kCFPreferencesAnyHost); + CFPreferencesSynchronize(kCFPreferencesCurrentApplication, + kCFPreferencesCurrentUser, + kCFPreferencesAnyHost); + // clear our locals + [curAppCachedDomain_ release]; + [curAppCachedLanguage_ release]; + curAppCachedDomain_ = nil; + curAppCachedLanguage_ = nil; +} + +- (void)clearPreferredDomainAndLanguageForAllApps { + // nil/nil to clear things out, this will also update our cached values. + [self updateAllAppsDomain:nil language:nil]; +} + +- (NSDictionary *)globalSearchArguments { + return globalSearchArguments_; +} + +- (void)setGlobalSearchArguments:(NSDictionary *)args { + [globalSearchArguments_ autorelease]; + globalSearchArguments_ = [args copy]; +} + +- (NSString*)searchURLFor:(NSString*)queryText + ofType:(NSString*)type + arguments:(NSDictionary *)localArgs { + if (!type) { + return nil; + } + + NSString *language; + NSString *domain; + [self preferredDomain:&domain + language:&language + areCurrentAppOnly:NULL]; + + NSMutableDictionary *args + = [NSMutableDictionary dictionaryWithObjectsAndKeys: + @"UTF-8", @"ie", + @"UTF-8", @"oe", + language, @"hl", + nil]; + if (queryText) { + [args setObject:queryText forKey:@"q"]; + } + + NSDictionary *globalSearchArgs = [self globalSearchArguments]; + if (globalSearchArgs) { + [args addEntriesFromDictionary:globalSearchArgs]; + } + if (localArgs) { + [args addEntriesFromDictionary:localArgs]; + } + + NSMutableArray *clientArgs = [NSMutableArray array]; + NSString *key; + NSNull *nsNull = [NSNull null]; + GTM_FOREACH_KEY(key, args) { + NSString *object = [args objectForKey:key]; + if (![object isEqual:nsNull]) { +#if DEBUG + // In debug we check key and object for things that should be escaped. + // Note that percent is not in there because escaped strings will have + // percents in them + NSCharacterSet *cs = [NSCharacterSet characterSetWithCharactersInString: + @"!*'();:@&=+$,/?#[] "]; + NSRange range = [key rangeOfCharacterFromSet:cs]; + if (range.location != NSNotFound) { + _GTMDevLog(@"Unescaped string %@ in argument pair {%@, %@} in -[%@ %@]", + key, key, object, [self class], NSStringFromSelector(_cmd)); + } + range = [object rangeOfCharacterFromSet:cs]; + if (range.location != NSNotFound) { + _GTMDevLog(@"Unescaped string %@ in argument pair {%@,%@ } in -[%@ %@]", + object, key, object, [self class], + NSStringFromSelector(_cmd)); + } +#endif // DEBUG + NSString *arg = [NSString stringWithFormat:@"%@=%@", key, object]; + [clientArgs addObject:arg]; + } + } + NSString *clientArg = [clientArgs componentsJoinedByString:@"&"]; + NSString *url = [NSString stringWithFormat:SEARCH_URL_TEMPLATE, + domain, type, clientArg]; + return url; +} + +- (BOOL)performQuery:(NSString*)queryText + ofType:(NSString *)type + arguments:(NSDictionary *)localArgs { + BOOL success = NO; + NSString *urlString = [self searchURLFor:queryText + ofType:type + arguments:localArgs]; + if (urlString) { + NSURL *url = [NSURL URLWithString:urlString]; + if (url) { +#if GTM_IPHONE_SDK + success = [[UIApplication sharedApplication] openURL:url]; +#else // GTM_IPHONE_SDK + success = [[NSWorkspace sharedWorkspace] openURL:url]; +#endif // GTM_IPHONE_SDK + } + } + return success; +} + +@end + + +@implementation GTMGoogleSearch (PrivateMethods) + +- (void)defaultDomain:(NSString**)preferredDomain + language:(NSString**)preferredLanguage { + // must have both + if (!preferredDomain || !preferredLanguage) { + return; + } + + // make sure they are clear to start + *preferredDomain = nil; + *preferredLanguage = nil; + + // loop over their language list trying to find something we have in + // out default table. + + NSUserDefaults* defs = [NSUserDefaults standardUserDefaults]; + NSArray* languages = [defs objectForKey:@"AppleLanguages"]; + // the current locale is only based on what languages the running apps is + // localized to, so we stick that at the end in case we weren't able to + // find anything else as a match, we'll match that. + languages = + [languages arrayByAddingObject:[[NSLocale currentLocale] localeIdentifier]]; + + NSEnumerator *enumerator = [languages objectEnumerator]; + NSString *localeIdentifier; + while ((localeIdentifier = [enumerator nextObject])) { + NSDictionary *localeParts + = [NSLocale componentsFromLocaleIdentifier:localeIdentifier]; + NSString *localeLanguage = [localeParts objectForKey:NSLocaleLanguageCode]; + // we don't use NSLocaleScriptCode for now + NSString *localeCountry = [localeParts objectForKey:NSLocaleCountryCode]; + + LanguageDefaultInfo *scan = kLanguageListDefaultMappingTable; + LanguageDefaultInfo *end = (scan + (sizeof(kLanguageListDefaultMappingTable) + / sizeof(LanguageDefaultInfo))); + // find a match + // check language, and if country is not nil, check that + for ( ; scan < end ; ++scan) { + if ([localeLanguage isEqualToString:scan->language] && + (!(scan->country) || [localeCountry isEqualToString:scan->country])) { + *preferredDomain = scan->defaultDomain; + *preferredLanguage = localeLanguage; + return; // out of here + } + } + } + + *preferredDomain = kDefaultDomain; + *preferredLanguage = kDefaultLanguage; +} + +// -reloadAllAppCachedValues: +// +- (void)reloadAllAppCachedValues:(NSNotification*)notification { + // drop the old... + [allAppsCachedDomain_ release]; + [allAppsCachedLanguage_ release]; + allAppsCachedDomain_ = nil; + allAppsCachedLanguage_ = nil; + + // load the new + CFPreferencesSynchronize(kAllAppsBuildIdentifier, + kCFPreferencesCurrentUser, + kCFPreferencesAnyHost); + CFStringRef domain = CFPreferencesCopyValue(kPreferredDomainPrefKey, + kAllAppsBuildIdentifier, + kCFPreferencesCurrentUser, + kCFPreferencesAnyHost); + CFStringRef lang = CFPreferencesCopyValue(kPreferredLanguagePrefKey, + kAllAppsBuildIdentifier, + kCFPreferencesCurrentUser, + kCFPreferencesAnyHost); + + // make sure we got values for both and domain is not empty + if (domain && CFStringGetLength(domain) == 0) { + CFRelease(domain); + domain = nil; + if (lang) { + CFRelease(lang); + lang = nil; + } + } + + allAppsCachedDomain_ = GTMNSMakeCollectable(domain); + allAppsCachedLanguage_ = GTMNSMakeCollectable(lang); +} + +// -updateAllAppsDomain:language: +// +- (void)updateAllAppsDomain:(NSString*)domain language:(NSString*)language { + // domain and language can be nil to clear the values + + // if they are the same, don't do anything + if ((domain == nil && allAppsCachedDomain_ == nil && + language == nil && allAppsCachedLanguage_ == nil) || + ([domain isEqualToString:allAppsCachedDomain_] && + [language isEqualToString:allAppsCachedLanguage_])) { + return; + } + + // write it to the file + CFPreferencesSetValue(kPreferredDomainPrefKey, + (CFStringRef)domain, + kAllAppsBuildIdentifier, + kCFPreferencesCurrentUser, + kCFPreferencesAnyHost); + CFPreferencesSetValue(kPreferredLanguagePrefKey, + (CFStringRef)language, + kAllAppsBuildIdentifier, + kCFPreferencesCurrentUser, + kCFPreferencesAnyHost); + CFPreferencesSynchronize(kAllAppsBuildIdentifier, + kCFPreferencesCurrentUser, + kCFPreferencesAnyHost); + + // update our values + [allAppsCachedDomain_ release]; + [allAppsCachedLanguage_ release]; + allAppsCachedDomain_ = [domain copy]; + allAppsCachedLanguage_ = [language copy]; + +#if GTM_GOOGLE_SEARCH_SUPPORTS_DISTRIBUTED_NOTIFICATIONS + // NOTE: we'll go ahead and reload when this comes back to ourselves since + // there is a race here if two folks wrote at about the same time. + NSDistributedNotificationCenter *distCenter = + [NSDistributedNotificationCenter defaultCenter]; + [distCenter postNotificationName:kNotificationName + object:nil + userInfo:nil]; +#endif // GTM_GOOGLE_SEARCH_SUPPORTS_DISTRIBUTED_NOTIFICATIONS +} + +@end diff --git a/AppKit/GTMGoogleSearchTest.m b/AppKit/GTMGoogleSearchTest.m new file mode 100644 index 0000000..45f7e66 --- /dev/null +++ b/AppKit/GTMGoogleSearchTest.m @@ -0,0 +1,208 @@ +// +// GTMGoogleSearchTest.m +// +// Copyright 2006-2009 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. +// + +#import "GTMGoogleSearch.h" +#import "GTMSenTestCase.h" +#import "GTMUnitTestDevLog.h" +#import + +@interface GTMGoogleSearchTest : GTMTestCase +@end + +@implementation GTMGoogleSearchTest + +- (void)testSearches { + typedef struct { + NSString *type; + NSString *expectedPrefix; + } TestSearchDesc; + static TestSearchDesc testSearches[] = { + { GTMGoogleSearchFroogle, @"http://www.google.xxx/products?" }, + { GTMGoogleSearchGroups, @"http://www.google.xxx/groups?" }, + { GTMGoogleSearchImages, @"http://www.google.xxx/images?"}, + { GTMGoogleSearchLocal, @"http://www.google.xxx/local?"}, + { GTMGoogleSearchNews, @"http://www.google.xxx/news?"}, + { GTMGoogleSearchFinance, @"http://www.google.xxx/finance?"}, + { GTMGoogleSearchBooks, @"http://www.google.xxx/books?"}, + { GTMGoogleSearchWeb, @"http://www.google.xxx/search?"}, + }; + + GTMGoogleSearch *googleSearch = [GTMGoogleSearch sharedInstance]; + STAssertNotNil(googleSearch, nil); + + // force the current app values so we aren't at the mercy of the + // global setting the users locale. + [googleSearch updatePreferredDomain:@"xxx" + language:@"yyy" + currentApplicationOnly:TRUE]; + + size_t count = sizeof(testSearches) / sizeof(testSearches[0]); + NSDictionary *globalArgs + = [NSDictionary dictionaryWithObject:@"f" forKey:@"foo"]; + [googleSearch setGlobalSearchArguments:globalArgs]; + NSDictionary *args = [NSDictionary dictionaryWithObject:@"Baba" + forKey:@"BaR"]; + NSString *expectedStrings[] = { + @"oe=UTF-8", @"hl=yyy", @"q=Foobar", + @"foo=f", @"ie=UTF-8", @"BaR=Baba" + }; + for (size_t i = 0; i < count; i++) { + // test building the url + NSString *urlString = [googleSearch searchURLFor:@"Foobar" + ofType:testSearches[i].type + arguments:args]; + STAssertTrue([urlString hasPrefix:testSearches[i].expectedPrefix], + @"Bad URL? URL:%@ Expected Prefix:%@", + urlString, testSearches[i].expectedPrefix); + for (size_t j = 0; + j < sizeof(expectedStrings) / sizeof(expectedStrings[0]); + ++j) { + STAssertGreaterThan([urlString rangeOfString:expectedStrings[j]].length, + (NSUInteger)0, @"URL: %@ expectedString: %@", + urlString, expectedStrings[j]); + } + } + + // clear what we just set for this test + [googleSearch setGlobalSearchArguments:nil]; + [googleSearch clearPreferredDomainAndLanguageForCurrentApplication]; +} + +- (void)testBadInputs { + GTMGoogleSearch *googleSearch = [GTMGoogleSearch sharedInstance]; + STAssertNotNil(googleSearch, nil); + NSDictionary *args = [NSDictionary dictionaryWithObject:@"Ba!ba" + forKey:@"Ba=R"]; + [GTMUnitTestDevLogDebug expectString: + @"Unescaped string Foo bar in argument pair {q,Foo bar } " + @"in -[GTMGoogleSearch searchURLFor:ofType:arguments:]"]; + [GTMUnitTestDevLogDebug expectString: + @"Unescaped string Ba=R in argument pair {Ba=R, Ba!ba} " + @"in -[GTMGoogleSearch searchURLFor:ofType:arguments:]"]; + [GTMUnitTestDevLogDebug expectString: + @"Unescaped string Ba!ba in argument pair {Ba=R,Ba!ba } " + @"in -[GTMGoogleSearch searchURLFor:ofType:arguments:]"]; + NSString *urlString = [googleSearch searchURLFor:@"Foo bar" + ofType:GTMGoogleSearchFroogle + arguments:args]; + STAssertNotNil(urlString, nil); +} + +- (void)testPreferredDefaults { + GTMGoogleSearch *googleSearch = [GTMGoogleSearch sharedInstance]; + STAssertNotNil(googleSearch, nil); + + // hey, we're a unit test, so start by blowing away what we have at the + // app level. + [googleSearch clearPreferredDomainAndLanguageForCurrentApplication]; + + // in theory, we could fetch now and save off what we get to reset at the + // end of this, but we can't tell if that was an "all apps" setting, or if + // it was the default, so...hey, we're a unit test, we'll just stomp what's + // there and clear it out when done... + [googleSearch clearPreferredDomainAndLanguageForAllApps]; + + // make sure the individual accessors work... + + // since they system level default can be set by any app, we just have to + // check for non nil here (also the users locale could control what + // we get if nothing is set). + NSString *domain; + NSString *lang; + // now do a detailed check... + BOOL areCurrentAppOnly = YES; + [googleSearch preferredDomain:&domain + language:&lang + areCurrentAppOnly:&areCurrentAppOnly]; + // should get something for defaults... + STAssertNotNil(domain, nil); + STAssertNotNil(lang, nil); + STAssertFalse(areCurrentAppOnly, nil); + + // test it for "all apps"... + [googleSearch updatePreferredDomain:@"domain" + language:@"lang" + currentApplicationOnly:NO]; + [googleSearch preferredDomain:&domain + language:&lang + areCurrentAppOnly:&areCurrentAppOnly]; + STAssertEqualObjects(domain, @"domain", nil); + STAssertEqualObjects(lang, @"lang", nil); + STAssertFalse(areCurrentAppOnly, nil); + + // test it for this app... + [googleSearch updatePreferredDomain:@"domainThisApp" + language:@"langThisApp" + currentApplicationOnly:YES]; + [googleSearch preferredDomain:&domain + language:&lang + areCurrentAppOnly:&areCurrentAppOnly]; + STAssertEqualObjects(domain, @"domainThisApp", nil); + STAssertEqualObjects(lang, @"langThisApp", nil); + STAssertTrue(areCurrentAppOnly, nil); + + // clear what we just set for this app + [googleSearch clearPreferredDomainAndLanguageForCurrentApplication]; + + // should get back what we set for all apps + [googleSearch preferredDomain:&domain + language:&lang + areCurrentAppOnly:&areCurrentAppOnly]; + STAssertEqualObjects(domain, @"domain", nil); + STAssertEqualObjects(lang, @"lang", nil); + STAssertFalse(areCurrentAppOnly, nil); +#if GTM_GOOGLE_SEARCH_SUPPORTS_DISTRIBUTED_NOTIFICATIONS + // We don't test launching other tasks on the phone since this isn't a valid + // case until we can support real multiple tasks on the phone. + + // try changing the value directly in the plist file (as if another app had + // done it) and sending our notification. + [[NSTask launchedTaskWithLaunchPath:@"/usr/bin/defaults" + arguments:[NSArray arrayWithObjects:@"write", + @"com.google.GoogleSearchAllApps", + @"{ \"com.google.PreferredDomain\" = xxx;" + @"\"com.google.PreferredLanguage\" = yyy; }", + nil]] waitUntilExit]; + // Sleep for a moment to let things flush + // (seen rarely as a problem on aharper's machine). + sleep(1); + NSDistributedNotificationCenter *distCenter = + [NSDistributedNotificationCenter defaultCenter]; + [distCenter postNotificationName:@"com.google.GoogleSearchAllApps.prefsWritten" + object:nil + userInfo:nil + options:NSNotificationDeliverImmediately]; + + // Spin the runloop so the notifications fire. + NSRunLoop *currentLoop = [NSRunLoop currentRunLoop]; + [currentLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]]; + // did we get what we expected? + [googleSearch preferredDomain:&domain + language:&lang + areCurrentAppOnly:&areCurrentAppOnly]; + STAssertEqualObjects(domain, @"xxx", nil); + STAssertEqualObjects(lang, @"yyy", nil); + STAssertFalse(areCurrentAppOnly, nil); +#endif // GTM_GOOGLE_SEARCH_SUPPORTS_DISTRIBUTED_NOTIFICATIONS + + // lastly, clean up what we set for all apps to leave the system at the + // default. + [googleSearch clearPreferredDomainAndLanguageForAllApps]; +} + +@end diff --git a/AppKit/GTMHotKeyTextFieldTest.m b/AppKit/GTMHotKeyTextFieldTest.m index b3e7e89..e48ee94 100644 --- a/AppKit/GTMHotKeyTextFieldTest.m +++ b/AppKit/GTMHotKeyTextFieldTest.m @@ -265,7 +265,7 @@ GTMHotKeyTextField *field = [controller_ view]; STAssertNotNil(field, nil); NSString *expectedNumberString = @"Hot key fields don't take numbers."; - [GTMUnitTestDevLog expect:6 casesOfString:expectedNumberString]; + [GTMUnitTestDevLog expect:6 casesOfString:@"%@", expectedNumberString]; [field setDoubleValue:2]; [field setIntValue:-1]; [field setFloatValue:0]; @@ -273,7 +273,7 @@ STAssertEquals([field intValue], 0, nil); STAssertEquals([field floatValue], 0.0f, nil); #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 - [GTMUnitTestDevLog expect:2 casesOfString:expectedNumberString]; + [GTMUnitTestDevLog expect:2 casesOfString:@"%@", expectedNumberString]; [field setIntegerValue:5]; STAssertEquals([field integerValue], (NSInteger)0, nil); #endif @@ -285,14 +285,14 @@ }; for (size_t i = 0; i < sizeof(takeNumberSels) / sizeof(takeNumberSels[0]); ++i) { - [GTMUnitTestDevLog expect:2 casesOfString:expectedNumberString]; + [GTMUnitTestDevLog expect:2 casesOfString:@"%@", expectedNumberString]; [field performSelector:takeNumberSels[i] withObject:self]; [field performSelector:takeNumberSels[i] withObject:nil]; } NSString *expectedStringString = @"Hot key fields want dictionaries, not strings."; - [GTMUnitTestDevLog expect:6 casesOfString:expectedStringString]; + [GTMUnitTestDevLog expect:6 casesOfString:@"%@", expectedStringString]; [field takeStringValueFrom:self]; [field takeStringValueFrom:nil]; [field setStringValue:nil]; diff --git a/AppKit/GTMNSImage+Scaling.h b/AppKit/GTMNSImage+Scaling.h index 7d48577..0f87709 100644 --- a/AppKit/GTMNSImage+Scaling.h +++ b/AppKit/GTMNSImage+Scaling.h @@ -19,7 +19,7 @@ // -#import +#import #import "GTMDefines.h" @interface NSImage (GTMNSImageScaling) @@ -30,7 +30,9 @@ #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_6 // Return the exact or next largest representation for a size // If you are on SnowLeopard use -// -[NSImage bestRepresentationForRect:context:hints:] +// -[NSImage bestRepresentationForRect:context:hints:] +// Also, please see http://openradar.appspot.com/radar?id=394401 +// and read notes in GTMNSImage+ScalingTest.m. Search for "8052200". - (NSImageRep *)gtm_bestRepresentationForSize:(NSSize)size; #endif diff --git a/AppKit/GTMNSImage+ScalingTest.m b/AppKit/GTMNSImage+ScalingTest.m index dddd00f..426e21b 100644 --- a/AppKit/GTMNSImage+ScalingTest.m +++ b/AppKit/GTMNSImage+ScalingTest.m @@ -49,7 +49,7 @@ STAssertNotNil([testImage gtm_representationOfSize:NSMakeSize(32, 32)], nil); NSImage *duplicate = [testImage gtm_duplicateOfSize:NSMakeSize(48, 48)]; - bestRepRect = NSMakeRect(0, 0, 50, 50); + bestRepRect = NSMakeRect(0, 0, 48, 48); #if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6 rep = [duplicate bestRepresentationForRect:bestRepRect context:nil @@ -60,6 +60,24 @@ STAssertTrue(NSEqualSizes([rep size], NSMakeSize(48, 48)), @"Size is %@", NSStringFromSize([rep size])); + // This should IMHO return 48,48 on both 10.6 and 10.5. It makes no sense + // at all that it returns 32,32 on 10_6 when the above code works for 48,48. + // rdar://8052200 "NSImage bestRepresentationForRect:context:hints: doesn't + // return the best rep" + // http://openradar.appspot.com/radar?id=394401 + bestRepRect = NSMakeRect(0, 0, 50, 50); +#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6 + rep = [duplicate bestRepresentationForRect:bestRepRect + context:nil + hints:nil]; + STAssertFalse(NSEqualSizes([rep size], NSMakeSize(48, 48)), + @"Size is %@", NSStringFromSize([rep size])); +#else + rep = [duplicate gtm_bestRepresentationForSize:bestRepRect.size]; + STAssertTrue(NSEqualSizes([rep size], NSMakeSize(48, 48)), + @"Size is %@", NSStringFromSize([rep size])); +#endif // MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6 + } @end diff --git a/AppKit/GTMNSImage+SearchCache.h b/AppKit/GTMNSImage+SearchCache.h index b0999bf..4e3a10d 100644 --- a/AppKit/GTMNSImage+SearchCache.h +++ b/AppKit/GTMNSImage+SearchCache.h @@ -32,6 +32,8 @@ // TODO(alcor): this class should have basic MRU cache // +#import + @interface NSImage (GTMNSImageSearchCache) + (NSImage *)gtm_imageWithPath:(NSString *)path; + (NSImage *)gtm_imageNamed:(NSString *)name; diff --git a/AppKit/TestData/GTMUILocalizerAndLayoutTweakerTest4-0.10_4_SDK.10.6.tiff b/AppKit/TestData/GTMUILocalizerAndLayoutTweakerTest4-0.10_4_SDK.10.6.tiff new file mode 100644 index 0000000..29c9762 Binary files /dev/null and b/AppKit/TestData/GTMUILocalizerAndLayoutTweakerTest4-0.10_4_SDK.10.6.tiff differ diff --git a/AppKit/TestData/GTMUILocalizerAndLayoutTweakerTest4-1.10_4_SDK.10.6.tiff b/AppKit/TestData/GTMUILocalizerAndLayoutTweakerTest4-1.10_4_SDK.10.6.tiff new file mode 100644 index 0000000..eb9815e Binary files /dev/null and b/AppKit/TestData/GTMUILocalizerAndLayoutTweakerTest4-1.10_4_SDK.10.6.tiff differ diff --git a/AppKit/TestData/GTMUILocalizerAndLayoutTweakerTest4-2.10_4_SDK.10.6.tiff b/AppKit/TestData/GTMUILocalizerAndLayoutTweakerTest4-2.10_4_SDK.10.6.tiff new file mode 100644 index 0000000..f677712 Binary files /dev/null and b/AppKit/TestData/GTMUILocalizerAndLayoutTweakerTest4-2.10_4_SDK.10.6.tiff differ -- cgit v1.2.3