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