File indexing completed on 2024-12-08 04:58:11
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(©Group); 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 }