1/*
2Open Tracker License
3
4Terms and Conditions
5
6Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
7
8Permission is hereby granted, free of charge, to any person obtaining a copy of
9this software and associated documentation files (the "Software"), to deal in
10the Software without restriction, including without limitation the rights to
11use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12of the Software, and to permit persons to whom the Software is furnished to do
13so, subject to the following conditions:
14
15The above copyright notice and this permission notice applies to all licensees
16and shall be included in all copies or substantial portions of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
20FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
23WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
25Except as contained in this notice, the name of Be Incorporated shall not be
26used in advertising or otherwise to promote the sale, use or other dealings in
27this Software without prior written authorization from Be Incorporated.
28
29Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
30of Be Incorporated in the United States and other countries. Other brand product
31names are registered trademarks or trademarks of their respective holders.
32All rights reserved.
33*/
34
35
36#include "WidgetAttributeText.h"
37
38#include <ctype.h>
39#include <stdlib.h>
40#include <strings.h>
41
42#include <fs_attr.h>
43#include <parsedate.h>
44
45#include <Alert.h>
46#include <AppFileInfo.h>
47#include <Catalog.h>
48#include <DateFormat.h>
49#include <DateTimeFormat.h>
50#include <Debug.h>
51#include <Locale.h>
52#include <NodeInfo.h>
53#include <Path.h>
54#include <StringFormat.h>
55#include <StringForSize.h>
56#include <SupportDefs.h>
57#include <TextView.h>
58#include <Volume.h>
59#include <VolumeRoster.h>
60
61#include "Attributes.h"
62#include "FSUtils.h"
63#include "Model.h"
64#include "OpenWithWindow.h"
65#include "MimeTypes.h"
66#include "PoseView.h"
67#include "SettingsViews.h"
68#include "Utilities.h"
69#include "ViewState.h"
70
71
72#undef B_TRANSLATION_CONTEXT
73#define B_TRANSLATION_CONTEXT "WidgetAttributeText"
74
75
76const int32 kGenericReadBufferSize = 1024;
77
78
79bool NameAttributeText::sSortFolderNamesFirst = false;
80bool RealNameAttributeText::sSortFolderNamesFirst = false;
81
82
83template <class View>
84float
85TruncFileSizeBase(BString* outString, int64 value, const View* view,
86	float width)
87{
88	// format file size value
89	if (value == kUnknownSize) {
90		*outString = "-";
91		return view->StringWidth("-");
92	}
93
94	char sizeBuffer[128];
95	BString buffer = string_for_size(value, sizeBuffer, sizeof(sizeBuffer));
96
97	if (value < kKBSize) {
98		if (view->StringWidth(buffer.String()) > width) {
99			buffer.SetToFormat(B_TRANSLATE_COMMENT("%lld B", "The filesize symbol for byte"),
100				(long long int)value);
101		}
102	} else {
103		// strip off an insignificant zero so we don't get readings
104		// such as 1.00
105		char* period = 0;
106		for (char* tmp = const_cast<char*>(buffer.String()); *tmp != '\0'; tmp++) {
107			if (*tmp == '.')
108				period = tmp;
109		}
110		if (period && period[1] && period[2] == '0') {
111			// move the rest of the string over the insignificant zero
112			for (char* tmp = &period[2]; *tmp; tmp++)
113				*tmp = tmp[1];
114		}
115		float resultWidth = view->StringWidth(buffer);
116		if (resultWidth <= width) {
117			*outString = buffer.String();
118			return resultWidth;
119		}
120	}
121
122	return TruncStringBase(outString, buffer.String(), buffer.Length(), view,
123		width, (uint32)B_TRUNCATE_END);
124}
125
126
127template <class View>
128float
129TruncStringBase(BString* outString, const char* inString, int32 length,
130	const View* view, float width, uint32 truncMode = B_TRUNCATE_MIDDLE)
131{
132	// we are using a template version of this call to make sure
133	// the right StringWidth gets picked up for BView x BPoseView
134	// for max speed and flexibility
135
136	// a standard ellipsis inserting fitting algorithm
137	if (view->StringWidth(inString, length) <= width)
138		*outString = inString;
139	else {
140		const char* source[1];
141		char* results[1];
142
143		source[0] = inString;
144		results[0] = outString->LockBuffer(length + 3);
145
146		BFont font;
147		view->GetFont(&font);
148
149		font.GetTruncatedStrings(source, 1, truncMode, width, results);
150		outString->UnlockBuffer();
151	}
152
153	return view->StringWidth(outString->String(), outString->Length());
154}
155
156
157template <class View>
158float
159TruncTimeBase(BString* outString, int64 value, const View* view, float width)
160{
161	float resultWidth = width + 1;
162
163	time_t timeValue = (time_t)value;
164
165	// Find the longest possible format that will fit the available space
166	struct {
167		BDateFormatStyle dateStyle;
168		BTimeFormatStyle timeStyle;
169	} formats[] = {
170		{ B_LONG_DATE_FORMAT, B_MEDIUM_TIME_FORMAT },
171		{ B_LONG_DATE_FORMAT, B_SHORT_TIME_FORMAT },
172		{ B_MEDIUM_DATE_FORMAT, B_SHORT_TIME_FORMAT },
173		{ B_SHORT_DATE_FORMAT, B_SHORT_TIME_FORMAT },
174	};
175
176	BString date;
177	BDateTimeFormat formatter;
178	for (unsigned int i = 0; i < B_COUNT_OF(formats); ++i) {
179		if (formatter.Format(date, timeValue, formats[i].dateStyle,
180				formats[i].timeStyle) == B_OK) {
181			resultWidth = view->StringWidth(date.String(), date.Length());
182			if (resultWidth <= width) {
183				// Found a format that fits the available space, stop searching
184				break;
185			}
186		}
187	}
188
189	// If we couldn't fit the date, try with just the time
190	// TODO we could use only the time for "today" dates
191	if (resultWidth > width
192		&& BDateFormat().Format(date, timeValue,
193			B_SHORT_DATE_FORMAT) == B_OK) {
194		resultWidth = view->StringWidth(date.String(), date.Length());
195	}
196
197	if (resultWidth > width) {
198		// even the shortest format string didn't do it, insert ellipsis
199		resultWidth = TruncStringBase(outString, date.String(),
200			(ssize_t)date.Length(), view, width);
201	} else
202		*outString = date;
203
204	return resultWidth;
205}
206
207
208// #pragma mark - WidgetAttributeText base class
209
210
211WidgetAttributeText*
212WidgetAttributeText::NewWidgetText(const Model* model,
213	const BColumn* column, const BPoseView* view)
214{
215	// call this to make the right WidgetAttributeText type for a
216	// given column
217
218	const char* attrName = column->AttrName();
219
220	if (strcmp(attrName, kAttrPath) == 0)
221		return new PathAttributeText(model, column);
222
223	if (strcmp(attrName, kAttrMIMEType) == 0)
224		return new KindAttributeText(model, column);
225
226	if (strcmp(attrName, kAttrStatName) == 0)
227		return new NameAttributeText(model, column);
228
229	if (strcmp(attrName, kAttrRealName) == 0)
230		return new RealNameAttributeText(model, column);
231
232	if (strcmp(attrName, kAttrStatSize) == 0)
233		return new SizeAttributeText(model, column);
234
235	if (strcmp(attrName, kAttrStatModified) == 0)
236		return new ModificationTimeAttributeText(model, column);
237
238	if (strcmp(attrName, kAttrStatCreated) == 0)
239		return new CreationTimeAttributeText(model, column);
240
241#ifdef OWNER_GROUP_ATTRIBUTES
242	if (strcmp(attrName, kAttrStatOwner) == 0)
243		return new OwnerAttributeText(model, column);
244
245	if (strcmp(attrName, kAttrStatGroup) == 0)
246		return new GroupAttributeText(model, column);
247#endif
248	if (strcmp(attrName, kAttrStatMode) == 0)
249		return new ModeAttributeText(model, column);
250
251	if (strcmp(attrName, kAttrOpenWithRelation) == 0)
252		return new OpenWithRelationAttributeText(model, column, view);
253
254	if (strcmp(attrName, kAttrAppVersion) == 0)
255		return new AppShortVersionAttributeText(model, column);
256
257	if (strcmp(attrName, kAttrSystemVersion) == 0)
258		return new SystemShortVersionAttributeText(model, column);
259
260	if (strcmp(attrName, kAttrOriginalPath) == 0)
261		return new OriginalPathAttributeText(model, column);
262
263	if (column->DisplayAs() != NULL) {
264		if (!strncmp(column->DisplayAs(), "checkbox", 8))
265			return new CheckboxAttributeText(model, column);
266
267		if (!strncmp(column->DisplayAs(), "duration", 8))
268			return new DurationAttributeText(model, column);
269
270		if (!strncmp(column->DisplayAs(), "rating", 6))
271			return new RatingAttributeText(model, column);
272	}
273
274	return new GenericAttributeText(model, column);
275}
276
277
278WidgetAttributeText::WidgetAttributeText(const Model* model,
279	const BColumn* column)
280	:
281	fModel(const_cast<Model*>(model)),
282	fColumn(column),
283	fOldWidth(-1.0f),
284	fTruncatedWidth(-1.0f),
285	fDirty(true),
286	fValueIsDefined(false)
287{
288	ASSERT(fColumn != NULL);
289
290	if (fColumn == NULL)
291		return;
292
293	ASSERT(fColumn->Width() > 0);
294}
295
296
297WidgetAttributeText::~WidgetAttributeText()
298{
299}
300
301
302const char*
303WidgetAttributeText::FittingText(const BPoseView* view)
304{
305	if (fDirty || fColumn->Width() != fOldWidth || CheckSettingsChanged()
306		|| !fValueIsDefined) {
307		CheckViewChanged(view);
308	}
309
310	ASSERT(!fDirty);
311	return fText.String();
312}
313
314
315bool
316WidgetAttributeText::CheckViewChanged(const BPoseView* view)
317{
318	BString newText;
319	FitValue(&newText, view);
320	if (newText == fText)
321		return false;
322
323	fText = newText;
324	return true;
325}
326
327
328bool
329WidgetAttributeText::CheckSettingsChanged()
330{
331	return false;
332}
333
334
335float
336WidgetAttributeText::TruncString(BString* outString, const char* inString,
337	int32 length, const BPoseView* view, float width, uint32 truncMode)
338{
339	return TruncStringBase(outString, inString, length, view, width, truncMode);
340}
341
342
343float
344WidgetAttributeText::TruncFileSize(BString* outString, int64 value,
345	const BPoseView* view, float width)
346{
347	return TruncFileSizeBase(outString, value, view, width);
348}
349
350
351float
352WidgetAttributeText::TruncTime(BString* outString, int64 value,
353	const BPoseView* view, float width)
354{
355	return TruncTimeBase(outString, value, view, width);
356}
357
358
359float
360WidgetAttributeText::CurrentWidth() const
361{
362	return fTruncatedWidth;
363}
364
365
366float
367WidgetAttributeText::Width(const BPoseView* pose)
368{
369	FittingText(pose);
370	return CurrentWidth();
371}
372
373
374void
375WidgetAttributeText::SetupEditing(BTextView*)
376{
377	ASSERT(fColumn->Editable());
378}
379
380
381bool
382WidgetAttributeText::CommitEditedText(BTextView*)
383{
384	// can't do anything here at this point
385	TRESPASS();
386	return false;
387}
388
389
390status_t
391WidgetAttributeText::AttrAsString(const Model* model, BString* outString,
392	const char* attrName, int32 attrType, float width, BView* view,
393	int64* resultingValue)
394{
395	int64 value;
396
397	status_t error = model->InitCheck();
398	if (error != B_OK)
399		return error;
400
401	switch (attrType) {
402		case B_TIME_TYPE:
403			if (strcmp(attrName, kAttrStatModified) == 0)
404				value = model->StatBuf()->st_mtime;
405			else if (strcmp(attrName, kAttrStatCreated) == 0)
406				value = model->StatBuf()->st_crtime;
407			else {
408				TRESPASS();
409				// not yet supported
410				return B_ERROR;
411			}
412			TruncTimeBase(outString, value, view, width);
413			if (resultingValue)
414				*resultingValue = value;
415
416			return B_OK;
417
418		case B_STRING_TYPE:
419			if (strcmp(attrName, kAttrPath) == 0) {
420				BEntry entry(model->EntryRef());
421				BPath path;
422				BString tmp;
423
424				if (entry.InitCheck() == B_OK
425					&& entry.GetPath(&path) == B_OK) {
426					tmp = path.Path();
427					TruncateLeaf(&tmp);
428				} else
429					tmp = "-";
430
431				if (width > 0) {
432					TruncStringBase(outString, tmp.String(), tmp.Length(), view,
433						width);
434				} else
435					*outString = tmp.String();
436
437				return B_OK;
438			}
439			break;
440
441		case kSizeType:
442//			TruncFileSizeBase(outString, model->StatBuf()->st_size, view,
443//				width);
444			return B_OK;
445			break;
446
447		default:
448			TRESPASS();
449			// not yet supported
450			return B_ERROR;
451
452	}
453
454	TRESPASS();
455	return B_ERROR;
456}
457
458
459bool
460WidgetAttributeText::IsEditable() const
461{
462	return fColumn->Editable();
463}
464
465
466void
467WidgetAttributeText::SetDirty(bool value)
468{
469	fDirty = value;
470}
471
472
473// #pragma mark - StringAttributeText
474
475
476StringAttributeText::StringAttributeText(const Model* model,
477	const BColumn* column)
478	:
479	WidgetAttributeText(model, column),
480	fValueDirty(true)
481{
482}
483
484
485const char*
486StringAttributeText::ValueAsText(const BPoseView* /*view*/)
487{
488	if (fValueDirty)
489		ReadValue(&fFullValueText);
490
491	return fFullValueText.String();
492}
493
494
495bool
496StringAttributeText::CheckAttributeChanged()
497{
498	BString newString;
499	ReadValue(&newString);
500
501	if (newString == fFullValueText)
502		return false;
503
504	fFullValueText = newString;
505	fDirty = true;		// have to redo fitted string
506	return true;
507}
508
509
510void
511StringAttributeText::FitValue(BString* outString, const BPoseView* view)
512{
513	if (fValueDirty)
514		ReadValue(&fFullValueText);
515	fOldWidth = fColumn->Width();
516
517	fTruncatedWidth = TruncString(outString, fFullValueText.String(),
518		fFullValueText.Length(), view, fOldWidth);
519	fDirty = false;
520}
521
522
523float
524StringAttributeText::PreferredWidth(const BPoseView* pose) const
525{
526	return pose->StringWidth(fFullValueText.String());
527}
528
529
530int
531StringAttributeText::Compare(WidgetAttributeText& attr, BPoseView* view)
532{
533	StringAttributeText* compareTo = dynamic_cast<StringAttributeText*>(&attr);
534	ThrowOnAssert(compareTo != NULL);
535
536	if (fValueDirty)
537		ReadValue(&fFullValueText);
538
539	return NaturalCompare(fFullValueText.String(),
540		compareTo->ValueAsText(view));
541}
542
543
544bool
545StringAttributeText::CommitEditedText(BTextView* textView)
546{
547	ASSERT(fColumn->Editable());
548	const char* text = textView->Text();
549
550	if (fFullValueText == text) {
551		// no change
552		return false;
553	}
554
555	if (textView->TextLength() == 0) {
556		// cannot do an empty name
557		return false;
558	}
559
560	// cause re-truncation
561	fDirty = true;
562
563	if (!CommitEditedTextFlavor(textView))
564		return false;
565
566	// update text and width in this widget
567	fFullValueText = text;
568
569	return true;
570}
571
572
573// #pragma mark - ScalarAttributeText
574
575
576ScalarAttributeText::ScalarAttributeText(const Model* model,
577	const BColumn* column)
578	:
579	WidgetAttributeText(model, column),
580	fValue(0),
581	fValueDirty(true)
582{
583}
584
585
586int64
587ScalarAttributeText::Value()
588{
589	if (fValueDirty)
590		fValue = ReadValue();
591
592	return fValue;
593}
594
595
596bool
597ScalarAttributeText::CheckAttributeChanged()
598{
599	int64 newValue = ReadValue();
600	if (newValue == fValue)
601		return false;
602
603	fValue = newValue;
604	fDirty = true;
605		// have to redo fitted string
606
607	return true;
608}
609
610
611float
612ScalarAttributeText::PreferredWidth(const BPoseView* pose) const
613{
614	BString widthString;
615	widthString << fValue;
616	return pose->StringWidth(widthString.String());
617}
618
619
620int
621ScalarAttributeText::Compare(WidgetAttributeText& attr, BPoseView*)
622{
623	ScalarAttributeText* compareTo = dynamic_cast<ScalarAttributeText*>(&attr);
624	ThrowOnAssert(compareTo != NULL);
625
626	if (fValueDirty)
627		fValue = ReadValue();
628
629	return fValue >= compareTo->Value()
630		? (fValue == compareTo->Value() ? 0 : 1) : -1;
631}
632
633
634// #pragma mark - PathAttributeText
635
636
637PathAttributeText::PathAttributeText(const Model* model, const BColumn* column)
638	:
639	StringAttributeText(model, column)
640{
641}
642
643
644void
645PathAttributeText::ReadValue(BString* outString)
646{
647	// get the path
648	BEntry entry(fModel->EntryRef());
649	BPath path;
650
651	if (entry.InitCheck() == B_OK && entry.GetPath(&path) == B_OK) {
652		*outString = path.Path();
653		TruncateLeaf(outString);
654	} else
655		*outString = "-";
656
657	fValueDirty = false;
658}
659
660
661// #pragma mark - OriginalPathAttributeText
662
663
664OriginalPathAttributeText::OriginalPathAttributeText(const Model* model,
665	const BColumn* column)
666	:
667	StringAttributeText(model, column)
668{
669}
670
671
672void
673OriginalPathAttributeText::ReadValue(BString* outString)
674{
675	BEntry entry(fModel->EntryRef());
676	BPath path;
677
678	// get the original path
679	if (entry.InitCheck() == B_OK && FSGetOriginalPath(&entry, &path) == B_OK)
680		*outString = path.Path();
681	else
682		*outString = "-";
683
684	fValueDirty = false;
685}
686
687
688// #pragma mark - KindAttributeText
689
690
691KindAttributeText::KindAttributeText(const Model* model, const BColumn* column)
692	:
693	StringAttributeText(model, column)
694{
695}
696
697
698void
699KindAttributeText::ReadValue(BString* outString)
700{
701	BMimeType mime;
702	char desc[B_MIME_TYPE_LENGTH];
703
704	// get the mime type
705	if (mime.SetType(fModel->MimeType()) != B_OK)
706		*outString = B_TRANSLATE("Unknown");
707	else if (mime.GetShortDescription(desc) == B_OK) {
708		// get the short mime type description
709		*outString = desc;
710	} else
711		*outString = fModel->MimeType();
712
713	fValueDirty = false;
714}
715
716
717// #pragma mark - NameAttributeText
718
719
720NameAttributeText::NameAttributeText(const Model* model,
721	const BColumn* column)
722	:
723	StringAttributeText(model, column)
724{
725}
726
727
728int
729NameAttributeText::Compare(WidgetAttributeText& attr, BPoseView* view)
730{
731	NameAttributeText* compareTo = dynamic_cast<NameAttributeText*>(&attr);
732	ThrowOnAssert(compareTo != NULL);
733
734	if (fValueDirty)
735		ReadValue(&fFullValueText);
736
737	if (NameAttributeText::sSortFolderNamesFirst)
738		return fModel->CompareFolderNamesFirst(attr.TargetModel());
739
740	return NaturalCompare(fFullValueText.String(),
741		compareTo->ValueAsText(view));
742}
743
744
745void
746NameAttributeText::ReadValue(BString* outString)
747{
748	*outString = fModel->Name();
749
750	fValueDirty = false;
751}
752
753
754void
755NameAttributeText::FitValue(BString* outString, const BPoseView* view)
756{
757	if (fValueDirty)
758		ReadValue(&fFullValueText);
759
760	fOldWidth = fColumn->Width();
761	fTruncatedWidth = TruncString(outString, fFullValueText.String(),
762		fFullValueText.Length(), view, fOldWidth, B_TRUNCATE_MIDDLE);
763	fDirty = false;
764}
765
766
767void
768NameAttributeText::SetupEditing(BTextView* textView)
769{
770	DisallowFilenameKeys(textView);
771
772	textView->SetMaxBytes(B_FILE_NAME_LENGTH);
773	textView->SetText(fFullValueText.String(), fFullValueText.Length());
774}
775
776
777bool
778NameAttributeText::CommitEditedTextFlavor(BTextView* textView)
779{
780	if (textView == NULL)
781		return false;
782
783	const char* name = textView->Text();
784	size_t length = (size_t)textView->TextLength();
785
786	BEntry entry(fModel->EntryRef());
787	status_t result = entry.InitCheck();
788	if (result == B_OK)
789		result = EditModelName(fModel, name, length);
790
791	return result == B_OK;
792}
793
794
795void
796NameAttributeText::SetSortFolderNamesFirst(bool enabled)
797{
798	NameAttributeText::sSortFolderNamesFirst = enabled;
799}
800
801
802bool
803NameAttributeText::IsEditable() const
804{
805	return StringAttributeText::IsEditable();
806}
807
808
809// #pragma mark - RealNameAttributeText
810
811
812RealNameAttributeText::RealNameAttributeText(const Model* model,
813	const BColumn* column)
814	:
815	NameAttributeText(model, column)
816{
817}
818
819
820int
821RealNameAttributeText::Compare(WidgetAttributeText& attr, BPoseView* view)
822{
823	RealNameAttributeText* compareTo
824		= dynamic_cast<RealNameAttributeText*>(&attr);
825	ThrowOnAssert(compareTo != NULL);
826
827	if (fValueDirty)
828		ReadValue(&fFullValueText);
829
830	if (RealNameAttributeText::sSortFolderNamesFirst)
831		return fModel->CompareFolderNamesFirst(attr.TargetModel());
832
833	return NaturalCompare(fFullValueText.String(),
834		compareTo->ValueAsText(view));
835}
836
837
838void
839RealNameAttributeText::ReadValue(BString* outString)
840{
841	*outString = fModel->EntryRef()->name;
842
843	fValueDirty = false;
844}
845
846
847void
848RealNameAttributeText::FitValue(BString* outString, const BPoseView* view)
849{
850	if (fValueDirty)
851		ReadValue(&fFullValueText);
852
853	fOldWidth = fColumn->Width();
854	fTruncatedWidth = TruncString(outString, fFullValueText.String(),
855		fFullValueText.Length(), view, fOldWidth, B_TRUNCATE_MIDDLE);
856	fDirty = false;
857}
858
859
860void
861RealNameAttributeText::SetupEditing(BTextView* textView)
862{
863	DisallowFilenameKeys(textView);
864
865	textView->SetMaxBytes(B_FILE_NAME_LENGTH);
866	textView->SetText(fFullValueText.String(), fFullValueText.Length());
867}
868
869
870void
871RealNameAttributeText::SetSortFolderNamesFirst(bool enabled)
872{
873	RealNameAttributeText::sSortFolderNamesFirst = enabled;
874}
875
876
877// #pragma mark - owner/group
878
879
880#ifdef OWNER_GROUP_ATTRIBUTES
881OwnerAttributeText::OwnerAttributeText(const Model* model,
882	const BColumn* column)
883	:
884	StringAttributeText(model, column)
885{
886}
887
888
889void
890OwnerAttributeText::ReadValue(BString* outString)
891{
892	uid_t nodeOwner = fModel->StatBuf()->st_uid;
893	BString user;
894
895	if (nodeOwner == 0) {
896		if (getenv("USER") != NULL)
897			user << getenv("USER");
898		else
899			user << "root";
900	} else
901		user << nodeOwner;
902	*outString = user.String();
903
904	fValueDirty = false;
905}
906
907
908GroupAttributeText::GroupAttributeText(const Model* model,
909	const BColumn* column)
910	:
911	StringAttributeText(model, column)
912{
913}
914
915
916void
917GroupAttributeText::ReadValue(BString* outString)
918{
919	gid_t nodeGroup = fModel->StatBuf()->st_gid;
920	BString group;
921
922	if (nodeGroup == 0) {
923		if (getenv("GROUP") != NULL)
924			group << getenv("GROUP");
925		else
926			group << "0";
927	} else
928		group << nodeGroup;
929	*outString = group.String();
930
931	fValueDirty = false;
932}
933#endif  // OWNER_GROUP_ATTRIBUTES
934
935
936//	#pragma mark - ModeAttributeText
937
938
939ModeAttributeText::ModeAttributeText(const Model* model,
940	const BColumn* column)
941	:
942	StringAttributeText(model, column)
943{
944}
945
946
947void
948ModeAttributeText::ReadValue(BString* outString)
949{
950	mode_t mode = fModel->StatBuf()->st_mode;
951	mode_t baseMask = 00400;
952	char buffer[11];
953
954	char* scanner = buffer;
955
956	if (S_ISDIR(mode))
957		*scanner++ = 'd';
958	else if (S_ISLNK(mode))
959		*scanner++ = 'l';
960	else if (S_ISBLK(mode))
961		*scanner++ = 'b';
962	else if (S_ISCHR(mode))
963		*scanner++ = 'c';
964	else
965		*scanner++ = '-';
966
967	for (int32 index = 0; index < 9; index++) {
968		*scanner++ = (mode & baseMask) ? "rwx"[index % 3] : '-';
969		baseMask >>= 1;
970	}
971
972	*scanner = 0;
973	*outString = buffer;
974
975	fValueDirty = false;
976}
977
978
979//	#pragma mark - SizeAttributeText
980
981
982SizeAttributeText::SizeAttributeText(const Model* model,
983	const BColumn* column)
984	:
985	ScalarAttributeText(model, column)
986{
987}
988
989
990int64
991SizeAttributeText::ReadValue()
992{
993	fValueDirty = false;
994	// get the size
995
996	if (fModel->IsVolume()) {
997		BVolume volume(fModel->NodeRef()->device);
998		fValueIsDefined = volume.Capacity() != 0;
999		return volume.Capacity();
1000	}
1001
1002	if (fModel->IsDirectory() || fModel->IsQuery()
1003		|| fModel->IsQueryTemplate() || fModel->IsSymLink()
1004		|| fModel->IsVirtualDirectory()) {
1005		fValueIsDefined = false;
1006		return kUnknownSize;
1007	}
1008
1009	fValueIsDefined = true;
1010
1011	return fModel->StatBuf()->st_size;
1012}
1013
1014
1015void
1016SizeAttributeText::FitValue(BString* outString, const BPoseView* poseView)
1017{
1018	if (fValueDirty)
1019		fValue = ReadValue();
1020
1021	fOldWidth = fColumn->Width();
1022	if (!fValueIsDefined) {
1023		*outString = "-";
1024		fTruncatedWidth = poseView->StringWidth("-");
1025	} else
1026		fTruncatedWidth = TruncFileSize(outString, fValue, poseView, fOldWidth);
1027	fDirty = false;
1028}
1029
1030
1031float
1032SizeAttributeText::PreferredWidth(const BPoseView* poseView) const
1033{
1034	if (!fValueIsDefined)
1035		return poseView->StringWidth("-");
1036
1037	BString widthString;
1038	TruncFileSize(&widthString, fValue, poseView, 100000);
1039
1040	return poseView->StringWidth(widthString.String());
1041}
1042
1043
1044// #pragma mark - TimeAttributeText
1045
1046
1047TimeAttributeText::TimeAttributeText(const Model* model,
1048	const BColumn* column)
1049	:
1050	ScalarAttributeText(model, column),
1051	fLastClockIs24(false),
1052	fLastDateOrder(kDateFormatEnd),
1053	fLastTimeFormatSeparator(kSeparatorsEnd)
1054{
1055}
1056
1057
1058float
1059TimeAttributeText::PreferredWidth(const BPoseView* pose) const
1060{
1061	BString widthString;
1062	TruncTimeBase(&widthString, fValue, pose, 100000);
1063	return pose->StringWidth(widthString.String());
1064}
1065
1066
1067void
1068TimeAttributeText::FitValue(BString* outString, const BPoseView* view)
1069{
1070	if (fValueDirty)
1071		fValue = ReadValue();
1072
1073	fOldWidth = fColumn->Width();
1074	fTruncatedWidth = TruncTime(outString, fValue, view, fOldWidth);
1075	fDirty = false;
1076}
1077
1078
1079bool
1080TimeAttributeText::CheckSettingsChanged(void)
1081{
1082	// TODO : check against the actual locale settings
1083	return false;
1084}
1085
1086
1087//	#pragma mark - CreationTimeAttributeText
1088
1089
1090CreationTimeAttributeText::CreationTimeAttributeText(const Model* model,
1091	const BColumn* column)
1092	:
1093	TimeAttributeText(model, column)
1094{
1095}
1096
1097
1098int64
1099CreationTimeAttributeText::ReadValue()
1100{
1101	fValueDirty = false;
1102	fValueIsDefined = true;
1103	return fModel->StatBuf()->st_crtime;
1104}
1105
1106
1107//	#pragma mark - ModificationTimeAttributeText
1108
1109
1110ModificationTimeAttributeText::ModificationTimeAttributeText(
1111	const Model* model, const BColumn* column)
1112	:
1113	TimeAttributeText(model, column)
1114{
1115}
1116
1117
1118int64
1119ModificationTimeAttributeText::ReadValue()
1120{
1121	fValueDirty = false;
1122	fValueIsDefined = true;
1123	return fModel->StatBuf()->st_mtime;
1124}
1125
1126
1127//	#pragma mark - GenericAttributeText
1128
1129
1130GenericAttributeText::GenericAttributeText(const Model* model,
1131	const BColumn* column)
1132	:
1133	StringAttributeText(model, column)
1134{
1135}
1136
1137
1138bool
1139GenericAttributeText::CheckAttributeChanged()
1140{
1141	GenericValueStruct tmpValue = fValue;
1142	BString tmpString(fFullValueText);
1143	ReadValue(&fFullValueText);
1144
1145	// fDirty could already be true, in that case we mustn't set it to
1146	// false, even if the attribute text hasn't changed
1147	bool changed = fValue.int64t != tmpValue.int64t
1148		|| tmpString != fFullValueText;
1149	if (changed)
1150		fDirty = true;
1151
1152	return fDirty;
1153}
1154
1155
1156float
1157GenericAttributeText::PreferredWidth(const BPoseView* pose) const
1158{
1159	return pose->StringWidth(fFullValueText.String());
1160}
1161
1162
1163void
1164GenericAttributeText::ReadValue(BString* outString)
1165{
1166	BModelOpener opener(const_cast<Model*>(fModel));
1167
1168	ssize_t length = 0;
1169	fFullValueText = "-";
1170	fValue.int64t = 0;
1171	fValueIsDefined = false;
1172	fValueDirty = false;
1173
1174	if (!fModel->Node())
1175		return;
1176
1177	switch (fColumn->AttrType()) {
1178		case B_STRING_TYPE:
1179		{
1180			char buffer[kGenericReadBufferSize];
1181			length = fModel->Node()->ReadAttr(fColumn->AttrName(),
1182				fColumn->AttrType(), 0, buffer, kGenericReadBufferSize - 1);
1183
1184			if (length > 0) {
1185				buffer[length] = '\0';
1186				// make sure the buffer is null-terminated even if we
1187				// didn't read the whole attribute in or it wasn't to
1188				// begin with
1189
1190				*outString = buffer;
1191				fValueIsDefined = true;
1192			}
1193			break;
1194		}
1195
1196		case B_SSIZE_T_TYPE:
1197		case B_TIME_TYPE:
1198		case B_OFF_T_TYPE:
1199		case B_FLOAT_TYPE:
1200		case B_BOOL_TYPE:
1201		case B_CHAR_TYPE:
1202		case B_INT8_TYPE:
1203		case B_INT16_TYPE:
1204		case B_INT32_TYPE:
1205		case B_INT64_TYPE:
1206		case B_UINT8_TYPE:
1207		case B_UINT16_TYPE:
1208		case B_UINT32_TYPE:
1209		case B_UINT64_TYPE:
1210		case B_DOUBLE_TYPE:
1211		{
1212			// read in the numerical bit representation and attach it
1213			// with a type, depending on the bytes that could be read
1214			attr_info info;
1215			GenericValueStruct tmp;
1216			if (fModel->Node()->GetAttrInfo(fColumn->AttrName(), &info)
1217					== B_OK) {
1218				if (info.size && info.size <= (off_t)sizeof(int64)) {
1219					length = fModel->Node()->ReadAttr(fColumn->AttrName(),
1220						fColumn->AttrType(), 0, &tmp, (size_t)info.size);
1221				}
1222
1223				// We used tmp as a block of memory, now set the
1224				// correct fValue:
1225
1226				if (length == info.size) {
1227					if (fColumn->AttrType() == B_FLOAT_TYPE
1228						|| fColumn->AttrType() == B_DOUBLE_TYPE) {
1229						// filter out special float/double types
1230						switch (info.size) {
1231							case sizeof(float):
1232								fValueIsDefined = true;
1233								fValue.floatt = tmp.floatt;
1234								break;
1235
1236							case sizeof(double):
1237								fValueIsDefined = true;
1238								fValue.doublet = tmp.doublet;
1239								break;
1240
1241							default:
1242								TRESPASS();
1243								break;
1244						}
1245					} else {
1246						// handle the standard data types
1247						switch (info.size) {
1248							case sizeof(char):
1249								// Takes care of bool too.
1250								fValueIsDefined = true;
1251								fValue.int8t = tmp.int8t;
1252								break;
1253
1254							case sizeof(int16):
1255								fValueIsDefined = true;
1256								fValue.int16t = tmp.int16t;
1257								break;
1258
1259							case sizeof(int32):
1260								// Takes care of time_t too.
1261								fValueIsDefined = true;
1262								fValue.int32t = tmp.int32t;
1263								break;
1264
1265							case sizeof(int64):
1266								// Takes care of off_t too.
1267								fValueIsDefined = true;
1268								fValue.int64t = tmp.int64t;
1269								break;
1270
1271							default:
1272								TRESPASS();
1273								break;
1274						}
1275					}
1276				}
1277			}
1278			break;
1279		}
1280	}
1281}
1282
1283
1284void
1285GenericAttributeText::FitValue(BString* outString, const BPoseView* view)
1286{
1287	if (fValueDirty)
1288		ReadValue(&fFullValueText);
1289
1290	fOldWidth = fColumn->Width();
1291
1292	if (!fValueIsDefined) {
1293		*outString = "-";
1294		fTruncatedWidth = TruncString(outString, fFullValueText.String(),
1295			fFullValueText.Length(), view, fOldWidth);
1296		fDirty = false;
1297		return;
1298	}
1299
1300	char buffer[256];
1301
1302	switch (fColumn->AttrType()) {
1303		case B_SIZE_T_TYPE:
1304			TruncFileSizeBase(outString, fValue.int32t, view, fOldWidth);
1305			return;
1306
1307		case B_SSIZE_T_TYPE:
1308			if (fValue.int32t > 0) {
1309				TruncFileSizeBase(outString, fValue.int32t, view, fOldWidth);
1310				return;
1311			}
1312			sprintf(buffer, "%s", strerror(fValue.int32t));
1313			fFullValueText = buffer;
1314			break;
1315
1316		case B_STRING_TYPE:
1317			fTruncatedWidth = TruncString(outString, fFullValueText.String(),
1318				fFullValueText.Length(), view, fOldWidth);
1319			fDirty = false;
1320			return;
1321
1322		case B_OFF_T_TYPE:
1323			// As a side effect update the fFullValueText to the string
1324			// representation of value
1325			TruncFileSize(&fFullValueText, fValue.off_tt, view, 100000);
1326			fTruncatedWidth = TruncFileSize(outString, fValue.off_tt, view,
1327				fOldWidth);
1328			fDirty = false;
1329			return;
1330
1331		case B_TIME_TYPE:
1332			// As a side effect update the fFullValueText to the string
1333			// representation of value
1334			TruncTime(&fFullValueText, fValue.time_tt, view, 100000);
1335			fTruncatedWidth = TruncTime(outString, fValue.time_tt, view,
1336				fOldWidth);
1337			fDirty = false;
1338			return;
1339
1340		case B_BOOL_TYPE:
1341			// For now use true/false, would be nice to be able to set
1342			// the value text
1343
1344 			sprintf(buffer, "%s", fValue.boolt ? "true" : "false");
1345			fFullValueText = buffer;
1346			break;
1347
1348		case B_CHAR_TYPE:
1349			// Make sure no non-printable characters are displayed:
1350			if (!isprint(fValue.uint8t)) {
1351				*outString = "-";
1352				fTruncatedWidth = TruncString(outString, fFullValueText.String(),
1353					fFullValueText.Length(), view, fOldWidth);
1354				fDirty = false;
1355				return;
1356			}
1357
1358			sprintf(buffer, "%c", fValue.uint8t);
1359			fFullValueText = buffer;
1360			break;
1361
1362		case B_INT8_TYPE:
1363			sprintf(buffer, "%d", fValue.int8t);
1364			fFullValueText = buffer;
1365			break;
1366
1367		case B_UINT8_TYPE:
1368			sprintf(buffer, "%d", fValue.uint8t);
1369			fFullValueText = buffer;
1370			break;
1371
1372		case B_INT16_TYPE:
1373			sprintf(buffer, "%d", fValue.int16t);
1374			fFullValueText = buffer;
1375			break;
1376
1377		case B_UINT16_TYPE:
1378			sprintf(buffer, "%d", fValue.uint16t);
1379			fFullValueText = buffer;
1380			break;
1381
1382		case B_INT32_TYPE:
1383			sprintf(buffer, "%" B_PRId32, fValue.int32t);
1384			fFullValueText = buffer;
1385			break;
1386
1387		case B_UINT32_TYPE:
1388			sprintf(buffer, "%" B_PRId32, fValue.uint32t);
1389			fFullValueText = buffer;
1390			break;
1391
1392		case B_INT64_TYPE:
1393			sprintf(buffer, "%" B_PRId64, fValue.int64t);
1394			fFullValueText = buffer;
1395			break;
1396
1397		case B_UINT64_TYPE:
1398			sprintf(buffer, "%" B_PRId64, fValue.uint64t);
1399			fFullValueText = buffer;
1400			break;
1401
1402		case B_FLOAT_TYPE:
1403			snprintf(buffer, sizeof(buffer), "%g", fValue.floatt);
1404			fFullValueText = buffer;
1405			break;
1406
1407		case B_DOUBLE_TYPE:
1408			snprintf(buffer, sizeof(buffer), "%g", fValue.doublet);
1409			fFullValueText = buffer;
1410			break;
1411
1412		default:
1413			*outString = "-";
1414			fTruncatedWidth = TruncString(outString, fFullValueText.String(),
1415				fFullValueText.Length(), view, fOldWidth);
1416			fDirty = false;
1417			return;
1418	}
1419	fTruncatedWidth = TruncString(outString, buffer, (ssize_t)strlen(buffer),
1420		view, fOldWidth);
1421	fDirty = false;
1422}
1423
1424
1425const char*
1426GenericAttributeText::ValueAsText(const BPoseView* view)
1427{
1428	// TODO: redesign this - this is to make sure the value is valid
1429	bool oldDirty = fDirty;
1430	BString outString;
1431	FitValue(&outString, view);
1432	fDirty = oldDirty;
1433
1434	return fFullValueText.String();
1435}
1436
1437
1438int
1439GenericAttributeText::Compare(WidgetAttributeText& attr, BPoseView*)
1440{
1441	GenericAttributeText* compareTo
1442		= dynamic_cast<GenericAttributeText*>(&attr);
1443	ThrowOnAssert(compareTo != NULL);
1444
1445	if (fValueDirty)
1446		ReadValue(&fFullValueText);
1447
1448	if (compareTo->fValueDirty)
1449		compareTo->ReadValue(&compareTo->fFullValueText);
1450
1451	// sort undefined values last, regardless of the other value
1452	if (!fValueIsDefined)
1453		return compareTo->fValueIsDefined ? 1 : 0;
1454
1455	if (!compareTo->fValueIsDefined)
1456		return -1;
1457
1458	switch (fColumn->AttrType()) {
1459		case B_STRING_TYPE:
1460			return fFullValueText.ICompare(compareTo->fFullValueText);
1461
1462		case B_CHAR_TYPE:
1463		{
1464			char vStr[2] = { static_cast<char>(fValue.uint8t), 0 };
1465			char cStr[2] = { static_cast<char>(compareTo->fValue.uint8t), 0};
1466
1467			BString valueStr(vStr);
1468			BString compareToStr(cStr);
1469
1470			return valueStr.ICompare(compareToStr);
1471		}
1472
1473		case B_FLOAT_TYPE:
1474			return fValue.floatt >= compareTo->fValue.floatt ?
1475				(fValue.floatt == compareTo->fValue.floatt ? 0 : 1) : -1;
1476
1477		case B_DOUBLE_TYPE:
1478			return fValue.doublet >= compareTo->fValue.doublet ?
1479				(fValue.doublet == compareTo->fValue.doublet ? 0 : 1) : -1;
1480
1481		case B_BOOL_TYPE:
1482			return fValue.boolt >= compareTo->fValue.boolt ?
1483				(fValue.boolt == compareTo->fValue.boolt ? 0 : 1) : -1;
1484
1485		case B_UINT8_TYPE:
1486			return fValue.uint8t >= compareTo->fValue.uint8t ?
1487				(fValue.uint8t == compareTo->fValue.uint8t ? 0 : 1) : -1;
1488
1489		case B_INT8_TYPE:
1490			return fValue.int8t >= compareTo->fValue.int8t ?
1491					(fValue.int8t == compareTo->fValue.int8t ? 0 : 1) : -1;
1492
1493		case B_UINT16_TYPE:
1494			return fValue.uint16t >= compareTo->fValue.uint16t ?
1495				(fValue.uint16t == compareTo->fValue.uint16t ? 0 : 1) : -1;
1496
1497		case B_INT16_TYPE:
1498			return fValue.int16t >= compareTo->fValue.int16t ?
1499				(fValue.int16t == compareTo->fValue.int16t ? 0 : 1) : -1;
1500
1501		case B_UINT32_TYPE:
1502			return fValue.uint32t >= compareTo->fValue.uint32t ?
1503				(fValue.uint32t == compareTo->fValue.uint32t ? 0 : 1) : -1;
1504
1505		case B_TIME_TYPE:
1506			// time_t typedef'd to a long, i.e. a int32
1507		case B_INT32_TYPE:
1508			return fValue.int32t >= compareTo->fValue.int32t ?
1509				(fValue.int32t == compareTo->fValue.int32t ? 0 : 1) : -1;
1510
1511		case B_OFF_T_TYPE:
1512			// off_t typedef'd to a long long, i.e. a int64
1513		case B_INT64_TYPE:
1514			return fValue.int64t >= compareTo->fValue.int64t ?
1515				(fValue.int64t == compareTo->fValue.int64t ? 0 : 1) : -1;
1516
1517		case B_UINT64_TYPE:
1518		default:
1519			return fValue.uint64t >= compareTo->fValue.uint64t ?
1520				(fValue.uint64t == compareTo->fValue.uint64t ? 0 : 1) : -1;
1521	}
1522
1523	return 0;
1524}
1525
1526
1527bool
1528GenericAttributeText::CommitEditedText(BTextView* textView)
1529{
1530	ASSERT(fColumn->Editable());
1531	const char* text = textView->Text();
1532
1533	if (fFullValueText == text)
1534		// no change
1535		return false;
1536
1537	if (!CommitEditedTextFlavor(textView))
1538		return false;
1539
1540	// update text and width in this widget
1541	fFullValueText = text;
1542	// cause re-truncation
1543	fDirty = true;
1544	fValueDirty = true;
1545
1546	return true;
1547}
1548
1549
1550void
1551GenericAttributeText::SetupEditing(BTextView* textView)
1552{
1553	textView->SetMaxBytes(kGenericReadBufferSize - 1);
1554	textView->SetText(fFullValueText.String(), fFullValueText.Length());
1555}
1556
1557
1558bool
1559GenericAttributeText::CommitEditedTextFlavor(BTextView* textView)
1560{
1561	BNode node(fModel->EntryRef());
1562
1563	if (node.InitCheck() != B_OK)
1564		return false;
1565
1566	uint32 type = fColumn->AttrType();
1567
1568	if (type != B_STRING_TYPE
1569		&& type != B_UINT64_TYPE
1570		&& type != B_UINT32_TYPE
1571		&& type != B_UINT16_TYPE
1572		&& type != B_UINT8_TYPE
1573		&& type != B_INT64_TYPE
1574		&& type != B_INT32_TYPE
1575		&& type != B_INT16_TYPE
1576		&& type != B_INT8_TYPE
1577		&& type != B_OFF_T_TYPE
1578		&& type != B_TIME_TYPE
1579		&& type != B_FLOAT_TYPE
1580		&& type != B_DOUBLE_TYPE
1581		&& type != B_CHAR_TYPE
1582		&& type != B_BOOL_TYPE) {
1583		BAlert* alert = new BAlert("",
1584			B_TRANSLATE("Sorry, you cannot edit that attribute."),
1585			B_TRANSLATE("Cancel"),
1586			0, 0, B_WIDTH_AS_USUAL, B_STOP_ALERT);
1587		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1588		alert->Go();
1589		return false;
1590	}
1591
1592	const char* columnName = fColumn->AttrName();
1593	ssize_t size = 0;
1594
1595	switch (type) {
1596		case B_STRING_TYPE:
1597			size = fModel->WriteAttr(columnName, type, 0, textView->Text(),
1598				(size_t)textView->TextLength() + 1);
1599			break;
1600
1601		case B_BOOL_TYPE:
1602		{
1603			bool value = strncasecmp(textView->Text(), "0", 1) != 0
1604				&& strncasecmp(textView->Text(), "off", 2) != 0
1605				&& strncasecmp(textView->Text(), "no", 3) != 0
1606				&& strncasecmp(textView->Text(), "false", 4) != 0
1607				&& strlen(textView->Text()) != 0;
1608
1609			size = fModel->WriteAttr(columnName, type, 0, &value, sizeof(bool));
1610			break;
1611		}
1612
1613		case B_CHAR_TYPE:
1614		{
1615			char ch;
1616			sscanf(textView->Text(), "%c", &ch);
1617			//Check if we read the start of a multi-byte glyph:
1618			if (!isprint(ch)) {
1619				BAlert* alert = new BAlert("",
1620					B_TRANSLATE("Sorry, the 'Character' "
1621					"attribute cannot store a multi-byte glyph."),
1622					B_TRANSLATE("Cancel"),
1623					0, 0, B_WIDTH_AS_USUAL, B_STOP_ALERT);
1624				alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1625				alert->Go();
1626				return false;
1627			}
1628
1629			size = fModel->WriteAttr(columnName, type, 0, &ch, sizeof(char));
1630			break;
1631		}
1632
1633		case B_FLOAT_TYPE:
1634		{
1635			float floatVal;
1636
1637			if (sscanf(textView->Text(), "%f", &floatVal) == 1) {
1638				fValueIsDefined = true;
1639				fValue.floatt = floatVal;
1640				size = fModel->WriteAttr(columnName, type, 0, &floatVal,
1641					sizeof(float));
1642			} else {
1643				// If the value was already defined, it's on disk.
1644				// Otherwise not.
1645				return fValueIsDefined;
1646			}
1647			break;
1648		}
1649
1650		case B_DOUBLE_TYPE:
1651		{
1652			double doubleVal;
1653
1654			if (sscanf(textView->Text(), "%lf", &doubleVal) == 1) {
1655				fValueIsDefined = true;
1656				fValue.doublet = doubleVal;
1657				size = fModel->WriteAttr(columnName, type, 0, &doubleVal,
1658					sizeof(double));
1659			} else {
1660				// If the value was already defined, it's on disk.
1661				// Otherwise not.
1662				return fValueIsDefined;
1663			}
1664			break;
1665		}
1666
1667		case B_TIME_TYPE:
1668		case B_OFF_T_TYPE:
1669		case B_UINT64_TYPE:
1670		case B_UINT32_TYPE:
1671		case B_UINT16_TYPE:
1672		case B_UINT8_TYPE:
1673		case B_INT64_TYPE:
1674		case B_INT32_TYPE:
1675		case B_INT16_TYPE:
1676		case B_INT8_TYPE:
1677		{
1678			GenericValueStruct tmp;
1679			size_t scalarSize = 0;
1680
1681			switch (type) {
1682				case B_TIME_TYPE:
1683					tmp.time_tt = parsedate(textView->Text(), time(0));
1684					scalarSize = sizeof(time_t);
1685					break;
1686
1687				// do some size independent conversion on builtin types
1688				case B_OFF_T_TYPE:
1689					tmp.off_tt = StringToScalar(textView->Text());
1690					scalarSize = sizeof(off_t);
1691					break;
1692
1693				case B_UINT64_TYPE:
1694				case B_INT64_TYPE:
1695					tmp.int64t = StringToScalar(textView->Text());
1696					scalarSize = sizeof(int64);
1697					break;
1698
1699				case B_UINT32_TYPE:
1700				case B_INT32_TYPE:
1701					tmp.int32t = (int32)StringToScalar(textView->Text());
1702					scalarSize = sizeof(int32);
1703					break;
1704
1705				case B_UINT16_TYPE:
1706				case B_INT16_TYPE:
1707					tmp.int16t = (int16)StringToScalar(textView->Text());
1708					scalarSize = sizeof(int16);
1709					break;
1710
1711				case B_UINT8_TYPE:
1712				case B_INT8_TYPE:
1713					tmp.int8t = (int8)StringToScalar(textView->Text());
1714					scalarSize = sizeof(int8);
1715					break;
1716
1717				default:
1718					TRESPASS();
1719			}
1720
1721			size = fModel->WriteAttr(columnName, type, 0, &tmp, scalarSize);
1722			break;
1723		}
1724	}
1725
1726	if (size < 0) {
1727		BAlert* alert = new BAlert("",
1728			B_TRANSLATE("There was an error writing the attribute."),
1729			B_TRANSLATE("Cancel"),
1730			0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
1731		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1732		alert->Go();
1733
1734		fValueIsDefined = false;
1735		return false;
1736	}
1737
1738	fValueIsDefined = true;
1739	return true;
1740}
1741
1742
1743// #pragma mark - DurationAttributeText (display as: duration)
1744
1745
1746DurationAttributeText::DurationAttributeText(const Model* model,
1747	const BColumn* column)
1748	:
1749	GenericAttributeText(model, column)
1750{
1751}
1752
1753
1754// TODO: support editing!
1755
1756
1757void
1758DurationAttributeText::FitValue(BString* outString, const BPoseView* view)
1759{
1760	if (fValueDirty)
1761		ReadValue(&fFullValueText);
1762
1763	fOldWidth = fColumn->Width();
1764	fDirty = false;
1765
1766	if (!fValueIsDefined) {
1767		*outString = "-";
1768		fTruncatedWidth = TruncString(outString, fFullValueText.String(),
1769			fFullValueText.Length(), view, fOldWidth);
1770		return;
1771	}
1772
1773	int64 time = 0;
1774
1775	switch (fColumn->AttrType()) {
1776		case B_TIME_TYPE:
1777			time = fValue.time_tt * 1000000LL;
1778			break;
1779
1780		case B_INT8_TYPE:
1781			time = fValue.int8t * 1000000LL;
1782			break;
1783
1784		case B_INT16_TYPE:
1785			time = fValue.int16t * 1000000LL;
1786			break;
1787
1788		case B_INT32_TYPE:
1789			time = fValue.int32t * 1000000LL;
1790			break;
1791
1792		case B_INT64_TYPE:
1793			time = fValue.int64t;
1794			break;
1795	}
1796
1797	// TODO: ignores micro seconds for now
1798	int32 seconds = time / 1000000LL;
1799
1800	bool negative = seconds < 0;
1801	if (negative)
1802		seconds = -seconds;
1803
1804	int32 hours = seconds / 3600;
1805	seconds -= hours * 3600;
1806	int32 minutes = seconds / 60;
1807	seconds = seconds % 60;
1808
1809	char buffer[256];
1810	if (hours > 0) {
1811		snprintf(buffer, sizeof(buffer), "%s%" B_PRId32 ":%02" B_PRId32 ":%02"
1812			B_PRId32, negative ? "-" : "", hours, minutes, seconds);
1813	} else {
1814		snprintf(buffer, sizeof(buffer), "%s%" B_PRId32 ":%02" B_PRId32,
1815			negative ? "-" : "", minutes, seconds);
1816	}
1817
1818	fFullValueText = buffer;
1819
1820	fTruncatedWidth = TruncString(outString, fFullValueText.String(),
1821		fFullValueText.Length(), view, fOldWidth);
1822}
1823
1824
1825// #pragma mark - CheckboxAttributeText (display as: checkbox)
1826
1827
1828CheckboxAttributeText::CheckboxAttributeText(const Model* model,
1829	const BColumn* column)
1830	:
1831	GenericAttributeText(model, column),
1832	fOnChar("���"),
1833	fOffChar("-")
1834{
1835	// TODO: better have common data in the column object!
1836	if (const char* separator = strchr(column->DisplayAs(), ':')) {
1837		BString chars(separator + 1);
1838		int32 length;
1839		const char* c = chars.CharAt(0, &length);
1840		fOnChar.SetTo(c, length);
1841		if (c[length]) {
1842			c = chars.CharAt(1, &length);
1843			fOffChar.SetTo(c, length);
1844		}
1845	}
1846}
1847
1848
1849void
1850CheckboxAttributeText::SetupEditing(BTextView* view)
1851{
1852	// TODO: support editing for real!
1853	BString outString;
1854	GenericAttributeText::FitValue(&outString, NULL);
1855	GenericAttributeText::SetupEditing(view);
1856}
1857
1858
1859void
1860CheckboxAttributeText::FitValue(BString* outString, const BPoseView* view)
1861{
1862	if (fValueDirty)
1863		ReadValue(&fFullValueText);
1864
1865	fOldWidth = fColumn->Width();
1866	fDirty = false;
1867
1868	if (!fValueIsDefined) {
1869		*outString = fOffChar;
1870		fTruncatedWidth = TruncString(outString, fFullValueText.String(),
1871			fFullValueText.Length(), view, fOldWidth);
1872		return;
1873	}
1874
1875	bool checked = false;
1876
1877	switch (fColumn->AttrType()) {
1878		case B_BOOL_TYPE:
1879			checked = fValue.boolt;
1880			break;
1881
1882		case B_INT8_TYPE:
1883		case B_UINT8_TYPE:
1884			checked = fValue.int8t != 0;
1885			break;
1886
1887		case B_INT16_TYPE:
1888		case B_UINT16_TYPE:
1889			checked = fValue.int16t != 0;
1890			break;
1891
1892		case B_INT32_TYPE:
1893		case B_UINT32_TYPE:
1894			checked = fValue.int32t != 0;
1895			break;
1896	}
1897
1898	fFullValueText = checked ? fOnChar : fOffChar;
1899
1900	fTruncatedWidth = TruncString(outString, fFullValueText.String(),
1901		fFullValueText.Length(), view, fOldWidth);
1902}
1903
1904
1905// #pragma mark - RatingAttributeText (display as: rating)
1906
1907
1908RatingAttributeText::RatingAttributeText(const Model* model,
1909	const BColumn* column)
1910	:
1911	GenericAttributeText(model, column),
1912	fCount(5),
1913	fMax(10)
1914{
1915	// TODO: support different star counts/max via specifier
1916}
1917
1918
1919void
1920RatingAttributeText::SetupEditing(BTextView* view)
1921{
1922	// TODO: support editing for real!
1923	BString outString;
1924	GenericAttributeText::FitValue(&outString, NULL);
1925	GenericAttributeText::SetupEditing(view);
1926}
1927
1928
1929void
1930RatingAttributeText::FitValue(BString* ratingString, const BPoseView* view)
1931{
1932	if (fValueDirty)
1933		ReadValue(&fFullValueText);
1934
1935	fOldWidth = fColumn->Width();
1936	fDirty = false;
1937
1938	int64 rating;
1939	if (fValueIsDefined) {
1940		switch (fColumn->AttrType()) {
1941			case B_INT8_TYPE:
1942				rating = fValue.int8t;
1943				break;
1944
1945			case B_INT16_TYPE:
1946				rating = fValue.int16t;
1947				break;
1948
1949			case B_INT32_TYPE:
1950				rating = fValue.int32t;
1951				break;
1952
1953			default:
1954				rating = 0;
1955				break;
1956		}
1957	} else
1958		rating = 0;
1959
1960	if (rating > fMax)
1961		rating = fMax;
1962
1963	if (rating < 0)
1964		rating = 0;
1965
1966	int32 steps = fMax / fCount;
1967	fFullValueText = "";
1968
1969	for (int32 i = 0; i < fCount; i++) {
1970		int64 n = i * steps;
1971		if (rating > n + steps / 2)
1972			fFullValueText += "���";
1973		else if (rating > n)
1974			fFullValueText += "���";
1975		else
1976			fFullValueText += "���";
1977	}
1978
1979	fTruncatedWidth = TruncString(ratingString, fFullValueText.String(),
1980		fFullValueText.Length(), view, fOldWidth);
1981}
1982
1983
1984// #pragma mark - OpenWithRelationAttributeText
1985
1986
1987OpenWithRelationAttributeText::OpenWithRelationAttributeText(const Model* model,
1988	const BColumn* column, const BPoseView* view)
1989	:
1990	ScalarAttributeText(model, column),
1991	fPoseView(view)
1992{
1993}
1994
1995
1996int64
1997OpenWithRelationAttributeText::ReadValue()
1998{
1999	fValueDirty = false;
2000
2001	const OpenWithPoseView* view
2002		= dynamic_cast<const OpenWithPoseView*>(fPoseView);
2003	if (view != NULL) {
2004		fValue = view->OpenWithRelation(fModel);
2005		fValueIsDefined = true;
2006	}
2007
2008	return fValue;
2009}
2010
2011
2012float
2013OpenWithRelationAttributeText::PreferredWidth(const BPoseView* pose) const
2014{
2015	BString widthString;
2016	TruncString(&widthString, fRelationText.String(), fRelationText.Length(),
2017		pose, 500, B_TRUNCATE_END);
2018	return pose->StringWidth(widthString.String());
2019}
2020
2021
2022void
2023OpenWithRelationAttributeText::FitValue(BString* outString,
2024	const BPoseView* view)
2025{
2026	if (fValueDirty)
2027		ReadValue();
2028
2029	ASSERT(view == fPoseView);
2030	const OpenWithPoseView* launchWithView
2031		= dynamic_cast<const OpenWithPoseView*>(view);
2032	if (launchWithView != NULL)
2033		launchWithView->OpenWithRelationDescription(fModel, &fRelationText);
2034
2035	fOldWidth = fColumn->Width();
2036	fTruncatedWidth = TruncString(outString, fRelationText.String(),
2037		fRelationText.Length(), view, fOldWidth, B_TRUNCATE_END);
2038	fDirty = false;
2039}
2040
2041
2042// #pragma mark - VersionAttributeText
2043
2044
2045VersionAttributeText::VersionAttributeText(const Model* model,
2046	const BColumn* column, bool app)
2047	:
2048	StringAttributeText(model, column),
2049	fAppVersion(app)
2050{
2051}
2052
2053
2054void
2055VersionAttributeText::ReadValue(BString* outString)
2056{
2057	fValueDirty = false;
2058
2059	BModelOpener opener(fModel);
2060	BFile* file = dynamic_cast<BFile*>(fModel->Node());
2061	if (file != NULL) {
2062		BAppFileInfo info(file);
2063		version_info version;
2064		if (info.InitCheck() == B_OK
2065			&& info.GetVersionInfo(&version, fAppVersion
2066				? B_APP_VERSION_KIND : B_SYSTEM_VERSION_KIND) == B_OK) {
2067			*outString = version.short_info;
2068			return;
2069		}
2070	}
2071
2072	*outString = "-";
2073}
2074