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 23const int64 kKB_SIZE = 1024; 24const int64 kMB_SIZE = 1048576; 25const int64 kGB_SIZE = 1073741824; 26const int64 kTB_SIZE = kGB_SIZE * kKB_SIZE; 27 28const char* kSIZE_FORMATS[] = { 29 "%.2f %s", 30 "%.1f %s", 31 "%.f %s", 32 "%.f%s", 33 0 34}; 35 36 37BTitledColumn::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 50void 51BTitledColumn::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 61void 62BTitledColumn::GetColumnName(BString* into) const 63{ 64 *into = fTitle; 65} 66 67 68void 69BTitledColumn::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 102void 103BTitledColumn::SetTitle(const char* title) 104{ 105 fTitle.SetTo(title); 106} 107 108 109void 110BTitledColumn::Title(BString* forTitle) const 111{ 112 if (forTitle) 113 forTitle->SetTo(fTitle.String()); 114} 115 116 117float 118BTitledColumn::FontHeight() const 119{ 120 return fFontHeight; 121} 122 123 124float 125BTitledColumn::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 134BStringField::BStringField(const char* string) 135 : 136 fWidth(0), 137 fString(string), 138 fClippedString(string) 139{ 140} 141 142 143void 144BStringField::SetString(const char* val) 145{ 146 fString = val; 147 fClippedString = ""; 148 fWidth = 0; 149} 150 151 152const char* 153BStringField::String() const 154{ 155 return fString.String(); 156} 157 158 159void 160BStringField::SetWidth(float width) 161{ 162 fWidth = width; 163} 164 165 166float 167BStringField::Width() 168{ 169 return fWidth; 170} 171 172 173void 174BStringField::SetClippedString(const char* val) 175{ 176 fClippedString = val; 177} 178 179 180bool 181BStringField::HasClippedString() const 182{ 183 return !fClippedString.IsEmpty(); 184} 185 186 187const char* 188BStringField::ClippedString() 189{ 190 return fClippedString.String(); 191} 192 193 194// #pragma mark - BStringColumn 195 196 197BStringColumn::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 206void 207BStringColumn::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 231float 232BStringColumn::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 239int 240BStringColumn::CompareFields(BField* field1, BField* field2) 241{ 242 return ICompare(((BStringField*)field1)->String(), 243 (((BStringField*)field2)->String())); 244} 245 246 247bool 248BStringColumn::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 257BDateField::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 269void 270BDateField::SetWidth(float width) 271{ 272 fWidth = width; 273} 274 275 276float 277BDateField::Width() 278{ 279 return fWidth; 280} 281 282 283void 284BDateField::SetClippedString(const char* string) 285{ 286 fClippedString = string; 287} 288 289 290const char* 291BDateField::ClippedString() 292{ 293 return fClippedString.String(); 294} 295 296 297time_t 298BDateField::Seconds() 299{ 300 return fSeconds; 301} 302 303 304time_t 305BDateField::UnixTime() 306{ 307 return fUnixTime; 308} 309 310 311// #pragma mark - BDateColumn 312 313 314BDateColumn::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 323const 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 334void 335BDateColumn::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 372int 373BDateColumn::CompareFields(BField* field1, BField* field2) 374{ 375 return((BDateField*)field1)->Seconds() - ((BDateField*)field2)->Seconds(); 376} 377 378 379// #pragma mark - BSizeField 380 381 382BSizeField::BSizeField(off_t size) 383 : 384 fSize(size) 385{ 386} 387 388 389void 390BSizeField::SetSize(off_t size) 391{ 392 fSize = size; 393} 394 395 396off_t 397BSizeField::Size() 398{ 399 return fSize; 400} 401 402 403// #pragma mark - BSizeColumn 404 405 406BSizeColumn::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 414void 415BSizeColumn::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 474int 475BSizeColumn::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 489BIntegerField::BIntegerField(int32 number) 490 : 491 fInteger(number) 492{ 493} 494 495 496void 497BIntegerField::SetValue(int32 value) 498{ 499 fInteger = value; 500} 501 502 503int32 504BIntegerField::Value() 505{ 506 return fInteger; 507} 508 509 510// #pragma mark - BIntegerColumn 511 512 513BIntegerColumn::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 521void 522BIntegerColumn::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 536int 537BIntegerColumn::CompareFields(BField *field1, BField *field2) 538{ 539 return (((BIntegerField*)field1)->Value() - ((BIntegerField*)field2)->Value()); 540} 541 542 543// #pragma mark - GraphColumn 544 545 546GraphColumn::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 554void 555GraphColumn::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 589BBitmapField::BBitmapField(BBitmap* bitmap) 590 : 591 fBitmap(bitmap) 592{ 593} 594 595 596const BBitmap* 597BBitmapField::Bitmap() 598{ 599 return fBitmap; 600} 601 602 603void 604BBitmapField::SetBitmap(BBitmap* bitmap) 605{ 606 fBitmap = bitmap; 607} 608 609 610// #pragma mark - BBitmapColumn 611 612 613BBitmapColumn::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 621void 622BBitmapColumn::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 664int 665BBitmapColumn::CompareFields(BField* /*field1*/, BField* /*field2*/) 666{ 667 // Comparing bitmaps doesn't really make sense... 668 return 0; 669} 670 671 672bool 673BBitmapColumn::AcceptsField(const BField *field) const 674{ 675 return static_cast<bool>(dynamic_cast<const BBitmapField*>(field)); 676} 677