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 || typeslist->IsEmpty()) 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(new BObjectList<BString>(10, true)) 267 { 268 if (list != NULL) 269 *fTypesList = *list; 270 InitIconPreloader(); 271 272 SetFont(be_plain_font); 273 274 // add the parent window to the invocation message so that it 275 // can be closed if option modifier held down during invocation 276 BContainerWindow* originatingWindow = 277 dynamic_cast<BContainerWindow*>(fParentWindow); 278 if (originatingWindow) 279 fMessage.AddData("nodeRefsToClose", B_RAW_TYPE, 280 originatingWindow->TargetModel()->NodeRef(), sizeof (node_ref)); 281 282 // too long to have triggers 283 SetTriggersEnabled(false); 284 } 285 286 287 BNavMenu::BNavMenu(const char* title, uint32 message, 288 const BMessenger& messenger, BWindow* parentWindow, 289 const BObjectList<BString>* list) 290 : BSlowMenu(title), 291 fMessage(message), 292 fMessenger(messenger), 293 fParentWindow(parentWindow), 294 fFlags(0), 295 fItemList(0), 296 fContainer(0), 297 fTypesList(new BObjectList<BString>(10, true)) 298 { 299 if (list != NULL) 300 *fTypesList = *list; 301 InitIconPreloader(); 302 303 SetFont(be_plain_font); 304 305 // add the parent window to the invocation message so that it 306 // can be closed if option modifier held down during invocation 307 BContainerWindow* originatingWindow = 308 dynamic_cast<BContainerWindow*>(fParentWindow); 309 if (originatingWindow) 310 fMessage.AddData("nodeRefsToClose", B_RAW_TYPE, 311 originatingWindow->TargetModel()->NodeRef(), sizeof (node_ref)); 312 313 // too long to have triggers 314 SetTriggersEnabled(false); 315 } 316 317 318 BNavMenu::~BNavMenu() 319 { 320 delete fTypesList; 321 } 322 323 324 void 325 BNavMenu::AttachedToWindow() 326 { 327 BSlowMenu::AttachedToWindow(); 328 329 SpringLoadedFolderSetMenuStates(this, fTypesList); 330 // if dragging (fTypesList != NULL) 331 // set the menu items enabled state 332 // relative to the ability to handle an item in the 333 // drag message 334 ResetTargets(); 335 // allow an opportunity to reset the target for each of the items 336 } 337 338 339 void 340 BNavMenu::DetachedFromWindow() 341 { 342 } 343 344 345 void 346 BNavMenu::ResetTargets() 347 { 348 SetTargetForItems(Target()); 349 } 350 351 352 void 353 BNavMenu::ForceRebuild() 354 { 355 ClearMenuBuildingState(); 356 fMenuBuilt = false; 357 } 358 359 360 bool 361 BNavMenu::NeedsToRebuild() const 362 { 363 return !fMenuBuilt; 364 } 365 366 367 void 368 BNavMenu::SetNavDir(const entry_ref* ref) 369 { 370 ForceRebuild(); 371 // reset the slow menu building mechanism so we can add more stuff 372 373 fNavDir = *ref; 374 } 375 376 377 void 378 BNavMenu::ClearMenuBuildingState() 379 { 380 delete fContainer; 381 fContainer = NULL; 382 383 // item list is non-owning, need to delete the items because 384 // they didn't get added to the menu 385 if (fItemList) { 386 int32 count = fItemList->CountItems(); 387 for (int32 index = count - 1; index >= 0; index--) 388 delete RemoveItem(index); 389 delete fItemList; 390 fItemList = NULL; 391 } 392 } 393 394 395 bool 396 BNavMenu::StartBuildingItemList() 397 { 398 BEntry entry; 399 400 if (fNavDir.device < 0 || entry.SetTo(&fNavDir) != B_OK 401 || !entry.Exists()) 402 return false; 403 404 fItemList = new BObjectList<BMenuItem>(50); 405 406 fIteratingDesktop = false; 407 408 BDirectory parent; 409 status_t status = entry.GetParent(&parent); 410 411 // if ref is the root item then build list of volume root dirs 412 fFlags = uint8((fFlags & ~kVolumesOnly) 413 | (status == B_ENTRY_NOT_FOUND ? kVolumesOnly : 0)); 414 if (fFlags & kVolumesOnly) 415 return true; 416 417 Model startModel(&entry, true); 418 if (startModel.InitCheck() != B_OK || !startModel.IsContainer()) 419 return false; 420 421 if (startModel.IsQuery()) 422 fContainer = new QueryEntryListCollection(&startModel); 423 else if (startModel.IsDesktop()) { 424 fIteratingDesktop = true; 425 fContainer = DesktopPoseView::InitDesktopDirentIterator( 426 0, startModel.EntryRef()); 427 AddRootItemsIfNeeded(); 428 AddTrashItem(); 429 } else if (startModel.IsTrash()) { 430 // the trash window needs to display a union of all the 431 // trash folders from all the mounted volumes 432 BVolumeRoster volRoster; 433 volRoster.Rewind(); 434 BVolume volume; 435 fContainer = new EntryIteratorList(); 436 437 while (volRoster.GetNextVolume(&volume) == B_OK) { 438 if (volume.IsReadOnly() || !volume.IsPersistent()) 439 continue; 440 441 BDirectory trashDir; 442 443 if (FSGetTrashDir(&trashDir, volume.Device()) == B_OK) 444 dynamic_cast<EntryIteratorList*>(fContainer)-> 445 AddItem(new DirectoryEntryList(trashDir)); 446 } 447 } else 448 fContainer = new DirectoryEntryList(*dynamic_cast<BDirectory*> 449 (startModel.Node())); 450 451 if (fContainer == NULL || fContainer->InitCheck() != B_OK) 452 return false; 453 454 fContainer->Rewind(); 455 456 return true; 457 } 458 459 460 void 461 BNavMenu::AddRootItemsIfNeeded() 462 { 463 BVolumeRoster roster; 464 roster.Rewind(); 465 BVolume volume; 466 while (roster.GetNextVolume(&volume) == B_OK) { 467 468 BDirectory root; 469 BEntry entry; 470 if (!volume.IsPersistent() 471 || volume.GetRootDirectory(&root) != B_OK 472 || root.GetEntry(&entry) != B_OK) 473 continue; 474 475 Model model(&entry); 476 AddOneItem(&model); 477 } 478 } 479 480 481 void 482 BNavMenu::AddTrashItem() 483 { 484 BPath path; 485 if (find_directory(B_TRASH_DIRECTORY, &path) == B_OK) { 486 BEntry entry(path.Path()); 487 Model model(&entry); 488 AddOneItem(&model); 489 } 490 } 491 492 493 bool 494 BNavMenu::AddNextItem() 495 { 496 if (fFlags & kVolumesOnly) { 497 BuildVolumeMenu(); 498 return false; 499 } 500 501 BEntry entry; 502 if (fContainer->GetNextEntry(&entry) != B_OK) { 503 // we're finished 504 return false; 505 } 506 507 if (TrackerSettings().HideDotFiles()) { 508 char name[B_FILE_NAME_LENGTH]; 509 if (entry.GetName(name) == B_OK && name[0] == '.') 510 return true; 511 } 512 513 Model model(&entry, true); 514 if (model.InitCheck() != B_OK) { 515 // PRINT(("not showing hidden item %s, wouldn't open\n", model->Name())); 516 return true; 517 } 518 519 QueryEntryListCollection* queryContainer 520 = dynamic_cast<QueryEntryListCollection*>(fContainer); 521 if (queryContainer && !queryContainer->ShowResultsFromTrash() 522 && FSInTrashDir(model.EntryRef())) { 523 // query entry is in trash and shall not be shown 524 return true; 525 } 526 527 ssize_t size = -1; 528 PoseInfo poseInfo; 529 530 if (model.Node()) 531 size = model.Node()->ReadAttr(kAttrPoseInfo, B_RAW_TYPE, 0, 532 &poseInfo, sizeof(poseInfo)); 533 534 model.CloseNode(); 535 536 // item might be in invisible 537 if (size == sizeof(poseInfo) 538 && !BPoseView::PoseVisible(&model, &poseInfo)) 539 return true; 540 541 AddOneItem(&model); 542 return true; 543 } 544 545 546 void 547 BNavMenu::AddOneItem(Model* model) 548 { 549 BMenuItem* item = NewModelItem(model, &fMessage, fMessenger, false, 550 dynamic_cast<BContainerWindow*>(fParentWindow), 551 fTypesList, &fTrackingHook); 552 553 if (item) 554 fItemList->AddItem(item); 555 } 556 557 558 ModelMenuItem* 559 BNavMenu::NewModelItem(Model* model, const BMessage* invokeMessage, 560 const BMessenger& target, bool suppressFolderHierarchy, 561 BContainerWindow* parentWindow, const BObjectList<BString>* typeslist, 562 TrackingHookData* hook) 563 { 564 if (model->InitCheck() != B_OK) 565 return 0; 566 entry_ref ref; 567 bool container = false; 568 if (model->IsSymLink()) { 569 570 Model* newResolvedModel = 0; 571 Model* result = model->LinkTo(); 572 573 if (!result) { 574 newResolvedModel = new Model(model->EntryRef(), true, true); 575 576 if (newResolvedModel->InitCheck() != B_OK) { 577 // broken link, still can show though, bail 578 delete newResolvedModel; 579 result = 0; 580 } else 581 result = newResolvedModel; 582 } 583 584 if (result) { 585 BModelOpener opener(result); 586 // open the model, if it ain't open already 587 588 PoseInfo poseInfo; 589 ssize_t size = -1; 590 591 if (result->Node()) 592 size = result->Node()->ReadAttr(kAttrPoseInfo, B_RAW_TYPE, 0, 593 &poseInfo, sizeof(poseInfo)); 594 595 result->CloseNode(); 596 597 if (size == sizeof(poseInfo) && !BPoseView::PoseVisible(result, 598 &poseInfo)) { 599 // link target does not want to be visible 600 delete newResolvedModel; 601 return NULL; 602 } 603 604 ref = *result->EntryRef(); 605 container = result->IsContainer(); 606 } 607 model->SetLinkTo(result); 608 } else { 609 ref = *model->EntryRef(); 610 container = model->IsContainer(); 611 } 612 613 BMessage* message = new BMessage(*invokeMessage); 614 message->AddRef("refs", model->EntryRef()); 615 616 // Truncate the name if necessary 617 BString truncatedString(model->Name()); 618 be_plain_font->TruncateString(&truncatedString, B_TRUNCATE_END, 619 GetMaxMenuWidth()); 620 621 ModelMenuItem* item = NULL; 622 if (!container || suppressFolderHierarchy) { 623 item = new ModelMenuItem(model, truncatedString.String(), message); 624 if (invokeMessage->what != B_REFS_RECEIVED) 625 item->SetEnabled(false); 626 // the above is broken for FavoritesMenu::AddNextItem, which uses a 627 // workaround - should fix this 628 } else { 629 BNavMenu* menu = new BNavMenu(truncatedString.String(), 630 invokeMessage->what, target, parentWindow, typeslist); 631 632 menu->SetNavDir(&ref); 633 if (hook) 634 menu->InitTrackingHook(hook->fTrackingHook, &(hook->fTarget), 635 hook->fDragMessage); 636 637 item = new ModelMenuItem(model, menu); 638 item->SetMessage(message); 639 } 640 641 return item; 642 } 643 644 645 void 646 BNavMenu::BuildVolumeMenu() 647 { 648 BVolumeRoster roster; 649 BVolume volume; 650 651 roster.Rewind(); 652 while (roster.GetNextVolume(&volume) == B_OK) { 653 654 if (!volume.IsPersistent()) 655 continue; 656 657 BDirectory startDir; 658 if (volume.GetRootDirectory(&startDir) == B_OK) { 659 BEntry entry; 660 startDir.GetEntry(&entry); 661 662 Model* model = new Model(&entry); 663 if (model->InitCheck() != B_OK) { 664 delete model; 665 continue; 666 } 667 668 BNavMenu* menu = new BNavMenu(model->Name(), fMessage.what, 669 fMessenger, fParentWindow, fTypesList); 670 671 menu->SetNavDir(model->EntryRef()); 672 673 ASSERT(menu->Name()); 674 675 ModelMenuItem* item = new ModelMenuItem(model, menu); 676 BMessage* message = new BMessage(fMessage); 677 678 message->AddRef("refs", model->EntryRef()); 679 680 item->SetMessage(message); 681 fItemList->AddItem(item); 682 ASSERT(item->Label()); 683 684 } 685 } 686 } 687 688 689 int 690 BNavMenu::CompareFolderNamesFirstOne(const BMenuItem* i1, const BMenuItem* i2) 691 { 692 const ModelMenuItem* item1 = dynamic_cast<const ModelMenuItem*>(i1); 693 const ModelMenuItem* item2 = dynamic_cast<const ModelMenuItem*>(i2); 694 695 if (item1 != NULL && item2 != NULL) 696 return item1->TargetModel()->CompareFolderNamesFirst(item2->TargetModel()); 697 698 return strcasecmp(i1->Label(), i2->Label()); 699 } 700 701 702 int 703 BNavMenu::CompareOne(const BMenuItem* i1, const BMenuItem* i2) 704 { 705 return strcasecmp(i1->Label(), i2->Label()); 706 } 707 708 709 void 710 BNavMenu::DoneBuildingItemList() 711 { 712 // add sorted items to menu 713 if (TrackerSettings().SortFolderNamesFirst()) 714 fItemList->SortItems(CompareFolderNamesFirstOne); 715 else 716 fItemList->SortItems(CompareOne); 717 718 // if the parent link should be shown, it will be the first 719 // entry in the menu - but don't add the item if we're already 720 // at the file system's root 721 if (fFlags & kShowParent) { 722 BDirectory directory(&fNavDir); 723 BEntry entry(&fNavDir); 724 if (!directory.IsRootDirectory() 725 && entry.GetParent(&entry) == B_OK) { 726 Model model(&entry, true); 727 BLooper* looper; 728 AddNavParentDir(&model, fMessage.what, 729 fMessenger.Target(&looper)); 730 } 731 } 732 733 int32 count = fItemList->CountItems(); 734 for (int32 index = 0; index < count; index++) 735 AddItem(fItemList->ItemAt(index)); 736 fItemList->MakeEmpty(); 737 738 if (!count) { 739 BMenuItem* item = new BMenuItem(B_TRANSLATE("Empty folder"), 0); 740 item->SetEnabled(false); 741 AddItem(item); 742 } 743 744 SetTargetForItems(fMessenger); 745 } 746 747 748 int32 749 BNavMenu::GetMaxMenuWidth(void) 750 { 751 int32 width = (int32)(BScreen().Frame().Width() / 4); 752 return (width < kMinMenuWidth) ? kMinMenuWidth : width; 753 } 754 755 756 void 757 BNavMenu::AddNavDir(const Model* model, uint32 what, BHandler* target, 758 bool populateSubmenu) 759 { 760 BMessage* message = new BMessage((uint32)what); 761 message->AddRef("refs", model->EntryRef()); 762 ModelMenuItem* item = NULL; 763 764 if (populateSubmenu) { 765 BNavMenu* navMenu = new BNavMenu(model->Name(), what, target); 766 navMenu->SetNavDir(model->EntryRef()); 767 navMenu->InitTrackingHook(fTrackingHook.fTrackingHook, 768 &(fTrackingHook.fTarget), fTrackingHook.fDragMessage); 769 item = new ModelMenuItem(model, navMenu); 770 item->SetMessage(message); 771 } else 772 item = new ModelMenuItem(model, model->Name(), message); 773 774 AddItem(item); 775 } 776 777 778 void 779 BNavMenu::AddNavParentDir(const char* name,const Model* model, 780 uint32 what, BHandler* target) 781 { 782 BNavMenu* menu = new BNavMenu(name, what, target); 783 menu->SetNavDir(model->EntryRef()); 784 menu->SetShowParent(true); 785 menu->InitTrackingHook(fTrackingHook.fTrackingHook, &(fTrackingHook.fTarget), 786 fTrackingHook.fDragMessage); 787 788 BMenuItem* item = new SpecialModelMenuItem(model, menu); 789 790 BMessage* message = new BMessage(what); 791 message->AddRef("refs",model->EntryRef()); 792 item->SetMessage(message); 793 794 AddItem(item); 795 } 796 797 798 void 799 BNavMenu::AddNavParentDir(const Model* model, uint32 what, BHandler* target) 800 { 801 AddNavParentDir(B_TRANSLATE("parent folder"),model, what, target); 802 } 803 804 805 void 806 BNavMenu::SetShowParent(bool show) 807 { 808 fFlags = uint8((fFlags & ~kShowParent) | (show ? kShowParent : 0)); 809 } 810 811 812 void 813 BNavMenu::SetTypesList(const BObjectList<BString>* list) 814 { 815 if (list != NULL) 816 *fTypesList = *list; 817 else 818 fTypesList->MakeEmpty(); 819 } 820 821 822 const BObjectList<BString>* 823 BNavMenu::TypesList() const 824 { 825 return fTypesList; 826 } 827 828 829 void 830 BNavMenu::SetTarget(const BMessenger& msngr) 831 { 832 fMessenger = msngr; 833 } 834 835 836 BMessenger 837 BNavMenu::Target() 838 { 839 return fMessenger; 840 } 841 842 843 TrackingHookData* 844 BNavMenu::InitTrackingHook(bool (*hook)(BMenu*, void*), 845 const BMessenger* target, const BMessage* dragMessage) 846 { 847 fTrackingHook.fTrackingHook = hook; 848 if (target) 849 fTrackingHook.fTarget = *target; 850 fTrackingHook.fDragMessage = dragMessage; 851 SetTrackingHookDeep(this, hook, &fTrackingHook); 852 return &fTrackingHook; 853 } 854 855 856 void 857 BNavMenu::SetTrackingHookDeep(BMenu* menu, bool (*func)(BMenu*, void*), 858 void* state) 859 { 860 menu->SetTrackingHook(func, state); 861 int32 count = menu->CountItems(); 862 for (int32 index = 0 ; index < count; index++) { 863 BMenuItem* item = menu->ItemAt(index); 864 if (!item) 865 continue; 866 867 BMenu* submenu = item->Submenu(); 868 if (submenu) 869 SetTrackingHookDeep(submenu, func, state); 870 } 871 } 872