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