| 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.h" | 5 #include "app/l10n_util_mac.h" |
| 6 #include "app/resource_bundle.h" |
| 6 #include "base/file_version_info.h" | 7 #include "base/file_version_info.h" |
| 7 #include "base/logging.h" | 8 #include "base/logging.h" |
| 8 #include "base/mac_util.h" | 9 #include "base/mac_util.h" |
| 9 #include "base/string_util.h" | 10 #include "base/string_util.h" |
| 10 #include "base/sys_string_conversions.h" | 11 #include "base/sys_string_conversions.h" |
| 11 #import "chrome/app/keystone_glue.h" | 12 #import "chrome/app/keystone_glue.h" |
| 12 #import "chrome/browser/cocoa/about_window_controller.h" | 13 #import "chrome/browser/cocoa/about_window_controller.h" |
| 14 #import "chrome/browser/cocoa/background_tile_view.h" |
| 15 #include "chrome/browser/cocoa/restart_browser.h" |
| 16 #include "grit/chromium_strings.h" |
| 13 #include "grit/generated_resources.h" | 17 #include "grit/generated_resources.h" |
| 18 #include "grit/theme_resources.h" |
| 19 #include "grit/locale_settings.h" |
| 20 #include "third_party/GTM/AppKit/GTMUILocalizerAndLayoutTweaker.h" |
| 14 | 21 |
| 15 NSString* const kUserClosedAboutNotification = | 22 NSString* const kUserClosedAboutNotification = |
| 16 @"kUserClosedAboutNotification"; | 23 @"kUserClosedAboutNotification"; |
| 17 | 24 |
| 18 @interface AboutWindowController (Private) | 25 @interface AboutWindowController (Private) |
| 19 - (KeystoneGlue*)defaultKeystoneGlue; | 26 - (KeystoneGlue*)defaultKeystoneGlue; |
| 27 - (void)startProgressMessageID:(uint32_t)messageID; |
| 28 - (void)startProgressMessage:(NSString*)message; |
| 29 - (void)stopProgressMessage:(NSString*)message imageID:(uint32_t)imageID; |
| 20 @end | 30 @end |
| 21 | 31 |
| 32 namespace { |
| 33 |
| 34 // Keystone doesn't give us error numbers on some results, so we just make |
| 35 // our own for reporting in the UI. |
| 36 const int kUpdateInstallFailed = 128; |
| 37 const int kUpdateInstallFailedToStart = 129; |
| 38 |
| 39 void AttributedStringAppendString(NSMutableAttributedString* attr_str, |
| 40 NSString* str) { |
| 41 // You might think doing [[attr_str mutableString] appendString:str] would |
| 42 // work, but it causes any trailing style to get extened, meaning as we |
| 43 // append links, they grow to include the new text, not what we want. |
| 44 NSAttributedString* new_attr_str = |
| 45 [[[NSAttributedString alloc] initWithString:str] autorelease]; |
| 46 [attr_str appendAttributedString:new_attr_str]; |
| 47 } |
| 48 |
| 49 void AttributedStringAppendHyperlink(NSMutableAttributedString* attr_str, |
| 50 NSString* text, NSString* url_str) { |
| 51 |
| 52 // Figure out the range of the text we're adding and add the text. |
| 53 NSRange range = NSMakeRange([attr_str length], [text length]); |
| 54 AttributedStringAppendString(attr_str, text); |
| 55 |
| 56 // Add the link |
| 57 [attr_str addAttribute:NSLinkAttributeName value:url_str range:range]; |
| 58 |
| 59 // Blue and underlined |
| 60 [attr_str addAttribute:NSForegroundColorAttributeName |
| 61 value:[NSColor blueColor] |
| 62 range:range]; |
| 63 [attr_str addAttribute:NSUnderlineStyleAttributeName |
| 64 value:[NSNumber numberWithInt:NSSingleUnderlineStyle] |
| 65 range:range]; |
| 66 } |
| 67 |
| 68 NSAttributedString* BuildLegalTextBlock() { |
| 69 // Windows builds this up in a very complex way, we're just trying to model |
| 70 // it the best we can to get all the information in (they actually do it |
| 71 // but created Labels and Links that they carefully place to make it appear |
| 72 // to be a paragraph of text). |
| 73 // src/chrome/browser/views/about_chrome_view.cc AboutChromeView::Init() |
| 74 |
| 75 NSMutableAttributedString* legal_block = |
| 76 [[[NSMutableAttributedString alloc] init] autorelease]; |
| 77 [legal_block beginEditing]; |
| 78 |
| 79 NSString* copyright = l10n_util::GetNSString(IDS_ABOUT_VERSION_COPYRIGHT); |
| 80 AttributedStringAppendString(legal_block, copyright); |
| 81 |
| 82 // These are the markers directly in IDS_ABOUT_VERSION_LICENSE |
| 83 NSString* kBeginLinkChr = @"BEGIN_LINK_CHR"; |
| 84 NSString* kBeginLinkOss = @"BEGIN_LINK_OSS"; |
| 85 NSString* kEndLinkChr = @"END_LINK_CHR"; |
| 86 NSString* kEndLinkOss = @"END_LINK_OSS"; |
| 87 // The CHR link should go to here |
| 88 NSString* kChromiumProject = l10n_util::GetNSString(IDS_CHROMIUM_PROJECT_URL); |
| 89 // The OSS link should go to here |
| 90 NSString* kAcknowledgements = @"about:credits"; |
| 91 |
| 92 // Now fetch the license string and deal with the markers |
| 93 |
| 94 NSString* license = l10n_util::GetNSString(IDS_ABOUT_VERSION_LICENSE); |
| 95 |
| 96 NSRange begin_chr = [license rangeOfString:kBeginLinkChr]; |
| 97 NSRange begin_oss = [license rangeOfString:kBeginLinkOss]; |
| 98 NSRange end_chr = [license rangeOfString:kEndLinkChr]; |
| 99 NSRange end_oss = [license rangeOfString:kEndLinkOss]; |
| 100 DCHECK(begin_chr.location != NSNotFound); |
| 101 DCHECK(begin_oss.location != NSNotFound); |
| 102 DCHECK(end_chr.location != NSNotFound); |
| 103 DCHECK(end_oss.location != NSNotFound); |
| 104 |
| 105 // We don't know which link will come first, so we have to deal with things |
| 106 // like this: |
| 107 // [text][begin][text][end][text][start][text][end][text] |
| 108 |
| 109 bool chromium_link_first = begin_chr.location < begin_oss.location; |
| 110 |
| 111 NSRange* begin1 = &begin_chr; |
| 112 NSRange* begin2 = &begin_oss; |
| 113 NSRange* end1 = &end_chr; |
| 114 NSRange* end2 = &end_oss; |
| 115 NSString* link1 = kChromiumProject; |
| 116 NSString* link2 = kAcknowledgements; |
| 117 if (!chromium_link_first) { |
| 118 // OSS came first, switch! |
| 119 begin2 = &begin_chr; |
| 120 begin1 = &begin_oss; |
| 121 end2 = &end_chr; |
| 122 end1 = &end_oss; |
| 123 link2 = kChromiumProject; |
| 124 link1 = kAcknowledgements; |
| 125 } |
| 126 |
| 127 NSString *sub_str; |
| 128 |
| 129 AttributedStringAppendString(legal_block, @"\n"); |
| 130 sub_str = [license substringWithRange:NSMakeRange(0, begin1->location)]; |
| 131 AttributedStringAppendString(legal_block, sub_str); |
| 132 sub_str = [license substringWithRange:NSMakeRange(NSMaxRange(*begin1), |
| 133 end1->location - |
| 134 NSMaxRange(*begin1))]; |
| 135 AttributedStringAppendHyperlink(legal_block, sub_str, link1); |
| 136 sub_str = [license substringWithRange:NSMakeRange(NSMaxRange(*end1), |
| 137 begin2->location - |
| 138 NSMaxRange(*end1))]; |
| 139 AttributedStringAppendString(legal_block, sub_str); |
| 140 sub_str = [license substringWithRange:NSMakeRange(NSMaxRange(*begin2), |
| 141 end2->location - |
| 142 NSMaxRange(*begin2))]; |
| 143 AttributedStringAppendHyperlink(legal_block, sub_str, link2); |
| 144 sub_str = [license substringWithRange:NSMakeRange(NSMaxRange(*end2), |
| 145 [license length] - |
| 146 NSMaxRange(*end2))]; |
| 147 AttributedStringAppendString(legal_block, sub_str); |
| 148 |
| 149 #if defined(GOOGLE_CHROME_BUILD) |
| 150 // Terms of service is only valid for Google Chrome |
| 151 |
| 152 // The url within terms should point here: |
| 153 NSString* kTOS = @"about:terms"; |
| 154 // Following Window. There is one marker in the string for where the terms |
| 155 // link goes, but the text of the link comes from a second string resources. |
| 156 std::vector<size_t> url_offsets; |
| 157 std::wstring w_about_terms = l10n_util::GetStringF(IDS_ABOUT_TERMS_OF_SERVICE, |
| 158 std::wstring(), |
| 159 std::wstring(), |
| 160 &url_offsets); |
| 161 DCHECK_EQ(url_offsets.size(), 1U); |
| 162 NSString* about_terms = base::SysWideToNSString(w_about_terms); |
| 163 NSString* terms_link_text = l10n_util::GetNSString(IDS_TERMS_OF_SERVICE); |
| 164 |
| 165 AttributedStringAppendString(legal_block, @"\n"); |
| 166 sub_str = [about_terms substringToIndex:url_offsets[0]]; |
| 167 AttributedStringAppendString(legal_block, sub_str); |
| 168 AttributedStringAppendHyperlink(legal_block, terms_link_text, kTOS); |
| 169 sub_str = [about_terms substringFromIndex:url_offsets[0]]; |
| 170 AttributedStringAppendString(legal_block, sub_str); |
| 171 #endif // defined(GOOGLE_CHROME_BUILD) |
| 172 |
| 173 [legal_block endEditing]; |
| 174 return legal_block; |
| 175 } |
| 176 |
| 177 } // namespace |
| 22 | 178 |
| 23 @implementation AboutWindowController | 179 @implementation AboutWindowController |
| 24 | 180 |
| 25 - (id)initWithWindowNibName:(NSString*)nibName { | 181 - (id)initWithWindowNibName:(NSString*)nibName { |
| 26 NSString* nibpath = [mac_util::MainAppBundle() pathForResource:nibName | 182 NSString* nibpath = [mac_util::MainAppBundle() pathForResource:nibName |
| 27 ofType:@"nib"]; | 183 ofType:@"nib"]; |
| 28 self = [super initWithWindowNibPath:nibpath owner:self]; | 184 self = [super initWithWindowNibPath:nibpath owner:self]; |
| 29 return self; | 185 return self; |
| 30 } | 186 } |
| 31 | 187 |
| 32 - (void)awakeFromNib { | 188 - (void)awakeFromNib { |
| 33 // Set our current version. | 189 // Set our current version. |
| 34 scoped_ptr<FileVersionInfo> version_info( | 190 scoped_ptr<FileVersionInfo> version_info( |
| 35 FileVersionInfo::CreateFileVersionInfoForCurrentModule()); | 191 FileVersionInfo::CreateFileVersionInfoForCurrentModule()); |
| 36 NSString* version = base::SysWideToNSString(version_info->file_version()); | 192 std::wstring version(version_info->product_version()); |
| 37 [version_ setStringValue:version]; | 193 #if !defined(GOOGLE_CHROME_BUILD) |
| 194 // Yes, Windows does this raw since it is only in Chromium builds |
| 195 // src/chrome/browser/views/about_chrome_view.cc AboutChromeView::Init() |
| 196 version += L" ("; |
| 197 version += version_info->last_change(); |
| 198 version += L")"; |
| 199 #endif |
| 200 NSString* nsversion = base::SysWideToNSString(version); |
| 201 [version_ setStringValue:nsversion]; |
| 38 | 202 |
| 39 // Localize the update now button. | 203 // Put the two images into the ui |
| 40 NSString* updateNow = base::SysWideToNSString( | 204 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); |
| 41 l10n_util::GetString(IDS_ABOUT_CHROME_UPDATE_CHECK)); | 205 NSImage* backgroundImage = rb.GetNSImageNamed(IDR_ABOUT_BACKGROUND_COLOR); |
| 42 [updateNowButton_ setStringValue:updateNow]; | 206 DCHECK(backgroundImage); |
| 207 [backgroundView_ setTileImage:backgroundImage]; |
| 208 NSImage* logoImage = rb.GetNSImageNamed(IDR_ABOUT_BACKGROUND); |
| 209 DCHECK(logoImage); |
| 210 [logoView_ setImage:logoImage]; |
| 43 | 211 |
| 44 // TODO(jrg): localize the rest of the elements: | 212 // Put the legal text into |
| 45 // - "Google Chrome" string at top | 213 [legalBlock_ setAttributedStringValue:BuildLegalTextBlock()]; |
| 46 // - copyright string at bottom | |
| 47 | 214 |
| 48 // Initiate an update check. | 215 KeystoneGlue* keystone = [self defaultKeystoneGlue]; |
| 49 if ([[self defaultKeystoneGlue] checkForUpdate:self]) | 216 CGFloat updateShift = 0.0; |
| 50 [spinner_ startAnimation:self]; | 217 if (keystone) { |
| 218 // Initiate an update check. |
| 219 if ([keystone checkForUpdate:self]) { |
| 220 [self startProgressMessageID:IDS_UPGRADE_CHECK_STARTED]; |
| 221 } |
| 222 } else { |
| 223 // Hide all the update UI |
| 224 [updateBlock_ setHidden:YES]; |
| 225 // Figure out the amount we're removing by taking about the update block |
| 226 // (and it's spacing). |
| 227 updateShift = NSMinY([legalBlock_ frame]) - NSMinY([updateBlock_ frame]); |
| 228 } |
| 229 |
| 230 // Adjust the sizes/locations. |
| 231 |
| 232 CGFloat legalShift = |
| 233 [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:legalBlock_]; |
| 234 |
| 235 NSRect rect = [legalBlock_ frame]; |
| 236 rect.origin.y -= updateShift; |
| 237 [legalBlock_ setFrame:rect]; |
| 238 |
| 239 rect = [backgroundView_ frame]; |
| 240 rect.origin.y = rect.origin.y - updateShift + legalShift; |
| 241 [backgroundView_ setFrame:rect]; |
| 242 |
| 243 NSWindow* window = [self window]; |
| 244 [[window contentView] setAutoresizesSubviews:NO]; |
| 245 rect = [window frame]; |
| 246 rect.size.height = rect.size.height - updateShift + legalShift; |
| 247 [window setFrame:rect display:NO]; |
| 248 [[window contentView] setAutoresizesSubviews:YES]; |
| 51 } | 249 } |
| 52 | 250 |
| 53 - (KeystoneGlue*)defaultKeystoneGlue { | 251 - (KeystoneGlue*)defaultKeystoneGlue { |
| 54 return [KeystoneGlue defaultKeystoneGlue]; | 252 return [KeystoneGlue defaultKeystoneGlue]; |
| 55 } | 253 } |
| 56 | 254 |
| 255 - (void)startProgressMessageID:(uint32_t)messageID { |
| 256 NSString* message = l10n_util::GetNSStringWithFixup(messageID); |
| 257 [self startProgressMessage:message]; |
| 258 } |
| 259 |
| 260 - (void)startProgressMessage:(NSString*)message { |
| 261 [updateStatusIndicator_ setHidden:YES]; |
| 262 [spinner_ setHidden:NO]; |
| 263 [spinner_ startAnimation:self]; |
| 264 |
| 265 [updateText_ setStringValue:message]; |
| 266 } |
| 267 |
| 268 - (void)stopProgressMessage:(NSString*)message imageID:(uint32_t)imageID { |
| 269 [spinner_ stopAnimation:self]; |
| 270 [spinner_ setHidden:YES]; |
| 271 if (imageID) { |
| 272 [updateStatusIndicator_ setHidden:NO]; |
| 273 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); |
| 274 NSImage* statusImage = rb.GetNSImageNamed(imageID); |
| 275 DCHECK(statusImage); |
| 276 [updateStatusIndicator_ setImage:statusImage]; |
| 277 } |
| 278 |
| 279 [updateText_ setStringValue:message]; |
| 280 } |
| 281 |
| 57 // Callback from KeystoneGlue; implementation of KeystoneGlueCallbacks protocol. | 282 // Callback from KeystoneGlue; implementation of KeystoneGlueCallbacks protocol. |
| 58 // Warning: latest version may be nil if not set in server config. | 283 // Warning: latest version may be nil if not set in server config. |
| 59 - (void)upToDateCheckCompleted:(BOOL)updatesAvailable | 284 - (void)upToDateCheckCompleted:(BOOL)updatesAvailable |
| 60 latestVersion:(NSString*)latestVersion { | 285 latestVersion:(NSString*)latestVersion { |
| 61 [spinner_ stopAnimation:self]; | 286 uint32_t imageID; |
| 287 NSString* message; |
| 288 if (updatesAvailable) { |
| 289 newVersionAvailable_.reset([latestVersion copy]); |
| 62 | 290 |
| 63 // If an update is available, be sure to enable the "update now" button. | 291 // Window UI doesn't put the version number in the string. |
| 64 NSString* display = @"No updates are available."; | 292 imageID = IDR_UPDATE_AVAILABLE; |
| 65 if (updatesAvailable) { | 293 message = |
| 294 l10n_util::GetNSStringF(IDS_UPGRADE_AVAILABLE, |
| 295 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)); |
| 66 [updateNowButton_ setEnabled:YES]; | 296 [updateNowButton_ setEnabled:YES]; |
| 67 // TODO(jrg): IDS_UPGRADE_AVAILABLE seems close but there is no facility | 297 } else { |
| 68 // for specifying a verision in the update string. Do we care? | 298 // NOTE: This is can be a lie, Keystone does not provide us with an error if |
| 69 display = latestVersion ? | 299 // it was not able to reach the server. So we can't completely map to the |
| 70 [NSString stringWithFormat:@"Version %@ is available for update.", | 300 // Windows UI. |
| 71 latestVersion] : | 301 |
| 72 @"A new version is available."; | 302 // Keystone does not provide the version number when we are up to date so to |
| 303 // maintain the UI, we just go fetch our version and call it good. |
| 304 scoped_ptr<FileVersionInfo> version_info( |
| 305 FileVersionInfo::CreateFileVersionInfoForCurrentModule()); |
| 306 std::wstring version(version_info->product_version()); |
| 307 |
| 308 // TODO: We really should check to see if what is on disk is newer then what |
| 309 // is running and report it as such. (Windows has some messages that can |
| 310 // help with this.) http://crbug.com/13165 |
| 311 |
| 312 imageID = IDR_UPDATE_UPTODATE; |
| 313 message = |
| 314 l10n_util::GetNSStringF(IDS_UPGRADE_ALREADY_UP_TO_DATE, |
| 315 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME), |
| 316 WideToUTF16(version)); |
| 73 } | 317 } |
| 74 [upToDate_ setStringValue:display]; | 318 [self stopProgressMessage:message imageID:imageID]; |
| 75 } | 319 } |
| 76 | 320 |
| 77 - (void)windowWillClose:(NSNotification*)notification { | 321 - (void)windowWillClose:(NSNotification*)notification { |
| 78 // If an update has ever been triggered, we force reuse of the same About Box. | 322 // If an update has ever been triggered, we force reuse of the same About Box. |
| 79 // This gives us 2 things: | 323 // This gives us 2 things: |
| 80 // 1. If an update is ongoing and the window was closed we would have | 324 // 1. If an update is ongoing and the window was closed we would have |
| 81 // no way of getting status. | 325 // no way of getting status. |
| 82 // 2. If we have a "Please restart" message we want it to stay there. | 326 // 2. If we have a "Please restart" message we want it to stay there. |
| 83 if (updateTriggered_) | 327 if (updateTriggered_) |
| 84 return; | 328 return; |
| 85 | 329 |
| 86 [[NSNotificationCenter defaultCenter] | 330 [[NSNotificationCenter defaultCenter] |
| 87 postNotificationName:kUserClosedAboutNotification | 331 postNotificationName:kUserClosedAboutNotification |
| 88 object:self]; | 332 object:self]; |
| 89 } | 333 } |
| 90 | 334 |
| 91 // Callback from KeystoneGlue; implementation of KeystoneGlueCallbacks protocol. | 335 // Callback from KeystoneGlue; implementation of KeystoneGlueCallbacks protocol. |
| 92 - (void)updateCompleted:(BOOL)successful installs:(int)installs { | 336 - (void)updateCompleted:(BOOL)successful installs:(int)installs { |
| 93 [spinner_ stopAnimation:self]; | 337 uint32_t imageID; |
| 94 // TODO(jrg): localize. No current string really says this. | 338 NSString* message; |
| 95 NSString* display = ((successful && installs) ? | 339 if (successful && installs) { |
| 96 @"Update completed! Please restart Chrome." : | 340 imageID = IDR_UPDATE_UPTODATE; |
| 97 @"Self update failed."); | 341 if ([newVersionAvailable_.get() length]) { |
| 98 [updateCompleted_ setStringValue:display]; | 342 message = |
| 343 l10n_util::GetNSStringF(IDS_UPGRADE_SUCCESSFUL, |
| 344 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME), |
| 345 base::SysNSStringToUTF16( |
| 346 newVersionAvailable_.get())); |
| 347 } else { |
| 348 message = |
| 349 l10n_util::GetNSStringF(IDS_UPGRADE_SUCCESSFUL_NOVERSION, |
| 350 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)); |
| 351 } |
| 99 | 352 |
| 100 // Allow a second chance. | 353 // Tell the user to restart their browser. |
| 101 if (!(successful && installs)) | 354 restart_browser::RequestRestart(nil); |
| 355 |
| 356 } else { |
| 357 imageID = IDR_UPDATE_FAIL; |
| 358 message = |
| 359 l10n_util::GetNSStringF(IDS_UPGRADE_ERROR, |
| 360 IntToString16(kUpdateInstallFailed)); |
| 361 |
| 362 // Allow a second chance. |
| 102 [updateNowButton_ setEnabled:YES]; | 363 [updateNowButton_ setEnabled:YES]; |
| 364 } |
| 365 |
| 366 [self stopProgressMessage:message imageID:imageID]; |
| 103 } | 367 } |
| 104 | 368 |
| 105 - (IBAction)updateNow:(id)sender { | 369 - (IBAction)updateNow:(id)sender { |
| 106 updateTriggered_ = YES; | 370 updateTriggered_ = YES; |
| 371 |
| 107 // Don't let someone click "Update Now" twice! | 372 // Don't let someone click "Update Now" twice! |
| 108 [updateNowButton_ setEnabled:NO]; | 373 [updateNowButton_ setEnabled:NO]; |
| 109 [spinner_ startAnimation:self]; | |
| 110 if ([[self defaultKeystoneGlue] startUpdate:self]) { | 374 if ([[self defaultKeystoneGlue] startUpdate:self]) { |
| 111 // Clear any previous error message from the throbber area. | 375 // Clear any previous error message from the throbber area. |
| 112 [updateCompleted_ setStringValue:@""]; | 376 [self startProgressMessageID:IDS_UPGRADE_STARTED]; |
| 113 [spinner_ startAnimation:self]; | |
| 114 } else { | 377 } else { |
| 115 // TODO(jrg): localize. | 378 NSString* message = |
| 116 // IDS_UPGRADE_ERROR doesn't work here; we don't have an error number, and | 379 l10n_util::GetNSStringF(IDS_UPGRADE_ERROR, |
| 117 // "server not available" is too specific. | 380 IntToString16(kUpdateInstallFailedToStart)); |
| 118 [updateCompleted_ setStringValue:@"Failed to start updates."]; | 381 [self stopProgressMessage:message imageID:IDR_UPDATE_FAIL]; |
| 119 } | 382 } |
| 120 } | 383 } |
| 121 | 384 |
| 122 - (NSButton*)updateButton { | 385 - (NSButton*)updateButton { |
| 123 return updateNowButton_; | 386 return updateNowButton_; |
| 124 } | 387 } |
| 125 | 388 |
| 126 - (NSTextField*)upToDateTextField { | 389 - (NSTextField*)updateText { |
| 127 return upToDate_; | 390 return updateText_; |
| 128 } | |
| 129 | |
| 130 - (NSTextField*)updateCompletedTextField { | |
| 131 return updateCompleted_; | |
| 132 } | 391 } |
| 133 | 392 |
| 134 @end | 393 @end |
| 135 | 394 |
| OLD | NEW |