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