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 "ContainerWindow.h" 37 38 #include <Alert.h> 39 #include <Application.h> 40 #include <AppFileInfo.h> 41 #include <Catalog.h> 42 #include <ControlLook.h> 43 #include <Debug.h> 44 #include <Directory.h> 45 #include <Entry.h> 46 #include <FindDirectory.h> 47 #include <Keymap.h> 48 #include <Locale.h> 49 #include <MenuItem.h> 50 #include <MenuBar.h> 51 #include <NodeMonitor.h> 52 #include <Path.h> 53 #include <PopUpMenu.h> 54 #include <Screen.h> 55 #include <UnicodeChar.h> 56 #include <Volume.h> 57 #include <VolumeRoster.h> 58 #include <Roster.h> 59 60 #include <fs_attr.h> 61 #include <image.h> 62 #include <string.h> 63 #include <stdlib.h> 64 65 #include <algorithm> 66 #include <memory> 67 68 #include "Attributes.h" 69 #include "AttributeStream.h" 70 #include "AutoLock.h" 71 #include "BackgroundImage.h" 72 #include "Commands.h" 73 #include "CountView.h" 74 #include "DeskWindow.h" 75 #include "FavoritesMenu.h" 76 #include "FindPanel.h" 77 #include "FSClipboard.h" 78 #include "FSUndoRedo.h" 79 #include "FSUtils.h" 80 #include "IconMenuItem.h" 81 #include "OpenWithWindow.h" 82 #include "MimeTypes.h" 83 #include "Model.h" 84 #include "MountMenu.h" 85 #include "Navigator.h" 86 #include "NavMenu.h" 87 #include "PoseView.h" 88 #include "QueryContainerWindow.h" 89 #include "SelectionWindow.h" 90 #include "TitleView.h" 91 #include "Tracker.h" 92 #include "TrackerSettings.h" 93 #include "Thread.h" 94 #include "TemplatesMenu.h" 95 96 97 #undef B_TRANSLATION_CONTEXT 98 #define B_TRANSLATION_CONTEXT "ContainerWindow" 99 100 101 const uint32 kRedo = 'REDO'; 102 // this is the same as B_REDO in Dano/Zeta/Haiku 103 104 105 #ifdef _IMPEXP_BE 106 _IMPEXP_BE 107 #endif 108 void do_minimize_team(BRect zoomRect, team_id team, bool zoom); 109 110 111 // Amount you have to move the mouse before a drag starts 112 const float kDragSlop = 3.0f; 113 114 115 namespace BPrivate { 116 117 class DraggableContainerIcon : public BView { 118 public: 119 DraggableContainerIcon(BRect rect, const char* name, 120 uint32 resizeMask); 121 122 virtual void AttachedToWindow(); 123 virtual void MouseDown(BPoint where); 124 virtual void MouseUp(BPoint where); 125 virtual void MouseMoved(BPoint point, uint32 /*transit*/, 126 const BMessage* message); 127 virtual void FrameMoved(BPoint newLocation); 128 virtual void Draw(BRect updateRect); 129 130 private: 131 uint32 fDragButton; 132 BPoint fClickPoint; 133 bool fDragStarted; 134 }; 135 136 } // namespace BPrivate 137 138 139 struct AddOneAddonParams { 140 BObjectList<BMenuItem>* primaryList; 141 BObjectList<BMenuItem>* secondaryList; 142 }; 143 144 struct StaggerOneParams { 145 bool rectFromParent; 146 }; 147 148 149 const int32 kContainerWidthMinLimit = 120; 150 const int32 kContainerWindowHeightLimit = 85; 151 152 const int32 kWindowStaggerBy = 17; 153 154 155 BRect BContainerWindow::sNewWindRect(85, 50, 548, 280); 156 157 LockingList<AddonShortcut>* BContainerWindow::fAddonsList 158 = new LockingList<struct AddonShortcut>(10, true); 159 160 161 namespace BPrivate { 162 163 filter_result 164 ActivateWindowFilter(BMessage*, BHandler** target, BMessageFilter*) 165 { 166 BView* view = dynamic_cast<BView*>(*target); 167 168 // activate the window if no PoseView or DraggableContainerIcon had been 169 // pressed (those will activate the window themselves, if necessary) 170 if (view != NULL 171 && !dynamic_cast<BPoseView*>(view) 172 && !dynamic_cast<DraggableContainerIcon*>(view) 173 && view->Window()) 174 view->Window()->Activate(true); 175 176 return B_DISPATCH_MESSAGE; 177 } 178 179 180 int 181 CompareLabels(const BMenuItem* item1, const BMenuItem* item2) 182 { 183 return strcasecmp(item1->Label(), item2->Label()); 184 } 185 186 } // namespace BPrivate 187 188 189 static bool 190 AddOneAddon(const Model* model, const char* name, uint32 shortcut, 191 uint32 modifiers, bool primary, void* context) 192 { 193 AddOneAddonParams* params = (AddOneAddonParams*)context; 194 195 BMessage* message = new BMessage(kLoadAddOn); 196 message->AddRef("refs", model->EntryRef()); 197 198 ModelMenuItem* item = new ModelMenuItem(model, name, message, 199 (char)shortcut, modifiers); 200 201 if (primary) 202 params->primaryList->AddItem(item); 203 else 204 params->secondaryList->AddItem(item); 205 206 return false; 207 } 208 209 210 static int32 211 AddOnThread(BMessage* refsMessage, entry_ref addonRef, entry_ref dirRef) 212 { 213 std::auto_ptr<BMessage> refsMessagePtr(refsMessage); 214 215 BEntry entry(&addonRef); 216 BPath path; 217 status_t result = entry.InitCheck(); 218 if (result == B_OK) 219 result = entry.GetPath(&path); 220 221 if (result == B_OK) { 222 image_id addonImage = load_add_on(path.Path()); 223 if (addonImage >= 0) { 224 void (*processRefs)(entry_ref, BMessage*, void*); 225 result = get_image_symbol(addonImage, "process_refs", 2, 226 (void**)&processRefs); 227 228 #ifndef __INTEL__ 229 if (result < 0) { 230 PRINT(("trying old legacy ppc signature\n")); 231 // try old-style addon signature 232 result = get_image_symbol(addonImage, 233 "process_refs__F9entry_refP8BMessagePv", 2, 234 (void**)&processRefs); 235 } 236 #endif 237 238 if (result >= 0) { 239 240 // call add-on code 241 (*processRefs)(dirRef, refsMessagePtr.get(), 0); 242 243 unload_add_on(addonImage); 244 return B_OK; 245 } else 246 PRINT(("couldn't find process_refs\n")); 247 248 unload_add_on(addonImage); 249 } else 250 result = addonImage; 251 } 252 253 BString buffer(B_TRANSLATE("Error %error loading Add-On %name.")); 254 buffer.ReplaceFirst("%error", strerror(result)); 255 buffer.ReplaceFirst("%name", addonRef.name); 256 257 BAlert* alert = new BAlert("", buffer.String(), B_TRANSLATE("Cancel"), 258 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 259 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 260 alert->Go(); 261 262 return result; 263 } 264 265 266 static bool 267 NodeHasSavedState(const BNode* node) 268 { 269 attr_info info; 270 return node->GetAttrInfo(kAttrWindowFrame, &info) == B_OK; 271 } 272 273 274 static bool 275 OffsetFrameOne(const char* DEBUG_ONLY(name), uint32, off_t, void* castToRect, 276 void* castToParams) 277 { 278 ASSERT(strcmp(name, kAttrWindowFrame) == 0); 279 StaggerOneParams* params = (StaggerOneParams*)castToParams; 280 281 if (!params->rectFromParent) 282 return false; 283 284 if (!castToRect) 285 return false; 286 287 ((BRect*)castToRect)->OffsetBy(kWindowStaggerBy, kWindowStaggerBy); 288 289 return true; 290 } 291 292 293 static void 294 AddMimeTypeString(BObjectList<BString> &list, Model* model) 295 { 296 BString* mimeType = new BString(model->MimeType()); 297 298 if (mimeType->Length()) { 299 // only add the type if it's not already there 300 for (int32 i = list.CountItems(); i-- > 0;) { 301 BString* string = list.ItemAt(i); 302 if (string != NULL && !string->ICompare(*mimeType)) { 303 delete mimeType; 304 return; 305 } 306 } 307 308 list.AddItem(mimeType); 309 } 310 } 311 312 313 // #pragma mark - DraggableContainerIcon 314 315 316 DraggableContainerIcon::DraggableContainerIcon(BRect rect, const char* name, 317 uint32 resizeMask) 318 : 319 BView(rect, name, resizeMask, B_WILL_DRAW | B_FRAME_EVENTS), 320 fDragButton(0), 321 fDragStarted(false) 322 { 323 } 324 325 326 void 327 DraggableContainerIcon::AttachedToWindow() 328 { 329 SetViewColor(Parent()->ViewColor()); 330 FrameMoved(BPoint(0, 0)); 331 // this decides whether to hide the icon or not 332 } 333 334 335 void 336 DraggableContainerIcon::MouseDown(BPoint point) 337 { 338 // we only like container windows 339 BContainerWindow* window = dynamic_cast<BContainerWindow*>(Window()); 340 if (window == NULL) 341 return; 342 343 // we don't like the Trash icon (because it cannot be moved) 344 if (window->IsTrash() || window->IsPrintersDir()) 345 return; 346 347 uint32 buttons; 348 window->CurrentMessage()->FindInt32("buttons", (int32*)&buttons); 349 350 if (IconCache::sIconCache->IconHitTest(point, window->TargetModel(), 351 kNormalIcon, B_MINI_ICON)) { 352 // The click hit the icon, initiate a drag 353 fDragButton = buttons 354 & (B_PRIMARY_MOUSE_BUTTON | B_SECONDARY_MOUSE_BUTTON); 355 fDragStarted = false; 356 fClickPoint = point; 357 } else 358 fDragButton = 0; 359 360 if (!fDragButton) 361 Window()->Activate(true); 362 } 363 364 365 void 366 DraggableContainerIcon::MouseUp(BPoint /*point*/) 367 { 368 if (!fDragStarted) 369 Window()->Activate(true); 370 371 fDragButton = 0; 372 fDragStarted = false; 373 } 374 375 376 void 377 DraggableContainerIcon::MouseMoved(BPoint point, uint32 /*transit*/, 378 const BMessage* /*message*/) 379 { 380 if (fDragButton == 0 || fDragStarted 381 || (abs((int32)(point.x - fClickPoint.x)) <= kDragSlop 382 && abs((int32)(point.y - fClickPoint.y)) <= kDragSlop)) 383 return; 384 385 BContainerWindow* window = static_cast<BContainerWindow*>(Window()); 386 // we can only get here in a BContainerWindow 387 Model* model = window->TargetModel(); 388 389 // Find the required height 390 BFont font; 391 GetFont(&font); 392 393 font_height fontHeight; 394 font.GetHeight(&fontHeight); 395 float height = ceilf(fontHeight.ascent + fontHeight.descent 396 + fontHeight.leading + 2 + Bounds().Height() + 8); 397 398 BRect rect(0, 0, std::max(Bounds().Width(), 399 font.StringWidth(model->Name()) + 4), height); 400 BBitmap* dragBitmap = new BBitmap(rect, B_RGBA32, true); 401 402 dragBitmap->Lock(); 403 BView* view = new BView(dragBitmap->Bounds(), "", B_FOLLOW_NONE, 0); 404 dragBitmap->AddChild(view); 405 view->SetOrigin(0, 0); 406 BRect clipRect(view->Bounds()); 407 BRegion newClip; 408 newClip.Set(clipRect); 409 view->ConstrainClippingRegion(&newClip); 410 411 // Transparent draw magic 412 view->SetHighColor(0, 0, 0, 0); 413 view->FillRect(view->Bounds()); 414 view->SetDrawingMode(B_OP_ALPHA); 415 view->SetHighColor(0, 0, 0, 128); 416 // set the level of transparency by value 417 view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_COMPOSITE); 418 419 // Draw the icon 420 float hIconOffset = (rect.Width() - Bounds().Width()) / 2; 421 IconCache::sIconCache->Draw(model, view, BPoint(hIconOffset, 0), 422 kNormalIcon, B_MINI_ICON, true); 423 424 // See if we need to truncate the string 425 BString nameString = model->Name(); 426 if (view->StringWidth(model->Name()) > rect.Width()) 427 view->TruncateString(&nameString, B_TRUNCATE_END, rect.Width() - 5); 428 429 // Draw the label 430 float leftText = (view->StringWidth(nameString.String()) 431 - Bounds().Width()) / 2; 432 view->MovePenTo(BPoint(hIconOffset - leftText + 2, Bounds().Height() 433 + (fontHeight.ascent + 2))); 434 view->DrawString(nameString.String()); 435 436 view->Sync(); 437 dragBitmap->Unlock(); 438 439 BMessage message(B_SIMPLE_DATA); 440 message.AddRef("refs", model->EntryRef()); 441 message.AddPoint("click_pt", fClickPoint); 442 443 BPoint tmpLoc; 444 uint32 button; 445 GetMouse(&tmpLoc, &button); 446 if (button) 447 message.AddInt32("buttons", (int32)button); 448 449 if (button & B_PRIMARY_MOUSE_BUTTON) { 450 // add an action specifier to the message, so that it is not copied 451 message.AddInt32("be:actions", (modifiers() & B_OPTION_KEY) != 0 452 ? B_COPY_TARGET : B_MOVE_TARGET); 453 } 454 455 fDragStarted = true; 456 fDragButton = 0; 457 458 DragMessage(&message, dragBitmap, B_OP_ALPHA, 459 BPoint(fClickPoint.x + hIconOffset, fClickPoint.y), this); 460 } 461 462 463 void 464 DraggableContainerIcon::FrameMoved(BPoint /*newLocation*/) 465 { 466 BMenuBar* bar = dynamic_cast<BMenuBar*>(Parent()); 467 if (bar == NULL) 468 return; 469 470 // TODO: ugly hack following: 471 // This is a trick to get the actual width of all menu items 472 // (BMenuBar may not have set the item coordinates yet...) 473 float width, height; 474 uint32 resizingMode = bar->ResizingMode(); 475 bar->SetResizingMode(B_FOLLOW_NONE); 476 bar->GetPreferredSize(&width, &height); 477 bar->SetResizingMode(resizingMode); 478 479 //BMenuItem* item = bar->ItemAt(bar->CountItems() - 1); 480 //if (item == NULL) 481 // return; 482 483 // BeOS shifts the coordinates for hidden views, so we cannot 484 // use them to decide if we should be visible or not... 485 486 float gap = bar->Frame().Width() - 2 - width; //item->Frame().right; 487 488 if (gap <= Bounds().Width() && !IsHidden()) 489 Hide(); 490 else if (gap > Bounds().Width() && IsHidden()) 491 Show(); 492 } 493 494 495 void 496 DraggableContainerIcon::Draw(BRect updateRect) 497 { 498 BContainerWindow* window = dynamic_cast<BContainerWindow*>(Window()); 499 if (window == NULL) 500 return; 501 502 if (be_control_look != NULL) { 503 BRect rect(Bounds()); 504 rgb_color base = ui_color(B_MENU_BACKGROUND_COLOR); 505 be_control_look->DrawMenuBarBackground(this, rect, updateRect, base, 506 0, 0); 507 } 508 509 // Draw the icon, straddling the border 510 #ifdef __HAIKU__ 511 SetDrawingMode(B_OP_ALPHA); 512 SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); 513 #else 514 SetDrawingMode(B_OP_OVER); 515 #endif 516 float iconOffset = (Bounds().Width() - B_MINI_ICON) / 2; 517 IconCache::sIconCache->Draw(window->TargetModel(), this, 518 BPoint(iconOffset, iconOffset), kNormalIcon, B_MINI_ICON, true); 519 } 520 521 522 // #pragma mark - BContainerWindow 523 524 525 BContainerWindow::BContainerWindow(LockingList<BWindow>* list, 526 uint32 containerWindowFlags, window_look look, window_feel feel, 527 uint32 flags, uint32 workspace) 528 : 529 BWindow(InitialWindowRect(feel), "TrackerWindow", look, feel, flags, 530 workspace), 531 fFileContextMenu(NULL), 532 fWindowContextMenu(NULL), 533 fDropContextMenu(NULL), 534 fVolumeContextMenu(NULL), 535 fDragContextMenu(NULL), 536 fMoveToItem(NULL), 537 fCopyToItem(NULL), 538 fCreateLinkItem(NULL), 539 fOpenWithItem(NULL), 540 fNavigationItem(NULL), 541 fMenuBar(NULL), 542 fNavigator(NULL), 543 fPoseView(NULL), 544 fWindowList(list), 545 fAttrMenu(NULL), 546 fWindowMenu(NULL), 547 fFileMenu(NULL), 548 fArrangeByMenu(NULL), 549 fSelectionWindow(NULL), 550 fTaskLoop(NULL), 551 fIsTrash(false), 552 fInTrash(false), 553 fIsPrinters(false), 554 fContainerWindowFlags(containerWindowFlags), 555 fBackgroundImage(NULL), 556 fSavedZoomRect(0, 0, -1, -1), 557 fContextMenu(NULL), 558 fDragMessage(NULL), 559 fCachedTypesList(NULL), 560 fStateNeedsSaving(false), 561 fSaveStateIsEnabled(true), 562 fIsWatchingPath(false) 563 { 564 InitIconPreloader(); 565 566 if (list) { 567 ASSERT(list->IsLocked()); 568 list->AddItem(this); 569 } 570 571 AddCommonFilter(new BMessageFilter(B_MOUSE_DOWN, ActivateWindowFilter)); 572 573 Run(); 574 575 // Watch out for settings changes: 576 if (TTracker* app = dynamic_cast<TTracker*>(be_app)) { 577 app->Lock(); 578 app->StartWatching(this, kWindowsShowFullPathChanged); 579 app->StartWatching(this, kSingleWindowBrowseChanged); 580 app->StartWatching(this, kShowNavigatorChanged); 581 app->StartWatching(this, kDontMoveFilesToTrashChanged); 582 app->Unlock(); 583 } 584 585 // ToDo: remove me once we have undo/redo menu items 586 // (that is, move them to AddShortcuts()) 587 AddShortcut('Z', B_COMMAND_KEY, new BMessage(B_UNDO), this); 588 AddShortcut('Z', B_COMMAND_KEY | B_SHIFT_KEY, new BMessage(kRedo), this); 589 } 590 591 592 BContainerWindow::~BContainerWindow() 593 { 594 ASSERT(IsLocked()); 595 596 // stop the watchers 597 if (TTracker* app = dynamic_cast<TTracker*>(be_app)) { 598 app->Lock(); 599 app->StopWatching(this, kWindowsShowFullPathChanged); 600 app->StopWatching(this, kSingleWindowBrowseChanged); 601 app->StopWatching(this, kShowNavigatorChanged); 602 app->StopWatching(this, kDontMoveFilesToTrashChanged); 603 app->Unlock(); 604 } 605 606 delete fTaskLoop; 607 delete fBackgroundImage; 608 delete fDragMessage; 609 delete fCachedTypesList; 610 611 if (fSelectionWindow != NULL && fSelectionWindow->Lock()) 612 fSelectionWindow->Quit(); 613 } 614 615 616 BRect 617 BContainerWindow::InitialWindowRect(window_feel feel) 618 { 619 if (feel != kPrivateDesktopWindowFeel) 620 return sNewWindRect; 621 622 // do not offset desktop window 623 BRect result = sNewWindRect; 624 result.OffsetTo(0, 0); 625 return result; 626 } 627 628 629 void 630 BContainerWindow::Minimize(bool minimize) 631 { 632 if (minimize && (modifiers() & B_OPTION_KEY) != 0) 633 do_minimize_team(BRect(0, 0, 0, 0), be_app->Team(), true); 634 else 635 _inherited::Minimize(minimize); 636 } 637 638 639 bool 640 BContainerWindow::QuitRequested() 641 { 642 // this is a response to the DeskBar sending us a B_QUIT, when it really 643 // means to say close all your windows. It might be better to have it 644 // send a kCloseAllWindows message and have windowless apps stay running, 645 // which is what we will do for the Tracker 646 if (CurrentMessage() != NULL 647 && ((CurrentMessage()->FindInt32("modifiers") & B_CONTROL_KEY)) != 0) { 648 be_app->PostMessage(kCloseAllWindows); 649 } 650 651 Hide(); 652 // this will close the window instantly, even if 653 // the file system is very busy right now 654 return true; 655 } 656 657 658 void 659 BContainerWindow::Quit() 660 { 661 // get rid of context menus 662 if (fNavigationItem) { 663 BMenu* menu = fNavigationItem->Menu(); 664 if (menu) 665 menu->RemoveItem(fNavigationItem); 666 delete fNavigationItem; 667 fNavigationItem = NULL; 668 } 669 670 if (fOpenWithItem && !fOpenWithItem->Menu()) { 671 delete fOpenWithItem; 672 fOpenWithItem = NULL; 673 } 674 675 if (fMoveToItem && !fMoveToItem->Menu()) { 676 delete fMoveToItem; 677 fMoveToItem = NULL; 678 } 679 680 if (fCopyToItem && !fCopyToItem->Menu()) { 681 delete fCopyToItem; 682 fCopyToItem = NULL; 683 } 684 685 if (fCreateLinkItem && !fCreateLinkItem->Menu()) { 686 delete fCreateLinkItem; 687 fCreateLinkItem = NULL; 688 } 689 690 if (fAttrMenu && !fAttrMenu->Supermenu()) { 691 delete fAttrMenu; 692 fAttrMenu = NULL; 693 } 694 695 delete fFileContextMenu; 696 fFileContextMenu = NULL; 697 delete fWindowContextMenu; 698 fWindowContextMenu = NULL; 699 delete fDropContextMenu; 700 fDropContextMenu = NULL; 701 delete fVolumeContextMenu; 702 fVolumeContextMenu = NULL; 703 delete fDragContextMenu; 704 fDragContextMenu = NULL; 705 706 int32 windowCount = 0; 707 708 // This is a deadlock code sequence - need to change this 709 // to acquire the window list while this container window is unlocked 710 if (fWindowList) { 711 AutoLock<LockingList<BWindow> > lock(fWindowList); 712 if (lock.IsLocked()) { 713 fWindowList->RemoveItem(this); 714 windowCount = fWindowList->CountItems(); 715 } 716 } 717 718 if (StateNeedsSaving()) 719 SaveState(); 720 721 if (fWindowList && windowCount == 0) 722 be_app->PostMessage(B_QUIT_REQUESTED); 723 724 _inherited::Quit(); 725 } 726 727 728 BPoseView* 729 BContainerWindow::NewPoseView(Model* model, BRect rect, uint32 viewMode) 730 { 731 return new BPoseView(model, rect, viewMode); 732 } 733 734 735 void 736 BContainerWindow::UpdateIfTrash(Model* model) 737 { 738 BEntry entry(model->EntryRef()); 739 740 if (entry.InitCheck() == B_OK) { 741 fIsTrash = model->IsTrash(); 742 fInTrash = FSInTrashDir(model->EntryRef()); 743 fIsPrinters = FSIsPrintersDir(&entry); 744 } 745 } 746 747 748 void 749 BContainerWindow::CreatePoseView(Model* model) 750 { 751 UpdateIfTrash(model); 752 BRect rect(Bounds()); 753 754 TrackerSettings settings; 755 if (settings.SingleWindowBrowse() && settings.ShowNavigator() 756 && model->IsDirectory()) { 757 rect.top += BNavigator::CalcNavigatorHeight() + 1; 758 } 759 760 rect.right -= B_V_SCROLL_BAR_WIDTH; 761 rect.bottom -= B_H_SCROLL_BAR_HEIGHT; 762 fPoseView = NewPoseView(model, rect, kListMode); 763 AddChild(fPoseView); 764 765 if (settings.SingleWindowBrowse() && model->IsDirectory() 766 && !fPoseView->IsFilePanel()) { 767 BRect rect(Bounds()); 768 rect.top = 0; 769 // The KeyMenuBar isn't attached yet, otherwise we'd use that 770 // to get the offset. 771 rect.bottom = BNavigator::CalcNavigatorHeight(); 772 fNavigator = new BNavigator(model, rect); 773 if (!settings.ShowNavigator()) 774 fNavigator->Hide(); 775 776 AddChild(fNavigator); 777 } 778 779 SetPathWatchingEnabled(settings.ShowNavigator() 780 || settings.ShowFullPathInTitleBar()); 781 } 782 783 784 void 785 BContainerWindow::AddContextMenus() 786 { 787 // create context sensitive menus 788 fFileContextMenu = new BPopUpMenu("FileContext", false, false); 789 fFileContextMenu->SetFont(be_plain_font); 790 AddFileContextMenus(fFileContextMenu); 791 792 fVolumeContextMenu = new BPopUpMenu("VolumeContext", false, false); 793 fVolumeContextMenu->SetFont(be_plain_font); 794 AddVolumeContextMenus(fVolumeContextMenu); 795 796 fWindowContextMenu = new BPopUpMenu("WindowContext", false, false); 797 fWindowContextMenu->SetFont(be_plain_font); 798 AddWindowContextMenus(fWindowContextMenu); 799 800 fDropContextMenu = new BPopUpMenu("DropContext", false, false); 801 fDropContextMenu->SetFont(be_plain_font); 802 AddDropContextMenus(fDropContextMenu); 803 804 fDragContextMenu = new BSlowContextMenu("DragContext"); 805 // will get added and built dynamically in ShowContextMenu 806 807 fTrashContextMenu = new BPopUpMenu("TrashContext", false, false); 808 fTrashContextMenu->SetFont(be_plain_font); 809 AddTrashContextMenus(fTrashContextMenu); 810 } 811 812 813 void 814 BContainerWindow::RepopulateMenus() 815 { 816 // Avoid these menus to be destroyed: 817 if (fMoveToItem && fMoveToItem->Menu()) 818 fMoveToItem->Menu()->RemoveItem(fMoveToItem); 819 820 if (fCopyToItem && fCopyToItem->Menu()) 821 fCopyToItem->Menu()->RemoveItem(fCopyToItem); 822 823 if (fCreateLinkItem && fCreateLinkItem->Menu()) 824 fCreateLinkItem->Menu()->RemoveItem(fCreateLinkItem); 825 826 if (fOpenWithItem && fOpenWithItem->Menu()) { 827 fOpenWithItem->Menu()->RemoveItem(fOpenWithItem); 828 delete fOpenWithItem; 829 fOpenWithItem = NULL; 830 } 831 832 if (fNavigationItem) { 833 BMenu* menu = fNavigationItem->Menu(); 834 if (menu) { 835 menu->RemoveItem(fNavigationItem); 836 BMenuItem* item = menu->RemoveItem((int32)0); 837 ASSERT(item != fNavigationItem); 838 delete item; 839 } 840 } 841 842 delete fFileContextMenu; 843 fFileContextMenu = new BPopUpMenu("FileContext", false, false); 844 fFileContextMenu->SetFont(be_plain_font); 845 AddFileContextMenus(fFileContextMenu); 846 847 delete fWindowContextMenu; 848 fWindowContextMenu = new BPopUpMenu("WindowContext", false, false); 849 fWindowContextMenu->SetFont(be_plain_font); 850 AddWindowContextMenus(fWindowContextMenu); 851 852 if (fMenuBar != NULL) { 853 fMenuBar->RemoveItem(fFileMenu); 854 delete fFileMenu; 855 fFileMenu = new BMenu(B_TRANSLATE("File")); 856 AddFileMenu(fFileMenu); 857 fMenuBar->AddItem(fFileMenu); 858 859 fMenuBar->RemoveItem(fWindowMenu); 860 delete fWindowMenu; 861 fWindowMenu = new BMenu(B_TRANSLATE("Window")); 862 fMenuBar->AddItem(fWindowMenu); 863 AddWindowMenu(fWindowMenu); 864 865 // just create the attribute, decide to add it later 866 fMenuBar->RemoveItem(fAttrMenu); 867 delete fAttrMenu; 868 fAttrMenu = new BMenu(B_TRANSLATE("Attributes")); 869 NewAttributeMenu(fAttrMenu); 870 if (PoseView()->ViewMode() == kListMode) 871 ShowAttributeMenu(); 872 873 PopulateArrangeByMenu(fArrangeByMenu); 874 875 int32 selectCount = PoseView()->SelectionList()->CountItems(); 876 877 SetupOpenWithMenu(fFileMenu); 878 SetupMoveCopyMenus(selectCount ? PoseView()->SelectionList() 879 ->FirstItem()->TargetModel()->EntryRef() : NULL, 880 fFileMenu); 881 } 882 } 883 884 885 void 886 BContainerWindow::Init(const BMessage* message) 887 { 888 float y_delta; 889 BEntry entry; 890 891 ASSERT(fPoseView); 892 if (!fPoseView) 893 return; 894 895 // deal with new unconfigured folders 896 if (NeedsDefaultStateSetup()) 897 SetUpDefaultState(); 898 899 if (ShouldAddScrollBars()) 900 fPoseView->AddScrollBars(); 901 902 fMoveToItem = new BMenuItem(new BNavMenu(B_TRANSLATE("Move to"), 903 kMoveSelectionTo, this)); 904 fCopyToItem = new BMenuItem(new BNavMenu(B_TRANSLATE("Copy to"), 905 kCopySelectionTo, this)); 906 fCreateLinkItem = new BMenuItem(new BNavMenu(B_TRANSLATE("Create link"), 907 kCreateLink, this), new BMessage(kCreateLink)); 908 909 TrackerSettings settings; 910 911 if (ShouldAddMenus()) { 912 // add menu bar, menus and resize poseview to fit 913 fMenuBar = new BMenuBar(BRect(0, 0, Bounds().Width() + 1, 1), 914 "MenuBar"); 915 fMenuBar->SetBorder(B_BORDER_FRAME); 916 AddMenus(); 917 AddChild(fMenuBar); 918 919 y_delta = KeyMenuBar()->Bounds().Height() + 1; 920 921 float navigatorDelta = 0; 922 923 if (Navigator() && settings.ShowNavigator()) { 924 Navigator()->MoveTo(BPoint(0, y_delta)); 925 navigatorDelta = BNavigator::CalcNavigatorHeight() + 1; 926 } 927 928 fPoseView->MoveTo(BPoint(0, navigatorDelta + y_delta)); 929 fPoseView->ResizeBy(0, -(y_delta)); 930 if (fPoseView->VScrollBar()) { 931 fPoseView->VScrollBar()->MoveBy(0, 932 KeyMenuBar()->Bounds().Height()); 933 fPoseView->VScrollBar()->ResizeBy(0, 934 -(KeyMenuBar()->Bounds().Height())); 935 } 936 937 // add folder icon to menu bar 938 if (!TargetModel()->IsRoot() && !IsTrash()) { 939 float iconSize = fMenuBar->Bounds().Height() - 2; 940 if (iconSize < 16) 941 iconSize = 16; 942 float iconPosY = 1 + (fMenuBar->Bounds().Height() - 2 943 - iconSize) / 2; 944 BView* icon = new DraggableContainerIcon(BRect(Bounds().Width() 945 - 4 - iconSize + 1, iconPosY, Bounds().Width() - 4, 946 iconPosY + iconSize - 1), "ThisContainer", 947 B_FOLLOW_RIGHT); 948 fMenuBar->AddChild(icon); 949 } 950 } else { 951 // add equivalents of the menu shortcuts to the menuless 952 // desktop window 953 AddShortcuts(); 954 } 955 956 AddContextMenus(); 957 AddShortcut('T', B_COMMAND_KEY | B_SHIFT_KEY, new BMessage(kDelete), 958 PoseView()); 959 AddShortcut('K', B_COMMAND_KEY | B_SHIFT_KEY, new BMessage(kCleanupAll), 960 PoseView()); 961 AddShortcut('Q', B_COMMAND_KEY | B_OPTION_KEY | B_SHIFT_KEY 962 | B_CONTROL_KEY, new BMessage(kQuitTracker)); 963 964 AddShortcut(B_DOWN_ARROW, B_COMMAND_KEY, new BMessage(kOpenSelection), 965 PoseView()); 966 967 SetSingleWindowBrowseShortcuts(settings.SingleWindowBrowse()); 968 969 #if DEBUG 970 // add some debugging shortcuts 971 AddShortcut('D', B_COMMAND_KEY | B_CONTROL_KEY, new BMessage('dbug'), 972 PoseView()); 973 AddShortcut('C', B_COMMAND_KEY | B_CONTROL_KEY, new BMessage('dpcc'), 974 PoseView()); 975 AddShortcut('F', B_COMMAND_KEY | B_CONTROL_KEY, new BMessage('dpfl'), 976 PoseView()); 977 AddShortcut('F', B_COMMAND_KEY | B_CONTROL_KEY | B_OPTION_KEY, 978 new BMessage('dpfL'), PoseView()); 979 #endif 980 981 BKeymap keymap; 982 keymap.SetToCurrent(); 983 BObjectList<const char> unmodified(3, true); 984 if (keymap.GetModifiedCharacters("+", B_SHIFT_KEY, 0, &unmodified) 985 == B_OK) { 986 int32 count = unmodified.CountItems(); 987 for (int32 i = 0; i < count; i++) { 988 uint32 key = BUnicodeChar::FromUTF8(unmodified.ItemAt(i)); 989 if (!HasShortcut(key, 0)) { 990 // Add semantic zoom in shortcut, bug #6692 991 BMessage* increaseSize = new BMessage(kIconMode); 992 increaseSize->AddInt32("scale", 1); 993 AddShortcut(key, B_COMMAND_KEY, increaseSize, PoseView()); 994 } 995 } 996 } 997 unmodified.MakeEmpty(); 998 999 if (message) 1000 RestoreState(*message); 1001 else 1002 RestoreState(); 1003 1004 if (ShouldAddMenus() && PoseView()->ViewMode() == kListMode) { 1005 // for now only show attributes in list view 1006 // eventually enable attribute menu to allow users to select 1007 // using different attributes as titles in icon view modes 1008 ShowAttributeMenu(); 1009 } 1010 MarkAttributeMenu(fAttrMenu); 1011 CheckScreenIntersect(); 1012 1013 if (fBackgroundImage != NULL && dynamic_cast<BDeskWindow*>(this) == NULL 1014 && PoseView()->ViewMode() != kListMode) { 1015 fBackgroundImage->Show(PoseView(), current_workspace()); 1016 } 1017 1018 Show(); 1019 1020 // done showing, turn the B_NO_WORKSPACE_ACTIVATION flag off; 1021 // it was on to prevent workspace jerking during boot 1022 SetFlags(Flags() & ~B_NO_WORKSPACE_ACTIVATION); 1023 } 1024 1025 1026 void 1027 BContainerWindow::RestoreState() 1028 { 1029 SetSizeLimits(kContainerWidthMinLimit, 10000, 1030 kContainerWindowHeightLimit, 10000); 1031 1032 UpdateTitle(); 1033 1034 WindowStateNodeOpener opener(this, false); 1035 RestoreWindowState(opener.StreamNode()); 1036 fPoseView->Init(opener.StreamNode()); 1037 1038 RestoreStateCommon(); 1039 } 1040 1041 1042 void 1043 BContainerWindow::RestoreState(const BMessage &message) 1044 { 1045 SetSizeLimits(kContainerWidthMinLimit, 10000, 1046 kContainerWindowHeightLimit, 10000); 1047 1048 UpdateTitle(); 1049 1050 RestoreWindowState(message); 1051 fPoseView->Init(message); 1052 1053 RestoreStateCommon(); 1054 } 1055 1056 1057 void 1058 BContainerWindow::RestoreStateCommon() 1059 { 1060 if (BootedInSafeMode()) 1061 // don't pick up backgrounds in safe mode 1062 return; 1063 1064 WindowStateNodeOpener opener(this, false); 1065 1066 bool isDesktop = dynamic_cast<BDeskWindow*>(this) != NULL; 1067 if (!TargetModel()->IsRoot() && opener.Node() != NULL) { 1068 // don't pick up background image for root disks 1069 // to do this, would have to have a unique attribute for the 1070 // disks window that doesn't collide with the desktop 1071 // for R4 this was not done to make things simpler 1072 // the default image will still work though 1073 fBackgroundImage = BackgroundImage::GetBackgroundImage( 1074 opener.Node(), isDesktop); 1075 // look for background image info in the window's node 1076 } 1077 1078 BNode defaultingNode; 1079 if (fBackgroundImage == NULL && !isDesktop 1080 && DefaultStateSourceNode(kDefaultFolderTemplate, &defaultingNode)) { 1081 // look for background image info in the source for defaults 1082 fBackgroundImage = BackgroundImage::GetBackgroundImage(&defaultingNode, 1083 isDesktop); 1084 } 1085 } 1086 1087 1088 void 1089 BContainerWindow::UpdateTitle() 1090 { 1091 // set title to full path, if necessary 1092 if (TrackerSettings().ShowFullPathInTitleBar()) { 1093 // use the Entry's full path 1094 BPath path; 1095 TargetModel()->GetPath(&path); 1096 SetTitle(path.Path()); 1097 } else { 1098 // use the default look 1099 SetTitle(TargetModel()->Name()); 1100 } 1101 1102 if (Navigator() != NULL) { 1103 Navigator()->UpdateLocation(PoseView()->TargetModel(), 1104 kActionUpdatePath); 1105 } 1106 } 1107 1108 1109 void 1110 BContainerWindow::UpdateBackgroundImage() 1111 { 1112 if (BootedInSafeMode()) 1113 return; 1114 1115 bool isDesktop = dynamic_cast<BDeskWindow*>(this) != NULL; 1116 WindowStateNodeOpener opener(this, false); 1117 1118 if (!TargetModel()->IsRoot() && opener.Node() != NULL) { 1119 fBackgroundImage = BackgroundImage::Refresh(fBackgroundImage, 1120 opener.Node(), isDesktop, PoseView()); 1121 } 1122 1123 // look for background image info in the window's node 1124 BNode defaultingNode; 1125 if (!fBackgroundImage && !isDesktop 1126 && DefaultStateSourceNode(kDefaultFolderTemplate, &defaultingNode)) { 1127 // look for background image info in the source for defaults 1128 fBackgroundImage = BackgroundImage::Refresh(fBackgroundImage, 1129 &defaultingNode, isDesktop, PoseView()); 1130 } 1131 } 1132 1133 1134 void 1135 BContainerWindow::FrameResized(float, float) 1136 { 1137 if (PoseView() != NULL && dynamic_cast<BDeskWindow*>(this) == NULL) { 1138 BRect extent = PoseView()->Extent(); 1139 float offsetX = extent.left - PoseView()->Bounds().left; 1140 float offsetY = extent.top - PoseView()->Bounds().top; 1141 1142 // scroll when the size augmented, there is a negative offset 1143 // and we have resized over the bottom right corner of the extent 1144 BPoint scroll(B_ORIGIN); 1145 if (offsetX < 0 && PoseView()->Bounds().right > extent.right 1146 && Bounds().Width() > fPreviousBounds.Width()) { 1147 scroll.x = std::max(fPreviousBounds.Width() - Bounds().Width(), 1148 offsetX); 1149 } 1150 1151 if (offsetY < 0 && PoseView()->Bounds().bottom > extent.bottom 1152 && Bounds().Height() > fPreviousBounds.Height()) { 1153 scroll.y = std::max(fPreviousBounds.Height() - Bounds().Height(), 1154 offsetY); 1155 } 1156 1157 if (scroll != B_ORIGIN) 1158 PoseView()->ScrollBy(scroll.x, scroll.y); 1159 1160 PoseView()->UpdateScrollRange(); 1161 PoseView()->ResetPosePlacementHint(); 1162 } 1163 1164 fPreviousBounds = Bounds(); 1165 fStateNeedsSaving = true; 1166 } 1167 1168 1169 void 1170 BContainerWindow::FrameMoved(BPoint) 1171 { 1172 fStateNeedsSaving = true; 1173 } 1174 1175 1176 void 1177 BContainerWindow::WorkspacesChanged(uint32, uint32) 1178 { 1179 fStateNeedsSaving = true; 1180 } 1181 1182 1183 void 1184 BContainerWindow::ViewModeChanged(uint32 oldMode, uint32 newMode) 1185 { 1186 BView* view = FindView("MenuBar"); 1187 if (view != NULL) { 1188 // make sure the draggable icon hides if it doesn't 1189 // have space left anymore 1190 view = view->FindView("ThisContainer"); 1191 if (view != NULL) 1192 view->FrameMoved(view->Frame().LeftTop()); 1193 } 1194 1195 if (fBackgroundImage == NULL) 1196 return; 1197 1198 if (newMode == kListMode) 1199 fBackgroundImage->Remove(); 1200 else if (oldMode == kListMode) 1201 fBackgroundImage->Show(PoseView(), current_workspace()); 1202 } 1203 1204 1205 void 1206 BContainerWindow::CheckScreenIntersect() 1207 { 1208 BScreen screen(this); 1209 BRect screenFrame(screen.Frame()); 1210 BRect frame(Frame()); 1211 1212 if (sNewWindRect.bottom > screenFrame.bottom) 1213 sNewWindRect.OffsetTo(85, 50); 1214 1215 if (sNewWindRect.right > screenFrame.right) 1216 sNewWindRect.OffsetTo(85, 50); 1217 1218 if (!frame.Intersects(screenFrame)) 1219 MoveTo(sNewWindRect.LeftTop()); 1220 } 1221 1222 1223 void 1224 BContainerWindow::SaveState(bool hide) 1225 { 1226 if (SaveStateIsEnabled()) { 1227 WindowStateNodeOpener opener(this, true); 1228 if (opener.StreamNode() != NULL) 1229 SaveWindowState(opener.StreamNode()); 1230 1231 if (hide) 1232 Hide(); 1233 1234 if (opener.StreamNode()) 1235 fPoseView->SaveState(opener.StreamNode()); 1236 1237 fStateNeedsSaving = false; 1238 } 1239 } 1240 1241 1242 void 1243 BContainerWindow::SaveState(BMessage& message) const 1244 { 1245 if (SaveStateIsEnabled()) { 1246 SaveWindowState(message); 1247 fPoseView->SaveState(message); 1248 } 1249 } 1250 1251 1252 bool 1253 BContainerWindow::StateNeedsSaving() const 1254 { 1255 return fStateNeedsSaving || PoseView()->StateNeedsSaving(); 1256 } 1257 1258 1259 status_t 1260 BContainerWindow::GetLayoutState(BNode* node, BMessage* message) 1261 { 1262 // ToDo: get rid of this, use AttrStream instead 1263 status_t result = node->InitCheck(); 1264 if (result != B_OK) 1265 return result; 1266 1267 node->RewindAttrs(); 1268 char attrName[256]; 1269 while (node->GetNextAttrName(attrName) == B_OK) { 1270 attr_info info; 1271 node->GetAttrInfo(attrName, &info); 1272 1273 // filter out attributes that are not related to window position 1274 // and column resizing 1275 // more can be added as needed 1276 if (strcmp(attrName, kAttrWindowFrame) != 0 1277 && strcmp(attrName, kAttrColumns) != 0 1278 && strcmp(attrName, kAttrViewState) != 0 1279 && strcmp(attrName, kAttrColumnsForeign) != 0 1280 && strcmp(attrName, kAttrViewStateForeign) != 0) { 1281 continue; 1282 } 1283 1284 char* buffer = new char[info.size]; 1285 if (node->ReadAttr(attrName, info.type, 0, buffer, 1286 (size_t)info.size) == info.size) { 1287 message->AddData(attrName, info.type, buffer, (ssize_t)info.size); 1288 } 1289 delete[] buffer; 1290 } 1291 1292 return B_OK; 1293 } 1294 1295 1296 status_t 1297 BContainerWindow::SetLayoutState(BNode* node, const BMessage* message) 1298 { 1299 status_t result = node->InitCheck(); 1300 if (result != B_OK) 1301 return result; 1302 1303 for (int32 globalIndex = 0; ;) { 1304 #if B_BEOS_VERSION_DANO 1305 const char* name; 1306 #else 1307 char* name; 1308 #endif 1309 type_code type; 1310 int32 count; 1311 status_t result = message->GetInfo(B_ANY_TYPE, globalIndex, &name, 1312 &type, &count); 1313 if (result != B_OK) 1314 break; 1315 1316 for (int32 index = 0; index < count; index++) { 1317 const void* buffer; 1318 ssize_t size; 1319 result = message->FindData(name, type, index, &buffer, &size); 1320 if (result != B_OK) { 1321 PRINT(("error reading %s \n", name)); 1322 return result; 1323 } 1324 1325 if (node->WriteAttr(name, type, 0, buffer, 1326 (size_t)size) != size) { 1327 PRINT(("error writing %s \n", name)); 1328 return result; 1329 } 1330 globalIndex++; 1331 } 1332 } 1333 1334 return B_OK; 1335 } 1336 1337 1338 bool 1339 BContainerWindow::ShouldAddMenus() const 1340 { 1341 return true; 1342 } 1343 1344 1345 bool 1346 BContainerWindow::ShouldAddScrollBars() const 1347 { 1348 return true; 1349 } 1350 1351 1352 bool 1353 BContainerWindow::ShouldAddCountView() const 1354 { 1355 return true; 1356 } 1357 1358 1359 Model* 1360 BContainerWindow::TargetModel() const 1361 { 1362 return fPoseView->TargetModel(); 1363 } 1364 1365 1366 void 1367 BContainerWindow::SelectionChanged() 1368 { 1369 } 1370 1371 1372 void 1373 BContainerWindow::Zoom(BPoint, float, float) 1374 { 1375 BRect oldZoomRect(fSavedZoomRect); 1376 fSavedZoomRect = Frame(); 1377 ResizeToFit(); 1378 1379 if (fSavedZoomRect == Frame() && oldZoomRect.IsValid()) 1380 ResizeTo(oldZoomRect.Width(), oldZoomRect.Height()); 1381 } 1382 1383 1384 void 1385 BContainerWindow::ResizeToFit() 1386 { 1387 BScreen screen(this); 1388 BRect screenFrame(screen.Frame()); 1389 1390 screenFrame.InsetBy(5, 5); 1391 screenFrame.top += 15; 1392 // keeps title bar of window visible 1393 1394 BRect frame(Frame()); 1395 1396 float widthDiff = frame.Width() - PoseView()->Frame().Width(); 1397 float heightDiff = frame.Height() - PoseView()->Frame().Height(); 1398 1399 // move frame left top on screen 1400 BPoint leftTop(frame.LeftTop()); 1401 leftTop.ConstrainTo(screenFrame); 1402 frame.OffsetTo(leftTop); 1403 1404 // resize to extent size 1405 BRect extent(PoseView()->Extent()); 1406 frame.right = frame.left + extent.Width() + widthDiff; 1407 frame.bottom = frame.top + extent.Height() + heightDiff; 1408 1409 // make sure entire window fits on screen 1410 frame = frame & screenFrame; 1411 1412 ResizeTo(frame.Width(), frame.Height()); 1413 MoveTo(frame.LeftTop()); 1414 PoseView()->DisableScrollBars(); 1415 1416 // scroll if there is an offset 1417 PoseView()->ScrollBy( 1418 extent.left - PoseView()->Bounds().left, 1419 extent.top - PoseView()->Bounds().top); 1420 1421 PoseView()->UpdateScrollRange(); 1422 PoseView()->EnableScrollBars(); 1423 } 1424 1425 1426 void 1427 BContainerWindow::MessageReceived(BMessage* message) 1428 { 1429 switch (message->what) { 1430 case B_CUT: 1431 case B_COPY: 1432 case B_PASTE: 1433 case kCutMoreSelectionToClipboard: 1434 case kCopyMoreSelectionToClipboard: 1435 case kPasteLinksFromClipboard: 1436 { 1437 BView* view = CurrentFocus(); 1438 if (view->LockLooper()) { 1439 view->MessageReceived(message); 1440 view->UnlockLooper(); 1441 } 1442 break; 1443 } 1444 1445 case kNewFolder: 1446 PostMessage(message, PoseView()); 1447 break; 1448 1449 case kRestoreState: 1450 if (message->HasMessage("state")) { 1451 BMessage state; 1452 message->FindMessage("state", &state); 1453 Init(&state); 1454 } else 1455 Init(); 1456 break; 1457 1458 case kResizeToFit: 1459 ResizeToFit(); 1460 break; 1461 1462 case kLoadAddOn: 1463 LoadAddOn(message); 1464 break; 1465 1466 case kCopySelectionTo: 1467 { 1468 entry_ref ref; 1469 if (message->FindRef("refs", &ref) != B_OK) 1470 break; 1471 1472 BRoster().AddToRecentFolders(&ref); 1473 1474 Model model(&ref); 1475 if (model.InitCheck() != B_OK) 1476 break; 1477 1478 if (*model.NodeRef() == *TargetModel()->NodeRef()) 1479 PoseView()->DuplicateSelection(); 1480 else 1481 PoseView()->MoveSelectionInto(&model, this, true); 1482 break; 1483 } 1484 1485 case kMoveSelectionTo: 1486 { 1487 entry_ref ref; 1488 if (message->FindRef("refs", &ref) != B_OK) 1489 break; 1490 1491 BRoster().AddToRecentFolders(&ref); 1492 1493 Model model(&ref); 1494 if (model.InitCheck() != B_OK) 1495 break; 1496 1497 PoseView()->MoveSelectionInto(&model, this, false, true); 1498 break; 1499 } 1500 1501 case kCreateLink: 1502 case kCreateRelativeLink: 1503 { 1504 entry_ref ref; 1505 if (message->FindRef("refs", &ref) == B_OK) { 1506 BRoster().AddToRecentFolders(&ref); 1507 1508 Model model(&ref); 1509 if (model.InitCheck() != B_OK) 1510 break; 1511 1512 PoseView()->MoveSelectionInto(&model, this, false, false, 1513 message->what == kCreateLink, 1514 message->what == kCreateRelativeLink); 1515 } else if (!TargetModel()->IsQuery() 1516 && !TargetModel()->IsVirtualDirectory()) { 1517 // no destination specified, create link in same dir as item 1518 PoseView()->MoveSelectionInto(TargetModel(), this, false, false, 1519 message->what == kCreateLink, 1520 message->what == kCreateRelativeLink); 1521 } 1522 break; 1523 } 1524 1525 case kShowSelectionWindow: 1526 ShowSelectionWindow(); 1527 break; 1528 1529 case kSelectMatchingEntries: 1530 PoseView()->SelectMatchingEntries(message); 1531 break; 1532 1533 case kFindButton: 1534 (new FindWindow())->Show(); 1535 break; 1536 1537 case kRestartDeskbar: 1538 { 1539 BRoster roster; 1540 roster.Launch(kDeskbarSignature); 1541 break; 1542 } 1543 1544 case kQuitTracker: 1545 be_app->PostMessage(B_QUIT_REQUESTED); 1546 break; 1547 1548 case kRestoreBackgroundImage: 1549 UpdateBackgroundImage(); 1550 break; 1551 1552 case kSwitchDirectory: 1553 { 1554 entry_ref ref; 1555 if (message->FindRef("refs", &ref) == B_OK) { 1556 BEntry entry; 1557 if (entry.SetTo(&ref) == B_OK) { 1558 if (StateNeedsSaving()) 1559 SaveState(false); 1560 1561 bool wasInTrash = IsTrash() || InTrash(); 1562 bool isRoot = PoseView()->TargetModel()->IsRoot(); 1563 1564 // Switch dir and apply new state 1565 WindowStateNodeOpener opener(this, false); 1566 opener.SetTo(&entry, false); 1567 1568 // Update PoseView 1569 PoseView()->SwitchDir(&ref, opener.StreamNode()); 1570 1571 fIsTrash = FSIsTrashDir(&entry); 1572 fInTrash = FSInTrashDir(&ref); 1573 1574 if (wasInTrash ^ (IsTrash() || InTrash()) 1575 || isRoot != PoseView()->TargetModel()->IsRoot()) 1576 RepopulateMenus(); 1577 1578 // Update Navigation bar 1579 if (Navigator()) { 1580 int32 action = kActionSet; 1581 if (message->FindInt32("action", &action) != B_OK) 1582 // Design problem? Why does FindInt32 touch 1583 // 'action' at all if he can't find it?? 1584 action = kActionSet; 1585 1586 Navigator()->UpdateLocation(PoseView()->TargetModel(), 1587 action); 1588 } 1589 1590 TrackerSettings settings; 1591 if (settings.ShowNavigator() 1592 || settings.ShowFullPathInTitleBar()) { 1593 SetPathWatchingEnabled(true); 1594 } 1595 SetSingleWindowBrowseShortcuts( 1596 settings.SingleWindowBrowse()); 1597 1598 // Update draggable folder icon 1599 BView* view = FindView("MenuBar"); 1600 if (view != NULL) { 1601 view = view->FindView("ThisContainer"); 1602 if (view != NULL) { 1603 IconCache::sIconCache->IconChanged(TargetModel()); 1604 view->Invalidate(); 1605 } 1606 } 1607 1608 // Update window title 1609 UpdateTitle(); 1610 } 1611 } 1612 break; 1613 } 1614 1615 case B_REFS_RECEIVED: 1616 if (Dragging()) { 1617 // ref in this message is the target, 1618 // the end point of the drag 1619 1620 entry_ref ref; 1621 if (message->FindRef("refs", &ref) == B_OK) { 1622 fWaitingForRefs = false; 1623 BEntry entry(&ref, true); 1624 // don't copy to printers dir 1625 if (!FSIsPrintersDir(&entry)) { 1626 if (entry.InitCheck() == B_OK 1627 && entry.IsDirectory()) { 1628 Model targetModel(&entry, true, false); 1629 BPoint dropPoint; 1630 uint32 buttons; 1631 PoseView()->GetMouse(&dropPoint, &buttons, true); 1632 PoseView()->HandleDropCommon(fDragMessage, 1633 &targetModel, NULL, PoseView(), dropPoint); 1634 } 1635 } 1636 } 1637 DragStop(); 1638 } 1639 break; 1640 1641 case B_OBSERVER_NOTICE_CHANGE: 1642 { 1643 int32 observerWhat; 1644 if (message->FindInt32("be:observe_change_what", &observerWhat) 1645 == B_OK) { 1646 TrackerSettings settings; 1647 switch (observerWhat) { 1648 case kWindowsShowFullPathChanged: 1649 UpdateTitle(); 1650 if (!IsPathWatchingEnabled() 1651 && settings.ShowFullPathInTitleBar()) { 1652 SetPathWatchingEnabled(true); 1653 } 1654 if (IsPathWatchingEnabled() 1655 && !(settings.ShowNavigator() 1656 || settings.ShowFullPathInTitleBar())) { 1657 SetPathWatchingEnabled(false); 1658 } 1659 break; 1660 1661 case kSingleWindowBrowseChanged: 1662 if (settings.SingleWindowBrowse() 1663 && !Navigator() 1664 && TargetModel()->IsDirectory() 1665 && !PoseView()->IsFilePanel() 1666 && !PoseView()->IsDesktopWindow()) { 1667 BRect rect(Bounds()); 1668 rect.top = KeyMenuBar()->Bounds().Height() + 1; 1669 rect.bottom = rect.top 1670 + BNavigator::CalcNavigatorHeight(); 1671 fNavigator = new BNavigator(TargetModel(), rect); 1672 fNavigator->Hide(); 1673 AddChild(fNavigator); 1674 SetPathWatchingEnabled(settings.ShowNavigator() 1675 || settings.ShowFullPathInTitleBar()); 1676 } 1677 1678 if (!settings.SingleWindowBrowse() 1679 && dynamic_cast<BDeskWindow*>(this) == NULL 1680 && TargetModel()->IsDesktop()) { 1681 // close the "Desktop" window, but not the Desktop 1682 this->Quit(); 1683 } 1684 1685 SetSingleWindowBrowseShortcuts( 1686 settings.SingleWindowBrowse()); 1687 break; 1688 1689 case kShowNavigatorChanged: 1690 ShowNavigator(settings.ShowNavigator()); 1691 if (!IsPathWatchingEnabled() 1692 && settings.ShowNavigator()) { 1693 SetPathWatchingEnabled(true); 1694 } 1695 if (IsPathWatchingEnabled() 1696 && !(settings.ShowNavigator() 1697 || settings.ShowFullPathInTitleBar())) { 1698 SetPathWatchingEnabled(false); 1699 } 1700 SetSingleWindowBrowseShortcuts( 1701 settings.SingleWindowBrowse()); 1702 break; 1703 1704 case kDontMoveFilesToTrashChanged: 1705 { 1706 bool dontMoveToTrash 1707 = settings.DontMoveFilesToTrash(); 1708 1709 BMenuItem* item 1710 = fFileContextMenu->FindItem(kMoveToTrash); 1711 if (item != NULL) { 1712 item->SetLabel(dontMoveToTrash 1713 ? B_TRANSLATE("Delete") 1714 : B_TRANSLATE("Move to Trash")); 1715 } 1716 // Deskbar doesn't have a menu bar, so check if 1717 // there is fMenuBar 1718 if (fMenuBar && fFileMenu) { 1719 item = fFileMenu->FindItem(kMoveToTrash); 1720 if (item) { 1721 item->SetLabel(dontMoveToTrash 1722 ? B_TRANSLATE("Delete") 1723 : B_TRANSLATE("Move to Trash")); 1724 } 1725 } 1726 UpdateIfNeeded(); 1727 break; 1728 } 1729 1730 default: 1731 _inherited::MessageReceived(message); 1732 break; 1733 } 1734 } 1735 break; 1736 } 1737 1738 case B_NODE_MONITOR: 1739 UpdateTitle(); 1740 break; 1741 1742 case B_UNDO: 1743 FSUndo(); 1744 break; 1745 1746 //case B_REDO: 1747 // only defined in Dano/Zeta/OpenBeOS 1748 case kRedo: 1749 FSRedo(); 1750 break; 1751 1752 default: 1753 _inherited::MessageReceived(message); 1754 break; 1755 } 1756 } 1757 1758 1759 void 1760 BContainerWindow::SetCutItem(BMenu* menu) 1761 { 1762 BMenuItem* item; 1763 if ((item = menu->FindItem(B_CUT)) == NULL 1764 && (item = menu->FindItem(kCutMoreSelectionToClipboard)) == NULL) 1765 return; 1766 1767 item->SetEnabled(PoseView()->SelectionList()->CountItems() > 0 1768 || PoseView() != CurrentFocus()); 1769 1770 if (modifiers() & B_SHIFT_KEY) { 1771 item->SetLabel(B_TRANSLATE("Cut more")); 1772 item->SetShortcut('X', B_COMMAND_KEY | B_SHIFT_KEY); 1773 item->SetMessage(new BMessage(kCutMoreSelectionToClipboard)); 1774 } else { 1775 item->SetLabel(B_TRANSLATE("Cut")); 1776 item->SetShortcut('X', B_COMMAND_KEY); 1777 item->SetMessage(new BMessage(B_CUT)); 1778 } 1779 } 1780 1781 1782 void 1783 BContainerWindow::SetCopyItem(BMenu* menu) 1784 { 1785 BMenuItem* item; 1786 if ((item = menu->FindItem(B_COPY)) == NULL 1787 && (item = menu->FindItem(kCopyMoreSelectionToClipboard)) == NULL) { 1788 return; 1789 } 1790 1791 item->SetEnabled(PoseView()->SelectionList()->CountItems() > 0 1792 || PoseView() != CurrentFocus()); 1793 1794 if (modifiers() & B_SHIFT_KEY) { 1795 item->SetLabel(B_TRANSLATE("Copy more")); 1796 item->SetShortcut('C', B_COMMAND_KEY | B_SHIFT_KEY); 1797 item->SetMessage(new BMessage(kCopyMoreSelectionToClipboard)); 1798 } else { 1799 item->SetLabel(B_TRANSLATE("Copy")); 1800 item->SetShortcut('C', B_COMMAND_KEY); 1801 item->SetMessage(new BMessage(B_COPY)); 1802 } 1803 } 1804 1805 1806 void 1807 BContainerWindow::SetPasteItem(BMenu* menu) 1808 { 1809 BMenuItem* item; 1810 if ((item = menu->FindItem(B_PASTE)) == NULL 1811 && (item = menu->FindItem(kPasteLinksFromClipboard)) == NULL) { 1812 return; 1813 } 1814 1815 item->SetEnabled(FSClipboardHasRefs() || PoseView() != CurrentFocus()); 1816 1817 if (modifiers() & B_SHIFT_KEY) { 1818 item->SetLabel(B_TRANSLATE("Paste links")); 1819 item->SetShortcut('V', B_COMMAND_KEY | B_SHIFT_KEY); 1820 item->SetMessage(new BMessage(kPasteLinksFromClipboard)); 1821 } else { 1822 item->SetLabel(B_TRANSLATE("Paste")); 1823 item->SetShortcut('V', B_COMMAND_KEY); 1824 item->SetMessage(new BMessage(B_PASTE)); 1825 } 1826 } 1827 1828 1829 void 1830 BContainerWindow::SetArrangeMenu(BMenu* menu) 1831 { 1832 BMenuItem* item; 1833 if ((item = menu->FindItem(kCleanup)) == NULL 1834 && (item = menu->FindItem(kCleanupAll)) == NULL) { 1835 return; 1836 } 1837 1838 item->Menu()->SetEnabled(PoseView()->CountItems() > 0 1839 && (PoseView()->ViewMode() != kListMode)); 1840 1841 BMenu* arrangeMenu; 1842 1843 if (modifiers() & B_SHIFT_KEY) { 1844 item->SetLabel(B_TRANSLATE("Clean up all")); 1845 item->SetShortcut('K', B_COMMAND_KEY | B_SHIFT_KEY); 1846 item->SetMessage(new BMessage(kCleanupAll)); 1847 arrangeMenu = item->Menu(); 1848 } else { 1849 item->SetLabel(B_TRANSLATE("Clean up")); 1850 item->SetShortcut('K', B_COMMAND_KEY); 1851 item->SetMessage(new BMessage(kCleanup)); 1852 arrangeMenu = item->Menu(); 1853 } 1854 1855 MarkArrangeByMenu(arrangeMenu); 1856 } 1857 1858 1859 void 1860 BContainerWindow::SetCloseItem(BMenu* menu) 1861 { 1862 BMenuItem* item; 1863 if ((item = menu->FindItem(B_QUIT_REQUESTED)) == NULL 1864 && (item = menu->FindItem(kCloseAllWindows)) == NULL) { 1865 return; 1866 } 1867 1868 if (modifiers() & B_SHIFT_KEY) { 1869 item->SetLabel(B_TRANSLATE("Close all")); 1870 item->SetShortcut('W', B_COMMAND_KEY | B_SHIFT_KEY); 1871 item->SetTarget(be_app); 1872 item->SetMessage(new BMessage(kCloseAllWindows)); 1873 } else { 1874 item->SetLabel(B_TRANSLATE("Close")); 1875 item->SetShortcut('W', B_COMMAND_KEY); 1876 item->SetTarget(this); 1877 item->SetMessage(new BMessage(B_QUIT_REQUESTED)); 1878 } 1879 } 1880 1881 1882 bool 1883 BContainerWindow::IsShowing(const node_ref* node) const 1884 { 1885 return PoseView()->Represents(node); 1886 } 1887 1888 1889 bool 1890 BContainerWindow::IsShowing(const entry_ref* entry) const 1891 { 1892 return PoseView()->Represents(entry); 1893 } 1894 1895 1896 void 1897 BContainerWindow::AddMenus() 1898 { 1899 fFileMenu = new BMenu(B_TRANSLATE("File")); 1900 AddFileMenu(fFileMenu); 1901 fMenuBar->AddItem(fFileMenu); 1902 fWindowMenu = new BMenu(B_TRANSLATE("Window")); 1903 fMenuBar->AddItem(fWindowMenu); 1904 AddWindowMenu(fWindowMenu); 1905 // just create the attribute, decide to add it later 1906 fAttrMenu = new BMenu(B_TRANSLATE("Attributes")); 1907 NewAttributeMenu(fAttrMenu); 1908 PopulateArrangeByMenu(fArrangeByMenu); 1909 } 1910 1911 1912 void 1913 BContainerWindow::AddFileMenu(BMenu* menu) 1914 { 1915 if (!PoseView()->IsFilePanel()) { 1916 menu->AddItem(new BMenuItem(B_TRANSLATE("Find" B_UTF8_ELLIPSIS), 1917 new BMessage(kFindButton), 'F')); 1918 } 1919 1920 if (!TargetModel()->IsQuery() && !TargetModel()->IsVirtualDirectory() 1921 && !IsTrash() && !IsPrintersDir()) { 1922 if (!PoseView()->IsFilePanel()) { 1923 TemplatesMenu* templateMenu = new TemplatesMenu(PoseView(), 1924 B_TRANSLATE("New")); 1925 menu->AddItem(templateMenu); 1926 templateMenu->SetTargetForItems(PoseView()); 1927 } else { 1928 menu->AddItem(new BMenuItem(B_TRANSLATE("New folder"), 1929 new BMessage(kNewFolder), 'N')); 1930 } 1931 } 1932 menu->AddSeparatorItem(); 1933 1934 menu->AddItem(new BMenuItem(B_TRANSLATE("Open"), 1935 new BMessage(kOpenSelection), 'O')); 1936 menu->AddItem(new BMenuItem(B_TRANSLATE("Get info"), 1937 new BMessage(kGetInfo), 'I')); 1938 menu->AddItem(new BMenuItem(B_TRANSLATE("Edit name"), 1939 new BMessage(kEditItem), 'E')); 1940 1941 if (IsTrash() || InTrash()) { 1942 menu->AddItem(new BMenuItem(B_TRANSLATE("Restore"), 1943 new BMessage(kRestoreFromTrash))); 1944 if (IsTrash()) { 1945 // add as first item in menu 1946 menu->AddItem(new BMenuItem(B_TRANSLATE("Empty Trash"), 1947 new BMessage(kEmptyTrash)), 0); 1948 menu->AddItem(new BSeparatorItem(), 1); 1949 } 1950 } else if (IsPrintersDir()) { 1951 menu->AddItem(new BMenuItem(B_TRANSLATE("Add printer" B_UTF8_ELLIPSIS), 1952 new BMessage(kAddPrinter), 'N'), 0); 1953 menu->AddItem(new BSeparatorItem(), 1); 1954 menu->AddItem(new BMenuItem(B_TRANSLATE("Make active printer"), 1955 new BMessage(kMakeActivePrinter))); 1956 } else { 1957 menu->AddItem(new BMenuItem(B_TRANSLATE("Duplicate"), 1958 new BMessage(kDuplicateSelection), 'D')); 1959 1960 menu->AddItem(new BMenuItem(TrackerSettings().DontMoveFilesToTrash() 1961 ? B_TRANSLATE("Delete") : B_TRANSLATE("Move to Trash"), 1962 new BMessage(kMoveToTrash), 'T')); 1963 1964 menu->AddSeparatorItem(); 1965 1966 // The "Move To", "Copy To", "Create Link" menus are inserted 1967 // at this place, have a look at: 1968 // BContainerWindow::SetupMoveCopyMenus() 1969 } 1970 1971 BMenuItem* cutItem = NULL,* copyItem = NULL,* pasteItem = NULL; 1972 if (!IsPrintersDir()) { 1973 menu->AddSeparatorItem(); 1974 1975 menu->AddItem(cutItem = new BMenuItem(B_TRANSLATE("Cut"), 1976 new BMessage(B_CUT), 'X')); 1977 menu->AddItem(copyItem = new BMenuItem(B_TRANSLATE("Copy"), 1978 new BMessage(B_COPY), 'C')); 1979 menu->AddItem(pasteItem = new BMenuItem(B_TRANSLATE("Paste"), 1980 new BMessage(B_PASTE), 'V')); 1981 1982 menu->AddSeparatorItem(); 1983 1984 menu->AddItem(new BMenuItem(B_TRANSLATE("Identify"), 1985 new BMessage(kIdentifyEntry))); 1986 BMenu* addOnMenuItem = new BMenu(B_TRANSLATE("Add-ons")); 1987 addOnMenuItem->SetFont(be_plain_font); 1988 menu->AddItem(addOnMenuItem); 1989 } 1990 1991 menu->SetTargetForItems(PoseView()); 1992 if (cutItem) 1993 cutItem->SetTarget(this); 1994 1995 if (copyItem) 1996 copyItem->SetTarget(this); 1997 1998 if (pasteItem) 1999 pasteItem->SetTarget(this); 2000 } 2001 2002 2003 void 2004 BContainerWindow::AddWindowMenu(BMenu* menu) 2005 { 2006 BMenuItem* item; 2007 2008 BMenu* iconSizeMenu = new BMenu(B_TRANSLATE("Icon view")); 2009 2010 BMessage* message = new BMessage(kIconMode); 2011 message->AddInt32("size", 32); 2012 item = new BMenuItem(B_TRANSLATE("32 x 32"), message); 2013 item->SetTarget(PoseView()); 2014 iconSizeMenu->AddItem(item); 2015 2016 message = new BMessage(kIconMode); 2017 message->AddInt32("size", 40); 2018 item = new BMenuItem(B_TRANSLATE("40 x 40"), message); 2019 item->SetTarget(PoseView()); 2020 iconSizeMenu->AddItem(item); 2021 2022 message = new BMessage(kIconMode); 2023 message->AddInt32("size", 48); 2024 item = new BMenuItem(B_TRANSLATE("48 x 48"), message); 2025 item->SetTarget(PoseView()); 2026 iconSizeMenu->AddItem(item); 2027 2028 message = new BMessage(kIconMode); 2029 message->AddInt32("size", 64); 2030 item = new BMenuItem(B_TRANSLATE("64 x 64"), message); 2031 item->SetTarget(PoseView()); 2032 iconSizeMenu->AddItem(item); 2033 2034 message = new BMessage(kIconMode); 2035 message->AddInt32("size", 96); 2036 item = new BMenuItem(B_TRANSLATE("96 x 96"), message); 2037 item->SetMarked(PoseView()->IconSizeInt() == 96); 2038 item->SetTarget(PoseView()); 2039 iconSizeMenu->AddItem(item); 2040 2041 message = new BMessage(kIconMode); 2042 message->AddInt32("size", 128); 2043 item = new BMenuItem(B_TRANSLATE("128 x 128"), message); 2044 item->SetMarked(PoseView()->IconSizeInt() == 128); 2045 item->SetTarget(PoseView()); 2046 iconSizeMenu->AddItem(item); 2047 2048 iconSizeMenu->AddSeparatorItem(); 2049 2050 message = new BMessage(kIconMode); 2051 message->AddInt32("scale", 0); 2052 item = new BMenuItem(B_TRANSLATE("Decrease size"), message, '-'); 2053 item->SetTarget(PoseView()); 2054 iconSizeMenu->AddItem(item); 2055 2056 message = new BMessage(kIconMode); 2057 message->AddInt32("scale", 1); 2058 item = new BMenuItem(B_TRANSLATE("Increase size"), message, '+'); 2059 item->SetTarget(PoseView()); 2060 iconSizeMenu->AddItem(item); 2061 2062 // A sub menu where the super item can be invoked. 2063 menu->AddItem(iconSizeMenu); 2064 iconSizeMenu->Superitem()->SetShortcut('1', B_COMMAND_KEY); 2065 iconSizeMenu->Superitem()->SetMessage(new BMessage(kIconMode)); 2066 iconSizeMenu->Superitem()->SetTarget(PoseView()); 2067 2068 item = new BMenuItem(B_TRANSLATE("Mini icon view"), 2069 new BMessage(kMiniIconMode), '2'); 2070 item->SetTarget(PoseView()); 2071 menu->AddItem(item); 2072 2073 item = new BMenuItem(B_TRANSLATE("List view"), 2074 new BMessage(kListMode), '3'); 2075 item->SetTarget(PoseView()); 2076 menu->AddItem(item); 2077 2078 menu->AddSeparatorItem(); 2079 2080 item = new BMenuItem(B_TRANSLATE("Resize to fit"), 2081 new BMessage(kResizeToFit), 'Y'); 2082 item->SetTarget(this); 2083 menu->AddItem(item); 2084 2085 fArrangeByMenu = new BMenu(B_TRANSLATE("Arrange by")); 2086 menu->AddItem(fArrangeByMenu); 2087 2088 item = new BMenuItem(B_TRANSLATE("Select" B_UTF8_ELLIPSIS), 2089 new BMessage(kShowSelectionWindow), 'A', B_SHIFT_KEY); 2090 item->SetTarget(PoseView()); 2091 menu->AddItem(item); 2092 2093 item = new BMenuItem(B_TRANSLATE("Select all"), 2094 new BMessage(B_SELECT_ALL), 'A'); 2095 item->SetTarget(PoseView()); 2096 menu->AddItem(item); 2097 2098 item = new BMenuItem(B_TRANSLATE("Invert selection"), 2099 new BMessage(kInvertSelection), 'S'); 2100 item->SetTarget(PoseView()); 2101 menu->AddItem(item); 2102 2103 if (!IsTrash()) { 2104 item = new BMenuItem(B_TRANSLATE("Open parent"), 2105 new BMessage(kOpenParentDir), B_UP_ARROW); 2106 item->SetTarget(PoseView()); 2107 menu->AddItem(item); 2108 } 2109 2110 item = new BMenuItem(B_TRANSLATE("Close"), 2111 new BMessage(B_QUIT_REQUESTED), 'W'); 2112 item->SetTarget(this); 2113 menu->AddItem(item); 2114 2115 item = new BMenuItem(B_TRANSLATE("Close all in workspace"), 2116 new BMessage(kCloseAllInWorkspace), 'Q'); 2117 item->SetTarget(be_app); 2118 menu->AddItem(item); 2119 2120 menu->AddSeparatorItem(); 2121 2122 item = new BMenuItem(B_TRANSLATE("Preferences" B_UTF8_ELLIPSIS), 2123 new BMessage(kShowSettingsWindow)); 2124 item->SetTarget(be_app); 2125 menu->AddItem(item); 2126 } 2127 2128 2129 void 2130 BContainerWindow::AddShortcuts() 2131 { 2132 // add equivalents of the menu shortcuts to the menuless desktop window 2133 ASSERT(!IsTrash()); 2134 ASSERT(!PoseView()->IsFilePanel()); 2135 ASSERT(!TargetModel()->IsQuery()); 2136 ASSERT(!TargetModel()->IsVirtualDirectory()); 2137 2138 AddShortcut('X', B_COMMAND_KEY | B_SHIFT_KEY, 2139 new BMessage(kCutMoreSelectionToClipboard), this); 2140 AddShortcut('C', B_COMMAND_KEY | B_SHIFT_KEY, 2141 new BMessage(kCopyMoreSelectionToClipboard), this); 2142 AddShortcut('F', B_COMMAND_KEY, 2143 new BMessage(kFindButton), PoseView()); 2144 AddShortcut('N', B_COMMAND_KEY, 2145 new BMessage(kNewFolder), PoseView()); 2146 AddShortcut('O', B_COMMAND_KEY, 2147 new BMessage(kOpenSelection), PoseView()); 2148 AddShortcut('I', B_COMMAND_KEY, 2149 new BMessage(kGetInfo), PoseView()); 2150 AddShortcut('E', B_COMMAND_KEY, 2151 new BMessage(kEditItem), PoseView()); 2152 AddShortcut('D', B_COMMAND_KEY, 2153 new BMessage(kDuplicateSelection), PoseView()); 2154 AddShortcut('T', B_COMMAND_KEY, 2155 new BMessage(kMoveToTrash), PoseView()); 2156 AddShortcut('K', B_COMMAND_KEY, 2157 new BMessage(kCleanup), PoseView()); 2158 AddShortcut('A', B_COMMAND_KEY, 2159 new BMessage(B_SELECT_ALL), PoseView()); 2160 AddShortcut('S', B_COMMAND_KEY, 2161 new BMessage(kInvertSelection), PoseView()); 2162 AddShortcut('A', B_COMMAND_KEY | B_SHIFT_KEY, 2163 new BMessage(kShowSelectionWindow), PoseView()); 2164 AddShortcut('G', B_COMMAND_KEY, 2165 new BMessage(kEditQuery), PoseView()); 2166 // it is ok to add a global Edit query shortcut here, PoseView will 2167 // filter out cases where selected pose is not a query 2168 AddShortcut('U', B_COMMAND_KEY, 2169 new BMessage(kUnmountVolume), PoseView()); 2170 AddShortcut(B_UP_ARROW, B_COMMAND_KEY, 2171 new BMessage(kOpenParentDir), PoseView()); 2172 AddShortcut('O', B_COMMAND_KEY | B_CONTROL_KEY, 2173 new BMessage(kOpenSelectionWith), PoseView()); 2174 2175 BMessage* decreaseSize = new BMessage(kIconMode); 2176 decreaseSize->AddInt32("scale", 0); 2177 AddShortcut('-', B_COMMAND_KEY, decreaseSize, PoseView()); 2178 2179 BMessage* increaseSize = new BMessage(kIconMode); 2180 increaseSize->AddInt32("scale", 1); 2181 AddShortcut('+', B_COMMAND_KEY, increaseSize, PoseView()); 2182 } 2183 2184 2185 void 2186 BContainerWindow::MenusBeginning() 2187 { 2188 if (fMenuBar == NULL) 2189 return; 2190 2191 if (CurrentMessage() != NULL && CurrentMessage()->what == B_MOUSE_DOWN) { 2192 // don't commit active pose if only a keyboard shortcut is 2193 // invoked - this would prevent Cut/Copy/Paste from working 2194 fPoseView->CommitActivePose(); 2195 } 2196 2197 // File menu 2198 int32 selectCount = PoseView()->SelectionList()->CountItems(); 2199 2200 SetupOpenWithMenu(fFileMenu); 2201 SetupMoveCopyMenus(selectCount 2202 ? PoseView()->SelectionList()->FirstItem()->TargetModel()->EntryRef() 2203 : NULL, fFileMenu); 2204 2205 UpdateMenu(fMenuBar, kMenuBarContext); 2206 2207 AddMimeTypesToMenu(fAttrMenu); 2208 2209 if (IsPrintersDir()) { 2210 EnableNamedMenuItem(fFileMenu, B_TRANSLATE("Make active printer"), 2211 selectCount == 1); 2212 } 2213 } 2214 2215 2216 void 2217 BContainerWindow::MenusEnded() 2218 { 2219 // when we're done we want to clear nav menus for next time 2220 DeleteSubmenu(fNavigationItem); 2221 DeleteSubmenu(fMoveToItem); 2222 DeleteSubmenu(fCopyToItem); 2223 DeleteSubmenu(fCreateLinkItem); 2224 DeleteSubmenu(fOpenWithItem); 2225 } 2226 2227 2228 void 2229 BContainerWindow::SetupNavigationMenu(const entry_ref* ref, BMenu* parent) 2230 { 2231 // start by removing nav item (and separator) from old menu 2232 if (fNavigationItem != NULL) { 2233 BMenu* menu = fNavigationItem->Menu(); 2234 if (menu != NULL) { 2235 menu->RemoveItem(fNavigationItem); 2236 BMenuItem* item = menu->RemoveItem((int32)0); 2237 ASSERT(item != fNavigationItem); 2238 delete item; 2239 } 2240 } 2241 2242 // if we weren't passed a ref then we're navigating this window 2243 if (ref == NULL) 2244 ref = TargetModel()->EntryRef(); 2245 2246 BEntry entry; 2247 if (entry.SetTo(ref) != B_OK) 2248 return; 2249 2250 // only navigate directories and queries (check for symlink here) 2251 Model model(&entry); 2252 entry_ref resolvedRef; 2253 2254 if (model.InitCheck() != B_OK 2255 || (!model.IsContainer() && !model.IsSymLink())) { 2256 return; 2257 } 2258 2259 if (model.IsSymLink()) { 2260 if (entry.SetTo(model.EntryRef(), true) != B_OK) 2261 return; 2262 2263 Model resolvedModel(&entry); 2264 if (resolvedModel.InitCheck() != B_OK || !resolvedModel.IsContainer()) 2265 return; 2266 2267 entry.GetRef(&resolvedRef); 2268 ref = &resolvedRef; 2269 } 2270 2271 if (fNavigationItem == NULL) { 2272 fNavigationItem = new ModelMenuItem(&model, 2273 new BNavMenu(model.Name(), B_REFS_RECEIVED, be_app, this)); 2274 } 2275 2276 // setup a navigation menu item which will dynamically load items 2277 // as menu items are traversed 2278 BNavMenu* navMenu = dynamic_cast<BNavMenu*>(fNavigationItem->Submenu()); 2279 navMenu->SetNavDir(ref); 2280 fNavigationItem->SetLabel(model.Name()); 2281 fNavigationItem->SetEntry(&entry); 2282 2283 parent->AddItem(fNavigationItem, 0); 2284 parent->AddItem(new BSeparatorItem(), 1); 2285 2286 BMessage* message = new BMessage(B_REFS_RECEIVED); 2287 message->AddRef("refs", ref); 2288 fNavigationItem->SetMessage(message); 2289 fNavigationItem->SetTarget(be_app); 2290 2291 if (!Dragging()) 2292 parent->SetTrackingHook(NULL, NULL); 2293 } 2294 2295 2296 void 2297 BContainerWindow::SetUpEditQueryItem(BMenu* menu) 2298 { 2299 ASSERT(menu); 2300 // File menu 2301 int32 selectCount = PoseView()->SelectionList()->CountItems(); 2302 2303 // add Edit query if appropriate 2304 bool queryInSelection = false; 2305 if (selectCount && selectCount < 100) { 2306 // only do this for a limited number of selected poses 2307 2308 // if any queries selected, add an edit query menu item 2309 for (int32 index = 0; index < selectCount; index++) { 2310 BPose* pose = PoseView()->SelectionList()->ItemAt(index); 2311 Model model(pose->TargetModel()->EntryRef(), true); 2312 if (model.InitCheck() != B_OK) 2313 continue; 2314 2315 if (model.IsQuery() || model.IsQueryTemplate()) { 2316 queryInSelection = true; 2317 break; 2318 } 2319 } 2320 } 2321 2322 bool poseViewIsQuery = TargetModel()->IsQuery(); 2323 // if the view is a query pose view, add edit query menu item 2324 2325 BMenuItem* item = menu->FindItem(kEditQuery); 2326 if (!poseViewIsQuery && !queryInSelection && item != NULL) 2327 item->Menu()->RemoveItem(item); 2328 else if ((poseViewIsQuery || queryInSelection) && item == NULL) { 2329 // add edit query item after Open 2330 item = menu->FindItem(kOpenSelection); 2331 if (item) { 2332 int32 itemIndex = item->Menu()->IndexOf(item); 2333 BMenuItem* query = new BMenuItem(B_TRANSLATE("Edit query"), 2334 new BMessage(kEditQuery), 'G'); 2335 item->Menu()->AddItem(query, itemIndex + 1); 2336 query->SetTarget(PoseView()); 2337 } 2338 } 2339 } 2340 2341 2342 void 2343 BContainerWindow::SetupOpenWithMenu(BMenu* parent) 2344 { 2345 // start by removing nav item (and separator) from old menu 2346 if (fOpenWithItem) { 2347 BMenu* menu = fOpenWithItem->Menu(); 2348 if (menu != NULL) 2349 menu->RemoveItem(fOpenWithItem); 2350 2351 delete fOpenWithItem; 2352 fOpenWithItem = 0; 2353 } 2354 2355 if (PoseView()->SelectionList()->CountItems() == 0) { 2356 // no selection, nothing to open 2357 return; 2358 } 2359 2360 if (TargetModel()->IsRoot()) { 2361 // don't add ourselves if we are root 2362 return; 2363 } 2364 2365 // ToDo: 2366 // check if only item in selection list is the root 2367 // and do not add if true 2368 2369 // add after "Open" 2370 BMenuItem* item = parent->FindItem(kOpenSelection); 2371 2372 int32 count = PoseView()->SelectionList()->CountItems(); 2373 if (count == 0) 2374 return; 2375 2376 // build a list of all refs to open 2377 BMessage message(B_REFS_RECEIVED); 2378 for (int32 index = 0; index < count; index++) { 2379 BPose* pose = PoseView()->SelectionList()->ItemAt(index); 2380 message.AddRef("refs", pose->TargetModel()->EntryRef()); 2381 } 2382 2383 // add Tracker token so that refs received recipients can script us 2384 message.AddMessenger("TrackerViewToken", BMessenger(PoseView())); 2385 2386 int32 index = item->Menu()->IndexOf(item); 2387 fOpenWithItem = new BMenuItem( 2388 new OpenWithMenu(B_TRANSLATE("Open with" B_UTF8_ELLIPSIS), 2389 &message, this, be_app), new BMessage(kOpenSelectionWith)); 2390 fOpenWithItem->SetTarget(PoseView()); 2391 fOpenWithItem->SetShortcut('O', B_COMMAND_KEY | B_CONTROL_KEY); 2392 2393 item->Menu()->AddItem(fOpenWithItem, index + 1); 2394 } 2395 2396 2397 void 2398 BContainerWindow::PopulateMoveCopyNavMenu(BNavMenu* navMenu, uint32 what, 2399 const entry_ref* ref, bool addLocalOnly) 2400 { 2401 BVolume volume; 2402 BVolumeRoster volumeRoster; 2403 BDirectory directory; 2404 BEntry entry; 2405 BPath path; 2406 Model model; 2407 dev_t device = ref->device; 2408 2409 int32 volumeCount = 0; 2410 2411 navMenu->RemoveItems(0, navMenu->CountItems(), true); 2412 2413 // count persistent writable volumes 2414 volumeRoster.Rewind(); 2415 while (volumeRoster.GetNextVolume(&volume) == B_OK) 2416 if (!volume.IsReadOnly() && volume.IsPersistent()) 2417 volumeCount++; 2418 2419 // add the current folder 2420 if (entry.SetTo(ref) == B_OK 2421 && entry.GetParent(&entry) == B_OK 2422 && model.SetTo(&entry) == B_OK) { 2423 BNavMenu* menu = new BNavMenu(B_TRANSLATE("Current folder"), what, 2424 this); 2425 menu->SetNavDir(model.EntryRef()); 2426 menu->SetShowParent(true); 2427 2428 BMenuItem* item = new SpecialModelMenuItem(&model,menu); 2429 item->SetMessage(new BMessage((uint32)what)); 2430 2431 navMenu->AddItem(item); 2432 } 2433 2434 // add the recent folder menu 2435 // the "Tracker" settings directory is only used to get its icon 2436 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) { 2437 path.Append("Tracker"); 2438 if (entry.SetTo(path.Path()) == B_OK 2439 && model.SetTo(&entry) == B_OK) { 2440 BMenu* menu = new RecentsMenu(B_TRANSLATE("Recent folders"), 2441 kRecentFolders, what, this); 2442 2443 BMenuItem* item = new SpecialModelMenuItem(&model,menu); 2444 item->SetMessage(new BMessage((uint32)what)); 2445 2446 navMenu->AddItem(item); 2447 } 2448 } 2449 2450 // add Desktop 2451 FSGetBootDeskDir(&directory); 2452 if (directory.InitCheck() == B_OK 2453 && directory.GetEntry(&entry) == B_OK 2454 && model.SetTo(&entry) == B_OK) 2455 navMenu->AddNavDir(&model, what, this, true); 2456 // ask NavMenu to populate submenu for us 2457 2458 // add the home dir 2459 if (find_directory(B_USER_DIRECTORY, &path) == B_OK 2460 && entry.SetTo(path.Path()) == B_OK 2461 && model.SetTo(&entry) == B_OK) 2462 navMenu->AddNavDir(&model, what, this, true); 2463 2464 navMenu->AddSeparatorItem(); 2465 2466 // either add all mounted volumes (for copy), or all the top-level 2467 // directories from the same device (for move) 2468 // ToDo: can be changed if cross-device moves are implemented 2469 2470 if (addLocalOnly || volumeCount < 2) { 2471 // add volume this item lives on 2472 if (volume.SetTo(device) == B_OK 2473 && volume.GetRootDirectory(&directory) == B_OK 2474 && directory.GetEntry(&entry) == B_OK 2475 && model.SetTo(&entry) == B_OK) { 2476 navMenu->AddNavDir(&model, what, this, false); 2477 // do not have submenu populated 2478 2479 navMenu->SetNavDir(model.EntryRef()); 2480 } 2481 } else { 2482 // add all persistent writable volumes 2483 volumeRoster.Rewind(); 2484 while (volumeRoster.GetNextVolume(&volume) == B_OK) { 2485 if (volume.IsReadOnly() || !volume.IsPersistent()) 2486 continue; 2487 2488 // add root dir 2489 if (volume.GetRootDirectory(&directory) == B_OK 2490 && directory.GetEntry(&entry) == B_OK 2491 && model.SetTo(&entry) == B_OK) { 2492 navMenu->AddNavDir(&model, what, this, true); 2493 // ask NavMenu to populate submenu for us 2494 } 2495 } 2496 } 2497 } 2498 2499 2500 void 2501 BContainerWindow::SetupMoveCopyMenus(const entry_ref* item_ref, BMenu* parent) 2502 { 2503 if (IsTrash() || InTrash() || IsPrintersDir() || !fMoveToItem 2504 || !fCopyToItem || !fCreateLinkItem) { 2505 return; 2506 } 2507 2508 // Grab the modifiers state since we use it twice 2509 uint32 modifierKeys = modifiers(); 2510 2511 // re-parent items to this menu since they're shared 2512 int32 index; 2513 BMenuItem* trash = parent->FindItem(kMoveToTrash); 2514 if (trash) 2515 index = parent->IndexOf(trash) + 2; 2516 else 2517 index = 0; 2518 2519 if (fMoveToItem->Menu() != parent) { 2520 if (fMoveToItem->Menu()) 2521 fMoveToItem->Menu()->RemoveItem(fMoveToItem); 2522 2523 parent->AddItem(fMoveToItem, index++); 2524 } 2525 2526 if (fCopyToItem->Menu() != parent) { 2527 if (fCopyToItem->Menu()) 2528 fCopyToItem->Menu()->RemoveItem(fCopyToItem); 2529 2530 parent->AddItem(fCopyToItem, index++); 2531 } 2532 2533 if (fCreateLinkItem->Menu() != parent) { 2534 if (fCreateLinkItem->Menu()) 2535 fCreateLinkItem->Menu()->RemoveItem(fCreateLinkItem); 2536 2537 parent->AddItem(fCreateLinkItem, index); 2538 } 2539 2540 // Set the "Create Link" item label here so it 2541 // appears correctly when menus are disabled, too. 2542 if (modifierKeys & B_SHIFT_KEY) 2543 fCreateLinkItem->SetLabel(B_TRANSLATE("Create relative link")); 2544 else 2545 fCreateLinkItem->SetLabel(B_TRANSLATE("Create link")); 2546 2547 // only enable once the menus are built 2548 fMoveToItem->SetEnabled(false); 2549 fCopyToItem->SetEnabled(false); 2550 fCreateLinkItem->SetEnabled(false); 2551 2552 // get ref for item which is selected 2553 BEntry entry; 2554 if (entry.SetTo(item_ref) != B_OK) 2555 return; 2556 2557 Model tempModel(&entry); 2558 if (tempModel.InitCheck() != B_OK) 2559 return; 2560 2561 if (tempModel.IsRoot() || tempModel.IsVolume()) 2562 return; 2563 2564 // configure "Move to" menu item 2565 PopulateMoveCopyNavMenu(dynamic_cast<BNavMenu*>(fMoveToItem->Submenu()), 2566 kMoveSelectionTo, item_ref, true); 2567 2568 // configure "Copy to" menu item 2569 // add all mounted volumes (except the one this item lives on) 2570 PopulateMoveCopyNavMenu(dynamic_cast<BNavMenu*>(fCopyToItem->Submenu()), 2571 kCopySelectionTo, item_ref, false); 2572 2573 // Set "Create Link" menu item message and 2574 // add all mounted volumes (except the one this item lives on) 2575 if (modifierKeys & B_SHIFT_KEY) { 2576 fCreateLinkItem->SetMessage(new BMessage(kCreateRelativeLink)); 2577 PopulateMoveCopyNavMenu(dynamic_cast<BNavMenu*> 2578 (fCreateLinkItem->Submenu()), 2579 kCreateRelativeLink, item_ref, false); 2580 } else { 2581 fCreateLinkItem->SetMessage(new BMessage(kCreateLink)); 2582 PopulateMoveCopyNavMenu(dynamic_cast<BNavMenu*> 2583 (fCreateLinkItem->Submenu()), 2584 kCreateLink, item_ref, false); 2585 } 2586 2587 fMoveToItem->SetEnabled(true); 2588 fCopyToItem->SetEnabled(true); 2589 fCreateLinkItem->SetEnabled(true); 2590 2591 // Set the "Identify" item label 2592 BMenuItem* identifyItem = parent->FindItem(kIdentifyEntry); 2593 if (identifyItem != NULL) { 2594 if (modifierKeys & B_SHIFT_KEY) { 2595 identifyItem->SetLabel(B_TRANSLATE("Force identify")); 2596 identifyItem->Message()->ReplaceBool("force", true); 2597 } else { 2598 identifyItem->SetLabel(B_TRANSLATE("Identify")); 2599 identifyItem->Message()->ReplaceBool("force", false); 2600 } 2601 } 2602 } 2603 2604 2605 uint32 2606 BContainerWindow::ShowDropContextMenu(BPoint loc) 2607 { 2608 BPoint global(loc); 2609 2610 PoseView()->ConvertToScreen(&global); 2611 PoseView()->CommitActivePose(); 2612 2613 // Change the "Create Link" item - allow user to 2614 // create relative links with the Shift key down. 2615 BMenuItem* item = fDropContextMenu->FindItem(kCreateLink); 2616 if (item == NULL) 2617 item = fDropContextMenu->FindItem(kCreateRelativeLink); 2618 if (item && (modifiers() & B_SHIFT_KEY)) { 2619 item->SetLabel(B_TRANSLATE("Create relative link here")); 2620 item->SetMessage(new BMessage(kCreateRelativeLink)); 2621 } else if (item) { 2622 item->SetLabel(B_TRANSLATE("Create link here")); 2623 item->SetMessage(new BMessage(kCreateLink)); 2624 } 2625 2626 item = fDropContextMenu->Go(global, true, true); 2627 if (item) 2628 return item->Command(); 2629 2630 return 0; 2631 } 2632 2633 2634 void 2635 BContainerWindow::ShowContextMenu(BPoint loc, const entry_ref* ref, BView*) 2636 { 2637 ASSERT(IsLocked()); 2638 BPoint global(loc); 2639 PoseView()->ConvertToScreen(&global); 2640 PoseView()->CommitActivePose(); 2641 2642 if (ref) { 2643 // clicked on a pose, show file or volume context menu 2644 Model model(ref); 2645 2646 if (model.IsTrash()) { 2647 2648 if (fTrashContextMenu->Window() || Dragging()) 2649 return; 2650 2651 DeleteSubmenu(fNavigationItem); 2652 2653 // selected item was trash, show the trash context menu instead 2654 2655 EnableNamedMenuItem(fTrashContextMenu, kEmptyTrash, 2656 static_cast<TTracker*>(be_app)->TrashFull()); 2657 2658 SetupNavigationMenu(ref, fTrashContextMenu); 2659 fTrashContextMenu->Go(global, true, true, true); 2660 } else { 2661 2662 bool showAsVolume = false; 2663 bool filePanel = PoseView()->IsFilePanel(); 2664 2665 if (Dragging()) { 2666 fContextMenu = NULL; 2667 2668 BEntry entry; 2669 model.GetEntry(&entry); 2670 2671 // only show for directories (directory, volume, root) 2672 // 2673 // don't show a popup for the trash or printers 2674 // trash is handled in DeskWindow 2675 // 2676 // since this menu is opened asynchronously 2677 // we need to make sure we don't open it more 2678 // than once, the IsShowing flag is set in 2679 // SlowContextPopup::AttachedToWindow and 2680 // reset in DetachedFromWindow 2681 // see the notes in SlowContextPopup::AttachedToWindow 2682 2683 if (!FSIsPrintersDir(&entry) 2684 && !fDragContextMenu->IsShowing()) { 2685 //printf("ShowContextMenu - target is %s %i\n", 2686 // ref->name, IsShowing(ref)); 2687 fDragContextMenu->ClearMenu(); 2688 2689 // in case the ref is a symlink, resolve it 2690 // only pop open for directories 2691 BEntry resolvedEntry(ref, true); 2692 if (!resolvedEntry.IsDirectory()) 2693 return; 2694 2695 entry_ref resolvedRef; 2696 resolvedEntry.GetRef(&resolvedRef); 2697 2698 // use the resolved ref for the menu 2699 fDragContextMenu->SetNavDir(&resolvedRef); 2700 fDragContextMenu->SetTypesList(fCachedTypesList); 2701 fDragContextMenu->SetTarget(BMessenger(this)); 2702 BPoseView* poseView = PoseView(); 2703 if (poseView != NULL) { 2704 BMessenger target(poseView); 2705 fDragContextMenu->InitTrackingHook( 2706 &BPoseView::MenuTrackingHook, &target, 2707 fDragMessage); 2708 } 2709 2710 // this is now asynchronous so that we don't 2711 // deadlock in Window::Quit, 2712 fDragContextMenu->Go(global, true, false, true); 2713 } 2714 2715 return; 2716 } else if (TargetModel()->IsRoot() || model.IsVolume()) { 2717 fContextMenu = fVolumeContextMenu; 2718 showAsVolume = true; 2719 } else 2720 fContextMenu = fFileContextMenu; 2721 2722 // clean up items from last context menu 2723 2724 if (fContextMenu != NULL) { 2725 if (fContextMenu->Window()) 2726 return; 2727 else 2728 MenusEnded(); 2729 2730 if (model.InitCheck() == B_OK) { // ??? Do I need this ??? 2731 if (showAsVolume) { 2732 // non-volume enable/disable copy, move, identify 2733 EnableNamedMenuItem(fContextMenu, kDuplicateSelection, 2734 false); 2735 EnableNamedMenuItem(fContextMenu, kMoveToTrash, false); 2736 EnableNamedMenuItem(fContextMenu, kIdentifyEntry, 2737 false); 2738 2739 // volume model, enable/disable the Unmount item 2740 bool ejectableVolumeSelected = false; 2741 2742 BVolume boot; 2743 BVolumeRoster().GetBootVolume(&boot); 2744 BVolume volume; 2745 volume.SetTo(model.NodeRef()->device); 2746 if (volume != boot) 2747 ejectableVolumeSelected = true; 2748 2749 EnableNamedMenuItem(fContextMenu, 2750 B_TRANSLATE("Unmount"), ejectableVolumeSelected); 2751 } 2752 } 2753 2754 SetupNavigationMenu(ref, fContextMenu); 2755 if (!showAsVolume && !filePanel) { 2756 SetupMoveCopyMenus(ref, fContextMenu); 2757 SetupOpenWithMenu(fContextMenu); 2758 } 2759 2760 UpdateMenu(fContextMenu, kPosePopUpContext); 2761 2762 fContextMenu->Go(global, true, true, true); 2763 } 2764 } 2765 } else if (fWindowContextMenu != NULL) { 2766 if (fWindowContextMenu->Window()) 2767 return; 2768 2769 // Repopulate desktop menu if IsDesktop 2770 if (dynamic_cast<BDeskWindow*>(this)) 2771 RepopulateMenus(); 2772 2773 MenusEnded(); 2774 2775 // clicked on a window, show window context menu 2776 2777 SetupNavigationMenu(ref, fWindowContextMenu); 2778 UpdateMenu(fWindowContextMenu, kWindowPopUpContext); 2779 2780 fWindowContextMenu->Go(global, true, true, true); 2781 } 2782 2783 fContextMenu = NULL; 2784 } 2785 2786 2787 void 2788 BContainerWindow::AddFileContextMenus(BMenu* menu) 2789 { 2790 menu->AddItem(new BMenuItem(B_TRANSLATE("Open"), 2791 new BMessage(kOpenSelection), 'O')); 2792 menu->AddItem(new BMenuItem(B_TRANSLATE("Get info"), 2793 new BMessage(kGetInfo), 'I')); 2794 menu->AddItem(new BMenuItem(B_TRANSLATE("Edit name"), 2795 new BMessage(kEditItem), 'E')); 2796 2797 if (!IsTrash() && !InTrash() && !IsPrintersDir()) { 2798 menu->AddItem(new BMenuItem(B_TRANSLATE("Duplicate"), 2799 new BMessage(kDuplicateSelection), 'D')); 2800 } 2801 2802 if (!IsTrash() && !InTrash()) { 2803 menu->AddItem(new BMenuItem(TrackerSettings().DontMoveFilesToTrash() 2804 ? B_TRANSLATE("Delete") : B_TRANSLATE("Move to Trash"), 2805 new BMessage(kMoveToTrash), 'T')); 2806 if (!IsPrintersDir()) { 2807 // add separator for copy to/move to items (navigation items) 2808 menu->AddSeparatorItem(); 2809 } 2810 } else { 2811 menu->AddItem(new BMenuItem(B_TRANSLATE("Delete"), 2812 new BMessage(kDelete), 0)); 2813 menu->AddItem(new BMenuItem(B_TRANSLATE("Restore"), 2814 new BMessage(kRestoreFromTrash), 0)); 2815 } 2816 2817 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU 2818 menu->AddSeparatorItem(); 2819 BMenuItem* cutItem,* copyItem; 2820 menu->AddItem(cutItem = new BMenuItem(B_TRANSLATE("Cut"), 2821 new BMessage(B_CUT), 'X')); 2822 menu->AddItem(copyItem = new BMenuItem(B_TRANSLATE("Copy"), 2823 new BMessage(B_COPY), 'C')); 2824 #endif 2825 2826 menu->AddSeparatorItem(); 2827 BMessage* message = new BMessage(kIdentifyEntry); 2828 message->AddBool("force", false); 2829 menu->AddItem(new BMenuItem(B_TRANSLATE("Identify"), message)); 2830 BMenu* addOnMenuItem = new BMenu(B_TRANSLATE("Add-ons")); 2831 addOnMenuItem->SetFont(be_plain_font); 2832 menu->AddItem(addOnMenuItem); 2833 2834 // set targets as needed 2835 menu->SetTargetForItems(PoseView()); 2836 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU 2837 cutItem->SetTarget(this); 2838 copyItem->SetTarget(this); 2839 #endif 2840 } 2841 2842 2843 void 2844 BContainerWindow::AddVolumeContextMenus(BMenu* menu) 2845 { 2846 menu->AddItem(new BMenuItem(B_TRANSLATE("Open"), 2847 new BMessage(kOpenSelection), 'O')); 2848 menu->AddItem(new BMenuItem(B_TRANSLATE("Get info"), 2849 new BMessage(kGetInfo), 'I')); 2850 menu->AddItem(new BMenuItem(B_TRANSLATE("Edit name"), 2851 new BMessage(kEditItem), 'E')); 2852 2853 menu->AddSeparatorItem(); 2854 menu->AddItem(new MountMenu(B_TRANSLATE("Mount"))); 2855 2856 BMenuItem* item = new BMenuItem(B_TRANSLATE("Unmount"), 2857 new BMessage(kUnmountVolume), 'U'); 2858 item->SetEnabled(false); 2859 menu->AddItem(item); 2860 2861 menu->AddSeparatorItem(); 2862 menu->AddItem(new BMenu(B_TRANSLATE("Add-ons"))); 2863 2864 menu->SetTargetForItems(PoseView()); 2865 } 2866 2867 2868 void 2869 BContainerWindow::AddWindowContextMenus(BMenu* menu) 2870 { 2871 // create context sensitive menu for empty area of window 2872 // since we check view mode before display, this should be a radio 2873 // mode menu 2874 2875 bool needSeparator = true; 2876 if (IsTrash()) { 2877 menu->AddItem(new BMenuItem(B_TRANSLATE("Empty Trash"), 2878 new BMessage(kEmptyTrash))); 2879 } else if (IsPrintersDir()) { 2880 menu->AddItem(new BMenuItem(B_TRANSLATE("Add printer" B_UTF8_ELLIPSIS), 2881 new BMessage(kAddPrinter), 'N')); 2882 } else if (InTrash()) 2883 needSeparator = false; 2884 else { 2885 TemplatesMenu* templateMenu = new TemplatesMenu(PoseView(), 2886 B_TRANSLATE("New")); 2887 menu->AddItem(templateMenu); 2888 templateMenu->SetTargetForItems(PoseView()); 2889 templateMenu->SetFont(be_plain_font); 2890 } 2891 2892 if (needSeparator) 2893 menu->AddSeparatorItem(); 2894 2895 #if 0 2896 BMenuItem* pasteItem = new BMenuItem("Paste", new BMessage(B_PASTE), 'V'); 2897 menu->AddItem(pasteItem); 2898 menu->AddSeparatorItem(); 2899 #endif 2900 BMenu* arrangeBy = new BMenu(B_TRANSLATE("Arrange by")); 2901 PopulateArrangeByMenu(arrangeBy); 2902 2903 menu->AddItem(arrangeBy); 2904 2905 menu->AddItem(new BMenuItem(B_TRANSLATE("Select" B_UTF8_ELLIPSIS), 2906 new BMessage(kShowSelectionWindow), 'A', B_SHIFT_KEY)); 2907 menu->AddItem(new BMenuItem(B_TRANSLATE("Select all"), 2908 new BMessage(B_SELECT_ALL), 'A')); 2909 if (!IsTrash()) { 2910 menu->AddItem(new BMenuItem(B_TRANSLATE("Open parent"), 2911 new BMessage(kOpenParentDir), B_UP_ARROW)); 2912 } 2913 2914 menu->AddSeparatorItem(); 2915 BMenu* addOnMenuItem = new BMenu(B_TRANSLATE("Add-ons")); 2916 addOnMenuItem->SetFont(be_plain_font); 2917 menu->AddItem(addOnMenuItem); 2918 2919 #if DEBUG 2920 menu->AddSeparatorItem(); 2921 BMenuItem* testing = new BMenuItem("Test icon cache", new BMessage(kTestIconCache)); 2922 menu->AddItem(testing); 2923 #endif 2924 2925 // target items as needed 2926 menu->SetTargetForItems(PoseView()); 2927 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU 2928 pasteItem->SetTarget(this); 2929 #endif 2930 } 2931 2932 2933 void 2934 BContainerWindow::AddDropContextMenus(BMenu* menu) 2935 { 2936 menu->AddItem(new BMenuItem(B_TRANSLATE("Create link here"), 2937 new BMessage(kCreateLink))); 2938 menu->AddItem(new BMenuItem(B_TRANSLATE("Move here"), 2939 new BMessage(kMoveSelectionTo))); 2940 menu->AddItem(new BMenuItem(B_TRANSLATE("Copy here"), 2941 new BMessage(kCopySelectionTo))); 2942 menu->AddSeparatorItem(); 2943 menu->AddItem(new BMenuItem(B_TRANSLATE("Cancel"), 2944 new BMessage(kCancelButton))); 2945 } 2946 2947 2948 void 2949 BContainerWindow::AddTrashContextMenus(BMenu* menu) 2950 { 2951 // setup special trash context menu 2952 menu->AddItem(new BMenuItem(B_TRANSLATE("Empty Trash"), 2953 new BMessage(kEmptyTrash))); 2954 menu->AddItem(new BMenuItem(B_TRANSLATE("Open"), 2955 new BMessage(kOpenSelection), 'O')); 2956 menu->AddItem(new BMenuItem(B_TRANSLATE("Get info"), 2957 new BMessage(kGetInfo), 'I')); 2958 menu->SetTargetForItems(PoseView()); 2959 } 2960 2961 2962 void 2963 BContainerWindow::EachAddon(bool (*eachAddon)(const Model*, const char*, 2964 uint32 shortcut, uint32 modifiers, bool primary, void* context), 2965 void* passThru, BObjectList<BString> &mimeTypes) 2966 { 2967 AutoLock<LockingList<AddonShortcut> > lock(fAddonsList); 2968 if (lock.IsLocked()) { 2969 for (int i = fAddonsList->CountItems() - 1; i >= 0; i--) { 2970 struct AddonShortcut* item = fAddonsList->ItemAt(i); 2971 bool primary = false; 2972 2973 if (mimeTypes.CountItems()) { 2974 BFile file(item->model->EntryRef(), B_READ_ONLY); 2975 if (file.InitCheck() == B_OK) { 2976 BAppFileInfo info(&file); 2977 if (info.InitCheck() == B_OK) { 2978 bool secondary = true; 2979 2980 // does this add-on has types set at all? 2981 BMessage message; 2982 if (info.GetSupportedTypes(&message) == B_OK) { 2983 type_code type; 2984 int32 count; 2985 if (message.GetInfo("types", &type, 2986 &count) == B_OK) 2987 secondary = false; 2988 } 2989 2990 // check all supported types if it has some set 2991 if (!secondary) { 2992 for (int32 i = mimeTypes.CountItems(); 2993 !primary && i-- > 0;) { 2994 BString* type = mimeTypes.ItemAt(i); 2995 if (info.IsSupportedType(type->String())) { 2996 BMimeType mimeType(type->String()); 2997 if (info.Supports(&mimeType)) 2998 primary = true; 2999 else 3000 secondary = true; 3001 } 3002 } 3003 } 3004 3005 if (!secondary && !primary) 3006 continue; 3007 } 3008 } 3009 } 3010 ((eachAddon)(item->model, item->model->Name(), item->key, 3011 item->modifiers, primary, passThru)); 3012 } 3013 } 3014 } 3015 3016 3017 void 3018 BContainerWindow::BuildMimeTypeList(BObjectList<BString> &mimeTypes) 3019 { 3020 int32 count = PoseView()->SelectionList()->CountItems(); 3021 if (!count) { 3022 // just add the type of the current directory 3023 AddMimeTypeString(mimeTypes, TargetModel()); 3024 } else { 3025 _UpdateSelectionMIMEInfo(); 3026 for (int32 index = 0; index < count; index++) { 3027 BPose* pose = PoseView()->SelectionList()->ItemAt(index); 3028 AddMimeTypeString(mimeTypes, pose->TargetModel()); 3029 // If it's a symlink, resolves it and add the Target's MimeType 3030 if (pose->TargetModel()->IsSymLink()) { 3031 Model* resolved = new Model( 3032 pose->TargetModel()->EntryRef(), true, true); 3033 if (resolved->InitCheck() == B_OK) { 3034 AddMimeTypeString(mimeTypes, resolved); 3035 } 3036 delete resolved; 3037 } 3038 } 3039 } 3040 } 3041 3042 3043 void 3044 BContainerWindow::BuildAddOnMenu(BMenu* menu) 3045 { 3046 BMenuItem* item = menu->FindItem(B_TRANSLATE("Add-ons")); 3047 if (menu->IndexOf(item) == 0) { 3048 // the folder of the context menu seems to be named "Add-Ons" 3049 // so we just take the last menu item, which is correct if not 3050 // build with debug option 3051 item = menu->ItemAt(menu->CountItems() - 1); 3052 } 3053 if (item == NULL) 3054 return; 3055 3056 menu = item->Submenu(); 3057 if (!menu) 3058 return; 3059 3060 menu->SetFont(be_plain_font); 3061 3062 // found the addons menu, empty it first 3063 for (;;) { 3064 item = menu->RemoveItem((int32)0); 3065 if (!item) 3066 break; 3067 delete item; 3068 } 3069 3070 BObjectList<BMenuItem> primaryList; 3071 BObjectList<BMenuItem> secondaryList; 3072 BObjectList<BString> mimeTypes(10, true); 3073 BuildMimeTypeList(mimeTypes); 3074 3075 AddOneAddonParams params; 3076 params.primaryList = &primaryList; 3077 params.secondaryList = &secondaryList; 3078 3079 // build a list of the MIME types of the selected items 3080 3081 EachAddon(AddOneAddon, ¶ms, mimeTypes); 3082 3083 primaryList.SortItems(CompareLabels); 3084 secondaryList.SortItems(CompareLabels); 3085 3086 int32 count = primaryList.CountItems(); 3087 for (int32 index = 0; index < count; index++) 3088 menu->AddItem(primaryList.ItemAt(index)); 3089 3090 if (count != 0) 3091 menu->AddSeparatorItem(); 3092 3093 count = secondaryList.CountItems(); 3094 for (int32 index = 0; index < count; index++) 3095 menu->AddItem(secondaryList.ItemAt(index)); 3096 3097 menu->SetTargetForItems(this); 3098 } 3099 3100 3101 void 3102 BContainerWindow::UpdateMenu(BMenu* menu, UpdateMenuContext context) 3103 { 3104 const int32 selectCount = PoseView()->SelectionList()->CountItems(); 3105 const int32 count = PoseView()->CountItems(); 3106 3107 if (context == kMenuBarContext) { 3108 EnableNamedMenuItem(menu, kOpenSelection, selectCount > 0); 3109 EnableNamedMenuItem(menu, kGetInfo, selectCount > 0); 3110 EnableNamedMenuItem(menu, kIdentifyEntry, selectCount > 0); 3111 EnableNamedMenuItem(menu, kMoveToTrash, selectCount > 0); 3112 EnableNamedMenuItem(menu, kRestoreFromTrash, selectCount > 0); 3113 EnableNamedMenuItem(menu, kDelete, selectCount > 0); 3114 EnableNamedMenuItem(menu, kDuplicateSelection, selectCount > 0); 3115 } 3116 3117 Model* selectedModel = NULL; 3118 if (selectCount == 1) 3119 selectedModel = PoseView()->SelectionList()->FirstItem()-> 3120 TargetModel(); 3121 3122 if (context == kMenuBarContext || context == kPosePopUpContext) { 3123 SetUpEditQueryItem(menu); 3124 EnableNamedMenuItem(menu, kEditItem, selectCount == 1 3125 && (context == kPosePopUpContext || !PoseView()->ActivePose()) 3126 && selectedModel != NULL 3127 && !selectedModel->IsDesktop() 3128 && !selectedModel->IsRoot() 3129 && !selectedModel->IsTrash() 3130 && !selectedModel->HasLocalizedName()); 3131 SetCutItem(menu); 3132 SetCopyItem(menu); 3133 SetPasteItem(menu); 3134 } 3135 3136 if (context == kMenuBarContext || context == kWindowPopUpContext) { 3137 BMenu* sizeMenu = NULL; 3138 if (BMenuItem* item = menu->FindItem(kIconMode)) { 3139 sizeMenu = item->Submenu(); 3140 } 3141 3142 uint32 viewMode = PoseView()->ViewMode(); 3143 if (sizeMenu) { 3144 if (viewMode == kIconMode) { 3145 int32 iconSize = (int32)PoseView()->IconSizeInt(); 3146 for (int32 i = 0; BMenuItem* item = sizeMenu->ItemAt(i); i++) { 3147 BMessage* message = item->Message(); 3148 if (!message) { 3149 item->SetMarked(false); 3150 continue; 3151 } 3152 int32 size; 3153 if (message->FindInt32("size", &size) < B_OK) 3154 size = -1; 3155 item->SetMarked(iconSize == size); 3156 } 3157 } else { 3158 for (int32 i = 0; BMenuItem* item = sizeMenu->ItemAt(i); i++) 3159 item->SetMarked(false); 3160 } 3161 } 3162 MarkNamedMenuItem(menu, kIconMode, viewMode == kIconMode); 3163 MarkNamedMenuItem(menu, kListMode, viewMode == kListMode); 3164 MarkNamedMenuItem(menu, kMiniIconMode, viewMode == kMiniIconMode); 3165 3166 SetCloseItem(menu); 3167 SetArrangeMenu(menu); 3168 SetPasteItem(menu); 3169 3170 BEntry entry(TargetModel()->EntryRef()); 3171 BDirectory parent; 3172 entry_ref ref; 3173 BEntry root("/"); 3174 3175 bool parentIsRoot = (entry.GetParent(&parent) == B_OK 3176 && parent.GetEntry(&entry) == B_OK 3177 && entry.GetRef(&ref) == B_OK 3178 && entry == root); 3179 3180 EnableNamedMenuItem(menu, kOpenParentDir, !TargetModel()->IsDesktop() 3181 && !TargetModel()->IsRoot() 3182 && (!parentIsRoot 3183 || TrackerSettings().SingleWindowBrowse() 3184 || TrackerSettings().ShowDisksIcon() 3185 || (modifiers() & B_CONTROL_KEY) != 0)); 3186 3187 EnableNamedMenuItem(menu, kEmptyTrash, count > 0); 3188 EnableNamedMenuItem(menu, B_SELECT_ALL, count > 0); 3189 3190 BMenuItem* item = menu->FindItem(B_TRANSLATE("New")); 3191 if (item != NULL) { 3192 TemplatesMenu* templateMenu = dynamic_cast<TemplatesMenu*> 3193 (item->Submenu()); 3194 if (templateMenu != NULL) 3195 templateMenu->UpdateMenuState(); 3196 } 3197 } 3198 3199 BuildAddOnMenu(menu); 3200 } 3201 3202 3203 void 3204 BContainerWindow::LoadAddOn(BMessage* message) 3205 { 3206 UpdateIfNeeded(); 3207 3208 entry_ref addonRef; 3209 status_t result = message->FindRef("refs", &addonRef); 3210 if (result != B_OK) { 3211 BString buffer(B_TRANSLATE("Error %error loading add-On %name.")); 3212 buffer.ReplaceFirst("%error", strerror(result)); 3213 buffer.ReplaceFirst("%name", addonRef.name); 3214 3215 BAlert* alert = new BAlert("", buffer.String(), B_TRANSLATE("Cancel"), 3216 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 3217 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 3218 alert->Go(); 3219 return; 3220 } 3221 3222 // add selected refs to message 3223 BMessage* refs = new BMessage(B_REFS_RECEIVED); 3224 3225 BObjectList<BPose>* list = PoseView()->SelectionList(); 3226 3227 int32 index = 0; 3228 BPose* pose; 3229 while ((pose = list->ItemAt(index++)) != NULL) 3230 refs->AddRef("refs", pose->TargetModel()->EntryRef()); 3231 3232 refs->AddMessenger("TrackerViewToken", BMessenger(PoseView())); 3233 3234 LaunchInNewThread("Add-on", B_NORMAL_PRIORITY, &AddOnThread, refs, 3235 addonRef, *TargetModel()->EntryRef()); 3236 } 3237 3238 3239 void 3240 BContainerWindow::_UpdateSelectionMIMEInfo() 3241 { 3242 BPose* pose; 3243 int32 index = 0; 3244 while ((pose = PoseView()->SelectionList()->ItemAt(index++)) != NULL) { 3245 BString mimeType(pose->TargetModel()->MimeType()); 3246 if (!mimeType.Length() || mimeType.ICompare(B_FILE_MIMETYPE) == 0) { 3247 pose->TargetModel()->Mimeset(true); 3248 if (pose->TargetModel()->IsSymLink()) { 3249 Model* resolved = new Model(pose->TargetModel()->EntryRef(), 3250 true, true); 3251 if (resolved->InitCheck() == B_OK) { 3252 mimeType.SetTo(resolved->MimeType()); 3253 if (!mimeType.Length() 3254 || mimeType.ICompare(B_FILE_MIMETYPE) == 0) { 3255 resolved->Mimeset(true); 3256 } 3257 } 3258 delete resolved; 3259 } 3260 } 3261 } 3262 } 3263 3264 3265 BMenuItem* 3266 BContainerWindow::NewAttributeMenuItem(const char* label, const char* name, 3267 int32 type, float width, int32 align, bool editable, bool statField) 3268 { 3269 return NewAttributeMenuItem(label, name, type, NULL, width, align, 3270 editable, statField); 3271 } 3272 3273 3274 BMenuItem* 3275 BContainerWindow::NewAttributeMenuItem(const char* label, const char* name, 3276 int32 type, const char* displayAs, float width, int32 align, 3277 bool editable, bool statField) 3278 { 3279 BMessage* message = new BMessage(kAttributeItem); 3280 message->AddString("attr_name", name); 3281 message->AddInt32("attr_type", type); 3282 message->AddInt32("attr_hash", (int32)AttrHashString(name, (uint32)type)); 3283 message->AddFloat("attr_width", width); 3284 message->AddInt32("attr_align", align); 3285 if (displayAs != NULL) 3286 message->AddString("attr_display_as", displayAs); 3287 message->AddBool("attr_editable", editable); 3288 message->AddBool("attr_statfield", statField); 3289 3290 BMenuItem* menuItem = new BMenuItem(label, message); 3291 menuItem->SetTarget(PoseView()); 3292 3293 return menuItem; 3294 } 3295 3296 3297 void 3298 BContainerWindow::NewAttributeMenu(BMenu* menu) 3299 { 3300 ASSERT(PoseView()); 3301 3302 BMenuItem* item; 3303 menu->AddItem(item = new BMenuItem(B_TRANSLATE("Copy layout"), 3304 new BMessage(kCopyAttributes))); 3305 item->SetTarget(PoseView()); 3306 menu->AddItem(item = new BMenuItem(B_TRANSLATE("Paste layout"), 3307 new BMessage(kPasteAttributes))); 3308 item->SetTarget(PoseView()); 3309 menu->AddSeparatorItem(); 3310 3311 menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Name"), 3312 kAttrStatName, B_STRING_TYPE, 145, B_ALIGN_LEFT, true, true)); 3313 3314 if (gLocalizedNamePreferred) { 3315 menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Real name"), 3316 kAttrRealName, B_STRING_TYPE, 145, B_ALIGN_LEFT, true, true)); 3317 } 3318 3319 menu->AddItem(NewAttributeMenuItem (B_TRANSLATE("Size"), kAttrStatSize, 3320 B_OFF_T_TYPE, 80, B_ALIGN_RIGHT, false, true)); 3321 3322 menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Modified"), 3323 kAttrStatModified, B_TIME_TYPE, 150, B_ALIGN_LEFT, false, true)); 3324 3325 menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Created"), 3326 kAttrStatCreated, B_TIME_TYPE, 150, B_ALIGN_LEFT, false, true)); 3327 3328 menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Kind"), 3329 kAttrMIMEType, B_MIME_STRING_TYPE, 145, B_ALIGN_LEFT, false, false)); 3330 3331 if (IsTrash() || InTrash()) { 3332 menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Original name"), 3333 kAttrOriginalPath, B_STRING_TYPE, 225, B_ALIGN_LEFT, false, 3334 false)); 3335 } else { 3336 menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Location"), kAttrPath, 3337 B_STRING_TYPE, 225, B_ALIGN_LEFT, false, false)); 3338 } 3339 3340 #ifdef OWNER_GROUP_ATTRIBUTES 3341 menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Owner"), kAttrStatOwner, 3342 B_STRING_TYPE, 60, B_ALIGN_LEFT, false, true)); 3343 3344 menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Group"), kAttrStatGroup, 3345 B_STRING_TYPE, 60, B_ALIGN_LEFT, false, true)); 3346 #endif 3347 3348 menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Permissions"), 3349 kAttrStatMode, B_STRING_TYPE, 80, B_ALIGN_LEFT, false, true)); 3350 } 3351 3352 3353 void 3354 BContainerWindow::ShowAttributeMenu() 3355 { 3356 ASSERT(fAttrMenu); 3357 fMenuBar->AddItem(fAttrMenu); 3358 } 3359 3360 3361 void 3362 BContainerWindow::HideAttributeMenu() 3363 { 3364 ASSERT(fAttrMenu); 3365 fMenuBar->RemoveItem(fAttrMenu); 3366 } 3367 3368 3369 void 3370 BContainerWindow::MarkAttributeMenu() 3371 { 3372 MarkAttributeMenu(fAttrMenu); 3373 } 3374 3375 3376 void 3377 BContainerWindow::MarkAttributeMenu(BMenu* menu) 3378 { 3379 if (!menu) 3380 return; 3381 3382 int32 count = menu->CountItems(); 3383 for (int32 index = 0; index < count; index++) { 3384 BMenuItem* item = menu->ItemAt(index); 3385 int32 attrHash; 3386 if (item->Message()) { 3387 if (item->Message()->FindInt32("attr_hash", &attrHash) == B_OK) 3388 item->SetMarked(PoseView()->ColumnFor((uint32)attrHash) != 0); 3389 else 3390 item->SetMarked(false); 3391 } 3392 3393 BMenu* submenu = item->Submenu(); 3394 if (submenu) { 3395 int32 count2 = submenu->CountItems(); 3396 for (int32 subindex = 0; subindex < count2; subindex++) { 3397 item = submenu->ItemAt(subindex); 3398 if (item->Message()) { 3399 if (item->Message()->FindInt32("attr_hash", &attrHash) 3400 == B_OK) { 3401 item->SetMarked(PoseView()->ColumnFor((uint32)attrHash) 3402 != 0); 3403 } else 3404 item->SetMarked(false); 3405 } 3406 } 3407 } 3408 } 3409 } 3410 3411 3412 void 3413 BContainerWindow::MarkArrangeByMenu(BMenu* menu) 3414 { 3415 if (!menu) 3416 return; 3417 3418 int32 count = menu->CountItems(); 3419 for (int32 index = 0; index < count; index++) { 3420 BMenuItem* item = menu->ItemAt(index); 3421 if (item->Message()) { 3422 uint32 attrHash; 3423 if (item->Message()->FindInt32("attr_hash", 3424 (int32*)&attrHash) == B_OK) { 3425 item->SetMarked(PoseView()->PrimarySort() == attrHash); 3426 } else if (item->Command() == kArrangeReverseOrder) 3427 item->SetMarked(PoseView()->ReverseSort()); 3428 } 3429 } 3430 } 3431 3432 3433 void 3434 BContainerWindow::AddMimeTypesToMenu() 3435 { 3436 AddMimeTypesToMenu(fAttrMenu); 3437 } 3438 3439 3440 // Adds a menu for a specific MIME type if it doesn't exist already. 3441 // Returns the menu, if it existed or not. 3442 BMenu* 3443 BContainerWindow::AddMimeMenu(const BMimeType& mimeType, bool isSuperType, 3444 BMenu* menu, int32 start) 3445 { 3446 AutoLock<BLooper> _(menu->Looper()); 3447 3448 if (!mimeType.IsValid()) 3449 return NULL; 3450 3451 // Check if we already have an entry for this MIME type in the menu. 3452 for (int32 i = start; BMenuItem* item = menu->ItemAt(i); i++) { 3453 BMessage* message = item->Message(); 3454 if (message == NULL) 3455 continue; 3456 3457 const char* type; 3458 if (message->FindString("mimetype", &type) == B_OK 3459 && !strcmp(mimeType.Type(), type)) { 3460 return item->Submenu(); 3461 } 3462 } 3463 3464 BMessage attrInfo; 3465 char description[B_MIME_TYPE_LENGTH]; 3466 const char* label = mimeType.Type(); 3467 3468 if (!mimeType.IsInstalled()) 3469 return NULL; 3470 3471 // only add things to menu which have "user-visible" data 3472 if (mimeType.GetAttrInfo(&attrInfo) != B_OK) 3473 return NULL; 3474 3475 if (mimeType.GetShortDescription(description) == B_OK && description[0]) 3476 label = description; 3477 3478 // go through each field in meta mime and add it to a menu 3479 BMenu* mimeMenu = NULL; 3480 if (isSuperType) { 3481 // If it is a supertype, we create the menu anyway as it may have 3482 // submenus later on. 3483 mimeMenu = new BMenu(label); 3484 BFont font; 3485 menu->GetFont(&font); 3486 mimeMenu->SetFont(&font); 3487 } 3488 3489 int32 index = -1; 3490 const char* publicName; 3491 while (attrInfo.FindString("attr:public_name", ++index, &publicName) 3492 == B_OK) { 3493 if (!attrInfo.FindBool("attr:viewable", index)) { 3494 // don't add if attribute not viewable 3495 continue; 3496 } 3497 3498 int32 type; 3499 int32 align; 3500 int32 width; 3501 bool editable; 3502 const char* attrName; 3503 if (attrInfo.FindString("attr:name", index, &attrName) != B_OK 3504 || attrInfo.FindInt32("attr:type", index, &type) != B_OK 3505 || attrInfo.FindBool("attr:editable", index, &editable) != B_OK 3506 || attrInfo.FindInt32("attr:width", index, &width) != B_OK 3507 || attrInfo.FindInt32("attr:alignment", index, &align) != B_OK) 3508 continue; 3509 3510 BString displayAs; 3511 attrInfo.FindString("attr:display_as", index, &displayAs); 3512 3513 if (mimeMenu == NULL) { 3514 // do a lazy allocation of the menu 3515 mimeMenu = new BMenu(label); 3516 BFont font; 3517 menu->GetFont(&font); 3518 mimeMenu->SetFont(&font); 3519 } 3520 mimeMenu->AddItem(NewAttributeMenuItem(publicName, attrName, type, 3521 displayAs.String(), width, align, editable, false)); 3522 } 3523 3524 if (mimeMenu == NULL) 3525 return NULL; 3526 3527 BMessage* message = new BMessage(kMIMETypeItem); 3528 message->AddString("mimetype", mimeType.Type()); 3529 menu->AddItem(new IconMenuItem(mimeMenu, message, mimeType.Type(), 3530 B_MINI_ICON)); 3531 3532 return mimeMenu; 3533 } 3534 3535 3536 void 3537 BContainerWindow::AddMimeTypesToMenu(BMenu* menu) 3538 { 3539 if (!menu) 3540 return; 3541 3542 // Remove old mime type menus 3543 int32 start = menu->CountItems(); 3544 while (start > 0 && menu->ItemAt(start - 1)->Submenu() != NULL) { 3545 delete menu->RemoveItem(start - 1); 3546 start--; 3547 } 3548 3549 // Add a separator item if there is none yet 3550 if (start > 0 3551 && dynamic_cast<BSeparatorItem*>(menu->ItemAt(start - 1)) == NULL) 3552 menu->AddSeparatorItem(); 3553 3554 // Add MIME type in case we're a default query type window 3555 BPath path; 3556 if (TargetModel() != NULL) { 3557 TargetModel()->GetPath(&path); 3558 if (path.InitCheck() == B_OK 3559 && strstr(path.Path(), "/" kQueryTemplates "/") != NULL) { 3560 // demangle MIME type name 3561 BString name(TargetModel()->Name()); 3562 name.ReplaceFirst('_', '/'); 3563 3564 PoseView()->AddMimeType(name.String()); 3565 } 3566 } 3567 3568 // Add MIME type menus 3569 3570 int32 typeCount = PoseView()->CountMimeTypes(); 3571 3572 for (int32 index = 0; index < typeCount; index++) { 3573 BMimeType mimeType(PoseView()->MimeTypeAt(index)); 3574 if (mimeType.InitCheck() == B_OK) { 3575 BMimeType superType; 3576 mimeType.GetSupertype(&superType); 3577 if (superType.InitCheck() == B_OK) { 3578 BMenu* superMenu = AddMimeMenu(superType, true, menu, start); 3579 if (superMenu != NULL) { 3580 // We have a supertype menu. 3581 AddMimeMenu(mimeType, false, superMenu, 0); 3582 } 3583 } 3584 } 3585 } 3586 3587 // remove empty super menus, promote sub-types if needed 3588 3589 for (int32 index = 0; index < typeCount; index++) { 3590 BMimeType mimeType(PoseView()->MimeTypeAt(index)); 3591 BMimeType superType; 3592 mimeType.GetSupertype(&superType); 3593 3594 BMenu* superMenu = AddMimeMenu(superType, true, menu, start); 3595 if (superMenu == NULL) 3596 continue; 3597 3598 int32 itemsFound = 0; 3599 int32 menusFound = 0; 3600 for (int32 i = 0; BMenuItem* item = superMenu->ItemAt(i); i++) { 3601 if (item->Submenu() != NULL) 3602 menusFound++; 3603 else 3604 itemsFound++; 3605 } 3606 3607 if (itemsFound == 0) { 3608 if (menusFound != 0) { 3609 // promote types to the top level 3610 while (BMenuItem* item = superMenu->RemoveItem((int32)0)) { 3611 menu->AddItem(item); 3612 } 3613 } 3614 3615 menu->RemoveItem(superMenu->Superitem()); 3616 delete superMenu->Superitem(); 3617 } 3618 } 3619 3620 // remove separator if it's the only item in menu 3621 BMenuItem* item = menu->ItemAt(menu->CountItems() - 1); 3622 if (dynamic_cast<BSeparatorItem*>(item) != NULL) { 3623 menu->RemoveItem(item); 3624 delete item; 3625 } 3626 3627 MarkAttributeMenu(menu); 3628 } 3629 3630 3631 BHandler* 3632 BContainerWindow::ResolveSpecifier(BMessage* message, int32 index, 3633 BMessage* specifier, int32 form, const char* property) 3634 { 3635 if (strcmp(property, "Poses") == 0) { 3636 // PRINT(("BContainerWindow::ResolveSpecifier %s\n", property)); 3637 message->PopSpecifier(); 3638 return PoseView(); 3639 } 3640 3641 return _inherited::ResolveSpecifier(message, index, specifier, 3642 form, property); 3643 } 3644 3645 3646 PiggybackTaskLoop* 3647 BContainerWindow::DelayedTaskLoop() 3648 { 3649 if (!fTaskLoop) 3650 fTaskLoop = new PiggybackTaskLoop; 3651 3652 return fTaskLoop; 3653 } 3654 3655 3656 bool 3657 BContainerWindow::NeedsDefaultStateSetup() 3658 { 3659 if (!TargetModel()) 3660 return false; 3661 3662 if (TargetModel()->IsRoot()) 3663 // don't try to set up anything if we are root 3664 return false; 3665 3666 WindowStateNodeOpener opener(this, false); 3667 if (opener.StreamNode() == NULL) { 3668 // can't read state, give up 3669 return false; 3670 } 3671 3672 return !NodeHasSavedState(opener.Node()); 3673 } 3674 3675 3676 bool 3677 BContainerWindow::DefaultStateSourceNode(const char* name, BNode* result, 3678 bool createNew, bool createFolder) 3679 { 3680 // PRINT(("looking for default state in tracker settings dir\n")); 3681 BPath settingsPath; 3682 if (FSFindTrackerSettingsDir(&settingsPath) != B_OK) 3683 return false; 3684 3685 BDirectory dir(settingsPath.Path()); 3686 3687 BPath path(settingsPath); 3688 path.Append(name); 3689 if (!BEntry(path.Path()).Exists()) { 3690 if (!createNew) 3691 return false; 3692 3693 BPath tmpPath(settingsPath); 3694 for (;;) { 3695 // deal with several levels of folders 3696 const char* nextSlash = strchr(name, '/'); 3697 if (!nextSlash) 3698 break; 3699 3700 BString tmp; 3701 tmp.SetTo(name, nextSlash - name); 3702 tmpPath.Append(tmp.String()); 3703 3704 mkdir(tmpPath.Path(), 0777); 3705 3706 name = nextSlash + 1; 3707 if (!name[0]) { 3708 // can't deal with a slash at end 3709 return false; 3710 } 3711 } 3712 3713 if (createFolder) { 3714 if (mkdir(path.Path(), 0777) < 0) 3715 return false; 3716 } else { 3717 BFile file; 3718 if (dir.CreateFile(name, &file) != B_OK) 3719 return false; 3720 } 3721 } 3722 3723 // PRINT(("using default state from %s\n", path.Path())); 3724 result->SetTo(path.Path()); 3725 return result->InitCheck() == B_OK; 3726 } 3727 3728 3729 void 3730 BContainerWindow::SetUpDefaultState() 3731 { 3732 BNode defaultingNode; 3733 // this is where we'll ulitimately get the state from 3734 bool gotDefaultingNode = 0; 3735 bool shouldStagger = false; 3736 3737 ASSERT(TargetModel()); 3738 3739 PRINT(("folder %s does not have any saved state\n", TargetModel()->Name())); 3740 3741 WindowStateNodeOpener opener(this, true); 3742 // this is our destination node, whatever it is for this window 3743 if (opener.StreamNode() == NULL) 3744 return; 3745 3746 if (!TargetModel()->IsRoot()) { 3747 BDirectory desktop; 3748 FSGetDeskDir(&desktop); 3749 3750 // try copying state from our parent directory, unless it is the 3751 // desktop folder 3752 BEntry entry(TargetModel()->EntryRef()); 3753 BNode parent; 3754 if (FSGetParentVirtualDirectoryAware(entry, parent) == B_OK 3755 && parent != desktop) { 3756 PRINT(("looking at parent for state\n")); 3757 if (NodeHasSavedState(&parent)) { 3758 PRINT(("got state from parent\n")); 3759 defaultingNode = parent; 3760 gotDefaultingNode = true; 3761 // when getting state from parent, stagger the window 3762 shouldStagger = true; 3763 } 3764 } 3765 } 3766 3767 if (!gotDefaultingNode 3768 // parent didn't have any state, use the template directory from 3769 // tracker settings folder for what our state should be 3770 // For simplicity we are not picking up the most recent 3771 // changes that didn't get committed if home is still open in 3772 // a window, that's probably not a problem; would be OK if state 3773 // got committed after every change 3774 && !DefaultStateSourceNode(kDefaultFolderTemplate, &defaultingNode, true)) 3775 return; 3776 3777 // copy over the attributes 3778 3779 // set up a filter of the attributes we want copied 3780 const char* allowAttrs[] = { 3781 kAttrWindowFrame, 3782 kAttrWindowWorkspace, 3783 kAttrViewState, 3784 kAttrViewStateForeign, 3785 kAttrColumns, 3786 kAttrColumnsForeign, 3787 0 3788 }; 3789 3790 // copy over attributes that apply; transform them properly, stripping 3791 // parts that do not apply, adding a window stagger, etc. 3792 3793 StaggerOneParams params; 3794 params.rectFromParent = shouldStagger; 3795 SelectiveAttributeTransformer frameOffsetter(kAttrWindowFrame, 3796 OffsetFrameOne, ¶ms); 3797 SelectiveAttributeTransformer scrollOriginCleaner(kAttrViewState, 3798 ClearViewOriginOne, ¶ms); 3799 3800 // do it 3801 AttributeStreamMemoryNode memoryNode; 3802 NamesToAcceptAttrFilter filter(allowAttrs); 3803 AttributeStreamFileNode fileNode(&defaultingNode); 3804 3805 *opener.StreamNode() << scrollOriginCleaner << frameOffsetter 3806 << memoryNode << filter << fileNode; 3807 } 3808 3809 3810 void 3811 BContainerWindow::RestoreWindowState(AttributeStreamNode* node) 3812 { 3813 if (node == NULL || dynamic_cast<BDeskWindow*>(this) != NULL) { 3814 // don't restore any window state if we are a desktop window 3815 return; 3816 } 3817 3818 const char* rectAttributeName; 3819 const char* workspaceAttributeName; 3820 if (TargetModel()->IsRoot()) { 3821 rectAttributeName = kAttrDisksFrame; 3822 workspaceAttributeName = kAttrDisksWorkspace; 3823 } else { 3824 rectAttributeName = kAttrWindowFrame; 3825 workspaceAttributeName = kAttrWindowWorkspace; 3826 } 3827 3828 BRect frame(Frame()); 3829 if (node->Read(rectAttributeName, 0, B_RECT_TYPE, sizeof(BRect), &frame) 3830 == sizeof(BRect)) { 3831 MoveTo(frame.LeftTop()); 3832 ResizeTo(frame.Width(), frame.Height()); 3833 } else 3834 sNewWindRect.OffsetBy(kWindowStaggerBy, kWindowStaggerBy); 3835 3836 fPreviousBounds = Bounds(); 3837 3838 uint32 workspace; 3839 if ((fContainerWindowFlags & kRestoreWorkspace) 3840 && node->Read(workspaceAttributeName, 0, B_INT32_TYPE, sizeof(uint32), 3841 &workspace) == sizeof(uint32)) 3842 SetWorkspaces(workspace); 3843 3844 if (fContainerWindowFlags & kIsHidden) 3845 Minimize(true); 3846 3847 #ifdef __HAIKU__ 3848 // restore window decor settings 3849 int32 size = node->Contains(kAttrWindowDecor, B_RAW_TYPE); 3850 if (size > 0) { 3851 char buffer[size]; 3852 if ((fContainerWindowFlags & kRestoreDecor) 3853 && node->Read(kAttrWindowDecor, 0, B_RAW_TYPE, size, buffer) 3854 == size) { 3855 BMessage decorSettings; 3856 if (decorSettings.Unflatten(buffer) == B_OK) 3857 SetDecoratorSettings(decorSettings); 3858 } 3859 } 3860 #endif // __HAIKU__ 3861 } 3862 3863 3864 void 3865 BContainerWindow::RestoreWindowState(const BMessage &message) 3866 { 3867 if (dynamic_cast<BDeskWindow*>(this)) { 3868 // don't restore any window state if we are a desktop window 3869 return; 3870 } 3871 3872 const char* rectAttributeName; 3873 const char* workspaceAttributeName; 3874 if (TargetModel()->IsRoot()) { 3875 rectAttributeName = kAttrDisksFrame; 3876 workspaceAttributeName = kAttrDisksWorkspace; 3877 } else { 3878 rectAttributeName = kAttrWindowFrame; 3879 workspaceAttributeName = kAttrWindowWorkspace; 3880 } 3881 3882 BRect frame(Frame()); 3883 if (message.FindRect(rectAttributeName, &frame) == B_OK) { 3884 MoveTo(frame.LeftTop()); 3885 ResizeTo(frame.Width(), frame.Height()); 3886 } else 3887 sNewWindRect.OffsetBy(kWindowStaggerBy, kWindowStaggerBy); 3888 3889 uint32 workspace; 3890 if ((fContainerWindowFlags & kRestoreWorkspace) 3891 && message.FindInt32(workspaceAttributeName, 3892 (int32*)&workspace) == B_OK) { 3893 SetWorkspaces(workspace); 3894 } 3895 3896 if (fContainerWindowFlags & kIsHidden) 3897 Minimize(true); 3898 3899 #ifdef __HAIKU__ 3900 // restore window decor settings 3901 BMessage decorSettings; 3902 if ((fContainerWindowFlags & kRestoreDecor) 3903 && message.FindMessage(kAttrWindowDecor, &decorSettings) == B_OK) { 3904 SetDecoratorSettings(decorSettings); 3905 } 3906 #endif // __HAIKU__ 3907 } 3908 3909 3910 void 3911 BContainerWindow::SaveWindowState(AttributeStreamNode* node) 3912 { 3913 ASSERT(node); 3914 const char* rectAttributeName; 3915 const char* workspaceAttributeName; 3916 if (TargetModel() && TargetModel()->IsRoot()) { 3917 rectAttributeName = kAttrDisksFrame; 3918 workspaceAttributeName = kAttrDisksWorkspace; 3919 } else { 3920 rectAttributeName = kAttrWindowFrame; 3921 workspaceAttributeName = kAttrWindowWorkspace; 3922 } 3923 3924 // node is null if it already got deleted 3925 BRect frame(Frame()); 3926 node->Write(rectAttributeName, 0, B_RECT_TYPE, sizeof(BRect), &frame); 3927 3928 uint32 workspaces = Workspaces(); 3929 node->Write(workspaceAttributeName, 0, B_INT32_TYPE, sizeof(uint32), 3930 &workspaces); 3931 3932 #ifdef __HAIKU__ 3933 BMessage decorSettings; 3934 if (GetDecoratorSettings(&decorSettings) == B_OK) { 3935 int32 size = decorSettings.FlattenedSize(); 3936 char buffer[size]; 3937 if (decorSettings.Flatten(buffer, size) == B_OK) { 3938 node->Write(kAttrWindowDecor, 0, B_RAW_TYPE, size, buffer); 3939 } 3940 } 3941 #endif // __HAIKU__ 3942 } 3943 3944 3945 void 3946 BContainerWindow::SaveWindowState(BMessage &message) const 3947 { 3948 const char* rectAttributeName; 3949 const char* workspaceAttributeName; 3950 3951 if (TargetModel() && TargetModel()->IsRoot()) { 3952 rectAttributeName = kAttrDisksFrame; 3953 workspaceAttributeName = kAttrDisksWorkspace; 3954 } else { 3955 rectAttributeName = kAttrWindowFrame; 3956 workspaceAttributeName = kAttrWindowWorkspace; 3957 } 3958 3959 // node is null if it already got deleted 3960 BRect frame(Frame()); 3961 message.AddRect(rectAttributeName, frame); 3962 message.AddInt32(workspaceAttributeName, (int32)Workspaces()); 3963 3964 #ifdef __HAIKU__ 3965 BMessage decorSettings; 3966 if (GetDecoratorSettings(&decorSettings) == B_OK) { 3967 message.AddMessage(kAttrWindowDecor, &decorSettings); 3968 } 3969 #endif // __HAIKU__ 3970 } 3971 3972 3973 status_t 3974 BContainerWindow::DragStart(const BMessage* dragMessage) 3975 { 3976 if (dragMessage == NULL) 3977 return B_ERROR; 3978 3979 // if already dragging, or 3980 // if all the refs match 3981 if (Dragging() 3982 && SpringLoadedFolderCompareMessages(dragMessage, fDragMessage)) { 3983 return B_OK; 3984 } 3985 3986 // cache the current drag message 3987 // build a list of the mimetypes in the message 3988 SpringLoadedFolderCacheDragData(dragMessage, &fDragMessage, 3989 &fCachedTypesList); 3990 3991 fWaitingForRefs = true; 3992 3993 return B_OK; 3994 } 3995 3996 3997 void 3998 BContainerWindow::DragStop() 3999 { 4000 delete fDragMessage; 4001 fDragMessage = NULL; 4002 4003 delete fCachedTypesList; 4004 fCachedTypesList = NULL; 4005 4006 fWaitingForRefs = false; 4007 } 4008 4009 4010 void 4011 BContainerWindow::ShowSelectionWindow() 4012 { 4013 if (fSelectionWindow == NULL) { 4014 fSelectionWindow = new SelectionWindow(this); 4015 fSelectionWindow->Show(); 4016 } else if (fSelectionWindow->Lock()) { 4017 // The window is already there, just bring it close 4018 fSelectionWindow->MoveCloseToMouse(); 4019 if (fSelectionWindow->IsHidden()) 4020 fSelectionWindow->Show(); 4021 4022 fSelectionWindow->Unlock(); 4023 } 4024 } 4025 4026 4027 void 4028 BContainerWindow::ShowNavigator(bool show) 4029 { 4030 if (PoseView()->IsDesktopWindow() || !TargetModel()->IsDirectory() 4031 || fPoseView->IsFilePanel()) { 4032 return; 4033 } 4034 4035 if (show) { 4036 if (Navigator() && !Navigator()->IsHidden()) 4037 return; 4038 4039 if (Navigator() == NULL) { 4040 BRect rect(Bounds()); 4041 rect.top = KeyMenuBar()->Bounds().Height() + 1; 4042 rect.bottom = rect.top + BNavigator::CalcNavigatorHeight(); 4043 fNavigator = new BNavigator(TargetModel(), rect); 4044 AddChild(fNavigator); 4045 } 4046 4047 if (Navigator()->IsHidden()) { 4048 if (Navigator()->Bounds().top == 0) 4049 Navigator()->MoveTo(0, KeyMenuBar()->Bounds().Height() + 1); 4050 // This is if the navigator was created with a .top = 0. 4051 Navigator()->Show(); 4052 } 4053 4054 float displacement = Navigator()->Frame().Height() + 1; 4055 4056 PoseView()->MoveBy(0, displacement); 4057 PoseView()->ResizeBy(0, -displacement); 4058 4059 if (PoseView()->VScrollBar()) { 4060 PoseView()->VScrollBar()->MoveBy(0, displacement); 4061 PoseView()->VScrollBar()->ResizeBy(0, -displacement); 4062 PoseView()->UpdateScrollRange(); 4063 } 4064 } else { 4065 if (!Navigator() || Navigator()->IsHidden()) 4066 return; 4067 4068 float displacement = Navigator()->Frame().Height() + 1; 4069 4070 PoseView()->ResizeBy(0, displacement); 4071 PoseView()->MoveBy(0, -displacement); 4072 4073 if (PoseView()->VScrollBar()) { 4074 PoseView()->VScrollBar()->ResizeBy(0, displacement); 4075 PoseView()->VScrollBar()->MoveBy(0, -displacement); 4076 PoseView()->UpdateScrollRange(); 4077 } 4078 4079 fNavigator->Hide(); 4080 } 4081 } 4082 4083 4084 void 4085 BContainerWindow::SetSingleWindowBrowseShortcuts(bool enabled) 4086 { 4087 if (PoseView()->IsDesktopWindow()) 4088 return; 4089 4090 if (enabled) { 4091 if (!Navigator()) 4092 return; 4093 4094 RemoveShortcut(B_DOWN_ARROW, B_COMMAND_KEY | B_OPTION_KEY); 4095 RemoveShortcut(B_UP_ARROW, B_COMMAND_KEY); 4096 RemoveShortcut(B_UP_ARROW, B_COMMAND_KEY | B_OPTION_KEY); 4097 4098 AddShortcut(B_LEFT_ARROW, B_COMMAND_KEY, 4099 new BMessage(kNavigatorCommandBackward), Navigator()); 4100 AddShortcut(B_RIGHT_ARROW, B_COMMAND_KEY, 4101 new BMessage(kNavigatorCommandForward), Navigator()); 4102 AddShortcut(B_UP_ARROW, B_COMMAND_KEY, 4103 new BMessage(kNavigatorCommandUp), Navigator()); 4104 4105 AddShortcut(B_LEFT_ARROW, B_OPTION_KEY | B_COMMAND_KEY, 4106 new BMessage(kNavigatorCommandBackward), Navigator()); 4107 AddShortcut(B_RIGHT_ARROW, B_OPTION_KEY | B_COMMAND_KEY, 4108 new BMessage(kNavigatorCommandForward), Navigator()); 4109 AddShortcut(B_UP_ARROW, B_OPTION_KEY | B_COMMAND_KEY, 4110 new BMessage(kNavigatorCommandUp), Navigator()); 4111 AddShortcut(B_DOWN_ARROW, B_OPTION_KEY | B_COMMAND_KEY, 4112 new BMessage(kOpenSelection), PoseView()); 4113 AddShortcut('L', B_COMMAND_KEY, 4114 new BMessage(kNavigatorCommandSetFocus), Navigator()); 4115 4116 } else { 4117 RemoveShortcut(B_LEFT_ARROW, B_COMMAND_KEY); 4118 RemoveShortcut(B_RIGHT_ARROW, B_COMMAND_KEY); 4119 RemoveShortcut(B_UP_ARROW, B_COMMAND_KEY); 4120 // This is added again, below, with a new meaning. 4121 4122 RemoveShortcut(B_LEFT_ARROW, B_OPTION_KEY | B_COMMAND_KEY); 4123 RemoveShortcut(B_RIGHT_ARROW, B_OPTION_KEY | B_COMMAND_KEY); 4124 RemoveShortcut(B_UP_ARROW, B_OPTION_KEY | B_COMMAND_KEY); 4125 RemoveShortcut(B_DOWN_ARROW, B_COMMAND_KEY | B_OPTION_KEY); 4126 // This also changes meaning, added again below. 4127 4128 AddShortcut(B_DOWN_ARROW, B_COMMAND_KEY | B_OPTION_KEY, 4129 new BMessage(kOpenSelection), PoseView()); 4130 AddShortcut(B_UP_ARROW, B_COMMAND_KEY, 4131 new BMessage(kOpenParentDir), PoseView()); 4132 // We change the meaning from kNavigatorCommandUp 4133 // to kOpenParentDir. 4134 AddShortcut(B_UP_ARROW, B_COMMAND_KEY | B_OPTION_KEY, 4135 new BMessage(kOpenParentDir), PoseView()); 4136 // command + option results in closing the parent window 4137 RemoveShortcut('L', B_COMMAND_KEY); 4138 } 4139 } 4140 4141 4142 void 4143 BContainerWindow::SetPathWatchingEnabled(bool enable) 4144 { 4145 if (IsPathWatchingEnabled()) { 4146 stop_watching(this); 4147 fIsWatchingPath = false; 4148 } 4149 4150 if (enable) { 4151 if (TargetModel() != NULL) { 4152 BEntry entry; 4153 4154 TargetModel()->GetEntry(&entry); 4155 status_t err; 4156 do { 4157 err = entry.GetParent(&entry); 4158 if (err != B_OK) 4159 break; 4160 4161 char name[B_FILE_NAME_LENGTH]; 4162 entry.GetName(name); 4163 if (strcmp(name, "/") == 0) 4164 break; 4165 4166 node_ref ref; 4167 entry.GetNodeRef(&ref); 4168 watch_node(&ref, B_WATCH_NAME, this); 4169 } while (err == B_OK); 4170 4171 fIsWatchingPath = err == B_OK; 4172 } else 4173 fIsWatchingPath = false; 4174 } 4175 } 4176 4177 4178 void 4179 BContainerWindow::PulseTaskLoop() 4180 { 4181 if (fTaskLoop) 4182 fTaskLoop->PulseMe(); 4183 } 4184 4185 4186 void 4187 BContainerWindow::PopulateArrangeByMenu(BMenu* menu) 4188 { 4189 if (!fAttrMenu || !menu) 4190 return; 4191 // empty fArrangeByMenu... 4192 BMenuItem* item; 4193 while ((item = menu->RemoveItem((int32)0)) != NULL) 4194 delete item; 4195 4196 int32 itemCount = fAttrMenu->CountItems(); 4197 for (int32 i = 0; i < itemCount; i++) { 4198 item = fAttrMenu->ItemAt(i); 4199 if (item->Command() == kAttributeItem) { 4200 BMessage* message = new BMessage(*(item->Message())); 4201 message->what = kArrangeBy; 4202 BMenuItem* newItem = new BMenuItem(item->Label(), message); 4203 newItem->SetTarget(PoseView()); 4204 menu->AddItem(newItem); 4205 } 4206 } 4207 4208 menu->AddSeparatorItem(); 4209 4210 item = new BMenuItem(B_TRANSLATE("Reverse order"), 4211 new BMessage(kArrangeReverseOrder)); 4212 4213 item->SetTarget(PoseView()); 4214 menu->AddItem(item); 4215 menu->AddSeparatorItem(); 4216 4217 4218 item = new BMenuItem(B_TRANSLATE("Clean up"), new BMessage(kCleanup), 4219 'K'); 4220 item->SetTarget(PoseView()); 4221 menu->AddItem(item); 4222 } 4223 4224 4225 // #pragma mark - WindowStateNodeOpener 4226 4227 4228 WindowStateNodeOpener::WindowStateNodeOpener(BContainerWindow* window, 4229 bool forWriting) 4230 : 4231 fModelOpener(NULL), 4232 fNode(NULL), 4233 fStreamNode(NULL) 4234 { 4235 if (window->TargetModel() && window->TargetModel()->IsRoot()) { 4236 BDirectory dir; 4237 if (FSGetDeskDir(&dir) == B_OK) { 4238 fNode = new BDirectory(dir); 4239 fStreamNode = new AttributeStreamFileNode(fNode); 4240 } 4241 } else if (window->TargetModel()){ 4242 fModelOpener = new ModelNodeLazyOpener(window->TargetModel(), 4243 forWriting, false); 4244 if (fModelOpener->IsOpen(forWriting)) { 4245 fStreamNode = new AttributeStreamFileNode( 4246 fModelOpener->TargetModel()->Node()); 4247 } 4248 } 4249 } 4250 4251 WindowStateNodeOpener::~WindowStateNodeOpener() 4252 { 4253 delete fModelOpener; 4254 delete fNode; 4255 delete fStreamNode; 4256 } 4257 4258 4259 void 4260 WindowStateNodeOpener::SetTo(const BDirectory* node) 4261 { 4262 delete fModelOpener; 4263 delete fNode; 4264 delete fStreamNode; 4265 4266 fModelOpener = NULL; 4267 fNode = new BDirectory(*node); 4268 fStreamNode = new AttributeStreamFileNode(fNode); 4269 } 4270 4271 4272 void 4273 WindowStateNodeOpener::SetTo(const BEntry* entry, bool forWriting) 4274 { 4275 delete fModelOpener; 4276 delete fNode; 4277 delete fStreamNode; 4278 4279 fModelOpener = NULL; 4280 fNode = new BFile(entry, (uint32)(forWriting ? O_RDWR : O_RDONLY)); 4281 fStreamNode = new AttributeStreamFileNode(fNode); 4282 } 4283 4284 4285 void 4286 WindowStateNodeOpener::SetTo(Model* model, bool forWriting) 4287 { 4288 delete fModelOpener; 4289 delete fNode; 4290 delete fStreamNode; 4291 4292 fNode = NULL; 4293 fStreamNode = NULL; 4294 fModelOpener = new ModelNodeLazyOpener(model, forWriting, false); 4295 if (fModelOpener->IsOpen(forWriting)) { 4296 fStreamNode = new AttributeStreamFileNode( 4297 fModelOpener->TargetModel()->Node()); 4298 } 4299 } 4300 4301 4302 AttributeStreamNode* 4303 WindowStateNodeOpener::StreamNode() const 4304 { 4305 return fStreamNode; 4306 } 4307 4308 4309 BNode* 4310 WindowStateNodeOpener::Node() const 4311 { 4312 if (!fStreamNode) 4313 return NULL; 4314 4315 if (fNode) 4316 return fNode; 4317 4318 return fModelOpener->TargetModel()->Node(); 4319 } 4320 4321 4322 // #pragma mark - BackgroundView 4323 4324 4325 BackgroundView::BackgroundView(BRect frame) 4326 : 4327 BView(frame, "", B_FOLLOW_ALL, 4328 B_FRAME_EVENTS | B_WILL_DRAW | B_PULSE_NEEDED) 4329 { 4330 } 4331 4332 4333 void 4334 BackgroundView::AttachedToWindow() 4335 { 4336 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 4337 } 4338 4339 4340 void 4341 BackgroundView::FrameResized(float, float) 4342 { 4343 Invalidate(); 4344 } 4345 4346 4347 void 4348 BackgroundView::PoseViewFocused(bool focused) 4349 { 4350 Invalidate(); 4351 4352 BContainerWindow* window = dynamic_cast<BContainerWindow*>(Window()); 4353 if (!window) 4354 return; 4355 4356 BScrollBar* hScrollBar = window->PoseView()->HScrollBar(); 4357 if (hScrollBar != NULL) 4358 hScrollBar->SetBorderHighlighted(focused); 4359 4360 BScrollBar* vScrollBar = window->PoseView()->VScrollBar(); 4361 if (vScrollBar != NULL) 4362 vScrollBar->SetBorderHighlighted(focused); 4363 4364 BCountView* countView = window->PoseView()->CountView(); 4365 if (countView != NULL) 4366 countView->SetBorderHighlighted(focused); 4367 } 4368 4369 4370 void 4371 BackgroundView::WindowActivated(bool) 4372 { 4373 Invalidate(); 4374 } 4375 4376 4377 void 4378 BackgroundView::Draw(BRect updateRect) 4379 { 4380 BContainerWindow* window = dynamic_cast<BContainerWindow*>(Window()); 4381 if (!window) 4382 return; 4383 4384 BPoseView* poseView = window->PoseView(); 4385 BRect frame(poseView->Frame()); 4386 frame.InsetBy(-1, -1); 4387 frame.top -= kTitleViewHeight; 4388 frame.bottom += B_H_SCROLL_BAR_HEIGHT; 4389 frame.right += B_V_SCROLL_BAR_WIDTH; 4390 4391 if (be_control_look != NULL) { 4392 uint32 flags = 0; 4393 if (window->IsActive() && window->PoseView()->IsFocus()) 4394 flags |= BControlLook::B_FOCUSED; 4395 4396 frame.top--; 4397 frame.InsetBy(-1, -1); 4398 BRect rect(frame); 4399 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 4400 4401 BScrollBar* hScrollBar = poseView->HScrollBar(); 4402 BScrollBar* vScrollBar = poseView->VScrollBar(); 4403 4404 BRect verticalScrollBarFrame(0, 0, -1, -1); 4405 if (vScrollBar) 4406 verticalScrollBarFrame = vScrollBar->Frame(); 4407 BRect horizontalScrollBarFrame(0, 0, -1, -1); 4408 if (hScrollBar) { 4409 horizontalScrollBarFrame = hScrollBar->Frame(); 4410 // CountView extends horizontal scroll bar frame: 4411 horizontalScrollBarFrame.left = frame.left + 1; 4412 } 4413 4414 be_control_look->DrawScrollViewFrame(this, rect, updateRect, 4415 verticalScrollBarFrame, horizontalScrollBarFrame, base, 4416 B_FANCY_BORDER, flags); 4417 4418 return; 4419 } 4420 4421 SetHighColor(100, 100, 100); 4422 StrokeRect(frame); 4423 4424 // draw the pose view focus 4425 if (window->IsActive() && window->PoseView()->IsFocus()) { 4426 frame.InsetBy(-2, -2); 4427 SetHighColor(keyboard_navigation_color()); 4428 StrokeRect(frame); 4429 } 4430 } 4431 4432 4433 void 4434 BackgroundView::Pulse() 4435 { 4436 BContainerWindow* window = dynamic_cast<BContainerWindow*>(Window()); 4437 if (window) 4438 window->PulseTaskLoop(); 4439 } 4440