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