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