Add virtual directory feature to Tracker

Similar to stored queries, files of the virtual directory type behave
like directories -- i.e. they open in a list-mode Tracker window and
show up as an item with submenu in navigation menus. The file itself is
a plain text file in driver settings format. It can have an arbitrary
number of "directory" entries, which specify the paths of (actual)
directories for which the virtual directory provides a merged view. The
view will not show duplicate entries. For non-directory entries the
first one encountered (according to the order the directory paths are
specified in the file) will be shown. A subdirectory entry will again
behave like a virtual directory.

The support in Tracker isn't perfect yet. I'm afraid major refactoring
would be necessary to get it there.

The virtual directory file type uses a differently colored version of
the folder icon. Alternatives welcome.
This commit is contained in:
Ingo Weinhold 2013-06-29 14:34:19 +02:00
parent de85051c81
commit 1c29b26e7c
28 changed files with 2175 additions and 24 deletions

Binary file not shown.

View File

@ -0,0 +1,24 @@
resource(0, "BEOS:TYPE") #'MIMS' "application/x-vnd.Be-meta-mime";
resource(1, "META:TYPE") "application/x-vnd.haiku-virtual-directory";
resource(2, "META:S:DESC") #'MSDC' "Virtual Directory";
resource(3, "META:L:DESC") #'MLDC'
"Virtual directory merging the contents of multiple actual directories";
resource(5, "META:PREF_APP") #'MSIG' "application/x-vnd.Be-TRAK";
resource(6, "META:ICON") #'VICN' array {
$"6E636966070500020006023841813C9B3BBEAB393A4F584B254D4A7AEB009BE2"
$"FFFF5A6E82020016022C60673CAAABBEBB082E6EFB4BBA064A22B000FFFF8E02"
$"0006022C25F43C6917BEBB082E6EFB4BAFBF497E0B0082AAC8FF40407C033D3D"
$"5D020016023AE3B43C79DCBE5BC53CC0974B42AC47CA41009AFF50040165070A"
$"064A5D505D545958595E53504F0A04232F2D494A5B463E0A04232F463E4A5BC3"
$"233C0A063A282E494A5A5C2C502A4C2C0A04302B30494A5951360A04302B5136"
$"4A5954BB1E0A0A232F2D494A5B5C2C502A4C2C3A28382D302BB957BAA7080A06"
$"0100000A0001061815FF01178400040A00010618001501178600040A03010300"
$"0A020104000A050105000A010101000A04010200"
};

View File

