/*
Open Tracker License

Terms and Conditions

Copyright (c) 1991-2000, Be Incorporated. All rights reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice applies to all licensees
and shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Except as contained in this notice, the name of Be Incorporated shall not be
used in advertising or otherwise to promote the sale, use or other dealings in
this Software without prior written authorization from Be Incorporated.

Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
of Be Incorporated in the United States and other countries. Other brand product
names are registered trademarks or trademarks of their respective holders.
All rights reserved.
*/

#include "Tracker.h"

#include <errno.h>
#include <fs_attr.h>
#include <fs_info.h>
#include <image.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/resource.h>
#include <unistd.h>

#include <Alert.h>
#include <Autolock.h>
#include <Catalog.h>
#include <Debug.h>
#include <FindDirectory.h>
#include <Locale.h>
#include <MenuItem.h>
#include <NodeInfo.h>
#include <NodeMonitor.h>
#include <Path.h>
#include <PathMonitor.h>
#include <Roster.h>
#ifndef HoG_GENODE
#include <StopWatch.h>
#endif
#include <Volume.h>
#include <VolumeRoster.h>

#include "Attributes.h"
#include "AutoLock.h"
#include "BackgroundImage.h"
#include "Bitmaps.h"
#include "Commands.h"
#include "ContainerWindow.h"
#include "DeskWindow.h"
#include "FindPanel.h"
#include "FunctionObject.h"
#include "FSClipboard.h"
#include "FSUtils.h"
#include "InfoWindow.h"
#include "MimeTypes.h"
#include "MimeTypeList.h"
#include "NodePreloader.h"
#include "OpenWithWindow.h"
#include "PoseView.h"
#include "QueryContainerWindow.h"
#include "StatusWindow.h"
#include "TaskLoop.h"
#include "Thread.h"
#include "TrackerSettings.h"
#include "TrackerSettingsWindow.h"
#include "TrackerString.h"
#include "TrashWatcher.h"
#include "VirtualDirectoryWindow.h"


#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "Tracker"


// prototypes for some private kernel calls that will some day be public
#ifndef _IMPEXP_ROOT
#	define _IMPEXP_ROOT
#endif


const int32 DEFAULT_MON_NUM = 4096;
	// copied from fsil.c

const int8 kOpenWindowNoFlags = 0;
const int8 kOpenWindowMinimized = 1;
const int8 kOpenWindowHasState = 2;

const uint32 PSV_MAKE_PRINTER_ACTIVE_QUIETLY = 'pmaq';
	// from pr_server.h

const int32 kNodeMonitorBumpValue = 512;


namespace BPrivate {

NodePreloader* gPreloader = NULL;


class LaunchLooper : public BLooper {
public:
	LaunchLooper()
		:
		BLooper("launch looper")
	{
	}

	virtual void
	MessageReceived(BMessage* message)
	{
		void (*function)(const entry_ref*, const BMessage*, bool);
		BMessage refs;
		bool openWithOK;
		entry_ref appRef;

		if (message->FindPointer("function", (void**)&function) != B_OK
			|| message->FindMessage("refs", &refs) != B_OK
			|| message->FindBool("openWithOK", &openWithOK) != B_OK) {
			printf("incomplete launch message\n");
			return;
		}

		if (message->FindRef("appRef", &appRef) == B_OK)
			function(&appRef, &refs, openWithOK);
		else
			function(NULL, &refs, openWithOK);
	}
};

BLooper* gLaunchLooper = NULL;


// #pragma mark - functions


void
InitIconPreloader()
{
	static int32 lock = 0;

	if (atomic_add(&lock, 1) != 0) {
		// Just wait for the icon cache to be instantiated
		int32 tries = 20;
		while (IconCache::sIconCache == NULL && tries-- > 0)
			snooze(10000);
		return;
	}

	if (IconCache::sIconCache != NULL)
		return;

	// only start the node preloader if its Tracker or the Deskbar itself,
	// don't start it for file panels

	bool preload = dynamic_cast<TTracker*>(be_app) != NULL;
	if (!preload) {
		// check for deskbar
		app_info info;
		if (be_app->GetAppInfo(&info) == B_OK
			&& !strcmp(info.signature, kDeskbarSignature))
			preload = true;
	}

	if (preload) {
		gPreloader = NodePreloader::InstallNodePreloader("NodePreloader",
			be_app);
	}

	IconCache::sIconCache = new IconCache();

	atomic_add(&lock, -1);
}

}	// namespace BPrivate


uint32
GetVolumeFlags(Model* model)
{
	fs_info info;
	if (model->IsVolume()) {
		// search for the correct volume
		int32 cookie = 0;
		dev_t device;
		while ((device = next_dev(&cookie)) >= B_OK) {
			if (fs_stat_dev(device,&info))
				continue;

			if (!strcmp(info.volume_name,model->Name()))
				return info.flags;
		}
		return B_FS_HAS_ATTR;
	}
	if (!fs_stat_dev(model->NodeRef()->device,&info))
		return info.flags;

	return B_FS_HAS_ATTR;
}


//	#pragma mark - WatchingInterface


class TTracker::WatchingInterface : public BPathMonitor::BWatchingInterface {
public:
	virtual status_t WatchNode(const node_ref* node, uint32 flags,
		const BMessenger& target)
	{
		return TTracker::WatchNode(node, flags, target);
	}

	virtual status_t WatchNode(const node_ref* node, uint32 flags,
		const BHandler* handler, const BLooper* looper = NULL)
	{
		return TTracker::WatchNode(node, flags, BMessenger(handler, looper));
	}
};


//	#pragma mark - TTracker


