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