@ -1507,7 +1507,8 @@ BContainerWindow::MessageReceived(BMessage* message)
PoseView()->MoveSelectionInto(&model, this, false, false,
message->what == kCreateLink,
message->what == kCreateRelativeLink);
} else if (!TargetModel()->IsQuery()) {
} else if (!TargetModel()->IsQuery()
&& !TargetModel()->IsVirtualDirectory()) {
// no destination specified, create link in same dir as item
PoseView()->MoveSelectionInto(TargetModel(), this, false, false,
message->what == kCreateLink,
@ -1895,7 +1896,8 @@ BContainerWindow::AddFileMenu(BMenu* menu)
new BMessage(kFindButton), 'F'));
}
if (!TargetModel()->IsQuery() && !IsTrash() && !IsPrintersDir()) {
if (!TargetModel()->IsQuery() && !TargetModel()->IsVirtualDirectory()
&& !IsTrash() && !IsPrintersDir()) {
if (!PoseView()->IsFilePanel()) {
TemplatesMenu* templateMenu = new TemplatesMenu(PoseView(),
B_TRANSLATE("New"));
@ -2108,6 +2110,7 @@ BContainerWindow::AddShortcuts()
ASSERT(!IsTrash());
ASSERT(!PoseView()->IsFilePanel());
ASSERT(!TargetModel()->IsQuery());
ASSERT(!TargetModel()->IsVirtualDirectory());
AddShortcut('X', B_COMMAND_KEY | B_SHIFT_KEY,
new BMessage(kCutMoreSelectionToClipboard), this);
@ -3772,8 +3775,9 @@ BContainerWindow::SetUpDefaultState()
// try copying state from our parent directory, unless it is the
// desktop folder
BEntry entry(TargetModel()->EntryRef());
BDirectory parent;
if (entry.GetParent(&parent) == B_OK && parent != desktop) {
BNode parent;
if (FSGetParentVirtualDirectoryAware(entry, parent) == B_OK
&& parent != desktop) {
PRINT(("looking at parent for state\n"));
if (NodeHasSavedState(&parent)) {
PRINT(("got state from parent\n"));

View File

@ -78,6 +78,7 @@ DesktopPoseView::InitDesktopDirentIterator(BPoseView* nodeMonitoringTarget,
CachedEntryIteratorList* result = new CachedEntryIteratorList();
ASSERT(!sourceModel.IsQuery());
ASSERT(!sourceModel.IsVirtualDirectory());
ASSERT(sourceModel.Node());
BDirectory* sourceDirectory
= dynamic_cast<BDirectory*>(sourceModel.Node());

View File

@ -115,10 +115,9 @@ BDirMenu::Populate(const BEntry* startEntry, BWindow* originatingWindow,
// if we're at the root directory skip "mnt" and
// go straight to "/"
parent.SetTo("/");
parent.GetEntry(&entry);
} else
entry.GetParent(&parent);
parent.GetEntry(&entry);
FSGetParentVirtualDirectoryAware(entry, entry);
}
BDirectory desktopDir;
@ -135,9 +134,7 @@ BDirMenu::Populate(const BEntry* startEntry, BWindow* originatingWindow,
kAttrPoseInfoForeign, B_RAW_TYPE, 0, &info, sizeof(PoseInfo),
&PoseInfo::EndianSwap);
BDirectory parent;
entry.GetParent(&parent);
BEntry parentEntry;
bool hitRoot = false;
BDirectory dir(&entry);
@ -146,8 +143,9 @@ BDirMenu::Populate(const BEntry* startEntry, BWindow* originatingWindow,
// if we're at the root directory skip "mnt" and
// go straight to "/"
hitRoot = true;
parent.SetTo("/");
}
parentEntry.SetTo("/");
} else
FSGetParentVirtualDirectoryAware(entry, parentEntry);
if (showDesktop) {
BEntry root("/");
@ -174,7 +172,9 @@ BDirMenu::Populate(const BEntry* startEntry, BWindow* originatingWindow,
break;
}
parent.GetEntry(&entry);
entry = parentEntry;
if (entry.InitCheck() != B_OK)
break;
}
// select last item in menu

View File

@ -71,6 +71,8 @@ respective holders. All rights reserved.
#include <fs_info.h>
#include <sys/utsname.h>
#include <AutoLocker.h>
#include "Attributes.h"
#include "Bitmaps.h"
#include "Commands.h"
@ -85,6 +87,7 @@ respective holders. All rights reserved.
#include "Tracker.h"
#include "TrackerSettings.h"
#include "Utilities.h"
#include "VirtualDirectoryManager.h"
enum {
@ -3157,6 +3160,66 @@ GetAttrInfo(const BNode* node, const char* hostAttrName,
return kReadAttrFailed;
}
status_t
FSGetParentVirtualDirectoryAware(const BEntry& entry, entry_ref& _ref)
{
node_ref nodeRef;
if (entry.GetNodeRef(&nodeRef) == B_OK) {
if (VirtualDirectoryManager* manager
= VirtualDirectoryManager::Instance()) {
AutoLocker<VirtualDirectoryManager> managerLocker(manager);
if (manager->GetParentDirectoryDefinitionFile(nodeRef, _ref,
nodeRef)) {
return B_OK;
}
}
}
status_t error;
BDirectory parent;
BEntry parentEntry;
if ((error = entry.GetParent(&parent)) != B_OK
|| (error = parent.GetEntry(&parentEntry)) != B_OK
|| (error = parentEntry.GetRef(&_ref)) != B_OK) {
return error;
}
return B_OK;
}
status_t
FSGetParentVirtualDirectoryAware(const BEntry& entry, BEntry& _entry)
{
node_ref nodeRef;
if (entry.GetNodeRef(&nodeRef) == B_OK) {
if (VirtualDirectoryManager* manager
= VirtualDirectoryManager::Instance()) {
AutoLocker<VirtualDirectoryManager> managerLocker(manager);
entry_ref parentRef;
if (manager->GetParentDirectoryDefinitionFile(nodeRef, parentRef,
nodeRef)) {
return _entry.SetTo(&parentRef);
}
}
}
return entry.GetParent(&_entry);
}
status_t
FSGetParentVirtualDirectoryAware(const BEntry& entry, BNode& _node)
{
entry_ref ref;
status_t error = FSGetParentVirtualDirectoryAware(entry, ref);
if (error == B_OK)
error = _node.SetTo(&ref);
return error;
}
// launching code
static status_t

View File

@ -238,6 +238,10 @@ status_t FSRecursiveCreateFolder(const char* path);
void FSMakeOriginalName(BString &name, const BDirectory* destDir,
const char* suffix = 0);
status_t FSGetParentVirtualDirectoryAware(const BEntry& entry, entry_ref& _ref);
status_t FSGetParentVirtualDirectoryAware(const BEntry& entry, BEntry& _entry);
status_t FSGetParentVirtualDirectoryAware(const BEntry& entry, BNode& _node);
status_t TrackerLaunch(const entry_ref* app, bool async);
status_t TrackerLaunch(const BMessage* refs, bool async,
bool okToRunOpenWith = true);

View File

@ -55,6 +55,7 @@ All rights reserved.
#include "QueryPoseView.h"
#include "Tracker.h"
#include "Utilities.h"
#include "VirtualDirectoryEntryList.h"
#undef B_TRANSLATION_CONTEXT
@ -138,6 +139,8 @@ FavoritesMenu::AddNextItem()
if (startModel.IsQuery())
fContainer = new QueryEntryListCollection(&startModel);
else if (startModel.IsVirtualDirectory())
fContainer = new VirtualDirectoryEntryList(&startModel);
else
fContainer = new DirectoryEntryList(*dynamic_cast<BDirectory*>
(startModel.Node()));

View File

@ -86,6 +86,10 @@ SharedLibrary libtracker.so :
TrashWatcher.cpp
Utilities.cpp
ViewState.cpp
VirtualDirectoryEntryList.cpp
VirtualDirectoryManager.cpp
VirtualDirectoryPoseView.cpp
VirtualDirectoryWindow.cpp
VolumeWindow.cpp
WidgetAttributeText.cpp

View File

@ -51,6 +51,7 @@ namespace BPrivate {
#define B_PRINTER_SPOOL_MIMETYPE "application/x-vnd.Be.printer-spool"
#define kPlainTextMimeType "text/plain"
#define kVirtualDirectoryMimeType "application/x-vnd.haiku-virtual-directory"
#define kBitmapMimeType "image/x-vnd.Be-bitmap"
#define kLargeIconType "icon/large"

View File

@ -325,9 +325,11 @@ Model::CompareFolderNamesFirst(const Model* compareModel) const
const Model* resolvedCompareModel = compareModel->ResolveIfLink();
const Model* resolvedMe = ResolveIfLink();
bool meIsDirOrVolume = resolvedMe->IsDirectory() || resolvedMe->IsVolume();
bool meIsDirOrVolume = resolvedMe->IsDirectory() || resolvedMe->IsVolume()
|| resolvedMe->IsVirtualDirectory();
bool otherIsDirOrVolume = resolvedCompareModel->IsDirectory()
|| resolvedCompareModel->IsVolume();
|| resolvedCompareModel->IsVolume()
|| resolvedCompareModel->IsVirtualDirectory();
if (meIsDirOrVolume) {
if (!otherIsDirOrVolume)
@ -426,6 +428,7 @@ Model::OpenNodeCommon(bool writable)
case kExecutableNode:
case kQueryNode:
case kQueryTemplateNode:
case kVirtualDirectoryNode:
// open or reopen
delete fNode;
fNode = new BFile(&fEntryRef,
@ -629,6 +632,8 @@ Model::FinishSettingUpType()
fBaseType = kQueryNode;
else if (strcmp(mimeString, B_QUERY_TEMPLATE_MIMETYPE) == 0)
fBaseType = kQueryTemplateNode;
else if (strcmp(mimeString, kVirtualDirectoryMimeType) == 0)
fBaseType = kVirtualDirectoryNode;
if (info.GetPreferredApp(mimeString) == B_OK) {
if (fPreferredAppName)
@ -1332,6 +1337,10 @@ Model::PrintToStream(int32 level, bool deep)
PRINT(("volume, name %s\n", fVolumeName ? fVolumeName : ""));
break;
case kVirtualDirectoryNode:
PRINT(("virtual directory\n"));
break;
default:
PRINT(("unknown\n"));
break;

View File

@ -141,6 +141,7 @@ class Model {
bool IsTrash() const;
bool IsDesktop() const;
bool IsVolume() const;
bool IsVirtualDirectory() const;
IconSource IconFrom() const;
void SetIconFrom(IconSource);
@ -239,6 +240,7 @@ class Model {
kRootNode,
kTrashNode,
kDesktopNode,
kVirtualDirectoryNode,
kUnknownNode
};
@ -381,7 +383,8 @@ Model::IsFile() const
return fBaseType == kPlainNode
|| fBaseType == kQueryNode
|| fBaseType == kQueryTemplateNode
|| fBaseType == kExecutableNode;
|| fBaseType == kExecutableNode
|| fBaseType == kVirtualDirectoryNode;
}
@ -422,7 +425,7 @@ Model::IsContainer() const
{
// I guess as in should show container window -
// volumes show the volume window
return IsQuery() || IsDirectory();
return IsQuery() || IsDirectory() || IsVirtualDirectory();
}
@ -461,6 +464,13 @@ Model::IsSymLink() const
}
inline bool
Model::IsVirtualDirectory() const
{
return fBaseType == kVirtualDirectoryNode;
}
inline bool
Model::HasLocalizedName() const
{

View File

@ -64,6 +64,7 @@ their respective holders. All rights reserved.
#include "QueryPoseView.h"
#include "Thread.h"
#include "Tracker.h"
#include "VirtualDirectoryEntryList.h"
namespace BPrivate {
@ -420,6 +421,8 @@ BNavMenu::StartBuildingItemList()
if (startModel.IsQuery())
fContainer = new QueryEntryListCollection(&startModel);
else if (startModel.IsVirtualDirectory())
fContainer = new VirtualDirectoryEntryList(&startModel);
else if (startModel.IsDesktop()) {
fIteratingDesktop = true;
fContainer = DesktopPoseView::InitDesktopDirentIterator(

View File

@ -144,3 +144,21 @@ PoseList::FindAllPoses(const node_ref* node) const
}
return result;
}
BPose*
PoseList::FindPoseByFileName(const char* name, int32* _index) const
{
int32 count = CountItems();
for (int32 index = 0; index < count; index++) {
BPose* pose = ItemAt(index);
ASSERT(pose->TargetModel());
if (strcmp(pose->TargetModel()->EntryRef()->name, name) == 0) {
if (_index)
*_index = index;
return pose;
}
}
return NULL;
}

View File

@ -67,6 +67,8 @@ public:
// same as FindPose, node can be a target of the actual
// pose if the pose is a symlink
PoseList* FindAllPoses(const node_ref* node) const;
BPose* FindPoseByFileName(const char* name, int32* _index = NULL) const;
};
// iteration glue, add permutations as needed

View File

@ -72,6 +72,7 @@ All rights reserved.
#include <Window.h>
#include <ObjectListPrivate.h>
#include <PathMonitor.h>
#include "Attributes.h"
#include "AttributeStream.h"
@ -1107,6 +1108,7 @@ BPoseView::InitDirentIterator(const entry_ref* ref)
return NULL;
ASSERT(!sourceModel.IsQuery());
ASSERT(!sourceModel.IsVirtualDirectory());
ASSERT(sourceModel.Node());
BDirectory* directory = dynamic_cast<BDirectory*>(sourceModel.Node());
@ -2153,6 +2155,7 @@ BPoseView::MessageReceived(BMessage* message)
break;
case B_NODE_MONITOR:
case B_PATH_MONITOR:
case B_QUERY_UPDATE:
if (!FSNotification(message))
pendingNodeMonitorCache.Add(message);
@ -4920,6 +4923,10 @@ BPoseView::MoveSelectionInto(Model* destFolder, BContainerWindow* srcWindow,
okToMove = alert->Go() == 1;
}
// TODO: Handle correctly!
if (srcWindow->TargetModel()->IsVirtualDirectory())
okToMove = false;
if (okToMove) {
PoseList* selectionList = srcWindow->PoseView()->SelectionList();
BList* pointList = destWindow->PoseView()->GetDropPointList(clickPt, loc, selectionList,
@ -5166,6 +5173,7 @@ BPoseView::FSNotification(const BMessage* message)
TrackerSettings settings;
if (dirNode != *TargetModel()->NodeRef()
&& !TargetModel()->IsQuery()
&& !TargetModel()->IsVirtualDirectory()
&& !TargetModel()->IsRoot()
&& (!settings.ShowDisksIcon() || !IsDesktopView())) {
if (count == 0)
@ -5446,7 +5454,8 @@ BPoseView::EntryMoved(const BMessage* message)
TargetModel()->UpdateEntryRef(&dirNode, name);
assert_cast<BContainerWindow*>(Window())->UpdateTitle();
}
if (oldDir == dirNode.node || TargetModel()->IsQuery()) {
if (oldDir == dirNode.node || TargetModel()->IsQuery()
|| TargetModel()->IsVirtualDirectory()) {
// rename or move of entry in this directory (or query)
@ -8329,11 +8338,9 @@ BPoseView::OpenParent()
return;
BEntry entry(TargetModel()->EntryRef());
BDirectory parent;
entry_ref ref;
if (entry.GetParent(&parent) != B_OK
|| parent.GetEntry(&entry) != B_OK
if (FSGetParentVirtualDirectoryAware(entry, entry) != B_OK
|| entry.GetRef(&ref) != B_OK)
return;

View File

@ -60,6 +60,7 @@ All rights reserved.
#include "SlowContextPopup.h"
#include "Thread.h"
#include "Tracker.h"
#include "VirtualDirectoryEntryList.h"
#undef B_TRANSLATION_CONTEXT
@ -247,6 +248,8 @@ BSlowContextMenu::StartBuildingItemList()
if (startModel.IsQuery())
fContainer = new QueryEntryListCollection(&startModel);
else if (startModel.IsVirtualDirectory())
fContainer = new VirtualDirectoryEntryList(&startModel);
else if (startModel.IsDesktop()) {
fIteratingDesktop = true;
fContainer = DesktopPoseView::InitDesktopDirentIterator(0,

View File

@ -59,6 +59,8 @@ All rights reserved.
#include <Volume.h>
#include <VolumeRoster.h>
#include <PathMonitor.h>
#include "Attributes.h"
#include "AutoLock.h"
#include "AutoMounterSettings.h"
@ -86,6 +88,7 @@ All rights reserved.
#include "TaskLoop.h"
#include "Thread.h"
#include "Utilities.h"
#include "VirtualDirectoryWindow.h"
#include "VolumeWindow.h"
// prototypes for some private kernel calls that will some day be public
@ -209,7 +212,26 @@ GetVolumeFlags(Model* model)
}
// #pragma mark -
// #pragma mark - WatchingInterface
class TTracker::WatchingInterface : public BPathMonitor::BWatchingInterface {
public:
virtual status_t WatchNode(const node_ref* node, uint32 flags,
const BMessenger& target)
{
return TTracker::WatchNode(node, flags, target);
}
virtual status_t WatchNode(const node_ref* node, uint32 flags,
const BHandler* handler, const BLooper* looper = NULL)
{
return TTracker::WatchNode(node, flags, BMessenger(handler, looper));
}
};
// #pragma mark - TTracker
#undef B_TRANSLATION_CONTEXT
@ -217,8 +239,11 @@ GetVolumeFlags(Model* model)
TTracker::TTracker()
: BApplication(kTrackerSignature),
fWatchingInterface(new WatchingInterface),
fSettingsWindow(NULL)
{
BPathMonitor::SetWatchingInterface(fWatchingInterface);
// set the cwd to /boot/home, anything that's launched
// from Tracker will automatically inherit this
BPath homePath;
@ -261,6 +286,9 @@ TTracker::~TTracker()
{
gLaunchLooper->Lock();
gLaunchLooper->Quit();
BPathMonitor::SetWatchingInterface(NULL);
delete fWatchingInterface;
}
@ -961,6 +989,9 @@ TTracker::OpenContainerWindow(Model* model, BMessage* originalRefsList,
} else if (model->IsQuery()) {
// window will adopt the model
window = new BQueryContainerWindow(&fWindowList, openFlags);
} else if (model->IsVirtualDirectory()) {
// window will adopt the model
window = new VirtualDirectoryWindow(&fWindowList, openFlags);
} else
// window will adopt the model
window = new BContainerWindow(&fWindowList, openFlags);

View File

@ -74,7 +74,7 @@ const uint32 B_ENTRY_SPECIFIER = 'sref';
class TTracker : public BApplication {
class TTracker : public BApplication {
public:
TTracker();
virtual ~TTracker();
@ -163,6 +163,9 @@ class TTracker : public BApplication {
bool GetProperty(BMessage*, int32, const char*, BMessage*);
bool SetProperty(BMessage*, BMessage*, int32, const char*, BMessage*);
private:
class WatchingInterface;
private:
// callbacks for ChildParentSoon calls
bool CloseParentWaitingForChild(const entry_ref* child,
@ -221,6 +224,7 @@ class TTracker : public BApplication {
BTrashWatcher* fTrashWatcher;
TaskLoop* fTaskLoop;
int32 fNodeMonitorCount;
WatchingInterface* fWatchingInterface;
TrackerSettingsWindow* fSettingsWindow;

View File

@ -0,0 +1,153 @@
/*
* Copyright 2013, Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold, ingo_weinhold@gmx.de
*/
#include "VirtualDirectoryEntryList.h"
#include <AutoLocker.h>
#include <storage_support.h>
#include "Model.h"
#include "VirtualDirectoryManager.h"
namespace BPrivate {
VirtualDirectoryEntryList::VirtualDirectoryEntryList(Model* model)
:
EntryListBase(),
fDefinitionFileRef(),
fMergedDirectory(BMergedDirectory::B_ALWAYS_FIRST)
{
VirtualDirectoryManager* manager = VirtualDirectoryManager::Instance();
if (manager == NULL) {
fStatus = B_NO_MEMORY;
return;
}
AutoLocker<VirtualDirectoryManager> managerLocker(manager);
BStringList directoryPaths;
fStatus = manager->ResolveDirectoryPaths(*model->NodeRef(),
*model->EntryRef(), directoryPaths, &fDefinitionFileRef);
if (fStatus != B_OK)
return;
fStatus = _InitMergedDirectory(directoryPaths);
}
VirtualDirectoryEntryList::VirtualDirectoryEntryList(
const node_ref& definitionFileRef, const BStringList& directoryPaths)
:
EntryListBase(),
fDefinitionFileRef(definitionFileRef),
fMergedDirectory(BMergedDirectory::B_ALWAYS_FIRST)
{
fStatus = _InitMergedDirectory(directoryPaths);
}
VirtualDirectoryEntryList::~VirtualDirectoryEntryList()
{
}
status_t
VirtualDirectoryEntryList::InitCheck() const
{
return EntryListBase::InitCheck();
}
status_t
VirtualDirectoryEntryList::GetNextEntry(BEntry* entry, bool traverse)
{
entry_ref ref;
status_t error = GetNextRef(&ref);
if (error != B_OK)
return error;
return entry->SetTo(&ref, traverse);
}
status_t
VirtualDirectoryEntryList::GetNextRef(entry_ref* ref)
{
BPrivate::Storage::LongDirEntry entry;
int32 result = GetNextDirents(&entry, sizeof(entry), 1);
if (result < 0)
return result;
if (result == 0)
return B_ENTRY_NOT_FOUND;
ref->device = entry.d_pdev;
ref->directory = entry.d_pino;
return ref->set_name(entry.d_name);
}
int32
VirtualDirectoryEntryList::GetNextDirents(struct dirent* buffer, size_t length,
int32 count)
{
if (count > 1)
count = 1;
int32 countRead = fMergedDirectory.GetNextDirents(buffer, length, count);
if (countRead != 1)
return countRead;
// deal with directories
entry_ref ref;
ref.device = buffer->d_pdev;
ref.directory = buffer->d_pino;
if (ref.set_name(buffer->d_name) == B_OK && BEntry(&ref).IsDirectory()) {
if (VirtualDirectoryManager* manager
= VirtualDirectoryManager::Instance()) {
AutoLocker<VirtualDirectoryManager> managerLocker(manager);
manager->TranslateDirectoryEntry(fDefinitionFileRef, buffer);
}
}
return countRead;
}
status_t
VirtualDirectoryEntryList::Rewind()
{
return fMergedDirectory.Rewind();
}
int32
VirtualDirectoryEntryList::CountEntries()
{
return 0;
}
status_t
VirtualDirectoryEntryList::_InitMergedDirectory(
const BStringList& directoryPaths)
{
status_t error = fMergedDirectory.Init();
if (error != B_OK)
return error;
int32 count = directoryPaths.CountStrings();
for (int32 i = 0; i < count; i++)
fMergedDirectory.AddDirectory(directoryPaths.StringAt(i));
return B_OK;
}
} // namespace BPrivate

View File

@ -0,0 +1,57 @@
/*
* Copyright 2013, Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold, ingo_weinhold@gmx.de
*/
#ifndef VIRTUAL_DIRECTORY_ENTRY_LIST_H
#define VIRTUAL_DIRECTORY_ENTRY_LIST_H
#include <StringList.h>
#include <MergedDirectory.h>
#include "EntryIterator.h"
namespace BPrivate {
class Model;
class VirtualDirectoryEntryList : public EntryListBase {
public:
VirtualDirectoryEntryList(Model* model);
VirtualDirectoryEntryList(
const node_ref& definitionFileRef,
const BStringList& directoryPaths);
virtual ~VirtualDirectoryEntryList();
virtual status_t InitCheck() const;
virtual status_t GetNextEntry(BEntry* entry,
bool traverse = false);
virtual status_t GetNextRef(entry_ref* ref);
virtual int32 GetNextDirents(struct dirent* buffer,
size_t length, int32 count = INT_MAX);
virtual status_t Rewind();
virtual int32 CountEntries();
private:
status_t _InitMergedDirectory(
const BStringList& directoryPaths);
private:
node_ref fDefinitionFileRef;
BMergedDirectory fMergedDirectory;
};
} // namespace BPrivate
#endif // VIRTUAL_DIRECTORY_ENTRY_LIST_H

View File

@ -0,0 +1,830 @@
/*
* Copyright 2013, Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold, ingo_weinhold@gmx.de
*/
#include "VirtualDirectoryManager.h"
#include <errno.h>
#include <new>
#include <Directory.h>
#include <File.h>
#include <StringList.h>
#include <AutoDeleter.h>
#include <AutoLocker.h>
#include <DriverSettings.h>
#include <NotOwningEntryRef.h>
#include <Uuid.h>
#include "MimeTypes.h"
#include "Model.h"
namespace BPrivate {
static const size_t kMaxVirtualDirectoryFileSize = 10 * 1024;
static const char* const kTemporaryDefinitionFileBaseDirectoryPath
= "/tmp/tracker/virtual-directories";
static bool
operator<(const node_ref& a, const node_ref& b)
{
if (a.device != b.device)
return a.device < b.device;
return a.node < b.node;
}
// #pragma mark - Info
class VirtualDirectoryManager::Info {
private:
typedef BObjectList<Info> InfoList;
public:
Info(RootInfo* root, Info* parent, const BString& path,
const node_ref& definitionFileNodeRef,
const entry_ref& definitionFileEntryRef)
:
fRoot(root),
fParent(parent),
fPath(path),
fDefinitionFileNodeRef(definitionFileNodeRef),
fDefinitionFileEntryRef(definitionFileEntryRef),
fId(),
fChildDefinitionsDirectoryRef(-1, -1),
fChildren(10, true)
{
}
~Info()
{
}
RootInfo* Root() const
{
return fRoot;
}
Info* Parent() const
{
return fParent;
}
const char* Name() const
{
return fDefinitionFileEntryRef.name;
}
const BString& Path() const
{
return fPath;
}
const node_ref& DefinitionFileNodeRef() const
{
return fDefinitionFileNodeRef;
}
const entry_ref& DefinitionFileEntryRef() const
{
return fDefinitionFileEntryRef;
}
const InfoList& Children() const
{
return fChildren;
}
const BString& Id() const
{
return fId;
}
void SetId(const BString& id)
{
fId = id;
}
const node_ref& ChildDefinitionsDirectoryRef() const
{
return fChildDefinitionsDirectoryRef;
}
void SetChildDefinitionsDirectoryRef(const node_ref& ref)
{
fChildDefinitionsDirectoryRef = ref;
}
Info* GetChild(const char* name) const
{
for (int32 i = 0; Info* child = fChildren.ItemAt(i); i++) {
if (strcmp(name, child->Name()) == 0)
return child;
}
return NULL;
}
Info* CreateChild(const node_ref& definitionFileNodeRef,
const entry_ref& definitionFileEntryRef)
{
BString path;
if (fPath.IsEmpty()) {
path = definitionFileEntryRef.name;
} else {
path.SetToFormat("%s/%s", fPath.String(),
definitionFileEntryRef.name);
}
if (path.IsEmpty())
return NULL;
Info* info = new(std::nothrow) Info(fRoot, this, path,
definitionFileNodeRef, definitionFileEntryRef);
if (info == NULL || !fChildren.AddItem(info)) {
delete info;
return NULL;
}
return info;
}
bool DeleteChild(Info* info)
{
return fChildren.RemoveItem(info, true);
}
void DeleteChildAt(int32 index)
{
delete fChildren.RemoveItemAt(index);
}
private:
RootInfo* fRoot;
Info* fParent;
BString fPath;
node_ref fDefinitionFileNodeRef;
entry_ref fDefinitionFileEntryRef;
BString fId;
node_ref fChildDefinitionsDirectoryRef;
InfoList fChildren;
};
// #pragma mark - RootInfo
class VirtualDirectoryManager::RootInfo {
public:
RootInfo(const node_ref& definitionFileNodeRef,
const entry_ref& definitionFileEntryRef)
:
fDirectoryPaths(),
fInfo(new(std::nothrow) VirtualDirectoryManager::Info(this, NULL,
BString(), definitionFileNodeRef, definitionFileEntryRef)),
fFileTime(-1),
fLastChangeTime(-1)
{
}
~RootInfo()
{
delete fInfo;
}
status_t InitCheck() const
{
return fInfo != NULL ? B_OK : B_NO_MEMORY;
}
bigtime_t FileTime() const
{
return fFileTime;
}
bigtime_t LastChangeTime() const
{
return fLastChangeTime;
}
const BStringList& DirectoryPaths() const
{
return fDirectoryPaths;
}
status_t ReadDefinition(bool* _changed = NULL)
{
// open the definition file
BFile file;
status_t error = file.SetTo(&fInfo->DefinitionFileEntryRef(),
B_READ_ONLY);
if (error != B_OK)
return error;
struct stat st;
error = file.GetStat(&st);
if (error != B_OK)
return error;
bigtime_t fileTime = st.st_mtim.tv_sec * 1000000
+ st.st_mtim.tv_nsec / 1000;
if (fileTime == fFileTime) {
if (_changed != NULL)
*_changed = false;
return B_OK;
}
if (node_ref(st.st_dev, st.st_ino) != fInfo->DefinitionFileNodeRef())
return B_ENTRY_NOT_FOUND;
// read the contents
off_t fileSize = st.st_size;
if (fileSize > (off_t)kMaxVirtualDirectoryFileSize)
return B_BAD_VALUE;
char* buffer = new(std::nothrow) char[fileSize + 1];
if (buffer == NULL)
return B_NO_MEMORY;
ArrayDeleter<char> bufferDeleter(buffer);
ssize_t bytesRead = file.ReadAt(0, buffer, fileSize);
if (bytesRead < 0)
return bytesRead;
buffer[bytesRead] = '\0';
// parse it
BStringList oldDirectoryPaths(fDirectoryPaths);
fDirectoryPaths.MakeEmpty();
BDriverSettings driverSettings;
error = driverSettings.SetToString(buffer);
if (error != B_OK)
return error;
BDriverParameterIterator it
= driverSettings.ParameterIterator("directory");
while (it.HasNext()) {
BDriverParameter parameter = it.Next();
for (int32 i = 0; i < parameter.CountValues(); i++)
fDirectoryPaths.Add(parameter.ValueAt(i));
}
// update file time and check whether something has changed
fFileTime = fileTime;
bool changed = fDirectoryPaths != oldDirectoryPaths;
if (changed || fLastChangeTime < 0)
fLastChangeTime = fFileTime;
if (_changed != NULL)
*_changed = changed;
return B_OK;
}
VirtualDirectoryManager::Info* Info() const
{
return fInfo;
}
private:
typedef std::map<BString, VirtualDirectoryManager::Info*> InfoMap;
private:
BStringList fDirectoryPaths;
VirtualDirectoryManager::Info* fInfo;
bigtime_t fFileTime;
// actual file modified time
bigtime_t fLastChangeTime;
// last time something actually changed
};
// #pragma mark - VirtualDirectoryManager
VirtualDirectoryManager::VirtualDirectoryManager()
:
fLock("virtual directory manager")
{
}
/*static*/ VirtualDirectoryManager*
VirtualDirectoryManager::Instance()
{
static VirtualDirectoryManager* manager
= new(std::nothrow) VirtualDirectoryManager;
return manager;
}
status_t
VirtualDirectoryManager::ResolveDirectoryPaths(
const node_ref& definitionFileNodeRef,
const entry_ref& definitionFileEntryRef, BStringList& _directoryPaths,
node_ref* _definitionFileNodeRef, entry_ref* _definitionFileEntryRef)
{
Info* info = _InfoForNodeRef(definitionFileNodeRef);
if (info == NULL) {
status_t error = _ResolveUnknownDefinitionFile(definitionFileNodeRef,
definitionFileEntryRef, info);
if (error != B_OK)
return error;
}
const BString& subDirectory = info->Path();
const BStringList& rootDirectoryPaths = info->Root()->DirectoryPaths();
if (subDirectory.IsEmpty()) {
_directoryPaths = rootDirectoryPaths;
} else {
_directoryPaths.MakeEmpty();
int32 count = rootDirectoryPaths.CountStrings();
for (int32 i = 0; i < count; i++) {
BString path = rootDirectoryPaths.StringAt(i);
_directoryPaths.Add(path << '/' << subDirectory);
}
}
if (_definitionFileEntryRef != NULL) {
*_definitionFileEntryRef = info->DefinitionFileEntryRef();
if (_definitionFileEntryRef->name == NULL)
return B_NO_MEMORY;
}
if (_definitionFileNodeRef != NULL)
*_definitionFileNodeRef = info->DefinitionFileNodeRef();
return B_OK;
}
bool
VirtualDirectoryManager::GetDefinitionFileChangeTime(
const node_ref& definitionFileRef, bigtime_t& _time) const
{
Info* info = _InfoForNodeRef(definitionFileRef);
if (info == NULL)
return false;
_time = info->Root()->LastChangeTime();
return true;
}
bool
VirtualDirectoryManager::GetRootDefinitionFile(
const node_ref& definitionFileRef, node_ref& _rootDefinitionFileRef)
{
Info* info = _InfoForNodeRef(definitionFileRef);
if (info == NULL)
return false;
_rootDefinitionFileRef = info->Root()->Info()->DefinitionFileNodeRef();
return true;
}
bool
VirtualDirectoryManager::GetSubDirectoryDefinitionFile(
const node_ref& baseDefinitionRef, const char* subDirName,
entry_ref& _entryRef, node_ref& _nodeRef)
{
Info* parentInfo = _InfoForNodeRef(baseDefinitionRef);
if (parentInfo == NULL)
return false;
Info* info = parentInfo->GetChild(subDirName);
if (info == NULL)
return false;
_entryRef = info->DefinitionFileEntryRef();
_nodeRef = info->DefinitionFileNodeRef();
return _entryRef.name != NULL;
}
bool
VirtualDirectoryManager::GetParentDirectoryDefinitionFile(
const node_ref& subDirDefinitionRef, entry_ref& _entryRef,
node_ref& _nodeRef)
{
Info* info = _InfoForNodeRef(subDirDefinitionRef);
if (info == NULL)
return false;
Info* parentInfo = info->Parent();
if (parentInfo == NULL)
return false;
_entryRef = parentInfo->DefinitionFileEntryRef();
_nodeRef = parentInfo->DefinitionFileNodeRef();
return _entryRef.name != NULL;
}
status_t
VirtualDirectoryManager::TranslateDirectoryEntry(
const node_ref& definitionFileRef, dirent* buffer)
{
NotOwningEntryRef entryRef(buffer->d_pdev, buffer->d_pino, buffer->d_name);
node_ref nodeRef(buffer->d_dev, buffer->d_ino);
status_t error = TranslateDirectoryEntry(definitionFileRef, entryRef,
nodeRef);
if (error != B_OK)
return error;
buffer->d_pdev = entryRef.device;
buffer->d_pino = entryRef.directory;
buffer->d_dev = nodeRef.device;
buffer->d_ino = nodeRef.node;
return B_OK;
}
status_t
VirtualDirectoryManager::TranslateDirectoryEntry(
const node_ref& definitionFileRef, entry_ref& _entryRef, node_ref& _nodeRef)
{
Info* parentInfo = _InfoForNodeRef(definitionFileRef);
if (parentInfo == NULL)
return B_BAD_VALUE;
// get the info for the entry
Info* info = parentInfo->GetChild(_entryRef.name);
if (info == NULL) {
// If not done yet, create a directory for the parent, where we can
// place the new definition file.
if (parentInfo->Id().IsEmpty()) {
BString id = BUuid().SetToRandom().ToString();
if (id.IsEmpty())
return B_NO_MEMORY;
BPath path(kTemporaryDefinitionFileBaseDirectoryPath, id);
status_t error = path.InitCheck();
if (error != B_OK)
return error;
error = create_directory(path.Path(),
S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
if (error != B_OK)
return error;
struct stat st;
if (lstat(path.Path(), &st) != 0)
return errno;
parentInfo->SetId(id);
parentInfo->SetChildDefinitionsDirectoryRef(
node_ref(st.st_dev, st.st_ino));
}
// create the definition file
const node_ref& directoryRef
= parentInfo->ChildDefinitionsDirectoryRef();
NotOwningEntryRef entryRef(directoryRef, _entryRef.name);
BFile definitionFile;
status_t error = definitionFile.SetTo(&entryRef,
B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
if (error != B_OK)
return error;
node_ref nodeRef;
error = definitionFile.GetNodeRef(&nodeRef);
if (error != B_OK)
return error;
BNodeInfo nodeInfo(&definitionFile);
error = nodeInfo.SetType(kVirtualDirectoryMimeType);
if (error != B_OK)
return error;
// create the info
info = parentInfo->CreateChild(nodeRef, entryRef);
if (info == NULL || !_AddInfo(info))
return B_NO_MEMORY;
// Write some info into the definition file that helps us to find the
// root definition file. This is only necessary when definition file
// entry refs are transferred between applications. Then the receiving
// application may need to find the root definition file and resolve
// the subdirectories.
const entry_ref& rootEntryRef
= parentInfo->Root()->Info()->DefinitionFileEntryRef();
BString definitionFileContent;
definitionFileContent.SetToFormat(
"root {\n"
" device %" B_PRIdDEV "\n"
" directory %" B_PRIdINO "\n"
" name \"%s\"\n"
"}\n"
"subdir \"%s\"\n",
rootEntryRef.device, rootEntryRef.directory, rootEntryRef.name,
info->Path().String());
// failure is not nice, but not mission critical for this application
if (!definitionFileContent.IsEmpty()) {
definitionFile.WriteAt(0,
definitionFileContent.String(), definitionFileContent.Length());
}
}
const entry_ref& entryRef = info->DefinitionFileEntryRef();
_nodeRef = info->DefinitionFileNodeRef();
_entryRef.device = entryRef.device;
_entryRef.directory = entryRef.directory;
return B_OK;
}
bool
VirtualDirectoryManager::DefinitionFileChanged(
const node_ref& definitionFileRef)
{
Info* info = _InfoForNodeRef(definitionFileRef);
if (info == NULL)
return false;
_UpdateTree(info->Root());
return _InfoForNodeRef(definitionFileRef) != NULL;
}
status_t
VirtualDirectoryManager::DirectoryRemoved(const node_ref& definitionFileRef)
{
Info* info = _InfoForNodeRef(definitionFileRef);
if (info == NULL)
return B_ENTRY_NOT_FOUND;
_RemoveDirectory(info);
// delete the info
if (info->Parent() == NULL)
delete info->Root();
else
info->Parent()->DeleteChild(info);
return B_OK;
}
/*static*/ bool
VirtualDirectoryManager::GetEntry(const BStringList& directoryPaths,
const char* name, entry_ref* _ref, struct stat* _st)
{
int32 count = directoryPaths.CountStrings();
for (int32 i = 0; i < count; i++) {
BPath path;
if (path.SetTo(directoryPaths.StringAt(i), name) != B_OK)
continue;
struct stat st;
if (lstat(path.Path(), &st) == 0) {
if (_ref != NULL) {
if (get_ref_for_path(path.Path(), _ref) != B_OK)
return false;
}
if (_st != NULL)
*_st = st;
return true;
}
}
return false;
}
VirtualDirectoryManager::Info*
VirtualDirectoryManager::_InfoForNodeRef(const node_ref& nodeRef) const
{
NodeRefInfoMap::const_iterator it = fInfos.find(nodeRef);
return it != fInfos.end() ? it->second : NULL;
}
bool
VirtualDirectoryManager::_AddInfo(Info* info)
{
try {
fInfos[info->DefinitionFileNodeRef()] = info;
return true;
} catch (...) {
return false;
}
}
void
VirtualDirectoryManager::_RemoveInfo(Info* info)
{
NodeRefInfoMap::iterator it = fInfos.find(info->DefinitionFileNodeRef());
if (it != fInfos.end())
fInfos.erase(it);
}
void
VirtualDirectoryManager::_UpdateTree(RootInfo* root)
{
bool changed = false;
status_t error = root->ReadDefinition(&changed);
if (error != B_OK) {
DirectoryRemoved(root->Info()->DefinitionFileNodeRef());
return;
}
if (!changed)
return;
_UpdateTree(root->Info());
}
void
VirtualDirectoryManager::_UpdateTree(Info* info)
{
const BStringList& directoryPaths = info->Root()->DirectoryPaths();
int32 childCount = info->Children().CountItems();
for (int32 i = childCount -1; i >= 0; i--) {
Info* childInfo = info->Children().ItemAt(i);
struct stat st;
if (GetEntry(directoryPaths, childInfo->Path(), NULL, &st)
&& S_ISDIR(st.st_mode)) {
_UpdateTree(childInfo);
} else {
_RemoveDirectory(childInfo);
info->DeleteChildAt(i);
}
}
}
void
VirtualDirectoryManager::_RemoveDirectory(Info* info)
{
// recursively remove the subdirectories
for (int32 i = 0; Info* child = info->Children().ItemAt(i); i++)
_RemoveDirectory(info);
// remove the directory for the child definition file
if (!info->Id().IsEmpty()) {
BPath path(kTemporaryDefinitionFileBaseDirectoryPath, info->Id());
if (path.InitCheck() == B_OK)
rmdir(path.Path());
}
// unless this is the root directory, remove the definition file
if (info != info->Root()->Info())
BEntry(&info->DefinitionFileEntryRef()).Remove();
_RemoveInfo(info);
}
status_t
VirtualDirectoryManager::_ResolveUnknownDefinitionFile(
const node_ref& definitionFileNodeRef,
const entry_ref& definitionFileEntryRef, Info*& _info)
{
// This is either a root definition file or a subdir definition file
// created by another application. We'll just try to read the info from the
// file that a subdir definition file would contain. If that fails, we
// assume a root definition file.
entry_ref entryRef;
BString subDirPath;
if (_ReadSubDirectoryDefinitionFileInfo(definitionFileEntryRef, entryRef,
subDirPath) != B_OK) {
return _CreateRootInfo(definitionFileNodeRef, definitionFileEntryRef,
_info);
}
if (subDirPath.IsEmpty())
return B_BAD_VALUE;
// get the root definition file node ref
node_ref nodeRef;
status_t error = BEntry(&entryRef).GetNodeRef(&nodeRef);
if (error != B_OK)
return error;
// resolve/create the root info
Info* info = _InfoForNodeRef(nodeRef);
if (info == NULL) {
error = _CreateRootInfo(nodeRef, entryRef, info);
if (error != B_OK)
return error;
} else if (info->Root()->Info() != info)
return B_BAD_VALUE;
const BStringList& rootDirectoryPaths = info->Root()->DirectoryPaths();
// now we can traverse the subdir path and resolve all infos along the way
int32 nextComponentOffset = 0;
while (nextComponentOffset < subDirPath.Length()) {
int32 componentEnd = subDirPath.FindFirst('/', nextComponentOffset);
if (componentEnd >= 0) {
// skip duplicate '/'s
if (componentEnd == nextComponentOffset + 1) {
nextComponentOffset = componentEnd;
continue;
}
nextComponentOffset = componentEnd + 1;
} else {
componentEnd = subDirPath.Length();
nextComponentOffset = componentEnd;
}
BString entryPath(subDirPath, componentEnd);
if (entryPath.IsEmpty())
return B_NO_MEMORY;
struct stat st;
if (!GetEntry(rootDirectoryPaths, entryPath, &entryRef, &st))
return B_ENTRY_NOT_FOUND;
if (!S_ISDIR(st.st_mode))
return B_BAD_VALUE;
error = TranslateDirectoryEntry(info->DefinitionFileNodeRef(), entryRef,
nodeRef);
if (error != B_OK)
return error;
info = _InfoForNodeRef(nodeRef);
}
_info = info;
return B_OK;
}
status_t
VirtualDirectoryManager::_CreateRootInfo(const node_ref& definitionFileNodeRef,
const entry_ref& definitionFileEntryRef, Info*& _info)
{
RootInfo* root = new(std::nothrow) RootInfo(definitionFileNodeRef,
definitionFileEntryRef);
if (root == NULL || root->InitCheck() != B_OK) {
delete root;
return B_NO_MEMORY;
}
ObjectDeleter<RootInfo> rootDeleter(root);
status_t error = root->ReadDefinition();
if (error != B_OK)
return error;
if (!_AddInfo(root->Info()))
return B_NO_MEMORY;
rootDeleter.Detach();
_info = root->Info();
return B_OK;
}
status_t
VirtualDirectoryManager::_ReadSubDirectoryDefinitionFileInfo(
const entry_ref& entryRef, entry_ref& _rootDefinitionFileEntryRef,
BString& _subDirPath)
{
BDriverSettings driverSettings;
status_t error = driverSettings.Load(entryRef);
if (error != B_OK)
return error;
const char* subDirPath = driverSettings.GetParameterValue("subdir");
if (subDirPath == NULL || subDirPath[0] == '\0')
return B_BAD_DATA;
BDriverParameter rootParameter;
if (!driverSettings.FindParameter("root", &rootParameter))
return B_BAD_DATA;
const char* name = rootParameter.GetParameterValue("name");
dev_t device = rootParameter.GetInt32ParameterValue("device", -1, -1);
ino_t directory = rootParameter.GetInt64ParameterValue("directory");
if (name == NULL || name[0] == '\0' || device < 0)
return B_BAD_DATA;
_rootDefinitionFileEntryRef = entry_ref(device, directory, name);
_subDirPath = subDirPath;
return !_subDirPath.IsEmpty() && _rootDefinitionFileEntryRef.name != NULL
? B_OK : B_NO_MEMORY;
}
} // namespace BPrivate

View File

@ -0,0 +1,117 @@
/*
* Copyright 2013, Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold, ingo_weinhold@gmx.de
*/
#ifndef VIRTUAL_DIRECTORY_MANAGER_H
#define VIRTUAL_DIRECTORY_MANAGER_H
#include <dirent.h>
#include <map>
#include <Locker.h>
#include <Node.h>
class BStringList;
namespace BPrivate {
class Model;
class VirtualDirectoryManager {
public:
static VirtualDirectoryManager* Instance();
bool Lock() { return fLock.Lock(); }
void Unlock() { fLock.Unlock(); }
status_t ResolveDirectoryPaths(
const node_ref& definitionFileNodeRef,
const entry_ref& definitionFileEntryRef,
BStringList& _directoryPaths,
node_ref* _definitionFileNodeRef = NULL,
entry_ref* _definitionFileEntryRef = NULL);
bool GetDefinitionFileChangeTime(
const node_ref& definitionFileRef,
bigtime_t& _time) const;
bool GetRootDefinitionFile(
const node_ref& definitionFileRef,
node_ref& _rootDefinitionFileRef);
bool GetSubDirectoryDefinitionFile(
const node_ref& baseDefinitionRef,
const char* subDirName,
entry_ref& _entryRef, node_ref& _nodeRef);
bool GetParentDirectoryDefinitionFile(
const node_ref& subDirDefinitionRef,
entry_ref& _entryRef, node_ref& _nodeRef);
status_t TranslateDirectoryEntry(
const node_ref& definitionFileRef,
dirent* buffer);
status_t TranslateDirectoryEntry(
const node_ref& definitionFileRef,
entry_ref& entryRef, node_ref& _nodeRef);
bool DefinitionFileChanged(
const node_ref& definitionFileRef);
// returns whether the directory still
// exists
status_t DirectoryRemoved(
const node_ref& definitionFileRef);
static bool GetEntry(const BStringList& directoryPaths,
const char* name, entry_ref* _ref,
struct stat* _st);
private:
class Info;
class RootInfo;
typedef std::map<node_ref, Info*> NodeRefInfoMap;
private:
VirtualDirectoryManager();
Info* _InfoForNodeRef(const node_ref& nodeRef) const;
bool _AddInfo(Info* info);
void _RemoveInfo(Info* info);
void _UpdateTree(RootInfo* root);
void _UpdateTree(Info* info);
void _RemoveDirectory(Info* info);
status_t _ResolveUnknownDefinitionFile(
const node_ref& definitionFileNodeRef,
const entry_ref& definitionFileEntryRef,
Info*& _info);
status_t _CreateRootInfo(
const node_ref& definitionFileNodeRef,
const entry_ref& definitionFileEntryRef,
Info*& _info);
status_t _ReadSubDirectoryDefinitionFileInfo(
const entry_ref& entryRef,
entry_ref& _rootDefinitionFileEntryRef,
BString& _subDirPath);
private:
BLocker fLock;
NodeRefInfoMap fInfos;
};
} // namespace BPrivate
#endif // VIRTUAL_DIRECTORY_MANAGER_H

View File

@ -0,0 +1,502 @@
/*
* Copyright 2013, Haiku, Inc.
* 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, BRect frame,
uint32 resizeMask)
:
BPoseView(model, frame, resizeMask),
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();
SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
}
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

View File

@ -0,0 +1,77 @@
/*
* Copyright 2013, Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold, ingo_weinhold@gmx.de
*/
#ifndef VIRTUAL_DIRECTORY_POSE_VIEW_H
#define VIRTUAL_DIRECTORY_POSE_VIEW_H
#include <StringList.h>
#include "PoseView.h"
namespace BPrivate {
class VirtualDirectoryEntryList;
class VirtualDirectoryPoseView : public BPoseView {
public:
VirtualDirectoryPoseView(Model* model,
BRect frame,
uint32 resizeMask = B_FOLLOW_ALL);
virtual ~VirtualDirectoryPoseView();
virtual void MessageReceived(BMessage* message);
protected:
virtual void AttachedToWindow();
virtual void RestoreState(AttributeStreamNode* node);
virtual void RestoreState(const BMessage& message);
virtual void SavePoseLocations(BRect* frameIfDesktop = NULL);
virtual void SetViewMode(uint32 newMode);
virtual EntryListBase* InitDirentIterator(const entry_ref* ref);
virtual void StartWatching();
virtual void StopWatching();
virtual bool FSNotification(const BMessage* message);
private:
typedef BPoseView _inherited;
private:
bool _EntryCreated(const BMessage* message);
bool _EntryRemoved(const BMessage* message);
bool _EntryMoved(const BMessage* message);
bool _NodeStatChanged(const BMessage* message);
void _DispatchEntryCreatedOrRemovedMessage(
int32 opcode, const node_ref& nodeRef,
const entry_ref& entryRef,
const char* path = NULL,
bool dispatchToSuperClass = true);
bool _GetEntry(const char* name, entry_ref& _ref,
struct stat* _st = NULL);
status_t _UpdateDirectoryPaths();
// manager must be locked
private:
BStringList fDirectoryPaths;
node_ref fRootDefinitionFileRef;
bigtime_t fFileChangeTime;
bool fIsRoot;
};
} // namespace BPrivate
#endif // VIRTUAL_DIRECTORY_POSE_VIEW_H

View File

@ -0,0 +1,179 @@
/*
* Copyright 2013, Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold, ingo_weinhold@gmx.de
*/
/*
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 "VirtualDirectoryWindow.h"
#include <Catalog.h>
#include <Locale.h>
#include <AutoLocker.h>
#include "Commands.h"
#include "VirtualDirectoryManager.h"
#include "VirtualDirectoryPoseView.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "VirtualDirectoryWindow"
namespace BPrivate {
VirtualDirectoryWindow::VirtualDirectoryWindow(LockingList<BWindow>* windowList,
uint32 containerWindowFlags, window_look look, window_feel feel,
uint32 flags, uint32 workspace)
:
BContainerWindow(windowList, containerWindowFlags, look, feel, flags,
workspace)
{
}
void
VirtualDirectoryWindow::CreatePoseView(Model* model)
{
BRect rect(Bounds());
rect.right -= B_V_SCROLL_BAR_WIDTH;
rect.bottom -= B_H_SCROLL_BAR_HEIGHT;
fPoseView = NewPoseView(model, rect, kListMode);
AddChild(fPoseView);
}
BPoseView*
VirtualDirectoryWindow::NewPoseView(Model* model, BRect rect, uint32 viewMode)
{
// If the model (or rather the entry_ref to it) came from another
// application, it may refer to a subdirectory we cannot use directly. The
// manager resolves the given refs to new ones, if necessary.
VirtualDirectoryManager* manager = VirtualDirectoryManager::Instance();
if (manager == NULL)
return NULL;
{
AutoLocker<VirtualDirectoryManager> managerLocker(manager);
BStringList directoryPaths;
node_ref nodeRef;
entry_ref entryRef;
if (manager->ResolveDirectoryPaths(*model->NodeRef(),
*model->EntryRef(), directoryPaths, &nodeRef, &entryRef)
!= B_OK) {
return NULL;
}
if (nodeRef != *model->NodeRef()) {
// Indeed a new file. Create a new model.
Model* newModel = new(std::nothrow) Model(&entryRef);
if (newModel == NULL || newModel->InitCheck() != B_OK) {
delete newModel;
return NULL;
}
delete model;
model = newModel;
}
}
return new VirtualDirectoryPoseView(model, rect);
}
void
VirtualDirectoryWindow::AddWindowMenu(BMenu* menu)
{
BMenuItem* item;
item = new BMenuItem(B_TRANSLATE("Resize to fit"),
new BMessage(kResizeToFit), 'Y');
item->SetTarget(this);
menu->AddItem(item);
item = new BMenuItem(B_TRANSLATE("Select"B_UTF8_ELLIPSIS),
new BMessage(kShowSelectionWindow), 'A', B_SHIFT_KEY);
item->SetTarget(PoseView());
menu->AddItem(item);
item = new BMenuItem(B_TRANSLATE("Select all"),
new BMessage(B_SELECT_ALL), 'A');
item->SetTarget(PoseView());
menu->AddItem(item);
item = new BMenuItem(B_TRANSLATE("Invert selection"),
new BMessage(kInvertSelection), 'S');
item->SetTarget(PoseView());
menu->AddItem(item);
item = new BMenuItem(B_TRANSLATE("Open parent"),
new BMessage(kOpenParentDir), B_UP_ARROW);
item->SetTarget(PoseView());
menu->AddItem(item);
item = new BMenuItem(B_TRANSLATE("Close"),
new BMessage(B_QUIT_REQUESTED), 'W');
item->SetTarget(this);
menu->AddItem(item);
}
void
VirtualDirectoryWindow::AddWindowContextMenus(BMenu* menu)
{
BMenuItem* resizeItem = new BMenuItem(B_TRANSLATE("Resize to fit"),
new BMessage(kResizeToFit), 'Y');
menu->AddItem(resizeItem);
menu->AddItem(new BMenuItem(B_TRANSLATE("Select"B_UTF8_ELLIPSIS),
new BMessage(kShowSelectionWindow), 'A', B_SHIFT_KEY));
menu->AddItem(new BMenuItem(B_TRANSLATE("Select all"),
new BMessage(B_SELECT_ALL), 'A'));
BMenuItem* closeItem = new BMenuItem(B_TRANSLATE("Close"),
new BMessage(B_QUIT_REQUESTED), 'W');
menu->AddItem(closeItem);
// target items as needed
menu->SetTargetForItems(PoseView());
closeItem->SetTarget(this);
resizeItem->SetTarget(this);
}
} // namespace BPrivate

View File

@ -0,0 +1,44 @@
/*
* Copyright 2013, Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold, ingo_weinhold@gmx.de
*/
#ifndef VIRTUAL_DIRECTORY_WINDOW_H
#define VIRTUAL_DIRECTORY_WINDOW_H
#include "ContainerWindow.h"
namespace BPrivate {
class VirtualDirectoryWindow : public BContainerWindow {
public:
VirtualDirectoryWindow(
LockingList<BWindow>* windowList,
uint32 containerWindowFlags,
window_look look = B_DOCUMENT_WINDOW_LOOK,
window_feel feel = B_NORMAL_WINDOW_FEEL,
uint32 flags = B_WILL_ACCEPT_FIRST_CLICK
| B_NO_WORKSPACE_ACTIVATION,
uint32 workspace = B_CURRENT_WORKSPACE);
protected:
virtual void CreatePoseView(Model* model);
virtual BPoseView* NewPoseView(Model* model, BRect rect,
uint32 viewMode);
virtual void AddWindowMenu(BMenu* menu);
virtual void AddWindowContextMenus(BMenu* menu);
private:
typedef BContainerWindow _inherited;
};
} // namespace BPrivate
#endif // VIRTUAL_DIRECTORY_WINDOW_H

View File

@ -1104,7 +1104,8 @@ SizeAttributeText::ReadValue()
}
if (fModel->IsDirectory() || fModel->IsQuery()
|| fModel->IsQueryTemplate() || fModel->IsSymLink())
|| fModel->IsQueryTemplate() || fModel->IsSymLink()
|| fModel->IsVirtualDirectory())
return kUnknownSize;
fValueIsDefined = true;