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