TTracker::TTracker()
	:
	BApplication(kTrackerSignature),
	fMimeTypeList(NULL),
	fClipboardRefsWatcher(NULL),
	fTrashWatcher(NULL),
	fTaskLoop(NULL),
	fNodeMonitorCount(-1),
	fWatchingInterface(new WatchingInterface),
	fSettingsWindow(NULL)
{
	BPathMonitor::SetWatchingInterface(fWatchingInterface);

	// set the cwd to /boot/home, anything that's launched
	// from Tracker will automatically inherit this
	BPath homePath;

	if (find_directory(B_USER_DIRECTORY, &homePath) == B_OK)
		chdir(homePath.Path());

	// ask for a bunch more file descriptors so that nested copying works well
	struct rlimit rl;
	rl.rlim_cur = 512;
	rl.rlim_max = RLIM_SAVED_MAX;
	setrlimit(RLIMIT_NOFILE, &rl);

	fNodeMonitorCount = DEFAULT_MON_NUM;

	gLocalizedNamePreferred
		= BLocaleRoster::Default()->IsFilesystemTranslationPreferred();

#ifdef CHECK_OPEN_MODEL_LEAKS
	InitOpenModelDumping();
#endif

	InitIconPreloader();

#ifdef LEAK_CHECKING
	SetNewLeakChecking(true);
	SetMallocLeakChecking(true);
#endif

	// This is how often it should update the free space bar on the
	// volume icons
	SetPulseRate(1000000);

	gLaunchLooper = new LaunchLooper();
	gLaunchLooper->Run();

	// open desktop window
	BContainerWindow* deskWindow = NULL;
	BDirectory deskDir;
	if (FSGetDeskDir(&deskDir) == B_OK) {
		// create desktop
		BEntry entry;
		deskDir.GetEntry(&entry);
		Model* model = new Model(&entry, true);
		if (model->InitCheck() == B_OK) {
			AutoLock<WindowList> lock(&fWindowList);
			deskWindow = new BDeskWindow(&fWindowList);
			AutoLock<BWindow> windowLock(deskWindow);
			deskWindow->CreatePoseView(model);
			deskWindow->Init();

			if (TrackerSettings().ShowDisksIcon()) {
				// create model for root of everything
				BEntry entry("/");
				Model model(&entry);
				if (model.InitCheck() == B_OK) {
					// add the root icon to desktop window
					BMessage message;
					message.what = B_NODE_MONITOR;
					message.AddInt32("opcode", B_ENTRY_CREATED);
#ifndef HoG_GENODE
					message.AddInt32("device", model.NodeRef()->device);
					message.AddInt64("node", model.NodeRef()->node);
					message.AddInt64("directory",
						model.EntryRef()->directory);
#else
					message.AddInt64("device", model.NodeRef()->device);
					message.AddUInt64("node", model.NodeRef()->node);
					message.AddUInt64("directory",
						model.EntryRef()->directory);
#endif
					message.AddString("name", model.EntryRef()->name);
					deskWindow->PostMessage(&message, deskWindow->PoseView());
				}
			}
		} else
			delete model;
	}
}


TTracker::~TTracker()
{
	gLaunchLooper->Lock();
	gLaunchLooper->Quit();

	BPathMonitor::SetWatchingInterface(NULL);
	delete fWatchingInterface;
}


bool
TTracker::QuitRequested()
{
	// don't allow user quitting
	if (CurrentMessage() != NULL && CurrentMessage()->FindBool("shortcut")) {
		// but allow quitting to hide fSettingsWindow
		int32 index = 0;
		BWindow* window = NULL;
		while ((window = WindowAt(index++)) != NULL) {
			if (window == fSettingsWindow) {
				if (fSettingsWindow->Lock()) {
					if (!fSettingsWindow->IsHidden()
						&& fSettingsWindow->IsActive()) {
						fSettingsWindow->Hide();
					}
					fSettingsWindow->Unlock();
				}
				break;
			}
		}

		return false;
	}

	gStatusWindow->AttemptToQuit();
		// try quitting the copy/move/empty trash threads

	BMessage message;
	AutoLock<WindowList> lock(&fWindowList);
	// save open windows in a message inside an attribute of the desktop
	int32 count = fWindowList.CountItems();
	for (int32 i = 0; i < count; i++) {
		BContainerWindow* window
			= dynamic_cast<BContainerWindow*>(fWindowList.ItemAt(i));

		if (window != NULL && window->Lock()) {
			if (window->TargetModel() != NULL
				&& !window->PoseView()->IsDesktopWindow()) {
				if (window->TargetModel()->IsRoot())
					message.AddBool("open_disks_window", true);
				else {
					BEntry entry;
					BPath path;
					const entry_ref* ref = window->TargetModel()->EntryRef();
					if (entry.SetTo(ref) == B_OK
						&& entry.GetPath(&path) == B_OK) {
						int8 flags = window->IsMinimized()
							? kOpenWindowMinimized : kOpenWindowNoFlags;
						uint32 deviceFlags
							= GetVolumeFlags(window->TargetModel());

						// save state for every window which is
						//	a) already open on another workspace
						//	b) on a volume not capable of writing attributes
						if (window != FindContainerWindow(ref)
							|| (deviceFlags
								& (B_FS_HAS_ATTR | B_FS_IS_READONLY))
									!= B_FS_HAS_ATTR) {
							BMessage stateMessage;
							window->SaveState(stateMessage);
							window->SetSaveStateEnabled(false);
								// This is to prevent its state to be saved
								// to the node when closed.
							message.AddMessage("window state", &stateMessage);
							flags |= kOpenWindowHasState;
						}
						const char* target;
						bool pathAlreadyExists = false;
						for (int32 index = 0;
								message.FindString("paths", index, &target)
									== B_OK; index++) {
							if (!strcmp(target,path.Path())) {
								pathAlreadyExists = true;
								break;
							}
						}
						if (!pathAlreadyExists)
							message.AddString("paths", path.Path());

						message.AddInt8(path.Path(), flags);
					}
				}
			}
			window->Unlock();
		}
	}
	lock.Unlock();

	// write windows to open on disk
	BDirectory deskDir;
	if (!BootedInSafeMode() && FSGetDeskDir(&deskDir) == B_OK) {
		// if message is empty, delete the corresponding attribute
		if (message.CountNames(B_ANY_TYPE)) {
			ssize_t size = message.FlattenedSize();
			if (size > 0) {
				char* buffer = new char[size];
				message.Flatten(buffer, size);
				deskDir.WriteAttr(kAttrOpenWindows, B_MESSAGE_TYPE, 0, buffer,
					size);
				delete[] buffer;
			}
		} else
			deskDir.RemoveAttr(kAttrOpenWindows);
	}

	for (int32 count = 0; count < 50; count++) {
		// wait 5 seconds for the copiing/moving to quit
		if (gStatusWindow->AttemptToQuit())
			break;

		snooze(100000);
	}

	return _inherited::QuitRequested();
}


