File indexing completed on 2024-05-12 05:38:17

0001 /*
0002     SPDX-FileCopyrightText: 2014 Vishesh Handa <me@vhanda.in>
0003     SPDX-FileCopyrightText: 2017 David Edmundson <davidedmundson@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.1-or-later
0006 */
0007 
0008 #include "baloosearchrunner.h"
0009 
0010 #include <KLocalizedString>
0011 #include <QApplication>
0012 #include <QDBusConnection>
0013 #include <QDir>
0014 #include <QIcon>
0015 #include <QMimeData>
0016 #include <QMimeDatabase>
0017 #include <QTimer>
0018 
0019 #include <Baloo/IndexerConfig>
0020 #include <Baloo/Query>
0021 
0022 #include <KIO/JobUiDelegate>
0023 #include <KIO/JobUiDelegateFactory>
0024 #include <KIO/OpenFileManagerWindowJob>
0025 #include <KIO/OpenUrlJob>
0026 #include <KNotificationJobUiDelegate>
0027 #include <KShell>
0028 
0029 #include "krunner1adaptor.h"
0030 
0031 static const QString s_openParentDirId = QStringLiteral("openParentDir");
0032 
0033 int main(int argc, char **argv)
0034 {
0035     QCoreApplication::setAttribute(Qt::AA_DisableSessionManager);
0036     QApplication::setQuitOnLastWindowClosed(false);
0037     QApplication app(argc, argv); // KRun needs widgets for error message boxes
0038     SearchRunner r;
0039     return app.exec();
0040 }
0041 
0042 SearchRunner::SearchRunner(QObject *parent)
0043     : QObject(parent)
0044 {
0045     new Krunner1Adaptor(this);
0046     qDBusRegisterMetaType<RemoteMatch>();
0047     qDBusRegisterMetaType<RemoteMatches>();
0048     qDBusRegisterMetaType<RemoteAction>();
0049     qDBusRegisterMetaType<RemoteActions>();
0050     QDBusConnection::sessionBus().registerObject(QStringLiteral("/runner"), this);
0051     QDBusConnection::sessionBus().registerService(QStringLiteral("org.kde.runners.baloo"));
0052 }
0053 
0054 RemoteActions SearchRunner::Actions()
0055 {
0056     Baloo::IndexerConfig config;
0057     if (!config.fileIndexingEnabled()) {
0058         sendErrorReply(QDBusError::ErrorType::NotSupported);
0059     }
0060     return RemoteActions({RemoteAction{s_openParentDirId, i18n("Open Containing Folder"), QStringLiteral("document-open-folder")}});
0061 }
0062 
0063 RemoteMatches SearchRunner::Match(const QString &searchTerm)
0064 {
0065     Baloo::IndexerConfig config;
0066     if (!config.fileIndexingEnabled()) {
0067         sendErrorReply(QDBusError::ErrorType::NotSupported);
0068         return {};
0069     }
0070 
0071     // Do not try to show results for queries starting with =
0072     // this should trigger the calculator, but the AdvancedQueryParser::parse method
0073     // in baloo interpreted it as an operator, BUG 345134
0074     if (searchTerm.startsWith(QLatin1Char('='))) {
0075         return RemoteMatches();
0076     }
0077 
0078     // Filter out duplicates
0079     QSet<QUrl> foundUrls;
0080 
0081     RemoteMatches matches;
0082     matches << matchInternal(searchTerm, QStringList(QStringLiteral("Audio")), i18n("Audios"), foundUrls);
0083     matches << matchInternal(searchTerm, QStringList(QStringLiteral("Image")), i18n("Images"), foundUrls);
0084     matches << matchInternal(searchTerm, QStringList(QStringLiteral("Video")), i18n("Videos"), foundUrls);
0085     matches << matchInternal(searchTerm, QStringList(QStringLiteral("Spreadsheet")), i18n("Spreadsheets"), foundUrls);
0086     matches << matchInternal(searchTerm, QStringList(QStringLiteral("Presentation")), i18n("Presentations"), foundUrls);
0087     matches << matchInternal(searchTerm, QStringList(QStringLiteral("Folder")), i18n("Folders"), foundUrls);
0088     matches << matchInternal(searchTerm, QStringList(QStringLiteral("Document")), i18n("Documents"), foundUrls);
0089     matches << matchInternal(searchTerm, QStringList(QStringLiteral("Archive")), i18n("Archives"), foundUrls);
0090     matches << matchInternal(searchTerm, QStringList(QStringLiteral("Text")), i18n("Texts"), foundUrls);
0091     matches << matchInternal(searchTerm, QStringList(), i18n("Files"), foundUrls);
0092 
0093     return matches;
0094 }
0095 
0096 RemoteMatches SearchRunner::matchInternal(const QString &searchTerm, const QStringList &types, const QString &category, QSet<QUrl> &foundUrls)
0097 {
0098     Baloo::Query query;
0099     query.setSearchString(searchTerm);
0100     query.setTypes(types);
0101     query.setLimit(10);
0102 
0103     Baloo::ResultIterator it = query.exec();
0104 
0105     RemoteMatches matches;
0106 
0107     QMimeDatabase mimeDb;
0108 
0109     // KRunner is absolutely daft and allows plugins to set the global
0110     // relevance levels. so Baloo should not set the relevance of results too
0111     // high because then Applications will often appear after if the application
0112     // runner has not a higher relevance. So stupid.
0113     // Each runner plugin should not have to know about the others.
0114     // Anyway, that's why we're starting with .75
0115     float relevance = .75;
0116     while (it.next()) {
0117         RemoteMatch match;
0118         QString localUrl = it.filePath();
0119         const QUrl url = QUrl::fromLocalFile(localUrl);
0120 
0121         if (foundUrls.contains(url)) {
0122             continue;
0123         }
0124 
0125         foundUrls.insert(url);
0126 
0127         match.id = url.toString();
0128         match.text = url.fileName();
0129         match.iconName = mimeDb.mimeTypeForFile(localUrl).iconName();
0130         match.relevance = relevance;
0131         const QString baseName = QFileInfo(url.fileName()).completeBaseName();
0132         bool isExactMatch = url.fileName().compare(searchTerm, Qt::CaseInsensitive) == 0 || baseName.compare(searchTerm, Qt::CaseInsensitive) == 0;
0133         bool isPossibleMatch = url.fileName().contains(searchTerm, Qt::CaseInsensitive);
0134         KRunner::QueryMatch::CategoryRelevance categoryRelevance = isExactMatch ? KRunner::QueryMatch::CategoryRelevance::Highest
0135             : isPossibleMatch                                                   ? KRunner::QueryMatch::CategoryRelevance::High
0136                                                                                 : KRunner::QueryMatch::CategoryRelevance::Low;
0137         match.categoryRelevance = qToUnderlying(categoryRelevance);
0138         QVariantMap properties;
0139 
0140         QString folderPath = url.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).toLocalFile();
0141         folderPath = KShell::tildeCollapse(folderPath);
0142 
0143         properties[QStringLiteral("urls")] = QStringList({QString::fromLocal8Bit(url.toEncoded())});
0144         properties[QStringLiteral("subtext")] = folderPath;
0145         properties[QStringLiteral("category")] = category;
0146 
0147         match.properties = properties;
0148         relevance -= 0.05;
0149 
0150         matches << match;
0151     }
0152 
0153     return matches;
0154 }
0155 
0156 void SearchRunner::Run(const QString &id, const QString &actionId)
0157 {
0158     const QUrl url(id);
0159     if (actionId == s_openParentDirId) {
0160         KIO::highlightInFileManager({url});
0161         return;
0162     }
0163 
0164     auto *job = new KIO::OpenUrlJob(url);
0165     job->setUiDelegate(KIO::createDefaultJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, QApplication::activeWindow()));
0166     job->setShowOpenOrExecuteDialog(true);
0167     job->start();
0168 }