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