void
TTracker::Quit()
{
	TrackerSettings().SaveSettings(false);

	fClipboardRefsWatcher->Lock();
	fClipboardRefsWatcher->Quit();

	fTrashWatcher->Lock();
	fTrashWatcher->Quit();

	WellKnowEntryList::Quit();

	delete gPreloader;
	delete fTaskLoop;
	delete IconCache::sIconCache;

	_inherited::Quit();
}


void
TTracker::MessageReceived(BMessage* message)
{
	if (HandleScriptingMessage(message))
		return;

	switch (message->what) {
		case kGetInfo:
			OpenInfoWindows(message);
			break;

		case kMoveToTrash:
			MoveRefsToTrash(message);
			break;

		case kSelect:
			SelectRefs(message);
			break;

		case kCloseWindowAndChildren:
		{
			const node_ref* itemNode;
			ssize_t bytes;
			if (message->FindData("node_ref", B_RAW_TYPE,
					(const void**)&itemNode, &bytes) == B_OK) {
				CloseWindowAndChildren(itemNode);
			}
			break;
		}

		case kCloseAllWindows:
			CloseAllWindows();
			break;

		case kCloseAllInWorkspace:
			CloseAllInWorkspace();
			break;

		case kFindButton:
			(new FindWindow())->Show();
			break;

		case kEditQuery:
			EditQueries(message);
			break;

		case kShowSplash:
			run_be_about();
			break;

		case kAddPrinter:
			// show the addprinter window
			run_add_printer_panel();
			break;

		case kMakeActivePrinter:
			// get the current selection
			SetDefaultPrinter(message);
			break;

#ifdef MOUNT_MENU_IN_DESKBAR
		case 'gmtv':
		{
			// Someone (probably the deskbar) has requested a list of
			// mountable volumes.
			BMessage reply;
			AutoMounterLoop()->EachMountableItemAndFloppy(
				&AddMountableItemToMessage, &reply);
			message->SendReply(&reply);
			break;
		}
#endif

		case kUnmountVolume:
			// When the user attempts to unmount a volume from the mount
			// context menu, this is where the message gets received.
			// Save pose locations and forward this to the automounter
			SaveAllPoseLocations();
			// Fall through...
		case kMountVolume:
		case kMountAllNow:
			MountServer().SendMessage(message);
			break;


		case kRestoreBackgroundImage:
		{
			BDeskWindow* desktop = GetDeskWindow();
			AutoLock<BWindow> lock(desktop);
			desktop->UpdateDesktopBackgroundImages();
			break;
		}

		case kRunAutomounterSettings:
			ShowSettingsWindow();
			fSettingsWindow->ShowPage(
				TrackerSettingsWindow::kAutomountSettings);
			break;

		case kShowSettingsWindow:
			ShowSettingsWindow();
			break;

		case kFavoriteCountChangedExternally:
			SendNotices(kFavoriteCountChangedExternally, message);
			break;

		case kStartWatchClipboardRefs:
		{
			BMessenger messenger;
			message->FindMessenger("target", &messenger);
			if (messenger.IsValid())
				fClipboardRefsWatcher->AddToNotifyList(messenger);
			break;
		}

		case kStopWatchClipboardRefs:
		{
			BMessenger messenger;
			if (message->FindMessenger("target", &messenger) == B_OK
				&& messenger.IsValid()) {
				fClipboardRefsWatcher->RemoveFromNotifyList(messenger);
			}
			break;
		}

		case kFSClipboardChanges:
			fClipboardRefsWatcher->UpdatePoseViews(message);
			break;

		case kShowVolumeSpaceBar:
		case kSpaceBarColorChanged:
			gPeriodicUpdatePoses.DoPeriodicUpdate(true);
			break;

		case B_LOCALE_CHANGED:
		{
			BLocaleRoster::Default()->Refresh();
			bool localize;
			if (message->FindBool("filesys", &localize) == B_OK)
				gLocalizedNamePreferred = localize;
			break;
		}

		default:
			_inherited::MessageReceived(message);
			break;
	}
}


void
TTracker::Pulse()
{
	if (!TrackerSettings().ShowVolumeSpaceBar())
		return;

	// update the volume icon's free space bars
	gPeriodicUpdatePoses.DoPeriodicUpdate(false);
}


void
TTracker::SetDefaultPrinter(const BMessage* message)
{
	// get the first item selected
	int32 count = 0;
	uint32 type = 0;
	message->GetInfo("refs", &type, &count);

	if (count <= 0)
		return;

	// will make the first item the default printer, disregards any
	// other files
	entry_ref ref;
	ASSERT(message->FindRef("refs", 0, &ref) == B_OK);
	if (message->FindRef("refs", 0, &ref) != B_OK)
		return;

#if B_BEOS_VERSION_DANO
	set_default_printer(ref.name);
#else
	// 	create a message for the print server
	BMessenger messenger("application/x-vnd.Be-PSRV", -1);
	if (!messenger.IsValid())
		return;

	//	send the selection to the print server
	BMessage makeActiveMessage(PSV_MAKE_PRINTER_ACTIVE_QUIETLY);
	makeActiveMessage.AddString("printer", ref.name);

	BMessage reply;
	messenger.SendMessage(&makeActiveMessage, &reply);
#endif
}


void
TTracker::MoveRefsToTrash(const BMessage* message)
{
	int32 count;
	uint32 type;
	message->GetInfo("refs", &type, &count);

	if (count <= 0)
		return;

	BObjectList<entry_ref>* srcList = new BObjectList<entry_ref>(count, true);

	for (int32 index = 0; index < count; index++) {
		entry_ref ref;
		ASSERT(message->FindRef("refs", index, &ref) == B_OK);
		if (message->FindRef("refs", index, &ref) != B_OK)
			continue;

		AutoLock<WindowList> lock(&fWindowList);
		BContainerWindow* window = FindParentContainerWindow(&ref);
		if (window != NULL) {
			// if we have a window open for this entry, ask the pose to
			// delete it, this will select the next entry
			window->PoseView()->MoveEntryToTrash(&ref);
		} else {
			// add all others to a list that gets deleted separately
			srcList->AddItem(new entry_ref(ref));
		}
	}

	// async move to trash
	FSMoveToTrash(srcList);
}


