Upstream version 7.35.139.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / cocoa / extensions / media_galleries_dialog_cocoa.mm
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.
4
5 #include "chrome/browser/ui/cocoa/extensions/media_galleries_dialog_cocoa.h"
6
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"
22
23 // Controller for UI events on items in the media galleries dialog.
24 @interface MediaGalleriesCocoaController : NSObject {
25  @private
26   MediaGalleriesDialogCocoa* dialog_;
27 }
28
29 @property(assign, nonatomic) MediaGalleriesDialogCocoa* dialog;
30
31 @end
32
33 @implementation MediaGalleriesCocoaController
34
35 @synthesize dialog = dialog_;
36
37 - (void)onAcceptButton:(id)sender {
38   dialog_->OnAcceptClicked();
39 }
40
41 - (void)onCancelButton:(id)sender {
42   dialog_->OnCancelClicked();
43 }
44
45 - (void)onAddFolderClicked:(id)sender {
46   DCHECK(dialog_);
47   dialog_->OnAddFolderClicked();
48 }
49
50 - (void)onCheckboxToggled:(id)sender {
51   DCHECK(dialog_);
52   dialog_->OnCheckboxToggled(sender);
53 }
54
55 @end
56
57
58 @interface MediaGalleriesCheckbox : NSButton {
59  @private
60   MediaGalleriesDialogCocoa* dialog_;  // |dialog_| owns |this|.
61   MediaGalleryPrefId prefId_;
62   base::scoped_nsobject<MenuController> menuController_;
63 }
64
65 - (id)initWithFrame:(NSRect)frameRect
66              dialog:(MediaGalleriesDialogCocoa*)dialog
67              prefId:(MediaGalleryPrefId)prefId;
68 - (NSMenu*)menuForEvent:(NSEvent*)theEvent;
69
70 @end
71
72 @implementation MediaGalleriesCheckbox
73
74 - (id)initWithFrame:(NSRect)frameRect
75              dialog:(MediaGalleriesDialogCocoa*)dialog
76              prefId:(MediaGalleryPrefId)prefId {
77   if ((self = [super initWithFrame:frameRect])) {
78     dialog_ = dialog;
79     prefId_ = prefId;
80   }
81   return self;
82 }
83
84 - (NSMenu*)menuForEvent:(NSEvent*)theEvent {
85   menuController_.reset(
86     [[MenuController alloc] initWithModel:dialog_->GetContextMenu(prefId_)
87                    useWithPopUpButtonCell:NO]);
88   return [menuController_ menu];
89 }
90
91 @end
92
93 namespace {
94
95 const CGFloat kCheckboxMargin = 10;
96 const CGFloat kCheckboxMaxWidth = 440;
97 const CGFloat kScrollAreaHeight = 220;
98
99 NSString* GetUniqueIDForGallery(const MediaGalleryPrefInfo& gallery) {
100   return base::SysUTF8ToNSString(gallery.device_id + gallery.path.value());
101 }
102
103 }  // namespace
104
105 MediaGalleriesDialogCocoa::MediaGalleriesDialogCocoa(
106     MediaGalleriesDialogController* controller,
107     MediaGalleriesCocoaController* cocoa_controller)
108     : controller_(controller),
109       accepted_(false),
110       cocoa_controller_([cocoa_controller retain]) {
111   [cocoa_controller_ setDialog:this];
112
113   alert_.reset([[ConstrainedWindowAlert alloc] init]);
114
115   [alert_ setMessageText:base::SysUTF16ToNSString(controller_->GetHeader())];
116   [alert_ setInformativeText:
117       base::SysUTF16ToNSString(controller_->GetSubtext())];
118   [alert_ addButtonWithTitle:
119     l10n_util::GetNSString(IDS_MEDIA_GALLERIES_DIALOG_CONFIRM)
120                keyEquivalent:kKeyEquivalentReturn
121                       target:cocoa_controller_
122                       action:@selector(onAcceptButton:)];
123   [alert_ addButtonWithTitle:
124       l10n_util::GetNSString(IDS_MEDIA_GALLERIES_DIALOG_CANCEL)
125                keyEquivalent:kKeyEquivalentEscape
126                       target:cocoa_controller_
127                       action:@selector(onCancelButton:)];
128   [alert_ addButtonWithTitle:
129       l10n_util::GetNSString(IDS_MEDIA_GALLERIES_DIALOG_ADD_GALLERY)
130                keyEquivalent:kKeyEquivalentNone
131                       target:cocoa_controller_
132                       action:@selector(onAddFolderClicked:)];
133   [[alert_ closeButton] setTarget:cocoa_controller_];
134   [[alert_ closeButton] setAction:@selector(onCancelButton:)];
135
136   InitDialogControls();
137
138   // May be NULL during tests.
139   if (controller->web_contents()) {
140     base::scoped_nsobject<CustomConstrainedWindowSheet> sheet(
141         [[CustomConstrainedWindowSheet alloc]
142             initWithCustomWindow:[alert_ window]]);
143     window_.reset(new ConstrainedWindowMac(
144         this, controller->web_contents(), sheet));
145   }
146 }
147
148 MediaGalleriesDialogCocoa::~MediaGalleriesDialogCocoa() {
149 }
150
151 void MediaGalleriesDialogCocoa::InitDialogControls() {
152   main_container_.reset([[NSBox alloc] init]);
153   [main_container_ setBoxType:NSBoxCustom];
154   [main_container_ setBorderType:NSLineBorder];
155   [main_container_ setBorderWidth:1];
156   [main_container_ setCornerRadius:0];
157   [main_container_ setContentViewMargins:NSZeroSize];
158   [main_container_ setTitlePosition:NSNoTitle];
159   [main_container_ setBorderColor:[NSColor colorWithCalibratedRed:0.625
160                                                             green:0.625
161                                                              blue:0.625
162                                                             alpha:1.0]];
163
164   base::scoped_nsobject<NSScrollView> scroll_view(
165       [[NSScrollView alloc] initWithFrame:
166           NSMakeRect(0, 0, kCheckboxMaxWidth, kScrollAreaHeight)]);
167   [scroll_view setHasVerticalScroller:YES];
168   [scroll_view setHasHorizontalScroller:NO];
169   [scroll_view setBorderType:NSNoBorder];
170   [scroll_view setAutohidesScrollers:YES];
171   [[main_container_ contentView] addSubview:scroll_view];
172
173   // Add gallery permission checkboxes inside the scrolling view.
174   checkbox_container_.reset([[FlippedView alloc] initWithFrame:NSZeroRect]);
175   checkboxes_.reset([[NSMutableArray alloc] init]);
176   [scroll_view setDocumentView:checkbox_container_];
177
178   CGFloat y_pos = 0;
179
180   y_pos = CreateAttachedCheckboxes(y_pos, controller_->AttachedPermissions());
181
182   if (!controller_->UnattachedPermissions().empty()) {
183     y_pos = CreateCheckboxSeparator(y_pos);
184
185     y_pos = CreateUnattachedCheckboxes(
186         y_pos, controller_->UnattachedPermissions());
187   }
188
189   [checkbox_container_ setFrame:NSMakeRect(0, 0, kCheckboxMaxWidth, y_pos + 2)];
190
191   // Resize to pack the scroll view if possible.
192   NSRect scroll_frame = [scroll_view frame];
193   if (NSHeight(scroll_frame) > NSHeight([checkbox_container_ frame])) {
194     scroll_frame.size.height = NSHeight([checkbox_container_ frame]);
195     [scroll_view setFrame:scroll_frame];
196   }
197
198   [main_container_ setFrameFromContentFrame:scroll_frame];
199   [alert_ setAccessoryView:main_container_];
200
201   // As a safeguard against the user skipping reading over the dialog and just
202   // confirming, the button will be unavailable for dialogs without any checks
203   // until the user toggles something.
204   [[[alert_ buttons] objectAtIndex:0] setEnabled:
205       controller_->HasPermittedGalleries()];
206
207   [alert_ layout];
208 }
209
210 CGFloat MediaGalleriesDialogCocoa::CreateAttachedCheckboxes(
211     CGFloat y_pos,
212     const MediaGalleriesDialogController::GalleryPermissionsVector&
213         permissions) {
214   y_pos += kCheckboxMargin;
215
216   for (MediaGalleriesDialogController::GalleryPermissionsVector::
217        const_iterator iter = permissions.begin();
218        iter != permissions.end(); iter++) {
219     const MediaGalleriesDialogController::GalleryPermission& permission = *iter;
220     UpdateGalleryCheckbox(permission.pref_info, permission.allowed, y_pos);
221     y_pos = NSMaxY([[checkboxes_ lastObject] frame]) + kCheckboxMargin;
222   }
223
224   return y_pos;
225 }
226
227 // Add checkboxes for galleries that aren't available (i.e. removable
228 // volumes that are not currently attached).
229 CGFloat MediaGalleriesDialogCocoa::CreateUnattachedCheckboxes(
230     CGFloat y_pos,
231     const MediaGalleriesDialogController::GalleryPermissionsVector&
232         permissions) {
233   y_pos += kCheckboxMargin;
234
235   for (MediaGalleriesDialogController::GalleryPermissionsVector::
236        const_iterator iter = permissions.begin();
237        iter != permissions.end(); iter++) {
238     const MediaGalleriesDialogController::GalleryPermission& permission = *iter;
239     UpdateGalleryCheckbox(permission.pref_info, permission.allowed, y_pos);
240     y_pos = NSMaxY([[checkboxes_ lastObject] frame]) + kCheckboxMargin;
241   }
242
243   return y_pos;
244 }
245
246 CGFloat MediaGalleriesDialogCocoa::CreateCheckboxSeparator(CGFloat y_pos) {
247   base::scoped_nsobject<NSBox> separator(
248       [[NSBox alloc] initWithFrame:NSMakeRect(
249           0, y_pos + kCheckboxMargin * 0.5, kCheckboxMaxWidth, 1.0)]);
250   [separator setBoxType:NSBoxSeparator];
251   [separator setBorderType:NSLineBorder];
252   [separator setAlphaValue:0.2];
253   [checkbox_container_ addSubview:separator];
254   y_pos += kCheckboxMargin * 0.5 + 4;
255
256   base::scoped_nsobject<NSTextField> unattached_label(
257       [[NSTextField alloc] initWithFrame:NSZeroRect]);
258   [unattached_label setEditable:NO];
259   [unattached_label setSelectable:NO];
260   [unattached_label setBezeled:NO];
261   [unattached_label setAttributedStringValue:
262       constrained_window::GetAttributedLabelString(
263           base::SysUTF16ToNSString(
264               controller_->GetUnattachedLocationsHeader()),
265           chrome_style::kTextFontStyle,
266           NSNaturalTextAlignment,
267           NSLineBreakByClipping
268       )];
269   [unattached_label sizeToFit];
270   NSSize unattached_label_size = [unattached_label frame].size;
271   [unattached_label setFrame:NSMakeRect(
272       kCheckboxMargin, y_pos + kCheckboxMargin,
273       kCheckboxMaxWidth, unattached_label_size.height)];
274   [checkbox_container_ addSubview:unattached_label];
275   y_pos = NSMaxY([unattached_label frame]) + kCheckboxMargin;
276
277   return y_pos;
278 }
279
280 void MediaGalleriesDialogCocoa::OnAcceptClicked() {
281   accepted_ = true;
282
283   if (window_)
284     window_->CloseWebContentsModalDialog();
285 }
286
287 void MediaGalleriesDialogCocoa::OnCancelClicked() {
288   if (window_)
289     window_->CloseWebContentsModalDialog();
290 }
291
292 void MediaGalleriesDialogCocoa::OnAddFolderClicked() {
293   controller_->OnAddFolderClicked();
294 }
295
296 void MediaGalleriesDialogCocoa::OnCheckboxToggled(NSButton* checkbox) {
297   [[[alert_ buttons] objectAtIndex:0] setEnabled:YES];
298
299   const MediaGalleriesDialogController::GalleryPermissionsVector&
300       attached_permissions = controller_->AttachedPermissions();
301   for (MediaGalleriesDialogController::GalleryPermissionsVector::
302        const_reverse_iterator iter = attached_permissions.rbegin();
303        iter != attached_permissions.rend(); iter++) {
304     const MediaGalleryPrefInfo* gallery = &iter->pref_info;
305     NSString* unique_id = GetUniqueIDForGallery(*gallery);
306     if ([[[checkbox cell] representedObject] isEqual:unique_id]) {
307       controller_->DidToggleGalleryId(gallery->pref_id,
308                                       [checkbox state] == NSOnState);
309       break;
310     }
311   }
312
313   const MediaGalleriesDialogController::GalleryPermissionsVector&
314       unattached_permissions = controller_->UnattachedPermissions();
315   for (MediaGalleriesDialogController::GalleryPermissionsVector::
316        const_reverse_iterator iter = unattached_permissions.rbegin();
317        iter != unattached_permissions.rend(); iter++) {
318     const MediaGalleryPrefInfo* gallery = &iter->pref_info;
319     NSString* unique_id = GetUniqueIDForGallery(*gallery);
320     if ([[[checkbox cell] representedObject] isEqual:unique_id]) {
321       controller_->DidToggleGalleryId(gallery->pref_id,
322                                       [checkbox state] == NSOnState);
323       break;
324     }
325   }
326
327 }
328
329 void MediaGalleriesDialogCocoa::UpdateGalleryCheckbox(
330     const MediaGalleryPrefInfo& gallery,
331     bool permitted,
332     CGFloat y_pos) {
333   // Checkbox.
334   base::scoped_nsobject<MediaGalleriesCheckbox> checkbox(
335       [[MediaGalleriesCheckbox alloc] initWithFrame:NSZeroRect
336                                              dialog:this
337                                              prefId:gallery.pref_id]);
338   NSString* unique_id = GetUniqueIDForGallery(gallery);
339   [[checkbox cell] setRepresentedObject:unique_id];
340   [[checkbox cell] setLineBreakMode:NSLineBreakByTruncatingMiddle];
341   [checkbox setButtonType:NSSwitchButton];
342   [checkbox setTarget:cocoa_controller_];
343   [checkbox setAction:@selector(onCheckboxToggled:)];
344   [checkboxes_ addObject:checkbox];
345
346   [checkbox setTitle:base::SysUTF16ToNSString(
347       gallery.GetGalleryDisplayName())];
348   [checkbox setToolTip:base::SysUTF16ToNSString(gallery.GetGalleryTooltip())];
349   [checkbox setState:permitted ? NSOnState : NSOffState];
350
351   [checkbox sizeToFit];
352   NSRect rect = [checkbox bounds];
353
354   // Detail text.
355   base::scoped_nsobject<NSTextField> details(
356       [[NSTextField alloc] initWithFrame:NSZeroRect]);
357   [details setEditable:NO];
358   [details setSelectable:NO];
359   [details setBezeled:NO];
360   [details setAttributedStringValue:
361       constrained_window::GetAttributedLabelString(
362           base::SysUTF16ToNSString(gallery.GetGalleryAdditionalDetails()),
363           chrome_style::kTextFontStyle,
364           NSNaturalTextAlignment,
365           NSLineBreakByClipping
366       )];
367   [details setTextColor:[NSColor colorWithCalibratedRed:0.625
368                                                   green:0.625
369                                                    blue:0.625
370                                                   alpha:1.0]];
371   [details sizeToFit];
372   NSRect details_rect = [details bounds];
373
374   // The checkbox will elide so reduce its size so it will all fit.
375   rect.size.width =
376       std::min(NSWidth(rect),
377                kCheckboxMaxWidth - 3 * kCheckboxMargin - NSWidth(details_rect));
378   rect.origin = NSMakePoint(kCheckboxMargin, y_pos);
379   [checkbox setFrame:rect];
380   [checkbox_container_ addSubview:checkbox];
381
382   details_rect.origin = NSMakePoint(NSMaxX(rect) + kCheckboxMargin, y_pos - 1);
383   [details setFrame:details_rect];
384
385   [checkbox_container_ addSubview:details];
386 }
387
388 void MediaGalleriesDialogCocoa::UpdateGalleries() {
389   InitDialogControls();
390 }
391
392 void MediaGalleriesDialogCocoa::OnConstrainedWindowClosed(
393     ConstrainedWindowMac* window) {
394   controller_->DialogFinished(accepted_);
395 }
396
397 ui::MenuModel* MediaGalleriesDialogCocoa::GetContextMenu(
398     MediaGalleryPrefId prefid) {
399   return controller_->GetContextMenu(prefid);
400 }
401
402 // static
403 MediaGalleriesDialog* MediaGalleriesDialog::Create(
404     MediaGalleriesDialogController* controller) {
405   base::scoped_nsobject<MediaGalleriesCocoaController> cocoa_controller(
406       [[MediaGalleriesCocoaController alloc] init]);
407   return new MediaGalleriesDialogCocoa(controller, cocoa_controller);
408 }