From 62367dff21e997ea9b7a13003a17fd46db92f03d Mon Sep 17 00:00:00 2001
From: Friedemann Kleint <Friedemann.Kleint@theqtcompany.com>
Date: Thu, 2 Apr 2015 14:38:32 +0200
Subject: [PATCH] WIP: QFileDialog (QWidget-based): Append the suffix of the
 selected filter.

Change QFileDialogPrivate::addDefaultSuffixToUrls/Files() to accept
the suffix as a parameter and pass in the suffix of the selected
name filter in QFileDialogPrivate::typedFiles() in case
defaultSuffix() is null. Add a test.

TODO:
- It is a behavioral change + feature. Is this desired
 and which branch should it go?
- The Windows native dialog prefers the name filter over the
  defaultSuffix (uses the same mechanism), I chose the convervative
  approach here (defaultSuffix takes precedence).

[ChangeLog][QtWidgets][QFileDialog] The QWidget-based file dialog
now automatically appends the suffix of the selected name filter
to file names entered without suffix.

Task-number: QTBUG-27186
Change-Id: I3ece055b328dfe361b93e68954cb0a33bd9e7d94
---
 src/widgets/dialogs/qfiledialog.cpp                | 41 ++++++++++++++++------
 src/widgets/dialogs/qfiledialog_p.h                |  8 +++--
 .../dialogs/qfiledialog2/tst_qfiledialog2.cpp      | 36 +++++++++++++++++++
 3 files changed, 72 insertions(+), 13 deletions(-)

diff --git a/src/widgets/dialogs/qfiledialog.cpp b/src/widgets/dialogs/qfiledialog.cpp
index 3f3a6e7..30ad5c3 100644
--- a/src/widgets/dialogs/qfiledialog.cpp
+++ b/src/widgets/dialogs/qfiledialog.cpp
@@ -1134,6 +1134,18 @@ Q_AUTOTEST_EXPORT QString qt_tildeExpansion(const QString &path, bool *expanded
 }
 #endif
 
+static inline QString selectedNameFilterSuffix(const QFileDialog *dialog)
+{
+    const QString nameFilter = dialog->selectedNameFilter();
+    if (nameFilter.isEmpty())
+        return QString();
+    foreach (const QString &filter, QPlatformFileDialogHelper::cleanFilterList(nameFilter)) {
+        if (filter.startsWith(QLatin1String("*.")) && filter.indexOf(QLatin1Char('*'), 2) < 0)
+            return filter.right(filter.size() - 2);
+    }
+    return QString();
+}
+
 /**
     Returns the text in the line edit which can be one or more file names
   */
@@ -1172,7 +1184,10 @@ QStringList QFileDialogPrivate::typedFiles() const
 #endif
         }
     }
-    return addDefaultSuffixToFiles(files);
+    QString suffix = q->defaultSuffix();
+    if (suffix.isNull()) // Intended, setting an empty string should suppress the name filter.
+        suffix = selectedNameFilterSuffix(q);
+    return addSuffixToFiles(files, suffix);
 }
 
 // Return selected files without defaulting to the root of the file system model
@@ -1200,16 +1215,15 @@ QList<QUrl> QFileDialogPrivate::userSelectedFiles() const
     return files;
 }
 
