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