3
0
mirror of https://github.com/Qortal/Brooklyn.git synced 2025-02-23 15:45:53 +00:00

353 lines
8.9 KiB
C++
Raw Normal View History

2022-03-05 22:41:29 +05:00
/*
SPDX-FileCopyrightText: 2012 Aurélien Gâteau <agateau@kde.org>
SPDX-FileCopyrightText: 2014 Eike Hein <hein@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#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();
}
Q_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();
}
Q_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();
}
Q_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();
}
Q_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 (QSet<QString>(runners.cbegin(), runners.cend()) != QSet<QString>(m_runners.cbegin(), m_runners.cend())) {
m_runners = runners;
if (m_runnerManager) {
m_runnerManager->setAllowedRunners(runners);
}
Q_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();
Q_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;
for (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, note the reverse iterators. This allows the best
// match to win whilest preserving order between runners.
for (auto &list : matchesForRunner) {
std::sort(list.rbegin(), list.rend());
}
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();
Q_EMIT countChanged();
} else {
matchesModel = m_models.at(0);
}
QList<Plasma::QueryMatch> matches;
// To preserve the old behavior when allowing all runners we use static sorting
const static QStringList runnerIds = {
QStringLiteral("desktopsessions"),
QStringLiteral("services"),
QStringLiteral("places"),
QStringLiteral("PowerDevil"),
QStringLiteral("calculator"),
QStringLiteral("unitconverter"),
QStringLiteral("shell"),
QStringLiteral("bookmarks"),
QStringLiteral("recentdocuments"),
QStringLiteral("locations"),
};
if (m_runners.isEmpty()) {
const auto baloo = matchesForRunner.take(QStringLiteral("baloosearch"));
const auto appstream = matchesForRunner.take(QStringLiteral("krunner_appstream"));
for (const QString &runnerId : runnerIds) {
matches.append(matchesForRunner.take(runnerId));
}
for (const auto &match : matchesForRunner) {
matches.append(match);
}
matches.append(baloo);
matches.append(appstream);
} else {
for (const QString &runnerId : qAsConst(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();
Q_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();
Q_EMIT countChanged();
} else {
m_models.append(matchesModel);
++appendCount;
}
}
if (appendCount > 0) {
beginInsertRows(QModelIndex(), rowCount() - appendCount, rowCount() - 1);
endInsertRows();
Q_EMIT countChanged();
}
}
}
void RunnerModel::createManager()
{
if (!m_runnerManager) {
m_runnerManager = new Plasma::RunnerManager(QStringLiteral("krunnerrc"), this);
if (m_runners.isEmpty()) {
m_runnerManager->enableKNotifyPluginWatcher();
} else {
m_runnerManager->setAllowedRunners(m_runners);
}
connect(m_runnerManager, &Plasma::RunnerManager::matchesChanged, this, &RunnerModel::matchesChanged);
}
}
void RunnerModel::clear()
{
if (m_runnerManager) {
m_runnerManager->reset();
m_runnerManager->matchSessionComplete();
}
if (m_models.isEmpty()) {
return;
}
beginResetModel();
qDeleteAll(m_models);
m_models.clear();
endResetModel();
Q_EMIT countChanged();
}