1 /******************************************************************************* 2 / 3 / File: ColumnTypes.h 4 / 5 / Description: Experimental classes that implement particular column/field 6 / data types for use in BColumnListView. 7 / 8 / Copyright 2000+, Be Incorporated, All Rights Reserved 9 / 10 *******************************************************************************/ 11 12 #include "ColumnTypes.h" 13 14 #include <View.h> 15 16 #include <parsedate.h> 17 #include <stdio.h> 18 19 20 #define kTEXT_MARGIN 8 21 22 23 const int64 kKB_SIZE = 1024; 24 const int64 kMB_SIZE = 1048576; 25 const int64 kGB_SIZE = 1073741824; 26 const int64 kTB_SIZE = kGB_SIZE * kKB_SIZE; 27 28 const char* kSIZE_FORMATS[] = { 29 "%.2f %s", 30 "%.1f %s", 31 "%.f %s", 32 "%.f%s", 33 0 34 }; 35 36 37 BTitledColumn::BTitledColumn(const char* title, float width, float minWidth, 38 float maxWidth, alignment align) 39 : 40 BColumn(width, minWidth, maxWidth, align), 41 fTitle(title) 42 { 43 font_height fh; 44 45 be_plain_font->GetHeight(&fh); 46 fFontHeight = fh.descent + fh.leading; 47 } 48 49 50 void 51 BTitledColumn::DrawTitle(BRect rect, BView* parent) 52 { 53 float width = rect.Width() - (2 * kTEXT_MARGIN); 54 BString out_string(fTitle); 55 56 parent->TruncateString(&out_string, B_TRUNCATE_END, width + 2); 57 DrawString(out_string.String(), parent, rect); 58 } 59 60 61 void 62 BTitledColumn::GetColumnName(BString* into) const 63 { 64 *into = fTitle; 65 } 66 67 68 void 69 BTitledColumn::DrawString(const char* string, BView* parent, BRect rect) 70 { 71 float width = rect.Width() - (2 * kTEXT_MARGIN); 72 float y; 73 BFont font; 74 font_height finfo; 75 76 parent->GetFont(&font); 77 font.GetHeight(&finfo); 78 y = rect.top + finfo.ascent 79 + (rect.Height() - ceilf(finfo.ascent + finfo.descent)) / 2.0f; 80 81 switch (Alignment()) { 82 default: 83 case B_ALIGN_LEFT: 84 parent->MovePenTo(rect.left + kTEXT_MARGIN, y); 85 break; 86 87 case B_ALIGN_CENTER: 88 parent->MovePenTo(rect.left + kTEXT_MARGIN 89 + ((width - font.StringWidth(string)) / 2), y); 90 break; 91 92 case B_ALIGN_RIGHT: 93 parent->MovePenTo(rect.right - kTEXT_MARGIN 94 - font.StringWidth(string), y); 95 break; 96 } 97 98 parent->DrawString(string); 99 } 100 101 102 void 103 BTitledColumn::SetTitle(const char* title) 104 { 105 fTitle.SetTo(title); 106 } 107 108 109 void 110 BTitledColumn::Title(BString* forTitle) const 111 { 112 if (forTitle) 113 forTitle->SetTo(fTitle.String()); 114 } 115 116 117 float 118 BTitledColumn::FontHeight() const 119 { 120 return fFontHeight; 121 } 122 123 124 float 125 BTitledColumn::GetPreferredWidth(BField *_field, BView* parent) const 126 { 127 return parent->StringWidth(fTitle.String()) + 2 * kTEXT_MARGIN; 128 } 129 130 131 // #pragma mark - BStringField 132 133 134 BStringField::BStringField(const char* string) 135 : 136 fWidth(0), 137 fString(string), 138 fClippedString(string) 139 { 140 } 141 142 143 void 144 BStringField::SetString(const char* val) 145 { 146 fString = val; 147 fClippedString = ""; 148 fWidth = 0; 149 } 150 151 152 const char* 153 BStringField::String() const 154 { 155 return fString.String(); 156 } 157 158 159 void 160 BStringField::SetWidth(float width) 161 { 162 fWidth = width; 163 } 164 165 166 float 167 BStringField::Width() 168 { 169 return fWidth; 170 } 171 172 173 void 174 BStringField::SetClippedString(const char* val) 175 { 176 fClippedString = val; 177 } 178 179 180 bool 181 BStringField::HasClippedString() const 182 { 183 return !fClippedString.IsEmpty(); 184 } 185 186 187 const char* 188 BStringField::ClippedString() 189 { 190 return fClippedString.String(); 191 } 192 193 194 // #pragma mark - BStringColumn 195 196 197 BStringColumn::BStringColumn(const char* title, float width, float minWidth, 198 float maxWidth, uint32 truncate, alignment align) 199 : 200 BTitledColumn(title, width, minWidth, maxWidth, align), 201 fTruncate(truncate) 202 { 203 } 204 205 206 void 207 BStringColumn::DrawField(BField* _field, BRect rect, BView* parent) 208 { 209 float width = rect.Width() - (2 * kTEXT_MARGIN); 210 BStringField* field = static_cast<BStringField*>(_field); 211 float fieldWidth = field->Width(); 212 bool updateNeeded = width != fieldWidth; 213 214 if (updateNeeded) { 215 BString out_string(field->String()); 216 float preferredWidth = parent->StringWidth(out_string.String()); 217 if (width < preferredWidth) { 218 parent->TruncateString(&out_string, fTruncate, width + 2); 219 field->SetClippedString(out_string.String()); 220 } else 221 field->SetClippedString(""); 222 field->SetWidth(width); 223 } 224 225 DrawString(field->HasClippedString() 226 ? field->ClippedString() 227 : field->String(), parent, rect); 228 } 229 230 231 float 232 BStringColumn::GetPreferredWidth(BField *_field, BView* parent) const 233 { 234 BStringField* field = static_cast<BStringField*>(_field); 235 return parent->StringWidth(field->String()) + 2 * kTEXT_MARGIN; 236 } 237 238 239 int 240 BStringColumn::CompareFields(BField* field1, BField* field2) 241 { 242 return ICompare(((BStringField*)field1)->String(), 243 (((BStringField*)field2)->String())); 244 } 245 246 247 bool 248 BStringColumn::AcceptsField(const BField *field) const 249 { 250 return static_cast<bool>(dynamic_cast<const BStringField*>(field)); 251 } 252 253 254 // #pragma mark - BDateField 255 256 257 BDateField::BDateField(time_t* time) 258 : 259 fTime(*localtime(time)), 260 fUnixTime(*time), 261 fSeconds(0), 262 fClippedString(""), 263 fWidth(0) 264 { 265 fSeconds = mktime(&fTime); 266 } 267 268 269 void 270 BDateField::SetWidth(float width) 271 { 272 fWidth = width; 273 } 274 275 276 float 277 BDateField::Width() 278 { 279 return fWidth; 280 } 281 282 283 void 284 BDateField::SetClippedString(const char* string) 285 { 286 fClippedString = string; 287 } 288 289 290 const char* 291 BDateField::ClippedString() 292 { 293 return fClippedString.String(); 294 } 295 296 297 time_t 298 BDateField::Seconds() 299 { 300 return fSeconds; 301 } 302 303 304 time_t 305 BDateField::UnixTime() 306 { 307 return fUnixTime; 308 } 309 310 311 // #pragma mark - BDateColumn 312 313 314 BDateColumn::BDateColumn(const char* title, float width, float minWidth, 315 float maxWidth, alignment align) 316 : 317 BTitledColumn(title, width, minWidth, maxWidth, align), 318 fTitle(title) 319 { 320 } 321 322 323 const char *kTIME_FORMATS[] = { 324 "%A, %B %d %Y, %I:%M:%S %p", // Monday, July 09 1997, 05:08:15 PM 325 "%a, %b %d %Y, %I:%M:%S %p", // Mon, Jul 09 1997, 05:08:15 PM 326 "%a, %b %d %Y, %I:%M %p", // Mon, Jul 09 1997, 05:08 PM 327 "%b %d %Y, %I:%M %p", // Jul 09 1997, 05:08 PM 328 "%m/%d/%y, %I:%M %p", // 07/09/97, 05:08 PM 329 "%m/%d/%y", // 07/09/97 330 NULL 331 }; 332 333 334 void 335 BDateColumn::DrawField(BField* _field, BRect rect, BView* parent) 336 { 337 float width = rect.Width() - (2 * kTEXT_MARGIN); 338 BDateField* field = (BDateField*)_field; 339 340 if (field->Width() != rect.Width()) { 341 char dateString[256]; 342 time_t currentTime = field->UnixTime(); 343 tm time_data; 344 BFont font; 345 346 parent->GetFont(&font); 347 localtime_r(¤tTime, &time_data); 348 349 for (int32 index = 0; ; index++) { 350 if (!kTIME_FORMATS[index]) 351 break; 352 353 strftime(dateString, 256, kTIME_FORMATS[index], &time_data); 354 if (font.StringWidth(dateString) <= width) 355 break; 356 } 357 358 if (font.StringWidth(dateString) > width) { 359 BString out_string(dateString); 360 361 parent->TruncateString(&out_string, B_TRUNCATE_MIDDLE, width + 2); 362 strcpy(dateString, out_string.String()); 363 } 364 field->SetClippedString(dateString); 365 field->SetWidth(width); 366 } 367 368 DrawString(field->ClippedString(), parent, rect); 369 } 370 371 372 int 373 BDateColumn::CompareFields(BField* field1, BField* field2) 374 { 375 return((BDateField*)field1)->Seconds() - ((BDateField*)field2)->Seconds(); 376 } 377 378 379 // #pragma mark - BSizeField 380 381 382 BSizeField::BSizeField(off_t size) 383 : 384 fSize(size) 385 { 386 } 387 388 389 void 390 BSizeField::SetSize(off_t size) 391 { 392 fSize = size; 393 } 394 395 396 off_t 397 BSizeField::Size() 398 { 399 return fSize; 400 } 401 402 403 // #pragma mark - BSizeColumn 404 405 406 BSizeColumn::BSizeColumn(const char* title, float width, float minWidth, 407 float maxWidth, alignment align) 408 : 409 BTitledColumn(title, width, minWidth, maxWidth, align) 410 { 411 } 412 413 414 void 415 BSizeColumn::DrawField(BField* _field, BRect rect, BView* parent) 416 { 417 char str[256]; 418 float width = rect.Width() - (2 * kTEXT_MARGIN); 419 BFont font; 420 BString string; 421 off_t size = ((BSizeField*)_field)->Size(); 422 423 parent->GetFont(&font); 424 if (size < kKB_SIZE) { 425 sprintf(str, "%" B_PRId64 " bytes", size); 426 if (font.StringWidth(str) > width) 427 sprintf(str, "%" B_PRId64 " B", size); 428 } else { 429 const char* suffix; 430 float float_value; 431 if (size >= kTB_SIZE) { 432 suffix = "TB"; 433 float_value = (float)size / kTB_SIZE; 434 } else if (size >= kGB_SIZE) { 435 suffix = "GB"; 436 float_value = (float)size / kGB_SIZE; 437 } else if (size >= kMB_SIZE) { 438 suffix = "MB"; 439 float_value = (float)size / kMB_SIZE; 440 } else { 441 suffix = "KB"; 442 float_value = (float)size / kKB_SIZE; 443 } 444 445 for (int32 index = 0; ; index++) { 446 if (!kSIZE_FORMATS[index]) 447 break; 448 449 sprintf(str, kSIZE_FORMATS[index], float_value, suffix); 450 // strip off an insignificant zero so we don't get readings 451 // such as 1.00 452 char *period = 0; 453 char *tmp (NULL); 454 for (tmp = str; *tmp; tmp++) { 455 if (*tmp == '.') 456 period = tmp; 457 } 458 if (period && period[1] && period[2] == '0') { 459 // move the rest of the string over the insignificant zero 460 for (tmp = &period[2]; *tmp; tmp++) 461 *tmp = tmp[1]; 462 } 463 if (font.StringWidth(str) <= width) 464 break; 465 } 466 } 467 468 string = str; 469 parent->TruncateString(&string, B_TRUNCATE_MIDDLE, width + 2); 470 DrawString(string.String(), parent, rect); 471 } 472 473 474 int 475 BSizeColumn::CompareFields(BField* field1, BField* field2) 476 { 477 return ((BSizeField*)field1)->Size() - ((BSizeField*)field2)->Size(); 478 } 479 480 481 // #pragma mark - BIntegerField 482 483 484 BIntegerField::BIntegerField(int32 number) 485 : 486 fInteger(number) 487 { 488 } 489 490 491 void 492 BIntegerField::SetValue(int32 value) 493 { 494 fInteger = value; 495 } 496 497 498 int32 499 BIntegerField::Value() 500 { 501 return fInteger; 502 } 503 504 505 // #pragma mark - BIntegerColumn 506 507 508 BIntegerColumn::BIntegerColumn(const char* title, float width, float minWidth, 509 float maxWidth, alignment align) 510 : 511 BTitledColumn(title, width, minWidth, maxWidth, align) 512 { 513 } 514 515 516 void 517 BIntegerColumn::DrawField(BField *field, BRect rect, BView* parent) 518 { 519 char formatted[256]; 520 float width = rect.Width() - (2 * kTEXT_MARGIN); 521 BString string; 522 523 sprintf(formatted, "%d", (int)((BIntegerField*)field)->Value()); 524 525 string = formatted; 526 parent->TruncateString(&string, B_TRUNCATE_MIDDLE, width + 2); 527 DrawString(string.String(), parent, rect); 528 } 529 530 531 int 532 BIntegerColumn::CompareFields(BField *field1, BField *field2) 533 { 534 return (((BIntegerField*)field1)->Value() - ((BIntegerField*)field2)->Value()); 535 } 536 537 538 // #pragma mark - GraphColumn 539 540 541 GraphColumn::GraphColumn(const char* name, float width, float minWidth, 542 float maxWidth, alignment align) 543 : 544 BIntegerColumn(name, width, minWidth, maxWidth, align) 545 { 546 } 547 548 549 void 550 GraphColumn::DrawField(BField* field, BRect rect, BView* parent) 551 { 552 int number = ((BIntegerField*)field)->Value(); 553 554 if (number > 100) 555 number = 100; 556 else if (number < 0) 557 number = 0; 558 559 BRect graphRect(rect); 560 graphRect.InsetBy(5, 3); 561 parent->StrokeRect(graphRect); 562 if (number > 0) { 563 graphRect.InsetBy(1, 1); 564 float value = graphRect.Width() * (float)number / 100; 565 graphRect.right = graphRect.left + value; 566 parent->SetHighColor(0, 0, 190); 567 parent->FillRect(graphRect); 568 } 569 570 parent->SetDrawingMode(B_OP_INVERT); 571 parent->SetHighColor(128, 128, 128); 572 char numberString[256]; 573 sprintf(numberString, "%d%%", number); 574 575 float width = be_plain_font->StringWidth(numberString); 576 parent->MovePenTo(rect.left + rect.Width() / 2 - width / 2, rect.bottom - FontHeight()); 577 parent->DrawString(numberString); 578 } 579 580 581 // #pragma mark - BBitmapField 582 583 584 BBitmapField::BBitmapField(BBitmap* bitmap) 585 : 586 fBitmap(bitmap) 587 { 588 } 589 590 591 const BBitmap* 592 BBitmapField::Bitmap() 593 { 594 return fBitmap; 595 } 596 597 598 void 599 BBitmapField::SetBitmap(BBitmap* bitmap) 600 { 601 fBitmap = bitmap; 602 } 603 604 605 // #pragma mark - BBitmapColumn 606 607 608 BBitmapColumn::BBitmapColumn(const char* title, float width, float minWidth, 609 float maxWidth, alignment align) 610 : 611 BTitledColumn(title, width, minWidth, maxWidth, align) 612 { 613 } 614 615 616 void 617 BBitmapColumn::DrawField(BField* field, BRect rect, BView* parent) 618 { 619 BBitmapField* bitmapField = static_cast<BBitmapField*>(field); 620 const BBitmap* bitmap = bitmapField->Bitmap(); 621 622 if (bitmap != NULL) { 623 float x = 0.0; 624 BRect r = bitmap->Bounds(); 625 float y = rect.top + ((rect.Height() - r.Height()) / 2); 626 627 switch (Alignment()) { 628 default: 629 case B_ALIGN_LEFT: 630 x = rect.left + kTEXT_MARGIN; 631 break; 632 633 case B_ALIGN_CENTER: 634 x = rect.left + ((rect.Width() - r.Width()) / 2); 635 break; 636 637 case B_ALIGN_RIGHT: 638 x = rect.right - kTEXT_MARGIN - r.Width(); 639 break; 640 } 641 // setup drawing mode according to bitmap color space, 642 // restore previous mode after drawing 643 drawing_mode oldMode = parent->DrawingMode(); 644 if (bitmap->ColorSpace() == B_RGBA32 645 || bitmap->ColorSpace() == B_RGBA32_BIG) { 646 parent->SetDrawingMode(B_OP_ALPHA); 647 parent->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); 648 } else { 649 parent->SetDrawingMode(B_OP_OVER); 650 } 651 652 parent->DrawBitmap(bitmap, BPoint(x, y)); 653 654 parent->SetDrawingMode(oldMode); 655 } 656 } 657 658 659 int 660 BBitmapColumn::CompareFields(BField* /*field1*/, BField* /*field2*/) 661 { 662 // Comparing bitmaps doesn't really make sense... 663 return 0; 664 } 665 666 667 bool 668 BBitmapColumn::AcceptsField(const BField *field) const 669 { 670 return static_cast<bool>(dynamic_cast<const BBitmapField*>(field)); 671 } 672