1 /* 2 Open Tracker License 3 4 Terms and Conditions 5 6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved. 7 8 Permission is hereby granted, free of charge, to any person obtaining a copy of 9 this software and associated documentation files (the "Software"), to deal in 10 the Software without restriction, including without limitation the rights to 11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 12 of the Software, and to permit persons to whom the Software is furnished to do 13 so, subject to the following conditions: 14 15 The above copyright notice and this permission notice applies to all licensees 16 and shall be included in all copies or substantial portions of the Software. 17 18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY, 20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION 23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 25 Except as contained in this notice, the name of Be Incorporated shall not be 26 used in advertising or otherwise to promote the sale, use or other dealings in 27 this Software without prior written authorization from Be Incorporated. 28 29 Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks 30 of Be Incorporated in the United States and other countries. Other brand product 31 names are registered trademarks or trademarks of their respective holders. 32 All rights reserved. 33 */ 34 35 36 #include "WidgetAttributeText.h" 37 38 #include <ctype.h> 39 #include <stdlib.h> 40 #include <strings.h> 41 42 #include <fs_attr.h> 43 #include <parsedate.h> 44 45 #include <Alert.h> 46 #include <AppFileInfo.h> 47 #include <Catalog.h> 48 #include <DateFormat.h> 49 #include <DateTimeFormat.h> 50 #include <Debug.h> 51 #include <Locale.h> 52 #include <NodeInfo.h> 53 #include <Path.h> 54 #include <StringFormat.h> 55 #include <SupportDefs.h> 56 #include <TextView.h> 57 #include <Volume.h> 58 #include <VolumeRoster.h> 59 60 #include "Attributes.h" 61 #include "FSUtils.h" 62 #include "Model.h" 63 #include "OpenWithWindow.h" 64 #include "MimeTypes.h" 65 #include "PoseView.h" 66 #include "SettingsViews.h" 67 #include "Utilities.h" 68 #include "ViewState.h" 69 70 71 #undef B_TRANSLATION_CONTEXT 72 #define B_TRANSLATION_CONTEXT "WidgetAttributeText" 73 74 75 const int32 kGenericReadBufferSize = 1024; 76 77 const char* kSizeFormats[] = { 78 "%.2f %s", 79 "%.1f %s", 80 "%.f %s", 81 "%.f%s", 82 0 83 }; 84 85 86 bool NameAttributeText::sSortFolderNamesFirst = false; 87 bool RealNameAttributeText::sSortFolderNamesFirst = false; 88 89 90 template <class View> 91 float 92 TruncFileSizeBase(BString* outString, int64 value, const View* view, 93 float width) 94 { 95 // ToDo: If slow, replace float divisions with shifts 96 // if fast enough, try fitting more decimal places. 97 98 // ToDo: Update string_for_size() in libshared to be able to 99 // handle this case. 100 101 BString buffer; 102 103 // format file size value 104 if (value == kUnknownSize) { 105 *outString = "-"; 106 return view->StringWidth("-"); 107 } else if (value < kKBSize) { 108 static BStringFormat format(B_TRANSLATE( 109 "{0, plural, one{# byte} other{# bytes}}")); 110 format.Format(buffer, value); 111 if (view->StringWidth(buffer.String()) > width) 112 buffer.SetToFormat(B_TRANSLATE("%Ld B"), value); 113 } else { 114 const char* suffix; 115 float doubleValue; 116 if (value >= kTBSize) { 117 suffix = B_TRANSLATE("TiB"); 118 doubleValue = (double)value / kTBSize; 119 } else if (value >= kGBSize) { 120 suffix = B_TRANSLATE("GiB"); 121 doubleValue = (double)value / kGBSize; 122 } else if (value >= kMBSize) { 123 suffix = B_TRANSLATE("MiB"); 124 doubleValue = (double)value / kMBSize; 125 } else { 126 ASSERT(value >= kKBSize); 127 suffix = B_TRANSLATE("KiB"); 128 doubleValue = (double)value / kKBSize; 129 } 130 131 for (int32 index = 0; ; index++) { 132 if (kSizeFormats[index] == 0) 133 break; 134 135 buffer.SetToFormat(kSizeFormats[index], doubleValue, suffix); 136 // strip off an insignificant zero so we don't get readings 137 // such as 1.00 138 char* period = 0; 139 for (char* tmp = const_cast<char*>(buffer.String()); *tmp != '\0'; 140 tmp++) { 141 if (*tmp == '.') 142 period = tmp; 143 } 144 if (period && period[1] && period[2] == '0') 145 // move the rest of the string over the insignificant zero 146 for (char* tmp = &period[2]; *tmp; tmp++) 147 *tmp = tmp[1]; 148 149 float resultWidth = view->StringWidth(buffer); 150 if (resultWidth <= width) { 151 *outString = buffer.String(); 152 return resultWidth; 153 } 154 } 155 } 156 157 return TruncStringBase(outString, buffer.String(), buffer.Length(), view, 158 width, (uint32)B_TRUNCATE_END); 159 } 160 161 162 template <class View> 163 float 164 TruncStringBase(BString* outString, const char* inString, int32 length, 165 const View* view, float width, uint32 truncMode = B_TRUNCATE_MIDDLE) 166 { 167 // we are using a template version of this call to make sure 168 // the right StringWidth gets picked up for BView x BPoseView 169 // for max speed and flexibility 170 171 // a standard ellipsis inserting fitting algorithm 172 if (view->StringWidth(inString, length) <= width) 173 *outString = inString; 174 else { 175 const char* source[1]; 176 char* results[1]; 177 178 source[0] = inString; 179 results[0] = outString->LockBuffer(length + 3); 180 181 BFont font; 182 view->GetFont(&font); 183 184 font.GetTruncatedStrings(source, 1, truncMode, width, results); 185 outString->UnlockBuffer(); 186 } 187 188 return view->StringWidth(outString->String(), outString->Length()); 189 } 190 191 192 template <class View> 193 float 194 TruncTimeBase(BString* outString, int64 value, const View* view, float width) 195 { 196 float resultWidth = width + 1; 197 198 time_t timeValue = (time_t)value; 199 200 // Find the longest possible format that will fit the available space 201 struct { 202 BDateFormatStyle dateStyle; 203 BTimeFormatStyle timeStyle; 204 } formats[] = { 205 { B_LONG_DATE_FORMAT, B_MEDIUM_TIME_FORMAT }, 206 { B_LONG_DATE_FORMAT, B_SHORT_TIME_FORMAT }, 207 { B_MEDIUM_DATE_FORMAT, B_SHORT_TIME_FORMAT }, 208 { B_SHORT_DATE_FORMAT, B_SHORT_TIME_FORMAT }, 209 }; 210 211 BString date; 212 BDateTimeFormat formatter; 213 for (unsigned int i = 0; i < B_COUNT_OF(formats); ++i) { 214 if (formatter.Format(date, timeValue, formats[i].dateStyle, 215 formats[i].timeStyle) == B_OK) { 216 resultWidth = view->StringWidth(date.String(), date.Length()); 217 if (resultWidth <= width) { 218 // Found a format that fits the available space, stop searching 219 break; 220 } 221 } 222 } 223 224 // If we couldn't fit the date, try with just the time 225 // TODO we could use only the time for "today" dates 226 if (resultWidth > width 227 && BDateFormat().Format(date, timeValue, 228 B_SHORT_DATE_FORMAT) == B_OK) { 229 resultWidth = view->StringWidth(date.String(), date.Length()); 230 } 231 232 if (resultWidth > width) { 233 // even the shortest format string didn't do it, insert ellipsis 234 resultWidth = TruncStringBase(outString, date.String(), 235 (ssize_t)date.Length(), view, width); 236 } else 237 *outString = date; 238 239 return resultWidth; 240 } 241 242 243 // #pragma mark - WidgetAttributeText base class 244 245 246 WidgetAttributeText* 247 WidgetAttributeText::NewWidgetText(const Model* model, 248 const BColumn* column, const BPoseView* view) 249 { 250 // call this to make the right WidgetAttributeText type for a 251 // given column 252 253 const char* attrName = column->AttrName(); 254 255 if (strcmp(attrName, kAttrPath) == 0) 256 return new PathAttributeText(model, column); 257 258 if (strcmp(attrName, kAttrMIMEType) == 0) 259 return new KindAttributeText(model, column); 260 261 if (strcmp(attrName, kAttrStatName) == 0) 262 return new NameAttributeText(model, column); 263 264 if (strcmp(attrName, kAttrRealName) == 0) 265 return new RealNameAttributeText(model, column); 266 267 if (strcmp(attrName, kAttrStatSize) == 0) 268 return new SizeAttributeText(model, column); 269 270 if (strcmp(attrName, kAttrStatModified) == 0) 271 return new ModificationTimeAttributeText(model, column); 272 273 if (strcmp(attrName, kAttrStatCreated) == 0) 274 return new CreationTimeAttributeText(model, column); 275 276 #ifdef OWNER_GROUP_ATTRIBUTES 277 if (strcmp(attrName, kAttrStatOwner) == 0) 278 return new OwnerAttributeText(model, column); 279 280 if (strcmp(attrName, kAttrStatGroup) == 0) 281 return new GroupAttributeText(model, column); 282 #endif 283 if (strcmp(attrName, kAttrStatMode) == 0) 284 return new ModeAttributeText(model, column); 285 286 if (strcmp(attrName, kAttrOpenWithRelation) == 0) 287 return new OpenWithRelationAttributeText(model, column, view); 288 289 if (strcmp(attrName, kAttrAppVersion) == 0) 290 return new AppShortVersionAttributeText(model, column); 291 292 if (strcmp(attrName, kAttrSystemVersion) == 0) 293 return new SystemShortVersionAttributeText(model, column); 294 295 if (strcmp(attrName, kAttrOriginalPath) == 0) 296 return new OriginalPathAttributeText(model, column); 297 298 if (column->DisplayAs() != NULL) { 299 if (!strncmp(column->DisplayAs(), "checkbox", 8)) 300 return new CheckboxAttributeText(model, column); 301 302 if (!strncmp(column->DisplayAs(), "duration", 8)) 303 return new DurationAttributeText(model, column); 304 305 if (!strncmp(column->DisplayAs(), "rating", 6)) 306 return new RatingAttributeText(model, column); 307 } 308 309 return new GenericAttributeText(model, column); 310 } 311 312 313 WidgetAttributeText::WidgetAttributeText(const Model* model, 314 const BColumn* column) 315 : 316 fModel(const_cast<Model*>(model)), 317 fColumn(column), 318 fOldWidth(-1.0f), 319 fTruncatedWidth(-1.0f), 320 fDirty(true), 321 fValueIsDefined(false) 322 { 323 ASSERT(fColumn != NULL); 324 325 if (fColumn == NULL) 326 return; 327 328 ASSERT(fColumn->Width() > 0); 329 } 330 331 332 WidgetAttributeText::~WidgetAttributeText() 333 { 334 } 335 336 337 const char* 338 WidgetAttributeText::FittingText(const BPoseView* view) 339 { 340 if (fDirty || fColumn->Width() != fOldWidth || CheckSettingsChanged() 341 || !fValueIsDefined) { 342 CheckViewChanged(view); 343 } 344 345 ASSERT(!fDirty); 346 return fText.String(); 347 } 348 349 350 bool 351 WidgetAttributeText::CheckViewChanged(const BPoseView* view) 352 { 353 BString newText; 354 FitValue(&newText, view); 355 if (newText == fText) 356 return false; 357 358 fText = newText; 359 return true; 360 } 361 362 363 bool 364 WidgetAttributeText::CheckSettingsChanged() 365 { 366 return false; 367 } 368 369 370 float 371 WidgetAttributeText::TruncString(BString* outString, const char* inString, 372 int32 length, const BPoseView* view, float width, uint32 truncMode) 373 { 374 return TruncStringBase(outString, inString, length, view, width, truncMode); 375 } 376 377 378 float 379 WidgetAttributeText::TruncFileSize(BString* outString, int64 value, 380 const BPoseView* view, float width) 381 { 382 return TruncFileSizeBase(outString, value, view, width); 383 } 384 385 386 float 387 WidgetAttributeText::TruncTime(BString* outString, int64 value, 388 const BPoseView* view, float width) 389 { 390 return TruncTimeBase(outString, value, view, width); 391 } 392 393 394 float 395 WidgetAttributeText::CurrentWidth() const 396 { 397 return fTruncatedWidth; 398 } 399 400 401 float 402 WidgetAttributeText::Width(const BPoseView* pose) 403 { 404 FittingText(pose); 405 return CurrentWidth(); 406 } 407 408 409 void 410 WidgetAttributeText::SetUpEditing(BTextView*) 411 { 412 ASSERT(fColumn->Editable()); 413 } 414 415 416 bool 417 WidgetAttributeText::CommitEditedText(BTextView*) 418 { 419 // can't do anything here at this point 420 TRESPASS(); 421 return false; 422 } 423 424 425 status_t 426 WidgetAttributeText::AttrAsString(const Model* model, BString* outString, 427 const char* attrName, int32 attrType, float width, BView* view, 428 int64* resultingValue) 429 { 430 int64 value; 431 432 status_t error = model->InitCheck(); 433 if (error != B_OK) 434 return error; 435 436 switch (attrType) { 437 case B_TIME_TYPE: 438 if (strcmp(attrName, kAttrStatModified) == 0) 439 value = model->StatBuf()->st_mtime; 440 else if (strcmp(attrName, kAttrStatCreated) == 0) 441 value = model->StatBuf()->st_crtime; 442 else { 443 TRESPASS(); 444 // not yet supported 445 return B_ERROR; 446 } 447 TruncTimeBase(outString, value, view, width); 448 if (resultingValue) 449 *resultingValue = value; 450 451 return B_OK; 452 453 case B_STRING_TYPE: 454 if (strcmp(attrName, kAttrPath) == 0) { 455 BEntry entry(model->EntryRef()); 456 BPath path; 457 BString tmp; 458 459 if (entry.InitCheck() == B_OK 460 && entry.GetPath(&path) == B_OK) { 461 tmp = path.Path(); 462 TruncateLeaf(&tmp); 463 } else 464 tmp = "-"; 465 466 if (width > 0) { 467 TruncStringBase(outString, tmp.String(), tmp.Length(), view, 468 width); 469 } else 470 *outString = tmp.String(); 471 472 return B_OK; 473 } 474 break; 475 476 case kSizeType: 477 // TruncFileSizeBase(outString, model->StatBuf()->st_size, view, 478 // width); 479 return B_OK; 480 break; 481 482 default: 483 TRESPASS(); 484 // not yet supported 485 return B_ERROR; 486 487 } 488 489 TRESPASS(); 490 return B_ERROR; 491 } 492 493 494 bool 495 WidgetAttributeText::IsEditable() const 496 { 497 return fColumn->Editable(); 498 } 499 500 501 void 502 WidgetAttributeText::SetDirty(bool value) 503 { 504 fDirty = value; 505 } 506 507 508 // #pragma mark - StringAttributeText 509 510 511 StringAttributeText::StringAttributeText(const Model* model, 512 const BColumn* column) 513 : 514 WidgetAttributeText(model, column), 515 fValueDirty(true) 516 { 517 } 518 519 520 const char* 521 StringAttributeText::ValueAsText(const BPoseView* /*view*/) 522 { 523 if (fValueDirty) 524 ReadValue(&fFullValueText); 525 526 return fFullValueText.String(); 527 } 528 529 530 bool 531 StringAttributeText::CheckAttributeChanged() 532 { 533 BString newString; 534 ReadValue(&newString); 535 536 if (newString == fFullValueText) 537 return false; 538 539 fFullValueText = newString; 540 fDirty = true; // have to redo fitted string 541 return true; 542 } 543 544 545 void 546 StringAttributeText::FitValue(BString* outString, const BPoseView* view) 547 { 548 if (fValueDirty) 549 ReadValue(&fFullValueText); 550 fOldWidth = fColumn->Width(); 551 552 fTruncatedWidth = TruncString(outString, fFullValueText.String(), 553 fFullValueText.Length(), view, fOldWidth); 554 fDirty = false; 555 } 556 557 558 float 559 StringAttributeText::PreferredWidth(const BPoseView* pose) const 560 { 561 return pose->StringWidth(fFullValueText.String()); 562 } 563 564 565 int 566 StringAttributeText::Compare(WidgetAttributeText& attr, BPoseView* view) 567 { 568 StringAttributeText* compareTo = dynamic_cast<StringAttributeText*>(&attr); 569 ThrowOnAssert(compareTo != NULL); 570 571 if (fValueDirty) 572 ReadValue(&fFullValueText); 573 574 return NaturalCompare(fFullValueText.String(), 575 compareTo->ValueAsText(view)); 576 } 577 578 579 bool 580 StringAttributeText::CommitEditedText(BTextView* textView) 581 { 582 ASSERT(fColumn->Editable()); 583 const char* text = textView->Text(); 584 585 if (fFullValueText == text) { 586 // no change 587 return false; 588 } 589 590 if (textView->TextLength() == 0) { 591 // cannot do an empty name 592 return false; 593 } 594 595 // cause re-truncation 596 fDirty = true; 597 598 if (!CommitEditedTextFlavor(textView)) 599 return false; 600 601 // update text and width in this widget 602 fFullValueText = text; 603 604 return true; 605 } 606 607 608 // #pragma mark - ScalarAttributeText 609 610 611 ScalarAttributeText::ScalarAttributeText(const Model* model, 612 const BColumn* column) 613 : 614 WidgetAttributeText(model, column), 615 fValue(0), 616 fValueDirty(true) 617 { 618 } 619 620 621 int64 622 ScalarAttributeText::Value() 623 { 624 if (fValueDirty) 625 fValue = ReadValue(); 626 627 return fValue; 628 } 629 630 631 bool 632 ScalarAttributeText::CheckAttributeChanged() 633 { 634 int64 newValue = ReadValue(); 635 if (newValue == fValue) 636 return false; 637 638 fValue = newValue; 639 fDirty = true; 640 // have to redo fitted string 641 642 return true; 643 } 644 645 646 float 647 ScalarAttributeText::PreferredWidth(const BPoseView* pose) const 648 { 649 BString widthString; 650 widthString << fValue; 651 return pose->StringWidth(widthString.String()); 652 } 653 654 655 int 656 ScalarAttributeText::Compare(WidgetAttributeText& attr, BPoseView*) 657 { 658 ScalarAttributeText* compareTo = dynamic_cast<ScalarAttributeText*>(&attr); 659 ThrowOnAssert(compareTo != NULL); 660 661 if (fValueDirty) 662 fValue = ReadValue(); 663 664 return fValue >= compareTo->Value() 665 ? (fValue == compareTo->Value() ? 0 : 1) : -1; 666 } 667 668 669 // #pragma mark - PathAttributeText 670 671 672 PathAttributeText::PathAttributeText(const Model* model, const BColumn* column) 673 : 674 StringAttributeText(model, column) 675 { 676 } 677 678 679 void 680 PathAttributeText::ReadValue(BString* outString) 681 { 682 // get the path 683 BEntry entry(fModel->EntryRef()); 684 BPath path; 685 686 if (entry.InitCheck() == B_OK && entry.GetPath(&path) == B_OK) { 687 *outString = path.Path(); 688 TruncateLeaf(outString); 689 } else 690 *outString = "-"; 691 692 fValueDirty = false; 693 } 694 695 696 // #pragma mark - OriginalPathAttributeText 697 698 699 OriginalPathAttributeText::OriginalPathAttributeText(const Model* model, 700 const BColumn* column) 701 : 702 StringAttributeText(model, column) 703 { 704 } 705 706 707 void 708 OriginalPathAttributeText::ReadValue(BString* outString) 709 { 710 BEntry entry(fModel->EntryRef()); 711 BPath path; 712 713 // get the original path 714 if (entry.InitCheck() == B_OK && FSGetOriginalPath(&entry, &path) == B_OK) 715 *outString = path.Path(); 716 else 717 *outString = "-"; 718 719 fValueDirty = false; 720 } 721 722 723 // #pragma mark - KindAttributeText 724 725 726 KindAttributeText::KindAttributeText(const Model* model, const BColumn* column) 727 : 728 StringAttributeText(model, column) 729 { 730 } 731 732 733 void 734 KindAttributeText::ReadValue(BString* outString) 735 { 736 BMimeType mime; 737 char desc[B_MIME_TYPE_LENGTH]; 738 739 // get the mime type 740 if (mime.SetType(fModel->MimeType()) != B_OK) 741 *outString = B_TRANSLATE("Unknown"); 742 else if (mime.GetShortDescription(desc) == B_OK) { 743 // get the short mime type description 744 *outString = desc; 745 } else 746 *outString = fModel->MimeType(); 747 748 fValueDirty = false; 749 } 750 751 752 // #pragma mark - NameAttributeText 753 754 755 NameAttributeText::NameAttributeText(const Model* model, 756 const BColumn* column) 757 : 758 StringAttributeText(model, column) 759 { 760 } 761 762 763 int 764 NameAttributeText::Compare(WidgetAttributeText& attr, BPoseView* view) 765 { 766 NameAttributeText* compareTo = dynamic_cast<NameAttributeText*>(&attr); 767 ThrowOnAssert(compareTo != NULL); 768 769 if (fValueDirty) 770 ReadValue(&fFullValueText); 771 772 if (NameAttributeText::sSortFolderNamesFirst) 773 return fModel->CompareFolderNamesFirst(attr.TargetModel()); 774 775 return NaturalCompare(fFullValueText.String(), 776 compareTo->ValueAsText(view)); 777 } 778 779 780 void 781 NameAttributeText::ReadValue(BString* outString) 782 { 783 *outString = fModel->Name(); 784 785 fValueDirty = false; 786 } 787 788 789 void 790 NameAttributeText::FitValue(BString* outString, const BPoseView* view) 791 { 792 if (fValueDirty) 793 ReadValue(&fFullValueText); 794 795 fOldWidth = fColumn->Width(); 796 fTruncatedWidth = TruncString(outString, fFullValueText.String(), 797 fFullValueText.Length(), view, fOldWidth, B_TRUNCATE_MIDDLE); 798 fDirty = false; 799 } 800 801 802 void 803 NameAttributeText::SetUpEditing(BTextView* textView) 804 { 805 DisallowFilenameKeys(textView); 806 807 textView->SetMaxBytes(B_FILE_NAME_LENGTH); 808 textView->SetText(fFullValueText.String(), fFullValueText.Length()); 809 } 810 811 812 bool 813 NameAttributeText::CommitEditedTextFlavor(BTextView* textView) 814 { 815 if (textView == NULL) 816 return false; 817 818 const char* name = textView->Text(); 819 size_t length = (size_t)textView->TextLength(); 820 821 BEntry entry(fModel->EntryRef()); 822 status_t result = entry.InitCheck(); 823 if (result == B_OK) 824 result = EditModelName(fModel, name, length); 825 826 return result == B_OK; 827 } 828 829 830 void 831 NameAttributeText::SetSortFolderNamesFirst(bool enabled) 832 { 833 NameAttributeText::sSortFolderNamesFirst = enabled; 834 } 835 836 837 bool 838 NameAttributeText::IsEditable() const 839 { 840 return StringAttributeText::IsEditable(); 841 } 842 843 844 // #pragma mark - RealNameAttributeText 845 846 847 RealNameAttributeText::RealNameAttributeText(const Model* model, 848 const BColumn* column) 849 : 850 NameAttributeText(model, column) 851 { 852 } 853 854 855 int 856 RealNameAttributeText::Compare(WidgetAttributeText& attr, BPoseView* view) 857 { 858 RealNameAttributeText* compareTo 859 = dynamic_cast<RealNameAttributeText*>(&attr); 860 ThrowOnAssert(compareTo != NULL); 861 862 if (fValueDirty) 863 ReadValue(&fFullValueText); 864 865 if (RealNameAttributeText::sSortFolderNamesFirst) 866 return fModel->CompareFolderNamesFirst(attr.TargetModel()); 867 868 return NaturalCompare(fFullValueText.String(), 869 compareTo->ValueAsText(view)); 870 } 871 872 873 void 874 RealNameAttributeText::ReadValue(BString* outString) 875 { 876 *outString = fModel->EntryRef()->name; 877 878 fValueDirty = false; 879 } 880 881 882 void 883 RealNameAttributeText::FitValue(BString* outString, const BPoseView* view) 884 { 885 if (fValueDirty) 886 ReadValue(&fFullValueText); 887 888 fOldWidth = fColumn->Width(); 889 fTruncatedWidth = TruncString(outString, fFullValueText.String(), 890 fFullValueText.Length(), view, fOldWidth, B_TRUNCATE_MIDDLE); 891 fDirty = false; 892 } 893 894 895 void 896 RealNameAttributeText::SetUpEditing(BTextView* textView) 897 { 898 DisallowFilenameKeys(textView); 899 900 textView->SetMaxBytes(B_FILE_NAME_LENGTH); 901 textView->SetText(fFullValueText.String(), fFullValueText.Length()); 902 } 903 904 905 void 906 RealNameAttributeText::SetSortFolderNamesFirst(bool enabled) 907 { 908 RealNameAttributeText::sSortFolderNamesFirst = enabled; 909 } 910 911 912 // #pragma mark - owner/group 913 914 915 #ifdef OWNER_GROUP_ATTRIBUTES 916 OwnerAttributeText::OwnerAttributeText(const Model* model, 917 const BColumn* column) 918 : 919 StringAttributeText(model, column) 920 { 921 } 922 923 924 void 925 OwnerAttributeText::ReadValue(BString* outString) 926 { 927 uid_t nodeOwner = fModel->StatBuf()->st_uid; 928 BString user; 929 930 if (nodeOwner == 0) { 931 if (getenv("USER") != NULL) 932 user << getenv("USER"); 933 else 934 user << "root"; 935 } else 936 user << nodeOwner; 937 *outString = user.String(); 938 939 fValueDirty = false; 940 } 941 942 943 GroupAttributeText::GroupAttributeText(const Model* model, 944 const BColumn* column) 945 : 946 StringAttributeText(model, column) 947 { 948 } 949 950 951 void 952 GroupAttributeText::ReadValue(BString* outString) 953 { 954 gid_t nodeGroup = fModel->StatBuf()->st_gid; 955 BString group; 956 957 if (nodeGroup == 0) { 958 if (getenv("GROUP") != NULL) 959 group << getenv("GROUP"); 960 else 961 group << "0"; 962 } else 963 group << nodeGroup; 964 *outString = group.String(); 965 966 fValueDirty = false; 967 } 968 #endif // OWNER_GROUP_ATTRIBUTES 969 970 971 // #pragma mark - ModeAttributeText 972 973 974 ModeAttributeText::ModeAttributeText(const Model* model, 975 const BColumn* column) 976 : 977 StringAttributeText(model, column) 978 { 979 } 980 981 982 void 983 ModeAttributeText::ReadValue(BString* outString) 984 { 985 mode_t mode = fModel->StatBuf()->st_mode; 986 mode_t baseMask = 00400; 987 char buffer[11]; 988 989 char* scanner = buffer; 990 991 if (S_ISDIR(mode)) 992 *scanner++ = 'd'; 993 else if (S_ISLNK(mode)) 994 *scanner++ = 'l'; 995 else if (S_ISBLK(mode)) 996 *scanner++ = 'b'; 997 else if (S_ISCHR(mode)) 998 *scanner++ = 'c'; 999 else 1000 *scanner++ = '-'; 1001 1002 for (int32 index = 0; index < 9; index++) { 1003 *scanner++ = (mode & baseMask) ? "rwx"[index % 3] : '-'; 1004 baseMask >>= 1; 1005 } 1006 1007 *scanner = 0; 1008 *outString = buffer; 1009 1010 fValueDirty = false; 1011 } 1012 1013 1014 // #pragma mark - SizeAttributeText 1015 1016 1017 SizeAttributeText::SizeAttributeText(const Model* model, 1018 const BColumn* column) 1019 : 1020 ScalarAttributeText(model, column) 1021 { 1022 } 1023 1024 1025 int64 1026 SizeAttributeText::ReadValue() 1027 { 1028 fValueDirty = false; 1029 // get the size 1030 1031 if (fModel->IsVolume()) { 1032 BVolume volume(fModel->NodeRef()->device); 1033 1034 return volume.Capacity(); 1035 } 1036 1037 if (fModel->IsDirectory() || fModel->IsQuery() 1038 || fModel->IsQueryTemplate() || fModel->IsSymLink() 1039 || fModel->IsVirtualDirectory()) { 1040 return kUnknownSize; 1041 } 1042 1043 fValueIsDefined = true; 1044 1045 return fModel->StatBuf()->st_size; 1046 } 1047 1048 1049 void 1050 SizeAttributeText::FitValue(BString* outString, const BPoseView* view) 1051 { 1052 if (fValueDirty) 1053 fValue = ReadValue(); 1054 1055 fOldWidth = fColumn->Width(); 1056 fTruncatedWidth = TruncFileSize(outString, fValue, view, fOldWidth); 1057 fDirty = false; 1058 } 1059 1060 1061 float 1062 SizeAttributeText::PreferredWidth(const BPoseView* pose) const 1063 { 1064 if (fValueIsDefined) { 1065 BString widthString; 1066 TruncFileSize(&widthString, fValue, pose, 100000); 1067 return pose->StringWidth(widthString.String()); 1068 } 1069 1070 return pose->StringWidth("-"); 1071 } 1072 1073 1074 // #pragma mark - TimeAttributeText 1075 1076 1077 TimeAttributeText::TimeAttributeText(const Model* model, 1078 const BColumn* column) 1079 : 1080 ScalarAttributeText(model, column), 1081 fLastClockIs24(false), 1082 fLastDateOrder(kDateFormatEnd), 1083 fLastTimeFormatSeparator(kSeparatorsEnd) 1084 { 1085 } 1086 1087 1088 float 1089 TimeAttributeText::PreferredWidth(const BPoseView* pose) const 1090 { 1091 BString widthString; 1092 TruncTimeBase(&widthString, fValue, pose, 100000); 1093 return pose->StringWidth(widthString.String()); 1094 } 1095 1096 1097 void 1098 TimeAttributeText::FitValue(BString* outString, const BPoseView* view) 1099 { 1100 if (fValueDirty) 1101 fValue = ReadValue(); 1102 1103 fOldWidth = fColumn->Width(); 1104 fTruncatedWidth = TruncTime(outString, fValue, view, fOldWidth); 1105 fDirty = false; 1106 } 1107 1108 1109 bool 1110 TimeAttributeText::CheckSettingsChanged(void) 1111 { 1112 // TODO : check against the actual locale settings 1113 return false; 1114 } 1115 1116 1117 // #pragma mark - CreationTimeAttributeText 1118 1119 1120 CreationTimeAttributeText::CreationTimeAttributeText(const Model* model, 1121 const BColumn* column) 1122 : 1123 TimeAttributeText(model, column) 1124 { 1125 } 1126 1127 1128 int64 1129 CreationTimeAttributeText::ReadValue() 1130 { 1131 fValueDirty = false; 1132 fValueIsDefined = true; 1133 return fModel->StatBuf()->st_crtime; 1134 } 1135 1136 1137 // #pragma mark - ModificationTimeAttributeText 1138 1139 1140 ModificationTimeAttributeText::ModificationTimeAttributeText( 1141 const Model* model, const BColumn* column) 1142 : 1143 TimeAttributeText(model, column) 1144 { 1145 } 1146 1147 1148 int64 1149 ModificationTimeAttributeText::ReadValue() 1150 { 1151 fValueDirty = false; 1152 fValueIsDefined = true; 1153 return fModel->StatBuf()->st_mtime; 1154 } 1155 1156 1157 // #pragma mark - GenericAttributeText 1158 1159 1160 GenericAttributeText::GenericAttributeText(const Model* model, 1161 const BColumn* column) 1162 : 1163 StringAttributeText(model, column) 1164 { 1165 } 1166 1167 1168 bool 1169 GenericAttributeText::CheckAttributeChanged() 1170 { 1171 GenericValueStruct tmpValue = fValue; 1172 BString tmpString(fFullValueText); 1173 ReadValue(&fFullValueText); 1174 1175 // fDirty could already be true, in that case we mustn't set it to 1176 // false, even if the attribute text hasn't changed 1177 bool changed = fValue.int64t != tmpValue.int64t 1178 || tmpString != fFullValueText; 1179 if (changed) 1180 fDirty = true; 1181 1182 return fDirty; 1183 } 1184 1185 1186 float 1187 GenericAttributeText::PreferredWidth(const BPoseView* pose) const 1188 { 1189 return pose->StringWidth(fFullValueText.String()); 1190 } 1191 1192 1193 void 1194 GenericAttributeText::ReadValue(BString* outString) 1195 { 1196 BModelOpener opener(const_cast<Model*>(fModel)); 1197 1198 ssize_t length = 0; 1199 fFullValueText = "-"; 1200 fValue.int64t = 0; 1201 fValueIsDefined = false; 1202 fValueDirty = false; 1203 1204 if (!fModel->Node()) 1205 return; 1206 1207 switch (fColumn->AttrType()) { 1208 case B_STRING_TYPE: 1209 { 1210 char buffer[kGenericReadBufferSize]; 1211 length = fModel->Node()->ReadAttr(fColumn->AttrName(), 1212 fColumn->AttrType(), 0, buffer, kGenericReadBufferSize - 1); 1213 1214 if (length > 0) { 1215 buffer[length] = '\0'; 1216 // make sure the buffer is null-terminated even if we 1217 // didn't read the whole attribute in or it wasn't to 1218 // begin with 1219 1220 *outString = buffer; 1221 fValueIsDefined = true; 1222 } 1223 break; 1224 } 1225 1226 case B_SSIZE_T_TYPE: 1227 case B_TIME_TYPE: 1228 case B_OFF_T_TYPE: 1229 case B_FLOAT_TYPE: 1230 case B_BOOL_TYPE: 1231 case B_CHAR_TYPE: 1232 case B_INT8_TYPE: 1233 case B_INT16_TYPE: 1234 case B_INT32_TYPE: 1235 case B_INT64_TYPE: 1236 case B_UINT8_TYPE: 1237 case B_UINT16_TYPE: 1238 case B_UINT32_TYPE: 1239 case B_UINT64_TYPE: 1240 case B_DOUBLE_TYPE: 1241 { 1242 // read in the numerical bit representation and attach it 1243 // with a type, depending on the bytes that could be read 1244 attr_info info; 1245 GenericValueStruct tmp; 1246 if (fModel->Node()->GetAttrInfo(fColumn->AttrName(), &info) 1247 == B_OK) { 1248 if (info.size && info.size <= (off_t)sizeof(int64)) { 1249 length = fModel->Node()->ReadAttr(fColumn->AttrName(), 1250 fColumn->AttrType(), 0, &tmp, (size_t)info.size); 1251 } 1252 1253 // We used tmp as a block of memory, now set the 1254 // correct fValue: 1255 1256 if (length == info.size) { 1257 if (fColumn->AttrType() == B_FLOAT_TYPE 1258 || fColumn->AttrType() == B_DOUBLE_TYPE) { 1259 // filter out special float/double types 1260 switch (info.size) { 1261 case sizeof(float): 1262 fValueIsDefined = true; 1263 fValue.floatt = tmp.floatt; 1264 break; 1265 1266 case sizeof(double): 1267 fValueIsDefined = true; 1268 fValue.doublet = tmp.doublet; 1269 break; 1270 1271 default: 1272 TRESPASS(); 1273 break; 1274 } 1275 } else { 1276 // handle the standard data types 1277 switch (info.size) { 1278 case sizeof(char): 1279 // Takes care of bool too. 1280 fValueIsDefined = true; 1281 fValue.int8t = tmp.int8t; 1282 break; 1283 1284 case sizeof(int16): 1285 fValueIsDefined = true; 1286 fValue.int16t = tmp.int16t; 1287 break; 1288 1289 case sizeof(int32): 1290 // Takes care of time_t too. 1291 fValueIsDefined = true; 1292 fValue.int32t = tmp.int32t; 1293 break; 1294 1295 case sizeof(int64): 1296 // Takes care of off_t too. 1297 fValueIsDefined = true; 1298 fValue.int64t = tmp.int64t; 1299 break; 1300 1301 default: 1302 TRESPASS(); 1303 break; 1304 } 1305 } 1306 } 1307 } 1308 break; 1309 } 1310 } 1311 } 1312 1313 1314 void 1315 GenericAttributeText::FitValue(BString* outString, const BPoseView* view) 1316 { 1317 if (fValueDirty) 1318 ReadValue(&fFullValueText); 1319 1320 fOldWidth = fColumn->Width(); 1321 1322 if (!fValueIsDefined) { 1323 *outString = "-"; 1324 fTruncatedWidth = TruncString(outString, fFullValueText.String(), 1325 fFullValueText.Length(), view, fOldWidth); 1326 fDirty = false; 1327 return; 1328 } 1329 1330 char buffer[256]; 1331 1332 switch (fColumn->AttrType()) { 1333 case B_SIZE_T_TYPE: 1334 TruncFileSizeBase(outString, fValue.int32t, view, fOldWidth); 1335 return; 1336 1337 case B_SSIZE_T_TYPE: 1338 if (fValue.int32t > 0) { 1339 TruncFileSizeBase(outString, fValue.int32t, view, fOldWidth); 1340 return; 1341 } 1342 sprintf(buffer, "%s", strerror(fValue.int32t)); 1343 fFullValueText = buffer; 1344 break; 1345 1346 case B_STRING_TYPE: 1347 fTruncatedWidth = TruncString(outString, fFullValueText.String(), 1348 fFullValueText.Length(), view, fOldWidth); 1349 fDirty = false; 1350 return; 1351 1352 case B_OFF_T_TYPE: 1353 // As a side effect update the fFullValueText to the string 1354 // representation of value 1355 TruncFileSize(&fFullValueText, fValue.off_tt, view, 100000); 1356 fTruncatedWidth = TruncFileSize(outString, fValue.off_tt, view, 1357 fOldWidth); 1358 fDirty = false; 1359 return; 1360 1361 case B_TIME_TYPE: 1362 // As a side effect update the fFullValueText to the string 1363 // representation of value 1364 TruncTime(&fFullValueText, fValue.time_tt, view, 100000); 1365 fTruncatedWidth = TruncTime(outString, fValue.time_tt, view, 1366 fOldWidth); 1367 fDirty = false; 1368 return; 1369 1370 case B_BOOL_TYPE: 1371 // For now use true/false, would be nice to be able to set 1372 // the value text 1373 1374 sprintf(buffer, "%s", fValue.boolt ? "true" : "false"); 1375 fFullValueText = buffer; 1376 break; 1377 1378 case B_CHAR_TYPE: 1379 // Make sure no non-printable characters are displayed: 1380 if (!isprint(fValue.uint8t)) { 1381 *outString = "-"; 1382 fTruncatedWidth = TruncString(outString, fFullValueText.String(), 1383 fFullValueText.Length(), view, fOldWidth); 1384 fDirty = false; 1385 return; 1386 } 1387 1388 sprintf(buffer, "%c", fValue.uint8t); 1389 fFullValueText = buffer; 1390 break; 1391 1392 case B_INT8_TYPE: 1393 sprintf(buffer, "%d", fValue.int8t); 1394 fFullValueText = buffer; 1395 break; 1396 1397 case B_UINT8_TYPE: 1398 sprintf(buffer, "%d", fValue.uint8t); 1399 fFullValueText = buffer; 1400 break; 1401 1402 case B_INT16_TYPE: 1403 sprintf(buffer, "%d", fValue.int16t); 1404 fFullValueText = buffer; 1405 break; 1406 1407 case B_UINT16_TYPE: 1408 sprintf(buffer, "%d", fValue.uint16t); 1409 fFullValueText = buffer; 1410 break; 1411 1412 case B_INT32_TYPE: 1413 sprintf(buffer, "%" B_PRId32, fValue.int32t); 1414 fFullValueText = buffer; 1415 break; 1416 1417 case B_UINT32_TYPE: 1418 sprintf(buffer, "%" B_PRId32, fValue.uint32t); 1419 fFullValueText = buffer; 1420 break; 1421 1422 case B_INT64_TYPE: 1423 sprintf(buffer, "%" B_PRId64, fValue.int64t); 1424 fFullValueText = buffer; 1425 break; 1426 1427 case B_UINT64_TYPE: 1428 sprintf(buffer, "%" B_PRId64, fValue.uint64t); 1429 fFullValueText = buffer; 1430 break; 1431 1432 case B_FLOAT_TYPE: 1433 snprintf(buffer, sizeof(buffer), "%g", fValue.floatt); 1434 fFullValueText = buffer; 1435 break; 1436 1437 case B_DOUBLE_TYPE: 1438 snprintf(buffer, sizeof(buffer), "%g", fValue.doublet); 1439 fFullValueText = buffer; 1440 break; 1441 1442 default: 1443 *outString = "-"; 1444 fTruncatedWidth = TruncString(outString, fFullValueText.String(), 1445 fFullValueText.Length(), view, fOldWidth); 1446 fDirty = false; 1447 return; 1448 } 1449 fTruncatedWidth = TruncString(outString, buffer, (ssize_t)strlen(buffer), 1450 view, fOldWidth); 1451 fDirty = false; 1452 } 1453 1454 1455 const char* 1456 GenericAttributeText::ValueAsText(const BPoseView* view) 1457 { 1458 // TODO: redesign this - this is to make sure the value is valid 1459 bool oldDirty = fDirty; 1460 BString outString; 1461 FitValue(&outString, view); 1462 fDirty = oldDirty; 1463 1464 return fFullValueText.String(); 1465 } 1466 1467 1468 int 1469 GenericAttributeText::Compare(WidgetAttributeText& attr, BPoseView*) 1470 { 1471 GenericAttributeText* compareTo 1472 = dynamic_cast<GenericAttributeText*>(&attr); 1473 ThrowOnAssert(compareTo != NULL); 1474 1475 if (fValueDirty) 1476 ReadValue(&fFullValueText); 1477 1478 if (compareTo->fValueDirty) 1479 compareTo->ReadValue(&compareTo->fFullValueText); 1480 1481 // sort undefined values last, regardless of the other value 1482 if (!fValueIsDefined) 1483 return compareTo->fValueIsDefined ? 1 : 0; 1484 1485 if (!compareTo->fValueIsDefined) 1486 return -1; 1487 1488 switch (fColumn->AttrType()) { 1489 case B_STRING_TYPE: 1490 return fFullValueText.ICompare(compareTo->fFullValueText); 1491 1492 case B_CHAR_TYPE: 1493 { 1494 char vStr[2] = { static_cast<char>(fValue.uint8t), 0 }; 1495 char cStr[2] = { static_cast<char>(compareTo->fValue.uint8t), 0}; 1496 1497 BString valueStr(vStr); 1498 BString compareToStr(cStr); 1499 1500 return valueStr.ICompare(compareToStr); 1501 } 1502 1503 case B_FLOAT_TYPE: 1504 return fValue.floatt >= compareTo->fValue.floatt ? 1505 (fValue.floatt == compareTo->fValue.floatt ? 0 : 1) : -1; 1506 1507 case B_DOUBLE_TYPE: 1508 return fValue.doublet >= compareTo->fValue.doublet ? 1509 (fValue.doublet == compareTo->fValue.doublet ? 0 : 1) : -1; 1510 1511 case B_BOOL_TYPE: 1512 return fValue.boolt >= compareTo->fValue.boolt ? 1513 (fValue.boolt == compareTo->fValue.boolt ? 0 : 1) : -1; 1514 1515 case B_UINT8_TYPE: 1516 return fValue.uint8t >= compareTo->fValue.uint8t ? 1517 (fValue.uint8t == compareTo->fValue.uint8t ? 0 : 1) : -1; 1518 1519 case B_INT8_TYPE: 1520 return fValue.int8t >= compareTo->fValue.int8t ? 1521 (fValue.int8t == compareTo->fValue.int8t ? 0 : 1) : -1; 1522 1523 case B_UINT16_TYPE: 1524 return fValue.uint16t >= compareTo->fValue.uint16t ? 1525 (fValue.uint16t == compareTo->fValue.uint16t ? 0 : 1) : -1; 1526 1527 case B_INT16_TYPE: 1528 return fValue.int16t >= compareTo->fValue.int16t ? 1529 (fValue.int16t == compareTo->fValue.int16t ? 0 : 1) : -1; 1530 1531 case B_UINT32_TYPE: 1532 return fValue.uint32t >= compareTo->fValue.uint32t ? 1533 (fValue.uint32t == compareTo->fValue.uint32t ? 0 : 1) : -1; 1534 1535 case B_TIME_TYPE: 1536 // time_t typedef'd to a long, i.e. a int32 1537 case B_INT32_TYPE: 1538 return fValue.int32t >= compareTo->fValue.int32t ? 1539 (fValue.int32t == compareTo->fValue.int32t ? 0 : 1) : -1; 1540 1541 case B_OFF_T_TYPE: 1542 // off_t typedef'd to a long long, i.e. a int64 1543 case B_INT64_TYPE: 1544 return fValue.int64t >= compareTo->fValue.int64t ? 1545 (fValue.int64t == compareTo->fValue.int64t ? 0 : 1) : -1; 1546 1547 case B_UINT64_TYPE: 1548 default: 1549 return fValue.uint64t >= compareTo->fValue.uint64t ? 1550 (fValue.uint64t == compareTo->fValue.uint64t ? 0 : 1) : -1; 1551 } 1552 1553 return 0; 1554 } 1555 1556 1557 bool 1558 GenericAttributeText::CommitEditedText(BTextView* textView) 1559 { 1560 ASSERT(fColumn->Editable()); 1561 const char* text = textView->Text(); 1562 1563 if (fFullValueText == text) 1564 // no change 1565 return false; 1566 1567 if (!CommitEditedTextFlavor(textView)) 1568 return false; 1569 1570 // update text and width in this widget 1571 fFullValueText = text; 1572 // cause re-truncation 1573 fDirty = true; 1574 fValueDirty = true; 1575 1576 return true; 1577 } 1578 1579 1580 void 1581 GenericAttributeText::SetUpEditing(BTextView* textView) 1582 { 1583 textView->SetMaxBytes(kGenericReadBufferSize - 1); 1584 textView->SetText(fFullValueText.String(), fFullValueText.Length()); 1585 } 1586 1587 1588 bool 1589 GenericAttributeText::CommitEditedTextFlavor(BTextView* textView) 1590 { 1591 BNode node(fModel->EntryRef()); 1592 1593 if (node.InitCheck() != B_OK) 1594 return false; 1595 1596 uint32 type = fColumn->AttrType(); 1597 1598 if (type != B_STRING_TYPE 1599 && type != B_UINT64_TYPE 1600 && type != B_UINT32_TYPE 1601 && type != B_UINT16_TYPE 1602 && type != B_UINT8_TYPE 1603 && type != B_INT64_TYPE 1604 && type != B_INT32_TYPE 1605 && type != B_INT16_TYPE 1606 && type != B_INT8_TYPE 1607 && type != B_OFF_T_TYPE 1608 && type != B_TIME_TYPE 1609 && type != B_FLOAT_TYPE 1610 && type != B_DOUBLE_TYPE 1611 && type != B_CHAR_TYPE 1612 && type != B_BOOL_TYPE) { 1613 BAlert* alert = new BAlert("", 1614 B_TRANSLATE("Sorry, you cannot edit that attribute."), 1615 B_TRANSLATE("Cancel"), 1616 0, 0, B_WIDTH_AS_USUAL, B_STOP_ALERT); 1617 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 1618 alert->Go(); 1619 return false; 1620 } 1621 1622 const char* columnName = fColumn->AttrName(); 1623 ssize_t size = 0; 1624 1625 switch (type) { 1626 case B_STRING_TYPE: 1627 size = fModel->WriteAttr(columnName, type, 0, textView->Text(), 1628 (size_t)textView->TextLength() + 1); 1629 break; 1630 1631 case B_BOOL_TYPE: 1632 { 1633 bool value = strncasecmp(textView->Text(), "0", 1) != 0 1634 && strncasecmp(textView->Text(), "off", 2) != 0 1635 && strncasecmp(textView->Text(), "no", 3) != 0 1636 && strncasecmp(textView->Text(), "false", 4) != 0 1637 && strlen(textView->Text()) != 0; 1638 1639 size = fModel->WriteAttr(columnName, type, 0, &value, sizeof(bool)); 1640 break; 1641 } 1642 1643 case B_CHAR_TYPE: 1644 { 1645 char ch; 1646 sscanf(textView->Text(), "%c", &ch); 1647 //Check if we read the start of a multi-byte glyph: 1648 if (!isprint(ch)) { 1649 BAlert* alert = new BAlert("", 1650 B_TRANSLATE("Sorry, the 'Character' " 1651 "attribute cannot store a multi-byte glyph."), 1652 B_TRANSLATE("Cancel"), 1653 0, 0, B_WIDTH_AS_USUAL, B_STOP_ALERT); 1654 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 1655 alert->Go(); 1656 return false; 1657 } 1658 1659 size = fModel->WriteAttr(columnName, type, 0, &ch, sizeof(char)); 1660 break; 1661 } 1662 1663 case B_FLOAT_TYPE: 1664 { 1665 float floatVal; 1666 1667 if (sscanf(textView->Text(), "%f", &floatVal) == 1) { 1668 fValueIsDefined = true; 1669 fValue.floatt = floatVal; 1670 size = fModel->WriteAttr(columnName, type, 0, &floatVal, 1671 sizeof(float)); 1672 } else { 1673 // If the value was already defined, it's on disk. 1674 // Otherwise not. 1675 return fValueIsDefined; 1676 } 1677 break; 1678 } 1679 1680 case B_DOUBLE_TYPE: 1681 { 1682 double doubleVal; 1683 1684 if (sscanf(textView->Text(), "%lf", &doubleVal) == 1) { 1685 fValueIsDefined = true; 1686 fValue.doublet = doubleVal; 1687 size = fModel->WriteAttr(columnName, type, 0, &doubleVal, 1688 sizeof(double)); 1689 } else { 1690 // If the value was already defined, it's on disk. 1691 // Otherwise not. 1692 return fValueIsDefined; 1693 } 1694 break; 1695 } 1696 1697 case B_TIME_TYPE: 1698 case B_OFF_T_TYPE: 1699 case B_UINT64_TYPE: 1700 case B_UINT32_TYPE: 1701 case B_UINT16_TYPE: 1702 case B_UINT8_TYPE: 1703 case B_INT64_TYPE: 1704 case B_INT32_TYPE: 1705 case B_INT16_TYPE: 1706 case B_INT8_TYPE: 1707 { 1708 GenericValueStruct tmp; 1709 size_t scalarSize = 0; 1710 1711 switch (type) { 1712 case B_TIME_TYPE: 1713 tmp.time_tt = parsedate(textView->Text(), time(0)); 1714 scalarSize = sizeof(time_t); 1715 break; 1716 1717 // do some size independent conversion on builtin types 1718 case B_OFF_T_TYPE: 1719 tmp.off_tt = StringToScalar(textView->Text()); 1720 scalarSize = sizeof(off_t); 1721 break; 1722 1723 case B_UINT64_TYPE: 1724 case B_INT64_TYPE: 1725 tmp.int64t = StringToScalar(textView->Text()); 1726 scalarSize = sizeof(int64); 1727 break; 1728 1729 case B_UINT32_TYPE: 1730 case B_INT32_TYPE: 1731 tmp.int32t = (int32)StringToScalar(textView->Text()); 1732 scalarSize = sizeof(int32); 1733 break; 1734 1735 case B_UINT16_TYPE: 1736 case B_INT16_TYPE: 1737 tmp.int16t = (int16)StringToScalar(textView->Text()); 1738 scalarSize = sizeof(int16); 1739 break; 1740 1741 case B_UINT8_TYPE: 1742 case B_INT8_TYPE: 1743 tmp.int8t = (int8)StringToScalar(textView->Text()); 1744 scalarSize = sizeof(int8); 1745 break; 1746 1747 default: 1748 TRESPASS(); 1749 } 1750 1751 size = fModel->WriteAttr(columnName, type, 0, &tmp, scalarSize); 1752 break; 1753 } 1754 } 1755 1756 if (size < 0) { 1757 BAlert* alert = new BAlert("", 1758 B_TRANSLATE("There was an error writing the attribute."), 1759 B_TRANSLATE("Cancel"), 1760 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 1761 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 1762 alert->Go(); 1763 1764 fValueIsDefined = false; 1765 return false; 1766 } 1767 1768 fValueIsDefined = true; 1769 return true; 1770 } 1771 1772 1773 // #pragma mark - DurationAttributeText (display as: duration) 1774 1775 1776 DurationAttributeText::DurationAttributeText(const Model* model, 1777 const BColumn* column) 1778 : 1779 GenericAttributeText(model, column) 1780 { 1781 } 1782 1783 1784 // TODO: support editing! 1785 1786 1787 void 1788 DurationAttributeText::FitValue(BString* outString, const BPoseView* view) 1789 { 1790 if (fValueDirty) 1791 ReadValue(&fFullValueText); 1792 1793 fOldWidth = fColumn->Width(); 1794 fDirty = false; 1795 1796 if (!fValueIsDefined) { 1797 *outString = "-"; 1798 fTruncatedWidth = TruncString(outString, fFullValueText.String(), 1799 fFullValueText.Length(), view, fOldWidth); 1800 return; 1801 } 1802 1803 int64 time = 0; 1804 1805 switch (fColumn->AttrType()) { 1806 case B_TIME_TYPE: 1807 time = fValue.time_tt * 1000000LL; 1808 break; 1809 1810 case B_INT8_TYPE: 1811 time = fValue.int8t * 1000000LL; 1812 break; 1813 1814 case B_INT16_TYPE: 1815 time = fValue.int16t * 1000000LL; 1816 break; 1817 1818 case B_INT32_TYPE: 1819 time = fValue.int32t * 1000000LL; 1820 break; 1821 1822 case B_INT64_TYPE: 1823 time = fValue.int64t; 1824 break; 1825 } 1826 1827 // TODO: ignores micro seconds for now 1828 int32 seconds = time / 1000000LL; 1829 1830 bool negative = seconds < 0; 1831 if (negative) 1832 seconds = -seconds; 1833 1834 int32 hours = seconds / 3600; 1835 seconds -= hours * 3600; 1836 int32 minutes = seconds / 60; 1837 seconds = seconds % 60; 1838 1839 char buffer[256]; 1840 if (hours > 0) { 1841 snprintf(buffer, sizeof(buffer), "%s%" B_PRId32 ":%02" B_PRId32 ":%02" 1842 B_PRId32, negative ? "-" : "", hours, minutes, seconds); 1843 } else { 1844 snprintf(buffer, sizeof(buffer), "%s%" B_PRId32 ":%02" B_PRId32, 1845 negative ? "-" : "", minutes, seconds); 1846 } 1847 1848 fFullValueText = buffer; 1849 1850 fTruncatedWidth = TruncString(outString, fFullValueText.String(), 1851 fFullValueText.Length(), view, fOldWidth); 1852 } 1853 1854 1855 // #pragma mark - CheckboxAttributeText (display as: checkbox) 1856 1857 1858 CheckboxAttributeText::CheckboxAttributeText(const Model* model, 1859 const BColumn* column) 1860 : 1861 GenericAttributeText(model, column), 1862 fOnChar("✖"), 1863 fOffChar("-") 1864 { 1865 // TODO: better have common data in the column object! 1866 if (const char* separator = strchr(column->DisplayAs(), ':')) { 1867 BString chars(separator + 1); 1868 int32 length; 1869 const char* c = chars.CharAt(0, &length); 1870 fOnChar.SetTo(c, length); 1871 if (c[length]) { 1872 c = chars.CharAt(1, &length); 1873 fOffChar.SetTo(c, length); 1874 } 1875 } 1876 } 1877 1878 1879 void 1880 CheckboxAttributeText::SetUpEditing(BTextView* view) 1881 { 1882 // TODO: support editing for real! 1883 BString outString; 1884 GenericAttributeText::FitValue(&outString, NULL); 1885 GenericAttributeText::SetUpEditing(view); 1886 } 1887 1888 1889 void 1890 CheckboxAttributeText::FitValue(BString* outString, const BPoseView* view) 1891 { 1892 if (fValueDirty) 1893 ReadValue(&fFullValueText); 1894 1895 fOldWidth = fColumn->Width(); 1896 fDirty = false; 1897 1898 if (!fValueIsDefined) { 1899 *outString = fOffChar; 1900 fTruncatedWidth = TruncString(outString, fFullValueText.String(), 1901 fFullValueText.Length(), view, fOldWidth); 1902 return; 1903 } 1904 1905 bool checked = false; 1906 1907 switch (fColumn->AttrType()) { 1908 case B_BOOL_TYPE: 1909 checked = fValue.boolt; 1910 break; 1911 1912 case B_INT8_TYPE: 1913 case B_UINT8_TYPE: 1914 checked = fValue.int8t != 0; 1915 break; 1916 1917 case B_INT16_TYPE: 1918 case B_UINT16_TYPE: 1919 checked = fValue.int16t != 0; 1920 break; 1921 1922 case B_INT32_TYPE: 1923 case B_UINT32_TYPE: 1924 checked = fValue.int32t != 0; 1925 break; 1926 } 1927 1928 fFullValueText = checked ? fOnChar : fOffChar; 1929 1930 fTruncatedWidth = TruncString(outString, fFullValueText.String(), 1931 fFullValueText.Length(), view, fOldWidth); 1932 } 1933 1934 1935 // #pragma mark - RatingAttributeText (display as: rating) 1936 1937 1938 RatingAttributeText::RatingAttributeText(const Model* model, 1939 const BColumn* column) 1940 : 1941 GenericAttributeText(model, column), 1942 fCount(5), 1943 fMax(10) 1944 { 1945 // TODO: support different star counts/max via specifier 1946 } 1947 1948 1949 void 1950 RatingAttributeText::SetUpEditing(BTextView* view) 1951 { 1952 // TODO: support editing for real! 1953 BString outString; 1954 GenericAttributeText::FitValue(&outString, NULL); 1955 GenericAttributeText::SetUpEditing(view); 1956 } 1957 1958 1959 void 1960 RatingAttributeText::FitValue(BString* ratingString, const BPoseView* view) 1961 { 1962 if (fValueDirty) 1963 ReadValue(&fFullValueText); 1964 1965 fOldWidth = fColumn->Width(); 1966 fDirty = false; 1967 1968 int64 rating; 1969 if (fValueIsDefined) { 1970 switch (fColumn->AttrType()) { 1971 case B_INT8_TYPE: 1972 rating = fValue.int8t; 1973 break; 1974 1975 case B_INT16_TYPE: 1976 rating = fValue.int16t; 1977 break; 1978 1979 case B_INT32_TYPE: 1980 rating = fValue.int32t; 1981 break; 1982 1983 default: 1984 rating = 0; 1985 break; 1986 } 1987 } else 1988 rating = 0; 1989 1990 if (rating > fMax) 1991 rating = fMax; 1992 1993 if (rating < 0) 1994 rating = 0; 1995 1996 int32 steps = fMax / fCount; 1997 fFullValueText = ""; 1998 1999 for (int32 i = 0; i < fCount; i++) { 2000 int64 n = i * steps; 2001 if (rating > n) 2002 fFullValueText += "★"; 2003 else 2004 fFullValueText += "☆"; 2005 } 2006 2007 fTruncatedWidth = TruncString(ratingString, fFullValueText.String(), 2008 fFullValueText.Length(), view, fOldWidth); 2009 } 2010 2011 2012 // #pragma mark - OpenWithRelationAttributeText 2013 2014 2015 OpenWithRelationAttributeText::OpenWithRelationAttributeText(const Model* model, 2016 const BColumn* column, const BPoseView* view) 2017 : 2018 ScalarAttributeText(model, column), 2019 fPoseView(view) 2020 { 2021 } 2022 2023 2024 int64 2025 OpenWithRelationAttributeText::ReadValue() 2026 { 2027 fValueDirty = false; 2028 2029 const OpenWithPoseView* view 2030 = dynamic_cast<const OpenWithPoseView*>(fPoseView); 2031 if (view != NULL) { 2032 fValue = view->OpenWithRelation(fModel); 2033 fValueIsDefined = true; 2034 } 2035 2036 return fValue; 2037 } 2038 2039 2040 float 2041 OpenWithRelationAttributeText::PreferredWidth(const BPoseView* pose) const 2042 { 2043 BString widthString; 2044 TruncString(&widthString, fRelationText.String(), fRelationText.Length(), 2045 pose, 500, B_TRUNCATE_END); 2046 return pose->StringWidth(widthString.String()); 2047 } 2048 2049 2050 void 2051 OpenWithRelationAttributeText::FitValue(BString* outString, 2052 const BPoseView* view) 2053 { 2054 if (fValueDirty) 2055 ReadValue(); 2056 2057 ASSERT(view == fPoseView); 2058 const OpenWithPoseView* launchWithView 2059 = dynamic_cast<const OpenWithPoseView*>(view); 2060 if (launchWithView != NULL) 2061 launchWithView->OpenWithRelationDescription(fModel, &fRelationText); 2062 2063 fOldWidth = fColumn->Width(); 2064 fTruncatedWidth = TruncString(outString, fRelationText.String(), 2065 fRelationText.Length(), view, fOldWidth, B_TRUNCATE_END); 2066 fDirty = false; 2067 } 2068 2069 2070 // #pragma mark - VersionAttributeText 2071 2072 2073 VersionAttributeText::VersionAttributeText(const Model* model, 2074 const BColumn* column, bool app) 2075 : 2076 StringAttributeText(model, column), 2077 fAppVersion(app) 2078 { 2079 } 2080 2081 2082 void 2083 VersionAttributeText::ReadValue(BString* outString) 2084 { 2085 fValueDirty = false; 2086 2087 BModelOpener opener(fModel); 2088 BFile* file = dynamic_cast<BFile*>(fModel->Node()); 2089 if (file != NULL) { 2090 BAppFileInfo info(file); 2091 version_info version; 2092 if (info.InitCheck() == B_OK 2093 && info.GetVersionInfo(&version, fAppVersion 2094 ? B_APP_VERSION_KIND : B_SYSTEM_VERSION_KIND) == B_OK) { 2095 *outString = version.short_info; 2096 return; 2097 } 2098 } 2099 2100 *outString = "-"; 2101 } 2102