xref: /haiku/src/kits/tracker/WidgetAttributeText.cpp (revision f5821a1aee77d3b9a979b42c68a79e50b5ebaefe)
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 		|| fModel->IsVirtualDirectory())
1109 		return kUnknownSize;
1110 
1111 	fValueIsDefined = true;
1112 
1113 	return fModel->StatBuf()->st_size;
1114 }
1115 
1116 
1117 void
1118 SizeAttributeText::FitValue(BString* result, const BPoseView* view)
1119 {
1120 	if (fValueDirty)
1121 		fValue = ReadValue();
1122 	fOldWidth = fColumn->Width();
1123 	fTruncatedWidth = TruncFileSize(result, fValue, view, fOldWidth);
1124 	fDirty = false;
1125 }
1126 
1127 
1128 float
1129 SizeAttributeText::PreferredWidth(const BPoseView* pose) const
1130 {
1131 	if (fValueIsDefined) {
1132 		BString widthString;
1133 		TruncFileSize(&widthString, fValue, pose, 100000);
1134 		return pose->StringWidth(widthString.String());
1135 	}
1136 	return pose->StringWidth("-");
1137 }
1138 
1139 
1140 // #pragma mark - time related
1141 
1142 
1143 TimeAttributeText::TimeAttributeText(const Model* model,
1144 	const BColumn* column)
1145 	:
1146 	ScalarAttributeText(model, column)
1147 {
1148 }
1149 
1150 
1151 float
1152 TimeAttributeText::PreferredWidth(const BPoseView* pose) const
1153 {
1154 	BString widthString;
1155 	TruncTimeBase(&widthString, fValue, pose, 100000);
1156 	return pose->StringWidth(widthString.String());
1157 }
1158 
1159 
1160 void
1161 TimeAttributeText::FitValue(BString* result, const BPoseView* view)
1162 {
1163 	if (fValueDirty)
1164 		fValue = ReadValue();
1165 	fOldWidth = fColumn->Width();
1166 	fTruncatedWidth = TruncTime(result, fValue, view, fOldWidth);
1167 	fDirty = false;
1168 }
1169 
1170 
1171 bool
1172 TimeAttributeText::CheckSettingsChanged(void)
1173 {
1174 	// TODO : check against the actual locale settings
1175 	return false;
1176 }
1177 
1178 
1179 CreationTimeAttributeText::CreationTimeAttributeText(const Model* model,
1180 	const BColumn* column)
1181 	:
1182 	TimeAttributeText(model, column)
1183 {
1184 }
1185 
1186 
1187 int64
1188 CreationTimeAttributeText::ReadValue()
1189 {
1190 	fValueDirty = false;
1191 	fValueIsDefined = true;
1192 	return fModel->StatBuf()->st_crtime;
1193 }
1194 
1195 
1196 ModificationTimeAttributeText::ModificationTimeAttributeText(
1197 	const Model* model, const BColumn* column)
1198 	:
1199 	TimeAttributeText(model, column)
1200 {
1201 }
1202 
1203 
1204 int64
1205 ModificationTimeAttributeText::ReadValue()
1206 {
1207 	fValueDirty = false;
1208 	fValueIsDefined = true;
1209 	return fModel->StatBuf()->st_mtime;
1210 }
1211 
1212 
1213 //	#pragma mark -
1214 
1215 
1216 GenericAttributeText::GenericAttributeText(const Model* model,
1217 	const BColumn* column)
1218 	:
1219 	StringAttributeText(model, column)
1220 {
1221 }
1222 
1223 
1224 bool
1225 GenericAttributeText::CheckAttributeChanged()
1226 {
1227 	GenericValueStruct tmpValue = fValue;
1228 	BString tmpString(fFullValueText);
1229 	ReadValue(&fFullValueText);
1230 
1231 	// fDirty could already be true, in that case we mustn't set it to
1232 	// false, even if the attribute text hasn't changed
1233 	bool changed = fValue.int64t != tmpValue.int64t
1234 		|| tmpString != fFullValueText;
1235 	if (changed)
1236 		fDirty = true;
1237 
1238 	return fDirty;
1239 }
1240 
1241 
1242 float
1243 GenericAttributeText::PreferredWidth(const BPoseView* pose) const
1244 {
1245 	return pose->StringWidth(fFullValueText.String());
1246 }
1247 
1248 
1249 void
1250 GenericAttributeText::ReadValue(BString* result)
1251 {
1252 	BModelOpener opener(const_cast<Model*>(fModel));
1253 
1254 	ssize_t length = 0;
1255 	fFullValueText = "-";
1256 	fValue.int64t = 0;
1257 	fValueIsDefined = false;
1258 	fValueDirty = false;
1259 
1260 	if (!fModel->Node())
1261 		return;
1262 
1263 	switch (fColumn->AttrType()) {
1264 		case B_STRING_TYPE:
1265 		{
1266 			char buffer[kGenericReadBufferSize];
1267 			length = fModel->Node()->ReadAttr(fColumn->AttrName(),
1268 				fColumn->AttrType(), 0, buffer, kGenericReadBufferSize - 1);
1269 
1270 			if (length > 0) {
1271 				buffer[length] = '\0';
1272 				// make sure the buffer is null-terminated even if we
1273 				// didn't read the whole attribute in or it wasn't to
1274 				// begin with
1275 
1276 				*result = buffer;
1277 				fValueIsDefined = true;
1278 			}
1279 			break;
1280 		}
1281 
1282 		case B_SSIZE_T_TYPE:
1283 		case B_TIME_TYPE:
1284 		case B_OFF_T_TYPE:
1285 		case B_FLOAT_TYPE:
1286 		case B_BOOL_TYPE:
1287 		case B_CHAR_TYPE:
1288 		case B_INT8_TYPE:
1289 		case B_INT16_TYPE:
1290 		case B_INT32_TYPE:
1291 		case B_INT64_TYPE:
1292 		case B_UINT8_TYPE:
1293 		case B_UINT16_TYPE:
1294 		case B_UINT32_TYPE:
1295 		case B_UINT64_TYPE:
1296 		case B_DOUBLE_TYPE:
1297 		{
1298 			// read in the numerical bit representation and attach it
1299 			// with a type, depending on the bytes that could be read
1300 			attr_info info;
1301 			GenericValueStruct tmp;
1302 			if (fModel->Node()->GetAttrInfo(fColumn->AttrName(), &info)
1303 					== B_OK) {
1304 				if (info.size && info.size <= (off_t)sizeof(int64)) {
1305 					length = fModel->Node()->ReadAttr(fColumn->AttrName(),
1306 						fColumn->AttrType(), 0, &tmp, (size_t)info.size);
1307 				}
1308 
1309 				// We used tmp as a block of memory, now set the
1310 				// correct fValue:
1311 
1312 				if (length == info.size) {
1313 					if (fColumn->AttrType() == B_FLOAT_TYPE
1314 						|| fColumn->AttrType() == B_DOUBLE_TYPE) {
1315 						// filter out special float/double types
1316 						switch (info.size) {
1317 							case sizeof(float):
1318 								fValueIsDefined = true;
1319 								fValue.floatt = tmp.floatt;
1320 								break;
1321 
1322 							case sizeof(double):
1323 								fValueIsDefined = true;
1324 								fValue.doublet = tmp.doublet;
1325 								break;
1326 
1327 							default:
1328 								TRESPASS();
1329 						}
1330 					} else {
1331 						// handle the standard data types
1332 						switch (info.size) {
1333 							case sizeof(char):	// Takes care of bool too.
1334 								fValueIsDefined = true;
1335 								fValue.int8t = tmp.int8t;
1336 								break;
1337 
1338 							case sizeof(int16):
1339 								fValueIsDefined = true;
1340 								fValue.int16t = tmp.int16t;
1341 								break;
1342 
1343 							case sizeof(int32):	// Takes care of time_t too.
1344 								fValueIsDefined = true;
1345 								fValue.int32t = tmp.int32t;
1346 								break;
1347 
1348 							case sizeof(int64):	// Takes care of off_t too.
1349 								fValueIsDefined = true;
1350 								fValue.int64t = tmp.int64t;
1351 								break;
1352 
1353 							default:
1354 								TRESPASS();
1355 						}
1356 					}
1357 				}
1358 			}
1359 			break;
1360 		}
1361 	}
1362 }
1363 
1364 
1365 void
1366 GenericAttributeText::FitValue(BString* result, const BPoseView* view)
1367 {
1368 	if (fValueDirty)
1369 		ReadValue(&fFullValueText);
1370 
1371 	fOldWidth = fColumn->Width();
1372 
1373 	if (!fValueIsDefined) {
1374 		*result = "-";
1375 		fTruncatedWidth = TruncString(result, fFullValueText.String(),
1376 			fFullValueText.Length(), view, fOldWidth);
1377 		fDirty = false;
1378 		return;
1379 	}
1380 
1381 	char buffer[256];
1382 
1383 	switch (fColumn->AttrType()) {
1384 		case B_SIZE_T_TYPE:
1385 			TruncFileSizeBase(result, fValue.int32t, view, fOldWidth);
1386 			return;
1387 
1388 		case B_SSIZE_T_TYPE:
1389 			if (fValue.int32t > 0) {
1390 				TruncFileSizeBase(result, fValue.int32t, view, fOldWidth);
1391 				return;
1392 			}
1393 			sprintf(buffer, "%s", strerror(fValue.int32t));
1394 			fFullValueText = buffer;
1395 			break;
1396 
1397 		case B_STRING_TYPE:
1398 			fTruncatedWidth = TruncString(result, fFullValueText.String(),
1399 				fFullValueText.Length(), view, fOldWidth);
1400 			fDirty = false;
1401 			return;
1402 
1403 		case B_OFF_T_TYPE:
1404 			// As a side effect update the fFullValueText to the string
1405 			// representation of value
1406 			TruncFileSize(&fFullValueText, fValue.off_tt, view, 100000);
1407 			fTruncatedWidth = TruncFileSize(result, fValue.off_tt, view,
1408 				fOldWidth);
1409 			fDirty = false;
1410 			return;
1411 
1412 		case B_TIME_TYPE:
1413 			// As a side effect update the fFullValueText to the string
1414 			// representation of value
1415 			TruncTime(&fFullValueText, fValue.time_tt, view, 100000);
1416 			fTruncatedWidth = TruncTime(result, fValue.time_tt, view,
1417 				fOldWidth);
1418 			fDirty = false;
1419 			return;
1420 
1421 		case B_BOOL_TYPE:
1422 			// For now use true/false, would be nice to be able to set
1423 			// the value text
1424 
1425  			sprintf(buffer, "%s", fValue.boolt ? "true" : "false");
1426 			fFullValueText = buffer;
1427 			break;
1428 
1429 		case B_CHAR_TYPE:
1430 			// Make sure no non-printable characters are displayed:
1431 			if (!isprint(fValue.uint8t)) {
1432 				*result = "-";
1433 				fTruncatedWidth = TruncString(result, fFullValueText.String(),
1434 					fFullValueText.Length(), view, fOldWidth);
1435 				fDirty = false;
1436 				return;
1437 			}
1438 
1439 			sprintf(buffer, "%c", fValue.uint8t);
1440 			fFullValueText = buffer;
1441 			break;
1442 
1443 		case B_INT8_TYPE:
1444 			sprintf(buffer, "%d", fValue.int8t);
1445 			fFullValueText = buffer;
1446 			break;
1447 
1448 		case B_UINT8_TYPE:
1449 			sprintf(buffer, "%d", fValue.uint8t);
1450 			fFullValueText = buffer;
1451 			break;
1452 
1453 		case B_INT16_TYPE:
1454 			sprintf(buffer, "%d", fValue.int16t);
1455 			fFullValueText = buffer;
1456 			break;
1457 
1458 		case B_UINT16_TYPE:
1459 			sprintf(buffer, "%d", fValue.uint16t);
1460 			fFullValueText = buffer;
1461 			break;
1462 
1463 		case B_INT32_TYPE:
1464 			sprintf(buffer, "%" B_PRId32, fValue.int32t);
1465 			fFullValueText = buffer;
1466 			break;
1467 
1468 		case B_UINT32_TYPE:
1469 			sprintf(buffer, "%" B_PRId32, fValue.uint32t);
1470 			fFullValueText = buffer;
1471 			break;
1472 
1473 		case B_INT64_TYPE:
1474 			sprintf(buffer, "%" B_PRId64, fValue.int64t);
1475 			fFullValueText = buffer;
1476 			break;
1477 
1478 		case B_UINT64_TYPE:
1479 			sprintf(buffer, "%" B_PRId64, fValue.uint64t);
1480 			fFullValueText = buffer;
1481 			break;
1482 
1483 		case B_FLOAT_TYPE:
1484 			snprintf(buffer, sizeof(buffer), "%g", fValue.floatt);
1485 			fFullValueText = buffer;
1486 			break;
1487 
1488 		case B_DOUBLE_TYPE:
1489 			snprintf(buffer, sizeof(buffer), "%g", fValue.doublet);
1490 			fFullValueText = buffer;
1491 			break;
1492 
1493 		default:
1494 			*result = "-";
1495 			fTruncatedWidth = TruncString(result, fFullValueText.String(),
1496 				fFullValueText.Length(), view, fOldWidth);
1497 			fDirty = false;
1498 			return;
1499 	}
1500 	fTruncatedWidth = TruncString(result, buffer, (ssize_t)strlen(buffer), view,
1501 		fOldWidth);
1502 	fDirty = false;
1503 }
1504 
1505 
1506 const char*
1507 GenericAttributeText::ValueAsText(const BPoseView* view)
1508 {
1509 	// TODO: redesign this - this is to make sure the value is valid
1510 	bool oldDirty = fDirty;
1511 	BString result;
1512 	FitValue(&result, view);
1513 	fDirty = oldDirty;
1514 
1515 	return fFullValueText.String();
1516 }
1517 
1518 
1519 int
1520 GenericAttributeText::Compare(WidgetAttributeText& attr, BPoseView*)
1521 {
1522 	GenericAttributeText* compareTo
1523 		= dynamic_cast<GenericAttributeText*>(&attr);
1524 	ASSERT(compareTo);
1525 
1526 	if (fValueDirty)
1527 		ReadValue(&fFullValueText);
1528 	if (compareTo->fValueDirty)
1529 		compareTo->ReadValue(&compareTo->fFullValueText);
1530 
1531 	// Sort undefined values last, regardless of the other value:
1532 	if (!fValueIsDefined)
1533 		return compareTo->fValueIsDefined ? 1 : 0;
1534 	if (!compareTo->fValueIsDefined)
1535 		return -1;
1536 
1537 	switch (fColumn->AttrType()) {
1538 		case B_STRING_TYPE:
1539 			return fFullValueText.ICompare(compareTo->fFullValueText);
1540 
1541 		case B_CHAR_TYPE:
1542 		{
1543 			char vStr[2] = { static_cast<char>(fValue.uint8t), 0 };
1544 			char cStr[2] = { static_cast<char>(compareTo->fValue.uint8t), 0};
1545 
1546 			BString valueStr(vStr);
1547 			BString compareToStr(cStr);
1548 
1549 			return valueStr.ICompare(compareToStr);
1550 		}
1551 
1552 		case B_FLOAT_TYPE:
1553 			return fValue.floatt >= compareTo->fValue.floatt ?
1554 				(fValue.floatt == compareTo->fValue.floatt ? 0 : 1) : -1;
1555 
1556 		case B_DOUBLE_TYPE:
1557 			return fValue.doublet >= compareTo->fValue.doublet ?
1558 				(fValue.doublet == compareTo->fValue.doublet ? 0 : 1) : -1;
1559 
1560 		case B_BOOL_TYPE:
1561 			return fValue.boolt >= compareTo->fValue.boolt ?
1562 				(fValue.boolt == compareTo->fValue.boolt ? 0 : 1) : -1;
1563 
1564 		case B_UINT8_TYPE:
1565 			return fValue.uint8t >= compareTo->fValue.uint8t ?
1566 				(fValue.uint8t == compareTo->fValue.uint8t ? 0 : 1) : -1;
1567 
1568 		case B_INT8_TYPE:
1569 			return fValue.int8t >= compareTo->fValue.int8t ?
1570 					(fValue.int8t == compareTo->fValue.int8t ? 0 : 1) : -1;
1571 
1572 		case B_UINT16_TYPE:
1573 			return fValue.uint16t >= compareTo->fValue.uint16t ?
1574 				(fValue.uint16t == compareTo->fValue.uint16t ? 0 : 1) : -1;
1575 
1576 		case B_INT16_TYPE:
1577 			return fValue.int16t >= compareTo->fValue.int16t ?
1578 				(fValue.int16t == compareTo->fValue.int16t ? 0 : 1) : -1;
1579 
1580 		case B_UINT32_TYPE:
1581 			return fValue.uint32t >= compareTo->fValue.uint32t ?
1582 				(fValue.uint32t == compareTo->fValue.uint32t ? 0 : 1) : -1;
1583 
1584 		case B_TIME_TYPE:
1585 			// time_t typedef'd to a long, i.e. a int32
1586 		case B_INT32_TYPE:
1587 			return fValue.int32t >= compareTo->fValue.int32t ?
1588 				(fValue.int32t == compareTo->fValue.int32t ? 0 : 1) : -1;
1589 
1590 		case B_OFF_T_TYPE:
1591 			// off_t typedef'd to a long long, i.e. a int64
1592 		case B_INT64_TYPE:
1593 			return fValue.int64t >= compareTo->fValue.int64t ?
1594 				(fValue.int64t == compareTo->fValue.int64t ? 0 : 1) : -1;
1595 
1596 		case B_UINT64_TYPE:
1597 		default:
1598 			return fValue.uint64t >= compareTo->fValue.uint64t ?
1599 				(fValue.uint64t == compareTo->fValue.uint64t ? 0 : 1) : -1;
1600 	}
1601 	return 0;
1602 }
1603 
1604 
1605 bool
1606 GenericAttributeText::CommitEditedText(BTextView* textView)
1607 {
1608 	ASSERT(fColumn->Editable());
1609 	const char* text = textView->Text();
1610 
1611 	if (fFullValueText == text)
1612 		// no change
1613 		return false;
1614 
1615 	if (!CommitEditedTextFlavor(textView))
1616 		return false;
1617 
1618 	// update text and width in this widget
1619 	fFullValueText = text;
1620 	// cause re-truncation
1621 	fDirty = true;
1622 	fValueDirty = true;
1623 
1624 	return true;
1625 }
1626 
1627 
1628 void
1629 GenericAttributeText::SetUpEditing(BTextView* textView)
1630 {
1631 	textView->SetMaxBytes(kGenericReadBufferSize - 1);
1632 	textView->SetText(fFullValueText.String(), fFullValueText.Length());
1633 }
1634 
1635 
1636 bool
1637 GenericAttributeText::CommitEditedTextFlavor(BTextView* textView)
1638 {
1639 	BNode node(fModel->EntryRef());
1640 
1641 	if (node.InitCheck() != B_OK)
1642 		return false;
1643 
1644 	uint32 type = fColumn->AttrType();
1645 
1646 	if (type != B_STRING_TYPE
1647 		&& type != B_UINT64_TYPE
1648 		&& type != B_UINT32_TYPE
1649 		&& type != B_UINT16_TYPE
1650 		&& type != B_UINT8_TYPE
1651 		&& type != B_INT64_TYPE
1652 		&& type != B_INT32_TYPE
1653 		&& type != B_INT16_TYPE
1654 		&& type != B_INT8_TYPE
1655 		&& type != B_OFF_T_TYPE
1656 		&& type != B_TIME_TYPE
1657 		&& type != B_FLOAT_TYPE
1658 		&& type != B_DOUBLE_TYPE
1659 		&& type != B_CHAR_TYPE
1660 		&& type != B_BOOL_TYPE) {
1661 		BAlert* alert = new BAlert("",
1662 			B_TRANSLATE("Sorry, you cannot edit that attribute."),
1663 			B_TRANSLATE("Cancel"),
1664 			0, 0, B_WIDTH_AS_USUAL, B_STOP_ALERT);
1665 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1666 		alert->Go();
1667 		return false;
1668 	}
1669 
1670 	const char* columnName = fColumn->AttrName();
1671 	ssize_t size = 0;
1672 
1673 	switch (type) {
1674 		case B_STRING_TYPE:
1675 			size = fModel->WriteAttr(columnName, type, 0, textView->Text(),
1676 				(size_t)(textView->TextLength() + 1));
1677 			break;
1678 
1679 		case B_BOOL_TYPE:
1680 		{
1681 			bool value = strncasecmp(textView->Text(), "0", 1) != 0
1682 				&& strncasecmp(textView->Text(), "off", 2) != 0
1683 				&& strncasecmp(textView->Text(), "no", 3) != 0
1684 				&& strncasecmp(textView->Text(), "false", 4) != 0
1685 				&& strlen(textView->Text()) != 0;
1686 
1687 			size = fModel->WriteAttr(columnName, type, 0, &value, sizeof(bool));
1688 			break;
1689 		}
1690 
1691 		case B_CHAR_TYPE:
1692 		{
1693 			char ch;
1694 			sscanf(textView->Text(), "%c", &ch);
1695 			//Check if we read the start of a multi-byte glyph:
1696 			if (!isprint(ch)) {
1697 				BAlert* alert = new BAlert("",
1698 					B_TRANSLATE("Sorry, the 'Character' "
1699 					"attribute cannot store a multi-byte glyph."),
1700 					B_TRANSLATE("Cancel"),
1701 					0, 0, B_WIDTH_AS_USUAL, B_STOP_ALERT);
1702 				alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1703 				alert->Go();
1704 				return false;
1705 			}
1706 
1707 			size = fModel->WriteAttr(columnName, type, 0, &ch, sizeof(char));
1708 			break;
1709 		}
1710 
1711 		case B_FLOAT_TYPE:
1712 		{
1713 			float floatVal;
1714 
1715 			if (sscanf(textView->Text(), "%f", &floatVal) == 1) {
1716 				fValueIsDefined = true;
1717 				fValue.floatt = floatVal;
1718 				size = fModel->WriteAttr(columnName, type, 0, &floatVal,
1719 					sizeof(float));
1720 			} else {
1721 				// If the value was already defined, it's on disk.
1722 				// Otherwise not.
1723 				return fValueIsDefined;
1724 			}
1725 			break;
1726 		}
1727 
1728 		case B_DOUBLE_TYPE:
1729 		{
1730 			double doubleVal;
1731 
1732 			if (sscanf(textView->Text(), "%lf", &doubleVal) == 1) {
1733 				fValueIsDefined = true;
1734 				fValue.doublet = doubleVal;
1735 				size = fModel->WriteAttr(columnName, type, 0, &doubleVal,
1736 					sizeof(double));
1737 			} else {
1738 				// If the value was already defined, it's on disk.
1739 				// Otherwise not.
1740 				return fValueIsDefined;
1741 			}
1742 			break;
1743 		}
1744 
1745 		case B_TIME_TYPE:
1746 		case B_OFF_T_TYPE:
1747 		case B_UINT64_TYPE:
1748 		case B_UINT32_TYPE:
1749 		case B_UINT16_TYPE:
1750 		case B_UINT8_TYPE:
1751 		case B_INT64_TYPE:
1752 		case B_INT32_TYPE:
1753 		case B_INT16_TYPE:
1754 		case B_INT8_TYPE:
1755 		{
1756 			GenericValueStruct tmp;
1757 			size_t scalarSize = 0;
1758 
1759 			switch (type) {
1760 				case B_TIME_TYPE:
1761 					tmp.time_tt = parsedate(textView->Text(), time(0));
1762 					scalarSize = sizeof(time_t);
1763 					break;
1764 
1765 				// do some size independent conversion on builtin types
1766 				case B_OFF_T_TYPE:
1767 					tmp.off_tt = StringToScalar(textView->Text());
1768 					scalarSize = sizeof(off_t);
1769 					break;
1770 
1771 				case B_UINT64_TYPE:
1772 				case B_INT64_TYPE:
1773 					tmp.int64t = StringToScalar(textView->Text());
1774 					scalarSize = sizeof(int64);
1775 					break;
1776 
1777 				case B_UINT32_TYPE:
1778 				case B_INT32_TYPE:
1779 					tmp.int32t = (int32)StringToScalar(textView->Text());
1780 					scalarSize = sizeof(int32);
1781 					break;
1782 
1783 				case B_UINT16_TYPE:
1784 				case B_INT16_TYPE:
1785 					tmp.int16t = (int16)StringToScalar(textView->Text());
1786 					scalarSize = sizeof(int16);
1787 					break;
1788 
1789 				case B_UINT8_TYPE:
1790 				case B_INT8_TYPE:
1791 					tmp.int8t = (int8)StringToScalar(textView->Text());
1792 					scalarSize = sizeof(int8);
1793 					break;
1794 
1795 				default:
1796 					TRESPASS();
1797 
1798 			}
1799 
1800 			size = fModel->WriteAttr(columnName, type, 0, &tmp, scalarSize);
1801 			break;
1802 		}
1803 	}
1804 
1805 	if (size < 0) {
1806 		BAlert* alert = new BAlert("",
1807 			B_TRANSLATE("There was an error writing the attribute."),
1808 			B_TRANSLATE("Cancel"),
1809 			0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
1810 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1811 		alert->Go();
1812 
1813 		fValueIsDefined = false;
1814 		return false;
1815 	}
1816 
1817 	fValueIsDefined = true;
1818 	return true;
1819 }
1820 
1821 
1822 // #pragma mark - display as: duration
1823 
1824 
1825 DurationAttributeText::DurationAttributeText(const Model* model,
1826 	const BColumn* column)
1827 	:
1828 	GenericAttributeText(model, column)
1829 {
1830 }
1831 
1832 
1833 // TODO: support editing!
1834 
1835 
1836 void
1837 DurationAttributeText::FitValue(BString* result, const BPoseView* view)
1838 {
1839 	if (fValueDirty)
1840 		ReadValue(&fFullValueText);
1841 
1842 	fOldWidth = fColumn->Width();
1843 	fDirty = false;
1844 
1845 	if (!fValueIsDefined) {
1846 		*result = "-";
1847 		fTruncatedWidth = TruncString(result, fFullValueText.String(),
1848 			fFullValueText.Length(), view, fOldWidth);
1849 		return;
1850 	}
1851 
1852 	int64 time = 0;
1853 
1854 	switch (fColumn->AttrType()) {
1855 		case B_TIME_TYPE:
1856 			time = fValue.time_tt * 1000000LL;
1857 			break;
1858 
1859 		case B_INT8_TYPE:
1860 			time = fValue.int8t * 1000000LL;
1861 			break;
1862 
1863 		case B_INT16_TYPE:
1864 			time = fValue.int16t * 1000000LL;
1865 			break;
1866 
1867 		case B_INT32_TYPE:
1868 			time = fValue.int32t * 1000000LL;
1869 			break;
1870 
1871 		case B_INT64_TYPE:
1872 			time = fValue.int64t;
1873 			break;
1874 	}
1875 
1876 	// TODO: ignores micro seconds for now
1877 	int32 seconds = time / 1000000LL;
1878 
1879 	bool negative = seconds < 0;
1880 	if (negative)
1881 		seconds = -seconds;
1882 
1883 	int32 hours = seconds / 3600;
1884 	seconds -= hours * 3600;
1885 	int32 minutes = seconds / 60;
1886 	seconds = seconds % 60;
1887 
1888 	char buffer[256];
1889 	if (hours > 0) {
1890 		snprintf(buffer, sizeof(buffer), "%s%" B_PRId32 ":%02" B_PRId32 ":%02"
1891 			B_PRId32, negative ? "-" : "", hours, minutes, seconds);
1892 	} else {
1893 		snprintf(buffer, sizeof(buffer), "%s%" B_PRId32 ":%02" B_PRId32,
1894 			negative ? "-" : "", minutes, seconds);
1895 	}
1896 
1897 	fFullValueText = buffer;
1898 
1899 	fTruncatedWidth = TruncString(result, fFullValueText.String(),
1900 		fFullValueText.Length(), view, fOldWidth);
1901 }
1902 
1903 
1904 // #pragma mark - display as: checkbox
1905 
1906 
1907 CheckboxAttributeText::CheckboxAttributeText(const Model* model,
1908 	const BColumn* column)
1909 	:
1910 	GenericAttributeText(model, column),
1911 	fOnChar("✖"),
1912 	fOffChar("-")
1913 {
1914 	// TODO: better have common data in the column object!
1915 	if (const char* separator = strchr(column->DisplayAs(), ':')) {
1916 		BString chars(separator + 1);
1917 		int32 length;
1918 		const char* c = chars.CharAt(0, &length);
1919 		fOnChar.SetTo(c, length);
1920 		if (c[length]) {
1921 			c = chars.CharAt(1, &length);
1922 			fOffChar.SetTo(c, length);
1923 		}
1924 	}
1925 }
1926 
1927 
1928 void
1929 CheckboxAttributeText::SetUpEditing(BTextView* view)
1930 {
1931 	// TODO: support editing for real!
1932 	BString result;
1933 	GenericAttributeText::FitValue(&result, NULL);
1934 	GenericAttributeText::SetUpEditing(view);
1935 }
1936 
1937 
1938 void
1939 CheckboxAttributeText::FitValue(BString* result, const BPoseView* view)
1940 {
1941 	if (fValueDirty)
1942 		ReadValue(&fFullValueText);
1943 
1944 	fOldWidth = fColumn->Width();
1945 	fDirty = false;
1946 
1947 	if (!fValueIsDefined) {
1948 		*result = fOffChar;
1949 		fTruncatedWidth = TruncString(result, fFullValueText.String(),
1950 			fFullValueText.Length(), view, fOldWidth);
1951 		return;
1952 	}
1953 
1954 	bool checked = false;
1955 
1956 	switch (fColumn->AttrType()) {
1957 		case B_BOOL_TYPE:
1958 			checked = fValue.boolt;
1959 			break;
1960 
1961 		case B_INT8_TYPE:
1962 		case B_UINT8_TYPE:
1963 			checked = fValue.int8t != 0;
1964 			break;
1965 
1966 		case B_INT16_TYPE:
1967 		case B_UINT16_TYPE:
1968 			checked = fValue.int16t != 0;
1969 			break;
1970 
1971 		case B_INT32_TYPE:
1972 		case B_UINT32_TYPE:
1973 			checked = fValue.int32t != 0;
1974 			break;
1975 	}
1976 
1977 	fFullValueText = checked ? fOnChar : fOffChar;
1978 
1979 	fTruncatedWidth = TruncString(result, fFullValueText.String(),
1980 		fFullValueText.Length(), view, fOldWidth);
1981 }
1982 
1983 
1984 // #pragma mark - display as: rating
1985 
1986 
1987 RatingAttributeText::RatingAttributeText(const Model* model,
1988 	const BColumn* column)
1989 	:
1990 	GenericAttributeText(model, column),
1991 	fCount(5),
1992 	fMax(10)
1993 {
1994 	// TODO: support different star counts/max via specifier
1995 }
1996 
1997 
1998 void
1999 RatingAttributeText::SetUpEditing(BTextView* view)
2000 {
2001 	// TODO: support editing for real!
2002 	BString result;
2003 	GenericAttributeText::FitValue(&result, NULL);
2004 	GenericAttributeText::SetUpEditing(view);
2005 }
2006 
2007 
2008 void
2009 RatingAttributeText::FitValue(BString* result, const BPoseView* view)
2010 {
2011 	if (fValueDirty)
2012 		ReadValue(&fFullValueText);
2013 
2014 	fOldWidth = fColumn->Width();
2015 	fDirty = false;
2016 
2017 	int64 rating = 0;
2018 
2019 	if (fValueIsDefined) {
2020 		switch (fColumn->AttrType()) {
2021 			case B_INT8_TYPE:
2022 				rating = fValue.int8t;
2023 				break;
2024 
2025 			case B_INT16_TYPE:
2026 				rating = fValue.int16t;
2027 				break;
2028 
2029 			case B_INT32_TYPE:
2030 				rating = fValue.int32t;
2031 				break;
2032 		}
2033 	}
2034 
2035 	if (rating > fMax)
2036 		rating = fMax;
2037 	if (rating < 0)
2038 		rating = 0;
2039 
2040 	int32 steps = fMax / fCount;
2041 	fFullValueText = "";
2042 
2043 	for (int32 i = 0; i < fCount; i++) {
2044 		if (rating > i * steps)
2045 			fFullValueText += "★";
2046 		else
2047 			fFullValueText += "☆";
2048 	}
2049 
2050 	fTruncatedWidth = TruncString(result, fFullValueText.String(),
2051 		fFullValueText.Length(), view, fOldWidth);
2052 }
2053 
2054 
2055 // #pragma mark -
2056 
2057 
2058 OpenWithRelationAttributeText::OpenWithRelationAttributeText(const Model* model,
2059 	const BColumn* column, const BPoseView* view)
2060 	:
2061 	ScalarAttributeText(model, column),
2062 	fPoseView(view)
2063 {
2064 }
2065 
2066 
2067 int64
2068 OpenWithRelationAttributeText::ReadValue()
2069 {
2070 	fValueDirty = false;
2071 
2072 	const OpenWithPoseView* view
2073 		= dynamic_cast<const OpenWithPoseView*>(fPoseView);
2074 	if (view != NULL) {
2075 		fValue = view->OpenWithRelation(fModel);
2076 		fValueIsDefined = true;
2077 	}
2078 
2079 	return fValue;
2080 }
2081 
2082 
2083 float
2084 OpenWithRelationAttributeText::PreferredWidth(const BPoseView* pose) const
2085 {
2086 	BString widthString;
2087 	TruncString(&widthString, fRelationText.String(), fRelationText.Length(),
2088 		pose, 500, B_TRUNCATE_END);
2089 	return pose->StringWidth(widthString.String());
2090 }
2091 
2092 
2093 void
2094 OpenWithRelationAttributeText::FitValue(BString* result, const BPoseView* view)
2095 {
2096 	if (fValueDirty)
2097 		ReadValue();
2098 
2099 	ASSERT(view == fPoseView);
2100 	const OpenWithPoseView* launchWithView
2101 		= dynamic_cast<const OpenWithPoseView*>(view);
2102 
2103 	if (launchWithView)
2104 		launchWithView->OpenWithRelationDescription(fModel, &fRelationText);
2105 
2106 	fOldWidth = fColumn->Width();
2107 	fTruncatedWidth = TruncString(result, fRelationText.String(),
2108 		fRelationText.Length(), view, fOldWidth, B_TRUNCATE_END);
2109 	fDirty = false;
2110 }
2111 
2112 
2113 VersionAttributeText::VersionAttributeText(const Model* model,
2114 	const BColumn* column, bool app)
2115 	:
2116 	StringAttributeText(model, column),
2117 	fAppVersion(app)
2118 {
2119 }
2120 
2121 
2122 void
2123 VersionAttributeText::ReadValue(BString* result)
2124 {
2125 	fValueDirty = false;
2126 
2127 	BModelOpener opener(fModel);
2128 	BFile* file = dynamic_cast<BFile*>(fModel->Node());
2129 	if (file) {
2130 		BAppFileInfo info(file);
2131 		version_info version;
2132 		if (info.InitCheck() == B_OK
2133 			&& info.GetVersionInfo(&version, fAppVersion
2134 					? B_APP_VERSION_KIND : B_SYSTEM_VERSION_KIND) == B_OK) {
2135 			*result = version.short_info;
2136 			return;
2137 		}
2138 	}
2139 	*result = "-";
2140 }
2141