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:
parent
de85051c81
commit
1c29b26e7c
BIN
data/artwork/icons/Folder_virtual
Normal file
BIN
data/artwork/icons/Folder_virtual
Normal file
Binary file not shown.
24
src/data/mime_db/application/x-vnd.haiku-virtual-directory
Normal file
24
src/data/mime_db/application/x-vnd.haiku-virtual-directory
Normal file
@ -0,0 +1,24 @@
|
||||
|
||||
resource(0, "BEOS:TYPE") #'MIMS' "application/x-vnd.Be-meta-mime";
|
||||
|
||||
resource(1, "META:TYPE") "application/x-vnd.haiku-virtual-directory";
|
||||
|
||||
resource(2, "META:S:DESC") #'MSDC' "Virtual Directory";
|
||||
|
||||
resource(3, "META:L:DESC") #'MLDC'
|
||||
"Virtual directory merging the contents of multiple actual directories";
|
||||
|
||||
resource(5, "META:PREF_APP") #'MSIG' "application/x-vnd.Be-TRAK";
|
||||
|
||||
resource(6, "META:ICON") #'VICN' array {
|
||||
$"6E636966070500020006023841813C9B3BBEAB393A4F584B254D4A7AEB009BE2"
|
||||
$"FFFF5A6E82020016022C60673CAAABBEBB082E6EFB4BBA064A22B000FFFF8E02"
|
||||
$"0006022C25F43C6917BEBB082E6EFB4BAFBF497E0B0082AAC8FF40407C033D3D"
|
||||
$"5D020016023AE3B43C79DCBE5BC53CC0974B42AC47CA41009AFF50040165070A"
|
||||
$"064A5D505D545958595E53504F0A04232F2D494A5B463E0A04232F463E4A5BC3"
|
||||
$"233C0A063A282E494A5A5C2C502A4C2C0A04302B30494A5951360A04302B5136"
|
||||
$"4A5954BB1E0A0A232F2D494A5B5C2C502A4C2C3A28382D302BB957BAA7080A06"
|
||||
$"0100000A0001061815FF01178400040A00010618001501178600040A03010300"
|
||||
$"0A020104000A050105000A010101000A04010200"
|
||||
};
|
||||
|
@ -1507,7 +1507,8 @@ BContainerWindow::MessageReceived(BMessage* message)
|
||||
PoseView()->MoveSelectionInto(&model, this, false, false,
|
||||
message->what == kCreateLink,
|
||||
message->what == kCreateRelativeLink);
|
||||
} else if (!TargetModel()->IsQuery()) {
|
||||
} else if (!TargetModel()->IsQuery()
|
||||
&& !TargetModel()->IsVirtualDirectory()) {
|
||||
// no destination specified, create link in same dir as item
|
||||
PoseView()->MoveSelectionInto(TargetModel(), this, false, false,
|
||||
message->what == kCreateLink,
|
||||
@ -1895,7 +1896,8 @@ BContainerWindow::AddFileMenu(BMenu* menu)
|
||||
new BMessage(kFindButton), 'F'));
|
||||
}
|
||||
|
||||
if (!TargetModel()->IsQuery() && !IsTrash() && !IsPrintersDir()) {
|
||||
if (!TargetModel()->IsQuery() && !TargetModel()->IsVirtualDirectory()
|
||||
&& !IsTrash() && !IsPrintersDir()) {
|
||||
if (!PoseView()->IsFilePanel()) {
|
||||
TemplatesMenu* templateMenu = new TemplatesMenu(PoseView(),
|
||||
B_TRANSLATE("New"));
|
||||
@ -2108,6 +2110,7 @@ BContainerWindow::AddShortcuts()
|
||||
ASSERT(!IsTrash());
|
||||
ASSERT(!PoseView()->IsFilePanel());
|
||||
ASSERT(!TargetModel()->IsQuery());
|
||||
ASSERT(!TargetModel()->IsVirtualDirectory());
|
||||
|
||||
AddShortcut('X', B_COMMAND_KEY | B_SHIFT_KEY,
|
||||
new BMessage(kCutMoreSelectionToClipboard), this);
|
||||
@ -3772,8 +3775,9 @@ BContainerWindow::SetUpDefaultState()
|
||||
// try copying state from our parent directory, unless it is the
|
||||
// desktop folder
|
||||
BEntry entry(TargetModel()->EntryRef());
|
||||
BDirectory parent;
|
||||
if (entry.GetParent(&parent) == B_OK && parent != desktop) {
|
||||
BNode parent;
|
||||
if (FSGetParentVirtualDirectoryAware(entry, parent) == B_OK
|
||||
&& parent != desktop) {
|
||||
PRINT(("looking at parent for state\n"));
|
||||
if (NodeHasSavedState(&parent)) {
|
||||
PRINT(("got state from parent\n"));
|
||||
|
@ -78,6 +78,7 @@ DesktopPoseView::InitDesktopDirentIterator(BPoseView* nodeMonitoringTarget,
|
||||
CachedEntryIteratorList* result = new CachedEntryIteratorList();
|
||||
|
||||
ASSERT(!sourceModel.IsQuery());
|
||||
ASSERT(!sourceModel.IsVirtualDirectory());
|
||||
ASSERT(sourceModel.Node());
|
||||
BDirectory* sourceDirectory
|
||||
= dynamic_cast<BDirectory*>(sourceModel.Node());
|
||||
|
@ -115,10 +115,9 @@ BDirMenu::Populate(const BEntry* startEntry, BWindow* originatingWindow,
|
||||
// if we're at the root directory skip "mnt" and
|
||||
// go straight to "/"
|
||||
parent.SetTo("/");
|
||||
parent.GetEntry(&entry);
|
||||
} else
|
||||
entry.GetParent(&parent);
|
||||
|
||||
parent.GetEntry(&entry);
|
||||
FSGetParentVirtualDirectoryAware(entry, entry);
|
||||
}
|
||||
|
||||
BDirectory desktopDir;
|
||||
@ -135,9 +134,7 @@ BDirMenu::Populate(const BEntry* startEntry, BWindow* originatingWindow,
|
||||
kAttrPoseInfoForeign, B_RAW_TYPE, 0, &info, sizeof(PoseInfo),
|
||||
&PoseInfo::EndianSwap);
|
||||
|
||||
BDirectory parent;
|
||||
entry.GetParent(&parent);
|
||||
|
||||
BEntry parentEntry;
|
||||
bool hitRoot = false;
|
||||
|
||||
BDirectory dir(&entry);
|
||||
@ -146,8 +143,9 @@ BDirMenu::Populate(const BEntry* startEntry, BWindow* originatingWindow,
|
||||
// if we're at the root directory skip "mnt" and
|
||||
// go straight to "/"
|
||||
hitRoot = true;
|
||||
parent.SetTo("/");
|
||||
}
|
||||
parentEntry.SetTo("/");
|
||||
} else
|
||||
FSGetParentVirtualDirectoryAware(entry, parentEntry);
|
||||
|
||||
if (showDesktop) {
|
||||
BEntry root("/");
|
||||
@ -174,7 +172,9 @@ BDirMenu::Populate(const BEntry* startEntry, BWindow* originatingWindow,
|
||||
break;
|
||||
}
|
||||
|
||||
parent.GetEntry(&entry);
|
||||
entry = parentEntry;
|
||||
if (entry.InitCheck() != B_OK)
|
||||
break;
|
||||
}
|
||||
|
||||
// select last item in menu
|
||||
|
@ -71,6 +71,8 @@ respective holders. All rights reserved.
|
||||
#include <fs_info.h>
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#include <AutoLocker.h>
|
||||
|
||||
#include "Attributes.h"
|
||||
#include "Bitmaps.h"
|
||||
#include "Commands.h"
|
||||
@ -85,6 +87,7 @@ respective holders. All rights reserved.
|
||||
#include "Tracker.h"
|
||||
#include "TrackerSettings.h"
|
||||
#include "Utilities.h"
|
||||
#include "VirtualDirectoryManager.h"
|
||||
|
||||
|
||||
enum {
|
||||
@ -3157,6 +3160,66 @@ GetAttrInfo(const BNode* node, const char* hostAttrName,
|
||||
return kReadAttrFailed;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
FSGetParentVirtualDirectoryAware(const BEntry& entry, entry_ref& _ref)
|
||||
{
|
||||
node_ref nodeRef;
|
||||
if (entry.GetNodeRef(&nodeRef) == B_OK) {
|
||||
if (VirtualDirectoryManager* manager
|
||||
= VirtualDirectoryManager::Instance()) {
|
||||
AutoLocker<VirtualDirectoryManager> managerLocker(manager);
|
||||
if (manager->GetParentDirectoryDefinitionFile(nodeRef, _ref,
|
||||
nodeRef)) {
|
||||
return B_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
status_t error;
|
||||
BDirectory parent;
|
||||
BEntry parentEntry;
|
||||
if ((error = entry.GetParent(&parent)) != B_OK
|
||||
|| (error = parent.GetEntry(&parentEntry)) != B_OK
|
||||
|| (error = parentEntry.GetRef(&_ref)) != B_OK) {
|
||||
return error;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
FSGetParentVirtualDirectoryAware(const BEntry& entry, BEntry& _entry)
|
||||
{
|
||||
node_ref nodeRef;
|
||||
if (entry.GetNodeRef(&nodeRef) == B_OK) {
|
||||
if (VirtualDirectoryManager* manager
|
||||
= VirtualDirectoryManager::Instance()) {
|
||||
AutoLocker<VirtualDirectoryManager> managerLocker(manager);
|
||||
entry_ref parentRef;
|
||||
if (manager->GetParentDirectoryDefinitionFile(nodeRef, parentRef,
|
||||
nodeRef)) {
|
||||
return _entry.SetTo(&parentRef);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return entry.GetParent(&_entry);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
FSGetParentVirtualDirectoryAware(const BEntry& entry, BNode& _node)
|
||||
{
|
||||
entry_ref ref;
|
||||
status_t error = FSGetParentVirtualDirectoryAware(entry, ref);
|
||||
if (error == B_OK)
|
||||
error = _node.SetTo(&ref);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
// launching code
|
||||
|
||||
static status_t
|
||||
|
@ -238,6 +238,10 @@ status_t FSRecursiveCreateFolder(const char* path);
|
||||
void FSMakeOriginalName(BString &name, const BDirectory* destDir,
|
||||
const char* suffix = 0);
|
||||
|
||||
status_t FSGetParentVirtualDirectoryAware(const BEntry& entry, entry_ref& _ref);
|
||||
status_t FSGetParentVirtualDirectoryAware(const BEntry& entry, BEntry& _entry);
|
||||
status_t FSGetParentVirtualDirectoryAware(const BEntry& entry, BNode& _node);
|
||||
|
||||
status_t TrackerLaunch(const entry_ref* app, bool async);
|
||||
status_t TrackerLaunch(const BMessage* refs, bool async,
|
||||
bool okToRunOpenWith = true);
|
||||
|
@ -55,6 +55,7 @@ All rights reserved.
|
||||
#include "QueryPoseView.h"
|
||||
#include "Tracker.h"
|
||||
#include "Utilities.h"
|
||||
#include "VirtualDirectoryEntryList.h"
|
||||
|
||||
|
||||
#undef B_TRANSLATION_CONTEXT
|
||||
@ -138,6 +139,8 @@ FavoritesMenu::AddNextItem()
|
||||
|
||||
if (startModel.IsQuery())
|
||||
fContainer = new QueryEntryListCollection(&startModel);
|
||||
else if (startModel.IsVirtualDirectory())
|
||||
fContainer = new VirtualDirectoryEntryList(&startModel);
|
||||
else
|
||||
fContainer = new DirectoryEntryList(*dynamic_cast<BDirectory*>
|
||||
(startModel.Node()));
|
||||
|
@ -86,6 +86,10 @@ SharedLibrary libtracker.so :
|
||||
TrashWatcher.cpp
|
||||
Utilities.cpp
|
||||
ViewState.cpp
|
||||
VirtualDirectoryEntryList.cpp
|
||||
VirtualDirectoryManager.cpp
|
||||
VirtualDirectoryPoseView.cpp
|
||||
VirtualDirectoryWindow.cpp
|
||||
VolumeWindow.cpp
|
||||
WidgetAttributeText.cpp
|
||||
|
||||
|
@ -51,6 +51,7 @@ namespace BPrivate {
|
||||
#define B_PRINTER_SPOOL_MIMETYPE "application/x-vnd.Be.printer-spool"
|
||||
|
||||
#define kPlainTextMimeType "text/plain"
|
||||
#define kVirtualDirectoryMimeType "application/x-vnd.haiku-virtual-directory"
|
||||
|
||||
#define kBitmapMimeType "image/x-vnd.Be-bitmap"
|
||||
#define kLargeIconType "icon/large"
|
||||
|
@ -325,9 +325,11 @@ Model::CompareFolderNamesFirst(const Model* compareModel) const
|
||||
const Model* resolvedCompareModel = compareModel->ResolveIfLink();
|
||||
const Model* resolvedMe = ResolveIfLink();
|
||||
|
||||
bool meIsDirOrVolume = resolvedMe->IsDirectory() || resolvedMe->IsVolume();
|
||||
bool meIsDirOrVolume = resolvedMe->IsDirectory() || resolvedMe->IsVolume()
|
||||
|| resolvedMe->IsVirtualDirectory();
|
||||
bool otherIsDirOrVolume = resolvedCompareModel->IsDirectory()
|
||||
|| resolvedCompareModel->IsVolume();
|
||||
|| resolvedCompareModel->IsVolume()
|
||||
|| resolvedCompareModel->IsVirtualDirectory();
|
||||
|
||||
if (meIsDirOrVolume) {
|
||||
if (!otherIsDirOrVolume)
|
||||
@ -426,6 +428,7 @@ Model::OpenNodeCommon(bool writable)
|
||||
case kExecutableNode:
|
||||
case kQueryNode:
|
||||
case kQueryTemplateNode:
|
||||
case kVirtualDirectoryNode:
|
||||
// open or reopen
|
||||
delete fNode;
|
||||
fNode = new BFile(&fEntryRef,
|
||||
@ -629,6 +632,8 @@ Model::FinishSettingUpType()
|
||||
fBaseType = kQueryNode;
|
||||
else if (strcmp(mimeString, B_QUERY_TEMPLATE_MIMETYPE) == 0)
|
||||
fBaseType = kQueryTemplateNode;
|
||||
else if (strcmp(mimeString, kVirtualDirectoryMimeType) == 0)
|
||||
fBaseType = kVirtualDirectoryNode;
|
||||
|
||||
if (info.GetPreferredApp(mimeString) == B_OK) {
|
||||
if (fPreferredAppName)
|
||||
@ -1332,6 +1337,10 @@ Model::PrintToStream(int32 level, bool deep)
|
||||
PRINT(("volume, name %s\n", fVolumeName ? fVolumeName : ""));
|
||||
break;
|
||||
|
||||
case kVirtualDirectoryNode:
|
||||
PRINT(("virtual directory\n"));
|
||||
break;
|
||||
|
||||
default:
|
||||
PRINT(("unknown\n"));
|
||||
break;
|
||||
|
@ -141,6 +141,7 @@ class Model {
|
||||
bool IsTrash() const;
|
||||
bool IsDesktop() const;
|
||||
bool IsVolume() const;
|
||||
bool IsVirtualDirectory() const;
|
||||
|
||||
IconSource IconFrom() const;
|
||||
void SetIconFrom(IconSource);
|
||||
@ -239,6 +240,7 @@ class Model {
|
||||
kRootNode,
|
||||
kTrashNode,
|
||||
kDesktopNode,
|
||||
kVirtualDirectoryNode,
|
||||
kUnknownNode
|
||||
};
|
||||
|
||||
@ -381,7 +383,8 @@ Model::IsFile() const
|
||||
return fBaseType == kPlainNode
|
||||
|| fBaseType == kQueryNode
|
||||
|| fBaseType == kQueryTemplateNode
|
||||
|| fBaseType == kExecutableNode;
|
||||
|| fBaseType == kExecutableNode
|
||||
|| fBaseType == kVirtualDirectoryNode;
|
||||
}
|
||||
|
||||
|
||||
@ -422,7 +425,7 @@ Model::IsContainer() const
|
||||
{
|
||||
// I guess as in should show container window -
|
||||
// volumes show the volume window
|
||||
return IsQuery() || IsDirectory();
|
||||
return IsQuery() || IsDirectory() || IsVirtualDirectory();
|
||||
}
|
||||
|
||||
|
||||
@ -461,6 +464,13 @@ Model::IsSymLink() const
|
||||
}
|
||||
|
||||
|
||||
inline bool
|
||||
Model::IsVirtualDirectory() const
|
||||
{
|
||||
return fBaseType == kVirtualDirectoryNode;
|
||||
}
|
||||
|
||||
|
||||
inline bool
|
||||
Model::HasLocalizedName() const
|
||||
{
|
||||
|
@ -64,6 +64,7 @@ their respective holders. All rights reserved.
|
||||
#include "QueryPoseView.h"
|
||||
#include "Thread.h"
|
||||
#include "Tracker.h"
|
||||
#include "VirtualDirectoryEntryList.h"
|
||||
|
||||
|
||||
namespace BPrivate {
|
||||
@ -420,6 +421,8 @@ BNavMenu::StartBuildingItemList()
|
||||
|
||||
if (startModel.IsQuery())
|
||||
fContainer = new QueryEntryListCollection(&startModel);
|
||||
else if (startModel.IsVirtualDirectory())
|
||||
fContainer = new VirtualDirectoryEntryList(&startModel);
|
||||
else if (startModel.IsDesktop()) {
|
||||
fIteratingDesktop = true;
|
||||
fContainer = DesktopPoseView::InitDesktopDirentIterator(
|
||||
|
@ -144,3 +144,21 @@ PoseList::FindAllPoses(const node_ref* node) const
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
BPose*
|
||||
PoseList::FindPoseByFileName(const char* name, int32* _index) const
|
||||
{
|
||||
int32 count = CountItems();
|
||||
for (int32 index = 0; index < count; index++) {
|
||||
BPose* pose = ItemAt(index);
|
||||
ASSERT(pose->TargetModel());
|
||||
if (strcmp(pose->TargetModel()->EntryRef()->name, name) == 0) {
|
||||
if (_index)
|
||||
*_index = index;
|
||||
|
||||
return pose;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
@ -67,6 +67,8 @@ public:
|
||||
// same as FindPose, node can be a target of the actual
|
||||
// pose if the pose is a symlink
|
||||
PoseList* FindAllPoses(const node_ref* node) const;
|
||||
|
||||
BPose* FindPoseByFileName(const char* name, int32* _index = NULL) const;
|
||||
};
|
||||
|
||||
// iteration glue, add permutations as needed
|
||||
|
@ -72,6 +72,7 @@ All rights reserved.
|
||||
#include <Window.h>
|
||||
|
||||
#include <ObjectListPrivate.h>
|
||||
#include <PathMonitor.h>
|
||||
|
||||
#include "Attributes.h"
|
||||
#include "AttributeStream.h"
|
||||
@ -1107,6 +1108,7 @@ BPoseView::InitDirentIterator(const entry_ref* ref)
|
||||
return NULL;
|
||||
|
||||
ASSERT(!sourceModel.IsQuery());
|
||||
ASSERT(!sourceModel.IsVirtualDirectory());
|
||||
ASSERT(sourceModel.Node());
|
||||
|
||||
BDirectory* directory = dynamic_cast<BDirectory*>(sourceModel.Node());
|
||||
@ -2153,6 +2155,7 @@ BPoseView::MessageReceived(BMessage* message)
|
||||
break;
|
||||
|
||||
case B_NODE_MONITOR:
|
||||
case B_PATH_MONITOR:
|
||||
case B_QUERY_UPDATE:
|
||||
if (!FSNotification(message))
|
||||
pendingNodeMonitorCache.Add(message);
|
||||
@ -4920,6 +4923,10 @@ BPoseView::MoveSelectionInto(Model* destFolder, BContainerWindow* srcWindow,
|
||||
okToMove = alert->Go() == 1;
|
||||
}
|
||||
|
||||
// TODO: Handle correctly!
|
||||
if (srcWindow->TargetModel()->IsVirtualDirectory())
|
||||
okToMove = false;
|
||||
|
||||
if (okToMove) {
|
||||
PoseList* selectionList = srcWindow->PoseView()->SelectionList();
|
||||
BList* pointList = destWindow->PoseView()->GetDropPointList(clickPt, loc, selectionList,
|
||||
@ -5166,6 +5173,7 @@ BPoseView::FSNotification(const BMessage* message)
|
||||
TrackerSettings settings;
|
||||
if (dirNode != *TargetModel()->NodeRef()
|
||||
&& !TargetModel()->IsQuery()
|
||||
&& !TargetModel()->IsVirtualDirectory()
|
||||
&& !TargetModel()->IsRoot()
|
||||
&& (!settings.ShowDisksIcon() || !IsDesktopView())) {
|
||||
if (count == 0)
|
||||
@ -5446,7 +5454,8 @@ BPoseView::EntryMoved(const BMessage* message)
|
||||
TargetModel()->UpdateEntryRef(&dirNode, name);
|
||||
assert_cast<BContainerWindow*>(Window())->UpdateTitle();
|
||||
}
|
||||
if (oldDir == dirNode.node || TargetModel()->IsQuery()) {
|
||||
if (oldDir == dirNode.node || TargetModel()->IsQuery()
|
||||
|| TargetModel()->IsVirtualDirectory()) {
|
||||
|
||||
// rename or move of entry in this directory (or query)
|
||||
|
||||
@ -8329,11 +8338,9 @@ BPoseView::OpenParent()
|
||||
return;
|
||||
|
||||
BEntry entry(TargetModel()->EntryRef());
|
||||
BDirectory parent;
|
||||
entry_ref ref;
|
||||
|
||||
if (entry.GetParent(&parent) != B_OK
|
||||
|| parent.GetEntry(&entry) != B_OK
|
||||
if (FSGetParentVirtualDirectoryAware(entry, entry) != B_OK
|
||||
|| entry.GetRef(&ref) != B_OK)
|
||||
return;
|
||||
|
||||
|
@ -60,6 +60,7 @@ All rights reserved.
|
||||
#include "SlowContextPopup.h"
|
||||
#include "Thread.h"
|
||||
#include "Tracker.h"
|
||||
#include "VirtualDirectoryEntryList.h"
|
||||
|
||||
|
||||
#undef B_TRANSLATION_CONTEXT
|
||||
@ -247,6 +248,8 @@ BSlowContextMenu::StartBuildingItemList()
|
||||
|
||||
if (startModel.IsQuery())
|
||||
fContainer = new QueryEntryListCollection(&startModel);
|
||||
else if (startModel.IsVirtualDirectory())
|
||||
fContainer = new VirtualDirectoryEntryList(&startModel);
|
||||
else if (startModel.IsDesktop()) {
|
||||
fIteratingDesktop = true;
|
||||
fContainer = DesktopPoseView::InitDesktopDirentIterator(0,
|
||||
|
@ -59,6 +59,8 @@ All rights reserved.
|
||||
#include <Volume.h>
|
||||
#include <VolumeRoster.h>
|
||||
|
||||
#include <PathMonitor.h>
|
||||
|
||||
#include "Attributes.h"
|
||||
#include "AutoLock.h"
|
||||
#include "AutoMounterSettings.h"
|
||||
@ -86,6 +88,7 @@ All rights reserved.
|
||||
#include "TaskLoop.h"
|
||||
#include "Thread.h"
|
||||
#include "Utilities.h"
|
||||
#include "VirtualDirectoryWindow.h"
|
||||
#include "VolumeWindow.h"
|
||||
|
||||
// prototypes for some private kernel calls that will some day be public
|
||||
@ -209,7 +212,26 @@ GetVolumeFlags(Model* model)
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
// #pragma mark - WatchingInterface
|
||||
|
||||
|
||||
class TTracker::WatchingInterface : public BPathMonitor::BWatchingInterface {
|
||||
public:
|
||||
virtual status_t WatchNode(const node_ref* node, uint32 flags,
|
||||
const BMessenger& target)
|
||||
{
|
||||
return TTracker::WatchNode(node, flags, target);
|
||||
}
|
||||
|
||||
virtual status_t WatchNode(const node_ref* node, uint32 flags,
|
||||
const BHandler* handler, const BLooper* looper = NULL)
|
||||
{
|
||||
return TTracker::WatchNode(node, flags, BMessenger(handler, looper));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// #pragma mark - TTracker
|
||||
|
||||
|
||||
#undef B_TRANSLATION_CONTEXT
|
||||
@ -217,8 +239,11 @@ GetVolumeFlags(Model* model)
|
||||
|
||||
TTracker::TTracker()
|
||||
: BApplication(kTrackerSignature),
|
||||
fWatchingInterface(new WatchingInterface),
|
||||
fSettingsWindow(NULL)
|
||||
{
|
||||
BPathMonitor::SetWatchingInterface(fWatchingInterface);
|
||||
|
||||
// set the cwd to /boot/home, anything that's launched
|
||||
// from Tracker will automatically inherit this
|
||||
BPath homePath;
|
||||
@ -261,6 +286,9 @@ TTracker::~TTracker()
|
||||
{
|
||||
gLaunchLooper->Lock();
|
||||
gLaunchLooper->Quit();
|
||||
|
||||
BPathMonitor::SetWatchingInterface(NULL);
|
||||
delete fWatchingInterface;
|
||||
}
|
||||
|
||||
|
||||
@ -961,6 +989,9 @@ TTracker::OpenContainerWindow(Model* model, BMessage* originalRefsList,
|
||||
} else if (model->IsQuery()) {
|
||||
// window will adopt the model
|
||||
window = new BQueryContainerWindow(&fWindowList, openFlags);
|
||||
} else if (model->IsVirtualDirectory()) {
|
||||
// window will adopt the model
|
||||
window = new VirtualDirectoryWindow(&fWindowList, openFlags);
|
||||
} else
|
||||
// window will adopt the model
|
||||
window = new BContainerWindow(&fWindowList, openFlags);
|
||||
|
@ -74,7 +74,7 @@ const uint32 B_ENTRY_SPECIFIER = 'sref';
|
||||
|
||||
|
||||
|
||||
class TTracker : public BApplication {
|
||||
class TTracker : public BApplication {
|
||||
public:
|
||||
TTracker();
|
||||
virtual ~TTracker();
|
||||
@ -163,6 +163,9 @@ class TTracker : public BApplication {
|
||||
bool GetProperty(BMessage*, int32, const char*, BMessage*);
|
||||
bool SetProperty(BMessage*, BMessage*, int32, const char*, BMessage*);
|
||||
|
||||
private:
|
||||
class WatchingInterface;
|
||||
|
||||
private:
|
||||
// callbacks for ChildParentSoon calls
|
||||
bool CloseParentWaitingForChild(const entry_ref* child,
|
||||
@ -221,6 +224,7 @@ class TTracker : public BApplication {
|
||||
BTrashWatcher* fTrashWatcher;
|
||||
TaskLoop* fTaskLoop;
|
||||
int32 fNodeMonitorCount;
|
||||
WatchingInterface* fWatchingInterface;
|
||||
|
||||
TrackerSettingsWindow* fSettingsWindow;
|
||||
|
||||
|
153
src/kits/tracker/VirtualDirectoryEntryList.cpp
Normal file
153
src/kits/tracker/VirtualDirectoryEntryList.cpp
Normal file
@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright 2013, Haiku, Inc.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Ingo Weinhold, ingo_weinhold@gmx.de
|
||||
*/
|
||||
|
||||
|
||||
#include "VirtualDirectoryEntryList.h"
|
||||
|
||||
#include <AutoLocker.h>
|
||||
#include <storage_support.h>
|
||||
|
||||
#include "Model.h"
|
||||
#include "VirtualDirectoryManager.h"
|
||||
|
||||
|
||||
namespace BPrivate {
|
||||
|
||||
|
||||
VirtualDirectoryEntryList::VirtualDirectoryEntryList(Model* model)
|
||||
:
|
||||
EntryListBase(),
|
||||
fDefinitionFileRef(),
|
||||
fMergedDirectory(BMergedDirectory::B_ALWAYS_FIRST)
|
||||
{
|
||||
VirtualDirectoryManager* manager = VirtualDirectoryManager::Instance();
|
||||
if (manager == NULL) {
|
||||
fStatus = B_NO_MEMORY;
|
||||
return;
|
||||
}
|
||||
|
||||
AutoLocker<VirtualDirectoryManager> managerLocker(manager);
|
||||
BStringList directoryPaths;
|
||||
fStatus = manager->ResolveDirectoryPaths(*model->NodeRef(),
|
||||
*model->EntryRef(), directoryPaths, &fDefinitionFileRef);
|
||||
if (fStatus != B_OK)
|
||||
return;
|
||||
|
||||
fStatus = _InitMergedDirectory(directoryPaths);
|
||||
}
|
||||
|
||||
|
||||
VirtualDirectoryEntryList::VirtualDirectoryEntryList(
|
||||
const node_ref& definitionFileRef, const BStringList& directoryPaths)
|
||||
:
|
||||
EntryListBase(),
|
||||
fDefinitionFileRef(definitionFileRef),
|
||||
fMergedDirectory(BMergedDirectory::B_ALWAYS_FIRST)
|
||||
{
|
||||
fStatus = _InitMergedDirectory(directoryPaths);
|
||||
}
|
||||
|
||||
|
||||
VirtualDirectoryEntryList::~VirtualDirectoryEntryList()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VirtualDirectoryEntryList::InitCheck() const
|
||||
{
|
||||
return EntryListBase::InitCheck();
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VirtualDirectoryEntryList::GetNextEntry(BEntry* entry, bool traverse)
|
||||
{
|
||||
entry_ref ref;
|
||||
status_t error = GetNextRef(&ref);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
return entry->SetTo(&ref, traverse);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VirtualDirectoryEntryList::GetNextRef(entry_ref* ref)
|
||||
{
|
||||
BPrivate::Storage::LongDirEntry entry;
|
||||
int32 result = GetNextDirents(&entry, sizeof(entry), 1);
|
||||
if (result < 0)
|
||||
return result;
|
||||
if (result == 0)
|
||||
return B_ENTRY_NOT_FOUND;
|
||||
|
||||
ref->device = entry.d_pdev;
|
||||
ref->directory = entry.d_pino;
|
||||
return ref->set_name(entry.d_name);
|
||||
}
|
||||
|
||||
|
||||
int32
|
||||
VirtualDirectoryEntryList::GetNextDirents(struct dirent* buffer, size_t length,
|
||||
int32 count)
|
||||
{
|
||||
if (count > 1)
|
||||
count = 1;
|
||||
|
||||
int32 countRead = fMergedDirectory.GetNextDirents(buffer, length, count);
|
||||
if (countRead != 1)
|
||||
return countRead;
|
||||
|
||||
// deal with directories
|
||||
entry_ref ref;
|
||||
ref.device = buffer->d_pdev;
|
||||
ref.directory = buffer->d_pino;
|
||||
if (ref.set_name(buffer->d_name) == B_OK && BEntry(&ref).IsDirectory()) {
|
||||
if (VirtualDirectoryManager* manager
|
||||
= VirtualDirectoryManager::Instance()) {
|
||||
AutoLocker<VirtualDirectoryManager> managerLocker(manager);
|
||||
manager->TranslateDirectoryEntry(fDefinitionFileRef, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
return countRead;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VirtualDirectoryEntryList::Rewind()
|
||||
{
|
||||
return fMergedDirectory.Rewind();
|
||||
}
|
||||
|
||||
|
||||
int32
|
||||
VirtualDirectoryEntryList::CountEntries()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VirtualDirectoryEntryList::_InitMergedDirectory(
|
||||
const BStringList& directoryPaths)
|
||||
{
|
||||
status_t error = fMergedDirectory.Init();
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
int32 count = directoryPaths.CountStrings();
|
||||
for (int32 i = 0; i < count; i++)
|
||||
fMergedDirectory.AddDirectory(directoryPaths.StringAt(i));
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
} // namespace BPrivate
|
57
src/kits/tracker/VirtualDirectoryEntryList.h
Normal file
57
src/kits/tracker/VirtualDirectoryEntryList.h
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2013, Haiku, Inc.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Ingo Weinhold, ingo_weinhold@gmx.de
|
||||
*/
|
||||
#ifndef VIRTUAL_DIRECTORY_ENTRY_LIST_H
|
||||
#define VIRTUAL_DIRECTORY_ENTRY_LIST_H
|
||||
|
||||
|
||||
#include <StringList.h>
|
||||
|
||||
#include <MergedDirectory.h>
|
||||
|
||||
#include "EntryIterator.h"
|
||||
|
||||
|
||||
namespace BPrivate {
|
||||
|
||||
|
||||
class Model;
|
||||
|
||||
|
||||
class VirtualDirectoryEntryList : public EntryListBase {
|
||||
public:
|
||||
VirtualDirectoryEntryList(Model* model);
|
||||
VirtualDirectoryEntryList(
|
||||
const node_ref& definitionFileRef,
|
||||
const BStringList& directoryPaths);
|
||||
virtual ~VirtualDirectoryEntryList();
|
||||
|
||||
virtual status_t InitCheck() const;
|
||||
|
||||
virtual status_t GetNextEntry(BEntry* entry,
|
||||
bool traverse = false);
|
||||
virtual status_t GetNextRef(entry_ref* ref);
|
||||
virtual int32 GetNextDirents(struct dirent* buffer,
|
||||
size_t length, int32 count = INT_MAX);
|
||||
|
||||
virtual status_t Rewind();
|
||||
virtual int32 CountEntries();
|
||||
|
||||
private:
|
||||
|
||||
status_t _InitMergedDirectory(
|
||||
const BStringList& directoryPaths);
|
||||
private:
|
||||
node_ref fDefinitionFileRef;
|
||||
BMergedDirectory fMergedDirectory;
|
||||
};
|
||||
|
||||
|
||||
} // namespace BPrivate
|
||||
|
||||
|
||||
#endif // VIRTUAL_DIRECTORY_ENTRY_LIST_H
|
830
src/kits/tracker/VirtualDirectoryManager.cpp
Normal file
830
src/kits/tracker/VirtualDirectoryManager.cpp
Normal file
@ -0,0 +1,830 @@
|
||||
/*
|
||||
* Copyright 2013, Haiku, Inc.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Ingo Weinhold, ingo_weinhold@gmx.de
|
||||
*/
|
||||
|
||||
|
||||
#include "VirtualDirectoryManager.h"
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <new>
|
||||
|
||||
#include <Directory.h>
|
||||
#include <File.h>
|
||||
#include <StringList.h>
|
||||
|
||||
#include <AutoDeleter.h>
|
||||
#include <AutoLocker.h>
|
||||
#include <DriverSettings.h>
|
||||
#include <NotOwningEntryRef.h>
|
||||
#include <Uuid.h>
|
||||
|
||||
#include "MimeTypes.h"
|
||||
#include "Model.h"
|
||||
|
||||
|
||||
namespace BPrivate {
|
||||
|
||||
|
||||
static const size_t kMaxVirtualDirectoryFileSize = 10 * 1024;
|
||||
static const char* const kTemporaryDefinitionFileBaseDirectoryPath
|
||||
= "/tmp/tracker/virtual-directories";
|
||||
|
||||
|
||||
static bool
|
||||
operator<(const node_ref& a, const node_ref& b)
|
||||
{
|
||||
if (a.device != b.device)
|
||||
return a.device < b.device;
|
||||
return a.node < b.node;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - Info
|
||||
|
||||
|
||||
class VirtualDirectoryManager::Info {
|
||||
private:
|
||||
typedef BObjectList<Info> InfoList;
|
||||
|
||||
public:
|
||||
Info(RootInfo* root, Info* parent, const BString& path,
|
||||
const node_ref& definitionFileNodeRef,
|
||||
const entry_ref& definitionFileEntryRef)
|
||||
:
|
||||
fRoot(root),
|
||||
fParent(parent),
|
||||
fPath(path),
|
||||
fDefinitionFileNodeRef(definitionFileNodeRef),
|
||||
fDefinitionFileEntryRef(definitionFileEntryRef),
|
||||
fId(),
|
||||
fChildDefinitionsDirectoryRef(-1, -1),
|
||||
fChildren(10, true)
|
||||
{
|
||||
}
|
||||
|
||||
~Info()
|
||||
{
|
||||
}
|
||||
|
||||
RootInfo* Root() const
|
||||
{
|
||||
return fRoot;
|
||||
}
|
||||
|
||||
Info* Parent() const
|
||||
{
|
||||
return fParent;
|
||||
}
|
||||
|
||||
const char* Name() const
|
||||
{
|
||||
return fDefinitionFileEntryRef.name;
|
||||
}
|
||||
|
||||
const BString& Path() const
|
||||
{
|
||||
return fPath;
|
||||
}
|
||||
|
||||
const node_ref& DefinitionFileNodeRef() const
|
||||
{
|
||||
return fDefinitionFileNodeRef;
|
||||
}
|
||||
|
||||
const entry_ref& DefinitionFileEntryRef() const
|
||||
{
|
||||
return fDefinitionFileEntryRef;
|
||||
}
|
||||
|
||||
const InfoList& Children() const
|
||||
{
|
||||
return fChildren;
|
||||
}
|
||||
|
||||
const BString& Id() const
|
||||
{
|
||||
return fId;
|
||||
}
|
||||
|
||||
void SetId(const BString& id)
|
||||
{
|
||||
fId = id;
|
||||
}
|
||||
|
||||
const node_ref& ChildDefinitionsDirectoryRef() const
|
||||
{
|
||||
return fChildDefinitionsDirectoryRef;
|
||||
}
|
||||
|
||||
void SetChildDefinitionsDirectoryRef(const node_ref& ref)
|
||||
{
|
||||
fChildDefinitionsDirectoryRef = ref;
|
||||
}
|
||||
|
||||
Info* GetChild(const char* name) const
|
||||
{
|
||||
for (int32 i = 0; Info* child = fChildren.ItemAt(i); i++) {
|
||||
if (strcmp(name, child->Name()) == 0)
|
||||
return child;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Info* CreateChild(const node_ref& definitionFileNodeRef,
|
||||
const entry_ref& definitionFileEntryRef)
|
||||
{
|
||||
BString path;
|
||||
if (fPath.IsEmpty()) {
|
||||
path = definitionFileEntryRef.name;
|
||||
} else {
|
||||
path.SetToFormat("%s/%s", fPath.String(),
|
||||
definitionFileEntryRef.name);
|
||||
}
|
||||
if (path.IsEmpty())
|
||||
return NULL;
|
||||
|
||||
Info* info = new(std::nothrow) Info(fRoot, this, path,
|
||||
definitionFileNodeRef, definitionFileEntryRef);
|
||||
if (info == NULL || !fChildren.AddItem(info)) {
|
||||
delete info;
|
||||
return NULL;
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
bool DeleteChild(Info* info)
|
||||
{
|
||||
return fChildren.RemoveItem(info, true);
|
||||
}
|
||||
|
||||
void DeleteChildAt(int32 index)
|
||||
{
|
||||
delete fChildren.RemoveItemAt(index);
|
||||
}
|
||||
|
||||
private:
|
||||
RootInfo* fRoot;
|
||||
Info* fParent;
|
||||
BString fPath;
|
||||
node_ref fDefinitionFileNodeRef;
|
||||
entry_ref fDefinitionFileEntryRef;
|
||||
BString fId;
|
||||
node_ref fChildDefinitionsDirectoryRef;
|
||||
InfoList fChildren;
|
||||
};
|
||||
|
||||
|
||||
// #pragma mark - RootInfo
|
||||
|
||||
|
||||
class VirtualDirectoryManager::RootInfo {
|
||||
public:
|
||||
RootInfo(const node_ref& definitionFileNodeRef,
|
||||
const entry_ref& definitionFileEntryRef)
|
||||
:
|
||||
fDirectoryPaths(),
|
||||
fInfo(new(std::nothrow) VirtualDirectoryManager::Info(this, NULL,
|
||||
BString(), definitionFileNodeRef, definitionFileEntryRef)),
|
||||
fFileTime(-1),
|
||||
fLastChangeTime(-1)
|
||||
{
|
||||
}
|
||||
|
||||
~RootInfo()
|
||||
{
|
||||
delete fInfo;
|
||||
}
|
||||
|
||||
status_t InitCheck() const
|
||||
{
|
||||
return fInfo != NULL ? B_OK : B_NO_MEMORY;
|
||||
}
|
||||
|
||||
bigtime_t FileTime() const
|
||||
{
|
||||
return fFileTime;
|
||||
}
|
||||
|
||||
bigtime_t LastChangeTime() const
|
||||
{
|
||||
return fLastChangeTime;
|
||||
}
|
||||
|
||||
const BStringList& DirectoryPaths() const
|
||||
{
|
||||
return fDirectoryPaths;
|
||||
}
|
||||
|
||||
status_t ReadDefinition(bool* _changed = NULL)
|
||||
{
|
||||
// open the definition file
|
||||
BFile file;
|
||||
status_t error = file.SetTo(&fInfo->DefinitionFileEntryRef(),
|
||||
B_READ_ONLY);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
struct stat st;
|
||||
error = file.GetStat(&st);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
bigtime_t fileTime = st.st_mtim.tv_sec * 1000000
|
||||
+ st.st_mtim.tv_nsec / 1000;
|
||||
if (fileTime == fFileTime) {
|
||||
if (_changed != NULL)
|
||||
*_changed = false;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
if (node_ref(st.st_dev, st.st_ino) != fInfo->DefinitionFileNodeRef())
|
||||
return B_ENTRY_NOT_FOUND;
|
||||
|
||||
// read the contents
|
||||
off_t fileSize = st.st_size;
|
||||
if (fileSize > (off_t)kMaxVirtualDirectoryFileSize)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
char* buffer = new(std::nothrow) char[fileSize + 1];
|
||||
if (buffer == NULL)
|
||||
return B_NO_MEMORY;
|
||||
ArrayDeleter<char> bufferDeleter(buffer);
|
||||
|
||||
ssize_t bytesRead = file.ReadAt(0, buffer, fileSize);
|
||||
if (bytesRead < 0)
|
||||
return bytesRead;
|
||||
|
||||
buffer[bytesRead] = '\0';
|
||||
|
||||
// parse it
|
||||
BStringList oldDirectoryPaths(fDirectoryPaths);
|
||||
fDirectoryPaths.MakeEmpty();
|
||||
|
||||
BDriverSettings driverSettings;
|
||||
error = driverSettings.SetToString(buffer);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
BDriverParameterIterator it
|
||||
= driverSettings.ParameterIterator("directory");
|
||||
while (it.HasNext()) {
|
||||
BDriverParameter parameter = it.Next();
|
||||
for (int32 i = 0; i < parameter.CountValues(); i++)
|
||||
fDirectoryPaths.Add(parameter.ValueAt(i));
|
||||
}
|
||||
|
||||
// update file time and check whether something has changed
|
||||
fFileTime = fileTime;
|
||||
|
||||
bool changed = fDirectoryPaths != oldDirectoryPaths;
|
||||
if (changed || fLastChangeTime < 0)
|
||||
fLastChangeTime = fFileTime;
|
||||
|
||||
if (_changed != NULL)
|
||||
*_changed = changed;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
VirtualDirectoryManager::Info* Info() const
|
||||
{
|
||||
return fInfo;
|
||||
}
|
||||
|
||||
private:
|
||||
typedef std::map<BString, VirtualDirectoryManager::Info*> InfoMap;
|
||||
|
||||
private:
|
||||
BStringList fDirectoryPaths;
|
||||
VirtualDirectoryManager::Info* fInfo;
|
||||
bigtime_t fFileTime;
|
||||
// actual file modified time
|
||||
bigtime_t fLastChangeTime;
|
||||
// last time something actually changed
|
||||
};
|
||||
|
||||
|
||||
// #pragma mark - VirtualDirectoryManager
|
||||
|
||||
|
||||
VirtualDirectoryManager::VirtualDirectoryManager()
|
||||
:
|
||||
fLock("virtual directory manager")
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
/*static*/ VirtualDirectoryManager*
|
||||
VirtualDirectoryManager::Instance()
|
||||
{
|
||||
static VirtualDirectoryManager* manager
|
||||
= new(std::nothrow) VirtualDirectoryManager;
|
||||
return manager;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VirtualDirectoryManager::ResolveDirectoryPaths(
|
||||
const node_ref& definitionFileNodeRef,
|
||||
const entry_ref& definitionFileEntryRef, BStringList& _directoryPaths,
|
||||
node_ref* _definitionFileNodeRef, entry_ref* _definitionFileEntryRef)
|
||||
{
|
||||
Info* info = _InfoForNodeRef(definitionFileNodeRef);
|
||||
if (info == NULL) {
|
||||
status_t error = _ResolveUnknownDefinitionFile(definitionFileNodeRef,
|
||||
definitionFileEntryRef, info);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
}
|
||||
|
||||
const BString& subDirectory = info->Path();
|
||||
const BStringList& rootDirectoryPaths = info->Root()->DirectoryPaths();
|
||||
if (subDirectory.IsEmpty()) {
|
||||
_directoryPaths = rootDirectoryPaths;
|
||||
} else {
|
||||
_directoryPaths.MakeEmpty();
|
||||
int32 count = rootDirectoryPaths.CountStrings();
|
||||
for (int32 i = 0; i < count; i++) {
|
||||
BString path = rootDirectoryPaths.StringAt(i);
|
||||
_directoryPaths.Add(path << '/' << subDirectory);
|
||||
}
|
||||
}
|
||||
|
||||
if (_definitionFileEntryRef != NULL) {
|
||||
*_definitionFileEntryRef = info->DefinitionFileEntryRef();
|
||||
if (_definitionFileEntryRef->name == NULL)
|
||||
return B_NO_MEMORY;
|
||||
}
|
||||
|
||||
if (_definitionFileNodeRef != NULL)
|
||||
*_definitionFileNodeRef = info->DefinitionFileNodeRef();
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
VirtualDirectoryManager::GetDefinitionFileChangeTime(
|
||||
const node_ref& definitionFileRef, bigtime_t& _time) const
|
||||
{
|
||||
Info* info = _InfoForNodeRef(definitionFileRef);
|
||||
if (info == NULL)
|
||||
return false;
|
||||
|
||||
_time = info->Root()->LastChangeTime();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
VirtualDirectoryManager::GetRootDefinitionFile(
|
||||
const node_ref& definitionFileRef, node_ref& _rootDefinitionFileRef)
|
||||
{
|
||||
Info* info = _InfoForNodeRef(definitionFileRef);
|
||||
if (info == NULL)
|
||||
return false;
|
||||
|
||||
_rootDefinitionFileRef = info->Root()->Info()->DefinitionFileNodeRef();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
VirtualDirectoryManager::GetSubDirectoryDefinitionFile(
|
||||
const node_ref& baseDefinitionRef, const char* subDirName,
|
||||
entry_ref& _entryRef, node_ref& _nodeRef)
|
||||
{
|
||||
Info* parentInfo = _InfoForNodeRef(baseDefinitionRef);
|
||||
if (parentInfo == NULL)
|
||||
return false;
|
||||
|
||||
Info* info = parentInfo->GetChild(subDirName);
|
||||
if (info == NULL)
|
||||
return false;
|
||||
|
||||
_entryRef = info->DefinitionFileEntryRef();
|
||||
_nodeRef = info->DefinitionFileNodeRef();
|
||||
return _entryRef.name != NULL;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
VirtualDirectoryManager::GetParentDirectoryDefinitionFile(
|
||||
const node_ref& subDirDefinitionRef, entry_ref& _entryRef,
|
||||
node_ref& _nodeRef)
|
||||
{
|
||||
Info* info = _InfoForNodeRef(subDirDefinitionRef);
|
||||
if (info == NULL)
|
||||
return false;
|
||||
|
||||
Info* parentInfo = info->Parent();
|
||||
if (parentInfo == NULL)
|
||||
return false;
|
||||
|
||||
_entryRef = parentInfo->DefinitionFileEntryRef();
|
||||
_nodeRef = parentInfo->DefinitionFileNodeRef();
|
||||
return _entryRef.name != NULL;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VirtualDirectoryManager::TranslateDirectoryEntry(
|
||||
const node_ref& definitionFileRef, dirent* buffer)
|
||||
{
|
||||
NotOwningEntryRef entryRef(buffer->d_pdev, buffer->d_pino, buffer->d_name);
|
||||
node_ref nodeRef(buffer->d_dev, buffer->d_ino);
|
||||
|
||||
status_t error = TranslateDirectoryEntry(definitionFileRef, entryRef,
|
||||
nodeRef);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
buffer->d_pdev = entryRef.device;
|
||||
buffer->d_pino = entryRef.directory;
|
||||
buffer->d_dev = nodeRef.device;
|
||||
buffer->d_ino = nodeRef.node;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VirtualDirectoryManager::TranslateDirectoryEntry(
|
||||
const node_ref& definitionFileRef, entry_ref& _entryRef, node_ref& _nodeRef)
|
||||
{
|
||||
Info* parentInfo = _InfoForNodeRef(definitionFileRef);
|
||||
if (parentInfo == NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
// get the info for the entry
|
||||
Info* info = parentInfo->GetChild(_entryRef.name);
|
||||
if (info == NULL) {
|
||||
// If not done yet, create a directory for the parent, where we can
|
||||
// place the new definition file.
|
||||
if (parentInfo->Id().IsEmpty()) {
|
||||
BString id = BUuid().SetToRandom().ToString();
|
||||
if (id.IsEmpty())
|
||||
return B_NO_MEMORY;
|
||||
|
||||
BPath path(kTemporaryDefinitionFileBaseDirectoryPath, id);
|
||||
status_t error = path.InitCheck();
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
error = create_directory(path.Path(),
|
||||
S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
struct stat st;
|
||||
if (lstat(path.Path(), &st) != 0)
|
||||
return errno;
|
||||
|
||||
parentInfo->SetId(id);
|
||||
parentInfo->SetChildDefinitionsDirectoryRef(
|
||||
node_ref(st.st_dev, st.st_ino));
|
||||
}
|
||||
|
||||
// create the definition file
|
||||
const node_ref& directoryRef
|
||||
= parentInfo->ChildDefinitionsDirectoryRef();
|
||||
NotOwningEntryRef entryRef(directoryRef, _entryRef.name);
|
||||
|
||||
BFile definitionFile;
|
||||
status_t error = definitionFile.SetTo(&entryRef,
|
||||
B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
node_ref nodeRef;
|
||||
error = definitionFile.GetNodeRef(&nodeRef);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
BNodeInfo nodeInfo(&definitionFile);
|
||||
error = nodeInfo.SetType(kVirtualDirectoryMimeType);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
// create the info
|
||||
info = parentInfo->CreateChild(nodeRef, entryRef);
|
||||
if (info == NULL || !_AddInfo(info))
|
||||
return B_NO_MEMORY;
|
||||
|
||||
// Write some info into the definition file that helps us to find the
|
||||
// root definition file. This is only necessary when definition file
|
||||
// entry refs are transferred between applications. Then the receiving
|
||||
// application may need to find the root definition file and resolve
|
||||
// the subdirectories.
|
||||
const entry_ref& rootEntryRef
|
||||
= parentInfo->Root()->Info()->DefinitionFileEntryRef();
|
||||
BString definitionFileContent;
|
||||
definitionFileContent.SetToFormat(
|
||||
"root {\n"
|
||||
" device %" B_PRIdDEV "\n"
|
||||
" directory %" B_PRIdINO "\n"
|
||||
" name \"%s\"\n"
|
||||
"}\n"
|
||||
"subdir \"%s\"\n",
|
||||
rootEntryRef.device, rootEntryRef.directory, rootEntryRef.name,
|
||||
info->Path().String());
|
||||
// failure is not nice, but not mission critical for this application
|
||||
if (!definitionFileContent.IsEmpty()) {
|
||||
definitionFile.WriteAt(0,
|
||||
definitionFileContent.String(), definitionFileContent.Length());
|
||||
}
|
||||
}
|
||||
|
||||
const entry_ref& entryRef = info->DefinitionFileEntryRef();
|
||||
_nodeRef = info->DefinitionFileNodeRef();
|
||||
_entryRef.device = entryRef.device;
|
||||
_entryRef.directory = entryRef.directory;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
VirtualDirectoryManager::DefinitionFileChanged(
|
||||
const node_ref& definitionFileRef)
|
||||
{
|
||||
Info* info = _InfoForNodeRef(definitionFileRef);
|
||||
if (info == NULL)
|
||||
return false;
|
||||
|
||||
_UpdateTree(info->Root());
|
||||
|
||||
return _InfoForNodeRef(definitionFileRef) != NULL;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VirtualDirectoryManager::DirectoryRemoved(const node_ref& definitionFileRef)
|
||||
{
|
||||
Info* info = _InfoForNodeRef(definitionFileRef);
|
||||
if (info == NULL)
|
||||
return B_ENTRY_NOT_FOUND;
|
||||
|
||||
_RemoveDirectory(info);
|
||||
|
||||
// delete the info
|
||||
if (info->Parent() == NULL)
|
||||
delete info->Root();
|
||||
else
|
||||
info->Parent()->DeleteChild(info);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/*static*/ bool
|
||||
VirtualDirectoryManager::GetEntry(const BStringList& directoryPaths,
|
||||
const char* name, entry_ref* _ref, struct stat* _st)
|
||||
{
|
||||
int32 count = directoryPaths.CountStrings();
|
||||
for (int32 i = 0; i < count; i++) {
|
||||
BPath path;
|
||||
if (path.SetTo(directoryPaths.StringAt(i), name) != B_OK)
|
||||
continue;
|
||||
|
||||
struct stat st;
|
||||
if (lstat(path.Path(), &st) == 0) {
|
||||
if (_ref != NULL) {
|
||||
if (get_ref_for_path(path.Path(), _ref) != B_OK)
|
||||
return false;
|
||||
}
|
||||
if (_st != NULL)
|
||||
*_st = st;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
VirtualDirectoryManager::Info*
|
||||
VirtualDirectoryManager::_InfoForNodeRef(const node_ref& nodeRef) const
|
||||
{
|
||||
NodeRefInfoMap::const_iterator it = fInfos.find(nodeRef);
|
||||
return it != fInfos.end() ? it->second : NULL;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
VirtualDirectoryManager::_AddInfo(Info* info)
|
||||
{
|
||||
try {
|
||||
fInfos[info->DefinitionFileNodeRef()] = info;
|
||||
return true;
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VirtualDirectoryManager::_RemoveInfo(Info* info)
|
||||
{
|
||||
NodeRefInfoMap::iterator it = fInfos.find(info->DefinitionFileNodeRef());
|
||||
if (it != fInfos.end())
|
||||
fInfos.erase(it);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VirtualDirectoryManager::_UpdateTree(RootInfo* root)
|
||||
{
|
||||
bool changed = false;
|
||||
status_t error = root->ReadDefinition(&changed);
|
||||
if (error != B_OK) {
|
||||
DirectoryRemoved(root->Info()->DefinitionFileNodeRef());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!changed)
|
||||
return;
|
||||
|
||||
_UpdateTree(root->Info());
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VirtualDirectoryManager::_UpdateTree(Info* info)
|
||||
{
|
||||
const BStringList& directoryPaths = info->Root()->DirectoryPaths();
|
||||
|
||||
int32 childCount = info->Children().CountItems();
|
||||
for (int32 i = childCount -1; i >= 0; i--) {
|
||||
Info* childInfo = info->Children().ItemAt(i);
|
||||
struct stat st;
|
||||
if (GetEntry(directoryPaths, childInfo->Path(), NULL, &st)
|
||||
&& S_ISDIR(st.st_mode)) {
|
||||
_UpdateTree(childInfo);
|
||||
} else {
|
||||
_RemoveDirectory(childInfo);
|
||||
info->DeleteChildAt(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VirtualDirectoryManager::_RemoveDirectory(Info* info)
|
||||
{
|
||||
// recursively remove the subdirectories
|
||||
for (int32 i = 0; Info* child = info->Children().ItemAt(i); i++)
|
||||
_RemoveDirectory(info);
|
||||
|
||||
// remove the directory for the child definition file
|
||||
if (!info->Id().IsEmpty()) {
|
||||
BPath path(kTemporaryDefinitionFileBaseDirectoryPath, info->Id());
|
||||
if (path.InitCheck() == B_OK)
|
||||
rmdir(path.Path());
|
||||
}
|
||||
|
||||
// unless this is the root directory, remove the definition file
|
||||
if (info != info->Root()->Info())
|
||||
BEntry(&info->DefinitionFileEntryRef()).Remove();
|
||||
|
||||
_RemoveInfo(info);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VirtualDirectoryManager::_ResolveUnknownDefinitionFile(
|
||||
const node_ref& definitionFileNodeRef,
|
||||
const entry_ref& definitionFileEntryRef, Info*& _info)
|
||||
{
|
||||
// This is either a root definition file or a subdir definition file
|
||||
// created by another application. We'll just try to read the info from the
|
||||
// file that a subdir definition file would contain. If that fails, we
|
||||
// assume a root definition file.
|
||||
entry_ref entryRef;
|
||||
BString subDirPath;
|
||||
if (_ReadSubDirectoryDefinitionFileInfo(definitionFileEntryRef, entryRef,
|
||||
subDirPath) != B_OK) {
|
||||
return _CreateRootInfo(definitionFileNodeRef, definitionFileEntryRef,
|
||||
_info);
|
||||
}
|
||||
|
||||
if (subDirPath.IsEmpty())
|
||||
return B_BAD_VALUE;
|
||||
|
||||
// get the root definition file node ref
|
||||
node_ref nodeRef;
|
||||
status_t error = BEntry(&entryRef).GetNodeRef(&nodeRef);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
// resolve/create the root info
|
||||
Info* info = _InfoForNodeRef(nodeRef);
|
||||
if (info == NULL) {
|
||||
error = _CreateRootInfo(nodeRef, entryRef, info);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
} else if (info->Root()->Info() != info)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
const BStringList& rootDirectoryPaths = info->Root()->DirectoryPaths();
|
||||
|
||||
// now we can traverse the subdir path and resolve all infos along the way
|
||||
int32 nextComponentOffset = 0;
|
||||
while (nextComponentOffset < subDirPath.Length()) {
|
||||
int32 componentEnd = subDirPath.FindFirst('/', nextComponentOffset);
|
||||
if (componentEnd >= 0) {
|
||||
// skip duplicate '/'s
|
||||
if (componentEnd == nextComponentOffset + 1) {
|
||||
nextComponentOffset = componentEnd;
|
||||
continue;
|
||||
}
|
||||
nextComponentOffset = componentEnd + 1;
|
||||
} else {
|
||||
componentEnd = subDirPath.Length();
|
||||
nextComponentOffset = componentEnd;
|
||||
}
|
||||
|
||||
BString entryPath(subDirPath, componentEnd);
|
||||
if (entryPath.IsEmpty())
|
||||
return B_NO_MEMORY;
|
||||
|
||||
struct stat st;
|
||||
if (!GetEntry(rootDirectoryPaths, entryPath, &entryRef, &st))
|
||||
return B_ENTRY_NOT_FOUND;
|
||||
|
||||
if (!S_ISDIR(st.st_mode))
|
||||
return B_BAD_VALUE;
|
||||
|
||||
error = TranslateDirectoryEntry(info->DefinitionFileNodeRef(), entryRef,
|
||||
nodeRef);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
info = _InfoForNodeRef(nodeRef);
|
||||
}
|
||||
|
||||
_info = info;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VirtualDirectoryManager::_CreateRootInfo(const node_ref& definitionFileNodeRef,
|
||||
const entry_ref& definitionFileEntryRef, Info*& _info)
|
||||
{
|
||||
RootInfo* root = new(std::nothrow) RootInfo(definitionFileNodeRef,
|
||||
definitionFileEntryRef);
|
||||
if (root == NULL || root->InitCheck() != B_OK) {
|
||||
delete root;
|
||||
return B_NO_MEMORY;
|
||||
}
|
||||
ObjectDeleter<RootInfo> rootDeleter(root);
|
||||
|
||||
status_t error = root->ReadDefinition();
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
if (!_AddInfo(root->Info()))
|
||||
return B_NO_MEMORY;
|
||||
|
||||
rootDeleter.Detach();
|
||||
_info = root->Info();
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VirtualDirectoryManager::_ReadSubDirectoryDefinitionFileInfo(
|
||||
const entry_ref& entryRef, entry_ref& _rootDefinitionFileEntryRef,
|
||||
BString& _subDirPath)
|
||||
{
|
||||
BDriverSettings driverSettings;
|
||||
status_t error = driverSettings.Load(entryRef);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
const char* subDirPath = driverSettings.GetParameterValue("subdir");
|
||||
if (subDirPath == NULL || subDirPath[0] == '\0')
|
||||
return B_BAD_DATA;
|
||||
|
||||
BDriverParameter rootParameter;
|
||||
if (!driverSettings.FindParameter("root", &rootParameter))
|
||||
return B_BAD_DATA;
|
||||
|
||||
const char* name = rootParameter.GetParameterValue("name");
|
||||
dev_t device = rootParameter.GetInt32ParameterValue("device", -1, -1);
|
||||
ino_t directory = rootParameter.GetInt64ParameterValue("directory");
|
||||
if (name == NULL || name[0] == '\0' || device < 0)
|
||||
return B_BAD_DATA;
|
||||
|
||||
_rootDefinitionFileEntryRef = entry_ref(device, directory, name);
|
||||
_subDirPath = subDirPath;
|
||||
return !_subDirPath.IsEmpty() && _rootDefinitionFileEntryRef.name != NULL
|
||||
? B_OK : B_NO_MEMORY;
|
||||
}
|
||||
|
||||
|
||||
} // namespace BPrivate
|
117
src/kits/tracker/VirtualDirectoryManager.h
Normal file
117
src/kits/tracker/VirtualDirectoryManager.h
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright 2013, Haiku, Inc.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Ingo Weinhold, ingo_weinhold@gmx.de
|
||||
*/
|
||||
#ifndef VIRTUAL_DIRECTORY_MANAGER_H
|
||||
#define VIRTUAL_DIRECTORY_MANAGER_H
|
||||
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
#include <Locker.h>
|
||||
#include <Node.h>
|
||||
|
||||
|
||||
class BStringList;
|
||||
|
||||
|
||||
namespace BPrivate {
|
||||
|
||||
|
||||
class Model;
|
||||
|
||||
|
||||
class VirtualDirectoryManager {
|
||||
public:
|
||||
static VirtualDirectoryManager* Instance();
|
||||
|
||||
bool Lock() { return fLock.Lock(); }
|
||||
void Unlock() { fLock.Unlock(); }
|
||||
|
||||
status_t ResolveDirectoryPaths(
|
||||
const node_ref& definitionFileNodeRef,
|
||||
const entry_ref& definitionFileEntryRef,
|
||||
BStringList& _directoryPaths,
|
||||
node_ref* _definitionFileNodeRef = NULL,
|
||||
entry_ref* _definitionFileEntryRef = NULL);
|
||||
|
||||
bool GetDefinitionFileChangeTime(
|
||||
const node_ref& definitionFileRef,
|
||||
bigtime_t& _time) const;
|
||||
|
||||
bool GetRootDefinitionFile(
|
||||
const node_ref& definitionFileRef,
|
||||
node_ref& _rootDefinitionFileRef);
|
||||
bool GetSubDirectoryDefinitionFile(
|
||||
const node_ref& baseDefinitionRef,
|
||||
const char* subDirName,
|
||||
entry_ref& _entryRef, node_ref& _nodeRef);
|
||||
bool GetParentDirectoryDefinitionFile(
|
||||
const node_ref& subDirDefinitionRef,
|
||||
entry_ref& _entryRef, node_ref& _nodeRef);
|
||||
|
||||
status_t TranslateDirectoryEntry(
|
||||
const node_ref& definitionFileRef,
|
||||
dirent* buffer);
|
||||
status_t TranslateDirectoryEntry(
|
||||
const node_ref& definitionFileRef,
|
||||
entry_ref& entryRef, node_ref& _nodeRef);
|
||||
|
||||
bool DefinitionFileChanged(
|
||||
const node_ref& definitionFileRef);
|
||||
// returns whether the directory still
|
||||
// exists
|
||||
status_t DirectoryRemoved(
|
||||
const node_ref& definitionFileRef);
|
||||
|
||||
static bool GetEntry(const BStringList& directoryPaths,
|
||||
const char* name, entry_ref* _ref,
|
||||
struct stat* _st);
|
||||
|
||||
private:
|
||||
class Info;
|
||||
class RootInfo;
|
||||
|
||||
typedef std::map<node_ref, Info*> NodeRefInfoMap;
|
||||
|
||||
private:
|
||||
VirtualDirectoryManager();
|
||||
|
||||
Info* _InfoForNodeRef(const node_ref& nodeRef) const;
|
||||
|
||||
bool _AddInfo(Info* info);
|
||||
void _RemoveInfo(Info* info);
|
||||
|
||||
void _UpdateTree(RootInfo* root);
|
||||
void _UpdateTree(Info* info);
|
||||
|
||||
void _RemoveDirectory(Info* info);
|
||||
|
||||
status_t _ResolveUnknownDefinitionFile(
|
||||
const node_ref& definitionFileNodeRef,
|
||||
const entry_ref& definitionFileEntryRef,
|
||||
Info*& _info);
|
||||
status_t _CreateRootInfo(
|
||||
const node_ref& definitionFileNodeRef,
|
||||
const entry_ref& definitionFileEntryRef,
|
||||
Info*& _info);
|
||||
status_t _ReadSubDirectoryDefinitionFileInfo(
|
||||
const entry_ref& entryRef,
|
||||
entry_ref& _rootDefinitionFileEntryRef,
|
||||
BString& _subDirPath);
|
||||
|
||||
private:
|
||||
BLocker fLock;
|
||||
NodeRefInfoMap fInfos;
|
||||
};
|
||||
|
||||
|
||||
} // namespace BPrivate
|
||||
|
||||
|
||||
#endif // VIRTUAL_DIRECTORY_MANAGER_H
|
502
src/kits/tracker/VirtualDirectoryPoseView.cpp
Normal file
502
src/kits/tracker/VirtualDirectoryPoseView.cpp
Normal file
@ -0,0 +1,502 @@
|
||||
/*
|
||||
* Copyright 2013, Haiku, Inc.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Ingo Weinhold, ingo_weinhold@gmx.de
|
||||
*/
|
||||
|
||||
|
||||
#include "VirtualDirectoryPoseView.h"
|
||||
|
||||
#include <new>
|
||||
|
||||
#include <AutoLocker.h>
|
||||
#include <NotOwningEntryRef.h>
|
||||
#include <PathMonitor.h>
|
||||
#include <storage_support.h>
|
||||
|
||||
#include "Commands.h"
|
||||
#include "Tracker.h"
|
||||
#include "VirtualDirectoryEntryList.h"
|
||||
#include "VirtualDirectoryManager.h"
|
||||
|
||||
|
||||
namespace BPrivate {
|
||||
|
||||
|
||||
VirtualDirectoryPoseView::VirtualDirectoryPoseView(Model* model, BRect frame,
|
||||
uint32 resizeMask)
|
||||
:
|
||||
BPoseView(model, frame, resizeMask),
|
||||
fDirectoryPaths(),
|
||||
fRootDefinitionFileRef(-1, -1),
|
||||
fFileChangeTime(-1),
|
||||
fIsRoot(false)
|
||||
{
|
||||
VirtualDirectoryManager* manager = VirtualDirectoryManager::Instance();
|
||||
if (manager == NULL)
|
||||
return;
|
||||
|
||||
AutoLocker<VirtualDirectoryManager> managerLocker(manager);
|
||||
if (_UpdateDirectoryPaths() != B_OK)
|
||||
return;
|
||||
|
||||
manager->GetRootDefinitionFile(*model->NodeRef(), fRootDefinitionFileRef);
|
||||
fIsRoot = fRootDefinitionFileRef == *model->NodeRef();
|
||||
}
|
||||
|
||||
|
||||
VirtualDirectoryPoseView::~VirtualDirectoryPoseView()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VirtualDirectoryPoseView::MessageReceived(BMessage* message)
|
||||
{
|
||||
switch (message->what) {
|
||||
// ignore all edit operations
|
||||
case B_CUT:
|
||||
case kCutMoreSelectionToClipboard:
|
||||
case kDuplicateSelection:
|
||||
case kDelete:
|
||||
case kMoveToTrash:
|
||||
case kNewEntryFromTemplate:
|
||||
case kNewFolder:
|
||||
case kEditItem:
|
||||
break;
|
||||
|
||||
default:
|
||||
_inherited::MessageReceived(message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VirtualDirectoryPoseView::AttachedToWindow()
|
||||
{
|
||||
_inherited::AttachedToWindow();
|
||||
SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
|
||||
SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VirtualDirectoryPoseView::RestoreState(AttributeStreamNode* node)
|
||||
{
|
||||
_inherited::RestoreState(node);
|
||||
fViewState->SetViewMode(kListMode);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VirtualDirectoryPoseView::RestoreState(const BMessage& message)
|
||||
{
|
||||
_inherited::RestoreState(message);
|
||||
fViewState->SetViewMode(kListMode);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VirtualDirectoryPoseView::SavePoseLocations(BRect* frameIfDesktop)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VirtualDirectoryPoseView::SetViewMode(uint32 newMode)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
EntryListBase*
|
||||
VirtualDirectoryPoseView::InitDirentIterator(const entry_ref* ref)
|
||||
{
|
||||
if (fRootDefinitionFileRef.node < 0 || *ref != *TargetModel()->EntryRef())
|
||||
return NULL;
|
||||
|
||||
Model sourceModel(ref, false, true);
|
||||
if (sourceModel.InitCheck() != B_OK)
|
||||
return NULL;
|
||||
|
||||
VirtualDirectoryEntryList* entryList
|
||||
= new(std::nothrow) VirtualDirectoryEntryList(
|
||||
*TargetModel()->NodeRef(), fDirectoryPaths);
|
||||
if (entryList == NULL || entryList->InitCheck() != B_OK) {
|
||||
delete entryList;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return entryList;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VirtualDirectoryPoseView::StartWatching()
|
||||
{
|
||||
// watch the directories
|
||||
int32 count = fDirectoryPaths.CountStrings();
|
||||
for (int32 i = 0; i < count; i++) {
|
||||
BString path = fDirectoryPaths.StringAt(i);
|
||||
BPathMonitor::StartWatching(path, B_WATCH_DIRECTORY, this);
|
||||
}
|
||||
|
||||
// watch the definition file
|
||||
TTracker::WatchNode(TargetModel()->NodeRef(),
|
||||
B_WATCH_NAME | B_WATCH_STAT | B_WATCH_ATTR, this);
|
||||
|
||||
// also watch the root definition file
|
||||
if (!fIsRoot)
|
||||
TTracker::WatchNode(&fRootDefinitionFileRef, B_WATCH_STAT, this);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VirtualDirectoryPoseView::StopWatching()
|
||||
{
|
||||
BPathMonitor::StopWatching(this);
|
||||
stop_watching(this);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
VirtualDirectoryPoseView::FSNotification(const BMessage* message)
|
||||
{
|
||||
switch (message->GetInt32("opcode", 0)) {
|
||||
case B_ENTRY_CREATED:
|
||||
return _EntryCreated(message);
|
||||
|
||||
case B_ENTRY_REMOVED:
|
||||
return _EntryRemoved(message);
|
||||
|
||||
case B_ENTRY_MOVED:
|
||||
return _EntryMoved(message);
|
||||
|
||||
case B_STAT_CHANGED:
|
||||
return _NodeStatChanged(message);
|
||||
|
||||
default:
|
||||
return _inherited::FSNotification(message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
VirtualDirectoryPoseView::_EntryCreated(const BMessage* message)
|
||||
{
|
||||
NotOwningEntryRef entryRef;
|
||||
node_ref nodeRef;
|
||||
|
||||
if (message->FindInt32("device", &nodeRef.device) != B_OK
|
||||
|| message->FindInt64("node", &nodeRef.node) != B_OK
|
||||
|| message->FindInt64("directory", &entryRef.directory) != B_OK
|
||||
|| message->FindString("name", (const char**)&entryRef.name) != B_OK) {
|
||||
return true;
|
||||
}
|
||||
entryRef.device = nodeRef.device;
|
||||
|
||||
// It might be one of our directories.
|
||||
BString path;
|
||||
if (message->FindString("path", &path) == B_OK
|
||||
&& fDirectoryPaths.HasString(path)) {
|
||||
// Iterate through the directory and generate an entry-created message
|
||||
// for each entry.
|
||||
BDirectory directory;
|
||||
if (directory.SetTo(&nodeRef) != B_OK)
|
||||
return true;
|
||||
|
||||
BPrivate::Storage::LongDirEntry entry;
|
||||
while (directory.GetNextDirents(&entry, sizeof(entry), 1) == 1) {
|
||||
if (strcmp(entry.d_name, ".") != 0
|
||||
&& strcmp(entry.d_name, "..") != 0) {
|
||||
_DispatchEntryCreatedOrRemovedMessage(B_ENTRY_CREATED,
|
||||
node_ref(entry.d_dev, entry.d_ino),
|
||||
NotOwningEntryRef(entry.d_pdev, entry.d_pino,
|
||||
entry.d_name),
|
||||
NULL, false);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// See, if this entry actually becomes visible. If not, we can simply ignore
|
||||
// it.
|
||||
struct stat st;
|
||||
entry_ref visibleEntryRef;
|
||||
if (!_GetEntry(entryRef.name, visibleEntryRef, &st)
|
||||
|| visibleEntryRef != entryRef) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If it is a directory, translate it.
|
||||
VirtualDirectoryManager* manager = VirtualDirectoryManager::Instance();
|
||||
AutoLocker<VirtualDirectoryManager> managerLocker(manager);
|
||||
|
||||
bool entryTranslated = S_ISDIR(st.st_mode);
|
||||
if (entryTranslated) {
|
||||
if (manager == NULL)
|
||||
return true;
|
||||
|
||||
if (manager->TranslateDirectoryEntry(*TargetModel()->NodeRef(),
|
||||
entryRef, nodeRef) != B_OK) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// The entry might replace another entry. If it does, we'll fake a removed
|
||||
// message for the old one first.
|
||||
BPose* pose = fPoseList->FindPoseByFileName(entryRef.name);
|
||||
if (pose != NULL) {
|
||||
if (nodeRef == *pose->TargetModel()->NodeRef()) {
|
||||
// apparently not really a new entry -- can happen for
|
||||
// subdirectories
|
||||
return true;
|
||||
}
|
||||
|
||||
// It may be a directory, so tell the manager.
|
||||
if (manager != NULL)
|
||||
manager->DirectoryRemoved(*pose->TargetModel()->NodeRef());
|
||||
|
||||
managerLocker.Unlock();
|
||||
|
||||
BMessage removedMessage(B_NODE_MONITOR);
|
||||
_DispatchEntryCreatedOrRemovedMessage(B_ENTRY_REMOVED,
|
||||
*pose->TargetModel()->NodeRef(), *pose->TargetModel()->EntryRef());
|
||||
} else
|
||||
managerLocker.Unlock();
|
||||
|
||||
return entryTranslated
|
||||
? (_DispatchEntryCreatedOrRemovedMessage(B_ENTRY_CREATED, nodeRef,
|
||||
entryRef), true)
|
||||
: _inherited::FSNotification(message);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
VirtualDirectoryPoseView::_EntryRemoved(const BMessage* message)
|
||||
{
|
||||
NotOwningEntryRef entryRef;
|
||||
node_ref nodeRef;
|
||||
|
||||
if (message->FindInt32("device", &nodeRef.device) != B_OK
|
||||
|| message->FindInt64("node", &nodeRef.node) != B_OK
|
||||
|| message->FindInt64("directory", &entryRef.directory)
|
||||
!= B_OK
|
||||
|| message->FindString("name", (const char**)&entryRef.name) != B_OK) {
|
||||
return true;
|
||||
}
|
||||
entryRef.device = nodeRef.device;
|
||||
|
||||
// It might be our definition file.
|
||||
if (nodeRef == *TargetModel()->NodeRef())
|
||||
return _inherited::FSNotification(message);
|
||||
|
||||
// It might be one of our directories.
|
||||
BString path;
|
||||
if (message->FindString("path", &path) == B_OK
|
||||
&& fDirectoryPaths.HasString(path)) {
|
||||
// Find all poses that stem from that directory and generate an
|
||||
// entry-removed message for each.
|
||||
PoseList poses;
|
||||
for (int32 i = 0; BPose* pose = fPoseList->ItemAt(i); i++) {
|
||||
NotOwningEntryRef poseEntryRef = *pose->TargetModel()->EntryRef();
|
||||
if (poseEntryRef.DirectoryNodeRef() == nodeRef)
|
||||
poses.AddItem(pose);
|
||||
}
|
||||
|
||||
for (int32 i = 0; BPose* pose = poses.ItemAt(i); i++) {
|
||||
_DispatchEntryCreatedOrRemovedMessage(B_ENTRY_REMOVED,
|
||||
*pose->TargetModel()->NodeRef(),
|
||||
*pose->TargetModel()->EntryRef(), NULL, false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// If it is a directory, translate it.
|
||||
entry_ref* actualEntryRef = &entryRef;
|
||||
node_ref* actualNodeRef = &nodeRef;
|
||||
entry_ref definitionEntryRef;
|
||||
node_ref definitionNodeRef;
|
||||
|
||||
VirtualDirectoryManager* manager = VirtualDirectoryManager::Instance();
|
||||
AutoLocker<VirtualDirectoryManager> managerLocker(manager);
|
||||
|
||||
if (manager != NULL
|
||||
&& manager->GetSubDirectoryDefinitionFile(*TargetModel()->NodeRef(),
|
||||
entryRef.name, definitionEntryRef, definitionNodeRef)) {
|
||||
actualEntryRef = &definitionEntryRef;
|
||||
actualNodeRef = &definitionNodeRef;
|
||||
}
|
||||
|
||||
// Check the pose. It might have been an entry that wasn't visible anyway.
|
||||
// In that case we can just ignore the notification.
|
||||
BPose* pose = fPoseList->FindPoseByFileName(actualEntryRef->name);
|
||||
if (pose == NULL || *actualNodeRef != *pose->TargetModel()->NodeRef())
|
||||
return true;
|
||||
|
||||
// See, if another entry becomes visible, now.
|
||||
struct stat st;
|
||||
entry_ref visibleEntryRef;
|
||||
node_ref visibleNodeRef;
|
||||
if (_GetEntry(actualEntryRef->name, visibleEntryRef, &st)) {
|
||||
// If the new entry is a directory, translate it.
|
||||
visibleNodeRef = node_ref(st.st_dev, st.st_ino);
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
if (manager == NULL || manager->TranslateDirectoryEntry(
|
||||
*TargetModel()->NodeRef(), visibleEntryRef, visibleNodeRef)
|
||||
!= B_OK) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Effectively nothing changes, when the removed entry was a
|
||||
// directory as well.
|
||||
if (visibleNodeRef == *actualNodeRef)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (actualEntryRef == &entryRef) {
|
||||
managerLocker.Unlock();
|
||||
if (_inherited::FSNotification(message))
|
||||
pendingNodeMonitorCache.Add(message);
|
||||
} else {
|
||||
// tell the manager that the directory has been removed
|
||||
manager->DirectoryRemoved(*actualNodeRef);
|
||||
managerLocker.Unlock();
|
||||
|
||||
_DispatchEntryCreatedOrRemovedMessage(B_ENTRY_REMOVED, *actualNodeRef,
|
||||
*actualEntryRef);
|
||||
}
|
||||
|
||||
_DispatchEntryCreatedOrRemovedMessage(B_ENTRY_CREATED, visibleNodeRef,
|
||||
visibleEntryRef);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
VirtualDirectoryPoseView::_EntryMoved(const BMessage* message)
|
||||
{
|
||||
NotOwningEntryRef fromEntryRef;
|
||||
NotOwningEntryRef toEntryRef;
|
||||
node_ref nodeRef;
|
||||
|
||||
if (message->FindInt32("node device", &nodeRef.device) != B_OK
|
||||
|| message->FindInt64("node", &nodeRef.node) != B_OK
|
||||
|| message->FindInt32("device", &fromEntryRef.device) != B_OK
|
||||
|| message->FindInt64("from directory", &fromEntryRef.directory) != B_OK
|
||||
|| message->FindInt64("to directory", &toEntryRef.directory) != B_OK
|
||||
|| message->FindString("from name", (const char**)&fromEntryRef.name)
|
||||
!= B_OK
|
||||
|| message->FindString("name", (const char**)&toEntryRef.name)
|
||||
!= B_OK) {
|
||||
return true;
|
||||
}
|
||||
toEntryRef.device = fromEntryRef.device;
|
||||
|
||||
// TODO: That's the lazy approach. Ideally we'd analyze the situation and
|
||||
// forward a B_ENTRY_MOVED, if possible. There are quite a few cases to
|
||||
// consider, though.
|
||||
_DispatchEntryCreatedOrRemovedMessage(B_ENTRY_REMOVED, nodeRef,
|
||||
fromEntryRef, message->GetString("from path", NULL), false);
|
||||
_DispatchEntryCreatedOrRemovedMessage(B_ENTRY_CREATED, nodeRef,
|
||||
toEntryRef, message->GetString("path", NULL), false);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
VirtualDirectoryPoseView::_NodeStatChanged(const BMessage* message)
|
||||
{
|
||||
node_ref nodeRef;
|
||||
if (message->FindInt32("device", &nodeRef.device) != B_OK
|
||||
|| message->FindInt64("node", &nodeRef.node) != B_OK) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (nodeRef == fRootDefinitionFileRef) {
|
||||
if ((message->GetInt32("fields", 0) & B_STAT_MODIFICATION_TIME) != 0) {
|
||||
VirtualDirectoryManager* manager
|
||||
= VirtualDirectoryManager::Instance();
|
||||
if (manager != NULL) {
|
||||
AutoLocker<VirtualDirectoryManager> managerLocker(manager);
|
||||
if (!manager->DefinitionFileChanged(
|
||||
*TargetModel()->NodeRef())) {
|
||||
// The definition file no longer exists. Ignore the message
|
||||
// -- we'll get a remove notification soon.
|
||||
return true;
|
||||
}
|
||||
|
||||
bigtime_t fileChangeTime;
|
||||
manager->GetDefinitionFileChangeTime(*TargetModel()->NodeRef(),
|
||||
fileChangeTime);
|
||||
if (fileChangeTime != fFileChangeTime) {
|
||||
_UpdateDirectoryPaths();
|
||||
managerLocker.Unlock();
|
||||
Refresh();
|
||||
// TODO: Refresh() is rather radical. Or rather its
|
||||
// implementation is. Ideally it would just compare the
|
||||
// currently added poses with what a new dir iterator
|
||||
// returns and remove/add poses as needed.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!fIsRoot)
|
||||
return true;
|
||||
}
|
||||
|
||||
return _inherited::FSNotification(message);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VirtualDirectoryPoseView::_DispatchEntryCreatedOrRemovedMessage(int32 opcode,
|
||||
const node_ref& nodeRef, const entry_ref& entryRef, const char* path,
|
||||
bool dispatchToSuperClass)
|
||||
{
|
||||
BMessage message(B_NODE_MONITOR);
|
||||
message.AddInt32("opcode", opcode);
|
||||
message.AddInt32("device", nodeRef.device);
|
||||
message.AddInt64("node", nodeRef.node);
|
||||
message.AddInt64("directory", entryRef.directory);
|
||||
message.AddString("name", entryRef.name);
|
||||
if (path != NULL && path[0] != '\0')
|
||||
message.AddString("path", path);
|
||||
bool result = dispatchToSuperClass
|
||||
? _inherited::FSNotification(&message)
|
||||
: FSNotification(&message);
|
||||
if (!result)
|
||||
pendingNodeMonitorCache.Add(&message);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
VirtualDirectoryPoseView::_GetEntry(const char* name, entry_ref& _ref,
|
||||
struct stat* _st)
|
||||
{
|
||||
return VirtualDirectoryManager::GetEntry(fDirectoryPaths, name, &_ref, _st);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VirtualDirectoryPoseView::_UpdateDirectoryPaths()
|
||||
{
|
||||
VirtualDirectoryManager* manager = VirtualDirectoryManager::Instance();
|
||||
Model* model = TargetModel();
|
||||
status_t error = manager->ResolveDirectoryPaths(*model->NodeRef(),
|
||||
*model->EntryRef(), fDirectoryPaths);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
|
||||
manager->GetDefinitionFileChangeTime(*model->NodeRef(), fFileChangeTime);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
} // namespace BPrivate
|
77
src/kits/tracker/VirtualDirectoryPoseView.h
Normal file
77
src/kits/tracker/VirtualDirectoryPoseView.h
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2013, Haiku, Inc.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Ingo Weinhold, ingo_weinhold@gmx.de
|
||||
*/
|
||||
#ifndef VIRTUAL_DIRECTORY_POSE_VIEW_H
|
||||
#define VIRTUAL_DIRECTORY_POSE_VIEW_H
|
||||
|
||||
|
||||
#include <StringList.h>
|
||||
|
||||
#include "PoseView.h"
|
||||
|
||||
|
||||
namespace BPrivate {
|
||||
|
||||
|
||||
class VirtualDirectoryEntryList;
|
||||
|
||||
|
||||
class VirtualDirectoryPoseView : public BPoseView {
|
||||
public:
|
||||
VirtualDirectoryPoseView(Model* model,
|
||||
BRect frame,
|
||||
uint32 resizeMask = B_FOLLOW_ALL);
|
||||
virtual ~VirtualDirectoryPoseView();
|
||||
|
||||
virtual void MessageReceived(BMessage* message);
|
||||
|
||||
protected:
|
||||
virtual void AttachedToWindow();
|
||||
virtual void RestoreState(AttributeStreamNode* node);
|
||||
virtual void RestoreState(const BMessage& message);
|
||||
virtual void SavePoseLocations(BRect* frameIfDesktop = NULL);
|
||||
virtual void SetViewMode(uint32 newMode);
|
||||
virtual EntryListBase* InitDirentIterator(const entry_ref* ref);
|
||||
|
||||
virtual void StartWatching();
|
||||
virtual void StopWatching();
|
||||
|
||||
virtual bool FSNotification(const BMessage* message);
|
||||
|
||||
private:
|
||||
typedef BPoseView _inherited;
|
||||
|
||||
private:
|
||||
bool _EntryCreated(const BMessage* message);
|
||||
bool _EntryRemoved(const BMessage* message);
|
||||
bool _EntryMoved(const BMessage* message);
|
||||
bool _NodeStatChanged(const BMessage* message);
|
||||
|
||||
void _DispatchEntryCreatedOrRemovedMessage(
|
||||
int32 opcode, const node_ref& nodeRef,
|
||||
const entry_ref& entryRef,
|
||||
const char* path = NULL,
|
||||
bool dispatchToSuperClass = true);
|
||||
|
||||
bool _GetEntry(const char* name, entry_ref& _ref,
|
||||
struct stat* _st = NULL);
|
||||
|
||||
status_t _UpdateDirectoryPaths();
|
||||
// manager must be locked
|
||||
|
||||
private:
|
||||
BStringList fDirectoryPaths;
|
||||
node_ref fRootDefinitionFileRef;
|
||||
bigtime_t fFileChangeTime;
|
||||
bool fIsRoot;
|
||||
};
|
||||
|
||||
|
||||
} // namespace BPrivate
|
||||
|
||||
|
||||
#endif // VIRTUAL_DIRECTORY_POSE_VIEW_H
|
179
src/kits/tracker/VirtualDirectoryWindow.cpp
Normal file
179
src/kits/tracker/VirtualDirectoryWindow.cpp
Normal file
@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Copyright 2013, Haiku, Inc.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Ingo Weinhold, ingo_weinhold@gmx.de
|
||||
*/
|
||||
/*
|
||||
Open Tracker License
|
||||
|
||||
Terms and Conditions
|
||||
|
||||
Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice applies to all licensees
|
||||
and shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
Except as contained in this notice, the name of Be Incorporated shall not be
|
||||
used in advertising or otherwise to promote the sale, use or other dealings in
|
||||
this Software without prior written authorization from Be Incorporated.
|
||||
|
||||
Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
|
||||
of Be Incorporated in the United States and other countries. Other brand product
|
||||
names are registered trademarks or trademarks of their respective holders.
|
||||
All rights reserved.
|
||||
*/
|
||||
|
||||
|
||||
#include "VirtualDirectoryWindow.h"
|
||||
|
||||
#include <Catalog.h>
|
||||
#include <Locale.h>
|
||||
|
||||
#include <AutoLocker.h>
|
||||
|
||||
#include "Commands.h"
|
||||
#include "VirtualDirectoryManager.h"
|
||||
#include "VirtualDirectoryPoseView.h"
|
||||
|
||||
|
||||
#undef B_TRANSLATION_CONTEXT
|
||||
#define B_TRANSLATION_CONTEXT "VirtualDirectoryWindow"
|
||||
|
||||
|
||||
namespace BPrivate {
|
||||
|
||||
|
||||
VirtualDirectoryWindow::VirtualDirectoryWindow(LockingList<BWindow>* windowList,
|
||||
uint32 containerWindowFlags, window_look look, window_feel feel,
|
||||
uint32 flags, uint32 workspace)
|
||||
:
|
||||
BContainerWindow(windowList, containerWindowFlags, look, feel, flags,
|
||||
workspace)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VirtualDirectoryWindow::CreatePoseView(Model* model)
|
||||
{
|
||||
BRect rect(Bounds());
|
||||
rect.right -= B_V_SCROLL_BAR_WIDTH;
|
||||
rect.bottom -= B_H_SCROLL_BAR_HEIGHT;
|
||||
fPoseView = NewPoseView(model, rect, kListMode);
|
||||
|
||||
AddChild(fPoseView);
|
||||
}
|
||||
|
||||
|
||||
BPoseView*
|
||||
VirtualDirectoryWindow::NewPoseView(Model* model, BRect rect, uint32 viewMode)
|
||||
{
|
||||
// If the model (or rather the entry_ref to it) came from another
|
||||
// application, it may refer to a subdirectory we cannot use directly. The
|
||||
// manager resolves the given refs to new ones, if necessary.
|
||||
VirtualDirectoryManager* manager = VirtualDirectoryManager::Instance();
|
||||
if (manager == NULL)
|
||||
return NULL;
|
||||
|
||||
{
|
||||
AutoLocker<VirtualDirectoryManager> managerLocker(manager);
|
||||
BStringList directoryPaths;
|
||||
node_ref nodeRef;
|
||||
entry_ref entryRef;
|
||||
if (manager->ResolveDirectoryPaths(*model->NodeRef(),
|
||||
*model->EntryRef(), directoryPaths, &nodeRef, &entryRef)
|
||||
!= B_OK) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (nodeRef != *model->NodeRef()) {
|
||||
// Indeed a new file. Create a new model.
|
||||
Model* newModel = new(std::nothrow) Model(&entryRef);
|
||||
if (newModel == NULL || newModel->InitCheck() != B_OK) {
|
||||
delete newModel;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
delete model;
|
||||
model = newModel;
|
||||
}
|
||||
}
|
||||
|
||||
return new VirtualDirectoryPoseView(model, rect);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VirtualDirectoryWindow::AddWindowMenu(BMenu* menu)
|
||||
{
|
||||
BMenuItem* item;
|
||||
|
||||
item = new BMenuItem(B_TRANSLATE("Resize to fit"),
|
||||
new BMessage(kResizeToFit), 'Y');
|
||||
item->SetTarget(this);
|
||||
menu->AddItem(item);
|
||||
|
||||
item = new BMenuItem(B_TRANSLATE("Select"B_UTF8_ELLIPSIS),
|
||||
new BMessage(kShowSelectionWindow), 'A', B_SHIFT_KEY);
|
||||
item->SetTarget(PoseView());
|
||||
menu->AddItem(item);
|
||||
|
||||
item = new BMenuItem(B_TRANSLATE("Select all"),
|
||||
new BMessage(B_SELECT_ALL), 'A');
|
||||
item->SetTarget(PoseView());
|
||||
menu->AddItem(item);
|
||||
|
||||
item = new BMenuItem(B_TRANSLATE("Invert selection"),
|
||||
new BMessage(kInvertSelection), 'S');
|
||||
item->SetTarget(PoseView());
|
||||
menu->AddItem(item);
|
||||
|
||||
item = new BMenuItem(B_TRANSLATE("Open parent"),
|
||||
new BMessage(kOpenParentDir), B_UP_ARROW);
|
||||
item->SetTarget(PoseView());
|
||||
menu->AddItem(item);
|
||||
|
||||
item = new BMenuItem(B_TRANSLATE("Close"),
|
||||
new BMessage(B_QUIT_REQUESTED), 'W');
|
||||
item->SetTarget(this);
|
||||
menu->AddItem(item);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
VirtualDirectoryWindow::AddWindowContextMenus(BMenu* menu)
|
||||
{
|
||||
BMenuItem* resizeItem = new BMenuItem(B_TRANSLATE("Resize to fit"),
|
||||
new BMessage(kResizeToFit), 'Y');
|
||||
menu->AddItem(resizeItem);
|
||||
menu->AddItem(new BMenuItem(B_TRANSLATE("Select"B_UTF8_ELLIPSIS),
|
||||
new BMessage(kShowSelectionWindow), 'A', B_SHIFT_KEY));
|
||||
menu->AddItem(new BMenuItem(B_TRANSLATE("Select all"),
|
||||
new BMessage(B_SELECT_ALL), 'A'));
|
||||
BMenuItem* closeItem = new BMenuItem(B_TRANSLATE("Close"),
|
||||
new BMessage(B_QUIT_REQUESTED), 'W');
|
||||
menu->AddItem(closeItem);
|
||||
// target items as needed
|
||||
menu->SetTargetForItems(PoseView());
|
||||
closeItem->SetTarget(this);
|
||||
resizeItem->SetTarget(this);
|
||||
}
|
||||
|
||||
|
||||
} // namespace BPrivate
|
44
src/kits/tracker/VirtualDirectoryWindow.h
Normal file
44
src/kits/tracker/VirtualDirectoryWindow.h
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2013, Haiku, Inc.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Ingo Weinhold, ingo_weinhold@gmx.de
|
||||
*/
|
||||
#ifndef VIRTUAL_DIRECTORY_WINDOW_H
|
||||
#define VIRTUAL_DIRECTORY_WINDOW_H
|
||||
|
||||
|
||||
#include "ContainerWindow.h"
|
||||
|
||||
|
||||
namespace BPrivate {
|
||||
|
||||
|
||||
class VirtualDirectoryWindow : public BContainerWindow {
|
||||
public:
|
||||
VirtualDirectoryWindow(
|
||||
LockingList<BWindow>* windowList,
|
||||
uint32 containerWindowFlags,
|
||||
window_look look = B_DOCUMENT_WINDOW_LOOK,
|
||||
window_feel feel = B_NORMAL_WINDOW_FEEL,
|
||||
uint32 flags = B_WILL_ACCEPT_FIRST_CLICK
|
||||
| B_NO_WORKSPACE_ACTIVATION,
|
||||
uint32 workspace = B_CURRENT_WORKSPACE);
|
||||
|
||||
protected:
|
||||
virtual void CreatePoseView(Model* model);
|
||||
virtual BPoseView* NewPoseView(Model* model, BRect rect,
|
||||
uint32 viewMode);
|
||||
virtual void AddWindowMenu(BMenu* menu);
|
||||
virtual void AddWindowContextMenus(BMenu* menu);
|
||||
|
||||
private:
|
||||
typedef BContainerWindow _inherited;
|
||||
};
|
||||
|
||||
|
||||
} // namespace BPrivate
|
||||
|
||||
|
||||
#endif // VIRTUAL_DIRECTORY_WINDOW_H
|
@ -1104,7 +1104,8 @@ SizeAttributeText::ReadValue()
|
||||
}
|
||||
|
||||
if (fModel->IsDirectory() || fModel->IsQuery()
|
||||
|| fModel->IsQueryTemplate() || fModel->IsSymLink())
|
||||
|| fModel->IsQueryTemplate() || fModel->IsSymLink()
|
||||
|| fModel->IsVirtualDirectory())
|
||||
return kUnknownSize;
|
||||
|
||||
fValueIsDefined = true;
|
||||
|
Loading…
x
Reference in New Issue
Block a user