#include "Model.h"
#include <stdlib.h>
#include <strings.h>
#include <fs_info.h>
#include <fs_attr.h>
#include <AppDefs.h>
#include <Bitmap.h>
#include <Catalog.h>
#include <Debug.h>
#include <Directory.h>
#include <Entry.h>
#include <File.h>
#include <Locale.h>
#include <NodeInfo.h>
#include <NodeMonitor.h>
#include <Path.h>
#include <SymLink.h>
#include <StringList.h>
#include <Query.h>
#include <Volume.h>
#include <VolumeRoster.h>
#include "Attributes.h"
#include "Bitmaps.h"
#include "FindPanel.h"
#include "FSUtils.h"
#include "MimeTypes.h"
#include "Thumbnails.h"
#include "Tracker.h"
#include "Utilities.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "Model"
#ifdef CHECK_OPEN_MODEL_LEAKS
BObjectList<Model>* writableOpenModelList = NULL;
BObjectList<Model>* readOnlyOpenModelList = NULL;
#endif
Model::Model()
:
fPreferredAppName(NULL),
fBaseType(kUnknownNode),
fIconFrom(kUnknownSource),
fWritable(false),
fNode(NULL),
fStatus(B_NO_INIT),
fHasLocalizedName(false),
fLocalizedNameIsCached(false)
{
}
Model::Model(const Model& other)
:
fEntryRef(other.fEntryRef),
fMimeType(other.fMimeType),
fPreferredAppName(NULL),
fBaseType(other.fBaseType),
fIconFrom(other.fIconFrom),
fWritable(false),
fNode(NULL),
fLocalizedName(other.fLocalizedName),
fHasLocalizedName(other.fHasLocalizedName),
fLocalizedNameIsCached(other.fLocalizedNameIsCached)
{
fStatBuf.st_dev = other.NodeRef()->device;
fStatBuf.st_ino = other.NodeRef()->node;
if (other.IsSymLink() && other.LinkTo())
fLinkTo = new Model(*other.LinkTo());
fStatus = OpenNode(other.IsNodeOpenForWriting());
if (fStatus == B_OK) {
ASSERT(fNode);
fNode->GetStat(&fStatBuf);
ASSERT(fStatBuf.st_dev == other.NodeRef()->device);
ASSERT(fStatBuf.st_ino == other.NodeRef()->node);
}
if (!other.IsNodeOpen())
CloseNode();
}
Model::Model(const node_ref* dirNode, const node_ref* node, const char* name,
bool open, bool writable)
:
fPreferredAppName(NULL),
fWritable(false),
fNode(NULL),
fHasLocalizedName(false),
fLocalizedNameIsCached(false)
{
SetTo(dirNode, node, name, open, writable);
}
Model::Model(const BEntry* entry, bool open, bool writable)
:
fPreferredAppName(NULL),
fWritable(false),
fNode(NULL),
fHasLocalizedName(false),
fLocalizedNameIsCached(false)
{
SetTo(entry, open, writable);
}
Model::Model(const entry_ref* ref, bool traverse, bool open, bool writable)
:
fPreferredAppName(NULL),
fBaseType(kUnknownNode),
fIconFrom(kUnknownSource),
fWritable(false),
fNode(NULL),
fHasLocalizedName(false),
fLocalizedNameIsCached(false)
{
BEntry entry(ref, traverse);
fStatus = entry.InitCheck();
if (fStatus == B_OK)
SetTo(&entry, open, writable);
}
void
Model::DeletePreferredAppVolumeNameLinkTo()
{
if (IsSymLink()) {
Model* tmp = fLinkTo;
fLinkTo = NULL;
delete tmp;
} else if (IsVolume())
free(fVolumeName);
else
free(fPreferredAppName);
fPreferredAppName = NULL;
}
Model::~Model()
{
#ifdef CHECK_OPEN_MODEL_LEAKS
if (writableOpenModelList != NULL)
writableOpenModelList->RemoveItem(this);
if (readOnlyOpenModelList != NULL)
readOnlyOpenModelList->RemoveItem(this);
#endif
DeletePreferredAppVolumeNameLinkTo();
if (IconCache::NeedsDeletionNotification((IconSource)fIconFrom)) {
IconCache::sIconCache->Deleting(this);
}
#if xDEBUG
if (fNode != NULL)
PRINT(("destructor closing node for %s\n", Name()));
#endif
delete fNode;
}
status_t
Model::SetTo(const BEntry* entry, bool open, bool writable)
{
delete fNode;
fNode = NULL;
DeletePreferredAppVolumeNameLinkTo();
fIconFrom = kUnknownSource;
fBaseType = kUnknownNode;
fMimeType = "";
fStatus = entry->GetRef(&fEntryRef);
if (fStatus != B_OK)
return fStatus;
fStatus = entry->GetStat(&fStatBuf);
if (fStatus != B_OK)
return fStatus;
fStatus = OpenNode(writable);
if (!open)
CloseNode();
return fStatus;
}
status_t
Model::SetTo(const entry_ref* newRef, bool traverse, bool open, bool writable)
{
delete fNode;
fNode = NULL;
DeletePreferredAppVolumeNameLinkTo();
fIconFrom = kUnknownSource;
fBaseType = kUnknownNode;
fMimeType = "";
BEntry tmpEntry(newRef, traverse);
fStatus = tmpEntry.InitCheck();
if (fStatus != B_OK)
return fStatus;
if (traverse)
tmpEntry.GetRef(&fEntryRef);
else
fEntryRef = *newRef;
fStatus = tmpEntry.GetStat(&fStatBuf);
if (fStatus != B_OK)
return fStatus;
fStatus = OpenNode(writable);
if (!open)
CloseNode();
return fStatus;
}
status_t
Model::SetTo(const node_ref* dirNode, const node_ref* nodeRef,
const char* name, bool open, bool writable)
{
delete fNode;
fNode = NULL;
DeletePreferredAppVolumeNameLinkTo();
fIconFrom = kUnknownSource;
fBaseType = kUnknownNode;
fMimeType = "";
fStatBuf.st_dev = nodeRef->device;
fStatBuf.st_ino = nodeRef->node;
fEntryRef.device = dirNode->device;
fEntryRef.directory = dirNode->node;
fEntryRef.name = strdup(name);
BEntry tmpNode(&fEntryRef);
fStatus = tmpNode.InitCheck();
if (fStatus != B_OK)
return fStatus;
fStatus = tmpNode.GetStat(&fStatBuf);
if (fStatus != B_OK)
return fStatus;
fStatus = OpenNode(writable);
if (!open)
CloseNode();
return fStatus;
}
status_t
Model::InitCheck() const
{
return fStatus;
}
int
Model::CompareFolderNamesFirst(const Model* compare) const
{
if (compare == NULL)
return -1;
const Model* resolved = ResolveIfLink();
const Model* resolvedCompare = compare->ResolveIfLink();
bool meIsRoot = resolved->IsRoot();
bool otherIsRoot = resolvedCompare->IsRoot();
if (meIsRoot && !otherIsRoot)
return -1;
else if (!meIsRoot && otherIsRoot)
return 1;
bool meIsVolume = resolved->IsVolume();
bool otherIsVolume = resolvedCompare->IsVolume();
if (meIsVolume) {
BVolume volume(resolved->NodeRef()->device);
if (volume.InitCheck() == B_OK && volume.Capacity() == 0)
meIsVolume = false;
}
if (otherIsVolume) {
BVolume volume(resolvedCompare->NodeRef()->device);
if (volume.InitCheck() == B_OK && volume.Capacity() == 0)
otherIsVolume = false;
}
if (meIsVolume && !otherIsVolume)
return -1;
else if (!meIsVolume && otherIsVolume)
return 1;
bool meIsDir = resolved->IsDirectory() || resolved->IsVirtualDirectory();
bool otherIsDir = resolvedCompare->IsDirectory() || resolvedCompare->IsVirtualDirectory();
if (meIsDir && !otherIsDir)
return -1;
else if (!meIsDir && otherIsDir)
return 1;
return NaturalCompare(Name(), compare->Name());
}
const char*
Model::Name() const
{
static const char* kRootNodeName = B_TRANSLATE_MARK(B_DISKS_DIR_NAME);
static const char* kTrashNodeName = B_TRANSLATE_MARK(B_TRASH_DIR_NAME);
static const char* kDesktopNodeName = B_TRANSLATE_MARK(B_DESKTOP_DIR_NAME);
switch (fBaseType) {
case kRootNode:
return B_TRANSLATE_NOCOLLECT(kRootNodeName);
case kVolumeNode:
if (fVolumeName != NULL)
return fVolumeName;
break;
case kTrashNode:
return B_TRANSLATE_NOCOLLECT(kTrashNodeName);
case kDesktopNode:
return B_TRANSLATE_NOCOLLECT(kDesktopNodeName);
default:
break;
}
if (fHasLocalizedName && gLocalizedNamePreferred)
return fLocalizedName.String();
else
return fEntryRef.name;
}
status_t
Model::OpenNode(bool writable)
{
if (IsNodeOpen() && (writable == IsNodeOpenForWriting()))
return B_OK;
OpenNodeCommon(writable);
return fStatus;
}
status_t
Model::UpdateStatAndOpenNode(bool writable)
{
if (IsNodeOpen() && (writable == IsNodeOpenForWriting()))
return B_OK;
BEntry tmpEntry(&fEntryRef);
fStatus = tmpEntry.InitCheck();
if (fStatus != B_OK)
return fStatus;
fStatus = tmpEntry.GetStat(&fStatBuf);
if (fStatus != B_OK)
return fStatus;
OpenNodeCommon(writable);
return fStatus;
}
status_t
Model::OpenNodeCommon(bool writable)
{
#if xDEBUG
PRINT(("opening node for %s\n", Name()));
#endif
#ifdef CHECK_OPEN_MODEL_LEAKS
if (writableOpenModelList != NULL)
writableOpenModelList->RemoveItem(this);
if (readOnlyOpenModelList != NULL)
readOnlyOpenModelList->RemoveItem(this);
#endif
if (fBaseType == kUnknownNode)
SetupBaseType();
switch (fBaseType) {
case kExecutableNode:
case kPlainNode:
case kQueryNode:
case kQueryTemplateNode:
case kVirtualDirectoryNode:
delete fNode;
fNode = new BFile(&fEntryRef,
(uint32)(writable ? O_RDWR : O_RDONLY));
break;
case kDesktopNode:
case kDirectoryNode:
case kRootNode:
case kTrashNode:
case kVolumeNode:
if (!IsNodeOpen())
fNode = new BDirectory(&fEntryRef);
if (fBaseType == kDirectoryNode
&& static_cast<BDirectory*>(fNode)->IsRootDirectory()) {
fBaseType = kVolumeNode;
}
break;
case kLinkNode:
if (!IsNodeOpen()) {
BEntry entry(&fEntryRef);
fNode = new BSymLink(&entry);
}
break;
default:
#if DEBUG
PrintToStream();
#endif
TRESPASS();
return fStatus = B_ERROR;
}
fStatus = fNode->InitCheck();
if (fStatus != B_OK) {
delete fNode;
fNode = NULL;
return fStatus;
}
fWritable = writable;
if (fMimeType.Length() <= 0)
FinishSettingUpType();
#ifdef CHECK_OPEN_MODEL_LEAKS
if (fWritable) {
if (!writableOpenModelList) {
TRACE();
writableOpenModelList = new BObjectList<Model>(100);
}
writableOpenModelList->AddItem(this);
} else {
if (!readOnlyOpenModelList) {
TRACE();
readOnlyOpenModelList = new BObjectList<Model>(100);
}
readOnlyOpenModelList->AddItem(this);
}
#endif
if (gLocalizedNamePreferred)
CacheLocalizedName();
return fStatus;
}
void
Model::CloseNode()
{
#if xDEBUG
PRINT(("closing node for %s\n", Name()));
#endif
#ifdef CHECK_OPEN_MODEL_LEAKS
if (writableOpenModelList != NULL)
writableOpenModelList->RemoveItem(this);
if (readOnlyOpenModelList != NULL)
readOnlyOpenModelList->RemoveItem(this);
#endif
delete fNode;
fNode = NULL;
}
bool
Model::IsNodeOpen() const
{
return fNode != NULL;
}
bool
Model::IsNodeOpenForWriting() const
{
return fNode != NULL && fWritable;
}
void
Model::SetupBaseType()
{
switch (fStatBuf.st_mode & S_IFMT) {
case S_IFDIR:
fBaseType = kDirectoryNode;
break;
case S_IFREG:
if ((fStatBuf.st_mode & S_IXUSR) != 0) {
fBaseType = kExecutableNode;
} else {
fBaseType = kPlainNode;
}
break;
case S_IFLNK:
fBaseType = kLinkNode;
break;
default:
fBaseType = kUnknownNode;
break;
}
}
void
Model::CacheLocalizedName()
{
if (!fLocalizedNameIsCached) {
fLocalizedNameIsCached = true;
if (BLocaleRoster::Default()->GetLocalizedFileName(
fLocalizedName, fEntryRef, true) == B_OK)
fHasLocalizedName = true;
else
fHasLocalizedName = false;
}
}
void
Model::FinishSettingUpType()
{
char type[B_MIME_TYPE_LENGTH];
BEntry entry;
if (fBaseType != kLinkNode && !CheckAppIconHint())
fIconFrom = kUnknownNotFromNode;
if (fBaseType != kDirectoryNode
&& fBaseType != kVolumeNode
&& fBaseType != kLinkNode
&& IsNodeOpen()) {
BNodeInfo info(fNode);
if (info.GetType(type) == B_OK) {
fMimeType = type;
if (strcmp(type, B_QUERY_MIMETYPE) == 0)
fBaseType = kQueryNode;
else if (strcmp(type, B_QUERY_TEMPLATE_MIMETYPE) == 0)
fBaseType = kQueryTemplateNode;
else if (strcmp(type, kVirtualDirectoryMimeType) == 0)
fBaseType = kVirtualDirectoryNode;
attr_info thumb;
if (fNode->GetAttrInfo(kAttrThumbnail, &thumb) == B_OK
|| ShouldGenerateThumbnail(type)) {
fIconFrom = kNode;
}
if (info.GetPreferredApp(type) == B_OK) {
if (fPreferredAppName)
DeletePreferredAppVolumeNameLinkTo();
if (*type != '\0')
fPreferredAppName = strdup(type);
}
}
}
switch (fBaseType) {
case kDirectoryNode:
entry.SetTo(&fEntryRef);
if (entry.InitCheck() == B_OK) {
if (FSIsTrashDir(&entry))
fBaseType = kTrashNode;
else if (FSIsDeskDir(&entry))
fBaseType = kDesktopNode;
}
fMimeType = B_DIR_MIMETYPE;
if (IsNodeOpen()) {
BNodeInfo info(fNode);
if (info.GetType(type) == B_OK)
fMimeType = type;
if (fIconFrom == kUnknownNotFromNode
&& WellKnowEntryList::Match(NodeRef())
> (directory_which)-1) {
fIconFrom = kTrackerSupplied;
}
}
break;
case kVolumeNode:
{
if (NodeRef()->node == fEntryRef.directory
&& NodeRef()->device == fEntryRef.device) {
fBaseType = kRootNode;
fMimeType = B_ROOT_MIMETYPE;
break;
}
fMimeType = B_VOLUME_MIMETYPE;
if (fIconFrom == kUnknownNotFromNode) {
if (WellKnowEntryList::Match(NodeRef()) > (directory_which)-1)
fIconFrom = kTrackerSupplied;
else
fIconFrom = kVolume;
}
char name[B_FILE_NAME_LENGTH];
BVolume volume(NodeRef()->device);
if (volume.InitCheck() == B_OK && volume.GetName(name) == B_OK) {
if (fVolumeName != NULL)
DeletePreferredAppVolumeNameLinkTo();
fVolumeName = strdup(name);
}
#if DEBUG
else
PRINT(("get volume name failed for %s\n", fEntryRef.name));
#endif
break;
}
case kLinkNode:
fMimeType = B_LINK_MIMETYPE;
break;
case kExecutableNode:
if (IsNodeOpen()) {
char signature[B_MIME_TYPE_LENGTH];
if (GetAppSignatureFromAttr(dynamic_cast<BFile*>(fNode),
signature) == B_OK) {
if (fPreferredAppName)
DeletePreferredAppVolumeNameLinkTo();
if (signature[0])
fPreferredAppName = strdup(signature);
}
}
if (fMimeType.Length() <= 0)
fMimeType = B_APP_MIME_TYPE;
break;
default:
if (fMimeType.Length() <= 0)
fMimeType = B_FILE_MIMETYPE;
break;
}
}
bool
Model::CheckAppIconHint() const
{
attr_info info;
if (fNode == NULL) {
return false;
}
if (fNode->GetAttrInfo(kAttrIcon, &info) == B_OK) {
return true;
}
if (fNode->GetAttrInfo(kAttrMiniIcon, &info) == B_OK
&& fNode->GetAttrInfo(kAttrLargeIcon, &info) == B_OK) {
return true;
}
return false;
}
void
Model::ResetIconFrom()
{
BModelOpener opener(this);
if (InitCheck() != B_OK)
return;
bool hasAttrIcon = CheckAppIconHint();
if (hasAttrIcon && (fBaseType == kDesktopNode || fBaseType == kTrashNode)) {
fIconFrom = kNode;
return;
} else if (!hasAttrIcon && (fBaseType == kDirectoryNode || fBaseType == kVolumeNode)) {
BDirectory* directory = dynamic_cast<BDirectory*>(fNode);
if (WellKnowEntryList::Match(NodeRef()) > (directory_which)-1) {
fIconFrom = kTrackerSupplied;
return;
} else if (directory != NULL && directory->IsRootDirectory()) {
fIconFrom = kVolume;
return;
}
}
fIconFrom = kUnknownSource;
}
const char*
Model::PreferredAppSignature() const
{
if (IsVolume() || IsSymLink())
return "";
return fPreferredAppName ? fPreferredAppName : "";
}
void
Model::SetPreferredAppSignature(const char* signature)
{
ASSERT(!IsVolume() && !IsSymLink());
ASSERT(signature != fPreferredAppName);
free(fPreferredAppName);
if (signature)
fPreferredAppName = strdup(signature);
else
fPreferredAppName = NULL;
}
bool
Model::IsPrintersDir() const
{
BEntry entry(EntryRef());
return FSIsPrintersDir(&entry);
}
bool
Model::InRoot() const
{
return FSInRootDir(EntryRef());
}
bool
Model::InTrash() const
{
return FSInTrashDir(EntryRef());
}
const Model*
Model::ResolveIfLink() const
{
if (!IsSymLink())
return this;
if (!fLinkTo)
return this;
return fLinkTo;
}
Model*
Model::ResolveIfLink()
{
if (!IsSymLink())
return this;
if (!fLinkTo)
return this;
return fLinkTo;
}
void
Model::SetLinkTo(Model* model)
{
ASSERT(IsSymLink());
ASSERT(!fLinkTo || (fLinkTo != model));
delete fLinkTo;
fLinkTo = model;
}
void
Model::UpdateEntryRef(const node_ref* dirNode, const char* name)
{
if (IsVolume()) {
if (fVolumeName != NULL)
DeletePreferredAppVolumeNameLinkTo();
fVolumeName = strdup(name);
}
fEntryRef.device = dirNode->device;
fEntryRef.directory = dirNode->node;
if (fEntryRef.name != NULL && strcmp(fEntryRef.name, name) == 0)
return;
fEntryRef.set_name(name);
}
status_t
Model::WatchVolumeAndMountPoint(uint32 , BHandler* target)
{
ASSERT(IsVolume());
if (fEntryRef.name != NULL && fVolumeName != NULL
&& strcmp(fEntryRef.name, "boot") == 0) {
BString bootMountPoint("/");
bootMountPoint += fVolumeName;
BEntry mountPointEntry(bootMountPoint.String());
Model mountPointModel(&mountPointEntry);
TTracker::WatchNode(mountPointModel.NodeRef(),
B_WATCH_NAME | B_WATCH_STAT | B_WATCH_ATTR, target);
}
return TTracker::WatchNode(NodeRef(),
B_WATCH_NAME | B_WATCH_STAT | B_WATCH_ATTR, target);
}
bool
Model::AttrChanged(const char* attrName)
{
ASSERT(IsNodeOpen());
if (IconAttrChanged(attrName))
return true;
if (attrName == NULL
|| strcmp(attrName, kAttrMIMEType) == 0
|| strcmp(attrName, kAttrPreferredApp) == 0) {
char type[B_MIME_TYPE_LENGTH];
BNodeInfo info(fNode);
if (info.GetType(type) != B_OK)
fMimeType = "";
else {
fMimeType = type;
if (!IsVolume() && !IsSymLink() && info.GetPreferredApp(type) == B_OK)
SetPreferredAppSignature(type);
}
#if xDEBUG
if (fIconFrom != kNode) {
PRINT(("%s, %s:updating icon because file type changed\n",
Name(), attrName != NULL ? attrName : ""));
} else {
PRINT(("Not updating icon even though type changed "
"because icon is from node.\n"));
}
#endif
return fIconFrom != kNode;
}
return attrName == NULL;
}
bool
Model::IconAttrChanged(const char* attrName)
{
ASSERT(IsNodeOpen());
return attrName != NULL
&& (strcmp(attrName, kAttrIcon) == 0
|| strcmp(attrName, kAttrLargeIcon) == 0
|| strcmp(attrName, kAttrMiniIcon) == 0
|| strcmp(attrName, kAttrThumbnail) == 0);
}
bool
Model::StatChanged()
{
if (fNode == NULL)
return false;
ASSERT(IsNodeOpen());
mode_t oldMode = fStatBuf.st_mode;
fStatus = fNode->GetStat(&fStatBuf);
if (oldMode != fStatBuf.st_mode) {
bool forWriting = IsNodeOpenForWriting();
CloseNode();
OpenNodeCommon(forWriting);
return true;
}
return false;
}
bool
Model::IsDropTarget(const Model* forDocument, bool traverse) const
{
switch (CanHandleDrops()) {
case kCanHandle:
return true;
case kCannotHandle:
return false;
default:
break;
}
if (forDocument == NULL)
return true;
if (traverse) {
BEntry entry(forDocument->EntryRef(), true);
if (entry.InitCheck() != B_OK)
return false;
BFile file(&entry, O_RDONLY);
BNodeInfo mime(&file);
if (mime.InitCheck() != B_OK)
return false;
char mimeType[B_MIME_TYPE_LENGTH];
mime.GetType(mimeType);
return SupportsMimeType(mimeType, 0) != kDoesNotSupportType;
}
const char* documentMimeType = forDocument->MimeType();
if (documentMimeType == NULL)
return false;
return SupportsMimeType(documentMimeType, 0) != kDoesNotSupportType;
}
Model::CanHandleResult
Model::CanHandleDrops() const
{
if (IsDirectory() || IsVirtualDirectory()) {
return kCanHandle;
}
if (IsSymLink()) {
BEntry entry(&fEntryRef, true);
if (entry.InitCheck() != B_OK)
return kCannotHandle;
if (entry == BEntry(EntryRef()))
return kCannotHandle;
Model model(&entry);
if (model.InitCheck() != B_OK)
return kCannotHandle;
return model.CanHandleDrops();
}
if (IsExecutable())
return kNeedToCheckType;
return kCannotHandle;
}
inline bool
IsSuperHandlerSignature(const char* signature)
{
return strcasecmp(signature, B_FILE_MIMETYPE) == 0;
}
enum {
kDontMatch = 0,
kMatchSupertype,
kMatch
};
static int32
MatchMimeTypeString(const BString& documentType, const char* handlerType)
{
int32 supertypeOnlyLength = 0;
const char* tmp = strstr(handlerType, "/");
if (tmp == NULL) {
supertypeOnlyLength = (int32)strlen(handlerType);
}
if (supertypeOnlyLength) {
tmp = strstr(documentType.String(), "/");
if (tmp && (tmp - documentType.String() == supertypeOnlyLength)) {
if (documentType.ICompare(handlerType, supertypeOnlyLength) == 0)
return kMatchSupertype;
else
return kDontMatch;
}
}
if (documentType.ICompare(handlerType) == 0)
return kMatch;
return kDontMatch;
}
int32
Model::SupportsMimeType(const char* type, const BStringList* list,
bool exactReason) const
{
ASSERT((type == 0) != (list == 0));
int32 result = kDoesNotSupportType;
BFile file(EntryRef(), O_RDONLY);
BAppFileInfo handlerInfo(&file);
BMessage message;
if (handlerInfo.GetSupportedTypes(&message) != B_OK)
return kDoesNotSupportType;
for (int32 index = 0; ; index++) {
const char* mimeSignature;
ssize_t bufferLength;
if (message.FindData("types", 'CSTR', index,
(const void**)&mimeSignature, &bufferLength)) {
return result;
}
if (IsSuperHandlerSignature(mimeSignature)) {
if (!exactReason)
return kSuperhandlerModel;
if (result == kDoesNotSupportType)
result = kSuperhandlerModel;
}
int32 match = kDontMatch;
if (type != NULL || (list != NULL && list->IsEmpty())) {
BString typeString(type);
match = MatchMimeTypeString(typeString, mimeSignature);
} else {
const int32 count = list->CountStrings();
for (int32 i = 0; i < count; i++) {
match = MatchMimeTypeString(list->StringAt(i), mimeSignature);
if (match != kDontMatch)
break;
}
}
if (match == kMatch) {
return kModelSupportsType;
} else if (match == kMatchSupertype) {
if (!exactReason)
return kModelSupportsSupertype;
result = kModelSupportsSupertype;
}
}
return result;
}
bool
Model::IsDropTargetForList(const BStringList* list) const
{
switch (CanHandleDrops()) {
case kCanHandle:
return true;
case kCannotHandle:
return false;
default:
break;
}
return SupportsMimeType(0, list) != kDoesNotSupportType;
}
bool
Model::IsSuperHandler() const
{
ASSERT(CanHandleDrops() == kNeedToCheckType);
BFile file(EntryRef(), O_RDONLY);
BAppFileInfo handlerInfo(&file);
BMessage message;
if (handlerInfo.GetSupportedTypes(&message) != B_OK)
return false;
for (int32 index = 0; ; index++) {
const char* mimeSignature;
ssize_t bufferLength;
if (message.FindData("types", 'CSTR', index,
(const void**)&mimeSignature, &bufferLength)) {
return false;
}
if (IsSuperHandlerSignature(mimeSignature))
return true;
}
return false;
}
void
Model::GetEntry(BEntry* entry) const
{
entry->SetTo(EntryRef());
}
void
Model::GetPath(BPath* path) const
{
BEntry entry(EntryRef());
entry.GetPath(path);
}
bool
Model::Mimeset(bool force)
{
BString oldType = MimeType();
BPath path;
GetPath(&path);
update_mime_info(path.Path(), 0, 1, force ? 2 : 0);
ModelNodeLazyOpener opener(this);
opener.OpenNode();
AttrChanged(NULL);
return oldType.ICompare(MimeType()) != 0;
}
void
Model::SniffMimeIfNeeded()
{
if (fMimeType != B_FILE_MIMETYPE)
return;
BVolume volume(fStatBuf.st_dev);
if (volume.InitCheck() == B_OK && !volume.KnowsMime()) {
BMimeType mimeType;
if (BMimeType::GuessMimeType(&fEntryRef, &mimeType) == B_OK)
fMimeType = mimeType.Type();
}
return;
}
ssize_t
Model::WriteAttr(const char* attr, type_code type, off_t offset,
const void* buffer, size_t length)
{
BModelWriteOpener opener(this);
if (!fNode)
return 0;
ssize_t result = fNode->WriteAttr(attr, type, offset, buffer, length);
return result;
}
ssize_t
Model::WriteAttrKillForeign(const char* attr, const char* foreignAttr,
type_code type, off_t offset, const void* buffer, size_t length)
{
BModelWriteOpener opener(this);
if (!fNode)
return 0;
ssize_t result = fNode->WriteAttr(attr, type, offset, buffer, length);
if (result == (ssize_t)length)
fNode->RemoveAttr(foreignAttr);
return result;
}
status_t
Model::GetLongVersionString(BString &result, version_kind kind)
{
BFile file(EntryRef(), O_RDONLY);
status_t error = file.InitCheck();
if (error != B_OK)
return error;
BAppFileInfo info(&file);
error = info.InitCheck();
if (error != B_OK)
return error;
version_info version;
error = info.GetVersionInfo(&version, kind);
if (error != B_OK)
return error;
result = version.long_info;
return B_OK;
}
status_t
Model::GetVersionString(BString &result, version_kind kind)
{
BFile file(EntryRef(), O_RDONLY);
status_t error = file.InitCheck();
if (error != B_OK)
return error;
BAppFileInfo info(&file);
error = info.InitCheck();
if (error != B_OK)
return error;
version_info version;
error = info.GetVersionInfo(&version, kind);
if (error != B_OK)
return error;
result.SetToFormat("%" B_PRId32 ".%" B_PRId32 ".%" B_PRId32, version.major,
version.middle, version.minor);
return B_OK;
}
#if DEBUG
void
Model::PrintToStream(int32 level, bool deep)
{
PRINT(("model name %s, entry name %s, inode %" B_PRIdINO ", dev %"
B_PRIdDEV ", directory inode %" B_PRIdINO "\n",
Name() ? Name() : "**empty name**",
EntryRef()->name ? EntryRef()->name : "**empty ref name**",
NodeRef()->node,
NodeRef()->device,
EntryRef()->directory));
PRINT(("type %s \n", MimeType()));
PRINT(("model type: "));
switch (fBaseType) {
case kPlainNode:
PRINT(("plain\n"));
break;
case kQueryNode:
PRINT(("query\n"));
break;
case kQueryTemplateNode:
PRINT(("query template\n"));
break;
case kExecutableNode:
PRINT(("exe\n"));
break;
case kDirectoryNode:
case kTrashNode:
case kDesktopNode:
PRINT(("dir\n"));
break;
case kLinkNode:
PRINT(("link\n"));
break;
case kRootNode:
PRINT(("root\n"));
break;
case kVolumeNode:
PRINT(("volume, name %s\n", fVolumeName ? fVolumeName : ""));
break;
case kVirtualDirectoryNode:
PRINT(("virtual directory\n"));
break;
default:
PRINT(("unknown\n"));
break;
}
if (level < 1)
return;
if (!IsVolume()) {
PRINT(("preferred app %s\n",
fPreferredAppName ? fPreferredAppName : ""));
}
PRINT(("icon from: "));
switch (IconFrom()) {
case kUnknownSource:
PRINT(("unknown\n"));
break;
case kUnknownNotFromNode:
PRINT(("unknown but not from a node\n"));
break;
case kTrackerDefault:
PRINT(("tracker default\n"));
break;
case kTrackerSupplied:
PRINT(("tracker supplied\n"));
break;
case kMetaMime:
PRINT(("metamime\n"));
break;
case kPreferredAppForType:
PRINT(("preferred app for type\n"));
break;
case kPreferredAppForNode:
PRINT(("preferred app for node\n"));
break;
case kNode:
PRINT(("node\n"));
break;
case kVolume:
PRINT(("volume\n"));
break;
default:
break;
}
PRINT(("model %s opened %s \n", !IsNodeOpen() ? "not " : "",
IsNodeOpenForWriting() ? "for writing" : ""));
if (IsNodeOpen()) {
node_ref nodeRef;
fNode->GetNodeRef(&nodeRef);
PRINT(("node ref of open Node %" B_PRIdINO " %" B_PRIdDEV "\n",
nodeRef.node, nodeRef.device));
}
if (deep && IsSymLink()) {
BEntry tmpEntry(EntryRef(), true);
Model tmp(&tmpEntry);
PRINT(("symlink to:\n"));
tmp.PrintToStream();
}
TrackIconSource(B_MINI_ICON);
TrackIconSource(B_LARGE_ICON);
}
void
Model::TrackIconSource(icon_size size)
{
PRINT(("tracking %s icon\n", size == B_LARGE_ICON ? "large" : "small"));
BRect rect;
if (size == B_MINI_ICON)
rect.Set(0, 0, B_MINI_ICON - 1, B_MINI_ICON - 1);
else
rect.Set(0, 0, B_LARGE_ICON - 1, B_LARGE_ICON - 1);
BBitmap bitmap(rect, B_CMAP8);
BModelOpener opener(this);
if (Node() == NULL) {
PRINT(("track icon error - no node\n"));
return;
}
if (IsSymLink()) {
PRINT(("tracking symlink icon\n"));
if (fLinkTo) {
fLinkTo->TrackIconSource(size);
return;
}
}
if (fBaseType == kVolumeNode) {
BVolume volume(NodeRef()->device);
status_t result = volume.GetIcon(&bitmap, size);
PRINT(("getting icon from volume %s\n", strerror(result)));
} else {
BNodeInfo nodeInfo(Node());
status_t err = nodeInfo.GetIcon(&bitmap, size);
if (err == B_OK) {
PRINT(("track icon - got icon from file\n"));
return;
}
char preferredApp[B_MIME_TYPE_LENGTH];
err = nodeInfo.GetPreferredApp(preferredApp);
if (err == B_OK && preferredApp[0]) {
BMimeType preferredAppType(preferredApp);
err = preferredAppType.GetIconForType(MimeType(), &bitmap, size);
if (err == B_OK) {
PRINT(
("track icon - got icon for type %s from preferred "
"app %s for file\n", MimeType(), preferredApp));
return;
}
}
BMimeType mimeType(MimeType());
err = mimeType.GetIcon(&bitmap, size);
if (err == B_OK) {
PRINT(("track icon - signature %s, got icon from system\n",
MimeType()));
return;
}
err = mimeType.GetPreferredApp(preferredApp);
if (err != B_OK) {
PRINT(("track icon - signature %s, no prefered app, error %s\n",
MimeType(), strerror(err)));
return;
}
BMimeType preferredAppType(preferredApp);
err = preferredAppType.GetIconForType(MimeType(), &bitmap, size);
if (err == B_OK) {
PRINT(
("track icon - signature %s, got icon from preferred "
"app %s\n", MimeType(), preferredApp));
return;
}
PRINT(
("track icon - signature %s, preferred app %s, no icon, "
"error %s\n", MimeType(), preferredApp, strerror(err)));
}
}
#endif
#ifdef CHECK_OPEN_MODEL_LEAKS
namespace BPrivate {
#include <stdio.h>
void
DumpOpenModels(bool extensive)
{
if (readOnlyOpenModelList) {
int32 count = readOnlyOpenModelList->CountItems();
printf("%ld models open read-only:\n", count);
printf("==========================\n");
for (int32 index = 0; index < count; index++) {
if (extensive) {
printf("---------------------------\n");
readOnlyOpenModelList->ItemAt(index)->PrintToStream();
} else
printf("%s\n", readOnlyOpenModelList->ItemAt(index)->Name());
}
}
if (writableOpenModelList) {
int32 count = writableOpenModelList->CountItems();
printf("%ld models open writable:\n", count);
printf("models open writable:\n");
printf("======================\n");
for (int32 index = 0; index < count; index++) {
if (extensive) {
printf("---------------------------\n");
writableOpenModelList->ItemAt(index)->PrintToStream();
} else
printf("%s\n", writableOpenModelList->ItemAt(index)->Name());
}
}
}
void
InitOpenModelDumping()
{
readOnlyOpenModelList = 0;
writableOpenModelList = 0;
}
}
#endif