1 /*
2 SPDX-FileCopyrightText: 2014 Vishesh Handa <me@vhanda.in>
3 SPDX-FileCopyrightText: 2017 David Edmundson <davidedmundson@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.1-or-later
6 */
7
8 #include "baloosearchrunner.h"
9
10 #include <KLocalizedString>
11 #include <QAction>
12 #include <QApplication>
13 #include <QDBusConnection>
14 #include <QDir>
15 #include <QIcon>
16 #include <QMimeData>
17 #include <QMimeDatabase>
18 #include <QTimer>
19
20 #include <Baloo/IndexerConfig>
21 #include <Baloo/Query>
22
23 #include <KIO/OpenFileManagerWindowJob>
24 #include <KIO/OpenUrlJob>
25 #include <KNotificationJobUiDelegate>
26 #include <KShell>
27
28 #include "krunner1adaptor.h"
29
30 static const QString s_openParentDirId = QStringLiteral("openParentDir");
31
main(int argc,char ** argv)32 int main(int argc, char **argv)
33 {
34 QCoreApplication::setAttribute(Qt::AA_DisableSessionManager);
35 QApplication::setQuitOnLastWindowClosed(false);
36 QApplication app(argc, argv); // KRun needs widgets for error message boxes
37 SearchRunner r;
38 return app.exec();
39 }
40
SearchRunner(QObject * parent)41 SearchRunner::SearchRunner(QObject *parent)
42 : QObject(parent)
43 {
44 new Krunner1Adaptor(this);
45 qDBusRegisterMetaType<RemoteMatch>();
46 qDBusRegisterMetaType<RemoteMatches>();
47 qDBusRegisterMetaType<RemoteAction>();
48 qDBusRegisterMetaType<RemoteActions>();
49 QDBusConnection::sessionBus().registerObject(QStringLiteral("/runner"), this);
50 QDBusConnection::sessionBus().registerService(QStringLiteral("org.kde.runners.baloo"));
51 }
52
~SearchRunner()53 SearchRunner::~SearchRunner()
54 {
55 }
56
Actions()57 RemoteActions SearchRunner::Actions()
58 {
59 Baloo::IndexerConfig config;
60 if (!config.fileIndexingEnabled()) {
61 sendErrorReply(QDBusError::ErrorType::NotSupported);
62 }
63 return RemoteActions({RemoteAction{s_openParentDirId, i18n("Open Containing Folder"), QStringLiteral("document-open-folder")}});
64 }
65
Match(const QString & searchTerm)66 RemoteMatches SearchRunner::Match(const QString &searchTerm)
67 {
68 Baloo::IndexerConfig config;
69 if (!config.fileIndexingEnabled()) {
70 sendErrorReply(QDBusError::ErrorType::NotSupported);
71 return {};
72 }
73
74 // Do not try to show results for queries starting with =
75 // this should trigger the calculator, but the AdvancedQueryParser::parse method
76 // in baloo interpreted it as an operator, BUG 345134
77 if (searchTerm.startsWith(QLatin1Char('='))) {
78 return RemoteMatches();
79 }
80
81 // Filter out duplicates
82 QSet<QUrl> foundUrls;
83
84 RemoteMatches matches;
85 matches << matchInternal(searchTerm, QStringLiteral("Audio"), i18n("Audio"), foundUrls);
86 matches << matchInternal(searchTerm, QStringLiteral("Image"), i18n("Image"), foundUrls);
87 matches << matchInternal(searchTerm, QStringLiteral("Video"), i18n("Video"), foundUrls);
88 matches << matchInternal(searchTerm, QStringLiteral("Spreadsheet"), i18n("Spreadsheet"), foundUrls);
89 matches << matchInternal(searchTerm, QStringLiteral("Presentation"), i18n("Presentation"), foundUrls);
90 matches << matchInternal(searchTerm, QStringLiteral("Folder"), i18n("Folder"), foundUrls);
91 matches << matchInternal(searchTerm, QStringLiteral("Document"), i18n("Document"), foundUrls);
92 matches << matchInternal(searchTerm, QStringLiteral("Archive"), i18n("Archive"), foundUrls);
93
94 return matches;
95 }
96
matchInternal(const QString & searchTerm,const QString & type,const QString & category,QSet<QUrl> & foundUrls)97 RemoteMatches SearchRunner::matchInternal(const QString &searchTerm, const QString &type, const QString &category, QSet<QUrl> &foundUrls)
98 {
99 Baloo::Query query;
100 query.setSearchString(searchTerm);
101 query.setType(type);
102 query.setLimit(10);
103
104 Baloo::ResultIterator it = query.exec();
105
106 RemoteMatches matches;
107
108 QMimeDatabase mimeDb;
109
110 // KRunner is absolutely daft and allows plugins to set the global
111 // relevance levels. so Baloo should not set the relevance of results too
112 // high because then Applications will often appear after if the application
113 // runner has not a higher relevance. So stupid.
114 // Each runner plugin should not have to know about the others.
115 // Anyway, that's why we're starting with .75
116 float relevance = .75;
117 while (it.next()) {
118 RemoteMatch match;
119 QString localUrl = it.filePath();
120 const QUrl url = QUrl::fromLocalFile(localUrl);
121
122 if (foundUrls.contains(url)) {
123 continue;
124 }
125
126 foundUrls.insert(url);
127
128 match.id = url.toString();
129 match.text = url.fileName();
130 match.iconName = mimeDb.mimeTypeForFile(localUrl).iconName();
131 match.relevance = relevance;
132 match.type = Plasma::QueryMatch::PossibleMatch;
133 QVariantMap properties;
134
135 QString folderPath = url.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).toLocalFile();
136 folderPath = KShell::tildeCollapse(folderPath);
137
138 properties[QStringLiteral("urls")] = QStringList({QString::fromLocal8Bit(url.toEncoded())});
139 properties[QStringLiteral("subtext")] = folderPath;
140 properties[QStringLiteral("category")] = category;
141
142 match.properties = properties;
143 relevance -= 0.05;
144
145 matches << match;
146 }
147
148 return matches;
149 }
150
Run(const QString & id,const QString & actionId)151 void SearchRunner::Run(const QString &id, const QString &actionId)
152 {
153 const QUrl url(id);
154 if (actionId == s_openParentDirId) {
155 KIO::highlightInFileManager({url});
156 return;
157 }
158
159 auto *job = new KIO::OpenUrlJob(url);
160 job->setUiDelegate(new KNotificationJobUiDelegate(KJobUiDelegate::AutoErrorHandlingEnabled));
161 job->setRunExecutables(false);
162 job->start();
163 }
164