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