| OLD | NEW |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "app/l10n_util_mac.h" | 5 #include "app/l10n_util_mac.h" |
| 6 #include "app/resource_bundle.h" | 6 #include "app/resource_bundle.h" |
| 7 #include "base/file_version_info.h" | |
| 8 #include "base/logging.h" | 7 #include "base/logging.h" |
| 9 #include "base/mac_util.h" | 8 #include "base/mac_util.h" |
| 10 #include "base/string_util.h" | 9 #include "base/string_util.h" |
| 11 #include "base/sys_string_conversions.h" | 10 #include "base/sys_string_conversions.h" |
| 12 #import "chrome/app/keystone_glue.h" | 11 #import "chrome/app/keystone_glue.h" |
| 13 #include "chrome/browser/browser_list.h" | 12 #include "chrome/browser/browser_list.h" |
| 14 #import "chrome/browser/cocoa/about_window_controller.h" | 13 #import "chrome/browser/cocoa/about_window_controller.h" |
| 15 #import "chrome/browser/cocoa/background_tile_view.h" | 14 #import "chrome/browser/cocoa/background_tile_view.h" |
| 16 #include "chrome/browser/cocoa/restart_browser.h" | 15 #include "chrome/browser/cocoa/restart_browser.h" |
| 17 #include "grit/chromium_strings.h" | 16 #include "grit/chromium_strings.h" |
| 18 #include "grit/generated_resources.h" | 17 #include "grit/generated_resources.h" |
| 19 #include "grit/theme_resources.h" | 18 #include "grit/theme_resources.h" |
| 20 #include "grit/locale_settings.h" | 19 #include "grit/locale_settings.h" |
| 21 #include "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h" | 20 #include "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h" |
| 22 | 21 |
| 23 NSString* const kUserClosedAboutNotification = | |
| 24 @"kUserClosedAboutNotification"; | |
| 25 | |
| 26 @interface AboutWindowController (Private) | |
| 27 - (KeystoneGlue*)defaultKeystoneGlue; | |
| 28 - (void)startProgressMessageID:(uint32_t)messageID; | |
| 29 - (void)startProgressMessage:(NSString*)message; | |
| 30 - (void)stopProgressMessage:(NSString*)message imageID:(uint32_t)imageID; | |
| 31 @end | |
| 32 | |
| 33 namespace { | 22 namespace { |
| 34 | 23 |
| 35 // Keystone doesn't give us error numbers on some results, so we just make | |
| 36 // our own for reporting in the UI. | |
| 37 const int kUpdateInstallFailed = 128; | |
| 38 const int kUpdateInstallFailedToStart = 129; | |
| 39 | |
| 40 void AttributedStringAppendString(NSMutableAttributedString* attr_str, | 24 void AttributedStringAppendString(NSMutableAttributedString* attr_str, |
| 41 NSString* str) { | 25 NSString* str) { |
| 42 // You might think doing [[attr_str mutableString] appendString:str] would | 26 // You might think doing [[attr_str mutableString] appendString:str] would |
| 43 // work, but it causes any trailing style to get extened, meaning as we | 27 // work, but it causes any trailing style to get extened, meaning as we |
| 44 // append links, they grow to include the new text, not what we want. | 28 // append links, they grow to include the new text, not what we want. |
| 45 NSAttributedString* new_attr_str = | 29 NSAttributedString* new_attr_str = |
| 46 [[[NSAttributedString alloc] initWithString:str] autorelease]; | 30 [[[NSAttributedString alloc] initWithString:str] autorelease]; |
| 47 [attr_str appendAttributedString:new_attr_str]; | 31 [attr_str appendAttributedString:new_attr_str]; |
| 48 } | 32 } |
| 49 | 33 |
| 50 void AttributedStringAppendHyperlink(NSMutableAttributedString* attr_str, | 34 void AttributedStringAppendHyperlink(NSMutableAttributedString* attr_str, |
| 51 NSString* text, NSString* url_str) { | 35 NSString* text, NSString* url_str) { |
| 52 | |
| 53 // Figure out the range of the text we're adding and add the text. | 36 // Figure out the range of the text we're adding and add the text. |
| 54 NSRange range = NSMakeRange([attr_str length], [text length]); | 37 NSRange range = NSMakeRange([attr_str length], [text length]); |
| 55 AttributedStringAppendString(attr_str, text); | 38 AttributedStringAppendString(attr_str, text); |
| 56 | 39 |
| 57 // Add the link | 40 // Add the link |
| 58 [attr_str addAttribute:NSLinkAttributeName value:url_str range:range]; | 41 [attr_str addAttribute:NSLinkAttributeName value:url_str range:range]; |
| 59 | 42 |
| 60 // Blue and underlined | 43 // Blue and underlined |
| 61 [attr_str addAttribute:NSForegroundColorAttributeName | 44 [attr_str addAttribute:NSForegroundColorAttributeName |
| 62 value:[NSColor blueColor] | 45 value:[NSColor blueColor] |
| 63 range:range]; | 46 range:range]; |
| 64 [attr_str addAttribute:NSUnderlineStyleAttributeName | 47 [attr_str addAttribute:NSUnderlineStyleAttributeName |
| 65 value:[NSNumber numberWithInt:NSSingleUnderlineStyle] | 48 value:[NSNumber numberWithInt:NSSingleUnderlineStyle] |
| 66 range:range]; | 49 range:range]; |
| 67 [attr_str addAttribute:NSCursorAttributeName | 50 [attr_str addAttribute:NSCursorAttributeName |
| 68 value:[NSCursor pointingHandCursor] | 51 value:[NSCursor pointingHandCursor] |
| 69 range:range]; | 52 range:range]; |
| 70 } | 53 } |
| 71 | 54 |
| 72 } // namespace | 55 } // namespace |
| 73 | 56 |
| 74 NSAttributedString* BuildAboutWindowLegalTextBlock() { | 57 @interface AboutWindowController(Private) |
| 58 |
| 59 // Launches a check for available updates. |
| 60 - (void)checkForUpdate; |
| 61 |
| 62 // Notification callback, called with the status of asynchronous |
| 63 // -checkForUpdate and -updateNow: operations. |
| 64 - (void)updateStatus:(NSNotification*)notification; |
| 65 |
| 66 // These methods maintain the image (or throbber) and text displayed regarding |
| 67 // update status. -setUpdateThrobberMessage: starts a progress throbber and |
| 68 // sets the text. -setUpdateImage:message: displays an image and sets the |
| 69 // text. |
| 70 - (void)setUpdateThrobberMessage:(NSString*)message; |
| 71 - (void)setUpdateImage:(int)imageID message:(NSString*)message; |
| 72 |
| 73 @end // @interface AboutWindowController(Private) |
| 74 |
| 75 const NSString* const kUserClosedAboutNotification = |
| 76 @"UserClosedAboutNotification"; |
| 77 |
| 78 @implementation AboutWindowController |
| 79 |
| 80 - (id)initWithProfile:(Profile*)profile { |
| 81 NSString* nibPath = [mac_util::MainAppBundle() pathForResource:@"About" |
| 82 ofType:@"nib"]; |
| 83 if ((self = [super initWithWindowNibPath:nibPath owner:self])) { |
| 84 profile_ = profile; |
| 85 NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; |
| 86 [center addObserver:self |
| 87 selector:@selector(updateStatus:) |
| 88 name:kAutoupdateStatusNotification |
| 89 object:nil]; |
| 90 } |
| 91 return self; |
| 92 } |
| 93 |
| 94 - (void)dealloc { |
| 95 [[NSNotificationCenter defaultCenter] removeObserver:self]; |
| 96 [super dealloc]; |
| 97 } |
| 98 |
| 99 - (void)awakeFromNib { |
| 100 NSBundle* bundle = mac_util::MainAppBundle(); |
| 101 NSString* chromeVersion = |
| 102 [bundle objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; |
| 103 |
| 104 #if defined(GOOGLE_CHROME_BUILD) |
| 105 NSString* version = chromeVersion; |
| 106 #else // GOOGLE_CHROME_BUILD |
| 107 // The format string is not localized, but this is how the displayed version |
| 108 // is built on Windows too. |
| 109 NSString* svnRevision = [bundle objectForInfoDictionaryKey:@"SVNRevision"]; |
| 110 NSString* version = |
| 111 [NSString stringWithFormat:@"%@ (%@)", chromeVersion, svnRevision]; |
| 112 #endif // GOOGLE_CHROME_BUILD |
| 113 |
| 114 [version_ setStringValue:version]; |
| 115 |
| 116 // Put the two images into the UI. |
| 117 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); |
| 118 NSImage* backgroundImage = rb.GetNSImageNamed(IDR_ABOUT_BACKGROUND_COLOR); |
| 119 DCHECK(backgroundImage); |
| 120 [backgroundView_ setTileImage:backgroundImage]; |
| 121 NSImage* logoImage = rb.GetNSImageNamed(IDR_ABOUT_BACKGROUND); |
| 122 DCHECK(logoImage); |
| 123 [logoView_ setImage:logoImage]; |
| 124 |
| 125 [[legalText_ textStorage] setAttributedString:[[self class] legalTextBlock]]; |
| 126 |
| 127 // Resize our text view now so that the |updateShift| below is set |
| 128 // correctly. The About box has its controls manually positioned, so we need |
| 129 // to calculate how much larger (or smaller) our text box is and store that |
| 130 // difference in |legalShift|. We do something similar with |updateShift| |
| 131 // below, which is either 0, or the amount of space to offset the window size |
| 132 // because the view that contains the update button has been removed because |
| 133 // this build doesn't have KeyStone. |
| 134 NSRect oldLegalRect = [legalBlock_ frame]; |
| 135 [legalText_ sizeToFit]; |
| 136 NSRect newRect = oldLegalRect; |
| 137 newRect.size.height = [legalText_ frame].size.height; |
| 138 [legalBlock_ setFrame:newRect]; |
| 139 CGFloat legalShift = newRect.size.height - oldLegalRect.size.height; |
| 140 |
| 141 KeystoneGlue* keystoneGlue = [KeystoneGlue defaultKeystoneGlue]; |
| 142 CGFloat updateShift; |
| 143 if (keystoneGlue) { |
| 144 NSNotification* recentNotification = [keystoneGlue recentNotification]; |
| 145 NSDictionary* recentDictionary = [recentNotification userInfo]; |
| 146 AutoupdateStatus recentStatus = static_cast<AutoupdateStatus>( |
| 147 [[recentDictionary objectForKey:kAutoupdateStatusStatus] intValue]); |
| 148 if (recentStatus == kAutoupdateInstallFailed) { |
| 149 // A previous update attempt was unsuccessful, but no About box was |
| 150 // around to report status. Use the saved notification to set up the |
| 151 // About box with the error message, and to allow another chance to |
| 152 // install the update. |
| 153 [self updateStatus:recentNotification]; |
| 154 } else { |
| 155 [self checkForUpdate]; |
| 156 } |
| 157 |
| 158 updateShift = 0.0; |
| 159 } else { |
| 160 // Hide all the update UI |
| 161 [updateBlock_ setHidden:YES]; |
| 162 |
| 163 // Figure out the amount being removed by taking out the update block |
| 164 // and its spacing. |
| 165 updateShift = NSMinY([legalBlock_ frame]) - NSMinY([updateBlock_ frame]); |
| 166 |
| 167 NSRect legalFrame = [legalBlock_ frame]; |
| 168 legalFrame.origin.y -= updateShift; |
| 169 [legalBlock_ setFrame:legalFrame]; |
| 170 } |
| 171 |
| 172 NSRect backgroundFrame = [backgroundView_ frame]; |
| 173 backgroundFrame.origin.y += legalShift - updateShift; |
| 174 [backgroundView_ setFrame:backgroundFrame]; |
| 175 |
| 176 NSSize windowDelta = NSMakeSize(0.0, legalShift - updateShift); |
| 177 |
| 178 [GTMUILocalizerAndLayoutTweaker |
| 179 resizeWindowWithoutAutoResizingSubViews:[self window] |
| 180 delta:windowDelta]; |
| 181 } |
| 182 |
| 183 - (void)windowWillClose:(NSNotification*)notification { |
| 184 NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; |
| 185 [center postNotificationName:kUserClosedAboutNotification object:self]; |
| 186 } |
| 187 |
| 188 - (void)setUpdateThrobberMessage:(NSString*)message { |
| 189 [updateStatusIndicator_ setHidden:YES]; |
| 190 |
| 191 [spinner_ setHidden:NO]; |
| 192 [spinner_ startAnimation:self]; |
| 193 |
| 194 [updateText_ setStringValue:message]; |
| 195 } |
| 196 |
| 197 - (void)setUpdateImage:(int)imageID message:(NSString*)message { |
| 198 [spinner_ stopAnimation:self]; |
| 199 [spinner_ setHidden:YES]; |
| 200 |
| 201 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); |
| 202 NSImage* statusImage = rb.GetNSImageNamed(imageID); |
| 203 DCHECK(statusImage); |
| 204 [updateStatusIndicator_ setImage:statusImage]; |
| 205 [updateStatusIndicator_ setHidden:NO]; |
| 206 |
| 207 [updateText_ setStringValue:message]; |
| 208 } |
| 209 |
| 210 - (void)checkForUpdate { |
| 211 [self setUpdateThrobberMessage: |
| 212 l10n_util::GetNSStringWithFixup(IDS_UPGRADE_CHECK_STARTED)]; |
| 213 [[KeystoneGlue defaultKeystoneGlue] checkForUpdate]; |
| 214 |
| 215 // Upon completion, kAutoupdateStatusNotification will be posted, and |
| 216 // -updateStatus: will be called. |
| 217 } |
| 218 |
| 219 - (IBAction)updateNow:(id)sender { |
| 220 updateTriggered_ = YES; |
| 221 |
| 222 // Don't let someone click "Update Now" twice! |
| 223 [updateNowButton_ setEnabled:NO]; |
| 224 [self setUpdateThrobberMessage: |
| 225 l10n_util::GetNSStringWithFixup(IDS_UPGRADE_STARTED)]; |
| 226 [[KeystoneGlue defaultKeystoneGlue] installUpdate]; |
| 227 |
| 228 // Upon completion, kAutoupdateStatusNotification will be posted, and |
| 229 // -updateStatus: will be called. |
| 230 } |
| 231 |
| 232 - (void)updateStatus:(NSNotification*)notification { |
| 233 NSDictionary* dictionary = [notification userInfo]; |
| 234 AutoupdateStatus status = static_cast<AutoupdateStatus>( |
| 235 [[dictionary objectForKey:kAutoupdateStatusStatus] intValue]); |
| 236 |
| 237 // Don't assume |version| is a real string. It may be nil. |
| 238 NSString* version = [dictionary objectForKey:kAutoupdateStatusVersion]; |
| 239 |
| 240 int imageID; |
| 241 NSString* message; |
| 242 |
| 243 switch (status) { |
| 244 case kAutoupdateCurrent: |
| 245 imageID = IDR_UPDATE_UPTODATE; |
| 246 message = l10n_util::GetNSStringFWithFixup( |
| 247 IDS_UPGRADE_ALREADY_UP_TO_DATE, |
| 248 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME), |
| 249 base::SysNSStringToUTF16(version)); |
| 250 |
| 251 break; |
| 252 |
| 253 case kAutoupdateAvailable: |
| 254 imageID = IDR_UPDATE_AVAILABLE; |
| 255 message = l10n_util::GetNSStringFWithFixup( |
| 256 IDS_UPGRADE_AVAILABLE, l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)); |
| 257 [updateNowButton_ setEnabled:YES]; |
| 258 |
| 259 break; |
| 260 |
| 261 case kAutoupdateInstalled: |
| 262 { |
| 263 imageID = IDR_UPDATE_UPTODATE; |
| 264 string16 productName = l10n_util::GetStringUTF16(IDS_PRODUCT_NAME); |
| 265 if (version) { |
| 266 message = l10n_util::GetNSStringFWithFixup( |
| 267 IDS_UPGRADE_SUCCESSFUL, |
| 268 productName, |
| 269 base::SysNSStringToUTF16(version)); |
| 270 } else { |
| 271 message = l10n_util::GetNSStringFWithFixup( |
| 272 IDS_UPGRADE_SUCCESSFUL_NOVERSION, productName); |
| 273 } |
| 274 |
| 275 // TODO(mark): Turn the button in the dialog into a restart button |
| 276 // instead of springing this sheet or dialog. |
| 277 NSWindow* window = [self window]; |
| 278 NSWindow* restartDialogParent = [window isVisible] ? window : nil; |
| 279 restart_browser::RequestRestart(restartDialogParent); |
| 280 } |
| 281 |
| 282 break; |
| 283 |
| 284 case kAutoupdateInstallFailed: |
| 285 // Allow another chance. |
| 286 [updateNowButton_ setEnabled:YES]; |
| 287 |
| 288 // Fall through. |
| 289 |
| 290 case kAutoupdateCheckFailed: |
| 291 // TODO(mark): Keystone doesn't currently indicate when a check for |
| 292 // updates failed. Fix that. |
| 293 imageID = IDR_UPDATE_FAIL; |
| 294 message = l10n_util::GetNSStringFWithFixup(IDS_UPGRADE_ERROR, |
| 295 IntToString16(status)); |
| 296 |
| 297 break; |
| 298 |
| 299 default: |
| 300 NOTREACHED(); |
| 301 return; |
| 302 } |
| 303 |
| 304 [self setUpdateImage:imageID message:message]; |
| 305 |
| 306 // Since the update status is now displayed in an About box, the saved state |
| 307 // can be cleared. If the About box is closed and then reopened, this will |
| 308 // let it start out with a clean slate and not be affected by past failures. |
| 309 [[KeystoneGlue defaultKeystoneGlue] clearRecentNotification]; |
| 310 } |
| 311 |
| 312 - (BOOL)textView:(NSTextView *)aTextView |
| 313 clickedOnLink:(id)link |
| 314 atIndex:(NSUInteger)charIndex { |
| 315 // We always create a new window, so there's no need to try to re-use |
| 316 // an existing one just to pass in the NEW_WINDOW disposition. |
| 317 Browser* browser = Browser::Create(profile_); |
| 318 if (browser) { |
| 319 browser->OpenURL(GURL([link UTF8String]), GURL(), NEW_WINDOW, |
| 320 PageTransition::LINK); |
| 321 } |
| 322 return YES; |
| 323 } |
| 324 |
| 325 - (NSTextView*)legalText { |
| 326 return legalText_; |
| 327 } |
| 328 |
| 329 - (NSButton*)updateButton { |
| 330 return updateNowButton_; |
| 331 } |
| 332 |
| 333 - (NSTextField*)updateText { |
| 334 return updateText_; |
| 335 } |
| 336 |
| 337 + (NSAttributedString*)legalTextBlock { |
| 75 // Windows builds this up in a very complex way, we're just trying to model | 338 // Windows builds this up in a very complex way, we're just trying to model |
| 76 // it the best we can to get all the information in (they actually do it | 339 // it the best we can to get all the information in (they actually do it |
| 77 // but created Labels and Links that they carefully place to make it appear | 340 // but created Labels and Links that they carefully place to make it appear |
| 78 // to be a paragraph of text). | 341 // to be a paragraph of text). |
| 79 // src/chrome/browser/views/about_chrome_view.cc AboutChromeView::Init() | 342 // src/chrome/browser/views/about_chrome_view.cc AboutChromeView::Init() |
| 80 | 343 |
| 81 NSMutableAttributedString* legal_block = | 344 NSMutableAttributedString* legal_block = |
| 82 [[[NSMutableAttributedString alloc] init] autorelease]; | 345 [[[NSMutableAttributedString alloc] init] autorelease]; |
| 83 [legal_block beginEditing]; | 346 [legal_block beginEditing]; |
| 84 | 347 |
| 85 NSString* copyright = l10n_util::GetNSString(IDS_ABOUT_VERSION_COPYRIGHT); | 348 NSString* copyright = |
| 349 l10n_util::GetNSStringWithFixup(IDS_ABOUT_VERSION_COPYRIGHT); |
| 86 AttributedStringAppendString(legal_block, copyright); | 350 AttributedStringAppendString(legal_block, copyright); |
| 87 | 351 |
| 88 // These are the markers directly in IDS_ABOUT_VERSION_LICENSE | 352 // These are the markers directly in IDS_ABOUT_VERSION_LICENSE |
| 89 NSString* kBeginLinkChr = @"BEGIN_LINK_CHR"; | 353 NSString* kBeginLinkChr = @"BEGIN_LINK_CHR"; |
| 90 NSString* kBeginLinkOss = @"BEGIN_LINK_OSS"; | 354 NSString* kBeginLinkOss = @"BEGIN_LINK_OSS"; |
| 91 NSString* kEndLinkChr = @"END_LINK_CHR"; | 355 NSString* kEndLinkChr = @"END_LINK_CHR"; |
| 92 NSString* kEndLinkOss = @"END_LINK_OSS"; | 356 NSString* kEndLinkOss = @"END_LINK_OSS"; |
| 93 // The CHR link should go to here | 357 // The CHR link should go to here |
| 94 NSString* kChromiumProject = l10n_util::GetNSString(IDS_CHROMIUM_PROJECT_URL); | 358 NSString* kChromiumProject = l10n_util::GetNSString(IDS_CHROMIUM_PROJECT_URL); |
| 95 // The OSS link should go to here | 359 // The OSS link should go to here |
| 96 NSString* kAcknowledgements = @"about:credits"; | 360 NSString* kAcknowledgements = @"about:credits"; |
| 97 | 361 |
| 98 // Now fetch the license string and deal with the markers | 362 // Now fetch the license string and deal with the markers |
| 99 | 363 |
| 100 NSString* license = l10n_util::GetNSString(IDS_ABOUT_VERSION_LICENSE); | 364 NSString* license = |
| 365 l10n_util::GetNSStringWithFixup(IDS_ABOUT_VERSION_LICENSE); |
| 101 | 366 |
| 102 NSRange begin_chr = [license rangeOfString:kBeginLinkChr]; | 367 NSRange begin_chr = [license rangeOfString:kBeginLinkChr]; |
| 103 NSRange begin_oss = [license rangeOfString:kBeginLinkOss]; | 368 NSRange begin_oss = [license rangeOfString:kBeginLinkOss]; |
| 104 NSRange end_chr = [license rangeOfString:kEndLinkChr]; | 369 NSRange end_chr = [license rangeOfString:kEndLinkChr]; |
| 105 NSRange end_oss = [license rangeOfString:kEndLinkOss]; | 370 NSRange end_oss = [license rangeOfString:kEndLinkOss]; |
| 106 DCHECK(begin_chr.location != NSNotFound); | 371 DCHECK(begin_chr.location != NSNotFound); |
| 107 DCHECK(begin_oss.location != NSNotFound); | 372 DCHECK(begin_oss.location != NSNotFound); |
| 108 DCHECK(end_chr.location != NSNotFound); | 373 DCHECK(end_chr.location != NSNotFound); |
| 109 DCHECK(end_oss.location != NSNotFound); | 374 DCHECK(end_oss.location != NSNotFound); |
| 110 | 375 |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 159 NSString* kTOS = @"about:terms"; | 424 NSString* kTOS = @"about:terms"; |
| 160 // Following Window. There is one marker in the string for where the terms | 425 // Following Window. There is one marker in the string for where the terms |
| 161 // link goes, but the text of the link comes from a second string resources. | 426 // link goes, but the text of the link comes from a second string resources. |
| 162 std::vector<size_t> url_offsets; | 427 std::vector<size_t> url_offsets; |
| 163 std::wstring w_about_terms = l10n_util::GetStringF(IDS_ABOUT_TERMS_OF_SERVICE, | 428 std::wstring w_about_terms = l10n_util::GetStringF(IDS_ABOUT_TERMS_OF_SERVICE, |
| 164 std::wstring(), | 429 std::wstring(), |
| 165 std::wstring(), | 430 std::wstring(), |
| 166 &url_offsets); | 431 &url_offsets); |
| 167 DCHECK_EQ(url_offsets.size(), 1U); | 432 DCHECK_EQ(url_offsets.size(), 1U); |
| 168 NSString* about_terms = base::SysWideToNSString(w_about_terms); | 433 NSString* about_terms = base::SysWideToNSString(w_about_terms); |
| 169 NSString* terms_link_text = l10n_util::GetNSString(IDS_TERMS_OF_SERVICE); | 434 NSString* terms_link_text = |
| 435 l10n_util::GetNSStringWithFixup(IDS_TERMS_OF_SERVICE); |
| 170 | 436 |
| 171 AttributedStringAppendString(legal_block, @"\n\n"); | 437 AttributedStringAppendString(legal_block, @"\n\n"); |
| 172 sub_str = [about_terms substringToIndex:url_offsets[0]]; | 438 sub_str = [about_terms substringToIndex:url_offsets[0]]; |
| 173 AttributedStringAppendString(legal_block, sub_str); | 439 AttributedStringAppendString(legal_block, sub_str); |
| 174 AttributedStringAppendHyperlink(legal_block, terms_link_text, kTOS); | 440 AttributedStringAppendHyperlink(legal_block, terms_link_text, kTOS); |
| 175 sub_str = [about_terms substringFromIndex:url_offsets[0]]; | 441 sub_str = [about_terms substringFromIndex:url_offsets[0]]; |
| 176 AttributedStringAppendString(legal_block, sub_str); | 442 AttributedStringAppendString(legal_block, sub_str); |
| 177 #endif // defined(GOOGLE_CHROME_BUILD) | 443 #endif // GOOGLE_CHROME_BUILD |
| 178 | 444 |
| 179 // We need to explicitly select Lucida Grande because once we click on | 445 // We need to explicitly select Lucida Grande because once we click on |
| 180 // the NSTextView, it changes to Helvetica 12 otherwise. | 446 // the NSTextView, it changes to Helvetica 12 otherwise. |
| 181 NSRange string_range = NSMakeRange(0, [legal_block length]); | 447 NSRange string_range = NSMakeRange(0, [legal_block length]); |
| 182 [legal_block addAttribute:NSFontAttributeName | 448 [legal_block addAttribute:NSFontAttributeName |
| 183 value:[NSFont labelFontOfSize:11] | 449 value:[NSFont labelFontOfSize:11] |
| 184 range:string_range]; | 450 range:string_range]; |
| 185 | 451 |
| 186 [legal_block endEditing]; | 452 [legal_block endEditing]; |
| 187 return legal_block; | 453 return legal_block; |
| 188 } | 454 } |
| 189 | 455 |
| 190 @implementation AboutWindowController | 456 @end // @implementation AboutWindowController |
| 191 | |
| 192 - (id)initWithProfile:(Profile*)profile { | |
| 193 NSString* nibpath = [mac_util::MainAppBundle() pathForResource:@"About" | |
| 194 ofType:@"nib"]; | |
| 195 self = [super initWithWindowNibPath:nibpath owner:self]; | |
| 196 if (self) { | |
| 197 profile_ = profile; | |
| 198 } | |
| 199 return self; | |
| 200 } | |
| 201 | |
| 202 - (void)awakeFromNib { | |
| 203 // Set our current version. | |
| 204 scoped_ptr<FileVersionInfo> version_info( | |
| 205 FileVersionInfo::CreateFileVersionInfoForCurrentModule()); | |
| 206 std::wstring version(version_info->product_version()); | |
| 207 #if !defined(GOOGLE_CHROME_BUILD) | |
| 208 // Yes, Windows does this raw since it is only in Chromium builds | |
| 209 // src/chrome/browser/views/about_chrome_view.cc AboutChromeView::Init() | |
| 210 version += L" ("; | |
| 211 version += version_info->last_change(); | |
| 212 version += L")"; | |
| 213 #endif | |
| 214 NSString* nsversion = base::SysWideToNSString(version); | |
| 215 [version_ setStringValue:nsversion]; | |
| 216 | |
| 217 // Put the two images into the ui | |
| 218 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
| 219 NSImage* backgroundImage = rb.GetNSImageNamed(IDR_ABOUT_BACKGROUND_COLOR); | |
| 220 DCHECK(backgroundImage); | |
| 221 [backgroundView_ setTileImage:backgroundImage]; | |
| 222 NSImage* logoImage = rb.GetNSImageNamed(IDR_ABOUT_BACKGROUND); | |
| 223 DCHECK(logoImage); | |
| 224 [logoView_ setImage:logoImage]; | |
| 225 | |
| 226 [[legalText_ textStorage] | |
| 227 setAttributedString:BuildAboutWindowLegalTextBlock()]; | |
| 228 | |
| 229 // Resize our text view now so that the |updateShift| below is set | |
| 230 // correctly. The about box has its controls manually positioned, so we need | |
| 231 // to calculate how much larger (or smaller) our text box is and store that | |
| 232 // difference in |legalShift|. We do something similar with |updateShift| | |
| 233 // below, which is either 0, or the amount of space to offset the window size | |
| 234 // because the view that contains the update button has been removed because | |
| 235 // this build doesn't have KeyStone. | |
| 236 NSRect oldLegalRect = [legalBlock_ frame]; | |
| 237 [legalText_ sizeToFit]; | |
| 238 NSRect newRect = oldLegalRect; | |
| 239 newRect.size.height = [legalText_ frame].size.height; | |
| 240 [legalBlock_ setFrame:newRect]; | |
| 241 CGFloat legalShift = newRect.size.height - oldLegalRect.size.height; | |
| 242 | |
| 243 KeystoneGlue* keystone = [self defaultKeystoneGlue]; | |
| 244 CGFloat updateShift = 0.0; | |
| 245 if (keystone) { | |
| 246 // Initiate an update check. | |
| 247 if ([keystone checkForUpdate:self]) { | |
| 248 [self startProgressMessageID:IDS_UPGRADE_CHECK_STARTED]; | |
| 249 } | |
| 250 } else { | |
| 251 // Hide all the update UI | |
| 252 [updateBlock_ setHidden:YES]; | |
| 253 // Figure out the amount we're removing by taking about the update block | |
| 254 // (and it's spacing). | |
| 255 updateShift = NSMinY([legalBlock_ frame]) - NSMinY([updateBlock_ frame]); | |
| 256 } | |
| 257 | |
| 258 // Adjust the sizes/locations. | |
| 259 NSRect rect = [legalBlock_ frame]; | |
| 260 rect.origin.y -= updateShift; | |
| 261 [legalBlock_ setFrame:rect]; | |
| 262 | |
| 263 rect = [backgroundView_ frame]; | |
| 264 rect.origin.y = rect.origin.y - updateShift + legalShift; | |
| 265 [backgroundView_ setFrame:rect]; | |
| 266 | |
| 267 NSSize windowDelta = NSMakeSize(0, (legalShift - updateShift)); | |
| 268 [GTMUILocalizerAndLayoutTweaker | |
| 269 resizeWindowWithoutAutoResizingSubViews:[self window] | |
| 270 delta:windowDelta]; | |
| 271 } | |
| 272 | |
| 273 - (KeystoneGlue*)defaultKeystoneGlue { | |
| 274 return [KeystoneGlue defaultKeystoneGlue]; | |
| 275 } | |
| 276 | |
| 277 - (void)startProgressMessageID:(uint32_t)messageID { | |
| 278 NSString* message = l10n_util::GetNSStringWithFixup(messageID); | |
| 279 [self startProgressMessage:message]; | |
| 280 } | |
| 281 | |
| 282 - (void)startProgressMessage:(NSString*)message { | |
| 283 [updateStatusIndicator_ setHidden:YES]; | |
| 284 [spinner_ setHidden:NO]; | |
| 285 [spinner_ startAnimation:self]; | |
| 286 | |
| 287 [updateText_ setStringValue:message]; | |
| 288 } | |
| 289 | |
| 290 - (void)stopProgressMessage:(NSString*)message imageID:(uint32_t)imageID { | |
| 291 [spinner_ stopAnimation:self]; | |
| 292 [spinner_ setHidden:YES]; | |
| 293 if (imageID) { | |
| 294 [updateStatusIndicator_ setHidden:NO]; | |
| 295 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); | |
| 296 NSImage* statusImage = rb.GetNSImageNamed(imageID); | |
| 297 DCHECK(statusImage); | |
| 298 [updateStatusIndicator_ setImage:statusImage]; | |
| 299 } | |
| 300 | |
| 301 [updateText_ setStringValue:message]; | |
| 302 } | |
| 303 | |
| 304 // Callback from KeystoneGlue; implementation of KeystoneGlueCallbacks protocol. | |
| 305 // Warning: latest version may be nil if not set in server config. | |
| 306 - (void)upToDateCheckCompleted:(BOOL)updatesAvailable | |
| 307 latestVersion:(NSString*)latestVersion { | |
| 308 uint32_t imageID; | |
| 309 NSString* message; | |
| 310 if (updatesAvailable) { | |
| 311 newVersionAvailable_.reset([latestVersion copy]); | |
| 312 | |
| 313 // Window UI doesn't put the version number in the string. | |
| 314 imageID = IDR_UPDATE_AVAILABLE; | |
| 315 message = | |
| 316 l10n_util::GetNSStringF(IDS_UPGRADE_AVAILABLE, | |
| 317 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)); | |
| 318 [updateNowButton_ setEnabled:YES]; | |
| 319 } else { | |
| 320 // NOTE: This is can be a lie, Keystone does not provide us with an error if | |
| 321 // it was not able to reach the server. So we can't completely map to the | |
| 322 // Windows UI. | |
| 323 | |
| 324 // Keystone does not provide the version number when we are up to date so to | |
| 325 // maintain the UI, we just go fetch our version and call it good. | |
| 326 scoped_ptr<FileVersionInfo> version_info( | |
| 327 FileVersionInfo::CreateFileVersionInfoForCurrentModule()); | |
| 328 std::wstring version(version_info->product_version()); | |
| 329 | |
| 330 // TODO: We really should check to see if what is on disk is newer then what | |
| 331 // is running and report it as such. (Windows has some messages that can | |
| 332 // help with this.) http://crbug.com/13165 | |
| 333 | |
| 334 imageID = IDR_UPDATE_UPTODATE; | |
| 335 message = | |
| 336 l10n_util::GetNSStringF(IDS_UPGRADE_ALREADY_UP_TO_DATE, | |
| 337 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME), | |
| 338 WideToUTF16(version)); | |
| 339 } | |
| 340 [self stopProgressMessage:message imageID:imageID]; | |
| 341 } | |
| 342 | |
| 343 - (void)windowWillClose:(NSNotification*)notification { | |
| 344 // If an update has ever been triggered, we force reuse of the same About Box. | |
| 345 // This gives us 2 things: | |
| 346 // 1. If an update is ongoing and the window was closed we would have | |
| 347 // no way of getting status. | |
| 348 // 2. If we have a "Please restart" message we want it to stay there. | |
| 349 if (updateTriggered_) | |
| 350 return; | |
| 351 | |
| 352 [[NSNotificationCenter defaultCenter] | |
| 353 postNotificationName:kUserClosedAboutNotification | |
| 354 object:self]; | |
| 355 } | |
| 356 | |
| 357 // Callback from KeystoneGlue; implementation of KeystoneGlueCallbacks protocol. | |
| 358 - (void)updateCompleted:(BOOL)successful installs:(int)installs { | |
| 359 uint32_t imageID; | |
| 360 NSString* message; | |
| 361 if (successful && installs) { | |
| 362 imageID = IDR_UPDATE_UPTODATE; | |
| 363 if ([newVersionAvailable_.get() length]) { | |
| 364 message = | |
| 365 l10n_util::GetNSStringF(IDS_UPGRADE_SUCCESSFUL, | |
| 366 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME), | |
| 367 base::SysNSStringToUTF16( | |
| 368 newVersionAvailable_.get())); | |
| 369 } else { | |
| 370 message = | |
| 371 l10n_util::GetNSStringF(IDS_UPGRADE_SUCCESSFUL_NOVERSION, | |
| 372 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)); | |
| 373 } | |
| 374 | |
| 375 // Tell the user to restart their browser. | |
| 376 restart_browser::RequestRestart(nil); | |
| 377 | |
| 378 } else { | |
| 379 imageID = IDR_UPDATE_FAIL; | |
| 380 message = | |
| 381 l10n_util::GetNSStringF(IDS_UPGRADE_ERROR, | |
| 382 IntToString16(kUpdateInstallFailed)); | |
| 383 | |
| 384 // Allow a second chance. | |
| 385 [updateNowButton_ setEnabled:YES]; | |
| 386 } | |
| 387 | |
| 388 [self stopProgressMessage:message imageID:imageID]; | |
| 389 } | |
| 390 | |
| 391 - (IBAction)updateNow:(id)sender { | |
| 392 updateTriggered_ = YES; | |
| 393 | |
| 394 // Don't let someone click "Update Now" twice! | |
| 395 [updateNowButton_ setEnabled:NO]; | |
| 396 if ([[self defaultKeystoneGlue] startUpdate:self]) { | |
| 397 // Clear any previous error message from the throbber area. | |
| 398 [self startProgressMessageID:IDS_UPGRADE_STARTED]; | |
| 399 } else { | |
| 400 NSString* message = | |
| 401 l10n_util::GetNSStringF(IDS_UPGRADE_ERROR, | |
| 402 IntToString16(kUpdateInstallFailedToStart)); | |
| 403 [self stopProgressMessage:message imageID:IDR_UPDATE_FAIL]; | |
| 404 } | |
| 405 } | |
| 406 | |
| 407 - (BOOL)textView:(NSTextView *)aTextView | |
| 408 clickedOnLink:(id)link | |
| 409 atIndex:(NSUInteger)charIndex { | |
| 410 // We always create a new window, so there's no need to try to re-use | |
| 411 // an existing one just to pass in the NEW_WINDOW disposition. | |
| 412 Browser* browser = Browser::Create(profile_); | |
| 413 if (browser) | |
| 414 browser->OpenURL(GURL([link UTF8String]), GURL(), NEW_WINDOW, | |
| 415 PageTransition::LINK); | |
| 416 return YES; | |
| 417 } | |
| 418 | |
| 419 - (NSTextView*)legalText { | |
| 420 return legalText_; | |
| 421 } | |
| 422 | |
| 423 - (NSButton*)updateButton { | |
| 424 return updateNowButton_; | |
| 425 } | |
| 426 | |
| 427 - (NSTextField*)updateText { | |
| 428 return updateText_; | |
| 429 } | |
| 430 | |
| 431 @end | |
| 432 | |
| OLD | NEW |