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