[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    [kdepim] /: Repurpose GrantleeTheme to also support rendering of the templates
From:       Dan_Vrátil <dvratil () redhat ! com>
Date:       2015-10-14 12:13:55
Message-ID: E1ZmKwN-0008Nn-OX () scm ! kde ! org
[Download RAW message or body]

Git commit b9c428ce0417feaa6eb0d3d8c5737f143f55339e by Dan Vrátil.
Committed on 14/10/2015 at 12:13.
Pushed by dvratil into branch 'master'.

Repurpose GrantleeTheme to also support rendering of the templates

The way we use Grantlee in PIM is basically the same everywhere, so
let's wrap the Grantlee setup and handling code into a common library
that can be used by all parts of PIM to handle and render themes with
Grantlee templates.

REVIEW: 125577

M  +3    -0    grantleetheme/CMakeLists.txt
A  +10   -0    grantleetheme/autotest/data/valid/header.html
A  +30   -0    grantleetheme/autotest/data/valid/header_expected.html
A  +36   -0    grantleetheme/autotest/data/valid/main.html
M  +111  -0    grantleetheme/autotest/grantleethemetest.cpp
M  +7    -0    grantleetheme/autotest/grantleethemetest.h
M  +11   -2    grantleetheme/src/CMakeLists.txt
A  +25   -0    grantleetheme/src/config-grantleetheme.h.cmake
A  +112  -0    grantleetheme/src/grantleeki18nlocalizer.cpp     [License: LGPL \
(v2.1+)] A  +55   -0    grantleetheme/src/grantleeki18nlocalizer_p.h     [License: \
LGPL (v2.1+)] M  +157  -46   grantleetheme/src/grantleetheme.cpp
M  +25   -25   grantleetheme/src/grantleetheme.h
A  +62   -0    grantleetheme/src/grantleetheme_p.h     [License: GPL (v2)]
M  +50   -38   grantleetheme/src/grantleethememanager.cpp
M  +12   -10   grantleetheme/src/grantleethememanager.h
A  +17   -0    grantleetheme/src/plugin/CMakeLists.txt
A  +151  -0    grantleetheme/src/plugin/icon.cpp     [License: LGPL (v2.1+)]
A  +79   -0    grantleetheme/src/plugin/icon.h     [License: LGPL (v2.1+)]
A  +46   -0    grantleetheme/src/plugin/kdegrantleeplugin.cpp     [License: LGPL \
(v2.1+)] A  +40   -0    grantleetheme/src/plugin/kdegrantleeplugin.h     [License: \
LGPL (v2.1+)] M  +6    -3    kaddressbook/mainwidget.cpp
M  +2    -2    kaddressbook/mainwidget.h
M  +1    -1    kaddressbookgrantlee/src/widget/grantleecontactviewer.cpp
M  +1    -0    kdepim.categories
M  +5    -3    messageviewer/src/header/grantleeheaderstyleplugin/grantleeheaderstyleinterface.cpp
 M  +2    -2    messageviewer/src/header/grantleeheaderstyleplugin/grantleeheaderstyleinterface.h


http://commits.kde.org/kdepim/b9c428ce0417feaa6eb0d3d8c5737f143f55339e

diff --git a/grantleetheme/CMakeLists.txt b/grantleetheme/CMakeLists.txt
index f6d9b42..54e771f 100644
--- a/grantleetheme/CMakeLists.txt
+++ b/grantleetheme/CMakeLists.txt
@@ -24,11 +24,14 @@ include(ECMAddTests)
 set(KF5_VERSION "5.16.0")
 set(KDEPIM_LIB_VERSION "5.0.45")
 set(QT_REQUIRED_VERSION "5.4.0")
+set(GRANTLEE_VERSION "5.0.0")
 
 find_package(Qt5 ${QT_REQUIRED_VERSION} CONFIG REQUIRED Widgets Test Network)
 find_package(KF5I18n ${KF5_VERSION} CONFIG REQUIRED)
 find_package(KF5NewStuff ${KF5_VERSION} CONFIG REQUIRED)
 
+find_package(Grantlee5 ${GRANTLEE_VERSION} CONFIG REQUIRED)
+
 ecm_setup_version(${KDEPIM_LIB_VERSION} VARIABLE_PREFIX GRANTLEETHEME
                         VERSION_HEADER \
                "${CMAKE_CURRENT_BINARY_DIR}/grantleetheme_version.h"
                         PACKAGE_VERSION_FILE \
                "${CMAKE_CURRENT_BINARY_DIR}/KF5GrantleeThemeConfigVersion.cmake"
diff --git a/grantleetheme/autotest/data/valid/header.html \
b/grantleetheme/autotest/data/valid/header.html new file mode 100644
index 0000000..e440f66
--- /dev/null
+++ b/grantleetheme/autotest/data/valid/header.html
@@ -0,0 +1,10 @@
+{% extends "main.html" %}
+
+{% block box %}
+
+<div class="center">
+    <h2>{{ title }}</h2>
+    <p>{{ subtext }}</p>
+</div>
+
+{% endblock box %}
\ No newline at end of file
diff --git a/grantleetheme/autotest/data/valid/header_expected.html \
b/grantleetheme/autotest/data/valid/header_expected.html new file mode 100644
index 0000000..8b2a501
--- /dev/null
+++ b/grantleetheme/autotest/data/valid/header_expected.html
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" \
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html \
xmlns="http://www.w3.org/1999/xhtml"> +  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF8" />
+    <style type="text/css">
+  /*<![CDATA[*/
+    @import "main.css";
+  /*]]>*/
+
+  </style>
+    <title>KMail</title>
+  </head>
+  <body>
+    <div id="header">
+      <img src="file:kmail.svg" align="top" height="128" width="128" alt="kmail" \
title="" /> +      <div id="title"><h1>KMail</h1>...just rocks!
+    </div>
+    </div>
+    <div id="box">
+      <div id="boxInner">
+        <div class="center">
+          <h2>Something's going on</h2>
+          <p>Please wait, it will be over soon.</p>
+        </div>
+      </div>
+    </div>
+  </body>
+</html>
+<!-- vim:set sw=2 et nocindent smartindent: -->
diff --git a/grantleetheme/autotest/data/valid/main.html \
b/grantleetheme/autotest/data/valid/main.html new file mode 100644
index 0000000..d80bf74
--- /dev/null
+++ b/grantleetheme/autotest/data/valid/main.html
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" \
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+  <style type="text/css">
+  /*<![CDATA[*/
+    @import "main.css";
+  /*]]>*/
+
+  {% block style %}
+  {% endblock style %}
+  </style>
+
+  <title>{{ name }}</title>
+</head>
+
+<body>
+  <div id="header">
+    {% icon icon sizeenormous %}
+    <div id="title">
+        <h1>{{ name }}</h1>
+        {{ subtitle }}
+    </div>
+  </div>
+
+  <div id="box">
+    <div id="boxInner">
+      {% block box %}
+      {% endblock box %}
+    </div>
+  </div>
+</body>
+</html>
+
+<!-- vim:set sw=2 et nocindent smartindent: -->
diff --git a/grantleetheme/autotest/grantleethemetest.cpp \
b/grantleetheme/autotest/grantleethemetest.cpp index fb6a4cf..8d33b87 100644
--- a/grantleetheme/autotest/grantleethemetest.cpp
+++ b/grantleetheme/autotest/grantleethemetest.cpp
@@ -17,8 +17,10 @@
 
 #include "grantleethemetest.h"
 #include "grantleetheme.h"
+
 #include <qtest.h>
 #include <QDir>
+#include <QProcess>
 
 GrantleeThemeTest::GrantleeThemeTest(QObject *parent)
     : QObject(parent)
@@ -89,4 +91,113 @@ void GrantleeThemeTest::shouldLoadTheme()
     QCOMPARE(theme.dirName(), dirname);
 }
 
+
+
+bool GrantleeThemeTest::validateHtml(const QString &themePath, const QString &name, \
const QString &html) +{
+    const QString outFileName = themePath + QStringLiteral("/%1.out").arg(name);
+    const QString htmlFileName = themePath + \
QStringLiteral("/%1.out.html").arg(name); +    QFile outFile(outFileName);
+    if (!outFile.open(QIODevice::WriteOnly)) {
+        return false;
+    }
+    outFile.write(html.toUtf8());
+    outFile.close();
+
+    // validate xml and pretty-print for comparisson
+    // TODO add proper cmake check for xmllint and diff
+    const QStringList args = {
+        QStringLiteral("--format"),
+        QStringLiteral("--encode"),
+        QStringLiteral("UTF8"),
+        QStringLiteral("--output"),
+        htmlFileName,
+        outFileName
+    };
+
+    const int result = QProcess::execute(QStringLiteral("xmllint"), args);
+    return result == 0;
+}
+
+bool GrantleeThemeTest::compareHtml(const QString &themePath, const QString &name)
+{
+    const QString htmlFileName = themePath + \
QStringLiteral("/%1.out.html").arg(name); +    const QString referenceFileName = \
themePath + QStringLiteral("/%1_expected.html").arg(name); +
+    // get rid of system dependent or random paths
+    {
+        QFile f(htmlFileName);
+        if (!f.open(QIODevice::ReadOnly)) {
+            return false;
+        }
+        QString content = QString::fromUtf8(f.readAll());
+        f.close();
+        content.replace(QRegExp(QLatin1String("\"file:[^\"]*[/(?:%2F)]([^\"/(?:%2F)]*)\"")), \
QStringLiteral("\"file:\\1\"")); +        if (!f.open(QIODevice::WriteOnly | \
QIODevice::Truncate)) { +            return false;
+        }
+        f.write(content.toUtf8());
+        f.close();
+    }
+
+    // compare to reference file
+    const QStringList args = {
+        QStringLiteral("-u"),
+        referenceFileName,
+        htmlFileName
+    };
+
+    QProcess proc;
+    proc.setProcessChannelMode(QProcess::ForwardedChannels);
+    proc.start(QLatin1String("diff"), args);
+    if (!proc.waitForFinished()) {
+        return false;
+    }
+
+    return proc.exitCode() == 0;
+}
+
+
+void GrantleeThemeTest::testRenderTemplate_data()
+{
+    QTest::addColumn<QString>("dirname");
+    QTest::addColumn<bool>("isValid");
+    QTest::addColumn<QString>("filename");
+    QTest::addColumn<QString>("templateBasename");
+
+    QTest::newRow("valid theme") << QStringLiteral("valid") << true << \
QStringLiteral("filename.testdesktop") << QStringLiteral("header"); +    \
QTest::newRow("invalid theme") << QStringLiteral("invalid") << false << \
QStringLiteral("filename.testdesktop") << QString(); +}
+
+void GrantleeThemeTest::testRenderTemplate()
+{
+    QFETCH(QString, dirname);
+    QFETCH(bool, isValid);
+    QFETCH(QString, filename);
+    QFETCH(QString, templateBasename);
+
+    const QString themePath = QStringLiteral(GRANTLEETHEME_DATA_DIR) + \
QDir::separator() + dirname; +
+    QVariantHash data;
+    data[QStringLiteral("icon")] = QStringLiteral("kmail");
+    data[QStringLiteral("name")] = QStringLiteral("KMail");
+    data[QStringLiteral("subtitle")] = QStringLiteral("...just rocks!");
+    data[QStringLiteral("title")] = QStringLiteral("Something's going on");
+    data[QStringLiteral("subtext")] = QStringLiteral("Please wait, it will be over \
soon."); +
+    GrantleeTheme::Theme theme(themePath, dirname, filename);
+    QCOMPARE(theme.isValid(), isValid);
+
+    if (isValid) {
+        const QString result = theme.render(templateBasename + \
QStringLiteral(".html"), data); +
+        QVERIFY(validateHtml(themePath, templateBasename, result));
+        QVERIFY(compareHtml(themePath, templateBasename));
+
+        QFile::remove(themePath + QDir::separator() + templateBasename + \
QStringLiteral(".out")); +        QFile::remove(themePath + QDir::separator() + \
templateBasename + QStringLiteral(".out.html")); +    }
+}
+
+
 QTEST_MAIN(GrantleeThemeTest)
diff --git a/grantleetheme/autotest/grantleethemetest.h \
b/grantleetheme/autotest/grantleethemetest.h index bb55bf4..d701338 100644
--- a/grantleetheme/autotest/grantleethemetest.h
+++ b/grantleetheme/autotest/grantleethemetest.h
@@ -31,6 +31,13 @@ private Q_SLOTS:
     void shouldInvalidWhenPathIsNotValid();
     void shouldLoadTheme_data();
     void shouldLoadTheme();
+
+    void testRenderTemplate_data();
+    void testRenderTemplate();
+
+private:
+    bool validateHtml(const QString &path, const QString &name, const QString \
&html); +    bool compareHtml(const QString &path, const QString &name);
 };
 
 #endif // GRANTLEETHEMETEST_H
