* Copyright 2013 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold, ingo_weinhold@gmx.de
*/
#include "VirtualDirectoryPoseView.h"
#include <new>
#include <AutoLocker.h>
#include <NotOwningEntryRef.h>
#include <PathMonitor.h>
#include <storage_support.h>
#include "Commands.h"
#include "Tracker.h"
#include "VirtualDirectoryEntryList.h"
#include "VirtualDirectoryManager.h"
namespace BPrivate {
VirtualDirectoryPoseView::VirtualDirectoryPoseView(Model* model)
:
BPoseView(model, kListMode),
fDirectoryPaths(),
fRootDefinitionFileRef(-1, -1),
fFileChangeTime(-1),
fIsRoot(false)
{
VirtualDirectoryManager* manager = VirtualDirectoryManager::Instance();
if (manager == NULL)
return;
AutoLocker<VirtualDirectoryManager> managerLocker(manager);
if (_UpdateDirectoryPaths() != B_OK)
return;
manager->GetRootDefinitionFile(*model->NodeRef(), fRootDefinitionFileRef);
fIsRoot = fRootDefinitionFileRef == *model->NodeRef();
}
VirtualDirectoryPoseView::~VirtualDirectoryPoseView()
{
}
void
VirtualDirectoryPoseView::MessageReceived(BMessage* message)
{
if (message->WasDropped())
return _inherited::MessageReceived(message);
switch (message->what) {
case B_CUT:
case B_PASTE:
case kCutMoreSelectionToClipboard:
case kDeleteSelection:
case kDuplicateSelection:
case kMoveSelectionToTrash:
case kNewEntryFromTemplate:
case kNewFolder:
break;
default:
_inherited::MessageReceived(message);
break;
}
}
void
VirtualDirectoryPoseView::AttachedToWindow()
{
_inherited::AttachedToWindow();
AddFilter(new TPoseViewFilter(this));
}
void
VirtualDirectoryPoseView::RestoreState(AttributeStreamNode* node)
{
_inherited::RestoreState(node);
fViewState->SetViewMode(kListMode);
}
void
VirtualDirectoryPoseView::RestoreState(const BMessage& message)
{
_inherited::RestoreState(message);
fViewState->SetViewMode(kListMode);
}
void
VirtualDirectoryPoseView::SavePoseLocations(BRect* frameIfDesktop)
{
}
void
VirtualDirectoryPoseView::SetViewMode(uint32 newMode)
{
}
EntryListBase*
VirtualDirectoryPoseView::InitDirentIterator(const entry_ref* ref)
{
if (fRootDefinitionFileRef.node < 0 || *ref != *TargetModel()->EntryRef())
return NULL;
Model sourceModel(ref, false, true);
if (sourceModel.InitCheck() != B_OK)
return NULL;
VirtualDirectoryEntryList* entryList
= new(std::nothrow) VirtualDirectoryEntryList(
*TargetModel()->NodeRef(), fDirectoryPaths);
if (entryList == NULL || entryList->InitCheck() != B_OK) {
delete entryList;
return NULL;
}
return entryList;
}
void
VirtualDirectoryPoseView::StartWatching()
{
int32 count = fDirectoryPaths.CountStrings();
for (int32 i = 0; i < count; i++) {
BString path = fDirectoryPaths.StringAt(i);
BPathMonitor::StartWatching(path, B_WATCH_DIRECTORY | B_WATCH_CHILDREN
| B_WATCH_NAME | B_WATCH_STAT | B_WATCH_INTERIM_STAT | B_WATCH_ATTR, this);
}
TTracker::WatchNode(TargetModel()->NodeRef(),
B_WATCH_NAME | B_WATCH_STAT | B_WATCH_ATTR, this);
if (!fIsRoot)
TTracker::WatchNode(&fRootDefinitionFileRef, B_WATCH_STAT, this);
}
void
VirtualDirectoryPoseView::StopWatching()
{
BPathMonitor::StopWatching(this);
stop_watching(this);
}
bool
VirtualDirectoryPoseView::FSNotification(const BMessage* message)
{
switch (message->GetInt32("opcode", 0)) {
case B_ENTRY_CREATED:
return _EntryCreated(message);
case B_ENTRY_REMOVED:
return _EntryRemoved(message);
case B_ENTRY_MOVED:
return _EntryMoved(message);
case B_STAT_CHANGED:
return _NodeStatChanged(message);
default:
return _inherited::FSNotification(message);
}
}
bool
VirtualDirectoryPoseView::_EntryCreated(const BMessage* message)
{
NotOwningEntryRef entryRef;
node_ref nodeRef;
if (message->FindInt32("device", &nodeRef.device) != B_OK
|| message->FindInt64("node", &nodeRef.node) != B_OK
|| message->FindInt64("directory", &entryRef.directory) != B_OK
|| message->FindString("name", (const char**)&entryRef.name) != B_OK) {
return true;
}
entryRef.device = nodeRef.device;
BString path;
if (message->FindString("path", &path) == B_OK
&& fDirectoryPaths.HasString(path)) {
BDirectory directory;
if (directory.SetTo(&nodeRef) != B_OK)
return true;
BPrivate::Storage::LongDirEntry longEntry;
struct dirent* entry = longEntry.dirent();
while (directory.GetNextDirents(entry, sizeof(longEntry), 1) == 1) {
if (strcmp(entry->d_name, ".") != 0
&& strcmp(entry->d_name, "..") != 0) {
_DispatchEntryCreatedOrRemovedMessage(B_ENTRY_CREATED,
node_ref(entry->d_dev, entry->d_ino),
NotOwningEntryRef(entry->d_pdev, entry->d_pino,
entry->d_name),
NULL, false);
}
}
return true;
}
struct stat st;
entry_ref visibleEntryRef;
if (!_GetEntry(entryRef.name, visibleEntryRef, &st)
|| visibleEntryRef != entryRef) {
return true;
}
VirtualDirectoryManager* manager = VirtualDirectoryManager::Instance();
AutoLocker<VirtualDirectoryManager> managerLocker(manager);
bool entryTranslated = S_ISDIR(st.st_mode);
if (entryTranslated) {
if (manager == NULL)
return true;
if (manager->TranslateDirectoryEntry(*TargetModel()->NodeRef(),
entryRef, nodeRef) != B_OK) {
return true;
}
}
BPose* pose = fPoseList->FindPoseByFileName(entryRef.name);
if (pose != NULL) {
if (nodeRef == *pose->TargetModel()->NodeRef()) {
return true;
}
if (manager != NULL)
manager->DirectoryRemoved(*pose->TargetModel()->NodeRef());
managerLocker.Unlock();
BMessage removedMessage(B_NODE_MONITOR);
_DispatchEntryCreatedOrRemovedMessage(B_ENTRY_REMOVED,
*pose->TargetModel()->NodeRef(), *pose->TargetModel()->EntryRef());
} else
managerLocker.Unlock();
return entryTranslated
? (_DispatchEntryCreatedOrRemovedMessage(B_ENTRY_CREATED, nodeRef,
entryRef), true)
: _inherited::FSNotification(message);
}
bool
VirtualDirectoryPoseView::_EntryRemoved(const BMessage* message)
{
NotOwningEntryRef entryRef;
node_ref nodeRef;
if (message->FindInt32("device", &nodeRef.device) != B_OK
|| message->FindInt64("node", &nodeRef.node) != B_OK
|| message->FindInt64("directory", &entryRef.directory)
!= B_OK
|| message->FindString("name", (const char**)&entryRef.name) != B_OK) {
return true;
}
entryRef.device = nodeRef.device;
if (nodeRef == *TargetModel()->NodeRef())
return _inherited::FSNotification(message);
BString path;
if (message->FindString("path", &path) == B_OK
&& fDirectoryPaths.HasString(path)) {
PoseList poses;
for (int32 i = 0; BPose* pose = fPoseList->ItemAt(i); i++) {
NotOwningEntryRef poseEntryRef = *pose->TargetModel()->EntryRef();
if (poseEntryRef.DirectoryNodeRef() == nodeRef)
poses.AddItem(pose);
}
for (int32 i = 0; BPose* pose = poses.ItemAt(i); i++) {
_DispatchEntryCreatedOrRemovedMessage(B_ENTRY_REMOVED,
*pose->TargetModel()->NodeRef(),
*pose->TargetModel()->EntryRef(), NULL, false);
}
return true;
}
entry_ref* actualEntryRef = &entryRef;
node_ref* actualNodeRef = &nodeRef;
entry_ref definitionEntryRef;
node_ref definitionNodeRef;
VirtualDirectoryManager* manager = VirtualDirectoryManager::Instance();
AutoLocker<VirtualDirectoryManager> managerLocker(manager);
if (manager != NULL
&& manager->GetSubDirectoryDefinitionFile(*TargetModel()->NodeRef(),
entryRef.name, definitionEntryRef, definitionNodeRef)) {
actualEntryRef = &definitionEntryRef;
actualNodeRef = &definitionNodeRef;
}
BPose* pose = fPoseList->FindPoseByFileName(actualEntryRef->name);
if (pose == NULL || *actualNodeRef != *pose->TargetModel()->NodeRef())
return true;
struct stat st;
entry_ref visibleEntryRef;
node_ref visibleNodeRef;
if (_GetEntry(actualEntryRef->name, visibleEntryRef, &st)) {
visibleNodeRef = node_ref(st.st_dev, st.st_ino);
if (S_ISDIR(st.st_mode)) {
if (manager == NULL || manager->TranslateDirectoryEntry(
*TargetModel()->NodeRef(), visibleEntryRef, visibleNodeRef)
!= B_OK) {
return true;
}
if (visibleNodeRef == *actualNodeRef)
return true;
}
}
if (actualEntryRef == &entryRef) {
managerLocker.Unlock();
if (_inherited::FSNotification(message))
pendingNodeMonitorCache.Add(message);
} else {
manager->DirectoryRemoved(*actualNodeRef);
managerLocker.Unlock();
_DispatchEntryCreatedOrRemovedMessage(B_ENTRY_REMOVED, *actualNodeRef,
*actualEntryRef);
}
_DispatchEntryCreatedOrRemovedMessage(B_ENTRY_CREATED, visibleNodeRef,
visibleEntryRef);
return true;
}
bool
VirtualDirectoryPoseView::_EntryMoved(const BMessage* message)
{
NotOwningEntryRef fromEntryRef;
NotOwningEntryRef toEntryRef;
node_ref nodeRef;
if (message->FindInt32("node device", &nodeRef.device) != B_OK
|| message->FindInt64("node", &nodeRef.node) != B_OK
|| message->FindInt32("device", &fromEntryRef.device) != B_OK
|| message->FindInt64("from directory", &fromEntryRef.directory) != B_OK
|| message->FindInt64("to directory", &toEntryRef.directory) != B_OK
|| message->FindString("from name", (const char**)&fromEntryRef.name)
!= B_OK
|| message->FindString("name", (const char**)&toEntryRef.name)
!= B_OK) {
return true;
}
toEntryRef.device = fromEntryRef.device;
_DispatchEntryCreatedOrRemovedMessage(B_ENTRY_REMOVED, nodeRef,
fromEntryRef, message->GetString("from path", NULL), false);
_DispatchEntryCreatedOrRemovedMessage(B_ENTRY_CREATED, nodeRef,
toEntryRef, message->GetString("path", NULL), false);
return true;
}
bool
VirtualDirectoryPoseView::_NodeStatChanged(const BMessage* message)
{
node_ref nodeRef;
if (message->FindInt32("device", &nodeRef.device) != B_OK
|| message->FindInt64("node", &nodeRef.node) != B_OK) {
return true;
}
if (nodeRef == fRootDefinitionFileRef) {
if ((message->GetInt32("fields", 0) & B_STAT_MODIFICATION_TIME) != 0) {
VirtualDirectoryManager* manager
= VirtualDirectoryManager::Instance();
if (manager != NULL) {
AutoLocker<VirtualDirectoryManager> managerLocker(manager);
if (!manager->DefinitionFileChanged(
*TargetModel()->NodeRef())) {
return true;
}
bigtime_t fileChangeTime;
manager->GetDefinitionFileChangeTime(*TargetModel()->NodeRef(),
fileChangeTime);
if (fileChangeTime != fFileChangeTime) {
_UpdateDirectoryPaths();
managerLocker.Unlock();
Refresh();
}
}
}
if (!fIsRoot)
return true;
}
return _inherited::FSNotification(message);
}
void
VirtualDirectoryPoseView::_DispatchEntryCreatedOrRemovedMessage(int32 opcode,
const node_ref& nodeRef, const entry_ref& entryRef, const char* path,
bool dispatchToSuperClass)
{
BMessage message(B_NODE_MONITOR);
message.AddInt32("opcode", opcode);
message.AddInt32("device", nodeRef.device);
message.AddInt64("node", nodeRef.node);
message.AddInt64("directory", entryRef.directory);
message.AddString("name", entryRef.name);
if (path != NULL && path[0] != '\0')
message.AddString("path", path);
bool result = dispatchToSuperClass
? _inherited::FSNotification(&message)
: FSNotification(&message);
if (!result)
pendingNodeMonitorCache.Add(&message);
}
bool
VirtualDirectoryPoseView::_GetEntry(const char* name, entry_ref& _ref,
struct stat* _st)
{
return VirtualDirectoryManager::GetEntry(fDirectoryPaths, name, &_ref, _st);
}
status_t
VirtualDirectoryPoseView::_UpdateDirectoryPaths()
{
VirtualDirectoryManager* manager = VirtualDirectoryManager::Instance();
Model* model = TargetModel();
status_t error = manager->ResolveDirectoryPaths(*model->NodeRef(),
*model->EntryRef(), fDirectoryPaths);
if (error != B_OK)
return error;
manager->GetDefinitionFileChangeTime(*model->NodeRef(), fFileChangeTime);
return B_OK;
}
}