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