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 %Ld, highPow %Ld\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("  -> %Ld\n", nLocal);
153//	nLocal %= nHighPow;
154////	printf("  -> %Ld\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	status_t err;
344
345	switch(pMsg->what) {
346
347		case ValControl::M_SET_VALUE:
348			err = pMsg->FindDouble("value", &fVal);
349			ASSERT(err == B_OK);
350			setValue((int64)fVal, fVal < 0);
351			break;
352
353		case ValControl::M_GET_VALUE: {
354			BMessage reply(ValControl::M_VALUE);
355			reply.AddDouble("value", value());
356			pMsg->SendReply(&reply);
357			break;
358		}
359	}
360}
361
362// -------------------------------------------------------- //
363// archiving/instantiation
364// -------------------------------------------------------- //
365
366ValControlDigitSegment::ValControlDigitSegment(BMessage* pArchive) :
367	ValControlSegment(pArchive),
368	m_font(0),
369	m_digitPadding(0.0) {
370
371	// #/digits
372	status_t err = pArchive->FindInt16("digits", (int16*)&m_digitCount);
373	ASSERT(err == B_OK);
374
375	// current value
376	err = pArchive->FindInt64("value", &m_value);
377	ASSERT(err == B_OK);
378
379	// scaling
380	err = pArchive->FindInt16("scaleFactor", &m_scaleFactor);
381	ASSERT(err == B_OK);
382}
383
384status_t ValControlDigitSegment::Archive(BMessage* pArchive, bool bDeep) const{
385	_inherited::Archive(pArchive, bDeep);
386
387	pArchive->AddInt16("digits", m_digitCount);
388	pArchive->AddInt64("value", m_value);
389	pArchive->AddInt16("scaleFactor", m_scaleFactor);
390
391	return B_OK;
392}
393
394/* static */
395BArchivable* ValControlDigitSegment::Instantiate(BMessage* pArchive) {
396	if(validate_instantiation(pArchive, "ValControlDigitSegment"))
397		return new ValControlDigitSegment(pArchive);
398	else
399		return 0;
400}
401
402// -------------------------------------------------------- //
403// helpers
404// -------------------------------------------------------- //
405
406/*static*/
407float ValControlDigitSegment::MaxDigitWidth(const BFont* pFont) {
408	ASSERT(pFont);
409	if(s_cachedFont == pFont)
410		return s_cachedDigitWidth;
411
412	s_cachedFont = pFont;
413	float fMax = 0.0;
414	for(char c = '0'; c <= '9'; c++) {
415		float fWidth = pFont->StringWidth(&c, 1);
416		if(fWidth > fMax)
417			fMax = fWidth;
418	}
419
420	s_cachedDigitWidth = ceil(fMax + s_widthTrim);
421	return s_cachedDigitWidth;
422}
423
424// END -- ValControlDigitSegment.cpp --
425