- add sources.
[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/GTM/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 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)initWithModel:(scoped_ptr<DesktopMediaPickerModel>)model
58            callback:(const DesktopMediaPicker::DoneCallback&)callback
59             appName:(const string16&)appName {
60   const NSUInteger kStyleMask =
61       NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask;
62   base::scoped_nsobject<NSWindow> window(
63       [[NSWindow alloc] initWithContentRect:ui::kWindowSizeDeterminedLater
64                                   styleMask:kStyleMask
65                                     backing:NSBackingStoreBuffered
66                                       defer:NO]);
67
68   if ((self = [super initWithWindow:window])) {
69     [window setDelegate:self];
70     [self initializeContentsWithAppName:appName];
71     model_ = model.Pass();
72     model_->SetViewDialogWindowId([window windowNumber]);
73     doneCallback_ = callback;
74     items_.reset([[NSMutableArray alloc] init]);
75     bridge_.reset(new DesktopMediaPickerBridge(self));
76   }
77   return self;
78 }
79
80 - (void)dealloc {
81   [sourceBrowser_ setDelegate:nil];
82   [sourceBrowser_ setDataSource:nil];
83   [super dealloc];
84 }
85
86 - (void)initializeContentsWithAppName:(const string16&)appName {
87   // Use flipped coordinates to facilitate manual layout.
88   const CGFloat kPaddedWidth = kInitialContentWidth - (kFramePadding * 2);
89   base::scoped_nsobject<FlippedView> content(
90       [[FlippedView alloc] initWithFrame:NSZeroRect]);
91   [[self window] setContentView:content];
92   NSPoint origin = NSMakePoint(kFramePadding, kFramePadding);
93
94   // Set the dialog's title.
95   NSString* titleText = l10n_util::GetNSStringF(
96       IDS_DESKTOP_MEDIA_PICKER_TITLE, appName);
97   [[self window] setTitle:titleText];
98
99   // Set the dialog's description.
100   NSString* descriptionText = l10n_util::GetNSStringF(
101       IDS_DESKTOP_MEDIA_PICKER_TEXT, appName);
102   NSTextField* description = [self createTextFieldWithText:descriptionText
103                                                 frameWidth:kPaddedWidth];
104   [description setFrameOrigin:origin];
105   [content addSubview:description];
106   origin.y += NSHeight([description frame]) + kControlSpacing;
107
108   // Create the image browser.
109   sourceBrowser_.reset([[IKImageBrowserView alloc] initWithFrame:NSZeroRect]);
110   NSUInteger cellStyle = IKCellsStyleShadowed | IKCellsStyleTitled;
111   [sourceBrowser_ setDelegate:self];
112   [sourceBrowser_ setDataSource:self];
113   [sourceBrowser_ setCellsStyleMask:cellStyle];
114   [sourceBrowser_ setCellSize:NSMakeSize(kThumbnailWidth, kThumbnailHeight)];
115
116   // Create a scroll view to host the image browser.
117   NSRect imageBrowserScrollFrame = NSMakeRect(
118       origin.x, origin.y, kPaddedWidth, 350);
119   base::scoped_nsobject<NSScrollView> imageBrowserScroll(
120       [[NSScrollView alloc] initWithFrame:imageBrowserScrollFrame]);
121   [imageBrowserScroll setHasVerticalScroller:YES];
122   [imageBrowserScroll setDocumentView:sourceBrowser_];
123   [imageBrowserScroll setBorderType:NSBezelBorder];
124   [imageBrowserScroll setAutoresizingMask:
125       NSViewWidthSizable | NSViewHeightSizable];
126   [content addSubview:imageBrowserScroll];
127   origin.y += NSHeight(imageBrowserScrollFrame) + kControlSpacing;
128
129   // Create the cancel button.
130   cancelButton_ =
131       [self createButtonWithTitle:l10n_util::GetNSString(IDS_CANCEL)];
132   origin.x = kInitialContentWidth - kFramePadding -
133       (NSWidth([cancelButton_ frame]) - kExcessButtonPadding);
134   [cancelButton_ setFrameOrigin:origin];
135   [cancelButton_ setAutoresizingMask:NSViewMinXMargin | NSViewMinYMargin];
136   [cancelButton_ setTarget:self];
137   [cancelButton_ setAction:@selector(cancelPressed:)];
138   [content addSubview:cancelButton_];
139
140   // Create the OK button.
141   okButton_ = [self createButtonWithTitle:l10n_util::GetNSString(IDS_OK)];
142   origin.x -= kControlSpacing +
143       (NSWidth([okButton_ frame]) - (kExcessButtonPadding * 2));
144   [okButton_ setEnabled:NO];
145   [okButton_ setFrameOrigin:origin];
146   [okButton_ setAutoresizingMask:NSViewMinXMargin | NSViewMinYMargin];
147   [okButton_ setTarget:self];
148   [okButton_ setAction:@selector(okPressed:)];
149   [content addSubview:okButton_];
150   origin.y += kFramePadding +
151       (NSHeight([okButton_ frame]) - kExcessButtonPadding);
152
153   // Resize window to fit.
154   [[[self window] contentView] setAutoresizesSubviews:NO];
155   [[self window] setContentSize:NSMakeSize(kInitialContentWidth, origin.y)];
156   [[self window] setContentMinSize:
157       NSMakeSize(kMinimumContentWidth, kMinimumContentHeight)];
158   [[[self window] contentView] setAutoresizesSubviews:YES];
159 }
160
161 - (void)showWindow:(id)sender {
162   // Signal the model to start sending thumbnails. |bridge_| is used as the
163   // observer, and will forward notifications to this object.
164   model_->SetThumbnailSize(gfx::Size(kThumbnailWidth, kThumbnailHeight));
165   model_->StartUpdating(bridge_.get());
166
167   [self.window center];
168   [super showWindow:sender];
169 }
170
171 - (void)reportResult:(content::DesktopMediaID)sourceID {
172   if (doneCallback_.is_null()) {
173     return;
174   }
175
176   // Notify the |callback_| asynchronously because it may release the
177   // controller.
178   content::BrowserThread::PostTask(
179       content::BrowserThread::UI, FROM_HERE,
180       base::Bind(doneCallback_, sourceID));
181   doneCallback_.Reset();
182 }
183
184 - (void)okPressed:(id)sender {
185   NSIndexSet* indexes = [sourceBrowser_ selectionIndexes];
186   NSUInteger selectedIndex = [indexes firstIndex];
187   DesktopMediaPickerItem* item =
188       [items_ objectAtIndex:selectedIndex];
189   [self reportResult:[item sourceID]];
190   [self close];
191 }
192
193 - (void)cancelPressed:(id)sender {
194   [self reportResult:content::DesktopMediaID()];
195   [self close];
196 }
197
198 - (NSTextField*)createTextFieldWithText:(NSString*)text
199                              frameWidth:(CGFloat)width {
200   NSRect frame = NSMakeRect(0, 0, width, 1);
201   base::scoped_nsobject<NSTextField> textField(
202       [[NSTextField alloc] initWithFrame:frame]);
203   [textField setEditable:NO];
204   [textField setSelectable:YES];
205   [textField setDrawsBackground:NO];
206   [textField setBezeled:NO];
207   [textField setStringValue:text];
208   [textField setFont:[NSFont systemFontOfSize:13]];
209   [textField setAutoresizingMask:NSViewWidthSizable];
210   [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:textField];
211   return textField.autorelease();
212 }
213
214 - (NSButton*)createButtonWithTitle:(NSString*)title {
215   base::scoped_nsobject<NSButton> button(
216       [[NSButton alloc] initWithFrame:NSZeroRect]);
217   [button setButtonType:NSMomentaryPushInButton];
218   [button setBezelStyle:NSRoundedBezelStyle];
219   [button setTitle:title];
220   [GTMUILocalizerAndLayoutTweaker sizeToFitView:button];
221   return button.autorelease();
222 }
223
224 #pragma mark NSWindowDelegate
225
226 - (void)windowWillClose:(NSNotification*)notification {
227   // Report the result if it hasn't been reported yet. |reportResult:| ensures
228   // that the result is only reported once.
229   [self reportResult:content::DesktopMediaID()];
230 }
231
232 #pragma mark IKImageBrowserDataSource
233
234 - (NSUInteger)numberOfItemsInImageBrowser:(IKImageBrowserView*)browser {
235   return [items_ count];
236 }
237
238 - (id)imageBrowser:(IKImageBrowserView *)browser
239        itemAtIndex:(NSUInteger)index {
240   return [items_ objectAtIndex:index];
241 }
242
243 #pragma mark IKImageBrowserDelegate
244
245 - (void)imageBrowser:(IKImageBrowserView *)browser
246       cellWasDoubleClickedAtIndex:(NSUInteger)index {
247   DesktopMediaPickerItem* item = [items_ objectAtIndex:index];
248   [self reportResult:[item sourceID]];
249   [self close];
250 }
251
252 - (void)imageBrowserSelectionDidChange:(IKImageBrowserView*) aBrowser {
253   // Enable or disable the OK button based on whether we have a selection.
254   [okButton_ setEnabled:([[sourceBrowser_ selectionIndexes] count] > 0)];
255 }
256
257 #pragma mark DesktopMediaPickerObserver
258
259 - (void)sourceAddedAtIndex:(int)index {
260   const DesktopMediaPickerModel::Source& source = model_->source(index);
261   NSString* imageTitle = base::SysUTF16ToNSString(source.name);
262   base::scoped_nsobject<DesktopMediaPickerItem> item(
263       [[DesktopMediaPickerItem alloc] initWithSourceId:source.id
264                                               imageUID:++lastImageUID_
265                                             imageTitle:imageTitle]);
266   [items_ insertObject:item atIndex:index];
267   [sourceBrowser_ reloadData];
268 }
269
270 - (void)sourceRemovedAtIndex:(int)index {
271   if ([[sourceBrowser_ selectionIndexes] containsIndex:index]) {
272     // Selected item was removed. Clear selection.
273     [sourceBrowser_ setSelectionIndexes:[NSIndexSet indexSet]
274                       byExtendingSelection:FALSE];
275   }
276   [items_ removeObjectAtIndex:index];
277   [sourceBrowser_ reloadData];
278 }
279
280 - (void)sourceNameChangedAtIndex:(int)index {
281   DesktopMediaPickerItem* item = [items_ objectAtIndex:index];
282   const DesktopMediaPickerModel::Source& source = model_->source(index);
283   [item setImageTitle:base::SysUTF16ToNSString(source.name)];
284   [sourceBrowser_ reloadData];
285 }
286
287 - (void)sourceThumbnailChangedAtIndex:(int)index {
288   const DesktopMediaPickerModel::Source& source = model_->source(index);
289   NSImage* image = gfx::NSImageFromImageSkia(source.thumbnail);
290
291   DesktopMediaPickerItem* item = [items_ objectAtIndex:index];
292   [item setImageRepresentation:image];
293   [sourceBrowser_ reloadData];
294 }
295
296 @end  // @interface DesktopMediaPickerController