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