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