• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4
5#import "ui/message_center/cocoa/settings_controller.h"
6
7#include <algorithm>
8
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 "skia/ext/skia_utils_mac.h"
14#include "ui/base/l10n/l10n_util.h"
15#import "ui/message_center/cocoa/opaque_views.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"
19#include "ui/strings/grit/ui_strings.h"
20
21using message_center::settings::kHorizontalMargin;
22using message_center::settings::kEntryHeight;
23
24// Intrinsic padding pixels out of our control.
25const int kIntrinsicHeaderTextTopPadding = 3;
26const int kIntrinsicSubheaderTextTopPadding = 5;
27const int kIntrinsicSubheaderTextBottomPadding = 3;
28const int kIntrinsicDropDownVerticalPadding = 2;
29const int kIntrinsicDropDownHorizontalPadding = 3;
30
31// Corrected padding values used in layout.
32// Calculated additional blank space above the header text, including
33// the intrinsic blank space above the header label.
34const int kCorrectedHeaderTextTopPadding =
35    message_center::settings::kTopMargin - kIntrinsicHeaderTextTopPadding;
36
37// Calculated additional blank space above the subheader text, including
38// the intrinsic blank space above the subheader label.
39const int kCorrectedSubheaderTextTopPadding =
40    message_center::settings::kTitleToDescriptionSpace -
41    kIntrinsicSubheaderTextTopPadding;
42
43// Calcoulated additional vertical padding for the drop-down, including the
44// blank space included with the drop-down control.
45const int kCorrectedDropDownTopPadding =
46    message_center::settings::kDescriptionToSwitcherSpace -
47    kIntrinsicDropDownVerticalPadding - kIntrinsicSubheaderTextBottomPadding;
48
49// Calculated additional horizontal blank space for the drop down, including
50// the blank space included with the drop-down control.
51const int kCorrectedDropDownMargin =
52    kHorizontalMargin - kIntrinsicDropDownHorizontalPadding;
53
54@interface MCSettingsController (Private)
55// Sets the icon on the checkbox corresponding to |notifiers_[index]|.
56- (void)setIcon:(NSImage*)icon forNotifierIndex:(size_t)index;
57
58- (void)setIcon:(NSImage*)icon
59    forNotifierId:(const message_center::NotifierId&)id;
60
61// Returns the NSButton corresponding to the checkbox for |notifiers_[index]|.
62- (MCSettingsEntryView*)entryForNotifierAtIndex:(size_t)index;
63
64// Update the contents view.
65- (void)updateView;
66
67// Handler for the notifier group dropdown menu.
68- (void)notifierGroupSelectionChanged:(id)sender;
69
70@end
71
72namespace message_center {
73
74NotifierSettingsObserverMac::~NotifierSettingsObserverMac() {}
75
76void NotifierSettingsObserverMac::UpdateIconImage(const NotifierId& notifier_id,
77                                                  const gfx::Image& icon) {
78  [settings_controller_ setIcon:icon.AsNSImage() forNotifierId:notifier_id];
79}
80
81void NotifierSettingsObserverMac::NotifierGroupChanged() {
82  [settings_controller_ updateView];
83}
84
85void NotifierSettingsObserverMac::NotifierEnabledChanged(
86    const NotifierId& notifier_id, bool enabled) {}
87
88}  // namespace message_center
89
90@implementation MCSettingsController
91
92- (id)initWithProvider:(message_center::NotifierSettingsProvider*)provider
93    trayViewController:(MCTrayViewController*)trayViewController {
94  if ((self = [super initWithNibName:nil bundle:nil])) {
95    observer_.reset(new message_center::NotifierSettingsObserverMac(self));
96    provider_ = provider;
97    trayViewController_ = trayViewController;
98    provider_->AddObserver(observer_.get());
99  }
100  return self;
101}
102
103- (void)dealloc {
104  provider_->RemoveObserver(observer_.get());
105  provider_->OnNotifierSettingsClosing();
106  STLDeleteElements(&notifiers_);
107  [super dealloc];
108}
109
110- (NSTextField*)newLabelWithFrame:(NSRect)frame {
111  NSColor* color = gfx::SkColorToCalibratedNSColor(
112      message_center::kMessageCenterBackgroundColor);
113  MCTextField* label =
114      [[MCTextField alloc] initWithFrame:frame backgroundColor:color];
115
116  return label;
117}
118
119- (void)updateView {
120  notifiers_.clear();
121  [trayViewController_ updateSettings];
122}
123
124- (void)loadView {
125  DCHECK(notifiers_.empty());
126  provider_->GetNotifierList(&notifiers_);
127  CGFloat maxHeight = [MCTrayViewController maxTrayClientHeight];
128
129  // Container view.
130  NSRect fullFrame =
131      NSMakeRect(0, 0, [MCTrayViewController trayWidth], maxHeight);
132  base::scoped_nsobject<NSBox> view([[NSBox alloc] initWithFrame:fullFrame]);
133  [view setBorderType:NSNoBorder];
134  [view setBoxType:NSBoxCustom];
135  [view setContentViewMargins:NSZeroSize];
136  [view setFillColor:gfx::SkColorToCalibratedNSColor(
137      message_center::kMessageCenterBackgroundColor)];
138  [view setTitlePosition:NSNoTitle];
139  [self setView:view];
140
141  // "Settings" text.
142  NSRect headerFrame = NSMakeRect(kHorizontalMargin,
143                                  kHorizontalMargin,
144                                  NSWidth(fullFrame),
145                                  NSHeight(fullFrame));
146  settingsText_.reset([self newLabelWithFrame:headerFrame]);
147  [settingsText_ setTextColor:
148          gfx::SkColorToCalibratedNSColor(message_center::kRegularTextColor)];
149  [settingsText_
150      setFont:[NSFont messageFontOfSize:message_center::kTitleFontSize]];
151
152  [settingsText_ setStringValue:
153          l10n_util::GetNSString(IDS_MESSAGE_CENTER_SETTINGS_BUTTON_LABEL)];
154  [settingsText_ sizeToFit];
155  headerFrame = [settingsText_ frame];
156  headerFrame.origin.y = NSMaxY(fullFrame) - kCorrectedHeaderTextTopPadding -
157                         NSHeight(headerFrame);
158  [[self view] addSubview:settingsText_];
159
160  // Subheader.
161  NSRect subheaderFrame = NSMakeRect(kHorizontalMargin,
162                                     kHorizontalMargin,
163                                     NSWidth(fullFrame),
164                                     NSHeight(fullFrame));
165  detailsText_.reset([self newLabelWithFrame:subheaderFrame]);
166  [detailsText_ setTextColor:
167      gfx::SkColorToCalibratedNSColor(message_center::kDimTextColor)];
168  [detailsText_
169      setFont:[NSFont messageFontOfSize:message_center::kMessageFontSize]];
170
171  size_t groupCount = provider_->GetNotifierGroupCount();
172  [detailsText_ setStringValue:l10n_util::GetNSString(
173      groupCount > 1 ? IDS_MESSAGE_CENTER_SETTINGS_DESCRIPTION_MULTIUSER
174                     : IDS_MESSAGE_CENTER_SETTINGS_DIALOG_DESCRIPTION)];
175  [detailsText_ sizeToFit];
176  subheaderFrame = [detailsText_ frame];
177  subheaderFrame.origin.y =
178      NSMinY(headerFrame) - kCorrectedSubheaderTextTopPadding -
179      NSHeight(subheaderFrame);
180  [[self view] addSubview:detailsText_];
181
182  // Profile switcher is only needed for more than one profile.
183  NSRect dropDownButtonFrame = subheaderFrame;
184  if (groupCount > 1) {
185    dropDownButtonFrame = NSMakeRect(kCorrectedDropDownMargin,
186                                     kHorizontalMargin,
187                                     NSWidth(fullFrame),
188                                     NSHeight(fullFrame));
189    groupDropDownButton_.reset(
190        [[MCDropDown alloc] initWithFrame:dropDownButtonFrame pullsDown:YES]);
191    [groupDropDownButton_
192        setBackgroundColor:gfx::SkColorToCalibratedNSColor(
193                               message_center::kMessageCenterBackgroundColor)];
194    [groupDropDownButton_ setAction:@selector(notifierGroupSelectionChanged:)];
195    [groupDropDownButton_ setTarget:self];
196    // Add a dummy item for pull-down.
197    [groupDropDownButton_ addItemWithTitle:@""];
198    base::string16 title;
199    for (size_t i = 0; i < groupCount; ++i) {
200      const message_center::NotifierGroup& group =
201          provider_->GetNotifierGroupAt(i);
202      base::string16 item =
203          group.login_info.empty() ? group.name : group.login_info;
204      [groupDropDownButton_ addItemWithTitle:base::SysUTF16ToNSString(item)];
205      if (provider_->IsNotifierGroupActiveAt(i)) {
206        title = item;
207        [[groupDropDownButton_ lastItem] setState:NSOnState];
208      }
209    }
210    [groupDropDownButton_ setTitle:base::SysUTF16ToNSString(title)];
211    [groupDropDownButton_ sizeToFit];
212    dropDownButtonFrame = [groupDropDownButton_ frame];
213    dropDownButtonFrame.origin.y =
214        NSMinY(subheaderFrame) - kCorrectedDropDownTopPadding -
215        NSHeight(dropDownButtonFrame);
216    dropDownButtonFrame.size.width =
217        NSWidth(fullFrame) - 2 * kCorrectedDropDownMargin;
218    [[self view] addSubview:groupDropDownButton_];
219  }
220
221  // Document view for the notifier settings.
222  CGFloat y = 0;
223  NSRect documentFrame = NSMakeRect(0, 0, NSWidth(fullFrame), 0);
224  base::scoped_nsobject<NSView> documentView(
225      [[NSView alloc] initWithFrame:documentFrame]);
226  int notifierCount = notifiers_.size();
227  for (int i = notifierCount - 1; i >= 0; --i) {
228    message_center::Notifier* notifier = notifiers_[i];
229    // TODO(thakis): Use a custom button cell.
230    NSRect frame = NSMakeRect(kHorizontalMargin,
231                              y,
232                              NSWidth(documentFrame) - kHorizontalMargin * 2,
233                              kEntryHeight);
234
235    base::scoped_nsobject<MCSettingsEntryView> entryView(
236        [[MCSettingsEntryView alloc]
237            initWithController:self
238                      notifier:notifier
239                         frame:frame
240                  hasSeparator:(i != notifierCount - 1)]);
241    [documentView addSubview:entryView];
242    y += NSHeight(frame);
243  }
244
245  documentFrame.size.height = y - kIntrinsicDropDownVerticalPadding;
246  [documentView setFrame:documentFrame];
247
248  // Scroll view for the notifier settings.
249  NSRect scrollFrame = documentFrame;
250  scrollFrame.origin.y = 0;
251  CGFloat remainingHeight = NSMinY(dropDownButtonFrame) - NSMinY(scrollFrame);
252
253  if (NSHeight(documentFrame) < remainingHeight) {
254    // Everything fits without scrolling.
255    CGFloat delta = remainingHeight - NSHeight(documentFrame);
256    headerFrame.origin.y -= delta;
257    subheaderFrame.origin.y -= delta;
258    dropDownButtonFrame.origin.y -= delta;
259    fullFrame.size.height -= delta;
260  } else {
261    scrollFrame.size.height = remainingHeight;
262  }
263
264  scrollView_.reset([[NSScrollView alloc] initWithFrame:scrollFrame]);
265  [scrollView_ setAutohidesScrollers:YES];
266  [scrollView_ setAutoresizingMask:NSViewMinYMargin];
267  [scrollView_ setDocumentView:documentView];
268  [scrollView_ setDrawsBackground:NO];
269  [scrollView_ setHasHorizontalScroller:NO];
270  [scrollView_ setHasVerticalScroller:YES];
271
272  // Scroll to top.
273  NSPoint newScrollOrigin =
274      NSMakePoint(0.0,
275                  NSMaxY([[scrollView_ documentView] frame]) -
276                      NSHeight([[scrollView_ contentView] bounds]));
277  [[scrollView_ documentView] scrollPoint:newScrollOrigin];
278
279  // Set final sizes.
280  [[self view] setFrame:fullFrame];
281  [[self view] addSubview:scrollView_];
282  [settingsText_ setFrame:headerFrame];
283  [detailsText_ setFrame:subheaderFrame];
284  [groupDropDownButton_ setFrame:dropDownButtonFrame];
285}
286
287- (void)setSettingsNotifier:(message_center::Notifier*)notifier
288                    enabled:(BOOL)enabled {
289  provider_->SetNotifierEnabled(*notifier, enabled);
290}
291
292- (void)learnMoreClicked:(message_center::Notifier*)notifier {
293  provider_->OnNotifierAdvancedSettingsRequested(notifier->notifier_id, NULL);
294}
295
296// Testing API /////////////////////////////////////////////////////////////////
297
298- (NSPopUpButton*)groupDropDownButton {
299  return groupDropDownButton_;
300}
301
302- (NSScrollView*)scrollView {
303  return scrollView_;
304}
305
306// Private API /////////////////////////////////////////////////////////////////
307
308- (void)setIcon:(NSImage*)icon forNotifierIndex:(size_t)index {
309  MCSettingsEntryView* entry = [self entryForNotifierAtIndex:index];
310  [entry setNotifierIcon:icon];
311}
312
313- (void)setIcon:(NSImage*)icon
314    forNotifierId:(const message_center::NotifierId&)id {
315  for (size_t i = 0; i < notifiers_.size(); ++i) {
316    if (notifiers_[i]->notifier_id == id) {
317      [self setIcon:icon forNotifierIndex:i];
318      return;
319    }
320  }
321}
322
323- (MCSettingsEntryView*)entryForNotifierAtIndex:(size_t)index {
324  NSArray* subviews = [[scrollView_ documentView] subviews];
325  // The checkboxes are in bottom-top order, the checkbox for notifiers_[0] is
326  // last.
327  DCHECK_LT(notifiers_.size() - 1 - index, [subviews count]);
328  NSView* view = [subviews objectAtIndex:notifiers_.size() - 1 - index];
329  return base::mac::ObjCCastStrict<MCSettingsEntryView>(view);
330}
331
332- (void)notifierGroupSelectionChanged:(id)sender {
333  DCHECK_EQ(groupDropDownButton_.get(), sender);
334  NSPopUpButton* button = static_cast<NSPopUpButton*>(sender);
335  // The first item is a dummy item.
336  provider_->SwitchToNotifierGroup([button indexOfSelectedItem] - 1);
337}
338
339- (BOOL)notifierHasAdvancedSettings:(const message_center::NotifierId&)id {
340  return provider_->NotifierHasAdvancedSettings(id);
341}
342
343@end
344