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