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