void
TTracker::SelectRefs(const BMessage* message)
{
	uint32 type = 0;
	int32 count = 0;
	message->GetInfo("refs", &type, &count);

	for (int32 index = 0; index < count; index++) {
		entry_ref ref;
		message->FindRef("refs", index, &ref);
		BEntry entry(&ref, true);
		if (entry.InitCheck() != B_OK || !entry.Exists())
			continue;

		AutoLock<WindowList> lock(&fWindowList);
		BContainerWindow* window = FindParentContainerWindow(&ref);
		if (window == NULL)
			continue;

		char name[B_FILE_NAME_LENGTH];
		if (entry.GetName(name) != B_OK)
			continue;

		BString expression;
		expression << "^";
		expression << name;
		expression << "$";

		BMessage* selectMessage = new BMessage(kSelectMatchingEntries);
		selectMessage->AddInt32("ExpressionType", kRegexpMatch);
		selectMessage->AddString("Expression", expression);
		selectMessage->AddBool("InvertSelection", false);
		selectMessage->AddBool("IgnoreCase", false);

		window->Activate();
			// must be activated to populate the pose list

		snooze(100000);
			// wait a bit for the pose list to be populated
			// ToDo: figure out why this is necessary

		window->PostMessage(selectMessage);
	}
}


template <class T, class FT>
class EntryAndNodeDoSoonWithMessageFunctor : public
	FunctionObjectWithResult<bool> {
public:
	EntryAndNodeDoSoonWithMessageFunctor(FT func, T* target,
		const entry_ref* child, const node_ref* parent,
		const BMessage* message)
		:
		fFunc(func),
		fTarget(target),
		fNode(*parent),
		fEntry(*child)
	{
		fSendMessage = message != NULL;
		if (message != NULL)
			fMessage = *message;
	}

	virtual ~EntryAndNodeDoSoonWithMessageFunctor() {}
	virtual void operator()()
	{
		result = (fTarget->*fFunc)(&fEntry, &fNode,
			fSendMessage ? &fMessage : NULL);
	}

protected:
	FT fFunc;
	T* fTarget;
	node_ref fNode;
	entry_ref fEntry;
	BMessage fMessage;
	bool fSendMessage;
};


bool
TTracker::LaunchAndCloseParentIfOK(const entry_ref* launchThis,
	const node_ref* closeThis, const BMessage* messageToBundle)
{
	BMessage refsReceived(B_REFS_RECEIVED);
	if (messageToBundle != NULL) {
		refsReceived = *messageToBundle;
		refsReceived.what = B_REFS_RECEIVED;
	}
	refsReceived.AddRef("refs", launchThis);
	// synchronous launch, we are already in our own thread
	if (TrackerLaunch(&refsReceived, false) == B_OK) {
		// if launched fine, close parent window in a bit
		fTaskLoop->RunLater(NewMemberFunctionObject(&TTracker::CloseParent,
			this, *closeThis), 1000000);
	}

	return false;
}


status_t
TTracker::OpenRef(const entry_ref* ref, const node_ref* nodeToClose,
	const node_ref* nodeToSelect, OpenSelector selector,
	const BMessage* messageToBundle)
{
	Model* model = NULL;
	BEntry entry(ref, true);
	status_t result = entry.InitCheck();

	bool brokenLinkWithSpecificHandler = false;
	BString brokenLinkPreferredApp;

	if (result != B_OK) {
		model = new Model(ref, false);
		if (model->IsSymLink() && !model->LinkTo()) {
			model->GetPreferredAppForBrokenSymLink(brokenLinkPreferredApp);
			if (brokenLinkPreferredApp.Length()
				&& brokenLinkPreferredApp != kTrackerSignature) {
				brokenLinkWithSpecificHandler = true;
			}
		}

		if (!brokenLinkWithSpecificHandler) {
			delete model;
			BAlert* alert = new BAlert("",
				B_TRANSLATE("There was an error resolving the link."),
				B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL,
					B_WARNING_ALERT);
			alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
			alert->Go();

			return result;
		}
	} else
		model = new Model(&entry);

	result = model->InitCheck();
	if (result != B_OK) {
		delete model;
		return result;
	}

	bool openAsContainer = model->IsContainer();

	if (openAsContainer && selector != kOpenWith) {
		// if folder or query has a preferred handler and it's not the
		// Tracker, open it by sending refs to the handling app

		// if we are responding to the final open of OpenWith, just
		// skip this and proceed to opening the container with Tracker
		model->OpenNode();
		BNodeInfo nodeInfo(model->Node());
		char preferredApp[B_MIME_TYPE_LENGTH];
		if (nodeInfo.GetPreferredApp(preferredApp) == B_OK
			&& strcasecmp(preferredApp, kTrackerSignature) != 0) {
			openAsContainer = false;
		}
		model->CloseNode();
	}

	if (openAsContainer || selector == kRunOpenWithWindow) {
		// special case opening plain folders, queries or using open with
		OpenContainerWindow(model, NULL, selector, kRestoreDecor);
			// window adopts model
		if (nodeToClose)
			CloseParentWaitingForChildSoon(ref, nodeToClose);
	} else if (model->IsQueryTemplate()) {
		// query template - open new find window
		(new FindWindow(model->EntryRef()))->Show();

		delete model;
		if (nodeToClose)
			CloseParentWaitingForChildSoon(ref, nodeToClose);
	} else {
		delete model;
		// run Launch in a separate thread and close parent if successful
		if (nodeToClose) {
			Thread::Launch(new EntryAndNodeDoSoonWithMessageFunctor<TTracker,
				bool (TTracker::*)(const entry_ref*, const node_ref*,
				const BMessage*)>(&TTracker::LaunchAndCloseParentIfOK, this,
				ref, nodeToClose, messageToBundle));
		} else {
			BMessage refsReceived(B_REFS_RECEIVED);
			if (messageToBundle) {
				refsReceived = *messageToBundle;
				refsReceived.what = B_REFS_RECEIVED;
			}
			refsReceived.AddRef("refs", ref);
			if (brokenLinkWithSpecificHandler) {
				// This cruft is to support a hacky workaround for
				// double-clicking broken refs for cifs; should get fixed
				// in R5
				LaunchBrokenLink(brokenLinkPreferredApp.String(),
					&refsReceived);
			} else
				TrackerLaunch(&refsReceived, true);
		}
	}

	if (nodeToSelect)
		SelectChildInParentSoon(ref, nodeToSelect);

	return B_OK;
}


