xref: /haiku/src/kits/interface/DecimalSpinner.cpp (revision de1e2d9227bcbc9041488d6ed80a728f888babdc)
1 /*
2  * Copyright 2015 Haiku, Inc. All rights reserved.
3  * Distributed under the terms of the MIT license.
4  *
5  * Authors:
6  *		John Scipione, jscipione@gmail.com
7  */
8 
9 
10 #include <DecimalSpinner.h>
11 
12 #include <stdio.h>
13 #include <stdlib.h>
14 
15 #include <PropertyInfo.h>
16 #include <TextView.h>
17 
18 
19 static double
roundTo(double value,uint32 n)20 roundTo(double value, uint32 n)
21 {
22 	return floor(value * pow(10.0, n) + 0.5) / pow(10.0, n);
23 }
24 
25 
26 static property_info sProperties[] = {
27 	{
28 		"MaxValue",
29 		{ B_GET_PROPERTY, 0 },
30 		{ B_DIRECT_SPECIFIER, 0 },
31 		"Returns the maximum value of the spinner.",
32 		0,
33 		{ B_DOUBLE_TYPE }
34 	},
35 	{
36 		"MaxValue",
37 		{ B_SET_PROPERTY, 0 },
38 		{ B_DIRECT_SPECIFIER, 0},
39 		"Sets the maximum value of the spinner.",
40 		0,
41 		{ B_DOUBLE_TYPE }
42 	},
43 
44 	{
45 		"MinValue",
46 		{ B_GET_PROPERTY, 0 },
47 		{ B_DIRECT_SPECIFIER, 0 },
48 		"Returns the minimum value of the spinner.",
49 		0,
50 		{ B_DOUBLE_TYPE }
51 	},
52 	{
53 		"MinValue",
54 		{ B_SET_PROPERTY, 0 },
55 		{ B_DIRECT_SPECIFIER, 0},
56 		"Sets the minimum value of the spinner.",
57 		0,
58 		{ B_DOUBLE_TYPE }
59 	},
60 
61 	{
62 		"Precision",
63 		{ B_SET_PROPERTY, 0 },
64 		{ B_DIRECT_SPECIFIER, 0},
65 		"Sets the number of decimal places of precision of the spinner.",
66 		0,
67 		{ B_UINT32_TYPE }
68 	},
69 	{
70 		"Precision",
71 		{ B_GET_PROPERTY, 0 },
72 		{ B_DIRECT_SPECIFIER, 0 },
73 		"Returns the number of decimal places of precision of the spinner.",
74 		0,
75 		{ B_UINT32_TYPE }
76 	},
77 
78 	{
79 		"Step",
80 		{ B_GET_PROPERTY, 0 },
81 		{ B_DIRECT_SPECIFIER, 0 },
82 		"Returns the step size of the spinner.",
83 		0,
84 		{ B_DOUBLE_TYPE }
85 	},
86 	{
87 		"Step",
88 		{ B_SET_PROPERTY, 0 },
89 		{ B_DIRECT_SPECIFIER, 0},
90 		"Sets the step size of the spinner.",
91 		0,
92 		{ B_DOUBLE_TYPE }
93 	},
94 
95 	{
96 		"Value",
97 		{ B_GET_PROPERTY, 0 },
98 		{ B_DIRECT_SPECIFIER, 0 },
99 		"Returns the value of the spinner.",
100 		0,
101 		{ B_DOUBLE_TYPE }
102 	},
103 	{
104 		"Value",
105 		{ B_SET_PROPERTY, 0 },
106 		{ B_DIRECT_SPECIFIER, 0},
107 		"Sets the value of the spinner.",
108 		0,
109 		{ B_DOUBLE_TYPE }
110 	},
111 
112 	{ 0 }
113 };
114 
115 
116 //	#pragma mark - BDecimalSpinner
117 
118 
BDecimalSpinner(BRect frame,const char * name,const char * label,BMessage * message,uint32 resizingMode,uint32 flags)119 BDecimalSpinner::BDecimalSpinner(BRect frame, const char* name,
120 	const char* label, BMessage* message, uint32 resizingMode, uint32 flags)
121 	:
122 	BAbstractSpinner(frame, name, label, message, resizingMode, flags)
123 {
124 	_InitObject();
125 }
126 
127 
BDecimalSpinner(const char * name,const char * label,BMessage * message,uint32 flags)128 BDecimalSpinner::BDecimalSpinner(const char* name, const char* label,
129 	BMessage* message, uint32 flags)
130 	:
131 	BAbstractSpinner(name, label, message, flags)
132 {
133 	_InitObject();
134 }
135 
136 
BDecimalSpinner(BMessage * data)137 BDecimalSpinner::BDecimalSpinner(BMessage* data)
138 	:
139 	BAbstractSpinner(data)
140 {
141 	_InitObject();
142 
143 	if (data->FindDouble("_min", &fMinValue) != B_OK)
144 		fMinValue = 0.0;
145 
146 	if (data->FindDouble("_max", &fMaxValue) != B_OK)
147 		fMinValue = 100.0;
148 
149 	if (data->FindUInt32("_precision", &fPrecision) != B_OK)
150 		fPrecision = 2;
151 
152 	if (data->FindDouble("_step", &fStep) != B_OK)
153 		fStep = 1.0;
154 
155 	if (data->FindDouble("_val", &fValue) != B_OK)
156 		fValue = 0.0;
157 }
158 
159 
~BDecimalSpinner()160 BDecimalSpinner::~BDecimalSpinner()
161 {
162 }
163 
164 
165 BArchivable*
Instantiate(BMessage * data)166 BDecimalSpinner::Instantiate(BMessage* data)
167 {
168 	if (validate_instantiation(data, "DecimalSpinner"))
169 		return new BDecimalSpinner(data);
170 
171 	return NULL;
172 }
173 
174 
175 status_t
Archive(BMessage * data,bool deep) const176 BDecimalSpinner::Archive(BMessage* data, bool deep) const
177 {
178 	status_t status = BAbstractSpinner::Archive(data, deep);
179 	data->AddString("class", "DecimalSpinner");
180 
181 	if (status == B_OK)
182 		status = data->AddDouble("_min", fMinValue);
183 
184 	if (status == B_OK)
185 		status = data->AddDouble("_max", fMaxValue);
186 
187 	if (status == B_OK)
188 		status = data->AddUInt32("_precision", fPrecision);
189 
190 	if (status == B_OK)
191 		status = data->AddDouble("_step", fStep);
192 
193 	if (status == B_OK)
194 		status = data->AddDouble("_val", fValue);
195 
196 	return status;
197 }
198 
199 
200 status_t
GetSupportedSuites(BMessage * message)201 BDecimalSpinner::GetSupportedSuites(BMessage* message)
202 {
203 	message->AddString("suites", "suite/vnd.Haiku-decimal-spinner");
204 
205 	BPropertyInfo prop_info(sProperties);
206 	message->AddFlat("messages", &prop_info);
207 
208 	return BView::GetSupportedSuites(message);
209 }
210 
211 
212 void
AttachedToWindow()213 BDecimalSpinner::AttachedToWindow()
214 {
215 	SetValue(fValue);
216 
217 	BAbstractSpinner::AttachedToWindow();
218 }
219 
220 
221 void
Decrement()222 BDecimalSpinner::Decrement()
223 {
224 	SetValue(Value() - Step());
225 }
226 
227 
228 void
Increment()229 BDecimalSpinner::Increment()
230 {
231 	SetValue(Value() + Step());
232 }
233 
234 
235 void
SetEnabled(bool enable)236 BDecimalSpinner::SetEnabled(bool enable)
237 {
238 	if (IsEnabled() == enable)
239 		return;
240 
241 	SetIncrementEnabled(enable && Value() < fMaxValue);
242 	SetDecrementEnabled(enable && Value() > fMinValue);
243 
244 	BAbstractSpinner::SetEnabled(enable);
245 }
246 
247 
248 void
SetMinValue(double min)249 BDecimalSpinner::SetMinValue(double min)
250 {
251 	fMinValue = min;
252 	SetValue(Value());
253 }
254 
255 
256 void
SetMaxValue(double max)257 BDecimalSpinner::SetMaxValue(double max)
258 {
259 	fMaxValue = max;
260 	SetValue(Value());
261 }
262 
263 
264 void
Range(double * min,double * max)265 BDecimalSpinner::Range(double* min, double* max)
266 {
267 	*min = fMinValue;
268 	*max = fMaxValue;
269 }
270 
271 
272 void
SetRange(double min,double max)273 BDecimalSpinner::SetRange(double min, double max)
274 {
275 	SetMinValue(min);
276 	SetMaxValue(max);
277 }
278 
279 
280 void
SetValue(int32 value)281 BDecimalSpinner::SetValue(int32 value)
282 {
283 	SetValue((double)value);
284 }
285 
286 
287 void
SetValue(double value)288 BDecimalSpinner::SetValue(double value)
289 {
290 	// clip to range
291 	if (value < fMinValue)
292 		value = fMinValue;
293 	else if (value > fMaxValue)
294 		value = fMaxValue;
295 
296 	// update the text view
297 	char* format;
298 	asprintf(&format, "%%.%" B_PRId32 "f", fPrecision);
299 	char* valueString;
300 	asprintf(&valueString, format, value);
301 	TextView()->SetText(valueString);
302 	free(format);
303 	free(valueString);
304 
305 	// update the up and down arrows
306 	SetIncrementEnabled(IsEnabled() && value < fMaxValue);
307 	SetDecrementEnabled(IsEnabled() && value > fMinValue);
308 
309 	if (value == fValue)
310 		return;
311 
312 	fValue = value;
313 	ValueChanged();
314 
315 	Invoke();
316 	Invalidate();
317 }
318 
319 
320 void
SetValueFromText()321 BDecimalSpinner::SetValueFromText()
322 {
323 	SetValue(roundTo(atof(TextView()->Text()), Precision()));
324 }
325 
326 
327 //	#pragma mark - BDecimalSpinner private methods
328 
329 
330 void
_InitObject()331 BDecimalSpinner::_InitObject()
332 {
333 	fMinValue = 0.0;
334 	fMaxValue = 100.0;
335 	fPrecision = 2;
336 	fStep = 1.0;
337 	fValue = 0.0;
338 
339 	TextView()->SetAlignment(B_ALIGN_RIGHT);
340 	for (uint32 c = 0; c <= 42; c++)
341 		TextView()->DisallowChar(c);
342 
343 	TextView()->DisallowChar('/');
344 	for (uint32 c = 58; c <= 127; c++)
345 		TextView()->DisallowChar(c);
346 }
347 
348 
349 // FBC padding
350 
_ReservedDecimalSpinner20()351 void BDecimalSpinner::_ReservedDecimalSpinner20() {}
_ReservedDecimalSpinner19()352 void BDecimalSpinner::_ReservedDecimalSpinner19() {}
_ReservedDecimalSpinner18()353 void BDecimalSpinner::_ReservedDecimalSpinner18() {}
_ReservedDecimalSpinner17()354 void BDecimalSpinner::_ReservedDecimalSpinner17() {}
_ReservedDecimalSpinner16()355 void BDecimalSpinner::_ReservedDecimalSpinner16() {}
_ReservedDecimalSpinner15()356 void BDecimalSpinner::_ReservedDecimalSpinner15() {}
_ReservedDecimalSpinner14()357 void BDecimalSpinner::_ReservedDecimalSpinner14() {}
_ReservedDecimalSpinner13()358 void BDecimalSpinner::_ReservedDecimalSpinner13() {}
_ReservedDecimalSpinner12()359 void BDecimalSpinner::_ReservedDecimalSpinner12() {}
_ReservedDecimalSpinner11()360 void BDecimalSpinner::_ReservedDecimalSpinner11() {}
_ReservedDecimalSpinner10()361 void BDecimalSpinner::_ReservedDecimalSpinner10() {}
_ReservedDecimalSpinner9()362 void BDecimalSpinner::_ReservedDecimalSpinner9() {}
_ReservedDecimalSpinner8()363 void BDecimalSpinner::_ReservedDecimalSpinner8() {}
_ReservedDecimalSpinner7()364 void BDecimalSpinner::_ReservedDecimalSpinner7() {}
_ReservedDecimalSpinner6()365 void BDecimalSpinner::_ReservedDecimalSpinner6() {}
_ReservedDecimalSpinner5()366 void BDecimalSpinner::_ReservedDecimalSpinner5() {}
_ReservedDecimalSpinner4()367 void BDecimalSpinner::_ReservedDecimalSpinner4() {}
_ReservedDecimalSpinner3()368 void BDecimalSpinner::_ReservedDecimalSpinner3() {}
_ReservedDecimalSpinner2()369 void BDecimalSpinner::_ReservedDecimalSpinner2() {}
_ReservedDecimalSpinner1()370 void BDecimalSpinner::_ReservedDecimalSpinner1() {}
371