File indexing completed on 2025-01-05 04:36:00

0001 /*
0002  *   SPDX-FileCopyrightText: 2012-2016 Ivan Cukic <ivan.cukic@kde.org>
0003  *
0004  *   SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005  */
0006 
0007 #include "FileItemLinkingPlugin.h"
0008 #include "FileItemLinkingPluginActionLoader.h"
0009 #include "FileItemLinkingPlugin_p.h"
0010 
0011 #include <KFileItemListProperties>
0012 #include <utils/d_ptr_implementation.h>
0013 #include <utils/qsqlquery_iterator.h>
0014 
0015 #include <QCursor>
0016 #include <QDBusPendingCall>
0017 #include <QDebug>
0018 #include <QFileInfo>
0019 #include <QMenu>
0020 #include <QSqlDatabase>
0021 #include <QSqlDriver>
0022 #include <QSqlError>
0023 #include <QSqlField>
0024 #include <QSqlQuery>
0025 #include <QStandardPaths>
0026 
0027 #include <KLocalizedString>
0028 #include <KPluginFactory>
0029 
0030 #include <algorithm>
0031 
0032 #include "common/dbus/common.h"
0033 
0034 K_PLUGIN_CLASS_WITH_JSON(FileItemLinkingPlugin, "kactivitymanagerd_fileitem_linking_plugin.json")
0035 
0036 // Private
0037 
0038 FileItemLinkingPlugin::Private::Private()
0039 {
0040     connect(&activities, &KActivities::Consumer::serviceStatusChanged, this, &Private::activitiesServiceStatusChanged);
0041 }
0042 
0043 void FileItemLinkingPlugin::Private::activitiesServiceStatusChanged(KActivities::Consumer::ServiceStatus status)
0044 {
0045     if (status != KActivities::Consumer::Unknown) {
0046         loadAllActions();
0047     }
0048 }
0049 
0050 void FileItemLinkingPlugin::Private::rootActionHovered()
0051 {
0052     if (status != Status::LoadingBlocked) {
0053         return;
0054     }
0055     status = Status::ShouldLoad;
0056     loadAllActions();
0057 }
0058 
0059 void FileItemLinkingPlugin::Private::actionTriggered()
0060 {
0061     QAction *action = dynamic_cast<QAction *>(sender());
0062 
0063     if (!action) {
0064         return;
0065     }
0066 
0067     bool link = action->property("link").toBool();
0068     QString activity = action->property("activity").toString();
0069 
0070     KAMD_DBUS_DECL_INTERFACE(service, "Resources/Linking", "ResourcesLinking");
0071 
0072     const auto urlList = items.urlList();
0073     for (const auto &item : urlList) {
0074         service.asyncCall(link ? "LinkResourceToActivity" : "UnlinkResourceFromActivity", QString(), item.toLocalFile(), activity);
0075     }
0076 }
0077 
0078 QAction *FileItemLinkingPlugin::Private::basicAction(QWidget *parentWidget)
0079 {
0080     if (root) {
0081         return root;
0082     }
0083 
0084     // If we are showing Loading... text, this means the submenu
0085     // is not opened yet, so activities should not be loaded
0086     status = Status::LoadingBlocked;
0087 
0088     root = new QAction(QIcon::fromTheme("activities"), i18n("Activities"), parentWidget);
0089 
0090     rootMenu = new QMenu(parentWidget);
0091     rootMenu->addAction(new QAction(i18n("Loading..."), this));
0092 
0093     connect(root, &QAction::hovered, this, &Private::rootActionHovered);
0094 
0095     root->setMenu(rootMenu);
0096 
0097     return root;
0098 }
0099 
0100 void FileItemLinkingPlugin::Private::loadAllActions()
0101 {
0102     if (status != Status::ShouldLoad || activities.serviceStatus() == KActivities::Consumer::Unknown) {
0103         return;
0104     }
0105 
0106     if (activities.serviceStatus() == KActivities::Consumer::NotRunning) {
0107         Action action = {};
0108         action.title = i18n("The Activity Manager is not running");
0109 
0110         setActions({action});
0111 
0112     } else if (status != Status::Loaded) {
0113         status = Status::Loaded; // loading is async, we don't want to slin two threads
0114 
0115         auto loader = FileItemLinkingPluginActionLoader::create(items);
0116 
0117         static FileItemLinkingPluginActionStaticInit init;
0118 
0119         connect(loader, &FileItemLinkingPluginActionLoader::result, this, &Private::setActions, Qt::QueuedConnection);
0120 
0121         loader->start();
0122     }
0123 }
0124 
0125 void FileItemLinkingPlugin::Private::setActions(const ActionList &actions)
0126 {
0127     if (!rootMenu) {
0128         return;
0129     }
0130 
0131     for (auto action : rootMenu->actions()) {
0132         rootMenu->removeAction(action);
0133         action->deleteLater();
0134     }
0135 
0136     for (const auto &actionInfo : actions) {
0137         if (actionInfo.icon != "-") {
0138             auto action = new QAction(nullptr);
0139 
0140             action->setText(actionInfo.title);
0141             action->setIcon(QIcon::fromTheme(actionInfo.icon));
0142             action->setProperty("activity", actionInfo.activity);
0143             action->setProperty("link", actionInfo.link);
0144 
0145             rootMenu->addAction(action);
0146 
0147             connect(action, &QAction::triggered, this, &Private::actionTriggered);
0148 
0149         } else {
0150             auto action = new QAction(actionInfo.title, nullptr);
0151             action->setSeparator(true);
0152 
0153             rootMenu->addAction(action);
0154         }
0155     }
0156 }
0157 
0158 FileItemLinkingPluginActionStaticInit::FileItemLinkingPluginActionStaticInit()
0159 {
0160     qRegisterMetaType<Action>("Action");
0161     qRegisterMetaType<ActionList>("ActionList");
0162 }
0163 
0164 // Main class
0165 
0166 FileItemLinkingPlugin::FileItemLinkingPlugin(QObject *parent, const QVariantList &)
0167     : KAbstractFileItemActionPlugin(parent)
0168 {
0169 }
0170 
0171 FileItemLinkingPlugin::~FileItemLinkingPlugin()
0172 {
0173     d->setActions({});
0174 }
0175 
0176 QList<QAction *> FileItemLinkingPlugin::actions(const KFileItemListProperties &fileItemInfos, QWidget *parentWidget)
0177 {
0178     // We can only link local files, don't show the menu if there are none
0179     // KFileItemListProperties::isLocal() is for *all* being local, we just want *any* local
0180     const auto urlList = fileItemInfos.urlList();
0181     const bool hasLocalUrl = std::any_of(urlList.begin(), urlList.end(), [](const QUrl &url) {
0182         return url.isLocalFile();
0183     });
0184 
0185     if (!hasLocalUrl) {
0186         return {};
0187     }
0188 
0189     d->items = fileItemInfos;
0190 
0191     return {d->basicAction(parentWidget)};
0192 }
0193 
0194 #include "FileItemLinkingPlugin.moc"
0195 #include "moc_FileItemLinkingPlugin_p.cpp"