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