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 "FunctionObject.h" 71 #include "FSClipboard.h" 72 #include "FSUtils.h" 73 #include "InfoWindow.h" 74 #include "MimeTypes.h" 75 #include "MimeTypeList.h" 76 #include "NodePreloader.h" 77 #include "OpenWithWindow.h" 78 #include "PoseView.h" 79 #include "QueryContainerWindow.h" 80 #include "StatusWindow.h" 81 #include "TaskLoop.h" 82 #include "Thread.h" 83 #include "TrackerSettings.h" 84 #include "TrackerSettingsWindow.h" 85 #include "TrackerString.h" 86 #include "TrashWatcher.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 ssize_t size = message.FlattenedSize(); 432 if (size > 0) { 433 char* buffer = new char[size]; 434 message.Flatten(buffer, size); 435 deskDir.WriteAttr(kAttrOpenWindows, B_MESSAGE_TYPE, 0, buffer, 436 size); 437 delete[] buffer; 438 } 439 } else 440 deskDir.RemoveAttr(kAttrOpenWindows); 441 } 442 443 for (int32 count = 0; count < 50; count++) { 444 // wait 5 seconds for the copiing/moving to quit 445 if (gStatusWindow->AttemptToQuit()) 446 break; 447 448 snooze(100000); 449 } 450 451 return _inherited::QuitRequested(); 452 } 453 454 455 void 456 TTracker::Quit() 457 { 458 TrackerSettings().SaveSettings(false); 459 460 fClipboardRefsWatcher->Lock(); 461 fClipboardRefsWatcher->Quit(); 462 463 fTrashWatcher->Lock(); 464 fTrashWatcher->Quit(); 465 466 WellKnowEntryList::Quit(); 467 468 delete gPreloader; 469 delete fTaskLoop; 470 delete IconCache::sIconCache; 471 472 _inherited::Quit(); 473 } 474 475 476 void 477 TTracker::MessageReceived(BMessage* message) 478 { 479 if (HandleScriptingMessage(message)) 480 return; 481 482 switch (message->what) { 483 case kGetInfo: 484 OpenInfoWindows(message); 485 break; 486 487 case kMoveToTrash: 488 MoveRefsToTrash(message); 489 break; 490 491 case kSelect: 492 SelectRefs(message); 493 break; 494 495 case kCloseWindowAndChildren: 496 { 497 const node_ref* itemNode; 498 ssize_t bytes; 499 if (message->FindData("node_ref", B_RAW_TYPE, 500 (const void**)&itemNode, &bytes) == B_OK) { 501 CloseWindowAndChildren(itemNode); 502 } 503 break; 504 } 505 506 case kCloseAllWindows: 507 CloseAllWindows(); 508 break; 509 510 case kCloseAllInWorkspace: 511 CloseAllInWorkspace(); 512 break; 513 514 case kFindButton: 515 (new FindWindow())->Show(); 516 break; 517 518 case kEditQuery: 519 EditQueries(message); 520 break; 521 522 case kShowSplash: 523 run_be_about(); 524 break; 525 526 case kAddPrinter: 527 // show the addprinter window 528 run_add_printer_panel(); 529 break; 530 531 case kMakeActivePrinter: 532 // get the current selection 533 SetDefaultPrinter(message); 534 break; 535 536 #ifdef MOUNT_MENU_IN_DESKBAR 537 case 'gmtv': 538 { 539 // Someone (probably the deskbar) has requested a list of 540 // mountable volumes. 541 BMessage reply; 542 AutoMounterLoop()->EachMountableItemAndFloppy( 543 &AddMountableItemToMessage, &reply); 544 message->SendReply(&reply); 545 break; 546 } 547 #endif 548 549 case kUnmountVolume: 550 // When the user attempts to unmount a volume from the mount 551 // context menu, this is where the message gets received. 552 // Save pose locations and forward this to the automounter 553 SaveAllPoseLocations(); 554 // Fall through... 555 case kMountVolume: 556 case kMountAllNow: 557 MountServer().SendMessage(message); 558 break; 559 560 561 case kRestoreBackgroundImage: 562 { 563 BDeskWindow* desktop = GetDeskWindow(); 564 AutoLock<BWindow> lock(desktop); 565 desktop->UpdateDesktopBackgroundImages(); 566 break; 567 } 568 569 case kRunAutomounterSettings: 570 ShowSettingsWindow(); 571 fSettingsWindow->ShowPage( 572 TrackerSettingsWindow::kAutomountSettings); 573 break; 574 575 case kShowSettingsWindow: 576 ShowSettingsWindow(); 577 break; 578 579 case kFavoriteCountChangedExternally: 580 SendNotices(kFavoriteCountChangedExternally, message); 581 break; 582 583 case kStartWatchClipboardRefs: 584 { 585 BMessenger messenger; 586 message->FindMessenger("target", &messenger); 587 if (messenger.IsValid()) 588 fClipboardRefsWatcher->AddToNotifyList(messenger); 589 break; 590 } 591 592 case kStopWatchClipboardRefs: 593 { 594 BMessenger messenger; 595 if (message->FindMessenger("target", &messenger) == B_OK 596 && messenger.IsValid()) { 597 fClipboardRefsWatcher->RemoveFromNotifyList(messenger); 598 } 599 break; 600 } 601 602 case kFSClipboardChanges: 603 fClipboardRefsWatcher->UpdatePoseViews(message); 604 break; 605 606 case kShowVolumeSpaceBar: 607 case kSpaceBarColorChanged: 608 gPeriodicUpdatePoses.DoPeriodicUpdate(true); 609 break; 610 611 case B_LOCALE_CHANGED: 612 { 613 BLocaleRoster::Default()->Refresh(); 614 bool localize; 615 if (message->FindBool("filesys", &localize) == B_OK) 616 gLocalizedNamePreferred = localize; 617 break; 618 } 619 620 case kUpdateThumbnail: 621 { 622 // message passed from generator thread 623 // update icon on passed-in node_ref 624 node_ref noderef; 625 if (message->FindNodeRef("noderef", &noderef) == B_OK) { 626 // cycle through open windows to find the node's pose 627 // TODO find a faster way 628 AutoLock<WindowList> lock(&fWindowList); 629 int32 count = fWindowList.CountItems(); 630 for (int32 index = 0; index < count; index++) { 631 BContainerWindow* window = dynamic_cast<BContainerWindow*>( 632 fWindowList.ItemAt(index)); 633 if (window == NULL) 634 continue; 635 636 AutoLock<BWindow> windowLock(window); 637 if (!windowLock.IsLocked()) 638 continue; 639 640 BPoseView* poseView = window->PoseView(); 641 if (poseView == NULL) 642 continue; 643 644 BPose* pose = poseView->FindPose(&noderef); 645 if (pose != NULL) { 646 poseView->UpdateIcon(pose); 647 break; // updated pose icon, exit loop 648 } 649 } 650 } 651 break; 652 } 653 654 default: 655 _inherited::MessageReceived(message); 656 break; 657 } 658 } 659 660 661 void 662 TTracker::Pulse() 663 { 664 if (!TrackerSettings().ShowVolumeSpaceBar()) 665 return; 666 667 // update the volume icon's free space bars 668 gPeriodicUpdatePoses.DoPeriodicUpdate(false); 669 } 670 671 672 void 673 TTracker::SetDefaultPrinter(const BMessage* message) 674 { 675 // get the first item selected 676 int32 count = 0; 677 uint32 type = 0; 678 message->GetInfo("refs", &type, &count); 679 680 if (count <= 0) 681 return; 682 683 // will make the first item the default printer, disregards any 684 // other files 685 entry_ref ref; 686 ASSERT(message->FindRef("refs", 0, &ref) == B_OK); 687 if (message->FindRef("refs", 0, &ref) != B_OK) 688 return; 689 690 #if B_BEOS_VERSION_DANO 691 set_default_printer(ref.name); 692 #else 693 // create a message for the print server 694 BMessenger messenger("application/x-vnd.Be-PSRV", -1); 695 if (!messenger.IsValid()) 696 return; 697 698 // send the selection to the print server 699 BMessage makeActiveMessage(PSV_MAKE_PRINTER_ACTIVE_QUIETLY); 700 makeActiveMessage.AddString("printer", ref.name); 701 702 BMessage reply; 703 messenger.SendMessage(&makeActiveMessage, &reply); 704 #endif 705 } 706 707 708 void 709 TTracker::MoveRefsToTrash(const BMessage* message) 710 { 711 int32 count; 712 uint32 type; 713 message->GetInfo("refs", &type, &count); 714 715 if (count <= 0) 716 return; 717 718 BObjectList<entry_ref>* srcList = new BObjectList<entry_ref>(count, true); 719 720 for (int32 index = 0; index < count; index++) { 721 entry_ref ref; 722 ASSERT(message->FindRef("refs", index, &ref) == B_OK); 723 if (message->FindRef("refs", index, &ref) != B_OK) 724 continue; 725 726 AutoLock<WindowList> lock(&fWindowList); 727 BContainerWindow* window = FindParentContainerWindow(&ref); 728 if (window != NULL) { 729 // if we have a window open for this entry, ask the pose to 730 // delete it, this will select the next entry 731 window->PoseView()->MoveEntryToTrash(&ref); 732 } else { 733 // add all others to a list that gets deleted separately 734 srcList->AddItem(new entry_ref(ref)); 735 } 736 } 737 738 // async move to trash 739 FSMoveToTrash(srcList); 740 } 741 742 743 void 744 TTracker::SelectRefs(const BMessage* message) 745 { 746 uint32 type = 0; 747 int32 count = 0; 748 message->GetInfo("refs", &type, &count); 749 750 for (int32 index = 0; index < count; index++) { 751 entry_ref ref; 752 message->FindRef("refs", index, &ref); 753 BEntry entry(&ref, true); 754 if (entry.InitCheck() != B_OK || !entry.Exists()) 755 continue; 756 757 AutoLock<WindowList> lock(&fWindowList); 758 BContainerWindow* window = FindParentContainerWindow(&ref); 759 if (window == NULL) 760 continue; 761 762 char name[B_FILE_NAME_LENGTH]; 763 if (entry.GetName(name) != B_OK) 764 continue; 765 766 BString expression; 767 expression << "^"; 768 expression << name; 769 expression << "$"; 770 771 BMessage* selectMessage = new BMessage(kSelectMatchingEntries); 772 selectMessage->AddInt32("ExpressionType", kRegexpMatch); 773 selectMessage->AddString("Expression", expression); 774 selectMessage->AddBool("InvertSelection", false); 775 selectMessage->AddBool("IgnoreCase", false); 776 777 window->Activate(); 778 // must be activated to populate the pose list 779 780 snooze(100000); 781 // wait a bit for the pose list to be populated 782 // ToDo: figure out why this is necessary 783 784 window->PostMessage(selectMessage); 785 } 786 } 787 788 789 template <class T, class FT> 790 class EntryAndNodeDoSoonWithMessageFunctor : public 791 FunctionObjectWithResult<bool> { 792 public: 793 EntryAndNodeDoSoonWithMessageFunctor(FT func, T* target, 794 const entry_ref* child, const node_ref* parent, 795 const BMessage* message) 796 : 797 fFunc(func), 798 fTarget(target), 799 fNode(*parent), 800 fEntry(*child) 801 { 802 fSendMessage = message != NULL; 803 if (message != NULL) 804 fMessage = *message; 805 } 806 807 virtual ~EntryAndNodeDoSoonWithMessageFunctor() {} 808 virtual void operator()() 809 { 810 result = (fTarget->*fFunc)(&fEntry, &fNode, 811 fSendMessage ? &fMessage : NULL); 812 } 813 814 protected: 815 FT fFunc; 816 T* fTarget; 817 node_ref fNode; 818 entry_ref fEntry; 819 BMessage fMessage; 820 bool fSendMessage; 821 }; 822 823 824 bool 825 TTracker::LaunchAndCloseParentIfOK(const entry_ref* launchThis, 826 const node_ref* closeThis, const BMessage* messageToBundle) 827 { 828 BMessage refsReceived(B_REFS_RECEIVED); 829 if (messageToBundle != NULL) { 830 refsReceived = *messageToBundle; 831 refsReceived.what = B_REFS_RECEIVED; 832 } 833 refsReceived.AddRef("refs", launchThis); 834 // synchronous launch, we are already in our own thread 835 if (TrackerLaunch(&refsReceived, false) == B_OK) { 836 // if launched fine, close parent window in a bit 837 fTaskLoop->RunLater(NewMemberFunctionObject(&TTracker::CloseParent, 838 this, *closeThis), 1000000); 839 } 840 841 return false; 842 } 843 844 845 status_t 846 TTracker::OpenRef(const entry_ref* ref, const node_ref* nodeToClose, 847 const node_ref* nodeToSelect, OpenSelector selector, 848 const BMessage* messageToBundle) 849 { 850 Model* model = NULL; 851 BEntry entry(ref, true); 852 status_t result = entry.InitCheck(); 853 854 if (result != B_OK) { 855 BAlert* alert = new BAlert("", 856 B_TRANSLATE("There was an error resolving the link."), 857 B_TRANSLATE("Cancel"), 0, 0, B_WIDTH_AS_USUAL, 858 B_WARNING_ALERT); 859 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 860 alert->Go(); 861 862 return result; 863 } else 864 model = new Model(&entry); 865 866 result = model->InitCheck(); 867 if (result != B_OK) { 868 delete model; 869 return result; 870 } 871 872 bool openAsContainer = model->IsContainer(); 873 874 if (openAsContainer && selector != kOpenWith) { 875 // if folder or query has a preferred handler and it's not the 876 // Tracker, open it by sending refs to the handling app 877 878 // if we are responding to the final open of OpenWith, just 879 // skip this and proceed to opening the container with Tracker 880 model->OpenNode(); 881 BNodeInfo nodeInfo(model->Node()); 882 char preferredApp[B_MIME_TYPE_LENGTH]; 883 if (nodeInfo.GetPreferredApp(preferredApp) == B_OK 884 && strcasecmp(preferredApp, kTrackerSignature) != 0) { 885 openAsContainer = false; 886 } 887 model->CloseNode(); 888 } 889 890 if (openAsContainer || selector == kRunOpenWithWindow) { 891 // special case opening plain folders, queries or using open with 892 OpenContainerWindow(model, NULL, selector, kRestoreDecor); 893 // window adopts model 894 if (nodeToClose) 895 CloseParentWaitingForChildSoon(ref, nodeToClose); 896 } else if (model->IsQueryTemplate()) { 897 // query template - open new find window 898 (new FindWindow(model->EntryRef()))->Show(); 899 900 delete model; 901 if (nodeToClose) 902 CloseParentWaitingForChildSoon(ref, nodeToClose); 903 } else { 904 delete model; 905 // run Launch in a separate thread and close parent if successful 906 if (nodeToClose) { 907 Thread::Launch(new EntryAndNodeDoSoonWithMessageFunctor<TTracker, 908 bool (TTracker::*)(const entry_ref*, const node_ref*, 909 const BMessage*)>(&TTracker::LaunchAndCloseParentIfOK, this, 910 ref, nodeToClose, messageToBundle)); 911 } else { 912 BMessage refsReceived(B_REFS_RECEIVED); 913 if (messageToBundle) { 914 refsReceived = *messageToBundle; 915 refsReceived.what = B_REFS_RECEIVED; 916 } 917 refsReceived.AddRef("refs", ref); 918 TrackerLaunch(&refsReceived, true); 919 } 920 } 921 922 if (nodeToSelect) 923 SelectChildInParentSoon(ref, nodeToSelect); 924 925 return B_OK; 926 } 927 928 929 void 930 TTracker::RefsReceived(BMessage* message) 931 { 932 OpenSelector selector = kOpen; 933 if (message->HasInt32("launchUsingSelector")) 934 selector = kRunOpenWithWindow; 935 936 entry_ref handlingApp; 937 if (message->FindRef("handler", &handlingApp) == B_OK) 938 selector = kOpenWith; 939 940 int32 count; 941 uint32 type; 942 message->GetInfo("refs", &type, &count); 943 944 switch (selector) { 945 case kRunOpenWithWindow: 946 OpenContainerWindow(NULL, message, selector); 947 // window adopts model 948 break; 949 950 case kOpenWith: 951 { 952 // Open With resulted in passing refs and a handler, 953 // open the files with the handling app 954 message->RemoveName("handler"); 955 956 // have to find out if handling app is the Tracker 957 // if it is, just pass it to the active Tracker, 958 // no matter which Tracker was chosen to handle the refs 959 char signature[B_MIME_TYPE_LENGTH]; 960 signature[0] = '\0'; 961 { 962 BFile handlingNode(&handlingApp, O_RDONLY); 963 BAppFileInfo appInfo(&handlingNode); 964 appInfo.GetSignature(signature); 965 } 966 967 if (strcasecmp(signature, kTrackerSignature) != 0) { 968 // handling app not Tracker, pass entries to the apps 969 // RefsReceived 970 TrackerLaunch(&handlingApp, message, true); 971 break; 972 } 973 } 974 // fall thru, opening refs by the Tracker as if they were 975 // double-clicked 976 case kOpen: 977 { 978 // copy over "Poses" messenger so that refs received 979 // recipients know where the open came from 980 BMessage* bundleThis = NULL; 981 BMessage stackBundleThis; 982 BMessenger messenger; 983 if (message->FindMessenger("TrackerViewToken", &messenger) 984 == B_OK) { 985 bundleThis = &stackBundleThis; 986 bundleThis->AddMessenger("TrackerViewToken", messenger); 987 } else { 988 // copy over any "be:*" fields -- e.g. /bin/open may include 989 // "be:line" and "be:column" 990 for (int32 i = 0;; i++) { 991 char* name; 992 type_code type; 993 int32 count; 994 status_t error = message->GetInfo(B_ANY_TYPE, i, &name, 995 &type, &count); 996 if (error != B_OK) 997 break; 998 999 if (strncmp(name, "be:", 3) != 0) 1000 continue; 1001 1002 for (int32 k = 0; k < count; k++) { 1003 const void* data; 1004 ssize_t size; 1005 if (message->FindData(name, type, k, &data, &size) 1006 != B_OK) { 1007 break; 1008 } 1009 if (stackBundleThis.AddData(name, type, data, size) 1010 != B_OK) { 1011 break; 1012 } 1013 bundleThis = &stackBundleThis; 1014 } 1015 } 1016 } 1017 1018 for (int32 index = 0; index < count; index++) { 1019 entry_ref ref; 1020 message->FindRef("refs", index, &ref); 1021 1022 const node_ref* nodeToClose = NULL; 1023 const node_ref* nodeToSelect = NULL; 1024 ssize_t numBytes; 1025 1026 message->FindData("nodeRefsToClose", B_RAW_TYPE, index, 1027 (const void**)&nodeToClose, &numBytes); 1028 message->FindData("nodeRefToSelect", B_RAW_TYPE, index, 1029 (const void**)&nodeToSelect, &numBytes); 1030 1031 OpenRef(&ref, nodeToClose, nodeToSelect, selector, 1032 bundleThis); 1033 } 1034 1035 break; 1036 } 1037 } 1038 } 1039 1040 1041 void 1042 TTracker::ArgvReceived(int32 argc, char** argv) 1043 { 1044 BMessage* message = CurrentMessage(); 1045 const char* currentWorkingDirectoryPath = NULL; 1046 entry_ref ref; 1047 1048 if (message->FindString("cwd", ¤tWorkingDirectoryPath) == B_OK) { 1049 BDirectory workingDirectory(currentWorkingDirectoryPath); 1050 for (int32 index = 1; index < argc; index++) { 1051 BEntry entry; 1052 if (entry.SetTo(&workingDirectory, argv[index]) == B_OK 1053 && entry.GetRef(&ref) == B_OK) { 1054 OpenRef(&ref); 1055 } else if (get_ref_for_path(argv[index], &ref) == B_OK) 1056 OpenRef(&ref); 1057 } 1058 } 1059 } 1060 1061 1062 void 1063 TTracker::OpenContainerWindow(Model* model, BMessage* originalRefsList, 1064 OpenSelector openSelector, uint32 openFlags, bool checkAlreadyOpen, 1065 const BMessage* stateMessage) 1066 { 1067 AutoLock<WindowList> lock(&fWindowList); 1068 BContainerWindow* window = NULL; 1069 const node_ref* modelNodeRef = model->NodeRef(); 1070 if (checkAlreadyOpen && openSelector != kRunOpenWithWindow) { 1071 // find out if window already open 1072 window = FindContainerWindow(modelNodeRef); 1073 } 1074 1075 bool someWindowActivated = false; 1076 1077 uint32 workspace = (uint32)(1 << current_workspace()); 1078 int32 windowCount = 0; 1079 while (window != NULL) { 1080 if ((window->Workspaces() & workspace) != 0 1081 && (dynamic_cast<BDeskWindow*>(window) == NULL 1082 || !TrackerSettings().SingleWindowBrowse())) { 1083 // We found at least one window that is open and is not Desktop 1084 // or we're in spatial mode, activate it and make sure we don't 1085 // jerk the workspaces around. 1086 window->Activate(); 1087 someWindowActivated = true; 1088 } 1089 window = FindContainerWindow(model->NodeRef(), ++windowCount); 1090 } 1091 1092 if (someWindowActivated) { 1093 delete model; 1094 return; 1095 } 1096 1097 // If no window was activated (none in the current workspace), 1098 // we open a new one. 1099 1100 if (openSelector == kRunOpenWithWindow) { 1101 BMessage* refList = NULL; 1102 if (originalRefsList == NULL) { 1103 // when passing just a single model, stuff it's entry in a single 1104 // element list anyway 1105 ASSERT(model != NULL); 1106 refList = new BMessage; 1107 refList->AddRef("refs", model->EntryRef()); 1108 delete model; 1109 model = NULL; 1110 } else { 1111 // clone the message, window adopts it for it's own use 1112 refList = new BMessage(*originalRefsList); 1113 } 1114 window = new OpenWithContainerWindow(refList, &fWindowList); 1115 } else if (model->IsQuery()) { 1116 // window will adopt the model 1117 window = new BQueryContainerWindow(&fWindowList, openFlags); 1118 } else if (model->IsVirtualDirectory()) { 1119 // window will adopt the model 1120 window = new VirtualDirectoryWindow(&fWindowList, openFlags); 1121 } else { 1122 // window will adopt the model 1123 window = new BContainerWindow(&fWindowList, openFlags); 1124 } 1125 1126 if (model != NULL && window->LockLooper()) { 1127 window->CreatePoseView(model); 1128 if (window->PoseView() == NULL) { 1129 // Failed initialization. 1130 window->PostMessage(B_QUIT_REQUESTED); 1131 window->UnlockLooper(); 1132 return; 1133 } 1134 window->UnlockLooper(); 1135 } 1136 1137 BMessage restoreStateMessage(kRestoreState); 1138 1139 if (stateMessage != NULL) 1140 restoreStateMessage.AddMessage("state", stateMessage); 1141 1142 window->PostMessage(&restoreStateMessage); 1143 } 1144 1145 1146 void 1147 TTracker::EditQueries(const BMessage* message) 1148 { 1149 bool editOnlyIfTemplate; 1150 if (message->FindBool("editQueryOnPose", &editOnlyIfTemplate) != B_OK) 1151 editOnlyIfTemplate = false; 1152 1153 type_code type; 1154 int32 count; 1155 message->GetInfo("refs", &type, &count); 1156 for (int32 index = 0; index < count; index++) { 1157 entry_ref ref; 1158 message->FindRef("refs", index, &ref); 1159 BEntry entry(&ref, true); 1160 if (entry.InitCheck() == B_OK && entry.Exists()) 1161 (new FindWindow(&ref, editOnlyIfTemplate))->Show(); 1162 } 1163 } 1164 1165 1166 void 1167 TTracker::OpenInfoWindows(BMessage* message) 1168 { 1169 type_code type; 1170 int32 count; 1171 message->GetInfo("refs", &type, &count); 1172 1173 for (int32 index = 0; index < count; index++) { 1174 entry_ref ref; 1175 message->FindRef("refs", index, &ref); 1176 BEntry entry; 1177 if (entry.SetTo(&ref) == B_OK) { 1178 Model* model = new Model(&entry); 1179 if (model->InitCheck() != B_OK) { 1180 delete model; 1181 continue; 1182 } 1183 1184 AutoLock<WindowList> lock(&fWindowList); 1185 BInfoWindow* wind = FindInfoWindow(model->NodeRef()); 1186 1187 if (wind) { 1188 wind->Activate(); 1189 delete model; 1190 } else { 1191 wind = new BInfoWindow(model, index, &fWindowList); 1192 wind->PostMessage(kRestoreState); 1193 } 1194 } 1195 } 1196 } 1197 1198 1199 BDeskWindow* 1200 TTracker::GetDeskWindow() const 1201 { 1202 int32 count = fWindowList.CountItems(); 1203 for (int32 index = 0; index < count; index++) { 1204 BDeskWindow* window = dynamic_cast<BDeskWindow*>( 1205 fWindowList.ItemAt(index)); 1206 if (window != NULL) 1207 return window; 1208 } 1209 TRESPASS(); 1210 1211 return NULL; 1212 } 1213 1214 1215 BContainerWindow* 1216 TTracker::FindContainerWindow(const node_ref* node, int32 number) const 1217 { 1218 ASSERT(fWindowList.IsLocked()); 1219 1220 int32 count = fWindowList.CountItems(); 1221 int32 windowsFound = 0; 1222 for (int32 index = 0; index < count; index++) { 1223 BContainerWindow* window = dynamic_cast<BContainerWindow*>( 1224 fWindowList.ItemAt(index)); 1225 1226 if (window != NULL && window->IsShowing(node) 1227 && number == windowsFound++) { 1228 return window; 1229 } 1230 } 1231 1232 return NULL; 1233 } 1234 1235 1236 BContainerWindow* 1237 TTracker::FindContainerWindow(const entry_ref* entry, int32 number) const 1238 { 1239 ASSERT(fWindowList.IsLocked()); 1240 1241 int32 count = fWindowList.CountItems(); 1242 1243 int32 windowsFound = 0; 1244 1245 for (int32 index = 0; index < count; index++) { 1246 BContainerWindow* window = dynamic_cast<BContainerWindow*> 1247 (fWindowList.ItemAt(index)); 1248 1249 if (window && window->IsShowing(entry) && number == windowsFound++) 1250 return window; 1251 } 1252 1253 return NULL; 1254 } 1255 1256 1257 bool 1258 TTracker::EntryHasWindowOpen(const entry_ref* entry) 1259 { 1260 AutoLock<WindowList> lock(&fWindowList); 1261 return FindContainerWindow(entry) != NULL; 1262 } 1263 1264 1265 BContainerWindow* 1266 TTracker::FindParentContainerWindow(const entry_ref* ref) const 1267 { 1268 BEntry entry(ref); 1269 BEntry parent; 1270 1271 if (entry.GetParent(&parent) != B_OK) 1272 return NULL; 1273 1274 entry_ref parentRef; 1275 parent.GetRef(&parentRef); 1276 1277 ASSERT(fWindowList.IsLocked()); 1278 1279 int32 count = fWindowList.CountItems(); 1280 for (int32 index = 0; index < count; index++) { 1281 BContainerWindow* window = dynamic_cast<BContainerWindow*>( 1282 fWindowList.ItemAt(index)); 1283 if (window != NULL && window->IsShowing(&parentRef)) 1284 return window; 1285 } 1286 1287 return NULL; 1288 } 1289 1290 1291 BInfoWindow* 1292 TTracker::FindInfoWindow(const node_ref* node) const 1293 { 1294 ASSERT(fWindowList.IsLocked()); 1295 1296 int32 count = fWindowList.CountItems(); 1297 for (int32 index = 0; index < count; index++) { 1298 BInfoWindow* window = dynamic_cast<BInfoWindow*>( 1299 fWindowList.ItemAt(index)); 1300 if (window != NULL && window->IsShowing(node)) 1301 return window; 1302 } 1303 1304 return NULL; 1305 } 1306 1307 1308 bool 1309 TTracker::QueryActiveForDevice(dev_t device) 1310 { 1311 AutoLock<WindowList> lock(&fWindowList); 1312 int32 count = fWindowList.CountItems(); 1313 for (int32 index = 0; index < count; index++) { 1314 BQueryContainerWindow* window = dynamic_cast<BQueryContainerWindow*>( 1315 fWindowList.ItemAt(index)); 1316 if (window != NULL) { 1317 AutoLock<BWindow> lock(window); 1318 if (window->ActiveOnDevice(device)) 1319 return true; 1320 } 1321 } 1322 1323 return false; 1324 } 1325 1326 1327 void 1328 TTracker::CloseActiveQueryWindows(dev_t device) 1329 { 1330 // used when trying to unmount a volume - an active query would prevent 1331 // that from happening 1332 bool closed = false; 1333 AutoLock<WindowList> lock(fWindowList); 1334 for (int32 index = fWindowList.CountItems(); index >= 0; index--) { 1335 BQueryContainerWindow* window 1336 = dynamic_cast<BQueryContainerWindow*>(fWindowList.ItemAt(index)); 1337 if (window != NULL) { 1338 AutoLock<BWindow> lock(window); 1339 if (window->ActiveOnDevice(device)) { 1340 window->PostMessage(B_QUIT_REQUESTED); 1341 closed = true; 1342 } 1343 } 1344 } 1345 1346 lock.Unlock(); 1347 1348 if (closed) { 1349 for (int32 timeout = 30; timeout; timeout--) { 1350 // wait a bit for windows to fully close 1351 if (!QueryActiveForDevice(device)) 1352 return; 1353 1354 snooze(100000); 1355 } 1356 } 1357 } 1358 1359 1360 void 1361 TTracker::SaveAllPoseLocations() 1362 { 1363 int32 numWindows = fWindowList.CountItems(); 1364 for (int32 windowIndex = 0; windowIndex < numWindows; windowIndex++) { 1365 BContainerWindow* window = dynamic_cast<BContainerWindow*>( 1366 fWindowList.ItemAt(windowIndex)); 1367 if (window != NULL) { 1368 AutoLock<BWindow> lock(window); 1369 BDeskWindow* deskWindow = dynamic_cast<BDeskWindow*>(window); 1370 if (deskWindow != NULL) 1371 deskWindow->SaveDesktopPoseLocations(); 1372 else 1373 window->PoseView()->SavePoseLocations(); 1374 } 1375 } 1376 } 1377 1378 1379 void 1380 TTracker::CloseWindowAndChildren(const node_ref* node) 1381 { 1382 BDirectory dir(node); 1383 if (dir.InitCheck() != B_OK) 1384 return; 1385 1386 AutoLock<WindowList> lock(&fWindowList); 1387 BObjectList<BContainerWindow> closeList; 1388 1389 // make a list of all windows to be closed 1390 // count from end to beginning so we can remove items safely 1391 for (int32 index = fWindowList.CountItems() - 1; index >= 0; index--) { 1392 BContainerWindow* window = dynamic_cast<BContainerWindow*>( 1393 fWindowList.ItemAt(index)); 1394 if (window && window->TargetModel()) { 1395 BEntry wind_entry; 1396 wind_entry.SetTo(window->TargetModel()->EntryRef()); 1397 1398 if ((*window->TargetModel()->NodeRef() == *node) 1399 || dir.Contains(&wind_entry)) { 1400 1401 // ToDo: 1402 // get rid of the Remove here, BContainerWindow::Quit does it 1403 fWindowList.RemoveItemAt(index); 1404 closeList.AddItem(window); 1405 } 1406 } 1407 } 1408 1409 // now really close the windows 1410 int32 numItems = closeList.CountItems(); 1411 for (int32 index = 0; index < numItems; index++) { 1412 BContainerWindow* window = closeList.ItemAt(index); 1413 window->PostMessage(B_QUIT_REQUESTED); 1414 } 1415 } 1416 1417 1418 void 1419 TTracker::CloseAllInWorkspace() 1420 { 1421 AutoLock<WindowList> lock(&fWindowList); 1422 1423 int32 currentWorkspace = 1 << current_workspace(); 1424 // count from end to beginning so we can remove items safely 1425 for (int32 index = fWindowList.CountItems() - 1; index >= 0; index--) { 1426 BWindow* window = fWindowList.ItemAt(index); 1427 if (window != NULL && (window->Workspaces() & currentWorkspace) != 0) { 1428 // avoid the desktop 1429 if (dynamic_cast<BDeskWindow*>(window) == NULL 1430 && dynamic_cast<BStatusWindow*>(window) == NULL) { 1431 window->PostMessage(B_QUIT_REQUESTED); 1432 } 1433 } 1434 } 1435 } 1436 1437 1438 void 1439 TTracker::CloseAllWindows() 1440 { 1441 // this is a response to the DeskBar sending us a B_QUIT, when it really 1442 // means to say close all your windows. It might be better to have it 1443 // send a kCloseAllWindows message and have windowless apps stay running, 1444 // which is what we will do for the Tracker 1445 AutoLock<WindowList> lock(&fWindowList); 1446 1447 int32 count = CountWindows(); 1448 for (int32 index = 0; index < count; index++) { 1449 BWindow* window = WindowAt(index); 1450 // avoid the desktop 1451 if (dynamic_cast<BDeskWindow*>(window) == NULL 1452 && dynamic_cast<BStatusWindow*>(window) == NULL) { 1453 window->PostMessage(B_QUIT_REQUESTED); 1454 } 1455 } 1456 1457 // count from end to beginning so we can remove items safely 1458 for (int32 index = fWindowList.CountItems() - 1; index >= 0; index--) { 1459 BWindow* window = fWindowList.ItemAt(index); 1460 if (dynamic_cast<BDeskWindow*>(window) == NULL 1461 && dynamic_cast<BStatusWindow*>(window) == NULL) { 1462 // ToDo: get rid of the Remove here, BContainerWindow::Quit() 1463 // does it 1464 fWindowList.RemoveItemAt(index); 1465 } 1466 } 1467 } 1468 1469 1470 void 1471 TTracker::_OpenPreviouslyOpenedWindows(const char* pathFilter) 1472 { 1473 size_t filterLength = 0; 1474 if (pathFilter != NULL) 1475 filterLength = strlen(pathFilter); 1476 1477 BDirectory deskDir; 1478 attr_info attrInfo; 1479 if (FSGetDeskDir(&deskDir) != B_OK 1480 || deskDir.GetAttrInfo(kAttrOpenWindows, &attrInfo) != B_OK) { 1481 return; 1482 } 1483 1484 char* buffer = (char*)malloc((size_t)attrInfo.size); 1485 BMessage message; 1486 if (deskDir.ReadAttr(kAttrOpenWindows, B_MESSAGE_TYPE, 0, buffer, 1487 (size_t)attrInfo.size) != attrInfo.size 1488 || message.Unflatten(buffer) != B_OK) { 1489 free(buffer); 1490 return; 1491 } 1492 1493 free(buffer); 1494 1495 node_ref nodeRef; 1496 deskDir.GetNodeRef(&nodeRef); 1497 1498 int32 stateMessageCounter = 0; 1499 const char* path; 1500 for (int32 i = 0; message.FindString("paths", i, &path) == B_OK; i++) { 1501 if (strncmp(path, pathFilter, filterLength) != 0) 1502 continue; 1503 1504 BEntry entry(path, true); 1505 if (entry.InitCheck() != B_OK) 1506 continue; 1507 1508 int8 flags = 0; 1509 for (int32 j = 0; message.FindInt8(path, j, &flags) == B_OK; j++) { 1510 Model* model = new Model(&entry); 1511 if (model->InitCheck() == B_OK && model->IsContainer()) { 1512 BMessage state; 1513 bool restoreStateFromMessage = false; 1514 if ((flags & kOpenWindowHasState) != 0 1515 && message.FindMessage("window state", 1516 stateMessageCounter++, &state) == B_OK) { 1517 restoreStateFromMessage = true; 1518 } 1519 1520 if (restoreStateFromMessage) { 1521 OpenContainerWindow(model, 0, kOpen, kRestoreWorkspace 1522 | (flags & kOpenWindowMinimized ? kIsHidden : 0U) 1523 | kRestoreDecor, false, &state); 1524 } else { 1525 OpenContainerWindow(model, 0, kOpen, kRestoreWorkspace 1526 | (flags & kOpenWindowMinimized ? kIsHidden : 0U) 1527 | kRestoreDecor); 1528 } 1529 } else 1530 delete model; 1531 } 1532 } 1533 1534 // open disks window if needed 1535 1536 if (pathFilter == NULL && TrackerSettings().ShowDisksIcon() 1537 && message.HasBool("open_disks_window")) { 1538 BEntry entry("/"); 1539 Model* model = new Model(&entry); 1540 if (model->InitCheck() == B_OK) 1541 OpenContainerWindow(model, 0, kOpen, kRestoreWorkspace); 1542 else 1543 delete model; 1544 } 1545 } 1546 1547 1548 void 1549 TTracker::ReadyToRun() 1550 { 1551 gStatusWindow = new BStatusWindow(); 1552 InitMimeTypes(); 1553 InstallDefaultTemplates(); 1554 InstallIndices(); 1555 InstallTemporaryBackgroundImages(); 1556 1557 fTrashWatcher = new BTrashWatcher(); 1558 fTrashWatcher->Run(); 1559 1560 fClipboardRefsWatcher = new BClipboardRefsWatcher(); 1561 fClipboardRefsWatcher->Run(); 1562 1563 fTaskLoop = new StandAloneTaskLoop(true); 1564 1565 // kick off building the mime type list for find panels, etc. 1566 fMimeTypeList = new MimeTypeList(); 1567 1568 if (!BootedInSafeMode()) { 1569 // kick of transient query killer 1570 DeleteTransientQueriesTask::StartUpTransientQueryCleaner(); 1571 // the mount_server will have mounted the previous volumes already. 1572 _OpenPreviouslyOpenedWindows(); 1573 } 1574 } 1575 1576 1577 MimeTypeList* 1578 TTracker::MimeTypes() const 1579 { 1580 return fMimeTypeList; 1581 } 1582 1583 1584 void 1585 TTracker::SelectChildInParentSoon(const entry_ref* parent, 1586 const node_ref* child) 1587 { 1588 fTaskLoop->RunLater(NewMemberFunctionObjectWithResult 1589 (&TTracker::SelectChildInParent, this, parent, child), 1590 100000, 200000, 5000000); 1591 } 1592 1593 1594 void 1595 TTracker::CloseParentWaitingForChildSoon(const entry_ref* child, 1596 const node_ref* parent) 1597 { 1598 fTaskLoop->RunLater(NewMemberFunctionObjectWithResult 1599 (&TTracker::CloseParentWaitingForChild, this, child, parent), 1600 200000, 100000, 5000000); 1601 } 1602 1603 1604 void 1605 TTracker::SelectPoseAtLocationSoon(node_ref parent, BPoint pointInPose) 1606 { 1607 fTaskLoop->RunLater(NewMemberFunctionObject 1608 (&TTracker::SelectPoseAtLocationInParent, this, parent, pointInPose), 1609 100000); 1610 } 1611 1612 1613 void 1614 TTracker::SelectPoseAtLocationInParent(node_ref parent, BPoint pointInPose) 1615 { 1616 AutoLock<WindowList> lock(&fWindowList); 1617 BContainerWindow* parentWindow = FindContainerWindow(&parent); 1618 if (parentWindow != NULL) { 1619 AutoLock<BWindow> lock(parentWindow); 1620 parentWindow->PoseView()->SelectPoseAtLocation(pointInPose); 1621 } 1622 } 1623 1624 1625 bool 1626 TTracker::CloseParentWaitingForChild(const entry_ref* child, 1627 const node_ref* parent) 1628 { 1629 AutoLock<WindowList> lock(&fWindowList); 1630 1631 BContainerWindow* parentWindow = FindContainerWindow(parent); 1632 if (parentWindow == NULL) { 1633 // parent window already closed, give up 1634 return true; 1635 } 1636 1637 // If child is a symbolic link, dereference it, so that 1638 // FindContainerWindow will succeed. 1639 BEntry entry(child, true); 1640 entry_ref resolvedChild; 1641 if (entry.GetRef(&resolvedChild) != B_OK) 1642 resolvedChild = *child; 1643 1644 BContainerWindow* window = FindContainerWindow(&resolvedChild); 1645 if (window != NULL) { 1646 AutoLock<BWindow> lock(window); 1647 if (!window->IsHidden()) 1648 return CloseParentWindowCommon(parentWindow); 1649 } 1650 1651 return false; 1652 } 1653 1654 1655 void 1656 TTracker::CloseParent(node_ref parent) 1657 { 1658 AutoLock<WindowList> lock(&fWindowList); 1659 if (!lock) 1660 return; 1661 1662 CloseParentWindowCommon(FindContainerWindow(&parent)); 1663 } 1664 1665 1666 void 1667 TTracker::ShowSettingsWindow() 1668 { 1669 if (fSettingsWindow == NULL) { 1670 fSettingsWindow = new TrackerSettingsWindow(); 1671 fSettingsWindow->Show(); 1672 } else { 1673 if (fSettingsWindow->Lock()) { 1674 if (fSettingsWindow->IsHidden()) 1675 fSettingsWindow->Show(); 1676 else 1677 fSettingsWindow->Activate(); 1678 1679 fSettingsWindow->Unlock(); 1680 } 1681 } 1682 } 1683 1684 1685 bool 1686 TTracker::CloseParentWindowCommon(BContainerWindow* window) 1687 { 1688 ASSERT(fWindowList.IsLocked()); 1689 1690 if (dynamic_cast<BDeskWindow*>(window) != NULL) { 1691 // don't close the desktop 1692 return false; 1693 } 1694 1695 window->PostMessage(B_QUIT_REQUESTED); 1696 return true; 1697 } 1698 1699 1700 bool 1701 TTracker::SelectChildInParent(const entry_ref* parent, const node_ref* child) 1702 { 1703 AutoLock<WindowList> lock(&fWindowList); 1704 1705 BContainerWindow* window = FindContainerWindow(parent); 1706 if (window == NULL) { 1707 // parent window already closed, give up 1708 return false; 1709 } 1710 1711 AutoLock<BWindow> windowLock(window); 1712 if (windowLock.IsLocked()) { 1713 BPoseView* view = window->PoseView(); 1714 int32 index; 1715 BPose* pose = view->FindPose(child, &index); 1716 if (pose != NULL) { 1717 view->SelectPose(pose, index); 1718 return true; 1719 } 1720 } 1721 1722 return false; 1723 } 1724 1725 1726 status_t 1727 TTracker::NeedMoreNodeMonitors() 1728 { 1729 fNodeMonitorCount += kNodeMonitorBumpValue; 1730 PRINT(("bumping nodeMonitorCount to %" B_PRId32 "\n", fNodeMonitorCount)); 1731 1732 struct rlimit rl; 1733 rl.rlim_cur = fNodeMonitorCount; 1734 rl.rlim_max = RLIM_SAVED_MAX; 1735 if (setrlimit(RLIMIT_NOVMON, &rl) < 0) { 1736 fNodeMonitorCount -= kNodeMonitorBumpValue; 1737 return errno; 1738 } 1739 1740 return B_OK; 1741 } 1742 1743 1744 status_t 1745 TTracker::WatchNode(const node_ref* node, uint32 flags, BMessenger target) 1746 { 1747 status_t result = watch_node(node, flags, target); 1748 if (result == B_OK || result != B_NO_MEMORY) { 1749 // need to make sure this uses the same error value as 1750 // the node monitor code 1751 return result; 1752 } 1753 1754 PRINT(("failed to start monitoring, trying to allocate more " 1755 "node monitors\n")); 1756 1757 TTracker* tracker = dynamic_cast<TTracker*>(be_app); 1758 if (tracker == NULL) { 1759 // we are the file panel only, just fail 1760 return result; 1761 } 1762 1763 result = tracker->NeedMoreNodeMonitors(); 1764 1765 if (result != B_OK) { 1766 PRINT(("failed to allocate more node monitors, %s\n", 1767 strerror(result))); 1768 return result; 1769 } 1770 1771 // try again, this time with more node monitors 1772 return watch_node(node, flags, target); 1773 } 1774 1775 1776 BMessenger 1777 TTracker::MountServer() const 1778 { 1779 return BMessenger(kMountServerSignature); 1780 } 1781 1782 1783 bool 1784 TTracker::TrashFull() const 1785 { 1786 return fTrashWatcher->CheckTrashDirs(); 1787 } 1788 1789 1790 bool 1791 TTracker::IsTrashNode(const node_ref* node) const 1792 { 1793 return fTrashWatcher->IsTrashNode(node); 1794 } 1795 1796 bool 1797 TTracker::InTrashNode(const entry_ref* ref) const 1798 { 1799 return FSInTrashDir(ref); 1800 } 1801