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