1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights. These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
32 **************************************************************************/
34 /****************************************************************************
36 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
37 ** Contact: Nokia Corporation (qt-info@nokia.com)
39 ** This file is part of the $MODULE$ of the Qt Toolkit.
41 ** $TROLLTECH_DUAL_LICENSE$
43 ****************************************************************************/
45 #include "qts60stylethemeio.h"
47 #if !defined(QT_NO_STYLE_S60)
49 #include "qs60style.h"
50 #include "qapplication.h"
52 #include "qwebframe.h"
53 #include "qeventloop.h"
59 #include "qfileinfo.h"
60 #include "qxmlstream.h"
65 static const quint32 blobVersion = 1;
66 static const int pictureSize = 256;
68 void dumpPartPictures(const QHash<QString, QPicture> &partPictures) {
69 foreach (const QString &partKey, partPictures.keys()) {
70 QPicture partPicture = partPictures.value(partKey);
71 qDebug() << partKey << partPicture.boundingRect();
72 QImage image(partPicture.boundingRect().size(), QImage::Format_ARGB32);
73 image.fill(Qt::transparent);
76 image.save(partKey + QString::fromLatin1(".png"));
80 void dumpColors(const QHash<QPair<QString, int>, QColor> &colors) {
81 foreach (const QColor &color, colors.values()) {
82 const QPair<QString, int> key = colors.key(color);
83 qDebug() << key << color;
87 bool setS60Theme(QHash<QString, QPicture> &partPictures,
88 QHash<QPair<QString, int>, QColor> &colors,
92 s60Style = qobject_cast<QS60Style *>(QApplication::style());
93 if (!s60Style || !qobject_cast<QS60Style *>(s60Style)) {
94 qWarning() << __FUNCTION__ << ": No QS60Style found.";
97 s60Style->setS60Theme(partPictures, colors);
102 class WebKitSVGRenderer : public QWebView
107 WebKitSVGRenderer(QWidget *parent = 0);
108 QPicture svgToQPicture(const QString &svgFileName);
111 void loadFinishedSlot(bool ok);
118 WebKitSVGRenderer::WebKitSVGRenderer(QWidget *parent)
121 connect(this, SIGNAL(loadFinished(bool)), SLOT(loadFinishedSlot(bool)));
122 setFixedSize(pictureSize, pictureSize);
123 QPalette pal = palette();
124 pal.setColor(QPalette::Base, Qt::transparent);
128 QPicture WebKitSVGRenderer::svgToQPicture(const QString &svgFileName)
130 load(QUrl::fromLocalFile(svgFileName));
135 void WebKitSVGRenderer::loadFinishedSlot(bool ok)
137 // crude error-checking
139 qDebug() << "Failed loading " << qPrintable(url().toString());
141 page()->mainFrame()->evaluateJavaScript(
142 "document.rootElement.preserveAspectRatio.baseVal.align = SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_NONE;"
143 "document.rootElement.style.width = '100%';"
144 "document.rootElement.style.height = '100%';"
145 "document.rootElement.width.baseVal.newValueSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_PERCENTAGE, 100);"
146 "document.rootElement.height.baseVal.newValueSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_PERCENTAGE, 100);"
149 m_result = QPicture(); // "Clear"
150 QPainter p(&m_result);
151 page()->mainFrame()->render(&p);
153 m_result.setBoundingRect(QRect(0, 0, pictureSize, pictureSize));
158 bool parseTdfFile(const QString &tdfFile,
159 QHash<QString, QString> &partSvgs,
160 QHash<QPair<QString, int>, QColor> &colors)
162 const QLatin1String elementKey("element");
163 const QLatin1String partKey("part");
164 const QLatin1String elementIdKey("id");
165 const QLatin1String layerKey("layer");
166 const QLatin1String layerFileNameKey("filename");
167 const QLatin1String layerColourrgbKey("colourrgb");
168 const QString annoyingPrefix("S60_2_6%");
170 if (!file.open(QIODevice::ReadOnly))
172 QXmlStreamReader reader(&file);
174 QPair<QString, int> colorId;
175 // Somebody with a sense of aesthetics may implement proper XML parsing, here.
176 while (!reader.atEnd()) {
177 const QXmlStreamReader::TokenType token = reader.readNext();
179 case QXmlStreamReader::StartElement:
180 if (reader.name() == elementKey || reader.name() == partKey) {
181 QString id = reader.attributes().value(elementIdKey).toString();
182 if (QS60Style::partKeys().contains(id)) {
184 } else if (!id.isEmpty() && id.at(id.length()-1).isDigit()) {
186 idText.remove(QRegExp("[0-9]"));
187 if (QS60Style::colorListKeys().contains(idText)) {
188 QString idNumber = id;
189 idNumber.remove(QRegExp("[a-zA-Z]"));
190 colorId = QPair<QString, int>(idText, idNumber.toInt());
192 } else if (QS60Style::partKeys().contains(id.mid(annoyingPrefix.length()))) {
193 partId = id.mid(annoyingPrefix.length());
195 } else if (reader.name() == layerKey) {
196 if (!partId.isEmpty()) {
197 const QString svgFile = reader.attributes().value(layerFileNameKey).toString();
198 partSvgs.insert(partId, svgFile);
200 } else if (!colorId.first.isEmpty()) {
201 const QColor colorValue(reader.attributes().value(layerColourrgbKey).toString().toInt(NULL, 16));
202 colors.insert(colorId, colorValue);
203 colorId.first.clear();
207 case QXmlStreamReader::EndElement:
208 if (reader.tokenString() == elementKey || reader.name() == partKey)
218 bool loadThemeFromTdf(const QString &tdfFile,
219 QHash<QString, QPicture> &partPictures,
220 QHash<QPair<QString, int>, QColor> &colors)
222 QHash<QString, QString> parsedPartSvgs;
223 QHash<QString, QPicture> parsedPartPictures;
224 QHash<QPair<QString, int>, QColor> parsedColors;
225 bool success = parseTdfFile(tdfFile, parsedPartSvgs, parsedColors);
228 const QString tdfBasePath = QFileInfo(tdfFile).absolutePath();
229 WebKitSVGRenderer renderer;
230 foreach(const QString& partKey, parsedPartSvgs.keys()) {
231 const QString tdfFullName =
232 tdfBasePath + QDir::separator() + parsedPartSvgs.value(partKey);
233 if (!QFile(tdfFullName).exists())
234 qWarning() << "Could not load part " << tdfFullName;
235 const QPicture partPicture = renderer.svgToQPicture(tdfFullName);
236 parsedPartPictures.insert(partKey, partPicture);
238 // dumpPartPictures(parsedPartPictures);
239 // dumpColors(colors);
240 partPictures = parsedPartPictures;
241 colors = parsedColors;
245 bool QtS60StyleThemeIO::loadThemeFromTdf(const QString &themeTdf, QS60Style *s60Style)
247 QHash<QString, QPicture> partPictures;
248 QHash<QPair<QString, int>, QColor> colors;
250 if (!::loadThemeFromTdf(themeTdf, partPictures, colors))
253 return ::setS60Theme(partPictures, colors, s60Style);
256 bool QtS60StyleThemeIO::convertTdfToBlob(const QString &themeTdf, const QString &themeBlob)
258 QHash<QString, QPicture> partPictures;
259 QHash<QPair<QString, int>, QColor> colors;
261 if (!::loadThemeFromTdf(themeTdf, partPictures, colors))
264 QFile blob(themeBlob);
265 if (!blob.open(QIODevice::WriteOnly)) {
266 qWarning() << __FUNCTION__ << ": Could not create blob: " << themeBlob;
271 QBuffer dataBuffer(&data);
272 dataBuffer.open(QIODevice::WriteOnly);
273 QDataStream dataOut(&dataBuffer);
275 const int colorsCount = colors.count();
276 dataOut << colorsCount;
277 const QList<QPair<QString, int> > colorKeys = colors.keys();
278 for (int i = 0; i < colorsCount; ++i) {
279 const QPair<QString, int> &key = colorKeys.at(i);
281 const QColor color = colors.value(key);
285 const int picturesCount = partPictures.count();
286 dataOut << picturesCount;
287 foreach (const QString &key, partPictures.keys()) {
288 const QPicture picture = partPictures.value(key);
293 QDataStream blobOut(&blob);
294 blobOut << blobVersion;
295 blobOut << qCompress(data);
296 return blobOut.status() == QDataStream::Ok;
298 #endif // !QT_NO_WEBKIT
300 bool QtS60StyleThemeIO::loadThemeFromBlob(const QString &themeBlob, QS60Style *s60Style)
302 QHash<QString, QPicture> partPictures;
303 QHash<QPair<QString, int>, QColor> colors;
305 QFile blob(themeBlob);
306 if (!blob.open(QIODevice::ReadOnly)) {
307 qWarning() << __FUNCTION__ << ": Could not read blob: " << themeBlob;
310 QDataStream blobIn(&blob);
315 if (version != blobVersion) {
316 qWarning() << __FUNCTION__ << ": Invalid blob version: " << version << " ...expected: " << blobVersion;
322 data = qUncompress(data);
323 QBuffer dataBuffer(&data);
324 dataBuffer.open(QIODevice::ReadOnly);
325 QDataStream dataIn(&dataBuffer);
328 dataIn >> colorsCount;
329 for (int i = 0; i < colorsCount; ++i) {
330 QPair<QString, int> key;
334 colors.insert(key, value);
338 dataIn >> picturesCount;
339 for (int i = 0; i < picturesCount; ++i) {
344 value.setBoundingRect(QRect(0, 0, pictureSize, pictureSize)); // Bug? The forced bounding rect was not deserialized.
345 partPictures.insert(key, value);
348 if (dataIn.status() != QDataStream::Ok) {
349 qWarning() << __FUNCTION__ << ": Invalid data blob: " << themeBlob;
353 // dumpPartPictures(partPictures);
354 // dumpColors(colors);
356 return ::setS60Theme(partPictures, colors, s60Style);
359 #include "qts60stylethemeio.moc"
361 #endif // QT_NO_STYLE_S60