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