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 30 trademarks of Be Incorporated in the United States and other countries. 31 Other brand product names are registered trademarks or trademarks of 32 their respective holders. All rights reserved. 33 */ 34 35 // NavMenu is a hierarchical menu of volumes, folders, files and queries 36 // displays icons, uses the SlowMenu API for full interruptability 37 38 #include <string.h> 39 #include <stdlib.h> 40 41 #include "NavMenu.h" 42 43 #include <Application.h> 44 #include <Catalog.h> 45 #include <Debug.h> 46 #include <Directory.h> 47 #include <Locale.h> 48 #include <Path.h> 49 #include <Query.h> 50 #include <Screen.h> 51 #include <StopWatch.h> 52 #include <Volume.h> 53 #include <VolumeRoster.h> 54 55 #include "Attributes.h" 56 #include "Commands.h" 57 #include "ContainerWindow.h" 58 #include "DesktopPoseView.h" 59 #include "FunctionObject.h" 60 #include "FSUtils.h" 61 #include "IconMenuItem.h" 62 #include "MimeTypes.h" 63 #include "PoseView.h" 64 #include "QueryPoseView.h" 65 #include "Thread.h" 66 #include "Tracker.h" 67 #include "VirtualDirectoryEntryList.h" 68 69 70 namespace BPrivate { 71 72 const int32 kMinMenuWidth = 150; 73 74 enum nav_flags { 75 kVolumesOnly = 1, 76 kShowParent = 2 77 }; 78 79 80 bool 81 SpringLoadedFolderCompareMessages(const BMessage* incoming, 82 const BMessage* dragmessage) 83 { 84 if (!dragmessage || !incoming) 85 return false; 86 87 bool retvalue = false; 88 for (int32 inIndex = 0; incoming->HasRef("refs", inIndex); inIndex++) { 89 entry_ref inRef; 90 if (incoming->FindRef("refs", inIndex, &inRef) != B_OK) { 91 retvalue = false; 92 break; 93 } 94 95 bool inRefMatch = false; 96 for (int32 dragIndex = 0; dragmessage->HasRef("refs", dragIndex); 97 dragIndex++) { 98 entry_ref dragRef; 99 if (dragmessage->FindRef("refs", dragIndex, &dragRef) != B_OK) { 100 inRefMatch = false; 101 break; 102 } 103 // if the incoming ref matches any ref in the drag ref 104 // then we can try the next incoming ref 105 if (inRef == dragRef) { 106 inRefMatch = true; 107 break; 108 } 109 } 110 retvalue = inRefMatch; 111 if (!inRefMatch) 112 break; 113 } 114 115 if (retvalue) { 116 // if all the refs match 117 // try and see if this is another instance of the same 118 // drag contents, but new drag 119 retvalue = false; 120 BPoint inPt, dPt; 121 if (incoming->FindPoint("click_pt", &inPt) == B_OK) 122 if (dragmessage->FindPoint("click_pt", &dPt) == B_OK) 123 retvalue = (inPt == dPt); 124 } 125 126 return retvalue; 127 } 128 129 130 void 131 SpringLoadedFolderSetMenuStates(const BMenu* menu, 132 const BObjectList<BString>* typeslist) 133 { 134 if (!menu || !typeslist || typeslist->IsEmpty()) 135 return; 136 137 // if a types list exists 138 // iterate through the list and see if each item 139 // can support any item in the list 140 // set the enabled state of the item 141 int32 count = menu->CountItems(); 142 for (int32 index = 0 ; index < count ; index++) { 143 ModelMenuItem* item = dynamic_cast<ModelMenuItem*>(menu->ItemAt(index)); 144 if (!item) 145 continue; 146 147 const Model* model = item->TargetModel(); 148 if (!model) 149 continue; 150 151 if (model->IsSymLink()) { 152 // find out what the model is, resolve if symlink 153 BEntry entry(model->EntryRef(), true); 154 if (entry.InitCheck() == B_OK) { 155 if (entry.IsDirectory()) { 156 // folder? always keep enabled 157 item->SetEnabled(true); 158 } else { 159 // other, check its support 160 Model resolvedModel(&entry); 161 int32 supported = resolvedModel.SupportsMimeType(NULL, typeslist); 162 item->SetEnabled(supported != kDoesNotSupportType); 163 } 164 } else 165 // bad entry ref (bad symlink?), disable 166 item->SetEnabled(false); 167 } else if (model->IsDirectory() || model->IsRoot() || model->IsVolume()) 168 // always enabled if a container 169 item->SetEnabled(true); 170 else if (model->IsFile() || model->IsExecutable()) { 171 int32 supported = model->SupportsMimeType(NULL, typeslist); 172 item->SetEnabled(supported != kDoesNotSupportType); 173 } else 174 item->SetEnabled(false); 175 } 176 } 177 178 179 void 180 SpringLoadedFolderAddUniqueTypeToList(entry_ref* ref, 181 BObjectList<BString>* typeslist) 182 { 183 if (!ref || !typeslist) 184 return; 185 186 // get the mime type for the current ref 187 BNodeInfo nodeinfo; 188 BNode node(ref); 189 if (node.InitCheck() != B_OK) 190 return; 191 192 nodeinfo.SetTo(&node); 193 194 char mimestr[B_MIME_TYPE_LENGTH]; 195 // add it to the list 196 if (nodeinfo.GetType(mimestr) == B_OK && strlen(mimestr) > 0) { 197 // if this is a symlink, add symlink to the list (below) 198 // resolve the symlink, add the resolved type 199 // to the list 200 if (strcmp(B_LINK_MIMETYPE, mimestr) == 0) { 201 BEntry entry(ref, true); 202 if (entry.InitCheck() == B_OK) { 203 entry_ref resolvedRef; 204 if (entry.GetRef(&resolvedRef) == B_OK) 205 SpringLoadedFolderAddUniqueTypeToList(&resolvedRef, typeslist); 206 } 207 } 208 // scan the current list, don't add dups 209 bool unique = true; 210 int32 count = typeslist->CountItems(); 211 for (int32 index = 0 ; index < count ; index++) { 212 if (typeslist->ItemAt(index)->Compare(mimestr) == 0) { 213 unique = false; 214 break; 215 } 216 } 217 218 if (unique) 219 typeslist->AddItem(new BString(mimestr)); 220 } 221 } 222 223 224 void 225 SpringLoadedFolderCacheDragData(const BMessage* incoming, BMessage** message, 226 BObjectList<BString>** typeslist) 227 { 228 if (!incoming) 229 return; 230 231 delete* message; 232 delete* typeslist; 233 234 BMessage* localMessage = new BMessage(*incoming); 235 BObjectList<BString>* localTypesList = new BObjectList<BString>(10, true); 236 237 for (int32 index = 0; incoming->HasRef("refs", index); index++) { 238 entry_ref ref; 239 if (incoming->FindRef("refs", index, &ref) != B_OK) 240 continue; 241 242 SpringLoadedFolderAddUniqueTypeToList(&ref, localTypesList); 243 } 244 245 *message = localMessage; 246 *typeslist = localTypesList; 247 } 248 249 } 250 251 252 // #pragma mark - 253 254 255 #undef B_TRANSLATION_CONTEXT 256 #define B_TRANSLATION_CONTEXT "NavMenu" 257 258 BNavMenu::BNavMenu(const char* title, uint32 message, const BHandler* target, 259 BWindow* parentWindow, const BObjectList<BString>* list) 260 : BSlowMenu(title), 261 fMessage(message), 262 fMessenger(target, target->Looper()), 263 fParentWindow(parentWindow), 264 fFlags(0), 265 fItemList(0), 266 fContainer(0), 267 fTypesList(new BObjectList<BString>(10, true)) 268 { 269 if (list != NULL) 270 *fTypesList = *list; 271 InitIconPreloader(); 272 273 SetFont(be_plain_font); 274 275 // add the parent window to the invocation message so that it 276 // can be closed if option modifier held down during invocation 277 BContainerWindow* originatingWindow = 278 dynamic_cast<BContainerWindow*>(fParentWindow); 279 if (originatingWindow) 280 fMessage.AddData("nodeRefsToClose", B_RAW_TYPE, 281 originatingWindow->TargetModel()->NodeRef(), sizeof (node_ref)); 282 283 // too long to have triggers 284 SetTriggersEnabled(false); 285 } 286 287 288 BNavMenu::BNavMenu(const char* title, uint32 message, 289 const BMessenger& messenger, BWindow* parentWindow, 290 const BObjectList<BString>* list) 291 : BSlowMenu(title), 292 fMessage(message), 293 fMessenger(messenger), 294 fParentWindow(parentWindow), 295 fFlags(0), 296 fItemList(0), 297 fContainer(0), 298 fTypesList(new BObjectList<BString>(10, true)) 299 { 300 if (list != NULL) 301 *fTypesList = *list; 302 InitIconPreloader(); 303 304 SetFont(be_plain_font); 305 306 // add the parent window to the invocation message so that it 307 // can be closed if option modifier held down during invocation 308 BContainerWindow* originatingWindow = 309 dynamic_cast<BContainerWindow*>(fParentWindow); 310 if (originatingWindow) 311 fMessage.AddData("nodeRefsToClose", B_RAW_TYPE, 312 originatingWindow->TargetModel()->NodeRef(), sizeof (node_ref)); 313 314 // too long to have triggers 315 SetTriggersEnabled(false); 316 } 317 318 319 BNavMenu::~BNavMenu() 320 { 321 delete fTypesList; 322 } 323 324 325 void 326 BNavMenu::AttachedToWindow() 327 { 328 BSlowMenu::AttachedToWindow(); 329 330 SpringLoadedFolderSetMenuStates(this, fTypesList); 331 // if dragging (fTypesList != NULL) 332 // set the menu items enabled state 333 // relative to the ability to handle an item in the 334 // drag message 335 ResetTargets(); 336 // allow an opportunity to reset the target for each of the items 337 } 338 339 340 void 341 BNavMenu::DetachedFromWindow() 342 { 343 } 344 345 346 void 347 BNavMenu::ResetTargets() 348 { 349 SetTargetForItems(Target()); 350 } 351 352 353 void 354 BNavMenu::ForceRebuild() 355 { 356 ClearMenuBuildingState(); 357 fMenuBuilt = false; 358 } 359 360 361 bool 362 BNavMenu::NeedsToRebuild() const 363 { 364 return !fMenuBuilt; 365 } 366 367 368 void 369 BNavMenu::SetNavDir(const entry_ref* ref) 370 { 371 ForceRebuild(); 372 // reset the slow menu building mechanism so we can add more stuff 373 374 fNavDir = *ref; 375 } 376 377 378 void 379 BNavMenu::ClearMenuBuildingState() 380 { 381 delete fContainer; 382 fContainer = NULL; 383 384 // item list is non-owning, need to delete the items because 385 // they didn't get added to the menu 386 if (fItemList) { 387 int32 count = fItemList->CountItems(); 388 for (int32 index = count - 1; index >= 0; index--) 389 delete RemoveItem(index); 390 delete fItemList; 391 fItemList = NULL; 392 } 393 } 394 395 396 bool 397 BNavMenu::StartBuildingItemList() 398 { 399 BEntry entry; 400 401 if (fNavDir.device < 0 || entry.SetTo(&fNavDir) != B_OK 402 || !entry.Exists()) 403 return false; 404 405 fItemList = new BObjectList<BMenuItem>(50); 406 407 fIteratingDesktop = false; 408 409 BDirectory parent; 410 status_t status = entry.GetParent(&parent); 411 412 // if ref is the root item then build list of volume root dirs 413 fFlags = uint8((fFlags & ~kVolumesOnly) 414 | (status == B_ENTRY_NOT_FOUND ? kVolumesOnly : 0)); 415 if (fFlags & kVolumesOnly) 416 return true; 417 418 Model startModel(&entry, true); 419 if (startModel.InitCheck() != B_OK || !startModel.IsContainer()) 420 return false; 421 422 if (startModel.IsQuery()) 423 fContainer = new QueryEntryListCollection(&startModel); 424 else if (startModel.IsVirtualDirectory()) 425 fContainer = new VirtualDirectoryEntryList(&startModel); 426 else if (startModel.IsDesktop()) { 427 fIteratingDesktop = true; 428 fContainer = DesktopPoseView::InitDesktopDirentIterator( 429 0, startModel.EntryRef()); 430 AddRootItemsIfNeeded(); 431 AddTrashItem(); 432 } else if (startModel.IsTrash()) { 433 // the trash window needs to display a union of all the 434 // trash folders from all the mounted volumes 435 BVolumeRoster volRoster; 436 volRoster.Rewind(); 437 BVolume volume; 438 fContainer = new EntryIteratorList(); 439 440 while (volRoster.GetNextVolume(&volume) == B_OK) { 441 if (volume.IsReadOnly() || !volume.IsPersistent()) 442 continue; 443 444 BDirectory trashDir; 445 446 if (FSGetTrashDir(&trashDir, volume.Device()) == B_OK) 447 dynamic_cast<EntryIteratorList*>(fContainer)-> 448 AddItem(new DirectoryEntryList(trashDir)); 449 } 450 } else 451 fContainer = new DirectoryEntryList(*dynamic_cast<BDirectory*> 452 (startModel.Node())); 453 454 if (fContainer == NULL || fContainer->InitCheck() != B_OK) 455 return false; 456 457 fContainer->Rewind(); 458 459 return true; 460 } 461 462 463 void 464 BNavMenu::AddRootItemsIfNeeded() 465 { 466 BVolumeRoster roster; 467 roster.Rewind(); 468 BVolume volume; 469 while (roster.GetNextVolume(&volume) == B_OK) { 470 471 BDirectory root; 472 BEntry entry; 473 if (!volume.IsPersistent() 474 || volume.GetRootDirectory(&root) != B_OK 475 || root.GetEntry(&entry) != B_OK) 476 continue; 477 478 Model model(&entry); 479 AddOneItem(&model); 480 } 481 } 482 483 484 void 485 BNavMenu::AddTrashItem() 486 { 487 BPath path; 488 if (find_directory(B_TRASH_DIRECTORY, &path) == B_OK) { 489 BEntry entry(path.Path()); 490 Model model(&entry); 491 AddOneItem(&model); 492 } 493 } 494 495 496 bool 497 BNavMenu::AddNextItem() 498 { 499 if (fFlags & kVolumesOnly) { 500 BuildVolumeMenu(); 501 return false; 502 } 503 504 BEntry entry; 505 if (fContainer->GetNextEntry(&entry) != B_OK) { 506 // we're finished 507 return false; 508 } 509 510 if (TrackerSettings().HideDotFiles()) { 511 char name[B_FILE_NAME_LENGTH]; 512 if (entry.GetName(name) == B_OK && name[0] == '.') 513 return true; 514 } 515 516 Model model(&entry, true); 517 if (model.InitCheck() != B_OK) { 518 // PRINT(("not showing hidden item %s, wouldn't open\n", model->Name())); 519 return true; 520 } 521 522 QueryEntryListCollection* queryContainer 523 = dynamic_cast<QueryEntryListCollection*>(fContainer); 524 if (queryContainer && !queryContainer->ShowResultsFromTrash() 525 && FSInTrashDir(model.EntryRef())) { 526 // query entry is in trash and shall not be shown 527 return true; 528 } 529 530 ssize_t size = -1; 531 PoseInfo poseInfo; 532 533 if (model.Node()) 534 size = model.Node()->ReadAttr(kAttrPoseInfo, B_RAW_TYPE, 0, 535 &poseInfo, sizeof(poseInfo)); 536 537 model.CloseNode(); 538 539 // item might be in invisible 540 if (size == sizeof(poseInfo) 541 && !BPoseView::PoseVisible(&model, &poseInfo)) 542 return true; 543 544 AddOneItem(&model); 545 return true; 546 } 547 548 549 void 550 BNavMenu::AddOneItem(Model* model) 551 { 552 BMenuItem* item = NewModelItem(model, &fMessage, fMessenger, false, 553 dynamic_cast<BContainerWindow*>(fParentWindow), 554 fTypesList, &fTrackingHook); 555 556 if (item) 557 fItemList->AddItem(item); 558 } 559 560 561 ModelMenuItem* 562 BNavMenu::NewModelItem(Model* model, const BMessage* invokeMessage, 563 const BMessenger& target, bool suppressFolderHierarchy, 564 BContainerWindow* parentWindow, const BObjectList<BString>* typeslist, 565 TrackingHookData* hook) 566 { 567 if (model->InitCheck() != B_OK) 568 return 0; 569 entry_ref ref; 570 bool container = false; 571 if (model->IsSymLink()) { 572 573 Model* newResolvedModel = 0; 574 Model* result = model->LinkTo(); 575 576 if (!result) { 577 newResolvedModel = new Model(model->EntryRef(), true, true); 578 579 if (newResolvedModel->InitCheck() != B_OK) { 580 // broken link, still can show though, bail 581 delete newResolvedModel; 582 result = 0; 583 } else 584 result = newResolvedModel; 585 } 586 587 if (result) { 588 BModelOpener opener(result); 589 // open the model, if it ain't open already 590 591 PoseInfo poseInfo; 592 ssize_t size = -1; 593 594 if (result->Node()) 595 size = result->Node()->ReadAttr(kAttrPoseInfo, B_RAW_TYPE, 0, 596 &poseInfo, sizeof(poseInfo)); 597 598 result->CloseNode(); 599 600 if (size == sizeof(poseInfo) && !BPoseView::PoseVisible(result, 601 &poseInfo)) { 602 // link target does not want to be visible 603 delete newResolvedModel; 604 return NULL; 605 } 606 607 ref = *result->EntryRef(); 608 container = result->IsContainer(); 609 } 610 model->SetLinkTo(result); 611 } else { 612 ref = *model->EntryRef(); 613 container = model->IsContainer(); 614 } 615 616 BMessage* message = new BMessage(*invokeMessage); 617 message->AddRef("refs", model->EntryRef()); 618 619 // Truncate the name if necessary 620 BString truncatedString(model->Name()); 621 be_plain_font->TruncateString(&truncatedString, B_TRUNCATE_END, 622 GetMaxMenuWidth()); 623 624 ModelMenuItem* item = NULL; 625 if (!container || suppressFolderHierarchy) { 626 item = new ModelMenuItem(model, truncatedString.String(), message); 627 if (invokeMessage->what != B_REFS_RECEIVED) 628 item->SetEnabled(false); 629 // the above is broken for FavoritesMenu::AddNextItem, which uses a 630 // workaround - should fix this 631 } else { 632 BNavMenu* menu = new BNavMenu(truncatedString.String(), 633 invokeMessage->what, target, parentWindow, typeslist); 634 635 menu->SetNavDir(&ref); 636 if (hook) 637 menu->InitTrackingHook(hook->fTrackingHook, &(hook->fTarget), 638 hook->fDragMessage); 639 640 item = new ModelMenuItem(model, menu); 641 item->SetMessage(message); 642 } 643 644 return item; 645 } 646 647 648 void 649 BNavMenu::BuildVolumeMenu() 650 { 651 BVolumeRoster roster; 652 BVolume volume; 653 654 roster.Rewind(); 655 while (roster.GetNextVolume(&volume) == B_OK) { 656 657 if (!volume.IsPersistent()) 658 continue; 659 660 BDirectory startDir; 661 if (volume.GetRootDirectory(&startDir) == B_OK) { 662 BEntry entry; 663 startDir.GetEntry(&entry); 664 665 Model* model = new Model(&entry); 666 if (model->InitCheck() != B_OK) { 667 delete model; 668 continue; 669 } 670 671 BNavMenu* menu = new BNavMenu(model->Name(), fMessage.what, 672 fMessenger, fParentWindow, fTypesList); 673 674 menu->SetNavDir(model->EntryRef()); 675 676 ASSERT(menu->Name()); 677 678 ModelMenuItem* item = new ModelMenuItem(model, menu); 679 BMessage* message = new BMessage(fMessage); 680 681 message->AddRef("refs", model->EntryRef()); 682 683 item->SetMessage(message); 684 fItemList->AddItem(item); 685 ASSERT(item->Label()); 686 687 } 688 } 689 } 690 691 692 int 693 BNavMenu::CompareFolderNamesFirstOne(const BMenuItem* i1, const BMenuItem* i2) 694 { 695 const ModelMenuItem* item1 = dynamic_cast<const ModelMenuItem*>(i1); 696 const ModelMenuItem* item2 = dynamic_cast<const ModelMenuItem*>(i2); 697 698 if (item1 != NULL && item2 != NULL) 699 return item1->TargetModel()->CompareFolderNamesFirst(item2->TargetModel()); 700 701 return strcasecmp(i1->Label(), i2->Label()); 702 } 703 704 705 int 706 BNavMenu::CompareOne(const BMenuItem* i1, const BMenuItem* i2) 707 { 708 return strcasecmp(i1->Label(), i2->Label()); 709 } 710 711 712 void 713 BNavMenu::DoneBuildingItemList() 714 { 715 // add sorted items to menu 716 if (TrackerSettings().SortFolderNamesFirst()) 717 fItemList->SortItems(CompareFolderNamesFirstOne); 718 else 719 fItemList->SortItems(CompareOne); 720 721 // if the parent link should be shown, it will be the first 722 // entry in the menu - but don't add the item if we're already 723 // at the file system's root 724 if (fFlags & kShowParent) { 725 BDirectory directory(&fNavDir); 726 BEntry entry(&fNavDir); 727 if (!directory.IsRootDirectory() 728 && entry.GetParent(&entry) == B_OK) { 729 Model model(&entry, true); 730 BLooper* looper; 731 AddNavParentDir(&model, fMessage.what, 732 fMessenger.Target(&looper)); 733 } 734 } 735 736 int32 count = fItemList->CountItems(); 737 for (int32 index = 0; index < count; index++) 738 AddItem(fItemList->ItemAt(index)); 739 fItemList->MakeEmpty(); 740 741 if (!count) { 742 BMenuItem* item = new BMenuItem(B_TRANSLATE("Empty folder"), 0); 743 item->SetEnabled(false); 744 AddItem(item); 745 } 746 747 SetTargetForItems(fMessenger); 748 } 749 750 751 int32 752 BNavMenu::GetMaxMenuWidth(void) 753 { 754 int32 width = (int32)(BScreen().Frame().Width() / 4); 755 return (width < kMinMenuWidth) ? kMinMenuWidth : width; 756 } 757 758 759 void 760 BNavMenu::AddNavDir(const Model* model, uint32 what, BHandler* target, 761 bool populateSubmenu) 762 { 763 BMessage* message = new BMessage((uint32)what); 764 message->AddRef("refs", model->EntryRef()); 765 ModelMenuItem* item = NULL; 766 767 if (populateSubmenu) { 768 BNavMenu* navMenu = new BNavMenu(model->Name(), what, target); 769 navMenu->SetNavDir(model->EntryRef()); 770 navMenu->InitTrackingHook(fTrackingHook.fTrackingHook, 771 &(fTrackingHook.fTarget), fTrackingHook.fDragMessage); 772 item = new ModelMenuItem(model, navMenu); 773 item->SetMessage(message); 774 } else 775 item = new ModelMenuItem(model, model->Name(), message); 776 777 AddItem(item); 778 } 779 780 781 void 782 BNavMenu::AddNavParentDir(const char* name,const Model* model, 783 uint32 what, BHandler* target) 784 { 785 BNavMenu* menu = new BNavMenu(name, what, target); 786 menu->SetNavDir(model->EntryRef()); 787 menu->SetShowParent(true); 788 menu->InitTrackingHook(fTrackingHook.fTrackingHook, &(fTrackingHook.fTarget), 789 fTrackingHook.fDragMessage); 790 791 BMenuItem* item = new SpecialModelMenuItem(model, menu); 792 793 BMessage* message = new BMessage(what); 794 message->AddRef("refs",model->EntryRef()); 795 item->SetMessage(message); 796 797 AddItem(item); 798 } 799 800 801 void 802 BNavMenu::AddNavParentDir(const Model* model, uint32 what, BHandler* target) 803 { 804 AddNavParentDir(B_TRANSLATE("parent folder"),model, what, target); 805 } 806 807 808 void 809 BNavMenu::SetShowParent(bool show) 810 { 811 fFlags = uint8((fFlags & ~kShowParent) | (show ? kShowParent : 0)); 812 } 813 814 815 void 816 BNavMenu::SetTypesList(const BObjectList<BString>* list) 817 { 818 if (list != NULL) 819 *fTypesList = *list; 820 else 821 fTypesList->MakeEmpty(); 822 } 823 824 825 const BObjectList<BString>* 826 BNavMenu::TypesList() const 827 { 828 return fTypesList; 829 } 830 831 832 void 833 BNavMenu::SetTarget(const BMessenger& msngr) 834 { 835 fMessenger = msngr; 836 } 837 838 839 BMessenger 840 BNavMenu::Target() 841 { 842 return fMessenger; 843 } 844 845 846 TrackingHookData* 847 BNavMenu::InitTrackingHook(bool (*hook)(BMenu*, void*), 848 const BMessenger* target, const BMessage* dragMessage) 849 { 850 fTrackingHook.fTrackingHook = hook; 851 if (target) 852 fTrackingHook.fTarget = *target; 853 fTrackingHook.fDragMessage = dragMessage; 854 SetTrackingHookDeep(this, hook, &fTrackingHook); 855 return &fTrackingHook; 856 } 857 858 859 void 860 BNavMenu::SetTrackingHookDeep(BMenu* menu, bool (*func)(BMenu*, void*), 861 void* state) 862 { 863 menu->SetTrackingHook(func, state); 864 int32 count = menu->CountItems(); 865 for (int32 index = 0 ; index < count; index++) { 866 BMenuItem* item = menu->ItemAt(index); 867 if (!item) 868 continue; 869 870 BMenu* submenu = item->Submenu(); 871 if (submenu) 872 SetTrackingHookDeep(submenu, func, state); 873 } 874 } 875