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