xref: /haiku/src/kits/tracker/Tracker.cpp (revision 4f2fd49bdc6078128b1391191e4edac647044c3d)
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 		return false;
246 
247 	gStatusWindow->AttemptToQuit();
248 		// try quitting the copy/move/empty trash threads
249 
250 	BVolume bootVolume;
251 	DEBUG_ONLY(status_t err =) BVolumeRoster().GetBootVolume(&bootVolume);
252 	ASSERT(err == B_OK);
253 	BMessage message;
254 	AutoLock<WindowList> lock(&fWindowList);
255 	// save open windows in a message inside an attribute of the desktop
256 	int32 count = fWindowList.CountItems();
257 	for (int32 i = 0; i < count; i++) {
258 		BContainerWindow *window = dynamic_cast<BContainerWindow *>
259 			(fWindowList.ItemAt(i));
260 
261 		if (window && window->Lock()) {
262 			if (window->TargetModel()
263 				&& !window->PoseView()->IsDesktopWindow()) {
264 				if (window->TargetModel()->IsRoot())
265 					message.AddBool("open_disks_window", true);
266 				else {
267 					BEntry entry;
268 					BPath path;
269 					const entry_ref *ref = window->TargetModel()->EntryRef();
270 					if (entry.SetTo(ref) == B_OK && entry.GetPath(&path) == B_OK) {
271 						int8 flags = window->IsMinimized() ? kOpenWindowMinimized : kOpenWindowNoFlags;
272 						uint32 deviceFlags = GetVolumeFlags(window->TargetModel());
273 
274 						// save state for every window which is
275 						//	a) already open on another workspace
276 						//	b) on a volume not capable of writing attributes
277 						if (window != FindContainerWindow(ref)
278 							|| (deviceFlags & (B_FS_HAS_ATTR | B_FS_IS_READONLY)) != B_FS_HAS_ATTR) {
279 							BMessage stateMessage;
280 							window->SaveState(stateMessage);
281 							window->SetSaveStateEnabled(false);
282 								// This is to prevent its state to be saved to the node when closed.
283 							message.AddMessage("window state", &stateMessage);
284 							flags |= kOpenWindowHasState;
285 						}
286 						const char *target;
287 						bool pathAlreadyExists = false;
288 						for (int32 index = 0;message.FindString("paths", index, &target) == B_OK;index++) {
289 							if (!strcmp(target,path.Path())) {
290 								pathAlreadyExists = true;
291 								break;
292 							}
293 						}
294 						if (!pathAlreadyExists)
295 							message.AddString("paths", path.Path());
296 						message.AddInt8(path.Path(), flags);
297 					}
298 				}
299 			}
300 			window->Unlock();
301 		}
302 	}
303 	lock.Unlock();
304 
305 	// write windows to open on disk
306 	BDirectory deskDir;
307 	if (!BootedInSafeMode() && FSGetDeskDir(&deskDir, bootVolume.Device()) == B_OK) {
308 		// if message is empty, delete the corresponding attribute
309 		if (message.CountNames(B_ANY_TYPE)) {
310 			size_t size = (size_t)message.FlattenedSize();
311 			char *buffer = new char[size];
312 			message.Flatten(buffer, (ssize_t)size);
313 			deskDir.WriteAttr(kAttrOpenWindows, B_MESSAGE_TYPE, 0, buffer, size);
314 			delete [] buffer;
315 		} else
316 			deskDir.RemoveAttr(kAttrOpenWindows);
317 	}
318 
319 	for (int32 count = 0; count < 50; count++) {
320 		// wait 5 seconds for the copiing/moving to quit
321 		if (gStatusWindow->AttemptToQuit())
322 			break;
323 
324 		snooze(100000);
325 	}
326 
327 	return _inherited::QuitRequested();
328 }
329 
330 
331 void
332 TTracker::Quit()
333 {
334 	TrackerSettings().SaveSettings(false);
335 
336 	fAutoMounter->Lock();
337 	fAutoMounter->QuitRequested();	// automounter does some stuff in QuitRequested
338 	fAutoMounter->Quit();			// but we really don't care if it is cooperating or not
339 
340 	fClipboardRefsWatcher->Lock();
341 	fClipboardRefsWatcher->Quit();
342 
343 	fTrashWatcher->Lock();
344 	fTrashWatcher->Quit();
345 
346 	WellKnowEntryList::Quit();
347 
348 	delete gPreloader;
349 	delete fTaskLoop;
350 	delete IconCache::sIconCache;
351 
352 	_inherited::Quit();
353 }
354 
355 
356 void
357 TTracker::MessageReceived(BMessage *message)
358 {
359 	if (HandleScriptingMessage(message))
360 		return;
361 
362 	switch (message->what) {
363 		case kOpenPreviouslyOpenWindows:
364 			if (!BootedInSafeMode())
365 				_OpenPreviouslyOpenedWindows();
366 			break;
367 
368 		case kGetInfo:
369 			OpenInfoWindows(message);
370 			break;
371 
372 		case kMoveToTrash:
373 			MoveRefsToTrash(message);
374 			break;
375 
376 		case kCloseWindowAndChildren:
377 			{
378 				const node_ref *itemNode;
379 				int32 bytes;
380 				message->FindData("node_ref", B_RAW_TYPE,
381 					(const void **)&itemNode, &bytes);
382 				CloseWindowAndChildren(itemNode);
383 				break;
384 			}
385 
386 		case kCloseAllWindows:
387 			CloseAllWindows();
388 			break;
389 
390 		case kFindButton:
391 			(new FindWindow())->Show();
392 			break;
393 
394 		case kEditQuery:
395 			EditQueries(message);
396 			break;
397 
398 		case kUnmountVolume:
399 			// When the user attempts to unmount a volume from the mount
400 			// context menu, this is where the message gets received.
401 			// Save pose locations and forward this to the automounter
402 			SaveAllPoseLocations();
403 			fAutoMounter->PostMessage(message);
404 			break;
405 
406 		case kRunAutomounterSettings:
407 			AutomountSettingsDialog::RunAutomountSettings(fAutoMounter);
408 			break;
409 
410 		case kVolumeMounted:
411 		{
412 			// This is sent to us by the AutoMounter whenever it mounts
413 			// a new volume - we use it to restore the previously opened
414 			// windows from that volume in case it has been mounted
415 			// during the AutoMounter's initial rescan
416 			const char* path;
417 			bool initial;
418 			if (message->FindBool("initial rescan", &initial) != B_OK
419 				|| message->FindString("path", &path) != B_OK
420 				|| !initial)
421 				break;
422 
423 			_OpenPreviouslyOpenedWindows(path);
424 			break;
425 		}
426 
427 		case kShowSplash:
428 			run_be_about();
429 			break;
430 
431 		case kAddPrinter:
432 			// show the addprinter window
433 			run_add_printer_panel();
434 			break;
435 
436 		case kMakeActivePrinter:
437 			// get the current selection
438 			SetDefaultPrinter(message);
439 			break;
440 
441 #ifdef MOUNT_MENU_IN_DESKBAR
442 
443 		case 'gmtv':
444 			{
445 				// Someone (probably the deskbar) has requested a list of
446 				// mountable volumes.
447 				BMessage reply;
448 				AutoMounterLoop()->EachMountableItemAndFloppy(&AddMountableItemToMessage,
449 				  &reply);
450 				message->SendReply(&reply);
451 				break;
452 			}
453 
454 #endif
455 
456 		case kMountVolume:
457 		case kMountAllNow:
458 			AutoMounterLoop()->PostMessage(message);
459 			break;
460 
461 
462 		case kRestoreBackgroundImage:
463 			{
464 				BDeskWindow *desktop = GetDeskWindow();
465 				AutoLock<BWindow> lock(desktop);
466 				desktop->UpdateDesktopBackgroundImages();
467 			}
468 			break;
469 
470  		case kShowSettingsWindow:
471  			ShowSettingsWindow();
472  			break;
473 
474 		case kFavoriteCountChangedExternally:
475 			SendNotices(kFavoriteCountChangedExternally, message);
476 			break;
477 
478 		case kStartWatchClipboardRefs:
479 		{
480 			BMessenger messenger;
481 			message->FindMessenger("target", &messenger);
482 			if (messenger.IsValid())
483 				fClipboardRefsWatcher->AddToNotifyList(messenger);
484 			break;
485 		}
486 
487 		case kStopWatchClipboardRefs:
488 		{
489 			BMessenger messenger;
490 			message->FindMessenger("target", &messenger);
491 			if (messenger.IsValid())
492 				fClipboardRefsWatcher->RemoveFromNotifyList(messenger);
493 			break;
494 		}
495 
496 		case kFSClipboardChanges:
497 		{
498 			fClipboardRefsWatcher->UpdatePoseViews(message);
499 			break;
500 		}
501 
502 		case kShowVolumeSpaceBar:
503 		case kSpaceBarColorChanged: {
504 			gPeriodicUpdatePoses.DoPeriodicUpdate(true);
505 			break;
506 		}
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 			(new BAlert("", "There was an error resolving the link.",
669 				"Cancel", 0, 0,
670 				B_WIDTH_AS_USUAL, B_WARNING_ALERT))->Go();
671 			return result;
672 		}
673 	} else
674 		model = new Model(&entry);
675 
676 	result = model->InitCheck();
677 	if (result != B_OK) {
678 		delete model;
679 		return result;
680 	}
681 
682 	bool openAsContainer = model->IsContainer();
683 
684 	if (openAsContainer && selector != kOpenWith) {
685 		// if folder or query has a preferred handler and it's not the
686 		// Tracker, open it by sending refs to the handling app
687 
688 		// if we are responding to the final open of OpenWith, just
689 		// skip this and proceed to opening the container with Tracker
690 		model->OpenNode();
691 		BNodeInfo nodeInfo(model->Node());
692 		char preferredApp[B_MIME_TYPE_LENGTH];
693 		if (nodeInfo.GetPreferredApp(preferredApp) == B_OK
694 			&& strcasecmp(preferredApp, kTrackerSignature) != 0)
695 			openAsContainer = false;
696 		model->CloseNode();
697 	}
698 
699 	if (openAsContainer || selector == kRunOpenWithWindow) {
700 		// special case opening plain folders, queries or using open with
701 		OpenContainerWindow(model, 0, selector, kRestoreDecor);	// window adopts model
702 		if (nodeToClose)
703 			CloseParentWaitingForChildSoon(ref, nodeToClose);
704 	} else if (model->IsQueryTemplate()) {
705 		// query template - open new find window
706 		(new FindWindow(model->EntryRef()))->Show();
707 		if (nodeToClose)
708 			CloseParentWaitingForChildSoon(ref, nodeToClose);
709 	} else {
710 		delete model;
711 		// run Launch in a separate thread
712 		// and close parent if successfull
713 		if (nodeToClose)
714 			Thread::Launch(new EntryAndNodeDoSoonWithMessageFunctor<TTracker,
715 				bool (TTracker::*)(const entry_ref *, const node_ref *,
716 				const BMessage *)>(&TTracker::LaunchAndCloseParentIfOK, this,
717 				ref, nodeToClose, messageToBundle));
718 		else {
719 			BMessage refsReceived(B_REFS_RECEIVED);
720 			if (messageToBundle) {
721 				refsReceived = *messageToBundle;
722 				refsReceived.what = B_REFS_RECEIVED;
723 			}
724 			refsReceived.AddRef("refs", ref);
725 			if (brokenLinkWithSpecificHandler)
726 				// This cruft is to support a hacky workaround for double-clicking
727 				// broken refs for cifs; should get fixed in R5
728 				LaunchBrokenLink(brokenLinkPreferredApp.String(), &refsReceived);
729 			else
730 				TrackerLaunch(&refsReceived, true);
731 		}
732 	}
733 	if (nodeToSelect)
734 		SelectChildInParentSoon(ref, nodeToSelect);
735 
736 	return B_OK;
737 }
738 
739 
740 void
741 TTracker::RefsReceived(BMessage *message)
742 {
743 	OpenSelector selector = kOpen;
744 	if (message->HasInt32("launchUsingSelector"))
745 		selector = kRunOpenWithWindow;
746 
747 	entry_ref handlingApp;
748 	if (message->FindRef("handler", &handlingApp) == B_OK)
749 		selector = kOpenWith;
750 
751 	int32 count;
752 	uint32 type;
753 	message->GetInfo("refs", &type, &count);
754 
755 	switch (selector) {
756 		case kRunOpenWithWindow:
757 			OpenContainerWindow(0, message, selector);
758 				// window adopts model
759 			break;
760 
761 		case kOpenWith:
762 			{
763 				// Open With resulted in passing refs and a handler, open the files
764 				// with the handling app
765 				message->RemoveName("handler");
766 
767 				// have to find out if handling app is the Tracker
768 				// if it is, just pass it to the active Tracker, no matter which Tracker
769 				// was chosen to handle the refs
770 				char signature[B_MIME_TYPE_LENGTH];
771 				signature[0] = '\0';
772 				{
773 					BFile handlingNode(&handlingApp, O_RDONLY);
774 					BAppFileInfo appInfo(&handlingNode);
775 					appInfo.GetSignature(signature);
776 				}
777 
778 				if (strcasecmp(signature, kTrackerSignature) != 0) {
779 					// handling app not Tracker, pass entries to the apps RefsReceived
780 					TrackerLaunch(&handlingApp, message, true);
781 					break;
782 				}
783 				// fall thru, opening refs by the Tracker, as if they were double clicked
784 			}
785 
786 		case kOpen:
787 			{
788 				// copy over "Poses" messenger so that refs received recipients know
789 				// where the open came from
790 				BMessage *bundleThis = NULL;
791 				BMessenger messenger;
792 				if (message->FindMessenger("TrackerViewToken", &messenger) == B_OK) {
793 					bundleThis = new BMessage();
794 					bundleThis->AddMessenger("TrackerViewToken", messenger);
795 				}
796 
797 				for (int32 index = 0; index < count; index++) {
798 					entry_ref ref;
799 					message->FindRef("refs", index, &ref);
800 
801 					const node_ref *nodeToClose = NULL;
802 					const node_ref *nodeToSelect = NULL;
803 					ssize_t numBytes;
804 
805 					message->FindData("nodeRefsToClose", B_RAW_TYPE, index,
806 						(const void **)&nodeToClose, &numBytes);
807 					message->FindData("nodeRefToSelect", B_RAW_TYPE, index,
808 						(const void **)&nodeToSelect, &numBytes);
809 
810 					OpenRef(&ref, nodeToClose, nodeToSelect, selector, bundleThis);
811 				}
812 
813 				delete bundleThis;
814 				break;
815 			}
816 	}
817 }
818 
819 
820 void
821 TTracker::ArgvReceived(int32 argc, char **argv)
822 {
823 	BMessage *message = CurrentMessage();
824 	const char *currentWorkingDirectoryPath = NULL;
825 	entry_ref ref;
826 
827 	if (message->FindString("cwd", &currentWorkingDirectoryPath) == B_OK) {
828 		BDirectory workingDirectory(currentWorkingDirectoryPath);
829 		for (int32 index = 1; index < argc; index++) {
830 			BEntry entry;
831 			if (entry.SetTo(&workingDirectory, argv[index]) == B_OK
832 				&& entry.GetRef(&ref) == B_OK)
833 				OpenRef(&ref);
834 			else if (get_ref_for_path(argv[index], &ref) == B_OK)
835 				OpenRef(&ref);
836 		}
837 	}
838 }
839 
840 
841 void
842 TTracker::OpenContainerWindow(Model *model, BMessage *originalRefsList,
843 	OpenSelector openSelector, uint32 openFlags, bool checkAlreadyOpen,
844 	const BMessage *stateMessage)
845 {
846 	AutoLock<WindowList> lock(&fWindowList);
847 	BContainerWindow *window = NULL;
848 	if (checkAlreadyOpen && openSelector != kRunOpenWithWindow)
849 		// find out if window already open
850 		window = FindContainerWindow(model->NodeRef());
851 
852 	bool someWindowActivated = false;
853 
854 	uint32 workspace = (uint32)(1 << current_workspace());
855 	int32 windowCount = 0;
856 
857 	while (window) {
858 		// At least one window open, just pull to front
859 		// make sure we don't jerk workspaces around
860 		uint32 windowWorkspaces = window->Workspaces();
861 		if (windowWorkspaces & workspace) {
862 			window->Activate();
863 			someWindowActivated = true;
864 		}
865 		window = FindContainerWindow(model->NodeRef(), ++windowCount);
866 	}
867 
868 	if (someWindowActivated) {
869 		delete model;
870 		return;
871 	} // If no window was actiated, (none in the current workspace
872 	  // we open a new one.
873 
874 	if (openSelector == kRunOpenWithWindow) {
875 		BMessage *refList = NULL;
876 		if (!originalRefsList) {
877 			// when passing just a single model, stuff it's entry in a single
878 			// element list anyway
879 			ASSERT(model);
880 			refList = new BMessage;
881 			refList->AddRef("refs", model->EntryRef());
882 			delete model;
883 			model = NULL;
884 		} else
885 			// clone the message, window adopts it for it's own use
886 			refList = new BMessage(*originalRefsList);
887 		window = new OpenWithContainerWindow(refList, &fWindowList);
888 	} else if (model->IsRoot()) {
889 		// window will adopt the model
890 		window = new BVolumeWindow(&fWindowList, openFlags);
891 	} else if (model->IsQuery()) {
892 		// window will adopt the model
893 		window = new BQueryContainerWindow(&fWindowList, openFlags);
894 	} else
895 		// window will adopt the model
896 		window = new BContainerWindow(&fWindowList, openFlags);
897 
898 	if (model)
899 		window->CreatePoseView(model);
900 
901 	BMessage restoreStateMessage(kRestoreState);
902 
903 	if (stateMessage)
904 		restoreStateMessage.AddMessage("state", stateMessage);
905 
906 	window->PostMessage(&restoreStateMessage);
907 }
908 
909 
910 void
911 TTracker::EditQueries(const BMessage *message)
912 {
913 	bool editOnlyIfTemplate;
914 	if (message->FindBool("editQueryOnPose", &editOnlyIfTemplate) != B_OK)
915 		editOnlyIfTemplate = false;
916 
917 	type_code type;
918 	int32 count;
919 	message->GetInfo("refs", &type, &count);
920 	for (int32 index = 0; index < count; index++) {
921 		entry_ref ref;
922 		message->FindRef("refs", index, &ref);
923 		BEntry entry(&ref, true);
924 		if (entry.InitCheck() == B_OK && entry.Exists())
925 			(new FindWindow(&ref, editOnlyIfTemplate))->Show();
926 	}
927 }
928 
929 
930 void
931 TTracker::OpenInfoWindows(BMessage *message)
932 {
933 	type_code type;
934 	int32 count;
935 	message->GetInfo("refs", &type, &count);
936 
937 	for (int32 index = 0; index < count; index++) {
938 		entry_ref ref;
939 		message->FindRef("refs", index, &ref);
940 		BEntry entry;
941 		if (entry.SetTo(&ref) == B_OK) {
942 			Model *model = new Model(&entry);
943 			if (model->InitCheck() != B_OK) {
944 				delete model;
945 				continue;
946 			}
947 
948 			AutoLock<WindowList> lock(&fWindowList);
949 			BInfoWindow *wind = FindInfoWindow(model->NodeRef());
950 
951 			if (wind) {
952 				wind->Activate();
953 				delete model;
954 			} else {
955 				wind = new BInfoWindow(model, index, &fWindowList);
956 				wind->PostMessage(kRestoreState);
957 			}
958 		}
959 	}
960 }
961 
962 
963 BDeskWindow *
964 TTracker::GetDeskWindow() const
965 {
966 	int32 count = fWindowList.CountItems();
967 	for (int32 index = 0; index < count; index++) {
968 		BDeskWindow *window = dynamic_cast<BDeskWindow *>
969 			(fWindowList.ItemAt(index));
970 
971 		if (window)
972 			return window;
973 	}
974 	TRESPASS();
975 	return NULL;
976 }
977 
978 
979 BContainerWindow *
980 TTracker::FindContainerWindow(const node_ref *node, int32 number) const
981 {
982 	ASSERT(fWindowList.IsLocked());
983 
984 	int32 count = fWindowList.CountItems();
985 
986 	int32 windowsFound = 0;
987 
988 	for (int32 index = 0; index < count; index++) {
989 		BContainerWindow *window = dynamic_cast<BContainerWindow *>
990 			(fWindowList.ItemAt(index));
991 
992 		if (window && window->IsShowing(node) && number == windowsFound++)
993 			return window;
994 	}
995 	return NULL;
996 }
997 
998 
999 BContainerWindow *
1000 TTracker::FindContainerWindow(const entry_ref *entry, int32 number) const
1001 {
1002 	ASSERT(fWindowList.IsLocked());
1003 
1004 	int32 count = fWindowList.CountItems();
1005 
1006 	int32 windowsFound = 0;
1007 
1008 	for (int32 index = 0; index < count; index++) {
1009 		BContainerWindow *window = dynamic_cast<BContainerWindow *>
1010 			(fWindowList.ItemAt(index));
1011 
1012 		if (window && window->IsShowing(entry) && number == windowsFound++)
1013 			return window;
1014 	}
1015 	return NULL;
1016 }
1017 
1018 
1019 bool
1020 TTracker::EntryHasWindowOpen(const entry_ref *entry)
1021 {
1022 	AutoLock<WindowList> lock(&fWindowList);
1023 	return FindContainerWindow(entry) != NULL;
1024 }
1025 
1026 
1027 BContainerWindow *
1028 TTracker::FindParentContainerWindow(const entry_ref *ref) const
1029 {
1030 	BEntry entry(ref);
1031 	BEntry parent;
1032 
1033 	if (entry.GetParent(&parent) != B_OK)
1034 		return NULL;
1035 
1036 	entry_ref parentRef;
1037 	parent.GetRef(&parentRef);
1038 
1039 	ASSERT(fWindowList.IsLocked());
1040 
1041 	int32 count = fWindowList.CountItems();
1042 	for (int32 index = 0; index < count; index++) {
1043 		BContainerWindow *window = dynamic_cast<BContainerWindow *>
1044 			(fWindowList.ItemAt(index));
1045 		if (window && window->IsShowing(&parentRef))
1046 			return window;
1047 	}
1048 	return NULL;
1049 }
1050 
1051 
1052 BInfoWindow *
1053 TTracker::FindInfoWindow(const node_ref* node) const
1054 {
1055 	ASSERT(fWindowList.IsLocked());
1056 
1057 	int32 count = fWindowList.CountItems();
1058 	for (int32 index = 0; index < count; index++) {
1059 		BInfoWindow *window = dynamic_cast<BInfoWindow *>
1060 			(fWindowList.ItemAt(index));
1061 		if (window && window->IsShowing(node))
1062 			return window;
1063 	}
1064 	return NULL;
1065 }
1066 
1067 
1068 bool
1069 TTracker::QueryActiveForDevice(dev_t device)
1070 {
1071 	AutoLock<WindowList> lock(&fWindowList);
1072 	int32 count = fWindowList.CountItems();
1073 	for (int32 index = 0; index < count; index++) {
1074 		BQueryContainerWindow *window = dynamic_cast<BQueryContainerWindow *>
1075 			(fWindowList.ItemAt(index));
1076 		if (window) {
1077 			AutoLock<BWindow> lock(window);
1078 			if (window->ActiveOnDevice(device))
1079 				return true;
1080 		}
1081 	}
1082 	return false;
1083 }
1084 
1085 
1086 void
1087 TTracker::CloseActiveQueryWindows(dev_t device)
1088 {
1089 	// used when trying to unmount a volume - an active query would prevent that from
1090 	// happening
1091 	bool closed = false;
1092 	AutoLock<WindowList> lock(fWindowList);
1093 	for (int32 index = fWindowList.CountItems(); index >= 0; index--) {
1094 		BQueryContainerWindow *window = dynamic_cast<BQueryContainerWindow *>
1095 			(fWindowList.ItemAt(index));
1096 		if (window) {
1097 			AutoLock<BWindow> lock(window);
1098 			if (window->ActiveOnDevice(device)) {
1099 				window->PostMessage(B_QUIT_REQUESTED);
1100 				closed = true;
1101 			}
1102 		}
1103 	}
1104 	lock.Unlock();
1105 	if (closed)
1106 		for (int32 timeout = 30; timeout; timeout--) {
1107 			// wait a bit for windows to fully close
1108 			if (!QueryActiveForDevice(device))
1109 				return;
1110 			snooze(100000);
1111 		}
1112 }
1113 
1114 
1115 void
1116 TTracker::SaveAllPoseLocations()
1117 {
1118 	int32 numWindows = fWindowList.CountItems();
1119 	for (int32 windowIndex = 0; windowIndex < numWindows; windowIndex++) {
1120 		BContainerWindow *window = dynamic_cast<BContainerWindow *>
1121 			(fWindowList.ItemAt(windowIndex));
1122 
1123 		if (window) {
1124 			AutoLock<BWindow> lock(window);
1125 			BDeskWindow *deskWindow = dynamic_cast<BDeskWindow *>(window);
1126 
1127 			if (deskWindow)
1128 				deskWindow->SaveDesktopPoseLocations();
1129 			else
1130 				window->PoseView()->SavePoseLocations();
1131 		}
1132 	}
1133 }
1134 
1135 
1136 void
1137 TTracker::CloseWindowAndChildren(const node_ref *node)
1138 {
1139 	BDirectory dir(node);
1140 	if (dir.InitCheck() != B_OK)
1141 		return;
1142 
1143 	AutoLock<WindowList> lock(&fWindowList);
1144 	BObjectList<BContainerWindow> closeList;
1145 
1146 	// make a list of all windows to be closed
1147 	// count from end to beginning so we can remove items safely
1148 	for (int32 index = fWindowList.CountItems() - 1; index >= 0; index--) {
1149 		BContainerWindow *window = dynamic_cast<BContainerWindow *>
1150 			(fWindowList.ItemAt(index));
1151 		if (window && window->TargetModel()) {
1152 			BEntry wind_entry;
1153 			wind_entry.SetTo(window->TargetModel()->EntryRef());
1154 
1155 			if ((*window->TargetModel()->NodeRef() == *node)
1156 				|| dir.Contains(&wind_entry)) {
1157 
1158 				// ToDo:
1159 				// get rid of the Remove here, BContainerWindow::Quit does it
1160 				fWindowList.RemoveItemAt(index);
1161 				closeList.AddItem(window);
1162 			}
1163 		}
1164 	}
1165 
1166 	// now really close the windows
1167 	int32 numItems = closeList.CountItems();
1168 	for (int32 index = 0; index < numItems; index++) {
1169 		BContainerWindow *window = closeList.ItemAt(index);
1170 		window->PostMessage(B_QUIT_REQUESTED);
1171 	}
1172 }
1173 
1174 
1175 void
1176 TTracker::CloseAllWindows()
1177 {
1178 	// this is a response to the DeskBar sending us a B_QUIT, when it really
1179 	// means to say close all your windows. It might be better to have it
1180 	// send a kCloseAllWindows message and have windowless apps stay running,
1181 	// which is what we will do for the Tracker
1182 	AutoLock<WindowList> lock(&fWindowList);
1183 
1184 	int32 count = CountWindows();
1185 	for (int32 index = 0; index < count; index++) {
1186 		BWindow *window = WindowAt(index);
1187 		// avoid the desktop
1188 		if (!dynamic_cast<BDeskWindow *>(window)
1189 			&& !dynamic_cast<BStatusWindow *>(window))
1190 			window->PostMessage(B_QUIT_REQUESTED);
1191 	}
1192 	// count from end to beginning so we can remove items safely
1193 	for (int32 index = fWindowList.CountItems() - 1; index >= 0; index--) {
1194 		BWindow *window = fWindowList.ItemAt(index);
1195 		if (!dynamic_cast<BDeskWindow *>(window)
1196 			&& !dynamic_cast<BStatusWindow *>(window))
1197 				// ToDo:
1198 				// get rid of the Remove here, BContainerWindow::Quit does it
1199 			fWindowList.RemoveItemAt(index);
1200 	}
1201 }
1202 
1203 
1204 void
1205 TTracker::_OpenPreviouslyOpenedWindows(const char* pathFilter)
1206 {
1207 	size_t filterLength = 0;
1208 	if (pathFilter != NULL)
1209 		filterLength = strlen(pathFilter);
1210 
1211 	BVolume	bootVolume;
1212 	BVolumeRoster().GetBootVolume(&bootVolume);
1213 	BDirectory deskDir;
1214 	attr_info attrInfo;
1215 	if (FSGetDeskDir(&deskDir, bootVolume.Device()) != B_OK
1216 		|| deskDir.GetAttrInfo(kAttrOpenWindows, &attrInfo) != B_OK)
1217 		return;
1218 
1219 	char *buffer = (char *)malloc((size_t)attrInfo.size);
1220 	BMessage message;
1221 	if (deskDir.ReadAttr(kAttrOpenWindows, B_MESSAGE_TYPE, 0, buffer,
1222 		(size_t)attrInfo.size) != attrInfo.size
1223 		|| message.Unflatten(buffer) != B_OK) {
1224 		free(buffer);
1225 		return;
1226 	}
1227 
1228 	free(buffer);
1229 
1230 	node_ref nodeRef;
1231 	deskDir.GetNodeRef(&nodeRef);
1232 
1233 	int32 stateMessageCounter = 0;
1234 	const char *path;
1235 	for (int32 i = 0; message.FindString("paths", i, &path) == B_OK; i++) {
1236 		if (strncmp(path, pathFilter, filterLength))
1237 			continue;
1238 
1239 		BEntry entry(path, true);
1240 		if (entry.InitCheck() != B_OK)
1241 			continue;
1242 
1243 		int8 flags = 0;
1244 		for (int32 j = 0; message.FindInt8(path, j, &flags) == B_OK; j++) {
1245 			Model *model = new Model(&entry);
1246 			if (model->InitCheck() == B_OK && model->IsContainer()) {
1247 				BMessage state;
1248 				bool restoreStateFromMessage = false;
1249 				if ((flags & kOpenWindowHasState) != 0
1250 					&& message.FindMessage("window state", stateMessageCounter++,
1251 							&state) == B_OK)
1252 					restoreStateFromMessage = true;
1253 
1254 				if (restoreStateFromMessage) {
1255 					OpenContainerWindow(model, 0, kOpen, kRestoreWorkspace
1256 						| (flags & kOpenWindowMinimized ? kIsHidden : 0U)
1257 						| kRestoreDecor, false, &state);
1258 				} else {
1259 					OpenContainerWindow(model, 0, kOpen, kRestoreWorkspace
1260 						| (flags & kOpenWindowMinimized ? kIsHidden : 0U)
1261 						| kRestoreDecor);
1262 				}
1263 			} else
1264 				delete model;
1265 		}
1266 	}
1267 
1268 	// Open disks window if needed
1269 
1270 	if (pathFilter == NULL && TrackerSettings().ShowDisksIcon()
1271 		&& message.HasBool("open_disks_window")) {
1272 		BEntry entry("/");
1273 		Model* model = new Model(&entry);
1274 		if (model->InitCheck() == B_OK)
1275 			OpenContainerWindow(model, 0, kOpen, kRestoreWorkspace);
1276 		else
1277 			delete model;
1278 	}
1279 }
1280 
1281 
1282 void
1283 TTracker::ReadyToRun()
1284 {
1285 	gStatusWindow = new BStatusWindow();
1286 	InitMimeTypes();
1287 	InstallDefaultTemplates();
1288 	InstallIndices();
1289 	InstallTemporaryBackgroundImages();
1290 
1291 	HideVarDir();
1292 
1293 	fTrashWatcher = new BTrashWatcher();
1294 	fTrashWatcher->Run();
1295 
1296 	fClipboardRefsWatcher = new BClipboardRefsWatcher();
1297 	fClipboardRefsWatcher->Run();
1298 
1299 	fAutoMounter = new AutoMounter();
1300 	fAutoMounter->Run();
1301 
1302 	fTaskLoop = new StandAloneTaskLoop(true);
1303 
1304 	// open desktop window
1305 	BContainerWindow *deskWindow = NULL;
1306 	BVolume	bootVolume;
1307 	BVolumeRoster().GetBootVolume(&bootVolume);
1308 	BDirectory deskDir;
1309 	if (FSGetDeskDir(&deskDir, bootVolume.Device()) == B_OK) {
1310 		// create desktop
1311 		BEntry entry;
1312 		deskDir.GetEntry(&entry);
1313 		Model *model = new Model(&entry);
1314 		if (model->InitCheck() == B_OK) {
1315 			AutoLock<WindowList> lock(&fWindowList);
1316 			deskWindow = new BDeskWindow(&fWindowList);
1317 			AutoLock<BWindow> windowLock(deskWindow);
1318 			deskWindow->CreatePoseView(model);
1319 			deskWindow->Init();
1320 
1321 			if (TrackerSettings().ShowDisksIcon()) {
1322 				// create model for root of everything
1323 				BEntry entry("/");
1324 				Model model(&entry);
1325 				if (model.InitCheck() == B_OK) {
1326 					// add the root icon to desktop window
1327 					BMessage message;
1328 					message.what = B_NODE_MONITOR;
1329 					message.AddInt32("opcode", B_ENTRY_CREATED);
1330 					message.AddInt32("device", model.NodeRef()->device);
1331 					message.AddInt64("node", model.NodeRef()->node);
1332 					message.AddInt64("directory", model.EntryRef()->directory);
1333 					message.AddString("name", model.EntryRef()->name);
1334 					deskWindow->PostMessage(&message, deskWindow->PoseView());
1335 				}
1336 			}
1337 		} else
1338 			delete model;
1339 	}
1340 
1341 	// kick off building the mime type list for find panels, etc.
1342 	fMimeTypeList = new MimeTypeList();
1343 
1344 	if (!BootedInSafeMode()) {
1345 		// kick of transient query killer
1346 		DeleteTransientQueriesTask::StartUpTransientQueryCleaner();
1347 	}
1348 }
1349 
1350 MimeTypeList *
1351 TTracker::MimeTypes() const
1352 {
1353 	return fMimeTypeList;
1354 }
1355 
1356 void
1357 TTracker::SelectChildInParentSoon(const entry_ref *parent,
1358 	const node_ref *child)
1359 {
1360 	fTaskLoop->RunLater(NewMemberFunctionObjectWithResult
1361 		(&TTracker::SelectChildInParent, this, parent, child),
1362 		100000, 200000, 5000000);
1363 }
1364 
1365 void
1366 TTracker::CloseParentWaitingForChildSoon(const entry_ref *child,
1367 	const node_ref *parent)
1368 {
1369 	fTaskLoop->RunLater(NewMemberFunctionObjectWithResult
1370 		(&TTracker::CloseParentWaitingForChild, this, child, parent),
1371 		200000, 100000, 5000000);
1372 }
1373 
1374 void
1375 TTracker::SelectPoseAtLocationSoon(node_ref parent, BPoint pointInPose)
1376 {
1377 	fTaskLoop->RunLater(NewMemberFunctionObject
1378 		(&TTracker::SelectPoseAtLocationInParent, this, parent, pointInPose),
1379 		100000);
1380 }
1381 
1382 void
1383 TTracker::SelectPoseAtLocationInParent(node_ref parent, BPoint pointInPose)
1384 {
1385 	AutoLock<WindowList> lock(&fWindowList);
1386 	BContainerWindow *parentWindow = FindContainerWindow(&parent);
1387 	if (parentWindow) {
1388 		AutoLock<BWindow> lock(parentWindow);
1389 		parentWindow->PoseView()->SelectPoseAtLocation(pointInPose);
1390 	}
1391 }
1392 
1393 bool
1394 TTracker::CloseParentWaitingForChild(const entry_ref *child,
1395 	const node_ref *parent)
1396 {
1397 	AutoLock<WindowList> lock(&fWindowList);
1398 
1399 	BContainerWindow *parentWindow = FindContainerWindow(parent);
1400 	if (!parentWindow)
1401 		// parent window already closed, give up
1402 		return true;
1403 
1404 	// If child is a symbolic link, dereference it, so that
1405 	// FindContainerWindow will succeed.
1406 	BEntry entry(child, true);
1407 	entry_ref resolvedChild;
1408 	if (entry.GetRef(&resolvedChild) != B_OK)
1409 		resolvedChild = *child;
1410 
1411 	BContainerWindow *window = FindContainerWindow(&resolvedChild);
1412 	if (window) {
1413 		AutoLock<BWindow> lock(window);
1414 		if (!window->IsHidden())
1415 			return CloseParentWindowCommon(parentWindow);
1416 	}
1417 	return false;
1418 }
1419 
1420 void
1421 TTracker::CloseParent(node_ref parent)
1422 {
1423 	AutoLock<WindowList> lock(&fWindowList);
1424 	if (!lock)
1425 		return;
1426 
1427 	CloseParentWindowCommon(FindContainerWindow(&parent));
1428 }
1429 
1430 void
1431 TTracker::ShowSettingsWindow()
1432 {
1433 	if (!fSettingsWindow) {
1434 		fSettingsWindow = new TrackerSettingsWindow();
1435 		fSettingsWindow->Show();
1436 	} else {
1437 		if (fSettingsWindow->Lock()) {
1438 			if (fSettingsWindow->IsHidden())
1439 				fSettingsWindow->Show();
1440 			else
1441 				fSettingsWindow->Activate();
1442 			fSettingsWindow->Unlock();
1443 		}
1444 	}
1445 }
1446 
1447 bool
1448 TTracker::CloseParentWindowCommon(BContainerWindow *window)
1449 {
1450 	ASSERT(fWindowList.IsLocked());
1451 
1452 	if (dynamic_cast<BDeskWindow *>(window))
1453 		// don't close the destop
1454 		return false;
1455 
1456 	window->PostMessage(B_QUIT_REQUESTED);
1457 	return true;
1458 }
1459 
1460 bool
1461 TTracker::SelectChildInParent(const entry_ref *parent, const node_ref *child)
1462 {
1463 	AutoLock<WindowList> lock(&fWindowList);
1464 
1465 	BContainerWindow *window = FindContainerWindow(parent);
1466 	if (!window)
1467 		// parent window already closed, give up
1468 		return false;
1469 
1470 	AutoLock<BWindow> windowLock(window);
1471 
1472 	if (windowLock.IsLocked()) {
1473 		BPoseView *view = window->PoseView();
1474 		int32 index;
1475 		BPose *pose = view->FindPose(child, &index);
1476 		if (pose) {
1477 			view->SelectPose(pose, index);
1478 			return true;
1479 		}
1480 	}
1481 	return false;
1482 }
1483 
1484 const int32 kNodeMonitorBumpValue = 512;
1485 
1486 status_t
1487 TTracker::NeedMoreNodeMonitors()
1488 {
1489 	fNodeMonitorCount += kNodeMonitorBumpValue;
1490 	PRINT(("bumping nodeMonitorCount to %d\n", fNodeMonitorCount));
1491 
1492 	return _kset_mon_limit_(fNodeMonitorCount);
1493 }
1494 
1495 status_t
1496 TTracker::WatchNode(const node_ref *node, uint32 flags,
1497 	BMessenger target)
1498 {
1499 	status_t result = watch_node(node, flags, target);
1500 	if (result == B_OK || result != B_NO_MEMORY) {
1501 		// need to make sure this uses the same error value as
1502 		// the node monitor code
1503 		return result;
1504 	}
1505 
1506 	PRINT(("failed to start monitoring, trying to allocate more "
1507 		"node monitors\n"));
1508 
1509 	TTracker *tracker = dynamic_cast<TTracker *>(be_app);
1510 	if (!tracker) {
1511 		// we are the file panel only, just fail
1512 		return result;
1513 	}
1514 
1515 	result = tracker->NeedMoreNodeMonitors();
1516 
1517 	if (result != B_OK) {
1518 		PRINT(("failed to allocate more node monitors, %s\n",
1519 			strerror(result)));
1520 		return result;
1521 	}
1522 
1523 	// try again, this time with more node monitors
1524 	return watch_node(node, flags, target);
1525 }
1526 
1527 
1528 AutoMounter *
1529 TTracker::AutoMounterLoop()
1530 {
1531 	return fAutoMounter;
1532 }
1533 
1534 
1535 bool
1536 TTracker::InTrashNode(const entry_ref *node) const
1537 {
1538 	return FSInTrashDir(node);
1539 }
1540 
1541 
1542 bool
1543 TTracker::TrashFull() const
1544 {
1545 	return fTrashWatcher->CheckTrashDirs();
1546 }
1547 
1548 
1549 bool
1550 TTracker::IsTrashNode(const node_ref *node) const
1551 {
1552 	return fTrashWatcher->IsTrashNode(node);
1553 }
1554 
1555