Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / cocoa / media_picker / desktop_media_picker_controller.mm
1 // Copyright 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 "chrome/browser/ui/cocoa/media_picker/desktop_media_picker_controller.h"
6
7 #include "base/bind.h"
8 #import "base/mac/bundle_locations.h"
9 #include "base/strings/sys_string_conversions.h"
10 #import "chrome/browser/ui/cocoa/media_picker/desktop_media_picker_item.h"
11 #include "content/public/browser/browser_thread.h"
12 #include "grit/generated_resources.h"
13 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMUILocalizerAndLayoutTweaker.h"
14 #import "ui/base/cocoa/flipped_view.h"
15 #import "ui/base/cocoa/window_size_constants.h"
16 #include "ui/base/l10n/l10n_util.h"
17 #include "ui/gfx/image/image_skia_util_mac.h"
18
19 namespace {
20
21 const int kInitialContentWidth = 620;
22 const int kMinimumContentWidth = 500;
23 const int kMinimumContentHeight = 390;
24 const int kThumbnailWidth = 150;
25 const int kThumbnailHeight = 150;
26 const int kFramePadding = 20;
27 const int kControlSpacing = 10;
28 const int kExcessButtonPadding = 6;
29
30 }  // namespace
31
32 @interface DesktopMediaPickerController (Private)
33
34 // Populate the window with controls and views.
35 - (void)initializeContentsWithAppName:(const base::string16&)appName;
36
37 // Create a |NSTextField| with label traits given |width|. Frame height is
38 // automatically adjusted to fit.
39 - (NSTextField*)createTextFieldWithText:(NSString*)text
40                              frameWidth:(CGFloat)width;
41
42 // Create a button with |title|, with size adjusted to fit.
43 - (NSButton*)createButtonWithTitle:(NSString*)title;
44
45 // Report result by invoking |doneCallback_|. The callback is invoked only on
46 // the first call to |reportResult:|. Subsequent calls will be no-ops.
47 - (void)reportResult:(content::DesktopMediaID)sourceID;
48
49 // Action handlers.
50 - (void)okPressed:(id)sender;
51 - (void)cancelPressed:(id)sender;
52
53 @end
54
55 @implementation DesktopMediaPickerController
56
57 - (id)initWithMediaList:(scoped_ptr<DesktopMediaList>)media_list
58                  parent:(NSWindow*)parent
59                callback:(const DesktopMediaPicker::DoneCallback&)callback
60                 appName:(const base::string16&)appName {
61   const NSUInteger kStyleMask =
62       NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask;
63   base::scoped_nsobject<NSWindow> window(
64       [[NSWindow alloc] initWithContentRect:ui::kWindowSizeDeterminedLater
65                                   styleMask:kStyleMask
66                                     backing:NSBackingStoreBuffered
67                                       defer:NO]);
68
69   if ((self = [super initWithWindow:window])) {
70     [parent addChildWindow:window ordered:NSWindowAbove];
71     [window setDelegate:self];
72     [self initializeContentsWithAppName:appName];
73     media_list_ = media_list.Pass();
74     media_list_->SetViewDialogWindowId([window windowNumber]);
75     doneCallback_ = callback;
76     items_.reset([[NSMutableArray alloc] init]);
77     bridge_.reset(new DesktopMediaPickerBridge(self));
78   }
79   return self;
80 }
81
82 - (void)dealloc {
83   [sourceBrowser_ setDelegate:nil];
84   [sourceBrowser_ setDataSource:nil];
85   [super dealloc];
86 }
87
88 - (void)initializeContentsWithAppName:(const base::string16&)appName {
89   // Use flipped coordinates to facilitate manual layout.
90   const CGFloat kPaddedWidth = kInitialContentWidth - (kFramePadding * 2);
91   base::scoped_nsobject<FlippedView> content(
92       [[FlippedView alloc] initWithFrame:NSZeroRect]);
93   [[self window] setContentView:content];
94   NSPoint origin = NSMakePoint(kFramePadding, kFramePadding);
95
96   // Set the dialog's title.
97   NSString* titleText = l10n_util::GetNSStringF(
98       IDS_DESKTOP_MEDIA_PICKER_TITLE, appName);
99   [[self window] setTitle:titleText];
100
101   // Set the dialog's description.
102   NSString* descriptionText = l10n_util::GetNSStringF(
103       IDS_DESKTOP_MEDIA_PICKER_TEXT, appName);
104   NSTextField* description = [self createTextFieldWithText:descriptionText
105                                                 frameWidth:kPaddedWidth];
106   [description setFrameOrigin:origin];
107   [content addSubview:description];
108   origin.y += NSHeight([description frame]) + kControlSpacing;
109
110   // Create the image browser.
111   sourceBrowser_.reset([[IKImageBrowserView alloc] initWithFrame:NSZeroRect]);
112   NSUInteger cellStyle = IKCellsStyleShadowed | IKCellsStyleTitled;
113   [sourceBrowser_ setDelegate:self];
114   [sourceBrowser_ setDataSource:self];
115   [sourceBrowser_ setCellsStyleMask:cellStyle];
116   [sourceBrowser_ setCellSize:NSMakeSize(kThumbnailWidth, kThumbnailHeight)];
117
118   // Create a scroll view to host the image browser.
119   NSRect imageBrowserScrollFrame = NSMakeRect(
120       origin.x, origin.y, kPaddedWidth, 350);
121   base::scoped_nsobject<NSScrollView> imageBrowserScroll(
122       [[NSScrollView alloc] initWithFrame:imageBrowserScrollFrame]);
123   [imageBrowserScroll setHasVerticalScroller:YES];
124   [imageBrowserScroll setDocumentView:sourceBrowser_];
125   [imageBrowserScroll setBorderType:NSBezelBorder];
126   [imageBrowserScroll setAutoresizingMask:
127       NSViewWidthSizable | NSViewHeightSizable];
128   [content addSubview:imageBrowserScroll];
129   origin.y += NSHeight(imageBrowserScrollFrame) + kControlSpacing;
130
131   // Create the cancel button.
132   cancelButton_ =
133       [self createButtonWithTitle:l10n_util::GetNSString(IDS_CANCEL)];
134   origin.x = kInitialContentWidth - kFramePadding -
135       (NSWidth([cancelButton_ frame]) - kExcessButtonPadding);
136   [cancelButton_ setFrameOrigin:origin];
137   [cancelButton_ setAutoresizingMask:NSViewMinXMargin | NSViewMinYMargin];
138   [cancelButton_ setTarget:self];
139   [cancelButton_ setAction:@selector(cancelPressed:)];
140   [content addSubview:cancelButton_];
141
142   // Create the OK button.
143   okButton_ = [self createButtonWithTitle:l10n_util::GetNSString(IDS_OK)];
144   origin.x -= kControlSpacing +
145       (NSWidth([okButton_ frame]) - (kExcessButtonPadding * 2));
146   [okButton_ setEnabled:NO];
147   [okButton_ setFrameOrigin:origin];
148   [okButton_ setAutoresizingMask:NSViewMinXMargin | NSViewMinYMargin];
149   [okButton_ setTarget:self];
150   [okButton_ setAction:@selector(okPressed:)];
151   [content addSubview:okButton_];
152   origin.y += kFramePadding +
153       (NSHeight([okButton_ frame]) - kExcessButtonPadding);
154
155   // Resize window to fit.
156   [[[self window] contentView] setAutoresizesSubviews:NO];
157   [[self window] setContentSize:NSMakeSize(kInitialContentWidth, origin.y)];
158   [[self window] setContentMinSize:
159       NSMakeSize(kMinimumContentWidth, kMinimumContentHeight)];
160   [[[self window] contentView] setAutoresizesSubviews:YES];
161 }
162
163 - (void)showWindow:(id)sender {
164   // Signal the media_list to start sending thumbnails. |bridge_| is used as the
165   // observer, and will forward notifications to this object.
166   media_list_->SetThumbnailSize(gfx::Size(kThumbnailWidth, kThumbnailHeight));
167   media_list_->StartUpdating(bridge_.get());
168
169   [self.window center];
170   [super showWindow:sender];
171 }
172
173 - (void)reportResult:(content::DesktopMediaID)sourceID {
174   if (doneCallback_.is_null()) {
175     return;
176   }
177
178   // Notify the |callback_| asynchronously because it may release the
179   // controller.
180   content::BrowserThread::PostTask(
181       content::BrowserThread::UI, FROM_HERE,
182       base::Bind(doneCallback_, sourceID));
183   doneCallback_.Reset();
184 }
185
186 - (void)okPressed:(id)sender {
187   NSIndexSet* indexes = [sourceBrowser_ selectionIndexes];
188   NSUInteger selectedIndex = [indexes firstIndex];
189   DesktopMediaPickerItem* item =
190       [items_ objectAtIndex:selectedIndex];
191   [self reportResult:[item sourceID]];
192   [self close];
193 }
194
195 - (void)cancelPressed:(id)sender {
196   [self reportResult:content::DesktopMediaID()];
197   [self close];
198 }
199
200 - (NSTextField*)createTextFieldWithText:(NSString*)text
201                              frameWidth:(CGFloat)width {
202   NSRect frame = NSMakeRect(0, 0, width, 1);
203   base::scoped_nsobject<NSTextField> textField(
204       [[NSTextField alloc] initWithFrame:frame]);
205   [textField setEditable:NO];
206   [textField setSelectable:YES];
207   [textField setDrawsBackground:NO];
208   [textField setBezeled:NO];
209   [textField setStringValue:text];
210   [textField setFont:[NSFont systemFontOfSize:13]];
211   [textField setAutoresizingMask:NSViewWidthSizable];
212   [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:textField];
213   return textField.autorelease();
214 }
215
216 - (NSButton*)createButtonWithTitle:(NSString*)title {
217   base::scoped_nsobject<NSButton> button(
218       [[NSButton alloc] initWithFrame:NSZeroRect]);
219   [button setButtonType:NSMomentaryPushInButton];
220   [button setBezelStyle:NSRoundedBezelStyle];
221   [button setTitle:title];
222   [GTMUILocalizerAndLayoutTweaker sizeToFitView:button];
223   return button.autorelease();
224 }
225
226 #pragma mark NSWindowDelegate
227
228 - (void)windowWillClose:(NSNotification*)notification {
229   // Report the result if it hasn't been reported yet. |reportResult:| ensures
230   // that the result is only reported once.
231   [self reportResult:content::DesktopMediaID()];
232
233   // Remove self from the parent.
234   NSWindow* window = [self window];
235   [[window parentWindow] removeChildWindow:window];
236 }
237
238 #pragma mark IKImageBrowserDataSource
239
240 - (NSUInteger)numberOfItemsInImageBrowser:(IKImageBrowserView*)browser {
241   return [items_ count];
242 }
243
244 - (id)imageBrowser:(IKImageBrowserView *)browser
245        itemAtIndex:(NSUInteger)index {
246   return [items_ objectAtIndex:index];
247 }
248
249 #pragma mark IKImageBrowserDelegate
250
251 - (void)imageBrowser:(IKImageBrowserView *)browser
252       cellWasDoubleClickedAtIndex:(NSUInteger)index {
253   DesktopMediaPickerItem* item = [items_ objectAtIndex:index];
254   [self reportResult:[item sourceID]];
255   [self close];
256 }
257
258 - (void)imageBrowserSelectionDidChange:(IKImageBrowserView*) aBrowser {
259   // Enable or disable the OK button based on whether we have a selection.
260   [okButton_ setEnabled:([[sourceBrowser_ selectionIndexes] count] > 0)];
261 }
262
263 #pragma mark DesktopMediaPickerObserver
264
265 - (void)sourceAddedAtIndex:(int)index {
266   const DesktopMediaList::Source& source = media_list_->GetSource(index);
267   NSString* imageTitle = base::SysUTF16ToNSString(source.name);
268   base::scoped_nsobject<DesktopMediaPickerItem> item(
269       [[DesktopMediaPickerItem alloc] initWithSourceId:source.id
270                                               imageUID:++lastImageUID_
271                                             imageTitle:imageTitle]);
272   [items_ insertObject:item atIndex:index];
273   [sourceBrowser_ reloadData];
274 }
275
276 - (void)sourceRemovedAtIndex:(int)index {
277   if ([[sourceBrowser_ selectionIndexes] containsIndex:index]) {
278     // Selected item was removed. Clear selection.
279     [sourceBrowser_ setSelectionIndexes:[NSIndexSet indexSet]
280                       byExtendingSelection:FALSE];
281   }
282   [items_ removeObjectAtIndex:index];
283   [sourceBrowser_ reloadData];
284 }
285
286 - (void)sourceMovedFrom:(int)oldIndex to:(int)newIndex {
287   base::scoped_nsobject<DesktopMediaPickerItem> item(
288       [[items_ objectAtIndex:oldIndex] retain]);
289   [items_ removeObjectAtIndex:oldIndex];
290   [items_ insertObject:item atIndex:newIndex];
291   [sourceBrowser_ reloadData];
292 }
293
294 - (void)sourceNameChangedAtIndex:(int)index {
295   DesktopMediaPickerItem* item = [items_ objectAtIndex:index];
296   const DesktopMediaList::Source& source = media_list_->GetSource(index);
297   [item setImageTitle:base::SysUTF16ToNSString(source.name)];
298   [sourceBrowser_ reloadData];
299 }
300
301 - (void)sourceThumbnailChangedAtIndex:(int)index {
302   const DesktopMediaList::Source& source = media_list_->GetSource(index);
303   NSImage* image = gfx::NSImageFromImageSkia(source.thumbnail);
304
305   DesktopMediaPickerItem* item = [items_ objectAtIndex:index];
306   [item setImageRepresentation:image];
307   [sourceBrowser_ reloadData];
308 }
309
310 @end  // @interface DesktopMediaPickerController