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 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 - 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 - 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()) { 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()) { 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()) 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 } 1733 } 1734 break; 1735 } 1736 1737 case B_NODE_MONITOR: 1738 UpdateTitle(); 1739 break; 1740 1741 case B_UNDO: 1742 FSUndo(); 1743 break; 1744 1745 //case B_REDO: // only defined in Dano/Zeta/OpenBeOS 1746 case kRedo: 1747 FSRedo(); 1748 break; 1749 1750 default: 1751 _inherited::MessageReceived(message); 1752 } 1753 } 1754 1755 1756 void 1757 BContainerWindow::SetCutItem(BMenu* menu) 1758 { 1759 BMenuItem* item; 1760 if ((item = menu->FindItem(B_CUT)) == NULL 1761 && (item = menu->FindItem(kCutMoreSelectionToClipboard)) == NULL) 1762 return; 1763 1764 item->SetEnabled(PoseView()->SelectionList()->CountItems() > 0 1765 || PoseView() != CurrentFocus()); 1766 1767 if (modifiers() & B_SHIFT_KEY) { 1768 item->SetLabel(B_TRANSLATE("Cut more")); 1769 item->SetShortcut('X', B_COMMAND_KEY | B_SHIFT_KEY); 1770 item->SetMessage(new BMessage(kCutMoreSelectionToClipboard)); 1771 } else { 1772 item->SetLabel(B_TRANSLATE("Cut")); 1773 item->SetShortcut('X', B_COMMAND_KEY); 1774 item->SetMessage(new BMessage(B_CUT)); 1775 } 1776 } 1777 1778 1779 void 1780 BContainerWindow::SetCopyItem(BMenu* menu) 1781 { 1782 BMenuItem* item; 1783 if ((item = menu->FindItem(B_COPY)) == NULL 1784 && (item = menu->FindItem(kCopyMoreSelectionToClipboard)) == NULL) { 1785 return; 1786 } 1787 1788 item->SetEnabled(PoseView()->SelectionList()->CountItems() > 0 1789 || PoseView() != CurrentFocus()); 1790 1791 if (modifiers() & B_SHIFT_KEY) { 1792 item->SetLabel(B_TRANSLATE("Copy more")); 1793 item->SetShortcut('C', B_COMMAND_KEY | B_SHIFT_KEY); 1794 item->SetMessage(new BMessage(kCopyMoreSelectionToClipboard)); 1795 } else { 1796 item->SetLabel(B_TRANSLATE("Copy")); 1797 item->SetShortcut('C', B_COMMAND_KEY); 1798 item->SetMessage(new BMessage(B_COPY)); 1799 } 1800 } 1801 1802 1803 void 1804 BContainerWindow::SetPasteItem(BMenu* menu) 1805 { 1806 BMenuItem* item; 1807 if ((item = menu->FindItem(B_PASTE)) == NULL 1808 && (item = menu->FindItem(kPasteLinksFromClipboard)) == NULL) { 1809 return; 1810 } 1811 1812 item->SetEnabled(FSClipboardHasRefs() || PoseView() != CurrentFocus()); 1813 1814 if (modifiers() & B_SHIFT_KEY) { 1815 item->SetLabel(B_TRANSLATE("Paste links")); 1816 item->SetShortcut('V', B_COMMAND_KEY | B_SHIFT_KEY); 1817 item->SetMessage(new BMessage(kPasteLinksFromClipboard)); 1818 } else { 1819 item->SetLabel(B_TRANSLATE("Paste")); 1820 item->SetShortcut('V', B_COMMAND_KEY); 1821 item->SetMessage(new BMessage(B_PASTE)); 1822 } 1823 } 1824 1825 1826 void 1827 BContainerWindow::SetArrangeMenu(BMenu* menu) 1828 { 1829 BMenuItem* item; 1830 if ((item = menu->FindItem(kCleanup)) == NULL 1831 && (item = menu->FindItem(kCleanupAll)) == NULL) { 1832 return; 1833 } 1834 1835 item->Menu()->SetEnabled(PoseView()->CountItems() > 0 1836 && (PoseView()->ViewMode() != kListMode)); 1837 1838 BMenu* arrangeMenu; 1839 1840 if (modifiers() & B_SHIFT_KEY) { 1841 item->SetLabel(B_TRANSLATE("Clean up all")); 1842 item->SetShortcut('K', B_COMMAND_KEY | B_SHIFT_KEY); 1843 item->SetMessage(new BMessage(kCleanupAll)); 1844 arrangeMenu = item->Menu(); 1845 } else { 1846 item->SetLabel(B_TRANSLATE("Clean up")); 1847 item->SetShortcut('K', B_COMMAND_KEY); 1848 item->SetMessage(new BMessage(kCleanup)); 1849 arrangeMenu = item->Menu(); 1850 } 1851 1852 MarkArrangeByMenu(arrangeMenu); 1853 } 1854 1855 1856 void 1857 BContainerWindow::SetCloseItem(BMenu* menu) 1858 { 1859 BMenuItem* item; 1860 if ((item = menu->FindItem(B_QUIT_REQUESTED)) == NULL 1861 && (item = menu->FindItem(kCloseAllWindows)) == NULL) { 1862 return; 1863 } 1864 1865 if (modifiers() & B_SHIFT_KEY) { 1866 item->SetLabel(B_TRANSLATE("Close all")); 1867 item->SetShortcut('W', B_COMMAND_KEY | B_SHIFT_KEY); 1868 item->SetTarget(be_app); 1869 item->SetMessage(new BMessage(kCloseAllWindows)); 1870 } else { 1871 item->SetLabel(B_TRANSLATE("Close")); 1872 item->SetShortcut('W', B_COMMAND_KEY); 1873 item->SetTarget(this); 1874 item->SetMessage(new BMessage(B_QUIT_REQUESTED)); 1875 } 1876 } 1877 1878 1879 bool 1880 BContainerWindow::IsShowing(const node_ref* node) const 1881 { 1882 return PoseView()->Represents(node); 1883 } 1884 1885 1886 bool 1887 BContainerWindow::IsShowing(const entry_ref* entry) const 1888 { 1889 return PoseView()->Represents(entry); 1890 } 1891 1892 1893 void 1894 BContainerWindow::AddMenus() 1895 { 1896 fFileMenu = new BMenu(B_TRANSLATE("File")); 1897 AddFileMenu(fFileMenu); 1898 fMenuBar->AddItem(fFileMenu); 1899 fWindowMenu = new BMenu(B_TRANSLATE("Window")); 1900 fMenuBar->AddItem(fWindowMenu); 1901 AddWindowMenu(fWindowMenu); 1902 // just create the attribute, decide to add it later 1903 fAttrMenu = new BMenu(B_TRANSLATE("Attributes")); 1904 NewAttributeMenu(fAttrMenu); 1905 PopulateArrangeByMenu(fArrangeByMenu); 1906 } 1907 1908 1909 void 1910 BContainerWindow::AddFileMenu(BMenu* menu) 1911 { 1912 if (!PoseView()->IsFilePanel()) { 1913 menu->AddItem(new BMenuItem(B_TRANSLATE("Find" B_UTF8_ELLIPSIS), 1914 new BMessage(kFindButton), 'F')); 1915 } 1916 1917 if (!TargetModel()->IsQuery() && !TargetModel()->IsVirtualDirectory() 1918 && !IsTrash() && !IsPrintersDir()) { 1919 if (!PoseView()->IsFilePanel()) { 1920 TemplatesMenu* templateMenu = new TemplatesMenu(PoseView(), 1921 B_TRANSLATE("New")); 1922 menu->AddItem(templateMenu); 1923 templateMenu->SetTargetForItems(PoseView()); 1924 } else { 1925 menu->AddItem(new BMenuItem(B_TRANSLATE("New folder"), 1926 new BMessage(kNewFolder), 'N')); 1927 } 1928 } 1929 menu->AddSeparatorItem(); 1930 1931 menu->AddItem(new BMenuItem(B_TRANSLATE("Open"), 1932 new BMessage(kOpenSelection), 'O')); 1933 menu->AddItem(new BMenuItem(B_TRANSLATE("Get info"), 1934 new BMessage(kGetInfo), 'I')); 1935 menu->AddItem(new BMenuItem(B_TRANSLATE("Edit name"), 1936 new BMessage(kEditItem), 'E')); 1937 1938 if (IsTrash() || InTrash()) { 1939 menu->AddItem(new BMenuItem(B_TRANSLATE("Restore"), 1940 new BMessage(kRestoreFromTrash))); 1941 if (IsTrash()) { 1942 // add as first item in menu 1943 menu->AddItem(new BMenuItem(B_TRANSLATE("Empty Trash"), 1944 new BMessage(kEmptyTrash)), 0); 1945 menu->AddItem(new BSeparatorItem(), 1); 1946 } 1947 } else if (IsPrintersDir()) { 1948 menu->AddItem(new BMenuItem(B_TRANSLATE("Add printer"B_UTF8_ELLIPSIS), 1949 new BMessage(kAddPrinter), 'N'), 0); 1950 menu->AddItem(new BSeparatorItem(), 1); 1951 menu->AddItem(new BMenuItem(B_TRANSLATE("Make active printer"), 1952 new BMessage(kMakeActivePrinter))); 1953 } else { 1954 menu->AddItem(new BMenuItem(B_TRANSLATE("Duplicate"), 1955 new BMessage(kDuplicateSelection), 'D')); 1956 1957 menu->AddItem(new BMenuItem(TrackerSettings().DontMoveFilesToTrash() 1958 ? B_TRANSLATE("Delete") : B_TRANSLATE("Move to Trash"), 1959 new BMessage(kMoveToTrash), 'T')); 1960 1961 menu->AddSeparatorItem(); 1962 1963 // The "Move To", "Copy To", "Create Link" menus are inserted 1964 // at this place, have a look at: 1965 // BContainerWindow::SetupMoveCopyMenus() 1966 } 1967 1968 BMenuItem* cutItem = NULL,* copyItem = NULL,* pasteItem = NULL; 1969 if (!IsPrintersDir()) { 1970 menu->AddSeparatorItem(); 1971 1972 menu->AddItem(cutItem = new BMenuItem(B_TRANSLATE("Cut"), 1973 new BMessage(B_CUT), 'X')); 1974 menu->AddItem(copyItem = new BMenuItem(B_TRANSLATE("Copy"), 1975 new BMessage(B_COPY), 'C')); 1976 menu->AddItem(pasteItem = new BMenuItem(B_TRANSLATE("Paste"), 1977 new BMessage(B_PASTE), 'V')); 1978 1979 menu->AddSeparatorItem(); 1980 1981 menu->AddItem(new BMenuItem(B_TRANSLATE("Identify"), 1982 new BMessage(kIdentifyEntry))); 1983 BMenu* addOnMenuItem = new BMenu(B_TRANSLATE("Add-ons")); 1984 addOnMenuItem->SetFont(be_plain_font); 1985 menu->AddItem(addOnMenuItem); 1986 } 1987 1988 menu->SetTargetForItems(PoseView()); 1989 if (cutItem) 1990 cutItem->SetTarget(this); 1991 1992 if (copyItem) 1993 copyItem->SetTarget(this); 1994 1995 if (pasteItem) 1996 pasteItem->SetTarget(this); 1997 } 1998 1999 2000 void 2001 BContainerWindow::AddWindowMenu(BMenu* menu) 2002 { 2003 BMenuItem* item; 2004 2005 BMenu* iconSizeMenu = new BMenu(B_TRANSLATE("Icon view")); 2006 2007 BMessage* message = new BMessage(kIconMode); 2008 message->AddInt32("size", 32); 2009 item = new BMenuItem(B_TRANSLATE("32 x 32"), message); 2010 item->SetTarget(PoseView()); 2011 iconSizeMenu->AddItem(item); 2012 2013 message = new BMessage(kIconMode); 2014 message->AddInt32("size", 40); 2015 item = new BMenuItem(B_TRANSLATE("40 x 40"), message); 2016 item->SetTarget(PoseView()); 2017 iconSizeMenu->AddItem(item); 2018 2019 message = new BMessage(kIconMode); 2020 message->AddInt32("size", 48); 2021 item = new BMenuItem(B_TRANSLATE("48 x 48"), message); 2022 item->SetTarget(PoseView()); 2023 iconSizeMenu->AddItem(item); 2024 2025 message = new BMessage(kIconMode); 2026 message->AddInt32("size", 64); 2027 item = new BMenuItem(B_TRANSLATE("64 x 64"), message); 2028 item->SetTarget(PoseView()); 2029 iconSizeMenu->AddItem(item); 2030 2031 message = new BMessage(kIconMode); 2032 message->AddInt32("size", 96); 2033 item = new BMenuItem(B_TRANSLATE("96 x 96"), message); 2034 item->SetMarked(PoseView()->IconSizeInt() == 96); 2035 item->SetTarget(PoseView()); 2036 iconSizeMenu->AddItem(item); 2037 2038 message = new BMessage(kIconMode); 2039 message->AddInt32("size", 128); 2040 item = new BMenuItem(B_TRANSLATE("128 x 128"), message); 2041 item->SetMarked(PoseView()->IconSizeInt() == 128); 2042 item->SetTarget(PoseView()); 2043 iconSizeMenu->AddItem(item); 2044 2045 iconSizeMenu->AddSeparatorItem(); 2046 2047 message = new BMessage(kIconMode); 2048 message->AddInt32("scale", 0); 2049 item = new BMenuItem(B_TRANSLATE("Decrease size"), message, '-'); 2050 item->SetTarget(PoseView()); 2051 iconSizeMenu->AddItem(item); 2052 2053 message = new BMessage(kIconMode); 2054 message->AddInt32("scale", 1); 2055 item = new BMenuItem(B_TRANSLATE("Increase size"), message, '+'); 2056 item->SetTarget(PoseView()); 2057 iconSizeMenu->AddItem(item); 2058 2059 // A sub menu where the super item can be invoked. 2060 menu->AddItem(iconSizeMenu); 2061 iconSizeMenu->Superitem()->SetShortcut('1', B_COMMAND_KEY); 2062 iconSizeMenu->Superitem()->SetMessage(new BMessage(kIconMode)); 2063 iconSizeMenu->Superitem()->SetTarget(PoseView()); 2064 2065 item = new BMenuItem(B_TRANSLATE("Mini icon view"), 2066 new BMessage(kMiniIconMode), '2'); 2067 item->SetTarget(PoseView()); 2068 menu->AddItem(item); 2069 2070 item = new BMenuItem(B_TRANSLATE("List view"), 2071 new BMessage(kListMode), '3'); 2072 item->SetTarget(PoseView()); 2073 menu->AddItem(item); 2074 2075 menu->AddSeparatorItem(); 2076 2077 item = new BMenuItem(B_TRANSLATE("Resize to fit"), 2078 new BMessage(kResizeToFit), 'Y'); 2079 item->SetTarget(this); 2080 menu->AddItem(item); 2081 2082 fArrangeByMenu = new BMenu(B_TRANSLATE("Arrange by")); 2083 menu->AddItem(fArrangeByMenu); 2084 2085 item = new BMenuItem(B_TRANSLATE("Select"B_UTF8_ELLIPSIS), 2086 new BMessage(kShowSelectionWindow), 'A', B_SHIFT_KEY); 2087 item->SetTarget(PoseView()); 2088 menu->AddItem(item); 2089 2090 item = new BMenuItem(B_TRANSLATE("Select all"), 2091 new BMessage(B_SELECT_ALL), 'A'); 2092 item->SetTarget(PoseView()); 2093 menu->AddItem(item); 2094 2095 item = new BMenuItem(B_TRANSLATE("Invert selection"), 2096 new BMessage(kInvertSelection), 'S'); 2097 item->SetTarget(PoseView()); 2098 menu->AddItem(item); 2099 2100 if (!IsTrash()) { 2101 item = new BMenuItem(B_TRANSLATE("Open parent"), 2102 new BMessage(kOpenParentDir), B_UP_ARROW); 2103 item->SetTarget(PoseView()); 2104 menu->AddItem(item); 2105 } 2106 2107 item = new BMenuItem(B_TRANSLATE("Close"), 2108 new BMessage(B_QUIT_REQUESTED), 'W'); 2109 item->SetTarget(this); 2110 menu->AddItem(item); 2111 2112 item = new BMenuItem(B_TRANSLATE("Close all in workspace"), 2113 new BMessage(kCloseAllInWorkspace), 'Q'); 2114 item->SetTarget(be_app); 2115 menu->AddItem(item); 2116 2117 menu->AddSeparatorItem(); 2118 2119 item = new BMenuItem(B_TRANSLATE("Preferences" B_UTF8_ELLIPSIS), 2120 new BMessage(kShowSettingsWindow)); 2121 item->SetTarget(be_app); 2122 menu->AddItem(item); 2123 } 2124 2125 2126 void 2127 BContainerWindow::AddShortcuts() 2128 { 2129 // add equivalents of the menu shortcuts to the menuless desktop window 2130 ASSERT(!IsTrash()); 2131 ASSERT(!PoseView()->IsFilePanel()); 2132 ASSERT(!TargetModel()->IsQuery()); 2133 ASSERT(!TargetModel()->IsVirtualDirectory()); 2134 2135 AddShortcut('X', B_COMMAND_KEY | B_SHIFT_KEY, 2136 new BMessage(kCutMoreSelectionToClipboard), this); 2137 AddShortcut('C', B_COMMAND_KEY | B_SHIFT_KEY, 2138 new BMessage(kCopyMoreSelectionToClipboard), this); 2139 AddShortcut('F', B_COMMAND_KEY, 2140 new BMessage(kFindButton), PoseView()); 2141 AddShortcut('N', B_COMMAND_KEY, 2142 new BMessage(kNewFolder), PoseView()); 2143 AddShortcut('O', B_COMMAND_KEY, 2144 new BMessage(kOpenSelection), PoseView()); 2145 AddShortcut('I', B_COMMAND_KEY, 2146 new BMessage(kGetInfo), PoseView()); 2147 AddShortcut('E', B_COMMAND_KEY, 2148 new BMessage(kEditItem), PoseView()); 2149 AddShortcut('D', B_COMMAND_KEY, 2150 new BMessage(kDuplicateSelection), PoseView()); 2151 AddShortcut('T', B_COMMAND_KEY, 2152 new BMessage(kMoveToTrash), PoseView()); 2153 AddShortcut('K', B_COMMAND_KEY, 2154 new BMessage(kCleanup), PoseView()); 2155 AddShortcut('A', B_COMMAND_KEY, 2156 new BMessage(B_SELECT_ALL), PoseView()); 2157 AddShortcut('S', B_COMMAND_KEY, 2158 new BMessage(kInvertSelection), PoseView()); 2159 AddShortcut('A', B_COMMAND_KEY | B_SHIFT_KEY, 2160 new BMessage(kShowSelectionWindow), PoseView()); 2161 AddShortcut('G', B_COMMAND_KEY, 2162 new BMessage(kEditQuery), PoseView()); 2163 // it is ok to add a global Edit query shortcut here, PoseView will 2164 // filter out cases where selected pose is not a query 2165 AddShortcut('U', B_COMMAND_KEY, 2166 new BMessage(kUnmountVolume), PoseView()); 2167 AddShortcut(B_UP_ARROW, B_COMMAND_KEY, 2168 new BMessage(kOpenParentDir), PoseView()); 2169 AddShortcut('O', B_COMMAND_KEY | B_CONTROL_KEY, 2170 new BMessage(kOpenSelectionWith), PoseView()); 2171 2172 BMessage* decreaseSize = new BMessage(kIconMode); 2173 decreaseSize->AddInt32("scale", 0); 2174 AddShortcut('-', B_COMMAND_KEY, decreaseSize, PoseView()); 2175 2176 BMessage* increaseSize = new BMessage(kIconMode); 2177 increaseSize->AddInt32("scale", 1); 2178 AddShortcut('+', B_COMMAND_KEY, increaseSize, PoseView()); 2179 } 2180 2181 2182 void 2183 BContainerWindow::MenusBeginning() 2184 { 2185 if (fMenuBar == NULL) 2186 return; 2187 2188 if (CurrentMessage() != NULL && CurrentMessage()->what == B_MOUSE_DOWN) { 2189 // don't commit active pose if only a keyboard shortcut is 2190 // invoked - this would prevent Cut/Copy/Paste from working 2191 fPoseView->CommitActivePose(); 2192 } 2193 2194 // File menu 2195 int32 selectCount = PoseView()->SelectionList()->CountItems(); 2196 2197 SetupOpenWithMenu(fFileMenu); 2198 SetupMoveCopyMenus(selectCount 2199 ? PoseView()->SelectionList()->FirstItem()->TargetModel()->EntryRef() 2200 : NULL, fFileMenu); 2201 2202 UpdateMenu(fMenuBar, kMenuBarContext); 2203 2204 AddMimeTypesToMenu(fAttrMenu); 2205 2206 if (IsPrintersDir()) { 2207 EnableNamedMenuItem(fFileMenu, B_TRANSLATE("Make active printer"), 2208 selectCount == 1); 2209 } 2210 } 2211 2212 2213 void 2214 BContainerWindow::MenusEnded() 2215 { 2216 // when we're done we want to clear nav menus for next time 2217 DeleteSubmenu(fNavigationItem); 2218 DeleteSubmenu(fMoveToItem); 2219 DeleteSubmenu(fCopyToItem); 2220 DeleteSubmenu(fCreateLinkItem); 2221 DeleteSubmenu(fOpenWithItem); 2222 } 2223 2224 2225 void 2226 BContainerWindow::SetupNavigationMenu(const entry_ref* ref, BMenu* parent) 2227 { 2228 // start by removing nav item (and separator) from old menu 2229 if (fNavigationItem != NULL) { 2230 BMenu* menu = fNavigationItem->Menu(); 2231 if (menu != NULL) { 2232 menu->RemoveItem(fNavigationItem); 2233 BMenuItem* item = menu->RemoveItem((int32)0); 2234 ASSERT(item != fNavigationItem); 2235 delete item; 2236 } 2237 } 2238 2239 // if we weren't passed a ref then we're navigating this window 2240 if (ref == NULL) 2241 ref = TargetModel()->EntryRef(); 2242 2243 BEntry entry; 2244 if (entry.SetTo(ref) != B_OK) 2245 return; 2246 2247 // only navigate directories and queries (check for symlink here) 2248 Model model(&entry); 2249 entry_ref resolvedRef; 2250 2251 if (model.InitCheck() != B_OK 2252 || (!model.IsContainer() && !model.IsSymLink())) { 2253 return; 2254 } 2255 2256 if (model.IsSymLink()) { 2257 if (entry.SetTo(model.EntryRef(), true) != B_OK) 2258 return; 2259 2260 Model resolvedModel(&entry); 2261 if (resolvedModel.InitCheck() != B_OK || !resolvedModel.IsContainer()) 2262 return; 2263 2264 entry.GetRef(&resolvedRef); 2265 ref = &resolvedRef; 2266 } 2267 2268 if (fNavigationItem == NULL) { 2269 fNavigationItem = new ModelMenuItem(&model, 2270 new BNavMenu(model.Name(), B_REFS_RECEIVED, be_app, this)); 2271 } 2272 2273 // setup a navigation menu item which will dynamically load items 2274 // as menu items are traversed 2275 BNavMenu* navMenu = dynamic_cast<BNavMenu*>(fNavigationItem->Submenu()); 2276 navMenu->SetNavDir(ref); 2277 fNavigationItem->SetLabel(model.Name()); 2278 fNavigationItem->SetEntry(&entry); 2279 2280 parent->AddItem(fNavigationItem, 0); 2281 parent->AddItem(new BSeparatorItem(), 1); 2282 2283 BMessage* message = new BMessage(B_REFS_RECEIVED); 2284 message->AddRef("refs", ref); 2285 fNavigationItem->SetMessage(message); 2286 fNavigationItem->SetTarget(be_app); 2287 2288 if (!Dragging()) 2289 parent->SetTrackingHook(NULL, NULL); 2290 } 2291 2292 2293 void 2294 BContainerWindow::SetUpEditQueryItem(BMenu* menu) 2295 { 2296 ASSERT(menu); 2297 // File menu 2298 int32 selectCount = PoseView()->SelectionList()->CountItems(); 2299 2300 // add Edit query if appropriate 2301 bool queryInSelection = false; 2302 if (selectCount && selectCount < 100) { 2303 // only do this for a limited number of selected poses 2304 2305 // if any queries selected, add an edit query menu item 2306 for (int32 index = 0; index < selectCount; index++) { 2307 BPose* pose = PoseView()->SelectionList()->ItemAt(index); 2308 Model model(pose->TargetModel()->EntryRef(), true); 2309 if (model.InitCheck() != B_OK) 2310 continue; 2311 2312 if (model.IsQuery() || model.IsQueryTemplate()) { 2313 queryInSelection = true; 2314 break; 2315 } 2316 } 2317 } 2318 2319 bool poseViewIsQuery = TargetModel()->IsQuery(); 2320 // if the view is a query pose view, add edit query menu item 2321 2322 BMenuItem* item = menu->FindItem(kEditQuery); 2323 if (!poseViewIsQuery && !queryInSelection && item != NULL) 2324 item->Menu()->RemoveItem(item); 2325 else if ((poseViewIsQuery || queryInSelection) && item == NULL) { 2326 // add edit query item after Open 2327 item = menu->FindItem(kOpenSelection); 2328 if (item) { 2329 int32 itemIndex = item->Menu()->IndexOf(item); 2330 BMenuItem* query = new BMenuItem(B_TRANSLATE("Edit query"), 2331 new BMessage(kEditQuery), 'G'); 2332 item->Menu()->AddItem(query, itemIndex + 1); 2333 query->SetTarget(PoseView()); 2334 } 2335 } 2336 } 2337 2338 2339 void 2340 BContainerWindow::SetupOpenWithMenu(BMenu* parent) 2341 { 2342 // start by removing nav item (and separator) from old menu 2343 if (fOpenWithItem) { 2344 BMenu* menu = fOpenWithItem->Menu(); 2345 if (menu != NULL) 2346 menu->RemoveItem(fOpenWithItem); 2347 2348 delete fOpenWithItem; 2349 fOpenWithItem = 0; 2350 } 2351 2352 if (PoseView()->SelectionList()->CountItems() == 0) { 2353 // no selection, nothing to open 2354 return; 2355 } 2356 2357 if (TargetModel()->IsRoot()) { 2358 // don't add ourselves if we are root 2359 return; 2360 } 2361 2362 // ToDo: 2363 // check if only item in selection list is the root 2364 // and do not add if true 2365 2366 // add after "Open" 2367 BMenuItem* item = parent->FindItem(kOpenSelection); 2368 2369 int32 count = PoseView()->SelectionList()->CountItems(); 2370 if (count == 0) 2371 return; 2372 2373 // build a list of all refs to open 2374 BMessage message(B_REFS_RECEIVED); 2375 for (int32 index = 0; index < count; index++) { 2376 BPose* pose = PoseView()->SelectionList()->ItemAt(index); 2377 message.AddRef("refs", pose->TargetModel()->EntryRef()); 2378 } 2379 2380 // add Tracker token so that refs received recipients can script us 2381 message.AddMessenger("TrackerViewToken", BMessenger(PoseView())); 2382 2383 int32 index = item->Menu()->IndexOf(item); 2384 fOpenWithItem = new BMenuItem( 2385 new OpenWithMenu(B_TRANSLATE("Open with" B_UTF8_ELLIPSIS), 2386 &message, this, be_app), new BMessage(kOpenSelectionWith)); 2387 fOpenWithItem->SetTarget(PoseView()); 2388 fOpenWithItem->SetShortcut('O', B_COMMAND_KEY | B_CONTROL_KEY); 2389 2390 item->Menu()->AddItem(fOpenWithItem, index + 1); 2391 } 2392 2393 2394 void 2395 BContainerWindow::PopulateMoveCopyNavMenu(BNavMenu* navMenu, uint32 what, 2396 const entry_ref* ref, bool addLocalOnly) 2397 { 2398 BVolume volume; 2399 BVolumeRoster volumeRoster; 2400 BDirectory directory; 2401 BEntry entry; 2402 BPath path; 2403 Model model; 2404 dev_t device = ref->device; 2405 2406 int32 volumeCount = 0; 2407 2408 navMenu->RemoveItems(0, navMenu->CountItems(), true); 2409 2410 // count persistent writable volumes 2411 volumeRoster.Rewind(); 2412 while (volumeRoster.GetNextVolume(&volume) == B_OK) 2413 if (!volume.IsReadOnly() && volume.IsPersistent()) 2414 volumeCount++; 2415 2416 // add the current folder 2417 if (entry.SetTo(ref) == B_OK 2418 && entry.GetParent(&entry) == B_OK 2419 && model.SetTo(&entry) == B_OK) { 2420 BNavMenu* menu = new BNavMenu(B_TRANSLATE("Current folder"), what, 2421 this); 2422 menu->SetNavDir(model.EntryRef()); 2423 menu->SetShowParent(true); 2424 2425 BMenuItem* item = new SpecialModelMenuItem(&model,menu); 2426 item->SetMessage(new BMessage((uint32)what)); 2427 2428 navMenu->AddItem(item); 2429 } 2430 2431 // add the recent folder menu 2432 // the "Tracker" settings directory is only used to get its icon 2433 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) { 2434 path.Append("Tracker"); 2435 if (entry.SetTo(path.Path()) == B_OK 2436 && model.SetTo(&entry) == B_OK) { 2437 BMenu* menu = new RecentsMenu(B_TRANSLATE("Recent folders"), 2438 kRecentFolders, what, this); 2439 2440 BMenuItem* item = new SpecialModelMenuItem(&model,menu); 2441 item->SetMessage(new BMessage((uint32)what)); 2442 2443 navMenu->AddItem(item); 2444 } 2445 } 2446 2447 // add Desktop 2448 FSGetBootDeskDir(&directory); 2449 if (directory.InitCheck() == B_OK 2450 && directory.GetEntry(&entry) == B_OK 2451 && model.SetTo(&entry) == B_OK) 2452 navMenu->AddNavDir(&model, what, this, true); 2453 // ask NavMenu to populate submenu for us 2454 2455 // add the home dir 2456 if (find_directory(B_USER_DIRECTORY, &path) == B_OK 2457 && entry.SetTo(path.Path()) == B_OK 2458 && model.SetTo(&entry) == B_OK) 2459 navMenu->AddNavDir(&model, what, this, true); 2460 2461 navMenu->AddSeparatorItem(); 2462 2463 // either add all mounted volumes (for copy), or all the top-level 2464 // directories from the same device (for move) 2465 // ToDo: can be changed if cross-device moves are implemented 2466 2467 if (addLocalOnly || volumeCount < 2) { 2468 // add volume this item lives on 2469 if (volume.SetTo(device) == B_OK 2470 && volume.GetRootDirectory(&directory) == B_OK 2471 && directory.GetEntry(&entry) == B_OK 2472 && model.SetTo(&entry) == B_OK) { 2473 navMenu->AddNavDir(&model, what, this, false); 2474 // do not have submenu populated 2475 2476 navMenu->SetNavDir(model.EntryRef()); 2477 } 2478 } else { 2479 // add all persistent writable volumes 2480 volumeRoster.Rewind(); 2481 while (volumeRoster.GetNextVolume(&volume) == B_OK) { 2482 if (volume.IsReadOnly() || !volume.IsPersistent()) 2483 continue; 2484 2485 // add root dir 2486 if (volume.GetRootDirectory(&directory) == B_OK 2487 && directory.GetEntry(&entry) == B_OK 2488 && model.SetTo(&entry) == B_OK) { 2489 navMenu->AddNavDir(&model, what, this, true); 2490 // ask NavMenu to populate submenu for us 2491 } 2492 } 2493 } 2494 } 2495 2496 2497 void 2498 BContainerWindow::SetupMoveCopyMenus(const entry_ref* item_ref, BMenu* parent) 2499 { 2500 if (IsTrash() || InTrash() || IsPrintersDir() || !fMoveToItem 2501 || !fCopyToItem || !fCreateLinkItem) { 2502 return; 2503 } 2504 2505 // Grab the modifiers state since we use it twice 2506 uint32 modifierKeys = modifiers(); 2507 2508 // re-parent items to this menu since they're shared 2509 int32 index; 2510 BMenuItem* trash = parent->FindItem(kMoveToTrash); 2511 if (trash) 2512 index = parent->IndexOf(trash) + 2; 2513 else 2514 index = 0; 2515 2516 if (fMoveToItem->Menu() != parent) { 2517 if (fMoveToItem->Menu()) 2518 fMoveToItem->Menu()->RemoveItem(fMoveToItem); 2519 2520 parent->AddItem(fMoveToItem, index++); 2521 } 2522 2523 if (fCopyToItem->Menu() != parent) { 2524 if (fCopyToItem->Menu()) 2525 fCopyToItem->Menu()->RemoveItem(fCopyToItem); 2526 2527 parent->AddItem(fCopyToItem, index++); 2528 } 2529 2530 if (fCreateLinkItem->Menu() != parent) { 2531 if (fCreateLinkItem->Menu()) 2532 fCreateLinkItem->Menu()->RemoveItem(fCreateLinkItem); 2533 2534 parent->AddItem(fCreateLinkItem, index); 2535 } 2536 2537 // Set the "Create Link" item label here so it 2538 // appears correctly when menus are disabled, too. 2539 if (modifierKeys & B_SHIFT_KEY) 2540 fCreateLinkItem->SetLabel(B_TRANSLATE("Create relative link")); 2541 else 2542 fCreateLinkItem->SetLabel(B_TRANSLATE("Create link")); 2543 2544 // only enable once the menus are built 2545 fMoveToItem->SetEnabled(false); 2546 fCopyToItem->SetEnabled(false); 2547 fCreateLinkItem->SetEnabled(false); 2548 2549 // get ref for item which is selected 2550 BEntry entry; 2551 if (entry.SetTo(item_ref) != B_OK) 2552 return; 2553 2554 Model tempModel(&entry); 2555 if (tempModel.InitCheck() != B_OK) 2556 return; 2557 2558 if (tempModel.IsRoot() || tempModel.IsVolume()) 2559 return; 2560 2561 // configure "Move to" menu item 2562 PopulateMoveCopyNavMenu(dynamic_cast<BNavMenu*>(fMoveToItem->Submenu()), 2563 kMoveSelectionTo, item_ref, true); 2564 2565 // configure "Copy to" menu item 2566 // add all mounted volumes (except the one this item lives on) 2567 PopulateMoveCopyNavMenu(dynamic_cast<BNavMenu*>(fCopyToItem->Submenu()), 2568 kCopySelectionTo, item_ref, false); 2569 2570 // Set "Create Link" menu item message and 2571 // add all mounted volumes (except the one this item lives on) 2572 if (modifierKeys & B_SHIFT_KEY) { 2573 fCreateLinkItem->SetMessage(new BMessage(kCreateRelativeLink)); 2574 PopulateMoveCopyNavMenu(dynamic_cast<BNavMenu*> 2575 (fCreateLinkItem->Submenu()), 2576 kCreateRelativeLink, item_ref, false); 2577 } else { 2578 fCreateLinkItem->SetMessage(new BMessage(kCreateLink)); 2579 PopulateMoveCopyNavMenu(dynamic_cast<BNavMenu*> 2580 (fCreateLinkItem->Submenu()), 2581 kCreateLink, item_ref, false); 2582 } 2583 2584 fMoveToItem->SetEnabled(true); 2585 fCopyToItem->SetEnabled(true); 2586 fCreateLinkItem->SetEnabled(true); 2587 2588 // Set the "Identify" item label 2589 BMenuItem* identifyItem = parent->FindItem(kIdentifyEntry); 2590 if (identifyItem != NULL) { 2591 if (modifierKeys & B_SHIFT_KEY) { 2592 identifyItem->SetLabel(B_TRANSLATE("Force identify")); 2593 identifyItem->Message()->ReplaceBool("force", true); 2594 } else { 2595 identifyItem->SetLabel(B_TRANSLATE("Identify")); 2596 identifyItem->Message()->ReplaceBool("force", false); 2597 } 2598 } 2599 } 2600 2601 2602 uint32 2603 BContainerWindow::ShowDropContextMenu(BPoint loc) 2604 { 2605 BPoint global(loc); 2606 2607 PoseView()->ConvertToScreen(&global); 2608 PoseView()->CommitActivePose(); 2609 2610 // Change the "Create Link" item - allow user to 2611 // create relative links with the Shift key down. 2612 BMenuItem* item = fDropContextMenu->FindItem(kCreateLink); 2613 if (item == NULL) 2614 item = fDropContextMenu->FindItem(kCreateRelativeLink); 2615 if (item && (modifiers() & B_SHIFT_KEY)) { 2616 item->SetLabel(B_TRANSLATE("Create relative link here")); 2617 item->SetMessage(new BMessage(kCreateRelativeLink)); 2618 } else if (item) { 2619 item->SetLabel(B_TRANSLATE("Create link here")); 2620 item->SetMessage(new BMessage(kCreateLink)); 2621 } 2622 2623 item = fDropContextMenu->Go(global, true, true); 2624 if (item) 2625 return item->Command(); 2626 2627 return 0; 2628 } 2629 2630 2631 void 2632 BContainerWindow::ShowContextMenu(BPoint loc, const entry_ref* ref, BView*) 2633 { 2634 ASSERT(IsLocked()); 2635 BPoint global(loc); 2636 PoseView()->ConvertToScreen(&global); 2637 PoseView()->CommitActivePose(); 2638 2639 if (ref) { 2640 // clicked on a pose, show file or volume context menu 2641 Model model(ref); 2642 2643 if (model.IsTrash()) { 2644 2645 if (fTrashContextMenu->Window() || Dragging()) 2646 return; 2647 2648 DeleteSubmenu(fNavigationItem); 2649 2650 // selected item was trash, show the trash context menu instead 2651 2652 EnableNamedMenuItem(fTrashContextMenu, kEmptyTrash, 2653 static_cast<TTracker*>(be_app)->TrashFull()); 2654 2655 SetupNavigationMenu(ref, fTrashContextMenu); 2656 fTrashContextMenu->Go(global, true, true, true); 2657 } else { 2658 2659 bool showAsVolume = false; 2660 bool filePanel = PoseView()->IsFilePanel(); 2661 2662 if (Dragging()) { 2663 fContextMenu = NULL; 2664 2665 BEntry entry; 2666 model.GetEntry(&entry); 2667 2668 // only show for directories (directory, volume, root) 2669 // 2670 // don't show a popup for the trash or printers 2671 // trash is handled in DeskWindow 2672 // 2673 // since this menu is opened asynchronously 2674 // we need to make sure we don't open it more 2675 // than once, the IsShowing flag is set in 2676 // SlowContextPopup::AttachedToWindow and 2677 // reset in DetachedFromWindow 2678 // see the notes in SlowContextPopup::AttachedToWindow 2679 2680 if (!FSIsPrintersDir(&entry) 2681 && !fDragContextMenu->IsShowing()) { 2682 //printf("ShowContextMenu - target is %s %i\n", 2683 // ref->name, IsShowing(ref)); 2684 fDragContextMenu->ClearMenu(); 2685 2686 // in case the ref is a symlink, resolve it 2687 // only pop open for directories 2688 BEntry resolvedEntry(ref, true); 2689 if (!resolvedEntry.IsDirectory()) 2690 return; 2691 2692 entry_ref resolvedRef; 2693 resolvedEntry.GetRef(&resolvedRef); 2694 2695 // use the resolved ref for the menu 2696 fDragContextMenu->SetNavDir(&resolvedRef); 2697 fDragContextMenu->SetTypesList(fCachedTypesList); 2698 fDragContextMenu->SetTarget(BMessenger(this)); 2699 BPoseView* poseView = PoseView(); 2700 if (poseView != NULL) { 2701 BMessenger target(poseView); 2702 fDragContextMenu->InitTrackingHook( 2703 &BPoseView::MenuTrackingHook, &target, 2704 fDragMessage); 2705 } 2706 2707 // this is now asynchronous so that we don't 2708 // deadlock in Window::Quit, 2709 fDragContextMenu->Go(global, true, false, true); 2710 } 2711 2712 return; 2713 } else if (TargetModel()->IsRoot() || model.IsVolume()) { 2714 fContextMenu = fVolumeContextMenu; 2715 showAsVolume = true; 2716 } else 2717 fContextMenu = fFileContextMenu; 2718 2719 // clean up items from last context menu 2720 2721 if (fContextMenu != NULL) { 2722 if (fContextMenu->Window()) 2723 return; 2724 else 2725 MenusEnded(); 2726 2727 if (model.InitCheck() == B_OK) { // ??? Do I need this ??? 2728 if (showAsVolume) { 2729 // non-volume enable/disable copy, move, identify 2730 EnableNamedMenuItem(fContextMenu, kDuplicateSelection, 2731 false); 2732 EnableNamedMenuItem(fContextMenu, kMoveToTrash, false); 2733 EnableNamedMenuItem(fContextMenu, kIdentifyEntry, 2734 false); 2735 2736 // volume model, enable/disable the Unmount item 2737 bool ejectableVolumeSelected = false; 2738 2739 BVolume boot; 2740 BVolumeRoster().GetBootVolume(&boot); 2741 BVolume volume; 2742 volume.SetTo(model.NodeRef()->device); 2743 if (volume != boot) 2744 ejectableVolumeSelected = true; 2745 2746 EnableNamedMenuItem(fContextMenu, 2747 B_TRANSLATE("Unmount"), ejectableVolumeSelected); 2748 } 2749 } 2750 2751 SetupNavigationMenu(ref, fContextMenu); 2752 if (!showAsVolume && !filePanel) { 2753 SetupMoveCopyMenus(ref, fContextMenu); 2754 SetupOpenWithMenu(fContextMenu); 2755 } 2756 2757 UpdateMenu(fContextMenu, kPosePopUpContext); 2758 2759 fContextMenu->Go(global, true, true, true); 2760 } 2761 } 2762 } else if (fWindowContextMenu != NULL) { 2763 if (fWindowContextMenu->Window()) 2764 return; 2765 2766 // Repopulate desktop menu if IsDesktop 2767 if (dynamic_cast<BDeskWindow*>(this)) 2768 RepopulateMenus(); 2769 2770 MenusEnded(); 2771 2772 // clicked on a window, show window context menu 2773 2774 SetupNavigationMenu(ref, fWindowContextMenu); 2775 UpdateMenu(fWindowContextMenu, kWindowPopUpContext); 2776 2777 fWindowContextMenu->Go(global, true, true, true); 2778 } 2779 2780 fContextMenu = NULL; 2781 } 2782 2783 2784 void 2785 BContainerWindow::AddFileContextMenus(BMenu* menu) 2786 { 2787 menu->AddItem(new BMenuItem(B_TRANSLATE("Open"), 2788 new BMessage(kOpenSelection), 'O')); 2789 menu->AddItem(new BMenuItem(B_TRANSLATE("Get info"), 2790 new BMessage(kGetInfo), 'I')); 2791 menu->AddItem(new BMenuItem(B_TRANSLATE("Edit name"), 2792 new BMessage(kEditItem), 'E')); 2793 2794 if (!IsTrash() && !InTrash() && !IsPrintersDir()) { 2795 menu->AddItem(new BMenuItem(B_TRANSLATE("Duplicate"), 2796 new BMessage(kDuplicateSelection), 'D')); 2797 } 2798 2799 if (!IsTrash() && !InTrash()) { 2800 menu->AddItem(new BMenuItem(TrackerSettings().DontMoveFilesToTrash() 2801 ? B_TRANSLATE("Delete") : B_TRANSLATE("Move to Trash"), 2802 new BMessage(kMoveToTrash), 'T')); 2803 if (!IsPrintersDir()) { 2804 // add separator for copy to/move to items (navigation items) 2805 menu->AddSeparatorItem(); 2806 } 2807 } else { 2808 menu->AddItem(new BMenuItem(B_TRANSLATE("Delete"), 2809 new BMessage(kDelete), 0)); 2810 menu->AddItem(new BMenuItem(B_TRANSLATE("Restore"), 2811 new BMessage(kRestoreFromTrash), 0)); 2812 } 2813 2814 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU 2815 menu->AddSeparatorItem(); 2816 BMenuItem* cutItem,* copyItem; 2817 menu->AddItem(cutItem = new BMenuItem(B_TRANSLATE("Cut"), 2818 new BMessage(B_CUT), 'X')); 2819 menu->AddItem(copyItem = new BMenuItem(B_TRANSLATE("Copy"), 2820 new BMessage(B_COPY), 'C')); 2821 #endif 2822 2823 menu->AddSeparatorItem(); 2824 BMessage* message = new BMessage(kIdentifyEntry); 2825 message->AddBool("force", false); 2826 menu->AddItem(new BMenuItem(B_TRANSLATE("Identify"), message)); 2827 BMenu* addOnMenuItem = new BMenu(B_TRANSLATE("Add-ons")); 2828 addOnMenuItem->SetFont(be_plain_font); 2829 menu->AddItem(addOnMenuItem); 2830 2831 // set targets as needed 2832 menu->SetTargetForItems(PoseView()); 2833 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU 2834 cutItem->SetTarget(this); 2835 copyItem->SetTarget(this); 2836 #endif 2837 } 2838 2839 2840 void 2841 BContainerWindow::AddVolumeContextMenus(BMenu* menu) 2842 { 2843 menu->AddItem(new BMenuItem(B_TRANSLATE("Open"), 2844 new BMessage(kOpenSelection), 'O')); 2845 menu->AddItem(new BMenuItem(B_TRANSLATE("Get info"), 2846 new BMessage(kGetInfo), 'I')); 2847 menu->AddItem(new BMenuItem(B_TRANSLATE("Edit name"), 2848 new BMessage(kEditItem), 'E')); 2849 2850 menu->AddSeparatorItem(); 2851 menu->AddItem(new MountMenu(B_TRANSLATE("Mount"))); 2852 2853 BMenuItem* item = new BMenuItem(B_TRANSLATE("Unmount"), 2854 new BMessage(kUnmountVolume), 'U'); 2855 item->SetEnabled(false); 2856 menu->AddItem(item); 2857 2858 menu->AddSeparatorItem(); 2859 menu->AddItem(new BMenu(B_TRANSLATE("Add-ons"))); 2860 2861 menu->SetTargetForItems(PoseView()); 2862 } 2863 2864 2865 void 2866 BContainerWindow::AddWindowContextMenus(BMenu* menu) 2867 { 2868 // create context sensitive menu for empty area of window 2869 // since we check view mode before display, this should be a radio 2870 // mode menu 2871 2872 bool needSeparator = true; 2873 if (IsTrash()) { 2874 menu->AddItem(new BMenuItem(B_TRANSLATE("Empty Trash"), 2875 new BMessage(kEmptyTrash))); 2876 } else if (IsPrintersDir()) { 2877 menu->AddItem(new BMenuItem(B_TRANSLATE("Add printer"B_UTF8_ELLIPSIS), 2878 new BMessage(kAddPrinter), 'N')); 2879 } else if (InTrash()) 2880 needSeparator = false; 2881 else { 2882 TemplatesMenu* templateMenu = new TemplatesMenu(PoseView(), 2883 B_TRANSLATE("New")); 2884 menu->AddItem(templateMenu); 2885 templateMenu->SetTargetForItems(PoseView()); 2886 templateMenu->SetFont(be_plain_font); 2887 } 2888 2889 if (needSeparator) 2890 menu->AddSeparatorItem(); 2891 2892 #if 0 2893 BMenuItem* pasteItem = new BMenuItem("Paste", new BMessage(B_PASTE), 'V'); 2894 menu->AddItem(pasteItem); 2895 menu->AddSeparatorItem(); 2896 #endif 2897 BMenu* arrangeBy = new BMenu(B_TRANSLATE("Arrange by")); 2898 PopulateArrangeByMenu(arrangeBy); 2899 2900 menu->AddItem(arrangeBy); 2901 2902 menu->AddItem(new BMenuItem(B_TRANSLATE("Select"B_UTF8_ELLIPSIS), 2903 new BMessage(kShowSelectionWindow), 'A', B_SHIFT_KEY)); 2904 menu->AddItem(new BMenuItem(B_TRANSLATE("Select all"), 2905 new BMessage(B_SELECT_ALL), 'A')); 2906 if (!IsTrash()) { 2907 menu->AddItem(new BMenuItem(B_TRANSLATE("Open parent"), 2908 new BMessage(kOpenParentDir), B_UP_ARROW)); 2909 } 2910 2911 menu->AddSeparatorItem(); 2912 BMenu* addOnMenuItem = new BMenu(B_TRANSLATE("Add-ons")); 2913 addOnMenuItem->SetFont(be_plain_font); 2914 menu->AddItem(addOnMenuItem); 2915 2916 #if DEBUG 2917 menu->AddSeparatorItem(); 2918 BMenuItem* testing = new BMenuItem("Test icon cache", new BMessage(kTestIconCache)); 2919 menu->AddItem(testing); 2920 #endif 2921 2922 // target items as needed 2923 menu->SetTargetForItems(PoseView()); 2924 #ifdef CUT_COPY_PASTE_IN_CONTEXT_MENU 2925 pasteItem->SetTarget(this); 2926 #endif 2927 } 2928 2929 2930 void 2931 BContainerWindow::AddDropContextMenus(BMenu* menu) 2932 { 2933 menu->AddItem(new BMenuItem(B_TRANSLATE("Create link here"), 2934 new BMessage(kCreateLink))); 2935 menu->AddItem(new BMenuItem(B_TRANSLATE("Move here"), 2936 new BMessage(kMoveSelectionTo))); 2937 menu->AddItem(new BMenuItem(B_TRANSLATE("Copy here"), 2938 new BMessage(kCopySelectionTo))); 2939 menu->AddSeparatorItem(); 2940 menu->AddItem(new BMenuItem(B_TRANSLATE("Cancel"), 2941 new BMessage(kCancelButton))); 2942 } 2943 2944 2945 void 2946 BContainerWindow::AddTrashContextMenus(BMenu* menu) 2947 { 2948 // setup special trash context menu 2949 menu->AddItem(new BMenuItem(B_TRANSLATE("Empty Trash"), 2950 new BMessage(kEmptyTrash))); 2951 menu->AddItem(new BMenuItem(B_TRANSLATE("Open"), 2952 new BMessage(kOpenSelection), 'O')); 2953 menu->AddItem(new BMenuItem(B_TRANSLATE("Get info"), 2954 new BMessage(kGetInfo), 'I')); 2955 menu->SetTargetForItems(PoseView()); 2956 } 2957 2958 2959 void 2960 BContainerWindow::EachAddon(bool (*eachAddon)(const Model*, const char*, 2961 uint32 shortcut, uint32 modifiers, bool primary, void* context), 2962 void* passThru, BObjectList<BString> &mimeTypes) 2963 { 2964 AutoLock<LockingList<AddonShortcut> > lock(fAddonsList); 2965 if (lock.IsLocked()) { 2966 for (int i = fAddonsList->CountItems() - 1; i >= 0; i--) { 2967 struct AddonShortcut* item = fAddonsList->ItemAt(i); 2968 bool primary = false; 2969 2970 if (mimeTypes.CountItems()) { 2971 BFile file(item->model->EntryRef(), B_READ_ONLY); 2972 if (file.InitCheck() == B_OK) { 2973 BAppFileInfo info(&file); 2974 if (info.InitCheck() == B_OK) { 2975 bool secondary = true; 2976 2977 // does this add-on has types set at all? 2978 BMessage message; 2979 if (info.GetSupportedTypes(&message) == B_OK) { 2980 type_code type; 2981 int32 count; 2982 if (message.GetInfo("types", &type, 2983 &count) == B_OK) 2984 secondary = false; 2985 } 2986 2987 // check all supported types if it has some set 2988 if (!secondary) { 2989 for (int32 i = mimeTypes.CountItems(); 2990 !primary && i-- > 0;) { 2991 BString* type = mimeTypes.ItemAt(i); 2992 if (info.IsSupportedType(type->String())) { 2993 BMimeType mimeType(type->String()); 2994 if (info.Supports(&mimeType)) 2995 primary = true; 2996 else 2997 secondary = true; 2998 } 2999 } 3000 } 3001 3002 if (!secondary && !primary) 3003 continue; 3004 } 3005 } 3006 } 3007 ((eachAddon)(item->model, item->model->Name(), item->key, 3008 item->modifiers, primary, passThru)); 3009 } 3010 } 3011 } 3012 3013 3014 void 3015 BContainerWindow::BuildMimeTypeList(BObjectList<BString> &mimeTypes) 3016 { 3017 int32 count = PoseView()->SelectionList()->CountItems(); 3018 if (!count) { 3019 // just add the type of the current directory 3020 AddMimeTypeString(mimeTypes, TargetModel()); 3021 } else { 3022 _UpdateSelectionMIMEInfo(); 3023 for (int32 index = 0; index < count; index++) { 3024 BPose* pose = PoseView()->SelectionList()->ItemAt(index); 3025 AddMimeTypeString(mimeTypes, pose->TargetModel()); 3026 // If it's a symlink, resolves it and add the Target's MimeType 3027 if (pose->TargetModel()->IsSymLink()) { 3028 Model* resolved = new Model( 3029 pose->TargetModel()->EntryRef(), true, true); 3030 if (resolved->InitCheck() == B_OK) { 3031 AddMimeTypeString(mimeTypes, resolved); 3032 } 3033 delete resolved; 3034 } 3035 } 3036 } 3037 } 3038 3039 3040 void 3041 BContainerWindow::BuildAddOnMenu(BMenu* menu) 3042 { 3043 BMenuItem* item = menu->FindItem(B_TRANSLATE("Add-ons")); 3044 if (menu->IndexOf(item) == 0) { 3045 // the folder of the context menu seems to be named "Add-Ons" 3046 // so we just take the last menu item, which is correct if not 3047 // build with debug option 3048 item = menu->ItemAt(menu->CountItems() - 1); 3049 } 3050 if (item == NULL) 3051 return; 3052 3053 menu = item->Submenu(); 3054 if (!menu) 3055 return; 3056 3057 menu->SetFont(be_plain_font); 3058 3059 // found the addons menu, empty it first 3060 for (;;) { 3061 item = menu->RemoveItem((int32)0); 3062 if (!item) 3063 break; 3064 delete item; 3065 } 3066 3067 BObjectList<BMenuItem> primaryList; 3068 BObjectList<BMenuItem> secondaryList; 3069 BObjectList<BString> mimeTypes(10, true); 3070 BuildMimeTypeList(mimeTypes); 3071 3072 AddOneAddonParams params; 3073 params.primaryList = &primaryList; 3074 params.secondaryList = &secondaryList; 3075 3076 // build a list of the MIME types of the selected items 3077 3078 EachAddon(AddOneAddon, ¶ms, mimeTypes); 3079 3080 primaryList.SortItems(CompareLabels); 3081 secondaryList.SortItems(CompareLabels); 3082 3083 int32 count = primaryList.CountItems(); 3084 for (int32 index = 0; index < count; index++) 3085 menu->AddItem(primaryList.ItemAt(index)); 3086 3087 if (count != 0) 3088 menu->AddSeparatorItem(); 3089 3090 count = secondaryList.CountItems(); 3091 for (int32 index = 0; index < count; index++) 3092 menu->AddItem(secondaryList.ItemAt(index)); 3093 3094 menu->SetTargetForItems(this); 3095 } 3096 3097 3098 void 3099 BContainerWindow::UpdateMenu(BMenu* menu, UpdateMenuContext context) 3100 { 3101 const int32 selectCount = PoseView()->SelectionList()->CountItems(); 3102 const int32 count = PoseView()->CountItems(); 3103 3104 if (context == kMenuBarContext) { 3105 EnableNamedMenuItem(menu, kOpenSelection, selectCount > 0); 3106 EnableNamedMenuItem(menu, kGetInfo, selectCount > 0); 3107 EnableNamedMenuItem(menu, kIdentifyEntry, selectCount > 0); 3108 EnableNamedMenuItem(menu, kMoveToTrash, selectCount > 0); 3109 EnableNamedMenuItem(menu, kRestoreFromTrash, selectCount > 0); 3110 EnableNamedMenuItem(menu, kDelete, selectCount > 0); 3111 EnableNamedMenuItem(menu, kDuplicateSelection, selectCount > 0); 3112 } 3113 3114 Model* selectedModel = NULL; 3115 if (selectCount == 1) 3116 selectedModel = PoseView()->SelectionList()->FirstItem()-> 3117 TargetModel(); 3118 3119 if (context == kMenuBarContext || context == kPosePopUpContext) { 3120 SetUpEditQueryItem(menu); 3121 EnableNamedMenuItem(menu, kEditItem, selectCount == 1 3122 && (context == kPosePopUpContext || !PoseView()->ActivePose()) 3123 && selectedModel != NULL 3124 && !selectedModel->IsDesktop() 3125 && !selectedModel->IsRoot() 3126 && !selectedModel->IsTrash() 3127 && !selectedModel->HasLocalizedName()); 3128 SetCutItem(menu); 3129 SetCopyItem(menu); 3130 SetPasteItem(menu); 3131 } 3132 3133 if (context == kMenuBarContext || context == kWindowPopUpContext) { 3134 BMenu* sizeMenu = NULL; 3135 if (BMenuItem* item = menu->FindItem(kIconMode)) { 3136 sizeMenu = item->Submenu(); 3137 } 3138 3139 uint32 viewMode = PoseView()->ViewMode(); 3140 if (sizeMenu) { 3141 if (viewMode == kIconMode) { 3142 int32 iconSize = (int32)PoseView()->IconSizeInt(); 3143 for (int32 i = 0; BMenuItem* item = sizeMenu->ItemAt(i); i++) { 3144 BMessage* message = item->Message(); 3145 if (!message) { 3146 item->SetMarked(false); 3147 continue; 3148 } 3149 int32 size; 3150 if (message->FindInt32("size", &size) < B_OK) 3151 size = -1; 3152 item->SetMarked(iconSize == size); 3153 } 3154 } else { 3155 for (int32 i = 0; BMenuItem* item = sizeMenu->ItemAt(i); i++) 3156 item->SetMarked(false); 3157 } 3158 } 3159 MarkNamedMenuItem(menu, kIconMode, viewMode == kIconMode); 3160 MarkNamedMenuItem(menu, kListMode, viewMode == kListMode); 3161 MarkNamedMenuItem(menu, kMiniIconMode, viewMode == kMiniIconMode); 3162 3163 SetCloseItem(menu); 3164 SetArrangeMenu(menu); 3165 SetPasteItem(menu); 3166 3167 BEntry entry(TargetModel()->EntryRef()); 3168 BDirectory parent; 3169 entry_ref ref; 3170 BEntry root("/"); 3171 3172 bool parentIsRoot = (entry.GetParent(&parent) == B_OK 3173 && parent.GetEntry(&entry) == B_OK 3174 && entry.GetRef(&ref) == B_OK 3175 && entry == root); 3176 3177 EnableNamedMenuItem(menu, kOpenParentDir, !TargetModel()->IsDesktop() 3178 && !TargetModel()->IsRoot() 3179 && (!parentIsRoot 3180 || TrackerSettings().SingleWindowBrowse() 3181 || TrackerSettings().ShowDisksIcon() 3182 || (modifiers() & B_CONTROL_KEY) != 0)); 3183 3184 EnableNamedMenuItem(menu, kEmptyTrash, count > 0); 3185 EnableNamedMenuItem(menu, B_SELECT_ALL, count > 0); 3186 3187 BMenuItem* item = menu->FindItem(B_TRANSLATE("New")); 3188 if (item) { 3189 TemplatesMenu* templateMenu = dynamic_cast<TemplatesMenu*> 3190 (item->Submenu()); 3191 if (templateMenu) 3192 templateMenu->UpdateMenuState(); 3193 } 3194 } 3195 3196 BuildAddOnMenu(menu); 3197 } 3198 3199 3200 void 3201 BContainerWindow::LoadAddOn(BMessage* message) 3202 { 3203 UpdateIfNeeded(); 3204 3205 entry_ref addonRef; 3206 status_t result = message->FindRef("refs", &addonRef); 3207 if (result != B_OK) { 3208 BString buffer(B_TRANSLATE("Error %error loading add-On %name.")); 3209 buffer.ReplaceFirst("%error", strerror(result)); 3210 buffer.ReplaceFirst("%name", addonRef.name); 3211 3212 BAlert* alert = new BAlert("", buffer.String(), B_TRANSLATE("Cancel"), 3213 0, 0, B_WIDTH_AS_USUAL, B_WARNING_ALERT); 3214 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 3215 alert->Go(); 3216 return; 3217 } 3218 3219 // add selected refs to message 3220 BMessage* refs = new BMessage(B_REFS_RECEIVED); 3221 3222 BObjectList<BPose>* list = PoseView()->SelectionList(); 3223 3224 int32 index = 0; 3225 BPose* pose; 3226 while ((pose = list->ItemAt(index++)) != NULL) 3227 refs->AddRef("refs", pose->TargetModel()->EntryRef()); 3228 3229 refs->AddMessenger("TrackerViewToken", BMessenger(PoseView())); 3230 3231 LaunchInNewThread("Add-on", B_NORMAL_PRIORITY, &AddOnThread, refs, 3232 addonRef, *TargetModel()->EntryRef()); 3233 } 3234 3235 3236 void 3237 BContainerWindow::_UpdateSelectionMIMEInfo() 3238 { 3239 BPose* pose; 3240 int32 index = 0; 3241 while ((pose = PoseView()->SelectionList()->ItemAt(index++)) != NULL) { 3242 BString mimeType(pose->TargetModel()->MimeType()); 3243 if (!mimeType.Length() || mimeType.ICompare(B_FILE_MIMETYPE) == 0) { 3244 pose->TargetModel()->Mimeset(true); 3245 if (pose->TargetModel()->IsSymLink()) { 3246 Model* resolved = new Model(pose->TargetModel()->EntryRef(), 3247 true, true); 3248 if (resolved->InitCheck() == B_OK) { 3249 mimeType.SetTo(resolved->MimeType()); 3250 if (!mimeType.Length() 3251 || mimeType.ICompare(B_FILE_MIMETYPE) == 0) { 3252 resolved->Mimeset(true); 3253 } 3254 } 3255 delete resolved; 3256 } 3257 } 3258 } 3259 } 3260 3261 3262 BMenuItem* 3263 BContainerWindow::NewAttributeMenuItem(const char* label, const char* name, 3264 int32 type, float width, int32 align, bool editable, bool statField) 3265 { 3266 return NewAttributeMenuItem(label, name, type, NULL, width, align, 3267 editable, statField); 3268 } 3269 3270 3271 BMenuItem* 3272 BContainerWindow::NewAttributeMenuItem(const char* label, const char* name, 3273 int32 type, const char* displayAs, float width, int32 align, 3274 bool editable, bool statField) 3275 { 3276 BMessage* message = new BMessage(kAttributeItem); 3277 message->AddString("attr_name", name); 3278 message->AddInt32("attr_type", type); 3279 message->AddInt32("attr_hash", (int32)AttrHashString(name, (uint32)type)); 3280 message->AddFloat("attr_width", width); 3281 message->AddInt32("attr_align", align); 3282 if (displayAs != NULL) 3283 message->AddString("attr_display_as", displayAs); 3284 message->AddBool("attr_editable", editable); 3285 message->AddBool("attr_statfield", statField); 3286 3287 BMenuItem* menuItem = new BMenuItem(label, message); 3288 menuItem->SetTarget(PoseView()); 3289 3290 return menuItem; 3291 } 3292 3293 3294 void 3295 BContainerWindow::NewAttributeMenu(BMenu* menu) 3296 { 3297 ASSERT(PoseView()); 3298 3299 BMenuItem* item; 3300 menu->AddItem(item = new BMenuItem(B_TRANSLATE("Copy layout"), 3301 new BMessage(kCopyAttributes))); 3302 item->SetTarget(PoseView()); 3303 menu->AddItem(item = new BMenuItem(B_TRANSLATE("Paste layout"), 3304 new BMessage(kPasteAttributes))); 3305 item->SetTarget(PoseView()); 3306 menu->AddSeparatorItem(); 3307 3308 menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Name"), 3309 kAttrStatName, B_STRING_TYPE, 145, B_ALIGN_LEFT, true, true)); 3310 3311 if (gLocalizedNamePreferred) { 3312 menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Real name"), 3313 kAttrRealName, B_STRING_TYPE, 145, B_ALIGN_LEFT, true, true)); 3314 } 3315 3316 menu->AddItem(NewAttributeMenuItem (B_TRANSLATE("Size"), kAttrStatSize, 3317 B_OFF_T_TYPE, 80, B_ALIGN_RIGHT, false, true)); 3318 3319 menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Modified"), 3320 kAttrStatModified, B_TIME_TYPE, 150, B_ALIGN_LEFT, false, true)); 3321 3322 menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Created"), 3323 kAttrStatCreated, B_TIME_TYPE, 150, B_ALIGN_LEFT, false, true)); 3324 3325 menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Kind"), 3326 kAttrMIMEType, B_MIME_STRING_TYPE, 145, B_ALIGN_LEFT, false, false)); 3327 3328 if (IsTrash() || InTrash()) { 3329 menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Original name"), 3330 kAttrOriginalPath, B_STRING_TYPE, 225, B_ALIGN_LEFT, false, 3331 false)); 3332 } else { 3333 menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Location"), kAttrPath, 3334 B_STRING_TYPE, 225, B_ALIGN_LEFT, false, false)); 3335 } 3336 3337 #ifdef OWNER_GROUP_ATTRIBUTES 3338 menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Owner"), kAttrStatOwner, 3339 B_STRING_TYPE, 60, B_ALIGN_LEFT, false, true)); 3340 3341 menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Group"), kAttrStatGroup, 3342 B_STRING_TYPE, 60, B_ALIGN_LEFT, false, true)); 3343 #endif 3344 3345 menu->AddItem(NewAttributeMenuItem(B_TRANSLATE("Permissions"), 3346 kAttrStatMode, B_STRING_TYPE, 80, B_ALIGN_LEFT, false, true)); 3347 } 3348 3349 3350 void 3351 BContainerWindow::ShowAttributeMenu() 3352 { 3353 ASSERT(fAttrMenu); 3354 fMenuBar->AddItem(fAttrMenu); 3355 } 3356 3357 3358 void 3359 BContainerWindow::HideAttributeMenu() 3360 { 3361 ASSERT(fAttrMenu); 3362 fMenuBar->RemoveItem(fAttrMenu); 3363 } 3364 3365 3366 void 3367 BContainerWindow::MarkAttributeMenu() 3368 { 3369 MarkAttributeMenu(fAttrMenu); 3370 } 3371 3372 3373 void 3374 BContainerWindow::MarkAttributeMenu(BMenu* menu) 3375 { 3376 if (!menu) 3377 return; 3378 3379 int32 count = menu->CountItems(); 3380 for (int32 index = 0; index < count; index++) { 3381 BMenuItem* item = menu->ItemAt(index); 3382 int32 attrHash; 3383 if (item->Message()) { 3384 if (item->Message()->FindInt32("attr_hash", &attrHash) == B_OK) 3385 item->SetMarked(PoseView()->ColumnFor((uint32)attrHash) != 0); 3386 else 3387 item->SetMarked(false); 3388 } 3389 3390 BMenu* submenu = item->Submenu(); 3391 if (submenu) { 3392 int32 count2 = submenu->CountItems(); 3393 for (int32 subindex = 0; subindex < count2; subindex++) { 3394 item = submenu->ItemAt(subindex); 3395 if (item->Message()) { 3396 if (item->Message()->FindInt32("attr_hash", &attrHash) 3397 == B_OK) { 3398 item->SetMarked(PoseView()->ColumnFor((uint32)attrHash) 3399 != 0); 3400 } else 3401 item->SetMarked(false); 3402 } 3403 } 3404 } 3405 } 3406 } 3407 3408 3409 void 3410 BContainerWindow::MarkArrangeByMenu(BMenu* menu) 3411 { 3412 if (!menu) 3413 return; 3414 3415 int32 count = menu->CountItems(); 3416 for (int32 index = 0; index < count; index++) { 3417 BMenuItem* item = menu->ItemAt(index); 3418 if (item->Message()) { 3419 uint32 attrHash; 3420 if (item->Message()->FindInt32("attr_hash", 3421 (int32*)&attrHash) == B_OK) { 3422 item->SetMarked(PoseView()->PrimarySort() == attrHash); 3423 } else if (item->Command() == kArrangeReverseOrder) 3424 item->SetMarked(PoseView()->ReverseSort()); 3425 } 3426 } 3427 } 3428 3429 3430 void 3431 BContainerWindow::AddMimeTypesToMenu() 3432 { 3433 AddMimeTypesToMenu(fAttrMenu); 3434 } 3435 3436 3437 // Adds a menu for a specific MIME type if it doesn't exist already. 3438 // Returns the menu, if it existed or not. 3439 BMenu* 3440 BContainerWindow::AddMimeMenu(const BMimeType& mimeType, bool isSuperType, 3441 BMenu* menu, int32 start) 3442 { 3443 AutoLock<BLooper> _(menu->Looper()); 3444 3445 if (!mimeType.IsValid()) 3446 return NULL; 3447 3448 // Check if we already have an entry for this MIME type in the menu. 3449 for (int32 i = start; BMenuItem* item = menu->ItemAt(i); i++) { 3450 BMessage* message = item->Message(); 3451 if (message == NULL) 3452 continue; 3453 3454 const char* type; 3455 if (message->FindString("mimetype", &type) == B_OK 3456 && !strcmp(mimeType.Type(), type)) { 3457 return item->Submenu(); 3458 } 3459 } 3460 3461 BMessage attrInfo; 3462 char description[B_MIME_TYPE_LENGTH]; 3463 const char* label = mimeType.Type(); 3464 3465 if (!mimeType.IsInstalled()) 3466 return NULL; 3467 3468 // only add things to menu which have "user-visible" data 3469 if (mimeType.GetAttrInfo(&attrInfo) != B_OK) 3470 return NULL; 3471 3472 if (mimeType.GetShortDescription(description) == B_OK && description[0]) 3473 label = description; 3474 3475 // go through each field in meta mime and add it to a menu 3476 BMenu* mimeMenu = NULL; 3477 if (isSuperType) { 3478 // If it is a supertype, we create the menu anyway as it may have 3479 // submenus later on. 3480 mimeMenu = new BMenu(label); 3481 BFont font; 3482 menu->GetFont(&font); 3483 mimeMenu->SetFont(&font); 3484 } 3485 3486 int32 index = -1; 3487 const char* publicName; 3488 while (attrInfo.FindString("attr:public_name", ++index, &publicName) 3489 == B_OK) { 3490 if (!attrInfo.FindBool("attr:viewable", index)) { 3491 // don't add if attribute not viewable 3492 continue; 3493 } 3494 3495 int32 type; 3496 int32 align; 3497 int32 width; 3498 bool editable; 3499 const char* attrName; 3500 if (attrInfo.FindString("attr:name", index, &attrName) != B_OK 3501 || attrInfo.FindInt32("attr:type", index, &type) != B_OK 3502 || attrInfo.FindBool("attr:editable", index, &editable) != B_OK 3503 || attrInfo.FindInt32("attr:width", index, &width) != B_OK 3504 || attrInfo.FindInt32("attr:alignment", index, &align) != B_OK) 3505 continue; 3506 3507 BString displayAs; 3508 attrInfo.FindString("attr:display_as", index, &displayAs); 3509 3510 if (mimeMenu == NULL) { 3511 // do a lazy allocation of the menu 3512 mimeMenu = new BMenu(label); 3513 BFont font; 3514 menu->GetFont(&font); 3515 mimeMenu->SetFont(&font); 3516 } 3517 mimeMenu->AddItem(NewAttributeMenuItem(publicName, attrName, type, 3518 displayAs.String(), width, align, editable, false)); 3519 } 3520 3521 if (mimeMenu == NULL) 3522 return NULL; 3523 3524 BMessage* message = new BMessage(kMIMETypeItem); 3525 message->AddString("mimetype", mimeType.Type()); 3526 menu->AddItem(new IconMenuItem(mimeMenu, message, mimeType.Type(), 3527 B_MINI_ICON)); 3528 3529 return mimeMenu; 3530 } 3531 3532 3533 void 3534 BContainerWindow::AddMimeTypesToMenu(BMenu* menu) 3535 { 3536 if (!menu) 3537 return; 3538 3539 // Remove old mime type menus 3540 int32 start = menu->CountItems(); 3541 while (start > 0 && menu->ItemAt(start - 1)->Submenu() != NULL) { 3542 delete menu->RemoveItem(start - 1); 3543 start--; 3544 } 3545 3546 // Add a separator item if there is none yet 3547 if (start > 0 3548 && dynamic_cast<BSeparatorItem*>(menu->ItemAt(start - 1)) == NULL) 3549 menu->AddSeparatorItem(); 3550 3551 // Add MIME type in case we're a default query type window 3552 BPath path; 3553 if (TargetModel() != NULL) { 3554 TargetModel()->GetPath(&path); 3555 if (path.InitCheck() == B_OK 3556 && strstr(path.Path(), "/" kQueryTemplates "/") != NULL) { 3557 // demangle MIME type name 3558 BString name(TargetModel()->Name()); 3559 name.ReplaceFirst('_', '/'); 3560 3561 PoseView()->AddMimeType(name.String()); 3562 } 3563 } 3564 3565 // Add MIME type menus 3566 3567 int32 typeCount = PoseView()->CountMimeTypes(); 3568 3569 for (int32 index = 0; index < typeCount; index++) { 3570 BMimeType mimeType(PoseView()->MimeTypeAt(index)); 3571 if (mimeType.InitCheck() == B_OK) { 3572 BMimeType superType; 3573 mimeType.GetSupertype(&superType); 3574 if (superType.InitCheck() == B_OK) { 3575 BMenu* superMenu = AddMimeMenu(superType, true, menu, start); 3576 if (superMenu != NULL) { 3577 // We have a supertype menu. 3578 AddMimeMenu(mimeType, false, superMenu, 0); 3579 } 3580 } 3581 } 3582 } 3583 3584 // remove empty super menus, promote sub-types if needed 3585 3586 for (int32 index = 0; index < typeCount; index++) { 3587 BMimeType mimeType(PoseView()->MimeTypeAt(index)); 3588 BMimeType superType; 3589 mimeType.GetSupertype(&superType); 3590 3591 BMenu* superMenu = AddMimeMenu(superType, true, menu, start); 3592 if (superMenu == NULL) 3593 continue; 3594 3595 int32 itemsFound = 0; 3596 int32 menusFound = 0; 3597 for (int32 i = 0; BMenuItem* item = superMenu->ItemAt(i); i++) { 3598 if (item->Submenu() != NULL) 3599 menusFound++; 3600 else 3601 itemsFound++; 3602 } 3603 3604 if (itemsFound == 0) { 3605 if (menusFound != 0) { 3606 // promote types to the top level 3607 while (BMenuItem* item = superMenu->RemoveItem((int32)0)) { 3608 menu->AddItem(item); 3609 } 3610 } 3611 3612 menu->RemoveItem(superMenu->Superitem()); 3613 delete superMenu->Superitem(); 3614 } 3615 } 3616 3617 // remove separator if it's the only item in menu 3618 BMenuItem* item = menu->ItemAt(menu->CountItems() - 1); 3619 if (dynamic_cast<BSeparatorItem*>(item) != NULL) { 3620 menu->RemoveItem(item); 3621 delete item; 3622 } 3623 3624 MarkAttributeMenu(menu); 3625 } 3626 3627 3628 BHandler* 3629 BContainerWindow::ResolveSpecifier(BMessage* message, int32 index, 3630 BMessage* specifier, int32 form, const char* property) 3631 { 3632 if (strcmp(property, "Poses") == 0) { 3633 // PRINT(("BContainerWindow::ResolveSpecifier %s\n", property)); 3634 message->PopSpecifier(); 3635 return PoseView(); 3636 } 3637 3638 return _inherited::ResolveSpecifier(message, index, specifier, 3639 form, property); 3640 } 3641 3642 3643 PiggybackTaskLoop* 3644 BContainerWindow::DelayedTaskLoop() 3645 { 3646 if (!fTaskLoop) 3647 fTaskLoop = new PiggybackTaskLoop; 3648 3649 return fTaskLoop; 3650 } 3651 3652 3653 bool 3654 BContainerWindow::NeedsDefaultStateSetup() 3655 { 3656 if (!TargetModel()) 3657 return false; 3658 3659 if (TargetModel()->IsRoot()) 3660 // don't try to set up anything if we are root 3661 return false; 3662 3663 WindowStateNodeOpener opener(this, false); 3664 if (!opener.StreamNode()) 3665 // can't read state, give up 3666 return false; 3667 3668 return !NodeHasSavedState(opener.Node()); 3669 } 3670 3671 3672 bool 3673 BContainerWindow::DefaultStateSourceNode(const char* name, BNode* result, 3674 bool createNew, bool createFolder) 3675 { 3676 // PRINT(("looking for default state in tracker settings dir\n")); 3677 BPath settingsPath; 3678 if (FSFindTrackerSettingsDir(&settingsPath) != B_OK) 3679 return false; 3680 3681 BDirectory dir(settingsPath.Path()); 3682 3683 BPath path(settingsPath); 3684 path.Append(name); 3685 if (!BEntry(path.Path()).Exists()) { 3686 if (!createNew) 3687 return false; 3688 3689 BPath tmpPath(settingsPath); 3690 for (;;) { 3691 // deal with several levels of folders 3692 const char* nextSlash = strchr(name, '/'); 3693 if (!nextSlash) 3694 break; 3695 3696 BString tmp; 3697 tmp.SetTo(name, nextSlash - name); 3698 tmpPath.Append(tmp.String()); 3699 3700 mkdir(tmpPath.Path(), 0777); 3701 3702 name = nextSlash + 1; 3703 if (!name[0]) { 3704 // can't deal with a slash at end 3705 return false; 3706 } 3707 } 3708 3709 if (createFolder) { 3710 if (mkdir(path.Path(), 0777) < 0) 3711 return false; 3712 } else { 3713 BFile file; 3714 if (dir.CreateFile(name, &file) != B_OK) 3715 return false; 3716 } 3717 } 3718 3719 // PRINT(("using default state from %s\n", path.Path())); 3720 result->SetTo(path.Path()); 3721 return result->InitCheck() == B_OK; 3722 } 3723 3724 3725 void 3726 BContainerWindow::SetUpDefaultState() 3727 { 3728 BNode defaultingNode; 3729 // this is where we'll ulitimately get the state from 3730 bool gotDefaultingNode = 0; 3731 bool shouldStagger = false; 3732 3733 ASSERT(TargetModel()); 3734 3735 PRINT(("folder %s does not have any saved state\n", TargetModel()->Name())); 3736 3737 WindowStateNodeOpener opener(this, true); 3738 // this is our destination node, whatever it is for this window 3739 if (!opener.StreamNode()) 3740 return; 3741 3742 if (!TargetModel()->IsRoot()) { 3743 BDirectory desktop; 3744 FSGetDeskDir(&desktop); 3745 3746 // try copying state from our parent directory, unless it is the 3747 // desktop folder 3748 BEntry entry(TargetModel()->EntryRef()); 3749 BNode parent; 3750 if (FSGetParentVirtualDirectoryAware(entry, parent) == B_OK 3751 && parent != desktop) { 3752 PRINT(("looking at parent for state\n")); 3753 if (NodeHasSavedState(&parent)) { 3754 PRINT(("got state from parent\n")); 3755 defaultingNode = parent; 3756 gotDefaultingNode = true; 3757 // when getting state from parent, stagger the window 3758 shouldStagger = true; 3759 } 3760 } 3761 } 3762 3763 if (!gotDefaultingNode 3764 // parent didn't have any state, use the template directory from 3765 // tracker settings folder for what our state should be 3766 // For simplicity we are not picking up the most recent 3767 // changes that didn't get committed if home is still open in 3768 // a window, that's probably not a problem; would be OK if state 3769 // got committed after every change 3770 && !DefaultStateSourceNode(kDefaultFolderTemplate, &defaultingNode, true)) 3771 return; 3772 3773 // copy over the attributes 3774 3775 // set up a filter of the attributes we want copied 3776 const char* allowAttrs[] = { 3777 kAttrWindowFrame, 3778 kAttrWindowWorkspace, 3779 kAttrViewState, 3780 kAttrViewStateForeign, 3781 kAttrColumns, 3782 kAttrColumnsForeign, 3783 0 3784 }; 3785 3786 // copy over attributes that apply; transform them properly, stripping 3787 // parts that do not apply, adding a window stagger, etc. 3788 3789 StaggerOneParams params; 3790 params.rectFromParent = shouldStagger; 3791 SelectiveAttributeTransformer frameOffsetter(kAttrWindowFrame, 3792 OffsetFrameOne, ¶ms); 3793 SelectiveAttributeTransformer scrollOriginCleaner(kAttrViewState, 3794 ClearViewOriginOne, ¶ms); 3795 3796 // do it 3797 AttributeStreamMemoryNode memoryNode; 3798 NamesToAcceptAttrFilter filter(allowAttrs); 3799 AttributeStreamFileNode fileNode(&defaultingNode); 3800 3801 *opener.StreamNode() << scrollOriginCleaner << frameOffsetter 3802 << memoryNode << filter << fileNode; 3803 } 3804 3805 3806 void 3807 BContainerWindow::RestoreWindowState(AttributeStreamNode* node) 3808 { 3809 if (node == NULL || dynamic_cast<BDeskWindow*>(this)) { 3810 // don't restore any window state if we are a desktop window 3811 return; 3812 } 3813 3814 const char* rectAttributeName; 3815 const char* workspaceAttributeName; 3816 if (TargetModel()->IsRoot()) { 3817 rectAttributeName = kAttrDisksFrame; 3818 workspaceAttributeName = kAttrDisksWorkspace; 3819 } else { 3820 rectAttributeName = kAttrWindowFrame; 3821 workspaceAttributeName = kAttrWindowWorkspace; 3822 } 3823 3824 BRect frame(Frame()); 3825 if (node->Read(rectAttributeName, 0, B_RECT_TYPE, sizeof(BRect), &frame) 3826 == sizeof(BRect)) { 3827 MoveTo(frame.LeftTop()); 3828 ResizeTo(frame.Width(), frame.Height()); 3829 } else 3830 sNewWindRect.OffsetBy(kWindowStaggerBy, kWindowStaggerBy); 3831 3832 fPreviousBounds = Bounds(); 3833 3834 uint32 workspace; 3835 if ((fContainerWindowFlags & kRestoreWorkspace) 3836 && node->Read(workspaceAttributeName, 0, B_INT32_TYPE, sizeof(uint32), 3837 &workspace) == sizeof(uint32)) 3838 SetWorkspaces(workspace); 3839 3840 if (fContainerWindowFlags & kIsHidden) 3841 Minimize(true); 3842 3843 #ifdef __HAIKU__ 3844 // restore window decor settings 3845 int32 size = node->Contains(kAttrWindowDecor, B_RAW_TYPE); 3846 if (size > 0) { 3847 char buffer[size]; 3848 if ((fContainerWindowFlags & kRestoreDecor) 3849 && node->Read(kAttrWindowDecor, 0, B_RAW_TYPE, size, buffer) 3850 == size) { 3851 BMessage decorSettings; 3852 if (decorSettings.Unflatten(buffer) == B_OK) 3853 SetDecoratorSettings(decorSettings); 3854 } 3855 } 3856 #endif // __HAIKU__ 3857 } 3858 3859 3860 void 3861 BContainerWindow::RestoreWindowState(const BMessage &message) 3862 { 3863 if (dynamic_cast<BDeskWindow*>(this)) { 3864 // don't restore any window state if we are a desktop window 3865 return; 3866 } 3867 3868 const char* rectAttributeName; 3869 const char* workspaceAttributeName; 3870 if (TargetModel()->IsRoot()) { 3871 rectAttributeName = kAttrDisksFrame; 3872 workspaceAttributeName = kAttrDisksWorkspace; 3873 } else { 3874 rectAttributeName = kAttrWindowFrame; 3875 workspaceAttributeName = kAttrWindowWorkspace; 3876 } 3877 3878 BRect frame(Frame()); 3879 if (message.FindRect(rectAttributeName, &frame) == B_OK) { 3880 MoveTo(frame.LeftTop()); 3881 ResizeTo(frame.Width(), frame.Height()); 3882 } else 3883 sNewWindRect.OffsetBy(kWindowStaggerBy, kWindowStaggerBy); 3884 3885 uint32 workspace; 3886 if ((fContainerWindowFlags & kRestoreWorkspace) 3887 && message.FindInt32(workspaceAttributeName, 3888 (int32*)&workspace) == B_OK) { 3889 SetWorkspaces(workspace); 3890 } 3891 3892 if (fContainerWindowFlags & kIsHidden) 3893 Minimize(true); 3894 3895 #ifdef __HAIKU__ 3896 // restore window decor settings 3897 BMessage decorSettings; 3898 if ((fContainerWindowFlags & kRestoreDecor) 3899 && message.FindMessage(kAttrWindowDecor, &decorSettings) == B_OK) { 3900 SetDecoratorSettings(decorSettings); 3901 } 3902 #endif // __HAIKU__ 3903 } 3904 3905 3906 void 3907 BContainerWindow::SaveWindowState(AttributeStreamNode* node) 3908 { 3909 ASSERT(node); 3910 const char* rectAttributeName; 3911 const char* workspaceAttributeName; 3912 if (TargetModel() && TargetModel()->IsRoot()) { 3913 rectAttributeName = kAttrDisksFrame; 3914 workspaceAttributeName = kAttrDisksWorkspace; 3915 } else { 3916 rectAttributeName = kAttrWindowFrame; 3917 workspaceAttributeName = kAttrWindowWorkspace; 3918 } 3919 3920 // node is null if it already got deleted 3921 BRect frame(Frame()); 3922 node->Write(rectAttributeName, 0, B_RECT_TYPE, sizeof(BRect), &frame); 3923 3924 uint32 workspaces = Workspaces(); 3925 node->Write(workspaceAttributeName, 0, B_INT32_TYPE, sizeof(uint32), 3926 &workspaces); 3927 3928 #ifdef __HAIKU__ 3929 BMessage decorSettings; 3930 if (GetDecoratorSettings(&decorSettings) == B_OK) { 3931 int32 size = decorSettings.FlattenedSize(); 3932 char buffer[size]; 3933 if (decorSettings.Flatten(buffer, size) == B_OK) { 3934 node->Write(kAttrWindowDecor, 0, B_RAW_TYPE, size, buffer); 3935 } 3936 } 3937 #endif // __HAIKU__ 3938 } 3939 3940 3941 void 3942 BContainerWindow::SaveWindowState(BMessage &message) const 3943 { 3944 const char* rectAttributeName; 3945 const char* workspaceAttributeName; 3946 3947 if (TargetModel() && TargetModel()->IsRoot()) { 3948 rectAttributeName = kAttrDisksFrame; 3949 workspaceAttributeName = kAttrDisksWorkspace; 3950 } else { 3951 rectAttributeName = kAttrWindowFrame; 3952 workspaceAttributeName = kAttrWindowWorkspace; 3953 } 3954 3955 // node is null if it already got deleted 3956 BRect frame(Frame()); 3957 message.AddRect(rectAttributeName, frame); 3958 message.AddInt32(workspaceAttributeName, (int32)Workspaces()); 3959 3960 #ifdef __HAIKU__ 3961 BMessage decorSettings; 3962 if (GetDecoratorSettings(&decorSettings) == B_OK) { 3963 message.AddMessage(kAttrWindowDecor, &decorSettings); 3964 } 3965 #endif // __HAIKU__ 3966 } 3967 3968 3969 status_t 3970 BContainerWindow::DragStart(const BMessage* dragMessage) 3971 { 3972 if (dragMessage == NULL) 3973 return B_ERROR; 3974 3975 // if already dragging, or 3976 // if all the refs match 3977 if (Dragging() 3978 && SpringLoadedFolderCompareMessages(dragMessage, fDragMessage)) { 3979 return B_OK; 3980 } 3981 3982 // cache the current drag message 3983 // build a list of the mimetypes in the message 3984 SpringLoadedFolderCacheDragData(dragMessage, &fDragMessage, 3985 &fCachedTypesList); 3986 3987 fWaitingForRefs = true; 3988 3989 return B_OK; 3990 } 3991 3992 3993 void 3994 BContainerWindow::DragStop() 3995 { 3996 delete fDragMessage; 3997 fDragMessage = NULL; 3998 3999 delete fCachedTypesList; 4000 fCachedTypesList = NULL; 4001 4002 fWaitingForRefs = false; 4003 } 4004 4005 4006 void 4007 BContainerWindow::ShowSelectionWindow() 4008 { 4009 if (fSelectionWindow == NULL) { 4010 fSelectionWindow = new SelectionWindow(this); 4011 fSelectionWindow->Show(); 4012 } else if (fSelectionWindow->Lock()) { 4013 // The window is already there, just bring it close 4014 fSelectionWindow->MoveCloseToMouse(); 4015 if (fSelectionWindow->IsHidden()) 4016 fSelectionWindow->Show(); 4017 4018 fSelectionWindow->Unlock(); 4019 } 4020 } 4021 4022 4023 void 4024 BContainerWindow::ShowNavigator(bool show) 4025 { 4026 if (PoseView()->IsDesktopWindow() || !TargetModel()->IsDirectory() 4027 || fPoseView->IsFilePanel()) { 4028 return; 4029 } 4030 4031 if (show) { 4032 if (Navigator() && !Navigator()->IsHidden()) 4033 return; 4034 4035 if (Navigator() == NULL) { 4036 BRect rect(Bounds()); 4037 rect.top = KeyMenuBar()->Bounds().Height() + 1; 4038 rect.bottom = rect.top + BNavigator::CalcNavigatorHeight(); 4039 fNavigator = new BNavigator(TargetModel(), rect); 4040 AddChild(fNavigator); 4041 } 4042 4043 if (Navigator()->IsHidden()) { 4044 if (Navigator()->Bounds().top == 0) 4045 Navigator()->MoveTo(0, KeyMenuBar()->Bounds().Height() + 1); 4046 // This is if the navigator was created with a .top = 0. 4047 Navigator()->Show(); 4048 } 4049 4050 float displacement = Navigator()->Frame().Height() + 1; 4051 4052 PoseView()->MoveBy(0, displacement); 4053 PoseView()->ResizeBy(0, -displacement); 4054 4055 if (PoseView()->VScrollBar()) { 4056 PoseView()->VScrollBar()->MoveBy(0, displacement); 4057 PoseView()->VScrollBar()->ResizeBy(0, -displacement); 4058 PoseView()->UpdateScrollRange(); 4059 } 4060 } else { 4061 if (!Navigator() || Navigator()->IsHidden()) 4062 return; 4063 4064 float displacement = Navigator()->Frame().Height() + 1; 4065 4066 PoseView()->ResizeBy(0, displacement); 4067 PoseView()->MoveBy(0, -displacement); 4068 4069 if (PoseView()->VScrollBar()) { 4070 PoseView()->VScrollBar()->ResizeBy(0, displacement); 4071 PoseView()->VScrollBar()->MoveBy(0, -displacement); 4072 PoseView()->UpdateScrollRange(); 4073 } 4074 4075 fNavigator->Hide(); 4076 } 4077 } 4078 4079 4080 void 4081 BContainerWindow::SetSingleWindowBrowseShortcuts(bool enabled) 4082 { 4083 if (PoseView()->IsDesktopWindow()) 4084 return; 4085 4086 if (enabled) { 4087 if (!Navigator()) 4088 return; 4089 4090 RemoveShortcut(B_DOWN_ARROW, B_COMMAND_KEY | B_OPTION_KEY); 4091 RemoveShortcut(B_UP_ARROW, B_COMMAND_KEY); 4092 RemoveShortcut(B_UP_ARROW, B_COMMAND_KEY | B_OPTION_KEY); 4093 4094 AddShortcut(B_LEFT_ARROW, B_COMMAND_KEY, 4095 new BMessage(kNavigatorCommandBackward), Navigator()); 4096 AddShortcut(B_RIGHT_ARROW, B_COMMAND_KEY, 4097 new BMessage(kNavigatorCommandForward), Navigator()); 4098 AddShortcut(B_UP_ARROW, B_COMMAND_KEY, 4099 new BMessage(kNavigatorCommandUp), Navigator()); 4100 4101 AddShortcut(B_LEFT_ARROW, B_OPTION_KEY | B_COMMAND_KEY, 4102 new BMessage(kNavigatorCommandBackward), Navigator()); 4103 AddShortcut(B_RIGHT_ARROW, B_OPTION_KEY | B_COMMAND_KEY, 4104 new BMessage(kNavigatorCommandForward), Navigator()); 4105 AddShortcut(B_UP_ARROW, B_OPTION_KEY | B_COMMAND_KEY, 4106 new BMessage(kNavigatorCommandUp), Navigator()); 4107 AddShortcut(B_DOWN_ARROW, B_OPTION_KEY | B_COMMAND_KEY, 4108 new BMessage(kOpenSelection), PoseView()); 4109 AddShortcut('L', B_COMMAND_KEY, 4110 new BMessage(kNavigatorCommandSetFocus), Navigator()); 4111 4112 } else { 4113 RemoveShortcut(B_LEFT_ARROW, B_COMMAND_KEY); 4114 RemoveShortcut(B_RIGHT_ARROW, B_COMMAND_KEY); 4115 RemoveShortcut(B_UP_ARROW, B_COMMAND_KEY); 4116 // This is added again, below, with a new meaning. 4117 4118 RemoveShortcut(B_LEFT_ARROW, B_OPTION_KEY | B_COMMAND_KEY); 4119 RemoveShortcut(B_RIGHT_ARROW, B_OPTION_KEY | B_COMMAND_KEY); 4120 RemoveShortcut(B_UP_ARROW, B_OPTION_KEY | B_COMMAND_KEY); 4121 RemoveShortcut(B_DOWN_ARROW, B_COMMAND_KEY | B_OPTION_KEY); 4122 // This also changes meaning, added again below. 4123 4124 AddShortcut(B_DOWN_ARROW, B_COMMAND_KEY | B_OPTION_KEY, 4125 new BMessage(kOpenSelection), PoseView()); 4126 AddShortcut(B_UP_ARROW, B_COMMAND_KEY, 4127 new BMessage(kOpenParentDir), PoseView()); 4128 // We change the meaning from kNavigatorCommandUp 4129 // to kOpenParentDir. 4130 AddShortcut(B_UP_ARROW, B_COMMAND_KEY | B_OPTION_KEY, 4131 new BMessage(kOpenParentDir), PoseView()); 4132 // command + option results in closing the parent window 4133 RemoveShortcut('L', B_COMMAND_KEY); 4134 } 4135 } 4136 4137 4138 void 4139 BContainerWindow::SetPathWatchingEnabled(bool enable) 4140 { 4141 if (IsPathWatchingEnabled()) { 4142 stop_watching(this); 4143 fIsWatchingPath = false; 4144 } 4145 4146 if (enable) { 4147 if (TargetModel() != NULL) { 4148 BEntry entry; 4149 4150 TargetModel()->GetEntry(&entry); 4151 status_t err; 4152 do { 4153 err = entry.GetParent(&entry); 4154 if (err != B_OK) 4155 break; 4156 4157 char name[B_FILE_NAME_LENGTH]; 4158 entry.GetName(name); 4159 if (strcmp(name, "/") == 0) 4160 break; 4161 4162 node_ref ref; 4163 entry.GetNodeRef(&ref); 4164 watch_node(&ref, B_WATCH_NAME, this); 4165 } while (err == B_OK); 4166 4167 fIsWatchingPath = err == B_OK; 4168 } else 4169 fIsWatchingPath = false; 4170 } 4171 } 4172 4173 4174 void 4175 BContainerWindow::PulseTaskLoop() 4176 { 4177 if (fTaskLoop) 4178 fTaskLoop->PulseMe(); 4179 } 4180 4181 4182 void 4183 BContainerWindow::PopulateArrangeByMenu(BMenu* menu) 4184 { 4185 if (!fAttrMenu || !menu) 4186 return; 4187 // empty fArrangeByMenu... 4188 BMenuItem* item; 4189 while ((item = menu->RemoveItem((int32)0)) != NULL) 4190 delete item; 4191 4192 int32 itemCount = fAttrMenu->CountItems(); 4193 for (int32 i = 0; i < itemCount; i++) { 4194 item = fAttrMenu->ItemAt(i); 4195 if (item->Command() == kAttributeItem) { 4196 BMessage* message = new BMessage(*(item->Message())); 4197 message->what = kArrangeBy; 4198 BMenuItem* newItem = new BMenuItem(item->Label(), message); 4199 newItem->SetTarget(PoseView()); 4200 menu->AddItem(newItem); 4201 } 4202 } 4203 4204 menu->AddSeparatorItem(); 4205 4206 item = new BMenuItem(B_TRANSLATE("Reverse order"), 4207 new BMessage(kArrangeReverseOrder)); 4208 4209 item->SetTarget(PoseView()); 4210 menu->AddItem(item); 4211 menu->AddSeparatorItem(); 4212 4213 4214 item = new BMenuItem(B_TRANSLATE("Clean up"), new BMessage(kCleanup), 4215 'K'); 4216 item->SetTarget(PoseView()); 4217 menu->AddItem(item); 4218 } 4219 4220 4221 // #pragma mark - 4222 4223 4224 WindowStateNodeOpener::WindowStateNodeOpener(BContainerWindow* window, 4225 bool forWriting) 4226 : fModelOpener(NULL), 4227 fNode(NULL), 4228 fStreamNode(NULL) 4229 { 4230 if (window->TargetModel() && window->TargetModel()->IsRoot()) { 4231 BDirectory dir; 4232 if (FSGetDeskDir(&dir) == B_OK) { 4233 fNode = new BDirectory(dir); 4234 fStreamNode = new AttributeStreamFileNode(fNode); 4235 } 4236 } else if (window->TargetModel()){ 4237 fModelOpener = new ModelNodeLazyOpener(window->TargetModel(), 4238 forWriting, false); 4239 if (fModelOpener->IsOpen(forWriting)) { 4240 fStreamNode = new AttributeStreamFileNode( 4241 fModelOpener->TargetModel()->Node()); 4242 } 4243 } 4244 } 4245 4246 WindowStateNodeOpener::~WindowStateNodeOpener() 4247 { 4248 delete fModelOpener; 4249 delete fNode; 4250 delete fStreamNode; 4251 } 4252 4253 4254 void 4255 WindowStateNodeOpener::SetTo(const BDirectory* node) 4256 { 4257 delete fModelOpener; 4258 delete fNode; 4259 delete fStreamNode; 4260 4261 fModelOpener = NULL; 4262 fNode = new BDirectory(*node); 4263 fStreamNode = new AttributeStreamFileNode(fNode); 4264 } 4265 4266 4267 void 4268 WindowStateNodeOpener::SetTo(const BEntry* entry, bool forWriting) 4269 { 4270 delete fModelOpener; 4271 delete fNode; 4272 delete fStreamNode; 4273 4274 fModelOpener = NULL; 4275 fNode = new BFile(entry, (uint32)(forWriting ? O_RDWR : O_RDONLY)); 4276 fStreamNode = new AttributeStreamFileNode(fNode); 4277 } 4278 4279 4280 void 4281 WindowStateNodeOpener::SetTo(Model* model, bool forWriting) 4282 { 4283 delete fModelOpener; 4284 delete fNode; 4285 delete fStreamNode; 4286 4287 fNode = NULL; 4288 fStreamNode = NULL; 4289 fModelOpener = new ModelNodeLazyOpener(model, forWriting, false); 4290 if (fModelOpener->IsOpen(forWriting)) { 4291 fStreamNode = new AttributeStreamFileNode( 4292 fModelOpener->TargetModel()->Node()); 4293 } 4294 } 4295 4296 4297 AttributeStreamNode* 4298 WindowStateNodeOpener::StreamNode() const 4299 { 4300 return fStreamNode; 4301 } 4302 4303 4304 BNode* 4305 WindowStateNodeOpener::Node() const 4306 { 4307 if (!fStreamNode) 4308 return NULL; 4309 4310 if (fNode) 4311 return fNode; 4312 4313 return fModelOpener->TargetModel()->Node(); 4314 } 4315 4316 4317 // #pragma mark - 4318 4319 4320 BackgroundView::BackgroundView(BRect frame) 4321 : BView(frame, "", B_FOLLOW_ALL, 4322 B_FRAME_EVENTS | B_WILL_DRAW | B_PULSE_NEEDED) 4323 { 4324 } 4325 4326 4327 void 4328 BackgroundView::AttachedToWindow() 4329 { 4330 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 4331 } 4332 4333 4334 void 4335 BackgroundView::FrameResized(float, float) 4336 { 4337 Invalidate(); 4338 } 4339 4340 4341 void 4342 BackgroundView::PoseViewFocused(bool focused) 4343 { 4344 Invalidate(); 4345 4346 BContainerWindow* window = dynamic_cast<BContainerWindow*>(Window()); 4347 if (!window) 4348 return; 4349 4350 BScrollBar* hScrollBar = window->PoseView()->HScrollBar(); 4351 if (hScrollBar != NULL) 4352 hScrollBar->SetBorderHighlighted(focused); 4353 4354 BScrollBar* vScrollBar = window->PoseView()->VScrollBar(); 4355 if (vScrollBar != NULL) 4356 vScrollBar->SetBorderHighlighted(focused); 4357 4358 BCountView* countView = window->PoseView()->CountView(); 4359 if (countView != NULL) 4360 countView->SetBorderHighlighted(focused); 4361 } 4362 4363 4364 void 4365 BackgroundView::WindowActivated(bool) 4366 { 4367 Invalidate(); 4368 } 4369 4370 4371 void 4372 BackgroundView::Draw(BRect updateRect) 4373 { 4374 BContainerWindow* window = dynamic_cast<BContainerWindow*>(Window()); 4375 if (!window) 4376 return; 4377 4378 BPoseView* poseView = window->PoseView(); 4379 BRect frame(poseView->Frame()); 4380 frame.InsetBy(-1, -1); 4381 frame.top -= kTitleViewHeight; 4382 frame.bottom += B_H_SCROLL_BAR_HEIGHT; 4383 frame.right += B_V_SCROLL_BAR_WIDTH; 4384 4385 if (be_control_look != NULL) { 4386 uint32 flags = 0; 4387 if (window->IsActive() && window->PoseView()->IsFocus()) 4388 flags |= BControlLook::B_FOCUSED; 4389 4390 frame.top--; 4391 frame.InsetBy(-1, -1); 4392 BRect rect(frame); 4393 rgb_color base = ui_color(B_PANEL_BACKGROUND_COLOR); 4394 4395 BScrollBar* hScrollBar = poseView->HScrollBar(); 4396 BScrollBar* vScrollBar = poseView->VScrollBar(); 4397 4398 BRect verticalScrollBarFrame(0, 0, -1, -1); 4399 if (vScrollBar) 4400 verticalScrollBarFrame = vScrollBar->Frame(); 4401 BRect horizontalScrollBarFrame(0, 0, -1, -1); 4402 if (hScrollBar) { 4403 horizontalScrollBarFrame = hScrollBar->Frame(); 4404 // CountView extends horizontal scroll bar frame: 4405 horizontalScrollBarFrame.left = frame.left + 1; 4406 } 4407 4408 be_control_look->DrawScrollViewFrame(this, rect, updateRect, 4409 verticalScrollBarFrame, horizontalScrollBarFrame, base, 4410 B_FANCY_BORDER, flags); 4411 4412 return; 4413 } 4414 4415 SetHighColor(100, 100, 100); 4416 StrokeRect(frame); 4417 4418 // draw the pose view focus 4419 if (window->IsActive() && window->PoseView()->IsFocus()) { 4420 frame.InsetBy(-2, -2); 4421 SetHighColor(keyboard_navigation_color()); 4422 StrokeRect(frame); 4423 } 4424 } 4425 4426 4427 void 4428 BackgroundView::Pulse() 4429 { 4430 BContainerWindow* window = dynamic_cast<BContainerWindow*>(Window()); 4431 if (window) 4432 window->PulseTaskLoop(); 4433 } 4434 4435