diff --git a/grantleetheme/src/CMakeLists.txt b/grantleetheme/src/CMakeLists.txt
index fffaf15..89b65bd 100644
--- a/grantleetheme/src/CMakeLists.txt
+++ b/grantleetheme/src/CMakeLists.txt
@@ -2,15 +2,22 @@ add_definitions( -DQT_NO_CAST_FROM_ASCII )
 add_definitions( -DQT_NO_CAST_TO_ASCII )
 add_definitions(-DTRANSLATION_DOMAIN=\"libgrantleetheme\")
 
+add_subdirectory(plugin)
+
 set(libgrantleetheme_SRCS
     grantleetheme.cpp
     grantleethememanager.cpp
+    grantleeki18nlocalizer.cpp
 )
 
+configure_file(config-grantleetheme.h.cmake \
${CMAKE_CURRENT_BINARY_DIR}/config-grantleetheme.h) +
 kconfig_add_kcfg_files(libgrantleetheme_SRCS
   globalsettings_grantleetheme.kcfgc
 )
 
+ecm_qt_declare_logging_category(libgrantleetheme_SRCS HEADER grantleetheme_debug.h \
IDENTIFIER GRANTLEETHEME_LOG CATEGORY_NAME log_grantleetheme) +
 add_library(KF5GrantleeTheme ${libgrantleetheme_SRCS})
 generate_export_header(KF5GrantleeTheme BASE_NAME grantleetheme)
 
@@ -21,8 +28,10 @@ target_include_directories(KF5GrantleeTheme PUBLIC \
"$<BUILD_INTERFACE:${Grantlee  
 
 target_link_libraries(KF5GrantleeTheme
-PRIVATE
-  KF5::NewStuff KF5::I18n
+  PRIVATE
+    KF5::NewStuff
+    KF5::I18n
+    Grantlee5::Templates
 )
 
 set_target_properties(KF5GrantleeTheme PROPERTIES
diff --git a/grantleetheme/src/config-grantleetheme.h.cmake \
b/grantleetheme/src/config-grantleetheme.h.cmake new file mode 100644
index 0000000..7287479
--- /dev/null
+++ b/grantleetheme/src/config-grantleetheme.h.cmake
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2015  Volker Krause <vkrause@kde.org>
+ *
+ * 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 CONFIG_GRANTLEETHEME_H
+#define CONFIG_GRANTLEETHEME_H
+
+#define GRANTLEE_PLUGIN_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${LIB_INSTALL_DIR}"
+
+#endif
\ No newline at end of file
diff --git a/grantleetheme/src/grantleeki18nlocalizer.cpp \
b/grantleetheme/src/grantleeki18nlocalizer.cpp new file mode 100644
index 0000000..5ea7d1c
--- /dev/null
+++ b/grantleetheme/src/grantleeki18nlocalizer.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2015  Daniel Vrátil <dvratil@redhat.com>
+ *
+ * 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 "grantleeki18nlocalizer_p.h"
+#include "grantleetheme_debug.h"
+
+#include <QLocale>
+#include <QDate>
+
+#include <grantlee/safestring.h>
+
+#include <KLocalizedString>
+
+GrantleeKi18nLocalizer::GrantleeKi18nLocalizer()
+    : Grantlee::QtLocalizer()
+{
+}
+
+GrantleeKi18nLocalizer::~GrantleeKi18nLocalizer()
+{
+}
+
+
+QString GrantleeKi18nLocalizer::processArguments(const KLocalizedString &kstr,
+                                                 const QVariantList &arguments) \
const +{
+    KLocalizedString str = kstr;
+    for (auto iter = arguments.cbegin(), end = arguments.cend(); iter != end; \
++iter) { +        switch (iter->type()) {
+        case QVariant::String:
+            str = str.subs(iter->toString());
+            break;
+        case QVariant::Int:
+            str = str.subs(iter->toInt());
+            break;
+        case QVariant::UInt:
+            str = str.subs(iter->toUInt());
+            break;
+        case QVariant::LongLong:
+            str = str.subs(iter->toLongLong());
+            break;
+        case QVariant::ULongLong:
+            str = str.subs(iter->toULongLong());
+            break;
+        case QVariant::Char:
+            str = str.subs(iter->toChar());
+            break;
+        case QVariant::Double:
+            str = str.subs(iter->toDouble());
+            break;
+        case QVariant::UserType:
+            if (iter->canConvert<Grantlee::SafeString>()) {
+                str = str.subs(iter->value<Grantlee::SafeString>().get());
+                break;
+            }
+            // fall-through
+        default:
+            qCWarning(GRANTLEETHEME_LOG) << "Unknown type" << iter->typeName() << \
"(" << iter->type() << ")"; +            break;
+        }
+    }
+
+    // Return localized in the currenctly active locale
+    return str.toString({ currentLocale() });
+}
+
+QString GrantleeKi18nLocalizer::localizeContextString(const QString &string, const \
QString &context, const QVariantList &arguments) const +{
+    const KLocalizedString str = kxi18nc(qPrintable(context), qPrintable(string));
+    return processArguments(str, arguments);
+}
+
+QString GrantleeKi18nLocalizer::localizeString(const QString &string, const \
QVariantList &arguments) const +{
+    const KLocalizedString str = kxi18n(qPrintable(string));
+    return processArguments(str, arguments);
+}
+
+QString GrantleeKi18nLocalizer::localizePluralContextString(const QString &string, \
const QString &pluralForm, +                                                          \
const QString &context, const QVariantList &arguments) const +{
+    const KLocalizedString str = kxi18ncp(qPrintable(context), qPrintable(string), \
qPrintable(pluralForm)); +    return processArguments(str, arguments);
+}
+
+QString GrantleeKi18nLocalizer::localizePluralString(const QString &string, const \
QString &pluralForm, +                                                     const \
QVariantList &arguments) const +{
+    const KLocalizedString str = kxi18np(qPrintable(string), \
qPrintable(pluralForm)); +    return processArguments(str, arguments);
+}
+
+QString GrantleeKi18nLocalizer::localizeMonetaryValue(qreal value, const QString \
&currencySymbol) const +{
+    return QLocale(currentLocale()).toCurrencyString(value, currencySymbol);
+}
diff --git a/grantleetheme/src/grantleeki18nlocalizer_p.h \
b/grantleetheme/src/grantleeki18nlocalizer_p.h new file mode 100644
index 0000000..3f757d8
--- /dev/null
+++ b/grantleetheme/src/grantleeki18nlocalizer_p.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015  Daniel Vrátil <dvratil@redhat.com>
+ *
+ * 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 GRANTLEEKI18NLOCALIZER_H
+#define GRANTLEEKI18NLOCALIZER_H
+
+#include <grantlee/qtlocalizer.h>
+
+#include <QLocale>
+#include <QVariantHash>
+
+class KLocalizedString;
+
+class GrantleeKi18nLocalizer : public Grantlee::QtLocalizer
+{
+public:
+    explicit GrantleeKi18nLocalizer();
+    ~GrantleeKi18nLocalizer();
+
+    // Only reimplement string localization to use KLocalizedString instead of
+    // tr(), the remaining methods use QLocale internally, so we can reuse them
+    QString localizeContextString(const QString &string, const QString &context,
+                                  const QVariantList &arguments) const \
Q_DECL_OVERRIDE; +    QString localizeString(const QString &string, const \
QVariantList &arguments) const Q_DECL_OVERRIDE; +    QString \
localizePluralContextString(const QString &string, const QString &pluralForm, +       \
const QString &context, const QVariantList &arguments) const Q_DECL_OVERRIDE; +    \
QString localizePluralString(const QString &string, const QString &pluralForm, +      \
const QVariantList &arguments) const Q_DECL_OVERRIDE; +
+    // Only exception, Grantlee's implementation is not using QLocale for this
+    // for some reason
+    QString localizeMonetaryValue(qreal value, const QString &currenctCode) const \
Q_DECL_OVERRIDE; +
+private:
+    QString processArguments(const KLocalizedString &str,
+                             const QVariantList &arguments) const;
+};
+
+#endif // GRANTLEEKI18NLOCALIZER_H
diff --git a/grantleetheme/src/grantleetheme.cpp \
b/grantleetheme/src/grantleetheme.cpp index 94faacf..534c891 100644
--- a/grantleetheme/src/grantleetheme.cpp
+++ b/grantleetheme/src/grantleetheme.cpp
@@ -16,116 +16,227 @@
 */
 
 #include "grantleetheme.h"
+#include "grantleetheme_p.h"
+#include "grantleetheme_debug.h"
+#include "config-grantleetheme.h"
+
 #include <KConfig>
 #include <KConfigGroup>
+#include <KLocalizedString>
+
 #include <QDir>
+#include <QSharedPointer>
 
 using namespace GrantleeTheme;
 
-Theme::Theme()
+QSharedPointer<GrantleeKi18nLocalizer> GrantleeTheme::ThemePrivate::sLocalizer;
+Grantlee::Engine *GrantleeTheme::ThemePrivate::sEngine = Q_NULLPTR;
+
+ThemePrivate::ThemePrivate()
+    : QSharedData()
 {
 }
 
-Theme::Theme(const QString &themePath, const QString &dirName, const QString \
&defaultDesktopFileName) +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)
+    , author(other.author)
+    , email(other.email)
+    , loader(other.loader)
 {
-    const QString themeInfoFile = themePath + QDir::separator() + \
                defaultDesktopFileName;
-    KConfig config(themeInfoFile);
-    KConfigGroup group(&config, QStringLiteral("Desktop Entry"));
-    if (group.isValid()) {
-        setDirName(dirName);
-        setName(group.readEntry("Name", QString()));
-        setDescription(group.readEntry("Description", QString()));
-        setThemeFilename(group.readEntry("FileName", QString()));
-        setDisplayExtraVariables(group.readEntry("DisplayExtraVariables", \
                QStringList()));
-        setAbsolutePath(themePath);
+}
+
+ThemePrivate::~ThemePrivate()
+{
+}
+
+void ThemePrivate::setupEngine()
+{
+    sEngine = new Grantlee::Engine;
+    sEngine->addPluginPath(QStringLiteral(GRANTLEE_PLUGIN_INSTALL_DIR));
+    sEngine->addDefaultLibrary(QStringLiteral("grantlee_i18ntags"));
+    sEngine->addDefaultLibrary(QStringLiteral("kde_grantlee_plugin"));
+    sEngine->setSmartTrimEnabled(true);
+}
+
+void ThemePrivate::setupLoader()
+{
+    // Get the parent dir with themes, we set the theme directory separately
+    QDir dir(absolutePath);
+    dir.cdUp();
+
+    loader = QSharedPointer<Grantlee::FileSystemTemplateLoader>::create();
+    loader->setTemplateDirs({ dir.absolutePath() });
+    loader->setTheme(dirName);
+
+    if (!sEngine) {
+        ThemePrivate::setupEngine();
     }
+    sEngine->addTemplateLoader(loader);
 }
 
-Theme::~Theme()
+Grantlee::Context ThemePrivate::createContext(const QVariantHash& data)
 {
+    if (!sLocalizer) {
+        sLocalizer.reset(new GrantleeKi18nLocalizer());
+    }
+
+    Grantlee::Context ctx(data);
+    ctx.setLocalizer(sLocalizer);
+    return ctx;
 }
 
-bool Theme::isValid() const
+QString ThemePrivate::errorTemplate(const QString &reason,
+                                    const QString &origTemplateName,
+                                    const Grantlee::Template &failedTemplate)
 {
-    return !mThemeFileName.isEmpty() && !mName.isEmpty();
+    Grantlee::Template tpl = sEngine->newTemplate(
+        QStringLiteral("<h1>{{ error }}</h1>\n"
+                       "<b>%1:</b> {{ templateName }}<br>\n"
+                       "<b>%2:</b> {{ errorMessage }}")
+            .arg(i18n("Template"))
+            .arg(i18n("Error message")),
+        QStringLiteral("TemplateError"));
+
+    Grantlee::Context ctx = createContext();
+    ctx.insert(QStringLiteral("error"), reason);
+    ctx.insert(QStringLiteral("templateName"), origTemplateName);
+    ctx.insert(QStringLiteral("errorMessage"), failedTemplate->errorString());
+    return tpl->render(&ctx);
 }
 
-QString Theme::description() const
+
+
+Theme::Theme()
+    : d(new ThemePrivate)
 {
-    return mDescription;
 }
 
-void Theme::setDescription(const QString &description)
+Theme::Theme(const QString &themePath, const QString &dirName, const QString \
&defaultDesktopFileName) +    : d(new ThemePrivate)
 {
-    mDescription = description;
+    const QString themeInfoFile = themePath + QDir::separator() + \
defaultDesktopFileName; +    KConfig config(themeInfoFile);
+    KConfigGroup group(&config, QStringLiteral("Desktop Entry"));
+    if (group.isValid()) {
+        d->dirName = dirName;
+        d->absolutePath = 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()); +    }
 }
 
-QString Theme::themeFilename() const
+Theme::Theme(const Theme &other)
+    : d(other.d)
 {
-    return mThemeFileName;
 }
 
-void Theme::setThemeFilename(const QString &file)
+Theme::~Theme()
 {
-    mThemeFileName = file;
 }
 
-QString Theme::name() const
+bool Theme::operator==(const Theme &other) const
 {
-    return mName;
+    return isValid() && other.isValid() && d->absolutePath == other.absolutePath();
 }
 
-void Theme::setName(const QString &n)
+Theme &Theme::operator=(const Theme &other)
 {
-    mName = n;
+    if (this != &other) {
+        d = other.d;
+    }
+
+    return *this;
 }
 
-QStringList Theme::displayExtraVariables() const
+
+bool Theme::isValid() const
 {
-    return mDisplayExtraVariables;
+    return !d->themeFileName.isEmpty() && !d->name.isEmpty();
 }
 
-void Theme::setDisplayExtraVariables(const QStringList &variables)
+QString Theme::description() const
 {
-    mDisplayExtraVariables = variables;
+    return d->description;
 }
 
-void Theme::setDirName(const QString &name)
+QString Theme::themeFilename() const
 {
-    mDirName = name;
+    return d->themeFileName;
 }
 
-QString Theme::dirName() const
+QString Theme::name() const
 {
-    return mDirName;
+    return d->name;
 }
 
-void Theme::setAbsolutePath(const QString &absPath)
+QStringList Theme::displayExtraVariables() const
 {
-    mAbsolutePath = absPath;
+    return d->displayExtraVariables;
 }
 
-QString Theme::absolutePath() const
+QString Theme::dirName() const
 {
-    return mAbsolutePath;
+    return d->dirName;
 }
 
-void Theme::setAuthor(const QString &author)
+QString Theme::absolutePath() const
 {
-    mAuthor = author;
+    return d->absolutePath;
 }
 
 QString Theme::author() const
 {
-    return mAuthor;
+    return d->author;
 }
 
-void Theme::setAuthorEmail(const QString &email)
+QString Theme::authorEmail() const
 {
-    mEmail = email;
+    return d->email;
 }
 
-QString Theme::authorEmail() const
+QString Theme::render(const QString &templateName, const QVariantHash &data)
+{
+    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"; +        return QString();
+    }
+
+    Grantlee::Template tpl = d->loader->loadByName(templateName, \
ThemePrivate::sEngine); +    if (tpl->error()) {
+        return d->errorTemplate(i18n("Template parsing error"), templateName, tpl);
+    }
+
+    Grantlee::Context ctx = d->createContext(data);
+    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)
 {
-    return mEmail;
+    if (!ThemePrivate::sEngine) {
+        ThemePrivate::setupEngine();
+    }
+
+    QStringList paths = ThemePrivate::sEngine->pluginPaths();
+    if (!paths.contains(path)) {
+        paths.prepend(path);
+    }
+    ThemePrivate::sEngine->setPluginPaths(paths);
 }
diff --git a/grantleetheme/src/grantleetheme.h b/grantleetheme/src/grantleetheme.h
index 995ee58..f1e4b21 100644
--- a/grantleetheme/src/grantleetheme.h
+++ b/grantleetheme/src/grantleetheme.h
@@ -18,52 +18,52 @@
 #define GRANTLEETHEME_H
 
 #include "grantleetheme_export.h"
+
 #include <QString>
 #include <QStringList>
+#include <QVariantHash>
+#include <QSharedDataPointer>
+
+class GrantleeThemeTest;
+
 namespace GrantleeTheme
 {
+
+class ThemeManager;
+class ThemePrivate;
+
 class GRANTLEETHEME_EXPORT Theme
 {
 public:
-    Theme();
-    Theme(const QString &themePath, const QString &dirName, const QString \
&defaultDesktopFileName); +    explicit Theme();
+    Theme(const Theme &other);
     ~Theme();
 
+    bool operator==(const Theme &other) const;
+    Theme &operator=(const Theme &other);
+
     bool isValid() const;
 
     QString description() const;
-    void setDescription(const QString &description);
-
     QString themeFilename() const;
-    void setThemeFilename(const QString &file);
-
     QString name() const;
-    void setName(const QString &);
-
     QStringList displayExtraVariables() const;
-    void setDisplayExtraVariables(const QStringList &);
-
-    void setDirName(const QString &name);
     QString dirName() const;
-
-    void setAbsolutePath(const QString &absPath);
     QString absolutePath() const;
-
-    void setAuthor(const QString &);
     QString author() const;
-
-    void setAuthorEmail(const QString &);
     QString authorEmail() const;
 
+
+    QString render(const QString &templateName, const QVariantHash &data);
+
+    static void addPluginPath(const QString &path);
+
 private:
-    QStringList mDisplayExtraVariables;
-    QString mThemeFileName;
-    QString mDescription;
-    QString mName;
-    QString mDirName;
-    QString mAbsolutePath;
-    QString mAuthor;
-    QString mEmail;
+    friend class ::GrantleeThemeTest;
+    friend class ThemeManager;
+    Theme(const QString &themePath, const QString &dirName, const QString \
&defaultDesktopFileName); +
+    QSharedDataPointer<ThemePrivate> d;
 };
 }
 
diff --git a/grantleetheme/src/grantleetheme_p.h \
b/grantleetheme/src/grantleetheme_p.h new file mode 100644
index 0000000..5f59436
--- /dev/null
+++ b/grantleetheme/src/grantleetheme_p.h
@@ -0,0 +1,62 @@
+/*
+  Copyright (c) 2013-2015 Montel Laurent <montel@kde.org>
+
+  This program is free software; you can redistribute it and/or modify it
+  under the terms of the GNU General Public License, version 2, as
+  published by the Free Software Foundation.
+
+  This program 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along
+  with this program; if not, write to the Free Software Foundation, Inc.,
+  51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#ifndef GRANTLEETHEME_THEME_P_H
+#define GRANTLEETHEME_THEME_P_H
+
+#include "grantleetheme.h"
+#include "grantleeki18nlocalizer_p.h"
+
+#include <grantlee/template.h>
+#include <grantlee/templateloader.h>
+#include <grantlee/engine.h>
+
+namespace GrantleeTheme
+{
+
+class GRANTLEETHEME_NO_EXPORT ThemePrivate : public QSharedData
+{
+public:
+    ThemePrivate();
+    ThemePrivate(const ThemePrivate &other);
+    ~ThemePrivate();
+
+    static void setupEngine();
+    void setupLoader();
+    Grantlee::Context createContext(const QVariantHash &data = QVariantHash());
+    QString errorTemplate(const QString &reason,
+                          const QString &templateName,
+                          const Grantlee::Template &errorTemplate);
+
+    QStringList displayExtraVariables;
+    QString themeFileName;
+    QString description;
+    QString name;
+    QString dirName;
+    QString absolutePath;
+    QString author;
+    QString email;
+
+    QSharedPointer<Grantlee::FileSystemTemplateLoader> loader;
+
+    static QSharedPointer<GrantleeKi18nLocalizer> sLocalizer;
+    static Grantlee::Engine *sEngine;
+};
+
+}
+
+#endif
diff --git a/grantleetheme/src/grantleethememanager.cpp \
b/grantleetheme/src/grantleethememanager.cpp index 4e887a6..99e97fd 100644
--- a/grantleetheme/src/grantleethememanager.cpp
+++ b/grantleetheme/src/grantleethememanager.cpp
@@ -16,6 +16,7 @@
 */
 
 #include "grantleethememanager.h"
+#include "grantleetheme_p.h"
 #include "globalsettings_grantleetheme.h"
 
 #include <KDirWatch>
@@ -37,10 +38,13 @@
 
 using namespace GrantleeTheme;
 
-class Q_DECL_HIDDEN GrantleeThemeManager::Private
+class Q_DECL_HIDDEN ThemeManager::Private
 {
 public:
-    Private(GrantleeTheme::GrantleeThemeManager::Application type, const QString \
&desktopFileName, KActionCollection *ac, const QString &relativePath, \
GrantleeThemeManager *qq) +    Private(const QString &type,
+            const QString &desktopFileName,
+            KActionCollection *ac,
+            const QString &relativePath, ThemeManager *qq)
         : applicationType(type),
           defaultDesktopFileName(desktopFileName),
           actionGroup(0),
@@ -61,6 +65,20 @@ public:
         q->connect(downloadThemesAction, SIGNAL(triggered(bool)), q, \
                SLOT(slotDownloadHeaderThemes()));
         q->connect(watch, SIGNAL(dirty(QString)), SLOT(directoryChanged()));
         updateThemesPath(true);
+
+        // Migrate the old configuration format that only support mail and \
addressbook +        // theming to the new generic format
+        KSharedConfig::Ptr config = GrantleeSettings::self()->sharedConfig();
+        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()
@@ -127,7 +145,7 @@ public:
                             themeName = originalName + QStringLiteral(" \
(%1)").arg(i);  ++i;
                         }
-                        theme.setName(themeName);
+                        theme.d->name = themeName;
                     }
                     alreadyLoadedThemeName << themeName;
                     themes.insert(dirName, theme);
