xref: /haiku/src/kits/tracker/Tracker.cpp (revision d12bb8b14803d030b4a8fba91131e4bb96c4f406)
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 	if (result != B_OK) {
821 		BAlert* alert = new BAlert("",
822 			B_TRANSLATE("There was an error resolving the link."),
823 			B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL,
824 				B_WARNING_ALERT);
825 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
826 		alert->Go();
827 
828 		return result;
829 	} else
830 		model = new Model(&entry);
831 
832 	result = model->InitCheck();
833 	if (result != B_OK) {
834 		delete model;
835 		return result;
836 	}
837 
838 	bool openAsContainer = model->IsContainer();
839 
840 	if (openAsContainer && selector != kOpenWith) {
841 		// if folder or query has a preferred handler and it's not the
842 		// Tracker, open it by sending refs to the handling app
843 
844 		// if we are responding to the final open of OpenWith, just
845 		// skip this and proceed to opening the container with Tracker
846 		model->OpenNode();
847 		BNodeInfo nodeInfo(model->Node());
848 		char preferredApp[B_MIME_TYPE_LENGTH];
849 		if (nodeInfo.GetPreferredApp(preferredApp) == B_OK
850 			&& strcasecmp(preferredApp, kTrackerSignature) != 0) {
851 			openAsContainer = false;
852 		}
853 		model->CloseNode();
854 	}
855 
856 	if (openAsContainer || selector == kRunOpenWithWindow) {
857 		// special case opening plain folders, queries or using open with
858 		OpenContainerWindow(model, NULL, selector, kRestoreDecor);
859 			// window adopts model
860 		if (nodeToClose)
861 			CloseParentWaitingForChildSoon(ref, nodeToClose);
862 	} else if (model->IsQueryTemplate()) {
863 		// query template - open new find window
864 		(new FindWindow(model->EntryRef()))->Show();
865 
866 		delete model;
867 		if (nodeToClose)
868 			CloseParentWaitingForChildSoon(ref, nodeToClose);
869 	} else {
870 		delete model;
871 		// run Launch in a separate thread and close parent if successful
872 		if (nodeToClose) {
873 			Thread::Launch(new EntryAndNodeDoSoonWithMessageFunctor<TTracker,
874 				bool (TTracker::*)(const entry_ref*, const node_ref*,
875 				const BMessage*)>(&TTracker::LaunchAndCloseParentIfOK, this,
876 				ref, nodeToClose, messageToBundle));
877 		} else {
878 			BMessage refsReceived(B_REFS_RECEIVED);
879 			if (messageToBundle) {
880 				refsReceived = *messageToBundle;
881 				refsReceived.what = B_REFS_RECEIVED;
882 			}
883 			refsReceived.AddRef("refs", ref);
884 			TrackerLaunch(&refsReceived, true);
885 		}
886 	}
887 
888 	if (nodeToSelect)
889 		SelectChildInParentSoon(ref, nodeToSelect);
890 
891 	return B_OK;
892 }
893 
894 
895 void
896 TTracker::RefsReceived(BMessage* message)
897 {
898 	OpenSelector selector = kOpen;
899 	if (message->HasInt32("launchUsingSelector"))
900 		selector = kRunOpenWithWindow;
901 
902 	entry_ref handlingApp;
903 	if (message->FindRef("handler", &handlingApp) == B_OK)
904 		selector = kOpenWith;
905 
906 	int32 count;
907 	uint32 type;
908 	message->GetInfo("refs", &type, &count);
909 
910 	switch (selector) {
911 		case kRunOpenWithWindow:
912 			OpenContainerWindow(NULL, message, selector);
913 				// window adopts model
914 			break;
915 
916 		case kOpenWith:
917 		{
918 			// Open With resulted in passing refs and a handler,
919 			// open the files with the handling app
920 			message->RemoveName("handler");
921 
922 			// have to find out if handling app is the Tracker
923 			// if it is, just pass it to the active Tracker,
924 			// no matter which Tracker was chosen to handle the refs
925 			char signature[B_MIME_TYPE_LENGTH];
926 			signature[0] = '\0';
927 			{
928 				BFile handlingNode(&handlingApp, O_RDONLY);
929 				BAppFileInfo appInfo(&handlingNode);
930 				appInfo.GetSignature(signature);
931 			}
932 
933 			if (strcasecmp(signature, kTrackerSignature) != 0) {
934 				// handling app not Tracker, pass entries to the apps
935 				// RefsReceived
936 				TrackerLaunch(&handlingApp, message, true);
937 				break;
938 			}
939 		}
940 		// fall thru, opening refs by the Tracker as if they were
941 		// double-clicked
942 		case kOpen:
943 		{
944 			// copy over "Poses" messenger so that refs received
945 			// recipients know where the open came from
946 			BMessage* bundleThis = NULL;
947 			BMessage stackBundleThis;
948 			BMessenger messenger;
949 			if (message->FindMessenger("TrackerViewToken", &messenger)
950 					== B_OK) {
951 				bundleThis = &stackBundleThis;
952 				bundleThis->AddMessenger("TrackerViewToken", messenger);
953 			} else {
954 				// copy over any "be:*" fields -- e.g. /bin/open may include
955 				// "be:line" and "be:column"
956 				for (int32 i = 0;; i++) {
957 					char* name;
958 					type_code type;
959 					int32 count;
960 					status_t error = message->GetInfo(B_ANY_TYPE, i, &name,
961 						&type, &count);
962 					if (error != B_OK)
963 						break;
964 
965 					if (strncmp(name, "be:", 3) != 0)
966 						continue;
967 
968 					for (int32 k = 0; k < count; k++) {
969 						const void* data;
970 						ssize_t size;
971 						if (message->FindData(name, type, k, &data, &size)
972 								!= B_OK) {
973 							break;
974 						}
975 						if (stackBundleThis.AddData(name, type, data, size)
976 								!= B_OK) {
977 							break;
978 						}
979 						bundleThis = &stackBundleThis;
980 					}
981 				}
982 			}
983 
984 			for (int32 index = 0; index < count; index++) {
985 				entry_ref ref;
986 				message->FindRef("refs", index, &ref);
987 
988 				const node_ref* nodeToClose = NULL;
989 				const node_ref* nodeToSelect = NULL;
990 				ssize_t numBytes;
991 
992 				message->FindData("nodeRefsToClose", B_RAW_TYPE, index,
993 					(const void**)&nodeToClose, &numBytes);
994 				message->FindData("nodeRefToSelect", B_RAW_TYPE, index,
995 					(const void**)&nodeToSelect, &numBytes);
996 
997 				OpenRef(&ref, nodeToClose, nodeToSelect, selector,
998 					bundleThis);
999 			}
1000 
1001 			break;
1002 		}
1003 	}
1004 }
1005 
1006 
1007 void
1008 TTracker::ArgvReceived(int32 argc, char** argv)
1009 {
1010 	BMessage* message = CurrentMessage();
1011 	const char* currentWorkingDirectoryPath = NULL;
1012 	entry_ref ref;
1013 
1014 	if (message->FindString("cwd", &currentWorkingDirectoryPath) == B_OK) {
1015 		BDirectory workingDirectory(currentWorkingDirectoryPath);
1016 		for (int32 index = 1; index < argc; index++) {
1017 			BEntry entry;
1018 			if (entry.SetTo(&workingDirectory, argv[index]) == B_OK
1019 				&& entry.GetRef(&ref) == B_OK) {
1020 				OpenRef(&ref);
1021 			} else if (get_ref_for_path(argv[index], &ref) == B_OK)
1022 				OpenRef(&ref);
1023 		}
1024 	}
1025 }
1026 
1027 
1028 void
1029 TTracker::OpenContainerWindow(Model* model, BMessage* originalRefsList,
1030 	OpenSelector openSelector, uint32 openFlags, bool checkAlreadyOpen,
1031 	const BMessage* stateMessage)
1032 {
1033 	AutoLock<WindowList> lock(&fWindowList);
1034 	BContainerWindow* window = NULL;
1035 	const node_ref* modelNodeRef = model->NodeRef();
1036 	if (checkAlreadyOpen && openSelector != kRunOpenWithWindow) {
1037 		// find out if window already open
1038 		window = FindContainerWindow(modelNodeRef);
1039 	}
1040 
1041 	bool someWindowActivated = false;
1042 
1043 	uint32 workspace = (uint32)(1 << current_workspace());
1044 	int32 windowCount = 0;
1045 	while (window != NULL) {
1046 		if ((window->Workspaces() & workspace) != 0
1047 			&& (dynamic_cast<BDeskWindow*>(window) == NULL
1048 				|| !TrackerSettings().SingleWindowBrowse())) {
1049 			// We found at least one window that is open and is not Desktop
1050 			// or we're in spatial mode, activate it and make sure we don't
1051 			// jerk the workspaces around.
1052 			window->Activate();
1053 			someWindowActivated = true;
1054 		}
1055 		window = FindContainerWindow(model->NodeRef(), ++windowCount);
1056 	}
1057 
1058 	if (someWindowActivated) {
1059 		delete model;
1060 		return;
1061 	}
1062 
1063 	// If no window was activated (none in the current workspace),
1064 	// we open a new one.
1065 
1066 	if (openSelector == kRunOpenWithWindow) {
1067 		BMessage* refList = NULL;
1068 		if (originalRefsList == NULL) {
1069 			// when passing just a single model, stuff it's entry in a single
1070 			// element list anyway
1071 			ASSERT(model != NULL);
1072 			refList = new BMessage;
1073 			refList->AddRef("refs", model->EntryRef());
1074 			delete model;
1075 			model = NULL;
1076 		} else {
1077 			// clone the message, window adopts it for it's own use
1078 			refList = new BMessage(*originalRefsList);
1079 		}
1080 		window = new OpenWithContainerWindow(refList, &fWindowList);
1081 	} else if (model->IsQuery()) {
1082 		// window will adopt the model
1083 		window = new BQueryContainerWindow(&fWindowList, openFlags);
1084 	} else if (model->IsVirtualDirectory()) {
1085 		// window will adopt the model
1086 		window = new VirtualDirectoryWindow(&fWindowList, openFlags);
1087 	} else {
1088 		// window will adopt the model
1089 		window = new BContainerWindow(&fWindowList, openFlags);
1090 	}
1091 
1092 	if (model != NULL && window->LockLooper()) {
1093 		window->CreatePoseView(model);
1094 		if (window->PoseView() == NULL) {
1095 			// Failed initialization.
1096 			window->PostMessage(B_QUIT_REQUESTED);
1097 			window->UnlockLooper();
1098 			return;
1099 		}
1100 		window->UnlockLooper();
1101 	}
1102 
1103 	BMessage restoreStateMessage(kRestoreState);
1104 
1105 	if (stateMessage != NULL)
1106 		restoreStateMessage.AddMessage("state", stateMessage);
1107 
1108 	window->PostMessage(&restoreStateMessage);
1109 }
1110 
1111 
1112 void
1113 TTracker::EditQueries(const BMessage* message)
1114 {
1115 	bool editOnlyIfTemplate;
1116 	if (message->FindBool("editQueryOnPose", &editOnlyIfTemplate) != B_OK)
1117 		editOnlyIfTemplate = false;
1118 
1119 	type_code type;
1120 	int32 count;
1121 	message->GetInfo("refs", &type, &count);
1122 	for (int32 index = 0; index < count; index++) {
1123 		entry_ref ref;
1124 		message->FindRef("refs", index, &ref);
1125 		BEntry entry(&ref, true);
1126 		if (entry.InitCheck() == B_OK && entry.Exists())
1127 			(new FindWindow(&ref, editOnlyIfTemplate))->Show();
1128 	}
1129 }
1130 
1131 
1132 void
1133 TTracker::OpenInfoWindows(BMessage* message)
1134 {
1135 	type_code type;
1136 	int32 count;
1137 	message->GetInfo("refs", &type, &count);
1138 
1139 	for (int32 index = 0; index < count; index++) {
1140 		entry_ref ref;
1141 		message->FindRef("refs", index, &ref);
1142 		BEntry entry;
1143 		if (entry.SetTo(&ref) == B_OK) {
1144 			Model* model = new Model(&entry);
1145 			if (model->InitCheck() != B_OK) {
1146 				delete model;
1147 				continue;
1148 			}
1149 
1150 			AutoLock<WindowList> lock(&fWindowList);
1151 			BInfoWindow* wind = FindInfoWindow(model->NodeRef());
1152 
1153 			if (wind) {
1154 				wind->Activate();
1155 				delete model;
1156 			} else {
1157 				wind = new BInfoWindow(model, index, &fWindowList);
1158 				wind->PostMessage(kRestoreState);
1159 			}
1160 		}
1161 	}
1162 }
1163 
1164 
1165 BDeskWindow*
1166 TTracker::GetDeskWindow() const
1167 {
1168 	int32 count = fWindowList.CountItems();
1169 	for (int32 index = 0; index < count; index++) {
1170 		BDeskWindow* window = dynamic_cast<BDeskWindow*>(
1171 			fWindowList.ItemAt(index));
1172 		if (window != NULL)
1173 			return window;
1174 	}
1175 	TRESPASS();
1176 
1177 	return NULL;
1178 }
1179 
1180 
1181 BContainerWindow*
1182 TTracker::FindContainerWindow(const node_ref* node, int32 number) const
1183 {
1184 	ASSERT(fWindowList.IsLocked());
1185 
1186 	int32 count = fWindowList.CountItems();
1187 	int32 windowsFound = 0;
1188 	for (int32 index = 0; index < count; index++) {
1189 		BContainerWindow* window = dynamic_cast<BContainerWindow*>(
1190 			fWindowList.ItemAt(index));
1191 
1192 		if (window != NULL && window->IsShowing(node)
1193 			&& number == windowsFound++) {
1194 			return window;
1195 		}
1196 	}
1197 
1198 	return NULL;
1199 }
1200 
1201 
1202 BContainerWindow*
1203 TTracker::FindContainerWindow(const entry_ref* entry, int32 number) const
1204 {
1205 	ASSERT(fWindowList.IsLocked());
1206 
1207 	int32 count = fWindowList.CountItems();
1208 
1209 	int32 windowsFound = 0;
1210 
1211 	for (int32 index = 0; index < count; index++) {
1212 		BContainerWindow* window = dynamic_cast<BContainerWindow*>
1213 			(fWindowList.ItemAt(index));
1214 
1215 		if (window && window->IsShowing(entry) && number == windowsFound++)
1216 			return window;
1217 	}
1218 
1219 	return NULL;
1220 }
1221 
1222 
1223 bool
1224 TTracker::EntryHasWindowOpen(const entry_ref* entry)
1225 {
1226 	AutoLock<WindowList> lock(&fWindowList);
1227 	return FindContainerWindow(entry) != NULL;
1228 }
1229 
1230 
1231 BContainerWindow*
1232 TTracker::FindParentContainerWindow(const entry_ref* ref) const
1233 {
1234 	BEntry entry(ref);
1235 	BEntry parent;
1236 
1237 	if (entry.GetParent(&parent) != B_OK)
1238 		return NULL;
1239 
1240 	entry_ref parentRef;
1241 	parent.GetRef(&parentRef);
1242 
1243 	ASSERT(fWindowList.IsLocked());
1244 
1245 	int32 count = fWindowList.CountItems();
1246 	for (int32 index = 0; index < count; index++) {
1247 		BContainerWindow* window = dynamic_cast<BContainerWindow*>(
1248 			fWindowList.ItemAt(index));
1249 		if (window != NULL && window->IsShowing(&parentRef))
1250 			return window;
1251 	}
1252 
1253 	return NULL;
1254 }
1255 
1256 
1257 BInfoWindow*
1258 TTracker::FindInfoWindow(const node_ref* node) const
1259 {
1260 	ASSERT(fWindowList.IsLocked());
1261 
1262 	int32 count = fWindowList.CountItems();
1263 	for (int32 index = 0; index < count; index++) {
1264 		BInfoWindow* window = dynamic_cast<BInfoWindow*>(
1265 			fWindowList.ItemAt(index));
1266 		if (window != NULL && window->IsShowing(node))
1267 			return window;
1268 	}
1269 
1270 	return NULL;
1271 }
1272 
1273 
1274 bool
1275 TTracker::QueryActiveForDevice(dev_t device)
1276 {
1277 	AutoLock<WindowList> lock(&fWindowList);
1278 	int32 count = fWindowList.CountItems();
1279 	for (int32 index = 0; index < count; index++) {
1280 		BQueryContainerWindow* window = dynamic_cast<BQueryContainerWindow*>(
1281 			fWindowList.ItemAt(index));
1282 		if (window != NULL) {
1283 			AutoLock<BWindow> lock(window);
1284 			if (window->ActiveOnDevice(device))
1285 				return true;
1286 		}
1287 	}
1288 
1289 	return false;
1290 }
1291 
1292 
1293 void
1294 TTracker::CloseActiveQueryWindows(dev_t device)
1295 {
1296 	// used when trying to unmount a volume - an active query would prevent
1297 	// that from happening
1298 	bool closed = false;
1299 	AutoLock<WindowList> lock(fWindowList);
1300 	for (int32 index = fWindowList.CountItems(); index >= 0; index--) {
1301 		BQueryContainerWindow* window
1302 			= dynamic_cast<BQueryContainerWindow*>(fWindowList.ItemAt(index));
1303 		if (window != NULL) {
1304 			AutoLock<BWindow> lock(window);
1305 			if (window->ActiveOnDevice(device)) {
1306 				window->PostMessage(B_QUIT_REQUESTED);
1307 				closed = true;
1308 			}
1309 		}
1310 	}
1311 
1312 	lock.Unlock();
1313 
1314 	if (closed) {
1315 		for (int32 timeout = 30; timeout; timeout--) {
1316 			// wait a bit for windows to fully close
1317 			if (!QueryActiveForDevice(device))
1318 				return;
1319 
1320 			snooze(100000);
1321 		}
1322 	}
1323 }
1324 
1325 
1326 void
1327 TTracker::SaveAllPoseLocations()
1328 {
1329 	int32 numWindows = fWindowList.CountItems();
1330 	for (int32 windowIndex = 0; windowIndex < numWindows; windowIndex++) {
1331 		BContainerWindow* window = dynamic_cast<BContainerWindow*>(
1332 			fWindowList.ItemAt(windowIndex));
1333 		if (window != NULL) {
1334 			AutoLock<BWindow> lock(window);
1335 			BDeskWindow* deskWindow = dynamic_cast<BDeskWindow*>(window);
1336 			if (deskWindow != NULL)
1337 				deskWindow->SaveDesktopPoseLocations();
1338 			else
1339 				window->PoseView()->SavePoseLocations();
1340 		}
1341 	}
1342 }
1343 
1344 
1345 void
1346 TTracker::CloseWindowAndChildren(const node_ref* node)
1347 {
1348 	BDirectory dir(node);
1349 	if (dir.InitCheck() != B_OK)
1350 		return;
1351 
1352 	AutoLock<WindowList> lock(&fWindowList);
1353 	BObjectList<BContainerWindow> closeList;
1354 
1355 	// make a list of all windows to be closed
1356 	// count from end to beginning so we can remove items safely
1357 	for (int32 index = fWindowList.CountItems() - 1; index >= 0; index--) {
1358 		BContainerWindow* window = dynamic_cast<BContainerWindow*>(
1359 			fWindowList.ItemAt(index));
1360 		if (window && window->TargetModel()) {
1361 			BEntry wind_entry;
1362 			wind_entry.SetTo(window->TargetModel()->EntryRef());
1363 
1364 			if ((*window->TargetModel()->NodeRef() == *node)
1365 				|| dir.Contains(&wind_entry)) {
1366 
1367 				// ToDo:
1368 				// get rid of the Remove here, BContainerWindow::Quit does it
1369 				fWindowList.RemoveItemAt(index);
1370 				closeList.AddItem(window);
1371 			}
1372 		}
1373 	}
1374 
1375 	// now really close the windows
1376 	int32 numItems = closeList.CountItems();
1377 	for (int32 index = 0; index < numItems; index++) {
1378 		BContainerWindow* window = closeList.ItemAt(index);
1379 		window->PostMessage(B_QUIT_REQUESTED);
1380 	}
1381 }
1382 
1383 
1384 void
1385 TTracker::CloseAllInWorkspace()
1386 {
1387 	AutoLock<WindowList> lock(&fWindowList);
1388 
1389 	int32 currentWorkspace = 1 << current_workspace();
1390 	// count from end to beginning so we can remove items safely
1391 	for (int32 index = fWindowList.CountItems() - 1; index >= 0; index--) {
1392 		BWindow* window = fWindowList.ItemAt(index);
1393 		if (window != NULL && (window->Workspaces() & currentWorkspace) != 0) {
1394 			// avoid the desktop
1395 			if (dynamic_cast<BDeskWindow*>(window) == NULL
1396 				&& dynamic_cast<BStatusWindow*>(window) == NULL) {
1397 				window->PostMessage(B_QUIT_REQUESTED);
1398 			}
1399 		}
1400 	}
1401 }
1402 
1403 
1404 void
1405 TTracker::CloseAllWindows()
1406 {
1407 	// this is a response to the DeskBar sending us a B_QUIT, when it really
1408 	// means to say close all your windows. It might be better to have it
1409 	// send a kCloseAllWindows message and have windowless apps stay running,
1410 	// which is what we will do for the Tracker
1411 	AutoLock<WindowList> lock(&fWindowList);
1412 
1413 	int32 count = CountWindows();
1414 	for (int32 index = 0; index < count; index++) {
1415 		BWindow* window = WindowAt(index);
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 	// count from end to beginning so we can remove items safely
1424 	for (int32 index = fWindowList.CountItems() - 1; index >= 0; index--) {
1425 		BWindow* window = fWindowList.ItemAt(index);
1426 		if (dynamic_cast<BDeskWindow*>(window) == NULL
1427 			&& dynamic_cast<BStatusWindow*>(window) == NULL) {
1428 			// ToDo: get rid of the Remove here, BContainerWindow::Quit()
1429 			// does it
1430 			fWindowList.RemoveItemAt(index);
1431 		}
1432 	}
1433 }
1434 
1435 
1436 void
1437 TTracker::_OpenPreviouslyOpenedWindows(const char* pathFilter)
1438 {
1439 	size_t filterLength = 0;
1440 	if (pathFilter != NULL)
1441 		filterLength = strlen(pathFilter);
1442 
1443 	BDirectory deskDir;
1444 	attr_info attrInfo;
1445 	if (FSGetDeskDir(&deskDir) != B_OK
1446 		|| deskDir.GetAttrInfo(kAttrOpenWindows, &attrInfo) != B_OK) {
1447 		return;
1448 	}
1449 
1450 	char* buffer = (char*)malloc((size_t)attrInfo.size);
1451 	BMessage message;
1452 	if (deskDir.ReadAttr(kAttrOpenWindows, B_MESSAGE_TYPE, 0, buffer,
1453 			(size_t)attrInfo.size) != attrInfo.size
1454 		|| message.Unflatten(buffer) != B_OK) {
1455 		free(buffer);
1456 		return;
1457 	}
1458 
1459 	free(buffer);
1460 
1461 	node_ref nodeRef;
1462 	deskDir.GetNodeRef(&nodeRef);
1463 
1464 	int32 stateMessageCounter = 0;
1465 	const char* path;
1466 	for (int32 i = 0; message.FindString("paths", i, &path) == B_OK; i++) {
1467 		if (strncmp(path, pathFilter, filterLength) != 0)
1468 			continue;
1469 
1470 		BEntry entry(path, true);
1471 		if (entry.InitCheck() != B_OK)
1472 			continue;
1473 
1474 		int8 flags = 0;
1475 		for (int32 j = 0; message.FindInt8(path, j, &flags) == B_OK; j++) {
1476 			Model* model = new Model(&entry);
1477 			if (model->InitCheck() == B_OK && model->IsContainer()) {
1478 				BMessage state;
1479 				bool restoreStateFromMessage = false;
1480 				if ((flags & kOpenWindowHasState) != 0
1481 					&& message.FindMessage("window state",
1482 						stateMessageCounter++, &state) == B_OK) {
1483 					restoreStateFromMessage = true;
1484 				}
1485 
1486 				if (restoreStateFromMessage) {
1487 					OpenContainerWindow(model, 0, kOpen, kRestoreWorkspace
1488 						| (flags & kOpenWindowMinimized ? kIsHidden : 0U)
1489 						| kRestoreDecor, false, &state);
1490 				} else {
1491 					OpenContainerWindow(model, 0, kOpen, kRestoreWorkspace
1492 						| (flags & kOpenWindowMinimized ? kIsHidden : 0U)
1493 						| kRestoreDecor);
1494 				}
1495 			} else
1496 				delete model;
1497 		}
1498 	}
1499 
1500 	// open disks window if needed
1501 
1502 	if (pathFilter == NULL && TrackerSettings().ShowDisksIcon()
1503 		&& message.HasBool("open_disks_window")) {
1504 		BEntry entry("/");
1505 		Model* model = new Model(&entry);
1506 		if (model->InitCheck() == B_OK)
1507 			OpenContainerWindow(model, 0, kOpen, kRestoreWorkspace);
1508 		else
1509 			delete model;
1510 	}
1511 }
1512 
1513 
1514 void
1515 TTracker::ReadyToRun()
1516 {
1517 	gStatusWindow = new BStatusWindow();
1518 	InitMimeTypes();
1519 	InstallDefaultTemplates();
1520 	InstallIndices();
1521 	InstallTemporaryBackgroundImages();
1522 
1523 	fTrashWatcher = new BTrashWatcher();
1524 	fTrashWatcher->Run();
1525 
1526 	fClipboardRefsWatcher = new BClipboardRefsWatcher();
1527 	fClipboardRefsWatcher->Run();
1528 
1529 	fTaskLoop = new StandAloneTaskLoop(true);
1530 
1531 	// kick off building the mime type list for find panels, etc.
1532 	fMimeTypeList = new MimeTypeList();
1533 
1534 	if (!BootedInSafeMode()) {
1535 		// kick of transient query killer
1536 		DeleteTransientQueriesTask::StartUpTransientQueryCleaner();
1537 		// the mount_server will have mounted the previous volumes already.
1538 		_OpenPreviouslyOpenedWindows();
1539 	}
1540 }
1541 
1542 
1543 MimeTypeList*
1544 TTracker::MimeTypes() const
1545 {
1546 	return fMimeTypeList;
1547 }
1548 
1549 
1550 void
1551 TTracker::SelectChildInParentSoon(const entry_ref* parent,
1552 	const node_ref* child)
1553 {
1554 	fTaskLoop->RunLater(NewMemberFunctionObjectWithResult
1555 		(&TTracker::SelectChildInParent, this, parent, child),
1556 		100000, 200000, 5000000);
1557 }
1558 
1559 
1560 void
1561 TTracker::CloseParentWaitingForChildSoon(const entry_ref* child,
1562 	const node_ref* parent)
1563 {
1564 	fTaskLoop->RunLater(NewMemberFunctionObjectWithResult
1565 		(&TTracker::CloseParentWaitingForChild, this, child, parent),
1566 		200000, 100000, 5000000);
1567 }
1568 
1569 
1570 void
1571 TTracker::SelectPoseAtLocationSoon(node_ref parent, BPoint pointInPose)
1572 {
1573 	fTaskLoop->RunLater(NewMemberFunctionObject
1574 		(&TTracker::SelectPoseAtLocationInParent, this, parent, pointInPose),
1575 		100000);
1576 }
1577 
1578 
1579 void
1580 TTracker::SelectPoseAtLocationInParent(node_ref parent, BPoint pointInPose)
1581 {
1582 	AutoLock<WindowList> lock(&fWindowList);
1583 	BContainerWindow* parentWindow = FindContainerWindow(&parent);
1584 	if (parentWindow != NULL) {
1585 		AutoLock<BWindow> lock(parentWindow);
1586 		parentWindow->PoseView()->SelectPoseAtLocation(pointInPose);
1587 	}
1588 }
1589 
1590 
1591 bool
1592 TTracker::CloseParentWaitingForChild(const entry_ref* child,
1593 	const node_ref* parent)
1594 {
1595 	AutoLock<WindowList> lock(&fWindowList);
1596 
1597 	BContainerWindow* parentWindow = FindContainerWindow(parent);
1598 	if (parentWindow == NULL) {
1599 		// parent window already closed, give up
1600 		return true;
1601 	}
1602 
1603 	// If child is a symbolic link, dereference it, so that
1604 	// FindContainerWindow will succeed.
1605 	BEntry entry(child, true);
1606 	entry_ref resolvedChild;
1607 	if (entry.GetRef(&resolvedChild) != B_OK)
1608 		resolvedChild = *child;
1609 
1610 	BContainerWindow* window = FindContainerWindow(&resolvedChild);
1611 	if (window != NULL) {
1612 		AutoLock<BWindow> lock(window);
1613 		if (!window->IsHidden())
1614 			return CloseParentWindowCommon(parentWindow);
1615 	}
1616 
1617 	return false;
1618 }
1619 
1620 
1621 void
1622 TTracker::CloseParent(node_ref parent)
1623 {
1624 	AutoLock<WindowList> lock(&fWindowList);
1625 	if (!lock)
1626 		return;
1627 
1628 	CloseParentWindowCommon(FindContainerWindow(&parent));
1629 }
1630 
1631 
1632 void
1633 TTracker::ShowSettingsWindow()
1634 {
1635 	if (fSettingsWindow == NULL) {
1636 		fSettingsWindow = new TrackerSettingsWindow();
1637 		fSettingsWindow->Show();
1638 	} else {
1639 		if (fSettingsWindow->Lock()) {
1640 			if (fSettingsWindow->IsHidden())
1641 				fSettingsWindow->Show();
1642 			else
1643 				fSettingsWindow->Activate();
1644 
1645 			fSettingsWindow->Unlock();
1646 		}
1647 	}
1648 }
1649 
1650 
1651 bool
1652 TTracker::CloseParentWindowCommon(BContainerWindow* window)
1653 {
1654 	ASSERT(fWindowList.IsLocked());
1655 
1656 	if (dynamic_cast<BDeskWindow*>(window) != NULL) {
1657 		// don't close the desktop
1658 		return false;
1659 	}
1660 
1661 	window->PostMessage(B_QUIT_REQUESTED);
1662 	return true;
1663 }
1664 
1665 
1666 bool
1667 TTracker::SelectChildInParent(const entry_ref* parent, const node_ref* child)
1668 {
1669 	AutoLock<WindowList> lock(&fWindowList);
1670 
1671 	BContainerWindow* window = FindContainerWindow(parent);
1672 	if (window == NULL) {
1673 		// parent window already closed, give up
1674 		return false;
1675 	}
1676 
1677 	AutoLock<BWindow> windowLock(window);
1678 	if (windowLock.IsLocked()) {
1679 		BPoseView* view = window->PoseView();
1680 		int32 index;
1681 		BPose* pose = view->FindPose(child, &index);
1682 		if (pose != NULL) {
1683 			view->SelectPose(pose, index);
1684 			return true;
1685 		}
1686 	}
1687 
1688 	return false;
1689 }
1690 
1691 
1692 status_t
1693 TTracker::NeedMoreNodeMonitors()
1694 {
1695 	fNodeMonitorCount += kNodeMonitorBumpValue;
1696 	PRINT(("bumping nodeMonitorCount to %" B_PRId32 "\n", fNodeMonitorCount));
1697 
1698 	struct rlimit rl;
1699 	rl.rlim_cur = fNodeMonitorCount;
1700 	rl.rlim_max = RLIM_SAVED_MAX;
1701 	if (setrlimit(RLIMIT_NOVMON, &rl) < 0) {
1702 		fNodeMonitorCount -= kNodeMonitorBumpValue;
1703 		return errno;
1704 	}
1705 
1706 	return B_OK;
1707 }
1708 
1709 
1710 status_t
1711 TTracker::WatchNode(const node_ref* node, uint32 flags, BMessenger target)
1712 {
1713 	status_t result = watch_node(node, flags, target);
1714 	if (result == B_OK || result != B_NO_MEMORY) {
1715 		// need to make sure this uses the same error value as
1716 		// the node monitor code
1717 		return result;
1718 	}
1719 
1720 	PRINT(("failed to start monitoring, trying to allocate more "
1721 		"node monitors\n"));
1722 
1723 	TTracker* tracker = dynamic_cast<TTracker*>(be_app);
1724 	if (tracker == NULL) {
1725 		// we are the file panel only, just fail
1726 		return result;
1727 	}
1728 
1729 	result = tracker->NeedMoreNodeMonitors();
1730 
1731 	if (result != B_OK) {
1732 		PRINT(("failed to allocate more node monitors, %s\n",
1733 			strerror(result)));
1734 		return result;
1735 	}
1736 
1737 	// try again, this time with more node monitors
1738 	return watch_node(node, flags, target);
1739 }
1740 
1741 
1742 BMessenger
1743 TTracker::MountServer() const
1744 {
1745 	return BMessenger(kMountServerSignature);
1746 }
1747 
1748 
1749 bool
1750 TTracker::TrashFull() const
1751 {
1752 	return fTrashWatcher->CheckTrashDirs();
1753 }
1754 
1755 
1756 bool
1757 TTracker::IsTrashNode(const node_ref* node) const
1758 {
1759 	return fTrashWatcher->IsTrashNode(node);
1760 }
1761 
1762 bool
1763 TTracker::InTrashNode(const entry_ref* ref) const
1764 {
1765 	return FSInTrashDir(ref);
1766 }
1767