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