michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #import michael@0: michael@0: #include "nsFilePicker.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsReadableUtils.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsIComponentManager.h" michael@0: #include "nsIFile.h" michael@0: #include "nsILocalFileMac.h" michael@0: #include "nsIURL.h" michael@0: #include "nsArrayEnumerator.h" michael@0: #include "nsIStringBundle.h" michael@0: #include "nsCocoaFeatures.h" michael@0: #include "nsCocoaUtils.h" michael@0: #include "mozilla/Preferences.h" michael@0: michael@0: // This must be included last: michael@0: #include "nsObjCExceptions.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: const float kAccessoryViewPadding = 5; michael@0: const int kSaveTypeControlTag = 1; michael@0: michael@0: static bool gCallSecretHiddenFileAPI = false; michael@0: const char kShowHiddenFilesPref[] = "filepicker.showHiddenFiles"; michael@0: michael@0: /** michael@0: * This class is an observer of NSPopUpButton selection change. michael@0: */ michael@0: @interface NSPopUpButtonObserver : NSObject michael@0: { michael@0: NSPopUpButton* mPopUpButton; michael@0: NSOpenPanel* mOpenPanel; michael@0: nsFilePicker* mFilePicker; michael@0: } michael@0: - (void) setPopUpButton:(NSPopUpButton*)aPopUpButton; michael@0: - (void) setOpenPanel:(NSOpenPanel*)aOpenPanel; michael@0: - (void) setFilePicker:(nsFilePicker*)aFilePicker; michael@0: - (void) menuChangedItem:(NSNotification*)aSender; michael@0: @end michael@0: michael@0: NS_IMPL_ISUPPORTS(nsFilePicker, nsIFilePicker) michael@0: michael@0: // We never want to call the secret show hidden files API unless the pref michael@0: // has been set. Once the pref has been set we always need to call it even michael@0: // if it disappears so that we stop showing hidden files if a user deletes michael@0: // the pref. If the secret API was used once and things worked out it should michael@0: // continue working for subsequent calls so the user is at no more risk. michael@0: static void SetShowHiddenFileState(NSSavePanel* panel) michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK; michael@0: michael@0: bool show = false; michael@0: if (NS_SUCCEEDED(Preferences::GetBool(kShowHiddenFilesPref, &show))) { michael@0: gCallSecretHiddenFileAPI = true; michael@0: } michael@0: michael@0: if (gCallSecretHiddenFileAPI) { michael@0: // invoke a method to get a Cocoa-internal nav view michael@0: SEL navViewSelector = @selector(_navView); michael@0: NSMethodSignature* navViewSignature = [panel methodSignatureForSelector:navViewSelector]; michael@0: if (!navViewSignature) michael@0: return; michael@0: NSInvocation* navViewInvocation = [NSInvocation invocationWithMethodSignature:navViewSignature]; michael@0: [navViewInvocation setSelector:navViewSelector]; michael@0: [navViewInvocation setTarget:panel]; michael@0: [navViewInvocation invoke]; michael@0: michael@0: // get the returned nav view michael@0: id navView = nil; michael@0: [navViewInvocation getReturnValue:&navView]; michael@0: michael@0: // invoke the secret show hidden file state method on the nav view michael@0: SEL showHiddenFilesSelector = @selector(setShowsHiddenFiles:); michael@0: NSMethodSignature* showHiddenFilesSignature = [navView methodSignatureForSelector:showHiddenFilesSelector]; michael@0: if (!showHiddenFilesSignature) michael@0: return; michael@0: NSInvocation* showHiddenFilesInvocation = [NSInvocation invocationWithMethodSignature:showHiddenFilesSignature]; michael@0: [showHiddenFilesInvocation setSelector:showHiddenFilesSelector]; michael@0: [showHiddenFilesInvocation setTarget:navView]; michael@0: [showHiddenFilesInvocation setArgument:&show atIndex:2]; michael@0: [showHiddenFilesInvocation invoke]; michael@0: } michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK; michael@0: } michael@0: michael@0: nsFilePicker::nsFilePicker() michael@0: : mSelectedTypeIndex(0) michael@0: { michael@0: } michael@0: michael@0: nsFilePicker::~nsFilePicker() michael@0: { michael@0: } michael@0: michael@0: void michael@0: nsFilePicker::InitNative(nsIWidget *aParent, const nsAString& aTitle) michael@0: { michael@0: mTitle = aTitle; michael@0: } michael@0: michael@0: NSView* nsFilePicker::GetAccessoryView() michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; michael@0: michael@0: NSView* accessoryView = [[[NSView alloc] initWithFrame:NSMakeRect(0, 0, 0, 0)] autorelease]; michael@0: michael@0: // Set a label's default value. michael@0: NSString* label = @"Format:"; michael@0: michael@0: // Try to get the localized string. michael@0: nsCOMPtr sbs = do_GetService(NS_STRINGBUNDLE_CONTRACTID); michael@0: nsCOMPtr bundle; michael@0: nsresult rv = sbs->CreateBundle("chrome://global/locale/filepicker.properties", getter_AddRefs(bundle)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: nsXPIDLString locaLabel; michael@0: bundle->GetStringFromName(NS_LITERAL_STRING("formatLabel").get(), michael@0: getter_Copies(locaLabel)); michael@0: if (locaLabel) { michael@0: label = [NSString stringWithCharacters:reinterpret_cast(locaLabel.get()) michael@0: length:locaLabel.Length()]; michael@0: } michael@0: } michael@0: michael@0: // set up label text field michael@0: NSTextField* textField = [[[NSTextField alloc] init] autorelease]; michael@0: [textField setEditable:NO]; michael@0: [textField setSelectable:NO]; michael@0: [textField setDrawsBackground:NO]; michael@0: [textField setBezeled:NO]; michael@0: [textField setBordered:NO]; michael@0: [textField setFont:[NSFont labelFontOfSize:13.0]]; michael@0: [textField setStringValue:label]; michael@0: [textField setTag:0]; michael@0: [textField sizeToFit]; michael@0: michael@0: // set up popup button michael@0: NSPopUpButton* popupButton = [[[NSPopUpButton alloc] initWithFrame:NSMakeRect(0, 0, 0, 0) pullsDown:NO] autorelease]; michael@0: uint32_t numMenuItems = mTitles.Length(); michael@0: for (uint32_t i = 0; i < numMenuItems; i++) { michael@0: const nsString& currentTitle = mTitles[i]; michael@0: NSString *titleString; michael@0: if (currentTitle.IsEmpty()) { michael@0: const nsString& currentFilter = mFilters[i]; michael@0: titleString = [[NSString alloc] initWithCharacters:reinterpret_cast(currentFilter.get()) michael@0: length:currentFilter.Length()]; michael@0: } michael@0: else { michael@0: titleString = [[NSString alloc] initWithCharacters:reinterpret_cast(currentTitle.get()) michael@0: length:currentTitle.Length()]; michael@0: } michael@0: [popupButton addItemWithTitle:titleString]; michael@0: [titleString release]; michael@0: } michael@0: if (mSelectedTypeIndex >= 0 && (uint32_t)mSelectedTypeIndex < numMenuItems) michael@0: [popupButton selectItemAtIndex:mSelectedTypeIndex]; michael@0: [popupButton setTag:kSaveTypeControlTag]; michael@0: [popupButton sizeToFit]; // we have to do sizeToFit to get the height calculated for us michael@0: // This is just a default width that works well, doesn't truncate the vast majority of michael@0: // things that might end up in the menu. michael@0: [popupButton setFrameSize:NSMakeSize(180, [popupButton frame].size.height)]; michael@0: michael@0: // position everything based on control sizes with kAccessoryViewPadding pix padding michael@0: // on each side kAccessoryViewPadding pix horizontal padding between controls michael@0: float greatestHeight = [textField frame].size.height; michael@0: if ([popupButton frame].size.height > greatestHeight) michael@0: greatestHeight = [popupButton frame].size.height; michael@0: float totalViewHeight = greatestHeight + kAccessoryViewPadding * 2; michael@0: float totalViewWidth = [textField frame].size.width + [popupButton frame].size.width + kAccessoryViewPadding * 3; michael@0: [accessoryView setFrameSize:NSMakeSize(totalViewWidth, totalViewHeight)]; michael@0: michael@0: float textFieldOriginY = ((greatestHeight - [textField frame].size.height) / 2 + 1) + kAccessoryViewPadding; michael@0: [textField setFrameOrigin:NSMakePoint(kAccessoryViewPadding, textFieldOriginY)]; michael@0: michael@0: float popupOriginX = [textField frame].size.width + kAccessoryViewPadding * 2; michael@0: float popupOriginY = ((greatestHeight - [popupButton frame].size.height) / 2) + kAccessoryViewPadding; michael@0: [popupButton setFrameOrigin:NSMakePoint(popupOriginX, popupOriginY)]; michael@0: michael@0: [accessoryView addSubview:textField]; michael@0: [accessoryView addSubview:popupButton]; michael@0: return accessoryView; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_NIL; michael@0: } michael@0: michael@0: // Display the file dialog michael@0: NS_IMETHODIMP nsFilePicker::Show(int16_t *retval) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(retval); michael@0: michael@0: *retval = returnCancel; michael@0: michael@0: int16_t userClicksOK = returnCancel; michael@0: michael@0: // Random questions from DHH: michael@0: // michael@0: // Why do we pass mTitle, mDefault to the functions? Can GetLocalFile. PutLocalFile, michael@0: // and GetLocalFolder get called someplace else? It generates a bunch of warnings michael@0: // as it is right now. michael@0: // michael@0: // I think we could easily combine GetLocalFile and GetLocalFolder together, just michael@0: // setting panel pick options based on mMode. I didn't do it here b/c I wanted to michael@0: // make this look as much like Carbon nsFilePicker as possible. michael@0: michael@0: mFiles.Clear(); michael@0: nsCOMPtr theFile; michael@0: michael@0: switch (mMode) michael@0: { michael@0: case modeOpen: michael@0: userClicksOK = GetLocalFiles(mTitle, false, mFiles); michael@0: break; michael@0: michael@0: case modeOpenMultiple: michael@0: userClicksOK = GetLocalFiles(mTitle, true, mFiles); michael@0: break; michael@0: michael@0: case modeSave: michael@0: userClicksOK = PutLocalFile(mTitle, mDefault, getter_AddRefs(theFile)); michael@0: break; michael@0: michael@0: case modeGetFolder: michael@0: userClicksOK = GetLocalFolder(mTitle, getter_AddRefs(theFile)); michael@0: break; michael@0: michael@0: default: michael@0: NS_ERROR("Unknown file picker mode"); michael@0: break; michael@0: } michael@0: michael@0: if (theFile) michael@0: mFiles.AppendObject(theFile); michael@0: michael@0: *retval = userClicksOK; michael@0: return NS_OK; michael@0: } michael@0: michael@0: static michael@0: void UpdatePanelFileTypes(NSOpenPanel* aPanel, NSArray* aFilters) michael@0: { michael@0: // If we show all file types, also "expose" bundles' contents. michael@0: [aPanel setTreatsFilePackagesAsDirectories:!aFilters]; michael@0: michael@0: [aPanel setAllowedFileTypes:aFilters]; michael@0: } michael@0: michael@0: @implementation NSPopUpButtonObserver michael@0: - (void) setPopUpButton:(NSPopUpButton*)aPopUpButton michael@0: { michael@0: mPopUpButton = aPopUpButton; michael@0: } michael@0: michael@0: - (void) setOpenPanel:(NSOpenPanel*)aOpenPanel michael@0: { michael@0: mOpenPanel = aOpenPanel; michael@0: } michael@0: michael@0: - (void) setFilePicker:(nsFilePicker*)aFilePicker michael@0: { michael@0: mFilePicker = aFilePicker; michael@0: } michael@0: michael@0: - (void) menuChangedItem:(NSNotification *)aSender michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; michael@0: int32_t selectedItem = [mPopUpButton indexOfSelectedItem]; michael@0: if (selectedItem < 0) { michael@0: return; michael@0: } michael@0: michael@0: mFilePicker->SetFilterIndex(selectedItem); michael@0: UpdatePanelFileTypes(mOpenPanel, mFilePicker->GetFilterList()); michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(); michael@0: } michael@0: @end michael@0: michael@0: // Use OpenPanel to do a GetFile. Returns |returnOK| if the user presses OK in the dialog. michael@0: int16_t michael@0: nsFilePicker::GetLocalFiles(const nsString& inTitle, bool inAllowMultiple, nsCOMArray& outFiles) michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; michael@0: michael@0: int16_t retVal = (int16_t)returnCancel; michael@0: NSOpenPanel *thePanel = [NSOpenPanel openPanel]; michael@0: michael@0: SetShowHiddenFileState(thePanel); michael@0: michael@0: // Set the options for how the get file dialog will appear michael@0: SetDialogTitle(inTitle, thePanel); michael@0: [thePanel setAllowsMultipleSelection:inAllowMultiple]; michael@0: [thePanel setCanSelectHiddenExtension:YES]; michael@0: [thePanel setCanChooseDirectories:NO]; michael@0: [thePanel setCanChooseFiles:YES]; michael@0: [thePanel setResolvesAliases:YES]; //this is default - probably doesn't need to be set michael@0: michael@0: // Get filters michael@0: // filters may be null, if we should allow all file types. michael@0: NSArray *filters = GetFilterList(); michael@0: michael@0: // set up default directory michael@0: NSString *theDir = PanelDefaultDirectory(); michael@0: michael@0: // if this is the "Choose application..." dialog, and no other start michael@0: // dir has been set, then use the Applications folder. michael@0: if (!theDir) { michael@0: if (filters && [filters count] == 1 && michael@0: [(NSString *)[filters objectAtIndex:0] isEqualToString:@"app"]) michael@0: theDir = @"/Applications/"; michael@0: else michael@0: theDir = @""; michael@0: } michael@0: michael@0: if (theDir) { michael@0: [thePanel setDirectoryURL:[NSURL fileURLWithPath:theDir isDirectory:YES]]; michael@0: } michael@0: michael@0: int result; michael@0: nsCocoaUtils::PrepareForNativeAppModalDialog(); michael@0: if (mFilters.Length() > 1) { michael@0: // [NSURL initWithString:] (below) throws an exception if URLString is nil. michael@0: michael@0: NSPopUpButtonObserver* observer = [[NSPopUpButtonObserver alloc] init]; michael@0: michael@0: NSView* accessoryView = GetAccessoryView(); michael@0: [thePanel setAccessoryView:accessoryView]; michael@0: michael@0: [observer setPopUpButton:[accessoryView viewWithTag:kSaveTypeControlTag]]; michael@0: [observer setOpenPanel:thePanel]; michael@0: [observer setFilePicker:this]; michael@0: michael@0: [[NSNotificationCenter defaultCenter] michael@0: addObserver:observer michael@0: selector:@selector(menuChangedItem:) michael@0: name:NSMenuWillSendActionNotification object:nil]; michael@0: michael@0: UpdatePanelFileTypes(thePanel, filters); michael@0: result = [thePanel runModal]; michael@0: michael@0: [[NSNotificationCenter defaultCenter] removeObserver:observer]; michael@0: [observer release]; michael@0: } else { michael@0: // If we show all file types, also "expose" bundles' contents. michael@0: if (!filters) { michael@0: [thePanel setTreatsFilePackagesAsDirectories:YES]; michael@0: } michael@0: [thePanel setAllowedFileTypes:filters]; michael@0: result = [thePanel runModal]; michael@0: } michael@0: nsCocoaUtils::CleanUpAfterNativeAppModalDialog(); michael@0: michael@0: if (result == NSFileHandlingPanelCancelButton) michael@0: return retVal; michael@0: michael@0: // Converts data from a NSArray of NSURL to the returned format. michael@0: // We should be careful to not call [thePanel URLs] more than once given that michael@0: // it creates a new array each time. michael@0: // We are using Fast Enumeration, thus the NSURL array is created once then michael@0: // iterated. michael@0: for (NSURL* url in [thePanel URLs]) { michael@0: if (!url) { michael@0: continue; michael@0: } michael@0: michael@0: nsCOMPtr localFile; michael@0: NS_NewLocalFile(EmptyString(), true, getter_AddRefs(localFile)); michael@0: nsCOMPtr macLocalFile = do_QueryInterface(localFile); michael@0: if (macLocalFile && NS_SUCCEEDED(macLocalFile->InitWithCFURL((CFURLRef)url))) { michael@0: outFiles.AppendObject(localFile); michael@0: } michael@0: } michael@0: michael@0: if (outFiles.Count() > 0) michael@0: retVal = returnOK; michael@0: michael@0: return retVal; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0); michael@0: } michael@0: michael@0: // Use OpenPanel to do a GetFolder. Returns |returnOK| if the user presses OK in the dialog. michael@0: int16_t michael@0: nsFilePicker::GetLocalFolder(const nsString& inTitle, nsIFile** outFile) michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; michael@0: NS_ASSERTION(outFile, "this protected member function expects a null initialized out pointer"); michael@0: michael@0: int16_t retVal = (int16_t)returnCancel; michael@0: NSOpenPanel *thePanel = [NSOpenPanel openPanel]; michael@0: michael@0: SetShowHiddenFileState(thePanel); michael@0: michael@0: // Set the options for how the get file dialog will appear michael@0: SetDialogTitle(inTitle, thePanel); michael@0: [thePanel setAllowsMultipleSelection:NO]; //this is default -probably doesn't need to be set michael@0: [thePanel setCanSelectHiddenExtension:YES]; michael@0: [thePanel setCanChooseDirectories:YES]; michael@0: [thePanel setCanChooseFiles:NO]; michael@0: [thePanel setResolvesAliases:YES]; //this is default - probably doesn't need to be set michael@0: [thePanel setCanCreateDirectories:YES]; michael@0: michael@0: // packages != folders michael@0: [thePanel setTreatsFilePackagesAsDirectories:NO]; michael@0: michael@0: // set up default directory michael@0: NSString *theDir = PanelDefaultDirectory(); michael@0: if (theDir) { michael@0: [thePanel setDirectoryURL:[NSURL fileURLWithPath:theDir isDirectory:YES]]; michael@0: } michael@0: nsCocoaUtils::PrepareForNativeAppModalDialog(); michael@0: int result = [thePanel runModal]; michael@0: nsCocoaUtils::CleanUpAfterNativeAppModalDialog(); michael@0: michael@0: if (result == NSFileHandlingPanelCancelButton) michael@0: return retVal; michael@0: michael@0: // get the path for the folder (we allow just 1, so that's all we get) michael@0: NSURL *theURL = [[thePanel URLs] objectAtIndex:0]; michael@0: if (theURL) { michael@0: nsCOMPtr localFile; michael@0: NS_NewLocalFile(EmptyString(), true, getter_AddRefs(localFile)); michael@0: nsCOMPtr macLocalFile = do_QueryInterface(localFile); michael@0: if (macLocalFile && NS_SUCCEEDED(macLocalFile->InitWithCFURL((CFURLRef)theURL))) { michael@0: *outFile = localFile; michael@0: NS_ADDREF(*outFile); michael@0: retVal = returnOK; michael@0: } michael@0: } michael@0: michael@0: return retVal; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0); michael@0: } michael@0: michael@0: // Returns |returnOK| if the user presses OK in the dialog. michael@0: int16_t michael@0: nsFilePicker::PutLocalFile(const nsString& inTitle, const nsString& inDefaultName, nsIFile** outFile) michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; michael@0: NS_ASSERTION(outFile, "this protected member function expects a null initialized out pointer"); michael@0: michael@0: int16_t retVal = returnCancel; michael@0: NSSavePanel *thePanel = [NSSavePanel savePanel]; michael@0: michael@0: SetShowHiddenFileState(thePanel); michael@0: michael@0: SetDialogTitle(inTitle, thePanel); michael@0: michael@0: // set up accessory view for file format options michael@0: NSView* accessoryView = GetAccessoryView(); michael@0: [thePanel setAccessoryView:accessoryView]; michael@0: michael@0: // set up default file name michael@0: NSString* defaultFilename = [NSString stringWithCharacters:(const unichar*)inDefaultName.get() length:inDefaultName.Length()]; michael@0: michael@0: // set up default directory michael@0: NSString *theDir = PanelDefaultDirectory(); michael@0: if (theDir) { michael@0: [thePanel setDirectoryURL:[NSURL fileURLWithPath:theDir isDirectory:YES]]; michael@0: } michael@0: michael@0: // load the panel michael@0: nsCocoaUtils::PrepareForNativeAppModalDialog(); michael@0: [thePanel setNameFieldStringValue:defaultFilename]; michael@0: int result = [thePanel runModal]; michael@0: nsCocoaUtils::CleanUpAfterNativeAppModalDialog(); michael@0: if (result == NSFileHandlingPanelCancelButton) michael@0: return retVal; michael@0: michael@0: // get the save type michael@0: NSPopUpButton* popupButton = [accessoryView viewWithTag:kSaveTypeControlTag]; michael@0: if (popupButton) { michael@0: mSelectedTypeIndex = [popupButton indexOfSelectedItem]; michael@0: } michael@0: michael@0: NSURL* fileURL = [thePanel URL]; michael@0: if (fileURL) { michael@0: nsCOMPtr localFile; michael@0: NS_NewLocalFile(EmptyString(), true, getter_AddRefs(localFile)); michael@0: nsCOMPtr macLocalFile = do_QueryInterface(localFile); michael@0: if (macLocalFile && NS_SUCCEEDED(macLocalFile->InitWithCFURL((CFURLRef)fileURL))) { michael@0: *outFile = localFile; michael@0: NS_ADDREF(*outFile); michael@0: // We tell if we are replacing or not by just looking to see if the file exists. michael@0: // The user could not have hit OK and not meant to replace the file. michael@0: if ([[NSFileManager defaultManager] fileExistsAtPath:[fileURL path]]) michael@0: retVal = returnReplace; michael@0: else michael@0: retVal = returnOK; michael@0: } michael@0: } michael@0: michael@0: return retVal; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0); michael@0: } michael@0: michael@0: NSArray * michael@0: nsFilePicker::GetFilterList() michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; michael@0: michael@0: if (!mFilters.Length()) { michael@0: return nil; michael@0: } michael@0: michael@0: if (mFilters.Length() <= (uint32_t)mSelectedTypeIndex) { michael@0: NS_WARNING("An out of range index has been selected. Using the first index instead."); michael@0: mSelectedTypeIndex = 0; michael@0: } michael@0: michael@0: const nsString& filterWide = mFilters[mSelectedTypeIndex]; michael@0: if (!filterWide.Length()) { michael@0: return nil; michael@0: } michael@0: michael@0: if (filterWide.Equals(NS_LITERAL_STRING("*"))) { michael@0: return nil; michael@0: } michael@0: michael@0: // The extensions in filterWide are in the format "*.ext" but are expected michael@0: // in the format "ext" by NSOpenPanel. So we need to filter some characters. michael@0: NSMutableString* filterString = [[[NSMutableString alloc] initWithString: michael@0: [NSString stringWithCharacters:reinterpret_cast(filterWide.get()) michael@0: length:filterWide.Length()]] autorelease]; michael@0: NSCharacterSet *set = [NSCharacterSet characterSetWithCharactersInString:@". *"]; michael@0: NSRange range = [filterString rangeOfCharacterFromSet:set]; michael@0: while (range.length) { michael@0: [filterString replaceCharactersInRange:range withString:@""]; michael@0: range = [filterString rangeOfCharacterFromSet:set]; michael@0: } michael@0: michael@0: return [[[NSArray alloc] initWithArray: michael@0: [filterString componentsSeparatedByString:@";"]] autorelease]; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_NIL; michael@0: } michael@0: michael@0: // Sets the dialog title to whatever it should be. If it fails, eh, michael@0: // the OS will provide a sensible default. michael@0: void michael@0: nsFilePicker::SetDialogTitle(const nsString& inTitle, id aPanel) michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK; michael@0: michael@0: [aPanel setTitle:[NSString stringWithCharacters:(const unichar*)inTitle.get() length:inTitle.Length()]]; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK; michael@0: } michael@0: michael@0: // Converts path from an nsIFile into a NSString path michael@0: // If it fails, returns an empty string. michael@0: NSString * michael@0: nsFilePicker::PanelDefaultDirectory() michael@0: { michael@0: NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; michael@0: michael@0: NSString *directory = nil; michael@0: if (mDisplayDirectory) { michael@0: nsAutoString pathStr; michael@0: mDisplayDirectory->GetPath(pathStr); michael@0: directory = [[[NSString alloc] initWithCharacters:reinterpret_cast(pathStr.get()) michael@0: length:pathStr.Length()] autorelease]; michael@0: } michael@0: return directory; michael@0: michael@0: NS_OBJC_END_TRY_ABORT_BLOCK_NIL; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsFilePicker::GetFile(nsIFile **aFile) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aFile); michael@0: *aFile = nullptr; michael@0: michael@0: // just return the first file michael@0: if (mFiles.Count() > 0) { michael@0: *aFile = mFiles.ObjectAt(0); michael@0: NS_IF_ADDREF(*aFile); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsFilePicker::GetFileURL(nsIURI **aFileURL) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aFileURL); michael@0: *aFileURL = nullptr; michael@0: michael@0: if (mFiles.Count() == 0) michael@0: return NS_OK; michael@0: michael@0: return NS_NewFileURI(aFileURL, mFiles.ObjectAt(0)); michael@0: } michael@0: michael@0: NS_IMETHODIMP nsFilePicker::GetFiles(nsISimpleEnumerator **aFiles) michael@0: { michael@0: return NS_NewArrayEnumerator(aFiles, mFiles); michael@0: } michael@0: michael@0: NS_IMETHODIMP nsFilePicker::SetDefaultString(const nsAString& aString) michael@0: { michael@0: mDefault = aString; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsFilePicker::GetDefaultString(nsAString& aString) michael@0: { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // The default extension to use for files michael@0: NS_IMETHODIMP nsFilePicker::GetDefaultExtension(nsAString& aExtension) michael@0: { michael@0: aExtension.Truncate(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsFilePicker::SetDefaultExtension(const nsAString& aExtension) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Append an entry to the filters array michael@0: NS_IMETHODIMP michael@0: nsFilePicker::AppendFilter(const nsAString& aTitle, const nsAString& aFilter) michael@0: { michael@0: // "..apps" has to be translated with native executable extensions. michael@0: if (aFilter.EqualsLiteral("..apps")) { michael@0: mFilters.AppendElement(NS_LITERAL_STRING("*.app")); michael@0: } else { michael@0: mFilters.AppendElement(aFilter); michael@0: } michael@0: mTitles.AppendElement(aTitle); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Get the filter index - do we still need this? michael@0: NS_IMETHODIMP nsFilePicker::GetFilterIndex(int32_t *aFilterIndex) michael@0: { michael@0: *aFilterIndex = mSelectedTypeIndex; michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Set the filter index - do we still need this? michael@0: NS_IMETHODIMP nsFilePicker::SetFilterIndex(int32_t aFilterIndex) michael@0: { michael@0: mSelectedTypeIndex = aFilterIndex; michael@0: return NS_OK; michael@0: }