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