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