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