- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / cocoa / autofill / autofill_details_container.mm
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
3 // found in the LICENSE file.
4
5 #import "chrome/browser/ui/cocoa/autofill/autofill_details_container.h"
6
7 #include <algorithm>
8
9 #include "base/mac/foundation_util.h"
10 #include "chrome/browser/ui/autofill/autofill_dialog_view_delegate.h"
11 #import "chrome/browser/ui/cocoa/autofill/autofill_section_container.h"
12 #import "chrome/browser/ui/cocoa/info_bubble_view.h"
13 #include "skia/ext/skia_utils_mac.h"
14
15 namespace {
16
17 // Imported constant from Views version. TODO(groby): Share.
18 SkColor const kWarningColor = 0xffde4932;  // SkColorSetRGB(0xde, 0x49, 0x32);
19
20 }  // namespace
21
22 @interface AutofillDetailsContainer ()
23 // Compute infobubble origin based on anchor/view.
24 - (NSPoint)originFromAnchorView:(NSView*)view;
25 @end
26
27 @implementation AutofillDetailsContainer
28
29 - (id)initWithDelegate:(autofill::AutofillDialogViewDelegate*)delegate {
30   if (self = [super init]) {
31     delegate_ = delegate;
32   }
33   return self;
34 }
35
36 - (void)addSection:(autofill::DialogSection)section {
37   base::scoped_nsobject<AutofillSectionContainer> sectionContainer(
38       [[AutofillSectionContainer alloc] initWithDelegate:delegate_
39                                                 forSection:section]);
40   [sectionContainer setValidationDelegate:self];
41   [details_ addObject:sectionContainer];
42 }
43
44 - (void)loadView {
45   details_.reset([[NSMutableArray alloc] init]);
46
47   [self addSection:autofill::SECTION_CC];
48   [self addSection:autofill::SECTION_BILLING];
49   [self addSection:autofill::SECTION_CC_BILLING];
50   [self addSection:autofill::SECTION_SHIPPING];
51
52   scrollView_.reset([[NSScrollView alloc] initWithFrame:NSZeroRect]);
53   [scrollView_ setHasVerticalScroller:YES];
54   [scrollView_ setHasHorizontalScroller:NO];
55   [scrollView_ setBorderType:NSNoBorder];
56   [scrollView_ setAutohidesScrollers:YES];
57   [self setView:scrollView_];
58
59   [scrollView_ setDocumentView:[[NSView alloc] initWithFrame:NSZeroRect]];
60
61   for (AutofillSectionContainer* container in details_.get())
62     [[scrollView_ documentView] addSubview:[container view]];
63
64   errorBubble_.reset([[InfoBubbleView alloc] initWithFrame:NSZeroRect]);
65   [errorBubble_ setBackgroundColor:
66       gfx::SkColorToCalibratedNSColor(kWarningColor)];
67   [errorBubble_ setArrowLocation:info_bubble::kTopCenter];
68   [errorBubble_ setAlignment:info_bubble::kAlignArrowToAnchor];
69   [errorBubble_ setHidden:YES];
70
71   base::scoped_nsobject<NSTextField> label([[NSTextField alloc] init]);
72   [label setEditable:NO];
73   [label setBordered:NO];
74   [label setDrawsBackground:NO];
75   [label setTextColor:[NSColor whiteColor]];
76   [errorBubble_ addSubview:label];
77
78   [[scrollView_ documentView] addSubview:errorBubble_];
79
80   [self performLayout];
81 }
82
83 - (NSSize)preferredSize {
84   NSSize size = NSZeroSize;
85   for (AutofillSectionContainer* container in details_.get()) {
86     NSSize containerSize = [container preferredSize];
87     size.height += containerSize.height;
88     size.width = std::max(containerSize.width, size.width);
89   }
90   return size;
91 }
92
93 - (void)performLayout {
94   NSRect rect = NSZeroRect;
95   for (AutofillSectionContainer* container in
96       [details_ reverseObjectEnumerator]) {
97     if (![[container view] isHidden]) {
98       [container performLayout];
99       [[container view] setFrameOrigin:NSMakePoint(0, NSMaxY(rect))];
100       rect = NSUnionRect(rect, [[container view] frame]);
101     }
102   }
103
104   [[scrollView_ documentView] setFrameSize:[self preferredSize]];
105 }
106
107 - (AutofillSectionContainer*)sectionForId:(autofill::DialogSection)section {
108   for (AutofillSectionContainer* details in details_.get()) {
109     if ([details section] == section)
110       return details;
111   }
112   return nil;
113 }
114
115 - (void)modelChanged {
116   for (AutofillSectionContainer* details in details_.get())
117     [details modelChanged];
118 }
119
120 - (BOOL)validate {
121   bool allValid = true;
122   for (AutofillSectionContainer* details in details_.get()) {
123     if (![[details view] isHidden])
124       allValid = [details validateFor:autofill::VALIDATE_FINAL] && allValid;
125   }
126   return allValid;
127 }
128
129 - (void)updateErrorBubble {
130   if (!delegate_->ShouldShowErrorBubble())
131     [errorBubble_ setHidden:YES];
132 }
133
134 // TODO(groby): Unify with BaseBubbleController's originFromAnchor:view:.
135 - (NSPoint)originFromAnchorView:(NSView*)view {
136   // All math done in window coordinates, since views might be flipped.
137   NSRect viewRect = [view convertRect:[view bounds] toView:nil];
138   NSPoint anchorPoint =
139       NSMakePoint(NSMidX(viewRect), NSMinY(viewRect));
140   NSRect bubbleRect = [errorBubble_ convertRect:[errorBubble_ bounds]
141                                          toView:nil];
142   NSPoint bubbleOrigin = NSMakePoint(anchorPoint.x - NSWidth(bubbleRect) / 2.0,
143                                      anchorPoint.y - NSHeight(bubbleRect));
144   return [[errorBubble_ superview] convertPoint:bubbleOrigin fromView:nil];
145 }
146
147 - (void)updateMessageForField:(NSControl<AutofillInputField>*)field {
148   // Ignore fields that are not first responder. Testing this is a bit
149   // convoluted, since for NSTextFields with firstResponder status, the
150   // firstResponder is a subview of the NSTextField, not the field itself.
151   NSView* firstResponderView =
152       base::mac::ObjCCast<NSView>([[field window] firstResponder]);
153   if (![firstResponderView isDescendantOf:field])
154     return;
155
156   if (!delegate_->ShouldShowErrorBubble()) {
157     DCHECK([errorBubble_ isHidden]);
158     return;
159   }
160
161   if ([field invalid]) {
162     const CGFloat labelInset = 3.0;
163
164     NSTextField* label = [[errorBubble_ subviews] objectAtIndex:0];
165     [label setStringValue:[field validityMessage]];
166     [label sizeToFit];
167     NSSize bubbleSize = [label frame].size;
168     bubbleSize.width += 2 * labelInset;
169     bubbleSize.height += 2 * labelInset + info_bubble::kBubbleArrowHeight;
170     [errorBubble_ setFrameSize:bubbleSize];
171     [label setFrameOrigin:NSMakePoint(labelInset, labelInset)];
172     [errorBubble_ setFrameOrigin:[self originFromAnchorView:field]];
173     [errorBubble_ setHidden:NO];
174   } else {
175     [errorBubble_ setHidden:YES];
176   }
177 }
178
179 @end