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 23BTitledColumn::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 35void 36BTitledColumn::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 46void 47BTitledColumn::GetColumnName(BString* into) const 48{ 49 *into = fTitle; 50} 51 52 53void 54BTitledColumn::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 84void 85BTitledColumn::SetTitle(const char* title) 86{ 87 fTitle.SetTo(title); 88} 89 90 91void 92BTitledColumn::Title(BString* forTitle) const 93{ 94 if (forTitle) 95 forTitle->SetTo(fTitle.String()); 96} 97 98 99float 100BTitledColumn::FontHeight() const 101{ 102 return fFontHeight; 103} 104 105 106float 107BTitledColumn::GetPreferredWidth(BField *_field, BView* parent) const 108{ 109 return parent->StringWidth(fTitle.String()) + 2 * kTEXT_MARGIN; 110} 111 112 113// #pragma mark - 114 115 116BStringField::BStringField(const char* string) 117 : 118 fWidth(0), 119 fString(string), 120 fClippedString(string) 121{ 122} 123 124 125void 126BStringField::SetString(const char* val) 127{ 128 fString = val; 129 fClippedString = ""; 130 fWidth = 0; 131} 132 133 134const char* 135BStringField::String() const 136{ 137 return fString.String(); 138} 139 140 141void 142BStringField::SetWidth(float width) 143{ 144 fWidth = width; 145} 146 147 148float 149BStringField::Width() 150{ 151 return fWidth; 152} 153 154 155void 156BStringField::SetClippedString(const char* val) 157{ 158 fClippedString = val; 159} 160 161 162const char* 163BStringField::ClippedString() 164{ 165 return fClippedString.String(); 166} 167 168 169// #pragma mark - 170 171 172BStringColumn::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 180void 181BStringColumn::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 199float 200BStringColumn::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 207int 208BStringColumn::CompareFields(BField* field1, BField* field2) 209{ 210 return ICompare(((BStringField*)field1)->String(), 211 (((BStringField*)field2)->String())); 212} 213 214 215bool 216BStringColumn::AcceptsField(const BField *field) const 217{ 218 return static_cast<bool>(dynamic_cast<const BStringField*>(field)); 219} 220 221 222// #pragma mark - 223 224 225BDateField::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 237void 238BDateField::SetWidth(float width) 239{ 240 fWidth = width; 241} 242 243 244float 245BDateField::Width() 246{ 247 return fWidth; 248} 249 250 251void 252BDateField::SetClippedString(const char* val) 253{ 254 fClippedString = val; 255} 256 257 258const char* 259BDateField::ClippedString() 260{ 261 return fClippedString.String(); 262} 263 264 265time_t 266BDateField::Seconds() 267{ 268 return fSeconds; 269} 270 271 272time_t 273BDateField::UnixTime() 274{ 275 return fUnixTime; 276} 277 278 279// #pragma mark - 280 281 282BDateColumn::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 290const 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 301void 302BDateColumn::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 338int 339BDateColumn::CompareFields(BField* field1, BField* field2) 340{ 341 return((BDateField*)field1)->Seconds() - ((BDateField*)field2)->Seconds(); 342} 343 344 345// #pragma mark - 346 347 348BSizeField::BSizeField(off_t size) 349 : 350 fSize(size) 351{ 352} 353 354 355void 356BSizeField::SetSize(off_t size) 357{ 358 fSize = size; 359} 360 361 362off_t 363BSizeField::Size() 364{ 365 return fSize; 366} 367 368 369// #pragma mark - 370 371 372BSizeColumn::BSizeColumn(const char* title, float width, float minWidth, 373 float maxWidth, alignment align) 374 : BTitledColumn(title, width, minWidth, maxWidth, align) 375{ 376} 377 378 379const int64 kKB_SIZE = 1024; 380const int64 kMB_SIZE = 1048576; 381const int64 kGB_SIZE = 1073741824; 382const int64 kTB_SIZE = kGB_SIZE * kKB_SIZE; 383 384const char *kSIZE_FORMATS[] = { 385 "%.2f %s", 386 "%.1f %s", 387 "%.f %s", 388 "%.f%s", 389 0 390}; 391 392void 393BSizeColumn::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 452int 453BSizeColumn::CompareFields(BField* field1, BField* field2) 454{ 455 return ((BSizeField*)field1)->Size() - ((BSizeField*)field2)->Size(); 456} 457 458 459// #pragma mark - 460 461 462BIntegerField::BIntegerField(int32 number) 463 : 464 fInteger(number) 465{ 466} 467 468 469void 470BIntegerField::SetValue(int32 value) 471{ 472 fInteger = value; 473} 474 475 476int32 477BIntegerField::Value() 478{ 479 return fInteger; 480} 481 482 483// #pragma mark - 484 485 486BIntegerColumn::BIntegerColumn(const char* title, float width, float minWidth, 487 float maxWidth, alignment align) 488 : BTitledColumn(title, width, minWidth, maxWidth, align) 489{ 490} 491 492 493void 494BIntegerColumn::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 508int 509BIntegerColumn::CompareFields(BField *field1, BField *field2) 510{ 511 return (((BIntegerField*)field1)->Value() - ((BIntegerField*)field2)->Value()); 512} 513 514 515// #pragma mark - 516 517 518GraphColumn::GraphColumn(const char* name, float width, float minWidth, 519 float maxWidth, alignment align) 520 : BIntegerColumn(name, width, minWidth, maxWidth, align) 521{ 522} 523 524 525void 526GraphColumn::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 560BBitmapField::BBitmapField(BBitmap *bitmap) 561 : 562 fBitmap(bitmap) 563{ 564} 565 566 567const BBitmap* 568BBitmapField::Bitmap() 569{ 570 return fBitmap; 571} 572 573 574void 575BBitmapField::SetBitmap(BBitmap* bitmap) 576{ 577 fBitmap = bitmap; 578} 579 580 581// #pragma mark - 582 583 584BBitmapColumn::BBitmapColumn(const char* title, float width, float minWidth, 585 float maxWidth, alignment align) 586 : BTitledColumn(title, width, minWidth, maxWidth, align) 587{ 588} 589 590 591void 592BBitmapColumn::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 634int 635BBitmapColumn::CompareFields(BField* /*field1*/, BField* /*field2*/) 636{ 637 // Comparing bitmaps doesn't really make sense... 638 return 0; 639} 640 641 642bool 643BBitmapColumn::AcceptsField(const BField *field) const 644{ 645 return static_cast<bool>(dynamic_cast<const BBitmapField*>(field)); 646} 647 648 649