/* SPDX-FileCopyrightText: 2008 David Edmundson SPDX-License-Identifier: LGPL-2.0-or-later */ #include "placesrunner.h" #include #include #include #include #include #include #include #include #include #include K_PLUGIN_CLASS_WITH_JSON(PlacesRunner, "plasma-runner-places.json") // Q_DECLARE_METATYPE(Plasma::RunnerContext) PlacesRunner::PlacesRunner(QObject *parent, const KPluginMetaData &metaData, const QVariantList &args) : Plasma::AbstractRunner(parent, metaData, args) { setObjectName(QStringLiteral("Places")); Plasma::RunnerSyntax defaultSyntax(i18n("places"), i18n("Lists all file manager locations")); addSyntax(defaultSyntax); addSyntax(Plasma::RunnerSyntax(QStringLiteral(":q:"), i18n("Finds file manager locations that match :q:"))); // ensure the bookmarkmanager, etc. in the places model gets creates created in the main thread // otherwise crashes ensue m_helper = new PlacesRunnerHelper(this); setMinLetterCount(3); } PlacesRunner::~PlacesRunner() { } void PlacesRunner::match(Plasma::RunnerContext &context) { if (QThread::currentThread() == QCoreApplication::instance()->thread()) { // from the main thread // qDebug() << "calling"; m_helper->match(&context); } else { // from the non-gui thread // qDebug() << "emitting"; Q_EMIT doMatch(&context); } // m_helper->match(c); } PlacesRunnerHelper::PlacesRunnerHelper(PlacesRunner *runner) : QObject(runner) { Q_ASSERT(QThread::currentThread() == QCoreApplication::instance()->thread()); connect(runner, &PlacesRunner::doMatch, this, &PlacesRunnerHelper::match, Qt::BlockingQueuedConnection); connect(&m_places, &KFilePlacesModel::setupDone, this, [this](const QModelIndex &index, bool success) { if (success && m_pendingUdi == m_places.deviceForIndex(index).udi()) { auto *job = new KIO::OpenUrlJob(m_places.url(index)); job->setUiDelegate(new KNotificationJobUiDelegate(KJobUiDelegate::AutoErrorHandlingEnabled)); job->setRunExecutables(false); job->start(); } m_pendingUdi.clear(); }); } void PlacesRunnerHelper::match(Plasma::RunnerContext *c) { Plasma::RunnerContext &context = *c; if (!context.isValid()) { return; } const QString term = context.query(); QList matches; const bool all = term.compare(i18n("places"), Qt::CaseInsensitive) == 0; for (int i = 0; i <= m_places.rowCount(); i++) { QModelIndex current_index = m_places.index(i, 0); Plasma::QueryMatch::Type type = Plasma::QueryMatch::NoMatch; qreal relevance = 0; const QString text = m_places.text(current_index); if ((all && !text.isEmpty()) || text.compare(term, Qt::CaseInsensitive) == 0) { type = Plasma::QueryMatch::ExactMatch; relevance = all ? 0.9 : 1.0; } else if (text.contains(term, Qt::CaseInsensitive)) { type = Plasma::QueryMatch::PossibleMatch; relevance = 0.7; } if (type != Plasma::QueryMatch::NoMatch) { Plasma::QueryMatch match(static_cast(parent())); match.setType(type); match.setRelevance(relevance); match.setIcon(m_places.icon(current_index)); match.setText(text); // Add category as subtext so one can tell "Pictures" folder from "Search for Pictures" // Don't add it if it would match the category ("Places") of the runner to avoid "Places: Pictures (Places)" const QString groupName = m_places.data(current_index, KFilePlacesModel::GroupRole).toString(); if (!groupName.isEmpty() && static_cast(parent())->name() != groupName) { match.setSubtext(groupName); } // if we have to mount it set the device udi instead of the URL, as we can't open it directly if (m_places.isDevice(current_index) && m_places.setupNeeded(current_index)) { const QString udi = m_places.deviceForIndex(current_index).udi(); match.setId(udi); match.setData(udi); } else { const QUrl url = KFilePlacesModel::convertedUrl(m_places.url(current_index)); match.setData(url); match.setUrls({url}); match.setId(url.toDisplayString()); } matches << match; } } context.addMatches(matches); } void PlacesRunnerHelper::openDevice(const QString &udi) { m_pendingUdi.clear(); for (int i = 0; i < m_places.rowCount(); ++i) { const QModelIndex idx = m_places.index(i, 0); if (m_places.isDevice(idx) && m_places.deviceForIndex(idx).udi() == udi) { m_pendingUdi = udi; m_places.requestSetup(idx); break; } } } void PlacesRunner::run(const Plasma::RunnerContext &context, const Plasma::QueryMatch &action) { Q_UNUSED(context); // I don't just pass the model index because the list could change before the user clicks on it, which would make everything go wrong. Ideally we don't want // things to go wrong. if (action.data().type() == QVariant::Url) { auto *job = new KIO::OpenUrlJob(action.data().toUrl()); job->setUiDelegate(new KNotificationJobUiDelegate(KJobUiDelegate::AutoErrorHandlingEnabled)); job->setRunExecutables(false); job->start(); } else if (action.data().canConvert()) { m_helper->openDevice(action.data().toString()); } } #include "placesrunner.moc"