File indexing completed on 2022-11-29 15:04:21

0001 /*
0002     SPDX-FileCopyrightText: 2019 Michail Vourlakos <mvourlakos@gmail.com>
0003     SPDX-License-Identifier: GPL-2.0-or-later
0004 */
0005 
0006 #include "containmentinterface.h"
0007 
0008 // local
0009 #include "view.h"
0010 #include "../lattecorona.h"
0011 #include "../layout/genericlayout.h"
0012 #include "../layouts/importer.h"
0013 #include "../layouts/storage.h"
0014 #include "../settings/universalsettings.h"
0015 
0016 // Qt
0017 #include <QDebug>
0018 #include <QDir>
0019 #include <QLatin1String>
0020 
0021 // Plasma
0022 #include <Plasma/Applet>
0023 #include <Plasma/Containment>
0024 #include <PlasmaQuick/AppletQuickItem>
0025 
0026 // KDE
0027 #include <KDesktopFile>
0028 #include <KLocalizedString>
0029 #include <KPluginMetaData>
0030 #include <KDeclarative/ConfigPropertyMap>
0031 
0032 namespace Latte {
0033 namespace ViewPart {
0034 
0035 ContainmentInterface::ContainmentInterface(Latte::View *parent)
0036     : QObject(parent),
0037       m_view(parent)
0038 {
0039     m_corona = qobject_cast<Latte::Corona *>(m_view->corona());
0040 
0041     m_latteTasksModel = new TasksModel(this);
0042     m_plasmaTasksModel = new TasksModel(this);
0043 
0044     m_appletsExpandedConnectionsTimer.setInterval(2000);
0045     m_appletsExpandedConnectionsTimer.setSingleShot(true);
0046 
0047     m_appletDelayedConfigurationTimer.setInterval(1000);
0048     m_appletDelayedConfigurationTimer.setSingleShot(true);
0049     connect(&m_appletDelayedConfigurationTimer, &QTimer::timeout, this, &ContainmentInterface::updateAppletDelayedConfiguration);
0050 
0051     connect(&m_appletsExpandedConnectionsTimer, &QTimer::timeout, this, &ContainmentInterface::updateAppletsTracking);
0052 
0053     connect(m_view, &View::containmentChanged
0054             , this, [&]() {
0055         if (m_view->containment()) {
0056             connect(m_view->containment(), &Plasma::Containment::appletAdded, this, &ContainmentInterface::onAppletAdded);
0057             m_appletsExpandedConnectionsTimer.start();
0058         }
0059     });
0060 
0061     connect(m_latteTasksModel, &TasksModel::countChanged, this, &ContainmentInterface::onLatteTasksCountChanged);
0062     connect(m_plasmaTasksModel, &TasksModel::countChanged, this, &ContainmentInterface::onPlasmaTasksCountChanged);
0063 }
0064 
0065 ContainmentInterface::~ContainmentInterface()
0066 {
0067 }
0068 
0069 void ContainmentInterface::identifyShortcutsHost()
0070 {
0071     if (m_shortcutsHost) {
0072         return;
0073     }
0074 
0075     if (QQuickItem *graphicItem = m_view->containment()->property("_plasma_graphicObject").value<QQuickItem *>()) {
0076         const auto &childItems = graphicItem->childItems();
0077 
0078         for (QQuickItem *item : childItems) {
0079             if (item->objectName() == QLatin1String("containmentViewLayout")) {
0080                 for (QQuickItem *subitem : item->childItems()) {
0081                     if (subitem->objectName() == QLatin1String("PositionShortcutsAbilityHost")) {
0082                         m_shortcutsHost = subitem;
0083                         identifyMethods();
0084                         return;
0085                     }
0086                 }
0087             }
0088         }
0089     }
0090 }
0091 
0092 void ContainmentInterface::identifyMethods()
0093 {
0094     int aeIndex = m_shortcutsHost->metaObject()->indexOfMethod("activateEntryAtIndex(QVariant)");
0095     int niIndex = m_shortcutsHost->metaObject()->indexOfMethod("newInstanceForEntryAtIndex(QVariant)");
0096     int sbIndex = m_shortcutsHost->metaObject()->indexOfMethod("setShowAppletShortcutBadges(QVariant,QVariant,QVariant,QVariant)");
0097     int afiIndex = m_shortcutsHost->metaObject()->indexOfMethod("appletIdForIndex(QVariant)");
0098 
0099     m_activateEntryMethod = m_shortcutsHost->metaObject()->method(aeIndex);
0100     m_appletIdForIndexMethod = m_shortcutsHost->metaObject()->method(afiIndex);
0101     m_newInstanceMethod = m_shortcutsHost->metaObject()->method(niIndex);
0102     m_showShortcutsMethod = m_shortcutsHost->metaObject()->method(sbIndex);
0103 }
0104 
0105 bool ContainmentInterface::applicationLauncherHasGlobalShortcut() const
0106 {
0107     if (!containsApplicationLauncher()) {
0108         return false;
0109     }
0110 
0111     uint launcherAppletId = applicationLauncherId();
0112 
0113     const auto applets = m_view->containment()->applets();
0114 
0115     for (auto applet : applets) {
0116         if (applet->id() == launcherAppletId) {
0117             return !applet->globalShortcut().isEmpty();
0118         }
0119     }
0120 
0121     return false;
0122 }
0123 
0124 bool ContainmentInterface::applicationLauncherInPopup() const
0125 {
0126     if (!containsApplicationLauncher()) {
0127         return false;
0128     }
0129 
0130     uint launcherAppletId = applicationLauncherId();
0131     const auto applets = m_view->containment()->applets();
0132 
0133     PlasmaQuick::AppletQuickItem *appLauncherItem{nullptr};
0134 
0135     for (auto applet : applets) {
0136         if (applet->id() == launcherAppletId) {
0137             appLauncherItem = applet->property("_plasma_graphicObject").value<PlasmaQuick::AppletQuickItem *>();
0138         }
0139     }
0140 
0141     return appLauncherItem && appletIsExpandable(appLauncherItem);
0142 }
0143 
0144 bool ContainmentInterface::containsApplicationLauncher() const
0145 {
0146     return (applicationLauncherId() >= 0);
0147 }
0148 
0149 bool ContainmentInterface::isCapableToShowShortcutBadges()
0150 {
0151     identifyShortcutsHost();
0152 
0153     if (!hasLatteTasks() && hasPlasmaTasks()) {
0154         return false;
0155     }
0156 
0157     return m_showShortcutsMethod.isValid();
0158 }
0159 
0160 bool ContainmentInterface::isApplication(const QUrl &url) const
0161 {
0162     if (!url.isValid() || !url.isLocalFile()) {
0163         return false;
0164     }
0165 
0166     const QString &localPath = url.toLocalFile();
0167 
0168     if (!KDesktopFile::isDesktopFile(localPath)) {
0169         return false;
0170     }
0171 
0172     KDesktopFile desktopFile(localPath);
0173     return desktopFile.hasApplicationType();
0174 }
0175 
0176 int ContainmentInterface::applicationLauncherId() const
0177 {
0178     const auto applets = m_view->containment()->applets();
0179 
0180     auto launcherId{-1};
0181 
0182     for (auto applet : applets) {
0183         const auto provides = applet->kPackage().metadata().value(QStringLiteral("X-Plasma-Provides"));
0184 
0185         if (provides.contains(QLatin1String("org.kde.plasma.launchermenu"))) {
0186             if (!applet->globalShortcut().isEmpty()) {
0187                 return applet->id();
0188             } else if (launcherId == -1) {
0189                 launcherId = applet->id();
0190             }
0191         }
0192     }
0193 
0194     return launcherId;
0195 }
0196 
0197 bool ContainmentInterface::updateBadgeForLatteTask(const QString identifier, const QString value)
0198 {
0199     if (!hasLatteTasks()) {
0200         return false;
0201     }
0202 
0203     const auto &applets = m_view->containment()->applets();
0204 
0205     for (auto *applet : applets) {
0206         KPluginMetaData meta = applet->kPackage().metadata();
0207 
0208         if (meta.pluginId() == QLatin1String("org.kde.latte.plasmoid")) {
0209 
0210             if (QQuickItem *appletInterface = applet->property("_plasma_graphicObject").value<QQuickItem *>()) {
0211                 const auto &childItems = appletInterface->childItems();
0212 
0213                 if (childItems.isEmpty()) {
0214                     continue;
0215                 }
0216 
0217                 for (QQuickItem *item : childItems) {
0218                     if (auto *metaObject = item->metaObject()) {
0219                         // not using QMetaObject::invokeMethod to avoid warnings when calling
0220                         // this on applets that don't have it or other child items since this
0221                         // is pretty much trial and error.
0222                         // Also, "var" arguments are treated as QVariant in QMetaObject
0223 
0224                         int methodIndex = metaObject->indexOfMethod("updateBadge(QVariant,QVariant)");
0225 
0226                         if (methodIndex == -1) {
0227                             continue;
0228                         }
0229 
0230                         QMetaMethod method = metaObject->method(methodIndex);
0231 
0232                         if (method.invoke(item, Q_ARG(QVariant, identifier), Q_ARG(QVariant, value))) {
0233                             return true;
0234                         }
0235                     }
0236                 }
0237             }
0238         }
0239     }
0240 
0241     return false;
0242 }
0243 
0244 bool ContainmentInterface::activatePlasmaTask(const int index)
0245 {
0246     bool containsPlasmaTaskManager{hasPlasmaTasks() && !hasLatteTasks()};
0247 
0248     if (!containsPlasmaTaskManager) {
0249         return false;
0250     }
0251 
0252     const auto &applets = m_view->containment()->applets();
0253 
0254     for (auto *applet : applets) {
0255         const auto &provides = KPluginMetaData::readStringList(applet->pluginMetaData().rawData(), QStringLiteral("X-Plasma-Provides"));
0256 
0257         if (provides.contains(QLatin1String("org.kde.plasma.multitasking"))) {
0258             if (QQuickItem *appletInterface = applet->property("_plasma_graphicObject").value<QQuickItem *>()) {
0259                 const auto &childItems = appletInterface->childItems();
0260 
0261                 if (childItems.isEmpty()) {
0262                     continue;
0263                 }
0264 
0265                 KPluginMetaData meta = applet->kPackage().metadata();
0266 
0267                 for (QQuickItem *item : childItems) {
0268                     if (auto *metaObject = item->metaObject()) {
0269                         int methodIndex{metaObject->indexOfMethod("activateTaskAtIndex(QVariant)")};
0270 
0271                         if (methodIndex == -1) {
0272                             continue;
0273                         }
0274 
0275                         QMetaMethod method = metaObject->method(methodIndex);
0276 
0277                         if (method.invoke(item, Q_ARG(QVariant, index - 1))) {
0278                             showShortcutBadges(false, true);
0279 
0280                             return true;
0281                         }
0282                     }
0283                 }
0284             }
0285         }
0286     }
0287 
0288     return false;
0289 }
0290 
0291 bool ContainmentInterface::newInstanceForPlasmaTask(const int index)
0292 {
0293     bool containsPlasmaTaskManager{hasPlasmaTasks() && !hasLatteTasks()};
0294 
0295     if (!containsPlasmaTaskManager) {
0296         return false;
0297     }
0298 
0299     const auto &applets = m_view->containment()->applets();
0300 
0301     for (auto *applet : applets) {
0302         const auto &provides = KPluginMetaData::readStringList(applet->pluginMetaData().rawData(), QStringLiteral("X-Plasma-Provides"));
0303 
0304         if (provides.contains(QLatin1String("org.kde.plasma.multitasking"))) {
0305             if (QQuickItem *appletInterface = applet->property("_plasma_graphicObject").value<QQuickItem *>()) {
0306                 const auto &childItems = appletInterface->childItems();
0307 
0308                 if (childItems.isEmpty()) {
0309                     continue;
0310                 }
0311 
0312                 KPluginMetaData meta = applet->kPackage().metadata();
0313 
0314                 for (QQuickItem *item : childItems) {
0315                     if (auto *metaObject = item->metaObject()) {
0316                         int methodIndex{metaObject->indexOfMethod("newInstanceForTaskAtIndex(QVariant)")};
0317 
0318                         if (methodIndex == -1) {
0319                             continue;
0320                         }
0321 
0322                         QMetaMethod method = metaObject->method(methodIndex);
0323 
0324                         if (method.invoke(item, Q_ARG(QVariant, index - 1))) {
0325                             showShortcutBadges(false, true);
0326 
0327                             return true;
0328                         }
0329                     }
0330                 }
0331             }
0332         }
0333     }
0334 
0335     return false;
0336 }
0337 
0338 bool ContainmentInterface::activateEntry(const int index)
0339 {
0340     identifyShortcutsHost();
0341 
0342     if (!m_activateEntryMethod.isValid()) {
0343         return false;
0344     }
0345 
0346     return m_activateEntryMethod.invoke(m_shortcutsHost, Q_ARG(QVariant, index));
0347 }
0348 
0349 bool ContainmentInterface::newInstanceForEntry(const int index)
0350 {
0351     identifyShortcutsHost();
0352 
0353     if (!m_newInstanceMethod.isValid()) {
0354         return false;
0355     }
0356 
0357     return m_newInstanceMethod.invoke(m_shortcutsHost, Q_ARG(QVariant, index));
0358 }
0359 
0360 bool ContainmentInterface::hideShortcutBadges()
0361 {
0362     identifyShortcutsHost();
0363 
0364     if (!m_showShortcutsMethod.isValid()) {
0365         return false;
0366     }
0367 
0368     return m_showShortcutsMethod.invoke(m_shortcutsHost, Q_ARG(QVariant, false), Q_ARG(QVariant, false), Q_ARG(QVariant, false), Q_ARG(QVariant, -1));
0369 }
0370 
0371 bool ContainmentInterface::showOnlyMeta()
0372 {
0373     if (!m_corona->universalSettings()->kwin_metaForwardedToLatte()) {
0374         return false;
0375     }
0376 
0377     return showShortcutBadges(false, true);
0378 }
0379 
0380 bool ContainmentInterface::showShortcutBadges(const bool showLatteShortcuts, const bool showMeta)
0381 {
0382     identifyShortcutsHost();
0383 
0384     if (!m_showShortcutsMethod.isValid() || !isCapableToShowShortcutBadges()) {
0385         return false;
0386     }
0387 
0388     int appLauncherId = m_corona->universalSettings()->kwin_metaForwardedToLatte() && showMeta ? applicationLauncherId() : -1;
0389 
0390     return m_showShortcutsMethod.invoke(m_shortcutsHost, Q_ARG(QVariant, showLatteShortcuts), Q_ARG(QVariant, true), Q_ARG(QVariant, showMeta), Q_ARG(QVariant, appLauncherId));
0391 }
0392 
0393 int ContainmentInterface::appletIdForVisualIndex(const int index)
0394 {
0395     identifyShortcutsHost();
0396 
0397     if (!m_appletIdForIndexMethod.isValid()) {
0398         return -1;
0399     }
0400 
0401     QVariant appletId{-1};
0402 
0403     m_appletIdForIndexMethod.invoke(m_shortcutsHost, Q_RETURN_ARG(QVariant, appletId), Q_ARG(QVariant, index));
0404 
0405     return appletId.toInt();
0406 }
0407 
0408 
0409 void ContainmentInterface::deactivateApplets()
0410 {
0411     if (!m_view->containment() || !m_view->inReadyState()) {
0412         return;
0413     }
0414 
0415     for (const auto applet : m_view->containment()->applets()) {
0416         PlasmaQuick::AppletQuickItem *ai = applet->property("_plasma_graphicObject").value<PlasmaQuick::AppletQuickItem *>();
0417 
0418         if (ai) {
0419             ai->setExpanded(false);
0420         }
0421     }
0422 }
0423 
0424 bool ContainmentInterface::appletIsExpandable(const int id) const
0425 {
0426     if (!m_view->containment() || !m_view->inReadyState()) {
0427         return false;
0428     }
0429 
0430     for (const auto applet : m_view->containment()->applets()) {
0431         if (applet && applet->id() == (uint)id) {
0432             if (Layouts::Storage::self()->isSubContainment(m_view->corona(), applet)) {
0433                 return true;
0434             }
0435 
0436             PlasmaQuick::AppletQuickItem *ai = applet->property("_plasma_graphicObject").value<PlasmaQuick::AppletQuickItem *>();
0437 
0438             if (ai) {
0439                 return appletIsExpandable(ai);
0440             }
0441         }
0442     }
0443 
0444     return false;
0445 }
0446 
0447 bool ContainmentInterface::appletIsExpandable(PlasmaQuick::AppletQuickItem *appletQuickItem) const
0448 {
0449     if (!appletQuickItem || !m_view->inReadyState()) {
0450         return false;
0451     }
0452 
0453     return ((appletQuickItem->fullRepresentation() != nullptr
0454             && appletQuickItem->preferredRepresentation() != appletQuickItem->fullRepresentation())
0455             || Latte::Layouts::Storage::self()->isSubContainment(m_view->corona(), appletQuickItem->applet()));
0456 }
0457 
0458 bool ContainmentInterface::appletIsActivationTogglesExpanded(const int id) const
0459 {
0460     if (!m_view->containment() || !m_view->inReadyState()) {
0461         return false;
0462     }
0463 
0464     for (const auto applet : m_view->containment()->applets()) {
0465         if (applet && applet->id() == (uint)id) {
0466             if (Layouts::Storage::self()->isSubContainment(m_view->corona(), applet)) {
0467                 return true;
0468             }
0469 
0470             PlasmaQuick::AppletQuickItem *ai = applet->property("_plasma_graphicObject").value<PlasmaQuick::AppletQuickItem *>();
0471 
0472             if (ai) {
0473                 return ai->isActivationTogglesExpanded();
0474             }
0475         }
0476     }
0477 
0478     return false;
0479 }
0480 
0481 bool ContainmentInterface::hasExpandedApplet() const
0482 {
0483     return m_expandedAppletIds.count() > 0;
0484 }
0485 
0486 bool ContainmentInterface::hasLatteTasks() const
0487 {
0488     return (m_latteTasksModel->count() > 0);
0489 }
0490 
0491 bool ContainmentInterface::hasPlasmaTasks() const
0492 {
0493     return (m_plasmaTasksModel->count() > 0);
0494 }
0495 
0496 int ContainmentInterface::indexOfApplet(const int &id)
0497 {
0498     if (m_appletOrder.contains(id)) {
0499         return m_appletOrder.indexOf(id);
0500     } else if (m_appletData.contains(id)) {
0501         return m_appletData[id].lastValidIndex;
0502     }
0503 
0504     return -1;
0505 }
0506 
0507 ViewPart::AppletInterfaceData ContainmentInterface::appletDataAtIndex(const int &index)
0508 {
0509     ViewPart::AppletInterfaceData data;
0510 
0511     if (index<0 || (index > (m_appletOrder.count()-1))) {
0512         return data;
0513     }
0514 
0515     return m_appletData[m_appletOrder[index]];
0516 }
0517 
0518 ViewPart::AppletInterfaceData ContainmentInterface::appletDataForId(const int &id)
0519 {
0520     ViewPart::AppletInterfaceData data;
0521 
0522     if (!m_appletData.contains(id)) {
0523         return data;
0524     }
0525 
0526     return m_appletData[id];
0527 }
0528 
0529 QObject *ContainmentInterface::plasmoid() const
0530 {
0531     return m_plasmoid;
0532 }
0533 
0534 void ContainmentInterface::setPlasmoid(QObject *plasmoid)
0535 {
0536     if (m_plasmoid == plasmoid) {
0537         return;
0538     }
0539 
0540     m_plasmoid = plasmoid;
0541 
0542     if (m_plasmoid) {
0543         m_configuration = qobject_cast<KDeclarative::ConfigPropertyMap *>(m_plasmoid->property("configuration").value<QObject *>());
0544 
0545         if (m_configuration) {
0546             connect(m_configuration, &QQmlPropertyMap::valueChanged, this, &ContainmentInterface::containmentConfigPropertyChanged);
0547         }
0548     }
0549 
0550     emit plasmoidChanged();
0551 }
0552 
0553 QObject *ContainmentInterface::layoutManager() const
0554 {
0555     return m_layoutManager;
0556 }
0557 
0558 void ContainmentInterface::setLayoutManager(QObject *manager)
0559 {
0560     if (m_layoutManager == manager) {
0561         return;
0562     }
0563 
0564     m_layoutManager = manager;
0565 
0566     // applets order
0567     int metaorderindex = m_layoutManager->metaObject()->indexOfProperty("order");
0568     if (metaorderindex >= 0) {
0569         QMetaProperty metaorder = m_layoutManager->metaObject()->property(metaorderindex);
0570         if (metaorder.hasNotifySignal()) {
0571             QMetaMethod metaorderchanged = metaorder.notifySignal();
0572             QMetaMethod metaupdateappletorder = this->metaObject()->method(this->metaObject()->indexOfSlot("updateAppletsOrder()"));
0573             connect(m_layoutManager, metaorderchanged, this, metaupdateappletorder);
0574             updateAppletsOrder();
0575         }
0576     }
0577 
0578     // applets in locked zoom
0579     metaorderindex = m_layoutManager->metaObject()->indexOfProperty("lockedZoomApplets");
0580     if (metaorderindex >= 0) {
0581         QMetaProperty metaorder = m_layoutManager->metaObject()->property(metaorderindex);
0582         if (metaorder.hasNotifySignal()) {
0583             QMetaMethod metaorderchanged = metaorder.notifySignal();
0584             QMetaMethod metaupdateapplets = this->metaObject()->method(this->metaObject()->indexOfSlot("updateAppletsInLockedZoom()"));
0585             connect(m_layoutManager, metaorderchanged, this, metaupdateapplets);
0586             updateAppletsInLockedZoom();
0587         }
0588     }
0589 
0590     // applets disabled their autocoloring
0591     metaorderindex = m_layoutManager->metaObject()->indexOfProperty("userBlocksColorizingApplets");
0592     if (metaorderindex >= 0) {
0593         QMetaProperty metaorder = m_layoutManager->metaObject()->property(metaorderindex);
0594         if (metaorder.hasNotifySignal()) {
0595             QMetaMethod metaorderchanged = metaorder.notifySignal();
0596             QMetaMethod metaupdateapplets = this->metaObject()->method(this->metaObject()->indexOfSlot("updateAppletsDisabledColoring()"));
0597             connect(m_layoutManager, metaorderchanged, this, metaupdateapplets);
0598             updateAppletsDisabledColoring();
0599         }
0600     }
0601 
0602     emit layoutManagerChanged();
0603 }
0604 
0605 void ContainmentInterface::addApplet(const QString &pluginId)
0606 {
0607     if (pluginId.isEmpty()) {
0608         return;
0609     }
0610 
0611     QStringList paths = Latte::Layouts::Importer::standardPaths();
0612     QString pluginpath;
0613 
0614     for(int i=0; i<paths.count(); ++i) {
0615         QString cpath = paths[i] + "/plasma/plasmoids/" + pluginId;
0616 
0617         if (QDir(cpath).exists()) {
0618             pluginpath = cpath;
0619             break;
0620         }
0621     }
0622 
0623     if (!pluginpath.isEmpty()) {
0624         m_view->containment()->createApplet(pluginId);
0625     }
0626 }
0627 
0628 void ContainmentInterface::addApplet(QObject *metadata, int x, int y)
0629 {
0630     int processmimedataindex = m_plasmoid->metaObject()->indexOfMethod("processMimeData(QObject*,int,int)");
0631     QMetaMethod processmethod = m_plasmoid->metaObject()->method(processmimedataindex);
0632     processmethod.invoke(m_plasmoid,
0633                          Q_ARG(QObject *, metadata),
0634                          Q_ARG(int, x),
0635                          Q_ARG(int, y));
0636 }
0637 
0638 void ContainmentInterface::addExpandedApplet(PlasmaQuick::AppletQuickItem * appletQuickItem)
0639 {
0640     if (appletQuickItem && m_expandedAppletIds.contains(appletQuickItem) && appletIsExpandable(appletQuickItem)) {
0641         return;
0642     }
0643 
0644     bool isExpanded = hasExpandedApplet();
0645 
0646     m_expandedAppletIds[appletQuickItem] = appletQuickItem->applet()->id();
0647 
0648     if (isExpanded != hasExpandedApplet()) {
0649         emit hasExpandedAppletChanged();
0650     }
0651 
0652     emit expandedAppletStateChanged();
0653 }
0654 
0655 void ContainmentInterface::removeExpandedApplet(PlasmaQuick::AppletQuickItem *appletQuickItem)
0656 {
0657     if (!m_expandedAppletIds.contains(appletQuickItem)) {
0658         return;
0659     }
0660 
0661     bool isExpanded = hasExpandedApplet();
0662 
0663     m_expandedAppletIds.remove(appletQuickItem);
0664 
0665     if (isExpanded != hasExpandedApplet()) {
0666         emit hasExpandedAppletChanged();
0667     }
0668 
0669     emit expandedAppletStateChanged();
0670 }
0671 
0672 QAbstractListModel *ContainmentInterface::latteTasksModel() const
0673 {
0674     return m_latteTasksModel;
0675 }
0676 
0677 QAbstractListModel *ContainmentInterface::plasmaTasksModel() const
0678 {
0679     return m_plasmaTasksModel;
0680 }
0681 
0682 void ContainmentInterface::onAppletExpandedChanged()
0683 {
0684     PlasmaQuick::AppletQuickItem *appletItem = static_cast<PlasmaQuick::AppletQuickItem *>(QObject::sender());
0685 
0686     if (appletItem) {
0687         bool added{false};
0688 
0689         if (appletItem->isExpanded()) {
0690             if (appletItem->switchWidth()>0 && appletItem->switchHeight()>0) {
0691                 added = ((appletItem->width()<=appletItem->switchWidth())
0692                          && (appletItem->height()<=appletItem->switchHeight()));
0693             } else {
0694                 added = true;
0695             }
0696         }
0697 
0698         if (added && appletIsExpandable(appletItem)) {
0699             addExpandedApplet(appletItem);
0700         } else {
0701             removeExpandedApplet(appletItem);
0702         }
0703     }
0704 }
0705 
0706 QList<int> ContainmentInterface::appletsOrder() const
0707 {
0708     return m_appletOrder;
0709 }
0710 
0711 void ContainmentInterface::updateAppletsOrder()
0712 {
0713     if (!m_layoutManager) {
0714         return;
0715     }
0716 
0717     QList<int> neworder = m_layoutManager->property("order").value<QList<int>>();
0718 
0719     if (m_appletOrder == neworder) {
0720         return;
0721     }
0722 
0723     m_appletOrder = neworder;
0724 
0725     //! update applets last recorded index, this is needed for example when an applet is removed
0726     //! to know in which index was located before the removal
0727     for(const auto &id: m_appletOrder) {
0728         if (m_appletData.contains(id)) {
0729             m_appletData[id].lastValidIndex = m_appletOrder.indexOf(id);
0730         }
0731     }
0732 
0733     emit appletsOrderChanged();
0734 }
0735 
0736 void ContainmentInterface::updateAppletsInLockedZoom()
0737 {
0738     if (!m_layoutManager) {
0739         return;
0740     }
0741 
0742     QList<int> appletslockedzoom = m_layoutManager->property("lockedZoomApplets").value<QList<int>>();
0743 
0744     if (m_appletsInLockedZoom == appletslockedzoom) {
0745         return;
0746     }
0747 
0748     m_appletsInLockedZoom = appletslockedzoom;
0749     emit appletsInLockedZoomChanged(m_appletsInLockedZoom);
0750 }
0751 
0752 void ContainmentInterface::updateAppletsDisabledColoring()
0753 {
0754     if (!m_layoutManager) {
0755         return;
0756     }
0757 
0758     QList<int> appletsdisabledcoloring = m_layoutManager->property("userBlocksColorizingApplets").value<QList<int>>();
0759 
0760     if (m_appletsDisabledColoring == appletsdisabledcoloring) {
0761         return;
0762     }
0763 
0764     m_appletsDisabledColoring = appletsdisabledcoloring;
0765     emit appletsDisabledColoringChanged(appletsdisabledcoloring);
0766 }
0767 
0768 void ContainmentInterface::onLatteTasksCountChanged()
0769 {
0770     if ((m_hasLatteTasks && m_latteTasksModel->count()>0)
0771             || (!m_hasLatteTasks && m_latteTasksModel->count() == 0)) {
0772         return;
0773     }
0774 
0775     m_hasLatteTasks = (m_latteTasksModel->count() > 0);
0776     emit hasLatteTasksChanged();
0777 }
0778 
0779 void ContainmentInterface::onPlasmaTasksCountChanged()
0780 {
0781     if ((m_hasPlasmaTasks && m_plasmaTasksModel->count()>0)
0782             || (!m_hasPlasmaTasks && m_plasmaTasksModel->count() == 0)) {
0783         return;
0784     }
0785 
0786     m_hasPlasmaTasks = (m_plasmaTasksModel->count() > 0);
0787     emit hasPlasmaTasksChanged();
0788 }
0789 
0790 bool ContainmentInterface::appletIsExpanded(const int id) const
0791 {
0792     return m_expandedAppletIds.values().contains(id);
0793 }
0794 
0795 void ContainmentInterface::toggleAppletExpanded(const int id)
0796 {
0797     if (!m_view->containment() || !m_view->inReadyState()) {
0798         return;
0799     }
0800 
0801     for (const auto applet : m_view->containment()->applets()) {
0802         if (applet->id() == (uint)id && !Layouts::Storage::self()->isSubContainment(m_view->corona(), applet)/*block for sub-containments*/) {
0803             PlasmaQuick::AppletQuickItem *ai = applet->property("_plasma_graphicObject").value<PlasmaQuick::AppletQuickItem *>();
0804 
0805             if (ai) {
0806                 emit applet->activated();
0807             }
0808         }
0809     }
0810 }
0811 
0812 void ContainmentInterface::removeApplet(const int &id)
0813 {
0814     if (!m_appletData.contains(id)) {
0815         return;
0816     }
0817 
0818     auto applet = m_appletData[id].applet;
0819     emit applet->appletDeleted(applet); //! this signal should be part of Plasma Frameworks AppletPrivate::destroy() function...
0820     applet->destroy();
0821 }
0822 
0823 
0824 void ContainmentInterface::setAppletsOrder(const QList<int> &order)
0825 {
0826     QMetaObject::invokeMethod(m_layoutManager,
0827                               "requestAppletsOrder",
0828                               Qt::DirectConnection,
0829                               Q_ARG(QList<int>, order));
0830 
0831 }
0832 
0833 void ContainmentInterface::setAppletsInLockedZoom(const QList<int> &applets)
0834 {
0835     QMetaObject::invokeMethod(m_layoutManager,
0836                               "requestAppletsInLockedZoom",
0837                               Qt::DirectConnection,
0838                               Q_ARG(QList<int>, applets));
0839 }
0840 
0841 void ContainmentInterface::setAppletsDisabledColoring(const QList<int> &applets)
0842 {
0843     QMetaObject::invokeMethod(m_layoutManager,
0844                               "requestAppletsDisabledColoring",
0845                               Qt::DirectConnection,
0846                               Q_ARG(QList<int>, applets));
0847 }
0848 
0849 void ContainmentInterface::setAppletInScheduledDestruction(const int &id, const bool &enabled)
0850 {
0851     QMetaObject::invokeMethod(m_layoutManager,
0852                               "setAppletInScheduledDestruction",
0853                               Qt::DirectConnection,
0854                               Q_ARG(int, id),
0855                               Q_ARG(bool, enabled));
0856 }
0857 
0858 void ContainmentInterface::updateContainmentConfigProperty(const QString &key, const QVariant &value)
0859 {
0860     if (!m_configuration || !m_configuration->keys().contains(key)) {
0861 
0862     }
0863 
0864     if (m_configuration->keys().contains(key)
0865             && (*m_configuration)[key] != value) {
0866         m_configuration->insert(key, value);
0867         emit m_configuration->valueChanged(key, value);
0868     }
0869 }
0870 
0871 void ContainmentInterface::updateAppletConfigProperty(const int &id, const QString &key, const QVariant &value)
0872 {
0873     if (!m_appletData.contains(id) || !m_appletData[id].configuration || !m_appletData[id].configuration->keys().contains(key)) {
0874         return;
0875     }
0876 
0877     if (m_appletData[id].configuration->keys().contains(key)
0878             && (*m_appletData[id].configuration)[key] != value) {
0879         m_appletData[id].configuration->insert(key, value);
0880         emit m_appletData[id].configuration->valueChanged(key, value);
0881     }
0882 }
0883 
0884 void ContainmentInterface::updateAppletsTracking()
0885 {
0886     if (!m_view->containment()) {
0887         return;
0888     }
0889 
0890     for (const auto applet : m_view->containment()->applets()) {
0891         onAppletAdded(applet);
0892     }
0893 
0894     emit initializationCompleted();
0895 }
0896 
0897 void ContainmentInterface::updateAppletDelayedConfiguration()
0898 {
0899     for (const auto id : m_appletData.keys()) {
0900         if (!m_appletData[id].configuration) {
0901             m_appletData[id].configuration = appletConfiguration(m_appletData[id].applet);
0902 
0903             if (m_appletData[id].configuration) {
0904                 qDebug() << "org.kde.sync delayed applet configuration was successful for : " << id;
0905                 initAppletConfigurationSignals(id, m_appletData[id].configuration);
0906             }
0907         }
0908     }
0909 }
0910 
0911 void ContainmentInterface::initAppletConfigurationSignals(const int &id, KDeclarative::ConfigPropertyMap *configuration)
0912 {
0913     if (!configuration) {
0914         return;
0915     }
0916 
0917     connect(configuration, &QQmlPropertyMap::valueChanged,
0918             this, [&, id](const QString &key, const QVariant &value) {
0919         //qDebug() << "org.kde.sync applet property changed : " << currentAppletId << " __ " << m_appletData[currentAppletId].plugin << " __ " << key << " __ " << value;
0920         emit appletConfigPropertyChanged(id, key, value);
0921     });
0922 }
0923 
0924 KDeclarative::ConfigPropertyMap *ContainmentInterface::appletConfiguration(const Plasma::Applet *applet)
0925 {
0926     if (!m_view->containment() || !applet) {
0927         return nullptr;
0928     }
0929 
0930     PlasmaQuick::AppletQuickItem *ai = applet->property("_plasma_graphicObject").value<PlasmaQuick::AppletQuickItem *>();
0931     bool isSubContainment = Layouts::Storage::self()->isSubContainment(m_view->corona(), applet); //we use corona() to make sure that returns true even when it is first created from user
0932     int currentAppletId = applet->id();
0933     KDeclarative::ConfigPropertyMap *configuration{nullptr};
0934 
0935     //! set configuration object properly for applets and subcontainments
0936     if (!isSubContainment) {
0937         int metaconfigindex = ai->metaObject()->indexOfProperty("configuration");
0938         if (metaconfigindex >=0 ){
0939             configuration = qobject_cast<KDeclarative::ConfigPropertyMap *>((ai->property("configuration")).value<QObject *>());
0940         }
0941     } else {
0942         Plasma::Containment *subcontainment = Layouts::Storage::self()->subContainmentOf(m_view->corona(), applet);
0943         if (subcontainment) {
0944             PlasmaQuick::AppletQuickItem *subcai = subcontainment->property("_plasma_graphicObject").value<PlasmaQuick::AppletQuickItem *>();
0945 
0946             if (subcai) {
0947                 int metaconfigindex = subcai->metaObject()->indexOfProperty("configuration");
0948                 if (metaconfigindex >=0 ){
0949                     configuration = qobject_cast<KDeclarative::ConfigPropertyMap *>((subcai->property("configuration")).value<QObject *>());
0950                 }
0951             }
0952         }
0953     }
0954 
0955     return configuration;
0956 }
0957 
0958 void ContainmentInterface::onAppletAdded(Plasma::Applet *applet)
0959 {
0960     if (!m_view->containment() || !applet) {
0961         return;
0962     }
0963 
0964     PlasmaQuick::AppletQuickItem *ai = applet->property("_plasma_graphicObject").value<PlasmaQuick::AppletQuickItem *>();
0965     bool isSubContainment = Layouts::Storage::self()->isSubContainment(m_view->corona(), applet); //we use corona() to make sure that returns true even when it is first created from user
0966     int currentAppletId = applet->id();
0967 
0968     //! Track expanded/able applets and Tasks applets
0969     if (isSubContainment) {
0970         //! internal containment case
0971         Plasma::Containment *subContainment = Layouts::Storage::self()->subContainmentOf(m_view->corona(), applet);
0972         PlasmaQuick::AppletQuickItem *contAi = ai;
0973 
0974         if (contAi && !m_appletsExpandedConnections.contains(contAi)) {
0975             m_appletsExpandedConnections[contAi] = connect(contAi, &PlasmaQuick::AppletQuickItem::expandedChanged, this, &ContainmentInterface::onAppletExpandedChanged);
0976 
0977             connect(contAi, &QObject::destroyed, this, [&, contAi](){
0978                 m_appletsExpandedConnections.remove(contAi);
0979                 removeExpandedApplet(contAi);
0980             });
0981         }
0982 
0983         for (const auto internalApplet : subContainment->applets()) {
0984             PlasmaQuick::AppletQuickItem *ai = internalApplet->property("_plasma_graphicObject").value<PlasmaQuick::AppletQuickItem *>();
0985 
0986             if (ai && !m_appletsExpandedConnections.contains(ai) ){
0987                 m_appletsExpandedConnections[ai] = connect(ai, &PlasmaQuick::AppletQuickItem::expandedChanged, this, &ContainmentInterface::onAppletExpandedChanged);
0988 
0989                 connect(ai, &QObject::destroyed, this, [&, ai](){
0990                     m_appletsExpandedConnections.remove(ai);
0991                     removeExpandedApplet(ai);
0992                 });
0993             }
0994         }
0995     } else if (ai) {
0996         KPluginMetaData meta = applet->kPackage().metadata();
0997         const auto &provides = KPluginMetaData::readStringList(meta.rawData(), QStringLiteral("X-Plasma-Provides"));
0998 
0999         if (meta.pluginId() == QLatin1String("org.kde.latte.plasmoid")) {
1000             //! populate latte tasks applet
1001             m_latteTasksModel->addTask(ai);
1002         } else if (provides.contains(QLatin1String("org.kde.plasma.multitasking"))) {
1003             //! populate plasma tasks applet
1004             m_plasmaTasksModel->addTask(ai);
1005         } else if (!m_appletsExpandedConnections.contains(ai)) {
1006             m_appletsExpandedConnections[ai] = connect(ai, &PlasmaQuick::AppletQuickItem::expandedChanged, this, &ContainmentInterface::onAppletExpandedChanged);
1007 
1008             connect(ai, &QObject::destroyed, this, [&, ai](){
1009                 m_appletsExpandedConnections.remove(ai);
1010                 removeExpandedApplet(ai);
1011             });
1012         }
1013     }
1014 
1015     //! Track All Applets, for example to support syncing between different docks and panels
1016     if (ai) {
1017         bool initializing{!m_appletData.contains(currentAppletId)};
1018 
1019         KPluginMetaData meta = applet->kPackage().metadata();
1020         ViewPart::AppletInterfaceData data;
1021         data.id = currentAppletId;
1022         data.plugin = meta.pluginId();
1023         data.applet = applet;
1024         data.plasmoid = ai;
1025         data.lastValidIndex = m_appletOrder.indexOf(data.id);
1026         //! set configuration object properly for applets and subcontainments
1027         data.configuration = appletConfiguration(applet);
1028 
1029         //! track property changes in applets
1030         if (data.configuration) {
1031             initAppletConfigurationSignals(data.id, data.configuration);
1032         } else {
1033             qDebug() << "org.kde.sync Unfortunately configuration syncing for :: " << currentAppletId << " was not established, configuration object was missing!";
1034             m_appletDelayedConfigurationTimer.start();
1035         }
1036 
1037         if (initializing) {
1038             //! track applet destroyed flag
1039             connect(applet, &Plasma::Applet::destroyedChanged, this, [&, currentAppletId](bool destroyed) {
1040                 emit appletInScheduledDestructionChanged(currentAppletId, destroyed);
1041             });
1042 
1043             //! remove on applet destruction
1044             connect(applet, &QObject::destroyed, this, [&, data](){
1045                 emit appletRemoved(data.id);
1046                 //qDebug() << "org.kde.sync: removing applet ::: " << data.id << " __ " << data.plugin << " remained : " << m_appletData.keys();
1047                 m_appletData.remove(data.id);
1048             });
1049         }
1050 
1051         m_appletData[data.id] = data;
1052         emit appletDataCreated(data.id);
1053     }
1054 }
1055 
1056 QList<int> ContainmentInterface::toIntList(const QVariantList &list)
1057 {
1058     QList<int> converted;
1059 
1060     for(const QVariant &item: list) {
1061         converted << item.toInt();
1062     }
1063 
1064     return converted;
1065 }
1066 
1067 }
1068 }