mirror of https://github.com/acidanthera/audk.git
3157 lines
100 KiB
C
3157 lines
100 KiB
C
/*++
|
|
|
|
Copyright (c) 2006, Intel Corporation
|
|
All rights reserved. This program and the accompanying materials
|
|
are licensed and made available under the terms and conditions of the BSD License
|
|
which accompanies this distribution. The full text of the license may be found at
|
|
http://opensource.org/licenses/bsd-license.php
|
|
|
|
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
|
|
Module Name:
|
|
|
|
Ui.c
|
|
|
|
Abstract:
|
|
|
|
Implementation for UI.
|
|
|
|
Revision History
|
|
|
|
--*/
|
|
|
|
#include "Setup.h"
|
|
#include "Ui.h"
|
|
#include "Colors.h"
|
|
|
|
//
|
|
// Implementation
|
|
//
|
|
VOID
|
|
SetUnicodeMem (
|
|
IN VOID *Buffer,
|
|
IN UINTN Size,
|
|
IN CHAR16 Value
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set Buffer to Value for Size bytes.
|
|
|
|
Arguments:
|
|
|
|
Buffer - Memory to set.
|
|
|
|
Size - Number of bytes to set
|
|
|
|
Value - Value of the set operation.
|
|
|
|
Returns:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
CHAR16 *Ptr;
|
|
|
|
Ptr = Buffer;
|
|
while (Size--) {
|
|
*(Ptr++) = Value;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
UiInitMenu (
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Initialize Menu option list.
|
|
|
|
Arguments:
|
|
|
|
Returns:
|
|
|
|
--*/
|
|
{
|
|
InitializeListHead (&Menu);
|
|
}
|
|
|
|
VOID
|
|
UiInitMenuList (
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Initialize Menu option list.
|
|
|
|
Arguments:
|
|
|
|
Returns:
|
|
|
|
--*/
|
|
{
|
|
InitializeListHead (&gMenuList);
|
|
}
|
|
|
|
VOID
|
|
UiRemoveMenuListEntry (
|
|
IN UI_MENU_OPTION *Selection,
|
|
OUT UI_MENU_OPTION **PreviousSelection
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Remove Menu option list.
|
|
|
|
Arguments:
|
|
|
|
Returns:
|
|
|
|
--*/
|
|
{
|
|
UI_MENU_LIST *UiMenuList;
|
|
|
|
*PreviousSelection = AllocateZeroPool (sizeof (UI_MENU_OPTION));
|
|
ASSERT (*PreviousSelection != NULL);
|
|
|
|
if (!IsListEmpty (&gMenuList)) {
|
|
UiMenuList = CR (gMenuList.ForwardLink, UI_MENU_LIST, MenuLink, UI_MENU_LIST_SIGNATURE);
|
|
(*PreviousSelection)->IfrNumber = UiMenuList->Selection.IfrNumber;
|
|
(*PreviousSelection)->FormId = UiMenuList->Selection.FormId;
|
|
(*PreviousSelection)->Tags = UiMenuList->Selection.Tags;
|
|
(*PreviousSelection)->ThisTag = UiMenuList->Selection.ThisTag;
|
|
(*PreviousSelection)->Handle = UiMenuList->Selection.Handle;
|
|
gEntryNumber = UiMenuList->FormerEntryNumber;
|
|
RemoveEntryList (&UiMenuList->MenuLink);
|
|
gBS->FreePool (UiMenuList);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
UiFreeMenuList (
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Free Menu option linked list.
|
|
|
|
Arguments:
|
|
|
|
Returns:
|
|
|
|
--*/
|
|
{
|
|
UI_MENU_LIST *UiMenuList;
|
|
|
|
while (!IsListEmpty (&gMenuList)) {
|
|
UiMenuList = CR (gMenuList.ForwardLink, UI_MENU_LIST, MenuLink, UI_MENU_LIST_SIGNATURE);
|
|
RemoveEntryList (&UiMenuList->MenuLink);
|
|
gBS->FreePool (UiMenuList);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
UiAddMenuListEntry (
|
|
IN UI_MENU_OPTION *Selection
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Add one menu entry to the linked lst
|
|
|
|
Arguments:
|
|
|
|
Returns:
|
|
|
|
--*/
|
|
{
|
|
UI_MENU_LIST *UiMenuList;
|
|
|
|
UiMenuList = AllocateZeroPool (sizeof (UI_MENU_LIST));
|
|
ASSERT (UiMenuList != NULL);
|
|
|
|
UiMenuList->Signature = UI_MENU_LIST_SIGNATURE;
|
|
CopyMem (&UiMenuList->Selection, Selection, sizeof (UI_MENU_OPTION));
|
|
|
|
InsertHeadList (&gMenuList, &UiMenuList->MenuLink);
|
|
}
|
|
|
|
VOID
|
|
UiFreeMenu (
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Free Menu option linked list.
|
|
|
|
Arguments:
|
|
|
|
Returns:
|
|
|
|
--*/
|
|
{
|
|
UI_MENU_OPTION *MenuOption;
|
|
|
|
while (!IsListEmpty (&Menu)) {
|
|
MenuOption = CR (Menu.ForwardLink, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
|
|
RemoveEntryList (&MenuOption->Link);
|
|
|
|
//
|
|
// We allocated space for this description when we did a GetToken, free it here
|
|
//
|
|
gBS->FreePool (MenuOption->Description);
|
|
gBS->FreePool (MenuOption);
|
|
}
|
|
}
|
|
|
|
STATIC
|
|
VOID
|
|
UpdateDateAndTime (
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Refresh screen with current date and/or time based on screen context
|
|
|
|
Arguments:
|
|
|
|
Returns:
|
|
|
|
--*/
|
|
{
|
|
CHAR16 *OptionString;
|
|
MENU_REFRESH_ENTRY *MenuRefreshEntry;
|
|
UINTN Index;
|
|
UINTN Loop;
|
|
|
|
OptionString = NULL;
|
|
|
|
if (gMenuRefreshHead != NULL) {
|
|
|
|
MenuRefreshEntry = gMenuRefreshHead;
|
|
|
|
do {
|
|
gST->ConOut->SetAttribute (gST->ConOut, MenuRefreshEntry->CurrentAttribute);
|
|
ProcessOptions (MenuRefreshEntry->MenuOption, FALSE, MenuRefreshEntry->FileFormTagsHead, NULL, &OptionString);
|
|
|
|
if (OptionString != NULL) {
|
|
//
|
|
// If leading spaces on OptionString - remove the spaces
|
|
//
|
|
for (Index = 0; OptionString[Index] == L' '; Index++)
|
|
;
|
|
|
|
for (Loop = 0; OptionString[Index] != CHAR_NULL; Index++) {
|
|
OptionString[Loop] = OptionString[Index];
|
|
Loop++;
|
|
}
|
|
|
|
OptionString[Loop] = CHAR_NULL;
|
|
|
|
PrintStringAt (MenuRefreshEntry->CurrentColumn, MenuRefreshEntry->CurrentRow, OptionString);
|
|
}
|
|
|
|
MenuRefreshEntry = MenuRefreshEntry->Next;
|
|
|
|
} while (MenuRefreshEntry != NULL);
|
|
}
|
|
|
|
if (OptionString != NULL) {
|
|
gBS->FreePool (OptionString);
|
|
}
|
|
}
|
|
|
|
EFI_STATUS
|
|
UiWaitForSingleEvent (
|
|
IN EFI_EVENT Event,
|
|
IN UINT64 Timeout OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Wait for a given event to fire, or for an optional timeout to expire.
|
|
|
|
Arguments:
|
|
Event - The event to wait for
|
|
|
|
Timeout - An optional timeout value in 100 ns units.
|
|
|
|
Returns:
|
|
|
|
EFI_SUCCESS - Event fired before Timeout expired.
|
|
EFI_TIME_OUT - Timout expired before Event fired.
|
|
|
|
--*/
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN Index;
|
|
EFI_EVENT TimerEvent;
|
|
EFI_EVENT WaitList[2];
|
|
|
|
if (Timeout) {
|
|
//
|
|
// Create a timer event
|
|
//
|
|
Status = gBS->CreateEvent (EFI_EVENT_TIMER, 0, NULL, NULL, &TimerEvent);
|
|
if (!EFI_ERROR (Status)) {
|
|
//
|
|
// Set the timer event
|
|
//
|
|
gBS->SetTimer (
|
|
TimerEvent,
|
|
TimerRelative,
|
|
Timeout
|
|
);
|
|
|
|
//
|
|
// Wait for the original event or the timer
|
|
//
|
|
WaitList[0] = Event;
|
|
WaitList[1] = TimerEvent;
|
|
Status = gBS->WaitForEvent (2, WaitList, &Index);
|
|
gBS->CloseEvent (TimerEvent);
|
|
|
|
//
|
|
// If the timer expired, change the return to timed out
|
|
//
|
|
if (!EFI_ERROR (Status) && Index == 1) {
|
|
Status = EFI_TIMEOUT;
|
|
}
|
|
}
|
|
} else {
|
|
//
|
|
// Update screen every second
|
|
//
|
|
Timeout = ONE_SECOND;
|
|
|
|
do {
|
|
Status = gBS->CreateEvent (EFI_EVENT_TIMER, 0, NULL, NULL, &TimerEvent);
|
|
|
|
//
|
|
// Set the timer event
|
|
//
|
|
gBS->SetTimer (
|
|
TimerEvent,
|
|
TimerRelative,
|
|
Timeout
|
|
);
|
|
|
|
//
|
|
// Wait for the original event or the timer
|
|
//
|
|
WaitList[0] = Event;
|
|
WaitList[1] = TimerEvent;
|
|
Status = gBS->WaitForEvent (2, WaitList, &Index);
|
|
|
|
//
|
|
// If the timer expired, update anything that needs a refresh and keep waiting
|
|
//
|
|
if (!EFI_ERROR (Status) && Index == 1) {
|
|
Status = EFI_TIMEOUT;
|
|
UpdateDateAndTime ();
|
|
}
|
|
|
|
gBS->CloseEvent (TimerEvent);
|
|
} while (Status == EFI_TIMEOUT);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
UiAddMenuOption (
|
|
IN CHAR16 *String,
|
|
IN EFI_HII_HANDLE Handle,
|
|
IN EFI_TAG *Tags,
|
|
IN VOID *FormBinary,
|
|
IN UINTN IfrNumber
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Add one menu option by specified description and context.
|
|
|
|
Arguments:
|
|
String - String description for this option.
|
|
Context - Context data for entry.
|
|
|
|
Returns:
|
|
|
|
--*/
|
|
{
|
|
UI_MENU_OPTION *MenuOption;
|
|
|
|
MenuOption = AllocateZeroPool (sizeof (UI_MENU_OPTION));
|
|
ASSERT (MenuOption);
|
|
|
|
MenuOption->Signature = UI_MENU_OPTION_SIGNATURE;
|
|
MenuOption->Description = String;
|
|
MenuOption->Handle = Handle;
|
|
MenuOption->FormBinary = FormBinary;
|
|
MenuOption->IfrNumber = IfrNumber;
|
|
MenuOption->Skip = 1;
|
|
MenuOption->Tags = Tags;
|
|
MenuOption->TagIndex = 0;
|
|
MenuOption->ThisTag = &(MenuOption->Tags[MenuOption->TagIndex]);
|
|
MenuOption->EntryNumber = (UINT16) IfrNumber;
|
|
|
|
InsertTailList (&Menu, &MenuOption->Link);
|
|
}
|
|
|
|
VOID
|
|
UiAddSubMenuOption (
|
|
IN CHAR16 *String,
|
|
IN EFI_HII_HANDLE Handle,
|
|
IN EFI_TAG *Tags,
|
|
IN UINTN TagIndex,
|
|
IN UINT16 FormId,
|
|
IN UINT16 MenuItemCount
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Add one menu option by specified description and context.
|
|
|
|
Arguments:
|
|
String - String description for this option.
|
|
Context - Context data for entry.
|
|
|
|
Returns:
|
|
|
|
--*/
|
|
{
|
|
UI_MENU_OPTION *MenuOption;
|
|
|
|
MenuOption = AllocateZeroPool (sizeof (UI_MENU_OPTION));
|
|
ASSERT (MenuOption);
|
|
|
|
MenuOption->Signature = UI_MENU_OPTION_SIGNATURE;
|
|
MenuOption->Description = String;
|
|
MenuOption->Handle = Handle;
|
|
MenuOption->Skip = Tags[TagIndex].NumberOfLines;
|
|
MenuOption->IfrNumber = gActiveIfr;
|
|
MenuOption->Tags = Tags;
|
|
MenuOption->TagIndex = TagIndex;
|
|
MenuOption->ThisTag = &(MenuOption->Tags[MenuOption->TagIndex]);
|
|
MenuOption->Consistency = Tags[TagIndex].Consistency;
|
|
MenuOption->FormId = FormId;
|
|
MenuOption->GrayOut = Tags[TagIndex].GrayOut;
|
|
MenuOption->EntryNumber = MenuItemCount;
|
|
|
|
InsertTailList (&Menu, &MenuOption->Link);
|
|
}
|
|
|
|
EFI_STATUS
|
|
CreateDialog (
|
|
IN UINTN NumberOfLines,
|
|
IN BOOLEAN HotKey,
|
|
IN UINTN MaximumStringSize,
|
|
OUT CHAR16 *StringBuffer,
|
|
OUT EFI_INPUT_KEY *KeyValue,
|
|
IN CHAR16 *String,
|
|
...
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Routine used to abstract a generic dialog interface and return the selected key or string
|
|
|
|
Arguments:
|
|
NumberOfLines - The number of lines for the dialog box
|
|
HotKey - Defines whether a single character is parsed (TRUE) and returned in KeyValue
|
|
or a string is returned in StringBuffer. Two special characters are considered when entering a string, a SCAN_ESC and
|
|
an CHAR_CARRIAGE_RETURN. SCAN_ESC terminates string input and returns
|
|
MaximumStringSize - The maximum size in bytes of a typed in string (each character is a CHAR16) and the minimum string returned is two bytes
|
|
StringBuffer - The passed in pointer to the buffer which will hold the typed in string if HotKey is FALSE
|
|
KeyValue - The EFI_KEY value returned if HotKey is TRUE..
|
|
String - Pointer to the first string in the list
|
|
... - A series of (quantity == NumberOfLines) text strings which will be used to construct the dialog box
|
|
|
|
Returns:
|
|
EFI_SUCCESS - Displayed dialog and received user interaction
|
|
EFI_INVALID_PARAMETER - One of the parameters was invalid (e.g. (StringBuffer == NULL) && (HotKey == FALSE))
|
|
EFI_DEVICE_ERROR - User typed in an ESC character to exit the routine
|
|
|
|
--*/
|
|
{
|
|
VA_LIST Marker;
|
|
UINTN Count;
|
|
EFI_INPUT_KEY Key;
|
|
UINTN LargestString;
|
|
CHAR16 *TempString;
|
|
CHAR16 *BufferedString;
|
|
CHAR16 *StackString;
|
|
CHAR16 KeyPad[2];
|
|
UINTN Start;
|
|
UINTN Top;
|
|
UINTN Index;
|
|
BOOLEAN SelectionComplete;
|
|
UINTN InputOffset;
|
|
UINTN CurrentAttribute;
|
|
UINTN DimensionsWidth;
|
|
UINTN DimensionsHeight;
|
|
|
|
DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn;
|
|
DimensionsHeight = gScreenDimensions.BottomRow - gScreenDimensions.TopRow;
|
|
|
|
SelectionComplete = FALSE;
|
|
InputOffset = 0;
|
|
TempString = AllocateZeroPool (MaximumStringSize * 2);
|
|
BufferedString = AllocateZeroPool (MaximumStringSize * 2);
|
|
CurrentAttribute = gST->ConOut->Mode->Attribute;
|
|
|
|
ASSERT (TempString);
|
|
ASSERT (BufferedString);
|
|
|
|
VA_START (Marker, String);
|
|
|
|
//
|
|
// Zero the outgoing buffer
|
|
//
|
|
ZeroMem (StringBuffer, MaximumStringSize);
|
|
|
|
if (HotKey) {
|
|
if (KeyValue == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
} else {
|
|
if (StringBuffer == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
//
|
|
// Disable cursor
|
|
//
|
|
gST->ConOut->EnableCursor (gST->ConOut, FALSE);
|
|
|
|
LargestString = (GetStringWidth (String) / 2);
|
|
|
|
if (LargestString == L' ') {
|
|
InputOffset = 1;
|
|
}
|
|
//
|
|
// Determine the largest string in the dialog box
|
|
// Notice we are starting with 1 since String is the first string
|
|
//
|
|
for (Count = 1; Count < NumberOfLines; Count++) {
|
|
StackString = VA_ARG (Marker, CHAR16 *);
|
|
|
|
if (StackString[0] == L' ') {
|
|
InputOffset = Count + 1;
|
|
}
|
|
|
|
if ((GetStringWidth (StackString) / 2) > LargestString) {
|
|
//
|
|
// Size of the string visually and subtract the width by one for the null-terminator
|
|
//
|
|
LargestString = (GetStringWidth (StackString) / 2);
|
|
}
|
|
}
|
|
|
|
Start = (DimensionsWidth - LargestString - 2) / 2 + gScreenDimensions.LeftColumn + 1;
|
|
Top = ((DimensionsHeight - NumberOfLines - 2) / 2) + gScreenDimensions.TopRow - 1;
|
|
|
|
Count = 0;
|
|
|
|
//
|
|
// Display the Popup
|
|
//
|
|
CreateSharedPopUp (LargestString, NumberOfLines, &String);
|
|
|
|
//
|
|
// Take the first key typed and report it back?
|
|
//
|
|
if (HotKey) {
|
|
WaitForKeyStroke (&Key);
|
|
CopyMem (KeyValue, &Key, sizeof (EFI_INPUT_KEY));
|
|
|
|
} else {
|
|
do {
|
|
WaitForKeyStroke (&Key);
|
|
|
|
switch (Key.UnicodeChar) {
|
|
case CHAR_NULL:
|
|
switch (Key.ScanCode) {
|
|
case SCAN_ESC:
|
|
gBS->FreePool (TempString);
|
|
gBS->FreePool (BufferedString);
|
|
gST->ConOut->SetAttribute (gST->ConOut, CurrentAttribute);
|
|
gST->ConOut->EnableCursor (gST->ConOut, TRUE);
|
|
return EFI_DEVICE_ERROR;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
case CHAR_CARRIAGE_RETURN:
|
|
SelectionComplete = TRUE;
|
|
gBS->FreePool (TempString);
|
|
gBS->FreePool (BufferedString);
|
|
gST->ConOut->SetAttribute (gST->ConOut, CurrentAttribute);
|
|
gST->ConOut->EnableCursor (gST->ConOut, TRUE);
|
|
return EFI_SUCCESS;
|
|
break;
|
|
|
|
case CHAR_BACKSPACE:
|
|
if (StringBuffer[0] != CHAR_NULL) {
|
|
for (Index = 0; StringBuffer[Index] != CHAR_NULL; Index++) {
|
|
TempString[Index] = StringBuffer[Index];
|
|
}
|
|
//
|
|
// Effectively truncate string by 1 character
|
|
//
|
|
TempString[Index - 1] = CHAR_NULL;
|
|
StrCpy (StringBuffer, TempString);
|
|
}
|
|
|
|
default:
|
|
//
|
|
// If it is the beginning of the string, don't worry about checking maximum limits
|
|
//
|
|
if ((StringBuffer[0] == CHAR_NULL) && (Key.UnicodeChar != CHAR_BACKSPACE)) {
|
|
StrnCpy (StringBuffer, &Key.UnicodeChar, 1);
|
|
StrnCpy (TempString, &Key.UnicodeChar, 1);
|
|
} else if ((GetStringWidth (StringBuffer) < MaximumStringSize) && (Key.UnicodeChar != CHAR_BACKSPACE)) {
|
|
KeyPad[0] = Key.UnicodeChar;
|
|
KeyPad[1] = CHAR_NULL;
|
|
StrCat (StringBuffer, KeyPad);
|
|
StrCat (TempString, KeyPad);
|
|
}
|
|
//
|
|
// If the width of the input string is now larger than the screen, we nee to
|
|
// adjust the index to start printing portions of the string
|
|
//
|
|
SetUnicodeMem (BufferedString, LargestString, L' ');
|
|
|
|
PrintStringAt (Start + 1, Top + InputOffset, BufferedString);
|
|
|
|
if ((GetStringWidth (StringBuffer) / 2) > (DimensionsWidth - 2)) {
|
|
Index = (GetStringWidth (StringBuffer) / 2) - DimensionsWidth + 2;
|
|
} else {
|
|
Index = 0;
|
|
}
|
|
|
|
for (Count = 0; Index + 1 < GetStringWidth (StringBuffer) / 2; Index++, Count++) {
|
|
BufferedString[Count] = StringBuffer[Index];
|
|
}
|
|
|
|
PrintStringAt (Start + 1, Top + InputOffset, BufferedString);
|
|
break;
|
|
}
|
|
} while (!SelectionComplete);
|
|
}
|
|
|
|
gST->ConOut->SetAttribute (gST->ConOut, CurrentAttribute);
|
|
gST->ConOut->EnableCursor (gST->ConOut, TRUE);
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
CreateSharedPopUp (
|
|
IN UINTN RequestedWidth,
|
|
IN UINTN NumberOfLines,
|
|
IN CHAR16 **ArrayOfStrings
|
|
)
|
|
{
|
|
UINTN Index;
|
|
UINTN Count;
|
|
CHAR16 Character;
|
|
UINTN Start;
|
|
UINTN End;
|
|
UINTN Top;
|
|
UINTN Bottom;
|
|
CHAR16 *String;
|
|
|
|
UINTN DimensionsWidth;
|
|
UINTN DimensionsHeight;
|
|
|
|
DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn;
|
|
DimensionsHeight = gScreenDimensions.BottomRow - gScreenDimensions.TopRow;
|
|
|
|
Count = 0;
|
|
|
|
gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);
|
|
|
|
if ((RequestedWidth + 2) > DimensionsWidth) {
|
|
RequestedWidth = DimensionsWidth - 2;
|
|
}
|
|
//
|
|
// Subtract the PopUp width from total Columns, allow for one space extra on
|
|
// each end plus a border.
|
|
//
|
|
Start = (DimensionsWidth - RequestedWidth - 2) / 2 + gScreenDimensions.LeftColumn + 1;
|
|
End = Start + RequestedWidth + 1;
|
|
|
|
Top = ((DimensionsHeight - NumberOfLines - 2) / 2) + gScreenDimensions.TopRow - 1;
|
|
Bottom = Top + NumberOfLines + 2;
|
|
|
|
Character = (CHAR16) BOXDRAW_DOWN_RIGHT;
|
|
PrintCharAt (Start, Top, Character);
|
|
Character = (CHAR16) BOXDRAW_HORIZONTAL;
|
|
for (Index = Start; Index + 2 < End; Index++) {
|
|
PrintChar (Character);
|
|
}
|
|
|
|
Character = (CHAR16) BOXDRAW_DOWN_LEFT;
|
|
PrintChar (Character);
|
|
Character = (CHAR16) BOXDRAW_VERTICAL;
|
|
for (Index = Top; Index + 2 < Bottom; Index++) {
|
|
String = ArrayOfStrings[Count];
|
|
Count++;
|
|
|
|
//
|
|
// This will clear the background of the line - we never know who might have been
|
|
// here before us. This differs from the next clear in that it used the non-reverse
|
|
// video for normal printing.
|
|
//
|
|
if (GetStringWidth (String) / 2 > 1) {
|
|
ClearLines (Start, End, Index + 1, Index + 1, POPUP_TEXT | POPUP_BACKGROUND);
|
|
}
|
|
//
|
|
// Passing in a space results in the assumption that this is where typing will occur
|
|
//
|
|
if (String[0] == L' ') {
|
|
ClearLines (Start + 1, End - 1, Index + 1, Index + 1, POPUP_INVERSE_TEXT | POPUP_INVERSE_BACKGROUND);
|
|
}
|
|
//
|
|
// Passing in a NULL results in a blank space
|
|
//
|
|
if (String[0] == CHAR_NULL) {
|
|
ClearLines (Start, End, Index + 1, Index + 1, POPUP_TEXT | POPUP_BACKGROUND);
|
|
}
|
|
|
|
PrintStringAt (
|
|
((DimensionsWidth - GetStringWidth (String) / 2) / 2) + gScreenDimensions.LeftColumn + 1,
|
|
Index + 1,
|
|
String
|
|
);
|
|
gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);
|
|
PrintCharAt (Start, Index + 1, Character);
|
|
PrintCharAt (End - 1, Index + 1, Character);
|
|
}
|
|
|
|
Character = (CHAR16) BOXDRAW_UP_RIGHT;
|
|
PrintCharAt (Start, Bottom - 1, Character);
|
|
Character = (CHAR16) BOXDRAW_HORIZONTAL;
|
|
for (Index = Start; Index + 2 < End; Index++) {
|
|
PrintChar (Character);
|
|
}
|
|
|
|
Character = (CHAR16) BOXDRAW_UP_LEFT;
|
|
PrintChar (Character);
|
|
}
|
|
|
|
VOID
|
|
CreatePopUp (
|
|
IN UINTN RequestedWidth,
|
|
IN UINTN NumberOfLines,
|
|
IN CHAR16 *ArrayOfStrings,
|
|
...
|
|
)
|
|
{
|
|
CreateSharedPopUp (RequestedWidth, NumberOfLines, &ArrayOfStrings);
|
|
}
|
|
|
|
VOID
|
|
UpdateStatusBar (
|
|
IN UINTN MessageType,
|
|
IN UINT8 Flags,
|
|
IN BOOLEAN State
|
|
)
|
|
{
|
|
UINTN Index;
|
|
STATIC BOOLEAN InputError;
|
|
CHAR16 *NvUpdateMessage;
|
|
CHAR16 *InputErrorMessage;
|
|
|
|
NvUpdateMessage = GetToken (STRING_TOKEN (NV_UPDATE_MESSAGE), gHiiHandle);
|
|
InputErrorMessage = GetToken (STRING_TOKEN (INPUT_ERROR_MESSAGE), gHiiHandle);
|
|
|
|
switch (MessageType) {
|
|
case INPUT_ERROR:
|
|
if (State) {
|
|
gST->ConOut->SetAttribute (gST->ConOut, ERROR_TEXT);
|
|
PrintStringAt (
|
|
gScreenDimensions.LeftColumn + gPromptBlockWidth,
|
|
gScreenDimensions.BottomRow - 1,
|
|
InputErrorMessage
|
|
);
|
|
InputError = TRUE;
|
|
} else {
|
|
gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_HIGHLIGHT);
|
|
for (Index = 0; Index < (GetStringWidth (InputErrorMessage) - 2) / 2; Index++) {
|
|
PrintAt (gScreenDimensions.LeftColumn + gPromptBlockWidth + Index, gScreenDimensions.BottomRow - 1, (CHAR16 *) L" ");
|
|
}
|
|
|
|
InputError = FALSE;
|
|
}
|
|
break;
|
|
|
|
case NV_UPDATE_REQUIRED:
|
|
if (gClassOfVfr != EFI_FRONT_PAGE_SUBCLASS) {
|
|
if (State) {
|
|
gST->ConOut->SetAttribute (gST->ConOut, INFO_TEXT);
|
|
PrintStringAt (
|
|
gScreenDimensions.LeftColumn + gPromptBlockWidth + gOptionBlockWidth,
|
|
gScreenDimensions.BottomRow - 1,
|
|
NvUpdateMessage
|
|
);
|
|
gResetRequired = (BOOLEAN) (gResetRequired | ((Flags & EFI_IFR_FLAG_RESET_REQUIRED) == EFI_IFR_FLAG_RESET_REQUIRED));
|
|
|
|
gNvUpdateRequired = TRUE;
|
|
} else {
|
|
gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_HIGHLIGHT);
|
|
for (Index = 0; Index < (GetStringWidth (NvUpdateMessage) - 2) / 2; Index++) {
|
|
PrintAt (
|
|
(gScreenDimensions.LeftColumn + gPromptBlockWidth + gOptionBlockWidth + Index),
|
|
gScreenDimensions.BottomRow - 1,
|
|
(CHAR16 *) L" "
|
|
);
|
|
}
|
|
|
|
gNvUpdateRequired = FALSE;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case REFRESH_STATUS_BAR:
|
|
if (InputError) {
|
|
UpdateStatusBar (INPUT_ERROR, Flags, TRUE);
|
|
}
|
|
|
|
if (gNvUpdateRequired) {
|
|
UpdateStatusBar (NV_UPDATE_REQUIRED, Flags, TRUE);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
gBS->FreePool (InputErrorMessage);
|
|
gBS->FreePool (NvUpdateMessage);
|
|
return ;
|
|
}
|
|
|
|
VOID
|
|
FreeData (
|
|
IN EFI_FILE_FORM_TAGS *FileFormTagsHead,
|
|
IN CHAR16 *FormattedString,
|
|
IN CHAR16 *OptionString
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Used to remove the allocated data instances
|
|
|
|
Arguments:
|
|
|
|
Returns:
|
|
|
|
--*/
|
|
{
|
|
EFI_FILE_FORM_TAGS *FileForm;
|
|
EFI_FILE_FORM_TAGS *PreviousFileForm;
|
|
EFI_FORM_TAGS *FormTags;
|
|
EFI_FORM_TAGS *PreviousFormTags;
|
|
EFI_IFR_BINARY *IfrBinary;
|
|
EFI_IFR_BINARY *PreviousIfrBinary;
|
|
EFI_INCONSISTENCY_DATA *Inconsistent;
|
|
EFI_VARIABLE_DEFINITION *VariableDefinition;
|
|
EFI_VARIABLE_DEFINITION *PreviousVariableDefinition;
|
|
VOID *Buffer;
|
|
UINTN Index;
|
|
|
|
FileForm = FileFormTagsHead;
|
|
|
|
if (FormattedString != NULL) {
|
|
gBS->FreePool (FormattedString);
|
|
}
|
|
|
|
if (OptionString != NULL) {
|
|
gBS->FreePool (OptionString);
|
|
}
|
|
|
|
for (; FileForm != NULL;) {
|
|
PreviousFileForm = NULL;
|
|
|
|
//
|
|
// Advance FileForm to the last entry
|
|
//
|
|
for (; FileForm->NextFile != NULL; FileForm = FileForm->NextFile) {
|
|
PreviousFileForm = FileForm;
|
|
}
|
|
|
|
FormTags = &FileForm->FormTags;
|
|
|
|
for (; FormTags != NULL;) {
|
|
FormTags = &FileForm->FormTags;
|
|
PreviousFormTags = NULL;
|
|
|
|
//
|
|
// Advance FormTags to the last entry
|
|
//
|
|
for (; FormTags->Next != NULL; FormTags = FormTags->Next) {
|
|
PreviousFormTags = FormTags;
|
|
}
|
|
//
|
|
// Walk through each of the tags and free the IntList allocation
|
|
//
|
|
for (Index = 0; FormTags->Tags[Index].Operand != EFI_IFR_END_FORM_OP; Index++) {
|
|
//
|
|
// It is more than likely that the very last page will contain an end formset
|
|
//
|
|
if (FormTags->Tags[Index].Operand == EFI_IFR_END_FORM_SET_OP) {
|
|
break;
|
|
}
|
|
|
|
if (FormTags->Tags[Index].IntList != NULL) {
|
|
gBS->FreePool (FormTags->Tags[Index].IntList);
|
|
}
|
|
}
|
|
|
|
if (PreviousFormTags != NULL) {
|
|
gBS->FreePool (FormTags->Tags);
|
|
FormTags = PreviousFormTags;
|
|
gBS->FreePool (FormTags->Next);
|
|
FormTags->Next = NULL;
|
|
} else {
|
|
gBS->FreePool (FormTags->Tags);
|
|
FormTags = NULL;
|
|
}
|
|
}
|
|
//
|
|
// Last FileForm entry's Inconsistent database
|
|
//
|
|
Inconsistent = FileForm->InconsistentTags;
|
|
|
|
//
|
|
// Advance Inconsistent to the last entry
|
|
//
|
|
for (; Inconsistent->Next != NULL; Inconsistent = Inconsistent->Next)
|
|
;
|
|
|
|
for (; Inconsistent != NULL;) {
|
|
//
|
|
// Preserve the Previous pointer
|
|
//
|
|
Buffer = (VOID *) Inconsistent->Previous;
|
|
|
|
//
|
|
// Free the current entry
|
|
//
|
|
gBS->FreePool (Inconsistent);
|
|
|
|
//
|
|
// Restore the Previous pointer
|
|
//
|
|
Inconsistent = (EFI_INCONSISTENCY_DATA *) Buffer;
|
|
}
|
|
|
|
VariableDefinition = FileForm->VariableDefinitions;
|
|
|
|
for (; VariableDefinition != NULL;) {
|
|
VariableDefinition = FileForm->VariableDefinitions;
|
|
PreviousVariableDefinition = NULL;
|
|
|
|
//
|
|
// Advance VariableDefinitions to the last entry
|
|
//
|
|
for (; VariableDefinition->Next != NULL; VariableDefinition = VariableDefinition->Next) {
|
|
PreviousVariableDefinition = VariableDefinition;
|
|
}
|
|
|
|
gBS->FreePool (VariableDefinition->VariableName);
|
|
gBS->FreePool (VariableDefinition->NvRamMap);
|
|
gBS->FreePool (VariableDefinition->FakeNvRamMap);
|
|
|
|
if (PreviousVariableDefinition != NULL) {
|
|
VariableDefinition = PreviousVariableDefinition;
|
|
gBS->FreePool (VariableDefinition->Next);
|
|
VariableDefinition->Next = NULL;
|
|
} else {
|
|
gBS->FreePool (VariableDefinition);
|
|
VariableDefinition = NULL;
|
|
}
|
|
}
|
|
|
|
if (PreviousFileForm != NULL) {
|
|
FileForm = PreviousFileForm;
|
|
gBS->FreePool (FileForm->NextFile);
|
|
FileForm->NextFile = NULL;
|
|
} else {
|
|
gBS->FreePool (FileForm);
|
|
FileForm = NULL;
|
|
}
|
|
}
|
|
|
|
IfrBinary = gBinaryDataHead;
|
|
|
|
for (; IfrBinary != NULL;) {
|
|
IfrBinary = gBinaryDataHead;
|
|
PreviousIfrBinary = NULL;
|
|
|
|
//
|
|
// Advance IfrBinary to the last entry
|
|
//
|
|
for (; IfrBinary->Next != NULL; IfrBinary = IfrBinary->Next) {
|
|
PreviousIfrBinary = IfrBinary;
|
|
}
|
|
|
|
gBS->FreePool (IfrBinary->IfrPackage);
|
|
|
|
if (PreviousIfrBinary != NULL) {
|
|
IfrBinary = PreviousIfrBinary;
|
|
gBS->FreePool (IfrBinary->Next);
|
|
IfrBinary->Next = NULL;
|
|
} else {
|
|
gBS->FreePool (IfrBinary);
|
|
IfrBinary = NULL;
|
|
}
|
|
}
|
|
|
|
gBS->FreePool (gPreviousValue);
|
|
gPreviousValue = NULL;
|
|
|
|
//
|
|
// Free Browser Strings
|
|
//
|
|
gBS->FreePool (gPressEnter);
|
|
gBS->FreePool (gConfirmError);
|
|
gBS->FreePool (gConfirmPassword);
|
|
gBS->FreePool (gPromptForNewPassword);
|
|
gBS->FreePool (gPromptForPassword);
|
|
gBS->FreePool (gToggleCheckBox);
|
|
gBS->FreePool (gNumericInput);
|
|
gBS->FreePool (gMakeSelection);
|
|
gBS->FreePool (gMoveHighlight);
|
|
gBS->FreePool (gEscapeString);
|
|
gBS->FreePool (gEnterCommitString);
|
|
gBS->FreePool (gEnterString);
|
|
gBS->FreePool (gFunctionOneString);
|
|
gBS->FreePool (gFunctionTwoString);
|
|
gBS->FreePool (gFunctionNineString);
|
|
gBS->FreePool (gFunctionTenString);
|
|
return ;
|
|
}
|
|
|
|
STATIC
|
|
BOOLEAN
|
|
SelectionsAreValid (
|
|
IN UI_MENU_OPTION *MenuOption,
|
|
IN EFI_FILE_FORM_TAGS *FileFormTagsHead
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Initiate late consistency checks against the current page.
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Returns:
|
|
|
|
--*/
|
|
{
|
|
LIST_ENTRY *Link;
|
|
EFI_TAG *Tag;
|
|
EFI_FILE_FORM_TAGS *FileFormTags;
|
|
CHAR16 *StringPtr;
|
|
CHAR16 NullCharacter;
|
|
UINTN Index;
|
|
UINT16 *NvRamMap;
|
|
STRING_REF PopUp;
|
|
EFI_INPUT_KEY Key;
|
|
EFI_VARIABLE_DEFINITION *VariableDefinition;
|
|
|
|
StringPtr = (CHAR16 *) L"\0";
|
|
NullCharacter = CHAR_NULL;
|
|
|
|
FileFormTags = FileFormTagsHead;
|
|
|
|
for (Index = 0; Index < MenuOption->IfrNumber; Index++) {
|
|
FileFormTags = FileFormTags->NextFile;
|
|
}
|
|
|
|
for (Link = Menu.ForwardLink; Link != &Menu; Link = Link->ForwardLink) {
|
|
MenuOption = CR (Link, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
|
|
|
|
Tag = MenuOption->ThisTag;
|
|
|
|
ExtractRequestedNvMap (FileFormTags, Tag->VariableNumber, &VariableDefinition);
|
|
NvRamMap = (UINT16 *) &VariableDefinition->NvRamMap[Tag->StorageStart];
|
|
|
|
//
|
|
// If the op-code has a late check, ensure consistency checks are now applied
|
|
//
|
|
if (Tag->Flags & EFI_IFR_FLAG_LATE_CHECK) {
|
|
if (ValueIsNotValid (TRUE, 0, Tag, FileFormTags, &PopUp)) {
|
|
if (PopUp != 0x0000) {
|
|
StringPtr = GetToken (PopUp, MenuOption->Handle);
|
|
|
|
CreatePopUp (GetStringWidth (StringPtr) / 2, 3, &NullCharacter, StringPtr, &NullCharacter);
|
|
|
|
do {
|
|
WaitForKeyStroke (&Key);
|
|
|
|
switch (Key.UnicodeChar) {
|
|
|
|
case CHAR_CARRIAGE_RETURN:
|
|
//
|
|
// Since the value can be one byte long or two bytes long, do a CopyMem based on StorageWidth
|
|
//
|
|
CopyMem (NvRamMap, &Tag->OldValue, Tag->StorageWidth);
|
|
gBS->FreePool (StringPtr);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
} while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
UINT16
|
|
GetWidth (
|
|
IN EFI_TAG *Tag,
|
|
IN EFI_HII_HANDLE Handle
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Get the supported width for a particular op-code
|
|
|
|
Arguments:
|
|
Tag - The Tag structure passed in.
|
|
Handle - The handle in the HII database being used
|
|
|
|
Returns:
|
|
Returns the number of CHAR16 characters that is support.
|
|
|
|
|
|
--*/
|
|
{
|
|
CHAR16 *String;
|
|
UINTN Size;
|
|
|
|
Size = 0x00;
|
|
|
|
//
|
|
// See if the second text parameter is really NULL
|
|
//
|
|
if ((Tag->Operand == EFI_IFR_TEXT_OP) && (Tag->TextTwo != 0)) {
|
|
String = GetToken (Tag->TextTwo, Handle);
|
|
Size = StrLen (String);
|
|
gBS->FreePool (String);
|
|
}
|
|
|
|
if ((Tag->Operand == EFI_IFR_SUBTITLE_OP) ||
|
|
(Tag->Operand == EFI_IFR_REF_OP) ||
|
|
(Tag->Operand == EFI_IFR_PASSWORD_OP) ||
|
|
(Tag->Operand == EFI_IFR_STRING_OP) ||
|
|
(Tag->Operand == EFI_IFR_INVENTORY_OP) ||
|
|
//
|
|
// Allow a wide display if text op-code and no secondary text op-code
|
|
//
|
|
((Tag->Operand == EFI_IFR_TEXT_OP) && (Size == 0x0000))
|
|
) {
|
|
return (UINT16) (gPromptBlockWidth + gOptionBlockWidth);
|
|
} else {
|
|
return (UINT16) gPromptBlockWidth;
|
|
}
|
|
}
|
|
|
|
UINT16
|
|
GetLineByWidth (
|
|
IN CHAR16 *InputString,
|
|
IN UINT16 LineWidth,
|
|
IN OUT UINTN *Index,
|
|
OUT CHAR16 **OutputString
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Will copy LineWidth amount of a string in the OutputString buffer and return the
|
|
number of CHAR16 characters that were copied into the OutputString buffer.
|
|
|
|
Arguments:
|
|
InputString - String description for this option.
|
|
LineWidth - Width of the desired string to extract in CHAR16 characters
|
|
Index - Where in InputString to start the copy process
|
|
OutputString - Buffer to copy the string into
|
|
|
|
Returns:
|
|
Returns the number of CHAR16 characters that were copied into the OutputString buffer.
|
|
|
|
|
|
--*/
|
|
{
|
|
static BOOLEAN Finished;
|
|
UINT16 Count;
|
|
UINT16 Count2;
|
|
|
|
if (Finished) {
|
|
Finished = FALSE;
|
|
return (UINT16) 0;
|
|
}
|
|
|
|
Count = LineWidth;
|
|
Count2 = 0;
|
|
|
|
*OutputString = AllocateZeroPool (((UINTN) (LineWidth + 1) * 2));
|
|
|
|
//
|
|
// Ensure we have got a valid buffer
|
|
//
|
|
if (*OutputString != NULL) {
|
|
|
|
//
|
|
//NARROW_CHAR can not be printed in screen, so if a line only contain the two CHARs: 'NARROW_CHAR + CHAR_CARRIAGE_RETURN' , it is a empty line in Screen.
|
|
//To avoid displaying this empty line in screen, just skip the two CHARs here.
|
|
//
|
|
if ((InputString[*Index] == NARROW_CHAR) && (InputString[*Index + 1] == CHAR_CARRIAGE_RETURN)) {
|
|
*Index = *Index + 2;
|
|
}
|
|
|
|
//
|
|
// Fast-forward the string and see if there is a carriage-return in the string
|
|
//
|
|
for (; (InputString[*Index + Count2] != CHAR_CARRIAGE_RETURN) && (Count2 != LineWidth); Count2++)
|
|
;
|
|
|
|
//
|
|
// Copy the desired LineWidth of data to the output buffer.
|
|
// Also make sure that we don't copy more than the string.
|
|
// Also make sure that if there are linefeeds, we account for them.
|
|
//
|
|
if ((StrSize (&InputString[*Index]) <= ((UINTN) (LineWidth + 1) * 2)) &&
|
|
(StrSize (&InputString[*Index]) <= ((UINTN) (Count2 + 1) * 2))
|
|
) {
|
|
//
|
|
// Convert to CHAR16 value and show that we are done with this operation
|
|
//
|
|
LineWidth = (UINT16) ((StrSize (&InputString[*Index]) - 2) / 2);
|
|
if (LineWidth != 0) {
|
|
Finished = TRUE;
|
|
}
|
|
} else {
|
|
if (Count2 == LineWidth) {
|
|
//
|
|
// Rewind the string from the maximum size until we see a space to break the line
|
|
//
|
|
for (; (InputString[*Index + LineWidth] != CHAR_SPACE) && (LineWidth != 0); LineWidth--)
|
|
;
|
|
if (LineWidth == 0) {
|
|
LineWidth = Count;
|
|
}
|
|
} else {
|
|
LineWidth = Count2;
|
|
}
|
|
}
|
|
|
|
CopyMem (*OutputString, &InputString[*Index], LineWidth * 2);
|
|
|
|
//
|
|
// If currently pointing to a space, increment the index to the first non-space character
|
|
//
|
|
for (;
|
|
(InputString[*Index + LineWidth] == CHAR_SPACE) || (InputString[*Index + LineWidth] == CHAR_CARRIAGE_RETURN);
|
|
(*Index)++
|
|
)
|
|
;
|
|
*Index = (UINT16) (*Index + LineWidth);
|
|
return LineWidth;
|
|
} else {
|
|
return (UINT16) 0;
|
|
}
|
|
}
|
|
|
|
STATIC
|
|
VOID
|
|
UpdateOptionSkipLines (
|
|
IN EFI_IFR_DATA_ARRAY *PageData,
|
|
IN UI_MENU_OPTION *MenuOption,
|
|
IN EFI_FILE_FORM_TAGS *FileFormTagsHead,
|
|
IN CHAR16 **OptionalString,
|
|
IN UINTN SkipValue
|
|
)
|
|
{
|
|
UINTN Index;
|
|
UINTN Loop;
|
|
UINT16 Width;
|
|
UINTN Row;
|
|
UINTN OriginalRow;
|
|
CHAR16 *OutputString;
|
|
CHAR16 *OptionString;
|
|
|
|
Row = 0;
|
|
OptionString = *OptionalString;
|
|
OutputString = NULL;
|
|
|
|
ProcessOptions (MenuOption, FALSE, FileFormTagsHead, PageData, &OptionString);
|
|
|
|
if (OptionString != NULL) {
|
|
//
|
|
// If leading spaces on OptionString - remove the spaces
|
|
//
|
|
for (Index = 0; OptionString[Index] == L' '; Index++)
|
|
;
|
|
|
|
for (Loop = 0; OptionString[Index] != CHAR_NULL; Index++) {
|
|
OptionString[Loop] = OptionString[Index];
|
|
Loop++;
|
|
}
|
|
|
|
OptionString[Loop] = CHAR_NULL;
|
|
|
|
Width = (UINT16) gOptionBlockWidth;
|
|
|
|
OriginalRow = Row;
|
|
|
|
for (Index = 0; GetLineByWidth (OptionString, Width, &Index, &OutputString) != 0x0000;) {
|
|
//
|
|
// If there is more string to process print on the next row and increment the Skip value
|
|
//
|
|
if (StrLen (&OptionString[Index])) {
|
|
if (SkipValue == 0) {
|
|
Row++;
|
|
//
|
|
// Since the Number of lines for this menu entry may or may not be reflected accurately
|
|
// since the prompt might be 1 lines and option might be many, and vice versa, we need to do
|
|
// some testing to ensure we are keeping this in-sync.
|
|
//
|
|
// If the difference in rows is greater than or equal to the skip value, increase the skip value
|
|
//
|
|
if ((Row - OriginalRow) >= MenuOption->Skip) {
|
|
MenuOption->Skip++;
|
|
}
|
|
}
|
|
}
|
|
|
|
gBS->FreePool (OutputString);
|
|
if (SkipValue != 0) {
|
|
SkipValue--;
|
|
}
|
|
}
|
|
|
|
Row = OriginalRow;
|
|
}
|
|
|
|
*OptionalString = OptionString;
|
|
}
|
|
//
|
|
// Search table for UiDisplayMenu()
|
|
//
|
|
SCAN_CODE_TO_SCREEN_OPERATION gScanCodeToOperation[] = {
|
|
{ SCAN_UP, UiUp },
|
|
{ SCAN_DOWN, UiDown },
|
|
{ SCAN_PAGE_UP, UiPageUp },
|
|
{ SCAN_PAGE_DOWN, UiPageDown},
|
|
{ SCAN_ESC, UiReset},
|
|
{ SCAN_F2, UiPrevious},
|
|
{ SCAN_LEFT, UiLeft },
|
|
{ SCAN_RIGHT, UiRight },
|
|
{ SCAN_F9, UiDefault},
|
|
{ SCAN_F10, UiSave }
|
|
};
|
|
|
|
SCREEN_OPERATION_T0_CONTROL_FLAG gScreenOperationToControlFlag[] = {
|
|
{ UiNoOperation, CfUiNoOperation },
|
|
{ UiDefault, CfUiDefault },
|
|
{ UiSelect, CfUiSelect },
|
|
{ UiUp, CfUiUp},
|
|
{ UiDown, CfUiDown },
|
|
{ UiLeft, CfUiLeft },
|
|
{ UiRight, CfUiRight },
|
|
{ UiReset, CfUiReset },
|
|
{ UiSave, CfUiSave },
|
|
{ UiPrevious, CfUiPrevious },
|
|
{ UiPageUp, CfUiPageUp },
|
|
{ UiPageDown, CfUiPageDown }
|
|
};
|
|
|
|
UI_MENU_OPTION *
|
|
UiDisplayMenu (
|
|
IN BOOLEAN SubMenu,
|
|
IN EFI_FILE_FORM_TAGS *FileFormTagsHead,
|
|
OUT EFI_IFR_DATA_ARRAY *PageData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Display menu and wait for user to select one menu option, then return it.
|
|
If AutoBoot is enabled, then if user doesn't select any option,
|
|
after period of time, it will automatically return the first menu option.
|
|
|
|
Arguments:
|
|
SubMenu - Indicate is sub menu.
|
|
FileFormTagsHead - A pointer to the EFI_FILE_FORM_TAGS structure.
|
|
PageData - A pointer to the EFI_IFR_DATA_ARRAY.
|
|
|
|
Returns:
|
|
Return the pointer of the menu which selected,
|
|
otherwise return NULL.
|
|
|
|
--*/
|
|
{
|
|
INTN SkipValue;
|
|
INTN Difference;
|
|
INTN OldSkipValue;
|
|
UINTN Row;
|
|
UINTN Col;
|
|
UINTN Temp;
|
|
UINTN Temp2;
|
|
UINTN TopRow;
|
|
UINTN BottomRow;
|
|
UINTN OriginalRow;
|
|
UINTN Index;
|
|
UINTN DataAndTimeLineNumberPad;
|
|
UINT32 Count;
|
|
INT16 OriginalTimeOut;
|
|
UINT8 *Location;
|
|
UINT16 Width;
|
|
CHAR16 *StringPtr;
|
|
CHAR16 *OptionString;
|
|
CHAR16 *OutputString;
|
|
CHAR16 *FormattedString;
|
|
CHAR16 YesResponse;
|
|
CHAR16 NoResponse;
|
|
BOOLEAN NewLine;
|
|
BOOLEAN Repaint;
|
|
BOOLEAN SavedValue;
|
|
EFI_STATUS Status;
|
|
UI_MENU_LIST *UiMenuList;
|
|
EFI_INPUT_KEY Key;
|
|
LIST_ENTRY *Link;
|
|
LIST_ENTRY *NewPos;
|
|
LIST_ENTRY *TopOfScreen;
|
|
LIST_ENTRY *SavedListEntry;
|
|
UI_MENU_OPTION *Selection;
|
|
UI_MENU_OPTION *MenuOption;
|
|
UI_MENU_OPTION *NextMenuOption;
|
|
UI_MENU_OPTION *SavedMenuOption;
|
|
UI_MENU_OPTION *PreviousMenuOption;
|
|
EFI_IFR_BINARY *IfrBinary;
|
|
UI_CONTROL_FLAG ControlFlag;
|
|
EFI_SCREEN_DESCRIPTOR LocalScreen;
|
|
EFI_FILE_FORM_TAGS *FileFormTags;
|
|
MENU_REFRESH_ENTRY *MenuRefreshEntry;
|
|
MENU_REFRESH_ENTRY *OldMenuRefreshEntry;
|
|
UI_SCREEN_OPERATION ScreenOperation;
|
|
EFI_VARIABLE_DEFINITION *VariableDefinition;
|
|
EFI_FORM_CALLBACK_PROTOCOL *FormCallback;
|
|
EFI_HII_VARIABLE_PACK_LIST *NvMapListHead;
|
|
EFI_HII_VARIABLE_PACK_LIST *NvMapListNode;
|
|
VOID *NvMap;
|
|
UINTN NvMapSize;
|
|
|
|
CopyMem (&LocalScreen, &gScreenDimensions, sizeof (EFI_SCREEN_DESCRIPTOR));
|
|
|
|
VariableDefinition = NULL;
|
|
Status = EFI_SUCCESS;
|
|
FormattedString = NULL;
|
|
OptionString = NULL;
|
|
ScreenOperation = UiNoOperation;
|
|
NewLine = TRUE;
|
|
FormCallback = NULL;
|
|
FileFormTags = NULL;
|
|
OutputString = NULL;
|
|
gUpArrow = FALSE;
|
|
gDownArrow = FALSE;
|
|
SkipValue = 0;
|
|
OldSkipValue = 0;
|
|
MenuRefreshEntry = gMenuRefreshHead;
|
|
OldMenuRefreshEntry = gMenuRefreshHead;
|
|
NextMenuOption = NULL;
|
|
PreviousMenuOption = NULL;
|
|
SavedMenuOption = NULL;
|
|
IfrBinary = NULL;
|
|
NvMap = NULL;
|
|
NvMapSize = 0;
|
|
|
|
ZeroMem (&Key, sizeof (EFI_INPUT_KEY));
|
|
|
|
if (gClassOfVfr == EFI_FRONT_PAGE_SUBCLASS) {
|
|
TopRow = LocalScreen.TopRow + FRONT_PAGE_HEADER_HEIGHT + SCROLL_ARROW_HEIGHT;
|
|
Row = LocalScreen.TopRow + FRONT_PAGE_HEADER_HEIGHT + SCROLL_ARROW_HEIGHT;
|
|
} else {
|
|
TopRow = LocalScreen.TopRow + NONE_FRONT_PAGE_HEADER_HEIGHT + SCROLL_ARROW_HEIGHT;
|
|
Row = LocalScreen.TopRow + NONE_FRONT_PAGE_HEADER_HEIGHT + SCROLL_ARROW_HEIGHT;
|
|
}
|
|
|
|
if (SubMenu) {
|
|
Col = LocalScreen.LeftColumn;
|
|
} else {
|
|
Col = LocalScreen.LeftColumn + LEFT_SKIPPED_COLUMNS;
|
|
}
|
|
|
|
BottomRow = LocalScreen.BottomRow - STATUS_BAR_HEIGHT - FOOTER_HEIGHT - SCROLL_ARROW_HEIGHT - 1;
|
|
|
|
TopOfScreen = Menu.ForwardLink;
|
|
Repaint = TRUE;
|
|
MenuOption = NULL;
|
|
|
|
//
|
|
// Get user's selection
|
|
//
|
|
Selection = NULL;
|
|
NewPos = Menu.ForwardLink;
|
|
gST->ConOut->EnableCursor (gST->ConOut, FALSE);
|
|
|
|
UpdateStatusBar (REFRESH_STATUS_BAR, (UINT8) 0, TRUE);
|
|
|
|
ControlFlag = CfInitialization;
|
|
|
|
while (TRUE) {
|
|
switch (ControlFlag) {
|
|
case CfInitialization:
|
|
ControlFlag = CfCheckSelection;
|
|
if (gExitRequired) {
|
|
ScreenOperation = UiReset;
|
|
ControlFlag = CfScreenOperation;
|
|
} else if (gSaveRequired) {
|
|
ScreenOperation = UiSave;
|
|
ControlFlag = CfScreenOperation;
|
|
} else if (IsListEmpty (&Menu)) {
|
|
ControlFlag = CfReadKey;
|
|
}
|
|
break;
|
|
|
|
case CfCheckSelection:
|
|
if (Selection != NULL) {
|
|
ControlFlag = CfExit;
|
|
} else {
|
|
ControlFlag = CfRepaint;
|
|
}
|
|
|
|
FileFormTags = FileFormTagsHead;
|
|
break;
|
|
|
|
case CfRepaint:
|
|
ControlFlag = CfRefreshHighLight;
|
|
|
|
if (Repaint) {
|
|
//
|
|
// Display menu
|
|
//
|
|
SavedMenuOption = MenuOption;
|
|
gDownArrow = FALSE;
|
|
gUpArrow = FALSE;
|
|
Row = TopRow;
|
|
|
|
Temp = SkipValue;
|
|
Temp2 = SkipValue;
|
|
|
|
ClearLines (
|
|
LocalScreen.LeftColumn,
|
|
LocalScreen.RightColumn,
|
|
TopRow - SCROLL_ARROW_HEIGHT,
|
|
BottomRow + SCROLL_ARROW_HEIGHT,
|
|
FIELD_TEXT | FIELD_BACKGROUND
|
|
);
|
|
|
|
while (gMenuRefreshHead != NULL) {
|
|
OldMenuRefreshEntry = gMenuRefreshHead->Next;
|
|
|
|
gBS->FreePool (gMenuRefreshHead);
|
|
|
|
gMenuRefreshHead = OldMenuRefreshEntry;
|
|
}
|
|
|
|
for (Link = TopOfScreen; Link != &Menu; Link = Link->ForwardLink) {
|
|
MenuOption = CR (Link, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
|
|
MenuOption->Row = Row;
|
|
OriginalRow = Row;
|
|
MenuOption->Col = Col;
|
|
MenuOption->OptCol = gPromptBlockWidth + 1 + LocalScreen.LeftColumn;
|
|
|
|
if (SubMenu) {
|
|
if (MenuOption->ThisTag->GrayOut) {
|
|
gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_GRAYED | FIELD_BACKGROUND);
|
|
} else {
|
|
if (MenuOption->ThisTag->Operand == EFI_IFR_SUBTITLE_OP) {
|
|
gST->ConOut->SetAttribute (gST->ConOut, SUBTITLE_TEXT | FIELD_BACKGROUND);
|
|
}
|
|
}
|
|
|
|
Width = GetWidth (MenuOption->ThisTag, MenuOption->Handle);
|
|
|
|
OriginalRow = Row;
|
|
|
|
for (Index = 0; GetLineByWidth (MenuOption->Description, Width, &Index, &OutputString) != 0x0000;) {
|
|
if ((Temp == 0) && (Row <= BottomRow)) {
|
|
PrintStringAt (Col, Row, OutputString);
|
|
}
|
|
//
|
|
// If there is more string to process print on the next row and increment the Skip value
|
|
//
|
|
if (StrLen (&MenuOption->Description[Index])) {
|
|
if (Temp == 0) {
|
|
Row++;
|
|
}
|
|
}
|
|
|
|
gBS->FreePool (OutputString);
|
|
if (Temp != 0) {
|
|
Temp--;
|
|
}
|
|
}
|
|
|
|
Temp = 0;
|
|
|
|
Row = OriginalRow;
|
|
|
|
gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
|
|
ProcessOptions (MenuOption, FALSE, FileFormTagsHead, PageData, &OptionString);
|
|
|
|
if (OptionString != NULL) {
|
|
//
|
|
// If leading spaces on OptionString - remove the spaces
|
|
//
|
|
for (Index = 0; OptionString[Index] == L' '; Index++) {
|
|
MenuOption->OptCol++;
|
|
}
|
|
|
|
for (Count = 0; OptionString[Index] != CHAR_NULL; Index++) {
|
|
OptionString[Count] = OptionString[Index];
|
|
Count++;
|
|
}
|
|
|
|
OptionString[Count] = CHAR_NULL;
|
|
|
|
//
|
|
// If this is a date or time op-code and is used to reflect an RTC, register the op-code
|
|
//
|
|
if ((MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP ||
|
|
MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP) &&
|
|
(MenuOption->ThisTag->StorageStart >= FileFormTags->FormTags.Tags[0].NvDataSize)) {
|
|
|
|
if (gMenuRefreshHead == NULL) {
|
|
MenuRefreshEntry = AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY));
|
|
ASSERT (MenuRefreshEntry != NULL);
|
|
MenuRefreshEntry->MenuOption = MenuOption;
|
|
MenuRefreshEntry->FileFormTagsHead = FileFormTagsHead;
|
|
MenuRefreshEntry->CurrentColumn = MenuOption->OptCol;
|
|
MenuRefreshEntry->CurrentRow = MenuOption->Row;
|
|
MenuRefreshEntry->CurrentAttribute = FIELD_TEXT | FIELD_BACKGROUND;
|
|
gMenuRefreshHead = MenuRefreshEntry;
|
|
} else {
|
|
//
|
|
// Advance to the last entry
|
|
//
|
|
for (MenuRefreshEntry = gMenuRefreshHead;
|
|
MenuRefreshEntry->Next != NULL;
|
|
MenuRefreshEntry = MenuRefreshEntry->Next
|
|
)
|
|
;
|
|
MenuRefreshEntry->Next = AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY));
|
|
ASSERT (MenuRefreshEntry->Next != NULL);
|
|
MenuRefreshEntry = MenuRefreshEntry->Next;
|
|
MenuRefreshEntry->MenuOption = MenuOption;
|
|
MenuRefreshEntry->FileFormTagsHead = FileFormTagsHead;
|
|
MenuRefreshEntry->CurrentColumn = MenuOption->OptCol;
|
|
MenuRefreshEntry->CurrentRow = MenuOption->Row;
|
|
MenuRefreshEntry->CurrentAttribute = FIELD_TEXT | FIELD_BACKGROUND;
|
|
}
|
|
}
|
|
|
|
Width = (UINT16) gOptionBlockWidth;
|
|
|
|
OriginalRow = Row;
|
|
|
|
for (Index = 0; GetLineByWidth (OptionString, Width, &Index, &OutputString) != 0x0000;) {
|
|
if ((Temp2 == 0) && (Row <= BottomRow)) {
|
|
PrintStringAt (MenuOption->OptCol, Row, OutputString);
|
|
}
|
|
//
|
|
// If there is more string to process print on the next row and increment the Skip value
|
|
//
|
|
if (StrLen (&OptionString[Index])) {
|
|
if (Temp2 == 0) {
|
|
Row++;
|
|
//
|
|
// Since the Number of lines for this menu entry may or may not be reflected accurately
|
|
// since the prompt might be 1 lines and option might be many, and vice versa, we need to do
|
|
// some testing to ensure we are keeping this in-sync.
|
|
//
|
|
// If the difference in rows is greater than or equal to the skip value, increase the skip value
|
|
//
|
|
if ((Row - OriginalRow) >= MenuOption->Skip) {
|
|
MenuOption->Skip++;
|
|
}
|
|
}
|
|
}
|
|
|
|
gBS->FreePool (OutputString);
|
|
if (Temp2 != 0) {
|
|
Temp2--;
|
|
}
|
|
}
|
|
|
|
Temp2 = 0;
|
|
Row = OriginalRow;
|
|
}
|
|
//
|
|
// If this is a text op with secondary text information
|
|
//
|
|
if ((MenuOption->ThisTag->Operand == EFI_IFR_TEXT_OP) && (MenuOption->ThisTag->TextTwo != 0)) {
|
|
StringPtr = GetToken (MenuOption->ThisTag->TextTwo, MenuOption->Handle);
|
|
|
|
Width = (UINT16) gOptionBlockWidth;
|
|
|
|
OriginalRow = Row;
|
|
|
|
for (Index = 0; GetLineByWidth (StringPtr, Width, &Index, &OutputString) != 0x0000;) {
|
|
if ((Temp == 0) && (Row <= BottomRow)) {
|
|
PrintStringAt (MenuOption->OptCol, Row, OutputString);
|
|
}
|
|
//
|
|
// If there is more string to process print on the next row and increment the Skip value
|
|
//
|
|
if (StrLen (&StringPtr[Index])) {
|
|
if (Temp2 == 0) {
|
|
Row++;
|
|
//
|
|
// Since the Number of lines for this menu entry may or may not be reflected accurately
|
|
// since the prompt might be 1 lines and option might be many, and vice versa, we need to do
|
|
// some testing to ensure we are keeping this in-sync.
|
|
//
|
|
// If the difference in rows is greater than or equal to the skip value, increase the skip value
|
|
//
|
|
if ((Row - OriginalRow) >= MenuOption->Skip) {
|
|
MenuOption->Skip++;
|
|
}
|
|
}
|
|
}
|
|
|
|
gBS->FreePool (OutputString);
|
|
if (Temp2 != 0) {
|
|
Temp2--;
|
|
}
|
|
}
|
|
|
|
Row = OriginalRow;
|
|
gBS->FreePool (StringPtr);
|
|
}
|
|
} else {
|
|
//
|
|
// For now, assume left-justified 72 width max setup entries
|
|
//
|
|
PrintStringAt (Col, Row, MenuOption->Description);
|
|
}
|
|
//
|
|
// Tracker 6210 - need to handle the bottom of the display
|
|
//
|
|
if (MenuOption->Skip > 1) {
|
|
Row += MenuOption->Skip - SkipValue;
|
|
SkipValue = 0;
|
|
} else {
|
|
Row += MenuOption->Skip;
|
|
}
|
|
|
|
if (Row > BottomRow) {
|
|
if (!ValueIsScroll (FALSE, Link)) {
|
|
gDownArrow = TRUE;
|
|
}
|
|
|
|
Row = BottomRow + 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!ValueIsScroll (TRUE, TopOfScreen)) {
|
|
gUpArrow = TRUE;
|
|
}
|
|
|
|
if (gUpArrow) {
|
|
gST->ConOut->SetAttribute (gST->ConOut, ARROW_TEXT | ARROW_BACKGROUND);
|
|
PrintAt (
|
|
LocalScreen.LeftColumn + gPromptBlockWidth + gOptionBlockWidth + 1,
|
|
TopRow - SCROLL_ARROW_HEIGHT,
|
|
(CHAR16 *) L"%c",
|
|
ARROW_UP
|
|
);
|
|
gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
|
|
}
|
|
|
|
if (gDownArrow) {
|
|
gST->ConOut->SetAttribute (gST->ConOut, ARROW_TEXT | ARROW_BACKGROUND);
|
|
PrintAt (
|
|
LocalScreen.LeftColumn + gPromptBlockWidth + gOptionBlockWidth + 1,
|
|
BottomRow + SCROLL_ARROW_HEIGHT,
|
|
(CHAR16 *) L"%c",
|
|
ARROW_DOWN
|
|
);
|
|
gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
|
|
}
|
|
|
|
if (SavedMenuOption != NULL) {
|
|
MenuOption = SavedMenuOption;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case CfRefreshHighLight:
|
|
ControlFlag = CfUpdateHelpString;
|
|
//
|
|
// Repaint flag is normally reset when finish processing CfUpdateHelpString. Temporarily
|
|
// reset Repaint flag because we may break halfway and skip CfUpdateHelpString processing.
|
|
//
|
|
SavedValue = Repaint;
|
|
Repaint = FALSE;
|
|
|
|
if (NewPos != NULL) {
|
|
gST->ConOut->SetCursorPosition (gST->ConOut, MenuOption->Col, MenuOption->Row);
|
|
if (SubMenu) {
|
|
if (gLastOpr && (gEntryNumber != -1)) {
|
|
MenuOption = CR (NewPos, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
|
|
if (gEntryNumber != MenuOption->EntryNumber) {
|
|
ScreenOperation = UiDown;
|
|
ControlFlag = CfScreenOperation;
|
|
break;
|
|
} else {
|
|
gLastOpr = FALSE;
|
|
}
|
|
}
|
|
|
|
ProcessOptions (MenuOption, FALSE, FileFormTagsHead, PageData, &OptionString);
|
|
gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
|
|
if (OptionString != NULL) {
|
|
//
|
|
// If leading spaces on OptionString - remove the spaces
|
|
//
|
|
for (Index = 0; OptionString[Index] == L' '; Index++)
|
|
;
|
|
|
|
for (Count = 0; OptionString[Index] != CHAR_NULL; Index++) {
|
|
OptionString[Count] = OptionString[Index];
|
|
Count++;
|
|
}
|
|
|
|
OptionString[Count] = CHAR_NULL;
|
|
|
|
Width = (UINT16) gOptionBlockWidth;
|
|
|
|
OriginalRow = MenuOption->Row;
|
|
|
|
for (Index = 0; GetLineByWidth (OptionString, Width, &Index, &OutputString) != 0x0000;) {
|
|
if (MenuOption->Row >= TopRow && MenuOption->Row <= BottomRow) {
|
|
PrintStringAt (MenuOption->OptCol, MenuOption->Row, OutputString);
|
|
}
|
|
//
|
|
// If there is more string to process print on the next row and increment the Skip value
|
|
//
|
|
if (StrLen (&OptionString[Index])) {
|
|
MenuOption->Row++;
|
|
}
|
|
|
|
gBS->FreePool (OutputString);
|
|
}
|
|
|
|
MenuOption->Row = OriginalRow;
|
|
} else {
|
|
if (NewLine) {
|
|
if (MenuOption->ThisTag->GrayOut) {
|
|
gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_GRAYED | FIELD_BACKGROUND);
|
|
} else {
|
|
if (MenuOption->ThisTag->Operand == EFI_IFR_SUBTITLE_OP) {
|
|
gST->ConOut->SetAttribute (gST->ConOut, SUBTITLE_TEXT | FIELD_BACKGROUND);
|
|
}
|
|
}
|
|
|
|
OriginalRow = MenuOption->Row;
|
|
Width = GetWidth (MenuOption->ThisTag, MenuOption->Handle);
|
|
|
|
for (Index = 0; GetLineByWidth (MenuOption->Description, Width, &Index, &OutputString) != 0x0000;) {
|
|
if (MenuOption->Row >= TopRow && MenuOption->Row <= BottomRow) {
|
|
PrintStringAt (Col, MenuOption->Row, OutputString);
|
|
}
|
|
//
|
|
// If there is more string to process print on the next row and increment the Skip value
|
|
//
|
|
if (StrLen (&MenuOption->Description[Index])) {
|
|
MenuOption->Row++;
|
|
}
|
|
|
|
gBS->FreePool (OutputString);
|
|
}
|
|
|
|
MenuOption->Row = OriginalRow;
|
|
gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
|
|
}
|
|
}
|
|
} else {
|
|
gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
|
|
gST->ConOut->OutputString (gST->ConOut, MenuOption->Description);
|
|
}
|
|
|
|
MenuOption = CR (NewPos, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
|
|
|
|
if ((gPriorMenuEntry != 0) && (MenuOption->EntryNumber != gPriorMenuEntry) && (NewPos->ForwardLink != &Menu)) {
|
|
ScreenOperation = UiDown;
|
|
ControlFlag = CfScreenOperation;
|
|
break;
|
|
} else {
|
|
gPriorMenuEntry = 0;
|
|
}
|
|
//
|
|
// This is only possible if we entered this page and the first menu option is
|
|
// a "non-menu" item. In that case, force it UiDown
|
|
//
|
|
if (MenuOption->ThisTag->Operand == EFI_IFR_SUBTITLE_OP || MenuOption->ThisTag->GrayOut) {
|
|
//
|
|
// If we previously hit an UP command and we are still sitting on a text operation
|
|
// we must continue going up
|
|
//
|
|
if (ScreenOperation == UiUp) {
|
|
ControlFlag = CfScreenOperation;
|
|
break;
|
|
} else {
|
|
ScreenOperation = UiDown;
|
|
ControlFlag = CfScreenOperation;
|
|
break;
|
|
}
|
|
}
|
|
//
|
|
// Set reverse attribute
|
|
//
|
|
gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_HIGHLIGHT | FIELD_BACKGROUND_HIGHLIGHT);
|
|
gST->ConOut->SetCursorPosition (gST->ConOut, MenuOption->Col, MenuOption->Row);
|
|
|
|
//
|
|
// Assuming that we have a refresh linked-list created, lets annotate the
|
|
// appropriate entry that we are highlighting with its new attribute. Just prior to this
|
|
// lets reset all of the entries' attribute so we do not get multiple highlights in he refresh
|
|
//
|
|
if (gMenuRefreshHead != NULL) {
|
|
for (MenuRefreshEntry = gMenuRefreshHead; MenuRefreshEntry != NULL; MenuRefreshEntry = MenuRefreshEntry->Next) {
|
|
MenuRefreshEntry->CurrentAttribute = FIELD_TEXT | FIELD_BACKGROUND;
|
|
if (MenuRefreshEntry->MenuOption == MenuOption) {
|
|
MenuRefreshEntry->CurrentAttribute = FIELD_TEXT_HIGHLIGHT | FIELD_BACKGROUND_HIGHLIGHT;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (SubMenu) {
|
|
ProcessOptions (MenuOption, FALSE, FileFormTagsHead, PageData, &OptionString);
|
|
if (OptionString != NULL) {
|
|
//
|
|
// If leading spaces on OptionString - remove the spaces
|
|
//
|
|
for (Index = 0; OptionString[Index] == L' '; Index++)
|
|
;
|
|
|
|
for (Count = 0; OptionString[Index] != CHAR_NULL; Index++) {
|
|
OptionString[Count] = OptionString[Index];
|
|
Count++;
|
|
}
|
|
|
|
OptionString[Count] = CHAR_NULL;
|
|
|
|
Width = (UINT16) gOptionBlockWidth;
|
|
|
|
OriginalRow = MenuOption->Row;
|
|
|
|
for (Index = 0; GetLineByWidth (OptionString, Width, &Index, &OutputString) != 0x0000;) {
|
|
if (MenuOption->Row >= TopRow && MenuOption->Row <= BottomRow) {
|
|
PrintStringAt (MenuOption->OptCol, MenuOption->Row, OutputString);
|
|
}
|
|
//
|
|
// If there is more string to process print on the next row and increment the Skip value
|
|
//
|
|
if (StrLen (&OptionString[Index])) {
|
|
MenuOption->Row++;
|
|
}
|
|
|
|
gBS->FreePool (OutputString);
|
|
}
|
|
|
|
MenuOption->Row = OriginalRow;
|
|
} else {
|
|
if (NewLine) {
|
|
OriginalRow = MenuOption->Row;
|
|
|
|
Width = GetWidth (MenuOption->ThisTag, MenuOption->Handle);
|
|
|
|
for (Index = 0; GetLineByWidth (MenuOption->Description, Width, &Index, &OutputString) != 0x0000;) {
|
|
if (MenuOption->Row >= TopRow && MenuOption->Row <= BottomRow) {
|
|
PrintStringAt (Col, MenuOption->Row, OutputString);
|
|
}
|
|
//
|
|
// If there is more string to process print on the next row and increment the Skip value
|
|
//
|
|
if (StrLen (&MenuOption->Description[Index])) {
|
|
MenuOption->Row++;
|
|
}
|
|
|
|
gBS->FreePool (OutputString);
|
|
}
|
|
|
|
MenuOption->Row = OriginalRow;
|
|
|
|
}
|
|
}
|
|
|
|
if (((NewPos->ForwardLink != &Menu) && (ScreenOperation == UiDown)) ||
|
|
((NewPos->BackLink != &Menu) && (ScreenOperation == UiUp)) ||
|
|
(ScreenOperation == UiNoOperation)
|
|
) {
|
|
UpdateKeyHelp (MenuOption, FALSE);
|
|
}
|
|
} else {
|
|
gST->ConOut->OutputString (gST->ConOut, MenuOption->Description);
|
|
}
|
|
//
|
|
// Clear reverse attribute
|
|
//
|
|
gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
|
|
}
|
|
//
|
|
// Repaint flag will be used when process CfUpdateHelpString, so restore its value
|
|
// if we didn't break halfway when process CfRefreshHighLight.
|
|
//
|
|
Repaint = SavedValue;
|
|
break;
|
|
|
|
case CfUpdateHelpString:
|
|
ControlFlag = CfPrepareToReadKey;
|
|
|
|
if (SubMenu &&
|
|
(Repaint || NewLine ||
|
|
(MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) ||
|
|
(MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)) &&
|
|
!(gClassOfVfr == EFI_GENERAL_APPLICATION_SUBCLASS)) {
|
|
//
|
|
// Don't print anything if it is a NULL help token
|
|
//
|
|
if (MenuOption->ThisTag->Help == 0x00000000) {
|
|
StringPtr = (CHAR16 *) L"\0";
|
|
} else {
|
|
StringPtr = GetToken (MenuOption->ThisTag->Help, MenuOption->Handle);
|
|
}
|
|
|
|
ProcessHelpString (StringPtr, &FormattedString, BottomRow - TopRow);
|
|
|
|
gST->ConOut->SetAttribute (gST->ConOut, HELP_TEXT | FIELD_BACKGROUND);
|
|
|
|
for (Index = 0; Index < BottomRow - TopRow; Index++) {
|
|
//
|
|
// Pad String with spaces to simulate a clearing of the previous line
|
|
//
|
|
for (; GetStringWidth (&FormattedString[Index * gHelpBlockWidth]) / 2 < gHelpBlockWidth;) {
|
|
StrCat (&FormattedString[Index * gHelpBlockWidth], (CHAR16 *) L" ");
|
|
}
|
|
|
|
PrintStringAt (
|
|
LocalScreen.RightColumn - gHelpBlockWidth,
|
|
Index + TopRow,
|
|
&FormattedString[Index * gHelpBlockWidth]
|
|
);
|
|
}
|
|
}
|
|
//
|
|
// Reset this flag every time we finish using it.
|
|
//
|
|
Repaint = FALSE;
|
|
NewLine = FALSE;
|
|
break;
|
|
|
|
case CfPrepareToReadKey:
|
|
ControlFlag = CfReadKey;
|
|
|
|
for (Index = 0; Index < MenuOption->IfrNumber; Index++) {
|
|
FileFormTags = FileFormTags->NextFile;
|
|
}
|
|
|
|
ScreenOperation = UiNoOperation;
|
|
|
|
Status = gBS->HandleProtocol (
|
|
(VOID *) (UINTN) FileFormTags->FormTags.Tags[0].CallbackHandle,
|
|
&gEfiFormCallbackProtocolGuid,
|
|
(VOID **) &FormCallback
|
|
);
|
|
|
|
break;
|
|
|
|
case CfReadKey:
|
|
ControlFlag = CfScreenOperation;
|
|
|
|
OriginalTimeOut = FrontPageTimeOutValue;
|
|
do {
|
|
if (FrontPageTimeOutValue >= 0 && (gClassOfVfr == EFI_FRONT_PAGE_SUBCLASS) && FrontPageTimeOutValue != (INT16) -1) {
|
|
//
|
|
// Remember that if set to 0, must immediately boot an option
|
|
//
|
|
if (FrontPageTimeOutValue == 0) {
|
|
FrontPageTimeOutValue = 0xFFFF;
|
|
Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
|
|
if (EFI_ERROR (Status)) {
|
|
Status = EFI_TIMEOUT;
|
|
}
|
|
break;
|
|
}
|
|
|
|
Status = UiWaitForSingleEvent (gST->ConIn->WaitForKey, ONE_SECOND);
|
|
if (Status == EFI_TIMEOUT) {
|
|
EFI_IFR_DATA_ENTRY *DataEntry;
|
|
|
|
DataEntry = (EFI_IFR_DATA_ENTRY *) (PageData + 1);
|
|
|
|
PageData->EntryCount = 1;
|
|
Count = (UINT32) ((OriginalTimeOut - FrontPageTimeOutValue) * 100 / OriginalTimeOut);
|
|
CopyMem (&DataEntry->Data, &Count, sizeof (UINT32));
|
|
|
|
if ((FormCallback != NULL) && (FormCallback->Callback != NULL)) {
|
|
FormCallback->Callback (
|
|
FormCallback,
|
|
0xFFFF,
|
|
(EFI_IFR_DATA_ARRAY *) PageData,
|
|
NULL
|
|
);
|
|
}
|
|
//
|
|
// Count down 1 second
|
|
//
|
|
FrontPageTimeOutValue--;
|
|
|
|
} else {
|
|
ASSERT (!EFI_ERROR (Status));
|
|
PageData->EntryCount = 0;
|
|
if ((FormCallback != NULL) && (FormCallback->Callback != NULL)) {
|
|
FormCallback->Callback (
|
|
FormCallback,
|
|
0xFFFE,
|
|
(EFI_IFR_DATA_ARRAY *) PageData,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
FrontPageTimeOutValue = 0xFFFF;
|
|
}
|
|
} else {
|
|
//
|
|
// Wait for user's selection, no auto boot
|
|
//
|
|
Status = UiWaitForSingleEvent (gST->ConIn->WaitForKey, 0);
|
|
}
|
|
} while (Status == EFI_TIMEOUT);
|
|
|
|
if (gFirstIn) {
|
|
gFirstIn = FALSE;
|
|
gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
|
|
DisableQuietBoot ();
|
|
}
|
|
|
|
if (Status == EFI_TIMEOUT) {
|
|
Key.UnicodeChar = CHAR_CARRIAGE_RETURN;
|
|
} else {
|
|
Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
|
|
//
|
|
// if we encounter error, continue to read another key in.
|
|
//
|
|
if (EFI_ERROR (Status)) {
|
|
ControlFlag = CfReadKey;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
switch (Key.UnicodeChar) {
|
|
case CHAR_CARRIAGE_RETURN:
|
|
Selection = MenuOption;
|
|
ScreenOperation = UiSelect;
|
|
gDirection = 0;
|
|
break;
|
|
|
|
//
|
|
// We will push the adjustment of these numeric values directly to the input handler
|
|
//
|
|
case '+':
|
|
case '-':
|
|
if ((MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) || (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)) {
|
|
|
|
if (Key.UnicodeChar == '+') {
|
|
gDirection = SCAN_RIGHT;
|
|
} else {
|
|
gDirection = SCAN_LEFT;
|
|
}
|
|
|
|
Status = ProcessOptions (MenuOption, TRUE, FileFormTagsHead, NULL, &OptionString);
|
|
}
|
|
break;
|
|
|
|
case '^':
|
|
ScreenOperation = UiUp;
|
|
break;
|
|
|
|
case 'V':
|
|
case 'v':
|
|
ScreenOperation = UiDown;
|
|
break;
|
|
|
|
case ' ':
|
|
if (gClassOfVfr != EFI_FRONT_PAGE_SUBCLASS) {
|
|
if (SubMenu) {
|
|
if (MenuOption->ThisTag->Operand == EFI_IFR_CHECKBOX_OP && !(MenuOption->ThisTag->GrayOut)) {
|
|
gST->ConOut->SetCursorPosition (gST->ConOut, MenuOption->Col, MenuOption->Row);
|
|
gST->ConOut->OutputString (gST->ConOut, MenuOption->Description);
|
|
Selection = MenuOption;
|
|
ScreenOperation = UiSelect;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case CHAR_NULL:
|
|
if (((Key.ScanCode == SCAN_F1) && ((gFunctionKeySetting & FUNCTION_ONE) != FUNCTION_ONE)) ||
|
|
((Key.ScanCode == SCAN_F2) && ((gFunctionKeySetting & FUNCTION_TWO) != FUNCTION_TWO)) ||
|
|
((Key.ScanCode == SCAN_F9) && ((gFunctionKeySetting & FUNCTION_NINE) != FUNCTION_NINE)) ||
|
|
((Key.ScanCode == SCAN_F10) && ((gFunctionKeySetting & FUNCTION_TEN) != FUNCTION_TEN))
|
|
) {
|
|
//
|
|
// If the function key has been disabled, just ignore the key.
|
|
//
|
|
} else {
|
|
for (Index = 0; Index < sizeof (gScanCodeToOperation) / sizeof (gScanCodeToOperation[0]); Index++) {
|
|
if (Key.ScanCode == gScanCodeToOperation[Index].ScanCode) {
|
|
if ((Key.ScanCode == SCAN_F9) || (Key.ScanCode == SCAN_F10)) {
|
|
if (SubMenu) {
|
|
ScreenOperation = gScanCodeToOperation[Index].ScreenOperation;
|
|
}
|
|
} else {
|
|
ScreenOperation = gScanCodeToOperation[Index].ScreenOperation;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case CfScreenOperation:
|
|
IfrBinary = gBinaryDataHead;
|
|
|
|
//
|
|
// Advance to the Ifr we are using
|
|
//
|
|
for (Index = 0; Index < gActiveIfr; Index++) {
|
|
IfrBinary = IfrBinary->Next;
|
|
}
|
|
|
|
if (ScreenOperation != UiPrevious && ScreenOperation != UiReset) {
|
|
//
|
|
// If the screen has no menu items, and the user didn't select UiPrevious, or UiReset
|
|
// ignore the selection and go back to reading keys.
|
|
//
|
|
if (IsListEmpty (&Menu)) {
|
|
ControlFlag = CfReadKey;
|
|
break;
|
|
}
|
|
//
|
|
// if there is nothing logical to place a cursor on, just move on to wait for a key.
|
|
//
|
|
for (Link = Menu.ForwardLink; Link != &Menu; Link = Link->ForwardLink) {
|
|
NextMenuOption = CR (Link, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
|
|
if (!(NextMenuOption->ThisTag->GrayOut) && (NextMenuOption->ThisTag->Operand != EFI_IFR_SUBTITLE_OP)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Link == &Menu) {
|
|
ControlFlag = CfPrepareToReadKey;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (Index = 0;
|
|
Index < sizeof (gScreenOperationToControlFlag) / sizeof (gScreenOperationToControlFlag[0]);
|
|
Index++
|
|
) {
|
|
if (ScreenOperation == gScreenOperationToControlFlag[Index].ScreenOperation) {
|
|
ControlFlag = gScreenOperationToControlFlag[Index].ControlFlag;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case CfUiPrevious:
|
|
ControlFlag = CfCheckSelection;
|
|
//
|
|
// Check for tags that might have LATE_CHECK enabled. If they do, we can't switch pages or save NV data.
|
|
//
|
|
if (MenuOption != NULL) {
|
|
if (!SelectionsAreValid (MenuOption, FileFormTagsHead)) {
|
|
Selection = NULL;
|
|
Repaint = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (IsListEmpty (&gMenuList)) {
|
|
Selection = NULL;
|
|
if (IsListEmpty (&Menu)) {
|
|
ControlFlag = CfReadKey;
|
|
}
|
|
break;
|
|
}
|
|
|
|
gLastOpr = TRUE;
|
|
|
|
while (gMenuRefreshHead != NULL) {
|
|
OldMenuRefreshEntry = gMenuRefreshHead->Next;
|
|
|
|
gBS->FreePool (gMenuRefreshHead);
|
|
|
|
gMenuRefreshHead = OldMenuRefreshEntry;
|
|
}
|
|
//
|
|
// Remove the Cached page entry, free and init the menus, flag Selection as jumping to previous page and a valid Tag
|
|
//
|
|
if (SubMenu) {
|
|
UiRemoveMenuListEntry (MenuOption, &Selection);
|
|
Selection->Previous = TRUE;
|
|
UiFreeMenu ();
|
|
UiInitMenu ();
|
|
}
|
|
|
|
gActiveIfr = Selection->IfrNumber;
|
|
return Selection;
|
|
|
|
case CfUiSelect:
|
|
ControlFlag = CfCheckSelection;
|
|
|
|
ExtractRequestedNvMap (FileFormTags, MenuOption->ThisTag->VariableNumber, &VariableDefinition);
|
|
|
|
if (SubMenu) {
|
|
if ((MenuOption->ThisTag->Operand == EFI_IFR_TEXT_OP &&
|
|
!(MenuOption->ThisTag->Flags & EFI_IFR_FLAG_INTERACTIVE)) ||
|
|
(MenuOption->ThisTag->GrayOut) ||
|
|
(MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) ||
|
|
(MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)) {
|
|
Selection = NULL;
|
|
break;
|
|
}
|
|
|
|
NewLine = TRUE;
|
|
UpdateKeyHelp (MenuOption, TRUE);
|
|
Status = ProcessOptions (MenuOption, TRUE, FileFormTagsHead, PageData, &OptionString);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
Selection = NULL;
|
|
Repaint = TRUE;
|
|
break;
|
|
}
|
|
|
|
if (OptionString != NULL) {
|
|
PrintStringAt (LocalScreen.LeftColumn + gPromptBlockWidth + 1, MenuOption->Row, OptionString);
|
|
}
|
|
|
|
if (MenuOption->ThisTag->Flags & EFI_IFR_FLAG_INTERACTIVE) {
|
|
Selection = MenuOption;
|
|
}
|
|
|
|
if (Selection == NULL) {
|
|
break;
|
|
}
|
|
|
|
Location = (UINT8 *) &PageData->EntryCount;
|
|
|
|
//
|
|
// If not a goto, dump single piece of data, otherwise dump everything
|
|
//
|
|
if (Selection->ThisTag->Operand == EFI_IFR_REF_OP) {
|
|
//
|
|
// Check for tags that might have LATE_CHECK enabled. If they do, we can't switch pages or save NV data.
|
|
//
|
|
if (!SelectionsAreValid (MenuOption, FileFormTagsHead)) {
|
|
Selection = NULL;
|
|
Repaint = TRUE;
|
|
break;
|
|
}
|
|
|
|
UiAddMenuListEntry (Selection);
|
|
gPriorMenuEntry = 0;
|
|
|
|
//
|
|
// Now that we added a menu entry specific to a goto, we can always go back when someone hits the UiPrevious
|
|
//
|
|
UiMenuList = CR (gMenuList.ForwardLink, UI_MENU_LIST, MenuLink, UI_MENU_LIST_SIGNATURE);
|
|
UiMenuList->FormerEntryNumber = MenuOption->EntryNumber;
|
|
|
|
gLastOpr = FALSE;
|
|
|
|
//
|
|
// Rewind to the beginning of the menu
|
|
//
|
|
for (; NewPos->BackLink != &Menu; NewPos = NewPos->BackLink)
|
|
;
|
|
|
|
//
|
|
// Get Total Count of Menu entries
|
|
//
|
|
for (Count = 1; NewPos->ForwardLink != &Menu; NewPos = NewPos->ForwardLink) {
|
|
Count++;
|
|
}
|
|
//
|
|
// Rewind to the beginning of the menu
|
|
//
|
|
for (; NewPos->BackLink != &Menu; NewPos = NewPos->BackLink)
|
|
;
|
|
|
|
//
|
|
// Copy the number of entries being described to the PageData location
|
|
//
|
|
CopyMem (&Location[0], &Count, sizeof (UINT32));
|
|
|
|
for (Index = 4; NewPos->ForwardLink != &Menu; Index = Index + MenuOption->ThisTag->StorageWidth + 2) {
|
|
|
|
MenuOption = CR (NewPos, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
|
|
Location[Index] = MenuOption->ThisTag->Operand;
|
|
Location[Index + 1] = (UINT8) (MenuOption->ThisTag->StorageWidth + 4);
|
|
CopyMem (
|
|
&Location[Index + 4],
|
|
&VariableDefinition->NvRamMap[MenuOption->ThisTag->StorageStart],
|
|
MenuOption->ThisTag->StorageWidth
|
|
);
|
|
NewPos = NewPos->ForwardLink;
|
|
}
|
|
} else {
|
|
|
|
gPriorMenuEntry = MenuOption->EntryNumber;
|
|
|
|
Count = 1;
|
|
|
|
//
|
|
// Copy the number of entries being described to the PageData location
|
|
//
|
|
CopyMem (&Location[0], &Count, sizeof (UINT32));
|
|
|
|
//
|
|
// Start at PageData[4] since the EntryCount is a UINT32
|
|
//
|
|
Index = 4;
|
|
|
|
//
|
|
// Copy data to destination
|
|
//
|
|
Location[Index] = MenuOption->ThisTag->Operand;
|
|
Location[Index + 1] = (UINT8) (MenuOption->ThisTag->StorageWidth + 4);
|
|
CopyMem (
|
|
&Location[Index + 4],
|
|
&VariableDefinition->NvRamMap[MenuOption->ThisTag->StorageStart],
|
|
MenuOption->ThisTag->StorageWidth
|
|
);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case CfUiReset:
|
|
ControlFlag = CfCheckSelection;
|
|
gLastOpr = FALSE;
|
|
if (gClassOfVfr == EFI_FRONT_PAGE_SUBCLASS) {
|
|
break;
|
|
}
|
|
//
|
|
// If NV flag is up, prompt user
|
|
//
|
|
if (gNvUpdateRequired) {
|
|
Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
|
|
|
|
YesResponse = gYesResponse[0];
|
|
NoResponse = gNoResponse[0];
|
|
|
|
do {
|
|
CreateDialog (3, TRUE, 0, NULL, &Key, gEmptyString, gAreYouSure, gEmptyString);
|
|
} while
|
|
(
|
|
(Key.ScanCode != SCAN_ESC) &&
|
|
((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) != (NoResponse | UPPER_LOWER_CASE_OFFSET)) &&
|
|
((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) != (YesResponse | UPPER_LOWER_CASE_OFFSET))
|
|
);
|
|
|
|
//
|
|
// If the user hits the YesResponse key
|
|
//
|
|
if ((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) == (YesResponse | UPPER_LOWER_CASE_OFFSET)) {
|
|
} else {
|
|
Repaint = TRUE;
|
|
NewLine = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
//
|
|
// Check for tags that might have LATE_CHECK enabled. If they do, we can't switch pages or save NV data.
|
|
//
|
|
if (MenuOption != NULL) {
|
|
if (!SelectionsAreValid (MenuOption, FileFormTagsHead)) {
|
|
Selection = NULL;
|
|
Repaint = TRUE;
|
|
NewLine = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
|
|
gST->ConOut->EnableCursor (gST->ConOut, TRUE);
|
|
|
|
if (SubMenu) {
|
|
UiFreeMenuList ();
|
|
gST->ConOut->ClearScreen (gST->ConOut);
|
|
return NULL;
|
|
}
|
|
|
|
UpdateStatusBar (INPUT_ERROR, MenuOption->ThisTag->Flags, FALSE);
|
|
UpdateStatusBar (NV_UPDATE_REQUIRED, MenuOption->ThisTag->Flags, FALSE);
|
|
|
|
if (IfrBinary->UnRegisterOnExit) {
|
|
Hii->RemovePack (Hii, MenuOption->Handle);
|
|
}
|
|
|
|
UiFreeMenu ();
|
|
|
|
//
|
|
// Clean up the allocated data buffers
|
|
//
|
|
FreeData (FileFormTagsHead, FormattedString, OptionString);
|
|
|
|
gST->ConOut->ClearScreen (gST->ConOut);
|
|
return NULL;
|
|
|
|
case CfUiLeft:
|
|
ControlFlag = CfCheckSelection;
|
|
if ((MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) || (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)) {
|
|
if (MenuOption->Skip == 1) {
|
|
//
|
|
// In the tail of the Date/Time op-code set, go left.
|
|
//
|
|
NewPos = NewPos->BackLink;
|
|
} else {
|
|
//
|
|
// In the middle of the Data/Time op-code set, go left.
|
|
//
|
|
NextMenuOption = CR (NewPos->ForwardLink, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
|
|
if (NextMenuOption->Skip == 1) {
|
|
NewPos = NewPos->BackLink;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case CfUiRight:
|
|
ControlFlag = CfCheckSelection;
|
|
if ((MenuOption->Skip == 0) &&
|
|
((MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) || (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP))
|
|
) {
|
|
//
|
|
// We are in the head or middle of the Date/Time op-code set, advance right.
|
|
//
|
|
NewPos = NewPos->ForwardLink;
|
|
}
|
|
break;
|
|
|
|
case CfUiUp:
|
|
ControlFlag = CfCheckSelection;
|
|
|
|
if (NewPos->BackLink != &Menu) {
|
|
NewLine = TRUE;
|
|
//
|
|
// Adjust Date/Time position before we advance forward.
|
|
//
|
|
AdjustDateAndTimePosition (TRUE, &NewPos);
|
|
|
|
//
|
|
// Caution that we have already rewind to the top, don't go backward in this situation.
|
|
//
|
|
if (NewPos->BackLink != &Menu) {
|
|
NewPos = NewPos->BackLink;
|
|
}
|
|
|
|
PreviousMenuOption = CR (NewPos, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
|
|
|
|
//
|
|
// Since the behavior of hitting the up arrow on a Date/Time op-code is intended
|
|
// to be one that back to the previous set of op-codes, we need to advance to the sencond
|
|
// Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate
|
|
// checking can be done.
|
|
//
|
|
DataAndTimeLineNumberPad = AdjustDateAndTimePosition (TRUE, &NewPos);
|
|
|
|
if (SubMenu) {
|
|
//
|
|
// If the previous MenuOption contains a display-only op-code, skip to the next one
|
|
//
|
|
if (PreviousMenuOption->ThisTag->Operand == EFI_IFR_SUBTITLE_OP || PreviousMenuOption->ThisTag->GrayOut) {
|
|
//
|
|
// This is ok as long as not at the end of the list
|
|
//
|
|
if (NewPos->BackLink == &Menu) {
|
|
//
|
|
// If we are at the start of the list, then this list must start with a display only
|
|
// piece of data, so do not allow the backward motion
|
|
//
|
|
ScreenOperation = UiDown;
|
|
|
|
if (PreviousMenuOption->Row <= TopRow) {
|
|
if (TopOfScreen->BackLink != &Menu) {
|
|
TopOfScreen = TopOfScreen->BackLink;
|
|
Repaint = TRUE;
|
|
}
|
|
}
|
|
|
|
UpdateStatusBar (INPUT_ERROR, PreviousMenuOption->ThisTag->Flags, FALSE);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// Check the previous menu entry to see if it was a zero-length advance. If it was,
|
|
// don't worry about a redraw.
|
|
//
|
|
if ((MenuOption->Row - PreviousMenuOption->Skip - DataAndTimeLineNumberPad < TopRow) ||
|
|
(PreviousMenuOption->Skip > MenuOption->Row)
|
|
) {
|
|
do {
|
|
if (TopOfScreen->BackLink == &Menu) {
|
|
break;
|
|
}
|
|
|
|
Repaint = TRUE;
|
|
|
|
//
|
|
// Is the current top of screen a zero-advance op-code?
|
|
// If so, keep moving forward till we hit a >0 advance op-code
|
|
//
|
|
SavedMenuOption = CR (TopOfScreen->BackLink, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
|
|
TopOfScreen = TopOfScreen->BackLink;
|
|
} while (SavedMenuOption->Skip == 0);
|
|
//
|
|
// If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
|
|
//
|
|
AdjustDateAndTimePosition (TRUE, &TopOfScreen);
|
|
}
|
|
|
|
UpdateStatusBar (INPUT_ERROR, MenuOption->ThisTag->Flags, FALSE);
|
|
} else {
|
|
if (SubMenu) {
|
|
SavedMenuOption = MenuOption;
|
|
MenuOption = CR (NewPos, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
|
|
if (MenuOption->ThisTag->Operand == EFI_IFR_SUBTITLE_OP || MenuOption->ThisTag->GrayOut) {
|
|
//
|
|
// If we are at the end of the list and sitting on a text op, we need to more forward
|
|
//
|
|
ScreenOperation = UiDown;
|
|
ControlFlag = CfScreenOperation;
|
|
break;
|
|
}
|
|
|
|
MenuOption = SavedMenuOption;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case CfUiPageUp:
|
|
ControlFlag = CfCheckSelection;
|
|
|
|
SavedListEntry = NewPos;
|
|
Link = TopOfScreen;
|
|
for (Index = BottomRow; Index >= TopRow + 1; Index -= MenuOption->Skip) {
|
|
if (Link->BackLink == &Menu) {
|
|
TopOfScreen = Link;
|
|
Link = SavedListEntry;
|
|
MenuOption = CR (Link, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
|
|
break;
|
|
}
|
|
|
|
NewLine = TRUE;
|
|
Repaint = TRUE;
|
|
Link = Link->BackLink;
|
|
MenuOption = CR (Link, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
|
|
TopOfScreen = Link;
|
|
SavedListEntry = Link;
|
|
}
|
|
|
|
NewPos = Link;
|
|
|
|
//
|
|
// If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
|
|
// Don't do this when we are already in the first page.
|
|
//
|
|
if (Repaint) {
|
|
AdjustDateAndTimePosition (TRUE, &TopOfScreen);
|
|
AdjustDateAndTimePosition (TRUE, &NewPos);
|
|
MenuOption = CR (NewPos, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
|
|
}
|
|
break;
|
|
|
|
case CfUiPageDown:
|
|
ControlFlag = CfCheckSelection;
|
|
|
|
SavedListEntry = NewPos;
|
|
Link = TopOfScreen;
|
|
NewPos = TopOfScreen;
|
|
for (Index = TopRow; Index <= BottomRow - 1; Index += MenuOption->Skip) {
|
|
if (NewPos->ForwardLink == &Menu) {
|
|
NewPos = SavedListEntry;
|
|
MenuOption = CR (NewPos, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
|
|
Link = TopOfScreen;
|
|
NewLine = FALSE;
|
|
Repaint = FALSE;
|
|
break;
|
|
}
|
|
|
|
NewLine = TRUE;
|
|
Repaint = TRUE;
|
|
MenuOption = CR (NewPos, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
|
|
NewPos = NewPos->ForwardLink;
|
|
Link = NewPos;
|
|
}
|
|
|
|
TopOfScreen = Link;
|
|
|
|
//
|
|
// If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
|
|
// Don't do this when we are already in the last page.
|
|
//
|
|
if (Repaint) {
|
|
AdjustDateAndTimePosition (TRUE, &TopOfScreen);
|
|
AdjustDateAndTimePosition (TRUE, &NewPos);
|
|
MenuOption = CR (NewPos, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
|
|
}
|
|
break;
|
|
|
|
case CfUiDown:
|
|
ControlFlag = CfCheckSelection;
|
|
//
|
|
// Since the behavior of hitting the down arrow on a Date/Time op-code is intended
|
|
// to be one that progresses to the next set of op-codes, we need to advance to the last
|
|
// Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate
|
|
// checking can be done. The only other logic we need to introduce is that if a Date/Time
|
|
// op-code is the last entry in the menu, we need to rewind back to the first op-code of
|
|
// the Date/Time op-code.
|
|
//
|
|
DataAndTimeLineNumberPad = AdjustDateAndTimePosition (FALSE, &NewPos);
|
|
|
|
if (NewPos->ForwardLink != &Menu) {
|
|
NewLine = TRUE;
|
|
NewPos = NewPos->ForwardLink;
|
|
NextMenuOption = CR (NewPos, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
|
|
|
|
if (SubMenu) {
|
|
//
|
|
// If the next MenuOption contains a display-only op-code, skip to the next one
|
|
// Also if the next MenuOption is date or time,
|
|
//
|
|
if (NextMenuOption->ThisTag->Operand == EFI_IFR_SUBTITLE_OP || NextMenuOption->ThisTag->GrayOut) {
|
|
//
|
|
// This is ok as long as not at the end of the list
|
|
//
|
|
if (NewPos == &Menu) {
|
|
//
|
|
// If we are at the end of the list, then this list must end with a display only
|
|
// piece of data, so do not allow the forward motion
|
|
//
|
|
UpdateStatusBar (INPUT_ERROR, NextMenuOption->ThisTag->Flags, FALSE);
|
|
NewPos = NewPos->BackLink;
|
|
ScreenOperation = UiUp;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// An option might be multi-line, so we need to reflect that data in the overall skip value
|
|
//
|
|
UpdateOptionSkipLines (PageData, NextMenuOption, FileFormTagsHead, &OptionString, SkipValue);
|
|
|
|
if (NextMenuOption->Skip > 1) {
|
|
Temp = MenuOption->Row + MenuOption->Skip + NextMenuOption->Skip - 1;
|
|
} else {
|
|
Temp = MenuOption->Row + MenuOption->Skip + DataAndTimeLineNumberPad;
|
|
}
|
|
//
|
|
// If we are going to scroll
|
|
//
|
|
if (Temp > BottomRow) {
|
|
do {
|
|
//
|
|
// Is the current top of screen a zero-advance op-code?
|
|
// If so, keep moving forward till we hit a >0 advance op-code
|
|
//
|
|
SavedMenuOption = CR (TopOfScreen, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
|
|
|
|
//
|
|
// If bottom op-code is more than one line or top op-code is more than one line
|
|
//
|
|
if ((NextMenuOption->Skip > 1) || (MenuOption->Skip > 1)) {
|
|
//
|
|
// Is the bottom op-code greater than or equal in size to the top op-code?
|
|
//
|
|
if ((Temp - BottomRow) >= (SavedMenuOption->Skip - OldSkipValue)) {
|
|
//
|
|
// Skip the top op-code
|
|
//
|
|
TopOfScreen = TopOfScreen->ForwardLink;
|
|
Difference = (Temp - BottomRow) - (SavedMenuOption->Skip - OldSkipValue);
|
|
|
|
OldSkipValue = Difference;
|
|
|
|
SavedMenuOption = CR (TopOfScreen, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
|
|
|
|
//
|
|
// If we have a remainder, skip that many more op-codes until we drain the remainder
|
|
//
|
|
for (;
|
|
Difference >= (INTN) SavedMenuOption->Skip;
|
|
Difference = Difference - (INTN) SavedMenuOption->Skip
|
|
) {
|
|
//
|
|
// Since the Difference is greater than or equal to this op-code's skip value, skip it
|
|
//
|
|
TopOfScreen = TopOfScreen->ForwardLink;
|
|
SavedMenuOption = CR (TopOfScreen, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
|
|
if (Difference < (INTN) SavedMenuOption->Skip) {
|
|
Difference = SavedMenuOption->Skip - Difference - 1;
|
|
break;
|
|
} else {
|
|
if (Difference == (INTN) SavedMenuOption->Skip) {
|
|
TopOfScreen = TopOfScreen->ForwardLink;
|
|
SavedMenuOption = CR (TopOfScreen, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
|
|
Difference = SavedMenuOption->Skip - Difference;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// Since we will act on this op-code in the next routine, and increment the
|
|
// SkipValue, set the skips to one less than what is required.
|
|
//
|
|
SkipValue = Difference - 1;
|
|
|
|
} else {
|
|
//
|
|
// Since we will act on this op-code in the next routine, and increment the
|
|
// SkipValue, set the skips to one less than what is required.
|
|
//
|
|
SkipValue = OldSkipValue + (Temp - BottomRow) - 1;
|
|
}
|
|
} else {
|
|
if ((OldSkipValue + 1) == (INTN) SavedMenuOption->Skip) {
|
|
TopOfScreen = TopOfScreen->ForwardLink;
|
|
break;
|
|
} else {
|
|
SkipValue = OldSkipValue;
|
|
}
|
|
}
|
|
//
|
|
// If the op-code at the top of the screen is more than one line, let's not skip it yet
|
|
// Let's set a skip flag to smoothly scroll the top of the screen.
|
|
//
|
|
if (SavedMenuOption->Skip > 1) {
|
|
if (SavedMenuOption == NextMenuOption) {
|
|
SkipValue = 0;
|
|
} else {
|
|
SkipValue++;
|
|
}
|
|
} else {
|
|
SkipValue = 0;
|
|
TopOfScreen = TopOfScreen->ForwardLink;
|
|
}
|
|
} while (SavedMenuOption->Skip == 0);
|
|
|
|
Repaint = TRUE;
|
|
OldSkipValue = SkipValue;
|
|
}
|
|
|
|
UpdateStatusBar (INPUT_ERROR, MenuOption->ThisTag->Flags, FALSE);
|
|
|
|
} else {
|
|
if (SubMenu) {
|
|
SavedMenuOption = MenuOption;
|
|
MenuOption = CR (NewPos, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
|
|
if (MenuOption->ThisTag->Operand == EFI_IFR_SUBTITLE_OP || MenuOption->ThisTag->GrayOut) {
|
|
//
|
|
// If we are at the end of the list and sitting on a text op, we need to more forward
|
|
//
|
|
ScreenOperation = UiUp;
|
|
ControlFlag = CfScreenOperation;
|
|
break;
|
|
}
|
|
|
|
MenuOption = SavedMenuOption;
|
|
//
|
|
// If we are at the end of the list and sitting on a Date/Time op, rewind to the head.
|
|
//
|
|
AdjustDateAndTimePosition (TRUE, &NewPos);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case CfUiSave:
|
|
ControlFlag = CfCheckSelection;
|
|
//
|
|
// Check for tags that might have LATE_CHECK enabled. If they do, we can't switch pages or save NV data.
|
|
//
|
|
if (MenuOption != NULL) {
|
|
if (!SelectionsAreValid (MenuOption, FileFormTagsHead)) {
|
|
Selection = NULL;
|
|
Repaint = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
//
|
|
// If callbacks are active, and the callback has a Write method, try to use it
|
|
//
|
|
if (FileFormTags->VariableDefinitions->VariableName == NULL) {
|
|
if ((FormCallback != NULL) && (FormCallback->NvWrite != NULL)) {
|
|
Status = FormCallback->NvWrite (
|
|
FormCallback,
|
|
(CHAR16 *) L"Setup",
|
|
&FileFormTags->FormTags.Tags[0].GuidValue,
|
|
EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
|
|
VariableDefinition->VariableSize,
|
|
(VOID *) VariableDefinition->NvRamMap,
|
|
&gResetRequired
|
|
);
|
|
|
|
} else {
|
|
Status = gRT->SetVariable (
|
|
(CHAR16 *) L"Setup",
|
|
&FileFormTags->FormTags.Tags[0].GuidValue,
|
|
EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
|
|
VariableDefinition->VariableSize,
|
|
(VOID *) VariableDefinition->NvRamMap
|
|
);
|
|
}
|
|
} else {
|
|
VariableDefinition = FileFormTags->VariableDefinitions;
|
|
|
|
for (; VariableDefinition != NULL; VariableDefinition = VariableDefinition->Next) {
|
|
if ((FormCallback != NULL) && (FormCallback->NvWrite != NULL)) {
|
|
Status = FormCallback->NvWrite (
|
|
FormCallback,
|
|
VariableDefinition->VariableName,
|
|
&VariableDefinition->Guid,
|
|
EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
|
|
VariableDefinition->VariableSize,
|
|
(VOID *) VariableDefinition->NvRamMap,
|
|
&gResetRequired
|
|
);
|
|
|
|
} else {
|
|
Status = gRT->SetVariable (
|
|
VariableDefinition->VariableName,
|
|
&VariableDefinition->Guid,
|
|
EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
|
|
VariableDefinition->VariableSize,
|
|
(VOID *) VariableDefinition->NvRamMap
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
UpdateStatusBar (INPUT_ERROR, MenuOption->ThisTag->Flags, FALSE);
|
|
UpdateStatusBar (NV_UPDATE_REQUIRED, MenuOption->ThisTag->Flags, FALSE);
|
|
break;
|
|
|
|
case CfUiDefault:
|
|
ControlFlag = CfCheckSelection;
|
|
|
|
NvMapListHead = NULL;
|
|
|
|
Status = Hii->GetDefaultImage (Hii, MenuOption->Handle, EFI_IFR_FLAG_DEFAULT, &NvMapListHead);
|
|
|
|
if (!EFI_ERROR (Status)) {
|
|
ASSERT_EFI_ERROR (NULL != NvMapListHead);
|
|
|
|
NvMapListNode = NvMapListHead;
|
|
|
|
while (NULL != NvMapListNode) {
|
|
if (FileFormTags->VariableDefinitions->VariableId == NvMapListNode->VariablePack->VariableId) {
|
|
NvMap = (VOID *) ((CHAR8 *) NvMapListNode->VariablePack + sizeof (EFI_HII_VARIABLE_PACK) + NvMapListNode->VariablePack->VariableNameLength);
|
|
NvMapSize = NvMapListNode->VariablePack->Header.Length - sizeof (EFI_HII_VARIABLE_PACK) - NvMapListNode->VariablePack->VariableNameLength;
|
|
break;
|
|
}
|
|
NvMapListNode = NvMapListNode->NextVariablePack;
|
|
}
|
|
|
|
//
|
|
// Free the buffer that was allocated.
|
|
//
|
|
gBS->FreePool (FileFormTags->VariableDefinitions->NvRamMap);
|
|
gBS->FreePool (FileFormTags->VariableDefinitions->FakeNvRamMap);
|
|
|
|
//
|
|
// Allocate, copy the NvRamMap.
|
|
//
|
|
FileFormTags->VariableDefinitions->VariableFakeSize = (UINT16) (FileFormTags->VariableDefinitions->VariableFakeSize - FileFormTags->VariableDefinitions->VariableSize);
|
|
FileFormTags->VariableDefinitions->VariableSize = (UINT16) NvMapSize;
|
|
FileFormTags->VariableDefinitions->VariableFakeSize = (UINT16) (FileFormTags->VariableDefinitions->VariableFakeSize + FileFormTags->VariableDefinitions->VariableSize);
|
|
|
|
FileFormTags->VariableDefinitions->NvRamMap = AllocateZeroPool (FileFormTags->VariableDefinitions->VariableSize);
|
|
ASSERT (FileFormTags->VariableDefinitions->NvRamMap != NULL);
|
|
|
|
FileFormTags->VariableDefinitions->FakeNvRamMap = AllocateZeroPool (NvMapSize + FileFormTags->VariableDefinitions->VariableFakeSize);
|
|
ASSERT (FileFormTags->VariableDefinitions->FakeNvRamMap != NULL);
|
|
|
|
CopyMem (FileFormTags->VariableDefinitions->NvRamMap, NvMap, NvMapSize);
|
|
gBS->FreePool (NvMapListHead);
|
|
}
|
|
|
|
UpdateStatusBar (NV_UPDATE_REQUIRED, MenuOption->ThisTag->Flags, TRUE);
|
|
Repaint = TRUE;
|
|
//
|
|
// After the repaint operation, we should refresh the highlight.
|
|
//
|
|
NewLine = TRUE;
|
|
break;
|
|
|
|
case CfUiNoOperation:
|
|
ControlFlag = CfCheckSelection;
|
|
break;
|
|
|
|
case CfExit:
|
|
while (gMenuRefreshHead != NULL) {
|
|
OldMenuRefreshEntry = gMenuRefreshHead->Next;
|
|
|
|
gBS->FreePool (gMenuRefreshHead);
|
|
|
|
gMenuRefreshHead = OldMenuRefreshEntry;
|
|
}
|
|
|
|
gST->ConOut->SetCursorPosition (gST->ConOut, 0, Row + 4);
|
|
gST->ConOut->EnableCursor (gST->ConOut, TRUE);
|
|
gST->ConOut->OutputString (gST->ConOut, (CHAR16 *) L"\n");
|
|
|
|
gActiveIfr = MenuOption->IfrNumber;
|
|
return Selection;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOLEAN
|
|
ValueIsScroll (
|
|
IN BOOLEAN Direction,
|
|
IN LIST_ENTRY *CurrentPos
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Determine if the menu is the last menu that can be selected.
|
|
|
|
Arguments:
|
|
Direction - the scroll direction. False is down. True is up.
|
|
|
|
Returns:
|
|
FALSE -- the menu isn't the last menu that can be selected.
|
|
TRUE -- the menu is the last menu that can be selected.
|
|
--*/
|
|
{
|
|
LIST_ENTRY *Temp;
|
|
UI_MENU_OPTION *MenuOption;
|
|
MenuOption = NULL;
|
|
|
|
Temp = Direction ? CurrentPos->BackLink : CurrentPos->ForwardLink;
|
|
|
|
if (Temp == &Menu) {
|
|
return TRUE;
|
|
}
|
|
|
|
for (; Temp != &Menu; Temp = Direction ? Temp->BackLink : Temp->ForwardLink) {
|
|
MenuOption = CR (Temp, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
|
|
if (!(MenuOption->ThisTag->Operand == EFI_IFR_SUBTITLE_OP || MenuOption->ThisTag->GrayOut)) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
UINTN
|
|
AdjustDateAndTimePosition (
|
|
IN BOOLEAN DirectionUp,
|
|
IN LIST_ENTRY **CurrentPosition
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Adjust Data and Time tag position accordingly.
|
|
Data format : [01/02/2004] [11:22:33]
|
|
Line number : 0 0 1 0 0 1
|
|
|
|
Arguments:
|
|
Direction - the up or down direction. False is down. True is up.
|
|
CurrentPos - Current position.
|
|
|
|
Returns:
|
|
Return line number to pad. It is possible that we stand on a zero-advance
|
|
data or time opcode, so pad one line when we judge if we are going to scroll outside.
|
|
--*/
|
|
{
|
|
UINTN Count;
|
|
LIST_ENTRY *NewPosition;
|
|
UI_MENU_OPTION *MenuOption;
|
|
UINTN PadLineNumber;
|
|
|
|
PadLineNumber = 0;
|
|
NewPosition = *CurrentPosition;
|
|
MenuOption = CR (NewPosition, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
|
|
|
|
if ((MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) || (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)) {
|
|
//
|
|
// Calculate the distance from current position to the last Date/Time op-code.
|
|
//
|
|
Count = 0;
|
|
while (MenuOption->ThisTag->NumberOfLines == 0) {
|
|
Count++;
|
|
NewPosition = NewPosition->ForwardLink;
|
|
MenuOption = CR (NewPosition, UI_MENU_OPTION, Link, UI_MENU_OPTION_SIGNATURE);
|
|
PadLineNumber = 1;
|
|
}
|
|
|
|
NewPosition = *CurrentPosition;
|
|
if (DirectionUp) {
|
|
//
|
|
// Since the behavior of hitting the up arrow on a Date/Time op-code is intended
|
|
// to be one that back to the previous set of op-codes, we need to advance to the first
|
|
// Date/Time op-code and leave the remaining logic in CfUiUp intact so the appropriate
|
|
// checking can be done.
|
|
//
|
|
while (Count++ < 2) {
|
|
NewPosition = NewPosition->BackLink;
|
|
}
|
|
} else {
|
|
//
|
|
// Since the behavior of hitting the down arrow on a Date/Time op-code is intended
|
|
// to be one that progresses to the next set of op-codes, we need to advance to the last
|
|
// Date/Time op-code and leave the remaining logic in CfUiDown intact so the appropriate
|
|
// checking can be done.
|
|
//
|
|
while (Count-- > 0) {
|
|
NewPosition = NewPosition->ForwardLink;
|
|
}
|
|
}
|
|
|
|
*CurrentPosition = NewPosition;
|
|
}
|
|
|
|
return PadLineNumber;
|
|
}
|