1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtGui module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qcssparser_p.h"
48 #include <qfileinfo.h>
49 #include <qfontmetrics.h>
51 #include <qimagereader.h>
52 #include "private/qfunctions_p.h"
54 #ifndef QT_NO_CSSPARSER
58 #include "qcssscanner.cpp"
68 static const QCssKnownValue properties[NumProperties - 1] = {
69 { "-qt-background-role", QtBackgroundRole },
70 { "-qt-block-indent", QtBlockIndent },
71 { "-qt-list-indent", QtListIndent },
72 { "-qt-list-number-prefix", QtListNumberPrefix },
73 { "-qt-list-number-suffix", QtListNumberSuffix },
74 { "-qt-paragraph-type", QtParagraphType },
75 { "-qt-style-features", QtStyleFeatures },
76 { "-qt-table-type", QtTableType },
77 { "-qt-user-state", QtUserState },
78 { "alternate-background-color", QtAlternateBackground },
79 { "background", Background },
80 { "background-attachment", BackgroundAttachment },
81 { "background-clip", BackgroundClip },
82 { "background-color", BackgroundColor },
83 { "background-image", BackgroundImage },
84 { "background-origin", BackgroundOrigin },
85 { "background-position", BackgroundPosition },
86 { "background-repeat", BackgroundRepeat },
88 { "border-bottom", BorderBottom },
89 { "border-bottom-color", BorderBottomColor },
90 { "border-bottom-left-radius", BorderBottomLeftRadius },
91 { "border-bottom-right-radius", BorderBottomRightRadius },
92 { "border-bottom-style", BorderBottomStyle },
93 { "border-bottom-width", BorderBottomWidth },
94 { "border-color", BorderColor },
95 { "border-image", BorderImage },
96 { "border-left", BorderLeft },
97 { "border-left-color", BorderLeftColor },
98 { "border-left-style", BorderLeftStyle },
99 { "border-left-width", BorderLeftWidth },
100 { "border-radius", BorderRadius },
101 { "border-right", BorderRight },
102 { "border-right-color", BorderRightColor },
103 { "border-right-style", BorderRightStyle },
104 { "border-right-width", BorderRightWidth },
105 { "border-style", BorderStyles },
106 { "border-top", BorderTop },
107 { "border-top-color", BorderTopColor },
108 { "border-top-left-radius", BorderTopLeftRadius },
109 { "border-top-right-radius", BorderTopRightRadius },
110 { "border-top-style", BorderTopStyle },
111 { "border-top-width", BorderTopWidth },
112 { "border-width", BorderWidth },
113 { "bottom", Bottom },
117 { "font-family", FontFamily },
118 { "font-size", FontSize },
119 { "font-style", FontStyle },
120 { "font-variant", FontVariant },
121 { "font-weight", FontWeight },
122 { "height", Height },
123 { "image", QtImage },
124 { "image-position", QtImageAlignment },
126 { "line-height", LineHeight },
127 { "list-style", ListStyle },
128 { "list-style-type", ListStyleType },
129 { "margin" , Margin },
130 { "margin-bottom", MarginBottom },
131 { "margin-left", MarginLeft },
132 { "margin-right", MarginRight },
133 { "margin-top", MarginTop },
134 { "max-height", MaximumHeight },
135 { "max-width", MaximumWidth },
136 { "min-height", MinimumHeight },
137 { "min-width", MinimumWidth },
138 { "outline", Outline },
139 { "outline-bottom-left-radius", OutlineBottomLeftRadius },
140 { "outline-bottom-right-radius", OutlineBottomRightRadius },
141 { "outline-color", OutlineColor },
142 { "outline-offset", OutlineOffset },
143 { "outline-radius", OutlineRadius },
144 { "outline-style", OutlineStyle },
145 { "outline-top-left-radius", OutlineTopLeftRadius },
146 { "outline-top-right-radius", OutlineTopRightRadius },
147 { "outline-width", OutlineWidth },
148 { "padding", Padding },
149 { "padding-bottom", PaddingBottom },
150 { "padding-left", PaddingLeft },
151 { "padding-right", PaddingRight },
152 { "padding-top", PaddingTop },
153 { "page-break-after", PageBreakAfter },
154 { "page-break-before", PageBreakBefore },
155 { "position", Position },
157 { "selection-background-color", QtSelectionBackground },
158 { "selection-color", QtSelectionForeground },
159 { "spacing", QtSpacing },
160 { "subcontrol-origin", QtOrigin },
161 { "subcontrol-position", QtPosition },
162 { "text-align", TextAlignment },
163 { "text-decoration", TextDecoration },
164 { "text-indent", TextIndent },
165 { "text-transform", TextTransform },
166 { "text-underline-style", TextUnderlineStyle },
168 { "vertical-align", VerticalAlignment },
169 { "white-space", Whitespace },
173 static const QCssKnownValue values[NumKnownValues - 1] = {
174 { "active", Value_Active },
175 { "alternate-base", Value_AlternateBase },
176 { "always", Value_Always },
177 { "auto", Value_Auto },
178 { "base", Value_Base },
179 { "bold", Value_Bold },
180 { "bottom", Value_Bottom },
181 { "bright-text", Value_BrightText },
182 { "button", Value_Button },
183 { "button-text", Value_ButtonText },
184 { "center", Value_Center },
185 { "circle", Value_Circle },
186 { "dark", Value_Dark },
187 { "dashed", Value_Dashed },
188 { "decimal", Value_Decimal },
189 { "disabled", Value_Disabled },
190 { "disc", Value_Disc },
191 { "dot-dash", Value_DotDash },
192 { "dot-dot-dash", Value_DotDotDash },
193 { "dotted", Value_Dotted },
194 { "double", Value_Double },
195 { "groove", Value_Groove },
196 { "highlight", Value_Highlight },
197 { "highlighted-text", Value_HighlightedText },
198 { "inset", Value_Inset },
199 { "italic", Value_Italic },
200 { "large", Value_Large },
201 { "left", Value_Left },
202 { "light", Value_Light },
203 { "line-through", Value_LineThrough },
204 { "link", Value_Link },
205 { "link-visited", Value_LinkVisited },
206 { "lower-alpha", Value_LowerAlpha },
207 { "lower-roman", Value_LowerRoman },
208 { "lowercase", Value_Lowercase },
209 { "medium", Value_Medium },
210 { "mid", Value_Mid },
211 { "middle", Value_Middle },
212 { "midlight", Value_Midlight },
213 { "native", Value_Native },
214 { "none", Value_None },
215 { "normal", Value_Normal },
216 { "nowrap", Value_NoWrap },
217 { "oblique", Value_Oblique },
218 { "off", Value_Off },
220 { "outset", Value_Outset },
221 { "overline", Value_Overline },
222 { "pre", Value_Pre },
223 { "pre-wrap", Value_PreWrap },
224 { "ridge", Value_Ridge },
225 { "right", Value_Right },
226 { "selected", Value_Selected },
227 { "shadow", Value_Shadow },
228 { "small" , Value_Small },
229 { "small-caps", Value_SmallCaps },
230 { "solid", Value_Solid },
231 { "square", Value_Square },
232 { "sub", Value_Sub },
233 { "super", Value_Super },
234 { "text", Value_Text },
235 { "top", Value_Top },
236 { "transparent", Value_Transparent },
237 { "underline", Value_Underline },
238 { "upper-alpha", Value_UpperAlpha },
239 { "upper-roman", Value_UpperRoman },
240 { "uppercase", Value_Uppercase },
241 { "wave", Value_Wave },
242 { "window", Value_Window },
243 { "window-text", Value_WindowText },
244 { "x-large", Value_XLarge },
245 { "xx-large", Value_XXLarge }
248 //Map id to strings as they appears in the 'values' array above
249 static const short indexOfId[NumKnownValues] = { 0, 41, 48, 42, 49, 54, 35, 26, 70, 71, 25, 43, 5, 63, 47,
250 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,
251 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,
252 1, 15, 0, 52, 45, 44 };
254 QString Value::toString() const
256 if (type == KnownIdentifier) {
257 return QLatin1String(values[indexOfId[variant.toInt()]].name);
259 return variant.toString();
263 static const QCssKnownValue pseudos[NumPseudos - 1] = {
264 { "active", PseudoClass_Active },
265 { "adjoins-item", PseudoClass_Item },
266 { "alternate", PseudoClass_Alternate },
267 { "bottom", PseudoClass_Bottom },
268 { "checked", PseudoClass_Checked },
269 { "closable", PseudoClass_Closable },
270 { "closed", PseudoClass_Closed },
271 { "default", PseudoClass_Default },
272 { "disabled", PseudoClass_Disabled },
273 { "edit-focus", PseudoClass_EditFocus },
274 { "editable", PseudoClass_Editable },
275 { "enabled", PseudoClass_Enabled },
276 { "exclusive", PseudoClass_Exclusive },
277 { "first", PseudoClass_First },
278 { "flat", PseudoClass_Flat },
279 { "floatable", PseudoClass_Floatable },
280 { "focus", PseudoClass_Focus },
281 { "has-children", PseudoClass_Children },
282 { "has-siblings", PseudoClass_Sibling },
283 { "horizontal", PseudoClass_Horizontal },
284 { "hover", PseudoClass_Hover },
285 { "indeterminate" , PseudoClass_Indeterminate },
286 { "last", PseudoClass_Last },
287 { "left", PseudoClass_Left },
288 { "maximized", PseudoClass_Maximized },
289 { "middle", PseudoClass_Middle },
290 { "minimized", PseudoClass_Minimized },
291 { "movable", PseudoClass_Movable },
292 { "next-selected", PseudoClass_NextSelected },
293 { "no-frame", PseudoClass_Frameless },
294 { "non-exclusive", PseudoClass_NonExclusive },
295 { "off", PseudoClass_Unchecked },
296 { "on", PseudoClass_Checked },
297 { "only-one", PseudoClass_OnlyOne },
298 { "open", PseudoClass_Open },
299 { "pressed", PseudoClass_Pressed },
300 { "previous-selected", PseudoClass_PreviousSelected },
301 { "read-only", PseudoClass_ReadOnly },
302 { "right", PseudoClass_Right },
303 { "selected", PseudoClass_Selected },
304 { "top", PseudoClass_Top },
305 { "unchecked" , PseudoClass_Unchecked },
306 { "vertical", PseudoClass_Vertical },
307 { "window", PseudoClass_Window }
310 static const QCssKnownValue origins[NumKnownOrigins - 1] = {
311 { "border", Origin_Border },
312 { "content", Origin_Content },
313 { "margin", Origin_Margin }, // not in css
314 { "padding", Origin_Padding }
317 static const QCssKnownValue repeats[NumKnownRepeats - 1] = {
318 { "no-repeat", Repeat_None },
319 { "repeat-x", Repeat_X },
320 { "repeat-xy", Repeat_XY },
321 { "repeat-y", Repeat_Y }
324 static const QCssKnownValue tileModes[NumKnownTileModes - 1] = {
325 { "repeat", TileMode_Repeat },
326 { "round", TileMode_Round },
327 { "stretch", TileMode_Stretch },
330 static const QCssKnownValue positions[NumKnownPositionModes - 1] = {
331 { "absolute", PositionMode_Absolute },
332 { "fixed", PositionMode_Fixed },
333 { "relative", PositionMode_Relative },
334 { "static", PositionMode_Static }
337 static const QCssKnownValue attachments[NumKnownAttachments - 1] = {
338 { "fixed", Attachment_Fixed },
339 { "scroll", Attachment_Scroll }
342 static const QCssKnownValue styleFeatures[NumKnownStyleFeatures - 1] = {
343 { "background-color", StyleFeature_BackgroundColor },
344 { "background-gradient", StyleFeature_BackgroundGradient },
345 { "none", StyleFeature_None }
348 Q_STATIC_GLOBAL_OPERATOR bool operator<(const QString &name, const QCssKnownValue &prop)
350 return QString::compare(name, QLatin1String(prop.name), Qt::CaseInsensitive) < 0;
353 Q_STATIC_GLOBAL_OPERATOR bool operator<(const QCssKnownValue &prop, const QString &name)
355 return QString::compare(QLatin1String(prop.name), name, Qt::CaseInsensitive) < 0;
358 static quint64 findKnownValue(const QString &name, const QCssKnownValue *start, int numValues)
360 const QCssKnownValue *end = &start[numValues - 1];
361 const QCssKnownValue *prop = qBinaryFind(start, end, name);
367 ///////////////////////////////////////////////////////////////////////////////
369 ValueExtractor::ValueExtractor(const QVector<Declaration> &decls, const QPalette &pal)
370 : declarations(decls), adjustment(0), fontExtracted(false), pal(pal)
374 LengthData ValueExtractor::lengthValue(const Value& v)
376 QString s = v.variant.toString();
377 s.reserve(s.length());
379 data.unit = LengthData::None;
380 if (s.endsWith(QLatin1String("px"), Qt::CaseInsensitive))
381 data.unit = LengthData::Px;
382 else if (s.endsWith(QLatin1String("ex"), Qt::CaseInsensitive))
383 data.unit = LengthData::Ex;
384 else if (s.endsWith(QLatin1String("em"), Qt::CaseInsensitive))
385 data.unit = LengthData::Em;
387 if (data.unit != LengthData::None)
390 data.number = s.toDouble();
394 static int lengthValueFromData(const LengthData& data, const QFont& f)
396 if (data.unit == LengthData::Ex)
397 return qRound(QFontMetrics(f).xHeight() * data.number);
398 else if (data.unit == LengthData::Em)
399 return qRound(QFontMetrics(f).height() * data.number);
400 return qRound(data.number);
403 int ValueExtractor::lengthValue(const Declaration &decl)
405 if (decl.d->parsed.isValid())
406 return lengthValueFromData(qvariant_cast<LengthData>(decl.d->parsed), f);
407 if (decl.d->values.count() < 1)
409 LengthData data = lengthValue(decl.d->values.at(0));
410 decl.d->parsed = QVariant::fromValue<LengthData>(data);
411 return lengthValueFromData(data,f);
414 void ValueExtractor::lengthValues(const Declaration &decl, int *m)
416 if (decl.d->parsed.isValid()) {
417 QList<QVariant> v = decl.d->parsed.toList();
418 for (int i = 0; i < 4; i++)
419 m[i] = lengthValueFromData(qvariant_cast<LengthData>(v.at(i)), f);
425 for (i = 0; i < qMin(decl.d->values.count(), 4); i++)
426 datas[i] = lengthValue(decl.d->values[i]);
429 LengthData zero = {0.0, LengthData::None};
430 datas[0] = datas[1] = datas[2] = datas[3] = zero;
432 datas[3] = datas[2] = datas[1] = datas[0];
441 for (i = 0; i < 4; i++) {
442 v += QVariant::fromValue<LengthData>(datas[i]);
443 m[i] = lengthValueFromData(datas[i], f);
448 bool ValueExtractor::extractGeometry(int *w, int *h, int *minw, int *minh, int *maxw, int *maxh)
452 for (int i = 0; i < declarations.count(); i++) {
453 const Declaration &decl = declarations.at(i);
454 switch (decl.d->propertyId) {
455 case Width: *w = lengthValue(decl); break;
456 case Height: *h = lengthValue(decl); break;
457 case MinimumWidth: *minw = lengthValue(decl); break;
458 case MinimumHeight: *minh = lengthValue(decl); break;
459 case MaximumWidth: *maxw = lengthValue(decl); break;
460 case MaximumHeight: *maxh = lengthValue(decl); break;
469 bool ValueExtractor::extractPosition(int *left, int *top, int *right, int *bottom, QCss::Origin *origin,
470 Qt::Alignment *position, QCss::PositionMode *mode, Qt::Alignment *textAlignment)
474 for (int i = 0; i < declarations.count(); i++) {
475 const Declaration &decl = declarations.at(i);
476 switch (decl.d->propertyId) {
477 case Left: *left = lengthValue(decl); break;
478 case Top: *top = lengthValue(decl); break;
479 case Right: *right = lengthValue(decl); break;
480 case Bottom: *bottom = lengthValue(decl); break;
481 case QtOrigin: *origin = decl.originValue(); break;
482 case QtPosition: *position = decl.alignmentValue(); break;
483 case TextAlignment: *textAlignment = decl.alignmentValue(); break;
484 case Position: *mode = decl.positionValue(); break;
493 bool ValueExtractor::extractBox(int *margins, int *paddings, int *spacing)
497 for (int i = 0; i < declarations.count(); i++) {
498 const Declaration &decl = declarations.at(i);
499 switch (decl.d->propertyId) {
500 case PaddingLeft: paddings[LeftEdge] = lengthValue(decl); break;
501 case PaddingRight: paddings[RightEdge] = lengthValue(decl); break;
502 case PaddingTop: paddings[TopEdge] = lengthValue(decl); break;
503 case PaddingBottom: paddings[BottomEdge] = lengthValue(decl); break;
504 case Padding: lengthValues(decl, paddings); break;
506 case MarginLeft: margins[LeftEdge] = lengthValue(decl); break;
507 case MarginRight: margins[RightEdge] = lengthValue(decl); break;
508 case MarginTop: margins[TopEdge] = lengthValue(decl); break;
509 case MarginBottom: margins[BottomEdge] = lengthValue(decl); break;
510 case Margin: lengthValues(decl, margins); break;
511 case QtSpacing: if (spacing) *spacing = lengthValue(decl); break;
521 int ValueExtractor::extractStyleFeatures()
523 int features = StyleFeature_None;
524 for (int i = 0; i < declarations.count(); i++) {
525 const Declaration &decl = declarations.at(i);
526 if (decl.d->propertyId == QtStyleFeatures)
527 features = decl.styleFeaturesValue();
532 QSize ValueExtractor::sizeValue(const Declaration &decl)
534 if (decl.d->parsed.isValid()) {
535 QList<QVariant> v = decl.d->parsed.toList();
536 return QSize(lengthValueFromData(qvariant_cast<LengthData>(v.at(0)), f),
537 lengthValueFromData(qvariant_cast<LengthData>(v.at(1)), f));
540 LengthData x[2] = { {0, LengthData::None }, {0, LengthData::None} };
541 if (decl.d->values.count() > 0)
542 x[0] = lengthValue(decl.d->values.at(0));
543 if (decl.d->values.count() > 1)
544 x[1] = lengthValue(decl.d->values.at(1));
548 v << QVariant::fromValue<LengthData>(x[0]) << QVariant::fromValue<LengthData>(x[1]);
550 return QSize(lengthValueFromData(x[0], f), lengthValueFromData(x[1], f));
553 void ValueExtractor::sizeValues(const Declaration &decl, QSize *radii)
555 radii[0] = sizeValue(decl);
556 for (int i = 1; i < 4; i++)
560 bool ValueExtractor::extractBorder(int *borders, QBrush *colors, BorderStyle *styles,
565 for (int i = 0; i < declarations.count(); i++) {
566 const Declaration &decl = declarations.at(i);
567 switch (decl.d->propertyId) {
568 case BorderLeftWidth: borders[LeftEdge] = lengthValue(decl); break;
569 case BorderRightWidth: borders[RightEdge] = lengthValue(decl); break;
570 case BorderTopWidth: borders[TopEdge] = lengthValue(decl); break;
571 case BorderBottomWidth: borders[BottomEdge] = lengthValue(decl); break;
572 case BorderWidth: lengthValues(decl, borders); break;
574 case BorderLeftColor: colors[LeftEdge] = decl.brushValue(pal); break;
575 case BorderRightColor: colors[RightEdge] = decl.brushValue(pal); break;
576 case BorderTopColor: colors[TopEdge] = decl.brushValue(pal); break;
577 case BorderBottomColor: colors[BottomEdge] = decl.brushValue(pal); break;
578 case BorderColor: decl.brushValues(colors, pal); break;
580 case BorderTopStyle: styles[TopEdge] = decl.styleValue(); break;
581 case BorderBottomStyle: styles[BottomEdge] = decl.styleValue(); break;
582 case BorderLeftStyle: styles[LeftEdge] = decl.styleValue(); break;
583 case BorderRightStyle: styles[RightEdge] = decl.styleValue(); break;
584 case BorderStyles: decl.styleValues(styles); break;
586 case BorderTopLeftRadius: radii[0] = sizeValue(decl); break;
587 case BorderTopRightRadius: radii[1] = sizeValue(decl); break;
588 case BorderBottomLeftRadius: radii[2] = sizeValue(decl); break;
589 case BorderBottomRightRadius: radii[3] = sizeValue(decl); break;
590 case BorderRadius: sizeValues(decl, radii); break;
593 borderValue(decl, &borders[LeftEdge], &styles[LeftEdge], &colors[LeftEdge]);
596 borderValue(decl, &borders[TopEdge], &styles[TopEdge], &colors[TopEdge]);
599 borderValue(decl, &borders[RightEdge], &styles[RightEdge], &colors[RightEdge]);
602 borderValue(decl, &borders[BottomEdge], &styles[BottomEdge], &colors[BottomEdge]);
605 borderValue(decl, &borders[LeftEdge], &styles[LeftEdge], &colors[LeftEdge]);
606 borders[TopEdge] = borders[RightEdge] = borders[BottomEdge] = borders[LeftEdge];
607 styles[TopEdge] = styles[RightEdge] = styles[BottomEdge] = styles[LeftEdge];
608 colors[TopEdge] = colors[RightEdge] = colors[BottomEdge] = colors[LeftEdge];
619 bool ValueExtractor::extractOutline(int *borders, QBrush *colors, BorderStyle *styles,
620 QSize *radii, int *offsets)
624 for (int i = 0; i < declarations.count(); i++) {
625 const Declaration &decl = declarations.at(i);
626 switch (decl.d->propertyId) {
627 case OutlineWidth: lengthValues(decl, borders); break;
628 case OutlineColor: decl.brushValues(colors, pal); break;
629 case OutlineStyle: decl.styleValues(styles); break;
631 case OutlineTopLeftRadius: radii[0] = sizeValue(decl); break;
632 case OutlineTopRightRadius: radii[1] = sizeValue(decl); break;
633 case OutlineBottomLeftRadius: radii[2] = sizeValue(decl); break;
634 case OutlineBottomRightRadius: radii[3] = sizeValue(decl); break;
635 case OutlineRadius: sizeValues(decl, radii); break;
636 case OutlineOffset: lengthValues(decl, offsets); break;
639 borderValue(decl, &borders[LeftEdge], &styles[LeftEdge], &colors[LeftEdge]);
640 borders[TopEdge] = borders[RightEdge] = borders[BottomEdge] = borders[LeftEdge];
641 styles[TopEdge] = styles[RightEdge] = styles[BottomEdge] = styles[LeftEdge];
642 colors[TopEdge] = colors[RightEdge] = colors[BottomEdge] = colors[LeftEdge];
653 static Qt::Alignment parseAlignment(const QCss::Value *values, int count)
655 Qt::Alignment a[2] = { 0, 0 };
656 for (int i = 0; i < qMin(2, count); i++) {
657 if (values[i].type != Value::KnownIdentifier)
659 switch (values[i].variant.toInt()) {
660 case Value_Left: a[i] = Qt::AlignLeft; break;
661 case Value_Right: a[i] = Qt::AlignRight; break;
662 case Value_Top: a[i] = Qt::AlignTop; break;
663 case Value_Bottom: a[i] = Qt::AlignBottom; break;
664 case Value_Center: a[i] = Qt::AlignCenter; break;
669 if (a[0] == Qt::AlignCenter && a[1] != 0 && a[1] != Qt::AlignCenter)
670 a[0] = (a[1] == Qt::AlignLeft || a[1] == Qt::AlignRight) ? Qt::AlignVCenter : Qt::AlignHCenter;
671 if ((a[1] == 0 || a[1] == Qt::AlignCenter) && a[0] != Qt::AlignCenter)
672 a[1] = (a[0] == Qt::AlignLeft || a[0] == Qt::AlignRight) ? Qt::AlignVCenter : Qt::AlignHCenter;
676 static ColorData parseColorValue(QCss::Value v)
678 if (v.type == Value::Identifier || v.type == Value::String) {
679 v.variant.convert(QVariant::Color);
680 v.type = Value::Color;
683 if (v.type == Value::Color)
684 return qvariant_cast<QColor>(v.variant);
686 if (v.type == Value::KnownIdentifier && v.variant.toInt() == Value_Transparent)
687 return QColor(Qt::transparent);
689 if (v.type != Value::Function)
692 QStringList lst = v.variant.toStringList();
693 if (lst.count() != 2)
696 if ((lst.at(0).compare(QLatin1String("palette"), Qt::CaseInsensitive)) == 0) {
697 int role = findKnownValue(lst.at(1).trimmed(), values, NumKnownValues);
698 if (role >= Value_FirstColorRole && role <= Value_LastColorRole)
699 return (QPalette::ColorRole)(role-Value_FirstColorRole);
704 bool rgb = lst.at(0).startsWith(QLatin1String("rgb"));
710 QVector<QCss::Value> colorDigits;
711 if (!p.parseExpr(&colorDigits))
714 for (int i = 0; i < qMin(colorDigits.count(), 7); i += 2) {
715 if (colorDigits.at(i).type == Value::Percentage) {
716 colorDigits[i].variant = colorDigits.at(i).variant.toReal() * (255. / 100.);
717 colorDigits[i].type = Value::Number;
718 } else if (colorDigits.at(i).type != Value::Number) {
723 int v1 = colorDigits.at(0).variant.toInt();
724 int v2 = colorDigits.at(2).variant.toInt();
725 int v3 = colorDigits.at(4).variant.toInt();
726 int alpha = colorDigits.count() >= 7 ? colorDigits.at(6).variant.toInt() : 255;
728 return rgb ? QColor::fromRgb(v1, v2, v3, alpha)
729 : QColor::fromHsv(v1, v2, v3, alpha);
732 static QColor colorFromData(const ColorData& c, const QPalette &pal)
734 if (c.type == ColorData::Color) {
736 } else if (c.type == ColorData::Role) {
737 return pal.color(c.role);
742 static BrushData parseBrushValue(const QCss::Value &v, const QPalette &pal)
744 ColorData c = parseColorValue(v);
745 if (c.type == ColorData::Color) {
746 return QBrush(c.color);
747 } else if (c.type == ColorData::Role) {
751 if (v.type != Value::Function)
754 QStringList lst = v.variant.toStringList();
755 if (lst.count() != 2)
758 QStringList gradFuncs;
759 gradFuncs << QLatin1String("qlineargradient") << QLatin1String("qradialgradient") << QLatin1String("qconicalgradient") << QLatin1String("qgradient");
762 if ((gradType = gradFuncs.indexOf(lst.at(0).toLower())) == -1)
765 QHash<QString, qreal> vars;
766 QVector<QGradientStop> stops;
770 spreads << QLatin1String("pad") << QLatin1String("reflect") << QLatin1String("repeat");
772 bool dependsOnThePalette = false;
773 Parser parser(lst.at(1));
774 while (parser.hasNext()) {
776 if (!parser.test(IDENT))
778 QString attr = parser.lexem();
780 if (!parser.test(COLON))
783 if (attr.compare(QLatin1String("stop"), Qt::CaseInsensitive) == 0) {
784 QCss::Value stop, color;
786 if (!parser.parseTerm(&stop)) return BrushData();
789 if (!parser.parseTerm(&color)) return BrushData();
790 ColorData cd = parseColorValue(color);
791 if(cd.type == ColorData::Role)
792 dependsOnThePalette = true;
793 stops.append(QGradientStop(stop.variant.toReal(), colorFromData(cd, pal)));
797 (void)parser.parseTerm(&value);
798 if (attr.compare(QLatin1String("spread"), Qt::CaseInsensitive) == 0) {
799 spread = spreads.indexOf(value.variant.toString());
801 vars[attr] = value.variant.toReal();
805 (void)parser.test(COMMA);
809 QLinearGradient lg(vars.value(QLatin1String("x1")), vars.value(QLatin1String("y1")),
810 vars.value(QLatin1String("x2")), vars.value(QLatin1String("y2")));
811 lg.setCoordinateMode(QGradient::ObjectBoundingMode);
814 lg.setSpread(QGradient::Spread(spread));
815 BrushData bd = QBrush(lg);
816 if (dependsOnThePalette)
817 bd.type = BrushData::DependsOnThePalette;
822 QRadialGradient rg(vars.value(QLatin1String("cx")), vars.value(QLatin1String("cy")),
823 vars.value(QLatin1String("radius")), vars.value(QLatin1String("fx")),
824 vars.value(QLatin1String("fy")));
825 rg.setCoordinateMode(QGradient::ObjectBoundingMode);
828 rg.setSpread(QGradient::Spread(spread));
829 BrushData bd = QBrush(rg);
830 if (dependsOnThePalette)
831 bd.type = BrushData::DependsOnThePalette;
836 QConicalGradient cg(vars.value(QLatin1String("cx")), vars.value(QLatin1String("cy")),
837 vars.value(QLatin1String("angle")));
838 cg.setCoordinateMode(QGradient::ObjectBoundingMode);
841 cg.setSpread(QGradient::Spread(spread));
842 BrushData bd = QBrush(cg);
843 if (dependsOnThePalette)
844 bd.type = BrushData::DependsOnThePalette;
851 static QBrush brushFromData(const BrushData& c, const QPalette &pal)
853 if (c.type == BrushData::Role) {
854 return pal.color(c.role);
860 static BorderStyle parseStyleValue(QCss::Value v)
862 if (v.type == Value::KnownIdentifier) {
863 switch (v.variant.toInt()) {
865 return BorderStyle_None;
867 return BorderStyle_Dotted;
869 return BorderStyle_Dashed;
871 return BorderStyle_Solid;
873 return BorderStyle_Double;
875 return BorderStyle_DotDash;
876 case Value_DotDotDash:
877 return BorderStyle_DotDotDash;
879 return BorderStyle_Groove;
881 return BorderStyle_Ridge;
883 return BorderStyle_Inset;
885 return BorderStyle_Outset;
887 return BorderStyle_Native;
893 return BorderStyle_Unknown;
896 void ValueExtractor::borderValue(const Declaration &decl, int *width, QCss::BorderStyle *style, QBrush *color)
898 if (decl.d->parsed.isValid()) {
899 BorderData data = qvariant_cast<BorderData>(decl.d->parsed);
900 *width = lengthValueFromData(data.width, f);
902 *color = data.color.type != BrushData::Invalid ? brushFromData(data.color, pal) : QBrush(QColor());
907 *style = BorderStyle_None;
910 if (decl.d->values.isEmpty())
914 data.width.number = 0;
915 data.width.unit = LengthData::None;
916 data.style = BorderStyle_None;
919 if (decl.d->values.at(i).type == Value::Length || decl.d->values.at(i).type == Value::Number) {
920 data.width = lengthValue(decl.d->values.at(i));
921 *width = lengthValueFromData(data.width, f);
922 if (++i >= decl.d->values.count()) {
923 decl.d->parsed = QVariant::fromValue<BorderData>(data);
928 data.style = parseStyleValue(decl.d->values.at(i));
929 if (data.style != BorderStyle_Unknown) {
931 if (++i >= decl.d->values.count()) {
932 decl.d->parsed = QVariant::fromValue<BorderData>(data);
936 data.style = BorderStyle_None;
939 data.color = parseBrushValue(decl.d->values.at(i), pal);
940 *color = brushFromData(data.color, pal);
941 if (data.color.type != BrushData::DependsOnThePalette)
942 decl.d->parsed = QVariant::fromValue<BorderData>(data);
945 static void parseShorthandBackgroundProperty(const QVector<QCss::Value> &values, BrushData *brush, QString *image, Repeat *repeat, Qt::Alignment *alignment, const QPalette &pal)
947 *brush = BrushData();
950 *alignment = Qt::AlignTop | Qt::AlignLeft;
952 for (int i = 0; i < values.count(); ++i) {
953 const QCss::Value &v = values.at(i);
954 if (v.type == Value::Uri) {
955 *image = v.variant.toString();
957 } else if (v.type == Value::KnownIdentifier && v.variant.toInt() == Value_None) {
960 } else if (v.type == Value::KnownIdentifier && v.variant.toInt() == Value_Transparent) {
961 *brush = QBrush(Qt::transparent);
964 Repeat repeatAttempt = static_cast<Repeat>(findKnownValue(v.variant.toString(),
965 repeats, NumKnownRepeats));
966 if (repeatAttempt != Repeat_Unknown) {
967 *repeat = repeatAttempt;
971 if (v.type == Value::KnownIdentifier) {
974 if (i < values.count() - 1
975 && values.at(i + 1).type == Value::KnownIdentifier) {
979 Qt::Alignment a = parseAlignment(values.constData() + start, count);
987 *brush = parseBrushValue(v, pal);
991 bool ValueExtractor::extractBackground(QBrush *brush, QString *image, Repeat *repeat,
992 Qt::Alignment *alignment, Origin *origin, Attachment *attachment,
996 for (int i = 0; i < declarations.count(); ++i) {
997 const Declaration &decl = declarations.at(i);
998 if (decl.d->values.isEmpty())
1000 const QCss::Value &val = decl.d->values.at(0);
1001 switch (decl.d->propertyId) {
1002 case BackgroundColor:
1003 *brush = decl.brushValue();
1005 case BackgroundImage:
1006 if (val.type == Value::Uri)
1007 *image = val.variant.toString();
1009 case BackgroundRepeat:
1010 if (decl.d->parsed.isValid()) {
1011 *repeat = static_cast<Repeat>(decl.d->parsed.toInt());
1013 *repeat = static_cast<Repeat>(findKnownValue(val.variant.toString(),
1014 repeats, NumKnownRepeats));
1015 decl.d->parsed = *repeat;
1018 case BackgroundPosition:
1019 *alignment = decl.alignmentValue();
1021 case BackgroundOrigin:
1022 *origin = decl.originValue();
1024 case BackgroundClip:
1025 *clip = decl.originValue();
1028 if (decl.d->parsed.isValid()) {
1029 BackgroundData data = qvariant_cast<BackgroundData>(decl.d->parsed);
1030 *brush = brushFromData(data.brush, pal);
1031 *image = data.image;
1032 *repeat = data.repeat;
1033 *alignment = data.alignment;
1035 BrushData brushData;
1036 parseShorthandBackgroundProperty(decl.d->values, &brushData, image, repeat, alignment, pal);
1037 *brush = brushFromData(brushData, pal);
1038 if (brushData.type != BrushData::DependsOnThePalette) {
1039 BackgroundData data = { brushData, *image, *repeat, *alignment };
1040 decl.d->parsed = QVariant::fromValue<BackgroundData>(data);
1044 case BackgroundAttachment:
1045 *attachment = decl.attachmentValue();
1054 static bool setFontSizeFromValue(QCss::Value value, QFont *font, int *fontSizeAdjustment)
1056 if (value.type == Value::KnownIdentifier) {
1058 switch (value.variant.toInt()) {
1059 case Value_Small: *fontSizeAdjustment = -1; break;
1060 case Value_Medium: *fontSizeAdjustment = 0; break;
1061 case Value_Large: *fontSizeAdjustment = 1; break;
1062 case Value_XLarge: *fontSizeAdjustment = 2; break;
1063 case Value_XXLarge: *fontSizeAdjustment = 3; break;
1064 default: valid = false; break;
1068 if (value.type != Value::Length)
1072 QString s = value.variant.toString();
1073 if (s.endsWith(QLatin1String("pt"), Qt::CaseInsensitive)) {
1076 if (value.variant.convert((QVariant::Type)qMetaTypeId<qreal>())) {
1077 font->setPointSizeF(value.variant.toReal());
1080 } else if (s.endsWith(QLatin1String("px"), Qt::CaseInsensitive)) {
1083 if (value.variant.convert(QVariant::Int)) {
1084 font->setPixelSize(value.variant.toInt());
1091 static bool setFontStyleFromValue(const QCss::Value &value, QFont *font)
1093 if (value.type != Value::KnownIdentifier)
1095 switch (value.variant.toInt()) {
1096 case Value_Normal: font->setStyle(QFont::StyleNormal); return true;
1097 case Value_Italic: font->setStyle(QFont::StyleItalic); return true;
1098 case Value_Oblique: font->setStyle(QFont::StyleOblique); return true;
1104 static bool setFontWeightFromValue(const QCss::Value &value, QFont *font)
1106 if (value.type == Value::KnownIdentifier) {
1107 switch (value.variant.toInt()) {
1108 case Value_Normal: font->setWeight(QFont::Normal); return true;
1109 case Value_Bold: font->setWeight(QFont::Bold); return true;
1114 if (value.type != Value::Number)
1116 font->setWeight(qMin(value.variant.toInt() / 8, 99));
1121 * parse the font family from the values (starting from index \a start)
1122 * and set it the \a font
1123 * \returns true if a family was extracted.
1125 static bool setFontFamilyFromValues(const QVector<QCss::Value> &values, QFont *font, int start = 0)
1128 bool shouldAddSpace = false;
1129 for (int i = start; i < values.count(); ++i) {
1130 const QCss::Value &v = values.at(i);
1131 if (v.type == Value::TermOperatorComma) {
1132 family += QLatin1Char(',');
1133 shouldAddSpace = false;
1136 const QString str = v.variant.toString();
1140 family += QLatin1Char(' ');
1142 shouldAddSpace = true;
1144 if (family.isEmpty())
1146 font->setFamily(family);
1150 static void setTextDecorationFromValues(const QVector<QCss::Value> &values, QFont *font)
1152 for (int i = 0; i < values.count(); ++i) {
1153 if (values.at(i).type != Value::KnownIdentifier)
1155 switch (values.at(i).variant.toInt()) {
1156 case Value_Underline: font->setUnderline(true); break;
1157 case Value_Overline: font->setOverline(true); break;
1158 case Value_LineThrough: font->setStrikeOut(true); break;
1160 font->setUnderline(false);
1161 font->setOverline(false);
1162 font->setStrikeOut(false);
1169 static void parseShorthandFontProperty(const QVector<QCss::Value> &values, QFont *font, int *fontSizeAdjustment)
1171 font->setStyle(QFont::StyleNormal);
1172 font->setWeight(QFont::Normal);
1173 *fontSizeAdjustment = -255;
1176 while (i < values.count()) {
1177 if (setFontStyleFromValue(values.at(i), font)
1178 || setFontWeightFromValue(values.at(i), font))
1184 if (i < values.count()) {
1185 setFontSizeFromValue(values.at(i), font, fontSizeAdjustment);
1189 if (i < values.count()) {
1190 setFontFamilyFromValues(values, font, i);
1194 static void setFontVariantFromValue(const QCss::Value &value, QFont *font)
1196 if (value.type == Value::KnownIdentifier) {
1197 switch (value.variant.toInt()) {
1198 case Value_Normal: font->setCapitalization(QFont::MixedCase); break;
1199 case Value_SmallCaps: font->setCapitalization(QFont::SmallCaps); break;
1205 static void setTextTransformFromValue(const QCss::Value &value, QFont *font)
1207 if (value.type == Value::KnownIdentifier) {
1208 switch (value.variant.toInt()) {
1209 case Value_None: font->setCapitalization(QFont::MixedCase); break;
1210 case Value_Uppercase: font->setCapitalization(QFont::AllUppercase); break;
1211 case Value_Lowercase: font->setCapitalization(QFont::AllLowercase); break;
1217 bool ValueExtractor::extractFont(QFont *font, int *fontSizeAdjustment)
1219 if (fontExtracted) {
1221 *fontSizeAdjustment = adjustment;
1222 return fontExtracted == 1;
1226 for (int i = 0; i < declarations.count(); ++i) {
1227 const Declaration &decl = declarations.at(i);
1228 if (decl.d->values.isEmpty())
1230 const QCss::Value &val = decl.d->values.at(0);
1231 switch (decl.d->propertyId) {
1232 case FontSize: setFontSizeFromValue(val, font, fontSizeAdjustment); break;
1233 case FontStyle: setFontStyleFromValue(val, font); break;
1234 case FontWeight: setFontWeightFromValue(val, font); break;
1235 case FontFamily: setFontFamilyFromValues(decl.d->values, font); break;
1236 case TextDecoration: setTextDecorationFromValues(decl.d->values, font); break;
1237 case Font: parseShorthandFontProperty(decl.d->values, font, fontSizeAdjustment); break;
1238 case FontVariant: setFontVariantFromValue(val, font); break;
1239 case TextTransform: setTextTransformFromValue(val, font); break;
1246 adjustment = *fontSizeAdjustment;
1247 fontExtracted = hit ? 1 : 2;
1251 bool ValueExtractor::extractPalette(QBrush *fg, QBrush *sfg, QBrush *sbg, QBrush *abg)
1254 for (int i = 0; i < declarations.count(); ++i) {
1255 const Declaration &decl = declarations.at(i);
1256 switch (decl.d->propertyId) {
1257 case Color: *fg = decl.brushValue(pal); break;
1258 case QtSelectionForeground: *sfg = decl.brushValue(pal); break;
1259 case QtSelectionBackground: *sbg = decl.brushValue(pal); break;
1260 case QtAlternateBackground: *abg = decl.brushValue(pal); break;
1268 void ValueExtractor::extractFont()
1273 extractFont(&f, &dummy);
1276 bool ValueExtractor::extractImage(QIcon *icon, Qt::Alignment *a, QSize *size)
1279 for (int i = 0; i < declarations.count(); ++i) {
1280 const Declaration &decl = declarations.at(i);
1281 switch (decl.d->propertyId) {
1283 *icon = decl.iconValue();
1284 if (decl.d->values.count() > 0 && decl.d->values.at(0).type == Value::Uri) {
1285 // try to pull just the size from the image...
1286 QImageReader imageReader(decl.d->values.at(0).variant.toString());
1287 if ((*size = imageReader.size()).isNull()) {
1288 // but we'll have to load the whole image if the
1289 // format doesn't support just reading the size
1290 *size = imageReader.read().size();
1294 case QtImageAlignment: *a = decl.alignmentValue(); break;
1302 ///////////////////////////////////////////////////////////////////////////////
1304 QColor Declaration::colorValue(const QPalette &pal) const
1306 if (d->values.count() != 1)
1309 if (d->parsed.isValid()) {
1310 if (d->parsed.type() == QVariant::Color)
1311 return qvariant_cast<QColor>(d->parsed);
1312 if (d->parsed.type() == QVariant::Int)
1313 return pal.color((QPalette::ColorRole)(d->parsed.toInt()));
1316 ColorData color = parseColorValue(d->values.at(0));
1317 if(color.type == ColorData::Role) {
1318 d->parsed = QVariant::fromValue<int>(color.role);
1319 return pal.color((QPalette::ColorRole)(color.role));
1321 d->parsed = QVariant::fromValue<QColor>(color.color);
1326 QBrush Declaration::brushValue(const QPalette &pal) const
1328 if (d->values.count() != 1)
1331 if (d->parsed.isValid()) {
1332 if (d->parsed.type() == QVariant::Brush)
1333 return qvariant_cast<QBrush>(d->parsed);
1334 if (d->parsed.type() == QVariant::Int)
1335 return pal.color((QPalette::ColorRole)(d->parsed.toInt()));
1338 BrushData data = parseBrushValue(d->values.at(0), pal);
1340 if(data.type == BrushData::Role) {
1341 d->parsed = QVariant::fromValue<int>(data.role);
1342 return pal.color((QPalette::ColorRole)(data.role));
1344 if (data.type != BrushData::DependsOnThePalette)
1345 d->parsed = QVariant::fromValue<QBrush>(data.brush);
1350 void Declaration::brushValues(QBrush *c, const QPalette &pal) const
1352 int needParse = 0x1f; // bits 0..3 say if we should parse the corresponding value.
1353 // the bit 4 say we need to update d->parsed
1355 if (d->parsed.isValid()) {
1357 QList<QVariant> v = d->parsed.toList();
1358 for (i = 0; i < qMin(v.count(), 4); i++) {
1359 if (v.at(i).type() == QVariant::Brush) {
1360 c[i] = qvariant_cast<QBrush>(v.at(i));
1361 } else if (v.at(i).type() == QVariant::Int) {
1362 c[i] = pal.color((QPalette::ColorRole)(v.at(i).toInt()));
1364 needParse |= (1<<i);
1368 if (needParse != 0) {
1370 for (i = 0; i < qMin(d->values.count(), 4); i++) {
1371 if (!(needParse & (1<<i)))
1373 BrushData data = parseBrushValue(d->values.at(i), pal);
1374 if(data.type == BrushData::Role) {
1375 v += QVariant::fromValue<int>(data.role);
1376 c[i] = pal.color((QPalette::ColorRole)(data.role));
1378 if (data.type != BrushData::DependsOnThePalette) {
1379 v += QVariant::fromValue<QBrush>(data.brush);
1386 if (needParse & 0x10)
1389 if (i == 0) c[0] = c[1] = c[2] = c[3] = QBrush();
1390 else if (i == 1) c[3] = c[2] = c[1] = c[0];
1391 else if (i == 2) c[2] = c[0], c[3] = c[1];
1392 else if (i == 3) c[3] = c[1];
1395 bool Declaration::realValue(qreal *real, const char *unit) const
1397 if (d->values.count() != 1)
1399 const Value &v = d->values.at(0);
1400 if (unit && v.type != Value::Length)
1402 QString s = v.variant.toString();
1404 if (!s.endsWith(QLatin1String(unit), Qt::CaseInsensitive))
1406 s.chop(qstrlen(unit));
1409 qreal val = s.toDouble(&ok);
1415 static bool intValueHelper(const QCss::Value &v, int *i, const char *unit)
1417 if (unit && v.type != Value::Length)
1419 QString s = v.variant.toString();
1421 if (!s.endsWith(QLatin1String(unit), Qt::CaseInsensitive))
1423 s.chop(qstrlen(unit));
1426 int val = s.toInt(&ok);
1432 bool Declaration::intValue(int *i, const char *unit) const
1434 if (d->values.count() != 1)
1436 return intValueHelper(d->values.at(0), i, unit);
1439 QSize Declaration::sizeValue() const
1441 if (d->parsed.isValid())
1442 return qvariant_cast<QSize>(d->parsed);
1444 int x[2] = { 0, 0 };
1445 if (d->values.count() > 0)
1446 intValueHelper(d->values.at(0), &x[0], "px");
1447 if (d->values.count() > 1)
1448 intValueHelper(d->values.at(1), &x[1], "px");
1451 QSize size(x[0], x[1]);
1452 d->parsed = QVariant::fromValue<QSize>(size);
1456 QRect Declaration::rectValue() const
1458 if (d->values.count() != 1)
1461 if (d->parsed.isValid())
1462 return qvariant_cast<QRect>(d->parsed);
1464 const QCss::Value &v = d->values.at(0);
1465 if (v.type != Value::Function)
1467 QStringList func = v.variant.toStringList();
1468 if (func.count() != 2 || func.at(0).compare(QLatin1String("rect")) != 0)
1470 QStringList args = func[1].split(QLatin1Char(' '), QString::SkipEmptyParts);
1471 if (args.count() != 4)
1473 QRect rect(args[0].toInt(), args[1].toInt(), args[2].toInt(), args[3].toInt());
1474 d->parsed = QVariant::fromValue<QRect>(rect);
1478 void Declaration::colorValues(QColor *c, const QPalette &pal) const
1481 if (d->parsed.isValid()) {
1482 QList<QVariant> v = d->parsed.toList();
1483 for (i = 0; i < qMin(d->values.count(), 4); i++) {
1484 if (v.at(i).type() == QVariant::Color) {
1485 c[i] = qvariant_cast<QColor>(v.at(i));
1487 c[i] = pal.color((QPalette::ColorRole)(v.at(i).toInt()));
1492 for (i = 0; i < qMin(d->values.count(), 4); i++) {
1493 ColorData color = parseColorValue(d->values.at(i));
1494 if(color.type == ColorData::Role) {
1495 v += QVariant::fromValue<int>(color.role);
1496 c[i] = pal.color((QPalette::ColorRole)(color.role));
1498 v += QVariant::fromValue<QColor>(color.color);
1505 if (i == 0) c[0] = c[1] = c[2] = c[3] = QColor();
1506 else if (i == 1) c[3] = c[2] = c[1] = c[0];
1507 else if (i == 2) c[2] = c[0], c[3] = c[1];
1508 else if (i == 3) c[3] = c[1];
1511 BorderStyle Declaration::styleValue() const
1513 if (d->values.count() != 1)
1514 return BorderStyle_None;
1515 return parseStyleValue(d->values.at(0));
1518 void Declaration::styleValues(BorderStyle *s) const
1521 for (i = 0; i < qMin(d->values.count(), 4); i++)
1522 s[i] = parseStyleValue(d->values.at(i));
1523 if (i == 0) s[0] = s[1] = s[2] = s[3] = BorderStyle_None;
1524 else if (i == 1) s[3] = s[2] = s[1] = s[0];
1525 else if (i == 2) s[2] = s[0], s[3] = s[1];
1526 else if (i == 3) s[3] = s[1];
1529 Repeat Declaration::repeatValue() const
1531 if (d->parsed.isValid())
1532 return static_cast<Repeat>(d->parsed.toInt());
1533 if (d->values.count() != 1)
1534 return Repeat_Unknown;
1535 int v = findKnownValue(d->values.at(0).variant.toString(),
1536 repeats, NumKnownRepeats);
1538 return static_cast<Repeat>(v);
1541 Origin Declaration::originValue() const
1543 if (d->parsed.isValid())
1544 return static_cast<Origin>(d->parsed.toInt());
1545 if (d->values.count() != 1)
1546 return Origin_Unknown;
1547 int v = findKnownValue(d->values.at(0).variant.toString(),
1548 origins, NumKnownOrigins);
1550 return static_cast<Origin>(v);
1553 PositionMode Declaration::positionValue() const
1555 if (d->parsed.isValid())
1556 return static_cast<PositionMode>(d->parsed.toInt());
1557 if (d->values.count() != 1)
1558 return PositionMode_Unknown;
1559 int v = findKnownValue(d->values.at(0).variant.toString(),
1560 positions, NumKnownPositionModes);
1562 return static_cast<PositionMode>(v);
1565 Attachment Declaration::attachmentValue() const
1567 if (d->parsed.isValid())
1568 return static_cast<Attachment>(d->parsed.toInt());
1569 if (d->values.count() != 1)
1570 return Attachment_Unknown;
1571 int v = findKnownValue(d->values.at(0).variant.toString(),
1572 attachments, NumKnownAttachments);
1574 return static_cast<Attachment>(v);
1577 int Declaration::styleFeaturesValue() const
1579 Q_ASSERT(d->propertyId == QtStyleFeatures);
1580 if (d->parsed.isValid())
1581 return d->parsed.toInt();
1582 int features = StyleFeature_None;
1583 for (int i = 0; i < d->values.count(); i++) {
1584 features |= static_cast<int>(findKnownValue(d->values.value(i).variant.toString(),
1585 styleFeatures, NumKnownStyleFeatures));
1587 d->parsed = features;
1591 QString Declaration::uriValue() const
1593 if (d->values.isEmpty() || d->values.at(0).type != Value::Uri)
1595 return d->values.at(0).variant.toString();
1598 Qt::Alignment Declaration::alignmentValue() const
1600 if (d->parsed.isValid())
1601 return Qt::Alignment(d->parsed.toInt());
1602 if (d->values.isEmpty() || d->values.count() > 2)
1603 return Qt::AlignLeft | Qt::AlignTop;
1605 Qt::Alignment v = parseAlignment(d->values.constData(), d->values.count());
1610 void Declaration::borderImageValue(QString *image, int *cuts,
1611 TileMode *h, TileMode *v) const
1613 *image = uriValue();
1614 for (int i = 0; i < 4; i++)
1616 *h = *v = TileMode_Stretch;
1618 if (d->values.count() < 2)
1621 if (d->values.at(1).type == Value::Number) { // cuts!
1623 for (i = 0; i < qMin(d->values.count()-1, 4); i++) {
1624 const Value& v = d->values.at(i+1);
1625 if (v.type != Value::Number)
1627 cuts[i] = v.variant.toString().toInt();
1629 if (i == 0) cuts[0] = cuts[1] = cuts[2] = cuts[3] = 0;
1630 else if (i == 1) cuts[3] = cuts[2] = cuts[1] = cuts[0];
1631 else if (i == 2) cuts[2] = cuts[0], cuts[3] = cuts[1];
1632 else if (i == 3) cuts[3] = cuts[1];
1635 if (d->values.last().type == Value::Identifier) {
1636 *v = static_cast<TileMode>(findKnownValue(d->values.last().variant.toString(),
1637 tileModes, NumKnownTileModes));
1639 if (d->values[d->values.count() - 2].type == Value::Identifier) {
1640 *h = static_cast<TileMode>
1641 (findKnownValue(d->values[d->values.count()-2].variant.toString(),
1642 tileModes, NumKnownTileModes));
1647 QIcon Declaration::iconValue() const
1649 if (d->parsed.isValid())
1650 return qvariant_cast<QIcon>(d->parsed);
1653 for (int i = 0; i < d->values.count();) {
1654 const Value &value = d->values.at(i++);
1655 if (value.type != Value::Uri)
1657 QString uri = value.variant.toString();
1658 QIcon::Mode mode = QIcon::Normal;
1659 QIcon::State state = QIcon::Off;
1660 for (int j = 0; j < 2; j++) {
1661 if (i != d->values.count() && d->values.at(i).type == Value::KnownIdentifier) {
1662 switch (d->values.at(i).variant.toInt()) {
1663 case Value_Disabled: mode = QIcon::Disabled; break;
1664 case Value_Active: mode = QIcon::Active; break;
1665 case Value_Selected: mode = QIcon::Selected; break;
1666 case Value_Normal: mode = QIcon::Normal; break;
1667 case Value_On: state = QIcon::On; break;
1668 case Value_Off: state = QIcon::Off; break;
1677 // QIcon is soo broken
1681 icon.addPixmap(uri, mode, state);
1683 if (i == d->values.count())
1686 if (d->values.at(i).type == Value::TermOperatorComma)
1690 d->parsed = QVariant::fromValue<QIcon>(icon);
1694 ///////////////////////////////////////////////////////////////////////////////
1696 int Selector::specificity() const
1699 for (int i = 0; i < basicSelectors.count(); ++i) {
1700 const BasicSelector &sel = basicSelectors.at(i);
1701 if (!sel.elementName.isEmpty())
1704 val += (sel.pseudos.count() + sel.attributeSelectors.count()) * 0x10;
1705 val += sel.ids.count() * 0x100;
1710 QString Selector::pseudoElement() const
1712 const BasicSelector& bs = basicSelectors.last();
1713 if (!bs.pseudos.isEmpty() && bs.pseudos.at(0).type == PseudoClass_Unknown)
1714 return bs.pseudos.at(0).name;
1718 quint64 Selector::pseudoClass(quint64 *negated) const
1720 const BasicSelector& bs = basicSelectors.last();
1721 if (bs.pseudos.isEmpty())
1722 return PseudoClass_Unspecified;
1723 quint64 pc = PseudoClass_Unknown;
1724 for (int i = !pseudoElement().isEmpty(); i < bs.pseudos.count(); i++) {
1725 const Pseudo &pseudo = bs.pseudos.at(i);
1726 if (pseudo.type == PseudoClass_Unknown)
1727 return PseudoClass_Unknown;
1728 if (!pseudo.negated)
1731 *negated |= pseudo.type;
1736 ///////////////////////////////////////////////////////////////////////////////
1738 void StyleSheet::buildIndexes(Qt::CaseSensitivity nameCaseSensitivity)
1740 QVector<StyleRule> universals;
1741 for (int i = 0; i < styleRules.count(); ++i) {
1742 const StyleRule &rule = styleRules.at(i);
1743 QVector<Selector> universalsSelectors;
1744 for (int j = 0; j < rule.selectors.count(); ++j) {
1745 const Selector& selector = rule.selectors.at(j);
1747 if (selector.basicSelectors.isEmpty())
1750 if (selector.basicSelectors.at(0).relationToNext == BasicSelector::NoRelation) {
1751 if (selector.basicSelectors.count() != 1)
1753 } else if (selector.basicSelectors.count() <= 1) {
1757 const BasicSelector &sel = selector.basicSelectors.at(selector.basicSelectors.count() - 1);
1759 if (!sel.ids.isEmpty()) {
1761 nr.selectors += selector;
1762 nr.declarations = rule.declarations;
1764 idIndex.insert(sel.ids.at(0), nr);
1765 } else if (!sel.elementName.isEmpty()) {
1767 nr.selectors += selector;
1768 nr.declarations = rule.declarations;
1770 QString name = sel.elementName;
1771 if (nameCaseSensitivity == Qt::CaseInsensitive)
1772 name=name.toLower();
1773 nameIndex.insert(name, nr);
1775 universalsSelectors += selector;
1778 if (!universalsSelectors.isEmpty()) {
1780 nr.selectors = universalsSelectors;
1781 nr.declarations = rule.declarations;
1786 styleRules = universals;
1789 ///////////////////////////////////////////////////////////////////////////////
1791 StyleSelector::~StyleSelector()
1795 bool StyleSelector::nodeNameEquals(NodePtr node, const QString& nodeName) const
1797 return nodeNames(node).contains(nodeName, nameCaseSensitivity);
1800 QStringList StyleSelector::nodeIds(NodePtr node) const
1802 return QStringList(attribute(node, QLatin1String("id")));
1805 bool StyleSelector::selectorMatches(const Selector &selector, NodePtr node)
1807 if (selector.basicSelectors.isEmpty())
1810 if (selector.basicSelectors.at(0).relationToNext == BasicSelector::NoRelation) {
1811 if (selector.basicSelectors.count() != 1)
1813 return basicSelectorMatches(selector.basicSelectors.at(0), node);
1815 if (selector.basicSelectors.count() <= 1)
1818 int i = selector.basicSelectors.count() - 1;
1819 node = duplicateNode(node);
1822 BasicSelector sel = selector.basicSelectors.at(i);
1824 match = basicSelectorMatches(sel, node);
1826 if (sel.relationToNext == BasicSelector::MatchNextSelectorIfParent
1827 || i == selector.basicSelectors.count() - 1) // first element must always match!
1831 if (match || sel.relationToNext != BasicSelector::MatchNextSelectorIfAncestor)
1837 sel = selector.basicSelectors.at(i);
1838 if (sel.relationToNext == BasicSelector::MatchNextSelectorIfAncestor
1839 || sel.relationToNext == BasicSelector::MatchNextSelectorIfParent) {
1841 NodePtr nextParent = parentNode(node);
1844 } else if (sel.relationToNext == BasicSelector::MatchNextSelectorIfPreceeds) {
1845 NodePtr previousSibling = previousSiblingNode(node);
1847 node = previousSibling;
1849 if (isNullNode(node)) {
1853 } while (i >= 0 && (match || sel.relationToNext == BasicSelector::MatchNextSelectorIfAncestor));
1860 bool StyleSelector::basicSelectorMatches(const BasicSelector &sel, NodePtr node)
1862 if (!sel.attributeSelectors.isEmpty()) {
1863 if (!hasAttributes(node))
1866 for (int i = 0; i < sel.attributeSelectors.count(); ++i) {
1867 const QCss::AttributeSelector &a = sel.attributeSelectors.at(i);
1869 const QString attrValue = attribute(node, a.name);
1870 if (attrValue.isNull())
1873 if (a.valueMatchCriterium == QCss::AttributeSelector::MatchContains) {
1875 QStringList lst = attrValue.split(QLatin1Char(' '));
1876 if (!lst.contains(a.value))
1879 (a.valueMatchCriterium == QCss::AttributeSelector::MatchEqual
1880 && attrValue != a.value)
1882 (a.valueMatchCriterium == QCss::AttributeSelector::MatchBeginsWith
1883 && !attrValue.startsWith(a.value))
1889 if (!sel.elementName.isEmpty()
1890 && !nodeNameEquals(node, sel.elementName))
1893 if (!sel.ids.isEmpty()
1894 && sel.ids != nodeIds(node))
1900 void StyleSelector::matchRule(NodePtr node, const StyleRule &rule, StyleSheetOrigin origin,
1901 int depth, QMap<uint, StyleRule> *weightedRules)
1903 for (int j = 0; j < rule.selectors.count(); ++j) {
1904 const Selector& selector = rule.selectors.at(j);
1905 if (selectorMatches(selector, node)) {
1906 uint weight = rule.order
1907 + selector.specificity() *0x100
1908 + (uint(origin) + depth)*0x100000;
1909 StyleRule newRule = rule;
1910 if(rule.selectors.count() > 1) {
1911 newRule.selectors.resize(1);
1912 newRule.selectors[0] = selector;
1914 //We might have rules with the same weight if they came from a rule with several selectors
1915 weightedRules->insertMulti(weight, newRule);
1920 // Returns style rules that are in ascending order of specificity
1921 // Each of the StyleRule returned will contain exactly one Selector
1922 QVector<StyleRule> StyleSelector::styleRulesForNode(NodePtr node)
1924 QVector<StyleRule> rules;
1925 if (styleSheets.isEmpty())
1928 QMap<uint, StyleRule> weightedRules; // (spec, rule) that will be sorted below
1930 //prune using indexed stylesheet
1931 for (int sheetIdx = 0; sheetIdx < styleSheets.count(); ++sheetIdx) {
1932 const StyleSheet &styleSheet = styleSheets.at(sheetIdx);
1933 for (int i = 0; i < styleSheet.styleRules.count(); ++i) {
1934 matchRule(node, styleSheet.styleRules.at(i), styleSheet.origin, styleSheet.depth, &weightedRules);
1937 if (!styleSheet.idIndex.isEmpty()) {
1938 QStringList ids = nodeIds(node);
1939 for (int i = 0; i < ids.count(); i++) {
1940 const QString &key = ids.at(i);
1941 QMultiHash<QString, StyleRule>::const_iterator it = styleSheet.idIndex.constFind(key);
1942 while (it != styleSheet.idIndex.constEnd() && it.key() == key) {
1943 matchRule(node, it.value(), styleSheet.origin, styleSheet.depth, &weightedRules);
1948 if (!styleSheet.nameIndex.isEmpty()) {
1949 QStringList names = nodeNames(node);
1950 for (int i = 0; i < names.count(); i++) {
1951 QString name = names.at(i);
1952 if (nameCaseSensitivity == Qt::CaseInsensitive)
1953 name = name.toLower();
1954 QMultiHash<QString, StyleRule>::const_iterator it = styleSheet.nameIndex.constFind(name);
1955 while (it != styleSheet.nameIndex.constEnd() && it.key() == name) {
1956 matchRule(node, it.value(), styleSheet.origin, styleSheet.depth, &weightedRules);
1961 if (!medium.isEmpty()) {
1962 for (int i = 0; i < styleSheet.mediaRules.count(); ++i) {
1963 if (styleSheet.mediaRules.at(i).media.contains(medium, Qt::CaseInsensitive)) {
1964 for (int j = 0; j < styleSheet.mediaRules.at(i).styleRules.count(); ++j) {
1965 matchRule(node, styleSheet.mediaRules.at(i).styleRules.at(j), styleSheet.origin,
1966 styleSheet.depth, &weightedRules);
1973 rules.reserve(weightedRules.count());
1974 QMap<uint, StyleRule>::const_iterator it = weightedRules.constBegin();
1975 for ( ; it != weightedRules.constEnd() ; ++it)
1981 // for qtexthtmlparser which requires just the declarations with Enabled state
1982 // and without pseudo elements
1983 QVector<Declaration> StyleSelector::declarationsForNode(NodePtr node, const char *extraPseudo)
1985 QVector<Declaration> decls;
1986 QVector<StyleRule> rules = styleRulesForNode(node);
1987 for (int i = 0; i < rules.count(); i++) {
1988 const Selector& selector = rules.at(i).selectors.at(0);
1989 const QString pseudoElement = selector.pseudoElement();
1991 if (extraPseudo && pseudoElement == QLatin1String(extraPseudo)) {
1992 decls += rules.at(i).declarations;
1996 if (!pseudoElement.isEmpty()) // skip rules with pseudo elements
1998 quint64 pseudoClass = selector.pseudoClass();
1999 if (pseudoClass == PseudoClass_Enabled || pseudoClass == PseudoClass_Unspecified)
2000 decls += rules.at(i).declarations;
2005 static inline bool isHexDigit(const char c)
2007 return (c >= '0' && c <= '9')
2008 || (c >= 'a' && c <= 'f')
2009 || (c >= 'A' && c <= 'F')
2013 QString Scanner::preprocess(const QString &input, bool *hasEscapeSequences)
2015 QString output = input;
2017 if (hasEscapeSequences)
2018 *hasEscapeSequences = false;
2021 while (i < output.size()) {
2022 if (output.at(i) == QLatin1Char('\\')) {
2025 // test for unicode hex escape
2027 const int hexStart = i;
2028 while (i < output.size()
2029 && isHexDigit(output.at(i).toLatin1())
2034 if (hexCount == 0) {
2035 if (hasEscapeSequences)
2036 *hasEscapeSequences = true;
2040 hexCount = qMin(hexCount, 6);
2042 ushort code = output.mid(hexStart, hexCount).toUShort(&ok, 16);
2044 output.replace(hexStart - 1, hexCount + 1, QChar(code));
2056 int QCssScanner_Generated::handleCommentStart()
2058 while (pos < input.size() - 1) {
2059 if (input.at(pos) == QLatin1Char('*')
2060 && input.at(pos + 1) == QLatin1Char('/')) {
2069 void Scanner::scan(const QString &preprocessedInput, QVector<Symbol> *symbols)
2071 QCssScanner_Generated scanner(preprocessedInput);
2073 int tok = scanner.lex();
2075 sym.token = static_cast<QCss::TokenType>(tok);
2076 sym.text = scanner.input;
2077 sym.start = scanner.lexemStart;
2078 sym.len = scanner.lexemLength;
2079 symbols->append(sym);
2080 tok = scanner.lex();
2084 QString Symbol::lexem() const
2088 result.reserve(len);
2089 for (int i = 0; i < len; ++i) {
2090 if (text.at(start + i) == QLatin1Char('\\') && i < len - 1)
2092 result += text.at(start + i);
2097 Parser::Parser(const QString &css, bool isFile)
2106 hasEscapeSequences = false;
2109 void Parser::init(const QString &css, bool isFile)
2111 QString styleSheet = css;
2114 if (file.open(QFile::ReadOnly)) {
2115 sourcePath = QFileInfo(styleSheet).absolutePath() + QLatin1Char('/');
2116 QTextStream stream(&file);
2117 styleSheet = stream.readAll();
2119 qWarning() << "QCss::Parser - Failed to load file " << css;
2126 hasEscapeSequences = false;
2129 Scanner::scan(Scanner::preprocess(styleSheet, &hasEscapeSequences), &symbols);
2134 bool Parser::parse(StyleSheet *styleSheet, Qt::CaseSensitivity nameCaseSensitivity)
2136 if (testTokenAndEndsWith(ATKEYWORD_SYM, QLatin1String("charset"))) {
2137 if (!next(STRING)) return false;
2138 if (!next(SEMICOLON)) return false;
2141 while (test(S) || test(CDO) || test(CDC)) {}
2143 while (testImport()) {
2145 if (!parseImport(&rule)) return false;
2146 styleSheet->importRules.append(rule);
2147 while (test(S) || test(CDO) || test(CDC)) {}
2153 if (!parseMedia(&rule)) return false;
2154 styleSheet->mediaRules.append(rule);
2155 } else if (testPage()) {
2157 if (!parsePage(&rule)) return false;
2158 styleSheet->pageRules.append(rule);
2159 } else if (testRuleset()) {
2161 if (!parseRuleset(&rule)) return false;
2162 styleSheet->styleRules.append(rule);
2163 } else if (test(ATKEYWORD_SYM)) {
2164 if (!until(RBRACE)) return false;
2165 } else if (hasNext()) {
2168 while (test(S) || test(CDO) || test(CDC)) {}
2169 } while (hasNext());
2170 styleSheet->buildIndexes(nameCaseSensitivity);
2174 Symbol Parser::errorSymbol()
2176 if (errorIndex == -1) return Symbol();
2177 return symbols.at(errorIndex);
2180 static inline void removeOptionalQuotes(QString *str)
2182 if (!str->startsWith(QLatin1Char('\''))
2183 && !str->startsWith(QLatin1Char('\"')))
2189 bool Parser::parseImport(ImportRule *importRule)
2194 importRule->href = lexem();
2196 if (!testAndParseUri(&importRule->href)) return false;
2198 removeOptionalQuotes(&importRule->href);
2203 if (!parseMedium(&importRule->media)) return false;
2205 while (test(COMMA)) {
2207 if (!parseNextMedium(&importRule->media)) return false;
2211 if (!next(SEMICOLON)) return false;
2217 bool Parser::parseMedia(MediaRule *mediaRule)
2221 if (!parseNextMedium(&mediaRule->media)) return false;
2222 } while (test(COMMA));
2224 if (!next(LBRACE)) return false;
2227 while (testRuleset()) {
2229 if (!parseRuleset(&rule)) return false;
2230 mediaRule->styleRules.append(rule);
2233 if (!next(RBRACE)) return false;
2238 bool Parser::parseMedium(QStringList *media)
2240 media->append(lexem());
2245 bool Parser::parsePage(PageRule *pageRule)
2249 if (testPseudoPage())
2250 if (!parsePseudoPage(&pageRule->selector)) return false;
2253 if (!next(LBRACE)) return false;
2258 if (!parseNextDeclaration(&decl)) return false;
2259 if (!decl.isEmpty())
2260 pageRule->declarations.append(decl);
2261 } while (test(SEMICOLON));
2263 if (!next(RBRACE)) return false;
2268 bool Parser::parsePseudoPage(QString *selector)
2270 if (!next(IDENT)) return false;
2271 *selector = lexem();
2275 bool Parser::parseNextOperator(Value *value)
2277 if (!hasNext()) return true;
2279 case SLASH: value->type = Value::TermOperatorSlash; skipSpace(); break;
2280 case COMMA: value->type = Value::TermOperatorComma; skipSpace(); break;
2281 default: prev(); break;
2286 bool Parser::parseCombinator(BasicSelector::Relation *relation)
2288 *relation = BasicSelector::NoRelation;
2289 if (lookup() == S) {
2290 *relation = BasicSelector::MatchNextSelectorIfAncestor;
2296 *relation = BasicSelector::MatchNextSelectorIfPreceeds;
2297 } else if (test(GREATER)) {
2298 *relation = BasicSelector::MatchNextSelectorIfParent;
2304 bool Parser::parseProperty(Declaration *decl)
2306 decl->d->property = lexem();
2307 decl->d->propertyId = static_cast<Property>(findKnownValue(decl->d->property, properties, NumProperties));
2312 bool Parser::parseRuleset(StyleRule *styleRule)
2315 if (!parseSelector(&sel)) return false;
2316 styleRule->selectors.append(sel);
2318 while (test(COMMA)) {
2321 if (!parseNextSelector(&sel)) return false;
2322 styleRule->selectors.append(sel);
2326 if (!next(LBRACE)) return false;
2327 const int declarationStart = index;
2332 const int rewind = index;
2333 if (!parseNextDeclaration(&decl)) {
2335 const bool foundSemicolon = until(SEMICOLON);
2336 const int semicolonIndex = index;
2338 index = declarationStart;
2339 const bool foundRBrace = until(RBRACE);
2341 if (foundSemicolon && semicolonIndex < index) {
2342 decl = Declaration();
2343 index = semicolonIndex - 1;
2349 if (!decl.isEmpty())
2350 styleRule->declarations.append(decl);
2351 } while (test(SEMICOLON));
2353 if (!next(RBRACE)) return false;
2358 bool Parser::parseSelector(Selector *sel)
2360 BasicSelector basicSel;
2361 if (!parseSimpleSelector(&basicSel)) return false;
2362 while (testCombinator()) {
2363 if (!parseCombinator(&basicSel.relationToNext)) return false;
2365 if (!testSimpleSelector()) break;
2366 sel->basicSelectors.append(basicSel);
2368 basicSel = BasicSelector();
2369 if (!parseSimpleSelector(&basicSel)) return false;
2371 sel->basicSelectors.append(basicSel);
2375 bool Parser::parseSimpleSelector(BasicSelector *basicSel)
2378 if (lookupElementName()) {
2379 if (!parseElementName(&basicSel->elementName)) return false;
2389 QString theid = lexem();
2390 // chop off leading #
2392 basicSel->ids.append(theid);
2394 } else if (testClass()) {
2396 AttributeSelector a;
2397 a.name = QLatin1String("class");
2398 a.valueMatchCriterium = AttributeSelector::MatchContains;
2399 if (!parseClass(&a.value)) return false;
2400 basicSel->attributeSelectors.append(a);
2401 } else if (testAttrib()) {
2403 AttributeSelector a;
2404 if (!parseAttrib(&a)) return false;
2405 basicSel->attributeSelectors.append(a);
2406 } else if (testPseudo()) {
2409 if (!parsePseudo(&ps)) return false;
2410 basicSel->pseudos.append(ps);
2412 if (onceMore) ++count;
2414 return count >= minCount;
2417 bool Parser::parseClass(QString *name)
2419 if (!next(IDENT)) return false;
2424 bool Parser::parseElementName(QString *name)
2427 case STAR: name->clear(); break;
2428 case IDENT: *name = lexem(); break;
2429 default: return false;
2434 bool Parser::parseAttrib(AttributeSelector *attr)
2437 if (!next(IDENT)) return false;
2438 attr->name = lexem();
2442 attr->valueMatchCriterium = AttributeSelector::MatchEqual;
2443 } else if (test(INCLUDES)) {
2444 attr->valueMatchCriterium = AttributeSelector::MatchContains;
2445 } else if (test(DASHMATCH)) {
2446 attr->valueMatchCriterium = AttributeSelector::MatchBeginsWith;
2448 return next(RBRACKET);
2453 if (!test(IDENT) && !test(STRING)) return false;
2454 attr->value = unquotedLexem();
2457 return next(RBRACKET);
2460 bool Parser::parsePseudo(Pseudo *pseudo)
2463 pseudo->negated = test(EXCLAMATION_SYM);
2465 pseudo->name = lexem();
2466 pseudo->type = static_cast<quint64>(findKnownValue(pseudo->name, pseudos, NumPseudos));
2469 if (!next(FUNCTION)) return false;
2470 pseudo->function = lexem();
2471 // chop off trailing parenthesis
2472 pseudo->function.chop(1);
2474 if (!test(IDENT)) return false;
2475 pseudo->name = lexem();
2477 return next(RPAREN);
2480 bool Parser::parseNextDeclaration(Declaration *decl)
2482 if (!testProperty())
2483 return true; // not an error!
2484 if (!parseProperty(decl)) return false;
2485 if (!next(COLON)) return false;
2487 if (!parseNextExpr(&decl->d->values)) return false;
2489 if (!parsePrio(decl)) return false;
2493 bool Parser::testPrio()
2495 const int rewind = index;
2496 if (!test(EXCLAMATION_SYM)) return false;
2502 if (lexem().compare(QLatin1String("important"), Qt::CaseInsensitive) != 0) {
2509 bool Parser::parsePrio(Declaration *declaration)
2511 declaration->d->important = true;
2516 bool Parser::parseExpr(QVector<Value> *values)
2519 if (!parseTerm(&val)) return false;
2520 values->append(val);
2525 if (!parseNextOperator(&val)) return false;
2526 if (val.type != QCss::Value::Unknown)
2527 values->append(val);
2531 if (!parseTerm(&val)) return false;
2532 values->append(val);
2538 bool Parser::testTerm()
2540 return test(PLUS) || test(MINUS)
2550 bool Parser::parseTerm(Value *value)
2552 QString str = lexem();
2553 bool haveUnary = false;
2554 if (lookup() == PLUS || lookup() == MINUS) {
2556 if (!hasNext()) return false;
2561 value->variant = str;
2562 value->type = QCss::Value::String;
2565 value->type = Value::Number;
2566 value->variant.convert(QVariant::Double);
2569 value->type = Value::Percentage;
2570 str.chop(1); // strip off %
2571 value->variant = str;
2574 value->type = Value::Length;
2578 if (haveUnary) return false;
2579 value->type = Value::String;
2582 value->variant = str;
2585 if (haveUnary) return false;
2586 value->type = Value::Identifier;
2587 const int theid = findKnownValue(str, values, NumKnownValues);
2589 value->type = Value::KnownIdentifier;
2590 value->variant = theid;
2595 if (haveUnary) return false;
2597 if (testHexColor()) {
2599 if (!parseHexColor(&col)) return false;
2600 value->type = Value::Color;
2601 value->variant = col;
2602 } else if (testFunction()) {
2604 if (!parseFunction(&name, &args)) return false;
2605 if (name == QLatin1String("url")) {
2606 value->type = Value::Uri;
2607 removeOptionalQuotes(&args);
2608 if (QFileInfo(args).isRelative() && !sourcePath.isEmpty()) {
2609 args.prepend(sourcePath);
2611 value->variant = args;
2613 value->type = Value::Function;
2614 value->variant = QStringList() << name << args;
2617 return recordError();
2626 bool Parser::parseFunction(QString *name, QString *args)
2631 const int start = index;
2632 if (!until(RPAREN)) return false;
2633 for (int i = start; i < index - 1; ++i)
2634 args->append(symbols.at(i).lexem());
2636 if (!nextExpr(&arguments)) return false;
2637 if (!next(RPAREN)) return false;
2643 bool Parser::parseHexColor(QColor *col)
2645 col->setNamedColor(lexem());
2646 if (!col->isValid()) {
2647 qWarning("QCssParser::parseHexColor: Unknown color name '%s'",lexem().toLatin1().constData());
2654 bool Parser::testAndParseUri(QString *uri)
2656 const int rewind = index;
2657 if (!testFunction()) return false;
2660 if (!parseFunction(&name, &args)) {
2664 if (name.toLower() != QLatin1String("url")) {
2669 removeOptionalQuotes(uri);
2673 bool Parser::testSimpleSelector()
2675 return testElementName()
2682 bool Parser::next(QCss::TokenType t)
2684 if (hasNext() && next() == t)
2686 return recordError();
2689 bool Parser::test(QCss::TokenType t)
2691 if (index >= symbols.count())
2693 if (symbols.at(index).token == t) {
2700 QString Parser::unquotedLexem() const
2702 QString s = lexem();
2703 if (lookup() == STRING) {
2710 QString Parser::lexemUntil(QCss::TokenType t)
2713 while (hasNext() && next() != t)
2714 lexem += symbol().lexem();
2718 bool Parser::until(QCss::TokenType target, QCss::TokenType target2)
2724 switch(symbols.at(index-1).token) {
2725 case LBRACE: ++braceCount; break;
2726 case LBRACKET: ++brackCount; break;
2728 case LPAREN: ++parenCount; break;
2732 while (index < symbols.size()) {
2733 QCss::TokenType t = symbols.at(index++).token;
2735 case LBRACE: ++braceCount; break;
2736 case RBRACE: --braceCount; break;
2737 case LBRACKET: ++brackCount; break;
2738 case RBRACKET: --brackCount; break;
2740 case LPAREN: ++parenCount; break;
2741 case RPAREN: --parenCount; break;
2744 if ((t == target || (target2 != NONE && t == target2))
2750 if (braceCount < 0 || brackCount < 0 || parenCount < 0) {
2758 bool Parser::testTokenAndEndsWith(QCss::TokenType t, QLatin1String str)
2760 if (!test(t)) return false;
2761 if (!lexem().endsWith(str, Qt::CaseInsensitive)) {
2769 #endif // QT_NO_CSSPARSER