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