1 // Copyright (c) 2013 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.
5 #import "ui/message_center/cocoa/settings_controller.h"
9 #include "base/mac/foundation_util.h"
10 #import "base/mac/scoped_nsobject.h"
11 #include "base/stl_util.h"
12 #include "base/strings/sys_string_conversions.h"
13 #include "grit/ui_strings.h"
14 #include "skia/ext/skia_utils_mac.h"
15 #include "ui/base/l10n/l10n_util.h"
16 #import "ui/message_center/cocoa/settings_entry_view.h"
17 #import "ui/message_center/cocoa/tray_view_controller.h"
18 #include "ui/message_center/message_center_style.h"
20 using message_center::settings::kHorizontalMargin;
21 using message_center::settings::kEntryHeight;
23 // Intrinsic padding pixels out of our control.
24 const int kIntrinsicHeaderTextTopPadding = 3;
25 const int kIntrinsicSubheaderTextTopPadding = 5;
26 const int kIntrinsicSubheaderTextBottomPadding = 3;
27 const int kIntrinsicDropDownVerticalPadding = 2;
28 const int kIntrinsicDropDownHorizontalPadding = 3;
30 // Corrected padding values used in layout.
31 // Calculated additional blank space above the header text, including
32 // the intrinsic blank space above the header label.
33 const int kCorrectedHeaderTextTopPadding =
34 message_center::settings::kTopMargin - kIntrinsicHeaderTextTopPadding;
36 // Calculated additional blank space above the subheader text, including
37 // the intrinsic blank space above the subheader label.
38 const int kCorrectedSubheaderTextTopPadding =
39 message_center::settings::kTitleToDescriptionSpace -
40 kIntrinsicSubheaderTextTopPadding;
42 // Calcoulated additional vertical padding for the drop-down, including the
43 // blank space included with the drop-down control.
44 const int kCorrectedDropDownTopPadding =
45 message_center::settings::kDescriptionToSwitcherSpace -
46 kIntrinsicDropDownVerticalPadding - kIntrinsicSubheaderTextBottomPadding;
48 // Calculated additional horizontal blank space for the drop down, including
49 // the blank space included with the drop-down control.
50 const int kCorrectedDropDownMargin =
51 kHorizontalMargin - kIntrinsicDropDownHorizontalPadding;
53 @interface MCSettingsController (Private)
54 // Sets the icon on the checkbox corresponding to |notifiers_[index]|.
55 - (void)setIcon:(NSImage*)icon forNotifierIndex:(size_t)index;
57 - (void)setIcon:(NSImage*)icon
58 forNotifierId:(const message_center::NotifierId&)id;
60 // Returns the NSButton corresponding to the checkbox for |notifiers_[index]|.
61 - (MCSettingsEntryView*)entryForNotifierAtIndex:(size_t)index;
63 // Update the contents view.
66 // Handler for the notifier group dropdown menu.
67 - (void)notifierGroupSelectionChanged:(id)sender;
71 namespace message_center {
73 NotifierSettingsObserverMac::~NotifierSettingsObserverMac() {}
75 void NotifierSettingsObserverMac::UpdateIconImage(const NotifierId& notifier_id,
76 const gfx::Image& icon) {
77 [settings_controller_ setIcon:icon.AsNSImage() forNotifierId:notifier_id];
80 void NotifierSettingsObserverMac::NotifierGroupChanged() {
81 [settings_controller_ updateView];
84 } // namespace message_center
86 @implementation MCSettingsController
88 - (id)initWithProvider:(message_center::NotifierSettingsProvider*)provider
89 trayViewController:(MCTrayViewController*)trayViewController {
90 if ((self = [super initWithNibName:nil bundle:nil])) {
91 observer_.reset(new message_center::NotifierSettingsObserverMac(self));
93 trayViewController_ = trayViewController;
94 provider_->AddObserver(observer_.get());
100 provider_->RemoveObserver(observer_.get());
101 provider_->OnNotifierSettingsClosing();
102 STLDeleteElements(¬ifiers_);
106 - (NSTextField*)newLabelWithFrame:(NSRect)frame {
107 NSTextField* label = [[NSTextField alloc] initWithFrame:frame];
108 [label setDrawsBackground:NO];
109 [label setBezeled:NO];
110 [label setEditable:NO];
111 [label setSelectable:NO];
112 [label setAutoresizingMask:NSViewMinYMargin];
118 [trayViewController_ updateSettings];
122 DCHECK(notifiers_.empty());
123 provider_->GetNotifierList(¬ifiers_);
124 CGFloat maxHeight = [MCTrayViewController maxTrayClientHeight];
128 NSMakeRect(0, 0, [MCTrayViewController trayWidth], maxHeight);
129 base::scoped_nsobject<NSBox> view([[NSBox alloc] initWithFrame:fullFrame]);
130 [view setBorderType:NSNoBorder];
131 [view setBoxType:NSBoxCustom];
132 [view setContentViewMargins:NSZeroSize];
133 [view setFillColor:gfx::SkColorToCalibratedNSColor(
134 message_center::kMessageCenterBackgroundColor)];
135 [view setTitlePosition:NSNoTitle];
139 NSRect headerFrame = NSMakeRect(kHorizontalMargin,
142 NSHeight(fullFrame));
143 settingsText_.reset([self newLabelWithFrame:headerFrame]);
144 [settingsText_ setAutoresizingMask:NSViewMinYMargin];
145 [settingsText_ setTextColor:
146 gfx::SkColorToCalibratedNSColor(message_center::kRegularTextColor)];
148 setFont:[NSFont messageFontOfSize:message_center::kTitleFontSize]];
150 [settingsText_ setStringValue:
151 l10n_util::GetNSString(IDS_MESSAGE_CENTER_SETTINGS_BUTTON_LABEL)];
152 [settingsText_ sizeToFit];
153 headerFrame = [settingsText_ frame];
154 headerFrame.origin.y = NSMaxY(fullFrame) - kCorrectedHeaderTextTopPadding -
155 NSHeight(headerFrame);
156 [[self view] addSubview:settingsText_];
159 NSRect subheaderFrame = NSMakeRect(kHorizontalMargin,
162 NSHeight(fullFrame));
163 detailsText_.reset([self newLabelWithFrame:subheaderFrame]);
164 [detailsText_ setAutoresizingMask:NSViewMinYMargin];
165 [detailsText_ setTextColor:
166 gfx::SkColorToCalibratedNSColor(message_center::kDimTextColor)];
168 setFont:[NSFont messageFontOfSize:message_center::kMessageFontSize]];
170 size_t groupCount = provider_->GetNotifierGroupCount();
171 [detailsText_ setStringValue:l10n_util::GetNSString(
172 groupCount > 1 ? IDS_MESSAGE_CENTER_SETTINGS_DESCRIPTION_MULTIUSER
173 : IDS_MESSAGE_CENTER_SETTINGS_DIALOG_DESCRIPTION)];
174 [detailsText_ sizeToFit];
175 subheaderFrame = [detailsText_ frame];
176 subheaderFrame.origin.y =
177 NSMinY(headerFrame) - kCorrectedSubheaderTextTopPadding -
178 NSHeight(subheaderFrame);
179 [[self view] addSubview:detailsText_];
181 // Profile switcher is only needed for more than one profile.
182 NSRect dropDownButtonFrame = subheaderFrame;
183 if (groupCount > 1) {
184 dropDownButtonFrame = NSMakeRect(kCorrectedDropDownMargin,
187 NSHeight(fullFrame));
188 groupDropDownButton_.reset(
189 [[NSPopUpButton alloc] initWithFrame:dropDownButtonFrame
191 [groupDropDownButton_ setAction:@selector(notifierGroupSelectionChanged:)];
192 [groupDropDownButton_ setTarget:self];
193 // Add a dummy item for pull-down.
194 [groupDropDownButton_ addItemWithTitle:@""];
195 base::string16 title;
196 for (size_t i = 0; i < groupCount; ++i) {
197 const message_center::NotifierGroup& group =
198 provider_->GetNotifierGroupAt(i);
199 base::string16 item =
200 group.login_info.empty() ? group.name : group.login_info;
201 [groupDropDownButton_ addItemWithTitle:base::SysUTF16ToNSString(item)];
202 if (provider_->IsNotifierGroupActiveAt(i)) {
204 [[groupDropDownButton_ lastItem] setState:NSOnState];
207 [groupDropDownButton_ setTitle:base::SysUTF16ToNSString(title)];
208 [groupDropDownButton_ sizeToFit];
209 dropDownButtonFrame = [groupDropDownButton_ frame];
210 dropDownButtonFrame.origin.y =
211 NSMinY(subheaderFrame) - kCorrectedDropDownTopPadding -
212 NSHeight(dropDownButtonFrame);
213 dropDownButtonFrame.size.width =
214 NSWidth(fullFrame) - 2 * kCorrectedDropDownMargin;
215 [[self view] addSubview:groupDropDownButton_];
218 // Document view for the notifier settings.
220 NSRect documentFrame = NSMakeRect(0, 0, NSWidth(fullFrame), 0);
221 base::scoped_nsobject<NSView> documentView(
222 [[NSView alloc] initWithFrame:documentFrame]);
223 int notifierCount = notifiers_.size();
224 for (int i = notifierCount - 1; i >= 0; --i) {
225 message_center::Notifier* notifier = notifiers_[i];
226 // TODO(thakis): Use a custom button cell.
227 NSRect frame = NSMakeRect(kHorizontalMargin,
229 NSWidth(documentFrame) - kHorizontalMargin * 2,
232 base::scoped_nsobject<MCSettingsEntryView> entryView(
233 [[MCSettingsEntryView alloc]
234 initWithController:self
237 hasSeparator:(i != notifierCount - 1)]);
238 [documentView addSubview:entryView];
239 y += NSHeight(frame);
242 documentFrame.size.height = y - kIntrinsicDropDownVerticalPadding;
243 [documentView setFrame:documentFrame];
245 // Scroll view for the notifier settings.
246 NSRect scrollFrame = documentFrame;
247 scrollFrame.origin.y = 0;
248 CGFloat remainingHeight = NSMinY(dropDownButtonFrame) - NSMinY(scrollFrame);
250 if (NSHeight(documentFrame) < remainingHeight) {
251 // Everything fits without scrolling.
252 CGFloat delta = remainingHeight - NSHeight(documentFrame);
253 headerFrame.origin.y -= delta;
254 subheaderFrame.origin.y -= delta;
255 dropDownButtonFrame.origin.y -= delta;
256 fullFrame.size.height -= delta;
258 scrollFrame.size.height = remainingHeight;
261 scrollView_.reset([[NSScrollView alloc] initWithFrame:scrollFrame]);
262 [scrollView_ setAutohidesScrollers:YES];
263 [scrollView_ setAutoresizingMask:NSViewMinYMargin];
264 [scrollView_ setDocumentView:documentView];
265 [scrollView_ setDrawsBackground:NO];
266 [scrollView_ setHasHorizontalScroller:NO];
267 [scrollView_ setHasVerticalScroller:YES];
270 NSPoint newScrollOrigin =
272 NSMaxY([[scrollView_ documentView] frame]) -
273 NSHeight([[scrollView_ contentView] bounds]));
274 [[scrollView_ documentView] scrollPoint:newScrollOrigin];
277 [[self view] setFrame:fullFrame];
278 [[self view] addSubview:scrollView_];
279 [settingsText_ setFrame:headerFrame];
280 [detailsText_ setFrame:subheaderFrame];
281 [groupDropDownButton_ setFrame:dropDownButtonFrame];
284 - (void)setSettingsNotifier:(message_center::Notifier*)notifier
285 enabled:(BOOL)enabled {
286 provider_->SetNotifierEnabled(*notifier, enabled);
289 - (void)learnMoreClicked:(message_center::Notifier*)notifier {
290 provider_->OnNotifierAdvancedSettingsRequested(notifier->notifier_id, NULL);
293 // Testing API /////////////////////////////////////////////////////////////////
295 - (NSPopUpButton*)groupDropDownButton {
296 return groupDropDownButton_;
299 - (NSScrollView*)scrollView {
303 // Private API /////////////////////////////////////////////////////////////////
305 - (void)setIcon:(NSImage*)icon forNotifierIndex:(size_t)index {
306 MCSettingsEntryView* entry = [self entryForNotifierAtIndex:index];
307 [entry setNotifierIcon:icon];
310 - (void)setIcon:(NSImage*)icon
311 forNotifierId:(const message_center::NotifierId&)id {
312 for (size_t i = 0; i < notifiers_.size(); ++i) {
313 if (notifiers_[i]->notifier_id == id) {
314 [self setIcon:icon forNotifierIndex:i];
320 - (MCSettingsEntryView*)entryForNotifierAtIndex:(size_t)index {
321 NSArray* subviews = [[scrollView_ documentView] subviews];
322 // The checkboxes are in bottom-top order, the checkbox for notifiers_[0] is
324 DCHECK_LT(notifiers_.size() - 1 - index, [subviews count]);
325 NSView* view = [subviews objectAtIndex:notifiers_.size() - 1 - index];
326 return base::mac::ObjCCastStrict<MCSettingsEntryView>(view);
329 - (void)notifierGroupSelectionChanged:(id)sender {
330 DCHECK_EQ(groupDropDownButton_.get(), sender);
331 NSPopUpButton* button = static_cast<NSPopUpButton*>(sender);
332 // The first item is a dummy item.
333 provider_->SwitchToNotifierGroup([button indexOfSelectedItem] - 1);
336 - (BOOL)notifierHasAdvancedSettings:(const message_center::NotifierId&)id {
337 return provider_->NotifierHasAdvancedSettings(id);