/*
 * 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.
 */
 
 
// ValControlDigitSegment.cpp
 
#include "ValControlDigitSegment.h"
#include "ValControl.h"
 
#include "NumericValControl.h"
 
#include <Debug.h>
 
#include <math.h>
#include <stdlib.h>
#include <cstdio>
 
__USE_CORTEX_NAMESPACE
 
// -------------------------------------------------------- //
// constants/static stuff
// -------------------------------------------------------- //
 
const float ValControlDigitSegment::s_widthTrim			= -2;
 
const BFont* ValControlDigitSegment::s_cachedFont 	= 0;
float ValControlDigitSegment::s_cachedDigitWidth 		= 0.0;
 
// -------------------------------------------------------- //
// ctor/dtor/accessors
// -------------------------------------------------------- //
 
ValControlDigitSegment::ValControlDigitSegment(
	uint16											digitCount,
	int16												scaleFactor,
	bool												negativeVisible,
	display_flags								flags) :
 
	ValControlSegment(SOLID_UNDERLINE),
 
	m_digitCount(digitCount),
	m_value(0),
	m_negative(false),
	m_scaleFactor(scaleFactor),
	m_font(0),
	m_yOffset(0.0),
	m_minusSignWidth(0.0),
	m_digitPadding(0.0),
	m_flags(flags),
	m_negativeVisible(negativeVisible) {}
 
ValControlDigitSegment::~ValControlDigitSegment() {}
 
uint16 ValControlDigitSegment::digitCount() const {
	return m_digitCount;
}
 
int16 ValControlDigitSegment::scaleFactor() const {
	return m_scaleFactor;
}
 
int64 ValControlDigitSegment::value() const {
	return m_value;
}
 
// -------------------------------------------------------- //
// operations
// -------------------------------------------------------- //
 
// revised setValue() 18sep99: now sets the
// value of the displayed digits ONLY.
//
// a tad simpler. the old setValue() is provided for giggles.
//
void ValControlDigitSegment::setValue(
	int64												value,
	bool												negative) {
 
	if(
		value == m_value &&
		m_negative == negative)
		return;
 
	m_value = value;
	m_negative = negative;
	Invalidate();
}
 
//// +++++
//void ValControlDigitSegment::setValue(double dfValue) {
//
//	printf("seg[%d]::setValue(%.12f)\n", m_digitCount, dfValue);
//
//	// convert possibly-negative value into absolute value and
//	// negative flag
//	bool m_bWasNegative = m_negative;
//	m_negative = (m_negativeVisible && dfValue < 0.0);
//	dfValue = fabs(dfValue);
//
//	// prepare to scale the value to fit the digits this segment
//	// represents
//	bool bMult = m_scaleFactor < 0;
//	int64 nLowPow = m_scaleFactor ? (int64)pow(10.0, abs(m_scaleFactor)) : 1;
//	int64 nHighPow = (int64)pow(10.0, m_digitCount);
//
////	printf("  lowPow %Ld, highPow %Ld\n", nLowPow, nHighPow);
//
//	double dfTemp = bMult ? dfValue * nLowPow : dfValue / nLowPow;
////	printf("  -> %.8lf\n", dfTemp);
//
//	int64 nLocal;
//	if(m_scaleFactor < 0) {
//		// really ugly rounding business: there must be a cleaner
//		// way to do this...
//		double dfC = ceil(dfTemp);
//		double dfCDelta = dfC-dfTemp;
//		double dfF = floor(dfTemp);
//		double dfFDelta = dfTemp - dfF;
//
//		nLocal = (int64)((dfCDelta < dfFDelta) ? dfC : dfF);
//	}
//	else
//		nLocal = (int64)dfTemp;
//
////	printf("  -> %Ld\n", nLocal);
//	nLocal %= nHighPow;
////	printf("  -> %Ld\n", nLocal);
//
//	if(nLocal != m_value || m_negative != m_bWasNegative) {
//		m_value = nLocal;
//		Invalidate();
//	}
//}
 
// -------------------------------------------------------- //
// ValControlSegment impl.
// -------------------------------------------------------- //
 
