| Index: chrome/browser/ui/cocoa/autofill/autofill_details_container.mm
|
| diff --git a/chrome/browser/ui/cocoa/autofill/autofill_details_container.mm b/chrome/browser/ui/cocoa/autofill/autofill_details_container.mm
|
| deleted file mode 100644
|
| index 88b426a268013c84b3ebd9ff1618b29fb4483f28..0000000000000000000000000000000000000000
|
| --- a/chrome/browser/ui/cocoa/autofill/autofill_details_container.mm
|
| +++ /dev/null
|
| @@ -1,289 +0,0 @@
|
| -// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
| -// Use of this source code is governed by a BSD-style license that can be
|
| -// found in the LICENSE file.
|
| -
|
| -#import "chrome/browser/ui/cocoa/autofill/autofill_details_container.h"
|
| -
|
| -#include <algorithm>
|
| -
|
| -#include "base/mac/foundation_util.h"
|
| -#include "chrome/browser/ui/autofill/autofill_dialog_view_delegate.h"
|
| -#import "chrome/browser/ui/cocoa/autofill/autofill_bubble_controller.h"
|
| -#import "chrome/browser/ui/cocoa/autofill/autofill_section_container.h"
|
| -#import "chrome/browser/ui/cocoa/info_bubble_view.h"
|
| -#include "ui/base/cocoa/cocoa_base_utils.h"
|
| -
|
| -typedef BOOL (^FieldFilterBlock)(NSView<AutofillInputField>*);
|
| -
|
| -@interface AutofillDetailsContainer ()
|
| -
|
| -// Find the editable input field that is closest to the top of the dialog and
|
| -// matches the |predicateBlock|.
|
| -- (NSView*)firstEditableFieldMatchingBlock:(FieldFilterBlock)predicateBlock;
|
| -
|
| -@end
|
| -
|
| -@implementation AutofillDetailsContainer
|
| -
|
| -- (id)initWithDelegate:(autofill::AutofillDialogViewDelegate*)delegate {
|
| - if (self = [super init]) {
|
| - delegate_ = delegate;
|
| - }
|
| - return self;
|
| -}
|
| -
|
| -- (void)addSection:(autofill::DialogSection)section {
|
| - base::scoped_nsobject<AutofillSectionContainer> sectionContainer(
|
| - [[AutofillSectionContainer alloc] initWithDelegate:delegate_
|
| - forSection:section]);
|
| - [sectionContainer setValidationDelegate:self];
|
| - [details_ addObject:sectionContainer];
|
| -}
|
| -
|
| -- (void)loadView {
|
| - details_.reset([[NSMutableArray alloc] init]);
|
| -
|
| - [self addSection:autofill::SECTION_CC];
|
| - [self addSection:autofill::SECTION_BILLING];
|
| - [self addSection:autofill::SECTION_SHIPPING];
|
| -
|
| - scrollView_.reset([[NSScrollView alloc] initWithFrame:NSZeroRect]);
|
| - [scrollView_ setHasVerticalScroller:YES];
|
| - [scrollView_ setHasHorizontalScroller:NO];
|
| - [scrollView_ setBorderType:NSNoBorder];
|
| - [scrollView_ setAutohidesScrollers:YES];
|
| - [self setView:scrollView_];
|
| -
|
| - [scrollView_ setDocumentView:[[NSView alloc] initWithFrame:NSZeroRect]];
|
| -
|
| - for (AutofillSectionContainer* container in details_.get())
|
| - [[scrollView_ documentView] addSubview:[container view]];
|
| -
|
| - [self performLayout];
|
| -}
|
| -
|
| -- (NSSize)preferredSize {
|
| - NSSize size = NSZeroSize;
|
| - for (AutofillSectionContainer* container in details_.get()) {
|
| - NSSize containerSize = [container preferredSize];
|
| - size.height += containerSize.height;
|
| - size.width = std::max(containerSize.width, size.width);
|
| - }
|
| - return size;
|
| -}
|
| -
|
| -- (void)performLayout {
|
| - NSRect rect = NSZeroRect;
|
| - for (AutofillSectionContainer* container in
|
| - [details_ reverseObjectEnumerator]) {
|
| - if (![[container view] isHidden]) {
|
| - [container performLayout];
|
| - [[container view] setFrameOrigin:NSMakePoint(0, NSMaxY(rect))];
|
| - rect = NSUnionRect(rect, [[container view] frame]);
|
| - }
|
| - }
|
| -
|
| - [[scrollView_ documentView] setFrameSize:[self preferredSize]];
|
| -}
|
| -
|
| -- (AutofillSectionContainer*)sectionForId:(autofill::DialogSection)section {
|
| - for (AutofillSectionContainer* details in details_.get()) {
|
| - if ([details section] == section)
|
| - return details;
|
| - }
|
| - return nil;
|
| -}
|
| -
|
| -- (void)modelChanged {
|
| - for (AutofillSectionContainer* details in details_.get())
|
| - [details modelChanged];
|
| -}
|
| -
|
| -- (BOOL)validate {
|
| - // Account for a subtle timing issue. -validate is called from the dialog's
|
| - // -accept. -accept then hides the dialog. If the data does not validate the
|
| - // dialog is then reshown, focusing on the first invalid field. This happens
|
| - // without running the message loop, so windowWillClose has not fired when
|
| - // the dialog and error bubble is reshown, leading to a missing error bubble.
|
| - // Resetting the anchor view here forces the bubble to show.
|
| - errorBubbleAnchorView_ = nil;
|
| -
|
| - bool allValid = true;
|
| - for (AutofillSectionContainer* details in details_.get()) {
|
| - if (![[details view] isHidden])
|
| - allValid = [details validateFor:autofill::VALIDATE_FINAL] && allValid;
|
| - }
|
| - return allValid;
|
| -}
|
| -
|
| -- (NSView*)firstInvalidField {
|
| - return [self firstEditableFieldMatchingBlock:
|
| - ^BOOL (NSView<AutofillInputField>* field) {
|
| - return [field invalid];
|
| - }];
|
| -}
|
| -
|
| -- (NSView*)firstVisibleField {
|
| - return [self firstEditableFieldMatchingBlock:
|
| - ^BOOL (NSView<AutofillInputField>* field) {
|
| - return YES;
|
| - }];
|
| -}
|
| -
|
| -- (void)scrollToView:(NSView*)field {
|
| - const CGFloat bottomPadding = 5.0; // Padding below the visible field.
|
| -
|
| - NSClipView* clipView = [scrollView_ contentView];
|
| - NSRect fieldRect = [field convertRect:[field bounds] toView:clipView];
|
| -
|
| - // If the entire field is already visible, let's not scroll.
|
| - NSRect documentRect = [clipView documentVisibleRect];
|
| - documentRect = [[clipView documentView] convertRect:documentRect
|
| - toView:clipView];
|
| - if (NSContainsRect(documentRect, fieldRect))
|
| - return;
|
| -
|
| - NSPoint scrollPoint = [clipView constrainScrollPoint:
|
| - NSMakePoint(0, NSMinY(fieldRect) - bottomPadding)];
|
| - [clipView scrollToPoint:scrollPoint];
|
| - [scrollView_ reflectScrolledClipView:clipView];
|
| - [self updateErrorBubble];
|
| -}
|
| -
|
| -- (void)updateErrorBubble {
|
| - if (!delegate_->ShouldShowErrorBubble()) {
|
| - [errorBubbleController_ close];
|
| - }
|
| -}
|
| -
|
| -- (void)errorBubbleWindowWillClose:(NSNotification*)notification {
|
| - DCHECK_EQ([notification object], [errorBubbleController_ window]);
|
| -
|
| - NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
|
| - [center removeObserver:self
|
| - name:NSWindowWillCloseNotification
|
| - object:[errorBubbleController_ window]];
|
| - errorBubbleController_ = nil;
|
| - errorBubbleAnchorView_ = nil;
|
| -}
|
| -
|
| -- (void)showErrorBubbleForField:(NSControl<AutofillInputField>*)field {
|
| - // If there is already a bubble controller handling this field, reuse.
|
| - if (errorBubbleController_ && errorBubbleAnchorView_ == field) {
|
| - [errorBubbleController_ setMessage:[field validityMessage]];
|
| -
|
| - return;
|
| - }
|
| -
|
| - if (errorBubbleController_)
|
| - [errorBubbleController_ close];
|
| - DCHECK(!errorBubbleController_);
|
| - NSWindow* parentWindow = [field window];
|
| - DCHECK(parentWindow);
|
| - errorBubbleController_ =
|
| - [[AutofillBubbleController alloc]
|
| - initWithParentWindow:parentWindow
|
| - message:[field validityMessage]];
|
| -
|
| - // Handle bubble self-deleting.
|
| - NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
|
| - [center addObserver:self
|
| - selector:@selector(errorBubbleWindowWillClose:)
|
| - name:NSWindowWillCloseNotification
|
| - object:[errorBubbleController_ window]];
|
| -
|
| - // Compute anchor point (in window coords - views might be flipped).
|
| - NSRect viewRect = [field convertRect:[field bounds] toView:nil];
|
| -
|
| - // If a bubble at maximum size with a left-aligned edge would exceed the
|
| - // window width, align the right edge of bubble and view. In all other
|
| - // cases, align the left edge of the bubble and the view.
|
| - // Alignment is based on maximum width to avoid the arrow changing positions
|
| - // if the validation bubble stays on the same field but gets a message of
|
| - // differing length. (E.g. "Field is required"/"Invalid Zip Code. Please
|
| - // check and try again" if an empty zip field gets changed to a bad zip).
|
| - NSPoint anchorPoint;
|
| - if ((NSMinX(viewRect) + [errorBubbleController_ maxWidth]) >
|
| - NSWidth([parentWindow frame])) {
|
| - anchorPoint = NSMakePoint(NSMaxX(viewRect), NSMinY(viewRect));
|
| - [[errorBubbleController_ bubble] setArrowLocation:info_bubble::kTopRight];
|
| - [[errorBubbleController_ bubble] setAlignment:
|
| - info_bubble::kAlignRightEdgeToAnchorEdge];
|
| -
|
| - } else {
|
| - anchorPoint = NSMakePoint(NSMinX(viewRect), NSMinY(viewRect));
|
| - [[errorBubbleController_ bubble] setArrowLocation:info_bubble::kTopLeft];
|
| - [[errorBubbleController_ bubble] setAlignment:
|
| - info_bubble::kAlignLeftEdgeToAnchorEdge];
|
| - }
|
| - [errorBubbleController_ setAnchorPoint:ui::ConvertPointFromWindowToScreen(
|
| - parentWindow, anchorPoint)];
|
| -
|
| - errorBubbleAnchorView_ = field;
|
| - [errorBubbleController_ showWindow:self];
|
| -}
|
| -
|
| -- (void)hideErrorBubble {
|
| - [errorBubbleController_ close];
|
| -}
|
| -
|
| -- (void)updateMessageForField:(NSControl<AutofillInputField>*)field {
|
| - // Ignore fields that are not first responder. Testing this is a bit
|
| - // convoluted, since for NSTextFields with firstResponder status, the
|
| - // firstResponder is a subview of the NSTextField, not the field itself.
|
| - NSView* firstResponderView =
|
| - base::mac::ObjCCast<NSView>([[field window] firstResponder]);
|
| - if (![firstResponderView isDescendantOf:field])
|
| - return;
|
| - if (!delegate_->ShouldShowErrorBubble()) {
|
| - DCHECK(!errorBubbleController_);
|
| - return;
|
| - }
|
| -
|
| - if ([field invalid]) {
|
| - [self showErrorBubbleForField:field];
|
| - } else {
|
| - [errorBubbleController_ close];
|
| - }
|
| -}
|
| -
|
| -- (NSView*)firstEditableFieldMatchingBlock:(FieldFilterBlock)predicateBlock {
|
| - base::scoped_nsobject<NSMutableArray> fields([[NSMutableArray alloc] init]);
|
| -
|
| - for (AutofillSectionContainer* details in details_.get()) {
|
| - if (![[details view] isHidden])
|
| - [details addInputsToArray:fields];
|
| - }
|
| -
|
| - NSPoint selectedFieldOrigin = NSZeroPoint;
|
| - NSView* selectedField = nil;
|
| - for (NSControl<AutofillInputField>* field in fields.get()) {
|
| - if (!base::mac::ObjCCast<NSControl>(field))
|
| - continue;
|
| - if (![field conformsToProtocol:@protocol(AutofillInputField)])
|
| - continue;
|
| - if ([field isHiddenOrHasHiddenAncestor])
|
| - continue;
|
| - if (![field isEnabled])
|
| - continue;
|
| - if (![field canBecomeKeyView])
|
| - continue;
|
| - if (!predicateBlock(field))
|
| - continue;
|
| -
|
| - NSPoint fieldOrigin = [field convertPoint:[field bounds].origin toView:nil];
|
| - if (fieldOrigin.y < selectedFieldOrigin.y)
|
| - continue;
|
| - if (fieldOrigin.y == selectedFieldOrigin.y &&
|
| - fieldOrigin.x > selectedFieldOrigin.x) {
|
| - continue;
|
| - }
|
| -
|
| - selectedField = field;
|
| - selectedFieldOrigin = fieldOrigin;
|
| - }
|
| -
|
| - return selectedField;
|
| -}
|
| -
|
| -@end
|
|
|