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 <stdlib.h> 36 #include <string.h> 37 #include <unistd.h> 38 39 #include <Alert.h> 40 #include <Autolock.h> 41 #include <Debug.h> 42 #include <FindDirectory.h> 43 #include <fs_attr.h> 44 #include <fs_info.h> 45 #include <image.h> 46 #include <MenuItem.h> 47 #include <NodeInfo.h> 48 #include <NodeMonitor.h> 49 #include <Path.h> 50 #include <Roster.h> 51 #include <StopWatch.h> 52 #include <Volume.h> 53 #include <VolumeRoster.h> 54 55 #include "Attributes.h" 56 #include "AutoLock.h" 57 #include "AutoMounter.h" 58 #include "AutoMounterSettings.h" 59 #include "BackgroundImage.h" 60 #include "Bitmaps.h" 61 #include "Commands.h" 62 #include "ContainerWindow.h" 63 #include "DeskWindow.h" 64 #include "FindPanel.h" 65 #include "FSClipboard.h" 66 #include "FSUtils.h" 67 #include "InfoWindow.h" 68 #include "MimeTypes.h" 69 #include "MimeTypeList.h" 70 #include "NodePreloader.h" 71 #include "OpenWithWindow.h" 72 #include "PoseView.h" 73 #include "QueryContainerWindow.h" 74 #include "StatusWindow.h" 75 #include "Tracker.h" 76 #include "TrackerSettings.h" 77 #include "TrashWatcher.h" 78 #include "FunctionObject.h" 79 #include "TrackerSettings.h" 80 #include "TrackerSettingsWindow.h" 81 #include "TaskLoop.h" 82 #include "Thread.h" 83 #include "Utilities.h" 84 #include "VolumeWindow.h" 85 86 // PPC binary compatibility. 87 #include "AboutBox.cpp" 88 89 // prototypes for some private kernel calls that will some day be public 90 #if B_BEOS_VERSION_DANO 91 #define _IMPEXP_ROOT 92 #endif 93 extern "C" _IMPEXP_ROOT int _kset_fd_limit_(int num); 94 extern "C" _IMPEXP_ROOT int _kset_mon_limit_(int num); 95 #if B_BEOS_VERSION_DANO 96 #undef _IMPEXP_ROOT 97 #endif 98 // from priv_syscalls.h 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 111 namespace BPrivate { 112 113 NodePreloader *gPreloader = NULL; 114 115 void 116 InitIconPreloader() 117 { 118 static int32 lock = 0; 119 120 if (atomic_add(&lock, 1) != 0) { 121 // Just wait for the icon cache to be instantiated 122 int32 tries = 20; 123 while (IconCache::sIconCache == NULL && tries-- > 0) 124 snooze(10000); 125 return; 126 } 127 128 if (IconCache::sIconCache != NULL) 129 return; 130 131 // only start the node preloader if its Tracker or the Deskbar itself - don't 132 // start it for file panels 133 134 bool preload = dynamic_cast<TTracker *>(be_app) != NULL; 135 if (!preload) { 136 // check for deskbar 137 app_info info; 138 if (be_app->GetAppInfo(&info) == B_OK 139 && !strcmp(info.signature, kDeskbarSignature)) 140 preload = true; 141 } 142 if (preload) 143 gPreloader = NodePreloader::InstallNodePreloader("NodePreloader", be_app); 144 145 IconCache::sIconCache = new IconCache(); 146 147 atomic_add(&lock, -1); 148 } 149 150 } // namespace BPrivate 151 152 153 uint32 154 GetVolumeFlags(Model *model) 155 { 156 fs_info info; 157 if (model->IsVolume()) { 158 // search for the correct volume 159 int32 cookie = 0; 160 dev_t device; 161 while ((device = next_dev(&cookie)) >= B_OK) { 162 if (fs_stat_dev(device,&info)) 163 continue; 164 165 if (!strcmp(info.volume_name,model->Name())) 166 return info.flags; 167 } 168 return B_FS_HAS_ATTR; 169 } 170 if (!fs_stat_dev(model->NodeRef()->device,&info)) 171 return info.flags; 172 173 return B_FS_HAS_ATTR; 174 } 175 176 177 static void 178 HideVarDir() 179 { 180 BPath path; 181 status_t err = find_directory(B_COMMON_VAR_DIRECTORY, &path); 182 183 if (err != B_OK){ 184 PRINT(("var err = %s\n", strerror(err))); 185 return; 186 } 187 188 BDirectory varDirectory(path.Path()); 189 if (varDirectory.InitCheck() == B_OK) { 190 PoseInfo info; 191 // make var dir invisible 192 info.fInvisible = true; 193 info.fInitedDirectory = -1; 194 195 if (varDirectory.WriteAttr(kAttrPoseInfo, B_RAW_TYPE, 0, &info, sizeof(info)) 196 == sizeof(info)) 197 varDirectory.RemoveAttr(kAttrPoseInfoForeign); 198 } 199 } 200 201 202 // #pragma mark - 203 204 205 TTracker::TTracker() 206 : BApplication(kTrackerSignature), 207 fSettingsWindow(NULL) 208 { 209 // set the cwd to /boot/home, anything that's launched 210 // from Tracker will automatically inherit this 211 BPath homePath; 212 213 if (find_directory(B_USER_DIRECTORY, &homePath) == B_OK) 214 chdir(homePath.Path()); 215 216 _kset_fd_limit_(512); 217 // ask for a bunch more file descriptors so that nested copying 218 // works well 219 220 fNodeMonitorCount = DEFAULT_MON_NUM; 221 222 #ifdef CHECK_OPEN_MODEL_LEAKS 223 InitOpenModelDumping(); 224 #endif 225 226 InitIconPreloader(); 227 228 #ifdef LEAK_CHECKING 229 SetNewLeakChecking(true); 230 SetMallocLeakChecking(true); 231 #endif 232 233 //This is how often it should update the free space bar on the volume icons 234 SetPulseRate(1000000); 235 } 236 237 238 TTracker::~TTracker() 239 { 240 } 241 242 243 bool 244 TTracker::QuitRequested() 245 { 246 // don't allow user quitting 247 if (CurrentMessage() && CurrentMessage()->FindBool("shortcut")) 248 return false; 249 250 gStatusWindow->AttemptToQuit(); 251 // try quitting the copy/move/empty trash threads 252 253 BVolume bootVolume; 254 DEBUG_ONLY(status_t err =) BVolumeRoster().GetBootVolume(&bootVolume); 255 ASSERT(err == B_OK); 256 BMessage message; 257 AutoLock<WindowList> lock(&fWindowList); 258 // save open windows in a message inside an attribute of the desktop 259 int32 count = fWindowList.CountItems(); 260 for (int32 i = 0; i < count; i++) { 261 BContainerWindow *window = dynamic_cast<BContainerWindow *> 262 (fWindowList.ItemAt(i)); 263 264 if (window && window->TargetModel() && !window->PoseView()->IsDesktopWindow()) { 265 if (window->TargetModel()->IsRoot()) 266 message.AddBool("open_disks_window", true); 267 else { 268 BEntry entry; 269 BPath path; 270 const entry_ref *ref = window->TargetModel()->EntryRef(); 271 if (entry.SetTo(ref) == B_OK && entry.GetPath(&path) == B_OK) { 272 int8 flags = window->IsMinimized() ? kOpenWindowMinimized : kOpenWindowNoFlags; 273 uint32 deviceFlags = GetVolumeFlags(window->TargetModel()); 274 275 // save state for every window which is 276 // a) already open on another workspace 277 // b) on a volume not capable of writing attributes 278 if (window != FindContainerWindow(ref) 279 || (deviceFlags & (B_FS_HAS_ATTR | B_FS_IS_READONLY)) != B_FS_HAS_ATTR) { 280 BMessage stateMessage; 281 window->SaveState(stateMessage); 282 window->SetSaveStateEnabled(false); 283 // This is to prevent its state to be saved to the node when closed. 284 message.AddMessage("window state", &stateMessage); 285 flags |= kOpenWindowHasState; 286 } 287 const char *target; 288 bool pathAlreadyExists = false; 289 for (int32 index = 0;message.FindString("paths", index, &target) == B_OK;index++) { 290 if (!strcmp(target,path.Path())) { 291 pathAlreadyExists = true; 292 break; 293 } 294 } 295 if (!pathAlreadyExists) 296 message.AddString("paths", path.Path()); 297 message.AddInt8(path.Path(), flags); 298 } 299 } 300 } 301 } 302 lock.Unlock(); 303 304 // write windows to open on disk 305 BDirectory deskDir; 306 if (!BootedInSafeMode() && FSGetDeskDir(&deskDir, bootVolume.Device()) == B_OK) { 307 // if message is empty, delete the corresponding attribute 308 if (message.CountNames(B_ANY_TYPE)) { 309 size_t size = (size_t)message.FlattenedSize(); 310 char *buffer = new char[size]; 311 message.Flatten(buffer, (ssize_t)size); 312 deskDir.WriteAttr(kAttrOpenWindows, B_MESSAGE_TYPE, 0, buffer, size); 313 delete [] buffer; 314 } else 315 deskDir.RemoveAttr(kAttrOpenWindows); 316 } 317 318 for (int32 count = 0; count == 50; count++) { 319 // wait 5 seconds for the copiing/moving to quit 320 if (gStatusWindow->AttemptToQuit()) 321 break; 322 323 snooze(100000); 324 } 325 326 return _inherited::QuitRequested(); 327 } 328 329 330 void 331 TTracker::Quit() 332 { 333 TrackerSettings().SaveSettings(false); 334 335 fAutoMounter->Lock(); 336 fAutoMounter->QuitRequested(); // automounter does some stuff in QuitRequested 337 fAutoMounter->Quit(); // but we really don't care if it is cooperating or not 338 339 fClipboardRefsWatcher->Lock(); 340 fClipboardRefsWatcher->Quit(); 341 342 fTrashWatcher->Lock(); 343 fTrashWatcher->Quit(); 344 345 WellKnowEntryList::Quit(); 346 347 delete gPreloader; 348 delete fTaskLoop; 349 delete IconCache::sIconCache; 350 351 _inherited::Quit(); 352 } 353 354 355 void 356 TTracker::MessageReceived(BMessage *message) 357 { 358 if (HandleScriptingMessage(message)) 359 return; 360 361 switch (message->what) { 362 case kGetInfo: 363 OpenInfoWindows(message); 364 break; 365 366 case kMoveToTrash: 367 MoveRefsToTrash(message); 368 break; 369 370 case kCloseWindowAndChildren: 371 { 372 const node_ref *itemNode; 373 int32 bytes; 374 message->FindData("node_ref", B_RAW_TYPE, 375 (const void **)&itemNode, &bytes); 376 CloseWindowAndChildren(itemNode); 377 break; 378 } 379 380 case kCloseAllWindows: 381 CloseAllWindows(); 382 break; 383 384 case kFindButton: 385 (new FindWindow())->Show(); 386 break; 387 388 case kEditQuery: 389 EditQueries(message); 390 break; 391 392 case kUnmountVolume: 393 // When the user attempts to unmount a volume from the mount 394 // context menu, this is where the message gets received. Save 395 // pose locations and forward this to the automounter 396 SaveAllPoseLocations(); 397 fAutoMounter->PostMessage(message); 398 break; 399 400 case kRunAutomounterSettings: 401 AutomountSettingsDialog::RunAutomountSettings(fAutoMounter); 402 break; 403 404 case kShowSplash: 405 { 406 // The AboutWindow was moved out of the Tracker in preparation 407 // for when we open source it. The AboutBox contains important 408 // credit and license issues that shouldn't be modified, and 409 // therefore shouldn't be open sourced. However, there is a public 410 // API for 3rd party apps to tell the Tracker to open the AboutBox. 411 run_be_about(); 412 break; 413 } 414 415 case kAddPrinter: 416 // show the addprinter window 417 run_add_printer_panel(); 418 break; 419 420 case kMakeActivePrinter: 421 // get the current selection 422 SetDefaultPrinter(message); 423 break; 424 425 #ifdef MOUNT_MENU_IN_DESKBAR 426 427 case 'gmtv': 428 { 429 // Someone (probably the deskbar) has requested a list of 430 // mountable volumes. 431 BMessage reply; 432 AutoMounterLoop()->EachMountableItemAndFloppy(&AddMountableItemToMessage, 433 &reply); 434 message->SendReply(&reply); 435 break; 436 } 437 438 #endif 439 440 case kMountVolume: 441 case kMountAllNow: 442 AutoMounterLoop()->PostMessage(message); 443 break; 444 445 446 case kRestoreBackgroundImage: 447 { 448 BDeskWindow *desktop = GetDeskWindow(); 449 AutoLock<BWindow> lock(desktop); 450 desktop->UpdateDesktopBackgroundImages(); 451 } 452 break; 453 454 case kShowSettingsWindow: 455 ShowSettingsWindow(); 456 break; 457 458 case kFavoriteCountChangedExternally: 459 SendNotices(kFavoriteCountChangedExternally, message); 460 break; 461 462 case kStartWatchClipboardRefs: 463 { 464 BMessenger messenger; 465 message->FindMessenger("target", &messenger); 466 if (messenger.IsValid()) 467 fClipboardRefsWatcher->AddToNotifyList(messenger); 468 break; 469 } 470 471 case kStopWatchClipboardRefs: 472 { 473 BMessenger messenger; 474 message->FindMessenger("target", &messenger); 475 if (messenger.IsValid()) 476 fClipboardRefsWatcher->RemoveFromNotifyList(messenger); 477 break; 478 } 479 480 case kFSClipboardChanges: 481 { 482 fClipboardRefsWatcher->UpdatePoseViews(message); 483 break; 484 } 485 486 default: 487 _inherited::MessageReceived(message); 488 break; 489 } 490 } 491 492 493 void 494 TTracker::Pulse() 495 { 496 if (!TrackerSettings().ShowVolumeSpaceBar()) 497 return; 498 499 // update the volume icon's free space bars 500 BVolumeRoster roster; 501 502 BVolume volume; 503 while (roster.GetNextVolume(&volume) == B_OK) { 504 BDirectory dir; 505 volume.GetRootDirectory(&dir); 506 node_ref nodeRef; 507 dir.GetNodeRef(&nodeRef); 508 509 BMessage notificationMessage; 510 notificationMessage.AddInt32("device", *(int32 *)&nodeRef.device); 511 512 SendNotices(kUpdateVolumeSpaceBar, ¬ificationMessage); 513 } 514 } 515 516 517 void 518 TTracker::SetDefaultPrinter(const BMessage *message) 519 { 520 // get the first item selected 521 int32 count = 0; 522 uint32 type = 0; 523 message->GetInfo("refs", &type, &count); 524 525 if (count <= 0) 526 return; 527 528 // will make the first item the default printer, disregards any other files 529 entry_ref ref; 530 ASSERT(message->FindRef("refs", 0, &ref) == B_OK); 531 if (message->FindRef("refs", 0, &ref) != B_OK) 532 return; 533 534 #if B_BEOS_VERSION_DANO 535 set_default_printer(ref.name); 536 #else 537 // create a message for the print server 538 BMessenger messenger("application/x-vnd.Be-PSRV", -1); 539 if (!messenger.IsValid()) 540 return; 541 542 // send the selection to the print server 543 BMessage makeActiveMessage(PSV_MAKE_PRINTER_ACTIVE_QUIETLY); 544 makeActiveMessage.AddString("printer", ref.name); 545 546 BMessage reply; 547 messenger.SendMessage(&makeActiveMessage, &reply); 548 #endif 549 } 550 551 552 void 553 TTracker::MoveRefsToTrash(const BMessage *message) 554 { 555 int32 count; 556 uint32 type; 557 message->GetInfo("refs", &type, &count); 558 559 if (count <= 0) 560 return; 561 562 BObjectList<entry_ref> *srcList = new BObjectList<entry_ref>(count, true); 563 564 for (int32 index = 0; index < count; index++) { 565 566 entry_ref ref; 567 ASSERT(message->FindRef("refs", index, &ref) == B_OK); 568 if (message->FindRef("refs", index, &ref) != B_OK) 569 continue; 570 571 AutoLock<WindowList> lock(&fWindowList); 572 BContainerWindow *window = FindParentContainerWindow(&ref); 573 if (window) 574 // if we have a window open for this entry, ask the pose to 575 // delete it, this will select the next entry 576 window->PoseView()->MoveEntryToTrash(&ref); 577 else 578 // add all others to a list that gets deleted separately 579 srcList->AddItem(new entry_ref(ref)); 580 } 581 582 if (srcList->CountItems()) 583 // async move to trash 584 FSMoveToTrash(srcList); 585 } 586 587 588 template <class T, class FT> 589 class EntryAndNodeDoSoonWithMessageFunctor : public FunctionObjectWithResult<bool> { 590 public: 591 EntryAndNodeDoSoonWithMessageFunctor(FT func, T *target, const entry_ref *child, 592 const node_ref *parent, const BMessage *message) 593 : fFunc(func), 594 fTarget(target), 595 fNode(*parent), 596 fEntry(*child) 597 { 598 fSendMessage = (message != NULL); 599 if (message) 600 fMessage = *message; 601 } 602 603 virtual ~EntryAndNodeDoSoonWithMessageFunctor() {} 604 virtual void operator()() 605 { result = (fTarget->*fFunc)(&fEntry, &fNode, fSendMessage ? &fMessage : NULL); } 606 607 protected: 608 FT fFunc; 609 T *fTarget; 610 node_ref fNode; 611 entry_ref fEntry; 612 BMessage fMessage; 613 bool fSendMessage; 614 }; 615 616 617 bool 618 TTracker::LaunchAndCloseParentIfOK(const entry_ref *launchThis, 619 const node_ref *closeThis, const BMessage *messageToBundle) 620 { 621 BMessage refsReceived(B_REFS_RECEIVED); 622 if (messageToBundle) { 623 refsReceived = *messageToBundle; 624 refsReceived.what = B_REFS_RECEIVED; 625 } 626 refsReceived.AddRef("refs", launchThis); 627 // synchronous launch, we are already in our own thread 628 if (TrackerLaunch(&refsReceived, false) == B_OK) { 629 // if launched fine, close parent window in a bit 630 fTaskLoop->RunLater(NewMemberFunctionObject(&TTracker::CloseParent, this, *closeThis), 631 1000000); 632 } 633 return false; 634 } 635 636 637 status_t 638 TTracker::OpenRef(const entry_ref *ref, const node_ref *nodeToClose, 639 const node_ref *nodeToSelect, OpenSelector selector, 640 const BMessage *messageToBundle) 641 { 642 Model *model = NULL; 643 BEntry entry(ref, true); 644 status_t result = entry.InitCheck(); 645 646 bool brokenLinkWithSpecificHandler = false; 647 BString brokenLinkPreferredApp; 648 649 if (result != B_OK) { 650 model = new Model(ref, false); 651 if (model->IsSymLink() && !model->LinkTo()) { 652 model->GetPreferredAppForBrokenSymLink(brokenLinkPreferredApp); 653 if (brokenLinkPreferredApp.Length() && brokenLinkPreferredApp != kTrackerSignature) 654 brokenLinkWithSpecificHandler = true; 655 } 656 657 if (!brokenLinkWithSpecificHandler) { 658 delete model; 659 (new BAlert("", "There was an error resolving the link.", 660 "Cancel", 0, 0, 661 B_WIDTH_AS_USUAL, B_WARNING_ALERT))->Go(); 662 return result; 663 } 664 } else 665 model = new Model(&entry); 666 667 result = model->InitCheck(); 668 if (result != B_OK) { 669 delete model; 670 return result; 671 } 672 673 bool openAsContainer = model->IsContainer(); 674 675 if (openAsContainer && selector != kOpenWith) { 676 // if folder or query has a preferred handler and it's not the 677 // Tracker, open it by sending refs to the handling app 678 679 // if we are responding to the final open of OpenWith, just 680 // skip this and proceed to opening the container with Tracker 681 model->OpenNode(); 682 BNodeInfo nodeInfo(model->Node()); 683 char preferredApp[B_MIME_TYPE_LENGTH]; 684 if (nodeInfo.GetPreferredApp(preferredApp) == B_OK 685 && strcasecmp(preferredApp, kTrackerSignature) != 0) 686 openAsContainer = false; 687 model->CloseNode(); 688 } 689 690 if (openAsContainer || selector == kRunOpenWithWindow) { 691 // special case opening plain folders, queries or using open with 692 OpenContainerWindow(model, 0, selector, kRestoreDecor); // window adopts model 693 if (nodeToClose) 694 CloseParentWaitingForChildSoon(ref, nodeToClose); 695 } else if (model->IsQueryTemplate()) { 696 // query template - open new find window 697 (new FindWindow(model->EntryRef()))->Show(); 698 if (nodeToClose) 699 CloseParentWaitingForChildSoon(ref, nodeToClose); 700 } else { 701 delete model; 702 // run Launch in a separate thread 703 // and close parent if successfull 704 if (nodeToClose) 705 Thread::Launch(new EntryAndNodeDoSoonWithMessageFunctor<TTracker, 706 bool (TTracker::*)(const entry_ref *, const node_ref *, 707 const BMessage *)>(&TTracker::LaunchAndCloseParentIfOK, this, 708 ref, nodeToClose, messageToBundle)); 709 else { 710 BMessage refsReceived(B_REFS_RECEIVED); 711 if (messageToBundle) { 712 refsReceived = *messageToBundle; 713 refsReceived.what = B_REFS_RECEIVED; 714 } 715 refsReceived.AddRef("refs", ref); 716 if (brokenLinkWithSpecificHandler) 717 // This cruft is to support a hacky workaround for double-clicking 718 // broken refs for cifs; should get fixed in R5 719 LaunchBrokenLink(brokenLinkPreferredApp.String(), &refsReceived); 720 else 721 TrackerLaunch(&refsReceived, true); 722 } 723 } 724 if (nodeToSelect) 725 SelectChildInParentSoon(ref, nodeToSelect); 726 727 return B_OK; 728 } 729 730 731 void 732 TTracker::RefsReceived(BMessage *message) 733 { 734 OpenSelector selector = kOpen; 735 if (message->HasInt32("launchUsingSelector")) 736 selector = kRunOpenWithWindow; 737 738 entry_ref handlingApp; 739 if (message->FindRef("handler", &handlingApp) == B_OK) 740 selector = kOpenWith; 741 742 int32 count; 743 uint32 type; 744 message->GetInfo("refs", &type, &count); 745 746 switch (selector) { 747 case kRunOpenWithWindow: 748 OpenContainerWindow(0, message, selector); 749 // window adopts model 750 break; 751 752 case kOpenWith: 753 { 754 // Open With resulted in passing refs and a handler, open the files 755 // with the handling app 756 message->RemoveName("handler"); 757 758 // have to find out if handling app is the Tracker 759 // if it is, just pass it to the active Tracker, no matter which Tracker 760 // was chosen to handle the refs 761 char signature[B_MIME_TYPE_LENGTH]; 762 signature[0] = '\0'; 763 { 764 BFile handlingNode(&handlingApp, O_RDONLY); 765 BAppFileInfo appInfo(&handlingNode); 766 appInfo.GetSignature(signature); 767 } 768 769 if (strcasecmp(signature, kTrackerSignature) != 0) { 770 // handling app not Tracker, pass entries to the apps RefsReceived 771 TrackerLaunch(&handlingApp, message, true); 772 break; 773 } 774 // fall thru, opening refs by the Tracker, as if they were double clicked 775 } 776 777 case kOpen: 778 { 779 // copy over "Poses" messenger so that refs received recipients know 780 // where the open came from 781 BMessage *bundleThis = NULL; 782 BMessenger messenger; 783 if (message->FindMessenger("TrackerViewToken", &messenger) == B_OK) { 784 bundleThis = new BMessage(); 785 bundleThis->AddMessenger("TrackerViewToken", messenger); 786 } 787 788 for (int32 index = 0; index < count; index++) { 789 entry_ref ref; 790 message->FindRef("refs", index, &ref); 791 792 const node_ref *nodeToClose = NULL; 793 const node_ref *nodeToSelect = NULL; 794 ssize_t numBytes; 795 796 message->FindData("nodeRefsToClose", B_RAW_TYPE, index, 797 (const void **)&nodeToClose, &numBytes); 798 message->FindData("nodeRefToSelect", B_RAW_TYPE, index, 799 (const void **)&nodeToSelect, &numBytes); 800 801 OpenRef(&ref, nodeToClose, nodeToSelect, selector, bundleThis); 802 } 803 804 delete bundleThis; 805 break; 806 } 807 } 808 } 809 810 811 void 812 TTracker::ArgvReceived(int32 argc, char **argv) 813 { 814 BMessage *message = CurrentMessage(); 815 const char *currentWorkingDirectoryPath = NULL; 816 entry_ref ref; 817 818 if (message->FindString("cwd", ¤tWorkingDirectoryPath) == B_OK) { 819 BDirectory workingDirectory(currentWorkingDirectoryPath); 820 for (int32 index = 1; index < argc; index++) { 821 BEntry entry; 822 if (entry.SetTo(&workingDirectory, argv[index]) == B_OK 823 && entry.GetRef(&ref) == B_OK) 824 OpenRef(&ref); 825 else if (get_ref_for_path(argv[index], &ref) == B_OK) 826 OpenRef(&ref); 827 } 828 } 829 } 830 831 832 void 833 TTracker::OpenContainerWindow(Model *model, BMessage *originalRefsList, 834 OpenSelector openSelector, uint32 openFlags, bool checkAlreadyOpen, 835 const BMessage *stateMessage) 836 { 837 AutoLock<WindowList> lock(&fWindowList); 838 BContainerWindow *window = NULL; 839 if (checkAlreadyOpen && openSelector != kRunOpenWithWindow) 840 // find out if window already open 841 window = FindContainerWindow(model->NodeRef()); 842 843 bool someWindowActivated = false; 844 845 uint32 workspace = (uint32)(1 << current_workspace()); 846 int32 windowCount = 0; 847 848 while (window) { 849 // At least one window open, just pull to front 850 // make sure we don't jerk workspaces around 851 uint32 windowWorkspaces = window->Workspaces(); 852 if (windowWorkspaces & workspace) { 853 window->Activate(); 854 someWindowActivated = true; 855 } 856 window = FindContainerWindow(model->NodeRef(), ++windowCount); 857 } 858 859 if (someWindowActivated) { 860 delete model; 861 return; 862 } // If no window was actiated, (none in the current workspace 863 // we open a new one. 864 865 if (openSelector == kRunOpenWithWindow) { 866 BMessage *refList = NULL; 867 if (!originalRefsList) { 868 // when passing just a single model, stuff it's entry in a single 869 // element list anyway 870 ASSERT(model); 871 refList = new BMessage; 872 refList->AddRef("refs", model->EntryRef()); 873 delete model; 874 model = NULL; 875 } else 876 // clone the message, window adopts it for it's own use 877 refList = new BMessage(*originalRefsList); 878 window = new OpenWithContainerWindow(refList, &fWindowList); 879 } else if (model->IsRoot()) { 880 // window will adopt the model 881 window = new BVolumeWindow(&fWindowList, openFlags); 882 } else if (model->IsQuery()) { 883 // window will adopt the model 884 window = new BQueryContainerWindow(&fWindowList, openFlags); 885 } else 886 // window will adopt the model 887 window = new BContainerWindow(&fWindowList, openFlags); 888 889 if (model) 890 window->CreatePoseView(model); 891 892 BMessage restoreStateMessage(kRestoreState); 893 894 if (stateMessage) 895 restoreStateMessage.AddMessage("state", stateMessage); 896 897 window->PostMessage(&restoreStateMessage); 898 } 899 900 901 void 902 TTracker::EditQueries(const BMessage *message) 903 { 904 bool editOnlyIfTemplate; 905 if (message->FindBool("editQueryOnPose", &editOnlyIfTemplate) != B_OK) 906 editOnlyIfTemplate = false; 907 908 type_code type; 909 int32 count; 910 message->GetInfo("refs", &type, &count); 911 for (int32 index = 0; index < count; index++) { 912 entry_ref ref; 913 message->FindRef("refs", index, &ref); 914 BEntry entry(&ref, true); 915 if (entry.InitCheck() == B_OK && entry.Exists()) 916 (new FindWindow(&ref, editOnlyIfTemplate))->Show(); 917 } 918 } 919 920 921 void 922 TTracker::OpenInfoWindows(BMessage *message) 923 { 924 type_code type; 925 int32 count; 926 message->GetInfo("refs", &type, &count); 927 928 for (int32 index = 0; index < count; index++) { 929 entry_ref ref; 930 message->FindRef("refs", index, &ref); 931 BEntry entry; 932 if (entry.SetTo(&ref) == B_OK) { 933 Model *model = new Model(&entry); 934 if (model->InitCheck() != B_OK) { 935 delete model; 936 continue; 937 } 938 939 AutoLock<WindowList> lock(&fWindowList); 940 BInfoWindow *wind = FindInfoWindow(model->NodeRef()); 941 942 if (wind) { 943 wind->Activate(); 944 delete model; 945 } else { 946 wind = new BInfoWindow(model, index, &fWindowList); 947 wind->PostMessage(kRestoreState); 948 } 949 } 950 } 951 } 952 953 954 BDeskWindow * 955 TTracker::GetDeskWindow() const 956 { 957 int32 count = fWindowList.CountItems(); 958 for (int32 index = 0; index < count; index++) { 959 BDeskWindow *window = dynamic_cast<BDeskWindow *> 960 (fWindowList.ItemAt(index)); 961 962 if (window) 963 return window; 964 } 965 TRESPASS(); 966 return NULL; 967 } 968 969 970 BContainerWindow * 971 TTracker::FindContainerWindow(const node_ref *node, int32 number) const 972 { 973 ASSERT(fWindowList.IsLocked()); 974 975 int32 count = fWindowList.CountItems(); 976 977 int32 windowsFound = 0; 978 979 for (int32 index = 0; index < count; index++) { 980 BContainerWindow *window = dynamic_cast<BContainerWindow *> 981 (fWindowList.ItemAt(index)); 982 983 if (window && window->IsShowing(node) && number == windowsFound++) 984 return window; 985 } 986 return NULL; 987 } 988 989 990 BContainerWindow * 991 TTracker::FindContainerWindow(const entry_ref *entry, int32 number) const 992 { 993 ASSERT(fWindowList.IsLocked()); 994 995 int32 count = fWindowList.CountItems(); 996 997 int32 windowsFound = 0; 998 999 for (int32 index = 0; index < count; index++) { 1000 BContainerWindow *window = dynamic_cast<BContainerWindow *> 1001 (fWindowList.ItemAt(index)); 1002 1003 if (window && window->IsShowing(entry) && number == windowsFound++) 1004 return window; 1005 } 1006 return NULL; 1007 } 1008 1009 1010 bool 1011 TTracker::EntryHasWindowOpen(const entry_ref *entry) 1012 { 1013 AutoLock<WindowList> lock(&fWindowList); 1014 return FindContainerWindow(entry) != NULL; 1015 } 1016 1017 1018 BContainerWindow * 1019 TTracker::FindParentContainerWindow(const entry_ref *ref) const 1020 { 1021 BEntry entry(ref); 1022 BEntry parent; 1023 1024 if (entry.GetParent(&parent) != B_OK) 1025 return NULL; 1026 1027 entry_ref parentRef; 1028 parent.GetRef(&parentRef); 1029 1030 ASSERT(fWindowList.IsLocked()); 1031 1032 int32 count = fWindowList.CountItems(); 1033 for (int32 index = 0; index < count; index++) { 1034 BContainerWindow *window = dynamic_cast<BContainerWindow *> 1035 (fWindowList.ItemAt(index)); 1036 if (window && window->IsShowing(&parentRef)) 1037 return window; 1038 } 1039 return NULL; 1040 } 1041 1042 1043 BInfoWindow * 1044 TTracker::FindInfoWindow(const node_ref* node) const 1045 { 1046 ASSERT(fWindowList.IsLocked()); 1047 1048 int32 count = fWindowList.CountItems(); 1049 for (int32 index = 0; index < count; index++) { 1050 BInfoWindow *window = dynamic_cast<BInfoWindow *> 1051 (fWindowList.ItemAt(index)); 1052 if (window && window->IsShowing(node)) 1053 return window; 1054 } 1055 return NULL; 1056 } 1057 1058 1059 bool 1060 TTracker::QueryActiveForDevice(dev_t device) 1061 { 1062 AutoLock<WindowList> lock(&fWindowList); 1063 int32 count = fWindowList.CountItems(); 1064 for (int32 index = 0; index < count; index++) { 1065 BQueryContainerWindow *window = dynamic_cast<BQueryContainerWindow *> 1066 (fWindowList.ItemAt(index)); 1067 if (window) { 1068 AutoLock<BWindow> lock(window); 1069 if (window->ActiveOnDevice(device)) 1070 return true; 1071 } 1072 } 1073 return false; 1074 } 1075 1076 1077 void 1078 TTracker::CloseActiveQueryWindows(dev_t device) 1079 { 1080 // used when trying to unmount a volume - an active query would prevent that from 1081 // happening 1082 bool closed = false; 1083 AutoLock<WindowList> lock(fWindowList); 1084 for (int32 index = fWindowList.CountItems(); index >= 0; index--) { 1085 BQueryContainerWindow *window = dynamic_cast<BQueryContainerWindow *> 1086 (fWindowList.ItemAt(index)); 1087 if (window) { 1088 AutoLock<BWindow> lock(window); 1089 if (window->ActiveOnDevice(device)) { 1090 window->PostMessage(B_QUIT_REQUESTED); 1091 closed = true; 1092 } 1093 } 1094 } 1095 lock.Unlock(); 1096 if (closed) 1097 for (int32 timeout = 30; timeout; timeout--) { 1098 // wait a bit for windows to fully close 1099 if (!QueryActiveForDevice(device)) 1100 return; 1101 snooze(100000); 1102 } 1103 } 1104 1105 1106 void 1107 TTracker::SaveAllPoseLocations() 1108 { 1109 int32 numWindows = fWindowList.CountItems(); 1110 for (int32 windowIndex = 0; windowIndex < numWindows; windowIndex++) { 1111 BContainerWindow *window = dynamic_cast<BContainerWindow *> 1112 (fWindowList.ItemAt(windowIndex)); 1113 1114 if (window) { 1115 AutoLock<BWindow> lock(window); 1116 BDeskWindow *deskWindow = dynamic_cast<BDeskWindow *>(window); 1117 1118 if (deskWindow) 1119 deskWindow->SaveDesktopPoseLocations(); 1120 else 1121 window->PoseView()->SavePoseLocations(); 1122 } 1123 } 1124 } 1125 1126 1127 void 1128 TTracker::CloseWindowAndChildren(const node_ref *node) 1129 { 1130 BDirectory dir(node); 1131 if (dir.InitCheck() != B_OK) 1132 return; 1133 1134 AutoLock<WindowList> lock(&fWindowList); 1135 BObjectList<BContainerWindow> closeList; 1136 1137 // make a list of all windows to be closed 1138 // count from end to beginning so we can remove items safely 1139 for (int32 index = fWindowList.CountItems() - 1; index >= 0; index--) { 1140 BContainerWindow *window = dynamic_cast<BContainerWindow *> 1141 (fWindowList.ItemAt(index)); 1142 if (window && window->TargetModel()) { 1143 BEntry wind_entry; 1144 wind_entry.SetTo(window->TargetModel()->EntryRef()); 1145 1146 if ((*window->TargetModel()->NodeRef() == *node) 1147 || dir.Contains(&wind_entry)) { 1148 1149 // ToDo: 1150 // get rid of the Remove here, BContainerWindow::Quit does it 1151 fWindowList.RemoveItemAt(index); 1152 closeList.AddItem(window); 1153 } 1154 } 1155 } 1156 1157 // now really close the windows 1158 int32 numItems = closeList.CountItems(); 1159 for (int32 index = 0; index < numItems; index++) { 1160 BContainerWindow *window = closeList.ItemAt(index); 1161 window->PostMessage(B_QUIT_REQUESTED); 1162 } 1163 } 1164 1165 1166 void 1167 TTracker::CloseAllWindows() 1168 { 1169 // this is a response to the DeskBar sending us a B_QUIT, when it really 1170 // means to say close all your windows. It might be better to have it 1171 // send a kCloseAllWindows message and have windowless apps stay running, 1172 // which is what we will do for the Tracker 1173 AutoLock<WindowList> lock(&fWindowList); 1174 1175 int32 count = CountWindows(); 1176 for (int32 index = 0; index < count; index++) { 1177 BWindow *window = WindowAt(index); 1178 // avoid the desktop 1179 if (!dynamic_cast<BDeskWindow *>(window) 1180 && !dynamic_cast<BStatusWindow *>(window)) 1181 window->PostMessage(B_QUIT_REQUESTED); 1182 } 1183 // count from end to beginning so we can remove items safely 1184 for (int32 index = fWindowList.CountItems() - 1; index >= 0; index--) { 1185 BWindow *window = fWindowList.ItemAt(index); 1186 if (!dynamic_cast<BDeskWindow *>(window) 1187 && !dynamic_cast<BStatusWindow *>(window)) 1188 // ToDo: 1189 // get rid of the Remove here, BContainerWindow::Quit does it 1190 fWindowList.RemoveItemAt(index); 1191 } 1192 } 1193 1194 1195 void 1196 TTracker::ReadyToRun() 1197 { 1198 gStatusWindow = new BStatusWindow(); 1199 InitMimeTypes(); 1200 InstallDefaultTemplates(); 1201 InstallIndices(); 1202 1203 HideVarDir(); 1204 1205 fTrashWatcher = new BTrashWatcher(); 1206 fTrashWatcher->Run(); 1207 1208 fClipboardRefsWatcher = new BClipboardRefsWatcher(); 1209 fClipboardRefsWatcher->Run(); 1210 1211 fAutoMounter = new AutoMounter(); 1212 fAutoMounter->Run(); 1213 1214 fTaskLoop = new StandAloneTaskLoop(true); 1215 1216 bool openDisksWindow = false; 1217 1218 // open desktop window 1219 BContainerWindow *deskWindow = NULL; 1220 BVolume bootVol; 1221 BVolumeRoster().GetBootVolume(&bootVol); 1222 BDirectory deskDir; 1223 if (FSGetDeskDir(&deskDir, bootVol.Device()) == B_OK) { 1224 // create desktop 1225 BEntry entry; 1226 deskDir.GetEntry(&entry); 1227 Model *model = new Model(&entry); 1228 if (model->InitCheck() == B_OK) { 1229 AutoLock<WindowList> lock(&fWindowList); 1230 deskWindow = new BDeskWindow(&fWindowList); 1231 AutoLock<BWindow> windowLock(deskWindow); 1232 deskWindow->CreatePoseView(model); 1233 deskWindow->Init(); 1234 } else 1235 delete model; 1236 1237 // open previously open windows 1238 attr_info attrInfo; 1239 if (!BootedInSafeMode() 1240 && deskDir.GetAttrInfo(kAttrOpenWindows, &attrInfo) == B_OK) { 1241 char *buffer = (char *)malloc((size_t)attrInfo.size); 1242 BMessage message; 1243 if (deskDir.ReadAttr(kAttrOpenWindows, B_MESSAGE_TYPE, 0, buffer, (size_t)attrInfo.size) 1244 == attrInfo.size 1245 && message.Unflatten(buffer) == B_OK) { 1246 1247 node_ref nodeRef; 1248 deskDir.GetNodeRef(&nodeRef); 1249 1250 int32 stateMessageCounter = 0; 1251 const char *path; 1252 for (int32 outer = 0;message.FindString("paths", outer, &path) == B_OK;outer++) { 1253 int8 flags = 0; 1254 for (int32 inner = 0;message.FindInt8(path, inner, &flags) == B_OK;inner++) { 1255 BEntry entry(path, true); 1256 if (entry.InitCheck() == B_OK) { 1257 Model *model = new Model(&entry); 1258 if (model->InitCheck() == B_OK && model->IsContainer()) { 1259 BMessage state; 1260 bool restoreStateFromMessage = false; 1261 if ((flags & kOpenWindowHasState) != 0 1262 && message.FindMessage("window state", stateMessageCounter++, &state) == B_OK) 1263 restoreStateFromMessage = true; 1264 1265 if (restoreStateFromMessage) 1266 OpenContainerWindow(model, 0, kOpen, 1267 kRestoreWorkspace | (flags & kOpenWindowMinimized ? kIsHidden : 0U) | kRestoreDecor, 1268 false, &state); 1269 else 1270 OpenContainerWindow(model, 0, kOpen, 1271 kRestoreWorkspace | (flags & kOpenWindowMinimized ? kIsHidden : 0U) | kRestoreDecor); 1272 } else 1273 delete model; 1274 } 1275 } 1276 } 1277 1278 if (message.HasBool("open_disks_window")) 1279 openDisksWindow = true; 1280 } 1281 free(buffer); 1282 } 1283 } 1284 1285 // create model for root of everything 1286 if (deskWindow) { 1287 BEntry entry("/"); 1288 Model model(&entry); 1289 if (model.InitCheck() == B_OK) { 1290 1291 if (TrackerSettings().ShowDisksIcon()) { 1292 // add the root icon to desktop window 1293 BMessage message; 1294 message.what = B_NODE_MONITOR; 1295 message.AddInt32("opcode", B_ENTRY_CREATED); 1296 message.AddInt32("device", model.NodeRef()->device); 1297 message.AddInt64("node", model.NodeRef()->node); 1298 message.AddInt64("directory", model.EntryRef()->directory); 1299 message.AddString("name", model.EntryRef()->name); 1300 deskWindow->PostMessage(&message, deskWindow->PoseView()); 1301 } 1302 1303 if (openDisksWindow) 1304 OpenContainerWindow(new Model(model), 0, kOpen, kRestoreWorkspace); 1305 } 1306 } 1307 1308 // kick off building the mime type list for find panels, etc. 1309 fMimeTypeList = new MimeTypeList(); 1310 1311 if (!BootedInSafeMode()) 1312 // kick of transient query killer 1313 DeleteTransientQueriesTask::StartUpTransientQueryCleaner(); 1314 } 1315 1316 MimeTypeList * 1317 TTracker::MimeTypes() const 1318 { 1319 return fMimeTypeList; 1320 } 1321 1322 void 1323 TTracker::SelectChildInParentSoon(const entry_ref *parent, 1324 const node_ref *child) 1325 { 1326 fTaskLoop->RunLater(NewMemberFunctionObjectWithResult 1327 (&TTracker::SelectChildInParent, this, parent, child), 1328 100000, 200000, 5000000); 1329 } 1330 1331 void 1332 TTracker::CloseParentWaitingForChildSoon(const entry_ref *child, 1333 const node_ref *parent) 1334 { 1335 fTaskLoop->RunLater(NewMemberFunctionObjectWithResult 1336 (&TTracker::CloseParentWaitingForChild, this, child, parent), 1337 200000, 100000, 5000000); 1338 } 1339 1340 void 1341 TTracker::SelectPoseAtLocationSoon(node_ref parent, BPoint pointInPose) 1342 { 1343 fTaskLoop->RunLater(NewMemberFunctionObject 1344 (&TTracker::SelectPoseAtLocationInParent, this, parent, pointInPose), 1345 100000); 1346 } 1347 1348 void 1349 TTracker::SelectPoseAtLocationInParent(node_ref parent, BPoint pointInPose) 1350 { 1351 AutoLock<WindowList> lock(&fWindowList); 1352 BContainerWindow *parentWindow = FindContainerWindow(&parent); 1353 if (parentWindow) { 1354 AutoLock<BWindow> lock(parentWindow); 1355 parentWindow->PoseView()->SelectPoseAtLocation(pointInPose); 1356 } 1357 } 1358 1359 bool 1360 TTracker::CloseParentWaitingForChild(const entry_ref *child, 1361 const node_ref *parent) 1362 { 1363 AutoLock<WindowList> lock(&fWindowList); 1364 1365 BContainerWindow *parentWindow = FindContainerWindow(parent); 1366 if (!parentWindow) 1367 // parent window already closed, give up 1368 return true; 1369 1370 // If child is a symbolic link, dereference it, so that 1371 // FindContainerWindow will succeed. 1372 BEntry entry(child, true); 1373 entry_ref resolvedChild; 1374 if (entry.GetRef(&resolvedChild) != B_OK) 1375 resolvedChild = *child; 1376 1377 BContainerWindow *window = FindContainerWindow(&resolvedChild); 1378 if (window) { 1379 AutoLock<BWindow> lock(window); 1380 if (!window->IsHidden()) 1381 return CloseParentWindowCommon(parentWindow); 1382 } 1383 return false; 1384 } 1385 1386 void 1387 TTracker::CloseParent(node_ref parent) 1388 { 1389 AutoLock<WindowList> lock(&fWindowList); 1390 if (!lock) 1391 return; 1392 1393 CloseParentWindowCommon(FindContainerWindow(&parent)); 1394 } 1395 1396 void 1397 TTracker::ShowSettingsWindow() 1398 { 1399 if (!fSettingsWindow) { 1400 fSettingsWindow = new TrackerSettingsWindow(); 1401 fSettingsWindow->Show(); 1402 } else { 1403 if (fSettingsWindow->Lock()) { 1404 if (fSettingsWindow->IsHidden()) 1405 fSettingsWindow->Show(); 1406 else 1407 fSettingsWindow->Activate(); 1408 fSettingsWindow->Unlock(); 1409 } 1410 } 1411 } 1412 1413 bool 1414 TTracker::CloseParentWindowCommon(BContainerWindow *window) 1415 { 1416 ASSERT(fWindowList.IsLocked()); 1417 1418 if (dynamic_cast<BDeskWindow *>(window)) 1419 // don't close the destop 1420 return false; 1421 1422 window->PostMessage(B_QUIT_REQUESTED); 1423 return true; 1424 } 1425 1426 bool 1427 TTracker::SelectChildInParent(const entry_ref *parent, const node_ref *child) 1428 { 1429 AutoLock<WindowList> lock(&fWindowList); 1430 1431 BContainerWindow *window = FindContainerWindow(parent); 1432 if (!window) 1433 // parent window already closed, give up 1434 return false; 1435 1436 AutoLock<BWindow> windowLock(window); 1437 1438 if (windowLock.IsLocked()) { 1439 BPoseView *view = window->PoseView(); 1440 int32 index; 1441 BPose *pose = view->FindPose(child, &index); 1442 if (pose) { 1443 view->SelectPose(pose, index); 1444 return true; 1445 } 1446 } 1447 return false; 1448 } 1449 1450 const int32 kNodeMonitorBumpValue = 512; 1451 1452 status_t 1453 TTracker::NeedMoreNodeMonitors() 1454 { 1455 fNodeMonitorCount += kNodeMonitorBumpValue; 1456 PRINT(("bumping nodeMonitorCount to %d\n", fNodeMonitorCount)); 1457 1458 return _kset_mon_limit_(fNodeMonitorCount); 1459 } 1460 1461 status_t 1462 TTracker::WatchNode(const node_ref *node, uint32 flags, 1463 BMessenger target) 1464 { 1465 status_t result = watch_node(node, flags, target); 1466 if (result == B_OK || result != B_NO_MEMORY) { 1467 // need to make sure this uses the same error value as 1468 // the node monitor code 1469 return result; 1470 } 1471 1472 PRINT(("failed to start monitoring, trying to allocate more " 1473 "node monitors\n")); 1474 1475 TTracker *tracker = dynamic_cast<TTracker *>(be_app); 1476 if (!tracker) { 1477 // we are the file panel only, just fail 1478 return result; 1479 } 1480 1481 result = tracker->NeedMoreNodeMonitors(); 1482 1483 if (result != B_OK) { 1484 PRINT(("failed to allocate more node monitors, %s\n", 1485 strerror(result))); 1486 return result; 1487 } 1488 1489 // try again, this time with more node monitors 1490 return watch_node(node, flags, target); 1491 } 1492 1493 1494 AutoMounter * 1495 TTracker::AutoMounterLoop() 1496 { 1497 return fAutoMounter; 1498 } 1499 1500 1501 bool 1502 TTracker::InTrashNode(const entry_ref *node) const 1503 { 1504 return FSInTrashDir(node); 1505 } 1506 1507 1508 bool 1509 TTracker::TrashFull() const 1510 { 1511 return fTrashWatcher->CheckTrashDirs(); 1512 } 1513 1514 1515 bool 1516 TTracker::IsTrashNode(const node_ref *node) const 1517 { 1518 return fTrashWatcher->IsTrashNode(node); 1519 } 1520 1521