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 off_t diff = ((BSizeField*)field1)->Size() - ((BSizeField*)field2)->Size(); 478 if (diff > 0) 479 return 1; 480 if (diff < 0) 481 return -1; 482 return 0; 483 } 484 485 486 // #pragma mark - BIntegerField 487 488 489 BIntegerField::BIntegerField(int32 number) 490 : 491 fInteger(number) 492 { 493 } 494 495 496 void 497 BIntegerField::SetValue(int32 value) 498 { 499 fInteger = value; 500 } 501 502 503 int32 504 BIntegerField::Value() 505 { 506 return fInteger; 507 } 508 509 510 // #pragma mark - BIntegerColumn 511 512 513 BIntegerColumn::BIntegerColumn(const char* title, float width, float minWidth, 514 float maxWidth, alignment align) 515 : 516 BTitledColumn(title, width, minWidth, maxWidth, align) 517 { 518 } 519 520 521 void 522 BIntegerColumn::DrawField(BField *field, BRect rect, BView* parent) 523 { 524 char formatted[256]; 525 float width = rect.Width() - (2 * kTEXT_MARGIN); 526 BString string; 527 528 sprintf(formatted, "%d", (int)((BIntegerField*)field)->Value()); 529 530 string = formatted; 531 parent->TruncateString(&string, B_TRUNCATE_MIDDLE, width + 2); 532 DrawString(string.String(), parent, rect); 533 } 534 535 536 int 537 BIntegerColumn::CompareFields(BField *field1, BField *field2) 538 { 539 return (((BIntegerField*)field1)->Value() - ((BIntegerField*)field2)->Value()); 540 } 541 542 543 // #pragma mark - GraphColumn 544 545 546 GraphColumn::GraphColumn(const char* name, float width, float minWidth, 547 float maxWidth, alignment align) 548 : 549 BIntegerColumn(name, width, minWidth, maxWidth, align) 550 { 551 } 552 553 554 void 555 GraphColumn::DrawField(BField* field, BRect rect, BView* parent) 556 { 557 int number = ((BIntegerField*)field)->Value(); 558 559 if (number > 100) 560 number = 100; 561 else if (number < 0) 562 number = 0; 563 564 BRect graphRect(rect); 565 graphRect.InsetBy(5, 3); 566 parent->StrokeRect(graphRect); 567 if (number > 0) { 568 graphRect.InsetBy(1, 1); 569 float value = graphRect.Width() * (float)number / 100; 570 graphRect.right = graphRect.left + value; 571 parent->SetHighColor(0, 0, 190); 572 parent->FillRect(graphRect); 573 } 574 575 parent->SetDrawingMode(B_OP_INVERT); 576 parent->SetHighColor(128, 128, 128); 577 char numberString[256]; 578 sprintf(numberString, "%d%%", number); 579 580 float width = be_plain_font->StringWidth(numberString); 581 parent->MovePenTo(rect.left + rect.Width() / 2 - width / 2, rect.bottom - FontHeight()); 582 parent->DrawString(numberString); 583 } 584 585 586 // #pragma mark - BBitmapField 587 588 589 BBitmapField::BBitmapField(BBitmap* bitmap) 590 : 591 fBitmap(bitmap) 592 { 593 } 594 595 596 const BBitmap* 597 BBitmapField::Bitmap() 598 { 599 return fBitmap; 600 } 601 602 603 void 604 BBitmapField::SetBitmap(BBitmap* bitmap) 605 { 606 fBitmap = bitmap; 607 } 608 609 610 // #pragma mark - BBitmapColumn 611 612 613 BBitmapColumn::BBitmapColumn(const char* title, float width, float minWidth, 614 float maxWidth, alignment align) 615 : 616 BTitledColumn(title, width, minWidth, maxWidth, align) 617 { 618 } 619 620 621 void 622 BBitmapColumn::DrawField(BField* field, BRect rect, BView* parent) 623 { 624 BBitmapField* bitmapField = static_cast<BBitmapField*>(field); 625 const BBitmap* bitmap = bitmapField->Bitmap(); 626 627 if (bitmap != NULL) { 628 float x = 0.0; 629 BRect r = bitmap->Bounds(); 630 float y = rect.top + ((rect.Height() - r.Height()) / 2); 631 632 switch (Alignment()) { 633 default: 634 case B_ALIGN_LEFT: 635 x = rect.left + kTEXT_MARGIN; 636 break; 637 638 case B_ALIGN_CENTER: 639 x = rect.left + ((rect.Width() - r.Width()) / 2); 640 break; 641 642 case B_ALIGN_RIGHT: 643 x = rect.right - kTEXT_MARGIN - r.Width(); 644 break; 645 } 646 // setup drawing mode according to bitmap color space, 647 // restore previous mode after drawing 648 drawing_mode oldMode = parent->DrawingMode(); 649 if (bitmap->ColorSpace() == B_RGBA32 650 || bitmap->ColorSpace() == B_RGBA32_BIG) { 651 parent->SetDrawingMode(B_OP_ALPHA); 652 parent->SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); 653 } else { 654 parent->SetDrawingMode(B_OP_OVER); 655 } 656 657 parent->DrawBitmap(bitmap, BPoint(x, y)); 658 659 parent->SetDrawingMode(oldMode); 660 } 661 } 662 663 664 int 665 BBitmapColumn::CompareFields(BField* /*field1*/, BField* /*field2*/) 666 { 667 // Comparing bitmaps doesn't really make sense... 668 return 0; 669 } 670 671 672 bool 673 BBitmapColumn::AcceptsField(const BField *field) const 674 { 675 return static_cast<bool>(dynamic_cast<const BBitmapField*>(field)); 676 } 677