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