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