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