| Index: chrome/browser/ui/cocoa/browser/profile_chooser_controller.mm
|
| diff --git a/chrome/browser/ui/cocoa/browser/profile_chooser_controller.mm b/chrome/browser/ui/cocoa/browser/profile_chooser_controller.mm
|
| deleted file mode 100644
|
| index 0c9e0f44e77fa4b7c913f092879c5b96e76c5ae9..0000000000000000000000000000000000000000
|
| --- a/chrome/browser/ui/cocoa/browser/profile_chooser_controller.mm
|
| +++ /dev/null
|
| @@ -1,1383 +0,0 @@
|
| -// Copyright 2013 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 <Cocoa/Cocoa.h>
|
| -
|
| -#import "chrome/browser/ui/cocoa/browser/profile_chooser_controller.h"
|
| -
|
| -#include "base/mac/bundle_locations.h"
|
| -#include "base/prefs/pref_service.h"
|
| -#include "base/strings/sys_string_conversions.h"
|
| -#include "base/strings/utf_string_conversions.h"
|
| -#include "chrome/browser/browser_process.h"
|
| -#include "chrome/browser/chrome_notification_types.h"
|
| -#include "chrome/browser/lifetime/application_lifetime.h"
|
| -#include "chrome/browser/profiles/avatar_menu.h"
|
| -#include "chrome/browser/profiles/avatar_menu_observer.h"
|
| -#include "chrome/browser/profiles/profile_info_cache.h"
|
| -#include "chrome/browser/profiles/profile_info_util.h"
|
| -#include "chrome/browser/profiles/profile_manager.h"
|
| -#include "chrome/browser/profiles/profile_metrics.h"
|
| -#include "chrome/browser/profiles/profile_window.h"
|
| -#include "chrome/browser/profiles/profiles_state.h"
|
| -#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
|
| -#include "chrome/browser/signin/signin_manager_factory.h"
|
| -#include "chrome/browser/signin/signin_promo.h"
|
| -#include "chrome/browser/ui/browser.h"
|
| -#include "chrome/browser/ui/browser_dialogs.h"
|
| -#include "chrome/browser/ui/browser_window.h"
|
| -#include "chrome/browser/ui/chrome_style.h"
|
| -#import "chrome/browser/ui/cocoa/info_bubble_view.h"
|
| -#import "chrome/browser/ui/cocoa/info_bubble_window.h"
|
| -#import "chrome/browser/ui/cocoa/user_manager_mac.h"
|
| -#include "chrome/browser/ui/singleton_tabs.h"
|
| -#include "chrome/common/pref_names.h"
|
| -#include "chrome/common/profile_management_switches.h"
|
| -#include "chrome/common/url_constants.h"
|
| -#include "components/signin/core/browser/mutable_profile_oauth2_token_service.h"
|
| -#include "components/signin/core/browser/profile_oauth2_token_service.h"
|
| -#include "components/signin/core/browser/signin_manager.h"
|
| -#include "content/public/browser/notification_service.h"
|
| -#include "content/public/browser/web_contents.h"
|
| -#include "content/public/browser/web_contents_view.h"
|
| -#include "google_apis/gaia/oauth2_token_service.h"
|
| -#include "grit/chromium_strings.h"
|
| -#include "grit/generated_resources.h"
|
| -#include "grit/theme_resources.h"
|
| -#include "skia/ext/skia_utils_mac.h"
|
| -#import "third_party/google_toolbox_for_mac/src/AppKit/GTMUILocalizerAndLayoutTweaker.h"
|
| -#import "ui/base/cocoa/cocoa_base_utils.h"
|
| -#import "ui/base/cocoa/controls/blue_label_button.h"
|
| -#import "ui/base/cocoa/controls/hyperlink_button_cell.h"
|
| -#import "ui/base/cocoa/hover_image_button.h"
|
| -#include "ui/base/cocoa/window_size_constants.h"
|
| -#include "ui/base/l10n/l10n_util.h"
|
| -#include "ui/base/l10n/l10n_util_mac.h"
|
| -#include "ui/base/resource/resource_bundle.h"
|
| -#include "ui/gfx/image/image.h"
|
| -#include "ui/gfx/text_elider.h"
|
| -#include "ui/native_theme/native_theme.h"
|
| -
|
| -namespace {
|
| -
|
| -// Constants taken from the Windows/Views implementation at:
|
| -// chrome/browser/ui/views/profile_chooser_view.cc
|
| -const int kLargeImageSide = 64;
|
| -const int kSmallImageSide = 32;
|
| -const CGFloat kFixedMenuWidth = 250;
|
| -
|
| -const CGFloat kVerticalSpacing = 20.0;
|
| -const CGFloat kSmallVerticalSpacing = 10.0;
|
| -const CGFloat kHorizontalSpacing = 20.0;
|
| -const CGFloat kTitleFontSize = 15.0;
|
| -const CGFloat kTextFontSize = 12.0;
|
| -const CGFloat kProfileButtonHeight = 30;
|
| -const int kOverlayHeight = 20; // Height of the "Change" avatar photo overlay.
|
| -const int kBezelThickness = 3; // Width of the bezel on an NSButton.
|
| -const int kImageTitleSpacing = 10;
|
| -const int kBlueButtonHeight = 30;
|
| -
|
| -// Fixed size for embedded sign in pages as defined in Gaia.
|
| -const CGFloat kFixedGaiaViewWidth = 360;
|
| -const CGFloat kFixedGaiaViewHeight = 400;
|
| -
|
| -// Fixed size for the account removal view.
|
| -const CGFloat kFixedAccountRemovalViewWidth = 280;
|
| -
|
| -// Maximum number of times to show the tutorial in the profile avatar bubble.
|
| -const int kProfileAvatarTutorialShowMax = 5;
|
| -
|
| -// The tag number for the primary account.
|
| -const int kPrimaryProfileTag = -1;
|
| -
|
| -gfx::Image CreateProfileImage(const gfx::Image& icon, int imageSize) {
|
| - return profiles::GetSizedAvatarIconWithBorder(
|
| - icon, true /* image is a square */,
|
| - imageSize + profiles::kAvatarIconPadding,
|
| - imageSize + profiles::kAvatarIconPadding);
|
| -}
|
| -
|
| -// Updates the window size and position.
|
| -void SetWindowSize(NSWindow* window, NSSize size) {
|
| - NSRect frame = [window frame];
|
| - frame.origin.x += frame.size.width - size.width;
|
| - frame.origin.y += frame.size.height - size.height;
|
| - frame.size = size;
|
| - [window setFrame:frame display:YES];
|
| -}
|
| -
|
| -NSString* ElideEmail(const std::string& email, CGFloat width) {
|
| - base::string16 elidedEmail = gfx::ElideEmail(
|
| - base::UTF8ToUTF16(email),
|
| - ui::ResourceBundle::GetSharedInstance().GetFontList(
|
| - ui::ResourceBundle::BaseFont),
|
| - width);
|
| - return base::SysUTF16ToNSString(elidedEmail);
|
| -}
|
| -
|
| -// Builds a label with the given |title| anchored at |frame_origin|. Sets the
|
| -// text color and background color to the given colors if not null.
|
| -NSTextField* BuildLabel(NSString* title,
|
| - NSPoint frame_origin,
|
| - NSColor* background_color,
|
| - NSColor* text_color) {
|
| - base::scoped_nsobject<NSTextField> label(
|
| - [[NSTextField alloc] initWithFrame:NSZeroRect]);
|
| - [label setStringValue:title];
|
| - [label setEditable:NO];
|
| - [label setAlignment:NSLeftTextAlignment];
|
| - [label setBezeled:NO];
|
| - [label setFont:[NSFont labelFontOfSize:kTextFontSize]];
|
| - [label setFrameOrigin:frame_origin];
|
| - [label sizeToFit];
|
| -
|
| - if (background_color) {
|
| - DCHECK(text_color);
|
| - [[label cell] setBackgroundColor:background_color];
|
| - [[label cell] setTextColor:text_color];
|
| - }
|
| - return label.autorelease();
|
| -}
|
| -
|
| -// Builds a title card with one back button right aligned and one label center
|
| -// aligned.
|
| -NSView* BuildTitleCard(NSRect frame_rect,
|
| - int message_id,
|
| - id back_button_target,
|
| - SEL back_button_action) {
|
| - base::scoped_nsobject<NSView> container(
|
| - [[NSView alloc] initWithFrame:frame_rect]);
|
| -
|
| - base::scoped_nsobject<HoverImageButton> button(
|
| - [[HoverImageButton alloc] initWithFrame:frame_rect]);
|
| - [button setBordered:NO];
|
| - ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
|
| - [button setDefaultImage:rb->GetNativeImageNamed(IDR_BACK).ToNSImage()];
|
| - [button setHoverImage:rb->GetNativeImageNamed(IDR_BACK_H).ToNSImage()];
|
| - [button setPressedImage:rb->GetNativeImageNamed(IDR_BACK_P).ToNSImage()];
|
| - [button setTarget:back_button_target];
|
| - [button setAction:back_button_action];
|
| - [button setFrameSize:NSMakeSize(kProfileButtonHeight, kProfileButtonHeight)];
|
| - [button setFrameOrigin:NSMakePoint(kHorizontalSpacing, 0)];
|
| -
|
| - NSTextField* title_label =
|
| - BuildLabel(l10n_util::GetNSString(message_id), NSZeroPoint,
|
| - nil /* background_color */, nil /* text_color */);
|
| - [title_label setAlignment:NSCenterTextAlignment];
|
| - [title_label setFont:[NSFont labelFontOfSize:kTitleFontSize]];
|
| - [title_label sizeToFit];
|
| - CGFloat x_offset = (frame_rect.size.width - NSWidth([title_label frame])) / 2;
|
| - [title_label setFrameOrigin:NSMakePoint(x_offset, 0)];
|
| -
|
| - [container addSubview:button];
|
| - [container addSubview:title_label];
|
| - CGFloat height = std::max(NSMaxY([title_label frame]),
|
| - NSMaxY([button frame])) + kSmallVerticalSpacing;
|
| - [container setFrameSize:NSMakeSize(NSWidth([container frame]), height)];
|
| -
|
| - return container.autorelease();
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| -// Class that listens to changes to the OAuth2Tokens for the active profile,
|
| -// changes to the avatar menu model or browser close notifications.
|
| -class ActiveProfileObserverBridge : public AvatarMenuObserver,
|
| - public content::NotificationObserver,
|
| - public OAuth2TokenService::Observer {
|
| - public:
|
| - ActiveProfileObserverBridge(ProfileChooserController* controller,
|
| - Browser* browser)
|
| - : controller_(controller),
|
| - browser_(browser),
|
| - token_observer_registered_(false) {
|
| - registrar_.Add(this, chrome::NOTIFICATION_BROWSER_CLOSING,
|
| - content::NotificationService::AllSources());
|
| - if (!browser_->profile()->IsGuestSession())
|
| - AddTokenServiceObserver();
|
| - }
|
| -
|
| - virtual ~ActiveProfileObserverBridge() {
|
| - RemoveTokenServiceObserver();
|
| - }
|
| -
|
| - private:
|
| - void AddTokenServiceObserver() {
|
| - ProfileOAuth2TokenService* oauth2_token_service =
|
| - ProfileOAuth2TokenServiceFactory::GetForProfile(browser_->profile());
|
| - DCHECK(oauth2_token_service);
|
| - oauth2_token_service->AddObserver(this);
|
| - token_observer_registered_ = true;
|
| - }
|
| -
|
| - void RemoveTokenServiceObserver() {
|
| - if (!token_observer_registered_)
|
| - return;
|
| - DCHECK(browser_->profile());
|
| - ProfileOAuth2TokenService* oauth2_token_service =
|
| - ProfileOAuth2TokenServiceFactory::GetForProfile(browser_->profile());
|
| - DCHECK(oauth2_token_service);
|
| - oauth2_token_service->RemoveObserver(this);
|
| - token_observer_registered_ = false;
|
| - }
|
| -
|
| - // OAuth2TokenService::Observer:
|
| - virtual void OnRefreshTokenAvailable(const std::string& account_id) OVERRIDE {
|
| - // Tokens can only be added by adding an account through the inline flow,
|
| - // which is started from the account management view. Refresh it to show the
|
| - // update.
|
| - BubbleViewMode viewMode = [controller_ viewMode];
|
| - if (viewMode == BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT ||
|
| - viewMode == BUBBLE_VIEW_MODE_GAIA_SIGNIN ||
|
| - viewMode == BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT) {
|
| - [controller_
|
| - initMenuContentsWithView:BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT];
|
| - }
|
| - }
|
| -
|
| - virtual void OnRefreshTokenRevoked(const std::string& account_id) OVERRIDE {
|
| - // Tokens can only be removed from the account management view. Refresh it
|
| - // to show the update.
|
| - if ([controller_ viewMode] == BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT)
|
| - [controller_
|
| - initMenuContentsWithView:BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT];
|
| - }
|
| -
|
| - // AvatarMenuObserver:
|
| - virtual void OnAvatarMenuChanged(AvatarMenu* avatar_menu) OVERRIDE {
|
| - // While the bubble is open, the avatar menu can only change from the
|
| - // profile chooser view by modifying the current profile's photo or name.
|
| - [controller_ initMenuContentsWithView:BUBBLE_VIEW_MODE_PROFILE_CHOOSER];
|
| - }
|
| -
|
| - // content::NotificationObserver:
|
| - virtual void Observe(
|
| - int type,
|
| - const content::NotificationSource& source,
|
| - const content::NotificationDetails& details) OVERRIDE {
|
| - DCHECK_EQ(chrome::NOTIFICATION_BROWSER_CLOSING, type);
|
| - if (browser_ == content::Source<Browser>(source).ptr()) {
|
| - RemoveTokenServiceObserver();
|
| - // Clean up the bubble's WebContents (used by the Gaia embedded view), to
|
| - // make sure the guest profile doesn't have any dangling host renderers.
|
| - // This can happen if Chrome is quit using Command-Q while the bubble is
|
| - // still open, which won't give the bubble a chance to be closed and
|
| - // clean up the WebContents itself.
|
| - [controller_ cleanUpEmbeddedViewContents];
|
| - }
|
| - }
|
| -
|
| - ProfileChooserController* controller_; // Weak; owns this.
|
| - Browser* browser_; // Weak.
|
| - content::NotificationRegistrar registrar_;
|
| -
|
| - // The observer can be removed both when closing the browser, and by just
|
| - // closing the avatar bubble. However, in the case of closing the browser,
|
| - // the avatar bubble will also be closed afterwards, resulting in a second
|
| - // attempt to remove the observer. This ensures the observer is only
|
| - // removed once.
|
| - bool token_observer_registered_;
|
| -
|
| - DISALLOW_COPY_AND_ASSIGN(ActiveProfileObserverBridge);
|
| -};
|
| -
|
| -// A custom button that has a transparent backround.
|
| -@interface TransparentBackgroundButton : NSButton
|
| -@end
|
| -
|
| -@implementation TransparentBackgroundButton
|
| -- (id)initWithFrame:(NSRect)frameRect {
|
| - if ((self = [super initWithFrame:frameRect])) {
|
| - [self setBordered:NO];
|
| - [self setFont:[NSFont labelFontOfSize:kTextFontSize]];
|
| - [self setButtonType:NSMomentaryChangeButton];
|
| - }
|
| - return self;
|
| -}
|
| -
|
| -- (void)drawRect:(NSRect)dirtyRect {
|
| - NSColor* backgroundColor = [NSColor colorWithCalibratedWhite:0 alpha:0.5f];
|
| - [backgroundColor setFill];
|
| - NSRectFillUsingOperation(dirtyRect, NSCompositeSourceAtop);
|
| - [super drawRect:dirtyRect];
|
| -}
|
| -@end
|
| -
|
| -// A custom image control that shows a "Change" button when moused over.
|
| -@interface EditableProfilePhoto : NSImageView {
|
| - @private
|
| - AvatarMenu* avatarMenu_; // Weak; Owned by ProfileChooserController.
|
| - base::scoped_nsobject<TransparentBackgroundButton> changePhotoButton_;
|
| - // Used to display the "Change" button on hover.
|
| - ui::ScopedCrTrackingArea trackingArea_;
|
| -}
|
| -
|
| -- (id)initWithFrame:(NSRect)frameRect
|
| - avatarMenu:(AvatarMenu*)avatarMenu
|
| - profileIcon:(const gfx::Image&)profileIcon
|
| - editingAllowed:(BOOL)editingAllowed;
|
| -
|
| -// Called when the "Change" button is clicked.
|
| -- (void)editPhoto:(id)sender;
|
| -
|
| -// When hovering over the profile photo, show the "Change" button.
|
| -- (void)mouseEntered:(NSEvent*)event;
|
| -
|
| -// When hovering away from the profile photo, hide the "Change" button.
|
| -- (void)mouseExited:(NSEvent*)event;
|
| -@end
|
| -
|
| -@interface EditableProfilePhoto (Private)
|
| -// Create the "Change" avatar photo button.
|
| -- (TransparentBackgroundButton*)changePhotoButtonWithRect:(NSRect)rect;
|
| -@end
|
| -
|
| -@implementation EditableProfilePhoto
|
| -- (id)initWithFrame:(NSRect)frameRect
|
| - avatarMenu:(AvatarMenu*)avatarMenu
|
| - profileIcon:(const gfx::Image&)profileIcon
|
| - editingAllowed:(BOOL)editingAllowed {
|
| - if ((self = [super initWithFrame:frameRect])) {
|
| - avatarMenu_ = avatarMenu;
|
| - [self setImage:CreateProfileImage(
|
| - profileIcon, kLargeImageSide).ToNSImage()];
|
| -
|
| - // Add a tracking area so that we can show/hide the button when hovering.
|
| - trackingArea_.reset([[CrTrackingArea alloc]
|
| - initWithRect:[self bounds]
|
| - options:NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways
|
| - owner:self
|
| - userInfo:nil]);
|
| - [self addTrackingArea:trackingArea_.get()];
|
| -
|
| - if (editingAllowed) {
|
| - // The avatar photo uses a frame of width profiles::kAvatarIconPadding,
|
| - // which we must subtract from the button's bounds.
|
| - changePhotoButton_.reset([self changePhotoButtonWithRect:NSMakeRect(
|
| - profiles::kAvatarIconPadding, profiles::kAvatarIconPadding,
|
| - kLargeImageSide - 2 * profiles::kAvatarIconPadding,
|
| - kOverlayHeight)]);
|
| - [self addSubview:changePhotoButton_];
|
| -
|
| - // Hide the button until the image is hovered over.
|
| - [changePhotoButton_ setHidden:YES];
|
| - }
|
| - }
|
| - return self;
|
| -}
|
| -
|
| -- (void)editPhoto:(id)sender {
|
| - avatarMenu_->EditProfile(avatarMenu_->GetActiveProfileIndex());
|
| -}
|
| -
|
| -- (void)mouseEntered:(NSEvent*)event {
|
| - [changePhotoButton_ setHidden:NO];
|
| -}
|
| -
|
| -- (void)mouseExited:(NSEvent*)event {
|
| - [changePhotoButton_ setHidden:YES];
|
| -}
|
| -
|
| -- (TransparentBackgroundButton*)changePhotoButtonWithRect:(NSRect)rect {
|
| - TransparentBackgroundButton* button =
|
| - [[TransparentBackgroundButton alloc] initWithFrame:rect];
|
| -
|
| - // The button has a centered white text and a transparent background.
|
| - base::scoped_nsobject<NSMutableParagraphStyle> textStyle(
|
| - [[NSMutableParagraphStyle alloc] init]);
|
| - [textStyle setAlignment:NSCenterTextAlignment];
|
| - NSDictionary* titleAttributes = @{
|
| - NSParagraphStyleAttributeName : textStyle,
|
| - NSForegroundColorAttributeName : [NSColor whiteColor]
|
| - };
|
| - NSString* buttonTitle = l10n_util::GetNSString(
|
| - IDS_PROFILES_PROFILE_CHANGE_PHOTO_BUTTON);
|
| - base::scoped_nsobject<NSAttributedString> attributedTitle(
|
| - [[NSAttributedString alloc] initWithString:buttonTitle
|
| - attributes:titleAttributes]);
|
| - [button setAttributedTitle:attributedTitle];
|
| - [button setTarget:self];
|
| - [button setAction:@selector(editPhoto:)];
|
| - return button;
|
| -}
|
| -@end
|
| -
|
| -// A custom text control that turns into a textfield for editing when clicked.
|
| -@interface EditableProfileNameButton : HoverImageButton<NSTextFieldDelegate> {
|
| - @private
|
| - base::scoped_nsobject<NSTextField> profileNameTextField_;
|
| - Profile* profile_; // Weak.
|
| -}
|
| -
|
| -- (id)initWithFrame:(NSRect)frameRect
|
| - profile:(Profile*)profile
|
| - profileName:(NSString*)profileName
|
| - editingAllowed:(BOOL)editingAllowed;
|
| -
|
| -// Called when the button is clicked.
|
| -- (void)showEditableView:(id)sender;
|
| -
|
| -// Called when the user presses "Enter" in the textfield.
|
| -- (void)controlTextDidEndEditing:(NSNotification *)obj;
|
| -@end
|
| -
|
| -@implementation EditableProfileNameButton
|
| -- (id)initWithFrame:(NSRect)frameRect
|
| - profile:(Profile*)profile
|
| - profileName:(NSString*)profileName
|
| - editingAllowed:(BOOL)editingAllowed {
|
| - if ((self = [super initWithFrame:frameRect])) {
|
| - profile_ = profile;
|
| -
|
| - [self setBordered:NO];
|
| - [self setFont:[NSFont labelFontOfSize:kTitleFontSize]];
|
| - [self setAlignment:NSLeftTextAlignment];
|
| - [[self cell] setLineBreakMode:NSLineBreakByTruncatingTail];
|
| - [self setTitle:profileName];
|
| -
|
| - if (editingAllowed) {
|
| - // Show an "edit" pencil icon when hovering over.
|
| - ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
|
| - [self setHoverImage:
|
| - rb->GetNativeImageNamed(IDR_ICON_PROFILES_EDIT_HOVER).AsNSImage()];
|
| - [self setAlternateImage:
|
| - rb->GetNativeImageNamed(IDR_ICON_PROFILES_EDIT_PRESSED).AsNSImage()];
|
| - [self setImagePosition:NSImageRight];
|
| - [self setTarget:self];
|
| - [self setAction:@selector(showEditableView:)];
|
| -
|
| - // We need to subtract the width of the bezel from the frame rect, so that
|
| - // the textfield can take the exact same space as the button.
|
| - frameRect.size.height -= 2 * kBezelThickness;
|
| - frameRect.origin = NSMakePoint(0, kBezelThickness);
|
| - profileNameTextField_.reset(
|
| - [[NSTextField alloc] initWithFrame:frameRect]);
|
| - [profileNameTextField_ setStringValue:profileName];
|
| - [profileNameTextField_ setFont:[NSFont labelFontOfSize:kTitleFontSize]];
|
| - [profileNameTextField_ setEditable:YES];
|
| - [profileNameTextField_ setDrawsBackground:YES];
|
| - [profileNameTextField_ setBezeled:YES];
|
| - [[profileNameTextField_ cell] setWraps:NO];
|
| - [[profileNameTextField_ cell] setLineBreakMode:
|
| - NSLineBreakByTruncatingTail];
|
| - [profileNameTextField_ setDelegate:self];
|
| - [self addSubview:profileNameTextField_];
|
| -
|
| - // Hide the textfield until the user clicks on the button.
|
| - [profileNameTextField_ setHidden:YES];
|
| - }
|
| - }
|
| - return self;
|
| -}
|
| -
|
| -// NSTextField objects send an NSNotification to a delegate if
|
| -// it implements this method:
|
| -- (void)controlTextDidEndEditing:(NSNotification *)obj {
|
| - NSString* text = [profileNameTextField_ stringValue];
|
| - // Empty profile names are not allowed, and are treated as a cancel.
|
| - if ([text length] > 0) {
|
| - profiles::UpdateProfileName(profile_, base::SysNSStringToUTF16(text));
|
| - [self setTitle:text];
|
| - }
|
| - [profileNameTextField_ setHidden:YES];
|
| - [profileNameTextField_ resignFirstResponder];
|
| -}
|
| -
|
| -- (void)showEditableView:(id)sender {
|
| - [profileNameTextField_ setHidden:NO];
|
| - [profileNameTextField_ becomeFirstResponder];
|
| -}
|
| -
|
| -@end
|
| -
|
| -// Custom button cell that adds a left padding before the button image, and
|
| -// a custom spacing between the button image and title.
|
| -@interface CustomPaddingImageButtonCell : NSButtonCell {
|
| - @private
|
| - // Padding between the left margin of the button and the cell image.
|
| - int leftMarginSpacing_;
|
| - // Spacing between the cell image and title.
|
| - int imageTitleSpacing_;
|
| -}
|
| -
|
| -- (id)initWithLeftMarginSpacing:(int)leftMarginSpacing
|
| - imageTitleSpacing:(int)imageTitleSpacing;
|
| -@end
|
| -
|
| -@implementation CustomPaddingImageButtonCell
|
| -- (id)initWithLeftMarginSpacing:(int)leftMarginSpacing
|
| - imageTitleSpacing:(int)imageTitleSpacing {
|
| - if ((self = [super init])) {
|
| - leftMarginSpacing_ = leftMarginSpacing;
|
| - imageTitleSpacing_ = imageTitleSpacing;
|
| - }
|
| - return self;
|
| -}
|
| -
|
| -- (NSRect)drawTitle:(NSAttributedString*)title
|
| - withFrame:(NSRect)frame
|
| - inView:(NSView*)controlView {
|
| - // The title frame origin isn't aware of the left margin spacing added
|
| - // in -drawImage, so it must be added when drawing the title as well.
|
| - frame.origin.x += leftMarginSpacing_ + imageTitleSpacing_;
|
| - frame.size.width -= (imageTitleSpacing_ + leftMarginSpacing_);
|
| - return [super drawTitle:title withFrame:frame inView:controlView];
|
| -}
|
| -
|
| -- (void)drawImage:(NSImage*)image
|
| - withFrame:(NSRect)frame
|
| - inView:(NSView*)controlView {
|
| - frame.origin.x = leftMarginSpacing_;
|
| - [super drawImage:image withFrame:frame inView:controlView];
|
| -}
|
| -
|
| -- (NSSize)cellSize {
|
| - NSSize buttonSize = [super cellSize];
|
| - buttonSize.width += leftMarginSpacing_ + imageTitleSpacing_;
|
| - return buttonSize;
|
| -}
|
| -
|
| -@end
|
| -
|
| -// A custom button that allows for setting a background color when hovered over.
|
| -@interface BackgroundColorHoverButton : HoverImageButton
|
| -@end
|
| -
|
| -@implementation BackgroundColorHoverButton
|
| -- (id)initWithFrame:(NSRect)frameRect {
|
| - if ((self = [super initWithFrame:frameRect])) {
|
| - [self setBordered:NO];
|
| - [self setFont:[NSFont labelFontOfSize:kTextFontSize]];
|
| - [self setButtonType:NSMomentaryChangeButton];
|
| -
|
| - base::scoped_nsobject<CustomPaddingImageButtonCell> cell(
|
| - [[CustomPaddingImageButtonCell alloc]
|
| - initWithLeftMarginSpacing:kHorizontalSpacing
|
| - imageTitleSpacing:kImageTitleSpacing]);
|
| - [cell setLineBreakMode:NSLineBreakByTruncatingTail];
|
| - [self setCell:cell.get()];
|
| - }
|
| - return self;
|
| -}
|
| -
|
| -- (void)setHoverState:(HoverState)state {
|
| - [super setHoverState:state];
|
| - bool isHighlighted = ([self hoverState] != kHoverStateNone);
|
| -
|
| - NSColor* backgroundColor = gfx::SkColorToCalibratedNSColor(
|
| - ui::NativeTheme::instance()->GetSystemColor(isHighlighted?
|
| - ui::NativeTheme::kColorId_MenuSeparatorColor :
|
| - ui::NativeTheme::kColorId_DialogBackground));
|
| -
|
| - [[self cell] setBackgroundColor:backgroundColor];
|
| -}
|
| -
|
| -@end
|
| -
|
| -// A custom view with the given background color.
|
| -@interface BackgroundColorView : NSView {
|
| - @private
|
| - base::scoped_nsobject<NSColor> backgroundColor_;
|
| -}
|
| -@end
|
| -
|
| -@implementation BackgroundColorView
|
| -- (id)initWithFrame:(NSRect)frameRect
|
| - withColor:(NSColor*)color {
|
| - if ((self = [super initWithFrame:frameRect]))
|
| - backgroundColor_.reset([color retain]);
|
| - return self;
|
| -}
|
| -
|
| -- (void)drawRect:(NSRect)dirtyRect {
|
| - [backgroundColor_ setFill];
|
| - NSRectFill(dirtyRect);
|
| - [super drawRect:dirtyRect];
|
| -}
|
| -@end
|
| -
|
| -@interface ProfileChooserController ()
|
| -// Creates a tutorial card for the profile |avatar_item| if needed.
|
| -- (NSView*)createTutorialViewIfNeeded:(const AvatarMenu::Item&)item;
|
| -
|
| -// Creates the main profile card for the profile |item| at the top of
|
| -// the bubble.
|
| -- (NSView*)createCurrentProfileView:(const AvatarMenu::Item&)item;
|
| -
|
| -// Creates the possible links for the main profile card with profile |item|.
|
| -- (NSView*)createCurrentProfileLinksForItem:(const AvatarMenu::Item&)item
|
| - withXOffset:(CGFloat)xOffset;
|
| -
|
| -// Creates a main profile card for the guest user.
|
| -- (NSView*)createGuestProfileView;
|
| -
|
| -// Creates an item for the profile |itemIndex| that is used in the fast profile
|
| -// switcher in the middle of the bubble.
|
| -- (NSButton*)createOtherProfileView:(int)itemIndex;
|
| -
|
| -// Creates the "Not you" and Lock option buttons.
|
| -- (NSView*)createOptionsViewWithRect:(NSRect)rect
|
| - enableLock:(BOOL)enableLock;
|
| -
|
| -// Creates the account management view for the active profile.
|
| -- (NSView*)createCurrentProfileAccountsView:(NSRect)rect;
|
| -
|
| -// Creates the list of accounts for the active profile.
|
| -- (NSView*)createAccountsListWithRect:(NSRect)rect;
|
| -
|
| -// Creates the Gaia sign-in/add account view.
|
| -- (NSView*)buildGaiaEmbeddedView;
|
| -
|
| -// Creates the account removal view.
|
| -- (NSView*)buildAccountRemovalView;
|
| -
|
| -// Creates a button with |text|, an icon given by |imageResourceId| and with
|
| -// |action|. The icon |alternateImageResourceId| is displayed in the button's
|
| -// hovered and pressed states.
|
| -- (NSButton*)hoverButtonWithRect:(NSRect)rect
|
| - text:(NSString*)text
|
| - imageResourceId:(int)imageResourceId
|
| - alternateImageResourceId:(int)alternateImageResourceId
|
| - action:(SEL)action;
|
| -
|
| -// Creates a generic link button with |title| and an |action| positioned at
|
| -// |frameOrigin|.
|
| -- (NSButton*)linkButtonWithTitle:(NSString*)title
|
| - frameOrigin:(NSPoint)frameOrigin
|
| - action:(SEL)action;
|
| -
|
| -// Creates an email account button with |title| and a remove icon.
|
| -- (NSButton*)accountButtonWithRect:(NSRect)rect
|
| - title:(const std::string&)title;
|
| -
|
| -@end
|
| -
|
| -@implementation ProfileChooserController
|
| -- (BubbleViewMode) viewMode {
|
| - return viewMode_;
|
| -}
|
| -
|
| -- (IBAction)switchToProfile:(id)sender {
|
| - // Check the event flags to see if a new window should be created.
|
| - bool alwaysCreate = ui::WindowOpenDispositionFromNSEvent(
|
| - [NSApp currentEvent]) == NEW_WINDOW;
|
| - avatarMenu_->SwitchToProfile([sender tag], alwaysCreate,
|
| - ProfileMetrics::SWITCH_PROFILE_ICON);
|
| -}
|
| -
|
| -- (IBAction)showUserManager:(id)sender {
|
| - // Guest users cannot appear in the User Manager, nor display a tutorial.
|
| - profiles::ShowUserManagerMaybeWithTutorial(
|
| - isGuestSession_ ? NULL : browser_->profile());
|
| -}
|
| -
|
| -- (IBAction)showAccountManagement:(id)sender {
|
| - [self initMenuContentsWithView:BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT];
|
| -}
|
| -
|
| -- (IBAction)lockProfile:(id)sender {
|
| - profiles::LockProfile(browser_->profile());
|
| -}
|
| -
|
| -- (IBAction)showSigninPage:(id)sender {
|
| - [self initMenuContentsWithView:BUBBLE_VIEW_MODE_GAIA_SIGNIN];
|
| -}
|
| -
|
| -- (IBAction)addAccount:(id)sender {
|
| - [self initMenuContentsWithView:BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT];
|
| -}
|
| -
|
| -- (IBAction)navigateBackFromSigninPage:(id)sender {
|
| - std::string primaryAccount = SigninManagerFactory::GetForProfile(
|
| - browser_->profile())->GetAuthenticatedUsername();
|
| - [self initMenuContentsWithView:primaryAccount.empty() ?
|
| - BUBBLE_VIEW_MODE_PROFILE_CHOOSER : BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT];
|
| -}
|
| -
|
| -- (IBAction)showAccountRemovalView:(id)sender {
|
| - DCHECK(!isGuestSession_);
|
| -
|
| - // Tag is either |kPrimaryProfileTag| for the primary account, or equal to the
|
| - // index in |currentProfileAccounts_| for a secondary account.
|
| - int tag = [sender tag];
|
| - if (tag == kPrimaryProfileTag) {
|
| - accountIdToRemove_ = SigninManagerFactory::GetForProfile(
|
| - browser_->profile())->GetAuthenticatedUsername();
|
| - } else {
|
| - DCHECK(ContainsKey(currentProfileAccounts_, tag));
|
| - accountIdToRemove_ = currentProfileAccounts_[tag];
|
| - }
|
| -
|
| - [self initMenuContentsWithView:BUBBLE_VIEW_MODE_ACCOUNT_REMOVAL];
|
| -}
|
| -
|
| -- (IBAction)removeAccountAndRelaunch:(id)sender {
|
| - DCHECK(!accountIdToRemove_.empty());
|
| - ProfileOAuth2TokenServiceFactory::GetPlatformSpecificForProfile(
|
| - browser_->profile())->RevokeCredentials(accountIdToRemove_);
|
| - accountIdToRemove_.clear();
|
| - chrome::AttemptRestart();
|
| -}
|
| -
|
| -- (IBAction)openTutorialLearnMoreURL:(id)sender {
|
| - // TODO(guohui): update |learnMoreUrl| once it is decided.
|
| - const GURL learnMoreUrl("https://support.google.com/chrome/?hl=en#to");
|
| - chrome::NavigateParams params(browser_->profile(), learnMoreUrl,
|
| - content::PAGE_TRANSITION_LINK);
|
| - params.disposition = NEW_FOREGROUND_TAB;
|
| - chrome::Navigate(¶ms);
|
| -}
|
| -
|
| -- (IBAction)dismissTutorial:(id)sender {
|
| - // If the user manually dismissed the tutorial, never show it again by setting
|
| - // the number of times shown to the maximum plus 1, so that later we could
|
| - // distinguish between the dismiss case and the case when the tutorial is
|
| - // indeed shown for the maximum number of times.
|
| - browser_->profile()->GetPrefs()->SetInteger(
|
| - prefs::kProfileAvatarTutorialShown, kProfileAvatarTutorialShowMax + 1);
|
| - [self initMenuContentsWithView:BUBBLE_VIEW_MODE_PROFILE_CHOOSER];
|
| -}
|
| -
|
| -- (void)cleanUpEmbeddedViewContents {
|
| - webContents_.reset();
|
| -}
|
| -
|
| -- (id)initWithBrowser:(Browser*)browser
|
| - anchoredAt:(NSPoint)point
|
| - withMode:(BubbleViewMode)mode {
|
| - base::scoped_nsobject<InfoBubbleWindow> window([[InfoBubbleWindow alloc]
|
| - initWithContentRect:ui::kWindowSizeDeterminedLater
|
| - styleMask:NSBorderlessWindowMask
|
| - backing:NSBackingStoreBuffered
|
| - defer:NO]);
|
| -
|
| - if ((self = [super initWithWindow:window
|
| - parentWindow:browser->window()->GetNativeWindow()
|
| - anchoredAt:point])) {
|
| - browser_ = browser;
|
| - viewMode_ = mode;
|
| - tutorialShowing_ = false;
|
| - observer_.reset(new ActiveProfileObserverBridge(self, browser_));
|
| -
|
| - avatarMenu_.reset(new AvatarMenu(
|
| - &g_browser_process->profile_manager()->GetProfileInfoCache(),
|
| - observer_.get(),
|
| - browser_));
|
| - avatarMenu_->RebuildMenu();
|
| -
|
| - // Guest profiles do not have a token service.
|
| - isGuestSession_ = browser_->profile()->IsGuestSession();
|
| -
|
| - ui::NativeTheme* nativeTheme = ui::NativeTheme::instance();
|
| - [[self bubble] setAlignment:info_bubble::kAlignRightEdgeToAnchorEdge];
|
| - [[self bubble] setArrowLocation:info_bubble::kNoArrow];
|
| - [[self bubble] setBackgroundColor:
|
| - gfx::SkColorToCalibratedNSColor(nativeTheme->GetSystemColor(
|
| - ui::NativeTheme::kColorId_DialogBackground))];
|
| - [self initMenuContentsWithView:viewMode_];
|
| - }
|
| -
|
| - return self;
|
| -}
|
| -
|
| -- (void)initMenuContentsWithView:(BubbleViewMode)viewToDisplay {
|
| - viewMode_ = viewToDisplay;
|
| - NSView* contentView = [[self window] contentView];
|
| - [contentView setSubviews:[NSArray array]];
|
| -
|
| - if (viewMode_ == BUBBLE_VIEW_MODE_GAIA_SIGNIN ||
|
| - viewMode_ == BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT ||
|
| - viewMode_ == BUBBLE_VIEW_MODE_ACCOUNT_REMOVAL) {
|
| - bool isRemovalView = viewMode_ == BUBBLE_VIEW_MODE_ACCOUNT_REMOVAL;
|
| - NSView* subView = isRemovalView ?
|
| - [self buildAccountRemovalView] : [self buildGaiaEmbeddedView];
|
| - [contentView addSubview:subView];
|
| - SetWindowSize([self window],
|
| - NSMakeSize(NSWidth([subView frame]), NSHeight([subView frame])));
|
| - return;
|
| - }
|
| -
|
| - NSView* tutorialView = nil;
|
| - NSView* currentProfileView = nil;
|
| - base::scoped_nsobject<NSMutableArray> otherProfiles(
|
| - [[NSMutableArray alloc] init]);
|
| - // Local and guest profiles cannot lock their profile.
|
| - bool enableLock = false;
|
| -
|
| - // Loop over the profiles in reverse, so that they are sorted by their
|
| - // y-coordinate, and separate them into active and "other" profiles.
|
| - for (int i = avatarMenu_->GetNumberOfItems() - 1; i >= 0; --i) {
|
| - const AvatarMenu::Item& item = avatarMenu_->GetItemAt(i);
|
| - if (item.active) {
|
| - if (viewMode_ == BUBBLE_VIEW_MODE_PROFILE_CHOOSER)
|
| - tutorialView = [self createTutorialViewIfNeeded:item];
|
| - currentProfileView = [self createCurrentProfileView:item];
|
| - enableLock = item.signed_in;
|
| - } else {
|
| - [otherProfiles addObject:[self createOtherProfileView:i]];
|
| - }
|
| - }
|
| - if (!currentProfileView) // Guest windows don't have an active profile.
|
| - currentProfileView = [self createGuestProfileView];
|
| -
|
| - // |yOffset| is the next position at which to draw in |contentView|
|
| - // coordinates.
|
| - CGFloat yOffset = kSmallVerticalSpacing;
|
| -
|
| - // Option buttons.
|
| - NSView* optionsView = [self createOptionsViewWithRect:
|
| - NSMakeRect(0, yOffset, kFixedMenuWidth, 0)
|
| - enableLock:enableLock];
|
| - [contentView addSubview:optionsView];
|
| - yOffset = NSMaxY([optionsView frame]) + kSmallVerticalSpacing;
|
| -
|
| - NSBox* separator = [self separatorWithFrame:
|
| - NSMakeRect(0, yOffset, kFixedMenuWidth, 0)];
|
| - [contentView addSubview:separator];
|
| - yOffset = NSMaxY([separator frame]) + kVerticalSpacing;
|
| -
|
| - if (viewToDisplay == BUBBLE_VIEW_MODE_PROFILE_CHOOSER &&
|
| - switches::IsFastUserSwitching()) {
|
| - // Other profiles switcher. The profiles have already been sorted
|
| - // by their y-coordinate, so they can be added in the existing order.
|
| - for (NSView *otherProfileView in otherProfiles.get()) {
|
| - [otherProfileView setFrameOrigin:NSMakePoint(kHorizontalSpacing,
|
| - yOffset)];
|
| - [contentView addSubview:otherProfileView];
|
| - yOffset = NSMaxY([otherProfileView frame]) + kSmallVerticalSpacing;
|
| - }
|
| -
|
| - // If we displayed other profiles, ensure the spacing between the last item
|
| - // and the active profile card is the same as the spacing between the active
|
| - // profile card and the bottom of the bubble.
|
| - if ([otherProfiles.get() count] > 0)
|
| - yOffset += kSmallVerticalSpacing;
|
| - } else if (viewToDisplay == BUBBLE_VIEW_MODE_ACCOUNT_MANAGEMENT) {
|
| - NSView* currentProfileAccountsView = [self createCurrentProfileAccountsView:
|
| - NSMakeRect(kHorizontalSpacing,
|
| - yOffset,
|
| - kFixedMenuWidth - 2 * kHorizontalSpacing,
|
| - 0)];
|
| - [contentView addSubview:currentProfileAccountsView];
|
| - yOffset = NSMaxY([currentProfileAccountsView frame]) + kVerticalSpacing;
|
| -
|
| - NSBox* accountsSeparator = [self separatorWithFrame:
|
| - NSMakeRect(0, yOffset, kFixedMenuWidth, 0)];
|
| - [contentView addSubview:accountsSeparator];
|
| - yOffset = NSMaxY([accountsSeparator frame]) + kVerticalSpacing;
|
| - }
|
| -
|
| - // Active profile card.
|
| - if (currentProfileView) {
|
| - [currentProfileView setFrameOrigin:NSMakePoint(kHorizontalSpacing,
|
| - yOffset)];
|
| - [contentView addSubview:currentProfileView];
|
| - yOffset = NSMaxY([currentProfileView frame]) + kVerticalSpacing;
|
| - }
|
| -
|
| - if (tutorialView) {
|
| - [tutorialView setFrameOrigin:NSMakePoint(0, yOffset)];
|
| - [contentView addSubview:tutorialView];
|
| - yOffset = NSMaxY([tutorialView frame]);
|
| - } else {
|
| - tutorialShowing_ = false;
|
| - }
|
| -
|
| - SetWindowSize([self window], NSMakeSize(kFixedMenuWidth, yOffset));
|
| -}
|
| -
|
| -- (NSView*)createTutorialViewIfNeeded:(const AvatarMenu::Item&)item {
|
| - if (!item.signed_in)
|
| - return nil;
|
| -
|
| - Profile* profile = browser_->profile();
|
| - const int showCount = profile->GetPrefs()->GetInteger(
|
| - prefs::kProfileAvatarTutorialShown);
|
| - // Do not show the tutorial if user has dismissed it.
|
| - if (showCount > kProfileAvatarTutorialShowMax)
|
| - return nil;
|
| -
|
| - if (!tutorialShowing_) {
|
| - if (showCount == kProfileAvatarTutorialShowMax)
|
| - return nil;
|
| - profile->GetPrefs()->SetInteger(
|
| - prefs::kProfileAvatarTutorialShown, showCount + 1);
|
| - tutorialShowing_ = true;
|
| - }
|
| -
|
| - NSColor* tutorialBackgroundColor =
|
| - gfx::SkColorToSRGBNSColor(profiles::kAvatarTutorialBackgroundColor);
|
| - base::scoped_nsobject<NSView> container([[BackgroundColorView alloc]
|
| - initWithFrame:NSMakeRect(0, 0, kFixedMenuWidth, 0)
|
| - withColor:tutorialBackgroundColor]);
|
| - CGFloat availableWidth = kFixedMenuWidth - 2 * kHorizontalSpacing;
|
| - CGFloat yOffset = kVerticalSpacing;
|
| -
|
| - // Adds links and buttons at the bottom.
|
| - NSButton* learnMoreLink =
|
| - [self linkButtonWithTitle:
|
| - l10n_util::GetNSString(IDS_PROFILES_PROFILE_TUTORIAL_LEARN_MORE)
|
| - frameOrigin:NSMakePoint(kHorizontalSpacing, yOffset)
|
| - action:@selector(openTutorialLearnMoreURL:)];
|
| - [[learnMoreLink cell] setTextColor:[NSColor whiteColor]];
|
| - [container addSubview:learnMoreLink];
|
| -
|
| - base::scoped_nsobject<NSButton> tutorialOkButton([[HoverButton alloc]
|
| - initWithFrame:NSZeroRect]);
|
| - [tutorialOkButton setTitle:l10n_util::GetNSString(
|
| - IDS_PROFILES_SIGNIN_TUTORIAL_OK_BUTTON)];
|
| - [tutorialOkButton setTarget:self];
|
| - [tutorialOkButton setAction:@selector(dismissTutorial:)];
|
| - [tutorialOkButton sizeToFit];
|
| - NSSize buttonSize = [tutorialOkButton frame].size;
|
| - const CGFloat kTopBottomTextPadding = 6;
|
| - const CGFloat kLeftRightTextPadding = 15;
|
| - buttonSize.width += 2 * kLeftRightTextPadding;
|
| - buttonSize.height += 2 * kTopBottomTextPadding;
|
| - [tutorialOkButton setFrameSize:buttonSize];
|
| - [tutorialOkButton setAlignment:NSCenterTextAlignment];
|
| - [tutorialOkButton setFrameOrigin:NSMakePoint(
|
| - kFixedMenuWidth - NSWidth([tutorialOkButton frame]) - kHorizontalSpacing,
|
| - yOffset)];
|
| - [container addSubview:tutorialOkButton];
|
| -
|
| - yOffset = std::max(NSMaxY([learnMoreLink frame]),
|
| - NSMaxY([tutorialOkButton frame])) + kVerticalSpacing;
|
| -
|
| - // Adds body content.
|
| - NSTextField* contentLabel = BuildLabel(
|
| - l10n_util::GetNSString(IDS_PROFILES_SIGNIN_TUTORIAL_CONTENT_TEXT),
|
| - NSMakePoint(kHorizontalSpacing, yOffset),
|
| - tutorialBackgroundColor,
|
| - gfx::SkColorToSRGBNSColor(profiles::kAvatarTutorialContentTextColor));
|
| - [contentLabel setFrameSize:NSMakeSize(availableWidth, 0)];
|
| - [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:contentLabel];
|
| - [container addSubview:contentLabel];
|
| - yOffset = NSMaxY([contentLabel frame]) + kSmallVerticalSpacing;
|
| -
|
| - // Adds title.
|
| - NSTextField* titleLabel =
|
| - BuildLabel(
|
| - l10n_util::GetNSString(IDS_PROFILES_SIGNIN_TUTORIAL_TITLE),
|
| - NSMakePoint(kHorizontalSpacing, yOffset),
|
| - tutorialBackgroundColor,
|
| - [NSColor whiteColor] /* text_color */);
|
| - [titleLabel setFont:[NSFont labelFontOfSize:kTitleFontSize]];
|
| - [titleLabel sizeToFit];
|
| - [titleLabel setFrameSize:
|
| - NSMakeSize(availableWidth, NSHeight([titleLabel frame]))];
|
| - [container addSubview:titleLabel];
|
| - yOffset = NSMaxY([titleLabel frame]) + kVerticalSpacing;
|
| -
|
| - [container setFrameSize:NSMakeSize(kFixedMenuWidth, yOffset)];
|
| -
|
| - // Adds caret at the bottom.
|
| - NSImage* caretImage = ui::ResourceBundle::GetSharedInstance().
|
| - GetNativeImageNamed(IDR_ICON_PROFILES_MENU_CARET).AsNSImage();
|
| - base::scoped_nsobject<NSImageView> caretView(
|
| - [[NSImageView alloc] initWithFrame:NSMakeRect(
|
| - kHorizontalSpacing, 0, caretImage.size.width,
|
| - caretImage.size.height)]);
|
| - [caretView setImage:caretImage];
|
| -
|
| - base::scoped_nsobject<NSView> containerWithCaret([[NSView alloc]
|
| - initWithFrame:NSMakeRect(0, 0, kFixedMenuWidth, 0)]);
|
| - [containerWithCaret addSubview:caretView];
|
| -
|
| - [container setFrameOrigin:NSMakePoint(0, caretImage.size.height)];
|
| - [containerWithCaret addSubview:container];
|
| -
|
| - [containerWithCaret setFrameSize:
|
| - NSMakeSize(kFixedMenuWidth, NSMaxY([container frame]))];
|
| - return containerWithCaret.autorelease();
|
| -}
|
| -
|
| -- (NSView*)createCurrentProfileView:(const AvatarMenu::Item&)item {
|
| - base::scoped_nsobject<NSView> container([[NSView alloc]
|
| - initWithFrame:NSZeroRect]);
|
| -
|
| - // Profile icon.
|
| - base::scoped_nsobject<EditableProfilePhoto> iconView(
|
| - [[EditableProfilePhoto alloc]
|
| - initWithFrame:NSMakeRect(0, 0, kLargeImageSide, kLargeImageSide)
|
| - avatarMenu:avatarMenu_.get()
|
| - profileIcon:item.icon
|
| - editingAllowed:!isGuestSession_]);
|
| -
|
| - [container addSubview:iconView];
|
| -
|
| - CGFloat xOffset = NSMaxX([iconView frame]) + kHorizontalSpacing;
|
| - CGFloat yOffset = kVerticalSpacing;
|
| - if (!isGuestSession_ && viewMode_ == BUBBLE_VIEW_MODE_PROFILE_CHOOSER) {
|
| - NSView* linksContainer =
|
| - [self createCurrentProfileLinksForItem:item withXOffset:xOffset];
|
| - [container addSubview:linksContainer];
|
| - yOffset = NSMaxY([linksContainer frame]);
|
| - }
|
| -
|
| - // Profile name.
|
| - CGFloat availableTextWidth =
|
| - kFixedMenuWidth - xOffset - 2 * kHorizontalSpacing;
|
| - base::scoped_nsobject<EditableProfileNameButton> profileName(
|
| - [[EditableProfileNameButton alloc]
|
| - initWithFrame:NSMakeRect(xOffset, yOffset,
|
| - availableTextWidth,
|
| - kProfileButtonHeight)
|
| - profile:browser_->profile()
|
| - profileName:base::SysUTF16ToNSString(
|
| - profiles::GetAvatarNameForProfile(browser_->profile()))
|
| - editingAllowed:!isGuestSession_]);
|
| -
|
| - [container addSubview:profileName];
|
| - [container setFrameSize:NSMakeSize(kFixedMenuWidth,
|
| - NSHeight([iconView frame]))];
|
| - return container.autorelease();
|
| -}
|
| -
|
| -- (NSView*)createCurrentProfileLinksForItem:(const AvatarMenu::Item&)item
|
| - withXOffset:(CGFloat)xOffset {
|
| - base::scoped_nsobject<NSView> container([[NSView alloc]
|
| - initWithFrame:NSZeroRect]);
|
| -
|
| - NSButton* link;
|
| - NSPoint frameOrigin = NSMakePoint(xOffset, kSmallVerticalSpacing);
|
| - // The available links depend on the type of profile that is active.
|
| - if (item.signed_in) {
|
| - link = [self linkButtonWithTitle:l10n_util::GetNSString(
|
| - IDS_PROFILES_PROFILE_MANAGE_ACCOUNTS_BUTTON)
|
| - frameOrigin:frameOrigin
|
| - action:@selector(showAccountManagement:)];
|
| - } else {
|
| - link = [self linkButtonWithTitle:l10n_util::GetNSStringFWithFixup(
|
| - IDS_SYNC_START_SYNC_BUTTON_LABEL,
|
| - l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME))
|
| - frameOrigin:frameOrigin
|
| - action:@selector(showSigninPage:)];
|
| - }
|
| -
|
| - [container addSubview:link];
|
| - [container setFrameSize:NSMakeSize(
|
| - NSMaxX([link frame]), NSMaxY([link frame]) + kSmallVerticalSpacing)];
|
| - return container.autorelease();
|
| -}
|
| -
|
| -- (NSView*)createGuestProfileView {
|
| - gfx::Image guestIcon =
|
| - ui::ResourceBundle::GetSharedInstance().GetNativeImageNamed(
|
| - IDR_LOGIN_GUEST);
|
| - AvatarMenu::Item guestItem(std::string::npos, /* menu_index, not used */
|
| - std::string::npos, /* profile_index, not used */
|
| - guestIcon);
|
| - guestItem.active = true;
|
| - guestItem.name = base::SysNSStringToUTF16(
|
| - l10n_util::GetNSString(IDS_PROFILES_GUEST_PROFILE_NAME));
|
| -
|
| - return [self createCurrentProfileView:guestItem];
|
| -}
|
| -
|
| -- (NSButton*)createOtherProfileView:(int)itemIndex {
|
| - const AvatarMenu::Item& item = avatarMenu_->GetItemAt(itemIndex);
|
| - base::scoped_nsobject<NSButton> profileButton([[NSButton alloc]
|
| - initWithFrame:NSZeroRect]);
|
| - base::scoped_nsobject<CustomPaddingImageButtonCell> cell(
|
| - [[CustomPaddingImageButtonCell alloc]
|
| - initWithLeftMarginSpacing:0
|
| - imageTitleSpacing:kImageTitleSpacing]);
|
| - [profileButton setCell:cell.get()];
|
| -
|
| - [[profileButton cell] setLineBreakMode:NSLineBreakByTruncatingTail];
|
| - [profileButton setTitle:base::SysUTF16ToNSString(item.name)];
|
| - [profileButton setImage:CreateProfileImage(
|
| - item.icon, kSmallImageSide).ToNSImage()];
|
| - [profileButton setImagePosition:NSImageLeft];
|
| - [profileButton setAlignment:NSLeftTextAlignment];
|
| - [profileButton setBordered:NO];
|
| - [profileButton setFont:[NSFont labelFontOfSize:kTitleFontSize]];
|
| - [profileButton setTag:itemIndex];
|
| - [profileButton setTarget:self];
|
| - [profileButton setAction:@selector(switchToProfile:)];
|
| -
|
| - // Since the bubble is fixed width, we need to calculate the width available
|
| - // for the profile name, as longer names will have to be elided.
|
| - CGFloat availableTextWidth = kFixedMenuWidth - 2 * kHorizontalSpacing;
|
| - [profileButton sizeToFit];
|
| - [profileButton setFrameSize:NSMakeSize(availableTextWidth,
|
| - NSHeight([profileButton frame]))];
|
| -
|
| - return profileButton.autorelease();
|
| -}
|
| -
|
| -- (NSView*)createOptionsViewWithRect:(NSRect)rect
|
| - enableLock:(BOOL)enableLock {
|
| - int widthOfLockButton = enableLock? 2 * kHorizontalSpacing + 12 : 0;
|
| - NSRect viewRect = NSMakeRect(0, 0,
|
| - rect.size.width - widthOfLockButton,
|
| - kBlueButtonHeight);
|
| - NSButton* notYouButton =
|
| - [self hoverButtonWithRect:viewRect
|
| - text:l10n_util::GetNSStringF(
|
| - IDS_PROFILES_NOT_YOU_BUTTON,
|
| - profiles::GetAvatarNameForProfile(browser_->profile()))
|
| - imageResourceId:IDR_ICON_PROFILES_MENU_AVATAR
|
| - alternateImageResourceId:IDR_ICON_PROFILES_MENU_AVATAR
|
| - action:@selector(showUserManager:)];
|
| -
|
| - rect.size.height = NSMaxY([notYouButton frame]);
|
| - base::scoped_nsobject<NSView> container([[NSView alloc]
|
| - initWithFrame:rect]);
|
| - [container addSubview:notYouButton];
|
| -
|
| - if (enableLock) {
|
| - viewRect.origin.x = NSMaxX([notYouButton frame]);
|
| - viewRect.size.width = widthOfLockButton;
|
| - NSButton* lockButton =
|
| - [self hoverButtonWithRect:viewRect
|
| - text:@""
|
| - imageResourceId:IDR_ICON_PROFILES_MENU_LOCK
|
| - alternateImageResourceId:IDR_ICON_PROFILES_MENU_LOCK
|
| - action:@selector(lockProfile:)];
|
| - [container addSubview:lockButton];
|
| - }
|
| -
|
| - return container.autorelease();
|
| -}
|
| -
|
| -- (NSView*)createCurrentProfileAccountsView:(NSRect)rect {
|
| - const CGFloat kAccountButtonHeight = 15;
|
| -
|
| - const AvatarMenu::Item& item =
|
| - avatarMenu_->GetItemAt(avatarMenu_->GetActiveProfileIndex());
|
| - DCHECK(item.signed_in);
|
| -
|
| - base::scoped_nsobject<NSView> container([[NSView alloc] initWithFrame:rect]);
|
| -
|
| - NSRect viewRect = NSMakeRect(0, 0, rect.size.width, kBlueButtonHeight);
|
| - base::scoped_nsobject<NSButton> addAccountsButton([[BlueLabelButton alloc]
|
| - initWithFrame:viewRect]);
|
| -
|
| - // Manually elide the button text so that the contents fit inside the bubble.
|
| - // This is needed because the BlueLabelButton cell resets the style on
|
| - // every call to -cellSize, which prevents setting a custom lineBreakMode.
|
| - NSString* elidedButtonText = base::SysUTF16ToNSString(gfx::ElideText(
|
| - l10n_util::GetStringFUTF16(
|
| - IDS_PROFILES_PROFILE_ADD_ACCOUNT_BUTTON, item.name),
|
| - ui::ResourceBundle::GetSharedInstance().GetFontList(
|
| - ui::ResourceBundle::BaseFont),
|
| - rect.size.width,
|
| - gfx::ELIDE_AT_END));
|
| -
|
| - [addAccountsButton setTitle:elidedButtonText];
|
| - [addAccountsButton setTarget:self];
|
| - [addAccountsButton setAction:@selector(addAccount:)];
|
| - [container addSubview:addAccountsButton];
|
| -
|
| - // Update the height of the email account buttons. This is needed so that the
|
| - // all the buttons span the entire width of the bubble.
|
| - viewRect.origin.y = NSMaxY([addAccountsButton frame]) + kVerticalSpacing;
|
| - viewRect.size.height = kAccountButtonHeight;
|
| -
|
| - NSView* accountEmails = [self createAccountsListWithRect:viewRect];
|
| - [container addSubview:accountEmails];
|
| - [container setFrameSize:NSMakeSize(
|
| - NSWidth([container frame]), NSMaxY([accountEmails frame]))];
|
| - return container.autorelease();
|
| -}
|
| -
|
| -- (NSView*)createAccountsListWithRect:(NSRect)rect {
|
| - base::scoped_nsobject<NSView> container([[NSView alloc] initWithFrame:rect]);
|
| - currentProfileAccounts_.clear();
|
| -
|
| - Profile* profile = browser_->profile();
|
| - std::string primaryAccount =
|
| - SigninManagerFactory::GetForProfile(profile)->GetAuthenticatedUsername();
|
| - DCHECK(!primaryAccount.empty());
|
| - std::vector<std::string>accounts =
|
| - profiles::GetSecondaryAccountsForProfile(profile, primaryAccount);
|
| -
|
| - rect.origin.y = 0;
|
| - for (size_t i = 0; i < accounts.size(); ++i) {
|
| - // Save the original email address, as the button text could be elided.
|
| - currentProfileAccounts_[i] = accounts[i];
|
| - NSButton* accountButton = [self accountButtonWithRect:rect
|
| - title:accounts[i]];
|
| - [accountButton setTag:i];
|
| - [container addSubview:accountButton];
|
| - rect.origin.y = NSMaxY([accountButton frame]) + kSmallVerticalSpacing;
|
| - }
|
| -
|
| - // The primary account should always be listed first.
|
| - NSButton* accountButton = [self accountButtonWithRect:rect
|
| - title:primaryAccount];
|
| - [container addSubview:accountButton];
|
| - [container setFrameSize:NSMakeSize(NSWidth([container frame]),
|
| - NSMaxY([accountButton frame]))];
|
| - [accountButton setTag:kPrimaryProfileTag];
|
| - return container.autorelease();
|
| -}
|
| -
|
| -- (NSView*)buildGaiaEmbeddedView {
|
| - base::scoped_nsobject<NSView> container(
|
| - [[NSView alloc] initWithFrame:NSZeroRect]);
|
| - CGFloat yOffset = 0;
|
| -
|
| - bool addSecondaryAccount = viewMode_ == BUBBLE_VIEW_MODE_GAIA_ADD_ACCOUNT;
|
| - signin::Source source = addSecondaryAccount ?
|
| - signin::SOURCE_AVATAR_BUBBLE_ADD_ACCOUNT :
|
| - signin::SOURCE_AVATAR_BUBBLE_SIGN_IN;
|
| -
|
| - webContents_.reset(content::WebContents::Create(
|
| - content::WebContents::CreateParams(browser_->profile())));
|
| - webContents_->GetController().LoadURL(
|
| - signin::GetPromoURL(
|
| - source, false /* auto_close */, true /* is_constrained */),
|
| - content::Referrer(),
|
| - content::PAGE_TRANSITION_AUTO_TOPLEVEL,
|
| - std::string());
|
| - NSView* webview = webContents_->GetView()->GetNativeView();
|
| - [webview setFrameSize:NSMakeSize(kFixedGaiaViewWidth, kFixedGaiaViewHeight)];
|
| - [container addSubview:webview];
|
| - yOffset = NSMaxY([webview frame]);
|
| -
|
| - // Adds the title card.
|
| - NSBox* separator = [self separatorWithFrame:
|
| - NSMakeRect(0, yOffset, kFixedGaiaViewWidth, 0)];
|
| - [container addSubview:separator];
|
| - yOffset = NSMaxY([separator frame]) + kSmallVerticalSpacing;
|
| -
|
| - NSView* titleView = BuildTitleCard(
|
| - NSMakeRect(0, yOffset, kFixedGaiaViewWidth,0),
|
| - addSecondaryAccount ? IDS_PROFILES_GAIA_ADD_ACCOUNT_TITLE :
|
| - IDS_PROFILES_GAIA_SIGNIN_TITLE,
|
| - self /* backButtonTarget*/,
|
| - @selector(navigateBackFromSigninPage:) /* backButtonAction */);
|
| - [container addSubview:titleView];
|
| - yOffset = NSMaxY([titleView frame]);
|
| -
|
| - [container setFrameSize:NSMakeSize(kFixedGaiaViewWidth, yOffset)];
|
| - return container.autorelease();
|
| -}
|
| -
|
| -- (NSView*)buildAccountRemovalView {
|
| - DCHECK(!accountIdToRemove_.empty());
|
| -
|
| - base::scoped_nsobject<NSView> container(
|
| - [[NSView alloc] initWithFrame:NSZeroRect]);
|
| - CGFloat availableWidth =
|
| - kFixedAccountRemovalViewWidth - 2 * kHorizontalSpacing;
|
| - CGFloat yOffset = kVerticalSpacing;
|
| -
|
| - const std::string& primaryAccount = SigninManagerFactory::GetForProfile(
|
| - browser_->profile())->GetAuthenticatedUsername();
|
| - bool isPrimaryAccount = primaryAccount == accountIdToRemove_;
|
| -
|
| - // Adds "remove and relaunch" button at the bottom if needed.
|
| - if (!isPrimaryAccount) {
|
| - base::scoped_nsobject<NSButton> removeAndRelaunchButton(
|
| - [[BlueLabelButton alloc] initWithFrame:NSZeroRect]);
|
| - [removeAndRelaunchButton setTitle:l10n_util::GetNSString(
|
| - IDS_PROFILES_ACCOUNT_REMOVAL_BUTTON)];
|
| - [removeAndRelaunchButton setTarget:self];
|
| - [removeAndRelaunchButton setAction:@selector(removeAccountAndRelaunch:)];
|
| - [removeAndRelaunchButton sizeToFit];
|
| - [removeAndRelaunchButton setAlignment:NSCenterTextAlignment];
|
| - CGFloat xOffset = (kFixedAccountRemovalViewWidth -
|
| - NSWidth([removeAndRelaunchButton frame])) / 2;
|
| - [removeAndRelaunchButton setFrameOrigin:NSMakePoint(xOffset, yOffset)];
|
| - [container addSubview:removeAndRelaunchButton];
|
| -
|
| - yOffset = NSMaxY([removeAndRelaunchButton frame]) + kVerticalSpacing;
|
| - }
|
| -
|
| - // Adds the main text.
|
| - NSString* contentStr = isPrimaryAccount ?
|
| - l10n_util::GetNSStringF(IDS_PROFILES_PRIMARY_ACCOUNT_REMOVAL_TEXT,
|
| - base::UTF8ToUTF16(accountIdToRemove_)) :
|
| - l10n_util::GetNSString(IDS_PROFILES_ACCOUNT_REMOVAL_TEXT);
|
| - NSTextField* contentLabel =
|
| - BuildLabel(contentStr, NSMakePoint(kHorizontalSpacing, yOffset),
|
| - nil /* background_color */, nil /* text_color */);
|
| - [contentLabel setFrameSize:NSMakeSize(availableWidth, 0)];
|
| - [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:contentLabel];
|
| - [container addSubview:contentLabel];
|
| - yOffset = NSMaxY([contentLabel frame]) + kVerticalSpacing;
|
| -
|
| - // Adds the title card.
|
| - NSBox* separator = [self separatorWithFrame:
|
| - NSMakeRect(0, yOffset, kFixedAccountRemovalViewWidth, 0)];
|
| - [container addSubview:separator];
|
| - yOffset = NSMaxY([separator frame]) + kSmallVerticalSpacing;
|
| -
|
| - NSView* titleView = BuildTitleCard(
|
| - NSMakeRect(0, yOffset, kFixedAccountRemovalViewWidth,0),
|
| - IDS_PROFILES_ACCOUNT_REMOVAL_TITLE,
|
| - self /* backButtonTarget*/,
|
| - @selector(showAccountManagement:) /* backButtonAction */);
|
| - [container addSubview:titleView];
|
| - yOffset = NSMaxY([titleView frame]);
|
| -
|
| - [container setFrameSize:NSMakeSize(kFixedAccountRemovalViewWidth, yOffset)];
|
| - return container.autorelease();
|
| -}
|
| -
|
| -- (NSButton*)hoverButtonWithRect:(NSRect)rect
|
| - text:(NSString*)text
|
| - imageResourceId:(int)imageResourceId
|
| - alternateImageResourceId:(int)alternateImageResourceId
|
| - action:(SEL)action {
|
| - base::scoped_nsobject<BackgroundColorHoverButton> button(
|
| - [[BackgroundColorHoverButton alloc] initWithFrame:rect]);
|
| -
|
| - [button setTitle:text];
|
| - ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
|
| - NSImage* alternateImage = rb->GetNativeImageNamed(
|
| - alternateImageResourceId).ToNSImage();
|
| - [button setDefaultImage:rb->GetNativeImageNamed(imageResourceId).ToNSImage()];
|
| - [button setHoverImage:alternateImage];
|
| - [button setPressedImage:alternateImage];
|
| - [button setImagePosition:NSImageLeft];
|
| - [button setAlignment:NSLeftTextAlignment];
|
| - [button setBordered:NO];
|
| - [button setTarget:self];
|
| - [button setAction:action];
|
| -
|
| - return button.autorelease();
|
| -}
|
| -
|
| -- (NSButton*)linkButtonWithTitle:(NSString*)title
|
| - frameOrigin:(NSPoint)frameOrigin
|
| - action:(SEL)action {
|
| - base::scoped_nsobject<NSButton> link(
|
| - [[HyperlinkButtonCell buttonWithString:title] retain]);
|
| -
|
| - [[link cell] setShouldUnderline:NO];
|
| - [[link cell] setTextColor:gfx::SkColorToCalibratedNSColor(
|
| - chrome_style::GetLinkColor())];
|
| - [link setTitle:title];
|
| - [link setBordered:NO];
|
| - [link setFont:[NSFont labelFontOfSize:kTextFontSize]];
|
| - [link setTarget:self];
|
| - [link setAction:action];
|
| - [link setFrameOrigin:frameOrigin];
|
| - [link sizeToFit];
|
| -
|
| - return link.autorelease();
|
| -}
|
| -
|
| -- (NSButton*)accountButtonWithRect:(NSRect)rect
|
| - title:(const std::string&)title {
|
| - base::scoped_nsobject<NSButton> button([[NSButton alloc] initWithFrame:rect]);
|
| - [button setTitle:ElideEmail(title, rect.size.width)];
|
| - [button setAlignment:NSLeftTextAlignment];
|
| - [button setBordered:NO];
|
| -
|
| - [button setImage:ui::ResourceBundle::GetSharedInstance().
|
| - GetNativeImageNamed(IDR_CLOSE_1).ToNSImage()];
|
| - [button setImagePosition:NSImageRight];
|
| - [button setTarget:self];
|
| - [button setAction:@selector(showAccountRemovalView:)];
|
| -
|
| - return button.autorelease();
|
| -}
|
| -
|
| -@end
|
|
|