source: trunk/src/gui/text/qcssparser.cpp

Last change on this file 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: 87.8 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 QtGui 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 "qcssparser_p.h"
43
44#include <qdebug.h>
45#include <qcolor.h>
46#include <qfont.h>
47#include <qfileinfo.h>
48#include <qfontmetrics.h>
49#include <qbrush.h>
50#include <qimagereader.h>
51#include "private/qfunctions_p.h"
52
53#ifndef QT_NO_CSSPARSER
54
55QT_BEGIN_NAMESPACE
56
57#include "qcssscanner.cpp"
58
59using namespace QCss;
60
61struct QCssKnownValue
62{
63 const char *name;
64 quint64 id;
65};
66
67static const QCssKnownValue properties[NumProperties - 1] = {
68 { "-qt-background-role", QtBackgroundRole },
69 { "-qt-block-indent", QtBlockIndent },
70 { "-qt-list-indent", QtListIndent },
71 { "-qt-paragraph-type", QtParagraphType },
72 { "-qt-style-features", QtStyleFeatures },
73 { "-qt-table-type", QtTableType },
74 { "-qt-user-state", QtUserState },
75 { "alternate-background-color", QtAlternateBackground },
76 { "background", Background },
77 { "background-attachment", BackgroundAttachment },
78 { "background-clip", BackgroundClip },
79 { "background-color", BackgroundColor },
80 { "background-image", BackgroundImage },
81 { "background-origin", BackgroundOrigin },
82 { "background-position", BackgroundPosition },
83 { "background-repeat", BackgroundRepeat },
84 { "border", Border },
85 { "border-bottom", BorderBottom },
86 { "border-bottom-color", BorderBottomColor },
87 { "border-bottom-left-radius", BorderBottomLeftRadius },
88 { "border-bottom-right-radius", BorderBottomRightRadius },
89 { "border-bottom-style", BorderBottomStyle },
90 { "border-bottom-width", BorderBottomWidth },
91 { "border-color", BorderColor },
92 { "border-image", BorderImage },
93 { "border-left", BorderLeft },
94 { "border-left-color", BorderLeftColor },
95 { "border-left-style", BorderLeftStyle },
96 { "border-left-width", BorderLeftWidth },
97 { "border-radius", BorderRadius },
98 { "border-right", BorderRight },
99 { "border-right-color", BorderRightColor },
100 { "border-right-style", BorderRightStyle },
101 { "border-right-width", BorderRightWidth },
102 { "border-style", BorderStyles },
103 { "border-top", BorderTop },
104 { "border-top-color", BorderTopColor },
105 { "border-top-left-radius", BorderTopLeftRadius },
106 { "border-top-right-radius", BorderTopRightRadius },
107 { "border-top-style", BorderTopStyle },
108 { "border-top-width", BorderTopWidth },
109 { "border-width", BorderWidth },
110 { "bottom", Bottom },
111 { "color", Color },
112 { "float", Float },
113 { "font", Font },
114 { "font-family", FontFamily },
115 { "font-size", FontSize },
116 { "font-style", FontStyle },
117 { "font-variant", FontVariant },
118 { "font-weight", FontWeight },
119 { "height", Height },
120 { "image", QtImage },
121 { "image-position", QtImageAlignment },
122 { "left", Left },
123 { "list-style", ListStyle },
124 { "list-style-type", ListStyleType },
125 { "margin" , Margin },
126 { "margin-bottom", MarginBottom },
127 { "margin-left", MarginLeft },
128 { "margin-right", MarginRight },
129 { "margin-top", MarginTop },
130 { "max-height", MaximumHeight },
131 { "max-width", MaximumWidth },
132 { "min-height", MinimumHeight },
133 { "min-width", MinimumWidth },
134 { "outline", Outline },
135 { "outline-bottom-left-radius", OutlineBottomLeftRadius },
136 { "outline-bottom-right-radius", OutlineBottomRightRadius },
137 { "outline-color", OutlineColor },
138 { "outline-offset", OutlineOffset },
139 { "outline-radius", OutlineRadius },
140 { "outline-style", OutlineStyle },
141 { "outline-top-left-radius", OutlineTopLeftRadius },
142 { "outline-top-right-radius", OutlineTopRightRadius },
143 { "outline-width", OutlineWidth },
144 { "padding", Padding },
145 { "padding-bottom", PaddingBottom },
146 { "padding-left", PaddingLeft },
147 { "padding-right", PaddingRight },
148 { "padding-top", PaddingTop },
149 { "page-break-after", PageBreakAfter },
150 { "page-break-before", PageBreakBefore },
151 { "position", Position },
152 { "right", Right },
153 { "selection-background-color", QtSelectionBackground },
154 { "selection-color", QtSelectionForeground },
155 { "spacing", QtSpacing },
156 { "subcontrol-origin", QtOrigin },
157 { "subcontrol-position", QtPosition },
158 { "text-align", TextAlignment },
159 { "text-decoration", TextDecoration },
160 { "text-indent", TextIndent },
161 { "text-transform", TextTransform },
162 { "text-underline-style", TextUnderlineStyle },
163 { "top", Top },
164 { "vertical-align", VerticalAlignment },
165 { "white-space", Whitespace },
166 { "width", Width }
167};
168
169static const QCssKnownValue values[NumKnownValues - 1] = {
170 { "active", Value_Active },
171 { "alternate-base", Value_AlternateBase },
172 { "always", Value_Always },
173 { "auto", Value_Auto },
174 { "base", Value_Base },
175 { "bold", Value_Bold },
176 { "bottom", Value_Bottom },
177 { "bright-text", Value_BrightText },
178 { "button", Value_Button },
179 { "button-text", Value_ButtonText },
180 { "center", Value_Center },
181 { "circle", Value_Circle },
182 { "dark", Value_Dark },
183 { "dashed", Value_Dashed },
184 { "decimal", Value_Decimal },
185 { "disabled", Value_Disabled },
186 { "disc", Value_Disc },
187 { "dot-dash", Value_DotDash },
188 { "dot-dot-dash", Value_DotDotDash },
189 { "dotted", Value_Dotted },
190 { "double", Value_Double },
191 { "groove", Value_Groove },
192 { "highlight", Value_Highlight },
193 { "highlighted-text", Value_HighlightedText },
194 { "inset", Value_Inset },
195 { "italic", Value_Italic },
196 { "large", Value_Large },
197 { "left", Value_Left },
198 { "light", Value_Light },
199 { "line-through", Value_LineThrough },
200 { "link", Value_Link },
201 { "link-visited", Value_LinkVisited },
202 { "lower-alpha", Value_LowerAlpha },
203 { "lower-roman", Value_LowerRoman },
204 { "lowercase", Value_Lowercase },
205 { "medium", Value_Medium },
206 { "mid", Value_Mid },
207 { "middle", Value_Middle },
208 { "midlight", Value_Midlight },
209 { "native", Value_Native },
210 { "none", Value_None },
211 { "normal", Value_Normal },
212 { "nowrap", Value_NoWrap },
213 { "oblique", Value_Oblique },
214 { "off", Value_Off },
215 { "on", Value_On },
216 { "outset", Value_Outset },
217 { "overline", Value_Overline },
218 { "pre", Value_Pre },
219 { "pre-wrap", Value_PreWrap },
220 { "ridge", Value_Ridge },
221 { "right", Value_Right },
222 { "selected", Value_Selected },
223 { "shadow", Value_Shadow },
224 { "small" , Value_Small },
225 { "small-caps", Value_SmallCaps },
226 { "solid", Value_Solid },
227 { "square", Value_Square },
228 { "sub", Value_Sub },
229 { "super", Value_Super },
230 { "text", Value_Text },
231 { "top", Value_Top },
232 { "transparent", Value_Transparent },
233 { "underline", Value_Underline },
234 { "upper-alpha", Value_UpperAlpha },
235 { "upper-roman", Value_UpperRoman },
236 { "uppercase", Value_Uppercase },
237 { "wave", Value_Wave },
238 { "window", Value_Window },
239 { "window-text", Value_WindowText },
240 { "x-large", Value_XLarge },
241 { "xx-large", Value_XXLarge }
242};
243
244//Map id to strings as they appears in the 'values' array above
245static const short indexOfId[NumKnownValues] = { 0, 41, 48, 42, 49, 54, 35, 26, 70, 71, 25, 43, 5, 63, 47,
246 29, 58, 59, 27, 51, 61, 6, 10, 39, 56, 19, 13, 17, 18, 20, 21, 50, 24, 46, 67, 37, 3, 2, 40, 62, 16,
247 11, 57, 14, 32, 64, 33, 65, 55, 66, 34, 69, 8, 28, 38, 12, 36, 60, 7, 9, 4, 68, 53, 22, 23, 30, 31,
248 1, 15, 0, 52, 45, 44 };
249
250QString Value::toString() const
251{
252 if (type == KnownIdentifier) {
253 return QLatin1String(values[indexOfId[variant.toInt()]].name);
254 } else {
255 return variant.toString();
256 }
257}
258
259static const QCssKnownValue pseudos[NumPseudos - 1] = {
260 { "active", PseudoClass_Active },
261 { "adjoins-item", PseudoClass_Item },
262 { "alternate", PseudoClass_Alternate },
263 { "bottom", PseudoClass_Bottom },
264 { "checked", PseudoClass_Checked },
265 { "closable", PseudoClass_Closable },
266 { "closed", PseudoClass_Closed },
267 { "default", PseudoClass_Default },
268 { "disabled", PseudoClass_Disabled },
269 { "edit-focus", PseudoClass_EditFocus },
270 { "editable", PseudoClass_Editable },
271 { "enabled", PseudoClass_Enabled },
272 { "exclusive", PseudoClass_Exclusive },
273 { "first", PseudoClass_First },
274 { "flat", PseudoClass_Flat },
275 { "floatable", PseudoClass_Floatable },
276 { "focus", PseudoClass_Focus },
277 { "has-children", PseudoClass_Children },
278 { "has-siblings", PseudoClass_Sibling },
279 { "horizontal", PseudoClass_Horizontal },
280 { "hover", PseudoClass_Hover },
281 { "indeterminate" , PseudoClass_Indeterminate },
282 { "last", PseudoClass_Last },
283 { "left", PseudoClass_Left },
284 { "maximized", PseudoClass_Maximized },
285 { "middle", PseudoClass_Middle },
286 { "minimized", PseudoClass_Minimized },
287 { "movable", PseudoClass_Movable },
288 { "next-selected", PseudoClass_NextSelected },
289 { "no-frame", PseudoClass_Frameless },
290 { "non-exclusive", PseudoClass_NonExclusive },
291 { "off", PseudoClass_Unchecked },
292 { "on", PseudoClass_Checked },
293 { "only-one", PseudoClass_OnlyOne },
294 { "open", PseudoClass_Open },
295 { "pressed", PseudoClass_Pressed },
296 { "previous-selected", PseudoClass_PreviousSelected },
297 { "read-only", PseudoClass_ReadOnly },
298 { "right", PseudoClass_Right },
299 { "selected", PseudoClass_Selected },
300 { "top", PseudoClass_Top },
301 { "unchecked" , PseudoClass_Unchecked },
302 { "vertical", PseudoClass_Vertical },
303 { "window", PseudoClass_Window }
304};
305
306static const QCssKnownValue origins[NumKnownOrigins - 1] = {
307 { "border", Origin_Border },
308 { "content", Origin_Content },
309 { "margin", Origin_Margin }, // not in css
310 { "padding", Origin_Padding }
311};
312
313static const QCssKnownValue repeats[NumKnownRepeats - 1] = {
314 { "no-repeat", Repeat_None },
315 { "repeat-x", Repeat_X },
316 { "repeat-xy", Repeat_XY },
317 { "repeat-y", Repeat_Y }
318};
319
320static const QCssKnownValue tileModes[NumKnownTileModes - 1] = {
321 { "repeat", TileMode_Repeat },
322 { "round", TileMode_Round },
323 { "stretch", TileMode_Stretch },
324};
325
326static const QCssKnownValue positions[NumKnownPositionModes - 1] = {
327 { "absolute", PositionMode_Absolute },
328 { "fixed", PositionMode_Fixed },
329 { "relative", PositionMode_Relative },
330 { "static", PositionMode_Static }
331};
332
333static const QCssKnownValue attachments[NumKnownAttachments - 1] = {
334 { "fixed", Attachment_Fixed },
335 { "scroll", Attachment_Scroll }
336};
337
338static const QCssKnownValue styleFeatures[NumKnownStyleFeatures - 1] = {
339 { "background-color", StyleFeature_BackgroundColor },
340 { "background-gradient", StyleFeature_BackgroundGradient },
341 { "none", StyleFeature_None }
342};
343
344Q_STATIC_GLOBAL_OPERATOR bool operator<(const QString &name, const QCssKnownValue &prop)
345{
346 return QString::compare(name, QLatin1String(prop.name), Qt::CaseInsensitive) < 0;
347}
348
349Q_STATIC_GLOBAL_OPERATOR bool operator<(const QCssKnownValue &prop, const QString &name)
350{
351 return QString::compare(QLatin1String(prop.name), name, Qt::CaseInsensitive) < 0;
352}
353
354static quint64 findKnownValue(const QString &name, const QCssKnownValue *start, int numValues)
355{
356 const QCssKnownValue *end = &start[numValues - 1];
357 const QCssKnownValue *prop = qBinaryFind(start, end, name);
358 if (prop == end)
359 return 0;
360 return prop->id;
361}
362
363///////////////////////////////////////////////////////////////////////////////
364// Value Extractor
365ValueExtractor::ValueExtractor(const QVector<Declaration> &decls, const QPalette &pal)
366: declarations(decls), adjustment(0), fontExtracted(false), pal(pal)
367{
368}
369
370LengthData ValueExtractor::lengthValue(const Value& v)
371{
372 QString s = v.variant.toString();
373 s.reserve(s.length());
374 LengthData data;
375 data.unit = LengthData::None;
376 if (s.endsWith(QLatin1String("px"), Qt::CaseInsensitive))
377 data.unit = LengthData::Px;
378 else if (s.endsWith(QLatin1String("ex"), Qt::CaseInsensitive))
379 data.unit = LengthData::Ex;
380 else if (s.endsWith(QLatin1String("em"), Qt::CaseInsensitive))
381 data.unit = LengthData::Em;
382
383 if (data.unit != LengthData::None)
384 s.chop(2);
385
386 data.number = s.toDouble();
387 return data;
388}
389
390static int lengthValueFromData(const LengthData& data, const QFont& f)
391{
392 if (data.unit == LengthData::Ex)
393 return qRound(QFontMetrics(f).xHeight() * data.number);
394 else if (data.unit == LengthData::Em)
395 return qRound(QFontMetrics(f).height() * data.number);
396 return qRound(data.number);
397}
398
399int ValueExtractor::lengthValue(const Declaration &decl)
400{
401 if (decl.d->parsed.isValid())
402 return lengthValueFromData(qvariant_cast<LengthData>(decl.d->parsed), f);
403 if (decl.d->values.count() < 1)
404 return 0;
405 LengthData data = lengthValue(decl.d->values.at(0));
406 decl.d->parsed = qVariantFromValue<LengthData>(data);
407 return lengthValueFromData(data,f);
408}
409
410void ValueExtractor::lengthValues(const Declaration &decl, int *m)
411{
412 if (decl.d->parsed.isValid()) {
413 QList<QVariant> v = decl.d->parsed.toList();
414 for (int i = 0; i < 4; i++)
415 m[i] = lengthValueFromData(qvariant_cast<LengthData>(v.at(i)), f);
416 return;
417 }
418
419 LengthData datas[4];
420 int i;
421 for (i = 0; i < qMin(decl.d->values.count(), 4); i++)
422 datas[i] = lengthValue(decl.d->values[i]);
423
424 if (i == 0) {
425 LengthData zero = {0.0, LengthData::None};
426 datas[0] = datas[1] = datas[2] = datas[3] = zero;
427 } else if (i == 1) {
428 datas[3] = datas[2] = datas[1] = datas[0];
429 } else if (i == 2) {
430 datas[2] = datas[0];
431 datas[3] = datas[1];
432 } else if (i == 3) {
433 datas[3] = datas[1];
434 }
435
436 QList<QVariant> v;
437 for (i = 0; i < 4; i++) {
438 v += qVariantFromValue<LengthData>(datas[i]);
439 m[i] = lengthValueFromData(datas[i], f);
440 }
441 decl.d->parsed = v;
442}
443
444bool ValueExtractor::extractGeometry(int *w, int *h, int *minw, int *minh, int *maxw, int *maxh)
445{
446 extractFont();
447 bool hit = false;
448 for (int i = 0; i < declarations.count(); i++) {
449 const Declaration &decl = declarations.at(i);
450 switch (decl.d->propertyId) {
451 case Width: *w = lengthValue(decl); break;
452 case Height: *h = lengthValue(decl); break;
453 case MinimumWidth: *minw = lengthValue(decl); break;
454 case MinimumHeight: *minh = lengthValue(decl); break;
455 case MaximumWidth: *maxw = lengthValue(decl); break;
456 case MaximumHeight: *maxh = lengthValue(decl); break;
457 default: continue;
458 }
459 hit = true;
460 }
461
462 return hit;
463}
464
465bool ValueExtractor::extractPosition(int *left, int *top, int *right, int *bottom, QCss::Origin *origin,
466 Qt::Alignment *position, QCss::PositionMode *mode, Qt::Alignment *textAlignment)
467{
468 extractFont();
469 bool hit = false;
470 for (int i = 0; i < declarations.count(); i++) {
471 const Declaration &decl = declarations.at(i);
472 switch (decl.d->propertyId) {
473 case Left: *left = lengthValue(decl); break;
474 case Top: *top = lengthValue(decl); break;
475 case Right: *right = lengthValue(decl); break;
476 case Bottom: *bottom = lengthValue(decl); break;
477 case QtOrigin: *origin = decl.originValue(); break;
478 case QtPosition: *position = decl.alignmentValue(); break;
479 case TextAlignment: *textAlignment = decl.alignmentValue(); break;
480 case Position: *mode = decl.positionValue(); break;
481 default: continue;
482 }
483 hit = true;
484 }
485
486 return hit;
487}
488
489bool ValueExtractor::extractBox(int *margins, int *paddings, int *spacing)
490{
491 extractFont();
492 bool hit = false;
493 for (int i = 0; i < declarations.count(); i++) {
494 const Declaration &decl = declarations.at(i);
495 switch (decl.d->propertyId) {
496 case PaddingLeft: paddings[LeftEdge] = lengthValue(decl); break;
497 case PaddingRight: paddings[RightEdge] = lengthValue(decl); break;
498 case PaddingTop: paddings[TopEdge] = lengthValue(decl); break;
499 case PaddingBottom: paddings[BottomEdge] = lengthValue(decl); break;
500 case Padding: lengthValues(decl, paddings); break;
501
502 case MarginLeft: margins[LeftEdge] = lengthValue(decl); break;
503 case MarginRight: margins[RightEdge] = lengthValue(decl); break;
504 case MarginTop: margins[TopEdge] = lengthValue(decl); break;
505 case MarginBottom: margins[BottomEdge] = lengthValue(decl); break;
506 case Margin: lengthValues(decl, margins); break;
507 case QtSpacing: if (spacing) *spacing = lengthValue(decl); break;
508
509 default: continue;
510 }
511 hit = true;
512 }
513
514 return hit;
515}
516
517int ValueExtractor::extractStyleFeatures()
518{
519 int features = StyleFeature_None;
520 for (int i = 0; i < declarations.count(); i++) {
521 const Declaration &decl = declarations.at(i);
522 if (decl.d->propertyId == QtStyleFeatures)
523 features = decl.styleFeaturesValue();
524 }
525 return features;
526}
527
528QSize ValueExtractor::sizeValue(const Declaration &decl)
529{
530 if (decl.d->parsed.isValid()) {
531 QList<QVariant> v = decl.d->parsed.toList();
532 return QSize(lengthValueFromData(qvariant_cast<LengthData>(v.at(0)), f),
533 lengthValueFromData(qvariant_cast<LengthData>(v.at(1)), f));
534 }
535
536 LengthData x[2] = { {0, LengthData::None }, {0, LengthData::None} };
537 if (decl.d->values.count() > 0)
538 x[0] = lengthValue(decl.d->values.at(0));
539 if (decl.d->values.count() > 1)
540 x[1] = lengthValue(decl.d->values.at(1));
541 else
542 x[1] = x[0];
543 QList<QVariant> v;
544 v << qVariantFromValue<LengthData>(x[0]) << qVariantFromValue<LengthData>(x[1]);
545 decl.d->parsed = v;
546 return QSize(lengthValueFromData(x[0], f), lengthValueFromData(x[1], f));
547}
548
549void ValueExtractor::sizeValues(const Declaration &decl, QSize *radii)
550{
551 radii[0] = sizeValue(decl);
552 for (int i = 1; i < 4; i++)
553 radii[i] = radii[0];
554}
555
556bool ValueExtractor::extractBorder(int *borders, QBrush *colors, BorderStyle *styles,
557 QSize *radii)
558{
559 extractFont();
560 bool hit = false;
561 for (int i = 0; i < declarations.count(); i++) {
562 const Declaration &decl = declarations.at(i);
563 switch (decl.d->propertyId) {
564 case BorderLeftWidth: borders[LeftEdge] = lengthValue(decl); break;
565 case BorderRightWidth: borders[RightEdge] = lengthValue(decl); break;
566 case BorderTopWidth: borders[TopEdge] = lengthValue(decl); break;
567 case BorderBottomWidth: borders[BottomEdge] = lengthValue(decl); break;
568 case BorderWidth: lengthValues(decl, borders); break;
569
570 case BorderLeftColor: colors[LeftEdge] = decl.brushValue(pal); break;
571 case BorderRightColor: colors[RightEdge] = decl.brushValue(pal); break;
572 case BorderTopColor: colors[TopEdge] = decl.brushValue(pal); break;
573 case BorderBottomColor: colors[BottomEdge] = decl.brushValue(pal); break;
574 case BorderColor: decl.brushValues(colors, pal); break;
575
576 case BorderTopStyle: styles[TopEdge] = decl.styleValue(); break;
577 case BorderBottomStyle: styles[BottomEdge] = decl.styleValue(); break;
578 case BorderLeftStyle: styles[LeftEdge] = decl.styleValue(); break;
579 case BorderRightStyle: styles[RightEdge] = decl.styleValue(); break;
580 case BorderStyles: decl.styleValues(styles); break;
581
582 case BorderTopLeftRadius: radii[0] = sizeValue(decl); break;
583 case BorderTopRightRadius: radii[1] = sizeValue(decl); break;
584 case BorderBottomLeftRadius: radii[2] = sizeValue(decl); break;
585 case BorderBottomRightRadius: radii[3] = sizeValue(decl); break;
586 case BorderRadius: sizeValues(decl, radii); break;
587
588 case BorderLeft:
589 borderValue(decl, &borders[LeftEdge], &styles[LeftEdge], &colors[LeftEdge]);
590 break;
591 case BorderTop:
592 borderValue(decl, &borders[TopEdge], &styles[TopEdge], &colors[TopEdge]);
593 break;
594 case BorderRight:
595 borderValue(decl, &borders[RightEdge], &styles[RightEdge], &colors[RightEdge]);
596 break;
597 case BorderBottom:
598 borderValue(decl, &borders[BottomEdge], &styles[BottomEdge], &colors[BottomEdge]);
599 break;
600 case Border:
601 borderValue(decl, &borders[LeftEdge], &styles[LeftEdge], &colors[LeftEdge]);
602 borders[TopEdge] = borders[RightEdge] = borders[BottomEdge] = borders[LeftEdge];
603 styles[TopEdge] = styles[RightEdge] = styles[BottomEdge] = styles[LeftEdge];
604 colors[TopEdge] = colors[RightEdge] = colors[BottomEdge] = colors[LeftEdge];
605 break;
606
607 default: continue;
608 }
609 hit = true;
610 }
611
612 return hit;
613}
614
615bool ValueExtractor::extractOutline(int *borders, QBrush *colors, BorderStyle *styles,
616 QSize *radii, int *offsets)
617{
618 extractFont();
619 bool hit = false;
620 for (int i = 0; i < declarations.count(); i++) {
621 const Declaration &decl = declarations.at(i);
622 switch (decl.d->propertyId) {
623 case OutlineWidth: lengthValues(decl, borders); break;
624 case OutlineColor: decl.brushValues(colors, pal); break;
625 case OutlineStyle: decl.styleValues(styles); break;
626
627 case OutlineTopLeftRadius: radii[0] = sizeValue(decl); break;
628 case OutlineTopRightRadius: radii[1] = sizeValue(decl); break;
629 case OutlineBottomLeftRadius: radii[2] = sizeValue(decl); break;
630 case OutlineBottomRightRadius: radii[3] = sizeValue(decl); break;
631 case OutlineRadius: sizeValues(decl, radii); break;
632 case OutlineOffset: lengthValues(decl, offsets); break;
633
634 case Outline:
635 borderValue(decl, &borders[LeftEdge], &styles[LeftEdge], &colors[LeftEdge]);
636 borders[TopEdge] = borders[RightEdge] = borders[BottomEdge] = borders[LeftEdge];
637 styles[TopEdge] = styles[RightEdge] = styles[BottomEdge] = styles[LeftEdge];
638 colors[TopEdge] = colors[RightEdge] = colors[BottomEdge] = colors[LeftEdge];
639 break;
640
641 default: continue;
642 }
643 hit = true;
644 }
645
646 return hit;
647}
648
649static Qt::Alignment parseAlignment(const Value *values, int count)
650{
651 Qt::Alignment a[2] = { 0, 0 };
652 for (int i = 0; i < qMin(2, count); i++) {
653 if (values[i].type != Value::KnownIdentifier)
654 break;
655 switch (values[i].variant.toInt()) {
656 case Value_Left: a[i] = Qt::AlignLeft; break;
657 case Value_Right: a[i] = Qt::AlignRight; break;
658 case Value_Top: a[i] = Qt::AlignTop; break;
659 case Value_Bottom: a[i] = Qt::AlignBottom; break;
660 case Value_Center: a[i] = Qt::AlignCenter; break;
661 default: break;
662 }
663 }
664
665 if (a[0] == Qt::AlignCenter && a[1] != 0 && a[1] != Qt::AlignCenter)
666 a[0] = (a[1] == Qt::AlignLeft || a[1] == Qt::AlignRight) ? Qt::AlignVCenter : Qt::AlignHCenter;
667 if ((a[1] == 0 || a[1] == Qt::AlignCenter) && a[0] != Qt::AlignCenter)
668 a[1] = (a[0] == Qt::AlignLeft || a[0] == Qt::AlignRight) ? Qt::AlignVCenter : Qt::AlignHCenter;
669 return a[0] | a[1];
670}
671
672static ColorData parseColorValue(Value v)
673{
674 if (v.type == Value::Identifier || v.type == Value::String) {
675 v.variant.convert(QVariant::Color);
676 v.type = Value::Color;
677 }
678
679 if (v.type == Value::Color)
680 return qvariant_cast<QColor>(v.variant);
681
682 if (v.type == Value::KnownIdentifier && v.variant.toInt() == Value_Transparent)
683 return QColor(Qt::transparent);
684
685 if (v.type != Value::Function)
686 return ColorData();
687
688 QStringList lst = v.variant.toStringList();
689 if (lst.count() != 2)
690 return ColorData();
691
692 if ((lst.at(0).compare(QLatin1String("palette"), Qt::CaseInsensitive)) == 0) {
693 int role = findKnownValue(lst.at(1).trimmed(), values, NumKnownValues);
694 if (role >= Value_FirstColorRole && role <= Value_LastColorRole)
695 return (QPalette::ColorRole)(role-Value_FirstColorRole);
696
697 return ColorData();
698 }
699
700 bool rgb = lst.at(0).startsWith(QLatin1String("rgb"));
701
702 Parser p(lst.at(1));
703 if (!p.testExpr())
704 return ColorData();
705
706 QVector<Value> colorDigits;
707 if (!p.parseExpr(&colorDigits))
708 return ColorData();
709
710 for (int i = 0; i < qMin(colorDigits.count(), 7); i += 2) {
711 if (colorDigits.at(i).type == Value::Percentage) {
712 colorDigits[i].variant = colorDigits.at(i).variant.toReal() * (255. / 100.);
713 colorDigits[i].type = Value::Number;
714 } else if (colorDigits.at(i).type != Value::Number) {
715 return ColorData();
716 }
717 }
718
719 int v1 = colorDigits.at(0).variant.toInt();
720 int v2 = colorDigits.at(2).variant.toInt();
721 int v3 = colorDigits.at(4).variant.toInt();
722 int alpha = colorDigits.count() >= 7 ? colorDigits.at(6).variant.toInt() : 255;
723
724 return rgb ? QColor::fromRgb(v1, v2, v3, alpha)
725 : QColor::fromHsv(v1, v2, v3, alpha);
726}
727
728static QColor colorFromData(const ColorData& c, const QPalette &pal)
729{
730 if (c.type == ColorData::Color) {
731 return c.color;
732 } else if (c.type == ColorData::Role) {
733 return pal.color(c.role);
734 }
735 return QColor();
736}
737
738static BrushData parseBrushValue(const Value &v, const QPalette &pal)
739{
740 ColorData c = parseColorValue(v);
741 if (c.type == ColorData::Color) {
742 return QBrush(c.color);
743 } else if (c.type == ColorData::Role) {
744 return c.role;
745 }
746
747 if (v.type != Value::Function)
748 return BrushData();
749
750 QStringList lst = v.variant.toStringList();
751 if (lst.count() != 2)
752 return BrushData();
753
754 QStringList gradFuncs;
755 gradFuncs << QLatin1String("qlineargradient") << QLatin1String("qradialgradient") << QLatin1String("qconicalgradient") << QLatin1String("qgradient");
756 int gradType = -1;
757
758 if ((gradType = gradFuncs.indexOf(lst.at(0).toLower())) == -1)
759 return BrushData();
760
761 QHash<QString, qreal> vars;
762 QVector<QGradientStop> stops;
763
764 int spread = -1;
765 QStringList spreads;
766 spreads << QLatin1String("pad") << QLatin1String("reflect") << QLatin1String("repeat");
767
768 bool dependsOnThePalette = false;
769 Parser parser(lst.at(1));
770 while (parser.hasNext()) {
771 parser.skipSpace();
772 if (!parser.test(IDENT))
773 return BrushData();
774 QString attr = parser.lexem();
775 parser.skipSpace();
776 if (!parser.test(COLON))
777 return BrushData();
778 parser.skipSpace();
779 if (attr.compare(QLatin1String("stop"), Qt::CaseInsensitive) == 0) {
780 Value stop, color;
781 parser.next();
782 if (!parser.parseTerm(&stop)) return BrushData();
783 parser.skipSpace();
784 parser.next();
785 if (!parser.parseTerm(&color)) return BrushData();
786 ColorData cd = parseColorValue(color);
787 if(cd.type == ColorData::Role)
788 dependsOnThePalette = true;
789 stops.append(QGradientStop(stop.variant.toReal(), colorFromData(cd, pal)));
790 } else {
791 parser.next();
792 Value value;
793 (void)parser.parseTerm(&value);
794 if (attr.compare(QLatin1String("spread"), Qt::CaseInsensitive) == 0) {
795 spread = spreads.indexOf(value.variant.toString());
796 } else {
797 vars[attr] = value.variant.toReal();
798 }
799 }
800 parser.skipSpace();
801 (void)parser.test(COMMA);
802 }
803
804 if (gradType == 0) {
805 QLinearGradient lg(vars.value(QLatin1String("x1")), vars.value(QLatin1String("y1")),
806 vars.value(QLatin1String("x2")), vars.value(QLatin1String("y2")));
807 lg.setCoordinateMode(QGradient::ObjectBoundingMode);
808 lg.setStops(stops);
809 if (spread != -1)
810 lg.setSpread(QGradient::Spread(spread));
811 BrushData bd = QBrush(lg);
812 if (dependsOnThePalette)
813 bd.type = BrushData::DependsOnThePalette;
814 return bd;
815 }
816
817 if (gradType == 1) {
818 QRadialGradient rg(vars.value(QLatin1String("cx")), vars.value(QLatin1String("cy")),
819 vars.value(QLatin1String("radius")), vars.value(QLatin1String("fx")),
820 vars.value(QLatin1String("fy")));
821 rg.setCoordinateMode(QGradient::ObjectBoundingMode);
822 rg.setStops(stops);
823 if (spread != -1)
824 rg.setSpread(QGradient::Spread(spread));
825 BrushData bd = QBrush(rg);
826 if (dependsOnThePalette)
827 bd.type = BrushData::DependsOnThePalette;
828 return bd;
829 }
830
831 if (gradType == 2) {
832 QConicalGradient cg(vars.value(QLatin1String("cx")), vars.value(QLatin1String("cy")),
833 vars.value(QLatin1String("angle")));
834 cg.setCoordinateMode(QGradient::ObjectBoundingMode);
835 cg.setStops(stops);
836 if (spread != -1)
837 cg.setSpread(QGradient::Spread(spread));
838 BrushData bd = QBrush(cg);
839 if (dependsOnThePalette)
840 bd.type = BrushData::DependsOnThePalette;
841 return bd;
842 }
843
844 return BrushData();
845}
846
847static QBrush brushFromData(const BrushData& c, const QPalette &pal)
848{
849 if (c.type == BrushData::Role) {
850 return pal.color(c.role);
851 } else {
852 return c.brush;
853 }
854}
855
856static BorderStyle parseStyleValue(Value v)
857{
858 if (v.type == Value::KnownIdentifier) {
859 switch (v.variant.toInt()) {
860 case Value_None:
861 return BorderStyle_None;
862 case Value_Dotted:
863 return BorderStyle_Dotted;
864 case Value_Dashed:
865 return BorderStyle_Dashed;
866 case Value_Solid:
867 return BorderStyle_Solid;
868 case Value_Double:
869 return BorderStyle_Double;
870 case Value_DotDash:
871 return BorderStyle_DotDash;
872 case Value_DotDotDash:
873 return BorderStyle_DotDotDash;
874 case Value_Groove:
875 return BorderStyle_Groove;
876 case Value_Ridge:
877 return BorderStyle_Ridge;
878 case Value_Inset:
879 return BorderStyle_Inset;
880 case Value_Outset:
881 return BorderStyle_Outset;
882 case Value_Native:
883 return BorderStyle_Native;
884 default:
885 break;
886 }
887 }
888
889 return BorderStyle_Unknown;
890}
891
892void ValueExtractor::borderValue(const Declaration &decl, int *width, QCss::BorderStyle *style, QBrush *color)
893{
894 if (decl.d->parsed.isValid()) {
895 BorderData data = qvariant_cast<BorderData>(decl.d->parsed);
896 *width = lengthValueFromData(data.width, f);
897 *style = data.style;
898 *color = data.color.type != BrushData::Invalid ? brushFromData(data.color, pal) : QBrush(QColor());
899 return;
900 }
901
902 *width = 0;
903 *style = BorderStyle_None;
904 *color = QColor();
905
906 if (decl.d->values.isEmpty())
907 return;
908
909 BorderData data;
910 data.width.number = 0;
911 data.width.unit = LengthData::None;
912 data.style = BorderStyle_None;
913
914 int i = 0;
915 if (decl.d->values.at(i).type == Value::Length || decl.d->values.at(i).type == Value::Number) {
916 data.width = lengthValue(decl.d->values.at(i));
917 *width = lengthValueFromData(data.width, f);
918 if (++i >= decl.d->values.count()) {
919 decl.d->parsed = qVariantFromValue<BorderData>(data);
920 return;
921 }
922 }
923
924 data.style = parseStyleValue(decl.d->values.at(i));
925 if (data.style != BorderStyle_Unknown) {
926 *style = data.style;
927 if (++i >= decl.d->values.count()) {
928 decl.d->parsed = qVariantFromValue<BorderData>(data);
929 return;
930 }
931 } else {
932 data.style = BorderStyle_None;
933 }
934
935 data.color = parseBrushValue(decl.d->values.at(i), pal);
936 *color = brushFromData(data.color, pal);
937 if (data.color.type != BrushData::DependsOnThePalette)
938 decl.d->parsed = qVariantFromValue<BorderData>(data);
939}
940
941static void parseShorthandBackgroundProperty(const QVector<Value> &values, BrushData *brush, QString *image, Repeat *repeat, Qt::Alignment *alignment, const QPalette &pal)
942{
943 *brush = BrushData();
944 *image = QString();
945 *repeat = Repeat_XY;
946 *alignment = Qt::AlignTop | Qt::AlignLeft;
947
948 for (int i = 0; i < values.count(); ++i) {
949 const Value &v = values.at(i);
950 if (v.type == Value::Uri) {
951 *image = v.variant.toString();
952 continue;
953 } else if (v.type == Value::KnownIdentifier && v.variant.toInt() == Value_None) {
954 *image = QString();
955 continue;
956 } else if (v.type == Value::KnownIdentifier && v.variant.toInt() == Value_Transparent) {
957 *brush = QBrush(Qt::transparent);
958 }
959
960 Repeat repeatAttempt = static_cast<Repeat>(findKnownValue(v.variant.toString(),
961 repeats, NumKnownRepeats));
962 if (repeatAttempt != Repeat_Unknown) {
963 *repeat = repeatAttempt;
964 continue;
965 }
966
967 if (v.type == Value::KnownIdentifier) {
968 const int start = i;
969 int count = 1;
970 if (i < values.count() - 1
971 && values.at(i + 1).type == Value::KnownIdentifier) {
972 ++i;
973 ++count;
974 }
975 Qt::Alignment a = parseAlignment(values.constData() + start, count);
976 if (int(a) != 0) {
977 *alignment = a;
978 continue;
979 }
980 i -= count - 1;
981 }
982
983 *brush = parseBrushValue(v, pal);
984 }
985}
986
987bool ValueExtractor::extractBackground(QBrush *brush, QString *image, Repeat *repeat,
988 Qt::Alignment *alignment, Origin *origin, Attachment *attachment,
989 Origin *clip)
990{
991 bool hit = false;
992 for (int i = 0; i < declarations.count(); ++i) {
993 const Declaration &decl = declarations.at(i);
994 if (decl.d->values.isEmpty())
995 continue;
996 const Value &val = decl.d->values.at(0);
997 switch (decl.d->propertyId) {
998 case BackgroundColor:
999 *brush = decl.brushValue();
1000 break;
1001 case BackgroundImage:
1002 if (val.type == Value::Uri)
1003 *image = val.variant.toString();
1004 break;
1005 case BackgroundRepeat:
1006 if (decl.d->parsed.isValid()) {
1007 *repeat = static_cast<Repeat>(decl.d->parsed.toInt());
1008 } else {
1009 *repeat = static_cast<Repeat>(findKnownValue(val.variant.toString(),
1010 repeats, NumKnownRepeats));
1011 decl.d->parsed = *repeat;
1012 }
1013 break;
1014 case BackgroundPosition:
1015 *alignment = decl.alignmentValue();
1016 break;
1017 case BackgroundOrigin:
1018 *origin = decl.originValue();
1019 break;
1020 case BackgroundClip:
1021 *clip = decl.originValue();
1022 break;
1023 case Background:
1024 if (decl.d->parsed.isValid()) {
1025 BackgroundData data = qvariant_cast<BackgroundData>(decl.d->parsed);
1026 *brush = brushFromData(data.brush, pal);
1027 *image = data.image;
1028 *repeat = data.repeat;
1029 *alignment = data.alignment;
1030 } else {
1031 BrushData brushData;
1032 parseShorthandBackgroundProperty(decl.d->values, &brushData, image, repeat, alignment, pal);
1033 *brush = brushFromData(brushData, pal);
1034 if (brushData.type != BrushData::DependsOnThePalette) {
1035#if defined Q_CC_MSVC && _MSC_VER <= 1300
1036 BackgroundData data;
1037 data.brush = brushData;
1038 data.image = *image;
1039 data.repeat = *repeat;
1040 data.alignment = *alignment;
1041#else
1042 BackgroundData data = { brushData, *image, *repeat, *alignment };
1043#endif
1044 decl.d->parsed = qVariantFromValue<BackgroundData>(data);
1045 }
1046 }
1047 break;
1048 case BackgroundAttachment:
1049 *attachment = decl.attachmentValue();
1050 break;
1051 default: continue;
1052 }
1053 hit = true;
1054 }
1055 return hit;
1056}
1057
1058static bool setFontSizeFromValue(Value value, QFont *font, int *fontSizeAdjustment)
1059{
1060 if (value.type == Value::KnownIdentifier) {
1061 bool valid = true;
1062 switch (value.variant.toInt()) {
1063 case Value_Small: *fontSizeAdjustment = -1; break;
1064 case Value_Medium: *fontSizeAdjustment = 0; break;
1065 case Value_Large: *fontSizeAdjustment = 1; break;
1066 case Value_XLarge: *fontSizeAdjustment = 2; break;
1067 case Value_XXLarge: *fontSizeAdjustment = 3; break;
1068 default: valid = false; break;
1069 }
1070 return valid;
1071 }
1072 if (value.type != Value::Length)
1073 return false;
1074
1075 bool valid = false;
1076 QString s = value.variant.toString();
1077 if (s.endsWith(QLatin1String("pt"), Qt::CaseInsensitive)) {
1078 s.chop(2);
1079 value.variant = s;
1080 if (value.variant.convert((QVariant::Type)qMetaTypeId<qreal>())) {
1081 font->setPointSizeF(value.variant.toReal());
1082 valid = true;
1083 }
1084 } else if (s.endsWith(QLatin1String("px"), Qt::CaseInsensitive)) {
1085 s.chop(2);
1086 value.variant = s;
1087 if (value.variant.convert(QVariant::Int)) {
1088 font->setPixelSize(value.variant.toInt());
1089 valid = true;
1090 }
1091 }
1092 return valid;
1093}
1094
1095static bool setFontStyleFromValue(const Value &value, QFont *font)
1096{
1097 if (value.type != Value::KnownIdentifier)
1098 return false ;
1099 switch (value.variant.toInt()) {
1100 case Value_Normal: font->setStyle(QFont::StyleNormal); return true;
1101 case Value_Italic: font->setStyle(QFont::StyleItalic); return true;
1102 case Value_Oblique: font->setStyle(QFont::StyleOblique); return true;
1103 default: break;
1104 }
1105 return false;
1106}
1107
1108static bool setFontWeightFromValue(const Value &value, QFont *font)
1109{
1110 if (value.type == Value::KnownIdentifier) {
1111 switch (value.variant.toInt()) {
1112 case Value_Normal: font->setWeight(QFont::Normal); return true;
1113 case Value_Bold: font->setWeight(QFont::Bold); return true;
1114 default: break;
1115 }
1116 return false;
1117 }
1118 if (value.type != Value::Number)
1119 return false;
1120 font->setWeight(qMin(value.variant.toInt() / 8, 99));
1121 return true;
1122}
1123
1124/** \internal
1125 * parse the font family from the values (starting from index \a start)
1126 * and set it the \a font
1127 * \returns true if a family was extracted.
1128 */
1129static bool setFontFamilyFromValues(const QVector<Value> &values, QFont *font, int start = 0)
1130{
1131 QString family;
1132 bool shouldAddSpace = false;
1133 for (int i = start; i < values.count(); ++i) {
1134 const Value &v = values.at(i);
1135 if (v.type == Value::TermOperatorComma) {
1136 family += QLatin1Char(',');
1137 shouldAddSpace = false;
1138 continue;
1139 }
1140 const QString str = v.variant.toString();
1141 if (str.isEmpty())
1142 break;
1143 if (shouldAddSpace)
1144 family += QLatin1Char(' ');
1145 family += str;
1146 shouldAddSpace = true;
1147 }
1148 if (family.isEmpty())
1149 return false;
1150 font->setFamily(family);
1151 return true;
1152}
1153
1154static void setTextDecorationFromValues(const QVector<Value> &values, QFont *font)
1155{
1156 for (int i = 0; i < values.count(); ++i) {
1157 if (values.at(i).type != Value::KnownIdentifier)
1158 continue;
1159 switch (values.at(i).variant.toInt()) {
1160 case Value_Underline: font->setUnderline(true); break;
1161 case Value_Overline: font->setOverline(true); break;
1162 case Value_LineThrough: font->setStrikeOut(true); break;
1163 case Value_None:
1164 font->setUnderline(false);
1165 font->setOverline(false);
1166 font->setStrikeOut(false);
1167 break;
1168 default: break;
1169 }
1170 }
1171}
1172
1173static void parseShorthandFontProperty(const QVector<Value> &values, QFont *font, int *fontSizeAdjustment)
1174{
1175 font->setStyle(QFont::StyleNormal);
1176 font->setWeight(QFont::Normal);
1177 *fontSizeAdjustment = -255;
1178
1179 int i = 0;
1180 while (i < values.count()) {
1181 if (setFontStyleFromValue(values.at(i), font)
1182 || setFontWeightFromValue(values.at(i), font))
1183 ++i;
1184 else
1185 break;
1186 }
1187
1188 if (i < values.count()) {
1189 setFontSizeFromValue(values.at(i), font, fontSizeAdjustment);
1190 ++i;
1191 }
1192
1193 if (i < values.count()) {
1194 setFontFamilyFromValues(values, font, i);
1195 }
1196}
1197
1198static void setFontVariantFromValue(const Value &value, QFont *font)
1199{
1200 if (value.type == Value::KnownIdentifier) {
1201 switch (value.variant.toInt()) {
1202 case Value_Normal: font->setCapitalization(QFont::MixedCase); break;
1203 case Value_SmallCaps: font->setCapitalization(QFont::SmallCaps); break;
1204 default: break;
1205 }
1206 }
1207}
1208
1209static void setTextTransformFromValue(const Value &value, QFont *font)
1210{
1211 if (value.type == Value::KnownIdentifier) {
1212 switch (value.variant.toInt()) {
1213 case Value_None: font->setCapitalization(QFont::MixedCase); break;
1214 case Value_Uppercase: font->setCapitalization(QFont::AllUppercase); break;
1215 case Value_Lowercase: font->setCapitalization(QFont::AllLowercase); break;
1216 default: break;
1217 }
1218 }
1219}
1220
1221bool ValueExtractor::extractFont(QFont *font, int *fontSizeAdjustment)
1222{
1223 if (fontExtracted) {
1224 *font = f;
1225 *fontSizeAdjustment = adjustment;
1226 return fontExtracted == 1;
1227 }
1228
1229 bool hit = false;
1230 for (int i = 0; i < declarations.count(); ++i) {
1231 const Declaration &decl = declarations.at(i);
1232 if (decl.d->values.isEmpty())
1233 continue;
1234 const Value &val = decl.d->values.at(0);
1235 switch (decl.d->propertyId) {
1236 case FontSize: setFontSizeFromValue(val, font, fontSizeAdjustment); break;
1237 case FontStyle: setFontStyleFromValue(val, font); break;
1238 case FontWeight: setFontWeightFromValue(val, font); break;
1239 case FontFamily: setFontFamilyFromValues(decl.d->values, font); break;
1240 case TextDecoration: setTextDecorationFromValues(decl.d->values, font); break;
1241 case Font: parseShorthandFontProperty(decl.d->values, font, fontSizeAdjustment); break;
1242 case FontVariant: setFontVariantFromValue(val, font); break;
1243 case TextTransform: setTextTransformFromValue(val, font); break;
1244 default: continue;
1245 }
1246 hit = true;
1247 }
1248
1249 f = *font;
1250 adjustment = *fontSizeAdjustment;
1251 fontExtracted = hit ? 1 : 2;
1252 return hit;
1253}
1254
1255bool ValueExtractor::extractPalette(QBrush *fg, QBrush *sfg, QBrush *sbg, QBrush *abg)
1256{
1257 bool hit = false;
1258 for (int i = 0; i < declarations.count(); ++i) {
1259 const Declaration &decl = declarations.at(i);
1260 switch (decl.d->propertyId) {
1261 case Color: *fg = decl.brushValue(pal); break;
1262 case QtSelectionForeground: *sfg = decl.brushValue(pal); break;
1263 case QtSelectionBackground: *sbg = decl.brushValue(pal); break;
1264 case QtAlternateBackground: *abg = decl.brushValue(pal); break;
1265 default: continue;
1266 }
1267 hit = true;
1268 }
1269 return hit;
1270}
1271
1272void ValueExtractor::extractFont()
1273{
1274 if (fontExtracted)
1275 return;
1276 int dummy = -255;
1277 extractFont(&f, &dummy);
1278}
1279
1280bool ValueExtractor::extractImage(QIcon *icon, Qt::Alignment *a, QSize *size)
1281{
1282 bool hit = false;
1283 for (int i = 0; i < declarations.count(); ++i) {
1284 const Declaration &decl = declarations.at(i);
1285 switch (decl.d->propertyId) {
1286 case QtImage:
1287 *icon = decl.iconValue();
1288 if (decl.d->values.count() > 0 && decl.d->values.at(0).type == Value::Uri) {
1289 // try to pull just the size from the image...
1290 QImageReader imageReader(decl.d->values.at(0).variant.toString());
1291 if ((*size = imageReader.size()).isNull()) {
1292 // but we'll have to load the whole image if the
1293 // format doesn't support just reading the size
1294 *size = imageReader.read().size();
1295 }
1296 }
1297 break;
1298 case QtImageAlignment: *a = decl.alignmentValue(); break;
1299 default: continue;
1300 }
1301 hit = true;
1302 }
1303 return hit;
1304}
1305
1306///////////////////////////////////////////////////////////////////////////////
1307// Declaration
1308QColor Declaration::colorValue(const QPalette &pal) const
1309{
1310 if (d->values.count() != 1)
1311 return QColor();
1312
1313 if (d->parsed.isValid()) {
1314 if (d->parsed.type() == QVariant::Color)
1315 return qvariant_cast<QColor>(d->parsed);
1316 if (d->parsed.type() == QVariant::Int)
1317 return pal.color((QPalette::ColorRole)(d->parsed.toInt()));
1318 }
1319
1320 ColorData color = parseColorValue(d->values.at(0));
1321 if(color.type == ColorData::Role) {
1322 d->parsed = qVariantFromValue<int>(color.role);
1323 return pal.color((QPalette::ColorRole)(color.role));
1324 } else {
1325 d->parsed = qVariantFromValue<QColor>(color.color);
1326 return color.color;
1327 }
1328}
1329
1330QBrush Declaration::brushValue(const QPalette &pal) const
1331{
1332 if (d->values.count() != 1)
1333 return QBrush();
1334
1335 if (d->parsed.isValid()) {
1336 if (d->parsed.type() == QVariant::Brush)
1337 return qvariant_cast<QBrush>(d->parsed);
1338 if (d->parsed.type() == QVariant::Int)
1339 return pal.color((QPalette::ColorRole)(d->parsed.toInt()));
1340 }
1341
1342 BrushData data = parseBrushValue(d->values.at(0), pal);
1343
1344 if(data.type == BrushData::Role) {
1345 d->parsed = qVariantFromValue<int>(data.role);
1346 return pal.color((QPalette::ColorRole)(data.role));
1347 } else {
1348 if (data.type != BrushData::DependsOnThePalette)
1349 d->parsed = qVariantFromValue<QBrush>(data.brush);
1350 return data.brush;
1351 }
1352}
1353
1354void Declaration::brushValues(QBrush *c, const QPalette &pal) const
1355{
1356 int needParse = 0x1f; // bits 0..3 say if we should parse the corresponding value.
1357 // the bit 4 say we need to update d->parsed
1358 int i = 0;
1359 if (d->parsed.isValid()) {
1360 needParse = 0;
1361 QList<QVariant> v = d->parsed.toList();
1362 for (i = 0; i < qMin(v.count(), 4); i++) {
1363 if (v.at(i).type() == QVariant::Brush) {
1364 c[i] = qvariant_cast<QBrush>(v.at(i));
1365 } else if (v.at(i).type() == QVariant::Int) {
1366 c[i] = pal.color((QPalette::ColorRole)(v.at(i).toInt()));
1367 } else {
1368 needParse |= (1<<i);
1369 }
1370 }
1371 }
1372 if (needParse != 0) {
1373 QList<QVariant> v;
1374 for (i = 0; i < qMin(d->values.count(), 4); i++) {
1375 if (!(needParse & (1<<i)))
1376 continue;
1377 BrushData data = parseBrushValue(d->values.at(i), pal);
1378 if(data.type == BrushData::Role) {
1379 v += qVariantFromValue<int>(data.role);
1380 c[i] = pal.color((QPalette::ColorRole)(data.role));
1381 } else {
1382 if (data.type != BrushData::DependsOnThePalette) {
1383 v += qVariantFromValue<QBrush>(data.brush);
1384 } else {
1385 v += QVariant();
1386 }
1387 c[i] = data.brush;
1388 }
1389 }
1390 if (needParse & 0x10)
1391 d->parsed = v;
1392 }
1393 if (i == 0) c[0] = c[1] = c[2] = c[3] = QBrush();
1394 else if (i == 1) c[3] = c[2] = c[1] = c[0];
1395 else if (i == 2) c[2] = c[0], c[3] = c[1];
1396 else if (i == 3) c[3] = c[1];
1397}
1398
1399bool Declaration::realValue(qreal *real, const char *unit) const
1400{
1401 if (d->values.count() != 1)
1402 return false;
1403 const Value &v = d->values.at(0);
1404 if (unit && v.type != Value::Length)
1405 return false;
1406 QString s = v.variant.toString();
1407 if (unit) {
1408 if (!s.endsWith(QLatin1String(unit), Qt::CaseInsensitive))
1409 return false;
1410 s.chop(qstrlen(unit));
1411 }
1412 bool ok = false;
1413 qreal val = s.toDouble(&ok);
1414 if (ok)
1415 *real = val;
1416 return ok;
1417}
1418
1419static bool intValueHelper(const Value &v, int *i, const char *unit)
1420{
1421 if (unit && v.type != Value::Length)
1422 return false;
1423 QString s = v.variant.toString();
1424 if (unit) {
1425 if (!s.endsWith(QLatin1String(unit), Qt::CaseInsensitive))
1426 return false;
1427 s.chop(qstrlen(unit));
1428 }
1429 bool ok = false;
1430 int val = s.toInt(&ok);
1431 if (ok)
1432 *i = val;
1433 return ok;
1434}
1435
1436bool Declaration::intValue(int *i, const char *unit) const
1437{
1438 if (d->values.count() != 1)
1439 return false;
1440 return intValueHelper(d->values.at(0), i, unit);
1441}
1442
1443QSize Declaration::sizeValue() const
1444{
1445 if (d->parsed.isValid())
1446 return qvariant_cast<QSize>(d->parsed);
1447
1448 int x[2] = { 0, 0 };
1449 if (d->values.count() > 0)
1450 intValueHelper(d->values.at(0), &x[0], "px");
1451 if (d->values.count() > 1)
1452 intValueHelper(d->values.at(1), &x[1], "px");
1453 else
1454 x[1] = x[0];
1455 QSize size(x[0], x[1]);
1456 d->parsed = qVariantFromValue<QSize>(size);
1457 return size;
1458}
1459
1460QRect Declaration::rectValue() const
1461{
1462 if (d->values.count() != 1)
1463 return QRect();
1464
1465 if (d->parsed.isValid())
1466 return qvariant_cast<QRect>(d->parsed);
1467
1468 const Value &v = d->values.at(0);
1469 if (v.type != Value::Function)
1470 return QRect();
1471 QStringList func = v.variant.toStringList();
1472 if (func.count() != 2 || func.at(0).compare(QLatin1String("rect")) != 0)
1473 return QRect();
1474 QStringList args = func[1].split(QLatin1Char(' '), QString::SkipEmptyParts);
1475 if (args.count() != 4)
1476 return QRect();
1477 QRect rect(args[0].toInt(), args[1].toInt(), args[2].toInt(), args[3].toInt());
1478 d->parsed = qVariantFromValue<QRect>(rect);
1479 return rect;
1480}
1481
1482void Declaration::colorValues(QColor *c, const QPalette &pal) const
1483{
1484 int i;
1485 if (d->parsed.isValid()) {
1486 QList<QVariant> v = d->parsed.toList();
1487 for (i = 0; i < qMin(d->values.count(), 4); i++) {
1488 if (v.at(i).type() == QVariant::Color) {
1489 c[i] = qvariant_cast<QColor>(v.at(i));
1490 } else {
1491 c[i] = pal.color((QPalette::ColorRole)(v.at(i).toInt()));
1492 }
1493 }
1494 } else {
1495 QList<QVariant> v;
1496 for (i = 0; i < qMin(d->values.count(), 4); i++) {
1497 ColorData color = parseColorValue(d->values.at(i));
1498 if(color.type == ColorData::Role) {
1499 v += qVariantFromValue<int>(color.role);
1500 c[i] = pal.color((QPalette::ColorRole)(color.role));
1501 } else {
1502 v += qVariantFromValue<QColor>(color.color);
1503 c[i] = color.color;
1504 }
1505 }
1506 d->parsed = v;
1507 }
1508
1509 if (i == 0) c[0] = c[1] = c[2] = c[3] = QColor();
1510 else if (i == 1) c[3] = c[2] = c[1] = c[0];
1511 else if (i == 2) c[2] = c[0], c[3] = c[1];
1512 else if (i == 3) c[3] = c[1];
1513}
1514
1515BorderStyle Declaration::styleValue() const
1516{
1517 if (d->values.count() != 1)
1518 return BorderStyle_None;
1519 return parseStyleValue(d->values.at(0));
1520}
1521
1522void Declaration::styleValues(BorderStyle *s) const
1523{
1524 int i;
1525 for (i = 0; i < qMin(d->values.count(), 4); i++)
1526 s[i] = parseStyleValue(d->values.at(i));
1527 if (i == 0) s[0] = s[1] = s[2] = s[3] = BorderStyle_None;
1528 else if (i == 1) s[3] = s[2] = s[1] = s[0];
1529 else if (i == 2) s[2] = s[0], s[3] = s[1];
1530 else if (i == 3) s[3] = s[1];
1531}
1532
1533Repeat Declaration::repeatValue() const
1534{
1535 if (d->parsed.isValid())
1536 return static_cast<Repeat>(d->parsed.toInt());
1537 if (d->values.count() != 1)
1538 return Repeat_Unknown;
1539 int v = findKnownValue(d->values.at(0).variant.toString(),
1540 repeats, NumKnownRepeats);
1541 d->parsed = v;
1542 return static_cast<Repeat>(v);
1543}
1544
1545Origin Declaration::originValue() const
1546{
1547 if (d->parsed.isValid())
1548 return static_cast<Origin>(d->parsed.toInt());
1549 if (d->values.count() != 1)
1550 return Origin_Unknown;
1551 int v = findKnownValue(d->values.at(0).variant.toString(),
1552 origins, NumKnownOrigins);
1553 d->parsed = v;
1554 return static_cast<Origin>(v);
1555}
1556
1557PositionMode Declaration::positionValue() const
1558{
1559 if (d->parsed.isValid())
1560 return static_cast<PositionMode>(d->parsed.toInt());
1561 if (d->values.count() != 1)
1562 return PositionMode_Unknown;
1563 int v = findKnownValue(d->values.at(0).variant.toString(),
1564 positions, NumKnownPositionModes);
1565 d->parsed = v;
1566 return static_cast<PositionMode>(v);
1567}
1568
1569Attachment Declaration::attachmentValue() const
1570{
1571 if (d->parsed.isValid())
1572 return static_cast<Attachment>(d->parsed.toInt());
1573 if (d->values.count() != 1)
1574 return Attachment_Unknown;
1575 int v = findKnownValue(d->values.at(0).variant.toString(),
1576 attachments, NumKnownAttachments);
1577 d->parsed = v;
1578 return static_cast<Attachment>(v);
1579}
1580
1581int Declaration::styleFeaturesValue() const
1582{
1583 Q_ASSERT(d->propertyId == QtStyleFeatures);
1584 if (d->parsed.isValid())
1585 return d->parsed.toInt();
1586 int features = StyleFeature_None;
1587 for (int i = 0; i < d->values.count(); i++) {
1588 features |= static_cast<int>(findKnownValue(d->values.value(i).variant.toString(),
1589 styleFeatures, NumKnownStyleFeatures));
1590 }
1591 d->parsed = features;
1592 return features;
1593}
1594
1595QString Declaration::uriValue() const
1596{
1597 if (d->values.isEmpty() || d->values.at(0).type != Value::Uri)
1598 return QString();
1599 return d->values.at(0).variant.toString();
1600}
1601
1602Qt::Alignment Declaration::alignmentValue() const
1603{
1604 if (d->parsed.isValid())
1605 return Qt::Alignment(d->parsed.toInt());
1606 if (d->values.isEmpty() || d->values.count() > 2)
1607 return Qt::AlignLeft | Qt::AlignTop;
1608
1609 Qt::Alignment v = parseAlignment(d->values.constData(), d->values.count());
1610 d->parsed = int(v);
1611 return v;
1612}
1613
1614void Declaration::borderImageValue(QString *image, int *cuts,
1615 TileMode *h, TileMode *v) const
1616{
1617 *image = uriValue();
1618 for (int i = 0; i < 4; i++)
1619 cuts[i] = -1;
1620 *h = *v = TileMode_Stretch;
1621
1622 if (d->values.count() < 2)
1623 return;
1624
1625 if (d->values.at(1).type == Value::Number) { // cuts!
1626 int i;
1627 for (i = 0; i < qMin(d->values.count()-1, 4); i++) {
1628 const Value& v = d->values.at(i+1);
1629 if (v.type != Value::Number)
1630 break;
1631 cuts[i] = v.variant.toString().toInt();
1632 }
1633 if (i == 0) cuts[0] = cuts[1] = cuts[2] = cuts[3] = 0;
1634 else if (i == 1) cuts[3] = cuts[2] = cuts[1] = cuts[0];
1635 else if (i == 2) cuts[2] = cuts[0], cuts[3] = cuts[1];
1636 else if (i == 3) cuts[3] = cuts[1];
1637 }
1638
1639 if (d->values.last().type == Value::Identifier) {
1640 *v = static_cast<TileMode>(findKnownValue(d->values.last().variant.toString(),
1641 tileModes, NumKnownTileModes));
1642 }
1643 if (d->values[d->values.count() - 2].type == Value::Identifier) {
1644 *h = static_cast<TileMode>
1645 (findKnownValue(d->values[d->values.count()-2].variant.toString(),
1646 tileModes, NumKnownTileModes));
1647 } else
1648 *h = *v;
1649}
1650
1651QIcon Declaration::iconValue() const
1652{
1653 if (d->parsed.isValid())
1654 return qvariant_cast<QIcon>(d->parsed);
1655
1656 QIcon icon;
1657 for (int i = 0; i < d->values.count();) {
1658 const Value &value = d->values.at(i++);
1659 if (value.type != Value::Uri)
1660 break;
1661 QString uri = value.variant.toString();
1662 QIcon::Mode mode = QIcon::Normal;
1663 QIcon::State state = QIcon::Off;
1664 for (int j = 0; j < 2; j++) {
1665 if (i != d->values.count() && d->values.at(i).type == Value::KnownIdentifier) {
1666 switch (d->values.at(i).variant.toInt()) {
1667 case Value_Disabled: mode = QIcon::Disabled; break;
1668 case Value_Active: mode = QIcon::Active; break;
1669 case Value_Selected: mode = QIcon::Selected; break;
1670 case Value_Normal: mode = QIcon::Normal; break;
1671 case Value_On: state = QIcon::On; break;
1672 case Value_Off: state = QIcon::Off; break;
1673 default: break;
1674 }
1675 ++i;
1676 } else {
1677 break;
1678 }
1679 }
1680
1681 // QIcon is soo broken
1682 if (icon.isNull())
1683 icon = QIcon(uri);
1684 else
1685 icon.addPixmap(uri, mode, state);
1686
1687 if (i == d->values.count())
1688 break;
1689
1690 if (d->values.at(i).type == Value::TermOperatorComma)
1691 i++;
1692 }
1693
1694 d->parsed = qVariantFromValue<QIcon>(icon);
1695 return icon;
1696}
1697
1698///////////////////////////////////////////////////////////////////////////////
1699// Selector
1700int Selector::specificity() const
1701{
1702 int val = 0;
1703 for (int i = 0; i < basicSelectors.count(); ++i) {
1704 const BasicSelector &sel = basicSelectors.at(i);
1705 if (!sel.elementName.isEmpty())
1706 val += 1;
1707
1708 val += (sel.pseudos.count() + sel.attributeSelectors.count()) * 0x10;
1709 val += sel.ids.count() * 0x100;
1710 }
1711 return val;
1712}
1713
1714QString Selector::pseudoElement() const
1715{
1716 const BasicSelector& bs = basicSelectors.last();
1717 if (!bs.pseudos.isEmpty() && bs.pseudos.at(0).type == PseudoClass_Unknown)
1718 return bs.pseudos.at(0).name;
1719 return QString();
1720}
1721
1722quint64 Selector::pseudoClass(quint64 *negated) const
1723{
1724 const BasicSelector& bs = basicSelectors.last();
1725 if (bs.pseudos.isEmpty())
1726 return PseudoClass_Unspecified;
1727 quint64 pc = PseudoClass_Unknown;
1728 for (int i = !pseudoElement().isEmpty(); i < bs.pseudos.count(); i++) {
1729 const Pseudo &pseudo = bs.pseudos.at(i);
1730 if (pseudo.type == PseudoClass_Unknown)
1731 return PseudoClass_Unknown;
1732 if (!pseudo.negated)
1733 pc |= pseudo.type;
1734 else if (negated)
1735 *negated |= pseudo.type;
1736 }
1737 return pc;
1738}
1739
1740///////////////////////////////////////////////////////////////////////////////
1741// StyleSheet
1742void StyleSheet::buildIndexes(Qt::CaseSensitivity nameCaseSensitivity)
1743{
1744 QVector<StyleRule> universals;
1745 for (int i = 0; i < styleRules.count(); ++i) {
1746 const StyleRule &rule = styleRules.at(i);
1747 QVector<Selector> universalsSelectors;
1748 for (int j = 0; j < rule.selectors.count(); ++j) {
1749 const Selector& selector = rule.selectors.at(j);
1750
1751 if (selector.basicSelectors.isEmpty())
1752 continue;
1753
1754 if (selector.basicSelectors.at(0).relationToNext == BasicSelector::NoRelation) {
1755 if (selector.basicSelectors.count() != 1)
1756 continue;
1757 } else if (selector.basicSelectors.count() <= 1) {
1758 continue;
1759 }
1760
1761 const BasicSelector &sel = selector.basicSelectors.at(selector.basicSelectors.count() - 1);
1762
1763 if (!sel.ids.isEmpty()) {
1764 StyleRule nr;
1765 nr.selectors += selector;
1766 nr.declarations = rule.declarations;
1767 nr.order = i;
1768 idIndex.insert(sel.ids.at(0), nr);
1769 } else if (!sel.elementName.isEmpty()) {
1770 StyleRule nr;
1771 nr.selectors += selector;
1772 nr.declarations = rule.declarations;
1773 nr.order = i;
1774 QString name = sel.elementName;
1775 if (nameCaseSensitivity == Qt::CaseInsensitive)
1776 name=name.toLower();
1777 nameIndex.insert(name, nr);
1778 } else {
1779 universalsSelectors += selector;
1780 }
1781 }
1782 if (!universalsSelectors.isEmpty()) {
1783 StyleRule nr;
1784 nr.selectors = universalsSelectors;
1785 nr.declarations = rule.declarations;
1786 nr.order = i;
1787 universals << nr;
1788 }
1789 }
1790 styleRules = universals;
1791}
1792
1793///////////////////////////////////////////////////////////////////////////////
1794// StyleSelector
1795StyleSelector::~StyleSelector()
1796{
1797}
1798
1799bool StyleSelector::nodeNameEquals(NodePtr node, const QString& nodeName) const
1800{
1801 return nodeNames(node).contains(nodeName, nameCaseSensitivity);
1802}
1803
1804QStringList StyleSelector::nodeIds(NodePtr node) const
1805{
1806 return QStringList(attribute(node, QLatin1String("id")));
1807}
1808
1809bool StyleSelector::selectorMatches(const Selector &selector, NodePtr node)
1810{
1811 if (selector.basicSelectors.isEmpty())
1812 return false;
1813
1814 if (selector.basicSelectors.at(0).relationToNext == BasicSelector::NoRelation) {
1815 if (selector.basicSelectors.count() != 1)
1816 return false;
1817 return basicSelectorMatches(selector.basicSelectors.at(0), node);
1818 }
1819 if (selector.basicSelectors.count() <= 1)
1820 return false;
1821
1822 int i = selector.basicSelectors.count() - 1;
1823 node = duplicateNode(node);
1824 bool match = true;
1825
1826 BasicSelector sel = selector.basicSelectors.at(i);
1827 do {
1828 match = basicSelectorMatches(sel, node);
1829 if (!match) {
1830 if (sel.relationToNext == BasicSelector::MatchNextSelectorIfParent
1831 || i == selector.basicSelectors.count() - 1) // first element must always match!
1832 break;
1833 }
1834
1835 if (match || sel.relationToNext != BasicSelector::MatchNextSelectorIfAncestor)
1836 --i;
1837
1838 if (i < 0)
1839 break;
1840
1841 sel = selector.basicSelectors.at(i);
1842 if (sel.relationToNext == BasicSelector::MatchNextSelectorIfAncestor
1843 || sel.relationToNext == BasicSelector::MatchNextSelectorIfParent) {
1844
1845 NodePtr nextParent = parentNode(node);
1846 freeNode(node);
1847 node = nextParent;
1848 } else if (sel.relationToNext == BasicSelector::MatchNextSelectorIfPreceeds) {
1849 NodePtr previousSibling = previousSiblingNode(node);
1850 freeNode(node);
1851 node = previousSibling;
1852 }
1853 if (isNullNode(node)) {
1854 match = false;
1855 break;
1856 }
1857 } while (i >= 0 && (match || sel.relationToNext == BasicSelector::MatchNextSelectorIfAncestor));
1858
1859 freeNode(node);
1860
1861 return match;
1862}
1863
1864bool StyleSelector::basicSelectorMatches(const BasicSelector &sel, NodePtr node)
1865{
1866 if (!sel.attributeSelectors.isEmpty()) {
1867 if (!hasAttributes(node))
1868 return false;
1869
1870 for (int i = 0; i < sel.attributeSelectors.count(); ++i) {
1871 const QCss::AttributeSelector &a = sel.attributeSelectors.at(i);
1872
1873 const QString attrValue = attribute(node, a.name);
1874 if (attrValue.isNull())
1875 return false;
1876
1877 if (a.valueMatchCriterium == QCss::AttributeSelector::MatchContains) {
1878
1879 QStringList lst = attrValue.split(QLatin1Char(' '));
1880 if (!lst.contains(a.value))
1881 return false;
1882 } else if (
1883 (a.valueMatchCriterium == QCss::AttributeSelector::MatchEqual
1884 && attrValue != a.value)
1885 ||
1886 (a.valueMatchCriterium == QCss::AttributeSelector::MatchBeginsWith
1887 && !attrValue.startsWith(a.value))
1888 )
1889 return false;
1890 }
1891 }
1892
1893 if (!sel.elementName.isEmpty()
1894 && !nodeNameEquals(node, sel.elementName))
1895 return false;
1896
1897 if (!sel.ids.isEmpty()
1898 && sel.ids != nodeIds(node))
1899 return false;
1900
1901 return true;
1902}
1903
1904void StyleSelector::matchRule(NodePtr node, const StyleRule &rule, StyleSheetOrigin origin,
1905 int depth, QMap<uint, StyleRule> *weightedRules)
1906{
1907 for (int j = 0; j < rule.selectors.count(); ++j) {
1908 const Selector& selector = rule.selectors.at(j);
1909 if (selectorMatches(selector, node)) {
1910 uint weight = rule.order
1911 + selector.specificity() *0x100
1912 + (uint(origin) + depth)*0x100000;
1913 StyleRule newRule = rule;
1914 if(rule.selectors.count() > 1) {
1915 newRule.selectors.resize(1);
1916 newRule.selectors[0] = selector;
1917 }
1918 //We might have rules with the same weight if they came from a rule with several selectors
1919 weightedRules->insertMulti(weight, newRule);
1920 }
1921 }
1922}
1923
1924// Returns style rules that are in ascending order of specificity
1925// Each of the StyleRule returned will contain exactly one Selector
1926QVector<StyleRule> StyleSelector::styleRulesForNode(NodePtr node)
1927{
1928 QVector<StyleRule> rules;
1929 if (styleSheets.isEmpty())
1930 return rules;
1931
1932 QMap<uint, StyleRule> weightedRules; // (spec, rule) that will be sorted below
1933
1934 //prune using indexed stylesheet
1935 for (int sheetIdx = 0; sheetIdx < styleSheets.count(); ++sheetIdx) {
1936 const StyleSheet &styleSheet = styleSheets.at(sheetIdx);
1937 for (int i = 0; i < styleSheet.styleRules.count(); ++i) {
1938 matchRule(node, styleSheet.styleRules.at(i), styleSheet.origin, styleSheet.depth, &weightedRules);
1939 }
1940
1941 if (!styleSheet.idIndex.isEmpty()) {
1942 QStringList ids = nodeIds(node);
1943 for (int i = 0; i < ids.count(); i++) {
1944 const QString &key = ids.at(i);
1945 QMultiHash<QString, StyleRule>::const_iterator it = styleSheet.idIndex.constFind(key);
1946 while (it != styleSheet.idIndex.constEnd() && it.key() == key) {
1947 matchRule(node, it.value(), styleSheet.origin, styleSheet.depth, &weightedRules);
1948 ++it;
1949 }
1950 }
1951 }
1952 if (!styleSheet.nameIndex.isEmpty()) {
1953 QStringList names = nodeNames(node);
1954 for (int i = 0; i < names.count(); i++) {
1955 QString name = names.at(i);
1956 if (nameCaseSensitivity == Qt::CaseInsensitive)
1957 name = name.toLower();
1958 QMultiHash<QString, StyleRule>::const_iterator it = styleSheet.nameIndex.constFind(name);
1959 while (it != styleSheet.nameIndex.constEnd() && it.key() == name) {
1960 matchRule(node, it.value(), styleSheet.origin, styleSheet.depth, &weightedRules);
1961 ++it;
1962 }
1963 }
1964 }
1965 if (!medium.isEmpty()) {
1966 for (int i = 0; i < styleSheet.mediaRules.count(); ++i) {
1967 if (styleSheet.mediaRules.at(i).media.contains(medium, Qt::CaseInsensitive)) {
1968 for (int j = 0; j < styleSheet.mediaRules.at(i).styleRules.count(); ++j) {
1969 matchRule(node, styleSheet.mediaRules.at(i).styleRules.at(j), styleSheet.origin,
1970 styleSheet.depth, &weightedRules);
1971 }
1972 }
1973 }
1974 }
1975 }
1976
1977 rules.reserve(weightedRules.count());
1978 QMap<uint, StyleRule>::const_iterator it = weightedRules.constBegin();
1979 for ( ; it != weightedRules.constEnd() ; ++it)
1980 rules += *it;
1981
1982 return rules;
1983}
1984
1985// for qtexthtmlparser which requires just the declarations with Enabled state
1986// and without pseudo elements
1987QVector<Declaration> StyleSelector::declarationsForNode(NodePtr node, const char *extraPseudo)
1988{
1989 QVector<Declaration> decls;
1990 QVector<StyleRule> rules = styleRulesForNode(node);
1991 for (int i = 0; i < rules.count(); i++) {
1992 const Selector& selector = rules.at(i).selectors.at(0);
1993 const QString pseudoElement = selector.pseudoElement();
1994
1995 if (extraPseudo && pseudoElement == QLatin1String(extraPseudo)) {
1996 decls += rules.at(i).declarations;
1997 continue;
1998 }
1999
2000 if (!pseudoElement.isEmpty()) // skip rules with pseudo elements
2001 continue;
2002 quint64 pseudoClass = selector.pseudoClass();
2003 if (pseudoClass == PseudoClass_Enabled || pseudoClass == PseudoClass_Unspecified)
2004 decls += rules.at(i).declarations;
2005 }
2006 return decls;
2007}
2008
2009static inline bool isHexDigit(const char c)
2010{
2011 return (c >= '0' && c <= '9')
2012 || (c >= 'a' && c <= 'f')
2013 || (c >= 'A' && c <= 'F')
2014 ;
2015}
2016
2017QString Scanner::preprocess(const QString &input, bool *hasEscapeSequences)
2018{
2019 QString output = input;
2020
2021 if (hasEscapeSequences)
2022 *hasEscapeSequences = false;
2023
2024 int i = 0;
2025 while (i < output.size()) {
2026 if (output.at(i) == QLatin1Char('\\')) {
2027
2028 ++i;
2029 // test for unicode hex escape
2030 int hexCount = 0;
2031 const int hexStart = i;
2032 while (i < output.size()
2033 && isHexDigit(output.at(i).toLatin1())
2034 && hexCount < 7) {
2035 ++hexCount;
2036 ++i;
2037 }
2038 if (hexCount == 0) {
2039 if (hasEscapeSequences)
2040 *hasEscapeSequences = true;
2041 continue;
2042 }
2043
2044 hexCount = qMin(hexCount, 6);
2045 bool ok = false;
2046 ushort code = output.mid(hexStart, hexCount).toUShort(&ok, 16);
2047 if (ok) {
2048 output.replace(hexStart - 1, hexCount + 1, QChar(code));
2049 i = hexStart;
2050 } else {
2051 i = hexStart;
2052 }
2053 } else {
2054 ++i;
2055 }
2056 }
2057 return output;
2058}
2059
2060int QCssScanner_Generated::handleCommentStart()
2061{
2062 while (pos < input.size() - 1) {
2063 if (input.at(pos) == QLatin1Char('*')
2064 && input.at(pos + 1) == QLatin1Char('/')) {
2065 pos += 2;
2066 break;
2067 }
2068 ++pos;
2069 }
2070 return S;
2071}
2072
2073void Scanner::scan(const QString &preprocessedInput, QVector<Symbol> *symbols)
2074{
2075 QCssScanner_Generated scanner(preprocessedInput);
2076 Symbol sym;
2077 int tok = scanner.lex();
2078 while (tok != -1) {
2079 sym.token = static_cast<QCss::TokenType>(tok);
2080 sym.text = scanner.input;
2081 sym.start = scanner.lexemStart;
2082 sym.len = scanner.lexemLength;
2083 symbols->append(sym);
2084 tok = scanner.lex();
2085 }
2086}
2087
2088QString Symbol::lexem() const
2089{
2090 QString result;
2091 if (len > 0)
2092 result.reserve(len);
2093 for (int i = 0; i < len; ++i) {
2094 if (text.at(start + i) == QLatin1Char('\\') && i < len - 1)
2095 ++i;
2096 result += text.at(start + i);
2097 }
2098 return result;
2099}
2100
2101Parser::Parser(const QString &css, bool isFile)
2102{
2103 init(css, isFile);
2104}
2105
2106Parser::Parser()
2107{
2108 index = 0;
2109 errorIndex = -1;
2110 hasEscapeSequences = false;
2111}
2112
2113void Parser::init(const QString &css, bool isFile)
2114{
2115 QString styleSheet = css;
2116 if (isFile) {
2117 QFile file(css);
2118 if (file.open(QFile::ReadOnly)) {
2119 sourcePath = QFileInfo(styleSheet).absolutePath() + QLatin1Char('/');
2120 QTextStream stream(&file);
2121 styleSheet = stream.readAll();
2122 } else {
2123 qWarning() << "QCss::Parser - Failed to load file " << css;
2124 styleSheet.clear();
2125 }
2126 } else {
2127 sourcePath.clear();
2128 }
2129
2130 hasEscapeSequences = false;
2131 symbols.resize(0);
2132 symbols.reserve(8);
2133 Scanner::scan(Scanner::preprocess(styleSheet, &hasEscapeSequences), &symbols);
2134 index = 0;
2135 errorIndex = -1;
2136}
2137
2138bool Parser::parse(StyleSheet *styleSheet, Qt::CaseSensitivity nameCaseSensitivity)
2139{
2140 if (testTokenAndEndsWith(ATKEYWORD_SYM, QLatin1String("charset"))) {
2141 if (!next(STRING)) return false;
2142 if (!next(SEMICOLON)) return false;
2143 }
2144
2145 while (test(S) || test(CDO) || test(CDC)) {}
2146
2147 while (testImport()) {
2148 ImportRule rule;
2149 if (!parseImport(&rule)) return false;
2150 styleSheet->importRules.append(rule);
2151 while (test(S) || test(CDO) || test(CDC)) {}
2152 }
2153
2154 do {
2155 if (testMedia()) {
2156 MediaRule rule;
2157 if (!parseMedia(&rule)) return false;
2158 styleSheet->mediaRules.append(rule);
2159 } else if (testPage()) {
2160 PageRule rule;
2161 if (!parsePage(&rule)) return false;
2162 styleSheet->pageRules.append(rule);
2163 } else if (testRuleset()) {
2164 StyleRule rule;
2165 if (!parseRuleset(&rule)) return false;
2166 styleSheet->styleRules.append(rule);
2167 } else if (test(ATKEYWORD_SYM)) {
2168 if (!until(RBRACE)) return false;
2169 } else if (hasNext()) {
2170 return false;
2171 }
2172 while (test(S) || test(CDO) || test(CDC)) {}
2173 } while (hasNext());
2174 styleSheet->buildIndexes(nameCaseSensitivity);
2175 return true;
2176}
2177
2178Symbol Parser::errorSymbol()
2179{
2180 if (errorIndex == -1) return Symbol();
2181 return symbols.at(errorIndex);
2182}
2183
2184static inline void removeOptionalQuotes(QString *str)
2185{
2186 if (!str->startsWith(QLatin1Char('\''))
2187 && !str->startsWith(QLatin1Char('\"')))
2188 return;
2189 str->remove(0, 1);
2190 str->chop(1);
2191}
2192
2193bool Parser::parseImport(ImportRule *importRule)
2194{
2195 skipSpace();
2196
2197 if (test(STRING)) {
2198 importRule->href = lexem();
2199 } else {
2200 if (!testAndParseUri(&importRule->href)) return false;
2201 }
2202 removeOptionalQuotes(&importRule->href);
2203
2204 skipSpace();
2205
2206 if (testMedium()) {
2207 if (!parseMedium(&importRule->media)) return false;
2208
2209 while (test(COMMA)) {
2210 skipSpace();
2211 if (!parseNextMedium(&importRule->media)) return false;
2212 }
2213 }
2214
2215 if (!next(SEMICOLON)) return false;
2216
2217 skipSpace();
2218 return true;
2219}
2220
2221bool Parser::parseMedia(MediaRule *mediaRule)
2222{
2223 do {
2224 skipSpace();
2225 if (!parseNextMedium(&mediaRule->media)) return false;
2226 } while (test(COMMA));
2227
2228 if (!next(LBRACE)) return false;
2229 skipSpace();
2230
2231 while (testRuleset()) {
2232 StyleRule rule;
2233 if (!parseRuleset(&rule)) return false;
2234 mediaRule->styleRules.append(rule);
2235 }
2236
2237 if (!next(RBRACE)) return false;
2238 skipSpace();
2239 return true;
2240}
2241
2242bool Parser::parseMedium(QStringList *media)
2243{
2244 media->append(lexem());
2245 skipSpace();
2246 return true;
2247}
2248
2249bool Parser::parsePage(PageRule *pageRule)
2250{
2251 skipSpace();
2252
2253 if (testPseudoPage())
2254 if (!parsePseudoPage(&pageRule->selector)) return false;
2255
2256 skipSpace();
2257 if (!next(LBRACE)) return false;
2258
2259 do {
2260 skipSpace();
2261 Declaration decl;
2262 if (!parseNextDeclaration(&decl)) return false;
2263 if (!decl.isEmpty())
2264 pageRule->declarations.append(decl);
2265 } while (test(SEMICOLON));
2266
2267 if (!next(RBRACE)) return false;
2268 skipSpace();
2269 return true;
2270}
2271
2272bool Parser::parsePseudoPage(QString *selector)
2273{
2274 if (!next(IDENT)) return false;
2275 *selector = lexem();
2276 return true;
2277}
2278
2279bool Parser::parseNextOperator(Value *value)
2280{
2281 if (!hasNext()) return true;
2282 switch (next()) {
2283 case SLASH: value->type = Value::TermOperatorSlash; skipSpace(); break;
2284 case COMMA: value->type = Value::TermOperatorComma; skipSpace(); break;
2285 default: prev(); break;
2286 }
2287 return true;
2288}
2289
2290bool Parser::parseCombinator(BasicSelector::Relation *relation)
2291{
2292 *relation = BasicSelector::NoRelation;
2293 if (lookup() == S) {
2294 *relation = BasicSelector::MatchNextSelectorIfAncestor;
2295 skipSpace();
2296 } else {
2297 prev();
2298 }
2299 if (test(PLUS)) {
2300 *relation = BasicSelector::MatchNextSelectorIfPreceeds;
2301 } else if (test(GREATER)) {
2302 *relation = BasicSelector::MatchNextSelectorIfParent;
2303 }
2304 skipSpace();
2305 return true;
2306}
2307
2308bool Parser::parseProperty(Declaration *decl)
2309{
2310 decl->d->property = lexem();
2311 decl->d->propertyId = static_cast<Property>(findKnownValue(decl->d->property, properties, NumProperties));
2312 skipSpace();
2313 return true;
2314}
2315
2316bool Parser::parseRuleset(StyleRule *styleRule)
2317{
2318 Selector sel;
2319 if (!parseSelector(&sel)) return false;
2320 styleRule->selectors.append(sel);
2321
2322 while (test(COMMA)) {
2323 skipSpace();
2324 Selector sel;
2325 if (!parseNextSelector(&sel)) return false;
2326 styleRule->selectors.append(sel);
2327 }
2328
2329 skipSpace();
2330 if (!next(LBRACE)) return false;
2331 const int declarationStart = index;
2332
2333 do {
2334 skipSpace();
2335 Declaration decl;
2336 const int rewind = index;
2337 if (!parseNextDeclaration(&decl)) {
2338 index = rewind;
2339 const bool foundSemicolon = until(SEMICOLON);
2340 const int semicolonIndex = index;
2341
2342 index = declarationStart;
2343 const bool foundRBrace = until(RBRACE);
2344
2345 if (foundSemicolon && semicolonIndex < index) {
2346 decl = Declaration();
2347 index = semicolonIndex - 1;
2348 } else {
2349 skipSpace();
2350 return foundRBrace;
2351 }
2352 }
2353 if (!decl.isEmpty())
2354 styleRule->declarations.append(decl);
2355 } while (test(SEMICOLON));
2356
2357 if (!next(RBRACE)) return false;
2358 skipSpace();
2359 return true;
2360}
2361
2362bool Parser::parseSelector(Selector *sel)
2363{
2364 BasicSelector basicSel;
2365 if (!parseSimpleSelector(&basicSel)) return false;
2366 while (testCombinator()) {
2367 if (!parseCombinator(&basicSel.relationToNext)) return false;
2368
2369 if (!testSimpleSelector()) break;
2370 sel->basicSelectors.append(basicSel);
2371
2372 basicSel = BasicSelector();
2373 if (!parseSimpleSelector(&basicSel)) return false;
2374 }
2375 sel->basicSelectors.append(basicSel);
2376 return true;
2377}
2378
2379bool Parser::parseSimpleSelector(BasicSelector *basicSel)
2380{
2381 int minCount = 0;
2382 if (lookupElementName()) {
2383 if (!parseElementName(&basicSel->elementName)) return false;
2384 } else {
2385 prev();
2386 minCount = 1;
2387 }
2388 bool onceMore;
2389 int count = 0;
2390 do {
2391 onceMore = false;
2392 if (test(HASH)) {
2393 QString theid = lexem();
2394 // chop off leading #
2395 theid.remove(0, 1);
2396 basicSel->ids.append(theid);
2397 onceMore = true;
2398 } else if (testClass()) {
2399 onceMore = true;
2400 AttributeSelector a;
2401 a.name = QLatin1String("class");
2402 a.valueMatchCriterium = AttributeSelector::MatchContains;
2403 if (!parseClass(&a.value)) return false;
2404 basicSel->attributeSelectors.append(a);
2405 } else if (testAttrib()) {
2406 onceMore = true;
2407 AttributeSelector a;
2408 if (!parseAttrib(&a)) return false;
2409 basicSel->attributeSelectors.append(a);
2410 } else if (testPseudo()) {
2411 onceMore = true;
2412 Pseudo ps;
2413 if (!parsePseudo(&ps)) return false;
2414 basicSel->pseudos.append(ps);
2415 }
2416 if (onceMore) ++count;
2417 } while (onceMore);
2418 return count >= minCount;
2419}
2420
2421bool Parser::parseClass(QString *name)
2422{
2423 if (!next(IDENT)) return false;
2424 *name = lexem();
2425 return true;
2426}
2427
2428bool Parser::parseElementName(QString *name)
2429{
2430 switch (lookup()) {
2431 case STAR: name->clear(); break;
2432 case IDENT: *name = lexem(); break;
2433 default: return false;
2434 }
2435 return true;
2436}
2437
2438bool Parser::parseAttrib(AttributeSelector *attr)
2439{
2440 skipSpace();
2441 if (!next(IDENT)) return false;
2442 attr->name = lexem();
2443 skipSpace();
2444
2445 if (test(EQUAL)) {
2446 attr->valueMatchCriterium = AttributeSelector::MatchEqual;
2447 } else if (test(INCLUDES)) {
2448 attr->valueMatchCriterium = AttributeSelector::MatchContains;
2449 } else if (test(DASHMATCH)) {
2450 attr->valueMatchCriterium = AttributeSelector::MatchBeginsWith;
2451 } else {
2452 return next(RBRACKET);
2453 }
2454
2455 skipSpace();
2456
2457 if (!test(IDENT) && !test(STRING)) return false;
2458 attr->value = unquotedLexem();
2459
2460 skipSpace();
2461 return next(RBRACKET);
2462}
2463
2464bool Parser::parsePseudo(Pseudo *pseudo)
2465{
2466 (void)test(COLON);
2467 pseudo->negated = test(EXCLAMATION_SYM);
2468 if (test(IDENT)) {
2469 pseudo->name = lexem();
2470 pseudo->type = static_cast<quint64>(findKnownValue(pseudo->name, pseudos, NumPseudos));
2471 return true;
2472 }
2473 if (!next(FUNCTION)) return false;
2474 pseudo->function = lexem();
2475 // chop off trailing parenthesis
2476 pseudo->function.chop(1);
2477 skipSpace();
2478 if (!test(IDENT)) return false;
2479 pseudo->name = lexem();
2480 skipSpace();
2481 return next(RPAREN);
2482}
2483
2484bool Parser::parseNextDeclaration(Declaration *decl)
2485{
2486 if (!testProperty())
2487 return true; // not an error!
2488 if (!parseProperty(decl)) return false;
2489 if (!next(COLON)) return false;
2490 skipSpace();
2491 if (!parseNextExpr(&decl->d->values)) return false;
2492 if (testPrio())
2493 if (!parsePrio(decl)) return false;
2494 return true;
2495}
2496
2497bool Parser::testPrio()
2498{
2499 const int rewind = index;
2500 if (!test(EXCLAMATION_SYM)) return false;
2501 skipSpace();
2502 if (!test(IDENT)) {
2503 index = rewind;
2504 return false;
2505 }
2506 if (lexem().compare(QLatin1String("important"), Qt::CaseInsensitive) != 0) {
2507 index = rewind;
2508 return false;
2509 }
2510 return true;
2511}
2512
2513bool Parser::parsePrio(Declaration *declaration)
2514{
2515 declaration->d->important = true;
2516 skipSpace();
2517 return true;
2518}
2519
2520bool Parser::parseExpr(QVector<Value> *values)
2521{
2522 Value val;
2523 if (!parseTerm(&val)) return false;
2524 values->append(val);
2525 bool onceMore;
2526 do {
2527 onceMore = false;
2528 val = Value();
2529 if (!parseNextOperator(&val)) return false;
2530 if (val.type != QCss::Value::Unknown)
2531 values->append(val);
2532 if (testTerm()) {
2533 onceMore = true;
2534 val = Value();
2535 if (!parseTerm(&val)) return false;
2536 values->append(val);
2537 }
2538 } while (onceMore);
2539 return true;
2540}
2541
2542bool Parser::testTerm()
2543{
2544 return test(PLUS) || test(MINUS)
2545 || test(NUMBER)
2546 || test(PERCENTAGE)
2547 || test(LENGTH)
2548 || test(STRING)
2549 || test(IDENT)
2550 || testHexColor()
2551 || testFunction();
2552}
2553
2554bool Parser::parseTerm(Value *value)
2555{
2556 QString str = lexem();
2557 bool haveUnary = false;
2558 if (lookup() == PLUS || lookup() == MINUS) {
2559 haveUnary = true;
2560 if (!hasNext()) return false;
2561 next();
2562 str += lexem();
2563 }
2564
2565 value->variant = str;
2566 value->type = QCss::Value::String;
2567 switch (lookup()) {
2568 case NUMBER:
2569 value->type = Value::Number;
2570 value->variant.convert(QVariant::Double);
2571 break;
2572 case PERCENTAGE:
2573 value->type = Value::Percentage;
2574 str.chop(1); // strip off %
2575 value->variant = str;
2576 break;
2577 case LENGTH:
2578 value->type = Value::Length;
2579 break;
2580
2581 case STRING:
2582 if (haveUnary) return false;
2583 value->type = Value::String;
2584 str.chop(1);
2585 str.remove(0, 1);
2586 value->variant = str;
2587 break;
2588 case IDENT: {
2589 if (haveUnary) return false;
2590 value->type = Value::Identifier;
2591 const int theid = findKnownValue(str, values, NumKnownValues);
2592 if (theid != 0) {
2593 value->type = Value::KnownIdentifier;
2594 value->variant = theid;
2595 }
2596 break;
2597 }
2598 default: {
2599 if (haveUnary) return false;
2600 prev();
2601 if (testHexColor()) {
2602 QColor col;
2603 if (!parseHexColor(&col)) return false;
2604 value->type = Value::Color;
2605 value->variant = col;
2606 } else if (testFunction()) {
2607 QString name, args;
2608 if (!parseFunction(&name, &args)) return false;
2609 if (name == QLatin1String("url")) {
2610 value->type = Value::Uri;
2611 removeOptionalQuotes(&args);
2612 if (QFileInfo(args).isRelative() && !sourcePath.isEmpty()) {
2613 args.prepend(sourcePath);
2614 }
2615 value->variant = args;
2616 } else {
2617 value->type = Value::Function;
2618 value->variant = QStringList() << name << args;
2619 }
2620 } else {
2621 return recordError();
2622 }
2623 return true;
2624 }
2625 }
2626 skipSpace();
2627 return true;
2628}
2629
2630bool Parser::parseFunction(QString *name, QString *args)
2631{
2632 *name = lexem();
2633 name->chop(1);
2634 skipSpace();
2635 const int start = index;
2636 if (!until(RPAREN)) return false;
2637 for (int i = start; i < index - 1; ++i)
2638 args->append(symbols.at(i).lexem());
2639 /*
2640 if (!nextExpr(&arguments)) return false;
2641 if (!next(RPAREN)) return false;
2642 */
2643 skipSpace();
2644 return true;
2645}
2646
2647bool Parser::parseHexColor(QColor *col)
2648{
2649 col->setNamedColor(lexem());
2650 if (!col->isValid()) {
2651 qWarning("QCssParser::parseHexColor: Unknown color name '%s'",lexem().toLatin1().constData());
2652 return false;
2653 }
2654 skipSpace();
2655 return true;
2656}
2657
2658bool Parser::testAndParseUri(QString *uri)
2659{
2660 const int rewind = index;
2661 if (!testFunction()) return false;
2662
2663 QString name, args;
2664 if (!parseFunction(&name, &args)) {
2665 index = rewind;
2666 return false;
2667 }
2668 if (name.toLower() != QLatin1String("url")) {
2669 index = rewind;
2670 return false;
2671 }
2672 *uri = args;
2673 removeOptionalQuotes(uri);
2674 return true;
2675}
2676
2677bool Parser::testSimpleSelector()
2678{
2679 return testElementName()
2680 || (test(HASH))
2681 || testClass()
2682 || testAttrib()
2683 || testPseudo();
2684}
2685
2686bool Parser::next(QCss::TokenType t)
2687{
2688 if (hasNext() && next() == t)
2689 return true;
2690 return recordError();
2691}
2692
2693bool Parser::test(QCss::TokenType t)
2694{
2695 if (index >= symbols.count())
2696 return false;
2697 if (symbols.at(index).token == t) {
2698 ++index;
2699 return true;
2700 }
2701 return false;
2702}
2703
2704QString Parser::unquotedLexem() const
2705{
2706 QString s = lexem();
2707 if (lookup() == STRING) {
2708 s.chop(1);
2709 s.remove(0, 1);
2710 }
2711 return s;
2712}
2713
2714QString Parser::lexemUntil(QCss::TokenType t)
2715{
2716 QString lexem;
2717 while (hasNext() && next() != t)
2718 lexem += symbol().lexem();
2719 return lexem;
2720}
2721
2722bool Parser::until(QCss::TokenType target, QCss::TokenType target2)
2723{
2724 int braceCount = 0;
2725 int brackCount = 0;
2726 int parenCount = 0;
2727 if (index) {
2728 switch(symbols.at(index-1).token) {
2729 case LBRACE: ++braceCount; break;
2730 case LBRACKET: ++brackCount; break;
2731 case FUNCTION:
2732 case LPAREN: ++parenCount; break;
2733 default: ;
2734 }
2735 }
2736 while (index < symbols.size()) {
2737 QCss::TokenType t = symbols.at(index++).token;
2738 switch (t) {
2739 case LBRACE: ++braceCount; break;
2740 case RBRACE: --braceCount; break;
2741 case LBRACKET: ++brackCount; break;
2742 case RBRACKET: --brackCount; break;
2743 case FUNCTION:
2744 case LPAREN: ++parenCount; break;
2745 case RPAREN: --parenCount; break;
2746 default: break;
2747 }
2748 if ((t == target || (target2 != NONE && t == target2))
2749 && braceCount <= 0
2750 && brackCount <= 0
2751 && parenCount <= 0)
2752 return true;
2753
2754 if (braceCount < 0 || brackCount < 0 || parenCount < 0) {
2755 --index;
2756 break;
2757 }
2758 }
2759 return false;
2760}
2761
2762bool Parser::testTokenAndEndsWith(QCss::TokenType t, const QLatin1String &str)
2763{
2764 if (!test(t)) return false;
2765 if (!lexem().endsWith(str, Qt::CaseInsensitive)) {
2766 prev();
2767 return false;
2768 }
2769 return true;
2770}
2771
2772QT_END_NAMESPACE
2773#endif // QT_NO_CSSPARSER
Note: See TracBrowser for help on using the repository browser.