1/*
2 * Copyright (c) 1999-2000, Eric Moon.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions, and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions, and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * 3. The name of the author may not be used to endorse or promote products
17 *    derived from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
27 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31
32// NumericValControl.cpp
33// e.moon 30jan99
34
35
36#include "NumericValControl.h"
37#include "ValControlDigitSegment.h"
38#include "ValCtrlLayoutEntry.h"
39
40#include <Debug.h>
41#include <MediaKit.h>
42#include <ParameterWeb.h>
43
44#include <cstdio>
45#include <stdlib.h>
46#include <string.h>
47
48__USE_CORTEX_NAMESPACE
49
50
51NumericValControl::NumericValControl(BRect frame, const char* name, BContinuousParameter* param,
52	uint16 wholeDigits, uint16 fractionalDigits, align_mode alignMode, align_flags alignFlags)
53	: ValControl(frame, name, 0, 0, alignMode, alignFlags, UPDATE_ASYNC, false),
54	fParam(param),
55	fWholeDigits(wholeDigits),
56	fFractionalDigits(fractionalDigits)
57{
58
59	// ensure that the parameter represents a continuous value
60	ASSERT(fParam->ValueType() == B_FLOAT_TYPE ||
61		fParam->ValueType() == B_DOUBLE_TYPE
62		/*||  unimplemented so far
63		m_pParam->ValueType() == B_INT8_TYPE ||
64		m_pParam->ValueType() == B_UINT8_TYPE ||
65		m_pParam->ValueType() == B_INT16_TYPE ||
66		m_pParam->ValueType() == B_UINT16_TYPE ||
67		m_pParam->ValueType() == B_INT32_TYPE ||
68		m_pParam->ValueType() == B_UINT32_TYPE ||
69		m_pParam->ValueType() == B_INT64_TYPE ||
70		m_pParam->ValueType() == B_UINT64_TYPE*/ );
71
72	initConstraintsFromParam();
73	initSegments();
74	mediaParameterChanged();
75}
76
77
78NumericValControl::NumericValControl(BRect frame, const char* name, BMessage* message,
79	uint16 wholeDigits, uint16 fractionalDigits, bool negativeVisible,
80	align_mode alignMode, align_flags alignFlags)
81	: ValControl(frame, name, 0, message, alignMode, alignFlags, UPDATE_ASYNC, false),
82	fParam(0),
83	fWholeDigits(wholeDigits),
84	fFractionalDigits(fractionalDigits)
85{
86	_SetDefaultConstraints(negativeVisible);
87	initSegments();
88}
89
90
91NumericValControl::~NumericValControl()
92{
93}
94
95
96BContinuousParameter*
97NumericValControl::param() const
98{
99	return fParam;
100}
101
102
103void
104NumericValControl::initSegments()
105{
106	ASSERT(fWholeDigits);
107
108	bool negativeVisible = fMinFixed < 0.0;
109
110	// *** SEGMENT DIVISION NEEDS TO BE CONFIGURABLE +++++
111	// GOOD 23aug99
112	// init segments:
113	_Add(new ValControlDigitSegment(fWholeDigits, 0, negativeVisible), RIGHT_MOST);
114
115	if (fFractionalDigits)
116		_Add(ValCtrlLayoutEntry::decimalPoint, RIGHT_MOST);
117
118	for (int n = 0; n < fFractionalDigits; ++n)
119		_Add(new ValControlDigitSegment(1, (-1)-n, false, ValControlDigitSegment::ZERO_FILL),
120			RIGHT_MOST);
121//		add(new ValControlDigitSegment(fFractionalDigits, -fFractionalDigits,
122//			false, ValControlDigitSegment::ZERO_FILL),
123//			RIGHT_MOST);
124//	}
125
126//	// +++++ individual-segment test
127//
128//	for(int n = 0; n < fWholeDigits; ++n)
129//		add(
130//			new ValControlDigitSegment(1, fWholeDigits-n, bNegativeCapable),
131//			RIGHT_MOST);
132//
133//	if(fFractionalDigits)
134//		add(ValCtrlLayoutEntry::decimalPoint, RIGHT_MOST);
135//
136//	for(int n = 0; n < fFractionalDigits; ++n)
137//		add(
138//			new ValControlDigitSegment(1, (-1)-n, false, ValControlDigitSegment::ZERO_FILL),
139//			RIGHT_MOST);
140}
141
142
143void
144NumericValControl::initConstraintsFromParam()
145{
146	ASSERT(fParam);
147
148	printf("NumericValControl::initConstraintsFromParam():\n  ");
149	int r;
150	float fFactor;
151	float fOffset;
152	fParam->GetResponse(&r, &fFactor, &fOffset);
153	switch (r) {
154		case BContinuousParameter::B_LINEAR:
155			printf("Linear");
156			break;
157
158		case BContinuousParameter::B_POLYNOMIAL:
159			printf("Polynomial");
160			break;
161
162		case BContinuousParameter::B_EXPONENTIAL:
163			printf("Exponential");
164			break;
165
166		case BContinuousParameter::B_LOGARITHMIC:
167			printf("Logarithmic");
168			break;
169
170		default:
171			printf("unknown (?)");
172	}
173	printf(" response; factor %.2f, offset %.2f\n", fFactor, fOffset);
174
175	setConstraints(fParam->MinValue(), fParam->MaxValue());
176
177	// step not yet supported +++++ 19sep99
178	//
179	float fStep = fParam->ValueStep();
180
181	printf("  min value: %f\n", fParam->MinValue());
182	printf("  max value: %f\n", fParam->MaxValue());
183	printf("  value step: %f\n\n", fStep);
184}
185
186
187//! Value constraints (by default, the min/max allowed by the
188// setting of nWholeDigits, nFractionalDigits, and bNegativeCapable)
189void
190NumericValControl::getConstraints(double* outMinValue, double* outMaxValue)
191{
192	double factor = pow(10, -fFractionalDigits);
193
194	*outMinValue = (double)fMinFixed * factor;
195	*outMaxValue = (double)fMaxFixed * factor;
196}
197
198
199status_t
200NumericValControl::setConstraints(double minValue, double maxValue)
201{
202	if (maxValue < minValue)
203		return B_BAD_VALUE;
204
205	double factor = pow(10, fFractionalDigits);
206
207	fMinFixed = (minValue < 0.0) ?
208		(int64)floor(minValue * factor) :
209		(int64)ceil(minValue * factor);
210
211	fMaxFixed = (maxValue < 0.0) ?
212		(int64)floor(maxValue * factor) :
213		(int64)ceil(maxValue * factor);
214
215	return B_OK;
216}
217
218
219//! Fetches the current value (calculated on the spot from each
220// segment.)
221double
222NumericValControl::value() const
223{
224//	double acc = 0.0;
225//
226//	// walk segments, adding the value of each
227//	for(int n = CountEntries(); n > 0; --n) {
228//		const ValCtrlLayoutEntry& e = entryAt(n-1);
229//		if(e.type == ValCtrlLayoutEntry::SEGMENT_ENTRY) {
230//			const ValControlDigitSegment* digitSegment =
231//				dynamic_cast<ValControlDigitSegment*>(e.pView);
232//			ASSERT(digitSegment);
233//
234//			PRINT((
235//				"\t...segment %d: %d digits at %d: %lld\n",
236//				n-1,
237//				digitSegment->digitCount(),
238//				digitSegment->scaleFactor(),
239//				digitSegment->value()));
240//
241//			acc += ((double)digitSegment->value() *
242//				pow(
243//					10,
244//					digitSegment->scaleFactor()));
245//
246//			PRINT((
247//				"\t-> %.12f\n\n", acc));
248//		}
249//	}
250//
251//	return acc;
252
253	double ret = (double)_ValueFixed() / pow(10, fFractionalDigits);
254
255//	PRINT((
256//		"### NumericValControl::value(): %.12f\n", ret));
257
258	return ret;
259}
260
261
262//! Set the displayed value (and, if setParam is true, the
263// linked parameter.)  The value will be constrained if necessary.
264void
265NumericValControl::setValue(double value, bool setParam)
266{
267
268//	PRINT((
269//		"### NumericValControl::setValue(%.12f)\n", value));
270
271	// round to displayed precision
272	double scaleFactor = pow(10, fFractionalDigits);
273
274	int64 fixed = (int64)(value * scaleFactor);
275	double junk = (value * scaleFactor) - (double)fixed;
276
277//	PRINT((
278//		" :  junk == %.12f\n", junk));
279
280	if (value < 0.0) {
281		if (junk * scaleFactor < 0.5)
282			fixed--;
283	} else {
284		if (junk * scaleFactor >= 0.5)
285			fixed++;
286	}
287
288	value = (double)fixed / scaleFactor;
289
290//	PRINT((
291//		" -> %.12f, %lld\n", value, fixed));
292
293	_SetValueFixed(fixed);
294
295	// notify target
296	Invoke();
297
298	// set parameter
299	if (setParam && fParam)
300		updateParameter(value);
301
302	// +++++ redraw?
303}
304
305//double NumericValControl::value() const {
306//	return m_dfValue;
307//	/*
308//	double dfCur = 0.0;
309//
310//	// sum the values of all segments
311//	for(int nIndex = CountEntries()-1; nIndex >= 0; nIndex--) {
312//		if(entryAt(nIndex).type == ValCtrlLayoutEntry::SEGMENT_ENTRY)	{
313//			const ValControlDigitSegment* pSeg =
314//				dynamic_cast<ValControlDigitSegment*>(entryAt(nIndex).pView);
315//			ASSERT(pSeg);
316//			dfCur += pSeg->value();
317//		}
318//	}
319//
320//	return dfCur;*/
321//}
322//
323//void NumericValControl::setValue(double dfValue, bool bSetParam) {
324//
325//	printf("setValue(%.12f)\n", dfValue);
326//
327//
328//	// constrain
329//	if(dfValue > m_maxValue)
330//		dfValue = m_maxValue;
331//	else if(dfValue < m_minValue)
332//		dfValue = m_minValue;
333//
334//	// +++++ round to displayed precision
335//
336//	// set value
337//	m_dfValue = dfValue;
338//
339//	// set parameter
340//	if(bSetParam && fParam)
341//		updateParameter();
342//
343//	// notify target (ugh.  what if the target called this? +++++)
344//	Invoke();
345//
346//	// hand value to each segment
347//	for(int nIndex = 0; nIndex < CountEntries(); nIndex++) {
348//		if(entryAt(nIndex).type == ValCtrlLayoutEntry::SEGMENT_ENTRY)	{
349//			const ValControlDigitSegment* pSeg =
350//				dynamic_cast<ValControlDigitSegment*>(entryAt(nIndex).pView);
351//			ASSERT(pSeg);
352//			pSeg->setValue(!nIndex ? m_dfValue : fabs(m_dfValue));
353//		}
354//	}
355//}
356
357// ---------------------------------------------------------------- //
358// segment interface
359// ---------------------------------------------------------------- //
360
361//void NumericValControl::offsetValue(double dfDelta) {
362////	printf("offset: %lf\t", dfDelta);
363//	setValue(value() + dfDelta, true);
364////	printf("%lf\n", value());
365//}
366
367// 18sep99: new segment interface.  'offset' is given
368// in the segment's units.
369
370void
371NumericValControl::offsetSegmentValue(ValControlDigitSegment* segment,
372	int64 offset)
373{
374
375//	PRINT((
376//		"### offsetSegmentValue(): %lld\n",
377//		offset));
378
379	int64 segmentFactor = (int64)pow(10, fFractionalDigits + segment->scaleFactor());
380
381	int64 value = _ValueFixed();
382
383	// cut values below domain of the changed segment
384	value /= segmentFactor;
385
386	// add offset
387	value += offset;
388
389	// restore
390	value *= segmentFactor;
391
392	_SetValueFixed(value);
393
394	// notify target
395	Invoke();
396
397	if (fParam)
398		updateParameter((double)value * (double)segmentFactor);
399}
400
401
402void
403NumericValControl::mediaParameterChanged()
404{
405	// fetch value
406	size_t nSize;
407	bigtime_t tLastChanged;
408	status_t err;
409	switch (fParam->ValueType()) {
410		case B_FLOAT_TYPE:
411		{	// +++++ left-channel hack
412			float fParamValue[4];
413			nSize = sizeof(float) * 4;
414			// +++++ broken
415			err = fParam->GetValue((void*)&fParamValue, &nSize, &tLastChanged);
416			// if (err != B_OK)
417			// break;
418
419			setValue(fParamValue[0]);
420			break;
421		}
422
423		case B_DOUBLE_TYPE:
424		{
425			double fParamValue;
426			nSize = sizeof(double);
427			err = fParam->GetValue((void*)&fParamValue, &nSize, &tLastChanged);
428			if (err != B_OK)
429				break;
430
431			setValue(fParamValue);
432			break;
433		}
434	}
435}
436
437
438void
439NumericValControl::updateParameter(double value)
440{
441	ASSERT(fParam);
442
443//	// is this kosher? +++++
444//	// ++++++ 18sep99: no.
445//	bigtime_t tpNow = system_time();
446
447	// store value
448	switch (fParam->ValueType()) {
449		case B_FLOAT_TYPE:
450		{	// +++++ left-channel hack
451			float fValue[2];
452			fValue[0] = value;
453			fValue[1] = value;
454			fParam->SetValue((void*)&fValue, sizeof(float)*2, 0LL);
455			break;
456		}
457
458		case B_DOUBLE_TYPE: {
459			double fValue = value;
460			fParam->SetValue((void*)&fValue, sizeof(double), 0LL);
461			break;
462		}
463	}
464}
465
466
467void
468NumericValControl::setValue(const void* data, size_t size)
469{
470}
471
472
473void
474NumericValControl::getValue(void* data, size_t* ioSize)
475{
476}
477
478
479status_t
480NumericValControl::setValueFrom(const char* text)
481{
482	double d = atof(text);
483	setValue(d, true);
484
485	return B_OK;
486}
487
488
489status_t
490NumericValControl::getString(BString& buffer)
491{
492	// should provide the same # of digits as the control! +++++
493	BString format = "%.";
494	format << (int32)fFractionalDigits << 'f';
495	char cbuf[120];
496	sprintf(cbuf, format.String(), value());
497	buffer = cbuf;
498
499	return B_OK;
500}
501
502
503void
504NumericValControl::MessageReceived(BMessage* pMsg)
505{
506	status_t err;
507	double dfValue;
508
509	switch (pMsg->what) {
510		case M_SET_VALUE:
511			err = pMsg->FindDouble("value", &dfValue);
512			if (err < B_OK) {
513				_inherited::MessageReceived(pMsg);
514				break;
515			}
516
517			setValue(dfValue);
518			break;
519
520		case B_MEDIA_PARAMETER_CHANGED:
521		{
522			int32 id;
523			if (pMsg->FindInt32("be:parameter", &id) != B_OK)
524				break;
525
526			ASSERT(id == fParam->ID());
527			mediaParameterChanged();
528			break;
529		}
530
531		default:
532			_inherited::MessageReceived(pMsg);
533			break;
534	}
535}
536
537
538void
539NumericValControl::_SetDefaultConstraints(bool negativeVisible)
540{
541	double max = pow(10, fWholeDigits) - pow(10, -fFractionalDigits);
542	double min = (negativeVisible) ? -max : 0.0;
543
544	setConstraints(min, max);
545}
546
547
548//! calculates the current value as an int64
549int64
550NumericValControl::_ValueFixed() const {
551
552//	PRINT((
553//		"### NumericValControl::_ValueFixed()\n", value));
554
555	int64 acc = 0LL;
556
557	int64 scaleBase = fFractionalDigits;
558
559	// walk segments, adding the value of each
560	for (int n = CountEntries(); n > 0; --n) {
561		const ValCtrlLayoutEntry& entry = _EntryAt(n-1);
562		if (entry.type == ValCtrlLayoutEntry::SEGMENT_ENTRY) {
563			const ValControlDigitSegment* digitSegment =
564				dynamic_cast<ValControlDigitSegment*>(entry.pView);
565			ASSERT(digitSegment);
566
567//			PRINT((
568//				"\t...segment %d: %d digits at %d: %lld\n",
569//				n-1,
570//				digitSegment->digitCount(),
571//				digitSegment->scaleFactor(),
572//				digitSegment->value()));
573//
574			acc += digitSegment->value() * (int64)pow(10,
575				scaleBase + digitSegment->scaleFactor());
576//
577//			PRINT((
578//				"\t-> %lld\n\n", acc));
579		}
580	}
581
582	return acc;
583}
584
585
586//! sets the value of each segment based on an int64 value;
587// does not constrain the value
588void
589NumericValControl::_SetValueFixed(int64 fixed)
590{
591//	PRINT((
592//		"### NumericValControl::_SetValueFixed(%lld)\n", fixed));
593
594	// constrain
595	if (fixed > fMaxFixed)
596		fixed = fMaxFixed;
597
598	if (fixed < fMinFixed)
599		fixed = fMinFixed;
600
601	int64 scaleBase = fFractionalDigits;
602
603	// set segments
604	for (int n = CountEntries(); n > 0; --n) {
605		const ValCtrlLayoutEntry& entry = _EntryAt(n-1);
606
607		if (entry.type == ValCtrlLayoutEntry::SEGMENT_ENTRY) {
608			ValControlDigitSegment* digitSegment =
609				dynamic_cast<ValControlDigitSegment*>(entry.pView);
610			ASSERT(digitSegment);
611
612//			PRINT((
613//				"\tsegment %d: %d digits at %d:\n",
614//				n-1,
615//				digitSegment->digitCount(),
616//				digitSegment->scaleFactor()));
617
618			// subtract higher-magnitude segments' value
619			int64 hiCut = fixed % (int64)pow(10,
620				scaleBase + digitSegment->scaleFactor() + digitSegment->digitCount());
621
622//			PRINT((
623//				"\t  [] %lld\n", hiCut));
624
625			// shift value
626			int64 segmentValue = hiCut / (int64)pow(10,
627				scaleBase + digitSegment->scaleFactor());
628
629//			PRINT((
630//				"\t  -> %lld\n\n", segmentValue));
631
632			digitSegment->setValue(segmentValue, fixed < 0);
633		}
634	}
635}
636