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