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