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 = "TB"; 226 floatValue = (float)value / kTBSize; 227 } else if (value >= kGBSize) { 228 suffix = "GB"; 229 floatValue = (float)value / kGBSize; 230 } else if (value >= kMBSize) { 231 suffix = "MB"; 232 floatValue = (float)value / kMBSize; 233 } else { 234 ASSERT(value >= kKBSize); 235 suffix = "KB"; 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(), compareTo->ValueAsText(view)); 598 } 599 600 601 bool 602 StringAttributeText::CommitEditedText(BTextView *textView) 603 { 604 ASSERT(fColumn->Editable()); 605 const char *text = textView->Text(); 606 607 if (fFullValueText == text) 608 // no change 609 return false; 610 611 if (textView->TextLength() == 0) 612 // cannot do an empty name 613 return false; 614 615 // cause re-truncation 616 fDirty = true; 617 618 if (!CommitEditedTextFlavor(textView)) 619 return false; 620 621 // update text and width in this widget 622 fFullValueText = text; 623 624 return true; 625 } 626 627 628 // #pragma mark - 629 630 631 ScalarAttributeText::ScalarAttributeText(const Model *model, 632 const BColumn *column) 633 : WidgetAttributeText(model, column), 634 fValueDirty(true) 635 { 636 } 637 638 639 int64 640 ScalarAttributeText::Value() 641 { 642 if (fValueDirty) 643 fValue = ReadValue(); 644 return fValue; 645 } 646 647 648 bool 649 ScalarAttributeText::CheckAttributeChanged() 650 { 651 int64 newValue = ReadValue(); 652 if (newValue == fValue) 653 return false; 654 655 fValue = newValue; 656 fDirty = true; // have to redo fitted string 657 return true; 658 } 659 660 661 float 662 ScalarAttributeText::PreferredWidth(const BPoseView *pose) const 663 { 664 BString widthString; 665 widthString << fValue; 666 return pose->StringWidth(widthString.String()); 667 } 668 669 670 int 671 ScalarAttributeText::Compare(WidgetAttributeText &attr, BPoseView *) 672 { 673 ScalarAttributeText *compareTo 674 = dynamic_cast<ScalarAttributeText *>(&attr); 675 ASSERT(compareTo); 676 // make sure we're not comparing apples and oranges 677 678 if (fValueDirty) 679 fValue = ReadValue(); 680 681 return fValue >= compareTo->Value() ? (fValue == compareTo->Value() ? 0 : -1) : 1 ; 682 } 683 684 685 // #pragma mark - 686 687 688 PathAttributeText::PathAttributeText(const Model *model, const BColumn *column) 689 : StringAttributeText(model, column) 690 { 691 } 692 693 694 void 695 PathAttributeText::ReadValue(BString *result) 696 { 697 // get the path 698 BEntry entry(fModel->EntryRef()); 699 BPath path; 700 701 if (entry.InitCheck() == B_OK && entry.GetPath(&path) == B_OK) { 702 *result = path.Path(); 703 TruncateLeaf(result); 704 } else 705 *result = "-"; 706 fValueDirty = false; 707 } 708 709 710 // #pragma mark - 711 712 713 OriginalPathAttributeText::OriginalPathAttributeText(const Model *model, 714 const BColumn *column) 715 : StringAttributeText(model, column) 716 { 717 } 718 719 720 void 721 OriginalPathAttributeText::ReadValue(BString *result) 722 { 723 BEntry entry(fModel->EntryRef()); 724 BPath path; 725 726 // get the original path 727 if (entry.InitCheck() == B_OK && FSGetOriginalPath(&entry, &path) == B_OK) 728 *result = path.Path(); 729 else 730 *result = "-"; 731 fValueDirty = false; 732 } 733 734 735 // #pragma mark - 736 737 738 KindAttributeText::KindAttributeText(const Model *model, const BColumn *column) 739 : StringAttributeText(model, column) 740 { 741 } 742 743 744 void 745 KindAttributeText::ReadValue(BString *result) 746 { 747 BMimeType mime; 748 char desc[B_MIME_TYPE_LENGTH]; 749 750 // get the mime type 751 if (mime.SetType(fModel->MimeType()) != B_OK) 752 *result = "Unknown"; 753 // get the short mime type description 754 else if (mime.GetShortDescription(desc) == B_OK) 755 *result = desc; 756 else 757 *result = fModel->MimeType(); 758 fValueDirty = false; 759 } 760 761 762 // #pragma mark - 763 764 765 NameAttributeText::NameAttributeText(const Model *model, const BColumn *column) 766 : StringAttributeText(model, column) 767 { 768 } 769 770 771 int 772 NameAttributeText::Compare(WidgetAttributeText &attr, BPoseView *view) 773 { 774 NameAttributeText *compareTo = dynamic_cast<NameAttributeText *>(&attr); 775 776 ASSERT(compareTo); 777 778 if (fValueDirty) 779 ReadValue(&fFullValueText); 780 781 if (NameAttributeText::sSortFolderNamesFirst) 782 return fModel->CompareFolderNamesFirst(attr.TargetModel()); 783 784 return NaturalCompare(fFullValueText.String(), compareTo->ValueAsText(view)); 785 } 786 787 788 void 789 NameAttributeText::ReadValue(BString *result) 790 { 791 #ifdef DEBUG 792 // x86 support :-) 793 if ((modifiers() & B_CAPS_LOCK) != 0) { 794 if (fModel->IsVolume()) { 795 BVolumeRoster roster; 796 roster.Rewind(); 797 BVolume volume; 798 char device = 'A'; 799 while (roster.GetNextVolume(&volume) == B_OK) { 800 char name[256]; 801 if (volume.GetName(name) == B_OK 802 && strcmp(name, fModel->Name()) == 0) { 803 *result += device; 804 *result += ':'; 805 fValueDirty = false; 806 return; 807 } 808 device++; 809 } 810 } 811 const char *modelName = fModel->Name(); 812 bool hasDot = strstr(".", modelName) != 0; 813 for (int32 index = 0; index < 8; index++) { 814 if (!modelName[index] || modelName[index] == '.') 815 break; 816 *result += toupper(modelName[index]); 817 } 818 if (hasDot) { 819 modelName = strstr(".", modelName); 820 for (int32 index = 0; index < 4; index++) { 821 if (!modelName[index]) 822 break; 823 *result += toupper(modelName[index]); 824 } 825 } else if (fModel->IsExecutable()) 826 *result += ".EXE"; 827 828 } else 829 #endif 830 *result = fModel->Name(); 831 832 fValueDirty = false; 833 } 834 835 836 void 837 NameAttributeText::FitValue(BString *result, const BPoseView *view) 838 { 839 if (fValueDirty) 840 ReadValue(&fFullValueText); 841 fOldWidth = fColumn->Width(); 842 fTruncatedWidth = TruncString(result, fFullValueText.String(), 843 fFullValueText.Length(), view, fOldWidth, B_TRUNCATE_END); 844 fDirty = false; 845 } 846 847 848 void 849 NameAttributeText::SetUpEditing(BTextView *textView) 850 { 851 DisallowFilenameKeys(textView); 852 853 textView->SetMaxBytes(B_FILE_NAME_LENGTH); 854 textView->SetText(fFullValueText.String(), fFullValueText.Length()); 855 } 856 857 858 bool 859 NameAttributeText::CommitEditedTextFlavor(BTextView *textView) 860 { 861 const char *text = textView->Text(); 862 863 BEntry entry(fModel->EntryRef()); 864 if (entry.InitCheck() != B_OK) 865 return false; 866 867 BDirectory parent; 868 if (entry.GetParent(&parent) != B_OK) 869 return false; 870 871 bool removeExisting = false; 872 if (parent.Contains(text)) { 873 BAlert *alert = new BAlert("", "That name is already taken. " 874 "Please type another one.", "Replace other file", "OK", NULL, 875 B_WIDTH_AS_USUAL, B_WARNING_ALERT); 876 877 alert->SetShortcut(0, 'r'); 878 879 if (alert->Go()) 880 return false; 881 882 removeExisting = true; 883 } 884 885 // ToDo: 886 // use model-flavor specific virtuals for all of these special 887 // renamings 888 status_t result; 889 if (fModel->IsVolume()) { 890 BVolume volume(fModel->NodeRef()->device); 891 result = volume.InitCheck(); 892 if (result == B_OK) { 893 RenameVolumeUndo undo(volume, text); 894 895 result = volume.SetName(text); 896 if (result != B_OK) 897 undo.Remove(); 898 } 899 } else { 900 if (fModel->IsQuery()) { 901 BModelWriteOpener opener(fModel); 902 ASSERT(fModel->Node()); 903 MoreOptionsStruct::SetQueryTemporary(fModel->Node(), false); 904 } 905 906 RenameUndo undo(entry, text); 907 908 result = entry.Rename(text, removeExisting); 909 if (result != B_OK) 910 undo.Remove(); 911 } 912 913 return result == B_OK; 914 } 915 916 bool NameAttributeText::sSortFolderNamesFirst = false; 917 918 void 919 NameAttributeText::SetSortFolderNamesFirst(bool enabled) 920 { 921 NameAttributeText::sSortFolderNamesFirst = enabled; 922 } 923 924 925 // #pragma mark - 926 927 #ifdef OWNER_GROUP_ATTRIBUTES 928 929 OwnerAttributeText::OwnerAttributeText(const Model *model, 930 const BColumn *column) 931 : StringAttributeText(model, column) 932 { 933 } 934 935 936 void 937 OwnerAttributeText::ReadValue(BString *result) 938 { 939 uid_t nodeOwner = fModel->StatBuf()->st_uid; 940 BString user; 941 942 if (nodeOwner == 0) { 943 if (getenv("USER") != NULL) 944 user << getenv("USER"); 945 else 946 user << "root"; 947 } else 948 user << nodeOwner; 949 *result = user.String(); 950 951 fValueDirty = false; 952 } 953 954 955 GroupAttributeText::GroupAttributeText(const Model *model, 956 const BColumn *column) 957 : StringAttributeText(model, column) 958 { 959 } 960 961 962 void 963 GroupAttributeText::ReadValue(BString *result) 964 { 965 gid_t nodeGroup = fModel->StatBuf()->st_gid; 966 BString group; 967 968 if (nodeGroup == 0) { 969 if (getenv("GROUP") != NULL) 970 group << getenv("GROUP"); 971 else 972 group << "0"; 973 } else 974 group << nodeGroup; 975 *result = group.String(); 976 977 fValueDirty = false; 978 } 979 980 #endif /* OWNER_GROUP_ATTRIBUTES */ 981 982 ModeAttributeText::ModeAttributeText(const Model *model, const BColumn *column) 983 : StringAttributeText(model, column) 984 { 985 } 986 987 988 void 989 ModeAttributeText::ReadValue(BString *result) 990 { 991 mode_t mode = fModel->StatBuf()->st_mode; 992 mode_t baseMask = 00400; 993 char buffer[11]; 994 995 char *scanner = buffer; 996 997 if (S_ISDIR(mode)) 998 *scanner++ = 'd'; 999 else if (S_ISLNK(mode)) 1000 *scanner++ = 'l'; 1001 else if (S_ISBLK(mode)) 1002 *scanner++ = 'b'; 1003 else if (S_ISCHR(mode)) 1004 *scanner++ = 'c'; 1005 else 1006 *scanner++ = '-'; 1007 1008 for (int32 index = 0; index < 9; index++) { 1009 *scanner++ = (mode & baseMask) ? "rwx"[index % 3] : '-'; 1010 baseMask >>= 1; 1011 } 1012 1013 *scanner = 0; 1014 *result = buffer; 1015 1016 fValueDirty = false; 1017 } 1018 1019 1020 // #pragma mark - 1021 1022 1023 SizeAttributeText::SizeAttributeText(const Model *model, const BColumn *column) 1024 : ScalarAttributeText(model, column) 1025 { 1026 } 1027 1028 1029 int64 1030 SizeAttributeText::ReadValue() 1031 { 1032 fValueDirty = false; 1033 // get the size 1034 1035 if (fModel->IsVolume()) { 1036 BVolume volume(fModel->NodeRef()->device); 1037 1038 return volume.Capacity(); 1039 } 1040 1041 if (fModel->IsDirectory() || fModel->IsQuery() 1042 || fModel->IsQueryTemplate() || fModel->IsSymLink()) 1043 return kUnknownSize; 1044 1045 fValueIsDefined = true; 1046 1047 return fModel->StatBuf()->st_size; 1048 } 1049 1050 1051 void 1052 SizeAttributeText::FitValue(BString *result, const BPoseView *view) 1053 { 1054 if (fValueDirty) 1055 fValue = ReadValue(); 1056 fOldWidth = fColumn->Width(); 1057 fTruncatedWidth = TruncFileSize(result, fValue, view, fOldWidth); 1058 fDirty = false; 1059 } 1060 1061 1062 float 1063 SizeAttributeText::PreferredWidth(const BPoseView *pose) const 1064 { 1065 if (fValueIsDefined) { 1066 BString widthString; 1067 TruncFileSize(&widthString, fValue, pose, 100000); 1068 return pose->StringWidth(widthString.String()); 1069 } 1070 return pose->StringWidth("-"); 1071 } 1072 1073 1074 // #pragma mark - 1075 1076 1077 TimeAttributeText::TimeAttributeText(const Model *model, const BColumn *column) 1078 : ScalarAttributeText(model, column) 1079 { 1080 } 1081 1082 1083 float 1084 TimeAttributeText::PreferredWidth(const BPoseView *pose) const 1085 { 1086 BString widthString; 1087 TruncTimeBase(&widthString, fValue, pose, 100000); 1088 return pose->StringWidth(widthString.String()); 1089 } 1090 1091 1092 void 1093 TimeAttributeText::FitValue(BString *result, const BPoseView *view) 1094 { 1095 if (fValueDirty) 1096 fValue = ReadValue(); 1097 fOldWidth = fColumn->Width(); 1098 fTruncatedWidth = TruncTime(result, fValue, view, fOldWidth); 1099 fDirty = false; 1100 } 1101 1102 1103 bool 1104 TimeAttributeText::CheckSettingsChanged() 1105 { 1106 bool changed = fLastClockIs24 != fSettings.ClockIs24Hr() 1107 || fLastDateOrder != fSettings.DateOrderFormat() 1108 || fLastTimeFormatSeparator != fSettings.TimeFormatSeparator(); 1109 1110 if (changed) { 1111 fLastClockIs24 = fSettings.ClockIs24Hr(); 1112 fLastDateOrder = fSettings.DateOrderFormat(); 1113 fLastTimeFormatSeparator = fSettings.TimeFormatSeparator(); 1114 } 1115 1116 return changed; 1117 } 1118 1119 1120 CreationTimeAttributeText::CreationTimeAttributeText(const Model *model, 1121 const BColumn *column) 1122 : TimeAttributeText(model, column) 1123 { 1124 } 1125 1126 1127 int64 1128 CreationTimeAttributeText::ReadValue() 1129 { 1130 fValueDirty = false; 1131 fValueIsDefined = true; 1132 return fModel->StatBuf()->st_crtime; 1133 } 1134 1135 ModificationTimeAttributeText::ModificationTimeAttributeText(const Model *model, 1136 const BColumn *column) 1137 : TimeAttributeText(model, column) 1138 { 1139 } 1140 1141 1142 int64 1143 ModificationTimeAttributeText::ReadValue() 1144 { 1145 fValueDirty = false; 1146 fValueIsDefined = true; 1147 return fModel->StatBuf()->st_mtime; 1148 } 1149 1150 1151 // #pragma mark - 1152 1153 1154 GenericAttributeText::GenericAttributeText(const Model *model, 1155 const BColumn *column) 1156 : StringAttributeText(model, column) 1157 { 1158 } 1159 1160 1161 bool 1162 GenericAttributeText::CheckAttributeChanged() 1163 { 1164 GenericValueStruct tmpValue = fValue; 1165 BString tmpString(fFullValueText); 1166 ReadValue(&fFullValueText); 1167 1168 // fDirty could already be true, in that case we mustn't set it to 1169 // false, even if the attribute text hasn't changed 1170 bool changed = (fValue.int64t != tmpValue.int64t) || (tmpString != fFullValueText); 1171 if (changed) 1172 fDirty = true; 1173 1174 return fDirty; 1175 } 1176 1177 1178 float 1179 GenericAttributeText::PreferredWidth(const BPoseView *pose) const 1180 { 1181 return pose->StringWidth(fFullValueText.String()); 1182 } 1183 1184 1185 void 1186 GenericAttributeText::ReadValue(BString *result) 1187 { 1188 BModelOpener opener(const_cast<Model *>(fModel)); 1189 1190 ssize_t length = 0; 1191 fFullValueText = "-"; 1192 fValue.int64t = 0; 1193 fValueIsDefined = false; 1194 fValueDirty = false; 1195 1196 if (!fModel->Node()) 1197 return; 1198 1199 switch (fColumn->AttrType()) { 1200 case B_STRING_TYPE: 1201 { 1202 char buffer[kGenericReadBufferSize]; 1203 length = fModel->Node()->ReadAttr(fColumn->AttrName(), 1204 fColumn->AttrType(), 0, buffer, kGenericReadBufferSize - 1); 1205 1206 if (length > 0) { 1207 buffer[length] = '\0'; 1208 // make sure the buffer is null-terminated even if we 1209 // didn't read the whole attribute in or it wasn't to 1210 // begin with 1211 1212 *result = buffer; 1213 fValueIsDefined = true; 1214 } 1215 break; 1216 } 1217 1218 case B_SSIZE_T_TYPE: 1219 case B_TIME_TYPE: 1220 case B_OFF_T_TYPE: 1221 case B_FLOAT_TYPE: 1222 case B_BOOL_TYPE: 1223 case B_CHAR_TYPE: 1224 case B_INT8_TYPE: 1225 case B_INT16_TYPE: 1226 case B_INT32_TYPE: 1227 case B_INT64_TYPE: 1228 case B_UINT8_TYPE: 1229 case B_UINT16_TYPE: 1230 case B_UINT32_TYPE: 1231 case B_UINT64_TYPE: 1232 case B_DOUBLE_TYPE: 1233 { 1234 // read in the numerical bit representation and attach it 1235 // with a type, depending on the bytes that could be read 1236 attr_info info; 1237 GenericValueStruct tmp; 1238 if (fModel->Node()->GetAttrInfo(fColumn->AttrName(), &info) == B_OK) { 1239 if (info.size && info.size <= sizeof(int64)) { 1240 length = fModel->Node()->ReadAttr(fColumn->AttrName(), 1241 fColumn->AttrType(), 0, &tmp, (size_t)info.size); 1242 } 1243 1244 // We used tmp as a block of memory, now set the correct fValue: 1245 1246 if (length == info.size) { 1247 if (fColumn->AttrType() == B_FLOAT_TYPE 1248 || fColumn->AttrType() == B_DOUBLE_TYPE) { 1249 // filter out special float/double types 1250 switch (info.size) { 1251 case sizeof(float): 1252 fValueIsDefined = true; 1253 fValue.floatt = tmp.floatt; 1254 break; 1255 1256 case sizeof(double): 1257 fValueIsDefined = true; 1258 fValue.doublet = tmp.doublet; 1259 break; 1260 1261 default: 1262 TRESPASS(); 1263 } 1264 } else { 1265 // handle the standard data types 1266 switch (info.size) { 1267 case sizeof(char): // Takes care of bool, too. 1268 fValueIsDefined = true; 1269 fValue.int8t = tmp.int8t; 1270 break; 1271 1272 case sizeof(int16): 1273 fValueIsDefined = true; 1274 fValue.int16t = tmp.int16t; 1275 break; 1276 1277 case sizeof(int32): // Takes care of time_t, too. 1278 fValueIsDefined = true; 1279 fValue.int32t = tmp.int32t; 1280 break; 1281 1282 case sizeof(int64): // Taked care of off_t, too. 1283 fValueIsDefined = true; 1284 fValue.int64t = tmp.int64t; 1285 break; 1286 1287 default: 1288 TRESPASS(); 1289 } 1290 } 1291 } 1292 } 1293 break; 1294 } 1295 } 1296 } 1297 1298 1299 void 1300 GenericAttributeText::FitValue(BString *result, const BPoseView *view) 1301 { 1302 if (fValueDirty) 1303 ReadValue(&fFullValueText); 1304 1305 fOldWidth = fColumn->Width(); 1306 1307 if (!fValueIsDefined) { 1308 *result = "-"; 1309 fTruncatedWidth = TruncString(result, fFullValueText.String(), 1310 fFullValueText.Length(), view, fOldWidth); 1311 fDirty = false; 1312 return; 1313 } 1314 1315 char buffer[256]; 1316 1317 switch (fColumn->AttrType()) { 1318 case B_SIZE_T_TYPE: 1319 TruncFileSizeBase(result, fValue.int32t, view, fOldWidth); 1320 return; 1321 1322 case B_SSIZE_T_TYPE: 1323 if (fValue.int32t > 0) { 1324 TruncFileSizeBase(result, fValue.int32t, view, fOldWidth); 1325 return; 1326 } 1327 sprintf(buffer, "%s", strerror(fValue.int32t)); 1328 fFullValueText = buffer; 1329 break; 1330 1331 case B_STRING_TYPE: 1332 fTruncatedWidth = TruncString(result, fFullValueText.String(), 1333 fFullValueText.Length(), view, fOldWidth); 1334 fDirty = false; 1335 return; 1336 1337 case B_OFF_T_TYPE: 1338 // as a side effect update the fFullValueText to the string representation 1339 // of value 1340 TruncFileSize(&fFullValueText, fValue.off_tt, view, 100000); 1341 fTruncatedWidth = TruncFileSize(result, fValue.off_tt, view, fOldWidth); 1342 fDirty = false; 1343 return; 1344 1345 case B_TIME_TYPE: 1346 // as a side effect update the fFullValueText to the string representation 1347 // of value 1348 TruncTime(&fFullValueText, fValue.time_tt, view, 100000); 1349 fTruncatedWidth = TruncTime(result, fValue.time_tt, view, fOldWidth); 1350 fDirty = false; 1351 return; 1352 1353 case B_BOOL_TYPE: 1354 // For now use true/false, would be nice to be able to set 1355 // the value text 1356 1357 sprintf(buffer, "%s", fValue.boolt ? "true" : "false"); 1358 fFullValueText = buffer; 1359 break; 1360 1361 case B_CHAR_TYPE: 1362 // Make sure no non-printable characters are displayed: 1363 if (!isprint(fValue.uint8t)) { 1364 *result = "-"; 1365 fTruncatedWidth = TruncString(result, fFullValueText.String(), 1366 fFullValueText.Length(), view, fOldWidth); 1367 fDirty = false; 1368 return; 1369 } 1370 1371 sprintf(buffer, "%c", fValue.uint8t); 1372 fFullValueText = buffer; 1373 break; 1374 1375 case B_INT8_TYPE: 1376 sprintf(buffer, "%d", fValue.int8t); 1377 fFullValueText = buffer; 1378 break; 1379 1380 case B_UINT8_TYPE: 1381 sprintf(buffer, "%d", fValue.uint8t); 1382 fFullValueText = buffer; 1383 break; 1384 1385 case B_INT16_TYPE: 1386 sprintf(buffer, "%d", fValue.int16t); 1387 fFullValueText = buffer; 1388 break; 1389 1390 case B_UINT16_TYPE: 1391 sprintf(buffer, "%d", fValue.uint16t); 1392 fFullValueText = buffer; 1393 break; 1394 1395 case B_INT32_TYPE: 1396 sprintf(buffer, "%ld", fValue.int32t); 1397 fFullValueText = buffer; 1398 break; 1399 1400 case B_UINT32_TYPE: 1401 sprintf(buffer, "%ld", fValue.uint32t); 1402 fFullValueText = buffer; 1403 break; 1404 1405 case B_INT64_TYPE: 1406 sprintf(buffer, "%Ld", fValue.int64t); 1407 fFullValueText = buffer; 1408 break; 1409 1410 case B_UINT64_TYPE: 1411 sprintf(buffer, "%Ld", fValue.uint64t); 1412 fFullValueText = buffer; 1413 break; 1414 1415 case B_FLOAT_TYPE: 1416 if (fabs(fValue.floatt) >= 10000 1417 || fabs(fValue.floatt) < 0.01) 1418 // The %f conversion can possibly overflow 'buffer' if the value is 1419 // too big, since it doesn't print in exponent form. Ever. 1420 sprintf(buffer, "%.3e", fValue.floatt); 1421 else 1422 sprintf(buffer, "%.3f", fValue.floatt); 1423 fFullValueText = buffer; 1424 break; 1425 1426 case B_DOUBLE_TYPE: 1427 if (fabs(fValue.doublet) >= 10000 1428 || fabs(fValue.doublet) < 0.01) 1429 // The %f conversion can possibly overflow 'buffer' if the value is 1430 // too big, since it doesn't print in exponent form. Ever. 1431 sprintf(buffer, "%.3e", fValue.doublet); 1432 else 1433 sprintf(buffer, "%.3f", fValue.doublet); 1434 fFullValueText = buffer; 1435 break; 1436 1437 default: 1438 *result = "-"; 1439 fTruncatedWidth = TruncString(result, fFullValueText.String(), 1440 fFullValueText.Length(), view, fOldWidth); 1441 fDirty = false; 1442 return; 1443 } 1444 fTruncatedWidth = TruncString(result, buffer, (ssize_t)strlen(buffer), view, 1445 fOldWidth); 1446 fDirty = false; 1447 } 1448 1449 1450 const char* 1451 GenericAttributeText::ValueAsText(const BPoseView *view) 1452 { 1453 // TODO: redesign this - this is to make sure the value is valid 1454 bool oldDirty = fDirty; 1455 BString result; 1456 FitValue(&result, view); 1457 fDirty = oldDirty; 1458 1459 return fFullValueText.String(); 1460 } 1461 1462 1463 int 1464 GenericAttributeText::Compare(WidgetAttributeText &attr, BPoseView *) 1465 { 1466 GenericAttributeText *compareTo 1467 = dynamic_cast<GenericAttributeText *>(&attr); 1468 ASSERT(compareTo); 1469 1470 if (fValueDirty) 1471 ReadValue(&fFullValueText); 1472 if (compareTo->fValueDirty) 1473 compareTo->ReadValue(&compareTo->fFullValueText); 1474 1475 // Sort undefined values last, regardless of the other value: 1476 if (!fValueIsDefined) 1477 return compareTo->fValueIsDefined ? -1 : 0; 1478 if (!compareTo->fValueIsDefined) 1479 return 1; 1480 1481 switch (fColumn->AttrType()) { 1482 case B_STRING_TYPE: 1483 return fFullValueText.ICompare(compareTo->fFullValueText); 1484 1485 case B_CHAR_TYPE: 1486 { 1487 char vStr[2] = { static_cast<char>(fValue.uint8t), 0 }; 1488 char cStr[2] = { static_cast<char>(compareTo->fValue.uint8t), 0}; 1489 1490 BString valueStr(vStr); 1491 BString compareToStr(cStr); 1492 1493 return valueStr.ICompare(compareToStr); 1494 } 1495 1496 case B_FLOAT_TYPE: 1497 return fValue.floatt >= compareTo->fValue.floatt ? 1498 (fValue.floatt == compareTo->fValue.floatt ? 0 : -1) : 1; 1499 1500 case B_DOUBLE_TYPE: 1501 return fValue.doublet >= compareTo->fValue.doublet ? 1502 (fValue.doublet == compareTo->fValue.doublet ? 0 : -1) : 1; 1503 1504 case B_BOOL_TYPE: 1505 return fValue.boolt >= compareTo->fValue.boolt ? 1506 (fValue.boolt == compareTo->fValue.boolt ? 0 : -1) : 1; 1507 1508 case B_UINT8_TYPE: 1509 return fValue.uint8t >= compareTo->fValue.uint8t ? 1510 (fValue.uint8t == compareTo->fValue.uint8t ? 0 : -1) : 1; 1511 1512 case B_INT8_TYPE: 1513 return fValue.int8t >= compareTo->fValue.int8t ? 1514 (fValue.int8t == compareTo->fValue.int8t ? 0 : -1) : 1; 1515 1516 case B_UINT16_TYPE: 1517 return fValue.uint16t >= compareTo->fValue.uint16t ? 1518 (fValue.uint16t == compareTo->fValue.uint16t ? 0 : -1) : 1; 1519 1520 case B_INT16_TYPE: 1521 return fValue.int16t >= compareTo->fValue.int16t ? 1522 (fValue.int16t == compareTo->fValue.int16t ? 0 : -1) : 1; 1523 1524 case B_UINT32_TYPE: 1525 return fValue.uint32t >= compareTo->fValue.uint32t ? 1526 (fValue.uint32t == compareTo->fValue.uint32t ? 0 : -1) : 1; 1527 1528 case B_TIME_TYPE: 1529 // time_t typedef'd to a long, i.e. a int32 1530 case B_INT32_TYPE: 1531 return fValue.int32t >= compareTo->fValue.int32t ? 1532 (fValue.int32t == compareTo->fValue.int32t ? 0 : -1) : 1; 1533 1534 case B_OFF_T_TYPE: 1535 // off_t typedef'd to a long long, i.e. a int64 1536 case B_INT64_TYPE: 1537 return fValue.int64t >= compareTo->fValue.int64t ? 1538 (fValue.int64t == compareTo->fValue.int64t ? 0 : -1) : 1; 1539 1540 case B_UINT64_TYPE: 1541 default: 1542 return fValue.uint64t >= compareTo->fValue.uint64t ? 1543 (fValue.uint64t == compareTo->fValue.uint64t ? 0 : -1) : 1; 1544 } 1545 return 0; 1546 } 1547 1548 1549 bool 1550 GenericAttributeText::CommitEditedText(BTextView *textView) 1551 { 1552 ASSERT(fColumn->Editable()); 1553 const char *text = textView->Text(); 1554 1555 if (fFullValueText == text) 1556 // no change 1557 return false; 1558 1559 if (!CommitEditedTextFlavor(textView)) 1560 return false; 1561 1562 // update text and width in this widget 1563 fFullValueText = text; 1564 // cause re-truncation 1565 fDirty = true; 1566 fValueDirty = true; 1567 1568 return true; 1569 } 1570 1571 1572 void 1573 GenericAttributeText::SetUpEditing(BTextView *textView) 1574 { 1575 textView->SetMaxBytes(kGenericReadBufferSize - 1); 1576 textView->SetText(fFullValueText.String(), fFullValueText.Length()); 1577 } 1578 1579 1580 bool 1581 GenericAttributeText::CommitEditedTextFlavor(BTextView *textView) 1582 { 1583 BNode node(fModel->EntryRef()); 1584 1585 if (node.InitCheck() != B_OK) 1586 return false; 1587 1588 uint32 type = fColumn->AttrType(); 1589 1590 if (type != B_STRING_TYPE 1591 && type != B_UINT64_TYPE 1592 && type != B_UINT32_TYPE 1593 && type != B_UINT16_TYPE 1594 && type != B_UINT8_TYPE 1595 && type != B_INT64_TYPE 1596 && type != B_INT32_TYPE 1597 && type != B_INT16_TYPE 1598 && type != B_INT8_TYPE 1599 && type != B_OFF_T_TYPE 1600 && type != B_TIME_TYPE 1601 && type != B_FLOAT_TYPE 1602 && type != B_DOUBLE_TYPE 1603 && type != B_CHAR_TYPE 1604 && type != B_BOOL_TYPE) { 1605 BAlert *alert = new BAlert("", "Sorry, you cannot edit that attribute.", 1606 "Cancel", 0, 0, B_WIDTH_AS_USUAL, B_STOP_ALERT); 1607 alert->SetShortcut(0, B_ESCAPE); 1608 alert->Go(); 1609 return false; 1610 } 1611 1612 const char *columnName = fColumn->AttrName(); 1613 ssize_t size = 0; 1614 1615 switch (type) { 1616 case B_STRING_TYPE: 1617 size = fModel->WriteAttr(columnName, 1618 type, 0, textView->Text(), (size_t)(textView->TextLength() + 1)); 1619 break; 1620 1621 case B_BOOL_TYPE: 1622 { 1623 bool value = strncasecmp(textView->Text(), "0", 1) != 0 1624 && strncasecmp(textView->Text(), "off", 2) != 0 1625 && strncasecmp(textView->Text(), "no", 3) != 0 1626 && strncasecmp(textView->Text(), "false", 4) != 0 1627 && strlen(textView->Text()) != 0; 1628 1629 size = fModel->WriteAttr(columnName, type, 0, &value, sizeof(bool)); 1630 break; 1631 } 1632 1633 case B_CHAR_TYPE: 1634 { 1635 char ch; 1636 sscanf(textView->Text(), "%c", &ch); 1637 //Check if we read the start of a multi-byte glyph: 1638 if (!isprint(ch)) { 1639 BAlert *alert = new BAlert("", "Sorry, The 'Character' " 1640 "attribute cannot store a multi-byte glyph.", 1641 "Cancel", 0, 0, B_WIDTH_AS_USUAL, B_STOP_ALERT); 1642 alert->SetShortcut(0, B_ESCAPE); 1643 alert->Go(); 1644 return false; 1645 } 1646 1647 size = fModel->WriteAttr(columnName, type, 0, &ch, sizeof(char)); 1648 break; 1649 } 1650 1651 case B_FLOAT_TYPE: 1652 { 1653 float floatVal; 1654 1655 if (sscanf(textView->Text(), "%f", &floatVal) == 1) { 1656 fValueIsDefined = true; 1657 fValue.floatt = floatVal; 1658 size = fModel->WriteAttr(columnName, type, 0, &floatVal, sizeof(float)); 1659 } else 1660 // If the value was already defined, it's on disk. Otherwise not. 1661 return fValueIsDefined; 1662 break; 1663 } 1664 1665 case B_DOUBLE_TYPE: 1666 { 1667 double doubleVal; 1668 1669 if (sscanf(textView->Text(), "%lf", &doubleVal) == 1) { 1670 fValueIsDefined = true; 1671 fValue.doublet = doubleVal; 1672 size = fModel->WriteAttr(columnName, type, 0, &doubleVal, sizeof(double)); 1673 } else 1674 // If the value was already defined, it's on disk. Otherwise not. 1675 return fValueIsDefined; 1676 break; 1677 } 1678 1679 case B_TIME_TYPE: 1680 case B_OFF_T_TYPE: 1681 case B_UINT64_TYPE: 1682 case B_UINT32_TYPE: 1683 case B_UINT16_TYPE: 1684 case B_UINT8_TYPE: 1685 case B_INT64_TYPE: 1686 case B_INT32_TYPE: 1687 case B_INT16_TYPE: 1688 case B_INT8_TYPE: 1689 { 1690 GenericValueStruct tmp; 1691 size_t scalarSize = 0; 1692 1693 switch (type) { 1694 case B_TIME_TYPE: 1695 tmp.time_tt = parsedate(textView->Text(), time(0)); 1696 scalarSize = sizeof(time_t); 1697 break; 1698 1699 // do some size independent conversion on builtin types 1700 case B_OFF_T_TYPE: 1701 tmp.off_tt = StringToScalar(textView->Text()); 1702 scalarSize = sizeof(off_t); 1703 break; 1704 1705 case B_UINT64_TYPE: 1706 case B_INT64_TYPE: 1707 tmp.int64t = StringToScalar(textView->Text()); 1708 scalarSize = sizeof(int64); 1709 break; 1710 1711 case B_UINT32_TYPE: 1712 case B_INT32_TYPE: 1713 tmp.int32t = (int32)StringToScalar(textView->Text()); 1714 scalarSize = sizeof(int32); 1715 break; 1716 1717 case B_UINT16_TYPE: 1718 case B_INT16_TYPE: 1719 tmp.int16t = (int16)StringToScalar(textView->Text()); 1720 scalarSize = sizeof(int16); 1721 break; 1722 1723 case B_UINT8_TYPE: 1724 case B_INT8_TYPE: 1725 tmp.int8t = (int8)StringToScalar(textView->Text()); 1726 scalarSize = sizeof(int8); 1727 break; 1728 1729 default: 1730 TRESPASS(); 1731 1732 } 1733 1734 size = fModel->WriteAttr(columnName, type, 0, &tmp, scalarSize); 1735 break; 1736 } 1737 } 1738 1739 if (size < 0) { 1740 BAlert *alert = new BAlert("", 1741 "There was an error writing the attribute.", 1742 "Cancel", 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 1743 alert->SetShortcut(0, B_CANCEL); 1744 alert->Go(); 1745 1746 fValueIsDefined = false; 1747 return false; 1748 } 1749 1750 fValueIsDefined = true; 1751 return true; 1752 } 1753 1754 1755 // #pragma mark - 1756 1757 1758 OpenWithRelationAttributeText::OpenWithRelationAttributeText(const Model *model, 1759 const BColumn *column, const BPoseView *view) 1760 : ScalarAttributeText(model, column), 1761 fPoseView(view) 1762 { 1763 } 1764 1765 1766 int64 1767 OpenWithRelationAttributeText::ReadValue() 1768 { 1769 fValueDirty = false; 1770 1771 const OpenWithPoseView *view = dynamic_cast<const OpenWithPoseView *>(fPoseView); 1772 1773 if (view) { 1774 fValue = view->OpenWithRelation(fModel); 1775 fValueIsDefined = true; 1776 } 1777 1778 return fValue; 1779 } 1780 1781 1782 float 1783 OpenWithRelationAttributeText::PreferredWidth(const BPoseView *pose) const 1784 { 1785 BString widthString; 1786 TruncString(&widthString, fRelationText.String(), fRelationText.Length(), 1787 pose, 500, B_TRUNCATE_END); 1788 return pose->StringWidth(widthString.String()); 1789 } 1790 1791 1792 void 1793 OpenWithRelationAttributeText::FitValue(BString *result, const BPoseView *view) 1794 { 1795 if (fValueDirty) 1796 ReadValue(); 1797 1798 ASSERT(view == fPoseView); 1799 const OpenWithPoseView *launchWithView 1800 = dynamic_cast<const OpenWithPoseView *>(view); 1801 1802 if (launchWithView) 1803 launchWithView->OpenWithRelationDescription(fModel, &fRelationText); 1804 1805 fOldWidth = fColumn->Width(); 1806 fTruncatedWidth = TruncString(result, fRelationText.String(), 1807 fRelationText.Length(), view, fOldWidth, B_TRUNCATE_END); 1808 fDirty = false; 1809 } 1810 1811 1812 VersionAttributeText::VersionAttributeText(const Model *model, 1813 const BColumn *column, bool app) 1814 : StringAttributeText(model, column), 1815 fAppVersion(app) 1816 { 1817 } 1818 1819 1820 void 1821 VersionAttributeText::ReadValue(BString *result) 1822 { 1823 fValueDirty = false; 1824 1825 BModelOpener opener(fModel); 1826 BFile *file = dynamic_cast<BFile *>(fModel->Node()); 1827 if (file) { 1828 BAppFileInfo info(file); 1829 version_info version; 1830 if (info.InitCheck() == B_OK 1831 && info.GetVersionInfo(&version, 1832 fAppVersion ? B_APP_VERSION_KIND : B_SYSTEM_VERSION_KIND) == B_OK) { 1833 *result = version.short_info; 1834 return; 1835 } 1836 } 1837 *result = "-"; 1838 } 1839 1840