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