1 /* 2 Open Tracker License 3 4 Terms and Conditions 5 6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved. 7 8 Permission is hereby granted, free of charge, to any person obtaining a copy of 9 this software and associated documentation files (the "Software"), to deal in 10 the Software without restriction, including without limitation the rights to 11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 12 of the Software, and to permit persons to whom the Software is furnished to do 13 so, subject to the following conditions: 14 15 The above copyright notice and this permission notice applies to all licensees 16 and shall be included in all copies or substantial portions of the Software. 17 18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY, 20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION 23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 25 Except as contained in this notice, the name of Be Incorporated shall not be 26 used in advertising or otherwise to promote the sale, use or other dealings in 27 this Software without prior written authorization from Be Incorporated. 28 29 Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks 30 of Be Incorporated in the United States and other countries. Other brand product 31 names are registered trademarks or trademarks of their respective holders. 32 All rights reserved. 33 */ 34 35 36 #include "Attributes.h" 37 #include "AttributeStream.h" 38 #include "AutoLock.h" 39 #include "Commands.h" 40 #include "DesktopPoseView.h" 41 #include "DirMenu.h" 42 #include "FavoritesConfig.h" 43 #include "FavoritesMenu.h" 44 #include "FilePanelPriv.h" 45 #include "FSUtils.h" 46 #include "FSClipboard.h" 47 #include "IconMenuItem.h" 48 #include "MimeTypes.h" 49 #include "NavMenu.h" 50 #include "PoseView.h" 51 #include "Tracker.h" 52 #include "tracker_private.h" 53 54 #include <Alert.h> 55 #include <Application.h> 56 #include <Button.h> 57 #include <Debug.h> 58 #include <Directory.h> 59 #include <FindDirectory.h> 60 #include <MenuBar.h> 61 #include <MenuField.h> 62 #include <MenuItem.h> 63 #include <MessageFilter.h> 64 #include <NodeInfo.h> 65 #include <NodeMonitor.h> 66 #include <Path.h> 67 #include <Roster.h> 68 #include <SymLink.h> 69 #include <ScrollView.h> 70 #include <String.h> 71 #include <StopWatch.h> 72 #include <TextControl.h> 73 #include <TextView.h> 74 #include <Volume.h> 75 #include <VolumeRoster.h> 76 77 #include <string.h> 78 79 80 const char *kDefaultFilePanelTemplate = "FilePanelSettings"; 81 82 83 static uint32 84 GetLinkFlavor(const Model *model, bool resolve = true) 85 { 86 if (model && model->IsSymLink()) { 87 if (!resolve) 88 return B_SYMLINK_NODE; 89 model = model->LinkTo(); 90 } 91 if (!model) 92 return 0; 93 94 if (model->IsDirectory()) 95 return B_DIRECTORY_NODE; 96 97 return B_FILE_NODE; 98 } 99 100 101 static filter_result 102 key_down_filter(BMessage *message, BHandler **, BMessageFilter *filter) 103 { 104 TFilePanel *panel = dynamic_cast<TFilePanel *>(filter->Looper()); 105 ASSERT(panel); 106 BPoseView *view = panel->PoseView(); 107 108 if (panel->TrackingMenu()) 109 return B_DISPATCH_MESSAGE; 110 111 uchar key; 112 if (message->FindInt8("byte", (int8 *)&key) != B_OK) 113 return B_DISPATCH_MESSAGE; 114 115 int32 modifier = 0; 116 message->FindInt32("modifiers", &modifier); 117 if (!modifier && key == B_ESCAPE) { 118 if (view->ActivePose()) 119 view->CommitActivePose(false); 120 else 121 filter->Looper()->PostMessage(kCancelButton); 122 return B_SKIP_MESSAGE; 123 } 124 125 if (key == B_RETURN && view->ActivePose()) { 126 view->CommitActivePose(); 127 return B_SKIP_MESSAGE; 128 } 129 130 return B_DISPATCH_MESSAGE; 131 } 132 133 134 // #pragma mark - 135 136 137 TFilePanel::TFilePanel(file_panel_mode mode, BMessenger *target, 138 const BEntry *startDir, uint32 nodeFlavors, bool multipleSelection, 139 BMessage *message, BRefFilter *filter, uint32 containerWindowFlags, 140 window_look look, window_feel feel, bool hideWhenDone) 141 : BContainerWindow(0, containerWindowFlags, look, feel, 0, B_CURRENT_WORKSPACE), 142 fDirMenu(NULL), 143 fDirMenuField(NULL), 144 fTextControl(NULL), 145 fClientObject(NULL), 146 fSelectionIterator(0), 147 fMessage(NULL), 148 fHideWhenDone(hideWhenDone), 149 fIsTrackingMenu(false), 150 fConfigWindow(NULL) 151 { 152 InitIconPreloader(); 153 154 fIsSavePanel = (mode == B_SAVE_PANEL); 155 156 BRect windRect(85, 50, 510, 296); 157 MoveTo(windRect.LeftTop()); 158 ResizeTo(windRect.Width(), windRect.Height()); 159 160 fNodeFlavors = (nodeFlavors == 0) ? B_FILE_NODE : nodeFlavors; 161 162 if (target) 163 fTarget = *target; 164 else 165 fTarget = BMessenger(be_app); 166 167 if (message) 168 SetMessage(message); 169 else if (fIsSavePanel) 170 fMessage = new BMessage(B_SAVE_REQUESTED); 171 else 172 fMessage = new BMessage(B_REFS_RECEIVED); 173 174 // check for legal starting directory 175 Model *model = new Model(); 176 bool useRoot = true; 177 178 if (startDir) { 179 if (model->SetTo(startDir) == B_OK && model->IsDirectory()) 180 useRoot = false; 181 else { 182 delete model; 183 model = new Model(); 184 } 185 } 186 187 if (useRoot) { 188 BPath path; 189 if (find_directory(B_USER_DIRECTORY, &path) == B_OK) { 190 BEntry entry(path.Path(), true); 191 if (entry.InitCheck() == B_OK && model->SetTo(&entry) == B_OK) 192 useRoot = false; 193 } 194 } 195 196 if (useRoot) { 197 BVolume volume; 198 BDirectory root; 199 BVolumeRoster volumeRoster; 200 volumeRoster.GetBootVolume(&volume); 201 volume.GetRootDirectory(&root); 202 203 BEntry entry; 204 root.GetEntry(&entry); 205 model->SetTo(&entry); 206 } 207 208 fTaskLoop = new PiggybackTaskLoop; 209 210 AutoLock<BWindow> lock(this); 211 CreatePoseView(model); 212 fPoseView->SetRefFilter(filter); 213 if (!fIsSavePanel) 214 fPoseView->SetMultipleSelection(multipleSelection); 215 216 fPoseView->SetFlags(fPoseView->Flags() | B_NAVIGABLE); 217 fPoseView->SetPoseEditing(false); 218 AddCommonFilter(new BMessageFilter(B_KEY_DOWN, key_down_filter)); 219 AddCommonFilter(new BMessageFilter(B_SIMPLE_DATA, TFilePanel::MessageDropFilter)); 220 AddCommonFilter(new BMessageFilter(B_NODE_MONITOR, TFilePanel::FSFilter)); 221 222 // inter-application observing 223 BMessenger tracker(kTrackerSignature); 224 BHandler::StartWatching(tracker, kDesktopFilePanelRootChanged); 225 226 Init(); 227 } 228 229 230 TFilePanel::~TFilePanel() 231 { 232 // regardless of the hide/close method 233 // always get rid of the config window 234 if (fConfigWindow) { 235 // moved from QuitRequested to ensure that 236 // if the config window is showing that 237 // it gets closed as well 238 fConfigWindow->Lock(); 239 fConfigWindow->Quit(); 240 } 241 242 BMessenger tracker(kTrackerSignature); 243 BHandler::StopWatching(tracker, kDesktopFilePanelRootChanged); 244 245 delete fMessage; 246 } 247 248 249 filter_result 250 TFilePanel::MessageDropFilter(BMessage *message, BHandler **, BMessageFilter *filter) 251 { 252 TFilePanel *panel = dynamic_cast<TFilePanel *>(filter->Looper()); 253 if (panel == NULL || !message->WasDropped()) 254 return B_SKIP_MESSAGE; 255 256 uint32 type; 257 int32 count; 258 if (message->GetInfo("refs", &type, &count) != B_OK) 259 return B_SKIP_MESSAGE; 260 261 if (count != 1) 262 return B_SKIP_MESSAGE; 263 264 entry_ref ref; 265 if (message->FindRef("refs", &ref) != B_OK) 266 return B_SKIP_MESSAGE; 267 268 BEntry entry(&ref); 269 if (entry.InitCheck() != B_OK) 270 return B_SKIP_MESSAGE; 271 272 // if the entry is a symlink 273 // resolve it and see if it is a directory 274 // pass it on if it is 275 if (entry.IsSymLink()) { 276 entry_ref resolvedRef; 277 278 entry.GetRef(&resolvedRef); 279 BEntry resolvedEntry(&resolvedRef, true); 280 281 if (resolvedEntry.IsDirectory()) { 282 // both entry and ref need to be the correct locations 283 // for the last setto 284 resolvedEntry.GetRef(&ref); 285 entry.SetTo(&ref); 286 } 287 } 288 289 // if not a directory, set to the parent, and select the child 290 if (!entry.IsDirectory()) { 291 node_ref child; 292 if (entry.GetNodeRef(&child) != B_OK) 293 return B_SKIP_MESSAGE; 294 295 BPath path(&entry); 296 297 if (entry.GetParent(&entry) != B_OK) 298 return B_SKIP_MESSAGE; 299 300 entry.GetRef(&ref); 301 302 panel->fTaskLoop->RunLater(NewMemberFunctionObjectWithResult 303 (&TFilePanel::SelectChildInParent, panel, 304 const_cast<const entry_ref *>(&ref), 305 const_cast<const node_ref *>(&child)), 306 ref == *panel->TargetModel()->EntryRef() ? 0 : 100000, 200000, 5000000); 307 // if the target directory is already current, we won't 308 // delay the initial selection try 309 310 // also set the save name to the dragged in entry 311 if (panel->IsSavePanel()) 312 panel->SetSaveText(path.Leaf()); 313 } 314 315 panel->SetTo(&ref); 316 317 return B_SKIP_MESSAGE; 318 } 319 320 321 filter_result 322 TFilePanel::FSFilter(BMessage *message, BHandler **, BMessageFilter *filter) 323 { 324 switch (message->FindInt32("opcode")) { 325 case B_ENTRY_MOVED: 326 { 327 node_ref itemNode; 328 node_ref dirNode; 329 TFilePanel *panel = dynamic_cast<TFilePanel *>(filter->Looper()); 330 331 message->FindInt32("device", &dirNode.device); 332 itemNode.device = dirNode.device; 333 message->FindInt64("to directory", (int64 *)&dirNode.node); 334 message->FindInt64("node", (int64 *)&itemNode.node); 335 const char *name; 336 if (message->FindString("name", &name) != B_OK) 337 break; 338 339 // if current directory moved, update entry ref and menu 340 // but not wind title 341 if (*(panel->TargetModel()->NodeRef()) == itemNode) { 342 panel->TargetModel()->UpdateEntryRef(&dirNode, name); 343 panel->SetTo(panel->TargetModel()->EntryRef()); 344 return B_SKIP_MESSAGE; 345 } 346 break; 347 } 348 case B_ENTRY_REMOVED: 349 { 350 node_ref itemNode; 351 TFilePanel *panel = dynamic_cast<TFilePanel *>(filter->Looper()); 352 message->FindInt32("device", &itemNode.device); 353 message->FindInt64("node", (int64 *)&itemNode.node); 354 355 // if folder we're watching is deleted, switch to root 356 // or Desktop 357 if (*(panel->TargetModel()->NodeRef()) == itemNode) { 358 BVolumeRoster volumeRoster; 359 BVolume volume; 360 volumeRoster.GetBootVolume(&volume); 361 362 BDirectory root; 363 volume.GetRootDirectory(&root); 364 365 BEntry entry; 366 entry_ref ref; 367 root.GetEntry(&entry); 368 entry.GetRef(&ref); 369 370 panel->SwitchDirToDesktopIfNeeded(ref); 371 372 panel->SetTo(&ref); 373 return B_SKIP_MESSAGE; 374 } 375 } 376 break; 377 } 378 return B_DISPATCH_MESSAGE; 379 } 380 381 382 void 383 TFilePanel::DispatchMessage(BMessage *message, BHandler *handler) 384 { 385 _inherited::DispatchMessage(message, handler); 386 if (message->what == B_KEY_DOWN || message->what == B_MOUSE_DOWN) 387 AdjustButton(); 388 } 389 390 391 BFilePanelPoseView * 392 TFilePanel::PoseView() const 393 { 394 ASSERT(dynamic_cast<BFilePanelPoseView *>(fPoseView)); 395 return static_cast<BFilePanelPoseView *>(fPoseView); 396 } 397 398 399 bool 400 TFilePanel::QuitRequested() 401 { 402 // If we have a client object then this window will simply hide 403 // itself, to be closed later when the client object itself is 404 // destroyed. If we have no client then we must have been started 405 // from the "easy" functions which simply instantiate a TFilePanel 406 // and expect it to go away by itself 407 408 if (fClientObject) { 409 Hide(); 410 if (fClientObject) 411 fClientObject->WasHidden(); 412 413 BMessage message(*fMessage); 414 message.what = B_CANCEL; 415 message.AddInt32("old_what", (int32)fMessage->what); 416 message.AddPointer("source", fClientObject); 417 fTarget.SendMessage(&message); 418 return false; 419 } 420 421 return _inherited::QuitRequested(); 422 } 423 424 425 BRefFilter * 426 TFilePanel::Filter() const 427 { 428 return fPoseView->RefFilter(); 429 } 430 431 432 void 433 TFilePanel::SetTarget(BMessenger target) 434 { 435 fTarget = target; 436 } 437 438 439 void 440 TFilePanel::SetMessage(BMessage *message) 441 { 442 delete fMessage; 443 fMessage = new BMessage(*message); 444 } 445 446 447 void 448 TFilePanel::SetRefFilter(BRefFilter *filter) 449 { 450 if (!filter) 451 return; 452 453 fPoseView->SetRefFilter(filter); 454 fPoseView->CommitActivePose(); 455 fPoseView->Refresh(); 456 } 457 458 459 void 460 TFilePanel::SetTo(const entry_ref *ref) 461 { 462 if (!ref) 463 return; 464 465 entry_ref setToRef(*ref); 466 467 bool isDesktop = SwitchDirToDesktopIfNeeded(setToRef); 468 469 BEntry entry(&setToRef); 470 if (entry.InitCheck() != B_OK || !entry.IsDirectory()) 471 return; 472 473 SwitchDirMenuTo(&setToRef); 474 475 PoseView()->SetIsDesktop(isDesktop); 476 fPoseView->SwitchDir(&setToRef); 477 478 AddShortcut('H', B_COMMAND_KEY, new BMessage(kSwitchToHome)); 479 // our shortcut got possibly removed because the home 480 // menu item got removed - we shouldn't really have to do 481 // this - this is a workaround for a kit bug. 482 } 483 484 485 void 486 TFilePanel::Rewind() 487 { 488 fSelectionIterator = 0; 489 } 490 491 492 void 493 TFilePanel::SetClientObject(BFilePanel *panel) 494 { 495 fClientObject = panel; 496 } 497 498 499 void 500 TFilePanel::AdjustButton() 501 { 502 // adjust button state 503 BButton *button = dynamic_cast<BButton *>(FindView("default button")); 504 if (!button) 505 return; 506 507 BTextControl *textControl = dynamic_cast<BTextControl *>(FindView("text view")); 508 BObjectList<BPose> *selectionList = fPoseView->SelectionList(); 509 const char *buttonText = fButtonText.String(); 510 bool enabled = false; 511 512 if (fIsSavePanel && textControl) { 513 enabled = textControl->Text()[0] != '\0'; 514 if (fPoseView->IsFocus()) { 515 fPoseView->ShowSelection(true); 516 if (selectionList->CountItems() == 1) { 517 Model *model = selectionList->FirstItem()->TargetModel(); 518 if (model->ResolveIfLink()->IsDirectory()) { 519 enabled = true; 520 buttonText = "Open"; 521 } else { 522 // insert the name of the selected model into the text field 523 textControl->SetText(model->Name()); 524 textControl->MakeFocus(true); 525 } 526 } 527 } else 528 fPoseView->ShowSelection(false); 529 } else { 530 int32 count = selectionList->CountItems(); 531 if (count) { 532 enabled = true; 533 534 // go through selection list looking at content 535 for (int32 index = 0; index < count; index++) { 536 Model *model = selectionList->ItemAt(index)->TargetModel(); 537 538 uint32 modelFlavor = GetLinkFlavor(model, false); 539 uint32 linkFlavor = GetLinkFlavor(model, true); 540 541 // if only one item is selected and we're not in dir 542 // selection mode then we don't disable button ever 543 if ((modelFlavor == B_DIRECTORY_NODE 544 || linkFlavor == B_DIRECTORY_NODE) 545 && count == 1) 546 break; 547 548 if ((fNodeFlavors & modelFlavor) == 0 549 && (fNodeFlavors & linkFlavor) == 0) { 550 enabled = false; 551 break; 552 } 553 } 554 } 555 } 556 557 button->SetLabel(buttonText); 558 button->SetEnabled(enabled); 559 } 560 561 562 void 563 TFilePanel::SelectionChanged() 564 { 565 AdjustButton(); 566 567 if (fClientObject) 568 fClientObject->SelectionChanged(); 569 } 570 571 572 status_t 573 TFilePanel::GetNextEntryRef(entry_ref *ref) 574 { 575 if (!ref) 576 return B_ERROR; 577 578 BPose *pose = fPoseView->SelectionList()->ItemAt(fSelectionIterator++); 579 if (!pose) 580 return B_ERROR; 581 582 *ref = *pose->TargetModel()->EntryRef(); 583 return B_OK; 584 } 585 586 587 BPoseView * 588 TFilePanel::NewPoseView(Model *model, BRect rect, uint32) 589 { 590 return new BFilePanelPoseView(model, rect); 591 } 592 593 594 void 595 TFilePanel::Init(const BMessage *) 596 { 597 BRect windRect(Bounds()); 598 AddChild(fBackView = new BackgroundView(windRect)); 599 600 // add poseview menu bar 601 fMenuBar = new BMenuBar(BRect(0, 0, windRect.Width(), 1), "MenuBar"); 602 fMenuBar->SetBorder(B_BORDER_FRAME); 603 fBackView->AddChild(fMenuBar); 604 605 AddMenus(); 606 AddContextMenus(); 607 608 FavoritesMenu *favorites = new FavoritesMenu("Favorites", 609 new BMessage(kSwitchDirectory), new BMessage(B_REFS_RECEIVED), 610 BMessenger(this), IsSavePanel()); 611 favorites->AddItem(new BMenuItem("Add Current Folder", 612 new BMessage(kAddCurrentDir))); 613 favorites->AddItem(new BMenuItem("Configure Favorites"B_UTF8_ELLIPSIS, 614 new BMessage(kConfigShow))); 615 616 fMenuBar->AddItem(favorites); 617 618 // configure menus 619 BMenuItem *item = fMenuBar->FindItem("Window"); 620 if (item) { 621 fMenuBar->RemoveItem(item); 622 delete item; 623 } 624 625 item = fMenuBar->FindItem("File"); 626 if (item) { 627 BMenu *menu = item->Submenu(); 628 if (menu) { 629 item = menu->FindItem(kOpenSelection); 630 if (item && menu->RemoveItem(item)) 631 delete item; 632 633 item = menu->FindItem(kDuplicateSelection); 634 if (item && menu->RemoveItem(item)) 635 delete item; 636 637 // remove add-ons menu, identifier menu, separator 638 item = menu->FindItem(kAddOnsMenuName); 639 if (item) { 640 int32 index = menu->IndexOf(item); 641 delete menu->RemoveItem(index); 642 delete menu->RemoveItem(--index); 643 delete menu->RemoveItem(--index); 644 } 645 646 // remove separator 647 item = menu->FindItem(B_CUT); 648 if (item) { 649 item = menu->ItemAt(menu->IndexOf(item)-1); 650 if (item && menu->RemoveItem(item)) 651 delete item; 652 } 653 } 654 } 655 656 // add directory menu and menufield 657 fDirMenu = new BDirMenu(0, kSwitchDirectory, "refs"); 658 659 font_height ht; 660 be_plain_font->GetHeight(&ht); 661 float f_height = ht.ascent + ht.descent + ht.leading; 662 663 BRect rect; 664 rect.top = fMenuBar->Bounds().Height() + 8; 665 rect.left = windRect.left + 8; 666 rect.right = rect.left + 300; 667 rect.bottom = rect.top + (f_height > 22 ? f_height : 22); 668 669 fDirMenuField = new BMenuField(rect, "DirMenuField", "", fDirMenu); 670 fDirMenuField->MenuBar()->SetFont(be_plain_font); 671 fDirMenuField->SetDivider(0); 672 673 fDirMenuField->MenuBar()->RemoveItem((int32)0); 674 fDirMenu->SetMenuBar(fDirMenuField->MenuBar()); 675 // the above is a weird call from BDirMenu 676 // ToDo: clean up 677 678 BEntry entry(TargetModel()->EntryRef()); 679 if (entry.InitCheck() == B_OK) 680 fDirMenu->Populate(&entry, 0, true, true, false, true); 681 else 682 fDirMenu->Populate(0, 0, true, true, false, true); 683 684 fBackView->AddChild(fDirMenuField); 685 686 // add file name text view 687 if (fIsSavePanel) { 688 BRect rect(windRect); 689 rect.top = rect.bottom - 35; 690 rect.left = 8; 691 rect.right = rect.left + 170; 692 rect.bottom = rect.top + 13; 693 694 fTextControl = new BTextControl(rect, "text view", "save text", "", NULL, 695 B_FOLLOW_LEFT | B_FOLLOW_BOTTOM); 696 DisallowMetaKeys(fTextControl->TextView()); 697 DisallowFilenameKeys(fTextControl->TextView()); 698 fBackView->AddChild(fTextControl); 699 fTextControl->SetDivider(0.0f); 700 fTextControl->TextView()->SetMaxBytes(B_FILE_NAME_LENGTH - 1); 701 702 fButtonText = "Save"; 703 } else 704 fButtonText = "Open"; 705 706 rect = windRect; 707 rect.OffsetTo(10, fDirMenuField->Frame().bottom + 10); 708 rect.bottom = windRect.bottom - 60; 709 rect.right -= B_V_SCROLL_BAR_WIDTH + 20; 710 711 // re-parent the poseview to our backview 712 // ToDo: 713 // This is terrible, fix it up 714 PoseView()->RemoveSelf(); 715 if (fIsSavePanel) 716 fBackView->AddChild(PoseView(), fTextControl); 717 else 718 fBackView->AddChild(PoseView()); 719 720 PoseView()->MoveTo(rect.LeftTop()); 721 PoseView()->ResizeTo(rect.Width(), rect.Height()); 722 PoseView()->AddScrollBars(); 723 PoseView()->SetDragEnabled(false); 724 PoseView()->SetDropEnabled(false); 725 PoseView()->SetSelectionHandler(this); 726 PoseView()->SetSelectionChangedHook(true); 727 PoseView()->DisableSaveLocation(); 728 PoseView()->VScrollBar()->MoveBy(0, -1); 729 PoseView()->VScrollBar()->ResizeBy(0, 1); 730 731 732 AddShortcut('W', B_COMMAND_KEY, new BMessage(kCancelButton)); 733 AddShortcut('H', B_COMMAND_KEY, new BMessage(kSwitchToHome)); 734 AddShortcut(B_DOWN_ARROW, B_COMMAND_KEY, new BMessage(kOpenDir)); 735 AddShortcut(B_DOWN_ARROW, B_COMMAND_KEY | B_OPTION_KEY, new BMessage(kOpenDir)); 736 AddShortcut(B_UP_ARROW, B_COMMAND_KEY, new BMessage(kOpenParentDir)); 737 AddShortcut(B_UP_ARROW, B_COMMAND_KEY | B_OPTION_KEY, new BMessage(kOpenParentDir)); 738 739 // New code to make buttons font sensitive 740 rect = windRect; 741 rect.top = rect.bottom - 35; 742 rect.bottom -= 10; 743 rect.right -= 25; 744 float default_width = be_plain_font->StringWidth(fButtonText.String()) + 20; 745 rect.left = (default_width > 75) ? (rect.right - default_width) : (rect.right - 75); 746 747 BButton *default_button = new BButton(rect, "default button", fButtonText.String(), 748 new BMessage(kDefaultButton), B_FOLLOW_RIGHT + B_FOLLOW_BOTTOM); 749 fBackView->AddChild(default_button); 750 751 rect.right = rect.left -= 10; 752 float cancel_width = be_plain_font->StringWidth("Cancel") + 20; 753 rect.left = (cancel_width > 75) ? (rect.right - cancel_width) : (rect.right - 75); 754 755 BButton *cancel_button = new BButton(rect, "cancel button", "Cancel", 756 new BMessage(kCancelButton), B_FOLLOW_RIGHT + B_FOLLOW_BOTTOM); 757 fBackView->AddChild(cancel_button); 758 759 if (!fIsSavePanel) 760 default_button->SetEnabled(false); 761 762 default_button->MakeDefault(true); 763 764 RestoreState(); 765 766 PoseView()->ScrollTo(B_ORIGIN); 767 PoseView()->UpdateScrollRange(); 768 PoseView()->ScrollTo(B_ORIGIN); 769 770 if (fTextControl) { 771 fTextControl->MakeFocus(); 772 fTextControl->TextView()->SelectAll(); 773 } else 774 PoseView()->MakeFocus(); 775 776 app_info info; 777 BString title; 778 if (be_app->GetAppInfo(&info) == B_OK) 779 title << info.ref.name << ": "; 780 781 title << fButtonText; // Open or Save 782 783 SetTitle(title.String()); 784 785 SetSizeLimits(370, 10000, 200, 10000); 786 } 787 788 789 void 790 TFilePanel::RestoreState() 791 { 792 BNode defaultingNode; 793 if (DefaultStateSourceNode(kDefaultFilePanelTemplate, &defaultingNode, false)) { 794 AttributeStreamFileNode streamNodeSource(&defaultingNode); 795 RestoreWindowState(&streamNodeSource); 796 PoseView()->Init(&streamNodeSource); 797 } else { 798 RestoreWindowState(NULL); 799 PoseView()->Init(NULL); 800 } 801 } 802 803 804 void 805 TFilePanel::SaveState(bool) 806 { 807 BNode defaultingNode; 808 if (DefaultStateSourceNode(kDefaultFilePanelTemplate, &defaultingNode, 809 true, false)) { 810 AttributeStreamFileNode streamNodeDestination(&defaultingNode); 811 SaveWindowState(&streamNodeDestination); 812 PoseView()->SaveState(&streamNodeDestination); 813 } 814 } 815 816 817 void 818 TFilePanel::SaveState(BMessage &message) const 819 { 820 _inherited::SaveState(message); 821 } 822 823 824 void 825 TFilePanel::RestoreWindowState(AttributeStreamNode *node) 826 { 827 SetSizeLimits(360, 10000, 200, 10000); 828 if (!node) 829 return; 830 831 const char *rectAttributeName = kAttrWindowFrame; 832 BRect frame(Frame()); 833 if (node->Read(rectAttributeName, 0, B_RECT_TYPE, sizeof(BRect), &frame) 834 == sizeof(BRect)) { 835 MoveTo(frame.LeftTop()); 836 ResizeTo(frame.Width(), frame.Height()); 837 } 838 } 839 840 841 void 842 TFilePanel::RestoreState(const BMessage &message) 843 { 844 _inherited::RestoreState(message); 845 } 846 847 848 void 849 TFilePanel::RestoreWindowState(const BMessage &message) 850 { 851 _inherited::RestoreWindowState(message); 852 } 853 854 855 void 856 TFilePanel::AddFileContextMenus(BMenu *menu) 857 { 858 menu->AddItem(new BMenuItem("Get Info", new BMessage(kGetInfo), 'I')); 859 menu->AddItem(new BMenuItem("Edit Name", new BMessage(kEditItem), 'E')); 860 menu->AddItem(new BMenuItem(TrackerSettings().DontMoveFilesToTrash() ? 861 "Delete" : "Move to Trash", 862 new BMessage(kMoveToTrash), 'T')); 863 menu->AddSeparatorItem(); 864 menu->AddItem(new BMenuItem("Cut", new BMessage(B_CUT), 'X')); 865 menu->AddItem(new BMenuItem("Copy", new BMessage(B_COPY), 'C')); 866 // menu->AddItem(pasteItem = new BMenuItem("Paste", new BMessage(B_PASTE), 'V')); 867 868 menu->SetTargetForItems(PoseView()); 869 } 870 871 872 void 873 TFilePanel::AddVolumeContextMenus(BMenu *menu) 874 { 875 menu->AddItem(new BMenuItem("Open", new BMessage(kOpenSelection), 'O')); 876 menu->AddItem(new BMenuItem("Get Info", new BMessage(kGetInfo), 'I')); 877 menu->AddItem(new BMenuItem("Edit Name", new BMessage(kEditItem), 'E')); 878 menu->AddSeparatorItem(); 879 menu->AddItem(new BMenuItem("Cut", new BMessage(B_CUT), 'X')); 880 menu->AddItem(new BMenuItem("Copy", new BMessage(B_COPY), 'C')); 881 // menu->AddItem(pasteItem = new BMenuItem("Paste", new BMessage(B_PASTE), 'V')); 882 883 menu->SetTargetForItems(PoseView()); 884 } 885 886 887 void 888 TFilePanel::AddWindowContextMenus(BMenu *menu) 889 { 890 BMenuItem *item = new BMenuItem("New Folder", new BMessage(kNewFolder), 'N'); 891 item->SetTarget(PoseView()); 892 menu->AddItem(item); 893 menu->AddSeparatorItem(); 894 895 item = new BMenuItem("Paste", new BMessage(B_PASTE), 'V'); 896 item->SetTarget(PoseView()); 897 menu->AddItem(item); 898 menu->AddSeparatorItem(); 899 900 item = new BMenuItem("Select"B_UTF8_ELLIPSIS, new BMessage(kShowSelectionWindow), 901 'A', B_SHIFT_KEY); 902 item->SetTarget(PoseView()); 903 menu->AddItem(item); 904 905 item = new BMenuItem("Select All", new BMessage(B_SELECT_ALL), 'A'); 906 item->SetTarget(PoseView()); 907 menu->AddItem(item); 908 909 item = new BMenuItem("Invert Selection", new BMessage(kInvertSelection), 'S'); 910 item->SetTarget(PoseView()); 911 menu->AddItem(item); 912 913 item = new BMenuItem("Go To Parent", new BMessage(kOpenParentDir), B_UP_ARROW); 914 item->SetTarget(this); 915 menu->AddItem(item); 916 } 917 918 919 void 920 TFilePanel::AddDropContextMenus(BMenu *) 921 { 922 } 923 924 925 void 926 TFilePanel::MenusBeginning() 927 { 928 int32 count = PoseView()->SelectionList()->CountItems(); 929 930 EnableNamedMenuItem(fMenuBar, kNewFolder, !TargetModel()->IsRoot()); 931 EnableNamedMenuItem(fMenuBar, kMoveToTrash, !TargetModel()->IsRoot() && count); 932 EnableNamedMenuItem(fMenuBar, kGetInfo, count != 0); 933 EnableNamedMenuItem(fMenuBar, kEditItem, count == 1); 934 935 SetCutItem(fMenuBar); 936 SetCopyItem(fMenuBar); 937 SetPasteItem(fMenuBar); 938 939 fIsTrackingMenu = true; 940 } 941 942 943 void 944 TFilePanel::MenusEnded() 945 { 946 fIsTrackingMenu = false; 947 } 948 949 950 void 951 TFilePanel::ShowContextMenu(BPoint point, const entry_ref *ref, BView *view) 952 { 953 EnableNamedMenuItem(fWindowContextMenu, kNewFolder, !TargetModel()->IsRoot()); 954 EnableNamedMenuItem(fWindowContextMenu, kOpenParentDir, !TargetModel()->IsRoot()); 955 EnableNamedMenuItem(fWindowContextMenu, kMoveToTrash, !TargetModel()->IsRoot()); 956 957 _inherited::ShowContextMenu(point, ref, view); 958 } 959 960 961 void 962 TFilePanel::SetupNavigationMenu(const entry_ref *, BMenu *) 963 { 964 // do nothing here so nav menu doesn't get added 965 } 966 967 968 void 969 TFilePanel::SetButtonLabel(file_panel_button selector, const char *text) 970 { 971 switch (selector) { 972 case B_CANCEL_BUTTON: 973 { 974 BButton *button = dynamic_cast<BButton *>(FindView("cancel button")); 975 if (!button) 976 break; 977 978 float old_width = button->StringWidth(button->Label()); 979 button->SetLabel(text); 980 float delta = old_width - button->StringWidth(text); 981 if (delta) { 982 button->MoveBy(delta, 0); 983 button->ResizeBy(-delta, 0); 984 } 985 } 986 break; 987 988 case B_DEFAULT_BUTTON: 989 { 990 fButtonText = text; 991 float delta = 0; 992 BButton *button = dynamic_cast<BButton *>(FindView("default button")); 993 if (button) { 994 float old_width = button->StringWidth(button->Label()); 995 button->SetLabel(text); 996 delta = old_width - button->StringWidth(text); 997 if (delta) { 998 button->MoveBy(delta, 0); 999 button->ResizeBy(-delta, 0); 1000 } 1001 } 1002 1003 // now must move cancel button 1004 button = dynamic_cast<BButton *>(FindView("cancel button")); 1005 if (button) 1006 button->MoveBy(delta, 0); 1007 } 1008 break; 1009 } 1010 } 1011 1012 1013 void 1014 TFilePanel::SetSaveText(const char *text) 1015 { 1016 if (!text) 1017 return; 1018 1019 BTextControl *textControl = dynamic_cast<BTextControl *>(FindView("text view")); 1020 textControl->SetText(text); 1021 textControl->TextView()->SelectAll(); 1022 } 1023 1024 1025 void 1026 TFilePanel::MessageReceived(BMessage *message) 1027 { 1028 entry_ref ref; 1029 1030 switch (message->what) { 1031 case B_REFS_RECEIVED: 1032 // item was double clicked in file panel (PoseView) 1033 if (message->FindRef("refs", &ref) == B_OK) { 1034 BEntry entry(&ref, true); 1035 if (entry.InitCheck() == B_OK) { 1036 // Double-click on dir or link-to-dir ALWAYS opens the dir. 1037 // If more than one dir is selected, the 1038 // first is entered. 1039 if (entry.IsDirectory()) { 1040 entry.GetRef(&ref); 1041 bool isDesktop = SwitchDirToDesktopIfNeeded(ref); 1042 1043 PoseView()->SetIsDesktop(isDesktop); 1044 entry.SetTo(&ref); 1045 PoseView()->SwitchDir(&ref); 1046 SwitchDirMenuTo(&ref); 1047 } else { 1048 // Otherwise, we have a file or a link to a file. 1049 // AdjustButton has already tested the flavor; 1050 // all we have to do is see if the button is enabled. 1051 BButton *button = dynamic_cast<BButton *>(FindView("default button")); 1052 if (!button) 1053 break; 1054 1055 if (IsSavePanel()) { 1056 int32 count = 0; 1057 type_code type; 1058 message->GetInfo("refs", &type, &count); 1059 1060 // Don't allow saves of multiple files 1061 if (count > 1) { 1062 ShowCenteredAlert("Sorry, saving of more than one item is not allowed.", 1063 "Cancel"); 1064 } else { 1065 // if we are a savepanel, set up the filepanel correctly 1066 // then pass control so we follow the same path as if the user 1067 // clicked the save button 1068 1069 // set the 'name' fld to the current ref's name 1070 // notify the panel that the default button should be enabled 1071 SetSaveText(ref.name); 1072 SelectionChanged(); 1073 1074 HandleSaveButton(); 1075 } 1076 break; 1077 } 1078 1079 // send handler a message and close 1080 BMessage openMessage(*fMessage); 1081 for (int32 index = 0; ; index++) { 1082 if (message->FindRef("refs", index, &ref) != B_OK) 1083 break; 1084 openMessage.AddRef("refs", &ref); 1085 } 1086 OpenSelectionCommon(&openMessage); 1087 } 1088 } 1089 } 1090 break; 1091 1092 case kSwitchDirectory: 1093 { 1094 entry_ref ref; 1095 // this comes from dir menu or nav menu, so switch directories 1096 if (message->FindRef("refs", &ref) == B_OK) { 1097 BEntry entry(&ref, true); 1098 if (entry.GetRef(&ref) == B_OK) 1099 SetTo(&ref); 1100 } 1101 break; 1102 } 1103 1104 case kSwitchToHome: 1105 { 1106 BPath homePath; 1107 entry_ref ref; 1108 if (find_directory(B_USER_DIRECTORY, &homePath) != B_OK 1109 || get_ref_for_path(homePath.Path(), &ref) != B_OK) 1110 break; 1111 1112 SetTo(&ref); 1113 break; 1114 } 1115 1116 case kAddCurrentDir: 1117 { 1118 BPath path; 1119 if (find_directory (B_USER_SETTINGS_DIRECTORY, &path, true) != B_OK) 1120 break; 1121 1122 path.Append(kGoDirectory); 1123 BDirectory goDirectory(path.Path()); 1124 1125 if (goDirectory.InitCheck() == B_OK) { 1126 BEntry entry(TargetModel()->EntryRef()); 1127 entry.GetPath(&path); 1128 1129 BSymLink link; 1130 goDirectory.CreateSymLink(TargetModel()->Name(), path.Path(), &link); 1131 } 1132 break; 1133 } 1134 1135 case kConfigShow: 1136 { 1137 if (fConfigWindow) { 1138 fConfigWindow->Activate(); 1139 break; 1140 } 1141 1142 BPath path; 1143 if (find_directory (B_USER_SETTINGS_DIRECTORY, &path, true) != B_OK) 1144 break; 1145 1146 path.Append(kGoDirectory); 1147 BDirectory goDirectory(path.Path()); 1148 1149 if (goDirectory.InitCheck() == B_OK) { 1150 entry_ref startref; 1151 BEntry entry; 1152 goDirectory.GetEntry(&entry); 1153 entry.GetRef(&startref); 1154 1155 int32 apps, docs, folders; 1156 TrackerSettings().RecentCounts(&apps, &docs, &folders); 1157 1158 // if this is a save panel 1159 // then don't show recent docs controls 1160 if (fIsSavePanel) 1161 docs = -1; 1162 1163 fConfigWindow = new TFavoritesConfigWindow(BRect(0, 0, 320, 24), 1164 "Configure Favorites", Feel() == B_MODAL_APP_WINDOW_FEEL, 1165 B_DIRECTORY_NODE, BMessenger(this), &startref, -1, docs, folders); 1166 } 1167 break; 1168 } 1169 1170 case kConfigClose: 1171 { 1172 int32 count = 0; 1173 TrackerSettings settings; 1174 1175 // save off whatever was last in the fields 1176 // do this just in case someone didn't tab out 1177 if (message->FindInt32("applications", &count) == B_OK) 1178 settings.SetRecentApplicationsCount(count); 1179 if (message->FindInt32("folders", &count) == B_OK) 1180 settings.SetRecentFoldersCount(count); 1181 if (message->FindInt32("documents", &count) == B_OK) 1182 settings.SetRecentDocumentsCount(count); 1183 1184 settings.SaveSettings(false); 1185 1186 fConfigWindow = NULL; 1187 break; 1188 } 1189 1190 case kUpdateAppsCount: 1191 case kUpdateDocsCount: 1192 case kUpdateFolderCount: 1193 { 1194 // messages sent when the user changes the count 1195 int32 count; 1196 TrackerSettings settings; 1197 1198 if (message->FindInt32("count", &count) == B_OK) { 1199 if (message->what == kUpdateAppsCount) 1200 settings.SetRecentApplicationsCount(count); 1201 else if (message->what == kUpdateDocsCount) 1202 settings.SetRecentDocumentsCount(count); 1203 else if (message->what == kUpdateFolderCount) 1204 settings.SetRecentFoldersCount(count); 1205 settings.SaveSettings(false); 1206 } 1207 break; 1208 } 1209 1210 case kCancelButton: 1211 PostMessage(B_QUIT_REQUESTED); 1212 break; 1213 1214 case kOpenDir: 1215 OpenDirectory(); 1216 break; 1217 1218 case kOpenParentDir: 1219 OpenParent(); 1220 break; 1221 1222 case kDefaultButton: 1223 if (fIsSavePanel) { 1224 if (PoseView()->IsFocus() 1225 && PoseView()->SelectionList()->CountItems() == 1) { 1226 Model *model = (PoseView()->SelectionList()->FirstItem())->TargetModel(); 1227 if (model->ResolveIfLink()->IsDirectory()) { 1228 PoseView()->CommitActivePose(); 1229 PoseView()->OpenSelection(); 1230 break; 1231 } 1232 } 1233 1234 HandleSaveButton(); 1235 } else 1236 HandleOpenButton(); 1237 break; 1238 1239 case B_OBSERVER_NOTICE_CHANGE: 1240 { 1241 int32 observerWhat; 1242 if (message->FindInt32("be:observe_change_what", &observerWhat) == B_OK) { 1243 switch (observerWhat) { 1244 case kDesktopFilePanelRootChanged: 1245 { 1246 bool desktopIsRoot = true; 1247 if (message->FindBool("DesktopFilePanelRoot", &desktopIsRoot) == B_OK) 1248 TrackerSettings().SetDesktopFilePanelRoot(desktopIsRoot); 1249 SetTo(TargetModel()->EntryRef()); 1250 break; 1251 } 1252 } 1253 } 1254 break; 1255 } 1256 1257 default: 1258 _inherited::MessageReceived(message); 1259 } 1260 } 1261 1262 1263 void 1264 TFilePanel::OpenDirectory() 1265 { 1266 BObjectList<BPose> *list = PoseView()->SelectionList(); 1267 if (list->CountItems() != 1) 1268 return; 1269 1270 Model *model = list->FirstItem()->TargetModel(); 1271 if (model->ResolveIfLink()->IsDirectory()) { 1272 BMessage message(B_REFS_RECEIVED); 1273 message.AddRef("refs", model->EntryRef()); 1274 PostMessage(&message); 1275 } 1276 } 1277 1278 1279 void 1280 TFilePanel::OpenParent() 1281 { 1282 if (!CanOpenParent()) 1283 return; 1284 1285 BEntry parentEntry; 1286 BDirectory dir; 1287 1288 Model oldModel(*PoseView()->TargetModel()); 1289 BEntry entry(oldModel.EntryRef()); 1290 1291 if (entry.InitCheck() == B_OK 1292 && entry.GetParent(&dir) == B_OK 1293 && dir.GetEntry(&parentEntry) == B_OK 1294 && entry != parentEntry) { 1295 1296 entry_ref ref; 1297 parentEntry.GetRef(&ref); 1298 1299 PoseView()->SetIsDesktop(SwitchDirToDesktopIfNeeded(ref)); 1300 PoseView()->SwitchDir(&ref); 1301 SwitchDirMenuTo(&ref); 1302 1303 // make sure the child get's selected in the new view once it 1304 // shows up 1305 fTaskLoop->RunLater(NewMemberFunctionObjectWithResult 1306 (&TFilePanel::SelectChildInParent, this, 1307 const_cast<const entry_ref *>(&ref), 1308 oldModel.NodeRef()), 100000, 200000, 5000000); 1309 } 1310 } 1311 1312 1313 bool 1314 TFilePanel::CanOpenParent() const 1315 { 1316 if (TrackerSettings().DesktopFilePanelRoot()) { 1317 // don't allow opening Desktop folder's parent 1318 BEntry entry(TargetModel()->EntryRef()); 1319 if (FSIsDeskDir(&entry, TargetModel()->NodeRef()->device)) 1320 return false; 1321 } 1322 1323 // block on "/" 1324 BEntry root("/"); 1325 node_ref rootRef; 1326 root.GetNodeRef(&rootRef); 1327 1328 return rootRef != *TargetModel()->NodeRef(); 1329 } 1330 1331 1332 bool 1333 TFilePanel::SwitchDirToDesktopIfNeeded(entry_ref &ref) 1334 { 1335 // support showing Desktop as root of everything 1336 // This call implements the worm hole that maps Desktop as 1337 // a root above the disks 1338 TrackerSettings settings; 1339 if (!settings.DesktopFilePanelRoot()) 1340 // Tracker isn't set up that way, just let Disks show 1341 return false; 1342 1343 BEntry entry(&ref); 1344 BEntry root("/"); 1345 1346 BDirectory desktopDir; 1347 BVolume bootVol; 1348 BVolumeRoster().GetBootVolume(&bootVol); 1349 FSGetDeskDir(&desktopDir, bootVol.Device()); 1350 1351 if ((bootVol.Device() != ref.device && FSIsDeskDir(&entry, ref.device)) 1352 // navigated into non-boot desktop, switch to boot desktop 1353 || (entry == root && !settings.ShowDisksIcon())) { 1354 // hit "/" level, map to desktop 1355 1356 desktopDir.GetEntry(&entry); 1357 entry.GetRef(&ref); 1358 return true; 1359 } 1360 return FSIsDeskDir(&entry, ref.device); 1361 } 1362 1363 1364 bool 1365 TFilePanel::SelectChildInParent(const entry_ref *, const node_ref *child) 1366 { 1367 AutoLock<TFilePanel> lock(this); 1368 1369 if (!IsLocked()) 1370 return false; 1371 1372 int32 index; 1373 BPose *pose = PoseView()->FindPose(child, &index); 1374 if (!pose) 1375 return false; 1376 1377 PoseView()->UpdateScrollRange(); 1378 // ToDo: Scroll range should be updated by now, for some 1379 // reason sometimes it is not right, force it here 1380 PoseView()->SelectPose(pose, index, true); 1381 return true; 1382 } 1383 1384 1385 int32 1386 TFilePanel::ShowCenteredAlert(const char *text, const char *button1, 1387 const char *button2, const char *button3) 1388 { 1389 BAlert *alert = new BAlert("", text, button1, button2, button3, 1390 B_WIDTH_AS_USUAL, B_WARNING_ALERT); 1391 alert->MoveTo(Frame().left + 10, Frame().top + 10); 1392 1393 #if 0 1394 if (button1 != NULL && !strncmp(button1, "Cancel", 7)) 1395 alert->SetShortcut(0, B_ESCAPE); 1396 else if (button2 != NULL && !strncmp(button2, "Cancel", 7)) 1397 alert->SetShortcut(1, B_ESCAPE); 1398 else if (button3 != NULL && !strncmp(button3, "Cancel", 7)) 1399 alert->SetShortcut(2, B_ESCAPE); 1400 #endif 1401 1402 return alert->Go(); 1403 } 1404 1405 1406 void 1407 TFilePanel::HandleSaveButton() 1408 { 1409 BDirectory dir; 1410 1411 if (TargetModel()->IsRoot()) { 1412 ShowCenteredAlert("Sorry, you can't save things at the root of " 1413 "your system.", "Cancel"); 1414 return; 1415 } 1416 1417 // check for some illegal file names 1418 if (strcmp(fTextControl->Text(), ".") == 0 1419 || strcmp(fTextControl->Text(), "..") == 0) { 1420 ShowCenteredAlert("The name you have specified is illegal. Please type " 1421 "another name.", "Cancel"); 1422 fTextControl->TextView()->SelectAll(); 1423 return; 1424 } 1425 1426 if (dir.SetTo(TargetModel()->EntryRef()) != B_OK) { 1427 ShowCenteredAlert("There was a problem trying to save in the folder " 1428 "you specified. Please try another one.", "Cancel"); 1429 return; 1430 } 1431 1432 if (dir.Contains(fTextControl->Text())) { 1433 if (dir.Contains(fTextControl->Text(), B_DIRECTORY_NODE)) { 1434 ShowCenteredAlert("The name you have specified is already the name " 1435 "of a folder. Please type another name.", "Cancel"); 1436 fTextControl->TextView()->SelectAll(); 1437 return; 1438 } else { 1439 // if this was invoked by a dbl click, it is an explicit replacement 1440 // of the file. 1441 BString str; 1442 str << "The file \"" << fTextControl->Text() << "\" already exists in the " 1443 "specified folder. Do you want to replace it?"; 1444 1445 if (ShowCenteredAlert(str.String(), "Cancel", "Replace") == 0) { 1446 // user canceled 1447 fTextControl->TextView()->SelectAll(); 1448 return; 1449 } 1450 // user selected "Replace" - let app deal with it 1451 } 1452 } 1453 1454 BMessage message(*fMessage); 1455 message.AddRef("directory", TargetModel()->EntryRef()); 1456 message.AddString("name", fTextControl->Text()); 1457 1458 if (fClientObject) 1459 fClientObject->SendMessage(&fTarget, &message); 1460 else 1461 fTarget.SendMessage(&message); 1462 1463 // close window if we're dealing with standard message 1464 if (fHideWhenDone) 1465 PostMessage(B_QUIT_REQUESTED); 1466 } 1467 1468 1469 void 1470 TFilePanel::OpenSelectionCommon(BMessage *openMessage) 1471 { 1472 if (!openMessage->HasRef("refs")) 1473 return; 1474 1475 for (int32 index = 0; ; index++) { 1476 entry_ref ref; 1477 if (openMessage->FindRef("refs", index, &ref) != B_OK) 1478 break; 1479 1480 BEntry entry(&ref, true); 1481 if (entry.InitCheck() == B_OK) { 1482 if (entry.IsDirectory()) 1483 BRoster().AddToRecentFolders(&ref); 1484 else 1485 BRoster().AddToRecentDocuments(&ref); 1486 } 1487 } 1488 1489 BRoster().AddToRecentFolders(TargetModel()->EntryRef()); 1490 1491 if (fClientObject) 1492 fClientObject->SendMessage(&fTarget, openMessage); 1493 else 1494 fTarget.SendMessage(openMessage); 1495 1496 // close window if we're dealing with standard message 1497 if (fHideWhenDone) 1498 PostMessage(B_QUIT_REQUESTED); 1499 } 1500 1501 1502 void 1503 TFilePanel::HandleOpenButton() 1504 { 1505 PoseView()->CommitActivePose(); 1506 BObjectList<BPose> *selection = PoseView()->SelectionList(); 1507 1508 // if we have only one directory and we're not opening dirs, enter. 1509 if ((fNodeFlavors & B_DIRECTORY_NODE) == 0 1510 && selection->CountItems() == 1) { 1511 Model *model = selection->FirstItem()->TargetModel(); 1512 1513 if (model->IsDirectory() 1514 || (model->IsSymLink() && !(fNodeFlavors & B_SYMLINK_NODE) 1515 && model->ResolveIfLink()->IsDirectory())) { 1516 1517 BMessage message(B_REFS_RECEIVED); 1518 message.AddRef("refs", model->EntryRef()); 1519 PostMessage(&message); 1520 return; 1521 } 1522 } 1523 1524 // don't do anything unless there are items selected 1525 // message->fMessage->message from here to end 1526 if (selection->CountItems()) { 1527 BMessage message(*fMessage); 1528 // go through selection and add appropriate items 1529 for (int32 index = 0; index < selection->CountItems(); index++) { 1530 Model *model = selection->ItemAt(index)->TargetModel(); 1531 1532 if (((fNodeFlavors & B_DIRECTORY_NODE) != 0 1533 && model->ResolveIfLink()->IsDirectory()) 1534 || ((fNodeFlavors & B_SYMLINK_NODE) != 0 && model->IsSymLink()) 1535 || ((fNodeFlavors & B_FILE_NODE) != 0 && model->ResolveIfLink()->IsFile())) 1536 message.AddRef("refs", model->EntryRef()); 1537 } 1538 1539 OpenSelectionCommon(&message); 1540 } 1541 } 1542 1543 1544 void 1545 TFilePanel::SwitchDirMenuTo(const entry_ref *ref) 1546 { 1547 BEntry entry(ref); 1548 for (int32 index = fDirMenu->CountItems() - 1; index >= 0; index--) 1549 delete fDirMenu->RemoveItem(index); 1550 1551 fDirMenuField->MenuBar()->RemoveItem((int32)0); 1552 fDirMenu->Populate(&entry, 0, true, true, false, true); 1553 1554 ModelMenuItem *item = dynamic_cast<ModelMenuItem *>( 1555 fDirMenuField->MenuBar()->ItemAt(0)); 1556 ASSERT(item); 1557 item->SetEntry(&entry); 1558 } 1559 1560 1561 void 1562 TFilePanel::WindowActivated(bool active) 1563 { 1564 // force focus to update properly 1565 fBackView->Invalidate(); 1566 _inherited::WindowActivated(active); 1567 } 1568 1569 1570 // #pragma mark - 1571 1572 1573 BFilePanelPoseView::BFilePanelPoseView(Model *model, BRect frame, uint32 resizeMask) 1574 : BPoseView(model, frame, kListMode, resizeMask) 1575 { 1576 BEntry entry; 1577 model->GetEntry(&entry); 1578 fIsDesktop = FSIsDeskDir(&entry); 1579 } 1580 1581 1582 void 1583 BFilePanelPoseView::StartWatching() 1584 { 1585 TTracker::WatchNode(0, B_WATCH_MOUNT, this); 1586 1587 // inter-application observing 1588 BMessenger tracker(kTrackerSignature); 1589 BHandler::StartWatching(tracker, kVolumesOnDesktopChanged); 1590 BHandler::StartWatching(tracker, kDesktopIntegrationChanged); 1591 } 1592 1593 1594 void 1595 BFilePanelPoseView::StopWatching() 1596 { 1597 stop_watching(this); 1598 1599 // inter-application observing 1600 BMessenger tracker(kTrackerSignature); 1601 BHandler::StopWatching(tracker, kVolumesOnDesktopChanged); 1602 BHandler::StopWatching(tracker, kDesktopIntegrationChanged); 1603 } 1604 1605 1606 bool 1607 BFilePanelPoseView::FSNotification(const BMessage *message) 1608 { 1609 if (IsDesktopView()) { 1610 // Pretty much copied straight from DesktopPoseView. Would be better 1611 // if the code could be shared somehow. 1612 switch (message->FindInt32("opcode")) { 1613 case B_DEVICE_MOUNTED: 1614 { 1615 dev_t device; 1616 if (message->FindInt32("new device", &device) != B_OK) 1617 break; 1618 1619 ASSERT(TargetModel()); 1620 TrackerSettings settings; 1621 1622 BVolume volume(device); 1623 if (volume.InitCheck() != B_OK) 1624 break; 1625 1626 if (settings.MountVolumesOntoDesktop() 1627 && (!volume.IsShared() || settings.MountSharedVolumesOntoDesktop())) { 1628 // place an icon for the volume onto the desktop 1629 CreateVolumePose(&volume, true); 1630 } 1631 1632 if (!ShouldIntegrateDesktop(volume)) 1633 break; 1634 1635 BDirectory otherDesktop; 1636 BEntry entry; 1637 1638 if (FSGetDeskDir(&otherDesktop, volume.Device()) == B_OK 1639 && otherDesktop.GetEntry(&entry) == B_OK) { 1640 // place desktop items from the mounted volume onto the desktop 1641 Model model(&entry); 1642 if (model.InitCheck() == B_OK) 1643 AddPoses(&model); 1644 } 1645 } 1646 break; 1647 } 1648 } 1649 return _inherited::FSNotification(message); 1650 } 1651 1652 1653 void 1654 BFilePanelPoseView::RestoreState(AttributeStreamNode *node) 1655 { 1656 _inherited::RestoreState(node); 1657 fViewState->SetViewMode(kListMode); 1658 } 1659 1660 1661 void 1662 BFilePanelPoseView::RestoreState(const BMessage &message) 1663 { 1664 _inherited::RestoreState(message); 1665 } 1666 1667 1668 void 1669 BFilePanelPoseView::SavePoseLocations(BRect *) 1670 { 1671 } 1672 1673 1674 EntryListBase * 1675 BFilePanelPoseView::InitDirentIterator(const entry_ref *ref) 1676 { 1677 if (IsDesktopView()) 1678 return DesktopPoseView::InitDesktopDirentIterator(this, ref); 1679 1680 return _inherited::InitDirentIterator(ref); 1681 } 1682 1683 1684 bool 1685 BFilePanelPoseView::ShouldShowPose(const Model *model, const PoseInfo *poseInfo) 1686 { 1687 if (IsDesktopView() && !ShouldShowDesktopPose(TargetModel()->NodeRef()->device, 1688 model, poseInfo)) 1689 return false; 1690 1691 return _inherited::ShouldShowPose(model, poseInfo); 1692 } 1693 1694 1695 void 1696 BFilePanelPoseView::SetIsDesktop(bool on) 1697 { 1698 fIsDesktop = on; 1699 } 1700 1701 1702 bool 1703 BFilePanelPoseView::IsDesktopView() const 1704 { 1705 return fIsDesktop; 1706 } 1707 1708 1709 void 1710 BFilePanelPoseView::ShowVolumes(bool visible, bool showShared) 1711 { 1712 if (IsDesktopView()) { 1713 if (!visible) 1714 RemoveRootPoses(); 1715 else 1716 AddRootPoses(true, showShared); 1717 } 1718 1719 1720 TFilePanel *filepanel = dynamic_cast<TFilePanel*>(Window()); 1721 if (filepanel) 1722 filepanel->SetTo(TargetModel()->EntryRef()); 1723 } 1724 1725 1726 void 1727 BFilePanelPoseView::AdaptToVolumeChange(BMessage *message) 1728 { 1729 bool showDisksIcon; 1730 bool mountVolumesOnDesktop; 1731 bool mountSharedVolumesOntoDesktop; 1732 1733 message->FindBool("ShowDisksIcon", &showDisksIcon); 1734 message->FindBool("MountVolumesOntoDesktop", &mountVolumesOnDesktop); 1735 message->FindBool("MountSharedVolumesOntoDesktop", &mountSharedVolumesOntoDesktop); 1736 1737 BEntry entry("/"); 1738 Model model(&entry); 1739 if (model.InitCheck() == B_OK) { 1740 BMessage monitorMsg; 1741 monitorMsg.what = B_NODE_MONITOR; 1742 1743 if (showDisksIcon) 1744 monitorMsg.AddInt32("opcode", B_ENTRY_CREATED); 1745 else 1746 monitorMsg.AddInt32("opcode", B_ENTRY_REMOVED); 1747 1748 monitorMsg.AddInt32("device", model.NodeRef()->device); 1749 monitorMsg.AddInt64("node", model.NodeRef()->node); 1750 monitorMsg.AddInt64("directory", model.EntryRef()->directory); 1751 monitorMsg.AddString("name", model.EntryRef()->name); 1752 TrackerSettings().SetShowDisksIcon(showDisksIcon); 1753 if (Window()) 1754 Window()->PostMessage(&monitorMsg, this); 1755 } 1756 1757 ShowVolumes(mountVolumesOnDesktop, mountSharedVolumesOntoDesktop); 1758 } 1759 1760 1761 void 1762 BFilePanelPoseView::AdaptToDesktopIntegrationChange(BMessage *message) 1763 { 1764 bool mountVolumesOnDesktop = true; 1765 bool mountSharedVolumesOntoDesktop = true; 1766 1767 message->FindBool("MountVolumesOntoDesktop", &mountVolumesOnDesktop); 1768 message->FindBool("MountSharedVolumesOntoDesktop", &mountSharedVolumesOntoDesktop); 1769 1770 ShowVolumes(false, mountSharedVolumesOntoDesktop); 1771 ShowVolumes(mountVolumesOnDesktop, mountSharedVolumesOntoDesktop); 1772 } 1773 1774