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"