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