macOS: Replace deprecated NSOpenSavePanelDelegate method

shouldShowFilename method has been deprecated since macOS 10.6.
In 10.11 the behavior of this method has been broken, causing
files containing metadata (e.g. audio) to be incorrectly filtered out,
displayed them as disabled in file dialog even though they shouldn’t be.
This erratic behavior applies also to NSOpenPanel setAllowedFileTypes
if set to anything but nil. This has been confirmed to be a known bug
in Cocoa.

Using shouldEnableURL solves this problem and also removes risk
of breaking compatibility with future SDKs.

Renamed and simplified private method isHiddenFile to
isHiddenFileAtURL.
Renamed to be consistent with other Cocoa file query methods.
Simplified to return true only if the file is hidden as the name of the method
implies. Previously it might have returned true also if the file has
not existed which was in fact very metaphysical answer.
Check for presence of the file is done by other method before calling
this one and the scope of the method is limited to one source file.

Task-number: QTBUG-57527
Change-Id: I2fded712d4e7098eb444331d92e38cee71655100
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
This commit is contained in:
Pavol Markovic 2017-02-23 15:20:04 +13:00
parent 4e2f0fe4d6
commit 116ade88ea

View File

@ -101,7 +101,7 @@ typedef QSharedPointer<QFileDialogOptions> SharedPointerFileDialogOptions;
} }
- (NSString *)strip:(const QString &)label; - (NSString *)strip:(const QString &)label;
- (BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename; - (BOOL)panel:(id)sender shouldEnableURL:(NSURL *)url;
- (void)filterChanged:(id)sender; - (void)filterChanged:(id)sender;
- (void)showModelessPanel; - (void)showModelessPanel;
- (BOOL)runApplicationModalPanel; - (BOOL)runApplicationModalPanel;
@ -222,12 +222,12 @@ static QString strippedText(QString s)
if (mOpenPanel){ if (mOpenPanel){
QFileInfo info(*mCurrentSelection); QFileInfo info(*mCurrentSelection);
NSString *filepath = info.filePath().toNSString(); NSString *filepath = info.filePath().toNSString();
NSURL *url = [NSURL fileURLWithPath:filepath isDirectory:info.isDir()];
bool selectable = (mOptions->acceptMode() == QFileDialogOptions::AcceptSave) bool selectable = (mOptions->acceptMode() == QFileDialogOptions::AcceptSave)
|| [self panel:nil shouldShowFilename:filepath]; || [self panel:nil shouldEnableURL:url];
[self updateProperties]; [self updateProperties];
QCocoaMenuBar::redirectKnownMenuItemsToFirstResponder(); QCocoaMenuBar::redirectKnownMenuItemsToFirstResponder();
[mOpenPanel setAllowedFileTypes:nil];
[mSavePanel setNameFieldStringValue:selectable ? info.fileName().toNSString() : @""]; [mSavePanel setNameFieldStringValue:selectable ? info.fileName().toNSString() : @""];
[mOpenPanel beginWithCompletionHandler:^(NSInteger result){ [mOpenPanel beginWithCompletionHandler:^(NSInteger result){
@ -242,8 +242,9 @@ static QString strippedText(QString s)
{ {
QFileInfo info(*mCurrentSelection); QFileInfo info(*mCurrentSelection);
NSString *filepath = info.filePath().toNSString(); NSString *filepath = info.filePath().toNSString();
NSURL *url = [NSURL fileURLWithPath:filepath isDirectory:info.isDir()];
bool selectable = (mOptions->acceptMode() == QFileDialogOptions::AcceptSave) bool selectable = (mOptions->acceptMode() == QFileDialogOptions::AcceptSave)
|| [self panel:nil shouldShowFilename:filepath]; || [self panel:nil shouldEnableURL:url];
[mSavePanel setDirectoryURL: [NSURL fileURLWithPath:mCurrentDir]]; [mSavePanel setDirectoryURL: [NSURL fileURLWithPath:mCurrentDir]];
[mSavePanel setNameFieldStringValue:selectable ? info.fileName().toNSString() : @""]; [mSavePanel setNameFieldStringValue:selectable ? info.fileName().toNSString() : @""];
@ -273,8 +274,9 @@ static QString strippedText(QString s)
{ {
QFileInfo info(*mCurrentSelection); QFileInfo info(*mCurrentSelection);
NSString *filepath = info.filePath().toNSString(); NSString *filepath = info.filePath().toNSString();
NSURL *url = [NSURL fileURLWithPath:filepath isDirectory:info.isDir()];
bool selectable = (mOptions->acceptMode() == QFileDialogOptions::AcceptSave) bool selectable = (mOptions->acceptMode() == QFileDialogOptions::AcceptSave)
|| [self panel:nil shouldShowFilename:filepath]; || [self panel:nil shouldEnableURL:url];
[self updateProperties]; [self updateProperties];
QCocoaMenuBar::redirectKnownMenuItemsToFirstResponder(); QCocoaMenuBar::redirectKnownMenuItemsToFirstResponder();
@ -290,26 +292,24 @@ static QString strippedText(QString s)
}]; }];
} }
- (BOOL)isHiddenFile:(NSString *)filename isDir:(BOOL)isDir - (BOOL)isHiddenFileAtURL:(NSURL *)url
{ {
CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, (CFStringRef)filename, kCFURLPOSIXPathStyle, isDir); BOOL hidden = NO;
CFBooleanRef isHidden; if (url) {
Boolean errorOrHidden = false; CFBooleanRef isHiddenProperty;
if (!CFURLCopyResourcePropertyForKey(url, kCFURLIsHiddenKey, &isHidden, NULL)) { if (CFURLCopyResourcePropertyForKey((__bridge CFURLRef)url, kCFURLIsHiddenKey, &isHiddenProperty, NULL)) {
errorOrHidden = true; hidden = CFBooleanGetValue(isHiddenProperty);
} else { CFRelease(isHiddenProperty);
if (CFBooleanGetValue(isHidden)) }
errorOrHidden = true;
CFRelease(isHidden);
} }
CFRelease(url); return hidden;
return errorOrHidden;
} }
- (BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename - (BOOL)panel:(id)sender shouldEnableURL:(NSURL *)url
{ {
Q_UNUSED(sender); Q_UNUSED(sender);
NSString *filename = [url path];
if ([filename length] == 0) if ([filename length] == 0)
return NO; return NO;
@ -353,7 +353,7 @@ static QString strippedText(QString s)
return NO; return NO;
} }
if (!(filter & QDir::Hidden) if (!(filter & QDir::Hidden)
&& (qtFileName.startsWith(QLatin1Char('.')) || [self isHiddenFile:filename isDir:isDir])) && (qtFileName.startsWith(QLatin1Char('.')) || [self isHiddenFileAtURL:url]))
return NO; return NO;
return YES; return YES;
@ -446,11 +446,15 @@ static QString strippedText(QString s)
[mSavePanel setTitle:mOptions->windowTitle().toNSString()]; [mSavePanel setTitle:mOptions->windowTitle().toNSString()];
[mPopUpButton setHidden:chooseDirsOnly]; // TODO hide the whole sunken pane instead? [mPopUpButton setHidden:chooseDirsOnly]; // TODO hide the whole sunken pane instead?
QStringList ext = [self acceptableExtensionsForSave]; if (mOptions->acceptMode() == QFileDialogOptions::AcceptSave) {
const QString defaultSuffix = mOptions->defaultSuffix(); QStringList ext = [self acceptableExtensionsForSave];
if (!ext.isEmpty() && !defaultSuffix.isEmpty()) const QString defaultSuffix = mOptions->defaultSuffix();
ext.prepend(defaultSuffix); if (!ext.isEmpty() && !defaultSuffix.isEmpty())
[mSavePanel setAllowedFileTypes:ext.isEmpty() ? nil : qt_mac_QStringListToNSMutableArray(ext)]; ext.prepend(defaultSuffix);
[mSavePanel setAllowedFileTypes:ext.isEmpty() ? nil : qt_mac_QStringListToNSMutableArray(ext)];
} else {
[mOpenPanel setAllowedFileTypes:nil]; // delegate panel:shouldEnableURL: does the file filtering for NSOpenPanel
}
if ([mSavePanel respondsToSelector:@selector(isVisible)] && [mSavePanel isVisible]) { if ([mSavePanel respondsToSelector:@selector(isVisible)] && [mSavePanel isVisible]) {
if ([mSavePanel respondsToSelector:@selector(validateVisibleColumns)]) if ([mSavePanel respondsToSelector:@selector(validateVisibleColumns)])