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 case kShowVolumeSpaceBar: 487 case kSpaceBarColorChanged: { 488 gPeriodicUpdatePoses.DoPeriodicUpdate(true); 489 break; 490 } 491 492 default: 493 _inherited::MessageReceived(message); 494 break; 495 } 496 } 497 498 499 void 500 TTracker::Pulse() 501 { 502 if (!TrackerSettings().ShowVolumeSpaceBar()) 503 return; 504 505 // update the volume icon's free space bars 506 gPeriodicUpdatePoses.DoPeriodicUpdate(false); 507 } 508 509 510 void 511 TTracker::SetDefaultPrinter(const BMessage *message) 512 { 513 // get the first item selected 514 int32 count = 0; 515 uint32 type = 0; 516 message->GetInfo("refs", &type, &count); 517 518 if (count <= 0) 519 return; 520 521 // will make the first item the default printer, disregards any other files 522 entry_ref ref; 523 ASSERT(message->FindRef("refs", 0, &ref) == B_OK); 524 if (message->FindRef("refs", 0, &ref) != B_OK) 525 return; 526 527 #if B_BEOS_VERSION_DANO 528 set_default_printer(ref.name); 529 #else 530 // create a message for the print server 531 BMessenger messenger("application/x-vnd.Be-PSRV", -1); 532 if (!messenger.IsValid()) 533 return; 534 535 // send the selection to the print server 536 BMessage makeActiveMessage(PSV_MAKE_PRINTER_ACTIVE_QUIETLY); 537 makeActiveMessage.AddString("printer", ref.name); 538 539 BMessage reply; 540 messenger.SendMessage(&makeActiveMessage, &reply); 541 #endif 542 } 543 544 545 void 546 TTracker::MoveRefsToTrash(const BMessage *message) 547 { 548 int32 count; 549 uint32 type; 550 message->GetInfo("refs", &type, &count); 551 552 if (count <= 0) 553 return; 554 555 BObjectList<entry_ref> *srcList = new BObjectList<entry_ref>(count, true); 556 557 for (int32 index = 0; index < count; index++) { 558 559 entry_ref ref; 560 ASSERT(message->FindRef("refs", index, &ref) == B_OK); 561 if (message->FindRef("refs", index, &ref) != B_OK) 562 continue; 563 564 AutoLock<WindowList> lock(&fWindowList); 565 BContainerWindow *window = FindParentContainerWindow(&ref); 566 if (window) 567 // if we have a window open for this entry, ask the pose to 568 // delete it, this will select the next entry 569 window->PoseView()->MoveEntryToTrash(&ref); 570 else 571 // add all others to a list that gets deleted separately 572 srcList->AddItem(new entry_ref(ref)); 573 } 574 575 if (srcList->CountItems()) 576 // async move to trash 577 FSMoveToTrash(srcList); 578 } 579 580 581 template <class T, class FT> 582 class EntryAndNodeDoSoonWithMessageFunctor : public FunctionObjectWithResult<bool> { 583 public: 584 EntryAndNodeDoSoonWithMessageFunctor(FT func, T *target, const entry_ref *child, 585 const node_ref *parent, const BMessage *message) 586 : fFunc(func), 587 fTarget(target), 588 fNode(*parent), 589 fEntry(*child) 590 { 591 fSendMessage = (message != NULL); 592 if (message) 593 fMessage = *message; 594 } 595 596 virtual ~EntryAndNodeDoSoonWithMessageFunctor() {} 597 virtual void operator()() 598 { result = (fTarget->*fFunc)(&fEntry, &fNode, fSendMessage ? &fMessage : NULL); } 599 600 protected: 601 FT fFunc; 602 T *fTarget; 603 node_ref fNode; 604 entry_ref fEntry; 605 BMessage fMessage; 606 bool fSendMessage; 607 }; 608 609 610 bool 611 TTracker::LaunchAndCloseParentIfOK(const entry_ref *launchThis, 612 const node_ref *closeThis, const BMessage *messageToBundle) 613 { 614 BMessage refsReceived(B_REFS_RECEIVED); 615 if (messageToBundle) { 616 refsReceived = *messageToBundle; 617 refsReceived.what = B_REFS_RECEIVED; 618 } 619 refsReceived.AddRef("refs", launchThis); 620 // synchronous launch, we are already in our own thread 621 if (TrackerLaunch(&refsReceived, false) == B_OK) { 622 // if launched fine, close parent window in a bit 623 fTaskLoop->RunLater(NewMemberFunctionObject(&TTracker::CloseParent, this, *closeThis), 624 1000000); 625 } 626 return false; 627 } 628 629 630 status_t 631 TTracker::OpenRef(const entry_ref *ref, const node_ref *nodeToClose, 632 const node_ref *nodeToSelect, OpenSelector selector, 633 const BMessage *messageToBundle) 634 { 635 Model *model = NULL; 636 BEntry entry(ref, true); 637 status_t result = entry.InitCheck(); 638 639 bool brokenLinkWithSpecificHandler = false; 640 BString brokenLinkPreferredApp; 641 642 if (result != B_OK) { 643 model = new Model(ref, false); 644 if (model->IsSymLink() && !model->LinkTo()) { 645 model->GetPreferredAppForBrokenSymLink(brokenLinkPreferredApp); 646 if (brokenLinkPreferredApp.Length() && brokenLinkPreferredApp != kTrackerSignature) 647 brokenLinkWithSpecificHandler = true; 648 } 649 650 if (!brokenLinkWithSpecificHandler) { 651 delete model; 652 (new BAlert("", "There was an error resolving the link.", 653 "Cancel", 0, 0, 654 B_WIDTH_AS_USUAL, B_WARNING_ALERT))->Go(); 655 return result; 656 } 657 } else 658 model = new Model(&entry); 659 660 result = model->InitCheck(); 661 if (result != B_OK) { 662 delete model; 663 return result; 664 } 665 666 bool openAsContainer = model->IsContainer(); 667 668 if (openAsContainer && selector != kOpenWith) { 669 // if folder or query has a preferred handler and it's not the 670 // Tracker, open it by sending refs to the handling app 671 672 // if we are responding to the final open of OpenWith, just 673 // skip this and proceed to opening the container with Tracker 674 model->OpenNode(); 675 BNodeInfo nodeInfo(model->Node()); 676 char preferredApp[B_MIME_TYPE_LENGTH]; 677 if (nodeInfo.GetPreferredApp(preferredApp) == B_OK 678 && strcasecmp(preferredApp, kTrackerSignature) != 0) 679 openAsContainer = false; 680 model->CloseNode(); 681 } 682 683 if (openAsContainer || selector == kRunOpenWithWindow) { 684 // special case opening plain folders, queries or using open with 685 OpenContainerWindow(model, 0, selector, kRestoreDecor); // window adopts model 686 if (nodeToClose) 687 CloseParentWaitingForChildSoon(ref, nodeToClose); 688 } else if (model->IsQueryTemplate()) { 689 // query template - open new find window 690 (new FindWindow(model->EntryRef()))->Show(); 691 if (nodeToClose) 692 CloseParentWaitingForChildSoon(ref, nodeToClose); 693 } else { 694 delete model; 695 // run Launch in a separate thread 696 // and close parent if successfull 697 if (nodeToClose) 698 Thread::Launch(new EntryAndNodeDoSoonWithMessageFunctor<TTracker, 699 bool (TTracker::*)(const entry_ref *, const node_ref *, 700 const BMessage *)>(&TTracker::LaunchAndCloseParentIfOK, this, 701 ref, nodeToClose, messageToBundle)); 702 else { 703 BMessage refsReceived(B_REFS_RECEIVED); 704 if (messageToBundle) { 705 refsReceived = *messageToBundle; 706 refsReceived.what = B_REFS_RECEIVED; 707 } 708 refsReceived.AddRef("refs", ref); 709 if (brokenLinkWithSpecificHandler) 710 // This cruft is to support a hacky workaround for double-clicking 711 // broken refs for cifs; should get fixed in R5 712 LaunchBrokenLink(brokenLinkPreferredApp.String(), &refsReceived); 713 else 714 TrackerLaunch(&refsReceived, true); 715 } 716 } 717 if (nodeToSelect) 718 SelectChildInParentSoon(ref, nodeToSelect); 719 720 return B_OK; 721 } 722 723 724 void 725 TTracker::RefsReceived(BMessage *message) 726 { 727 OpenSelector selector = kOpen; 728 if (message->HasInt32("launchUsingSelector")) 729 selector = kRunOpenWithWindow; 730 731 entry_ref handlingApp; 732 if (message->FindRef("handler", &handlingApp) == B_OK) 733 selector = kOpenWith; 734 735 int32 count; 736 uint32 type; 737 message->GetInfo("refs", &type, &count); 738 739 switch (selector) { 740 case kRunOpenWithWindow: 741 OpenContainerWindow(0, message, selector); 742 // window adopts model 743 break; 744 745 case kOpenWith: 746 { 747 // Open With resulted in passing refs and a handler, open the files 748 // with the handling app 749 message->RemoveName("handler"); 750 751 // have to find out if handling app is the Tracker 752 // if it is, just pass it to the active Tracker, no matter which Tracker 753 // was chosen to handle the refs 754 char signature[B_MIME_TYPE_LENGTH]; 755 signature[0] = '\0'; 756 { 757 BFile handlingNode(&handlingApp, O_RDONLY); 758 BAppFileInfo appInfo(&handlingNode); 759 appInfo.GetSignature(signature); 760 } 761 762 if (strcasecmp(signature, kTrackerSignature) != 0) { 763 // handling app not Tracker, pass entries to the apps RefsReceived 764 TrackerLaunch(&handlingApp, message, true); 765 break; 766 } 767 // fall thru, opening refs by the Tracker, as if they were double clicked 768 } 769 770 case kOpen: 771 { 772 // copy over "Poses" messenger so that refs received recipients know 773 // where the open came from 774 BMessage *bundleThis = NULL; 775 BMessenger messenger; 776 if (message->FindMessenger("TrackerViewToken", &messenger) == B_OK) { 777 bundleThis = new BMessage(); 778 bundleThis->AddMessenger("TrackerViewToken", messenger); 779 } 780 781 for (int32 index = 0; index < count; index++) { 782 entry_ref ref; 783 message->FindRef("refs", index, &ref); 784 785 const node_ref *nodeToClose = NULL; 786 const node_ref *nodeToSelect = NULL; 787 ssize_t numBytes; 788 789 message->FindData("nodeRefsToClose", B_RAW_TYPE, index, 790 (const void **)&nodeToClose, &numBytes); 791 message->FindData("nodeRefToSelect", B_RAW_TYPE, index, 792 (const void **)&nodeToSelect, &numBytes); 793 794 OpenRef(&ref, nodeToClose, nodeToSelect, selector, bundleThis); 795 } 796 797 delete bundleThis; 798 break; 799 } 800 } 801 } 802 803 804 void 805 TTracker::ArgvReceived(int32 argc, char **argv) 806 { 807 BMessage *message = CurrentMessage(); 808 const char *currentWorkingDirectoryPath = NULL; 809 entry_ref ref; 810 811 if (message->FindString("cwd", ¤tWorkingDirectoryPath) == B_OK) { 812 BDirectory workingDirectory(currentWorkingDirectoryPath); 813 for (int32 index = 1; index < argc; index++) { 814 BEntry entry; 815 if (entry.SetTo(&workingDirectory, argv[index]) == B_OK 816 && entry.GetRef(&ref) == B_OK) 817 OpenRef(&ref); 818 else if (get_ref_for_path(argv[index], &ref) == B_OK) 819 OpenRef(&ref); 820 } 821 } 822 } 823 824 825 void 826 TTracker::OpenContainerWindow(Model *model, BMessage *originalRefsList, 827 OpenSelector openSelector, uint32 openFlags, bool checkAlreadyOpen, 828 const BMessage *stateMessage) 829 { 830 AutoLock<WindowList> lock(&fWindowList); 831 BContainerWindow *window = NULL; 832 if (checkAlreadyOpen && openSelector != kRunOpenWithWindow) 833 // find out if window already open 834 window = FindContainerWindow(model->NodeRef()); 835 836 bool someWindowActivated = false; 837 838 uint32 workspace = (uint32)(1 << current_workspace()); 839 int32 windowCount = 0; 840 841 while (window) { 842 // At least one window open, just pull to front 843 // make sure we don't jerk workspaces around 844 uint32 windowWorkspaces = window->Workspaces(); 845 if (windowWorkspaces & workspace) { 846 window->Activate(); 847 someWindowActivated = true; 848 } 849 window = FindContainerWindow(model->NodeRef(), ++windowCount); 850 } 851 852 if (someWindowActivated) { 853 delete model; 854 return; 855 } // If no window was actiated, (none in the current workspace 856 // we open a new one. 857 858 if (openSelector == kRunOpenWithWindow) { 859 BMessage *refList = NULL; 860 if (!originalRefsList) { 861 // when passing just a single model, stuff it's entry in a single 862 // element list anyway 863 ASSERT(model); 864 refList = new BMessage; 865 refList->AddRef("refs", model->EntryRef()); 866 delete model; 867 model = NULL; 868 } else 869 // clone the message, window adopts it for it's own use 870 refList = new BMessage(*originalRefsList); 871 window = new OpenWithContainerWindow(refList, &fWindowList); 872 } else if (model->IsRoot()) { 873 // window will adopt the model 874 window = new BVolumeWindow(&fWindowList, openFlags); 875 } else if (model->IsQuery()) { 876 // window will adopt the model 877 window = new BQueryContainerWindow(&fWindowList, openFlags); 878 } else 879 // window will adopt the model 880 window = new BContainerWindow(&fWindowList, openFlags); 881 882 if (model) 883 window->CreatePoseView(model); 884 885 BMessage restoreStateMessage(kRestoreState); 886 887 if (stateMessage) 888 restoreStateMessage.AddMessage("state", stateMessage); 889 890 window->PostMessage(&restoreStateMessage); 891 } 892 893 894 void 895 TTracker::EditQueries(const BMessage *message) 896 { 897 bool editOnlyIfTemplate; 898 if (message->FindBool("editQueryOnPose", &editOnlyIfTemplate) != B_OK) 899 editOnlyIfTemplate = false; 900 901 type_code type; 902 int32 count; 903 message->GetInfo("refs", &type, &count); 904 for (int32 index = 0; index < count; index++) { 905 entry_ref ref; 906 message->FindRef("refs", index, &ref); 907 BEntry entry(&ref, true); 908 if (entry.InitCheck() == B_OK && entry.Exists()) 909 (new FindWindow(&ref, editOnlyIfTemplate))->Show(); 910 } 911 } 912 913 914 void 915 TTracker::OpenInfoWindows(BMessage *message) 916 { 917 type_code type; 918 int32 count; 919 message->GetInfo("refs", &type, &count); 920 921 for (int32 index = 0; index < count; index++) { 922 entry_ref ref; 923 message->FindRef("refs", index, &ref); 924 BEntry entry; 925 if (entry.SetTo(&ref) == B_OK) { 926 Model *model = new Model(&entry); 927 if (model->InitCheck() != B_OK) { 928 delete model; 929 continue; 930 } 931 932 AutoLock<WindowList> lock(&fWindowList); 933 BInfoWindow *wind = FindInfoWindow(model->NodeRef()); 934 935 if (wind) { 936 wind->Activate(); 937 delete model; 938 } else { 939 wind = new BInfoWindow(model, index, &fWindowList); 940 wind->PostMessage(kRestoreState); 941 } 942 } 943 } 944 } 945 946 947 BDeskWindow * 948 TTracker::GetDeskWindow() const 949 { 950 int32 count = fWindowList.CountItems(); 951 for (int32 index = 0; index < count; index++) { 952 BDeskWindow *window = dynamic_cast<BDeskWindow *> 953 (fWindowList.ItemAt(index)); 954 955 if (window) 956 return window; 957 } 958 TRESPASS(); 959 return NULL; 960 } 961 962 963 BContainerWindow * 964 TTracker::FindContainerWindow(const node_ref *node, int32 number) const 965 { 966 ASSERT(fWindowList.IsLocked()); 967 968 int32 count = fWindowList.CountItems(); 969 970 int32 windowsFound = 0; 971 972 for (int32 index = 0; index < count; index++) { 973 BContainerWindow *window = dynamic_cast<BContainerWindow *> 974 (fWindowList.ItemAt(index)); 975 976 if (window && window->IsShowing(node) && number == windowsFound++) 977 return window; 978 } 979 return NULL; 980 } 981 982 983 BContainerWindow * 984 TTracker::FindContainerWindow(const entry_ref *entry, int32 number) const 985 { 986 ASSERT(fWindowList.IsLocked()); 987 988 int32 count = fWindowList.CountItems(); 989 990 int32 windowsFound = 0; 991 992 for (int32 index = 0; index < count; index++) { 993 BContainerWindow *window = dynamic_cast<BContainerWindow *> 994 (fWindowList.ItemAt(index)); 995 996 if (window && window->IsShowing(entry) && number == windowsFound++) 997 return window; 998 } 999 return NULL; 1000 } 1001 1002 1003 bool 1004 TTracker::EntryHasWindowOpen(const entry_ref *entry) 1005 { 1006 AutoLock<WindowList> lock(&fWindowList); 1007 return FindContainerWindow(entry) != NULL; 1008 } 1009 1010 1011 BContainerWindow * 1012 TTracker::FindParentContainerWindow(const entry_ref *ref) const 1013 { 1014 BEntry entry(ref); 1015 BEntry parent; 1016 1017 if (entry.GetParent(&parent) != B_OK) 1018 return NULL; 1019 1020 entry_ref parentRef; 1021 parent.GetRef(&parentRef); 1022 1023 ASSERT(fWindowList.IsLocked()); 1024 1025 int32 count = fWindowList.CountItems(); 1026 for (int32 index = 0; index < count; index++) { 1027 BContainerWindow *window = dynamic_cast<BContainerWindow *> 1028 (fWindowList.ItemAt(index)); 1029 if (window && window->IsShowing(&parentRef)) 1030 return window; 1031 } 1032 return NULL; 1033 } 1034 1035 1036 BInfoWindow * 1037 TTracker::FindInfoWindow(const node_ref* node) const 1038 { 1039 ASSERT(fWindowList.IsLocked()); 1040 1041 int32 count = fWindowList.CountItems(); 1042 for (int32 index = 0; index < count; index++) { 1043 BInfoWindow *window = dynamic_cast<BInfoWindow *> 1044 (fWindowList.ItemAt(index)); 1045 if (window && window->IsShowing(node)) 1046 return window; 1047 } 1048 return NULL; 1049 } 1050 1051 1052 bool 1053 TTracker::QueryActiveForDevice(dev_t device) 1054 { 1055 AutoLock<WindowList> lock(&fWindowList); 1056 int32 count = fWindowList.CountItems(); 1057 for (int32 index = 0; index < count; index++) { 1058 BQueryContainerWindow *window = dynamic_cast<BQueryContainerWindow *> 1059 (fWindowList.ItemAt(index)); 1060 if (window) { 1061 AutoLock<BWindow> lock(window); 1062 if (window->ActiveOnDevice(device)) 1063 return true; 1064 } 1065 } 1066 return false; 1067 } 1068 1069 1070 void 1071 TTracker::CloseActiveQueryWindows(dev_t device) 1072 { 1073 // used when trying to unmount a volume - an active query would prevent that from 1074 // happening 1075 bool closed = false; 1076 AutoLock<WindowList> lock(fWindowList); 1077 for (int32 index = fWindowList.CountItems(); index >= 0; index--) { 1078 BQueryContainerWindow *window = dynamic_cast<BQueryContainerWindow *> 1079 (fWindowList.ItemAt(index)); 1080 if (window) { 1081 AutoLock<BWindow> lock(window); 1082 if (window->ActiveOnDevice(device)) { 1083 window->PostMessage(B_QUIT_REQUESTED); 1084 closed = true; 1085 } 1086 } 1087 } 1088 lock.Unlock(); 1089 if (closed) 1090 for (int32 timeout = 30; timeout; timeout--) { 1091 // wait a bit for windows to fully close 1092 if (!QueryActiveForDevice(device)) 1093 return; 1094 snooze(100000); 1095 } 1096 } 1097 1098 1099 void 1100 TTracker::SaveAllPoseLocations() 1101 { 1102 int32 numWindows = fWindowList.CountItems(); 1103 for (int32 windowIndex = 0; windowIndex < numWindows; windowIndex++) { 1104 BContainerWindow *window = dynamic_cast<BContainerWindow *> 1105 (fWindowList.ItemAt(windowIndex)); 1106 1107 if (window) { 1108 AutoLock<BWindow> lock(window); 1109 BDeskWindow *deskWindow = dynamic_cast<BDeskWindow *>(window); 1110 1111 if (deskWindow) 1112 deskWindow->SaveDesktopPoseLocations(); 1113 else 1114 window->PoseView()->SavePoseLocations(); 1115 } 1116 } 1117 } 1118 1119 1120 void 1121 TTracker::CloseWindowAndChildren(const node_ref *node) 1122 { 1123 BDirectory dir(node); 1124 if (dir.InitCheck() != B_OK) 1125 return; 1126 1127 AutoLock<WindowList> lock(&fWindowList); 1128 BObjectList<BContainerWindow> closeList; 1129 1130 // make a list of all windows to be closed 1131 // count from end to beginning so we can remove items safely 1132 for (int32 index = fWindowList.CountItems() - 1; index >= 0; index--) { 1133 BContainerWindow *window = dynamic_cast<BContainerWindow *> 1134 (fWindowList.ItemAt(index)); 1135 if (window && window->TargetModel()) { 1136 BEntry wind_entry; 1137 wind_entry.SetTo(window->TargetModel()->EntryRef()); 1138 1139 if ((*window->TargetModel()->NodeRef() == *node) 1140 || dir.Contains(&wind_entry)) { 1141 1142 // ToDo: 1143 // get rid of the Remove here, BContainerWindow::Quit does it 1144 fWindowList.RemoveItemAt(index); 1145 closeList.AddItem(window); 1146 } 1147 } 1148 } 1149 1150 // now really close the windows 1151 int32 numItems = closeList.CountItems(); 1152 for (int32 index = 0; index < numItems; index++) { 1153 BContainerWindow *window = closeList.ItemAt(index); 1154 window->PostMessage(B_QUIT_REQUESTED); 1155 } 1156 } 1157 1158 1159 void 1160 TTracker::CloseAllWindows() 1161 { 1162 // this is a response to the DeskBar sending us a B_QUIT, when it really 1163 // means to say close all your windows. It might be better to have it 1164 // send a kCloseAllWindows message and have windowless apps stay running, 1165 // which is what we will do for the Tracker 1166 AutoLock<WindowList> lock(&fWindowList); 1167 1168 int32 count = CountWindows(); 1169 for (int32 index = 0; index < count; index++) { 1170 BWindow *window = WindowAt(index); 1171 // avoid the desktop 1172 if (!dynamic_cast<BDeskWindow *>(window) 1173 && !dynamic_cast<BStatusWindow *>(window)) 1174 window->PostMessage(B_QUIT_REQUESTED); 1175 } 1176 // count from end to beginning so we can remove items safely 1177 for (int32 index = fWindowList.CountItems() - 1; index >= 0; index--) { 1178 BWindow *window = fWindowList.ItemAt(index); 1179 if (!dynamic_cast<BDeskWindow *>(window) 1180 && !dynamic_cast<BStatusWindow *>(window)) 1181 // ToDo: 1182 // get rid of the Remove here, BContainerWindow::Quit does it 1183 fWindowList.RemoveItemAt(index); 1184 } 1185 } 1186 1187 1188 void 1189 TTracker::ReadyToRun() 1190 { 1191 gStatusWindow = new BStatusWindow(); 1192 InitMimeTypes(); 1193 InstallDefaultTemplates(); 1194 InstallIndices(); 1195 1196 HideVarDir(); 1197 1198 fTrashWatcher = new BTrashWatcher(); 1199 fTrashWatcher->Run(); 1200 1201 fClipboardRefsWatcher = new BClipboardRefsWatcher(); 1202 fClipboardRefsWatcher->Run(); 1203 1204 fAutoMounter = new AutoMounter(); 1205 fAutoMounter->Run(); 1206 1207 fTaskLoop = new StandAloneTaskLoop(true); 1208 1209 bool openDisksWindow = false; 1210 1211 // open desktop window 1212 BContainerWindow *deskWindow = NULL; 1213 BVolume bootVol; 1214 BVolumeRoster().GetBootVolume(&bootVol); 1215 BDirectory deskDir; 1216 if (FSGetDeskDir(&deskDir, bootVol.Device()) == B_OK) { 1217 // create desktop 1218 BEntry entry; 1219 deskDir.GetEntry(&entry); 1220 Model *model = new Model(&entry); 1221 if (model->InitCheck() == B_OK) { 1222 AutoLock<WindowList> lock(&fWindowList); 1223 deskWindow = new BDeskWindow(&fWindowList); 1224 AutoLock<BWindow> windowLock(deskWindow); 1225 deskWindow->CreatePoseView(model); 1226 deskWindow->Init(); 1227 } else 1228 delete model; 1229 1230 // open previously open windows 1231 attr_info attrInfo; 1232 if (!BootedInSafeMode() 1233 && deskDir.GetAttrInfo(kAttrOpenWindows, &attrInfo) == B_OK) { 1234 char *buffer = (char *)malloc((size_t)attrInfo.size); 1235 BMessage message; 1236 if (deskDir.ReadAttr(kAttrOpenWindows, B_MESSAGE_TYPE, 0, buffer, (size_t)attrInfo.size) 1237 == attrInfo.size 1238 && message.Unflatten(buffer) == B_OK) { 1239 1240 node_ref nodeRef; 1241 deskDir.GetNodeRef(&nodeRef); 1242 1243 int32 stateMessageCounter = 0; 1244 const char *path; 1245 for (int32 outer = 0;message.FindString("paths", outer, &path) == B_OK;outer++) { 1246 int8 flags = 0; 1247 for (int32 inner = 0;message.FindInt8(path, inner, &flags) == B_OK;inner++) { 1248 BEntry entry(path, true); 1249 if (entry.InitCheck() == B_OK) { 1250 Model *model = new Model(&entry); 1251 if (model->InitCheck() == B_OK && model->IsContainer()) { 1252 BMessage state; 1253 bool restoreStateFromMessage = false; 1254 if ((flags & kOpenWindowHasState) != 0 1255 && message.FindMessage("window state", stateMessageCounter++, &state) == B_OK) 1256 restoreStateFromMessage = true; 1257 1258 if (restoreStateFromMessage) 1259 OpenContainerWindow(model, 0, kOpen, 1260 kRestoreWorkspace | (flags & kOpenWindowMinimized ? kIsHidden : 0U) | kRestoreDecor, 1261 false, &state); 1262 else 1263 OpenContainerWindow(model, 0, kOpen, 1264 kRestoreWorkspace | (flags & kOpenWindowMinimized ? kIsHidden : 0U) | kRestoreDecor); 1265 } else 1266 delete model; 1267 } 1268 } 1269 } 1270 1271 if (message.HasBool("open_disks_window")) 1272 openDisksWindow = true; 1273 } 1274 free(buffer); 1275 } 1276 } 1277 1278 // create model for root of everything 1279 if (deskWindow) { 1280 BEntry entry("/"); 1281 Model model(&entry); 1282 if (model.InitCheck() == B_OK) { 1283 1284 if (TrackerSettings().ShowDisksIcon()) { 1285 // add the root icon to desktop window 1286 BMessage message; 1287 message.what = B_NODE_MONITOR; 1288 message.AddInt32("opcode", B_ENTRY_CREATED); 1289 message.AddInt32("device", model.NodeRef()->device); 1290 message.AddInt64("node", model.NodeRef()->node); 1291 message.AddInt64("directory", model.EntryRef()->directory); 1292 message.AddString("name", model.EntryRef()->name); 1293 deskWindow->PostMessage(&message, deskWindow->PoseView()); 1294 } 1295 1296 if (openDisksWindow) 1297 OpenContainerWindow(new Model(model), 0, kOpen, kRestoreWorkspace); 1298 } 1299 } 1300 1301 // kick off building the mime type list for find panels, etc. 1302 fMimeTypeList = new MimeTypeList(); 1303 1304 if (!BootedInSafeMode()) 1305 // kick of transient query killer 1306 DeleteTransientQueriesTask::StartUpTransientQueryCleaner(); 1307 } 1308 1309 MimeTypeList * 1310 TTracker::MimeTypes() const 1311 { 1312 return fMimeTypeList; 1313 } 1314 1315 void 1316 TTracker::SelectChildInParentSoon(const entry_ref *parent, 1317 const node_ref *child) 1318 { 1319 fTaskLoop->RunLater(NewMemberFunctionObjectWithResult 1320 (&TTracker::SelectChildInParent, this, parent, child), 1321 100000, 200000, 5000000); 1322 } 1323 1324 void 1325 TTracker::CloseParentWaitingForChildSoon(const entry_ref *child, 1326 const node_ref *parent) 1327 { 1328 fTaskLoop->RunLater(NewMemberFunctionObjectWithResult 1329 (&TTracker::CloseParentWaitingForChild, this, child, parent), 1330 200000, 100000, 5000000); 1331 } 1332 1333 void 1334 TTracker::SelectPoseAtLocationSoon(node_ref parent, BPoint pointInPose) 1335 { 1336 fTaskLoop->RunLater(NewMemberFunctionObject 1337 (&TTracker::SelectPoseAtLocationInParent, this, parent, pointInPose), 1338 100000); 1339 } 1340 1341 void 1342 TTracker::SelectPoseAtLocationInParent(node_ref parent, BPoint pointInPose) 1343 { 1344 AutoLock<WindowList> lock(&fWindowList); 1345 BContainerWindow *parentWindow = FindContainerWindow(&parent); 1346 if (parentWindow) { 1347 AutoLock<BWindow> lock(parentWindow); 1348 parentWindow->PoseView()->SelectPoseAtLocation(pointInPose); 1349 } 1350 } 1351 1352 bool 1353 TTracker::CloseParentWaitingForChild(const entry_ref *child, 1354 const node_ref *parent) 1355 { 1356 AutoLock<WindowList> lock(&fWindowList); 1357 1358 BContainerWindow *parentWindow = FindContainerWindow(parent); 1359 if (!parentWindow) 1360 // parent window already closed, give up 1361 return true; 1362 1363 // If child is a symbolic link, dereference it, so that 1364 // FindContainerWindow will succeed. 1365 BEntry entry(child, true); 1366 entry_ref resolvedChild; 1367 if (entry.GetRef(&resolvedChild) != B_OK) 1368 resolvedChild = *child; 1369 1370 BContainerWindow *window = FindContainerWindow(&resolvedChild); 1371 if (window) { 1372 AutoLock<BWindow> lock(window); 1373 if (!window->IsHidden()) 1374 return CloseParentWindowCommon(parentWindow); 1375 } 1376 return false; 1377 } 1378 1379 void 1380 TTracker::CloseParent(node_ref parent) 1381 { 1382 AutoLock<WindowList> lock(&fWindowList); 1383 if (!lock) 1384 return; 1385 1386 CloseParentWindowCommon(FindContainerWindow(&parent)); 1387 } 1388 1389 void 1390 TTracker::ShowSettingsWindow() 1391 { 1392 if (!fSettingsWindow) { 1393 fSettingsWindow = new TrackerSettingsWindow(); 1394 fSettingsWindow->Show(); 1395 } else { 1396 if (fSettingsWindow->Lock()) { 1397 if (fSettingsWindow->IsHidden()) 1398 fSettingsWindow->Show(); 1399 else 1400 fSettingsWindow->Activate(); 1401 fSettingsWindow->Unlock(); 1402 } 1403 } 1404 } 1405 1406 bool 1407 TTracker::CloseParentWindowCommon(BContainerWindow *window) 1408 { 1409 ASSERT(fWindowList.IsLocked()); 1410 1411 if (dynamic_cast<BDeskWindow *>(window)) 1412 // don't close the destop 1413 return false; 1414 1415 window->PostMessage(B_QUIT_REQUESTED); 1416 return true; 1417 } 1418 1419 bool 1420 TTracker::SelectChildInParent(const entry_ref *parent, const node_ref *child) 1421 { 1422 AutoLock<WindowList> lock(&fWindowList); 1423 1424 BContainerWindow *window = FindContainerWindow(parent); 1425 if (!window) 1426 // parent window already closed, give up 1427 return false; 1428 1429 AutoLock<BWindow> windowLock(window); 1430 1431 if (windowLock.IsLocked()) { 1432 BPoseView *view = window->PoseView(); 1433 int32 index; 1434 BPose *pose = view->FindPose(child, &index); 1435 if (pose) { 1436 view->SelectPose(pose, index); 1437 return true; 1438 } 1439 } 1440 return false; 1441 } 1442 1443 const int32 kNodeMonitorBumpValue = 512; 1444 1445 status_t 1446 TTracker::NeedMoreNodeMonitors() 1447 { 1448 fNodeMonitorCount += kNodeMonitorBumpValue; 1449 PRINT(("bumping nodeMonitorCount to %d\n", fNodeMonitorCount)); 1450 1451 return _kset_mon_limit_(fNodeMonitorCount); 1452 } 1453 1454 status_t 1455 TTracker::WatchNode(const node_ref *node, uint32 flags, 1456 BMessenger target) 1457 { 1458 status_t result = watch_node(node, flags, target); 1459 if (result == B_OK || result != B_NO_MEMORY) { 1460 // need to make sure this uses the same error value as 1461 // the node monitor code 1462 return result; 1463 } 1464 1465 PRINT(("failed to start monitoring, trying to allocate more " 1466 "node monitors\n")); 1467 1468 TTracker *tracker = dynamic_cast<TTracker *>(be_app); 1469 if (!tracker) { 1470 // we are the file panel only, just fail 1471 return result; 1472 } 1473 1474 result = tracker->NeedMoreNodeMonitors(); 1475 1476 if (result != B_OK) { 1477 PRINT(("failed to allocate more node monitors, %s\n", 1478 strerror(result))); 1479 return result; 1480 } 1481 1482 // try again, this time with more node monitors 1483 return watch_node(node, flags, target); 1484 } 1485 1486 1487 AutoMounter * 1488 TTracker::AutoMounterLoop() 1489 { 1490 return fAutoMounter; 1491 } 1492 1493 1494 bool 1495 TTracker::InTrashNode(const entry_ref *node) const 1496 { 1497 return FSInTrashDir(node); 1498 } 1499 1500 1501 bool 1502 TTracker::TrashFull() const 1503 { 1504 return fTrashWatcher->CheckTrashDirs(); 1505 } 1506 1507 1508 bool 1509 TTracker::IsTrashNode(const node_ref *node) const 1510 { 1511 return fTrashWatcher->IsTrashNode(node); 1512 } 1513 1514