@@ -194,14 +212,8 @@ public:
     void selectTheme(KToggleAction *act)
     {
         if (act) {
-            switch (applicationType) {
-            case GrantleeThemeManager::Mail:
-                GrantleeSettings::self()->setGrantleeMailThemeName(act->data().toString());
                
-                break;
-            case GrantleeThemeManager::Addressbook:
-                GrantleeSettings::self()->setGrantleeAddressBookThemeName(act->data().toString());
                
-                break;
-            }
+            KConfigGroup group = \
GrantleeSettings::self()->sharedConfig()->group(applicationType); +            \
group.writeEntry(QStringLiteral("themeName"), act->data().toString());  \
GrantleeSettings::self()->save();  }
     }
@@ -217,15 +229,9 @@ public:
 
     KToggleAction *actionForTheme()
     {
-        QString themeName;
-        switch (applicationType) {
-        case GrantleeThemeManager::Mail:
-            themeName = GrantleeSettings::self()->grantleeMailThemeName();
-            break;
-        case GrantleeThemeManager::Addressbook:
-            themeName = GrantleeSettings::self()->grantleeAddressBookThemeName();
-            break;
-        }
+        const KConfigGroup group = \
GrantleeSettings::self()->sharedConfig()->group(applicationType); +        const \
QString themeName = group.readEntry(QStringLiteral("themeName"), \
QStringLiteral("default")); +
         if (themeName.isEmpty()) {
             return 0;
         }
@@ -251,7 +257,7 @@ public:
             }
         }
     }
