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:
Binary file not shown.
@@ -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"
|
||||||
|
};
|
||||||
|
|
||||||
@@ -1507,7 +1507,8 @@ BContainerWindow::MessageReceived(BMessage* message)
|
|||||||
PoseView()->MoveSelectionInto(&model, this, false, false,
|
PoseView()->MoveSelectionInto(&model, this, false, false,
|
||||||
message->what == kCreateLink,
|
message->what == kCreateLink,
|
||||||
message->what == kCreateRelativeLink);
|
message->what == kCreateRelativeLink);
|
||||||
} else if (!TargetModel()->IsQuery()) {
|
} else if (!TargetModel()->IsQuery()
|
||||||
|
&& !TargetModel()->IsVirtualDirectory()) {
|
||||||
// no destination specified, create link in same dir as item
|
// no destination specified, create link in same dir as item
|
||||||
PoseView()->MoveSelectionInto(TargetModel(), this, false, false,
|
PoseView()->MoveSelectionInto(TargetModel(), this, false, false,
|
||||||
message->what == kCreateLink,
|
message->what == kCreateLink,
|
||||||
@@ -1895,7 +1896,8 @@ BContainerWindow::AddFileMenu(BMenu* menu)
|
|||||||
new BMessage(kFindButton), 'F'));
|
new BMessage(kFindButton), 'F'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TargetModel()->IsQuery() && !IsTrash() && !IsPrintersDir()) {
|
if (!TargetModel()->IsQuery() && !TargetModel()->IsVirtualDirectory()
|
||||||
|
&& !IsTrash() && !IsPrintersDir()) {
|
||||||
if (!PoseView()->IsFilePanel()) {
|
if (!PoseView()->IsFilePanel()) {
|
||||||
TemplatesMenu* templateMenu = new TemplatesMenu(PoseView(),
|
TemplatesMenu* templateMenu = new TemplatesMenu(PoseView(),
|
||||||
B_TRANSLATE("New"));
|
B_TRANSLATE("New"));
|
||||||
@@ -2108,6 +2110,7 @@ BContainerWindow::AddShortcuts()
|
|||||||
ASSERT(!IsTrash());
|
ASSERT(!IsTrash());
|
||||||
ASSERT(!PoseView()->IsFilePanel());
|
ASSERT(!PoseView()->IsFilePanel());
|
||||||
ASSERT(!TargetModel()->IsQuery());
|
ASSERT(!TargetModel()->IsQuery());
|
||||||
|
ASSERT(!TargetModel()->IsVirtualDirectory());
|
||||||
|
|
||||||
AddShortcut('X', B_COMMAND_KEY | B_SHIFT_KEY,
|
AddShortcut('X', B_COMMAND_KEY | B_SHIFT_KEY,
|
||||||
new BMessage(kCutMoreSelectionToClipboard), this);
|
new BMessage(kCutMoreSelectionToClipboard), this);
|
||||||
@@ -3772,8 +3775,9 @@ BContainerWindow::SetUpDefaultState()
|
|||||||
// try copying state from our parent directory, unless it is the
|
// try copying state from our parent directory, unless it is the
|
||||||
// desktop folder
|
// desktop folder
|
||||||
BEntry entry(TargetModel()->EntryRef());
|
BEntry entry(TargetModel()->EntryRef());
|
||||||
BDirectory parent;
|
BNode parent;
|
||||||
if (entry.GetParent(&parent) == B_OK && parent != desktop) {
|
if (FSGetParentVirtualDirectoryAware(entry, parent) == B_OK
|
||||||
|
&& parent != desktop) {
|
||||||
PRINT(("looking at parent for state\n"));
|
PRINT(("looking at parent for state\n"));
|
||||||
if (NodeHasSavedState(&parent)) {
|
if (NodeHasSavedState(&parent)) {
|
||||||
PRINT(("got state from parent\n"));
|
PRINT(("got state from parent\n"));
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ DesktopPoseView::InitDesktopDirentIterator(BPoseView* nodeMonitoringTarget,
|
|||||||
CachedEntryIteratorList* result = new CachedEntryIteratorList();
|
CachedEntryIteratorList* result = new CachedEntryIteratorList();
|
||||||
|
|
||||||
ASSERT(!sourceModel.IsQuery());
|
ASSERT(!sourceModel.IsQuery());
|
||||||
|
ASSERT(!sourceModel.IsVirtualDirectory());
|
||||||
ASSERT(sourceModel.Node());
|
ASSERT(sourceModel.Node());
|
||||||
BDirectory* sourceDirectory
|
BDirectory* sourceDirectory
|
||||||
= dynamic_cast<BDirectory*>(sourceModel.Node());
|
= dynamic_cast<BDirectory*>(sourceModel.Node());
|
||||||
|
|||||||
@@ -115,10 +115,9 @@ BDirMenu::Populate(const BEntry* startEntry, BWindow* originatingWindow,
|
|||||||
// if we're at the root directory skip "mnt" and
|
// if we're at the root directory skip "mnt" and
|
||||||
// go straight to "/"
|
// go straight to "/"
|
||||||
parent.SetTo("/");
|
parent.SetTo("/");
|
||||||
|
parent.GetEntry(&entry);
|
||||||
} else
|
} else
|
||||||
entry.GetParent(&parent);
|
FSGetParentVirtualDirectoryAware(entry, entry);
|
||||||
|
|
||||||
parent.GetEntry(&entry);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BDirectory desktopDir;
|
BDirectory desktopDir;
|
||||||
@@ -135,9 +134,7 @@ BDirMenu::Populate(const BEntry* startEntry, BWindow* originatingWindow,
|
|||||||
kAttrPoseInfoForeign, B_RAW_TYPE, 0, &info, sizeof(PoseInfo),
|
kAttrPoseInfoForeign, B_RAW_TYPE, 0, &info, sizeof(PoseInfo),
|
||||||
&PoseInfo::EndianSwap);
|
&PoseInfo::EndianSwap);
|
||||||
|
|
||||||
BDirectory parent;
|
BEntry parentEntry;
|
||||||
entry.GetParent(&parent);
|
|
||||||
|
|
||||||
bool hitRoot = false;
|
bool hitRoot = false;
|
||||||
|
|
||||||
BDirectory dir(&entry);
|
BDirectory dir(&entry);
|
||||||
@@ -146,8 +143,9 @@ BDirMenu::Populate(const BEntry* startEntry, BWindow* originatingWindow,
|
|||||||
// if we're at the root directory skip "mnt" and
|
// if we're at the root directory skip "mnt" and
|
||||||
// go straight to "/"
|
// go straight to "/"
|
||||||
hitRoot = true;
|
hitRoot = true;
|
||||||
parent.SetTo("/");
|
parentEntry.SetTo("/");
|
||||||
}
|
} else
|
||||||
|
FSGetParentVirtualDirectoryAware(entry, parentEntry);
|
||||||
|
|
||||||
if (showDesktop) {
|
if (showDesktop) {
|
||||||
BEntry root("/");
|
BEntry root("/");
|
||||||
@@ -174,7 +172,9 @@ BDirMenu::Populate(const BEntry* startEntry, BWindow* originatingWindow,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
parent.GetEntry(&entry);
|
entry = parentEntry;
|
||||||
|
if (entry.InitCheck() != B_OK)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// select last item in menu
|
// select last item in menu
|
||||||
|
|||||||
@@ -71,6 +71,8 @@ respective holders. All rights reserved.
|
|||||||
#include <fs_info.h>
|
#include <fs_info.h>
|
||||||
#include <sys/utsname.h>
|
#include <sys/utsname.h>
|
||||||
|
|
||||||
|
#include <AutoLocker.h>
|
||||||
|
|
||||||
#include "Attributes.h"
|
#include "Attributes.h"
|
||||||
#include "Bitmaps.h"
|
#include "Bitmaps.h"
|
||||||
#include "Commands.h"
|
#include "Commands.h"
|
||||||
@@ -85,6 +87,7 @@ respective holders. All rights reserved.
|
|||||||
#include "Tracker.h"
|
#include "Tracker.h"
|
||||||
#include "TrackerSettings.h"
|
#include "TrackerSettings.h"
|
||||||
#include "Utilities.h"
|
#include "Utilities.h"
|
||||||
|
#include "VirtualDirectoryManager.h"
|
||||||
|
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
@@ -3157,6 +3160,66 @@ GetAttrInfo(const BNode* node, const char* hostAttrName,
|
|||||||
return kReadAttrFailed;
|
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
|
// launching code
|
||||||
|
|
||||||
static status_t
|
static status_t
|
||||||
|
|||||||
@@ -238,6 +238,10 @@ status_t FSRecursiveCreateFolder(const char* path);
|
|||||||
void FSMakeOriginalName(BString &name, const BDirectory* destDir,
|
void FSMakeOriginalName(BString &name, const BDirectory* destDir,
|
||||||
const char* suffix = 0);
|
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 entry_ref* app, bool async);
|
||||||
status_t TrackerLaunch(const BMessage* refs, bool async,
|
status_t TrackerLaunch(const BMessage* refs, bool async,
|
||||||
bool okToRunOpenWith = true);
|
bool okToRunOpenWith = true);
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ All rights reserved.
|
|||||||
#include "QueryPoseView.h"
|
#include "QueryPoseView.h"
|
||||||
#include "Tracker.h"
|
#include "Tracker.h"
|
||||||
#include "Utilities.h"
|
#include "Utilities.h"
|
||||||
|
#include "VirtualDirectoryEntryList.h"
|
||||||
|
|
||||||
|
|
||||||
#undef B_TRANSLATION_CONTEXT
|
#undef B_TRANSLATION_CONTEXT
|
||||||
@@ -138,6 +139,8 @@ FavoritesMenu::AddNextItem()
|
|||||||
|
|
||||||
if (startModel.IsQuery())
|
if (startModel.IsQuery())
|
||||||
fContainer = new QueryEntryListCollection(&startModel);
|
fContainer = new QueryEntryListCollection(&startModel);
|
||||||
|
else if (startModel.IsVirtualDirectory())
|
||||||
|
fContainer = new VirtualDirectoryEntryList(&startModel);
|
||||||
else
|
else
|
||||||
fContainer = new DirectoryEntryList(*dynamic_cast<BDirectory*>
|
fContainer = new DirectoryEntryList(*dynamic_cast<BDirectory*>
|
||||||
(startModel.Node()));
|
(startModel.Node()));
|
||||||
|
|||||||
@@ -86,6 +86,10 @@ SharedLibrary libtracker.so :
|
|||||||
TrashWatcher.cpp
|
TrashWatcher.cpp
|
||||||
Utilities.cpp
|
Utilities.cpp
|
||||||
ViewState.cpp
|
ViewState.cpp
|
||||||
|
VirtualDirectoryEntryList.cpp
|
||||||
|
VirtualDirectoryManager.cpp
|
||||||
|
VirtualDirectoryPoseView.cpp
|
||||||
|
VirtualDirectoryWindow.cpp
|
||||||
VolumeWindow.cpp
|
VolumeWindow.cpp
|
||||||
WidgetAttributeText.cpp
|
WidgetAttributeText.cpp
|
||||||
|
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ namespace BPrivate {
|
|||||||
#define B_PRINTER_SPOOL_MIMETYPE "application/x-vnd.Be.printer-spool"
|
#define B_PRINTER_SPOOL_MIMETYPE "application/x-vnd.Be.printer-spool"
|
||||||
|
|
||||||
#define kPlainTextMimeType "text/plain"
|
#define kPlainTextMimeType "text/plain"
|
||||||
|
#define kVirtualDirectoryMimeType "application/x-vnd.haiku-virtual-directory"
|
||||||
|
|
||||||
#define kBitmapMimeType "image/x-vnd.Be-bitmap"
|
#define kBitmapMimeType "image/x-vnd.Be-bitmap"
|
||||||
#define kLargeIconType "icon/large"
|
#define kLargeIconType "icon/large"
|
||||||
|
|||||||
@@ -325,9 +325,11 @@ Model::CompareFolderNamesFirst(const Model* compareModel) const
|
|||||||
const Model* resolvedCompareModel = compareModel->ResolveIfLink();
|
const Model* resolvedCompareModel = compareModel->ResolveIfLink();
|
||||||
const Model* resolvedMe = ResolveIfLink();
|
const Model* resolvedMe = ResolveIfLink();
|
||||||
|
|
||||||
bool meIsDirOrVolume = resolvedMe->IsDirectory() || resolvedMe->IsVolume();
|
bool meIsDirOrVolume = resolvedMe->IsDirectory() || resolvedMe->IsVolume()
|
||||||
|
|| resolvedMe->IsVirtualDirectory();
|
||||||
bool otherIsDirOrVolume = resolvedCompareModel->IsDirectory()
|
bool otherIsDirOrVolume = resolvedCompareModel->IsDirectory()
|
||||||
|| resolvedCompareModel->IsVolume();
|
|| resolvedCompareModel->IsVolume()
|
||||||
|
|| resolvedCompareModel->IsVirtualDirectory();
|
||||||
|
|
||||||
if (meIsDirOrVolume) {
|
if (meIsDirOrVolume) {
|
||||||
if (!otherIsDirOrVolume)
|
if (!otherIsDirOrVolume)
|
||||||
@@ -426,6 +428,7 @@ Model::OpenNodeCommon(bool writable)
|
|||||||
case kExecutableNode:
|
case kExecutableNode:
|
||||||
case kQueryNode:
|
case kQueryNode:
|
||||||
case kQueryTemplateNode:
|
case kQueryTemplateNode:
|
||||||
|
case kVirtualDirectoryNode:
|
||||||
// open or reopen
|
// open or reopen
|
||||||
delete fNode;
|
delete fNode;
|
||||||
fNode = new BFile(&fEntryRef,
|
fNode = new BFile(&fEntryRef,
|
||||||
@@ -629,6 +632,8 @@ Model::FinishSettingUpType()
|
|||||||
fBaseType = kQueryNode;
|
fBaseType = kQueryNode;
|
||||||
else if (strcmp(mimeString, B_QUERY_TEMPLATE_MIMETYPE) == 0)
|
else if (strcmp(mimeString, B_QUERY_TEMPLATE_MIMETYPE) == 0)
|
||||||
fBaseType = kQueryTemplateNode;
|
fBaseType = kQueryTemplateNode;
|
||||||
|
else if (strcmp(mimeString, kVirtualDirectoryMimeType) == 0)
|
||||||
|
fBaseType = kVirtualDirectoryNode;
|
||||||
|
|
||||||
if (info.GetPreferredApp(mimeString) == B_OK) {
|
if (info.GetPreferredApp(mimeString) == B_OK) {
|
||||||
if (fPreferredAppName)
|
if (fPreferredAppName)
|
||||||
@@ -1332,6 +1337,10 @@ Model::PrintToStream(int32 level, bool deep)
|
|||||||
PRINT(("volume, name %s\n", fVolumeName ? fVolumeName : ""));
|
PRINT(("volume, name %s\n", fVolumeName ? fVolumeName : ""));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case kVirtualDirectoryNode:
|
||||||
|
PRINT(("virtual directory\n"));
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
PRINT(("unknown\n"));
|
PRINT(("unknown\n"));
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -141,6 +141,7 @@ class Model {
|
|||||||
bool IsTrash() const;
|
bool IsTrash() const;
|
||||||
bool IsDesktop() const;
|
bool IsDesktop() const;
|
||||||
bool IsVolume() const;
|
bool IsVolume() const;
|
||||||
|
bool IsVirtualDirectory() const;
|
||||||
|
|
||||||
IconSource IconFrom() const;
|
IconSource IconFrom() const;
|
||||||
void SetIconFrom(IconSource);
|
void SetIconFrom(IconSource);
|
||||||
@@ -239,6 +240,7 @@ class Model {
|
|||||||
kRootNode,
|
kRootNode,
|
||||||
kTrashNode,
|
kTrashNode,
|
||||||
kDesktopNode,
|
kDesktopNode,
|
||||||
|
kVirtualDirectoryNode,
|
||||||
kUnknownNode
|
kUnknownNode
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -381,7 +383,8 @@ Model::IsFile() const
|
|||||||
return fBaseType == kPlainNode
|
return fBaseType == kPlainNode
|
||||||
|| fBaseType == kQueryNode
|
|| fBaseType == kQueryNode
|
||||||
|| fBaseType == kQueryTemplateNode
|
|| fBaseType == kQueryTemplateNode
|
||||||
|| fBaseType == kExecutableNode;
|
|| fBaseType == kExecutableNode
|
||||||
|
|| fBaseType == kVirtualDirectoryNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -422,7 +425,7 @@ Model::IsContainer() const
|
|||||||
{
|
{
|
||||||
// I guess as in should show container window -
|
// I guess as in should show container window -
|
||||||
// volumes show the volume 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
|
inline bool
|
||||||
Model::HasLocalizedName() const
|
Model::HasLocalizedName() const
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ their respective holders. All rights reserved.
|
|||||||
#include "QueryPoseView.h"
|
#include "QueryPoseView.h"
|
||||||
#include "Thread.h"
|
#include "Thread.h"
|
||||||
#include "Tracker.h"
|
#include "Tracker.h"
|
||||||
|
#include "VirtualDirectoryEntryList.h"
|
||||||
|
|
||||||
|
|
||||||
namespace BPrivate {
|
namespace BPrivate {
|
||||||
@@ -420,6 +421,8 @@ BNavMenu::StartBuildingItemList()
|
|||||||
|
|
||||||
if (startModel.IsQuery())
|
if (startModel.IsQuery())
|
||||||
fContainer = new QueryEntryListCollection(&startModel);
|
fContainer = new QueryEntryListCollection(&startModel);
|
||||||
|
else if (startModel.IsVirtualDirectory())
|
||||||
|
fContainer = new VirtualDirectoryEntryList(&startModel);
|
||||||
else if (startModel.IsDesktop()) {
|
else if (startModel.IsDesktop()) {
|
||||||
fIteratingDesktop = true;
|
fIteratingDesktop = true;
|
||||||
fContainer = DesktopPoseView::InitDesktopDirentIterator(
|
fContainer = DesktopPoseView::InitDesktopDirentIterator(
|
||||||
|
|||||||
@@ -144,3 +144,21 @@ PoseList::FindAllPoses(const node_ref* node) const
|
|||||||
}
|
}
|
||||||
return result;
|
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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -67,6 +67,8 @@ public:
|
|||||||
// same as FindPose, node can be a target of the actual
|
// same as FindPose, node can be a target of the actual
|
||||||
// pose if the pose is a symlink
|
// pose if the pose is a symlink
|
||||||
PoseList* FindAllPoses(const node_ref* node) const;
|
PoseList* FindAllPoses(const node_ref* node) const;
|
||||||
|
|
||||||
|
BPose* FindPoseByFileName(const char* name, int32* _index = NULL) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
// iteration glue, add permutations as needed
|
// iteration glue, add permutations as needed
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ All rights reserved.
|
|||||||
#include <Window.h>
|
#include <Window.h>
|
||||||
|
|
||||||
#include <ObjectListPrivate.h>
|
#include <ObjectListPrivate.h>
|
||||||
|
#include <PathMonitor.h>
|
||||||
|
|
||||||
#include "Attributes.h"
|
#include "Attributes.h"
|
||||||
#include "AttributeStream.h"
|
#include "AttributeStream.h"
|
||||||
@@ -1107,6 +1108,7 @@ BPoseView::InitDirentIterator(const entry_ref* ref)
|
|||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
ASSERT(!sourceModel.IsQuery());
|
ASSERT(!sourceModel.IsQuery());
|
||||||
|
ASSERT(!sourceModel.IsVirtualDirectory());
|
||||||
ASSERT(sourceModel.Node());
|
ASSERT(sourceModel.Node());
|
||||||
|
|
||||||
BDirectory* directory = dynamic_cast<BDirectory*>(sourceModel.Node());
|
BDirectory* directory = dynamic_cast<BDirectory*>(sourceModel.Node());
|
||||||
@@ -2153,6 +2155,7 @@ BPoseView::MessageReceived(BMessage* message)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case B_NODE_MONITOR:
|
case B_NODE_MONITOR:
|
||||||
|
case B_PATH_MONITOR:
|
||||||
case B_QUERY_UPDATE:
|
case B_QUERY_UPDATE:
|
||||||
if (!FSNotification(message))
|
if (!FSNotification(message))
|
||||||
pendingNodeMonitorCache.Add(message);
|
pendingNodeMonitorCache.Add(message);
|
||||||
@@ -4920,6 +4923,10 @@ BPoseView::MoveSelectionInto(Model* destFolder, BContainerWindow* srcWindow,
|
|||||||
okToMove = alert->Go() == 1;
|
okToMove = alert->Go() == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Handle correctly!
|
||||||
|
if (srcWindow->TargetModel()->IsVirtualDirectory())
|
||||||
|
okToMove = false;
|
||||||
|
|
||||||
if (okToMove) {
|
if (okToMove) {
|
||||||
PoseList* selectionList = srcWindow->PoseView()->SelectionList();
|
PoseList* selectionList = srcWindow->PoseView()->SelectionList();
|
||||||
BList* pointList = destWindow->PoseView()->GetDropPointList(clickPt, loc, selectionList,
|
BList* pointList = destWindow->PoseView()->GetDropPointList(clickPt, loc, selectionList,
|
||||||
@@ -5166,6 +5173,7 @@ BPoseView::FSNotification(const BMessage* message)
|
|||||||
TrackerSettings settings;
|
TrackerSettings settings;
|
||||||
if (dirNode != *TargetModel()->NodeRef()
|
if (dirNode != *TargetModel()->NodeRef()
|
||||||
&& !TargetModel()->IsQuery()
|
&& !TargetModel()->IsQuery()
|
||||||
|
&& !TargetModel()->IsVirtualDirectory()
|
||||||
&& !TargetModel()->IsRoot()
|
&& !TargetModel()->IsRoot()
|
||||||
&& (!settings.ShowDisksIcon() || !IsDesktopView())) {
|
&& (!settings.ShowDisksIcon() || !IsDesktopView())) {
|
||||||
if (count == 0)
|
if (count == 0)
|
||||||
@@ -5446,7 +5454,8 @@ BPoseView::EntryMoved(const BMessage* message)
|
|||||||
TargetModel()->UpdateEntryRef(&dirNode, name);
|
TargetModel()->UpdateEntryRef(&dirNode, name);
|
||||||
assert_cast<BContainerWindow*>(Window())->UpdateTitle();
|
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)
|
// rename or move of entry in this directory (or query)
|
||||||
|
|
||||||
@@ -8329,11 +8338,9 @@ BPoseView::OpenParent()
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
BEntry entry(TargetModel()->EntryRef());
|
BEntry entry(TargetModel()->EntryRef());
|
||||||
BDirectory parent;
|
|
||||||
entry_ref ref;
|
entry_ref ref;
|
||||||
|
|
||||||
if (entry.GetParent(&parent) != B_OK
|
if (FSGetParentVirtualDirectoryAware(entry, entry) != B_OK
|
||||||
|| parent.GetEntry(&entry) != B_OK
|
|
||||||
|| entry.GetRef(&ref) != B_OK)
|
|| entry.GetRef(&ref) != B_OK)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ All rights reserved.
|
|||||||
#include "SlowContextPopup.h"
|
#include "SlowContextPopup.h"
|
||||||
#include "Thread.h"
|
#include "Thread.h"
|
||||||
#include "Tracker.h"
|
#include "Tracker.h"
|
||||||
|
#include "VirtualDirectoryEntryList.h"
|
||||||
|
|
||||||
|
|
||||||
#undef B_TRANSLATION_CONTEXT
|
#undef B_TRANSLATION_CONTEXT
|
||||||
@@ -247,6 +248,8 @@ BSlowContextMenu::StartBuildingItemList()
|
|||||||
|
|
||||||
if (startModel.IsQuery())
|
if (startModel.IsQuery())
|
||||||
fContainer = new QueryEntryListCollection(&startModel);
|
fContainer = new QueryEntryListCollection(&startModel);
|
||||||
|
else if (startModel.IsVirtualDirectory())
|
||||||
|
fContainer = new VirtualDirectoryEntryList(&startModel);
|
||||||
else if (startModel.IsDesktop()) {
|
else if (startModel.IsDesktop()) {
|
||||||
fIteratingDesktop = true;
|
fIteratingDesktop = true;
|
||||||
fContainer = DesktopPoseView::InitDesktopDirentIterator(0,
|
fContainer = DesktopPoseView::InitDesktopDirentIterator(0,
|
||||||
|
|||||||
@@ -59,6 +59,8 @@ All rights reserved.
|
|||||||
#include <Volume.h>
|
#include <Volume.h>
|
||||||
#include <VolumeRoster.h>
|
#include <VolumeRoster.h>
|
||||||
|
|
||||||
|
#include <PathMonitor.h>
|
||||||
|
|
||||||
#include "Attributes.h"
|
#include "Attributes.h"
|
||||||
#include "AutoLock.h"
|
#include "AutoLock.h"
|
||||||
#include "AutoMounterSettings.h"
|
#include "AutoMounterSettings.h"
|
||||||
@@ -86,6 +88,7 @@ All rights reserved.
|
|||||||
#include "TaskLoop.h"
|
#include "TaskLoop.h"
|
||||||
#include "Thread.h"
|
#include "Thread.h"
|
||||||
#include "Utilities.h"
|
#include "Utilities.h"
|
||||||
|
#include "VirtualDirectoryWindow.h"
|
||||||
#include "VolumeWindow.h"
|
#include "VolumeWindow.h"
|
||||||
|
|
||||||
// prototypes for some private kernel calls that will some day be public
|
// 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
|
#undef B_TRANSLATION_CONTEXT
|
||||||
@@ -217,8 +239,11 @@ GetVolumeFlags(Model* model)
|
|||||||
|
|
||||||
TTracker::TTracker()
|
TTracker::TTracker()
|
||||||
: BApplication(kTrackerSignature),
|
: BApplication(kTrackerSignature),
|
||||||
|
fWatchingInterface(new WatchingInterface),
|
||||||
fSettingsWindow(NULL)
|
fSettingsWindow(NULL)
|
||||||
{
|
{
|
||||||
|
BPathMonitor::SetWatchingInterface(fWatchingInterface);
|
||||||
|
|
||||||
// set the cwd to /boot/home, anything that's launched
|
// set the cwd to /boot/home, anything that's launched
|
||||||
// from Tracker will automatically inherit this
|
// from Tracker will automatically inherit this
|
||||||
BPath homePath;
|
BPath homePath;
|
||||||
@@ -261,6 +286,9 @@ TTracker::~TTracker()
|
|||||||
{
|
{
|
||||||
gLaunchLooper->Lock();
|
gLaunchLooper->Lock();
|
||||||
gLaunchLooper->Quit();
|
gLaunchLooper->Quit();
|
||||||
|
|
||||||
|
BPathMonitor::SetWatchingInterface(NULL);
|
||||||
|
delete fWatchingInterface;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -961,6 +989,9 @@ TTracker::OpenContainerWindow(Model* model, BMessage* originalRefsList,
|
|||||||
} else if (model->IsQuery()) {
|
} else if (model->IsQuery()) {
|
||||||
// window will adopt the model
|
// window will adopt the model
|
||||||
window = new BQueryContainerWindow(&fWindowList, openFlags);
|
window = new BQueryContainerWindow(&fWindowList, openFlags);
|
||||||
|
} else if (model->IsVirtualDirectory()) {
|
||||||
|
// window will adopt the model
|
||||||
|
window = new VirtualDirectoryWindow(&fWindowList, openFlags);
|
||||||
} else
|
} else
|
||||||
// window will adopt the model
|
// window will adopt the model
|
||||||
window = new BContainerWindow(&fWindowList, openFlags);
|
window = new BContainerWindow(&fWindowList, openFlags);
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ const uint32 B_ENTRY_SPECIFIER = 'sref';
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
class TTracker : public BApplication {
|
class TTracker : public BApplication {
|
||||||
public:
|
public:
|
||||||
TTracker();
|
TTracker();
|
||||||
virtual ~TTracker();
|
virtual ~TTracker();
|
||||||
@@ -163,6 +163,9 @@ class TTracker : public BApplication {
|
|||||||
bool GetProperty(BMessage*, int32, const char*, BMessage*);
|
bool GetProperty(BMessage*, int32, const char*, BMessage*);
|
||||||
bool SetProperty(BMessage*, BMessage*, int32, const char*, BMessage*);
|
bool SetProperty(BMessage*, BMessage*, int32, const char*, BMessage*);
|
||||||
|
|
||||||
|
private:
|
||||||
|
class WatchingInterface;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// callbacks for ChildParentSoon calls
|
// callbacks for ChildParentSoon calls
|
||||||
bool CloseParentWaitingForChild(const entry_ref* child,
|
bool CloseParentWaitingForChild(const entry_ref* child,
|
||||||
@@ -221,6 +224,7 @@ class TTracker : public BApplication {
|
|||||||
BTrashWatcher* fTrashWatcher;
|
BTrashWatcher* fTrashWatcher;
|
||||||
TaskLoop* fTaskLoop;
|
TaskLoop* fTaskLoop;
|
||||||
int32 fNodeMonitorCount;
|
int32 fNodeMonitorCount;
|
||||||
|
WatchingInterface* fWatchingInterface;
|
||||||
|
|
||||||
TrackerSettingsWindow* fSettingsWindow;
|
TrackerSettingsWindow* fSettingsWindow;
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -1104,7 +1104,8 @@ SizeAttributeText::ReadValue()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (fModel->IsDirectory() || fModel->IsQuery()
|
if (fModel->IsDirectory() || fModel->IsQuery()
|
||||||
|| fModel->IsQueryTemplate() || fModel->IsSymLink())
|
|| fModel->IsQueryTemplate() || fModel->IsSymLink()
|
||||||
|
|| fModel->IsVirtualDirectory())
|
||||||
return kUnknownSize;
|
return kUnknownSize;
|
||||||
|
|
||||||
fValueIsDefined = true;
|
fValueIsDefined = true;
|
||||||
|
|||||||
Reference in New Issue
Block a user