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