one_click_signin_view_controller.mm revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
1// Copyright (c) 2012 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#import "chrome/browser/ui/cocoa/one_click_signin_view_controller.h" 6 7#include "base/callback_helpers.h" 8#include "base/logging.h" 9#include "base/mac/bundle_locations.h" 10#import "chrome/browser/ui/chrome_style.h" 11#import "chrome/browser/ui/cocoa/hyperlink_text_view.h" 12#include "chrome/browser/ui/sync/one_click_signin_helper.h" 13#include "chrome/browser/ui/sync/one_click_signin_histogram.h" 14#include "chrome/common/url_constants.h" 15#include "content/public/browser/web_contents.h" 16#include "grit/chromium_strings.h" 17#include "grit/generated_resources.h" 18#include "skia/ext/skia_utils_mac.h" 19#import "third_party/google_toolbox_for_mac/src/AppKit/GTMUILocalizerAndLayoutTweaker.h" 20#include "ui/base/l10n/l10n_util_mac.h" 21 22namespace { 23 24// The margin between the top edge of the border and the error message text in 25// the sign-in bubble, in the case of an error. 26const CGFloat kTopErrorMessageMargin = 12; 27 28// Shift the origin of |view|'s frame by the given amount in the 29// positive y direction (up). 30void ShiftOriginY(NSView* view, CGFloat amount) { 31 NSPoint origin = [view frame].origin; 32 origin.y += amount; 33 [view setFrameOrigin:origin]; 34} 35 36} // namespace 37 38@interface OneClickSigninViewController () 39- (CGFloat)initializeInformativeTextView; 40- (void)close; 41@end 42 43@implementation OneClickSigninViewController 44 45 46- (id)initWithNibName:(NSString*)nibName 47 webContents:(content::WebContents*)webContents 48 syncCallback:(const BrowserWindow::StartSyncCallback&)syncCallback 49 closeCallback:(const base::Closure&)closeCallback 50 isSyncDialog:(BOOL)isSyncDialog 51 email:(const base::string16&)email 52 errorMessage:(NSString*)errorMessage { 53 if ((self = [super initWithNibName:nibName 54 bundle:base::mac::FrameworkBundle()])) { 55 webContents_ = webContents; 56 startSyncCallback_ = syncCallback; 57 closeCallback_ = closeCallback; 58 isSyncDialog_ = isSyncDialog; 59 clickedLearnMore_ = NO; 60 email_ = email; 61 errorMessage_.reset([errorMessage retain]); 62 if (isSyncDialog_) 63 DCHECK(!startSyncCallback_.is_null()); 64 } 65 return self; 66} 67 68- (void)viewWillClose { 69 // This is usually called after a click handler has initiated sync 70 // and has reset the callback. However, in the case that we are closing 71 // the window and nothing else has initiated the sync, we must do so here 72 if (isSyncDialog_ && !startSyncCallback_.is_null()) { 73 base::ResetAndReturn(&startSyncCallback_).Run( 74 OneClickSigninSyncStarter::UNDO_SYNC); 75 } 76} 77 78- (IBAction)ok:(id)sender { 79 if (isSyncDialog_) { 80 OneClickSigninHelper::LogConfirmHistogramValue( 81 clickedLearnMore_ ? 82 one_click_signin::HISTOGRAM_CONFIRM_LEARN_MORE_OK : 83 one_click_signin::HISTOGRAM_CONFIRM_OK); 84 85 base::ResetAndReturn(&startSyncCallback_).Run( 86 OneClickSigninSyncStarter::SYNC_WITH_DEFAULT_SETTINGS); 87 } 88 [self close]; 89} 90 91- (IBAction)onClickUndo:(id)sender { 92 if (isSyncDialog_) { 93 OneClickSigninHelper::LogConfirmHistogramValue( 94 clickedLearnMore_ ? 95 one_click_signin::HISTOGRAM_CONFIRM_LEARN_MORE_UNDO : 96 one_click_signin::HISTOGRAM_CONFIRM_UNDO); 97 98 base::ResetAndReturn(&startSyncCallback_).Run( 99 OneClickSigninSyncStarter::UNDO_SYNC); 100 } 101 [self close]; 102} 103 104- (IBAction)onClickAdvancedLink:(id)sender { 105 if (isSyncDialog_) { 106 OneClickSigninHelper::LogConfirmHistogramValue( 107 clickedLearnMore_ ? 108 one_click_signin::HISTOGRAM_CONFIRM_LEARN_MORE_ADVANCED : 109 one_click_signin::HISTOGRAM_CONFIRM_ADVANCED); 110 111 base::ResetAndReturn(&startSyncCallback_).Run( 112 OneClickSigninSyncStarter::CONFIGURE_SYNC_FIRST); 113 } 114 else { 115 content::OpenURLParams params(GURL(chrome::kChromeUISettingsURL), 116 content::Referrer(), CURRENT_TAB, 117 content::PAGE_TRANSITION_LINK, false); 118 webContents_->OpenURL(params); 119 } 120 [self close]; 121} 122 123- (IBAction)onClickClose:(id)sender { 124 if (isSyncDialog_) { 125 OneClickSigninHelper::LogConfirmHistogramValue( 126 clickedLearnMore_ ? 127 one_click_signin::HISTOGRAM_CONFIRM_LEARN_MORE_CLOSE : 128 one_click_signin::HISTOGRAM_CONFIRM_CLOSE); 129 130 base::ResetAndReturn(&startSyncCallback_).Run( 131 OneClickSigninSyncStarter::UNDO_SYNC); 132 } 133 [self close]; 134} 135 136- (void)awakeFromNib { 137 // Lay out the text controls from the bottom up. 138 CGFloat totalYOffset = 0.0; 139 140 if ([errorMessage_ length] == 0) { 141 totalYOffset += 142 [GTMUILocalizerAndLayoutTweaker sizeToFitView:advancedLink_].height; 143 [[advancedLink_ cell] setTextColor: 144 gfx::SkColorToCalibratedNSColor(chrome_style::GetLinkColor())]; 145 } else { 146 // Don't display the advanced link for the error bubble. 147 // To align the Learn More link with the OK button, we need to offset by 148 // the height of the Advanced link, plus the padding between it and the 149 // Learn More link above. 150 float advancedLinkHeightPlusPadding = 151 [informativePlaceholderTextField_ frame].origin.y - 152 [advancedLink_ frame].origin.y; 153 154 totalYOffset -= advancedLinkHeightPlusPadding; 155 [advancedLink_ removeFromSuperview]; 156 } 157 158 if (informativePlaceholderTextField_) { 159 if (!isSyncDialog_ && ([errorMessage_ length] != 0)) { 160 // Move up the "Learn more" origin in error case to account for the 161 // smaller bubble. 162 NSRect frame = [informativePlaceholderTextField_ frame]; 163 frame = NSOffsetRect(frame, 0, NSHeight([titleTextField_ frame])); 164 [informativePlaceholderTextField_ setFrame:frame]; 165 } 166 167 ShiftOriginY(informativePlaceholderTextField_, totalYOffset); 168 totalYOffset += [self initializeInformativeTextView]; 169 } 170 171 ShiftOriginY(messageTextField_, totalYOffset); 172 totalYOffset += 173 [GTMUILocalizerAndLayoutTweaker 174 sizeToFitFixedWidthTextField:messageTextField_]; 175 176 ShiftOriginY(titleTextField_, totalYOffset); 177 totalYOffset += 178 [GTMUILocalizerAndLayoutTweaker 179 sizeToFitFixedWidthTextField:titleTextField_]; 180 181 NSSize delta = NSMakeSize(0.0, totalYOffset); 182 183 if (isSyncDialog_) { 184 [messageTextField_ setStringValue:l10n_util::GetNSStringWithFixup( 185 IDS_ONE_CLICK_SIGNIN_DIALOG_TITLE_NEW)]; 186 } else if ([errorMessage_ length] != 0) { 187 [titleTextField_ setHidden:YES]; 188 [messageTextField_ setStringValue:errorMessage_]; 189 190 // Make the bubble less tall, as the title text will be hidden. 191 NSSize size = [[self view] frame].size; 192 size.height = size.height - NSHeight([titleTextField_ frame]); 193 [[self view] setFrameSize:size]; 194 195 // Shift the message text up to where the title text used to be. 196 NSPoint origin = [titleTextField_ frame].origin; 197 [messageTextField_ setFrameOrigin:origin]; 198 ShiftOriginY(messageTextField_, -kTopErrorMessageMargin); 199 200 // Use "OK" instead of "OK, got it" in the error case, and size the button 201 // accordingly. 202 [closeButton_ setTitle:l10n_util::GetNSStringWithFixup( 203 IDS_OK)]; 204 [GTMUILocalizerAndLayoutTweaker sizeToFitView:[closeButton_ superview]]; 205 } 206 207 // Resize bubble and window to hold the controls. 208 [GTMUILocalizerAndLayoutTweaker 209 resizeViewWithoutAutoResizingSubViews:[self view] 210 delta:delta]; 211 212 if (isSyncDialog_) { 213 OneClickSigninHelper::LogConfirmHistogramValue( 214 one_click_signin::HISTOGRAM_CONFIRM_SHOWN); 215 } 216} 217 218- (CGFloat)initializeInformativeTextView { 219 NSRect oldFrame = [informativePlaceholderTextField_ frame]; 220 221 // Replace the placeholder NSTextField with the real label NSTextView. The 222 // former doesn't show links in a nice way, but the latter can't be added in 223 // a xib without a containing scroll view, so create the NSTextView 224 // programmatically. 225 informativeTextView_.reset( 226 [[HyperlinkTextView alloc] initWithFrame:oldFrame]); 227 [informativeTextView_.get() setAutoresizingMask: 228 [informativePlaceholderTextField_ autoresizingMask]]; 229 [informativeTextView_.get() setDelegate:self]; 230 231 // Set the text. 232 NSString* learnMoreText = l10n_util::GetNSStringWithFixup(IDS_LEARN_MORE); 233 NSString* messageText; 234 235 ui::ResourceBundle::FontStyle fontStyle = isSyncDialog_ ? 236 chrome_style::kTextFontStyle : ui::ResourceBundle::SmallFont; 237 NSFont* font = ui::ResourceBundle::GetSharedInstance().GetFont( 238 fontStyle).GetNativeFont(); 239 240 // The non-modal bubble already has a text content and only needs the 241 // Learn More link (in a smaller font). 242 if (isSyncDialog_) { 243 messageText = l10n_util::GetNSStringFWithFixup( 244 IDS_ONE_CLICK_SIGNIN_DIALOG_MESSAGE_NEW, email_); 245 messageText = [messageText stringByAppendingString:@" "]; 246 } else { 247 messageText = @""; 248 } 249 250 NSColor* linkColor = 251 gfx::SkColorToCalibratedNSColor(chrome_style::GetLinkColor()); 252 [informativeTextView_ setMessageAndLink:messageText 253 withLink:learnMoreText 254 atOffset:[messageText length] 255 font:font 256 messageColor:[NSColor blackColor] 257 linkColor:linkColor]; 258 259 260 // Make the "Advanced" link font as large as the "Learn More" link. 261 [[advancedLink_ cell] setFont:font]; 262 [advancedLink_ sizeToFit]; 263 264 // Size to fit. 265 [[informativePlaceholderTextField_ cell] setAttributedStringValue: 266 [informativeTextView_ attributedString]]; 267 [GTMUILocalizerAndLayoutTweaker 268 sizeToFitFixedWidthTextField:informativePlaceholderTextField_]; 269 NSRect newFrame = [informativePlaceholderTextField_ frame]; 270 [informativeTextView_ setFrame:newFrame]; 271 272 // Swap placeholder. 273 [[informativePlaceholderTextField_ superview] 274 replaceSubview:informativePlaceholderTextField_ 275 with:informativeTextView_.get()]; 276 informativePlaceholderTextField_ = nil; // Now released. 277 278 return NSHeight(newFrame) - NSHeight(oldFrame); 279} 280 281- (BOOL)textView:(NSTextView*)textView 282 clickedOnLink:(id)link 283 atIndex:(NSUInteger)charIndex { 284 if (isSyncDialog_ && !clickedLearnMore_) { 285 clickedLearnMore_ = YES; 286 287 OneClickSigninHelper::LogConfirmHistogramValue( 288 one_click_signin::HISTOGRAM_CONFIRM_LEARN_MORE); 289 } 290 WindowOpenDisposition location = isSyncDialog_ ? 291 NEW_WINDOW : NEW_FOREGROUND_TAB; 292 content::OpenURLParams params(GURL(chrome::kChromeSyncLearnMoreURL), 293 content::Referrer(), location, 294 content::PAGE_TRANSITION_LINK, false); 295 webContents_->OpenURL(params); 296 return YES; 297} 298 299- (void)close { 300 base::ResetAndReturn(&closeCallback_).Run(); 301} 302 303@end 304