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