File indexing completed on 2024-05-12 05:38:20
0001 /* 0002 SPDX-FileCopyrightText: 2008 Sebastian Kügler <sebas@kde.org> 0003 SPDX-FileCopyrightText: 2023 Alexander Lohnau <alexander.lohnau@gmx.de> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "recentdocuments.h" 0009 0010 #include <QApplication> 0011 #include <QDir> 0012 #include <QMimeData> 0013 #include <QMimeDatabase> 0014 #include <QMimeType> 0015 0016 #include <KIO/Job> 0017 #include <KIO/JobUiDelegate> 0018 #include <KIO/JobUiDelegateFactory> 0019 #include <KIO/OpenFileManagerWindowJob> 0020 #include <KIO/OpenUrlJob> 0021 #include <KLocalizedString> 0022 #include <KNotificationJobUiDelegate> 0023 #include <KShell> 0024 0025 #include <PlasmaActivities/Stats/Query> 0026 #include <PlasmaActivities/Stats/Terms> 0027 0028 using namespace KActivities::Stats::Terms; 0029 0030 K_PLUGIN_CLASS_WITH_JSON(RecentDocuments, "plasma-runner-recentdocuments.json") 0031 0032 RecentDocuments::RecentDocuments(QObject *parent, const KPluginMetaData &metaData) 0033 : KRunner::AbstractRunner(parent, metaData) 0034 , m_actions({KRunner::Action(QStringLiteral("open-folder"), QStringLiteral("document-open-folder"), i18n("Open Containing Folder"))}) 0035 { 0036 addSyntax(QStringLiteral(":q:"), i18n("Looks for documents recently used with names matching :q:.")); 0037 setMinLetterCount(m_minLetterCount); 0038 } 0039 0040 void RecentDocuments::match(KRunner::RunnerContext &context) 0041 { 0042 const QString term = context.query(); 0043 0044 if (!m_resultsModel || m_resultsModel->rowCount() == m_maxResults || m_lastLoadedQuery.size() < m_minLetterCount || !term.startsWith(m_lastLoadedQuery)) { 0045 const QLatin1String asterix("*"); 0046 const QString termPattern = (term.size() < m_minLetterCount ? QLatin1String() : asterix) + term + asterix; 0047 auto query = UsedResources | Activity::current() | Order::RecentlyUsedFirst | Agent::any() // 0048 | Type::files() // Only show files and not folders 0049 | Limit(m_maxResults) // In case we are in single runner mode, we could get tons of results for one or two letter queries 0050 | Url("/*/*") // we search only for local files 0051 | Title({termPattern}); // check the title, because that is the filename 0052 0053 // Reuse the model in case our query starts with the previous one. We filter out irrelevant results later on anyway 0054 m_resultsModel.reset(new ResultModel(query)); 0055 m_lastLoadedQuery = term; 0056 } 0057 0058 if (!context.isValid()) { 0059 return; // The initial fetching could take a moment, check the context validity afterward 0060 } 0061 float relevance = 0.75; 0062 QMimeDatabase db; 0063 QList<KRunner::QueryMatch> matches; 0064 for (int i = 0; i < m_resultsModel->rowCount(); ++i) { 0065 const QModelIndex index = m_resultsModel->index(i, 0); 0066 0067 const auto fileName = m_resultsModel->data(index, ResultModel::TitleRole).toString(); 0068 const int indexOfTerm = fileName.indexOf(term, Qt::CaseInsensitive); 0069 if (indexOfTerm == -1) { 0070 continue; // A previous result or a result where the path, but not filename matches 0071 } 0072 0073 KRunner::QueryMatch match(this); 0074 // We know the term starts with the query, check size to see if it is an exact match 0075 if (term.size() >= 5 && indexOfTerm == 0 && (fileName.size() == term.size() || QFileInfo(fileName).baseName().size() == term.size())) { 0076 match.setRelevance(relevance + 0.1); 0077 match.setCategoryRelevance(KRunner::QueryMatch::CategoryRelevance::Highest); 0078 } else if (indexOfTerm == 0 /*startswith, but not equals => smaller relevance boost*/) { 0079 match.setRelevance(relevance + 0.1); 0080 match.setCategoryRelevance(KRunner::QueryMatch::CategoryRelevance::High); 0081 } else { 0082 match.setRelevance(relevance); 0083 match.setCategoryRelevance(KRunner::QueryMatch::CategoryRelevance::Low); 0084 } 0085 const QMimeType mimeType = db.mimeTypeForName(m_resultsModel->data(index, ResultModel::MimeType).toString()); 0086 match.setIconName(mimeType.iconName()); 0087 const QUrl url = QUrl::fromLocalFile(m_resultsModel->data(index, ResultModel::ResourceRole).toString()); 0088 match.setData(QVariant(url)); 0089 match.setUrls({url}); 0090 match.setId(url.toString()); 0091 if (url.isLocalFile()) { 0092 match.setActions(m_actions); 0093 } 0094 match.setText(fileName); 0095 QString destUrlString = KShell::tildeCollapse(url.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).path()); 0096 match.setSubtext(destUrlString); 0097 0098 relevance -= 0.05; 0099 matches << match; 0100 } 0101 context.addMatches(matches); 0102 } 0103 0104 void RecentDocuments::run(const KRunner::RunnerContext & /*context*/, const KRunner::QueryMatch &match) 0105 { 0106 const QUrl url = match.data().toUrl(); 0107 0108 if (match.selectedAction()) { 0109 KIO::highlightInFileManager({url}); 0110 return; 0111 } 0112 0113 auto *job = new KIO::OpenUrlJob(url); 0114 job->setUiDelegate(KIO::createDefaultJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, QApplication::activeWindow())); 0115 job->setShowOpenOrExecuteDialog(true); 0116 job->start(); 0117 } 0118 0119 #include "recentdocuments.moc"