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 #include <fs_attr.h> 36 #include <stdlib.h> 37 #include <string.h> 38 #include <ctype.h> 39 #include <parsedate.h> 40 41 #include <Alert.h> 42 #include <AppFileInfo.h> 43 #include <Catalog.h> 44 #include <Debug.h> 45 #include <Locale.h> 46 #include <NodeInfo.h> 47 #include <Path.h> 48 #include <TextView.h> 49 #include <Volume.h> 50 #include <VolumeRoster.h> 51 52 #include "Attributes.h" 53 #include "FindPanel.h" 54 #include "FSUndoRedo.h" 55 #include "FSUtils.h" 56 #include "Model.h" 57 #include "OpenWithWindow.h" 58 #include "MimeTypes.h" 59 #include "PoseView.h" 60 #include "SettingsViews.h" 61 #include "Utilities.h" 62 #include "ViewState.h" 63 #include "WidgetAttributeText.h" 64 65 66 67 #undef B_TRANSLATE_CONTEXT 68 #define B_TRANSLATE_CONTEXT "libtracker" 69 70 71 const int32 kGenericReadBufferSize = 1024; 72 73 template <class View> 74 float 75 TruncStringBase(BString *result, const char *str, int32 length, 76 const View *view, float width, uint32 truncMode = B_TRUNCATE_MIDDLE) 77 { 78 // we are using a template version of this call to make sure 79 // the right StringWidth gets picked up for BView x BPoseView 80 // for max speed and flexibility 81 82 // a standard ellipsis inserting fitting algorithm 83 if (view->StringWidth(str, length) <= width) 84 *result = str; 85 else { 86 const char *srcstr[1]; 87 char *results[1]; 88 89 srcstr[0] = str; 90 results[0] = result->LockBuffer(length + 3); 91 92 BFont font; 93 view->GetFont(&font); 94 95 font.GetTruncatedStrings(srcstr, 1, truncMode, width, results); 96 result->UnlockBuffer(); 97 } 98 return view->StringWidth(result->String(), result->Length()); 99 } 100 101 102 WidgetAttributeText * 103 WidgetAttributeText::NewWidgetText(const Model *model, 104 const BColumn *column, const BPoseView *view) 105 { 106 // call this to make the right WidgetAttributeText type for a 107 // given column 108 109 const char *attrName = column->AttrName(); 110 111 if (strcmp(attrName, kAttrPath) == 0) 112 return new PathAttributeText(model, column); 113 else if (strcmp(attrName, kAttrMIMEType) == 0) 114 return new KindAttributeText(model, column); 115 else if (strcmp(attrName, kAttrStatName) == 0) 116 return new NameAttributeText(model, column); 117 else if (strcmp(attrName, kAttrStatSize) == 0) 118 return new SizeAttributeText(model, column); 119 else if (strcmp(attrName, kAttrStatModified) == 0) 120 return new ModificationTimeAttributeText(model, column); 121 else if (strcmp(attrName, kAttrStatCreated) == 0) 122 return new CreationTimeAttributeText(model, column); 123 #ifdef OWNER_GROUP_ATTRIBUTES 124 else if (strcmp(attrName, kAttrStatOwner) == 0) 125 return new OwnerAttributeText(model, column); 126 else if (strcmp(attrName, kAttrStatGroup) == 0) 127 return new GroupAttributeText(model, column); 128 #endif 129 else if (strcmp(attrName, kAttrStatMode) == 0) 130 return new ModeAttributeText(model, column); 131 else if (strcmp(attrName, kAttrOpenWithRelation) == 0) 132 return new OpenWithRelationAttributeText(model, column, view); 133 else if (strcmp(attrName, kAttrAppVersion) == 0) 134 return new AppShortVersionAttributeText(model, column); 135 else if (strcmp(attrName, kAttrSystemVersion) == 0) 136 return new SystemShortVersionAttributeText(model, column); 137 else if (strcmp(attrName, kAttrOriginalPath) == 0) 138 return new OriginalPathAttributeText(model, column); 139 140 return new GenericAttributeText(model, column); 141 } 142 143 144 WidgetAttributeText::WidgetAttributeText(const Model *model, 145 const BColumn *column) 146 : 147 fModel(const_cast<Model *>(model)), 148 fColumn(column), 149 fDirty(true), 150 fValueIsDefined(false) 151 { 152 ASSERT(fColumn); 153 ASSERT(fColumn->Width() > 0); 154 } 155 156 157 WidgetAttributeText::~WidgetAttributeText() 158 { 159 } 160 161 162 const char * 163 WidgetAttributeText::FittingText(const BPoseView *view) 164 { 165 if (fDirty || fColumn->Width() != fOldWidth || CheckSettingsChanged() 166 || !fValueIsDefined ) 167 CheckViewChanged(view); 168 169 ASSERT(!fDirty); 170 return fText.String(); 171 } 172 173 174 bool 175 WidgetAttributeText::CheckViewChanged(const BPoseView *view) 176 { 177 BString newText; 178 FitValue(&newText, view); 179 if (newText == fText) 180 return false; 181 182 fText = newText; 183 return true; 184 } 185 186 187 bool 188 WidgetAttributeText::CheckSettingsChanged() 189 { 190 return false; 191 } 192 193 194 float 195 WidgetAttributeText::TruncString(BString *result, const char *str, 196 int32 length, const BPoseView *view, float width, uint32 truncMode) 197 { 198 return TruncStringBase(result, str, length, view, width, truncMode); 199 } 200 201 202 const char *kSizeFormats[] = { 203 "%.2f %s", 204 "%.1f %s", 205 "%.f %s", 206 "%.f%s", 207 0 208 }; 209 210 211 template <class View> 212 float 213 TruncFileSizeBase(BString *result, int64 value, const View *view, float width) 214 { 215 // ToDo: 216 // if slow, replace float divisions with shifts 217 // if fast enough, try fitting more decimal places 218 219 // TODO: reuse libshared's string_for_size 220 221 // format file size value 222 char buffer[1024]; 223 if (value == kUnknownSize) { 224 *result = "-"; 225 return view->StringWidth("-"); 226 } else if (value < kKBSize) { 227 sprintf(buffer, B_TRANSLATE("%Ld bytes"), value); 228 if (view->StringWidth(buffer) > width) 229 sprintf(buffer, B_TRANSLATE("%Ld B"), value); 230 } else { 231 const char *suffix; 232 float floatValue; 233 if (value >= kTBSize) { 234 suffix = B_TRANSLATE("TiB"); 235 floatValue = (float)value / kTBSize; 236 } else if (value >= kGBSize) { 237 suffix = B_TRANSLATE("GiB"); 238 floatValue = (float)value / kGBSize; 239 } else if (value >= kMBSize) { 240 suffix = B_TRANSLATE("MiB"); 241 floatValue = (float)value / kMBSize; 242 } else { 243 ASSERT(value >= kKBSize); 244 suffix = B_TRANSLATE("KiB"); 245 floatValue = (float)value / kKBSize; 246 } 247 248 for (int32 index = 0; ; index++) { 249 if (!kSizeFormats[index]) 250 break; 251 252 sprintf(buffer, kSizeFormats[index], floatValue, suffix); 253 254 // strip off an insignificant zero so we don't get readings 255 // such as 1.00 256 char *period = 0; 257 for (char *tmp = buffer; *tmp; tmp++) { 258 if (*tmp == '.') 259 period = tmp; 260 } 261 if (period && period[1] && period[2] == '0') 262 // move the rest of the string over the insignificant zero 263 for (char *tmp = &period[2]; *tmp; tmp++) 264 *tmp = tmp[1]; 265 266 float resultWidth = view->StringWidth(buffer); 267 if (resultWidth <= width) { 268 *result = buffer; 269 return resultWidth; 270 } 271 } 272 } 273 274 return TruncStringBase(result, buffer, (ssize_t)strlen(buffer), view, width, 275 (uint32)B_TRUNCATE_END); 276 } 277 278 279 float 280 WidgetAttributeText::TruncFileSize(BString *result, int64 value, 281 const BPoseView *view, float width) 282 { 283 return TruncFileSizeBase(result, value, view, width); 284 } 285 286 template <class View> 287 float 288 TruncTimeBase(BString *result, int64 value, const View *view, float width) 289 { 290 float resultWidth = 0; 291 char buffer[256]; 292 293 time_t timeValue = (time_t)value; 294 295 BLocale here; 296 be_locale_roster->GetDefaultLocale(&here); 297 298 if (here.FormatDateTime(buffer, 256, timeValue, true) == B_OK) { 299 resultWidth = view->StringWidth(buffer); 300 } 301 302 if (resultWidth > width && here.FormatDateTime(buffer, 256, timeValue, 303 false) == B_OK) { 304 resultWidth = view->StringWidth(buffer); 305 } 306 307 if (resultWidth > width) { 308 // even the shortest format string didn't do it, insert ellipsis 309 resultWidth = TruncStringBase(result, buffer, (ssize_t)strlen(buffer), 310 view, width); 311 } else 312 *result = buffer; 313 314 return resultWidth; 315 } 316 317 318 float 319 WidgetAttributeText::TruncTime(BString *result, int64 value, 320 const BPoseView *view, float width) 321 { 322 return TruncTimeBase(result, value, view, width); 323 } 324 325 326 float 327 WidgetAttributeText::CurrentWidth() const 328 { 329 return fTruncatedWidth; 330 } 331 332 333 float 334 WidgetAttributeText::Width(const BPoseView *pose) 335 { 336 FittingText(pose); 337 return CurrentWidth(); 338 } 339 340 341 void 342 WidgetAttributeText::SetUpEditing(BTextView *) 343 { 344 ASSERT(fColumn->Editable()); 345 } 346 347 348 bool 349 WidgetAttributeText::CommitEditedText(BTextView *) 350 { 351 // can't do anything here at this point 352 TRESPASS(); 353 return false; 354 } 355 356 357 status_t 358 WidgetAttributeText::AttrAsString(const Model *model, BString *result, 359 const char *attrName, int32 attrType, 360 float width, BView *view, int64 *resultingValue) 361 { 362 int64 value; 363 364 status_t error = model->InitCheck(); 365 if (error != B_OK) 366 return error; 367 368 switch (attrType) { 369 case B_TIME_TYPE: 370 if (strcmp(attrName, kAttrStatModified) == 0) 371 value = model->StatBuf()->st_mtime; 372 else if (strcmp(attrName, kAttrStatCreated) == 0) 373 value = model->StatBuf()->st_crtime; 374 else { 375 TRESPASS(); 376 // not yet supported 377 return B_ERROR; 378 } 379 TruncTimeBase(result, value, view, width); 380 if (resultingValue) 381 *resultingValue = value; 382 return B_OK; 383 384 case B_STRING_TYPE: 385 if (strcmp(attrName, kAttrPath) == 0) { 386 BEntry entry(model->EntryRef()); 387 BPath path; 388 BString tmp; 389 390 if (entry.InitCheck() == B_OK && entry.GetPath(&path) == B_OK) { 391 tmp = path.Path(); 392 TruncateLeaf(&tmp); 393 } else 394 tmp = "-"; 395 396 if (width > 0) { 397 TruncStringBase(result, tmp.String(), tmp.Length(), view, 398 width); 399 } else 400 *result = tmp.String(); 401 402 return B_OK; 403 } 404 break; 405 406 case kSizeType: 407 // TruncFileSizeBase(result, model->StatBuf()->st_size, view, width); 408 return B_OK; 409 break; 410 411 default: 412 TRESPASS(); 413 // not yet supported 414 return B_ERROR; 415 416 } 417 418 TRESPASS(); 419 return B_ERROR; 420 } 421 422 423 bool 424 WidgetAttributeText::IsEditable() const 425 { 426 return fColumn->Editable() 427 && !BVolume(fModel->StatBuf()->st_dev).IsReadOnly(); 428 } 429 430 431 void 432 WidgetAttributeText::SetDirty(bool value) 433 { 434 fDirty = value; 435 } 436 437 438 // #pragma mark - 439 440 441 StringAttributeText::StringAttributeText(const Model *model, 442 const BColumn *column) 443 : WidgetAttributeText(model, column), 444 fValueDirty(true) 445 { 446 } 447 448 449 const char * 450 StringAttributeText::ValueAsText(const BPoseView * /*view*/) 451 { 452 if (fValueDirty) 453 ReadValue(&fFullValueText); 454 455 return fFullValueText.String(); 456 } 457 458 459 bool 460 StringAttributeText::CheckAttributeChanged() 461 { 462 BString newString; 463 ReadValue(&newString); 464 465 if (newString == fFullValueText) 466 return false; 467 468 fFullValueText = newString; 469 fDirty = true; // have to redo fitted string 470 return true; 471 } 472 473 474 void 475 StringAttributeText::FitValue(BString *result, const BPoseView *view) 476 { 477 if (fValueDirty) 478 ReadValue(&fFullValueText); 479 fOldWidth = fColumn->Width(); 480 481 fTruncatedWidth = TruncString(result, fFullValueText.String(), 482 fFullValueText.Length(), view, fOldWidth); 483 fDirty = false; 484 } 485 486 487 float 488 StringAttributeText::PreferredWidth(const BPoseView *pose) const 489 { 490 return pose->StringWidth(fFullValueText.String()); 491 } 492 493 494 int 495 StringAttributeText::Compare(WidgetAttributeText &attr, BPoseView *view) 496 { 497 StringAttributeText *compareTo 498 = dynamic_cast<StringAttributeText *>(&attr); 499 ASSERT(compareTo); 500 501 if (fValueDirty) 502 ReadValue(&fFullValueText); 503 504 return NaturalCompare(fFullValueText.String(), 505 compareTo->ValueAsText(view)); 506 } 507 508 509 bool 510 StringAttributeText::CommitEditedText(BTextView *textView) 511 { 512 ASSERT(fColumn->Editable()); 513 const char *text = textView->Text(); 514 515 if (fFullValueText == text) 516 // no change 517 return false; 518 519 if (textView->TextLength() == 0) 520 // cannot do an empty name 521 return false; 522 523 // cause re-truncation 524 fDirty = true; 525 526 if (!CommitEditedTextFlavor(textView)) 527 return false; 528 529 // update text and width in this widget 530 fFullValueText = text; 531 532 return true; 533 } 534 535 536 // #pragma mark - 537 538 539 ScalarAttributeText::ScalarAttributeText(const Model *model, 540 const BColumn *column) 541 : WidgetAttributeText(model, column), 542 fValueDirty(true) 543 { 544 } 545 546 547 int64 548 ScalarAttributeText::Value() 549 { 550 if (fValueDirty) 551 fValue = ReadValue(); 552 return fValue; 553 } 554 555 556 bool 557 ScalarAttributeText::CheckAttributeChanged() 558 { 559 int64 newValue = ReadValue(); 560 if (newValue == fValue) 561 return false; 562 563 fValue = newValue; 564 fDirty = true; // have to redo fitted string 565 return true; 566 } 567 568 569 float 570 ScalarAttributeText::PreferredWidth(const BPoseView *pose) const 571 { 572 BString widthString; 573 widthString << fValue; 574 return pose->StringWidth(widthString.String()); 575 } 576 577 578 int 579 ScalarAttributeText::Compare(WidgetAttributeText &attr, BPoseView *) 580 { 581 ScalarAttributeText *compareTo 582 = dynamic_cast<ScalarAttributeText *>(&attr); 583 ASSERT(compareTo); 584 // make sure we're not comparing apples and oranges 585 586 if (fValueDirty) 587 fValue = ReadValue(); 588 589 return fValue >= compareTo->Value() 590 ? (fValue == compareTo->Value() ? 0 : 1) : -1; 591 } 592 593 594 // #pragma mark - 595 596 597 PathAttributeText::PathAttributeText(const Model *model, const BColumn *column) 598 : StringAttributeText(model, column) 599 { 600 } 601 602 603 void 604 PathAttributeText::ReadValue(BString *result) 605 { 606 // get the path 607 BEntry entry(fModel->EntryRef()); 608 BPath path; 609 610 if (entry.InitCheck() == B_OK && entry.GetPath(&path) == B_OK) { 611 *result = path.Path(); 612 TruncateLeaf(result); 613 } else 614 *result = "-"; 615 fValueDirty = false; 616 } 617 618 619 // #pragma mark - 620 621 622 OriginalPathAttributeText::OriginalPathAttributeText(const Model *model, 623 const BColumn *column) 624 : StringAttributeText(model, column) 625 { 626 } 627 628 629 void 630 OriginalPathAttributeText::ReadValue(BString *result) 631 { 632 BEntry entry(fModel->EntryRef()); 633 BPath path; 634 635 // get the original path 636 if (entry.InitCheck() == B_OK && FSGetOriginalPath(&entry, &path) == B_OK) 637 *result = path.Path(); 638 else 639 *result = "-"; 640 fValueDirty = false; 641 } 642 643 644 // #pragma mark - 645 646 647 KindAttributeText::KindAttributeText(const Model *model, const BColumn *column) 648 : StringAttributeText(model, column) 649 { 650 } 651 652 653 void 654 KindAttributeText::ReadValue(BString *result) 655 { 656 BMimeType mime; 657 char desc[B_MIME_TYPE_LENGTH]; 658 659 // get the mime type 660 if (mime.SetType(fModel->MimeType()) != B_OK) 661 *result = B_TRANSLATE("Unknown"); 662 // get the short mime type description 663 else if (mime.GetShortDescription(desc) == B_OK) 664 *result = desc; 665 else 666 *result = fModel->MimeType(); 667 fValueDirty = false; 668 } 669 670 671 // #pragma mark - 672 673 674 NameAttributeText::NameAttributeText(const Model *model, const BColumn *column) 675 : StringAttributeText(model, column) 676 { 677 } 678 679 680 int 681 NameAttributeText::Compare(WidgetAttributeText &attr, BPoseView *view) 682 { 683 NameAttributeText *compareTo = dynamic_cast<NameAttributeText *>(&attr); 684 685 ASSERT(compareTo); 686 687 if (fValueDirty) 688 ReadValue(&fFullValueText); 689 690 if (NameAttributeText::sSortFolderNamesFirst) 691 return fModel->CompareFolderNamesFirst(attr.TargetModel()); 692 693 return NaturalCompare(fFullValueText.String(), compareTo->ValueAsText(view)); 694 } 695 696 697 void 698 NameAttributeText::ReadValue(BString *result) 699 { 700 #ifdef DEBUG 701 // x86 support :-) 702 if ((modifiers() & B_CAPS_LOCK) != 0) { 703 if (fModel->IsVolume()) { 704 BVolumeRoster roster; 705 roster.Rewind(); 706 BVolume volume; 707 char device = 'A'; 708 while (roster.GetNextVolume(&volume) == B_OK) { 709 char name[256]; 710 if (volume.GetName(name) == B_OK 711 && strcmp(name, fModel->Name()) == 0) { 712 *result += device; 713 *result += ':'; 714 fValueDirty = false; 715 return; 716 } 717 device++; 718 } 719 } 720 const char *modelName = fModel->Name(); 721 bool hasDot = strstr(".", modelName) != 0; 722 for (int32 index = 0; index < 8; index++) { 723 if (!modelName[index] || modelName[index] == '.') 724 break; 725 *result += toupper(modelName[index]); 726 } 727 if (hasDot) { 728 modelName = strstr(".", modelName); 729 for (int32 index = 0; index < 4; index++) { 730 if (!modelName[index]) 731 break; 732 *result += toupper(modelName[index]); 733 } 734 } else if (fModel->IsExecutable()) 735 *result += ".EXE"; 736 737 } else 738 #endif 739 *result = fModel->Name(); 740 741 fValueDirty = false; 742 } 743 744 745 void 746 NameAttributeText::FitValue(BString *result, const BPoseView *view) 747 { 748 if (fValueDirty) 749 ReadValue(&fFullValueText); 750 fOldWidth = fColumn->Width(); 751 fTruncatedWidth = TruncString(result, fFullValueText.String(), 752 fFullValueText.Length(), view, fOldWidth, B_TRUNCATE_END); 753 fDirty = false; 754 } 755 756 757 void 758 NameAttributeText::SetUpEditing(BTextView *textView) 759 { 760 DisallowFilenameKeys(textView); 761 762 textView->SetMaxBytes(B_FILE_NAME_LENGTH); 763 textView->SetText(fFullValueText.String(), fFullValueText.Length()); 764 } 765 766 767 bool 768 NameAttributeText::CommitEditedTextFlavor(BTextView *textView) 769 { 770 const char *text = textView->Text(); 771 772 BEntry entry(fModel->EntryRef()); 773 if (entry.InitCheck() != B_OK) 774 return false; 775 776 BDirectory parent; 777 if (entry.GetParent(&parent) != B_OK) 778 return false; 779 780 bool removeExisting = false; 781 if (parent.Contains(text)) { 782 BAlert* alert = new BAlert("", 783 B_TRANSLATE("That name is already taken. " 784 "Please type another one."), 785 B_TRANSLATE("Replace other file"), 786 B_TRANSLATE("OK"), 787 NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 788 789 alert->SetShortcut(0, 'r'); 790 791 if (alert->Go()) 792 return false; 793 794 removeExisting = true; 795 } 796 797 // ToDo: 798 // use model-flavor specific virtuals for all of these special 799 // renamings 800 status_t result; 801 if (fModel->IsVolume()) { 802 BVolume volume(fModel->NodeRef()->device); 803 result = volume.InitCheck(); 804 if (result == B_OK) { 805 RenameVolumeUndo undo(volume, text); 806 807 result = volume.SetName(text); 808 if (result != B_OK) 809 undo.Remove(); 810 } 811 } else { 812 if (fModel->IsQuery()) { 813 BModelWriteOpener opener(fModel); 814 ASSERT(fModel->Node()); 815 MoreOptionsStruct::SetQueryTemporary(fModel->Node(), false); 816 } 817 818 RenameUndo undo(entry, text); 819 820 result = entry.Rename(text, removeExisting); 821 if (result != B_OK) 822 undo.Remove(); 823 } 824 825 return result == B_OK; 826 } 827 828 bool NameAttributeText::sSortFolderNamesFirst = false; 829 830 void 831 NameAttributeText::SetSortFolderNamesFirst(bool enabled) 832 { 833 NameAttributeText::sSortFolderNamesFirst = enabled; 834 } 835 836 837 // #pragma mark - 838 839 #ifdef OWNER_GROUP_ATTRIBUTES 840 841 OwnerAttributeText::OwnerAttributeText(const Model *model, 842 const BColumn *column) 843 : StringAttributeText(model, column) 844 { 845 } 846 847 848 void 849 OwnerAttributeText::ReadValue(BString *result) 850 { 851 uid_t nodeOwner = fModel->StatBuf()->st_uid; 852 BString user; 853 854 if (nodeOwner == 0) { 855 if (getenv("USER") != NULL) 856 user << getenv("USER"); 857 else 858 user << "root"; 859 } else 860 user << nodeOwner; 861 *result = user.String(); 862 863 fValueDirty = false; 864 } 865 866 867 GroupAttributeText::GroupAttributeText(const Model *model, 868 const BColumn *column) 869 : StringAttributeText(model, column) 870 { 871 } 872 873 874 void 875 GroupAttributeText::ReadValue(BString *result) 876 { 877 gid_t nodeGroup = fModel->StatBuf()->st_gid; 878 BString group; 879 880 if (nodeGroup == 0) { 881 if (getenv("GROUP") != NULL) 882 group << getenv("GROUP"); 883 else 884 group << "0"; 885 } else 886 group << nodeGroup; 887 *result = group.String(); 888 889 fValueDirty = false; 890 } 891 892 #endif /* OWNER_GROUP_ATTRIBUTES */ 893 894 ModeAttributeText::ModeAttributeText(const Model *model, const BColumn *column) 895 : StringAttributeText(model, column) 896 { 897 } 898 899 900 void 901 ModeAttributeText::ReadValue(BString *result) 902 { 903 mode_t mode = fModel->StatBuf()->st_mode; 904 mode_t baseMask = 00400; 905 char buffer[11]; 906 907 char *scanner = buffer; 908 909 if (S_ISDIR(mode)) 910 *scanner++ = 'd'; 911 else if (S_ISLNK(mode)) 912 *scanner++ = 'l'; 913 else if (S_ISBLK(mode)) 914 *scanner++ = 'b'; 915 else if (S_ISCHR(mode)) 916 *scanner++ = 'c'; 917 else 918 *scanner++ = '-'; 919 920 for (int32 index = 0; index < 9; index++) { 921 *scanner++ = (mode & baseMask) ? "rwx"[index % 3] : '-'; 922 baseMask >>= 1; 923 } 924 925 *scanner = 0; 926 *result = buffer; 927 928 fValueDirty = false; 929 } 930 931 932 // #pragma mark - 933 934 935 SizeAttributeText::SizeAttributeText(const Model *model, const BColumn *column) 936 : ScalarAttributeText(model, column) 937 { 938 } 939 940 941 int64 942 SizeAttributeText::ReadValue() 943 { 944 fValueDirty = false; 945 // get the size 946 947 if (fModel->IsVolume()) { 948 BVolume volume(fModel->NodeRef()->device); 949 950 return volume.Capacity(); 951 } 952 953 if (fModel->IsDirectory() || fModel->IsQuery() 954 || fModel->IsQueryTemplate() || fModel->IsSymLink()) 955 return kUnknownSize; 956 957 fValueIsDefined = true; 958 959 return fModel->StatBuf()->st_size; 960 } 961 962 963 void 964 SizeAttributeText::FitValue(BString *result, const BPoseView *view) 965 { 966 if (fValueDirty) 967 fValue = ReadValue(); 968 fOldWidth = fColumn->Width(); 969 fTruncatedWidth = TruncFileSize(result, fValue, view, fOldWidth); 970 fDirty = false; 971 } 972 973 974 float 975 SizeAttributeText::PreferredWidth(const BPoseView *pose) const 976 { 977 if (fValueIsDefined) { 978 BString widthString; 979 TruncFileSize(&widthString, fValue, pose, 100000); 980 return pose->StringWidth(widthString.String()); 981 } 982 return pose->StringWidth("-"); 983 } 984 985 986 // #pragma mark - 987 988 989 TimeAttributeText::TimeAttributeText(const Model *model, const BColumn *column) 990 : ScalarAttributeText(model, column) 991 { 992 } 993 994 995 float 996 TimeAttributeText::PreferredWidth(const BPoseView *pose) const 997 { 998 BString widthString; 999 TruncTimeBase(&widthString, fValue, pose, 100000); 1000 return pose->StringWidth(widthString.String()); 1001 } 1002 1003 1004 void 1005 TimeAttributeText::FitValue(BString *result, const BPoseView *view) 1006 { 1007 if (fValueDirty) 1008 fValue = ReadValue(); 1009 fOldWidth = fColumn->Width(); 1010 fTruncatedWidth = TruncTime(result, fValue, view, fOldWidth); 1011 fDirty = false; 1012 } 1013 1014 1015 bool 1016 TimeAttributeText::CheckSettingsChanged(void) 1017 { 1018 // TODO : check against the actual locale settings 1019 return false; 1020 } 1021 1022 1023 CreationTimeAttributeText::CreationTimeAttributeText(const Model *model, 1024 const BColumn *column) 1025 : TimeAttributeText(model, column) 1026 { 1027 } 1028 1029 1030 int64 1031 CreationTimeAttributeText::ReadValue() 1032 { 1033 fValueDirty = false; 1034 fValueIsDefined = true; 1035 return fModel->StatBuf()->st_crtime; 1036 } 1037 1038 ModificationTimeAttributeText::ModificationTimeAttributeText(const Model *model, 1039 const BColumn *column) 1040 : TimeAttributeText(model, column) 1041 { 1042 } 1043 1044 1045 int64 1046 ModificationTimeAttributeText::ReadValue() 1047 { 1048 fValueDirty = false; 1049 fValueIsDefined = true; 1050 return fModel->StatBuf()->st_mtime; 1051 } 1052 1053 1054 // #pragma mark - 1055 1056 1057 GenericAttributeText::GenericAttributeText(const Model *model, 1058 const BColumn *column) 1059 : StringAttributeText(model, column) 1060 { 1061 } 1062 1063 1064 bool 1065 GenericAttributeText::CheckAttributeChanged() 1066 { 1067 GenericValueStruct tmpValue = fValue; 1068 BString tmpString(fFullValueText); 1069 ReadValue(&fFullValueText); 1070 1071 // fDirty could already be true, in that case we mustn't set it to 1072 // false, even if the attribute text hasn't changed 1073 bool changed = (fValue.int64t != tmpValue.int64t) || (tmpString != fFullValueText); 1074 if (changed) 1075 fDirty = true; 1076 1077 return fDirty; 1078 } 1079 1080 1081 float 1082 GenericAttributeText::PreferredWidth(const BPoseView *pose) const 1083 { 1084 return pose->StringWidth(fFullValueText.String()); 1085 } 1086 1087 1088 void 1089 GenericAttributeText::ReadValue(BString *result) 1090 { 1091 BModelOpener opener(const_cast<Model *>(fModel)); 1092 1093 ssize_t length = 0; 1094 fFullValueText = "-"; 1095 fValue.int64t = 0; 1096 fValueIsDefined = false; 1097 fValueDirty = false; 1098 1099 if (!fModel->Node()) 1100 return; 1101 1102 switch (fColumn->AttrType()) { 1103 case B_STRING_TYPE: 1104 { 1105 char buffer[kGenericReadBufferSize]; 1106 length = fModel->Node()->ReadAttr(fColumn->AttrName(), 1107 fColumn->AttrType(), 0, buffer, kGenericReadBufferSize - 1); 1108 1109 if (length > 0) { 1110 buffer[length] = '\0'; 1111 // make sure the buffer is null-terminated even if we 1112 // didn't read the whole attribute in or it wasn't to 1113 // begin with 1114 1115 *result = buffer; 1116 fValueIsDefined = true; 1117 } 1118 break; 1119 } 1120 1121 case B_SSIZE_T_TYPE: 1122 case B_TIME_TYPE: 1123 case B_OFF_T_TYPE: 1124 case B_FLOAT_TYPE: 1125 case B_BOOL_TYPE: 1126 case B_CHAR_TYPE: 1127 case B_INT8_TYPE: 1128 case B_INT16_TYPE: 1129 case B_INT32_TYPE: 1130 case B_INT64_TYPE: 1131 case B_UINT8_TYPE: 1132 case B_UINT16_TYPE: 1133 case B_UINT32_TYPE: 1134 case B_UINT64_TYPE: 1135 case B_DOUBLE_TYPE: 1136 { 1137 // read in the numerical bit representation and attach it 1138 // with a type, depending on the bytes that could be read 1139 attr_info info; 1140 GenericValueStruct tmp; 1141 if (fModel->Node()->GetAttrInfo(fColumn->AttrName(), &info) == B_OK) { 1142 if (info.size && info.size <= sizeof(int64)) { 1143 length = fModel->Node()->ReadAttr(fColumn->AttrName(), 1144 fColumn->AttrType(), 0, &tmp, (size_t)info.size); 1145 } 1146 1147 // We used tmp as a block of memory, now set the correct fValue: 1148 1149 if (length == info.size) { 1150 if (fColumn->AttrType() == B_FLOAT_TYPE 1151 || fColumn->AttrType() == B_DOUBLE_TYPE) { 1152 // filter out special float/double types 1153 switch (info.size) { 1154 case sizeof(float): 1155 fValueIsDefined = true; 1156 fValue.floatt = tmp.floatt; 1157 break; 1158 1159 case sizeof(double): 1160 fValueIsDefined = true; 1161 fValue.doublet = tmp.doublet; 1162 break; 1163 1164 default: 1165 TRESPASS(); 1166 } 1167 } else { 1168 // handle the standard data types 1169 switch (info.size) { 1170 case sizeof(char): // Takes care of bool, too. 1171 fValueIsDefined = true; 1172 fValue.int8t = tmp.int8t; 1173 break; 1174 1175 case sizeof(int16): 1176 fValueIsDefined = true; 1177 fValue.int16t = tmp.int16t; 1178 break; 1179 1180 case sizeof(int32): // Takes care of time_t, too. 1181 fValueIsDefined = true; 1182 fValue.int32t = tmp.int32t; 1183 break; 1184 1185 case sizeof(int64): // Taked care of off_t, too. 1186 fValueIsDefined = true; 1187 fValue.int64t = tmp.int64t; 1188 break; 1189 1190 default: 1191 TRESPASS(); 1192 } 1193 } 1194 } 1195 } 1196 break; 1197 } 1198 } 1199 } 1200 1201 1202 void 1203 GenericAttributeText::FitValue(BString *result, const BPoseView *view) 1204 { 1205 if (fValueDirty) 1206 ReadValue(&fFullValueText); 1207 1208 fOldWidth = fColumn->Width(); 1209 1210 if (!fValueIsDefined) { 1211 *result = "-"; 1212 fTruncatedWidth = TruncString(result, fFullValueText.String(), 1213 fFullValueText.Length(), view, fOldWidth); 1214 fDirty = false; 1215 return; 1216 } 1217 1218 char buffer[256]; 1219 1220 switch (fColumn->AttrType()) { 1221 case B_SIZE_T_TYPE: 1222 TruncFileSizeBase(result, fValue.int32t, view, fOldWidth); 1223 return; 1224 1225 case B_SSIZE_T_TYPE: 1226 if (fValue.int32t > 0) { 1227 TruncFileSizeBase(result, fValue.int32t, view, fOldWidth); 1228 return; 1229 } 1230 sprintf(buffer, "%s", strerror(fValue.int32t)); 1231 fFullValueText = buffer; 1232 break; 1233 1234 case B_STRING_TYPE: 1235 fTruncatedWidth = TruncString(result, fFullValueText.String(), 1236 fFullValueText.Length(), view, fOldWidth); 1237 fDirty = false; 1238 return; 1239 1240 case B_OFF_T_TYPE: 1241 // as a side effect update the fFullValueText to the string representation 1242 // of value 1243 TruncFileSize(&fFullValueText, fValue.off_tt, view, 100000); 1244 fTruncatedWidth = TruncFileSize(result, fValue.off_tt, view, fOldWidth); 1245 fDirty = false; 1246 return; 1247 1248 case B_TIME_TYPE: 1249 // as a side effect update the fFullValueText to the string representation 1250 // of value 1251 TruncTime(&fFullValueText, fValue.time_tt, view, 100000); 1252 fTruncatedWidth = TruncTime(result, fValue.time_tt, view, fOldWidth); 1253 fDirty = false; 1254 return; 1255 1256 case B_BOOL_TYPE: 1257 // For now use true/false, would be nice to be able to set 1258 // the value text 1259 1260 sprintf(buffer, "%s", fValue.boolt ? "true" : "false"); 1261 fFullValueText = buffer; 1262 break; 1263 1264 case B_CHAR_TYPE: 1265 // Make sure no non-printable characters are displayed: 1266 if (!isprint(fValue.uint8t)) { 1267 *result = "-"; 1268 fTruncatedWidth = TruncString(result, fFullValueText.String(), 1269 fFullValueText.Length(), view, fOldWidth); 1270 fDirty = false; 1271 return; 1272 } 1273 1274 sprintf(buffer, "%c", fValue.uint8t); 1275 fFullValueText = buffer; 1276 break; 1277 1278 case B_INT8_TYPE: 1279 sprintf(buffer, "%d", fValue.int8t); 1280 fFullValueText = buffer; 1281 break; 1282 1283 case B_UINT8_TYPE: 1284 sprintf(buffer, "%d", fValue.uint8t); 1285 fFullValueText = buffer; 1286 break; 1287 1288 case B_INT16_TYPE: 1289 sprintf(buffer, "%d", fValue.int16t); 1290 fFullValueText = buffer; 1291 break; 1292 1293 case B_UINT16_TYPE: 1294 sprintf(buffer, "%d", fValue.uint16t); 1295 fFullValueText = buffer; 1296 break; 1297 1298 case B_INT32_TYPE: 1299 sprintf(buffer, "%ld", fValue.int32t); 1300 fFullValueText = buffer; 1301 break; 1302 1303 case B_UINT32_TYPE: 1304 sprintf(buffer, "%ld", fValue.uint32t); 1305 fFullValueText = buffer; 1306 break; 1307 1308 case B_INT64_TYPE: 1309 sprintf(buffer, "%Ld", fValue.int64t); 1310 fFullValueText = buffer; 1311 break; 1312 1313 case B_UINT64_TYPE: 1314 sprintf(buffer, "%Ld", fValue.uint64t); 1315 fFullValueText = buffer; 1316 break; 1317 1318 case B_FLOAT_TYPE: 1319 snprintf(buffer, sizeof(buffer), "%g", fValue.floatt); 1320 fFullValueText = buffer; 1321 break; 1322 1323 case B_DOUBLE_TYPE: 1324 snprintf(buffer, sizeof(buffer), "%g", fValue.doublet); 1325 fFullValueText = buffer; 1326 break; 1327 1328 default: 1329 *result = "-"; 1330 fTruncatedWidth = TruncString(result, fFullValueText.String(), 1331 fFullValueText.Length(), view, fOldWidth); 1332 fDirty = false; 1333 return; 1334 } 1335 fTruncatedWidth = TruncString(result, buffer, (ssize_t)strlen(buffer), view, 1336 fOldWidth); 1337 fDirty = false; 1338 } 1339 1340 1341 const char* 1342 GenericAttributeText::ValueAsText(const BPoseView *view) 1343 { 1344 // TODO: redesign this - this is to make sure the value is valid 1345 bool oldDirty = fDirty; 1346 BString result; 1347 FitValue(&result, view); 1348 fDirty = oldDirty; 1349 1350 return fFullValueText.String(); 1351 } 1352 1353 1354 int 1355 GenericAttributeText::Compare(WidgetAttributeText &attr, BPoseView *) 1356 { 1357 GenericAttributeText *compareTo 1358 = dynamic_cast<GenericAttributeText *>(&attr); 1359 ASSERT(compareTo); 1360 1361 if (fValueDirty) 1362 ReadValue(&fFullValueText); 1363 if (compareTo->fValueDirty) 1364 compareTo->ReadValue(&compareTo->fFullValueText); 1365 1366 // Sort undefined values last, regardless of the other value: 1367 if (!fValueIsDefined) 1368 return compareTo->fValueIsDefined ? 1 : 0; 1369 if (!compareTo->fValueIsDefined) 1370 return -1; 1371 1372 switch (fColumn->AttrType()) { 1373 case B_STRING_TYPE: 1374 return fFullValueText.ICompare(compareTo->fFullValueText); 1375 1376 case B_CHAR_TYPE: 1377 { 1378 char vStr[2] = { static_cast<char>(fValue.uint8t), 0 }; 1379 char cStr[2] = { static_cast<char>(compareTo->fValue.uint8t), 0}; 1380 1381 BString valueStr(vStr); 1382 BString compareToStr(cStr); 1383 1384 return valueStr.ICompare(compareToStr); 1385 } 1386 1387 case B_FLOAT_TYPE: 1388 return fValue.floatt >= compareTo->fValue.floatt ? 1389 (fValue.floatt == compareTo->fValue.floatt ? 0 : 1) : -1; 1390 1391 case B_DOUBLE_TYPE: 1392 return fValue.doublet >= compareTo->fValue.doublet ? 1393 (fValue.doublet == compareTo->fValue.doublet ? 0 : 1) : -1; 1394 1395 case B_BOOL_TYPE: 1396 return fValue.boolt >= compareTo->fValue.boolt ? 1397 (fValue.boolt == compareTo->fValue.boolt ? 0 : 1) : -1; 1398 1399 case B_UINT8_TYPE: 1400 return fValue.uint8t >= compareTo->fValue.uint8t ? 1401 (fValue.uint8t == compareTo->fValue.uint8t ? 0 : 1) : -1; 1402 1403 case B_INT8_TYPE: 1404 return fValue.int8t >= compareTo->fValue.int8t ? 1405 (fValue.int8t == compareTo->fValue.int8t ? 0 : 1) : -1; 1406 1407 case B_UINT16_TYPE: 1408 return fValue.uint16t >= compareTo->fValue.uint16t ? 1409 (fValue.uint16t == compareTo->fValue.uint16t ? 0 : 1) : -1; 1410 1411 case B_INT16_TYPE: 1412 return fValue.int16t >= compareTo->fValue.int16t ? 1413 (fValue.int16t == compareTo->fValue.int16t ? 0 : 1) : -1; 1414 1415 case B_UINT32_TYPE: 1416 return fValue.uint32t >= compareTo->fValue.uint32t ? 1417 (fValue.uint32t == compareTo->fValue.uint32t ? 0 : 1) : -1; 1418 1419 case B_TIME_TYPE: 1420 // time_t typedef'd to a long, i.e. a int32 1421 case B_INT32_TYPE: 1422 return fValue.int32t >= compareTo->fValue.int32t ? 1423 (fValue.int32t == compareTo->fValue.int32t ? 0 : 1) : -1; 1424 1425 case B_OFF_T_TYPE: 1426 // off_t typedef'd to a long long, i.e. a int64 1427 case B_INT64_TYPE: 1428 return fValue.int64t >= compareTo->fValue.int64t ? 1429 (fValue.int64t == compareTo->fValue.int64t ? 0 : 1) : -1; 1430 1431 case B_UINT64_TYPE: 1432 default: 1433 return fValue.uint64t >= compareTo->fValue.uint64t ? 1434 (fValue.uint64t == compareTo->fValue.uint64t ? 0 : 1) : -1; 1435 } 1436 return 0; 1437 } 1438 1439 1440 bool 1441 GenericAttributeText::CommitEditedText(BTextView *textView) 1442 { 1443 ASSERT(fColumn->Editable()); 1444 const char *text = textView->Text(); 1445 1446 if (fFullValueText == text) 1447 // no change 1448 return false; 1449 1450 if (!CommitEditedTextFlavor(textView)) 1451 return false; 1452 1453 // update text and width in this widget 1454 fFullValueText = text; 1455 // cause re-truncation 1456 fDirty = true; 1457 fValueDirty = true; 1458 1459 return true; 1460 } 1461 1462 1463 void 1464 GenericAttributeText::SetUpEditing(BTextView *textView) 1465 { 1466 textView->SetMaxBytes(kGenericReadBufferSize - 1); 1467 textView->SetText(fFullValueText.String(), fFullValueText.Length()); 1468 } 1469 1470 1471 bool 1472 GenericAttributeText::CommitEditedTextFlavor(BTextView *textView) 1473 { 1474 BNode node(fModel->EntryRef()); 1475 1476 if (node.InitCheck() != B_OK) 1477 return false; 1478 1479 uint32 type = fColumn->AttrType(); 1480 1481 if (type != B_STRING_TYPE 1482 && type != B_UINT64_TYPE 1483 && type != B_UINT32_TYPE 1484 && type != B_UINT16_TYPE 1485 && type != B_UINT8_TYPE 1486 && type != B_INT64_TYPE 1487 && type != B_INT32_TYPE 1488 && type != B_INT16_TYPE 1489 && type != B_INT8_TYPE 1490 && type != B_OFF_T_TYPE 1491 && type != B_TIME_TYPE 1492 && type != B_FLOAT_TYPE 1493 && type != B_DOUBLE_TYPE 1494 && type != B_CHAR_TYPE 1495 && type != B_BOOL_TYPE) { 1496 BAlert* alert = new BAlert("", 1497 B_TRANSLATE("Sorry, you cannot edit that attribute."), 1498 B_TRANSLATE("Cancel"), 1499 0, 0, B_WIDTH_AS_USUAL, B_STOP_ALERT); 1500 alert->SetShortcut(0, B_ESCAPE); 1501 alert->Go(); 1502 return false; 1503 } 1504 1505 const char *columnName = fColumn->AttrName(); 1506 ssize_t size = 0; 1507 1508 switch (type) { 1509 case B_STRING_TYPE: 1510 size = fModel->WriteAttr(columnName, 1511 type, 0, textView->Text(), (size_t)(textView->TextLength() + 1)); 1512 break; 1513 1514 case B_BOOL_TYPE: 1515 { 1516 bool value = strncasecmp(textView->Text(), "0", 1) != 0 1517 && strncasecmp(textView->Text(), "off", 2) != 0 1518 && strncasecmp(textView->Text(), "no", 3) != 0 1519 && strncasecmp(textView->Text(), "false", 4) != 0 1520 && strlen(textView->Text()) != 0; 1521 1522 size = fModel->WriteAttr(columnName, type, 0, &value, sizeof(bool)); 1523 break; 1524 } 1525 1526 case B_CHAR_TYPE: 1527 { 1528 char ch; 1529 sscanf(textView->Text(), "%c", &ch); 1530 //Check if we read the start of a multi-byte glyph: 1531 if (!isprint(ch)) { 1532 BAlert* alert = new BAlert("", 1533 B_TRANSLATE("Sorry, the 'Character' " 1534 "attribute cannot store a multi-byte glyph."), 1535 B_TRANSLATE("Cancel"), 1536 0, 0, B_WIDTH_AS_USUAL, B_STOP_ALERT); 1537 alert->SetShortcut(0, B_ESCAPE); 1538 alert->Go(); 1539 return false; 1540 } 1541 1542 size = fModel->WriteAttr(columnName, type, 0, &ch, sizeof(char)); 1543 break; 1544 } 1545 1546 case B_FLOAT_TYPE: 1547 { 1548 float floatVal; 1549 1550 if (sscanf(textView->Text(), "%f", &floatVal) == 1) { 1551 fValueIsDefined = true; 1552 fValue.floatt = floatVal; 1553 size = fModel->WriteAttr(columnName, type, 0, &floatVal, sizeof(float)); 1554 } else 1555 // If the value was already defined, it's on disk. Otherwise not. 1556 return fValueIsDefined; 1557 break; 1558 } 1559 1560 case B_DOUBLE_TYPE: 1561 { 1562 double doubleVal; 1563 1564 if (sscanf(textView->Text(), "%lf", &doubleVal) == 1) { 1565 fValueIsDefined = true; 1566 fValue.doublet = doubleVal; 1567 size = fModel->WriteAttr(columnName, type, 0, &doubleVal, sizeof(double)); 1568 } else 1569 // If the value was already defined, it's on disk. Otherwise not. 1570 return fValueIsDefined; 1571 break; 1572 } 1573 1574 case B_TIME_TYPE: 1575 case B_OFF_T_TYPE: 1576 case B_UINT64_TYPE: 1577 case B_UINT32_TYPE: 1578 case B_UINT16_TYPE: 1579 case B_UINT8_TYPE: 1580 case B_INT64_TYPE: 1581 case B_INT32_TYPE: 1582 case B_INT16_TYPE: 1583 case B_INT8_TYPE: 1584 { 1585 GenericValueStruct tmp; 1586 size_t scalarSize = 0; 1587 1588 switch (type) { 1589 case B_TIME_TYPE: 1590 tmp.time_tt = parsedate(textView->Text(), time(0)); 1591 scalarSize = sizeof(time_t); 1592 break; 1593 1594 // do some size independent conversion on builtin types 1595 case B_OFF_T_TYPE: 1596 tmp.off_tt = StringToScalar(textView->Text()); 1597 scalarSize = sizeof(off_t); 1598 break; 1599 1600 case B_UINT64_TYPE: 1601 case B_INT64_TYPE: 1602 tmp.int64t = StringToScalar(textView->Text()); 1603 scalarSize = sizeof(int64); 1604 break; 1605 1606 case B_UINT32_TYPE: 1607 case B_INT32_TYPE: 1608 tmp.int32t = (int32)StringToScalar(textView->Text()); 1609 scalarSize = sizeof(int32); 1610 break; 1611 1612 case B_UINT16_TYPE: 1613 case B_INT16_TYPE: 1614 tmp.int16t = (int16)StringToScalar(textView->Text()); 1615 scalarSize = sizeof(int16); 1616 break; 1617 1618 case B_UINT8_TYPE: 1619 case B_INT8_TYPE: 1620 tmp.int8t = (int8)StringToScalar(textView->Text()); 1621 scalarSize = sizeof(int8); 1622 break; 1623 1624 default: 1625 TRESPASS(); 1626 1627 } 1628 1629 size = fModel->WriteAttr(columnName, type, 0, &tmp, scalarSize); 1630 break; 1631 } 1632 } 1633 1634 if (size < 0) { 1635 BAlert* alert = new BAlert("", 1636 B_TRANSLATE("There was an error writing the attribute."), 1637 B_TRANSLATE("Cancel"), 1638 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 1639 alert->SetShortcut(0, B_ESCAPE); 1640 alert->Go(); 1641 1642 fValueIsDefined = false; 1643 return false; 1644 } 1645 1646 fValueIsDefined = true; 1647 return true; 1648 } 1649 1650 1651 // #pragma mark - 1652 1653 1654 OpenWithRelationAttributeText::OpenWithRelationAttributeText(const Model *model, 1655 const BColumn *column, const BPoseView *view) 1656 : ScalarAttributeText(model, column), 1657 fPoseView(view) 1658 { 1659 } 1660 1661 1662 int64 1663 OpenWithRelationAttributeText::ReadValue() 1664 { 1665 fValueDirty = false; 1666 1667 const OpenWithPoseView *view = dynamic_cast<const OpenWithPoseView *>(fPoseView); 1668 1669 if (view) { 1670 fValue = view->OpenWithRelation(fModel); 1671 fValueIsDefined = true; 1672 } 1673 1674 return fValue; 1675 } 1676 1677 1678 float 1679 OpenWithRelationAttributeText::PreferredWidth(const BPoseView *pose) const 1680 { 1681 BString widthString; 1682 TruncString(&widthString, fRelationText.String(), fRelationText.Length(), 1683 pose, 500, B_TRUNCATE_END); 1684 return pose->StringWidth(widthString.String()); 1685 } 1686 1687 1688 void 1689 OpenWithRelationAttributeText::FitValue(BString *result, const BPoseView *view) 1690 { 1691 if (fValueDirty) 1692 ReadValue(); 1693 1694 ASSERT(view == fPoseView); 1695 const OpenWithPoseView *launchWithView 1696 = dynamic_cast<const OpenWithPoseView *>(view); 1697 1698 if (launchWithView) 1699 launchWithView->OpenWithRelationDescription(fModel, &fRelationText); 1700 1701 fOldWidth = fColumn->Width(); 1702 fTruncatedWidth = TruncString(result, fRelationText.String(), 1703 fRelationText.Length(), view, fOldWidth, B_TRUNCATE_END); 1704 fDirty = false; 1705 } 1706 1707 1708 VersionAttributeText::VersionAttributeText(const Model *model, 1709 const BColumn *column, bool app) 1710 : StringAttributeText(model, column), 1711 fAppVersion(app) 1712 { 1713 } 1714 1715 1716 void 1717 VersionAttributeText::ReadValue(BString *result) 1718 { 1719 fValueDirty = false; 1720 1721 BModelOpener opener(fModel); 1722 BFile *file = dynamic_cast<BFile *>(fModel->Node()); 1723 if (file) { 1724 BAppFileInfo info(file); 1725 version_info version; 1726 if (info.InitCheck() == B_OK 1727 && info.GetVersionInfo(&version, 1728 fAppVersion ? B_APP_VERSION_KIND : B_SYSTEM_VERSION_KIND) == B_OK) { 1729 *result = version.short_info; 1730 return; 1731 } 1732 } 1733 *result = "-"; 1734 } 1735 1736