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