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, 510, 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 = dynamic_cast<BTextControl *>(FindView("text view")); 511 BObjectList<BPose> *selectionList = fPoseView->SelectionList(); 512 BString buttonText = fButtonText; 513 bool enabled = false; 514 515 if (fIsSavePanel && textControl) { 516 enabled = textControl->Text()[0] != '\0'; 517 if (fPoseView->IsFocus()) { 518 fPoseView->ShowSelection(true); 519 if (selectionList->CountItems() == 1) { 520 Model *model = selectionList->FirstItem()->TargetModel(); 521 if (model->ResolveIfLink()->IsDirectory()) { 522 enabled = true; 523 buttonText = B_TRANSLATE("Open"); 524 } else { 525 // insert the name of the selected model into the text field 526 textControl->SetText(model->Name()); 527 textControl->MakeFocus(true); 528 } 529 } 530 } else 531 fPoseView->ShowSelection(false); 532 } else { 533 int32 count = selectionList->CountItems(); 534 if (count) { 535 enabled = true; 536 537 // go through selection list looking at content 538 for (int32 index = 0; index < count; index++) { 539 Model *model = selectionList->ItemAt(index)->TargetModel(); 540 541 uint32 modelFlavor = GetLinkFlavor(model, false); 542 uint32 linkFlavor = GetLinkFlavor(model, true); 543 544 // if only one item is selected and we're not in dir 545 // selection mode then we don't disable button ever 546 if ((modelFlavor == B_DIRECTORY_NODE 547 || linkFlavor == B_DIRECTORY_NODE) 548 && count == 1) 549 break; 550 551 if ((fNodeFlavors & modelFlavor) == 0 552 && (fNodeFlavors & linkFlavor) == 0) { 553 enabled = false; 554 break; 555 } 556 } 557 } 558 } 559 560 button->SetLabel(buttonText.String()); 561 button->SetEnabled(enabled); 562 } 563 564 565 void 566 TFilePanel::SelectionChanged() 567 { 568 AdjustButton(); 569 570 if (fClientObject) 571 fClientObject->SelectionChanged(); 572 } 573 574 575 status_t 576 TFilePanel::GetNextEntryRef(entry_ref *ref) 577 { 578 if (!ref) 579 return B_ERROR; 580 581 BPose *pose = fPoseView->SelectionList()->ItemAt(fSelectionIterator++); 582 if (!pose) 583 return B_ERROR; 584 585 *ref = *pose->TargetModel()->EntryRef(); 586 return B_OK; 587 } 588 589 590 BPoseView * 591 TFilePanel::NewPoseView(Model *model, BRect rect, uint32) 592 { 593 return new BFilePanelPoseView(model, rect); 594 } 595 596 597 void 598 TFilePanel::Init(const BMessage *) 599 { 600 BRect windRect(Bounds()); 601 AddChild(fBackView = new BackgroundView(windRect)); 602 603 // add poseview menu bar 604 fMenuBar = new BMenuBar(BRect(0, 0, windRect.Width(), 1), "MenuBar"); 605 fMenuBar->SetBorder(B_BORDER_FRAME); 606 fBackView->AddChild(fMenuBar); 607 608 AddMenus(); 609 AddContextMenus(); 610 611 FavoritesMenu* favorites = new FavoritesMenu(B_TRANSLATE("Favorites"), 612 new BMessage(kSwitchDirectory), new BMessage(B_REFS_RECEIVED), 613 BMessenger(this), IsSavePanel(), fPoseView->RefFilter()); 614 favorites->AddItem(new BMenuItem(B_TRANSLATE("Add current folder"), 615 new BMessage(kAddCurrentDir))); 616 favorites->AddItem(new BMenuItem( 617 B_TRANSLATE("Edit favorites"B_UTF8_ELLIPSIS), 618 new BMessage(kEditFavorites))); 619 620 fMenuBar->AddItem(favorites); 621 622 // configure menus 623 BMenuItem* item = fMenuBar->FindItem(B_TRANSLATE("Window")); 624 if (item) { 625 fMenuBar->RemoveItem(item); 626 delete item; 627 } 628 629 item = fMenuBar->FindItem(B_TRANSLATE("File")); 630 if (item) { 631 BMenu *menu = item->Submenu(); 632 if (menu) { 633 item = menu->FindItem(kOpenSelection); 634 if (item && menu->RemoveItem(item)) 635 delete item; 636 637 item = menu->FindItem(kDuplicateSelection); 638 if (item && menu->RemoveItem(item)) 639 delete item; 640 641 // remove add-ons menu, identifier menu, separator 642 item = menu->FindItem(B_TRANSLATE("Add-ons")); 643 if (item) { 644 int32 index = menu->IndexOf(item); 645 delete menu->RemoveItem(index); 646 delete menu->RemoveItem(--index); 647 delete menu->RemoveItem(--index); 648 } 649 650 // remove separator 651 item = menu->FindItem(B_CUT); 652 if (item) { 653 item = menu->ItemAt(menu->IndexOf(item)-1); 654 if (item && menu->RemoveItem(item)) 655 delete item; 656 } 657 } 658 } 659 660 // add directory menu and menufield 661 fDirMenu = new BDirMenu(0, this, kSwitchDirectory, "refs"); 662 663 font_height ht; 664 be_plain_font->GetHeight(&ht); 665 float f_height = ht.ascent + ht.descent + ht.leading; 666 667 BRect rect; 668 rect.top = fMenuBar->Bounds().Height() + 8; 669 rect.left = windRect.left + 8; 670 rect.right = rect.left + 300; 671 rect.bottom = rect.top + (f_height > 22 ? f_height : 22); 672 673 fDirMenuField = new BMenuField(rect, "DirMenuField", "", fDirMenu); 674 fDirMenuField->MenuBar()->SetFont(be_plain_font); 675 fDirMenuField->SetDivider(0); 676 677 fDirMenuField->MenuBar()->RemoveItem((int32)0); 678 fDirMenu->SetMenuBar(fDirMenuField->MenuBar()); 679 // the above is a weird call from BDirMenu 680 // ToDo: clean up 681 682 BEntry entry(TargetModel()->EntryRef()); 683 if (entry.InitCheck() == B_OK) 684 fDirMenu->Populate(&entry, 0, true, true, false, true); 685 else 686 fDirMenu->Populate(0, 0, true, true, false, true); 687 688 fBackView->AddChild(fDirMenuField); 689 690 // add file name text view 691 if (fIsSavePanel) { 692 BRect rect(windRect); 693 rect.top = rect.bottom - 35; 694 rect.left = 8; 695 rect.right = rect.left + 170; 696 rect.bottom = rect.top + 13; 697 698 fTextControl = new BTextControl(rect, "text view", 699 B_TRANSLATE("save text"), "", NULL, 700 B_FOLLOW_LEFT | B_FOLLOW_BOTTOM); 701 DisallowMetaKeys(fTextControl->TextView()); 702 DisallowFilenameKeys(fTextControl->TextView()); 703 fBackView->AddChild(fTextControl); 704 fTextControl->SetDivider(0.0f); 705 fTextControl->TextView()->SetMaxBytes(B_FILE_NAME_LENGTH - 1); 706 707 fButtonText.SetTo(B_TRANSLATE("Save")); 708 } else 709 fButtonText.SetTo(B_TRANSLATE("Open")); 710 711 rect = windRect; 712 rect.OffsetTo(10, fDirMenuField->Frame().bottom + 10); 713 rect.bottom = windRect.bottom - 60; 714 rect.right -= B_V_SCROLL_BAR_WIDTH + 20; 715 716 // re-parent the poseview to our backview 717 // ToDo: 718 // This is terrible, fix it up 719 PoseView()->RemoveSelf(); 720 if (fIsSavePanel) 721 fBackView->AddChild(PoseView(), fTextControl); 722 else 723 fBackView->AddChild(PoseView()); 724 725 PoseView()->MoveTo(rect.LeftTop()); 726 PoseView()->ResizeTo(rect.Width(), rect.Height()); 727 PoseView()->AddScrollBars(); 728 PoseView()->SetDragEnabled(false); 729 PoseView()->SetDropEnabled(false); 730 PoseView()->SetSelectionHandler(this); 731 PoseView()->SetSelectionChangedHook(true); 732 PoseView()->DisableSaveLocation(); 733 PoseView()->VScrollBar()->MoveBy(0, -1); 734 PoseView()->VScrollBar()->ResizeBy(0, 1); 735 736 737 AddShortcut('W', B_COMMAND_KEY, new BMessage(kCancelButton)); 738 AddShortcut('H', B_COMMAND_KEY, new BMessage(kSwitchToHome)); 739 AddShortcut('A', B_COMMAND_KEY | B_SHIFT_KEY, new BMessage(kShowSelectionWindow)); 740 AddShortcut('A', B_COMMAND_KEY, new BMessage(B_SELECT_ALL), PoseView()); 741 AddShortcut('S', B_COMMAND_KEY, new BMessage(kInvertSelection), PoseView()); 742 AddShortcut(B_DOWN_ARROW, B_COMMAND_KEY, new BMessage(kOpenDir)); 743 AddShortcut(B_DOWN_ARROW, B_COMMAND_KEY | B_OPTION_KEY, new BMessage(kOpenDir)); 744 AddShortcut(B_UP_ARROW, B_COMMAND_KEY, new BMessage(kOpenParentDir)); 745 AddShortcut(B_UP_ARROW, B_COMMAND_KEY | B_OPTION_KEY, new BMessage(kOpenParentDir)); 746 747 // New code to make buttons font sensitive 748 rect = windRect; 749 rect.top = rect.bottom - 35; 750 rect.bottom -= 10; 751 rect.right -= 25; 752 float default_width = be_plain_font->StringWidth(fButtonText.String()) + 20; 753 rect.left = (default_width > 75) ? (rect.right - default_width) : (rect.right - 75); 754 755 BButton *default_button = new BButton(rect, "default button", fButtonText.String(), 756 new BMessage(kDefaultButton), B_FOLLOW_RIGHT + B_FOLLOW_BOTTOM); 757 fBackView->AddChild(default_button); 758 759 rect.right = rect.left -= 10; 760 float cancel_width = be_plain_font->StringWidth(B_TRANSLATE("Cancel")) + 20; 761 rect.left = (cancel_width > 75) ? (rect.right - cancel_width) : (rect.right - 75); 762 763 BButton* cancel_button = new BButton(rect, "cancel button", 764 B_TRANSLATE("Cancel"), new BMessage(kCancelButton), 765 B_FOLLOW_RIGHT + B_FOLLOW_BOTTOM); 766 fBackView->AddChild(cancel_button); 767 768 if (!fIsSavePanel) 769 default_button->SetEnabled(false); 770 771 default_button->MakeDefault(true); 772 773 RestoreState(); 774 775 PoseView()->ScrollTo(B_ORIGIN); 776 PoseView()->UpdateScrollRange(); 777 PoseView()->ScrollTo(B_ORIGIN); 778 779 if (fTextControl) { 780 fTextControl->MakeFocus(); 781 fTextControl->TextView()->SelectAll(); 782 } else 783 PoseView()->MakeFocus(); 784 785 app_info info; 786 BString title; 787 if (be_app->GetAppInfo(&info) == B_OK) { 788 if (!gLocalizedNamePreferred 789 || BLocaleRoster::Default()->GetLocalizedFileName( 790 title, info.ref, false) != B_OK) 791 title = info.ref.name; 792 title << ": "; 793 } 794 title << fButtonText; // Open or Save 795 796 SetTitle(title.String()); 797 798 SetSizeLimits(370, 10000, 200, 10000); 799 } 800 801 802 void 803 TFilePanel::RestoreState() 804 { 805 BNode defaultingNode; 806 if (DefaultStateSourceNode(kDefaultFilePanelTemplate, &defaultingNode, false)) { 807 AttributeStreamFileNode streamNodeSource(&defaultingNode); 808 RestoreWindowState(&streamNodeSource); 809 PoseView()->Init(&streamNodeSource); 810 } else { 811 RestoreWindowState(NULL); 812 PoseView()->Init(NULL); 813 } 814 } 815 816 817 void 818 TFilePanel::SaveState(bool) 819 { 820 BNode defaultingNode; 821 if (DefaultStateSourceNode(kDefaultFilePanelTemplate, &defaultingNode, 822 true, false)) { 823 AttributeStreamFileNode streamNodeDestination(&defaultingNode); 824 SaveWindowState(&streamNodeDestination); 825 PoseView()->SaveState(&streamNodeDestination); 826 } 827 } 828 829 830 void 831 TFilePanel::SaveState(BMessage &message) const 832 { 833 _inherited::SaveState(message); 834 } 835 836 837 void 838 TFilePanel::RestoreWindowState(AttributeStreamNode *node) 839 { 840 SetSizeLimits(360, 10000, 200, 10000); 841 if (!node) 842 return; 843 844 const char *rectAttributeName = kAttrWindowFrame; 845 BRect frame(Frame()); 846 if (node->Read(rectAttributeName, 0, B_RECT_TYPE, sizeof(BRect), &frame) 847 == sizeof(BRect)) { 848 MoveTo(frame.LeftTop()); 849 ResizeTo(frame.Width(), frame.Height()); 850 } 851 } 852 853 854 void 855 TFilePanel::RestoreState(const BMessage &message) 856 { 857 _inherited::RestoreState(message); 858 } 859 860 861 void 862 TFilePanel::RestoreWindowState(const BMessage &message) 863 { 864 _inherited::RestoreWindowState(message); 865 } 866 867 868 void 869 TFilePanel::AddFileContextMenus(BMenu *menu) 870 { 871 menu->AddItem(new BMenuItem(B_TRANSLATE("Get info"), 872 new BMessage(kGetInfo), 'I')); 873 menu->AddItem(new BMenuItem(B_TRANSLATE("Edit name"), 874 new BMessage(kEditItem), 'E')); 875 menu->AddItem(new BMenuItem(TrackerSettings().DontMoveFilesToTrash() 876 ? B_TRANSLATE("Delete") 877 : B_TRANSLATE("Move to Trash"), 878 new BMessage(kMoveToTrash), 'T')); 879 menu->AddSeparatorItem(); 880 menu->AddItem(new BMenuItem(B_TRANSLATE("Cut"), 881 new BMessage(B_CUT), 'X')); 882 menu->AddItem(new BMenuItem(B_TRANSLATE("Copy"), 883 new BMessage(B_COPY), 'C')); 884 // menu->AddItem(pasteItem = new BMenuItem("Paste", new BMessage(B_PASTE), 'V')); 885 886 menu->SetTargetForItems(PoseView()); 887 } 888 889 890 void 891 TFilePanel::AddVolumeContextMenus(BMenu *menu) 892 { 893 menu->AddItem(new BMenuItem(B_TRANSLATE("Open"), 894 new BMessage(kOpenSelection), 'O')); 895 menu->AddItem(new BMenuItem(B_TRANSLATE("Get info"), 896 new BMessage(kGetInfo), 'I')); 897 menu->AddItem(new BMenuItem(B_TRANSLATE("Edit name"), 898 new BMessage(kEditItem), 'E')); 899 menu->AddSeparatorItem(); 900 menu->AddItem(new BMenuItem(B_TRANSLATE("Cut"), new BMessage(B_CUT), 'X')); 901 menu->AddItem(new BMenuItem(B_TRANSLATE("Copy"), 902 new BMessage(B_COPY), 'C')); 903 // menu->AddItem(pasteItem = new BMenuItem("Paste", new BMessage(B_PASTE), 'V')); 904 905 menu->SetTargetForItems(PoseView()); 906 } 907 908 909 void 910 TFilePanel::AddWindowContextMenus(BMenu *menu) 911 { 912 BMenuItem* item = new BMenuItem(B_TRANSLATE("New folder"), 913 new BMessage(kNewFolder), 'N'); 914 item->SetTarget(PoseView()); 915 menu->AddItem(item); 916 menu->AddSeparatorItem(); 917 918 item = new BMenuItem(B_TRANSLATE("Paste"), new BMessage(B_PASTE), 'V'); 919 item->SetTarget(PoseView()); 920 menu->AddItem(item); 921 menu->AddSeparatorItem(); 922 923 item = new BMenuItem(B_TRANSLATE("Select"B_UTF8_ELLIPSIS), 924 new BMessage(kShowSelectionWindow), 'A', B_SHIFT_KEY); 925 item->SetTarget(PoseView()); 926 menu->AddItem(item); 927 928 item = new BMenuItem(B_TRANSLATE("Select all"), 929 new BMessage(B_SELECT_ALL), 'A'); 930 item->SetTarget(PoseView()); 931 menu->AddItem(item); 932 933 item = new BMenuItem(B_TRANSLATE("Invert selection"), 934 new BMessage(kInvertSelection), 'S'); 935 item->SetTarget(PoseView()); 936 menu->AddItem(item); 937 938 item = new BMenuItem(B_TRANSLATE("Go to parent"), 939 new BMessage(kOpenParentDir), B_UP_ARROW); 940 item->SetTarget(this); 941 menu->AddItem(item); 942 } 943 944 945 void 946 TFilePanel::AddDropContextMenus(BMenu *) 947 { 948 } 949 950 951 void 952 TFilePanel::MenusBeginning() 953 { 954 int32 count = PoseView()->SelectionList()->CountItems(); 955 956 EnableNamedMenuItem(fMenuBar, kNewFolder, !TargetModel()->IsRoot()); 957 EnableNamedMenuItem(fMenuBar, kMoveToTrash, !TargetModel()->IsRoot() && count); 958 EnableNamedMenuItem(fMenuBar, kGetInfo, count != 0); 959 EnableNamedMenuItem(fMenuBar, kEditItem, count == 1); 960 961 SetCutItem(fMenuBar); 962 SetCopyItem(fMenuBar); 963 SetPasteItem(fMenuBar); 964 965 fIsTrackingMenu = true; 966 } 967 968 969 void 970 TFilePanel::MenusEnded() 971 { 972 fIsTrackingMenu = false; 973 } 974 975 976 void 977 TFilePanel::ShowContextMenu(BPoint point, const entry_ref *ref, BView *view) 978 { 979 EnableNamedMenuItem(fWindowContextMenu, kNewFolder, !TargetModel()->IsRoot()); 980 EnableNamedMenuItem(fWindowContextMenu, kOpenParentDir, !TargetModel()->IsRoot()); 981 EnableNamedMenuItem(fWindowContextMenu, kMoveToTrash, !TargetModel()->IsRoot()); 982 983 _inherited::ShowContextMenu(point, ref, view); 984 } 985 986 987 void 988 TFilePanel::SetupNavigationMenu(const entry_ref *, BMenu *) 989 { 990 // do nothing here so nav menu doesn't get added 991 } 992 993 994 void 995 TFilePanel::SetButtonLabel(file_panel_button selector, const char *text) 996 { 997 switch (selector) { 998 case B_CANCEL_BUTTON: 999 { 1000 BButton *button = dynamic_cast<BButton *>(FindView("cancel button")); 1001 if (!button) 1002 break; 1003 1004 float old_width = button->StringWidth(button->Label()); 1005 button->SetLabel(text); 1006 float delta = old_width - button->StringWidth(text); 1007 if (delta) { 1008 button->MoveBy(delta, 0); 1009 button->ResizeBy(-delta, 0); 1010 } 1011 } 1012 break; 1013 1014 case B_DEFAULT_BUTTON: 1015 { 1016 fButtonText = text; 1017 float delta = 0; 1018 BButton *button = dynamic_cast<BButton *>(FindView("default button")); 1019 if (button) { 1020 float old_width = button->StringWidth(button->Label()); 1021 button->SetLabel(text); 1022 delta = old_width - button->StringWidth(text); 1023 if (delta) { 1024 button->MoveBy(delta, 0); 1025 button->ResizeBy(-delta, 0); 1026 } 1027 } 1028 1029 // now must move cancel button 1030 button = dynamic_cast<BButton *>(FindView("cancel button")); 1031 if (button) 1032 button->MoveBy(delta, 0); 1033 } 1034 break; 1035 } 1036 } 1037 1038 1039 void 1040 TFilePanel::SetSaveText(const char *text) 1041 { 1042 if (!text) 1043 return; 1044 1045 BTextControl *textControl = dynamic_cast<BTextControl *>(FindView("text view")); 1046 textControl->SetText(text); 1047 textControl->TextView()->SelectAll(); 1048 } 1049 1050 1051 void 1052 TFilePanel::MessageReceived(BMessage *message) 1053 { 1054 entry_ref ref; 1055 1056 switch (message->what) { 1057 case B_REFS_RECEIVED: 1058 // item was double clicked in file panel (PoseView) 1059 if (message->FindRef("refs", &ref) == B_OK) { 1060 BEntry entry(&ref, true); 1061 if (entry.InitCheck() == B_OK) { 1062 // Double-click on dir or link-to-dir ALWAYS opens the dir. 1063 // If more than one dir is selected, the 1064 // first is entered. 1065 if (entry.IsDirectory()) { 1066 entry.GetRef(&ref); 1067 bool isDesktop = SwitchDirToDesktopIfNeeded(ref); 1068 1069 PoseView()->SetIsDesktop(isDesktop); 1070 entry.SetTo(&ref); 1071 PoseView()->SwitchDir(&ref); 1072 SwitchDirMenuTo(&ref); 1073 } else { 1074 // Otherwise, we have a file or a link to a file. 1075 // AdjustButton has already tested the flavor; 1076 // all we have to do is see if the button is enabled. 1077 BButton *button = dynamic_cast<BButton *>(FindView("default button")); 1078 if (!button) 1079 break; 1080 1081 if (IsSavePanel()) { 1082 int32 count = 0; 1083 type_code type; 1084 message->GetInfo("refs", &type, &count); 1085 1086 // Don't allow saves of multiple files 1087 if (count > 1) { 1088 ShowCenteredAlert( 1089 B_TRANSLATE("Sorry, saving more than one item is not allowed."), 1090 B_TRANSLATE("Cancel")); 1091 } else { 1092 // if we are a savepanel, set up the filepanel correctly 1093 // then pass control so we follow the same path as if the user 1094 // clicked the save button 1095 1096 // set the 'name' fld to the current ref's name 1097 // notify the panel that the default button should be enabled 1098 SetSaveText(ref.name); 1099 SelectionChanged(); 1100 1101 HandleSaveButton(); 1102 } 1103 break; 1104 } 1105 1106 // send handler a message and close 1107 BMessage openMessage(*fMessage); 1108 for (int32 index = 0; ; index++) { 1109 if (message->FindRef("refs", index, &ref) != B_OK) 1110 break; 1111 openMessage.AddRef("refs", &ref); 1112 } 1113 OpenSelectionCommon(&openMessage); 1114 } 1115 } 1116 } 1117 break; 1118 1119 case kSwitchDirectory: 1120 { 1121 entry_ref ref; 1122 // this comes from dir menu or nav menu, so switch directories 1123 if (message->FindRef("refs", &ref) == B_OK) { 1124 BEntry entry(&ref, true); 1125 if (entry.GetRef(&ref) == B_OK) 1126 SetTo(&ref); 1127 } 1128 break; 1129 } 1130 1131 case kSwitchToHome: 1132 { 1133 BPath homePath; 1134 entry_ref ref; 1135 if (find_directory(B_USER_DIRECTORY, &homePath) != B_OK 1136 || get_ref_for_path(homePath.Path(), &ref) != B_OK) 1137 break; 1138 1139 SetTo(&ref); 1140 break; 1141 } 1142 1143 case kAddCurrentDir: 1144 { 1145 BPath path; 1146 if (find_directory (B_USER_SETTINGS_DIRECTORY, &path, true) != B_OK) 1147 break; 1148 1149 path.Append(kGoDirectory); 1150 BDirectory goDirectory(path.Path()); 1151 1152 if (goDirectory.InitCheck() == B_OK) { 1153 BEntry entry(TargetModel()->EntryRef()); 1154 entry.GetPath(&path); 1155 1156 BSymLink link; 1157 goDirectory.CreateSymLink(TargetModel()->Name(), path.Path(), &link); 1158 } 1159 break; 1160 } 1161 1162 case kEditFavorites: 1163 { 1164 BPath path; 1165 if (find_directory (B_USER_SETTINGS_DIRECTORY, &path, true) != B_OK) 1166 break; 1167 1168 path.Append(kGoDirectory); 1169 BMessenger msgr(kTrackerSignature); 1170 if (msgr.IsValid()) { 1171 BMessage message(B_REFS_RECEIVED); 1172 entry_ref ref; 1173 if (get_ref_for_path(path.Path(), &ref) == B_OK) { 1174 message.AddRef("refs", &ref); 1175 msgr.SendMessage(&message); 1176 } 1177 } 1178 break; 1179 } 1180 1181 case kCancelButton: 1182 PostMessage(B_QUIT_REQUESTED); 1183 break; 1184 1185 case kOpenDir: 1186 OpenDirectory(); 1187 break; 1188 1189 case kOpenParentDir: 1190 OpenParent(); 1191 break; 1192 1193 case kDefaultButton: 1194 if (fIsSavePanel) { 1195 if (PoseView()->IsFocus() 1196 && PoseView()->SelectionList()->CountItems() == 1) { 1197 Model *model = (PoseView()->SelectionList()->FirstItem())->TargetModel(); 1198 if (model->ResolveIfLink()->IsDirectory()) { 1199 PoseView()->CommitActivePose(); 1200 PoseView()->OpenSelection(); 1201 break; 1202 } 1203 } 1204 1205 HandleSaveButton(); 1206 } else 1207 HandleOpenButton(); 1208 break; 1209 1210 case B_OBSERVER_NOTICE_CHANGE: 1211 { 1212 int32 observerWhat; 1213 if (message->FindInt32("be:observe_change_what", &observerWhat) == B_OK) { 1214 switch (observerWhat) { 1215 case kDesktopFilePanelRootChanged: 1216 { 1217 bool desktopIsRoot = true; 1218 if (message->FindBool("DesktopFilePanelRoot", &desktopIsRoot) == B_OK) 1219 TrackerSettings().SetDesktopFilePanelRoot(desktopIsRoot); 1220 SetTo(TargetModel()->EntryRef()); 1221 break; 1222 } 1223 } 1224 } 1225 break; 1226 } 1227 1228 default: 1229 _inherited::MessageReceived(message); 1230 } 1231 } 1232 1233 1234 void 1235 TFilePanel::OpenDirectory() 1236 { 1237 BObjectList<BPose> *list = PoseView()->SelectionList(); 1238 if (list->CountItems() != 1) 1239 return; 1240 1241 Model *model = list->FirstItem()->TargetModel(); 1242 if (model->ResolveIfLink()->IsDirectory()) { 1243 BMessage message(B_REFS_RECEIVED); 1244 message.AddRef("refs", model->EntryRef()); 1245 PostMessage(&message); 1246 } 1247 } 1248 1249 1250 void 1251 TFilePanel::OpenParent() 1252 { 1253 if (!CanOpenParent()) 1254 return; 1255 1256 BEntry parentEntry; 1257 BDirectory dir; 1258 1259 Model oldModel(*PoseView()->TargetModel()); 1260 BEntry entry(oldModel.EntryRef()); 1261 1262 if (entry.InitCheck() == B_OK 1263 && entry.GetParent(&dir) == B_OK 1264 && dir.GetEntry(&parentEntry) == B_OK 1265 && entry != parentEntry) { 1266 1267 entry_ref ref; 1268 parentEntry.GetRef(&ref); 1269 1270 PoseView()->SetIsDesktop(SwitchDirToDesktopIfNeeded(ref)); 1271 PoseView()->SwitchDir(&ref); 1272 SwitchDirMenuTo(&ref); 1273 1274 // make sure the child get's selected in the new view once it 1275 // shows up 1276 fTaskLoop->RunLater(NewMemberFunctionObjectWithResult 1277 (&TFilePanel::SelectChildInParent, this, 1278 const_cast<const entry_ref *>(&ref), 1279 oldModel.NodeRef()), 100000, 200000, 5000000); 1280 } 1281 } 1282 1283 1284 bool 1285 TFilePanel::CanOpenParent() const 1286 { 1287 if (TrackerSettings().DesktopFilePanelRoot()) { 1288 // don't allow opening Desktop folder's parent 1289 if (TargetModel()->IsDesktop()) 1290 return false; 1291 } 1292 1293 // block on "/" 1294 BEntry root("/"); 1295 node_ref rootRef; 1296 root.GetNodeRef(&rootRef); 1297 1298 return rootRef != *TargetModel()->NodeRef(); 1299 } 1300 1301 1302 bool 1303 TFilePanel::SwitchDirToDesktopIfNeeded(entry_ref &ref) 1304 { 1305 // support showing Desktop as root of everything 1306 // This call implements the worm hole that maps Desktop as 1307 // a root above the disks 1308 TrackerSettings settings; 1309 if (!settings.DesktopFilePanelRoot()) 1310 // Tracker isn't set up that way, just let Disks show 1311 return false; 1312 1313 BEntry entry(&ref); 1314 BEntry root("/"); 1315 1316 BDirectory desktopDir; 1317 FSGetDeskDir(&desktopDir); 1318 if (FSIsDeskDir(&entry) 1319 // navigated into non-boot desktop, switch to boot desktop 1320 || (entry == root && !settings.ShowDisksIcon())) { 1321 // hit "/" level, map to desktop 1322 1323 desktopDir.GetEntry(&entry); 1324 entry.GetRef(&ref); 1325 return true; 1326 } 1327 return FSIsDeskDir(&entry); 1328 } 1329 1330 1331 bool 1332 TFilePanel::SelectChildInParent(const entry_ref *, const node_ref *child) 1333 { 1334 AutoLock<TFilePanel> lock(this); 1335 1336 if (!IsLocked()) 1337 return false; 1338 1339 int32 index; 1340 BPose *pose = PoseView()->FindPose(child, &index); 1341 if (!pose) 1342 return false; 1343 1344 PoseView()->UpdateScrollRange(); 1345 // ToDo: Scroll range should be updated by now, for some 1346 // reason sometimes it is not right, force it here 1347 PoseView()->SelectPose(pose, index, true); 1348 return true; 1349 } 1350 1351 1352 int32 1353 TFilePanel::ShowCenteredAlert(const char *text, const char *button1, 1354 const char *button2, const char *button3) 1355 { 1356 BAlert *alert = new BAlert("", text, button1, button2, button3, 1357 B_WIDTH_AS_USUAL, B_WARNING_ALERT); 1358 alert->MoveTo(Frame().left + 10, Frame().top + 10); 1359 1360 #if 0 1361 if (button1 != NULL && !strncmp(button1, "Cancel", 7)) 1362 alert->SetShortcut(0, B_ESCAPE); 1363 else if (button2 != NULL && !strncmp(button2, "Cancel", 7)) 1364 alert->SetShortcut(1, B_ESCAPE); 1365 else if (button3 != NULL && !strncmp(button3, "Cancel", 7)) 1366 alert->SetShortcut(2, B_ESCAPE); 1367 #endif 1368 1369 return alert->Go(); 1370 } 1371 1372 1373 void 1374 TFilePanel::HandleSaveButton() 1375 { 1376 BDirectory dir; 1377 1378 if (TargetModel()->IsRoot()) { 1379 ShowCenteredAlert( 1380 B_TRANSLATE("Sorry, you can't save things at the root of " 1381 "your system."), 1382 B_TRANSLATE("Cancel")); 1383 return; 1384 } 1385 1386 // check for some illegal file names 1387 if (strcmp(fTextControl->Text(), ".") == 0 1388 || strcmp(fTextControl->Text(), "..") == 0) { 1389 ShowCenteredAlert( 1390 B_TRANSLATE("The specified name is illegal. Please choose " 1391 "another name."), 1392 B_TRANSLATE("Cancel")); 1393 fTextControl->TextView()->SelectAll(); 1394 return; 1395 } 1396 1397 if (dir.SetTo(TargetModel()->EntryRef()) != B_OK) { 1398 ShowCenteredAlert( 1399 B_TRANSLATE("There was a problem trying to save in the folder " 1400 "you specified. Please try another one."), 1401 B_TRANSLATE("Cancel")); 1402 return; 1403 } 1404 1405 if (dir.Contains(fTextControl->Text())) { 1406 if (dir.Contains(fTextControl->Text(), B_DIRECTORY_NODE)) { 1407 ShowCenteredAlert( 1408 B_TRANSLATE("The specified name is already used as the name " 1409 "of a folder. Please choose another name."), 1410 B_TRANSLATE("Cancel")); 1411 fTextControl->TextView()->SelectAll(); 1412 return; 1413 } else { 1414 // if this was invoked by a dbl click, it is an explicit replacement 1415 // of the file. 1416 BString str(B_TRANSLATE("The file \"%name\" already exists in the specified folder. Do you want to replace it?")); 1417 str.ReplaceFirst("%name", fTextControl->Text()); 1418 1419 if (ShowCenteredAlert(str.String(), B_TRANSLATE("Cancel"), 1420 B_TRANSLATE("Replace")) == 0) { 1421 // user canceled 1422 fTextControl->TextView()->SelectAll(); 1423 return; 1424 } 1425 // user selected "Replace" - let app deal with it 1426 } 1427 } 1428 1429 BMessage message(*fMessage); 1430 message.AddRef("directory", TargetModel()->EntryRef()); 1431 message.AddString("name", fTextControl->Text()); 1432 1433 if (fClientObject) 1434 fClientObject->SendMessage(&fTarget, &message); 1435 else 1436 fTarget.SendMessage(&message); 1437 1438 // close window if we're dealing with standard message 1439 if (fHideWhenDone) 1440 PostMessage(B_QUIT_REQUESTED); 1441 } 1442 1443 1444 void 1445 TFilePanel::OpenSelectionCommon(BMessage *openMessage) 1446 { 1447 if (!openMessage->HasRef("refs")) 1448 return; 1449 1450 for (int32 index = 0; ; index++) { 1451 entry_ref ref; 1452 if (openMessage->FindRef("refs", index, &ref) != B_OK) 1453 break; 1454 1455 BEntry entry(&ref, true); 1456 if (entry.InitCheck() == B_OK) { 1457 if (entry.IsDirectory()) 1458 BRoster().AddToRecentFolders(&ref); 1459 else 1460 BRoster().AddToRecentDocuments(&ref); 1461 } 1462 } 1463 1464 BRoster().AddToRecentFolders(TargetModel()->EntryRef()); 1465 1466 if (fClientObject) 1467 fClientObject->SendMessage(&fTarget, openMessage); 1468 else 1469 fTarget.SendMessage(openMessage); 1470 1471 // close window if we're dealing with standard message 1472 if (fHideWhenDone) 1473 PostMessage(B_QUIT_REQUESTED); 1474 } 1475 1476 1477 void 1478 TFilePanel::HandleOpenButton() 1479 { 1480 PoseView()->CommitActivePose(); 1481 BObjectList<BPose> *selection = PoseView()->SelectionList(); 1482 1483 // if we have only one directory and we're not opening dirs, enter. 1484 if ((fNodeFlavors & B_DIRECTORY_NODE) == 0 1485 && selection->CountItems() == 1) { 1486 Model *model = selection->FirstItem()->TargetModel(); 1487 1488 if (model->IsDirectory() 1489 || (model->IsSymLink() && !(fNodeFlavors & B_SYMLINK_NODE) 1490 && model->ResolveIfLink()->IsDirectory())) { 1491 1492 BMessage message(B_REFS_RECEIVED); 1493 message.AddRef("refs", model->EntryRef()); 1494 PostMessage(&message); 1495 return; 1496 } 1497 } 1498 1499 // don't do anything unless there are items selected 1500 // message->fMessage->message from here to end 1501 if (selection->CountItems()) { 1502 BMessage message(*fMessage); 1503 // go through selection and add appropriate items 1504 for (int32 index = 0; index < selection->CountItems(); index++) { 1505 Model *model = selection->ItemAt(index)->TargetModel(); 1506 1507 if (((fNodeFlavors & B_DIRECTORY_NODE) != 0 1508 && model->ResolveIfLink()->IsDirectory()) 1509 || ((fNodeFlavors & B_SYMLINK_NODE) != 0 && model->IsSymLink()) 1510 || ((fNodeFlavors & B_FILE_NODE) != 0 && model->ResolveIfLink()->IsFile())) 1511 message.AddRef("refs", model->EntryRef()); 1512 } 1513 1514 OpenSelectionCommon(&message); 1515 } 1516 } 1517 1518 1519 void 1520 TFilePanel::SwitchDirMenuTo(const entry_ref *ref) 1521 { 1522 BEntry entry(ref); 1523 for (int32 index = fDirMenu->CountItems() - 1; index >= 0; index--) 1524 delete fDirMenu->RemoveItem(index); 1525 1526 fDirMenuField->MenuBar()->RemoveItem((int32)0); 1527 fDirMenu->Populate(&entry, 0, true, true, false, true); 1528 1529 ModelMenuItem *item = dynamic_cast<ModelMenuItem *>( 1530 fDirMenuField->MenuBar()->ItemAt(0)); 1531 ASSERT(item); 1532 item->SetEntry(&entry); 1533 } 1534 1535 1536 void 1537 TFilePanel::WindowActivated(bool active) 1538 { 1539 // force focus to update properly 1540 fBackView->Invalidate(); 1541 _inherited::WindowActivated(active); 1542 } 1543 1544 1545 // #pragma mark - 1546 1547 1548 BFilePanelPoseView::BFilePanelPoseView(Model *model, BRect frame, uint32 resizeMask) 1549 : BPoseView(model, frame, kListMode, resizeMask), 1550 fIsDesktop(model->IsDesktop()) 1551 { 1552 } 1553 1554 1555 void 1556 BFilePanelPoseView::StartWatching() 1557 { 1558 TTracker::WatchNode(0, B_WATCH_MOUNT, this); 1559 1560 // inter-application observing 1561 BMessenger tracker(kTrackerSignature); 1562 BHandler::StartWatching(tracker, kVolumesOnDesktopChanged); 1563 } 1564 1565 1566 void 1567 BFilePanelPoseView::StopWatching() 1568 { 1569 stop_watching(this); 1570 1571 // inter-application observing 1572 BMessenger tracker(kTrackerSignature); 1573 BHandler::StopWatching(tracker, kVolumesOnDesktopChanged); 1574 } 1575 1576 1577 bool 1578 BFilePanelPoseView::FSNotification(const BMessage *message) 1579 { 1580 if (IsDesktopView()) { 1581 // Pretty much copied straight from DesktopPoseView. Would be better 1582 // if the code could be shared somehow. 1583 switch (message->FindInt32("opcode")) { 1584 case B_DEVICE_MOUNTED: 1585 { 1586 dev_t device; 1587 if (message->FindInt32("new device", &device) != B_OK) 1588 break; 1589 1590 ASSERT(TargetModel()); 1591 TrackerSettings settings; 1592 1593 BVolume volume(device); 1594 if (volume.InitCheck() != B_OK) 1595 break; 1596 1597 if (settings.MountVolumesOntoDesktop() 1598 && (!volume.IsShared() || settings.MountSharedVolumesOntoDesktop())) { 1599 // place an icon for the volume onto the desktop 1600 CreateVolumePose(&volume, true); 1601 } 1602 } 1603 break; 1604 } 1605 } 1606 return _inherited::FSNotification(message); 1607 } 1608 1609 1610 void 1611 BFilePanelPoseView::RestoreState(AttributeStreamNode *node) 1612 { 1613 _inherited::RestoreState(node); 1614 fViewState->SetViewMode(kListMode); 1615 } 1616 1617 1618 void 1619 BFilePanelPoseView::RestoreState(const BMessage &message) 1620 { 1621 _inherited::RestoreState(message); 1622 } 1623 1624 1625 void 1626 BFilePanelPoseView::SavePoseLocations(BRect *) 1627 { 1628 } 1629 1630 1631 EntryListBase * 1632 BFilePanelPoseView::InitDirentIterator(const entry_ref *ref) 1633 { 1634 if (IsDesktopView()) 1635 return DesktopPoseView::InitDesktopDirentIterator(this, ref); 1636 1637 return _inherited::InitDirentIterator(ref); 1638 } 1639 1640 1641 void 1642 BFilePanelPoseView::AddPosesCompleted() 1643 { 1644 _inherited::AddPosesCompleted(); 1645 if (IsDesktopView()) 1646 CreateTrashPose(); 1647 } 1648 1649 1650 void 1651 BFilePanelPoseView::SetIsDesktop(bool on) 1652 { 1653 fIsDesktop = on; 1654 } 1655 1656 1657 bool 1658 BFilePanelPoseView::IsDesktopView() const 1659 { 1660 return fIsDesktop; 1661 } 1662 1663 1664 void 1665 BFilePanelPoseView::ShowVolumes(bool visible, bool showShared) 1666 { 1667 if (IsDesktopView()) { 1668 if (!visible) 1669 RemoveRootPoses(); 1670 else 1671 AddRootPoses(true, showShared); 1672 } 1673 1674 1675 TFilePanel *filepanel = dynamic_cast<TFilePanel*>(Window()); 1676 if (filepanel) 1677 filepanel->SetTo(TargetModel()->EntryRef()); 1678 } 1679 1680 1681 void 1682 BFilePanelPoseView::AdaptToVolumeChange(BMessage *message) 1683 { 1684 bool showDisksIcon; 1685 bool mountVolumesOnDesktop; 1686 bool mountSharedVolumesOntoDesktop; 1687 1688 message->FindBool("ShowDisksIcon", &showDisksIcon); 1689 message->FindBool("MountVolumesOntoDesktop", &mountVolumesOnDesktop); 1690 message->FindBool("MountSharedVolumesOntoDesktop", &mountSharedVolumesOntoDesktop); 1691 1692 BEntry entry("/"); 1693 Model model(&entry); 1694 if (model.InitCheck() == B_OK) { 1695 BMessage monitorMsg; 1696 monitorMsg.what = B_NODE_MONITOR; 1697 1698 if (showDisksIcon) 1699 monitorMsg.AddInt32("opcode", B_ENTRY_CREATED); 1700 else 1701 monitorMsg.AddInt32("opcode", B_ENTRY_REMOVED); 1702 1703 monitorMsg.AddInt32("device", model.NodeRef()->device); 1704 monitorMsg.AddInt64("node", model.NodeRef()->node); 1705 monitorMsg.AddInt64("directory", model.EntryRef()->directory); 1706 monitorMsg.AddString("name", model.EntryRef()->name); 1707 TrackerSettings().SetShowDisksIcon(showDisksIcon); 1708 if (Window()) 1709 Window()->PostMessage(&monitorMsg, this); 1710 } 1711 1712 ShowVolumes(mountVolumesOnDesktop, mountSharedVolumesOntoDesktop); 1713 } 1714 1715 1716 void 1717 BFilePanelPoseView::AdaptToDesktopIntegrationChange(BMessage *message) 1718 { 1719 bool mountVolumesOnDesktop = true; 1720 bool mountSharedVolumesOntoDesktop = true; 1721 1722 message->FindBool("MountVolumesOntoDesktop", &mountVolumesOnDesktop); 1723 message->FindBool("MountSharedVolumesOntoDesktop", &mountSharedVolumesOntoDesktop); 1724 1725 ShowVolumes(false, mountSharedVolumesOntoDesktop); 1726 ShowVolumes(mountVolumesOnDesktop, mountSharedVolumesOntoDesktop); 1727 } 1728 1729