xref: /haiku/src/kits/tracker/WidgetAttributeText.cpp (revision 508f54795f39c3e7552d87c95aae9dd8ec6f505b)
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 = "TiB";
226 			floatValue = (float)value / kTBSize;
227 		} else if (value >= kGBSize) {
228 			suffix = "GiB";
229 			floatValue = (float)value / kGBSize;
230 		} else if (value >= kMBSize) {
231 			suffix = "MiB";
232 			floatValue = (float)value / kMBSize;
233 		} else {
234 			ASSERT(value >= kKBSize);
235 			suffix = "KiB";
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(),
598 		compareTo->ValueAsText(view));
599 }
600 
601 
602 bool
603 StringAttributeText::CommitEditedText(BTextView *textView)
604 {
605 	ASSERT(fColumn->Editable());
606 	const char *text = textView->Text();
607 
608 	if (fFullValueText == text)
609 		// no change
610 		return false;
611 
612 	if (textView->TextLength() == 0)
613 		// cannot do an empty name
614 		return false;
615 
616 	// cause re-truncation
617 	fDirty = true;
618 
619 	if (!CommitEditedTextFlavor(textView))
620 		return false;
621 
622 	// update text and width in this widget
623 	fFullValueText = text;
624 
625 	return true;
626 }
627 
628 
629 //	#pragma mark -
630 
631 
632 ScalarAttributeText::ScalarAttributeText(const Model *model,
633 	const BColumn *column)
634 	:	WidgetAttributeText(model, column),
635 		fValueDirty(true)
636 {
637 }
638 
639 
640 int64
641 ScalarAttributeText::Value()
642 {
643 	if (fValueDirty)
644 		fValue = ReadValue();
645 	return fValue;
646 }
647 
648 
649 bool
650 ScalarAttributeText::CheckAttributeChanged()
651 {
652 	int64 newValue = ReadValue();
653 	if (newValue == fValue)
654 		return false;
655 
656 	fValue = newValue;
657 	fDirty = true;		// have to redo fitted string
658 	return true;
659 }
660 
661 
662 float
663 ScalarAttributeText::PreferredWidth(const BPoseView *pose) const
664 {
665 	BString widthString;
666 	widthString << fValue;
667 	return pose->StringWidth(widthString.String());
668 }
669 
670 
671 int
672 ScalarAttributeText::Compare(WidgetAttributeText &attr, BPoseView *)
673 {
674 	ScalarAttributeText *compareTo
675 		= dynamic_cast<ScalarAttributeText *>(&attr);
676 	ASSERT(compareTo);
677 		// make sure we're not comparing apples and oranges
678 
679 	if (fValueDirty)
680 		fValue = ReadValue();
681 
682 	return fValue >= compareTo->Value()
683 		? (fValue == compareTo->Value() ? 0 : 1) : -1;
684 }
685 
686 
687 //	#pragma mark -
688 
689 
690 PathAttributeText::PathAttributeText(const Model *model, const BColumn *column)
691 	: StringAttributeText(model, column)
692 {
693 }
694 
695 
696 void
697 PathAttributeText::ReadValue(BString *result)
698 {
699 	// get the path
700 	BEntry entry(fModel->EntryRef());
701 	BPath path;
702 
703 	if (entry.InitCheck() == B_OK && entry.GetPath(&path) == B_OK) {
704 		*result = path.Path();
705 		TruncateLeaf(result);
706 	} else
707 		*result = "-";
708 	fValueDirty = false;
709 }
710 
711 
712 //	#pragma mark -
713 
714 
715 OriginalPathAttributeText::OriginalPathAttributeText(const Model *model,
716 		const BColumn *column)
717 	: StringAttributeText(model, column)
718 {
719 }
720 
721 
722 void
723 OriginalPathAttributeText::ReadValue(BString *result)
724 {
725 	BEntry entry(fModel->EntryRef());
726 	BPath path;
727 
728 	// get the original path
729 	if (entry.InitCheck() == B_OK && FSGetOriginalPath(&entry, &path) == B_OK)
730 		*result = path.Path();
731 	else
732 		*result = "-";
733 	fValueDirty = false;
734 }
735 
736 
737 //	#pragma mark -
738 
739 
740 KindAttributeText::KindAttributeText(const Model *model, const BColumn *column)
741 	: StringAttributeText(model, column)
742 {
743 }
744 
745 
746 void
747 KindAttributeText::ReadValue(BString *result)
748 {
749 	BMimeType mime;
750 	char desc[B_MIME_TYPE_LENGTH];
751 
752 	// get the mime type
753 	if (mime.SetType(fModel->MimeType()) != B_OK)
754 		*result = "Unknown";
755 	// get the short mime type description
756 	else if (mime.GetShortDescription(desc) == B_OK)
757 		*result = desc;
758 	else
759 		*result = fModel->MimeType();
760 	fValueDirty = false;
761 }
762 
763 
764 //	#pragma mark -
765 
766 
767 NameAttributeText::NameAttributeText(const Model *model, const BColumn *column)
768 	: StringAttributeText(model, column)
769 {
770 }
771 
772 
773 int
774 NameAttributeText::Compare(WidgetAttributeText &attr, BPoseView *view)
775 {
776 	NameAttributeText *compareTo = dynamic_cast<NameAttributeText *>(&attr);
777 
778 	ASSERT(compareTo);
779 
780 	if (fValueDirty)
781 		ReadValue(&fFullValueText);
782 
783 	if (NameAttributeText::sSortFolderNamesFirst)
784 		return fModel->CompareFolderNamesFirst(attr.TargetModel());
785 
786 	return NaturalCompare(fFullValueText.String(), compareTo->ValueAsText(view));
787 }
788 
789 
790 void
791 NameAttributeText::ReadValue(BString *result)
792 {
793 #ifdef DEBUG
794 // x86 support :-)
795 	if ((modifiers() & B_CAPS_LOCK) != 0) {
796 		if (fModel->IsVolume()) {
797 			BVolumeRoster roster;
798 			roster.Rewind();
799 			BVolume volume;
800 			char device = 'A';
801 			while (roster.GetNextVolume(&volume) == B_OK) {
802 				char name[256];
803 				if (volume.GetName(name) == B_OK
804 					&& strcmp(name, fModel->Name()) == 0) {
805 					*result += device;
806 					*result += ':';
807 					fValueDirty = false;
808 					return;
809 				}
810 				device++;
811 			}
812 		}
813 		const char *modelName = fModel->Name();
814 		bool hasDot = strstr(".", modelName) != 0;
815 		for (int32 index = 0; index < 8; index++) {
816 			if (!modelName[index] || modelName[index] == '.')
817 				break;
818 			*result += toupper(modelName[index]);
819 		}
820 		if (hasDot) {
821 			modelName = strstr(".", modelName);
822 			for (int32 index = 0; index < 4; index++) {
823 				if (!modelName[index])
824 					break;
825 				*result += toupper(modelName[index]);
826 			}
827 		} else if (fModel->IsExecutable())
828 			*result += ".EXE";
829 
830 	} else
831 #endif
832 	*result = fModel->Name();
833 
834 	fValueDirty = false;
835 }
836 
837 
838 void
839 NameAttributeText::FitValue(BString *result, const BPoseView *view)
840 {
841 	if (fValueDirty)
842 		ReadValue(&fFullValueText);
843 	fOldWidth = fColumn->Width();
844 	fTruncatedWidth = TruncString(result, fFullValueText.String(),
845 		fFullValueText.Length(), view, fOldWidth, B_TRUNCATE_END);
846 	fDirty = false;
847 }
848 
849 
850 void
851 NameAttributeText::SetUpEditing(BTextView *textView)
852 {
853 	DisallowFilenameKeys(textView);
854 
855 	textView->SetMaxBytes(B_FILE_NAME_LENGTH);
856 	textView->SetText(fFullValueText.String(), fFullValueText.Length());
857 }
858 
859 
860 bool
861 NameAttributeText::CommitEditedTextFlavor(BTextView *textView)
862 {
863 	const char *text = textView->Text();
864 
865 	BEntry entry(fModel->EntryRef());
866 	if (entry.InitCheck() != B_OK)
867 		return false;
868 
869 	BDirectory	parent;
870 	if (entry.GetParent(&parent) != B_OK)
871 		return false;
872 
873 	bool removeExisting = false;
874 	if (parent.Contains(text)) {
875 		BAlert *alert = new BAlert("", "That name is already taken. "
876 				"Please type another one.", "Replace other file", "OK", NULL,
877 				B_WIDTH_AS_USUAL, B_WARNING_ALERT);
878 
879 		alert->SetShortcut(0, 'r');
880 
881 		if (alert->Go())
882 			return false;
883 
884 		removeExisting = true;
885 	}
886 
887 	// ToDo:
888 	// use model-flavor specific virtuals for all of these special
889 	// renamings
890 	status_t result;
891 	if (fModel->IsVolume()) {
892 		BVolume	volume(fModel->NodeRef()->device);
893 		result = volume.InitCheck();
894 		if (result == B_OK) {
895 			RenameVolumeUndo undo(volume, text);
896 
897 			result = volume.SetName(text);
898 			if (result != B_OK)
899 				undo.Remove();
900 		}
901 	} else {
902 		if (fModel->IsQuery()) {
903 			BModelWriteOpener opener(fModel);
904 			ASSERT(fModel->Node());
905 			MoreOptionsStruct::SetQueryTemporary(fModel->Node(), false);
906 		}
907 
908 		RenameUndo undo(entry, text);
909 
910 		result = entry.Rename(text, removeExisting);
911 		if (result != B_OK)
912 			undo.Remove();
913 	}
914 
915 	return result == B_OK;
916 }
917 
918 bool NameAttributeText::sSortFolderNamesFirst = false;
919 
920 void
921 NameAttributeText::SetSortFolderNamesFirst(bool enabled)
922 {
923 	NameAttributeText::sSortFolderNamesFirst = enabled;
924 }
925 
926 
927 //	#pragma mark -
928 
929 #ifdef OWNER_GROUP_ATTRIBUTES
930 
931 OwnerAttributeText::OwnerAttributeText(const Model *model,
932 		const BColumn *column)
933 	: StringAttributeText(model, column)
934 {
935 }
936 
937 
938 void
939 OwnerAttributeText::ReadValue(BString *result)
940 {
941 	uid_t nodeOwner = fModel->StatBuf()->st_uid;
942 	BString user;
943 
944 	if (nodeOwner == 0) {
945 		if (getenv("USER") != NULL)
946 			user << getenv("USER");
947 		else
948 			user << "root";
949 	} else
950 		user << nodeOwner;
951 	*result = user.String();
952 
953 	fValueDirty = false;
954 }
955 
956 
957 GroupAttributeText::GroupAttributeText(const Model *model,
958 		const BColumn *column)
959 	: StringAttributeText(model, column)
960 {
961 }
962 
963 
964 void
965 GroupAttributeText::ReadValue(BString *result)
966 {
967 	gid_t nodeGroup = fModel->StatBuf()->st_gid;
968 	BString group;
969 
970 	if (nodeGroup == 0) {
971 		if (getenv("GROUP") != NULL)
972 			group << getenv("GROUP");
973 		else
974 			group << "0";
975 	} else
976 		group << nodeGroup;
977 	*result = group.String();
978 
979 	fValueDirty = false;
980 }
981 
982 #endif  /* OWNER_GROUP_ATTRIBUTES */
983 
984 ModeAttributeText::ModeAttributeText(const Model *model, const BColumn *column)
985 	: StringAttributeText(model, column)
986 {
987 }
988 
989 
990 void
991 ModeAttributeText::ReadValue(BString *result)
992 {
993 	mode_t mode = fModel->StatBuf()->st_mode;
994 	mode_t baseMask = 00400;
995 	char buffer[11];
996 
997 	char *scanner = buffer;
998 
999 	if (S_ISDIR(mode))
1000 		*scanner++ = 'd';
1001 	else if (S_ISLNK(mode))
1002 		*scanner++ = 'l';
1003 	else if (S_ISBLK(mode))
1004 		*scanner++ = 'b';
1005 	else if (S_ISCHR(mode))
1006 		*scanner++ = 'c';
1007 	else
1008 		*scanner++ = '-';
1009 
1010 	for (int32 index = 0; index < 9; index++) {
1011 		*scanner++ = (mode & baseMask) ? "rwx"[index % 3] : '-';
1012 		baseMask >>= 1;
1013 	}
1014 
1015 	*scanner = 0;
1016 	*result = buffer;
1017 
1018 	fValueDirty = false;
1019 }
1020 
1021 
1022 //	#pragma mark -
1023 
1024 
1025 SizeAttributeText::SizeAttributeText(const Model *model, const BColumn *column)
1026 	: ScalarAttributeText(model, column)
1027 {
1028 }
1029 
1030 
1031 int64
1032 SizeAttributeText::ReadValue()
1033 {
1034 	fValueDirty = false;
1035 	// get the size
1036 
1037 	if (fModel->IsVolume()) {
1038 		BVolume volume(fModel->NodeRef()->device);
1039 
1040 		return volume.Capacity();
1041 	}
1042 
1043 	if (fModel->IsDirectory() || fModel->IsQuery()
1044 		|| fModel->IsQueryTemplate() || fModel->IsSymLink())
1045 		return kUnknownSize;
1046 
1047 	fValueIsDefined = true;
1048 
1049 	return fModel->StatBuf()->st_size;
1050 }
1051 
1052 
1053 void
1054 SizeAttributeText::FitValue(BString *result, const BPoseView *view)
1055 {
1056 	if (fValueDirty)
1057 		fValue = ReadValue();
1058 	fOldWidth = fColumn->Width();
1059 	fTruncatedWidth = TruncFileSize(result, fValue, view, fOldWidth);
1060 	fDirty = false;
1061 }
1062 
1063 
1064 float
1065 SizeAttributeText::PreferredWidth(const BPoseView *pose) const
1066 {
1067 	if (fValueIsDefined) {
1068 		BString widthString;
1069 		TruncFileSize(&widthString, fValue, pose, 100000);
1070 		return pose->StringWidth(widthString.String());
1071 	}
1072 	return pose->StringWidth("-");
1073 }
1074 
1075 
1076 //	#pragma mark -
1077 
1078 
1079 TimeAttributeText::TimeAttributeText(const Model *model, const BColumn *column)
1080 	: ScalarAttributeText(model, column)
1081 {
1082 }
1083 
1084 
1085 float
1086 TimeAttributeText::PreferredWidth(const BPoseView *pose) const
1087 {
1088 	BString widthString;
1089 	TruncTimeBase(&widthString, fValue, pose, 100000);
1090 	return pose->StringWidth(widthString.String());
1091 }
1092 
1093 
1094 void
1095 TimeAttributeText::FitValue(BString *result, const BPoseView *view)
1096 {
1097 	if (fValueDirty)
1098 		fValue = ReadValue();
1099 	fOldWidth = fColumn->Width();
1100 	fTruncatedWidth = TruncTime(result, fValue, view, fOldWidth);
1101 	fDirty = false;
1102 }
1103 
1104 
1105 bool
1106 TimeAttributeText::CheckSettingsChanged()
1107 {
1108 	bool changed = fLastClockIs24 != fSettings.ClockIs24Hr()
1109 		|| fLastDateOrder != fSettings.DateOrderFormat()
1110 		|| fLastTimeFormatSeparator != fSettings.TimeFormatSeparator();
1111 
1112 	if (changed) {
1113 		fLastClockIs24 = fSettings.ClockIs24Hr();
1114 		fLastDateOrder = fSettings.DateOrderFormat();
1115 		fLastTimeFormatSeparator = fSettings.TimeFormatSeparator();
1116 	}
1117 
1118 	return changed;
1119 }
1120 
1121 
1122 CreationTimeAttributeText::CreationTimeAttributeText(const Model *model,
1123 		const BColumn *column)
1124 	: TimeAttributeText(model, column)
1125 {
1126 }
1127 
1128 
1129 int64
1130 CreationTimeAttributeText::ReadValue()
1131 {
1132 	fValueDirty = false;
1133 	fValueIsDefined = true;
1134 	return fModel->StatBuf()->st_crtime;
1135 }
1136 
1137 ModificationTimeAttributeText::ModificationTimeAttributeText(const Model *model,
1138 		const BColumn *column)
1139 	: TimeAttributeText(model, column)
1140 {
1141 }
1142 
1143 
1144 int64
1145 ModificationTimeAttributeText::ReadValue()
1146 {
1147 	fValueDirty = false;
1148 	fValueIsDefined = true;
1149 	return fModel->StatBuf()->st_mtime;
1150 }
1151 
1152 
1153 //	#pragma mark -
1154 
1155 
1156 GenericAttributeText::GenericAttributeText(const Model *model,
1157 		const BColumn *column)
1158 	: StringAttributeText(model, column)
1159 {
1160 }
1161 
1162 
1163 bool
1164 GenericAttributeText::CheckAttributeChanged()
1165 {
1166 	GenericValueStruct tmpValue = fValue;
1167 	BString tmpString(fFullValueText);
1168 	ReadValue(&fFullValueText);
1169 
1170 	// fDirty could already be true, in that case we mustn't set it to
1171 	// false, even if the attribute text hasn't changed
1172 	bool changed = (fValue.int64t != tmpValue.int64t) || (tmpString != fFullValueText);
1173 	if (changed)
1174 		fDirty = true;
1175 
1176 	return fDirty;
1177 }
1178 
1179 
1180 float
1181 GenericAttributeText::PreferredWidth(const BPoseView *pose) const
1182 {
1183 	return pose->StringWidth(fFullValueText.String());
1184 }
1185 
1186 
1187 void
1188 GenericAttributeText::ReadValue(BString *result)
1189 {
1190 	BModelOpener opener(const_cast<Model *>(fModel));
1191 
1192 	ssize_t length = 0;
1193 	fFullValueText = "-";
1194 	fValue.int64t = 0;
1195 	fValueIsDefined = false;
1196 	fValueDirty = false;
1197 
1198 	if (!fModel->Node())
1199 		return;
1200 
1201 	switch (fColumn->AttrType()) {
1202 		case B_STRING_TYPE:
1203 		{
1204 			char buffer[kGenericReadBufferSize];
1205 			length = fModel->Node()->ReadAttr(fColumn->AttrName(),
1206 				fColumn->AttrType(), 0, buffer, kGenericReadBufferSize - 1);
1207 
1208 			if (length > 0) {
1209 				buffer[length] = '\0';
1210 				// make sure the buffer is null-terminated even if we
1211 				// didn't read the whole attribute in or it wasn't to
1212 				// begin with
1213 
1214 				*result = buffer;
1215 				fValueIsDefined = true;
1216 			}
1217 			break;
1218 		}
1219 
1220 		case B_SSIZE_T_TYPE:
1221 		case B_TIME_TYPE:
1222 		case B_OFF_T_TYPE:
1223 		case B_FLOAT_TYPE:
1224 		case B_BOOL_TYPE:
1225 		case B_CHAR_TYPE:
1226 		case B_INT8_TYPE:
1227 		case B_INT16_TYPE:
1228 		case B_INT32_TYPE:
1229 		case B_INT64_TYPE:
1230 		case B_UINT8_TYPE:
1231 		case B_UINT16_TYPE:
1232 		case B_UINT32_TYPE:
1233 		case B_UINT64_TYPE:
1234 		case B_DOUBLE_TYPE:
1235 		{
1236 			// read in the numerical bit representation and attach it
1237 			// with a type, depending on the bytes that could be read
1238 			attr_info info;
1239 			GenericValueStruct tmp;
1240 			if (fModel->Node()->GetAttrInfo(fColumn->AttrName(), &info) == B_OK) {
1241 				if (info.size && info.size <= sizeof(int64)) {
1242 					length = fModel->Node()->ReadAttr(fColumn->AttrName(),
1243 						fColumn->AttrType(), 0, &tmp, (size_t)info.size);
1244 				}
1245 
1246 				// We used tmp as a block of memory, now set the correct fValue:
1247 
1248 				if (length == info.size) {
1249 					if (fColumn->AttrType() == B_FLOAT_TYPE
1250 						|| fColumn->AttrType() == B_DOUBLE_TYPE) {
1251 						// filter out special float/double types
1252 						switch (info.size) {
1253 							case sizeof(float):
1254 								fValueIsDefined = true;
1255 								fValue.floatt = tmp.floatt;
1256 								break;
1257 
1258 							case sizeof(double):
1259 								fValueIsDefined = true;
1260 								fValue.doublet = tmp.doublet;
1261 								break;
1262 
1263 							default:
1264 								TRESPASS();
1265 						}
1266 					} else {
1267 						// handle the standard data types
1268 						switch (info.size) {
1269 							case sizeof(char):	// Takes care of bool, too.
1270 								fValueIsDefined = true;
1271 								fValue.int8t = tmp.int8t;
1272 								break;
1273 
1274 							case sizeof(int16):
1275 								fValueIsDefined = true;
1276 								fValue.int16t = tmp.int16t;
1277 								break;
1278 
1279 							case sizeof(int32):	// Takes care of time_t, too.
1280 								fValueIsDefined = true;
1281 								fValue.int32t = tmp.int32t;
1282 								break;
1283 
1284 							case sizeof(int64):	// Taked care of off_t, too.
1285 								fValueIsDefined = true;
1286 								fValue.int64t = tmp.int64t;
1287 								break;
1288 
1289 							default:
1290 								TRESPASS();
1291 						}
1292 					}
1293 				}
1294 			}
1295 			break;
1296 		}
1297 	}
1298 }
1299 
1300 
1301 void
1302 GenericAttributeText::FitValue(BString *result, const BPoseView *view)
1303 {
1304 	if (fValueDirty)
1305 		ReadValue(&fFullValueText);
1306 
1307 	fOldWidth = fColumn->Width();
1308 
1309 	if (!fValueIsDefined) {
1310 		*result = "-";
1311 		fTruncatedWidth = TruncString(result, fFullValueText.String(),
1312 			fFullValueText.Length(), view, fOldWidth);
1313 		fDirty = false;
1314 		return;
1315 	}
1316 
1317 	char buffer[256];
1318 
1319 	switch (fColumn->AttrType()) {
1320 		case B_SIZE_T_TYPE:
1321 			TruncFileSizeBase(result, fValue.int32t, view, fOldWidth);
1322 			return;
1323 
1324 		case B_SSIZE_T_TYPE:
1325 			if (fValue.int32t > 0) {
1326 				TruncFileSizeBase(result, fValue.int32t, view, fOldWidth);
1327 				return;
1328 			}
1329 			sprintf(buffer, "%s", strerror(fValue.int32t));
1330 			fFullValueText = buffer;
1331 			break;
1332 
1333 		case B_STRING_TYPE:
1334 			fTruncatedWidth = TruncString(result, fFullValueText.String(),
1335 				fFullValueText.Length(), view, fOldWidth);
1336 			fDirty = false;
1337 			return;
1338 
1339 		case B_OFF_T_TYPE:
1340 			// as a side effect update the fFullValueText to the string representation
1341 			// of value
1342 			TruncFileSize(&fFullValueText, fValue.off_tt, view, 100000);
1343 			fTruncatedWidth = TruncFileSize(result, fValue.off_tt, view, fOldWidth);
1344 			fDirty = false;
1345 			return;
1346 
1347 		case B_TIME_TYPE:
1348 			// as a side effect update the fFullValueText to the string representation
1349 			// of value
1350 			TruncTime(&fFullValueText, fValue.time_tt, view, 100000);
1351 			fTruncatedWidth = TruncTime(result, fValue.time_tt, view, fOldWidth);
1352 			fDirty = false;
1353 			return;
1354 
1355 		case B_BOOL_TYPE:
1356 			// For now use true/false, would be nice to be able to set
1357 			// the value text
1358 
1359  			sprintf(buffer, "%s", fValue.boolt ? "true" : "false");
1360 			fFullValueText = buffer;
1361 			break;
1362 
1363 		case B_CHAR_TYPE:
1364 			// Make sure no non-printable characters are displayed:
1365 			if (!isprint(fValue.uint8t)) {
1366 				*result = "-";
1367 				fTruncatedWidth = TruncString(result, fFullValueText.String(),
1368 					fFullValueText.Length(), view, fOldWidth);
1369 				fDirty = false;
1370 				return;
1371 			}
1372 
1373 			sprintf(buffer, "%c", fValue.uint8t);
1374 			fFullValueText = buffer;
1375 			break;
1376 
1377 		case B_INT8_TYPE:
1378 			sprintf(buffer, "%d", fValue.int8t);
1379 			fFullValueText = buffer;
1380 			break;
1381 
1382 		case B_UINT8_TYPE:
1383 			sprintf(buffer, "%d", fValue.uint8t);
1384 			fFullValueText = buffer;
1385 			break;
1386 
1387 		case B_INT16_TYPE:
1388 			sprintf(buffer, "%d", fValue.int16t);
1389 			fFullValueText = buffer;
1390 			break;
1391 
1392 		case B_UINT16_TYPE:
1393 			sprintf(buffer, "%d", fValue.uint16t);
1394 			fFullValueText = buffer;
1395 			break;
1396 
1397 		case B_INT32_TYPE:
1398 			sprintf(buffer, "%ld", fValue.int32t);
1399 			fFullValueText = buffer;
1400 			break;
1401 
1402 		case B_UINT32_TYPE:
1403 			sprintf(buffer, "%ld", fValue.uint32t);
1404 			fFullValueText = buffer;
1405 			break;
1406 
1407 		case B_INT64_TYPE:
1408 			sprintf(buffer, "%Ld", fValue.int64t);
1409 			fFullValueText = buffer;
1410 			break;
1411 
1412 		case B_UINT64_TYPE:
1413 			sprintf(buffer, "%Ld", fValue.uint64t);
1414 			fFullValueText = buffer;
1415 			break;
1416 
1417 		case B_FLOAT_TYPE:
1418 			snprintf(buffer, sizeof(buffer), "%g", fValue.floatt);
1419 			fFullValueText = buffer;
1420 			break;
1421 
1422 		case B_DOUBLE_TYPE:
1423 			snprintf(buffer, sizeof(buffer), "%g", fValue.doublet);
1424 			fFullValueText = buffer;
1425 			break;
1426 
1427 		default:
1428 			*result = "-";
1429 			fTruncatedWidth = TruncString(result, fFullValueText.String(),
1430 				fFullValueText.Length(), view, fOldWidth);
1431 			fDirty = false;
1432 			return;
1433 	}
1434 	fTruncatedWidth = TruncString(result, buffer, (ssize_t)strlen(buffer), view,
1435 		fOldWidth);
1436 	fDirty = false;
1437 }
1438 
1439 
1440 const char*
1441 GenericAttributeText::ValueAsText(const BPoseView *view)
1442 {
1443 	// TODO: redesign this - this is to make sure the value is valid
1444 	bool oldDirty = fDirty;
1445 	BString result;
1446 	FitValue(&result, view);
1447 	fDirty = oldDirty;
1448 
1449 	return fFullValueText.String();
1450 }
1451 
1452 
1453 int
1454 GenericAttributeText::Compare(WidgetAttributeText &attr, BPoseView *)
1455 {
1456 	GenericAttributeText *compareTo
1457 		= dynamic_cast<GenericAttributeText *>(&attr);
1458 	ASSERT(compareTo);
1459 
1460 	if (fValueDirty)
1461 		ReadValue(&fFullValueText);
1462 	if (compareTo->fValueDirty)
1463 		compareTo->ReadValue(&compareTo->fFullValueText);
1464 
1465 	// Sort undefined values last, regardless of the other value:
1466 	if (!fValueIsDefined)
1467 		return compareTo->fValueIsDefined ? 1 : 0;
1468 	if (!compareTo->fValueIsDefined)
1469 		return -1;
1470 
1471 	switch (fColumn->AttrType()) {
1472 		case B_STRING_TYPE:
1473 			return fFullValueText.ICompare(compareTo->fFullValueText);
1474 
1475 		case B_CHAR_TYPE:
1476 		{
1477 			char vStr[2] = { static_cast<char>(fValue.uint8t), 0 };
1478 			char cStr[2] = { static_cast<char>(compareTo->fValue.uint8t), 0};
1479 
1480 			BString valueStr(vStr);
1481 			BString compareToStr(cStr);
1482 
1483 			return valueStr.ICompare(compareToStr);
1484 		}
1485 
1486 		case B_FLOAT_TYPE:
1487 			return fValue.floatt >= compareTo->fValue.floatt ?
1488 				(fValue.floatt == compareTo->fValue.floatt ? 0 : 1) : -1;
1489 
1490 		case B_DOUBLE_TYPE:
1491 			return fValue.doublet >= compareTo->fValue.doublet ?
1492 				(fValue.doublet == compareTo->fValue.doublet ? 0 : 1) : -1;
1493 
1494 		case B_BOOL_TYPE:
1495 			return fValue.boolt >= compareTo->fValue.boolt ?
1496 				(fValue.boolt == compareTo->fValue.boolt ? 0 : 1) : -1;
1497 
1498 		case B_UINT8_TYPE:
1499 			return fValue.uint8t >= compareTo->fValue.uint8t ?
1500 				(fValue.uint8t == compareTo->fValue.uint8t ? 0 : 1) : -1;
1501 
1502 		case B_INT8_TYPE:
1503 			return fValue.int8t >= compareTo->fValue.int8t ?
1504 					(fValue.int8t == compareTo->fValue.int8t ? 0 : 1) : -1;
1505 
1506 		case B_UINT16_TYPE:
1507 			return fValue.uint16t >= compareTo->fValue.uint16t ?
1508 				(fValue.uint16t == compareTo->fValue.uint16t ? 0 : 1) : -1;
1509 
1510 		case B_INT16_TYPE:
1511 			return fValue.int16t >= compareTo->fValue.int16t ?
1512 				(fValue.int16t == compareTo->fValue.int16t ? 0 : 1) : -1;
1513 
1514 		case B_UINT32_TYPE:
1515 			return fValue.uint32t >= compareTo->fValue.uint32t ?
1516 				(fValue.uint32t == compareTo->fValue.uint32t ? 0 : 1) : -1;
1517 
1518 		case B_TIME_TYPE:
1519 			// time_t typedef'd to a long, i.e. a int32
1520 		case B_INT32_TYPE:
1521 			return fValue.int32t >= compareTo->fValue.int32t ?
1522 				(fValue.int32t == compareTo->fValue.int32t ? 0 : 1) : -1;
1523 
1524 		case B_OFF_T_TYPE:
1525 			// off_t typedef'd to a long long, i.e. a int64
1526 		case B_INT64_TYPE:
1527 			return fValue.int64t >= compareTo->fValue.int64t ?
1528 				(fValue.int64t == compareTo->fValue.int64t ? 0 : 1) : -1;
1529 
1530 		case B_UINT64_TYPE:
1531 		default:
1532 			return fValue.uint64t >= compareTo->fValue.uint64t ?
1533 				(fValue.uint64t == compareTo->fValue.uint64t ? 0 : 1) : -1;
1534 	}
1535 	return 0;
1536 }
1537 
1538 
1539 bool
1540 GenericAttributeText::CommitEditedText(BTextView *textView)
1541 {
1542 	ASSERT(fColumn->Editable());
1543 	const char *text = textView->Text();
1544 
1545 	if (fFullValueText == text)
1546 		// no change
1547 		return false;
1548 
1549 	if (!CommitEditedTextFlavor(textView))
1550 		return false;
1551 
1552 	// update text and width in this widget
1553 	fFullValueText = text;
1554 	// cause re-truncation
1555 	fDirty = true;
1556 	fValueDirty = true;
1557 
1558 	return true;
1559 }
1560 
1561 
1562 void
1563 GenericAttributeText::SetUpEditing(BTextView *textView)
1564 {
1565 	textView->SetMaxBytes(kGenericReadBufferSize - 1);
1566 	textView->SetText(fFullValueText.String(), fFullValueText.Length());
1567 }
1568 
1569 
1570 bool
1571 GenericAttributeText::CommitEditedTextFlavor(BTextView *textView)
1572 {
1573 	BNode node(fModel->EntryRef());
1574 
1575 	if (node.InitCheck() != B_OK)
1576 		return false;
1577 
1578 	uint32 type = fColumn->AttrType();
1579 
1580 	if (type != B_STRING_TYPE
1581 		&& type != B_UINT64_TYPE
1582 		&& type != B_UINT32_TYPE
1583 		&& type != B_UINT16_TYPE
1584 		&& type != B_UINT8_TYPE
1585 		&& type != B_INT64_TYPE
1586 		&& type != B_INT32_TYPE
1587 		&& type != B_INT16_TYPE
1588 		&& type != B_INT8_TYPE
1589 		&& type != B_OFF_T_TYPE
1590 		&& type != B_TIME_TYPE
1591 		&& type != B_FLOAT_TYPE
1592 		&& type != B_DOUBLE_TYPE
1593 		&& type != B_CHAR_TYPE
1594 		&& type != B_BOOL_TYPE) {
1595 		BAlert *alert = new BAlert("", "Sorry, you cannot edit that attribute.",
1596 			"Cancel", 0, 0, B_WIDTH_AS_USUAL, B_STOP_ALERT);
1597 		alert->SetShortcut(0, B_ESCAPE);
1598 		alert->Go();
1599 		return false;
1600 	}
1601 
1602 	const char *columnName = fColumn->AttrName();
1603 	ssize_t size = 0;
1604 
1605 	switch (type) {
1606 		case B_STRING_TYPE:
1607 			size = fModel->WriteAttr(columnName,
1608 				type, 0, textView->Text(), (size_t)(textView->TextLength() + 1));
1609 			break;
1610 
1611 		case B_BOOL_TYPE:
1612 			{
1613 				bool value = strncasecmp(textView->Text(), "0", 1) != 0
1614 					&& strncasecmp(textView->Text(), "off", 2) != 0
1615 					&& strncasecmp(textView->Text(), "no", 3) != 0
1616 					&& strncasecmp(textView->Text(), "false", 4) != 0
1617 					&& strlen(textView->Text()) != 0;
1618 
1619 				size = fModel->WriteAttr(columnName, type, 0, &value, sizeof(bool));
1620 				break;
1621 			}
1622 
1623 		case B_CHAR_TYPE:
1624 			{
1625 				char ch;
1626 				sscanf(textView->Text(), "%c", &ch);
1627 				//Check if we read the start of a multi-byte glyph:
1628 				if (!isprint(ch)) {
1629 					BAlert *alert = new BAlert("", "Sorry, the 'Character' "
1630 						"attribute cannot store a multi-byte glyph.",
1631 						"Cancel", 0, 0, B_WIDTH_AS_USUAL, B_STOP_ALERT);
1632 					alert->SetShortcut(0, B_ESCAPE);
1633 					alert->Go();
1634 					return false;
1635 				}
1636 
1637 				size = fModel->WriteAttr(columnName, type, 0, &ch, sizeof(char));
1638 				break;
1639 			}
1640 
1641 		case B_FLOAT_TYPE:
1642 			{
1643 				float floatVal;
1644 
1645 				if (sscanf(textView->Text(), "%f", &floatVal) == 1) {
1646 					fValueIsDefined = true;
1647 					fValue.floatt = floatVal;
1648 					size = fModel->WriteAttr(columnName, type, 0, &floatVal, sizeof(float));
1649 				} else
1650 					// If the value was already defined, it's on disk. Otherwise not.
1651 					return fValueIsDefined;
1652 				break;
1653 			}
1654 
1655 		case B_DOUBLE_TYPE:
1656 			{
1657 				double doubleVal;
1658 
1659 				if (sscanf(textView->Text(), "%lf", &doubleVal) == 1) {
1660 					fValueIsDefined = true;
1661 					fValue.doublet = doubleVal;
1662 					size = fModel->WriteAttr(columnName, type, 0, &doubleVal, sizeof(double));
1663 				} else
1664 					// If the value was already defined, it's on disk. Otherwise not.
1665 					return fValueIsDefined;
1666 				break;
1667 			}
1668 
1669 		case B_TIME_TYPE:
1670 		case B_OFF_T_TYPE:
1671 		case B_UINT64_TYPE:
1672 		case B_UINT32_TYPE:
1673 		case B_UINT16_TYPE:
1674 		case B_UINT8_TYPE:
1675 		case B_INT64_TYPE:
1676 		case B_INT32_TYPE:
1677 		case B_INT16_TYPE:
1678 		case B_INT8_TYPE:
1679 			{
1680 				GenericValueStruct tmp;
1681 				size_t scalarSize = 0;
1682 
1683 				switch (type) {
1684 					case B_TIME_TYPE:
1685 						tmp.time_tt = parsedate(textView->Text(), time(0));
1686 						scalarSize = sizeof(time_t);
1687 						break;
1688 
1689 					// do some size independent conversion on builtin types
1690 					case B_OFF_T_TYPE:
1691 						tmp.off_tt = StringToScalar(textView->Text());
1692 						scalarSize = sizeof(off_t);
1693 						break;
1694 
1695 					case B_UINT64_TYPE:
1696 					case B_INT64_TYPE:
1697 						tmp.int64t = StringToScalar(textView->Text());
1698 						scalarSize = sizeof(int64);
1699 						break;
1700 
1701 					case B_UINT32_TYPE:
1702 					case B_INT32_TYPE:
1703 						tmp.int32t = (int32)StringToScalar(textView->Text());
1704 						scalarSize = sizeof(int32);
1705 						break;
1706 
1707 					case B_UINT16_TYPE:
1708 					case B_INT16_TYPE:
1709 						tmp.int16t = (int16)StringToScalar(textView->Text());
1710 						scalarSize = sizeof(int16);
1711 						break;
1712 
1713 					case B_UINT8_TYPE:
1714 					case B_INT8_TYPE:
1715 						tmp.int8t = (int8)StringToScalar(textView->Text());
1716 						scalarSize = sizeof(int8);
1717 						break;
1718 
1719 					default:
1720 						TRESPASS();
1721 
1722 				}
1723 
1724 				size = fModel->WriteAttr(columnName, type, 0, &tmp, scalarSize);
1725 				break;
1726 			}
1727 	}
1728 
1729 	if (size < 0) {
1730 		BAlert *alert = new BAlert("",
1731 			"There was an error writing the attribute.",
1732 			"Cancel", 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
1733 		alert->SetShortcut(0, B_ESCAPE);
1734 		alert->Go();
1735 
1736 		fValueIsDefined = false;
1737 		return false;
1738 	}
1739 
1740 	fValueIsDefined = true;
1741 	return true;
1742 }
1743 
1744 
1745 //	#pragma mark -
1746 
1747 
1748 OpenWithRelationAttributeText::OpenWithRelationAttributeText(const Model *model,
1749 		const BColumn *column, const BPoseView *view)
1750 	: ScalarAttributeText(model, column),
1751 	fPoseView(view)
1752 {
1753 }
1754 
1755 
1756 int64
1757 OpenWithRelationAttributeText::ReadValue()
1758 {
1759 	fValueDirty = false;
1760 
1761 	const OpenWithPoseView *view = dynamic_cast<const OpenWithPoseView *>(fPoseView);
1762 
1763 	if (view) {
1764 		fValue = view->OpenWithRelation(fModel);
1765 		fValueIsDefined = true;
1766 	}
1767 
1768 	return fValue;
1769 }
1770 
1771 
1772 float
1773 OpenWithRelationAttributeText::PreferredWidth(const BPoseView *pose) const
1774 {
1775 	BString widthString;
1776 	TruncString(&widthString, fRelationText.String(), fRelationText.Length(),
1777 		pose, 500, B_TRUNCATE_END);
1778 	return pose->StringWidth(widthString.String());
1779 }
1780 
1781 
1782 void
1783 OpenWithRelationAttributeText::FitValue(BString *result, const BPoseView *view)
1784 {
1785 	if (fValueDirty)
1786 		ReadValue();
1787 
1788 	ASSERT(view == fPoseView);
1789 	const OpenWithPoseView *launchWithView
1790 		= dynamic_cast<const OpenWithPoseView *>(view);
1791 
1792 	if (launchWithView)
1793 		launchWithView->OpenWithRelationDescription(fModel, &fRelationText);
1794 
1795 	fOldWidth = fColumn->Width();
1796 	fTruncatedWidth = TruncString(result, fRelationText.String(),
1797 		fRelationText.Length(), view, fOldWidth, B_TRUNCATE_END);
1798 	fDirty = false;
1799 }
1800 
1801 
1802 VersionAttributeText::VersionAttributeText(const Model *model,
1803 		const BColumn *column, bool app)
1804 	: StringAttributeText(model, column),
1805 	fAppVersion(app)
1806 {
1807 }
1808 
1809 
1810 void
1811 VersionAttributeText::ReadValue(BString *result)
1812 {
1813 	fValueDirty = false;
1814 
1815 	BModelOpener opener(fModel);
1816 	BFile *file = dynamic_cast<BFile *>(fModel->Node());
1817 	if (file) {
1818 		BAppFileInfo info(file);
1819 		version_info version;
1820 		if (info.InitCheck() == B_OK
1821 			&& info.GetVersionInfo(&version,
1822 				fAppVersion ? B_APP_VERSION_KIND : B_SYSTEM_VERSION_KIND) == B_OK) {
1823 			*result = version.short_info;
1824 			return;
1825 		}
1826 	}
1827 	*result = "-";
1828 }
1829 
1830