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