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