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