- add sources.
[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/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"
19
20 @interface MediaGalleriesCocoaController : NSObject {
21  @private
22   MediaGalleriesDialogCocoa* dialog_;
23 }
24
25 @property(nonatomic, assign) MediaGalleriesDialogCocoa* dialog;
26
27 @end
28
29 @implementation MediaGalleriesCocoaController
30
31 @synthesize dialog = dialog_;
32
33 - (void)onAcceptButton:(id)sender {
34   dialog_->OnAcceptClicked();
35 }
36
37 - (void)onCancelButton:(id)sender {
38   dialog_->OnCancelClicked();
39 }
40
41 - (void)onAddFolderClicked:(id)sender {
42   DCHECK(dialog_);
43   dialog_->OnAddFolderClicked();
44 }
45
46 - (void)onCheckboxToggled:(id)sender {
47   DCHECK(dialog_);
48   dialog_->OnCheckboxToggled(sender);
49 }
50
51 @end
52
53 namespace {
54
55 const CGFloat kCheckboxMargin = 10;
56 const CGFloat kCheckboxMaxWidth = 440;
57 const CGFloat kScrollAreaHeight = 220;
58
59 NSString* GetUniqueIDForGallery(const MediaGalleryPrefInfo& gallery) {
60   return base::SysUTF8ToNSString(gallery.device_id + gallery.path.value());
61 }
62
63 }  // namespace
64
65 MediaGalleriesDialogCocoa::MediaGalleriesDialogCocoa(
66     MediaGalleriesDialogController* controller,
67     MediaGalleriesCocoaController* cocoa_controller)
68     : controller_(controller),
69       accepted_(false),
70       cocoa_controller_([cocoa_controller retain]) {
71   [cocoa_controller_ setDialog:this];
72
73   alert_.reset([[ConstrainedWindowAlert alloc] init]);
74
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:)];
94
95   InitDialogControls();
96
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));
104   }
105 }
106
107 MediaGalleriesDialogCocoa::~MediaGalleriesDialogCocoa() {
108 }
109
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
118                                                        green:0.625
119                                                         blue:0.625
120                                                        alpha:1.0]];
121
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];
129
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_];
134
135   CGFloat y_pos = kCheckboxMargin;
136
137   y_pos = CreateAttachedCheckboxes(y_pos, controller_->AttachedPermissions());
138
139   if (!controller_->UnattachedPermissions().empty()) {
140     y_pos = CreateCheckboxSeparator(y_pos);
141
142     y_pos = CreateUnattachedCheckboxes(
143         y_pos, controller_->UnattachedPermissions());
144   }
145
146   [checkbox_container_ setFrame:NSMakeRect(0, 0, kCheckboxMaxWidth, y_pos + 2)];
147
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];
153   }
154
155   [accessory_ setFrame:NSMakeRect(
156       0, 0, kCheckboxMaxWidth, NSHeight(scroll_frame))];
157   [alert_ setAccessoryView:accessory_];
158
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()];
164
165   [alert_ layout];
166 }
167
168 CGFloat MediaGalleriesDialogCocoa::CreateAttachedCheckboxes(
169     CGFloat y_pos,
170     const MediaGalleriesDialogController::GalleryPermissionsVector&
171         permissions) {
172   y_pos += kCheckboxMargin;
173
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;
180   }
181
182   return y_pos;
183 }
184
185 // Add checkboxes for galleries that aren't available (i.e. removable
186 // volumes that are not currently attached).
187 CGFloat MediaGalleriesDialogCocoa::CreateUnattachedCheckboxes(
188     CGFloat y_pos,
189     const MediaGalleriesDialogController::GalleryPermissionsVector&
190         permissions) {
191   y_pos += kCheckboxMargin;
192
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;
199   }
200
201   return y_pos;
202 }
203
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;
213
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
226       )];
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;
234
235   return y_pos;
236 }
237
238 void MediaGalleriesDialogCocoa::OnAcceptClicked() {
239   accepted_ = true;
240
241   if (window_)
242     window_->CloseWebContentsModalDialog();
243 }
244
245 void MediaGalleriesDialogCocoa::OnCancelClicked() {
246   if (window_)
247     window_->CloseWebContentsModalDialog();
248 }
249
250 void MediaGalleriesDialogCocoa::OnAddFolderClicked() {
251   controller_->OnAddFolderClicked();
252 }
253
254 void MediaGalleriesDialogCocoa::OnCheckboxToggled(NSButton* checkbox) {
255   [[[alert_ buttons] objectAtIndex:0] setEnabled:YES];
256
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);
267       break;
268     }
269   }
270
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);
281       break;
282     }
283   }
284
285 }
286
287 void MediaGalleriesDialogCocoa::UpdateGalleryCheckbox(
288     const MediaGalleryPrefInfo& gallery,
289     bool permitted,
290     CGFloat y_pos) {
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];
300
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];
306
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];
314
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
326       )];
327   [details setTextColor:[NSColor colorWithCalibratedRed:0.625
328                                                   green:0.625
329                                                    blue:0.625
330                                                   alpha:1.0]];
331   [details sizeToFit];
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];
337
338   [checkbox_container_ addSubview:details];
339 }
340
341 void MediaGalleriesDialogCocoa::UpdateGalleries() {
342   InitDialogControls();
343 }
344
345 void MediaGalleriesDialogCocoa::OnConstrainedWindowClosed(
346     ConstrainedWindowMac* window) {
347   controller_->DialogFinished(accepted_);
348 }
349
350 // static
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);
356 }