// Copyright (c) 2012 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/one_click_signin_view_controller.h" #include "base/callback_helpers.h" #include "base/logging.h" #include "base/mac/bundle_locations.h" #import "chrome/browser/ui/chrome_style.h" #import "chrome/browser/ui/cocoa/hyperlink_text_view.h" #include "chrome/browser/ui/sync/one_click_signin_helper.h" #include "chrome/browser/ui/sync/one_click_signin_histogram.h" #include "chrome/common/url_constants.h" #include "content/public/browser/web_contents.h" #include "grit/chromium_strings.h" #include "grit/generated_resources.h" #include "skia/ext/skia_utils_mac.h" #import "third_party/google_toolbox_for_mac/src/AppKit/GTMUILocalizerAndLayoutTweaker.h" #include "ui/base/l10n/l10n_util_mac.h" namespace { // The margin between the top edge of the border and the error message text in // the sign-in bubble, in the case of an error. const CGFloat kTopErrorMessageMargin = 12; // Shift the origin of |view|'s frame by the given amount in the // positive y direction (up). void ShiftOriginY(NSView* view, CGFloat amount) { NSPoint origin = [view frame].origin; origin.y += amount; [view setFrameOrigin:origin]; } } // namespace @interface OneClickSigninViewController () - (CGFloat)initializeInformativeTextView; - (void)close; @end @implementation OneClickSigninViewController - (id)initWithNibName:(NSString*)nibName webContents:(content::WebContents*)webContents syncCallback:(const BrowserWindow::StartSyncCallback&)syncCallback closeCallback:(const base::Closure&)closeCallback isSyncDialog:(BOOL)isSyncDialog email:(const base::string16&)email errorMessage:(NSString*)errorMessage { if ((self = [super initWithNibName:nibName bundle:base::mac::FrameworkBundle()])) { webContents_ = webContents; startSyncCallback_ = syncCallback; closeCallback_ = closeCallback; isSyncDialog_ = isSyncDialog; clickedLearnMore_ = NO; email_ = email; errorMessage_.reset([errorMessage retain]); if (isSyncDialog_) DCHECK(!startSyncCallback_.is_null()); } return self; } - (void)viewWillClose { // This is usually called after a click handler has initiated sync // and has reset the callback. However, in the case that we are closing // the window and nothing else has initiated the sync, we must do so here if (isSyncDialog_ && !startSyncCallback_.is_null()) { base::ResetAndReturn(&startSyncCallback_).Run( OneClickSigninSyncStarter::UNDO_SYNC); } } - (IBAction)ok:(id)sender { if (isSyncDialog_) { OneClickSigninHelper::LogConfirmHistogramValue( clickedLearnMore_ ? one_click_signin::HISTOGRAM_CONFIRM_LEARN_MORE_OK : one_click_signin::HISTOGRAM_CONFIRM_OK); base::ResetAndReturn(&startSyncCallback_).Run( OneClickSigninSyncStarter::SYNC_WITH_DEFAULT_SETTINGS); } [self close]; } - (IBAction)onClickUndo:(id)sender { if (isSyncDialog_) { OneClickSigninHelper::LogConfirmHistogramValue( clickedLearnMore_ ? one_click_signin::HISTOGRAM_CONFIRM_LEARN_MORE_UNDO : one_click_signin::HISTOGRAM_CONFIRM_UNDO); base::ResetAndReturn(&startSyncCallback_).Run( OneClickSigninSyncStarter::UNDO_SYNC); } [self close]; } - (IBAction)onClickAdvancedLink:(id)sender { if (isSyncDialog_) { OneClickSigninHelper::LogConfirmHistogramValue( clickedLearnMore_ ? one_click_signin::HISTOGRAM_CONFIRM_LEARN_MORE_ADVANCED : one_click_signin::HISTOGRAM_CONFIRM_ADVANCED); base::ResetAndReturn(&startSyncCallback_).Run( OneClickSigninSyncStarter::CONFIGURE_SYNC_FIRST); } else { content::OpenURLParams params(GURL(chrome::kChromeUISettingsURL), content::Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_LINK, false); webContents_->OpenURL(params); } [self close]; } - (IBAction)onClickClose:(id)sender { if (isSyncDialog_) { OneClickSigninHelper::LogConfirmHistogramValue( clickedLearnMore_ ? one_click_signin::HISTOGRAM_CONFIRM_LEARN_MORE_CLOSE : one_click_signin::HISTOGRAM_CONFIRM_CLOSE); base::ResetAndReturn(&startSyncCallback_).Run( OneClickSigninSyncStarter::UNDO_SYNC); } [self close]; } - (void)awakeFromNib { // Lay out the text controls from the bottom up. CGFloat totalYOffset = 0.0; if ([errorMessage_ length] == 0) { totalYOffset += [GTMUILocalizerAndLayoutTweaker sizeToFitView:advancedLink_].height; [[advancedLink_ cell] setTextColor: gfx::SkColorToCalibratedNSColor(chrome_style::GetLinkColor())]; } else { // Don't display the advanced link for the error bubble. // To align the Learn More link with the OK button, we need to offset by // the height of the Advanced link, plus the padding between it and the // Learn More link above. float advancedLinkHeightPlusPadding = [informativePlaceholderTextField_ frame].origin.y - [advancedLink_ frame].origin.y; totalYOffset -= advancedLinkHeightPlusPadding; [advancedLink_ removeFromSuperview]; } if (informativePlaceholderTextField_) { if (!isSyncDialog_ && ([errorMessage_ length] != 0)) { // Move up the "Learn more" origin in error case to account for the // smaller bubble. NSRect frame = [informativePlaceholderTextField_ frame]; frame = NSOffsetRect(frame, 0, NSHeight([titleTextField_ frame])); [informativePlaceholderTextField_ setFrame:frame]; } ShiftOriginY(informativePlaceholderTextField_, totalYOffset); totalYOffset += [self initializeInformativeTextView]; } ShiftOriginY(messageTextField_, totalYOffset); totalYOffset += [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:messageTextField_]; ShiftOriginY(titleTextField_, totalYOffset); totalYOffset += [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:titleTextField_]; NSSize delta = NSMakeSize(0.0, totalYOffset); if (isSyncDialog_) { [messageTextField_ setStringValue:l10n_util::GetNSStringWithFixup( IDS_ONE_CLICK_SIGNIN_DIALOG_TITLE_NEW)]; } else if ([errorMessage_ length] != 0) { [titleTextField_ setHidden:YES]; [messageTextField_ setStringValue:errorMessage_]; // Make the bubble less tall, as the title text will be hidden. NSSize size = [[self view] frame].size; size.height = size.height - NSHeight([titleTextField_ frame]); [[self view] setFrameSize:size]; // Shift the message text up to where the title text used to be. NSPoint origin = [titleTextField_ frame].origin; [messageTextField_ setFrameOrigin:origin]; ShiftOriginY(messageTextField_, -kTopErrorMessageMargin); // Use "OK" instead of "OK, got it" in the error case, and size the button // accordingly. [closeButton_ setTitle:l10n_util::GetNSStringWithFixup( IDS_OK)]; [GTMUILocalizerAndLayoutTweaker sizeToFitView:[closeButton_ superview]]; } // Resize bubble and window to hold the controls. [GTMUILocalizerAndLayoutTweaker resizeViewWithoutAutoResizingSubViews:[self view] delta:delta]; if (isSyncDialog_) { OneClickSigninHelper::LogConfirmHistogramValue( one_click_signin::HISTOGRAM_CONFIRM_SHOWN); } } - (CGFloat)initializeInformativeTextView { NSRect oldFrame = [informativePlaceholderTextField_ frame]; // Replace the placeholder NSTextField with the real label NSTextView. The // former doesn't show links in a nice way, but the latter can't be added in // a xib without a containing scroll view, so create the NSTextView // programmatically. informativeTextView_.reset( [[HyperlinkTextView alloc] initWithFrame:oldFrame]); [informativeTextView_.get() setAutoresizingMask: [informativePlaceholderTextField_ autoresizingMask]]; [informativeTextView_.get() setDelegate:self]; // Set the text. NSString* learnMoreText = l10n_util::GetNSStringWithFixup(IDS_LEARN_MORE); NSString* messageText; ui::ResourceBundle::FontStyle fontStyle = isSyncDialog_ ? chrome_style::kTextFontStyle : ui::ResourceBundle::SmallFont; NSFont* font = ui::ResourceBundle::GetSharedInstance().GetFont( fontStyle).GetNativeFont(); // The non-modal bubble already has a text content and only needs the // Learn More link (in a smaller font). if (isSyncDialog_) { messageText = l10n_util::GetNSStringFWithFixup( IDS_ONE_CLICK_SIGNIN_DIALOG_MESSAGE_NEW, email_); messageText = [messageText stringByAppendingString:@" "]; } else { messageText = @""; } NSColor* linkColor = gfx::SkColorToCalibratedNSColor(chrome_style::GetLinkColor()); [informativeTextView_ setMessageAndLink:messageText withLink:learnMoreText atOffset:[messageText length] font:font messageColor:[NSColor blackColor] linkColor:linkColor]; // Make the "Advanced" link font as large as the "Learn More" link. [[advancedLink_ cell] setFont:font]; [advancedLink_ sizeToFit]; // Size to fit. [[informativePlaceholderTextField_ cell] setAttributedStringValue: [informativeTextView_ attributedString]]; [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:informativePlaceholderTextField_]; NSRect newFrame = [informativePlaceholderTextField_ frame]; [informativeTextView_ setFrame:newFrame]; // Swap placeholder. [[informativePlaceholderTextField_ superview] replaceSubview:informativePlaceholderTextField_ with:informativeTextView_.get()]; informativePlaceholderTextField_ = nil; // Now released. return NSHeight(newFrame) - NSHeight(oldFrame); } - (BOOL)textView:(NSTextView*)textView clickedOnLink:(id)link atIndex:(NSUInteger)charIndex { if (isSyncDialog_ && !clickedLearnMore_) { clickedLearnMore_ = YES; OneClickSigninHelper::LogConfirmHistogramValue( one_click_signin::HISTOGRAM_CONFIRM_LEARN_MORE); } WindowOpenDisposition location = isSyncDialog_ ? NEW_WINDOW : NEW_FOREGROUND_TAB; content::OpenURLParams params(GURL(chrome::kChromeSyncLearnMoreURL), content::Referrer(), location, content::PAGE_TRANSITION_LINK, false); webContents_->OpenURL(params); return YES; } - (void)close { base::ResetAndReturn(&closeCallback_).Run(); } @end