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