| OLD | NEW |
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 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 "chrome/browser/ui/cocoa/autofill/autofill_dialog_cocoa.h" | 5 #include "chrome/browser/ui/cocoa/autofill/autofill_dialog_cocoa.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/mac/bundle_locations.h" | |
| 9 #include "base/mac/foundation_util.h" | |
| 10 #include "base/mac/scoped_nsobject.h" | 8 #include "base/mac/scoped_nsobject.h" |
| 11 #include "base/message_loop/message_loop.h" | 9 #include "base/message_loop/message_loop.h" |
| 12 #include "base/strings/sys_string_conversions.h" | 10 #include "base/strings/sys_string_conversions.h" |
| 13 #include "chrome/browser/ui/autofill/autofill_dialog_view_delegate.h" | 11 #include "chrome/browser/ui/autofill/autofill_dialog_view_delegate.h" |
| 14 #include "chrome/browser/ui/chrome_style.h" | |
| 15 #import "chrome/browser/ui/cocoa/autofill/autofill_account_chooser.h" | |
| 16 #import "chrome/browser/ui/cocoa/autofill/autofill_details_container.h" | 12 #import "chrome/browser/ui/cocoa/autofill/autofill_details_container.h" |
| 17 #include "chrome/browser/ui/cocoa/autofill/autofill_dialog_constants.h" | 13 #import "chrome/browser/ui/cocoa/autofill/autofill_dialog_window_controller.h" |
| 18 #import "chrome/browser/ui/cocoa/autofill/autofill_input_field.h" | |
| 19 #import "chrome/browser/ui/cocoa/autofill/autofill_loading_shield_controller.h" | |
| 20 #import "chrome/browser/ui/cocoa/autofill/autofill_main_container.h" | |
| 21 #import "chrome/browser/ui/cocoa/autofill/autofill_overlay_controller.h" | |
| 22 #import "chrome/browser/ui/cocoa/autofill/autofill_section_container.h" | |
| 23 #import "chrome/browser/ui/cocoa/autofill/autofill_sign_in_container.h" | |
| 24 #import "chrome/browser/ui/cocoa/autofill/autofill_textfield.h" | |
| 25 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_sh
eet.h" | 14 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_sh
eet.h" |
| 26 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_wi
ndow.h" | |
| 27 #include "content/public/browser/web_contents.h" | |
| 28 #include "content/public/browser/web_contents_view.h" | |
| 29 #include "grit/generated_resources.h" | |
| 30 #import "ui/base/cocoa/flipped_view.h" | |
| 31 #include "ui/base/cocoa/window_size_constants.h" | |
| 32 #include "ui/base/l10n/l10n_util.h" | |
| 33 #include "ui/gfx/platform_font.h" | |
| 34 | |
| 35 const CGFloat kAccountChooserHeight = 20.0; | |
| 36 const CGFloat kMinimumContentsHeight = 101; | |
| 37 | |
| 38 // Height of all decorations & paddings on main dialog together. | |
| 39 const CGFloat kDecorationHeight = kAccountChooserHeight + | |
| 40 autofill::kDetailVerticalPadding + | |
| 41 chrome_style::kClientBottomPadding + | |
| 42 chrome_style::kTitleTopPadding; | |
| 43 | 15 |
| 44 namespace autofill { | 16 namespace autofill { |
| 45 | 17 |
| 46 // static | 18 // static |
| 47 AutofillDialogView* AutofillDialogView::Create( | 19 AutofillDialogView* AutofillDialogView::Create( |
| 48 AutofillDialogViewDelegate* delegate) { | 20 AutofillDialogViewDelegate* delegate) { |
| 49 return new AutofillDialogCocoa(delegate); | 21 return new AutofillDialogCocoa(delegate); |
| 50 } | 22 } |
| 51 | 23 |
| 52 AutofillDialogCocoa::AutofillDialogCocoa(AutofillDialogViewDelegate* delegate) | 24 AutofillDialogCocoa::AutofillDialogCocoa(AutofillDialogViewDelegate* delegate) |
| (...skipping 162 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 215 gfx::Size AutofillDialogCocoa::GetSize() const { | 187 gfx::Size AutofillDialogCocoa::GetSize() const { |
| 216 return gfx::Size(NSSizeToCGSize([[sheet_delegate_ window] frame].size)); | 188 return gfx::Size(NSSizeToCGSize([[sheet_delegate_ window] frame].size)); |
| 217 } | 189 } |
| 218 | 190 |
| 219 content::WebContents* AutofillDialogCocoa::GetSignInWebContents() { | 191 content::WebContents* AutofillDialogCocoa::GetSignInWebContents() { |
| 220 return [sheet_delegate_ getSignInWebContents]; | 192 return [sheet_delegate_ getSignInWebContents]; |
| 221 } | 193 } |
| 222 | 194 |
| 223 | 195 |
| 224 bool AutofillDialogCocoa::IsShowingOverlay() const { | 196 bool AutofillDialogCocoa::IsShowingOverlay() const { |
| 225 return [sheet_delegate_ IsShowingOverlay]; | 197 return [sheet_delegate_ isShowingOverlay]; |
| 226 } | 198 } |
| 227 | 199 |
| 228 void AutofillDialogCocoa::OnConstrainedWindowClosed( | 200 void AutofillDialogCocoa::OnConstrainedWindowClosed( |
| 229 ConstrainedWindowMac* window) { | 201 ConstrainedWindowMac* window) { |
| 230 constrained_window_.reset(); | 202 constrained_window_.reset(); |
| 231 // |this| belongs to |delegate_|, so no self-destruction here. | 203 // |this| belongs to |delegate_|, so no self-destruction here. |
| 232 delegate_->ViewClosed(); | 204 delegate_->ViewClosed(); |
| 233 } | 205 } |
| 234 | 206 |
| 235 } // autofill | 207 } // autofill |
| 236 | |
| 237 #pragma mark Field Editor | |
| 238 | |
| 239 @interface AutofillDialogFieldEditor : NSTextView | |
| 240 @end | |
| 241 | |
| 242 | |
| 243 @implementation AutofillDialogFieldEditor | |
| 244 | |
| 245 - (void)mouseDown:(NSEvent*)event { | |
| 246 // Delegate _must_ be notified before mouseDown is complete, since it needs | |
| 247 // to distinguish between mouseDown for already focused fields, and fields | |
| 248 // that will receive focus as part of the mouseDown. | |
| 249 AutofillTextField* textfield = | |
| 250 base::mac::ObjCCastStrict<AutofillTextField>([self delegate]); | |
| 251 [textfield onEditorMouseDown:self]; | |
| 252 [super mouseDown:event]; | |
| 253 } | |
| 254 | |
| 255 @end | |
| 256 | |
| 257 | |
| 258 #pragma mark Window Controller | |
| 259 | |
| 260 @interface AutofillDialogWindowController () | |
| 261 | |
| 262 // Compute maximum allowed height for the dialog. | |
| 263 - (CGFloat)maxHeight; | |
| 264 | |
| 265 // Update size constraints on sign-in container. | |
| 266 - (void)updateSignInSizeConstraints; | |
| 267 | |
| 268 // Notification that the WebContent's view frame has changed. | |
| 269 - (void)onContentViewFrameDidChange:(NSNotification*)notification; | |
| 270 | |
| 271 @end | |
| 272 | |
| 273 | |
| 274 @implementation AutofillDialogWindowController (NSWindowDelegate) | |
| 275 | |
| 276 - (id)windowWillReturnFieldEditor:(NSWindow*)window toObject:(id)client { | |
| 277 AutofillTextField* textfield = base::mac::ObjCCast<AutofillTextField>(client); | |
| 278 if (!textfield) | |
| 279 return nil; | |
| 280 | |
| 281 if (!fieldEditor_) { | |
| 282 fieldEditor_.reset([[AutofillDialogFieldEditor alloc] init]); | |
| 283 [fieldEditor_ setFieldEditor:YES]; | |
| 284 } | |
| 285 return fieldEditor_.get(); | |
| 286 } | |
| 287 | |
| 288 @end | |
| 289 | |
| 290 | |
| 291 @implementation AutofillDialogWindowController | |
| 292 | |
| 293 - (id)initWithWebContents:(content::WebContents*)webContents | |
| 294 autofillDialog:(autofill::AutofillDialogCocoa*)autofillDialog { | |
| 295 DCHECK(webContents); | |
| 296 | |
| 297 base::scoped_nsobject<ConstrainedWindowCustomWindow> window( | |
| 298 [[ConstrainedWindowCustomWindow alloc] | |
| 299 initWithContentRect:ui::kWindowSizeDeterminedLater]); | |
| 300 | |
| 301 if ((self = [super initWithWindow:window])) { | |
| 302 [window setDelegate:self]; | |
| 303 webContents_ = webContents; | |
| 304 autofillDialog_ = autofillDialog; | |
| 305 | |
| 306 mainContainer_.reset([[AutofillMainContainer alloc] | |
| 307 initWithDelegate:autofillDialog->delegate()]); | |
| 308 [mainContainer_ setTarget:self]; | |
| 309 | |
| 310 signInContainer_.reset( | |
| 311 [[AutofillSignInContainer alloc] initWithDialog:autofillDialog]); | |
| 312 [[signInContainer_ view] setHidden:YES]; | |
| 313 | |
| 314 // Set dialog title. | |
| 315 titleTextField_.reset([[NSTextField alloc] initWithFrame:NSZeroRect]); | |
| 316 [titleTextField_ setEditable:NO]; | |
| 317 [titleTextField_ setBordered:NO]; | |
| 318 [titleTextField_ setDrawsBackground:NO]; | |
| 319 [titleTextField_ setFont:[NSFont systemFontOfSize:15.0]]; | |
| 320 [titleTextField_ setStringValue: | |
| 321 base::SysUTF16ToNSString(autofillDialog->delegate()->DialogTitle())]; | |
| 322 [titleTextField_ sizeToFit]; | |
| 323 | |
| 324 accountChooser_.reset([[AutofillAccountChooser alloc] | |
| 325 initWithFrame:NSZeroRect | |
| 326 delegate:autofillDialog->delegate()]); | |
| 327 | |
| 328 loadingShieldController_.reset( | |
| 329 [[AutofillLoadingShieldController alloc] | |
| 330 initWithDelegate:autofillDialog->delegate()]); | |
| 331 [[loadingShieldController_ view] setHidden:YES]; | |
| 332 | |
| 333 overlayController_.reset( | |
| 334 [[AutofillOverlayController alloc] initWithDelegate: | |
| 335 autofillDialog->delegate()]); | |
| 336 [[overlayController_ view] setHidden:YES]; | |
| 337 | |
| 338 // This needs a flipped content view because otherwise the size | |
| 339 // animation looks odd. However, replacing the contentView for constrained | |
| 340 // windows does not work - it does custom rendering. | |
| 341 base::scoped_nsobject<NSView> flippedContentView( | |
| 342 [[FlippedView alloc] initWithFrame: | |
| 343 [[[self window] contentView] frame]]); | |
| 344 [flippedContentView setSubviews: | |
| 345 @[accountChooser_, | |
| 346 titleTextField_, | |
| 347 [mainContainer_ view], | |
| 348 [signInContainer_ view], | |
| 349 [loadingShieldController_ view], | |
| 350 [overlayController_ view]]]; | |
| 351 [flippedContentView setAutoresizingMask: | |
| 352 (NSViewWidthSizable | NSViewHeightSizable)]; | |
| 353 [[[self window] contentView] addSubview:flippedContentView]; | |
| 354 [mainContainer_ setAnchorView:[[accountChooser_ subviews] objectAtIndex:1]]; | |
| 355 } | |
| 356 return self; | |
| 357 } | |
| 358 | |
| 359 - (void)dealloc { | |
| 360 [[NSNotificationCenter defaultCenter] removeObserver:self]; | |
| 361 [super dealloc]; | |
| 362 } | |
| 363 | |
| 364 - (CGFloat)maxHeight { | |
| 365 NSRect dialogFrameRect = [[self window] frame]; | |
| 366 NSRect browserFrameRect = | |
| 367 [webContents_->GetView()->GetTopLevelNativeWindow() frame]; | |
| 368 dialogFrameRect.size.height = | |
| 369 NSMaxY(dialogFrameRect) - NSMinY(browserFrameRect); | |
| 370 dialogFrameRect = [[self window] contentRectForFrameRect:dialogFrameRect]; | |
| 371 return NSHeight(dialogFrameRect); | |
| 372 } | |
| 373 | |
| 374 - (void)updateSignInSizeConstraints { | |
| 375 // Adjust for the size of all decorations and paddings outside main content. | |
| 376 CGFloat minHeight = kMinimumContentsHeight - kDecorationHeight; | |
| 377 CGFloat maxHeight = std::max([self maxHeight] - kDecorationHeight, minHeight); | |
| 378 CGFloat width = NSWidth([[[self window] contentView] frame]); | |
| 379 | |
| 380 [signInContainer_ constrainSizeToMinimum:NSMakeSize(width, minHeight) | |
| 381 maximum:NSMakeSize(width, maxHeight)]; | |
| 382 } | |
| 383 | |
| 384 - (void)onContentViewFrameDidChange:(NSNotification*)notification { | |
| 385 [self updateSignInSizeConstraints]; | |
| 386 if ([[signInContainer_ view] isHidden]) | |
| 387 [self requestRelayout]; | |
| 388 } | |
| 389 | |
| 390 - (void)cancelRelayout { | |
| 391 [NSObject cancelPreviousPerformRequestsWithTarget:self | |
| 392 selector:@selector(performLayout) | |
| 393 object:nil]; | |
| 394 } | |
| 395 | |
| 396 - (void)requestRelayout { | |
| 397 [self cancelRelayout]; | |
| 398 [self performSelector:@selector(performLayout) withObject:nil afterDelay:0.0]; | |
| 399 } | |
| 400 | |
| 401 - (NSSize)preferredSize { | |
| 402 NSSize size; | |
| 403 | |
| 404 // Overall size is determined by either main container or sign in view. | |
| 405 if ([[signInContainer_ view] isHidden]) | |
| 406 size = [mainContainer_ preferredSize]; | |
| 407 else | |
| 408 size = [signInContainer_ preferredSize]; | |
| 409 | |
| 410 // Always make room for the header. | |
| 411 size.height += kDecorationHeight; | |
| 412 | |
| 413 if (![[overlayController_ view] isHidden]) { | |
| 414 CGFloat height = [overlayController_ heightForWidth:size.width]; | |
| 415 if (height != 0.0) | |
| 416 size.height = height; | |
| 417 } | |
| 418 | |
| 419 // Show as much of the main view as is possible without going past the | |
| 420 // bottom of the browser window. | |
| 421 size.height = std::min(size.height, [self maxHeight]); | |
| 422 | |
| 423 return size; | |
| 424 } | |
| 425 | |
| 426 - (void)performLayout { | |
| 427 NSRect contentRect = NSZeroRect; | |
| 428 contentRect.size = [self preferredSize]; | |
| 429 NSRect clientRect = contentRect; | |
| 430 clientRect.origin.y = chrome_style::kTitleTopPadding; | |
| 431 clientRect.size.height -= chrome_style::kTitleTopPadding + | |
| 432 chrome_style::kClientBottomPadding; | |
| 433 | |
| 434 [titleTextField_ setStringValue: | |
| 435 base::SysUTF16ToNSString(autofillDialog_->delegate()->DialogTitle())]; | |
| 436 [titleTextField_ sizeToFit]; | |
| 437 | |
| 438 NSRect headerRect, mainRect, titleRect, dummyRect; | |
| 439 NSDivideRect(clientRect, &headerRect, &mainRect, | |
| 440 kAccountChooserHeight, NSMinYEdge); | |
| 441 NSDivideRect(mainRect, &dummyRect, &mainRect, | |
| 442 autofill::kDetailVerticalPadding, NSMinYEdge); | |
| 443 headerRect = NSInsetRect(headerRect, chrome_style::kHorizontalPadding, 0); | |
| 444 NSDivideRect(headerRect, &titleRect, &headerRect, | |
| 445 NSWidth([titleTextField_ frame]), NSMinXEdge); | |
| 446 | |
| 447 // Align baseline of title with bottom of accountChooser. | |
| 448 base::scoped_nsobject<NSLayoutManager> layout_manager( | |
| 449 [[NSLayoutManager alloc] init]); | |
| 450 NSFont* titleFont = [titleTextField_ font]; | |
| 451 titleRect.origin.y += NSHeight(titleRect) - | |
| 452 [layout_manager defaultBaselineOffsetForFont:titleFont]; | |
| 453 [titleTextField_ setFrame:titleRect]; | |
| 454 | |
| 455 [accountChooser_ setFrame:headerRect]; | |
| 456 [accountChooser_ performLayout]; | |
| 457 if ([[signInContainer_ view] isHidden]) { | |
| 458 [[mainContainer_ view] setFrame:mainRect]; | |
| 459 [mainContainer_ performLayout]; | |
| 460 } else { | |
| 461 [[signInContainer_ view] setFrame:mainRect]; | |
| 462 } | |
| 463 | |
| 464 [[loadingShieldController_ view] setFrame:contentRect]; | |
| 465 [loadingShieldController_ performLayout]; | |
| 466 | |
| 467 [[overlayController_ view] setFrame:contentRect]; | |
| 468 [overlayController_ performLayout]; | |
| 469 | |
| 470 NSRect frameRect = [[self window] frameRectForContentRect:contentRect]; | |
| 471 [[self window] setFrame:frameRect display:YES]; | |
| 472 [[self window] recalculateKeyViewLoop]; | |
| 473 } | |
| 474 | |
| 475 - (IBAction)accept:(id)sender { | |
| 476 if ([mainContainer_ validate]) | |
| 477 autofillDialog_->delegate()->OnAccept(); | |
| 478 else | |
| 479 [mainContainer_ makeFirstInvalidInputFirstResponder]; | |
| 480 } | |
| 481 | |
| 482 - (IBAction)cancel:(id)sender { | |
| 483 autofillDialog_->delegate()->OnCancel(); | |
| 484 autofillDialog_->PerformClose(); | |
| 485 } | |
| 486 | |
| 487 - (void)show { | |
| 488 // Resizing the browser causes the ConstrainedWindow to move. | |
| 489 // Observe that to allow resizes based on browser size. | |
| 490 // NOTE: This MUST come last after all initial setup is done, because there | |
| 491 // is an immediate notification post registration. | |
| 492 DCHECK([self window]); | |
| 493 [[NSNotificationCenter defaultCenter] | |
| 494 addObserver:self | |
| 495 selector:@selector(onContentViewFrameDidChange:) | |
| 496 name:NSWindowDidMoveNotification | |
| 497 object:[self window]]; | |
| 498 | |
| 499 [self updateAccountChooser]; | |
| 500 [self updateNotificationArea]; | |
| 501 [self requestRelayout]; | |
| 502 } | |
| 503 | |
| 504 - (void)hide { | |
| 505 autofillDialog_->delegate()->OnCancel(); | |
| 506 autofillDialog_->PerformClose(); | |
| 507 } | |
| 508 | |
| 509 - (void)updateNotificationArea { | |
| 510 [mainContainer_ updateNotificationArea]; | |
| 511 } | |
| 512 | |
| 513 - (void)updateAccountChooser { | |
| 514 [accountChooser_ update]; | |
| 515 [mainContainer_ updateLegalDocuments]; | |
| 516 | |
| 517 // For the duration of the loading shield, hide the main contents. | |
| 518 // This prevents the currently focused text field "shining through". | |
| 519 // No need to remember previous state, because the loading shield | |
| 520 // always flows through to the main container. | |
| 521 [loadingShieldController_ update]; | |
| 522 [[mainContainer_ view] setHidden:![[loadingShieldController_ view] isHidden]]; | |
| 523 } | |
| 524 | |
| 525 - (void)updateButtonStrip { | |
| 526 [overlayController_ updateState]; | |
| 527 } | |
| 528 | |
| 529 - (void)updateSection:(autofill::DialogSection)section { | |
| 530 [[mainContainer_ sectionForId:section] update]; | |
| 531 [mainContainer_ updateSaveInChrome]; | |
| 532 } | |
| 533 | |
| 534 - (void)fillSection:(autofill::DialogSection)section | |
| 535 forInput:(const autofill::DetailInput&)input { | |
| 536 [[mainContainer_ sectionForId:section] fillForInput:input]; | |
| 537 [mainContainer_ updateSaveInChrome]; | |
| 538 } | |
| 539 | |
| 540 - (content::NavigationController*)showSignIn { | |
| 541 [self updateSignInSizeConstraints]; | |
| 542 [signInContainer_ loadSignInPage]; | |
| 543 [[mainContainer_ view] setHidden:YES]; | |
| 544 [[signInContainer_ view] setHidden:NO]; | |
| 545 [self requestRelayout]; | |
| 546 | |
| 547 return [signInContainer_ navigationController]; | |
| 548 } | |
| 549 | |
| 550 - (void)getInputs:(autofill::DetailOutputMap*)output | |
| 551 forSection:(autofill::DialogSection)section { | |
| 552 [[mainContainer_ sectionForId:section] getInputs:output]; | |
| 553 } | |
| 554 | |
| 555 - (NSString*)getCvc { | |
| 556 autofill::DialogSection section = autofill::SECTION_CC; | |
| 557 NSString* value = [[mainContainer_ sectionForId:section] suggestionText]; | |
| 558 if (!value) { | |
| 559 section = autofill::SECTION_CC_BILLING; | |
| 560 value = [[mainContainer_ sectionForId:section] suggestionText]; | |
| 561 } | |
| 562 return value; | |
| 563 } | |
| 564 | |
| 565 - (BOOL)saveDetailsLocally { | |
| 566 return [mainContainer_ saveDetailsLocally]; | |
| 567 } | |
| 568 | |
| 569 - (void)hideSignIn { | |
| 570 [[signInContainer_ view] setHidden:YES]; | |
| 571 [[mainContainer_ view] setHidden:NO]; | |
| 572 [self requestRelayout]; | |
| 573 } | |
| 574 | |
| 575 - (void)modelChanged { | |
| 576 [mainContainer_ modelChanged]; | |
| 577 } | |
| 578 | |
| 579 - (void)updateErrorBubble { | |
| 580 [mainContainer_ updateErrorBubble]; | |
| 581 } | |
| 582 | |
| 583 - (void)onSignInResize:(NSSize)size { | |
| 584 [signInContainer_ setPreferredSize:size]; | |
| 585 [self requestRelayout]; | |
| 586 } | |
| 587 | |
| 588 @end | |
| 589 | |
| 590 | |
| 591 @implementation AutofillDialogWindowController (TestableAutofillDialogView) | |
| 592 | |
| 593 - (void)setTextContents:(NSString*)text | |
| 594 forInput:(const autofill::DetailInput&)input { | |
| 595 for (size_t i = autofill::SECTION_MIN; i <= autofill::SECTION_MAX; ++i) { | |
| 596 autofill::DialogSection section = static_cast<autofill::DialogSection>(i); | |
| 597 // TODO(groby): Need to find the section for an input directly - wasteful. | |
| 598 [[mainContainer_ sectionForId:section] setFieldValue:text forInput:input]; | |
| 599 } | |
| 600 } | |
| 601 | |
| 602 - (void)setTextContents:(NSString*)text | |
| 603 ofSuggestionForSection:(autofill::DialogSection)section { | |
| 604 [[mainContainer_ sectionForId:section] setSuggestionFieldValue:text]; | |
| 605 } | |
| 606 | |
| 607 - (void)activateFieldForInput:(const autofill::DetailInput&)input { | |
| 608 for (size_t i = autofill::SECTION_MIN; i <= autofill::SECTION_MAX; ++i) { | |
| 609 autofill::DialogSection section = static_cast<autofill::DialogSection>(i); | |
| 610 [[mainContainer_ sectionForId:section] activateFieldForInput:input]; | |
| 611 } | |
| 612 } | |
| 613 | |
| 614 - (content::WebContents*)getSignInWebContents { | |
| 615 return [signInContainer_ webContents]; | |
| 616 } | |
| 617 | |
| 618 - (BOOL)IsShowingOverlay { | |
| 619 return ![[overlayController_ view] isHidden]; | |
| 620 } | |
| 621 | |
| 622 @end | |
| OLD | NEW |