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