|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 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 for (int i = start; i < values.count(); ++i) { |
|
1133 const Value &v = values.at(i); |
|
1134 if (v.type == Value::TermOperatorComma) { |
|
1135 family += QLatin1Char(','); |
|
1136 continue; |
|
1137 } |
|
1138 const QString str = v.variant.toString(); |
|
1139 if (str.isEmpty()) |
|
1140 break; |
|
1141 family += str; |
|
1142 family += QLatin1Char(' '); |
|
1143 } |
|
1144 family = family.simplified(); |
|
1145 if (family.isEmpty()) |
|
1146 return false; |
|
1147 font->setFamily(family); |
|
1148 return true; |
|
1149 } |
|
1150 |
|
1151 static void setTextDecorationFromValues(const QVector<Value> &values, QFont *font) |
|
1152 { |
|
1153 for (int i = 0; i < values.count(); ++i) { |
|
1154 if (values.at(i).type != Value::KnownIdentifier) |
|
1155 continue; |
|
1156 switch (values.at(i).variant.toInt()) { |
|
1157 case Value_Underline: font->setUnderline(true); break; |
|
1158 case Value_Overline: font->setOverline(true); break; |
|
1159 case Value_LineThrough: font->setStrikeOut(true); break; |
|
1160 case Value_None: |
|
1161 font->setUnderline(false); |
|
1162 font->setOverline(false); |
|
1163 font->setStrikeOut(false); |
|
1164 break; |
|
1165 default: break; |
|
1166 } |
|
1167 } |
|
1168 } |
|
1169 |
|
1170 static void parseShorthandFontProperty(const QVector<Value> &values, QFont *font, int *fontSizeAdjustment) |
|
1171 { |
|
1172 font->setStyle(QFont::StyleNormal); |
|
1173 font->setWeight(QFont::Normal); |
|
1174 *fontSizeAdjustment = -255; |
|
1175 |
|
1176 int i = 0; |
|
1177 while (i < values.count()) { |
|
1178 if (setFontStyleFromValue(values.at(i), font) |
|
1179 || setFontWeightFromValue(values.at(i), font)) |
|
1180 ++i; |
|
1181 else |
|
1182 break; |
|
1183 } |
|
1184 |
|
1185 if (i < values.count()) { |
|
1186 setFontSizeFromValue(values.at(i), font, fontSizeAdjustment); |
|
1187 ++i; |
|
1188 } |
|
1189 |
|
1190 if (i < values.count()) { |
|
1191 setFontFamilyFromValues(values, font, i); |
|
1192 } |
|
1193 } |
|
1194 |
|
1195 static void setFontVariantFromValue(const Value &value, QFont *font) |
|
1196 { |
|
1197 if (value.type == Value::KnownIdentifier) { |
|
1198 switch (value.variant.toInt()) { |
|
1199 case Value_Normal: font->setCapitalization(QFont::MixedCase); break; |
|
1200 case Value_SmallCaps: font->setCapitalization(QFont::SmallCaps); break; |
|
1201 default: break; |
|
1202 } |
|
1203 } |
|
1204 } |
|
1205 |
|
1206 static void setTextTransformFromValue(const Value &value, QFont *font) |
|
1207 { |
|
1208 if (value.type == Value::KnownIdentifier) { |
|
1209 switch (value.variant.toInt()) { |
|
1210 case Value_None: font->setCapitalization(QFont::MixedCase); break; |
|
1211 case Value_Uppercase: font->setCapitalization(QFont::AllUppercase); break; |
|
1212 case Value_Lowercase: font->setCapitalization(QFont::AllLowercase); break; |
|
1213 default: break; |
|
1214 } |
|
1215 } |
|
1216 } |
|
1217 |
|
1218 bool ValueExtractor::extractFont(QFont *font, int *fontSizeAdjustment) |
|
1219 { |
|
1220 if (fontExtracted) { |
|
1221 *font = f; |
|
1222 *fontSizeAdjustment = adjustment; |
|
1223 return fontExtracted == 1; |
|
1224 } |
|
1225 |
|
1226 bool hit = false; |
|
1227 for (int i = 0; i < declarations.count(); ++i) { |
|
1228 const Declaration &decl = declarations.at(i); |
|
1229 if (decl.d->values.isEmpty()) |
|
1230 continue; |
|
1231 const Value &val = decl.d->values.at(0); |
|
1232 switch (decl.d->propertyId) { |
|
1233 case FontSize: setFontSizeFromValue(val, font, fontSizeAdjustment); break; |
|
1234 case FontStyle: setFontStyleFromValue(val, font); break; |
|
1235 case FontWeight: setFontWeightFromValue(val, font); break; |
|
1236 case FontFamily: setFontFamilyFromValues(decl.d->values, font); break; |
|
1237 case TextDecoration: setTextDecorationFromValues(decl.d->values, font); break; |
|
1238 case Font: parseShorthandFontProperty(decl.d->values, font, fontSizeAdjustment); break; |
|
1239 case FontVariant: setFontVariantFromValue(val, font); break; |
|
1240 case TextTransform: setTextTransformFromValue(val, font); break; |
|
1241 default: continue; |
|
1242 } |
|
1243 hit = true; |
|
1244 } |
|
1245 |
|
1246 f = *font; |
|
1247 adjustment = *fontSizeAdjustment; |
|
1248 fontExtracted = hit ? 1 : 2; |
|
1249 return hit; |
|
1250 } |
|
1251 |
|
1252 bool ValueExtractor::extractPalette(QBrush *fg, QBrush *sfg, QBrush *sbg, QBrush *abg) |
|
1253 { |
|
1254 bool hit = false; |
|
1255 for (int i = 0; i < declarations.count(); ++i) { |
|
1256 const Declaration &decl = declarations.at(i); |
|
1257 switch (decl.d->propertyId) { |
|
1258 case Color: *fg = decl.brushValue(pal); break; |
|
1259 case QtSelectionForeground: *sfg = decl.brushValue(pal); break; |
|
1260 case QtSelectionBackground: *sbg = decl.brushValue(pal); break; |
|
1261 case QtAlternateBackground: *abg = decl.brushValue(pal); break; |
|
1262 default: continue; |
|
1263 } |
|
1264 hit = true; |
|
1265 } |
|
1266 return hit; |
|
1267 } |
|
1268 |
|
1269 void ValueExtractor::extractFont() |
|
1270 { |
|
1271 if (fontExtracted) |
|
1272 return; |
|
1273 int dummy = -255; |
|
1274 extractFont(&f, &dummy); |
|
1275 } |
|
1276 |
|
1277 bool ValueExtractor::extractImage(QIcon *icon, Qt::Alignment *a, QSize *size) |
|
1278 { |
|
1279 bool hit = false; |
|
1280 for (int i = 0; i < declarations.count(); ++i) { |
|
1281 const Declaration &decl = declarations.at(i); |
|
1282 switch (decl.d->propertyId) { |
|
1283 case QtImage: |
|
1284 *icon = decl.iconValue(); |
|
1285 if (decl.d->values.count() > 0 && decl.d->values.at(0).type == Value::Uri) { |
|
1286 // try to pull just the size from the image... |
|
1287 QImageReader imageReader(decl.d->values.at(0).variant.toString()); |
|
1288 if ((*size = imageReader.size()).isNull()) { |
|
1289 // but we'll have to load the whole image if the |
|
1290 // format doesn't support just reading the size |
|
1291 *size = imageReader.read().size(); |
|
1292 } |
|
1293 } |
|
1294 break; |
|
1295 case QtImageAlignment: *a = decl.alignmentValue(); break; |
|
1296 default: continue; |
|
1297 } |
|
1298 hit = true; |
|
1299 } |
|
1300 return hit; |
|
1301 } |
|
1302 |
|
1303 /////////////////////////////////////////////////////////////////////////////// |
|
1304 // Declaration |
|
1305 QColor Declaration::colorValue(const QPalette &pal) const |
|
1306 { |
|
1307 if (d->values.count() != 1) |
|
1308 return QColor(); |
|
1309 |
|
1310 if (d->parsed.isValid()) { |
|
1311 if (d->parsed.type() == QVariant::Color) |
|
1312 return qvariant_cast<QColor>(d->parsed); |
|
1313 if (d->parsed.type() == QVariant::Int) |
|
1314 return pal.color((QPalette::ColorRole)(d->parsed.toInt())); |
|
1315 } |
|
1316 |
|
1317 ColorData color = parseColorValue(d->values.at(0)); |
|
1318 if(color.type == ColorData::Role) { |
|
1319 d->parsed = qVariantFromValue<int>(color.role); |
|
1320 return pal.color((QPalette::ColorRole)(color.role)); |
|
1321 } else { |
|
1322 d->parsed = qVariantFromValue<QColor>(color.color); |
|
1323 return color.color; |
|
1324 } |
|
1325 } |
|
1326 |
|
1327 QBrush Declaration::brushValue(const QPalette &pal) const |
|
1328 { |
|
1329 if (d->values.count() != 1) |
|
1330 return QBrush(); |
|
1331 |
|
1332 if (d->parsed.isValid()) { |
|
1333 if (d->parsed.type() == QVariant::Brush) |
|
1334 return qvariant_cast<QBrush>(d->parsed); |
|
1335 if (d->parsed.type() == QVariant::Int) |
|
1336 return pal.color((QPalette::ColorRole)(d->parsed.toInt())); |
|
1337 } |
|
1338 |
|
1339 BrushData data = parseBrushValue(d->values.at(0), pal); |
|
1340 |
|
1341 if(data.type == BrushData::Role) { |
|
1342 d->parsed = qVariantFromValue<int>(data.role); |
|
1343 return pal.color((QPalette::ColorRole)(data.role)); |
|
1344 } else { |
|
1345 if (data.type != BrushData::DependsOnThePalette) |
|
1346 d->parsed = qVariantFromValue<QBrush>(data.brush); |
|
1347 return data.brush; |
|
1348 } |
|
1349 } |
|
1350 |
|
1351 void Declaration::brushValues(QBrush *c, const QPalette &pal) const |
|
1352 { |
|
1353 int needParse = 0x1f; // bits 0..3 say if we should parse the corresponding value. |
|
1354 // the bit 4 say we need to update d->parsed |
|
1355 int i = 0; |
|
1356 if (d->parsed.isValid()) { |
|
1357 needParse = 0; |
|
1358 QList<QVariant> v = d->parsed.toList(); |
|
1359 for (i = 0; i < qMin(v.count(), 4); i++) { |
|
1360 if (v.at(i).type() == QVariant::Brush) { |
|
1361 c[i] = qvariant_cast<QBrush>(v.at(i)); |
|
1362 } else if (v.at(i).type() == QVariant::Int) { |
|
1363 c[i] = pal.color((QPalette::ColorRole)(v.at(i).toInt())); |
|
1364 } else { |
|
1365 needParse |= (1<<i); |
|
1366 } |
|
1367 } |
|
1368 } |
|
1369 if (needParse != 0) { |
|
1370 QList<QVariant> v; |
|
1371 for (i = 0; i < qMin(d->values.count(), 4); i++) { |
|
1372 if (!(needParse & (1<<i))) |
|
1373 continue; |
|
1374 BrushData data = parseBrushValue(d->values.at(i), pal); |
|
1375 if(data.type == BrushData::Role) { |
|
1376 v += qVariantFromValue<int>(data.role); |
|
1377 c[i] = pal.color((QPalette::ColorRole)(data.role)); |
|
1378 } else { |
|
1379 if (data.type != BrushData::DependsOnThePalette) { |
|
1380 v += qVariantFromValue<QBrush>(data.brush); |
|
1381 } else { |
|
1382 v += QVariant(); |
|
1383 } |
|
1384 c[i] = data.brush; |
|
1385 } |
|
1386 } |
|
1387 if (needParse & 0x10) |
|
1388 d->parsed = v; |
|
1389 } |
|
1390 if (i == 0) c[0] = c[1] = c[2] = c[3] = QBrush(); |
|
1391 else if (i == 1) c[3] = c[2] = c[1] = c[0]; |
|
1392 else if (i == 2) c[2] = c[0], c[3] = c[1]; |
|
1393 else if (i == 3) c[3] = c[1]; |
|
1394 } |
|
1395 |
|
1396 bool Declaration::realValue(qreal *real, const char *unit) const |
|
1397 { |
|
1398 if (d->values.count() != 1) |
|
1399 return false; |
|
1400 const Value &v = d->values.at(0); |
|
1401 if (unit && v.type != Value::Length) |
|
1402 return false; |
|
1403 QString s = v.variant.toString(); |
|
1404 if (unit) { |
|
1405 if (!s.endsWith(QLatin1String(unit), Qt::CaseInsensitive)) |
|
1406 return false; |
|
1407 s.chop(qstrlen(unit)); |
|
1408 } |
|
1409 bool ok = false; |
|
1410 qreal val = s.toDouble(&ok); |
|
1411 if (ok) |
|
1412 *real = val; |
|
1413 return ok; |
|
1414 } |
|
1415 |
|
1416 static bool intValueHelper(const Value &v, int *i, const char *unit) |
|
1417 { |
|
1418 if (unit && v.type != Value::Length) |
|
1419 return false; |
|
1420 QString s = v.variant.toString(); |
|
1421 if (unit) { |
|
1422 if (!s.endsWith(QLatin1String(unit), Qt::CaseInsensitive)) |
|
1423 return false; |
|
1424 s.chop(qstrlen(unit)); |
|
1425 } |
|
1426 bool ok = false; |
|
1427 int val = s.toInt(&ok); |
|
1428 if (ok) |
|
1429 *i = val; |
|
1430 return ok; |
|
1431 } |
|
1432 |
|
1433 bool Declaration::intValue(int *i, const char *unit) const |
|
1434 { |
|
1435 if (d->values.count() != 1) |
|
1436 return false; |
|
1437 return intValueHelper(d->values.at(0), i, unit); |
|
1438 } |
|
1439 |
|
1440 QSize Declaration::sizeValue() const |
|
1441 { |
|
1442 if (d->parsed.isValid()) |
|
1443 return qvariant_cast<QSize>(d->parsed); |
|
1444 |
|
1445 int x[2] = { 0, 0 }; |
|
1446 if (d->values.count() > 0) |
|
1447 intValueHelper(d->values.at(0), &x[0], "px"); |
|
1448 if (d->values.count() > 1) |
|
1449 intValueHelper(d->values.at(1), &x[1], "px"); |
|
1450 else |
|
1451 x[1] = x[0]; |
|
1452 QSize size(x[0], x[1]); |
|
1453 d->parsed = qVariantFromValue<QSize>(size); |
|
1454 return size; |
|
1455 } |
|
1456 |
|
1457 QRect Declaration::rectValue() const |
|
1458 { |
|
1459 if (d->values.count() != 1) |
|
1460 return QRect(); |
|
1461 |
|
1462 if (d->parsed.isValid()) |
|
1463 return qvariant_cast<QRect>(d->parsed); |
|
1464 |
|
1465 const Value &v = d->values.at(0); |
|
1466 if (v.type != Value::Function) |
|
1467 return QRect(); |
|
1468 QStringList func = v.variant.toStringList(); |
|
1469 if (func.count() != 2 || func.at(0).compare(QLatin1String("rect")) != 0) |
|
1470 return QRect(); |
|
1471 QStringList args = func[1].split(QLatin1Char(' '), QString::SkipEmptyParts); |
|
1472 if (args.count() != 4) |
|
1473 return QRect(); |
|
1474 QRect rect(args[0].toInt(), args[1].toInt(), args[2].toInt(), args[3].toInt()); |
|
1475 d->parsed = qVariantFromValue<QRect>(rect); |
|
1476 return rect; |
|
1477 } |
|
1478 |
|
1479 void Declaration::colorValues(QColor *c, const QPalette &pal) const |
|
1480 { |
|
1481 int i; |
|
1482 if (d->parsed.isValid()) { |
|
1483 QList<QVariant> v = d->parsed.toList(); |
|
1484 for (i = 0; i < qMin(d->values.count(), 4); i++) { |
|
1485 if (v.at(i).type() == QVariant::Color) { |
|
1486 c[i] = qvariant_cast<QColor>(v.at(i)); |
|
1487 } else { |
|
1488 c[i] = pal.color((QPalette::ColorRole)(v.at(i).toInt())); |
|
1489 } |
|
1490 } |
|
1491 } else { |
|
1492 QList<QVariant> v; |
|
1493 for (i = 0; i < qMin(d->values.count(), 4); i++) { |
|
1494 ColorData color = parseColorValue(d->values.at(i)); |
|
1495 if(color.type == ColorData::Role) { |
|
1496 v += qVariantFromValue<int>(color.role); |
|
1497 c[i] = pal.color((QPalette::ColorRole)(color.role)); |
|
1498 } else { |
|
1499 v += qVariantFromValue<QColor>(color.color); |
|
1500 c[i] = color.color; |
|
1501 } |
|
1502 } |
|
1503 d->parsed = v; |
|
1504 } |
|
1505 |
|
1506 if (i == 0) c[0] = c[1] = c[2] = c[3] = QColor(); |
|
1507 else if (i == 1) c[3] = c[2] = c[1] = c[0]; |
|
1508 else if (i == 2) c[2] = c[0], c[3] = c[1]; |
|
1509 else if (i == 3) c[3] = c[1]; |
|
1510 } |
|
1511 |
|
1512 BorderStyle Declaration::styleValue() const |
|
1513 { |
|
1514 if (d->values.count() != 1) |
|
1515 return BorderStyle_None; |
|
1516 return parseStyleValue(d->values.at(0)); |
|
1517 } |
|
1518 |
|
1519 void Declaration::styleValues(BorderStyle *s) const |
|
1520 { |
|
1521 int i; |
|
1522 for (i = 0; i < qMin(d->values.count(), 4); i++) |
|
1523 s[i] = parseStyleValue(d->values.at(i)); |
|
1524 if (i == 0) s[0] = s[1] = s[2] = s[3] = BorderStyle_None; |
|
1525 else if (i == 1) s[3] = s[2] = s[1] = s[0]; |
|
1526 else if (i == 2) s[2] = s[0], s[3] = s[1]; |
|
1527 else if (i == 3) s[3] = s[1]; |
|
1528 } |
|
1529 |
|
1530 Repeat Declaration::repeatValue() const |
|
1531 { |
|
1532 if (d->parsed.isValid()) |
|
1533 return static_cast<Repeat>(d->parsed.toInt()); |
|
1534 if (d->values.count() != 1) |
|
1535 return Repeat_Unknown; |
|
1536 int v = findKnownValue(d->values.at(0).variant.toString(), |
|
1537 repeats, NumKnownRepeats); |
|
1538 d->parsed = v; |
|
1539 return static_cast<Repeat>(v); |
|
1540 } |
|
1541 |
|
1542 Origin Declaration::originValue() const |
|
1543 { |
|
1544 if (d->parsed.isValid()) |
|
1545 return static_cast<Origin>(d->parsed.toInt()); |
|
1546 if (d->values.count() != 1) |
|
1547 return Origin_Unknown; |
|
1548 int v = findKnownValue(d->values.at(0).variant.toString(), |
|
1549 origins, NumKnownOrigins); |
|
1550 d->parsed = v; |
|
1551 return static_cast<Origin>(v); |
|
1552 } |
|
1553 |
|
1554 PositionMode Declaration::positionValue() const |
|
1555 { |
|
1556 if (d->parsed.isValid()) |
|
1557 return static_cast<PositionMode>(d->parsed.toInt()); |
|
1558 if (d->values.count() != 1) |
|
1559 return PositionMode_Unknown; |
|
1560 int v = findKnownValue(d->values.at(0).variant.toString(), |
|
1561 positions, NumKnownPositionModes); |
|
1562 d->parsed = v; |
|
1563 return static_cast<PositionMode>(v); |
|
1564 } |
|
1565 |
|
1566 Attachment Declaration::attachmentValue() const |
|
1567 { |
|
1568 if (d->parsed.isValid()) |
|
1569 return static_cast<Attachment>(d->parsed.toInt()); |
|
1570 if (d->values.count() != 1) |
|
1571 return Attachment_Unknown; |
|
1572 int v = findKnownValue(d->values.at(0).variant.toString(), |
|
1573 attachments, NumKnownAttachments); |
|
1574 d->parsed = v; |
|
1575 return static_cast<Attachment>(v); |
|
1576 } |
|
1577 |
|
1578 int Declaration::styleFeaturesValue() const |
|
1579 { |
|
1580 Q_ASSERT(d->propertyId == QtStyleFeatures); |
|
1581 if (d->parsed.isValid()) |
|
1582 return d->parsed.toInt(); |
|
1583 int features = StyleFeature_None; |
|
1584 for (int i = 0; i < d->values.count(); i++) { |
|
1585 features |= static_cast<int>(findKnownValue(d->values.value(i).variant.toString(), |
|
1586 styleFeatures, NumKnownStyleFeatures)); |
|
1587 } |
|
1588 d->parsed = features; |
|
1589 return features; |
|
1590 } |
|
1591 |
|
1592 QString Declaration::uriValue() const |
|
1593 { |
|
1594 if (d->values.isEmpty() || d->values.at(0).type != Value::Uri) |
|
1595 return QString(); |
|
1596 return d->values.at(0).variant.toString(); |
|
1597 } |
|
1598 |
|
1599 Qt::Alignment Declaration::alignmentValue() const |
|
1600 { |
|
1601 if (d->parsed.isValid()) |
|
1602 return Qt::Alignment(d->parsed.toInt()); |
|
1603 if (d->values.isEmpty() || d->values.count() > 2) |
|
1604 return Qt::AlignLeft | Qt::AlignTop; |
|
1605 |
|
1606 Qt::Alignment v = parseAlignment(d->values.constData(), d->values.count()); |
|
1607 d->parsed = int(v); |
|
1608 return v; |
|
1609 } |
|
1610 |
|
1611 void Declaration::borderImageValue(QString *image, int *cuts, |
|
1612 TileMode *h, TileMode *v) const |
|
1613 { |
|
1614 *image = uriValue(); |
|
1615 for (int i = 0; i < 4; i++) |
|
1616 cuts[i] = -1; |
|
1617 *h = *v = TileMode_Stretch; |
|
1618 |
|
1619 if (d->values.count() < 2) |
|
1620 return; |
|
1621 |
|
1622 if (d->values.at(1).type == Value::Number) { // cuts! |
|
1623 int i; |
|
1624 for (i = 0; i < qMin(d->values.count()-1, 4); i++) { |
|
1625 const Value& v = d->values.at(i+1); |
|
1626 if (v.type != Value::Number) |
|
1627 break; |
|
1628 cuts[i] = v.variant.toString().toInt(); |
|
1629 } |
|
1630 if (i == 0) cuts[0] = cuts[1] = cuts[2] = cuts[3] = 0; |
|
1631 else if (i == 1) cuts[3] = cuts[2] = cuts[1] = cuts[0]; |
|
1632 else if (i == 2) cuts[2] = cuts[0], cuts[3] = cuts[1]; |
|
1633 else if (i == 3) cuts[3] = cuts[1]; |
|
1634 } |
|
1635 |
|
1636 if (d->values.last().type == Value::Identifier) { |
|
1637 *v = static_cast<TileMode>(findKnownValue(d->values.last().variant.toString(), |
|
1638 tileModes, NumKnownTileModes)); |
|
1639 } |
|
1640 if (d->values[d->values.count() - 2].type == Value::Identifier) { |
|
1641 *h = static_cast<TileMode> |
|
1642 (findKnownValue(d->values[d->values.count()-2].variant.toString(), |
|
1643 tileModes, NumKnownTileModes)); |
|
1644 } else |
|
1645 *h = *v; |
|
1646 } |
|
1647 |
|
1648 QIcon Declaration::iconValue() const |
|
1649 { |
|
1650 if (d->parsed.isValid()) |
|
1651 return qvariant_cast<QIcon>(d->parsed); |
|
1652 |
|
1653 QIcon icon; |
|
1654 for (int i = 0; i < d->values.count();) { |
|
1655 const Value &value = d->values.at(i++); |
|
1656 if (value.type != Value::Uri) |
|
1657 break; |
|
1658 QString uri = value.variant.toString(); |
|
1659 QIcon::Mode mode = QIcon::Normal; |
|
1660 QIcon::State state = QIcon::Off; |
|
1661 for (int j = 0; j < 2; j++) { |
|
1662 if (i != d->values.count() && d->values.at(i).type == Value::KnownIdentifier) { |
|
1663 switch (d->values.at(i).variant.toInt()) { |
|
1664 case Value_Disabled: mode = QIcon::Disabled; break; |
|
1665 case Value_Active: mode = QIcon::Active; break; |
|
1666 case Value_Selected: mode = QIcon::Selected; break; |
|
1667 case Value_Normal: mode = QIcon::Normal; break; |
|
1668 case Value_On: state = QIcon::On; break; |
|
1669 case Value_Off: state = QIcon::Off; break; |
|
1670 default: break; |
|
1671 } |
|
1672 ++i; |
|
1673 } else { |
|
1674 break; |
|
1675 } |
|
1676 } |
|
1677 |
|
1678 // QIcon is soo broken |
|
1679 if (icon.isNull()) |
|
1680 icon = QIcon(uri); |
|
1681 else |
|
1682 icon.addPixmap(uri, mode, state); |
|
1683 |
|
1684 if (i == d->values.count()) |
|
1685 break; |
|
1686 |
|
1687 if (d->values.at(i).type == Value::TermOperatorComma) |
|
1688 i++; |
|
1689 } |
|
1690 |
|
1691 d->parsed = qVariantFromValue<QIcon>(icon); |
|
1692 return icon; |
|
1693 } |
|
1694 |
|
1695 /////////////////////////////////////////////////////////////////////////////// |
|
1696 // Selector |
|
1697 int Selector::specificity() const |
|
1698 { |
|
1699 int val = 0; |
|
1700 for (int i = 0; i < basicSelectors.count(); ++i) { |
|
1701 const BasicSelector &sel = basicSelectors.at(i); |
|
1702 if (!sel.elementName.isEmpty()) |
|
1703 val += 1; |
|
1704 |
|
1705 val += (sel.pseudos.count() + sel.attributeSelectors.count()) * 0x10; |
|
1706 val += sel.ids.count() * 0x100; |
|
1707 } |
|
1708 return val; |
|
1709 } |
|
1710 |
|
1711 QString Selector::pseudoElement() const |
|
1712 { |
|
1713 const BasicSelector& bs = basicSelectors.last(); |
|
1714 if (!bs.pseudos.isEmpty() && bs.pseudos.at(0).type == PseudoClass_Unknown) |
|
1715 return bs.pseudos.at(0).name; |
|
1716 return QString(); |
|
1717 } |
|
1718 |
|
1719 quint64 Selector::pseudoClass(quint64 *negated) const |
|
1720 { |
|
1721 const BasicSelector& bs = basicSelectors.last(); |
|
1722 if (bs.pseudos.isEmpty()) |
|
1723 return PseudoClass_Unspecified; |
|
1724 quint64 pc = PseudoClass_Unknown; |
|
1725 for (int i = !pseudoElement().isEmpty(); i < bs.pseudos.count(); i++) { |
|
1726 const Pseudo &pseudo = bs.pseudos.at(i); |
|
1727 if (pseudo.type == PseudoClass_Unknown) |
|
1728 return PseudoClass_Unknown; |
|
1729 if (!pseudo.negated) |
|
1730 pc |= pseudo.type; |
|
1731 else if (negated) |
|
1732 *negated |= pseudo.type; |
|
1733 } |
|
1734 return pc; |
|
1735 } |
|
1736 |
|
1737 /////////////////////////////////////////////////////////////////////////////// |
|
1738 // StyleSheet |
|
1739 void StyleSheet::buildIndexes(Qt::CaseSensitivity nameCaseSensitivity) |
|
1740 { |
|
1741 QVector<StyleRule> universals; |
|
1742 for (int i = 0; i < styleRules.count(); ++i) { |
|
1743 const StyleRule &rule = styleRules.at(i); |
|
1744 QVector<Selector> universalsSelectors; |
|
1745 for (int j = 0; j < rule.selectors.count(); ++j) { |
|
1746 const Selector& selector = rule.selectors.at(j); |
|
1747 |
|
1748 if (selector.basicSelectors.isEmpty()) |
|
1749 continue; |
|
1750 |
|
1751 if (selector.basicSelectors.at(0).relationToNext == BasicSelector::NoRelation) { |
|
1752 if (selector.basicSelectors.count() != 1) |
|
1753 continue; |
|
1754 } else if (selector.basicSelectors.count() <= 1) { |
|
1755 continue; |
|
1756 } |
|
1757 |
|
1758 const BasicSelector &sel = selector.basicSelectors.at(selector.basicSelectors.count() - 1); |
|
1759 |
|
1760 if (!sel.ids.isEmpty()) { |
|
1761 StyleRule nr; |
|
1762 nr.selectors += selector; |
|
1763 nr.declarations = rule.declarations; |
|
1764 nr.order = i; |
|
1765 idIndex.insert(sel.ids.at(0), nr); |
|
1766 } else if (!sel.elementName.isEmpty()) { |
|
1767 StyleRule nr; |
|
1768 nr.selectors += selector; |
|
1769 nr.declarations = rule.declarations; |
|
1770 nr.order = i; |
|
1771 QString name = sel.elementName; |
|
1772 if (nameCaseSensitivity == Qt::CaseInsensitive) |
|
1773 name=name.toLower(); |
|
1774 nameIndex.insert(name, nr); |
|
1775 } else { |
|
1776 universalsSelectors += selector; |
|
1777 } |
|
1778 } |
|
1779 if (!universalsSelectors.isEmpty()) { |
|
1780 StyleRule nr; |
|
1781 nr.selectors = universalsSelectors; |
|
1782 nr.declarations = rule.declarations; |
|
1783 nr.order = i; |
|
1784 universals << nr; |
|
1785 } |
|
1786 } |
|
1787 styleRules = universals; |
|
1788 } |
|
1789 |
|
1790 /////////////////////////////////////////////////////////////////////////////// |
|
1791 // StyleSelector |
|
1792 StyleSelector::~StyleSelector() |
|
1793 { |
|
1794 } |
|
1795 |
|
1796 bool StyleSelector::nodeNameEquals(NodePtr node, const QString& nodeName) const |
|
1797 { |
|
1798 return nodeNames(node).contains(nodeName, nameCaseSensitivity); |
|
1799 } |
|
1800 |
|
1801 QStringList StyleSelector::nodeIds(NodePtr node) const |
|
1802 { |
|
1803 return QStringList(attribute(node, QLatin1String("id"))); |
|
1804 } |
|
1805 |
|
1806 bool StyleSelector::selectorMatches(const Selector &selector, NodePtr node) |
|
1807 { |
|
1808 if (selector.basicSelectors.isEmpty()) |
|
1809 return false; |
|
1810 |
|
1811 if (selector.basicSelectors.at(0).relationToNext == BasicSelector::NoRelation) { |
|
1812 if (selector.basicSelectors.count() != 1) |
|
1813 return false; |
|
1814 return basicSelectorMatches(selector.basicSelectors.at(0), node); |
|
1815 } |
|
1816 if (selector.basicSelectors.count() <= 1) |
|
1817 return false; |
|
1818 |
|
1819 int i = selector.basicSelectors.count() - 1; |
|
1820 node = duplicateNode(node); |
|
1821 bool match = true; |
|
1822 |
|
1823 BasicSelector sel = selector.basicSelectors.at(i); |
|
1824 do { |
|
1825 match = basicSelectorMatches(sel, node); |
|
1826 if (!match) { |
|
1827 if (sel.relationToNext == BasicSelector::MatchNextSelectorIfParent |
|
1828 || i == selector.basicSelectors.count() - 1) // first element must always match! |
|
1829 break; |
|
1830 } |
|
1831 |
|
1832 if (match || sel.relationToNext != BasicSelector::MatchNextSelectorIfAncestor) |
|
1833 --i; |
|
1834 |
|
1835 if (i < 0) |
|
1836 break; |
|
1837 |
|
1838 sel = selector.basicSelectors.at(i); |
|
1839 if (sel.relationToNext == BasicSelector::MatchNextSelectorIfAncestor |
|
1840 || sel.relationToNext == BasicSelector::MatchNextSelectorIfParent) { |
|
1841 |
|
1842 NodePtr nextParent = parentNode(node); |
|
1843 freeNode(node); |
|
1844 node = nextParent; |
|
1845 } else if (sel.relationToNext == BasicSelector::MatchNextSelectorIfPreceeds) { |
|
1846 NodePtr previousSibling = previousSiblingNode(node); |
|
1847 freeNode(node); |
|
1848 node = previousSibling; |
|
1849 } |
|
1850 if (isNullNode(node)) { |
|
1851 match = false; |
|
1852 break; |
|
1853 } |
|
1854 } while (i >= 0 && (match || sel.relationToNext == BasicSelector::MatchNextSelectorIfAncestor)); |
|
1855 |
|
1856 freeNode(node); |
|
1857 |
|
1858 return match; |
|
1859 } |
|
1860 |
|
1861 bool StyleSelector::basicSelectorMatches(const BasicSelector &sel, NodePtr node) |
|
1862 { |
|
1863 if (!sel.attributeSelectors.isEmpty()) { |
|
1864 if (!hasAttributes(node)) |
|
1865 return false; |
|
1866 |
|
1867 for (int i = 0; i < sel.attributeSelectors.count(); ++i) { |
|
1868 const QCss::AttributeSelector &a = sel.attributeSelectors.at(i); |
|
1869 |
|
1870 const QString attrValue = attribute(node, a.name); |
|
1871 if (attrValue.isNull()) |
|
1872 return false; |
|
1873 |
|
1874 if (a.valueMatchCriterium == QCss::AttributeSelector::MatchContains) { |
|
1875 |
|
1876 QStringList lst = attrValue.split(QLatin1Char(' ')); |
|
1877 if (!lst.contains(a.value)) |
|
1878 return false; |
|
1879 } else if ( |
|
1880 (a.valueMatchCriterium == QCss::AttributeSelector::MatchEqual |
|
1881 && attrValue != a.value) |
|
1882 || |
|
1883 (a.valueMatchCriterium == QCss::AttributeSelector::MatchBeginsWith |
|
1884 && !attrValue.startsWith(a.value)) |
|
1885 ) |
|
1886 return false; |
|
1887 } |
|
1888 } |
|
1889 |
|
1890 if (!sel.elementName.isEmpty() |
|
1891 && !nodeNameEquals(node, sel.elementName)) |
|
1892 return false; |
|
1893 |
|
1894 if (!sel.ids.isEmpty() |
|
1895 && sel.ids != nodeIds(node)) |
|
1896 return false; |
|
1897 |
|
1898 return true; |
|
1899 } |
|
1900 |
|
1901 void StyleSelector::matchRule(NodePtr node, const StyleRule &rule, StyleSheetOrigin origin, |
|
1902 int depth, QMap<uint, StyleRule> *weightedRules) |
|
1903 { |
|
1904 for (int j = 0; j < rule.selectors.count(); ++j) { |
|
1905 const Selector& selector = rule.selectors.at(j); |
|
1906 if (selectorMatches(selector, node)) { |
|
1907 uint weight = rule.order |
|
1908 + selector.specificity() *0x100 |
|
1909 + (uint(origin) + depth)*0x100000; |
|
1910 StyleRule newRule = rule; |
|
1911 if(rule.selectors.count() > 1) { |
|
1912 newRule.selectors.resize(1); |
|
1913 newRule.selectors[0] = selector; |
|
1914 } |
|
1915 //We might have rules with the same weight if they came from a rule with several selectors |
|
1916 weightedRules->insertMulti(weight, newRule); |
|
1917 } |
|
1918 } |
|
1919 } |
|
1920 |
|
1921 // Returns style rules that are in ascending order of specificity |
|
1922 // Each of the StyleRule returned will contain exactly one Selector |
|
1923 QVector<StyleRule> StyleSelector::styleRulesForNode(NodePtr node) |
|
1924 { |
|
1925 QVector<StyleRule> rules; |
|
1926 if (styleSheets.isEmpty()) |
|
1927 return rules; |
|
1928 |
|
1929 QMap<uint, StyleRule> weightedRules; // (spec, rule) that will be sorted below |
|
1930 |
|
1931 //prune using indexed stylesheet |
|
1932 for (int sheetIdx = 0; sheetIdx < styleSheets.count(); ++sheetIdx) { |
|
1933 const StyleSheet &styleSheet = styleSheets.at(sheetIdx); |
|
1934 for (int i = 0; i < styleSheet.styleRules.count(); ++i) { |
|
1935 matchRule(node, styleSheet.styleRules.at(i), styleSheet.origin, styleSheet.depth, &weightedRules); |
|
1936 } |
|
1937 |
|
1938 if (!styleSheet.idIndex.isEmpty()) { |
|
1939 QStringList ids = nodeIds(node); |
|
1940 for (int i = 0; i < ids.count(); i++) { |
|
1941 const QString &key = ids.at(i); |
|
1942 QMultiHash<QString, StyleRule>::const_iterator it = styleSheet.idIndex.constFind(key); |
|
1943 while (it != styleSheet.idIndex.constEnd() && it.key() == key) { |
|
1944 matchRule(node, it.value(), styleSheet.origin, styleSheet.depth, &weightedRules); |
|
1945 ++it; |
|
1946 } |
|
1947 } |
|
1948 } |
|
1949 if (!styleSheet.nameIndex.isEmpty()) { |
|
1950 QStringList names = nodeNames(node); |
|
1951 for (int i = 0; i < names.count(); i++) { |
|
1952 QString name = names.at(i); |
|
1953 if (nameCaseSensitivity == Qt::CaseInsensitive) |
|
1954 name = name.toLower(); |
|
1955 QMultiHash<QString, StyleRule>::const_iterator it = styleSheet.nameIndex.constFind(name); |
|
1956 while (it != styleSheet.nameIndex.constEnd() && it.key() == name) { |
|
1957 matchRule(node, it.value(), styleSheet.origin, styleSheet.depth, &weightedRules); |
|
1958 ++it; |
|
1959 } |
|
1960 } |
|
1961 } |
|
1962 if (!medium.isEmpty()) { |
|
1963 for (int i = 0; i < styleSheet.mediaRules.count(); ++i) { |
|
1964 if (styleSheet.mediaRules.at(i).media.contains(medium, Qt::CaseInsensitive)) { |
|
1965 for (int j = 0; j < styleSheet.mediaRules.at(i).styleRules.count(); ++j) { |
|
1966 matchRule(node, styleSheet.mediaRules.at(i).styleRules.at(j), styleSheet.origin, |
|
1967 styleSheet.depth, &weightedRules); |
|
1968 } |
|
1969 } |
|
1970 } |
|
1971 } |
|
1972 } |
|
1973 |
|
1974 rules.reserve(weightedRules.count()); |
|
1975 QMap<uint, StyleRule>::const_iterator it = weightedRules.constBegin(); |
|
1976 for ( ; it != weightedRules.constEnd() ; ++it) |
|
1977 rules += *it; |
|
1978 |
|
1979 return rules; |
|
1980 } |
|
1981 |
|
1982 // for qtexthtmlparser which requires just the declarations with Enabled state |
|
1983 // and without pseudo elements |
|
1984 QVector<Declaration> StyleSelector::declarationsForNode(NodePtr node, const char *extraPseudo) |
|
1985 { |
|
1986 QVector<Declaration> decls; |
|
1987 QVector<StyleRule> rules = styleRulesForNode(node); |
|
1988 for (int i = 0; i < rules.count(); i++) { |
|
1989 const Selector& selector = rules.at(i).selectors.at(0); |
|
1990 const QString pseudoElement = selector.pseudoElement(); |
|
1991 |
|
1992 if (extraPseudo && pseudoElement == QLatin1String(extraPseudo)) { |
|
1993 decls += rules.at(i).declarations; |
|
1994 continue; |
|
1995 } |
|
1996 |
|
1997 if (!pseudoElement.isEmpty()) // skip rules with pseudo elements |
|
1998 continue; |
|
1999 quint64 pseudoClass = selector.pseudoClass(); |
|
2000 if (pseudoClass == PseudoClass_Enabled || pseudoClass == PseudoClass_Unspecified) |
|
2001 decls += rules.at(i).declarations; |
|
2002 } |
|
2003 return decls; |
|
2004 } |
|
2005 |
|
2006 static inline bool isHexDigit(const char c) |
|
2007 { |
|
2008 return (c >= '0' && c <= '9') |
|
2009 || (c >= 'a' && c <= 'f') |
|
2010 || (c >= 'A' && c <= 'F') |
|
2011 ; |
|
2012 } |
|
2013 |
|
2014 QString Scanner::preprocess(const QString &input, bool *hasEscapeSequences) |
|
2015 { |
|
2016 QString output = input; |
|
2017 |
|
2018 if (hasEscapeSequences) |
|
2019 *hasEscapeSequences = false; |
|
2020 |
|
2021 int i = 0; |
|
2022 while (i < output.size()) { |
|
2023 if (output.at(i) == QLatin1Char('\\')) { |
|
2024 |
|
2025 ++i; |
|
2026 // test for unicode hex escape |
|
2027 int hexCount = 0; |
|
2028 const int hexStart = i; |
|
2029 while (i < output.size() |
|
2030 && isHexDigit(output.at(i).toLatin1()) |
|
2031 && hexCount < 7) { |
|
2032 ++hexCount; |
|
2033 ++i; |
|
2034 } |
|
2035 if (hexCount == 0) { |
|
2036 if (hasEscapeSequences) |
|
2037 *hasEscapeSequences = true; |
|
2038 continue; |
|
2039 } |
|
2040 |
|
2041 hexCount = qMin(hexCount, 6); |
|
2042 bool ok = false; |
|
2043 ushort code = output.mid(hexStart, hexCount).toUShort(&ok, 16); |
|
2044 if (ok) { |
|
2045 output.replace(hexStart - 1, hexCount + 1, QChar(code)); |
|
2046 i = hexStart; |
|
2047 } else { |
|
2048 i = hexStart; |
|
2049 } |
|
2050 } else { |
|
2051 ++i; |
|
2052 } |
|
2053 } |
|
2054 return output; |
|
2055 } |
|
2056 |
|
2057 int QCssScanner_Generated::handleCommentStart() |
|
2058 { |
|
2059 while (pos < input.size() - 1) { |
|
2060 if (input.at(pos) == QLatin1Char('*') |
|
2061 && input.at(pos + 1) == QLatin1Char('/')) { |
|
2062 pos += 2; |
|
2063 break; |
|
2064 } |
|
2065 ++pos; |
|
2066 } |
|
2067 return S; |
|
2068 } |
|
2069 |
|
2070 void Scanner::scan(const QString &preprocessedInput, QVector<Symbol> *symbols) |
|
2071 { |
|
2072 QCssScanner_Generated scanner(preprocessedInput); |
|
2073 Symbol sym; |
|
2074 int tok = scanner.lex(); |
|
2075 while (tok != -1) { |
|
2076 sym.token = static_cast<QCss::TokenType>(tok); |
|
2077 sym.text = scanner.input; |
|
2078 sym.start = scanner.lexemStart; |
|
2079 sym.len = scanner.lexemLength; |
|
2080 symbols->append(sym); |
|
2081 tok = scanner.lex(); |
|
2082 } |
|
2083 } |
|
2084 |
|
2085 QString Symbol::lexem() const |
|
2086 { |
|
2087 QString result; |
|
2088 if (len > 0) |
|
2089 result.reserve(len); |
|
2090 for (int i = 0; i < len; ++i) { |
|
2091 if (text.at(start + i) == QLatin1Char('\\') && i < len - 1) |
|
2092 ++i; |
|
2093 result += text.at(start + i); |
|
2094 } |
|
2095 return result; |
|
2096 } |
|
2097 |
|
2098 Parser::Parser(const QString &css, bool isFile) |
|
2099 { |
|
2100 init(css, isFile); |
|
2101 } |
|
2102 |
|
2103 Parser::Parser() |
|
2104 { |
|
2105 index = 0; |
|
2106 errorIndex = -1; |
|
2107 hasEscapeSequences = false; |
|
2108 } |
|
2109 |
|
2110 void Parser::init(const QString &css, bool isFile) |
|
2111 { |
|
2112 QString styleSheet = css; |
|
2113 if (isFile) { |
|
2114 QFile file(css); |
|
2115 if (file.open(QFile::ReadOnly)) { |
|
2116 sourcePath = QFileInfo(styleSheet).absolutePath() + QLatin1Char('/'); |
|
2117 QTextStream stream(&file); |
|
2118 styleSheet = stream.readAll(); |
|
2119 } else { |
|
2120 qWarning() << "QCss::Parser - Failed to load file " << css; |
|
2121 styleSheet.clear(); |
|
2122 } |
|
2123 } else { |
|
2124 sourcePath.clear(); |
|
2125 } |
|
2126 |
|
2127 hasEscapeSequences = false; |
|
2128 symbols.resize(0); |
|
2129 symbols.reserve(8); |
|
2130 Scanner::scan(Scanner::preprocess(styleSheet, &hasEscapeSequences), &symbols); |
|
2131 index = 0; |
|
2132 errorIndex = -1; |
|
2133 } |
|
2134 |
|
2135 bool Parser::parse(StyleSheet *styleSheet, Qt::CaseSensitivity nameCaseSensitivity) |
|
2136 { |
|
2137 if (testTokenAndEndsWith(ATKEYWORD_SYM, QLatin1String("charset"))) { |
|
2138 if (!next(STRING)) return false; |
|
2139 if (!next(SEMICOLON)) return false; |
|
2140 } |
|
2141 |
|
2142 while (test(S) || test(CDO) || test(CDC)) {} |
|
2143 |
|
2144 while (testImport()) { |
|
2145 ImportRule rule; |
|
2146 if (!parseImport(&rule)) return false; |
|
2147 styleSheet->importRules.append(rule); |
|
2148 while (test(S) || test(CDO) || test(CDC)) {} |
|
2149 } |
|
2150 |
|
2151 do { |
|
2152 if (testMedia()) { |
|
2153 MediaRule rule; |
|
2154 if (!parseMedia(&rule)) return false; |
|
2155 styleSheet->mediaRules.append(rule); |
|
2156 } else if (testPage()) { |
|
2157 PageRule rule; |
|
2158 if (!parsePage(&rule)) return false; |
|
2159 styleSheet->pageRules.append(rule); |
|
2160 } else if (testRuleset()) { |
|
2161 StyleRule rule; |
|
2162 if (!parseRuleset(&rule)) return false; |
|
2163 styleSheet->styleRules.append(rule); |
|
2164 } else if (test(ATKEYWORD_SYM)) { |
|
2165 if (!until(RBRACE)) return false; |
|
2166 } else if (hasNext()) { |
|
2167 return false; |
|
2168 } |
|
2169 while (test(S) || test(CDO) || test(CDC)) {} |
|
2170 } while (hasNext()); |
|
2171 styleSheet->buildIndexes(nameCaseSensitivity); |
|
2172 return true; |
|
2173 } |
|
2174 |
|
2175 Symbol Parser::errorSymbol() |
|
2176 { |
|
2177 if (errorIndex == -1) return Symbol(); |
|
2178 return symbols.at(errorIndex); |
|
2179 } |
|
2180 |
|
2181 static inline void removeOptionalQuotes(QString *str) |
|
2182 { |
|
2183 if (!str->startsWith(QLatin1Char('\'')) |
|
2184 && !str->startsWith(QLatin1Char('\"'))) |
|
2185 return; |
|
2186 str->remove(0, 1); |
|
2187 str->chop(1); |
|
2188 } |
|
2189 |
|
2190 bool Parser::parseImport(ImportRule *importRule) |
|
2191 { |
|
2192 skipSpace(); |
|
2193 |
|
2194 if (test(STRING)) { |
|
2195 importRule->href = lexem(); |
|
2196 } else { |
|
2197 if (!testAndParseUri(&importRule->href)) return false; |
|
2198 } |
|
2199 removeOptionalQuotes(&importRule->href); |
|
2200 |
|
2201 skipSpace(); |
|
2202 |
|
2203 if (testMedium()) { |
|
2204 if (!parseMedium(&importRule->media)) return false; |
|
2205 |
|
2206 while (test(COMMA)) { |
|
2207 skipSpace(); |
|
2208 if (!parseNextMedium(&importRule->media)) return false; |
|
2209 } |
|
2210 } |
|
2211 |
|
2212 if (!next(SEMICOLON)) return false; |
|
2213 |
|
2214 skipSpace(); |
|
2215 return true; |
|
2216 } |
|
2217 |
|
2218 bool Parser::parseMedia(MediaRule *mediaRule) |
|
2219 { |
|
2220 do { |
|
2221 skipSpace(); |
|
2222 if (!parseNextMedium(&mediaRule->media)) return false; |
|
2223 } while (test(COMMA)); |
|
2224 |
|
2225 if (!next(LBRACE)) return false; |
|
2226 skipSpace(); |
|
2227 |
|
2228 while (testRuleset()) { |
|
2229 StyleRule rule; |
|
2230 if (!parseRuleset(&rule)) return false; |
|
2231 mediaRule->styleRules.append(rule); |
|
2232 } |
|
2233 |
|
2234 if (!next(RBRACE)) return false; |
|
2235 skipSpace(); |
|
2236 return true; |
|
2237 } |
|
2238 |
|
2239 bool Parser::parseMedium(QStringList *media) |
|
2240 { |
|
2241 media->append(lexem()); |
|
2242 skipSpace(); |
|
2243 return true; |
|
2244 } |
|
2245 |
|
2246 bool Parser::parsePage(PageRule *pageRule) |
|
2247 { |
|
2248 skipSpace(); |
|
2249 |
|
2250 if (testPseudoPage()) |
|
2251 if (!parsePseudoPage(&pageRule->selector)) return false; |
|
2252 |
|
2253 skipSpace(); |
|
2254 if (!next(LBRACE)) return false; |
|
2255 |
|
2256 do { |
|
2257 skipSpace(); |
|
2258 Declaration decl; |
|
2259 if (!parseNextDeclaration(&decl)) return false; |
|
2260 if (!decl.isEmpty()) |
|
2261 pageRule->declarations.append(decl); |
|
2262 } while (test(SEMICOLON)); |
|
2263 |
|
2264 if (!next(RBRACE)) return false; |
|
2265 skipSpace(); |
|
2266 return true; |
|
2267 } |
|
2268 |
|
2269 bool Parser::parsePseudoPage(QString *selector) |
|
2270 { |
|
2271 if (!next(IDENT)) return false; |
|
2272 *selector = lexem(); |
|
2273 return true; |
|
2274 } |
|
2275 |
|
2276 bool Parser::parseNextOperator(Value *value) |
|
2277 { |
|
2278 if (!hasNext()) return true; |
|
2279 switch (next()) { |
|
2280 case SLASH: value->type = Value::TermOperatorSlash; skipSpace(); break; |
|
2281 case COMMA: value->type = Value::TermOperatorComma; skipSpace(); break; |
|
2282 default: prev(); break; |
|
2283 } |
|
2284 return true; |
|
2285 } |
|
2286 |
|
2287 bool Parser::parseCombinator(BasicSelector::Relation *relation) |
|
2288 { |
|
2289 *relation = BasicSelector::NoRelation; |
|
2290 if (lookup() == S) { |
|
2291 *relation = BasicSelector::MatchNextSelectorIfAncestor; |
|
2292 skipSpace(); |
|
2293 } else { |
|
2294 prev(); |
|
2295 } |
|
2296 if (test(PLUS)) { |
|
2297 *relation = BasicSelector::MatchNextSelectorIfPreceeds; |
|
2298 } else if (test(GREATER)) { |
|
2299 *relation = BasicSelector::MatchNextSelectorIfParent; |
|
2300 } |
|
2301 skipSpace(); |
|
2302 return true; |
|
2303 } |
|
2304 |
|
2305 bool Parser::parseProperty(Declaration *decl) |
|
2306 { |
|
2307 decl->d->property = lexem(); |
|
2308 decl->d->propertyId = static_cast<Property>(findKnownValue(decl->d->property, properties, NumProperties)); |
|
2309 skipSpace(); |
|
2310 return true; |
|
2311 } |
|
2312 |
|
2313 bool Parser::parseRuleset(StyleRule *styleRule) |
|
2314 { |
|
2315 Selector sel; |
|
2316 if (!parseSelector(&sel)) return false; |
|
2317 styleRule->selectors.append(sel); |
|
2318 |
|
2319 while (test(COMMA)) { |
|
2320 skipSpace(); |
|
2321 Selector sel; |
|
2322 if (!parseNextSelector(&sel)) return false; |
|
2323 styleRule->selectors.append(sel); |
|
2324 } |
|
2325 |
|
2326 skipSpace(); |
|
2327 if (!next(LBRACE)) return false; |
|
2328 const int declarationStart = index; |
|
2329 |
|
2330 do { |
|
2331 skipSpace(); |
|
2332 Declaration decl; |
|
2333 const int rewind = index; |
|
2334 if (!parseNextDeclaration(&decl)) { |
|
2335 index = rewind; |
|
2336 const bool foundSemicolon = until(SEMICOLON); |
|
2337 const int semicolonIndex = index; |
|
2338 |
|
2339 index = declarationStart; |
|
2340 const bool foundRBrace = until(RBRACE); |
|
2341 |
|
2342 if (foundSemicolon && semicolonIndex < index) { |
|
2343 decl = Declaration(); |
|
2344 index = semicolonIndex - 1; |
|
2345 } else { |
|
2346 skipSpace(); |
|
2347 return foundRBrace; |
|
2348 } |
|
2349 } |
|
2350 if (!decl.isEmpty()) |
|
2351 styleRule->declarations.append(decl); |
|
2352 } while (test(SEMICOLON)); |
|
2353 |
|
2354 if (!next(RBRACE)) return false; |
|
2355 skipSpace(); |
|
2356 return true; |
|
2357 } |
|
2358 |
|
2359 bool Parser::parseSelector(Selector *sel) |
|
2360 { |
|
2361 BasicSelector basicSel; |
|
2362 if (!parseSimpleSelector(&basicSel)) return false; |
|
2363 while (testCombinator()) { |
|
2364 if (!parseCombinator(&basicSel.relationToNext)) return false; |
|
2365 |
|
2366 if (!testSimpleSelector()) break; |
|
2367 sel->basicSelectors.append(basicSel); |
|
2368 |
|
2369 basicSel = BasicSelector(); |
|
2370 if (!parseSimpleSelector(&basicSel)) return false; |
|
2371 } |
|
2372 sel->basicSelectors.append(basicSel); |
|
2373 return true; |
|
2374 } |
|
2375 |
|
2376 bool Parser::parseSimpleSelector(BasicSelector *basicSel) |
|
2377 { |
|
2378 int minCount = 0; |
|
2379 if (lookupElementName()) { |
|
2380 if (!parseElementName(&basicSel->elementName)) return false; |
|
2381 } else { |
|
2382 prev(); |
|
2383 minCount = 1; |
|
2384 } |
|
2385 bool onceMore; |
|
2386 int count = 0; |
|
2387 do { |
|
2388 onceMore = false; |
|
2389 if (test(HASH)) { |
|
2390 QString theid = lexem(); |
|
2391 // chop off leading # |
|
2392 theid.remove(0, 1); |
|
2393 basicSel->ids.append(theid); |
|
2394 onceMore = true; |
|
2395 } else if (testClass()) { |
|
2396 onceMore = true; |
|
2397 AttributeSelector a; |
|
2398 a.name = QLatin1String("class"); |
|
2399 a.valueMatchCriterium = AttributeSelector::MatchContains; |
|
2400 if (!parseClass(&a.value)) return false; |
|
2401 basicSel->attributeSelectors.append(a); |
|
2402 } else if (testAttrib()) { |
|
2403 onceMore = true; |
|
2404 AttributeSelector a; |
|
2405 if (!parseAttrib(&a)) return false; |
|
2406 basicSel->attributeSelectors.append(a); |
|
2407 } else if (testPseudo()) { |
|
2408 onceMore = true; |
|
2409 Pseudo ps; |
|
2410 if (!parsePseudo(&ps)) return false; |
|
2411 basicSel->pseudos.append(ps); |
|
2412 } |
|
2413 if (onceMore) ++count; |
|
2414 } while (onceMore); |
|
2415 return count >= minCount; |
|
2416 } |
|
2417 |
|
2418 bool Parser::parseClass(QString *name) |
|
2419 { |
|
2420 if (!next(IDENT)) return false; |
|
2421 *name = lexem(); |
|
2422 return true; |
|
2423 } |
|
2424 |
|
2425 bool Parser::parseElementName(QString *name) |
|
2426 { |
|
2427 switch (lookup()) { |
|
2428 case STAR: name->clear(); break; |
|
2429 case IDENT: *name = lexem(); break; |
|
2430 default: return false; |
|
2431 } |
|
2432 return true; |
|
2433 } |
|
2434 |
|
2435 bool Parser::parseAttrib(AttributeSelector *attr) |
|
2436 { |
|
2437 skipSpace(); |
|
2438 if (!next(IDENT)) return false; |
|
2439 attr->name = lexem(); |
|
2440 skipSpace(); |
|
2441 |
|
2442 if (test(EQUAL)) { |
|
2443 attr->valueMatchCriterium = AttributeSelector::MatchEqual; |
|
2444 } else if (test(INCLUDES)) { |
|
2445 attr->valueMatchCriterium = AttributeSelector::MatchContains; |
|
2446 } else if (test(DASHMATCH)) { |
|
2447 attr->valueMatchCriterium = AttributeSelector::MatchBeginsWith; |
|
2448 } else { |
|
2449 return next(RBRACKET); |
|
2450 } |
|
2451 |
|
2452 skipSpace(); |
|
2453 |
|
2454 if (!test(IDENT) && !test(STRING)) return false; |
|
2455 attr->value = unquotedLexem(); |
|
2456 |
|
2457 skipSpace(); |
|
2458 return next(RBRACKET); |
|
2459 } |
|
2460 |
|
2461 bool Parser::parsePseudo(Pseudo *pseudo) |
|
2462 { |
|
2463 (void)test(COLON); |
|
2464 pseudo->negated = test(EXCLAMATION_SYM); |
|
2465 if (test(IDENT)) { |
|
2466 pseudo->name = lexem(); |
|
2467 pseudo->type = static_cast<quint64>(findKnownValue(pseudo->name, pseudos, NumPseudos)); |
|
2468 return true; |
|
2469 } |
|
2470 if (!next(FUNCTION)) return false; |
|
2471 pseudo->function = lexem(); |
|
2472 // chop off trailing parenthesis |
|
2473 pseudo->function.chop(1); |
|
2474 skipSpace(); |
|
2475 if (!test(IDENT)) return false; |
|
2476 pseudo->name = lexem(); |
|
2477 skipSpace(); |
|
2478 return next(RPAREN); |
|
2479 } |
|
2480 |
|
2481 bool Parser::parseNextDeclaration(Declaration *decl) |
|
2482 { |
|
2483 if (!testProperty()) |
|
2484 return true; // not an error! |
|
2485 if (!parseProperty(decl)) return false; |
|
2486 if (!next(COLON)) return false; |
|
2487 skipSpace(); |
|
2488 if (!parseNextExpr(&decl->d->values)) return false; |
|
2489 if (testPrio()) |
|
2490 if (!parsePrio(decl)) return false; |
|
2491 return true; |
|
2492 } |
|
2493 |
|
2494 bool Parser::testPrio() |
|
2495 { |
|
2496 const int rewind = index; |
|
2497 if (!test(EXCLAMATION_SYM)) return false; |
|
2498 skipSpace(); |
|
2499 if (!test(IDENT)) { |
|
2500 index = rewind; |
|
2501 return false; |
|
2502 } |
|
2503 if (lexem().compare(QLatin1String("important"), Qt::CaseInsensitive) != 0) { |
|
2504 index = rewind; |
|
2505 return false; |
|
2506 } |
|
2507 return true; |
|
2508 } |
|
2509 |
|
2510 bool Parser::parsePrio(Declaration *declaration) |
|
2511 { |
|
2512 declaration->d->important = true; |
|
2513 skipSpace(); |
|
2514 return true; |
|
2515 } |
|
2516 |
|
2517 bool Parser::parseExpr(QVector<Value> *values) |
|
2518 { |
|
2519 Value val; |
|
2520 if (!parseTerm(&val)) return false; |
|
2521 values->append(val); |
|
2522 bool onceMore; |
|
2523 do { |
|
2524 onceMore = false; |
|
2525 val = Value(); |
|
2526 if (!parseNextOperator(&val)) return false; |
|
2527 if (val.type != QCss::Value::Unknown) |
|
2528 values->append(val); |
|
2529 if (testTerm()) { |
|
2530 onceMore = true; |
|
2531 val = Value(); |
|
2532 if (!parseTerm(&val)) return false; |
|
2533 values->append(val); |
|
2534 } |
|
2535 } while (onceMore); |
|
2536 return true; |
|
2537 } |
|
2538 |
|
2539 bool Parser::testTerm() |
|
2540 { |
|
2541 return test(PLUS) || test(MINUS) |
|
2542 || test(NUMBER) |
|
2543 || test(PERCENTAGE) |
|
2544 || test(LENGTH) |
|
2545 || test(STRING) |
|
2546 || test(IDENT) |
|
2547 || testHexColor() |
|
2548 || testFunction(); |
|
2549 } |
|
2550 |
|
2551 bool Parser::parseTerm(Value *value) |
|
2552 { |
|
2553 QString str = lexem(); |
|
2554 bool haveUnary = false; |
|
2555 if (lookup() == PLUS || lookup() == MINUS) { |
|
2556 haveUnary = true; |
|
2557 if (!hasNext()) return false; |
|
2558 next(); |
|
2559 str += lexem(); |
|
2560 } |
|
2561 |
|
2562 value->variant = str; |
|
2563 value->type = QCss::Value::String; |
|
2564 switch (lookup()) { |
|
2565 case NUMBER: |
|
2566 value->type = Value::Number; |
|
2567 value->variant.convert(QVariant::Double); |
|
2568 break; |
|
2569 case PERCENTAGE: |
|
2570 value->type = Value::Percentage; |
|
2571 str.chop(1); // strip off % |
|
2572 value->variant = str; |
|
2573 break; |
|
2574 case LENGTH: |
|
2575 value->type = Value::Length; |
|
2576 break; |
|
2577 |
|
2578 case STRING: |
|
2579 if (haveUnary) return false; |
|
2580 value->type = Value::String; |
|
2581 str.chop(1); |
|
2582 str.remove(0, 1); |
|
2583 value->variant = str; |
|
2584 break; |
|
2585 case IDENT: { |
|
2586 if (haveUnary) return false; |
|
2587 value->type = Value::Identifier; |
|
2588 const int theid = findKnownValue(str, values, NumKnownValues); |
|
2589 if (theid != 0) { |
|
2590 value->type = Value::KnownIdentifier; |
|
2591 value->variant = theid; |
|
2592 } |
|
2593 break; |
|
2594 } |
|
2595 default: { |
|
2596 if (haveUnary) return false; |
|
2597 prev(); |
|
2598 if (testHexColor()) { |
|
2599 QColor col; |
|
2600 if (!parseHexColor(&col)) return false; |
|
2601 value->type = Value::Color; |
|
2602 value->variant = col; |
|
2603 } else if (testFunction()) { |
|
2604 QString name, args; |
|
2605 if (!parseFunction(&name, &args)) return false; |
|
2606 if (name == QLatin1String("url")) { |
|
2607 value->type = Value::Uri; |
|
2608 removeOptionalQuotes(&args); |
|
2609 if (QFileInfo(args).isRelative() && !sourcePath.isEmpty()) { |
|
2610 args.prepend(sourcePath); |
|
2611 } |
|
2612 value->variant = args; |
|
2613 } else { |
|
2614 value->type = Value::Function; |
|
2615 value->variant = QStringList() << name << args; |
|
2616 } |
|
2617 } else { |
|
2618 return recordError(); |
|
2619 } |
|
2620 return true; |
|
2621 } |
|
2622 } |
|
2623 skipSpace(); |
|
2624 return true; |
|
2625 } |
|
2626 |
|
2627 bool Parser::parseFunction(QString *name, QString *args) |
|
2628 { |
|
2629 *name = lexem(); |
|
2630 name->chop(1); |
|
2631 skipSpace(); |
|
2632 const int start = index; |
|
2633 if (!until(RPAREN)) return false; |
|
2634 for (int i = start; i < index - 1; ++i) |
|
2635 args->append(symbols.at(i).lexem()); |
|
2636 /* |
|
2637 if (!nextExpr(&arguments)) return false; |
|
2638 if (!next(RPAREN)) return false; |
|
2639 */ |
|
2640 skipSpace(); |
|
2641 return true; |
|
2642 } |
|
2643 |
|
2644 bool Parser::parseHexColor(QColor *col) |
|
2645 { |
|
2646 col->setNamedColor(lexem()); |
|
2647 if (!col->isValid()) { |
|
2648 qWarning("QCssParser::parseHexColor: Unknown color name '%s'",lexem().toLatin1().constData()); |
|
2649 return false; |
|
2650 } |
|
2651 skipSpace(); |
|
2652 return true; |
|
2653 } |
|
2654 |
|
2655 bool Parser::testAndParseUri(QString *uri) |
|
2656 { |
|
2657 const int rewind = index; |
|
2658 if (!testFunction()) return false; |
|
2659 |
|
2660 QString name, args; |
|
2661 if (!parseFunction(&name, &args)) { |
|
2662 index = rewind; |
|
2663 return false; |
|
2664 } |
|
2665 if (name.toLower() != QLatin1String("url")) { |
|
2666 index = rewind; |
|
2667 return false; |
|
2668 } |
|
2669 *uri = args; |
|
2670 removeOptionalQuotes(uri); |
|
2671 return true; |
|
2672 } |
|
2673 |
|
2674 bool Parser::testSimpleSelector() |
|
2675 { |
|
2676 return testElementName() |
|
2677 || (test(HASH)) |
|
2678 || testClass() |
|
2679 || testAttrib() |
|
2680 || testPseudo(); |
|
2681 } |
|
2682 |
|
2683 bool Parser::next(QCss::TokenType t) |
|
2684 { |
|
2685 if (hasNext() && next() == t) |
|
2686 return true; |
|
2687 return recordError(); |
|
2688 } |
|
2689 |
|
2690 bool Parser::test(QCss::TokenType t) |
|
2691 { |
|
2692 if (index >= symbols.count()) |
|
2693 return false; |
|
2694 if (symbols.at(index).token == t) { |
|
2695 ++index; |
|
2696 return true; |
|
2697 } |
|
2698 return false; |
|
2699 } |
|
2700 |
|
2701 QString Parser::unquotedLexem() const |
|
2702 { |
|
2703 QString s = lexem(); |
|
2704 if (lookup() == STRING) { |
|
2705 s.chop(1); |
|
2706 s.remove(0, 1); |
|
2707 } |
|
2708 return s; |
|
2709 } |
|
2710 |
|
2711 QString Parser::lexemUntil(QCss::TokenType t) |
|
2712 { |
|
2713 QString lexem; |
|
2714 while (hasNext() && next() != t) |
|
2715 lexem += symbol().lexem(); |
|
2716 return lexem; |
|
2717 } |
|
2718 |
|
2719 bool Parser::until(QCss::TokenType target, QCss::TokenType target2) |
|
2720 { |
|
2721 int braceCount = 0; |
|
2722 int brackCount = 0; |
|
2723 int parenCount = 0; |
|
2724 if (index) { |
|
2725 switch(symbols.at(index-1).token) { |
|
2726 case LBRACE: ++braceCount; break; |
|
2727 case LBRACKET: ++brackCount; break; |
|
2728 case FUNCTION: |
|
2729 case LPAREN: ++parenCount; break; |
|
2730 default: ; |
|
2731 } |
|
2732 } |
|
2733 while (index < symbols.size()) { |
|
2734 QCss::TokenType t = symbols.at(index++).token; |
|
2735 switch (t) { |
|
2736 case LBRACE: ++braceCount; break; |
|
2737 case RBRACE: --braceCount; break; |
|
2738 case LBRACKET: ++brackCount; break; |
|
2739 case RBRACKET: --brackCount; break; |
|
2740 case FUNCTION: |
|
2741 case LPAREN: ++parenCount; break; |
|
2742 case RPAREN: --parenCount; break; |
|
2743 default: break; |
|
2744 } |
|
2745 if ((t == target || (target2 != NONE && t == target2)) |
|
2746 && braceCount <= 0 |
|
2747 && brackCount <= 0 |
|
2748 && parenCount <= 0) |
|
2749 return true; |
|
2750 |
|
2751 if (braceCount < 0 || brackCount < 0 || parenCount < 0) { |
|
2752 --index; |
|
2753 break; |
|
2754 } |
|
2755 } |
|
2756 return false; |
|
2757 } |
|
2758 |
|
2759 bool Parser::testTokenAndEndsWith(QCss::TokenType t, const QLatin1String &str) |
|
2760 { |
|
2761 if (!test(t)) return false; |
|
2762 if (!lexem().endsWith(str, Qt::CaseInsensitive)) { |
|
2763 prev(); |
|
2764 return false; |
|
2765 } |
|
2766 return true; |
|
2767 } |
|
2768 |
|
2769 QT_END_NAMESPACE |
|
2770 #endif // QT_NO_CSSPARSER |