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