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/strings/sys_string_conversions.h"
8 #include "chrome/browser/ui/chrome_style.h"
9 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_alert.h"
10 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_button.h"
11 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_control_utils.h"
12 #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_custom_sheet.h"
13 #import "chrome/browser/ui/cocoa/key_equivalent_constants.h"
14 #include "content/public/browser/web_contents.h"
15 #include "grit/generated_resources.h"
16 #import "ui/base/cocoa/flipped_view.h"
17 #include "ui/base/l10n/l10n_util.h"
18 #include "ui/base/resource/resource_bundle.h"
20 @interface MediaGalleriesCocoaController : NSObject {
22 MediaGalleriesDialogCocoa* dialog_;
25 @property(nonatomic, assign) MediaGalleriesDialogCocoa* dialog;
29 @implementation MediaGalleriesCocoaController
31 @synthesize dialog = dialog_;
33 - (void)onAcceptButton:(id)sender {
34 dialog_->OnAcceptClicked();
37 - (void)onCancelButton:(id)sender {
38 dialog_->OnCancelClicked();
41 - (void)onAddFolderClicked:(id)sender {
43 dialog_->OnAddFolderClicked();
46 - (void)onCheckboxToggled:(id)sender {
48 dialog_->OnCheckboxToggled(sender);
55 const CGFloat kCheckboxMargin = 10;
56 const CGFloat kCheckboxMaxWidth = 440;
57 const CGFloat kScrollAreaHeight = 220;
59 NSString* GetUniqueIDForGallery(const MediaGalleryPrefInfo& gallery) {
60 return base::SysUTF8ToNSString(gallery.device_id + gallery.path.value());
65 MediaGalleriesDialogCocoa::MediaGalleriesDialogCocoa(
66 MediaGalleriesDialogController* controller,
67 MediaGalleriesCocoaController* cocoa_controller)
68 : controller_(controller),
70 cocoa_controller_([cocoa_controller retain]) {
71 [cocoa_controller_ setDialog:this];
73 alert_.reset([[ConstrainedWindowAlert alloc] init]);
75 [alert_ setMessageText:base::SysUTF16ToNSString(controller_->GetHeader())];
76 [alert_ setInformativeText:SysUTF16ToNSString(controller_->GetSubtext())];
77 [alert_ addButtonWithTitle:
78 l10n_util::GetNSString(IDS_MEDIA_GALLERIES_DIALOG_CONFIRM)
79 keyEquivalent:kKeyEquivalentReturn
80 target:cocoa_controller_
81 action:@selector(onAcceptButton:)];
82 [alert_ addButtonWithTitle:
83 l10n_util::GetNSString(IDS_MEDIA_GALLERIES_DIALOG_CANCEL)
84 keyEquivalent:kKeyEquivalentEscape
85 target:cocoa_controller_
86 action:@selector(onCancelButton:)];
87 [alert_ addButtonWithTitle:
88 l10n_util::GetNSString(IDS_MEDIA_GALLERIES_DIALOG_ADD_GALLERY)
89 keyEquivalent:kKeyEquivalentNone
90 target:cocoa_controller_
91 action:@selector(onAddFolderClicked:)];
92 [[alert_ closeButton] setTarget:cocoa_controller_];
93 [[alert_ closeButton] setAction:@selector(onCancelButton:)];
97 // May be NULL during tests.
98 if (controller->web_contents()) {
99 base::scoped_nsobject<CustomConstrainedWindowSheet> sheet(
100 [[CustomConstrainedWindowSheet alloc]
101 initWithCustomWindow:[alert_ window]]);
102 window_.reset(new ConstrainedWindowMac(
103 this, controller->web_contents(), sheet));
107 MediaGalleriesDialogCocoa::~MediaGalleriesDialogCocoa() {
110 void MediaGalleriesDialogCocoa::InitDialogControls() {
111 accessory_.reset([[NSBox alloc] init]);
112 [accessory_ setBoxType:NSBoxCustom];
113 [accessory_ setBorderType:NSLineBorder];
114 [accessory_ setBorderWidth:1];
115 [accessory_ setCornerRadius:0];
116 [accessory_ setTitlePosition:NSNoTitle];
117 [accessory_ setBorderColor:[NSColor colorWithCalibratedRed:0.625
122 base::scoped_nsobject<NSScrollView> scroll_view([[NSScrollView alloc]
123 initWithFrame:NSMakeRect(0, 0, kCheckboxMaxWidth, kScrollAreaHeight)]);
124 [scroll_view setHasVerticalScroller:YES];
125 [scroll_view setHasHorizontalScroller:NO];
126 [scroll_view setBorderType:NSNoBorder];
127 [scroll_view setAutohidesScrollers:YES];
128 [[accessory_ contentView] addSubview:scroll_view];
130 // Add gallery permission checkboxes inside the scrolling view.
131 checkbox_container_.reset([[FlippedView alloc] initWithFrame:NSZeroRect]);
132 checkboxes_.reset([[NSMutableArray alloc] init]);
133 [scroll_view setDocumentView:checkbox_container_];
135 CGFloat y_pos = kCheckboxMargin;
137 y_pos = CreateAttachedCheckboxes(y_pos, controller_->AttachedPermissions());
139 if (!controller_->UnattachedPermissions().empty()) {
140 y_pos = CreateCheckboxSeparator(y_pos);
142 y_pos = CreateUnattachedCheckboxes(
143 y_pos, controller_->UnattachedPermissions());
146 [checkbox_container_ setFrame:NSMakeRect(0, 0, kCheckboxMaxWidth, y_pos + 2)];
148 // Resize to pack the scroll view if possible.
149 NSRect scroll_frame = [scroll_view frame];
150 if (NSHeight(scroll_frame) > NSHeight([checkbox_container_ frame])) {
151 scroll_frame.size.height = NSHeight([checkbox_container_ frame]);
152 [scroll_view setFrame:scroll_frame];
155 [accessory_ setFrame:NSMakeRect(
156 0, 0, kCheckboxMaxWidth, NSHeight(scroll_frame))];
157 [alert_ setAccessoryView:accessory_];
159 // As a safeguard against the user skipping reading over the dialog and just
160 // confirming, the button will be unavailable for dialogs without any checks
161 // until the user toggles something.
162 [[[alert_ buttons] objectAtIndex:0] setEnabled:
163 controller_->HasPermittedGalleries()];
168 CGFloat MediaGalleriesDialogCocoa::CreateAttachedCheckboxes(
170 const MediaGalleriesDialogController::GalleryPermissionsVector&
172 y_pos += kCheckboxMargin;
174 for (MediaGalleriesDialogController::GalleryPermissionsVector::
175 const_iterator iter = permissions.begin();
176 iter != permissions.end(); iter++) {
177 const MediaGalleriesDialogController::GalleryPermission& permission = *iter;
178 UpdateGalleryCheckbox(permission.pref_info, permission.allowed, y_pos);
179 y_pos = NSMaxY([[checkboxes_ lastObject] frame]) + kCheckboxMargin;
185 // Add checkboxes for galleries that aren't available (i.e. removable
186 // volumes that are not currently attached).
187 CGFloat MediaGalleriesDialogCocoa::CreateUnattachedCheckboxes(
189 const MediaGalleriesDialogController::GalleryPermissionsVector&
191 y_pos += kCheckboxMargin;
193 for (MediaGalleriesDialogController::GalleryPermissionsVector::
194 const_iterator iter = permissions.begin();
195 iter != permissions.end(); iter++) {
196 const MediaGalleriesDialogController::GalleryPermission& permission = *iter;
197 UpdateGalleryCheckbox(permission.pref_info, permission.allowed, y_pos);
198 y_pos = NSMaxY([[checkboxes_ lastObject] frame]) + kCheckboxMargin;
204 CGFloat MediaGalleriesDialogCocoa::CreateCheckboxSeparator(CGFloat y_pos) {
205 base::scoped_nsobject<NSBox> separator(
206 [[NSBox alloc] initWithFrame:NSMakeRect(
207 0, y_pos + kCheckboxMargin * 0.5, kCheckboxMaxWidth, 1.0)]);
208 [separator setBoxType:NSBoxSeparator];
209 [separator setBorderType:NSLineBorder];
210 [separator setAlphaValue:0.2];
211 [checkbox_container_ addSubview:separator];
212 y_pos += kCheckboxMargin * 0.5 + 4;
214 base::scoped_nsobject<NSTextField> unattached_label(
215 [[NSTextField alloc] initWithFrame:NSZeroRect]);
216 [unattached_label setEditable:NO];
217 [unattached_label setSelectable:NO];
218 [unattached_label setBezeled:NO];
219 [unattached_label setAttributedStringValue:
220 constrained_window::GetAttributedLabelString(
221 base::SysUTF16ToNSString(
222 controller_->GetUnattachedLocationsHeader()),
223 chrome_style::kTextFontStyle,
224 NSNaturalTextAlignment,
225 NSLineBreakByClipping
227 [unattached_label sizeToFit];
228 NSSize unattached_label_size = [unattached_label frame].size;
229 [unattached_label setFrame:NSMakeRect(
230 kCheckboxMargin, y_pos + kCheckboxMargin,
231 kCheckboxMaxWidth, unattached_label_size.height)];
232 [checkbox_container_ addSubview:unattached_label];
233 y_pos = NSMaxY([unattached_label frame]) + kCheckboxMargin;
238 void MediaGalleriesDialogCocoa::OnAcceptClicked() {
242 window_->CloseWebContentsModalDialog();
245 void MediaGalleriesDialogCocoa::OnCancelClicked() {
247 window_->CloseWebContentsModalDialog();
250 void MediaGalleriesDialogCocoa::OnAddFolderClicked() {
251 controller_->OnAddFolderClicked();
254 void MediaGalleriesDialogCocoa::OnCheckboxToggled(NSButton* checkbox) {
255 [[[alert_ buttons] objectAtIndex:0] setEnabled:YES];
257 const MediaGalleriesDialogController::GalleryPermissionsVector&
258 attached_permissions = controller_->AttachedPermissions();
259 for (MediaGalleriesDialogController::GalleryPermissionsVector::
260 const_reverse_iterator iter = attached_permissions.rbegin();
261 iter != attached_permissions.rend(); iter++) {
262 const MediaGalleryPrefInfo* gallery = &iter->pref_info;
263 NSString* unique_id = GetUniqueIDForGallery(*gallery);
264 if ([[[checkbox cell] representedObject] isEqual:unique_id]) {
265 controller_->DidToggleGalleryId(gallery->pref_id,
266 [checkbox state] == NSOnState);
271 const MediaGalleriesDialogController::GalleryPermissionsVector&
272 unattached_permissions = controller_->UnattachedPermissions();
273 for (MediaGalleriesDialogController::GalleryPermissionsVector::
274 const_reverse_iterator iter = unattached_permissions.rbegin();
275 iter != unattached_permissions.rend(); iter++) {
276 const MediaGalleryPrefInfo* gallery = &iter->pref_info;
277 NSString* unique_id = GetUniqueIDForGallery(*gallery);
278 if ([[[checkbox cell] representedObject] isEqual:unique_id]) {
279 controller_->DidToggleGalleryId(gallery->pref_id,
280 [checkbox state] == NSOnState);
287 void MediaGalleriesDialogCocoa::UpdateGalleryCheckbox(
288 const MediaGalleryPrefInfo& gallery,
291 base::scoped_nsobject<NSButton> checkbox(
292 [[NSButton alloc] initWithFrame:NSZeroRect]);
293 NSString* unique_id = GetUniqueIDForGallery(gallery);
294 [[checkbox cell] setRepresentedObject:unique_id];
295 [[checkbox cell] setLineBreakMode:NSLineBreakByTruncatingMiddle];
296 [checkbox setButtonType:NSSwitchButton];
297 [checkbox setTarget:cocoa_controller_];
298 [checkbox setAction:@selector(onCheckboxToggled:)];
299 [checkboxes_ addObject:checkbox];
301 // TODO(gbillock): Would be nice to add middle text elide behavior here.
302 [checkbox setTitle:base::SysUTF16ToNSString(
303 gallery.GetGalleryDisplayName())];
304 [checkbox setToolTip:base::SysUTF16ToNSString(gallery.GetGalleryTooltip())];
305 [checkbox setState:permitted ? NSOnState : NSOffState];
307 [checkbox sizeToFit];
308 NSRect rect = [checkbox bounds];
309 rect.origin.y = y_pos;
310 rect.origin.x = kCheckboxMargin;
311 rect.size.width = std::min(NSWidth(rect), kCheckboxMaxWidth);
312 [checkbox setFrame:rect];
313 [checkbox_container_ addSubview:checkbox];
315 base::scoped_nsobject<NSTextField> details(
316 [[NSTextField alloc] initWithFrame:NSZeroRect]);
317 [details setEditable:NO];
318 [details setSelectable:NO];
319 [details setBezeled:NO];
320 [details setAttributedStringValue:
321 constrained_window::GetAttributedLabelString(
322 base::SysUTF16ToNSString(gallery.GetGalleryAdditionalDetails()),
323 chrome_style::kTextFontStyle,
324 NSNaturalTextAlignment,
325 NSLineBreakByClipping
327 [details setTextColor:[NSColor colorWithCalibratedRed:0.625
332 NSRect details_rect = [details bounds];
333 details_rect.origin.y = y_pos - 1;
334 details_rect.origin.x = kCheckboxMargin + rect.size.width + kCheckboxMargin;
335 details_rect.size.width = kCheckboxMaxWidth - details_rect.origin.x;
336 [details setFrame:details_rect];
338 [checkbox_container_ addSubview:details];
341 void MediaGalleriesDialogCocoa::UpdateGalleries() {
342 InitDialogControls();
345 void MediaGalleriesDialogCocoa::OnConstrainedWindowClosed(
346 ConstrainedWindowMac* window) {
347 controller_->DialogFinished(accepted_);
351 MediaGalleriesDialog* MediaGalleriesDialog::Create(
352 MediaGalleriesDialogController* controller) {
353 base::scoped_nsobject<MediaGalleriesCocoaController> cocoa_controller(
354 [[MediaGalleriesCocoaController alloc] init]);
355 return new MediaGalleriesDialogCocoa(controller, cocoa_controller);