void
TTracker::RefsReceived(BMessage* message)
{
	OpenSelector selector = kOpen;
	if (message->HasInt32("launchUsingSelector"))
		selector = kRunOpenWithWindow;

	entry_ref handlingApp;
	if (message->FindRef("handler", &handlingApp) == B_OK)
		selector = kOpenWith;

	int32 count;
	uint32 type;
	message->GetInfo("refs", &type, &count);

	switch (selector) {
		case kRunOpenWithWindow:
			OpenContainerWindow(NULL, message, selector);
				// window adopts model
			break;

		case kOpenWith:
		{
			// Open With resulted in passing refs and a handler,
			// open the files with the handling app
			message->RemoveName("handler");

			// have to find out if handling app is the Tracker
			// if it is, just pass it to the active Tracker,
			// no matter which Tracker was chosen to handle the refs
			char signature[B_MIME_TYPE_LENGTH];
			signature[0] = '\0';
			{
				BFile handlingNode(&handlingApp, O_RDONLY);
				BAppFileInfo appInfo(&handlingNode);
				appInfo.GetSignature(signature);
			}

			if (strcasecmp(signature, kTrackerSignature) != 0) {
				// handling app not Tracker, pass entries to the apps
				// RefsReceived
				TrackerLaunch(&handlingApp, message, true);
				break;
			}
		}
		// fall thru, opening refs by the Tracker as if they were
		// double-clicked
		case kOpen:
		{
			// copy over "Poses" messenger so that refs received
			// recipients know where the open came from
			BMessage* bundleThis = NULL;
			BMessage stackBundleThis;
			BMessenger messenger;
			if (message->FindMessenger("TrackerViewToken", &messenger)
					== B_OK) {
				bundleThis = &stackBundleThis;
				bundleThis->AddMessenger("TrackerViewToken", messenger);
			} else {
				// copy over any "be:*" fields -- e.g. /bin/open may include
				// "be:line" and "be:column"
				for (int32 i = 0;; i++) {
					char* name;
					type_code type;
					int32 count;
					status_t error = message->GetInfo(B_ANY_TYPE, i, &name,
						&type, &count);
					if (error != B_OK)
						break;

					if (strncmp(name, "be:", 3) != 0)
						continue;

					for (int32 k = 0; k < count; k++) {
						const void* data;
						ssize_t size;
						if (message->FindData(name, type, k, &data, &size)
								!= B_OK) {
							break;
						}
						if (stackBundleThis.AddData(name, type, data, size)
								!= B_OK) {
							break;
						}
						bundleThis = &stackBundleThis;
					}
				}
			}

			for (int32 index = 0; index < count; index++) {
				entry_ref ref;
				message->FindRef("refs", index, &ref);

				const node_ref* nodeToClose = NULL;
				const node_ref* nodeToSelect = NULL;
				ssize_t numBytes;

				message->FindData("nodeRefsToClose", B_RAW_TYPE, index,
					(const void**)&nodeToClose, &numBytes);
				message->FindData("nodeRefToSelect", B_RAW_TYPE, index,
					(const void**)&nodeToSelect, &numBytes);

				OpenRef(&ref, nodeToClose, nodeToSelect, selector,
					bundleThis);
			}

			break;
		}
	}
}


void
TTracker::ArgvReceived(int32 argc, char** argv)
{
	BMessage* message = CurrentMessage();
	const char* currentWorkingDirectoryPath = NULL;
	entry_ref ref;

	if (message->FindString("cwd", &currentWorkingDirectoryPath) == B_OK) {
		BDirectory workingDirectory(currentWorkingDirectoryPath);
		for (int32 index = 1; index < argc; index++) {
			BEntry entry;
			if (entry.SetTo(&workingDirectory, argv[index]) == B_OK
				&& entry.GetRef(&ref) == B_OK) {
				OpenRef(&ref);
			} else if (get_ref_for_path(argv[index], &ref) == B_OK)
				OpenRef(&ref);
		}
	}
}


void
TTracker::OpenContainerWindow(Model* model, BMessage* originalRefsList,
	OpenSelector openSelector, uint32 openFlags, bool checkAlreadyOpen,
	const BMessage* stateMessage)
{
	AutoLock<WindowList> lock(&fWindowList);
	BContainerWindow* window = NULL;
	const node_ref* modelNodeRef = model->NodeRef();
	if (checkAlreadyOpen && openSelector != kRunOpenWithWindow) {
		// find out if window already open
		window = FindContainerWindow(modelNodeRef);
	}

	bool someWindowActivated = false;

	uint32 workspace = (uint32)(1 << current_workspace());
	int32 windowCount = 0;
	while (window != NULL) {
		if ((window->Workspaces() & workspace) != 0
			&& (dynamic_cast<BDeskWindow*>(window) == NULL
				|| !TrackerSettings().SingleWindowBrowse())) {
			// We found at least one window that is open and is not Desktop
			// or we're in spatial mode, activate it and make sure we don't
			// jerk the workspaces around.
			window->Activate();
			someWindowActivated = true;
		}
		window = FindContainerWindow(model->NodeRef(), ++windowCount);
	}

	if (someWindowActivated) {
		delete model;
		return;
	}

	// If no window was activated (none in the current workspace),
	// we open a new one.

	if (openSelector == kRunOpenWithWindow) {
		BMessage* refList = NULL;
		if (originalRefsList == NULL) {
			// when passing just a single model, stuff it's entry in a single
			// element list anyway
			ASSERT(model != NULL);
			refList = new BMessage;
			refList->AddRef("refs", model->EntryRef());
			delete model;
			model = NULL;
		} else {
			// clone the message, window adopts it for it's own use
			refList = new BMessage(*originalRefsList);
		}
		window = new OpenWithContainerWindow(refList, &fWindowList);
	} else if (model->IsQuery()) {
		// window will adopt the model
		window = new BQueryContainerWindow(&fWindowList, openFlags);
	} else if (model->IsVirtualDirectory()) {
		// window will adopt the model
		window = new VirtualDirectoryWindow(&fWindowList, openFlags);
	} else {
		// window will adopt the model
		window = new BContainerWindow(&fWindowList, openFlags);
	}

	if (model != NULL && window->LockLooper()) {
		window->CreatePoseView(model);
		if (window->PoseView() == NULL) {
			// Failed initialization.
			window->PostMessage(B_QUIT_REQUESTED);
			window->UnlockLooper();
			return;
		}
		window->UnlockLooper();
	}

	BMessage restoreStateMessage(kRestoreState);

	if (stateMessage != NULL)
		restoreStateMessage.AddMessage("state", stateMessage);

	window->PostMessage(&restoreStateMessage);
}


