File indexing completed on 2024-05-19 05:38:03

0001 /*
0002     SPDX-FileCopyrightText: 2021 Dan Leinir Turthra Jensen <admin@leinir.dk>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-only
0005 */
0006 
0007 #include "colorsapplicator.h"
0008 #include "colorsmodel.h"
0009 #include "colorssettings.h"
0010 
0011 #include "../kcms-common_p.h"
0012 #include "../krdb/krdb.h"
0013 
0014 #include <KColorScheme>
0015 #include <KConfig>
0016 #include <KLocalizedString>
0017 
0018 #include <QCommandLineParser>
0019 #include <QDBusConnection>
0020 #include <QDBusMessage>
0021 #include <QDebug>
0022 #include <QFile>
0023 #include <QGuiApplication>
0024 #include <QTimer>
0025 
0026 int main(int argc, char **argv)
0027 {
0028     // This is a CLI application, but we require at least a QGuiApplication to be able
0029     // to use QColor, so let's just roll with one of these
0030     QGuiApplication app(argc, argv);
0031     QCoreApplication::setApplicationName(QStringLiteral("plasma-apply-colorscheme"));
0032     QCoreApplication::setApplicationVersion(QStringLiteral("1.0"));
0033     QCoreApplication::setOrganizationDomain(QStringLiteral("kde.org"));
0034     KLocalizedString::setApplicationDomain(QByteArrayLiteral("plasma-apply-colorscheme"));
0035 
0036     QCommandLineParser *parser = new QCommandLineParser;
0037     parser->addHelpOption();
0038     parser->setApplicationDescription(
0039         i18n("This tool allows you to set the color scheme for the current Plasma session, without accidentally setting it to one that is either not "
0040              "available, or which is already set."));
0041     parser->addPositionalArgument(
0042         QStringLiteral("colorscheme"),
0043         i18n("The name of the color scheme you wish to set for your current Plasma session (passing a full path will only use the last part of the path)"));
0044 
0045     const auto listSchemes = QStringList({QStringLiteral("list-schemes"), QStringLiteral("l")});
0046     parser->addOption(QCommandLineOption(listSchemes, i18n("Show all the color schemes available on the system (and which is the current theme)")));
0047 
0048     const auto accentColor = QStringList({QStringLiteral("accent-color"), QStringLiteral("a")});
0049     parser->addOption(
0050         QCommandLineOption(accentColor,
0051                            i18n("The name of the accent color you want to set. SVG color names (https://www.w3.org/TR/SVG11/types.html#ColorKeywords) and hex "
0052                                 "color codes are supported. Quote the hex code if there is possibility of shell expansion"),
0053                            "accentColor",
0054                            "0"));
0055     parser->process(app);
0056 
0057     int exitCode{0};
0058     ColorsSettings *settings = new ColorsSettings(&app);
0059     QTextStream ts(stdout);
0060     ColorsModel *model = new ColorsModel(&app);
0061     model->load();
0062     model->setSelectedScheme(settings->colorScheme());
0063     if (!parser->positionalArguments().isEmpty() && !parser->isSet(QStringLiteral("accent-color"))) {
0064         QString requestedScheme{parser->positionalArguments().first()};
0065         const QString dirSplit{"/"};
0066         if (requestedScheme.contains(dirSplit)) {
0067             QStringList splitScheme = requestedScheme.split(dirSplit, Qt::SkipEmptyParts);
0068             requestedScheme = splitScheme.last();
0069             if (requestedScheme.endsWith(QStringLiteral(".colors"))) {
0070                 requestedScheme = requestedScheme.left(requestedScheme.lastIndexOf(QStringLiteral(".")));
0071             } else {
0072                 exitCode = -1;
0073             }
0074         }
0075 
0076         if (exitCode == 0) {
0077             if (settings->colorScheme() == requestedScheme) {
0078                 ts << i18n("The requested theme \"%1\" is already set as the theme for the current Plasma session.", requestedScheme) << Qt::endl;
0079                 // Not an error condition, no reason to set the theme, but basically this is fine
0080             } else if (!requestedScheme.isEmpty()) {
0081                 int newSchemeIndex{-1};
0082                 QStringList availableThemes;
0083                 for (int i = 0; i < model->rowCount(QModelIndex()); ++i) {
0084                     QString schemeName = model->data(model->index(i, 0), ColorsModel::SchemeNameRole).toString();
0085                     availableThemes << schemeName;
0086                     if (schemeName == requestedScheme) {
0087                         newSchemeIndex = i;
0088                         // No breaking out, we're using the list of names if things fail, and
0089                         // it's not particularly expensive compared to what we've already done
0090                     }
0091                 }
0092 
0093                 if (newSchemeIndex > -1) {
0094                     model->setSelectedScheme(requestedScheme);
0095                     settings->setColorScheme(requestedScheme);
0096                     const QString path =
0097                         QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("color-schemes/%1.colors").arg(model->selectedScheme()));
0098 
0099                     auto msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.KWin"),
0100                                                               QStringLiteral("/org/kde/KWin/BlendChanges"),
0101                                                               QStringLiteral("org.kde.KWin.BlendChanges"),
0102                                                               QStringLiteral("start"));
0103                     msg << 300;
0104                     // This is deliberately blocking so that we ensure Kwin has processed the
0105                     // animation start event before we potentially trigger client side changes
0106                     QDBusConnection::sessionBus().call(msg);
0107 
0108                     applyScheme(path, settings->config());
0109                     settings->save();
0110                     notifyKcmChange(GlobalChangeType::PaletteChanged);
0111                     ts << i18n("Successfully applied the color scheme %1 to your current Plasma session", requestedScheme) << Qt::endl;
0112                 } else {
0113                     ts << i18n("Could not find theme \"%1\". The theme should be one of the following options: %2",
0114                                requestedScheme,
0115                                availableThemes.join(QLatin1String{", "}))
0116                        << Qt::endl;
0117                 }
0118             } else {
0119                 // This shouldn't happen, but let's catch it and make angry noises, just in case...
0120                 ts << i18n("You have managed to pass an empty color scheme name, which isn't supported behavior.") << Qt::endl;
0121                 exitCode = -1;
0122             }
0123         } else {
0124             ts << i18n("The file you attempted to set as your scheme, %1, could not be identified as a color scheme.", parser->positionalArguments().first())
0125                << Qt::endl;
0126             exitCode = -1;
0127         }
0128     } else if (parser->isSet(QStringLiteral("accent-color"))) {
0129         QString accentColor = parser->value("accent-color");
0130 
0131         if (QColor::isValidColor(accentColor)) {
0132             const QString path =
0133                 QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("color-schemes/%1.colors").arg(settings->colorScheme()));
0134 
0135             auto msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.KWin"),
0136                                                       QStringLiteral("/org/kde/KWin/BlendChanges"),
0137                                                       QStringLiteral("org.kde.KWin.BlendChanges"),
0138                                                       QStringLiteral("start"));
0139             msg << 300;
0140             // This is deliberately blocking so that we ensure Kwin has processed the
0141             // animation start event before we potentially trigger client side changes
0142             QDBusConnection::sessionBus().call(msg);
0143 
0144             applyScheme(path, settings->config(), KConfig::Notify, {accentColor});
0145             notifyKcmChange(GlobalChangeType::PaletteChanged);
0146 
0147             ts << i18n("Successfully applied the accent color %1", accentColor) << Qt::endl;
0148         } else {
0149             ts << i18n("Invalid accent color ") << accentColor << Qt::endl;
0150             exitCode = -1;
0151         }
0152 
0153     } else if (parser->isSet(QStringLiteral("list-schemes"))) {
0154         ts << i18n("You have the following color schemes on your system:") << Qt::endl;
0155         int currentThemeIndex = model->selectedSchemeIndex();
0156         for (int i = 0; i < model->rowCount(QModelIndex()); ++i) {
0157             const QString schemeName = model->data(model->index(i, 0), ColorsModel::SchemeNameRole).toString();
0158             if (i == currentThemeIndex) {
0159                 ts << i18n(" * %1 (current color scheme)", schemeName) << Qt::endl;
0160             } else {
0161                 ts << QString(" * %1").arg(schemeName) << Qt::endl;
0162             }
0163         }
0164     } else {
0165         parser->showHelp();
0166     }
0167     QTimer::singleShot(0, &app, [&app, &exitCode]() {
0168         app.exit(exitCode);
0169     });
0170 
0171     return app.exec();
0172 }