diff --git a/autotests/kfilewidgettest.cpp b/autotests/kfilewidgettest.cpp --- a/autotests/kfilewidgettest.cpp +++ b/autotests/kfilewidgettest.cpp @@ -29,6 +29,8 @@ #include #include #include +#include +#include "kiotesthelper.h" // createTestFile /** * Unit test for KFileWidget @@ -245,6 +247,123 @@ QVERIFY(u.isValid()); QCOMPARE(u, expectedUrl); } + + void testSetFilterForSave_data() + { + QTest::addColumn("fileName"); + QTest::addColumn("filter"); + QTest::addColumn("expectedCurrentText"); + QTest::addColumn("expectedSelectedFileName"); + + const QString filter = QStringLiteral("*.txt|Text files\n*.HTML|HTML files"); + + QTest::newRow("some.txt") + << "some.txt" + << filter + << QStringLiteral("some.txt") + << QStringLiteral("some.txt"); + + // If an application provides a name without extension, then the + // displayed name will not receive an extension. It will however be + // appended when the dialog is closed. + QTest::newRow("extensionless name") + << "some" + << filter + << QStringLiteral("some") + << QStringLiteral("some.txt"); + + // If the file literally exists, then no new extension will be appended. + QTest::newRow("existing file") + << "README" + << filter + << QStringLiteral("README") + << QStringLiteral("README"); + + // XXX perhaps the "extension" should not be modified when it does not + // match any of the existing types? Should "some.2019.txt" be expected? + QTest::newRow("some.2019") + << "some.2019" + << filter + << QStringLiteral("some.txt") + << QStringLiteral("some.txt"); + + // XXX be smarter and do not change the extension if one of the other + // filters match. Should "some.html" be expected? + QTest::newRow("some.html") + << "some.html" + << filter + << QStringLiteral("some.txt") + << QStringLiteral("some.txt"); + } + + void testSetFilterForSave() + { + QFETCH(QString, fileName); + QFETCH(QString, filter); + QFETCH(QString, expectedCurrentText); + QFETCH(QString, expectedSelectedFileName); + + // Use a temporary directory since the presence of existing files + // influences whether an extension is automatically appended. + QTemporaryDir tempDir; + const QUrl dirUrl = QUrl::fromLocalFile(tempDir.path()); + const QUrl fileUrl = QUrl::fromLocalFile(tempDir.filePath(fileName)); + const QUrl expectedFileUrl = QUrl::fromLocalFile(tempDir.filePath(expectedSelectedFileName)); + createTestFile(tempDir.filePath("README")); + + KFileWidget fw(dirUrl); + fw.setOperationMode(KFileWidget::Saving); + fw.setSelectedUrl(fileUrl); + // Calling setFilter has side-effects and changes the file name. + fw.setFilter(filter); + fw.show(); + fw.activateWindow(); + QVERIFY(QTest::qWaitForWindowActive(&fw)); + + // Verify the expected populated name. + QCOMPARE(fw.baseUrl().adjusted(QUrl::StripTrailingSlash), dirUrl); + QCOMPARE(fw.locationEdit()->currentText(), expectedCurrentText); + + // QFileDialog ends up calling KDEPlatformFileDialog::selectedFiles() + // which calls KFileWidget::selectedUrls(). + // Accept the filename to ensure that a filename is selected. + connect(&fw, &KFileWidget::accepted, &fw, &KFileWidget::accept); + QTest::keyClick(fw.locationEdit(), Qt::Key_Return); + QList urls = fw.selectedUrls(); + QCOMPARE(urls.size(), 1); + QCOMPARE(urls[0], expectedFileUrl); + } + + void testFilterChange() + { + QTemporaryDir tempDir; + createTestFile(tempDir.filePath("some.c")); + bool created = QDir(tempDir.path()).mkdir("directory"); + Q_ASSERT(created); + + KFileWidget fw(QUrl::fromLocalFile(tempDir.path())); + fw.setOperationMode(KFileWidget::Saving); + fw.setSelectedUrl(QUrl::fromLocalFile(tempDir.filePath("some.txt"))); + fw.setFilter("*.txt|Txt\n*.c|C"); + fw.show(); + fw.activateWindow(); + QVERIFY(QTest::qWaitForWindowActive(&fw)); + + // Initial filename. + QCOMPARE(fw.locationEdit()->currentText(), QStringLiteral("some.txt")); + QCOMPARE(fw.filterWidget()->currentFilter(), QStringLiteral("*.txt")); + + // Select type with an existing file. + fw.filterWidget()->setCurrentFilter("*.c|C"); + QCOMPARE(fw.locationEdit()->currentText(), QStringLiteral("some.c")); + QCOMPARE(fw.filterWidget()->currentFilter(), QStringLiteral("*.c")); + + // Do not update extension if the current selection is a directory. + fw.setSelectedUrl(QUrl::fromLocalFile(tempDir.filePath("directory"))); + fw.filterWidget()->setCurrentFilter("*.txt|Txt"); + QCOMPARE(fw.locationEdit()->currentText(), QStringLiteral("directory")); + QCOMPARE(fw.filterWidget()->currentFilter(), QStringLiteral("*.txt")); + } }; QTEST_MAIN(KFileWidgetTest) diff --git a/src/filewidgets/kfilewidget.cpp b/src/filewidgets/kfilewidget.cpp --- a/src/filewidgets/kfilewidget.cpp +++ b/src/filewidgets/kfilewidget.cpp @@ -789,7 +789,13 @@ !KProtocolInfo::isKnownProtocol(relativeUrlTest)) { u = relativeUrlTest; } else { - u = QUrl(url); // keep it relative + // Try to preserve URLs if they have a scheme (for example, + // "https://example.com/foo.txt") and otherwise resolve relative + // paths to absolute ones (e.g. "foo.txt" -> "file:///tmp/foo.txt"). + u = QUrl(url); + if (u.isRelative()) { + u = relativeUrlTest; + } } } @@ -1588,7 +1594,8 @@ } QUrl u = d->getCompleteUrl(url); - if (!u.isValid()) { // if it still is + if (!u.isValid()) { + // Relative path was treated as URL, but it was found to be invalid. qWarning() << url << " is not a correct argument for setSelection!"; return; }