Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(952)

Side by Side Diff: chrome/browser/cocoa/about_window_controller.mm

Issue 338012: About box auto-update improvements (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 11 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
OLDNEW
« no previous file with comments | « chrome/browser/cocoa/about_window_controller.h ('k') | chrome/browser/cocoa/about_window_controller_unittest.mm » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698