xref: /haiku/src/apps/cortex/ValControl/ValControlDigitSegment.cpp (revision 68ea01249e1e2088933cb12f9c28d4e5c5d1c9ef)
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 
51 const float ValControlDigitSegment::s_widthTrim			= -2;
52 
53 const BFont* ValControlDigitSegment::s_cachedFont 	= 0;
54 float ValControlDigitSegment::s_cachedDigitWidth 		= 0.0;
55 
56 // -------------------------------------------------------- //
57 // ctor/dtor/accessors
58 // -------------------------------------------------------- //
59 
60 ValControlDigitSegment::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 
79 ValControlDigitSegment::~ValControlDigitSegment() {}
80 
81 uint16 ValControlDigitSegment::digitCount() const {
82 	return m_digitCount;
83 }
84 
85 int16 ValControlDigitSegment::scaleFactor() const {
86 	return m_scaleFactor;
87 }
88 
89 int64 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 //
102 void 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 
166 ValCtrlLayoutEntry ValControlDigitSegment::makeLayoutEntry() {
167 	return ValCtrlLayoutEntry(this, ValCtrlLayoutEntry::SEGMENT_ENTRY);
168 }
169 
170 float 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 
200 void ValControlDigitSegment::mouseReleased() {
201 	// +++++
202 }
203 
204 // -------------------------------------------------------- //
205 // BView impl.
206 // -------------------------------------------------------- //
207 
208 void 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 +++++
287 void 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.
300 float 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 
311 float 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
317 void 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 
340 void 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 
364 ValControlDigitSegment::ValControlDigitSegment(BMessage* pArchive) :
365 	ValControlSegment(pArchive),
366 	m_font(0),
367 	m_digitPadding(0.0) {
368 
369 	// #/digits
370 	status_t err = 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 
382 status_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 */
393 BArchivable* 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*/
405 float 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