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