1 /*
2 SPDX-FileCopyrightText: 2020 Michail Vourlakos <mvourlakos@gmail.com>
3 SPDX-License-Identifier: GPL-2.0-or-later
4 */
5
6 #include "storage.h"
7
8 // local
9 #include <coretypes.h>
10 #include "importer.h"
11 #include "manager.h"
12 #include "../lattecorona.h"
13 #include "../screenpool.h"
14 #include "../data/errordata.h"
15 #include "../layout/abstractlayout.h"
16 #include "../view/view.h"
17
18 // Qt
19 #include <QDebug>
20 #include <QDir>
21 #include <QFile>
22 #include <QFileInfo>
23 #include <QLatin1String>
24
25 // KDE
26 #include <KConfigGroup>
27 #include <KPluginMetaData>
28 #include <KSharedConfig>
29 #include <KPackage/Package>
30 #include <KPackage/PackageLoader>
31
32 // Plasma
33 #include <Plasma>
34 #include <Plasma/Applet>
35 #include <Plasma/Containment>
36
37 namespace Latte {
38 namespace Layouts {
39
40 const int Storage::IDNULL = -1;
41 const int Storage::IDBASE = 0;
42
Storage()43 Storage::Storage()
44 {
45 qDebug() << " >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> LAYOUTS::STORAGE, TEMP DIR ::: " << m_storageTmpDir.path();
46
47 //! Known Errors / Warnings
48 s_knownErrors << Data::Generic(Data::Error::APPLETSWITHSAMEID, i18n("Different Applets With Same Id"));
49 s_knownErrors << Data::Generic(Data::Error::ORPHANEDPARENTAPPLETOFSUBCONTAINMENT, i18n("Orphaned Parent Applet Of Subcontainment"));
50 s_knownErrors<< Data::Generic(Data::Warning::APPLETANDCONTAINMENTWITHSAMEID, i18n("Different Applet And Containment With Same Id"));
51 s_knownErrors << Data::Generic(Data::Warning::ORPHANEDSUBCONTAINMENT, i18n("Orphaned Subcontainment"));
52
53
54 //! Known SubContainment Families
55 SubContaimentIdentityData data;
56 //! Systray Family
57 m_subIdentities << SubContaimentIdentityData{.cfgGroup="Configuration", .cfgProperty="SystrayContainmentId"};
58 //! Group applet Family
59 m_subIdentities << SubContaimentIdentityData{.cfgGroup="Configuration", .cfgProperty="ContainmentId"};
60 }
61
~Storage()62 Storage::~Storage()
63 {
64 }
65
self()66 Storage *Storage::self()
67 {
68 static Storage store;
69 return &store;
70 }
71
isWritable(const Layout::GenericLayout * layout) const72 bool Storage::isWritable(const Layout::GenericLayout *layout) const
73 {
74 QFileInfo layoutFileInfo(layout->file());
75
76 if (layoutFileInfo.exists() && !layoutFileInfo.isWritable()) {
77 return false;
78 } else {
79 return true;
80 }
81 }
82
isLatteContainment(const Plasma::Containment * containment) const83 bool Storage::isLatteContainment(const Plasma::Containment *containment) const
84 {
85 if (!containment) {
86 return false;
87 }
88
89 if (containment->pluginMetaData().pluginId() == QLatin1String("org.kde.latte.containment")) {
90 return true;
91 }
92
93 return false;
94 }
95
isLatteContainment(const KConfigGroup & group) const96 bool Storage::isLatteContainment(const KConfigGroup &group) const
97 {
98 QString pluginId = group.readEntry("plugin", "");
99 return pluginId == QLatin1String("org.kde.latte.containment");
100 }
101
isSubContainment(const Layout::GenericLayout * layout,const Plasma::Applet * applet) const102 bool Storage::isSubContainment(const Layout::GenericLayout *layout, const Plasma::Applet *applet) const
103 {
104 if (!layout || !applet) {
105 return false;
106 }
107
108 for (const auto containment : *layout->containments()) {
109 Plasma::Applet *parentApplet = qobject_cast<Plasma::Applet *>(containment->parent());
110 if (parentApplet && parentApplet == applet) {
111 return true;
112 }
113 }
114
115 return false;
116 }
117
isSubContainment(const KConfigGroup & appletGroup) const118 bool Storage::isSubContainment(const KConfigGroup &appletGroup) const
119 {
120 return isValid(subContainmentId(appletGroup));
121 }
122
isValid(const int & id)123 bool Storage::isValid(const int &id)
124 {
125 return id >= IDBASE;
126 }
127
subContainmentId(const KConfigGroup & appletGroup) const128 int Storage::subContainmentId(const KConfigGroup &appletGroup) const
129 {
130 //! cycle through subcontainments identities
131 for (auto subidentity : m_subIdentities) {
132 KConfigGroup appletConfigGroup = appletGroup;
133
134 if (!subidentity.cfgGroup.isEmpty()) {
135 //! if identity provides specific configuration group
136 if (appletConfigGroup.hasGroup(subidentity.cfgGroup)) {
137 appletConfigGroup = appletGroup.group(subidentity.cfgGroup);
138 }
139 }
140
141 if (!subidentity.cfgProperty.isEmpty()) {
142 //! if identity provides specific property for configuration group
143 if (appletConfigGroup.hasKey(subidentity.cfgProperty)) {
144 return appletConfigGroup.readEntry(subidentity.cfgProperty, IDNULL);
145 }
146 }
147 }
148
149 return IDNULL;
150 }
151
subIdentityIndex(const KConfigGroup & appletGroup) const152 int Storage::subIdentityIndex(const KConfigGroup &appletGroup) const
153 {
154 if (!isSubContainment(appletGroup)) {
155 return IDNULL;
156 }
157
158 //! cycle through subcontainments identities
159 for (int i=0; i<m_subIdentities.count(); ++i) {
160 KConfigGroup appletConfigGroup = appletGroup;
161
162 if (!m_subIdentities[i].cfgGroup.isEmpty()) {
163 //! if identity provides specific configuration group
164 if (appletConfigGroup.hasGroup(m_subIdentities[i].cfgGroup)) {
165 appletConfigGroup = appletGroup.group(m_subIdentities[i].cfgGroup);
166 }
167 }
168
169 if (!m_subIdentities[i].cfgProperty.isEmpty()) {
170 //! if identity provides specific property for configuration group
171 if (appletConfigGroup.hasKey(m_subIdentities[i].cfgProperty)) {
172 int subId = appletConfigGroup.readEntry(m_subIdentities[i].cfgProperty, IDNULL);
173 return isValid(subId) ? i : IDNULL;
174 }
175 }
176 }
177
178 return IDNULL;
179 }
180
subContainmentOf(const Layout::GenericLayout * layout,const Plasma::Applet * applet)181 Plasma::Containment *Storage::subContainmentOf(const Layout::GenericLayout *layout, const Plasma::Applet *applet)
182 {
183 if (!layout || !applet) {
184 return nullptr;
185 }
186
187 if (isSubContainment(layout, applet)) {
188 for (const auto containment : *layout->containments()) {
189 Plasma::Applet *parentApplet = qobject_cast<Plasma::Applet *>(containment->parent());
190 if (parentApplet && parentApplet == applet) {
191 return containment;
192 }
193 }
194 }
195
196 return nullptr;
197 }
198
lock(const Layout::GenericLayout * layout)199 void Storage::lock(const Layout::GenericLayout *layout)
200 {
201 QFileInfo layoutFileInfo(layout->file());
202
203 if (layoutFileInfo.exists() && layoutFileInfo.isWritable()) {
204 QFile(layout->file()).setPermissions(QFileDevice::ReadUser | QFileDevice::ReadGroup | QFileDevice::ReadOther);
205 }
206 }
207
unlock(const Layout::GenericLayout * layout)208 void Storage::unlock(const Layout::GenericLayout *layout)
209 {
210 QFileInfo layoutFileInfo(layout->file());
211
212 if (layoutFileInfo.exists() && !layoutFileInfo.isWritable()) {
213 QFile(layout->file()).setPermissions(QFileDevice::ReadUser | QFileDevice::WriteUser | QFileDevice::ReadGroup | QFileDevice::ReadOther);
214 }
215 }
216
217
importToCorona(const Layout::GenericLayout * layout)218 void Storage::importToCorona(const Layout::GenericLayout *layout)
219 {
220 if (!layout->corona()) {
221 return;
222 }
223
224 //! Setting mutable for create a containment
225 layout->corona()->setImmutability(Plasma::Types::Mutable);
226
227 QString temp1FilePath = m_storageTmpDir.path() + "/" + layout->name() + ".multiple.views";
228 //! we need to copy first the layout file because the kde cache
229 //! may not have yet been updated (KSharedConfigPtr)
230 //! this way we make sure at the latest changes stored in the layout file
231 //! will be also available when changing to Multiple Layouts
232 QString tempLayoutFilePath = m_storageTmpDir.path() + "/" + layout->name() + ".multiple.tmplayout";
233
234 //! WE NEED A WAY TO COPY A CONTAINMENT!!!!
235 QFile tempLayoutFile(tempLayoutFilePath);
236 QFile copyFile(temp1FilePath);
237 QFile layoutOriginalFile(layout->file());
238
239 if (tempLayoutFile.exists()) {
240 tempLayoutFile.remove();
241 }
242
243 if (copyFile.exists())
244 copyFile.remove();
245
246 layoutOriginalFile.copy(tempLayoutFilePath);
247
248 KSharedConfigPtr filePtr = KSharedConfig::openConfig(tempLayoutFilePath);
249 KSharedConfigPtr newFile = KSharedConfig::openConfig(temp1FilePath);
250 KConfigGroup copyGroup = KConfigGroup(newFile, "Containments");
251 KConfigGroup current_containments = KConfigGroup(filePtr, "Containments");
252
253 current_containments.copyTo(©Group);
254
255 newFile->reparseConfiguration();
256
257 //! update ids to unique ones
258 QString temp2File = newUniqueIdsFile(temp1FilePath, layout);
259
260 //! Finally import the configuration
261 importLayoutFile(layout, temp2File);
262 }
263
264
availableId(QStringList all,QStringList assigned,int base)265 QString Storage::availableId(QStringList all, QStringList assigned, int base)
266 {
267 bool found = false;
268
269 int i = base;
270
271 while (!found && i < 32000) {
272 QString iStr = QString::number(i);
273
274 if (!all.contains(iStr) && !assigned.contains(iStr)) {
275 return iStr;
276 }
277
278 i++;
279 }
280
281 return QString("");
282 }
283
appletGroupIsValid(const KConfigGroup & appletGroup)284 bool Storage::appletGroupIsValid(const KConfigGroup &appletGroup)
285 {
286 return !( appletGroup.keyList().count() == 0
287 && appletGroup.groupList().count() == 1
288 && appletGroup.groupList().at(0) == QLatin1String("Configuration")
289 && appletGroup.group("Configuration").keyList().count() == 1
290 && appletGroup.group("Configuration").hasKey("PreloadWeight") );
291 }
292
containmentsIds(const QString & filepath)293 QStringList Storage::containmentsIds(const QString &filepath)
294 {
295 QStringList ids;
296
297 KSharedConfigPtr filePtr = KSharedConfig::openConfig(filepath);
298 KConfigGroup containments = KConfigGroup(filePtr, "Containments");
299
300 for(const auto &cId : containments.groupList()) {
301 ids << cId;
302 }
303
304 return ids;
305 }
306
appletsIds(const QString & filepath)307 QStringList Storage::appletsIds(const QString &filepath)
308 {
309 QStringList ids;
310
311 KSharedConfigPtr filePtr = KSharedConfig::openConfig(filepath);
312 KConfigGroup containments = KConfigGroup(filePtr, "Containments");
313
314 for(const auto &cId : containments.groupList()) {
315 for(const auto &aId : containments.group(cId).group("Applets").groupList()) {
316 ids << aId;
317 }
318 }
319
320 return ids;
321 }
322
newUniqueIdsFile(QString originFile,const Layout::GenericLayout * destinationLayout)323 QString Storage::newUniqueIdsFile(QString originFile, const Layout::GenericLayout *destinationLayout)
324 {
325 if (!destinationLayout) {
326 return QString();
327 }
328
329 QString currentdestinationname = destinationLayout->name();
330 QString currentdestinationfile = "";
331
332 if (!destinationLayout->isActive()) {
333 currentdestinationfile = destinationLayout->file();
334 }
335
336 QString tempFile = m_storageTmpDir.path() + "/" + currentdestinationname + ".views.newids";
337
338 QFile copyFile(tempFile);
339
340 if (copyFile.exists()) {
341 copyFile.remove();
342 }
343
344 //! BEGIN updating the ids in the temp file
345 QStringList allIds;
346
347 if (destinationLayout->isActive()) {
348 allIds << destinationLayout->corona()->containmentsIds();
349 allIds << destinationLayout->corona()->appletsIds();
350 } else {
351 allIds << containmentsIds(currentdestinationfile);
352 allIds << appletsIds(currentdestinationfile);
353 }
354
355 QStringList toInvestigateContainmentIds;
356 QStringList toInvestigateAppletIds;
357 QStringList toInvestigateSubContIds;
358
359 //! first is the subcontainment id
360 QHash<QString, QString> subParentContainmentIds;
361 QHash<QString, QString> subAppletIds;
362
363 //qDebug() << "Ids:" << allIds;
364
365 //qDebug() << "to copy containments: " << toCopyContainmentIds;
366 //qDebug() << "to copy applets: " << toCopyAppletIds;
367
368 QStringList assignedIds;
369 QHash<QString, QString> assigned;
370
371 KSharedConfigPtr filePtr = KSharedConfig::openConfig(originFile);
372 KConfigGroup investigate_conts = KConfigGroup(filePtr, "Containments");
373
374 //! Record the containment and applet ids
375 for (const auto &cId : investigate_conts.groupList()) {
376 toInvestigateContainmentIds << cId;
377 auto appletsEntries = investigate_conts.group(cId).group("Applets");
378 toInvestigateAppletIds << appletsEntries.groupList();
379
380 //! investigate for subcontainments
381 for (const auto &appletId : appletsEntries.groupList()) {
382 int subId = subContainmentId(appletsEntries.group(appletId));
383
384 //! It is a subcontainment !!!
385 if (isValid(subId)) {
386 QString tSubIdStr = QString::number(subId);
387 toInvestigateSubContIds << tSubIdStr;
388 subParentContainmentIds[tSubIdStr] = cId;
389 subAppletIds[tSubIdStr] = appletId;
390 qDebug() << "subcontainment was found in the containment...";
391 }
392 }
393 }
394
395 //! Reassign containment and applet ids to unique ones
396 for (const auto &contId : toInvestigateContainmentIds) {
397 QString newId;
398
399 if (contId.toInt()>=12 && !allIds.contains(contId) && !assignedIds.contains(contId)) {
400 newId = contId;
401 } else {
402 newId = availableId(allIds, assignedIds, 12);
403 }
404
405 assignedIds << newId;
406 assigned[contId] = newId;
407 }
408
409 for (const auto &appId : toInvestigateAppletIds) {
410 QString newId;
411
412 if (appId.toInt()>=40 && !allIds.contains(appId) && !assignedIds.contains(appId)) {
413 newId = appId;
414 } else {
415 newId = availableId(allIds, assignedIds, 40);
416 }
417
418 assignedIds << newId;
419 assigned[appId] = newId;
420 }
421
422 qDebug() << "ALL CORONA IDS ::: " << allIds;
423 qDebug() << "FULL ASSIGNMENTS ::: " << assigned;
424
425 for (const auto &cId : toInvestigateContainmentIds) {
426 QString value = assigned[cId];
427
428 if (assigned.contains(value)) {
429 QString value2 = assigned[value];
430
431 if (cId != assigned[cId] && !value2.isEmpty() && cId == value2) {
432 qDebug() << "PROBLEM APPEARED !!!! FOR :::: " << cId << " .. fixed ..";
433 assigned[cId] = cId;
434 assigned[value] = value;
435 }
436 }
437 }
438
439 for (const auto &aId : toInvestigateAppletIds) {
440 QString value = assigned[aId];
441
442 if (assigned.contains(value)) {
443 QString value2 = assigned[value];
444
445 if (aId != assigned[aId] && !value2.isEmpty() && aId == value2) {
446 qDebug() << "PROBLEM APPEARED !!!! FOR :::: " << aId << " .. fixed ..";
447 assigned[aId] = aId;
448 assigned[value] = value;
449 }
450 }
451 }
452
453 qDebug() << "FIXED FULL ASSIGNMENTS ::: " << assigned;
454
455 //! update applet ids in their containment order and in MultipleLayouts update also the layoutId
456 for (const auto &cId : investigate_conts.groupList()) {
457 //! Update options that contain applet ids
458 //! (appletOrder) and (lockedZoomApplets) and (userBlocksColorizingApplets)
459 QStringList options;
460 options << "appletOrder" << "lockedZoomApplets" << "userBlocksColorizingApplets";
461
462 for (const auto &settingStr : options) {
463 QString order1 = investigate_conts.group(cId).group("General").readEntry(settingStr, QString());
464
465 if (!order1.isEmpty()) {
466 QStringList order1Ids = order1.split(";");
467 QStringList fixedOrder1Ids;
468
469 for (int i = 0; i < order1Ids.count(); ++i) {
470 fixedOrder1Ids.append(assigned[order1Ids[i]]);
471 }
472
473 QString fixedOrder1 = fixedOrder1Ids.join(";");
474 investigate_conts.group(cId).group("General").writeEntry(settingStr, fixedOrder1);
475 }
476 }
477
478 if (destinationLayout->isActive() && destinationLayout->corona()->layoutsManager()->memoryUsage() == MemoryUsage::MultipleLayouts) {
479 //! will be added in main corona multiple layouts file
480 investigate_conts.group(cId).writeEntry("layoutId", destinationLayout->name());
481 } else {
482 //! will be added in inactive layout
483 investigate_conts.group(cId).writeEntry("layoutId", QString());
484 }
485 }
486
487 //! must update also the sub id in its applet
488 for (const auto &subId : toInvestigateSubContIds) {
489 KConfigGroup subParentContainment = investigate_conts.group(subParentContainmentIds[subId]);
490 KConfigGroup subAppletConfig = subParentContainment.group("Applets").group(subAppletIds[subId]);
491
492 int entityIndex = subIdentityIndex(subAppletConfig);
493
494 if (entityIndex >= 0) {
495 if (!m_subIdentities[entityIndex].cfgGroup.isEmpty()) {
496 subAppletConfig = subAppletConfig.group(m_subIdentities[entityIndex].cfgGroup);
497 }
498
499 if (!m_subIdentities[entityIndex].cfgProperty.isEmpty()) {
500 subAppletConfig.writeEntry(m_subIdentities[entityIndex].cfgProperty, assigned[subId]);
501 subParentContainment.sync();
502 }
503 }
504 }
505
506 investigate_conts.sync();
507
508 //! Copy To Temp 2 File And Update Correctly The Ids
509 KSharedConfigPtr file2Ptr = KSharedConfig::openConfig(tempFile);
510 KConfigGroup fixedNewContainmets = KConfigGroup(file2Ptr, "Containments");
511
512 for (const auto &contId : investigate_conts.groupList()) {
513 QString pluginId = investigate_conts.group(contId).readEntry("plugin", "");
514
515 if (pluginId != "org.kde.desktopcontainment") { //!don't add ghost containments
516 KConfigGroup newContainmentGroup = fixedNewContainmets.group(assigned[contId]);
517 investigate_conts.group(contId).copyTo(&newContainmentGroup);
518
519 newContainmentGroup.group("Applets").deleteGroup();
520
521 for (const auto &appId : investigate_conts.group(contId).group("Applets").groupList()) {
522 KConfigGroup appletGroup = investigate_conts.group(contId).group("Applets").group(appId);
523 KConfigGroup newAppletGroup = fixedNewContainmets.group(assigned[contId]).group("Applets").group(assigned[appId]);
524 appletGroup.copyTo(&newAppletGroup);
525 }
526 }
527 }
528
529 file2Ptr->reparseConfiguration();
530
531 return tempFile;
532 }
533
syncToLayoutFile(const Layout::GenericLayout * layout,bool removeLayoutId)534 void Storage::syncToLayoutFile(const Layout::GenericLayout *layout, bool removeLayoutId)
535 {
536 if (!layout->corona() || !isWritable(layout)) {
537 return;
538 }
539
540 KSharedConfigPtr filePtr = KSharedConfig::openConfig(layout->file());
541
542 KConfigGroup oldContainments = KConfigGroup(filePtr, "Containments");
543 oldContainments.deleteGroup();
544
545 qDebug() << " LAYOUT :: " << layout->name() << " is syncing its original file.";
546
547 for (const auto containment : *layout->containments()) {
548 if (removeLayoutId) {
549 containment->config().writeEntry("layoutId", "");
550 }
551
552 KConfigGroup newGroup = oldContainments.group(QString::number(containment->id()));
553 containment->config().copyTo(&newGroup);
554
555 if (!removeLayoutId) {
556 newGroup.writeEntry("layoutId", "");
557 }
558
559 newGroup.sync();
560 }
561
562 filePtr->reparseConfiguration();
563 }
564
importLayoutFile(const Layout::GenericLayout * layout,QString file)565 QList<Plasma::Containment *> Storage::importLayoutFile(const Layout::GenericLayout *layout, QString file)
566 {
567 KSharedConfigPtr filePtr = KSharedConfig::openConfig(file);
568 auto newContainments = layout->corona()->importLayout(KConfigGroup(filePtr, ""));
569
570 qDebug() << " imported containments ::: " << newContainments.length();
571
572 QList<Plasma::Containment *> importedViews;
573
574 for (const auto containment : newContainments) {
575 if (isLatteContainment(containment)) {
576 qDebug() << "new latte containment id: " << containment->id();
577 importedViews << containment;
578 }
579 }
580
581 return importedViews;
582 }
583
importContainments(const QString & originFile,const QString & destinationFile)584 void Storage::importContainments(const QString &originFile, const QString &destinationFile)
585 {
586 if (originFile.isEmpty() || destinationFile.isEmpty()) {
587 return;
588 }
589
590 KSharedConfigPtr originPtr = KSharedConfig::openConfig(originFile);
591 KSharedConfigPtr destinationPtr = KSharedConfig::openConfig(destinationFile);
592
593 KConfigGroup originContainments = KConfigGroup(originPtr, "Containments");
594 KConfigGroup destinationContainments = KConfigGroup(destinationPtr, "Containments");
595
596 for (const auto originContId : originContainments.groupList()) {
597 KConfigGroup destinationContainment(&destinationContainments, originContId);
598 originContainments.group(originContId).copyTo(&destinationContainment);
599 }
600
601 destinationContainments.sync();
602 }
603
newView(const Layout::GenericLayout * destinationLayout,const Data::View & nextViewData)604 Data::View Storage::newView(const Layout::GenericLayout *destinationLayout, const Data::View &nextViewData)
605 {
606 if (!destinationLayout || nextViewData.originFile().isEmpty()) {
607 return Data::View();
608 }
609
610 qDebug() << "new view for layout";
611
612 if (destinationLayout->isActive()) {
613 //! Setting mutable for create a containment
614 destinationLayout->corona()->setImmutability(Plasma::Types::Mutable);
615 }
616
617 QString templateFile = nextViewData.originFile();
618 //! copy view template path in temp file
619 QString templateTmpAbsolutePath = m_storageTmpDir.path() + "/" + QFileInfo(templateFile).fileName() + ".newids";
620
621 if (QFile(templateTmpAbsolutePath).exists()) {
622 QFile(templateTmpAbsolutePath).remove();
623 }
624
625 QFile(templateFile).copy(templateTmpAbsolutePath);
626
627 //! update ids to unique ones
628 QString temp2File = newUniqueIdsFile(templateTmpAbsolutePath, destinationLayout);
629
630 //! update view containment data in case next data are provided
631 if (nextViewData.state() != Data::View::IsInvalid) {
632
633 KSharedConfigPtr lFile = KSharedConfig::openConfig(temp2File);
634 KConfigGroup containments = KConfigGroup(lFile, "Containments");
635
636 for (const auto cId : containments.groupList()) {
637 if (Layouts::Storage::self()->isLatteContainment(containments.group(cId))) {
638 //! first view we will find, we update its value
639 updateView(containments.group(cId), nextViewData);
640 break;
641 }
642 }
643
644 lFile->reparseConfiguration();
645 }
646
647 Data::ViewsTable updatedNextViews = views(temp2File);
648
649 if (updatedNextViews.rowCount() <= 0) {
650 return Data::View();
651 }
652
653 if (destinationLayout->isActive()) {
654 //! import views for active layout
655 QList<Plasma::Containment *> importedViews = importLayoutFile(destinationLayout, temp2File);
656
657 Plasma::Containment *newContainment = (importedViews.size() == 1 ? importedViews[0] : nullptr);
658
659 if (!newContainment || !newContainment->kPackage().isValid()) {
660 qWarning() << "the requested containment plugin can not be located or loaded from:" << templateFile;
661 return Data::View();
662 }
663 } else {
664 //! import views for inactive layout
665 importContainments(temp2File, destinationLayout->file());
666 }
667
668 return updatedNextViews[0];
669 }
670
clearExportedLayoutSettings(KConfigGroup & layoutSettingsGroup)671 void Storage::clearExportedLayoutSettings(KConfigGroup &layoutSettingsGroup)
672 {
673 layoutSettingsGroup.writeEntry("preferredForShortcutsTouched", false);
674 layoutSettingsGroup.writeEntry("lastUsedActivity", QString());
675 layoutSettingsGroup.writeEntry("activities", QStringList());
676 layoutSettingsGroup.sync();
677 }
678
exportTemplate(const QString & originFile,const QString & destinationFile,const Data::AppletsTable & approvedApplets)679 bool Storage::exportTemplate(const QString &originFile, const QString &destinationFile,const Data::AppletsTable &approvedApplets)
680 {
681 if (originFile.isEmpty() || !QFile(originFile).exists() || destinationFile.isEmpty()) {
682 return false;
683 }
684
685 if (QFile(destinationFile).exists()) {
686 QFile::remove(destinationFile);
687 }
688
689 QFile(originFile).copy(destinationFile);
690
691 KSharedConfigPtr destFilePtr = KSharedConfig::openConfig(destinationFile);
692 destFilePtr->reparseConfiguration();
693
694 KConfigGroup containments = KConfigGroup(destFilePtr, "Containments");
695
696 QStringList rejectedSubContainments;
697
698 //! clear applets that are not approved
699 for (const auto &cId : containments.groupList()) {
700 //! clear properties
701 containments.group(cId).writeEntry("layoutId", QString());
702 if (isLatteContainment(containments.group(cId))) {
703 containments.group(cId).writeEntry("isPreferredForShortcuts", false);
704 }
705
706 //! clear applets
707 auto applets = containments.group(cId).group("Applets");
708 for (const auto &aId: applets.groupList()) {
709 QString pluginId = applets.group(aId).readEntry("plugin", "");
710
711 if (!approvedApplets.containsId(pluginId)) {
712 if (!isSubContainment(applets.group(aId))) {
713 //!remove all configuration for that applet
714 for (const auto &configId: applets.group(aId).groupList()) {
715 applets.group(aId).group(configId).deleteGroup();
716 }
717 } else {
718 //! register which subcontaiments should return to default properties
719 rejectedSubContainments << QString::number(subContainmentId(applets.group(aId)));
720 }
721 }
722 }
723 }
724
725 //! clear rejected SubContainments
726 for (const auto &cId : containments.groupList()) {
727 if (rejectedSubContainments.contains(cId)) {
728 containments.group(cId).group("General").deleteGroup();
729 }
730 };
731
732 KConfigGroup layoutSettingsGrp(destFilePtr, "LayoutSettings");
733 clearExportedLayoutSettings(layoutSettingsGrp);
734 destFilePtr->reparseConfiguration();
735
736 return true;
737 }
738
exportTemplate(const Layout::GenericLayout * layout,Plasma::Containment * containment,const QString & destinationFile,const Data::AppletsTable & approvedApplets)739 bool Storage::exportTemplate(const Layout::GenericLayout *layout, Plasma::Containment *containment, const QString &destinationFile, const Data::AppletsTable &approvedApplets)
740 {
741 if (!layout || !containment || destinationFile.isEmpty()) {
742 return false;
743 }
744
745 if (QFile(destinationFile).exists()) {
746 QFile::remove(destinationFile);
747 }
748
749 KSharedConfigPtr destFilePtr = KSharedConfig::openConfig(destinationFile);
750 destFilePtr->reparseConfiguration();
751
752 KConfigGroup copied_conts = KConfigGroup(destFilePtr, "Containments");
753 KConfigGroup copied_c1 = KConfigGroup(&copied_conts, QString::number(containment->id()));
754
755 containment->config().copyTo(&copied_c1);
756
757 //!investigate if there are subcontainments in the containment to copy also
758
759 //! subId, subAppletId
760 QHash<uint, QString> subInfo;
761 auto applets = containment->config().group("Applets");
762
763 for (const auto &applet : applets.groupList()) {
764 int tSubId = subContainmentId(applets.group(applet));
765
766 //! It is a subcontainment !!!
767 if (isValid(tSubId)) {
768 subInfo[tSubId] = applet;
769 qDebug() << "subcontainment with id "<< tSubId << " was found in the containment... ::: " << containment->id();
770 }
771 }
772
773 if (subInfo.count() > 0) {
774 for(const auto subId : subInfo.keys()) {
775 Plasma::Containment *subcontainment{nullptr};
776
777 for (const auto containment : layout->corona()->containments()) {
778 if (containment->id() == subId) {
779 subcontainment = containment;
780 break;
781 }
782 }
783
784 if (subcontainment) {
785 KConfigGroup copied_sub = KConfigGroup(&copied_conts, QString::number(subcontainment->id()));
786 subcontainment->config().copyTo(&copied_sub);
787 }
788 }
789 }
790 //! end of subcontainments specific code
791
792 QStringList rejectedSubContainments;
793
794 //! clear applets that are not approved
795 for (const auto &cId : copied_conts.groupList()) {
796 //! clear properties
797 copied_conts.group(cId).writeEntry("layoutId", QString());
798 if (isLatteContainment(copied_conts.group(cId))) {
799 copied_conts.group(cId).writeEntry("isPreferredForShortcuts", false);
800 }
801
802 //! clear applets
803 auto applets = copied_conts.group(cId).group("Applets");
804 for (const auto &aId: applets.groupList()) {
805 QString pluginId = applets.group(aId).readEntry("plugin", "");
806
807 if (!approvedApplets.containsId(pluginId)) {
808 if (!isSubContainment(applets.group(aId))) {
809 //!remove all configuration for that applet
810 for (const auto &configId: applets.group(aId).groupList()) {
811 applets.group(aId).group(configId).deleteGroup();
812 }
813 } else {
814 //! register which subcontaiments should return to default properties
815 rejectedSubContainments << QString::number(subContainmentId(applets.group(aId)));
816 }
817 }
818 }
819 }
820
821 //! clear rejected SubContainments
822 for (const auto &cId : copied_conts.groupList()) {
823 if (rejectedSubContainments.contains(cId)) {
824 copied_conts.group(cId).group("General").deleteGroup();
825 }
826 };
827
828 KConfigGroup layoutSettingsGrp(destFilePtr, "LayoutSettings");
829 clearExportedLayoutSettings(layoutSettingsGrp);
830 destFilePtr->reparseConfiguration();
831
832 return true;
833 }
834
hasDifferentAppletsWithSameId(const Layout::GenericLayout * layout,Data::Error & error)835 bool Storage::hasDifferentAppletsWithSameId(const Layout::GenericLayout *layout, Data::Error &error)
836 {
837 if (!layout || layout->file().isEmpty() || !QFile(layout->file()).exists()) {
838 return false;
839 }
840
841 error.id = s_knownErrors[Data::Error::APPLETSWITHSAMEID].id;
842 error.name = s_knownErrors[Data::Error::APPLETSWITHSAMEID].name;
843
844 if (layout->isActive()) { // active layout
845 QStringList registeredapplets;
846 QStringList conflictedapplets;
847
848 //! split ids to normal registered and conflicted
849 for (const auto containment : *layout->containments()) {
850 QString cid = QString::number(containment->id());
851
852 for (const auto applet : containment->applets()) {
853 QString aid = QString::number(applet->id());
854
855 if (!registeredapplets.contains(aid)) {
856 registeredapplets << aid;
857 } else if (!conflictedapplets.contains(aid)) {
858 conflictedapplets << aid;
859 }
860 }
861 }
862
863 //! create error data
864 for (const auto containment : *layout->containments()) {
865 QString cid = QString::number(containment->id());
866
867 for (const auto applet : containment->applets()) {
868 QString aid = QString::number(applet->id());
869
870 if (!conflictedapplets.contains(aid)) {
871 continue;
872 }
873
874 Data::ErrorInformation errorinfo;
875 errorinfo.id = QString::number(error.information.rowCount());
876 errorinfo.containment = metadata(containment->pluginMetaData().pluginId());
877 errorinfo.containment.storageId = cid;
878 errorinfo.applet = metadata(applet->pluginMetaData().pluginId());
879 errorinfo.applet.storageId = aid;
880
881 error.information << errorinfo;
882 }
883 }
884 } else { // inactive layout
885 KSharedConfigPtr lfile = KSharedConfig::openConfig(layout->file());
886 KConfigGroup containmentsEntries = KConfigGroup(lfile, "Containments");
887
888 QStringList registeredapplets;
889 QStringList conflictedapplets;
890
891 //! split ids to normal registered and conflicted
892 for (const auto &cid : containmentsEntries.groupList()) {
893 for (const auto &aid : containmentsEntries.group(cid).group("Applets").groupList()) {
894 if (!registeredapplets.contains(aid)) {
895 registeredapplets << aid;
896 } else if (!conflictedapplets.contains(aid)) {
897 conflictedapplets << aid;
898 }
899 }
900 }
901
902 //! create error data
903 for (const auto &cid : containmentsEntries.groupList()) {
904 for (const auto &aid : containmentsEntries.group(cid).group("Applets").groupList()) {
905 if (!conflictedapplets.contains(aid)) {
906 continue;
907 }
908
909 Data::ErrorInformation errorinfo;
910 errorinfo.id = QString::number(error.information.rowCount());
911 errorinfo.containment = metadata(containmentsEntries.group(cid).readEntry("plugin", ""));
912 errorinfo.containment.storageId = cid;
913 errorinfo.applet = metadata(containmentsEntries.group(cid).group("Applets").group(aid).readEntry("plugin", ""));
914 errorinfo.applet.storageId = aid;
915
916 error.information << errorinfo;
917 }
918 }
919 }
920
921 return !error.information.isEmpty();
922 }
923
hasAppletsAndContainmentsWithSameId(const Layout::GenericLayout * layout,Data::Warning & warning)924 bool Storage::hasAppletsAndContainmentsWithSameId(const Layout::GenericLayout *layout, Data::Warning &warning)
925 {
926 if (!layout || layout->file().isEmpty() || !QFile(layout->file()).exists()) {
927 return false;
928 }
929
930 warning.id = s_knownErrors[Data::Error::APPLETANDCONTAINMENTWITHSAMEID].id;
931 warning.name = s_knownErrors[Data::Error::APPLETANDCONTAINMENTWITHSAMEID].name;
932
933 if (layout->isActive()) { // active layout
934 QStringList registeredcontainments;
935 QStringList conflicted;
936
937 //! discover normal containment ids
938 for (const auto containment : *layout->containments()) {
939 QString cid = QString::number(containment->id());
940
941 if (registeredcontainments.contains(cid)) {
942 continue;
943 }
944
945 registeredcontainments << cid;
946 }
947
948 //! discover conflicted ids between containments and applets
949 for (const auto containment : *layout->containments()) {
950 QString cid = QString::number(containment->id());
951
952 for (const auto applet : containment->applets()) {
953 QString aid = QString::number(applet->id());
954
955 if (!registeredcontainments.contains(aid)) {
956 continue;
957 } else if (!conflicted.contains(aid)) {
958 conflicted << aid;
959 }
960 }
961 }
962
963 //! create warning data
964 for (const auto containment : *layout->containments()) {
965 QString cid = QString::number(containment->id());
966
967 if (conflicted.contains(cid)) {
968 Data::WarningInformation warninginfo;
969 warninginfo.id = QString::number(warning.information.rowCount());
970 warninginfo.containment = metadata(containment->pluginMetaData().pluginId());
971 warninginfo.containment.storageId = cid;
972
973 warning.information << warninginfo;
974 }
975
976 for (const auto applet : containment->applets()) {
977 QString aid = QString::number(applet->id());
978
979 if (!conflicted.contains(aid)) {
980 continue;
981 }
982
983 Data::WarningInformation warninginfo;
984 warninginfo.id = QString::number(warning.information.rowCount());
985 warninginfo.containment = metadata(containment->pluginMetaData().pluginId());
986 warninginfo.containment.storageId = cid;
987 warninginfo.applet = metadata(applet->pluginMetaData().pluginId());
988 warninginfo.applet.storageId = aid;
989
990 warning.information << warninginfo;
991 }
992 }
993 } else { // inactive layout
994 KSharedConfigPtr lfile = KSharedConfig::openConfig(layout->file());
995 KConfigGroup containmentsEntries = KConfigGroup(lfile, "Containments");
996
997 QStringList registeredcontainments;
998 QStringList conflicted;
999
1000 //! discover normal containment ids
1001 for (const auto &cid : containmentsEntries.groupList()) {
1002 if (registeredcontainments.contains(cid)) {
1003 continue;
1004 }
1005
1006 registeredcontainments << cid;
1007 }
1008
1009 //! discover conflicted ids between containments and applets
1010 for (const auto &cid : containmentsEntries.groupList()) {
1011 for (const auto &aid : containmentsEntries.group(cid).group("Applets").groupList()) {
1012 if (!registeredcontainments.contains(aid)) {
1013 continue;
1014 } else if (!conflicted.contains(aid)) {
1015 conflicted << aid;
1016 }
1017 }
1018 }
1019
1020 //! create warning data
1021 for (const auto &cid : containmentsEntries.groupList()) {
1022 if (conflicted.contains(cid)) {
1023 Data::WarningInformation warninginfo;
1024 warninginfo.id = QString::number(warning.information.rowCount());
1025 warninginfo.containment = metadata(containmentsEntries.group(cid).readEntry("plugin", ""));
1026 warninginfo.containment.storageId = cid;
1027
1028 warning.information << warninginfo;
1029 }
1030
1031 for (const auto &aid : containmentsEntries.group(cid).group("Applets").groupList()) {
1032 if (!conflicted.contains(aid)) {
1033 continue;
1034 }
1035
1036 Data::WarningInformation warninginfo;
1037 warninginfo.id = QString::number(warning.information.rowCount());
1038 warninginfo.containment = metadata(containmentsEntries.group(cid).readEntry("plugin", ""));
1039 warninginfo.containment.storageId = cid;
1040 warninginfo.applet = metadata(containmentsEntries.group(cid).group("Applets").group(aid).readEntry("plugin", ""));
1041 warninginfo.applet.storageId = aid;
1042
1043 warning.information << warninginfo;
1044 }
1045 }
1046 }
1047
1048 return !warning.information.isEmpty();
1049 }
1050
hasOrphanedParentAppletOfSubContainment(const Layout::GenericLayout * layout,Data::Error & error)1051 bool Storage::hasOrphanedParentAppletOfSubContainment(const Layout::GenericLayout *layout, Data::Error &error)
1052 {
1053 if (!layout || layout->file().isEmpty() || !QFile(layout->file()).exists()) {
1054 return false;
1055 }
1056
1057 error.id = s_knownErrors[Data::Error::ORPHANEDPARENTAPPLETOFSUBCONTAINMENT].id;
1058 error.name = s_knownErrors[Data::Error::ORPHANEDPARENTAPPLETOFSUBCONTAINMENT].name;
1059
1060 Data::ViewsTable views = Layouts::Storage::self()->views(layout);
1061
1062 if (layout->isActive()) { // active layout
1063
1064 //! create error data
1065 for (const auto containment : *layout->containments()) {
1066 QString cid = QString::number(containment->id());
1067
1068 for (const auto applet : containment->applets()) {
1069 QString aid = QString::number(applet->id());
1070
1071 int subid = subContainmentId(applet->config());
1072
1073 if (subid == IDNULL || hasContainment(layout, subid)) {
1074 continue;
1075 }
1076
1077 Data::ErrorInformation errorinfo;
1078 errorinfo.id = QString::number(error.information.rowCount());
1079 errorinfo.containment = metadata(containment->pluginMetaData().pluginId());
1080 errorinfo.containment.storageId = cid;
1081 errorinfo.applet = metadata(applet->pluginMetaData().pluginId());
1082 errorinfo.applet.storageId = aid;
1083 errorinfo.applet.subcontainmentId = subid;
1084
1085 error.information << errorinfo;
1086 }
1087 }
1088 } else {
1089 KSharedConfigPtr lfile = KSharedConfig::openConfig(layout->file());
1090 KConfigGroup containmentsEntries = KConfigGroup(lfile, "Containments");
1091
1092 //! create error data
1093 for (const auto &cid : containmentsEntries.groupList()) {
1094 for (const auto &aid : containmentsEntries.group(cid).group("Applets").groupList()) {
1095 int subid = subContainmentId(containmentsEntries.group(cid).group("Applets").group(aid));
1096
1097 if (subid == IDNULL || hasContainment(layout, subid)) {
1098 continue;
1099 }
1100
1101 Data::ErrorInformation errorinfo;
1102 errorinfo.id = QString::number(error.information.rowCount());
1103 errorinfo.containment = metadata(containmentsEntries.group(cid).readEntry("plugin", ""));
1104 errorinfo.containment.storageId = cid;
1105 errorinfo.applet = metadata(containmentsEntries.group(cid).group("Applets").group(aid).readEntry("plugin", ""));
1106 errorinfo.applet.storageId = aid;
1107 errorinfo.applet.subcontainmentId = subid;
1108
1109 error.information << errorinfo;
1110 }
1111 }
1112 }
1113
1114 Data::Warning warning1;
1115 if (!error.information.isEmpty() && hasOrphanedSubContainments(layout, warning1)) {
1116 error.information << warning1.information;
1117 }
1118
1119 return !error.information.isEmpty();
1120 }
1121
hasOrphanedSubContainments(const Layout::GenericLayout * layout,Data::Warning & warning)1122 bool Storage::hasOrphanedSubContainments(const Layout::GenericLayout *layout, Data::Warning &warning)
1123 {
1124 if (!layout || layout->file().isEmpty() || !QFile(layout->file()).exists()) {
1125 return false;
1126 }
1127
1128 warning.id = s_knownErrors[Data::Error::ORPHANEDSUBCONTAINMENT].id;
1129 warning.name = s_knownErrors[Data::Error::ORPHANEDSUBCONTAINMENT].name;
1130
1131 Data::ViewsTable views = Layouts::Storage::self()->views(layout);
1132
1133 if (layout->isActive()) { // active layout
1134 //! create warning data
1135 for (const auto containment : *layout->containments()) {
1136 QString cid = QString::number(containment->id());
1137
1138 Plasma::Applet *parentApplet = qobject_cast<Plasma::Applet *>(containment->parent());
1139 Plasma::Containment *parentContainment = parentApplet ? qobject_cast<Plasma::Containment *>(parentApplet->parent()) : nullptr;
1140
1141 if (isLatteContainment(containment) || (parentApplet && parentContainment && layout->contains(parentContainment))) {
1142 //! is latte containment or is subcontainment that belongs to latte containment
1143 continue;
1144 }
1145
1146 Data::WarningInformation warninginfo;
1147 warninginfo.id = QString::number(warning.information.rowCount());
1148 warninginfo.containment = metadata(containment->pluginMetaData().pluginId());
1149 warninginfo.containment.storageId = cid;
1150 warning.information << warninginfo;
1151 }
1152 } else { // inactive layout
1153 KSharedConfigPtr lfile = KSharedConfig::openConfig(layout->file());
1154 KConfigGroup containmentsEntries = KConfigGroup(lfile, "Containments");
1155
1156 //! create warning data
1157 for (const auto &cid : containmentsEntries.groupList()) {
1158 if (views.hasContainmentId(cid)) {
1159 continue;
1160 }
1161
1162 Data::WarningInformation warninginfo;
1163 warninginfo.id = QString::number(warning.information.rowCount());
1164 warninginfo.containment = metadata(containmentsEntries.group(cid).readEntry("plugin", ""));
1165 warninginfo.containment.storageId = cid;
1166 warning.information << warninginfo;
1167 }
1168 }
1169
1170 return !warning.information.isEmpty();
1171 }
1172
errors(const Layout::GenericLayout * layout)1173 Data::ErrorsList Storage::errors(const Layout::GenericLayout *layout)
1174 {
1175 Data::ErrorsList errs;
1176
1177 if (!layout || layout->file().isEmpty() || !QFile(layout->file()).exists()) {
1178 return errs;
1179 }
1180
1181 Data::Error error1;
1182
1183 if (hasDifferentAppletsWithSameId(layout, error1)) {
1184 errs << error1;
1185 }
1186
1187 Data::Error error2;
1188
1189 if (hasOrphanedParentAppletOfSubContainment(layout, error2)) {
1190 errs << error2;
1191 }
1192
1193 return errs;
1194 }
1195
warnings(const Layout::GenericLayout * layout)1196 Data::WarningsList Storage::warnings(const Layout::GenericLayout *layout)
1197 {
1198 Data::WarningsList warns;
1199
1200 if (!layout || layout->file().isEmpty() || !QFile(layout->file()).exists()) {
1201 return warns;
1202 }
1203
1204 Data::Warning warning1;
1205
1206 if (hasAppletsAndContainmentsWithSameId(layout, warning1)) {
1207 warns << warning1;
1208 }
1209
1210 Data::Error error1;
1211 Data::Warning warning2;
1212
1213 if (!hasOrphanedParentAppletOfSubContainment(layout, error1) /*this is needed because this error has higher priority*/
1214 && hasOrphanedSubContainments(layout, warning2)) {
1215 warns << warning2;
1216 }
1217
1218 return warns;
1219 }
1220
1221 //! AppletsData Information
metadata(const QString & pluginId)1222 Data::Applet Storage::metadata(const QString &pluginId)
1223 {
1224 Data::Applet data;
1225 data.id = pluginId;
1226
1227 KPackage::Package pkg = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/Applet"));
1228 pkg.setDefaultPackageRoot(QStringLiteral("plasma/plasmoids"));
1229 pkg.setPath(pluginId);
1230
1231 if (pkg.isValid()) {
1232 data.name = pkg.metadata().name();
1233 data.description = pkg.metadata().description();
1234
1235 QString iconName = pkg.metadata().iconName();
1236 if (!iconName.startsWith("/") && iconName.contains("/")) {
1237 data.icon = QFileInfo(pkg.metadata().fileName()).absolutePath() + "/" + iconName;
1238 } else {
1239 data.icon = iconName;
1240 }
1241 }
1242
1243 if (data.name.isEmpty()) {
1244 //! this is also a way to identify if a package is installed or not in current system
1245 data.name = data.id;
1246 }
1247
1248 return data;
1249 }
1250
plugins(const Layout::GenericLayout * layout,const int containmentid)1251 Data::AppletsTable Storage::plugins(const Layout::GenericLayout *layout, const int containmentid)
1252 {
1253 Data::AppletsTable knownapplets;
1254 Data::AppletsTable unknownapplets;
1255
1256 if (!layout) {
1257 return knownapplets;
1258 }
1259
1260 //! empty means all containments are valid
1261 QList<int> validcontainmentids;
1262
1263 if (isValid(containmentid)) {
1264 validcontainmentids << containmentid;
1265
1266 //! searching for specific containment and subcontainments and ignore all other containments
1267 for(auto containment : *layout->containments()) {
1268 if (((int)containment->id()) != containmentid) {
1269 //! ignore irrelevant containments
1270 continue;
1271 }
1272
1273 for (auto applet : containment->applets()) {
1274 if (isSubContainment(layout, applet)) {
1275 validcontainmentids << subContainmentId(applet->config());
1276 }
1277 }
1278 }
1279 }
1280
1281 //! cycle through valid contaiments in order to retrieve their metadata
1282 for(auto containment : *layout->containments()) {
1283 if (validcontainmentids.count()>0 && !validcontainmentids.contains(containment->id())) {
1284 //! searching only for valid containments
1285 continue;
1286 }
1287
1288 for (auto applet : containment->applets()) {
1289 QString pluginId = applet->pluginMetaData().pluginId();
1290 if (!knownapplets.containsId(pluginId) && !unknownapplets.containsId(pluginId)) {
1291 Data::Applet appletdata = metadata(pluginId);
1292
1293 if (appletdata.isInstalled()) {
1294 knownapplets.insertBasedOnName(appletdata);
1295 } else if (appletdata.isValid()) {
1296 unknownapplets.insertBasedOnName(appletdata);
1297 }
1298 }
1299 }
1300 }
1301
1302 knownapplets << unknownapplets;
1303
1304 return knownapplets;
1305 }
1306
plugins(const QString & layoutfile,const int containmentid)1307 Data::AppletsTable Storage::plugins(const QString &layoutfile, const int containmentid)
1308 {
1309 Data::AppletsTable knownapplets;
1310 Data::AppletsTable unknownapplets;
1311
1312 if (layoutfile.isEmpty()) {
1313 return knownapplets;
1314 }
1315
1316 KSharedConfigPtr lFile = KSharedConfig::openConfig(layoutfile);
1317 KConfigGroup containmentGroups = KConfigGroup(lFile, "Containments");
1318
1319 //! empty means all containments are valid
1320 QList<int> validcontainmentids;
1321
1322 if (isValid(containmentid)) {
1323 validcontainmentids << containmentid;
1324
1325 //! searching for specific containment and subcontainments and ignore all other containments
1326 for (const auto &cId : containmentGroups.groupList()) {
1327 if (cId.toInt() != containmentid) {
1328 //! ignore irrelevant containments
1329 continue;
1330 }
1331
1332 auto appletGroups = containmentGroups.group(cId).group("Applets");
1333
1334 for (const auto &appletId : appletGroups.groupList()) {
1335 KConfigGroup appletCfg = appletGroups.group(appletId);
1336 if (isSubContainment(appletCfg)) {
1337 validcontainmentids << subContainmentId(appletCfg);
1338 }
1339 }
1340 }
1341 }
1342
1343 //! cycle through valid contaiments in order to retrieve their metadata
1344 for (const auto &cId : containmentGroups.groupList()) {
1345 if (validcontainmentids.count()>0 && !validcontainmentids.contains(cId.toInt())) {
1346 //! searching only for valid containments
1347 continue;
1348 }
1349
1350 auto appletGroups = containmentGroups.group(cId).group("Applets");
1351
1352 for (const auto &appletId : appletGroups.groupList()) {
1353 KConfigGroup appletCfg = appletGroups.group(appletId);
1354 QString pluginId = appletCfg.readEntry("plugin", "");
1355
1356 if (!knownapplets.containsId(pluginId) && !unknownapplets.containsId(pluginId)) {
1357 Data::Applet appletdata = metadata(pluginId);
1358
1359 if (appletdata.isInstalled()) {
1360 knownapplets.insertBasedOnName(appletdata);
1361 } else if (appletdata.isValid()) {
1362 unknownapplets.insertBasedOnName(appletdata);
1363 }
1364 }
1365 }
1366 }
1367
1368 knownapplets << unknownapplets;
1369
1370 return knownapplets;
1371 }
1372
1373 //! Views Data
1374
syncContainmentConfig(Plasma::Containment * containment)1375 void Storage::syncContainmentConfig(Plasma::Containment *containment)
1376 {
1377 if (!containment) {
1378 return;
1379 }
1380
1381 for(auto applet: containment->applets()) {
1382 KConfigGroup appletGeneralConfig = applet->config().group("General");
1383
1384 if (appletGeneralConfig.exists()) {
1385 appletGeneralConfig.sync();
1386 }
1387
1388 applet->config().sync();
1389 }
1390
1391 containment->config().sync();
1392 }
1393
containsView(const QString & filepath,const int & viewId)1394 bool Storage::containsView(const QString &filepath, const int &viewId)
1395 {
1396 KSharedConfigPtr lFile = KSharedConfig::openConfig(filepath);
1397 KConfigGroup containmentGroups = KConfigGroup(lFile, "Containments");
1398 KConfigGroup viewGroup = containmentGroups.group(QString::number(viewId));
1399 return viewGroup.exists() && isLatteContainment(viewGroup);
1400 }
1401
hasContainment(const Layout::GenericLayout * layout,const int & id)1402 bool Storage::hasContainment(const Layout::GenericLayout *layout, const int &id)
1403 {
1404 if (!layout || layout->file().isEmpty() || !QFile(layout->file()).exists()) {
1405 return false;
1406 }
1407
1408 if (layout->isActive()) { // active layout
1409 for(const auto containment : *layout->containments()) {
1410 if ((int)containment->id() == id) {
1411 return true;
1412 }
1413 }
1414 } else { // inactive layout
1415 KSharedConfigPtr lfile = KSharedConfig::openConfig(layout->file());
1416 KConfigGroup containmentsEntries = KConfigGroup(lfile, "Containments");
1417
1418 //! create warning data
1419 for (const auto &cid : containmentsEntries.groupList()) {
1420 if (cid.toInt() == id) {
1421 return true;
1422 }
1423 }
1424 }
1425
1426 return false;
1427 }
1428
1429
subcontainments(const Layout::GenericLayout * layout,const Plasma::Containment * lattecontainment) const1430 Data::GenericTable<Data::Generic> Storage::subcontainments(const Layout::GenericLayout *layout, const Plasma::Containment *lattecontainment) const
1431 {
1432 Data::GenericTable<Data::Generic> subs;
1433
1434 if (!layout || !Layouts::Storage::self()->isLatteContainment(lattecontainment)) {
1435 return subs;
1436 }
1437
1438 for (const auto containment : (*layout->containments())) {
1439 if (containment == lattecontainment) {
1440 continue;
1441 }
1442
1443 Plasma::Applet *parentApplet = qobject_cast<Plasma::Applet *>(containment->parent());
1444
1445 //! add subcontainments for that lattecontainment
1446 if (parentApplet && parentApplet->containment() && parentApplet->containment() == lattecontainment) {
1447 Data::Generic subdata;
1448 subdata.id = QString::number(containment->id());
1449 subs << subdata;
1450 }
1451 }
1452
1453 return subs;
1454 }
1455
subcontainments(const KConfigGroup & containmentGroup)1456 Data::GenericTable<Data::Generic> Storage::subcontainments(const KConfigGroup &containmentGroup)
1457 {
1458 Data::GenericTable<Data::Generic> subs;
1459
1460 if (!Layouts::Storage::self()->isLatteContainment(containmentGroup)) {
1461 return subs;
1462 }
1463
1464 auto applets = containmentGroup.group("Applets");
1465
1466 for (const auto &applet : applets.groupList()) {
1467 if (isSubContainment(applets.group(applet))) {
1468 Data::Generic subdata;
1469 subdata.id = QString::number(subContainmentId(applets.group(applet)));
1470 subs << subdata;
1471 }
1472 }
1473
1474 return subs;
1475 }
1476
view(const Layout::GenericLayout * layout,const Plasma::Containment * lattecontainment)1477 Data::View Storage::view(const Layout::GenericLayout *layout, const Plasma::Containment *lattecontainment)
1478 {
1479 Data::View vdata;
1480
1481 if (!layout || !Layouts::Storage::self()->isLatteContainment(lattecontainment)) {
1482 return vdata;
1483 }
1484
1485 vdata = view(lattecontainment->config());
1486
1487 vdata.screen = lattecontainment->screen();
1488 if (!isValid(vdata.screen)) {
1489 vdata.screen = lattecontainment->lastScreen();
1490 }
1491
1492 vdata.subcontainments = subcontainments(layout, lattecontainment);
1493
1494 return vdata;
1495 }
1496
view(const KConfigGroup & containmentGroup)1497 Data::View Storage::view(const KConfigGroup &containmentGroup)
1498 {
1499 Data::View vdata;
1500
1501 if (!Layouts::Storage::self()->isLatteContainment(containmentGroup)) {
1502 return vdata;
1503 }
1504
1505 vdata.id = containmentGroup.name();
1506 vdata.name = containmentGroup.readEntry("name", QString());
1507 vdata.isActive = false;
1508 vdata.onPrimary = containmentGroup.readEntry("onPrimary", true);
1509 vdata.screen = containmentGroup.readEntry("lastScreen", IDNULL);
1510 vdata.screenEdgeMargin = containmentGroup.group("General").readEntry("screenEdgeMargin", (int)-1);
1511
1512 int location = containmentGroup.readEntry("location", (int)Plasma::Types::BottomEdge);
1513 vdata.edge = (Plasma::Types::Location)location;
1514
1515 vdata.maxLength = containmentGroup.group("General").readEntry("maxLength", (float)100.0);
1516
1517 int alignment = containmentGroup.group("General").readEntry("alignment", (int)Latte::Types::Center) ;
1518 vdata.alignment = (Latte::Types::Alignment)alignment;
1519
1520 vdata.subcontainments = subcontainments(containmentGroup);
1521 vdata.setState(Data::View::IsCreated);
1522
1523 return vdata;
1524 }
1525
updateView(KConfigGroup viewGroup,const Data::View & viewData)1526 void Storage::updateView(KConfigGroup viewGroup, const Data::View &viewData)
1527 {
1528 if (!Layouts::Storage::self()->isLatteContainment(viewGroup)) {
1529 return;
1530 }
1531
1532 viewGroup.writeEntry("name", viewData.name);
1533 viewGroup.writeEntry("onPrimary", viewData.onPrimary);
1534 viewGroup.writeEntry("lastScreen", viewData.screen);
1535 viewGroup.group("General").writeEntry("screenEdgeMargin", viewData.screenEdgeMargin);
1536 viewGroup.writeEntry("location", (int)viewData.edge);
1537 viewGroup.writeEntry("maxLength", viewData.maxLength);
1538 viewGroup.group("General").writeEntry("alignment", (int)viewData.alignment);
1539 viewGroup.sync();
1540 }
1541
updateView(const Layout::GenericLayout * layout,const Data::View & viewData)1542 void Storage::updateView(const Layout::GenericLayout *layout, const Data::View &viewData)
1543 {
1544 if (!layout) {
1545 return;
1546 }
1547
1548 auto view = layout->viewForContainment(viewData.id.toUInt());
1549
1550 if (view) {
1551 qDebug() << "Storage::updateView should not be called because view is active and present...";
1552 return;
1553 }
1554
1555 if (layout->isActive()) {
1556 //! active view but is not present in active screens;
1557 auto containment = layout->containmentForId(viewData.id.toUInt());
1558 if (containment) {
1559 //! update containment
1560 containment->setLocation(viewData.edge);
1561 updateView(containment->config(), viewData);
1562 }
1563 } else {
1564 //! inactive view and in layout storage
1565 KSharedConfigPtr lFile = KSharedConfig::openConfig(layout->file());
1566 KConfigGroup containmentGroups = KConfigGroup(lFile, "Containments");
1567 KConfigGroup viewContainment = containmentGroups.group(viewData.id);
1568
1569 if (viewContainment.exists() && Layouts::Storage::self()->isLatteContainment(viewContainment)) {
1570 updateView(viewContainment, viewData);
1571 }
1572 }
1573 }
1574
removeView(const QString & filepath,const Data::View & viewData)1575 void Storage::removeView(const QString &filepath, const Data::View &viewData)
1576 {
1577 if (!viewData.isValid()) {
1578 return;
1579 }
1580
1581 removeContainment(filepath, viewData.id);
1582
1583 for (int i=0; i<viewData.subcontainments.rowCount(); ++i) {
1584 removeContainment(filepath, viewData.subcontainments[i].id);
1585 }
1586 }
1587
removeContainment(const QString & filepath,const QString & containmentId)1588 void Storage::removeContainment(const QString &filepath, const QString &containmentId)
1589 {
1590 if (containmentId.isEmpty()) {
1591 return;
1592 }
1593
1594 KSharedConfigPtr lFile = KSharedConfig::openConfig(filepath);
1595 KConfigGroup containmentGroups = KConfigGroup(lFile, "Containments");
1596
1597 if (!containmentGroups.group(containmentId).exists()) {
1598 return;
1599 }
1600
1601 containmentGroups.group(containmentId).deleteGroup();
1602 lFile->reparseConfiguration();
1603 }
1604
storedView(const Layout::GenericLayout * layout,const int & containmentId)1605 QString Storage::storedView(const Layout::GenericLayout *layout, const int &containmentId)
1606 {
1607 //! make sure that layout and containmentId are valid
1608 if (!layout) {
1609 return QString();
1610 }
1611
1612 if (layout->isActive()) {
1613 auto containment = layout->containmentForId((uint)containmentId);
1614 if (!containment || !isLatteContainment(containment)) {
1615 return QString();
1616 }
1617 } else {
1618 if (!containsView(layout->file(), containmentId)) {
1619 return QString();
1620 }
1621 }
1622
1623 //! at this point we are sure that both layout and containmentId are acceptable
1624 QString nextTmpStoredViewAbsolutePath = m_storageTmpDir.path() + "/" + QFileInfo(layout->name()).fileName() + "." + QString::number(containmentId) + ".stored.tmp";
1625
1626 QFile tempStoredViewFile(nextTmpStoredViewAbsolutePath);
1627
1628 if (tempStoredViewFile.exists()) {
1629 tempStoredViewFile.remove();
1630 }
1631
1632 KSharedConfigPtr destinationPtr = KSharedConfig::openConfig(nextTmpStoredViewAbsolutePath);
1633 KConfigGroup destinationContainments = KConfigGroup(destinationPtr, "Containments");
1634
1635 if (layout->isActive()) {
1636 //! update and copy containments
1637 auto containment = layout->containmentForId((uint)containmentId);
1638 syncContainmentConfig(containment);
1639
1640 KConfigGroup destinationViewContainment(&destinationContainments, QString::number(containment->id()));
1641 containment->config().copyTo(&destinationViewContainment);
1642
1643 QList<Plasma::Containment *> subconts = layout->subContainmentsOf(containment->id());
1644
1645 for(const auto subcont : subconts) {
1646 syncContainmentConfig(subcont);
1647 KConfigGroup destinationsubcontainment(&destinationContainments, QString::number(subcont->id()));
1648 subcont->config().copyTo(&destinationsubcontainment);
1649 }
1650
1651 //! update with latest view data if active view is present
1652 auto view = layout->viewForContainment(containment);
1653
1654 if (view) {
1655 Data::View currentviewdata = view->data();
1656 updateView(destinationViewContainment, currentviewdata);
1657 }
1658 } else {
1659 QString containmentid = QString::number(containmentId);
1660 KConfigGroup destinationViewContainment(&destinationContainments, containmentid);
1661
1662 KSharedConfigPtr originPtr = KSharedConfig::openConfig(layout->file());
1663 KConfigGroup originContainments = KConfigGroup(originPtr, "Containments");
1664
1665 originContainments.group(containmentid).copyTo(&destinationViewContainment);
1666
1667 Data::GenericTable<Data::Generic> subconts = subcontainments(originContainments.group(containmentid));
1668
1669 for(int i=0; i<subconts.rowCount(); ++i) {
1670 QString subid = subconts[i].id;
1671 KConfigGroup destinationsubcontainment(&destinationContainments, subid);
1672 originContainments.group(subid).copyTo(&destinationsubcontainment);
1673 }
1674 }
1675
1676 destinationPtr->reparseConfiguration();
1677 return nextTmpStoredViewAbsolutePath;
1678 }
1679
views(const Layout::GenericLayout * layout)1680 Data::ViewsTable Storage::views(const Layout::GenericLayout *layout)
1681 {
1682 Data::ViewsTable vtable;
1683
1684 if (!layout) {
1685 return vtable;
1686 } else if (!layout->isActive()) {
1687 return views(layout->file());
1688 }
1689
1690 for (const auto containment : (*layout->containments())) {
1691 if (!isLatteContainment(containment)) {
1692 continue;
1693 }
1694
1695 Latte::View *vw = layout->viewForContainment(containment);
1696
1697 if (vw) {
1698 vtable << vw->data();
1699 } else {
1700 vtable << view(layout, containment);
1701 }
1702 }
1703
1704 return vtable;
1705 }
1706
views(const QString & file)1707 Data::ViewsTable Storage::views(const QString &file)
1708 {
1709 Data::ViewsTable vtable;
1710
1711 KSharedConfigPtr lFile = KSharedConfig::openConfig(file);
1712 KConfigGroup containmentGroups = KConfigGroup(lFile, "Containments");
1713
1714 for (const auto &cId : containmentGroups.groupList()) {
1715 if (Layouts::Storage::self()->isLatteContainment(containmentGroups.group(cId))) {
1716 vtable << view(containmentGroups.group(cId));
1717 }
1718 }
1719
1720 return vtable;
1721 }
1722
1723 }
1724 }
1725