void
TTracker::EditQueries(const BMessage* message)
{
	bool editOnlyIfTemplate;
	if (message->FindBool("editQueryOnPose", &editOnlyIfTemplate) != B_OK)
		editOnlyIfTemplate = false;

	type_code type;
	int32 count;
	message->GetInfo("refs", &type, &count);
	for (int32 index = 0; index < count; index++) {
		entry_ref ref;
		message->FindRef("refs", index, &ref);
		BEntry entry(&ref, true);
		if (entry.InitCheck() == B_OK && entry.Exists())
			(new FindWindow(&ref, editOnlyIfTemplate))->Show();
	}
}


void
TTracker::OpenInfoWindows(BMessage* message)
{
	type_code type;
	int32 count;
	message->GetInfo("refs", &type, &count);

	for (int32 index = 0; index < count; index++) {
		entry_ref ref;
		message->FindRef("refs", index, &ref);
		BEntry entry;
		if (entry.SetTo(&ref) == B_OK) {
			Model* model = new Model(&entry);
			if (model->InitCheck() != B_OK) {
				delete model;
				continue;
			}

			AutoLock<WindowList> lock(&fWindowList);
			BInfoWindow* wind = FindInfoWindow(model->NodeRef());

			if (wind) {
				wind->Activate();
				delete model;
			} else {
				wind = new BInfoWindow(model, index, &fWindowList);
				wind->PostMessage(kRestoreState);
			}
		}
	}
}


BDeskWindow*
TTracker::GetDeskWindow() const
{
	int32 count = fWindowList.CountItems();
	for (int32 index = 0; index < count; index++) {
		BDeskWindow* window = dynamic_cast<BDeskWindow*>(
			fWindowList.ItemAt(index));
		if (window != NULL)
			return window;
	}
	TRESPASS();

	return NULL;
}


BContainerWindow*
TTracker::FindContainerWindow(const node_ref* node, int32 number) const
{
	ASSERT(fWindowList.IsLocked());

	int32 count = fWindowList.CountItems();
	int32 windowsFound = 0;
	for (int32 index = 0; index < count; index++) {
		BContainerWindow* window = dynamic_cast<BContainerWindow*>(
			fWindowList.ItemAt(index));

		if (window != NULL && window->IsShowing(node)
			&& number == windowsFound++) {
			return window;
		}
	}

	return NULL;
}


BContainerWindow*
TTracker::FindContainerWindow(const entry_ref* entry, int32 number) const
{
///ToDo: when compiled with -DDEBUG, we hit a few asserts, like this one ; is that a case of "Haiku chokes on its own debug, and so do we", or to the contrary does that hint at a difference between Haiku and HoG that we should look into ?
	ASSERT(fWindowList.IsLocked());

	int32 count = fWindowList.CountItems();

	int32 windowsFound = 0;

	for (int32 index = 0; index < count; index++) {
		BContainerWindow* window = dynamic_cast<BContainerWindow*>
			(fWindowList.ItemAt(index));

		if (window && window->IsShowing(entry) && number == windowsFound++)
			return window;
	}

	return NULL;
}


bool
TTracker::EntryHasWindowOpen(const entry_ref* entry)
{
	AutoLock<WindowList> lock(&fWindowList);
	return FindContainerWindow(entry) != NULL;
}


BContainerWindow*
TTracker::FindParentContainerWindow(const entry_ref* ref) const
{
	BEntry entry(ref);
	BEntry parent;

	if (entry.GetParent(&parent) != B_OK)
		return NULL;

	entry_ref parentRef;
	parent.GetRef(&parentRef);

	ASSERT(fWindowList.IsLocked());

	int32 count = fWindowList.CountItems();
	for (int32 index = 0; index < count; index++) {
		BContainerWindow* window = dynamic_cast<BContainerWindow*>(
			fWindowList.ItemAt(index));
		if (window != NULL && window->IsShowing(&parentRef))
			return window;
	}

	return NULL;
}


BInfoWindow*
TTracker::FindInfoWindow(const node_ref* node) const
{
	ASSERT(fWindowList.IsLocked());

	int32 count = fWindowList.CountItems();
	for (int32 index = 0; index < count; index++) {
		BInfoWindow* window = dynamic_cast<BInfoWindow*>(
			fWindowList.ItemAt(index));
		if (window != NULL && window->IsShowing(node))
			return window;
	}

	return NULL;
}


bool
TTracker::QueryActiveForDevice(dev_t device)
{
	AutoLock<WindowList> lock(&fWindowList);
	int32 count = fWindowList.CountItems();
	for (int32 index = 0; index < count; index++) {
		BQueryContainerWindow* window = dynamic_cast<BQueryContainerWindow*>(
			fWindowList.ItemAt(index));
		if (window != NULL) {
			AutoLock<BWindow> lock(window);
			if (window->ActiveOnDevice(device))
				return true;
		}
	}

	return false;
}


void
TTracker::CloseActiveQueryWindows(dev_t device)
{
	// used when trying to unmount a volume - an active query would prevent
	// that from happening
	bool closed = false;
	AutoLock<WindowList> lock(fWindowList);
	for (int32 index = fWindowList.CountItems(); index >= 0; index--) {
		BQueryContainerWindow* window
			= dynamic_cast<BQueryContainerWindow*>(fWindowList.ItemAt(index));
		if (window != NULL) {
			AutoLock<BWindow> lock(window);
			if (window->ActiveOnDevice(device)) {
				window->PostMessage(B_QUIT_REQUESTED);
				closed = true;
			}
		}
	}

	lock.Unlock();

	if (closed) {
		for (int32 timeout = 30; timeout; timeout--) {
			// wait a bit for windows to fully close
			if (!QueryActiveForDevice(device))
				return;

			snooze(100000);
		}
	}
}


void
TTracker::SaveAllPoseLocations()
{
	int32 numWindows = fWindowList.CountItems();
	for (int32 windowIndex = 0; windowIndex < numWindows; windowIndex++) {
		BContainerWindow* window = dynamic_cast<BContainerWindow*>(
			fWindowList.ItemAt(windowIndex));
		if (window != NULL) {
			AutoLock<BWindow> lock(window);
			BDeskWindow* deskWindow = dynamic_cast<BDeskWindow*>(window);
			if (deskWindow != NULL)
				deskWindow->SaveDesktopPoseLocations();
			else
				window->PoseView()->SavePoseLocations();
		}
	}
}


