Assorted whitespace cleanup and typo fixes.
[haiku.git] / src / kits / tracker / VirtualDirectoryPoseView.cpp
bloba44921d94d904d32ca1bc02f79f33b076242b633
1 /*
2 * Copyright 2013 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Ingo Weinhold, ingo_weinhold@gmx.de
7 */
10 #include "VirtualDirectoryPoseView.h"
12 #include <new>
14 #include <AutoLocker.h>
15 #include <NotOwningEntryRef.h>
16 #include <PathMonitor.h>
17 #include <storage_support.h>
19 #include "Commands.h"
20 #include "Tracker.h"
21 #include "VirtualDirectoryEntryList.h"
22 #include "VirtualDirectoryManager.h"
25 namespace BPrivate {
27 // #pragma mark - VirtualDirectoryPoseView
30 VirtualDirectoryPoseView::VirtualDirectoryPoseView(Model* model)
32 BPoseView(model, kListMode),
33 fDirectoryPaths(),
34 fRootDefinitionFileRef(-1, -1),
35 fFileChangeTime(-1),
36 fIsRoot(false)
38 VirtualDirectoryManager* manager = VirtualDirectoryManager::Instance();
39 if (manager == NULL)
40 return;
42 AutoLocker<VirtualDirectoryManager> managerLocker(manager);
43 if (_UpdateDirectoryPaths() != B_OK)
44 return;
46 manager->GetRootDefinitionFile(*model->NodeRef(), fRootDefinitionFileRef);
47 fIsRoot = fRootDefinitionFileRef == *model->NodeRef();
51 VirtualDirectoryPoseView::~VirtualDirectoryPoseView()
56 void
57 VirtualDirectoryPoseView::MessageReceived(BMessage* message)
59 switch (message->what) {
60 // ignore all edit operations
61 case B_CUT:
62 case kCutMoreSelectionToClipboard:
63 case kDuplicateSelection:
64 case kDelete:
65 case kMoveToTrash:
66 case kNewEntryFromTemplate:
67 case kNewFolder:
68 case kEditItem:
69 break;
71 default:
72 _inherited::MessageReceived(message);
73 break;
78 void
79 VirtualDirectoryPoseView::AttachedToWindow()
81 _inherited::AttachedToWindow();
82 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
83 SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
87 void
88 VirtualDirectoryPoseView::RestoreState(AttributeStreamNode* node)
90 _inherited::RestoreState(node);
91 fViewState->SetViewMode(kListMode);
95 void
96 VirtualDirectoryPoseView::RestoreState(const BMessage& message)
98 _inherited::RestoreState(message);
99 fViewState->SetViewMode(kListMode);
103 void
104 VirtualDirectoryPoseView::SavePoseLocations(BRect* frameIfDesktop)
109 void
110 VirtualDirectoryPoseView::SetViewMode(uint32 newMode)
115 EntryListBase*
116 VirtualDirectoryPoseView::InitDirentIterator(const entry_ref* ref)
118 if (fRootDefinitionFileRef.node < 0 || *ref != *TargetModel()->EntryRef())
119 return NULL;
121 Model sourceModel(ref, false, true);
122 if (sourceModel.InitCheck() != B_OK)
123 return NULL;
125 VirtualDirectoryEntryList* entryList
126 = new(std::nothrow) VirtualDirectoryEntryList(
127 *TargetModel()->NodeRef(), fDirectoryPaths);
128 if (entryList == NULL || entryList->InitCheck() != B_OK) {
129 delete entryList;
130 return NULL;
133 return entryList;
137 void
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
152 if (!fIsRoot)
153 TTracker::WatchNode(&fRootDefinitionFileRef, B_WATCH_STAT, this);
157 void
158 VirtualDirectoryPoseView::StopWatching()
160 BPathMonitor::StopWatching(this);
161 stop_watching(this);
165 bool
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);
175 case B_ENTRY_MOVED:
176 return _EntryMoved(message);
178 case B_STAT_CHANGED:
179 return _NodeStatChanged(message);
181 default:
182 return _inherited::FSNotification(message);
187 bool
188 VirtualDirectoryPoseView::_EntryCreated(const BMessage* message)
190 NotOwningEntryRef entryRef;
191 node_ref nodeRef;
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) {
197 return true;
199 entryRef.device = nodeRef.device;
201 // It might be one of our directories.
202 BString path;
203 if (message->FindString("path", &path) == B_OK
204 && fDirectoryPaths.HasString(path)) {
205 // Iterate through the directory and generate an entry-created message
206 // for each entry.
207 BDirectory directory;
208 if (directory.SetTo(&nodeRef) != B_OK)
209 return true;
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,
218 entry.d_name),
219 NULL, false);
222 return true;
225 // See, if this entry actually becomes visible. If not, we can simply ignore
226 // it.
227 struct stat st;
228 entry_ref visibleEntryRef;
229 if (!_GetEntry(entryRef.name, visibleEntryRef, &st)
230 || visibleEntryRef != entryRef) {
231 return true;
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) {
240 if (manager == NULL)
241 return true;
243 if (manager->TranslateDirectoryEntry(*TargetModel()->NodeRef(),
244 entryRef, nodeRef) != B_OK) {
245 return true;
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);
252 if (pose != NULL) {
253 if (nodeRef == *pose->TargetModel()->NodeRef()) {
254 // apparently not really a new entry -- can happen for
255 // subdirectories
256 return true;
259 // It may be a directory, so tell the manager.
260 if (manager != NULL)
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());
268 } else
269 managerLocker.Unlock();
271 return entryTranslated
272 ? (_DispatchEntryCreatedOrRemovedMessage(B_ENTRY_CREATED, nodeRef,
273 entryRef), true)
274 : _inherited::FSNotification(message);
278 bool
279 VirtualDirectoryPoseView::_EntryRemoved(const BMessage* message)
281 NotOwningEntryRef entryRef;
282 node_ref nodeRef;
284 if (message->FindInt32("device", &nodeRef.device) != B_OK
285 || message->FindInt64("node", &nodeRef.node) != B_OK
286 || message->FindInt64("directory", &entryRef.directory)
287 != B_OK
288 || message->FindString("name", (const char**)&entryRef.name) != B_OK) {
289 return true;
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.
298 BString path;
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.
303 PoseList poses;
304 for (int32 i = 0; BPose* pose = fPoseList->ItemAt(i); i++) {
305 NotOwningEntryRef poseEntryRef = *pose->TargetModel()->EntryRef();
306 if (poseEntryRef.DirectoryNodeRef() == nodeRef)
307 poses.AddItem(pose);
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);
316 return true;
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);
328 if (manager != NULL
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())
339 return true;
341 // See, if another entry becomes visible, now.
342 struct stat st;
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)
351 != B_OK) {
352 return true;
355 // Effectively nothing changes, when the removed entry was a
356 // directory as well.
357 if (visibleNodeRef == *actualNodeRef)
358 return true;
362 if (actualEntryRef == &entryRef) {
363 managerLocker.Unlock();
364 if (_inherited::FSNotification(message))
365 pendingNodeMonitorCache.Add(message);
366 } else {
367 // tell the manager that the directory has been removed
368 manager->DirectoryRemoved(*actualNodeRef);
369 managerLocker.Unlock();
371 _DispatchEntryCreatedOrRemovedMessage(B_ENTRY_REMOVED, *actualNodeRef,
372 *actualEntryRef);
375 _DispatchEntryCreatedOrRemovedMessage(B_ENTRY_CREATED, visibleNodeRef,
376 visibleEntryRef);
378 return true;
382 bool
383 VirtualDirectoryPoseView::_EntryMoved(const BMessage* message)
385 NotOwningEntryRef fromEntryRef;
386 NotOwningEntryRef toEntryRef;
387 node_ref nodeRef;
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)
395 != B_OK
396 || message->FindString("name", (const char**)&toEntryRef.name)
397 != B_OK) {
398 return true;
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
404 // consider, though.
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);
410 return true;
414 bool
415 VirtualDirectoryPoseView::_NodeStatChanged(const BMessage* message)
417 node_ref nodeRef;
418 if (message->FindInt32("device", &nodeRef.device) != B_OK
419 || message->FindInt64("node", &nodeRef.node) != B_OK) {
420 return true;
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.
433 return true;
436 bigtime_t fileChangeTime;
437 manager->GetDefinitionFileChangeTime(*TargetModel()->NodeRef(),
438 fileChangeTime);
439 if (fileChangeTime != fFileChangeTime) {
440 _UpdateDirectoryPaths();
441 managerLocker.Unlock();
442 Refresh();
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.
451 if (!fIsRoot)
452 return true;
455 return _inherited::FSNotification(message);
459 void
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);
475 if (!result)
476 pendingNodeMonitorCache.Add(&message);
480 bool
481 VirtualDirectoryPoseView::_GetEntry(const char* name, entry_ref& _ref,
482 struct stat* _st)
484 return VirtualDirectoryManager::GetEntry(fDirectoryPaths, name, &_ref, _st);
488 status_t
489 VirtualDirectoryPoseView::_UpdateDirectoryPaths()
491 VirtualDirectoryManager* manager = VirtualDirectoryManager::Instance();
492 Model* model = TargetModel();
493 status_t error = manager->ResolveDirectoryPaths(*model->NodeRef(),
494 *model->EntryRef(), fDirectoryPaths);
495 if (error != B_OK)
496 return error;
498 manager->GetDefinitionFileChangeTime(*model->NodeRef(), fFileChangeTime);
499 return B_OK;
502 } // namespace BPrivate