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