18df661e6SAxel Dörfler/*
24b723e3fSAxel Dörfler * Copyright 2003-2013, Axel D��rfler, axeld@pinc-software.de.
3d2b49a00SRene Gollent * Copyright 2011, Rene Gollent, rene@gollent.com.
45c0f8450SIngo Weinhold * Copyright 2013-2014, Ingo Weinhold, ingo_weinhold@gmx.de.
52d7ad656SAxel Dörfler * Distributed under the terms of the MIT License.
62d7ad656SAxel Dörfler */
78df661e6SAxel Dörfler
88df661e6SAxel Dörfler
98df661e6SAxel Dörfler#include "menu.h"
1028a2172cSIngo Weinhold
11085cf27bSIngo Weinhold#include <errno.h>
123aeed660SJérôme Duval#include <strings.h>
138df661e6SAxel Dörfler
143ce26345SIngo Weinhold#include <algorithm>
153ce26345SIngo Weinhold
168df661e6SAxel Dörfler#include <OS.h>
178df661e6SAxel Dörfler
18c04f3a62SIngo Weinhold#include <AutoDeleter.h>
19c54d49c2SAxel Dörfler#include <boot/menu.h>
20c04f3a62SIngo Weinhold#include <boot/PathBlacklist.h>
218df661e6SAxel Dörfler#include <boot/stage2.h>
228df661e6SAxel Dörfler#include <boot/vfs.h>
238df661e6SAxel Dörfler#include <boot/platform.h>
24085cf27bSIngo Weinhold#include <boot/platform/generic/text_console.h>
258df661e6SAxel Dörfler#include <boot/stdio.h>
262d7ad656SAxel Dörfler#include <safemode.h>
2728a2172cSIngo Weinhold#include <util/ring_buffer.h>
28c04f3a62SIngo Weinhold#include <util/SinglyLinkedList.h>
298df661e6SAxel Dörfler
3038c7ed7cSIngo Weinhold#include "kernel_debug_config.h"
3138c7ed7cSIngo Weinhold
3228a2172cSIngo Weinhold#include "load_driver_settings.h"
3328a2172cSIngo Weinhold#include "loader.h"
345c0f8450SIngo Weinhold#include "package_support.h"
3528a2172cSIngo Weinhold#include "pager.h"
3628a2172cSIngo Weinhold#include "RootFileSystem.h"
37c54d49c2SAxel Dörfler
3889294b50SAxel Dörfler
39c04f3a62SIngo Weinhold//#define TRACE_MENU
40fc205e97SMarcus Overhagen#ifdef TRACE_MENU
41fc205e97SMarcus Overhagen#	define TRACE(x) dprintf x
42fc205e97SMarcus Overhagen#else
43fc205e97SMarcus Overhagen#	define TRACE(x) ;
44fc205e97SMarcus Overhagen#endif
45c54d49c2SAxel Dörfler
4689294b50SAxel Dörfler
47c04f3a62SIngo Weinhold// only set while in user_menu()
48c04f3a62SIngo Weinholdstatic Menu* sMainMenu = NULL;
49c04f3a62SIngo Weinholdstatic Menu* sBlacklistRootMenu = NULL;
50c04f3a62SIngo Weinholdstatic BootVolume* sBootVolume = NULL;
51c04f3a62SIngo Weinholdstatic PathBlacklist* sPathBlacklist;
52d2b49a00SRene Gollent
53d2b49a00SRene Gollent
54c54d49c2SAxel DörflerMenuItem::MenuItem(const char *label, Menu *subMenu)
55c54d49c2SAxel Dörfler	:
56c54d49c2SAxel Dörfler	fLabel(strdup(label)),
57c54d49c2SAxel Dörfler	fTarget(NULL),
58c54d49c2SAxel Dörfler	fIsMarked(false),
59c54d49c2SAxel Dörfler	fIsSelected(false),
60b430068aSAxel Dörfler	fIsEnabled(true),
61c54d49c2SAxel Dörfler	fType(MENU_ITEM_STANDARD),
62c54d49c2SAxel Dörfler	fMenu(NULL),
636c7abe98SIngo Weinhold	fSubMenu(NULL),
64649f65ccSAxel Dörfler	fData(NULL),
6580b5decbSIngo Weinhold	fHelpText(NULL),
6680b5decbSIngo Weinhold	fShortcut(0)
67c54d49c2SAxel Dörfler{
686c7abe98SIngo Weinhold	SetSubmenu(subMenu);
69c54d49c2SAxel Dörfler}
70c54d49c2SAxel Dörfler
71c54d49c2SAxel Dörfler
72c54d49c2SAxel DörflerMenuItem::~MenuItem()
73c54d49c2SAxel Dörfler{
746c7abe98SIngo Weinhold	delete fSubMenu;
75c54d49c2SAxel Dörfler	free(const_cast<char *>(fLabel));
76c54d49c2SAxel Dörfler}
77c54d49c2SAxel Dörfler
78c54d49c2SAxel Dörfler
79c668dff5SAxel Dörflervoid
80c54d49c2SAxel DörflerMenuItem::SetTarget(menu_item_hook target)
81c54d49c2SAxel Dörfler{
82c54d49c2SAxel Dörfler	fTarget = target;
83c54d49c2SAxel Dörfler}
84c54d49c2SAxel Dörfler
85c54d49c2SAxel Dörfler
86df13a980SAxel Dörfler/**	Marks or unmarks a menu item. A marked menu item usually gets a visual
87df13a980SAxel Dörfler *	clue like a checkmark that distinguishes it from others.
88df13a980SAxel Dörfler *	For menus of type CHOICE_MENU, there can only be one marked item - the
89df13a980SAxel Dörfler *	chosen one.
90df13a980SAxel Dörfler */
91df13a980SAxel Dörfler
92c668dff5SAxel Dörflervoid
93c54d49c2SAxel DörflerMenuItem::SetMarked(bool marked)
94c54d49c2SAxel Dörfler{
95df13a980SAxel Dörfler	if (marked && fMenu != NULL && fMenu->Type() == CHOICE_MENU) {
96df13a980SAxel Dörfler		// always set choice text of parent if we were marked
97df13a980SAxel Dörfler		fMenu->SetChoiceText(Label());
98df13a980SAxel Dörfler	}
99df13a980SAxel Dörfler
100c54d49c2SAxel Dörfler	if (fIsMarked == marked)
101c54d49c2SAxel Dörfler		return;
102c54d49c2SAxel Dörfler
103c54d49c2SAxel Dörfler	if (marked && fMenu != NULL && fMenu->Type() == CHOICE_MENU) {
104c54d49c2SAxel Dörfler		// unmark previous item
105c54d49c2SAxel Dörfler		MenuItem *markedItem = fMenu->FindMarked();
106c54d49c2SAxel Dörfler		if (markedItem != NULL)
107c54d49c2SAxel Dörfler			markedItem->SetMarked(false);
108c54d49c2SAxel Dörfler	}
109c54d49c2SAxel Dörfler
110c54d49c2SAxel Dörfler	fIsMarked = marked;
111c54d49c2SAxel Dörfler
112c54d49c2SAxel Dörfler	if (fMenu != NULL)
113c54d49c2SAxel Dörfler		fMenu->Draw(this);
114c54d49c2SAxel Dörfler}
115c54d49c2SAxel Dörfler
116c54d49c2SAxel Dörfler
1175ea23bb0SAxel Dörflervoid
1185ea23bb0SAxel DörflerMenuItem::Select(bool selected)
1195ea23bb0SAxel Dörfler{
1205ea23bb0SAxel Dörfler	if (fIsSelected == selected)
1215ea23bb0SAxel Dörfler		return;
122c54d49c2SAxel Dörfler
123c54d49c2SAxel Dörfler	if (selected && fMenu != NULL) {
124c54d49c2SAxel Dörfler		// unselect previous item
1255ea23bb0SAxel Dörfler		MenuItem *selectedItem = fMenu->FindSelected();
1265ea23bb0SAxel Dörfler		if (selectedItem != NULL)
1275ea23bb0SAxel Dörfler			selectedItem->Select(false);
1285ea23bb0SAxel Dörfler	}
129c54d49c2SAxel Dörfler
130c54d49c2SAxel Dörfler	fIsSelected = selected;
131c54d49c2SAxel Dörfler
132c54d49c2SAxel Dörfler	if (fMenu != NULL)
133c54d49c2SAxel Dörfler		fMenu->Draw(this);
134c54d49c2SAxel Dörfler}
135c54d49c2SAxel Dörfler
136c54d49c2SAxel Dörfler
137c668dff5SAxel Dörflervoid
138c54d49c2SAxel DörflerMenuItem::SetType(menu_item_type type)
139c54d49c2SAxel Dörfler{
140c54d49c2SAxel Dörfler	fType = type;
141c54d49c2SAxel Dörfler}
142c54d49c2SAxel Dörfler
143c54d49c2SAxel Dörfler
144b430068aSAxel Dörflervoid
145b430068aSAxel DörflerMenuItem::SetEnabled(bool enabled)
146b430068aSAxel Dörfler{
147b430068aSAxel Dörfler	if (fIsEnabled == enabled)
148b430068aSAxel Dörfler		return;
149b430068aSAxel Dörfler
150b430068aSAxel Dörfler	fIsEnabled = enabled;
151b430068aSAxel Dörfler
152b430068aSAxel Dörfler	if (fMenu != NULL)
153b430068aSAxel Dörfler		fMenu->Draw(this);
154b430068aSAxel Dörfler}
155b430068aSAxel Dörfler
156b430068aSAxel Dörfler
157c668dff5SAxel Dörflervoid
1582d7ad656SAxel DörflerMenuItem::SetData(const void *data)
159c668dff5SAxel Dörfler{
160c668dff5SAxel Dörfler	fData = data;
161c668dff5SAxel Dörfler}
162c668dff5SAxel Dörfler
163c668dff5SAxel Dörfler
164868aa7a0SAxel Dörfler/*!	This sets a help text that is shown when the item is
165868aa7a0SAxel Dörfler	selected.
166868aa7a0SAxel Dörfler	Note, unlike the label, the string is not copied, it's
167868aa7a0SAxel Dörfler	just referenced and has to stay valid as long as the
168868aa7a0SAxel Dörfler	item's menu is being used.
169868aa7a0SAxel Dörfler*/
170649f65ccSAxel Dörflervoid
171868aa7a0SAxel DörflerMenuItem::SetHelpText(const char* text)
172649f65ccSAxel Dörfler{
173649f65ccSAxel Dörfler	fHelpText = text;
174649f65ccSAxel Dörfler}
175649f65ccSAxel Dörfler
176649f65ccSAxel Dörfler
17789294b50SAxel Dörflervoid
178868aa7a0SAxel DörflerMenuItem::SetShortcut(char key)
179868aa7a0SAxel Dörfler{
180868aa7a0SAxel Dörfler	fShortcut = key;
181868aa7a0SAxel Dörfler}
182868aa7a0SAxel Dörfler
183868aa7a0SAxel Dörfler
184cbfc38c6SIngo Weinholdvoid
185cbfc38c6SIngo WeinholdMenuItem::SetLabel(const char* label)
186cbfc38c6SIngo Weinhold{
187cbfc38c6SIngo Weinhold	if (char* newLabel = strdup(label)) {
188cbfc38c6SIngo Weinhold		free(const_cast<char*>(fLabel));
189cbfc38c6SIngo Weinhold		fLabel = newLabel;
190cbfc38c6SIngo Weinhold	}
191cbfc38c6SIngo Weinhold}
192cbfc38c6SIngo Weinhold
193cbfc38c6SIngo Weinhold
1946c7abe98SIngo Weinholdvoid
1956c7abe98SIngo WeinholdMenuItem::SetSubmenu(Menu* subMenu)
1966c7abe98SIngo Weinhold{
1976c7abe98SIngo Weinhold	fSubMenu = subMenu;
1986c7abe98SIngo Weinhold
1996c7abe98SIngo Weinhold	if (fSubMenu != NULL)
2006c7abe98SIngo Weinhold		fSubMenu->fSuperItem = this;
2016c7abe98SIngo Weinhold}
2026c7abe98SIngo Weinhold
2036c7abe98SIngo Weinhold
204868aa7a0SAxel Dörflervoid
205868aa7a0SAxel DörflerMenuItem::SetMenu(Menu* menu)
206c54d49c2SAxel Dörfler{
207c54d49c2SAxel Dörfler	fMenu = menu;
208c54d49c2SAxel Dörfler}
209c54d49c2SAxel Dörfler
210c54d49c2SAxel Dörfler
211c54d49c2SAxel Dörfler//	#pragma mark -
212c54d49c2SAxel Dörfler
213c54d49c2SAxel Dörfler
214868aa7a0SAxel DörflerMenu::Menu(menu_type type, const char* title)
215c54d49c2SAxel Dörfler	:
216c54d49c2SAxel Dörfler	fTitle(title),
2175ea23bb0SAxel Dörfler	fChoiceText(NULL),
218c54d49c2SAxel Dörfler	fCount(0),
219c54d49c2SAxel Dörfler	fIsHidden(true),
220628e5a03SAxel Dörfler	fType(type),
221868aa7a0SAxel Dörfler	fSuperItem(NULL),
222868aa7a0SAxel Dörfler	fShortcuts(NULL)
223c54d49c2SAxel Dörfler{
224c54d49c2SAxel Dörfler}
225c54d49c2SAxel Dörfler
226c54d49c2SAxel Dörfler
227c54d49c2SAxel DörflerMenu::~Menu()
228c54d49c2SAxel Dörfler{
229c54d49c2SAxel Dörfler	// take all remaining items with us
230c54d49c2SAxel Dörfler
231c54d49c2SAxel Dörfler	MenuItem *item;
232b0f7c845SIngo Weinhold	while ((item = fItems.Head()) != NULL) {
233b0f7c845SIngo Weinhold		fItems.Remove(item);
234c54d49c2SAxel Dörfler		delete item;
235c54d49c2SAxel Dörfler	}
236c54d49c2SAxel Dörfler}
237c54d49c2SAxel Dörfler
238c54d49c2SAxel Dörfler
2396c7abe98SIngo Weinholdvoid
2406c7abe98SIngo WeinholdMenu::Entered()
2416c7abe98SIngo Weinhold{
2426c7abe98SIngo Weinhold}
2436c7abe98SIngo Weinhold
2446c7abe98SIngo Weinhold
2456c7abe98SIngo Weinholdvoid
2466c7abe98SIngo WeinholdMenu::Exited()
2476c7abe98SIngo Weinhold{
2486c7abe98SIngo Weinhold}
2496c7abe98SIngo Weinhold
2506c7abe98SIngo Weinhold
251868aa7a0SAxel DörflerMenuItem*
252c54d49c2SAxel DörflerMenu::ItemAt(int32 index)
253c54d49c2SAxel Dörfler{
254c54d49c2SAxel Dörfler	if (index < 0 || index >= fCount)
255c54d49c2SAxel Dörfler		return NULL;
256c54d49c2SAxel Dörfler
257c54d49c2SAxel Dörfler	MenuItemIterator iterator = ItemIterator();
258c54d49c2SAxel Dörfler	MenuItem *item;
259c54d49c2SAxel Dörfler
260c54d49c2SAxel Dörfler	while ((item = iterator.Next()) != NULL) {
261c54d49c2SAxel Dörfler		if (index-- == 0)
262c54d49c2SAxel Dörfler			return item;
263c54d49c2SAxel Dörfler	}
264c54d49c2SAxel Dörfler
265c54d49c2SAxel Dörfler	return NULL;
266c54d49c2SAxel Dörfler}
267c54d49c2SAxel Dörfler
268c54d49c2SAxel Dörfler
26989294b50SAxel Dörflerint32
270868aa7a0SAxel DörflerMenu::IndexOf(MenuItem* searchedItem)
271c54d49c2SAxel Dörfler{
272c54d49c2SAxel Dörfler	int32 index = 0;
273c54d49c2SAxel Dörfler
274868aa7a0SAxel Dörfler	MenuItemIterator iterator = ItemIterator();
275868aa7a0SAxel Dörfler	while (MenuItem* item = iterator.Next()) {
276c54d49c2SAxel Dörfler		if (item == searchedItem)
277c54d49c2SAxel Dörfler			return index;
278c54d49c2SAxel Dörfler
279c54d49c2SAxel Dörfler		index++;
280c54d49c2SAxel Dörfler	}
281c54d49c2SAxel Dörfler
282c54d49c2SAxel Dörfler	return -1;
283c54d49c2SAxel Dörfler}
284c54d49c2SAxel Dörfler
285c54d49c2SAxel Dörfler
286c54d49c2SAxel Dörflerint32
287c54d49c2SAxel DörflerMenu::CountItems() const
288c54d49c2SAxel Dörfler{
289c54d49c2SAxel Dörfler	return fCount;
290c54d49c2SAxel Dörfler}
291c54d49c2SAxel Dörfler
292c54d49c2SAxel Dörfler
293868aa7a0SAxel DörflerMenuItem*
294868aa7a0SAxel DörflerMenu::FindItem(const char* label)
295628e5a03SAxel Dörfler{
296628e5a03SAxel Dörfler	MenuItemIterator iterator = ItemIterator();
297868aa7a0SAxel Dörfler	while (MenuItem* item = iterator.Next()) {
298628e5a03SAxel Dörfler		if (item->Label() != NULL && !strcmp(item->Label(), label))
299628e5a03SAxel Dörfler			return item;
300628e5a03SAxel Dörfler	}
301628e5a03SAxel Dörfler
302628e5a03SAxel Dörfler	return NULL;
303628e5a03SAxel Dörfler}
304628e5a03SAxel Dörfler
305628e5a03SAxel Dörfler
306868aa7a0SAxel DörflerMenuItem*
307c54d49c2SAxel DörflerMenu::FindMarked()
308c54d49c2SAxel Dörfler{
309c54d49c2SAxel Dörfler	MenuItemIterator iterator = ItemIterator();
310868aa7a0SAxel Dörfler	while (MenuItem* item = iterator.Next()) {
311c54d49c2SAxel Dörfler		if (item->IsMarked())
312c54d49c2SAxel Dörfler			return item;
313c54d49c2SAxel Dörfler	}
314c54d49c2SAxel Dörfler
315c54d49c2SAxel Dörfler	return NULL;
316c54d49c2SAxel Dörfler}
317c54d49c2SAxel Dörfler
318c54d49c2SAxel Dörfler
319868aa7a0SAxel DörflerMenuItem*
320868aa7a0SAxel DörflerMenu::FindSelected(int32* _index)
321c54d49c2SAxel Dörfler{
322c54d49c2SAxel Dörfler	int32 index = 0;
323c54d49c2SAxel Dörfler
324868aa7a0SAxel Dörfler	MenuItemIterator iterator = ItemIterator();
325868aa7a0SAxel Dörfler	while (MenuItem* item = iterator.Next()) {
326c54d49c2SAxel Dörfler		if (item->IsSelected()) {
327c54d49c2SAxel Dörfler			if (_index != NULL)
328c54d49c2SAxel Dörfler				*_index = index;
329c54d49c2SAxel Dörfler			return item;
330c54d49c2SAxel Dörfler		}
331c54d49c2SAxel Dörfler
332c54d49c2SAxel Dörfler		index++;
333c54d49c2SAxel Dörfler	}
334c54d49c2SAxel Dörfler
335c54d49c2SAxel Dörfler	return NULL;
336c54d49c2SAxel Dörfler}
337c54d49c2SAxel Dörfler
338c54d49c2SAxel Dörfler
339c54d49c2SAxel Dörflervoid
340868aa7a0SAxel DörflerMenu::AddItem(MenuItem* item)
341c54d49c2SAxel Dörfler{
342c54d49c2SAxel Dörfler	item->fMenu = this;
343c54d49c2SAxel Dörfler	fItems.Add(item);
344c54d49c2SAxel Dörfler	fCount++;
345c54d49c2SAxel Dörfler}
346c54d49c2SAxel Dörfler
3478df661e6SAxel Dörfler
3488df661e6SAxel Dörflerstatus_t
349c54d49c2SAxel DörflerMenu::AddSeparatorItem()
350c54d49c2SAxel Dörfler{
351868aa7a0SAxel Dörfler	MenuItem* item = new(std::nothrow) MenuItem();
352c54d49c2SAxel Dörfler	if (item == NULL)
353c54d49c2SAxel Dörfler		return B_NO_MEMORY;
354c54d49c2SAxel Dörfler
355c54d49c2SAxel Dörfler	item->SetType(MENU_ITEM_SEPARATOR);
356c54d49c2SAxel Dörfler
357c54d49c2SAxel Dörfler	AddItem(item);
358c54d49c2SAxel Dörfler	return B_OK;
359c54d49c2SAxel Dörfler}
360c54d49c2SAxel Dörfler
361c54d49c2SAxel Dörfler
362868aa7a0SAxel DörflerMenuItem*
363c54d49c2SAxel DörflerMenu::RemoveItemAt(int32 index)
364c54d49c2SAxel Dörfler{
365c54d49c2SAxel Dörfler	if (index < 0 || index >= fCount)
366c54d49c2SAxel Dörfler		return NULL;
367c54d49c2SAxel Dörfler
368c54d49c2SAxel Dörfler	MenuItemIterator iterator = ItemIterator();
369868aa7a0SAxel Dörfler	while (MenuItem* item = iterator.Next()) {
370c54d49c2SAxel Dörfler		if (index-- == 0) {
371c54d49c2SAxel Dörfler			RemoveItem(item);
372c54d49c2SAxel Dörfler			return item;
373c54d49c2SAxel Dörfler		}
374c54d49c2SAxel Dörfler	}
375c54d49c2SAxel Dörfler
376c54d49c2SAxel Dörfler	return NULL;
377c54d49c2SAxel Dörfler}
378c54d49c2SAxel Dörfler
379c54d49c2SAxel Dörfler
380c54d49c2SAxel Dörflervoid
381868aa7a0SAxel DörflerMenu::RemoveItem(MenuItem* item)
382c54d49c2SAxel Dörfler{
383c54d49c2SAxel Dörfler	item->fMenu = NULL;
384c54d49c2SAxel Dörfler	fItems.Remove(item);
385c54d49c2SAxel Dörfler	fCount--;
386c54d49c2SAxel Dörfler}
387c54d49c2SAxel Dörfler
388c54d49c2SAxel Dörfler
389c54d49c2SAxel Dörflervoid
390868aa7a0SAxel DörflerMenu::AddShortcut(char key, shortcut_hook function)
391c54d49c2SAxel Dörfler{
39236c80d70SAxel Dörfler	Menu::shortcut* shortcut = new(std::nothrow) Menu::shortcut;
393868aa7a0SAxel Dörfler	if (shortcut == NULL)
394868aa7a0SAxel Dörfler		return;
395868aa7a0SAxel Dörfler
396868aa7a0SAxel Dörfler	shortcut->key = key;
397868aa7a0SAxel Dörfler	shortcut->function = function;
398868aa7a0SAxel Dörfler
399868aa7a0SAxel Dörfler	shortcut->next = fShortcuts;
400868aa7a0SAxel Dörfler	fShortcuts = shortcut;
401868aa7a0SAxel Dörfler}
402868aa7a0SAxel Dörfler
403868aa7a0SAxel Dörfler
404868aa7a0SAxel Dörflershortcut_hook
405868aa7a0SAxel DörflerMenu::FindShortcut(char key) const
406868aa7a0SAxel Dörfler{
407868aa7a0SAxel Dörfler	if (key == 0)
408868aa7a0SAxel Dörfler		return NULL;
409868aa7a0SAxel Dörfler
410868aa7a0SAxel Dörfler	const Menu::shortcut* shortcut = fShortcuts;
411868aa7a0SAxel Dörfler	while (shortcut != NULL) {
412868aa7a0SAxel Dörfler		if (shortcut->key == key)
413868aa7a0SAxel Dörfler			return shortcut->function;
414868aa7a0SAxel Dörfler
415868aa7a0SAxel Dörfler		shortcut = shortcut->next;
416868aa7a0SAxel Dörfler	}
417868aa7a0SAxel Dörfler
418c552bd67SJessica Hamilton	Menu *superMenu = Supermenu();
419c552bd67SJessica Hamilton
420c552bd67SJessica Hamilton	if (superMenu != NULL)
421c552bd67SJessica Hamilton		return superMenu->FindShortcut(key);
422c552bd67SJessica Hamilton
423868aa7a0SAxel Dörfler	return NULL;
424868aa7a0SAxel Dörfler}
425868aa7a0SAxel Dörfler
426868aa7a0SAxel Dörfler
427868aa7a0SAxel DörflerMenuItem*
428868aa7a0SAxel DörflerMenu::FindItemByShortcut(char key)
429868aa7a0SAxel Dörfler{
430868aa7a0SAxel Dörfler	if (key == 0)
431868aa7a0SAxel Dörfler		return NULL;
432868aa7a0SAxel Dörfler
433868aa7a0SAxel Dörfler	MenuItemList::Iterator iterator = ItemIterator();
434868aa7a0SAxel Dörfler	while (MenuItem* item = iterator.Next()) {
435868aa7a0SAxel Dörfler		if (item->Shortcut() == key)
436868aa7a0SAxel Dörfler			return item;
437868aa7a0SAxel Dörfler	}
438868aa7a0SAxel Dörfler
439c552bd67SJessica Hamilton	Menu *superMenu = Supermenu();
440c552bd67SJessica Hamilton
441c552bd67SJessica Hamilton	if (superMenu != NULL)
442c552bd67SJessica Hamilton		return superMenu->FindItemByShortcut(key);
443c552bd67SJessica Hamilton
444868aa7a0SAxel Dörfler	return NULL;
445c54d49c2SAxel Dörfler}
446c54d49c2SAxel Dörfler
447c54d49c2SAxel Dörfler
4486c7abe98SIngo Weinholdvoid
4496c7abe98SIngo WeinholdMenu::SortItems(bool (*less)(const MenuItem*, const MenuItem*))
4506c7abe98SIngo Weinhold{
4516c7abe98SIngo Weinhold	fItems.Sort(less);
4526c7abe98SIngo Weinhold}
4536c7abe98SIngo Weinhold
4546c7abe98SIngo Weinhold
455c54d49c2SAxel Dörflervoid
456c54d49c2SAxel DörflerMenu::Run()
4578df661e6SAxel Dörfler{
458c668dff5SAxel Dörfler	platform_run_menu(this);
459c54d49c2SAxel Dörfler}
460c54d49c2SAxel Dörfler
461c54d49c2SAxel Dörfler
462868aa7a0SAxel Dörflervoid
463868aa7a0SAxel DörflerMenu::Draw(MenuItem* item)
464868aa7a0SAxel Dörfler{
465868aa7a0SAxel Dörfler	if (!IsHidden())
466868aa7a0SAxel Dörfler		platform_update_menu_item(this, item);
467868aa7a0SAxel Dörfler}
468868aa7a0SAxel Dörfler
469868aa7a0SAxel Dörfler
470c54d49c2SAxel Dörfler//	#pragma mark -
471c8079ef9SAxel Dörfler
472c54d49c2SAxel Dörfler
473085cf27bSIngo Weinholdstatic const char*
474085cf27bSIngo Weinholdsize_to_string(off_t size, char* buffer, size_t bufferSize)
475085cf27bSIngo Weinhold{
476085cf27bSIngo Weinhold	static const char* const kPrefixes[] = { "K", "M", "G", "T", "P", NULL };
477085cf27bSIngo Weinhold	int32 nextIndex = 0;
478085cf27bSIngo Weinhold	int32 remainder = 0;
479085cf27bSIngo Weinhold	while (size >= 1024 && kPrefixes[nextIndex] != NULL) {
480085cf27bSIngo Weinhold		remainder = size % 1024;
481085cf27bSIngo Weinhold		size /= 1024;
482085cf27bSIngo Weinhold		nextIndex++;
483085cf27bSIngo Weinhold
484085cf27bSIngo Weinhold		if (size < 1024) {
485085cf27bSIngo Weinhold			// Compute the decimal remainder and make sure we have at most
486085cf27bSIngo Weinhold			// 3 decimal places (or 4 for 1000 <= size <= 1023).
487085cf27bSIngo Weinhold			int32 factor;
488085cf27bSIngo Weinhold			if (size >= 100)
489085cf27bSIngo Weinhold				factor = 100;
490085cf27bSIngo Weinhold			else if (size >= 10)
491085cf27bSIngo Weinhold				factor = 10;
492085cf27bSIngo Weinhold			else
493085cf27bSIngo Weinhold				factor = 1;
494085cf27bSIngo Weinhold
495085cf27bSIngo Weinhold			remainder = (remainder * 1000 + 5 * factor) / 1024;
496085cf27bSIngo Weinhold
497085cf27bSIngo Weinhold			if (remainder >= 1000) {
498085cf27bSIngo Weinhold				size++;
499085cf27bSIngo Weinhold				remainder = 0;
500085cf27bSIngo Weinhold			} else
501085cf27bSIngo Weinhold				remainder /= 10 * factor;
502085cf27bSIngo Weinhold		} else
503085cf27bSIngo Weinhold			size += (remainder + 512) / 1024;
504085cf27bSIngo Weinhold	}
505085cf27bSIngo Weinhold
506085cf27bSIngo Weinhold	if (remainder == 0) {
507085cf27bSIngo Weinhold		snprintf(buffer, bufferSize, "%" B_PRIdOFF, size);
508085cf27bSIngo Weinhold	} else {
509085cf27bSIngo Weinhold		snprintf(buffer, bufferSize, "%" B_PRIdOFF ".%" B_PRId32, size,
510085cf27bSIngo Weinhold			remainder);
511085cf27bSIngo Weinhold	}
512085cf27bSIngo Weinhold
513085cf27bSIngo Weinhold	size_t length = strlen(buffer);
514085cf27bSIngo Weinhold	snprintf(buffer + length, bufferSize - length, " %sB",
515085cf27bSIngo Weinhold		nextIndex == 0 ? "" : kPrefixes[nextIndex - 1]);
516085cf27bSIngo Weinhold
517085cf27bSIngo Weinhold	return buffer;
518085cf27bSIngo Weinhold}
519085cf27bSIngo Weinhold
520085cf27bSIngo Weinhold
521c04f3a62SIngo Weinhold// #pragma mark - blacklist menu
522c04f3a62SIngo Weinhold
523c04f3a62SIngo Weinhold
524c04f3a62SIngo Weinholdclass BlacklistMenuItem : public MenuItem {
525c04f3a62SIngo Weinholdpublic:
526c04f3a62SIngo Weinhold	BlacklistMenuItem(char* label, Node* node, Menu* subMenu)
527c04f3a62SIngo Weinhold		:
528c04f3a62SIngo Weinhold		MenuItem(label, subMenu),
529c04f3a62SIngo Weinhold		fNode(node),
530c04f3a62SIngo Weinhold		fSubMenu(subMenu)
531c04f3a62SIngo Weinhold	{
532c04f3a62SIngo Weinhold		fNode->Acquire();
533c04f3a62SIngo Weinhold		SetType(MENU_ITEM_MARKABLE);
534c04f3a62SIngo Weinhold	}
535c04f3a62SIngo Weinhold
536c04f3a62SIngo Weinhold	~BlacklistMenuItem()
537c04f3a62SIngo Weinhold	{
538c04f3a62SIngo Weinhold		fNode->Release();
539c04f3a62SIngo Weinhold
540c04f3a62SIngo Weinhold		// make sure the submenu is destroyed
541c04f3a62SIngo Weinhold		SetSubmenu(fSubMenu);
542c04f3a62SIngo Weinhold	}
543c04f3a62SIngo Weinhold
544c04f3a62SIngo Weinhold	bool IsDirectoryItem() const
545c04f3a62SIngo Weinhold	{
546c04f3a62SIngo Weinhold		return fNode->Type() == S_IFDIR;
547c04f3a62SIngo Weinhold	}
548c04f3a62SIngo Weinhold
549c04f3a62SIngo Weinhold	bool GetPath(BlacklistedPath& _path) const
550c04f3a62SIngo Weinhold	{
551c04f3a62SIngo Weinhold		Menu* menu = Supermenu();
552c04f3a62SIngo Weinhold		if (menu != NULL && menu != sBlacklistRootMenu
553c04f3a62SIngo Weinhold			&& menu->Superitem() != NULL) {
554c04f3a62SIngo Weinhold			return static_cast<BlacklistMenuItem*>(menu->Superitem())
555c04f3a62SIngo Weinhold					->GetPath(_path)
556c04f3a62SIngo Weinhold			   && _path.Append(Label());
557c04f3a62SIngo Weinhold		}
558c04f3a62SIngo Weinhold
559c04f3a62SIngo Weinhold		return _path.SetTo(Label());
560c04f3a62SIngo Weinhold	}
561c04f3a62SIngo Weinhold
562c04f3a62SIngo Weinhold	void UpdateBlacklisted()
563c04f3a62SIngo Weinhold	{
564c04f3a62SIngo Weinhold		BlacklistedPath path;
565c04f3a62SIngo Weinhold		if (GetPath(path))
566c04f3a62SIngo Weinhold			_SetMarked(sPathBlacklist->Contains(path.Path()), false);
567c04f3a62SIngo Weinhold	}
568c04f3a62SIngo Weinhold
569c04f3a62SIngo Weinhold	virtual void SetMarked(bool marked)
570c04f3a62SIngo Weinhold	{
571c04f3a62SIngo Weinhold		_SetMarked(marked, true);
572c04f3a62SIngo Weinhold	}
573c04f3a62SIngo Weinhold
574c04f3a62SIngo Weinhold	static bool Less(const MenuItem* a, const MenuItem* b)
575c04f3a62SIngo Weinhold	{
576c04f3a62SIngo Weinhold		const BlacklistMenuItem* item1
577c04f3a62SIngo Weinhold			= static_cast<const BlacklistMenuItem*>(a);
578c04f3a62SIngo Weinhold		const BlacklistMenuItem* item2
579c04f3a62SIngo Weinhold			= static_cast<const BlacklistMenuItem*>(b);
580c04f3a62SIngo Weinhold
581c04f3a62SIngo Weinhold		// directories come first
582c04f3a62SIngo Weinhold		if (item1->IsDirectoryItem() != item2->IsDirectoryItem())
583c04f3a62SIngo Weinhold			return item1->IsDirectoryItem();
584c04f3a62SIngo Weinhold
585c04f3a62SIngo Weinhold		// compare the labels
586c04f3a62SIngo Weinhold		return strcasecmp(item1->Label(), item2->Label()) < 0;
587c04f3a62SIngo Weinhold	}
588c04f3a62SIngo Weinhold
589c04f3a62SIngo Weinholdprivate:
590c04f3a62SIngo Weinhold	void _SetMarked(bool marked, bool updateBlacklist)
591c04f3a62SIngo Weinhold	{
592c04f3a62SIngo Weinhold		if (marked == IsMarked())
593c04f3a62SIngo Weinhold			return;
594c04f3a62SIngo Weinhold
595c04f3a62SIngo Weinhold		// For directories toggle the availability of the submenu.
596c04f3a62SIngo Weinhold		if (IsDirectoryItem())
597c04f3a62SIngo Weinhold			SetSubmenu(marked ? NULL : fSubMenu);
598c04f3a62SIngo Weinhold
599c04f3a62SIngo Weinhold		if (updateBlacklist) {
600c04f3a62SIngo Weinhold			BlacklistedPath path;
601c04f3a62SIngo Weinhold			if (GetPath(path)) {
602c04f3a62SIngo Weinhold				if (marked)
603c04f3a62SIngo Weinhold					sPathBlacklist->Add(path.Path());
604c04f3a62SIngo Weinhold				else
605c04f3a62SIngo Weinhold					sPathBlacklist->Remove(path.Path());
606c04f3a62SIngo Weinhold			}
607c04f3a62SIngo Weinhold		}
608c04f3a62SIngo Weinhold
609c04f3a62SIngo Weinhold		MenuItem::SetMarked(marked);
610c04f3a62SIngo Weinhold	}
611c04f3a62SIngo Weinhold
612c04f3a62SIngo Weinholdprivate:
613c04f3a62SIngo Weinhold	Node*	fNode;
614c04f3a62SIngo Weinhold	Menu*	fSubMenu;
615c04f3a62SIngo Weinhold};
616c04f3a62SIngo Weinhold
617c04f3a62SIngo Weinhold
618c04f3a62SIngo Weinholdclass BlacklistMenu : public Menu {
619c04f3a62SIngo Weinholdpublic:
620c04f3a62SIngo Weinhold	BlacklistMenu()
621c04f3a62SIngo Weinhold		:
62265947ae5SIngo Weinhold		Menu(STANDARD_MENU, kDefaultMenuTitle),
623c04f3a62SIngo Weinhold		fDirectory(NULL)
624c04f3a62SIngo Weinhold	{
625c04f3a62SIngo Weinhold	}
626c04f3a62SIngo Weinhold
627c04f3a62SIngo Weinhold	~BlacklistMenu()
628c04f3a62SIngo Weinhold	{
629c04f3a62SIngo Weinhold		SetDirectory(NULL);
630c04f3a62SIngo Weinhold	}
631c04f3a62SIngo Weinhold
632c04f3a62SIngo Weinhold	virtual void Entered()
633c04f3a62SIngo Weinhold	{
634c04f3a62SIngo Weinhold		_DeleteItems();
635c04f3a62SIngo Weinhold
636ccfc7beaSIngo Weinhold		if (fDirectory != NULL) {
637ccfc7beaSIngo Weinhold			void* cookie;
638ccfc7beaSIngo Weinhold			if (fDirectory->Open(&cookie, O_RDONLY) == B_OK) {
639ccfc7beaSIngo Weinhold				Node* node;
640ccfc7beaSIngo Weinhold				while (fDirectory->GetNextNode(cookie, &node) == B_OK) {
641ccfc7beaSIngo Weinhold					BlacklistMenuItem* item = _CreateItem(node);
642ccfc7beaSIngo Weinhold					node->Release();
643ccfc7beaSIngo Weinhold					if (item == NULL)
644ccfc7beaSIngo Weinhold						break;
645ccfc7beaSIngo Weinhold
646ccfc7beaSIngo Weinhold					AddItem(item);
647ccfc7beaSIngo Weinhold
648ccfc7beaSIngo Weinhold					item->UpdateBlacklisted();
649ccfc7beaSIngo Weinhold				}
650ccfc7beaSIngo Weinhold				fDirectory->Close(cookie);
651c04f3a62SIngo Weinhold			}
652ccfc7beaSIngo Weinhold
653ccfc7beaSIngo Weinhold			SortItems(&BlacklistMenuItem::Less);
654c04f3a62SIngo Weinhold		}
655c04f3a62SIngo Weinhold
656ccfc7beaSIngo Weinhold		if (CountItems() > 0)
657ccfc7beaSIngo Weinhold			AddSeparatorItem();
658ccfc7beaSIngo Weinhold		AddItem(new(nothrow) MenuItem("Return to parent directory"));
659c04f3a62SIngo Weinhold	}
660c04f3a62SIngo Weinhold
661c04f3a62SIngo Weinhold	virtual void Exited()
662c04f3a62SIngo Weinhold	{
663c04f3a62SIngo Weinhold		_DeleteItems();
664c04f3a62SIngo Weinhold	}
665c04f3a62SIngo Weinhold
666c04f3a62SIngo Weinholdprotected:
667c04f3a62SIngo Weinhold	void SetDirectory(Directory* directory)
668c04f3a62SIngo Weinhold	{
669c04f3a62SIngo Weinhold		if (fDirectory != NULL)
670c04f3a62SIngo Weinhold			fDirectory->Release();
671c04f3a62SIngo Weinhold
672c04f3a62SIngo Weinhold		fDirectory = directory;
673c04f3a62SIngo Weinhold
674c04f3a62SIngo Weinhold		if (fDirectory != NULL)
675c04f3a62SIngo Weinhold			fDirectory->Acquire();
676c04f3a62SIngo Weinhold	}
677c04f3a62SIngo Weinhold
678c04f3a62SIngo Weinholdprivate:
679c04f3a62SIngo Weinhold	static BlacklistMenuItem* _CreateItem(Node* node)
680c04f3a62SIngo Weinhold	{
681c04f3a62SIngo Weinhold		// Get the node name and duplicate it, so we can use it as a label.
682c04f3a62SIngo Weinhold		char name[B_FILE_NAME_LENGTH];
683c04f3a62SIngo Weinhold		if (node->GetName(name, sizeof(name)) != B_OK)
684c04f3a62SIngo Weinhold			return NULL;
685c04f3a62SIngo Weinhold
686c04f3a62SIngo Weinhold		// append '/' to directory labels
687c04f3a62SIngo Weinhold		bool isDirectory = node->Type() == S_IFDIR;
688c04f3a62SIngo Weinhold		if (isDirectory)
689c04f3a62SIngo Weinhold			strlcat(name, "/", sizeof(name));
690c04f3a62SIngo Weinhold
691c04f3a62SIngo Weinhold		// If this is a directory, create the submenu.
692c04f3a62SIngo Weinhold		BlacklistMenu* subMenu = NULL;
693c04f3a62SIngo Weinhold		if (isDirectory) {
694c04f3a62SIngo Weinhold			subMenu = new(std::nothrow) BlacklistMenu;
695c04f3a62SIngo Weinhold			if (subMenu != NULL)
696c04f3a62SIngo Weinhold				subMenu->SetDirectory(static_cast<Directory*>(node));
697c04f3a62SIngo Weinhold
698c04f3a62SIngo Weinhold		}
699c04f3a62SIngo Weinhold		ObjectDeleter<BlacklistMenu> subMenuDeleter(subMenu);
700c04f3a62SIngo Weinhold
701c04f3a62SIngo Weinhold		// create the menu item
702c04f3a62SIngo Weinhold		BlacklistMenuItem* item = new(std::nothrow) BlacklistMenuItem(name,
703c04f3a62SIngo Weinhold			node, subMenu);
704c04f3a62SIngo Weinhold		if (item == NULL)
705c04f3a62SIngo Weinhold			return NULL;
706c04f3a62SIngo Weinhold
707c04f3a62SIngo Weinhold		subMenuDeleter.Detach();
708c04f3a62SIngo Weinhold		return item;
709c04f3a62SIngo Weinhold	}
710c04f3a62SIngo Weinhold
711c04f3a62SIngo Weinhold	void _DeleteItems()
712c04f3a62SIngo Weinhold	{
713c04f3a62SIngo Weinhold		int32 count = CountItems();
714c04f3a62SIngo Weinhold		for (int32 i = 0; i < count; i++)
715c04f3a62SIngo Weinhold			delete RemoveItemAt(0);
716c04f3a62SIngo Weinhold	}
717c04f3a62SIngo Weinhold
718c04f3a62SIngo Weinholdprivate:
719c04f3a62SIngo Weinhold	Directory*	fDirectory;
72065947ae5SIngo Weinhold
72165947ae5SIngo Weinholdprotected:
72265947ae5SIngo Weinhold	static const char* const kDefaultMenuTitle;
723c04f3a62SIngo Weinhold};
724c04f3a62SIngo Weinhold
725c04f3a62SIngo Weinhold
72665947ae5SIngo Weinholdconst char* const BlacklistMenu::kDefaultMenuTitle
72765947ae5SIngo Weinhold	= "Mark the entries to blacklist";
72865947ae5SIngo Weinhold
72965947ae5SIngo Weinhold
730c04f3a62SIngo Weinholdclass BlacklistRootMenu : public BlacklistMenu {
731c04f3a62SIngo Weinholdpublic:
732c04f3a62SIngo Weinhold	BlacklistRootMenu()
733c04f3a62SIngo Weinhold		:
734c04f3a62SIngo Weinhold		BlacklistMenu()
735c04f3a62SIngo Weinhold	{
736c04f3a62SIngo Weinhold	}
737c04f3a62SIngo Weinhold
738c04f3a62SIngo Weinhold	virtual void Entered()
739c04f3a62SIngo Weinhold	{
740c04f3a62SIngo Weinhold		// Get the system directory, but only if this is a packaged Haiku.
741c04f3a62SIngo Weinhold		// Otherwise blacklisting isn't supported.
742c04f3a62SIngo Weinhold		if (sBootVolume != NULL && sBootVolume->IsValid()
743c04f3a62SIngo Weinhold			&& sBootVolume->IsPackaged()) {
744c04f3a62SIngo Weinhold			SetDirectory(sBootVolume->SystemDirectory());
74565947ae5SIngo Weinhold			SetTitle(kDefaultMenuTitle);
74665947ae5SIngo Weinhold		} else {
747c04f3a62SIngo Weinhold			SetDirectory(NULL);
74865947ae5SIngo Weinhold			SetTitle(sBootVolume != NULL && sBootVolume->IsValid()
74965947ae5SIngo Weinhold				? "The selected boot volume doesn't support blacklisting!"
75065947ae5SIngo Weinhold				: "No boot volume selected!");
75165947ae5SIngo Weinhold		}
752c04f3a62SIngo Weinhold
753c04f3a62SIngo Weinhold		BlacklistMenu::Entered();
754ccfc7beaSIngo Weinhold
755ccfc7beaSIngo Weinhold		// rename last item
756ccfc7beaSIngo Weinhold		if (MenuItem* item = ItemAt(CountItems() - 1))
757ccfc7beaSIngo Weinhold			item->SetLabel("Return to safe mode menu");
758c04f3a62SIngo Weinhold	}
759c04f3a62SIngo Weinhold
760c04f3a62SIngo Weinhold	virtual void Exited()
761c04f3a62SIngo Weinhold	{
762c04f3a62SIngo Weinhold		BlacklistMenu::Exited();
763c04f3a62SIngo Weinhold		SetDirectory(NULL);
764c04f3a62SIngo Weinhold	}
765c04f3a62SIngo Weinhold};
766c04f3a62SIngo Weinhold
767c04f3a62SIngo Weinhold
7685c0f8450SIngo Weinhold// #pragma mark - boot volume menu
7695c0f8450SIngo Weinhold
7705c0f8450SIngo Weinhold
7715c0f8450SIngo Weinholdclass BootVolumeMenuItem : public MenuItem {
7725c0f8450SIngo Weinholdpublic:
7735c0f8450SIngo Weinhold	BootVolumeMenuItem(const char* volumeName)
7745c0f8450SIngo Weinhold		:
7755c0f8450SIngo Weinhold		MenuItem(volumeName),
7765c0f8450SIngo Weinhold		fStateChoiceText(NULL)
7775c0f8450SIngo Weinhold	{
7785c0f8450SIngo Weinhold	}
7795c0f8450SIngo Weinhold
7805c0f8450SIngo Weinhold	~BootVolumeMenuItem()
7815c0f8450SIngo Weinhold	{
7825c0f8450SIngo Weinhold		UpdateStateName(NULL);
7835c0f8450SIngo Weinhold	}
7845c0f8450SIngo Weinhold
7855c0f8450SIngo Weinhold	void UpdateStateName(PackageVolumeState* volumeState)
7865c0f8450SIngo Weinhold	{
7875c0f8450SIngo Weinhold		free(fStateChoiceText);
7885c0f8450SIngo Weinhold		fStateChoiceText = NULL;
789c1c99c5cSRyan Leavengood
7905c0f8450SIngo Weinhold		if (volumeState != NULL && volumeState->Name() != NULL) {
7915c0f8450SIngo Weinhold			char nameBuffer[128];
7925c0f8450SIngo Weinhold			snprintf(nameBuffer, sizeof(nameBuffer), "%s (%s)", Label(),
7935c0f8450SIngo Weinhold				volumeState->DisplayName());
7945c0f8450SIngo Weinhold			fStateChoiceText = strdup(nameBuffer);
7955c0f8450SIngo Weinhold		}
7965c0f8450SIngo Weinhold
7975c0f8450SIngo Weinhold		Supermenu()->SetChoiceText(
7985c0f8450SIngo Weinhold			fStateChoiceText != NULL ? fStateChoiceText : Label());
7995c0f8450SIngo Weinhold	}
8005c0f8450SIngo Weinhold
8015c0f8450SIngo Weinholdprivate:
8025c0f8450SIngo Weinhold	char*	fStateChoiceText;
8035c0f8450SIngo Weinhold};
8045c0f8450SIngo Weinhold
8055c0f8450SIngo Weinhold
8065c0f8450SIngo Weinholdclass PackageVolumeStateMenuItem : public MenuItem {
8075c0f8450SIngo Weinholdpublic:
8085c0f8450SIngo Weinhold	PackageVolumeStateMenuItem(const char* label, PackageVolumeInfo* volumeInfo,
8095c0f8450SIngo Weinhold		PackageVolumeState*	volumeState)
8105c0f8450SIngo Weinhold		:
8115c0f8450SIngo Weinhold		MenuItem(label),
8125c0f8450SIngo Weinhold		fVolumeInfo(volumeInfo),
8135c0f8450SIngo Weinhold		fVolumeState(volumeState)
8145c0f8450SIngo Weinhold	{
8155c0f8450SIngo Weinhold		fVolumeInfo->AcquireReference();
8165c0f8450SIngo Weinhold	}
8175c0f8450SIngo Weinhold
8185c0f8450SIngo Weinhold	~PackageVolumeStateMenuItem()
8195c0f8450SIngo Weinhold	{
8205c0f8450SIngo Weinhold		fVolumeInfo->ReleaseReference();
8215c0f8450SIngo Weinhold	}
8225c0f8450SIngo Weinhold
8235c0f8450SIngo Weinhold	PackageVolumeInfo* VolumeInfo() const
8245c0f8450SIngo Weinhold	{
8255c0f8450SIngo Weinhold		return fVolumeInfo;
8265c0f8450SIngo Weinhold	}
8275c0f8450SIngo Weinhold
8285c0f8450S