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: %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. 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, %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 370void 371NumericValControl::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 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 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 467void 468NumericValControl::setValue(const void* data, size_t size) 469{ 470} 471 472 473void 474NumericValControl::getValue(void* data, size_t* ioSize) 475{ 476} 477 478 479status_t 480NumericValControl::setValueFrom(const char* text) 481{ 482 double d = atof(text); 483 setValue(d, true); 484 485 return B_OK; 486} 487 488 489status_t 490NumericValControl::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 503void 504NumericValControl::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 538void 539NumericValControl::_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 549int64 550NumericValControl::_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 588void 589NumericValControl::_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