xref: /haiku/src/kits/tracker/Tracker.cpp (revision adb0d19d561947362090081e81d90dde59142026)
1 /*
2 Open Tracker License
3 
4 Terms and Conditions
5 
6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
7 
8 Permission is hereby granted, free of charge, to any person obtaining a copy of
9 this software and associated documentation files (the "Software"), to deal in
10 the Software without restriction, including without limitation the rights to
11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12 of the Software, and to permit persons to whom the Software is furnished to do
13 so, subject to the following conditions:
14 
15 The above copyright notice and this permission notice applies to all licensees
16 and shall be included in all copies or substantial portions of the Software.
17 
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 
25 Except as contained in this notice, the name of Be Incorporated shall not be
26 used in advertising or otherwise to promote the sale, use or other dealings in
27 this Software without prior written authorization from Be Incorporated.
28 
29 Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
30 of Be Incorporated in the United States and other countries. Other brand product
31 names are registered trademarks or trademarks of their respective holders.
32 All rights reserved.
33 */
34 
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38 
39 #include <Alert.h>
40 #include <Autolock.h>
41 #include <Debug.h>
42 #include <FindDirectory.h>
43 #include <fs_attr.h>
44 #include <fs_info.h>
45 #include <image.h>
46 #include <MenuItem.h>
47 #include <NodeInfo.h>
48 #include <NodeMonitor.h>
49 #include <Path.h>
50 #include <Roster.h>
51 #include <StopWatch.h>
52 #include <Volume.h>
53 #include <VolumeRoster.h>
54 
55 #include "Attributes.h"
56 #include "AutoLock.h"
57 #include "AutoMounter.h"
58 #include "AutoMounterSettings.h"
59 #include "BackgroundImage.h"
60 #include "Bitmaps.h"
61 #include "Commands.h"
62 #include "ContainerWindow.h"
63 #include "DeskWindow.h"
64 #include "FindPanel.h"
65 #include "FSClipboard.h"
66 #include "FSUtils.h"
67 #include "InfoWindow.h"
68 #include "MimeTypes.h"
69 #include "MimeTypeList.h"
70 #include "NodePreloader.h"
71 #include "OpenWithWindow.h"
72 #include "PoseView.h"
73 #include "QueryContainerWindow.h"
74 #include "StatusWindow.h"
75 #include "Tracker.h"
76 #include "TrackerSettings.h"
77 #include "TrashWatcher.h"
78 #include "FunctionObject.h"
79 #include "TrackerSettings.h"
80 #include "TrackerSettingsWindow.h"
81 #include "TaskLoop.h"
82 #include "Thread.h"
83 #include "Utilities.h"
84 #include "VolumeWindow.h"
85 
86 // PPC binary compatibility.
87 #include "AboutBox.cpp"
88 
89 // prototypes for some private kernel calls that will some day be public
90 #ifndef _IMPEXP_ROOT
91 #	define _IMPEXP_ROOT
92 #endif
93 extern "C" _IMPEXP_ROOT int _kset_fd_limit_(int num);
94 extern "C" _IMPEXP_ROOT int _kset_mon_limit_(int num);
95 	// from priv_syscalls.h
96 
97 const int32 DEFAULT_MON_NUM = 4096;
98 	// copied from fsil.c
99 
100 const int8 kOpenWindowNoFlags = 0;
101 const int8 kOpenWindowMinimized = 1;
102 const int8 kOpenWindowHasState = 2;
103 
104 const uint32 PSV_MAKE_PRINTER_ACTIVE_QUIETLY = 'pmaq';
105 	// from pr_server.h
106 
107 
108 namespace BPrivate {
109 
110 NodePreloader *gPreloader = NULL;
111 
112 void
113 InitIconPreloader()
114 {
115 	static int32 lock = 0;
116 
117 	if (atomic_add(&lock, 1) != 0) {
118 		// Just wait for the icon cache to be instantiated
119 		int32 tries = 20;
120 		while (IconCache::sIconCache == NULL && tries-- > 0)
121 			snooze(10000);
122 		return;
123 	}
124 
125 	if (IconCache::sIconCache != NULL)
126 		return;
127 
128 	// only start the node preloader if its Tracker or the Deskbar itself - don't
129 	// start it for file panels
130 
131 	bool preload = dynamic_cast<TTracker *>(be_app) != NULL;
132 	if (!preload) {
133 		// check for deskbar
134 		app_info info;
135 		if (be_app->GetAppInfo(&info) == B_OK
136 			&& !strcmp(info.signature, kDeskbarSignature))
137 			preload = true;
138 	}
139 	if (preload)
140 		gPreloader = NodePreloader::InstallNodePreloader("NodePreloader", be_app);
141 
142 	IconCache::sIconCache = new IconCache();
143 
144 	atomic_add(&lock, -1);
145 }
146 
147 }	// namespace BPrivate
148 
149 
150 uint32
151 GetVolumeFlags(Model *model)
152 {
153 	fs_info info;
154 	if (model->IsVolume()) {
155 		// search for the correct volume
156 		int32 cookie = 0;
157 		dev_t device;
158 		while ((device = next_dev(&cookie)) >= B_OK) {
159 			if (fs_stat_dev(device,&info))
160 				continue;
161 
162 			if (!strcmp(info.volume_name,model->Name()))
163 				return info.flags;
164 		}
165 		return B_FS_HAS_ATTR;
166 	}
167 	if (!fs_stat_dev(model->NodeRef()->device,&info))
168 		return info.flags;
169 
170 	return B_FS_HAS_ATTR;
171 }
172 
173 
174 static void
175 HideVarDir()
176 {
177 	BPath path;
178 	status_t err = find_directory(B_COMMON_VAR_DIRECTORY, &path);
179 
180 	if (err != B_OK){
181 		PRINT(("var err = %s\n", strerror(err)));
182 		return;
183 	}
184 
185 	BDirectory varDirectory(path.Path());
186 	if (varDirectory.InitCheck() == B_OK) {
187 		PoseInfo info;
188 		// make var dir invisible
189 		info.fInvisible = true;
190 		info.fInitedDirectory = -1;
191 
192 		if (varDirectory.WriteAttr(kAttrPoseInfo, B_RAW_TYPE, 0, &info, sizeof(info))
193 			== sizeof(info))
194 			varDirectory.RemoveAttr(kAttrPoseInfoForeign);
195 	}
196 }
197 
198 
199 //	#pragma mark -
200 
201 
202 TTracker::TTracker()
203 	:	BApplication(kTrackerSignature),
204 	fSettingsWindow(NULL)
205 {
206 	// set the cwd to /boot/home, anything that's launched
207 	// from Tracker will automatically inherit this
208 	BPath homePath;
209 
210 	if (find_directory(B_USER_DIRECTORY, &homePath) == B_OK)
211 		chdir(homePath.Path());
212 
213 	_kset_fd_limit_(512);
214 		// ask for a bunch more file descriptors so that nested copying
215 		// works well
216 
217 	fNodeMonitorCount = DEFAULT_MON_NUM;
218 
219 #ifdef CHECK_OPEN_MODEL_LEAKS
220 	InitOpenModelDumping();
221 #endif
222 
223 	InitIconPreloader();
224 
225 #ifdef LEAK_CHECKING
226 	SetNewLeakChecking(true);
227 	SetMallocLeakChecking(true);
228 #endif
229 
230 	//This is how often it should update the free space bar on the volume icons
231 	SetPulseRate(1000000);
232 }
233 
234 
235 TTracker::~TTracker()
236 {
237 }
238 
239 
240 bool
241 TTracker::QuitRequested()
242 {
243 	// don't allow user quitting
244 	if (CurrentMessage() && CurrentMessage()->FindBool("shortcut")) {
245 		// but allow quitting to hide fSettingsWindow
246 		int32 index = 0;
247 		BWindow *window = NULL;
248 		while ((window = WindowAt(index++)) != NULL) {
249 			if (window == fSettingsWindow) {
250 				if (fSettingsWindow->Lock()) {
251 					if (!fSettingsWindow->IsHidden()
252 						&& fSettingsWindow->IsActive())
253 						fSettingsWindow->Hide();
254 					fSettingsWindow->Unlock();
255 				}
256 				break;
257 			}
258 		}
259 		return false;
260 	}
261 
262 	gStatusWindow->AttemptToQuit();
263 		// try quitting the copy/move/empty trash threads
264 
265 	BVolume bootVolume;
266 	DEBUG_ONLY(status_t err =) BVolumeRoster().GetBootVolume(&bootVolume);
267 	ASSERT(err == B_OK);
268 	BMessage message;
269 	AutoLock<WindowList> lock(&fWindowList);
270 	// save open windows in a message inside an attribute of the desktop
271 	int32 count = fWindowList.CountItems();
272 	for (int32 i = 0; i < count; i++) {
273 		BContainerWindow *window = dynamic_cast<BContainerWindow *>
274 			(fWindowList.ItemAt(i));
275 
276 		if (window && window->Lock()) {
277 			if (window->TargetModel()
278 				&& !window->PoseView()->IsDesktopWindow()) {
279 				if (window->TargetModel()->IsRoot())
280 					message.AddBool("open_disks_window", true);
281 				else {
282 					BEntry entry;
283 					BPath path;
284 					const entry_ref *ref = window->TargetModel()->EntryRef();
285 					if (entry.SetTo(ref) == B_OK && entry.GetPath(&path) == B_OK) {
286 						int8 flags = window->IsMinimized() ? kOpenWindowMinimized : kOpenWindowNoFlags;
287 						uint32 deviceFlags = GetVolumeFlags(window->TargetModel());
288 
289 						// save state for every window which is
290 						//	a) already open on another workspace
291 						//	b) on a volume not capable of writing attributes
292 						if (window != FindContainerWindow(ref)
293 							|| (deviceFlags & (B_FS_HAS_ATTR | B_FS_IS_READONLY)) != B_FS_HAS_ATTR) {
294 							BMessage stateMessage;
295 							window->SaveState(stateMessage);
296 							window->SetSaveStateEnabled(false);
297 								// This is to prevent its state to be saved to the node when closed.
298 							message.AddMessage("window state", &stateMessage);
299 							flags |= kOpenWindowHasState;
300 						}
301 						const char *target;
302 						bool pathAlreadyExists = false;
303 						for (int32 index = 0;message.FindString("paths", index, &target) == B_OK;index++) {
304 							if (!strcmp(target,path.Path())) {
305 								pathAlreadyExists = true;
306 								break;
307 							}
308 						}
309 						if (!pathAlreadyExists)
310 							message.AddString("paths", path.Path());
311 						message.AddInt8(path.Path(), flags);
312 					}
313 				}
314 			}
315 			window->Unlock();
316 		}
317 	}
318 	lock.Unlock();
319 
320 	// write windows to open on disk
321 	BDirectory deskDir;
322 	if (!BootedInSafeMode() && FSGetDeskDir(&deskDir, bootVolume.Device()) == B_OK) {
323 		// if message is empty, delete the corresponding attribute
324 		if (message.CountNames(B_ANY_TYPE)) {
325 			size_t size = (size_t)message.FlattenedSize();
326 			char *buffer = new char[size];
327 			message.Flatten(buffer, (ssize_t)size);
328 			deskDir.WriteAttr(kAttrOpenWindows, B_MESSAGE_TYPE, 0, buffer, size);
329 			delete [] buffer;
330 		} else
331 			deskDir.RemoveAttr(kAttrOpenWindows);
332 	}
333 
334 	for (int32 count = 0; count < 50; count++) {
335 		// wait 5 seconds for the copiing/moving to quit
336 		if (gStatusWindow->AttemptToQuit())
337 			break;
338 
339 		snooze(100000);
340 	}
341 
342 	return _inherited::QuitRequested();
343 }
344 
345 
346 void
347 TTracker::Quit()
348 {
349 	TrackerSettings().SaveSettings(false);
350 
351 	fAutoMounter->Lock();
352 	fAutoMounter->QuitRequested();	// automounter does some stuff in QuitRequested
353 	fAutoMounter->Quit();			// but we really don't care if it is cooperating or not
354 
355 	fClipboardRefsWatcher->Lock();
356 	fClipboardRefsWatcher->Quit();
357 
358 	fTrashWatcher->Lock();
359 	fTrashWatcher->Quit();
360 
361 	WellKnowEntryList::Quit();
362 
363 	delete gPreloader;
364 	delete fTaskLoop;
365 	delete IconCache::sIconCache;
366 
367 	_inherited::Quit();
368 }
369 
370 
371 void
372 TTracker::MessageReceived(BMessage *message)
373 {
374 	if (HandleScriptingMessage(message))
375 		return;
376 
377 	switch (message->what) {
378 		case kOpenPreviouslyOpenWindows:
379 			if (!BootedInSafeMode())
380 				_OpenPreviouslyOpenedWindows();
381 			break;
382 
383 		case kGetInfo:
384 			OpenInfoWindows(message);
385 			break;
386 
387 		case kMoveToTrash:
388 			MoveRefsToTrash(message);
389 			break;
390 
391 		case kCloseWindowAndChildren:
392 			{
393 				const node_ref *itemNode;
394 				int32 bytes;
395 				message->FindData("node_ref", B_RAW_TYPE,
396 					(const void **)&itemNode, &bytes);
397 				CloseWindowAndChildren(itemNode);
398 				break;
399 			}
400 
401 		case kCloseAllWindows:
402 			CloseAllWindows();
403 			break;
404 
405 		case kCloseAllInWorkspace:
406 			CloseAllInWorkspace();
407 			break;
408 
409 		case kFindButton:
410 			(new FindWindow())->Show();
411 			break;
412 
413 		case kEditQuery:
414 			EditQueries(message);
415 			break;
416 
417 		case kUnmountVolume:
418 			// When the user attempts to unmount a volume from the mount
419 			// context menu, this is where the message gets received.
420 			// Save pose locations and forward this to the automounter
421 			SaveAllPoseLocations();
422 			fAutoMounter->PostMessage(message);
423 			break;
424 
425 		case kRunAutomounterSettings:
426 			AutomountSettingsDialog::RunAutomountSettings(fAutoMounter);
427 			break;
428 
429 		case kVolumeMounted:
430 		{
431 			// This is sent to us by the AutoMounter whenever it mounts
432 			// a new volume - we use it to restore the previously opened
433 			// windows from that volume in case it has been mounted
434 			// during the AutoMounter's initial rescan
435 			const char* path;
436 			bool initial;
437 			if (message->FindBool("initial rescan", &initial) != B_OK
438 				|| message->FindString("path", &path) != B_OK
439 				|| !initial)
440 				break;
441 
442 			_OpenPreviouslyOpenedWindows(path);
443 			break;
444 		}
445 
446 		case kShowSplash:
447 			run_be_about();
448 			break;
449 
450 		case kAddPrinter:
451 			// show the addprinter window
452 			run_add_printer_panel();
453 			break;
454 
455 		case kMakeActivePrinter:
456 			// get the current selection
457 			SetDefaultPrinter(message);
458 			break;
459 
460 #ifdef MOUNT_MENU_IN_DESKBAR
461 
462 		case 'gmtv':
463 			{
464 				// Someone (probably the deskbar) has requested a list of
465 				// mountable volumes.
466 				BMessage reply;
467 				AutoMounterLoop()->EachMountableItemAndFloppy(&AddMountableItemToMessage,
468 				  &reply);
469 				message->SendReply(&reply);
470 				break;
471 			}
472 
473 #endif
474 
475 		case kMountVolume:
476 		case kMountAllNow:
477 			AutoMounterLoop()->PostMessage(message);
478 			break;
479 
480 
481 		case kRestoreBackgroundImage:
482 			{
483 				BDeskWindow *desktop = GetDeskWindow();
484 				AutoLock<BWindow> lock(desktop);
485 				desktop->UpdateDesktopBackgroundImages();
486 			}
487 			break;
488 
489  		case kShowSettingsWindow:
490  			ShowSettingsWindow();
491  			break;
492 
493 		case kFavoriteCountChangedExternally:
494 			SendNotices(kFavoriteCountChangedExternally, message);
495 			break;
496 
497 		case kStartWatchClipboardRefs:
498 		{
499 			BMessenger messenger;
500 			message->FindMessenger("target", &messenger);
501 			if (messenger.IsValid())
502 				fClipboardRefsWatcher->AddToNotifyList(messenger);
503 			break;
504 		}
505 
506 		case kStopWatchClipboardRefs:
507 		{
508 			BMessenger messenger;
509 			message->FindMessenger("target", &messenger);
510 			if (messenger.IsValid())
511 				fClipboardRefsWatcher->RemoveFromNotifyList(messenger);
512 			break;
513 		}
514 
515 		case kFSClipboardChanges:
516 		{
517 			fClipboardRefsWatcher->UpdatePoseViews(message);
518 			break;
519 		}
520 
521 		case kShowVolumeSpaceBar:
522 		case kSpaceBarColorChanged: {
523 			gPeriodicUpdatePoses.DoPeriodicUpdate(true);
524 			break;
525 		}
526 
527 		default:
528 			_inherited::MessageReceived(message);
529 			break;
530 	}
531 }
532 
533 
534 void
535 TTracker::Pulse()
536 {
537 	if (!TrackerSettings().ShowVolumeSpaceBar())
538 		return;
539 
540 	// update the volume icon's free space bars
541 	gPeriodicUpdatePoses.DoPeriodicUpdate(false);
542 }
543 
544 
545 void
546 TTracker::SetDefaultPrinter(const BMessage *message)
547 {
548 	//	get the first item selected
549 	int32 count = 0;
550 	uint32 type = 0;
551 	message->GetInfo("refs", &type, &count);
552 
553 	if (count <= 0)
554 		return;
555 
556 	//	will make the first item the default printer, disregards any other files
557 	entry_ref ref;
558 	ASSERT(message->FindRef("refs", 0, &ref) == B_OK);
559 	if (message->FindRef("refs", 0, &ref) != B_OK)
560 		return;
561 
562 #if B_BEOS_VERSION_DANO
563 	set_default_printer(ref.name);
564 #else
565 	// 	create a message for the print server
566 	BMessenger messenger("application/x-vnd.Be-PSRV", -1);
567 	if (!messenger.IsValid())
568 		return;
569 
570 	//	send the selection to the print server
571 	BMessage makeActiveMessage(PSV_MAKE_PRINTER_ACTIVE_QUIETLY);
572 	makeActiveMessage.AddString("printer", ref.name);
573 
574 	BMessage reply;
575 	messenger.SendMessage(&makeActiveMessage, &reply);
576 #endif
577 }
578 
579 
580 void
581 TTracker::MoveRefsToTrash(const BMessage *message)
582 {
583 	int32 count;
584 	uint32 type;
585 	message->GetInfo("refs", &type, &count);
586 
587 	if (count <= 0)
588 		return;
589 
590 	BObjectList<entry_ref> *srcList = new BObjectList<entry_ref>(count, true);
591 
592 	for (int32 index = 0; index < count; index++) {
593 
594 		entry_ref ref;
595 		ASSERT(message->FindRef("refs", index, &ref) == B_OK);
596 		if (message->FindRef("refs", index, &ref) != B_OK)
597 			continue;
598 
599 		AutoLock<WindowList> lock(&fWindowList);
600 		BContainerWindow *window = FindParentContainerWindow(&ref);
601 		if (window)
602 			// if we have a window open for this entry, ask the pose to
603 			// delete it, this will select the next entry
604 			window->PoseView()->MoveEntryToTrash(&ref);
605 		else
606 			// add all others to a list that gets deleted separately
607 			srcList->AddItem(new entry_ref(ref));
608 	}
609 
610 	if (srcList->CountItems())
611 		// async move to trash
612 		FSMoveToTrash(srcList);
613 }
614 
615 
616 template <class T, class FT>
617 class EntryAndNodeDoSoonWithMessageFunctor : public FunctionObjectWithResult<bool> {
618 public:
619 	EntryAndNodeDoSoonWithMessageFunctor(FT func, T *target, const entry_ref *child,
620 		const node_ref *parent, const BMessage *message)
621 		:	fFunc(func),
622 			fTarget(target),
623 			fNode(*parent),
624 			fEntry(*child)
625 		{
626 			fSendMessage = (message != NULL);
627 			if (message)
628 				fMessage = *message;
629 		}
630 
631 	virtual ~EntryAndNodeDoSoonWithMessageFunctor() {}
632 	virtual void operator()()
633 		{ result = (fTarget->*fFunc)(&fEntry, &fNode, fSendMessage ? &fMessage : NULL); }
634 
635 protected:
636 	FT fFunc;
637 	T *fTarget;
638 	node_ref fNode;
639 	entry_ref fEntry;
640 	BMessage fMessage;
641 	bool fSendMessage;
642 };
643 
644 
645 bool
646 TTracker::LaunchAndCloseParentIfOK(const entry_ref *launchThis,
647 	const node_ref *closeThis, const BMessage *messageToBundle)
648 {
649 	BMessage refsReceived(B_REFS_RECEIVED);
650 	if (messageToBundle) {
651 		refsReceived = *messageToBundle;
652 		refsReceived.what = B_REFS_RECEIVED;
653 	}
654 	refsReceived.AddRef("refs", launchThis);
655 	// synchronous launch, we are already in our own thread
656 	if (TrackerLaunch(&refsReceived, false) == B_OK) {
657 		// if launched fine, close parent window in a bit
658 		fTaskLoop->RunLater(NewMemberFunctionObject(&TTracker::CloseParent, this, *closeThis),
659 			1000000);
660 	}
661 	return false;
662 }
663 
664 
665 status_t
666 TTracker::OpenRef(const entry_ref *ref, const node_ref *nodeToClose,
667 	const node_ref *nodeToSelect, OpenSelector selector,
668 	const BMessage *messageToBundle)
669 {
670 	Model *model = NULL;
671 	BEntry entry(ref, true);
672 	status_t result = entry.InitCheck();
673 
674 	bool brokenLinkWithSpecificHandler = false;
675 	BString brokenLinkPreferredApp;
676 
677 	if (result != B_OK) {
678 		model = new Model(ref, false);
679 		if (model->IsSymLink() && !model->LinkTo()) {
680 			model->GetPreferredAppForBrokenSymLink(brokenLinkPreferredApp);
681 			if (brokenLinkPreferredApp.Length() && brokenLinkPreferredApp != kTrackerSignature)
682 				brokenLinkWithSpecificHandler = true;
683 		}
684 
685 		if (!brokenLinkWithSpecificHandler) {
686 			delete model;
687 			BAlert *alert = new BAlert("",
688 				"There was an error resolving the link.",
689 				"Cancel", 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
690 			alert->SetShortcut(0, B_ESCAPE);
691 			alert->Go();
692 			return result;
693 		}
694 	} else
695 		model = new Model(&entry);
696 
697 	result = model->InitCheck();
698 	if (result != B_OK) {
699 		delete model;
700 		return result;
701 	}
702 
703 	bool openAsContainer = model->IsContainer();
704 
705 	if (openAsContainer && selector != kOpenWith) {
706 		// if folder or query has a preferred handler and it's not the
707 		// Tracker, open it by sending refs to the handling app
708 
709 		// if we are responding to the final open of OpenWith, just
710 		// skip this and proceed to opening the container with Tracker
711 		model->OpenNode();
712 		BNodeInfo nodeInfo(model->Node());
713 		char preferredApp[B_MIME_TYPE_LENGTH];
714 		if (nodeInfo.GetPreferredApp(preferredApp) == B_OK
715 			&& strcasecmp(preferredApp, kTrackerSignature) != 0)
716 			openAsContainer = false;
717 		model->CloseNode();
718 	}
719 
720 	if (openAsContainer || selector == kRunOpenWithWindow) {
721 		// special case opening plain folders, queries or using open with
722 		OpenContainerWindow(model, 0, selector, kRestoreDecor);	// window adopts model
723 		if (nodeToClose)
724 			CloseParentWaitingForChildSoon(ref, nodeToClose);
725 	} else if (model->IsQueryTemplate()) {
726 		// query template - open new find window
727 		(new FindWindow(model->EntryRef()))->Show();
728 		if (nodeToClose)
729 			CloseParentWaitingForChildSoon(ref, nodeToClose);
730 	} else {
731 		delete model;
732 		// run Launch in a separate thread
733 		// and close parent if successfull
734 		if (nodeToClose)
735 			Thread::Launch(new EntryAndNodeDoSoonWithMessageFunctor<TTracker,
736 				bool (TTracker::*)(const entry_ref *, const node_ref *,
737 				const BMessage *)>(&TTracker::LaunchAndCloseParentIfOK, this,
738 				ref, nodeToClose, messageToBundle));
739 		else {
740 			BMessage refsReceived(B_REFS_RECEIVED);
741 			if (messageToBundle) {
742 				refsReceived = *messageToBundle;
743 				refsReceived.what = B_REFS_RECEIVED;
744 			}
745 			refsReceived.AddRef("refs", ref);
746 			if (brokenLinkWithSpecificHandler)
747 				// This cruft is to support a hacky workaround for double-clicking
748 				// broken refs for cifs; should get fixed in R5
749 				LaunchBrokenLink(brokenLinkPreferredApp.String(), &refsReceived);
750 			else
751 				TrackerLaunch(&refsReceived, true);
752 		}
753 	}
754 	if (nodeToSelect)
755 		SelectChildInParentSoon(ref, nodeToSelect);
756 
757 	return B_OK;
758 }
759 
760 
761 void
762 TTracker::RefsReceived(BMessage *message)
763 {
764 	OpenSelector selector = kOpen;
765 	if (message->HasInt32("launchUsingSelector"))
766 		selector = kRunOpenWithWindow;
767 
768 	entry_ref handlingApp;
769 	if (message->FindRef("handler", &handlingApp) == B_OK)
770 		selector = kOpenWith;
771 
772 	int32 count;
773 	uint32 type;
774 	message->GetInfo("refs", &type, &count);
775 
776 	switch (selector) {
777 		case kRunOpenWithWindow:
778 			OpenContainerWindow(0, message, selector);
779 				// window adopts model
780 			break;
781 
782 		case kOpenWith:
783 			{
784 				// Open With resulted in passing refs and a handler, open the files
785 				// with the handling app
786 				message->RemoveName("handler");
787 
788 				// have to find out if handling app is the Tracker
789 				// if it is, just pass it to the active Tracker, no matter which Tracker
790 				// was chosen to handle the refs
791 				char signature[B_MIME_TYPE_LENGTH];
792 				signature[0] = '\0';
793 				{
794 					BFile handlingNode(&handlingApp, O_RDONLY);
795 					BAppFileInfo appInfo(&handlingNode);
796 					appInfo.GetSignature(signature);
797 				}
798 
799 				if (strcasecmp(signature, kTrackerSignature) != 0) {
800 					// handling app not Tracker, pass entries to the apps RefsReceived
801 					TrackerLaunch(&handlingApp, message, true);
802 					break;
803 				}
804 				// fall thru, opening refs by the Tracker, as if they were double clicked
805 			}
806 
807 		case kOpen:
808 			{
809 				// copy over "Poses" messenger so that refs received recipients know
810 				// where the open came from
811 				BMessage *bundleThis = NULL;
812 				BMessenger messenger;
813 				if (message->FindMessenger("TrackerViewToken", &messenger) == B_OK) {
814 					bundleThis = new BMessage();
815 					bundleThis->AddMessenger("TrackerViewToken", messenger);
816 				}
817 
818 				for (int32 index = 0; index < count; index++) {
819 					entry_ref ref;
820 					message->FindRef("refs", index, &ref);
821 
822 					const node_ref *nodeToClose = NULL;
823 					const node_ref *nodeToSelect = NULL;
824 					ssize_t numBytes;
825 
826 					message->FindData("nodeRefsToClose", B_RAW_TYPE, index,
827 						(const void **)&nodeToClose, &numBytes);
828 					message->FindData("nodeRefToSelect", B_RAW_TYPE, index,
829 						(const void **)&nodeToSelect, &numBytes);
830 
831 					OpenRef(&ref, nodeToClose, nodeToSelect, selector, bundleThis);
832 				}
833 
834 				delete bundleThis;
835 				break;
836 			}
837 	}
838 }
839 
840 
841 void
842 TTracker::ArgvReceived(int32 argc, char **argv)
843 {
844 	BMessage *message = CurrentMessage();
845 	const char *currentWorkingDirectoryPath = NULL;
846 	entry_ref ref;
847 
848 	if (message->FindString("cwd", &currentWorkingDirectoryPath) == B_OK) {
849 		BDirectory workingDirectory(currentWorkingDirectoryPath);
850 		for (int32 index = 1; index < argc; index++) {
851 			BEntry entry;
852 			if (entry.SetTo(&workingDirectory, argv[index]) == B_OK
853 				&& entry.GetRef(&ref) == B_OK)
854 				OpenRef(&ref);
855 			else if (get_ref_for_path(argv[index], &ref) == B_OK)
856 				OpenRef(&ref);
857 		}
858 	}
859 }
860 
861 void
862 TTracker::OpenContainerWindow(Model *model, BMessage *originalRefsList,
863 	OpenSelector openSelector, uint32 openFlags, bool checkAlreadyOpen,
864 	const BMessage *stateMessage)
865 {
866 	AutoLock<WindowList> lock(&fWindowList);
867 	BContainerWindow *window = NULL;
868 	if (checkAlreadyOpen && openSelector != kRunOpenWithWindow)
869 		// find out if window already open
870 		window = FindContainerWindow(model->NodeRef());
871 
872 	bool someWindowActivated = false;
873 
874 	uint32 workspace = (uint32)(1 << current_workspace());
875 	int32 windowCount = 0;
876 
877 	while (window) {
878 		// At least one window open, just pull to front
879 		// make sure we don't jerk workspaces around
880 		uint32 windowWorkspaces = window->Workspaces();
881 		if (windowWorkspaces & workspace) {
882 			window->Activate();
883 			someWindowActivated = true;
884 		}
885 		window = FindContainerWindow(model->NodeRef(), ++windowCount);
886 	}
887 
888 	if (someWindowActivated) {
889 		delete model;
890 		return;
891 	} // If no window was actiated, (none in the current workspace
892 	  // we open a new one.
893 
894 	if (openSelector == kRunOpenWithWindow) {
895 		BMessage *refList = NULL;
896 		if (!originalRefsList) {
897 			// when passing just a single model, stuff it's entry in a single
898 			// element list anyway
899 			ASSERT(model);
900 			refList = new BMessage;
901 			refList->AddRef("refs", model->EntryRef());
902 			delete model;
903 			model = NULL;
904 		} else
905 			// clone the message, window adopts it for it's own use
906 			refList = new BMessage(*originalRefsList);
907 		window = new OpenWithContainerWindow(refList, &fWindowList);
908 	} else if (model->IsRoot()) {
909 		// window will adopt the model
910 		window = new BVolumeWindow(&fWindowList, openFlags);
911 	} else if (model->IsQuery()) {
912 		// window will adopt the model
913 		window = new BQueryContainerWindow(&fWindowList, openFlags);
914 	} else
915 		// window will adopt the model
916 		window = new BContainerWindow(&fWindowList, openFlags);
917 
918 	if (model)
919 		window->CreatePoseView(model);
920 
921 	BMessage restoreStateMessage(kRestoreState);
922 
923 	if (stateMessage)
924 		restoreStateMessage.AddMessage("state", stateMessage);
925 
926 	window->PostMessage(&restoreStateMessage);
927 }
928 
929 
930 void
931 TTracker::EditQueries(const BMessage *message)
932 {
933 	bool editOnlyIfTemplate;
934 	if (message->FindBool("editQueryOnPose", &editOnlyIfTemplate) != B_OK)
935 		editOnlyIfTemplate = false;
936 
937 	type_code type;
938 	int32 count;
939 	message->GetInfo("refs", &type, &count);
940 	for (int32 index = 0; index < count; index++) {
941 		entry_ref ref;
942 		message->FindRef("refs", index, &ref);
943 		BEntry entry(&ref, true);
944 		if (entry.InitCheck() == B_OK && entry.Exists())
945 			(new FindWindow(&ref, editOnlyIfTemplate))->Show();
946 	}
947 }
948 
949 
950 void
951 TTracker::OpenInfoWindows(BMessage *message)
952 {
953 	type_code type;
954 	int32 count;
955 	message->GetInfo("refs", &type, &count);
956 
957 	for (int32 index = 0; index < count; index++) {
958 		entry_ref ref;
959 		message->FindRef("refs", index, &ref);
960 		BEntry entry;
961 		if (entry.SetTo(&ref) == B_OK) {
962 			Model *model = new Model(&entry);
963 			if (model->InitCheck() != B_OK) {
964 				delete model;
965 				continue;
966 			}
967 
968 			AutoLock<WindowList> lock(&fWindowList);
969 			BInfoWindow *wind = FindInfoWindow(model->NodeRef());
970 
971 			if (wind) {
972 				wind->Activate();
973 				delete model;
974 			} else {
975 				wind = new BInfoWindow(model, index, &fWindowList);
976 				wind->PostMessage(kRestoreState);
977 			}
978 		}
979 	}
980 }
981 
982 
983 BDeskWindow *
984 TTracker::GetDeskWindow() const
985 {
986 	int32 count = fWindowList.CountItems();
987 	for (int32 index = 0; index < count; index++) {
988 		BDeskWindow *window = dynamic_cast<BDeskWindow *>
989 			(fWindowList.ItemAt(index));
990 
991 		if (window)
992 			return window;
993 	}
994 	TRESPASS();
995 	return NULL;
996 }
997 
998 
999 BContainerWindow *
1000 TTracker::FindContainerWindow(const node_ref *node, int32 number) const
1001 {
1002 	ASSERT(fWindowList.IsLocked());
1003 
1004 	int32 count = fWindowList.CountItems();
1005 
1006 	int32 windowsFound = 0;
1007 
1008 	for (int32 index = 0; index < count; index++) {
1009 		BContainerWindow *window = dynamic_cast<BContainerWindow *>
1010 			(fWindowList.ItemAt(index));
1011 
1012 		if (window && window->IsShowing(node) && number == windowsFound++)
1013 			return window;
1014 	}
1015 	return NULL;
1016 }
1017 
1018 
1019 BContainerWindow *
1020 TTracker::FindContainerWindow(const entry_ref *entry, int32 number) const
1021 {
1022 	ASSERT(fWindowList.IsLocked());
1023 
1024 	int32 count = fWindowList.CountItems();
1025 
1026 	int32 windowsFound = 0;
1027 
1028 	for (int32 index = 0; index < count; index++) {
1029 		BContainerWindow *window = dynamic_cast<BContainerWindow *>
1030 			(fWindowList.ItemAt(index));
1031 
1032 		if (window && window->IsShowing(entry) && number == windowsFound++)
1033 			return window;
1034 	}
1035 	return NULL;
1036 }
1037 
1038 
1039 bool
1040 TTracker::EntryHasWindowOpen(const entry_ref *entry)
1041 {
1042 	AutoLock<WindowList> lock(&fWindowList);
1043 	return FindContainerWindow(entry) != NULL;
1044 }
1045 
1046 
1047 BContainerWindow *
1048 TTracker::FindParentContainerWindow(const entry_ref *ref) const
1049 {
1050 	BEntry entry(ref);
1051 	BEntry parent;
1052 
1053 	if (entry.GetParent(&parent) != B_OK)
1054 		return NULL;
1055 
1056 	entry_ref parentRef;
1057 	parent.GetRef(&parentRef);
1058 
1059 	ASSERT(fWindowList.IsLocked());
1060 
1061 	int32 count = fWindowList.CountItems();
1062 	for (int32 index = 0; index < count; index++) {
1063 		BContainerWindow *window = dynamic_cast<BContainerWindow *>
1064 			(fWindowList.ItemAt(index));
1065 		if (window && window->IsShowing(&parentRef))
1066 			return window;
1067 	}
1068 	return NULL;
1069 }
1070 
1071 
1072 BInfoWindow *
1073 TTracker::FindInfoWindow(const node_ref* node) const
1074 {
1075 	ASSERT(fWindowList.IsLocked());
1076 
1077 	int32 count = fWindowList.CountItems();
1078 	for (int32 index = 0; index < count; index++) {
1079 		BInfoWindow *window = dynamic_cast<BInfoWindow *>
1080 			(fWindowList.ItemAt(index));
1081 		if (window && window->IsShowing(node))
1082 			return window;
1083 	}
1084 	return NULL;
1085 }
1086 
1087 
1088 bool
1089 TTracker::QueryActiveForDevice(dev_t device)
1090 {
1091 	AutoLock<WindowList> lock(&fWindowList);
1092 	int32 count = fWindowList.CountItems();
1093 	for (int32 index = 0; index < count; index++) {
1094 		BQueryContainerWindow *window = dynamic_cast<BQueryContainerWindow *>
1095 			(fWindowList.ItemAt(index));
1096 		if (window) {
1097 			AutoLock<BWindow> lock(window);
1098 			if (window->ActiveOnDevice(device))
1099 				return true;
1100 		}
1101 	}
1102 	return false;
1103 }
1104 
1105 
1106 void
1107 TTracker::CloseActiveQueryWindows(dev_t device)
1108 {
1109 	// used when trying to unmount a volume - an active query would prevent that from
1110 	// happening
1111 	bool closed = false;
1112 	AutoLock<WindowList> lock(fWindowList);
1113 	for (int32 index = fWindowList.CountItems(); index >= 0; index--) {
1114 		BQueryContainerWindow *window = dynamic_cast<BQueryContainerWindow *>
1115 			(fWindowList.ItemAt(index));
1116 		if (window) {
1117 			AutoLock<BWindow> lock(window);
1118 			if (window->ActiveOnDevice(device)) {
1119 				window->PostMessage(B_QUIT_REQUESTED);
1120 				closed = true;
1121 			}
1122 		}
1123 	}
1124 	lock.Unlock();
1125 	if (closed)
1126 		for (int32 timeout = 30; timeout; timeout--) {
1127 			// wait a bit for windows to fully close
1128 			if (!QueryActiveForDevice(device))
1129 				return;
1130 			snooze(100000);
1131 		}
1132 }
1133 
1134 
1135 void
1136 TTracker::SaveAllPoseLocations()
1137 {
1138 	int32 numWindows = fWindowList.CountItems();
1139 	for (int32 windowIndex = 0; windowIndex < numWindows; windowIndex++) {
1140 		BContainerWindow *window = dynamic_cast<BContainerWindow *>
1141 			(fWindowList.ItemAt(windowIndex));
1142 
1143 		if (window) {
1144 			AutoLock<BWindow> lock(window);
1145 			BDeskWindow *deskWindow = dynamic_cast<BDeskWindow *>(window);
1146 
1147 			if (deskWindow)
1148 				deskWindow->SaveDesktopPoseLocations();
1149 			else
1150 				window->PoseView()->SavePoseLocations();
1151 		}
1152 	}
1153 }
1154 
1155 
1156 void
1157 TTracker::CloseWindowAndChildren(const node_ref *node)
1158 {
1159 	BDirectory dir(node);
1160 	if (dir.InitCheck() != B_OK)
1161 		return;
1162 
1163 	AutoLock<WindowList> lock(&fWindowList);
1164 	BObjectList<BContainerWindow> closeList;
1165 
1166 	// make a list of all windows to be closed
1167 	// count from end to beginning so we can remove items safely
1168 	for (int32 index = fWindowList.CountItems() - 1; index >= 0; index--) {
1169 		BContainerWindow *window = dynamic_cast<BContainerWindow *>
1170 			(fWindowList.ItemAt(index));
1171 		if (window && window->TargetModel()) {
1172 			BEntry wind_entry;
1173 			wind_entry.SetTo(window->TargetModel()->EntryRef());
1174 
1175 			if ((*window->TargetModel()->NodeRef() == *node)
1176 				|| dir.Contains(&wind_entry)) {
1177 
1178 				// ToDo:
1179 				// get rid of the Remove here, BContainerWindow::Quit does it
1180 				fWindowList.RemoveItemAt(index);
1181 				closeList.AddItem(window);
1182 			}
1183 		}
1184 	}
1185 
1186 	// now really close the windows
1187 	int32 numItems = closeList.CountItems();
1188 	for (int32 index = 0; index < numItems; index++) {
1189 		BContainerWindow *window = closeList.ItemAt(index);
1190 		window->PostMessage(B_QUIT_REQUESTED);
1191 	}
1192 }
1193 
1194 
1195 void
1196 TTracker::CloseAllInWorkspace()
1197 {
1198 	AutoLock<WindowList> lock(&fWindowList);
1199 
1200 	int32 currentWorkspace = 1 << current_workspace();
1201 	// count from end to beginning so we can remove items safely
1202 	for (int32 index = fWindowList.CountItems() - 1; index >= 0; index--) {
1203 		BWindow *window = fWindowList.ItemAt(index);
1204 		if (window->Workspaces() & currentWorkspace)
1205 			// avoid the desktop
1206 			if (!dynamic_cast<BDeskWindow *>(window)
1207 				&& !dynamic_cast<BStatusWindow *>(window))
1208 				window->PostMessage(B_QUIT_REQUESTED);
1209 	}
1210 }
1211 
1212 
1213 void
1214 TTracker::CloseAllWindows()
1215 {
1216 	// this is a response to the DeskBar sending us a B_QUIT, when it really
1217 	// means to say close all your windows. It might be better to have it
1218 	// send a kCloseAllWindows message and have windowless apps stay running,
1219 	// which is what we will do for the Tracker
1220 	AutoLock<WindowList> lock(&fWindowList);
1221 
1222 	int32 count = CountWindows();
1223 	for (int32 index = 0; index < count; index++) {
1224 		BWindow *window = WindowAt(index);
1225 		// avoid the desktop
1226 		if (!dynamic_cast<BDeskWindow *>(window)
1227 			&& !dynamic_cast<BStatusWindow *>(window))
1228 			window->PostMessage(B_QUIT_REQUESTED);
1229 	}
1230 	// count from end to beginning so we can remove items safely
1231 	for (int32 index = fWindowList.CountItems() - 1; index >= 0; index--) {
1232 		BWindow *window = fWindowList.ItemAt(index);
1233 		if (!dynamic_cast<BDeskWindow *>(window)
1234 			&& !dynamic_cast<BStatusWindow *>(window))
1235 				// ToDo:
1236 				// get rid of the Remove here, BContainerWindow::Quit does it
1237 			fWindowList.RemoveItemAt(index);
1238 	}
1239 }
1240 
1241 
1242 void
1243 TTracker::_OpenPreviouslyOpenedWindows(const char* pathFilter)
1244 {
1245 	size_t filterLength = 0;
1246 	if (pathFilter != NULL)
1247 		filterLength = strlen(pathFilter);
1248 
1249 	BVolume	bootVolume;
1250 	BVolumeRoster().GetBootVolume(&bootVolume);
1251 	BDirectory deskDir;
1252 	attr_info attrInfo;
1253 	if (FSGetDeskDir(&deskDir, bootVolume.Device()) != B_OK
1254 		|| deskDir.GetAttrInfo(kAttrOpenWindows, &attrInfo) != B_OK)
1255 		return;
1256 
1257 	char *buffer = (char *)malloc((size_t)attrInfo.size);
1258 	BMessage message;
1259 	if (deskDir.ReadAttr(kAttrOpenWindows, B_MESSAGE_TYPE, 0, buffer,
1260 		(size_t)attrInfo.size) != attrInfo.size
1261 		|| message.Unflatten(buffer) != B_OK) {
1262 		free(buffer);
1263 		return;
1264 	}
1265 
1266 	free(buffer);
1267 
1268 	node_ref nodeRef;
1269 	deskDir.GetNodeRef(&nodeRef);
1270 
1271 	int32 stateMessageCounter = 0;
1272 	const char *path;
1273 	for (int32 i = 0; message.FindString("paths", i, &path) == B_OK; i++) {
1274 		if (strncmp(path, pathFilter, filterLength))
1275 			continue;
1276 
1277 		BEntry entry(path, true);
1278 		if (entry.InitCheck() != B_OK)
1279 			continue;
1280 
1281 		int8 flags = 0;
1282 		for (int32 j = 0; message.FindInt8(path, j, &flags) == B_OK; j++) {
1283 			Model *model = new Model(&entry);
1284 			if (model->InitCheck() == B_OK && model->IsContainer()) {
1285 				BMessage state;
1286 				bool restoreStateFromMessage = false;
1287 				if ((flags & kOpenWindowHasState) != 0
1288 					&& message.FindMessage("window state", stateMessageCounter++,
1289 							&state) == B_OK)
1290 					restoreStateFromMessage = true;
1291 
1292 				if (restoreStateFromMessage) {
1293 					OpenContainerWindow(model, 0, kOpen, kRestoreWorkspace
1294 						| (flags & kOpenWindowMinimized ? kIsHidden : 0U)
1295 						| kRestoreDecor, false, &state);
1296 				} else {
1297 					OpenContainerWindow(model, 0, kOpen, kRestoreWorkspace
1298 						| (flags & kOpenWindowMinimized ? kIsHidden : 0U)
1299 						| kRestoreDecor);
1300 				}
1301 			} else
1302 				delete model;
1303 		}
1304 	}
1305 
1306 	// Open disks window if needed
1307 
1308 	if (pathFilter == NULL && TrackerSettings().ShowDisksIcon()
1309 		&& message.HasBool("open_disks_window")) {
1310 		BEntry entry("/");
1311 		Model* model = new Model(&entry);
1312 		if (model->InitCheck() == B_OK)
1313 			OpenContainerWindow(model, 0, kOpen, kRestoreWorkspace);
1314 		else
1315 			delete model;
1316 	}
1317 }
1318 
1319 
1320 void
1321 TTracker::ReadyToRun()
1322 {
1323 	gStatusWindow = new BStatusWindow();
1324 	InitMimeTypes();
1325 	InstallDefaultTemplates();
1326 	InstallIndices();
1327 	InstallTemporaryBackgroundImages();
1328 
1329 	HideVarDir();
1330 
1331 	fTrashWatcher = new BTrashWatcher();
1332 	fTrashWatcher->Run();
1333 
1334 	fClipboardRefsWatcher = new BClipboardRefsWatcher();
1335 	fClipboardRefsWatcher->Run();
1336 
1337 	fAutoMounter = new AutoMounter();
1338 	fAutoMounter->Run();
1339 
1340 	fTaskLoop = new StandAloneTaskLoop(true);
1341 
1342 	// open desktop window
1343 	BContainerWindow *deskWindow = NULL;
1344 	BVolume	bootVolume;
1345 	BVolumeRoster().GetBootVolume(&bootVolume);
1346 	BDirectory deskDir;
1347 	if (FSGetDeskDir(&deskDir, bootVolume.Device()) == B_OK) {
1348 		// create desktop
1349 		BEntry entry;
1350 		deskDir.GetEntry(&entry);
1351 		Model *model = new Model(&entry);
1352 		if (model->InitCheck() == B_OK) {
1353 			AutoLock<WindowList> lock(&fWindowList);
1354 			deskWindow = new BDeskWindow(&fWindowList);
1355 			AutoLock<BWindow> windowLock(deskWindow);
1356 			deskWindow->CreatePoseView(model);
1357 			deskWindow->Init();
1358 
1359 			if (TrackerSettings().ShowDisksIcon()) {
1360 				// create model for root of everything
1361 				BEntry entry("/");
1362 				Model model(&entry);
1363 				if (model.InitCheck() == B_OK) {
1364 					// add the root icon to desktop window
1365 					BMessage message;
1366 					message.what = B_NODE_MONITOR;
1367 					message.AddInt32("opcode", B_ENTRY_CREATED);
1368 					message.AddInt32("device", model.NodeRef()->device);
1369 					message.AddInt64("node", model.NodeRef()->node);
1370 					message.AddInt64("directory", model.EntryRef()->directory);
1371 					message.AddString("name", model.EntryRef()->name);
1372 					deskWindow->PostMessage(&message, deskWindow->PoseView());
1373 				}
1374 			}
1375 		} else
1376 			delete model;
1377 	}
1378 
1379 	// kick off building the mime type list for find panels, etc.
1380 	fMimeTypeList = new MimeTypeList();
1381 
1382 	if (!BootedInSafeMode()) {
1383 		// kick of transient query killer
1384 		DeleteTransientQueriesTask::StartUpTransientQueryCleaner();
1385 	}
1386 }
1387 
1388 MimeTypeList *
1389 TTracker::MimeTypes() const
1390 {
1391 	return fMimeTypeList;
1392 }
1393 
1394 void
1395 TTracker::SelectChildInParentSoon(const entry_ref *parent,
1396 	const node_ref *child)
1397 {
1398 	fTaskLoop->RunLater(NewMemberFunctionObjectWithResult
1399 		(&TTracker::SelectChildInParent, this, parent, child),
1400 		100000, 200000, 5000000);
1401 }
1402 
1403 void
1404 TTracker::CloseParentWaitingForChildSoon(const entry_ref *child,
1405 	const node_ref *parent)
1406 {
1407 	fTaskLoop->RunLater(NewMemberFunctionObjectWithResult
1408 		(&TTracker::CloseParentWaitingForChild, this, child, parent),
1409 		200000, 100000, 5000000);
1410 }
1411 
1412 void
1413 TTracker::SelectPoseAtLocationSoon(node_ref parent, BPoint pointInPose)
1414 {
1415 	fTaskLoop->RunLater(NewMemberFunctionObject
1416 		(&TTracker::SelectPoseAtLocationInParent, this, parent, pointInPose),
1417 		100000);
1418 }
1419 
1420 void
1421 TTracker::SelectPoseAtLocationInParent(node_ref parent, BPoint pointInPose)
1422 {
1423 	AutoLock<WindowList> lock(&fWindowList);
1424 	BContainerWindow *parentWindow = FindContainerWindow(&parent);
1425 	if (parentWindow) {
1426 		AutoLock<BWindow> lock(parentWindow);
1427 		parentWindow->PoseView()->SelectPoseAtLocation(pointInPose);
1428 	}
1429 }
1430 
1431 bool
1432 TTracker::CloseParentWaitingForChild(const entry_ref *child,
1433 	const node_ref *parent)
1434 {
1435 	AutoLock<WindowList> lock(&fWindowList);
1436 
1437 	BContainerWindow *parentWindow = FindContainerWindow(parent);
1438 	if (!parentWindow)
1439 		// parent window already closed, give up
1440 		return true;
1441 
1442 	// If child is a symbolic link, dereference it, so that
1443 	// FindContainerWindow will succeed.
1444 	BEntry entry(child, true);
1445 	entry_ref resolvedChild;
1446 	if (entry.GetRef(&resolvedChild) != B_OK)
1447 		resolvedChild = *child;
1448 
1449 	BContainerWindow *window = FindContainerWindow(&resolvedChild);
1450 	if (window) {
1451 		AutoLock<BWindow> lock(window);
1452 		if (!window->IsHidden())
1453 			return CloseParentWindowCommon(parentWindow);
1454 	}
1455 	return false;
1456 }
1457 
1458 void
1459 TTracker::CloseParent(node_ref parent)
1460 {
1461 	AutoLock<WindowList> lock(&fWindowList);
1462 	if (!lock)
1463 		return;
1464 
1465 	CloseParentWindowCommon(FindContainerWindow(&parent));
1466 }
1467 
1468 void
1469 TTracker::ShowSettingsWindow()
1470 {
1471 	if (!fSettingsWindow) {
1472 		fSettingsWindow = new TrackerSettingsWindow();
1473 		fSettingsWindow->Show();
1474 	} else {
1475 		if (fSettingsWindow->Lock()) {
1476 			if (fSettingsWindow->IsHidden())
1477 				fSettingsWindow->Show();
1478 			else
1479 				fSettingsWindow->Activate();
1480 			fSettingsWindow->Unlock();
1481 		}
1482 	}
1483 }
1484 
1485 bool
1486 TTracker::CloseParentWindowCommon(BContainerWindow *window)
1487 {
1488 	ASSERT(fWindowList.IsLocked());
1489 
1490 	if (dynamic_cast<BDeskWindow *>(window))
1491 		// don't close the destop
1492 		return false;
1493 
1494 	window->PostMessage(B_QUIT_REQUESTED);
1495 	return true;
1496 }
1497 
1498 bool
1499 TTracker::SelectChildInParent(const entry_ref *parent, const node_ref *child)
1500 {
1501 	AutoLock<WindowList> lock(&fWindowList);
1502 
1503 	BContainerWindow *window = FindContainerWindow(parent);
1504 	if (!window)
1505 		// parent window already closed, give up
1506 		return false;
1507 
1508 	AutoLock<BWindow> windowLock(window);
1509 
1510 	if (windowLock.IsLocked()) {
1511 		BPoseView *view = window->PoseView();
1512 		int32 index;
1513 		BPose *pose = view->FindPose(child, &index);
1514 		if (pose) {
1515 			view->SelectPose(pose, index);
1516 			return true;
1517 		}
1518 	}
1519 	return false;
1520 }
1521 
1522 const int32 kNodeMonitorBumpValue = 512;
1523 
1524 status_t
1525 TTracker::NeedMoreNodeMonitors()
1526 {
1527 	fNodeMonitorCount += kNodeMonitorBumpValue;
1528 	PRINT(("bumping nodeMonitorCount to %d\n", fNodeMonitorCount));
1529 
1530 	return _kset_mon_limit_(fNodeMonitorCount);
1531 }
1532 
1533 status_t
1534 TTracker::WatchNode(const node_ref *node, uint32 flags,
1535 	BMessenger target)
1536 {
1537 	status_t result = watch_node(node, flags, target);
1538 	if (result == B_OK || result != B_NO_MEMORY) {
1539 		// need to make sure this uses the same error value as
1540 		// the node monitor code
1541 		return result;
1542 	}
1543 
1544 	PRINT(("failed to start monitoring, trying to allocate more "
1545 		"node monitors\n"));
1546 
1547 	TTracker *tracker = dynamic_cast<TTracker *>(be_app);
1548 	if (!tracker) {
1549 		// we are the file panel only, just fail
1550 		return result;
1551 	}
1552 
1553 	result = tracker->NeedMoreNodeMonitors();
1554 
1555 	if (result != B_OK) {
1556 		PRINT(("failed to allocate more node monitors, %s\n",
1557 			strerror(result)));
1558 		return result;
1559 	}
1560 
1561 	// try again, this time with more node monitors
1562 	return watch_node(node, flags, target);
1563 }
1564 
1565 
1566 AutoMounter *
1567 TTracker::AutoMounterLoop()
1568 {
1569 	return fAutoMounter;
1570 }
1571 
1572 
1573 bool
1574 TTracker::InTrashNode(const entry_ref *node) const
1575 {
1576 	return FSInTrashDir(node);
1577 }
1578 
1579 
1580 bool
1581 TTracker::TrashFull() const
1582 {
1583 	return fTrashWatcher->CheckTrashDirs();
1584 }
1585 
1586 
1587 bool
1588 TTracker::IsTrashNode(const node_ref *node) const
1589 {
1590 	return fTrashWatcher->IsTrashNode(node);
1591 }
1592 
1593