void
TTracker::CloseWindowAndChildren(const node_ref* node)
{
	BDirectory dir(node);
	if (dir.InitCheck() != B_OK)
		return;

	AutoLock<WindowList> lock(&fWindowList);
	BObjectList<BContainerWindow> closeList;

	// make a list of all windows to be closed
	// count from end to beginning so we can remove items safely
	for (int32 index = fWindowList.CountItems() - 1; index >= 0; index--) {
		BContainerWindow* window = dynamic_cast<BContainerWindow*>(
			fWindowList.ItemAt(index));
		if (window && window->TargetModel()) {
			BEntry wind_entry;
			wind_entry.SetTo(window->TargetModel()->EntryRef());

			if ((*window->TargetModel()->NodeRef() == *node)
				|| dir.Contains(&wind_entry)) {

				// ToDo:
				// get rid of the Remove here, BContainerWindow::Quit does it
				fWindowList.RemoveItemAt(index);
				closeList.AddItem(window);
			}
		}
	}

	// now really close the windows
	int32 numItems = closeList.CountItems();
	for (int32 index = 0; index < numItems; index++) {
		BContainerWindow* window = closeList.ItemAt(index);
		window->PostMessage(B_QUIT_REQUESTED);
	}
}


void
TTracker::CloseAllInWorkspace()
{
	AutoLock<WindowList> lock(&fWindowList);

	int32 currentWorkspace = 1 << current_workspace();
	// count from end to beginning so we can remove items safely
	for (int32 index = fWindowList.CountItems() - 1; index >= 0; index--) {
		BWindow* window = fWindowList.ItemAt(index);
		if (window != NULL && (window->Workspaces() & currentWorkspace) != 0) {
			// avoid the desktop
			if (dynamic_cast<BDeskWindow*>(window) == NULL
				&& dynamic_cast<BStatusWindow*>(window) == NULL) {
				window->PostMessage(B_QUIT_REQUESTED);
			}
		}
	}
}


void
TTracker::CloseAllWindows()
{
	// this is a response to the DeskBar sending us a B_QUIT, when it really
	// means to say close all your windows. It might be better to have it
	// send a kCloseAllWindows message and have windowless apps stay running,
	// which is what we will do for the Tracker
	AutoLock<WindowList> lock(&fWindowList);

	int32 count = CountWindows();
	for (int32 index = 0; index < count; index++) {
		BWindow* window = WindowAt(index);
		// avoid the desktop
		if (dynamic_cast<BDeskWindow*>(window) == NULL
			&& dynamic_cast<BStatusWindow*>(window) == NULL) {
			window->PostMessage(B_QUIT_REQUESTED);
		}
	}

	// count from end to beginning so we can remove items safely
	for (int32 index = fWindowList.CountItems() - 1; index >= 0; index--) {
		BWindow* window = fWindowList.ItemAt(index);
		if (dynamic_cast<BDeskWindow*>(window) == NULL
			&& dynamic_cast<BStatusWindow*>(window) == NULL) {
			// ToDo: get rid of the Remove here, BContainerWindow::Quit()
			// does it
			fWindowList.RemoveItemAt(index);
		}
	}
}


void
TTracker::_OpenPreviouslyOpenedWindows(const char* pathFilter)
{
	size_t filterLength = 0;
	if (pathFilter != NULL)
		filterLength = strlen(pathFilter);

	BDirectory deskDir;
	attr_info attrInfo;
	if (FSGetDeskDir(&deskDir) != B_OK
		|| deskDir.GetAttrInfo(kAttrOpenWindows, &attrInfo) != B_OK) {
		return;
	}

	char* buffer = (char*)malloc((size_t)attrInfo.size);
	BMessage message;
	if (deskDir.ReadAttr(kAttrOpenWindows, B_MESSAGE_TYPE, 0, buffer,
			(size_t)attrInfo.size) != attrInfo.size
		|| message.Unflatten(buffer) != B_OK) {
		free(buffer);
		return;
	}

	free(buffer);

	node_ref nodeRef;
	deskDir.GetNodeRef(&nodeRef);

	int32 stateMessageCounter = 0;
	const char* path;
	for (int32 i = 0; message.FindString("paths", i, &path) == B_OK; i++) {
		if (strncmp(path, pathFilter, filterLength) != 0)
			continue;

		BEntry entry(path, true);
		if (entry.InitCheck() != B_OK)
			continue;

		int8 flags = 0;
		for (int32 j = 0; message.FindInt8(path, j, &flags) == B_OK; j++) {
			Model* model = new Model(&entry);
			if (model->InitCheck() == B_OK && model->IsContainer()) {
				BMessage state;
				bool restoreStateFromMessage = false;
				if ((flags & kOpenWindowHasState) != 0
					&& message.FindMessage("window state",
						stateMessageCounter++, &state) == B_OK) {
					restoreStateFromMessage = true;
				}

				if (restoreStateFromMessage) {
					OpenContainerWindow(model, 0, kOpen, kRestoreWorkspace
						| (flags & kOpenWindowMinimized ? kIsHidden : 0U)
						| kRestoreDecor, false, &state);
				} else {
					OpenContainerWindow(model, 0, kOpen, kRestoreWorkspace
						| (flags & kOpenWindowMinimized ? kIsHidden : 0U)
						| kRestoreDecor);
				}
			} else
				delete model;
		}
	}

	// open disks window if needed

	if (pathFilter == NULL && TrackerSettings().ShowDisksIcon()
		&& message.HasBool("open_disks_window")) {
		BEntry entry("/");
		Model* model = new Model(&entry);
		if (model->InitCheck() == B_OK)
			OpenContainerWindow(model, 0, kOpen, kRestoreWorkspace);
		else
			delete model;
	}
}


