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