⛏️ index : haiku.git

/*
 * Copyright (c) 1999-2000, Eric Moon.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions, and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions, and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */


// NumericValControl.cpp
// e.moon 30jan99


#include "NumericValControl.h"
#include "ValControlDigitSegment.h"
#include "ValCtrlLayoutEntry.h"

#include <Debug.h>
#include <MediaKit.h>
#include <ParameterWeb.h>

#include <cstdio>
#include <stdlib.h>
#include <string.h>

__USE_CORTEX_NAMESPACE


NumericValControl::NumericValControl(BRect frame, const char* name, BContinuousParameter* param,
	uint16 wholeDigits, uint16 fractionalDigits, align_mode alignMode, align_flags alignFlags)
	: ValControl(frame, name, 0, 0, alignMode, alignFlags, UPDATE_ASYNC, false),
	fParam(param),
	fWholeDigits(wholeDigits),
	fFractionalDigits(fractionalDigits)
{
	
	// ensure that the parameter represents a continuous value
	ASSERT(fParam->ValueType() == B_FLOAT_TYPE ||
		fParam->ValueType() == B_DOUBLE_TYPE 		
		/*||  unimplemented so far
		m_pParam->ValueType() == B_INT8_TYPE ||
		m_pParam->ValueType() == B_UINT8_TYPE ||
		m_pParam->ValueType() == B_INT16_TYPE ||
		m_pParam->ValueType() == B_UINT16_TYPE ||
		m_pParam->ValueType() == B_INT32_TYPE ||
		m_pParam->ValueType() == B_UINT32_TYPE ||
		m_pParam->ValueType() == B_INT64_TYPE ||
		m_pParam->ValueType() == B_UINT64_TYPE*/ );
	
	initConstraintsFromParam();
	initSegments();
	mediaParameterChanged();
}


NumericValControl::NumericValControl(BRect frame, const char* name, BMessage* message,
	uint16 wholeDigits, uint16 fractionalDigits, bool negativeVisible,
	align_mode alignMode, align_flags alignFlags)
	: ValControl(frame, name, 0, message, alignMode, alignFlags, UPDATE_ASYNC, false),
	fParam(0),
	fWholeDigits(wholeDigits),
	fFractionalDigits(fractionalDigits)
{
	_SetDefaultConstraints(negativeVisible);
	initSegments();
}


NumericValControl::~NumericValControl()
{
}


BContinuousParameter*
NumericValControl::param() const
{
	return fParam;
}


void
NumericValControl::initSegments()
{
	ASSERT(fWholeDigits);
	
	bool negativeVisible = fMinFixed < 0.0;

	// *** SEGMENT DIVISION NEEDS TO BE CONFIGURABLE +++++
	// GOOD 23aug99
	// init segments:
	_Add(new ValControlDigitSegment(fWholeDigits, 0, negativeVisible), RIGHT_MOST);

	if (fFractionalDigits)
		_Add(ValCtrlLayoutEntry::decimalPoint, RIGHT_MOST);

	for (int n = 0; n < fFractionalDigits; ++n)
		_Add(new ValControlDigitSegment(1, (-1)-n, false, ValControlDigitSegment::ZERO_FILL),
			RIGHT_MOST);
//		add(new ValControlDigitSegment(fFractionalDigits, -fFractionalDigits,
//			false, ValControlDigitSegment::ZERO_FILL),
//			RIGHT_MOST);
//	}

//	// +++++ individual-segment test
//	
//	for(int n = 0; n < fWholeDigits; ++n)
//		add(
//			new ValControlDigitSegment(1, fWholeDigits-n, bNegativeCapable),
//			RIGHT_MOST);
//
//	if(fFractionalDigits)
//		add(ValCtrlLayoutEntry::decimalPoint, RIGHT_MOST);
//		
//	for(int n = 0; n < fFractionalDigits; ++n)
//		add(
//			new ValControlDigitSegment(1, (-1)-n, false, ValControlDigitSegment::ZERO_FILL),
//			RIGHT_MOST);
}


