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
19static double
20roundTo(double value, uint32 n)
21{
22	return floor(value * pow(10.0, n) + 0.5) / pow(10.0, n);
23}
24
25
26static 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
119BDecimalSpinner::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
128BDecimalSpinner::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
137BDecimalSpinner::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
160BDecimalSpinner::~BDecimalSpinner()
161{
162}
163
164
165BArchivable*
166BDecimalSpinner::Instantiate(BMessage* data)
167{
168	if (validate_instantiation(data, "DecimalSpinner"))
169		return new BDecimalSpinner(data);
170
171	return NULL;
172}
173
174
175status_t
176BDecimalSpinner::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
200status_t
201BDecimalSpinner::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
212void
213BDecimalSpinner::AttachedToWindow()
214{
215	SetValue(fValue);
216
217	BAbstractSpinner::AttachedToWindow();
218}
219
220
221void
222BDecimalSpinner::Decrement()
223{
224	SetValue(Value() - Step());
225}
226
227
228void
229BDecimalSpinner::Increment()
230{
231	SetValue(Value() + Step());
232}
233
234
235void
236BDecimalSpinner::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
248void
249BDecimalSpinner::SetMinValue(double min)
250{
251	fMinValue = min;
252	if (fValue < fMinValue)
253		SetValue(fMinValue);
254}
255
256
257void
258BDecimalSpinner::SetMaxValue(double max)
259{
260	fMaxValue = max;
261	if (fValue > fMaxValue)
262		SetValue(fMaxValue);
263}
264
265
266void
267BDecimalSpinner::Range(double* min, double* max)
268{
269	*min = fMinValue;
270	*max = fMaxValue;
271}
272
273
274void
275BDecimalSpinner::SetRange(double min, double max)
276{
277	SetMinValue(min);
278	SetMaxValue(max);
279}
280
281
282void
283BDecimalSpinner::SetValue(int32 value)
284{
285	SetValue((double)value);
286}
287
288
289void
290BDecimalSpinner::SetValue(double value)
291{
292	// clip to range
293	if (value < fMinValue)
294		value = fMinValue;
295	else if (value > fMaxValue)
296		value = fMaxValue;
297
298	// update the text view
299	char* format;
300	asprintf(&format, "%%.%" B_PRId32 "f", fPrecision);
301	char* valueString;
302	asprintf(&valueString, format, value);
303	TextView()->SetText(valueString);
304	free(format);
305	free(valueString);
306
307	// update the up and down arrows
308	SetIncrementEnabled(IsEnabled() && value < fMaxValue);
309	SetDecrementEnabled(IsEnabled() && value > fMinValue);
310
311	if (value == fValue)
312		return;
313
314	fValue = value;
315	ValueChanged();
316
317	Invoke();
318	Invalidate();
319}
320
321
322void
323BDecimalSpinner::SetValueFromText()
324{
325	SetValue(roundTo(atof(TextView()->Text()), Precision()));
326}
327
328
329//	#pragma mark - BDecimalSpinner private methods
330
331
332void
333BDecimalSpinner::_InitObject()
334{
335	fMinValue = 0.0;
336	fMaxValue = 100.0;
337	fPrecision = 2;
338	fStep = 1.0;
339	fValue = 0.0;
340
341	TextView()->SetAlignment(B_ALIGN_RIGHT);
342	for (uint32 c = 0; c <= 42; c++)
343		TextView()->DisallowChar(c);
344
345	TextView()->DisallowChar('/');
346	for (uint32 c = 58; c <= 127; c++)
347		TextView()->DisallowChar(c);
348}
349
350
351// FBC padding
352
353void BDecimalSpinner::_ReservedDecimalSpinner20() {}
354void BDecimalSpinner::_ReservedDecimalSpinner19() {}
355void BDecimalSpinner::_ReservedDecimalSpinner18() {}
356void BDecimalSpinner::_ReservedDecimalSpinner17() {}
357void BDecimalSpinner::_ReservedDecimalSpinner16() {}
358void BDecimalSpinner::_ReservedDecimalSpinner15() {}
359void BDecimalSpinner::_ReservedDecimalSpinner14() {}
360void BDecimalSpinner::_ReservedDecimalSpinner13() {}
361void BDecimalSpinner::_ReservedDecimalSpinner12() {}
362void BDecimalSpinner::_ReservedDecimalSpinner11() {}
363void BDecimalSpinner::_ReservedDecimalSpinner10() {}
364void BDecimalSpinner::_ReservedDecimalSpinner9() {}
365void BDecimalSpinner::_ReservedDecimalSpinner8() {}
366void BDecimalSpinner::_ReservedDecimalSpinner7() {}
367void BDecimalSpinner::_ReservedDecimalSpinner6() {}
368void BDecimalSpinner::_ReservedDecimalSpinner5() {}
369void BDecimalSpinner::_ReservedDecimalSpinner4() {}
370void BDecimalSpinner::_ReservedDecimalSpinner3() {}
371void BDecimalSpinner::_ReservedDecimalSpinner2() {}
372void BDecimalSpinner::_ReservedDecimalSpinner1() {}
373