Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / cocoa / passwords / manage_password_item_view_controller.mm
index 143f605..eaab4fc 100644 (file)
@@ -7,16 +7,68 @@
 #include "base/logging.h"
 #include "base/strings/string16.h"
 #include "base/strings/sys_string_conversions.h"
+#include "chrome/browser/ui/chrome_style.h"
+#import "chrome/browser/ui/cocoa/passwords/manage_passwords_bubble_content_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"
-#include "ui/native_theme/native_theme.h"
+#import "ui/base/cocoa/controls/hyperlink_button_cell.h"
+#import "ui/base/cocoa/hover_image_button.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/gfx/image/image.h"
+#include "ui/native_theme/common_theme.h"
+#include "ui/resources/grit/ui_resources.h"
 #include "ui/views/layout/layout_constants.h"
 
+using namespace password_manager::mac::ui;
+
 namespace {
 
-// TODO(dconnelly): Figure out how to share all these constants.
-static const CGFloat kBorderWidth = 1;
-static const CGFloat kFramePadding = 16;
+const CGFloat kBorderWidth = 1;
+const SkColor kHoverColor = SkColorSetARGBInline(0xFF, 0xEB, 0xEB, 0xEB);
+
+NSColor* HoverColor() {
+  return gfx::SkColorToCalibratedNSColor(kHoverColor);
+}
+
+NSFont* LabelFont() {
+  return [NSFont systemFontOfSize:[NSFont smallSystemFontSize]];
+}
+
+NSSize LabelSize(int resourceID) {
+  return [l10n_util::GetNSString(resourceID)
+      sizeWithAttributes:@{NSFontAttributeName : LabelFont()}];
+}
+
+CGFloat FirstFieldWidth() {
+  const CGFloat undoExplanationWidth =
+      LabelSize(IDS_MANAGE_PASSWORDS_DELETED).width;
+  const CGFloat kUsernameWidth =
+      ManagePasswordsBubbleModel::UsernameFieldWidth();
+  const CGFloat width = std::max(kUsernameWidth, undoExplanationWidth);
+  return width;
+}
+
+CGFloat SecondFieldWidth() {
+  const CGFloat undoLinkWidth =
+      LabelSize(IDS_MANAGE_PASSWORDS_UNDO).width;
+  const CGFloat kPasswordWidth =
+      ManagePasswordsBubbleModel::PasswordFieldWidth();
+  const CGFloat width = std::max(kPasswordWidth, undoLinkWidth);
+  return width;
+}
+
+CGFloat ItemWidth() {
+  const CGFloat width =
+      kFramePadding +
+      FirstFieldWidth() +
+      views::kItemLabelSpacing +
+      SecondFieldWidth() +
+      views::kItemLabelSpacing +
+      chrome_style::GetCloseButtonSize() +
+      kFramePadding;
+  return width;
+}
 
 void InitLabel(NSTextField* textField, const base::string16& text) {
   [textField setStringValue:base::SysUTF16ToNSString(text)];
@@ -24,34 +76,156 @@ void InitLabel(NSTextField* textField, const base::string16& text) {
   [textField setSelectable:NO];
   [textField setDrawsBackground:NO];
   [textField setBezeled:NO];
-  NSFont* font = [NSFont systemFontOfSize:[NSFont smallSystemFontSize]];
-  [textField setFont:font];
+  [textField setFont:LabelFont()];
   [textField sizeToFit];
-  // TODO(dconnelly): Handle max width.
 }
 
-NSTextField* UsernameLabel(const base::string16& text) {
+NSTextField* Label(const base::string16& text) {
   base::scoped_nsobject<NSTextField> textField(
       [[NSTextField alloc] initWithFrame:NSZeroRect]);
   InitLabel(textField, text);
   return textField.autorelease();
 }
 
+NSTextField* UsernameLabel(const base::string16& text) {
+  NSTextField* textField = Label(text);
+  [textField
+      setFrameSize:NSMakeSize(FirstFieldWidth(), NSHeight([textField frame]))];
+  return textField;
+}
+
 NSSecureTextField* PasswordLabel(const base::string16& text) {
   base::scoped_nsobject<NSSecureTextField> textField(
       [[NSSecureTextField alloc] initWithFrame:NSZeroRect]);
   InitLabel(textField, text);
+  [textField
+      setFrameSize:NSMakeSize(SecondFieldWidth(), NSHeight([textField frame]))];
   return textField.autorelease();
 }
 
 }  // namespace
 
+@implementation ManagePasswordItemUndoView
+- (id)initWithTarget:(id)target action:(SEL)action {
+  if ((self = [super init])) {
+    // The button should look like a link.
+    undoButton_.reset([[NSButton alloc] initWithFrame:NSZeroRect]);
+    base::scoped_nsobject<HyperlinkButtonCell> cell([[HyperlinkButtonCell alloc]
+        initTextCell:l10n_util::GetNSString(IDS_MANAGE_PASSWORDS_UNDO)]);
+    [cell setControlSize:NSSmallControlSize];
+    [cell setShouldUnderline:NO];
+    [cell setUnderlineOnHover:NO];
+    [cell setTextColor:gfx::SkColorToCalibratedNSColor(
+        chrome_style::GetLinkColor())];
+    [undoButton_ setCell:cell.get()];
+    [undoButton_ sizeToFit];
+    [undoButton_ setTarget:target];
+    [undoButton_ setAction:action];
+
+    const CGFloat width = ItemWidth();
+    CGFloat curX = kFramePadding;
+    CGFloat curY = views::kRelatedControlVerticalSpacing;
+
+    // Add the explanation text.
+    NSTextField* label =
+        Label(l10n_util::GetStringUTF16(IDS_MANAGE_PASSWORDS_DELETED));
+    [label setFrameOrigin:NSMakePoint(curX, curY)];
+    [self addSubview:label];
+
+    // The undo button should be right-aligned.
+    curX = width - kFramePadding - NSWidth([undoButton_ frame]);
+    [undoButton_ setFrameOrigin:NSMakePoint(curX, curY)];
+    [self addSubview:undoButton_ ];
+
+    // Move to the top-right of the delete button.
+    curX = NSMaxX([undoButton_ frame]) + kFramePadding;
+    curY = NSMaxY([undoButton_ frame]) + views::kRelatedControlVerticalSpacing;
+
+    // Update the frame.
+    DCHECK_EQ(width, curX);
+    [self setFrameSize:NSMakeSize(curX, curY)];
+  }
+  return self;
+}
+@end
+
+@implementation ManagePasswordItemUndoView (Testing)
+- (NSButton*)undoButton {
+  return undoButton_.get();
+}
+@end
+
+@implementation ManagePasswordItemManageView
+- (id)initWithForm:(const autofill::PasswordForm&)form
+            target:(id)target
+            action:(SEL)action {
+  if ((self = [super init])) {
+    deleteButton_.reset([[HoverImageButton alloc] initWithFrame:NSZeroRect]);
+    [deleteButton_ setFrameSize:NSMakeSize(chrome_style::GetCloseButtonSize(),
+                                           chrome_style::GetCloseButtonSize())];
+    [deleteButton_ setBordered:NO];
+    [[deleteButton_ cell] setHighlightsBy:NSNoCellMask];
+    ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
+    [deleteButton_
+        setDefaultImage:bundle.GetImageNamed(IDR_CLOSE_2).ToNSImage()];
+    [deleteButton_
+        setHoverImage:bundle.GetImageNamed(IDR_CLOSE_2_H).ToNSImage()];
+    [deleteButton_
+        setPressedImage:bundle.GetImageNamed(IDR_CLOSE_2_P).ToNSImage()];
+    [deleteButton_ setTarget:target];
+    [deleteButton_ setAction:action];
+
+    const CGFloat width = ItemWidth();
+    CGFloat curX = kFramePadding;
+    CGFloat curY = views::kRelatedControlVerticalSpacing;
+
+    // Add the username.
+    usernameField_.reset([UsernameLabel(form.username_value) retain]);
+    [usernameField_ setFrameOrigin:NSMakePoint(curX, curY)];
+    [self addSubview:usernameField_];
+
+    // Move to the right of the username and add the password.
+    curX = NSMaxX([usernameField_ frame]) + views::kItemLabelSpacing;
+    passwordField_.reset([PasswordLabel(form.password_value) retain]);
+    [passwordField_ setFrameOrigin:NSMakePoint(curX, curY)];
+    [self addSubview:passwordField_];
+
+    // The delete button should be right-aligned.
+    curX = width - kFramePadding - NSWidth([deleteButton_ frame]);
+    [deleteButton_ setFrameOrigin:NSMakePoint(curX, curY)];
+    [self addSubview:deleteButton_];
+
+    // Move to the top-right of the delete button.
+    curX = NSMaxX([deleteButton_ frame]) + kFramePadding;
+    curY =
+        NSMaxY([deleteButton_ frame]) + views::kRelatedControlVerticalSpacing;
+
+    // Update the frame.
+    DCHECK_EQ(width, curX);
+    [self setFrameSize:NSMakeSize(curX, curY)];
+  }
+  return self;
+}
+@end
+
+@implementation ManagePasswordItemManageView (Testing)
+- (NSTextField*)usernameField {
+  return usernameField_.get();
+}
+- (NSSecureTextField*)passwordField {
+  return passwordField_.get();
+}
+- (NSButton*)deleteButton {
+  return deleteButton_.get();
+}
+@end
+
 @implementation ManagePasswordItemPendingView
 
 - (id)initWithForm:(const autofill::PasswordForm&)form {
   if ((self = [super initWithFrame:NSZeroRect])) {
-    CGFloat curX = 0;
-    CGFloat curY = 0;
+    CGFloat curX = kFramePadding;
+    CGFloat curY = views::kRelatedControlVerticalSpacing;
 
     // Add the username.
     usernameField_.reset([UsernameLabel(form.username_value) retain]);
@@ -59,17 +233,17 @@ NSSecureTextField* PasswordLabel(const base::string16& text) {
     [self addSubview:usernameField_];
 
     // Move to the right of the username and add the password.
-    curX += NSWidth([usernameField_ frame]) + views::kItemLabelSpacing;
+    curX = NSMaxX([usernameField_ frame]) + views::kItemLabelSpacing;
     passwordField_.reset([PasswordLabel(form.password_value) retain]);
     [passwordField_ setFrameOrigin:NSMakePoint(curX, curY)];
     [self addSubview:passwordField_];
 
     // Move to the top-right of the password.
-    curX = NSMaxX([passwordField_ frame]);
-    curY = NSMaxY([passwordField_ frame]);
+    curY =
+        NSMaxY([passwordField_ frame]) + views::kRelatedControlVerticalSpacing;
 
     // Update the frame.
-    [self setFrameSize:NSMakeSize(curX, curY)];
+    [self setFrameSize:NSMakeSize(ItemWidth(), curY)];
   }
   return self;
 }
@@ -88,58 +262,93 @@ NSSecureTextField* PasswordLabel(const base::string16& text) {
 
 @end
 
+@interface ManagePasswordItemViewController ()
+- (void)onDeleteClicked:(id)sender;
+- (void)onUndoClicked:(id)sender;
+
+// Find the next content view and repaint.
+- (void)refresh;
+
+// Find the next content view.
+- (void)updateContent;
+
+// Repaint the content.
+- (void)layoutContent;
+@end
+
 @implementation ManagePasswordItemViewController
 
 - (id)initWithModel:(ManagePasswordsBubbleModel*)model
-           position:(password_manager::ui::PasswordItemPosition)position
-           minWidth:(CGFloat)minWidth {
+       passwordForm:(const autofill::PasswordForm&)passwordForm
+           position:(password_manager::ui::PasswordItemPosition)position {
   if ((self = [super initWithNibName:nil bundle:nil])) {
     model_ = model;
     position_ = position;
-    minWidth_ = minWidth;
+    passwordForm_ = passwordForm;
     state_ = password_manager::ui::IsPendingState(model_->state())
         ? MANAGE_PASSWORD_ITEM_STATE_PENDING
         : MANAGE_PASSWORD_ITEM_STATE_MANAGE;
-    switch (state_) {
-      default:
-        NOTREACHED();
-      case MANAGE_PASSWORD_ITEM_STATE_PENDING:
-        contentView_.reset([[ManagePasswordItemPendingView alloc]
-            initWithForm:model_->pending_credentials()]);
-        break;
-      case MANAGE_PASSWORD_ITEM_STATE_MANAGE:
-        NOTIMPLEMENTED();
-        break;
-      case MANAGE_PASSWORD_ITEM_STATE_DELETED:
-        NOTIMPLEMENTED();
-        break;
-    };
+    [self updateContent];
   }
   return self;
 }
 
-- (void)loadView {
-  self.view = [[[NSView alloc] initWithFrame:NSZeroRect] autorelease];
-  [self.view addSubview:contentView_];
+- (void)onDeleteClicked:(id)sender {
+  DCHECK_EQ(MANAGE_PASSWORD_ITEM_STATE_MANAGE, state_);
+  state_ = MANAGE_PASSWORD_ITEM_STATE_DELETED;
+  [self refresh];
+  model_->OnPasswordAction(passwordForm_,
+                           ManagePasswordsBubbleModel::REMOVE_PASSWORD);
+}
+
+- (void)onUndoClicked:(id)sender {
+  DCHECK_EQ(MANAGE_PASSWORD_ITEM_STATE_DELETED, state_);
+  state_ = MANAGE_PASSWORD_ITEM_STATE_MANAGE;
+  [self refresh];
+  model_->OnPasswordAction(passwordForm_,
+                           ManagePasswordsBubbleModel::ADD_PASSWORD);
+}
 
-  // Update the view size according to the content view size, expanding if
-  // necessary to fill the min width.
+- (void)refresh {
+  [self updateContent];
+  [self layoutContent];
+}
+
+- (void)updateContent {
+  switch (state_) {
+    default:
+      NOTREACHED();
+    case MANAGE_PASSWORD_ITEM_STATE_PENDING:
+      contentView_.reset(
+          [[ManagePasswordItemPendingView alloc] initWithForm:passwordForm_]);
+      return;
+    case MANAGE_PASSWORD_ITEM_STATE_MANAGE:
+      contentView_.reset([[ManagePasswordItemManageView alloc]
+          initWithForm:passwordForm_
+                target:self
+                action:@selector(onDeleteClicked:)]);
+      return;
+    case MANAGE_PASSWORD_ITEM_STATE_DELETED:
+      contentView_.reset([[ManagePasswordItemUndoView alloc]
+          initWithTarget:self
+                  action:@selector(onUndoClicked:)]);
+      return;
+  };
+}
+
+- (void)layoutContent {
+  // Update the view size according to the content view size.
   const NSSize contentSize = [contentView_ frame].size;
-  const CGFloat width =
-      std::max(contentSize.width + 2 * kFramePadding, minWidth_);
-  const CGFloat height =
-      contentSize.height + 2 * views::kRelatedControlVerticalSpacing;
-  [self.view setFrameSize:NSMakeSize(width, height)];
+  [self.view setFrameSize:contentSize];
 
-  // Position the content view with some padding in the center of the view.
-  [contentView_
-      setFrameOrigin:NSMakePoint(kFramePadding,
-                                 views::kRelatedControlVerticalSpacing)];
+  // Add the content.
+  [self.view setSubviews:@[ contentView_ ]];
 
   // Add the borders, which go along the entire view.
-  CGColorRef borderColor =
-      gfx::CGColorCreateFromSkColor(ui::NativeTheme::instance()->GetSystemColor(
-          ui::NativeTheme::kColorId_EnabledMenuButtonBorderColor));
+  SkColor borderSkColor;
+  ui::CommonThemeGetSystemColor(
+      ui::NativeTheme::kColorId_EnabledMenuButtonBorderColor, &borderSkColor);
+  CGColorRef borderColor = gfx::CGColorCreateFromSkColor(borderSkColor);
 
   // Mac views don't have backing layers by default.
   base::scoped_nsobject<CALayer> rootLayer([[CALayer alloc] init]);
@@ -151,18 +360,25 @@ NSSecureTextField* PasswordLabel(const base::string16& text) {
   if (position_ == password_manager::ui::FIRST_ITEM) {
     base::scoped_nsobject<CALayer> topBorder([[CALayer alloc] init]);
     [topBorder setBackgroundColor:borderColor];
-    [topBorder
-        setFrame:CGRectMake(0, height - kBorderWidth, width, kBorderWidth)];
+    [topBorder setFrame:CGRectMake(0,
+                                   contentSize.height - kBorderWidth,
+                                   contentSize.width,
+                                   kBorderWidth)];
     [self.view.layer addSublayer:topBorder];
   }
 
   // The bottom border is always present.
   base::scoped_nsobject<CALayer> bottomBorder([[CALayer alloc] init]);
   [bottomBorder setBackgroundColor:borderColor];
-  [bottomBorder setFrame:CGRectMake(0, 0, width, kBorderWidth)];
+  [bottomBorder setFrame:CGRectMake(0, 0, contentSize.width, kBorderWidth)];
   [self.view.layer addSublayer:bottomBorder];
 }
 
+- (void)loadView {
+  self.view = [[[NSView alloc] initWithFrame:NSZeroRect] autorelease];
+  [self layoutContent];
+}
+
 @end
 
 @implementation ManagePasswordItemViewController (Testing)
@@ -175,4 +391,43 @@ NSSecureTextField* PasswordLabel(const base::string16& text) {
   return contentView_.get();
 }
 
+- (autofill::PasswordForm)passwordForm {
+  return passwordForm_;
+}
+
+@end
+
+@implementation ManagePasswordItemClickableView
+
+- (void)drawRect:(NSRect)dirtyRect {
+  [super drawRect:dirtyRect];
+  if (hovering_) {
+    [HoverColor() setFill];
+    NSRectFill(dirtyRect);
+  }
+}
+
+- (void)mouseEntered:(NSEvent*)event {
+  hovering_ = YES;
+  [self setNeedsDisplay:YES];
+}
+
+- (void)mouseExited:(NSEvent*)event {
+  hovering_ = NO;
+  [self setNeedsDisplay:YES];
+}
+
+- (void)updateTrackingAreas {
+  [super updateTrackingAreas];
+  if (trackingArea_.get())
+    [self removeTrackingArea:trackingArea_.get()];
+  NSTrackingAreaOptions options =
+      NSTrackingMouseEnteredAndExited | NSTrackingActiveInKeyWindow;
+  trackingArea_.reset([[CrTrackingArea alloc] initWithRect:[self bounds]
+                                                   options:options
+                                                     owner:self
+                                                  userInfo:nil]);
+  [self addTrackingArea:trackingArea_.get()];
+}
+
 @end