-QStringList QFileDialogPrivate::addDefaultSuffixToFiles(const QStringList &filesToFix) const
+QStringList QFileDialogPrivate::addSuffixToFiles(const QStringList &filesToFix, const QString &suffix) const
 {
     QStringList files;
     for (int i=0; i<filesToFix.size(); ++i) {
         QString name = toInternal(filesToFix.at(i));
         QFileInfo info(name);
-        // if the filename has no suffix, add the default suffix
-        const QString defaultSuffix = options->defaultSuffix();
-        if (!defaultSuffix.isEmpty() && !info.isDir() && name.lastIndexOf(QLatin1Char('.')) == -1)
-            name += QLatin1Char('.') + defaultSuffix;
+        // if the filename has no suffix, add the desired suffix
+        if (!suffix.isEmpty() && !info.isDir() && name.lastIndexOf(QLatin1Char('.')) == -1)
+            name += QLatin1Char('.') + suffix;
         if (info.isAbsolute()) {
             files.append(name);
         } else {
@@ -1226,17 +1240,16 @@ QStringList QFileDialogPrivate::addDefaultSuffixToFiles(const QStringList &files
     return files;
 }
 
-QList<QUrl> QFileDialogPrivate::addDefaultSuffixToUrls(const QList<QUrl> &urlsToFix) const
+QList<QUrl> QFileDialogPrivate::addSuffixToUrls(const QList<QUrl> &urlsToFix, const QString &suffix) const
 {
     QList<QUrl> urls;
     const int numUrlsToFix = urlsToFix.size();
     urls.reserve(numUrlsToFix);
     for (int i = 0; i < numUrlsToFix; ++i) {
         QUrl url = urlsToFix.at(i);
-        // if the filename has no suffix, add the default suffix
-        const QString defaultSuffix = options->defaultSuffix();
-        if (!defaultSuffix.isEmpty() && !url.path().endsWith(QLatin1Char('/')) && url.path().lastIndexOf(QLatin1Char('.')) == -1)
-            url.setPath(url.path() + QLatin1Char('.') + defaultSuffix);
+        // if the filename has no suffix, add the desired suffix
+        if (!suffix.isEmpty() && !url.path().endsWith(QLatin1Char('/')) && url.path().lastIndexOf(QLatin1Char('.')) == -1)
+            url.setPath(url.path() + QLatin1Char('.') + suffix);
         urls.append(url);
     }
     return urls;
@@ -1864,6 +1877,12 @@ bool QFileDialog::confirmOverwrite() const
     file).
 
     If the first character is a dot ('.'), it is removed.
+
+    If the suffix is not set, the first usable suffix of the selected name
+    filter is appended. This can be suppressed by the property to a non-null,
+    empty string.
+
+    \sa selectedNameFilter(), QString::isNull()
 */
 void QFileDialog::setDefaultSuffix(const QString &suffix)
 {
diff --git a/src/widgets/dialogs/qfiledialog_p.h b/src/widgets/dialogs/qfiledialog_p.h
index f610e46..41eb33f 100644
--- a/src/widgets/dialogs/qfiledialog_p.h
+++ b/src/widgets/dialogs/qfiledialog_p.h
@@ -123,8 +123,12 @@ public:
     static QString initialSelection(const QUrl &path);
     QStringList typedFiles() const;
     QList<QUrl> userSelectedFiles() const;
-    QStringList addDefaultSuffixToFiles(const QStringList &filesToFix) const;
-    QList<QUrl> addDefaultSuffixToUrls(const QList<QUrl> &urlsToFix) const;
+    QStringList addSuffixToFiles(const QStringList &filesToFix, const QString &suffix) const;
+    QStringList addDefaultSuffixToFiles(const QStringList &filesToFix) const
+        { return addSuffixToFiles(filesToFix, options->defaultSuffix()); }
+    QList<QUrl> addSuffixToUrls(const QList<QUrl> &urlsToFix, const QString &suffix) const;
+    QList<QUrl> addDefaultSuffixToUrls(const QList<QUrl> &urlsToFix) const
+        { return addSuffixToUrls(urlsToFix, options->defaultSuffix()); }
     bool removeDirectory(const QString &path);
     void setLabelTextControl(QFileDialog::DialogLabel label, const QString &text);
     inline void updateLookInLabel();
diff --git a/tests/auto/widgets/dialogs/qfiledialog2/tst_qfiledialog2.cpp b/tests/auto/widgets/dialogs/qfiledialog2/tst_qfiledialog2.cpp
index b457558..97eaf5d 100644
--- a/tests/auto/widgets/dialogs/qfiledialog2/tst_qfiledialog2.cpp
+++ b/tests/auto/widgets/dialogs/qfiledialog2/tst_qfiledialog2.cpp
@@ -140,6 +140,7 @@ private slots:
     void task259105_filtersCornerCases();
 
     void QTBUG4419_lineEditSelectAll();
+    void appendSuffixes();
     void QTBUG6558_showDirsOnly();
     void QTBUG4842_selectFilterWithHideNameFilterDetails();
     void dontShowCompleterOnRoot();
@@ -1250,6 +1251,41 @@ void tst_QFileDialog2::QTBUG4419_lineEditSelectAll()
     QCOMPARE(tempPath + QChar('/') + lineEdit->selectedText(), temporaryFile.fileName());
 }
 
+void tst_QFileDialog2::appendSuffixes()
+{
+    const QString allFilesFilter = QStringLiteral("All Files (*)");
+    const QString textSuffix = QStringLiteral("txt");
+    const QString textFilesFilter = QStringLiteral("Text Files (*.")
+        + textSuffix + QLatin1Char(')');
+    const QString defaultSuffix = QStringLiteral("defaultSuffix");
+    const QString baseName = QStringLiteral("nonexistingfile");
+
+    QNonNativeFileDialog fd(0, "TestFileDialog", tempDir.path(),
+                            allFilesFilter + QStringLiteral(";;") + textFilesFilter);
+    fd.setViewMode(QFileDialog::List);
+    fd.setAcceptMode(QFileDialog::AcceptSave);
+    fd.setFileMode(QFileDialog::AnyFile);
+    fd.show();
+    QApplication::setActiveWindow(&fd);
+    QVERIFY(QTest::qWaitForWindowActive(&fd));
+    QLineEdit *lineEdit = fd.findChild<QLineEdit*>("fileNameEdit");
+    QVERIFY(lineEdit);
+    lineEdit->setText(baseName);
+
+    // No default suffix, all files selected: Nothing should be appended
+    QVERIFY(fd.selectedFiles().first().endsWith(baseName));
+    // "Text Files" selected: textSuffix should be appended
+    fd.selectNameFilter(textFilesFilter);
+    QVERIFY(fd.selectedFiles().first().endsWith(textSuffix));
+    // Non-null, empty defaultSuffix: Nothing should be appended
+    fd.setDefaultSuffix(QLatin1String(""));
+    QVERIFY(fd.selectedFiles().first().endsWith(baseName));
+    // "Text Files" selected with defaultSuffix: defaultSuffix should be appended
+    // (overriding the name filter).
+    fd.setDefaultSuffix(defaultSuffix);
+    QVERIFY(fd.selectedFiles().first().endsWith(defaultSuffix));
+}
+
 void tst_QFileDialog2::QTBUG6558_showDirsOnly()
 {
     const QString tempPath = tempDir.path();
-- 
1.9.1

