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