void
NumericValControl::initConstraintsFromParam()
{
	ASSERT(fParam);
	
	printf("NumericValControl::initConstraintsFromParam():\n  ");
	int r;
	float fFactor;
	float fOffset;
	fParam->GetResponse(&r, &fFactor, &fOffset);
	switch (r) {
		case BContinuousParameter::B_LINEAR:
			printf("Linear");
			break;
			
		case BContinuousParameter::B_POLYNOMIAL:
			printf("Polynomial");
			break;
			
		case BContinuousParameter::B_EXPONENTIAL:
			printf("Exponential");
			break;
			
		case BContinuousParameter::B_LOGARITHMIC:
			printf("Logarithmic");
			break;
		
		default:
			printf("unknown (?)");
	}
	printf(" response; factor %.2f, offset %.2f\n", fFactor, fOffset);

	setConstraints(fParam->MinValue(), fParam->MaxValue());

	// step not yet supported +++++ 19sep99
	//		
	float fStep = fParam->ValueStep();
	
	printf("  min value: %f\n", fParam->MinValue());
	printf("  max value: %f\n", fParam->MaxValue());
	printf("  value step: %f\n\n", fStep);
}


//! Value constraints (by default, the min/max allowed by the
// setting of nWholeDigits, nFractionalDigits, and bNegativeCapable)
void
NumericValControl::getConstraints(double* outMinValue, double* outMaxValue)
{
	double factor = pow(10, -fFractionalDigits);

	*outMinValue = (double)fMinFixed * factor;
	*outMaxValue = (double)fMaxFixed * factor;
}

	
status_t
NumericValControl::setConstraints(double minValue, double maxValue)
{	
	if (maxValue < minValue)
		return B_BAD_VALUE;

	double factor = pow(10, fFractionalDigits);

	fMinFixed = (minValue < 0.0) ?
		(int64)floor(minValue * factor) :
		(int64)ceil(minValue * factor);
	
	fMaxFixed = (maxValue < 0.0) ?
		(int64)floor(maxValue * factor) :
		(int64)ceil(maxValue * factor);

	return B_OK;
}


//! Fetches the current value (calculated on the spot from each
// segment.) 
double
NumericValControl::value() const
{
//	double acc = 0.0;
//
//	// walk segments, adding the value of each
//	for(int n = CountEntries(); n > 0; --n) {
//		const ValCtrlLayoutEntry& e = entryAt(n-1);
//		if(e.type == ValCtrlLayoutEntry::SEGMENT_ENTRY) {
//			const ValControlDigitSegment* digitSegment =
//				dynamic_cast<ValControlDigitSegment*>(e.pView);
//			ASSERT(digitSegment);
//
//			PRINT((
//				"\t...segment %d: %d digits at %d: %lld\n",
//				n-1,
//				digitSegment->digitCount(),
//				digitSegment->scaleFactor(),
//				digitSegment->value()));
//
//			acc += ((double)digitSegment->value() *
//				pow(
//					10,
//					digitSegment->scaleFactor()));
//				
//			PRINT((
//				"\t-> %.12f\n\n", acc));
//		}
//	}
//	
//	return acc;

	double ret = (double)_ValueFixed() / pow(10, fFractionalDigits);

//	PRINT((
//		"### NumericValControl::value(): %.12f\n", ret));

	return ret;
}

	
//! Set the displayed value (and, if setParam is true, the
// linked parameter.)  The value will be constrained if necessary.
void
NumericValControl::setValue(double value, bool setParam)
{

//	PRINT((
//		"### NumericValControl::setValue(%.12f)\n", value));

	// round to displayed precision
	double scaleFactor = pow(10, fFractionalDigits);

	int64 fixed = (int64)(value * scaleFactor);
	double junk = (value * scaleFactor) - (double)fixed;
	
//	PRINT((
//		" :  junk == %.12f\n", junk));
	
	if (value < 0.0) {
		if (junk * scaleFactor < 0.5)
			fixed--;
	} else {
		if (junk * scaleFactor >= 0.5)
			fixed++;
	}
	
	value = (double)fixed / scaleFactor;
	
//	PRINT((
//		" -> %.12f, %lld\n", value, fixed));

	_SetValueFixed(fixed);
		
	// notify target 
	Invoke();

	// set parameter
	if (setParam && fParam)
		updateParameter(value);
	
	// +++++ redraw?
}

