xref: /haiku/src/apps/cortex/ValControl/NumericValControl.cpp (revision 2a7d6a985060113e4bad8f3eb9efc706fc35cbd3)
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 
NumericValControl(BRect frame,const char * name,BContinuousParameter * param,uint16 wholeDigits,uint16 fractionalDigits,align_mode alignMode,align_flags alignFlags)51 NumericValControl::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 
NumericValControl(BRect frame,const char * name,BMessage * message,uint16 wholeDigits,uint16 fractionalDigits,bool negativeVisible,align_mode alignMode,align_flags alignFlags)78 NumericValControl::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 
~NumericValControl()91 NumericValControl::~NumericValControl()
92 {
93 }
94 
95 
96 BContinuousParameter*
param() const97 NumericValControl::param() const
98 {
99 	return fParam;
100 }
101 
102 
103 void
initSegments()104 NumericValControl::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 
143 void
initConstraintsFromParam()144 NumericValControl::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)
189 void
getConstraints(double * outMinValue,double * outMaxValue)190 NumericValControl::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 
199 status_t
setConstraints(double minValue,double maxValue)200 NumericValControl::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.)
221 double
value() const222 NumericValControl::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.
264 void
setValue(double value,bool setParam)265 NumericValControl::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 
370 void
offsetSegmentValue(ValControlDigitSegment * segment,int64 offset)371 NumericValControl::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 
402 void
mediaParameterChanged()403 NumericValControl::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 
438 void
updateParameter(double value)439 NumericValControl::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 
467 void
setValue(const void * data,size_t size)468 NumericValControl::setValue(const void* data, size_t size)
469 {
470 }
471 
472 
473 void
getValue(void * data,size_t * ioSize)474 NumericValControl::getValue(void* data, size_t* ioSize)
475 {
476 }
477 
478 
479 status_t
setValueFrom(const char * text)480 NumericValControl::setValueFrom(const char* text)
481 {
482 	double d = atof(text);
483 	setValue(d, true);
484 
485 	return B_OK;
486 }
487 
488 
489 status_t
getString(BString & buffer)490 NumericValControl::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 
503 void
MessageReceived(BMessage * pMsg)504 NumericValControl::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 
538 void
_SetDefaultConstraints(bool negativeVisible)539 NumericValControl::_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
549 int64
_ValueFixed() const550 NumericValControl::_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
588 void
_SetValueFixed(int64 fixed)589 NumericValControl::_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