ValCtrlLayoutEntry ValControlDigitSegment::makeLayoutEntry() {
	return ValCtrlLayoutEntry(this, ValCtrlLayoutEntry::SEGMENT_ENTRY);
}
 
float ValControlDigitSegment::handleDragUpdate(
		float												distance) {
 
	int64 units = (int64)(distance / dragScaleFactor());
	float remaining = distance;
 
	if(units) {
		remaining = fmod(distance, dragScaleFactor());
 
		// +++++ echk [23aug99] -- is this the only way?
		NumericValControl* numericParent = dynamic_cast<NumericValControl*>(parent());
		ASSERT(numericParent);
 
		// adjust value for parent:
//		dfUnits = floor(dfUnits);
//		dfUnits *= pow(10.0, m_scaleFactor);
//
//		// ++++++ 17sep99
//		PRINT((
//			"offset: %.8f\n", dfUnits));
//
//		numericParent->offsetValue(dfUnits);
 
		numericParent->offsetSegmentValue(this, units);
	}
 
	// return 'unused pixels'
	return remaining;
}
 
void ValControlDigitSegment::mouseReleased() {
	// +++++
}
 
// -------------------------------------------------------- //
// BView impl.
// -------------------------------------------------------- //
 
void ValControlDigitSegment::Draw(BRect updateRect) {
 
//	PRINT((
//		"### ValControlDigitSegment::Draw()\n"));
//
 
	ASSERT(m_font);
 
	BBitmap* pBufferBitmap = parent()->backBuffer();
	BView* pView = pBufferBitmap ? parent()->backBufferView() : this;
	if(pBufferBitmap)
		pBufferBitmap->Lock();
 
//	rgb_color white = {255,255,255,255};
	rgb_color black = {0,0,0,255};
	rgb_color disabled = tint_color(black, B_LIGHTEN_2_TINT);
	rgb_color viewColor = ViewColor();
 
	// +++++
 
	BRect b = Bounds();
//	PRINT((
//		"# ValControlDigitSegment::Draw(%.1f,%.1f,%.1f,%.1f) %s\n"
//		"  frame(%.1f,%.1f,%.1f,%.1f)\n\n",
//		updateRect.left, updateRect.top, updateRect.right, updateRect.bottom,
//		pBufferBitmap ? "(BUFFERED)" : "(DIRECT)",
//		Frame().left, Frame().top, Frame().right, Frame().bottom));
 
	float digitWidth = MaxDigitWidth(m_font);
	BPoint p;
	p.x = b.right - digitWidth;
	p.y = m_yOffset;
 
//	// clear background
//	pView->SetHighColor(white);
//	pView->FillRect(b);
 
	// draw a digit at a time, right to left (low->high)
	pView->SetFont(m_font);
	if(parent()->IsEnabled()) {
 
		pView->SetHighColor(black);
	} else {
 
		pView->SetHighColor(disabled);
	}
 
	pView->SetLowColor(viewColor);
	int16 digit;
	int64 cur = abs(m_value);
 
	for(digit = 0;
		digit < m_digitCount;
		digit++, cur /= 10, p.x -= (digitWidth+m_digitPadding)) {
 
		uint8 digitValue = (uint8)(cur % 10);
		if(digit && !(m_flags & ZERO_FILL) && !cur)
			break;
		pView->DrawChar('0' + digitValue, p);
//		PRINT(("ch(%.1f,%.1f): %c\n", p.x, p.y, '0' + digitValue));
	}
 
	if(m_negative) {
		// draw minus sign
		p.x += (digitWidth-m_minusSignWidth);
		pView->DrawChar('-', p);
	}
 
	// paint buffer?
	if(pBufferBitmap) {
		pView->Sync();
		DrawBitmap(parent()->backBuffer(), b, b);
		pBufferBitmap->Unlock();
	}
 
	_inherited::Draw(updateRect);
}
 
// must have parent at this point +++++
void ValControlDigitSegment::GetPreferredSize(float* pWidth, float* pHeight) {
 
//	// font initialized?
//	if(!m_font) {
//		initFont();
//	}
 
	*pWidth = prefWidth();
	*pHeight = prefHeight();
}
 
