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