git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@34131 a95241bf-73f2-0310-859d-f6bbb57e9c96
1830 lines
40 KiB
C++
1830 lines
40 KiB
C++
/*
|
|
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 <fs_attr.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <parsedate.h>
|
|
|
|
#include <Alert.h>
|
|
#include <AppFileInfo.h>
|
|
#include <Debug.h>
|
|
#include <NodeInfo.h>
|
|
#include <Path.h>
|
|
#include <TextView.h>
|
|
#include <Volume.h>
|
|
#include <VolumeRoster.h>
|
|
|
|
#include "Attributes.h"
|
|
#include "FindPanel.h"
|
|
#include "FSUndoRedo.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"
|
|
#include "WidgetAttributeText.h"
|
|
|
|
|
|
const int32 kGenericReadBufferSize = 1024;
|
|
|
|
template <class View>
|
|
float
|
|
TruncStringBase(BString *result, const char *str, int32 length,
|
|
const View *view, float width, uint32 truncMode = B_TRUNCATE_MIDDLE)
|
|
{
|
|
// we are using a template version of this call to make sure
|
|
// the right StringWidth gets picked up for BView x BPoseView
|
|
// for max speed and flexibility
|
|
|
|
// a standard ellipsis inserting fitting algorithm
|
|
if (view->StringWidth(str, length) <= width)
|
|
*result = str;
|
|
else {
|
|
const char *srcstr[1];
|
|
char *results[1];
|
|
|
|
srcstr[0] = str;
|
|
results[0] = result->LockBuffer(length + 3);
|
|
|
|
BFont font;
|
|
view->GetFont(&font);
|
|
|
|
font.GetTruncatedStrings(srcstr, 1, truncMode, width, results);
|
|
result->UnlockBuffer();
|
|
}
|
|
return view->StringWidth(result->String(), result->Length());
|
|
}
|
|
|
|
|
|
WidgetAttributeText *
|
|
WidgetAttributeText::NewWidgetText(const Model *model,
|
|
const BColumn *column, const BPoseView *view)
|
|
{
|
|
// call this to make the right WidgetAttributeText type for a
|
|
// given column
|
|
|
|
const char *attrName = column->AttrName();
|
|
|
|
if (strcmp(attrName, kAttrPath) == 0)
|
|
return new PathAttributeText(model, column);
|
|
else if (strcmp(attrName, kAttrMIMEType) == 0)
|
|
return new KindAttributeText(model, column);
|
|
else if (strcmp(attrName, kAttrStatName) == 0)
|
|
return new NameAttributeText(model, column);
|
|
else if (strcmp(attrName, kAttrStatSize) == 0)
|
|
return new SizeAttributeText(model, column);
|
|
else if (strcmp(attrName, kAttrStatModified) == 0)
|
|
return new ModificationTimeAttributeText(model, column);
|
|
else if (strcmp(attrName, kAttrStatCreated) == 0)
|
|
return new CreationTimeAttributeText(model, column);
|
|
#ifdef OWNER_GROUP_ATTRIBUTES
|
|
else if (strcmp(attrName, kAttrStatOwner) == 0)
|
|
return new OwnerAttributeText(model, column);
|
|
else if (strcmp(attrName, kAttrStatGroup) == 0)
|
|
return new GroupAttributeText(model, column);
|
|
#endif
|
|
else if (strcmp(attrName, kAttrStatMode) == 0)
|
|
return new ModeAttributeText(model, column);
|
|
else if (strcmp(attrName, kAttrOpenWithRelation) == 0)
|
|
return new OpenWithRelationAttributeText(model, column, view);
|
|
else if (strcmp(attrName, kAttrAppVersion) == 0)
|
|
return new AppShortVersionAttributeText(model, column);
|
|
else if (strcmp(attrName, kAttrSystemVersion) == 0)
|
|
return new SystemShortVersionAttributeText(model, column);
|
|
else if (strcmp(attrName, kAttrOriginalPath) == 0)
|
|
return new OriginalPathAttributeText(model, column);
|
|
|
|
return new GenericAttributeText(model, column);
|
|
}
|
|
|
|
|
|
WidgetAttributeText::WidgetAttributeText(const Model *model,
|
|
const BColumn *column)
|
|
:
|
|
fModel(const_cast<Model *>(model)),
|
|
fColumn(column),
|
|
fDirty(true),
|
|
fValueIsDefined(false)
|
|
{
|
|
ASSERT(fColumn);
|
|
ASSERT(fColumn->Width() > 0);
|
|
}
|
|
|
|
|
|
WidgetAttributeText::~WidgetAttributeText()
|
|
{
|
|
}
|
|
|
|
|
|
const char *
|
|
WidgetAttributeText::FittingText(const BPoseView *view)
|
|
{
|
|
if (fDirty || fColumn->Width() != fOldWidth || CheckSettingsChanged()
|
|
|| !fValueIsDefined )
|
|
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 *result, const char *str,
|
|
int32 length, const BPoseView *view, float width, uint32 truncMode)
|
|
{
|
|
return TruncStringBase(result, str, length, view, width, truncMode);
|
|
}
|
|
|
|
|
|
const char *kSizeFormats[] = {
|
|
"%.2f %s",
|
|
"%.1f %s",
|
|
"%.f %s",
|
|
"%.f%s",
|
|
0
|
|
};
|
|
|
|
|
|
template <class View>
|
|
float
|
|
TruncFileSizeBase(BString *result, int64 value, const View *view, float width)
|
|
{
|
|
// ToDo:
|
|
// if slow, replace float divisions with shifts
|
|
// if fast enough, try fitting more decimal places
|
|
|
|
// format file size value
|
|
char buffer[1024];
|
|
if (value == kUnknownSize) {
|
|
*result = "-";
|
|
return view->StringWidth("-");
|
|
} else if (value < kKBSize) {
|
|
sprintf(buffer, "%Ld bytes", value);
|
|
if (view->StringWidth(buffer) > width)
|
|
sprintf(buffer, "%Ld B", value);
|
|
} else {
|
|
const char *suffix;
|
|
float floatValue;
|
|
if (value >= kTBSize) {
|
|
suffix = "TB";
|
|
floatValue = (float)value / kTBSize;
|
|
} else if (value >= kGBSize) {
|
|
suffix = "GB";
|
|
floatValue = (float)value / kGBSize;
|
|
} else if (value >= kMBSize) {
|
|
suffix = "MB";
|
|
floatValue = (float)value / kMBSize;
|
|
} else {
|
|
ASSERT(value >= kKBSize);
|
|
suffix = "KB";
|
|
floatValue = (float)value / kKBSize;
|
|
}
|
|
|
|
for (int32 index = 0; ; index++) {
|
|
if (!kSizeFormats[index])
|
|
break;
|
|
|
|
sprintf(buffer, kSizeFormats[index], floatValue, suffix);
|
|
|
|
// strip off an insignificant zero so we don't get readings
|
|
// such as 1.00
|
|
char *period = 0;
|
|
for (char *tmp = buffer; *tmp; tmp++) {
|
|
if (*tmp == '.')
|
|
period = tmp;
|
|
}
|
|
if (period && period[1] && period[2] == '0')
|
|
// move the rest of the string over the insignificant zero
|
|
for (char *tmp = &period[2]; *tmp; tmp++)
|
|
*tmp = tmp[1];
|
|
|
|
float resultWidth = view->StringWidth(buffer);
|
|
if (resultWidth <= width) {
|
|
*result = buffer;
|
|
return resultWidth;
|
|
}
|
|
}
|
|
}
|
|
|
|
return TruncStringBase(result, buffer, (ssize_t)strlen(buffer), view, width,
|
|
(uint32)B_TRUNCATE_END);
|
|
}
|
|
|
|
|
|
float
|
|
WidgetAttributeText::TruncFileSize(BString *result, int64 value,
|
|
const BPoseView *view, float width)
|
|
{
|
|
return TruncFileSizeBase(result, value, view, width);
|
|
}
|
|
|
|
/*
|
|
const char *kTimeFormats[] = {
|
|
"%A, %B %d %Y, %I:%M:%S %p", // Monday, July 09 1997, 05:08:15 PM
|
|
"%a, %b %d %Y, %I:%M:%S %p", // Mon, Jul 09 1997, 05:08:15 PM
|
|
"%a, %b %d %Y, %I:%M %p", // Mon, Jul 09 1997, 05:08 PM
|
|
"%b %d %Y, %I:%M %p", // Jul 09 1997, 05:08 PM
|
|
"%m/%d/%y, %I:%M %p", // 07/09/97, 05:08 PM
|
|
"%m/%d/%y", // 07/09/97
|
|
NULL
|
|
};
|
|
*/
|
|
|
|
status_t
|
|
TimeFormat(BString &string, int32 index, FormatSeparator separator,
|
|
DateOrder order, bool clockIs24Hour)
|
|
{
|
|
if (index >= 6)
|
|
return B_ERROR;
|
|
|
|
BString clockString;
|
|
BString dateString;
|
|
|
|
if (index <= 1)
|
|
if (clockIs24Hour)
|
|
clockString = "%H:%M:%S";
|
|
else
|
|
clockString = "%I:%M:%S %p";
|
|
else
|
|
if (clockIs24Hour)
|
|
clockString = "%H:%M";
|
|
else
|
|
clockString = "%I:%M %p";
|
|
|
|
if (index <= 3) {
|
|
switch (order) {
|
|
case kYMDFormat:
|
|
dateString = "%Y %! %d";
|
|
break;
|
|
case kDMYFormat:
|
|
dateString = "%d %! %Y";
|
|
break;
|
|
case kMDYFormat:
|
|
// Fall through
|
|
case kDateFormatEnd:
|
|
// Fall through
|
|
default:
|
|
dateString = "%! %d %Y";
|
|
break;
|
|
}
|
|
if (index == 0)
|
|
dateString.Replace('!', 'B', 1);
|
|
else
|
|
dateString.Replace('!', 'b', 1);
|
|
} else {
|
|
switch (order) {
|
|
case kYMDFormat:
|
|
dateString = "%y!%m!%d";
|
|
break;
|
|
case kDMYFormat:
|
|
dateString = "%d!%m!%y";
|
|
break;
|
|
case kMDYFormat:
|
|
// Fall through
|
|
case kDateFormatEnd:
|
|
// Fall through
|
|
default:
|
|
dateString = "%m!%d!%y";
|
|
break;
|
|
}
|
|
|
|
char separatorArray[] = {' ', '-', '/', '\\', '.'};
|
|
|
|
if (separator == kNoSeparator)
|
|
dateString.ReplaceAll("!", "");
|
|
else
|
|
dateString.ReplaceAll('!', separatorArray[separator-1]);
|
|
}
|
|
|
|
if (index == 0)
|
|
string = "%A, ";
|
|
else if (index < 3)
|
|
string = "%a, ";
|
|
else
|
|
string = "";
|
|
|
|
string << dateString;
|
|
|
|
if (index < 5)
|
|
string << ", " << clockString;
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
template <class View>
|
|
float
|
|
TruncTimeBase(BString *result, int64 value, const View *view, float width)
|
|
{
|
|
TrackerSettings settings;
|
|
FormatSeparator separator = settings.TimeFormatSeparator();
|
|
DateOrder order = settings.DateOrderFormat();
|
|
bool clockIs24hr = settings.ClockIs24Hr();
|
|
|
|
float resultWidth = 0;
|
|
char buffer[256];
|
|
|
|
time_t timeValue = (time_t)value;
|
|
tm timeData;
|
|
// use reentrant version of localtime to avoid having to have a semaphore
|
|
// (localtime uses a global structure to do it's conversion)
|
|
localtime_r(&timeValue, &timeData);
|
|
|
|
BString timeFormat;
|
|
|
|
for (int32 index = 0; ; index++) {
|
|
if (TimeFormat(timeFormat, index, separator, order, clockIs24hr)
|
|
!= B_OK)
|
|
break;
|
|
strftime(buffer, 256, timeFormat.String(), &timeData);
|
|
resultWidth = view->StringWidth(buffer);
|
|
if (resultWidth <= width)
|
|
break;
|
|
}
|
|
if (resultWidth > width) {
|
|
// even the shortest format string didn't do it, insert ellipsis
|
|
resultWidth = TruncStringBase(result, buffer, (ssize_t)strlen(buffer),
|
|
view, width);
|
|
} else
|
|
*result = buffer;
|
|
|
|
return resultWidth;
|
|
}
|
|
|
|
|
|
float
|
|
WidgetAttributeText::TruncTime(BString *result, int64 value,
|
|
const BPoseView *view, float width)
|
|
{
|
|
return TruncTimeBase(result, 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 *)
|
|
{
|
|
// can't do anything here at this point
|
|
TRESPASS();
|
|
return false;
|
|
}
|
|
|
|
|
|
status_t
|
|
WidgetAttributeText::AttrAsString(const Model *model, BString *result,
|
|
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();
|
|
// not yet supported
|
|
return B_ERROR;
|
|
}
|
|
TruncTimeBase(result, 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(result, tmp.String(), tmp.Length(), view,
|
|
width);
|
|
} else
|
|
*result = tmp.String();
|
|
|
|
return B_OK;
|
|
}
|
|
break;
|
|
|
|
case kSizeType:
|
|
// TruncFileSizeBase(result, model->StatBuf()->st_size, view, width);
|
|
return B_OK;
|
|
break;
|
|
|
|
default:
|
|
TRESPASS();
|
|
// not yet supported
|
|
return B_ERROR;
|
|
|
|
}
|
|
|
|
TRESPASS();
|
|
return B_ERROR;
|
|
}
|
|
|
|
|
|
bool
|
|
WidgetAttributeText::IsEditable() const
|
|
{
|
|
return fColumn->Editable()
|
|
&& !BVolume(fModel->StatBuf()->st_dev).IsReadOnly();
|
|
}
|
|
|
|
|
|
void
|
|
WidgetAttributeText::SetDirty(bool value)
|
|
{
|
|
fDirty = value;
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
StringAttributeText::StringAttributeText(const Model *model,
|
|
const BColumn *column)
|
|
: WidgetAttributeText(model, column),
|
|
fValueDirty(true)
|
|
{
|
|
}
|
|
|
|
|
|
const char *
|
|
StringAttributeText::ValueAsText(const BPoseView * /*view*/)
|
|
{
|
|
if (fValueDirty)
|
|
ReadValue(&fFullValueText);
|
|
|
|
return fFullValueText.String();
|
|
}
|
|
|
|
|
|
bool
|
|
StringAttributeText::CheckAttributeChanged()
|
|
{
|
|
BString newString;
|
|
ReadValue(&newString);
|
|
|
|
if (newString == fFullValueText)
|
|
return false;
|
|
|
|
fFullValueText = newString;
|
|
fDirty = true; // have to redo fitted string
|
|
return true;
|
|
}
|
|
|
|
|
|
void
|
|
StringAttributeText::FitValue(BString *result, const BPoseView *view)
|
|
{
|
|
if (fValueDirty)
|
|
ReadValue(&fFullValueText);
|
|
fOldWidth = fColumn->Width();
|
|
|
|
fTruncatedWidth = TruncString(result, 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);
|
|
ASSERT(compareTo);
|
|
|
|
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)
|
|
// no change
|
|
return false;
|
|
|
|
if (textView->TextLength() == 0)
|
|
// cannot do an empty name
|
|
return false;
|
|
|
|
// cause re-truncation
|
|
fDirty = true;
|
|
|
|
if (!CommitEditedTextFlavor(textView))
|
|
return false;
|
|
|
|
// update text and width in this widget
|
|
fFullValueText = text;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
ScalarAttributeText::ScalarAttributeText(const Model *model,
|
|
const BColumn *column)
|
|
: WidgetAttributeText(model, column),
|
|
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; // have to redo fitted string
|
|
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);
|
|
ASSERT(compareTo);
|
|
// make sure we're not comparing apples and oranges
|
|
|
|
if (fValueDirty)
|
|
fValue = ReadValue();
|
|
|
|
return fValue >= compareTo->Value()
|
|
? (fValue == compareTo->Value() ? 0 : 1) : -1;
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
PathAttributeText::PathAttributeText(const Model *model, const BColumn *column)
|
|
: StringAttributeText(model, column)
|
|
{
|
|
}
|
|
|
|
|
|
void
|
|
PathAttributeText::ReadValue(BString *result)
|
|
{
|
|
// get the path
|
|
BEntry entry(fModel->EntryRef());
|
|
BPath path;
|
|
|
|
if (entry.InitCheck() == B_OK && entry.GetPath(&path) == B_OK) {
|
|
*result = path.Path();
|
|
TruncateLeaf(result);
|
|
} else
|
|
*result = "-";
|
|
fValueDirty = false;
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
OriginalPathAttributeText::OriginalPathAttributeText(const Model *model,
|
|
const BColumn *column)
|
|
: StringAttributeText(model, column)
|
|
{
|
|
}
|
|
|
|
|
|
void
|
|
OriginalPathAttributeText::ReadValue(BString *result)
|
|
{
|
|
BEntry entry(fModel->EntryRef());
|
|
BPath path;
|
|
|
|
// get the original path
|
|
if (entry.InitCheck() == B_OK && FSGetOriginalPath(&entry, &path) == B_OK)
|
|
*result = path.Path();
|
|
else
|
|
*result = "-";
|
|
fValueDirty = false;
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
KindAttributeText::KindAttributeText(const Model *model, const BColumn *column)
|
|
: StringAttributeText(model, column)
|
|
{
|
|
}
|
|
|
|
|
|
void
|
|
KindAttributeText::ReadValue(BString *result)
|
|
{
|
|
BMimeType mime;
|
|
char desc[B_MIME_TYPE_LENGTH];
|
|
|
|
// get the mime type
|
|
if (mime.SetType(fModel->MimeType()) != B_OK)
|
|
*result = "Unknown";
|
|
// get the short mime type description
|
|
else if (mime.GetShortDescription(desc) == B_OK)
|
|
*result = desc;
|
|
else
|
|
*result = fModel->MimeType();
|
|
fValueDirty = false;
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
NameAttributeText::NameAttributeText(const Model *model, const BColumn *column)
|
|
: StringAttributeText(model, column)
|
|
{
|
|
}
|
|
|
|
|
|
int
|
|
NameAttributeText::Compare(WidgetAttributeText &attr, BPoseView *view)
|
|
{
|
|
NameAttributeText *compareTo = dynamic_cast<NameAttributeText *>(&attr);
|
|
|
|
ASSERT(compareTo);
|
|
|
|
if (fValueDirty)
|
|
ReadValue(&fFullValueText);
|
|
|
|
if (NameAttributeText::sSortFolderNamesFirst)
|
|
return fModel->CompareFolderNamesFirst(attr.TargetModel());
|
|
|
|
return NaturalCompare(fFullValueText.String(), compareTo->ValueAsText(view));
|
|
}
|
|
|
|
|
|
void
|
|
NameAttributeText::ReadValue(BString *result)
|
|
{
|
|
#ifdef DEBUG
|
|
// x86 support :-)
|
|
if ((modifiers() & B_CAPS_LOCK) != 0) {
|
|
if (fModel->IsVolume()) {
|
|
BVolumeRoster roster;
|
|
roster.Rewind();
|
|
BVolume volume;
|
|
char device = 'A';
|
|
while (roster.GetNextVolume(&volume) == B_OK) {
|
|
char name[256];
|
|
if (volume.GetName(name) == B_OK
|
|
&& strcmp(name, fModel->Name()) == 0) {
|
|
*result += device;
|
|
*result += ':';
|
|
fValueDirty = false;
|
|
return;
|
|
}
|
|
device++;
|
|
}
|
|
}
|
|
const char *modelName = fModel->Name();
|
|
bool hasDot = strstr(".", modelName) != 0;
|
|
for (int32 index = 0; index < 8; index++) {
|
|
if (!modelName[index] || modelName[index] == '.')
|
|
break;
|
|
*result += toupper(modelName[index]);
|
|
}
|
|
if (hasDot) {
|
|
modelName = strstr(".", modelName);
|
|
for (int32 index = 0; index < 4; index++) {
|
|
if (!modelName[index])
|
|
break;
|
|
*result += toupper(modelName[index]);
|
|
}
|
|
} else if (fModel->IsExecutable())
|
|
*result += ".EXE";
|
|
|
|
} else
|
|
#endif
|
|
*result = fModel->Name();
|
|
|
|
fValueDirty = false;
|
|
}
|
|
|
|
|
|
void
|
|
NameAttributeText::FitValue(BString *result, const BPoseView *view)
|
|
{
|
|
if (fValueDirty)
|
|
ReadValue(&fFullValueText);
|
|
fOldWidth = fColumn->Width();
|
|
fTruncatedWidth = TruncString(result, fFullValueText.String(),
|
|
fFullValueText.Length(), view, fOldWidth, B_TRUNCATE_END);
|
|
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)
|
|
{
|
|
const char *text = textView->Text();
|
|
|
|
BEntry entry(fModel->EntryRef());
|
|
if (entry.InitCheck() != B_OK)
|
|
return false;
|
|
|
|
BDirectory parent;
|
|
if (entry.GetParent(&parent) != B_OK)
|
|
return false;
|
|
|
|
bool removeExisting = false;
|
|
if (parent.Contains(text)) {
|
|
BAlert *alert = new BAlert("", "That name is already taken. "
|
|
"Please type another one.", "Replace other file", "OK", NULL,
|
|
B_WIDTH_AS_USUAL, B_WARNING_ALERT);
|
|
|
|
alert->SetShortcut(0, 'r');
|
|
|
|
if (alert->Go())
|
|
return false;
|
|
|
|
removeExisting = true;
|
|
}
|
|
|
|
// ToDo:
|
|
// use model-flavor specific virtuals for all of these special
|
|
// renamings
|
|
status_t result;
|
|
if (fModel->IsVolume()) {
|
|
BVolume volume(fModel->NodeRef()->device);
|
|
result = volume.InitCheck();
|
|
if (result == B_OK) {
|
|
RenameVolumeUndo undo(volume, text);
|
|
|
|
result = volume.SetName(text);
|
|
if (result != B_OK)
|
|
undo.Remove();
|
|
}
|
|
} else {
|
|
if (fModel->IsQuery()) {
|
|
BModelWriteOpener opener(fModel);
|
|
ASSERT(fModel->Node());
|
|
MoreOptionsStruct::SetQueryTemporary(fModel->Node(), false);
|
|
}
|
|
|
|
RenameUndo undo(entry, text);
|
|
|
|
result = entry.Rename(text, removeExisting);
|
|
if (result != B_OK)
|
|
undo.Remove();
|
|
}
|
|
|
|
return result == B_OK;
|
|
}
|
|
|
|
bool NameAttributeText::sSortFolderNamesFirst = false;
|
|
|
|
void
|
|
NameAttributeText::SetSortFolderNamesFirst(bool enabled)
|
|
{
|
|
NameAttributeText::sSortFolderNamesFirst = enabled;
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
#ifdef OWNER_GROUP_ATTRIBUTES
|
|
|
|
OwnerAttributeText::OwnerAttributeText(const Model *model,
|
|
const BColumn *column)
|
|
: StringAttributeText(model, column)
|
|
{
|
|
}
|
|
|
|
|
|
void
|
|
OwnerAttributeText::ReadValue(BString *result)
|
|
{
|
|
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;
|
|
*result = user.String();
|
|
|
|
fValueDirty = false;
|
|
}
|
|
|
|
|
|
GroupAttributeText::GroupAttributeText(const Model *model,
|
|
const BColumn *column)
|
|
: StringAttributeText(model, column)
|
|
{
|
|
}
|
|
|
|
|
|
void
|
|
GroupAttributeText::ReadValue(BString *result)
|
|
{
|
|
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;
|
|
*result = group.String();
|
|
|
|
fValueDirty = false;
|
|
}
|
|
|
|
#endif /* OWNER_GROUP_ATTRIBUTES */
|
|
|
|
ModeAttributeText::ModeAttributeText(const Model *model, const BColumn *column)
|
|
: StringAttributeText(model, column)
|
|
{
|
|
}
|
|
|
|
|
|
void
|
|
ModeAttributeText::ReadValue(BString *result)
|
|
{
|
|
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;
|
|
*result = buffer;
|
|
|
|
fValueDirty = false;
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
SizeAttributeText::SizeAttributeText(const Model *model, const BColumn *column)
|
|
: ScalarAttributeText(model, column)
|
|
{
|
|
}
|
|
|
|
|
|
int64
|
|
SizeAttributeText::ReadValue()
|
|
{
|
|
fValueDirty = false;
|
|
// get the size
|
|
|
|
if (fModel->IsVolume()) {
|
|
BVolume volume(fModel->NodeRef()->device);
|
|
|
|
return volume.Capacity();
|
|
}
|
|
|
|
if (fModel->IsDirectory() || fModel->IsQuery()
|
|
|| fModel->IsQueryTemplate() || fModel->IsSymLink())
|
|
return kUnknownSize;
|
|
|
|
fValueIsDefined = true;
|
|
|
|
return fModel->StatBuf()->st_size;
|
|
}
|
|
|
|
|
|
void
|
|
SizeAttributeText::FitValue(BString *result, const BPoseView *view)
|
|
{
|
|
if (fValueDirty)
|
|
fValue = ReadValue();
|
|
fOldWidth = fColumn->Width();
|
|
fTruncatedWidth = TruncFileSize(result, fValue, view, fOldWidth);
|
|
fDirty = false;
|
|
}
|
|
|
|
|
|
float
|
|
SizeAttributeText::PreferredWidth(const BPoseView *pose) const
|
|
{
|
|
if (fValueIsDefined) {
|
|
BString widthString;
|
|
TruncFileSize(&widthString, fValue, pose, 100000);
|
|
return pose->StringWidth(widthString.String());
|
|
}
|
|
return pose->StringWidth("-");
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
TimeAttributeText::TimeAttributeText(const Model *model, const BColumn *column)
|
|
: ScalarAttributeText(model, column)
|
|
{
|
|
}
|
|
|
|
|
|
float
|
|
TimeAttributeText::PreferredWidth(const BPoseView *pose) const
|
|
{
|
|
BString widthString;
|
|
TruncTimeBase(&widthString, fValue, pose, 100000);
|
|
return pose->StringWidth(widthString.String());
|
|
}
|
|
|
|
|
|
void
|
|
TimeAttributeText::FitValue(BString *result, const BPoseView *view)
|
|
{
|
|
if (fValueDirty)
|
|
fValue = ReadValue();
|
|
fOldWidth = fColumn->Width();
|
|
fTruncatedWidth = TruncTime(result, fValue, view, fOldWidth);
|
|
fDirty = false;
|
|
}
|
|
|
|
|
|
bool
|
|
TimeAttributeText::CheckSettingsChanged()
|
|
{
|
|
bool changed = fLastClockIs24 != fSettings.ClockIs24Hr()
|
|
|| fLastDateOrder != fSettings.DateOrderFormat()
|
|
|| fLastTimeFormatSeparator != fSettings.TimeFormatSeparator();
|
|
|
|
if (changed) {
|
|
fLastClockIs24 = fSettings.ClockIs24Hr();
|
|
fLastDateOrder = fSettings.DateOrderFormat();
|
|
fLastTimeFormatSeparator = fSettings.TimeFormatSeparator();
|
|
}
|
|
|
|
return changed;
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
GenericAttributeText::GenericAttributeText(const Model *model,
|
|
const BColumn *column)
|
|
: StringAttributeText(model, column)
|
|
{
|
|
}
|
|
|
|
|
|
bool
|
|
GenericAttributeText::CheckAttributeChanged()
|
|
{
|
|
GenericValueStruct tmpValue = fValue;
|
|
BString tmpString(fFullValueText);
|
|
ReadValue(&fFullValueText);
|
|
|
|
// fDirty could already be true, in that case we mustn't set it to
|
|
// false, even if the attribute text hasn't changed
|
|
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 *result)
|
|
{
|
|
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';
|
|
// make sure the buffer is null-terminated even if we
|
|
// didn't read the whole attribute in or it wasn't to
|
|
// begin with
|
|
|
|
*result = 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:
|
|
{
|
|
// read in the numerical bit representation and attach it
|
|
// with a type, depending on the bytes that could be read
|
|
attr_info info;
|
|
GenericValueStruct tmp;
|
|
if (fModel->Node()->GetAttrInfo(fColumn->AttrName(), &info) == B_OK) {
|
|
if (info.size && info.size <= sizeof(int64)) {
|
|
length = fModel->Node()->ReadAttr(fColumn->AttrName(),
|
|
fColumn->AttrType(), 0, &tmp, (size_t)info.size);
|
|
}
|
|
|
|
// We used tmp as a block of memory, now set the correct fValue:
|
|
|
|
if (length == info.size) {
|
|
if (fColumn->AttrType() == B_FLOAT_TYPE
|
|
|| fColumn->AttrType() == B_DOUBLE_TYPE) {
|
|
// filter out special float/double types
|
|
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();
|
|
}
|
|
} else {
|
|
// handle the standard data types
|
|
switch (info.size) {
|
|
case sizeof(char): // Takes care of bool, too.
|
|
fValueIsDefined = true;
|
|
fValue.int8t = tmp.int8t;
|
|
break;
|
|
|
|
case sizeof(int16):
|
|
fValueIsDefined = true;
|
|
fValue.int16t = tmp.int16t;
|
|
break;
|
|
|
|
case sizeof(int32): // Takes care of time_t, too.
|
|
fValueIsDefined = true;
|
|
fValue.int32t = tmp.int32t;
|
|
break;
|
|
|
|
case sizeof(int64): // Taked care of off_t, too.
|
|
fValueIsDefined = true;
|
|
fValue.int64t = tmp.int64t;
|
|
break;
|
|
|
|
default:
|
|
TRESPASS();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
GenericAttributeText::FitValue(BString *result, const BPoseView *view)
|
|
{
|
|
if (fValueDirty)
|
|
ReadValue(&fFullValueText);
|
|
|
|
fOldWidth = fColumn->Width();
|
|
|
|
if (!fValueIsDefined) {
|
|
*result = "-";
|
|
fTruncatedWidth = TruncString(result, fFullValueText.String(),
|
|
fFullValueText.Length(), view, fOldWidth);
|
|
fDirty = false;
|
|
return;
|
|
}
|
|
|
|
char buffer[256];
|
|
|
|
switch (fColumn->AttrType()) {
|
|
case B_SIZE_T_TYPE:
|
|
TruncFileSizeBase(result, fValue.int32t, view, fOldWidth);
|
|
return;
|
|
|
|
case B_SSIZE_T_TYPE:
|
|
if (fValue.int32t > 0) {
|
|
TruncFileSizeBase(result, fValue.int32t, view, fOldWidth);
|
|
return;
|
|
}
|
|
sprintf(buffer, "%s", strerror(fValue.int32t));
|
|
fFullValueText = buffer;
|
|
break;
|
|
|
|
case B_STRING_TYPE:
|
|
fTruncatedWidth = TruncString(result, fFullValueText.String(),
|
|
fFullValueText.Length(), view, fOldWidth);
|
|
fDirty = false;
|
|
return;
|
|
|
|
case B_OFF_T_TYPE:
|
|
// as a side effect update the fFullValueText to the string representation
|
|
// of value
|
|
TruncFileSize(&fFullValueText, fValue.off_tt, view, 100000);
|
|
fTruncatedWidth = TruncFileSize(result, fValue.off_tt, view, fOldWidth);
|
|
fDirty = false;
|
|
return;
|
|
|
|
case B_TIME_TYPE:
|
|
// as a side effect update the fFullValueText to the string representation
|
|
// of value
|
|
TruncTime(&fFullValueText, fValue.time_tt, view, 100000);
|
|
fTruncatedWidth = TruncTime(result, fValue.time_tt, view, fOldWidth);
|
|
fDirty = false;
|
|
return;
|
|
|
|
case B_BOOL_TYPE:
|
|
// For now use true/false, would be nice to be able to set
|
|
// the value text
|
|
|
|
sprintf(buffer, "%s", fValue.boolt ? "true" : "false");
|
|
fFullValueText = buffer;
|
|
break;
|
|
|
|
case B_CHAR_TYPE:
|
|
// Make sure no non-printable characters are displayed:
|
|
if (!isprint(fValue.uint8t)) {
|
|
*result = "-";
|
|
fTruncatedWidth = TruncString(result, 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, "%ld", fValue.int32t);
|
|
fFullValueText = buffer;
|
|
break;
|
|
|
|
case B_UINT32_TYPE:
|
|
sprintf(buffer, "%ld", fValue.uint32t);
|
|
fFullValueText = buffer;
|
|
break;
|
|
|
|
case B_INT64_TYPE:
|
|
sprintf(buffer, "%Ld", fValue.int64t);
|
|
fFullValueText = buffer;
|
|
break;
|
|
|
|
case B_UINT64_TYPE:
|
|
sprintf(buffer, "%Ld", 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:
|
|
*result = "-";
|
|
fTruncatedWidth = TruncString(result, fFullValueText.String(),
|
|
fFullValueText.Length(), view, fOldWidth);
|
|
fDirty = false;
|
|
return;
|
|
}
|
|
fTruncatedWidth = TruncString(result, buffer, (ssize_t)strlen(buffer), view,
|
|
fOldWidth);
|
|
fDirty = false;
|
|
}
|
|
|
|
|
|
const char*
|
|
GenericAttributeText::ValueAsText(const BPoseView *view)
|
|
{
|
|
// TODO: redesign this - this is to make sure the value is valid
|
|
bool oldDirty = fDirty;
|
|
BString result;
|
|
FitValue(&result, view);
|
|
fDirty = oldDirty;
|
|
|
|
return fFullValueText.String();
|
|
}
|
|
|
|
|
|
int
|
|
GenericAttributeText::Compare(WidgetAttributeText &attr, BPoseView *)
|
|
{
|
|
GenericAttributeText *compareTo
|
|
= dynamic_cast<GenericAttributeText *>(&attr);
|
|
ASSERT(compareTo);
|
|
|
|
if (fValueDirty)
|
|
ReadValue(&fFullValueText);
|
|
if (compareTo->fValueDirty)
|
|
compareTo->ReadValue(&compareTo->fFullValueText);
|
|
|
|
// Sort undefined values last, regardless of the other value:
|
|
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:
|
|
// time_t typedef'd to a long, i.e. a int32
|
|
case B_INT32_TYPE:
|
|
return fValue.int32t >= compareTo->fValue.int32t ?
|
|
(fValue.int32t == compareTo->fValue.int32t ? 0 : 1) : -1;
|
|
|
|
case B_OFF_T_TYPE:
|
|
// off_t typedef'd to a long long, i.e. a int64
|
|
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)
|
|
// no change
|
|
return false;
|
|
|
|
if (!CommitEditedTextFlavor(textView))
|
|
return false;
|
|
|
|
// update text and width in this widget
|
|
fFullValueText = text;
|
|
// cause re-truncation
|
|
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("", "Sorry, you cannot edit that attribute.",
|
|
"Cancel", 0, 0, B_WIDTH_AS_USUAL, B_STOP_ALERT);
|
|
alert->SetShortcut(0, B_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);
|
|
//Check if we read the start of a multi-byte glyph:
|
|
if (!isprint(ch)) {
|
|
BAlert *alert = new BAlert("", "Sorry, The 'Character' "
|
|
"attribute cannot store a multi-byte glyph.",
|
|
"Cancel", 0, 0, B_WIDTH_AS_USUAL, B_STOP_ALERT);
|
|
alert->SetShortcut(0, B_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
|
|
// If the value was already defined, it's on disk. Otherwise not.
|
|
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
|
|
// If the value was already defined, it's on disk. Otherwise not.
|
|
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;
|
|
|
|
// do some size independent conversion on builtin types
|
|
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("",
|
|
"There was an error writing the attribute.",
|
|
"Cancel", 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
|
|
alert->SetShortcut(0, B_ESCAPE);
|
|
alert->Go();
|
|
|
|
fValueIsDefined = false;
|
|
return false;
|
|
}
|
|
|
|
fValueIsDefined = true;
|
|
return true;
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
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) {
|
|
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 *result, const BPoseView *view)
|
|
{
|
|
if (fValueDirty)
|
|
ReadValue();
|
|
|
|
ASSERT(view == fPoseView);
|
|
const OpenWithPoseView *launchWithView
|
|
= dynamic_cast<const OpenWithPoseView *>(view);
|
|
|
|
if (launchWithView)
|
|
launchWithView->OpenWithRelationDescription(fModel, &fRelationText);
|
|
|
|
fOldWidth = fColumn->Width();
|
|
fTruncatedWidth = TruncString(result, 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 *result)
|
|
{
|
|
fValueDirty = false;
|
|
|
|
BModelOpener opener(fModel);
|
|
BFile *file = dynamic_cast<BFile *>(fModel->Node());
|
|
if (file) {
|
|
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) {
|
|
*result = version.short_info;
|
|
return;
|
|
}
|
|
}
|
|
*result = "-";
|
|
}
|
|
|