1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/widget/cocoa/nsFilePicker.mm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,657 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#import <Cocoa/Cocoa.h> 1.10 + 1.11 +#include "nsFilePicker.h" 1.12 +#include "nsCOMPtr.h" 1.13 +#include "nsReadableUtils.h" 1.14 +#include "nsNetUtil.h" 1.15 +#include "nsIComponentManager.h" 1.16 +#include "nsIFile.h" 1.17 +#include "nsILocalFileMac.h" 1.18 +#include "nsIURL.h" 1.19 +#include "nsArrayEnumerator.h" 1.20 +#include "nsIStringBundle.h" 1.21 +#include "nsCocoaFeatures.h" 1.22 +#include "nsCocoaUtils.h" 1.23 +#include "mozilla/Preferences.h" 1.24 + 1.25 +// This must be included last: 1.26 +#include "nsObjCExceptions.h" 1.27 + 1.28 +using namespace mozilla; 1.29 + 1.30 +const float kAccessoryViewPadding = 5; 1.31 +const int kSaveTypeControlTag = 1; 1.32 + 1.33 +static bool gCallSecretHiddenFileAPI = false; 1.34 +const char kShowHiddenFilesPref[] = "filepicker.showHiddenFiles"; 1.35 + 1.36 +/** 1.37 + * This class is an observer of NSPopUpButton selection change. 1.38 + */ 1.39 +@interface NSPopUpButtonObserver : NSObject 1.40 +{ 1.41 + NSPopUpButton* mPopUpButton; 1.42 + NSOpenPanel* mOpenPanel; 1.43 + nsFilePicker* mFilePicker; 1.44 +} 1.45 +- (void) setPopUpButton:(NSPopUpButton*)aPopUpButton; 1.46 +- (void) setOpenPanel:(NSOpenPanel*)aOpenPanel; 1.47 +- (void) setFilePicker:(nsFilePicker*)aFilePicker; 1.48 +- (void) menuChangedItem:(NSNotification*)aSender; 1.49 +@end 1.50 + 1.51 +NS_IMPL_ISUPPORTS(nsFilePicker, nsIFilePicker) 1.52 + 1.53 +// We never want to call the secret show hidden files API unless the pref 1.54 +// has been set. Once the pref has been set we always need to call it even 1.55 +// if it disappears so that we stop showing hidden files if a user deletes 1.56 +// the pref. If the secret API was used once and things worked out it should 1.57 +// continue working for subsequent calls so the user is at no more risk. 1.58 +static void SetShowHiddenFileState(NSSavePanel* panel) 1.59 +{ 1.60 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1.61 + 1.62 + bool show = false; 1.63 + if (NS_SUCCEEDED(Preferences::GetBool(kShowHiddenFilesPref, &show))) { 1.64 + gCallSecretHiddenFileAPI = true; 1.65 + } 1.66 + 1.67 + if (gCallSecretHiddenFileAPI) { 1.68 + // invoke a method to get a Cocoa-internal nav view 1.69 + SEL navViewSelector = @selector(_navView); 1.70 + NSMethodSignature* navViewSignature = [panel methodSignatureForSelector:navViewSelector]; 1.71 + if (!navViewSignature) 1.72 + return; 1.73 + NSInvocation* navViewInvocation = [NSInvocation invocationWithMethodSignature:navViewSignature]; 1.74 + [navViewInvocation setSelector:navViewSelector]; 1.75 + [navViewInvocation setTarget:panel]; 1.76 + [navViewInvocation invoke]; 1.77 + 1.78 + // get the returned nav view 1.79 + id navView = nil; 1.80 + [navViewInvocation getReturnValue:&navView]; 1.81 + 1.82 + // invoke the secret show hidden file state method on the nav view 1.83 + SEL showHiddenFilesSelector = @selector(setShowsHiddenFiles:); 1.84 + NSMethodSignature* showHiddenFilesSignature = [navView methodSignatureForSelector:showHiddenFilesSelector]; 1.85 + if (!showHiddenFilesSignature) 1.86 + return; 1.87 + NSInvocation* showHiddenFilesInvocation = [NSInvocation invocationWithMethodSignature:showHiddenFilesSignature]; 1.88 + [showHiddenFilesInvocation setSelector:showHiddenFilesSelector]; 1.89 + [showHiddenFilesInvocation setTarget:navView]; 1.90 + [showHiddenFilesInvocation setArgument:&show atIndex:2]; 1.91 + [showHiddenFilesInvocation invoke]; 1.92 + } 1.93 + 1.94 + NS_OBJC_END_TRY_ABORT_BLOCK; 1.95 +} 1.96 + 1.97 +nsFilePicker::nsFilePicker() 1.98 +: mSelectedTypeIndex(0) 1.99 +{ 1.100 +} 1.101 + 1.102 +nsFilePicker::~nsFilePicker() 1.103 +{ 1.104 +} 1.105 + 1.106 +void 1.107 +nsFilePicker::InitNative(nsIWidget *aParent, const nsAString& aTitle) 1.108 +{ 1.109 + mTitle = aTitle; 1.110 +} 1.111 + 1.112 +NSView* nsFilePicker::GetAccessoryView() 1.113 +{ 1.114 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 1.115 + 1.116 + NSView* accessoryView = [[[NSView alloc] initWithFrame:NSMakeRect(0, 0, 0, 0)] autorelease]; 1.117 + 1.118 + // Set a label's default value. 1.119 + NSString* label = @"Format:"; 1.120 + 1.121 + // Try to get the localized string. 1.122 + nsCOMPtr<nsIStringBundleService> sbs = do_GetService(NS_STRINGBUNDLE_CONTRACTID); 1.123 + nsCOMPtr<nsIStringBundle> bundle; 1.124 + nsresult rv = sbs->CreateBundle("chrome://global/locale/filepicker.properties", getter_AddRefs(bundle)); 1.125 + if (NS_SUCCEEDED(rv)) { 1.126 + nsXPIDLString locaLabel; 1.127 + bundle->GetStringFromName(NS_LITERAL_STRING("formatLabel").get(), 1.128 + getter_Copies(locaLabel)); 1.129 + if (locaLabel) { 1.130 + label = [NSString stringWithCharacters:reinterpret_cast<const unichar*>(locaLabel.get()) 1.131 + length:locaLabel.Length()]; 1.132 + } 1.133 + } 1.134 + 1.135 + // set up label text field 1.136 + NSTextField* textField = [[[NSTextField alloc] init] autorelease]; 1.137 + [textField setEditable:NO]; 1.138 + [textField setSelectable:NO]; 1.139 + [textField setDrawsBackground:NO]; 1.140 + [textField setBezeled:NO]; 1.141 + [textField setBordered:NO]; 1.142 + [textField setFont:[NSFont labelFontOfSize:13.0]]; 1.143 + [textField setStringValue:label]; 1.144 + [textField setTag:0]; 1.145 + [textField sizeToFit]; 1.146 + 1.147 + // set up popup button 1.148 + NSPopUpButton* popupButton = [[[NSPopUpButton alloc] initWithFrame:NSMakeRect(0, 0, 0, 0) pullsDown:NO] autorelease]; 1.149 + uint32_t numMenuItems = mTitles.Length(); 1.150 + for (uint32_t i = 0; i < numMenuItems; i++) { 1.151 + const nsString& currentTitle = mTitles[i]; 1.152 + NSString *titleString; 1.153 + if (currentTitle.IsEmpty()) { 1.154 + const nsString& currentFilter = mFilters[i]; 1.155 + titleString = [[NSString alloc] initWithCharacters:reinterpret_cast<const unichar*>(currentFilter.get()) 1.156 + length:currentFilter.Length()]; 1.157 + } 1.158 + else { 1.159 + titleString = [[NSString alloc] initWithCharacters:reinterpret_cast<const unichar*>(currentTitle.get()) 1.160 + length:currentTitle.Length()]; 1.161 + } 1.162 + [popupButton addItemWithTitle:titleString]; 1.163 + [titleString release]; 1.164 + } 1.165 + if (mSelectedTypeIndex >= 0 && (uint32_t)mSelectedTypeIndex < numMenuItems) 1.166 + [popupButton selectItemAtIndex:mSelectedTypeIndex]; 1.167 + [popupButton setTag:kSaveTypeControlTag]; 1.168 + [popupButton sizeToFit]; // we have to do sizeToFit to get the height calculated for us 1.169 + // This is just a default width that works well, doesn't truncate the vast majority of 1.170 + // things that might end up in the menu. 1.171 + [popupButton setFrameSize:NSMakeSize(180, [popupButton frame].size.height)]; 1.172 + 1.173 + // position everything based on control sizes with kAccessoryViewPadding pix padding 1.174 + // on each side kAccessoryViewPadding pix horizontal padding between controls 1.175 + float greatestHeight = [textField frame].size.height; 1.176 + if ([popupButton frame].size.height > greatestHeight) 1.177 + greatestHeight = [popupButton frame].size.height; 1.178 + float totalViewHeight = greatestHeight + kAccessoryViewPadding * 2; 1.179 + float totalViewWidth = [textField frame].size.width + [popupButton frame].size.width + kAccessoryViewPadding * 3; 1.180 + [accessoryView setFrameSize:NSMakeSize(totalViewWidth, totalViewHeight)]; 1.181 + 1.182 + float textFieldOriginY = ((greatestHeight - [textField frame].size.height) / 2 + 1) + kAccessoryViewPadding; 1.183 + [textField setFrameOrigin:NSMakePoint(kAccessoryViewPadding, textFieldOriginY)]; 1.184 + 1.185 + float popupOriginX = [textField frame].size.width + kAccessoryViewPadding * 2; 1.186 + float popupOriginY = ((greatestHeight - [popupButton frame].size.height) / 2) + kAccessoryViewPadding; 1.187 + [popupButton setFrameOrigin:NSMakePoint(popupOriginX, popupOriginY)]; 1.188 + 1.189 + [accessoryView addSubview:textField]; 1.190 + [accessoryView addSubview:popupButton]; 1.191 + return accessoryView; 1.192 + 1.193 + NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 1.194 +} 1.195 + 1.196 +// Display the file dialog 1.197 +NS_IMETHODIMP nsFilePicker::Show(int16_t *retval) 1.198 +{ 1.199 + NS_ENSURE_ARG_POINTER(retval); 1.200 + 1.201 + *retval = returnCancel; 1.202 + 1.203 + int16_t userClicksOK = returnCancel; 1.204 + 1.205 +// Random questions from DHH: 1.206 +// 1.207 +// Why do we pass mTitle, mDefault to the functions? Can GetLocalFile. PutLocalFile, 1.208 +// and GetLocalFolder get called someplace else? It generates a bunch of warnings 1.209 +// as it is right now. 1.210 +// 1.211 +// I think we could easily combine GetLocalFile and GetLocalFolder together, just 1.212 +// setting panel pick options based on mMode. I didn't do it here b/c I wanted to 1.213 +// make this look as much like Carbon nsFilePicker as possible. 1.214 + 1.215 + mFiles.Clear(); 1.216 + nsCOMPtr<nsIFile> theFile; 1.217 + 1.218 + switch (mMode) 1.219 + { 1.220 + case modeOpen: 1.221 + userClicksOK = GetLocalFiles(mTitle, false, mFiles); 1.222 + break; 1.223 + 1.224 + case modeOpenMultiple: 1.225 + userClicksOK = GetLocalFiles(mTitle, true, mFiles); 1.226 + break; 1.227 + 1.228 + case modeSave: 1.229 + userClicksOK = PutLocalFile(mTitle, mDefault, getter_AddRefs(theFile)); 1.230 + break; 1.231 + 1.232 + case modeGetFolder: 1.233 + userClicksOK = GetLocalFolder(mTitle, getter_AddRefs(theFile)); 1.234 + break; 1.235 + 1.236 + default: 1.237 + NS_ERROR("Unknown file picker mode"); 1.238 + break; 1.239 + } 1.240 + 1.241 + if (theFile) 1.242 + mFiles.AppendObject(theFile); 1.243 + 1.244 + *retval = userClicksOK; 1.245 + return NS_OK; 1.246 +} 1.247 + 1.248 +static 1.249 +void UpdatePanelFileTypes(NSOpenPanel* aPanel, NSArray* aFilters) 1.250 +{ 1.251 + // If we show all file types, also "expose" bundles' contents. 1.252 + [aPanel setTreatsFilePackagesAsDirectories:!aFilters]; 1.253 + 1.254 + [aPanel setAllowedFileTypes:aFilters]; 1.255 +} 1.256 + 1.257 +@implementation NSPopUpButtonObserver 1.258 +- (void) setPopUpButton:(NSPopUpButton*)aPopUpButton 1.259 +{ 1.260 + mPopUpButton = aPopUpButton; 1.261 +} 1.262 + 1.263 +- (void) setOpenPanel:(NSOpenPanel*)aOpenPanel 1.264 +{ 1.265 + mOpenPanel = aOpenPanel; 1.266 +} 1.267 + 1.268 +- (void) setFilePicker:(nsFilePicker*)aFilePicker 1.269 +{ 1.270 + mFilePicker = aFilePicker; 1.271 +} 1.272 + 1.273 +- (void) menuChangedItem:(NSNotification *)aSender 1.274 +{ 1.275 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; 1.276 + int32_t selectedItem = [mPopUpButton indexOfSelectedItem]; 1.277 + if (selectedItem < 0) { 1.278 + return; 1.279 + } 1.280 + 1.281 + mFilePicker->SetFilterIndex(selectedItem); 1.282 + UpdatePanelFileTypes(mOpenPanel, mFilePicker->GetFilterList()); 1.283 + 1.284 + NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(); 1.285 +} 1.286 +@end 1.287 + 1.288 +// Use OpenPanel to do a GetFile. Returns |returnOK| if the user presses OK in the dialog. 1.289 +int16_t 1.290 +nsFilePicker::GetLocalFiles(const nsString& inTitle, bool inAllowMultiple, nsCOMArray<nsIFile>& outFiles) 1.291 +{ 1.292 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; 1.293 + 1.294 + int16_t retVal = (int16_t)returnCancel; 1.295 + NSOpenPanel *thePanel = [NSOpenPanel openPanel]; 1.296 + 1.297 + SetShowHiddenFileState(thePanel); 1.298 + 1.299 + // Set the options for how the get file dialog will appear 1.300 + SetDialogTitle(inTitle, thePanel); 1.301 + [thePanel setAllowsMultipleSelection:inAllowMultiple]; 1.302 + [thePanel setCanSelectHiddenExtension:YES]; 1.303 + [thePanel setCanChooseDirectories:NO]; 1.304 + [thePanel setCanChooseFiles:YES]; 1.305 + [thePanel setResolvesAliases:YES]; //this is default - probably doesn't need to be set 1.306 + 1.307 + // Get filters 1.308 + // filters may be null, if we should allow all file types. 1.309 + NSArray *filters = GetFilterList(); 1.310 + 1.311 + // set up default directory 1.312 + NSString *theDir = PanelDefaultDirectory(); 1.313 + 1.314 + // if this is the "Choose application..." dialog, and no other start 1.315 + // dir has been set, then use the Applications folder. 1.316 + if (!theDir) { 1.317 + if (filters && [filters count] == 1 && 1.318 + [(NSString *)[filters objectAtIndex:0] isEqualToString:@"app"]) 1.319 + theDir = @"/Applications/"; 1.320 + else 1.321 + theDir = @""; 1.322 + } 1.323 + 1.324 + if (theDir) { 1.325 + [thePanel setDirectoryURL:[NSURL fileURLWithPath:theDir isDirectory:YES]]; 1.326 + } 1.327 + 1.328 + int result; 1.329 + nsCocoaUtils::PrepareForNativeAppModalDialog(); 1.330 + if (mFilters.Length() > 1) { 1.331 + // [NSURL initWithString:] (below) throws an exception if URLString is nil. 1.332 + 1.333 + NSPopUpButtonObserver* observer = [[NSPopUpButtonObserver alloc] init]; 1.334 + 1.335 + NSView* accessoryView = GetAccessoryView(); 1.336 + [thePanel setAccessoryView:accessoryView]; 1.337 + 1.338 + [observer setPopUpButton:[accessoryView viewWithTag:kSaveTypeControlTag]]; 1.339 + [observer setOpenPanel:thePanel]; 1.340 + [observer setFilePicker:this]; 1.341 + 1.342 + [[NSNotificationCenter defaultCenter] 1.343 + addObserver:observer 1.344 + selector:@selector(menuChangedItem:) 1.345 + name:NSMenuWillSendActionNotification object:nil]; 1.346 + 1.347 + UpdatePanelFileTypes(thePanel, filters); 1.348 + result = [thePanel runModal]; 1.349 + 1.350 + [[NSNotificationCenter defaultCenter] removeObserver:observer]; 1.351 + [observer release]; 1.352 + } else { 1.353 + // If we show all file types, also "expose" bundles' contents. 1.354 + if (!filters) { 1.355 + [thePanel setTreatsFilePackagesAsDirectories:YES]; 1.356 + } 1.357 + [thePanel setAllowedFileTypes:filters]; 1.358 + result = [thePanel runModal]; 1.359 + } 1.360 + nsCocoaUtils::CleanUpAfterNativeAppModalDialog(); 1.361 + 1.362 + if (result == NSFileHandlingPanelCancelButton) 1.363 + return retVal; 1.364 + 1.365 + // Converts data from a NSArray of NSURL to the returned format. 1.366 + // We should be careful to not call [thePanel URLs] more than once given that 1.367 + // it creates a new array each time. 1.368 + // We are using Fast Enumeration, thus the NSURL array is created once then 1.369 + // iterated. 1.370 + for (NSURL* url in [thePanel URLs]) { 1.371 + if (!url) { 1.372 + continue; 1.373 + } 1.374 + 1.375 + nsCOMPtr<nsIFile> localFile; 1.376 + NS_NewLocalFile(EmptyString(), true, getter_AddRefs(localFile)); 1.377 + nsCOMPtr<nsILocalFileMac> macLocalFile = do_QueryInterface(localFile); 1.378 + if (macLocalFile && NS_SUCCEEDED(macLocalFile->InitWithCFURL((CFURLRef)url))) { 1.379 + outFiles.AppendObject(localFile); 1.380 + } 1.381 + } 1.382 + 1.383 + if (outFiles.Count() > 0) 1.384 + retVal = returnOK; 1.385 + 1.386 + return retVal; 1.387 + 1.388 + NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0); 1.389 +} 1.390 + 1.391 +// Use OpenPanel to do a GetFolder. Returns |returnOK| if the user presses OK in the dialog. 1.392 +int16_t 1.393 +nsFilePicker::GetLocalFolder(const nsString& inTitle, nsIFile** outFile) 1.394 +{ 1.395 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; 1.396 + NS_ASSERTION(outFile, "this protected member function expects a null initialized out pointer"); 1.397 + 1.398 + int16_t retVal = (int16_t)returnCancel; 1.399 + NSOpenPanel *thePanel = [NSOpenPanel openPanel]; 1.400 + 1.401 + SetShowHiddenFileState(thePanel); 1.402 + 1.403 + // Set the options for how the get file dialog will appear 1.404 + SetDialogTitle(inTitle, thePanel); 1.405 + [thePanel setAllowsMultipleSelection:NO]; //this is default -probably doesn't need to be set 1.406 + [thePanel setCanSelectHiddenExtension:YES]; 1.407 + [thePanel setCanChooseDirectories:YES]; 1.408 + [thePanel setCanChooseFiles:NO]; 1.409 + [thePanel setResolvesAliases:YES]; //this is default - probably doesn't need to be set 1.410 + [thePanel setCanCreateDirectories:YES]; 1.411 + 1.412 + // packages != folders 1.413 + [thePanel setTreatsFilePackagesAsDirectories:NO]; 1.414 + 1.415 + // set up default directory 1.416 + NSString *theDir = PanelDefaultDirectory(); 1.417 + if (theDir) { 1.418 + [thePanel setDirectoryURL:[NSURL fileURLWithPath:theDir isDirectory:YES]]; 1.419 + } 1.420 + nsCocoaUtils::PrepareForNativeAppModalDialog(); 1.421 + int result = [thePanel runModal]; 1.422 + nsCocoaUtils::CleanUpAfterNativeAppModalDialog(); 1.423 + 1.424 + if (result == NSFileHandlingPanelCancelButton) 1.425 + return retVal; 1.426 + 1.427 + // get the path for the folder (we allow just 1, so that's all we get) 1.428 + NSURL *theURL = [[thePanel URLs] objectAtIndex:0]; 1.429 + if (theURL) { 1.430 + nsCOMPtr<nsIFile> localFile; 1.431 + NS_NewLocalFile(EmptyString(), true, getter_AddRefs(localFile)); 1.432 + nsCOMPtr<nsILocalFileMac> macLocalFile = do_QueryInterface(localFile); 1.433 + if (macLocalFile && NS_SUCCEEDED(macLocalFile->InitWithCFURL((CFURLRef)theURL))) { 1.434 + *outFile = localFile; 1.435 + NS_ADDREF(*outFile); 1.436 + retVal = returnOK; 1.437 + } 1.438 + } 1.439 + 1.440 + return retVal; 1.441 + 1.442 + NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0); 1.443 +} 1.444 + 1.445 +// Returns |returnOK| if the user presses OK in the dialog. 1.446 +int16_t 1.447 +nsFilePicker::PutLocalFile(const nsString& inTitle, const nsString& inDefaultName, nsIFile** outFile) 1.448 +{ 1.449 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; 1.450 + NS_ASSERTION(outFile, "this protected member function expects a null initialized out pointer"); 1.451 + 1.452 + int16_t retVal = returnCancel; 1.453 + NSSavePanel *thePanel = [NSSavePanel savePanel]; 1.454 + 1.455 + SetShowHiddenFileState(thePanel); 1.456 + 1.457 + SetDialogTitle(inTitle, thePanel); 1.458 + 1.459 + // set up accessory view for file format options 1.460 + NSView* accessoryView = GetAccessoryView(); 1.461 + [thePanel setAccessoryView:accessoryView]; 1.462 + 1.463 + // set up default file name 1.464 + NSString* defaultFilename = [NSString stringWithCharacters:(const unichar*)inDefaultName.get() length:inDefaultName.Length()]; 1.465 + 1.466 + // set up default directory 1.467 + NSString *theDir = PanelDefaultDirectory(); 1.468 + if (theDir) { 1.469 + [thePanel setDirectoryURL:[NSURL fileURLWithPath:theDir isDirectory:YES]]; 1.470 + } 1.471 + 1.472 + // load the panel 1.473 + nsCocoaUtils::PrepareForNativeAppModalDialog(); 1.474 + [thePanel setNameFieldStringValue:defaultFilename]; 1.475 + int result = [thePanel runModal]; 1.476 + nsCocoaUtils::CleanUpAfterNativeAppModalDialog(); 1.477 + if (result == NSFileHandlingPanelCancelButton) 1.478 + return retVal; 1.479 + 1.480 + // get the save type 1.481 + NSPopUpButton* popupButton = [accessoryView viewWithTag:kSaveTypeControlTag]; 1.482 + if (popupButton) { 1.483 + mSelectedTypeIndex = [popupButton indexOfSelectedItem]; 1.484 + } 1.485 + 1.486 + NSURL* fileURL = [thePanel URL]; 1.487 + if (fileURL) { 1.488 + nsCOMPtr<nsIFile> localFile; 1.489 + NS_NewLocalFile(EmptyString(), true, getter_AddRefs(localFile)); 1.490 + nsCOMPtr<nsILocalFileMac> macLocalFile = do_QueryInterface(localFile); 1.491 + if (macLocalFile && NS_SUCCEEDED(macLocalFile->InitWithCFURL((CFURLRef)fileURL))) { 1.492 + *outFile = localFile; 1.493 + NS_ADDREF(*outFile); 1.494 + // We tell if we are replacing or not by just looking to see if the file exists. 1.495 + // The user could not have hit OK and not meant to replace the file. 1.496 + if ([[NSFileManager defaultManager] fileExistsAtPath:[fileURL path]]) 1.497 + retVal = returnReplace; 1.498 + else 1.499 + retVal = returnOK; 1.500 + } 1.501 + } 1.502 + 1.503 + return retVal; 1.504 + 1.505 + NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0); 1.506 +} 1.507 + 1.508 +NSArray * 1.509 +nsFilePicker::GetFilterList() 1.510 +{ 1.511 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 1.512 + 1.513 + if (!mFilters.Length()) { 1.514 + return nil; 1.515 + } 1.516 + 1.517 + if (mFilters.Length() <= (uint32_t)mSelectedTypeIndex) { 1.518 + NS_WARNING("An out of range index has been selected. Using the first index instead."); 1.519 + mSelectedTypeIndex = 0; 1.520 + } 1.521 + 1.522 + const nsString& filterWide = mFilters[mSelectedTypeIndex]; 1.523 + if (!filterWide.Length()) { 1.524 + return nil; 1.525 + } 1.526 + 1.527 + if (filterWide.Equals(NS_LITERAL_STRING("*"))) { 1.528 + return nil; 1.529 + } 1.530 + 1.531 + // The extensions in filterWide are in the format "*.ext" but are expected 1.532 + // in the format "ext" by NSOpenPanel. So we need to filter some characters. 1.533 + NSMutableString* filterString = [[[NSMutableString alloc] initWithString: 1.534 + [NSString stringWithCharacters:reinterpret_cast<const unichar*>(filterWide.get()) 1.535 + length:filterWide.Length()]] autorelease]; 1.536 + NSCharacterSet *set = [NSCharacterSet characterSetWithCharactersInString:@". *"]; 1.537 + NSRange range = [filterString rangeOfCharacterFromSet:set]; 1.538 + while (range.length) { 1.539 + [filterString replaceCharactersInRange:range withString:@""]; 1.540 + range = [filterString rangeOfCharacterFromSet:set]; 1.541 + } 1.542 + 1.543 + return [[[NSArray alloc] initWithArray: 1.544 + [filterString componentsSeparatedByString:@";"]] autorelease]; 1.545 + 1.546 + NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 1.547 +} 1.548 + 1.549 +// Sets the dialog title to whatever it should be. If it fails, eh, 1.550 +// the OS will provide a sensible default. 1.551 +void 1.552 +nsFilePicker::SetDialogTitle(const nsString& inTitle, id aPanel) 1.553 +{ 1.554 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK; 1.555 + 1.556 + [aPanel setTitle:[NSString stringWithCharacters:(const unichar*)inTitle.get() length:inTitle.Length()]]; 1.557 + 1.558 + NS_OBJC_END_TRY_ABORT_BLOCK; 1.559 +} 1.560 + 1.561 +// Converts path from an nsIFile into a NSString path 1.562 +// If it fails, returns an empty string. 1.563 +NSString * 1.564 +nsFilePicker::PanelDefaultDirectory() 1.565 +{ 1.566 + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 1.567 + 1.568 + NSString *directory = nil; 1.569 + if (mDisplayDirectory) { 1.570 + nsAutoString pathStr; 1.571 + mDisplayDirectory->GetPath(pathStr); 1.572 + directory = [[[NSString alloc] initWithCharacters:reinterpret_cast<const unichar*>(pathStr.get()) 1.573 + length:pathStr.Length()] autorelease]; 1.574 + } 1.575 + return directory; 1.576 + 1.577 + NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 1.578 +} 1.579 + 1.580 +NS_IMETHODIMP nsFilePicker::GetFile(nsIFile **aFile) 1.581 +{ 1.582 + NS_ENSURE_ARG_POINTER(aFile); 1.583 + *aFile = nullptr; 1.584 + 1.585 + // just return the first file 1.586 + if (mFiles.Count() > 0) { 1.587 + *aFile = mFiles.ObjectAt(0); 1.588 + NS_IF_ADDREF(*aFile); 1.589 + } 1.590 + 1.591 + return NS_OK; 1.592 +} 1.593 + 1.594 +NS_IMETHODIMP nsFilePicker::GetFileURL(nsIURI **aFileURL) 1.595 +{ 1.596 + NS_ENSURE_ARG_POINTER(aFileURL); 1.597 + *aFileURL = nullptr; 1.598 + 1.599 + if (mFiles.Count() == 0) 1.600 + return NS_OK; 1.601 + 1.602 + return NS_NewFileURI(aFileURL, mFiles.ObjectAt(0)); 1.603 +} 1.604 + 1.605 +NS_IMETHODIMP nsFilePicker::GetFiles(nsISimpleEnumerator **aFiles) 1.606 +{ 1.607 + return NS_NewArrayEnumerator(aFiles, mFiles); 1.608 +} 1.609 + 1.610 +NS_IMETHODIMP nsFilePicker::SetDefaultString(const nsAString& aString) 1.611 +{ 1.612 + mDefault = aString; 1.613 + return NS_OK; 1.614 +} 1.615 + 1.616 +NS_IMETHODIMP nsFilePicker::GetDefaultString(nsAString& aString) 1.617 +{ 1.618 + return NS_ERROR_FAILURE; 1.619 +} 1.620 + 1.621 +// The default extension to use for files 1.622 +NS_IMETHODIMP nsFilePicker::GetDefaultExtension(nsAString& aExtension) 1.623 +{ 1.624 + aExtension.Truncate(); 1.625 + return NS_OK; 1.626 +} 1.627 + 1.628 +NS_IMETHODIMP nsFilePicker::SetDefaultExtension(const nsAString& aExtension) 1.629 +{ 1.630 + return NS_OK; 1.631 +} 1.632 + 1.633 +// Append an entry to the filters array 1.634 +NS_IMETHODIMP 1.635 +nsFilePicker::AppendFilter(const nsAString& aTitle, const nsAString& aFilter) 1.636 +{ 1.637 + // "..apps" has to be translated with native executable extensions. 1.638 + if (aFilter.EqualsLiteral("..apps")) { 1.639 + mFilters.AppendElement(NS_LITERAL_STRING("*.app")); 1.640 + } else { 1.641 + mFilters.AppendElement(aFilter); 1.642 + } 1.643 + mTitles.AppendElement(aTitle); 1.644 + 1.645 + return NS_OK; 1.646 +} 1.647 + 1.648 +// Get the filter index - do we still need this? 1.649 +NS_IMETHODIMP nsFilePicker::GetFilterIndex(int32_t *aFilterIndex) 1.650 +{ 1.651 + *aFilterIndex = mSelectedTypeIndex; 1.652 + return NS_OK; 1.653 +} 1.654 + 1.655 +// Set the filter index - do we still need this? 1.656 +NS_IMETHODIMP nsFilePicker::SetFilterIndex(int32_t aFilterIndex) 1.657 +{ 1.658 + mSelectedTypeIndex = aFilterIndex; 1.659 + return NS_OK; 1.660 +}