xref: /haiku/src/kits/tracker/Tracker.cpp (revision 80d75f15dfa48ebea421c6b2c19a5296cc63d7eb)
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 		case kShowVolumeSpaceBar:
487 		case kSpaceBarColorChanged: {
488 			gPeriodicUpdatePoses.DoPeriodicUpdate(true);
489 			break;
490 		}
491 
492 		default:
493 			_inherited::MessageReceived(message);
494 			break;
495 	}
496 }
497 
498 
499 void
500 TTracker::Pulse()
501 {
502 	if (!TrackerSettings().ShowVolumeSpaceBar())
503 		return;
504 
505 	// update the volume icon's free space bars
506 	gPeriodicUpdatePoses.DoPeriodicUpdate(false);
507 }
508 
509 
510 void
511 TTracker::SetDefaultPrinter(const BMessage *message)
512 {
513 	//	get the first item selected
514 	int32 count = 0;
515 	uint32 type = 0;
516 	message->GetInfo("refs", &type, &count);
517 
518 	if (count <= 0)
519 		return;
520 
521 	//	will make the first item the default printer, disregards any other files
522 	entry_ref ref;
523 	ASSERT(message->FindRef("refs", 0, &ref) == B_OK);
524 	if (message->FindRef("refs", 0, &ref) != B_OK)
525 		return;
526 
527 #if B_BEOS_VERSION_DANO
528 	set_default_printer(ref.name);
529 #else
530 	// 	create a message for the print server
531 	BMessenger messenger("application/x-vnd.Be-PSRV", -1);
532 	if (!messenger.IsValid())
533 		return;
534 
535 	//	send the selection to the print server
536 	BMessage makeActiveMessage(PSV_MAKE_PRINTER_ACTIVE_QUIETLY);
537 	makeActiveMessage.AddString("printer", ref.name);
538 
539 	BMessage reply;
540 	messenger.SendMessage(&makeActiveMessage, &reply);
541 #endif
542 }
543 
544 
545 void
546 TTracker::MoveRefsToTrash(const BMessage *message)
547 {
548 	int32 count;
549 	uint32 type;
550 	message->GetInfo("refs", &type, &count);
551 
552 	if (count <= 0)
553 		return;
554 
555 	BObjectList<entry_ref> *srcList = new BObjectList<entry_ref>(count, true);
556 
557 	for (int32 index = 0; index < count; index++) {
558 
559 		entry_ref ref;
560 		ASSERT(message->FindRef("refs", index, &ref) == B_OK);
561 		if (message->FindRef("refs", index, &ref) != B_OK)
562 			continue;
563 
564 		AutoLock<WindowList> lock(&fWindowList);
565 		BContainerWindow *window = FindParentContainerWindow(&ref);
566 		if (window)
567 			// if we have a window open for this entry, ask the pose to
568 			// delete it, this will select the next entry
569 			window->PoseView()->MoveEntryToTrash(&ref);
570 		else
571 			// add all others to a list that gets deleted separately
572 			srcList->AddItem(new entry_ref(ref));
573 	}
574 
575 	if (srcList->CountItems())
576 		// async move to trash
577 		FSMoveToTrash(srcList);
578 }
579 
580 
581 template <class T, class FT>
582 class EntryAndNodeDoSoonWithMessageFunctor : public FunctionObjectWithResult<bool> {
583 public:
584 	EntryAndNodeDoSoonWithMessageFunctor(FT func, T *target, const entry_ref *child,
585 		const node_ref *parent, const BMessage *message)
586 		:	fFunc(func),
587 			fTarget(target),
588 			fNode(*parent),
589 			fEntry(*child)
590 		{
591 			fSendMessage = (message != NULL);
592 			if (message)
593 				fMessage = *message;
594 		}
595 
596 	virtual ~EntryAndNodeDoSoonWithMessageFunctor() {}
597 	virtual void operator()()
598 		{ result = (fTarget->*fFunc)(&fEntry, &fNode, fSendMessage ? &fMessage : NULL); }
599 
600 protected:
601 	FT fFunc;
602 	T *fTarget;
603 	node_ref fNode;
604 	entry_ref fEntry;
605 	BMessage fMessage;
606 	bool fSendMessage;
607 };
608 
609 
610 bool
611 TTracker::LaunchAndCloseParentIfOK(const entry_ref *launchThis,
612 	const node_ref *closeThis, const BMessage *messageToBundle)
613 {
614 	BMessage refsReceived(B_REFS_RECEIVED);
615 	if (messageToBundle) {
616 		refsReceived = *messageToBundle;
617 		refsReceived.what = B_REFS_RECEIVED;
618 	}
619 	refsReceived.AddRef("refs", launchThis);
620 	// synchronous launch, we are already in our own thread
621 	if (TrackerLaunch(&refsReceived, false) == B_OK) {
622 		// if launched fine, close parent window in a bit
623 		fTaskLoop->RunLater(NewMemberFunctionObject(&TTracker::CloseParent, this, *closeThis),
624 			1000000);
625 	}
626 	return false;
627 }
628 
629 
630 status_t
631 TTracker::OpenRef(const entry_ref *ref, const node_ref *nodeToClose,
632 	const node_ref *nodeToSelect, OpenSelector selector,
633 	const BMessage *messageToBundle)
634 {
635 	Model *model = NULL;
636 	BEntry entry(ref, true);
637 	status_t result = entry.InitCheck();
638 
639 	bool brokenLinkWithSpecificHandler = false;
640 	BString brokenLinkPreferredApp;
641 
642 	if (result != B_OK) {
643 		model = new Model(ref, false);
644 		if (model->IsSymLink() && !model->LinkTo()) {
645 			model->GetPreferredAppForBrokenSymLink(brokenLinkPreferredApp);
646 			if (brokenLinkPreferredApp.Length() && brokenLinkPreferredApp != kTrackerSignature)
647 				brokenLinkWithSpecificHandler = true;
648 		}
649 
650 		if (!brokenLinkWithSpecificHandler) {
651 			delete model;
652 			(new BAlert("", "There was an error resolving the link.",
653 				"Cancel", 0, 0,
654 				B_WIDTH_AS_USUAL, B_WARNING_ALERT))->Go();
655 			return result;
656 		}
657 	} else
658 		model = new Model(&entry);
659 
660 	result = model->InitCheck();
661 	if (result != B_OK) {
662 		delete model;
663 		return result;
664 	}
665 
666 	bool openAsContainer = model->IsContainer();
667 
668 	if (openAsContainer && selector != kOpenWith) {
669 		// if folder or query has a preferred handler and it's not the
670 		// Tracker, open it by sending refs to the handling app
671 
672 		// if we are responding to the final open of OpenWith, just
673 		// skip this and proceed to opening the container with Tracker
674 		model->OpenNode();
675 		BNodeInfo nodeInfo(model->Node());
676 		char preferredApp[B_MIME_TYPE_LENGTH];
677 		if (nodeInfo.GetPreferredApp(preferredApp) == B_OK
678 			&& strcasecmp(preferredApp, kTrackerSignature) != 0)
679 			openAsContainer = false;
680 		model->CloseNode();
681 	}
682 
683 	if (openAsContainer || selector == kRunOpenWithWindow) {
684 		// special case opening plain folders, queries or using open with
685 		OpenContainerWindow(model, 0, selector, kRestoreDecor);	// window adopts model
686 		if (nodeToClose)
687 			CloseParentWaitingForChildSoon(ref, nodeToClose);
688 	} else if (model->IsQueryTemplate()) {
689 		// query template - open new find window
690 		(new FindWindow(model->EntryRef()))->Show();
691 		if (nodeToClose)
692 			CloseParentWaitingForChildSoon(ref, nodeToClose);
693 	} else {
694 		delete model;
695 		// run Launch in a separate thread
696 		// and close parent if successfull
697 		if (nodeToClose)
698 			Thread::Launch(new EntryAndNodeDoSoonWithMessageFunctor<TTracker,
699 				bool (TTracker::*)(const entry_ref *, const node_ref *,
700 				const BMessage *)>(&TTracker::LaunchAndCloseParentIfOK, this,
701 				ref, nodeToClose, messageToBundle));
702 		else {
703 			BMessage refsReceived(B_REFS_RECEIVED);
704 			if (messageToBundle) {
705 				refsReceived = *messageToBundle;
706 				refsReceived.what = B_REFS_RECEIVED;
707 			}
708 			refsReceived.AddRef("refs", ref);
709 			if (brokenLinkWithSpecificHandler)
710 				// This cruft is to support a hacky workaround for double-clicking
711 				// broken refs for cifs; should get fixed in R5
712 				LaunchBrokenLink(brokenLinkPreferredApp.String(), &refsReceived);
713 			else
714 				TrackerLaunch(&refsReceived, true);
715 		}
716 	}
717 	if (nodeToSelect)
718 		SelectChildInParentSoon(ref, nodeToSelect);
719 
720 	return B_OK;
721 }
722 
723 
724 void
725 TTracker::RefsReceived(BMessage *message)
726 {
727 	OpenSelector selector = kOpen;
728 	if (message->HasInt32("launchUsingSelector"))
729 		selector = kRunOpenWithWindow;
730 
731 	entry_ref handlingApp;
732 	if (message->FindRef("handler", &handlingApp) == B_OK)
733 		selector = kOpenWith;
734 
735 	int32 count;
736 	uint32 type;
737 	message->GetInfo("refs", &type, &count);
738 
739 	switch (selector) {
740 		case kRunOpenWithWindow:
741 			OpenContainerWindow(0, message, selector);
742 				// window adopts model
743 			break;
744 
745 		case kOpenWith:
746 			{
747 				// Open With resulted in passing refs and a handler, open the files
748 				// with the handling app
749 				message->RemoveName("handler");
750 
751 				// have to find out if handling app is the Tracker
752 				// if it is, just pass it to the active Tracker, no matter which Tracker
753 				// was chosen to handle the refs
754 				char signature[B_MIME_TYPE_LENGTH];
755 				signature[0] = '\0';
756 				{
757 					BFile handlingNode(&handlingApp, O_RDONLY);
758 					BAppFileInfo appInfo(&handlingNode);
759 					appInfo.GetSignature(signature);
760 				}
761 
762 				if (strcasecmp(signature, kTrackerSignature) != 0) {
763 					// handling app not Tracker, pass entries to the apps RefsReceived
764 					TrackerLaunch(&handlingApp, message, true);
765 					break;
766 				}
767 				// fall thru, opening refs by the Tracker, as if they were double clicked
768 			}
769 
770 		case kOpen:
771 			{
772 				// copy over "Poses" messenger so that refs received recipients know
773 				// where the open came from
774 				BMessage *bundleThis = NULL;
775 				BMessenger messenger;
776 				if (message->FindMessenger("TrackerViewToken", &messenger) == B_OK) {
777 					bundleThis = new BMessage();
778 					bundleThis->AddMessenger("TrackerViewToken", messenger);
779 				}
780 
781 				for (int32 index = 0; index < count; index++) {
782 					entry_ref ref;
783 					message->FindRef("refs", index, &ref);
784 
785 					const node_ref *nodeToClose = NULL;
786 					const node_ref *nodeToSelect = NULL;
787 					ssize_t numBytes;
788 
789 					message->FindData("nodeRefsToClose", B_RAW_TYPE, index,
790 						(const void **)&nodeToClose, &numBytes);
791 					message->FindData("nodeRefToSelect", B_RAW_TYPE, index,
792 						(const void **)&nodeToSelect, &numBytes);
793 
794 					OpenRef(&ref, nodeToClose, nodeToSelect, selector, bundleThis);
795 				}
796 
797 				delete bundleThis;
798 				break;
799 			}
800 	}
801 }
802 
803 
804 void
805 TTracker::ArgvReceived(int32 argc, char **argv)
806 {
807 	BMessage *message = CurrentMessage();
808 	const char *currentWorkingDirectoryPath = NULL;
809 	entry_ref ref;
810 
811 	if (message->FindString("cwd", &currentWorkingDirectoryPath) == B_OK) {
812 		BDirectory workingDirectory(currentWorkingDirectoryPath);
813 		for (int32 index = 1; index < argc; index++) {
814 			BEntry entry;
815 			if (entry.SetTo(&workingDirectory, argv[index]) == B_OK
816 				&& entry.GetRef(&ref) == B_OK)
817 				OpenRef(&ref);
818 			else if (get_ref_for_path(argv[index], &ref) == B_OK)
819 				OpenRef(&ref);
820 		}
821 	}
822 }
823 
824 
825 void
826 TTracker::OpenContainerWindow(Model *model, BMessage *originalRefsList,
827 	OpenSelector openSelector, uint32 openFlags, bool checkAlreadyOpen,
828 	const BMessage *stateMessage)
829 {
830 	AutoLock<WindowList> lock(&fWindowList);
831 	BContainerWindow *window = NULL;
832 	if (checkAlreadyOpen && openSelector != kRunOpenWithWindow)
833 		// find out if window already open
834 		window = FindContainerWindow(model->NodeRef());
835 
836 	bool someWindowActivated = false;
837 
838 	uint32 workspace = (uint32)(1 << current_workspace());
839 	int32 windowCount = 0;
840 
841 	while (window) {
842 		// At least one window open, just pull to front
843 		// make sure we don't jerk workspaces around
844 		uint32 windowWorkspaces = window->Workspaces();
845 		if (windowWorkspaces & workspace) {
846 			window->Activate();
847 			someWindowActivated = true;
848 		}
849 		window = FindContainerWindow(model->NodeRef(), ++windowCount);
850 	}
851 
852 	if (someWindowActivated) {
853 		delete model;
854 		return;
855 	} // If no window was actiated, (none in the current workspace
856 	  // we open a new one.
857 
858 	if (openSelector == kRunOpenWithWindow) {
859 		BMessage *refList = NULL;
860 		if (!originalRefsList) {
861 			// when passing just a single model, stuff it's entry in a single
862 			// element list anyway
863 			ASSERT(model);
864 			refList = new BMessage;
865 			refList->AddRef("refs", model->EntryRef());
866 			delete model;
867 			model = NULL;
868 		} else
869 			// clone the message, window adopts it for it's own use
870 			refList = new BMessage(*originalRefsList);
871 		window = new OpenWithContainerWindow(refList, &fWindowList);
872 	} else if (model->IsRoot()) {
873 		// window will adopt the model
874 		window = new BVolumeWindow(&fWindowList, openFlags);
875 	} else if (model->IsQuery()) {
876 		// window will adopt the model
877 		window = new BQueryContainerWindow(&fWindowList, openFlags);
878 	} else
879 		// window will adopt the model
880 		window = new BContainerWindow(&fWindowList, openFlags);
881 
882 	if (model)
883 		window->CreatePoseView(model);
884 
885 	BMessage restoreStateMessage(kRestoreState);
886 
887 	if (stateMessage)
888 		restoreStateMessage.AddMessage("state", stateMessage);
889 
890 	window->PostMessage(&restoreStateMessage);
891 }
892 
893 
894 void
895 TTracker::EditQueries(const BMessage *message)
896 {
897 	bool editOnlyIfTemplate;
898 	if (message->FindBool("editQueryOnPose", &editOnlyIfTemplate) != B_OK)
899 		editOnlyIfTemplate = false;
900 
901 	type_code type;
902 	int32 count;
903 	message->GetInfo("refs", &type, &count);
904 	for (int32 index = 0; index < count; index++) {
905 		entry_ref ref;
906 		message->FindRef("refs", index, &ref);
907 		BEntry entry(&ref, true);
908 		if (entry.InitCheck() == B_OK && entry.Exists())
909 			(new FindWindow(&ref, editOnlyIfTemplate))->Show();
910 	}
911 }
912 
913 
914 void
915 TTracker::OpenInfoWindows(BMessage *message)
916 {
917 	type_code type;
918 	int32 count;
919 	message->GetInfo("refs", &type, &count);
920 
921 	for (int32 index = 0; index < count; index++) {
922 		entry_ref ref;
923 		message->FindRef("refs", index, &ref);
924 		BEntry entry;
925 		if (entry.SetTo(&ref) == B_OK) {
926 			Model *model = new Model(&entry);
927 			if (model->InitCheck() != B_OK) {
928 				delete model;
929 				continue;
930 			}
931 
932 			AutoLock<WindowList> lock(&fWindowList);
933 			BInfoWindow *wind = FindInfoWindow(model->NodeRef());
934 
935 			if (wind) {
936 				wind->Activate();
937 				delete model;
938 			} else {
939 				wind = new BInfoWindow(model, index, &fWindowList);
940 				wind->PostMessage(kRestoreState);
941 			}
942 		}
943 	}
944 }
945 
946 
947 BDeskWindow *
948 TTracker::GetDeskWindow() const
949 {
950 	int32 count = fWindowList.CountItems();
951 	for (int32 index = 0; index < count; index++) {
952 		BDeskWindow *window = dynamic_cast<BDeskWindow *>
953 			(fWindowList.ItemAt(index));
954 
955 		if (window)
956 			return window;
957 	}
958 	TRESPASS();
959 	return NULL;
960 }
961 
962 
963 BContainerWindow *
964 TTracker::FindContainerWindow(const node_ref *node, int32 number) const
965 {
966 	ASSERT(fWindowList.IsLocked());
967 
968 	int32 count = fWindowList.CountItems();
969 
970 	int32 windowsFound = 0;
971 
972 	for (int32 index = 0; index < count; index++) {
973 		BContainerWindow *window = dynamic_cast<BContainerWindow *>
974 			(fWindowList.ItemAt(index));
975 
976 		if (window && window->IsShowing(node) && number == windowsFound++)
977 			return window;
978 	}
979 	return NULL;
980 }
981 
982 
983 BContainerWindow *
984 TTracker::FindContainerWindow(const entry_ref *entry, int32 number) const
985 {
986 	ASSERT(fWindowList.IsLocked());
987 
988 	int32 count = fWindowList.CountItems();
989 
990 	int32 windowsFound = 0;
991 
992 	for (int32 index = 0; index < count; index++) {
993 		BContainerWindow *window = dynamic_cast<BContainerWindow *>
994 			(fWindowList.ItemAt(index));
995 
996 		if (window && window->IsShowing(entry) && number == windowsFound++)
997 			return window;
998 	}
999 	return NULL;
1000 }
1001 
1002 
1003 bool
1004 TTracker::EntryHasWindowOpen(const entry_ref *entry)
1005 {
1006 	AutoLock<WindowList> lock(&fWindowList);
1007 	return FindContainerWindow(entry) != NULL;
1008 }
1009 
1010 
1011 BContainerWindow *
1012 TTracker::FindParentContainerWindow(const entry_ref *ref) const
1013 {
1014 	BEntry entry(ref);
1015 	BEntry parent;
1016 
1017 	if (entry.GetParent(&parent) != B_OK)
1018 		return NULL;
1019 
1020 	entry_ref parentRef;
1021 	parent.GetRef(&parentRef);
1022 
1023 	ASSERT(fWindowList.IsLocked());
1024 
1025 	int32 count = fWindowList.CountItems();
1026 	for (int32 index = 0; index < count; index++) {
1027 		BContainerWindow *window = dynamic_cast<BContainerWindow *>
1028 			(fWindowList.ItemAt(index));
1029 		if (window && window->IsShowing(&parentRef))
1030 			return window;
1031 	}
1032 	return NULL;
1033 }
1034 
1035 
1036 BInfoWindow *
1037 TTracker::FindInfoWindow(const node_ref* node) const
1038 {
1039 	ASSERT(fWindowList.IsLocked());
1040 
1041 	int32 count = fWindowList.CountItems();
1042 	for (int32 index = 0; index < count; index++) {
1043 		BInfoWindow *window = dynamic_cast<BInfoWindow *>
1044 			(fWindowList.ItemAt(index));
1045 		if (window && window->IsShowing(node))
1046 			return window;
1047 	}
1048 	return NULL;
1049 }
1050 
1051 
1052 bool
1053 TTracker::QueryActiveForDevice(dev_t device)
1054 {
1055 	AutoLock<WindowList> lock(&fWindowList);
1056 	int32 count = fWindowList.CountItems();
1057 	for (int32 index = 0; index < count; index++) {
1058 		BQueryContainerWindow *window = dynamic_cast<BQueryContainerWindow *>
1059 			(fWindowList.ItemAt(index));
1060 		if (window) {
1061 			AutoLock<BWindow> lock(window);
1062 			if (window->ActiveOnDevice(device))
1063 				return true;
1064 		}
1065 	}
1066 	return false;
1067 }
1068 
1069 
1070 void
1071 TTracker::CloseActiveQueryWindows(dev_t device)
1072 {
1073 	// used when trying to unmount a volume - an active query would prevent that from
1074 	// happening
1075 	bool closed = false;
1076 	AutoLock<WindowList> lock(fWindowList);
1077 	for (int32 index = fWindowList.CountItems(); index >= 0; index--) {
1078 		BQueryContainerWindow *window = dynamic_cast<BQueryContainerWindow *>
1079 			(fWindowList.ItemAt(index));
1080 		if (window) {
1081 			AutoLock<BWindow> lock(window);
1082 			if (window->ActiveOnDevice(device)) {
1083 				window->PostMessage(B_QUIT_REQUESTED);
1084 				closed = true;
1085 			}
1086 		}
1087 	}
1088 	lock.Unlock();
1089 	if (closed)
1090 		for (int32 timeout = 30; timeout; timeout--) {
1091 			// wait a bit for windows to fully close
1092 			if (!QueryActiveForDevice(device))
1093 				return;
1094 			snooze(100000);
1095 		}
1096 }
1097 
1098 
1099 void
1100 TTracker::SaveAllPoseLocations()
1101 {
1102 	int32 numWindows = fWindowList.CountItems();
1103 	for (int32 windowIndex = 0; windowIndex < numWindows; windowIndex++) {
1104 		BContainerWindow *window = dynamic_cast<BContainerWindow *>
1105 			(fWindowList.ItemAt(windowIndex));
1106 
1107 		if (window) {
1108 			AutoLock<BWindow> lock(window);
1109 			BDeskWindow *deskWindow = dynamic_cast<BDeskWindow *>(window);
1110 
1111 			if (deskWindow)
1112 				deskWindow->SaveDesktopPoseLocations();
1113 			else
1114 				window->PoseView()->SavePoseLocations();
1115 		}
1116 	}
1117 }
1118 
1119 
1120 void
1121 TTracker::CloseWindowAndChildren(const node_ref *node)
1122 {
1123 	BDirectory dir(node);
1124 	if (dir.InitCheck() != B_OK)
1125 		return;
1126 
1127 	AutoLock<WindowList> lock(&fWindowList);
1128 	BObjectList<BContainerWindow> closeList;
1129 
1130 	// make a list of all windows to be closed
1131 	// count from end to beginning so we can remove items safely
1132 	for (int32 index = fWindowList.CountItems() - 1; index >= 0; index--) {
1133 		BContainerWindow *window = dynamic_cast<BContainerWindow *>
1134 			(fWindowList.ItemAt(index));
1135 		if (window && window->TargetModel()) {
1136 			BEntry wind_entry;
1137 			wind_entry.SetTo(window->TargetModel()->EntryRef());
1138 
1139 			if ((*window->TargetModel()->NodeRef() == *node)
1140 				|| dir.Contains(&wind_entry)) {
1141 
1142 				// ToDo:
1143 				// get rid of the Remove here, BContainerWindow::Quit does it
1144 				fWindowList.RemoveItemAt(index);
1145 				closeList.AddItem(window);
1146 			}
1147 		}
1148 	}
1149 
1150 	// now really close the windows
1151 	int32 numItems = closeList.CountItems();
1152 	for (int32 index = 0; index < numItems; index++) {
1153 		BContainerWindow *window = closeList.ItemAt(index);
1154 		window->PostMessage(B_QUIT_REQUESTED);
1155 	}
1156 }
1157 
1158 
1159 void
1160 TTracker::CloseAllWindows()
1161 {
1162 	// this is a response to the DeskBar sending us a B_QUIT, when it really
1163 	// means to say close all your windows. It might be better to have it
1164 	// send a kCloseAllWindows message and have windowless apps stay running,
1165 	// which is what we will do for the Tracker
1166 	AutoLock<WindowList> lock(&fWindowList);
1167 
1168 	int32 count = CountWindows();
1169 	for (int32 index = 0; index < count; index++) {
1170 		BWindow *window = WindowAt(index);
1171 		// avoid the desktop
1172 		if (!dynamic_cast<BDeskWindow *>(window)
1173 			&& !dynamic_cast<BStatusWindow *>(window))
1174 			window->PostMessage(B_QUIT_REQUESTED);
1175 	}
1176 	// count from end to beginning so we can remove items safely
1177 	for (int32 index = fWindowList.CountItems() - 1; index >= 0; index--) {
1178 		BWindow *window = fWindowList.ItemAt(index);
1179 		if (!dynamic_cast<BDeskWindow *>(window)
1180 			&& !dynamic_cast<BStatusWindow *>(window))
1181 				// ToDo:
1182 				// get rid of the Remove here, BContainerWindow::Quit does it
1183 			fWindowList.RemoveItemAt(index);
1184 	}
1185 }
1186 
1187 
1188 void
1189 TTracker::ReadyToRun()
1190 {
1191 	gStatusWindow = new BStatusWindow();
1192 	InitMimeTypes();
1193 	InstallDefaultTemplates();
1194 	InstallIndices();
1195 
1196 	HideVarDir();
1197 
1198 	fTrashWatcher = new BTrashWatcher();
1199 	fTrashWatcher->Run();
1200 
1201 	fClipboardRefsWatcher = new BClipboardRefsWatcher();
1202 	fClipboardRefsWatcher->Run();
1203 
1204 	fAutoMounter = new AutoMounter();
1205 	fAutoMounter->Run();
1206 
1207 	fTaskLoop = new StandAloneTaskLoop(true);
1208 
1209 	bool openDisksWindow = false;
1210 
1211 	// open desktop window
1212 	BContainerWindow *deskWindow = NULL;
1213 	BVolume	bootVol;
1214 	BVolumeRoster().GetBootVolume(&bootVol);
1215 	BDirectory deskDir;
1216 	if (FSGetDeskDir(&deskDir, bootVol.Device()) == B_OK) {
1217 		// create desktop
1218 		BEntry entry;
1219 		deskDir.GetEntry(&entry);
1220 		Model *model = new Model(&entry);
1221 		if (model->InitCheck() == B_OK) {
1222 			AutoLock<WindowList> lock(&fWindowList);
1223 			deskWindow = new BDeskWindow(&fWindowList);
1224 			AutoLock<BWindow> windowLock(deskWindow);
1225 			deskWindow->CreatePoseView(model);
1226 			deskWindow->Init();
1227 		} else
1228 			delete model;
1229 
1230 		// open previously open windows
1231 		attr_info attrInfo;
1232 		if (!BootedInSafeMode()
1233 			&& deskDir.GetAttrInfo(kAttrOpenWindows, &attrInfo) == B_OK) {
1234 			char *buffer = (char *)malloc((size_t)attrInfo.size);
1235 			BMessage message;
1236 			if (deskDir.ReadAttr(kAttrOpenWindows, B_MESSAGE_TYPE, 0, buffer, (size_t)attrInfo.size)
1237 				== attrInfo.size
1238 				&& message.Unflatten(buffer) == B_OK) {
1239 
1240 				node_ref nodeRef;
1241 				deskDir.GetNodeRef(&nodeRef);
1242 
1243 				int32 stateMessageCounter = 0;
1244 				const char *path;
1245 				for (int32 outer = 0;message.FindString("paths", outer, &path) == B_OK;outer++) {
1246 					int8 flags = 0;
1247 					for (int32 inner = 0;message.FindInt8(path, inner, &flags) == B_OK;inner++) {
1248 						BEntry entry(path, true);
1249 						if (entry.InitCheck() == B_OK) {
1250 							Model *model = new Model(&entry);
1251 							if (model->InitCheck() == B_OK && model->IsContainer()) {
1252 								BMessage state;
1253 								bool restoreStateFromMessage = false;
1254 								if ((flags & kOpenWindowHasState) != 0
1255 									&& message.FindMessage("window state", stateMessageCounter++, &state) == B_OK)
1256 									restoreStateFromMessage = true;
1257 
1258 								if (restoreStateFromMessage)
1259 									OpenContainerWindow(model, 0, kOpen,
1260 										kRestoreWorkspace | (flags & kOpenWindowMinimized ? kIsHidden : 0U) | kRestoreDecor,
1261 										false, &state);
1262 								else
1263 									OpenContainerWindow(model, 0, kOpen,
1264 										kRestoreWorkspace | (flags & kOpenWindowMinimized ? kIsHidden : 0U) | kRestoreDecor);
1265 							} else
1266 								delete model;
1267 						}
1268 					}
1269 				}
1270 
1271 				if (message.HasBool("open_disks_window"))
1272 					openDisksWindow = true;
1273 			}
1274 			free(buffer);
1275 		}
1276 	}
1277 
1278 	// create model for root of everything
1279 	if (deskWindow) {
1280 		BEntry entry("/");
1281 		Model model(&entry);
1282 		if (model.InitCheck() == B_OK) {
1283 
1284 			if (TrackerSettings().ShowDisksIcon()) {
1285 				// add the root icon to desktop window
1286 				BMessage message;
1287 				message.what = B_NODE_MONITOR;
1288 				message.AddInt32("opcode", B_ENTRY_CREATED);
1289 				message.AddInt32("device", model.NodeRef()->device);
1290 				message.AddInt64("node", model.NodeRef()->node);
1291 				message.AddInt64("directory", model.EntryRef()->directory);
1292 				message.AddString("name", model.EntryRef()->name);
1293 				deskWindow->PostMessage(&message, deskWindow->PoseView());
1294 			}
1295 
1296 			if (openDisksWindow)
1297 				OpenContainerWindow(new Model(model), 0, kOpen, kRestoreWorkspace);
1298 		}
1299 	}
1300 
1301 	// kick off building the mime type list for find panels, etc.
1302 	fMimeTypeList = new MimeTypeList();
1303 
1304 	if (!BootedInSafeMode())
1305 		// kick of transient query killer
1306 		DeleteTransientQueriesTask::StartUpTransientQueryCleaner();
1307 }
1308 
1309 MimeTypeList *
1310 TTracker::MimeTypes() const
1311 {
1312 	return fMimeTypeList;
1313 }
1314 
1315 void
1316 TTracker::SelectChildInParentSoon(const entry_ref *parent,
1317 	const node_ref *child)
1318 {
1319 	fTaskLoop->RunLater(NewMemberFunctionObjectWithResult
1320 		(&TTracker::SelectChildInParent, this, parent, child),
1321 		100000, 200000, 5000000);
1322 }
1323 
1324 void
1325 TTracker::CloseParentWaitingForChildSoon(const entry_ref *child,
1326 	const node_ref *parent)
1327 {
1328 	fTaskLoop->RunLater(NewMemberFunctionObjectWithResult
1329 		(&TTracker::CloseParentWaitingForChild, this, child, parent),
1330 		200000, 100000, 5000000);
1331 }
1332 
1333 void
1334 TTracker::SelectPoseAtLocationSoon(node_ref parent, BPoint pointInPose)
1335 {
1336 	fTaskLoop->RunLater(NewMemberFunctionObject
1337 		(&TTracker::SelectPoseAtLocationInParent, this, parent, pointInPose),
1338 		100000);
1339 }
1340 
1341 void
1342 TTracker::SelectPoseAtLocationInParent(node_ref parent, BPoint pointInPose)
1343 {
1344 	AutoLock<WindowList> lock(&fWindowList);
1345 	BContainerWindow *parentWindow = FindContainerWindow(&parent);
1346 	if (parentWindow) {
1347 		AutoLock<BWindow> lock(parentWindow);
1348 		parentWindow->PoseView()->SelectPoseAtLocation(pointInPose);
1349 	}
1350 }
1351 
1352 bool
1353 TTracker::CloseParentWaitingForChild(const entry_ref *child,
1354 	const node_ref *parent)
1355 {
1356 	AutoLock<WindowList> lock(&fWindowList);
1357 
1358 	BContainerWindow *parentWindow = FindContainerWindow(parent);
1359 	if (!parentWindow)
1360 		// parent window already closed, give up
1361 		return true;
1362 
1363 	// If child is a symbolic link, dereference it, so that
1364 	// FindContainerWindow will succeed.
1365 	BEntry entry(child, true);
1366 	entry_ref resolvedChild;
1367 	if (entry.GetRef(&resolvedChild) != B_OK)
1368 		resolvedChild = *child;
1369 
1370 	BContainerWindow *window = FindContainerWindow(&resolvedChild);
1371 	if (window) {
1372 		AutoLock<BWindow> lock(window);
1373 		if (!window->IsHidden())
1374 			return CloseParentWindowCommon(parentWindow);
1375 	}
1376 	return false;
1377 }
1378 
1379 void
1380 TTracker::CloseParent(node_ref parent)
1381 {
1382 	AutoLock<WindowList> lock(&fWindowList);
1383 	if (!lock)
1384 		return;
1385 
1386 	CloseParentWindowCommon(FindContainerWindow(&parent));
1387 }
1388 
1389 void
1390 TTracker::ShowSettingsWindow()
1391 {
1392 	if (!fSettingsWindow) {
1393 		fSettingsWindow = new TrackerSettingsWindow();
1394 		fSettingsWindow->Show();
1395 	} else {
1396 		if (fSettingsWindow->Lock()) {
1397 			if (fSettingsWindow->IsHidden())
1398 				fSettingsWindow->Show();
1399 			else
1400 				fSettingsWindow->Activate();
1401 			fSettingsWindow->Unlock();
1402 		}
1403 	}
1404 }
1405 
1406 bool
1407 TTracker::CloseParentWindowCommon(BContainerWindow *window)
1408 {
1409 	ASSERT(fWindowList.IsLocked());
1410 
1411 	if (dynamic_cast<BDeskWindow *>(window))
1412 		// don't close the destop
1413 		return false;
1414 
1415 	window->PostMessage(B_QUIT_REQUESTED);
1416 	return true;
1417 }
1418 
1419 bool
1420 TTracker::SelectChildInParent(const entry_ref *parent, const node_ref *child)
1421 {
1422 	AutoLock<WindowList> lock(&fWindowList);
1423 
1424 	BContainerWindow *window = FindContainerWindow(parent);
1425 	if (!window)
1426 		// parent window already closed, give up
1427 		return false;
1428 
1429 	AutoLock<BWindow> windowLock(window);
1430 
1431 	if (windowLock.IsLocked()) {
1432 		BPoseView *view = window->PoseView();
1433 		int32 index;
1434 		BPose *pose = view->FindPose(child, &index);
1435 		if (pose) {
1436 			view->SelectPose(pose, index);
1437 			return true;
1438 		}
1439 	}
1440 	return false;
1441 }
1442 
1443 const int32 kNodeMonitorBumpValue = 512;
1444 
1445 status_t
1446 TTracker::NeedMoreNodeMonitors()
1447 {
1448 	fNodeMonitorCount += kNodeMonitorBumpValue;
1449 	PRINT(("bumping nodeMonitorCount to %d\n", fNodeMonitorCount));
1450 
1451 	return _kset_mon_limit_(fNodeMonitorCount);
1452 }
1453 
1454 status_t
1455 TTracker::WatchNode(const node_ref *node, uint32 flags,
1456 	BMessenger target)
1457 {
1458 	status_t result = watch_node(node, flags, target);
1459 	if (result == B_OK || result != B_NO_MEMORY) {
1460 		// need to make sure this uses the same error value as
1461 		// the node monitor code
1462 		return result;
1463 	}
1464 
1465 	PRINT(("failed to start monitoring, trying to allocate more "
1466 		"node monitors\n"));
1467 
1468 	TTracker *tracker = dynamic_cast<TTracker *>(be_app);
1469 	if (!tracker) {
1470 		// we are the file panel only, just fail
1471 		return result;
1472 	}
1473 
1474 	result = tracker->NeedMoreNodeMonitors();
1475 
1476 	if (result != B_OK) {
1477 		PRINT(("failed to allocate more node monitors, %s\n",
1478 			strerror(result)));
1479 		return result;
1480 	}
1481 
1482 	// try again, this time with more node monitors
1483 	return watch_node(node, flags, target);
1484 }
1485 
1486 
1487 AutoMounter *
1488 TTracker::AutoMounterLoop()
1489 {
1490 	return fAutoMounter;
1491 }
1492 
1493 
1494 bool
1495 TTracker::InTrashNode(const entry_ref *node) const
1496 {
1497 	return FSInTrashDir(node);
1498 }
1499 
1500 
1501 bool
1502 TTracker::TrashFull() const
1503 {
1504 	return fTrashWatcher->CheckTrashDirs();
1505 }
1506 
1507 
1508 bool
1509 TTracker::IsTrashNode(const node_ref *node) const
1510 {
1511 	return fTrashWatcher->IsTrashNode(node);
1512 }
1513 
1514