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