Index: chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_manage_view_controller.mm |
diff --git a/chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_manage_view_controller.mm b/chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_manage_view_controller.mm |
new file mode 100644 |
index 0000000000000000000000000000000000000000..4d4095366901af2eabd3a4a531f3bc9010db22db |
--- /dev/null |
+++ b/chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_manage_view_controller.mm |
@@ -0,0 +1,213 @@ |
+// Copyright 2014 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/passwords/manage_passwords_bubble_manage_view_controller.h" |
+ |
+#include <cmath> |
+ |
+#include "base/strings/sys_string_conversions.h" |
+#include "chrome/browser/ui/chrome_style.h" |
+#import "chrome/browser/ui/cocoa/passwords/manage_password_item_view_controller.h" |
+#include "chrome/browser/ui/passwords/manage_passwords_bubble_model.h" |
+#include "grit/generated_resources.h" |
+#include "skia/ext/skia_utils_mac.h" |
+#import "third_party/google_toolbox_for_mac/src/AppKit/GTMUILocalizerAndLayoutTweaker.h" |
+#import "ui/base/cocoa/controls/hyperlink_button_cell.h" |
+#include "ui/base/l10n/l10n_util.h" |
+ |
+using namespace password_manager::mac::ui; |
+ |
+@implementation PasswordItemListView |
+- (id)initWithModel:(ManagePasswordsBubbleModel*)model { |
+ if ((self = [super initWithFrame:NSZeroRect])) { |
+ base::scoped_nsobject<NSMutableArray> items([[NSMutableArray alloc] init]); |
+ |
+ // Create and lay out the items. |
+ const CGFloat curX = 0; |
+ CGFloat maxX = 0; |
+ CGFloat curY = 0; |
+ for (autofill::ConstPasswordFormMap::const_reverse_iterator i = |
+ model->best_matches().rbegin(); |
+ i != model->best_matches().rend(); |
+ ++i) { |
+ autofill::PasswordForm form = *i->second; |
+ password_manager::ui::PasswordItemPosition position = |
+ (&(*i) == &(*model->best_matches().begin())) |
+ ? password_manager::ui::FIRST_ITEM |
+ : password_manager::ui::SUBSEQUENT_ITEM; |
+ base::scoped_nsobject<ManagePasswordItemViewController> item( |
+ [[ManagePasswordItemViewController alloc] initWithModel:model |
+ passwordForm:form |
+ position:position]); |
+ [items addObject:item.get()]; |
+ NSView* itemView = [item view]; |
+ [self addSubview:itemView]; |
+ |
+ // The items stack up on each other. |
+ [itemView setFrameOrigin:NSMakePoint(curX, curY)]; |
+ maxX = NSMaxX([itemView frame]); |
+ curY = NSMaxY([itemView frame]); |
+ } |
+ [self setFrameSize:NSMakeSize(maxX, curY)]; |
+ itemViews_.reset(items.release()); |
+ } |
+ return self; |
+} |
+@end |
+ |
+@implementation PasswordItemListView (Testing) |
+- (NSArray*)itemViews { |
+ return itemViews_.get(); |
+} |
+@end |
+ |
+@implementation NoPasswordsView |
+- (id)initWithWidth:(CGFloat)width { |
+ if ((self = [super initWithFrame:NSZeroRect])) { |
+ [self setEditable:NO]; |
+ [self setSelectable:NO]; |
+ [self setDrawsBackground:NO]; |
+ [self setBezeled:NO]; |
+ [self setStringValue:l10n_util::GetNSString( |
+ IDS_MANAGE_PASSWORDS_NO_PASSWORDS)]; |
+ [self setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; |
+ [[self cell] setWraps:YES]; |
+ [self setFrameSize:NSMakeSize(width, MAXFLOAT)]; |
+ [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:self]; |
+ } |
+ return self; |
+} |
+@end |
+ |
+@interface ManagePasswordsBubbleManageViewController () |
+- (void)onDoneClicked:(id)sender; |
+- (void)onManageClicked:(id)sender; |
+@end |
+ |
+@implementation ManagePasswordsBubbleManageViewController |
+ |
+- (id)initWithModel:(ManagePasswordsBubbleModel*)model |
+ delegate:(id<ManagePasswordsBubbleContentViewDelegate>)delegate { |
+ if ((self = [super initWithNibName:nil bundle:nil])) { |
+ model_ = model; |
+ delegate_ = delegate; |
+ } |
+ return self; |
+} |
+ |
+- (void)loadView { |
+ self.view = [[[NSView alloc] initWithFrame:NSZeroRect] autorelease]; |
+ |
+ // ----------------------------------- |
+ // | Title | |
+ // | ----------------------------- | (1 px border) |
+ // | username password x | |
+ // | ----------------------------- | (1 px border) |
+ // | username password x | |
+ // | ----------------------------- | (1 px border) |
+ // | username password x | |
+ // | ----------------------------- | (1 px border) |
+ // | Manage [Done] | |
+ // ----------------------------------- |
+ |
+ // The bubble should be wide enough to fit the title row, the username and |
+ // password rows, and the buttons row on one line each, but not smaller than |
+ // kDesiredBubbleWidth. |
+ |
+ // Create the elements and add them to the view. |
+ NSTextField* titleLabel = |
+ [self addTitleLabel:base::SysUTF16ToNSString(model_->title())]; |
+ |
+ // Content. If we have a list of passwords to store for the current site, we |
+ // display them to the user for management. Otherwise, we show a "No passwords |
+ // for this site" message. |
+ if (model_->best_matches().empty()) { |
+ const CGFloat noPasswordsWidth = std::max( |
+ kDesiredBubbleWidth - 2 * kFramePadding, NSWidth([titleLabel frame])); |
+ contentView_.reset( |
+ [[NoPasswordsView alloc] initWithWidth:noPasswordsWidth]); |
+ } else { |
+ contentView_.reset( |
+ [[PasswordItemListView alloc] initWithModel:model_]); |
+ } |
+ [self.view addSubview:contentView_]; |
+ DCHECK_GE(NSWidth([contentView_ frame]), NSWidth([titleLabel frame])); |
+ |
+ // Done button. |
+ doneButton_.reset([[self addButton:l10n_util::GetNSString(IDS_DONE) |
+ target:self |
+ action:@selector(onDoneClicked:)] retain]); |
+ |
+ // Manage button. |
+ manageButton_.reset([[NSButton alloc] initWithFrame:NSZeroRect]); |
+ base::scoped_nsobject<HyperlinkButtonCell> cell([[HyperlinkButtonCell alloc] |
+ initTextCell:base::SysUTF16ToNSString(model_->manage_link())]); |
+ [cell setControlSize:NSSmallControlSize]; |
+ [cell setShouldUnderline:NO]; |
+ [cell setUnderlineOnHover:NO]; |
+ [cell setTextColor:gfx::SkColorToCalibratedNSColor( |
+ chrome_style::GetLinkColor())]; |
+ [manageButton_ setCell:cell.get()]; |
+ [manageButton_ sizeToFit]; |
+ [manageButton_ setTarget:self]; |
+ [manageButton_ setAction:@selector(onManageClicked:)]; |
+ [self.view addSubview:manageButton_]; |
+ |
+ // Layout the elements, starting at the bottom and moving up. |
+ |
+ // The Done button goes in the bottom-right corner. |
+ const CGFloat width = 2 * kFramePadding + NSWidth([contentView_ frame]); |
+ CGFloat curX = width - kFramePadding - NSWidth([doneButton_ frame]); |
+ CGFloat curY = kFramePadding; |
+ [doneButton_ setFrameOrigin:NSMakePoint(curX, curY)]; |
+ |
+ // The Manage button goes in the bottom-left corner, centered vertically with |
+ // the Done button. |
+ curX = kFramePadding; |
+ const CGFloat diffY = std::ceil( |
+ (NSHeight([doneButton_ frame]) - NSHeight([manageButton_ frame])) / 2.0); |
+ [manageButton_ setFrameOrigin:NSMakePoint(curX, curY + diffY)]; |
+ |
+ // The content goes above the button row. |
+ curX = kFramePadding; |
+ curY = NSMaxY([doneButton_ frame]) + kUnrelatedControlVerticalPadding; |
+ [contentView_ setFrameOrigin:NSMakePoint(curX, curY)]; |
+ |
+ // The title goes above the content. |
+ curY = NSMaxY([contentView_ frame]) + kUnrelatedControlVerticalPadding; |
+ [titleLabel setFrameOrigin:NSMakePoint(curX, curY)]; |
+ |
+ curX = NSMaxX([contentView_ frame]) + kFramePadding; |
+ curY = NSMaxY([titleLabel frame]) + kFramePadding; |
+ DCHECK_EQ(width, curX); |
+ [self.view setFrameSize:NSMakeSize(curX, curY)]; |
+} |
+ |
+- (void)onDoneClicked:(id)sender { |
+ model_->OnDoneClicked(); |
+ [delegate_ viewShouldDismiss]; |
+} |
+ |
+- (void)onManageClicked:(id)sender { |
+ model_->OnManageLinkClicked(); |
+ [delegate_ viewShouldDismiss]; |
+} |
+ |
+@end |
+ |
+@implementation ManagePasswordsBubbleManageViewController (Testing) |
+ |
+- (NSButton*)doneButton { |
+ return doneButton_.get(); |
+} |
+ |
+- (NSButton*)manageButton { |
+ return manageButton_.get(); |
+} |
+ |
+- (NSView*)contentView { |
+ return contentView_.get(); |
+} |
+ |
+@end |