macOS: Reduce save dialog extension filters to their last component

NSSavePanel does not deal well with multi-part extensions, to the point
where it will fail to open if that's the only acceptable extension.

We follow Chromium's lead here and reduce the extension to its last
component, which enables selecting and saving files such as 'foo.tar.gz'.

To improve the user experience we always show file extensions when we
detect a multi-part extension. This makes it clearer what the final
extension will be, and avoids confusing macOS about the intention of
the user when choosing a file that without the final extension also
matches another known extension.

Fixes: QTBUG-38303
Fixes: QTBUG-44227
Change-Id: Id0cee84f758c2cd59fcf1b339caa30f7da07dd1e
Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
bb10
Tor Arne Vestbø 2020-03-02 19:36:26 +01:00 committed by Tor Arne Vestbø
parent 1edf586ed7
commit 5d9d3ff326
1 changed files with 41 additions and 18 deletions

View File

@ -419,8 +419,7 @@ static QString strippedText(QString s)
[mPopUpButton setHidden:chooseDirsOnly]; // TODO hide the whole sunken pane instead?
if (mOptions->acceptMode() == QFileDialogOptions::AcceptSave) {
const QStringList ext = [self acceptableExtensionsForSave];
[mSavePanel setAllowedFileTypes:ext.isEmpty() ? nil : qt_mac_QStringListToNSMutableArray(ext)];
[self recomputeAcceptableExtensionsForSave];
} else {
[mOpenPanel setAllowedFileTypes:nil]; // delegate panel:shouldEnableURL: does the file filtering for NSOpenPanel
}
@ -457,25 +456,49 @@ static QString strippedText(QString s)
}
/*
Returns a list of extensions (e.g. "png", "jpg", "gif")
for the current name filter. If a filter do not conform
to the format *.xyz or * or *.*, an empty list
is returned meaning accept everything.
Computes a list of extensions (e.g. "png", "jpg", "gif")
for the current name filter, and updates the save panel.
If a filter do not conform to the format *.xyz or * or *.*,
all files types are allowed.
Extensions with more than one part (e.g. "tar.gz") are
reduced to their final part, as NSSavePanel does not deal
well with multi-part extensions.
*/
- (QStringList)acceptableExtensionsForSave
- (void)recomputeAcceptableExtensionsForSave
{
QStringList result;
for (int i=0; i<mSelectedNameFilter->count(); ++i) {
const QString &filter = mSelectedNameFilter->at(i);
if (filter.startsWith(QLatin1String("*."))
&& !filter.contains(QLatin1Char('?'))
&& filter.count(QLatin1Char('*')) == 1) {
result += filter.mid(2);
} else {
return QStringList(); // Accept everything
}
QStringList fileTypes;
for (const QString &filter : *mSelectedNameFilter) {
if (!filter.startsWith(QLatin1String("*.")))
continue;
if (filter.contains(QLatin1Char('?')))
continue;
if (filter.count(QLatin1Char('*')) != 1)
continue;
auto extensions = filter.split('.', Qt::SkipEmptyParts);
fileTypes += extensions.last();
// Explicitly show extensions if we detect a filter
// that has a multi-part extension. This prevents
// confusing situations where the user clicks e.g.
// 'foo.tar.gz' and 'foo.tar' is populated in the
// file name box, but when then clicking save macOS
// will warn that the file needs to end in .gz,
// due to thinking the user tried to save the file
// as a 'tar' file instead. Unfortunately this
// property can only be set before the panel is
// shown, so it will not have any effect when
// swithcing filters in an already opened dialog.
if (extensions.size() > 2)
mSavePanel.extensionHidden = NO;
}
return result;
mSavePanel.allowedFileTypes = fileTypes.isEmpty() ? nil
: qt_mac_QStringListToNSMutableArray(fileTypes);
}
- (QString)removeExtensions:(const QString &)filter