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: %Ld\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, %Ld\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(): %Ld\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	status_t err;
449	switch (fParam->ValueType()) {
450		case B_FLOAT_TYPE:
451		{	// +++++ left-channel hack
452			float fValue[2];
453			fValue[0] = value;
454			fValue[1] = value;
455			err = fParam->SetValue((void*)&fValue, sizeof(float)*2, 0LL);
456			break;
457		}
458
459		case B_DOUBLE_TYPE: {
460			double fValue = value;
461			err = fParam->SetValue((void*)&fValue, sizeof(double), 0LL);
462			break;
463		}
464	}
465}
466
467
468void
469NumericValControl::setValue(const void* data, size_t size)
470{
471}
472
473
474void
475NumericValControl::getValue(void* data, size_t* ioSize)
476{
477}
478
479
480status_t
481NumericValControl::setValueFrom(const char* text)
482{
483	double d = atof(text);
484	setValue(d, true);
485
486	return B_OK;
487}
488
489
490status_t
491NumericValControl::getString(BString& buffer)
492{
493	// should provide the same # of digits as the control! +++++
494	BString format = "%.";
495	format << (int32)fFractionalDigits << 'f';
496	char cbuf[120];
497	sprintf(cbuf, format.String(), value());
498	buffer = cbuf;
499
500	return B_OK;
501}
502
503
504void
505NumericValControl::MessageReceived(BMessage* pMsg)
506{
507	status_t err;
508	double dfValue;
509
510	switch (pMsg->what) {
511		case M_SET_VALUE:
512			err = pMsg->FindDouble("value", &dfValue);
513			if (err < B_OK) {
514				_inherited::MessageReceived(pMsg);
515				break;
516			}
517
518			setValue(dfValue);
519			break;
520
521		case B_MEDIA_PARAMETER_CHANGED:
522		{
523			int32 id;
524			if (pMsg->FindInt32("be:parameter", &id) != B_OK)
525				break;
526
527			ASSERT(id == fParam->ID());
528			mediaParameterChanged();
529			break;
530		}
531
532		default:
533			_inherited::MessageReceived(pMsg);
534			break;
535	}
536}
537
538
539void
540NumericValControl::_SetDefaultConstraints(bool negativeVisible)
541{
542	double max = pow(10, fWholeDigits) - pow(10, -fFractionalDigits);
543	double min = (negativeVisible) ? -max : 0.0;
544
545	setConstraints(min, max);
546}
547
548
549//! calculates the current value as an int64
550int64
551NumericValControl::_ValueFixed() const {
552
553//	PRINT((
554//		"### NumericValControl::_ValueFixed()\n", value));
555
556	int64 acc = 0LL;
557
558	int64 scaleBase = fFractionalDigits;
559
560	// walk segments, adding the value of each
561	for (int n = CountEntries(); n > 0; --n) {
562		const ValCtrlLayoutEntry& entry = _EntryAt(n-1);
563		if (entry.type == ValCtrlLayoutEntry::SEGMENT_ENTRY) {
564			const ValControlDigitSegment* digitSegment =
565				dynamic_cast<ValControlDigitSegment*>(entry.pView);
566			ASSERT(digitSegment);
567
568//			PRINT((
569//				"\t...segment %d: %d digits at %d: %Ld\n",
570//				n-1,
571//				digitSegment->digitCount(),
572//				digitSegment->scaleFactor(),
573//				digitSegment->value()));
574//
575			acc += digitSegment->value() * (int64)pow(10,
576				scaleBase + digitSegment->scaleFactor());
577//
578//			PRINT((
579//				"\t-> %Ld\n\n", acc));
580		}
581	}
582
583	return acc;
584}
585
586
587//! sets the value of each segment based on an int64 value;
588// does not constrain the value
589void
590NumericValControl::_SetValueFixed(int64 fixed)
591{
592//	PRINT((
593//		"### NumericValControl::_SetValueFixed(%Ld)\n", fixed));
594
595	// constrain
596	if (fixed > fMaxFixed)
597		fixed = fMaxFixed;
598
599	if (fixed < fMinFixed)
600		fixed = fMinFixed;
601
602	int64 scaleBase = fFractionalDigits;
603
604	// set segments
605	for (int n = CountEntries(); n > 0; --n) {
606		const ValCtrlLayoutEntry& entry = _EntryAt(n-1);
607
608		if (entry.type == ValCtrlLayoutEntry::SEGMENT_ENTRY) {
609			ValControlDigitSegment* digitSegment =
610				dynamic_cast<ValControlDigitSegment*>(entry.pView);
611			ASSERT(digitSegment);
612
613//			PRINT((
614//				"\tsegment %d: %d digits at %d:\n",
615//				n-1,
616//				digitSegment->digitCount(),
617//				digitSegment->scaleFactor()));
618
619			// subtract higher-magnitude segments' value
620			int64 hiCut = fixed % (int64)pow(10,
621				scaleBase + digitSegment->scaleFactor() + digitSegment->digitCount());
622
623//			PRINT((
624//				"\t  [] %Ld\n", hiCut));
625
626			// shift value
627			int64 segmentValue = hiCut / (int64)pow(10,
628				scaleBase + digitSegment->scaleFactor());
629
630//			PRINT((
631//				"\t  -> %Ld\n\n", segmentValue));
632
633			digitSegment->setValue(segmentValue, fixed < 0);
634		}
635	}
636}
637