baloosearchrunner.cpp 7.05 KB
Newer Older
1
2
3
/*
 * This file is part of the KDE Baloo Project
 * Copyright (C) 2014  Vishesh Handa <me@vhanda.in>
4
 * Copyright (C) 2017 David Edmundson <davidedmundson@kde.org>
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

#include "baloosearchrunner.h"

24
#include <QAction>
25
26
27
28
29
#include <QIcon>
#include <QDir>
#include <KRun>
#include <KRunner/QueryMatch>
#include <KLocalizedString>
30
#include <QMimeDatabase>
31
#include <QTimer>
32
#include <QMimeData>
33
34
#include <QApplication>
#include <QDBusConnection>
35

36
#include <Baloo/Query>
37
#include <Baloo/IndexerConfig>
38

39
40
#include <KIO/OpenFileManagerWindowJob>

41
42
43
#include "dbusutils_p.h"
#include "krunner1adaptor.h"

44
45
static const QString s_openParentDirId = QStringLiteral("openParentDir");

46
int main (int argc, char **argv)
47
{
48
49
50
51
52
53
54
55
    Baloo::IndexerConfig config;
    if (!config.fileIndexingEnabled()) {
        return -1;
    }
    QApplication::setQuitOnLastWindowClosed(false);
    QApplication app(argc, argv); //KRun needs widgets for error message boxes
    SearchRunner r;
    return app.exec();
56
57
}

58
59
60
SearchRunner::SearchRunner(QObject* parent)
    : QObject(parent),
    m_timer(new QTimer(this))
61
62
{

63
64
    m_timer->setSingleShot(true);
    connect(m_timer, &QTimer::timeout, this, &SearchRunner::performMatch);
65

66
67
68
69
70
    new Krunner1Adaptor(this);
    qDBusRegisterMetaType<RemoteMatch>();
    qDBusRegisterMetaType<RemoteMatches>();
    qDBusRegisterMetaType<RemoteAction>();
    qDBusRegisterMetaType<RemoteActions>();
71
72
    QDBusConnection::sessionBus().registerObject(QStringLiteral("/runner"), this);
    QDBusConnection::sessionBus().registerService(QStringLiteral("org.kde.runners.baloo"));
73
74
75
76
77
78
}

SearchRunner::~SearchRunner()
{
}

79
RemoteActions SearchRunner::Actions()
80
{
81
82
83
84
85
    return RemoteActions({RemoteAction{
        s_openParentDirId,
        i18n("Open Containing Folder"),
        QStringLiteral("document-open-folder")
    }});
86
87
}

88
RemoteMatches SearchRunner::Match(const QString& searchTerm)
89
{
90
91
92
93
94
95
    // Do not try to show results for queries starting with =
    // this should trigger the calculator, but the AdvancedQueryParser::parse method
    // in baloo interpreted it as an operator, BUG 345134
    if (searchTerm.startsWith(QLatin1Char('='))) {
        return RemoteMatches();
    }
96
97
98
99
    setDelayedReply(true);

    if (m_lastRequest.type() != QDBusMessage::InvalidMessage) {
         QDBusConnection::sessionBus().send(m_lastRequest.createReply(QVariantList()));
100
101
    }

102
103
    m_lastRequest = message();
    m_searchTerm = searchTerm;
104

105
106
107
108
109
110
111
112
113
114
115
    // Baloo (as of 2014-11-20) is single threaded. It has an internal mutex which results in
    // queries being queued one after another. Also, Baloo is really really slow for small queries
    // For example - on my SSD, it takes about 1.4 seconds for 'f' with an SSD.
    // When searching for "fire", it results in "f", "fi", "fir" and then "fire" being searched
    // We're therefore hacking around this by having a small delay for very short queries so that
    // they do not get queued internally in Baloo
    //
    int waitTimeMs = 0;

    if (searchTerm.length() <= 3) {
        waitTimeMs = 100;
116
    }
117
118
    //we're still using the event delayed call even when the length is < 3 so that if we have multiple Match() calls in our DBus queue, we only process the last one
    m_timer->start(waitTimeMs);
119

120
121
    return RemoteMatches();
}
122

123
124
void SearchRunner::performMatch()
{
125
126
127
    // Filter out duplicates
    QSet<QUrl> foundUrls;

128
    RemoteMatches matches;
129
130
131
132
133
134
135
136
    matches << matchInternal(m_searchTerm, QStringLiteral("Audio"), i18n("Audio"), foundUrls);
    matches << matchInternal(m_searchTerm, QStringLiteral("Image"), i18n("Image"), foundUrls);
    matches << matchInternal(m_searchTerm, QStringLiteral("Video"), i18n("Video"), foundUrls);
    matches << matchInternal(m_searchTerm, QStringLiteral("Spreadsheet"), i18n("Spreadsheet"), foundUrls);
    matches << matchInternal(m_searchTerm, QStringLiteral("Presentation"), i18n("Presentation"), foundUrls);
    matches << matchInternal(m_searchTerm, QStringLiteral("Folder"), i18n("Folder"), foundUrls);
    matches << matchInternal(m_searchTerm, QStringLiteral("Document"), i18n("Document"), foundUrls);
    matches << matchInternal(m_searchTerm, QStringLiteral("Archive"), i18n("Archive"), foundUrls);
137
138
139
140

    QDBusConnection::sessionBus().send(m_lastRequest.createReply(QVariant::fromValue(matches)));
    m_lastRequest = QDBusMessage();
}
141

142
RemoteMatches SearchRunner::matchInternal(const QString& searchTerm, const QString &type, const QString &category, QSet<QUrl> &foundUrls)
143
{
144
    Baloo::Query query;
145
    query.setSearchString(searchTerm);
146
147
148
149
150
    query.setType(type);
    query.setLimit(10);

    Baloo::ResultIterator it = query.exec();

151
    RemoteMatches matches;
152

153
154
    QMimeDatabase mimeDb;

155
    // KRunner is absolutely daft and allows plugins to set the global
156
157
158
159
160
161
    // relevance levels. so Baloo should not set the relevance of results too
    // high because then Applications will often appear after if the application
    // runner has not a higher relevance. So stupid.
    // Each runner plugin should not have to know about the others.
    // Anyway, that's why we're starting with .75
    float relevance = .75;
162
163
    while (it.next()) {
        RemoteMatch match;
Vishesh Handa's avatar
Vishesh Handa committed
164
165
        QString localUrl = it.filePath();
        const QUrl url = QUrl::fromLocalFile(localUrl);
166
167
168
169
170
171
172

        if (foundUrls.contains(url)) {
            continue;
        }

        foundUrls.insert(url);

173
174
175
176
177
178
        match.id = it.filePath();
        match.text = url.fileName();
        match.iconName = mimeDb.mimeTypeForFile(localUrl).iconName();
        match.relevance = relevance;
        match.type = Plasma::QueryMatch::PossibleMatch;
        QVariantMap properties;
179

180
181
182
        QString folderPath = url.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).toLocalFile();
        if (folderPath.startsWith(QDir::homePath())) {
            folderPath.replace(0, QDir::homePath().length(), QStringLiteral("~"));
183
184
        }

185
186
187
        properties[QStringLiteral("urls")] = QStringList({QString::fromLocal8Bit(url.toEncoded())});
        properties[QStringLiteral("subtext")] = folderPath;
        properties[QStringLiteral("category")] = category;
188

189
190
        match.properties = properties;
        relevance -= 0.05;
191

192
         matches << match;
193
194
    }

195
    return matches;
196
197
}

198
void SearchRunner::Run(const QString& id, const QString& actionId)
199
{
200
201
    const QUrl url = QUrl::fromLocalFile(id);
    if (actionId == s_openParentDirId) {
202
203
        KIO::highlightInFileManager({url});
        return;
204
205
    }

Friedrich W. H. Kossebau's avatar
Friedrich W. H. Kossebau committed
206
    new KRun(url, nullptr);
207
}