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