1
0
mirror of https://github.com/KDE/latte-dock.git synced 2024-12-24 17:33:50 +03:00
latte-dock/app/layouts/storage.cpp
2021-04-20 20:40:05 +03:00

1447 lines
47 KiB
C++

/*
* Copyright 2020 Michail Vourlakos <mvourlakos@gmail.com>
*
* This file is part of Latte-Dock
*
* Latte-Dock is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* Latte-Dock is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "storage.h"
// local
#include <coretypes.h>
#include "importer.h"
#include "manager.h"
#include "../lattecorona.h"
#include "../screenpool.h"
#include "../layout/abstractlayout.h"
#include "../view/view.h"
// Qt
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QFileInfo>
// KDE
#include <KConfigGroup>
#include <KPluginMetaData>
#include <KSharedConfig>
#include <KPackage/Package>
#include <KPackage/PackageLoader>
// Plasma
#include <Plasma>
#include <Plasma/Applet>
#include <Plasma/Containment>
namespace Latte {
namespace Layouts {
const int Storage::IDNULL = -1;
const int Storage::IDBASE = 0;
Storage::Storage()
{
qDebug() << " >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> LAYOUTS::STORAGE, TEMP DIR ::: " << m_storageTmpDir.path();
SubContaimentIdentityData data;
//! Systray
m_subIdentities << SubContaimentIdentityData{.cfgGroup="Configuration", .cfgProperty="SystrayContainmentId"};
//! Group applet
m_subIdentities << SubContaimentIdentityData{.cfgGroup="Configuration", .cfgProperty="ContainmentId"};
}
Storage::~Storage()
{
}
Storage *Storage::self()
{
static Storage store;
return &store;
}
bool Storage::isWritable(const Layout::GenericLayout *layout) const
{
QFileInfo layoutFileInfo(layout->file());
if (layoutFileInfo.exists() && !layoutFileInfo.isWritable()) {
return false;
} else {
return true;
}
}
bool Storage::isLatteContainment(const Plasma::Containment *containment) const
{
if (!containment) {
return false;
}
if (containment->pluginMetaData().pluginId() == "org.kde.latte.containment") {
return true;
}
return false;
}
bool Storage::isLatteContainment(const KConfigGroup &group) const
{
QString pluginId = group.readEntry("plugin", "");
return pluginId == "org.kde.latte.containment";
}
bool Storage::isSubContainment(const Layout::GenericLayout *layout, const Plasma::Applet *applet) const
{
if (!layout || !applet) {
return false;
}
for (const auto containment : *layout->containments()) {
Plasma::Applet *parentApplet = qobject_cast<Plasma::Applet *>(containment->parent());
if (parentApplet && parentApplet == applet) {
return true;
}
}
return false;
}
bool Storage::isSubContainment(const KConfigGroup &appletGroup) const
{
return isValid(subContainmentId(appletGroup));
}
bool Storage::isValid(const int &id)
{
return id >= IDBASE;
}
int Storage::subContainmentId(const KConfigGroup &appletGroup) const
{
//! cycle through subcontainments identities
for (auto subidentity : m_subIdentities) {
KConfigGroup appletConfigGroup = appletGroup;
if (!subidentity.cfgGroup.isEmpty()) {
//! if identity provides specific configuration group
if (appletConfigGroup.hasGroup(subidentity.cfgGroup)) {
appletConfigGroup = appletGroup.group(subidentity.cfgGroup);
}
}
if (!subidentity.cfgProperty.isEmpty()) {
//! if identity provides specific property for configuration group
if (appletConfigGroup.hasKey(subidentity.cfgProperty)) {
return appletConfigGroup.readEntry(subidentity.cfgProperty, IDNULL);
}
}
}
return IDNULL;
}
int Storage::subIdentityIndex(const KConfigGroup &appletGroup) const
{
if (!isSubContainment(appletGroup)) {
return IDNULL;
}
//! cycle through subcontainments identities
for (int i=0; i<m_subIdentities.count(); ++i) {
KConfigGroup appletConfigGroup = appletGroup;
if (!m_subIdentities[i].cfgGroup.isEmpty()) {
//! if identity provides specific configuration group
if (appletConfigGroup.hasGroup(m_subIdentities[i].cfgGroup)) {
appletConfigGroup = appletGroup.group(m_subIdentities[i].cfgGroup);
}
}
if (!m_subIdentities[i].cfgProperty.isEmpty()) {
//! if identity provides specific property for configuration group
if (appletConfigGroup.hasKey(m_subIdentities[i].cfgProperty)) {
int subId = appletConfigGroup.readEntry(m_subIdentities[i].cfgProperty, IDNULL);
return isValid(subId) ? i : IDNULL;
}
}
}
return IDNULL;
}
Plasma::Containment *Storage::subContainmentOf(const Layout::GenericLayout *layout, const Plasma::Applet *applet)
{
if (!layout || !applet) {
return nullptr;
}
if (isSubContainment(layout, applet)) {
for (const auto containment : *layout->containments()) {
Plasma::Applet *parentApplet = qobject_cast<Plasma::Applet *>(containment->parent());
if (parentApplet && parentApplet == applet) {
return containment;
}
}
}
return nullptr;
}
void Storage::lock(const Layout::GenericLayout *layout)
{
QFileInfo layoutFileInfo(layout->file());
if (layoutFileInfo.exists() && layoutFileInfo.isWritable()) {
QFile(layout->file()).setPermissions(QFileDevice::ReadUser | QFileDevice::ReadGroup | QFileDevice::ReadOther);
}
}
void Storage::unlock(const Layout::GenericLayout *layout)
{
QFileInfo layoutFileInfo(layout->file());
if (layoutFileInfo.exists() && !layoutFileInfo.isWritable()) {
QFile(layout->file()).setPermissions(QFileDevice::ReadUser | QFileDevice::WriteUser | QFileDevice::ReadGroup | QFileDevice::ReadOther);
}
}
void Storage::importToCorona(const Layout::GenericLayout *layout)
{
if (!layout->corona()) {
return;
}
//! Setting mutable for create a containment
layout->corona()->setImmutability(Plasma::Types::Mutable);
QString temp1FilePath = m_storageTmpDir.path() + "/" + layout->name() + ".multiple.views";
//! we need to copy first the layout file because the kde cache
//! may not have yet been updated (KSharedConfigPtr)
//! this way we make sure at the latest changes stored in the layout file
//! will be also available when changing to Multiple Layouts
QString tempLayoutFilePath = m_storageTmpDir.path() + "/" + layout->name() + ".multiple.tmplayout";
//! WE NEED A WAY TO COPY A CONTAINMENT!!!!
QFile tempLayoutFile(tempLayoutFilePath);
QFile copyFile(temp1FilePath);
QFile layoutOriginalFile(layout->file());
if (tempLayoutFile.exists()) {
tempLayoutFile.remove();
}
if (copyFile.exists())
copyFile.remove();
layoutOriginalFile.copy(tempLayoutFilePath);
KSharedConfigPtr filePtr = KSharedConfig::openConfig(tempLayoutFilePath);
KSharedConfigPtr newFile = KSharedConfig::openConfig(temp1FilePath);
KConfigGroup copyGroup = KConfigGroup(newFile, "Containments");
KConfigGroup current_containments = KConfigGroup(filePtr, "Containments");
current_containments.copyTo(&copyGroup);
copyGroup.sync();
//! update ids to unique ones
QString temp2File = newUniqueIdsFile(temp1FilePath, layout);
//! Finally import the configuration
importLayoutFile(layout, temp2File);
}
QString Storage::availableId(QStringList all, QStringList assigned, int base)
{
bool found = false;
int i = base;
while (!found && i < 32000) {
QString iStr = QString::number(i);
if (!all.contains(iStr) && !assigned.contains(iStr)) {
return iStr;
}
i++;
}
return QString("");
}
bool Storage::appletGroupIsValid(const KConfigGroup &appletGroup)
{
return !( appletGroup.keyList().count() == 0
&& appletGroup.groupList().count() == 1
&& appletGroup.groupList().at(0) == "Configuration"
&& appletGroup.group("Configuration").keyList().count() == 1
&& appletGroup.group("Configuration").hasKey("PreloadWeight") );
}
QStringList Storage::containmentsIds(const QString &filepath)
{
QStringList ids;
KSharedConfigPtr filePtr = KSharedConfig::openConfig(filepath);
KConfigGroup containments = KConfigGroup(filePtr, "Containments");
for(const auto &cId : containments.groupList()) {
ids << cId;
}
return ids;
}
QStringList Storage::appletsIds(const QString &filepath)
{
QStringList ids;
KSharedConfigPtr filePtr = KSharedConfig::openConfig(filepath);
KConfigGroup containments = KConfigGroup(filePtr, "Containments");
for(const auto &cId : containments.groupList()) {
for(const auto &aId : containments.group(cId).group("Applets").groupList()) {
ids << aId;
}
}
return ids;
}
QString Storage::newUniqueIdsFile(QString originFile, const Layout::GenericLayout *destinationLayout)
{
if (!destinationLayout) {
return QString();
}
QString currentdestinationname = destinationLayout->name();
QString currentdestinationfile = "";
if (!destinationLayout->isActive()) {
currentdestinationfile = destinationLayout->file();
}
QString tempFile = m_storageTmpDir.path() + "/" + currentdestinationname + ".views.newids";
QFile copyFile(tempFile);
if (copyFile.exists()) {
copyFile.remove();
}
//! BEGIN updating the ids in the temp file
QStringList allIds;
if (destinationLayout->isActive()) {
allIds << destinationLayout->corona()->containmentsIds();
allIds << destinationLayout->corona()->appletsIds();
} else {
allIds << containmentsIds(currentdestinationfile);
allIds << appletsIds(currentdestinationfile);
}
QStringList toInvestigateContainmentIds;
QStringList toInvestigateAppletIds;
QStringList toInvestigateSubContIds;
//! first is the subcontainment id
QHash<QString, QString> subParentContainmentIds;
QHash<QString, QString> subAppletIds;
//qDebug() << "Ids:" << allIds;
//qDebug() << "to copy containments: " << toCopyContainmentIds;
//qDebug() << "to copy applets: " << toCopyAppletIds;
QStringList assignedIds;
QHash<QString, QString> assigned;
KSharedConfigPtr filePtr = KSharedConfig::openConfig(originFile);
KConfigGroup investigate_conts = KConfigGroup(filePtr, "Containments");
//! Record the containment and applet ids
for (const auto &cId : investigate_conts.groupList()) {
toInvestigateContainmentIds << cId;
auto appletsEntries = investigate_conts.group(cId).group("Applets");
toInvestigateAppletIds << appletsEntries.groupList();
//! investigate for subcontainments
for (const auto &appletId : appletsEntries.groupList()) {
int subId = subContainmentId(appletsEntries.group(appletId));
//! It is a subcontainment !!!
if (isValid(subId)) {
QString tSubIdStr = QString::number(subId);
toInvestigateSubContIds << tSubIdStr;
subParentContainmentIds[tSubIdStr] = cId;
subAppletIds[tSubIdStr] = appletId;
qDebug() << "subcontainment was found in the containment...";
}
}
}
//! Reassign containment and applet ids to unique ones
for (const auto &contId : toInvestigateContainmentIds) {
QString newId;
if (contId.toInt()>=12 && !allIds.contains(contId) && !assignedIds.contains(contId)) {
newId = contId;
} else {
newId = availableId(allIds, assignedIds, 12);
}
assignedIds << newId;
assigned[contId] = newId;
}
for (const auto &appId : toInvestigateAppletIds) {
QString newId;
if (appId.toInt()>=40 && !allIds.contains(appId) && !assignedIds.contains(appId)) {
newId = appId;
} else {
newId = availableId(allIds, assignedIds, 40);
}
assignedIds << newId;
assigned[appId] = newId;
}
qDebug() << "ALL CORONA IDS ::: " << allIds;
qDebug() << "FULL ASSIGNMENTS ::: " << assigned;
for (const auto &cId : toInvestigateContainmentIds) {
QString value = assigned[cId];
if (assigned.contains(value)) {
QString value2 = assigned[value];
if (cId != assigned[cId] && !value2.isEmpty() && cId == value2) {
qDebug() << "PROBLEM APPEARED !!!! FOR :::: " << cId << " .. fixed ..";
assigned[cId] = cId;
assigned[value] = value;
}
}
}
for (const auto &aId : toInvestigateAppletIds) {
QString value = assigned[aId];
if (assigned.contains(value)) {
QString value2 = assigned[value];
if (aId != assigned[aId] && !value2.isEmpty() && aId == value2) {
qDebug() << "PROBLEM APPEARED !!!! FOR :::: " << aId << " .. fixed ..";
assigned[aId] = aId;
assigned[value] = value;
}
}
}
qDebug() << "FIXED FULL ASSIGNMENTS ::: " << assigned;
//! update applet ids in their containment order and in MultipleLayouts update also the layoutId
for (const auto &cId : investigate_conts.groupList()) {
//! Update options that contain applet ids
//! (appletOrder) and (lockedZoomApplets) and (userBlocksColorizingApplets)
QStringList options;
options << "appletOrder" << "lockedZoomApplets" << "userBlocksColorizingApplets";
for (const auto &settingStr : options) {
QString order1 = investigate_conts.group(cId).group("General").readEntry(settingStr, QString());
if (!order1.isEmpty()) {
QStringList order1Ids = order1.split(";");
QStringList fixedOrder1Ids;
for (int i = 0; i < order1Ids.count(); ++i) {
fixedOrder1Ids.append(assigned[order1Ids[i]]);
}
QString fixedOrder1 = fixedOrder1Ids.join(";");
investigate_conts.group(cId).group("General").writeEntry(settingStr, fixedOrder1);
}
}
if (destinationLayout->isActive() && destinationLayout->corona()->layoutsManager()->memoryUsage() == MemoryUsage::MultipleLayouts) {
//! will be added in main corona multiple layouts file
investigate_conts.group(cId).writeEntry("layoutId", destinationLayout->name());
} else {
//! will be added in inactive layout
investigate_conts.group(cId).writeEntry("layoutId", QString());
}
}
//! must update also the sub id in its applet
for (const auto &subId : toInvestigateSubContIds) {
KConfigGroup subParentContainment = investigate_conts.group(subParentContainmentIds[subId]);
KConfigGroup subAppletConfig = subParentContainment.group("Applets").group(subAppletIds[subId]);
int entityIndex = subIdentityIndex(subAppletConfig);
if (entityIndex >= 0) {
if (!m_subIdentities[entityIndex].cfgGroup.isEmpty()) {
subAppletConfig = subAppletConfig.group(m_subIdentities[entityIndex].cfgGroup);
}
if (!m_subIdentities[entityIndex].cfgProperty.isEmpty()) {
subAppletConfig.writeEntry(m_subIdentities[entityIndex].cfgProperty, assigned[subId]);
subParentContainment.sync();
}
}
}
investigate_conts.sync();
//! Copy To Temp 2 File And Update Correctly The Ids
KSharedConfigPtr file2Ptr = KSharedConfig::openConfig(tempFile);
KConfigGroup fixedNewContainmets = KConfigGroup(file2Ptr, "Containments");
for (const auto &contId : investigate_conts.groupList()) {
QString pluginId = investigate_conts.group(contId).readEntry("plugin", "");
if (pluginId != "org.kde.desktopcontainment") { //!don't add ghost containments
KConfigGroup newContainmentGroup = fixedNewContainmets.group(assigned[contId]);
investigate_conts.group(contId).copyTo(&newContainmentGroup);
newContainmentGroup.group("Applets").deleteGroup();
for (const auto &appId : investigate_conts.group(contId).group("Applets").groupList()) {
KConfigGroup appletGroup = investigate_conts.group(contId).group("Applets").group(appId);
KConfigGroup newAppletGroup = fixedNewContainmets.group(assigned[contId]).group("Applets").group(assigned[appId]);
appletGroup.copyTo(&newAppletGroup);
}
}
}
fixedNewContainmets.sync();
return tempFile;
}
void Storage::syncToLayoutFile(const Layout::GenericLayout *layout, bool removeLayoutId)
{
if (!layout->corona() || !isWritable(layout)) {
return;
}
KSharedConfigPtr filePtr = KSharedConfig::openConfig(layout->file());
KConfigGroup oldContainments = KConfigGroup(filePtr, "Containments");
oldContainments.deleteGroup();
qDebug() << " LAYOUT :: " << layout->name() << " is syncing its original file.";
for (const auto containment : *layout->containments()) {
if (removeLayoutId) {
containment->config().writeEntry("layoutId", "");
}
KConfigGroup newGroup = oldContainments.group(QString::number(containment->id()));
containment->config().copyTo(&newGroup);
if (!removeLayoutId) {
newGroup.writeEntry("layoutId", "");
newGroup.sync();
}
}
oldContainments.sync();
}
QList<Plasma::Containment *> Storage::importLayoutFile(const Layout::GenericLayout *layout, QString file)
{
KSharedConfigPtr filePtr = KSharedConfig::openConfig(file);
auto newContainments = layout->corona()->importLayout(KConfigGroup(filePtr, ""));
qDebug() << " imported containments ::: " << newContainments.length();
QList<Plasma::Containment *> importedViews;
for (const auto containment : newContainments) {
if (isLatteContainment(containment)) {
qDebug() << "new latte containment id: " << containment->id();
importedViews << containment;
}
}
return importedViews;
}
void Storage::importContainments(const QString &originFile, const QString &destinationFile)
{
if (originFile.isEmpty() || destinationFile.isEmpty()) {
return;
}
KSharedConfigPtr originPtr = KSharedConfig::openConfig(originFile);
KSharedConfigPtr destinationPtr = KSharedConfig::openConfig(destinationFile);
KConfigGroup originContainments = KConfigGroup(originPtr, "Containments");
KConfigGroup destinationContainments = KConfigGroup(destinationPtr, "Containments");
for (const auto originContId : originContainments.groupList()) {
KConfigGroup destinationContainment(&destinationContainments, originContId);
originContainments.group(originContId).copyTo(&destinationContainment);
}
destinationContainments.sync();
}
Data::View Storage::newView(const Layout::GenericLayout *destinationLayout, const Data::View &nextViewData)
{
if (!destinationLayout || nextViewData.originFile().isEmpty()) {
return Data::View();
}
qDebug() << "new view for layout";
if (destinationLayout->isActive()) {
//! Setting mutable for create a containment
destinationLayout->corona()->setImmutability(Plasma::Types::Mutable);
}
QString templateFile = nextViewData.originFile();
//! copy view template path in temp file
QString templateTmpAbsolutePath = m_storageTmpDir.path() + "/" + QFileInfo(templateFile).fileName() + ".newids";
if (QFile(templateTmpAbsolutePath).exists()) {
QFile(templateTmpAbsolutePath).remove();
}
QFile(templateFile).copy(templateTmpAbsolutePath);
//! update ids to unique ones
QString temp2File = newUniqueIdsFile(templateTmpAbsolutePath, destinationLayout);
//! update view containment data in case next data are provided
if (nextViewData.state() != Data::View::IsInvalid) {
KSharedConfigPtr lFile = KSharedConfig::openConfig(temp2File);
KConfigGroup containments = KConfigGroup(lFile, "Containments");
for (const auto cId : containments.groupList()) {
if (Layouts::Storage::self()->isLatteContainment(containments.group(cId))) {
//! first view we will find, we update its value
updateView(containments.group(cId), nextViewData);
break;
}
}
}
Data::ViewsTable updatedNextViews = views(temp2File);
if (updatedNextViews.rowCount() <= 0) {
return Data::View();
}
if (destinationLayout->isActive()) {
//! import views for active layout
QList<Plasma::Containment *> importedViews = importLayoutFile(destinationLayout, temp2File);
Plasma::Containment *newContainment = (importedViews.size() == 1 ? importedViews[0] : nullptr);
if (!newContainment || !newContainment->kPackage().isValid()) {
qWarning() << "the requested containment plugin can not be located or loaded from:" << templateFile;
return Data::View();
}
} else {
//! import views for inactive layout
importContainments(temp2File, destinationLayout->file());
}
return updatedNextViews[0];
}
void Storage::clearExportedLayoutSettings(KConfigGroup &layoutSettingsGroup)
{
layoutSettingsGroup.writeEntry("preferredForShortcutsTouched", false);
layoutSettingsGroup.writeEntry("lastUsedActivity", QString());
layoutSettingsGroup.writeEntry("activities", QStringList());
layoutSettingsGroup.sync();
}
bool Storage::exportTemplate(const QString &originFile, const QString &destinationFile,const Data::AppletsTable &approvedApplets)
{
if (originFile.isEmpty() || !QFile(originFile).exists() || destinationFile.isEmpty()) {
return false;
}
if (QFile(destinationFile).exists()) {
QFile::remove(destinationFile);
}
QFile(originFile).copy(destinationFile);
KSharedConfigPtr destFilePtr = KSharedConfig::openConfig(destinationFile);
KConfigGroup containments = KConfigGroup(destFilePtr, "Containments");
QStringList rejectedSubContainments;
//! clear applets that are not approved
for (const auto &cId : containments.groupList()) {
//! clear properties
containments.group(cId).writeEntry("layoutId", QString());
if (isLatteContainment(containments.group(cId))) {
containments.group(cId).writeEntry("isPreferredForShortcuts", false);
}
//! clear applets
auto applets = containments.group(cId).group("Applets");
for (const auto &aId: applets.groupList()) {
QString pluginId = applets.group(aId).readEntry("plugin", "");
if (!approvedApplets.containsId(pluginId)) {
if (!isSubContainment(applets.group(aId))) {
//!remove all configuration for that applet
for (const auto &configId: applets.group(aId).groupList()) {
applets.group(aId).group(configId).deleteGroup();
}
} else {
//! register which subcontaiments should return to default properties
rejectedSubContainments << QString::number(subContainmentId(applets.group(aId)));
}
}
}
}
//! clear rejected SubContainments
for (const auto &cId : containments.groupList()) {
if (rejectedSubContainments.contains(cId)) {
containments.group(cId).group("General").deleteGroup();
}
};
KConfigGroup layoutSettingsGrp(destFilePtr, "LayoutSettings");
clearExportedLayoutSettings(layoutSettingsGrp);
containments.sync();
return true;
}
bool Storage::exportTemplate(const Layout::GenericLayout *layout, Plasma::Containment *containment, const QString &destinationFile, const Data::AppletsTable &approvedApplets)
{
if (!layout || !containment || destinationFile.isEmpty()) {
return false;
}
if (QFile(destinationFile).exists()) {
QFile::remove(destinationFile);
}
KSharedConfigPtr destFilePtr = KSharedConfig::openConfig(destinationFile);
KConfigGroup copied_conts = KConfigGroup(destFilePtr, "Containments");
KConfigGroup copied_c1 = KConfigGroup(&copied_conts, QString::number(containment->id()));
containment->config().copyTo(&copied_c1);
//!investigate if there are subcontainments in the containment to copy also
//! subId, subAppletId
QHash<uint, QString> subInfo;
auto applets = containment->config().group("Applets");
for (const auto &applet : applets.groupList()) {
int tSubId = subContainmentId(applets.group(applet));
//! It is a subcontainment !!!
if (isValid(tSubId)) {
subInfo[tSubId] = applet;
qDebug() << "subcontainment with id "<< tSubId << " was found in the containment... ::: " << containment->id();
}
}
if (subInfo.count() > 0) {
for(const auto subId : subInfo.keys()) {
Plasma::Containment *subcontainment{nullptr};
for (const auto containment : layout->corona()->containments()) {
if (containment->id() == subId) {
subcontainment = containment;
break;
}
}
if (subcontainment) {
KConfigGroup copied_sub = KConfigGroup(&copied_conts, QString::number(subcontainment->id()));
subcontainment->config().copyTo(&copied_sub);
}
}
}
//! end of subcontainments specific code
QStringList rejectedSubContainments;
//! clear applets that are not approved
for (const auto &cId : copied_conts.groupList()) {
//! clear properties
copied_conts.group(cId).writeEntry("layoutId", QString());
if (isLatteContainment(copied_conts.group(cId))) {
copied_conts.group(cId).writeEntry("isPreferredForShortcuts", false);
}
//! clear applets
auto applets = copied_conts.group(cId).group("Applets");
for (const auto &aId: applets.groupList()) {
QString pluginId = applets.group(aId).readEntry("plugin", "");
if (!approvedApplets.containsId(pluginId)) {
if (!isSubContainment(applets.group(aId))) {
//!remove all configuration for that applet
for (const auto &configId: applets.group(aId).groupList()) {
applets.group(aId).group(configId).deleteGroup();
}
} else {
//! register which subcontaiments should return to default properties
rejectedSubContainments << QString::number(subContainmentId(applets.group(aId)));
}
}
}
}
//! clear rejected SubContainments
for (const auto &cId : copied_conts.groupList()) {
if (rejectedSubContainments.contains(cId)) {
copied_conts.group(cId).group("General").deleteGroup();
}
};
KConfigGroup layoutSettingsGrp(destFilePtr, "LayoutSettings");
clearExportedLayoutSettings(layoutSettingsGrp);
copied_conts.sync();
return true;
}
bool Storage::isBroken(const Layout::GenericLayout *layout, QStringList &errors) const
{
if (layout->file().isEmpty() || !QFile(layout->file()).exists()) {
return false;
}
QStringList ids;
QStringList conts;
QStringList applets;
KSharedConfigPtr lFile = KSharedConfig::openConfig(layout->file());
if (!layout->corona()) {
KConfigGroup containmentsEntries = KConfigGroup(lFile, "Containments");
ids << containmentsEntries.groupList();
conts << ids;
for (const auto &cId : containmentsEntries.groupList()) {
auto appletsEntries = containmentsEntries.group(cId).group("Applets");
QStringList validAppletIds;
bool updated{false};
for (const auto &appletId : appletsEntries.groupList()) {
KConfigGroup appletGroup = appletsEntries.group(appletId);
if (Layouts::Storage::appletGroupIsValid(appletGroup)) {
validAppletIds << appletId;
} else {
updated = true;
//! heal layout file by removing applet config records that are not used any more
qDebug() << "Layout: " << layout->name() << " removing deprecated applet : " << appletId;
appletsEntries.deleteGroup(appletId);
}
}
if (updated) {
appletsEntries.sync();
}
ids << validAppletIds;
applets << validAppletIds;
}
} else {
for (const auto containment : *layout->containments()) {
ids << QString::number(containment->id());
conts << QString::number(containment->id());
for (const auto applet : containment->applets()) {
ids << QString::number(applet->id());
applets << QString::number(applet->id());
}
}
}
#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
QSet<QString> idsSet = QSet<QString>::fromList(ids);
#else
QSet<QString> idsSet(ids.begin(), ids.end());
#endif
/* a different way to count duplicates
QMap<QString, int> countOfStrings;
for (int i = 0; i < ids.count(); i++) {
countOfStrings[ids[i]]++;
}*/
if (idsSet.count() != ids.count()) {
qDebug() << " ---- ERROR - BROKEN LAYOUT :: " << layout->name() << " ----";
if (!layout->corona()) {
qDebug() << " --- storaged file : " << layout->file();
} else {
if (layout->corona()->layoutsManager()->memoryUsage() == MemoryUsage::MultipleLayouts) {
qDebug() << " --- in multiple layouts hidden file : " << Layouts::Importer::layoutUserFilePath(Layout::MULTIPLELAYOUTSHIDDENNAME);
} else {
qDebug() << " --- in active layout file : " << layout->file();
}
}
qDebug() << "Containments :: " << conts;
qDebug() << "Applets :: " << applets;
for (const QString &c : conts) {
if (applets.contains(c)) {
QString errorStr = i18n("Same applet and containment id found ::: ") + c;
qDebug() << "Error: " << errorStr;
errors << errorStr;
}
}
for (int i = 0; i < ids.count(); ++i) {
for (int j = i + 1; j < ids.count(); ++j) {
if (ids[i] == ids[j]) {
QString errorStr = i18n("Different applets with same id ::: ") + ids[i];
qDebug() << "Error: " << errorStr;
errors << errorStr;
}
}
}
qDebug() << " -- - -- - -- - -- - - -- - - - - -- - - - - ";
if (!layout->corona()) {
KConfigGroup containmentsEntries = KConfigGroup(lFile, "Containments");
for (const auto &cId : containmentsEntries.groupList()) {
auto appletsEntries = containmentsEntries.group(cId).group("Applets");
qDebug() << " CONTAINMENT : " << cId << " APPLETS : " << appletsEntries.groupList();
}
} else {
for (const auto containment : *layout->containments()) {
QStringList appletsIds;
for (const auto applet : containment->applets()) {
appletsIds << QString::number(applet->id());
}
qDebug() << " CONTAINMENT : " << containment->id() << " APPLETS : " << appletsIds.join(",");
}
}
return true;
}
return false;
}
//! AppletsData Information
Data::Applet Storage::metadata(const QString &pluginId)
{
Data::Applet data;
data.id = pluginId;
KPackage::Package pkg = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/Applet"));
pkg.setDefaultPackageRoot(QStringLiteral("plasma/plasmoids"));
pkg.setPath(pluginId);
if (pkg.isValid()) {
data.name = pkg.metadata().name();
data.description = pkg.metadata().description();
QString iconName = pkg.metadata().iconName();
if (!iconName.startsWith("/") && iconName.contains("/")) {
data.icon = QFileInfo(pkg.metadata().fileName()).absolutePath() + "/" + iconName;
} else {
data.icon = iconName;
}
}
if (data.name.isEmpty()) {
//! this is also a way to identify if a package is installed or not in current system
data.name = data.id;
}
return data;
}
Data::AppletsTable Storage::plugins(const Layout::GenericLayout *layout, const int containmentid)
{
Data::AppletsTable knownapplets;
Data::AppletsTable unknownapplets;
if (!layout) {
return knownapplets;
}
//! empty means all containments are valid
QList<int> validcontainmentids;
if (isValid(containmentid)) {
validcontainmentids << containmentid;
//! searching for specific containment and subcontainments and ignore all other containments
for(auto containment : *layout->containments()) {
if (((int)containment->id()) != containmentid) {
//! ignore irrelevant containments
continue;
}
for (auto applet : containment->applets()) {
if (isSubContainment(layout, applet)) {
validcontainmentids << subContainmentId(applet->config());
}
}
}
}
//! cycle through valid contaiments in order to retrieve their metadata
for(auto containment : *layout->containments()) {
if (validcontainmentids.count()>0 && !validcontainmentids.contains(containment->id())) {
//! searching only for valid containments
continue;
}
for (auto applet : containment->applets()) {
QString pluginId = applet->pluginMetaData().pluginId();
if (!knownapplets.containsId(pluginId) && !unknownapplets.containsId(pluginId)) {
Data::Applet appletdata = metadata(pluginId);
if (appletdata.isInstalled()) {
knownapplets.insertBasedOnName(appletdata);
} else if (appletdata.isValid()) {
unknownapplets.insertBasedOnName(appletdata);
}
}
}
}
knownapplets << unknownapplets;
return knownapplets;
}
Data::AppletsTable Storage::plugins(const QString &layoutfile, const int containmentid)
{
Data::AppletsTable knownapplets;
Data::AppletsTable unknownapplets;
if (layoutfile.isEmpty()) {
return knownapplets;
}
KSharedConfigPtr lFile = KSharedConfig::openConfig(layoutfile);
KConfigGroup containmentGroups = KConfigGroup(lFile, "Containments");
//! empty means all containments are valid
QList<int> validcontainmentids;
if (isValid(containmentid)) {
validcontainmentids << containmentid;
//! searching for specific containment and subcontainments and ignore all other containments
for (const auto &cId : containmentGroups.groupList()) {
if (cId.toInt() != containmentid) {
//! ignore irrelevant containments
continue;
}
auto appletGroups = containmentGroups.group(cId).group("Applets");
for (const auto &appletId : appletGroups.groupList()) {
KConfigGroup appletCfg = appletGroups.group(appletId);
if (isSubContainment(appletCfg)) {
validcontainmentids << subContainmentId(appletCfg);
}
}
}
}
//! cycle through valid contaiments in order to retrieve their metadata
for (const auto &cId : containmentGroups.groupList()) {
if (validcontainmentids.count()>0 && !validcontainmentids.contains(cId.toInt())) {
//! searching only for valid containments
continue;
}
auto appletGroups = containmentGroups.group(cId).group("Applets");
for (const auto &appletId : appletGroups.groupList()) {
KConfigGroup appletCfg = appletGroups.group(appletId);
QString pluginId = appletCfg.readEntry("plugin", "");
if (!knownapplets.containsId(pluginId) && !unknownapplets.containsId(pluginId)) {
Data::Applet appletdata = metadata(pluginId);
if (appletdata.isInstalled()) {
knownapplets.insertBasedOnName(appletdata);
} else if (appletdata.isValid()) {
unknownapplets.insertBasedOnName(appletdata);
}
}
}
}
knownapplets << unknownapplets;
return knownapplets;
}
//! Views Data
void Storage::syncContainmentConfig(Plasma::Containment *containment)
{
if (!containment) {
return;
}
for(auto applet: containment->applets()) {
KConfigGroup appletGeneralConfig = applet->config().group("General");
if (appletGeneralConfig.exists()) {
appletGeneralConfig.sync();
}
applet->config().sync();
}
containment->config().sync();
}
bool Storage::containsView(const QString &filepath, const int &viewId)
{
KSharedConfigPtr lFile = KSharedConfig::openConfig(filepath);
KConfigGroup containmentGroups = KConfigGroup(lFile, "Containments");
KConfigGroup viewGroup = containmentGroups.group(QString::number(viewId));
return viewGroup.exists() && isLatteContainment(viewGroup);
}
Data::GenericTable<Data::Generic> Storage::subcontainments(const Layout::GenericLayout *layout, const Plasma::Containment *lattecontainment) const
{
Data::GenericTable<Data::Generic> subs;
if (!layout || !Layouts::Storage::self()->isLatteContainment(lattecontainment)) {
return subs;
}
for (const auto containment : (*layout->containments())) {
if (containment == lattecontainment) {
continue;
}
Plasma::Applet *parentApplet = qobject_cast<Plasma::Applet *>(containment->parent());
//! add subcontainments for that lattecontainment
if (parentApplet && parentApplet->containment() && parentApplet->containment() == lattecontainment) {
Data::Generic subdata;
subdata.id = QString::number(containment->id());
subs << subdata;
}
}
return subs;
}
Data::GenericTable<Data::Generic> Storage::subcontainments(const KConfigGroup &containmentGroup)
{
Data::GenericTable<Data::Generic> subs;
if (!Layouts::Storage::self()->isLatteContainment(containmentGroup)) {
return subs;
}
auto applets = containmentGroup.group("Applets");
for (const auto &applet : applets.groupList()) {
if (isSubContainment(applets.group(applet))) {
Data::Generic subdata;
subdata.id = QString::number(subContainmentId(applets.group(applet)));
subs << subdata;
}
}
return subs;
}
Data::View Storage::view(const Layout::GenericLayout *layout, const Plasma::Containment *lattecontainment)
{
Data::View vdata;
if (!layout || !Layouts::Storage::self()->isLatteContainment(lattecontainment)) {
return vdata;
}
vdata = view(lattecontainment->config());
vdata.screen = lattecontainment->screen();
if (!isValid(vdata.screen)) {
vdata.screen = lattecontainment->lastScreen();
}
vdata.subcontainments = subcontainments(layout, lattecontainment);
return vdata;
}
Data::View Storage::view(const KConfigGroup &containmentGroup)
{
Data::View vdata;
if (!Layouts::Storage::self()->isLatteContainment(containmentGroup)) {
return vdata;
}
vdata.id = containmentGroup.name();
vdata.name = containmentGroup.readEntry("name", QString());
vdata.isActive = false;
vdata.onPrimary = containmentGroup.readEntry("onPrimary", true);
vdata.screen = containmentGroup.readEntry("lastScreen", IDNULL);
vdata.screenEdgeMargin = containmentGroup.group("General").readEntry("screenEdgeMargin", (int)0);
int location = containmentGroup.readEntry("location", (int)Plasma::Types::BottomEdge);
vdata.edge = (Plasma::Types::Location)location;
vdata.maxLength = containmentGroup.group("General").readEntry("maxLength", (float)100.0);
int alignment = containmentGroup.group("General").readEntry("alignment", (int)Latte::Types::Center) ;
vdata.alignment = (Latte::Types::Alignment)alignment;
vdata.subcontainments = subcontainments(containmentGroup);
vdata.setState(Data::View::IsCreated);
return vdata;
}
void Storage::updateView(KConfigGroup viewGroup, const Data::View &viewData)
{
if (!Layouts::Storage::self()->isLatteContainment(viewGroup)) {
return;
}
viewGroup.writeEntry("name", viewData.name);
viewGroup.writeEntry("onPrimary", viewData.onPrimary);
viewGroup.writeEntry("lastScreen", viewData.screen);
viewGroup.group("General").writeEntry("screenEdgeMargin", viewData.screenEdgeMargin);
viewGroup.writeEntry("location", (int)viewData.edge);
viewGroup.writeEntry("maxLength", viewData.maxLength);
viewGroup.group("General").writeEntry("alignment", (int)viewData.alignment);
viewGroup.sync();
}
void Storage::updateView(const Layout::GenericLayout *layout, const Data::View &viewData)
{
if (!layout) {
return;
}
auto view = layout->viewForContainment(viewData.id.toUInt());
if (view) {
qDebug() << "Storage::updateView should not be called because view is active and present...";
return;
}
if (layout->isActive()) {
//! active view but is not present in active screens;
auto containment = layout->containmentForId(viewData.id.toUInt());
if (containment) {
//! update containment
containment->setLocation(viewData.edge);
updateView(containment->config(), viewData);
}
} else {
//! inactive view and in layout storage
KSharedConfigPtr lFile = KSharedConfig::openConfig(layout->file());
KConfigGroup containmentGroups = KConfigGroup(lFile, "Containments");
KConfigGroup viewContainment = containmentGroups.group(viewData.id);
if (viewContainment.exists() && Layouts::Storage::self()->isLatteContainment(viewContainment)) {
updateView(viewContainment, viewData);
}
}
}
void Storage::removeView(const QString &filepath, const Data::View &viewData)
{
if (!viewData.isValid()) {
return;
}
KSharedConfigPtr lFile = KSharedConfig::openConfig(filepath);
KConfigGroup containmentGroups = KConfigGroup(lFile, "Containments");
containmentGroups.group(viewData.id).deleteGroup();
for (int i=0; i<viewData.subcontainments.rowCount(); ++i) {
containmentGroups.group(viewData.subcontainments[i].id).deleteGroup();
}
containmentGroups.sync();
}
QString Storage::storedView(const Layout::GenericLayout *layout, const int &containmentId)
{
//! make sure that layout and containmentId are valid
if (!layout) {
return QString();
}
if (layout->isActive()) {
auto containment = layout->containmentForId((uint)containmentId);
if (!containment || !isLatteContainment(containment)) {
return QString();
}
} else {
if (!containsView(layout->file(), containmentId)) {
return QString();
}
}
//! at this point we are sure that both layout and containmentId are acceptable
QString nextTmpStoredViewAbsolutePath = m_storageTmpDir.path() + "/" + QFileInfo(layout->name()).fileName() + "." + QString::number(containmentId) + ".stored.tmp";
QFile tempStoredViewFile(nextTmpStoredViewAbsolutePath);
if (tempStoredViewFile.exists()) {
tempStoredViewFile.remove();
}
KSharedConfigPtr destinationPtr = KSharedConfig::openConfig(nextTmpStoredViewAbsolutePath);
KConfigGroup destinationContainments = KConfigGroup(destinationPtr, "Containments");
if (layout->isActive()) {
//! update and copy containments
auto containment = layout->containmentForId((uint)containmentId);
syncContainmentConfig(containment);
KConfigGroup destinationViewContainment(&destinationContainments, QString::number(containment->id()));
containment->config().copyTo(&destinationViewContainment);
QList<Plasma::Containment *> subconts = layout->subContainmentsOf(containment->id());
for(const auto subcont : subconts) {
syncContainmentConfig(subcont);
KConfigGroup destinationsubcontainment(&destinationContainments, QString::number(subcont->id()));
subcont->config().copyTo(&destinationsubcontainment);
}
//! update with latest view data if active view is present
auto view = layout->viewForContainment(containment);
if (view) {
Data::View currentviewdata = view->data();
updateView(destinationViewContainment, currentviewdata);
}
} else {
QString containmentid = QString::number(containmentId);
KConfigGroup destinationViewContainment(&destinationContainments, containmentid);
KSharedConfigPtr originPtr = KSharedConfig::openConfig(layout->file());
KConfigGroup originContainments = KConfigGroup(originPtr, "Containments");
originContainments.group(containmentid).copyTo(&destinationViewContainment);
Data::GenericTable<Data::Generic> subconts = subcontainments(originContainments.group(containmentid));
for(int i=0; i<subconts.rowCount(); ++i) {
QString subid = subconts[i].id;
KConfigGroup destinationsubcontainment(&destinationContainments, subid);
originContainments.group(subid).copyTo(&destinationsubcontainment);
}
}
destinationContainments.sync();
return nextTmpStoredViewAbsolutePath;
}
Data::ViewsTable Storage::views(const Layout::GenericLayout *layout)
{
Data::ViewsTable vtable;
if (!layout) {
return vtable;
} else if (!layout->isActive()) {
return views(layout->file());
}
for (const auto containment : (*layout->containments())) {
if (!isLatteContainment(containment)) {
continue;
}
Latte::View *vw = layout->viewForContainment(containment);
if (vw) {
vtable << vw->data();
} else {
vtable << view(layout, containment);
}
}
return vtable;
}
Data::ViewsTable Storage::views(const QString &file)
{
Data::ViewsTable vtable;
KSharedConfigPtr lFile = KSharedConfig::openConfig(file);
KConfigGroup containmentGroups = KConfigGroup(lFile, "Containments");
for (const auto &cId : containmentGroups.groupList()) {
if (Layouts::Storage::self()->isLatteContainment(containmentGroups.group(cId))) {
vtable << view(containmentGroups.group(cId));
}
}
return vtable;
}
QList<int> Storage::viewsScreens(const QString &file)
{
QList<int> screens;
KSharedConfigPtr lFile = KSharedConfig::openConfig(file);
KConfigGroup containmentGroups = KConfigGroup(lFile, "Containments");
for (const auto &cId : containmentGroups.groupList()) {
if (Layouts::Storage::self()->isLatteContainment(containmentGroups.group(cId))) {
int screenId = containmentGroups.group(cId).readEntry("lastScreen", IDNULL);
if (isValid(screenId) && !screens.contains(screenId)) {
screens << screenId;
}
}
}
return screens;
}
}
}