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