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