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