// +++++ need a way to return an overlap amount?
//       -> underline should extend a pixel to the right.
float ValControlDigitSegment::prefWidth() const {
	ASSERT(m_font);
 
	float width = (m_digitCount*MaxDigitWidth(m_font)) +
		((m_digitCount - 1)*m_digitPadding);
	if(m_negativeVisible)
		width += (m_minusSignWidth + m_digitPadding);
 
	return width;
}
 
float ValControlDigitSegment::prefHeight() const {
	ASSERT(m_font);
	return m_fontHeight.ascent + m_fontHeight.descent + m_fontHeight.leading;
}
 
// do any font-related layout work
void ValControlDigitSegment::fontChanged(
	const BFont*								font) {
//	PRINT((
//		"* ValControlDigitSegment::fontChanged()\n"));
 
	m_font = font;
 
	m_font->GetHeight(&m_fontHeight);
 
	ASSERT(parent());
	m_yOffset = parent()->baselineOffset();
	char c = '-';
	m_minusSignWidth = m_font->StringWidth(&c, 1) + s_widthTrim;
 
	// space between digits should be the same as space between
	// segments, for consistent look:
	m_digitPadding = parent()->segmentPadding();
}
 
// -------------------------------------------------------- //
// BHandler impl.
// -------------------------------------------------------- //
 
void ValControlDigitSegment::MessageReceived(BMessage* pMsg) {
 
	double fVal;
 
	switch(pMsg->what) {
 
		case ValControl::M_SET_VALUE:
			pMsg->FindDouble("value", &fVal);
			setValue((int64)fVal, fVal < 0);
			break;
 
		case ValControl::M_GET_VALUE: {
			BMessage reply(ValControl::M_VALUE);
			reply.AddDouble("value", value());
			pMsg->SendReply(&reply);
			break;
		}
	}
}
 
// -------------------------------------------------------- //
// archiving/instantiation
// -------------------------------------------------------- //
 
ValControlDigitSegment::ValControlDigitSegment(BMessage* pArchive) :
	ValControlSegment(pArchive),
	m_font(0),
	m_digitPadding(0.0) {
 
	// #/digits
	status_t err = pArchive->FindInt16("digits", (int16*)&m_digitCount);
	ASSERT(err == B_OK);
 
	// current value
	err = pArchive->FindInt64("value", &m_value);
	ASSERT(err == B_OK);
 
	// scaling
	err = pArchive->FindInt16("scaleFactor", &m_scaleFactor);
	ASSERT(err == B_OK);
}
 
status_t ValControlDigitSegment::Archive(BMessage* pArchive, bool bDeep) const{
	_inherited::Archive(pArchive, bDeep);
 
	pArchive->AddInt16("digits", m_digitCount);
	pArchive->AddInt64("value", m_value);
	pArchive->AddInt16("scaleFactor", m_scaleFactor);
 
	return B_OK;
}
 
/* static */
BArchivable* ValControlDigitSegment::Instantiate(BMessage* pArchive) {
	if(validate_instantiation(pArchive, "ValControlDigitSegment"))
		return new ValControlDigitSegment(pArchive);
	else
		return 0;
}
 
// -------------------------------------------------------- //
// helpers
// -------------------------------------------------------- //
 
/*static*/
float ValControlDigitSegment::MaxDigitWidth(const BFont* pFont) {
	ASSERT(pFont);
	if(s_cachedFont == pFont)
		return s_cachedDigitWidth;
 
	s_cachedFont = pFont;
	float fMax = 0.0;
	for(char c = '0'; c <= '9'; c++) {
		float fWidth = pFont->StringWidth(&c, 1);
		if(fWidth > fMax)
			fMax = fWidth;
	}
 
	s_cachedDigitWidth = ceil(fMax + s_widthTrim);
	return s_cachedDigitWidth;
}
 
// END -- ValControlDigitSegment.cpp --

V730 Not all members of a class are initialized inside the constructor. Consider inspecting: m_negative, m_fontHeight, m_yOffset, m_minusSignWidth, m_flags, m_negativeVisible.

V730 Not all members of a class are initialized inside the constructor. Consider inspecting: m_fontHeight.