* Added shortcut handling to the boot loader menu (in preparation of adopting

ticket #5312).
* Added shortcut 'b' to continue booting, 'r' to reboot.
* Consolidated asterisk style.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@36310 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler
2010-04-15 18:07:40 +00:00
parent 1cae99d78e
commit 868aa7a0e0
3 changed files with 204 additions and 114 deletions
+18 -2
View File
@@ -13,7 +13,8 @@
class Menu; class Menu;
class MenuItem; class MenuItem;
typedef bool (*menu_item_hook)(Menu *, MenuItem *); typedef bool (*menu_item_hook)(Menu* menu, MenuItem* item);
typedef void (*shortcut_hook)(char key);
enum menu_item_type { enum menu_item_type {
@@ -52,6 +53,9 @@ public:
void SetHelpText(const char* text); void SetHelpText(const char* text);
const char* HelpText() const { return fHelpText; } const char* HelpText() const { return fHelpText; }
void SetShortcut(char key);
char Shortcut() const { return fShortcut; }
const char* Label() const { return fLabel; } const char* Label() const { return fLabel; }
Menu* Submenu() const { return fSubMenu; } Menu* Submenu() const { return fSubMenu; }
@@ -70,6 +74,7 @@ private:
Menu* fSubMenu; Menu* fSubMenu;
const void* fData; const void* fData;
const char* fHelpText; const char* fHelpText;
char fShortcut;
}; };
@@ -122,13 +127,23 @@ public:
{ fChoiceText = text; } { fChoiceText = text; }
const char* ChoiceText() const { return fChoiceText; } const char* ChoiceText() const { return fChoiceText; }
void Run(); void AddShortcut(char key, shortcut_hook function);
shortcut_hook FindShortcut(char key) const;
MenuItem* FindItemByShortcut(char key);
void Run();
private: private:
friend class MenuItem; friend class MenuItem;
void Draw(MenuItem* item); void Draw(MenuItem* item);
private: private:
struct shortcut {
shortcut* next;
shortcut_hook function;
char key;
};
const char* fTitle; const char* fTitle;
const char* fChoiceText; const char* fChoiceText;
int32 fCount; int32 fCount;
@@ -136,6 +151,7 @@ private:
MenuItemList fItems; MenuItemList fItems;
menu_type fType; menu_type fType;
MenuItem* fSuperItem; MenuItem* fSuperItem;
shortcut* fShortcuts;
}; };
+125 -76
View File
@@ -151,22 +151,28 @@ MenuItem::SetData(const void *data)
} }
/** This sets a help text that is shown when the item is /*! This sets a help text that is shown when the item is
* selected. selected.
* Note, unlike the label, the string is not copied, it's Note, unlike the label, the string is not copied, it's
* just referenced and has to stay valid as long as the just referenced and has to stay valid as long as the
* item's menu is being used. item's menu is being used.
*/ */
void void
MenuItem::SetHelpText(const char *text) MenuItem::SetHelpText(const char* text)
{ {
fHelpText = text; fHelpText = text;
} }
void void
MenuItem::SetMenu(Menu *menu) MenuItem::SetShortcut(char key)
{
fShortcut = key;
}
void
MenuItem::SetMenu(Menu* menu)
{ {
fMenu = menu; fMenu = menu;
} }
@@ -175,14 +181,15 @@ MenuItem::SetMenu(Menu *menu)
// #pragma mark - // #pragma mark -
Menu::Menu(menu_type type, const char *title) Menu::Menu(menu_type type, const char* title)
: :
fTitle(title), fTitle(title),
fChoiceText(NULL), fChoiceText(NULL),
fCount(0), fCount(0),
fIsHidden(true), fIsHidden(true),
fType(type), fType(type),
fSuperItem(NULL) fSuperItem(NULL),
fShortcuts(NULL)
{ {
} }
@@ -199,7 +206,7 @@ Menu::~Menu()
} }
MenuItem * MenuItem*
Menu::ItemAt(int32 index) Menu::ItemAt(int32 index)
{ {
if (index < 0 || index >= fCount) if (index < 0 || index >= fCount)
@@ -218,13 +225,12 @@ Menu::ItemAt(int32 index)
int32 int32
Menu::IndexOf(MenuItem *searchedItem) Menu::IndexOf(MenuItem* searchedItem)
{ {
MenuItemIterator iterator = ItemIterator();
MenuItem *item;
int32 index = 0; int32 index = 0;
while ((item = iterator.Next()) != NULL) { MenuItemIterator iterator = ItemIterator();
while (MenuItem* item = iterator.Next()) {
if (item == searchedItem) if (item == searchedItem)
return index; return index;
@@ -242,13 +248,11 @@ Menu::CountItems() const
} }
MenuItem * MenuItem*
Menu::FindItem(const char *label) Menu::FindItem(const char* label)
{ {
MenuItemIterator iterator = ItemIterator(); MenuItemIterator iterator = ItemIterator();
MenuItem *item; while (MenuItem* item = iterator.Next()) {
while ((item = iterator.Next()) != NULL) {
if (item->Label() != NULL && !strcmp(item->Label(), label)) if (item->Label() != NULL && !strcmp(item->Label(), label))
return item; return item;
} }
@@ -257,13 +261,11 @@ Menu::FindItem(const char *label)
} }
MenuItem * MenuItem*
Menu::FindMarked() Menu::FindMarked()
{ {
MenuItemIterator iterator = ItemIterator(); MenuItemIterator iterator = ItemIterator();
MenuItem *item; while (MenuItem* item = iterator.Next()) {
while ((item = iterator.Next()) != NULL) {
if (item->IsMarked()) if (item->IsMarked())
return item; return item;
} }
@@ -272,14 +274,13 @@ Menu::FindMarked()
} }
MenuItem * MenuItem*
Menu::FindSelected(int32 *_index) Menu::FindSelected(int32* _index)
{ {
MenuItemIterator iterator = ItemIterator();
MenuItem *item;
int32 index = 0; int32 index = 0;
while ((item = iterator.Next()) != NULL) { MenuItemIterator iterator = ItemIterator();
while (MenuItem* item = iterator.Next()) {
if (item->IsSelected()) { if (item->IsSelected()) {
if (_index != NULL) if (_index != NULL)
*_index = index; *_index = index;
@@ -294,7 +295,7 @@ Menu::FindSelected(int32 *_index)
void void
Menu::AddItem(MenuItem *item) Menu::AddItem(MenuItem* item)
{ {
item->fMenu = this; item->fMenu = this;
fItems.Add(item); fItems.Add(item);
@@ -305,7 +306,7 @@ Menu::AddItem(MenuItem *item)
status_t status_t
Menu::AddSeparatorItem() Menu::AddSeparatorItem()
{ {
MenuItem *item = new(nothrow) MenuItem(); MenuItem* item = new(std::nothrow) MenuItem();
if (item == NULL) if (item == NULL)
return B_NO_MEMORY; return B_NO_MEMORY;
@@ -316,16 +317,14 @@ Menu::AddSeparatorItem()
} }
MenuItem * MenuItem*
Menu::RemoveItemAt(int32 index) Menu::RemoveItemAt(int32 index)
{ {
if (index < 0 || index >= fCount) if (index < 0 || index >= fCount)
return NULL; return NULL;
MenuItemIterator iterator = ItemIterator(); MenuItemIterator iterator = ItemIterator();
MenuItem *item; while (MenuItem* item = iterator.Next()) {
while ((item = iterator.Next()) != NULL) {
if (index-- == 0) { if (index-- == 0) {
RemoveItem(item); RemoveItem(item);
return item; return item;
@@ -337,7 +336,7 @@ Menu::RemoveItemAt(int32 index)
void void
Menu::RemoveItem(MenuItem *item) Menu::RemoveItem(MenuItem* item)
{ {
item->fMenu = NULL; item->fMenu = NULL;
fItems.Remove(item); fItems.Remove(item);
@@ -346,10 +345,51 @@ Menu::RemoveItem(MenuItem *item)
void void
Menu::Draw(MenuItem *item) Menu::AddShortcut(char key, shortcut_hook function)
{ {
if (!IsHidden()) Menu::shortcut* shortcut = new(std::nothrow) struct Menu::shortcut;
platform_update_menu_item(this, item); if (shortcut == NULL)
return;
shortcut->key = key;
shortcut->function = function;
shortcut->next = fShortcuts;
fShortcuts = shortcut;
}
shortcut_hook
Menu::FindShortcut(char key) const
{
if (key == 0)
return NULL;
const Menu::shortcut* shortcut = fShortcuts;
while (shortcut != NULL) {
if (shortcut->key == key)
return shortcut->function;
shortcut = shortcut->next;
}
return NULL;
}
MenuItem*
Menu::FindItemByShortcut(char key)
{
if (key == 0)
return NULL;
MenuItemList::Iterator iterator = ItemIterator();
while (MenuItem* item = iterator.Next()) {
if (item->Shortcut() == key)
return item;
}
return NULL;
} }
@@ -360,6 +400,14 @@ Menu::Run()
} }
void
Menu::Draw(MenuItem* item)
{
if (!IsHidden())
platform_update_menu_item(this, item);
}
// #pragma mark - // #pragma mark -
@@ -415,9 +463,9 @@ size_to_string(off_t size, char* buffer, size_t bufferSize)
static bool static bool
user_menu_boot_volume(Menu *menu, MenuItem *item) user_menu_boot_volume(Menu* menu, MenuItem* item)
{ {
Menu *super = menu->Supermenu(); Menu* super = menu->Supermenu();
if (super == NULL) { if (super == NULL) {
// huh? // huh?
return true; return true;
@@ -434,7 +482,7 @@ user_menu_boot_volume(Menu *menu, MenuItem *item)
static bool static bool
debug_menu_display_syslog(Menu *menu, MenuItem *item) debug_menu_display_syslog(Menu* menu, MenuItem* item)
{ {
ring_buffer* buffer = (ring_buffer*)gKernelArgs.debug_output; ring_buffer* buffer = (ring_buffer*)gKernelArgs.debug_output;
if (buffer == NULL) if (buffer == NULL)
@@ -520,7 +568,7 @@ save_syslog_to_volume(Directory* directory)
static bool static bool
debug_menu_toggle_debug_syslog(Menu *menu, MenuItem *item) debug_menu_toggle_debug_syslog(Menu* menu, MenuItem* item)
{ {
gKernelArgs.keep_debug_output_buffer = item->IsMarked(); gKernelArgs.keep_debug_output_buffer = item->IsMarked();
return true; return true;
@@ -528,7 +576,7 @@ debug_menu_toggle_debug_syslog(Menu *menu, MenuItem *item)
static bool static bool
debug_menu_save_syslog(Menu *menu, MenuItem *item) debug_menu_save_syslog(Menu* menu, MenuItem* item)
{ {
Directory* volume = (Directory*)item->Data(); Directory* volume = (Directory*)item->Data();
@@ -543,17 +591,17 @@ debug_menu_save_syslog(Menu *menu, MenuItem *item)
} }
static Menu * static Menu*
add_boot_volume_menu(Directory *bootVolume) add_boot_volume_menu(Directory* bootVolume)
{ {
Menu *menu = new(nothrow) Menu(CHOICE_MENU, "Select Boot Volume"); Menu* menu = new(std::nothrow) Menu(CHOICE_MENU, "Select Boot Volume");
MenuItem *item; MenuItem* item;
void *cookie; void* cookie;
int32 count = 0; int32 count = 0;
if (gRoot->Open(&cookie, O_RDONLY) == B_OK) { if (gRoot->Open(&cookie, O_RDONLY) == B_OK) {
Directory *volume; Directory* volume;
while (gRoot->GetNextNode(cookie, (Node **)&volume) == B_OK) { while (gRoot->GetNextNode(cookie, (Node**)&volume) == B_OK) {
// only list bootable volumes // only list bootable volumes
if (!is_bootable(volume)) if (!is_bootable(volume))
continue; continue;
@@ -601,11 +649,11 @@ add_boot_volume_menu(Directory *bootVolume)
} }
static Menu * static Menu*
add_safe_mode_menu() add_safe_mode_menu()
{ {
Menu *safeMenu = new(nothrow) Menu(SAFE_MODE_MENU, "Safe Mode Options"); Menu* safeMenu = new(nothrow) Menu(SAFE_MODE_MENU, "Safe Mode Options");
MenuItem *item; MenuItem* item;
safeMenu->AddItem(item = new(nothrow) MenuItem("Safe mode")); safeMenu->AddItem(item = new(nothrow) MenuItem("Safe mode"));
item->SetData(B_SAFEMODE_SAFE_MODE); item->SetData(B_SAFEMODE_SAFE_MODE);
@@ -704,11 +752,11 @@ add_save_debug_syslog_menu()
} }
static Menu * static Menu*
add_debug_menu() add_debug_menu()
{ {
Menu *menu = new(nothrow) Menu(STANDARD_MENU, "Debug Options"); Menu* menu = new(std::nothrow) Menu(STANDARD_MENU, "Debug Options");
MenuItem *item; MenuItem* item;
#if DEBUG_SPINLOCK_LATENCIES #if DEBUG_SPINLOCK_LATENCIES
item = new(std::nothrow) MenuItem("Disable latency checks"); item = new(std::nothrow) MenuItem("Disable latency checks");
@@ -766,22 +814,21 @@ add_debug_menu()
static void static void
apply_safe_mode_options(Menu *menu) apply_safe_mode_options(Menu* menu)
{ {
MenuItemIterator iterator = menu->ItemIterator();
MenuItem *item;
char buffer[2048]; char buffer[2048];
int32 pos = 0; int32 pos = 0;
buffer[0] = '\0'; buffer[0] = '\0';
while ((item = iterator.Next()) != NULL) { MenuItemIterator iterator = menu->ItemIterator();
while (MenuItem* item = iterator.Next()) {
if (item->Type() == MENU_ITEM_SEPARATOR || !item->IsMarked() if (item->Type() == MENU_ITEM_SEPARATOR || !item->IsMarked()
|| item->Data() == NULL || (uint32)pos > sizeof(buffer)) || item->Data() == NULL || (uint32)pos > sizeof(buffer))
continue; continue;
size_t totalBytes = snprintf(buffer + pos, sizeof(buffer) - pos, size_t totalBytes = snprintf(buffer + pos, sizeof(buffer) - pos,
"%s true\n", (const char *)item->Data()); "%s true\n", (const char*)item->Data());
pos += std::min(totalBytes, sizeof(buffer) - pos - 1); pos += std::min(totalBytes, sizeof(buffer) - pos - 1);
} }
@@ -790,7 +837,7 @@ apply_safe_mode_options(Menu *menu)
static bool static bool
user_menu_reboot(Menu *menu, MenuItem *item) user_menu_reboot(Menu* menu, MenuItem* item)
{ {
platform_exit(); platform_exit();
return true; return true;
@@ -798,25 +845,25 @@ user_menu_reboot(Menu *menu, MenuItem *item)
status_t status_t
user_menu(Directory **_bootVolume) user_menu(Directory** _bootVolume)
{ {
Menu *menu = new(nothrow) Menu(MAIN_MENU); Menu* menu = new(std::nothrow) Menu(MAIN_MENU);
Menu *safeModeMenu = NULL; Menu* safeModeMenu = NULL;
Menu *debugMenu = NULL; Menu* debugMenu = NULL;
MenuItem *item; MenuItem* item;
TRACE(("user_menu: enter\n")); TRACE(("user_menu: enter\n"));
// Add boot volume // Add boot volume
menu->AddItem(item = new(nothrow) MenuItem("Select boot volume", menu->AddItem(item = new(std::nothrow) MenuItem("Select boot volume",
add_boot_volume_menu(*_bootVolume))); add_boot_volume_menu(*_bootVolume)));
// Add safe mode // Add safe mode
menu->AddItem(item = new(nothrow) MenuItem("Select safe mode options", menu->AddItem(item = new(std::nothrow) MenuItem("Select safe mode options",
safeModeMenu = add_safe_mode_menu())); safeModeMenu = add_safe_mode_menu()));
// add debug menu // add debug menu
menu->AddItem(item = new(nothrow) MenuItem("Select debug options", menu->AddItem(item = new(std::nothrow) MenuItem("Select debug options",
debugMenu = add_debug_menu())); debugMenu = add_debug_menu()));
// Add platform dependent menus // Add platform dependent menus
@@ -824,20 +871,22 @@ user_menu(Directory **_bootVolume)
menu->AddSeparatorItem(); menu->AddSeparatorItem();
menu->AddItem(item = new(nothrow) MenuItem("Reboot")); menu->AddItem(item = new(std::nothrow) MenuItem("Reboot"));
item->SetTarget(user_menu_reboot); item->SetTarget(user_menu_reboot);
item->SetShortcut('r');
menu->AddItem(item = new(nothrow) MenuItem("Continue booting")); menu->AddItem(item = new(std::nothrow) MenuItem("Continue booting"));
if (*_bootVolume == NULL) { if (*_bootVolume == NULL) {
item->SetEnabled(false); item->SetEnabled(false);
menu->ItemAt(0)->Select(true); menu->ItemAt(0)->Select(true);
} } else
item->SetShortcut('b');
menu->Run(); menu->Run();
// See if a new boot device has been selected, and propagate that back // See if a new boot device has been selected, and propagate that back
if (item->Data() != NULL) if (item->Data() != NULL)
*_bootVolume = (Directory *)item->Data(); *_bootVolume = (Directory*)item->Data();
apply_safe_mode_options(safeModeMenu); apply_safe_mode_options(safeModeMenu);
apply_safe_mode_options(debugMenu); apply_safe_mode_options(debugMenu);
+61 -36
View File
@@ -1,5 +1,5 @@
/* /*
* Copyright 2004-2009, Axel Dörfler, [email protected]. All rights reserved. * Copyright 2004-2010, Axel Dörfler, [email protected].
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
*/ */
@@ -38,6 +38,9 @@ static const console_color kArrowColor = GRAY;
static int32 sMenuOffset = 0; static int32 sMenuOffset = 0;
static void run_menu(Menu* menu);
static int32 static int32
menu_height() menu_height()
{ {
@@ -340,8 +343,48 @@ select_next_valid_item(Menu *menu, int32 selected)
} }
static bool
invoke_item(Menu* menu, MenuItem* item, int32& selected, char key)
{
// leave the menu
if (item->Submenu() != NULL && key == TEXT_CONSOLE_KEY_RETURN) {
int32 offset = sMenuOffset;
menu->Hide();
run_menu(item->Submenu());
if (item->Target() != NULL)
(*item->Target())(menu, item);
// restore current menu
sMenuOffset = offset;
menu->FindSelected(&selected);
menu->Show();
draw_menu(menu);
} else if (item->Type() == MENU_ITEM_MARKABLE) {
// toggle state
item->SetMarked(!item->IsMarked());
print_item_at(selected, item);
if (item->Target() != NULL)
(*item->Target())(menu, item);
} else if (key == TEXT_CONSOLE_KEY_RETURN) {
// the space key does not exit the menu, only return does
if (menu->Type() == CHOICE_MENU
&& item->Type() != MENU_ITEM_NO_CHOICE
&& item->Type() != MENU_ITEM_TITLE)
item->SetMarked(true);
if (item->Target() != NULL)
(*item->Target())(menu, item);
return true;
}
return false;
}
static void static void
run_menu(Menu *menu) run_menu(Menu* menu)
{ {
sMenuOffset = 0; sMenuOffset = 0;
menu->Show(); menu->Show();
@@ -413,43 +456,25 @@ run_menu(Menu *menu)
} }
} else if (key == TEXT_CONSOLE_KEY_RETURN } else if (key == TEXT_CONSOLE_KEY_RETURN
|| key == TEXT_CONSOLE_KEY_SPACE) { || key == TEXT_CONSOLE_KEY_SPACE) {
// leave the menu if (invoke_item(menu, item, selected, key))
if (item->Submenu() != NULL && key == TEXT_CONSOLE_KEY_RETURN) {
int32 offset = sMenuOffset;
menu->Hide();
run_menu(item->Submenu());
if (item->Target() != NULL)
(*item->Target())(menu, item);
// restore current menu
sMenuOffset = offset;
menu->FindSelected(&selected);
menu->Show();
draw_menu(menu);
} else if (item->Type() == MENU_ITEM_MARKABLE) {
// toggle state
item->SetMarked(!item->IsMarked());
print_item_at(selected, item);
if (item->Target() != NULL)
(*item->Target())(menu, item);
} else if (key == TEXT_CONSOLE_KEY_RETURN) {
// the space key does not exit the menu
if (menu->Type() == CHOICE_MENU
&& item->Type() != MENU_ITEM_NO_CHOICE
&& item->Type() != MENU_ITEM_TITLE)
item->SetMarked(true);
if (item->Target() != NULL)
(*item->Target())(menu, item);
break; break;
} } else if (key == TEXT_CONSOLE_KEY_ESCAPE
} else if (key == TEXT_CONSOLE_KEY_ESCAPE && menu->Type() != MAIN_MENU) && menu->Type() != MAIN_MENU) {
// escape key was hit // escape key was hit
break; break;
} else {
// Shortcut processing
shortcut_hook function = menu->FindShortcut(key);
if (function != NULL)
function(key);
else {
item = menu->FindItemByShortcut(key);
if (item != NULL && invoke_item(menu, item, selected,
TEXT_CONSOLE_KEY_RETURN)) {
break;
}
}
}
} }
menu->Hide(); menu->Hide();