File indexing completed on 2024-04-21 16:17:08

0001 /*
0002 *  Copyright 2019  Michail Vourlakos <mvourlakos@gmail.com>
0003 *
0004 *  This file is part of Latte-Dock
0005 *
0006 *  Latte-Dock is free software; you can redistribute it and/or
0007 *  modify it under the terms of the GNU General Public License as
0008 *  published by the Free Software Foundation; either version 2 of
0009 *  the License, or (at your option) any later version.
0010 *
0011 *  Latte-Dock is distributed in the hope that it will be useful,
0012 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
0013 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014 *  GNU General Public License for more details.
0015 *
0016 *  You should have received a copy of the GNU General Public License
0017 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
0018 */
0019 
0020 #include "storage.h"
0021 
0022 // local
0023 #include "../lattecorona.h"
0024 #include "../screenpool.h"
0025 #include "../layouts/manager.h"
0026 #include "../layouts/importer.h"
0027 #include "../view/view.h"
0028 // Qt
0029 #include <QDir>
0030 #include <QFile>
0031 #include <QFileInfo>
0032 
0033 // KDE
0034 #include <KConfigGroup>
0035 #include <KSharedConfig>
0036 
0037 // Plasma
0038 #include <Plasma>
0039 #include <Plasma/Applet>
0040 #include <Plasma/Containment>
0041 
0042 namespace Latte {
0043 namespace Layout {
0044 
0045 Storage::Storage(GenericLayout *parent)
0046     : QObject(parent),
0047       m_layout(parent)
0048 {
0049 }
0050 
0051 Storage::~Storage()
0052 {
0053 }
0054 
0055 bool Storage::isWritable() const
0056 {
0057     QFileInfo layoutFileInfo(m_layout->file());
0058 
0059     if (layoutFileInfo.exists() && !layoutFileInfo.isWritable()) {
0060         return false;
0061     } else {
0062         return true;
0063     }
0064 }
0065 
0066 bool Storage::isLatteContainment(Plasma::Containment *containment) const
0067 {
0068     if (!containment) {
0069         return false;
0070     }
0071 
0072     if (containment->pluginMetaData().pluginId() == "org.kde.latte.containment") {
0073         return true;
0074     }
0075 
0076     return false;
0077 }
0078 
0079 bool Storage::isLatteContainment(const KConfigGroup &group) const
0080 {
0081     QString pluginId = group.readEntry("plugin", "");
0082     return pluginId == "org.kde.latte.containment";
0083 }
0084 
0085 void Storage::lock()
0086 {
0087     QFileInfo layoutFileInfo(m_layout->file());
0088 
0089     if (layoutFileInfo.exists() && layoutFileInfo.isWritable()) {
0090         QFile(m_layout->file()).setPermissions(QFileDevice::ReadUser | QFileDevice::ReadGroup | QFileDevice::ReadOther);
0091     }
0092 }
0093 
0094 void Storage::unlock()
0095 {
0096     QFileInfo layoutFileInfo(m_layout->file());
0097 
0098     if (layoutFileInfo.exists() && !layoutFileInfo.isWritable()) {
0099         QFile(m_layout->file()).setPermissions(QFileDevice::ReadUser | QFileDevice::WriteUser | QFileDevice::ReadGroup | QFileDevice::ReadOther);
0100     }
0101 }
0102 
0103 void Storage::importToCorona()
0104 {
0105     if (!m_layout->corona()) {
0106         return;
0107     }
0108 
0109     //! Setting mutable for create a containment
0110     m_layout->corona()->setImmutability(Plasma::Types::Mutable);
0111 
0112     QString temp1FilePath = QDir::homePath() + "/.config/lattedock.copy1.bak";
0113     //! we need to copy first the layout file because the kde cache
0114     //! may not have yet been updated (KSharedConfigPtr)
0115     //! this way we make sure at the latest changes stored in the layout file
0116     //! will be also available when changing to Multiple Layouts
0117     QString tempLayoutFilePath = QDir::homePath() + "/.config/lattedock.layout.bak";
0118 
0119     //! WE NEED A WAY TO COPY A CONTAINMENT!!!!
0120     QFile tempLayoutFile(tempLayoutFilePath);
0121     QFile copyFile(temp1FilePath);
0122     QFile layoutOriginalFile(m_layout->file());
0123 
0124     if (tempLayoutFile.exists()) {
0125         tempLayoutFile.remove();
0126     }
0127 
0128     if (copyFile.exists())
0129         copyFile.remove();
0130 
0131     layoutOriginalFile.copy(tempLayoutFilePath);
0132 
0133     KSharedConfigPtr filePtr = KSharedConfig::openConfig(tempLayoutFilePath);
0134     KSharedConfigPtr newFile = KSharedConfig::openConfig(temp1FilePath);
0135     KConfigGroup copyGroup = KConfigGroup(newFile, "Containments");
0136     KConfigGroup current_containments = KConfigGroup(filePtr, "Containments");
0137 
0138     current_containments.copyTo(&copyGroup);
0139 
0140     copyGroup.sync();
0141 
0142     //! update ids to unique ones
0143     QString temp2File = newUniqueIdsLayoutFromFile(temp1FilePath);
0144 
0145 
0146     //! Finally import the configuration
0147     importLayoutFile(temp2File);
0148 }
0149 
0150 void Storage::syncToLayoutFile(bool removeLayoutId)
0151 {
0152     if (!m_layout->corona() || !isWritable()) {
0153         return;
0154     }
0155 
0156     KSharedConfigPtr filePtr = KSharedConfig::openConfig(m_layout->file());
0157 
0158     KConfigGroup oldContainments = KConfigGroup(filePtr, "Containments");
0159     oldContainments.deleteGroup();
0160     oldContainments.sync();
0161 
0162     qDebug() << " LAYOUT :: " << m_layout->name() << " is syncing its original file.";
0163 
0164     for (const auto containment : *m_layout->containments()) {
0165         if (removeLayoutId) {
0166             containment->config().writeEntry("layoutId", "");
0167         }
0168 
0169         KConfigGroup newGroup = oldContainments.group(QString::number(containment->id()));
0170         containment->config().copyTo(&newGroup);
0171 
0172         if (!removeLayoutId) {
0173             newGroup.writeEntry("layoutId", "");
0174             newGroup.sync();
0175         }
0176     }
0177 
0178     oldContainments.sync();
0179 }
0180 
0181 void Storage::copyView(Plasma::Containment *containment)
0182 {
0183     if (!containment || !m_layout->corona())
0184         return;
0185 
0186     qDebug() << "copying containment layout";
0187     //! Setting mutable for create a containment
0188     m_layout->corona()->setImmutability(Plasma::Types::Mutable);
0189 
0190     QString temp1File = QDir::homePath() + "/.config/lattedock.copy1.bak";
0191 
0192     //! WE NEED A WAY TO COPY A CONTAINMENT!!!!
0193     QFile copyFile(temp1File);
0194 
0195     if (copyFile.exists())
0196         copyFile.remove();
0197 
0198     KSharedConfigPtr newFile = KSharedConfig::openConfig(temp1File);
0199     KConfigGroup copied_conts = KConfigGroup(newFile, "Containments");
0200     KConfigGroup copied_c1 = KConfigGroup(&copied_conts, QString::number(containment->id()));
0201 
0202     containment->config().copyTo(&copied_c1);
0203 
0204     //!investigate if there multiple systray(s) in the containment to copy also
0205 
0206     //! systrayId, systrayAppletId
0207     QHash<int, QString> systraysInfo;
0208     auto applets = containment->config().group("Applets");
0209 
0210     for (const auto &applet : applets.groupList()) {
0211         KConfigGroup appletSettings = applets.group(applet).group("Configuration");
0212 
0213         int tSysId = appletSettings.readEntry("SystrayContainmentId", -1);
0214 
0215         if (tSysId != -1) {
0216             systraysInfo[tSysId] = applet;
0217             qDebug() << "systray with id "<< tSysId << " was found in the containment... ::: " << tSysId;
0218         }
0219     }
0220 
0221     if (systraysInfo.count() > 0) {
0222         for(const auto systrayId : systraysInfo.keys()) {
0223             Plasma::Containment *systray{nullptr};
0224 
0225             for (const auto containment : m_layout->corona()->containments()) {
0226                 if (containment->id() == systrayId) {
0227                     systray = containment;
0228                     break;
0229                 }
0230             }
0231 
0232             if (systray) {
0233                 KConfigGroup copied_systray = KConfigGroup(&copied_conts, QString::number(systray->id()));
0234                 systray->config().copyTo(&copied_systray);
0235             }
0236         }
0237     }
0238     //! end of systray specific code
0239 
0240     //! update ids to unique ones
0241     QString temp2File = newUniqueIdsLayoutFromFile(temp1File);
0242 
0243 
0244     //! Don't create LatteView when the containment is created because we must update
0245     //! its screen settings first
0246     m_layout->setBlockAutomaticLatteViewCreation(true);
0247     //! Finally import the configuration
0248     QList<Plasma::Containment *> importedDocks = importLayoutFile(temp2File);
0249 
0250     Plasma::Containment *newContainment{nullptr};
0251 
0252     if (importedDocks.size() == 1) {
0253         newContainment = importedDocks[0];
0254     }
0255 
0256     if (!newContainment || !newContainment->kPackage().isValid()) {
0257         qWarning() << "the requested containment plugin can not be located or loaded";
0258         return;
0259     }
0260 
0261     auto config = newContainment->config();
0262 
0263     //in multi-screen environment the copied dock is moved to alternative screens first
0264     const auto screens = qGuiApp->screens();
0265     auto dock =  m_layout->viewForContainment(containment);
0266 
0267     bool setOnExplicitScreen = false;
0268 
0269     int dockScrId = -1;
0270     int copyScrId = -1;
0271 
0272     if (dock) {
0273         dockScrId = dock->positioner()->currentScreenId();
0274         qDebug() << "COPY DOCK SCREEN ::: " << dockScrId;
0275 
0276         if (dockScrId != -1 && screens.count() > 1) {
0277             for (const auto scr : screens) {
0278                 copyScrId = m_layout->corona()->screenPool()->id(scr->name());
0279 
0280                 //the screen must exist and not be the same with the original dock
0281                 if (copyScrId > -1 && copyScrId != dockScrId) {
0282                     QList<Plasma::Types::Location> fEdges = m_layout->freeEdges(copyScrId);
0283 
0284                     if (fEdges.contains((Plasma::Types::Location)containment->location())) {
0285                         ///set this containment to an explicit screen
0286                         config.writeEntry("onPrimary", false);
0287                         config.writeEntry("lastScreen", copyScrId);
0288                         newContainment->setLocation(containment->location());
0289 
0290                         qDebug() << "COPY DOCK SCREEN NEW SCREEN ::: " << copyScrId;
0291 
0292                         setOnExplicitScreen = true;
0293                         break;
0294                     }
0295                 }
0296             }
0297         }
0298     }
0299 
0300     if (!setOnExplicitScreen) {
0301         QList<Plasma::Types::Location> edges = m_layout->freeEdges(newContainment->screen());
0302 
0303         if (edges.count() > 0) {
0304             newContainment->setLocation(edges.at(0));
0305         } else {
0306             newContainment->setLocation(Plasma::Types::BottomEdge);
0307         }
0308 
0309         config.writeEntry("onPrimary", true);
0310         config.writeEntry("lastScreen", dockScrId);
0311     }
0312 
0313     newContainment->config().sync();
0314 
0315     if (setOnExplicitScreen && copyScrId > -1) {
0316         qDebug() << "Copy Dock in explicit screen ::: " << copyScrId;
0317         m_layout->addView(newContainment, false, copyScrId);
0318         newContainment->reactToScreenChange();
0319     } else {
0320         qDebug() << "Copy Dock in current screen...";
0321         m_layout->addView(newContainment, false, dockScrId);
0322     }
0323 
0324     m_layout->setBlockAutomaticLatteViewCreation(false);
0325 }
0326 
0327 QList<Plasma::Containment *> Storage::importLayoutFile(QString file)
0328 {
0329     KSharedConfigPtr filePtr = KSharedConfig::openConfig(file);
0330     auto newContainments = m_layout->corona()->importLayout(KConfigGroup(filePtr, ""));
0331 
0332     ///Find latte and systray containments
0333     qDebug() << " imported containments ::: " << newContainments.length();
0334 
0335     QList<Plasma::Containment *> importedDocks;
0336     //QList<Plasma::Containment *> systrays;
0337 
0338     for (const auto containment : newContainments) {
0339         if (isLatteContainment(containment)) {
0340             qDebug() << "new latte containment id: " << containment->id();
0341             importedDocks << containment;
0342         }
0343     }
0344 
0345     return importedDocks;
0346 }
0347 
0348 void Storage::systraysInformation(QHash<int, QList<int>> &systrays, QList<int> &assignedSystrays, QList<int> &orphanSystrays)
0349 {
0350     systrays.clear();
0351     assignedSystrays.clear();
0352     orphanSystrays.clear();
0353 
0354     KSharedConfigPtr lFile = KSharedConfig::openConfig(m_layout->file());
0355     KConfigGroup containmentGroups = KConfigGroup(lFile, "Containments");
0356 
0357     //! assigned systrays
0358     for (const auto &cId : containmentGroups.groupList()) {
0359         if (isLatteContainment(containmentGroups.group(cId))) {
0360             auto applets = containmentGroups.group(cId).group("Applets");
0361 
0362             for (const auto &applet : applets.groupList()) {
0363                 KConfigGroup appletSettings = applets.group(applet).group("Configuration");
0364                 int tSysId = appletSettings.readEntry("SystrayContainmentId", -1);
0365 
0366                 if (tSysId != -1) {
0367                     assignedSystrays << tSysId;
0368                     systrays[cId.toInt()].append(tSysId);
0369                 }
0370             }
0371         }
0372     }
0373 
0374     //! orphan systrays
0375     for (const auto &cId : containmentGroups.groupList()) {
0376         if (!isLatteContainment(containmentGroups.group(cId)) && !assignedSystrays.contains(cId.toInt())) {
0377             orphanSystrays << cId.toInt();
0378         }
0379     }
0380 }
0381 
0382 QList<ViewData> Storage::viewsData(const QHash<int, QList<int>> &systrays)
0383 {
0384     QList<ViewData> viewsData;
0385 
0386     KSharedConfigPtr lFile = KSharedConfig::openConfig(m_layout->file());
0387     KConfigGroup containmentGroups = KConfigGroup(lFile, "Containments");
0388 
0389     for (const auto &cId : containmentGroups.groupList()) {
0390         if (isLatteContainment(containmentGroups.group(cId))) {
0391             ViewData vData;
0392             int id = cId.toInt();
0393 
0394             //! id
0395             vData.id = id;
0396 
0397             //! active
0398             vData.active = false;
0399 
0400             //! onPrimary
0401             vData.onPrimary = containmentGroups.group(cId).readEntry("onPrimary", true);
0402 
0403             //! Screen
0404             vData.screenId = containmentGroups.group(cId).readEntry("lastScreen", -1);
0405 
0406             //! location
0407             vData.location = containmentGroups.group(cId).readEntry("location", (int)Plasma::Types::BottomEdge);
0408 
0409             //! systrays
0410             vData.systrays = systrays[id];
0411 
0412             viewsData << vData;
0413         }
0414     }
0415 
0416     return viewsData;
0417 }
0418 
0419 QList<int> Storage::viewsScreens()
0420 {
0421     QList<int> screens;
0422 
0423     KSharedConfigPtr lFile = KSharedConfig::openConfig(m_layout->file());
0424 
0425     KConfigGroup containmentGroups = KConfigGroup(lFile, "Containments");
0426 
0427     for (const auto &cId : containmentGroups.groupList()) {
0428         if (isLatteContainment(containmentGroups.group(cId))) {
0429             int screenId = containmentGroups.group(cId).readEntry("lastScreen", -1);
0430 
0431             if (screenId != -1 && !screens.contains(screenId)) {
0432                 screens << screenId;
0433             }
0434         }
0435     }
0436 
0437     return screens;
0438 }
0439 
0440 QString Storage::availableId(QStringList all, QStringList assigned, int base)
0441 {
0442     bool found = false;
0443 
0444     int i = base;
0445 
0446     while (!found && i < 32000) {
0447         QString iStr = QString::number(i);
0448 
0449         if (!all.contains(iStr) && !assigned.contains(iStr)) {
0450             return iStr;
0451         }
0452 
0453         i++;
0454     }
0455 
0456     return QString("");
0457 }
0458 
0459 QString Storage::newUniqueIdsLayoutFromFile(QString file)
0460 {
0461     if (!m_layout->corona()) {
0462         return QString();
0463     }
0464 
0465     QString tempFile = QDir::homePath() + "/.config/lattedock.copy2.bak";
0466 
0467     QFile copyFile(tempFile);
0468 
0469     if (copyFile.exists()) {
0470         copyFile.remove();
0471     }
0472 
0473     //! BEGIN updating the ids in the temp file
0474     QStringList allIds;
0475     allIds << m_layout->corona()->containmentsIds();
0476     allIds << m_layout->corona()->appletsIds();
0477 
0478     QStringList toInvestigateContainmentIds;
0479     QStringList toInvestigateAppletIds;
0480     QStringList toInvestigateSystrayContIds;
0481 
0482     //! first is the systray containment id
0483     QHash<QString, QString> systrayParentContainmentIds;
0484     QHash<QString, QString> systrayAppletIds;
0485 
0486     //qDebug() << "Ids:" << allIds;
0487 
0488     //qDebug() << "to copy containments: " << toCopyContainmentIds;
0489     //qDebug() << "to copy applets: " << toCopyAppletIds;
0490 
0491     QStringList assignedIds;
0492     QHash<QString, QString> assigned;
0493 
0494     KSharedConfigPtr filePtr = KSharedConfig::openConfig(file);
0495     KConfigGroup investigate_conts = KConfigGroup(filePtr, "Containments");
0496 
0497     //! Record the containment and applet ids
0498     for (const auto &cId : investigate_conts.groupList()) {
0499         toInvestigateContainmentIds << cId;
0500         auto appletsEntries = investigate_conts.group(cId).group("Applets");
0501         toInvestigateAppletIds << appletsEntries.groupList();
0502 
0503         //! investigate for systrays
0504         for (const auto &appletId : appletsEntries.groupList()) {
0505             KConfigGroup appletSettings = appletsEntries.group(appletId).group("Configuration");
0506 
0507             int tSysId = appletSettings.readEntry("SystrayContainmentId", -1);
0508 
0509             //! It is a systray !!!
0510             if (tSysId != -1) {
0511                 QString tSysIdStr = QString::number(tSysId);
0512                 toInvestigateSystrayContIds << tSysIdStr;
0513                 systrayParentContainmentIds[tSysIdStr] = cId;
0514                 systrayAppletIds[tSysIdStr] = appletId;
0515                 qDebug() << "systray was found in the containment...";
0516             }
0517         }
0518     }
0519 
0520     //! Reassign containment and applet ids to unique ones
0521     for (const auto &contId : toInvestigateContainmentIds) {
0522         QString newId = availableId(allIds, assignedIds, 12);
0523 
0524         assignedIds << newId;
0525         assigned[contId] = newId;
0526     }
0527 
0528     for (const auto &appId : toInvestigateAppletIds) {
0529         QString newId = availableId(allIds, assignedIds, 40);
0530 
0531         assignedIds << newId;
0532         assigned[appId] = newId;
0533     }
0534 
0535     qDebug() << "ALL CORONA IDS ::: " << allIds;
0536     qDebug() << "FULL ASSIGNMENTS ::: " << assigned;
0537 
0538     for (const auto &cId : toInvestigateContainmentIds) {
0539         QString value = assigned[cId];
0540 
0541         if (assigned.contains(value)) {
0542             QString value2 = assigned[value];
0543 
0544             if (cId != assigned[cId] && !value2.isEmpty() && cId == value2) {
0545                 qDebug() << "PROBLEM APPEARED !!!! FOR :::: " << cId << " .. fixed ..";
0546                 assigned[cId] = cId;
0547                 assigned[value] = value;
0548             }
0549         }
0550     }
0551 
0552     for (const auto &aId : toInvestigateAppletIds) {
0553         QString value = assigned[aId];
0554 
0555         if (assigned.contains(value)) {
0556             QString value2 = assigned[value];
0557 
0558             if (aId != assigned[aId] && !value2.isEmpty() && aId == value2) {
0559                 qDebug() << "PROBLEM APPEARED !!!! FOR :::: " << aId << " .. fixed ..";
0560                 assigned[aId] = aId;
0561                 assigned[value] = value;
0562             }
0563         }
0564     }
0565 
0566     qDebug() << "FIXED FULL ASSIGNMENTS ::: " << assigned;
0567 
0568     //! update applet ids in their containment order and in MultipleLayouts update also the layoutId
0569     for (const auto &cId : investigate_conts.groupList()) {
0570         //! Update options that contain applet ids
0571         //! (appletOrder) and (lockedZoomApplets) and (userBlocksColorizingApplets)
0572         QStringList options;
0573         options << "appletOrder" << "lockedZoomApplets" << "userBlocksColorizingApplets";
0574 
0575         for (const auto &settingStr : options) {
0576             QString order1 = investigate_conts.group(cId).group("General").readEntry(settingStr, QString());
0577 
0578             if (!order1.isEmpty()) {
0579                 QStringList order1Ids = order1.split(";");
0580                 QStringList fixedOrder1Ids;
0581 
0582                 for (int i = 0; i < order1Ids.count(); ++i) {
0583                     fixedOrder1Ids.append(assigned[order1Ids[i]]);
0584                 }
0585 
0586                 QString fixedOrder1 = fixedOrder1Ids.join(";");
0587                 investigate_conts.group(cId).group("General").writeEntry(settingStr, fixedOrder1);
0588             }
0589         }
0590 
0591         if (m_layout->corona()->layoutsManager()->memoryUsage() == Types::MultipleLayouts) {
0592             investigate_conts.group(cId).writeEntry("layoutId", m_layout->name());
0593         }
0594     }
0595 
0596     //! must update also the systray id in its applet
0597     for (const auto &systrayId : toInvestigateSystrayContIds) {
0598         KConfigGroup systrayParentContainment = investigate_conts.group(systrayParentContainmentIds[systrayId]);
0599         systrayParentContainment.group("Applets").group(systrayAppletIds[systrayId]).group("Configuration").writeEntry("SystrayContainmentId", assigned[systrayId]);
0600         systrayParentContainment.sync();
0601     }
0602 
0603     investigate_conts.sync();
0604 
0605     //! Copy To Temp 2 File And Update Correctly The Ids
0606     KSharedConfigPtr file2Ptr = KSharedConfig::openConfig(tempFile);
0607     KConfigGroup fixedNewContainmets = KConfigGroup(file2Ptr, "Containments");
0608 
0609     for (const auto &contId : investigate_conts.groupList()) {
0610         QString pluginId = investigate_conts.group(contId).readEntry("plugin", "");
0611 
0612         if (pluginId != "org.kde.desktopcontainment") { //!don't add ghost containments
0613             KConfigGroup newContainmentGroup = fixedNewContainmets.group(assigned[contId]);
0614             investigate_conts.group(contId).copyTo(&newContainmentGroup);
0615 
0616             newContainmentGroup.group("Applets").deleteGroup();
0617 
0618             for (const auto &appId : investigate_conts.group(contId).group("Applets").groupList()) {
0619                 KConfigGroup appletGroup = investigate_conts.group(contId).group("Applets").group(appId);
0620                 KConfigGroup newAppletGroup = fixedNewContainmets.group(assigned[contId]).group("Applets").group(assigned[appId]);
0621                 appletGroup.copyTo(&newAppletGroup);
0622             }
0623         }
0624     }
0625 
0626     fixedNewContainmets.sync();
0627 
0628     return tempFile;
0629 }
0630 
0631 bool Storage::appletGroupIsValid(KConfigGroup appletGroup)
0632 {
0633     return !( appletGroup.keyList().count() == 0
0634               && appletGroup.groupList().count() == 1
0635               && appletGroup.groupList().at(0) == "Configuration"
0636               && appletGroup.group("Configuration").keyList().count() == 1
0637               && appletGroup.group("Configuration").hasKey("PreloadWeight") );
0638 }
0639 
0640 bool Storage::layoutIsBroken(QStringList &errors) const
0641 {
0642     if (m_layout->file().isEmpty() || !QFile(m_layout->file()).exists()) {
0643         return false;
0644     }
0645 
0646     QStringList ids;
0647     QStringList conts;
0648     QStringList applets;
0649 
0650     KSharedConfigPtr lFile = KSharedConfig::openConfig(m_layout->file());
0651 
0652     if (!m_layout->corona()) {
0653         KConfigGroup containmentsEntries = KConfigGroup(lFile, "Containments");
0654         ids << containmentsEntries.groupList();
0655         conts << ids;
0656 
0657         for (const auto &cId : containmentsEntries.groupList()) {
0658             auto appletsEntries = containmentsEntries.group(cId).group("Applets");
0659 
0660             QStringList validAppletIds;
0661             bool updated{false};
0662 
0663             for (const auto &appletId : appletsEntries.groupList()) {
0664                 KConfigGroup appletGroup = appletsEntries.group(appletId);
0665 
0666                 if (appletGroupIsValid(appletGroup)) {
0667                     validAppletIds << appletId;
0668                 } else {
0669                     updated = true;
0670                     //! heal layout file by removing applet config records that are not used any more
0671                     qDebug() << "Layout: " << m_layout->name() << " removing deprecated applet : " << appletId;
0672                     appletsEntries.deleteGroup(appletId);
0673                 }
0674             }
0675 
0676             if (updated) {
0677                 appletsEntries.sync();
0678             }
0679 
0680             ids << validAppletIds;
0681             applets << validAppletIds;
0682         }
0683     } else {
0684         for (const auto containment : *m_layout->containments()) {
0685             ids << QString::number(containment->id());
0686             conts << QString::number(containment->id());
0687 
0688             for (const auto applet : containment->applets()) {
0689                 ids << QString::number(applet->id());
0690                 applets << QString::number(applet->id());
0691             }
0692         }
0693     }
0694 
0695     QSet<QString> idsSet = QSet<QString>::fromList(ids);
0696 
0697     /* a different way to count duplicates
0698     QMap<QString, int> countOfStrings;
0699 
0700     for (int i = 0; i < ids.count(); i++) {
0701         countOfStrings[ids[i]]++;
0702     }*/
0703 
0704     if (idsSet.count() != ids.count()) {
0705         qDebug() << "   ----   ERROR - BROKEN LAYOUT :: " << m_layout->name() << " ----";
0706 
0707         if (!m_layout->corona()) {
0708             qDebug() << "   --- storaged file : " << m_layout->file();
0709         } else {
0710             if (m_layout->corona()->layoutsManager()->memoryUsage() == Types::MultipleLayouts) {
0711                 qDebug() << "   --- in multiple layouts hidden file : " << Layouts::Importer::layoutFilePath(AbstractLayout::MultipleLayoutsName);
0712             } else {
0713                 qDebug() << "   --- in active layout file : " << m_layout->file();
0714             }
0715         }
0716 
0717         qDebug() << "Containments :: " << conts;
0718         qDebug() << "Applets :: " << applets;
0719 
0720         for (const QString &c : conts) {
0721             if (applets.contains(c)) {
0722                 QString errorStr = i18n("Same applet and containment id found ::: ") + c;
0723                 qDebug() << "Error: " << errorStr;
0724                 errors << errorStr;
0725             }
0726         }
0727 
0728         for (int i = 0; i < ids.count(); ++i) {
0729             for (int j = i + 1; j < ids.count(); ++j) {
0730                 if (ids[i] == ids[j]) {
0731                     QString errorStr = i18n("Different applets with same id ::: ") + ids[i];
0732                     qDebug() << "Error: " << errorStr;
0733                     errors << errorStr;
0734                 }
0735             }
0736         }
0737 
0738         qDebug() << "  -- - -- - -- - -- - - -- - - - - -- - - - - ";
0739 
0740         if (!m_layout->corona()) {
0741             KConfigGroup containmentsEntries = KConfigGroup(lFile, "Containments");
0742 
0743             for (const auto &cId : containmentsEntries.groupList()) {
0744                 auto appletsEntries = containmentsEntries.group(cId).group("Applets");
0745 
0746                 qDebug() << " CONTAINMENT : " << cId << " APPLETS : " << appletsEntries.groupList();
0747             }
0748         } else {
0749             for (const auto containment : *m_layout->containments()) {
0750                 QStringList appletsIds;
0751 
0752                 for (const auto applet : containment->applets()) {
0753                     appletsIds << QString::number(applet->id());
0754                 }
0755 
0756                 qDebug() << " CONTAINMENT : " << containment->id() << " APPLETS : " << appletsIds.join(",");
0757             }
0758         }
0759 
0760         return true;
0761     }
0762 
0763     return false;
0764 }
0765 
0766 
0767 }
0768 }