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