1/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2/* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6#import <Cocoa/Cocoa.h> 7 8#include "nsFilePicker.h" 9#include "nsCOMPtr.h" 10#include "nsReadableUtils.h" 11#include "nsNetUtil.h" 12#include "nsIComponentManager.h" 13#include "nsIFile.h" 14#include "nsILocalFileMac.h" 15#include "nsIURL.h" 16#include "nsArrayEnumerator.h" 17#include "nsIStringBundle.h" 18#include "nsCocoaUtils.h" 19#include "mozilla/Preferences.h" 20 21// This must be included last: 22#include "nsObjCExceptions.h" 23 24using namespace mozilla; 25 26const float kAccessoryViewPadding = 5; 27const int kSaveTypeControlTag = 1; 28 29static bool gCallSecretHiddenFileAPI = false; 30const char kShowHiddenFilesPref[] = "filepicker.showHiddenFiles"; 31 32/** 33 * This class is an observer of NSPopUpButton selection change. 34 */ 35@interface NSPopUpButtonObserver : NSObject 36{ 37 NSPopUpButton* mPopUpButton; 38 NSOpenPanel* mOpenPanel; 39 nsFilePicker* mFilePicker; 40} 41- (void) setPopUpButton:(NSPopUpButton*)aPopUpButton; 42- (void) setOpenPanel:(NSOpenPanel*)aOpenPanel; 43- (void) setFilePicker:(nsFilePicker*)aFilePicker; 44- (void) menuChangedItem:(NSNotification*)aSender; 45@end 46 47NS_IMPL_ISUPPORTS(nsFilePicker, nsIFilePicker) 48 49// We never want to call the secret show hidden files API unless the pref 50// has been set. Once the pref has been set we always need to call it even 51// if it disappears so that we stop showing hidden files if a user deletes 52// the pref. If the secret API was used once and things worked out it should 53// continue working for subsequent calls so the user is at no more risk. 54static void SetShowHiddenFileState(NSSavePanel* panel) 55{ 56 NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 57 58 bool show = false; 59 if (NS_SUCCEEDED(Preferences::GetBool(kShowHiddenFilesPref, &show))) { 60 gCallSecretHiddenFileAPI = true; 61 } 62 63 if (gCallSecretHiddenFileAPI) { 64 // invoke a method to get a Cocoa-internal nav view 65 SEL navViewSelector = @selector(_navView); 66 NSMethodSignature* navViewSignature = [panel methodSignatureForSelector:navViewSelector]; 67 if (!navViewSignature) 68 return; 69 NSInvocation* navViewInvocation = [NSInvocation invocationWithMethodSignature:navViewSignature]; 70 [navViewInvocation setSelector:navViewSelector]; 71 [navViewInvocation setTarget:panel]; 72 [navViewInvocation invoke]; 73 74 // get the returned nav view 75 id navView = nil; 76 [navViewInvocation getReturnValue:&navView]; 77 78 // invoke the secret show hidden file state method on the nav view 79 SEL showHiddenFilesSelector = @selector(setShowsHiddenFiles:); 80 NSMethodSignature* showHiddenFilesSignature = [navView methodSignatureForSelector:showHiddenFilesSelector]; 81 if (!showHiddenFilesSignature) 82 return; 83 NSInvocation* showHiddenFilesInvocation = [NSInvocation invocationWithMethodSignature:showHiddenFilesSignature]; 84 [showHiddenFilesInvocation setSelector:showHiddenFilesSelector]; 85 [showHiddenFilesInvocation setTarget:navView]; 86 [showHiddenFilesInvocation setArgument:&show atIndex:2]; 87 [showHiddenFilesInvocation invoke]; 88 } 89 90 NS_OBJC_END_TRY_ABORT_BLOCK; 91} 92 93nsFilePicker::nsFilePicker() 94: mSelectedTypeIndex(0) 95{ 96} 97 98nsFilePicker::~nsFilePicker() 99{ 100} 101 102void 103nsFilePicker::InitNative(nsIWidget *aParent, const nsAString& aTitle) 104{ 105 mTitle = aTitle; 106} 107 108NSView* nsFilePicker::GetAccessoryView() 109{ 110 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 111 112 NSView* accessoryView = [[[NSView alloc] initWithFrame:NSMakeRect(0, 0, 0, 0)] autorelease]; 113 114 // Set a label's default value. 115 NSString* label = @"Format:"; 116 117 // Try to get the localized string. 118 nsCOMPtr<nsIStringBundleService> sbs = do_GetService(NS_STRINGBUNDLE_CONTRACTID); 119 nsCOMPtr<nsIStringBundle> bundle; 120 nsresult rv = sbs->CreateBundle("chrome://global/locale/filepicker.properties", getter_AddRefs(bundle)); 121 if (NS_SUCCEEDED(rv)) { 122 nsXPIDLString locaLabel; 123 bundle->GetStringFromName(u"formatLabel", getter_Copies(locaLabel)); 124 if (locaLabel) { 125 label = [NSString stringWithCharacters:reinterpret_cast<const unichar*>(locaLabel.get()) 126 length:locaLabel.Length()]; 127 } 128 } 129 130 // set up label text field 131 NSTextField* textField = [[[NSTextField alloc] init] autorelease]; 132 [textField setEditable:NO]; 133 [textField setSelectable:NO]; 134 [textField setDrawsBackground:NO]; 135 [textField setBezeled:NO]; 136 [textField setBordered:NO]; 137 [textField setFont:[NSFont labelFontOfSize:13.0]]; 138 [textField setStringValue:label]; 139 [textField setTag:0]; 140 [textField sizeToFit]; 141 142 // set up popup button 143 NSPopUpButton* popupButton = [[[NSPopUpButton alloc] initWithFrame:NSMakeRect(0, 0, 0, 0) pullsDown:NO] autorelease]; 144 uint32_t numMenuItems = mTitles.Length(); 145 for (uint32_t i = 0; i < numMenuItems; i++) { 146 const nsString& currentTitle = mTitles[i]; 147 NSString *titleString; 148 if (currentTitle.IsEmpty()) { 149 const nsString& currentFilter = mFilters[i]; 150 titleString = [[NSString alloc] initWithCharacters:reinterpret_cast<const unichar*>(currentFilter.get()) 151 length:currentFilter.Length()]; 152 } 153 else { 154 titleString = [[NSString alloc] initWithCharacters:reinterpret_cast<const unichar*>(currentTitle.get()) 155 length:currentTitle.Length()]; 156 } 157 [popupButton addItemWithTitle:titleString]; 158 [titleString release]; 159 } 160 if (mSelectedTypeIndex >= 0 && (uint32_t)mSelectedTypeIndex < numMenuItems) 161 [popupButton selectItemAtIndex:mSelectedTypeIndex]; 162 [popupButton setTag:kSaveTypeControlTag]; 163 [popupButton sizeToFit]; // we have to do sizeToFit to get the height calculated for us 164 // This is just a default width that works well, doesn't truncate the vast majority of 165 // things that might end up in the menu. 166 [popupButton setFrameSize:NSMakeSize(180, [popupButton frame].size.height)]; 167 168 // position everything based on control sizes with kAccessoryViewPadding pix padding 169 // on each side kAccessoryViewPadding pix horizontal padding between controls 170 float greatestHeight = [textField frame].size.height; 171 if ([popupButton frame].size.height > greatestHeight) 172 greatestHeight = [popupButton frame].size.height; 173 float totalViewHeight = greatestHeight + kAccessoryViewPadding * 2; 174 float totalViewWidth = [textField frame].size.width + [popupButton frame].size.width + kAccessoryViewPadding * 3; 175 [accessoryView setFrameSize:NSMakeSize(totalViewWidth, totalViewHeight)]; 176 177 float textFieldOriginY = ((greatestHeight - [textField frame].size.height) / 2 + 1) + kAccessoryViewPadding; 178 [textField setFrameOrigin:NSMakePoint(kAccessoryViewPadding, textFieldOriginY)]; 179 180 float popupOriginX = [textField frame].size.width + kAccessoryViewPadding * 2; 181 float popupOriginY = ((greatestHeight - [popupButton frame].size.height) / 2) + kAccessoryViewPadding; 182 [popupButton setFrameOrigin:NSMakePoint(popupOriginX, popupOriginY)]; 183 184 [accessoryView addSubview:textField]; 185 [accessoryView addSubview:popupButton]; 186 return accessoryView; 187 188 NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 189} 190 191// Display the file dialog 192NS_IMETHODIMP nsFilePicker::Show(int16_t *retval) 193{ 194 NS_ENSURE_ARG_POINTER(retval); 195 196 *retval = returnCancel; 197 198 int16_t userClicksOK = returnCancel; 199 200// Random questions from DHH: 201// 202// Why do we pass mTitle, mDefault to the functions? Can GetLocalFile. PutLocalFile, 203// and GetLocalFolder get called someplace else? It generates a bunch of warnings 204// as it is right now. 205// 206// I think we could easily combine GetLocalFile and GetLocalFolder together, just 207// setting panel pick options based on mMode. I didn't do it here b/c I wanted to 208// make this look as much like Carbon nsFilePicker as possible. 209 210 mFiles.Clear(); 211 nsCOMPtr<nsIFile> theFile; 212 213 switch (mMode) 214 { 215 case modeOpen: 216 userClicksOK = GetLocalFiles(mTitle, false, mFiles); 217 break; 218 219 case modeOpenMultiple: 220 userClicksOK = GetLocalFiles(mTitle, true, mFiles); 221 break; 222 223 case modeSave: 224 userClicksOK = PutLocalFile(mTitle, mDefault, getter_AddRefs(theFile)); 225 break; 226 227 case modeGetFolder: 228 userClicksOK = GetLocalFolder(mTitle, getter_AddRefs(theFile)); 229 break; 230 231 default: 232 NS_ERROR("Unknown file picker mode"); 233 break; 234 } 235 236 if (theFile) 237 mFiles.AppendObject(theFile); 238 239 *retval = userClicksOK; 240 return NS_OK; 241} 242 243static 244void UpdatePanelFileTypes(NSOpenPanel* aPanel, NSArray* aFilters) 245{ 246 // If we show all file types, also "expose" bundles' contents. 247 [aPanel setTreatsFilePackagesAsDirectories:!aFilters]; 248 249 [aPanel setAllowedFileTypes:aFilters]; 250} 251 252@implementation NSPopUpButtonObserver 253- (void) setPopUpButton:(NSPopUpButton*)aPopUpButton 254{ 255 mPopUpButton = aPopUpButton; 256} 257 258- (void) setOpenPanel:(NSOpenPanel*)aOpenPanel 259{ 260 mOpenPanel = aOpenPanel; 261} 262 263- (void) setFilePicker:(nsFilePicker*)aFilePicker 264{ 265 mFilePicker = aFilePicker; 266} 267 268- (void) menuChangedItem:(NSNotification *)aSender 269{ 270 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; 271 int32_t selectedItem = [mPopUpButton indexOfSelectedItem]; 272 if (selectedItem < 0) { 273 return; 274 } 275 276 mFilePicker->SetFilterIndex(selectedItem); 277 UpdatePanelFileTypes(mOpenPanel, mFilePicker->GetFilterList()); 278 279 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(); 280} 281@end 282 283// Use OpenPanel to do a GetFile. Returns |returnOK| if the user presses OK in the dialog. 284int16_t 285nsFilePicker::GetLocalFiles(const nsString& inTitle, bool inAllowMultiple, nsCOMArray<nsIFile>& outFiles) 286{ 287 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; 288 289 int16_t retVal = (int16_t)returnCancel; 290 NSOpenPanel *thePanel = [NSOpenPanel openPanel]; 291 292 SetShowHiddenFileState(thePanel); 293 294 // Set the options for how the get file dialog will appear 295 SetDialogTitle(inTitle, thePanel); 296 [thePanel setAllowsMultipleSelection:inAllowMultiple]; 297 [thePanel setCanSelectHiddenExtension:YES]; 298 [thePanel setCanChooseDirectories:NO]; 299 [thePanel setCanChooseFiles:YES]; 300 [thePanel setResolvesAliases:YES]; //this is default - probably doesn't need to be set 301 302 // Get filters 303 // filters may be null, if we should allow all file types. 304 NSArray *filters = GetFilterList(); 305 306 // set up default directory 307 NSString *theDir = PanelDefaultDirectory(); 308 309 // if this is the "Choose application..." dialog, and no other start 310 // dir has been set, then use the Applications folder. 311 if (!theDir) { 312 if (filters && [filters count] == 1 && 313 [(NSString *)[filters objectAtIndex:0] isEqualToString:@"app"]) 314 theDir = @"/Applications/"; 315 else 316 theDir = @""; 317 } 318 319 if (theDir) { 320 [thePanel setDirectoryURL:[NSURL fileURLWithPath:theDir isDirectory:YES]]; 321 } 322 323 int result; 324 nsCocoaUtils::PrepareForNativeAppModalDialog(); 325 if (mFilters.Length() > 1) { 326 // [NSURL initWithString:] (below) throws an exception if URLString is nil. 327 328 NSPopUpButtonObserver* observer = [[NSPopUpButtonObserver alloc] init]; 329 330 NSView* accessoryView = GetAccessoryView(); 331 [thePanel setAccessoryView:accessoryView]; 332 333 [observer setPopUpButton:[accessoryView viewWithTag:kSaveTypeControlTag]]; 334 [observer setOpenPanel:thePanel]; 335 [observer setFilePicker:this]; 336 337 [[NSNotificationCenter defaultCenter] 338 addObserver:observer 339 selector:@selector(menuChangedItem:) 340 name:NSMenuWillSendActionNotification object:nil]; 341 342 UpdatePanelFileTypes(thePanel, filters); 343 result = [thePanel runModal]; 344 345 [[NSNotificationCenter defaultCenter] removeObserver:observer]; 346 [observer release]; 347 } else { 348 // If we show all file types, also "expose" bundles' contents. 349 if (!filters) { 350 [thePanel setTreatsFilePackagesAsDirectories:YES]; 351 } 352 [thePanel setAllowedFileTypes:filters]; 353 result = [thePanel runModal]; 354 } 355 nsCocoaUtils::CleanUpAfterNativeAppModalDialog(); 356 357 if (result == NSFileHandlingPanelCancelButton) 358 return retVal; 359 360 // Converts data from a NSArray of NSURL to the returned format. 361 // We should be careful to not call [thePanel URLs] more than once given that 362 // it creates a new array each time. 363 // We are using Fast Enumeration, thus the NSURL array is created once then 364 // iterated. 365 for (NSURL* url in [thePanel URLs]) { 366 if (!url) { 367 continue; 368 } 369 370 nsCOMPtr<nsIFile> localFile; 371 NS_NewLocalFile(EmptyString(), true, getter_AddRefs(localFile)); 372 nsCOMPtr<nsILocalFileMac> macLocalFile = do_QueryInterface(localFile); 373 if (macLocalFile && NS_SUCCEEDED(macLocalFile->InitWithCFURL((CFURLRef)url))) { 374 outFiles.AppendObject(localFile); 375 } 376 } 377 378 if (outFiles.Count() > 0) 379 retVal = returnOK; 380 381 return retVal; 382 383 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0); 384} 385 386// Use OpenPanel to do a GetFolder. Returns |returnOK| if the user presses OK in the dialog. 387int16_t 388nsFilePicker::GetLocalFolder(const nsString& inTitle, nsIFile** outFile) 389{ 390 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; 391 NS_ASSERTION(outFile, "this protected member function expects a null initialized out pointer"); 392 393 int16_t retVal = (int16_t)returnCancel; 394 NSOpenPanel *thePanel = [NSOpenPanel openPanel]; 395 396 SetShowHiddenFileState(thePanel); 397 398 // Set the options for how the get file dialog will appear 399 SetDialogTitle(inTitle, thePanel); 400 [thePanel setAllowsMultipleSelection:NO]; //this is default -probably doesn't need to be set 401 [thePanel setCanSelectHiddenExtension:YES]; 402 [thePanel setCanChooseDirectories:YES]; 403 [thePanel setCanChooseFiles:NO]; 404 [thePanel setResolvesAliases:YES]; //this is default - probably doesn't need to be set 405 [thePanel setCanCreateDirectories:YES]; 406 407 // packages != folders 408 [thePanel setTreatsFilePackagesAsDirectories:NO]; 409 410 // set up default directory 411 NSString *theDir = PanelDefaultDirectory(); 412 if (theDir) { 413 [thePanel setDirectoryURL:[NSURL fileURLWithPath:theDir isDirectory:YES]]; 414 } 415 nsCocoaUtils::PrepareForNativeAppModalDialog(); 416 int result = [thePanel runModal]; 417 nsCocoaUtils::CleanUpAfterNativeAppModalDialog(); 418 419 if (result == NSFileHandlingPanelCancelButton) 420 return retVal; 421 422 // get the path for the folder (we allow just 1, so that's all we get) 423 NSURL *theURL = [[thePanel URLs] objectAtIndex:0]; 424 if (theURL) { 425 nsCOMPtr<nsIFile> localFile; 426 NS_NewLocalFile(EmptyString(), true, getter_AddRefs(localFile)); 427 nsCOMPtr<nsILocalFileMac> macLocalFile = do_QueryInterface(localFile); 428 if (macLocalFile && NS_SUCCEEDED(macLocalFile->InitWithCFURL((CFURLRef)theURL))) { 429 *outFile = localFile; 430 NS_ADDREF(*outFile); 431 retVal = returnOK; 432 } 433 } 434 435 return retVal; 436 437 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0); 438} 439 440// Returns |returnOK| if the user presses OK in the dialog. 441int16_t 442nsFilePicker::PutLocalFile(const nsString& inTitle, const nsString& inDefaultName, nsIFile** outFile) 443{ 444 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; 445 NS_ASSERTION(outFile, "this protected member function expects a null initialized out pointer"); 446 447 int16_t retVal = returnCancel; 448 NSSavePanel *thePanel = [NSSavePanel savePanel]; 449 450 SetShowHiddenFileState(thePanel); 451 452 SetDialogTitle(inTitle, thePanel); 453 454 // set up accessory view for file format options 455 NSView* accessoryView = GetAccessoryView(); 456 [thePanel setAccessoryView:accessoryView]; 457 458 // set up default file name 459 NSString* defaultFilename = [NSString stringWithCharacters:(const unichar*)inDefaultName.get() length:inDefaultName.Length()]; 460 461 // set up allowed types; this prevents the extension from being selected 462 // use the UTI for the file type to allow alternate extensions (e.g., jpg vs. jpeg) 463 NSString* extension = defaultFilename.pathExtension; 464 if (extension.length != 0) { 465 CFStringRef type = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (CFStringRef)extension, NULL); 466 467 if (type) { 468 thePanel.allowedFileTypes = @[(NSString*)type]; 469 CFRelease(type); 470 } else { 471 // if there's no UTI for the file extension, use the extension itself. 472 thePanel.allowedFileTypes = @[extension]; 473 } 474 } 475 // Allow users to change the extension. 476 thePanel.allowsOtherFileTypes = YES; 477 478 // set up default directory 479 NSString *theDir = PanelDefaultDirectory(); 480 if (theDir) { 481 [thePanel setDirectoryURL:[NSURL fileURLWithPath:theDir isDirectory:YES]]; 482 } 483 484 // load the panel 485 nsCocoaUtils::PrepareForNativeAppModalDialog(); 486 [thePanel setNameFieldStringValue:defaultFilename]; 487 int result = [thePanel runModal]; 488 nsCocoaUtils::CleanUpAfterNativeAppModalDialog(); 489 if (result == NSFileHandlingPanelCancelButton) 490 return retVal; 491 492 // get the save type 493 NSPopUpButton* popupButton = [accessoryView viewWithTag:kSaveTypeControlTag]; 494 if (popupButton) { 495 mSelectedTypeIndex = [popupButton indexOfSelectedItem]; 496 } 497 498 NSURL* fileURL = [thePanel URL]; 499 if (fileURL) { 500 nsCOMPtr<nsIFile> localFile; 501 NS_NewLocalFile(EmptyString(), true, getter_AddRefs(localFile)); 502 nsCOMPtr<nsILocalFileMac> macLocalFile = do_QueryInterface(localFile); 503 if (macLocalFile && NS_SUCCEEDED(macLocalFile->InitWithCFURL((CFURLRef)fileURL))) { 504 *outFile = localFile; 505 NS_ADDREF(*outFile); 506 // We tell if we are replacing or not by just looking to see if the file exists. 507 // The user could not have hit OK and not meant to replace the file. 508 if ([[NSFileManager defaultManager] fileExistsAtPath:[fileURL path]]) 509 retVal = returnReplace; 510 else 511 retVal = returnOK; 512 } 513 } 514 515 return retVal; 516 517 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0); 518} 519 520NSArray * 521nsFilePicker::GetFilterList() 522{ 523 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 524 525 if (!mFilters.Length()) { 526 return nil; 527 } 528 529 if (mFilters.Length() <= (uint32_t)mSelectedTypeIndex) { 530 NS_WARNING("An out of range index has been selected. Using the first index instead."); 531 mSelectedTypeIndex = 0; 532 } 533 534 const nsString& filterWide = mFilters[mSelectedTypeIndex]; 535 if (!filterWide.Length()) { 536 return nil; 537 } 538 539 if (filterWide.Equals(NS_LITERAL_STRING("*"))) { 540 return nil; 541 } 542 543 // The extensions in filterWide are in the format "*.ext" but are expected 544 // in the format "ext" by NSOpenPanel. So we need to filter some characters. 545 NSMutableString* filterString = [[[NSMutableString alloc] initWithString: 546 [NSString stringWithCharacters:reinterpret_cast<const unichar*>(filterWide.get()) 547 length:filterWide.Length()]] autorelease]; 548 NSCharacterSet *set = [NSCharacterSet characterSetWithCharactersInString:@". *"]; 549 NSRange range = [filterString rangeOfCharacterFromSet:set]; 550 while (range.length) { 551 [filterString replaceCharactersInRange:range withString:@""]; 552 range = [filterString rangeOfCharacterFromSet:set]; 553 } 554 555 return [[[NSArray alloc] initWithArray: 556 [filterString componentsSeparatedByString:@";"]] autorelease]; 557 558 NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 559} 560 561// Sets the dialog title to whatever it should be. If it fails, eh, 562// the OS will provide a sensible default. 563void 564nsFilePicker::SetDialogTitle(const nsString& inTitle, id aPanel) 565{ 566 NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 567 568 [aPanel setTitle:[NSString stringWithCharacters:(const unichar*)inTitle.get() length:inTitle.Length()]]; 569 570 if (!mOkButtonLabel.IsEmpty()) { 571 [aPanel setPrompt:[NSString stringWithCharacters:(const unichar*)mOkButtonLabel.get() length:mOkButtonLabel.Length()]]; 572 } 573 574 NS_OBJC_END_TRY_ABORT_BLOCK; 575} 576 577// Converts path from an nsIFile into a NSString path 578// If it fails, returns an empty string. 579NSString * 580nsFilePicker::PanelDefaultDirectory() 581{ 582 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 583 584 NSString *directory = nil; 585 if (mDisplayDirectory) { 586 nsAutoString pathStr; 587 mDisplayDirectory->GetPath(pathStr); 588 directory = [[[NSString alloc] initWithCharacters:reinterpret_cast<const unichar*>(pathStr.get()) 589 length:pathStr.Length()] autorelease]; 590 } 591 return directory; 592 593 NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 594} 595 596NS_IMETHODIMP nsFilePicker::GetFile(nsIFile **aFile) 597{ 598 NS_ENSURE_ARG_POINTER(aFile); 599 *aFile = nullptr; 600 601 // just return the first file 602 if (mFiles.Count() > 0) { 603 *aFile = mFiles.ObjectAt(0); 604 NS_IF_ADDREF(*aFile); 605 } 606 607 return NS_OK; 608} 609 610NS_IMETHODIMP nsFilePicker::GetFileURL(nsIURI **aFileURL) 611{ 612 NS_ENSURE_ARG_POINTER(aFileURL); 613 *aFileURL = nullptr; 614 615 if (mFiles.Count() == 0) 616 return NS_OK; 617 618 return NS_NewFileURI(aFileURL, mFiles.ObjectAt(0)); 619} 620 621NS_IMETHODIMP nsFilePicker::GetFiles(nsISimpleEnumerator **aFiles) 622{ 623 return NS_NewArrayEnumerator(aFiles, mFiles); 624} 625 626NS_IMETHODIMP nsFilePicker::SetDefaultString(const nsAString& aString) 627{ 628 mDefault = aString; 629 return NS_OK; 630} 631 632NS_IMETHODIMP nsFilePicker::GetDefaultString(nsAString& aString) 633{ 634 return NS_ERROR_FAILURE; 635} 636 637// The default extension to use for files 638NS_IMETHODIMP nsFilePicker::GetDefaultExtension(nsAString& aExtension) 639{ 640 aExtension.Truncate(); 641 return NS_OK; 642} 643 644NS_IMETHODIMP nsFilePicker::SetDefaultExtension(const nsAString& aExtension) 645{ 646 return NS_OK; 647} 648 649// Append an entry to the filters array 650NS_IMETHODIMP 651nsFilePicker::AppendFilter(const nsAString& aTitle, const nsAString& aFilter) 652{ 653 // "..apps" has to be translated with native executable extensions. 654 if (aFilter.EqualsLiteral("..apps")) { 655 mFilters.AppendElement(NS_LITERAL_STRING("*.app")); 656 } else { 657 mFilters.AppendElement(aFilter); 658 } 659 mTitles.AppendElement(aTitle); 660 661 return NS_OK; 662} 663 664// Get the filter index - do we still need this? 665NS_IMETHODIMP nsFilePicker::GetFilterIndex(int32_t *aFilterIndex) 666{ 667 *aFilterIndex = mSelectedTypeIndex; 668 return NS_OK; 669} 670 671// Set the filter index - do we still need this? 672NS_IMETHODIMP nsFilePicker::SetFilterIndex(int32_t aFilterIndex) 673{ 674 mSelectedTypeIndex = aFilterIndex; 675 return NS_OK; 676} 677