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