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