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