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