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