File indexing completed on 2024-05-12 05:38:20

0001 /*
0002     SPDX-FileCopyrightText: 2008 David Edmundson <kde@davidedmundson.co.uk>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "placesrunner.h"
0008 
0009 #include <QCoreApplication>
0010 #include <QTimer>
0011 
0012 #include <QDebug>
0013 #include <QIcon>
0014 #include <QMimeData>
0015 #include <QUrl>
0016 
0017 #include <KIO/OpenUrlJob>
0018 #include <KLocalizedString>
0019 #include <KNotificationJobUiDelegate>
0020 #include <qttypetraits.h>
0021 
0022 K_PLUGIN_CLASS_WITH_JSON(PlacesRunner, "plasma-runner-places.json")
0023 
0024 PlacesRunner::PlacesRunner(QObject *parent, const KPluginMetaData &metaData)
0025     : KRunner::AbstractRunner(parent, metaData)
0026 {
0027     addSyntax(i18n("places"), i18n("Lists all file manager locations"));
0028     addSyntax(QStringLiteral(":q:"), i18n("Finds file manager locations that match :q:"));
0029 
0030     setMinLetterCount(3);
0031 }
0032 
0033 void PlacesRunner::init()
0034 {
0035     Q_ASSERT(!m_places);
0036     m_places = new KFilePlacesModel(this);
0037     connect(m_places, &KFilePlacesModel::setupDone, this, [this](const QModelIndex &index, bool success) {
0038         if (success && m_pendingUdi == m_places->deviceForIndex(index).udi()) {
0039             auto *job = new KIO::OpenUrlJob(m_places->url(index));
0040             job->setUiDelegate(new KNotificationJobUiDelegate(KJobUiDelegate::AutoErrorHandlingEnabled));
0041             job->setRunExecutables(false);
0042             job->start();
0043         }
0044         m_pendingUdi.clear();
0045     });
0046 }
0047 void PlacesRunner::match(KRunner::RunnerContext &context)
0048 {
0049     const QString term = context.query();
0050     QList<KRunner::QueryMatch> matches;
0051     const bool all = term.compare(i18n("places"), Qt::CaseInsensitive) == 0;
0052     for (int i = 0; i <= m_places->rowCount(); i++) {
0053         QModelIndex current_index = m_places->index(i, 0);
0054         qreal relevance = 0;
0055 
0056         const QString text = m_places->text(current_index);
0057         int categoryRelevance = 0;
0058         if ((all && !text.isEmpty()) || text.compare(term, Qt::CaseInsensitive) == 0) {
0059             categoryRelevance = qToUnderlying(KRunner::QueryMatch::CategoryRelevance::Highest);
0060             relevance = all ? 0.9 : 1.0;
0061         } else if (text.contains(term, Qt::CaseInsensitive)) {
0062             categoryRelevance = qToUnderlying(KRunner::QueryMatch::CategoryRelevance::High);
0063             relevance = 0.7;
0064         } else {
0065             continue;
0066         }
0067 
0068         KRunner::QueryMatch match(this);
0069         match.setCategoryRelevance(categoryRelevance);
0070         match.setRelevance(relevance);
0071         match.setIcon(m_places->icon(current_index));
0072         match.setText(text);
0073 
0074         // Add category as subtext so one can tell "Pictures" folder from "Search for Pictures"
0075         // Don't add it if it would match the category ("Places") of the runner to avoid "Places: Pictures (Places)"
0076         const QString groupName = m_places->data(current_index, KFilePlacesModel::GroupRole).toString();
0077         if (!groupName.isEmpty() && name() != groupName) {
0078             match.setSubtext(groupName);
0079         }
0080 
0081         // if we have to mount it set the device udi instead of the URL, as we can't open it directly
0082         if (m_places->isDevice(current_index) && m_places->setupNeeded(current_index)) {
0083             const QString udi = m_places->deviceForIndex(current_index).udi();
0084             match.setId(udi);
0085             match.setData(udi);
0086         } else {
0087             const QUrl url = KFilePlacesModel::convertedUrl(m_places->url(current_index));
0088             match.setData(url);
0089             match.setUrls({url});
0090             match.setId(url.toDisplayString());
0091         }
0092 
0093         matches << match;
0094     }
0095 
0096     context.addMatches(matches);
0097 }
0098 
0099 void PlacesRunner::openDevice(const QString &udi)
0100 {
0101     m_pendingUdi.clear();
0102 
0103     for (int i = 0; i < m_places->rowCount(); ++i) {
0104         const QModelIndex idx = m_places->index(i, 0);
0105         if (m_places->isDevice(idx) && m_places->deviceForIndex(idx).udi() == udi) {
0106             m_pendingUdi = udi;
0107             m_places->requestSetup(idx);
0108             break;
0109         }
0110     }
0111 }
0112 
0113 void PlacesRunner::run(const KRunner::RunnerContext & /*context*/, const KRunner::QueryMatch &match)
0114 {
0115     // 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
0116     // things to go wrong.
0117     if (match.data().typeId() == QMetaType::QUrl) {
0118         auto *job = new KIO::OpenUrlJob(match.data().toUrl());
0119         job->setUiDelegate(new KNotificationJobUiDelegate(KJobUiDelegate::AutoErrorHandlingEnabled));
0120         job->setRunExecutables(false);
0121         job->start();
0122     } else if (match.data().canConvert<QString>()) {
0123         QMetaObject::invokeMethod(this, "openDevice", match.data().toString());
0124     }
0125 }
0126 
0127 #include "placesrunner.moc"