Files
sousa-gecko/widget/cocoa/nsFilePicker.mm
T
Emilio Cobos Álvarez 2861287f6d Bug 2054311 - Sort headers in widget/. r=stransky,win-reviewers,geckoview-reviewers,gstoll,owlish
Autogenerated with:

    cp dom/.clang-format widget/ && mach format widget/**.{cpp,h,mm}

Manual changes:

 * Had to add a manual override in nsWaylandDisplay.h /
   DMABufFormats.cpp to include mozwayland before protocols.
 * Missing includes in nsWindowWayland/X11.h / nsDragService.
 * nsFilePicker missed an nsWindow forward declaration.
 * Various overrides in widget/windows because windows headers are order
   dependent.

Differential Revision: https://phabricator.services.mozilla.com/D311688
2026-07-14 16:17:37 +00:00

746 lines
24 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* 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 "mozilla/Preferences.h"
#include "nsArrayEnumerator.h"
#include "nsCOMPtr.h"
#include "nsCocoaUtils.h"
#include "nsFilePicker.h"
#include "nsIFile.h"
#include "nsILocalFileMac.h"
#include "nsIStringBundle.h"
#include "nsNetUtil.h"
#include "nsReadableUtils.h"
#include "nsThreadUtils.h"
// This must be included last:
#include "nsObjCExceptions.h"
using namespace mozilla;
const float kAccessoryViewPadding = 5;
const int kSaveTypeControlTag = 1;
const char kShowHiddenFilesPref[] = "filepicker.showHiddenFiles";
// This class is an observer of NSPopUpButton selection change.
@interface MOZFilePickerPopUpObserver : NSObject {
NSPopUpButton* mPopUpButton;
NSOpenPanel* mOpenPanel;
RefPtr<nsFilePicker> mFilePicker;
}
- (void)setPopUpButton:(NSPopUpButton*)aPopUpButton;
- (void)setOpenPanel:(NSOpenPanel*)aOpenPanel;
- (void)setFilePicker:(nsFilePicker*)aFilePicker;
- (void)menuChangedItem:(NSNotification*)aSender;
@end
@interface MOZSaveFilePickerPopUpObserver : NSObject {
NSPopUpButton* mPopUpButton;
NSSavePanel* mSavePanel;
RefPtr<nsFilePicker> mFilePicker;
}
- (void)setPopUpButton:(NSPopUpButton*)aPopUpButton;
- (void)setSavePanel:(NSSavePanel*)aSavePanel;
- (void)setFilePicker:(nsFilePicker*)aFilePicker;
- (void)menuChangedItem:(NSNotification*)aSender;
@end
NS_IMPL_ISUPPORTS(nsFilePicker, nsIFilePicker)
static void SetShowHiddenFileState(NSSavePanel* panel) {
NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
bool show = false;
if (NS_SUCCEEDED(Preferences::GetBool(kShowHiddenFilesPref, &show))) {
[panel setShowsHiddenFiles:show];
}
NS_OBJC_END_TRY_IGNORE_BLOCK;
}
// On macOS 26 (Tahoe), the panel's sheet completion handler can run before the
// modal session has fully unwound. Invoking the callback synchronously here
// would let a consumer open another modal on top of a session that is still
// tearing down, which hangs (bug 2053177). Deferring to a fresh main-thread
// turn lets the modal finish unwinding before the callback runs.
static void InvokeFilePickerCallbackDeferred(
nsIFilePickerShownCallback* aCallback, nsIFilePicker::ResultCode aResult) {
if (!aCallback) {
return;
}
nsCOMPtr<nsIFilePickerShownCallback> callback = aCallback;
NS_DispatchToMainThread(NS_NewRunnableFunction(
"nsFilePicker::InvokeCallback",
[callback, aResult]() { callback->Done(aResult); }));
}
nsFilePicker::nsFilePicker() = default;
nsFilePicker::~nsFilePicker() = default;
void nsFilePicker::InitNative(nsIWidget* aParent, const nsAString& aTitle) {
mParentWidget = aParent;
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 localizedLabel;
rv = bundle->GetStringFromName("formatLabel", localizedLabel);
if (NS_SUCCEEDED(rv)) {
label = [NSString stringWithCharacters:reinterpret_cast<const unichar*>(
localizedLabel.get())
length:localizedLabel.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 &&
static_cast<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);
}
NS_IMETHODIMP
nsFilePicker::Open(nsIFilePickerShownCallback* aCallback) {
if (MaybeBlockFilePicker(aCallback)) {
return NS_OK;
}
RefPtr<nsFilePicker> self = this;
nsCOMPtr<nsIFilePickerShownCallback> callback = aCallback;
return NS_DispatchToMainThread(
NS_NewRunnableFunction("nsFilePicker::Open", [self, callback]() mutable {
switch (self->mMode) {
case modeOpen:
self->PresentOpenPanel(false, callback);
break;
case modeOpenMultiple:
self->PresentOpenPanel(true, callback);
break;
case modeSave:
self->PresentSavePanel(callback);
break;
case modeGetFolder:
self->PresentFolderPanel(callback);
break;
default:
NS_ERROR("Unknown file picker mode");
if (callback) {
callback->Done(nsIFilePicker::returnCancel);
}
break;
}
}));
}
void nsFilePicker::BeginPanelAsync(NSSavePanel* aPanel,
void (^aHandler)(NSModalResponse)) {
NSWindow* parentWindow = nil;
if (mParentWidget) {
parentWindow =
static_cast<NSWindow*>(mParentWidget->GetNativeData(NS_NATIVE_WINDOW));
}
if (parentWindow) {
[aPanel beginSheetModalForWindow:parentWindow completionHandler:aHandler];
} else {
[aPanel beginWithCompletionHandler:aHandler];
}
}
static void UpdatePanelFileTypes(NSOpenPanel* aPanel, NSArray* aFilters) {
// If we show all file types, also "expose" bundles' contents.
[aPanel setTreatsFilePackagesAsDirectories:!aFilters];
[aPanel setAllowedFileTypes:aFilters];
}
@implementation MOZFilePickerPopUpObserver
- (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_IGNORE_BLOCK;
int32_t selectedItem = [mPopUpButton indexOfSelectedItem];
if (selectedItem < 0) {
return;
}
mFilePicker->SetFilterIndex(selectedItem);
UpdatePanelFileTypes(mOpenPanel, mFilePicker->GetFilterList());
NS_OBJC_END_TRY_IGNORE_BLOCK;
}
@end
@implementation MOZSaveFilePickerPopUpObserver
- (void)setPopUpButton:(NSPopUpButton*)aPopUpButton {
mPopUpButton = aPopUpButton;
}
- (void)setSavePanel:(NSSavePanel*)aSavePanel {
mSavePanel = aSavePanel;
}
- (void)setFilePicker:(nsFilePicker*)aFilePicker {
mFilePicker = aFilePicker;
}
- (void)menuChangedItem:(NSNotification*)aSender {
NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
int32_t selectedItem = [mPopUpButton indexOfSelectedItem];
if (selectedItem < 0) {
return;
}
mFilePicker->SetFilterIndex(selectedItem);
NSArray* filterList = mFilePicker->GetFilterList();
if (filterList && [filterList count] > 0) {
NSString* newExtension = [filterList objectAtIndex:0];
NSString* currentName = [mSavePanel nameFieldStringValue];
NSString* baseName = [currentName stringByDeletingPathExtension];
if (baseName.length > 0) {
[mSavePanel setNameFieldStringValue:
[baseName stringByAppendingPathExtension:newExtension]];
// Keep the panel's allowed type in sync with the name field so the new
// extension is preserved on the saved file.
mSavePanel.allowedFileTypes = @[ newExtension ];
}
}
// For the "All Files" filter GetFilterList returns nil; we leave the name
// field and the allowed type untouched so the current extension is kept.
NS_OBJC_END_TRY_IGNORE_BLOCK;
}
@end
void nsFilePicker::PresentOpenPanel(bool aAllowMultiple,
nsIFilePickerShownCallback* aCallback) {
NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
MOZ_ASSERT(NS_IsMainThread());
mFiles.Clear();
NSOpenPanel* thePanel = [NSOpenPanel openPanel];
SetShowHiddenFileState(thePanel);
// Set the options for how the get file dialog will appear
SetDialogTitle(mTitle, thePanel);
[thePanel setAllowsMultipleSelection:aAllowMultiple];
[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();
NSString* theDir = PanelDefaultDirectory();
// If no other dir was set and this is the "Choose application..." dialog
// then use the Applications folder.
if (!theDir && filters && [filters count] == 1 &&
[static_cast<NSString*>([filters objectAtIndex:0])
isEqualToString:@"app"]) {
theDir = @"/Applications/";
}
if (theDir) {
[thePanel setDirectoryURL:[NSURL fileURLWithPath:theDir isDirectory:YES]];
}
MOZFilePickerPopUpObserver* observer = nil;
if (mFilters.Length() > 1) {
observer = [[MOZFilePickerPopUpObserver alloc] init];
NSView* accessoryView = GetAccessoryView();
[thePanel setAccessoryView:accessoryView];
NSPopUpButton* popupButton =
[accessoryView viewWithTag:kSaveTypeControlTag];
[observer setPopUpButton:popupButton];
[observer setOpenPanel:thePanel];
[observer setFilePicker:this];
[[NSNotificationCenter defaultCenter]
addObserver:observer
selector:@selector(menuChangedItem:)
name:NSMenuWillSendActionNotification
object:[popupButton menu]];
UpdatePanelFileTypes(thePanel, filters);
} else {
// If we show all file types, also "expose" bundles' contents.
if (!filters) {
[thePanel setTreatsFilePackagesAsDirectories:YES];
}
[thePanel setAllowedFileTypes:filters];
}
RefPtr<nsFilePicker> self = this;
nsCOMPtr<nsIFilePickerShownCallback> callback = aCallback;
BeginPanelAsync(thePanel, ^(NSModalResponse result) {
NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
if (observer) {
[[NSNotificationCenter defaultCenter] removeObserver:observer];
[observer release];
}
ResultCode retVal = returnCancel;
if (result != NSModalResponseCancel) {
for (NSURL* url in [thePanel URLs]) {
if (!url) {
continue;
}
nsCOMPtr<nsILocalFileMac> macLocalFile;
if (NS_SUCCEEDED(NS_NewLocalFileWithCFURL(
static_cast<CFURLRef>(url), getter_AddRefs(macLocalFile)))) {
self->mFiles.AppendObject(macLocalFile);
}
}
if (self->mFiles.Count() > 0) {
retVal = returnOK;
}
}
InvokeFilePickerCallbackDeferred(callback, retVal);
NS_OBJC_END_TRY_IGNORE_BLOCK;
});
NS_OBJC_END_TRY_IGNORE_BLOCK;
}
void nsFilePicker::PresentFolderPanel(nsIFilePickerShownCallback* aCallback) {
NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
MOZ_ASSERT(NS_IsMainThread());
mFiles.Clear();
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]];
}
RefPtr<nsFilePicker> self = this;
nsCOMPtr<nsIFilePickerShownCallback> callback = aCallback;
BeginPanelAsync(thePanel, ^(NSModalResponse result) {
NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
ResultCode retVal = returnCancel;
if (result != NSModalResponseCancel) {
// get the path for the folder (we allow just 1, so that's all we get)
NSArray* urls = [thePanel URLs];
if ([urls count] > 0) {
NSURL* theURL = [urls objectAtIndex:0];
if (theURL) {
nsCOMPtr<nsILocalFileMac> macLocalFile;
if (NS_SUCCEEDED(
NS_NewLocalFileWithCFURL(static_cast<CFURLRef>(theURL),
getter_AddRefs(macLocalFile)))) {
self->mFiles.AppendObject(macLocalFile);
retVal = returnOK;
}
}
}
}
InvokeFilePickerCallbackDeferred(callback, retVal);
NS_OBJC_END_TRY_IGNORE_BLOCK;
});
NS_OBJC_END_TRY_IGNORE_BLOCK;
}
void nsFilePicker::PresentSavePanel(nsIFilePickerShownCallback* aCallback) {
NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
MOZ_ASSERT(NS_IsMainThread());
mFiles.Clear();
NSSavePanel* thePanel = [NSSavePanel savePanel];
SetShowHiddenFileState(thePanel);
SetDialogTitle(mTitle, thePanel);
// set up default file name
NSString* defaultFilename =
[NSString stringWithCharacters:reinterpret_cast<const unichar*>(
mDefaultFilename.get())
length:mDefaultFilename.Length()];
// set up accessory view for file format options
MOZSaveFilePickerPopUpObserver* observer = nil;
if (mFilters.Length()) {
NSView* accessoryView = GetAccessoryView();
[thePanel setAccessoryView:accessoryView];
observer = [[MOZSaveFilePickerPopUpObserver alloc] init];
NSPopUpButton* popupButton =
[accessoryView viewWithTag:kSaveTypeControlTag];
[observer setPopUpButton:popupButton];
[observer setSavePanel:thePanel];
[observer setFilePicker:this];
[[NSNotificationCenter defaultCenter]
addObserver:observer
selector:@selector(menuChangedItem:)
name:NSMenuWillSendActionNotification
object:[popupButton menu]];
}
// Declare the default file's extension as the allowed type so that saving
// without changing the format popup keeps it on the file. The observer
// updates this when the user changes the format.
NSString* defaultExtension = defaultFilename.pathExtension;
if (defaultExtension.length != 0) {
thePanel.allowedFileTypes = @[ defaultExtension ];
}
// 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, static_cast<CFStringRef>(otherExtension),
nullptr);
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]];
}
[thePanel setNameFieldStringValue:defaultFilename];
RefPtr<nsFilePicker> self = this;
nsCOMPtr<nsIFilePickerShownCallback> callback = aCallback;
BeginPanelAsync(thePanel, ^(NSModalResponse result) {
NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
if (observer) {
[[NSNotificationCenter defaultCenter] removeObserver:observer];
[observer release];
}
ResultCode retVal = returnCancel;
if (result != NSModalResponseCancel) {
// get the save type
NSPopUpButton* popupButton =
[[thePanel accessoryView] viewWithTag:kSaveTypeControlTag];
if (popupButton) {
self->mSelectedTypeIndex = [popupButton indexOfSelectedItem];
}
NSURL* fileURL = [thePanel URL];
if (fileURL) {
nsCOMPtr<nsILocalFileMac> macLocalFile;
if (NS_SUCCEEDED(
NS_NewLocalFileWithCFURL(static_cast<CFURLRef>(fileURL),
getter_AddRefs(macLocalFile)))) {
self->mFiles.AppendObject(macLocalFile);
// We tell if we are replacing by looking to see if the
// file exists. The user will only have hit OK if they meant to
// replace the file.
if ([[NSFileManager defaultManager]
fileExistsAtPath:[fileURL path]]) {
retVal = returnReplace;
} else {
retVal = returnOK;
}
}
}
}
InvokeFilePickerCallbackDeferred(callback, retVal);
NS_OBJC_END_TRY_IGNORE_BLOCK;
});
NS_OBJC_END_TRY_IGNORE_BLOCK;
}
NSArray* nsFilePicker::GetFilterList() {
NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
if (!mFilters.Length()) {
return nil;
}
if (mFilters.Length() <= static_cast<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:reinterpret_cast<const unichar*>(
inTitle.get())
length:inTitle.Length()]];
if (!mOkButtonLabel.IsEmpty()) {
[aPanel setPrompt:[NSString
stringWithCharacters:reinterpret_cast<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;
}
NS_IMETHODIMP nsFilePicker::GetDefaultExtension(nsAString& aExtension) {
aExtension.Truncate();
return NS_OK;
}
NS_IMETHODIMP nsFilePicker::SetDefaultExtension(const nsAString& aExtension) {
return NS_OK;
}
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;
}
NS_IMETHODIMP nsFilePicker::GetFilterIndex(int32_t* aFilterIndex) {
*aFilterIndex = mSelectedTypeIndex;
return NS_OK;
}
NS_IMETHODIMP nsFilePicker::SetFilterIndex(int32_t aFilterIndex) {
mSelectedTypeIndex = aFilterIndex;
return NS_OK;
}