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