-    GrantleeThemeManager::Application applicationType;
+    QString applicationType;
     QString defaultDesktopFileName;
     QString downloadConfigFileName;
     QStringList themesDirectories;
@@ -265,25 +271,30 @@ public:
 
     QAction *downloadThemesAction;
     QWeakPointer<KNS3::DownloadDialog> downloadThemesDialog;
-    GrantleeThemeManager *q;
+    ThemeManager *q;
 };
 
-GrantleeThemeManager::GrantleeThemeManager(GrantleeTheme::GrantleeThemeManager::Application \
applicationType, const QString &defaultDesktopFileName, KActionCollection \
                *actionCollection, const QString &path, QObject *parent)
-    : QObject(parent), d(new Private(applicationType, defaultDesktopFileName, \
actionCollection, path, this)) +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))  {
 }
 
-GrantleeThemeManager::~GrantleeThemeManager()
+ThemeManager::~ThemeManager()
 {
     delete d;
 }
 
-QMap<QString, GrantleeTheme::Theme> GrantleeThemeManager::themes() const
+QMap<QString, GrantleeTheme::Theme> ThemeManager::themes() const
 {
     return d->themes;
 }
 
-void GrantleeThemeManager::setActionGroup(QActionGroup *actionGroup)
+void ThemeManager::setActionGroup(QActionGroup *actionGroup)
 {
     if (d->actionGroup != actionGroup) {
         d->actionGroup = actionGroup;
@@ -291,12 +302,12 @@ void GrantleeThemeManager::setActionGroup(QActionGroup \
*actionGroup)  }
 }
 
