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