1 //////////////////////////////////////////////////////////////////////////////
2 // Name: CSSStyleDeclaration.cpp
3 // Purpose:
4 // Author: Alex Thuering
5 // Created: 2005/05/03
6 // RCS-ID: $Id: CSSStyleDeclaration.cpp,v 1.13 2015/03/21 16:28:23 ntalex Exp $
7 // Copyright: (c) 2005 Alex Thuering
8 // Licence: wxWindows licence
9 //////////////////////////////////////////////////////////////////////////////
10
11 #include "CSSStyleDeclaration.h"
12 #include <wx/tokenzr.h>
13 #include <wx/log.h>
14
15 wxCSSPrimitiveValue* wxCSSStyleDeclaration::s_emptyCSSValue = new wxCSSPrimitiveValue;
16 wxSVGColor* wxCSSStyleDeclaration::s_emptySVGColor = new wxSVGColor;
17 wxSVGPaint* wxCSSStyleDeclaration::s_emptySVGPaint = new wxSVGPaint;
18 wxSVGPaint* wxCSSStyleDeclaration::s_blackSVGPaint = new wxSVGPaint(0,0,0);
19 wxCSSValueList* wxCSSStyleDeclaration::s_emptyValueList = new wxCSSValueList;
20
~wxCSSStyleDeclaration()21 wxCSSStyleDeclaration::~wxCSSStyleDeclaration() {
22 for (iterator it = begin(); it != end(); ++it)
23 delete it->second;
24 }
25
operator =(const wxCSSStyleDeclaration & src)26 wxCSSStyleDeclaration& wxCSSStyleDeclaration::operator=(
27 const wxCSSStyleDeclaration& src) {
28 for (iterator it = begin(); it != end(); ++it)
29 delete it->second;
30 clear();
31 Add(src);
32 return *this;
33 }
34
Add(const wxCSSStyleDeclaration & style)35 void wxCSSStyleDeclaration::Add(const wxCSSStyleDeclaration& style) {
36 const_iterator it;
37 for (it = style.begin(); it != style.end(); ++it) {
38 iterator it2 = find(it->first);
39 if (it2 != end()) { // replace old value with new one
40 delete it2->second;
41 it2->second = it->second->Clone();
42 } else
43 (*this)[it->first] = it->second->Clone();
44 }
45 }
46
GetCSSText() const47 wxString wxCSSStyleDeclaration::GetCSSText() const {
48 wxString text;
49 const_iterator it;
50 for (it = begin(); it != end(); ++it)
51 text = text + GetPropertyName(it->first) + wxT(":") + it->second->GetCSSText() + wxT(";");
52 return text;
53 }
54
SetCSSText(const wxString & text)55 void wxCSSStyleDeclaration::SetCSSText(const wxString& text) {
56 wxStringTokenizer tkz(text, wxT(";"));
57 while (tkz.HasMoreTokens()) {
58 wxString token = tkz.GetNextToken().Strip(wxString::both);
59 int pos = token.find(wxT(':'));
60 if (pos <= 0)
61 continue;
62 SetProperty(token.substr(0, pos), token.substr(pos + 1));
63 }
64 }
65
66 static wxSortedArrayString* s_cssProperties = NULL;
67 #include "css_properties.cpp"
FillCSSProperties()68 inline void FillCSSProperties() {
69 if (s_cssProperties == NULL) {
70 s_cssProperties = new wxSortedArrayString;
71 for (unsigned int i = 0; i < sizeof(s_cssPropertyStrings) / sizeof(s_cssPropertyStrings[0]); i++)
72 s_cssProperties->Add(s_cssPropertyStrings[i]);
73 }
74 }
75
GetPropertyId(const wxString & propertyName)76 wxCSS_PROPERTY wxCSSStyleDeclaration::GetPropertyId(const wxString& propertyName) {
77 FillCSSProperties();
78 int id = s_cssProperties->Index(propertyName);
79 if (id >= 0)
80 return wxCSS_PROPERTY(id + 1);
81 return wxCSS_PROPERTY_UNKNOWN;
82 }
83
GetPropertyName(wxCSS_PROPERTY propertyId)84 wxString wxCSSStyleDeclaration::GetPropertyName(wxCSS_PROPERTY propertyId) {
85 FillCSSProperties();
86 if (propertyId == wxCSS_PROPERTY_UNKNOWN)
87 return wxT("");
88 return (*s_cssProperties)[int(propertyId) - 1];
89 }
90
SetProperty(wxCSS_PROPERTY propertyId,const wxString & svalue)91 void wxCSSStyleDeclaration::SetProperty(wxCSS_PROPERTY propertyId, const wxString& svalue) {
92 if (propertyId == wxCSS_PROPERTY_UNKNOWN)
93 return;
94 wxString value = svalue.Strip(wxString::both);
95 wxCSSValue* cssValue = NULL;
96 iterator it = find(propertyId);
97 if (it != end())
98 cssValue = it->second;
99 if (value == wxT("inherit")) {
100 if (cssValue != NULL)
101 erase(propertyId);
102 return;
103 }
104 switch (propertyId) {
105 case wxCSS_PROPERTY_UNKNOWN:
106 break;
107 // only idents
108 case wxCSS_PROPERTY_ALIGNMENT_BASELINE:
109 case wxCSS_PROPERTY_BASELINE_SHIFT:
110 case wxCSS_PROPERTY_CLIP_RULE:
111 case wxCSS_PROPERTY_COLOR_INTERPOLATION:
112 case wxCSS_PROPERTY_COLOR_INTERPOLATION_FILTERS:
113 case wxCSS_PROPERTY_COLOR_RENDERING:
114 case wxCSS_PROPERTY_DIRECTION:
115 case wxCSS_PROPERTY_DISPLAY:
116 case wxCSS_PROPERTY_DOMINANT_BASELINE:
117 case wxCSS_PROPERTY_ENABLE_BACKGROUND:
118 case wxCSS_PROPERTY_FILL_RULE:
119 case wxCSS_PROPERTY_FONT_STRETCH:
120 case wxCSS_PROPERTY_FONT_STYLE:
121 case wxCSS_PROPERTY_FONT_VARIANT:
122 case wxCSS_PROPERTY_FONT_WEIGHT:
123 case wxCSS_PROPERTY_IMAGE_RENDERING:
124 case wxCSS_PROPERTY_OVERFLOW:
125 case wxCSS_PROPERTY_POINTER_EVENTS:
126 case wxCSS_PROPERTY_SHAPE_RENDERING:
127 case wxCSS_PROPERTY_STROKE_LINECAP:
128 case wxCSS_PROPERTY_STROKE_LINEJOIN:
129 case wxCSS_PROPERTY_TEXT_ANCHOR:
130 case wxCSS_PROPERTY_TEXT_DECORATION:
131 case wxCSS_PROPERTY_TEXT_RENDERING:
132 case wxCSS_PROPERTY_UNICODE_BIDI:
133 case wxCSS_PROPERTY_VISIBILITY:
134 case wxCSS_PROPERTY_WRITING_MODE:
135 if (!cssValue)
136 cssValue = new wxCSSPrimitiveValue;
137 ((wxCSSPrimitiveValue*) cssValue)->SetIdentValue(wxCSSValue::GetValueId(value));
138 break;
139 case wxCSS_PROPERTY_CLIP:
140 if (!cssValue)
141 cssValue = new wxCSSPrimitiveValue;
142 if (value == wxT("none"))
143 ((wxCSSPrimitiveValue*) cssValue)->SetIdentValue(wxCSS_VALUE_NONE);
144 else
145 ((wxCSSPrimitiveValue*) cssValue)->SetStringValue(wxCSS_STRING, value);
146 break;
147 // url or ident
148 case wxCSS_PROPERTY_CLIP_PATH:
149 case wxCSS_PROPERTY_CURSOR:
150 case wxCSS_PROPERTY_FILTER:
151 case wxCSS_PROPERTY_MARKER_END:
152 case wxCSS_PROPERTY_MARKER_MID:
153 case wxCSS_PROPERTY_MARKER_START:
154 case wxCSS_PROPERTY_MASK:
155 if (!cssValue)
156 cssValue = new wxCSSPrimitiveValue;
157 if (value.Left(3) == wxT("url"))
158 ((wxCSSPrimitiveValue*) cssValue)->SetStringValue(wxCSS_URI,
159 value.AfterFirst(wxT('(')).BeforeFirst(wxT(')')));
160 else
161 ((wxCSSPrimitiveValue*) cssValue)->SetIdentValue(wxCSSValue::GetValueId(value));
162 break;
163 case wxCSS_PROPERTY_COLOR:
164 if (!cssValue)
165 cssValue = new wxCSSPrimitiveValue;
166 ((wxCSSPrimitiveValue*) cssValue)->SetRGBColorValue(ParseColor(value));
167 break;
168 case wxCSS_PROPERTY_COLOR_PROFILE:
169 if (!cssValue)
170 cssValue = new wxCSSPrimitiveValue;
171 if (value == wxT("auto"))
172 ((wxCSSPrimitiveValue*) cssValue)->SetIdentValue(wxCSS_VALUE_AUTO);
173 else if (value == wxT("sRGB"))
174 ((wxCSSPrimitiveValue*) cssValue)->SetIdentValue(wxCSS_VALUE_SRGB);
175 else if (value.Left(3) == wxT("url"))
176 ((wxCSSPrimitiveValue*) cssValue)->SetStringValue(wxCSS_URI,
177 value.AfterFirst(wxT('(')).BeforeFirst(wxT(')')));
178 else
179 ((wxCSSPrimitiveValue*) cssValue)->SetStringValue(wxCSS_STRING, value);
180 break;
181 // number
182 case wxCSS_PROPERTY_FILL_OPACITY:
183 case wxCSS_PROPERTY_FLOOD_OPACITY:
184 case wxCSS_PROPERTY_FONT_SIZE:
185 case wxCSS_PROPERTY_GLYPH_ORIENTATION_HORIZONTAL:
186 case wxCSS_PROPERTY_STROKE_DASHOFFSET:
187 case wxCSS_PROPERTY_STROKE_MITERLIMIT:
188 case wxCSS_PROPERTY_STROKE_OPACITY:
189 case wxCSS_PROPERTY_STROKE_WIDTH:
190 case wxCSS_PROPERTY_OPACITY:
191 case wxCSS_PROPERTY_STOP_OPACITY:
192 if (!cssValue)
193 cssValue = new wxCSSPrimitiveValue;
194 ((wxCSSPrimitiveValue*) cssValue)->SetFloatValue(wxCSS_NUMBER, ParseNumber(value));
195 break;
196 // 'none' or number
197 case wxCSS_PROPERTY_FONT_SIZE_ADJUST:
198 if (!cssValue)
199 cssValue = new wxCSSPrimitiveValue;
200 if (value == wxT("none"))
201 ((wxCSSPrimitiveValue*) cssValue)->SetIdentValue(wxCSS_VALUE_NONE);
202 else
203 ((wxCSSPrimitiveValue*) cssValue)->SetFloatValue(wxCSS_NUMBER, ParseNumber(value));
204 break;
205 // 'auto' or number
206 case wxCSS_PROPERTY_GLYPH_ORIENTATION_VERTICAL:
207 case wxCSS_PROPERTY_KERNING:
208 case wxCSS_PROPERTY_LETTER_SPACING:
209 if (!cssValue)
210 cssValue = new wxCSSPrimitiveValue;
211 if (value == wxT("auto"))
212 ((wxCSSPrimitiveValue*) cssValue)->SetIdentValue(wxCSS_VALUE_AUTO);
213 else
214 ((wxCSSPrimitiveValue*) cssValue)->SetFloatValue(wxCSS_NUMBER, ParseNumber(value));
215 break;
216 // 'normal' or number
217 case wxCSS_PROPERTY_WORD_SPACING:
218 if (!cssValue)
219 cssValue = new wxCSSPrimitiveValue;
220 if (value == wxT("normal"))
221 ((wxCSSPrimitiveValue*) cssValue)->SetIdentValue(wxCSS_VALUE_AUTO);
222 else
223 ((wxCSSPrimitiveValue*) cssValue)->SetFloatValue(wxCSS_NUMBER, ParseNumber(value));
224 break;
225 // string
226 case wxCSS_PROPERTY_FONT_FAMILY:
227 if (!cssValue)
228 cssValue = new wxCSSPrimitiveValue;
229 ((wxCSSPrimitiveValue*) cssValue)->SetStringValue(wxCSS_STRING, value);
230 break;
231 // <color>
232 case wxCSS_PROPERTY_FLOOD_COLOR:
233 case wxCSS_PROPERTY_LIGHTING_COLOR:
234 case wxCSS_PROPERTY_STOP_COLOR:
235 if (!cssValue)
236 cssValue = new wxSVGColor;
237 ((wxSVGColor*) cssValue)->SetRGBColor(ParseColor(value));
238 break;
239 // <paint>
240 case wxCSS_PROPERTY_FILL:
241 case wxCSS_PROPERTY_STROKE:
242 if (!cssValue)
243 cssValue = new wxSVGPaint;
244 ParseSVGPaint(*(wxSVGPaint*) cssValue, value);
245 break;
246 // <dasharray>
247 case wxCSS_PROPERTY_STROKE_DASHARRAY:
248 if (!cssValue)
249 cssValue = new wxCSSValueList;
250 ((wxCSSValueList*) cssValue)->SetCSSText(value);
251 break;
252 }
253 if (it == end())
254 (*this)[propertyId] = cssValue;
255 }
256
SetProperty(wxCSS_PROPERTY propertyId,const wxSVGAnimatedType & value)257 void wxCSSStyleDeclaration::SetProperty(wxCSS_PROPERTY propertyId, const wxSVGAnimatedType& value) {
258 if (value.GetPropertyType() != wxSVG_ANIMATED_LENGTH && value.GetPropertyType() != wxSVG_ANIMATED_COLOR) {
259 SetProperty(propertyId, value.GetString());
260 return;
261 }
262 if (propertyId == wxCSS_PROPERTY_UNKNOWN)
263 return;
264 wxCSSValue* cssValue = NULL;
265 iterator it = find(propertyId);
266 if (it != end())
267 cssValue = it->second;
268 if (value.GetPropertyType() == wxSVG_ANIMATED_LENGTH) {
269 switch (propertyId) {
270 // number
271 case wxCSS_PROPERTY_FILL_OPACITY:
272 case wxCSS_PROPERTY_FLOOD_OPACITY:
273 case wxCSS_PROPERTY_FONT_SIZE:
274 case wxCSS_PROPERTY_GLYPH_ORIENTATION_HORIZONTAL:
275 case wxCSS_PROPERTY_STROKE_DASHOFFSET:
276 case wxCSS_PROPERTY_STROKE_MITERLIMIT:
277 case wxCSS_PROPERTY_STROKE_OPACITY:
278 case wxCSS_PROPERTY_STROKE_WIDTH:
279 case wxCSS_PROPERTY_OPACITY:
280 case wxCSS_PROPERTY_STOP_OPACITY:
281 case wxCSS_PROPERTY_GLYPH_ORIENTATION_VERTICAL:
282 case wxCSS_PROPERTY_KERNING:
283 case wxCSS_PROPERTY_LETTER_SPACING:
284 case wxCSS_PROPERTY_WORD_SPACING:
285 if (!cssValue)
286 cssValue = new wxCSSPrimitiveValue;
287 ((wxCSSPrimitiveValue*) cssValue)->SetFloatValue(wxCSS_NUMBER, value.GetLength());
288 break;
289 default:
290 break;
291 }
292 } else if (value.GetPropertyType() == wxSVG_ANIMATED_COLOR) {
293 switch (propertyId) {
294 case wxCSS_PROPERTY_COLOR:
295 if (!cssValue)
296 cssValue = new wxCSSPrimitiveValue;
297 ((wxCSSPrimitiveValue*) cssValue)->SetRGBColorValue(value.GetColor());
298 break;
299 // <color>
300 case wxCSS_PROPERTY_FLOOD_COLOR:
301 case wxCSS_PROPERTY_LIGHTING_COLOR:
302 case wxCSS_PROPERTY_STOP_COLOR:
303 if (!cssValue)
304 cssValue = new wxSVGColor;
305 ((wxSVGColor*) cssValue)->SetRGBColor(value.GetColor());
306 break;
307 // <paint>
308 case wxCSS_PROPERTY_FILL:
309 case wxCSS_PROPERTY_STROKE:
310 if (!cssValue)
311 cssValue = new wxSVGPaint;
312 ((wxSVGPaint*) cssValue)->SetRGBColor(value.GetColor());
313 break;
314 default:
315 break;
316 }
317 }
318 if (it == end())
319 (*this)[propertyId] = cssValue;
320 }
321
ParseNumber(const wxString & value)322 double wxCSSStyleDeclaration::ParseNumber(const wxString& value) {
323 double val = 0;
324 value.ToDouble(&val);
325 return val;
326 }
327
328 static wxSortedArrayString* s_cssColors = NULL;
329 #include "css_colors.cpp"
FillCSSColors()330 inline void FillCSSColors() {
331 if (s_cssColors == NULL) {
332 s_cssColors = new wxSortedArrayString;
333 for (unsigned int i = 0;
334 i < sizeof(s_cssNamedColors) / sizeof(s_cssNamedColors[0]); i++)
335 s_cssColors->Add(s_cssNamedColors[i].name);
336 }
337 }
338
ParseColor(const wxString & value)339 wxRGBColor wxCSSStyleDeclaration::ParseColor(const wxString& value) {
340 if (!value.length() || value == wxT("none"))
341 return wxRGBColor();
342 else if (value.GetChar(0) == wxT('#')) {
343 long r = 0, g = 0, b = 0, test;
344 if (!value.Mid(4, 1).ToLong(&test, 16)) {
345 value.Mid(1, 1).ToLong(&r, 16);
346 value.Mid(2, 1).ToLong(&g, 16);
347 value.Mid(3, 1).ToLong(&b, 16);
348 return wxRGBColor((r << 4) | r, (g << 4) | g, (b << 4) | b);
349 } else {
350 value.Mid(1, 2).ToLong(&r, 16);
351 value.Mid(3, 2).ToLong(&g, 16);
352 value.Mid(5, 2).ToLong(&b, 16);
353 return wxRGBColor(r, g, b);
354 }
355 } else if (value.Left(3) == wxT("rgb")) {
356 wxStringTokenizer tkz(value.Mid(3), wxT(",()"));
357 long rgb[3] = { 0, 0, 0 };
358 for (int i = 0; tkz.HasMoreTokens() && i < 3;) {
359 wxString token = tkz.GetNextToken().Strip(wxString::both);
360 if (token.length())
361 token.ToLong(&rgb[i++]);
362 }
363 return wxRGBColor(rgb[0], rgb[1], rgb[2]);
364 } else {
365 FillCSSColors();
366 int num = s_cssColors->Index(value);
367 if (num >= 0)
368 return s_cssNamedColors[num].colour;
369 }
370 return wxRGBColor();
371 }
372
ParseSVGPaint(wxSVGPaint & cssValue,const wxString & value)373 void wxCSSStyleDeclaration::ParseSVGPaint(wxSVGPaint& cssValue, const wxString& value) {
374 wxString val = value;
375 if (val.Left(3) == wxT("url")) {
376 cssValue.SetUri(value.AfterFirst(wxT('(')).BeforeFirst(wxT(')')));
377 val = value.AfterFirst(wxT(')')).Strip(wxString::both);
378 }
379 cssValue.SetRGBColor(ParseColor(val));
380 }
381
382 //////////////////////////////////////////////////////////////////////////////
383 ///////////////////////// wxCSSStyleRef //////////////////////////////////////
384 //////////////////////////////////////////////////////////////////////////////
~wxCSSStyleRef()385 wxCSSStyleRef::~wxCSSStyleRef() {
386 while (size())
387 erase(begin());
388 }
389
Add(const wxCSSStyleDeclaration & style)390 void wxCSSStyleRef::Add(const wxCSSStyleDeclaration& style) {
391 const_iterator it;
392 for (it = style.begin(); it != style.end(); ++it)
393 (*this)[it->first] = it->second;
394 }
395
396