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