| 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 | 
|---|