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