1 // Copyright 2014 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.
5 #import "chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_manage_view_controller.h"
9 #include "base/strings/sys_string_conversions.h"
10 #include "chrome/browser/ui/chrome_style.h"
11 #import "chrome/browser/ui/cocoa/passwords/manage_password_item_view_controller.h"
12 #include "chrome/browser/ui/passwords/manage_passwords_bubble_model.h"
13 #include "grit/generated_resources.h"
14 #include "skia/ext/skia_utils_mac.h"
15 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMUILocalizerAndLayoutTweaker.h"
16 #import "ui/base/cocoa/controls/hyperlink_button_cell.h"
17 #include "ui/base/l10n/l10n_util.h"
19 using namespace password_manager::mac::ui;
21 @implementation PasswordItemListView
22 - (id)initWithModel:(ManagePasswordsBubbleModel*)model {
23 if ((self = [super initWithFrame:NSZeroRect])) {
24 base::scoped_nsobject<NSMutableArray> items([[NSMutableArray alloc] init]);
26 // Create and lay out the items.
27 const CGFloat curX = 0;
30 for (autofill::ConstPasswordFormMap::const_reverse_iterator i =
31 model->best_matches().rbegin();
32 i != model->best_matches().rend();
34 autofill::PasswordForm form = *i->second;
35 password_manager::ui::PasswordItemPosition position =
36 (&(*i) == &(*model->best_matches().begin()))
37 ? password_manager::ui::FIRST_ITEM
38 : password_manager::ui::SUBSEQUENT_ITEM;
39 base::scoped_nsobject<ManagePasswordItemViewController> item(
40 [[ManagePasswordItemViewController alloc] initWithModel:model
43 [items addObject:item.get()];
44 NSView* itemView = [item view];
45 [self addSubview:itemView];
47 // The items stack up on each other.
48 [itemView setFrameOrigin:NSMakePoint(curX, curY)];
49 maxX = NSMaxX([itemView frame]);
50 curY = NSMaxY([itemView frame]);
52 [self setFrameSize:NSMakeSize(maxX, curY)];
53 itemViews_.reset(items.release());
59 @implementation PasswordItemListView (Testing)
60 - (NSArray*)itemViews {
61 return itemViews_.get();
65 @implementation NoPasswordsView
66 - (id)initWithWidth:(CGFloat)width {
67 if ((self = [super initWithFrame:NSZeroRect])) {
68 [self setEditable:NO];
69 [self setSelectable:NO];
70 [self setDrawsBackground:NO];
72 [self setStringValue:l10n_util::GetNSString(
73 IDS_MANAGE_PASSWORDS_NO_PASSWORDS)];
74 [self setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]];
75 [[self cell] setWraps:YES];
76 [self setFrameSize:NSMakeSize(width, MAXFLOAT)];
77 [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:self];
83 @interface ManagePasswordsBubbleManageViewController ()
84 - (void)onDoneClicked:(id)sender;
85 - (void)onManageClicked:(id)sender;
88 @implementation ManagePasswordsBubbleManageViewController
90 - (id)initWithModel:(ManagePasswordsBubbleModel*)model
91 delegate:(id<ManagePasswordsBubbleContentViewDelegate>)delegate {
92 if ((self = [super initWithNibName:nil bundle:nil])) {
100 self.view = [[[NSView alloc] initWithFrame:NSZeroRect] autorelease];
102 // -----------------------------------
104 // | ----------------------------- | (1 px border)
105 // | username password x |
106 // | ----------------------------- | (1 px border)
107 // | username password x |
108 // | ----------------------------- | (1 px border)
109 // | username password x |
110 // | ----------------------------- | (1 px border)
112 // -----------------------------------
114 // The bubble should be wide enough to fit the title row, the username and
115 // password rows, and the buttons row on one line each, but not smaller than
116 // kDesiredBubbleWidth.
118 // Create the elements and add them to the view.
119 NSTextField* titleLabel =
120 [self addTitleLabel:base::SysUTF16ToNSString(model_->title())];
122 // Content. If we have a list of passwords to store for the current site, we
123 // display them to the user for management. Otherwise, we show a "No passwords
124 // for this site" message.
125 if (model_->best_matches().empty()) {
126 const CGFloat noPasswordsWidth = std::max(
127 kDesiredBubbleWidth - 2 * kFramePadding, NSWidth([titleLabel frame]));
129 [[NoPasswordsView alloc] initWithWidth:noPasswordsWidth]);
132 [[PasswordItemListView alloc] initWithModel:model_]);
134 [self.view addSubview:contentView_];
135 DCHECK_GE(NSWidth([contentView_ frame]), NSWidth([titleLabel frame]));
138 doneButton_.reset([[self addButton:l10n_util::GetNSString(IDS_DONE)
140 action:@selector(onDoneClicked:)] retain]);
143 manageButton_.reset([[NSButton alloc] initWithFrame:NSZeroRect]);
144 base::scoped_nsobject<HyperlinkButtonCell> cell([[HyperlinkButtonCell alloc]
145 initTextCell:base::SysUTF16ToNSString(model_->manage_link())]);
146 [cell setControlSize:NSSmallControlSize];
147 [cell setShouldUnderline:NO];
148 [cell setUnderlineOnHover:NO];
149 [cell setTextColor:gfx::SkColorToCalibratedNSColor(
150 chrome_style::GetLinkColor())];
151 [manageButton_ setCell:cell.get()];
152 [manageButton_ sizeToFit];
153 [manageButton_ setTarget:self];
154 [manageButton_ setAction:@selector(onManageClicked:)];
155 [self.view addSubview:manageButton_];
157 // Layout the elements, starting at the bottom and moving up.
159 // The Done button goes in the bottom-right corner.
160 const CGFloat width = 2 * kFramePadding + NSWidth([contentView_ frame]);
161 CGFloat curX = width - kFramePadding - NSWidth([doneButton_ frame]);
162 CGFloat curY = kFramePadding;
163 [doneButton_ setFrameOrigin:NSMakePoint(curX, curY)];
165 // The Manage button goes in the bottom-left corner, centered vertically with
167 curX = kFramePadding;
168 const CGFloat diffY = std::ceil(
169 (NSHeight([doneButton_ frame]) - NSHeight([manageButton_ frame])) / 2.0);
170 [manageButton_ setFrameOrigin:NSMakePoint(curX, curY + diffY)];
172 // The content goes above the button row.
173 curX = kFramePadding;
174 curY = NSMaxY([doneButton_ frame]) + kUnrelatedControlVerticalPadding;
175 [contentView_ setFrameOrigin:NSMakePoint(curX, curY)];
177 // The title goes above the content.
178 curY = NSMaxY([contentView_ frame]) + kUnrelatedControlVerticalPadding;
179 [titleLabel setFrameOrigin:NSMakePoint(curX, curY)];
181 curX = NSMaxX([contentView_ frame]) + kFramePadding;
182 curY = NSMaxY([titleLabel frame]) + kFramePadding;
183 DCHECK_EQ(width, curX);
184 [self.view setFrameSize:NSMakeSize(curX, curY)];
187 - (void)onDoneClicked:(id)sender {
188 model_->OnDoneClicked();
189 [delegate_ viewShouldDismiss];
192 - (void)onManageClicked:(id)sender {
193 model_->OnManageLinkClicked();
194 [delegate_ viewShouldDismiss];
199 @implementation ManagePasswordsBubbleManageViewController (Testing)
201 - (NSButton*)doneButton {
202 return doneButton_.get();
205 - (NSButton*)manageButton {
206 return manageButton_.get();
209 - (NSView*)contentView {
210 return contentView_.get();