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