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