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