//double NumericValControl::value() const {
//	return m_dfValue;
//	/*
//	double dfCur = 0.0;
//	
//	// sum the values of all segments
//	for(int nIndex = CountEntries()-1; nIndex >= 0; nIndex--) {
//		if(entryAt(nIndex).type == ValCtrlLayoutEntry::SEGMENT_ENTRY)	{
//			const ValControlDigitSegment* pSeg =
//				dynamic_cast<ValControlDigitSegment*>(entryAt(nIndex).pView);
//			ASSERT(pSeg);
//			dfCur += pSeg->value();
//		}
//	}
//	
//	return dfCur;*/
//}
//
//void NumericValControl::setValue(double dfValue, bool bSetParam) {
//
//	printf("setValue(%.12f)\n", dfValue);
//
//
//	// constrain
//	if(dfValue > m_maxValue)
//		dfValue = m_maxValue;
//	else if(dfValue < m_minValue)
//		dfValue = m_minValue;
//
//	// +++++ round to displayed precision
//
//	// set value
//	m_dfValue = dfValue;
//
//	// set parameter
//	if(bSetParam && fParam)
//		updateParameter();
//	
//	// notify target (ugh.  what if the target called this? +++++)
//	Invoke();
//	
//	// hand value to each segment
//	for(int nIndex = 0; nIndex < CountEntries(); nIndex++) {
//		if(entryAt(nIndex).type == ValCtrlLayoutEntry::SEGMENT_ENTRY)	{
//			const ValControlDigitSegment* pSeg =
//				dynamic_cast<ValControlDigitSegment*>(entryAt(nIndex).pView);
//			ASSERT(pSeg);
//			pSeg->setValue(!nIndex ? m_dfValue : fabs(m_dfValue));
//		}
//	}
//}

// ---------------------------------------------------------------- //
// segment interface
// ---------------------------------------------------------------- //

//void NumericValControl::offsetValue(double dfDelta) {
////	printf("offset: %lf\t", dfDelta);
//	setValue(value() + dfDelta, true);	
////	printf("%lf\n", value());
//}

// 18sep99: new segment interface.  'offset' is given
// in the segment's units.

void
NumericValControl::offsetSegmentValue(ValControlDigitSegment* segment,
	int64 offset)
{

//	PRINT((
//		"### offsetSegmentValue(): %lld\n",
//		offset));
		
	int64 segmentFactor = (int64)pow(10, fFractionalDigits + segment->scaleFactor());

	int64 value = _ValueFixed();
	
	// cut values below domain of the changed segment
	value /= segmentFactor;
	
	// add offset
	value += offset;
	
	// restore
	value *= segmentFactor;
	
	_SetValueFixed(value);

	// notify target 
	Invoke();

	if (fParam)
		updateParameter((double)value * (double)segmentFactor);
}


void
NumericValControl::mediaParameterChanged()
{	
	// fetch value
	size_t nSize;
	bigtime_t tLastChanged;
	status_t err;
	switch (fParam->ValueType()) {
		case B_FLOAT_TYPE:
		{	// +++++ left-channel hack
			float fParamValue[4];
			nSize = sizeof(float) * 4;
			// +++++ broken
			err = fParam->GetValue((void*)&fParamValue, &nSize, &tLastChanged);
			// if (err != B_OK)
			// break;

			setValue(fParamValue[0]);
			break;
		}

		case B_DOUBLE_TYPE:
		{
			double fParamValue;
			nSize = sizeof(double);
			err = fParam->GetValue((void*)&fParamValue, &nSize, &tLastChanged);
			if (err != B_OK)
				break;
		
			setValue(fParamValue);
			break;
		}
	}
}


void
NumericValControl::updateParameter(double value)
{
	ASSERT(fParam);
	
//	// is this kosher? +++++
//	// ++++++ 18sep99: no.
//	bigtime_t tpNow = system_time();

	// store value
	switch (fParam->ValueType()) {
		case B_FLOAT_TYPE:
		{	// +++++ left-channel hack
			float fValue[2];
			fValue[0] = value;
			fValue[1] = value;
			fParam->SetValue((void*)&fValue, sizeof(float)*2, 0LL);
			break;
		}
		
		case B_DOUBLE_TYPE: {
			double fValue = value;
			fParam->SetValue((void*)&fValue, sizeof(double), 0LL);
			break;
		}
	}			
}


void
NumericValControl::setValue(const void* data, size_t size)
{
}


