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