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