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