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

Last change on this file since 219 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

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