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