File indexing completed on 2024-04-21 05:30:51

0001 /*
0002     SPDX-FileCopyrightText: 2020 Michail Vourlakos <mvourlakos@gmail.com>
0003     SPDX-License-Identifier: GPL-2.0-or-later
0004 */
0005 
0006 #include "storage.h"
0007 
0008 // local
0009 #include <coretypes.h>
0010 #include "importer.h"
0011 #include "manager.h"
0012 #include "../lattecorona.h"
0013 #include "../screenpool.h"
0014 #include "../data/errordata.h"
0015 #include "../data/viewdata.h"
0016 #include "../layout/abstractlayout.h"
0017 #include "../view/view.h"
0018 
0019 // Qt
0020 #include <QDebug>
0021 #include <QDir>
0022 #include <QFile>
0023 #include <QFileInfo>
0024 #include <QLatin1String>
0025 
0026 // KDE
0027 #include <KConfigGroup>
0028 #include <KPluginMetaData>
0029 #include <KSharedConfig>
0030 #include <KPackage/Package>
0031 #include <KPackage/PackageLoader>
0032 
0033 // Plasma
0034 #include <Plasma>
0035 #include <Plasma/Applet>
0036 #include <Plasma/Containment>
0037 
0038 namespace Latte {
0039 namespace Layouts {
0040 
0041 const int Storage::IDNULL = -1;
0042 const int Storage::IDBASE = 0;
0043 
0044 Storage::Storage()
0045 {
0046     qDebug() << " >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> LAYOUTS::STORAGE, TEMP DIR ::: " << m_storageTmpDir.path();
0047 
0048     //! Known Errors / Warnings
0049     s_knownErrors << Data::Generic(Data::Error::APPLETSWITHSAMEID, i18n("Different Applets With Same Id"));
0050     s_knownErrors << Data::Generic(Data::Error::ORPHANEDPARENTAPPLETOFSUBCONTAINMENT, i18n("Orphaned Parent Applet Of Subcontainment"));
0051     s_knownErrors<< Data::Generic(Data::Warning::APPLETANDCONTAINMENTWITHSAMEID, i18n("Different Applet And Containment With Same Id"));
0052     s_knownErrors << Data::Generic(Data::Warning::ORPHANEDSUBCONTAINMENT, i18n("Orphaned Subcontainment"));
0053 
0054 
0055     //! Known SubContainment Families
0056     SubContaimentIdentityData data;
0057     //! Systray Family
0058     m_subIdentities << SubContaimentIdentityData{.cfgGroup="Configuration", .cfgProperty="SystrayContainmentId"};
0059     //! Group applet Family
0060     m_subIdentities << SubContaimentIdentityData{.cfgGroup="Configuration", .cfgProperty="ContainmentId"};
0061 }
0062 
0063 Storage::~Storage()
0064 {
0065 }
0066 
0067 Storage *Storage::self()
0068 {
0069     static Storage store;
0070     return &store;
0071 }
0072 
0073 bool Storage::isWritable(const Layout::GenericLayout *layout) const
0074 {
0075     QFileInfo layoutFileInfo(layout->file());
0076 
0077     if (layoutFileInfo.exists() && !layoutFileInfo.isWritable()) {
0078         return false;
0079     } else {
0080         return true;
0081     }
0082 }
0083 
0084 bool Storage::isLatteContainment(const Plasma::Containment *containment) const
0085 {
0086     if (!containment) {
0087         return false;
0088     }
0089 
0090     if (containment->pluginMetaData().pluginId() == QLatin1String("org.kde.latte.containment")) {
0091         return true;
0092     }
0093 
0094     return false;
0095 }
0096 
0097 bool Storage::isLatteContainment(const KConfigGroup &group) const
0098 {
0099     QString pluginId = group.readEntry("plugin", "");
0100     return pluginId == QLatin1String("org.kde.latte.containment");
0101 }
0102 
0103 bool Storage::isSubContainment(const Plasma::Corona *corona, const Plasma::Applet *applet) const
0104 {
0105     if (!corona || !applet) {
0106         return false;
0107     }
0108 
0109     for (const auto containment : corona->containments()) {
0110         Plasma::Applet *parentApplet = qobject_cast<Plasma::Applet *>(containment->parent());
0111         if (parentApplet && parentApplet == applet) {
0112             return true;
0113         }
0114     }
0115 
0116     return false;
0117 }
0118 
0119 bool Storage::isSubContainment(const KConfigGroup &appletGroup) const
0120 {
0121     return isValid(subContainmentId(appletGroup));
0122 }
0123 
0124 bool Storage::isValid(const int &id)
0125 {
0126     return id >= IDBASE;
0127 }
0128 
0129 int Storage::subContainmentId(const KConfigGroup &appletGroup) const
0130 {
0131     //! cycle through subcontainments identities
0132     for (auto subidentity : m_subIdentities) {
0133         KConfigGroup appletConfigGroup = appletGroup;
0134 
0135         if (!subidentity.cfgGroup.isEmpty()) {
0136             //! if identity provides specific configuration group
0137             if (appletConfigGroup.hasGroup(subidentity.cfgGroup)) {
0138                 appletConfigGroup = appletGroup.group(subidentity.cfgGroup);
0139             }
0140         }
0141 
0142         if (!subidentity.cfgProperty.isEmpty()) {
0143             //! if identity provides specific property for configuration group
0144             if (appletConfigGroup.hasKey(subidentity.cfgProperty)) {
0145                 return appletConfigGroup.readEntry(subidentity.cfgProperty, IDNULL);
0146             }
0147         }
0148     }
0149 
0150     return IDNULL;
0151 }
0152 
0153 int Storage::subIdentityIndex(const KConfigGroup &appletGroup) const
0154 {
0155     if (!isSubContainment(appletGroup)) {
0156         return IDNULL;
0157     }
0158 
0159     //! cycle through subcontainments identities
0160     for (int i=0; i<m_subIdentities.count(); ++i) {
0161         KConfigGroup appletConfigGroup = appletGroup;
0162 
0163         if (!m_subIdentities[i].cfgGroup.isEmpty()) {
0164             //! if identity provides specific configuration group
0165             if (appletConfigGroup.hasGroup(m_subIdentities[i].cfgGroup)) {
0166                 appletConfigGroup = appletGroup.group(m_subIdentities[i].cfgGroup);
0167             }
0168         }
0169 
0170         if (!m_subIdentities[i].cfgProperty.isEmpty()) {
0171             //! if identity provides specific property for configuration group
0172             if (appletConfigGroup.hasKey(m_subIdentities[i].cfgProperty)) {
0173                 int subId = appletConfigGroup.readEntry(m_subIdentities[i].cfgProperty, IDNULL);
0174                 return isValid(subId) ? i : IDNULL;
0175             }
0176         }
0177     }
0178 
0179     return IDNULL;
0180 }
0181 
0182 Plasma::Containment *Storage::subContainmentOf(const Plasma::Corona *corona, const Plasma::Applet *applet)
0183 {
0184     if (!corona || !applet) {
0185         return nullptr;
0186     }
0187 
0188     if (isSubContainment(corona, applet)) {
0189         for (const auto containment : corona->containments()) {
0190             Plasma::Applet *parentApplet = qobject_cast<Plasma::Applet *>(containment->parent());
0191             if (parentApplet && parentApplet == applet) {
0192                 return containment;
0193             }
0194         }
0195     }
0196 
0197     return nullptr;
0198 }
0199 
0200 void Storage::lock(const Layout::GenericLayout *layout)
0201 {
0202     QFileInfo layoutFileInfo(layout->file());
0203 
0204     if (layoutFileInfo.exists() && layoutFileInfo.isWritable()) {
0205         QFile(layout->file()).setPermissions(QFileDevice::ReadUser | QFileDevice::ReadGroup | QFileDevice::ReadOther);
0206     }
0207 }
0208 
0209 void Storage::unlock(const Layout::GenericLayout *layout)
0210 {
0211     QFileInfo layoutFileInfo(layout->file());
0212 
0213     if (layoutFileInfo.exists() && !layoutFileInfo.isWritable()) {
0214         QFile(layout->file()).setPermissions(QFileDevice::ReadUser | QFileDevice::WriteUser | QFileDevice::ReadGroup | QFileDevice::ReadOther);
0215     }
0216 }
0217 
0218 
0219 void Storage::importToCorona(const Layout::GenericLayout *layout)
0220 {
0221     if (!layout->corona()) {
0222         return;
0223     }
0224 
0225     //! Setting mutable for create a containment
0226     layout->corona()->setImmutability(Plasma::Types::Mutable);
0227 
0228     removeAllClonedViews(layout->file());
0229 
0230     QString temp1FilePath = m_storageTmpDir.path() +  "/" + layout->name() + ".multiple.views";
0231     //! we need to copy first the layout file because the kde cache
0232     //! may not have yet been updated (KSharedConfigPtr)
0233     //! this way we make sure at the latest changes stored in the layout file
0234     //! will be also available when changing to Multiple Layouts
0235     QString tempLayoutFilePath = m_storageTmpDir.path() +  "/" + layout->name() + ".multiple.tmplayout";
0236 
0237     //! WE NEED A WAY TO COPY A CONTAINMENT!!!!
0238     QFile tempLayoutFile(tempLayoutFilePath);
0239     QFile copyFile(temp1FilePath);
0240     QFile layoutOriginalFile(layout->file());
0241 
0242     if (tempLayoutFile.exists()) {
0243         tempLayoutFile.remove();
0244     }
0245 
0246     if (copyFile.exists()) {
0247         copyFile.remove();
0248     }
0249 
0250     layoutOriginalFile.copy(tempLayoutFilePath);
0251 
0252     KSharedConfigPtr filePtr = KSharedConfig::openConfig(tempLayoutFilePath);
0253     KSharedConfigPtr newFile = KSharedConfig::openConfig(temp1FilePath);
0254     KConfigGroup copyGroup = KConfigGroup(newFile, "Containments");
0255     KConfigGroup current_containments = KConfigGroup(filePtr, "Containments");
0256 
0257     current_containments.copyTo(&copyGroup);
0258 
0259     newFile->reparseConfiguration();
0260 
0261     //! update ids to unique ones
0262     QString temp2File = newUniqueIdsFile(temp1FilePath, layout);
0263 
0264     //! Finally import the configuration
0265     importLayoutFile(layout, temp2File);
0266 }
0267 
0268 
0269 QString Storage::availableId(QStringList all, QStringList assigned, int base)
0270 {
0271     bool found = false;
0272 
0273     int i = base;
0274 
0275     while (!found && i < 32000) {
0276         QString iStr = QString::number(i);
0277 
0278         if (!all.contains(iStr) && !assigned.contains(iStr)) {
0279             return iStr;
0280         }
0281 
0282         i++;
0283     }
0284 
0285     return QString("");
0286 }
0287 
0288 bool Storage::appletGroupIsValid(const KConfigGroup &appletGroup)
0289 {
0290     return !( appletGroup.keyList().count() == 0
0291               && appletGroup.groupList().count() == 1
0292               && appletGroup.groupList().at(0) == QLatin1String("Configuration")
0293               && appletGroup.group("Configuration").keyList().count() == 1
0294               && appletGroup.group("Configuration").hasKey("PreloadWeight") );
0295 }
0296 
0297 QStringList Storage::containmentsIds(const QString &filepath)
0298 {
0299     QStringList ids;
0300 
0301     KSharedConfigPtr filePtr = KSharedConfig::openConfig(filepath);
0302     KConfigGroup containments = KConfigGroup(filePtr, "Containments");
0303 
0304     for(const auto &cId : containments.groupList()) {
0305         ids << cId;
0306     }
0307 
0308     return ids;
0309 }
0310 
0311 QStringList Storage::appletsIds(const QString &filepath)
0312 {
0313     QStringList ids;
0314 
0315     KSharedConfigPtr filePtr = KSharedConfig::openConfig(filepath);
0316     KConfigGroup containments = KConfigGroup(filePtr, "Containments");
0317 
0318     for(const auto &cId : containments.groupList()) {
0319         for(const auto &aId : containments.group(cId).group("Applets").groupList()) {
0320             ids << aId;
0321         }
0322     }
0323 
0324     return ids;
0325 }
0326 
0327 QString Storage::newUniqueIdsFile(QString originFile, const Layout::GenericLayout *destinationLayout)
0328 {
0329     if (!destinationLayout) {
0330         return QString();
0331     }
0332 
0333     QString currentdestinationname = destinationLayout->name();
0334     QString currentdestinationfile = "";
0335 
0336     if (!destinationLayout->hasCorona()) {
0337         currentdestinationfile = destinationLayout->file();
0338     }
0339 
0340     QString tempFile = m_storageTmpDir.path() + "/" + currentdestinationname + ".views.newids";
0341 
0342     QFile copyFile(tempFile);
0343 
0344     if (copyFile.exists()) {
0345         copyFile.remove();
0346     }
0347 
0348     //! BEGIN updating the ids in the temp file
0349     QStringList allIds;
0350 
0351     if (destinationLayout->hasCorona()) {
0352         allIds << destinationLayout->corona()->containmentsIds();
0353         allIds << destinationLayout->corona()->appletsIds();
0354     } else {
0355         allIds << containmentsIds(currentdestinationfile);
0356         allIds << appletsIds(currentdestinationfile);
0357     }
0358 
0359     QStringList toInvestigateContainmentIds;
0360     QStringList toInvestigateAppletIds;
0361     QStringList toInvestigateSubContIds;
0362 
0363     //! first is the subcontainment id
0364     QHash<QString, QString> subParentContainmentIds;
0365     QHash<QString, QString> subAppletIds;
0366 
0367     //qDebug() << "Ids:" << allIds;
0368 
0369     //qDebug() << "to copy containments: " << toCopyContainmentIds;
0370     //qDebug() << "to copy applets: " << toCopyAppletIds;
0371 
0372     QStringList assignedIds;
0373     QHash<QString, QString> assigned;
0374 
0375     KSharedConfigPtr filePtr = KSharedConfig::openConfig(originFile);
0376     KConfigGroup investigate_conts = KConfigGroup(filePtr, "Containments");
0377 
0378     //! Record the containment and applet ids
0379     for (const auto &cId : investigate_conts.groupList()) {
0380         toInvestigateContainmentIds << cId;
0381         auto appletsEntries = investigate_conts.group(cId).group("Applets");
0382         toInvestigateAppletIds << appletsEntries.groupList();
0383 
0384         //! investigate for subcontainments
0385         for (const auto &appletId : appletsEntries.groupList()) {
0386             int subId = subContainmentId(appletsEntries.group(appletId));
0387 
0388             //! It is a subcontainment !!!
0389             if (isValid(subId)) {
0390                 QString tSubIdStr = QString::number(subId);
0391                 toInvestigateSubContIds << tSubIdStr;
0392                 subParentContainmentIds[tSubIdStr] = cId;
0393                 subAppletIds[tSubIdStr] = appletId;
0394                 qDebug() << "subcontainment was found in the containment...";
0395             }
0396         }
0397     }
0398 
0399     //! Reassign containment and applet ids to unique ones
0400     for (const auto &contId : toInvestigateContainmentIds) {        
0401         QString newId;
0402 
0403         if (contId.toInt()>=12 && !allIds.contains(contId) && !assignedIds.contains(contId)) {
0404             newId = contId;
0405         } else {
0406             newId = availableId(allIds, assignedIds, 12);
0407         }
0408 
0409         assignedIds << newId;
0410         assigned[contId] = newId;
0411     }
0412 
0413     for (const auto &appId : toInvestigateAppletIds) {
0414         QString newId;
0415 
0416         if (appId.toInt()>=40 && !allIds.contains(appId) && !assignedIds.contains(appId)) {
0417             newId = appId;
0418         } else {
0419             newId = availableId(allIds, assignedIds, 40);
0420         }
0421 
0422         assignedIds << newId;
0423         assigned[appId] = newId;
0424     }
0425 
0426     qDebug() << "ALL CORONA IDS ::: " << allIds;
0427     qDebug() << "FULL ASSIGNMENTS ::: " << assigned;
0428 
0429     for (const auto &cId : toInvestigateContainmentIds) {
0430         QString value = assigned[cId];
0431 
0432         if (assigned.contains(value)) {
0433             QString value2 = assigned[value];
0434 
0435             if (cId != assigned[cId] && !value2.isEmpty() && cId == value2) {
0436                 qDebug() << "PROBLEM APPEARED !!!! FOR :::: " << cId << " .. fixed ..";
0437                 assigned[cId] = cId;
0438                 assigned[value] = value;
0439             }
0440         }
0441     }
0442 
0443     for (const auto &aId : toInvestigateAppletIds) {
0444         QString value = assigned[aId];
0445 
0446         if (assigned.contains(value)) {
0447             QString value2 = assigned[value];
0448 
0449             if (aId != assigned[aId] && !value2.isEmpty() && aId == value2) {
0450                 qDebug() << "PROBLEM APPEARED !!!! FOR :::: " << aId << " .. fixed ..";
0451                 assigned[aId] = aId;
0452                 assigned[value] = value;
0453             }
0454         }
0455     }
0456 
0457     qDebug() << "FIXED FULL ASSIGNMENTS ::: " << assigned;
0458 
0459     //! update applet ids in their containment order and in MultipleLayouts update also the layoutId
0460     for (const auto &cId : investigate_conts.groupList()) {
0461         //! Update options that contain applet ids
0462         //! (appletOrder) and (lockedZoomApplets) and (userBlocksColorizingApplets)
0463         QStringList options;
0464         options << "appletOrder" << "lockedZoomApplets" << "userBlocksColorizingApplets";
0465 
0466         for (const auto &settingStr : options) {
0467             QString order1 = investigate_conts.group(cId).group("General").readEntry(settingStr, QString());
0468 
0469             if (!order1.isEmpty()) {
0470                 QStringList order1Ids = order1.split(";");
0471                 QStringList fixedOrder1Ids;
0472 
0473                 for (int i = 0; i < order1Ids.count(); ++i) {
0474                     fixedOrder1Ids.append(assigned[order1Ids[i]]);
0475                 }
0476 
0477                 QString fixedOrder1 = fixedOrder1Ids.join(";");
0478                 investigate_conts.group(cId).group("General").writeEntry(settingStr, fixedOrder1);
0479             }
0480         }
0481 
0482         if (destinationLayout->hasCorona() && destinationLayout->corona()->layoutsManager()->memoryUsage() == MemoryUsage::MultipleLayouts) {
0483             //! will be added in main corona multiple layouts file
0484             investigate_conts.group(cId).writeEntry("layoutId", destinationLayout->name());
0485         } else {
0486             //! will be added in inactive layout
0487             investigate_conts.group(cId).writeEntry("layoutId", QString());
0488         }
0489     }
0490 
0491     //! must update also the sub id in its applet
0492     for (const auto &subId : toInvestigateSubContIds) {
0493         KConfigGroup subParentContainment = investigate_conts.group(subParentContainmentIds[subId]);
0494         KConfigGroup subAppletConfig = subParentContainment.group("Applets").group(subAppletIds[subId]);
0495 
0496         int entityIndex = subIdentityIndex(subAppletConfig);
0497 
0498         if (entityIndex >= 0) {
0499             if (!m_subIdentities[entityIndex].cfgGroup.isEmpty()) {
0500                 subAppletConfig = subAppletConfig.group(m_subIdentities[entityIndex].cfgGroup);
0501             }
0502 
0503             if (!m_subIdentities[entityIndex].cfgProperty.isEmpty()) {
0504                 subAppletConfig.writeEntry(m_subIdentities[entityIndex].cfgProperty, assigned[subId]);
0505                 subParentContainment.sync();
0506             }
0507         }
0508     }
0509 
0510     investigate_conts.sync();
0511 
0512     //! Copy To Temp 2 File And Update Correctly The Ids
0513     KSharedConfigPtr file2Ptr = KSharedConfig::openConfig(tempFile);
0514     KConfigGroup fixedNewContainmets = KConfigGroup(file2Ptr, "Containments");
0515 
0516     for (const auto &contId : investigate_conts.groupList()) {
0517         QString pluginId = investigate_conts.group(contId).readEntry("plugin", "");
0518 
0519         if (pluginId != "org.kde.desktopcontainment") { //!don't add ghost containments
0520             KConfigGroup newContainmentGroup = fixedNewContainmets.group(assigned[contId]);
0521             investigate_conts.group(contId).copyTo(&newContainmentGroup);
0522 
0523             newContainmentGroup.group("Applets").deleteGroup();
0524 
0525             for (const auto &appId : investigate_conts.group(contId).group("Applets").groupList()) {
0526                 KConfigGroup appletGroup = investigate_conts.group(contId).group("Applets").group(appId);
0527                 KConfigGroup newAppletGroup = fixedNewContainmets.group(assigned[contId]).group("Applets").group(assigned[appId]);
0528                 appletGroup.copyTo(&newAppletGroup);
0529             }
0530         }
0531     }
0532 
0533     file2Ptr->reparseConfiguration();
0534 
0535     return tempFile;
0536 }
0537 
0538 void Storage::syncToLayoutFile(const Layout::GenericLayout *layout, bool removeLayoutId)
0539 {
0540     if (!layout->corona() || !isWritable(layout)) {
0541         return;
0542     }
0543 
0544     KSharedConfigPtr filePtr = KSharedConfig::openConfig(layout->file());
0545 
0546     KConfigGroup oldContainments = KConfigGroup(filePtr, "Containments");
0547     oldContainments.deleteGroup();
0548 
0549     qDebug() << " LAYOUT :: " << layout->name() << " is syncing its original file.";
0550 
0551     for (const auto containment : *layout->containments()) {
0552         if (removeLayoutId) {
0553             containment->config().writeEntry("layoutId", "");
0554         }
0555 
0556         KConfigGroup newGroup = oldContainments.group(QString::number(containment->id()));
0557         containment->config().copyTo(&newGroup);
0558 
0559         if (!removeLayoutId) {
0560             newGroup.writeEntry("layoutId", "");
0561         }
0562 
0563         newGroup.sync();
0564     }
0565 
0566     filePtr->reparseConfiguration();
0567     removeAllClonedViews(layout->file());
0568 }
0569 
0570 void Storage::moveToLayoutFile(const QString &layoutName)
0571 {
0572     if (layoutName.isEmpty()) {
0573         return;
0574     }
0575 
0576     QString linkedFilePath = Importer::layoutUserFilePath(Layout::MULTIPLELAYOUTSHIDDENNAME);
0577     QString layoutFilePath = Importer::layoutUserFilePath(layoutName);
0578 
0579     if (linkedFilePath.isEmpty() || layoutFilePath.isEmpty() || !QFileInfo(linkedFilePath).exists() || !QFileInfo(layoutFilePath).exists()) {
0580         return;
0581     }
0582 
0583     KSharedConfigPtr layoutFilePtr = KSharedConfig::openConfig(layoutFilePath);
0584     KConfigGroup singleContainments = KConfigGroup(layoutFilePtr, "Containments");
0585     singleContainments.deleteGroup();
0586 
0587     KSharedConfigPtr multiFilePtr = KSharedConfig::openConfig(linkedFilePath);
0588     KConfigGroup multiContainments = KConfigGroup(multiFilePtr, "Containments");
0589 
0590     for(const auto &cId : multiContainments.groupList()) {
0591         QString cname = multiContainments.group(cId).readEntry("layoutId", QString());
0592 
0593         if (!cname.isEmpty() && cname == layoutName) {
0594             multiContainments.group(cId).writeEntry("layoutId", "");
0595             KConfigGroup singleGroup = singleContainments.group(cId);
0596             multiContainments.group(cId).copyTo(&singleGroup);
0597             singleGroup.writeEntry("layoutId", "");
0598             singleGroup.sync();
0599 
0600             multiContainments.group(cId).deleteGroup();
0601         }
0602     }
0603 
0604     layoutFilePtr->reparseConfiguration();
0605     removeAllClonedViews(layoutFilePath);
0606 }
0607 
0608 QList<Plasma::Containment *> Storage::importLayoutFile(const Layout::GenericLayout *layout, QString file)
0609 {
0610     KSharedConfigPtr filePtr = KSharedConfig::openConfig(file);
0611     auto newContainments = layout->corona()->importLayout(KConfigGroup(filePtr, ""));
0612 
0613     QList<Plasma::Containment *> importedViews;
0614 
0615     for (const auto containment : newContainments) {
0616         if (isLatteContainment(containment)) {
0617             importedViews << containment;
0618         }
0619     }
0620 
0621     return importedViews;
0622 }
0623 
0624 void Storage::importContainments(const QString &originFile, const QString &destinationFile)
0625 {
0626     if (originFile.isEmpty() || destinationFile.isEmpty()) {
0627         return;
0628     }
0629 
0630     KSharedConfigPtr originPtr = KSharedConfig::openConfig(originFile);
0631     KSharedConfigPtr destinationPtr = KSharedConfig::openConfig(destinationFile);
0632 
0633     KConfigGroup originContainments = KConfigGroup(originPtr, "Containments");
0634     KConfigGroup destinationContainments = KConfigGroup(destinationPtr, "Containments");
0635 
0636     for (const auto originContId : originContainments.groupList()) {
0637         KConfigGroup destinationContainment(&destinationContainments, originContId);
0638         originContainments.group(originContId).copyTo(&destinationContainment);
0639     }
0640 
0641     destinationContainments.sync();
0642 }
0643 
0644 Data::View Storage::newView(const Layout::GenericLayout *destinationLayout, const Data::View &nextViewData)
0645 {
0646     if (!destinationLayout || nextViewData.originFile().isEmpty()) {
0647         return Data::View();
0648     }
0649 
0650     qDebug() << "new view for layout";
0651 
0652     if (destinationLayout->hasCorona()) {
0653         //! Setting mutable for create a containment
0654         destinationLayout->corona()->setImmutability(Plasma::Types::Mutable);
0655     }
0656 
0657     QString templateFile = nextViewData.originFile();
0658     //! copy view template path in temp file
0659     QString templateTmpAbsolutePath = m_storageTmpDir.path() + "/" + QFileInfo(templateFile).fileName() + ".newids";
0660 
0661     if (QFile(templateTmpAbsolutePath).exists()) {
0662         QFile(templateTmpAbsolutePath).remove();
0663     }
0664 
0665     QFile(templateFile).copy(templateTmpAbsolutePath);
0666 
0667     //! update ids to unique ones
0668     QString temp2File = newUniqueIdsFile(templateTmpAbsolutePath, destinationLayout);
0669 
0670     //! update view containment data in case next data are provided
0671     if (nextViewData.state() != Data::View::IsInvalid) {
0672 
0673         KSharedConfigPtr lFile = KSharedConfig::openConfig(temp2File);
0674         KConfigGroup containments = KConfigGroup(lFile, "Containments");
0675 
0676         for (const auto cId : containments.groupList()) {
0677             if (Layouts::Storage::self()->isLatteContainment(containments.group(cId))) {
0678                 //! first view we will find, we update its value
0679                 updateView(containments.group(cId), nextViewData);
0680                 break;
0681             }
0682         }
0683 
0684         lFile->reparseConfiguration();
0685     }
0686 
0687     Data::ViewsTable updatedNextViews = views(temp2File);
0688 
0689     if (updatedNextViews.rowCount() <= 0) {
0690         return Data::View();
0691     }
0692 
0693     if (destinationLayout->hasCorona()) {
0694         //! import views for active layout
0695         QList<Plasma::Containment *> importedViews = importLayoutFile(destinationLayout, temp2File);
0696 
0697         Plasma::Containment *newContainment = (importedViews.size() == 1 ? importedViews[0] : nullptr);
0698 
0699         if (!newContainment || !newContainment->kPackage().isValid()) {
0700             qWarning() << "the requested containment plugin can not be located or loaded from:" << templateFile;
0701             return Data::View();
0702         }
0703     } else {
0704         //! import views for inactive layout
0705         importContainments(temp2File, destinationLayout->file());
0706     }
0707 
0708     return updatedNextViews[0];
0709 }
0710 
0711 void Storage::clearExportedLayoutSettings(KConfigGroup &layoutSettingsGroup)
0712 {
0713     layoutSettingsGroup.writeEntry("preferredForShortcutsTouched", false);
0714     layoutSettingsGroup.writeEntry("lastUsedActivity", QString());
0715     layoutSettingsGroup.writeEntry("activities", QStringList());
0716     layoutSettingsGroup.sync();
0717 }
0718 
0719 bool Storage::exportTemplate(const QString &originFile, const QString &destinationFile,const Data::AppletsTable &approvedApplets)
0720 {
0721     if (originFile.isEmpty() || !QFile(originFile).exists() || destinationFile.isEmpty()) {
0722         return false;
0723     }
0724 
0725     if (QFile(destinationFile).exists()) {
0726         QFile::remove(destinationFile);
0727     }
0728 
0729     QFile(originFile).copy(destinationFile);
0730 
0731     KSharedConfigPtr destFilePtr = KSharedConfig::openConfig(destinationFile);
0732     destFilePtr->reparseConfiguration();
0733 
0734     KConfigGroup containments = KConfigGroup(destFilePtr, "Containments");
0735 
0736     QStringList rejectedSubContainments;
0737 
0738     //! clear applets that are not approved
0739     for (const auto &cId : containments.groupList()) {
0740         //! clear properties
0741         containments.group(cId).writeEntry("layoutId", QString());
0742         if (isLatteContainment(containments.group(cId))) {
0743             containments.group(cId).writeEntry("isPreferredForShortcuts", false);
0744         }
0745 
0746         //! clear applets
0747         auto applets = containments.group(cId).group("Applets");
0748         for (const auto &aId: applets.groupList()) {
0749             QString pluginId = applets.group(aId).readEntry("plugin", "");
0750 
0751             if (!approvedApplets.containsId(pluginId)) {
0752                 if (!isSubContainment(applets.group(aId))) {
0753                     //!remove all configuration for that applet
0754                     for (const auto &configId: applets.group(aId).groupList()) {
0755                         applets.group(aId).group(configId).deleteGroup();
0756                     }
0757                 } else {
0758                     //! register which subcontaiments should return to default properties
0759                     rejectedSubContainments << QString::number(subContainmentId(applets.group(aId)));
0760                 }
0761             }
0762         }
0763     }
0764 
0765     //! clear rejected SubContainments
0766     for (const auto &cId : containments.groupList()) {
0767         if (rejectedSubContainments.contains(cId)) {
0768             containments.group(cId).group("General").deleteGroup();
0769         }
0770     };
0771 
0772     KConfigGroup layoutSettingsGrp(destFilePtr, "LayoutSettings");
0773     clearExportedLayoutSettings(layoutSettingsGrp);
0774     destFilePtr->reparseConfiguration();
0775     removeAllClonedViews(destinationFile);
0776 
0777     return true;
0778 }
0779 
0780 bool Storage::exportTemplate(const Layout::GenericLayout *layout, Plasma::Containment *containment, const QString &destinationFile, const Data::AppletsTable &approvedApplets)
0781 {
0782     if (!layout || !containment || destinationFile.isEmpty()) {
0783         return false;
0784     }
0785 
0786     if (QFile(destinationFile).exists()) {
0787         QFile::remove(destinationFile);
0788     }
0789 
0790     KSharedConfigPtr destFilePtr = KSharedConfig::openConfig(destinationFile);
0791     destFilePtr->reparseConfiguration();
0792 
0793     KConfigGroup copied_conts = KConfigGroup(destFilePtr, "Containments");
0794     KConfigGroup copied_c1 = KConfigGroup(&copied_conts, QString::number(containment->id()));
0795 
0796     containment->config().copyTo(&copied_c1);
0797 
0798     //!investigate if there are subcontainments in the containment to copy also
0799 
0800     //! subId, subAppletId
0801     QHash<uint, QString> subInfo;
0802     auto applets = containment->config().group("Applets");
0803 
0804     for (const auto &applet : applets.groupList()) {
0805         int tSubId = subContainmentId(applets.group(applet));
0806 
0807         //! It is a subcontainment !!!
0808         if (isValid(tSubId)) {
0809             subInfo[tSubId] = applet;
0810             qDebug() << "subcontainment with id "<< tSubId << " was found in the containment... ::: " << containment->id();
0811         }
0812     }
0813 
0814     if (subInfo.count() > 0) {
0815         for(const auto subId : subInfo.keys()) {
0816             Plasma::Containment *subcontainment{nullptr};
0817 
0818             for (const auto containment : layout->corona()->containments()) {
0819                 if (containment->id() == subId) {
0820                     subcontainment = containment;
0821                     break;
0822                 }
0823             }
0824 
0825             if (subcontainment) {
0826                 KConfigGroup copied_sub = KConfigGroup(&copied_conts, QString::number(subcontainment->id()));
0827                 subcontainment->config().copyTo(&copied_sub);
0828             }
0829         }
0830     }
0831     //! end of subcontainments specific code
0832 
0833     QStringList rejectedSubContainments;
0834 
0835     //! clear applets that are not approved
0836     for (const auto &cId : copied_conts.groupList()) {
0837         //! clear properties
0838         copied_conts.group(cId).writeEntry("layoutId", QString());
0839         if (isLatteContainment(copied_conts.group(cId))) {
0840             copied_conts.group(cId).writeEntry("isPreferredForShortcuts", false);
0841         }
0842 
0843         //! clear applets
0844         auto applets = copied_conts.group(cId).group("Applets");
0845         for (const auto &aId: applets.groupList()) {
0846             QString pluginId = applets.group(aId).readEntry("plugin", "");
0847 
0848             if (!approvedApplets.containsId(pluginId)) {
0849                 if (!isSubContainment(applets.group(aId))) {
0850                     //!remove all configuration for that applet
0851                     for (const auto &configId: applets.group(aId).groupList()) {
0852                         applets.group(aId).group(configId).deleteGroup();
0853                     }
0854                 } else {
0855                     //! register which subcontaiments should return to default properties
0856                     rejectedSubContainments << QString::number(subContainmentId(applets.group(aId)));
0857                 }
0858             }
0859         }
0860     }
0861 
0862     //! clear rejected SubContainments
0863     for (const auto &cId : copied_conts.groupList()) {
0864         if (rejectedSubContainments.contains(cId)) {
0865             copied_conts.group(cId).group("General").deleteGroup();
0866         }
0867     };
0868 
0869     KConfigGroup layoutSettingsGrp(destFilePtr, "LayoutSettings");
0870     clearExportedLayoutSettings(layoutSettingsGrp);
0871     destFilePtr->reparseConfiguration();
0872     removeAllClonedViews(destinationFile);
0873 
0874     return true;
0875 }
0876 
0877 bool Storage::hasDifferentAppletsWithSameId(const Layout::GenericLayout *layout, Data::Error &error)
0878 {
0879     if (!layout  || layout->file().isEmpty() || !QFile(layout->file()).exists()) {
0880         return false;
0881     }
0882 
0883     error.id = s_knownErrors[Data::Error::APPLETSWITHSAMEID].id;
0884     error.name = s_knownErrors[Data::Error::APPLETSWITHSAMEID].name;
0885 
0886     if (layout->isActive()) { // active layout
0887         QStringList registeredapplets;
0888         QStringList conflictedapplets;
0889 
0890         //! split ids to normal registered and conflicted
0891         for (const auto containment : *layout->containments()) {
0892             QString cid = QString::number(containment->id());
0893 
0894             for (const auto applet : containment->applets()) {
0895                 QString aid = QString::number(applet->id());
0896 
0897                 if (!registeredapplets.contains(aid)) {
0898                     registeredapplets << aid;
0899                 } else if (!conflictedapplets.contains(aid)) {
0900                     conflictedapplets << aid;
0901                 }
0902             }
0903         }
0904 
0905         //! create error data
0906         for (const auto containment : *layout->containments()) {
0907             QString cid = QString::number(containment->id());
0908 
0909             for (const auto applet : containment->applets()) {
0910                 QString aid = QString::number(applet->id());
0911 
0912                 if (!conflictedapplets.contains(aid)) {
0913                    continue;
0914                 }
0915 
0916                 Data::ErrorInformation errorinfo;
0917                 errorinfo.id = QString::number(error.information.rowCount());
0918                 errorinfo.containment = metadata(containment->pluginMetaData().pluginId());
0919                 errorinfo.containment.storageId = cid;
0920                 errorinfo.applet = metadata(applet->pluginMetaData().pluginId());
0921                 errorinfo.applet.storageId = aid;
0922 
0923                 error.information << errorinfo;
0924             }
0925         }
0926     } else { // inactive layout
0927         KSharedConfigPtr lfile = KSharedConfig::openConfig(layout->file());
0928         KConfigGroup containmentsEntries = KConfigGroup(lfile, "Containments");
0929 
0930         QStringList registeredapplets;
0931         QStringList conflictedapplets;
0932 
0933         //! split ids to normal registered and conflicted
0934         for (const auto &cid : containmentsEntries.groupList()) {
0935             for (const auto &aid : containmentsEntries.group(cid).group("Applets").groupList()) {
0936                 if (!registeredapplets.contains(aid)) {
0937                     registeredapplets << aid;
0938                 } else if (!conflictedapplets.contains(aid)) {
0939                     conflictedapplets << aid;
0940                 }
0941             }
0942         }
0943 
0944         //! create error data
0945         for (const auto &cid : containmentsEntries.groupList()) {
0946             for (const auto &aid : containmentsEntries.group(cid).group("Applets").groupList()) {
0947                 if (!conflictedapplets.contains(aid)) {
0948                    continue;
0949                 }
0950 
0951                 Data::ErrorInformation errorinfo;
0952                 errorinfo.id = QString::number(error.information.rowCount());
0953                 errorinfo.containment = metadata(containmentsEntries.group(cid).readEntry("plugin", ""));
0954                 errorinfo.containment.storageId = cid;
0955                 errorinfo.applet = metadata(containmentsEntries.group(cid).group("Applets").group(aid).readEntry("plugin", ""));
0956                 errorinfo.applet.storageId = aid;
0957 
0958                 error.information << errorinfo;
0959             }
0960         }
0961     }
0962 
0963     return !error.information.isEmpty();
0964 }
0965 
0966 bool Storage::hasAppletsAndContainmentsWithSameId(const Layout::GenericLayout *layout, Data::Warning &warning)
0967 {
0968     if (!layout  || layout->file().isEmpty() || !QFile(layout->file()).exists()) {
0969         return false;
0970     }
0971 
0972     warning.id = s_knownErrors[Data::Error::APPLETANDCONTAINMENTWITHSAMEID].id;
0973     warning.name = s_knownErrors[Data::Error::APPLETANDCONTAINMENTWITHSAMEID].name;
0974 
0975     if (layout->isActive()) { // active layout
0976         QStringList registeredcontainments;
0977         QStringList conflicted;
0978 
0979         //! discover normal containment ids
0980         for (const auto containment : *layout->containments()) {
0981             QString cid = QString::number(containment->id());
0982 
0983             if (registeredcontainments.contains(cid)) {
0984                 continue;
0985             }
0986 
0987             registeredcontainments << cid;
0988         }
0989 
0990         //! discover conflicted ids between containments and applets
0991         for (const auto containment : *layout->containments()) {
0992             QString cid = QString::number(containment->id());
0993 
0994             for (const auto applet : containment->applets()) {
0995                 QString aid = QString::number(applet->id());
0996 
0997                 if (!registeredcontainments.contains(aid)) {
0998                     continue;
0999                 } else if (!conflicted.contains(aid)) {
1000                     conflicted << aid;
1001                 }
1002             }
1003         }
1004 
1005         //! create warning data
1006         for (const auto containment : *layout->containments()) {
1007             QString cid = QString::number(containment->id());
1008 
1009             if (conflicted.contains(cid)) {
1010                 Data::WarningInformation warninginfo;
1011                 warninginfo.id = QString::number(warning.information.rowCount());
1012                 warninginfo.containment = metadata(containment->pluginMetaData().pluginId());
1013                 warninginfo.containment.storageId = cid;
1014 
1015                 warning.information << warninginfo;
1016             }
1017 
1018             for (const auto applet : containment->applets()) {
1019                 QString aid = QString::number(applet->id());
1020 
1021                 if (!conflicted.contains(aid)) {
1022                    continue;
1023                 }
1024 
1025                 Data::WarningInformation warninginfo;
1026                 warninginfo.id = QString::number(warning.information.rowCount());
1027                 warninginfo.containment = metadata(containment->pluginMetaData().pluginId());
1028                 warninginfo.containment.storageId = cid;
1029                 warninginfo.applet = metadata(applet->pluginMetaData().pluginId());
1030                 warninginfo.applet.storageId = aid;
1031 
1032                 warning.information << warninginfo;
1033             }
1034         }
1035     } else { // inactive layout
1036         KSharedConfigPtr lfile = KSharedConfig::openConfig(layout->file());
1037         KConfigGroup containmentsEntries = KConfigGroup(lfile, "Containments");
1038 
1039         QStringList registeredcontainments;
1040         QStringList conflicted;
1041 
1042         //! discover normal containment ids
1043         for (const auto &cid : containmentsEntries.groupList()) {
1044             if (registeredcontainments.contains(cid)) {
1045                 continue;
1046             }
1047 
1048             registeredcontainments << cid;
1049         }
1050 
1051         //! discover conflicted ids between containments and applets
1052         for (const auto &cid : containmentsEntries.groupList()) {
1053             for (const auto &aid : containmentsEntries.group(cid).group("Applets").groupList()) {
1054                 if (!registeredcontainments.contains(aid)) {
1055                     continue;
1056                 } else if (!conflicted.contains(aid)) {
1057                     conflicted << aid;
1058                 }
1059             }
1060         }
1061 
1062         //! create warning data
1063         for (const auto &cid : containmentsEntries.groupList()) {
1064             if (conflicted.contains(cid)) {
1065                 Data::WarningInformation warninginfo;
1066                 warninginfo.id = QString::number(warning.information.rowCount());
1067                 warninginfo.containment = metadata(containmentsEntries.group(cid).readEntry("plugin", ""));
1068                 warninginfo.containment.storageId = cid;
1069 
1070                 warning.information << warninginfo;
1071             }
1072 
1073             for (const auto &aid : containmentsEntries.group(cid).group("Applets").groupList()) {
1074                 if (!conflicted.contains(aid)) {
1075                    continue;
1076                 }
1077 
1078                 Data::WarningInformation warninginfo;
1079                 warninginfo.id = QString::number(warning.information.rowCount());
1080                 warninginfo.containment = metadata(containmentsEntries.group(cid).readEntry("plugin", ""));
1081                 warninginfo.containment.storageId = cid;
1082                 warninginfo.applet = metadata(containmentsEntries.group(cid).group("Applets").group(aid).readEntry("plugin", ""));
1083                 warninginfo.applet.storageId = aid;
1084 
1085                 warning.information << warninginfo;
1086             }
1087         }
1088     }
1089 
1090     return !warning.information.isEmpty();
1091 }
1092 
1093 bool Storage::hasOrphanedParentAppletOfSubContainment(const Layout::GenericLayout *layout, Data::Error &error)
1094 {
1095     if (!layout  || layout->file().isEmpty() || !QFile(layout->file()).exists()) {
1096         return false;
1097     }
1098 
1099     error.id = s_knownErrors[Data::Error::ORPHANEDPARENTAPPLETOFSUBCONTAINMENT].id;
1100     error.name = s_knownErrors[Data::Error::ORPHANEDPARENTAPPLETOFSUBCONTAINMENT].name;
1101 
1102     Data::ViewsTable views = Layouts::Storage::self()->views(layout);
1103 
1104     if (layout->isActive()) { // active layout
1105 
1106         //! create error data
1107         for (const auto containment : *layout->containments()) {
1108             QString cid = QString::number(containment->id());
1109 
1110             for (const auto applet : containment->applets()) {
1111                 QString aid = QString::number(applet->id());
1112 
1113                 int subid = subContainmentId(applet->config());
1114 
1115                 if (subid == IDNULL || hasContainment(layout, subid)) {
1116                     continue;
1117                 }
1118 
1119                 Data::ErrorInformation errorinfo;
1120                 errorinfo.id = QString::number(error.information.rowCount());
1121                 errorinfo.containment = metadata(containment->pluginMetaData().pluginId());
1122                 errorinfo.containment.storageId = cid;
1123                 errorinfo.applet = metadata(applet->pluginMetaData().pluginId());
1124                 errorinfo.applet.storageId = aid;
1125                 errorinfo.applet.subcontainmentId = subid;
1126 
1127                 error.information << errorinfo;
1128             }
1129         }
1130     } else {
1131         KSharedConfigPtr lfile = KSharedConfig::openConfig(layout->file());
1132         KConfigGroup containmentsEntries = KConfigGroup(lfile, "Containments");
1133 
1134         //! create error data
1135         for (const auto &cid : containmentsEntries.groupList()) {
1136             for (const auto &aid : containmentsEntries.group(cid).group("Applets").groupList()) {
1137                 int subid = subContainmentId(containmentsEntries.group(cid).group("Applets").group(aid));
1138 
1139                 if (subid == IDNULL || hasContainment(layout, subid)) {
1140                     continue;
1141                 }
1142 
1143                 Data::ErrorInformation errorinfo;
1144                 errorinfo.id = QString::number(error.information.rowCount());
1145                 errorinfo.containment = metadata(containmentsEntries.group(cid).readEntry("plugin", ""));
1146                 errorinfo.containment.storageId = cid;
1147                 errorinfo.applet = metadata(containmentsEntries.group(cid).group("Applets").group(aid).readEntry("plugin", ""));
1148                 errorinfo.applet.storageId = aid;
1149                 errorinfo.applet.subcontainmentId = subid;
1150 
1151                 error.information << errorinfo;
1152             }
1153         }
1154     }
1155 
1156     Data::Warning warning1;
1157     if (!error.information.isEmpty() && hasOrphanedSubContainments(layout, warning1)) {
1158         error.information << warning1.information;
1159     }
1160 
1161     return !error.information.isEmpty();
1162 }
1163 
1164 bool Storage::hasOrphanedSubContainments(const Layout::GenericLayout *layout, Data::Warning &warning)
1165 {
1166     if (!layout  || layout->file().isEmpty() || !QFile(layout->file()).exists()) {
1167         return false;
1168     }
1169 
1170     warning.id = s_knownErrors[Data::Error::ORPHANEDSUBCONTAINMENT].id;
1171     warning.name = s_knownErrors[Data::Error::ORPHANEDSUBCONTAINMENT].name;
1172 
1173     Data::ViewsTable views = Layouts::Storage::self()->views(layout);
1174 
1175     if (layout->isActive()) { // active layout
1176         //! create warning data
1177         for (const auto containment : *layout->containments()) {
1178             QString cid = QString::number(containment->id());
1179 
1180             Plasma::Applet *parentApplet = qobject_cast<Plasma::Applet *>(containment->parent());
1181             Plasma::Containment *parentContainment = parentApplet ? qobject_cast<Plasma::Containment *>(parentApplet->parent()) : nullptr;
1182 
1183             if (isLatteContainment(containment) || (parentApplet && parentContainment && layout->contains(parentContainment))) {
1184                 //! is latte containment or is subcontainment that belongs to latte containment
1185                 continue;
1186             }
1187 
1188             Data::WarningInformation warninginfo;
1189             warninginfo.id = QString::number(warning.information.rowCount());
1190             warninginfo.containment = metadata(containment->pluginMetaData().pluginId());
1191             warninginfo.containment.storageId = cid;
1192             warning.information << warninginfo;
1193         }
1194     } else { // inactive layout
1195         KSharedConfigPtr lfile = KSharedConfig::openConfig(layout->file());
1196         KConfigGroup containmentsEntries = KConfigGroup(lfile, "Containments");
1197 
1198         //! create warning data
1199         for (const auto &cid : containmentsEntries.groupList()) {
1200             if (views.hasContainmentId(cid)) {
1201                 continue;
1202             }
1203 
1204             Data::WarningInformation warninginfo;
1205             warninginfo.id = QString::number(warning.information.rowCount());
1206             warninginfo.containment = metadata(containmentsEntries.group(cid).readEntry("plugin", ""));
1207             warninginfo.containment.storageId = cid;
1208             warning.information << warninginfo;
1209         }
1210     }
1211 
1212     return !warning.information.isEmpty();
1213 }
1214 
1215 Data::ErrorsList Storage::errors(const Layout::GenericLayout *layout)
1216 {
1217     Data::ErrorsList errs;
1218 
1219     if (!layout  || layout->file().isEmpty() || !QFile(layout->file()).exists()) {
1220         return errs;
1221     }
1222 
1223     Data::Error error1;
1224 
1225     if (hasDifferentAppletsWithSameId(layout, error1)) {
1226         errs << error1;
1227     }
1228 
1229     Data::Error error2;
1230 
1231     if (hasOrphanedParentAppletOfSubContainment(layout, error2)) {
1232         errs << error2;
1233     }
1234 
1235     return errs;
1236 }
1237 
1238 Data::WarningsList Storage::warnings(const Layout::GenericLayout *layout)
1239 {
1240     Data::WarningsList warns;
1241 
1242     if (!layout  || layout->file().isEmpty() || !QFile(layout->file()).exists()) {
1243         return warns;
1244     }
1245 
1246     Data::Warning warning1;
1247 
1248     if (hasAppletsAndContainmentsWithSameId(layout, warning1)) {
1249         warns << warning1;
1250     }
1251 
1252     Data::Error error1;
1253     Data::Warning warning2;
1254 
1255     if (!hasOrphanedParentAppletOfSubContainment(layout, error1) /*this is needed because this error has higher priority*/
1256             && hasOrphanedSubContainments(layout, warning2)) {
1257         warns << warning2;
1258     }
1259 
1260     return warns;
1261 }
1262 
1263 //! AppletsData Information
1264 Data::Applet Storage::metadata(const QString &pluginId)
1265 {
1266     Data::Applet data;
1267     data.id = pluginId;
1268 
1269     KPackage::Package pkg = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/Applet"));
1270     pkg.setDefaultPackageRoot(QStringLiteral("plasma/plasmoids"));
1271     pkg.setPath(pluginId);
1272 
1273     if (pkg.isValid()) {
1274         data.name = pkg.metadata().name();
1275         data.description = pkg.metadata().description();
1276 
1277         QString iconName = pkg.metadata().iconName();
1278         if (!iconName.startsWith("/") && iconName.contains("/")) {
1279             data.icon = QFileInfo(pkg.metadata().fileName()).absolutePath() + "/" + iconName;
1280         } else {
1281             data.icon = iconName;
1282         }
1283     }
1284 
1285     if (data.name.isEmpty()) {
1286         //! this is also a way to identify if a package is installed or not in current system
1287         data.name = data.id;
1288     }
1289 
1290     return data;
1291 }
1292 
1293 Data::AppletsTable Storage::plugins(const Layout::GenericLayout *layout, const int containmentid)
1294 {
1295     Data::AppletsTable knownapplets;
1296     Data::AppletsTable unknownapplets;
1297 
1298     if (!layout) {
1299         return knownapplets;
1300     }
1301 
1302     //! empty means all containments are valid
1303     QList<int> validcontainmentids;
1304 
1305     if (isValid(containmentid)) {
1306         validcontainmentids << containmentid;
1307 
1308         //! searching for specific containment and subcontainments and ignore all other containments
1309         for(auto containment : *layout->containments()) {
1310             if (((int)containment->id()) != containmentid) {
1311                 //! ignore irrelevant containments
1312                 continue;
1313             }
1314 
1315             for (auto applet : containment->applets()) {
1316                 if (isSubContainment(layout->corona(), applet)) {
1317                     validcontainmentids << subContainmentId(applet->config());
1318                 }
1319             }
1320         }
1321     }
1322 
1323     //! cycle through valid contaiments in order to retrieve their metadata
1324     for(auto containment : *layout->containments()) {
1325         if (validcontainmentids.count()>0 && !validcontainmentids.contains(containment->id())) {
1326             //! searching only for valid containments
1327             continue;
1328         }
1329 
1330         for (auto applet : containment->applets()) {
1331             QString pluginId = applet->pluginMetaData().pluginId();
1332             if (!knownapplets.containsId(pluginId) && !unknownapplets.containsId(pluginId)) {
1333                 Data::Applet appletdata = metadata(pluginId);
1334 
1335                 if (appletdata.isInstalled()) {
1336                     knownapplets.insertBasedOnName(appletdata);
1337                 } else if (appletdata.isValid()) {
1338                     unknownapplets.insertBasedOnName(appletdata);
1339                 }
1340             }
1341         }
1342     }
1343 
1344     knownapplets << unknownapplets;
1345 
1346     return knownapplets;
1347 }
1348 
1349 Data::AppletsTable Storage::plugins(const QString &layoutfile, const int containmentid)
1350 {
1351     Data::AppletsTable knownapplets;
1352     Data::AppletsTable unknownapplets;
1353 
1354     if (layoutfile.isEmpty()) {
1355         return knownapplets;
1356     }
1357 
1358     KSharedConfigPtr lFile = KSharedConfig::openConfig(layoutfile);
1359     KConfigGroup containmentGroups = KConfigGroup(lFile, "Containments");
1360 
1361     //! empty means all containments are valid
1362     QList<int> validcontainmentids;
1363 
1364     if (isValid(containmentid)) {
1365         validcontainmentids << containmentid;
1366 
1367         //! searching for specific containment and subcontainments and ignore all other containments
1368         for (const auto &cId : containmentGroups.groupList()) {
1369             if (cId.toInt() != containmentid) {
1370                 //! ignore irrelevant containments
1371                 continue;
1372             }
1373 
1374             auto appletGroups = containmentGroups.group(cId).group("Applets");
1375 
1376             for (const auto &appletId : appletGroups.groupList()) {
1377                 KConfigGroup appletCfg = appletGroups.group(appletId);
1378                 if (isSubContainment(appletCfg)) {
1379                     validcontainmentids << subContainmentId(appletCfg);
1380                 }
1381             }
1382         }
1383     }
1384 
1385     //! cycle through valid contaiments in order to retrieve their metadata
1386     for (const auto &cId : containmentGroups.groupList()) {
1387         if (validcontainmentids.count()>0 && !validcontainmentids.contains(cId.toInt())) {
1388             //! searching only for valid containments
1389             continue;
1390         }
1391 
1392         auto appletGroups = containmentGroups.group(cId).group("Applets");
1393 
1394         for (const auto &appletId : appletGroups.groupList()) {
1395             KConfigGroup appletCfg = appletGroups.group(appletId);
1396             QString pluginId = appletCfg.readEntry("plugin", "");
1397 
1398             if (!knownapplets.containsId(pluginId) && !unknownapplets.containsId(pluginId)) {
1399                 Data::Applet appletdata = metadata(pluginId);
1400 
1401                 if (appletdata.isInstalled()) {
1402                     knownapplets.insertBasedOnName(appletdata);
1403                 } else if (appletdata.isValid()) {
1404                     unknownapplets.insertBasedOnName(appletdata);
1405                 }
1406             }
1407         }
1408     }
1409 
1410     knownapplets << unknownapplets;
1411 
1412     return knownapplets;
1413 }
1414 
1415 //! Views Data
1416 
1417 void Storage::syncContainmentConfig(Plasma::Containment *containment)
1418 {
1419     if (!containment) {
1420         return;
1421     }
1422 
1423     for(auto applet: containment->applets()) {
1424         KConfigGroup appletGeneralConfig = applet->config().group("General");
1425 
1426         if (appletGeneralConfig.exists()) {
1427             appletGeneralConfig.sync();
1428         }
1429 
1430         applet->config().sync();
1431     }
1432 
1433     containment->config().sync();
1434 }
1435 
1436 bool Storage::containsView(const QString &filepath, const int &viewId)
1437 {
1438     KSharedConfigPtr lFile = KSharedConfig::openConfig(filepath);
1439     KConfigGroup containmentGroups = KConfigGroup(lFile, "Containments");
1440     KConfigGroup viewGroup = containmentGroups.group(QString::number(viewId));
1441     return viewGroup.exists() && isLatteContainment(viewGroup);
1442 }
1443 
1444 bool Storage::hasContainment(const Layout::GenericLayout *layout, const int &id)
1445 {
1446     if (!layout  || layout->file().isEmpty() || !QFile(layout->file()).exists()) {
1447         return false;
1448     }
1449 
1450     if (layout->isActive()) { // active layout
1451         for(const auto containment : *layout->containments()) {
1452             if ((int)containment->id() == id) {
1453                 return true;
1454             }
1455         }
1456     } else { // inactive layout
1457         KSharedConfigPtr lfile = KSharedConfig::openConfig(layout->file());
1458         KConfigGroup containmentsEntries = KConfigGroup(lfile, "Containments");
1459 
1460         //! create warning data
1461         for (const auto &cid : containmentsEntries.groupList()) {
1462             if (cid.toInt() == id) {
1463                 return true;
1464             }
1465         }
1466     }
1467 
1468     return false;
1469 }
1470 
1471 bool Storage::isClonedView(const Plasma::Containment *containment) const
1472 {
1473     if (!containment) {
1474         return false;
1475     }
1476 
1477     return isClonedView(containment->config());
1478 }
1479 
1480 bool Storage::isClonedView(const KConfigGroup &containmentGroup) const
1481 {
1482     if (!isLatteContainment(containmentGroup)) {
1483         return false;
1484     }
1485 
1486     int isClonedFrom = containmentGroup.readEntry("isClonedFrom", Data::View::ISCLONEDNULL);
1487     return (isClonedFrom != IDNULL);
1488 }
1489 
1490 void Storage::removeAllClonedViews(const QString &filepath)
1491 {
1492     KSharedConfigPtr lFile = KSharedConfig::openConfig(filepath);
1493     KConfigGroup containmentGroups = KConfigGroup(lFile, "Containments");
1494 
1495     QList<Data::View> clones;
1496 
1497     for (const auto &contId : containmentGroups.groupList()) {
1498         if (isClonedView(containmentGroups.group(contId))) {
1499             clones << view(containmentGroups.group(contId));
1500         }
1501     }
1502 
1503     if (clones.size() <= 0) {
1504         return;
1505     }
1506 
1507     if (clones.count()>0) {
1508         qDebug() << "org.kde.layout :: Removing clones from file: " << filepath;
1509     }
1510 
1511     for (const auto &clonedata : clones) {
1512         qDebug() << "org.kde.layout :: Removing clone:" << clonedata.id << " and its subcontainments:" << clonedata.subcontainments;
1513         removeView(filepath, clonedata);
1514     }
1515 }
1516 
1517 Data::GenericTable<Data::Generic> Storage::subcontainments(const Layout::GenericLayout *layout, const Plasma::Containment *lattecontainment) const
1518 {
1519     Data::GenericTable<Data::Generic> subs;
1520 
1521     if (!layout || !Layouts::Storage::self()->isLatteContainment(lattecontainment)) {
1522         return subs;
1523     }
1524 
1525     for (const auto containment : (*layout->containments())) {
1526         if (containment == lattecontainment) {
1527             continue;
1528         }
1529 
1530         Plasma::Applet *parentApplet = qobject_cast<Plasma::Applet *>(containment->parent());
1531 
1532         //! add subcontainments for that lattecontainment
1533         if (parentApplet && parentApplet->containment() && parentApplet->containment() == lattecontainment) {
1534             Data::Generic subdata;
1535             subdata.id = QString::number(containment->id());
1536             subs << subdata;
1537         }
1538     }
1539 
1540     return subs;
1541 }
1542 
1543 Data::GenericTable<Data::Generic> Storage::subcontainments(const KConfigGroup &containmentGroup)
1544 {
1545     Data::GenericTable<Data::Generic> subs;
1546 
1547     if (!Layouts::Storage::self()->isLatteContainment(containmentGroup)) {
1548         return subs;
1549     }
1550 
1551     auto applets = containmentGroup.group("Applets");
1552 
1553     for (const auto &applet : applets.groupList()) {
1554         if (isSubContainment(applets.group(applet))) {
1555             Data::Generic subdata;
1556             subdata.id = QString::number(subContainmentId(applets.group(applet)));
1557             subs << subdata;
1558         }
1559     }
1560 
1561     return subs;
1562 }
1563 
1564 Data::View Storage::view(const Layout::GenericLayout *layout, const Plasma::Containment *lattecontainment)
1565 {
1566     Data::View vdata;
1567 
1568     if (!layout || !Layouts::Storage::self()->isLatteContainment(lattecontainment)) {
1569         return vdata;
1570     }
1571 
1572     vdata = view(lattecontainment->config());
1573 
1574     vdata.screen = lattecontainment->screen();
1575     if (!isValid(vdata.screen)) {
1576         vdata.screen = lattecontainment->lastScreen();
1577     }
1578 
1579     vdata.subcontainments = subcontainments(layout, lattecontainment);
1580 
1581     return vdata;
1582 }
1583 
1584 Data::View Storage::view(const KConfigGroup &containmentGroup)
1585 {
1586     Data::View vdata;
1587 
1588     if (!Layouts::Storage::self()->isLatteContainment(containmentGroup)) {
1589         return vdata;
1590     }
1591 
1592     vdata.id = containmentGroup.name();
1593     vdata.name = containmentGroup.readEntry("name", QString());
1594     vdata.isActive = false;
1595     vdata.screensGroup = static_cast<Latte::Types::ScreensGroup>(containmentGroup.readEntry("screensGroup", (int)Latte::Types::SingleScreenGroup));
1596     vdata.onPrimary = containmentGroup.readEntry("onPrimary", true);
1597     vdata.screen = containmentGroup.readEntry("lastScreen", IDNULL);
1598     vdata.isClonedFrom = containmentGroup.readEntry("isClonedFrom", Data::View::ISCLONEDNULL);
1599     vdata.screenEdgeMargin = containmentGroup.group("General").readEntry("screenEdgeMargin", (int)-1);
1600 
1601     int location = containmentGroup.readEntry("location", (int)Plasma::Types::BottomEdge);
1602     vdata.edge = (Plasma::Types::Location)location;
1603 
1604     vdata.maxLength = containmentGroup.group("General").readEntry("maxLength", (float)100.0);
1605 
1606     int alignment = containmentGroup.group("General").readEntry("alignment", (int)Latte::Types::Center) ;
1607     vdata.alignment = (Latte::Types::Alignment)alignment;
1608 
1609     vdata.subcontainments = subcontainments(containmentGroup);
1610     vdata.setState(Data::View::IsCreated);
1611 
1612     return vdata;
1613 }
1614 
1615 void Storage::updateView(KConfigGroup viewGroup, const Data::View &viewData)
1616 {
1617     if (!Layouts::Storage::self()->isLatteContainment(viewGroup)) {
1618         return;
1619     }
1620 
1621     viewGroup.writeEntry("name", viewData.name);
1622     viewGroup.writeEntry("screensGroup", (int)viewData.screensGroup);
1623     viewGroup.writeEntry("onPrimary", viewData.onPrimary);
1624     viewGroup.writeEntry("isClonedFrom", viewData.isClonedFrom);
1625     viewGroup.writeEntry("lastScreen", viewData.screen);
1626     viewGroup.group("General").writeEntry("screenEdgeMargin", viewData.screenEdgeMargin);
1627     viewGroup.writeEntry("location", (int)viewData.edge);
1628     viewGroup.writeEntry("maxLength", viewData.maxLength);
1629     viewGroup.group("General").writeEntry("alignment", (int)viewData.alignment);
1630     viewGroup.sync();
1631 }
1632 
1633 void Storage::updateView(const Layout::GenericLayout *layout, const Data::View &viewData)
1634 {
1635     if (!layout) {
1636         return;
1637     }
1638 
1639     auto view = layout->viewForContainment(viewData.id.toUInt());
1640 
1641     if (view) {
1642         qDebug() << "Storage::updateView should not be called because view is active and present...";
1643         return;
1644     }
1645 
1646     if (layout->isActive()) {
1647         //! active view but is not present in active screens;
1648         auto containment = layout->containmentForId(viewData.id.toUInt());
1649         if (containment) {
1650             //! update containment
1651             containment->setLocation(viewData.edge);
1652             updateView(containment->config(), viewData);
1653         }
1654     } else {
1655         //! inactive view and in layout storage
1656         KSharedConfigPtr lFile = KSharedConfig::openConfig(layout->file());
1657         KConfigGroup containmentGroups = KConfigGroup(lFile, "Containments");
1658         KConfigGroup viewContainment = containmentGroups.group(viewData.id);
1659 
1660         if (viewContainment.exists() && Layouts::Storage::self()->isLatteContainment(viewContainment)) {
1661             updateView(viewContainment, viewData);
1662         }
1663     }
1664 }
1665 
1666 void Storage::removeView(const QString &filepath, const Data::View &viewData)
1667 {
1668     if (!viewData.isValid()) {
1669         return;
1670     }
1671 
1672     removeContainment(filepath, viewData.id);
1673 
1674     for (int i=0; i<viewData.subcontainments.rowCount(); ++i) {
1675         removeContainment(filepath, viewData.subcontainments[i].id);
1676     }
1677 }
1678 
1679 void Storage::removeContainment(const QString &filepath, const QString &containmentId)
1680 {
1681     if (containmentId.isEmpty()) {
1682         return;
1683     }
1684 
1685     KSharedConfigPtr lFile = KSharedConfig::openConfig(filepath);
1686     KConfigGroup containmentGroups = KConfigGroup(lFile, "Containments");
1687 
1688     if (!containmentGroups.group(containmentId).exists()) {
1689         return;
1690     }
1691 
1692     containmentGroups.group(containmentId).deleteGroup();
1693     lFile->reparseConfiguration();
1694 }
1695 
1696 QStringList Storage::storedLayoutsInMultipleFile()
1697 {
1698     QStringList layouts;
1699     QString linkedFilePath = Importer::layoutUserFilePath(Layout::MULTIPLELAYOUTSHIDDENNAME);
1700 
1701     if (linkedFilePath.isEmpty() || !QFileInfo(linkedFilePath).exists()) {
1702         return layouts;
1703     }
1704 
1705     KSharedConfigPtr filePtr = KSharedConfig::openConfig(linkedFilePath);
1706     KConfigGroup linkedContainments = KConfigGroup(filePtr, "Containments");
1707 
1708     for(const auto &cId : linkedContainments.groupList()) {
1709         QString layoutName = linkedContainments.group(cId).readEntry("layoutId", QString());
1710 
1711         if (!layoutName.isEmpty() && !layouts.contains(layoutName)) {
1712             layouts << layoutName;
1713         }
1714     }
1715 
1716     return layouts;
1717 }
1718 
1719 
1720 QString Storage::storedView(const Layout::GenericLayout *layout, const int &containmentId)
1721 {
1722     //! make sure that layout and containmentId are valid
1723     if (!layout) {
1724         return QString();
1725     }
1726 
1727     if (layout->isActive()) {
1728         auto containment = layout->containmentForId((uint)containmentId);
1729         if (!containment || !isLatteContainment(containment)) {
1730             return QString();
1731         }
1732     } else {
1733         if (!containsView(layout->file(), containmentId)) {
1734             return QString();
1735         }
1736     }
1737 
1738     //! at this point we are sure that both layout and containmentId are acceptable
1739     QString nextTmpStoredViewAbsolutePath = m_storageTmpDir.path() + "/" + QFileInfo(layout->name()).fileName() + "." + QString::number(containmentId) + ".stored.tmp";
1740 
1741     QFile tempStoredViewFile(nextTmpStoredViewAbsolutePath);
1742 
1743     if (tempStoredViewFile.exists()) {
1744         tempStoredViewFile.remove();
1745     }
1746 
1747     KSharedConfigPtr destinationPtr = KSharedConfig::openConfig(nextTmpStoredViewAbsolutePath);
1748     KConfigGroup destinationContainments = KConfigGroup(destinationPtr, "Containments");
1749 
1750     if (layout->isActive()) {
1751         //! update and copy containments
1752         auto containment = layout->containmentForId((uint)containmentId);
1753         syncContainmentConfig(containment);
1754 
1755         KConfigGroup destinationViewContainment(&destinationContainments, QString::number(containment->id()));
1756         containment->config().copyTo(&destinationViewContainment);
1757 
1758         QList<Plasma::Containment *> subconts = layout->subContainmentsOf(containment->id());
1759 
1760         for(const auto subcont : subconts) {
1761             syncContainmentConfig(subcont);
1762             KConfigGroup destinationsubcontainment(&destinationContainments, QString::number(subcont->id()));
1763             subcont->config().copyTo(&destinationsubcontainment);
1764         }
1765 
1766         //! update with latest view data if active view is present
1767         auto view = layout->viewForContainment(containment);
1768 
1769         if (view) {
1770             Data::View currentviewdata = view->data();
1771             updateView(destinationViewContainment, currentviewdata);
1772         }
1773     } else {
1774         QString containmentid = QString::number(containmentId);
1775         KConfigGroup destinationViewContainment(&destinationContainments, containmentid);
1776 
1777         KSharedConfigPtr originPtr = KSharedConfig::openConfig(layout->file());
1778         KConfigGroup originContainments = KConfigGroup(originPtr, "Containments");
1779 
1780         originContainments.group(containmentid).copyTo(&destinationViewContainment);
1781 
1782         Data::GenericTable<Data::Generic> subconts = subcontainments(originContainments.group(containmentid));
1783 
1784         for(int i=0; i<subconts.rowCount(); ++i) {
1785             QString subid = subconts[i].id;
1786             KConfigGroup destinationsubcontainment(&destinationContainments, subid);
1787             originContainments.group(subid).copyTo(&destinationsubcontainment);
1788         }
1789     }
1790 
1791     destinationPtr->reparseConfiguration();
1792     return nextTmpStoredViewAbsolutePath;
1793 }
1794 
1795 int Storage::expectedViewScreenId(const Latte::Corona *corona, const KConfigGroup &containmentGroup) const
1796 {
1797     return expectedViewScreenId(corona, self()->view(containmentGroup));
1798 }
1799 
1800 int Storage::expectedViewScreenId(const Layout::GenericLayout *layout, const Plasma::Containment *lattecontainment) const
1801 {
1802     if (!layout || !layout->corona()) {
1803         return Latte::ScreenPool::NOSCREENID;
1804     }
1805 
1806     return expectedViewScreenId(layout->corona(), self()->view(layout, lattecontainment));
1807 }
1808 
1809 int Storage::expectedViewScreenId(const Latte::Corona *corona, const Data::View &view) const
1810 {
1811     if (!corona || !view.isValid()) {
1812         return Latte::ScreenPool::NOSCREENID;
1813     }
1814 
1815     if (view.screensGroup == Latte::Types::SingleScreenGroup || view.isCloned()) {
1816         return view.onPrimary ? corona->screenPool()->primaryScreenId() : view.screen;
1817     } else if (view.screensGroup == Latte::Types::AllScreensGroup) {
1818         return corona->screenPool()->primaryScreenId();
1819     } else if (view.screensGroup == Latte::Types::AllSecondaryScreensGroup) {
1820         QList<int> secondaryscreens = corona->screenPool()->secondaryScreenIds();
1821         return secondaryscreens.contains(view.screen) || secondaryscreens.isEmpty() ? view.screen : secondaryscreens[0];
1822     }
1823 
1824     return Latte::ScreenPool::NOSCREENID;
1825 }
1826 
1827 Data::ViewsTable Storage::views(const Layout::GenericLayout *layout)
1828 {
1829     Data::ViewsTable vtable;
1830 
1831     if (!layout) {
1832         return vtable;
1833     } else if (!layout->isActive()) {
1834         return views(layout->file());
1835     }
1836 
1837     for (const auto containment : (*layout->containments())) {
1838         if (!isLatteContainment(containment)) {
1839             continue;
1840         }
1841 
1842         Latte::View *vw = layout->viewForContainment(containment);
1843 
1844         if (vw) {
1845             vtable << vw->data();
1846         } else {
1847             vtable << view(layout, containment);
1848         }
1849     }
1850 
1851     return vtable;
1852 }
1853 
1854 Data::ViewsTable Storage::views(const QString &file)
1855 {
1856     Data::ViewsTable vtable;
1857 
1858     KSharedConfigPtr lFile = KSharedConfig::openConfig(file);
1859     KConfigGroup containmentGroups = KConfigGroup(lFile, "Containments");
1860 
1861     for (const auto &cId : containmentGroups.groupList()) {
1862         if (Layouts::Storage::self()->isLatteContainment(containmentGroups.group(cId))) {
1863             vtable << view(containmentGroups.group(cId));
1864         }
1865     }
1866 
1867     return vtable;
1868 }
1869 
1870 }
1871 }