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