1 /****************************************************************************
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtGui module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qcssparser_p.h"
47 #include <qfileinfo.h>
48 #include <qfontmetrics.h>
50 #include <qimagereader.h>
51 #include "private/qfunctions_p.h"
53 #ifndef QT_NO_CSSPARSER
57 #include "qcssscanner.cpp"
67 static const QCssKnownValue properties[NumProperties - 1] = {
68 { "-qt-background-role", QtBackgroundRole },
69 { "-qt-block-indent", QtBlockIndent },
70 { "-qt-list-indent", QtListIndent },
71 { "-qt-list-number-prefix", QtListNumberPrefix },
72 { "-qt-list-number-suffix", QtListNumberSuffix },
73 { "-qt-paragraph-type", QtParagraphType },
74 { "-qt-style-features", QtStyleFeatures },
75 { "-qt-table-type", QtTableType },
76 { "-qt-user-state", QtUserState },
77 { "alternate-background-color", QtAlternateBackground },
78 { "background", Background },
79 { "background-attachment", BackgroundAttachment },
80 { "background-clip", BackgroundClip },
81 { "background-color", BackgroundColor },
82 { "background-image", BackgroundImage },
83 { "background-origin", BackgroundOrigin },
84 { "background-position", BackgroundPosition },
85 { "background-repeat", BackgroundRepeat },
87 { "border-bottom", BorderBottom },
88 { "border-bottom-color", BorderBottomColor },
89 { "border-bottom-left-radius", BorderBottomLeftRadius },
90 { "border-bottom-right-radius", BorderBottomRightRadius },
91 { "border-bottom-style", BorderBottomStyle },
92 { "border-bottom-width", BorderBottomWidth },
93 { "border-color", BorderColor },
94 { "border-image", BorderImage },
95 { "border-left", BorderLeft },
96 { "border-left-color", BorderLeftColor },
97 { "border-left-style", BorderLeftStyle },
98 { "border-left-width", BorderLeftWidth },
99 { "border-radius", BorderRadius },
100 { "border-right", BorderRight },
101 { "border-right-color", BorderRightColor },
102 { "border-right-style", BorderRightStyle },
103 { "border-right-width", BorderRightWidth },
104 { "border-style", BorderStyles },
105 { "border-top", BorderTop },
106 { "border-top-color", BorderTopColor },
107 { "border-top-left-radius", BorderTopLeftRadius },
108 { "border-top-right-radius", BorderTopRightRadius },
109 { "border-top-style", BorderTopStyle },
110 { "border-top-width", BorderTopWidth },
111 { "border-width", BorderWidth },
112 { "bottom", Bottom },
116 { "font-family", FontFamily },
117 { "font-size", FontSize },
118 { "font-style", FontStyle },
119 { "font-variant", FontVariant },
120 { "font-weight", FontWeight },
121 { "height", Height },
122 { "image", QtImage },
123 { "image-position", QtImageAlignment },
125 { "line-height", LineHeight },
126 { "list-style", ListStyle },
127 { "list-style-type", ListStyleType },
128 { "margin" , Margin },
129 { "margin-bottom", MarginBottom },
130 { "margin-left", MarginLeft },
131 { "margin-right", MarginRight },
132 { "margin-top", MarginTop },
133 { "max-height", MaximumHeight },
134 { "max-width", MaximumWidth },
135 { "min-height", MinimumHeight },
136 { "min-width", MinimumWidth },
137 { "outline", Outline },
138 { "outline-bottom-left-radius", OutlineBottomLeftRadius },
139 { "outline-bottom-right-radius", OutlineBottomRightRadius },
140 { "outline-color", OutlineColor },
141 { "outline-offset", OutlineOffset },
142 { "outline-radius", OutlineRadius },
143 { "outline-style", OutlineStyle },
144 { "outline-top-left-radius", OutlineTopLeftRadius },
145 { "outline-top-right-radius", OutlineTopRightRadius },
146 { "outline-width", OutlineWidth },
147 { "padding", Padding },
148 { "padding-bottom", PaddingBottom },
149 { "padding-left", PaddingLeft },
150 { "padding-right", PaddingRight },
151 { "padding-top", PaddingTop },
152 { "page-break-after", PageBreakAfter },
153 { "page-break-before", PageBreakBefore },
154 { "position", Position },
156 { "selection-background-color", QtSelectionBackground },
157 { "selection-color", QtSelectionForeground },
158 { "spacing", QtSpacing },
159 { "subcontrol-origin", QtOrigin },
160 { "subcontrol-position", QtPosition },
161 { "text-align", TextAlignment },
162 { "text-decoration", TextDecoration },
163 { "text-indent", TextIndent },
164 { "text-transform", TextTransform },
165 { "text-underline-style", TextUnderlineStyle },
167 { "vertical-align", VerticalAlignment },
168 { "white-space", Whitespace },
172 static const QCssKnownValue values[NumKnownValues - 1] = {
173 { "active", Value_Active },
174 { "alternate-base", Value_AlternateBase },
175 { "always", Value_Always },
176 { "auto", Value_Auto },
177 { "base", Value_Base },
178 { "bold", Value_Bold },
179 { "bottom", Value_Bottom },
180 { "bright-text", Value_BrightText },
181 { "button", Value_Button },
182 { "button-text", Value_ButtonText },
183 { "center", Value_Center },
184 { "circle", Value_Circle },
185 { "dark", Value_Dark },
186 { "dashed", Value_Dashed },
187 { "decimal", Value_Decimal },
188 { "disabled", Value_Disabled },
189 { "disc", Value_Disc },
190 { "dot-dash", Value_DotDash },
191 { "dot-dot-dash", Value_DotDotDash },
192 { "dotted", Value_Dotted },
193 { "double", Value_Double },
194 { "groove", Value_Groove },
195 { "highlight", Value_Highlight },
196 { "highlighted-text", Value_HighlightedText },
197 { "inset", Value_Inset },
198 { "italic", Value_Italic },
199 { "large", Value_Large },
200 { "left", Value_Left },
201 { "light", Value_Light },
202 { "line-through", Value_LineThrough },
203 { "link", Value_Link },
204 { "link-visited", Value_LinkVisited },
205 { "lower-alpha", Value_LowerAlpha },
206 { "lower-roman", Value_LowerRoman },
207 { "lowercase", Value_Lowercase },
208 { "medium", Value_Medium },
209 { "mid", Value_Mid },
210 { "middle", Value_Middle },
211 { "midlight", Value_Midlight },
212 { "native", Value_Native },
213 { "none", Value_None },
214 { "normal", Value_Normal },
215 { "nowrap", Value_NoWrap },
216 { "oblique", Value_Oblique },
217 { "off", Value_Off },
219 { "outset", Value_Outset },
220 { "overline", Value_Overline },
221 { "pre", Value_Pre },
222 { "pre-wrap", Value_PreWrap },
223 { "ridge", Value_Ridge },
224 { "right", Value_Right },
225 { "selected", Value_Selected },
226 { "shadow", Value_Shadow },
227 { "small" , Value_Small },
228 { "small-caps", Value_SmallCaps },
229 { "solid", Value_Solid },
230 { "square", Value_Square },
231 { "sub", Value_Sub },
232 { "super", Value_Super },
233 { "text", Value_Text },
234 { "top", Value_Top },
235 { "transparent", Value_Transparent },
236 { "underline", Value_Underline },
237 { "upper-alpha", Value_UpperAlpha },
238 { "upper-roman", Value_UpperRoman },
239 { "uppercase", Value_Uppercase },
240 { "wave", Value_Wave },
241 { "window", Value_Window },
242 { "window-text", Value_WindowText },
243 { "x-large", Value_XLarge },
244 { "xx-large", Value_XXLarge }
247 //Map id to strings as they appears in the 'values' array above
248 static const short indexOfId[NumKnownValues] = { 0, 41, 48, 42, 49, 54, 35, 26, 70, 71, 25, 43, 5, 63, 47,
249 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,
250 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,
251 1, 15, 0, 52, 45, 44 };
253 QString Value::toString() const
255 if (type == KnownIdentifier) {
256 return QLatin1String(values[indexOfId[variant.toInt()]].name);
258 return variant.toString();
262 static const QCssKnownValue pseudos[NumPseudos - 1] = {
263 { "active", PseudoClass_Active },
264 { "adjoins-item", PseudoClass_Item },
265 { "alternate", PseudoClass_Alternate },
266 { "bottom", PseudoClass_Bottom },
267 { "checked", PseudoClass_Checked },
268 { "closable", PseudoClass_Closable },
269 { "closed", PseudoClass_Closed },
270 { "default", PseudoClass_Default },
271 { "disabled", PseudoClass_Disabled },
272 { "edit-focus", PseudoClass_EditFocus },
273 { "editable", PseudoClass_Editable },
274 { "enabled", PseudoClass_Enabled },
275 { "exclusive", PseudoClass_Exclusive },
276 { "first", PseudoClass_First },
277 { "flat", PseudoClass_Flat },
278 { "floatable", PseudoClass_Floatable },
279 { "focus", PseudoClass_Focus },
280 { "has-children", PseudoClass_Children },
281 { "has-siblings", PseudoClass_Sibling },
282 { "horizontal", PseudoClass_Horizontal },
283 { "hover", PseudoClass_Hover },
284 { "indeterminate" , PseudoClass_Indeterminate },
285 { "last", PseudoClass_Last },
286 { "left", PseudoClass_Left },
287 { "maximized", PseudoClass_Maximized },
288 { "middle", PseudoClass_Middle },
289 { "minimized", PseudoClass_Minimized },
290 { "movable", PseudoClass_Movable },
291 { "next-selected", PseudoClass_NextSelected },
292 { "no-frame", PseudoClass_Frameless },
293 { "non-exclusive", PseudoClass_NonExclusive },
294 { "off", PseudoClass_Unchecked },
295 { "on", PseudoClass_Checked },
296 { "only-one", PseudoClass_OnlyOne },
297 { "open", PseudoClass_Open },
298 { "pressed", PseudoClass_Pressed },
299 { "previous-selected", PseudoClass_PreviousSelected },
300 { "read-only", PseudoClass_ReadOnly },
301 { "right", PseudoClass_Right },
302 { "selected", PseudoClass_Selected },
303 { "top", PseudoClass_Top },
304 { "unchecked" , PseudoClass_Unchecked },
305 { "vertical", PseudoClass_Vertical },
306 { "window", PseudoClass_Window }
309 static const QCssKnownValue origins[NumKnownOrigins - 1] = {
310 { "border", Origin_Border },
311 { "content", Origin_Content },
312 { "margin", Origin_Margin }, // not in css
313 { "padding", Origin_Padding }
316 static const QCssKnownValue repeats[NumKnownRepeats - 1] = {
317 { "no-repeat", Repeat_None },
318 { "repeat-x", Repeat_X },
319 { "repeat-xy", Repeat_XY },
320 { "repeat-y", Repeat_Y }
323 static const QCssKnownValue tileModes[NumKnownTileModes - 1] = {
324 { "repeat", TileMode_Repeat },
325 { "round", TileMode_Round },
326 { "stretch", TileMode_Stretch },
329 static const QCssKnownValue positions[NumKnownPositionModes - 1] = {
330 { "absolute", PositionMode_Absolute },
331 { "fixed", PositionMode_Fixed },
332 { "relative", PositionMode_Relative },
333 { "static", PositionMode_Static }
336 static const QCssKnownValue attachments[NumKnownAttachments - 1] = {
337 { "fixed", Attachment_Fixed },
338 { "scroll", Attachment_Scroll }
341 static const QCssKnownValue styleFeatures[NumKnownStyleFeatures - 1] = {
342 { "background-color", StyleFeature_BackgroundColor },
343 { "background-gradient", StyleFeature_BackgroundGradient },
344 { "none", StyleFeature_None }
347 Q_STATIC_GLOBAL_OPERATOR bool operator<(const QString &name, const QCssKnownValue &prop)
349 return QString::compare(name, QLatin1String(prop.name), Qt::CaseInsensitive) < 0;
352 Q_STATIC_GLOBAL_OPERATOR bool operator<(const QCssKnownValue &prop, const QString &name)
354 return QString::compare(QLatin1String(prop.name), name, Qt::CaseInsensitive) < 0;
357 static quint64 findKnownValue(const QString &name, const QCssKnownValue *start, int numValues)
359 const QCssKnownValue *end = &start[numValues - 1];
360 const QCssKnownValue *prop = qBinaryFind(start, end, name);
366 ///////////////////////////////////////////////////////////////////////////////
368 ValueExtractor::ValueExtractor(const QVector<Declaration> &decls, const QPalette &pal)
369 : declarations(decls), adjustment(0), fontExtracted(false), pal(pal)
373 LengthData ValueExtractor::lengthValue(const Value& v)
375 QString s = v.variant.toString();
376 s.reserve(s.length());
378 data.unit = LengthData::None;
379 if (s.endsWith(QLatin1String("px"), Qt::CaseInsensitive))
380 data.unit = LengthData::Px;
381 else if (s.endsWith(QLatin1String("ex"), Qt::CaseInsensitive))
382 data.unit = LengthData::Ex;
383 else if (s.endsWith(QLatin1String("em"), Qt::CaseInsensitive))
384 data.unit = LengthData::Em;
386 if (data.unit != LengthData::None)
389 data.number = s.toDouble();
393 static int lengthValueFromData(const LengthData& data, const QFont& f)
395 if (data.unit == LengthData::Ex)
396 return qRound(QFontMetrics(f).xHeight() * data.number);
397 else if (data.unit == LengthData::Em)
398 return qRound(QFontMetrics(f).height() * data.number);
399 return qRound(data.number);
402 int ValueExtractor::lengthValue(const Declaration &decl)
404 if (decl.d->parsed.isValid())
405 return lengthValueFromData(qvariant_cast<LengthData>(decl.d->parsed), f);
406 if (decl.d->values.count() < 1)
408 LengthData data = lengthValue(decl.d->values.at(0));
409 decl.d->parsed = QVariant::fromValue<LengthData>(data);
410 return lengthValueFromData(data,f);
413 void ValueExtractor::lengthValues(const Declaration &decl, int *m)
415 if (decl.d->parsed.isValid()) {
416 QList<QVariant> v = decl.d->parsed.toList();
417 for (int i = 0; i < 4; i++)
418 m[i] = lengthValueFromData(qvariant_cast<LengthData>(v.at(i)), f);
424 for (i = 0; i < qMin(decl.d->values.count(), 4); i++)
425 datas[i] = lengthValue(decl.d->values[i]);
428 LengthData zero = {0.0, LengthData::None};
429 datas[0] = datas[1] = datas[2] = datas[3] = zero;
431 datas[3] = datas[2] = datas[1] = datas[0];
440 for (i = 0; i < 4; i++) {
441 v += QVariant::fromValue<LengthData>(datas[i]);
442 m[i] = lengthValueFromData(datas[i], f);
447 bool ValueExtractor::extractGeometry(int *w, int *h, int *minw, int *minh, int *maxw, int *maxh)
451 for (int i = 0; i < declarations.count(); i++) {
452 const Declaration &decl = declarations.at(i);
453 switch (decl.d->propertyId) {
454 case Width: *w = lengthValue(decl); break;
455 case Height: *h = lengthValue(decl); break;
456 case MinimumWidth: *minw = lengthValue(decl); break;
457 case MinimumHeight: *minh = lengthValue(decl); break;
458 case MaximumWidth: *maxw = lengthValue(decl); break;
459 case MaximumHeight: *maxh = lengthValue(decl); break;
468 bool ValueExtractor::extractPosition(int *left, int *top, int *right, int *bottom, QCss::Origin *origin,
469 Qt::Alignment *position, QCss::PositionMode *mode, Qt::Alignment *textAlignment)
473 for (int i = 0; i < declarations.count(); i++) {
474 const Declaration &decl = declarations.at(i);
475 switch (decl.d->propertyId) {
476 case Left: *left = lengthValue(decl); break;
477 case Top: *top = lengthValue(decl); break;
478 case Right: *right = lengthValue(decl); break;
479 case Bottom: *bottom = lengthValue(decl); break;
480 case QtOrigin: *origin = decl.originValue(); break;
481 case QtPosition: *position = decl.alignmentValue(); break;
482 case TextAlignment: *textAlignment = decl.alignmentValue(); break;
483 case Position: *mode = decl.positionValue(); break;
492 bool ValueExtractor::extractBox(int *margins, int *paddings, int *spacing)
496 for (int i = 0; i < declarations.count(); i++) {
497 const Declaration &decl = declarations.at(i);
498 switch (decl.d->propertyId) {
499 case PaddingLeft: paddings[LeftEdge] = lengthValue(decl); break;
500 case PaddingRight: paddings[RightEdge] = lengthValue(decl); break;
501 case PaddingTop: paddings[TopEdge] = lengthValue(decl); break;
502 case PaddingBottom: paddings[BottomEdge] = lengthValue(decl); break;
503 case Padding: lengthValues(decl, paddings); break;
505 case MarginLeft: margins[LeftEdge] = lengthValue(decl); break;
506 case MarginRight: margins[RightEdge] = lengthValue(decl); break;
507 case MarginTop: margins[TopEdge] = lengthValue(decl); break;
508 case MarginBottom: margins[BottomEdge] = lengthValue(decl); break;
509 case Margin: lengthValues(decl, margins); break;
510 case QtSpacing: if (spacing) *spacing = lengthValue(decl); break;
520 int ValueExtractor::extractStyleFeatures()
522 int features = StyleFeature_None;
523 for (int i = 0; i < declarations.count(); i++) {
524 const Declaration &decl = declarations.at(i);
525 if (decl.d->propertyId == QtStyleFeatures)
526 features = decl.styleFeaturesValue();
531 QSize ValueExtractor::sizeValue(const Declaration &decl)
533 if (decl.d->parsed.isValid()) {
534 QList<QVariant> v = decl.d->parsed.toList();
535 return QSize(lengthValueFromData(qvariant_cast<LengthData>(v.at(0)), f),
536 lengthValueFromData(qvariant_cast<LengthData>(v.at(1)), f));
539 LengthData x[2] = { {0, LengthData::None }, {0, LengthData::None} };
540 if (decl.d->values.count() > 0)
541 x[0] = lengthValue(decl.d->values.at(0));
542 if (decl.d->values.count() > 1)
543 x[1] = lengthValue(decl.d->values.at(1));
547 v << QVariant::fromValue<LengthData>(x[0]) << qVariantFromValue<LengthData>(x[1]);
549 return QSize(lengthValueFromData(x[0], f), lengthValueFromData(x[1], f));
552 void ValueExtractor::sizeValues(const Declaration &decl, QSize *radii)
554 radii[0] = sizeValue(decl);
555 for (int i = 1; i < 4; i++)
559 bool ValueExtractor::extractBorder(int *borders, QBrush *colors, BorderStyle *styles,
564 for (int i = 0; i < declarations.count(); i++) {
565 const Declaration &decl = declarations.at(i);
566 switch (decl.d->propertyId) {
567 case BorderLeftWidth: borders[LeftEdge] = lengthValue(decl); break;
568 case BorderRightWidth: borders[RightEdge] = lengthValue(decl); break;
569 case BorderTopWidth: borders[TopEdge] = lengthValue(decl); break;
570 case BorderBottomWidth: borders[BottomEdge] = lengthValue(decl); break;
571 case BorderWidth: lengthValues(decl, borders); break;
573 case BorderLeftColor: colors[LeftEdge] = decl.brushValue(pal); break;
574 case BorderRightColor: colors[RightEdge] = decl.brushValue(pal); break;
575 case BorderTopColor: colors[TopEdge] = decl.brushValue(pal); break;
576 case BorderBottomColor: colors[BottomEdge] = decl.brushValue(pal); break;
577 case BorderColor: decl.brushValues(colors, pal); break;
579 case BorderTopStyle: styles[TopEdge] = decl.styleValue(); break;
580 case BorderBottomStyle: styles[BottomEdge] = decl.styleValue(); break;
581 case BorderLeftStyle: styles[LeftEdge] = decl.styleValue(); break;
582 case BorderRightStyle: styles[RightEdge] = decl.styleValue(); break;
583 case BorderStyles: decl.styleValues(styles); break;
585 case BorderTopLeftRadius: radii[0] = sizeValue(decl); break;
586 case BorderTopRightRadius: radii[1] = sizeValue(decl); break;
587 case BorderBottomLeftRadius: radii[2] = sizeValue(decl); break;
588 case BorderBottomRightRadius: radii[3] = sizeValue(decl); break;
589 case BorderRadius: sizeValues(decl, radii); break;
592 borderValue(decl, &borders[LeftEdge], &styles[LeftEdge], &colors[LeftEdge]);
595 borderValue(decl, &borders[TopEdge], &styles[TopEdge], &colors[TopEdge]);
598 borderValue(decl, &borders[RightEdge], &styles[RightEdge], &colors[RightEdge]);
601 borderValue(decl, &borders[BottomEdge], &styles[BottomEdge], &colors[BottomEdge]);
604 borderValue(decl, &borders[LeftEdge], &styles[LeftEdge], &colors[LeftEdge]);
605 borders[TopEdge] = borders[RightEdge] = borders[BottomEdge] = borders[LeftEdge];
606 styles[TopEdge] = styles[RightEdge] = styles[BottomEdge] = styles[LeftEdge];
607 colors[TopEdge] = colors[RightEdge] = colors[BottomEdge] = colors[LeftEdge];
618 bool ValueExtractor::extractOutline(int *borders, QBrush *colors, BorderStyle *styles,
619 QSize *radii, int *offsets)
623 for (int i = 0; i < declarations.count(); i++) {
624 const Declaration &decl = declarations.at(i);
625 switch (decl.d->propertyId) {
626 case OutlineWidth: lengthValues(decl, borders); break;
627 case OutlineColor: decl.brushValues(colors, pal); break;
628 case OutlineStyle: decl.styleValues(styles); break;
630 case OutlineTopLeftRadius: radii[0] = sizeValue(decl); break;
631 case OutlineTopRightRadius: radii[1] = sizeValue(decl); break;
632 case OutlineBottomLeftRadius: radii[2] = sizeValue(decl); break;
633 case OutlineBottomRightRadius: radii[3] = sizeValue(decl); break;
634 case OutlineRadius: sizeValues(decl, radii); break;
635 case OutlineOffset: lengthValues(decl, offsets); break;
638 borderValue(decl, &borders[LeftEdge], &styles[LeftEdge], &colors[LeftEdge]);
639 borders[TopEdge] = borders[RightEdge] = borders[BottomEdge] = borders[LeftEdge];
640 styles[TopEdge] = styles[RightEdge] = styles[BottomEdge] = styles[LeftEdge];
641 colors[TopEdge] = colors[RightEdge] = colors[BottomEdge] = colors[LeftEdge];
652 static Qt::Alignment parseAlignment(const QCss::Value *values, int count)
654 Qt::Alignment a[2] = { 0, 0 };
655 for (int i = 0; i < qMin(2, count); i++) {
656 if (values[i].type != Value::KnownIdentifier)
658 switch (values[i].variant.toInt()) {
659 case Value_Left: a[i] = Qt::AlignLeft; break;
660 case Value_Right: a[i] = Qt::AlignRight; break;
661 case Value_Top: a[i] = Qt::AlignTop; break;
662 case Value_Bottom: a[i] = Qt::AlignBottom; break;
663 case Value_Center: a[i] = Qt::AlignCenter; break;
668 if (a[0] == Qt::AlignCenter && a[1] != 0 && a[1] != Qt::AlignCenter)
669 a[0] = (a[1] == Qt::AlignLeft || a[1] == Qt::AlignRight) ? Qt::AlignVCenter : Qt::AlignHCenter;
670 if ((a[1] == 0 || a[1] == Qt::AlignCenter) && a[0] != Qt::AlignCenter)
671 a[1] = (a[0] == Qt::AlignLeft || a[0] == Qt::AlignRight) ? Qt::AlignVCenter : Qt::AlignHCenter;
675 static ColorData parseColorValue(QCss::Value v)
677 if (v.type == Value::Identifier || v.type == Value::String) {
678 v.variant.convert(QVariant::Color);
679 v.type = Value::Color;
682 if (v.type == Value::Color)
683 return qvariant_cast<QColor>(v.variant);
685 if (v.type == Value::KnownIdentifier && v.variant.toInt() == Value_Transparent)
686 return QColor(Qt::transparent);
688 if (v.type != Value::Function)
691 QStringList lst = v.variant.toStringList();
692 if (lst.count() != 2)
695 if ((lst.at(0).compare(QLatin1String("palette"), Qt::CaseInsensitive)) == 0) {
696 int role = findKnownValue(lst.at(1).trimmed(), values, NumKnownValues);
697 if (role >= Value_FirstColorRole && role <= Value_LastColorRole)
698 return (QPalette::ColorRole)(role-Value_FirstColorRole);
703 bool rgb = lst.at(0).startsWith(QLatin1String("rgb"));
709 QVector<QCss::Value> colorDigits;
710 if (!p.parseExpr(&colorDigits))
713 for (int i = 0; i < qMin(colorDigits.count(), 7); i += 2) {
714 if (colorDigits.at(i).type == Value::Percentage) {
715 colorDigits[i].variant = colorDigits.at(i).variant.toReal() * (255. / 100.);
716 colorDigits[i].type = Value::Number;
717 } else if (colorDigits.at(i).type != Value::Number) {
722 int v1 = colorDigits.at(0).variant.toInt();
723 int v2 = colorDigits.at(2).variant.toInt();
724 int v3 = colorDigits.at(4).variant.toInt();
725 int alpha = colorDigits.count() >= 7 ? colorDigits.at(6).variant.toInt() : 255;
727 return rgb ? QColor::fromRgb(v1, v2, v3, alpha)
728 : QColor::fromHsv(v1, v2, v3, alpha);
731 static QColor colorFromData(const ColorData& c, const QPalette &pal)
733 if (c.type == ColorData::Color) {
735 } else if (c.type == ColorData::Role) {
736 return pal.color(c.role);
741 static BrushData parseBrushValue(const QCss::Value &v, const QPalette &pal)
743 ColorData c = parseColorValue(v);
744 if (c.type == ColorData::Color) {
745 return QBrush(c.color);
746 } else if (c.type == ColorData::Role) {
750 if (v.type != Value::Function)
753 QStringList lst = v.variant.toStringList();
754 if (lst.count() != 2)
757 QStringList gradFuncs;
758 gradFuncs << QLatin1String("qlineargradient") << QLatin1String("qradialgradient") << QLatin1String("qconicalgradient") << QLatin1String("qgradient");
761 if ((gradType = gradFuncs.indexOf(lst.at(0).toLower())) == -1)
764 QHash<QString, qreal> vars;
765 QVector<QGradientStop> stops;
769 spreads << QLatin1String("pad") << QLatin1String("reflect") << QLatin1String("repeat");
771 bool dependsOnThePalette = false;
772 Parser parser(lst.at(1));
773 while (parser.hasNext()) {
775 if (!parser.test(IDENT))
777 QString attr = parser.lexem();
779 if (!parser.test(COLON))
782 if (attr.compare(QLatin1String("stop"), Qt::CaseInsensitive) == 0) {
783 QCss::Value stop, color;
785 if (!parser.parseTerm(&stop)) return BrushData();
788 if (!parser.parseTerm(&color)) return BrushData();
789 ColorData cd = parseColorValue(color);
790 if(cd.type == ColorData::Role)
791 dependsOnThePalette = true;
792 stops.append(QGradientStop(stop.variant.toReal(), colorFromData(cd, pal)));
796 (void)parser.parseTerm(&value);
797 if (attr.compare(QLatin1String("spread"), Qt::CaseInsensitive) == 0) {
798 spread = spreads.indexOf(value.variant.toString());
800 vars[attr] = value.variant.toReal();
804 (void)parser.test(COMMA);
808 QLinearGradient lg(vars.value(QLatin1String("x1")), vars.value(QLatin1String("y1")),
809 vars.value(QLatin1String("x2")), vars.value(QLatin1String("y2")));
810 lg.setCoordinateMode(QGradient::ObjectBoundingMode);
813 lg.setSpread(QGradient::Spread(spread));
814 BrushData bd = QBrush(lg);
815 if (dependsOnThePalette)
816 bd.type = BrushData::DependsOnThePalette;
821 QRadialGradient rg(vars.value(QLatin1String("cx")), vars.value(QLatin1String("cy")),
822 vars.value(QLatin1String("radius")), vars.value(QLatin1String("fx")),
823 vars.value(QLatin1String("fy")));
824 rg.setCoordinateMode(QGradient::ObjectBoundingMode);
827 rg.setSpread(QGradient::Spread(spread));
828 BrushData bd = QBrush(rg);
829 if (dependsOnThePalette)
830 bd.type = BrushData::DependsOnThePalette;
835 QConicalGradient cg(vars.value(QLatin1String("cx")), vars.value(QLatin1String("cy")),
836 vars.value(QLatin1String("angle")));
837 cg.setCoordinateMode(QGradient::ObjectBoundingMode);
840 cg.setSpread(QGradient::Spread(spread));
841 BrushData bd = QBrush(cg);
842 if (dependsOnThePalette)
843 bd.type = BrushData::DependsOnThePalette;
850 static QBrush brushFromData(const BrushData& c, const QPalette &pal)
852 if (c.type == BrushData::Role) {
853 return pal.color(c.role);
859 static BorderStyle parseStyleValue(QCss::Value v)
861 if (v.type == Value::KnownIdentifier) {
862 switch (v.variant.toInt()) {
864 return BorderStyle_None;
866 return BorderStyle_Dotted;
868 return BorderStyle_Dashed;
870 return BorderStyle_Solid;
872 return BorderStyle_Double;
874 return BorderStyle_DotDash;
875 case Value_DotDotDash:
876 return BorderStyle_DotDotDash;
878 return BorderStyle_Groove;
880 return BorderStyle_Ridge;
882 return BorderStyle_Inset;
884 return BorderStyle_Outset;
886 return BorderStyle_Native;
892 return BorderStyle_Unknown;
895 void ValueExtractor::borderValue(const Declaration &decl, int *width, QCss::BorderStyle *style, QBrush *color)
897 if (decl.d->parsed.isValid()) {
898 BorderData data = qvariant_cast<BorderData>(decl.d->parsed);
899 *width = lengthValueFromData(data.width, f);
901 *color = data.color.type != BrushData::Invalid ? brushFromData(data.color, pal) : QBrush(QColor());
906 *style = BorderStyle_None;
909 if (decl.d->values.isEmpty())
913 data.width.number = 0;
914 data.width.unit = LengthData::None;
915 data.style = BorderStyle_None;
918 if (decl.d->values.at(i).type == Value::Length || decl.d->values.at(i).type == Value::Number) {
919 data.width = lengthValue(decl.d->values.at(i));
920 *width = lengthValueFromData(data.width, f);
921 if (++i >= decl.d->values.count()) {
922 decl.d->parsed = QVariant::fromValue<BorderData>(data);
927 data.style = parseStyleValue(decl.d->values.at(i));
928 if (data.style != BorderStyle_Unknown) {
930 if (++i >= decl.d->values.count()) {
931 decl.d->parsed = QVariant::fromValue<BorderData>(data);
935 data.style = BorderStyle_None;
938 data.color = parseBrushValue(decl.d->values.at(i), pal);
939 *color = brushFromData(data.color, pal);
940 if (data.color.type != BrushData::DependsOnThePalette)
941 decl.d->parsed = QVariant::fromValue<BorderData>(data);
944 static void parseShorthandBackgroundProperty(const QVector<QCss::Value> &values, BrushData *brush, QString *image, Repeat *repeat, Qt::Alignment *alignment, const QPalette &pal)
946 *brush = BrushData();
949 *alignment = Qt::AlignTop | Qt::AlignLeft;
951 for (int i = 0; i < values.count(); ++i) {
952 const QCss::Value &v = values.at(i);
953 if (v.type == Value::Uri) {
954 *image = v.variant.toString();
956 } else if (v.type == Value::KnownIdentifier && v.variant.toInt() == Value_None) {
959 } else if (v.type == Value::KnownIdentifier && v.variant.toInt() == Value_Transparent) {
960 *brush = QBrush(Qt::transparent);
963 Repeat repeatAttempt = static_cast<Repeat>(findKnownValue(v.variant.toString(),
964 repeats, NumKnownRepeats));
965 if (repeatAttempt != Repeat_Unknown) {
966 *repeat = repeatAttempt;
970 if (v.type == Value::KnownIdentifier) {
973 if (i < values.count() - 1
974 && values.at(i + 1).type == Value::KnownIdentifier) {
978 Qt::Alignment a = parseAlignment(values.constData() + start, count);
986 *brush = parseBrushValue(v, pal);
990 bool ValueExtractor::extractBackground(QBrush *brush, QString *image, Repeat *repeat,
991 Qt::Alignment *alignment, Origin *origin, Attachment *attachment,
995 for (int i = 0; i < declarations.count(); ++i) {
996 const Declaration &decl = declarations.at(i);
997 if (decl.d->values.isEmpty())
999 const QCss::Value &val = decl.d->values.at(0);
1000 switch (decl.d->propertyId) {
1001 case BackgroundColor:
1002 *brush = decl.brushValue();
1004 case BackgroundImage:
1005 if (val.type == Value::Uri)
1006 *image = val.variant.toString();
1008 case BackgroundRepeat:
1009 if (decl.d->parsed.isValid()) {
1010 *repeat = static_cast<Repeat>(decl.d->parsed.toInt());
1012 *repeat = static_cast<Repeat>(findKnownValue(val.variant.toString(),
1013 repeats, NumKnownRepeats));
1014 decl.d->parsed = *repeat;
1017 case BackgroundPosition:
1018 *alignment = decl.alignmentValue();
1020 case BackgroundOrigin:
1021 *origin = decl.originValue();
1023 case BackgroundClip:
1024 *clip = decl.originValue();
1027 if (decl.d->parsed.isValid()) {
1028 BackgroundData data = qvariant_cast<BackgroundData>(decl.d->parsed);
1029 *brush = brushFromData(data.brush, pal);
1030 *image = data.image;
1031 *repeat = data.repeat;
1032 *alignment = data.alignment;
1034 BrushData brushData;
1035 parseShorthandBackgroundProperty(decl.d->values, &brushData, image, repeat, alignment, pal);
1036 *brush = brushFromData(brushData, pal);
1037 if (brushData.type != BrushData::DependsOnThePalette) {
1038 BackgroundData data = { brushData, *image, *repeat, *alignment };
1039 decl.d->parsed = QVariant::fromValue<BackgroundData>(data);
1043 case BackgroundAttachment:
1044 *attachment = decl.attachmentValue();
1053 static bool setFontSizeFromValue(QCss::Value value, QFont *font, int *fontSizeAdjustment)
1055 if (value.type == Value::KnownIdentifier) {
1057 switch (value.variant.toInt()) {
1058 case Value_Small: *fontSizeAdjustment = -1; break;
1059 case Value_Medium: *fontSizeAdjustment = 0; break;
1060 case Value_Large: *fontSizeAdjustment = 1; break;
1061 case Value_XLarge: *fontSizeAdjustment = 2; break;
1062 case Value_XXLarge: *fontSizeAdjustment = 3; break;
1063 default: valid = false; break;
1067 if (value.type != Value::Length)
1071 QString s = value.variant.toString();
1072 if (s.endsWith(QLatin1String("pt"), Qt::CaseInsensitive)) {
1075 if (value.variant.convert((QVariant::Type)qMetaTypeId<qreal>())) {
1076 font->setPointSizeF(value.variant.toReal());
1079 } else if (s.endsWith(QLatin1String("px"), Qt::CaseInsensitive)) {
1082 if (value.variant.convert(QVariant::Int)) {
1083 font->setPixelSize(value.variant.toInt());
1090 static bool setFontStyleFromValue(const QCss::Value &value, QFont *font)
1092 if (value.type != Value::KnownIdentifier)
1094 switch (value.variant.toInt()) {
1095 case Value_Normal: font->setStyle(QFont::StyleNormal); return true;
1096 case Value_Italic: font->setStyle(QFont::StyleItalic); return true;
1097 case Value_Oblique: font->setStyle(QFont::StyleOblique); return true;
1103 static bool setFontWeightFromValue(const QCss::Value &value, QFont *font)
1105 if (value.type == Value::KnownIdentifier) {
1106 switch (value.variant.toInt()) {
1107 case Value_Normal: font->setWeight(QFont::Normal); return true;
1108 case Value_Bold: font->setWeight(QFont::Bold); return true;
1113 if (value.type != Value::Number)
1115 font->setWeight(qMin(value.variant.toInt() / 8, 99));
1120 * parse the font family from the values (starting from index \a start)
1121 * and set it the \a font
1122 * \returns true if a family was extracted.
1124 static bool setFontFamilyFromValues(const QVector<QCss::Value> &values, QFont *font, int start = 0)
1127 bool shouldAddSpace = false;
1128 for (int i = start; i < values.count(); ++i) {
1129 const QCss::Value &v = values.at(i);
1130 if (v.type == Value::TermOperatorComma) {
1131 family += QLatin1Char(',');
1132 shouldAddSpace = false;
1135 const QString str = v.variant.toString();
1139 family += QLatin1Char(' ');
1141 shouldAddSpace = true;
1143 if (family.isEmpty())
1145 font->setFamily(family);
1149 static void setTextDecorationFromValues(const QVector<QCss::Value> &values, QFont *font)
1151 for (int i = 0; i < values.count(); ++i) {
1152 if (values.at(i).type != Value::KnownIdentifier)
1154 switch (values.at(i).variant.toInt()) {
1155 case Value_Underline: font->setUnderline(true); break;
1156 case Value_Overline: font->setOverline(true); break;
1157 case Value_LineThrough: font->setStrikeOut(true); break;
1159 font->setUnderline(false);
1160 font->setOverline(false);
1161 font->setStrikeOut(false);
1168 static void parseShorthandFontProperty(const QVector<QCss::Value> &values, QFont *font, int *fontSizeAdjustment)
1170 font->setStyle(QFont::StyleNormal);
1171 font->setWeight(QFont::Normal);
1172 *fontSizeAdjustment = -255;
1175 while (i < values.count()) {
1176 if (setFontStyleFromValue(values.at(i), font)
1177 || setFontWeightFromValue(values.at(i), font))
1183 if (i < values.count()) {
1184 setFontSizeFromValue(values.at(i), font, fontSizeAdjustment);
1188 if (i < values.count()) {
1189 setFontFamilyFromValues(values, font, i);
1193 static void setFontVariantFromValue(const QCss::Value &value, QFont *font)
1195 if (value.type == Value::KnownIdentifier) {
1196 switch (value.variant.toInt()) {
1197 case Value_Normal: font->setCapitalization(QFont::MixedCase); break;
1198 case Value_SmallCaps: font->setCapitalization(QFont::SmallCaps); break;
1204 static void setTextTransformFromValue(const QCss::Value &value, QFont *font)
1206 if (value.type == Value::KnownIdentifier) {
1207 switch (value.variant.toInt()) {
1208 case Value_None: font->setCapitalization(QFont::MixedCase); break;
1209 case Value_Uppercase: font->setCapitalization(QFont::AllUppercase); break;
1210 case Value_Lowercase: font->setCapitalization(QFont::AllLowercase); break;
1216 bool ValueExtractor::extractFont(QFont *font, int *fontSizeAdjustment)
1218 if (fontExtracted) {
1220 *fontSizeAdjustment = adjustment;
1221 return fontExtracted == 1;
1225 for (int i = 0; i < declarations.count(); ++i) {
1226 const Declaration &decl = declarations.at(i);
1227 if (decl.d->values.isEmpty())
1229 const QCss::Value &val = decl.d->values.at(0);
1230 switch (decl.d->propertyId) {
1231 case FontSize: setFontSizeFromValue(val, font, fontSizeAdjustment); break;
1232 case FontStyle: setFontStyleFromValue(val, font); break;
1233 case FontWeight: setFontWeightFromValue(val, font); break;
1234 case FontFamily: setFontFamilyFromValues(decl.d->values, font); break;
1235 case TextDecoration: setTextDecorationFromValues(decl.d->values, font); break;
1236 case Font: parseShorthandFontProperty(decl.d->values, font, fontSizeAdjustment); break;
1237 case FontVariant: setFontVariantFromValue(val, font); break;
1238 case TextTransform: setTextTransformFromValue(val, font); break;
1245 adjustment = *fontSizeAdjustment;
1246 fontExtracted = hit ? 1 : 2;
1250 bool ValueExtractor::extractPalette(QBrush *fg, QBrush *sfg, QBrush *sbg, QBrush *abg)
1253 for (int i = 0; i < declarations.count(); ++i) {
1254 const Declaration &decl = declarations.at(i);
1255 switch (decl.d->propertyId) {
1256 case Color: *fg = decl.brushValue(pal); break;
1257 case QtSelectionForeground: *sfg = decl.brushValue(pal); break;
1258 case QtSelectionBackground: *sbg = decl.brushValue(pal); break;
1259 case QtAlternateBackground: *abg = decl.brushValue(pal); break;
1267 void ValueExtractor::extractFont()
1272 extractFont(&f, &dummy);
1275 bool ValueExtractor::extractImage(QIcon *icon, Qt::Alignment *a, QSize *size)
1278 for (int i = 0; i < declarations.count(); ++i) {
1279 const Declaration &decl = declarations.at(i);
1280 switch (decl.d->propertyId) {
1282 *icon = decl.iconValue();
1283 if (decl.d->values.count() > 0 && decl.d->values.at(0).type == Value::Uri) {
1284 // try to pull just the size from the image...
1285 QImageReader imageReader(decl.d->values.at(0).variant.toString());
1286 if ((*size = imageReader.size()).isNull()) {
1287 // but we'll have to load the whole image if the
1288 // format doesn't support just reading the size
1289 *size = imageReader.read().size();
1293 case QtImageAlignment: *a = decl.alignmentValue(); break;
1301 ///////////////////////////////////////////////////////////////////////////////
1303 QColor Declaration::colorValue(const QPalette &pal) const
1305 if (d->values.count() != 1)
1308 if (d->parsed.isValid()) {
1309 if (d->parsed.type() == QVariant::Color)
1310 return qvariant_cast<QColor>(d->parsed);
1311 if (d->parsed.type() == QVariant::Int)
1312 return pal.color((QPalette::ColorRole)(d->parsed.toInt()));
1315 ColorData color = parseColorValue(d->values.at(0));
1316 if(color.type == ColorData::Role) {
1317 d->parsed = QVariant::fromValue<int>(color.role);
1318 return pal.color((QPalette::ColorRole)(color.role));
1320 d->parsed = QVariant::fromValue<QColor>(color.color);
1325 QBrush Declaration::brushValue(const QPalette &pal) const
1327 if (d->values.count() != 1)
1330 if (d->parsed.isValid()) {
1331 if (d->parsed.type() == QVariant::Brush)
1332 return qvariant_cast<QBrush>(d->parsed);
1333 if (d->parsed.type() == QVariant::Int)
1334 return pal.color((QPalette::ColorRole)(d->parsed.toInt()));
1337 BrushData data = parseBrushValue(d->values.at(0), pal);
1339 if(data.type == BrushData::Role) {
1340 d->parsed = QVariant::fromValue<int>(data.role);
1341 return pal.color((QPalette::ColorRole)(data.role));
1343 if (data.type != BrushData::DependsOnThePalette)
1344 d->parsed = QVariant::fromValue<QBrush>(data.brush);
1349 void Declaration::brushValues(QBrush *c, const QPalette &pal) const
1351 int needParse = 0x1f; // bits 0..3 say if we should parse the corresponding value.
1352 // the bit 4 say we need to update d->parsed
1354 if (d->parsed.isValid()) {
1356 QList<QVariant> v = d->parsed.toList();
1357 for (i = 0; i < qMin(v.count(), 4); i++) {
1358 if (v.at(i).type() == QVariant::Brush) {
1359 c[i] = qvariant_cast<QBrush>(v.at(i));
1360 } else if (v.at(i).type() == QVariant::Int) {
1361 c[i] = pal.color((QPalette::ColorRole)(v.at(i).toInt()));
1363 needParse |= (1<<i);
1367 if (needParse != 0) {
1369 for (i = 0; i < qMin(d->values.count(), 4); i++) {
1370 if (!(needParse & (1<<i)))
1372 BrushData data = parseBrushValue(d->values.at(i), pal);
1373 if(data.type == BrushData::Role) {
1374 v += QVariant::fromValue<int>(data.role);
1375 c[i] = pal.color((QPalette::ColorRole)(data.role));
1377 if (data.type != BrushData::DependsOnThePalette) {
1378 v += QVariant::fromValue<QBrush>(data.brush);
1385 if (needParse & 0x10)
1388 if (i == 0) c[0] = c[1] = c[2] = c[3] = QBrush();
1389 else if (i == 1) c[3] = c[2] = c[1] = c[0];
1390 else if (i == 2) c[2] = c[0], c[3] = c[1];
1391 else if (i == 3) c[3] = c[1];
1394 bool Declaration::realValue(qreal *real, const char *unit) const
1396 if (d->values.count() != 1)
1398 const Value &v = d->values.at(0);
1399 if (unit && v.type != Value::Length)
1401 QString s = v.variant.toString();
1403 if (!s.endsWith(QLatin1String(unit), Qt::CaseInsensitive))
1405 s.chop(qstrlen(unit));
1408 qreal val = s.toDouble(&ok);
1414 static bool intValueHelper(const QCss::Value &v, int *i, const char *unit)
1416 if (unit && v.type != Value::Length)
1418 QString s = v.variant.toString();
1420 if (!s.endsWith(QLatin1String(unit), Qt::CaseInsensitive))
1422 s.chop(qstrlen(unit));
1425 int val = s.toInt(&ok);
1431 bool Declaration::intValue(int *i, const char *unit) const
1433 if (d->values.count() != 1)
1435 return intValueHelper(d->values.at(0), i, unit);
1438 QSize Declaration::sizeValue() const
1440 if (d->parsed.isValid())
1441 return qvariant_cast<QSize>(d->parsed);
1443 int x[2] = { 0, 0 };
1444 if (d->values.count() > 0)
1445 intValueHelper(d->values.at(0), &x[0], "px");
1446 if (d->values.count() > 1)
1447 intValueHelper(d->values.at(1), &x[1], "px");
1450 QSize size(x[0], x[1]);
1451 d->parsed = QVariant::fromValue<QSize>(size);
1455 QRect Declaration::rectValue() const
1457 if (d->values.count() != 1)
1460 if (d->parsed.isValid())
1461 return qvariant_cast<QRect>(d->parsed);
1463 const QCss::Value &v = d->values.at(0);
1464 if (v.type != Value::Function)
1466 QStringList func = v.variant.toStringList();
1467 if (func.count() != 2 || func.at(0).compare(QLatin1String("rect")) != 0)
1469 QStringList args = func[1].split(QLatin1Char(' '), QString::SkipEmptyParts);
1470 if (args.count() != 4)
1472 QRect rect(args[0].toInt(), args[1].toInt(), args[2].toInt(), args[3].toInt());
1473 d->parsed = QVariant::fromValue<QRect>(rect);
1477 void Declaration::colorValues(QColor *c, const QPalette &pal) const
1480 if (d->parsed.isValid()) {
1481 QList<QVariant> v = d->parsed.toList();
1482 for (i = 0; i < qMin(d->values.count(), 4); i++) {
1483 if (v.at(i).type() == QVariant::Color) {
1484 c[i] = qvariant_cast<QColor>(v.at(i));
1486 c[i] = pal.color((QPalette::ColorRole)(v.at(i).toInt()));
1491 for (i = 0; i < qMin(d->values.count(), 4); i++) {
1492 ColorData color = parseColorValue(d->values.at(i));
1493 if(color.type == ColorData::Role) {
1494 v += QVariant::fromValue<int>(color.role);
1495 c[i] = pal.color((QPalette::ColorRole)(color.role));
1497 v += QVariant::fromValue<QColor>(color.color);
1504 if (i == 0) c[0] = c[1] = c[2] = c[3] = QColor();
1505 else if (i == 1) c[3] = c[2] = c[1] = c[0];
1506 else if (i == 2) c[2] = c[0], c[3] = c[1];
1507 else if (i == 3) c[3] = c[1];
1510 BorderStyle Declaration::styleValue() const
1512 if (d->values.count() != 1)
1513 return BorderStyle_None;
1514 return parseStyleValue(d->values.at(0));
1517 void Declaration::styleValues(BorderStyle *s) const
1520 for (i = 0; i < qMin(d->values.count(), 4); i++)
1521 s[i] = parseStyleValue(d->values.at(i));
1522 if (i == 0) s[0] = s[1] = s[2] = s[3] = BorderStyle_None;
1523 else if (i == 1) s[3] = s[2] = s[1] = s[0];
1524 else if (i == 2) s[2] = s[0], s[3] = s[1];
1525 else if (i == 3) s[3] = s[1];
1528 Repeat Declaration::repeatValue() const
1530 if (d->parsed.isValid())
1531 return static_cast<Repeat>(d->parsed.toInt());
1532 if (d->values.count() != 1)
1533 return Repeat_Unknown;
1534 int v = findKnownValue(d->values.at(0).variant.toString(),
1535 repeats, NumKnownRepeats);
1537 return static_cast<Repeat>(v);
1540 Origin Declaration::originValue() const
1542 if (d->parsed.isValid())
1543 return static_cast<Origin>(d->parsed.toInt());
1544 if (d->values.count() != 1)
1545 return Origin_Unknown;
1546 int v = findKnownValue(d->values.at(0).variant.toString(),
1547 origins, NumKnownOrigins);
1549 return static_cast<Origin>(v);
1552 PositionMode Declaration::positionValue() const
1554 if (d->parsed.isValid())
1555 return static_cast<PositionMode>(d->parsed.toInt());
1556 if (d->values.count() != 1)
1557 return PositionMode_Unknown;
1558 int v = findKnownValue(d->values.at(0).variant.toString(),
1559 positions, NumKnownPositionModes);
1561 return static_cast<PositionMode>(v);
1564 Attachment Declaration::attachmentValue() const
1566 if (d->parsed.isValid())
1567 return static_cast<Attachment>(d->parsed.toInt());
1568 if (d->values.count() != 1)
1569 return Attachment_Unknown;
1570 int v = findKnownValue(d->values.at(0).variant.toString(),
1571 attachments, NumKnownAttachments);
1573 return static_cast<Attachment>(v);
1576 int Declaration::styleFeaturesValue() const
1578 Q_ASSERT(d->propertyId == QtStyleFeatures);
1579 if (d->parsed.isValid())
1580 return d->parsed.toInt();
1581 int features = StyleFeature_None;
1582 for (int i = 0; i < d->values.count(); i++) {
1583 features |= static_cast<int>(findKnownValue(d->values.value(i).variant.toString(),
1584 styleFeatures, NumKnownStyleFeatures));
1586 d->parsed = features;
1590 QString Declaration::uriValue() const
1592 if (d->values.isEmpty() || d->values.at(0).type != Value::Uri)
1594 return d->values.at(0).variant.toString();
1597 Qt::Alignment Declaration::alignmentValue() const
1599 if (d->parsed.isValid())
1600 return Qt::Alignment(d->parsed.toInt());
1601 if (d->values.isEmpty() || d->values.count() > 2)
1602 return Qt::AlignLeft | Qt::AlignTop;
1604 Qt::Alignment v = parseAlignment(d->values.constData(), d->values.count());
1609 void Declaration::borderImageValue(QString *image, int *cuts,
1610 TileMode *h, TileMode *v) const
1612 *image = uriValue();
1613 for (int i = 0; i < 4; i++)
1615 *h = *v = TileMode_Stretch;
1617 if (d->values.count() < 2)
1620 if (d->values.at(1).type == Value::Number) { // cuts!
1622 for (i = 0; i < qMin(d->values.count()-1, 4); i++) {
1623 const Value& v = d->values.at(i+1);
1624 if (v.type != Value::Number)
1626 cuts[i] = v.variant.toString().toInt();
1628 if (i == 0) cuts[0] = cuts[1] = cuts[2] = cuts[3] = 0;
1629 else if (i == 1) cuts[3] = cuts[2] = cuts[1] = cuts[0];
1630 else if (i == 2) cuts[2] = cuts[0], cuts[3] = cuts[1];
1631 else if (i == 3) cuts[3] = cuts[1];
1634 if (d->values.last().type == Value::Identifier) {
1635 *v = static_cast<TileMode>(findKnownValue(d->values.last().variant.toString(),
1636 tileModes, NumKnownTileModes));
1638 if (d->values[d->values.count() - 2].type == Value::Identifier) {
1639 *h = static_cast<TileMode>
1640 (findKnownValue(d->values[d->values.count()-2].variant.toString(),
1641 tileModes, NumKnownTileModes));
1646 QIcon Declaration::iconValue() const
1648 if (d->parsed.isValid())
1649 return qvariant_cast<QIcon>(d->parsed);
1652 for (int i = 0; i < d->values.count();) {
1653 const Value &value = d->values.at(i++);
1654 if (value.type != Value::Uri)
1656 QString uri = value.variant.toString();
1657 QIcon::Mode mode = QIcon::Normal;
1658 QIcon::State state = QIcon::Off;
1659 for (int j = 0; j < 2; j++) {
1660 if (i != d->values.count() && d->values.at(i).type == Value::KnownIdentifier) {
1661 switch (d->values.at(i).variant.toInt()) {
1662 case Value_Disabled: mode = QIcon::Disabled; break;
1663 case Value_Active: mode = QIcon::Active; break;
1664 case Value_Selected: mode = QIcon::Selected; break;
1665 case Value_Normal: mode = QIcon::Normal; break;
1666 case Value_On: state = QIcon::On; break;
1667 case Value_Off: state = QIcon::Off; break;
1676 // QIcon is soo broken
1680 icon.addPixmap(uri, mode, state);
1682 if (i == d->values.count())
1685 if (d->values.at(i).type == Value::TermOperatorComma)
1689 d->parsed = QVariant::fromValue<QIcon>(icon);
1693 ///////////////////////////////////////////////////////////////////////////////
1695 int Selector::specificity() const
1698 for (int i = 0; i < basicSelectors.count(); ++i) {
1699 const BasicSelector &sel = basicSelectors.at(i);
1700 if (!sel.elementName.isEmpty())
1703 val += (sel.pseudos.count() + sel.attributeSelectors.count()) * 0x10;
1704 val += sel.ids.count() * 0x100;
1709 QString Selector::pseudoElement() const
1711 const BasicSelector& bs = basicSelectors.last();
1712 if (!bs.pseudos.isEmpty() && bs.pseudos.at(0).type == PseudoClass_Unknown)
1713 return bs.pseudos.at(0).name;
1717 quint64 Selector::pseudoClass(quint64 *negated) const
1719 const BasicSelector& bs = basicSelectors.last();
1720 if (bs.pseudos.isEmpty())
1721 return PseudoClass_Unspecified;
1722 quint64 pc = PseudoClass_Unknown;
1723 for (int i = !pseudoElement().isEmpty(); i < bs.pseudos.count(); i++) {
1724 const Pseudo &pseudo = bs.pseudos.at(i);
1725 if (pseudo.type == PseudoClass_Unknown)
1726 return PseudoClass_Unknown;
1727 if (!pseudo.negated)
1730 *negated |= pseudo.type;
1735 ///////////////////////////////////////////////////////////////////////////////
1737 void StyleSheet::buildIndexes(Qt::CaseSensitivity nameCaseSensitivity)
1739 QVector<StyleRule> universals;
1740 for (int i = 0; i < styleRules.count(); ++i) {
1741 const StyleRule &rule = styleRules.at(i);
1742 QVector<Selector> universalsSelectors;
1743 for (int j = 0; j < rule.selectors.count(); ++j) {
1744 const Selector& selector = rule.selectors.at(j);
1746 if (selector.basicSelectors.isEmpty())
1749 if (selector.basicSelectors.at(0).relationToNext == BasicSelector::NoRelation) {
1750 if (selector.basicSelectors.count() != 1)
1752 } else if (selector.basicSelectors.count() <= 1) {
1756 const BasicSelector &sel = selector.basicSelectors.at(selector.basicSelectors.count() - 1);
1758 if (!sel.ids.isEmpty()) {
1760 nr.selectors += selector;
1761 nr.declarations = rule.declarations;
1763 idIndex.insert(sel.ids.at(0), nr);
1764 } else if (!sel.elementName.isEmpty()) {
1766 nr.selectors += selector;
1767 nr.declarations = rule.declarations;
1769 QString name = sel.elementName;
1770 if (nameCaseSensitivity == Qt::CaseInsensitive)
1771 name=name.toLower();
1772 nameIndex.insert(name, nr);
1774 universalsSelectors += selector;
1777 if (!universalsSelectors.isEmpty()) {
1779 nr.selectors = universalsSelectors;
1780 nr.declarations = rule.declarations;
1785 styleRules = universals;
1788 ///////////////////////////////////////////////////////////////////////////////
1790 StyleSelector::~StyleSelector()
1794 bool StyleSelector::nodeNameEquals(NodePtr node, const QString& nodeName) const
1796 return nodeNames(node).contains(nodeName, nameCaseSensitivity);
1799 QStringList StyleSelector::nodeIds(NodePtr node) const
1801 return QStringList(attribute(node, QLatin1String("id")));
1804 bool StyleSelector::selectorMatches(const Selector &selector, NodePtr node)
1806 if (selector.basicSelectors.isEmpty())
1809 if (selector.basicSelectors.at(0).relationToNext == BasicSelector::NoRelation) {
1810 if (selector.basicSelectors.count() != 1)
1812 return basicSelectorMatches(selector.basicSelectors.at(0), node);
1814 if (selector.basicSelectors.count() <= 1)
1817 int i = selector.basicSelectors.count() - 1;
1818 node = duplicateNode(node);
1821 BasicSelector sel = selector.basicSelectors.at(i);
1823 match = basicSelectorMatches(sel, node);
1825 if (sel.relationToNext == BasicSelector::MatchNextSelectorIfParent
1826 || i == selector.basicSelectors.count() - 1) // first element must always match!
1830 if (match || sel.relationToNext != BasicSelector::MatchNextSelectorIfAncestor)
1836 sel = selector.basicSelectors.at(i);
1837 if (sel.relationToNext == BasicSelector::MatchNextSelectorIfAncestor
1838 || sel.relationToNext == BasicSelector::MatchNextSelectorIfParent) {
1840 NodePtr nextParent = parentNode(node);
1843 } else if (sel.relationToNext == BasicSelector::MatchNextSelectorIfPreceeds) {
1844 NodePtr previousSibling = previousSiblingNode(node);
1846 node = previousSibling;
1848 if (isNullNode(node)) {
1852 } while (i >= 0 && (match || sel.relationToNext == BasicSelector::MatchNextSelectorIfAncestor));
1859 bool StyleSelector::basicSelectorMatches(const BasicSelector &sel, NodePtr node)
1861 if (!sel.attributeSelectors.isEmpty()) {
1862 if (!hasAttributes(node))
1865 for (int i = 0; i < sel.attributeSelectors.count(); ++i) {
1866 const QCss::AttributeSelector &a = sel.attributeSelectors.at(i);
1868 const QString attrValue = attribute(node, a.name);
1869 if (attrValue.isNull())
1872 if (a.valueMatchCriterium == QCss::AttributeSelector::MatchContains) {
1874 QStringList lst = attrValue.split(QLatin1Char(' '));
1875 if (!lst.contains(a.value))
1878 (a.valueMatchCriterium == QCss::AttributeSelector::MatchEqual
1879 && attrValue != a.value)
1881 (a.valueMatchCriterium == QCss::AttributeSelector::MatchBeginsWith
1882 && !attrValue.startsWith(a.value))
1888 if (!sel.elementName.isEmpty()
1889 && !nodeNameEquals(node, sel.elementName))
1892 if (!sel.ids.isEmpty()
1893 && sel.ids != nodeIds(node))
1899 void StyleSelector::matchRule(NodePtr node, const StyleRule &rule, StyleSheetOrigin origin,
1900 int depth, QMap<uint, StyleRule> *weightedRules)
1902 for (int j = 0; j < rule.selectors.count(); ++j) {
1903 const Selector& selector = rule.selectors.at(j);
1904 if (selectorMatches(selector, node)) {
1905 uint weight = rule.order
1906 + selector.specificity() *0x100
1907 + (uint(origin) + depth)*0x100000;
1908 StyleRule newRule = rule;
1909 if(rule.selectors.count() > 1) {
1910 newRule.selectors.resize(1);
1911 newRule.selectors[0] = selector;
1913 //We might have rules with the same weight if they came from a rule with several selectors
1914 weightedRules->insertMulti(weight, newRule);
1919 // Returns style rules that are in ascending order of specificity
1920 // Each of the StyleRule returned will contain exactly one Selector
1921 QVector<StyleRule> StyleSelector::styleRulesForNode(NodePtr node)
1923 QVector<StyleRule> rules;
1924 if (styleSheets.isEmpty())
1927 QMap<uint, StyleRule> weightedRules; // (spec, rule) that will be sorted below
1929 //prune using indexed stylesheet
1930 for (int sheetIdx = 0; sheetIdx < styleSheets.count(); ++sheetIdx) {
1931 const StyleSheet &styleSheet = styleSheets.at(sheetIdx);
1932 for (int i = 0; i < styleSheet.styleRules.count(); ++i) {
1933 matchRule(node, styleSheet.styleRules.at(i), styleSheet.origin, styleSheet.depth, &weightedRules);
1936 if (!styleSheet.idIndex.isEmpty()) {
1937 QStringList ids = nodeIds(node);
1938 for (int i = 0; i < ids.count(); i++) {
1939 const QString &key = ids.at(i);
1940 QMultiHash<QString, StyleRule>::const_iterator it = styleSheet.idIndex.constFind(key);
1941 while (it != styleSheet.idIndex.constEnd() && it.key() == key) {
1942 matchRule(node, it.value(), styleSheet.origin, styleSheet.depth, &weightedRules);
1947 if (!styleSheet.nameIndex.isEmpty()) {
1948 QStringList names = nodeNames(node);
1949 for (int i = 0; i < names.count(); i++) {
1950 QString name = names.at(i);
1951 if (nameCaseSensitivity == Qt::CaseInsensitive)
1952 name = name.toLower();
1953 QMultiHash<QString, StyleRule>::const_iterator it = styleSheet.nameIndex.constFind(name);
1954 while (it != styleSheet.nameIndex.constEnd() && it.key() == name) {
1955 matchRule(node, it.value(), styleSheet.origin, styleSheet.depth, &weightedRules);
1960 if (!medium.isEmpty()) {
1961 for (int i = 0; i < styleSheet.mediaRules.count(); ++i) {
1962 if (styleSheet.mediaRules.at(i).media.contains(medium, Qt::CaseInsensitive)) {
1963 for (int j = 0; j < styleSheet.mediaRules.at(i).styleRules.count(); ++j) {
1964 matchRule(node, styleSheet.mediaRules.at(i).styleRules.at(j), styleSheet.origin,
1965 styleSheet.depth, &weightedRules);
1972 rules.reserve(weightedRules.count());
1973 QMap<uint, StyleRule>::const_iterator it = weightedRules.constBegin();
1974 for ( ; it != weightedRules.constEnd() ; ++it)
1980 // for qtexthtmlparser which requires just the declarations with Enabled state
1981 // and without pseudo elements
1982 QVector<Declaration> StyleSelector::declarationsForNode(NodePtr node, const char *extraPseudo)
1984 QVector<Declaration> decls;
1985 QVector<StyleRule> rules = styleRulesForNode(node);
1986 for (int i = 0; i < rules.count(); i++) {
1987 const Selector& selector = rules.at(i).selectors.at(0);
1988 const QString pseudoElement = selector.pseudoElement();
1990 if (extraPseudo && pseudoElement == QLatin1String(extraPseudo)) {
1991 decls += rules.at(i).declarations;
1995 if (!pseudoElement.isEmpty()) // skip rules with pseudo elements
1997 quint64 pseudoClass = selector.pseudoClass();
1998 if (pseudoClass == PseudoClass_Enabled || pseudoClass == PseudoClass_Unspecified)
1999 decls += rules.at(i).declarations;
2004 static inline bool isHexDigit(const char c)
2006 return (c >= '0' && c <= '9')
2007 || (c >= 'a' && c <= 'f')
2008 || (c >= 'A' && c <= 'F')
2012 QString Scanner::preprocess(const QString &input, bool *hasEscapeSequences)
2014 QString output = input;
2016 if (hasEscapeSequences)
2017 *hasEscapeSequences = false;
2020 while (i < output.size()) {
2021 if (output.at(i) == QLatin1Char('\\')) {
2024 // test for unicode hex escape
2026 const int hexStart = i;
2027 while (i < output.size()
2028 && isHexDigit(output.at(i).toLatin1())
2033 if (hexCount == 0) {
2034 if (hasEscapeSequences)
2035 *hasEscapeSequences = true;
2039 hexCount = qMin(hexCount, 6);
2041 ushort code = output.mid(hexStart, hexCount).toUShort(&ok, 16);
2043 output.replace(hexStart - 1, hexCount + 1, QChar(code));
2055 int QCssScanner_Generated::handleCommentStart()
2057 while (pos < input.size() - 1) {
2058 if (input.at(pos) == QLatin1Char('*')
2059 && input.at(pos + 1) == QLatin1Char('/')) {
2068 void Scanner::scan(const QString &preprocessedInput, QVector<Symbol> *symbols)
2070 QCssScanner_Generated scanner(preprocessedInput);
2072 int tok = scanner.lex();
2074 sym.token = static_cast<QCss::TokenType>(tok);
2075 sym.text = scanner.input;
2076 sym.start = scanner.lexemStart;
2077 sym.len = scanner.lexemLength;
2078 symbols->append(sym);
2079 tok = scanner.lex();
2083 QString Symbol::lexem() const
2087 result.reserve(len);
2088 for (int i = 0; i < len; ++i) {
2089 if (text.at(start + i) == QLatin1Char('\\') && i < len - 1)
2091 result += text.at(start + i);
2096 Parser::Parser(const QString &css, bool isFile)
2105 hasEscapeSequences = false;
2108 void Parser::init(const QString &css, bool isFile)
2110 QString styleSheet = css;
2113 if (file.open(QFile::ReadOnly)) {
2114 sourcePath = QFileInfo(styleSheet).absolutePath() + QLatin1Char('/');
2115 QTextStream stream(&file);
2116 styleSheet = stream.readAll();
2118 qWarning() << "QCss::Parser - Failed to load file " << css;
2125 hasEscapeSequences = false;
2128 Scanner::scan(Scanner::preprocess(styleSheet, &hasEscapeSequences), &symbols);
2133 bool Parser::parse(StyleSheet *styleSheet, Qt::CaseSensitivity nameCaseSensitivity)
2135 if (testTokenAndEndsWith(ATKEYWORD_SYM, QLatin1String("charset"))) {
2136 if (!next(STRING)) return false;
2137 if (!next(SEMICOLON)) return false;
2140 while (test(S) || test(CDO) || test(CDC)) {}
2142 while (testImport()) {
2144 if (!parseImport(&rule)) return false;
2145 styleSheet->importRules.append(rule);
2146 while (test(S) || test(CDO) || test(CDC)) {}
2152 if (!parseMedia(&rule)) return false;
2153 styleSheet->mediaRules.append(rule);
2154 } else if (testPage()) {
2156 if (!parsePage(&rule)) return false;
2157 styleSheet->pageRules.append(rule);
2158 } else if (testRuleset()) {
2160 if (!parseRuleset(&rule)) return false;
2161 styleSheet->styleRules.append(rule);
2162 } else if (test(ATKEYWORD_SYM)) {
2163 if (!until(RBRACE)) return false;
2164 } else if (hasNext()) {
2167 while (test(S) || test(CDO) || test(CDC)) {}
2168 } while (hasNext());
2169 styleSheet->buildIndexes(nameCaseSensitivity);
2173 Symbol Parser::errorSymbol()
2175 if (errorIndex == -1) return Symbol();
2176 return symbols.at(errorIndex);
2179 static inline void removeOptionalQuotes(QString *str)
2181 if (!str->startsWith(QLatin1Char('\''))
2182 && !str->startsWith(QLatin1Char('\"')))
2188 bool Parser::parseImport(ImportRule *importRule)
2193 importRule->href = lexem();
2195 if (!testAndParseUri(&importRule->href)) return false;
2197 removeOptionalQuotes(&importRule->href);
2202 if (!parseMedium(&importRule->media)) return false;
2204 while (test(COMMA)) {
2206 if (!parseNextMedium(&importRule->media)) return false;
2210 if (!next(SEMICOLON)) return false;
2216 bool Parser::parseMedia(MediaRule *mediaRule)
2220 if (!parseNextMedium(&mediaRule->media)) return false;
2221 } while (test(COMMA));
2223 if (!next(LBRACE)) return false;
2226 while (testRuleset()) {
2228 if (!parseRuleset(&rule)) return false;
2229 mediaRule->styleRules.append(rule);
2232 if (!next(RBRACE)) return false;
2237 bool Parser::parseMedium(QStringList *media)
2239 media->append(lexem());
2244 bool Parser::parsePage(PageRule *pageRule)
2248 if (testPseudoPage())
2249 if (!parsePseudoPage(&pageRule->selector)) return false;
2252 if (!next(LBRACE)) return false;
2257 if (!parseNextDeclaration(&decl)) return false;
2258 if (!decl.isEmpty())
2259 pageRule->declarations.append(decl);
2260 } while (test(SEMICOLON));
2262 if (!next(RBRACE)) return false;
2267 bool Parser::parsePseudoPage(QString *selector)
2269 if (!next(IDENT)) return false;
2270 *selector = lexem();
2274 bool Parser::parseNextOperator(Value *value)
2276 if (!hasNext()) return true;
2278 case SLASH: value->type = Value::TermOperatorSlash; skipSpace(); break;
2279 case COMMA: value->type = Value::TermOperatorComma; skipSpace(); break;
2280 default: prev(); break;
2285 bool Parser::parseCombinator(BasicSelector::Relation *relation)
2287 *relation = BasicSelector::NoRelation;
2288 if (lookup() == S) {
2289 *relation = BasicSelector::MatchNextSelectorIfAncestor;
2295 *relation = BasicSelector::MatchNextSelectorIfPreceeds;
2296 } else if (test(GREATER)) {
2297 *relation = BasicSelector::MatchNextSelectorIfParent;
2303 bool Parser::parseProperty(Declaration *decl)
2305 decl->d->property = lexem();
2306 decl->d->propertyId = static_cast<Property>(findKnownValue(decl->d->property, properties, NumProperties));
2311 bool Parser::parseRuleset(StyleRule *styleRule)
2314 if (!parseSelector(&sel)) return false;
2315 styleRule->selectors.append(sel);
2317 while (test(COMMA)) {
2320 if (!parseNextSelector(&sel)) return false;
2321 styleRule->selectors.append(sel);
2325 if (!next(LBRACE)) return false;
2326 const int declarationStart = index;
2331 const int rewind = index;
2332 if (!parseNextDeclaration(&decl)) {
2334 const bool foundSemicolon = until(SEMICOLON);
2335 const int semicolonIndex = index;
2337 index = declarationStart;
2338 const bool foundRBrace = until(RBRACE);
2340 if (foundSemicolon && semicolonIndex < index) {
2341 decl = Declaration();
2342 index = semicolonIndex - 1;
2348 if (!decl.isEmpty())
2349 styleRule->declarations.append(decl);
2350 } while (test(SEMICOLON));
2352 if (!next(RBRACE)) return false;
2357 bool Parser::parseSelector(Selector *sel)
2359 BasicSelector basicSel;
2360 if (!parseSimpleSelector(&basicSel)) return false;
2361 while (testCombinator()) {
2362 if (!parseCombinator(&basicSel.relationToNext)) return false;
2364 if (!testSimpleSelector()) break;
2365 sel->basicSelectors.append(basicSel);
2367 basicSel = BasicSelector();
2368 if (!parseSimpleSelector(&basicSel)) return false;
2370 sel->basicSelectors.append(basicSel);
2374 bool Parser::parseSimpleSelector(BasicSelector *basicSel)
2377 if (lookupElementName()) {
2378 if (!parseElementName(&basicSel->elementName)) return false;
2388 QString theid = lexem();
2389 // chop off leading #
2391 basicSel->ids.append(theid);
2393 } else if (testClass()) {
2395 AttributeSelector a;
2396 a.name = QLatin1String("class");
2397 a.valueMatchCriterium = AttributeSelector::MatchContains;
2398 if (!parseClass(&a.value)) return false;
2399 basicSel->attributeSelectors.append(a);
2400 } else if (testAttrib()) {
2402 AttributeSelector a;
2403 if (!parseAttrib(&a)) return false;
2404 basicSel->attributeSelectors.append(a);
2405 } else if (testPseudo()) {
2408 if (!parsePseudo(&ps)) return false;
2409 basicSel->pseudos.append(ps);
2411 if (onceMore) ++count;
2413 return count >= minCount;
2416 bool Parser::parseClass(QString *name)
2418 if (!next(IDENT)) return false;
2423 bool Parser::parseElementName(QString *name)
2426 case STAR: name->clear(); break;
2427 case IDENT: *name = lexem(); break;
2428 default: return false;
2433 bool Parser::parseAttrib(AttributeSelector *attr)
2436 if (!next(IDENT)) return false;
2437 attr->name = lexem();
2441 attr->valueMatchCriterium = AttributeSelector::MatchEqual;
2442 } else if (test(INCLUDES)) {
2443 attr->valueMatchCriterium = AttributeSelector::MatchContains;
2444 } else if (test(DASHMATCH)) {
2445 attr->valueMatchCriterium = AttributeSelector::MatchBeginsWith;
2447 return next(RBRACKET);
2452 if (!test(IDENT) && !test(STRING)) return false;
2453 attr->value = unquotedLexem();
2456 return next(RBRACKET);
2459 bool Parser::parsePseudo(Pseudo *pseudo)
2462 pseudo->negated = test(EXCLAMATION_SYM);
2464 pseudo->name = lexem();
2465 pseudo->type = static_cast<quint64>(findKnownValue(pseudo->name, pseudos, NumPseudos));
2468 if (!next(FUNCTION)) return false;
2469 pseudo->function = lexem();
2470 // chop off trailing parenthesis
2471 pseudo->function.chop(1);
2473 if (!test(IDENT)) return false;
2474 pseudo->name = lexem();
2476 return next(RPAREN);
2479 bool Parser::parseNextDeclaration(Declaration *decl)
2481 if (!testProperty())
2482 return true; // not an error!
2483 if (!parseProperty(decl)) return false;
2484 if (!next(COLON)) return false;
2486 if (!parseNextExpr(&decl->d->values)) return false;
2488 if (!parsePrio(decl)) return false;
2492 bool Parser::testPrio()
2494 const int rewind = index;
2495 if (!test(EXCLAMATION_SYM)) return false;
2501 if (lexem().compare(QLatin1String("important"), Qt::CaseInsensitive) != 0) {
2508 bool Parser::parsePrio(Declaration *declaration)
2510 declaration->d->important = true;
2515 bool Parser::parseExpr(QVector<Value> *values)
2518 if (!parseTerm(&val)) return false;
2519 values->append(val);
2524 if (!parseNextOperator(&val)) return false;
2525 if (val.type != QCss::Value::Unknown)
2526 values->append(val);
2530 if (!parseTerm(&val)) return false;
2531 values->append(val);
2537 bool Parser::testTerm()
2539 return test(PLUS) || test(MINUS)
2549 bool Parser::parseTerm(Value *value)
2551 QString str = lexem();
2552 bool haveUnary = false;
2553 if (lookup() == PLUS || lookup() == MINUS) {
2555 if (!hasNext()) return false;
2560 value->variant = str;
2561 value->type = QCss::Value::String;
2564 value->type = Value::Number;
2565 value->variant.convert(QVariant::Double);
2568 value->type = Value::Percentage;
2569 str.chop(1); // strip off %
2570 value->variant = str;
2573 value->type = Value::Length;
2577 if (haveUnary) return false;
2578 value->type = Value::String;
2581 value->variant = str;
2584 if (haveUnary) return false;
2585 value->type = Value::Identifier;
2586 const int theid = findKnownValue(str, values, NumKnownValues);
2588 value->type = Value::KnownIdentifier;
2589 value->variant = theid;
2594 if (haveUnary) return false;
2596 if (testHexColor()) {
2598 if (!parseHexColor(&col)) return false;
2599 value->type = Value::Color;
2600 value->variant = col;
2601 } else if (testFunction()) {
2603 if (!parseFunction(&name, &args)) return false;
2604 if (name == QLatin1String("url")) {
2605 value->type = Value::Uri;
2606 removeOptionalQuotes(&args);
2607 if (QFileInfo(args).isRelative() && !sourcePath.isEmpty()) {
2608 args.prepend(sourcePath);
2610 value->variant = args;
2612 value->type = Value::Function;
2613 value->variant = QStringList() << name << args;
2616 return recordError();
2625 bool Parser::parseFunction(QString *name, QString *args)
2630 const int start = index;
2631 if (!until(RPAREN)) return false;
2632 for (int i = start; i < index - 1; ++i)
2633 args->append(symbols.at(i).lexem());
2635 if (!nextExpr(&arguments)) return false;
2636 if (!next(RPAREN)) return false;
2642 bool Parser::parseHexColor(QColor *col)
2644 col->setNamedColor(lexem());
2645 if (!col->isValid()) {
2646 qWarning("QCssParser::parseHexColor: Unknown color name '%s'",lexem().toLatin1().constData());
2653 bool Parser::testAndParseUri(QString *uri)
2655 const int rewind = index;
2656 if (!testFunction()) return false;
2659 if (!parseFunction(&name, &args)) {
2663 if (name.toLower() != QLatin1String("url")) {
2668 removeOptionalQuotes(uri);
2672 bool Parser::testSimpleSelector()
2674 return testElementName()
2681 bool Parser::next(QCss::TokenType t)
2683 if (hasNext() && next() == t)
2685 return recordError();
2688 bool Parser::test(QCss::TokenType t)
2690 if (index >= symbols.count())
2692 if (symbols.at(index).token == t) {
2699 QString Parser::unquotedLexem() const
2701 QString s = lexem();
2702 if (lookup() == STRING) {
2709 QString Parser::lexemUntil(QCss::TokenType t)
2712 while (hasNext() && next() != t)
2713 lexem += symbol().lexem();
2717 bool Parser::until(QCss::TokenType target, QCss::TokenType target2)
2723 switch(symbols.at(index-1).token) {
2724 case LBRACE: ++braceCount; break;
2725 case LBRACKET: ++brackCount; break;
2727 case LPAREN: ++parenCount; break;
2731 while (index < symbols.size()) {
2732 QCss::TokenType t = symbols.at(index++).token;
2734 case LBRACE: ++braceCount; break;
2735 case RBRACE: --braceCount; break;
2736 case LBRACKET: ++brackCount; break;
2737 case RBRACKET: --brackCount; break;
2739 case LPAREN: ++parenCount; break;
2740 case RPAREN: --parenCount; break;
2743 if ((t == target || (target2 != NONE && t == target2))
2749 if (braceCount < 0 || brackCount < 0 || parenCount < 0) {
2757 bool Parser::testTokenAndEndsWith(QCss::TokenType t, const QLatin1String &str)
2759 if (!test(t)) return false;
2760 if (!lexem().endsWith(str, Qt::CaseInsensitive)) {
2768 #endif // QT_NO_CSSPARSER