// Copyright (c) 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 "ui/message_center/cocoa/settings_controller.h" #include <algorithm> #include "base/mac/foundation_util.h" #import "base/mac/scoped_nsobject.h" #include "base/stl_util.h" #include "base/strings/sys_string_conversions.h" #include "grit/ui_strings.h" #include "skia/ext/skia_utils_mac.h" #include "ui/base/l10n/l10n_util.h" #import "ui/message_center/cocoa/settings_entry_view.h" #import "ui/message_center/cocoa/tray_view_controller.h" #include "ui/message_center/message_center_style.h" using message_center::settings::kHorizontalMargin; using message_center::settings::kEntryHeight; // Intrinsic padding pixels out of our control. const int kIntrinsicHeaderTextTopPadding = 3; const int kIntrinsicSubheaderTextTopPadding = 5; const int kIntrinsicSubheaderTextBottomPadding = 3; const int kIntrinsicDropDownVerticalPadding = 2; const int kIntrinsicDropDownHorizontalPadding = 3; // Corrected padding values used in layout. // Calculated additional blank space above the header text, including // the intrinsic blank space above the header label. const int kCorrectedHeaderTextTopPadding = message_center::settings::kTopMargin - kIntrinsicHeaderTextTopPadding; // Calculated additional blank space above the subheader text, including // the intrinsic blank space above the subheader label. const int kCorrectedSubheaderTextTopPadding = message_center::settings::kTitleToDescriptionSpace - kIntrinsicSubheaderTextTopPadding; // Calcoulated additional vertical padding for the drop-down, including the // blank space included with the drop-down control. const int kCorrectedDropDownTopPadding = message_center::settings::kDescriptionToSwitcherSpace - kIntrinsicDropDownVerticalPadding - kIntrinsicSubheaderTextBottomPadding; // Calculated additional horizontal blank space for the drop down, including // the blank space included with the drop-down control. const int kCorrectedDropDownMargin = kHorizontalMargin - kIntrinsicDropDownHorizontalPadding; @interface MCSettingsController (Private) // Sets the icon on the checkbox corresponding to |notifiers_[index]|. - (void)setIcon:(NSImage*)icon forNotifierIndex:(size_t)index; - (void)setIcon:(NSImage*)icon forNotifierId:(const message_center::NotifierId&)id; // Returns the NSButton corresponding to the checkbox for |notifiers_[index]|. - (MCSettingsEntryView*)entryForNotifierAtIndex:(size_t)index; // Update the contents view. - (void)updateView; // Handler for the notifier group dropdown menu. - (void)notifierGroupSelectionChanged:(id)sender; @end namespace message_center { NotifierSettingsObserverMac::~NotifierSettingsObserverMac() {} void NotifierSettingsObserverMac::UpdateIconImage(const NotifierId& notifier_id, const gfx::Image& icon) { [settings_controller_ setIcon:icon.AsNSImage() forNotifierId:notifier_id]; } void NotifierSettingsObserverMac::NotifierGroupChanged() { [settings_controller_ updateView]; } } // namespace message_center @implementation MCSettingsController - (id)initWithProvider:(message_center::NotifierSettingsProvider*)provider trayViewController:(MCTrayViewController*)trayViewController { if ((self = [super initWithNibName:nil bundle:nil])) { observer_.reset(new message_center::NotifierSettingsObserverMac(self)); provider_ = provider; trayViewController_ = trayViewController; provider_->AddObserver(observer_.get()); } return self; } - (void)dealloc { provider_->RemoveObserver(observer_.get()); provider_->OnNotifierSettingsClosing(); STLDeleteElements(¬ifiers_); [super dealloc]; } - (NSTextField*)newLabelWithFrame:(NSRect)frame { NSTextField* label = [[NSTextField alloc] initWithFrame:frame]; [label setDrawsBackground:NO]; [label setBezeled:NO]; [label setEditable:NO]; [label setSelectable:NO]; [label setAutoresizingMask:NSViewMinYMargin]; return label; } - (void)updateView { notifiers_.clear(); [trayViewController_ updateSettings]; } - (void)loadView { DCHECK(notifiers_.empty()); provider_->GetNotifierList(¬ifiers_); CGFloat maxHeight = [MCTrayViewController maxTrayClientHeight]; // Container view. NSRect fullFrame = NSMakeRect(0, 0, [MCTrayViewController trayWidth], maxHeight); base::scoped_nsobject<NSBox> view([[NSBox alloc] initWithFrame:fullFrame]); [view setBorderType:NSNoBorder]; [view setBoxType:NSBoxCustom]; [view setContentViewMargins:NSZeroSize]; [view setFillColor:gfx::SkColorToCalibratedNSColor( message_center::kMessageCenterBackgroundColor)]; [view setTitlePosition:NSNoTitle]; [self setView:view]; // "Settings" text. NSRect headerFrame = NSMakeRect(kHorizontalMargin, kHorizontalMargin, NSWidth(fullFrame), NSHeight(fullFrame)); settingsText_.reset([self newLabelWithFrame:headerFrame]); [settingsText_ setAutoresizingMask:NSViewMinYMargin]; [settingsText_ setTextColor: gfx::SkColorToCalibratedNSColor(message_center::kRegularTextColor)]; [settingsText_ setFont:[NSFont messageFontOfSize:message_center::kTitleFontSize]]; [settingsText_ setStringValue: l10n_util::GetNSString(IDS_MESSAGE_CENTER_SETTINGS_BUTTON_LABEL)]; [settingsText_ sizeToFit]; headerFrame = [settingsText_ frame]; headerFrame.origin.y = NSMaxY(fullFrame) - kCorrectedHeaderTextTopPadding - NSHeight(headerFrame); [[self view] addSubview:settingsText_]; // Subheader. NSRect subheaderFrame = NSMakeRect(kHorizontalMargin, kHorizontalMargin, NSWidth(fullFrame), NSHeight(fullFrame)); detailsText_.reset([self newLabelWithFrame:subheaderFrame]); [detailsText_ setAutoresizingMask:NSViewMinYMargin]; [detailsText_ setTextColor: gfx::SkColorToCalibratedNSColor(message_center::kDimTextColor)]; [detailsText_ setFont:[NSFont messageFontOfSize:message_center::kMessageFontSize]]; size_t groupCount = provider_->GetNotifierGroupCount(); [detailsText_ setStringValue:l10n_util::GetNSString( groupCount > 1 ? IDS_MESSAGE_CENTER_SETTINGS_DESCRIPTION_MULTIUSER : IDS_MESSAGE_CENTER_SETTINGS_DIALOG_DESCRIPTION)]; [detailsText_ sizeToFit]; subheaderFrame = [detailsText_ frame]; subheaderFrame.origin.y = NSMinY(headerFrame) - kCorrectedSubheaderTextTopPadding - NSHeight(subheaderFrame); [[self view] addSubview:detailsText_]; // Profile switcher is only needed for more than one profile. NSRect dropDownButtonFrame = subheaderFrame; if (groupCount > 1) { dropDownButtonFrame = NSMakeRect(kCorrectedDropDownMargin, kHorizontalMargin, NSWidth(fullFrame), NSHeight(fullFrame)); groupDropDownButton_.reset( [[NSPopUpButton alloc] initWithFrame:dropDownButtonFrame pullsDown:YES]); [groupDropDownButton_ setAction:@selector(notifierGroupSelectionChanged:)]; [groupDropDownButton_ setTarget:self]; // Add a dummy item for pull-down. [groupDropDownButton_ addItemWithTitle:@""]; string16 title; for (size_t i = 0; i < groupCount; ++i) { const message_center::NotifierGroup& group = provider_->GetNotifierGroupAt(i); string16 item = group.login_info.empty() ? group.name : group.login_info; [groupDropDownButton_ addItemWithTitle:base::SysUTF16ToNSString(item)]; if (provider_->IsNotifierGroupActiveAt(i)) { title = item; [[groupDropDownButton_ lastItem] setState:NSOnState]; } } [groupDropDownButton_ setTitle:base::SysUTF16ToNSString(title)]; [groupDropDownButton_ sizeToFit]; dropDownButtonFrame = [groupDropDownButton_ frame]; dropDownButtonFrame.origin.y = NSMinY(subheaderFrame) - kCorrectedDropDownTopPadding - NSHeight(dropDownButtonFrame); dropDownButtonFrame.size.width = NSWidth(fullFrame) - 2 * kCorrectedDropDownMargin; [[self view] addSubview:groupDropDownButton_]; } // Document view for the notifier settings. CGFloat y = 0; NSRect documentFrame = NSMakeRect(0, 0, NSWidth(fullFrame), 0); base::scoped_nsobject<NSView> documentView( [[NSView alloc] initWithFrame:documentFrame]); int notifierCount = notifiers_.size(); for (int i = notifierCount - 1; i >= 0; --i) { message_center::Notifier* notifier = notifiers_[i]; // TODO(thakis): Use a custom button cell. NSRect frame = NSMakeRect(kHorizontalMargin, y, NSWidth(documentFrame) - kHorizontalMargin * 2, kEntryHeight); base::scoped_nsobject<MCSettingsEntryView> entryView( [[MCSettingsEntryView alloc] initWithController:self notifier:notifier frame:frame hasSeparator:(i != notifierCount - 1)]); [documentView addSubview:entryView]; y += NSHeight(frame); } documentFrame.size.height = y - kIntrinsicDropDownVerticalPadding; [documentView setFrame:documentFrame]; // Scroll view for the notifier settings. NSRect scrollFrame = documentFrame; scrollFrame.origin.y = 0; CGFloat remainingHeight = NSMinY(dropDownButtonFrame) - NSMinY(scrollFrame); if (NSHeight(documentFrame) < remainingHeight) { // Everything fits without scrolling. CGFloat delta = remainingHeight - NSHeight(documentFrame); headerFrame.origin.y -= delta; subheaderFrame.origin.y -= delta; dropDownButtonFrame.origin.y -= delta; fullFrame.size.height -= delta; } else { scrollFrame.size.height = remainingHeight; } scrollView_.reset([[NSScrollView alloc] initWithFrame:scrollFrame]); [scrollView_ setAutohidesScrollers:YES]; [scrollView_ setAutoresizingMask:NSViewMinYMargin]; [scrollView_ setDocumentView:documentView]; [scrollView_ setDrawsBackground:NO]; [scrollView_ setHasHorizontalScroller:NO]; [scrollView_ setHasVerticalScroller:YES]; // Scroll to top. NSPoint newScrollOrigin = NSMakePoint(0.0, NSMaxY([[scrollView_ documentView] frame]) - NSHeight([[scrollView_ contentView] bounds])); [[scrollView_ documentView] scrollPoint:newScrollOrigin]; // Set final sizes. [[self view] setFrame:fullFrame]; [[self view] addSubview:scrollView_]; [settingsText_ setFrame:headerFrame]; [detailsText_ setFrame:subheaderFrame]; [groupDropDownButton_ setFrame:dropDownButtonFrame]; } - (void)setSettingsNotifier:(message_center::Notifier*)notifier enabled:(BOOL)enabled { provider_->SetNotifierEnabled(*notifier, enabled); } - (void)learnMoreClicked:(message_center::Notifier*)notifier { provider_->OnNotifierAdvancedSettingsRequested(notifier->notifier_id, NULL); } // Testing API ///////////////////////////////////////////////////////////////// - (NSPopUpButton*)groupDropDownButton { return groupDropDownButton_; } - (NSScrollView*)scrollView { return scrollView_; } // Private API ///////////////////////////////////////////////////////////////// - (void)setIcon:(NSImage*)icon forNotifierIndex:(size_t)index { MCSettingsEntryView* entry = [self entryForNotifierAtIndex:index]; [entry setNotifierIcon:icon]; } - (void)setIcon:(NSImage*)icon forNotifierId:(const message_center::NotifierId&)id { for (size_t i = 0; i < notifiers_.size(); ++i) { if (notifiers_[i]->notifier_id == id) { [self setIcon:icon forNotifierIndex:i]; return; } } } - (MCSettingsEntryView*)entryForNotifierAtIndex:(size_t)index { NSArray* subviews = [[scrollView_ documentView] subviews]; // The checkboxes are in bottom-top order, the checkbox for notifiers_[0] is // last. DCHECK_LT(notifiers_.size() - 1 - index, [subviews count]); NSView* view = [subviews objectAtIndex:notifiers_.size() - 1 - index]; return base::mac::ObjCCastStrict<MCSettingsEntryView>(view); } - (void)notifierGroupSelectionChanged:(id)sender { DCHECK_EQ(groupDropDownButton_.get(), sender); NSPopUpButton* button = static_cast<NSPopUpButton*>(sender); // The first item is a dummy item. provider_->SwitchToNotifierGroup([button indexOfSelectedItem] - 1); } - (BOOL)notifierHasAdvancedSettings:(const message_center::NotifierId&)id { return provider_->NotifierHasAdvancedSettings(id); } @end