xref: /haiku/src/kits/tracker/Tracker.cpp (revision 5c6260dc232fcb2d4d5d1103c1623dba9663b753)
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_TRANSLATE_CONTEXT
209 #define B_TRANSLATE_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 	if (srcList->CountItems())
604 		// async move to trash
605 		FSMoveToTrash(srcList);
606 }
607 
608 
609 template <class T, class FT>
610 class EntryAndNodeDoSoonWithMessageFunctor : public FunctionObjectWithResult<bool> {
611 public:
612 	EntryAndNodeDoSoonWithMessageFunctor(FT func, T *target, const entry_ref *child,
613 		const node_ref *parent, const BMessage *message)
614 		:	fFunc(func),
615 			fTarget(target),
616 			fNode(*parent),
617 			fEntry(*child)
618 		{
619 			fSendMessage = (message != NULL);
620 			if (message)
621 				fMessage = *message;
622 		}
623 
624 	virtual ~EntryAndNodeDoSoonWithMessageFunctor() {}
625 	virtual void operator()()
626 		{ result = (fTarget->*fFunc)(&fEntry, &fNode, fSendMessage ? &fMessage : NULL); }
627 
628 protected:
629 	FT fFunc;
630 	T *fTarget;
631 	node_ref fNode;
632 	entry_ref fEntry;
633 	BMessage fMessage;
634 	bool fSendMessage;
635 };
636 
637 
638 bool
639 TTracker::LaunchAndCloseParentIfOK(const entry_ref *launchThis,
640 	const node_ref *closeThis, const BMessage *messageToBundle)
641 {
642 	BMessage refsReceived(B_REFS_RECEIVED);
643 	if (messageToBundle) {
644 		refsReceived = *messageToBundle;
645 		refsReceived.what = B_REFS_RECEIVED;
646 	}
647 	refsReceived.AddRef("refs", launchThis);
648 	// synchronous launch, we are already in our own thread
649 	if (TrackerLaunch(&refsReceived, false) == B_OK) {
650 		// if launched fine, close parent window in a bit
651 		fTaskLoop->RunLater(NewMemberFunctionObject(&TTracker::CloseParent, this, *closeThis),
652 			1000000);
653 	}
654 	return false;
655 }
656 
657 
658 status_t
659 TTracker::OpenRef(const entry_ref *ref, const node_ref *nodeToClose,
660 	const node_ref *nodeToSelect, OpenSelector selector,
661 	const BMessage *messageToBundle)
662 {
663 	Model *model = NULL;
664 	BEntry entry(ref, true);
665 	status_t result = entry.InitCheck();
666 
667 	bool brokenLinkWithSpecificHandler = false;
668 	BString brokenLinkPreferredApp;
669 
670 	if (result != B_OK) {
671 		model = new Model(ref, false);
672 		if (model->IsSymLink() && !model->LinkTo()) {
673 			model->GetPreferredAppForBrokenSymLink(brokenLinkPreferredApp);
674 			if (brokenLinkPreferredApp.Length() && brokenLinkPreferredApp != kTrackerSignature)
675 				brokenLinkWithSpecificHandler = true;
676 		}
677 
678 		if (!brokenLinkWithSpecificHandler) {
679 			delete model;
680 			BAlert* alert = new BAlert("",
681 				B_TRANSLATE("There was an error resolving the link."),
682 				B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT);
683 			alert->SetShortcut(0, B_ESCAPE);
684 			alert->Go();
685 			return result;
686 		}
687 	} else
688 		model = new Model(&entry);
689 
690 	result = model->InitCheck();
691 	if (result != B_OK) {
692 		delete model;
693 		return result;
694 	}
695 
696 	bool openAsContainer = model->IsContainer();
697 
698 	if (openAsContainer && selector != kOpenWith) {
699 		// if folder or query has a preferred handler and it's not the
700 		// Tracker, open it by sending refs to the handling app
701 
702 		// if we are responding to the final open of OpenWith, just
703 		// skip this and proceed to opening the container with Tracker
704 		model->OpenNode();
705 		BNodeInfo nodeInfo(model->Node());
706 		char preferredApp[B_MIME_TYPE_LENGTH];
707 		if (nodeInfo.GetPreferredApp(preferredApp) == B_OK
708 			&& strcasecmp(preferredApp, kTrackerSignature) != 0)
709 			openAsContainer = false;
710 		model->CloseNode();
711 	}
712 
713 	if (openAsContainer || selector == kRunOpenWithWindow) {
714 		// special case opening plain folders, queries or using open with
715 		OpenContainerWindow(model, 0, selector, kRestoreDecor);	// window adopts model
716 		if (nodeToClose)
717 			CloseParentWaitingForChildSoon(ref, nodeToClose);
718 	} else if (model->IsQueryTemplate()) {
719 		// query template - open new find window
720 		(new FindWindow(model->EntryRef()))->Show();
721 		if (nodeToClose)
722 			CloseParentWaitingForChildSoon(ref, nodeToClose);
723 	} else {
724 		delete model;
725 		// run Launch in a separate thread
726 		// and close parent if successfull
727 		if (nodeToClose)
728 			Thread::Launch(new EntryAndNodeDoSoonWithMessageFunctor<TTracker,
729 				bool (TTracker::*)(const entry_ref *, const node_ref *,
730 				const BMessage *)>(&TTracker::LaunchAndCloseParentIfOK, this,
731 				ref, nodeToClose, messageToBundle));
732 		else {
733 			BMessage refsReceived(B_REFS_RECEIVED);
734 			if (messageToBundle) {
735 				refsReceived = *messageToBundle;
736 				refsReceived.what = B_REFS_RECEIVED;
737 			}
738 			refsReceived.AddRef("refs", ref);
739 			if (brokenLinkWithSpecificHandler)
740 				// This cruft is to support a hacky workaround for double-clicking
741 				// broken refs for cifs; should get fixed in R5
742 				LaunchBrokenLink(brokenLinkPreferredApp.String(), &refsReceived);
743 			else
744 				TrackerLaunch(&refsReceived, true);
745 		}
746 	}
747 	if (nodeToSelect)
748 		SelectChildInParentSoon(ref, nodeToSelect);
749 
750 	return B_OK;
751 }
752 
753 
754 void
755 TTracker::RefsReceived(BMessage *message)
756 {
757 	OpenSelector selector = kOpen;
758 	if (message->HasInt32("launchUsingSelector"))
759 		selector = kRunOpenWithWindow;
760 
761 	entry_ref handlingApp;
762 	if (message->FindRef("handler", &handlingApp) == B_OK)
763 		selector = kOpenWith;
764 
765 	int32 count;
766 	uint32 type;
767 	message->GetInfo("refs", &type, &count);
768 
769 	switch (selector) {
770 		case kRunOpenWithWindow:
771 			OpenContainerWindow(0, message, selector);
772 				// window adopts model
773 			break;
774 
775 		case kOpenWith:
776 			{
777 				// Open With resulted in passing refs and a handler, open the files
778 				// with the handling app
779 				message->RemoveName("handler");
780 
781 				// have to find out if handling app is the Tracker
782 				// if it is, just pass it to the active Tracker, no matter which Tracker
783 				// was chosen to handle the refs
784 				char signature[B_MIME_TYPE_LENGTH];
785 				signature[0] = '\0';
786 				{
787 					BFile handlingNode(&handlingApp, O_RDONLY);
788 					BAppFileInfo appInfo(&handlingNode);
789 					appInfo.GetSignature(signature);
790 				}
791 
792 				if (strcasecmp(signature, kTrackerSignature) != 0) {
793 					// handling app not Tracker, pass entries to the apps RefsReceived
794 					TrackerLaunch(&handlingApp, message, true);
795 					break;
796 				}
797 				// fall thru, opening refs by the Tracker, as if they were double clicked
798 			}
799 
800 		case kOpen:
801 			{
802 				// copy over "Poses" messenger so that refs received recipients know
803 				// where the open came from
804 				BMessage *bundleThis = NULL;
805 				BMessenger messenger;
806 				if (message->FindMessenger("TrackerViewToken", &messenger) == B_OK) {
807 					bundleThis = new BMessage();
808 					bundleThis->AddMessenger("TrackerViewToken", messenger);
809 				}
810 
811 				for (int32 index = 0; index < count; index++) {
812 					entry_ref ref;
813 					message->FindRef("refs", index, &ref);
814 
815 					const node_ref *nodeToClose = NULL;
816 					const node_ref *nodeToSelect = NULL;
817 					ssize_t numBytes;
818 
819 					message->FindData("nodeRefsToClose", B_RAW_TYPE, index,
820 						(const void **)&nodeToClose, &numBytes);
821 					message->FindData("nodeRefToSelect", B_RAW_TYPE, index,
822 						(const void **)&nodeToSelect, &numBytes);
823 
824 					OpenRef(&ref, nodeToClose, nodeToSelect, selector, bundleThis);
825 				}
826 
827 				delete bundleThis;
828 				break;
829 			}
830 	}
831 }
832 
833 
834 void
835 TTracker::ArgvReceived(int32 argc, char **argv)
836 {
837 	BMessage *message = CurrentMessage();
838 	const char *currentWorkingDirectoryPath = NULL;
839 	entry_ref ref;
840 
841 	if (message->FindString("cwd", &currentWorkingDirectoryPath) == B_OK) {
842 		BDirectory workingDirectory(currentWorkingDirectoryPath);
843 		for (int32 index = 1; index < argc; index++) {
844 			BEntry entry;
845 			if (entry.SetTo(&workingDirectory, argv[index]) == B_OK
846 				&& entry.GetRef(&ref) == B_OK)
847 				OpenRef(&ref);
848 			else if (get_ref_for_path(argv[index], &ref) == B_OK)
849 				OpenRef(&ref);
850 		}
851 	}
852 }
853 
854 void
855 TTracker::OpenContainerWindow(Model *model, BMessage *originalRefsList,
856 	OpenSelector openSelector, uint32 openFlags, bool checkAlreadyOpen,
857 	const BMessage *stateMessage)
858 {
859 	AutoLock<WindowList> lock(&fWindowList);
860 	BContainerWindow *window = NULL;
861 	if (checkAlreadyOpen && openSelector != kRunOpenWithWindow)
862 		// find out if window already open
863 		window = FindContainerWindow(model->NodeRef());
864 
865 	bool someWindowActivated = false;
866 
867 	uint32 workspace = (uint32)(1 << current_workspace());
868 	int32 windowCount = 0;
869 
870 	while (window) {
871 		// At least one window open, just pull to front
872 		// make sure we don't jerk workspaces around
873 		uint32 windowWorkspaces = window->Workspaces();
874 		if (windowWorkspaces & workspace) {
875 			window->Activate();
876 			someWindowActivated = true;
877 		}
878 		window = FindContainerWindow(model->NodeRef(), ++windowCount);
879 	}
880 
881 	if (someWindowActivated) {
882 		delete model;
883 		return;
884 	} // If no window was actiated, (none in the current workspace
885 	  // we open a new one.
886 
887 	if (openSelector == kRunOpenWithWindow) {
888 		BMessage *refList = NULL;
889 		if (!originalRefsList) {
890 			// when passing just a single model, stuff it's entry in a single
891 			// element list anyway
892 			ASSERT(model);
893 			refList = new BMessage;
894 			refList->AddRef("refs", model->EntryRef());
895 			delete model;
896 			model = NULL;
897 		} else
898 			// clone the message, window adopts it for it's own use
899 			refList = new BMessage(*originalRefsList);
900 		window = new OpenWithContainerWindow(refList, &fWindowList);
901 	} else if (model->IsRoot()) {
902 		// window will adopt the model
903 		window = new BVolumeWindow(&fWindowList, openFlags);
904 	} else if (model->IsQuery()) {
905 		// window will adopt the model
906 		window = new BQueryContainerWindow(&fWindowList, openFlags);
907 	} else
908 		// window will adopt the model
909 		window = new BContainerWindow(&fWindowList, openFlags);
910 
911 	if (model)
912 		window->CreatePoseView(model);
913 
914 	BMessage restoreStateMessage(kRestoreState);
915 
916 	if (stateMessage)
917 		restoreStateMessage.AddMessage("state", stateMessage);
918 
919 	window->PostMessage(&restoreStateMessage);
920 }
921 
922 
923 void
924 TTracker::EditQueries(const BMessage *message)
925 {
926 	bool editOnlyIfTemplate;
927 	if (message->FindBool("editQueryOnPose", &editOnlyIfTemplate) != B_OK)
928 		editOnlyIfTemplate = false;
929 
930 	type_code type;
931 	int32 count;
932 	message->GetInfo("refs", &type, &count);
933 	for (int32 index = 0; index < count; index++) {
934 		entry_ref ref;
935 		message->FindRef("refs", index, &ref);
936 		BEntry entry(&ref, true);
937 		if (entry.InitCheck() == B_OK && entry.Exists())
938 			(new FindWindow(&ref, editOnlyIfTemplate))->Show();
939 	}
940 }
941 
942 
943 void
944 TTracker::OpenInfoWindows(BMessage *message)
945 {
946 	type_code type;
947 	int32 count;
948 	message->GetInfo("refs", &type, &count);
949 
950 	for (int32 index = 0; index < count; index++) {
951 		entry_ref ref;
952 		message->FindRef("refs", index, &ref);
953 		BEntry entry;
954 		if (entry.SetTo(&ref) == B_OK) {
955 			Model *model = new Model(&entry);
956 			if (model->InitCheck() != B_OK) {
957 				delete model;
958 				continue;
959 			}
960 
961 			AutoLock<WindowList> lock(&fWindowList);
962 			BInfoWindow *wind = FindInfoWindow(model->NodeRef());
963 
964 			if (wind) {
965 				wind->Activate();
966 				delete model;
967 			} else {
968 				wind = new BInfoWindow(model, index, &fWindowList);
969 				wind->PostMessage(kRestoreState);
970 			}
971 		}
972 	}
973 }
974 
975 
976 BDeskWindow *
977 TTracker::GetDeskWindow() const
978 {
979 	int32 count = fWindowList.CountItems();
980 	for (int32 index = 0; index < count; index++) {
981 		BDeskWindow *window = dynamic_cast<BDeskWindow *>
982 			(fWindowList.ItemAt(index));
983 
984 		if (window)
985 			return window;
986 	}
987 	TRESPASS();
988 	return NULL;
989 }
990 
991 
992 BContainerWindow *
993 TTracker::FindContainerWindow(const node_ref *node, int32 number) const
994 {
995 	ASSERT(fWindowList.IsLocked());
996 
997 	int32 count = fWindowList.CountItems();
998 
999 	int32 windowsFound = 0;
1000 
1001 	for (int32 index = 0; index < count; index++) {
1002 		BContainerWindow *window = dynamic_cast<BContainerWindow *>
1003 			(fWindowList.ItemAt(index));
1004 
1005 		if (window && window->IsShowing(node) && number == windowsFound++)
1006 			return window;
1007 	}
1008 	return NULL;
1009 }
1010 
1011 
1012 BContainerWindow *
1013 TTracker::FindContainerWindow(const entry_ref *entry, int32 number) const
1014 {
1015 	ASSERT(fWindowList.IsLocked());
1016 
1017 	int32 count = fWindowList.CountItems();
1018 
1019 	int32 windowsFound = 0;
1020 
1021 	for (int32 index = 0; index < count; index++) {
1022 		BContainerWindow *window = dynamic_cast<BContainerWindow *>
1023 			(fWindowList.ItemAt(index));
1024 
1025 		if (window && window->IsShowing(entry) && number == windowsFound++)
1026 			return window;
1027 	}
1028 	return NULL;
1029 }
1030 
1031 
1032 bool
1033 TTracker::EntryHasWindowOpen(const entry_ref *entry)
1034 {
1035 	AutoLock<WindowList> lock(&fWindowList);
1036 	return FindContainerWindow(entry) != NULL;
1037 }
1038 
1039 
1040 BContainerWindow *
1041 TTracker::FindParentContainerWindow(const entry_ref *ref) const
1042 {
1043 	BEntry entry(ref);
1044 	BEntry parent;
1045 
1046 	if (entry.GetParent(&parent) != B_OK)
1047 		return NULL;
1048 
1049 	entry_ref parentRef;
1050 	parent.GetRef(&parentRef);
1051 
1052 	ASSERT(fWindowList.IsLocked());
1053 
1054 	int32 count = fWindowList.CountItems();
1055 	for (int32 index = 0; index < count; index++) {
1056 		BContainerWindow *window = dynamic_cast<BContainerWindow *>
1057 			(fWindowList.ItemAt(index));
1058 		if (window && window->IsShowing(&parentRef))
1059 			return window;
1060 	}
1061 	return NULL;
1062 }
1063 
1064 
1065 BInfoWindow *
1066 TTracker::FindInfoWindow(const node_ref* node) const
1067 {
1068 	ASSERT(fWindowList.IsLocked());
1069 
1070 	int32 count = fWindowList.CountItems();
1071 	for (int32 index = 0; index < count; index++) {
1072 		BInfoWindow *window = dynamic_cast<BInfoWindow *>
1073 			(fWindowList.ItemAt(index));
1074 		if (window && window->IsShowing(node))
1075 			return window;
1076 	}
1077 	return NULL;
1078 }
1079 
1080 
1081 bool
1082 TTracker::QueryActiveForDevice(dev_t device)
1083 {
1084 	AutoLock<WindowList> lock(&fWindowList);
1085 	int32 count = fWindowList.CountItems();
1086 	for (int32 index = 0; index < count; index++) {
1087 		BQueryContainerWindow *window = dynamic_cast<BQueryContainerWindow *>
1088 			(fWindowList.ItemAt(index));
1089 		if (window) {
1090 			AutoLock<BWindow> lock(window);
1091 			if (window->ActiveOnDevice(device))
1092 				return true;
1093 		}
1094 	}
1095 	return false;
1096 }
1097 
1098 
1099 void
1100 TTracker::CloseActiveQueryWindows(dev_t device)
1101 {
1102 	// used when trying to unmount a volume - an active query would prevent that from
1103 	// happening
1104 	bool closed = false;
1105 	AutoLock<WindowList> lock(fWindowList);
1106 	for (int32 index = fWindowList.CountItems(); index >= 0; index--) {
1107 		BQueryContainerWindow *window = dynamic_cast<BQueryContainerWindow *>
1108 			(fWindowList.ItemAt(index));
1109 		if (window) {
1110 			AutoLock<BWindow> lock(window);
1111 			if (window->ActiveOnDevice(device)) {
1112 				window->PostMessage(B_QUIT_REQUESTED);
1113 				closed = true;
1114 			}
1115 		}
1116 	}
1117 	lock.Unlock();
1118 	if (closed)
1119 		for (int32 timeout = 30; timeout; timeout--) {
1120 			// wait a bit for windows to fully close
1121 			if (!QueryActiveForDevice(device))
1122 				return;
1123 			snooze(100000);
1124 		}
1125 }
1126 
1127 
1128 void
1129 TTracker::SaveAllPoseLocations()
1130 {
1131 	int32 numWindows = fWindowList.CountItems();
1132 	for (int32 windowIndex = 0; windowIndex < numWindows; windowIndex++) {
1133 		BContainerWindow *window = dynamic_cast<BContainerWindow *>
1134 			(fWindowList.ItemAt(windowIndex));
1135 
1136 		if (window) {
1137 			AutoLock<BWindow> lock(window);
1138 			BDeskWindow *deskWindow = dynamic_cast<BDeskWindow *>(window);
1139 
1140 			if (deskWindow)
1141 				deskWindow->SaveDesktopPoseLocations();
1142 			else
1143 				window->PoseView()->SavePoseLocations();
1144 		}
1145 	}
1146 }
1147 
1148 
1149 void
1150 TTracker::CloseWindowAndChildren(const node_ref *node)
1151 {
1152 	BDirectory dir(node);
1153 	if (dir.InitCheck() != B_OK)
1154 		return;
1155 
1156 	AutoLock<WindowList> lock(&fWindowList);
1157 	BObjectList<BContainerWindow> closeList;
1158 
1159 	// make a list of all windows to be closed
1160 	// count from end to beginning so we can remove items safely
1161 	for (int32 index = fWindowList.CountItems() - 1; index >= 0; index--) {
1162 		BContainerWindow *window = dynamic_cast<BContainerWindow *>
1163 			(fWindowList.ItemAt(index));
1164 		if (window && window->TargetModel()) {
1165 			BEntry wind_entry;
1166 			wind_entry.SetTo(window->TargetModel()->EntryRef());
1167 
1168 			if ((*window->TargetModel()->NodeRef() == *node)
1169 				|| dir.Contains(&wind_entry)) {
1170 
1171 				// ToDo:
1172 				// get rid of the Remove here, BContainerWindow::Quit does it
1173 				fWindowList.RemoveItemAt(index);
1174 				closeList.AddItem(window);
1175 			}
1176 		}
1177 	}
1178 
1179 	// now really close the windows
1180 	int32 numItems = closeList.CountItems();
1181 	for (int32 index = 0; index < numItems; index++) {
1182 		BContainerWindow *window = closeList.ItemAt(index);
1183 		window->PostMessage(B_QUIT_REQUESTED);
1184 	}
1185 }
1186 
1187 
1188 void
1189 TTracker::CloseAllInWorkspace()
1190 {
1191 	AutoLock<WindowList> lock(&fWindowList);
1192 
1193 	int32 currentWorkspace = 1 << current_workspace();
1194 	// count from end to beginning so we can remove items safely
1195 	for (int32 index = fWindowList.CountItems() - 1; index >= 0; index--) {
1196 		BWindow *window = fWindowList.ItemAt(index);
1197 		if (window->Workspaces() & currentWorkspace)
1198 			// avoid the desktop
1199 			if (!dynamic_cast<BDeskWindow *>(window)
1200 				&& !dynamic_cast<BStatusWindow *>(window))
1201 				window->PostMessage(B_QUIT_REQUESTED);
1202 	}
1203 }
1204 
1205 
1206 void
1207 TTracker::CloseAllWindows()
1208 {
1209 	// this is a response to the DeskBar sending us a B_QUIT, when it really
1210 	// means to say close all your windows. It might be better to have it
1211 	// send a kCloseAllWindows message and have windowless apps stay running,
1212 	// which is what we will do for the Tracker
1213 	AutoLock<WindowList> lock(&fWindowList);
1214 
1215 	int32 count = CountWindows();
1216 	for (int32 index = 0; index < count; index++) {
1217 		BWindow *window = WindowAt(index);
1218 		// avoid the desktop
1219 		if (!dynamic_cast<BDeskWindow *>(window)
1220 			&& !dynamic_cast<BStatusWindow *>(window))
1221 			window->PostMessage(B_QUIT_REQUESTED);
1222 	}
1223 	// count from end to beginning so we can remove items safely
1224 	for (int32 index = fWindowList.CountItems() - 1; index >= 0; index--) {
1225 		BWindow *window = fWindowList.ItemAt(index);
1226 		if (!dynamic_cast<BDeskWindow *>(window)
1227 			&& !dynamic_cast<BStatusWindow *>(window))
1228 				// ToDo:
1229 				// get rid of the Remove here, BContainerWindow::Quit does it
1230 			fWindowList.RemoveItemAt(index);
1231 	}
1232 }
1233 
1234 
1235 void
1236 TTracker::_OpenPreviouslyOpenedWindows(const char* pathFilter)
1237 {
1238 	size_t filterLength = 0;
1239 	if (pathFilter != NULL)
1240 		filterLength = strlen(pathFilter);
1241 
1242 	BDirectory deskDir;
1243 	attr_info attrInfo;
1244 	if (FSGetDeskDir(&deskDir) != B_OK
1245 		|| deskDir.GetAttrInfo(kAttrOpenWindows, &attrInfo) != B_OK)
1246 		return;
1247 
1248 	char *buffer = (char *)malloc((size_t)attrInfo.size);
1249 	BMessage message;
1250 	if (deskDir.ReadAttr(kAttrOpenWindows, B_MESSAGE_TYPE, 0, buffer,
1251 		(size_t)attrInfo.size) != attrInfo.size
1252 		|| message.Unflatten(buffer) != B_OK) {
1253 		free(buffer);
1254 		return;
1255 	}
1256 
1257 	free(buffer);
1258 
1259 	node_ref nodeRef;
1260 	deskDir.GetNodeRef(&nodeRef);
1261 
1262 	int32 stateMessageCounter = 0;
1263 	const char *path;
1264 	for (int32 i = 0; message.FindString("paths", i, &path) == B_OK; i++) {
1265 		if (strncmp(path, pathFilter, filterLength))
1266 			continue;
1267 
1268 		BEntry entry(path, true);
1269 		if (entry.InitCheck() != B_OK)
1270 			continue;
1271 
1272 		int8 flags = 0;
1273 		for (int32 j = 0; message.FindInt8(path, j, &flags) == B_OK; j++) {
1274 			Model *model = new Model(&entry);
1275 			if (model->InitCheck() == B_OK && model->IsContainer()) {
1276 				BMessage state;
1277 				bool restoreStateFromMessage = false;
1278 				if ((flags & kOpenWindowHasState) != 0
1279 					&& message.FindMessage("window state", stateMessageCounter++,
1280 							&state) == B_OK)
1281 					restoreStateFromMessage = true;
1282 
1283 				if (restoreStateFromMessage) {
1284 					OpenContainerWindow(model, 0, kOpen, kRestoreWorkspace
1285 						| (flags & kOpenWindowMinimized ? kIsHidden : 0U)
1286 						| kRestoreDecor, false, &state);
1287 				} else {
1288 					OpenContainerWindow(model, 0, kOpen, kRestoreWorkspace
1289 						| (flags & kOpenWindowMinimized ? kIsHidden : 0U)
1290 						| kRestoreDecor);
1291 				}
1292 			} else
1293 				delete model;
1294 		}
1295 	}
1296 
1297 	// Open disks window if needed
1298 
1299 	if (pathFilter == NULL && TrackerSettings().ShowDisksIcon()
1300 		&& message.HasBool("open_disks_window")) {
1301 		BEntry entry("/");
1302 		Model* model = new Model(&entry);
1303 		if (model->InitCheck() == B_OK)
1304 			OpenContainerWindow(model, 0, kOpen, kRestoreWorkspace);
1305 		else
1306 			delete model;
1307 	}
1308 }
1309 
1310 
1311 void
1312 TTracker::ReadyToRun()
1313 {
1314 	gStatusWindow = new BStatusWindow();
1315 	InitMimeTypes();
1316 	InstallDefaultTemplates();
1317 	InstallIndices();
1318 	InstallTemporaryBackgroundImages();
1319 
1320 	fTrashWatcher = new BTrashWatcher();
1321 	fTrashWatcher->Run();
1322 
1323 	fClipboardRefsWatcher = new BClipboardRefsWatcher();
1324 	fClipboardRefsWatcher->Run();
1325 
1326 	fTaskLoop = new StandAloneTaskLoop(true);
1327 
1328 	// open desktop window
1329 	BContainerWindow *deskWindow = NULL;
1330 	BDirectory deskDir;
1331 	if (FSGetDeskDir(&deskDir) == B_OK) {
1332 		// create desktop
1333 		BEntry entry;
1334 		deskDir.GetEntry(&entry);
1335 		Model *model = new Model(&entry, true);
1336 		if (model->InitCheck() == B_OK) {
1337 			AutoLock<WindowList> lock(&fWindowList);
1338 			deskWindow = new BDeskWindow(&fWindowList);
1339 			AutoLock<BWindow> windowLock(deskWindow);
1340 			deskWindow->CreatePoseView(model);
1341 			deskWindow->Init();
1342 
1343 			if (TrackerSettings().ShowDisksIcon()) {
1344 				// create model for root of everything
1345 				BEntry entry("/");
1346 				Model model(&entry);
1347 				if (model.InitCheck() == B_OK) {
1348 					// add the root icon to desktop window
1349 					BMessage message;
1350 					message.what = B_NODE_MONITOR;
1351 					message.AddInt32("opcode", B_ENTRY_CREATED);
1352 					message.AddInt32("device", model.NodeRef()->device);
1353 					message.AddInt64("node", model.NodeRef()->node);
1354 					message.AddInt64("directory", model.EntryRef()->directory);
1355 					message.AddString("name", model.EntryRef()->name);
1356 					deskWindow->PostMessage(&message, deskWindow->PoseView());
1357 				}
1358 			}
1359 		} else
1360 			delete model;
1361 	}
1362 
1363 	// kick off building the mime type list for find panels, etc.
1364 	fMimeTypeList = new MimeTypeList();
1365 
1366 	if (!BootedInSafeMode()) {
1367 		// kick of transient query killer
1368 		DeleteTransientQueriesTask::StartUpTransientQueryCleaner();
1369 		// the mount_server will have mounted the previous volumes already.
1370 		_OpenPreviouslyOpenedWindows();
1371 	}
1372 }
1373 
1374 MimeTypeList *
1375 TTracker::MimeTypes() const
1376 {
1377 	return fMimeTypeList;
1378 }
1379 
1380 void
1381 TTracker::SelectChildInParentSoon(const entry_ref *parent,
1382 	const node_ref *child)
1383 {
1384 	fTaskLoop->RunLater(NewMemberFunctionObjectWithResult
1385 		(&TTracker::SelectChildInParent, this, parent, child),
1386 		100000, 200000, 5000000);
1387 }
1388 
1389 void
1390 TTracker::CloseParentWaitingForChildSoon(const entry_ref *child,
1391 	const node_ref *parent)
1392 {
1393 	fTaskLoop->RunLater(NewMemberFunctionObjectWithResult
1394 		(&TTracker::CloseParentWaitingForChild, this, child, parent),
1395 		200000, 100000, 5000000);
1396 }
1397 
1398 void
1399 TTracker::SelectPoseAtLocationSoon(node_ref parent, BPoint pointInPose)
1400 {
1401 	fTaskLoop->RunLater(NewMemberFunctionObject
1402 		(&TTracker::SelectPoseAtLocationInParent, this, parent, pointInPose),
1403 		100000);
1404 }
1405 
1406 void
1407 TTracker::SelectPoseAtLocationInParent(node_ref parent, BPoint pointInPose)
1408 {
1409 	AutoLock<WindowList> lock(&fWindowList);
1410 	BContainerWindow *parentWindow = FindContainerWindow(&parent);
1411 	if (parentWindow) {
1412 		AutoLock<BWindow> lock(parentWindow);
1413 		parentWindow->PoseView()->SelectPoseAtLocation(pointInPose);
1414 	}
1415 }
1416 
1417 bool
1418 TTracker::CloseParentWaitingForChild(const entry_ref *child,
1419 	const node_ref *parent)
1420 {
1421 	AutoLock<WindowList> lock(&fWindowList);
1422 
1423 	BContainerWindow *parentWindow = FindContainerWindow(parent);
1424 	if (!parentWindow)
1425 		// parent window already closed, give up
1426 		return true;
1427 
1428 	// If child is a symbolic link, dereference it, so that
1429 	// FindContainerWindow will succeed.
1430 	BEntry entry(child, true);
1431 	entry_ref resolvedChild;
1432 	if (entry.GetRef(&resolvedChild) != B_OK)
1433 		resolvedChild = *child;
1434 
1435 	BContainerWindow *window = FindContainerWindow(&resolvedChild);
1436 	if (window) {
1437 		AutoLock<BWindow> lock(window);
1438 		if (!window->IsHidden())
1439 			return CloseParentWindowCommon(parentWindow);
1440 	}
1441 	return false;
1442 }
1443 
1444 void
1445 TTracker::CloseParent(node_ref parent)
1446 {
1447 	AutoLock<WindowList> lock(&fWindowList);
1448 	if (!lock)
1449 		return;
1450 
1451 	CloseParentWindowCommon(FindContainerWindow(&parent));
1452 }
1453 
1454 void
1455 TTracker::ShowSettingsWindow()
1456 {
1457 	if (!fSettingsWindow) {
1458 		fSettingsWindow = new TrackerSettingsWindow();
1459 		fSettingsWindow->Show();
1460 	} else {
1461 		if (fSettingsWindow->Lock()) {
1462 			if (fSettingsWindow->IsHidden())
1463 				fSettingsWindow->Show();
1464 			else
1465 				fSettingsWindow->Activate();
1466 			fSettingsWindow->Unlock();
1467 		}
1468 	}
1469 }
1470 
1471 bool
1472 TTracker::CloseParentWindowCommon(BContainerWindow *window)
1473 {
1474 	ASSERT(fWindowList.IsLocked());
1475 
1476 	if (dynamic_cast<BDeskWindow *>(window))
1477 		// don't close the destop
1478 		return false;
1479 
1480 	window->PostMessage(B_QUIT_REQUESTED);
1481 	return true;
1482 }
1483 
1484 bool
1485 TTracker::SelectChildInParent(const entry_ref *parent, const node_ref *child)
1486 {
1487 	AutoLock<WindowList> lock(&fWindowList);
1488 
1489 	BContainerWindow *window = FindContainerWindow(parent);
1490 	if (!window)
1491 		// parent window already closed, give up
1492 		return false;
1493 
1494 	AutoLock<BWindow> windowLock(window);
1495 
1496 	if (windowLock.IsLocked()) {
1497 		BPoseView *view = window->PoseView();
1498 		int32 index;
1499 		BPose *pose = view->FindPose(child, &index);
1500 		if (pose) {
1501 			view->SelectPose(pose, index);
1502 			return true;
1503 		}
1504 	}
1505 	return false;
1506 }
1507 
1508 const int32 kNodeMonitorBumpValue = 512;
1509 
1510 status_t
1511 TTracker::NeedMoreNodeMonitors()
1512 {
1513 	fNodeMonitorCount += kNodeMonitorBumpValue;
1514 	PRINT(("bumping nodeMonitorCount to %" B_PRId32 "\n", fNodeMonitorCount));
1515 
1516 	struct rlimit rl;
1517 	rl.rlim_cur = fNodeMonitorCount;
1518 	rl.rlim_max = RLIM_SAVED_MAX;
1519 	if (setrlimit(RLIMIT_NOVMON, &rl) < 0) {
1520 		fNodeMonitorCount -= kNodeMonitorBumpValue;
1521 		return errno;
1522 	}
1523 	return B_OK;
1524 
1525 }
1526 
1527 status_t
1528 TTracker::WatchNode(const node_ref *node, uint32 flags,
1529 	BMessenger target)
1530 {
1531 	status_t result = watch_node(node, flags, target);
1532 	if (result == B_OK || result != B_NO_MEMORY) {
1533 		// need to make sure this uses the same error value as
1534 		// the node monitor code
1535 		return result;
1536 	}
1537 
1538 	PRINT(("failed to start monitoring, trying to allocate more "
1539 		"node monitors\n"));
1540 
1541 	TTracker *tracker = dynamic_cast<TTracker *>(be_app);
1542 	if (!tracker) {
1543 		// we are the file panel only, just fail
1544 		return result;
1545 	}
1546 
1547 	result = tracker->NeedMoreNodeMonitors();
1548 
1549 	if (result != B_OK) {
1550 		PRINT(("failed to allocate more node monitors, %s\n",
1551 			strerror(result)));
1552 		return result;
1553 	}
1554 
1555 	// try again, this time with more node monitors
1556 	return watch_node(node, flags, target);
1557 }
1558 
1559 
1560 BMessenger
1561 TTracker::MountServer() const
1562 {
1563 	return BMessenger(kMountServerSignature);
1564 }
1565 
1566 
1567 bool
1568 TTracker::InTrashNode(const entry_ref *node) const
1569 {
1570 	return FSInTrashDir(node);
1571 }
1572 
1573 
1574 bool
1575 TTracker::TrashFull() const
1576 {
1577 	return fTrashWatcher->CheckTrashDirs();
1578 }
1579 
1580 
1581 bool
1582 TTracker::IsTrashNode(const node_ref *node) const
1583 {
1584 	return fTrashWatcher->IsTrashNode(node);
1585 }
1586 
1587