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