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