void
TTracker::ReadyToRun()
{
	gStatusWindow = new BStatusWindow();
	InitMimeTypes();
	InstallDefaultTemplates();
	InstallIndices();
	InstallTemporaryBackgroundImages();

	fTrashWatcher = new BTrashWatcher();
	fTrashWatcher->Run();

	fClipboardRefsWatcher = new BClipboardRefsWatcher();
	fClipboardRefsWatcher->Run();

	fTaskLoop = new StandAloneTaskLoop(true);

	// kick off building the mime type list for find panels, etc.
	fMimeTypeList = new MimeTypeList();

	if (!BootedInSafeMode()) {
		// kick of transient query killer
		DeleteTransientQueriesTask::StartUpTransientQueryCleaner();
		// the mount_server will have mounted the previous volumes already.
		_OpenPreviouslyOpenedWindows();
	}
}


MimeTypeList*
TTracker::MimeTypes() const
{
	return fMimeTypeList;
}


void
TTracker::SelectChildInParentSoon(const entry_ref* parent,
	const node_ref* child)
{
	fTaskLoop->RunLater(NewMemberFunctionObjectWithResult
		(&TTracker::SelectChildInParent, this, parent, child),
		100000, 200000, 5000000);
}


void
TTracker::CloseParentWaitingForChildSoon(const entry_ref* child,
	const node_ref* parent)
{
	fTaskLoop->RunLater(NewMemberFunctionObjectWithResult
		(&TTracker::CloseParentWaitingForChild, this, child, parent),
		200000, 100000, 5000000);
}


void
TTracker::SelectPoseAtLocationSoon(node_ref parent, BPoint pointInPose)
{
	fTaskLoop->RunLater(NewMemberFunctionObject
		(&TTracker::SelectPoseAtLocationInParent, this, parent, pointInPose),
		100000);
}


void
TTracker::SelectPoseAtLocationInParent(node_ref parent, BPoint pointInPose)
{
	AutoLock<WindowList> lock(&fWindowList);
	BContainerWindow* parentWindow = FindContainerWindow(&parent);
	if (parentWindow != NULL) {
		AutoLock<BWindow> lock(parentWindow);
		parentWindow->PoseView()->SelectPoseAtLocation(pointInPose);
	}
}


bool
TTracker::CloseParentWaitingForChild(const entry_ref* child,
	const node_ref* parent)
{
	AutoLock<WindowList> lock(&fWindowList);

	BContainerWindow* parentWindow = FindContainerWindow(parent);
	if (parentWindow == NULL) {
		// parent window already closed, give up
		return true;
	}

	// If child is a symbolic link, dereference it, so that
	// FindContainerWindow will succeed.
	BEntry entry(child, true);
	entry_ref resolvedChild;
	if (entry.GetRef(&resolvedChild) != B_OK)
		resolvedChild = *child;

	BContainerWindow* window = FindContainerWindow(&resolvedChild);
	if (window != NULL) {
		AutoLock<BWindow> lock(window);
		if (!window->IsHidden())
			return CloseParentWindowCommon(parentWindow);
	}

	return false;
}


void
TTracker::CloseParent(node_ref parent)
{
	AutoLock<WindowList> lock(&fWindowList);
	if (!lock)
		return;

	CloseParentWindowCommon(FindContainerWindow(&parent));
}


void
TTracker::ShowSettingsWindow()
{
	if (fSettingsWindow == NULL) {
		fSettingsWindow = new TrackerSettingsWindow();
		fSettingsWindow->Show();
	} else {
		if (fSettingsWindow->Lock()) {
			if (fSettingsWindow->IsHidden())
				fSettingsWindow->Show();
			else
				fSettingsWindow->Activate();

			fSettingsWindow->Unlock();
		}
	}
}


bool
TTracker::CloseParentWindowCommon(BContainerWindow* window)
{
	ASSERT(fWindowList.IsLocked());

	if (dynamic_cast<BDeskWindow*>(window) != NULL) {
		// don't close the desktop
		return false;
	}

	window->PostMessage(B_QUIT_REQUESTED);
	return true;
}


bool
TTracker::SelectChildInParent(const entry_ref* parent, const node_ref* child)
{
	AutoLock<WindowList> lock(&fWindowList);

	BContainerWindow* window = FindContainerWindow(parent);
	if (window == NULL) {
		// parent window already closed, give up
		return false;
	}

	AutoLock<BWindow> windowLock(window);
	if (windowLock.IsLocked()) {
		BPoseView* view = window->PoseView();
		int32 index;
		BPose* pose = view->FindPose(child, &index);
		if (pose != NULL) {
			view->SelectPose(pose, index);
			return true;
		}
	}

	return false;
}


status_t
TTracker::NeedMoreNodeMonitors()
{
	fNodeMonitorCount += kNodeMonitorBumpValue;
	PRINT(("bumping nodeMonitorCount to %" B_PRId32 "\n", fNodeMonitorCount));

	struct rlimit rl;
	rl.rlim_cur = fNodeMonitorCount;
	rl.rlim_max = RLIM_SAVED_MAX;
#ifndef HoG_GENODE
	if (setrlimit(RLIMIT_NOVMON, &rl) < 0) {
		fNodeMonitorCount -= kNodeMonitorBumpValue;
		return errno;
	}
#else
///later: setrlimit(RLIMIT_NOVMON) ?
	return -1;
#endif

	return B_OK;
}


status_t
TTracker::WatchNode(const node_ref* node, uint32 flags, BMessenger target)
{
	status_t result = watch_node(node, flags, target);
	if (result == B_OK || result != B_NO_MEMORY) {
		// need to make sure this uses the same error value as
		// the node monitor code
		return result;
	}

	PRINT(("failed to start monitoring, trying to allocate more "
		"node monitors\n"));

	TTracker* tracker = dynamic_cast<TTracker*>(be_app);
	if (tracker == NULL) {
		// we are the file panel only, just fail
		return result;
	}

	result = tracker->NeedMoreNodeMonitors();

	if (result != B_OK) {
		PRINT(("failed to allocate more node monitors, %s\n",
			strerror(result)));
		return result;
	}

	// try again, this time with more node monitors
	return watch_node(node, flags, target);
}


BMessenger
TTracker::MountServer() const
{
	return BMessenger(kMountServerSignature);
}


bool
TTracker::TrashFull() const
{
	return fTrashWatcher->CheckTrashDirs();
}


bool
TTracker::IsTrashNode(const node_ref* node) const
{
	return fTrashWatcher->IsTrashNode(node);
}

bool
TTracker::InTrashNode(const entry_ref* ref) const
{
	return FSInTrashDir(ref);
}
