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// ValControlDigitSegment.cpp
33
34#include "ValControlDigitSegment.h"
35#include "ValControl.h"
36
37#include "NumericValControl.h"
38
39#include <Debug.h>
40
41#include <math.h>
42#include <stdlib.h>
43#include <cstdio>
44
45__USE_CORTEX_NAMESPACE
46
47// -------------------------------------------------------- //
48// constants/static stuff
49// -------------------------------------------------------- //
50
51const float ValControlDigitSegment::s_widthTrim			= -2;
52
53const BFont* ValControlDigitSegment::s_cachedFont 	= 0;
54float ValControlDigitSegment::s_cachedDigitWidth 		= 0.0;
55
56// -------------------------------------------------------- //
57// ctor/dtor/accessors
58// -------------------------------------------------------- //
59
60ValControlDigitSegment::ValControlDigitSegment(
61	uint16											digitCount,
62	int16												scaleFactor,
63	bool												negativeVisible,
64	display_flags								flags) :
65
66	ValControlSegment(SOLID_UNDERLINE),
67
68	m_digitCount(digitCount),
69	m_value(0),
70	m_negative(false),
71	m_scaleFactor(scaleFactor),
72	m_font(0),
73	m_yOffset(0.0),
74	m_minusSignWidth(0.0),
75	m_digitPadding(0.0),
76	m_flags(flags),
77	m_negativeVisible(negativeVisible) {}
78
79ValControlDigitSegment::~ValControlDigitSegment() {}
80
81uint16 ValControlDigitSegment::digitCount() const {
82	return m_digitCount;
83}
84
85int16 ValControlDigitSegment::scaleFactor() const {
86	return m_scaleFactor;
87}
88
89int64 ValControlDigitSegment::value() const {
90	return m_value;
91}
92
93// -------------------------------------------------------- //
94// operations
95// -------------------------------------------------------- //
96
97// revised setValue() 18sep99: now sets the
98// value of the displayed digits ONLY.
99//
100// a tad simpler. the old setValue() is provided for giggles.
101//
102void ValControlDigitSegment::setValue(
103	int64												value,
104	bool												negative) {
105
106	if(
107		value == m_value &&
108		m_negative == negative)
109		return;
110
111	m_value = value;
112	m_negative = negative;
113	Invalidate();
114}
115
116//// +++++
117//void ValControlDigitSegment::setValue(double dfValue) {
118//
119//	printf("seg[%d]::setValue(%.12f)\n", m_digitCount, dfValue);
120//
121//	// convert possibly-negative value into absolute value and
122//	// negative flag
123//	bool m_bWasNegative = m_negative;
124//	m_negative = (m_negativeVisible && dfValue < 0.0);
125//	dfValue = fabs(dfValue);
126//
127//	// prepare to scale the value to fit the digits this segment
128//	// represents
129//	bool bMult = m_scaleFactor < 0;
130//	int64 nLowPow = m_scaleFactor ? (int64)pow(10.0, abs(m_scaleFactor)) : 1;
131//	int64 nHighPow = (int64)pow(10.0, m_digitCount);
132//
133////	printf("  lowPow %lld, highPow %lld\n", nLowPow, nHighPow);
134//
135//	double dfTemp = bMult ? dfValue * nLowPow : dfValue / nLowPow;
136////	printf("  -> %.8lf\n", dfTemp);
137//
138//	int64 nLocal;
139//	if(m_scaleFactor < 0) {
140//		// really ugly rounding business: there must be a cleaner
141//		// way to do this...
142//		double dfC = ceil(dfTemp);
143//		double dfCDelta = dfC-dfTemp;
144//		double dfF = floor(dfTemp);
145//		double dfFDelta = dfTemp - dfF;
146//
147//		nLocal = (int64)((dfCDelta < dfFDelta) ? dfC : dfF);
148//	}
149//	else
150//		nLocal = (int64)dfTemp;
151//
152////	printf("  -> %lld\n", nLocal);
153//	nLocal %= nHighPow;
154////	printf("  -> %lld\n", nLocal);
155//
156//	if(nLocal != m_value || m_negative != m_bWasNegative) {
157//		m_value = nLocal;
158//		Invalidate();
159//	}
160//}
161
162// -------------------------------------------------------- //
163// ValControlSegment impl.
164// -------------------------------------------------------- //
165
166ValCtrlLayoutEntry ValControlDigitSegment::makeLayoutEntry() {
167	return ValCtrlLayoutEntry(this, ValCtrlLayoutEntry::SEGMENT_ENTRY);
168}
169
170float ValControlDigitSegment::handleDragUpdate(
171		float												distance) {
172
173	int64 units = (int64)(distance / dragScaleFactor());
174	float remaining = distance;
175
176	if(units) {
177		remaining = fmod(distance, dragScaleFactor());
178
179		// +++++ echk [23aug99] -- is this the only way?
180		NumericValControl* numericParent = dynamic_cast<NumericValControl*>(parent());
181		ASSERT(numericParent);
182
183		// adjust value for parent:
184//		dfUnits = floor(dfUnits);
185//		dfUnits *= pow(10.0, m_scaleFactor);
186//
187//		// ++++++ 17sep99
188//		PRINT((
189//			"offset: %.8f\n", dfUnits));
190//
191//		numericParent->offsetValue(dfUnits);
192
193		numericParent->offsetSegmentValue(this, units);
194	}
195
196	// return 'unused pixels'
197	return remaining;
198}
199
200void ValControlDigitSegment::mouseReleased() {
201	// +++++
202}
203
204// -------------------------------------------------------- //
205// BView impl.
206// -------------------------------------------------------- //
207
208void ValControlDigitSegment::Draw(BRect updateRect) {
209
210//	PRINT((
211//		"### ValControlDigitSegment::Draw()\n"));
212//
213
214	ASSERT(m_font);
215
216	BBitmap* pBufferBitmap = parent()->backBuffer();
217	BView* pView = pBufferBitmap ? parent()->backBufferView() : this;
218	if(pBufferBitmap)
219		pBufferBitmap->Lock();
220
221//	rgb_color white = {255,255,255,255};
222	rgb_color black = {0,0,0,255};
223	rgb_color disabled = tint_color(black, B_LIGHTEN_2_TINT);
224	rgb_color viewColor = ViewColor();
225
226	// +++++
227
228	BRect b = Bounds();
229//	PRINT((
230//		"# ValControlDigitSegment::Draw(%.1f,%.1f,%.1f,%.1f) %s\n"
231//		"  frame(%.1f,%.1f,%.1f,%.1f)\n\n",
232//		updateRect.left, updateRect.top, updateRect.right, updateRect.bottom,
233//		pBufferBitmap ? "(BUFFERED)" : "(DIRECT)",
234//		Frame().left, Frame().top, Frame().right, Frame().bottom));
235
236	float digitWidth = MaxDigitWidth(m_font);
237	BPoint p;
238	p.x = b.right - digitWidth;
239	p.y = m_yOffset;
240
241//	// clear background
242//	pView->SetHighColor(white);
243//	pView->FillRect(b);
244
245	// draw a digit at a time, right to left (low->high)
246	pView->SetFont(m_font);
247	if(parent()->IsEnabled()) {
248
249		pView->SetHighColor(black);
250	} else {
251
252		pView->SetHighColor(disabled);
253	}
254
255	pView->SetLowColor(viewColor);
256	int16 digit;
257	int64 cur = abs(m_value);
258
259	for(digit = 0;
260		digit < m_digitCount;
261		digit++, cur /= 10, p.x -= (digitWidth+m_digitPadding)) {
262
263		uint8 digitValue = (uint8)(cur % 10);
264		if(digit && !(m_flags & ZERO_FILL) && !cur)
265			break;
266		pView->DrawChar('0' + digitValue, p);
267//		PRINT(("ch(%.1f,%.1f): %c\n", p.x, p.y, '0' + digitValue));
268	}
269
270	if(m_negative) {
271		// draw minus sign
272		p.x += (digitWidth-m_minusSignWidth);
273		pView->DrawChar('-', p);
274	}
275
276	// paint buffer?
277	if(pBufferBitmap) {
278		pView->Sync();
279		DrawBitmap(parent()->backBuffer(), b, b);
280		pBufferBitmap->Unlock();
281	}
282
283	_inherited::Draw(updateRect);
284}
285
286// must have parent at this point +++++
287void ValControlDigitSegment::GetPreferredSize(float* pWidth, float* pHeight) {
288
289//	// font initialized?
290//	if(!m_font) {
291//		initFont();
292//	}
293
294	*pWidth = prefWidth();
295	*pHeight = prefHeight();
296}
297
298// +++++ need a way to return an overlap amount?
299//       -> underline should extend a pixel to the right.
300float ValControlDigitSegment::prefWidth() const {
301	ASSERT(m_font);
302
303	float width = (m_digitCount*MaxDigitWidth(m_font)) +
304		((m_digitCount - 1)*m_digitPadding);
305	if(m_negativeVisible)
306		width += (m_minusSignWidth + m_digitPadding);
307
308	return width;
309}
310
311float ValControlDigitSegment::prefHeight() const {
312	ASSERT(m_font);
313	return m_fontHeight.ascent + m_fontHeight.descent + m_fontHeight.leading;
314}
315
316// do any font-related layout work
317void ValControlDigitSegment::fontChanged(
318	const BFont*								font) {
319//	PRINT((
320//		"* ValControlDigitSegment::fontChanged()\n"));
321
322	m_font = font;
323
324	m_font->GetHeight(&m_fontHeight);
325
326	ASSERT(parent());
327	m_yOffset = parent()->baselineOffset();
328	char c = '-';
329	m_minusSignWidth = m_font->StringWidth(&c, 1) + s_widthTrim;
330
331	// space between digits should be the same as space between
332	// segments, for consistent look:
333	m_digitPadding = parent()->segmentPadding();
334}
335
336// -------------------------------------------------------- //
337// BHandler impl.
338// -------------------------------------------------------- //
339
340void ValControlDigitSegment::MessageReceived(BMessage* pMsg) {
341
342	double fVal;
343
344	switch(pMsg->what) {
345
346		case ValControl::M_SET_VALUE:
347			pMsg->FindDouble("value", &fVal);
348			setValue((int64)fVal, fVal < 0);
349			break;
350
351		case ValControl::M_GET_VALUE: {
352			BMessage reply(ValControl::M_VALUE);
353			reply.AddDouble("value", value());
354			pMsg->SendReply(&reply);
355			break;
356		}
357	}
358}
359
360// -------------------------------------------------------- //
361// archiving/instantiation
362// -------------------------------------------------------- //
363
364ValControlDigitSegment::ValControlDigitSegment(BMessage* pArchive) :
365	ValControlSegment(pArchive),
366	m_font(0),
367	m_digitPadding(0.0) {
368
369	// #/digits
370	status_t err __attribute__((unused)) = pArchive->FindInt16("digits", (int16*)&m_digitCount);
371	ASSERT(err == B_OK);
372
373	// current value
374	err = pArchive->FindInt64("value", &m_value);
375	ASSERT(err == B_OK);
376
377	// scaling
378	err = pArchive->FindInt16("scaleFactor", &m_scaleFactor);
379	ASSERT(err == B_OK);
380}
381
382status_t ValControlDigitSegment::Archive(BMessage* pArchive, bool bDeep) const{
383	_inherited::Archive(pArchive, bDeep);
384
385	pArchive->AddInt16("digits", m_digitCount);
386	pArchive->AddInt64("value", m_value);
387	pArchive->AddInt16("scaleFactor", m_scaleFactor);
388
389	return B_OK;
390}
391
392/* static */
393BArchivable* ValControlDigitSegment::Instantiate(BMessage* pArchive) {
394	if(validate_instantiation(pArchive, "ValControlDigitSegment"))
395		return new ValControlDigitSegment(pArchive);
396	else
397		return 0;
398}
399
400// -------------------------------------------------------- //
401// helpers
402// -------------------------------------------------------- //
403
404/*static*/
405float ValControlDigitSegment::MaxDigitWidth(const BFont* pFont) {
406	ASSERT(pFont);
407	if(s_cachedFont == pFont)
408		return s_cachedDigitWidth;
409
410	s_cachedFont = pFont;
411	float fMax = 0.0;
412	for(char c = '0'; c <= '9'; c++) {
413		float fWidth = pFont->StringWidth(&c, 1);
414		if(fWidth > fMax)
415			fMax = fWidth;
416	}
417
418	s_cachedDigitWidth = ceil(fMax + s_widthTrim);
419	return s_cachedDigitWidth;
420}
421
422// END -- ValControlDigitSegment.cpp --
423