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