You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
337 lines
8.8 KiB
337 lines
8.8 KiB
/*************************************************************************** |
|
* Copyright (C) 2012 by Aurélien Gâteau <agateau@kde.org> * |
|
* Copyright (C) 2014 by Eike Hein <hein@kde.org> * |
|
* * |
|
* This program is free software; you can redistribute it and/or modify * |
|
* it under the terms of the GNU General Public License as published by * |
|
* the Free Software Foundation; either version 2 of the License, or * |
|
* (at your option) any later version. * |
|
* * |
|
* This program 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 General Public License for more details. * |
|
* * |
|
* You should have received a copy of the GNU General Public License * |
|
* along with this program; if not, write to the * |
|
* Free Software Foundation, Inc., * |
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . * |
|
***************************************************************************/ |
|
|
|
#include "runnermodel.h" |
|
#include "runnermatchesmodel.h" |
|
|
|
#include <QSet> |
|
|
|
#include <KLocalizedString> |
|
#include <KRunner/AbstractRunner> |
|
#include <KRunner/RunnerManager> |
|
|
|
RunnerModel::RunnerModel(QObject *parent) : QAbstractListModel(parent) |
|
, m_favoritesModel(nullptr) |
|
, m_appletInterface(nullptr) |
|
, m_runnerManager(nullptr) |
|
, m_mergeResults(false) |
|
, m_deleteWhenEmpty(false) |
|
{ |
|
m_queryTimer.setSingleShot(true); |
|
m_queryTimer.setInterval(10); |
|
connect(&m_queryTimer, &QTimer::timeout, this, &RunnerModel::startQuery); |
|
} |
|
|
|
RunnerModel::~RunnerModel() |
|
{ |
|
} |
|
|
|
QHash<int, QByteArray> RunnerModel::roleNames() const |
|
{ |
|
return {{ Qt::DisplayRole, "display" }}; |
|
} |
|
|
|
AbstractModel *RunnerModel::favoritesModel() const |
|
{ |
|
return m_favoritesModel; |
|
} |
|
|
|
void RunnerModel::setFavoritesModel(AbstractModel *model) |
|
{ |
|
if (m_favoritesModel != model) { |
|
m_favoritesModel = model; |
|
|
|
clear(); |
|
|
|
if (!m_query.isEmpty()) { |
|
m_queryTimer.start(); |
|
} |
|
|
|
emit favoritesModelChanged(); |
|
} |
|
} |
|
|
|
QObject *RunnerModel::appletInterface() const |
|
{ |
|
return m_appletInterface; |
|
} |
|
|
|
void RunnerModel::setAppletInterface(QObject *appletInterface) |
|
{ |
|
if (m_appletInterface != appletInterface) { |
|
m_appletInterface = appletInterface; |
|
|
|
clear(); |
|
|
|
if (!m_query.isEmpty()) { |
|
m_queryTimer.start(); |
|
} |
|
|
|
emit appletInterfaceChanged(); |
|
} |
|
} |
|
|
|
bool RunnerModel::deleteWhenEmpty() const |
|
{ |
|
return m_deleteWhenEmpty; |
|
} |
|
|
|
void RunnerModel::setDeleteWhenEmpty(bool deleteWhenEmpty) |
|
{ |
|
if (m_deleteWhenEmpty != deleteWhenEmpty) { |
|
m_deleteWhenEmpty = deleteWhenEmpty; |
|
|
|
clear(); |
|
|
|
if (!m_query.isEmpty()) { |
|
m_queryTimer.start(); |
|
} |
|
|
|
emit deleteWhenEmptyChanged(); |
|
} |
|
} |
|
|
|
bool RunnerModel::mergeResults() const |
|
{ |
|
return m_mergeResults; |
|
} |
|
|
|
void RunnerModel::setMergeResults(bool merge) |
|
{ |
|
if (m_mergeResults != merge) { |
|
m_mergeResults = merge; |
|
|
|
clear(); |
|
|
|
if (!m_query.isEmpty()) { |
|
m_queryTimer.start(); |
|
} |
|
|
|
emit mergeResultsChanged(); |
|
} |
|
} |
|
|
|
QVariant RunnerModel::data(const QModelIndex &index, int role) const |
|
{ |
|
if (!index.isValid() || index.row() >= m_models.count()) { |
|
return QVariant(); |
|
} |
|
|
|
if (role == Qt::DisplayRole) { |
|
return m_models.at(index.row())->name(); |
|
} |
|
|
|
return QVariant(); |
|
} |
|
|
|
int RunnerModel::rowCount(const QModelIndex &parent) const |
|
{ |
|
return parent.isValid() ? 0 : m_models.count(); |
|
} |
|
|
|
int RunnerModel::count() const |
|
{ |
|
return rowCount(); |
|
} |
|
|
|
QObject *RunnerModel::modelForRow(int row) |
|
{ |
|
if (row < 0 || row >= m_models.count()) { |
|
return nullptr; |
|
} |
|
|
|
return m_models.at(row); |
|
} |
|
|
|
QStringList RunnerModel::runners() const |
|
{ |
|
return m_runners; |
|
} |
|
|
|
void RunnerModel::setRunners(const QStringList &runners) |
|
{ |
|
if (m_runners.toSet() != runners.toSet()) { |
|
m_runners = runners; |
|
|
|
if (m_runnerManager) { |
|
m_runnerManager->setAllowedRunners(runners); |
|
} |
|
|
|
emit runnersChanged(); |
|
} |
|
} |
|
|
|
QString RunnerModel::query() const |
|
{ |
|
return m_query; |
|
} |
|
|
|
void RunnerModel::setQuery(const QString &query) |
|
{ |
|
if (m_query != query) { |
|
m_query = query; |
|
|
|
m_queryTimer.start(); |
|
|
|
emit queryChanged(); |
|
} |
|
} |
|
|
|
void RunnerModel::startQuery() |
|
{ |
|
if (m_query.isEmpty()) { |
|
clear(); |
|
} |
|
|
|
if (m_query.isEmpty() && m_runnerManager) { |
|
return; |
|
} |
|
|
|
createManager(); |
|
|
|
m_runnerManager->launchQuery(m_query); |
|
} |
|
|
|
void RunnerModel::matchesChanged(const QList<Plasma::QueryMatch> &matches) |
|
{ |
|
// Group matches by runner. |
|
// We do not use a QMultiHash here because it keeps values in LIFO order, while we want FIFO. |
|
QHash<QString, QList<Plasma::QueryMatch> > matchesForRunner; |
|
|
|
foreach (const Plasma::QueryMatch &match, matches) { |
|
auto it = matchesForRunner.find(match.runner()->id()); |
|
|
|
if (it == matchesForRunner.end()) { |
|
it = matchesForRunner.insert(match.runner()->id(), QList<Plasma::QueryMatch>()); |
|
} |
|
|
|
it.value().append(match); |
|
} |
|
|
|
// Sort matches for all runners in descending order. This allows the best |
|
// match to win whilest preserving order between runners. |
|
for (auto &list : matchesForRunner) { |
|
std::sort(list.begin(), list.end(), qGreater<Plasma::QueryMatch>()); |
|
} |
|
|
|
if (m_mergeResults) { |
|
RunnerMatchesModel *matchesModel = nullptr; |
|
|
|
if (m_models.isEmpty()) { |
|
matchesModel = new RunnerMatchesModel(QString(), i18n("Search results"), |
|
m_runnerManager, this); |
|
|
|
beginInsertRows(QModelIndex(), 0, 0); |
|
m_models.append(matchesModel); |
|
endInsertRows(); |
|
emit countChanged(); |
|
} else { |
|
matchesModel = m_models.at(0); |
|
} |
|
|
|
QList<Plasma::QueryMatch> matches; |
|
|
|
foreach (const QString &runnerId, m_runners) { |
|
matches.append(matchesForRunner.take(runnerId)); |
|
} |
|
|
|
matchesModel->setMatches(matches); |
|
|
|
return; |
|
} |
|
|
|
// Assign matches to existing models. If there is no match for a model, delete it. |
|
for (int row = m_models.count() - 1; row >= 0; --row) { |
|
RunnerMatchesModel *matchesModel = m_models.at(row); |
|
QList<Plasma::QueryMatch> matches = matchesForRunner.take(matchesModel->runnerId()); |
|
|
|
if (m_deleteWhenEmpty && matches.isEmpty()) { |
|
beginRemoveRows(QModelIndex(), row, row); |
|
m_models.removeAt(row); |
|
delete matchesModel; |
|
endRemoveRows(); |
|
emit countChanged(); |
|
} else { |
|
matchesModel->setMatches(matches); |
|
} |
|
} |
|
|
|
// At this point, matchesForRunner contains only matches for runners which |
|
// do not have a model yet. Create new models for them. |
|
if (!matchesForRunner.isEmpty()) { |
|
auto it = matchesForRunner.constBegin(); |
|
auto end = matchesForRunner.constEnd(); |
|
int appendCount = 0; |
|
|
|
for (; it != end; ++it) { |
|
QList<Plasma::QueryMatch> matches = it.value(); |
|
Q_ASSERT(!matches.isEmpty()); |
|
RunnerMatchesModel *matchesModel = new RunnerMatchesModel(it.key(), |
|
matches.first().runner()->name(), m_runnerManager, this); |
|
matchesModel->setMatches(matches); |
|
|
|
if (it.key() == QLatin1String("services")) { |
|
beginInsertRows(QModelIndex(), 0, 0); |
|
m_models.prepend(matchesModel); |
|
endInsertRows(); |
|
emit countChanged(); |
|
} else { |
|
m_models.append(matchesModel); |
|
++appendCount; |
|
} |
|
} |
|
|
|
if (appendCount > 0) { |
|
beginInsertRows(QModelIndex(), rowCount() - appendCount, rowCount() - 1); |
|
endInsertRows(); |
|
emit countChanged(); |
|
} |
|
} |
|
} |
|
|
|
void RunnerModel::createManager() |
|
{ |
|
if (!m_runnerManager) { |
|
m_runnerManager = new Plasma::RunnerManager(this); // FIXME: Which KConfigGroup is this using now? |
|
m_runnerManager->setAllowedRunners(m_runners); |
|
connect(m_runnerManager, &Plasma::RunnerManager::matchesChanged, |
|
this, &RunnerModel::matchesChanged); |
|
} |
|
} |
|
|
|
void RunnerModel::clear() |
|
{ |
|
if (m_runnerManager) { |
|
m_runnerManager->reset(); |
|
} |
|
|
|
if (m_models.isEmpty()) { |
|
return; |
|
} |
|
|
|
beginResetModel(); |
|
|
|
qDeleteAll(m_models); |
|
m_models.clear(); |
|
|
|
endResetModel(); |
|
|
|
emit countChanged(); |
|
}
|
|
|