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