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 trademarks
30of Be Incorporated in the United States and other countries. Other brand product
31names are registered trademarks or trademarks of their respective holders.
32All rights reserved.
33*/
34
35// ToDo:
36// get rid of fMenuBar, SetMenuBar and related mess
37
38
39#include <Catalog.h>
40#include <Debug.h>
41#include <Directory.h>
42#include <Locale.h>
43#include <MenuBar.h>
44#include <Path.h>
45#include <Volume.h>
46#include <VolumeRoster.h>
47
48#include "Attributes.h"
49#include "ContainerWindow.h"
50#include "DirMenu.h"
51#include "FSUtils.h"
52#include "IconMenuItem.h"
53#include "NavMenu.h"
54#include "TrackerSettings.h"
55#include "Utilities.h"
56
57
58#undef B_TRANSLATION_CONTEXT
59#define B_TRANSLATION_CONTEXT "DirMenu"
60
61
62//	#pragma mark - BDirMenu
63
64
65BDirMenu::BDirMenu(BMenuBar* bar, BMessenger target, uint32 command,
66	const char* entryName)
67	:
68	BPopUpMenu("directories"),
69	fTarget(target),
70	fMenuBar(bar),
71	fCommand(command)
72{
73	SetFont(be_plain_font);
74	if (entryName)
75		fEntryName = entryName;
76	else
77		fEntryName = "refs";
78}
79
80
81BDirMenu::~BDirMenu()
82{
83}
84
85
86void
87BDirMenu::Populate(const BEntry* startEntry, BWindow* originatingWindow,
88	bool includeStartEntry, bool select, bool reverse, bool addShortcuts,
89	bool navMenuEntries)
90{
91	try {
92		if (!startEntry)
93			throw (status_t)B_ERROR;
94
95		Model model(startEntry);
96		ThrowOnInitCheckError(&model);
97
98		ModelMenuItem* menu = NULL;
99
100		if (fMenuBar) {
101			menu = new ModelMenuItem(&model, this, true, true);
102			fMenuBar->AddItem(menu);
103		}
104
105		BEntry entry(*startEntry);
106
107		bool showDesktop, showDisksIcon;
108		{
109			TrackerSettings settings;
110			showDesktop = settings.DesktopFilePanelRoot();
111			showDisksIcon = settings.ShowDisksIcon();
112		}
113
114		// might start one level above startEntry
115		if (!includeStartEntry) {
116			BDirectory parent;
117			BDirectory dir(&entry);
118
119			if (!showDesktop && dir.InitCheck() == B_OK
120				&& dir.IsRootDirectory()) {
121				// if we're at the root directory skip "mnt" and
122				// go straight to "/"
123				parent.SetTo("/");
124				parent.GetEntry(&entry);
125			} else
126				FSGetParentVirtualDirectoryAware(entry, entry);
127		}
128
129		BDirectory desktopDir;
130		FSGetDeskDir(&desktopDir);
131		BEntry desktopEntry;
132		desktopDir.GetEntry(&desktopEntry);
133
134		for (;;) {
135			BNode node(&entry);
136			ThrowOnInitCheckError(&node);
137
138			PoseInfo info;
139			ReadAttrResult result = ReadAttr(&node, kAttrPoseInfo,
140				kAttrPoseInfoForeign, B_RAW_TYPE, 0, &info, sizeof(PoseInfo),
141				&PoseInfo::EndianSwap);
142
143			BEntry parentEntry;
144			bool hitRoot = false;
145
146			BDirectory dir(&entry);
147			if (!showDesktop && dir.InitCheck() == B_OK
148				&& dir.IsRootDirectory()) {
149				// if we're at the root directory skip "mnt" and
150				// go straight to "/"
151				hitRoot = true;
152				parentEntry.SetTo("/");
153			} else
154				FSGetParentVirtualDirectoryAware(entry, parentEntry);
155
156			if (showDesktop) {
157				BEntry root("/");
158				// warp from "/" to Desktop properly
159				if (entry == root) {
160					if (showDisksIcon)
161						AddDisksIconToMenu(reverse);
162					entry = desktopEntry;
163				}
164
165				if (entry == desktopEntry)
166					hitRoot = true;
167			}
168
169			if (result == kReadAttrFailed || !info.fInvisible
170				|| (showDesktop && desktopEntry == entry)) {
171				AddItemToDirMenu(&entry, originatingWindow, reverse,
172					addShortcuts, navMenuEntries);
173			}
174
175			if (hitRoot) {
176				if (!showDesktop && showDisksIcon && *startEntry != "/")
177					AddDisksIconToMenu(reverse);
178				break;
179			}
180
181			entry = parentEntry;
182			if (entry.InitCheck() != B_OK)
183				break;
184		}
185
186		// select last item in menu
187		if (!select)
188			return;
189
190		ModelMenuItem* item
191			= dynamic_cast<ModelMenuItem*>(ItemAt(CountItems() - 1));
192		if (item != NULL) {
193			item->SetMarked(true);
194			if (menu) {
195				entry.SetTo(item->TargetModel()->EntryRef());
196				ThrowOnError(menu->SetEntry(&entry));
197			}
198		}
199	} catch (status_t err) {
200		PRINT(("BDirMenu::Populate: caught error %s\n", strerror(err)));
201		if (!CountItems()) {
202			BString error;
203			error << "Error [" << strerror(err) << "] populating menu";
204			AddItem(new BMenuItem(error.String(), 0));
205		}
206	}
207}
208
209
210void
211BDirMenu::AddItemToDirMenu(const BEntry* entry, BWindow* originatingWindow,
212	bool atEnd, bool addShortcuts, bool navMenuEntries)
213{
214	Model model(entry);
215	if (model.InitCheck() != B_OK)
216		return;
217
218	BMessage* message = new BMessage(fCommand);
219	message->AddRef(fEntryName.String(), model.EntryRef());
220
221	// add reference to the container windows model so that we can
222	// close the window if
223	BContainerWindow* window = originatingWindow ?
224		dynamic_cast<BContainerWindow*>(originatingWindow) : 0;
225	if (window != NULL) {
226		message->AddData("nodeRefsToClose", B_RAW_TYPE,
227			window->TargetModel()->NodeRef(), sizeof (node_ref));
228	}
229	ModelMenuItem* item;
230	if (navMenuEntries) {
231		BNavMenu* subMenu = new BNavMenu(model.Name(), B_REFS_RECEIVED,
232			fTarget, window);
233		entry_ref ref;
234		entry->GetRef(&ref);
235		subMenu->SetNavDir(&ref);
236		item = new ModelMenuItem(&model, subMenu);
237		item->SetLabel(model.Name());
238		item->SetMessage(message);
239	} else {
240		item = new ModelMenuItem(&model, model.Name(), message);
241	}
242
243	if (addShortcuts) {
244		if (model.IsDesktop())
245			item->SetShortcut('D', B_COMMAND_KEY);
246		else if (FSIsHomeDir(entry))
247			item->SetShortcut('H', B_COMMAND_KEY);
248	}
249
250	if (atEnd)
251		AddItem(item);
252	else
253		AddItem(item, 0);
254
255	item->SetTarget(fTarget);
256
257	if (fMenuBar != NULL) {
258		ModelMenuItem* menu
259			= dynamic_cast<ModelMenuItem*>(fMenuBar->ItemAt(0));
260		if (menu != NULL) {
261			ThrowOnError(menu->SetEntry(entry));
262			item->SetMarked(true);
263		}
264	}
265}
266
267
268void
269BDirMenu::AddDisksIconToMenu(bool atEnd)
270{
271	BEntry entry("/");
272	Model model(&entry);
273	if (model.InitCheck() != B_OK)
274		return;
275
276	BMessage* message = new BMessage(fCommand);
277	message->AddRef(fEntryName.String(), model.EntryRef());
278
279	ModelMenuItem* item = new ModelMenuItem(&model,
280		B_TRANSLATE(B_DISKS_DIR_NAME), message);
281	if (atEnd)
282		AddItem(item);
283	else
284		AddItem(item, 0);
285}
286