vfs: check userland buffers before reading them.
[haiku.git] / src / kits / tracker / WidgetAttributeText.cpp
blobb5bf3896ae9787f520c81ae1e82b4d4d55259611
1 /*
2 Open Tracker License
4 Terms and Conditions
6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
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:
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.
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.
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.
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.
36 #include "WidgetAttributeText.h"
38 #include <ctype.h>
39 #include <stdlib.h>
40 #include <strings.h>
42 #include <fs_attr.h>
43 #include <parsedate.h>
45 #include <Alert.h>
46 #include <AppFileInfo.h>
47 #include <Catalog.h>
48 #include <DateFormat.h>
49 #include <DateTimeFormat.h>
50 #include <Debug.h>
51 #include <Locale.h>
52 #include <MessageFormat.h>
53 #include <NodeInfo.h>
54 #include <Path.h>
55 #include <SupportDefs.h>
56 #include <TextView.h>
57 #include <Volume.h>
58 #include <VolumeRoster.h>
60 #include "Attributes.h"
61 #include "FindPanel.h"
62 #include "FSUndoRedo.h"
63 #include "FSUtils.h"
64 #include "Model.h"
65 #include "OpenWithWindow.h"
66 #include "MimeTypes.h"
67 #include "PoseView.h"
68 #include "SettingsViews.h"
69 #include "Utilities.h"
70 #include "ViewState.h"
73 #undef B_TRANSLATION_CONTEXT
74 #define B_TRANSLATION_CONTEXT "WidgetAttributeText"
77 const int32 kGenericReadBufferSize = 1024;
79 const char* kSizeFormats[] = {
80 "%.2f %s",
81 "%.1f %s",
82 "%.f %s",
83 "%.f%s",
88 bool NameAttributeText::sSortFolderNamesFirst = false;
89 bool RealNameAttributeText::sSortFolderNamesFirst = false;
92 template <class View>
93 float
94 TruncFileSizeBase(BString* outString, int64 value, const View* view,
95 float width)
97 // ToDo: If slow, replace float divisions with shifts
98 // if fast enough, try fitting more decimal places.
100 // ToDo: Update string_for_size() in libshared to be able to
101 // handle this case.
103 BString buffer;
105 // format file size value
106 if (value == kUnknownSize) {
107 *outString = "-";
108 return view->StringWidth("-");
109 } else if (value < kKBSize) {
110 static BMessageFormat format(B_TRANSLATE(
111 "{0, plural, one{# byte} other{# bytes}}"));
112 format.Format(buffer, value);
113 if (view->StringWidth(buffer.String()) > width)
114 buffer.SetToFormat(B_TRANSLATE("%Ld B"), value);
115 } else {
116 const char* suffix;
117 float doubleValue;
118 if (value >= kTBSize) {
119 suffix = B_TRANSLATE("TiB");
120 doubleValue = (double)value / kTBSize;
121 } else if (value >= kGBSize) {
122 suffix = B_TRANSLATE("GiB");
123 doubleValue = (double)value / kGBSize;
124 } else if (value >= kMBSize) {
125 suffix = B_TRANSLATE("MiB");
126 doubleValue = (double)value / kMBSize;
127 } else {
128 ASSERT(value >= kKBSize);
129 suffix = B_TRANSLATE("KiB");
130 doubleValue = (double)value / kKBSize;
133 for (int32 index = 0; ; index++) {
134 if (kSizeFormats[index] == 0)
135 break;
137 buffer.SetToFormat(kSizeFormats[index], doubleValue, suffix);
138 // strip off an insignificant zero so we don't get readings
139 // such as 1.00
140 char* period = 0;
141 for (char* tmp = const_cast<char*>(buffer.String()); *tmp != '\0';
142 tmp++) {
143 if (*tmp == '.')
144 period = tmp;
146 if (period && period[1] && period[2] == '0')
147 // move the rest of the string over the insignificant zero
148 for (char* tmp = &period[2]; *tmp; tmp++)
149 *tmp = tmp[1];
151 float resultWidth = view->StringWidth(buffer);
152 if (resultWidth <= width) {
153 *outString = buffer.String();
154 return resultWidth;
159 return TruncStringBase(outString, buffer.String(), buffer.Length(), view,
160 width, (uint32)B_TRUNCATE_END);
164 template <class View>
165 float
166 TruncStringBase(BString* outString, const char* inString, int32 length,
167 const View* view, float width, uint32 truncMode = B_TRUNCATE_MIDDLE)
169 // we are using a template version of this call to make sure
170 // the right StringWidth gets picked up for BView x BPoseView
171 // for max speed and flexibility
173 // a standard ellipsis inserting fitting algorithm
174 if (view->StringWidth(inString, length) <= width)
175 *outString = inString;
176 else {
177 const char* source[1];
178 char* results[1];
180 source[0] = inString;
181 results[0] = outString->LockBuffer(length + 3);
183 BFont font;
184 view->GetFont(&font);
186 font.GetTruncatedStrings(source, 1, truncMode, width, results);
187 outString->UnlockBuffer();
190 return view->StringWidth(outString->String(), outString->Length());
194 template <class View>
195 float
196 TruncTimeBase(BString* outString, int64 value, const View* view, float width)
198 float resultWidth = width + 1;
200 time_t timeValue = (time_t)value;
202 // Find the longest possible format that will fit the available space
203 struct {
204 BDateFormatStyle dateStyle;
205 BTimeFormatStyle timeStyle;
206 } formats[] = {
207 { B_LONG_DATE_FORMAT, B_MEDIUM_TIME_FORMAT },
208 { B_LONG_DATE_FORMAT, B_SHORT_TIME_FORMAT },
209 { B_MEDIUM_DATE_FORMAT, B_SHORT_TIME_FORMAT },
210 { B_SHORT_DATE_FORMAT, B_SHORT_TIME_FORMAT },
213 BString date;
214 BDateTimeFormat formatter;
215 for (unsigned int i = 0; i < B_COUNT_OF(formats); ++i) {
216 if (formatter.Format(date, timeValue, formats[i].dateStyle,
217 formats[i].timeStyle) == B_OK) {
218 resultWidth = view->StringWidth(date.String(), date.Length());
219 if (resultWidth <= width) {
220 // Found a format that fits the available space, stop searching
221 break;
226 // If we couldn't fit the date, try with just the time
227 // TODO we could use only the time for "today" dates
228 if (resultWidth > width
229 && BDateFormat().Format(date, timeValue,
230 B_SHORT_DATE_FORMAT) == B_OK) {
231 resultWidth = view->StringWidth(date.String(), date.Length());
234 if (resultWidth > width) {
235 // even the shortest format string didn't do it, insert ellipsis
236 resultWidth = TruncStringBase(outString, date.String(),
237 (ssize_t)date.Length(), view, width);
238 } else
239 *outString = date;
241 return resultWidth;
245 // #pragma mark - WidgetAttributeText base class
248 WidgetAttributeText*
249 WidgetAttributeText::NewWidgetText(const Model* model,
250 const BColumn* column, const BPoseView* view)
252 // call this to make the right WidgetAttributeText type for a
253 // given column
255 const char* attrName = column->AttrName();
257 if (strcmp(attrName, kAttrPath) == 0)
258 return new PathAttributeText(model, column);
260 if (strcmp(attrName, kAttrMIMEType) == 0)
261 return new KindAttributeText(model, column);
263 if (strcmp(attrName, kAttrStatName) == 0)
264 return new NameAttributeText(model, column);
266 if (strcmp(attrName, kAttrRealName) == 0)
267 return new RealNameAttributeText(model, column);
269 if (strcmp(attrName, kAttrStatSize) == 0)
270 return new SizeAttributeText(model, column);
272 if (strcmp(attrName, kAttrStatModified) == 0)
273 return new ModificationTimeAttributeText(model, column);
275 if (strcmp(attrName, kAttrStatCreated) == 0)
276 return new CreationTimeAttributeText(model, column);
278 #ifdef OWNER_GROUP_ATTRIBUTES
279 if (strcmp(attrName, kAttrStatOwner) == 0)
280 return new OwnerAttributeText(model, column);
282 if (strcmp(attrName, kAttrStatGroup) == 0)
283 return new GroupAttributeText(model, column);
284 #endif
285 if (strcmp(attrName, kAttrStatMode) == 0)
286 return new ModeAttributeText(model, column);
288 if (strcmp(attrName, kAttrOpenWithRelation) == 0)
289 return new OpenWithRelationAttributeText(model, column, view);
291 if (strcmp(attrName, kAttrAppVersion) == 0)
292 return new AppShortVersionAttributeText(model, column);
294 if (strcmp(attrName, kAttrSystemVersion) == 0)
295 return new SystemShortVersionAttributeText(model, column);
297 if (strcmp(attrName, kAttrOriginalPath) == 0)
298 return new OriginalPathAttributeText(model, column);
300 if (column->DisplayAs() != NULL) {
301 if (!strncmp(column->DisplayAs(), "checkbox", 8))
302 return new CheckboxAttributeText(model, column);
304 if (!strncmp(column->DisplayAs(), "duration", 8))
305 return new DurationAttributeText(model, column);
307 if (!strncmp(column->DisplayAs(), "rating", 6))
308 return new RatingAttributeText(model, column);
311 return new GenericAttributeText(model, column);
315 WidgetAttributeText::WidgetAttributeText(const Model* model,
316 const BColumn* column)
318 fModel(const_cast<Model*>(model)),
319 fColumn(column),
320 fOldWidth(-1.0f),
321 fTruncatedWidth(-1.0f),
322 fDirty(true),
323 fValueIsDefined(false)
325 ASSERT(fColumn != NULL);
327 if (fColumn == NULL)
328 return;
330 ASSERT(fColumn->Width() > 0);
334 WidgetAttributeText::~WidgetAttributeText()
339 const char*
340 WidgetAttributeText::FittingText(const BPoseView* view)
342 if (fDirty || fColumn->Width() != fOldWidth || CheckSettingsChanged()
343 || !fValueIsDefined) {
344 CheckViewChanged(view);
347 ASSERT(!fDirty);
348 return fText.String();
352 bool
353 WidgetAttributeText::CheckViewChanged(const BPoseView* view)
355 BString newText;
356 FitValue(&newText, view);
357 if (newText == fText)
358 return false;
360 fText = newText;
361 return true;
365 bool
366 WidgetAttributeText::CheckSettingsChanged()
368 return false;
372 float
373 WidgetAttributeText::TruncString(BString* outString, const char* inString,
374 int32 length, const BPoseView* view, float width, uint32 truncMode)
376 return TruncStringBase(outString, inString, length, view, width, truncMode);
380 float
381 WidgetAttributeText::TruncFileSize(BString* outString, int64 value,
382 const BPoseView* view, float width)
384 return TruncFileSizeBase(outString, value, view, width);
388 float
389 WidgetAttributeText::TruncTime(BString* outString, int64 value,
390 const BPoseView* view, float width)
392 return TruncTimeBase(outString, value, view, width);
396 float
397 WidgetAttributeText::CurrentWidth() const
399 return fTruncatedWidth;
403 float
404 WidgetAttributeText::Width(const BPoseView* pose)
406 FittingText(pose);
407 return CurrentWidth();
411 void
412 WidgetAttributeText::SetUpEditing(BTextView*)
414 ASSERT(fColumn->Editable());
418 bool
419 WidgetAttributeText::CommitEditedText(BTextView*)
421 // can't do anything here at this point
422 TRESPASS();
423 return false;
427 status_t
428 WidgetAttributeText::AttrAsString(const Model* model, BString* outString,
429 const char* attrName, int32 attrType, float width, BView* view,
430 int64* resultingValue)
432 int64 value;
434 status_t error = model->InitCheck();
435 if (error != B_OK)
436 return error;
438 switch (attrType) {
439 case B_TIME_TYPE:
440 if (strcmp(attrName, kAttrStatModified) == 0)
441 value = model->StatBuf()->st_mtime;
442 else if (strcmp(attrName, kAttrStatCreated) == 0)
443 value = model->StatBuf()->st_crtime;
444 else {
445 TRESPASS();
446 // not yet supported
447 return B_ERROR;
449 TruncTimeBase(outString, value, view, width);
450 if (resultingValue)
451 *resultingValue = value;
453 return B_OK;
455 case B_STRING_TYPE:
456 if (strcmp(attrName, kAttrPath) == 0) {
457 BEntry entry(model->EntryRef());
458 BPath path;
459 BString tmp;
461 if (entry.InitCheck() == B_OK
462 && entry.GetPath(&path) == B_OK) {
463 tmp = path.Path();
464 TruncateLeaf(&tmp);
465 } else
466 tmp = "-";
468 if (width > 0) {
469 TruncStringBase(outString, tmp.String(), tmp.Length(), view,
470 width);
471 } else
472 *outString = tmp.String();
474 return B_OK;
476 break;
478 case kSizeType:
479 // TruncFileSizeBase(outString, model->StatBuf()->st_size, view,
480 // width);
481 return B_OK;
482 break;
484 default:
485 TRESPASS();
486 // not yet supported
487 return B_ERROR;
491 TRESPASS();
492 return B_ERROR;
496 bool
497 WidgetAttributeText::IsEditable() const
499 return fColumn->Editable()
500 && !BVolume(fModel->StatBuf()->st_dev).IsReadOnly();
504 void
505 WidgetAttributeText::SetDirty(bool value)
507 fDirty = value;
511 // #pragma mark - StringAttributeText
514 StringAttributeText::StringAttributeText(const Model* model,
515 const BColumn* column)
517 WidgetAttributeText(model, column),
518 fValueDirty(true)
523 const char*
524 StringAttributeText::ValueAsText(const BPoseView* /*view*/)
526 if (fValueDirty)
527 ReadValue(&fFullValueText);
529 return fFullValueText.String();
533 bool
534 StringAttributeText::CheckAttributeChanged()
536 BString newString;
537 ReadValue(&newString);
539 if (newString == fFullValueText)
540 return false;
542 fFullValueText = newString;
543 fDirty = true; // have to redo fitted string
544 return true;
548 void
549 StringAttributeText::FitValue(BString* outString, const BPoseView* view)
551 if (fValueDirty)
552 ReadValue(&fFullValueText);
553 fOldWidth = fColumn->Width();
555 fTruncatedWidth = TruncString(outString, fFullValueText.String(),
556 fFullValueText.Length(), view, fOldWidth);
557 fDirty = false;
561 float
562 StringAttributeText::PreferredWidth(const BPoseView* pose) const
564 return pose->StringWidth(fFullValueText.String());
569 StringAttributeText::Compare(WidgetAttributeText& attr, BPoseView* view)
571 StringAttributeText* compareTo = dynamic_cast<StringAttributeText*>(&attr);
572 ThrowOnAssert(compareTo != NULL);
574 if (fValueDirty)
575 ReadValue(&fFullValueText);
577 return NaturalCompare(fFullValueText.String(),
578 compareTo->ValueAsText(view));
582 bool
583 StringAttributeText::CommitEditedText(BTextView* textView)
585 ASSERT(fColumn->Editable());
586 const char* text = textView->Text();
588 if (fFullValueText == text) {
589 // no change
590 return false;
593 if (textView->TextLength() == 0) {
594 // cannot do an empty name
595 return false;
598 // cause re-truncation
599 fDirty = true;
601 if (!CommitEditedTextFlavor(textView))
602 return false;
604 // update text and width in this widget
605 fFullValueText = text;
607 return true;
611 // #pragma mark - ScalarAttributeText
614 ScalarAttributeText::ScalarAttributeText(const Model* model,
615 const BColumn* column)
617 WidgetAttributeText(model, column),
618 fValue(0),
619 fValueDirty(true)
624 int64
625 ScalarAttributeText::Value()
627 if (fValueDirty)
628 fValue = ReadValue();
630 return fValue;
634 bool
635 ScalarAttributeText::CheckAttributeChanged()
637 int64 newValue = ReadValue();
638 if (newValue == fValue)
639 return false;
641 fValue = newValue;
642 fDirty = true;
643 // have to redo fitted string
645 return true;
649 float
650 ScalarAttributeText::PreferredWidth(const BPoseView* pose) const
652 BString widthString;
653 widthString << fValue;
654 return pose->StringWidth(widthString.String());
659 ScalarAttributeText::Compare(WidgetAttributeText& attr, BPoseView*)
661 ScalarAttributeText* compareTo = dynamic_cast<ScalarAttributeText*>(&attr);
662 ThrowOnAssert(compareTo != NULL);
664 if (fValueDirty)
665 fValue = ReadValue();
667 return fValue >= compareTo->Value()
668 ? (fValue == compareTo->Value() ? 0 : 1) : -1;
672 // #pragma mark - PathAttributeText
675 PathAttributeText::PathAttributeText(const Model* model, const BColumn* column)
677 StringAttributeText(model, column)
682 void
683 PathAttributeText::ReadValue(BString* outString)
685 // get the path
686 BEntry entry(fModel->EntryRef());
687 BPath path;
689 if (entry.InitCheck() == B_OK && entry.GetPath(&path) == B_OK) {
690 *outString = path.Path();
691 TruncateLeaf(outString);
692 } else
693 *outString = "-";
695 fValueDirty = false;
699 // #pragma mark - OriginalPathAttributeText
702 OriginalPathAttributeText::OriginalPathAttributeText(const Model* model,
703 const BColumn* column)
705 StringAttributeText(model, column)
710 void
711 OriginalPathAttributeText::ReadValue(BString* outString)
713 BEntry entry(fModel->EntryRef());
714 BPath path;
716 // get the original path
717 if (entry.InitCheck() == B_OK && FSGetOriginalPath(&entry, &path) == B_OK)
718 *outString = path.Path();
719 else
720 *outString = "-";
722 fValueDirty = false;
726 // #pragma mark - KindAttributeText
729 KindAttributeText::KindAttributeText(const Model* model, const BColumn* column)
731 StringAttributeText(model, column)
736 void
737 KindAttributeText::ReadValue(BString* outString)
739 BMimeType mime;
740 char desc[B_MIME_TYPE_LENGTH];
742 // get the mime type
743 if (mime.SetType(fModel->MimeType()) != B_OK)
744 *outString = B_TRANSLATE("Unknown");
745 else if (mime.GetShortDescription(desc) == B_OK) {
746 // get the short mime type description
747 *outString = desc;
748 } else
749 *outString = fModel->MimeType();
751 fValueDirty = false;
755 // #pragma mark - NameAttributeText
758 NameAttributeText::NameAttributeText(const Model* model,
759 const BColumn* column)
761 StringAttributeText(model, column)
767 NameAttributeText::Compare(WidgetAttributeText& attr, BPoseView* view)
769 NameAttributeText* compareTo = dynamic_cast<NameAttributeText*>(&attr);
770 ThrowOnAssert(compareTo != NULL);
772 if (fValueDirty)
773 ReadValue(&fFullValueText);
775 if (NameAttributeText::sSortFolderNamesFirst)
776 return fModel->CompareFolderNamesFirst(attr.TargetModel());
778 return NaturalCompare(fFullValueText.String(),
779 compareTo->ValueAsText(view));
783 void
784 NameAttributeText::ReadValue(BString* outString)
786 *outString = fModel->Name();
788 fValueDirty = false;
792 void
793 NameAttributeText::FitValue(BString* outString, const BPoseView* view)
795 if (fValueDirty)
796 ReadValue(&fFullValueText);
797 fOldWidth = fColumn->Width();
798 fTruncatedWidth = TruncString(outString, fFullValueText.String(),
799 fFullValueText.Length(), view, fOldWidth, B_TRUNCATE_END);
800 fDirty = false;
804 void
805 NameAttributeText::SetUpEditing(BTextView* textView)
807 DisallowFilenameKeys(textView);
809 textView->SetMaxBytes(B_FILE_NAME_LENGTH);
810 textView->SetText(fFullValueText.String(), fFullValueText.Length());
814 bool
815 NameAttributeText::CommitEditedTextFlavor(BTextView* textView)
817 const char* text = textView->Text();
819 BEntry entry(fModel->EntryRef());
820 if (entry.InitCheck() != B_OK)
821 return false;
823 BDirectory parent;
824 if (entry.GetParent(&parent) != B_OK)
825 return false;
827 bool removeExisting = false;
828 if (parent.Contains(text)) {
829 BAlert* alert = new BAlert("",
830 B_TRANSLATE("That name is already taken. "
831 "Please type another one."),
832 B_TRANSLATE("Replace other file"),
833 B_TRANSLATE("OK"),
834 NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
835 alert->SetShortcut(0, 'r');
836 if (alert->Go())
837 return false;
839 removeExisting = true;
842 // TODO:
843 // use model-flavor specific virtuals for all of these special
844 // renamings
845 status_t result;
846 if (fModel->IsVolume()) {
847 BVolume volume(fModel->NodeRef()->device);
848 result = volume.InitCheck();
849 if (result == B_OK) {
850 RenameVolumeUndo undo(volume, text);
852 result = volume.SetName(text);
853 if (result != B_OK)
854 undo.Remove();
856 } else {
857 if (fModel->IsQuery()) {
858 BModelWriteOpener opener(fModel);
859 ASSERT(fModel->Node());
860 MoreOptionsStruct::SetQueryTemporary(fModel->Node(), false);
863 RenameUndo undo(entry, text);
865 result = entry.Rename(text, removeExisting);
866 if (result != B_OK)
867 undo.Remove();
870 return result == B_OK;
874 void
875 NameAttributeText::SetSortFolderNamesFirst(bool enabled)
877 NameAttributeText::sSortFolderNamesFirst = enabled;
881 bool
882 NameAttributeText::IsEditable() const
884 return StringAttributeText::IsEditable()
885 && !fModel->HasLocalizedName();
889 // #pragma mark - RealNameAttributeText
892 RealNameAttributeText::RealNameAttributeText(const Model* model,
893 const BColumn* column)
895 StringAttributeText(model, column)
901 RealNameAttributeText::Compare(WidgetAttributeText& attr, BPoseView* view)
903 RealNameAttributeText* compareTo
904 = dynamic_cast<RealNameAttributeText*>(&attr);
905 ThrowOnAssert(compareTo != NULL);
907 if (fValueDirty)
908 ReadValue(&fFullValueText);
910 if (RealNameAttributeText::sSortFolderNamesFirst)
911 return fModel->CompareFolderNamesFirst(attr.TargetModel());
913 return NaturalCompare(fFullValueText.String(),
914 compareTo->ValueAsText(view));
918 void
919 RealNameAttributeText::ReadValue(BString* outString)
921 *outString = fModel->EntryRef()->name;
923 fValueDirty = false;
927 void
928 RealNameAttributeText::FitValue(BString* outString, const BPoseView* view)
930 if (fValueDirty)
931 ReadValue(&fFullValueText);
932 fOldWidth = fColumn->Width();
933 fTruncatedWidth = TruncString(outString, fFullValueText.String(),
934 fFullValueText.Length(), view, fOldWidth, B_TRUNCATE_END);
935 fDirty = false;
939 void
940 RealNameAttributeText::SetUpEditing(BTextView* textView)
942 DisallowFilenameKeys(textView);
944 textView->SetMaxBytes(B_FILE_NAME_LENGTH);
945 textView->SetText(fFullValueText.String(), fFullValueText.Length());
949 bool
950 RealNameAttributeText::CommitEditedTextFlavor(BTextView* textView)
952 const char* text = textView->Text();
954 BEntry entry(fModel->EntryRef());
955 if (entry.InitCheck() != B_OK)
956 return false;
958 BDirectory parent;
959 if (entry.GetParent(&parent) != B_OK)
960 return false;
962 bool removeExisting = false;
963 if (parent.Contains(text)) {
964 BAlert* alert = new BAlert("",
965 B_TRANSLATE("That name is already taken. "
966 "Please type another one."),
967 B_TRANSLATE("Replace other file"),
968 B_TRANSLATE("OK"),
969 NULL, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
971 alert->SetShortcut(0, 'r');
973 if (alert->Go())
974 return false;
976 removeExisting = true;
979 // TODO:
980 // use model-flavor specific virtuals for all of these special
981 // renamings
982 status_t result;
983 if (fModel->IsVolume()) {
984 BVolume volume(fModel->NodeRef()->device);
985 result = volume.InitCheck();
986 if (result == B_OK) {
987 RenameVolumeUndo undo(volume, text);
989 result = volume.SetName(text);
990 if (result != B_OK)
991 undo.Remove();
993 } else {
994 if (fModel->IsQuery()) {
995 BModelWriteOpener opener(fModel);
996 ASSERT(fModel->Node());
997 MoreOptionsStruct::SetQueryTemporary(fModel->Node(), false);
1000 RenameUndo undo(entry, text);
1002 result = entry.Rename(text, removeExisting);
1003 if (result != B_OK)
1004 undo.Remove();
1007 return result == B_OK;
1011 void
1012 RealNameAttributeText::SetSortFolderNamesFirst(bool enabled)
1014 RealNameAttributeText::sSortFolderNamesFirst = enabled;
1018 // #pragma mark - owner/group
1021 #ifdef OWNER_GROUP_ATTRIBUTES
1022 OwnerAttributeText::OwnerAttributeText(const Model* model,
1023 const BColumn* column)
1025 StringAttributeText(model, column)
1030 void
1031 OwnerAttributeText::ReadValue(BString* outString)
1033 uid_t nodeOwner = fModel->StatBuf()->st_uid;
1034 BString user;
1036 if (nodeOwner == 0) {
1037 if (getenv("USER") != NULL)
1038 user << getenv("USER");
1039 else
1040 user << "root";
1041 } else
1042 user << nodeOwner;
1043 *outString = user.String();
1045 fValueDirty = false;
1049 GroupAttributeText::GroupAttributeText(const Model* model,
1050 const BColumn* column)
1052 StringAttributeText(model, column)
1057 void
1058 GroupAttributeText::ReadValue(BString* outString)
1060 gid_t nodeGroup = fModel->StatBuf()->st_gid;
1061 BString group;
1063 if (nodeGroup == 0) {
1064 if (getenv("GROUP") != NULL)
1065 group << getenv("GROUP");
1066 else
1067 group << "0";
1068 } else
1069 group << nodeGroup;
1070 *outString = group.String();
1072 fValueDirty = false;
1074 #endif // OWNER_GROUP_ATTRIBUTES
1077 // #pragma mark - ModeAttributeText
1080 ModeAttributeText::ModeAttributeText(const Model* model,
1081 const BColumn* column)
1083 StringAttributeText(model, column)
1088 void
1089 ModeAttributeText::ReadValue(BString* outString)
1091 mode_t mode = fModel->StatBuf()->st_mode;
1092 mode_t baseMask = 00400;
1093 char buffer[11];
1095 char* scanner = buffer;
1097 if (S_ISDIR(mode))
1098 *scanner++ = 'd';
1099 else if (S_ISLNK(mode))
1100 *scanner++ = 'l';
1101 else if (S_ISBLK(mode))
1102 *scanner++ = 'b';
1103 else if (S_ISCHR(mode))
1104 *scanner++ = 'c';
1105 else
1106 *scanner++ = '-';
1108 for (int32 index = 0; index < 9; index++) {
1109 *scanner++ = (mode & baseMask) ? "rwx"[index % 3] : '-';
1110 baseMask >>= 1;
1113 *scanner = 0;
1114 *outString = buffer;
1116 fValueDirty = false;
1120 // #pragma mark - SizeAttributeText
1123 SizeAttributeText::SizeAttributeText(const Model* model,
1124 const BColumn* column)
1126 ScalarAttributeText(model, column)
1131 int64
1132 SizeAttributeText::ReadValue()
1134 fValueDirty = false;
1135 // get the size
1137 if (fModel->IsVolume()) {
1138 BVolume volume(fModel->NodeRef()->device);
1140 return volume.Capacity();
1143 if (fModel->IsDirectory() || fModel->IsQuery()
1144 || fModel->IsQueryTemplate() || fModel->IsSymLink()
1145 || fModel->IsVirtualDirectory()) {
1146 return kUnknownSize;
1149 fValueIsDefined = true;
1151 return fModel->StatBuf()->st_size;
1155 void
1156 SizeAttributeText::FitValue(BString* outString, const BPoseView* view)
1158 if (fValueDirty)
1159 fValue = ReadValue();
1161 fOldWidth = fColumn->Width();
1162 fTruncatedWidth = TruncFileSize(outString, fValue, view, fOldWidth);
1163 fDirty = false;
1167 float
1168 SizeAttributeText::PreferredWidth(const BPoseView* pose) const
1170 if (fValueIsDefined) {
1171 BString widthString;
1172 TruncFileSize(&widthString, fValue, pose, 100000);
1173 return pose->StringWidth(widthString.String());
1176 return pose->StringWidth("-");
1180 // #pragma mark - TimeAttributeText
1183 TimeAttributeText::TimeAttributeText(const Model* model,
1184 const BColumn* column)
1186 ScalarAttributeText(model, column),
1187 fLastClockIs24(false),
1188 fLastDateOrder(kDateFormatEnd),
1189 fLastTimeFormatSeparator(kSeparatorsEnd)
1194 float
1195 TimeAttributeText::PreferredWidth(const BPoseView* pose) const
1197 BString widthString;
1198 TruncTimeBase(&widthString, fValue, pose, 100000);
1199 return pose->StringWidth(widthString.String());
1203 void
1204 TimeAttributeText::FitValue(BString* outString, const BPoseView* view)
1206 if (fValueDirty)
1207 fValue = ReadValue();
1209 fOldWidth = fColumn->Width();
1210 fTruncatedWidth = TruncTime(outString, fValue, view, fOldWidth);
1211 fDirty = false;
1215 bool
1216 TimeAttributeText::CheckSettingsChanged(void)
1218 // TODO : check against the actual locale settings
1219 return false;
1223 // #pragma mark - CreationTimeAttributeText
1226 CreationTimeAttributeText::CreationTimeAttributeText(const Model* model,
1227 const BColumn* column)
1229 TimeAttributeText(model, column)
1234 int64
1235 CreationTimeAttributeText::ReadValue()
1237 fValueDirty = false;
1238 fValueIsDefined = true;
1239 return fModel->StatBuf()->st_crtime;
1243 // #pragma mark - ModificationTimeAttributeText
1246 ModificationTimeAttributeText::ModificationTimeAttributeText(
1247 const Model* model, const BColumn* column)
1249 TimeAttributeText(model, column)
1254 int64
1255 ModificationTimeAttributeText::ReadValue()
1257 fValueDirty = false;
1258 fValueIsDefined = true;
1259 return fModel->StatBuf()->st_mtime;
1263 // #pragma mark - GenericAttributeText
1266 GenericAttributeText::GenericAttributeText(const Model* model,
1267 const BColumn* column)
1269 StringAttributeText(model, column)
1274 bool
1275 GenericAttributeText::CheckAttributeChanged()
1277 GenericValueStruct tmpValue = fValue;
1278 BString tmpString(fFullValueText);
1279 ReadValue(&fFullValueText);
1281 // fDirty could already be true, in that case we mustn't set it to
1282 // false, even if the attribute text hasn't changed
1283 bool changed = fValue.int64t != tmpValue.int64t
1284 || tmpString != fFullValueText;
1285 if (changed)
1286 fDirty = true;
1288 return fDirty;
1292 float
1293 GenericAttributeText::PreferredWidth(const BPoseView* pose) const
1295 return pose->StringWidth(fFullValueText.String());
1299 void
1300 GenericAttributeText::ReadValue(BString* outString)
1302 BModelOpener opener(const_cast<Model*>(fModel));
1304 ssize_t length = 0;
1305 fFullValueText = "-";
1306 fValue.int64t = 0;
1307 fValueIsDefined = false;
1308 fValueDirty = false;
1310 if (!fModel->Node())
1311 return;
1313 switch (fColumn->AttrType()) {
1314 case B_STRING_TYPE:
1316 char buffer[kGenericReadBufferSize];
1317 length = fModel->Node()->ReadAttr(fColumn->AttrName(),
1318 fColumn->AttrType(), 0, buffer, kGenericReadBufferSize - 1);
1320 if (length > 0) {
1321 buffer[length] = '\0';
1322 // make sure the buffer is null-terminated even if we
1323 // didn't read the whole attribute in or it wasn't to
1324 // begin with
1326 *outString = buffer;
1327 fValueIsDefined = true;
1329 break;
1332 case B_SSIZE_T_TYPE:
1333 case B_TIME_TYPE:
1334 case B_OFF_T_TYPE:
1335 case B_FLOAT_TYPE:
1336 case B_BOOL_TYPE:
1337 case B_CHAR_TYPE:
1338 case B_INT8_TYPE:
1339 case B_INT16_TYPE:
1340 case B_INT32_TYPE:
1341 case B_INT64_TYPE:
1342 case B_UINT8_TYPE:
1343 case B_UINT16_TYPE:
1344 case B_UINT32_TYPE:
1345 case B_UINT64_TYPE:
1346 case B_DOUBLE_TYPE:
1348 // read in the numerical bit representation and attach it
1349 // with a type, depending on the bytes that could be read
1350 attr_info info;
1351 GenericValueStruct tmp;
1352 if (fModel->Node()->GetAttrInfo(fColumn->AttrName(), &info)
1353 == B_OK) {
1354 if (info.size && info.size <= (off_t)sizeof(int64)) {
1355 length = fModel->Node()->ReadAttr(fColumn->AttrName(),
1356 fColumn->AttrType(), 0, &tmp, (size_t)info.size);
1359 // We used tmp as a block of memory, now set the
1360 // correct fValue:
1362 if (length == info.size) {
1363 if (fColumn->AttrType() == B_FLOAT_TYPE
1364 || fColumn->AttrType() == B_DOUBLE_TYPE) {
1365 // filter out special float/double types
1366 switch (info.size) {
1367 case sizeof(float):
1368 fValueIsDefined = true;
1369 fValue.floatt = tmp.floatt;
1370 break;
1372 case sizeof(double):
1373 fValueIsDefined = true;
1374 fValue.doublet = tmp.doublet;
1375 break;
1377 default:
1378 TRESPASS();
1379 break;
1381 } else {
1382 // handle the standard data types
1383 switch (info.size) {
1384 case sizeof(char):
1385 // Takes care of bool too.
1386 fValueIsDefined = true;
1387 fValue.int8t = tmp.int8t;
1388 break;
1390 case sizeof(int16):
1391 fValueIsDefined = true;
1392 fValue.int16t = tmp.int16t;
1393 break;
1395 case sizeof(int32):
1396 // Takes care of time_t too.
1397 fValueIsDefined = true;
1398 fValue.int32t = tmp.int32t;
1399 break;
1401 case sizeof(int64):
1402 // Takes care of off_t too.
1403 fValueIsDefined = true;
1404 fValue.int64t = tmp.int64t;
1405 break;
1407 default:
1408 TRESPASS();
1409 break;
1414 break;
1420 void
1421 GenericAttributeText::FitValue(BString* outString, const BPoseView* view)
1423 if (fValueDirty)
1424 ReadValue(&fFullValueText);
1426 fOldWidth = fColumn->Width();
1428 if (!fValueIsDefined) {
1429 *outString = "-";
1430 fTruncatedWidth = TruncString(outString, fFullValueText.String(),
1431 fFullValueText.Length(), view, fOldWidth);
1432 fDirty = false;
1433 return;
1436 char buffer[256];
1438 switch (fColumn->AttrType()) {
1439 case B_SIZE_T_TYPE:
1440 TruncFileSizeBase(outString, fValue.int32t, view, fOldWidth);
1441 return;
1443 case B_SSIZE_T_TYPE:
1444 if (fValue.int32t > 0) {
1445 TruncFileSizeBase(outString, fValue.int32t, view, fOldWidth);
1446 return;
1448 sprintf(buffer, "%s", strerror(fValue.int32t));
1449 fFullValueText = buffer;
1450 break;
1452 case B_STRING_TYPE:
1453 fTruncatedWidth = TruncString(outString, fFullValueText.String(),
1454 fFullValueText.Length(), view, fOldWidth);
1455 fDirty = false;
1456 return;
1458 case B_OFF_T_TYPE:
1459 // As a side effect update the fFullValueText to the string
1460 // representation of value
1461 TruncFileSize(&fFullValueText, fValue.off_tt, view, 100000);
1462 fTruncatedWidth = TruncFileSize(outString, fValue.off_tt, view,
1463 fOldWidth);
1464 fDirty = false;
1465 return;
1467 case B_TIME_TYPE:
1468 // As a side effect update the fFullValueText to the string
1469 // representation of value
1470 TruncTime(&fFullValueText, fValue.time_tt, view, 100000);
1471 fTruncatedWidth = TruncTime(outString, fValue.time_tt, view,
1472 fOldWidth);
1473 fDirty = false;
1474 return;
1476 case B_BOOL_TYPE:
1477 // For now use true/false, would be nice to be able to set
1478 // the value text
1480 sprintf(buffer, "%s", fValue.boolt ? "true" : "false");
1481 fFullValueText = buffer;
1482 break;
1484 case B_CHAR_TYPE:
1485 // Make sure no non-printable characters are displayed:
1486 if (!isprint(fValue.uint8t)) {
1487 *outString = "-";
1488 fTruncatedWidth = TruncString(outString, fFullValueText.String(),
1489 fFullValueText.Length(), view, fOldWidth);
1490 fDirty = false;
1491 return;
1494 sprintf(buffer, "%c", fValue.uint8t);
1495 fFullValueText = buffer;
1496 break;
1498 case B_INT8_TYPE:
1499 sprintf(buffer, "%d", fValue.int8t);
1500 fFullValueText = buffer;
1501 break;
1503 case B_UINT8_TYPE:
1504 sprintf(buffer, "%d", fValue.uint8t);
1505 fFullValueText = buffer;
1506 break;
1508 case B_INT16_TYPE:
1509 sprintf(buffer, "%d", fValue.int16t);
1510 fFullValueText = buffer;
1511 break;
1513 case B_UINT16_TYPE:
1514 sprintf(buffer, "%d", fValue.uint16t);
1515 fFullValueText = buffer;
1516 break;
1518 case B_INT32_TYPE:
1519 sprintf(buffer, "%" B_PRId32, fValue.int32t);
1520 fFullValueText = buffer;
1521 break;
1523 case B_UINT32_TYPE:
1524 sprintf(buffer, "%" B_PRId32, fValue.uint32t);
1525 fFullValueText = buffer;
1526 break;
1528 case B_INT64_TYPE:
1529 sprintf(buffer, "%" B_PRId64, fValue.int64t);
1530 fFullValueText = buffer;
1531 break;
1533 case B_UINT64_TYPE:
1534 sprintf(buffer, "%" B_PRId64, fValue.uint64t);
1535 fFullValueText = buffer;
1536 break;
1538 case B_FLOAT_TYPE:
1539 snprintf(buffer, sizeof(buffer), "%g", fValue.floatt);
1540 fFullValueText = buffer;
1541 break;
1543 case B_DOUBLE_TYPE:
1544 snprintf(buffer, sizeof(buffer), "%g", fValue.doublet);
1545 fFullValueText = buffer;
1546 break;
1548 default:
1549 *outString = "-";
1550 fTruncatedWidth = TruncString(outString, fFullValueText.String(),
1551 fFullValueText.Length(), view, fOldWidth);
1552 fDirty = false;
1553 return;
1555 fTruncatedWidth = TruncString(outString, buffer, (ssize_t)strlen(buffer),
1556 view, fOldWidth);
1557 fDirty = false;
1561 const char*
1562 GenericAttributeText::ValueAsText(const BPoseView* view)
1564 // TODO: redesign this - this is to make sure the value is valid
1565 bool oldDirty = fDirty;
1566 BString outString;
1567 FitValue(&outString, view);
1568 fDirty = oldDirty;
1570 return fFullValueText.String();
1575 GenericAttributeText::Compare(WidgetAttributeText& attr, BPoseView*)
1577 GenericAttributeText* compareTo
1578 = dynamic_cast<GenericAttributeText*>(&attr);
1579 ThrowOnAssert(compareTo != NULL);
1581 if (fValueDirty)
1582 ReadValue(&fFullValueText);
1584 if (compareTo->fValueDirty)
1585 compareTo->ReadValue(&compareTo->fFullValueText);
1587 // sort undefined values last, regardless of the other value
1588 if (!fValueIsDefined)
1589 return compareTo->fValueIsDefined ? 1 : 0;
1591 if (!compareTo->fValueIsDefined)
1592 return -1;
1594 switch (fColumn->AttrType()) {
1595 case B_STRING_TYPE:
1596 return fFullValueText.ICompare(compareTo->fFullValueText);
1598 case B_CHAR_TYPE:
1600 char vStr[2] = { static_cast<char>(fValue.uint8t), 0 };
1601 char cStr[2] = { static_cast<char>(compareTo->fValue.uint8t), 0};
1603 BString valueStr(vStr);
1604 BString compareToStr(cStr);
1606 return valueStr.ICompare(compareToStr);
1609 case B_FLOAT_TYPE:
1610 return fValue.floatt >= compareTo->fValue.floatt ?
1611 (fValue.floatt == compareTo->fValue.floatt ? 0 : 1) : -1;
1613 case B_DOUBLE_TYPE:
1614 return fValue.doublet >= compareTo->fValue.doublet ?
1615 (fValue.doublet == compareTo->fValue.doublet ? 0 : 1) : -1;
1617 case B_BOOL_TYPE:
1618 return fValue.boolt >= compareTo->fValue.boolt ?
1619 (fValue.boolt == compareTo->fValue.boolt ? 0 : 1) : -1;
1621 case B_UINT8_TYPE:
1622 return fValue.uint8t >= compareTo->fValue.uint8t ?
1623 (fValue.uint8t == compareTo->fValue.uint8t ? 0 : 1) : -1;
1625 case B_INT8_TYPE:
1626 return fValue.int8t >= compareTo->fValue.int8t ?
1627 (fValue.int8t == compareTo->fValue.int8t ? 0 : 1) : -1;
1629 case B_UINT16_TYPE:
1630 return fValue.uint16t >= compareTo->fValue.uint16t ?
1631 (fValue.uint16t == compareTo->fValue.uint16t ? 0 : 1) : -1;
1633 case B_INT16_TYPE:
1634 return fValue.int16t >= compareTo->fValue.int16t ?
1635 (fValue.int16t == compareTo->fValue.int16t ? 0 : 1) : -1;
1637 case B_UINT32_TYPE:
1638 return fValue.uint32t >= compareTo->fValue.uint32t ?
1639 (fValue.uint32t == compareTo->fValue.uint32t ? 0 : 1) : -1;
1641 case B_TIME_TYPE:
1642 // time_t typedef'd to a long, i.e. a int32
1643 case B_INT32_TYPE:
1644 return fValue.int32t >= compareTo->fValue.int32t ?
1645 (fValue.int32t == compareTo->fValue.int32t ? 0 : 1) : -1;
1647 case B_OFF_T_TYPE:
1648 // off_t typedef'd to a long long, i.e. a int64
1649 case B_INT64_TYPE:
1650 return fValue.int64t >= compareTo->fValue.int64t ?
1651 (fValue.int64t == compareTo->fValue.int64t ? 0 : 1) : -1;
1653 case B_UINT64_TYPE:
1654 default:
1655 return fValue.uint64t >= compareTo->fValue.uint64t ?
1656 (fValue.uint64t == compareTo->fValue.uint64t ? 0 : 1) : -1;
1659 return 0;
1663 bool
1664 GenericAttributeText::CommitEditedText(BTextView* textView)
1666 ASSERT(fColumn->Editable());
1667 const char* text = textView->Text();
1669 if (fFullValueText == text)
1670 // no change
1671 return false;
1673 if (!CommitEditedTextFlavor(textView))
1674 return false;
1676 // update text and width in this widget
1677 fFullValueText = text;
1678 // cause re-truncation
1679 fDirty = true;
1680 fValueDirty = true;
1682 return true;
1686 void
1687 GenericAttributeText::SetUpEditing(BTextView* textView)
1689 textView->SetMaxBytes(kGenericReadBufferSize - 1);
1690 textView->SetText(fFullValueText.String(), fFullValueText.Length());
1694 bool
1695 GenericAttributeText::CommitEditedTextFlavor(BTextView* textView)
1697 BNode node(fModel->EntryRef());
1699 if (node.InitCheck() != B_OK)
1700 return false;
1702 uint32 type = fColumn->AttrType();
1704 if (type != B_STRING_TYPE
1705 && type != B_UINT64_TYPE
1706 && type != B_UINT32_TYPE
1707 && type != B_UINT16_TYPE
1708 && type != B_UINT8_TYPE
1709 && type != B_INT64_TYPE
1710 && type != B_INT32_TYPE
1711 && type != B_INT16_TYPE
1712 && type != B_INT8_TYPE
1713 && type != B_OFF_T_TYPE
1714 && type != B_TIME_TYPE
1715 && type != B_FLOAT_TYPE
1716 && type != B_DOUBLE_TYPE
1717 && type != B_CHAR_TYPE
1718 && type != B_BOOL_TYPE) {
1719 BAlert* alert = new BAlert("",
1720 B_TRANSLATE("Sorry, you cannot edit that attribute."),
1721 B_TRANSLATE("Cancel"),
1722 0, 0, B_WIDTH_AS_USUAL, B_STOP_ALERT);
1723 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1724 alert->Go();
1725 return false;
1728 const char* columnName = fColumn->AttrName();
1729 ssize_t size = 0;
1731 switch (type) {
1732 case B_STRING_TYPE:
1733 size = fModel->WriteAttr(columnName, type, 0, textView->Text(),
1734 (size_t)(textView->TextLength() + 1));
1735 break;
1737 case B_BOOL_TYPE:
1739 bool value = strncasecmp(textView->Text(), "0", 1) != 0
1740 && strncasecmp(textView->Text(), "off", 2) != 0
1741 && strncasecmp(textView->Text(), "no", 3) != 0
1742 && strncasecmp(textView->Text(), "false", 4) != 0
1743 && strlen(textView->Text()) != 0;
1745 size = fModel->WriteAttr(columnName, type, 0, &value, sizeof(bool));
1746 break;
1749 case B_CHAR_TYPE:
1751 char ch;
1752 sscanf(textView->Text(), "%c", &ch);
1753 //Check if we read the start of a multi-byte glyph:
1754 if (!isprint(ch)) {
1755 BAlert* alert = new BAlert("",
1756 B_TRANSLATE("Sorry, the 'Character' "
1757 "attribute cannot store a multi-byte glyph."),
1758 B_TRANSLATE("Cancel"),
1759 0, 0, B_WIDTH_AS_USUAL, B_STOP_ALERT);
1760 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1761 alert->Go();
1762 return false;
1765 size = fModel->WriteAttr(columnName, type, 0, &ch, sizeof(char));
1766 break;
1769 case B_FLOAT_TYPE:
1771 float floatVal;
1773 if (sscanf(textView->Text(), "%f", &floatVal) == 1) {
1774 fValueIsDefined = true;
1775 fValue.floatt = floatVal;
1776 size = fModel->WriteAttr(columnName, type, 0, &floatVal,
1777 sizeof(float));
1778 } else {
1779 // If the value was already defined, it's on disk.
1780 // Otherwise not.
1781 return fValueIsDefined;
1783 break;
1786 case B_DOUBLE_TYPE:
1788 double doubleVal;
1790 if (sscanf(textView->Text(), "%lf", &doubleVal) == 1) {
1791 fValueIsDefined = true;
1792 fValue.doublet = doubleVal;
1793 size = fModel->WriteAttr(columnName, type, 0, &doubleVal,
1794 sizeof(double));
1795 } else {
1796 // If the value was already defined, it's on disk.
1797 // Otherwise not.
1798 return fValueIsDefined;
1800 break;
1803 case B_TIME_TYPE:
1804 case B_OFF_T_TYPE:
1805 case B_UINT64_TYPE:
1806 case B_UINT32_TYPE:
1807 case B_UINT16_TYPE:
1808 case B_UINT8_TYPE:
1809 case B_INT64_TYPE:
1810 case B_INT32_TYPE:
1811 case B_INT16_TYPE:
1812 case B_INT8_TYPE:
1814 GenericValueStruct tmp;
1815 size_t scalarSize = 0;
1817 switch (type) {
1818 case B_TIME_TYPE:
1819 tmp.time_tt = parsedate(textView->Text(), time(0));
1820 scalarSize = sizeof(time_t);
1821 break;
1823 // do some size independent conversion on builtin types
1824 case B_OFF_T_TYPE:
1825 tmp.off_tt = StringToScalar(textView->Text());
1826 scalarSize = sizeof(off_t);
1827 break;
1829 case B_UINT64_TYPE:
1830 case B_INT64_TYPE:
1831 tmp.int64t = StringToScalar(textView->Text());
1832 scalarSize = sizeof(int64);
1833 break;
1835 case B_UINT32_TYPE:
1836 case B_INT32_TYPE:
1837 tmp.int32t = (int32)StringToScalar(textView->Text());
1838 scalarSize = sizeof(int32);
1839 break;
1841 case B_UINT16_TYPE:
1842 case B_INT16_TYPE:
1843 tmp.int16t = (int16)StringToScalar(textView->Text());
1844 scalarSize = sizeof(int16);
1845 break;
1847 case B_UINT8_TYPE:
1848 case B_INT8_TYPE:
1849 tmp.int8t = (int8)StringToScalar(textView->Text());
1850 scalarSize = sizeof(int8);
1851 break;
1853 default:
1854 TRESPASS();
1857 size = fModel->WriteAttr(columnName, type, 0, &tmp, scalarSize);
1858 break;
1862 if (size < 0) {
1863 BAlert* alert = new BAlert("",
1864 B_TRANSLATE("There was an error writing the attribute."),
1865 B_TRANSLATE("Cancel"),
1866 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
1867 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1868 alert->Go();
1870 fValueIsDefined = false;
1871 return false;
1874 fValueIsDefined = true;
1875 return true;
1879 // #pragma mark - DurationAttributeText (display as: duration)
1882 DurationAttributeText::DurationAttributeText(const Model* model,
1883 const BColumn* column)
1885 GenericAttributeText(model, column)
1890 // TODO: support editing!
1893 void
1894 DurationAttributeText::FitValue(BString* outString, const BPoseView* view)
1896 if (fValueDirty)
1897 ReadValue(&fFullValueText);
1899 fOldWidth = fColumn->Width();
1900 fDirty = false;
1902 if (!fValueIsDefined) {
1903 *outString = "-";
1904 fTruncatedWidth = TruncString(outString, fFullValueText.String(),
1905 fFullValueText.Length(), view, fOldWidth);
1906 return;
1909 int64 time = 0;
1911 switch (fColumn->AttrType()) {
1912 case B_TIME_TYPE:
1913 time = fValue.time_tt * 1000000LL;
1914 break;
1916 case B_INT8_TYPE:
1917 time = fValue.int8t * 1000000LL;
1918 break;
1920 case B_INT16_TYPE:
1921 time = fValue.int16t * 1000000LL;
1922 break;
1924 case B_INT32_TYPE:
1925 time = fValue.int32t * 1000000LL;
1926 break;
1928 case B_INT64_TYPE:
1929 time = fValue.int64t;
1930 break;
1933 // TODO: ignores micro seconds for now
1934 int32 seconds = time / 1000000LL;
1936 bool negative = seconds < 0;
1937 if (negative)
1938 seconds = -seconds;
1940 int32 hours = seconds / 3600;
1941 seconds -= hours * 3600;
1942 int32 minutes = seconds / 60;
1943 seconds = seconds % 60;
1945 char buffer[256];
1946 if (hours > 0) {
1947 snprintf(buffer, sizeof(buffer), "%s%" B_PRId32 ":%02" B_PRId32 ":%02"
1948 B_PRId32, negative ? "-" : "", hours, minutes, seconds);
1949 } else {
1950 snprintf(buffer, sizeof(buffer), "%s%" B_PRId32 ":%02" B_PRId32,
1951 negative ? "-" : "", minutes, seconds);
1954 fFullValueText = buffer;
1956 fTruncatedWidth = TruncString(outString, fFullValueText.String(),
1957 fFullValueText.Length(), view, fOldWidth);
1961 // #pragma mark - CheckboxAttributeText (display as: checkbox)
1964 CheckboxAttributeText::CheckboxAttributeText(const Model* model,
1965 const BColumn* column)
1967 GenericAttributeText(model, column),
1968 fOnChar("✖"),
1969 fOffChar("-")
1971 // TODO: better have common data in the column object!
1972 if (const char* separator = strchr(column->DisplayAs(), ':')) {
1973 BString chars(separator + 1);
1974 int32 length;
1975 const char* c = chars.CharAt(0, &length);
1976 fOnChar.SetTo(c, length);
1977 if (c[length]) {
1978 c = chars.CharAt(1, &length);
1979 fOffChar.SetTo(c, length);
1985 void
1986 CheckboxAttributeText::SetUpEditing(BTextView* view)
1988 // TODO: support editing for real!
1989 BString outString;
1990 GenericAttributeText::FitValue(&outString, NULL);
1991 GenericAttributeText::SetUpEditing(view);
1995 void
1996 CheckboxAttributeText::FitValue(BString* outString, const BPoseView* view)
1998 if (fValueDirty)
1999 ReadValue(&fFullValueText);
2001 fOldWidth = fColumn->Width();
2002 fDirty = false;
2004 if (!fValueIsDefined) {
2005 *outString = fOffChar;
2006 fTruncatedWidth = TruncString(outString, fFullValueText.String(),
2007 fFullValueText.Length(), view, fOldWidth);
2008 return;
2011 bool checked = false;
2013 switch (fColumn->AttrType()) {
2014 case B_BOOL_TYPE:
2015 checked = fValue.boolt;
2016 break;
2018 case B_INT8_TYPE:
2019 case B_UINT8_TYPE:
2020 checked = fValue.int8t != 0;
2021 break;
2023 case B_INT16_TYPE:
2024 case B_UINT16_TYPE:
2025 checked = fValue.int16t != 0;
2026 break;
2028 case B_INT32_TYPE:
2029 case B_UINT32_TYPE:
2030 checked = fValue.int32t != 0;
2031 break;
2034 fFullValueText = checked ? fOnChar : fOffChar;
2036 fTruncatedWidth = TruncString(outString, fFullValueText.String(),
2037 fFullValueText.Length(), view, fOldWidth);
2041 // #pragma mark - RatingAttributeText (display as: rating)
2044 RatingAttributeText::RatingAttributeText(const Model* model,
2045 const BColumn* column)
2047 GenericAttributeText(model, column),
2048 fCount(5),
2049 fMax(10)
2051 // TODO: support different star counts/max via specifier
2055 void
2056 RatingAttributeText::SetUpEditing(BTextView* view)
2058 // TODO: support editing for real!
2059 BString outString;
2060 GenericAttributeText::FitValue(&outString, NULL);
2061 GenericAttributeText::SetUpEditing(view);
2065 void
2066 RatingAttributeText::FitValue(BString* ratingString, const BPoseView* view)
2068 if (fValueDirty)
2069 ReadValue(&fFullValueText);
2071 fOldWidth = fColumn->Width();
2072 fDirty = false;
2074 int64 rating;
2075 if (fValueIsDefined) {
2076 switch (fColumn->AttrType()) {
2077 case B_INT8_TYPE:
2078 rating = fValue.int8t;
2079 break;
2081 case B_INT16_TYPE:
2082 rating = fValue.int16t;
2083 break;
2085 case B_INT32_TYPE:
2086 rating = fValue.int32t;
2087 break;
2089 default:
2090 rating = 0;
2091 break;
2093 } else
2094 rating = 0;
2096 if (rating > fMax)
2097 rating = fMax;
2099 if (rating < 0)
2100 rating = 0;
2102 int32 steps = fMax / fCount;
2103 fFullValueText = "";
2105 for (int32 i = 0; i < fCount; i++) {
2106 int64 n = i * steps;
2107 if (rating > n)
2108 fFullValueText += "★";
2109 else
2110 fFullValueText += "☆";
2113 fTruncatedWidth = TruncString(ratingString, fFullValueText.String(),
2114 fFullValueText.Length(), view, fOldWidth);
2118 // #pragma mark - OpenWithRelationAttributeText
2121 OpenWithRelationAttributeText::OpenWithRelationAttributeText(const Model* model,
2122 const BColumn* column, const BPoseView* view)
2124 ScalarAttributeText(model, column),
2125 fPoseView(view)
2130 int64
2131 OpenWithRelationAttributeText::ReadValue()
2133 fValueDirty = false;
2135 const OpenWithPoseView* view
2136 = dynamic_cast<const OpenWithPoseView*>(fPoseView);
2137 if (view != NULL) {
2138 fValue = view->OpenWithRelation(fModel);
2139 fValueIsDefined = true;
2142 return fValue;
2146 float
2147 OpenWithRelationAttributeText::PreferredWidth(const BPoseView* pose) const
2149 BString widthString;
2150 TruncString(&widthString, fRelationText.String(), fRelationText.Length(),
2151 pose, 500, B_TRUNCATE_END);
2152 return pose->StringWidth(widthString.String());
2156 void
2157 OpenWithRelationAttributeText::FitValue(BString* outString,
2158 const BPoseView* view)
2160 if (fValueDirty)
2161 ReadValue();
2163 ASSERT(view == fPoseView);
2164 const OpenWithPoseView* launchWithView
2165 = dynamic_cast<const OpenWithPoseView*>(view);
2166 if (launchWithView != NULL)
2167 launchWithView->OpenWithRelationDescription(fModel, &fRelationText);
2169 fOldWidth = fColumn->Width();
2170 fTruncatedWidth = TruncString(outString, fRelationText.String(),
2171 fRelationText.Length(), view, fOldWidth, B_TRUNCATE_END);
2172 fDirty = false;
2176 // #pragma mark - VersionAttributeText
2179 VersionAttributeText::VersionAttributeText(const Model* model,
2180 const BColumn* column, bool app)
2182 StringAttributeText(model, column),
2183 fAppVersion(app)
2188 void
2189 VersionAttributeText::ReadValue(BString* outString)
2191 fValueDirty = false;
2193 BModelOpener opener(fModel);
2194 BFile* file = dynamic_cast<BFile*>(fModel->Node());
2195 if (file != NULL) {
2196 BAppFileInfo info(file);
2197 version_info version;
2198 if (info.InitCheck() == B_OK
2199 && info.GetVersionInfo(&version, fAppVersion
2200 ? B_APP_VERSION_KIND : B_SYSTEM_VERSION_KIND) == B_OK) {
2201 *outString = version.short_info;
2202 return;
2206 *outString = "-";