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