2 * Copyright 2013 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
6 * Ingo Weinhold, ingo_weinhold@gmx.de
10 #include "VirtualDirectoryPoseView.h"
14 #include <AutoLocker.h>
15 #include <NotOwningEntryRef.h>
16 #include <PathMonitor.h>
17 #include <storage_support.h>
21 #include "VirtualDirectoryEntryList.h"
22 #include "VirtualDirectoryManager.h"
27 // #pragma mark - VirtualDirectoryPoseView
30 VirtualDirectoryPoseView::VirtualDirectoryPoseView(Model
* model
)
32 BPoseView(model
, kListMode
),
34 fRootDefinitionFileRef(-1, -1),
38 VirtualDirectoryManager
* manager
= VirtualDirectoryManager::Instance();
42 AutoLocker
<VirtualDirectoryManager
> managerLocker(manager
);
43 if (_UpdateDirectoryPaths() != B_OK
)
46 manager
->GetRootDefinitionFile(*model
->NodeRef(), fRootDefinitionFileRef
);
47 fIsRoot
= fRootDefinitionFileRef
== *model
->NodeRef();
51 VirtualDirectoryPoseView::~VirtualDirectoryPoseView()
57 VirtualDirectoryPoseView::MessageReceived(BMessage
* message
)
59 switch (message
->what
) {
60 // ignore all edit operations
62 case kCutMoreSelectionToClipboard
:
63 case kDuplicateSelection
:
66 case kNewEntryFromTemplate
:
72 _inherited::MessageReceived(message
);
79 VirtualDirectoryPoseView::AttachedToWindow()
81 _inherited::AttachedToWindow();
82 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR
));
83 SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR
));
88 VirtualDirectoryPoseView::RestoreState(AttributeStreamNode
* node
)
90 _inherited::RestoreState(node
);
91 fViewState
->SetViewMode(kListMode
);
96 VirtualDirectoryPoseView::RestoreState(const BMessage
& message
)
98 _inherited::RestoreState(message
);
99 fViewState
->SetViewMode(kListMode
);
104 VirtualDirectoryPoseView::SavePoseLocations(BRect
* frameIfDesktop
)
110 VirtualDirectoryPoseView::SetViewMode(uint32 newMode
)
116 VirtualDirectoryPoseView::InitDirentIterator(const entry_ref
* ref
)
118 if (fRootDefinitionFileRef
.node
< 0 || *ref
!= *TargetModel()->EntryRef())
121 Model
sourceModel(ref
, false, true);
122 if (sourceModel
.InitCheck() != B_OK
)
125 VirtualDirectoryEntryList
* entryList
126 = new(std::nothrow
) VirtualDirectoryEntryList(
127 *TargetModel()->NodeRef(), fDirectoryPaths
);
128 if (entryList
== NULL
|| entryList
->InitCheck() != B_OK
) {
138 VirtualDirectoryPoseView::StartWatching()
140 // watch the directories
141 int32 count
= fDirectoryPaths
.CountStrings();
142 for (int32 i
= 0; i
< count
; i
++) {
143 BString path
= fDirectoryPaths
.StringAt(i
);
144 BPathMonitor::StartWatching(path
, B_WATCH_DIRECTORY
, this);
147 // watch the definition file
148 TTracker::WatchNode(TargetModel()->NodeRef(),
149 B_WATCH_NAME
| B_WATCH_STAT
| B_WATCH_ATTR
, this);
151 // also watch the root definition file
153 TTracker::WatchNode(&fRootDefinitionFileRef
, B_WATCH_STAT
, this);
158 VirtualDirectoryPoseView::StopWatching()
160 BPathMonitor::StopWatching(this);
166 VirtualDirectoryPoseView::FSNotification(const BMessage
* message
)
168 switch (message
->GetInt32("opcode", 0)) {
169 case B_ENTRY_CREATED
:
170 return _EntryCreated(message
);
172 case B_ENTRY_REMOVED
:
173 return _EntryRemoved(message
);
176 return _EntryMoved(message
);
179 return _NodeStatChanged(message
);
182 return _inherited::FSNotification(message
);
188 VirtualDirectoryPoseView::_EntryCreated(const BMessage
* message
)
190 NotOwningEntryRef entryRef
;
193 if (message
->FindInt32("device", &nodeRef
.device
) != B_OK
194 || message
->FindInt64("node", &nodeRef
.node
) != B_OK
195 || message
->FindInt64("directory", &entryRef
.directory
) != B_OK
196 || message
->FindString("name", (const char**)&entryRef
.name
) != B_OK
) {
199 entryRef
.device
= nodeRef
.device
;
201 // It might be one of our directories.
203 if (message
->FindString("path", &path
) == B_OK
204 && fDirectoryPaths
.HasString(path
)) {
205 // Iterate through the directory and generate an entry-created message
207 BDirectory directory
;
208 if (directory
.SetTo(&nodeRef
) != B_OK
)
211 BPrivate::Storage::LongDirEntry entry
;
212 while (directory
.GetNextDirents(&entry
, sizeof(entry
), 1) == 1) {
213 if (strcmp(entry
.d_name
, ".") != 0
214 && strcmp(entry
.d_name
, "..") != 0) {
215 _DispatchEntryCreatedOrRemovedMessage(B_ENTRY_CREATED
,
216 node_ref(entry
.d_dev
, entry
.d_ino
),
217 NotOwningEntryRef(entry
.d_pdev
, entry
.d_pino
,
225 // See, if this entry actually becomes visible. If not, we can simply ignore
228 entry_ref visibleEntryRef
;
229 if (!_GetEntry(entryRef
.name
, visibleEntryRef
, &st
)
230 || visibleEntryRef
!= entryRef
) {
234 // If it is a directory, translate it.
235 VirtualDirectoryManager
* manager
= VirtualDirectoryManager::Instance();
236 AutoLocker
<VirtualDirectoryManager
> managerLocker(manager
);
238 bool entryTranslated
= S_ISDIR(st
.st_mode
);
239 if (entryTranslated
) {
243 if (manager
->TranslateDirectoryEntry(*TargetModel()->NodeRef(),
244 entryRef
, nodeRef
) != B_OK
) {
249 // The entry might replace another entry. If it does, we'll fake a removed
250 // message for the old one first.
251 BPose
* pose
= fPoseList
->FindPoseByFileName(entryRef
.name
);
253 if (nodeRef
== *pose
->TargetModel()->NodeRef()) {
254 // apparently not really a new entry -- can happen for
259 // It may be a directory, so tell the manager.
261 manager
->DirectoryRemoved(*pose
->TargetModel()->NodeRef());
263 managerLocker
.Unlock();
265 BMessage
removedMessage(B_NODE_MONITOR
);
266 _DispatchEntryCreatedOrRemovedMessage(B_ENTRY_REMOVED
,
267 *pose
->TargetModel()->NodeRef(), *pose
->TargetModel()->EntryRef());
269 managerLocker
.Unlock();
271 return entryTranslated
272 ? (_DispatchEntryCreatedOrRemovedMessage(B_ENTRY_CREATED
, nodeRef
,
274 : _inherited::FSNotification(message
);
279 VirtualDirectoryPoseView::_EntryRemoved(const BMessage
* message
)
281 NotOwningEntryRef entryRef
;
284 if (message
->FindInt32("device", &nodeRef
.device
) != B_OK
285 || message
->FindInt64("node", &nodeRef
.node
) != B_OK
286 || message
->FindInt64("directory", &entryRef
.directory
)
288 || message
->FindString("name", (const char**)&entryRef
.name
) != B_OK
) {
291 entryRef
.device
= nodeRef
.device
;
293 // It might be our definition file.
294 if (nodeRef
== *TargetModel()->NodeRef())
295 return _inherited::FSNotification(message
);
297 // It might be one of our directories.
299 if (message
->FindString("path", &path
) == B_OK
300 && fDirectoryPaths
.HasString(path
)) {
301 // Find all poses that stem from that directory and generate an
302 // entry-removed message for each.
304 for (int32 i
= 0; BPose
* pose
= fPoseList
->ItemAt(i
); i
++) {
305 NotOwningEntryRef poseEntryRef
= *pose
->TargetModel()->EntryRef();
306 if (poseEntryRef
.DirectoryNodeRef() == nodeRef
)
310 for (int32 i
= 0; BPose
* pose
= poses
.ItemAt(i
); i
++) {
311 _DispatchEntryCreatedOrRemovedMessage(B_ENTRY_REMOVED
,
312 *pose
->TargetModel()->NodeRef(),
313 *pose
->TargetModel()->EntryRef(), NULL
, false);
319 // If it is a directory, translate it.
320 entry_ref
* actualEntryRef
= &entryRef
;
321 node_ref
* actualNodeRef
= &nodeRef
;
322 entry_ref definitionEntryRef
;
323 node_ref definitionNodeRef
;
325 VirtualDirectoryManager
* manager
= VirtualDirectoryManager::Instance();
326 AutoLocker
<VirtualDirectoryManager
> managerLocker(manager
);
329 && manager
->GetSubDirectoryDefinitionFile(*TargetModel()->NodeRef(),
330 entryRef
.name
, definitionEntryRef
, definitionNodeRef
)) {
331 actualEntryRef
= &definitionEntryRef
;
332 actualNodeRef
= &definitionNodeRef
;
335 // Check the pose. It might have been an entry that wasn't visible anyway.
336 // In that case we can just ignore the notification.
337 BPose
* pose
= fPoseList
->FindPoseByFileName(actualEntryRef
->name
);
338 if (pose
== NULL
|| *actualNodeRef
!= *pose
->TargetModel()->NodeRef())
341 // See, if another entry becomes visible, now.
343 entry_ref visibleEntryRef
;
344 node_ref visibleNodeRef
;
345 if (_GetEntry(actualEntryRef
->name
, visibleEntryRef
, &st
)) {
346 // If the new entry is a directory, translate it.
347 visibleNodeRef
= node_ref(st
.st_dev
, st
.st_ino
);
348 if (S_ISDIR(st
.st_mode
)) {
349 if (manager
== NULL
|| manager
->TranslateDirectoryEntry(
350 *TargetModel()->NodeRef(), visibleEntryRef
, visibleNodeRef
)
355 // Effectively nothing changes, when the removed entry was a
356 // directory as well.
357 if (visibleNodeRef
== *actualNodeRef
)
362 if (actualEntryRef
== &entryRef
) {
363 managerLocker
.Unlock();
364 if (_inherited::FSNotification(message
))
365 pendingNodeMonitorCache
.Add(message
);
367 // tell the manager that the directory has been removed
368 manager
->DirectoryRemoved(*actualNodeRef
);
369 managerLocker
.Unlock();
371 _DispatchEntryCreatedOrRemovedMessage(B_ENTRY_REMOVED
, *actualNodeRef
,
375 _DispatchEntryCreatedOrRemovedMessage(B_ENTRY_CREATED
, visibleNodeRef
,
383 VirtualDirectoryPoseView::_EntryMoved(const BMessage
* message
)
385 NotOwningEntryRef fromEntryRef
;
386 NotOwningEntryRef toEntryRef
;
389 if (message
->FindInt32("node device", &nodeRef
.device
) != B_OK
390 || message
->FindInt64("node", &nodeRef
.node
) != B_OK
391 || message
->FindInt32("device", &fromEntryRef
.device
) != B_OK
392 || message
->FindInt64("from directory", &fromEntryRef
.directory
) != B_OK
393 || message
->FindInt64("to directory", &toEntryRef
.directory
) != B_OK
394 || message
->FindString("from name", (const char**)&fromEntryRef
.name
)
396 || message
->FindString("name", (const char**)&toEntryRef
.name
)
400 toEntryRef
.device
= fromEntryRef
.device
;
402 // TODO: That's the lazy approach. Ideally we'd analyze the situation and
403 // forward a B_ENTRY_MOVED, if possible. There are quite a few cases to
405 _DispatchEntryCreatedOrRemovedMessage(B_ENTRY_REMOVED
, nodeRef
,
406 fromEntryRef
, message
->GetString("from path", NULL
), false);
407 _DispatchEntryCreatedOrRemovedMessage(B_ENTRY_CREATED
, nodeRef
,
408 toEntryRef
, message
->GetString("path", NULL
), false);
415 VirtualDirectoryPoseView::_NodeStatChanged(const BMessage
* message
)
418 if (message
->FindInt32("device", &nodeRef
.device
) != B_OK
419 || message
->FindInt64("node", &nodeRef
.node
) != B_OK
) {
423 if (nodeRef
== fRootDefinitionFileRef
) {
424 if ((message
->GetInt32("fields", 0) & B_STAT_MODIFICATION_TIME
) != 0) {
425 VirtualDirectoryManager
* manager
426 = VirtualDirectoryManager::Instance();
427 if (manager
!= NULL
) {
428 AutoLocker
<VirtualDirectoryManager
> managerLocker(manager
);
429 if (!manager
->DefinitionFileChanged(
430 *TargetModel()->NodeRef())) {
431 // The definition file no longer exists. Ignore the message
432 // -- we'll get a remove notification soon.
436 bigtime_t fileChangeTime
;
437 manager
->GetDefinitionFileChangeTime(*TargetModel()->NodeRef(),
439 if (fileChangeTime
!= fFileChangeTime
) {
440 _UpdateDirectoryPaths();
441 managerLocker
.Unlock();
443 // TODO: Refresh() is rather radical. Or rather its
444 // implementation is. Ideally it would just compare the
445 // currently added poses with what a new dir iterator
446 // returns and remove/add poses as needed.
455 return _inherited::FSNotification(message
);
460 VirtualDirectoryPoseView::_DispatchEntryCreatedOrRemovedMessage(int32 opcode
,
461 const node_ref
& nodeRef
, const entry_ref
& entryRef
, const char* path
,
462 bool dispatchToSuperClass
)
464 BMessage
message(B_NODE_MONITOR
);
465 message
.AddInt32("opcode", opcode
);
466 message
.AddInt32("device", nodeRef
.device
);
467 message
.AddInt64("node", nodeRef
.node
);
468 message
.AddInt64("directory", entryRef
.directory
);
469 message
.AddString("name", entryRef
.name
);
470 if (path
!= NULL
&& path
[0] != '\0')
471 message
.AddString("path", path
);
472 bool result
= dispatchToSuperClass
473 ? _inherited::FSNotification(&message
)
474 : FSNotification(&message
);
476 pendingNodeMonitorCache
.Add(&message
);
481 VirtualDirectoryPoseView::_GetEntry(const char* name
, entry_ref
& _ref
,
484 return VirtualDirectoryManager::GetEntry(fDirectoryPaths
, name
, &_ref
, _st
);
489 VirtualDirectoryPoseView::_UpdateDirectoryPaths()
491 VirtualDirectoryManager
* manager
= VirtualDirectoryManager::Instance();
492 Model
* model
= TargetModel();
493 status_t error
= manager
->ResolveDirectoryPaths(*model
->NodeRef(),
494 *model
->EntryRef(), fDirectoryPaths
);
498 manager
->GetDefinitionFileChangeTime(*model
->NodeRef(), fFileChangeTime
);
502 } // namespace BPrivate