Home | History | Annotate | Download | only in cocoa
      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 
     21 using message_center::settings::kHorizontalMargin;
     22 using message_center::settings::kEntryHeight;
     23 
     24 // Intrinsic padding pixels out of our control.
     25 const int kIntrinsicHeaderTextTopPadding = 3;
     26 const int kIntrinsicSubheaderTextTopPadding = 5;
     27 const int kIntrinsicSubheaderTextBottomPadding = 3;
     28 const int kIntrinsicDropDownVerticalPadding = 2;
     29 const 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.
     34 const 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.
     39 const 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.
     45 const 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.
     51 const 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 
     72 namespace message_center {
     73 
     74 NotifierSettingsObserverMac::~NotifierSettingsObserverMac() {}
     75 
     76 void NotifierSettingsObserverMac::UpdateIconImage(const NotifierId& notifier_id,
     77                                                   const gfx::Image& icon) {
     78   [settings_controller_ setIcon:icon.AsNSImage() forNotifierId:notifier_id];
     79 }
     80 
     81 void NotifierSettingsObserverMac::NotifierGroupChanged() {
     82   [settings_controller_ updateView];
     83 }
     84 
     85 void 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