Assorted whitespace cleanup and typo fixes.
[haiku.git] / src / kits / tracker / NavMenu.cpp
blob7d442a1c447dbbe1da328260708d855dd74d4cbe
1 /*
2 Open Tracker License
4 Terms and Conditions
6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
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:
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.
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.
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.
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.
35 // NavMenu is a hierarchical menu of volumes, folders, files and queries
36 // displays icons, uses the SlowMenu API for full interruptability
39 #include "NavMenu.h"
41 #include <algorithm>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <strings.h>
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>
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"
74 namespace BPrivate {
76 const int32 kMinMenuWidth = 150;
78 enum nav_flags {
79 kVolumesOnly = 1,
80 kShowParent = 2
84 bool
85 SpringLoadedFolderCompareMessages(const BMessage* incoming,
86 const BMessage* dragMessage)
88 if (incoming == NULL || dragMessage == NULL)
89 return false;
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;
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;
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;
114 refsMatch = inRefMatch;
115 if (!inRefMatch)
116 break;
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);
131 return refsMatch;
135 void
136 SpringLoadedFolderSetMenuStates(const BMenu* menu,
137 const BObjectList<BString>* typeslist)
139 if (menu == NULL || typeslist == NULL || typeslist->IsEmpty())
140 return;
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;
150 const Model* model = item->TargetModel();
151 if (!model)
152 continue;
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);
168 } else {
169 // bad entry ref (bad symlink?), disable
170 item->SetEnabled(false);
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);
184 void
185 SpringLoadedFolderAddUniqueTypeToList(entry_ref* ref,
186 BObjectList<BString>* typeslist)
188 if (ref == NULL || typeslist == NULL)
189 return;
191 // get the mime type for the current ref
192 BNodeInfo nodeinfo;
193 BNode node(ref);
194 if (node.InitCheck() != B_OK)
195 return;
197 nodeinfo.SetTo(&node);
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);
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;
223 if (isUnique)
224 typeslist->AddItem(new BString(mimestr));
229 void
230 SpringLoadedFolderCacheDragData(const BMessage* incoming, BMessage** message,
231 BObjectList<BString>** typeslist)
233 if (incoming == NULL)
234 return;
236 delete* message;
237 delete* typeslist;
239 BMessage* localMessage = new BMessage(*incoming);
240 BObjectList<BString>* localTypesList = new BObjectList<BString>(10, true);
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;
247 SpringLoadedFolderAddUniqueTypeToList(&ref, localTypesList);
250 *message = localMessage;
251 *typeslist = localTypesList;
257 // #pragma mark - BNavMenu
260 #undef B_TRANSLATION_CONTEXT
261 #define B_TRANSLATION_CONTEXT "NavMenu"
264 BNavMenu::BNavMenu(const char* title, uint32 message, const BHandler* target,
265 BWindow* parentWindow, const BObjectList<BString>* list)
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))
277 if (list != NULL)
278 *fTypesList = *list;
280 InitIconPreloader();
282 SetFont(be_plain_font);
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));
293 // too long to have triggers
294 SetTriggersEnabled(false);
298 BNavMenu::BNavMenu(const char* title, uint32 message,
299 const BMessenger& messenger, BWindow* parentWindow,
300 const BObjectList<BString>* list)
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))
312 if (list != NULL)
313 *fTypesList = *list;
315 InitIconPreloader();
317 SetFont(be_plain_font);
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));
328 // too long to have triggers
329 SetTriggersEnabled(false);
333 BNavMenu::~BNavMenu()
335 delete fTypesList;
339 void
340 BNavMenu::AttachedToWindow()
342 BSlowMenu::AttachedToWindow();
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
352 void
353 BNavMenu::DetachedFromWindow()
358 void
359 BNavMenu::ResetTargets()
361 SetTargetForItems(Target());
365 void
366 BNavMenu::ForceRebuild()
368 ClearMenuBuildingState();
369 fMenuBuilt = false;
373 bool
374 BNavMenu::NeedsToRebuild() const
376 return !fMenuBuilt;
380 void
381 BNavMenu::SetNavDir(const entry_ref* ref)
383 ForceRebuild();
384 // reset the slow menu building mechanism so we can add more stuff
386 fNavDir = *ref;
390 void
391 BNavMenu::ClearMenuBuildingState()
393 delete fContainer;
394 fContainer = NULL;
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);
403 delete fItemList;
404 fItemList = NULL;
409 bool
410 BNavMenu::StartBuildingItemList()
412 BEntry entry;
414 if (fNavDir.device < 0 || entry.SetTo(&fNavDir, true) != B_OK
415 || !entry.Exists()) {
416 return false;
419 fItemList = new BObjectList<BMenuItem>(50);
421 fIteratingDesktop = false;
423 BDirectory parent;
424 status_t status = entry.GetParent(&parent);
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;
432 Model startModel(&entry, true);
433 if (startModel.InitCheck() != B_OK || !startModel.IsContainer())
434 return false;
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();
454 while (volRoster.GetNextVolume(&volume) == B_OK) {
455 if (volume.IsReadOnly() || !volume.IsPersistent())
456 continue;
458 BDirectory trashDir;
460 if (FSGetTrashDir(&trashDir, volume.Device()) == B_OK) {
461 EntryIteratorList* iteratorList
462 = dynamic_cast<EntryIteratorList*>(fContainer);
464 ASSERT(iteratorList != NULL);
466 if (iteratorList != NULL)
467 iteratorList->AddItem(new DirectoryEntryList(trashDir));
470 } else {
471 BDirectory* directory = dynamic_cast<BDirectory*>(startModel.Node());
473 ASSERT(directory != NULL);
475 if (directory != NULL)
476 fContainer = new DirectoryEntryList(*directory);
479 if (fContainer == NULL || fContainer->InitCheck() != B_OK)
480 return false;
482 fContainer->Rewind();
484 return true;
488 void
489 BNavMenu::AddRootItemsIfNeeded()
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;
503 Model model(&entry);
504 AddOneItem(&model);
509 void
510 BNavMenu::AddTrashItem()
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);
521 bool
522 BNavMenu::AddNextItem()
524 if ((fFlags & kVolumesOnly) != 0) {
525 BuildVolumeMenu();
526 return false;
529 BEntry entry;
530 if (fContainer->GetNextEntry(&entry) != B_OK) {
531 // we're finished
532 return false;
535 if (TrackerSettings().HideDotFiles()) {
536 char name[B_FILE_NAME_LENGTH];
537 if (entry.GetName(name) == B_OK && name[0] == '.')
538 return true;
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;
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;
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));
562 model.CloseNode();
564 // item might be in invisible
565 if (size == sizeof(poseInfo)
566 && !BPoseView::PoseVisible(&model, &poseInfo)) {
567 return true;
570 AddOneItem(&model);
572 return true;
576 void
577 BNavMenu::AddOneItem(Model* model)
579 BMenuItem* item = NewModelItem(model, &fMessage, fMessenger, false,
580 dynamic_cast<BContainerWindow*>(fParentWindow),
581 fTypesList, &fTrackingHook);
583 if (item != NULL)
584 fItemList->AddItem(item);
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)
594 if (model->InitCheck() != B_OK)
595 return 0;
597 entry_ref ref;
598 bool isContainer = false;
599 if (model->IsSymLink()) {
600 Model* newResolvedModel = 0;
601 Model* result = model->LinkTo();
603 if (result == NULL) {
604 newResolvedModel = new Model(model->EntryRef(), true, true);
606 if (newResolvedModel->InitCheck() != B_OK) {
607 // broken link, still can show though, bail
608 delete newResolvedModel;
609 result = 0;
610 } else
611 result = newResolvedModel;
614 if (result != NULL) {
615 BModelOpener opener(result);
616 // open the model, if it ain't open already
618 PoseInfo poseInfo;
619 ssize_t size = -1;
621 if (result->Node() != NULL) {
622 size = result->Node()->ReadAttr(kAttrPoseInfo, B_RAW_TYPE, 0,
623 &poseInfo, sizeof(poseInfo));
626 result->CloseNode();
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;
635 ref = *result->EntryRef();
636 isContainer = result->IsContainer();
639 model->SetLinkTo(result);
640 } else {
641 ref = *model->EntryRef();
642 isContainer = model->IsContainer();
645 BMessage* message = new BMessage(*invokeMessage);
646 message->AddRef("refs", model->EntryRef());
648 // truncate name if necessary
649 BString truncatedString(model->Name());
650 be_plain_font->TruncateString(&truncatedString, B_TRUNCATE_END,
651 GetMaxMenuWidth());
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);
669 item = new ModelMenuItem(model, menu);
670 item->SetMessage(message);
673 return item;
677 void
678 BNavMenu::BuildVolumeMenu()
680 BVolumeRoster roster;
681 BVolume volume;
683 roster.Rewind();
684 while (roster.GetNextVolume(&volume) == B_OK) {
685 if (!volume.IsPersistent())
686 continue;
688 BDirectory startDir;
689 if (volume.GetRootDirectory(&startDir) == B_OK) {
690 BEntry entry;
691 startDir.GetEntry(&entry);
693 Model* model = new Model(&entry);
694 if (model->InitCheck() != B_OK) {
695 delete model;
696 continue;
699 BNavMenu* menu = new BNavMenu(model->Name(), fMessage.what,
700 fMessenger, fParentWindow, fTypesList);
702 menu->SetNavDir(model->EntryRef());
704 ASSERT(menu->Name() != NULL);
706 ModelMenuItem* item = new ModelMenuItem(model, menu);
707 BMessage* message = new BMessage(fMessage);
709 message->AddRef("refs", model->EntryRef());
711 item->SetMessage(message);
712 fItemList->AddItem(item);
713 ASSERT(item->Label() != NULL);
720 BNavMenu::CompareFolderNamesFirstOne(const BMenuItem* i1, const BMenuItem* i2)
722 ThrowOnAssert(i1 != NULL && i2 != NULL);
724 const ModelMenuItem* item1 = dynamic_cast<const ModelMenuItem*>(i1);
725 const ModelMenuItem* item2 = dynamic_cast<const ModelMenuItem*>(i2);
727 if (item1 != NULL && item2 != NULL) {
728 return item1->TargetModel()->CompareFolderNamesFirst(
729 item2->TargetModel());
732 return strcasecmp(i1->Label(), i2->Label());
737 BNavMenu::CompareOne(const BMenuItem* i1, const BMenuItem* i2)
739 ThrowOnAssert(i1 != NULL && i2 != NULL);
741 return strcasecmp(i1->Label(), i2->Label());
745 void
746 BNavMenu::DoneBuildingItemList()
748 // add sorted items to menu
749 if (TrackerSettings().SortFolderNamesFirst())
750 fItemList->SortItems(CompareFolderNamesFirstOne);
751 else
752 fItemList->SortItems(CompareOne);
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));
769 int32 count = fItemList->CountItems();
770 for (int32 index = 0; index < count; index++)
771 AddItem(fItemList->ItemAt(index));
773 fItemList->MakeEmpty();
775 if (count == 0) {
776 BMenuItem* item = new BMenuItem(B_TRANSLATE("Empty folder"), 0);
777 item->SetEnabled(false);
778 AddItem(item);
781 SetTargetForItems(fMessenger);
785 int32
786 BNavMenu::GetMaxMenuWidth(void)
788 return std::max((int32)(BScreen().Frame().Width() / 4), kMinMenuWidth);
792 void
793 BNavMenu::AddNavDir(const Model* model, uint32 what, BHandler* target,
794 bool populateSubmenu)
796 BMessage* message = new BMessage((uint32)what);
797 message->AddRef("refs", model->EntryRef());
798 ModelMenuItem* item = NULL;
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);
810 AddItem(item);
814 void
815 BNavMenu::AddNavParentDir(const char* name,const Model* model,
816 uint32 what, BHandler* target)
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);
824 BMenuItem* item = new SpecialModelMenuItem(model, menu);
825 BMessage* message = new BMessage(what);
826 message->AddRef("refs", model->EntryRef());
827 item->SetMessage(message);
829 AddItem(item);
833 void
834 BNavMenu::AddNavParentDir(const Model* model, uint32 what, BHandler* target)
836 AddNavParentDir(B_TRANSLATE("parent folder"),model, what, target);
840 void
841 BNavMenu::SetShowParent(bool show)
843 fFlags = uint8((fFlags & ~kShowParent) | (show ? kShowParent : 0));
847 void
848 BNavMenu::SetTypesList(const BObjectList<BString>* list)
850 if (list != NULL)
851 *fTypesList = *list;
852 else
853 fTypesList->MakeEmpty();
857 const BObjectList<BString>*
858 BNavMenu::TypesList() const
860 return fTypesList;
864 void
865 BNavMenu::SetTarget(const BMessenger& messenger)
867 fMessenger = messenger;
871 BMessenger
872 BNavMenu::Target()
874 return fMessenger;
878 TrackingHookData*
879 BNavMenu::InitTrackingHook(bool (*hook)(BMenu*, void*),
880 const BMessenger* target, const BMessage* dragMessage)
882 fTrackingHook.fTrackingHook = hook;
883 if (target != NULL)
884 fTrackingHook.fTarget = *target;
886 fTrackingHook.fDragMessage = dragMessage;
887 SetTrackingHookDeep(this, hook, &fTrackingHook);
889 return &fTrackingHook;
893 void
894 BNavMenu::SetTrackingHookDeep(BMenu* menu, bool (*func)(BMenu*, void*),
895 void* state)
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;
904 BMenu* submenu = item->Submenu();
905 if (submenu != NULL)
906 SetTrackingHookDeep(submenu, func, state);