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.
7 #import "chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_pending_view_controller.h"
9 #include "base/strings/sys_string_conversions.h"
10 #import "chrome/browser/ui/cocoa/bubble_combobox.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 "chrome/browser/ui/passwords/save_password_refusal_combobox_model.h"
14 #include "chrome/grit/generated_resources.h"
15 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMUILocalizerAndLayoutTweaker.h"
16 #include "ui/base/l10n/l10n_util.h"
17 #include "ui/base/models/combobox_model.h"
19 using namespace password_manager::mac::ui;
21 @interface ManagePasswordsBubblePendingViewController ()
22 - (void)onSaveClicked:(id)sender;
23 - (void)onNopeClicked:(id)sender;
24 - (void)onNeverForThisSiteClicked:(id)sender;
27 @implementation ManagePasswordsBubblePendingViewController
29 - (id)initWithModel:(ManagePasswordsBubbleModel*)model
30 delegate:(id<ManagePasswordsBubblePendingViewDelegate>)delegate {
31 if ((self = [super initWithNibName:nil bundle:nil])) {
38 - (void)bubbleWillDisappear {
39 // The "nope" drop-down won't be dismissed until the user chooses an option,
40 // but if the bubble is dismissed (by cross-platform code) before the user
41 // makes a choice, then the choice won't actually take any effect.
42 [[nopeButton_ menu] cancelTrackingWithoutAnimation];
45 - (void)onSaveClicked:(id)sender {
46 model_->OnSaveClicked();
47 [delegate_ viewShouldDismiss];
50 - (void)onNopeClicked:(id)sender {
51 model_->OnNopeClicked();
52 [delegate_ viewShouldDismiss];
55 - (void)onNeverForThisSiteClicked:(id)sender {
56 if (model_->best_matches().empty()) {
57 // Skip confirmation if there are no existing passwords for this site.
58 model_->OnNeverForThisSiteClicked();
59 [delegate_ viewShouldDismiss];
61 [delegate_ passwordShouldNeverBeSavedOnSiteWithExistingPasswords];
66 self.view = [[[NSView alloc] initWithFrame:NSZeroRect] autorelease];
68 // -----------------------------------
70 // | ----------------------------- | (1 px border)
71 // | username password |
72 // | ----------------------------- | (1 px border)
73 // | [Save] [Nope v] |
74 // -----------------------------------
76 // The bubble should be wide enough to fit the title row, the username and
77 // password row, and the buttons row on one line each, but not smaller than
78 // kDesiredBubbleWidth.
80 // Create the elements and add them to the view.
83 NSTextField* titleLabel =
84 [self addTitleLabel:base::SysUTF16ToNSString(model_->title())];
87 // It should be at least as wide as the box without the padding.
88 passwordItem_.reset([[ManagePasswordItemViewController alloc]
90 passwordForm:model_->pending_credentials()
91 position:password_manager::ui::FIRST_ITEM]);
92 NSView* password = [passwordItem_ view];
93 [self.view addSubview:password];
97 [[self addButton:l10n_util::GetNSString(IDS_PASSWORD_MANAGER_SAVE_BUTTON)
99 action:@selector(onSaveClicked:)] retain]);
102 SavePasswordRefusalComboboxModel comboboxModel;
103 nopeButton_.reset([[BubbleCombobox alloc] initWithFrame:NSZeroRect
105 model:&comboboxModel]);
106 NSMenuItem* nopeItem =
107 [nopeButton_ itemAtIndex:SavePasswordRefusalComboboxModel::INDEX_NOPE];
108 [nopeItem setTarget:self];
109 [nopeItem setAction:@selector(onNopeClicked:)];
110 NSMenuItem* neverItem = [nopeButton_
111 itemAtIndex:SavePasswordRefusalComboboxModel::INDEX_NEVER_FOR_THIS_SITE];
112 [neverItem setTarget:self];
113 [neverItem setAction:@selector(onNeverForThisSiteClicked:)];
115 // Insert a dummy row at the top and set its title to the desired title.
116 // Must be done in two steps to prevent NSPopUpButton from removing
118 [nopeButton_ insertItemWithTitle:@"" atIndex:0];
119 [[nopeButton_ itemAtIndex:0]
120 setTitle:l10n_util::GetNSString(IDS_PASSWORD_MANAGER_CANCEL_BUTTON)];
122 [nopeButton_ sizeToFit];
123 [self.view addSubview:nopeButton_.get()];
125 // Compute the bubble width using the password item.
126 const CGFloat contentWidth =
127 kFramePadding + NSWidth([password frame]) + kFramePadding;
128 const CGFloat width = std::max(kDesiredBubbleWidth, contentWidth);
130 // Layout the elements, starting at the bottom and moving up.
132 // Buttons go on the bottom row and are right-aligned.
133 // Start with [Save].
134 CGFloat curX = width - kFramePadding - NSWidth([saveButton_ frame]);
135 CGFloat curY = kFramePadding;
136 [saveButton_ setFrameOrigin:NSMakePoint(curX, curY)];
138 // [Save] goes to the left of [Nope].
139 curX -= kRelatedControlHorizontalPadding + NSWidth([nopeButton_ frame]);
140 curY = kFramePadding +
141 (NSHeight([saveButton_ frame]) - NSHeight([nopeButton_ frame])) / 2.0;
142 [nopeButton_ setFrameOrigin:NSMakePoint(curX, curY)];
144 // Password item goes on the next row and is shifted right.
145 curX = kFramePadding;
146 curY = NSMaxY([saveButton_ frame]) + kUnrelatedControlVerticalPadding;
147 [password setFrameOrigin:NSMakePoint(curX, curY)];
149 // Title goes at the top after some padding.
150 curY = NSMaxY([password frame]) + kUnrelatedControlVerticalPadding;
151 [titleLabel setFrameOrigin:NSMakePoint(curX, curY)];
153 // Update the bubble size.
154 const CGFloat height = NSMaxY([titleLabel frame]) + kFramePadding;
155 [self.view setFrame:NSMakeRect(0, 0, width, height)];
160 @implementation ManagePasswordsBubblePendingViewController (Testing)
162 - (NSButton*)saveButton {
163 return saveButton_.get();
166 - (BubbleCombobox*)nopeButton {
167 return nopeButton_.get();