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