xref: /haiku/src/kits/tracker/DirMenu.cpp (revision f2b4344867e97c3f4e742a1b4a15e6879644601a)
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 trademarks
30 of Be Incorporated in the United States and other countries. Other brand product
31 names are registered trademarks or trademarks of their respective holders.
32 All 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_TRANSLATE_CONTEXT
58 #define B_TRANSLATE_CONTEXT "DirMenu"
59 
60 
61 BDirMenu::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 
77 BDirMenu::~BDirMenu()
78 {
79 }
80 
81 
82 void
83 BDirMenu::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 			// if we're at the root directory skip "mnt" and go straight to "/"
113 			if (!showDesktop && dir.InitCheck() == B_OK && dir.IsRootDirectory())
114 				parent.SetTo("/");
115 			else
116 				entry.GetParent(&parent);
117 
118 			parent.GetEntry(&entry);
119 		}
120 
121 		BDirectory desktopDir;
122 		FSGetDeskDir(&desktopDir);
123 		BEntry desktopEntry;
124 		desktopDir.GetEntry(&desktopEntry);
125 
126 		for (;;) {
127 			BNode node(&entry);
128 			ThrowOnInitCheckError(&node);
129 
130 			PoseInfo info;
131 			ReadAttrResult result = ReadAttr(&node, kAttrPoseInfo,
132 				kAttrPoseInfoForeign, B_RAW_TYPE, 0, &info, sizeof(PoseInfo),
133 				&PoseInfo::EndianSwap);
134 
135 			BDirectory parent;
136 			entry.GetParent(&parent);
137 
138 			bool hitRoot = false;
139 
140 			// if we're at the root directory skip "mnt" and go straight to "/"
141 			BDirectory dir(&entry);
142 			if (!showDesktop && dir.InitCheck() == B_OK && dir.IsRootDirectory()) {
143 				hitRoot = true;
144 				parent.SetTo("/");
145 			}
146 
147 			if (showDesktop) {
148 				BEntry root("/");
149 				// warp from "/" to Desktop properly
150 				if (entry == root) {
151 					if (showDisksIcon)
152 						AddDisksIconToMenu(reverse);
153 					entry = desktopEntry;
154 				}
155 
156 				if (entry == desktopEntry)
157 					hitRoot = true;
158 			}
159 
160 			if (result == kReadAttrFailed || !info.fInvisible
161 				|| (showDesktop && desktopEntry == entry)) {
162 				AddItemToDirMenu(&entry, originatingWindow, reverse,
163 								 addShortcuts, navMenuEntries);
164 			}
165 
166 			if (hitRoot) {
167 				if (!showDesktop && showDisksIcon && *startEntry != "/")
168 					AddDisksIconToMenu(reverse);
169 				break;
170 			}
171 
172 			parent.GetEntry(&entry);
173 		}
174 
175 		// select last item in menu
176 		if (!select)
177 			return;
178 
179 		ModelMenuItem *item = dynamic_cast<ModelMenuItem *>(ItemAt(CountItems() - 1));
180 		if (item) {
181 			item->SetMarked(true);
182 			if (menu) {
183 				entry.SetTo(item->TargetModel()->EntryRef());
184 				ThrowOnError(menu->SetEntry(&entry));
185 			}
186 		}
187 	} catch (status_t err) {
188 		PRINT(("BDirMenu::Populate: caught error %s\n", strerror(err)));
189 		if (!CountItems()) {
190 			BString error;
191 			error << "Error [" << strerror(err) << "] populating menu";
192 			AddItem(new BMenuItem(error.String(), 0));
193 		}
194 	}
195 }
196 
197 
198 void
199 BDirMenu::AddItemToDirMenu(const BEntry *entry, BWindow *originatingWindow,
200 	bool atEnd, bool addShortcuts, bool navMenuEntries)
201 {
202 	Model model(entry);
203 	if (model.InitCheck() != B_OK)
204 		return;
205 
206 	BMessage *message = new BMessage(fCommand);
207 	message->AddRef(fEntryName.String(), model.EntryRef());
208 
209 	// add reference to the container windows model so that we can
210 	// close the window if
211 	BContainerWindow *window = originatingWindow ?
212 		dynamic_cast<BContainerWindow *>(originatingWindow) : 0;
213 	if (window)
214 		message->AddData("nodeRefsToClose", B_RAW_TYPE, window->TargetModel()->NodeRef(),
215 			sizeof (node_ref));
216 	ModelMenuItem *item;
217 	if (navMenuEntries) {
218 		BNavMenu* subMenu = new BNavMenu(model.Name(), B_REFS_RECEIVED, fTarget,
219 			window);
220 		entry_ref ref;
221 		entry->GetRef(&ref);
222 		subMenu->SetNavDir(&ref);
223 		item = new ModelMenuItem(&model, subMenu);
224 		item->SetLabel(model.Name());
225 		item->SetMessage(message);
226 	} else {
227 		item = new ModelMenuItem(&model, model.Name(), message);
228 	}
229 
230 	if (addShortcuts) {
231 		if (model.IsDesktop())
232 			item->SetShortcut('D', B_COMMAND_KEY);
233 		else if (FSIsHomeDir(entry))
234 			item->SetShortcut('H', B_COMMAND_KEY);
235 	}
236 
237 	if (atEnd)
238 		AddItem(item);
239 	else
240 		AddItem(item, 0);
241 
242 	item->SetTarget(fTarget);
243 
244 	if (fMenuBar) {
245 		ModelMenuItem *menu = dynamic_cast<ModelMenuItem *>(fMenuBar->ItemAt(0));
246 		if (menu) {
247 			ThrowOnError(menu->SetEntry(entry));
248 			item->SetMarked(true);
249 		}
250 	}
251 }
252 
253 
254 void
255 BDirMenu::AddDisksIconToMenu(bool atEnd)
256 {
257 	BEntry entry("/");
258 	Model model(&entry);
259 	if (model.InitCheck() != B_OK)
260 		return;
261 
262 	BMessage *message = new BMessage(fCommand);
263 	message->AddRef(fEntryName.String(), model.EntryRef());
264 
265 	ModelMenuItem* item = new ModelMenuItem(&model,	B_TRANSLATE("Disks"),
266 		message);
267 	if (atEnd)
268 		AddItem(item);
269 	else
270 		AddItem(item, 0);
271 }
272 
273