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 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 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 91 NumericValControl::~NumericValControl() 92 { 93 } 94 95 96 BContinuousParameter* 97 NumericValControl::param() const 98 { 99 return fParam; 100 } 101 102 103 void 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 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 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 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 222 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 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 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 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 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 468 NumericValControl::setValue(const void* data, size_t size) 469 { 470 } 471 472 473 void 474 NumericValControl::getValue(void* data, size_t* ioSize) 475 { 476 } 477 478 479 status_t 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 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 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 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 550 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 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