void
NumericValControl::getValue(void* data, size_t* ioSize)
{
}


status_t
NumericValControl::setValueFrom(const char* text)
{
	double d = atof(text);
	setValue(d, true);

	return B_OK;
}


status_t
NumericValControl::getString(BString& buffer)
{
	// should provide the same # of digits as the control! +++++
	BString format = "%.";
	format << (int32)fFractionalDigits << 'f';
	char cbuf[120];
	sprintf(cbuf, format.String(), value());
	buffer = cbuf;

	return B_OK;
}


void
NumericValControl::MessageReceived(BMessage* pMsg)
{
	status_t err;
	double dfValue;

	switch (pMsg->what) {
		case M_SET_VALUE:
			err = pMsg->FindDouble("value", &dfValue);
			if (err < B_OK) {
				_inherited::MessageReceived(pMsg);
				break;
			}
			
			setValue(dfValue);
			break;

		case B_MEDIA_PARAMETER_CHANGED:
		{
			int32 id;
			if (pMsg->FindInt32("be:parameter", &id) != B_OK)
				break;

			ASSERT(id == fParam->ID());
			mediaParameterChanged();
			break;
		}
			
		default:
			_inherited::MessageReceived(pMsg);
			break;
	}
}


void
NumericValControl::_SetDefaultConstraints(bool negativeVisible)
{
	double max = pow(10, fWholeDigits) - pow(10, -fFractionalDigits);
	double min = (negativeVisible) ? -max : 0.0;
	
	setConstraints(min, max);
}


//! calculates the current value as an int64
int64
NumericValControl::_ValueFixed() const {

//	PRINT((
//		"### NumericValControl::_ValueFixed()\n", value));

	int64 acc = 0LL;

	int64 scaleBase = fFractionalDigits;
	
	// walk segments, adding the value of each
	for (int n = CountEntries(); n > 0; --n) {
		const ValCtrlLayoutEntry& entry = _EntryAt(n-1);
		if (entry.type == ValCtrlLayoutEntry::SEGMENT_ENTRY) {
			const ValControlDigitSegment* digitSegment =
				dynamic_cast<ValControlDigitSegment*>(entry.pView);
			ASSERT(digitSegment);

//			PRINT((
//				"\t...segment %d: %d digits at %d: %lld\n",
//				n-1,
//				digitSegment->digitCount(),
//				digitSegment->scaleFactor(),
//				digitSegment->value()));
//
			acc += digitSegment->value() * (int64)pow(10,
				scaleBase + digitSegment->scaleFactor());
//				
//			PRINT((
//				"\t-> %lld\n\n", acc));
		}
	}
	
	return acc;
}


//! sets the value of each segment based on an int64 value;
// does not constrain the value
void
NumericValControl::_SetValueFixed(int64 fixed)
{
//	PRINT((
//		"### NumericValControl::_SetValueFixed(%lld)\n", fixed));

	// constrain
	if (fixed > fMaxFixed)
		fixed = fMaxFixed;

	if (fixed < fMinFixed)
		fixed = fMinFixed;

	int64 scaleBase = fFractionalDigits;

	// set segments
	for (int n = CountEntries(); n > 0; --n) {
		const ValCtrlLayoutEntry& entry = _EntryAt(n-1);
		
		if (entry.type == ValCtrlLayoutEntry::SEGMENT_ENTRY) {
			ValControlDigitSegment* digitSegment =
				dynamic_cast<ValControlDigitSegment*>(entry.pView);
			ASSERT(digitSegment);

//			PRINT((
//				"\tsegment %d: %d digits at %d:\n",
//				n-1,
//				digitSegment->digitCount(),
//				digitSegment->scaleFactor()));
				
			// subtract higher-magnitude segments' value
			int64 hiCut = fixed % (int64)pow(10,
				scaleBase + digitSegment->scaleFactor() + digitSegment->digitCount());
					
//			PRINT((
//				"\t  [] %lld\n", hiCut));

			// shift value
			int64 segmentValue = hiCut / (int64)pow(10,
				scaleBase + digitSegment->scaleFactor());
		
//			PRINT((
//				"\t  -> %lld\n\n", segmentValue));

			digitSegment->setValue(segmentValue, fixed < 0);
		}
	}
}