diff --git a/src/grantleetheme.cpp b/src/grantleetheme.cpp index 5881081..1da9b06 100644 --- a/src/grantleetheme.cpp +++ b/src/grantleetheme.cpp @@ -1,238 +1,248 @@ /* Copyright (c) 2013-2020 Laurent Montel This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "grantleetheme.h" #include "grantleetheme_p.h" #include "grantleetheme_debug.h" #include "grantleethemeengine.h" #include #include "qtresourcetemplateloader.h" #include #include #include #include #include using namespace GrantleeTheme; QSharedPointer GrantleeTheme::ThemePrivate::sLocalizer; Grantlee::Engine *GrantleeTheme::ThemePrivate::sEngine = nullptr; ThemePrivate::ThemePrivate() : QSharedData() { } ThemePrivate::ThemePrivate(const ThemePrivate &other) : QSharedData(other) , displayExtraVariables(other.displayExtraVariables) , themeFileName(other.themeFileName) , description(other.description) , name(other.name) , dirName(other.dirName) - , absolutePath(other.absolutePath) + , absolutePaths(other.absolutePaths) , author(other.author) , email(other.email) , loader(other.loader) { } ThemePrivate::~ThemePrivate() { } void ThemePrivate::setupEngine() { sEngine = new GrantleeTheme::Engine(); } void ThemePrivate::setupLoader() { // Get the parent dir with themes, we set the theme directory separately - QDir dir(absolutePath); - dir.cdUp(); + + QStringList templatePaths; + for (const QString &absolutePath : qAsConst(absolutePaths)) { + QDir dir(absolutePath); + dir.cdUp(); + templatePaths << dir.absolutePath(); + } loader = QSharedPointer::create(); - loader->setTemplateDirs({ dir.absolutePath() }); + loader->setTemplateDirs(templatePaths); loader->setTheme(dirName); if (!sEngine) { ThemePrivate::setupEngine(); } sEngine->addTemplateLoader(loader); } Grantlee::Context ThemePrivate::createContext(const QVariantHash &data, const QByteArray &applicationDomain) { if (!sLocalizer) { sLocalizer.reset(new GrantleeKi18nLocalizer()); } sLocalizer->setApplicationDomain(applicationDomain); Grantlee::Context ctx(data); ctx.setLocalizer(sLocalizer); return ctx; } QString ThemePrivate::errorTemplate(const QString &reason, const QString &origTemplateName, const Grantlee::Template &failedTemplate) { Grantlee::Template tpl = sEngine->newTemplate( QStringLiteral("

{{ error }}

\n" "%1: {{ templateName }}
\n" "%2: {{ errorMessage }}") .arg(i18n("Template"), i18n("Error message")), QStringLiteral("TemplateError")); Grantlee::Context ctx = createContext(); ctx.insert(QStringLiteral("error"), reason); ctx.insert(QStringLiteral("templateName"), origTemplateName); const QString errorString = failedTemplate ? failedTemplate->errorString() : i18n("(null template)"); ctx.insert(QStringLiteral("errorMessage"), errorString); return tpl->render(&ctx); } Theme::Theme() : d(new ThemePrivate) { } Theme::Theme(const QString &themePath, const QString &dirName, const QString &defaultDesktopFileName) : d(new ThemePrivate) { const QString themeInfoFile = themePath + QLatin1Char('/') + defaultDesktopFileName; KConfig config(themeInfoFile); KConfigGroup group(&config, QStringLiteral("Desktop Entry")); if (group.isValid()) { d->dirName = dirName; - d->absolutePath = themePath; + d->absolutePaths.append(themePath); d->name = group.readEntry("Name", QString()); d->description = group.readEntry("Description", QString()); d->themeFileName = group.readEntry("FileName", QString()); d->displayExtraVariables = group.readEntry("DisplayExtraVariables", QStringList()); } } Theme::Theme(const Theme &other) : d(other.d) { } Theme::~Theme() { } bool Theme::operator==(const Theme &other) const { - return isValid() && other.isValid() && d->absolutePath == other.absolutePath(); + return isValid() && other.isValid() && d->absolutePaths == other.d->absolutePaths; } Theme &Theme::operator=(const Theme &other) { if (this != &other) { d = other.d; } return *this; } bool Theme::isValid() const { return !d->themeFileName.isEmpty() && !d->name.isEmpty(); } QString Theme::description() const { return d->description; } QString Theme::themeFilename() const { return d->themeFileName; } QString Theme::name() const { return d->name; } QStringList Theme::displayExtraVariables() const { return d->displayExtraVariables; } QString Theme::dirName() const { return d->dirName; } QString Theme::absolutePath() const { - return d->absolutePath; + return d->absolutePaths.at(0); // #### } QString Theme::author() const { return d->author; } QString Theme::authorEmail() const { return d->email; } +void Theme::addThemePath(const QString &path) +{ + d->absolutePaths.append(path); +} + QString Theme::render(const QString &templateName, const QVariantHash &data, const QByteArray &applicationDomain) { if (!d->loader) { d->setupLoader(); } Q_ASSERT(d->loader); if (!d->loader->canLoadTemplate(templateName)) { - qCWarning(GRANTLEETHEME_LOG) << "Cannot load template" << templateName << ", please check your installation"; + qCWarning(GRANTLEETHEME_LOG) << "Cannot load template" << templateName << ", please check your installation. Tried in these dirs:" << d->loader->templateDirs(); return QString(); } Grantlee::Template tpl = d->loader->loadByName(templateName, ThemePrivate::sEngine); if (!tpl || tpl->error()) { return d->errorTemplate(i18n("Template parsing error"), templateName, tpl); } Grantlee::Context ctx = d->createContext(data, applicationDomain); const QString result = tpl->render(&ctx); if (tpl->error()) { return d->errorTemplate(i18n("Template rendering error"), templateName, tpl); } return result; } void Theme::addPluginPath(const QString &path) { if (!ThemePrivate::sEngine) { ThemePrivate::setupEngine(); } QStringList paths = ThemePrivate::sEngine->pluginPaths(); if (!paths.contains(path)) { paths.prepend(path); ThemePrivate::sEngine->setPluginPaths(paths); } } diff --git a/src/grantleetheme.h b/src/grantleetheme.h index ab1bd30..8f51a5a 100644 --- a/src/grantleetheme.h +++ b/src/grantleetheme.h @@ -1,70 +1,72 @@ /* Copyright (c) 2013-2020 Laurent Montel This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef GRANTLEETHEME_H #define GRANTLEETHEME_H #include "grantleetheme_export.h" #include #include #include #include class GrantleeThemeTest; namespace GrantleeTheme { class ThemeManager; class ThemePrivate; /** * @brief The Theme class */ class GRANTLEETHEME_EXPORT Theme { public: explicit Theme(); Theme(const Theme &other); ~Theme(); Q_REQUIRED_RESULT bool operator==(const Theme &other) const; Theme &operator=(const Theme &other); Q_REQUIRED_RESULT bool isValid() const; Q_REQUIRED_RESULT QString description() const; Q_REQUIRED_RESULT QString themeFilename() const; Q_REQUIRED_RESULT QString name() const; Q_REQUIRED_RESULT QStringList displayExtraVariables() const; Q_REQUIRED_RESULT QString dirName() const; Q_REQUIRED_RESULT QString absolutePath() const; Q_REQUIRED_RESULT QString author() const; Q_REQUIRED_RESULT QString authorEmail() const; + void addThemePath(const QString &path); + Q_REQUIRED_RESULT QString render(const QString &templateName, const QVariantHash &data, const QByteArray &applicationDomain = QByteArray()); static void addPluginPath(const QString &path); private: friend class ::GrantleeThemeTest; friend class ThemeManager; Theme(const QString &themePath, const QString &dirName, const QString &defaultDesktopFileName); QSharedDataPointer d; }; } #endif // GRANTLEETHEME_H diff --git a/src/grantleetheme_p.h b/src/grantleetheme_p.h index a9679e4..9c7754d 100644 --- a/src/grantleetheme_p.h +++ b/src/grantleetheme_p.h @@ -1,58 +1,58 @@ /* Copyright (c) 2013-2020 Laurent Montel This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef GRANTLEE_THEME_P_H #define GRANTLEE_THEME_P_H #include "grantleetheme.h" #include "grantleeki18nlocalizer.h" #include #include #include namespace GrantleeTheme { class GRANTLEETHEME_NO_EXPORT ThemePrivate : public QSharedData { public: ThemePrivate(); ThemePrivate(const ThemePrivate &other); ~ThemePrivate(); static void setupEngine(); void setupLoader(); Q_REQUIRED_RESULT Grantlee::Context createContext(const QVariantHash &data = QVariantHash(), const QByteArray &applicationDomain = QByteArray()); Q_REQUIRED_RESULT QString errorTemplate(const QString &reason, const QString &templateName, const Grantlee::Template &errorTemplate); QStringList displayExtraVariables; QString themeFileName; QString description; QString name; QString dirName; - QString absolutePath; + QStringList absolutePaths; QString author; QString email; QSharedPointer loader; static QSharedPointer sLocalizer; static Grantlee::Engine *sEngine; }; } #endif diff --git a/src/grantleethememanager.cpp b/src/grantleethememanager.cpp index 8ea9d47..ddd72e7 100644 --- a/src/grantleethememanager.cpp +++ b/src/grantleethememanager.cpp @@ -1,392 +1,396 @@ /* Copyright (c) 2013-2020 Laurent Montel This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "grantleethememanager.h" #include "grantleetheme_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace GrantleeTheme; class Q_DECL_HIDDEN ThemeManager::Private { public: Private(const QString &type, const QString &desktopFileName, KActionCollection *ac, const QString &relativePath, ThemeManager *qq) : applicationType(type) , defaultDesktopFileName(desktopFileName) , actionGroup(nullptr) , menu(nullptr) , actionCollection(ac) , q(qq) { watch = new KDirWatch(q); initThemesDirectories(relativePath); if (KAuthorized::authorize(QStringLiteral("ghns"))) { downloadThemesAction = new QAction(i18n("Download New Themes..."), q); downloadThemesAction->setIcon(QIcon::fromTheme(QStringLiteral("get-hot-new-stuff"))); if (actionCollection) { actionCollection->addAction(QStringLiteral("download_header_themes"), downloadThemesAction); } separatorAction = new QAction(q); separatorAction->setSeparator(true); q->connect(downloadThemesAction, &QAction::triggered, q, [this]() { slotDownloadHeaderThemes(); }); } q->connect(watch, &KDirWatch::dirty, q, [this]() { directoryChanged(); }); updateThemesPath(true); // Migrate the old configuration format that only support mail and addressbook // theming to the new generic format KSharedConfig::Ptr config = KSharedConfig::openConfig(); if (config->hasGroup(QStringLiteral("GrantleeTheme"))) { const KConfigGroup group = config->group(QStringLiteral("GrantleeTheme")); const QString mailTheme = group.readEntry(QStringLiteral("grantleeMailThemeName")); const QString addressbookTheme = group.readEntry(QStringLiteral("grantleeAddressBookThemeName")); config->group(QStringLiteral("mail")).writeEntry(QStringLiteral("themeName"), mailTheme); config->group(QStringLiteral("addressbook")).writeEntry(QStringLiteral("themeName"), addressbookTheme); config->deleteGroup(QStringLiteral("GrantleeTheme")); } } ~Private() { removeActions(); themes.clear(); if (downloadThemesDialog) { delete downloadThemesDialog.data(); } } void slotDownloadHeaderThemes() { if (!downloadThemesDialog) { downloadThemesDialog = new KNS3::DownloadDialog(downloadConfigFileName); } downloadThemesDialog.data()->show(); } void directoryChanged() { updateThemesPath(); updateActionList(); Q_EMIT q->updateThemes(); } void updateThemesPath(bool init = false) { if (!init) { if (!themesDirectories.isEmpty()) { for (const QString &directory : qAsConst(themesDirectories)) { watch->removeDir(directory); } } else { return; } } // clear all previous theme information themes.clear(); for (const QString &directory : qAsConst(themesDirectories)) { QDirIterator dirIt(directory, QStringList(), QDir::AllDirs | QDir::NoDotAndDotDot); QStringList alreadyLoadedThemeName; while (dirIt.hasNext()) { dirIt.next(); const QString dirName = dirIt.fileName(); GrantleeTheme::Theme theme = q->loadTheme(dirIt.filePath(), dirName, defaultDesktopFileName); if (theme.isValid()) { QString themeName = theme.name(); if (alreadyLoadedThemeName.contains(themeName)) { int i = 2; const QString originalName(theme.name()); while (alreadyLoadedThemeName.contains(themeName)) { themeName = originalName + QStringLiteral(" (%1)").arg(i); ++i; } theme.d->name = themeName; } alreadyLoadedThemeName << themeName; - themes.insert(dirName, theme); - //qDebug()<<" theme.name()"<addDir(directory); } Q_EMIT q->themesChanged(); watch->startScan(); } void removeActions() { if (!actionGroup || !menu) { return; } for (KToggleAction *action : qAsConst(themesActionList)) { actionGroup->removeAction(action); menu->removeAction(action); if (actionCollection) { actionCollection->removeAction(action); } } if (separatorAction) { menu->removeAction(separatorAction); menu->removeAction(downloadThemesAction); } themesActionList.clear(); } void updateActionList() { if (!actionGroup || !menu) { return; } QString themeActivated; QAction *selectedAction = actionGroup->checkedAction(); if (selectedAction) { themeActivated = selectedAction->data().toString(); } removeActions(); bool themeActivatedFound = false; QMapIterator i(themes); while (i.hasNext()) { i.next(); GrantleeTheme::Theme theme = i.value(); KToggleAction *act = new KToggleAction(theme.name(), q); act->setToolTip(theme.description()); act->setData(theme.dirName()); if (theme.dirName() == themeActivated) { act->setChecked(true); themeActivatedFound = true; } themesActionList.append(act); actionGroup->addAction(act); menu->addAction(act); q->connect(act, &KToggleAction::triggered, q, [this]() { slotThemeSelected(); }); } if (!themeActivatedFound) { if (!themesActionList.isEmpty() && !themeActivated.isEmpty()) { //Activate first item if we removed theme. KToggleAction *act = themesActionList.at(0); act->setChecked(true); selectTheme(act); } } if (separatorAction) { menu->addAction(separatorAction); menu->addAction(downloadThemesAction); } } void selectTheme(KToggleAction *act) { if (act) { KSharedConfig::Ptr config = KSharedConfig::openConfig(); KConfigGroup group = config->group(applicationType); group.writeEntry(QStringLiteral("themeName"), act->data().toString()); config->sync(); } } void slotThemeSelected() { if (q->sender()) { KToggleAction *act = qobject_cast(q->sender()); selectTheme(act); Q_EMIT q->grantleeThemeSelected(); } } KToggleAction *actionForTheme() { const KSharedConfig::Ptr config = KSharedConfig::openConfig(); const KConfigGroup group = config->group(applicationType); const QString themeName = group.readEntry(QStringLiteral("themeName"), QStringLiteral("default")); if (themeName.isEmpty()) { return nullptr; } for (KToggleAction *act : qAsConst(themesActionList)) { if (act->data().toString() == themeName) { return static_cast(act); } } return nullptr; } void initThemesDirectories(const QString &themesRelativePath) { if (!themesRelativePath.isEmpty()) { themesDirectories = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, themesRelativePath, QStandardPaths::LocateDirectory); const QString localDirectory = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + themesRelativePath; themesDirectories.append(localDirectory); } } QString applicationType; QString defaultDesktopFileName; QString downloadConfigFileName; QStringList themesDirectories; QMap themes; QVector themesActionList; KDirWatch *watch = nullptr; QActionGroup *actionGroup = nullptr; KActionMenu *menu = nullptr; KActionCollection *actionCollection = nullptr; QAction *separatorAction = nullptr; QAction *downloadThemesAction = nullptr; QPointer downloadThemesDialog; ThemeManager *q; }; ThemeManager::ThemeManager(const QString &applicationType, const QString &defaultDesktopFileName, KActionCollection *actionCollection, const QString &path, QObject *parent) : QObject(parent) , d(new Private(applicationType, defaultDesktopFileName, actionCollection, path, this)) { } ThemeManager::~ThemeManager() { delete d; } QMap ThemeManager::themes() const { return d->themes; } void ThemeManager::setActionGroup(QActionGroup *actionGroup) { if (d->actionGroup != actionGroup) { d->removeActions(); d->actionGroup = actionGroup; d->updateActionList(); } } KToggleAction *ThemeManager::actionForTheme() { return d->actionForTheme(); } void ThemeManager::setThemeMenu(KActionMenu *menu) { if (d->menu != menu) { d->menu = menu; d->updateActionList(); } } QStringList ThemeManager::displayExtraVariables(const QString &themename) const { QMapIterator i(d->themes); while (i.hasNext()) { i.next(); if (i.value().dirName() == themename) { return i.value().displayExtraVariables(); } } return QStringList(); } GrantleeTheme::Theme ThemeManager::theme(const QString &themeName) { return d->themes.value(themeName); } void ThemeManager::setDownloadNewStuffConfigFile(const QString &configFileName) { d->downloadConfigFileName = configFileName; } QString ThemeManager::pathFromThemes(const QString &themesRelativePath, const QString &themeName, const QString &defaultDesktopFileName) { QStringList themesDirectories; if (!themesRelativePath.isEmpty()) { themesDirectories = QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, themesRelativePath, QStandardPaths::LocateDirectory); if (themesDirectories.count() < 2) { //Make sure to add local directory const QString localDirectory = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + themesRelativePath; if (!themesDirectories.contains(localDirectory)) { themesDirectories.append(localDirectory); } } for (const QString &directory : qAsConst(themesDirectories)) { QDirIterator dirIt(directory, QStringList(), QDir::AllDirs | QDir::NoDotAndDotDot); while (dirIt.hasNext()) { dirIt.next(); const QString dirName = dirIt.fileName(); GrantleeTheme::Theme theme = loadTheme(dirIt.filePath(), dirName, defaultDesktopFileName); if (theme.isValid()) { if (dirName == themeName) { return theme.absolutePath(); } } } } } return QString(); } GrantleeTheme::Theme ThemeManager::loadTheme(const QString &themePath, const QString &dirName, const QString &defaultDesktopFileName) { const GrantleeTheme::Theme theme(themePath, dirName, defaultDesktopFileName); return theme; } QString ThemeManager::configuredThemeName() const { return configuredThemeName(d->applicationType); } QString ThemeManager::configuredThemeName(const QString &themeType) { const KSharedConfig::Ptr config = KSharedConfig::openConfig(); const KConfigGroup grp = config->group(themeType); return grp.readEntry(QStringLiteral("themeName")); } #include "moc_grantleethememanager.cpp"