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