File indexing completed on 2024-05-12 05:36:48

0001 /*
0002  *   SPDX-FileCopyrightText: 2016 Marco Martin <mart@kde.org>
0003  *
0004  *   SPDX-License-Identifier: LGPL-2.0-or-later
0005  */
0006 
0007 #include "lnflogic.h"
0008 #include "lnflistmodel.h"
0009 
0010 #include <QDebug>
0011 #include <QDir>
0012 #include <QFile>
0013 #include <QFileDialog>
0014 #include <QStandardPaths>
0015 #include <QUrl>
0016 
0017 #include <QDBusConnection>
0018 #include <QDBusMessage>
0019 #include <QDBusPendingCall>
0020 #include <QDBusPendingCallWatcher>
0021 
0022 #include <KAboutData>
0023 #include <KConfig>
0024 #include <KConfigGroup>
0025 #include <KLocalizedString>
0026 #include <KPackage/PackageLoader>
0027 #include <KSharedConfig>
0028 
0029 LnfLogic::LnfLogic(QObject *parent)
0030     : QObject(parent)
0031     , m_themeName(QStringLiteral("org.kde.breeze.desktop"))
0032     , m_lnfListModel(new LnfListModel(this))
0033     , m_needsSave(false)
0034 {
0035     m_package = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/LookAndFeel"));
0036 }
0037 
0038 LnfLogic::~LnfLogic()
0039 {
0040 }
0041 
0042 void LnfLogic::createNewTheme(const QString &pluginName,
0043                               const QString &name,
0044                               const QString &comment,
0045                               const QString &author,
0046                               const QString &email,
0047                               const QString &license,
0048                               const QString &website)
0049 {
0050     const QString metadataPath(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) % QLatin1String("/plasma/look-and-feel/") % pluginName
0051                                % QLatin1String("/metadata.desktop"));
0052     KConfig c(metadataPath);
0053 
0054     KConfigGroup cg(&c, "Desktop Entry");
0055     cg.writeEntry("Name", name);
0056     cg.writeEntry("Comment", comment);
0057     cg.writeEntry("X-KDE-PluginInfo-Name", pluginName);
0058     cg.writeEntry("X-KDE-ServiceTypes", "Plasma/LookAndFeel");
0059     cg.writeEntry("X-KDE-PluginInfo-Author", author);
0060     cg.writeEntry("X-KDE-PluginInfo-Email", email);
0061     cg.writeEntry("X-KDE-PluginInfo-Website", website);
0062     cg.writeEntry("X-KDE-PluginInfo-Category", "Plasma Look And Feel");
0063     cg.writeEntry("X-KDE-PluginInfo-License", license);
0064     cg.writeEntry("X-KDE-PluginInfo-EnabledByDefault", "true");
0065     cg.writeEntry("X-KDE-PluginInfo-Version", "0.1");
0066     cg.sync();
0067 
0068     dumpPlasmaLayout(pluginName);
0069     dumpDefaultsConfigFile(pluginName);
0070 
0071     m_lnfListModel->reload();
0072 }
0073 
0074 void LnfLogic::dumpPlasmaLayout(const QString &pluginName)
0075 {
0076     QDBusMessage message = QDBusMessage::createMethodCall("org.kde.plasmashell", "/PlasmaShell", "org.kde.PlasmaShell", "dumpCurrentLayoutJS");
0077     QDBusPendingCall pcall = QDBusConnection::sessionBus().asyncCall(message);
0078 
0079     QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pcall, this);
0080 
0081     QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, pluginName](QDBusPendingCallWatcher *watcher) {
0082         const QDBusMessage &msg = watcher->reply();
0083         watcher->deleteLater();
0084         if (watcher->isError()) {
0085             emit messageRequested(ErrorLevel::Error, i18n("Cannot retrieve the current Plasma layout."));
0086             return;
0087         }
0088 
0089         const QString layout = msg.arguments().first().toString();
0090         QDir themeDir(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) % QLatin1String("/plasma/look-and-feel/") % pluginName);
0091         if (!themeDir.mkpath("contents/layouts")) {
0092             qWarning() << "Impossible to create the layouts directory in the look and feel package";
0093             emit messageRequested(ErrorLevel::Error, i18n("Impossible to create the layouts directory in the look and feel package"));
0094             return;
0095         }
0096 
0097         QFile layoutFile(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) % QLatin1String("/plasma/look-and-feel/") % pluginName
0098                          % QLatin1String("/contents/layouts/org.kde.plasma.desktop-layout.js"));
0099         if (layoutFile.open(QIODevice::WriteOnly)) {
0100             layoutFile.write(layout.toUtf8());
0101             layoutFile.close();
0102         } else {
0103             qWarning() << "Impossible to write to org.kde.plasma.desktop-layout.js";
0104             emit messageRequested(ErrorLevel::Error, i18n("Impossible to write to org.kde.plasma.desktop-layout.js"));
0105             return;
0106         }
0107         emit messageRequested(ErrorLevel::Info, i18n("Plasma Layout successfully duplicated"));
0108     });
0109 }
0110 
0111 void LnfLogic::dumpDefaultsConfigFile(const QString &pluginName)
0112 {
0113     // write the defaults file, read from kde config files and save to the defaultsrc
0114     KConfig defaultsConfig(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) % QLatin1String("/plasma/look-and-feel/") % pluginName
0115                            % "/contents/defaults");
0116 
0117     KConfigGroup defaultsConfigGroup(&defaultsConfig, "kdeglobals");
0118     defaultsConfigGroup = KConfigGroup(&defaultsConfigGroup, "KDE");
0119 
0120     // widget style
0121     KConfigGroup systemCG(KSharedConfig::openConfig(QStringLiteral("kdeglobals")), "KDE");
0122     defaultsConfigGroup.writeEntry("widgetStyle", systemCG.readEntry("widgetStyle", QStringLiteral("breeze")));
0123 
0124     // color scheme (TODO: create an in-place color scheme?)
0125     defaultsConfigGroup = KConfigGroup(&defaultsConfig, "kdeglobals");
0126     defaultsConfigGroup = KConfigGroup(&defaultsConfigGroup, "General");
0127     systemCG = KConfigGroup(KSharedConfig::openConfig(QStringLiteral("kdeglobals")), "General");
0128     defaultsConfigGroup.writeEntry("ColorScheme", systemCG.readEntry("ColorScheme", QStringLiteral("Breeze")));
0129 
0130     // icon theme
0131     defaultsConfigGroup = KConfigGroup(&defaultsConfig, "kdeglobals");
0132     defaultsConfigGroup = KConfigGroup(&defaultsConfigGroup, "Icons");
0133     systemCG = KConfigGroup(KSharedConfig::openConfig(QStringLiteral("kdeglobals")), "Icons");
0134     defaultsConfigGroup.writeEntry("Theme", systemCG.readEntry("Theme", QStringLiteral("breeze")));
0135 
0136     // plasma theme
0137     defaultsConfigGroup = KConfigGroup(&defaultsConfig, "plasmarc");
0138     defaultsConfigGroup = KConfigGroup(&defaultsConfigGroup, "Theme");
0139     systemCG = KConfigGroup(KSharedConfig::openConfig(QStringLiteral("plasmarc")), "Theme");
0140     defaultsConfigGroup.writeEntry("name", systemCG.readEntry("name", QStringLiteral("default")));
0141 
0142     // cursor theme
0143     defaultsConfigGroup = KConfigGroup(&defaultsConfig, "kcminputrc");
0144     defaultsConfigGroup = KConfigGroup(&defaultsConfigGroup, "Mouse");
0145     systemCG = KConfigGroup(KSharedConfig::openConfig(QStringLiteral("kcminputrc")), "Mouse");
0146     defaultsConfigGroup.writeEntry("cursorTheme", systemCG.readEntry("cursorTheme", QStringLiteral("breeze_cursors")));
0147 
0148     // KWin window switcher theme
0149     systemCG = KConfigGroup(KSharedConfig::openConfig(QStringLiteral("kwinrc")), "TabBox");
0150     defaultsConfigGroup = KConfigGroup(&defaultsConfig, "kwinrc");
0151     defaultsConfigGroup = KConfigGroup(&defaultsConfigGroup, "WindowSwitcher");
0152     defaultsConfigGroup.writeEntry("LayoutName", systemCG.readEntry("LayoutName", QStringLiteral("org.kde.breeze.desktop")));
0153 
0154     defaultsConfigGroup = KConfigGroup(&defaultsConfig, "kwinrc");
0155     defaultsConfigGroup = KConfigGroup(&defaultsConfigGroup, "DesktopSwitcher");
0156     defaultsConfigGroup.writeEntry("LayoutName", systemCG.readEntry("DesktopLayout", QStringLiteral("org.kde.breeze.desktop")));
0157 
0158     systemCG = KConfigGroup(KSharedConfig::openConfig(QStringLiteral("kwinrc")), "org.kde.kdecoration2");
0159     defaultsConfigGroup = KConfigGroup(&defaultsConfig, "kwinrc");
0160     defaultsConfigGroup = KConfigGroup(&defaultsConfigGroup, "org.kde.kdecoration2");
0161     defaultsConfigGroup.writeEntry("library", systemCG.readEntry("library", QStringLiteral("org.kde.breeze")));
0162     defaultsConfigGroup.writeEntry("theme", systemCG.readEntry("theme", QString()));
0163 
0164     emit messageRequested(ErrorLevel::Info, i18n("Defaults config file saved from your current setup"));
0165 }
0166 
0167 void LnfLogic::dumpCurrentPlasmaLayout()
0168 {
0169     dumpPlasmaLayout(m_themeName);
0170 }
0171 
0172 void LnfLogic::save()
0173 {
0174     KConfig c(m_package.filePath("metadata"));
0175     KConfigGroup cg(&c, "Desktop Entry");
0176 
0177     QHash<QString, QString>::const_iterator i;
0178     for (i = m_tempMetadata.constBegin(); i != m_tempMetadata.constEnd(); ++i) {
0179         cg.writeEntry(i.key(), i.value());
0180     }
0181     m_tempMetadata.clear();
0182     m_needsSave = false;
0183     if (m_performLayoutDump) {
0184         dumpCurrentPlasmaLayout();
0185         m_performLayoutDump = false;
0186     }
0187     if (m_performDefaultsDump) {
0188         dumpDefaultsConfigFile(m_themeName);
0189         m_performDefaultsDump = false;
0190     }
0191 
0192     emit needsSaveChanged();
0193 
0194     // HACK
0195     m_package.setPath(QString());
0196     m_package.setPath(m_themeName);
0197 }
0198 
0199 LnfListModel *LnfLogic::lnfList()
0200 {
0201     return m_lnfListModel;
0202 }
0203 
0204 QString LnfLogic::themeFolder() const
0205 {
0206     return m_package.path();
0207 }
0208 
0209 bool LnfLogic::isWritable() const
0210 {
0211     return QFile::exists(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/plasma/look-and-feel/" + m_themeName);
0212 }
0213 
0214 QString LnfLogic::theme() const
0215 {
0216     return m_themeName;
0217 }
0218 
0219 void LnfLogic::setTheme(const QString &theme)
0220 {
0221     if (theme == m_themeName) {
0222         return;
0223     }
0224 
0225     m_tempMetadata.clear();
0226     m_themeName = theme;
0227     m_package.setPath(theme);
0228     m_needsSave = false;
0229     emit needsSaveChanged();
0230     emit themeChanged();
0231     emit nameChanged();
0232     emit commentChanged();
0233     emit authorChanged();
0234     emit emailChanged();
0235     emit versionChanged();
0236     emit websiteChanged();
0237     emit licenseChanged();
0238 }
0239 
0240 QString LnfLogic::name() const
0241 {
0242     if (m_tempMetadata.contains(QStringLiteral("Name"))) {
0243         return m_tempMetadata.value(QStringLiteral("Name"));
0244     }
0245     return m_package.metadata().name();
0246 }
0247 
0248 void LnfLogic::setName(const QString &name)
0249 {
0250     if (LnfLogic::name() == name) {
0251         return;
0252     }
0253 
0254     m_tempMetadata[QStringLiteral("Name")] = name;
0255     m_needsSave = true;
0256     emit needsSaveChanged();
0257     emit nameChanged();
0258 }
0259 
0260 QString LnfLogic::comment() const
0261 {
0262     if (m_tempMetadata.contains(QStringLiteral("Comment"))) {
0263         return m_tempMetadata.value(QStringLiteral("Comment"));
0264     }
0265     return m_package.metadata().description();
0266 }
0267 
0268 void LnfLogic::setComment(const QString &comment)
0269 {
0270     if (LnfLogic::comment() == comment) {
0271         return;
0272     }
0273 
0274     m_tempMetadata[QStringLiteral("Comment")] = comment;
0275     m_needsSave = true;
0276     emit needsSaveChanged();
0277     emit commentChanged();
0278 }
0279 
0280 QString LnfLogic::author() const
0281 {
0282     if (m_tempMetadata.contains(QStringLiteral("X-KDE-PluginInfo-Author"))) {
0283         return m_tempMetadata.value(QStringLiteral("X-KDE-PluginInfo-Author"));
0284     }
0285     if (m_package.metadata().authors().isEmpty()) {
0286         return QString();
0287     }
0288     return m_package.metadata().authors().first().name();
0289 }
0290 
0291 void LnfLogic::setAuthor(const QString &author)
0292 {
0293     if (LnfLogic::author() == author) {
0294         return;
0295     }
0296 
0297     m_tempMetadata[QStringLiteral("X-KDE-PluginInfo-Author")] = author;
0298     m_needsSave = true;
0299     emit needsSaveChanged();
0300     emit authorChanged();
0301 }
0302 
0303 QString LnfLogic::email() const
0304 {
0305     if (m_tempMetadata.contains(QStringLiteral("X-KDE-PluginInfo-Email"))) {
0306         return m_tempMetadata.value(QStringLiteral("X-KDE-PluginInfo-Email"));
0307     }
0308     if (m_package.metadata().authors().isEmpty()) {
0309         return QString();
0310     }
0311     return m_package.metadata().authors().first().emailAddress();
0312 }
0313 
0314 void LnfLogic::setEmail(const QString &email)
0315 {
0316     if (LnfLogic::email() == email) {
0317         return;
0318     }
0319 
0320     m_tempMetadata[QStringLiteral("X-KDE-PluginInfo-Email")] = email;
0321     m_needsSave = true;
0322     emit needsSaveChanged();
0323     emit emailChanged();
0324 }
0325 
0326 QString LnfLogic::version() const
0327 {
0328     if (m_tempMetadata.contains(QStringLiteral("X-KDE-PluginInfo-Version"))) {
0329         return m_tempMetadata.value(QStringLiteral("X-KDE-PluginInfo-Version"));
0330     }
0331     return m_package.metadata().version();
0332 }
0333 
0334 void LnfLogic::setVersion(const QString &version)
0335 {
0336     if (LnfLogic::version() == version) {
0337         return;
0338     }
0339 
0340     m_tempMetadata[QStringLiteral("X-KDE-PluginInfo-Version")] = version;
0341     m_needsSave = true;
0342     emit needsSaveChanged();
0343     emit versionChanged();
0344 }
0345 
0346 QString LnfLogic::website() const
0347 {
0348     if (m_tempMetadata.contains(QStringLiteral("X-KDE-PluginInfo-Website"))) {
0349         return m_tempMetadata.value(QStringLiteral("X-KDE-PluginInfo-Website"));
0350     }
0351     return m_package.metadata().website();
0352 }
0353 
0354 void LnfLogic::setWebsite(const QString &website)
0355 {
0356     if (LnfLogic::website() == website) {
0357         return;
0358     }
0359 
0360     m_tempMetadata[QStringLiteral("X-KDE-PluginInfo-Website")] = website;
0361     m_needsSave = true;
0362     emit needsSaveChanged();
0363     emit websiteChanged();
0364 }
0365 
0366 QString LnfLogic::license() const
0367 {
0368     if (m_tempMetadata.contains(QStringLiteral("X-KDE-PluginInfo-License"))) {
0369         return m_tempMetadata.value(QStringLiteral("X-KDE-PluginInfo-License"));
0370     }
0371     return m_package.metadata().license();
0372 }
0373 
0374 void LnfLogic::setLicense(const QString &license)
0375 {
0376     if (LnfLogic::license() == license) {
0377         return;
0378     }
0379 
0380     m_tempMetadata[QStringLiteral("X-KDE-PluginInfo-License")] = license;
0381     m_needsSave = true;
0382     emit needsSaveChanged();
0383     emit licenseChanged();
0384 }
0385 
0386 bool LnfLogic::performLayoutDump() const
0387 {
0388     return m_performLayoutDump;
0389 }
0390 
0391 void LnfLogic::setPerformLayoutDump(bool dump)
0392 {
0393     if (m_performLayoutDump == dump) {
0394         return;
0395     }
0396 
0397     m_needsSave = true;
0398     emit needsSaveChanged();
0399     m_performLayoutDump = dump;
0400     emit performLayoutDumpChanged();
0401 }
0402 
0403 bool LnfLogic::performDefaultsDump() const
0404 {
0405     return m_performDefaultsDump;
0406 }
0407 
0408 void LnfLogic::setPerformDefaultsDump(bool dump)
0409 {
0410     if (m_performDefaultsDump == dump) {
0411         return;
0412     }
0413 
0414     m_needsSave = true;
0415     emit needsSaveChanged();
0416     m_performDefaultsDump = dump;
0417     emit performDefaultsDumpChanged();
0418 }
0419 
0420 bool LnfLogic::needsSave()
0421 {
0422     return m_needsSave;
0423 }
0424 
0425 QString LnfLogic::thumbnailPath() const
0426 {
0427     // don't fallback
0428     QString path = m_package.filePath("previews", QStringLiteral("preview.png"));
0429     if (path.contains(m_package.path())) {
0430         return path;
0431     }
0432 
0433     return QString();
0434 }
0435 
0436 void LnfLogic::processThumbnail(const QString &path)
0437 {
0438     if (path.isEmpty()) {
0439         return;
0440     }
0441 
0442     QDir themeDir(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) % QLatin1String("/plasma/look-and-feel/") % m_themeName);
0443     if (!themeDir.mkpath("contents/previews")) {
0444         qWarning() << "Impossible to create the layouts directory in the look and feel package";
0445     }
0446 
0447     QFile imageFile(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) % QLatin1String("/plasma/look-and-feel/") % m_themeName
0448                     % QLatin1String("/contents/previews/preview.png"));
0449     if (!imageFile.open(QIODevice::WriteOnly)) {
0450         qWarning() << "Impossible to write to the thumbnail file";
0451         return;
0452     }
0453 
0454     QImage image(QUrl(path).path());
0455     if (image.isNull()) {
0456         qWarning() << "invalid image";
0457         return;
0458     }
0459     image = image.scaledToWidth(512, Qt::SmoothTransformation);
0460 
0461     image.save(&imageFile, "PNG"); // writes image into ba in PNG format
0462     imageFile.close();
0463 
0464     // copy the fullscreen preview
0465     QFile fullScreenImageFile(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) % QLatin1String("/plasma/look-and-feel/") % m_themeName
0466                               % QLatin1String("/contents/previews/fullscreenpreview.jpg"));
0467     if (!fullScreenImageFile.open(QIODevice::WriteOnly)) {
0468         qWarning() << "Impossible to write to the thumbnail file";
0469         return;
0470     }
0471 
0472     QImage fullScreenImage(QUrl(path).path());
0473     if (fullScreenImage.isNull()) {
0474         qWarning() << "invalid image";
0475         return;
0476     }
0477 
0478     fullScreenImage.save(&fullScreenImageFile, "JPG"); // writes image into ba in PNG format
0479     fullScreenImageFile.close();
0480 
0481     emit themeChanged();
0482 }
0483 
0484 QString LnfLogic::openFile()
0485 {
0486     return QFileDialog::getOpenFileName(nullptr,
0487                                         i18n("Open Image"),
0488                                         QStandardPaths::writableLocation(QStandardPaths::HomeLocation),
0489                                         i18n("Image Files (*.png *.jpg *.bmp)"));
0490 }
0491 
0492 #include "moc_lnflogic.cpp"