1f8e2045171916b1d3c0b48da0ff2894efef5c51
[platform/framework/web/crosswalk.git] / src / ui / message_center / cocoa / settings_controller.mm
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 "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"
19
20 using message_center::settings::kHorizontalMargin;
21 using message_center::settings::kEntryHeight;
22
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;
29
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;
35
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;
41
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;
47
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;
52
53 @interface MCSettingsController (Private)
54 // Sets the icon on the checkbox corresponding to |notifiers_[index]|.
55 - (void)setIcon:(NSImage*)icon forNotifierIndex:(size_t)index;
56
57 - (void)setIcon:(NSImage*)icon
58     forNotifierId:(const message_center::NotifierId&)id;
59
60 // Returns the NSButton corresponding to the checkbox for |notifiers_[index]|.
61 - (MCSettingsEntryView*)entryForNotifierAtIndex:(size_t)index;
62
63 // Update the contents view.
64 - (void)updateView;
65
66 // Handler for the notifier group dropdown menu.
67 - (void)notifierGroupSelectionChanged:(id)sender;
68
69 @end
70
71 namespace message_center {
72
73 NotifierSettingsObserverMac::~NotifierSettingsObserverMac() {}
74
75 void NotifierSettingsObserverMac::UpdateIconImage(const NotifierId& notifier_id,
76                                                   const gfx::Image& icon) {
77   [settings_controller_ setIcon:icon.AsNSImage() forNotifierId:notifier_id];
78 }
79
80 void NotifierSettingsObserverMac::NotifierGroupChanged() {
81   [settings_controller_ updateView];
82 }
83
84 }  // namespace message_center
85
86 @implementation MCSettingsController
87
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));
92     provider_ = provider;
93     trayViewController_ = trayViewController;
94     provider_->AddObserver(observer_.get());
95   }
96   return self;
97 }
98
99 - (void)dealloc {
100   provider_->RemoveObserver(observer_.get());
101   provider_->OnNotifierSettingsClosing();
102   STLDeleteElements(&notifiers_);
103   [super dealloc];
104 }
105
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];
113   return label;
114 }
115
116 - (void)updateView {
117   notifiers_.clear();
118   [trayViewController_ updateSettings];
119 }
120
121 - (void)loadView {
122   DCHECK(notifiers_.empty());
123   provider_->GetNotifierList(&notifiers_);
124   CGFloat maxHeight = [MCTrayViewController maxTrayClientHeight];
125
126   // Container view.
127   NSRect fullFrame =
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];
136   [self setView:view];
137
138   // "Settings" text.
139   NSRect headerFrame = NSMakeRect(kHorizontalMargin,
140                                   kHorizontalMargin,
141                                   NSWidth(fullFrame),
142                                   NSHeight(fullFrame));
143   settingsText_.reset([self newLabelWithFrame:headerFrame]);
144   [settingsText_ setAutoresizingMask:NSViewMinYMargin];
145   [settingsText_ setTextColor:
146           gfx::SkColorToCalibratedNSColor(message_center::kRegularTextColor)];
147   [settingsText_
148       setFont:[NSFont messageFontOfSize:message_center::kTitleFontSize]];
149
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_];
157
158   // Subheader.
159   NSRect subheaderFrame = NSMakeRect(kHorizontalMargin,
160                                      kHorizontalMargin,
161                                      NSWidth(fullFrame),
162                                      NSHeight(fullFrame));
163   detailsText_.reset([self newLabelWithFrame:subheaderFrame]);
164   [detailsText_ setAutoresizingMask:NSViewMinYMargin];
165   [detailsText_ setTextColor:
166       gfx::SkColorToCalibratedNSColor(message_center::kDimTextColor)];
167   [detailsText_
168       setFont:[NSFont messageFontOfSize:message_center::kMessageFontSize]];
169
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_];
180
181   // Profile switcher is only needed for more than one profile.
182   NSRect dropDownButtonFrame = subheaderFrame;
183   if (groupCount > 1) {
184     dropDownButtonFrame = NSMakeRect(kCorrectedDropDownMargin,
185                                      kHorizontalMargin,
186                                      NSWidth(fullFrame),
187                                      NSHeight(fullFrame));
188     groupDropDownButton_.reset(
189         [[NSPopUpButton alloc] initWithFrame:dropDownButtonFrame
190                                    pullsDown:YES]);
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)) {
203         title = item;
204         [[groupDropDownButton_ lastItem] setState:NSOnState];
205       }
206     }
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_];
216   }
217
218   // Document view for the notifier settings.
219   CGFloat y = 0;
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,
228                               y,
229                               NSWidth(documentFrame) - kHorizontalMargin * 2,
230                               kEntryHeight);
231
232     base::scoped_nsobject<MCSettingsEntryView> entryView(
233         [[MCSettingsEntryView alloc]
234             initWithController:self
235                       notifier:notifier
236                          frame:frame
237                   hasSeparator:(i != notifierCount - 1)]);
238     [documentView addSubview:entryView];
239     y += NSHeight(frame);
240   }
241
242   documentFrame.size.height = y - kIntrinsicDropDownVerticalPadding;
243   [documentView setFrame:documentFrame];
244
245   // Scroll view for the notifier settings.
246   NSRect scrollFrame = documentFrame;
247   scrollFrame.origin.y = 0;
248   CGFloat remainingHeight = NSMinY(dropDownButtonFrame) - NSMinY(scrollFrame);
249
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;
257   } else {
258     scrollFrame.size.height = remainingHeight;
259   }
260
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];
268
269   // Scroll to top.
270   NSPoint newScrollOrigin =
271       NSMakePoint(0.0,
272                   NSMaxY([[scrollView_ documentView] frame]) -
273                       NSHeight([[scrollView_ contentView] bounds]));
274   [[scrollView_ documentView] scrollPoint:newScrollOrigin];
275
276   // Set final sizes.
277   [[self view] setFrame:fullFrame];
278   [[self view] addSubview:scrollView_];
279   [settingsText_ setFrame:headerFrame];
280   [detailsText_ setFrame:subheaderFrame];
281   [groupDropDownButton_ setFrame:dropDownButtonFrame];
282 }
283
284 - (void)setSettingsNotifier:(message_center::Notifier*)notifier
285                     enabled:(BOOL)enabled {
286   provider_->SetNotifierEnabled(*notifier, enabled);
287 }
288
289 - (void)learnMoreClicked:(message_center::Notifier*)notifier {
290   provider_->OnNotifierAdvancedSettingsRequested(notifier->notifier_id, NULL);
291 }
292
293 // Testing API /////////////////////////////////////////////////////////////////
294
295 - (NSPopUpButton*)groupDropDownButton {
296   return groupDropDownButton_;
297 }
298
299 - (NSScrollView*)scrollView {
300   return scrollView_;
301 }
302
303 // Private API /////////////////////////////////////////////////////////////////
304
305 - (void)setIcon:(NSImage*)icon forNotifierIndex:(size_t)index {
306   MCSettingsEntryView* entry = [self entryForNotifierAtIndex:index];
307   [entry setNotifierIcon:icon];
308 }
309
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];
315       return;
316     }
317   }
318 }
319
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
323   // last.
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);
327 }
328
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);
334 }
335
336 - (BOOL)notifierHasAdvancedSettings:(const message_center::NotifierId&)id {
337   return provider_->NotifierHasAdvancedSettings(id);
338 }
339
340 @end