File indexing completed on 2024-05-12 17:08:26
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, [=](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"