manage_passwords_bubble_pending_view_controller.mm revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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.
4
5#include <algorithm>
6
7#import "chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_pending_view_controller.h"
8
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"
18
19using namespace password_manager::mac::ui;
20
21@interface ManagePasswordsBubblePendingViewController ()
22- (void)onSaveClicked:(id)sender;
23- (void)onNopeClicked:(id)sender;
24- (void)onNeverForThisSiteClicked:(id)sender;
25@end
26
27@implementation ManagePasswordsBubblePendingViewController
28
29- (id)initWithModel:(ManagePasswordsBubbleModel*)model
30           delegate:(id<ManagePasswordsBubblePendingViewDelegate>)delegate {
31  if ((self = [super initWithNibName:nil bundle:nil])) {
32    model_ = model;
33    delegate_ = delegate;
34  }
35  return self;
36}
37
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];
43}
44
45- (void)onSaveClicked:(id)sender {
46  model_->OnSaveClicked();
47  [delegate_ viewShouldDismiss];
48}
49
50- (void)onNopeClicked:(id)sender {
51  model_->OnNopeClicked();
52  [delegate_ viewShouldDismiss];
53}
54
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];
60  } else {
61    [delegate_ passwordShouldNeverBeSavedOnSiteWithExistingPasswords];
62  }
63}
64
65- (void)loadView {
66  self.view = [[[NSView alloc] initWithFrame:NSZeroRect] autorelease];
67
68  // -----------------------------------
69  // |  Title                          |
70  // |  -----------------------------  | (1 px border)
71  // |    username   password          |
72  // |  -----------------------------  | (1 px border)
73  // |                [Save] [Nope v]  |
74  // -----------------------------------
75
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.
79
80  // Create the elements and add them to the view.
81
82  // Title.
83  NSTextField* titleLabel =
84      [self addTitleLabel:base::SysUTF16ToNSString(model_->title())];
85
86  // Password item.
87  // It should be at least as wide as the box without the padding.
88  passwordItem_.reset([[ManagePasswordItemViewController alloc]
89      initWithModel:model_
90       passwordForm:model_->pending_credentials()
91           position:password_manager::ui::FIRST_ITEM]);
92  NSView* password = [passwordItem_ view];
93  [self.view addSubview:password];
94
95  // Save button.
96  saveButton_.reset(
97      [[self addButton:l10n_util::GetNSString(IDS_PASSWORD_MANAGER_SAVE_BUTTON)
98                target:self
99                action:@selector(onSaveClicked:)] retain]);
100
101  // Nope combobox.
102  SavePasswordRefusalComboboxModel comboboxModel;
103  nopeButton_.reset([[BubbleCombobox alloc] initWithFrame:NSZeroRect
104                                                pullsDown:YES
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:)];
114
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
117  // duplicates.
118  [nopeButton_ insertItemWithTitle:@"" atIndex:0];
119  [[nopeButton_ itemAtIndex:0]
120      setTitle:l10n_util::GetNSString(IDS_PASSWORD_MANAGER_CANCEL_BUTTON)];
121
122  [nopeButton_ sizeToFit];
123  [self.view addSubview:nopeButton_.get()];
124
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);
129
130  // Layout the elements, starting at the bottom and moving up.
131
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)];
137
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)];
143
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)];
148
149  // Title goes at the top after some padding.
150  curY = NSMaxY([password frame]) + kUnrelatedControlVerticalPadding;
151  [titleLabel setFrameOrigin:NSMakePoint(curX, curY)];
152
153  // Update the bubble size.
154  const CGFloat height = NSMaxY([titleLabel frame]) + kFramePadding;
155  [self.view setFrame:NSMakeRect(0, 0, width, height)];
156}
157
158@end
159
160@implementation ManagePasswordsBubblePendingViewController (Testing)
161
162- (NSButton*)saveButton {
163  return saveButton_.get();
164}
165
166- (BubbleCombobox*)nopeButton {
167  return nopeButton_.get();
168}
169
170@end
171