-KToggleAction *GrantleeThemeManager::actionForTheme()
+KToggleAction *ThemeManager::actionForTheme()
 {
     return d->actionForTheme();
 }
 
-void GrantleeThemeManager::setThemeMenu(KActionMenu *menu)
+void ThemeManager::setThemeMenu(KActionMenu *menu)
 {
     if (d->menu != menu) {
         d->menu = menu;
@@ -304,7 +315,7 @@ void GrantleeThemeManager::setThemeMenu(KActionMenu *menu)
     }
 }
 
-QStringList GrantleeThemeManager::displayExtraVariables(const QString &themename) \
const +QStringList ThemeManager::displayExtraVariables(const QString &themename) \
const  {
     QMapIterator<QString, GrantleeTheme::Theme> i(d->themes);
     while (i.hasNext()) {
@@ -316,20 +327,19 @@ QStringList GrantleeThemeManager::displayExtraVariables(const \
QString &themename  return QStringList();
 }
 
-GrantleeTheme::Theme GrantleeThemeManager::theme(const QString &themeName)
+GrantleeTheme::Theme ThemeManager::theme(const QString &themeName)
 {
-    if (d->themes.contains(themeName)) {
-        return d->themes.value(themeName);
-    }
-    return GrantleeTheme::Theme();
+    return d->themes.value(themeName);
 }
 
-void GrantleeThemeManager::setDownloadNewStuffConfigFile(const QString \
&configFileName) +void ThemeManager::setDownloadNewStuffConfigFile(const QString \
&configFileName)  {
     d->downloadConfigFileName = configFileName;
 }
 
-QString GrantleeThemeManager::pathFromThemes(const QString &themesRelativePath, \
const QString &themeName, const QString &defaultDesktopFileName) +QString \
ThemeManager::pathFromThemes(const QString &themesRelativePath, +                     \
const QString &themeName, +                                     const QString \
&defaultDesktopFileName)  {
     QStringList themesDirectories;
     if (!themesRelativePath.isEmpty()) {
@@ -358,7 +368,9 @@ QString GrantleeThemeManager::pathFromThemes(const QString \
&themesRelativePath,  return QString();
 }
 
-GrantleeTheme::Theme GrantleeThemeManager::loadTheme(const QString &themePath, const \
QString &dirName, const QString &defaultDesktopFileName) +GrantleeTheme::Theme \
ThemeManager::loadTheme(const QString &themePath, +                                   \
const QString &dirName, +                                             const QString \
&defaultDesktopFileName)  {
     const GrantleeTheme::Theme theme(themePath, dirName, defaultDesktopFileName);
     return theme;
diff --git a/grantleetheme/src/grantleethememanager.h \
b/grantleetheme/src/grantleethememanager.h index 669d2b8..8f2ab7e 100644
--- a/grantleetheme/src/grantleethememanager.h
+++ b/grantleetheme/src/grantleethememanager.h
@@ -29,17 +29,16 @@ class KActionMenu;
 
 namespace GrantleeTheme
 {
-class GRANTLEETHEME_EXPORT GrantleeThemeManager : public QObject
+class GRANTLEETHEME_EXPORT ThemeManager : public QObject
 {
     Q_OBJECT
 public:
-    enum Application {
-        Mail = 0,
-        Addressbook
-    };
-
-    explicit GrantleeThemeManager(GrantleeTheme::GrantleeThemeManager::Application \
applicationType, const QString &defaultDesktopFileName, KActionCollection \
                *actionCollection, const QString &path, QObject *parent = Q_NULLPTR);
-    ~GrantleeThemeManager();
+    explicit ThemeManager(const QString &themeType,
+                          const QString &defaultDesktopFileName,
+                          KActionCollection *actionCollection = Q_NULLPTR,
+                          const QString &path = QString(),
+                          QObject *parent = Q_NULLPTR);
+    ~ThemeManager();
 
     QMap<QString, GrantleeTheme::Theme> themes() const;
 
@@ -55,8 +54,11 @@ public:
 
     void setDownloadNewStuffConfigFile(const QString &configFileName);
 
-    static QString pathFromThemes(const QString &path, const QString &themeName, \
                const QString &defaultDesktopFilename);
-    static GrantleeTheme::Theme loadTheme(const QString &themePath, const QString \
&dirName, const QString &defaultDesktopFilename); +    static QString \
pathFromThemes(const QString &path, const QString &themeName, +                       \
const QString &defaultDesktopFilename); +    static GrantleeTheme::Theme \
loadTheme(const QString &themePath, +                                          const \
QString &dirName, +                                          const QString \
&defaultDesktopFilename);  
 Q_SIGNALS:
     void themesChanged();
diff --git a/grantleetheme/src/plugin/CMakeLists.txt \
b/grantleetheme/src/plugin/CMakeLists.txt new file mode 100644
index 0000000..f1564f0
--- /dev/null
+++ b/grantleetheme/src/plugin/CMakeLists.txt
@@ -0,0 +1,17 @@
+kde_enable_exceptions()
+
+set(grantleeplugin_SRCS
+    kdegrantleeplugin.cpp
+    icon.cpp
+)
+
+add_library(kde_grantlee_plugin MODULE ${grantleeplugin_SRCS})
+grantlee_adjust_plugin_name(kde_grantlee_plugin)
+target_link_libraries(kde_grantlee_plugin
+    Grantlee5::Templates
+    KF5::IconThemes
+)
+
+install(TARGETS kde_grantlee_plugin
+        LIBRARY DESTINATION \
${LIB_INSTALL_DIR}/grantlee/${Grantlee5_VERSION_MAJOR}.${Grantlee5_VERSION_MINOR}/ +)
diff --git a/grantleetheme/src/plugin/icon.cpp b/grantleetheme/src/plugin/icon.cpp
new file mode 100644
index 0000000..39e0ffa
--- /dev/null
+++ b/grantleetheme/src/plugin/icon.cpp
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2015  Daniel Vrátil <dvratil@redhat.com>
+ *
+ * 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 "icon.h"
+
+#include <grantlee/exception.h>
+#include <grantlee/parser.h>
+#include <grantlee/variable.h>
+
+#include <QDebug>
+
+#include <KIconLoader>
+
+IconTag::IconTag(QObject* parent)
+    : Grantlee::AbstractNodeFactory(parent)
+{
+}
+
+IconTag::~IconTag()
+{
+}
+
+Grantlee::Node *IconTag::getNode(const QString &tagContent, Grantlee::Parser *p) \
const +{
+    Q_UNUSED(p);
+
+    static QHash<QString, int> sizeOrGroupLookup
+        = { { QStringLiteral("desktop"), KIconLoader::Desktop },
+            { QStringLiteral("toolbar"), KIconLoader::Toolbar },
+            { QStringLiteral("maintoolbar"), KIconLoader::MainToolbar },
+            { QStringLiteral("small"), KIconLoader::Small },
+            { QStringLiteral("panel"), KIconLoader::Panel },
+            { QStringLiteral("dialog"), KIconLoader::Dialog },
+            { QStringLiteral("sizesmall"), KIconLoader::SizeSmall },
+            { QStringLiteral("sizesmallmedium"), KIconLoader::SizeSmallMedium },
+            { QStringLiteral("sizemedium"), KIconLoader::SizeMedium },
+            { QStringLiteral("sizelarge"), KIconLoader::SizeLarge },
+            { QStringLiteral("sizehuge"), KIconLoader::SizeHuge },
+            { QStringLiteral("sizeenormous"), KIconLoader::SizeEnormous }
+        };
+
+    const QStringList parts = smartSplit(tagContent);
+    const int partsSize = parts.size();
+    if (partsSize < 2) {
+        throw Grantlee::Exception(Grantlee::TagSyntaxError, QStringLiteral("icon tag \
takes at least 1 argument")); +    }
+    if (partsSize > 4) {
+        throw Grantlee::Exception(Grantlee::TagSyntaxError, QStringLiteral("icon tag \
takes at maximum 3 arguments, %1 given").arg(partsSize)); +    }
+
+    int sizeOrGroup = KIconLoader::Small;
+    QString altText;
+    if (partsSize >= 3) {
+        const QString sizeStr = parts.at(2);
+        bool ok = false;
+        // Try to convert to pixel size
+        sizeOrGroup = sizeStr.toInt(&ok);
+        if (!ok) {
+            // If failed, then try to map the string to one of tne enums
+            const auto size = sizeOrGroupLookup.constFind(sizeStr);
+            if (size == sizeOrGroupLookup.cend()) {
+                // If it's not  a valid size string, assume it's an alt text
+                altText = sizeStr;
+            } else {
+                sizeOrGroup = (*size);
+            }
+        }
+    }
+    if (partsSize == 4) {
+        altText = parts.at(3);
+    }
+
+    return new IconNode(parts.at(1), sizeOrGroup, altText);
+}
+
+
+
+IconNode::IconNode(QObject* parent)
+    : Grantlee::Node(parent)
+{
+}
+
+IconNode::IconNode(const QString &iconName, int sizeOrGroup, const QString &altText, \
QObject *parent) +    : Grantlee::Node(parent)
+    , mIconName(iconName)
+    , mAltText(altText)
+    , mSizeOrGroup(sizeOrGroup)
+{
+}
+
+IconNode::~IconNode()
+{
+}
+
+void IconNode::render(Grantlee::OutputStream *stream, Grantlee::Context *c) const
+{
+    Q_UNUSED(c);
+
+    QString iconName = mIconName;
+    if (iconName.startsWith(QLatin1Char('"')) && \
iconName.endsWith(QLatin1Char('"'))) { +        iconName = iconName.mid(1, \
iconName.size() - 2); +    } else {
+        const QVariant val = Grantlee::Variable(mIconName).resolve(c);
+        if (val.type() == QVariant::String) {
+            iconName = val.toString();
+        } else {
+            iconName = val.value<Grantlee::SafeString>().get();
+        }
+    }
+
+    QString altText;
+    if (!mAltText.isEmpty()) {
+        if (mAltText.startsWith(QLatin1Char('"')) && \
mAltText.endsWith(QLatin1Char('"'))) { +            altText = mAltText.mid(1, \
mAltText.size() - 2); +        } else {
+            const QVariant v = Grantlee::Variable(mAltText).resolve(c);
+            if (v.isValid()) {
+                if (v.canConvert<Grantlee::SafeString>()) {
+                    altText = v.value<Grantlee::SafeString>().get();
+                } else {
+                    altText = v.toString();
+                }
+            }
+        }
+    }
+
+    const QString html = QStringLiteral("<img src=\"file://%1\" align=\"top\" \
height=\"%2\" width=\"%2\" alt=\"%3\" title=\"%4\" />") +                            \
.arg(KIconLoader::global()->iconPath(iconName, mSizeOrGroup < KIconLoader::LastGroup \
? mSizeOrGroup : -mSizeOrGroup)) +                            .arg(mSizeOrGroup < \
KIconLoader::LastGroup ? +                                    \
IconSize(static_cast<KIconLoader::Group>(mSizeOrGroup)) +                             \
: mSizeOrGroup) +                            .arg(altText.isEmpty() ? iconName : \
altText) +                            .arg(altText); // title is intentionally blank \
if no alt is provided +    (*stream) << Grantlee::SafeString(html, \
Grantlee::SafeString::IsSafe); +}
diff --git a/grantleetheme/src/plugin/icon.h b/grantleetheme/src/plugin/icon.h
new file mode 100644
index 0000000..5b32802
--- /dev/null
+++ b/grantleetheme/src/plugin/icon.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2015  Daniel Vrátil <dvratil@redhat.com>
+ *
+ * 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 ICON_H
+#define ICON_H
+
+#include <grantlee/node.h>
+
+/**
+ * @name icon tag
+ * @brief Provides {% icon %} tag for inserting themed icons
+ *
+ * The syntax is:
+ * @code
+ * {% icon "icon-name"|var-with-icon-name [ sizeOrGroup ] [ alt text ] %}
+ * @endcode
+ *
+ * Where @p icon-name is a string literal with icon name, @p var-with-icon-name
+ * is a variable that contains a string with the icon name. @p sizeOrGrop is
+ * one of the KIconLoader::Group or KIconLoader::StdSizes enum values. The value
+ * is case-insensitive.
+ *
+ * The tag generates a full <img> HTML code:
+ * @code
+ * <img src="/usr/share/icons/[theme]/[type]/[size]/[icon-name].png" width="[width]" \
height="[height]"> + * @endcode
+ *
+ * The full path to the icon is resolved using KIconLoader::iconPath(). The
+ * @p width and @p height attributes are calculated based on current settings
+ * for icon sizes in KDE.
+ *
+ * @note Support for nested variables inside tags is non-standard for Grantlee
+ * tags, but makes it easier to use {% icon %} in sub-templates.
+ */
+
+
+class IconTag : public Grantlee::AbstractNodeFactory
+{
+public:
+    explicit IconTag(QObject *parent = Q_NULLPTR);
+    ~IconTag();
+
+    Grantlee::Node *getNode(const QString &tagContent, Grantlee::Parser *p) const \
Q_DECL_OVERRIDE; +};
+
+class IconNode : public Grantlee::Node
+{
+    Q_OBJECT
+public:
+    explicit IconNode(QObject *parent = Q_NULLPTR);
+    IconNode(const QString &iconName, int sizeOrGroup, const QString &altText, \
QObject *parent = Q_NULLPTR); +    ~IconNode();
+
+    void render(Grantlee::OutputStream *stream, Grantlee::Context *c) const \
Q_DECL_OVERRIDE; +
+private:
+    QString mIconName;
+    QString mAltText;
+    int mSizeOrGroup;
+};
+
+
+#endif // ICON_H
diff --git a/grantleetheme/src/plugin/kdegrantleeplugin.cpp \
b/grantleetheme/src/plugin/kdegrantleeplugin.cpp new file mode 100644
index 0000000..9c25254
--- /dev/null
+++ b/grantleetheme/src/plugin/kdegrantleeplugin.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2015  Daniel Vrátil <dvratil@redhat.com>
+ *
+ * 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 "kdegrantleeplugin.h"
+#include "icon.h"
+
+KDEGrantleePlugin::KDEGrantleePlugin(QObject *parent)
+    : QObject(parent)
+    , Grantlee::TagLibraryInterface()
+{
+}
+
+KDEGrantleePlugin::~KDEGrantleePlugin()
+{
+}
+
+QHash<QString, Grantlee::AbstractNodeFactory *> \
KDEGrantleePlugin::nodeFactories(const QString &name) +{
+    Q_UNUSED(name);
+
+    QHash<QString, Grantlee::AbstractNodeFactory *> nodeFactories;
+    nodeFactories[QStringLiteral("icon")] = new IconTag();
+
+    return nodeFactories;
+}
+
+QHash<QString, Grantlee::Filter *> KDEGrantleePlugin::filters(const QString& name)
+{
+    return Grantlee::TagLibraryInterface::filters(name);
+}
diff --git a/grantleetheme/src/plugin/kdegrantleeplugin.h \
b/grantleetheme/src/plugin/kdegrantleeplugin.h new file mode 100644
index 0000000..7f20027
--- /dev/null
+++ b/grantleetheme/src/plugin/kdegrantleeplugin.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2015  Daniel Vrátil <dvratil@redhat.com>
+ *
+ * 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 KDEGRANTLEEPLUGIN_H
+#define KDEGRANTLEEPLUGIN_H
+
+#include <grantlee/taglibraryinterface.h>
+
+class KDEGrantleePlugin : public QObject
+                              , public Grantlee::TagLibraryInterface
+{
+    Q_OBJECT
+    Q_INTERFACES(Grantlee::TagLibraryInterface)
+    Q_PLUGIN_METADATA(IID "org.kde.KDEGrantleePlugin")
+
+public:
+    explicit KDEGrantleePlugin(QObject *parent = Q_NULLPTR);
+    ~KDEGrantleePlugin();
+
+    QHash<QString, Grantlee::Filter *> filters(const QString  &name) \
Q_DECL_OVERRIDE; +    QHash<QString, Grantlee::AbstractNodeFactory *> \
nodeFactories(const QString &name) Q_DECL_OVERRIDE; +};
+
+#endif // KDEGRANTLEEPLUGIN_H
diff --git a/kaddressbook/mainwidget.cpp b/kaddressbook/mainwidget.cpp
index 734a3c3..cc37519 100644
--- a/kaddressbook/mainwidget.cpp
+++ b/kaddressbook/mainwidget.cpp
@@ -573,10 +573,13 @@ void MainWidget::setupGui()
 
 void MainWidget::setupActions(KActionCollection *collection)
 {
-    mGrantleeThemeManager = new \
GrantleeTheme::GrantleeThemeManager(GrantleeTheme::GrantleeThemeManager::Addressbook, \
QStringLiteral("theme.desktop"), collection, \
QStringLiteral("kaddressbook/viewertemplates/")); +    mGrantleeThemeManager = new \
GrantleeTheme::ThemeManager(QStringLiteral("addressbook"), +                          \
QStringLiteral("theme.desktop"), +                                                    \
collection, +                                                            \
                QStringLiteral("kaddressbook/viewertemplates/"));
     mGrantleeThemeManager->setDownloadNewStuffConfigFile(QStringLiteral("kaddressbook_themes.knsrc"));
                
-    connect(mGrantleeThemeManager, \
&GrantleeTheme::GrantleeThemeManager::grantleeThemeSelected, this, \
                &MainWidget::slotGrantleeThemeSelected);
-    connect(mGrantleeThemeManager, \
&GrantleeTheme::GrantleeThemeManager::updateThemes, this, \
&MainWidget::slotGrantleeThemesUpdated); +    connect(mGrantleeThemeManager, \
&GrantleeTheme::ThemeManager::grantleeThemeSelected, this, \
&MainWidget::slotGrantleeThemeSelected); +    connect(mGrantleeThemeManager, \
&GrantleeTheme::ThemeManager::updateThemes, this, \
&MainWidget::slotGrantleeThemesUpdated);  
     KActionMenu *themeMenu  = new KActionMenu(i18n("&Themes"), this);
     collection->addAction(QStringLiteral("theme_menu"), themeMenu);
diff --git a/kaddressbook/mainwidget.h b/kaddressbook/mainwidget.h
index 9c9946f..32924be 100644
--- a/kaddressbook/mainwidget.h
+++ b/kaddressbook/mainwidget.h
@@ -28,7 +28,7 @@
 
 namespace GrantleeTheme
 {
-class GrantleeThemeManager;
+class ThemeManager;
 }
 class QAction;
 namespace Akonadi
@@ -166,7 +166,7 @@ private:
     KXMLGUIClient *mXmlGuiClient;
     KAddressBookGrantlee::GrantleeContactFormatter *mFormatter;
     KAddressBookGrantlee::GrantleeContactGroupFormatter *mGroupFormatter;
-    GrantleeTheme::GrantleeThemeManager *mGrantleeThemeManager;
+    GrantleeTheme::ThemeManager *mGrantleeThemeManager;
     Akonadi::EntityTreeModel *entityTreeModel() const;
     QAction *mQuickSearchAction;
     QAction *mServerSideSubscription;
diff --git a/kaddressbookgrantlee/src/widget/grantleecontactviewer.cpp \
b/kaddressbookgrantlee/src/widget/grantleecontactviewer.cpp index 56c2a9f..183dc8e \
                100644
--- a/kaddressbookgrantlee/src/widget/grantleecontactviewer.cpp
+++ b/kaddressbookgrantlee/src/widget/grantleecontactviewer.cpp
@@ -41,7 +41,7 @@ QString GrantleeContactViewer::kaddressBookAbsoluteThemePath()
     if (themeName.isEmpty()) {
         themeName = QStringLiteral("default");
     }
-    const QString absolutePath = \
GrantleeTheme::GrantleeThemeManager::pathFromThemes(QStringLiteral("kaddressbook/viewertemplates/"), \
themeName, QStringLiteral("theme.desktop")); +    const QString absolutePath = \
GrantleeTheme::ThemeManager::pathFromThemes(QStringLiteral("kaddressbook/viewertemplates/"), \
themeName, QStringLiteral("theme.desktop"));  return absolutePath;
 }
 
diff --git a/kdepim.categories b/kdepim.categories
index 26dbac3..e21166b 100644
--- a/kdepim.categories
+++ b/kdepim.categories
@@ -69,3 +69,4 @@ log_createnoteplugin kdepim (create note plugin)
 log_createtodoplugin kdepim (create todo plugin)
 log_isgdshorturlengineplugin kdepim (shorturl engine isgd)
 log_ur1cashorturlengineplugin kdepim (shorturl engine ur1ca)
+log_grantleetheme kdepim (grantleetheme)
diff --git a/messageviewer/src/header/grantleeheaderstyleplugin/grantleeheaderstyleinterface.cpp \
b/messageviewer/src/header/grantleeheaderstyleplugin/grantleeheaderstyleinterface.cpp \
                index 50325c4..de1ccab 100644
--- a/messageviewer/src/header/grantleeheaderstyleplugin/grantleeheaderstyleinterface.cpp
                
+++ b/messageviewer/src/header/grantleeheaderstyleplugin/grantleeheaderstyleinterface.cpp
 @@ -37,10 +37,12 @@ GrantleeHeaderStyleInterface::~GrantleeHeaderStyleInterface()
 
 void GrantleeHeaderStyleInterface::createAction(KActionMenu *menu, QActionGroup \
*actionGroup, KActionCollection *ac)  {
-    mThemeManager = new \
GrantleeTheme::GrantleeThemeManager(GrantleeTheme::GrantleeThemeManager::Mail, \
QStringLiteral("header.desktop"), ac, QStringLiteral("messageviewer/themes/")); +    \
mThemeManager = new GrantleeTheme::ThemeManager(QStringLiteral("mail"), +             \
QStringLiteral("header.desktop"), ac, +                                               \
                QStringLiteral("messageviewer/themes/"));
     mThemeManager->setDownloadNewStuffConfigFile(QStringLiteral("messageviewer_header_themes.knsrc"));
                
-    connect(mThemeManager, \
&GrantleeTheme::GrantleeThemeManager::grantleeThemeSelected, this, \
                &GrantleeHeaderStyleInterface::slotGrantleeHeaders);
-    connect(mThemeManager, &GrantleeTheme::GrantleeThemeManager::updateThemes, this, \
&HeaderStyleInterface::styleUpdated); +    connect(mThemeManager, \
&GrantleeTheme::ThemeManager::grantleeThemeSelected, this, \
&GrantleeHeaderStyleInterface::slotGrantleeHeaders); +    connect(mThemeManager, \
&GrantleeTheme::ThemeManager::updateThemes, this, \
&HeaderStyleInterface::styleUpdated);  
     mThemeManager->setActionGroup(actionGroup);
     mThemeManager->setThemeMenu(menu);
diff --git a/messageviewer/src/header/grantleeheaderstyleplugin/grantleeheaderstyleinterface.h \
b/messageviewer/src/header/grantleeheaderstyleplugin/grantleeheaderstyleinterface.h \
                index 1fdf939..6696a4d 100644
--- a/messageviewer/src/header/grantleeheaderstyleplugin/grantleeheaderstyleinterface.h
                
+++ b/messageviewer/src/header/grantleeheaderstyleplugin/grantleeheaderstyleinterface.h
 @@ -22,7 +22,7 @@
 
 namespace GrantleeTheme
 {
-class GrantleeThemeManager;
+class ThemeManager;
 }
 
 namespace MessageViewer
@@ -40,7 +40,7 @@ public:
 private Q_SLOTS:
     void slotGrantleeHeaders();
 private:
-    GrantleeTheme::GrantleeThemeManager *mThemeManager;
+    GrantleeTheme::ThemeManager *mThemeManager;
 };
 
 }


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic