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