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