File indexing completed on 2024-05-19 05:38:22
0001 /* 0002 SPDX-FileCopyrightText: 2014 Marco Martin <mart@kde.org> 0003 SPDX-FileCopyrightText: 2014 Vishesh Handa <me@vhanda.in> 0004 SPDX-FileCopyrightText: 2019 Cyril Rossi <cyril.rossi@enioka.com> 0005 SPDX-FileCopyrightText: 2021 Benjamin Port <benjamin.port@enioka.com> 0006 SPDX-FileCopyrightText: 2022 Dominic Hayes <ferenosdev@outlook.com> 0007 SPDX-FileCopyrightText: 2023 Ismael Asensio <isma.af@gmail.com> 0008 0009 SPDX-License-Identifier: LGPL-2.0-only 0010 */ 0011 0012 #include "lookandfeelmanager.h" 0013 #include "../../startkde/plasmaautostart/plasmaautostart.h" 0014 #include "../colors/colorsapplicator.h" 0015 #include "config-kcm.h" 0016 #include "lookandfeeldata.h" 0017 #include "lookandfeelsettings.h" 0018 #include <KIO/CommandLauncherJob> 0019 #include <KPackage/PackageLoader> 0020 #include <KSharedConfig> 0021 #include <QDBusConnection> 0022 #include <QDBusMessage> 0023 #include <QGuiApplication> 0024 #include <QStyle> 0025 #include <QStyleFactory> 0026 0027 #ifdef HAVE_XCURSOR 0028 #include <X11/Xcursor/Xcursor.h> 0029 #endif 0030 0031 using namespace Qt::StringLiterals; 0032 0033 namespace 0034 { 0035 // Helper methods to read or check entries from a nested group within a config 0036 0037 QString configValue(KSharedConfigPtr config, const QString &groupPath, const QString &entry) 0038 { 0039 // Navigate through the group hierarchy 0040 QStringList groups = groupPath.split(QLatin1Char('/')); 0041 KConfigGroup cg(config, groups.takeAt(0)); 0042 for (const QString &group : groups) { 0043 cg = KConfigGroup(&cg, group); 0044 } 0045 0046 return cg.readEntry(entry, QString()); 0047 } 0048 0049 bool configProvides(KSharedConfigPtr config, const QString &groupPath, const QStringList &entries) 0050 { 0051 return std::any_of(entries.cbegin(), entries.cend(), [config, groupPath](const QString &entry) { 0052 return !configValue(config, groupPath, entry).isEmpty(); 0053 }); 0054 } 0055 0056 bool configProvides(KSharedConfigPtr config, const QString &groupPath, const QString &entry) 0057 { 0058 return !configValue(config, groupPath, entry).isEmpty(); 0059 } 0060 0061 } // Anonymouse namespace 0062 0063 LookAndFeelManager::LookAndFeelManager(QObject *parent) 0064 : QObject(parent) 0065 , m_data(new LookAndFeelData(this)) 0066 , m_plasmashellChanged(false) 0067 , m_fontsChanged(false) 0068 { 0069 m_applyLatteLayout = (KService::serviceByDesktopName("org.kde.latte-dock") != nullptr); 0070 } 0071 0072 LookAndFeelSettings *LookAndFeelManager::settings() const 0073 { 0074 return m_data->settings(); 0075 } 0076 0077 LookAndFeelManager::Contents LookAndFeelManager::packageContents(const KPackage::Package &pkg) const 0078 { 0079 Contents contents = Empty; 0080 0081 contents.setFlag(SplashScreen, !pkg.filePath("splashmainscript").isEmpty()); 0082 contents.setFlag(LockScreen, !pkg.filePath("lockscreenmainscript").isEmpty()); 0083 0084 contents.setFlag(DesktopLayout, !pkg.filePath("layouts").isEmpty()); 0085 0086 // TODO: Those seem unused... are deprecated? 0087 contents.setFlag(RunCommand, !pkg.filePath("runcommandmainscript").isEmpty()); 0088 contents.setFlag(LogOutScript, !pkg.filePath("logoutmainscript").isEmpty()); 0089 0090 if (!pkg.filePath("layoutdefaults").isEmpty()) { 0091 KSharedConfigPtr conf = KSharedConfig::openConfig(pkg.filePath("layoutdefaults")); 0092 contents.setFlag(TitlebarLayout, configProvides(conf, "kwinrc/org.kde.kdecoration2", {"ButtonsOnLeft", "ButtonsOnRight"})); 0093 } 0094 0095 if (!pkg.filePath("defaults").isEmpty()) { 0096 KSharedConfigPtr conf = KSharedConfig::openConfig(pkg.filePath("defaults")); 0097 0098 contents.setFlag(Colors, configProvides(conf, "kdeglobals/General", "ColorScheme") || !pkg.filePath("colors").isEmpty()); 0099 contents.setFlag(WidgetStyle, configProvides(conf, "kdeglobals/KDE", "widgetStyle")); 0100 contents.setFlag(Icons, configProvides(conf, "kdeglobals/Icons", "Theme")); 0101 0102 contents.setFlag(PlasmaTheme, configProvides(conf, "plasmarc/Theme", "name")); 0103 contents.setFlag(Wallpaper, configProvides(conf, "Wallpaper", "Image")); 0104 contents.setFlag(Cursors, configProvides(conf, "kcminputrc/Mouse", "cursorTheme")); 0105 0106 contents.setFlag(WindowSwitcher, configProvides(conf, "kwinrc/WindowSwitcher", "LayoutName")); 0107 contents.setFlag(WindowDecoration, configProvides(conf, "kwinrc/org.kde.kdecoration2", {"library", "NoPlugin"})); 0108 contents.setFlag(BorderSize, configProvides(conf, "kwinrc/org.kde.kdecoration2", "BorderSize")); 0109 0110 contents.setFlag(Fonts, 0111 configProvides(conf, "kdeglobals/WM", {"font", "fixed", "smallestReadableFont", "toolBarFont", "menuFont"}) 0112 || configProvides(conf, "kdeglobals/General", "activeFont")); 0113 0114 contents.setFlag(WindowPlacement, configProvides(conf, "kwinrc/Windows", "Placement")); 0115 contents.setFlag(ShellPackage, configProvides(conf, "plasmashellrc/Shell", "ShellPackage")); 0116 0117 // TODO: Currently managed together within the "DesktopLayout" content 0118 // contents.setFlag(Autostart, configProvides(conf, "Autostart", "Services")); 0119 } 0120 0121 return contents; 0122 } 0123 0124 void LookAndFeelManager::setSplashScreen(const QString &theme) 0125 { 0126 if (theme.isEmpty()) { 0127 return; 0128 } 0129 0130 KSharedConfigPtr config = KSharedConfig::openConfig(QStringLiteral("ksplashrc")); 0131 KConfigGroup group(config, QStringLiteral("KSplash")); 0132 0133 KConfig configDefault(configDefaults(QStringLiteral("ksplashrc"))); 0134 KConfigGroup defaultGroup(&configDefault, QStringLiteral("KSplash")); 0135 writeNewDefaults(group, defaultGroup, QStringLiteral("Theme"), theme); 0136 // TODO: a way to set none as spash in the l&f 0137 writeNewDefaults(group, defaultGroup, QStringLiteral("Engine"), QStringLiteral("KSplashQML")); 0138 } 0139 0140 void LookAndFeelManager::setLockScreen(const QString &theme) 0141 { 0142 if (theme.isEmpty()) { 0143 return; 0144 } 0145 0146 writeNewDefaults(QStringLiteral("kscreenlockerrc"), QStringLiteral("Greeter"), QStringLiteral("Theme"), theme); 0147 } 0148 0149 void LookAndFeelManager::setWindowSwitcher(const QString &theme) 0150 { 0151 if (theme.isEmpty()) { 0152 return; 0153 } 0154 0155 writeNewDefaults(QStringLiteral("kwinrc"), QStringLiteral("TabBox"), QStringLiteral("LayoutName"), theme); 0156 } 0157 0158 void LookAndFeelManager::setWindowPlacement(const QString &value) 0159 { 0160 if (value.isEmpty()) { 0161 return; 0162 } 0163 0164 writeNewDefaults(QStringLiteral("kwinrc"), QStringLiteral("Windows"), QStringLiteral("Placement"), value); 0165 } 0166 0167 void LookAndFeelManager::setShellPackage(const QString &value) 0168 { 0169 if (value.isEmpty()) { 0170 return; 0171 } 0172 0173 writeNewDefaults(QStringLiteral("plasmashellrc"), QStringLiteral("Shell"), QStringLiteral("ShellPackage"), value); 0174 m_plasmashellChanged = true; 0175 } 0176 0177 void LookAndFeelManager::setWindowDecoration(const QString &library, const QString &theme, bool noPlugin) 0178 { 0179 if (library.isEmpty()) { 0180 return; 0181 } 0182 0183 KSharedConfigPtr config = KSharedConfig::openConfig(QStringLiteral("kwinrc")); 0184 KConfigGroup group(config, QStringLiteral("org.kde.kdecoration2")); 0185 0186 KConfig configDefault(configDefaults(QStringLiteral("kwinrc"))); 0187 KConfigGroup defaultGroup(&configDefault, QStringLiteral("org.kde.kdecoration2")); 0188 writeNewDefaults(group, defaultGroup, QStringLiteral("library"), library); 0189 writeNewDefaults(group, defaultGroup, QStringLiteral("theme"), theme, KConfig::Notify); 0190 writeNewDefaults(group, defaultGroup, QStringLiteral("NoPlugin"), noPlugin ? "true" : "false", KConfig::Notify); 0191 } 0192 0193 void LookAndFeelManager::setTitlebarLayout(const QString &leftbtns, const QString &rightbtns) 0194 { 0195 if (leftbtns.isEmpty() && rightbtns.isEmpty()) { 0196 return; 0197 } 0198 0199 writeNewDefaults(QStringLiteral("kwinrc"), QStringLiteral("org.kde.kdecoration2"), QStringLiteral("ButtonsOnLeft"), leftbtns, KConfig::Notify); 0200 writeNewDefaults(QStringLiteral("kwinrc"), QStringLiteral("org.kde.kdecoration2"), QStringLiteral("ButtonsOnRight"), rightbtns, KConfig::Notify); 0201 } 0202 0203 void LookAndFeelManager::setBorderSize(const QString &size) 0204 { 0205 if (size.isEmpty()) { 0206 return; 0207 } 0208 0209 writeNewDefaults(QStringLiteral("kwinrc"), QStringLiteral("org.kde.kdecoration2"), QStringLiteral("BorderSize"), size, KConfig::Notify); 0210 } 0211 0212 void LookAndFeelManager::setBorderlessMaximized(const QString &value) 0213 { 0214 if (value.isEmpty()) { // Turn borderless off for unsupported LNFs to prevent issues 0215 writeNewDefaults(QStringLiteral("kwinrc"), QStringLiteral("Windows"), QStringLiteral("BorderlessMaximizedWindows"), "false", KConfig::Notify); 0216 return; 0217 } 0218 0219 writeNewDefaults(QStringLiteral("kwinrc"), QStringLiteral("Windows"), QStringLiteral("BorderlessMaximizedWindows"), value, KConfig::Notify); 0220 } 0221 0222 void LookAndFeelManager::setWidgetStyle(const QString &style) 0223 { 0224 if (style.isEmpty()) { 0225 return; 0226 } 0227 0228 if (qobject_cast<QGuiApplication *>(QCoreApplication::instance())) { 0229 // Some global themes use styles that may not be installed. 0230 // Test if style can be installed before updating the config. 0231 std::unique_ptr<QStyle> testStyle(QStyleFactory::create(style)); 0232 if (!testStyle) { 0233 return; 0234 } 0235 } 0236 0237 writeNewDefaults(QStringLiteral("kdeglobals"), QStringLiteral("KDE"), QStringLiteral("widgetStyle"), style, KConfig::Notify); 0238 Q_EMIT styleChanged(style); 0239 } 0240 0241 void LookAndFeelManager::setColors(const QString &scheme, const QString &colorFile) 0242 { 0243 if (scheme.isEmpty() && colorFile.isEmpty()) { 0244 return; 0245 } 0246 0247 KConfig configDefault(configDefaults(QStringLiteral("kdeglobals"))); 0248 auto kdeGlobalsCfg = KSharedConfig::openConfig(QStringLiteral("kdeglobals"), KConfig::FullConfig); 0249 0250 if (m_mode == Mode::Apply) { 0251 applyScheme(colorFile, kdeGlobalsCfg.data(), KConfig::Notify); 0252 } 0253 0254 writeNewDefaults(*kdeGlobalsCfg, configDefault, QStringLiteral("General"), QStringLiteral("ColorScheme"), scheme, KConfig::Notify); 0255 0256 Q_EMIT colorsChanged(); 0257 } 0258 0259 void LookAndFeelManager::setIcons(const QString &theme) 0260 { 0261 if (theme.isEmpty()) { 0262 return; 0263 } 0264 0265 writeNewDefaults(QStringLiteral("kdeglobals"), QStringLiteral("Icons"), QStringLiteral("Theme"), theme, KConfig::Notify); 0266 0267 Q_EMIT iconsChanged(); 0268 } 0269 0270 void LookAndFeelManager::setLatteLayout(const QString &filepath, const QString &name) 0271 { 0272 if (filepath.isEmpty()) { 0273 // there is no latte layout 0274 KIO::CommandLauncherJob latteapp(QStringLiteral("latte-dock"), {QStringLiteral("--disable-autostart")}); 0275 latteapp.setDesktopName("org.kde.latte-dock"); 0276 latteapp.start(); 0277 0278 QDBusMessage quitmessage = QDBusMessage::createMethodCall(QStringLiteral("org.kde.lattedock"), 0279 QStringLiteral("/MainApplication"), 0280 QStringLiteral("org.qtproject.Qt.QCoreApplication"), 0281 QStringLiteral("quit")); 0282 QDBusConnection::sessionBus().call(quitmessage, QDBus::NoBlock); 0283 } else { 0284 KIO::CommandLauncherJob latteapp( 0285 QStringLiteral("latte-dock"), 0286 {QStringLiteral("--enable-autostart"), QStringLiteral("--import-layout"), filepath, QStringLiteral("--suggested-layout-name"), name}); 0287 latteapp.setDesktopName("org.kde.latte-dock"); 0288 latteapp.start(); 0289 } 0290 } 0291 0292 void LookAndFeelManager::setPlasmaTheme(const QString &theme) 0293 { 0294 if (theme.isEmpty()) { 0295 return; 0296 } 0297 0298 writeNewDefaults(QStringLiteral("plasmarc"), QStringLiteral("Theme"), QStringLiteral("name"), theme); 0299 } 0300 0301 void LookAndFeelManager::setGeneralFont(const QString &font) 0302 { 0303 if (font.isEmpty()) { 0304 return; 0305 } 0306 0307 writeNewDefaults(QStringLiteral("kdeglobals"), QStringLiteral("General"), QStringLiteral("font"), font, KConfig::Notify); 0308 m_fontsChanged = true; 0309 } 0310 0311 void LookAndFeelManager::setFixedFont(const QString &font) 0312 { 0313 if (font.isEmpty()) { 0314 return; 0315 } 0316 0317 writeNewDefaults(QStringLiteral("kdeglobals"), QStringLiteral("General"), QStringLiteral("fixed"), font, KConfig::Notify); 0318 m_fontsChanged = true; 0319 } 0320 0321 void LookAndFeelManager::setSmallestReadableFont(const QString &font) 0322 { 0323 if (font.isEmpty()) { 0324 return; 0325 } 0326 0327 writeNewDefaults(QStringLiteral("kdeglobals"), QStringLiteral("General"), QStringLiteral("smallestReadableFont"), font, KConfig::Notify); 0328 m_fontsChanged = true; 0329 } 0330 0331 void LookAndFeelManager::setToolbarFont(const QString &font) 0332 { 0333 if (font.isEmpty()) { 0334 return; 0335 } 0336 0337 writeNewDefaults(QStringLiteral("kdeglobals"), QStringLiteral("General"), QStringLiteral("toolBarFont"), font, KConfig::Notify); 0338 m_fontsChanged = true; 0339 } 0340 0341 void LookAndFeelManager::setMenuFont(const QString &font) 0342 { 0343 if (font.isEmpty()) { 0344 return; 0345 } 0346 0347 writeNewDefaults(QStringLiteral("kdeglobals"), QStringLiteral("General"), QStringLiteral("menuFont"), font, KConfig::Notify); 0348 m_fontsChanged = true; 0349 } 0350 0351 void LookAndFeelManager::setWindowTitleFont(const QString &font) 0352 { 0353 if (font.isEmpty()) { 0354 return; 0355 } 0356 0357 writeNewDefaults(QStringLiteral("kdeglobals"), QStringLiteral("WM"), QStringLiteral("activeFont"), font, KConfig::Notify); 0358 m_fontsChanged = true; 0359 } 0360 0361 void LookAndFeelManager::writeNewDefaults(const QString &filename, 0362 const QString &group, 0363 const QString &key, 0364 const QString &value, 0365 KConfig::WriteConfigFlags writeFlags) 0366 { 0367 KSharedConfigPtr config = KSharedConfig::openConfig(filename); 0368 KConfigGroup configGroup(config, group); 0369 0370 KConfig configDefault(configDefaults(filename)); 0371 KConfigGroup defaultGroup(&configDefault, group); 0372 0373 writeNewDefaults(configGroup, defaultGroup, key, value, writeFlags); 0374 } 0375 0376 void LookAndFeelManager::writeNewDefaults(KConfig &config, 0377 KConfig &configDefault, 0378 const QString &group, 0379 const QString &key, 0380 const QString &value, 0381 KConfig::WriteConfigFlags writeFlags) 0382 { 0383 KConfigGroup configGroup(&config, group); 0384 KConfigGroup defaultGroup(&configDefault, group); 0385 0386 writeNewDefaults(configGroup, defaultGroup, key, value, writeFlags); 0387 } 0388 0389 void LookAndFeelManager::writeNewDefaults(KConfigGroup &group, 0390 KConfigGroup &defaultGroup, 0391 const QString &key, 0392 const QString &value, 0393 KConfig::WriteConfigFlags writeFlags) 0394 { 0395 defaultGroup.writeEntry(key, value, writeFlags); 0396 defaultGroup.sync(); 0397 0398 if (m_mode == Mode::Apply) { 0399 group.revertToDefault(key, writeFlags); 0400 group.sync(); 0401 } 0402 } 0403 0404 KConfig LookAndFeelManager::configDefaults(const QString &filename) 0405 { 0406 return KConfig(QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1String("/kdedefaults/") + filename, KConfig::SimpleConfig); 0407 } 0408 0409 QString LookAndFeelManager::colorSchemeFile(const QString &schemeName) const 0410 { 0411 QString colorScheme(schemeName); 0412 colorScheme.remove(QLatin1Char('\'')); // So Foo's does not become FooS 0413 QRegularExpression fixer(QStringLiteral("[\\W,.-]+(.?)")); 0414 for (auto match = fixer.match(colorScheme); match.hasMatch(); match = fixer.match(colorScheme)) { 0415 colorScheme.replace(match.capturedStart(), match.capturedLength(), match.captured(1).toUpper()); 0416 } 0417 colorScheme.replace(0, 1, colorScheme.at(0).toUpper()); 0418 0419 // NOTE: why this loop trough all the scheme files? 0420 // the scheme theme name is an heuristic, there is no plugin metadata whatsoever. 0421 // is based on the file name stripped from weird characters or the 0422 // eventual id- prefix store.kde.org puts, so we can just find a 0423 // theme that ends as the specified name 0424 const QStringList schemeDirs = 0425 QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("color-schemes"), QStandardPaths::LocateDirectory); 0426 for (const QString &dir : schemeDirs) { 0427 const QStringList fileNames = QDir(dir).entryList(QStringList() << QStringLiteral("*.colors")); 0428 for (const QString &file : fileNames) { 0429 if (file.endsWith(colorScheme + QStringLiteral(".colors"))) { 0430 return dir + QLatin1Char('/') + file; 0431 } 0432 } 0433 } 0434 return QString(); 0435 } 0436 0437 void LookAndFeelManager::save(const KPackage::Package &package, const KPackage::Package &previousPackage, Contents applyMask) 0438 { 0439 // The items to apply are the package contents filtered with the user selection mask 0440 const Contents itemsToApply = packageContents(package) & applyMask; 0441 0442 if (itemsToApply.testFlag(DesktopLayout) && m_mode == Mode::Apply) { 0443 QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral("org.kde.plasmashell"), 0444 QStringLiteral("/PlasmaShell"), 0445 QStringLiteral("org.kde.PlasmaShell"), 0446 QStringLiteral("loadLookAndFeelDefaultLayout")); 0447 0448 QList<QVariant> args; 0449 args << m_data->settings()->lookAndFeelPackage(); 0450 message.setArguments(args); 0451 0452 QDBusConnection::sessionBus().call(message, QDBus::NoBlock); 0453 0454 if (m_applyLatteLayout) { 0455 //! latte exists in system and user has chosen to update desktop layout 0456 setLatteLayout(package.filePath("layouts", "looknfeel.layout.latte"), package.metadata().name()); 0457 } 0458 } 0459 0460 if (!package.filePath("layoutdefaults").isEmpty()) { 0461 KSharedConfigPtr conf = KSharedConfig::openConfig(package.filePath("layoutdefaults")); 0462 KConfigGroup group(conf, u"kwinrc"_s); 0463 if (itemsToApply.testFlag(TitlebarLayout)) { 0464 group = KConfigGroup(&group, u"org.kde.kdecoration2"_s); 0465 setTitlebarLayout(group.readEntry("ButtonsOnLeft", QString()), group.readEntry("ButtonsOnRight", QString())); 0466 } 0467 if (itemsToApply.testFlag(DesktopLayout) && m_mode == Mode::Apply) { 0468 group = KConfigGroup(conf, u"kwinrc"_s); 0469 group = KConfigGroup(&group, u"Windows"_s); 0470 setBorderlessMaximized(group.readEntry("BorderlessMaximizedWindows", QString())); 0471 } 0472 } 0473 if (!package.filePath("defaults").isEmpty()) { 0474 KSharedConfigPtr conf = KSharedConfig::openConfig(package.filePath("defaults")); 0475 KConfigGroup group(conf, u"kdeglobals"_s); 0476 group = KConfigGroup(&group, u"KDE"_s); 0477 if (itemsToApply.testFlag(WidgetStyle)) { 0478 QString widgetStyle = group.readEntry("widgetStyle", QString()); 0479 // Some global themes refer to breeze's widgetStyle with a lowercase b. 0480 if (widgetStyle == QStringLiteral("breeze")) { 0481 widgetStyle = QStringLiteral("Breeze"); 0482 } 0483 0484 setWidgetStyle(widgetStyle); 0485 } 0486 0487 if (itemsToApply.testFlag(Colors)) { 0488 QString colorsFile = package.filePath("colors"); 0489 KConfigGroup group(conf, u"kdeglobals"_s); 0490 group = KConfigGroup(&group, u"General"_s); 0491 QString colorScheme = group.readEntry("ColorScheme", QString()); 0492 0493 if (!colorsFile.isEmpty()) { 0494 if (!colorScheme.isEmpty()) { 0495 setColors(colorScheme, colorsFile); 0496 } else { 0497 setColors(package.metadata().name(), colorsFile); 0498 } 0499 } else if (!colorScheme.isEmpty()) { 0500 QString path = colorSchemeFile(colorScheme); 0501 if (!path.isEmpty()) { 0502 setColors(colorScheme, path); 0503 } 0504 } 0505 } 0506 0507 if (itemsToApply.testFlag(Icons)) { 0508 group = KConfigGroup(conf, u"kdeglobals"_s); 0509 group = KConfigGroup(&group, u"Icons"_s); 0510 setIcons(group.readEntry("Theme", QString())); 0511 } 0512 0513 if (itemsToApply.testFlag(PlasmaTheme)) { 0514 group = KConfigGroup(conf, u"plasmarc"_s); 0515 group = KConfigGroup(&group, u"Theme"_s); 0516 setPlasmaTheme(group.readEntry("name", QString())); 0517 } 0518 0519 if (itemsToApply.testFlag(Cursors)) { 0520 group = KConfigGroup(conf, u"kcminputrc"_s); 0521 group = KConfigGroup(&group, u"Mouse"_s); 0522 setCursorTheme(group.readEntry("cursorTheme", QString())); 0523 } 0524 0525 if (itemsToApply.testFlag(WindowSwitcher)) { 0526 group = KConfigGroup(conf, u"kwinrc"_s); 0527 group = KConfigGroup(&group, u"WindowSwitcher"_s); 0528 setWindowSwitcher(group.readEntry("LayoutName", QString())); 0529 } 0530 0531 if (itemsToApply.testFlag(WindowPlacement)) { 0532 group = KConfigGroup(conf, u"kwinrc"_s); 0533 group = KConfigGroup(&group, u"Windows"_s); 0534 setWindowPlacement(group.readEntry("Placement", QStringLiteral("Centered"))); 0535 } 0536 0537 if (itemsToApply.testFlag(ShellPackage)) { 0538 group = KConfigGroup(conf, u"plasmashellrc"_s); 0539 group = KConfigGroup(&group, u"Shell"_s); 0540 setShellPackage(group.readEntry("ShellPackage", QString())); 0541 } 0542 0543 if (itemsToApply.testFlag(WindowDecoration)) { 0544 group = KConfigGroup(conf, u"kwinrc"_s); 0545 group = KConfigGroup(&group, u"org.kde.kdecoration2"_s); 0546 0547 #ifdef HAVE_BREEZE_DECO 0548 setWindowDecoration(group.readEntry("library", QStringLiteral(BREEZE_KDECORATION_PLUGIN_ID)), 0549 group.readEntry("theme", QStringLiteral("Breeze")), 0550 group.readEntry("NoPlugin", false)); 0551 #else 0552 setWindowDecoration(group.readEntry("library", QStringLiteral("org.kde.kwin.aurorae")), 0553 group.readEntry("theme", QStringLiteral("kwin4_decoration_qml_plastik")), 0554 group.readEntry("NoPlugin", false)); 0555 #endif 0556 } 0557 0558 if (itemsToApply.testFlag(Fonts)) { 0559 group = KConfigGroup(conf, u"kdeglobals"_s); 0560 group = KConfigGroup(&group, u"General"_s); 0561 setGeneralFont(group.readEntry("font", QString())); 0562 setFixedFont(group.readEntry("fixed", QString())); 0563 setSmallestReadableFont(group.readEntry("smallestReadableFont", QString())); 0564 setToolbarFont(group.readEntry("toolBarFont", QString())); 0565 setMenuFont(group.readEntry("menuFont", QString())); 0566 group = KConfigGroup(conf, u"kdeglobals"_s); 0567 group = KConfigGroup(&group, u"WM"_s); 0568 setWindowTitleFont(group.readEntry("activeFont")); 0569 if (m_fontsChanged) { 0570 Q_EMIT fontsChanged(); 0571 m_fontsChanged = false; 0572 } 0573 } 0574 0575 if (itemsToApply.testFlag(BorderSize)) { 0576 group = KConfigGroup(conf, u"kwinrc"_s); 0577 group = KConfigGroup(&group, u"org.kde.kdecoration2"_s); 0578 setBorderSize(group.readEntry("BorderSize", QString())); 0579 } 0580 0581 if (itemsToApply.testFlag(SplashScreen)) { 0582 group = KConfigGroup(conf, u"ksplashrc"_s); 0583 group = KConfigGroup(&group, u"KSplash"_s); 0584 QString splashScreen = (group.readEntry("Theme", QString())); 0585 if (!splashScreen.isEmpty()) { 0586 setSplashScreen(splashScreen); 0587 } else { 0588 setSplashScreen(m_data->settings()->lookAndFeelPackage()); 0589 } 0590 } 0591 if (itemsToApply.testFlag(LockScreen)) { 0592 setLockScreen(m_data->settings()->lookAndFeelPackage()); 0593 } 0594 0595 QFile packageFile(QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1String("/kdedefaults/package")); 0596 packageFile.open(QIODevice::WriteOnly); 0597 packageFile.write(m_data->settings()->lookAndFeelPackage().toUtf8()); 0598 0599 if (m_mode == Mode::Defaults) { 0600 return; 0601 } 0602 0603 if (m_plasmashellChanged) { 0604 QDBusMessage message = 0605 QDBusMessage::createSignal(QStringLiteral("/PlasmaShell"), QStringLiteral("org.kde.PlasmaShell"), QStringLiteral("refreshCurrentShell")); 0606 QDBusConnection::sessionBus().send(message); 0607 } 0608 0609 // autostart 0610 if (itemsToApply.testFlag(DesktopLayout)) { 0611 QStringList toStop; 0612 KService::List toStart; 0613 // remove all the old package to autostart 0614 { 0615 KSharedConfigPtr oldConf = KSharedConfig::openConfig(previousPackage.filePath("defaults")); 0616 group = KConfigGroup(oldConf, u"Autostart"_s); 0617 const QStringList autostartServices = group.readEntry("Services", QStringList()); 0618 0619 if (qEnvironmentVariableIsSet("KDE_FULL_SESSION")) { 0620 for (const QString &serviceFile : autostartServices) { 0621 KService service(serviceFile + QStringLiteral(".desktop")); 0622 PlasmaAutostart as(serviceFile); 0623 as.setAutostarts(false); 0624 QString serviceName = service.property<QString>(QStringLiteral("X-DBUS-ServiceName")); 0625 toStop.append(serviceName); 0626 } 0627 } 0628 } 0629 // Set all the stuff in the new lnf to autostart 0630 { 0631 group = KConfigGroup(conf, u"Autostart"_s); 0632 const QStringList autostartServices = group.readEntry("Services", QStringList()); 0633 0634 for (const QString &serviceFile : autostartServices) { 0635 KService::Ptr service(new KService(serviceFile + QStringLiteral(".desktop"))); 0636 PlasmaAutostart as(serviceFile); 0637 as.setCommand(service->exec()); 0638 as.setAutostarts(true); 0639 const QString serviceName = service->property<QString>(QStringLiteral("X-DBUS-ServiceName")); 0640 toStop.removeAll(serviceName); 0641 if (qEnvironmentVariableIsSet("KDE_FULL_SESSION")) { 0642 toStart += service; 0643 } 0644 } 0645 } 0646 Q_EMIT refreshServices(toStop, toStart); 0647 } 0648 } 0649 // Reload KWin if something changed, but only once. 0650 if (itemsToApply & KWinSettings) { 0651 QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/KWin"), QStringLiteral("org.kde.KWin"), QStringLiteral("reloadConfig")); 0652 QDBusConnection::sessionBus().send(message); 0653 } 0654 } 0655 0656 bool LookAndFeelManager::remove(const KPackage::Package &package, LookAndFeelManager::Contents contentsMask) 0657 { 0658 QDir packageRootDir(package.path()); 0659 if (!packageRootDir.isReadable()) { 0660 // Permission denied 0661 return false; 0662 } 0663 0664 const Contents itemsToRemove = packageContents(package) & contentsMask; 0665 0666 // Remove package dependencies 0667 auto config = KSharedConfig::openConfig(package.filePath("defaults"), KConfig::NoGlobals); 0668 0669 // WidgetStyle 0670 if (itemsToRemove.testFlag(WidgetStyle)) { 0671 const QString widgetStyle = configValue(config, "kdeglobals/KDE", "widgetStyle"); 0672 0673 const QDir widgetStyleDir(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QDir::separator() + QStringLiteral("kstyle") 0674 + QDir::separator() + QStringLiteral("themes")); 0675 if (widgetStyleDir.exists()) { 0676 // Read config to get style name 0677 const QStringList styleFileList = widgetStyleDir.entryList({QStringLiteral("*.themerc")}, QDir::Files | QDir::NoDotAndDotDot); 0678 for (const QString &path : styleFileList) { 0679 auto widgetStyleConfig = KSharedConfig::openConfig(widgetStyleDir.absoluteFilePath(path), KConfig::SimpleConfig); 0680 KConfigGroup widgetStyleKDEGroup(widgetStyleConfig, u"KDE"_s); 0681 if (widgetStyleKDEGroup.exists() && widgetStyleKDEGroup.hasKey("WidgetStyle")) { 0682 if (widgetStyleKDEGroup.readEntry("WidgetStyle", "") == widgetStyle) { 0683 QFile(widgetStyleDir.absoluteFilePath(path)).remove(); 0684 break; 0685 } 0686 } 0687 } 0688 } 0689 } 0690 0691 // Color scheme 0692 if (itemsToRemove.testFlag(Colors)) { 0693 const QString colorScheme = configValue(config, "kdeglobals/General", "ColorScheme"); 0694 QFile schemeFile(colorSchemeFile(colorScheme)); 0695 if (schemeFile.exists()) { 0696 schemeFile.remove(); 0697 } 0698 } 0699 0700 // Desktop Theme and Icon (Icons are in the theme folder). Only remove if both selected 0701 // TODO: Remove separately 0702 if (itemsToRemove.testFlag(PlasmaTheme) && itemsToRemove.testFlag(Icons)) { 0703 const QString themeName = configValue(config, "plasmarc/Theme", "name"); 0704 QDir themeDir(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QDir::separator() + QStringLiteral("plasma") + QDir::separator() 0705 + QStringLiteral("desktoptheme") + QDir::separator() + themeName); 0706 if (themeDir.exists()) { 0707 themeDir.removeRecursively(); 0708 } 0709 } 0710 0711 // Wallpaper 0712 if (itemsToRemove.testFlag(Wallpaper)) { 0713 const QString image = configValue(config, "Wallpaper", "Image"); 0714 const QDir wallpaperDir(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QDir::separator() + QStringLiteral("wallpapers")); 0715 if (wallpaperDir.exists()) { 0716 QFileInfo wallpaperFile(wallpaperDir.absoluteFilePath(image)); 0717 if (wallpaperFile.isFile()) { 0718 // Single file, use QFile to remove 0719 QFile(wallpaperFile.absoluteFilePath()).remove(); 0720 } else if (wallpaperFile.isDir()) { 0721 // A package, use QDir to remove the folder 0722 QDir(wallpaperFile.absoluteFilePath()).removeRecursively(); 0723 } 0724 } 0725 } 0726 0727 if (itemsToRemove.testFlag(WindowSwitcher)) { 0728 const QString layoutName = configValue(config, "kwinrc/WindowSwitcher", "LayoutName"); 0729 const auto windowSwitcherPackages = 0730 KPackage::PackageLoader::self()->findPackages(QStringLiteral("KWin/WindowSwitcher"), QString(), [&layoutName](const KPluginMetaData &meta) { 0731 return meta.pluginId() == layoutName; 0732 }); 0733 for (const auto &meta : windowSwitcherPackages) { 0734 QFileInfo(meta.fileName()).absoluteDir().removeRecursively(); 0735 } 0736 } 0737 0738 return packageRootDir.removeRecursively(); 0739 } 0740 0741 void LookAndFeelManager::setCursorTheme(const QString themeName) 0742 { 0743 // TODO: use pieces of cursor kcm when moved to plasma-desktop 0744 if (themeName.isEmpty()) { 0745 return; 0746 } 0747 0748 writeNewDefaults(QStringLiteral("kcminputrc"), QStringLiteral("Mouse"), QStringLiteral("cursorTheme"), themeName, KConfig::Notify); 0749 Q_EMIT cursorsChanged(themeName); 0750 } 0751 0752 void LookAndFeelManager::setMode(Mode mode) 0753 { 0754 m_mode = mode; 0755 }