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