haiku/src/kits/tracker/VirtualDirectoryPoseView.cpp
looncraz c5b4dc4007 Tracker: Use Set*UIColor, improved font awareness.
InfoWindow now uses the font size to determine the window size and
placement of elements. Also uses system colors, including link
colors. Permissions view not font sensitive yet.

Signed-off-by: Augustin Cavalier <waddlesplash@gmail.com>
Patch 0039 from looncraz, unmodified.
2016-01-04 07:24:41 -05:00

503 lines
13 KiB
C++

/*
* 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 {
// #pragma mark - VirtualDirectoryPoseView
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)
{
switch (message->what) {
// ignore all edit operations
case B_CUT:
case kCutMoreSelectionToClipboard:
case kDuplicateSelection:
case kDelete:
case kMoveToTrash:
case kNewEntryFromTemplate:
case kNewFolder:
case kEditItem:
break;
default:
_inherited::MessageReceived(message);
break;
}
}
void
VirtualDirectoryPoseView::AttachedToWindow()
{
_inherited::AttachedToWindow();
SetViewUIColor(B_DOCUMENT_BACKGROUND_COLOR, B_DARKEN_1_TINT);
SetLowUIColor(B_DOCUMENT_BACKGROUND_COLOR, B_DARKEN_1_TINT);
}
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()
{
// watch the directories
int32 count = fDirectoryPaths.CountStrings();
for (int32 i = 0; i < count; i++) {
BString path = fDirectoryPaths.StringAt(i);
BPathMonitor::StartWatching(path, B_WATCH_DIRECTORY, this);
}
// watch the definition file
TTracker::WatchNode(TargetModel()->NodeRef(),
B_WATCH_NAME | B_WATCH_STAT | B_WATCH_ATTR, this);
// also watch the root definition file
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;
// It might be one of our directories.
BString path;
if (message->FindString("path", &path) == B_OK
&& fDirectoryPaths.HasString(path)) {
// Iterate through the directory and generate an entry-created message
// for each entry.
BDirectory directory;
if (directory.SetTo(&nodeRef) != B_OK)
return true;
BPrivate::Storage::LongDirEntry entry;
while (directory.GetNextDirents(&entry, sizeof(entry), 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;
}
// See, if this entry actually becomes visible. If not, we can simply ignore
// it.
struct stat st;
entry_ref visibleEntryRef;
if (!_GetEntry(entryRef.name, visibleEntryRef, &st)
|| visibleEntryRef != entryRef) {
return true;
}
// If it is a directory, translate it.
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;
}
}
// The entry might replace another entry. If it does, we'll fake a removed
// message for the old one first.
BPose* pose = fPoseList->FindPoseByFileName(entryRef.name);
if (pose != NULL) {
if (nodeRef == *pose->TargetModel()->NodeRef()) {
// apparently not really a new entry -- can happen for
// subdirectories
return true;
}
// It may be a directory, so tell the manager.
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;
// It might be our definition file.
if (nodeRef == *TargetModel()->NodeRef())
return _inherited::FSNotification(message);
// It might be one of our directories.
BString path;
if (message->FindString("path", &path) == B_OK
&& fDirectoryPaths.HasString(path)) {
// Find all poses that stem from that directory and generate an
// entry-removed message for each.
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;
}
// If it is a directory, translate it.
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;
}
// Check the pose. It might have been an entry that wasn't visible anyway.
// In that case we can just ignore the notification.
BPose* pose = fPoseList->FindPoseByFileName(actualEntryRef->name);
if (pose == NULL || *actualNodeRef != *pose->TargetModel()->NodeRef())
return true;
// See, if another entry becomes visible, now.
struct stat st;
entry_ref visibleEntryRef;
node_ref visibleNodeRef;
if (_GetEntry(actualEntryRef->name, visibleEntryRef, &st)) {
// If the new entry is a directory, translate it.
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;
}
// Effectively nothing changes, when the removed entry was a
// directory as well.
if (visibleNodeRef == *actualNodeRef)
return true;
}
}
if (actualEntryRef == &entryRef) {
managerLocker.Unlock();
if (_inherited::FSNotification(message))
pendingNodeMonitorCache.Add(message);
} else {
// tell the manager that the directory has been removed
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;
// TODO: That's the lazy approach. Ideally we'd analyze the situation and
// forward a B_ENTRY_MOVED, if possible. There are quite a few cases to
// consider, though.
_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())) {
// The definition file no longer exists. Ignore the message
// -- we'll get a remove notification soon.
return true;
}
bigtime_t fileChangeTime;
manager->GetDefinitionFileChangeTime(*TargetModel()->NodeRef(),
fileChangeTime);
if (fileChangeTime != fFileChangeTime) {
_UpdateDirectoryPaths();
managerLocker.Unlock();
Refresh();
// TODO: Refresh() is rather radical. Or rather its
// implementation is. Ideally it would just compare the
// currently added poses with what a new dir iterator
// returns and remove/add poses as needed.
}
}
}
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;
}
} // namespace BPrivate