File indexing completed on 2024-05-12 05:13:33

0001 /*
0002   SPDX-FileCopyrightText: 2013-2024 Laurent Montel <montel@kde.org>
0003 
0004   SPDX-License-Identifier: LGPL-2.1-or-later
0005 */
0006 
0007 #include "grantleetheme.h"
0008 #include "grantleetheme_debug.h"
0009 #include "grantleetheme_p.h"
0010 #include "grantleethemeengine.h"
0011 #include "qtresourcetemplateloader.h"
0012 #include <config-grantleetheme.h>
0013 
0014 #include <KConfig>
0015 #include <KConfigGroup>
0016 #include <KLocalizedString>
0017 
0018 #include <QDir>
0019 #include <QSharedPointer>
0020 
0021 using namespace GrantleeTheme;
0022 
0023 QSharedPointer<GrantleeKi18nLocalizer> GrantleeTheme::ThemePrivate::sLocalizer;
0024 KTextTemplate::Engine *GrantleeTheme::ThemePrivate::sEngine = nullptr;
0025 
0026 ThemePrivate::ThemePrivate()
0027     : QSharedData()
0028 {
0029 }
0030 
0031 ThemePrivate::ThemePrivate(const ThemePrivate &other)
0032 
0033     = default;
0034 
0035 ThemePrivate::~ThemePrivate() = default;
0036 
0037 void ThemePrivate::setupEngine()
0038 {
0039     sEngine = new GrantleeTheme::Engine();
0040 }
0041 
0042 void ThemePrivate::setupLoader()
0043 {
0044     // Get the parent dir with themes, we set the theme directory separately
0045 
0046     QStringList templatePaths;
0047     for (const QString &absolutePath : std::as_const(absolutePaths)) {
0048         QDir dir(absolutePath);
0049         dir.cdUp();
0050         templatePaths << dir.absolutePath();
0051     }
0052     loader = QSharedPointer<GrantleeTheme::QtResourceTemplateLoader>::create();
0053     loader->setTemplateDirs(templatePaths);
0054     loader->setTheme(dirName);
0055 
0056     if (!sEngine) {
0057         ThemePrivate::setupEngine();
0058     }
0059     sEngine->addTemplateLoader(loader);
0060 }
0061 KTextTemplate::Context ThemePrivate::createContext(const QVariantHash &data, const QByteArray &applicationDomain)
0062 {
0063     if (!sLocalizer) {
0064         sLocalizer.reset(new GrantleeKi18nLocalizer());
0065     }
0066     sLocalizer->setApplicationDomain(applicationDomain);
0067     KTextTemplate::Context ctx(data);
0068     ctx.setLocalizer(sLocalizer);
0069     return ctx;
0070 }
0071 QString ThemePrivate::errorTemplate(const QString &reason, const QString &origTemplateName, const KTextTemplate::Template &failedTemplate)
0072 {
0073     KTextTemplate::Template tpl = sEngine->newTemplate(QStringLiteral("<h1>{{ error }}</h1>\n"
0074                                                                       "<b>%1:</b> {{ templateName }}<br>\n"
0075                                                                       "<b>%2:</b> {{ errorMessage }}")
0076                                                            .arg(i18n("Template"), i18n("Error message")),
0077                                                        QStringLiteral("TemplateError"));
0078 
0079     KTextTemplate::Context ctx = createContext();
0080     ctx.insert(QStringLiteral("error"), reason);
0081     ctx.insert(QStringLiteral("templateName"), origTemplateName);
0082     const QString errorString = failedTemplate ? failedTemplate->errorString() : i18n("(null template)");
0083     ctx.insert(QStringLiteral("errorMessage"), errorString);
0084     return tpl->render(&ctx);
0085 }
0086 
0087 Theme::Theme()
0088     : d(new ThemePrivate)
0089 {
0090 }
0091 
0092 Theme::Theme(const QString &themePath, const QString &dirName, const QString &defaultDesktopFileName)
0093     : d(new ThemePrivate)
0094 {
0095     const QString themeInfoFile = themePath + QLatin1Char('/') + defaultDesktopFileName;
0096     KConfig config(themeInfoFile);
0097     KConfigGroup group(&config, QStringLiteral("Desktop Entry"));
0098     if (group.isValid()) {
0099         d->dirName = dirName;
0100         d->absolutePaths.append(themePath);
0101         d->name = group.readEntry("Name", QString());
0102         d->description = group.readEntry("Description", QString());
0103         d->themeFileName = group.readEntry("FileName", QString());
0104         d->displayExtraVariables = group.readEntry("DisplayExtraVariables", QStringList());
0105     }
0106 }
0107 
0108 Theme::Theme(const Theme &other)
0109 
0110     = default;
0111 
0112 Theme::~Theme() = default;
0113 
0114 bool Theme::operator==(const Theme &other) const
0115 {
0116     return isValid() && other.isValid() && d->absolutePaths == other.d->absolutePaths;
0117 }
0118 
0119 Theme &Theme::operator=(const Theme &other)
0120 {
0121     if (this != &other) {
0122         d = other.d;
0123     }
0124 
0125     return *this;
0126 }
0127 
0128 bool Theme::isValid() const
0129 {
0130     return !d->themeFileName.isEmpty() && !d->name.isEmpty();
0131 }
0132 
0133 QString Theme::description() const
0134 {
0135     return d->description;
0136 }
0137 
0138 QString Theme::themeFilename() const
0139 {
0140     return d->themeFileName;
0141 }
0142 
0143 QString Theme::name() const
0144 {
0145     return d->name;
0146 }
0147 
0148 QStringList Theme::displayExtraVariables() const
0149 {
0150     return d->displayExtraVariables;
0151 }
0152 
0153 QString Theme::dirName() const
0154 {
0155     return d->dirName;
0156 }
0157 
0158 QString Theme::absolutePath() const
0159 {
0160     if (!d->absolutePaths.isEmpty()) {
0161         return d->absolutePaths.at(0); // ####
0162     }
0163     return {};
0164 }
0165 
0166 QString Theme::author() const
0167 {
0168     return d->author;
0169 }
0170 
0171 QString Theme::authorEmail() const
0172 {
0173     return d->email;
0174 }
0175 
0176 void Theme::addThemePath(const QString &path)
0177 {
0178     d->absolutePaths.append(path);
0179 }
0180 
0181 QString Theme::render(const QString &templateName, const QVariantHash &data, const QByteArray &applicationDomain)
0182 {
0183     if (!d->loader) {
0184         d->setupLoader();
0185     }
0186     Q_ASSERT(d->loader);
0187 
0188     if (!d->loader->canLoadTemplate(templateName)) {
0189         qCWarning(GRANTLEETHEME_LOG) << "Cannot load template" << templateName
0190                                      << ", please check your installation. Tried in these dirs:" << d->loader->templateDirs();
0191         return {};
0192     }
0193     KTextTemplate::Template tpl = d->loader->loadByName(templateName, ThemePrivate::sEngine);
0194     if (!tpl || tpl->error()) {
0195         return d->errorTemplate(i18n("Template parsing error"), templateName, tpl);
0196     }
0197     KTextTemplate::Context ctx = d->createContext(data, applicationDomain);
0198     const QString result = tpl->render(&ctx);
0199     if (tpl->error()) {
0200         return d->errorTemplate(i18n("Template rendering error"), templateName, tpl);
0201     }
0202 
0203     return result;
0204 }
0205 
0206 void Theme::addPluginPath(const QString &path)
0207 {
0208     if (!ThemePrivate::sEngine) {
0209         ThemePrivate::setupEngine();
0210     }
0211 
0212     QStringList paths = ThemePrivate::sEngine->pluginPaths();
0213     if (!paths.contains(path)) {
0214         paths.prepend(path);
0215         ThemePrivate::sEngine->setPluginPaths(paths);
0216     }
0217 }