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