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