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