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