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)
1280 for (int i = 0; i < declarations.count(); ++i) {
1281 const Declaration &decl = declarations.at(i);
1282 switch (decl.d->propertyId) {
1284 *icon = decl.iconValue();
1285 if (decl.d->values.count() > 0 && decl.d->values.at(0).type == Value::Uri) {
1286 // try to pull just the size from the image...
1287 QImageReader imageReader(decl.d->values.at(0).variant.toString());
1288 if ((*size = imageReader.size()).isNull()) {
1289 // but we'll have to load the whole image if the
1290 // format doesn't support just reading the size
1291 *size = imageReader.read().size();
1295 case QtImageAlignment: *a = decl.alignmentValue(); break;
1304 ///////////////////////////////////////////////////////////////////////////////
1306 QColor Declaration::colorValue(const QPalette &pal) const
1308 if (d->values.count() != 1)
1311 if (d->parsed.isValid()) {
1312 if (d->parsed.type() == QVariant::Color)
1313 return qvariant_cast<QColor>(d->parsed);
1314 if (d->parsed.type() == QVariant::Int)
1315 return pal.color((QPalette::ColorRole)(d->parsed.toInt()));
1318 ColorData color = parseColorValue(d->values.at(0));
1319 if(color.type == ColorData::Role) {
1320 d->parsed = QVariant::fromValue<int>(color.role);
1321 return pal.color((QPalette::ColorRole)(color.role));
1323 d->parsed = QVariant::fromValue<QColor>(color.color);
1328 QBrush Declaration::brushValue(const QPalette &pal) const
1330 if (d->values.count() != 1)
1333 if (d->parsed.isValid()) {
1334 if (d->parsed.type() == QVariant::Brush)
1335 return qvariant_cast<QBrush>(d->parsed);
1336 if (d->parsed.type() == QVariant::Int)
1337 return pal.color((QPalette::ColorRole)(d->parsed.toInt()));
1340 BrushData data = parseBrushValue(d->values.at(0), pal);
1342 if(data.type == BrushData::Role) {
1343 d->parsed = QVariant::fromValue<int>(data.role);
1344 return pal.color((QPalette::ColorRole)(data.role));
1346 if (data.type != BrushData::DependsOnThePalette)
1347 d->parsed = QVariant::fromValue<QBrush>(data.brush);
1352 void Declaration::brushValues(QBrush *c, const QPalette &pal) const
1354 int needParse = 0x1f; // bits 0..3 say if we should parse the corresponding value.
1355 // the bit 4 say we need to update d->parsed
1357 if (d->parsed.isValid()) {
1359 QList<QVariant> v = d->parsed.toList();
1360 for (i = 0; i < qMin(v.count(), 4); i++) {
1361 if (v.at(i).type() == QVariant::Brush) {
1362 c[i] = qvariant_cast<QBrush>(v.at(i));
1363 } else if (v.at(i).type() == QVariant::Int) {
1364 c[i] = pal.color((QPalette::ColorRole)(v.at(i).toInt()));
1366 needParse |= (1<<i);
1370 if (needParse != 0) {
1372 for (i = 0; i < qMin(d->values.count(), 4); i++) {
1373 if (!(needParse & (1<<i)))
1375 BrushData data = parseBrushValue(d->values.at(i), pal);
1376 if(data.type == BrushData::Role) {
1377 v += QVariant::fromValue<int>(data.role);
1378 c[i] = pal.color((QPalette::ColorRole)(data.role));
1380 if (data.type != BrushData::DependsOnThePalette) {
1381 v += QVariant::fromValue<QBrush>(data.brush);
1388 if (needParse & 0x10)
1391 if (i == 0) c[0] = c[1] = c[2] = c[3] = QBrush();
1392 else if (i == 1) c[3] = c[2] = c[1] = c[0];
1393 else if (i == 2) c[2] = c[0], c[3] = c[1];
1394 else if (i == 3) c[3] = c[1];
1397 bool Declaration::realValue(qreal *real, const char *unit) const
1399 if (d->values.count() != 1)
1401 const Value &v = d->values.at(0);
1402 if (unit && v.type != Value::Length)
1404 QString s = v.variant.toString();
1406 if (!s.endsWith(QLatin1String(unit), Qt::CaseInsensitive))
1408 s.chop(qstrlen(unit));
1411 qreal val = s.toDouble(&ok);
1417 static bool intValueHelper(const QCss::Value &v, int *i, const char *unit)
1419 if (unit && v.type != Value::Length)
1421 QString s = v.variant.toString();
1423 if (!s.endsWith(QLatin1String(unit), Qt::CaseInsensitive))
1425 s.chop(qstrlen(unit));
1428 int val = s.toInt(&ok);
1434 bool Declaration::intValue(int *i, const char *unit) const
1436 if (d->values.count() != 1)
1438 return intValueHelper(d->values.at(0), i, unit);
1441 QSize Declaration::sizeValue() const
1443 if (d->parsed.isValid())
1444 return qvariant_cast<QSize>(d->parsed);
1446 int x[2] = { 0, 0 };
1447 if (d->values.count() > 0)
1448 intValueHelper(d->values.at(0), &x[0], "px");
1449 if (d->values.count() > 1)
1450 intValueHelper(d->values.at(1), &x[1], "px");
1453 QSize size(x[0], x[1]);
1454 d->parsed = QVariant::fromValue<QSize>(size);
1458 QRect Declaration::rectValue() const
1460 if (d->values.count() != 1)
1463 if (d->parsed.isValid())
1464 return qvariant_cast<QRect>(d->parsed);
1466 const QCss::Value &v = d->values.at(0);
1467 if (v.type != Value::Function)
1469 QStringList func = v.variant.toStringList();
1470 if (func.count() != 2 || func.at(0).compare(QLatin1String("rect")) != 0)
1472 QStringList args = func[1].split(QLatin1Char(' '), QString::SkipEmptyParts);
1473 if (args.count() != 4)
1475 QRect rect(args[0].toInt(), args[1].toInt(), args[2].toInt(), args[3].toInt());
1476 d->parsed = QVariant::fromValue<QRect>(rect);
1480 void Declaration::colorValues(QColor *c, const QPalette &pal) const
1483 if (d->parsed.isValid()) {
1484 QList<QVariant> v = d->parsed.toList();
1485 for (i = 0; i < qMin(d->values.count(), 4); i++) {
1486 if (v.at(i).type() == QVariant::Color) {
1487 c[i] = qvariant_cast<QColor>(v.at(i));
1489 c[i] = pal.color((QPalette::ColorRole)(v.at(i).toInt()));
1494 for (i = 0; i < qMin(d->values.count(), 4); i++) {
1495 ColorData color = parseColorValue(d->values.at(i));
1496 if(color.type == ColorData::Role) {
1497 v += QVariant::fromValue<int>(color.role);
1498 c[i] = pal.color((QPalette::ColorRole)(color.role));
1500 v += QVariant::fromValue<QColor>(color.color);
1507 if (i == 0) c[0] = c[1] = c[2] = c[3] = QColor();
1508 else if (i == 1) c[3] = c[2] = c[1] = c[0];
1509 else if (i == 2) c[2] = c[0], c[3] = c[1];
1510 else if (i == 3) c[3] = c[1];
1513 BorderStyle Declaration::styleValue() const
1515 if (d->values.count() != 1)
1516 return BorderStyle_None;
1517 return parseStyleValue(d->values.at(0));
1520 void Declaration::styleValues(BorderStyle *s) const
1523 for (i = 0; i < qMin(d->values.count(), 4); i++)
1524 s[i] = parseStyleValue(d->values.at(i));
1525 if (i == 0) s[0] = s[1] = s[2] = s[3] = BorderStyle_None;
1526 else if (i == 1) s[3] = s[2] = s[1] = s[0];
1527 else if (i == 2) s[2] = s[0], s[3] = s[1];
1528 else if (i == 3) s[3] = s[1];
1531 Repeat Declaration::repeatValue() const
1533 if (d->parsed.isValid())
1534 return static_cast<Repeat>(d->parsed.toInt());
1535 if (d->values.count() != 1)
1536 return Repeat_Unknown;
1537 int v = findKnownValue(d->values.at(0).variant.toString(),
1538 repeats, NumKnownRepeats);
1540 return static_cast<Repeat>(v);
1543 Origin Declaration::originValue() const
1545 if (d->parsed.isValid())
1546 return static_cast<Origin>(d->parsed.toInt());
1547 if (d->values.count() != 1)
1548 return Origin_Unknown;
1549 int v = findKnownValue(d->values.at(0).variant.toString(),
1550 origins, NumKnownOrigins);
1552 return static_cast<Origin>(v);
1555 PositionMode Declaration::positionValue() const
1557 if (d->parsed.isValid())
1558 return static_cast<PositionMode>(d->parsed.toInt());
1559 if (d->values.count() != 1)
1560 return PositionMode_Unknown;
1561 int v = findKnownValue(d->values.at(0).variant.toString(),
1562 positions, NumKnownPositionModes);
1564 return static_cast<PositionMode>(v);
1567 Attachment Declaration::attachmentValue() const
1569 if (d->parsed.isValid())
1570 return static_cast<Attachment>(d->parsed.toInt());
1571 if (d->values.count() != 1)
1572 return Attachment_Unknown;
1573 int v = findKnownValue(d->values.at(0).variant.toString(),
1574 attachments, NumKnownAttachments);
1576 return static_cast<Attachment>(v);
1579 int Declaration::styleFeaturesValue() const
1581 Q_ASSERT(d->propertyId == QtStyleFeatures);
1582 if (d->parsed.isValid())
1583 return d->parsed.toInt();
1584 int features = StyleFeature_None;
1585 for (int i = 0; i < d->values.count(); i++) {
1586 features |= static_cast<int>(findKnownValue(d->values.value(i).variant.toString(),
1587 styleFeatures, NumKnownStyleFeatures));
1589 d->parsed = features;
1593 QString Declaration::uriValue() const
1595 if (d->values.isEmpty() || d->values.at(0).type != Value::Uri)
1597 return d->values.at(0).variant.toString();
1600 Qt::Alignment Declaration::alignmentValue() const
1602 if (d->parsed.isValid())
1603 return Qt::Alignment(d->parsed.toInt());
1604 if (d->values.isEmpty() || d->values.count() > 2)
1605 return Qt::AlignLeft | Qt::AlignTop;
1607 Qt::Alignment v = parseAlignment(d->values.constData(), d->values.count());
1612 void Declaration::borderImageValue(QString *image, int *cuts,
1613 TileMode *h, TileMode *v) const
1615 *image = uriValue();
1616 for (int i = 0; i < 4; i++)
1618 *h = *v = TileMode_Stretch;
1620 if (d->values.count() < 2)
1623 if (d->values.at(1).type == Value::Number) { // cuts!
1625 for (i = 0; i < qMin(d->values.count()-1, 4); i++) {
1626 const Value& v = d->values.at(i+1);
1627 if (v.type != Value::Number)
1629 cuts[i] = v.variant.toString().toInt();
1631 if (i == 0) cuts[0] = cuts[1] = cuts[2] = cuts[3] = 0;
1632 else if (i == 1) cuts[3] = cuts[2] = cuts[1] = cuts[0];
1633 else if (i == 2) cuts[2] = cuts[0], cuts[3] = cuts[1];
1634 else if (i == 3) cuts[3] = cuts[1];
1637 if (d->values.last().type == Value::Identifier) {
1638 *v = static_cast<TileMode>(findKnownValue(d->values.last().variant.toString(),
1639 tileModes, NumKnownTileModes));
1641 if (d->values[d->values.count() - 2].type == Value::Identifier) {
1642 *h = static_cast<TileMode>
1643 (findKnownValue(d->values[d->values.count()-2].variant.toString(),
1644 tileModes, NumKnownTileModes));
1651 QIcon Declaration::iconValue() const
1653 if (d->parsed.isValid())
1654 return qvariant_cast<QIcon>(d->parsed);
1657 for (int i = 0; i < d->values.count();) {
1658 const Value &value = d->values.at(i++);
1659 if (value.type != Value::Uri)
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;
1681 // QIcon is soo broken
1685 icon.addPixmap(uri, mode, state);
1687 if (i == d->values.count())
1690 if (d->values.at(i).type == Value::TermOperatorComma)
1694 d->parsed = QVariant::fromValue<QIcon>(icon);
1699 ///////////////////////////////////////////////////////////////////////////////
1701 int Selector::specificity() const
1704 for (int i = 0; i < basicSelectors.count(); ++i) {
1705 const BasicSelector &sel = basicSelectors.at(i);
1706 if (!sel.elementName.isEmpty())
1709 val += (sel.pseudos.count() + sel.attributeSelectors.count()) * 0x10;
1710 val += sel.ids.count() * 0x100;
1715 QString Selector::pseudoElement() const
1717 const BasicSelector& bs = basicSelectors.last();
1718 if (!bs.pseudos.isEmpty() && bs.pseudos.at(0).type == PseudoClass_Unknown)
1719 return bs.pseudos.at(0).name;
1723 quint64 Selector::pseudoClass(quint64 *negated) const
1725 const BasicSelector& bs = basicSelectors.last();
1726 if (bs.pseudos.isEmpty())
1727 return PseudoClass_Unspecified;
1728 quint64 pc = PseudoClass_Unknown;
1729 for (int i = !pseudoElement().isEmpty(); i < bs.pseudos.count(); i++) {
1730 const Pseudo &pseudo = bs.pseudos.at(i);
1731 if (pseudo.type == PseudoClass_Unknown)
1732 return PseudoClass_Unknown;
1733 if (!pseudo.negated)
1736 *negated |= pseudo.type;
1741 ///////////////////////////////////////////////////////////////////////////////
1743 void StyleSheet::buildIndexes(Qt::CaseSensitivity nameCaseSensitivity)
1745 QVector<StyleRule> universals;
1746 for (int i = 0; i < styleRules.count(); ++i) {
1747 const StyleRule &rule = styleRules.at(i);
1748 QVector<Selector> universalsSelectors;
1749 for (int j = 0; j < rule.selectors.count(); ++j) {
1750 const Selector& selector = rule.selectors.at(j);
1752 if (selector.basicSelectors.isEmpty())
1755 if (selector.basicSelectors.at(0).relationToNext == BasicSelector::NoRelation) {
1756 if (selector.basicSelectors.count() != 1)
1758 } else if (selector.basicSelectors.count() <= 1) {
1762 const BasicSelector &sel = selector.basicSelectors.at(selector.basicSelectors.count() - 1);
1764 if (!sel.ids.isEmpty()) {
1766 nr.selectors += selector;
1767 nr.declarations = rule.declarations;
1769 idIndex.insert(sel.ids.at(0), nr);
1770 } else if (!sel.elementName.isEmpty()) {
1772 nr.selectors += selector;
1773 nr.declarations = rule.declarations;
1775 QString name = sel.elementName;
1776 if (nameCaseSensitivity == Qt::CaseInsensitive)
1777 name=name.toLower();
1778 nameIndex.insert(name, nr);
1780 universalsSelectors += selector;
1783 if (!universalsSelectors.isEmpty()) {
1785 nr.selectors = universalsSelectors;
1786 nr.declarations = rule.declarations;
1791 styleRules = universals;
1794 ///////////////////////////////////////////////////////////////////////////////
1796 StyleSelector::~StyleSelector()
1800 bool StyleSelector::nodeNameEquals(NodePtr node, const QString& nodeName) const
1802 return nodeNames(node).contains(nodeName, nameCaseSensitivity);
1805 QStringList StyleSelector::nodeIds(NodePtr node) const
1807 return QStringList(attribute(node, QLatin1String("id")));
1810 bool StyleSelector::selectorMatches(const Selector &selector, NodePtr node)
1812 if (selector.basicSelectors.isEmpty())
1815 if (selector.basicSelectors.at(0).relationToNext == BasicSelector::NoRelation) {
1816 if (selector.basicSelectors.count() != 1)
1818 return basicSelectorMatches(selector.basicSelectors.at(0), node);
1820 if (selector.basicSelectors.count() <= 1)
1823 int i = selector.basicSelectors.count() - 1;
1824 node = duplicateNode(node);
1827 BasicSelector sel = selector.basicSelectors.at(i);
1829 match = basicSelectorMatches(sel, node);
1831 if (sel.relationToNext == BasicSelector::MatchNextSelectorIfParent
1832 || i == selector.basicSelectors.count() - 1) // first element must always match!
1836 if (match || sel.relationToNext != BasicSelector::MatchNextSelectorIfAncestor)
1842 sel = selector.basicSelectors.at(i);
1843 if (sel.relationToNext == BasicSelector::MatchNextSelectorIfAncestor
1844 || sel.relationToNext == BasicSelector::MatchNextSelectorIfParent) {
1846 NodePtr nextParent = parentNode(node);
1849 } else if (sel.relationToNext == BasicSelector::MatchNextSelectorIfPreceeds) {
1850 NodePtr previousSibling = previousSiblingNode(node);
1852 node = previousSibling;
1854 if (isNullNode(node)) {
1858 } while (i >= 0 && (match || sel.relationToNext == BasicSelector::MatchNextSelectorIfAncestor));
1865 bool StyleSelector::basicSelectorMatches(const BasicSelector &sel, NodePtr node)
1867 if (!sel.attributeSelectors.isEmpty()) {
1868 if (!hasAttributes(node))
1871 for (int i = 0; i < sel.attributeSelectors.count(); ++i) {
1872 const QCss::AttributeSelector &a = sel.attributeSelectors.at(i);
1874 const QString attrValue = attribute(node, a.name);
1875 if (attrValue.isNull())
1878 if (a.valueMatchCriterium == QCss::AttributeSelector::MatchContains) {
1880 QStringList lst = attrValue.split(QLatin1Char(' '));
1881 if (!lst.contains(a.value))
1884 (a.valueMatchCriterium == QCss::AttributeSelector::MatchEqual
1885 && attrValue != a.value)
1887 (a.valueMatchCriterium == QCss::AttributeSelector::MatchBeginsWith
1888 && !attrValue.startsWith(a.value))
1894 if (!sel.elementName.isEmpty()
1895 && !nodeNameEquals(node, sel.elementName))
1898 if (!sel.ids.isEmpty()
1899 && sel.ids != nodeIds(node))
1905 void StyleSelector::matchRule(NodePtr node, const StyleRule &rule, StyleSheetOrigin origin,
1906 int depth, QMap<uint, StyleRule> *weightedRules)
1908 for (int j = 0; j < rule.selectors.count(); ++j) {
1909 const Selector& selector = rule.selectors.at(j);
1910 if (selectorMatches(selector, node)) {
1911 uint weight = rule.order
1912 + selector.specificity() *0x100
1913 + (uint(origin) + depth)*0x100000;
1914 StyleRule newRule = rule;
1915 if(rule.selectors.count() > 1) {
1916 newRule.selectors.resize(1);
1917 newRule.selectors[0] = selector;
1919 //We might have rules with the same weight if they came from a rule with several selectors
1920 weightedRules->insertMulti(weight, newRule);
1925 // Returns style rules that are in ascending order of specificity
1926 // Each of the StyleRule returned will contain exactly one Selector
1927 QVector<StyleRule> StyleSelector::styleRulesForNode(NodePtr node)
1929 QVector<StyleRule> rules;
1930 if (styleSheets.isEmpty())
1933 QMap<uint, StyleRule> weightedRules; // (spec, rule) that will be sorted below
1935 //prune using indexed stylesheet
1936 for (int sheetIdx = 0; sheetIdx < styleSheets.count(); ++sheetIdx) {
1937 const StyleSheet &styleSheet = styleSheets.at(sheetIdx);
1938 for (int i = 0; i < styleSheet.styleRules.count(); ++i) {
1939 matchRule(node, styleSheet.styleRules.at(i), styleSheet.origin, styleSheet.depth, &weightedRules);
1942 if (!styleSheet.idIndex.isEmpty()) {
1943 QStringList ids = nodeIds(node);
1944 for (int i = 0; i < ids.count(); i++) {
1945 const QString &key = ids.at(i);
1946 QMultiHash<QString, StyleRule>::const_iterator it = styleSheet.idIndex.constFind(key);
1947 while (it != styleSheet.idIndex.constEnd() && it.key() == key) {
1948 matchRule(node, it.value(), styleSheet.origin, styleSheet.depth, &weightedRules);
1953 if (!styleSheet.nameIndex.isEmpty()) {
1954 QStringList names = nodeNames(node);
1955 for (int i = 0; i < names.count(); i++) {
1956 QString name = names.at(i);
1957 if (nameCaseSensitivity == Qt::CaseInsensitive)
1958 name = name.toLower();
1959 QMultiHash<QString, StyleRule>::const_iterator it = styleSheet.nameIndex.constFind(name);
1960 while (it != styleSheet.nameIndex.constEnd() && it.key() == name) {
1961 matchRule(node, it.value(), styleSheet.origin, styleSheet.depth, &weightedRules);
1966 if (!medium.isEmpty()) {
1967 for (int i = 0; i < styleSheet.mediaRules.count(); ++i) {
1968 if (styleSheet.mediaRules.at(i).media.contains(medium, Qt::CaseInsensitive)) {
1969 for (int j = 0; j < styleSheet.mediaRules.at(i).styleRules.count(); ++j) {
1970 matchRule(node, styleSheet.mediaRules.at(i).styleRules.at(j), styleSheet.origin,
1971 styleSheet.depth, &weightedRules);
1978 rules.reserve(weightedRules.count());
1979 QMap<uint, StyleRule>::const_iterator it = weightedRules.constBegin();
1980 for ( ; it != weightedRules.constEnd() ; ++it)
1986 // for qtexthtmlparser which requires just the declarations with Enabled state
1987 // and without pseudo elements
1988 QVector<Declaration> StyleSelector::declarationsForNode(NodePtr node, const char *extraPseudo)
1990 QVector<Declaration> decls;
1991 QVector<StyleRule> rules = styleRulesForNode(node);
1992 for (int i = 0; i < rules.count(); i++) {
1993 const Selector& selector = rules.at(i).selectors.at(0);
1994 const QString pseudoElement = selector.pseudoElement();
1996 if (extraPseudo && pseudoElement == QLatin1String(extraPseudo)) {
1997 decls += rules.at(i).declarations;
2001 if (!pseudoElement.isEmpty()) // skip rules with pseudo elements
2003 quint64 pseudoClass = selector.pseudoClass();
2004 if (pseudoClass == PseudoClass_Enabled || pseudoClass == PseudoClass_Unspecified)
2005 decls += rules.at(i).declarations;
2010 static inline bool isHexDigit(const char c)
2012 return (c >= '0' && c <= '9')
2013 || (c >= 'a' && c <= 'f')
2014 || (c >= 'A' && c <= 'F')
2018 QString Scanner::preprocess(const QString &input, bool *hasEscapeSequences)
2020 QString output = input;
2022 if (hasEscapeSequences)
2023 *hasEscapeSequences = false;
2026 while (i < output.size()) {
2027 if (output.at(i) == QLatin1Char('\\')) {
2030 // test for unicode hex escape
2032 const int hexStart = i;
2033 while (i < output.size()
2034 && isHexDigit(output.at(i).toLatin1())
2039 if (hexCount == 0) {
2040 if (hasEscapeSequences)
2041 *hasEscapeSequences = true;
2045 hexCount = qMin(hexCount, 6);
2047 ushort code = output.mid(hexStart, hexCount).toUShort(&ok, 16);
2049 output.replace(hexStart - 1, hexCount + 1, QChar(code));
2061 int QCssScanner_Generated::handleCommentStart()
2063 while (pos < input.size() - 1) {
2064 if (input.at(pos) == QLatin1Char('*')
2065 && input.at(pos + 1) == QLatin1Char('/')) {
2074 void Scanner::scan(const QString &preprocessedInput, QVector<Symbol> *symbols)
2076 QCssScanner_Generated scanner(preprocessedInput);
2078 int tok = scanner.lex();
2080 sym.token = static_cast<QCss::TokenType>(tok);
2081 sym.text = scanner.input;
2082 sym.start = scanner.lexemStart;
2083 sym.len = scanner.lexemLength;
2084 symbols->append(sym);
2085 tok = scanner.lex();
2089 QString Symbol::lexem() const
2093 result.reserve(len);
2094 for (int i = 0; i < len; ++i) {
2095 if (text.at(start + i) == QLatin1Char('\\') && i < len - 1)
2097 result += text.at(start + i);
2102 Parser::Parser(const QString &css, bool isFile)
2111 hasEscapeSequences = false;
2114 void Parser::init(const QString &css, bool isFile)
2116 QString styleSheet = css;
2119 if (file.open(QFile::ReadOnly)) {
2120 sourcePath = QFileInfo(styleSheet).absolutePath() + QLatin1Char('/');
2121 QTextStream stream(&file);
2122 styleSheet = stream.readAll();
2124 qWarning() << "QCss::Parser - Failed to load file " << css;
2131 hasEscapeSequences = false;
2134 Scanner::scan(Scanner::preprocess(styleSheet, &hasEscapeSequences), &symbols);
2139 bool Parser::parse(StyleSheet *styleSheet, Qt::CaseSensitivity nameCaseSensitivity)
2141 if (testTokenAndEndsWith(ATKEYWORD_SYM, QLatin1String("charset"))) {
2142 if (!next(STRING)) return false;
2143 if (!next(SEMICOLON)) return false;
2146 while (test(S) || test(CDO) || test(CDC)) {}
2148 while (testImport()) {
2150 if (!parseImport(&rule)) return false;
2151 styleSheet->importRules.append(rule);
2152 while (test(S) || test(CDO) || test(CDC)) {}
2158 if (!parseMedia(&rule)) return false;
2159 styleSheet->mediaRules.append(rule);
2160 } else if (testPage()) {
2162 if (!parsePage(&rule)) return false;
2163 styleSheet->pageRules.append(rule);
2164 } else if (testRuleset()) {
2166 if (!parseRuleset(&rule)) return false;
2167 styleSheet->styleRules.append(rule);
2168 } else if (test(ATKEYWORD_SYM)) {
2169 if (!until(RBRACE)) return false;
2170 } else if (hasNext()) {
2173 while (test(S) || test(CDO) || test(CDC)) {}
2174 } while (hasNext());
2175 styleSheet->buildIndexes(nameCaseSensitivity);
2179 Symbol Parser::errorSymbol()
2181 if (errorIndex == -1) return Symbol();
2182 return symbols.at(errorIndex);
2185 static inline void removeOptionalQuotes(QString *str)
2187 if (!str->startsWith(QLatin1Char('\''))
2188 && !str->startsWith(QLatin1Char('\"')))
2194 bool Parser::parseImport(ImportRule *importRule)
2199 importRule->href = lexem();
2201 if (!testAndParseUri(&importRule->href)) return false;
2203 removeOptionalQuotes(&importRule->href);
2208 if (!parseMedium(&importRule->media)) return false;
2210 while (test(COMMA)) {
2212 if (!parseNextMedium(&importRule->media)) return false;
2216 if (!next(SEMICOLON)) return false;
2222 bool Parser::parseMedia(MediaRule *mediaRule)
2226 if (!parseNextMedium(&mediaRule->media)) return false;
2227 } while (test(COMMA));
2229 if (!next(LBRACE)) return false;
2232 while (testRuleset()) {
2234 if (!parseRuleset(&rule)) return false;
2235 mediaRule->styleRules.append(rule);
2238 if (!next(RBRACE)) return false;
2243 bool Parser::parseMedium(QStringList *media)
2245 media->append(lexem());
2250 bool Parser::parsePage(PageRule *pageRule)
2254 if (testPseudoPage())
2255 if (!parsePseudoPage(&pageRule->selector)) return false;
2258 if (!next(LBRACE)) return false;
2263 if (!parseNextDeclaration(&decl)) return false;
2264 if (!decl.isEmpty())
2265 pageRule->declarations.append(decl);
2266 } while (test(SEMICOLON));
2268 if (!next(RBRACE)) return false;
2273 bool Parser::parsePseudoPage(QString *selector)
2275 if (!next(IDENT)) return false;
2276 *selector = lexem();
2280 bool Parser::parseNextOperator(Value *value)
2282 if (!hasNext()) return true;
2284 case SLASH: value->type = Value::TermOperatorSlash; skipSpace(); break;
2285 case COMMA: value->type = Value::TermOperatorComma; skipSpace(); break;
2286 default: prev(); break;
2291 bool Parser::parseCombinator(BasicSelector::Relation *relation)
2293 *relation = BasicSelector::NoRelation;
2294 if (lookup() == S) {
2295 *relation = BasicSelector::MatchNextSelectorIfAncestor;
2301 *relation = BasicSelector::MatchNextSelectorIfPreceeds;
2302 } else if (test(GREATER)) {
2303 *relation = BasicSelector::MatchNextSelectorIfParent;
2309 bool Parser::parseProperty(Declaration *decl)
2311 decl->d->property = lexem();
2312 decl->d->propertyId = static_cast<Property>(findKnownValue(decl->d->property, properties, NumProperties));
2317 bool Parser::parseRuleset(StyleRule *styleRule)
2320 if (!parseSelector(&sel)) return false;
2321 styleRule->selectors.append(sel);
2323 while (test(COMMA)) {
2326 if (!parseNextSelector(&sel)) return false;
2327 styleRule->selectors.append(sel);
2331 if (!next(LBRACE)) return false;
2332 const int declarationStart = index;
2337 const int rewind = index;
2338 if (!parseNextDeclaration(&decl)) {
2340 const bool foundSemicolon = until(SEMICOLON);
2341 const int semicolonIndex = index;
2343 index = declarationStart;
2344 const bool foundRBrace = until(RBRACE);
2346 if (foundSemicolon && semicolonIndex < index) {
2347 decl = Declaration();
2348 index = semicolonIndex - 1;
2354 if (!decl.isEmpty())
2355 styleRule->declarations.append(decl);
2356 } while (test(SEMICOLON));
2358 if (!next(RBRACE)) return false;
2363 bool Parser::parseSelector(Selector *sel)
2365 BasicSelector basicSel;
2366 if (!parseSimpleSelector(&basicSel)) return false;
2367 while (testCombinator()) {
2368 if (!parseCombinator(&basicSel.relationToNext)) return false;
2370 if (!testSimpleSelector()) break;
2371 sel->basicSelectors.append(basicSel);
2373 basicSel = BasicSelector();
2374 if (!parseSimpleSelector(&basicSel)) return false;
2376 sel->basicSelectors.append(basicSel);
2380 bool Parser::parseSimpleSelector(BasicSelector *basicSel)
2383 if (lookupElementName()) {
2384 if (!parseElementName(&basicSel->elementName)) return false;
2394 QString theid = lexem();
2395 // chop off leading #
2397 basicSel->ids.append(theid);
2399 } else if (testClass()) {
2401 AttributeSelector a;
2402 a.name = QLatin1String("class");
2403 a.valueMatchCriterium = AttributeSelector::MatchContains;
2404 if (!parseClass(&a.value)) return false;
2405 basicSel->attributeSelectors.append(a);
2406 } else if (testAttrib()) {
2408 AttributeSelector a;
2409 if (!parseAttrib(&a)) return false;
2410 basicSel->attributeSelectors.append(a);
2411 } else if (testPseudo()) {
2414 if (!parsePseudo(&ps)) return false;
2415 basicSel->pseudos.append(ps);
2417 if (onceMore) ++count;
2419 return count >= minCount;
2422 bool Parser::parseClass(QString *name)
2424 if (!next(IDENT)) return false;
2429 bool Parser::parseElementName(QString *name)
2432 case STAR: name->clear(); break;
2433 case IDENT: *name = lexem(); break;
2434 default: return false;
2439 bool Parser::parseAttrib(AttributeSelector *attr)
2442 if (!next(IDENT)) return false;
2443 attr->name = lexem();
2447 attr->valueMatchCriterium = AttributeSelector::MatchEqual;
2448 } else if (test(INCLUDES)) {
2449 attr->valueMatchCriterium = AttributeSelector::MatchContains;
2450 } else if (test(DASHMATCH)) {
2451 attr->valueMatchCriterium = AttributeSelector::MatchBeginsWith;
2453 return next(RBRACKET);
2458 if (!test(IDENT) && !test(STRING)) return false;
2459 attr->value = unquotedLexem();
2462 return next(RBRACKET);
2465 bool Parser::parsePseudo(Pseudo *pseudo)
2468 pseudo->negated = test(EXCLAMATION_SYM);
2470 pseudo->name = lexem();
2471 pseudo->type = static_cast<quint64>(findKnownValue(pseudo->name, pseudos, NumPseudos));
2474 if (!next(FUNCTION)) return false;
2475 pseudo->function = lexem();
2476 // chop off trailing parenthesis
2477 pseudo->function.chop(1);
2479 if (!test(IDENT)) return false;
2480 pseudo->name = lexem();
2482 return next(RPAREN);
2485 bool Parser::parseNextDeclaration(Declaration *decl)
2487 if (!testProperty())
2488 return true; // not an error!
2489 if (!parseProperty(decl)) return false;
2490 if (!next(COLON)) return false;
2492 if (!parseNextExpr(&decl->d->values)) return false;
2494 if (!parsePrio(decl)) return false;
2498 bool Parser::testPrio()
2500 const int rewind = index;
2501 if (!test(EXCLAMATION_SYM)) return false;
2507 if (lexem().compare(QLatin1String("important"), Qt::CaseInsensitive) != 0) {
2514 bool Parser::parsePrio(Declaration *declaration)
2516 declaration->d->important = true;
2521 bool Parser::parseExpr(QVector<Value> *values)
2524 if (!parseTerm(&val)) return false;
2525 values->append(val);
2530 if (!parseNextOperator(&val)) return false;
2531 if (val.type != QCss::Value::Unknown)
2532 values->append(val);
2536 if (!parseTerm(&val)) return false;
2537 values->append(val);
2543 bool Parser::testTerm()
2545 return test(PLUS) || test(MINUS)
2555 bool Parser::parseTerm(Value *value)
2557 QString str = lexem();
2558 bool haveUnary = false;
2559 if (lookup() == PLUS || lookup() == MINUS) {
2561 if (!hasNext()) return false;
2566 value->variant = str;
2567 value->type = QCss::Value::String;
2570 value->type = Value::Number;
2571 value->variant.convert(QVariant::Double);
2574 value->type = Value::Percentage;
2575 str.chop(1); // strip off %
2576 value->variant = str;
2579 value->type = Value::Length;
2583 if (haveUnary) return false;
2584 value->type = Value::String;
2587 value->variant = str;
2590 if (haveUnary) return false;
2591 value->type = Value::Identifier;
2592 const int theid = findKnownValue(str, values, NumKnownValues);
2594 value->type = Value::KnownIdentifier;
2595 value->variant = theid;
2600 if (haveUnary) return false;
2602 if (testHexColor()) {
2604 if (!parseHexColor(&col)) return false;
2605 value->type = Value::Color;
2606 value->variant = col;
2607 } else if (testFunction()) {
2609 if (!parseFunction(&name, &args)) return false;
2610 if (name == QLatin1String("url")) {
2611 value->type = Value::Uri;
2612 removeOptionalQuotes(&args);
2613 if (QFileInfo(args).isRelative() && !sourcePath.isEmpty()) {
2614 args.prepend(sourcePath);
2616 value->variant = args;
2618 value->type = Value::Function;
2619 value->variant = QStringList() << name << args;
2622 return recordError();
2631 bool Parser::parseFunction(QString *name, QString *args)
2636 const int start = index;
2637 if (!until(RPAREN)) return false;
2638 for (int i = start; i < index - 1; ++i)
2639 args->append(symbols.at(i).lexem());
2641 if (!nextExpr(&arguments)) return false;
2642 if (!next(RPAREN)) return false;
2648 bool Parser::parseHexColor(QColor *col)
2650 col->setNamedColor(lexem());
2651 if (!col->isValid()) {
2652 qWarning("QCssParser::parseHexColor: Unknown color name '%s'",lexem().toLatin1().constData());
2659 bool Parser::testAndParseUri(QString *uri)
2661 const int rewind = index;
2662 if (!testFunction()) return false;
2665 if (!parseFunction(&name, &args)) {
2669 if (name.toLower() != QLatin1String("url")) {
2674 removeOptionalQuotes(uri);
2678 bool Parser::testSimpleSelector()
2680 return testElementName()
2687 bool Parser::next(QCss::TokenType t)
2689 if (hasNext() && next() == t)
2691 return recordError();
2694 bool Parser::test(QCss::TokenType t)
2696 if (index >= symbols.count())
2698 if (symbols.at(index).token == t) {
2705 QString Parser::unquotedLexem() const
2707 QString s = lexem();
2708 if (lookup() == STRING) {
2715 QString Parser::lexemUntil(QCss::TokenType t)
2718 while (hasNext() && next() != t)
2719 lexem += symbol().lexem();
2723 bool Parser::until(QCss::TokenType target, QCss::TokenType target2)
2729 switch(symbols.at(index-1).token) {
2730 case LBRACE: ++braceCount; break;
2731 case LBRACKET: ++brackCount; break;
2733 case LPAREN: ++parenCount; break;
2737 while (index < symbols.size()) {
2738 QCss::TokenType t = symbols.at(index++).token;
2740 case LBRACE: ++braceCount; break;
2741 case RBRACE: --braceCount; break;
2742 case LBRACKET: ++brackCount; break;
2743 case RBRACKET: --brackCount; break;
2745 case LPAREN: ++parenCount; break;
2746 case RPAREN: --parenCount; break;
2749 if ((t == target || (target2 != NONE && t == target2))
2755 if (braceCount < 0 || brackCount < 0 || parenCount < 0) {
2763 bool Parser::testTokenAndEndsWith(QCss::TokenType t, const QLatin1String &str)
2765 if (!test(t)) return false;
2766 if (!lexem().endsWith(str, Qt::CaseInsensitive)) {
2774 #endif // QT_NO_CSSPARSER