diff --git a/autotests/kurlcompletiontest.cpp b/autotests/kurlcompletiontest.cpp --- a/autotests/kurlcompletiontest.cpp +++ b/autotests/kurlcompletiontest.cpp @@ -59,6 +59,7 @@ private: void waitForCompletion(KUrlCompletion *completion); KUrlCompletion *m_completion; + KUrlCompletion *m_completionWithMimeFilter; QTemporaryDir *m_tempDir; QUrl m_dirURL; QString m_dir; @@ -69,12 +70,15 @@ { qDebug(); m_completion = new KUrlCompletion; + m_completionWithMimeFilter = new KUrlCompletion; + m_completionWithMimeFilter->setMimeTypeFilters({QStringLiteral("text/x-c++src")}); m_tempDir = new QTemporaryDir; m_dir = m_tempDir->path(); m_dir += QLatin1String("/Dir With#Spaces/"); QDir().mkdir(m_dir); qDebug() << "m_dir=" << m_dir; m_completion->setDir(QUrl::fromLocalFile(m_dir)); + m_completionWithMimeFilter->setDir(m_completion->dir()); m_dirURL = QUrl::fromLocalFile(m_dir); QFile f1(m_dir + "/file1"); @@ -92,6 +96,16 @@ QVERIFY(ok); f3.close(); + QFile f4(m_dir + "/source.cpp"); + ok = f4.open(QIODevice::WriteOnly); + QVERIFY(ok); + f4.close(); + + QFile f5(m_dir + "/source.php"); + ok = f5.open(QIODevice::WriteOnly); + QVERIFY(ok); + f5.close(); + QDir().mkdir(m_dir + "/file_subdir"); QDir().mkdir(m_dir + "/.1_hidden_file_subdir"); QDir().mkdir(m_dir + "/.2_hidden_file_subdir"); @@ -104,6 +118,8 @@ { delete m_completion; m_completion = nullptr; + delete m_completionWithMimeFilter; + m_completionWithMimeFilter = nullptr; delete m_tempDir; m_tempDir = nullptr; delete m_completionEmptyCwd; @@ -179,6 +195,20 @@ const auto compFileEndingWithDot = m_completion->allMatches(); QCOMPARE(compFileEndingWithDot.count(), 1); QVERIFY(compFileEndingWithDot.contains("file.")); + + // Completion with 'source' should only find the C++ file + m_completionWithMimeFilter->makeCompletion("source"); + waitForCompletion(m_completionWithMimeFilter); + const auto compSourceFile = m_completionWithMimeFilter->allMatches(); + QCOMPARE(compSourceFile.count(), 1); + QVERIFY(compSourceFile.contains("source.cpp")); + + // But it should also be able to find folders + m_completionWithMimeFilter->makeCompletion("file_subdir"); + waitForCompletion(m_completionWithMimeFilter); + const auto compMimeFolder = m_completionWithMimeFilter->allMatches(); + QCOMPARE(compMimeFolder.count(), 1); + QVERIFY(compMimeFolder.contains("file_subdir/")); } void KUrlCompletionTest::testLocalAbsolutePath() @@ -218,6 +248,20 @@ const auto compFileEndingWithDot = m_completion->allMatches(); QCOMPARE(compFileEndingWithDot.count(), 1); QVERIFY(compFileEndingWithDot.contains(m_dir + "file.")); + + // Completion with 'source' should only find the C++ file + m_completionWithMimeFilter->makeCompletion(m_dir + "source"); + waitForCompletion(m_completionWithMimeFilter); + const auto compSourceFile = m_completionWithMimeFilter->allMatches(); + QCOMPARE(compSourceFile.count(), 1); + QVERIFY(compSourceFile.contains(m_dir + "source.cpp")); + + // But it should also be able to find folders + m_completionWithMimeFilter->makeCompletion(m_dir + "file_subdir"); + waitForCompletion(m_completionWithMimeFilter); + const auto compMimeFolder = m_completionWithMimeFilter->allMatches(); + QCOMPARE(compMimeFolder.count(), 1); + QVERIFY(compMimeFolder.contains(m_dir + "file_subdir/")); } void KUrlCompletionTest::testLocalURL() @@ -286,6 +330,20 @@ const auto compFileEndingWithDot = m_completion->allMatches(); QCOMPARE(compFileEndingWithDot.count(), 1); QVERIFY(compFileEndingWithDot.contains(m_dirURL.toString() + "file.")); + + // Completion with 'source' should only find the C++ file + m_completionWithMimeFilter->makeCompletion(m_dirURL.toString() + "source"); + waitForCompletion(m_completionWithMimeFilter); + const auto compSourceFile = m_completionWithMimeFilter->allMatches(); + QCOMPARE(compSourceFile.count(), 1); + QVERIFY(compSourceFile.contains(m_dirURL.toString() + "source.cpp")); + + // But it should also be able to find folders + m_completionWithMimeFilter->makeCompletion(m_dirURL.toString() + "file_subdir"); + waitForCompletion(m_completionWithMimeFilter); + const auto compMimeFolder = m_completionWithMimeFilter->allMatches(); + QCOMPARE(compMimeFolder.count(), 1); + QVERIFY(compMimeFolder.contains(m_dirURL.toString() + "file_subdir/")); } void KUrlCompletionTest::testEmptyCwd() diff --git a/src/widgets/kurlcompletion.h b/src/widgets/kurlcompletion.h --- a/src/widgets/kurlcompletion.h +++ b/src/widgets/kurlcompletion.h @@ -169,6 +169,20 @@ static QString replacedPath(const QString &text, bool replaceHome, bool replaceEnv = true); + /** + * Sets the mimetype filters for the file dialog. + * @see QFileDialog::setMimeTypeFilters() + * @since 5.38 + */ + void setMimeTypeFilters(const QStringList &mimeTypes); + + /** + * Returns the mimetype filters for the file dialog. + * @see QFileDialog::mimeTypeFilters() + * @since 5.38 + */ + QStringList mimeTypeFilters() const; + protected: // Called by KCompletion, adds '/' to directories void postProcessMatch(QString *match) const Q_DECL_OVERRIDE; diff --git a/src/widgets/kurlcompletion.cpp b/src/widgets/kurlcompletion.cpp --- a/src/widgets/kurlcompletion.cpp +++ b/src/widgets/kurlcompletion.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include // QT_LSTAT, QT_STAT, QT_STATBUF @@ -201,6 +202,8 @@ CompletionThread *userListThread; CompletionThread *dirListThread; + + QStringList mimeTypeFilters; }; class CompletionThread : public QThread @@ -308,13 +311,15 @@ DirectoryListThread(KUrlCompletionPrivate *receiver, const QStringList &dirList, const QString &filter, + const QStringList &mimeTypeFilters, bool onlyExe, bool onlyDir, bool noHidden, bool appendSlashToDir) : CompletionThread(receiver), m_dirList(dirList), m_filter(filter), + m_mimeTypeFilters(mimeTypeFilters), m_onlyExe(onlyExe), m_onlyDir(onlyDir), m_noHidden(noHidden), @@ -326,6 +331,7 @@ private: QStringList m_dirList; QString m_filter; + QStringList m_mimeTypeFilters; bool m_onlyExe; bool m_onlyDir; bool m_noHidden; @@ -345,6 +351,8 @@ iterator_filter |= (QDir::Dirs | QDir::Files); } + QMimeDatabase mimeTypes; + const QStringList::const_iterator end = m_dirList.constEnd(); for (QStringList::const_iterator it = m_dirList.constBegin(); it != end && !terminationRequested(); @@ -361,21 +369,29 @@ //qDebug() << "Found" << file_name; - if (m_filter.isEmpty() || file_name.startsWith(m_filter)) { + if (!m_filter.isEmpty() && !file_name.startsWith(m_filter)) { + continue; + } - QString toAppend = file_name; - // Add '/' to directories - if (m_appendSlashToDir && file_info.isDir()) { - toAppend.append(QLatin1Char('/')); + if (!m_mimeTypeFilters.isEmpty() && !file_info.isDir()) { + auto mimeType = mimeTypes.mimeTypeForFile(file_info); + if (!m_mimeTypeFilters.contains(mimeType.name())) { + continue; } + } - if (m_complete_url) { - QUrl info(m_prepend); - info = addPathToUrl(info, toAppend); - addMatch(info.toDisplayString()); - } else { - addMatch(m_prepend + toAppend); - } + QString toAppend = file_name; + // Add '/' to directories + if (m_appendSlashToDir && file_info.isDir()) { + toAppend.append(QLatin1Char('/')); + } + + if (m_complete_url) { + QUrl info(m_prepend); + info = addPathToUrl(info, toAppend); + addMatch(info.toDisplayString()); + } else { + addMatch(m_prepend + toAppend); } } } @@ -1114,8 +1130,9 @@ } Q_ASSERT(!dirListThread); // caller called stop() - dirListThread = new DirectoryListThread(this, dirs, filter, only_exe, only_dir, - no_hidden, append_slash_to_dir); + dirListThread = new DirectoryListThread(this, dirs, filter, mimeTypeFilters, + only_exe, only_dir, no_hidden, + append_slash_to_dir); QObject::connect(dirListThread, &CompletionThread::completionThreadDone, q, [this](QThread *thread, const QStringList &matches){ slotCompletionThreadDone(thread, matches); }); dirListThread->start(); @@ -1221,26 +1238,33 @@ continue; } - if (filter_len == 0 || entry_name.left(filter_len) == filter) { + if (filter_len != 0 && entry_name.left(filter_len) != filter) { + continue; + } + + if (!mimeTypeFilters.isEmpty() && !isDir && + !mimeTypeFilters.contains(entry.stringValue(KIO::UDSEntry::UDS_MIME_TYPE))) { + continue; + } - QString toAppend = entry_name; + QString toAppend = entry_name; - if (isDir) { - toAppend.append(QLatin1Char('/')); - } + if (isDir) { + toAppend.append(QLatin1Char('/')); + } - if (!list_urls_only_exe || - (entry.numberValue(KIO::UDSEntry::UDS_ACCESS) & MODE_EXE) // true if executable - ) { - if (complete_url) { - QUrl url(prepend); - url = addPathToUrl(url, toAppend); - matchList.append(url.toDisplayString()); - } else { - matchList.append(prepend + toAppend); - } + if (!list_urls_only_exe || + (entry.numberValue(KIO::UDSEntry::UDS_ACCESS) & MODE_EXE) // true if executable + ) { + if (complete_url) { + QUrl url(prepend); + url = addPathToUrl(url, toAppend); + matchList.append(url.toDisplayString()); + } else { + matchList.append(prepend + toAppend); } } + } addMatches(matchList); @@ -1396,6 +1420,16 @@ return replacedPath(text, d->replace_home, d->replace_env); } +void KUrlCompletion::setMimeTypeFilters(const QStringList &mimeTypeFilters) +{ + d->mimeTypeFilters = mimeTypeFilters; +} + +QStringList KUrlCompletion::mimeTypeFilters() const +{ + return d->mimeTypeFilters; +} + ///////////////////////////////////////////////////////// ///////////////////////////////////////////////////////// // Static functions diff --git a/src/widgets/kurlrequester.cpp b/src/widgets/kurlrequester.cpp --- a/src/widgets/kurlrequester.cpp +++ b/src/widgets/kurlrequester.cpp @@ -519,6 +519,7 @@ if (d->myFileDialog) { d->myFileDialog->setMimeTypeFilters(d->mimeTypeFilters); } + d->myCompletion->setMimeTypeFilters(d->mimeTypeFilters); } QStringList KUrlRequester::mimeTypeFilters() const diff --git a/tests/kurlrequestertest_gui.cpp b/tests/kurlrequestertest_gui.cpp --- a/tests/kurlrequestertest_gui.cpp +++ b/tests/kurlrequestertest_gui.cpp @@ -33,5 +33,10 @@ comboReq->setWindowTitle(QStringLiteral("KUrlComboRequester")); comboReq->show(); + auto *mimeFilterReq = new KUrlRequester(); + mimeFilterReq->setMimeTypeFilters({QStringLiteral("text/x-c++src")}); + mimeFilterReq->setWindowTitle(QStringLiteral("MimeFilter")); + mimeFilterReq->show(); + return app.exec(); }