1 // This file is part of VSTGUI. It is subject to the license terms
2 // in the LICENSE file found in the top-level directory of this
3 // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE
4 
5 #include "uiattributes.h"
6 #include "cstream.h"
7 #include "../lib/cpoint.h"
8 #include "../lib/crect.h"
9 #include "../lib/cstring.h"
10 #include <sstream>
11 #include <algorithm>
12 
13 namespace VSTGUI {
14 namespace {
15 
16 //------------------------------------------------------------------------
17 template<bool OnlyInteger>
trimmedNumericalString(const std::string & str,size_t from,size_t numChars)18 Optional<std::string> trimmedNumericalString (const std::string& str, size_t from, size_t numChars)
19 {
20 	std::string result;
21 	auto strSize = str.size ();
22 	if (from >= strSize)
23 		return {};
24 	auto points = 0u;
25 	auto to = numChars == std::string::npos ? strSize : from + numChars;
26 	for (auto i = from; i < to && i < strSize; ++i)
27 	{
28 		auto c = str[i];
29 		if (!std::isspace (c))
30 		{
31 			if (!std::isdigit (c) && c != '-' && c != '+')
32 			{
33 				if (!OnlyInteger && c == '.' && points == 0u)
34 					++points;
35 				else if (points == 1u && c == 'e')
36 				{
37 				}
38 				else
39 					return {};
40 			}
41 			result.push_back (c);
42 		}
43 	}
44 	return Optional<std::string> {std::move (result)};
45 }
46 
47 } // anonymous
48 
49 //-----------------------------------------------------------------------------
pointToString(CPoint p)50 std::string UIAttributes::pointToString (CPoint p)
51 {
52 	return doubleToString (p.x) + ", " + doubleToString (p.y);
53 }
54 
55 //-----------------------------------------------------------------------------
stringToPoint(const std::string & str,CPoint & p)56 bool UIAttributes::stringToPoint (const std::string& str, CPoint& p)
57 {
58 	size_t start = 0;
59 	size_t pos = str.find (",", start, 1);
60 	if (pos != std::string::npos)
61 	{
62 		std::vector<std::string> subStrings;
63 		while (pos != std::string::npos)
64 		{
65 			if (!subStrings.empty ())
66 				return false;
67 			if (auto subStr = trimmedNumericalString<false> (str, start, pos - start))
68 				subStrings.emplace_back (std::move (*subStr));
69 			else
70 				return false;
71 			start = pos + 1;
72 			pos = str.find (",", start, 1);
73 		}
74 		if (auto subStr = trimmedNumericalString<false> (str, start, std::string::npos))
75 			subStrings.emplace_back (std::move (*subStr));
76 		else
77 			return false;
78 		if (subStrings.size () == 2)
79 		{
80 			p.x = UTF8StringView (subStrings[0].data ()).toDouble ();
81 			p.y = UTF8StringView (subStrings[1].data ()).toDouble ();
82 			return true;
83 		}
84 	}
85 	return false;
86 }
87 
88 //------------------------------------------------------------------------
doubleToString(double value,uint32_t precision)89 std::string UIAttributes::doubleToString (double value, uint32_t precision)
90 {
91 	std::stringstream str;
92 	str.imbue (std::locale::classic ());
93 	str.precision (precision);
94 	str << value;
95 	return str.str ();
96 }
97 
98 //-----------------------------------------------------------------------------
stringToDouble(const std::string & str,double & value)99 bool UIAttributes::stringToDouble (const std::string& str, double& value)
100 {
101 	if (auto string = trimmedNumericalString<false> (str, 0, str.size ()))
102 	{
103 		std::istringstream sstream (*string);
104 		sstream.imbue (std::locale::classic ());
105 		sstream >> value;
106 		return sstream.fail () == false;
107 	}
108 	return false;
109 }
110 
111 //-----------------------------------------------------------------------------
boolToString(bool value)112 std::string UIAttributes::boolToString (bool value)
113 {
114 	return value ? "true" : "false";
115 }
116 
117 //-----------------------------------------------------------------------------
stringToBool(const std::string & str,bool & value)118 bool UIAttributes::stringToBool (const std::string& str, bool& value)
119 {
120 	if (str == "true")
121 	{
122 		value = true;
123 		return true;
124 	}
125 	if (str == "false")
126 	{
127 		value = false;
128 		return true;
129 	}
130 	return false;
131 }
132 
133 //-----------------------------------------------------------------------------
integerToString(int32_t value)134 std::string UIAttributes::integerToString (int32_t value)
135 {
136 	std::stringstream str;
137 	str << value;
138 	return str.str ();
139 }
140 
141 //-----------------------------------------------------------------------------
stringToInteger(const std::string & str,int32_t & value)142 bool UIAttributes::stringToInteger (const std::string& str, int32_t& value)
143 {
144 	if (auto string = trimmedNumericalString<true> (str, 0, str.size ()))
145 	{
146 		std::istringstream sstream (*string);
147 		sstream.imbue (std::locale::classic ());
148 		sstream >> value;
149 		return sstream.fail () == false;
150 	}
151 	return false;
152 }
153 
154 //-----------------------------------------------------------------------------
rectToString(CRect r,uint32_t precision)155 std::string UIAttributes::rectToString (CRect r, uint32_t precision)
156 {
157 	return doubleToString (r.left) + ", " + doubleToString (r.top) + ", " +
158 	       doubleToString (r.right) + ", " + doubleToString (r.bottom);
159 }
160 
161 //-----------------------------------------------------------------------------
stringToRect(const std::string & str,CRect & r)162 bool UIAttributes::stringToRect (const std::string& str, CRect& r)
163 {
164 	size_t start = 0;
165 	size_t pos = str.find (",", start, 1);
166 	if (pos != std::string::npos)
167 	{
168 		std::vector<std::string> subStrings;
169 		while (pos != std::string::npos)
170 		{
171 			if (subStrings.size () >= 3)
172 				return false;
173 			if (auto subStr = trimmedNumericalString<false> (str, start, pos - start))
174 				subStrings.emplace_back (std::move (*subStr));
175 			else
176 				return false;
177 			start = pos + 1;
178 			pos = str.find (",", start, 1);
179 		}
180 		if (auto subStr = trimmedNumericalString<false> (str, start, std::string::npos))
181 			subStrings.emplace_back (std::move (*subStr));
182 		else
183 			return false;
184 		if (subStrings.size () == 4)
185 		{
186 			r.left = UTF8StringView (subStrings[0].data ()).toDouble ();
187 			r.top = UTF8StringView (subStrings[1].data ()).toDouble ();
188 			r.right = UTF8StringView (subStrings[2].data ()).toDouble ();
189 			r.bottom = UTF8StringView (subStrings[3].data ()).toDouble ();
190 			return true;
191 		}
192 	}
193 	return false;
194 }
195 
196 //-----------------------------------------------------------------------------
stringArrayToString(const StringArray & values)197 std::string UIAttributes::stringArrayToString (const StringArray& values)
198 {
199 	std::string value;
200 	size_t numValues = values.size ();
201 	for (size_t i = 0; i < numValues - 1; i++)
202 	{
203 		value += values[i];
204 		value += ',';
205 	}
206 	value += values[numValues-1];
207 	return value;
208 }
209 
210 //-----------------------------------------------------------------------------
stringToStringArray(const std::string & str,std::vector<std::string> & values)211 bool UIAttributes::stringToStringArray (const std::string& str, std::vector<std::string>& values)
212 {
213 	std::stringstream ss (str);
214 	std::string item;
215 	while (std::getline (ss, item, ','))
216 	{
217 		values.emplace_back (std::move (item));
218 	}
219 	return true;
220 }
221 
222 //-----------------------------------------------------------------------------
223 //-----------------------------------------------------------------------------
224 //-----------------------------------------------------------------------------
UIAttributes(UTF8StringPtr * attributes)225 UIAttributes::UIAttributes (UTF8StringPtr* attributes)
226 {
227 	if (attributes)
228 	{
229 		int32_t i = 0;
230 		while (attributes[i] != nullptr && attributes[i+1] != nullptr)
231 		{
232 			emplace (attributes[i], attributes[i+1]);
233 			i += 2;
234 		}
235 	}
236 }
237 
238 //-----------------------------------------------------------------------------
hasAttribute(const std::string & name) const239 bool UIAttributes::hasAttribute (const std::string& name) const
240 {
241 	if (getAttributeValue (name) != nullptr)
242 		return true;
243 	return false;
244 }
245 
246 //-----------------------------------------------------------------------------
getAttributeValue(const std::string & name) const247 const std::string* UIAttributes::getAttributeValue (const std::string& name) const
248 {
249 	const_iterator iter = find (name);
250 	if (iter != end ())
251 		return &iter->second;
252 	return nullptr;
253 }
254 
255 //-----------------------------------------------------------------------------
setAttribute(const std::string & name,const std::string & value)256 void UIAttributes::setAttribute (const std::string& name, const std::string& value)
257 {
258 	iterator iter = find (name);
259 	if (iter != end ())
260 		iter->second = value;
261 	else
262 		emplace (name, value);
263 }
264 
265 //-----------------------------------------------------------------------------
setAttribute(const std::string & name,std::string && value)266 void UIAttributes::setAttribute (const std::string& name, std::string&& value)
267 {
268 	iterator iter = find (name);
269 	if (iter != end ())
270 		iter->second = std::move (value);
271 	else
272 		emplace (name, std::move (value));
273 }
274 
275 //-----------------------------------------------------------------------------
setAttribute(std::string && name,std::string && value)276 void UIAttributes::setAttribute (std::string&& name, std::string&& value)
277 {
278 	iterator iter = find (name);
279 	if (iter != end ())
280 		iter->second = std::move (value);
281 	else
282 		emplace (std::move (name), std::move (value));
283 }
284 
285 //-----------------------------------------------------------------------------
removeAttribute(const std::string & name)286 void UIAttributes::removeAttribute (const std::string& name)
287 {
288 	iterator iter = find (name);
289 	if (iter != end ())
290 		erase (iter);
291 }
292 
293 //-----------------------------------------------------------------------------
setDoubleAttribute(const std::string & name,double value)294 void UIAttributes::setDoubleAttribute (const std::string& name, double value)
295 {
296 	setAttribute (name, doubleToString (value, 40));
297 }
298 
299 //-----------------------------------------------------------------------------
getDoubleAttribute(const std::string & name,double & value) const300 bool UIAttributes::getDoubleAttribute (const std::string& name, double& value) const
301 {
302 	if (auto str = getAttributeValue (name))
303 		return stringToDouble (*str, value);
304 	return false;
305 }
306 
307 //-----------------------------------------------------------------------------
setBooleanAttribute(const std::string & name,bool value)308 void UIAttributes::setBooleanAttribute (const std::string& name, bool value)
309 {
310 	setAttribute (name, boolToString (value));
311 }
312 
313 //-----------------------------------------------------------------------------
getBooleanAttribute(const std::string & name,bool & value) const314 bool UIAttributes::getBooleanAttribute (const std::string& name, bool& value) const
315 {
316 	if (auto str = getAttributeValue (name))
317 		return stringToBool (*str, value);
318 	return false;
319 }
320 
321 //-----------------------------------------------------------------------------
setIntegerAttribute(const std::string & name,int32_t value)322 void UIAttributes::setIntegerAttribute (const std::string& name, int32_t value)
323 {
324 	setAttribute (name, integerToString (value));
325 }
326 
327 //-----------------------------------------------------------------------------
getIntegerAttribute(const std::string & name,int32_t & value) const328 bool UIAttributes::getIntegerAttribute (const std::string& name, int32_t& value) const
329 {
330 	if (auto str = getAttributeValue (name))
331 		return stringToInteger(*str, value);
332 	return false;
333 }
334 
335 //-----------------------------------------------------------------------------
setPointAttribute(const std::string & name,const CPoint & p)336 void UIAttributes::setPointAttribute (const std::string& name, const CPoint& p)
337 {
338 	setAttribute (name, pointToString (p));
339 }
340 
341 //-----------------------------------------------------------------------------
getPointAttribute(const std::string & name,CPoint & p) const342 bool UIAttributes::getPointAttribute (const std::string& name, CPoint& p) const
343 {
344 	if (auto str = getAttributeValue (name))
345 		return stringToPoint (*str, p);
346 	return false;
347 }
348 
349 //-----------------------------------------------------------------------------
setRectAttribute(const std::string & name,const CRect & r)350 void UIAttributes::setRectAttribute (const std::string& name, const CRect& r)
351 {
352 	setAttribute (name, rectToString (r));
353 }
354 
355 //-----------------------------------------------------------------------------
getRectAttribute(const std::string & name,CRect & r) const356 bool UIAttributes::getRectAttribute (const std::string& name, CRect& r) const
357 {
358 	if (auto str = getAttributeValue (name))
359 		return stringToRect (*str, r);
360 	return false;
361 }
362 
363 //-----------------------------------------------------------------------------
setStringArrayAttribute(const std::string & name,const StringArray & values)364 void UIAttributes::setStringArrayAttribute (const std::string& name, const StringArray& values)
365 {
366 	setAttribute (name, stringArrayToString (values));
367 }
368 
369 //-----------------------------------------------------------------------------
getStringArrayAttribute(const std::string & name,StringArray & values) const370 bool UIAttributes::getStringArrayAttribute (const std::string& name, StringArray& values) const
371 {
372 	if (auto str = getAttributeValue (name))
373 		return stringToStringArray(*str, values);
374 	return false;
375 }
376 
377 //-----------------------------------------------------------------------------
store(OutputStream & stream) const378 bool UIAttributes::store (OutputStream& stream) const
379 {
380 	if (!(stream << (int32_t)'UIAT')) return false;
381 	if (!(stream << (uint32_t)size ())) return false;
382 	const_iterator it = begin ();
383 	while (it != end ())
384 	{
385 		if (!(stream << (*it).first)) return false;
386 		if (!(stream << (*it).second)) return false;
387 		++it;
388 	}
389 	return true;
390 }
391 
392 //-----------------------------------------------------------------------------
restore(InputStream & stream)393 bool UIAttributes::restore (InputStream& stream)
394 {
395 	int32_t identifier;
396 	if (!(stream >> identifier)) return false;
397 	if (identifier == 'UIAT')
398 	{
399 		uint32_t numAttr;
400 		if (!(stream >> numAttr)) return false;
401 		for (uint32_t i = 0; i < numAttr; i++)
402 		{
403 			std::string key, value;
404 			if (!(stream >> key)) return false;
405 			if (!(stream >> value)) return false;
406 			setAttribute (std::move (key), std::move (value));
407 		}
408 		return true;
409 	}
410 	return false;
411 }
412 
413 }
414