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