Open Tracker License
Terms and Conditions
Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice applies to all licensees
and shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of Be Incorporated shall not be
used in advertising or otherwise to promote the sale, use or other dealings in
this Software without prior written authorization from Be Incorporated.
Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
of Be Incorporated in the United States and other countries. Other brand product
names are registered trademarks or trademarks of their respective holders.
All rights reserved.
*/
#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.SetType(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;
}