Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / extensions / media_galleries_dialog_cocoa.mm
bloba92f70b4d29290cd183b603ae0cfa0c05aabe569
1 // Copyright (c) 2012 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 #include "chrome/browser/ui/cocoa/extensions/media_galleries_dialog_cocoa.h"
7 #include "base/mac/scoped_nsobject.h"
8 #include "base/strings/sys_string_conversions.h"
9 #include "chrome/browser/ui/chrome_style.h"
10 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_alert.h"
11 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_button.h"
12 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_control_utils.h"
13 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_sheet.h"
14 #import "chrome/browser/ui/cocoa/key_equivalent_constants.h"
15 #include "content/public/browser/web_contents.h"
16 #include "grit/generated_resources.h"
17 #import "ui/base/cocoa/flipped_view.h"
18 #import "ui/base/cocoa/menu_controller.h"
19 #import "ui/base/models/menu_model.h"
20 #include "ui/base/l10n/l10n_util.h"
21 #include "ui/base/resource/resource_bundle.h"
23 // Controller for UI events on items in the media galleries dialog.
24 @interface MediaGalleriesCocoaController : NSObject {
25  @private
26   MediaGalleriesDialogCocoa* dialog_;
29 @property(nonatomic, assign) MediaGalleriesDialogCocoa* dialog;
31 @end
33 @implementation MediaGalleriesCocoaController
35 @synthesize dialog = dialog_;
37 - (void)onAcceptButton:(id)sender {
38   dialog_->OnAcceptClicked();
41 - (void)onCancelButton:(id)sender {
42   dialog_->OnCancelClicked();
45 - (void)onAddFolderClicked:(id)sender {
46   DCHECK(dialog_);
47   dialog_->OnAddFolderClicked();
50 - (void)onCheckboxToggled:(id)sender {
51   DCHECK(dialog_);
52   dialog_->OnCheckboxToggled(sender);
55 @end
57 @interface MediaGalleriesCheckbox : NSButton {
58  @private
59   MediaGalleriesDialogCocoa* dialog_;
60   MediaGalleryPrefId prefId_;
61   base::scoped_nsobject<MenuController> menu_controller_;
64 - (id)initWithFrame:(NSRect)frameRect
65              dialog:(MediaGalleriesDialogCocoa*)dialog
66              prefId:(MediaGalleryPrefId)prefId;
67 - (NSMenu*)menuForEvent:(NSEvent*)theEvent;
69 @end
71 @implementation MediaGalleriesCheckbox
73 - (id)initWithFrame:(NSRect)frameRect
74              dialog:(MediaGalleriesDialogCocoa*)dialog
75              prefId:(MediaGalleryPrefId)prefId {
76   if ((self = [super initWithFrame:frameRect])) {
77     dialog_ = dialog;
78     prefId_ = prefId;
79   }
80   return self;
83 - (NSMenu*)menuForEvent:(NSEvent*)theEvent {
84   menu_controller_.reset(
85     [[MenuController alloc] initWithModel:dialog_->GetContextMenuModel(prefId_)
86                    useWithPopUpButtonCell:NO]);
87   return [menu_controller_ menu];
90 @end
92 namespace {
94 const CGFloat kCheckboxMargin = 10;
95 const CGFloat kCheckboxMaxWidth = 440;
96 const CGFloat kScrollAreaHeight = 220;
98 NSString* GetUniqueIDForGallery(const MediaGalleryPrefInfo& gallery) {
99   return base::SysUTF8ToNSString(gallery.device_id + gallery.path.value());
102 }  // namespace
104 MediaGalleriesDialogCocoa::MediaGalleriesDialogCocoa(
105     MediaGalleriesDialogController* controller,
106     MediaGalleriesCocoaController* cocoa_controller)
107     : controller_(controller),
108       accepted_(false),
109       cocoa_controller_([cocoa_controller retain]) {
110   [cocoa_controller_ setDialog:this];
112   alert_.reset([[ConstrainedWindowAlert alloc] init]);
114   [alert_ setMessageText:base::SysUTF16ToNSString(controller_->GetHeader())];
115   [alert_ setInformativeText:SysUTF16ToNSString(controller_->GetSubtext())];
116   [alert_ addButtonWithTitle:
117     l10n_util::GetNSString(IDS_MEDIA_GALLERIES_DIALOG_CONFIRM)
118                keyEquivalent:kKeyEquivalentReturn
119                       target:cocoa_controller_
120                       action:@selector(onAcceptButton:)];
121   [alert_ addButtonWithTitle:
122       l10n_util::GetNSString(IDS_MEDIA_GALLERIES_DIALOG_CANCEL)
123                keyEquivalent:kKeyEquivalentEscape
124                       target:cocoa_controller_
125                       action:@selector(onCancelButton:)];
126   [alert_ addButtonWithTitle:
127       l10n_util::GetNSString(IDS_MEDIA_GALLERIES_DIALOG_ADD_GALLERY)
128                keyEquivalent:kKeyEquivalentNone
129                       target:cocoa_controller_
130                       action:@selector(onAddFolderClicked:)];
131   [[alert_ closeButton] setTarget:cocoa_controller_];
132   [[alert_ closeButton] setAction:@selector(onCancelButton:)];
134   InitDialogControls();
136   // May be NULL during tests.
137   if (controller->web_contents()) {
138     base::scoped_nsobject<CustomConstrainedWindowSheet> sheet(
139         [[CustomConstrainedWindowSheet alloc]
140             initWithCustomWindow:[alert_ window]]);
141     window_.reset(new ConstrainedWindowMac(
142         this, controller->web_contents(), sheet));
143   }
146 MediaGalleriesDialogCocoa::~MediaGalleriesDialogCocoa() {
149 void MediaGalleriesDialogCocoa::InitDialogControls() {
150   accessory_.reset([[NSBox alloc] init]);
151   [accessory_ setBoxType:NSBoxCustom];
152   [accessory_ setBorderType:NSLineBorder];
153   [accessory_ setBorderWidth:1];
154   [accessory_ setCornerRadius:0];
155   [accessory_ setTitlePosition:NSNoTitle];
156   [accessory_ setBorderColor:[NSColor colorWithCalibratedRed:0.625
157                                                        green:0.625
158                                                         blue:0.625
159                                                        alpha:1.0]];
161   base::scoped_nsobject<NSScrollView> scroll_view([[NSScrollView alloc]
162       initWithFrame:NSMakeRect(0, 0, kCheckboxMaxWidth, kScrollAreaHeight)]);
163   [scroll_view setHasVerticalScroller:YES];
164   [scroll_view setHasHorizontalScroller:NO];
165   [scroll_view setBorderType:NSNoBorder];
166   [scroll_view setAutohidesScrollers:YES];
167   [[accessory_ contentView] addSubview:scroll_view];
169   // Add gallery permission checkboxes inside the scrolling view.
170   checkbox_container_.reset([[FlippedView alloc] initWithFrame:NSZeroRect]);
171   checkboxes_.reset([[NSMutableArray alloc] init]);
172   [scroll_view setDocumentView:checkbox_container_];
174   CGFloat y_pos = kCheckboxMargin;
176   y_pos = CreateAttachedCheckboxes(y_pos, controller_->AttachedPermissions());
178   if (!controller_->UnattachedPermissions().empty()) {
179     y_pos = CreateCheckboxSeparator(y_pos);
181     y_pos = CreateUnattachedCheckboxes(
182         y_pos, controller_->UnattachedPermissions());
183   }
185   [checkbox_container_ setFrame:NSMakeRect(0, 0, kCheckboxMaxWidth, y_pos + 2)];
187   // Resize to pack the scroll view if possible.
188   NSRect scroll_frame = [scroll_view frame];
189   if (NSHeight(scroll_frame) > NSHeight([checkbox_container_ frame])) {
190     scroll_frame.size.height = NSHeight([checkbox_container_ frame]);
191     [scroll_view setFrame:scroll_frame];
192   }
194   [accessory_ setFrame:NSMakeRect(
195       0, 0, kCheckboxMaxWidth, NSHeight(scroll_frame))];
196   [alert_ setAccessoryView:accessory_];
198   // As a safeguard against the user skipping reading over the dialog and just
199   // confirming, the button will be unavailable for dialogs without any checks
200   // until the user toggles something.
201   [[[alert_ buttons] objectAtIndex:0] setEnabled:
202       controller_->HasPermittedGalleries()];
204   [alert_ layout];
207 CGFloat MediaGalleriesDialogCocoa::CreateAttachedCheckboxes(
208     CGFloat y_pos,
209     const MediaGalleriesDialogController::GalleryPermissionsVector&
210         permissions) {
211   y_pos += kCheckboxMargin;
213   for (MediaGalleriesDialogController::GalleryPermissionsVector::
214        const_iterator iter = permissions.begin();
215        iter != permissions.end(); iter++) {
216     const MediaGalleriesDialogController::GalleryPermission& permission = *iter;
217     UpdateGalleryCheckbox(permission.pref_info, permission.allowed, y_pos);
218     y_pos = NSMaxY([[checkboxes_ lastObject] frame]) + kCheckboxMargin;
219   }
221   return y_pos;
224 // Add checkboxes for galleries that aren't available (i.e. removable
225 // volumes that are not currently attached).
226 CGFloat MediaGalleriesDialogCocoa::CreateUnattachedCheckboxes(
227     CGFloat y_pos,
228     const MediaGalleriesDialogController::GalleryPermissionsVector&
229         permissions) {
230   y_pos += kCheckboxMargin;
232   for (MediaGalleriesDialogController::GalleryPermissionsVector::
233        const_iterator iter = permissions.begin();
234        iter != permissions.end(); iter++) {
235     const MediaGalleriesDialogController::GalleryPermission& permission = *iter;
236     UpdateGalleryCheckbox(permission.pref_info, permission.allowed, y_pos);
237     y_pos = NSMaxY([[checkboxes_ lastObject] frame]) + kCheckboxMargin;
238   }
240   return y_pos;
243 CGFloat MediaGalleriesDialogCocoa::CreateCheckboxSeparator(CGFloat y_pos) {
244   base::scoped_nsobject<NSBox> separator(
245       [[NSBox alloc] initWithFrame:NSMakeRect(
246           0, y_pos + kCheckboxMargin * 0.5, kCheckboxMaxWidth, 1.0)]);
247   [separator setBoxType:NSBoxSeparator];
248   [separator setBorderType:NSLineBorder];
249   [separator setAlphaValue:0.2];
250   [checkbox_container_ addSubview:separator];
251   y_pos += kCheckboxMargin * 0.5 + 4;
253   base::scoped_nsobject<NSTextField> unattached_label(
254       [[NSTextField alloc] initWithFrame:NSZeroRect]);
255   [unattached_label setEditable:NO];
256   [unattached_label setSelectable:NO];
257   [unattached_label setBezeled:NO];
258   [unattached_label setAttributedStringValue:
259       constrained_window::GetAttributedLabelString(
260           base::SysUTF16ToNSString(
261               controller_->GetUnattachedLocationsHeader()),
262           chrome_style::kTextFontStyle,
263           NSNaturalTextAlignment,
264           NSLineBreakByClipping
265       )];
266   [unattached_label sizeToFit];
267   NSSize unattached_label_size = [unattached_label frame].size;
268   [unattached_label setFrame:NSMakeRect(
269       kCheckboxMargin, y_pos + kCheckboxMargin,
270       kCheckboxMaxWidth, unattached_label_size.height)];
271   [checkbox_container_ addSubview:unattached_label];
272   y_pos = NSMaxY([unattached_label frame]) + kCheckboxMargin;
274   return y_pos;
277 void MediaGalleriesDialogCocoa::OnAcceptClicked() {
278   accepted_ = true;
280   if (window_)
281     window_->CloseWebContentsModalDialog();
284 void MediaGalleriesDialogCocoa::OnCancelClicked() {
285   if (window_)
286     window_->CloseWebContentsModalDialog();
289 void MediaGalleriesDialogCocoa::OnAddFolderClicked() {
290   controller_->OnAddFolderClicked();
293 void MediaGalleriesDialogCocoa::OnCheckboxToggled(NSButton* checkbox) {
294   [[[alert_ buttons] objectAtIndex:0] setEnabled:YES];
296   const MediaGalleriesDialogController::GalleryPermissionsVector&
297       attached_permissions = controller_->AttachedPermissions();
298   for (MediaGalleriesDialogController::GalleryPermissionsVector::
299        const_reverse_iterator iter = attached_permissions.rbegin();
300        iter != attached_permissions.rend(); iter++) {
301     const MediaGalleryPrefInfo* gallery = &iter->pref_info;
302     NSString* unique_id = GetUniqueIDForGallery(*gallery);
303     if ([[[checkbox cell] representedObject] isEqual:unique_id]) {
304       controller_->DidToggleGalleryId(gallery->pref_id,
305                                       [checkbox state] == NSOnState);
306       break;
307     }
308   }
310   const MediaGalleriesDialogController::GalleryPermissionsVector&
311       unattached_permissions = controller_->UnattachedPermissions();
312   for (MediaGalleriesDialogController::GalleryPermissionsVector::
313        const_reverse_iterator iter = unattached_permissions.rbegin();
314        iter != unattached_permissions.rend(); iter++) {
315     const MediaGalleryPrefInfo* gallery = &iter->pref_info;
316     NSString* unique_id = GetUniqueIDForGallery(*gallery);
317     if ([[[checkbox cell] representedObject] isEqual:unique_id]) {
318       controller_->DidToggleGalleryId(gallery->pref_id,
319                                       [checkbox state] == NSOnState);
320       break;
321     }
322   }
326 void MediaGalleriesDialogCocoa::UpdateGalleryCheckbox(
327     const MediaGalleryPrefInfo& gallery,
328     bool permitted,
329     CGFloat y_pos) {
330   base::scoped_nsobject<MediaGalleriesCheckbox> checkbox(
331       [[MediaGalleriesCheckbox alloc] initWithFrame:NSZeroRect
332                                              dialog:this
333                                              prefId:gallery.pref_id]);
334   NSString* unique_id = GetUniqueIDForGallery(gallery);
335   [[checkbox cell] setRepresentedObject:unique_id];
336   [[checkbox cell] setLineBreakMode:NSLineBreakByTruncatingMiddle];
337   [checkbox setButtonType:NSSwitchButton];
338   [checkbox setTarget:cocoa_controller_];
339   [checkbox setAction:@selector(onCheckboxToggled:)];
340   [checkboxes_ addObject:checkbox];
342   // TODO(gbillock): Would be nice to add middle text elide behavior here.
343   [checkbox setTitle:base::SysUTF16ToNSString(
344       gallery.GetGalleryDisplayName())];
345   [checkbox setToolTip:base::SysUTF16ToNSString(gallery.GetGalleryTooltip())];
346   [checkbox setState:permitted ? NSOnState : NSOffState];
348   [checkbox sizeToFit];
349   NSRect rect = [checkbox bounds];
350   rect.origin.y = y_pos;
351   rect.origin.x = kCheckboxMargin;
352   rect.size.width = std::min(NSWidth(rect), kCheckboxMaxWidth);
353   [checkbox setFrame:rect];
354   [checkbox_container_ addSubview:checkbox];
356   base::scoped_nsobject<NSTextField> details(
357       [[NSTextField alloc] initWithFrame:NSZeroRect]);
358   [details setEditable:NO];
359   [details setSelectable:NO];
360   [details setBezeled:NO];
361   [details setAttributedStringValue:
362       constrained_window::GetAttributedLabelString(
363           base::SysUTF16ToNSString(gallery.GetGalleryAdditionalDetails()),
364           chrome_style::kTextFontStyle,
365           NSNaturalTextAlignment,
366           NSLineBreakByClipping
367       )];
368   [details setTextColor:[NSColor colorWithCalibratedRed:0.625
369                                                   green:0.625
370                                                    blue:0.625
371                                                   alpha:1.0]];
372   [details sizeToFit];
373   NSRect details_rect = [details bounds];
374   details_rect.origin.y = y_pos - 1;
375   details_rect.origin.x = kCheckboxMargin + rect.size.width + kCheckboxMargin;
376   details_rect.size.width = kCheckboxMaxWidth - details_rect.origin.x;
377   [details setFrame:details_rect];
379   [checkbox_container_ addSubview:details];
382 void MediaGalleriesDialogCocoa::UpdateGalleries() {
383   InitDialogControls();
386 void MediaGalleriesDialogCocoa::OnConstrainedWindowClosed(
387     ConstrainedWindowMac* window) {
388   controller_->DialogFinished(accepted_);
391 ui::MenuModel* MediaGalleriesDialogCocoa::GetContextMenuModel(
392     MediaGalleryPrefId prefid) {
393   return controller_->GetContextMenuModel(prefid);
396 // static
397 MediaGalleriesDialog* MediaGalleriesDialog::Create(
398     MediaGalleriesDialogController* controller) {
399   base::scoped_nsobject<MediaGalleriesCocoaController> cocoa_controller(
400       [[MediaGalleriesCocoaController alloc] init]);
401   return new MediaGalleriesDialogCocoa(controller, cocoa_controller);