source: trunk/src/declarative/util/qdeclarativestyledtext.cpp@ 1147

Last change on this file since 1147 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 11.6 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
6**
7** This file is part of the QtDeclarative module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include <QStack>
43#include <QVector>
44#include <QPainter>
45#include <QTextLayout>
46#include <QDebug>
47#include <qmath.h>
48#include "private/qdeclarativestyledtext_p.h"
49
50/*
51 QDeclarativeStyledText supports few tags:
52
53 <b></b> - bold
54 <i></i> - italic
55 <br> - new line
56 <font color="color_name" size="1-7"></font>
57
58 The opening and closing tags must be correctly nested.
59*/
60
61QT_BEGIN_NAMESPACE
62
63class QDeclarativeStyledTextPrivate
64{
65public:
66 QDeclarativeStyledTextPrivate(const QString &t, QTextLayout &l) : text(t), layout(l), baseFont(layout.font()) {}
67
68 void parse();
69 bool parseTag(const QChar *&ch, const QString &textIn, QString &textOut, QTextCharFormat &format);
70 bool parseCloseTag(const QChar *&ch, const QString &textIn);
71 void parseEntity(const QChar *&ch, const QString &textIn, QString &textOut);
72 bool parseFontAttributes(const QChar *&ch, const QString &textIn, QTextCharFormat &format);
73 QPair<QStringRef,QStringRef> parseAttribute(const QChar *&ch, const QString &textIn);
74 QStringRef parseValue(const QChar *&ch, const QString &textIn);
75
76 inline void skipSpace(const QChar *&ch) {
77 while (ch->isSpace() && !ch->isNull())
78 ++ch;
79 }
80
81 QString text;
82 QTextLayout &layout;
83 QFont baseFont;
84
85 static const QChar lessThan;
86 static const QChar greaterThan;
87 static const QChar equals;
88 static const QChar singleQuote;
89 static const QChar doubleQuote;
90 static const QChar slash;
91 static const QChar ampersand;
92};
93
94const QChar QDeclarativeStyledTextPrivate::lessThan(QLatin1Char('<'));
95const QChar QDeclarativeStyledTextPrivate::greaterThan(QLatin1Char('>'));
96const QChar QDeclarativeStyledTextPrivate::equals(QLatin1Char('='));
97const QChar QDeclarativeStyledTextPrivate::singleQuote(QLatin1Char('\''));
98const QChar QDeclarativeStyledTextPrivate::doubleQuote(QLatin1Char('\"'));
99const QChar QDeclarativeStyledTextPrivate::slash(QLatin1Char('/'));
100const QChar QDeclarativeStyledTextPrivate::ampersand(QLatin1Char('&'));
101
102QDeclarativeStyledText::QDeclarativeStyledText(const QString &string, QTextLayout &layout)
103: d(new QDeclarativeStyledTextPrivate(string, layout))
104{
105}
106
107QDeclarativeStyledText::~QDeclarativeStyledText()
108{
109 delete d;
110}
111
112void QDeclarativeStyledText::parse(const QString &string, QTextLayout &layout)
113{
114 if (string.isEmpty())
115 return;
116 QDeclarativeStyledText styledText(string, layout);
117 styledText.d->parse();
118}
119
120void QDeclarativeStyledTextPrivate::parse()
121{
122 QList<QTextLayout::FormatRange> ranges;
123 QStack<QTextCharFormat> formatStack;
124
125 QString drawText;
126 drawText.reserve(text.count());
127
128 int textStart = 0;
129 int textLength = 0;
130 int rangeStart = 0;
131 const QChar *ch = text.constData();
132 while (!ch->isNull()) {
133 if (*ch == lessThan) {
134 if (textLength)
135 drawText.append(QStringRef(&text, textStart, textLength));
136 if (rangeStart != drawText.length() && formatStack.count()) {
137 QTextLayout::FormatRange formatRange;
138 formatRange.format = formatStack.top();
139 formatRange.start = rangeStart;
140 formatRange.length = drawText.length() - rangeStart;
141 ranges.append(formatRange);
142 }
143 rangeStart = drawText.length();
144 ++ch;
145 if (*ch == slash) {
146 ++ch;
147 if (parseCloseTag(ch, text)) {
148 if (formatStack.count())
149 formatStack.pop();
150 }
151 } else {
152 QTextCharFormat format;
153 if (formatStack.count())
154 format = formatStack.top();
155 if (parseTag(ch, text, drawText, format))
156 formatStack.push(format);
157 }
158 textStart = ch - text.constData() + 1;
159 textLength = 0;
160 } else if (*ch == ampersand) {
161 ++ch;
162 drawText.append(QStringRef(&text, textStart, textLength));
163 parseEntity(ch, text, drawText);
164 textStart = ch - text.constData() + 1;
165 textLength = 0;
166 } else {
167 ++textLength;
168 }
169 if (!ch->isNull())
170 ++ch;
171 }
172 if (textLength)
173 drawText.append(QStringRef(&text, textStart, textLength));
174 if (rangeStart != drawText.length() && formatStack.count()) {
175 QTextLayout::FormatRange formatRange;
176 formatRange.format = formatStack.top();
177 formatRange.start = rangeStart;
178 formatRange.length = drawText.length() - rangeStart;
179 ranges.append(formatRange);
180 }
181
182 layout.setText(drawText);
183 layout.setAdditionalFormats(ranges);
184}
185
186bool QDeclarativeStyledTextPrivate::parseTag(const QChar *&ch, const QString &textIn, QString &textOut, QTextCharFormat &format)
187{
188 skipSpace(ch);
189
190 int tagStart = ch - textIn.constData();
191 int tagLength = 0;
192 while (!ch->isNull()) {
193 if (*ch == greaterThan) {
194 QStringRef tag(&textIn, tagStart, tagLength);
195 const QChar char0 = tag.at(0);
196 if (char0 == QLatin1Char('b')) {
197 if (tagLength == 1)
198 format.setFontWeight(QFont::Bold);
199 else if (tagLength == 2 && tag.at(1) == QLatin1Char('r')) {
200 textOut.append(QChar(QChar::LineSeparator));
201 return false;
202 }
203 } else if (char0 == QLatin1Char('i')) {
204 if (tagLength == 1)
205 format.setFontItalic(true);
206 }
207 return true;
208 } else if (ch->isSpace()) {
209 // may have params.
210 QStringRef tag(&textIn, tagStart, tagLength);
211 if (tag == QLatin1String("font"))
212 return parseFontAttributes(ch, textIn, format);
213 if (*ch == greaterThan || ch->isNull())
214 continue;
215 } else if (*ch != slash){
216 tagLength++;
217 }
218 ++ch;
219 }
220
221 return false;
222}
223
224bool QDeclarativeStyledTextPrivate::parseCloseTag(const QChar *&ch, const QString &textIn)
225{
226 skipSpace(ch);
227
228 int tagStart = ch - textIn.constData();
229 int tagLength = 0;
230 while (!ch->isNull()) {
231 if (*ch == greaterThan) {
232 QStringRef tag(&textIn, tagStart, tagLength);
233 const QChar char0 = tag.at(0);
234 if (char0 == QLatin1Char('b')) {
235 if (tagLength == 1)
236 return true;
237 else if (tag.at(1) == QLatin1Char('r') && tagLength == 2)
238 return true;
239 } else if (char0 == QLatin1Char('i')) {
240 if (tagLength == 1)
241 return true;
242 } else if (tag == QLatin1String("font")) {
243 return true;
244 }
245 return false;
246 } else if (!ch->isSpace()){
247 tagLength++;
248 }
249 ++ch;
250 }
251
252 return false;
253}
254
255void QDeclarativeStyledTextPrivate::parseEntity(const QChar *&ch, const QString &textIn, QString &textOut)
256{
257 int entityStart = ch - textIn.constData();
258 int entityLength = 0;
259 while (!ch->isNull()) {
260 if (*ch == QLatin1Char(';')) {
261 QStringRef entity(&textIn, entityStart, entityLength);
262 if (entity == QLatin1String("gt"))
263 textOut += QChar(62);
264 else if (entity == QLatin1String("lt"))
265 textOut += QChar(60);
266 else if (entity == QLatin1String("amp"))
267 textOut += QChar(38);
268 return;
269 }
270 ++entityLength;
271 ++ch;
272 }
273}
274
275bool QDeclarativeStyledTextPrivate::parseFontAttributes(const QChar *&ch, const QString &textIn, QTextCharFormat &format)
276{
277 bool valid = false;
278 QPair<QStringRef,QStringRef> attr;
279 do {
280 attr = parseAttribute(ch, textIn);
281 if (attr.first == QLatin1String("color")) {
282 valid = true;
283 format.setForeground(QColor(attr.second.toString()));
284 } else if (attr.first == QLatin1String("size")) {
285 valid = true;
286 int size = attr.second.toString().toInt();
287 if (attr.second.at(0) == QLatin1Char('-') || attr.second.at(0) == QLatin1Char('+'))
288 size += 3;
289 if (size >= 1 && size <= 7) {
290 static const qreal scaling[] = { 0.7, 0.8, 1.0, 1.2, 1.5, 2.0, 2.4 };
291 format.setFontPointSize(baseFont.pointSize() * scaling[size-1]);
292 }
293 }
294 } while (!ch->isNull() && !attr.first.isEmpty());
295
296 return valid;
297}
298
299QPair<QStringRef,QStringRef> QDeclarativeStyledTextPrivate::parseAttribute(const QChar *&ch, const QString &textIn)
300{
301 skipSpace(ch);
302
303 int attrStart = ch - textIn.constData();
304 int attrLength = 0;
305 while (!ch->isNull()) {
306 if (*ch == greaterThan) {
307 break;
308 } else if (*ch == equals) {
309 ++ch;
310 if (*ch != singleQuote && *ch != doubleQuote) {
311 while (*ch != greaterThan && !ch->isNull())
312 ++ch;
313 break;
314 }
315 ++ch;
316 if (!attrLength)
317 break;
318 QStringRef attr(&textIn, attrStart, attrLength);
319 QStringRef val = parseValue(ch, textIn);
320 if (!val.isEmpty())
321 return QPair<QStringRef,QStringRef>(attr,val);
322 break;
323 } else {
324 ++attrLength;
325 }
326 ++ch;
327 }
328
329 return QPair<QStringRef,QStringRef>();
330}
331
332QStringRef QDeclarativeStyledTextPrivate::parseValue(const QChar *&ch, const QString &textIn)
333{
334 int valStart = ch - textIn.constData();
335 int valLength = 0;
336 while (*ch != singleQuote && *ch != doubleQuote && !ch->isNull()) {
337 ++valLength;
338 ++ch;
339 }
340 if (ch->isNull())
341 return QStringRef();
342 ++ch; // skip quote
343
344 return QStringRef(&textIn, valStart, valLength);
345}
346
347QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.