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