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