1 /****************************************************************************
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the QtGui module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
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"));
705 bool rgba = lst.at(0).startsWith(QLatin1String("rgba"));
711 QVector<QCss::Value> colorDigits;
712 if (!p.parseExpr(&colorDigits))
715 for (int i = 0; i < qMin(colorDigits.count(), 7); i += 2) {
716 if (colorDigits.at(i).type == Value::Percentage) {
717 colorDigits[i].variant = colorDigits.at(i).variant.toReal() * (255. / 100.);
718 colorDigits[i].type = Value::Number;
719 } else if (colorDigits.at(i).type != Value::Number) {
724 int v1 = colorDigits.at(0).variant.toInt();
725 int v2 = colorDigits.at(2).variant.toInt();
726 int v3 = colorDigits.at(4).variant.toInt();
728 if (colorDigits.count() >= 7) {
729 int alphaValue = colorDigits.at(6).variant.toInt();
730 if (rgba && alphaValue <= 1)
731 alpha = colorDigits.at(6).variant.toReal() * 255.;
736 return rgb ? QColor::fromRgb(v1, v2, v3, alpha)
737 : QColor::fromHsv(v1, v2, v3, alpha);
740 static QColor colorFromData(const ColorData& c, const QPalette &pal)
742 if (c.type == ColorData::Color) {
744 } else if (c.type == ColorData::Role) {
745 return pal.color(c.role);
750 static BrushData parseBrushValue(const QCss::Value &v, const QPalette &pal)
752 ColorData c = parseColorValue(v);
753 if (c.type == ColorData::Color) {
754 return QBrush(c.color);
755 } else if (c.type == ColorData::Role) {
759 if (v.type != Value::Function)
762 QStringList lst = v.variant.toStringList();
763 if (lst.count() != 2)
766 QStringList gradFuncs;
767 gradFuncs << QLatin1String("qlineargradient") << QLatin1String("qradialgradient") << QLatin1String("qconicalgradient") << QLatin1String("qgradient");
770 if ((gradType = gradFuncs.indexOf(lst.at(0).toLower())) == -1)
773 QHash<QString, qreal> vars;
774 QVector<QGradientStop> stops;
778 spreads << QLatin1String("pad") << QLatin1String("reflect") << QLatin1String("repeat");
780 bool dependsOnThePalette = false;
781 Parser parser(lst.at(1));
782 while (parser.hasNext()) {
784 if (!parser.test(IDENT))
786 QString attr = parser.lexem();
788 if (!parser.test(COLON))
791 if (attr.compare(QLatin1String("stop"), Qt::CaseInsensitive) == 0) {
792 QCss::Value stop, color;
794 if (!parser.parseTerm(&stop)) return BrushData();
797 if (!parser.parseTerm(&color)) return BrushData();
798 ColorData cd = parseColorValue(color);
799 if(cd.type == ColorData::Role)
800 dependsOnThePalette = true;
801 stops.append(QGradientStop(stop.variant.toReal(), colorFromData(cd, pal)));
805 (void)parser.parseTerm(&value);
806 if (attr.compare(QLatin1String("spread"), Qt::CaseInsensitive) == 0) {
807 spread = spreads.indexOf(value.variant.toString());
809 vars[attr] = value.variant.toReal();
813 (void)parser.test(COMMA);
817 QLinearGradient lg(vars.value(QLatin1String("x1")), vars.value(QLatin1String("y1")),
818 vars.value(QLatin1String("x2")), vars.value(QLatin1String("y2")));
819 lg.setCoordinateMode(QGradient::ObjectBoundingMode);
822 lg.setSpread(QGradient::Spread(spread));
823 BrushData bd = QBrush(lg);
824 if (dependsOnThePalette)
825 bd.type = BrushData::DependsOnThePalette;
830 QRadialGradient rg(vars.value(QLatin1String("cx")), vars.value(QLatin1String("cy")),
831 vars.value(QLatin1String("radius")), vars.value(QLatin1String("fx")),
832 vars.value(QLatin1String("fy")));
833 rg.setCoordinateMode(QGradient::ObjectBoundingMode);
836 rg.setSpread(QGradient::Spread(spread));
837 BrushData bd = QBrush(rg);
838 if (dependsOnThePalette)
839 bd.type = BrushData::DependsOnThePalette;
844 QConicalGradient cg(vars.value(QLatin1String("cx")), vars.value(QLatin1String("cy")),
845 vars.value(QLatin1String("angle")));
846 cg.setCoordinateMode(QGradient::ObjectBoundingMode);
849 cg.setSpread(QGradient::Spread(spread));
850 BrushData bd = QBrush(cg);
851 if (dependsOnThePalette)
852 bd.type = BrushData::DependsOnThePalette;
859 static QBrush brushFromData(const BrushData& c, const QPalette &pal)
861 if (c.type == BrushData::Role) {
862 return pal.color(c.role);
868 static BorderStyle parseStyleValue(QCss::Value v)
870 if (v.type == Value::KnownIdentifier) {
871 switch (v.variant.toInt()) {
873 return BorderStyle_None;
875 return BorderStyle_Dotted;
877 return BorderStyle_Dashed;
879 return BorderStyle_Solid;
881 return BorderStyle_Double;
883 return BorderStyle_DotDash;
884 case Value_DotDotDash:
885 return BorderStyle_DotDotDash;
887 return BorderStyle_Groove;
889 return BorderStyle_Ridge;
891 return BorderStyle_Inset;
893 return BorderStyle_Outset;
895 return BorderStyle_Native;
901 return BorderStyle_Unknown;
904 void ValueExtractor::borderValue(const Declaration &decl, int *width, QCss::BorderStyle *style, QBrush *color)
906 if (decl.d->parsed.isValid()) {
907 BorderData data = qvariant_cast<BorderData>(decl.d->parsed);
908 *width = lengthValueFromData(data.width, f);
910 *color = data.color.type != BrushData::Invalid ? brushFromData(data.color, pal) : QBrush(QColor());
915 *style = BorderStyle_None;
918 if (decl.d->values.isEmpty())
922 data.width.number = 0;
923 data.width.unit = LengthData::None;
924 data.style = BorderStyle_None;
927 if (decl.d->values.at(i).type == Value::Length || decl.d->values.at(i).type == Value::Number) {
928 data.width = lengthValue(decl.d->values.at(i));
929 *width = lengthValueFromData(data.width, f);
930 if (++i >= decl.d->values.count()) {
931 decl.d->parsed = QVariant::fromValue<BorderData>(data);
936 data.style = parseStyleValue(decl.d->values.at(i));
937 if (data.style != BorderStyle_Unknown) {
939 if (++i >= decl.d->values.count()) {
940 decl.d->parsed = QVariant::fromValue<BorderData>(data);
944 data.style = BorderStyle_None;
947 data.color = parseBrushValue(decl.d->values.at(i), pal);
948 *color = brushFromData(data.color, pal);
949 if (data.color.type != BrushData::DependsOnThePalette)
950 decl.d->parsed = QVariant::fromValue<BorderData>(data);
953 static void parseShorthandBackgroundProperty(const QVector<QCss::Value> &values, BrushData *brush, QString *image, Repeat *repeat, Qt::Alignment *alignment, const QPalette &pal)
955 *brush = BrushData();
958 *alignment = Qt::AlignTop | Qt::AlignLeft;
960 for (int i = 0; i < values.count(); ++i) {
961 const QCss::Value &v = values.at(i);
962 if (v.type == Value::Uri) {
963 *image = v.variant.toString();
965 } else if (v.type == Value::KnownIdentifier && v.variant.toInt() == Value_None) {
968 } else if (v.type == Value::KnownIdentifier && v.variant.toInt() == Value_Transparent) {
969 *brush = QBrush(Qt::transparent);
972 Repeat repeatAttempt = static_cast<Repeat>(findKnownValue(v.variant.toString(),
973 repeats, NumKnownRepeats));
974 if (repeatAttempt != Repeat_Unknown) {
975 *repeat = repeatAttempt;
979 if (v.type == Value::KnownIdentifier) {
982 if (i < values.count() - 1
983 && values.at(i + 1).type == Value::KnownIdentifier) {
987 Qt::Alignment a = parseAlignment(values.constData() + start, count);
995 *brush = parseBrushValue(v, pal);
999 bool ValueExtractor::extractBackground(QBrush *brush, QString *image, Repeat *repeat,
1000 Qt::Alignment *alignment, Origin *origin, Attachment *attachment,
1004 for (int i = 0; i < declarations.count(); ++i) {
1005 const Declaration &decl = declarations.at(i);
1006 if (decl.d->values.isEmpty())
1008 const QCss::Value &val = decl.d->values.at(0);
1009 switch (decl.d->propertyId) {
1010 case BackgroundColor:
1011 *brush = decl.brushValue();
1013 case BackgroundImage:
1014 if (val.type == Value::Uri)
1015 *image = val.variant.toString();
1017 case BackgroundRepeat:
1018 if (decl.d->parsed.isValid()) {
1019 *repeat = static_cast<Repeat>(decl.d->parsed.toInt());
1021 *repeat = static_cast<Repeat>(findKnownValue(val.variant.toString(),
1022 repeats, NumKnownRepeats));
1023 decl.d->parsed = *repeat;
1026 case BackgroundPosition:
1027 *alignment = decl.alignmentValue();
1029 case BackgroundOrigin:
1030 *origin = decl.originValue();
1032 case BackgroundClip:
1033 *clip = decl.originValue();
1036 if (decl.d->parsed.isValid()) {
1037 BackgroundData data = qvariant_cast<BackgroundData>(decl.d->parsed);
1038 *brush = brushFromData(data.brush, pal);
1039 *image = data.image;
1040 *repeat = data.repeat;
1041 *alignment = data.alignment;
1043 BrushData brushData;
1044 parseShorthandBackgroundProperty(decl.d->values, &brushData, image, repeat, alignment, pal);
1045 *brush = brushFromData(brushData, pal);
1046 if (brushData.type != BrushData::DependsOnThePalette) {
1047 BackgroundData data = { brushData, *image, *repeat, *alignment };
1048 decl.d->parsed = QVariant::fromValue<BackgroundData>(data);
1052 case BackgroundAttachment:
1053 *attachment = decl.attachmentValue();
1062 static bool setFontSizeFromValue(QCss::Value value, QFont *font, int *fontSizeAdjustment)
1064 if (value.type == Value::KnownIdentifier) {
1066 switch (value.variant.toInt()) {
1067 case Value_Small: *fontSizeAdjustment = -1; break;
1068 case Value_Medium: *fontSizeAdjustment = 0; break;
1069 case Value_Large: *fontSizeAdjustment = 1; break;
1070 case Value_XLarge: *fontSizeAdjustment = 2; break;
1071 case Value_XXLarge: *fontSizeAdjustment = 3; break;
1072 default: valid = false; break;
1076 if (value.type != Value::Length)
1080 QString s = value.variant.toString();
1081 if (s.endsWith(QLatin1String("pt"), Qt::CaseInsensitive)) {
1084 if (value.variant.convert((QVariant::Type)qMetaTypeId<qreal>())) {
1085 font->setPointSizeF(value.variant.toReal());
1088 } else if (s.endsWith(QLatin1String("px"), Qt::CaseInsensitive)) {
1091 if (value.variant.convert(QVariant::Int)) {
1092 font->setPixelSize(value.variant.toInt());
1099 static bool setFontStyleFromValue(const QCss::Value &value, QFont *font)
1101 if (value.type != Value::KnownIdentifier)
1103 switch (value.variant.toInt()) {
1104 case Value_Normal: font->setStyle(QFont::StyleNormal); return true;
1105 case Value_Italic: font->setStyle(QFont::StyleItalic); return true;
1106 case Value_Oblique: font->setStyle(QFont::StyleOblique); return true;
1112 static bool setFontWeightFromValue(const QCss::Value &value, QFont *font)
1114 if (value.type == Value::KnownIdentifier) {
1115 switch (value.variant.toInt()) {
1116 case Value_Normal: font->setWeight(QFont::Normal); return true;
1117 case Value_Bold: font->setWeight(QFont::Bold); return true;
1122 if (value.type != Value::Number)
1124 font->setWeight(qMin(value.variant.toInt() / 8, 99));
1129 * parse the font family from the values (starting from index \a start)
1130 * and set it the \a font
1131 * The function returns true if a family was extracted.
1133 static bool setFontFamilyFromValues(const QVector<QCss::Value> &values, QFont *font, int start = 0)
1136 bool shouldAddSpace = false;
1137 for (int i = start; i < values.count(); ++i) {
1138 const QCss::Value &v = values.at(i);
1139 if (v.type == Value::TermOperatorComma) {
1140 family += QLatin1Char(',');
1141 shouldAddSpace = false;
1144 const QString str = v.variant.toString();
1148 family += QLatin1Char(' ');
1150 shouldAddSpace = true;
1152 if (family.isEmpty())
1154 font->setFamily(family);
1158 static void setTextDecorationFromValues(const QVector<QCss::Value> &values, QFont *font)
1160 for (int i = 0; i < values.count(); ++i) {
1161 if (values.at(i).type != Value::KnownIdentifier)
1163 switch (values.at(i).variant.toInt()) {
1164 case Value_Underline: font->setUnderline(true); break;
1165 case Value_Overline: font->setOverline(true); break;
1166 case Value_LineThrough: font->setStrikeOut(true); break;
1168 font->setUnderline(false);
1169 font->setOverline(false);
1170 font->setStrikeOut(false);
1177 static void parseShorthandFontProperty(const QVector<QCss::Value> &values, QFont *font, int *fontSizeAdjustment)
1179 font->setStyle(QFont::StyleNormal);
1180 font->setWeight(QFont::Normal);
1181 *fontSizeAdjustment = -255;
1184 while (i < values.count()) {
1185 if (setFontStyleFromValue(values.at(i), font)
1186 || setFontWeightFromValue(values.at(i), font))
1192 if (i < values.count()) {
1193 setFontSizeFromValue(values.at(i), font, fontSizeAdjustment);
1197 if (i < values.count()) {
1198 setFontFamilyFromValues(values, font, i);
1202 static void setFontVariantFromValue(const QCss::Value &value, QFont *font)
1204 if (value.type == Value::KnownIdentifier) {
1205 switch (value.variant.toInt()) {
1206 case Value_Normal: font->setCapitalization(QFont::MixedCase); break;
1207 case Value_SmallCaps: font->setCapitalization(QFont::SmallCaps); break;
1213 static void setTextTransformFromValue(const QCss::Value &value, QFont *font)
1215 if (value.type == Value::KnownIdentifier) {
1216 switch (value.variant.toInt()) {
1217 case Value_None: font->setCapitalization(QFont::MixedCase); break;
1218 case Value_Uppercase: font->setCapitalization(QFont::AllUppercase); break;
1219 case Value_Lowercase: font->setCapitalization(QFont::AllLowercase); break;
1225 bool ValueExtractor::extractFont(QFont *font, int *fontSizeAdjustment)
1227 if (fontExtracted) {
1229 *fontSizeAdjustment = adjustment;
1230 return fontExtracted == 1;
1234 for (int i = 0; i < declarations.count(); ++i) {
1235 const Declaration &decl = declarations.at(i);
1236 if (decl.d->values.isEmpty())
1238 const QCss::Value &val = decl.d->values.at(0);
1239 switch (decl.d->propertyId) {
1240 case FontSize: setFontSizeFromValue(val, font, fontSizeAdjustment); break;
1241 case FontStyle: setFontStyleFromValue(val, font); break;
1242 case FontWeight: setFontWeightFromValue(val, font); break;
1243 case FontFamily: setFontFamilyFromValues(decl.d->values, font); break;
1244 case TextDecoration: setTextDecorationFromValues(decl.d->values, font); break;
1245 case Font: parseShorthandFontProperty(decl.d->values, font, fontSizeAdjustment); break;
1246 case FontVariant: setFontVariantFromValue(val, font); break;
1247 case TextTransform: setTextTransformFromValue(val, font); break;
1254 adjustment = *fontSizeAdjustment;
1255 fontExtracted = hit ? 1 : 2;
1259 bool ValueExtractor::extractPalette(QBrush *fg, QBrush *sfg, QBrush *sbg, QBrush *abg)
1262 for (int i = 0; i < declarations.count(); ++i) {
1263 const Declaration &decl = declarations.at(i);
1264 switch (decl.d->propertyId) {
1265 case Color: *fg = decl.brushValue(pal); break;
1266 case QtSelectionForeground: *sfg = decl.brushValue(pal); break;
1267 case QtSelectionBackground: *sbg = decl.brushValue(pal); break;
1268 case QtAlternateBackground: *abg = decl.brushValue(pal); break;
1276 void ValueExtractor::extractFont()
1281 extractFont(&f, &dummy);
1284 bool ValueExtractor::extractImage(QIcon *icon, Qt::Alignment *a, QSize *size)
1287 for (int i = 0; i < declarations.count(); ++i) {
1288 const Declaration &decl = declarations.at(i);
1289 switch (decl.d->propertyId) {
1291 *icon = decl.iconValue();
1292 if (decl.d->values.count() > 0 && decl.d->values.at(0).type == Value::Uri) {
1293 // try to pull just the size from the image...
1294 QImageReader imageReader(decl.d->values.at(0).variant.toString());
1295 if ((*size = imageReader.size()).isNull()) {
1296 // but we'll have to load the whole image if the
1297 // format doesn't support just reading the size
1298 *size = imageReader.read().size();
1302 case QtImageAlignment: *a = decl.alignmentValue(); break;
1310 ///////////////////////////////////////////////////////////////////////////////
1312 QColor Declaration::colorValue(const QPalette &pal) const
1314 if (d->values.count() != 1)
1317 if (d->parsed.isValid()) {
1318 if (d->parsed.type() == QVariant::Color)
1319 return qvariant_cast<QColor>(d->parsed);
1320 if (d->parsed.type() == QVariant::Int)
1321 return pal.color((QPalette::ColorRole)(d->parsed.toInt()));
1324 ColorData color = parseColorValue(d->values.at(0));
1325 if(color.type == ColorData::Role) {
1326 d->parsed = QVariant::fromValue<int>(color.role);
1327 return pal.color((QPalette::ColorRole)(color.role));
1329 d->parsed = QVariant::fromValue<QColor>(color.color);
1334 QBrush Declaration::brushValue(const QPalette &pal) const
1336 if (d->values.count() != 1)
1339 if (d->parsed.isValid()) {
1340 if (d->parsed.type() == QVariant::Brush)
1341 return qvariant_cast<QBrush>(d->parsed);
1342 if (d->parsed.type() == QVariant::Int)
1343 return pal.color((QPalette::ColorRole)(d->parsed.toInt()));
1346 BrushData data = parseBrushValue(d->values.at(0), pal);
1348 if(data.type == BrushData::Role) {
1349 d->parsed = QVariant::fromValue<int>(data.role);
1350 return pal.color((QPalette::ColorRole)(data.role));
1352 if (data.type != BrushData::DependsOnThePalette)
1353 d->parsed = QVariant::fromValue<QBrush>(data.brush);
1358 void Declaration::brushValues(QBrush *c, const QPalette &pal) const
1360 int needParse = 0x1f; // bits 0..3 say if we should parse the corresponding value.
1361 // the bit 4 say we need to update d->parsed
1363 if (d->parsed.isValid()) {
1365 QList<QVariant> v = d->parsed.toList();
1366 for (i = 0; i < qMin(v.count(), 4); i++) {
1367 if (v.at(i).type() == QVariant::Brush) {
1368 c[i] = qvariant_cast<QBrush>(v.at(i));
1369 } else if (v.at(i).type() == QVariant::Int) {
1370 c[i] = pal.color((QPalette::ColorRole)(v.at(i).toInt()));
1372 needParse |= (1<<i);
1376 if (needParse != 0) {
1378 for (i = 0; i < qMin(d->values.count(), 4); i++) {
1379 if (!(needParse & (1<<i)))
1381 BrushData data = parseBrushValue(d->values.at(i), pal);
1382 if(data.type == BrushData::Role) {
1383 v += QVariant::fromValue<int>(data.role);
1384 c[i] = pal.color((QPalette::ColorRole)(data.role));
1386 if (data.type != BrushData::DependsOnThePalette) {
1387 v += QVariant::fromValue<QBrush>(data.brush);
1394 if (needParse & 0x10)
1397 if (i == 0) c[0] = c[1] = c[2] = c[3] = QBrush();
1398 else if (i == 1) c[3] = c[2] = c[1] = c[0];
1399 else if (i == 2) c[2] = c[0], c[3] = c[1];
1400 else if (i == 3) c[3] = c[1];
1403 bool Declaration::realValue(qreal *real, const char *unit) const
1405 if (d->values.count() != 1)
1407 const Value &v = d->values.at(0);
1408 if (unit && v.type != Value::Length)
1410 QString s = v.variant.toString();
1412 if (!s.endsWith(QLatin1String(unit), Qt::CaseInsensitive))
1414 s.chop(qstrlen(unit));
1417 qreal val = s.toDouble(&ok);
1423 static bool intValueHelper(const QCss::Value &v, int *i, const char *unit)
1425 if (unit && v.type != Value::Length)
1427 QString s = v.variant.toString();
1429 if (!s.endsWith(QLatin1String(unit), Qt::CaseInsensitive))
1431 s.chop(qstrlen(unit));
1434 int val = s.toInt(&ok);
1440 bool Declaration::intValue(int *i, const char *unit) const
1442 if (d->values.count() != 1)
1444 return intValueHelper(d->values.at(0), i, unit);
1447 QSize Declaration::sizeValue() const
1449 if (d->parsed.isValid())
1450 return qvariant_cast<QSize>(d->parsed);
1452 int x[2] = { 0, 0 };
1453 if (d->values.count() > 0)
1454 intValueHelper(d->values.at(0), &x[0], "px");
1455 if (d->values.count() > 1)
1456 intValueHelper(d->values.at(1), &x[1], "px");
1459 QSize size(x[0], x[1]);
1460 d->parsed = QVariant::fromValue<QSize>(size);
1464 QRect Declaration::rectValue() const
1466 if (d->values.count() != 1)
1469 if (d->parsed.isValid())
1470 return qvariant_cast<QRect>(d->parsed);
1472 const QCss::Value &v = d->values.at(0);
1473 if (v.type != Value::Function)
1475 QStringList func = v.variant.toStringList();
1476 if (func.count() != 2 || func.at(0).compare(QLatin1String("rect")) != 0)
1478 QStringList args = func[1].split(QLatin1Char(' '), QString::SkipEmptyParts);
1479 if (args.count() != 4)
1481 QRect rect(args[0].toInt(), args[1].toInt(), args[2].toInt(), args[3].toInt());
1482 d->parsed = QVariant::fromValue<QRect>(rect);
1486 void Declaration::colorValues(QColor *c, const QPalette &pal) const
1489 if (d->parsed.isValid()) {
1490 QList<QVariant> v = d->parsed.toList();
1491 for (i = 0; i < qMin(d->values.count(), 4); i++) {
1492 if (v.at(i).type() == QVariant::Color) {
1493 c[i] = qvariant_cast<QColor>(v.at(i));
1495 c[i] = pal.color((QPalette::ColorRole)(v.at(i).toInt()));
1500 for (i = 0; i < qMin(d->values.count(), 4); i++) {
1501 ColorData color = parseColorValue(d->values.at(i));
1502 if(color.type == ColorData::Role) {
1503 v += QVariant::fromValue<int>(color.role);
1504 c[i] = pal.color((QPalette::ColorRole)(color.role));
1506 v += QVariant::fromValue<QColor>(color.color);
1513 if (i == 0) c[0] = c[1] = c[2] = c[3] = QColor();
1514 else if (i == 1) c[3] = c[2] = c[1] = c[0];
1515 else if (i == 2) c[2] = c[0], c[3] = c[1];
1516 else if (i == 3) c[3] = c[1];
1519 BorderStyle Declaration::styleValue() const
1521 if (d->values.count() != 1)
1522 return BorderStyle_None;
1523 return parseStyleValue(d->values.at(0));
1526 void Declaration::styleValues(BorderStyle *s) const
1529 for (i = 0; i < qMin(d->values.count(), 4); i++)
1530 s[i] = parseStyleValue(d->values.at(i));
1531 if (i == 0) s[0] = s[1] = s[2] = s[3] = BorderStyle_None;
1532 else if (i == 1) s[3] = s[2] = s[1] = s[0];
1533 else if (i == 2) s[2] = s[0], s[3] = s[1];
1534 else if (i == 3) s[3] = s[1];
1537 Repeat Declaration::repeatValue() const
1539 if (d->parsed.isValid())
1540 return static_cast<Repeat>(d->parsed.toInt());
1541 if (d->values.count() != 1)
1542 return Repeat_Unknown;
1543 int v = findKnownValue(d->values.at(0).variant.toString(),
1544 repeats, NumKnownRepeats);
1546 return static_cast<Repeat>(v);
1549 Origin Declaration::originValue() const
1551 if (d->parsed.isValid())
1552 return static_cast<Origin>(d->parsed.toInt());
1553 if (d->values.count() != 1)
1554 return Origin_Unknown;
1555 int v = findKnownValue(d->values.at(0).variant.toString(),
1556 origins, NumKnownOrigins);
1558 return static_cast<Origin>(v);
1561 PositionMode Declaration::positionValue() const
1563 if (d->parsed.isValid())
1564 return static_cast<PositionMode>(d->parsed.toInt());
1565 if (d->values.count() != 1)
1566 return PositionMode_Unknown;
1567 int v = findKnownValue(d->values.at(0).variant.toString(),
1568 positions, NumKnownPositionModes);
1570 return static_cast<PositionMode>(v);
1573 Attachment Declaration::attachmentValue() const
1575 if (d->parsed.isValid())
1576 return static_cast<Attachment>(d->parsed.toInt());
1577 if (d->values.count() != 1)
1578 return Attachment_Unknown;
1579 int v = findKnownValue(d->values.at(0).variant.toString(),
1580 attachments, NumKnownAttachments);
1582 return static_cast<Attachment>(v);
1585 int Declaration::styleFeaturesValue() const
1587 Q_ASSERT(d->propertyId == QtStyleFeatures);
1588 if (d->parsed.isValid())
1589 return d->parsed.toInt();
1590 int features = StyleFeature_None;
1591 for (int i = 0; i < d->values.count(); i++) {
1592 features |= static_cast<int>(findKnownValue(d->values.value(i).variant.toString(),
1593 styleFeatures, NumKnownStyleFeatures));
1595 d->parsed = features;
1599 QString Declaration::uriValue() const
1601 if (d->values.isEmpty() || d->values.at(0).type != Value::Uri)
1603 return d->values.at(0).variant.toString();
1606 Qt::Alignment Declaration::alignmentValue() const
1608 if (d->parsed.isValid())
1609 return Qt::Alignment(d->parsed.toInt());
1610 if (d->values.isEmpty() || d->values.count() > 2)
1611 return Qt::AlignLeft | Qt::AlignTop;
1613 Qt::Alignment v = parseAlignment(d->values.constData(), d->values.count());
1618 void Declaration::borderImageValue(QString *image, int *cuts,
1619 TileMode *h, TileMode *v) const
1621 *image = uriValue();
1622 for (int i = 0; i < 4; i++)
1624 *h = *v = TileMode_Stretch;
1626 if (d->values.count() < 2)
1629 if (d->values.at(1).type == Value::Number) { // cuts!
1631 for (i = 0; i < qMin(d->values.count()-1, 4); i++) {
1632 const Value& v = d->values.at(i+1);
1633 if (v.type != Value::Number)
1635 cuts[i] = v.variant.toString().toInt();
1637 if (i == 0) cuts[0] = cuts[1] = cuts[2] = cuts[3] = 0;
1638 else if (i == 1) cuts[3] = cuts[2] = cuts[1] = cuts[0];
1639 else if (i == 2) cuts[2] = cuts[0], cuts[3] = cuts[1];
1640 else if (i == 3) cuts[3] = cuts[1];
1643 if (d->values.last().type == Value::Identifier) {
1644 *v = static_cast<TileMode>(findKnownValue(d->values.last().variant.toString(),
1645 tileModes, NumKnownTileModes));
1647 if (d->values[d->values.count() - 2].type == Value::Identifier) {
1648 *h = static_cast<TileMode>
1649 (findKnownValue(d->values[d->values.count()-2].variant.toString(),
1650 tileModes, NumKnownTileModes));
1655 QIcon Declaration::iconValue() const
1657 if (d->parsed.isValid())
1658 return qvariant_cast<QIcon>(d->parsed);
1661 for (int i = 0; i < d->values.count();) {
1662 const Value &value = d->values.at(i++);
1663 if (value.type != Value::Uri)
1665 QString uri = value.variant.toString();
1666 QIcon::Mode mode = QIcon::Normal;
1667 QIcon::State state = QIcon::Off;
1668 for (int j = 0; j < 2; j++) {
1669 if (i != d->values.count() && d->values.at(i).type == Value::KnownIdentifier) {
1670 switch (d->values.at(i).variant.toInt()) {
1671 case Value_Disabled: mode = QIcon::Disabled; break;
1672 case Value_Active: mode = QIcon::Active; break;
1673 case Value_Selected: mode = QIcon::Selected; break;
1674 case Value_Normal: mode = QIcon::Normal; break;
1675 case Value_On: state = QIcon::On; break;
1676 case Value_Off: state = QIcon::Off; break;
1685 // QIcon is soo broken
1689 icon.addPixmap(uri, mode, state);
1691 if (i == d->values.count())
1694 if (d->values.at(i).type == Value::TermOperatorComma)
1698 d->parsed = QVariant::fromValue<QIcon>(icon);
1702 ///////////////////////////////////////////////////////////////////////////////
1704 int Selector::specificity() const
1707 for (int i = 0; i < basicSelectors.count(); ++i) {
1708 const BasicSelector &sel = basicSelectors.at(i);
1709 if (!sel.elementName.isEmpty())
1712 val += (sel.pseudos.count() + sel.attributeSelectors.count()) * 0x10;
1713 val += sel.ids.count() * 0x100;
1718 QString Selector::pseudoElement() const
1720 const BasicSelector& bs = basicSelectors.last();
1721 if (!bs.pseudos.isEmpty() && bs.pseudos.at(0).type == PseudoClass_Unknown)
1722 return bs.pseudos.at(0).name;
1726 quint64 Selector::pseudoClass(quint64 *negated) const
1728 const BasicSelector& bs = basicSelectors.last();
1729 if (bs.pseudos.isEmpty())
1730 return PseudoClass_Unspecified;
1731 quint64 pc = PseudoClass_Unknown;
1732 for (int i = !pseudoElement().isEmpty(); i < bs.pseudos.count(); i++) {
1733 const Pseudo &pseudo = bs.pseudos.at(i);
1734 if (pseudo.type == PseudoClass_Unknown)
1735 return PseudoClass_Unknown;
1736 if (!pseudo.negated)
1739 *negated |= pseudo.type;
1744 ///////////////////////////////////////////////////////////////////////////////
1746 void StyleSheet::buildIndexes(Qt::CaseSensitivity nameCaseSensitivity)
1748 QVector<StyleRule> universals;
1749 for (int i = 0; i < styleRules.count(); ++i) {
1750 const StyleRule &rule = styleRules.at(i);
1751 QVector<Selector> universalsSelectors;
1752 for (int j = 0; j < rule.selectors.count(); ++j) {
1753 const Selector& selector = rule.selectors.at(j);
1755 if (selector.basicSelectors.isEmpty())
1758 if (selector.basicSelectors.at(0).relationToNext == BasicSelector::NoRelation) {
1759 if (selector.basicSelectors.count() != 1)
1761 } else if (selector.basicSelectors.count() <= 1) {
1765 const BasicSelector &sel = selector.basicSelectors.at(selector.basicSelectors.count() - 1);
1767 if (!sel.ids.isEmpty()) {
1769 nr.selectors += selector;
1770 nr.declarations = rule.declarations;
1772 idIndex.insert(sel.ids.at(0), nr);
1773 } else if (!sel.elementName.isEmpty()) {
1775 nr.selectors += selector;
1776 nr.declarations = rule.declarations;
1778 QString name = sel.elementName;
1779 if (nameCaseSensitivity == Qt::CaseInsensitive)
1780 name=name.toLower();
1781 nameIndex.insert(name, nr);
1783 universalsSelectors += selector;
1786 if (!universalsSelectors.isEmpty()) {
1788 nr.selectors = universalsSelectors;
1789 nr.declarations = rule.declarations;
1794 styleRules = universals;
1797 ///////////////////////////////////////////////////////////////////////////////
1799 StyleSelector::~StyleSelector()
1803 bool StyleSelector::nodeNameEquals(NodePtr node, const QString& nodeName) const
1805 return nodeNames(node).contains(nodeName, nameCaseSensitivity);
1808 QStringList StyleSelector::nodeIds(NodePtr node) const
1810 return QStringList(attribute(node, QLatin1String("id")));
1813 bool StyleSelector::selectorMatches(const Selector &selector, NodePtr node)
1815 if (selector.basicSelectors.isEmpty())
1818 if (selector.basicSelectors.at(0).relationToNext == BasicSelector::NoRelation) {
1819 if (selector.basicSelectors.count() != 1)
1821 return basicSelectorMatches(selector.basicSelectors.at(0), node);
1823 if (selector.basicSelectors.count() <= 1)
1826 int i = selector.basicSelectors.count() - 1;
1827 node = duplicateNode(node);
1830 BasicSelector sel = selector.basicSelectors.at(i);
1832 match = basicSelectorMatches(sel, node);
1834 if (sel.relationToNext == BasicSelector::MatchNextSelectorIfParent
1835 || i == selector.basicSelectors.count() - 1) // first element must always match!
1839 if (match || sel.relationToNext != BasicSelector::MatchNextSelectorIfAncestor)
1845 sel = selector.basicSelectors.at(i);
1846 if (sel.relationToNext == BasicSelector::MatchNextSelectorIfAncestor
1847 || sel.relationToNext == BasicSelector::MatchNextSelectorIfParent) {
1849 NodePtr nextParent = parentNode(node);
1852 } else if (sel.relationToNext == BasicSelector::MatchNextSelectorIfPreceeds) {
1853 NodePtr previousSibling = previousSiblingNode(node);
1855 node = previousSibling;
1857 if (isNullNode(node)) {
1861 } while (i >= 0 && (match || sel.relationToNext == BasicSelector::MatchNextSelectorIfAncestor));
1868 bool StyleSelector::basicSelectorMatches(const BasicSelector &sel, NodePtr node)
1870 if (!sel.attributeSelectors.isEmpty()) {
1871 if (!hasAttributes(node))
1874 for (int i = 0; i < sel.attributeSelectors.count(); ++i) {
1875 const QCss::AttributeSelector &a = sel.attributeSelectors.at(i);
1877 const QString attrValue = attribute(node, a.name);
1878 if (attrValue.isNull())
1881 if (a.valueMatchCriterium == QCss::AttributeSelector::MatchContains) {
1883 QStringList lst = attrValue.split(QLatin1Char(' '));
1884 if (!lst.contains(a.value))
1887 (a.valueMatchCriterium == QCss::AttributeSelector::MatchEqual
1888 && attrValue != a.value)
1890 (a.valueMatchCriterium == QCss::AttributeSelector::MatchBeginsWith
1891 && !attrValue.startsWith(a.value))
1897 if (!sel.elementName.isEmpty()
1898 && !nodeNameEquals(node, sel.elementName))
1901 if (!sel.ids.isEmpty()
1902 && sel.ids != nodeIds(node))
1908 void StyleSelector::matchRule(NodePtr node, const StyleRule &rule, StyleSheetOrigin origin,
1909 int depth, QMap<uint, StyleRule> *weightedRules)
1911 for (int j = 0; j < rule.selectors.count(); ++j) {
1912 const Selector& selector = rule.selectors.at(j);
1913 if (selectorMatches(selector, node)) {
1914 uint weight = rule.order
1915 + selector.specificity() *0x100
1916 + (uint(origin) + depth)*0x100000;
1917 StyleRule newRule = rule;
1918 if(rule.selectors.count() > 1) {
1919 newRule.selectors.resize(1);
1920 newRule.selectors[0] = selector;
1922 //We might have rules with the same weight if they came from a rule with several selectors
1923 weightedRules->insertMulti(weight, newRule);
1928 // Returns style rules that are in ascending order of specificity
1929 // Each of the StyleRule returned will contain exactly one Selector
1930 QVector<StyleRule> StyleSelector::styleRulesForNode(NodePtr node)
1932 QVector<StyleRule> rules;
1933 if (styleSheets.isEmpty())
1936 QMap<uint, StyleRule> weightedRules; // (spec, rule) that will be sorted below
1938 //prune using indexed stylesheet
1939 for (int sheetIdx = 0; sheetIdx < styleSheets.count(); ++sheetIdx) {
1940 const StyleSheet &styleSheet = styleSheets.at(sheetIdx);
1941 for (int i = 0; i < styleSheet.styleRules.count(); ++i) {
1942 matchRule(node, styleSheet.styleRules.at(i), styleSheet.origin, styleSheet.depth, &weightedRules);
1945 if (!styleSheet.idIndex.isEmpty()) {
1946 QStringList ids = nodeIds(node);
1947 for (int i = 0; i < ids.count(); i++) {
1948 const QString &key = ids.at(i);
1949 QMultiHash<QString, StyleRule>::const_iterator it = styleSheet.idIndex.constFind(key);
1950 while (it != styleSheet.idIndex.constEnd() && it.key() == key) {
1951 matchRule(node, it.value(), styleSheet.origin, styleSheet.depth, &weightedRules);
1956 if (!styleSheet.nameIndex.isEmpty()) {
1957 QStringList names = nodeNames(node);
1958 for (int i = 0; i < names.count(); i++) {
1959 QString name = names.at(i);
1960 if (nameCaseSensitivity == Qt::CaseInsensitive)
1961 name = name.toLower();
1962 QMultiHash<QString, StyleRule>::const_iterator it = styleSheet.nameIndex.constFind(name);
1963 while (it != styleSheet.nameIndex.constEnd() && it.key() == name) {
1964 matchRule(node, it.value(), styleSheet.origin, styleSheet.depth, &weightedRules);
1969 if (!medium.isEmpty()) {
1970 for (int i = 0; i < styleSheet.mediaRules.count(); ++i) {
1971 if (styleSheet.mediaRules.at(i).media.contains(medium, Qt::CaseInsensitive)) {
1972 for (int j = 0; j < styleSheet.mediaRules.at(i).styleRules.count(); ++j) {
1973 matchRule(node, styleSheet.mediaRules.at(i).styleRules.at(j), styleSheet.origin,
1974 styleSheet.depth, &weightedRules);
1981 rules.reserve(weightedRules.count());
1982 QMap<uint, StyleRule>::const_iterator it = weightedRules.constBegin();
1983 for ( ; it != weightedRules.constEnd() ; ++it)
1989 // for qtexthtmlparser which requires just the declarations with Enabled state
1990 // and without pseudo elements
1991 QVector<Declaration> StyleSelector::declarationsForNode(NodePtr node, const char *extraPseudo)
1993 QVector<Declaration> decls;
1994 QVector<StyleRule> rules = styleRulesForNode(node);
1995 for (int i = 0; i < rules.count(); i++) {
1996 const Selector& selector = rules.at(i).selectors.at(0);
1997 const QString pseudoElement = selector.pseudoElement();
1999 if (extraPseudo && pseudoElement == QLatin1String(extraPseudo)) {
2000 decls += rules.at(i).declarations;
2004 if (!pseudoElement.isEmpty()) // skip rules with pseudo elements
2006 quint64 pseudoClass = selector.pseudoClass();
2007 if (pseudoClass == PseudoClass_Enabled || pseudoClass == PseudoClass_Unspecified)
2008 decls += rules.at(i).declarations;
2013 static inline bool isHexDigit(const char c)
2015 return (c >= '0' && c <= '9')
2016 || (c >= 'a' && c <= 'f')
2017 || (c >= 'A' && c <= 'F')
2021 QString Scanner::preprocess(const QString &input, bool *hasEscapeSequences)
2023 QString output = input;
2025 if (hasEscapeSequences)
2026 *hasEscapeSequences = false;
2029 while (i < output.size()) {
2030 if (output.at(i) == QLatin1Char('\\')) {
2033 // test for unicode hex escape
2035 const int hexStart = i;
2036 while (i < output.size()
2037 && isHexDigit(output.at(i).toLatin1())
2042 if (hexCount == 0) {
2043 if (hasEscapeSequences)
2044 *hasEscapeSequences = true;
2048 hexCount = qMin(hexCount, 6);
2050 ushort code = output.mid(hexStart, hexCount).toUShort(&ok, 16);
2052 output.replace(hexStart - 1, hexCount + 1, QChar(code));
2064 int QCssScanner_Generated::handleCommentStart()
2066 while (pos < input.size() - 1) {
2067 if (input.at(pos) == QLatin1Char('*')
2068 && input.at(pos + 1) == QLatin1Char('/')) {
2077 void Scanner::scan(const QString &preprocessedInput, QVector<Symbol> *symbols)
2079 QCssScanner_Generated scanner(preprocessedInput);
2081 int tok = scanner.lex();
2083 sym.token = static_cast<QCss::TokenType>(tok);
2084 sym.text = scanner.input;
2085 sym.start = scanner.lexemStart;
2086 sym.len = scanner.lexemLength;
2087 symbols->append(sym);
2088 tok = scanner.lex();
2092 QString Symbol::lexem() const
2096 result.reserve(len);
2097 for (int i = 0; i < len; ++i) {
2098 if (text.at(start + i) == QLatin1Char('\\') && i < len - 1)
2100 result += text.at(start + i);
2105 Parser::Parser(const QString &css, bool isFile)
2114 hasEscapeSequences = false;
2117 void Parser::init(const QString &css, bool isFile)
2119 QString styleSheet = css;
2122 if (file.open(QFile::ReadOnly)) {
2123 sourcePath = QFileInfo(styleSheet).absolutePath() + QLatin1Char('/');
2124 QTextStream stream(&file);
2125 styleSheet = stream.readAll();
2127 qWarning() << "QCss::Parser - Failed to load file " << css;
2134 hasEscapeSequences = false;
2137 Scanner::scan(Scanner::preprocess(styleSheet, &hasEscapeSequences), &symbols);
2142 bool Parser::parse(StyleSheet *styleSheet, Qt::CaseSensitivity nameCaseSensitivity)
2144 if (testTokenAndEndsWith(ATKEYWORD_SYM, QLatin1String("charset"))) {
2145 if (!next(STRING)) return false;
2146 if (!next(SEMICOLON)) return false;
2149 while (test(S) || test(CDO) || test(CDC)) {}
2151 while (testImport()) {
2153 if (!parseImport(&rule)) return false;
2154 styleSheet->importRules.append(rule);
2155 while (test(S) || test(CDO) || test(CDC)) {}
2161 if (!parseMedia(&rule)) return false;
2162 styleSheet->mediaRules.append(rule);
2163 } else if (testPage()) {
2165 if (!parsePage(&rule)) return false;
2166 styleSheet->pageRules.append(rule);
2167 } else if (testRuleset()) {
2169 if (!parseRuleset(&rule)) return false;
2170 styleSheet->styleRules.append(rule);
2171 } else if (test(ATKEYWORD_SYM)) {
2172 if (!until(RBRACE)) return false;
2173 } else if (hasNext()) {
2176 while (test(S) || test(CDO) || test(CDC)) {}
2177 } while (hasNext());
2178 styleSheet->buildIndexes(nameCaseSensitivity);
2182 Symbol Parser::errorSymbol()
2184 if (errorIndex == -1) return Symbol();
2185 return symbols.at(errorIndex);
2188 static inline void removeOptionalQuotes(QString *str)
2190 if (!str->startsWith(QLatin1Char('\''))
2191 && !str->startsWith(QLatin1Char('\"')))
2197 bool Parser::parseImport(ImportRule *importRule)
2202 importRule->href = lexem();
2204 if (!testAndParseUri(&importRule->href)) return false;
2206 removeOptionalQuotes(&importRule->href);
2211 if (!parseMedium(&importRule->media)) return false;
2213 while (test(COMMA)) {
2215 if (!parseNextMedium(&importRule->media)) return false;
2219 if (!next(SEMICOLON)) return false;
2225 bool Parser::parseMedia(MediaRule *mediaRule)
2229 if (!parseNextMedium(&mediaRule->media)) return false;
2230 } while (test(COMMA));
2232 if (!next(LBRACE)) return false;
2235 while (testRuleset()) {
2237 if (!parseRuleset(&rule)) return false;
2238 mediaRule->styleRules.append(rule);
2241 if (!next(RBRACE)) return false;
2246 bool Parser::parseMedium(QStringList *media)
2248 media->append(lexem());
2253 bool Parser::parsePage(PageRule *pageRule)
2257 if (testPseudoPage())
2258 if (!parsePseudoPage(&pageRule->selector)) return false;
2261 if (!next(LBRACE)) return false;
2266 if (!parseNextDeclaration(&decl)) return false;
2267 if (!decl.isEmpty())
2268 pageRule->declarations.append(decl);
2269 } while (test(SEMICOLON));
2271 if (!next(RBRACE)) return false;
2276 bool Parser::parsePseudoPage(QString *selector)
2278 if (!next(IDENT)) return false;
2279 *selector = lexem();
2283 bool Parser::parseNextOperator(Value *value)
2285 if (!hasNext()) return true;
2287 case SLASH: value->type = Value::TermOperatorSlash; skipSpace(); break;
2288 case COMMA: value->type = Value::TermOperatorComma; skipSpace(); break;
2289 default: prev(); break;
2294 bool Parser::parseCombinator(BasicSelector::Relation *relation)
2296 *relation = BasicSelector::NoRelation;
2297 if (lookup() == S) {
2298 *relation = BasicSelector::MatchNextSelectorIfAncestor;
2304 *relation = BasicSelector::MatchNextSelectorIfPreceeds;
2305 } else if (test(GREATER)) {
2306 *relation = BasicSelector::MatchNextSelectorIfParent;
2312 bool Parser::parseProperty(Declaration *decl)
2314 decl->d->property = lexem();
2315 decl->d->propertyId = static_cast<Property>(findKnownValue(decl->d->property, properties, NumProperties));
2320 bool Parser::parseRuleset(StyleRule *styleRule)
2323 if (!parseSelector(&sel)) return false;
2324 styleRule->selectors.append(sel);
2326 while (test(COMMA)) {
2329 if (!parseNextSelector(&sel)) return false;
2330 styleRule->selectors.append(sel);
2334 if (!next(LBRACE)) return false;
2335 const int declarationStart = index;
2340 const int rewind = index;
2341 if (!parseNextDeclaration(&decl)) {
2343 const bool foundSemicolon = until(SEMICOLON);
2344 const int semicolonIndex = index;
2346 index = declarationStart;
2347 const bool foundRBrace = until(RBRACE);
2349 if (foundSemicolon && semicolonIndex < index) {
2350 decl = Declaration();
2351 index = semicolonIndex - 1;
2357 if (!decl.isEmpty())
2358 styleRule->declarations.append(decl);
2359 } while (test(SEMICOLON));
2361 if (!next(RBRACE)) return false;
2366 bool Parser::parseSelector(Selector *sel)
2368 BasicSelector basicSel;
2369 if (!parseSimpleSelector(&basicSel)) return false;
2370 while (testCombinator()) {
2371 if (!parseCombinator(&basicSel.relationToNext)) return false;
2373 if (!testSimpleSelector()) break;
2374 sel->basicSelectors.append(basicSel);
2376 basicSel = BasicSelector();
2377 if (!parseSimpleSelector(&basicSel)) return false;
2379 sel->basicSelectors.append(basicSel);
2383 bool Parser::parseSimpleSelector(BasicSelector *basicSel)
2386 if (lookupElementName()) {
2387 if (!parseElementName(&basicSel->elementName)) return false;
2397 QString theid = lexem();
2398 // chop off leading #
2400 basicSel->ids.append(theid);
2402 } else if (testClass()) {
2404 AttributeSelector a;
2405 a.name = QLatin1String("class");
2406 a.valueMatchCriterium = AttributeSelector::MatchContains;
2407 if (!parseClass(&a.value)) return false;
2408 basicSel->attributeSelectors.append(a);
2409 } else if (testAttrib()) {
2411 AttributeSelector a;
2412 if (!parseAttrib(&a)) return false;
2413 basicSel->attributeSelectors.append(a);
2414 } else if (testPseudo()) {
2417 if (!parsePseudo(&ps)) return false;
2418 basicSel->pseudos.append(ps);
2420 if (onceMore) ++count;
2422 return count >= minCount;
2425 bool Parser::parseClass(QString *name)
2427 if (!next(IDENT)) return false;
2432 bool Parser::parseElementName(QString *name)
2435 case STAR: name->clear(); break;
2436 case IDENT: *name = lexem(); break;
2437 default: return false;
2442 bool Parser::parseAttrib(AttributeSelector *attr)
2445 if (!next(IDENT)) return false;
2446 attr->name = lexem();
2450 attr->valueMatchCriterium = AttributeSelector::MatchEqual;
2451 } else if (test(INCLUDES)) {
2452 attr->valueMatchCriterium = AttributeSelector::MatchContains;
2453 } else if (test(DASHMATCH)) {
2454 attr->valueMatchCriterium = AttributeSelector::MatchBeginsWith;
2456 return next(RBRACKET);
2461 if (!test(IDENT) && !test(STRING)) return false;
2462 attr->value = unquotedLexem();
2465 return next(RBRACKET);
2468 bool Parser::parsePseudo(Pseudo *pseudo)
2471 pseudo->negated = test(EXCLAMATION_SYM);
2473 pseudo->name = lexem();
2474 pseudo->type = static_cast<quint64>(findKnownValue(pseudo->name, pseudos, NumPseudos));
2477 if (!next(FUNCTION)) return false;
2478 pseudo->function = lexem();
2479 // chop off trailing parenthesis
2480 pseudo->function.chop(1);
2482 if (!test(IDENT)) return false;
2483 pseudo->name = lexem();
2485 return next(RPAREN);
2488 bool Parser::parseNextDeclaration(Declaration *decl)
2490 if (!testProperty())
2491 return true; // not an error!
2492 if (!parseProperty(decl)) return false;
2493 if (!next(COLON)) return false;
2495 if (!parseNextExpr(&decl->d->values)) return false;
2497 if (!parsePrio(decl)) return false;
2501 bool Parser::testPrio()
2503 const int rewind = index;
2504 if (!test(EXCLAMATION_SYM)) return false;
2510 if (lexem().compare(QLatin1String("important"), Qt::CaseInsensitive) != 0) {
2517 bool Parser::parsePrio(Declaration *declaration)
2519 declaration->d->important = true;
2524 bool Parser::parseExpr(QVector<Value> *values)
2527 if (!parseTerm(&val)) return false;
2528 values->append(val);
2533 if (!parseNextOperator(&val)) return false;
2534 if (val.type != QCss::Value::Unknown)
2535 values->append(val);
2539 if (!parseTerm(&val)) return false;
2540 values->append(val);
2546 bool Parser::testTerm()
2548 return test(PLUS) || test(MINUS)
2558 bool Parser::parseTerm(Value *value)
2560 QString str = lexem();
2561 bool haveUnary = false;
2562 if (lookup() == PLUS || lookup() == MINUS) {
2564 if (!hasNext()) return false;
2569 value->variant = str;
2570 value->type = QCss::Value::String;
2573 value->type = Value::Number;
2574 value->variant.convert(QVariant::Double);
2577 value->type = Value::Percentage;
2578 str.chop(1); // strip off %
2579 value->variant = str;
2582 value->type = Value::Length;
2586 if (haveUnary) return false;
2587 value->type = Value::String;
2590 value->variant = str;
2593 if (haveUnary) return false;
2594 value->type = Value::Identifier;
2595 const int theid = findKnownValue(str, values, NumKnownValues);
2597 value->type = Value::KnownIdentifier;
2598 value->variant = theid;
2603 if (haveUnary) return false;
2605 if (testHexColor()) {
2607 if (!parseHexColor(&col)) return false;
2608 value->type = Value::Color;
2609 value->variant = col;
2610 } else if (testFunction()) {
2612 if (!parseFunction(&name, &args)) return false;
2613 if (name == QLatin1String("url")) {
2614 value->type = Value::Uri;
2615 removeOptionalQuotes(&args);
2616 if (QFileInfo(args).isRelative() && !sourcePath.isEmpty()) {
2617 args.prepend(sourcePath);
2619 value->variant = args;
2621 value->type = Value::Function;
2622 value->variant = QStringList() << name << args;
2625 return recordError();
2634 bool Parser::parseFunction(QString *name, QString *args)
2639 const int start = index;
2640 if (!until(RPAREN)) return false;
2641 for (int i = start; i < index - 1; ++i)
2642 args->append(symbols.at(i).lexem());
2644 if (!nextExpr(&arguments)) return false;
2645 if (!next(RPAREN)) return false;
2651 bool Parser::parseHexColor(QColor *col)
2653 col->setNamedColor(lexem());
2654 if (!col->isValid()) {
2655 qWarning("QCssParser::parseHexColor: Unknown color name '%s'",lexem().toLatin1().constData());
2662 bool Parser::testAndParseUri(QString *uri)
2664 const int rewind = index;
2665 if (!testFunction()) return false;
2668 if (!parseFunction(&name, &args)) {
2672 if (name.toLower() != QLatin1String("url")) {
2677 removeOptionalQuotes(uri);
2681 bool Parser::testSimpleSelector()
2683 return testElementName()
2690 bool Parser::next(QCss::TokenType t)
2692 if (hasNext() && next() == t)
2694 return recordError();
2697 bool Parser::test(QCss::TokenType t)
2699 if (index >= symbols.count())
2701 if (symbols.at(index).token == t) {
2708 QString Parser::unquotedLexem() const
2710 QString s = lexem();
2711 if (lookup() == STRING) {
2718 QString Parser::lexemUntil(QCss::TokenType t)
2721 while (hasNext() && next() != t)
2722 lexem += symbol().lexem();
2726 bool Parser::until(QCss::TokenType target, QCss::TokenType target2)
2732 switch(symbols.at(index-1).token) {
2733 case LBRACE: ++braceCount; break;
2734 case LBRACKET: ++brackCount; break;
2736 case LPAREN: ++parenCount; break;
2740 while (index < symbols.size()) {
2741 QCss::TokenType t = symbols.at(index++).token;
2743 case LBRACE: ++braceCount; break;
2744 case RBRACE: --braceCount; break;
2745 case LBRACKET: ++brackCount; break;
2746 case RBRACKET: --brackCount; break;
2748 case LPAREN: ++parenCount; break;
2749 case RPAREN: --parenCount; break;
2752 if ((t == target || (target2 != NONE && t == target2))
2758 if (braceCount < 0 || brackCount < 0 || parenCount < 0) {
2766 bool Parser::testTokenAndEndsWith(QCss::TokenType t, QLatin1String str)
2768 if (!test(t)) return false;
2769 if (!lexem().endsWith(str, Qt::CaseInsensitive)) {
2777 #endif // QT_NO_CSSPARSER