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