OSDN Git Service

It's 2011 now.
[qt-creator-jp/qt-creator-jp.git] / src / plugins / qmldesigner / components / themeloader / qts60stylethemeio.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 **
9 ** No Commercial Usage
10 **
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
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 **
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.
24 **
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.
28 **
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
31 **
32 **************************************************************************/
33
34 /****************************************************************************
35 **
36 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
37 ** Contact: Nokia Corporation (qt-info@nokia.com)
38 **
39 ** This file is part of the $MODULE$ of the Qt Toolkit.
40 **
41 ** $TROLLTECH_DUAL_LICENSE$
42 **
43 ****************************************************************************/
44
45 #include "qts60stylethemeio.h"
46
47 #if !defined(QT_NO_STYLE_S60)
48
49 #include "qs60style.h"
50 #include "qapplication.h"
51 #include "qwebview.h"
52 #include "qwebframe.h"
53 #include "qeventloop.h"
54 #include "qpicture.h"
55 #include "qpicture.h"
56 #include "qpainter.h"
57 #include "qfile.h"
58 #include "qdir.h"
59 #include "qfileinfo.h"
60 #include "qxmlstream.h"
61 #include "qbuffer.h"
62
63 #include "qdebug.h"
64
65 static const quint32 blobVersion = 1;
66 static const int pictureSize = 256;
67
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);
74         QPainter p(&image);
75         partPicture.play(&p);
76         image.save(partKey + QString::fromLatin1(".png"));
77     }
78 }
79
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;
84     }
85 }
86
87 bool setS60Theme(QHash<QString, QPicture> &partPictures,
88         QHash<QPair<QString, int>, QColor> &colors,
89         QS60Style *s60Style)
90 {
91     if (!s60Style)
92         s60Style = qobject_cast<QS60Style *>(QApplication::style());
93     if (!s60Style || !qobject_cast<QS60Style *>(s60Style)) {
94         qWarning() << __FUNCTION__ << ": No QS60Style found.";
95         return false;
96     }
97     s60Style->setS60Theme(partPictures, colors);
98     return true;
99 }
100
101 #ifndef QT_NO_WEBKIT
102 class WebKitSVGRenderer : public QWebView
103 {
104     Q_OBJECT
105
106 public:
107     WebKitSVGRenderer(QWidget *parent = 0);
108     QPicture svgToQPicture(const QString &svgFileName);
109
110 private slots:
111     void loadFinishedSlot(bool ok);
112
113 private:
114     QEventLoop m_loop;
115     QPicture m_result;
116 };
117
118 WebKitSVGRenderer::WebKitSVGRenderer(QWidget *parent)
119     : QWebView(parent)
120 {
121     connect(this, SIGNAL(loadFinished(bool)), SLOT(loadFinishedSlot(bool)));
122     setFixedSize(pictureSize, pictureSize);
123     QPalette pal = palette();
124     pal.setColor(QPalette::Base, Qt::transparent);
125     setPalette(pal);
126 }
127
128 QPicture WebKitSVGRenderer::svgToQPicture(const QString &svgFileName)
129 {
130     load(QUrl::fromLocalFile(svgFileName));
131     m_loop.exec();
132     return m_result;
133 }
134
135 void WebKitSVGRenderer::loadFinishedSlot(bool ok)
136 {
137     // crude error-checking
138     if (!ok)
139         qDebug() << "Failed loading " << qPrintable(url().toString());
140
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);"
147     );
148
149     m_result = QPicture(); // "Clear"
150     QPainter p(&m_result);
151     page()->mainFrame()->render(&p);
152     p.end();
153     m_result.setBoundingRect(QRect(0, 0, pictureSize, pictureSize));
154
155     m_loop.exit();
156 }
157
158 bool parseTdfFile(const QString &tdfFile,
159         QHash<QString, QString> &partSvgs,
160         QHash<QPair<QString, int>, QColor> &colors)
161 {
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%");
169     QFile file(tdfFile);
170     if (!file.open(QIODevice::ReadOnly))
171         return false;
172     QXmlStreamReader reader(&file);
173     QString partId;
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();
178         switch (token) {
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)) {
183                         partId = id;
184                     } else if (!id.isEmpty() && id.at(id.length()-1).isDigit()) {
185                         QString idText = id;
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());
191                         }
192                     } else if (QS60Style::partKeys().contains(id.mid(annoyingPrefix.length()))) {
193                         partId = id.mid(annoyingPrefix.length());
194                     }
195                 } else if (reader.name() == layerKey) {
196                     if (!partId.isEmpty()) {
197                         const QString svgFile = reader.attributes().value(layerFileNameKey).toString();
198                         partSvgs.insert(partId, svgFile);
199                         partId.clear();
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();
204                     }
205                 }
206                 break;
207             case QXmlStreamReader::EndElement:
208                 if (reader.tokenString() == elementKey || reader.name() == partKey)
209                     partId.clear();
210                 break;
211             default:
212                 break;
213         }
214     }
215     return true;
216 }
217
218 bool loadThemeFromTdf(const QString &tdfFile,
219         QHash<QString, QPicture> &partPictures,
220         QHash<QPair<QString, int>, QColor> &colors)
221 {
222     QHash<QString, QString> parsedPartSvgs;
223     QHash<QString, QPicture> parsedPartPictures;
224     QHash<QPair<QString, int>, QColor> parsedColors;
225     bool success = parseTdfFile(tdfFile, parsedPartSvgs, parsedColors);
226     if (!success)
227         return false;
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);
237     }
238 //    dumpPartPictures(parsedPartPictures);
239 //    dumpColors(colors);
240     partPictures = parsedPartPictures;
241     colors = parsedColors;
242     return true;
243 }
244
245 bool QtS60StyleThemeIO::loadThemeFromTdf(const QString &themeTdf, QS60Style *s60Style)
246 {
247     QHash<QString, QPicture> partPictures;
248     QHash<QPair<QString, int>, QColor> colors;
249
250     if (!::loadThemeFromTdf(themeTdf, partPictures, colors))
251         return false;
252
253     return ::setS60Theme(partPictures, colors, s60Style);
254 }
255
256 bool QtS60StyleThemeIO::convertTdfToBlob(const QString &themeTdf, const QString &themeBlob)
257 {
258     QHash<QString, QPicture> partPictures;
259     QHash<QPair<QString, int>, QColor> colors;
260
261     if (!::loadThemeFromTdf(themeTdf, partPictures, colors))
262         return false;
263
264     QFile blob(themeBlob);
265     if (!blob.open(QIODevice::WriteOnly)) {
266         qWarning() << __FUNCTION__ << ": Could not create blob: " << themeBlob;
267         return false;
268     }
269
270     QByteArray data;
271     QBuffer dataBuffer(&data);
272     dataBuffer.open(QIODevice::WriteOnly);
273     QDataStream dataOut(&dataBuffer);
274
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);
280         dataOut << key;
281         const QColor color = colors.value(key);
282         dataOut << color;
283     }
284
285     const int picturesCount = partPictures.count();
286     dataOut << picturesCount;
287     foreach (const QString &key, partPictures.keys()) {
288         const QPicture picture = partPictures.value(key);
289         dataOut << key;
290         dataOut << picture;
291     }
292
293     QDataStream blobOut(&blob);
294     blobOut << blobVersion;
295     blobOut << qCompress(data);
296     return blobOut.status() == QDataStream::Ok;
297 }
298 #endif // !QT_NO_WEBKIT
299
300 bool QtS60StyleThemeIO::loadThemeFromBlob(const QString &themeBlob, QS60Style *s60Style)
301 {
302     QHash<QString, QPicture> partPictures;
303     QHash<QPair<QString, int>, QColor> colors;
304
305     QFile blob(themeBlob);
306     if (!blob.open(QIODevice::ReadOnly)) {
307         qWarning() << __FUNCTION__ << ": Could not read blob: " << themeBlob;
308         return false;
309     }
310     QDataStream blobIn(&blob);
311
312     quint32 version;
313     blobIn >> version;
314
315     if (version != blobVersion) {
316         qWarning() << __FUNCTION__ << ": Invalid blob version: " << version << " ...expected: " << blobVersion;
317         return false;
318     }
319
320     QByteArray data;
321     blobIn >> data;
322     data = qUncompress(data);
323     QBuffer dataBuffer(&data);
324     dataBuffer.open(QIODevice::ReadOnly);
325     QDataStream dataIn(&dataBuffer);
326
327     int colorsCount;
328     dataIn >> colorsCount;
329     for (int i = 0; i < colorsCount; ++i) {
330         QPair<QString, int> key;
331         dataIn >> key;
332         QColor value;
333         dataIn >> value;
334         colors.insert(key, value);
335     }
336
337     int picturesCount;
338     dataIn >> picturesCount;
339     for (int i = 0; i < picturesCount; ++i) {
340         QString key;
341         dataIn >> key;
342         QPicture value;
343         dataIn >> value;
344         value.setBoundingRect(QRect(0, 0, pictureSize, pictureSize)); // Bug? The forced bounding rect was not deserialized.
345         partPictures.insert(key, value);
346     }
347
348     if (dataIn.status() != QDataStream::Ok) {
349         qWarning() << __FUNCTION__ << ": Invalid data blob: " << themeBlob;
350         return false;
351     }
352
353 //    dumpPartPictures(partPictures);
354 //    dumpColors(colors);
355
356     return ::setS60Theme(partPictures, colors, s60Style);
357 }
358
359 #include "qts60stylethemeio.moc"
360
361 #endif // QT_NO_STYLE_S60