| OLD | NEW |
| (Empty) |
| 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_de
precated.h" | |
| 6 | |
| 7 #include <utility> | |
| 8 | |
| 9 #include "base/bind.h" | |
| 10 #include "base/command_line.h" | |
| 11 #import "base/mac/bundle_locations.h" | |
| 12 #include "base/strings/sys_string_conversions.h" | |
| 13 #include "chrome/browser/media/combined_desktop_media_list.h" | |
| 14 #import "chrome/browser/ui/cocoa/key_equivalent_constants.h" | |
| 15 #import "chrome/browser/ui/cocoa/media_picker/desktop_media_picker_item.h" | |
| 16 #include "chrome/common/chrome_switches.h" | |
| 17 #include "chrome/grit/generated_resources.h" | |
| 18 #include "components/strings/grit/components_strings.h" | |
| 19 #include "content/public/browser/browser_thread.h" | |
| 20 #include "content/public/browser/render_frame_host.h" | |
| 21 #include "content/public/browser/web_contents.h" | |
| 22 #include "content/public/browser/web_contents_delegate.h" | |
| 23 #import "third_party/google_toolbox_for_mac/src/AppKit/GTMUILocalizerAndLayoutTw
eaker.h" | |
| 24 #import "ui/base/cocoa/flipped_view.h" | |
| 25 #import "ui/base/cocoa/window_size_constants.h" | |
| 26 #include "ui/base/l10n/l10n_util.h" | |
| 27 #include "ui/gfx/image/image_skia_util_mac.h" | |
| 28 | |
| 29 namespace { | |
| 30 | |
| 31 const int kInitialContentWidth = 620; | |
| 32 const int kMinimumContentWidth = 500; | |
| 33 const int kMinimumContentHeight = 390; | |
| 34 const int kThumbnailWidth = 150; | |
| 35 const int kThumbnailHeight = 150; | |
| 36 const int kFramePadding = 20; | |
| 37 const int kControlSpacing = 10; | |
| 38 const int kExcessButtonPadding = 6; | |
| 39 | |
| 40 } // namespace | |
| 41 | |
| 42 @interface DesktopMediaPickerControllerDeprecated (Private) | |
| 43 | |
| 44 // Populate the window with controls and views. | |
| 45 - (void)initializeContentsWithAppName:(const base::string16&)appName; | |
| 46 | |
| 47 // Create a |NSTextField| with label traits given |width|. Frame height is | |
| 48 // automatically adjusted to fit. | |
| 49 - (NSTextField*)createTextFieldWithText:(NSString*)text | |
| 50 frameWidth:(CGFloat)width; | |
| 51 | |
| 52 // Create a button with |title|, with size adjusted to fit. | |
| 53 - (NSButton*)createButtonWithTitle:(NSString*)title; | |
| 54 | |
| 55 // Report result by invoking |doneCallback_|. The callback is invoked only on | |
| 56 // the first call to |reportResult:|. Subsequent calls will be no-ops. | |
| 57 - (void)reportResult:(content::DesktopMediaID)sourceID; | |
| 58 | |
| 59 // Action handlers. | |
| 60 - (void)sharePressed:(id)sender; | |
| 61 - (void)cancelPressed:(id)sender; | |
| 62 | |
| 63 @end | |
| 64 | |
| 65 @implementation DesktopMediaPickerControllerDeprecated | |
| 66 | |
| 67 - (id)initWithScreenList:(std::unique_ptr<DesktopMediaList>)screen_list | |
| 68 windowList:(std::unique_ptr<DesktopMediaList>)window_list | |
| 69 tabList:(std::unique_ptr<DesktopMediaList>)tab_list | |
| 70 parent:(NSWindow*)parent | |
| 71 callback:(const DesktopMediaPicker::DoneCallback&)callback | |
| 72 appName:(const base::string16&)appName | |
| 73 targetName:(const base::string16&)targetName | |
| 74 requestAudio:(bool)requestAudio { | |
| 75 const NSUInteger kStyleMask = | |
| 76 NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask; | |
| 77 base::scoped_nsobject<NSWindow> window([[NSWindow alloc] | |
| 78 initWithContentRect:ui::kWindowSizeDeterminedLater | |
| 79 styleMask:kStyleMask | |
| 80 backing:NSBackingStoreBuffered | |
| 81 defer:NO]); | |
| 82 | |
| 83 if ((self = [super initWithWindow:window])) { | |
| 84 [parent addChildWindow:window ordered:NSWindowAbove]; | |
| 85 [window setDelegate:self]; | |
| 86 [self initializeContentsWithAppName:appName | |
| 87 targetName:targetName | |
| 88 requestAudio:requestAudio]; | |
| 89 std::vector<std::unique_ptr<DesktopMediaList>> media_lists; | |
| 90 if (screen_list) | |
| 91 media_lists.push_back(std::move(screen_list)); | |
| 92 | |
| 93 if (window_list) | |
| 94 media_lists.push_back(std::move(window_list)); | |
| 95 | |
| 96 if (tab_list) | |
| 97 media_lists.push_back(std::move(tab_list)); | |
| 98 | |
| 99 if (media_lists.size() > 1) | |
| 100 media_list_.reset(new CombinedDesktopMediaList(media_lists)); | |
| 101 else | |
| 102 media_list_ = std::move(media_lists[0]); | |
| 103 media_list_->SetViewDialogWindowId(content::DesktopMediaID( | |
| 104 content::DesktopMediaID::TYPE_WINDOW, [window windowNumber])); | |
| 105 doneCallback_ = callback; | |
| 106 items_.reset([[NSMutableArray alloc] init]); | |
| 107 bridge_.reset(new DesktopMediaPickerBridge(self)); | |
| 108 } | |
| 109 return self; | |
| 110 } | |
| 111 | |
| 112 - (void)dealloc { | |
| 113 [shareButton_ setTarget:nil]; | |
| 114 [cancelButton_ setTarget:nil]; | |
| 115 [sourceBrowser_ setDelegate:nil]; | |
| 116 [sourceBrowser_ setDataSource:nil]; | |
| 117 [[self window] close]; | |
| 118 [super dealloc]; | |
| 119 } | |
| 120 | |
| 121 - (void)initializeContentsWithAppName:(const base::string16&)appName | |
| 122 targetName:(const base::string16&)targetName | |
| 123 requestAudio:(bool)requestAudio { | |
| 124 // Use flipped coordinates to facilitate manual layout. | |
| 125 const CGFloat kPaddedWidth = kInitialContentWidth - (kFramePadding * 2); | |
| 126 base::scoped_nsobject<FlippedView> content( | |
| 127 [[FlippedView alloc] initWithFrame:NSZeroRect]); | |
| 128 [[self window] setContentView:content]; | |
| 129 NSPoint origin = NSMakePoint(kFramePadding, kFramePadding); | |
| 130 | |
| 131 // Set the dialog's title. | |
| 132 NSString* titleText = l10n_util::GetNSStringF( | |
| 133 IDS_DESKTOP_MEDIA_PICKER_TITLE_DEPRECATED, appName); | |
| 134 [[self window] setTitle:titleText]; | |
| 135 | |
| 136 // Set the dialog's description. | |
| 137 NSString* descriptionText; | |
| 138 if (appName == targetName) { | |
| 139 descriptionText = | |
| 140 l10n_util::GetNSStringF(IDS_DESKTOP_MEDIA_PICKER_TEXT, appName); | |
| 141 } else { | |
| 142 descriptionText = l10n_util::GetNSStringF( | |
| 143 IDS_DESKTOP_MEDIA_PICKER_TEXT_DELEGATED, appName, targetName); | |
| 144 } | |
| 145 NSTextField* description = | |
| 146 [self createTextFieldWithText:descriptionText frameWidth:kPaddedWidth]; | |
| 147 [description setFrameOrigin:origin]; | |
| 148 [content addSubview:description]; | |
| 149 origin.y += NSHeight([description frame]) + kControlSpacing; | |
| 150 | |
| 151 // Create the image browser. | |
| 152 sourceBrowser_.reset([[IKImageBrowserView alloc] initWithFrame:NSZeroRect]); | |
| 153 NSUInteger cellStyle = IKCellsStyleShadowed | IKCellsStyleTitled; | |
| 154 [sourceBrowser_ setDelegate:self]; | |
| 155 [sourceBrowser_ setDataSource:self]; | |
| 156 [sourceBrowser_ setCellsStyleMask:cellStyle]; | |
| 157 [sourceBrowser_ setCellSize:NSMakeSize(kThumbnailWidth, kThumbnailHeight)]; | |
| 158 [sourceBrowser_ setAllowsMultipleSelection:NO]; | |
| 159 | |
| 160 // Create a scroll view to host the image browser. | |
| 161 NSRect imageBrowserScrollFrame = | |
| 162 NSMakeRect(origin.x, origin.y, kPaddedWidth, 350); | |
| 163 base::scoped_nsobject<NSScrollView> imageBrowserScroll( | |
| 164 [[NSScrollView alloc] initWithFrame:imageBrowserScrollFrame]); | |
| 165 [imageBrowserScroll setHasVerticalScroller:YES]; | |
| 166 [imageBrowserScroll setDocumentView:sourceBrowser_]; | |
| 167 [imageBrowserScroll setBorderType:NSBezelBorder]; | |
| 168 [imageBrowserScroll | |
| 169 setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; | |
| 170 [content addSubview:imageBrowserScroll]; | |
| 171 origin.y += NSHeight(imageBrowserScrollFrame) + kControlSpacing; | |
| 172 | |
| 173 // Create a checkbox for audio sharing. | |
| 174 if (requestAudio) { | |
| 175 audioShareCheckbox_.reset([[NSButton alloc] initWithFrame:NSZeroRect]); | |
| 176 [audioShareCheckbox_ setFrameOrigin:origin]; | |
| 177 [audioShareCheckbox_ | |
| 178 setAutoresizingMask:NSViewMaxXMargin | NSViewMinYMargin]; | |
| 179 [audioShareCheckbox_ setButtonType:NSSwitchButton]; | |
| 180 audioShareState_ = NSOnState; | |
| 181 [audioShareCheckbox_ | |
| 182 setTitle:l10n_util::GetNSString(IDS_DESKTOP_MEDIA_PICKER_AUDIO_SHARE)]; | |
| 183 [audioShareCheckbox_ sizeToFit]; | |
| 184 [audioShareCheckbox_ setEnabled:NO]; | |
| 185 [audioShareCheckbox_ | |
| 186 setToolTip:l10n_util::GetNSString( | |
| 187 IDS_DESKTOP_MEDIA_PICKER_AUDIO_SHARE_TOOLTIP_MAC)]; | |
| 188 [content addSubview:audioShareCheckbox_]; | |
| 189 origin.y += NSHeight([audioShareCheckbox_ frame]) + kControlSpacing; | |
| 190 } | |
| 191 | |
| 192 // Create the share button. | |
| 193 shareButton_ = | |
| 194 [self createButtonWithTitle:l10n_util::GetNSString( | |
| 195 IDS_DESKTOP_MEDIA_PICKER_SHARE)]; | |
| 196 origin.x = kInitialContentWidth - kFramePadding - | |
| 197 (NSWidth([shareButton_ frame]) - kExcessButtonPadding); | |
| 198 [shareButton_ setEnabled:NO]; | |
| 199 [shareButton_ setFrameOrigin:origin]; | |
| 200 [shareButton_ setAutoresizingMask:NSViewMinXMargin | NSViewMinYMargin]; | |
| 201 [shareButton_ setTarget:self]; | |
| 202 [shareButton_ setKeyEquivalent:kKeyEquivalentReturn]; | |
| 203 [shareButton_ setAction:@selector(sharePressed:)]; | |
| 204 [content addSubview:shareButton_]; | |
| 205 | |
| 206 // Create the cancel button. | |
| 207 cancelButton_ = | |
| 208 [self createButtonWithTitle:l10n_util::GetNSString(IDS_CANCEL)]; | |
| 209 origin.x -= kControlSpacing + | |
| 210 (NSWidth([cancelButton_ frame]) - (kExcessButtonPadding * 2)); | |
| 211 [cancelButton_ setFrameOrigin:origin]; | |
| 212 [cancelButton_ setAutoresizingMask:NSViewMinXMargin | NSViewMinYMargin]; | |
| 213 [cancelButton_ setTarget:self]; | |
| 214 [cancelButton_ setKeyEquivalent:kKeyEquivalentEscape]; | |
| 215 [cancelButton_ setAction:@selector(cancelPressed:)]; | |
| 216 [content addSubview:cancelButton_]; | |
| 217 origin.y += | |
| 218 kFramePadding + (NSHeight([cancelButton_ frame]) - kExcessButtonPadding); | |
| 219 | |
| 220 // Resize window to fit. | |
| 221 [[[self window] contentView] setAutoresizesSubviews:NO]; | |
| 222 [[self window] setContentSize:NSMakeSize(kInitialContentWidth, origin.y)]; | |
| 223 [[self window] setContentMinSize:NSMakeSize(kMinimumContentWidth, | |
| 224 kMinimumContentHeight)]; | |
| 225 [[[self window] contentView] setAutoresizesSubviews:YES]; | |
| 226 | |
| 227 // Make sourceBrowser_ get keyboard focus. | |
| 228 [[self window] makeFirstResponder:sourceBrowser_]; | |
| 229 } | |
| 230 | |
| 231 - (void)showWindow:(id)sender { | |
| 232 // Signal the media_list to start sending thumbnails. |bridge_| is used as the | |
| 233 // observer, and will forward notifications to this object. | |
| 234 media_list_->SetThumbnailSize(gfx::Size(kThumbnailWidth, kThumbnailHeight)); | |
| 235 media_list_->StartUpdating(bridge_.get()); | |
| 236 | |
| 237 [self.window center]; | |
| 238 [super showWindow:sender]; | |
| 239 } | |
| 240 | |
| 241 - (void)reportResult:(content::DesktopMediaID)sourceID { | |
| 242 if (doneCallback_.is_null()) { | |
| 243 return; | |
| 244 } | |
| 245 | |
| 246 sourceID.audio_share = [audioShareCheckbox_ isEnabled] && | |
| 247 [audioShareCheckbox_ state] == NSOnState; | |
| 248 | |
| 249 // If the media source is an tab, activate it. | |
| 250 if (sourceID.type == content::DesktopMediaID::TYPE_WEB_CONTENTS) { | |
| 251 content::WebContents* tab = content::WebContents::FromRenderFrameHost( | |
| 252 content::RenderFrameHost::FromID( | |
| 253 sourceID.web_contents_id.render_process_id, | |
| 254 sourceID.web_contents_id.main_render_frame_id)); | |
| 255 if (tab) | |
| 256 tab->GetDelegate()->ActivateContents(tab); | |
| 257 } | |
| 258 | |
| 259 // Notify the |callback_| asynchronously because it may release the | |
| 260 // controller. | |
| 261 content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, | |
| 262 base::Bind(doneCallback_, sourceID)); | |
| 263 doneCallback_.Reset(); | |
| 264 } | |
| 265 | |
| 266 - (void)sharePressed:(id)sender { | |
| 267 NSIndexSet* indexes = [sourceBrowser_ selectionIndexes]; | |
| 268 NSUInteger selectedIndex = [indexes firstIndex]; | |
| 269 DesktopMediaPickerItem* item = [items_ objectAtIndex:selectedIndex]; | |
| 270 [self reportResult:[item sourceID]]; | |
| 271 [self close]; | |
| 272 } | |
| 273 | |
| 274 - (void)cancelPressed:(id)sender { | |
| 275 [self reportResult:content::DesktopMediaID()]; | |
| 276 [self close]; | |
| 277 } | |
| 278 | |
| 279 - (NSTextField*)createTextFieldWithText:(NSString*)text | |
| 280 frameWidth:(CGFloat)width { | |
| 281 NSRect frame = NSMakeRect(0, 0, width, 1); | |
| 282 base::scoped_nsobject<NSTextField> textField( | |
| 283 [[NSTextField alloc] initWithFrame:frame]); | |
| 284 [textField setEditable:NO]; | |
| 285 [textField setSelectable:YES]; | |
| 286 [textField setDrawsBackground:NO]; | |
| 287 [textField setBezeled:NO]; | |
| 288 [textField setStringValue:text]; | |
| 289 [textField setFont:[NSFont systemFontOfSize:13]]; | |
| 290 [textField setAutoresizingMask:NSViewWidthSizable]; | |
| 291 [GTMUILocalizerAndLayoutTweaker sizeToFitFixedWidthTextField:textField]; | |
| 292 return textField.autorelease(); | |
| 293 } | |
| 294 | |
| 295 - (NSButton*)createButtonWithTitle:(NSString*)title { | |
| 296 base::scoped_nsobject<NSButton> button( | |
| 297 [[NSButton alloc] initWithFrame:NSZeroRect]); | |
| 298 [button setButtonType:NSMomentaryPushInButton]; | |
| 299 [button setBezelStyle:NSRoundedBezelStyle]; | |
| 300 [button setTitle:title]; | |
| 301 [GTMUILocalizerAndLayoutTweaker sizeToFitView:button]; | |
| 302 return button.autorelease(); | |
| 303 } | |
| 304 | |
| 305 #pragma mark NSWindowDelegate | |
| 306 | |
| 307 - (void)windowWillClose:(NSNotification*)notification { | |
| 308 // Report the result if it hasn't been reported yet. |reportResult:| ensures | |
| 309 // that the result is only reported once. | |
| 310 [self reportResult:content::DesktopMediaID()]; | |
| 311 | |
| 312 // Remove self from the parent. | |
| 313 NSWindow* window = [self window]; | |
| 314 [[window parentWindow] removeChildWindow:window]; | |
| 315 } | |
| 316 | |
| 317 #pragma mark IKImageBrowserDataSource | |
| 318 | |
| 319 - (NSUInteger)numberOfItemsInImageBrowser:(IKImageBrowserView*)browser { | |
| 320 return [items_ count]; | |
| 321 } | |
| 322 | |
| 323 - (id)imageBrowser:(IKImageBrowserView*)browser itemAtIndex:(NSUInteger)index { | |
| 324 return [items_ objectAtIndex:index]; | |
| 325 } | |
| 326 | |
| 327 #pragma mark IKImageBrowserDelegate | |
| 328 | |
| 329 - (void)imageBrowser:(IKImageBrowserView*)browser | |
| 330 cellWasDoubleClickedAtIndex:(NSUInteger)index { | |
| 331 DesktopMediaPickerItem* item = [items_ objectAtIndex:index]; | |
| 332 [self reportResult:[item sourceID]]; | |
| 333 [self close]; | |
| 334 } | |
| 335 | |
| 336 - (void)imageBrowserSelectionDidChange:(IKImageBrowserView*)browser { | |
| 337 NSIndexSet* indexes = [sourceBrowser_ selectionIndexes]; | |
| 338 | |
| 339 // Enable or disable the OK button based on whether we have a selection. | |
| 340 [shareButton_ setEnabled:([indexes count] > 0)]; | |
| 341 | |
| 342 // Enable or disable the checkbox based on whether we can support audio for | |
| 343 // the selected source. | |
| 344 // On Mac, the checkbox will enabled for tab sharing, namely | |
| 345 // TYPE_WEB_CONTENTS. | |
| 346 if ([indexes count] == 0) { | |
| 347 if ([audioShareCheckbox_ isEnabled]) { | |
| 348 [audioShareCheckbox_ setEnabled:NO]; | |
| 349 audioShareState_ = [audioShareCheckbox_ state]; | |
| 350 [audioShareCheckbox_ setState:NSOffState]; | |
| 351 } | |
| 352 [audioShareCheckbox_ | |
| 353 setToolTip:l10n_util::GetNSString( | |
| 354 IDS_DESKTOP_MEDIA_PICKER_AUDIO_SHARE_TOOLTIP_MAC)]; | |
| 355 return; | |
| 356 } | |
| 357 | |
| 358 NSUInteger selectedIndex = [indexes firstIndex]; | |
| 359 DesktopMediaPickerItem* item = [items_ objectAtIndex:selectedIndex]; | |
| 360 switch ([item sourceID].type) { | |
| 361 case content::DesktopMediaID::TYPE_SCREEN: | |
| 362 case content::DesktopMediaID::TYPE_WINDOW: | |
| 363 if ([audioShareCheckbox_ isEnabled]) { | |
| 364 [audioShareCheckbox_ setEnabled:NO]; | |
| 365 audioShareState_ = [audioShareCheckbox_ state]; | |
| 366 [audioShareCheckbox_ setState:NSOffState]; | |
| 367 } | |
| 368 [audioShareCheckbox_ | |
| 369 setToolTip:l10n_util::GetNSString( | |
| 370 IDS_DESKTOP_MEDIA_PICKER_AUDIO_SHARE_TOOLTIP_MAC)]; | |
| 371 break; | |
| 372 case content::DesktopMediaID::TYPE_WEB_CONTENTS: | |
| 373 if (![audioShareCheckbox_ isEnabled]) { | |
| 374 [audioShareCheckbox_ setEnabled:YES]; | |
| 375 [audioShareCheckbox_ setState:audioShareState_]; | |
| 376 } | |
| 377 [audioShareCheckbox_ setToolTip:@""]; | |
| 378 break; | |
| 379 case content::DesktopMediaID::TYPE_NONE: | |
| 380 NOTREACHED(); | |
| 381 } | |
| 382 } | |
| 383 | |
| 384 #pragma mark DesktopMediaPickerObserver | |
| 385 | |
| 386 - (void)sourceAddedForList:(DesktopMediaList*)list atIndex:(int)index { | |
| 387 const DesktopMediaList::Source& source = media_list_->GetSource(index); | |
| 388 NSString* imageTitle = base::SysUTF16ToNSString(source.name); | |
| 389 base::scoped_nsobject<DesktopMediaPickerItem> item( | |
| 390 [[DesktopMediaPickerItem alloc] initWithSourceId:source.id | |
| 391 imageUID:++lastImageUID_ | |
| 392 imageTitle:imageTitle]); | |
| 393 [items_ insertObject:item atIndex:index]; | |
| 394 [sourceBrowser_ reloadData]; | |
| 395 | |
| 396 NSString* autoselectSource = base::SysUTF8ToNSString( | |
| 397 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( | |
| 398 switches::kAutoSelectDesktopCaptureSource)); | |
| 399 | |
| 400 if ([autoselectSource isEqualToString:imageTitle]) { | |
| 401 [self reportResult:[item sourceID]]; | |
| 402 [self close]; | |
| 403 } | |
| 404 } | |
| 405 | |
| 406 - (void)sourceRemovedForList:(DesktopMediaList*)list atIndex:(int)index { | |
| 407 if ([[sourceBrowser_ selectionIndexes] containsIndex:index]) { | |
| 408 // Selected item was removed. Clear selection. | |
| 409 [sourceBrowser_ setSelectionIndexes:[NSIndexSet indexSet] | |
| 410 byExtendingSelection:FALSE]; | |
| 411 } | |
| 412 [items_ removeObjectAtIndex:index]; | |
| 413 [sourceBrowser_ reloadData]; | |
| 414 } | |
| 415 | |
| 416 - (void)sourceMovedForList:(DesktopMediaList*)list | |
| 417 from:(int)oldIndex | |
| 418 to:(int)newIndex { | |
| 419 base::scoped_nsobject<DesktopMediaPickerItem> item( | |
| 420 [[items_ objectAtIndex:oldIndex] retain]); | |
| 421 [items_ removeObjectAtIndex:oldIndex]; | |
| 422 [items_ insertObject:item atIndex:newIndex]; | |
| 423 [sourceBrowser_ reloadData]; | |
| 424 } | |
| 425 | |
| 426 - (void)sourceNameChangedForList:(DesktopMediaList*)list atIndex:(int)index { | |
| 427 DesktopMediaPickerItem* item = [items_ objectAtIndex:index]; | |
| 428 const DesktopMediaList::Source& source = media_list_->GetSource(index); | |
| 429 [item setImageTitle:base::SysUTF16ToNSString(source.name)]; | |
| 430 [sourceBrowser_ reloadData]; | |
| 431 } | |
| 432 | |
| 433 - (void)sourceThumbnailChangedForList:(DesktopMediaList*)list | |
| 434 atIndex:(int)index { | |
| 435 const DesktopMediaList::Source& source = media_list_->GetSource(index); | |
| 436 NSImage* image = gfx::NSImageFromImageSkia(source.thumbnail); | |
| 437 | |
| 438 DesktopMediaPickerItem* item = [items_ objectAtIndex:index]; | |
| 439 [item setImageRepresentation:image]; | |
| 440 [sourceBrowser_ reloadData]; | |
| 441 } | |
| 442 | |
| 443 @end // @interface DesktopMediaPickerController | |
| OLD | NEW |