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