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