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 (startModel.IsDesktop()) { 412 fIteratingDesktop = true; 413 fContainer = DesktopPoseView::InitDesktopDirentIterator(0, startModel.EntryRef()); 414 AddRootItemsIfNeeded(); 415 AddTrashItem(); 416 } else if (startModel.IsTrash()) { 417 // the trash window needs to display a union of all the 418 // trash folders from all the mounted volumes 419 BVolumeRoster volRoster; 420 volRoster.Rewind(); 421 BVolume volume; 422 fContainer = new EntryIteratorList(); 423 424 while (volRoster.GetNextVolume(&volume) == B_OK) { 425 if (volume.IsReadOnly() || !volume.IsPersistent()) 426 continue; 427 428 BDirectory trashDir; 429 430 if (FSGetTrashDir(&trashDir, volume.Device()) == B_OK) 431 dynamic_cast<EntryIteratorList *>(fContainer)-> 432 AddItem(new DirectoryEntryList(trashDir)); 433 } 434 } else 435 fContainer = new DirectoryEntryList(*dynamic_cast<BDirectory *> 436 (startModel.Node())); 437 438 if (fContainer == NULL || fContainer->InitCheck() != B_OK) 439 return false; 440 441 fContainer->Rewind(); 442 443 return true; 444 } 445 446 447 void 448 BNavMenu::AddRootItemsIfNeeded() 449 { 450 BVolumeRoster roster; 451 roster.Rewind(); 452 BVolume volume; 453 while (roster.GetNextVolume(&volume) == B_OK) { 454 455 BDirectory root; 456 BEntry entry; 457 if (!volume.IsPersistent() 458 || volume.GetRootDirectory(&root) != B_OK 459 || root.GetEntry(&entry) != B_OK) 460 continue; 461 462 Model model(&entry); 463 AddOneItem(&model); 464 } 465 } 466 467 468 void 469 BNavMenu::AddTrashItem() 470 { 471 BPath path; 472 if (find_directory(B_TRASH_DIRECTORY, &path) == B_OK) { 473 BEntry entry(path.Path()); 474 Model model(&entry); 475 AddOneItem(&model); 476 } 477 } 478 479 480 bool 481 BNavMenu::AddNextItem() 482 { 483 if (fFlags & kVolumesOnly) { 484 BuildVolumeMenu(); 485 return false; 486 } 487 488 BEntry entry; 489 if (fContainer->GetNextEntry(&entry) != B_OK) { 490 // we're finished 491 return false; 492 } 493 494 if (TrackerSettings().HideDotFiles()) { 495 char name[B_FILE_NAME_LENGTH]; 496 if (entry.GetName(name) == B_OK && name[0] == '.') 497 return true; 498 } 499 500 Model model(&entry, true); 501 if (model.InitCheck() != B_OK) { 502 // PRINT(("not showing hidden item %s, wouldn't open\n", model->Name())); 503 return true; 504 } 505 506 QueryEntryListCollection *queryContainer 507 = dynamic_cast<QueryEntryListCollection*>(fContainer); 508 if (queryContainer && !queryContainer->ShowResultsFromTrash() 509 && FSInTrashDir(model.EntryRef())) { 510 // query entry is in trash and shall not be shown 511 return true; 512 } 513 514 ssize_t size = -1; 515 PoseInfo poseInfo; 516 517 if (model.Node()) 518 size = model.Node()->ReadAttr(kAttrPoseInfo, B_RAW_TYPE, 0, 519 &poseInfo, sizeof(poseInfo)); 520 521 model.CloseNode(); 522 523 // item might be in invisible 524 // ToDo: 525 // use more of PoseView's filtering here 526 if ((size == sizeof(poseInfo) 527 && !BPoseView::PoseVisible(&model, &poseInfo, false)) 528 || fIteratingDesktop && !ShouldShowDesktopPose(fNavDir.device, 529 &model, &poseInfo)) { 530 // PRINT(("not showing hidden item %s\n", model.Name())); 531 return true; 532 } 533 534 AddOneItem(&model); 535 return true; 536 } 537 538 539 void 540 BNavMenu::AddOneItem(Model *model) 541 { 542 BMenuItem *item = NewModelItem(model, &fMessage, fMessenger, false, 543 dynamic_cast<BContainerWindow *>(fParentWindow), 544 fTypesList, &fTrackingHook); 545 546 if (item) 547 fItemList->AddItem(item); 548 } 549 550 551 ModelMenuItem * 552 BNavMenu::NewModelItem(Model *model, const BMessage *invokeMessage, 553 const BMessenger &target, bool suppressFolderHierarchy, 554 BContainerWindow *parentWindow, const BObjectList<BString> *typeslist, 555 TrackingHookData *hook) 556 { 557 if (model->InitCheck() != B_OK) 558 return 0; 559 entry_ref ref; 560 bool container = false; 561 if (model->IsSymLink()) { 562 563 Model *newResolvedModel = 0; 564 Model *result = model->LinkTo(); 565 566 if (!result) { 567 newResolvedModel = new Model(model->EntryRef(), true, true); 568 569 if (newResolvedModel->InitCheck() != B_OK) { 570 // broken link, still can show though, bail 571 delete newResolvedModel; 572 result = 0; 573 } else 574 result = newResolvedModel; 575 } 576 577 if (result) { 578 BModelOpener opener(result); 579 // open the model, if it ain't open already 580 581 PoseInfo poseInfo; 582 ssize_t size = -1; 583 584 if (result->Node()) 585 size = result->Node()->ReadAttr(kAttrPoseInfo, B_RAW_TYPE, 0, 586 &poseInfo, sizeof(poseInfo)); 587 588 result->CloseNode(); 589 590 if (size == sizeof(poseInfo) && !BPoseView::PoseVisible(result, 591 &poseInfo, false)) { 592 // link target does not want to be visible 593 delete newResolvedModel; 594 return NULL; 595 } 596 597 ref = *result->EntryRef(); 598 container = result->IsContainer(); 599 } 600 model->SetLinkTo(result); 601 } else { 602 ref = *model->EntryRef(); 603 container = model->IsContainer(); 604 } 605 606 BMessage *message = new BMessage(*invokeMessage); 607 message->AddRef("refs", model->EntryRef()); 608 609 // Truncate the name if necessary 610 BString truncatedString(model->Name()); 611 be_plain_font->TruncateString(&truncatedString, B_TRUNCATE_END, 612 GetMaxMenuWidth()); 613 614 ModelMenuItem *item = NULL; 615 if (!container || suppressFolderHierarchy) { 616 item = new ModelMenuItem(model, truncatedString.String(), message); 617 if (invokeMessage->what != B_REFS_RECEIVED) 618 item->SetEnabled(false); 619 // the above is broken for FavoritesMenu::AddNextItem, which uses a 620 // workaround - should fix this 621 } else { 622 BNavMenu *menu = new BNavMenu(truncatedString.String(), 623 invokeMessage->what, target, parentWindow, typeslist); 624 625 menu->SetNavDir(&ref); 626 if (hook) 627 menu->InitTrackingHook(hook->fTrackingHook, &(hook->fTarget), 628 hook->fDragMessage); 629 630 item = new ModelMenuItem(model, menu); 631 item->SetMessage(message); 632 } 633 634 return item; 635 } 636 637 638 void 639 BNavMenu::BuildVolumeMenu() 640 { 641 BVolumeRoster roster; 642 BVolume volume; 643 644 roster.Rewind(); 645 while (roster.GetNextVolume(&volume) == B_OK) { 646 647 if (!volume.IsPersistent()) 648 continue; 649 650 BDirectory startDir; 651 if (volume.GetRootDirectory(&startDir) == B_OK) { 652 BEntry entry; 653 startDir.GetEntry(&entry); 654 655 Model *model = new Model(&entry); 656 if (model->InitCheck() != B_OK) { 657 delete model; 658 continue; 659 } 660 661 BNavMenu *menu = new BNavMenu(model->Name(), fMessage.what, 662 fMessenger, fParentWindow, fTypesList); 663 664 menu->SetNavDir(model->EntryRef()); 665 666 ASSERT(menu->Name()); 667 668 ModelMenuItem *item = new ModelMenuItem(model, menu); 669 BMessage *message = new BMessage(fMessage); 670 671 message->AddRef("refs", model->EntryRef()); 672 673 item->SetMessage(message); 674 fItemList->AddItem(item); 675 ASSERT(item->Label()); 676 677 } 678 } 679 } 680 681 682 int 683 BNavMenu::CompareFolderNamesFirstOne(const BMenuItem *i1, const BMenuItem *i2) 684 { 685 const ModelMenuItem *item1 = dynamic_cast<const ModelMenuItem *>(i1); 686 const ModelMenuItem *item2 = dynamic_cast<const ModelMenuItem *>(i2); 687 688 if (item1 != NULL && item2 != NULL) 689 return item1->TargetModel()->CompareFolderNamesFirst(item2->TargetModel()); 690 691 return strcasecmp(i1->Label(), i2->Label()); 692 } 693 694 695 int 696 BNavMenu::CompareOne(const BMenuItem *i1, const BMenuItem *i2) 697 { 698 return strcasecmp(i1->Label(), i2->Label()); 699 } 700 701 702 void 703 BNavMenu::DoneBuildingItemList() 704 { 705 // add sorted items to menu 706 if (TrackerSettings().SortFolderNamesFirst()) 707 fItemList->SortItems(CompareFolderNamesFirstOne); 708 else 709 fItemList->SortItems(CompareOne); 710 711 // if the parent link should be shown, it will be the first 712 // entry in the menu - but don't add the item if we're already 713 // at the file system's root 714 if (fFlags & kShowParent) { 715 BDirectory directory(&fNavDir); 716 BEntry entry(&fNavDir); 717 if (!directory.IsRootDirectory() 718 && entry.GetParent(&entry) == B_OK) { 719 Model model(&entry, true); 720 BLooper *looper; 721 AddNavParentDir(&model,fMessage.what,fMessenger.Target(&looper)); 722 } 723 } 724 725 int32 count = fItemList->CountItems(); 726 for (int32 index = 0; index < count; index++) 727 AddItem(fItemList->ItemAt(index)); 728 fItemList->MakeEmpty(); 729 730 if (!count) { 731 BMenuItem *item = new BMenuItem("Empty folder", 0); 732 item->SetEnabled(false); 733 AddItem(item); 734 } 735 736 SetTargetForItems(fMessenger); 737 } 738 739 740 int32 741 BNavMenu::GetMaxMenuWidth(void) 742 { 743 int32 width = (int32)(BScreen().Frame().Width() / 4); 744 return (width < kMinMenuWidth) ? kMinMenuWidth : width; 745 } 746 747 748 void 749 BNavMenu::AddNavDir(const Model *model, uint32 what, BHandler *target, 750 bool populateSubmenu) 751 { 752 BMessage *message = new BMessage((uint32)what); 753 message->AddRef("refs", model->EntryRef()); 754 ModelMenuItem *item = NULL; 755 756 if (populateSubmenu) { 757 BNavMenu *navMenu = new BNavMenu(model->Name(), what, target); 758 navMenu->SetNavDir(model->EntryRef()); 759 navMenu->InitTrackingHook(fTrackingHook.fTrackingHook, &(fTrackingHook.fTarget), 760 fTrackingHook.fDragMessage); 761 item = new ModelMenuItem(model, navMenu); 762 item->SetMessage(message); 763 } else 764 item = new ModelMenuItem(model, model->Name(), message); 765 766 AddItem(item); 767 } 768 769 770 void 771 BNavMenu::AddNavParentDir(const char *name,const Model *model,uint32 what,BHandler *target) 772 { 773 BNavMenu *menu = new BNavMenu(name,what,target); 774 menu->SetNavDir(model->EntryRef()); 775 menu->SetShowParent(true); 776 menu->InitTrackingHook(fTrackingHook.fTrackingHook, &(fTrackingHook.fTarget), 777 fTrackingHook.fDragMessage); 778 779 BMenuItem *item = new SpecialModelMenuItem(model,menu); 780 781 BMessage *message = new BMessage(what); 782 message->AddRef("refs",model->EntryRef()); 783 item->SetMessage(message); 784 785 AddItem(item); 786 } 787 788 789 void 790 BNavMenu::AddNavParentDir(const Model *model, uint32 what, BHandler *target) 791 { 792 AddNavParentDir("parent folder",model,what,target); 793 } 794 795 796 void 797 BNavMenu::SetShowParent(bool show) 798 { 799 fFlags = uint8((fFlags & ~kShowParent) | (show ? kShowParent : 0)); 800 } 801 802 803 void 804 BNavMenu::SetTypesList(const BObjectList<BString> *list) 805 { 806 fTypesList = list; 807 } 808 809 810 const BObjectList<BString> * 811 BNavMenu::TypesList() const 812 { 813 return fTypesList; 814 } 815 816 817 void 818 BNavMenu::SetTarget(const BMessenger &msngr) 819 { 820 fMessenger = msngr; 821 } 822 823 824 BMessenger 825 BNavMenu::Target() 826 { 827 return fMessenger; 828 } 829 830 831 TrackingHookData * 832 BNavMenu::InitTrackingHook(bool (*hook)(BMenu *, void *), const BMessenger *target, 833 const BMessage *dragMessage) 834 { 835 fTrackingHook.fTrackingHook = hook; 836 if (target) 837 fTrackingHook.fTarget = *target; 838 fTrackingHook.fDragMessage = dragMessage; 839 SetTrackingHookDeep(this, hook, &fTrackingHook); 840 return &fTrackingHook; 841 } 842 843 844 void 845 BNavMenu::SetTrackingHookDeep(BMenu *menu, bool (*func)(BMenu *, void *), void *state) 846 { 847 menu->SetTrackingHook(func, state); 848 int32 count = menu->CountItems(); 849 for (int32 index = 0 ; index < count; index++) { 850 BMenuItem *item = menu->ItemAt(index); 851 if (!item) 852 continue; 853 854 BMenu *submenu = item->Submenu(); 855 if (submenu) 856 SetTrackingHookDeep(submenu, func, state); 857 } 858 } 859 860