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