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