#include "WidgetAttributeText.h"
#include <ctype.h>
#include <stdlib.h>
#include <strings.h>
#include <fs_attr.h>
#include <parsedate.h>
#include <Alert.h>
#include <AppFileInfo.h>
#include <Catalog.h>
#include <DateFormat.h>
#include <DateTimeFormat.h>
#include <Debug.h>
#include <Locale.h>
#include <NodeInfo.h>
#include <NumberFormat.h>
#include <Path.h>
#include <StringFormat.h>
#include <StringForSize.h>
#include <SupportDefs.h>
#include <TextView.h>
#include <Volume.h>
#include <VolumeRoster.h>
#include "Attributes.h"
#include "FSUtils.h"
#include "Model.h"
#include "OpenWithWindow.h"
#include "MimeTypes.h"
#include "PoseView.h"
#include "SettingsViews.h"
#include "Utilities.h"
#include "ViewState.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "WidgetAttributeText"
const int32 kGenericReadBufferSize = 1024;
bool NameAttributeText::sSortFolderNamesFirst = false;
bool RealNameAttributeText::sSortFolderNamesFirst = false;
template <class View>
float
TruncFileSizeBase(BString* outString, int64 value, const View* view,
float width)
{
if (value == kUnknownSize) {
*outString = "-";
return view->StringWidth("-");
}
char sizeBuffer[128];
BString buffer = string_for_size(value, sizeBuffer, sizeof(sizeBuffer));
if (value < kKBSize) {
if (view->StringWidth(buffer.String()) > width) {
buffer.SetToFormat(B_TRANSLATE_COMMENT("%lld B", "The filesize symbol for byte"),
(long long int)value);
}
} else {
BNumberFormat numberFormat;
BString separator(numberFormat.GetSeparator(B_DECIMAL_SEPARATOR));
if (!separator.IsEmpty()) {
int32 position = buffer.FindFirst(separator);
if (position != B_ERROR && buffer.ByteAt(position + 2) == '0')
buffer.Remove(position + 2, 1);
}
float resultWidth = view->StringWidth(buffer);
if (resultWidth <= width) {
*outString = buffer.String();
return resultWidth;
}
}
return TruncStringBase(outString, buffer.String(), buffer.Length(), view,
width, (uint32)B_TRUNCATE_END);
}
template <class View>
float
TruncStringBase(BString* outString, const char* inString, int32 length,
const View* view, float width, uint32 truncMode = B_TRUNCATE_MIDDLE)
{
if (view->StringWidth(inString, length) <= width)
*outString = inString;
else {
const char* source[1];
char* results[1];
source[0] = inString;
results[0] = outString->LockBuffer(length + 3);
BFont font;
view->GetFont(&font);
font.GetTruncatedStrings(source, 1, truncMode, width, results);
outString->UnlockBuffer();
}
return view->StringWidth(outString->String(), outString->Length());
}
template <class View>
float
TruncTimeBase(BString* outString, int64 value, const View* view, float width)
{
float resultWidth = width + 1;
time_t timeValue = (time_t)value;
struct {
BDateFormatStyle dateStyle;
BTimeFormatStyle timeStyle;
} formats[] = {
{ B_LONG_DATE_FORMAT, B_MEDIUM_TIME_FORMAT },
{ B_LONG_DATE_FORMAT, B_SHORT_TIME_FORMAT },
{ B_MEDIUM_DATE_FORMAT, B_SHORT_TIME_FORMAT },
{ B_SHORT_DATE_FORMAT, B_SHORT_TIME_FORMAT },
};
BString date;
BDateTimeFormat formatter;
for (unsigned int i = 0; i < B_COUNT_OF(formats); ++i) {
if (formatter.Format(date, timeValue, formats[i].dateStyle,
formats[i].timeStyle) == B_OK) {
resultWidth = view->StringWidth(date.String(), date.Length());
if (resultWidth <= width) {
break;
}
}
}
if (resultWidth > width
&& BDateFormat().Format(date, timeValue,
B_SHORT_DATE_FORMAT) == B_OK) {
resultWidth = view->StringWidth(date.String(), date.Length());
}
if (resultWidth > width) {
resultWidth = TruncStringBase(outString, date.String(),
(ssize_t)date.Length(), view, width);
} else
*outString = date;
return resultWidth;
}
WidgetAttributeText*
WidgetAttributeText::NewWidgetText(const Model* model,
const BColumn* column, const BPoseView* view)
{
const char* attrName = column->AttrName();
if (strcmp(attrName, kAttrPath) == 0)
return new PathAttributeText(model, column);
if (strcmp(attrName, kAttrMIMEType) == 0)
return new KindAttributeText(model, column);
if (strcmp(attrName, kAttrStatName) == 0)
return new NameAttributeText(model, column);
if (strcmp(attrName, kAttrRealName) == 0)
return new RealNameAttributeText(model, column);
if (strcmp(attrName, kAttrStatSize) == 0)
return new SizeAttributeText(model, column);
if (strcmp(attrName, kAttrStatModified) == 0)
return new ModificationTimeAttributeText(model, column);
if (strcmp(attrName, kAttrStatCreated) == 0)
return new CreationTimeAttributeText(model, column);
#ifdef OWNER_GROUP_ATTRIBUTES
if (strcmp(attrName, kAttrStatOwner) == 0)
return new OwnerAttributeText(model, column);
if (strcmp(attrName, kAttrStatGroup) == 0)
return new GroupAttributeText(model, column);
#endif
if (strcmp(attrName, kAttrStatMode) == 0)
return new ModeAttributeText(model, column);
if (strcmp(attrName, kAttrOpenWithRelation) == 0)
return new OpenWithRelationAttributeText(model, column, view);
if (strcmp(attrName, kAttrAppVersion) == 0)
return new AppShortVersionAttributeText(model, column);
if (strcmp(attrName, kAttrSystemVersion) == 0)
return new SystemShortVersionAttributeText(model, column);
if (strcmp(attrName, kAttrOriginalPath) == 0)
return new OriginalPathAttributeText(model, column);
if (column->DisplayAs() != NULL) {
if (!strncmp(column->DisplayAs(), "checkbox", 8))
return new CheckboxAttributeText(model, column);
if (!strncmp(column->DisplayAs(), "duration", 8))
return new DurationAttributeText(model, column);
if (!strncmp(column->DisplayAs(), "rating", 6))
return new RatingAttributeText(model, column);
}
return new GenericAttributeText(model, column);
}
WidgetAttributeText::WidgetAttributeText(const Model* model,
const BColumn* column)
:
fModel(const_cast<Model*>(model)),
fColumn(column),
fOldWidth(-1.0f),
fTruncatedWidth(-1.0f),
fDirty(true),
fValueIsDefined(false)
{
ASSERT(fColumn != NULL);
if (fColumn == NULL)
return;
ASSERT(fColumn->Width() > 0);
}
WidgetAttributeText::~WidgetAttributeText()
{
}
const char*
WidgetAttributeText::FittingText(const BPoseView* view)
{
bool widthChanged = view->ViewMode() == kListMode ? fColumn->Width() != fOldWidth : false;
if (fDirty || widthChanged || CheckSettingsChanged())
CheckViewChanged(view);
ASSERT(!fDirty);
return fText.String();
}
bool
WidgetAttributeText::CheckViewChanged(const BPoseView* view)
{
BString newText;
FitValue(&newText, view);
if (newText == fText)
return false;
fText = newText;
return true;
}
bool
WidgetAttributeText::CheckSettingsChanged()
{
return false;
}
float
WidgetAttributeText::TruncString(BString* outString, const char* inString,
int32 length, const BPoseView* view, float width, uint32 truncMode)
{
return TruncStringBase(outString, inString, length, view, width, truncMode);
}
float
WidgetAttributeText::TruncFileSize(BString* outString, int64 value,
const BPoseView* view, float width)
{
return TruncFileSizeBase(outString, value, view, width);
}
float
WidgetAttributeText::TruncTime(BString* outString, int64 value,
const BPoseView* view, float width)
{
return TruncTimeBase(outString, value, view, width);
}
float
WidgetAttributeText::CurrentWidth() const
{
return fTruncatedWidth;
}
float
WidgetAttributeText::Width(const BPoseView* pose)
{
FittingText(pose);
return CurrentWidth();
}
void
WidgetAttributeText::SetupEditing(BTextView*)
{
ASSERT(fColumn->Editable());
}
bool
WidgetAttributeText::CommitEditedText(BTextView*)
{
TRESPASS();
return false;
}
status_t
WidgetAttributeText::AttrAsString(const Model* model, BString* outString,
const char* attrName, int32 attrType, float width, BView* view,
int64* resultingValue)
{
int64 value;
status_t error = model->InitCheck();
if (error != B_OK)
return error;
switch (attrType) {
case B_TIME_TYPE:
if (strcmp(attrName, kAttrStatModified) == 0)
value = model->StatBuf()->st_mtime;
else if (strcmp(attrName, kAttrStatCreated) == 0)
value = model->StatBuf()->st_crtime;
else {
TRESPASS();
return B_ERROR;
}
TruncTimeBase(outString, value, view, width);
if (resultingValue)
*resultingValue = value;
return B_OK;
case B_STRING_TYPE:
if (strcmp(attrName, kAttrPath) == 0) {
BEntry entry(model->EntryRef());
BPath path;
BString tmp;
if (entry.InitCheck() == B_OK
&& entry.GetPath(&path) == B_OK) {
tmp = path.Path();
TruncateLeaf(&tmp);
} else
tmp = "-";
if (width > 0) {
TruncStringBase(outString, tmp.String(), tmp.Length(), view,
width);
} else
*outString = tmp.String();
return B_OK;
}
break;
case kSizeType:
return B_OK;
break;
default:
TRESPASS();
return B_ERROR;
}
TRESPASS();
return B_ERROR;
}
bool
WidgetAttributeText::IsEditable() const
{
return fColumn->Editable();
}
void
WidgetAttributeText::SetDirty(bool value)
{
fDirty = value;
}
StringAttributeText::StringAttributeText(const Model* model,
const BColumn* column)
:
WidgetAttributeText(model, column),
fValueDirty(true)
{
}
const char*
StringAttributeText::ValueAsText(const BPoseView* )
{
if (fValueDirty)
ReadValue(&fFullValueText);
return fFullValueText.String();
}
bool
StringAttributeText::CheckAttributeChanged()
{
BString newString;
ReadValue(&newString);
if (newString == fFullValueText)
return false;
fFullValueText = newString;
fDirty = true;
return true;
}
void
StringAttributeText::FitValue(BString* outString, const BPoseView* view)
{
if (fValueDirty)
ReadValue(&fFullValueText);
fOldWidth = fColumn->Width();
fTruncatedWidth = TruncString(outString, fFullValueText.String(),
fFullValueText.Length(), view, fOldWidth);
fDirty = false;
}
float
StringAttributeText::PreferredWidth(const BPoseView* pose) const
{
return pose->StringWidth(fFullValueText.String());
}
int
StringAttributeText::Compare(WidgetAttributeText& attr, BPoseView* view)
{
StringAttributeText* compareTo = dynamic_cast<StringAttributeText*>(&attr);
ThrowOnAssert(compareTo != NULL);
if (fValueDirty)
ReadValue(&fFullValueText);
return NaturalCompare(fFullValueText.String(),
compareTo->ValueAsText(view));
}
bool
StringAttributeText::CommitEditedText(BTextView* textView)
{
ASSERT(fColumn->Editable());
const char* text = textView->Text();
if (fFullValueText == text) {
return false;
}
if (textView->TextLength() == 0) {
return false;
}
fDirty = true;
if (!CommitEditedTextFlavor(textView))
return false;
fFullValueText = text;
return true;
}
ScalarAttributeText::ScalarAttributeText(const Model* model,
const BColumn* column)
:
WidgetAttributeText(model, column),
fValue(0),
fValueDirty(true)
{
}
int64
ScalarAttributeText::Value()
{
if (fValueDirty)
fValue = ReadValue();
return fValue;
}
bool
ScalarAttributeText::CheckAttributeChanged()
{
int64 newValue = ReadValue();
if (newValue == fValue)
return false;
fValue = newValue;
fDirty = true;
return true;
}
float
ScalarAttributeText::PreferredWidth(const BPoseView* pose) const
{
BString widthString;
widthString << fValue;
return pose->StringWidth(widthString.String());
}
int
ScalarAttributeText::Compare(WidgetAttributeText& attr, BPoseView*)
{
ScalarAttributeText* compareTo = dynamic_cast<ScalarAttributeText*>(&attr);
ThrowOnAssert(compareTo != NULL);
if (fValueDirty)
fValue = ReadValue();
return fValue >= compareTo->Value()
? (fValue == compareTo->Value() ? 0 : 1) : -1;
}
PathAttributeText::PathAttributeText(const Model* model, const BColumn* column)
:
StringAttributeText(model, column)
{
}
void
PathAttributeText::ReadValue(BString* outString)
{
BEntry entry(fModel->EntryRef());
BPath path;
if (entry.InitCheck() == B_OK && entry.GetPath(&path) == B_OK) {
*outString = path.Path();
TruncateLeaf(outString);
fValueIsDefined = true;
} else {
*outString = "-";
fValueIsDefined = false;
}
fValueDirty = false;
}
OriginalPathAttributeText::OriginalPathAttributeText(const Model* model,
const BColumn* column)
:
StringAttributeText(model, column)
{
}
void
OriginalPathAttributeText::ReadValue(BString* outString)
{
BEntry entry(fModel->EntryRef());
BPath path;
if (entry.InitCheck() == B_OK && FSGetOriginalPath(&entry, &path) == B_OK) {
*outString = path.Path();
fValueIsDefined = true;
} else {
*outString = "-";
fValueIsDefined = false;
}
fValueDirty = false;
}
KindAttributeText::KindAttributeText(const Model* model, const BColumn* column)
:
StringAttributeText(model, column)
{
}
void
KindAttributeText::ReadValue(BString* outString)
{
BMimeType mime;
char desc[B_MIME_TYPE_LENGTH];
if (mime.SetTo(fModel->MimeType()) != B_OK)
*outString = B_TRANSLATE("Unknown");
else if (mime.GetShortDescription(desc) == B_OK) {
*outString = desc;
} else
*outString = fModel->MimeType();
fValueDirty = false;
fValueIsDefined = true;
}
NameAttributeText::NameAttributeText(const Model* model,
const BColumn* column)
:
StringAttributeText(model, column)
{
}
int
NameAttributeText::Compare(WidgetAttributeText& attr, BPoseView* view)
{
NameAttributeText* compareTo = dynamic_cast<NameAttributeText*>(&attr);
ThrowOnAssert(compareTo != NULL);
if (fValueDirty)
ReadValue(&fFullValueText);
if (NameAttributeText::sSortFolderNamesFirst)
return fModel->CompareFolderNamesFirst(attr.TargetModel());
return NaturalCompare(fFullValueText.String(),
compareTo->ValueAsText(view));
}
void
NameAttributeText::ReadValue(BString* outString)
{
*outString = fModel->Name();
fValueDirty = false;
fValueIsDefined = true;
}
void
NameAttributeText::FitValue(BString* outString, const BPoseView* view)
{
if (fValueDirty)
ReadValue(&fFullValueText);
if (view->ViewMode() != kListMode)
fOldWidth = view->StringWidth("M") * 30;
else
fOldWidth = fColumn->Width();
fTruncatedWidth = TruncString(outString, fFullValueText.String(),
fFullValueText.Length(), view, fOldWidth, B_TRUNCATE_MIDDLE);
fDirty = false;
}
void
NameAttributeText::SetupEditing(BTextView* textView)
{
DisallowFilenameKeys(textView);
textView->SetMaxBytes(B_FILE_NAME_LENGTH);
textView->SetText(fFullValueText.String(), fFullValueText.Length());
}
bool
NameAttributeText::CommitEditedTextFlavor(BTextView* textView)
{
if (textView == NULL)
return false;
const char* name = textView->Text();
size_t length = (size_t)textView->TextLength();
BEntry entry(fModel->EntryRef());
status_t result = entry.InitCheck();
if (result == B_OK)
result = EditModelName(fModel, name, length);
return result == B_OK;
}
void
NameAttributeText::SetSortFolderNamesFirst(bool enabled)
{
NameAttributeText::sSortFolderNamesFirst = enabled;
}
bool
NameAttributeText::IsEditable() const
{
return StringAttributeText::IsEditable();
}
RealNameAttributeText::RealNameAttributeText(const Model* model,
const BColumn* column)
:
NameAttributeText(model, column)
{
}
int
RealNameAttributeText::Compare(WidgetAttributeText& attr, BPoseView* view)
{
RealNameAttributeText* compareTo
= dynamic_cast<RealNameAttributeText*>(&attr);
ThrowOnAssert(compareTo != NULL);
if (fValueDirty)
ReadValue(&fFullValueText);
if (RealNameAttributeText::sSortFolderNamesFirst)
return fModel->CompareFolderNamesFirst(attr.TargetModel());
return NaturalCompare(fFullValueText.String(),
compareTo->ValueAsText(view));
}
void
RealNameAttributeText::ReadValue(BString* outString)
{
*outString = fModel->EntryRef()->name;
fValueDirty = false;
fValueIsDefined = true;
}
void
RealNameAttributeText::FitValue(BString* outString, const BPoseView* view)
{
if (fValueDirty)
ReadValue(&fFullValueText);
fOldWidth = fColumn->Width();
fTruncatedWidth = TruncString(outString, fFullValueText.String(),
fFullValueText.Length(), view, fOldWidth, B_TRUNCATE_MIDDLE);
fDirty = false;
}
void
RealNameAttributeText::SetupEditing(BTextView* textView)
{
DisallowFilenameKeys(textView);
textView->SetMaxBytes(B_FILE_NAME_LENGTH);
textView->SetText(fFullValueText.String(), fFullValueText.Length());
}
void
RealNameAttributeText::SetSortFolderNamesFirst(bool enabled)
{
RealNameAttributeText::sSortFolderNamesFirst = enabled;
}
#ifdef OWNER_GROUP_ATTRIBUTES
OwnerAttributeText::OwnerAttributeText(const Model* model,
const BColumn* column)
:
StringAttributeText(model, column)
{
}
void
OwnerAttributeText::ReadValue(BString* outString)
{
uid_t nodeOwner = fModel->StatBuf()->st_uid;
BString user;
if (nodeOwner == 0) {
if (getenv("USER") != NULL)
user << getenv("USER");
else
user << "root";
} else
user << nodeOwner;
*outString = user.String();
fValueDirty = false;
fValueIsDefined = true;
}
GroupAttributeText::GroupAttributeText(const Model* model,
const BColumn* column)
:
StringAttributeText(model, column)
{
}
void
GroupAttributeText::ReadValue(BString* outString)
{
gid_t nodeGroup = fModel->StatBuf()->st_gid;
BString group;
if (nodeGroup == 0) {
if (getenv("GROUP") != NULL)
group << getenv("GROUP");
else
group << "0";
} else
group << nodeGroup;
*outString = group.String();
fValueDirty = false;
fValueIsDefined = true;
}
#endif
ModeAttributeText::ModeAttributeText(const Model* model,
const BColumn* column)
:
StringAttributeText(model, column)
{
}
void
ModeAttributeText::ReadValue(BString* outString)
{
mode_t mode = fModel->StatBuf()->st_mode;
mode_t baseMask = 00400;
char buffer[11];
char* scanner = buffer;
if (S_ISDIR(mode))
*scanner++ = 'd';
else if (S_ISLNK(mode))
*scanner++ = 'l';
else if (S_ISBLK(mode))
*scanner++ = 'b';
else if (S_ISCHR(mode))
*scanner++ = 'c';
else
*scanner++ = '-';
for (int32 index = 0; index < 9; index++) {
*scanner++ = (mode & baseMask) ? "rwx"[index % 3] : '-';
baseMask >>= 1;
}
*scanner = 0;
*outString = buffer;
fValueDirty = false;
fValueIsDefined = true;
}
SizeAttributeText::SizeAttributeText(const Model* model,
const BColumn* column)
:
ScalarAttributeText(model, column)
{
}
int64
SizeAttributeText::ReadValue()
{
fValueDirty = false;
if (fModel->IsVolume()) {
BVolume volume(fModel->NodeRef()->device);
fValueIsDefined = volume.Capacity() != 0;
return volume.Capacity();
}
if (fModel->IsDirectory() || fModel->IsQuery()
|| fModel->IsQueryTemplate() || fModel->IsSymLink()
|| fModel->IsVirtualDirectory()) {
fValueIsDefined = false;
return kUnknownSize;
}
fValueIsDefined = true;
return fModel->StatBuf()->st_size;
}
void
SizeAttributeText::FitValue(BString* outString, const BPoseView* poseView)
{
if (fValueDirty)
fValue = ReadValue();
fOldWidth = fColumn->Width();
if (!fValueIsDefined) {
*outString = "-";
fTruncatedWidth = poseView->StringWidth("-");
} else
fTruncatedWidth = TruncFileSize(outString, fValue, poseView, fOldWidth);
fDirty = false;
}
float
SizeAttributeText::PreferredWidth(const BPoseView* poseView) const
{
if (!fValueIsDefined)
return poseView->StringWidth("-");
BString widthString;
TruncFileSize(&widthString, fValue, poseView, 100000);
return poseView->StringWidth(widthString.String());
}
TimeAttributeText::TimeAttributeText(const Model* model,
const BColumn* column)
:
ScalarAttributeText(model, column),
fLastClockIs24(false),
fLastDateOrder(kDateFormatEnd),
fLastTimeFormatSeparator(kSeparatorsEnd)
{
}
float
TimeAttributeText::PreferredWidth(const BPoseView* pose) const
{
BString widthString;
TruncTimeBase(&widthString, fValue, pose, 100000);
return pose->StringWidth(widthString.String());
}
void
TimeAttributeText::FitValue(BString* outString, const BPoseView* view)
{
if (fValueDirty)
fValue = ReadValue();
fOldWidth = fColumn->Width();
fTruncatedWidth = TruncTime(outString, fValue, view, fOldWidth);
fDirty = false;
}
bool
TimeAttributeText::CheckSettingsChanged(void)
{
return false;
}
CreationTimeAttributeText::CreationTimeAttributeText(const Model* model,
const BColumn* column)
:
TimeAttributeText(model, column)
{
}
int64
CreationTimeAttributeText::ReadValue()
{
fValueDirty = false;
fValueIsDefined = true;
return fModel->StatBuf()->st_crtime;
}
ModificationTimeAttributeText::ModificationTimeAttributeText(
const Model* model, const BColumn* column)
:
TimeAttributeText(model, column)
{
}
int64
ModificationTimeAttributeText::ReadValue()
{
fValueDirty = false;
fValueIsDefined = true;
return fModel->StatBuf()->st_mtime;
}
GenericAttributeText::GenericAttributeText(const Model* model,
const BColumn* column)
:
StringAttributeText(model, column)
{
}
bool
GenericAttributeText::CheckAttributeChanged()
{
GenericValueStruct tmpValue = fValue;
BString tmpString(fFullValueText);
ReadValue(&fFullValueText);
bool changed = fValue.int64t != tmpValue.int64t || tmpString != fFullValueText;
if (changed)
fDirty = true;
return fDirty;
}
float
GenericAttributeText::PreferredWidth(const BPoseView* pose) const
{
return pose->StringWidth(fFullValueText.String());
}
void
GenericAttributeText::ReadValue(BString* outString)
{
BModelOpener opener(const_cast<Model*>(fModel));
ssize_t length = 0;
fFullValueText = "-";
fValue.int64t = 0;
fValueIsDefined = false;
fValueDirty = false;
if (!fModel->Node())
return;
switch (fColumn->AttrType()) {
case B_STRING_TYPE:
{
char buffer[kGenericReadBufferSize];
length = fModel->Node()->ReadAttr(fColumn->AttrName(),
fColumn->AttrType(), 0, buffer, kGenericReadBufferSize - 1);
if (length > 0) {
buffer[length] = '\0';
*outString = buffer;
fValueIsDefined = true;
}
break;
}
case B_SSIZE_T_TYPE:
case B_TIME_TYPE:
case B_OFF_T_TYPE:
case B_FLOAT_TYPE:
case B_BOOL_TYPE:
case B_CHAR_TYPE:
case B_INT8_TYPE:
case B_INT16_TYPE:
case B_INT32_TYPE:
case B_INT64_TYPE:
case B_UINT8_TYPE:
case B_UINT16_TYPE:
case B_UINT32_TYPE:
case B_UINT64_TYPE:
case B_DOUBLE_TYPE:
{
attr_info info;
GenericValueStruct tmp;
if (fModel->Node()->GetAttrInfo(fColumn->AttrName(), &info)
== B_OK) {
if (info.size && info.size <= (off_t)sizeof(int64)) {
length = fModel->Node()->ReadAttr(fColumn->AttrName(),
fColumn->AttrType(), 0, &tmp, (size_t)info.size);
}
if (length == info.size) {
if (fColumn->AttrType() == B_FLOAT_TYPE
|| fColumn->AttrType() == B_DOUBLE_TYPE) {
switch (info.size) {
case sizeof(float):
fValueIsDefined = true;
fValue.floatt = tmp.floatt;
break;
case sizeof(double):
fValueIsDefined = true;
fValue.doublet = tmp.doublet;
break;
default:
TRESPASS();
break;
}
} else {
switch (info.size) {
case sizeof(char):
fValueIsDefined = true;
fValue.int8t = tmp.int8t;
break;
case sizeof(int16):
fValueIsDefined = true;
fValue.int16t = tmp.int16t;
break;
case sizeof(int32):
fValueIsDefined = true;
fValue.int32t = tmp.int32t;
break;
case sizeof(int64):
fValueIsDefined = true;
fValue.int64t = tmp.int64t;
break;
default:
TRESPASS();
break;
}
}
}
}
break;
}
}
}
void
GenericAttributeText::FitValue(BString* outString, const BPoseView* view)
{
if (fValueDirty)
ReadValue(&fFullValueText);
fOldWidth = fColumn->Width();
if (!fValueIsDefined) {
*outString = "-";
fTruncatedWidth = TruncString(outString, fFullValueText.String(),
fFullValueText.Length(), view, fOldWidth);
fDirty = false;
return;
}
char buffer[256];
switch (fColumn->AttrType()) {
case B_SIZE_T_TYPE:
TruncFileSizeBase(outString, fValue.int32t, view, fOldWidth);
return;
case B_SSIZE_T_TYPE:
if (fValue.int32t > 0) {
TruncFileSizeBase(outString, fValue.int32t, view, fOldWidth);
return;
}
sprintf(buffer, "%s", strerror(fValue.int32t));
fFullValueText = buffer;
break;
case B_STRING_TYPE:
fTruncatedWidth = TruncString(outString, fFullValueText.String(),
fFullValueText.Length(), view, fOldWidth);
fDirty = false;
return;
case B_OFF_T_TYPE:
TruncFileSize(&fFullValueText, fValue.off_tt, view, 100000);
fTruncatedWidth = TruncFileSize(outString, fValue.off_tt, view,
fOldWidth);
fDirty = false;
return;
case B_TIME_TYPE:
TruncTime(&fFullValueText, fValue.time_tt, view, 100000);
fTruncatedWidth = TruncTime(outString, fValue.time_tt, view,
fOldWidth);
fDirty = false;
return;
case B_BOOL_TYPE:
sprintf(buffer, "%s", fValue.boolt ? "true" : "false");
fFullValueText = buffer;
break;
case B_CHAR_TYPE:
if (!isprint(fValue.uint8t)) {
*outString = "-";
fTruncatedWidth = TruncString(outString, fFullValueText.String(),
fFullValueText.Length(), view, fOldWidth);
fDirty = false;
return;
}
sprintf(buffer, "%c", fValue.uint8t);
fFullValueText = buffer;
break;
case B_INT8_TYPE:
sprintf(buffer, "%d", fValue.int8t);
fFullValueText = buffer;
break;
case B_UINT8_TYPE:
sprintf(buffer, "%d", fValue.uint8t);
fFullValueText = buffer;
break;
case B_INT16_TYPE:
sprintf(buffer, "%d", fValue.int16t);
fFullValueText = buffer;
break;
case B_UINT16_TYPE:
sprintf(buffer, "%d", fValue.uint16t);
fFullValueText = buffer;
break;
case B_INT32_TYPE:
sprintf(buffer, "%" B_PRId32, fValue.int32t);
fFullValueText = buffer;
break;
case B_UINT32_TYPE:
sprintf(buffer, "%" B_PRId32, fValue.uint32t);
fFullValueText = buffer;
break;
case B_INT64_TYPE:
sprintf(buffer, "%" B_PRId64, fValue.int64t);
fFullValueText = buffer;
break;
case B_UINT64_TYPE:
sprintf(buffer, "%" B_PRId64, fValue.uint64t);
fFullValueText = buffer;
break;
case B_FLOAT_TYPE:
snprintf(buffer, sizeof(buffer), "%g", fValue.floatt);
fFullValueText = buffer;
break;
case B_DOUBLE_TYPE:
snprintf(buffer, sizeof(buffer), "%g", fValue.doublet);
fFullValueText = buffer;
break;
default:
*outString = "-";
fTruncatedWidth = TruncString(outString, fFullValueText.String(),
fFullValueText.Length(), view, fOldWidth);
fDirty = false;
return;
}
fTruncatedWidth = TruncString(outString, buffer, (ssize_t)strlen(buffer),
view, fOldWidth);
fDirty = false;
}
const char*
GenericAttributeText::ValueAsText(const BPoseView* view)
{
bool oldDirty = fDirty;
BString outString;
FitValue(&outString, view);
fDirty = oldDirty;
return fFullValueText.String();
}
int
GenericAttributeText::Compare(WidgetAttributeText& attr, BPoseView*)
{
GenericAttributeText* compareTo
= dynamic_cast<GenericAttributeText*>(&attr);
ThrowOnAssert(compareTo != NULL);
if (fValueDirty)
ReadValue(&fFullValueText);
if (compareTo->fValueDirty)
compareTo->ReadValue(&compareTo->fFullValueText);
if (!fValueIsDefined)
return compareTo->fValueIsDefined ? 1 : 0;
if (!compareTo->fValueIsDefined)
return -1;
switch (fColumn->AttrType()) {
case B_STRING_TYPE:
return fFullValueText.ICompare(compareTo->fFullValueText);
case B_CHAR_TYPE:
{
char vStr[2] = { static_cast<char>(fValue.uint8t), 0 };
char cStr[2] = { static_cast<char>(compareTo->fValue.uint8t), 0};
BString valueStr(vStr);
BString compareToStr(cStr);
return valueStr.ICompare(compareToStr);
}
case B_FLOAT_TYPE:
return fValue.floatt >= compareTo->fValue.floatt ?
(fValue.floatt == compareTo->fValue.floatt ? 0 : 1) : -1;
case B_DOUBLE_TYPE:
return fValue.doublet >= compareTo->fValue.doublet ?
(fValue.doublet == compareTo->fValue.doublet ? 0 : 1) : -1;
case B_BOOL_TYPE:
return fValue.boolt >= compareTo->fValue.boolt ?
(fValue.boolt == compareTo->fValue.boolt ? 0 : 1) : -1;
case B_UINT8_TYPE:
return fValue.uint8t >= compareTo->fValue.uint8t ?
(fValue.uint8t == compareTo->fValue.uint8t ? 0 : 1) : -1;
case B_INT8_TYPE:
return fValue.int8t >= compareTo->fValue.int8t ?
(fValue.int8t == compareTo->fValue.int8t ? 0 : 1) : -1;
case B_UINT16_TYPE:
return fValue.uint16t >= compareTo->fValue.uint16t ?
(fValue.uint16t == compareTo->fValue.uint16t ? 0 : 1) : -1;
case B_INT16_TYPE:
return fValue.int16t >= compareTo->fValue.int16t ?
(fValue.int16t == compareTo->fValue.int16t ? 0 : 1) : -1;
case B_UINT32_TYPE:
return fValue.uint32t >= compareTo->fValue.uint32t ?
(fValue.uint32t == compareTo->fValue.uint32t ? 0 : 1) : -1;
case B_TIME_TYPE:
case B_INT32_TYPE:
return fValue.int32t >= compareTo->fValue.int32t ?
(fValue.int32t == compareTo->fValue.int32t ? 0 : 1) : -1;
case B_OFF_T_TYPE:
case B_INT64_TYPE:
return fValue.int64t >= compareTo->fValue.int64t ?
(fValue.int64t == compareTo->fValue.int64t ? 0 : 1) : -1;
case B_UINT64_TYPE:
default:
return fValue.uint64t >= compareTo->fValue.uint64t ?
(fValue.uint64t == compareTo->fValue.uint64t ? 0 : 1) : -1;
}
return 0;
}
bool
GenericAttributeText::CommitEditedText(BTextView* textView)
{
ASSERT(fColumn->Editable());
const char* text = textView->Text();
if (fFullValueText == text)
return false;
if (!CommitEditedTextFlavor(textView))
return false;
fFullValueText = text;
fDirty = true;
fValueDirty = true;
return true;
}
void
GenericAttributeText::SetupEditing(BTextView* textView)
{
textView->SetMaxBytes(kGenericReadBufferSize - 1);
textView->SetText(fFullValueText.String(), fFullValueText.Length());
}
bool
GenericAttributeText::CommitEditedTextFlavor(BTextView* textView)
{
BNode node(fModel->EntryRef());
if (node.InitCheck() != B_OK)
return false;
uint32 type = fColumn->AttrType();
if (type != B_STRING_TYPE
&& type != B_UINT64_TYPE
&& type != B_UINT32_TYPE
&& type != B_UINT16_TYPE
&& type != B_UINT8_TYPE
&& type != B_INT64_TYPE
&& type != B_INT32_TYPE
&& type != B_INT16_TYPE
&& type != B_INT8_TYPE
&& type != B_OFF_T_TYPE
&& type != B_TIME_TYPE
&& type != B_FLOAT_TYPE
&& type != B_DOUBLE_TYPE
&& type != B_CHAR_TYPE
&& type != B_BOOL_TYPE) {
BAlert* alert = new BAlert("",
B_TRANSLATE("Sorry, you cannot edit that attribute."),
B_TRANSLATE("Cancel"),
0, 0, B_WIDTH_AS_USUAL, B_STOP_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
return false;
}
const char* columnName = fColumn->AttrName();
ssize_t size = 0;
switch (type) {
case B_STRING_TYPE:
size = fModel->WriteAttr(columnName, type, 0, textView->Text(),
(size_t)textView->TextLength() + 1);
break;
case B_BOOL_TYPE:
{
bool value = strncasecmp(textView->Text(), "0", 1) != 0
&& strncasecmp(textView->Text(), "off", 2) != 0
&& strncasecmp(textView->Text(), "no", 3) != 0
&& strncasecmp(textView->Text(), "false", 4) != 0
&& strlen(textView->Text()) != 0;
size = fModel->WriteAttr(columnName, type, 0, &value, sizeof(bool));
break;
}
case B_CHAR_TYPE:
{
char ch;
sscanf(textView->Text(), "%c", &ch);
if (!isprint(ch)) {
BAlert* alert = new BAlert("",
B_TRANSLATE("Sorry, the 'Character' "
"attribute cannot store a multi-byte glyph."),
B_TRANSLATE("Cancel"),
0, 0, B_WIDTH_AS_USUAL, B_STOP_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
return false;
}
size = fModel->WriteAttr(columnName, type, 0, &ch, sizeof(char));
break;
}
case B_FLOAT_TYPE:
{
float floatVal;
if (sscanf(textView->Text(), "%f", &floatVal) == 1) {
fValueIsDefined = true;
fValue.floatt = floatVal;
size = fModel->WriteAttr(columnName, type, 0, &floatVal,
sizeof(float));
} else {
return fValueIsDefined;
}
break;
}
case B_DOUBLE_TYPE:
{
double doubleVal;
if (sscanf(textView->Text(), "%lf", &doubleVal) == 1) {
fValueIsDefined = true;
fValue.doublet = doubleVal;
size = fModel->WriteAttr(columnName, type, 0, &doubleVal,
sizeof(double));
} else {
return fValueIsDefined;
}
break;
}
case B_TIME_TYPE:
case B_OFF_T_TYPE:
case B_UINT64_TYPE:
case B_UINT32_TYPE:
case B_UINT16_TYPE:
case B_UINT8_TYPE:
case B_INT64_TYPE:
case B_INT32_TYPE:
case B_INT16_TYPE:
case B_INT8_TYPE:
{
GenericValueStruct tmp;
size_t scalarSize = 0;
switch (type) {
case B_TIME_TYPE:
tmp.time_tt = parsedate(textView->Text(), time(0));
scalarSize = sizeof(time_t);
break;
case B_OFF_T_TYPE:
tmp.off_tt = StringToScalar(textView->Text());
scalarSize = sizeof(off_t);
break;
case B_UINT64_TYPE:
case B_INT64_TYPE:
tmp.int64t = StringToScalar(textView->Text());
scalarSize = sizeof(int64);
break;
case B_UINT32_TYPE:
case B_INT32_TYPE:
tmp.int32t = (int32)StringToScalar(textView->Text());
scalarSize = sizeof(int32);
break;
case B_UINT16_TYPE:
case B_INT16_TYPE:
tmp.int16t = (int16)StringToScalar(textView->Text());
scalarSize = sizeof(int16);
break;
case B_UINT8_TYPE:
case B_INT8_TYPE:
tmp.int8t = (int8)StringToScalar(textView->Text());
scalarSize = sizeof(int8);
break;
default:
TRESPASS();
}
size = fModel->WriteAttr(columnName, type, 0, &tmp, scalarSize);
break;
}
}
if (size < 0) {
BAlert* alert = new BAlert("",
B_TRANSLATE("There was an error writing the attribute."),
B_TRANSLATE("Cancel"),
0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
fValueIsDefined = false;
return false;
}
fValueIsDefined = true;
return true;
}
DurationAttributeText::DurationAttributeText(const Model* model,
const BColumn* column)
:
GenericAttributeText(model, column)
{
}
void
DurationAttributeText::FitValue(BString* outString, const BPoseView* view)
{
if (fValueDirty)
ReadValue(&fFullValueText);
fOldWidth = fColumn->Width();
fDirty = false;
if (!fValueIsDefined) {
*outString = "-";
fTruncatedWidth = TruncString(outString, fFullValueText.String(),
fFullValueText.Length(), view, fOldWidth);
return;
}
int64 time = 0;
switch (fColumn->AttrType()) {
case B_TIME_TYPE:
time = fValue.time_tt * 1000000LL;
break;
case B_INT8_TYPE:
time = fValue.int8t * 1000000LL;
break;
case B_INT16_TYPE:
time = fValue.int16t * 1000000LL;
break;
case B_INT32_TYPE:
time = fValue.int32t * 1000000LL;
break;
case B_INT64_TYPE:
time = fValue.int64t;
break;
}
int32 seconds = time / 1000000LL;
bool negative = seconds < 0;
if (negative)
seconds = -seconds;
int32 hours = seconds / 3600;
seconds -= hours * 3600;
int32 minutes = seconds / 60;
seconds = seconds % 60;
char buffer[256];
if (hours > 0) {
snprintf(buffer, sizeof(buffer), "%s%" B_PRId32 ":%02" B_PRId32 ":%02"
B_PRId32, negative ? "-" : "", hours, minutes, seconds);
} else {
snprintf(buffer, sizeof(buffer), "%s%" B_PRId32 ":%02" B_PRId32,
negative ? "-" : "", minutes, seconds);
}
fFullValueText = buffer;
fTruncatedWidth = TruncString(outString, fFullValueText.String(),
fFullValueText.Length(), view, fOldWidth);
}
CheckboxAttributeText::CheckboxAttributeText(const Model* model,
const BColumn* column)
:
GenericAttributeText(model, column),
fOnChar("✖"),
fOffChar("-")
{
if (const char* separator = strchr(column->DisplayAs(), ':')) {
BString chars(separator + 1);
int32 length;
const char* c = chars.CharAt(0, &length);
fOnChar.SetTo(c, length);
if (c[length]) {
c = chars.CharAt(1, &length);
fOffChar.SetTo(c, length);
}
}
}
void
CheckboxAttributeText::SetupEditing(BTextView* view)
{
BString outString;
GenericAttributeText::FitValue(&outString, NULL);
GenericAttributeText::SetupEditing(view);
}
void
CheckboxAttributeText::FitValue(BString* outString, const BPoseView* view)
{
if (fValueDirty)
ReadValue(&fFullValueText);
fOldWidth = fColumn->Width();
fDirty = false;
if (!fValueIsDefined) {
*outString = fOffChar;
fTruncatedWidth = TruncString(outString, fFullValueText.String(),
fFullValueText.Length(), view, fOldWidth);
return;
}
bool checked = false;
switch (fColumn->AttrType()) {
case B_BOOL_TYPE:
checked = fValue.boolt;
break;
case B_INT8_TYPE:
case B_UINT8_TYPE:
checked = fValue.int8t != 0;
break;
case B_INT16_TYPE:
case B_UINT16_TYPE:
checked = fValue.int16t != 0;
break;
case B_INT32_TYPE:
case B_UINT32_TYPE:
checked = fValue.int32t != 0;
break;
}
fFullValueText = checked ? fOnChar : fOffChar;
fTruncatedWidth = TruncString(outString, fFullValueText.String(),
fFullValueText.Length(), view, fOldWidth);
}
RatingAttributeText::RatingAttributeText(const Model* model,
const BColumn* column)
:
GenericAttributeText(model, column),
fCount(5),
fMax(10)
{
}
void
RatingAttributeText::SetupEditing(BTextView* view)
{
BString outString;
GenericAttributeText::FitValue(&outString, NULL);
GenericAttributeText::SetupEditing(view);
}
void
RatingAttributeText::FitValue(BString* ratingString, const BPoseView* view)
{
if (fValueDirty)
ReadValue(&fFullValueText);
fOldWidth = fColumn->Width();
fDirty = false;
int64 rating;
if (fValueIsDefined) {
switch (fColumn->AttrType()) {
case B_INT8_TYPE:
rating = fValue.int8t;
break;
case B_INT16_TYPE:
rating = fValue.int16t;
break;
case B_INT32_TYPE:
rating = fValue.int32t;
break;
default:
rating = 0;
break;
}
} else
rating = 0;
if (rating > fMax)
rating = fMax;
if (rating < 0)
rating = 0;
int32 steps = fMax / fCount;
fFullValueText = "";
for (int32 i = 0; i < fCount; i++) {
int64 n = i * steps;
if (rating > n + steps / 2)
fFullValueText += "★";
else if (rating > n)
fFullValueText += "⯪";
else
fFullValueText += "☆";
}
fTruncatedWidth = TruncString(ratingString, fFullValueText.String(),
fFullValueText.Length(), view, fOldWidth);
}
OpenWithRelationAttributeText::OpenWithRelationAttributeText(const Model* model,
const BColumn* column, const BPoseView* view)
:
ScalarAttributeText(model, column),
fPoseView(view)
{
}
int64
OpenWithRelationAttributeText::ReadValue()
{
fValueDirty = false;
const OpenWithPoseView* view
= dynamic_cast<const OpenWithPoseView*>(fPoseView);
if (view != NULL) {
fValue = view->OpenWithRelation(fModel);
fValueIsDefined = true;
}
return fValue;
}
float
OpenWithRelationAttributeText::PreferredWidth(const BPoseView* pose) const
{
BString widthString;
TruncString(&widthString, fRelationText.String(), fRelationText.Length(),
pose, 500, B_TRUNCATE_END);
return pose->StringWidth(widthString.String());
}
void
OpenWithRelationAttributeText::FitValue(BString* outString,
const BPoseView* view)
{
if (fValueDirty)
ReadValue();
ASSERT(view == fPoseView);
const OpenWithPoseView* launchWithView
= dynamic_cast<const OpenWithPoseView*>(view);
if (launchWithView != NULL)
launchWithView->OpenWithRelationDescription(fModel, &fRelationText);
fOldWidth = fColumn->Width();
fTruncatedWidth = TruncString(outString, fRelationText.String(),
fRelationText.Length(), view, fOldWidth, B_TRUNCATE_END);
fDirty = false;
}
VersionAttributeText::VersionAttributeText(const Model* model,
const BColumn* column, bool app)
:
StringAttributeText(model, column),
fAppVersion(app)
{
}
void
VersionAttributeText::ReadValue(BString* outString)
{
fValueDirty = false;
BModelOpener opener(fModel);
BFile* file = dynamic_cast<BFile*>(fModel->Node());
if (file != NULL) {
BAppFileInfo info(file);
version_info version;
if (info.InitCheck() == B_OK
&& info.GetVersionInfo(&version, fAppVersion
? B_APP_VERSION_KIND : B_SYSTEM_VERSION_KIND) == B_OK) {
*outString = version.short_info;
fValueIsDefined = true;
return;
}
}
*outString = "-";
fValueIsDefined = false;
}