xref: /haiku/src/kits/tracker/Tracker.cpp (revision 2f470aec1c92ce6917b8a903e343795dc77af41f)
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 #if B_BEOS_VERSION_DANO
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 #if B_BEOS_VERSION_DANO
96 #undef _IMPEXP_ROOT
97 #endif
98 	// from priv_syscalls.h
99 
100 const int32 DEFAULT_MON_NUM = 4096;
101 	// copied from fsil.c
102 
103 const int8 kOpenWindowNoFlags = 0;
104 const int8 kOpenWindowMinimized = 1;
105 const int8 kOpenWindowHasState = 2;
106 
107 const uint32 PSV_MAKE_PRINTER_ACTIVE_QUIETLY = 'pmaq';
108 	// from pr_server.h
109 
110 
111 namespace BPrivate {
112 
113 NodePreloader *gPreloader = NULL;
114 
115 void
116 InitIconPreloader()
117 {
118 	static int32 lock = 0;
119 
120 	if (atomic_add(&lock, 1) != 0) {
121 		// Just wait for the icon cache to be instantiated
122 		int32 tries = 20;
123 		while (IconCache::sIconCache == NULL && tries-- > 0)
124 			snooze(10000);
125 		return;
126 	}
127 
128 	if (IconCache::sIconCache != NULL)
129 		return;
130 
131 	// only start the node preloader if its Tracker or the Deskbar itself - don't
132 	// start it for file panels
133 
134 	bool preload = dynamic_cast<TTracker *>(be_app) != NULL;
135 	if (!preload) {
136 		// check for deskbar
137 		app_info info;
138 		if (be_app->GetAppInfo(&info) == B_OK
139 			&& !strcmp(info.signature, kDeskbarSignature))
140 			preload = true;
141 	}
142 	if (preload)
143 		gPreloader = NodePreloader::InstallNodePreloader("NodePreloader", be_app);
144 
145 	IconCache::sIconCache = new IconCache();
146 
147 	atomic_add(&lock, -1);
148 }
149 
150 }	// namespace BPrivate
151 
152 
153 uint32
154 GetVolumeFlags(Model *model)
155 {
156 	fs_info info;
157 	if (model->IsVolume()) {
158 		// search for the correct volume
159 		int32 cookie = 0;
160 		dev_t device;
161 		while ((device = next_dev(&cookie)) >= B_OK) {
162 			if (fs_stat_dev(device,&info))
163 				continue;
164 
165 			if (!strcmp(info.volume_name,model->Name()))
166 				return info.flags;
167 		}
168 		return B_FS_HAS_ATTR;
169 	}
170 	if (!fs_stat_dev(model->NodeRef()->device,&info))
171 		return info.flags;
172 
173 	return B_FS_HAS_ATTR;
174 }
175 
176 
177 static void
178 HideVarDir()
179 {
180 	BPath path;
181 	status_t err = find_directory(B_COMMON_VAR_DIRECTORY, &path);
182 
183 	if (err != B_OK){
184 		PRINT(("var err = %s\n", strerror(err)));
185 		return;
186 	}
187 
188 	BDirectory varDirectory(path.Path());
189 	if (varDirectory.InitCheck() == B_OK) {
190 		PoseInfo info;
191 		// make var dir invisible
192 		info.fInvisible = true;
193 		info.fInitedDirectory = -1;
194 
195 		if (varDirectory.WriteAttr(kAttrPoseInfo, B_RAW_TYPE, 0, &info, sizeof(info))
196 			== sizeof(info))
197 			varDirectory.RemoveAttr(kAttrPoseInfoForeign);
198 	}
199 }
200 
201 
202 //	#pragma mark -
203 
204 
205 TTracker::TTracker()
206 	:	BApplication(kTrackerSignature),
207 	fSettingsWindow(NULL)
208 {
209 	// set the cwd to /boot/home, anything that's launched
210 	// from Tracker will automatically inherit this
211 	BPath homePath;
212 
213 	if (find_directory(B_USER_DIRECTORY, &homePath) == B_OK)
214 		chdir(homePath.Path());
215 
216 	_kset_fd_limit_(512);
217 		// ask for a bunch more file descriptors so that nested copying
218 		// works well
219 
220 	fNodeMonitorCount = DEFAULT_MON_NUM;
221 
222 #ifdef CHECK_OPEN_MODEL_LEAKS
223 	InitOpenModelDumping();
224 #endif
225 
226 	InitIconPreloader();
227 
228 #ifdef LEAK_CHECKING
229 	SetNewLeakChecking(true);
230 	SetMallocLeakChecking(true);
231 #endif
232 
233 	//This is how often it should update the free space bar on the volume icons
234 	SetPulseRate(1000000);
235 }
236 
237 
238 TTracker::~TTracker()
239 {
240 }
241 
242 
243 bool
244 TTracker::QuitRequested()
245 {
246 	// don't allow user quitting
247 	if (CurrentMessage() && CurrentMessage()->FindBool("shortcut"))
248 		return false;
249 
250 	gStatusWindow->AttemptToQuit();
251 		// try quitting the copy/move/empty trash threads
252 
253 	BVolume bootVolume;
254 	DEBUG_ONLY(status_t err =) BVolumeRoster().GetBootVolume(&bootVolume);
255 	ASSERT(err == B_OK);
256 	BMessage message;
257 	AutoLock<WindowList> lock(&fWindowList);
258 	// save open windows in a message inside an attribute of the desktop
259 	int32 count = fWindowList.CountItems();
260 	for (int32 i = 0; i < count; i++) {
261 		BContainerWindow *window = dynamic_cast<BContainerWindow *>
262 			(fWindowList.ItemAt(i));
263 
264 		if (window && window->TargetModel() && !window->PoseView()->IsDesktopWindow()) {
265 			if (window->TargetModel()->IsRoot())
266 				message.AddBool("open_disks_window", true);
267 			else {
268 				BEntry entry;
269 				BPath path;
270 				const entry_ref *ref = window->TargetModel()->EntryRef();
271 				if (entry.SetTo(ref) == B_OK && entry.GetPath(&path) == B_OK) {
272 					int8 flags = window->IsMinimized() ? kOpenWindowMinimized : kOpenWindowNoFlags;
273 					uint32 deviceFlags = GetVolumeFlags(window->TargetModel());
274 
275 					// save state for every window which is
276 					//	a) already open on another workspace
277 					//	b) on a volume not capable of writing attributes
278 					if (window != FindContainerWindow(ref)
279 						|| (deviceFlags & (B_FS_HAS_ATTR | B_FS_IS_READONLY)) != B_FS_HAS_ATTR) {
280 						BMessage stateMessage;
281 						window->SaveState(stateMessage);
282 						window->SetSaveStateEnabled(false);
283 							// This is to prevent its state to be saved to the node when closed.
284 						message.AddMessage("window state", &stateMessage);
285 						flags |= kOpenWindowHasState;
286 					}
287 					const char *target;
288 					bool pathAlreadyExists = false;
289 					for (int32 index = 0;message.FindString("paths", index, &target) == B_OK;index++) {
290 						if (!strcmp(target,path.Path())) {
291 							pathAlreadyExists = true;
292 							break;
293 						}
294 					}
295 					if (!pathAlreadyExists)
296 						message.AddString("paths", path.Path());
297 					message.AddInt8(path.Path(), flags);
298 				}
299 			}
300 		}
301 	}
302 	lock.Unlock();
303 
304 	// write windows to open on disk
305 	BDirectory deskDir;
306 	if (!BootedInSafeMode() && FSGetDeskDir(&deskDir, bootVolume.Device()) == B_OK) {
307 		// if message is empty, delete the corresponding attribute
308 		if (message.CountNames(B_ANY_TYPE)) {
309 			size_t size = (size_t)message.FlattenedSize();
310 			char *buffer = new char[size];
311 			message.Flatten(buffer, (ssize_t)size);
312 			deskDir.WriteAttr(kAttrOpenWindows, B_MESSAGE_TYPE, 0, buffer, size);
313 			delete [] buffer;
314 		} else
315 			deskDir.RemoveAttr(kAttrOpenWindows);
316 	}
317 
318 	for (int32 count = 0; count == 50; count++) {
319 		// wait 5 seconds for the copiing/moving to quit
320 		if (gStatusWindow->AttemptToQuit())
321 			break;
322 
323 		snooze(100000);
324 	}
325 
326 	return _inherited::QuitRequested();
327 }
328 
329 
330 void
331 TTracker::Quit()
332 {
333 	TrackerSettings().SaveSettings(false);
334 
335 	fAutoMounter->Lock();
336 	fAutoMounter->QuitRequested();	// automounter does some stuff in QuitRequested
337 	fAutoMounter->Quit();			// but we really don't care if it is cooperating or not
338 
339 	fClipboardRefsWatcher->Lock();
340 	fClipboardRefsWatcher->Quit();
341 
342 	fTrashWatcher->Lock();
343 	fTrashWatcher->Quit();
344 
345 	WellKnowEntryList::Quit();
346 
347 	delete gPreloader;
348 	delete fTaskLoop;
349 	delete IconCache::sIconCache;
350 
351 	_inherited::Quit();
352 }
353 
354 
355 void
356 TTracker::MessageReceived(BMessage *message)
357 {
358 	if (HandleScriptingMessage(message))
359 		return;
360 
361 	switch (message->what) {
362 		case kGetInfo:
363 			OpenInfoWindows(message);
364 			break;
365 
366 		case kMoveToTrash:
367 			MoveRefsToTrash(message);
368 			break;
369 
370 		case kCloseWindowAndChildren:
371 			{
372 				const node_ref *itemNode;
373 				int32 bytes;
374 				message->FindData("node_ref", B_RAW_TYPE,
375 					(const void **)&itemNode, &bytes);
376 				CloseWindowAndChildren(itemNode);
377 				break;
378 			}
379 
380 		case kCloseAllWindows:
381 			CloseAllWindows();
382 			break;
383 
384 		case kFindButton:
385 			(new FindWindow())->Show();
386 			break;
387 
388 		case kEditQuery:
389 			EditQueries(message);
390 			break;
391 
392 		case kUnmountVolume:
393 			//	When the user attempts to unmount a volume from the mount
394 			//	context menu, this is where the message gets received.  Save
395 			//	pose locations and forward this to the automounter
396 			SaveAllPoseLocations();
397 			fAutoMounter->PostMessage(message);
398 			break;
399 
400 		case kRunAutomounterSettings:
401 			AutomountSettingsDialog::RunAutomountSettings(fAutoMounter);
402 			break;
403 
404 		case kShowSplash:
405 			{
406 				// The AboutWindow was moved out of the Tracker in preparation
407 				// for when we open source it. The AboutBox contains important
408 				// credit and license issues that shouldn't be modified, and
409 				// therefore shouldn't be open sourced. However, there is a public
410 				// API for 3rd party apps to tell the Tracker to open the AboutBox.
411 				run_be_about();
412 				break;
413 			}
414 
415 		case kAddPrinter:
416 			// show the addprinter window
417 			run_add_printer_panel();
418 			break;
419 
420 		case kMakeActivePrinter:
421 			// get the current selection
422 			SetDefaultPrinter(message);
423 			break;
424 
425 #ifdef MOUNT_MENU_IN_DESKBAR
426 
427 		case 'gmtv':
428 			{
429 				// Someone (probably the deskbar) has requested a list of
430 				// mountable volumes.
431 				BMessage reply;
432 				AutoMounterLoop()->EachMountableItemAndFloppy(&AddMountableItemToMessage,
433 				  &reply);
434 				message->SendReply(&reply);
435 				break;
436 			}
437 
438 #endif
439 
440 		case kMountVolume:
441 		case kMountAllNow:
442 			AutoMounterLoop()->PostMessage(message);
443 			break;
444 
445 
446 		case kRestoreBackgroundImage:
447 			{
448 				BDeskWindow *desktop = GetDeskWindow();
449 				AutoLock<BWindow> lock(desktop);
450 				desktop->UpdateDesktopBackgroundImages();
451 			}
452 			break;
453 
454  		case kShowSettingsWindow:
455  			ShowSettingsWindow();
456  			break;
457 
458 		case kFavoriteCountChangedExternally:
459 			SendNotices(kFavoriteCountChangedExternally, message);
460 			break;
461 
462 		case kStartWatchClipboardRefs:
463 		{
464 			BMessenger messenger;
465 			message->FindMessenger("target", &messenger);
466 			if (messenger.IsValid())
467 				fClipboardRefsWatcher->AddToNotifyList(messenger);
468 			break;
469 		}
470 
471 		case kStopWatchClipboardRefs:
472 		{
473 			BMessenger messenger;
474 			message->FindMessenger("target", &messenger);
475 			if (messenger.IsValid())
476 				fClipboardRefsWatcher->RemoveFromNotifyList(messenger);
477 			break;
478 		}
479 
480 		case kFSClipboardChanges:
481 		{
482 			fClipboardRefsWatcher->UpdatePoseViews(message);
483 			break;
484 		}
485 
486 		default:
487 			_inherited::MessageReceived(message);
488 			break;
489 	}
490 }
491 
492 
493 void
494 TTracker::Pulse()
495 {
496 	if (!TrackerSettings().ShowVolumeSpaceBar())
497 		return;
498 
499 	// update the volume icon's free space bars
500 	BVolumeRoster roster;
501 
502  	BVolume volume;
503 	while (roster.GetNextVolume(&volume) == B_OK) {
504 		BDirectory dir;
505 		volume.GetRootDirectory(&dir);
506 		node_ref nodeRef;
507 		dir.GetNodeRef(&nodeRef);
508 
509 		BMessage notificationMessage;
510 		notificationMessage.AddInt32("device", *(int32 *)&nodeRef.device);
511 
512 		SendNotices(kUpdateVolumeSpaceBar, &notificationMessage);
513 	}
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::ReadyToRun()
1197 {
1198 	gStatusWindow = new BStatusWindow();
1199 	InitMimeTypes();
1200 	InstallDefaultTemplates();
1201 	InstallIndices();
1202 
1203 	HideVarDir();
1204 
1205 	fTrashWatcher = new BTrashWatcher();
1206 	fTrashWatcher->Run();
1207 
1208 	fClipboardRefsWatcher = new BClipboardRefsWatcher();
1209 	fClipboardRefsWatcher->Run();
1210 
1211 	fAutoMounter = new AutoMounter();
1212 	fAutoMounter->Run();
1213 
1214 	fTaskLoop = new StandAloneTaskLoop(true);
1215 
1216 	bool openDisksWindow = false;
1217 
1218 	// open desktop window
1219 	BContainerWindow *deskWindow = NULL;
1220 	BVolume	bootVol;
1221 	BVolumeRoster().GetBootVolume(&bootVol);
1222 	BDirectory deskDir;
1223 	if (FSGetDeskDir(&deskDir, bootVol.Device()) == B_OK) {
1224 		// create desktop
1225 		BEntry entry;
1226 		deskDir.GetEntry(&entry);
1227 		Model *model = new Model(&entry);
1228 		if (model->InitCheck() == B_OK) {
1229 			AutoLock<WindowList> lock(&fWindowList);
1230 			deskWindow = new BDeskWindow(&fWindowList);
1231 			AutoLock<BWindow> windowLock(deskWindow);
1232 			deskWindow->CreatePoseView(model);
1233 			deskWindow->Init();
1234 		} else
1235 			delete model;
1236 
1237 		// open previously open windows
1238 		attr_info attrInfo;
1239 		if (!BootedInSafeMode()
1240 			&& deskDir.GetAttrInfo(kAttrOpenWindows, &attrInfo) == B_OK) {
1241 			char *buffer = (char *)malloc((size_t)attrInfo.size);
1242 			BMessage message;
1243 			if (deskDir.ReadAttr(kAttrOpenWindows, B_MESSAGE_TYPE, 0, buffer, (size_t)attrInfo.size)
1244 				== attrInfo.size
1245 				&& message.Unflatten(buffer) == B_OK) {
1246 
1247 				node_ref nodeRef;
1248 				deskDir.GetNodeRef(&nodeRef);
1249 
1250 				int32 stateMessageCounter = 0;
1251 				const char *path;
1252 				for (int32 outer = 0;message.FindString("paths", outer, &path) == B_OK;outer++) {
1253 					int8 flags = 0;
1254 					for (int32 inner = 0;message.FindInt8(path, inner, &flags) == B_OK;inner++) {
1255 						BEntry entry(path, true);
1256 						if (entry.InitCheck() == B_OK) {
1257 							Model *model = new Model(&entry);
1258 							if (model->InitCheck() == B_OK && model->IsContainer()) {
1259 								BMessage state;
1260 								bool restoreStateFromMessage = false;
1261 								if ((flags & kOpenWindowHasState) != 0
1262 									&& message.FindMessage("window state", stateMessageCounter++, &state) == B_OK)
1263 									restoreStateFromMessage = true;
1264 
1265 								if (restoreStateFromMessage)
1266 									OpenContainerWindow(model, 0, kOpen,
1267 										kRestoreWorkspace | (flags & kOpenWindowMinimized ? kIsHidden : 0U) | kRestoreDecor,
1268 										false, &state);
1269 								else
1270 									OpenContainerWindow(model, 0, kOpen,
1271 										kRestoreWorkspace | (flags & kOpenWindowMinimized ? kIsHidden : 0U) | kRestoreDecor);
1272 							} else
1273 								delete model;
1274 						}
1275 					}
1276 				}
1277 
1278 				if (message.HasBool("open_disks_window"))
1279 					openDisksWindow = true;
1280 			}
1281 			free(buffer);
1282 		}
1283 	}
1284 
1285 	// create model for root of everything
1286 	if (deskWindow) {
1287 		BEntry entry("/");
1288 		Model model(&entry);
1289 		if (model.InitCheck() == B_OK) {
1290 
1291 			if (TrackerSettings().ShowDisksIcon()) {
1292 				// add the root icon to desktop window
1293 				BMessage message;
1294 				message.what = B_NODE_MONITOR;
1295 				message.AddInt32("opcode", B_ENTRY_CREATED);
1296 				message.AddInt32("device", model.NodeRef()->device);
1297 				message.AddInt64("node", model.NodeRef()->node);
1298 				message.AddInt64("directory", model.EntryRef()->directory);
1299 				message.AddString("name", model.EntryRef()->name);
1300 				deskWindow->PostMessage(&message, deskWindow->PoseView());
1301 			}
1302 
1303 			if (openDisksWindow)
1304 				OpenContainerWindow(new Model(model), 0, kOpen, kRestoreWorkspace);
1305 		}
1306 	}
1307 
1308 	// kick off building the mime type list for find panels, etc.
1309 	fMimeTypeList = new MimeTypeList();
1310 
1311 	if (!BootedInSafeMode())
1312 		// kick of transient query killer
1313 		DeleteTransientQueriesTask::StartUpTransientQueryCleaner();
1314 }
1315 
1316 MimeTypeList *
1317 TTracker::MimeTypes() const
1318 {
1319 	return fMimeTypeList;
1320 }
1321 
1322 void
1323 TTracker::SelectChildInParentSoon(const entry_ref *parent,
1324 	const node_ref *child)
1325 {
1326 	fTaskLoop->RunLater(NewMemberFunctionObjectWithResult
1327 		(&TTracker::SelectChildInParent, this, parent, child),
1328 		100000, 200000, 5000000);
1329 }
1330 
1331 void
1332 TTracker::CloseParentWaitingForChildSoon(const entry_ref *child,
1333 	const node_ref *parent)
1334 {
1335 	fTaskLoop->RunLater(NewMemberFunctionObjectWithResult
1336 		(&TTracker::CloseParentWaitingForChild, this, child, parent),
1337 		200000, 100000, 5000000);
1338 }
1339 
1340 void
1341 TTracker::SelectPoseAtLocationSoon(node_ref parent, BPoint pointInPose)
1342 {
1343 	fTaskLoop->RunLater(NewMemberFunctionObject
1344 		(&TTracker::SelectPoseAtLocationInParent, this, parent, pointInPose),
1345 		100000);
1346 }
1347 
1348 void
1349 TTracker::SelectPoseAtLocationInParent(node_ref parent, BPoint pointInPose)
1350 {
1351 	AutoLock<WindowList> lock(&fWindowList);
1352 	BContainerWindow *parentWindow = FindContainerWindow(&parent);
1353 	if (parentWindow) {
1354 		AutoLock<BWindow> lock(parentWindow);
1355 		parentWindow->PoseView()->SelectPoseAtLocation(pointInPose);
1356 	}
1357 }
1358 
1359 bool
1360 TTracker::CloseParentWaitingForChild(const entry_ref *child,
1361 	const node_ref *parent)
1362 {
1363 	AutoLock<WindowList> lock(&fWindowList);
1364 
1365 	BContainerWindow *parentWindow = FindContainerWindow(parent);
1366 	if (!parentWindow)
1367 		// parent window already closed, give up
1368 		return true;
1369 
1370 	// If child is a symbolic link, dereference it, so that
1371 	// FindContainerWindow will succeed.
1372 	BEntry entry(child, true);
1373 	entry_ref resolvedChild;
1374 	if (entry.GetRef(&resolvedChild) != B_OK)
1375 		resolvedChild = *child;
1376 
1377 	BContainerWindow *window = FindContainerWindow(&resolvedChild);
1378 	if (window) {
1379 		AutoLock<BWindow> lock(window);
1380 		if (!window->IsHidden())
1381 			return CloseParentWindowCommon(parentWindow);
1382 	}
1383 	return false;
1384 }
1385 
1386 void
1387 TTracker::CloseParent(node_ref parent)
1388 {
1389 	AutoLock<WindowList> lock(&fWindowList);
1390 	if (!lock)
1391 		return;
1392 
1393 	CloseParentWindowCommon(FindContainerWindow(&parent));
1394 }
1395 
1396 void
1397 TTracker::ShowSettingsWindow()
1398 {
1399 	if (!fSettingsWindow) {
1400 		fSettingsWindow = new TrackerSettingsWindow();
1401 		fSettingsWindow->Show();
1402 	} else {
1403 		if (fSettingsWindow->Lock()) {
1404 			if (fSettingsWindow->IsHidden())
1405 				fSettingsWindow->Show();
1406 			else
1407 				fSettingsWindow->Activate();
1408 			fSettingsWindow->Unlock();
1409 		}
1410 	}
1411 }
1412 
1413 bool
1414 TTracker::CloseParentWindowCommon(BContainerWindow *window)
1415 {
1416 	ASSERT(fWindowList.IsLocked());
1417 
1418 	if (dynamic_cast<BDeskWindow *>(window))
1419 		// don't close the destop
1420 		return false;
1421 
1422 	window->PostMessage(B_QUIT_REQUESTED);
1423 	return true;
1424 }
1425 
1426 bool
1427 TTracker::SelectChildInParent(const entry_ref *parent, const node_ref *child)
1428 {
1429 	AutoLock<WindowList> lock(&fWindowList);
1430 
1431 	BContainerWindow *window = FindContainerWindow(parent);
1432 	if (!window)
1433 		// parent window already closed, give up
1434 		return false;
1435 
1436 	AutoLock<BWindow> windowLock(window);
1437 
1438 	if (windowLock.IsLocked()) {
1439 		BPoseView *view = window->PoseView();
1440 		int32 index;
1441 		BPose *pose = view->FindPose(child, &index);
1442 		if (pose) {
1443 			view->SelectPose(pose, index);
1444 			return true;
1445 		}
1446 	}
1447 	return false;
1448 }
1449 
1450 const int32 kNodeMonitorBumpValue = 512;
1451 
1452 status_t
1453 TTracker::NeedMoreNodeMonitors()
1454 {
1455 	fNodeMonitorCount += kNodeMonitorBumpValue;
1456 	PRINT(("bumping nodeMonitorCount to %d\n", fNodeMonitorCount));
1457 
1458 	return _kset_mon_limit_(fNodeMonitorCount);
1459 }
1460 
1461 status_t
1462 TTracker::WatchNode(const node_ref *node, uint32 flags,
1463 	BMessenger target)
1464 {
1465 	status_t result = watch_node(node, flags, target);
1466 	if (result == B_OK || result != B_NO_MEMORY) {
1467 		// need to make sure this uses the same error value as
1468 		// the node monitor code
1469 		return result;
1470 	}
1471 
1472 	PRINT(("failed to start monitoring, trying to allocate more "
1473 		"node monitors\n"));
1474 
1475 	TTracker *tracker = dynamic_cast<TTracker *>(be_app);
1476 	if (!tracker) {
1477 		// we are the file panel only, just fail
1478 		return result;
1479 	}
1480 
1481 	result = tracker->NeedMoreNodeMonitors();
1482 
1483 	if (result != B_OK) {
1484 		PRINT(("failed to allocate more node monitors, %s\n",
1485 			strerror(result)));
1486 		return result;
1487 	}
1488 
1489 	// try again, this time with more node monitors
1490 	return watch_node(node, flags, target);
1491 }
1492 
1493 
1494 AutoMounter *
1495 TTracker::AutoMounterLoop()
1496 {
1497 	return fAutoMounter;
1498 }
1499 
1500 
1501 bool
1502 TTracker::InTrashNode(const entry_ref *node) const
1503 {
1504 	return FSInTrashDir(node);
1505 }
1506 
1507 
1508 bool
1509 TTracker::TrashFull() const
1510 {
1511 	return fTrashWatcher->CheckTrashDirs();
1512 }
1513 
1514 
1515 bool
1516 TTracker::IsTrashNode(const node_ref *node) const
1517 {
1518 	return fTrashWatcher->IsTrashNode(node);
1519 }
1520 
1521