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	SetValue(Value());
253}
254
255
256void
257BDecimalSpinner::SetMaxValue(double max)
258{
259	fMaxValue = max;
260	SetValue(Value());
261}
262
263
264void
265BDecimalSpinner::Range(double* min, double* max)
266{
267	*min = fMinValue;
268	*max = fMaxValue;
269}
270
271
272void
273BDecimalSpinner::SetRange(double min, double max)
274{
275	SetMinValue(min);
276	SetMaxValue(max);
277}
278
279
280void
281BDecimalSpinner::SetValue(int32 value)
282{
283	SetValue((double)value);
284}
285
286
287void
288BDecimalSpinner::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
320void
321BDecimalSpinner::SetValueFromText()
322{
323	SetValue(roundTo(atof(TextView()->Text()), Precision()));
324}
325
326
327//	#pragma mark - BDecimalSpinner private methods
328
329
330void
331BDecimalSpinner::_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
351void BDecimalSpinner::_ReservedDecimalSpinner20() {}
352void BDecimalSpinner::_ReservedDecimalSpinner19() {}
353void BDecimalSpinner::_ReservedDecimalSpinner18() {}
354void BDecimalSpinner::_ReservedDecimalSpinner17() {}
355void BDecimalSpinner::_ReservedDecimalSpinner16() {}
356void BDecimalSpinner::_ReservedDecimalSpinner15() {}
357void BDecimalSpinner::_ReservedDecimalSpinner14() {}
358void BDecimalSpinner::_ReservedDecimalSpinner13() {}
359void BDecimalSpinner::_ReservedDecimalSpinner12() {}
360void BDecimalSpinner::_ReservedDecimalSpinner11() {}
361void BDecimalSpinner::_ReservedDecimalSpinner10() {}
362void BDecimalSpinner::_ReservedDecimalSpinner9() {}
363void BDecimalSpinner::_ReservedDecimalSpinner8() {}
364void BDecimalSpinner::_ReservedDecimalSpinner7() {}
365void BDecimalSpinner::_ReservedDecimalSpinner6() {}
366void BDecimalSpinner::_ReservedDecimalSpinner5() {}
367void BDecimalSpinner::_ReservedDecimalSpinner4() {}
368void BDecimalSpinner::_ReservedDecimalSpinner3() {}
369void BDecimalSpinner::_ReservedDecimalSpinner2() {}
370void BDecimalSpinner::_ReservedDecimalSpinner1() {}
371