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