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