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