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