mirror of https://github.com/acidanthera/audk.git
2875 lines
84 KiB
C
2875 lines
84 KiB
C
/** @file
|
|
|
|
Copyright (c) 2004 - 2007, 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 "Ui.h"
|
|
#include "Setup.h"
|
|
|
|
LIST_ENTRY Menu;
|
|
LIST_ENTRY gMenuList;
|
|
MENU_REFRESH_ENTRY *gMenuRefreshHead;
|
|
|
|
//
|
|
// 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
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
Set Buffer to Value for Size bytes.
|
|
|
|
@param Buffer Memory to set.
|
|
@param Size Number of bytes to set
|
|
@param Value Value of the set operation.
|
|
|
|
@return None
|
|
|
|
**/
|
|
VOID
|
|
SetUnicodeMem (
|
|
IN VOID *Buffer,
|
|
IN UINTN Size,
|
|
IN CHAR16 Value
|
|
)
|
|
{
|
|
CHAR16 *Ptr;
|
|
|
|
Ptr = Buffer;
|
|
while (Size--) {
|
|
*(Ptr++) = Value;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
Initialize Menu option list.
|
|
|
|
None.
|
|
|
|
@return None.
|
|
|
|
**/
|
|
VOID
|
|
UiInitMenu (
|
|
VOID
|
|
)
|
|
{
|
|
InitializeListHead (&Menu);
|
|
}
|
|
|
|
|
|
/**
|
|
Initialize Menu option list.
|
|
|
|
None.
|
|
|
|
@return None.
|
|
|
|
**/
|
|
VOID
|
|
UiInitMenuList (
|
|
VOID
|
|
)
|
|
{
|
|
InitializeListHead (&gMenuList);
|
|
}
|
|
|
|
|
|
/**
|
|
Remove a Menu in list, and return FormId/QuestionId for previous Menu.
|
|
|
|
@param Selection Menu selection.
|
|
|
|
@return None.
|
|
|
|
**/
|
|
VOID
|
|
UiRemoveMenuListEntry (
|
|
IN OUT UI_MENU_SELECTION *Selection
|
|
)
|
|
{
|
|
UI_MENU_LIST *UiMenuList;
|
|
|
|
if (!IsListEmpty (&gMenuList)) {
|
|
UiMenuList = CR (gMenuList.ForwardLink, UI_MENU_LIST, MenuLink, UI_MENU_LIST_SIGNATURE);
|
|
|
|
Selection->FormId = UiMenuList->FormId;
|
|
Selection->QuestionId = UiMenuList->QuestionId;
|
|
RemoveEntryList (&UiMenuList->MenuLink);
|
|
gBS->FreePool (UiMenuList);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
Free Menu option linked list.
|
|
|
|
None.
|
|
|
|
@return None.
|
|
|
|
**/
|
|
VOID
|
|
UiFreeMenuList (
|
|
VOID
|
|
)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
Add one menu entry to the linked lst
|
|
|
|
@param Selection Menu selection.
|
|
|
|
@return None.
|
|
|
|
**/
|
|
VOID
|
|
UiAddMenuListEntry (
|
|
IN UI_MENU_SELECTION *Selection
|
|
)
|
|
{
|
|
UI_MENU_LIST *UiMenuList;
|
|
|
|
UiMenuList = AllocateZeroPool (sizeof (UI_MENU_LIST));
|
|
ASSERT (UiMenuList != NULL);
|
|
|
|
UiMenuList->Signature = UI_MENU_LIST_SIGNATURE;
|
|
UiMenuList->FormId = Selection->FormId;
|
|
UiMenuList->QuestionId = Selection->QuestionId;
|
|
|
|
InsertHeadList (&gMenuList, &UiMenuList->MenuLink);
|
|
}
|
|
|
|
|
|
/**
|
|
Free Menu option linked list.
|
|
|
|
None.
|
|
|
|
@return None.
|
|
|
|
**/
|
|
VOID
|
|
UiFreeMenu (
|
|
VOID
|
|
)
|
|
{
|
|
UI_MENU_OPTION *MenuOption;
|
|
|
|
while (!IsListEmpty (&Menu)) {
|
|
MenuOption = MENU_OPTION_FROM_LINK (Menu.ForwardLink);
|
|
RemoveEntryList (&MenuOption->Link);
|
|
|
|
//
|
|
// We allocated space for this description when we did a GetToken, free it here
|
|
//
|
|
if (MenuOption->Skip != 0) {
|
|
//
|
|
// For date/time, MenuOption->Description is shared by three Menu Options
|
|
// Data format : [01/02/2004] [11:22:33]
|
|
// Line number : 0 0 1 0 0 1
|
|
//
|
|
gBS->FreePool (MenuOption->Description);
|
|
}
|
|
gBS->FreePool (MenuOption);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
Free Menu option linked list.
|
|
|
|
None.
|
|
|
|
@return None.
|
|
|
|
**/
|
|
VOID
|
|
UiFreeRefreshList (
|
|
VOID
|
|
)
|
|
{
|
|
MENU_REFRESH_ENTRY *OldMenuRefreshEntry;
|
|
|
|
while (gMenuRefreshHead != NULL) {
|
|
OldMenuRefreshEntry = gMenuRefreshHead->Next;
|
|
gBS->FreePool (gMenuRefreshHead);
|
|
gMenuRefreshHead = OldMenuRefreshEntry;
|
|
}
|
|
|
|
gMenuRefreshHead = NULL;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
Refresh screen.
|
|
|
|
None.
|
|
|
|
@return None.
|
|
|
|
**/
|
|
VOID
|
|
RefreshForm (
|
|
VOID
|
|
)
|
|
{
|
|
CHAR16 *OptionString;
|
|
MENU_REFRESH_ENTRY *MenuRefreshEntry;
|
|
UINTN Index;
|
|
UINTN Loop;
|
|
EFI_STATUS Status;
|
|
UI_MENU_SELECTION *Selection;
|
|
FORM_BROWSER_STATEMENT *Question;
|
|
|
|
OptionString = NULL;
|
|
|
|
if (gMenuRefreshHead != NULL) {
|
|
|
|
MenuRefreshEntry = gMenuRefreshHead;
|
|
|
|
do {
|
|
gST->ConOut->SetAttribute (gST->ConOut, MenuRefreshEntry->CurrentAttribute);
|
|
|
|
Selection = MenuRefreshEntry->Selection;
|
|
Question = MenuRefreshEntry->MenuOption->ThisTag;
|
|
|
|
//
|
|
// Don't update Question being edited
|
|
//
|
|
if (Question != MenuRefreshEntry->Selection->Statement) {
|
|
|
|
Status = GetQuestionValue (Selection->FormSet, Selection->Form, Question, FALSE);
|
|
if (EFI_ERROR (Status)) {
|
|
return;
|
|
}
|
|
|
|
ProcessOptions (Selection, MenuRefreshEntry->MenuOption, FALSE, &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);
|
|
gBS->FreePool (OptionString);
|
|
}
|
|
}
|
|
|
|
MenuRefreshEntry = MenuRefreshEntry->Next;
|
|
|
|
} while (MenuRefreshEntry != NULL);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
Wait for a given event to fire, or for an optional timeout to expire.
|
|
|
|
@param Event The event to wait for
|
|
@param Timeout An optional timeout value in 100 ns units.
|
|
@param RefreshInterval Menu refresh interval (in seconds).
|
|
|
|
@retval EFI_SUCCESS Event fired before Timeout expired.
|
|
@retval EFI_TIME_OUT Timout expired before Event fired.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
UiWaitForSingleEvent (
|
|
IN EFI_EVENT Event,
|
|
IN UINT64 Timeout, OPTIONAL
|
|
IN UINT8 RefreshInterval OPTIONAL
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN Index;
|
|
EFI_EVENT TimerEvent;
|
|
EFI_EVENT WaitList[2];
|
|
|
|
if (Timeout) {
|
|
//
|
|
// Create a timer event
|
|
//
|
|
Status = gBS->CreateEvent (EVT_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
|
|
//
|
|
if (RefreshInterval == 0) {
|
|
Timeout = ONE_SECOND;
|
|
} else {
|
|
Timeout = RefreshInterval * ONE_SECOND;
|
|
}
|
|
|
|
do {
|
|
Status = gBS->CreateEvent (EVT_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;
|
|
if (RefreshInterval != 0) {
|
|
RefreshForm ();
|
|
}
|
|
}
|
|
|
|
gBS->CloseEvent (TimerEvent);
|
|
} while (Status == EFI_TIMEOUT);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/**
|
|
Add one menu option by specified description and context.
|
|
|
|
@param String String description for this option.
|
|
@param Handle Hii handle for the package list.
|
|
@param Statement Statement of this Menu Option.
|
|
@param NumberOfLines Display lines for this Menu Option.
|
|
@param MenuItemCount The index for this Option in the Menu.
|
|
|
|
@return None.
|
|
|
|
**/
|
|
VOID
|
|
UiAddMenuOption (
|
|
IN CHAR16 *String,
|
|
IN EFI_HII_HANDLE Handle,
|
|
IN FORM_BROWSER_STATEMENT *Statement,
|
|
IN UINT16 NumberOfLines,
|
|
IN UINT16 MenuItemCount
|
|
)
|
|
{
|
|
UI_MENU_OPTION *MenuOption;
|
|
UINTN Index;
|
|
UINTN Count;
|
|
|
|
Count = 1;
|
|
|
|
if (Statement->Operand == EFI_IFR_DATE_OP || Statement->Operand == EFI_IFR_TIME_OP) {
|
|
//
|
|
// Add three MenuOptions for Date/Time
|
|
// Data format : [01/02/2004] [11:22:33]
|
|
// Line number : 0 0 1 0 0 1
|
|
//
|
|
NumberOfLines = 0;
|
|
Count = 3;
|
|
|
|
if (Statement->Storage == NULL) {
|
|
//
|
|
// For RTC type of date/time, set default refresh interval to be 1 second
|
|
//
|
|
if (Statement->RefreshInterval == 0) {
|
|
Statement->RefreshInterval = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (Index = 0; Index < Count; Index++) {
|
|
MenuOption = AllocateZeroPool (sizeof (UI_MENU_OPTION));
|
|
ASSERT (MenuOption);
|
|
|
|
MenuOption->Signature = UI_MENU_OPTION_SIGNATURE;
|
|
MenuOption->Description = String;
|
|
MenuOption->Handle = Handle;
|
|
MenuOption->ThisTag = Statement;
|
|
MenuOption->EntryNumber = MenuItemCount;
|
|
|
|
if (Index == 2) {
|
|
//
|
|
// Override LineNumber for the MenuOption in Date/Time sequence
|
|
//
|
|
MenuOption->Skip = 1;
|
|
} else {
|
|
MenuOption->Skip = NumberOfLines;
|
|
}
|
|
MenuOption->Sequence = Index;
|
|
|
|
if (Statement->GrayOutExpression != NULL) {
|
|
MenuOption->GrayOut = Statement->GrayOutExpression->Result.Value.b;
|
|
}
|
|
|
|
if ((Statement->ValueExpression != NULL) ||
|
|
(Statement->QuestionFlags & EFI_IFR_FLAG_READ_ONLY)) {
|
|
MenuOption->ReadOnly = TRUE;
|
|
}
|
|
|
|
InsertTailList (&Menu, &MenuOption->Link);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
Routine used to abstract a generic dialog interface and return the selected key or string
|
|
|
|
@param NumberOfLines The number of lines for the dialog box
|
|
@param 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
|
|
@param MaximumStringSize The maximum size in bytes of a typed in string
|
|
(each character is a CHAR16) and the minimum
|
|
string returned is two bytes
|
|
@param StringBuffer The passed in pointer to the buffer which will
|
|
hold the typed in string if HotKey is FALSE
|
|
@param KeyValue The EFI_KEY value returned if HotKey is TRUE..
|
|
@param String Pointer to the first string in the list
|
|
@param ... A series of (quantity == NumberOfLines) text
|
|
strings which will be used to construct the dialog
|
|
box
|
|
|
|
@retval EFI_SUCCESS Displayed dialog and received user interaction
|
|
@retval EFI_INVALID_PARAMETER One of the parameters was invalid (e.g.
|
|
(StringBuffer == NULL) && (HotKey == FALSE))
|
|
@retval EFI_DEVICE_ERROR User typed in an ESC character to exit the routine
|
|
|
|
**/
|
|
EFI_STATUS
|
|
CreateDialog (
|
|
IN UINTN NumberOfLines,
|
|
IN BOOLEAN HotKey,
|
|
IN UINTN MaximumStringSize,
|
|
OUT CHAR16 *StringBuffer,
|
|
OUT EFI_INPUT_KEY *KeyValue,
|
|
IN CHAR16 *String,
|
|
...
|
|
)
|
|
{
|
|
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;
|
|
EFI_STATUS Status;
|
|
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 (*String == 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) {
|
|
Status = WaitForKeyStroke (&Key);
|
|
ASSERT_EFI_ERROR (Status);
|
|
CopyMem (KeyValue, &Key, sizeof (EFI_INPUT_KEY));
|
|
|
|
} else {
|
|
do {
|
|
Status = 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 = BOXDRAW_DOWN_RIGHT;
|
|
PrintCharAt (Start, Top, Character);
|
|
Character = BOXDRAW_HORIZONTAL;
|
|
for (Index = Start; Index + 2 < End; Index++) {
|
|
PrintChar (Character);
|
|
}
|
|
|
|
Character = BOXDRAW_DOWN_LEFT;
|
|
PrintChar (Character);
|
|
Character = 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 = BOXDRAW_UP_RIGHT;
|
|
PrintCharAt (Start, Bottom - 1, Character);
|
|
Character = BOXDRAW_HORIZONTAL;
|
|
for (Index = Start; Index + 2 < End; Index++) {
|
|
PrintChar (Character);
|
|
}
|
|
|
|
Character = BOXDRAW_UP_LEFT;
|
|
PrintChar (Character);
|
|
}
|
|
|
|
VOID
|
|
CreatePopUp (
|
|
IN UINTN RequestedWidth,
|
|
IN UINTN NumberOfLines,
|
|
IN CHAR16 *ArrayOfStrings,
|
|
...
|
|
)
|
|
{
|
|
CreateSharedPopUp (RequestedWidth, NumberOfLines, &ArrayOfStrings);
|
|
}
|
|
|
|
|
|
/**
|
|
Update status bar on the bottom of menu.
|
|
|
|
@param MessageType The type of message to be shown.
|
|
@param Flags The flags in Question header.
|
|
@param State Set or clear.
|
|
|
|
@return None.
|
|
|
|
**/
|
|
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, 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,
|
|
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 ;
|
|
}
|
|
|
|
|
|
/**
|
|
Get the supported width for a particular op-code
|
|
|
|
@param Statement The FORM_BROWSER_STATEMENT structure passed in.
|
|
@param Handle The handle in the HII database being used
|
|
|
|
@return Returns the number of CHAR16 characters that is support.
|
|
|
|
**/
|
|
UINT16
|
|
GetWidth (
|
|
IN FORM_BROWSER_STATEMENT *Statement,
|
|
IN EFI_HII_HANDLE Handle
|
|
)
|
|
{
|
|
CHAR16 *String;
|
|
UINTN Size;
|
|
UINT16 Width;
|
|
|
|
Size = 0;
|
|
|
|
//
|
|
// See if the second text parameter is really NULL
|
|
//
|
|
if ((Statement->Operand == EFI_IFR_TEXT_OP) && (Statement->TextTwo != 0)) {
|
|
String = GetToken (Statement->TextTwo, Handle);
|
|
Size = StrLen (String);
|
|
gBS->FreePool (String);
|
|
}
|
|
|
|
if ((Statement->Operand == EFI_IFR_SUBTITLE_OP) ||
|
|
(Statement->Operand == EFI_IFR_REF_OP) ||
|
|
(Statement->Operand == EFI_IFR_PASSWORD_OP) ||
|
|
(Statement->Operand == EFI_IFR_ACTION_OP) ||
|
|
(Statement->Operand == EFI_IFR_RESET_BUTTON_OP) ||
|
|
//
|
|
// Allow a wide display if text op-code and no secondary text op-code
|
|
//
|
|
((Statement->Operand == EFI_IFR_TEXT_OP) && (Size == 0))
|
|
) {
|
|
Width = (UINT16) (gPromptBlockWidth + gOptionBlockWidth);
|
|
} else {
|
|
Width = (UINT16) gPromptBlockWidth;
|
|
}
|
|
|
|
if (Statement->InSubtitle) {
|
|
Width -= SUBTITLE_INDENT;
|
|
}
|
|
|
|
return Width;
|
|
}
|
|
|
|
|
|
/**
|
|
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.
|
|
|
|
@param InputString String description for this option.
|
|
@param LineWidth Width of the desired string to extract in CHAR16
|
|
characters
|
|
@param Index Where in InputString to start the copy process
|
|
@param OutputString Buffer to copy the string into
|
|
|
|
@return Returns the number of CHAR16 characters that were copied into the OutputString buffer.
|
|
|
|
**/
|
|
UINT16
|
|
GetLineByWidth (
|
|
IN CHAR16 *InputString,
|
|
IN UINT16 LineWidth,
|
|
IN OUT UINTN *Index,
|
|
OUT CHAR16 **OutputString
|
|
)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
Update display lines for a Menu Option.
|
|
|
|
@param MenuOption The MenuOption to be checked.
|
|
|
|
@retval TRUE This Menu Option is selectable.
|
|
@retval FALSE This Menu Option could not be selected.
|
|
|
|
**/
|
|
VOID
|
|
UpdateOptionSkipLines (
|
|
IN UI_MENU_SELECTION *Selection,
|
|
IN UI_MENU_OPTION *MenuOption,
|
|
IN CHAR16 **OptionalString,
|
|
IN UINTN SkipValue
|
|
)
|
|
{
|
|
UINTN Index;
|
|
UINT16 Width;
|
|
UINTN Row;
|
|
UINTN OriginalRow;
|
|
CHAR16 *OutputString;
|
|
CHAR16 *OptionString;
|
|
|
|
Row = 0;
|
|
OptionString = *OptionalString;
|
|
OutputString = NULL;
|
|
|
|
ProcessOptions (Selection, MenuOption, FALSE, &OptionString);
|
|
|
|
if (OptionString != 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;
|
|
}
|
|
|
|
|
|
/**
|
|
Check whether this Menu Option could be highlighted.
|
|
|
|
@param MenuOption The MenuOption to be checked.
|
|
|
|
@retval TRUE This Menu Option is selectable.
|
|
@retval FALSE This Menu Option could not be selected.
|
|
|
|
**/
|
|
STATIC
|
|
BOOLEAN
|
|
IsSelectable (
|
|
UI_MENU_OPTION *MenuOption
|
|
)
|
|
{
|
|
if ((MenuOption->ThisTag->Operand == EFI_IFR_SUBTITLE_OP) ||
|
|
MenuOption->GrayOut || MenuOption->ReadOnly) {
|
|
return FALSE;
|
|
} else {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
Determine if the menu is the last menu that can be selected.
|
|
|
|
@param Direction the scroll direction. False is down. True is up.
|
|
|
|
@return FALSE -- the menu isn't the last menu that can be selected.
|
|
@return TRUE -- the menu is the last menu that can be selected.
|
|
|
|
**/
|
|
STATIC
|
|
BOOLEAN
|
|
ValueIsScroll (
|
|
IN BOOLEAN Direction,
|
|
IN LIST_ENTRY *CurrentPos
|
|
)
|
|
{
|
|
LIST_ENTRY *Temp;
|
|
UI_MENU_OPTION *MenuOption;
|
|
|
|
Temp = Direction ? CurrentPos->BackLink : CurrentPos->ForwardLink;
|
|
|
|
if (Temp == &Menu) {
|
|
return TRUE;
|
|
}
|
|
|
|
for (; Temp != &Menu; Temp = Direction ? Temp->BackLink : Temp->ForwardLink) {
|
|
MenuOption = MENU_OPTION_FROM_LINK (Temp);
|
|
if (IsSelectable (MenuOption)) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/**
|
|
Move to next selectable statement.
|
|
|
|
@param GoUp The navigation direction. TRUE: up, FALSE: down.
|
|
@param CurrentPosition Current position.
|
|
|
|
@return The row distance from current MenuOption to next selectable MenuOption.
|
|
|
|
**/
|
|
STATIC
|
|
INTN
|
|
MoveToNextStatement (
|
|
IN BOOLEAN GoUp,
|
|
IN OUT LIST_ENTRY **CurrentPosition
|
|
)
|
|
{
|
|
INTN Distance;
|
|
LIST_ENTRY *Pos;
|
|
BOOLEAN HitEnd;
|
|
UI_MENU_OPTION *NextMenuOption;
|
|
|
|
Distance = 0;
|
|
Pos = *CurrentPosition;
|
|
HitEnd = FALSE;
|
|
|
|
while (TRUE) {
|
|
NextMenuOption = MENU_OPTION_FROM_LINK (Pos);
|
|
if (IsSelectable (NextMenuOption)) {
|
|
break;
|
|
}
|
|
if ((GoUp ? Pos->BackLink : Pos->ForwardLink) == &Menu) {
|
|
HitEnd = TRUE;
|
|
break;
|
|
}
|
|
Distance += NextMenuOption->Skip;
|
|
Pos = (GoUp ? Pos->BackLink : Pos->ForwardLink);
|
|
}
|
|
|
|
if (HitEnd) {
|
|
//
|
|
// If we hit end there is still no statement can be focused,
|
|
// we go backwards to find the statement can be focused.
|
|
//
|
|
Distance = 0;
|
|
Pos = *CurrentPosition;
|
|
|
|
while (TRUE) {
|
|
NextMenuOption = MENU_OPTION_FROM_LINK (Pos);
|
|
if (IsSelectable (NextMenuOption)) {
|
|
break;
|
|
}
|
|
if ((!GoUp ? Pos->BackLink : Pos->ForwardLink) == &Menu) {
|
|
ASSERT (FALSE);
|
|
break;
|
|
}
|
|
Distance -= NextMenuOption->Skip;
|
|
Pos = (!GoUp ? Pos->BackLink : Pos->ForwardLink);
|
|
}
|
|
}
|
|
|
|
*CurrentPosition = &NextMenuOption->Link;
|
|
return Distance;
|
|
}
|
|
|
|
|
|
/**
|
|
Adjust Data and Time position accordingly.
|
|
Data format : [01/02/2004] [11:22:33]
|
|
Line number : 0 0 1 0 0 1
|
|
|
|
@param DirectionUp the up or down direction. False is down. True is
|
|
up.
|
|
@param CurrentPosition Current position. On return: Point to the last
|
|
Option (Year or Second) if up; Point to the first
|
|
Option (Month or Hour) if down.
|
|
|
|
@return Return line number to pad. It is possible that we stand on a zero-advance
|
|
@return data or time opcode, so pad one line when we judge if we are going to scroll outside.
|
|
|
|
**/
|
|
STATIC
|
|
UINTN
|
|
AdjustDateAndTimePosition (
|
|
IN BOOLEAN DirectionUp,
|
|
IN OUT LIST_ENTRY **CurrentPosition
|
|
)
|
|
{
|
|
UINTN Count;
|
|
LIST_ENTRY *NewPosition;
|
|
UI_MENU_OPTION *MenuOption;
|
|
UINTN PadLineNumber;
|
|
|
|
PadLineNumber = 0;
|
|
NewPosition = *CurrentPosition;
|
|
MenuOption = MENU_OPTION_FROM_LINK (NewPosition);
|
|
|
|
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 MenuOption
|
|
//
|
|
Count = 0;
|
|
while (MenuOption->Skip == 0) {
|
|
Count++;
|
|
NewPosition = NewPosition->ForwardLink;
|
|
MenuOption = MENU_OPTION_FROM_LINK (NewPosition);
|
|
PadLineNumber = 1;
|
|
}
|
|
|
|
NewPosition = *CurrentPosition;
|
|
if (DirectionUp) {
|
|
//
|
|
// Since the behavior of hitting the up arrow on a Date/Time MenuOption is intended
|
|
// to be one that back to the previous set of MenuOptions, we need to advance to the first
|
|
// Date/Time MenuOption 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 MenuOption is intended
|
|
// to be one that progresses to the next set of MenuOptions, we need to advance to the last
|
|
// Date/Time MenuOption 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;
|
|
}
|
|
|
|
|
|
/**
|
|
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.
|
|
|
|
|
|
@return Return the pointer of the menu which selected,
|
|
@return otherwise return NULL.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
UiDisplayMenu (
|
|
IN OUT UI_MENU_SELECTION *Selection
|
|
)
|
|
{
|
|
INTN SkipValue;
|
|
INTN Difference;
|
|
INTN OldSkipValue;
|
|
UINTN DistanceValue;
|
|
UINTN Row;
|
|
UINTN Col;
|
|
UINTN Temp;
|
|
UINTN Temp2;
|
|
UINTN TopRow;
|
|
UINTN BottomRow;
|
|
UINTN OriginalRow;
|
|
UINTN Index;
|
|
UINT32 Count;
|
|
UINT16 Width;
|
|
CHAR16 *StringPtr;
|
|
CHAR16 *OptionString;
|
|
CHAR16 *OutputString;
|
|
CHAR16 *FormattedString;
|
|
CHAR16 YesResponse;
|
|
CHAR16 NoResponse;
|
|
BOOLEAN NewLine;
|
|
BOOLEAN Repaint;
|
|
BOOLEAN SavedValue;
|
|
EFI_STATUS Status;
|
|
EFI_INPUT_KEY Key;
|
|
LIST_ENTRY *Link;
|
|
LIST_ENTRY *NewPos;
|
|
LIST_ENTRY *TopOfScreen;
|
|
LIST_ENTRY *SavedListEntry;
|
|
UI_MENU_OPTION *MenuOption;
|
|
UI_MENU_OPTION *NextMenuOption;
|
|
UI_MENU_OPTION *SavedMenuOption;
|
|
UI_MENU_OPTION *PreviousMenuOption;
|
|
UI_CONTROL_FLAG ControlFlag;
|
|
EFI_SCREEN_DESCRIPTOR LocalScreen;
|
|
MENU_REFRESH_ENTRY *MenuRefreshEntry;
|
|
UI_SCREEN_OPERATION ScreenOperation;
|
|
UINT8 MinRefreshInterval;
|
|
UINTN BufferSize;
|
|
UINT16 DefaultId;
|
|
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
|
|
FORM_BROWSER_STATEMENT *Statement;
|
|
|
|
CopyMem (&LocalScreen, &gScreenDimensions, sizeof (EFI_SCREEN_DESCRIPTOR));
|
|
|
|
Status = EFI_SUCCESS;
|
|
FormattedString = NULL;
|
|
OptionString = NULL;
|
|
ScreenOperation = UiNoOperation;
|
|
NewLine = TRUE;
|
|
MinRefreshInterval = 0;
|
|
DefaultId = 0;
|
|
|
|
OutputString = NULL;
|
|
gUpArrow = FALSE;
|
|
gDownArrow = FALSE;
|
|
SkipValue = 0;
|
|
OldSkipValue = 0;
|
|
MenuRefreshEntry = gMenuRefreshHead;
|
|
|
|
NextMenuOption = NULL;
|
|
PreviousMenuOption = NULL;
|
|
SavedMenuOption = NULL;
|
|
|
|
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;
|
|
}
|
|
|
|
Col = LocalScreen.LeftColumn;
|
|
BottomRow = LocalScreen.BottomRow - STATUS_BAR_HEIGHT - FOOTER_HEIGHT - SCROLL_ARROW_HEIGHT - 1;
|
|
|
|
Selection->TopRow = TopRow;
|
|
Selection->BottomRow = BottomRow;
|
|
Selection->PromptCol = Col;
|
|
Selection->OptionCol = gPromptBlockWidth + 1 + LocalScreen.LeftColumn;
|
|
Selection->Statement = NULL;
|
|
|
|
TopOfScreen = Menu.ForwardLink;
|
|
Repaint = TRUE;
|
|
MenuOption = NULL;
|
|
|
|
//
|
|
// Get user's selection
|
|
//
|
|
NewPos = Menu.ForwardLink;
|
|
|
|
gST->ConOut->EnableCursor (gST->ConOut, FALSE);
|
|
UpdateStatusBar (REFRESH_STATUS_BAR, (UINT8) 0, TRUE);
|
|
|
|
ControlFlag = CfInitialization;
|
|
Selection->Action = UI_ACTION_NONE;
|
|
while (TRUE) {
|
|
switch (ControlFlag) {
|
|
case CfInitialization:
|
|
if (IsListEmpty (&Menu)) {
|
|
ControlFlag = CfReadKey;
|
|
} else {
|
|
ControlFlag = CfCheckSelection;
|
|
}
|
|
break;
|
|
|
|
case CfCheckSelection:
|
|
if (Selection->Action != UI_ACTION_NONE) {
|
|
ControlFlag = CfExit;
|
|
} else {
|
|
ControlFlag = CfRepaint;
|
|
}
|
|
break;
|
|
|
|
case CfRepaint:
|
|
ControlFlag = CfRefreshHighLight;
|
|
|
|
if (Repaint) {
|
|
//
|
|
// Display menu
|
|
//
|
|
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
|
|
);
|
|
|
|
UiFreeRefreshList ();
|
|
MinRefreshInterval = 0;
|
|
|
|
for (Link = TopOfScreen; Link != &Menu; Link = Link->ForwardLink) {
|
|
MenuOption = MENU_OPTION_FROM_LINK (Link);
|
|
MenuOption->Row = Row;
|
|
MenuOption->Col = Col;
|
|
MenuOption->OptCol = gPromptBlockWidth + 1 + LocalScreen.LeftColumn;
|
|
|
|
Statement = MenuOption->ThisTag;
|
|
if (Statement->InSubtitle) {
|
|
MenuOption->Col += SUBTITLE_INDENT;
|
|
}
|
|
|
|
if (MenuOption->GrayOut) {
|
|
gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_GRAYED | FIELD_BACKGROUND);
|
|
} else {
|
|
if (Statement->Operand == EFI_IFR_SUBTITLE_OP) {
|
|
gST->ConOut->SetAttribute (gST->ConOut, SUBTITLE_TEXT | FIELD_BACKGROUND);
|
|
}
|
|
}
|
|
|
|
Width = GetWidth (Statement, MenuOption->Handle);
|
|
OriginalRow = Row;
|
|
|
|
for (Index = 0; GetLineByWidth (MenuOption->Description, Width, &Index, &OutputString) != 0x0000;) {
|
|
if ((Temp == 0) && (Row <= BottomRow)) {
|
|
PrintStringAt (MenuOption->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 (Selection, MenuOption, FALSE, &OptionString);
|
|
|
|
if (OptionString != NULL) {
|
|
if (Statement->Operand == EFI_IFR_DATE_OP || Statement->Operand == EFI_IFR_TIME_OP) {
|
|
//
|
|
// 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 Question request refresh, register the op-code
|
|
//
|
|
if (Statement->RefreshInterval != 0) {
|
|
//
|
|
// Menu will be refreshed at minimal interval of all Questions
|
|
// which have refresh request
|
|
//
|
|
if (MinRefreshInterval == 0 || Statement->RefreshInterval < MinRefreshInterval) {
|
|
MinRefreshInterval = Statement->RefreshInterval;
|
|
}
|
|
|
|
if (gMenuRefreshHead == NULL) {
|
|
MenuRefreshEntry = AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY));
|
|
ASSERT (MenuRefreshEntry != NULL);
|
|
MenuRefreshEntry->MenuOption = MenuOption;
|
|
MenuRefreshEntry->Selection = Selection;
|
|
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->Selection = Selection;
|
|
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;
|
|
|
|
gBS->FreePool (OptionString);
|
|
}
|
|
//
|
|
// If this is a text op with secondary text information
|
|
//
|
|
if ((Statement->Operand == EFI_IFR_TEXT_OP) && (Statement->TextTwo != 0)) {
|
|
StringPtr = GetToken (Statement->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);
|
|
}
|
|
|
|
//
|
|
// 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,
|
|
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,
|
|
L"%c",
|
|
ARROW_DOWN
|
|
);
|
|
gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
|
|
}
|
|
|
|
MenuOption = NULL;
|
|
}
|
|
break;
|
|
|
|
case CfRefreshHighLight:
|
|
//
|
|
// MenuOption: Last menu option that need to remove hilight
|
|
// MenuOption is set to NULL in Repaint
|
|
// NewPos: Current menu option that need to hilight
|
|
//
|
|
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 (Selection->QuestionId != 0) {
|
|
NewPos = Menu.ForwardLink;
|
|
SavedMenuOption = MENU_OPTION_FROM_LINK (NewPos);
|
|
|
|
while (SavedMenuOption->ThisTag->QuestionId != Selection->QuestionId && NewPos->ForwardLink != &Menu) {
|
|
NewPos = NewPos->ForwardLink;
|
|
SavedMenuOption = MENU_OPTION_FROM_LINK (NewPos);
|
|
}
|
|
if (SavedMenuOption->ThisTag->QuestionId == Selection->QuestionId) {
|
|
//
|
|
// Target Question found, find its MenuOption
|
|
//
|
|
Link = TopOfScreen;
|
|
|
|
for (Index = TopRow; Index <= BottomRow && Link != NewPos;) {
|
|
SavedMenuOption = MENU_OPTION_FROM_LINK (Link);
|
|
Index += SavedMenuOption->Skip;
|
|
Link = Link->ForwardLink;
|
|
}
|
|
|
|
if (Link != NewPos || Index > BottomRow) {
|
|
//
|
|
// NewPos is not in the current page, simply scroll page so that NewPos is in the end of the page
|
|
//
|
|
Link = NewPos;
|
|
for (Index = TopRow; Index <= BottomRow; ) {
|
|
Link = Link->BackLink;
|
|
SavedMenuOption = MENU_OPTION_FROM_LINK (Link);
|
|
Index += SavedMenuOption->Skip;
|
|
}
|
|
TopOfScreen = Link->ForwardLink;
|
|
|
|
Repaint = TRUE;
|
|
NewLine = TRUE;
|
|
ControlFlag = CfRepaint;
|
|
break;
|
|
}
|
|
} else {
|
|
//
|
|
// Target Question not found, highlight the default menu option
|
|
//
|
|
NewPos = TopOfScreen;
|
|
}
|
|
|
|
Selection->QuestionId = 0;
|
|
}
|
|
|
|
if (NewPos != NULL && (MenuOption == NULL || NewPos != &MenuOption->Link)) {
|
|
if (MenuOption != NULL) {
|
|
//
|
|
// Remove highlight on last Menu Option
|
|
//
|
|
gST->ConOut->SetCursorPosition (gST->ConOut, MenuOption->Col, MenuOption->Row);
|
|
ProcessOptions (Selection, MenuOption, FALSE, &OptionString);
|
|
gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
|
|
if (OptionString != NULL) {
|
|
if ((MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) ||
|
|
(MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)
|
|
) {
|
|
//
|
|
// 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;
|
|
|
|
gBS->FreePool (OptionString);
|
|
} else {
|
|
if (NewLine) {
|
|
if (MenuOption->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 (MenuOption->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);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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
|
|
//
|
|
MenuOption = MENU_OPTION_FROM_LINK (NewPos);
|
|
if (!IsSelectable (MenuOption)) {
|
|
ASSERT (ScreenOperation == UiNoOperation);
|
|
ScreenOperation = UiDown;
|
|
ControlFlag = CfScreenOperation;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// This is the current selected statement
|
|
//
|
|
Statement = MenuOption->ThisTag;
|
|
Selection->Statement = Statement;
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
ProcessOptions (Selection, MenuOption, FALSE, &OptionString);
|
|
if (OptionString != NULL) {
|
|
if (Statement->Operand == EFI_IFR_DATE_OP || Statement->Operand == EFI_IFR_TIME_OP) {
|
|
//
|
|
// 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;
|
|
|
|
gBS->FreePool (OptionString);
|
|
} else {
|
|
if (NewLine) {
|
|
OriginalRow = MenuOption->Row;
|
|
|
|
Width = GetWidth (Statement, MenuOption->Handle);
|
|
|
|
for (Index = 0; GetLineByWidth (MenuOption->Description, Width, &Index, &OutputString) != 0x0000;) {
|
|
if (MenuOption->Row >= TopRow && MenuOption->Row <= BottomRow) {
|
|
PrintStringAt (MenuOption->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);
|
|
}
|
|
//
|
|
// 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 ((Repaint || NewLine) && (gClassOfVfr != EFI_GENERAL_APPLICATION_SUBCLASS)) {
|
|
//
|
|
// Don't print anything if it is a NULL help token
|
|
//
|
|
if (MenuOption->ThisTag->Help == 0) {
|
|
StringPtr = 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]) / 2 < gHelpBlockWidth;) {
|
|
StrCat (&FormattedString[Index * gHelpBlockWidth * 2], L" ");
|
|
}
|
|
|
|
PrintStringAt (
|
|
LocalScreen.RightColumn - gHelpBlockWidth,
|
|
Index + TopRow,
|
|
&FormattedString[Index * gHelpBlockWidth * 2]
|
|
);
|
|
}
|
|
}
|
|
//
|
|
// Reset this flag every time we finish using it.
|
|
//
|
|
Repaint = FALSE;
|
|
NewLine = FALSE;
|
|
break;
|
|
|
|
case CfPrepareToReadKey:
|
|
ControlFlag = CfReadKey;
|
|
ScreenOperation = UiNoOperation;
|
|
break;
|
|
|
|
case CfReadKey:
|
|
ControlFlag = CfScreenOperation;
|
|
|
|
//
|
|
// Wait for user's selection
|
|
//
|
|
do {
|
|
Status = UiWaitForSingleEvent (gST->ConIn->WaitForKey, 0, MinRefreshInterval);
|
|
} while (Status == EFI_TIMEOUT);
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
if (IsListEmpty (&Menu) && Key.UnicodeChar != CHAR_NULL) {
|
|
//
|
|
// If the screen has no menu items, and the user didn't select UiPrevious, or UiReset
|
|
//
|
|
break;
|
|
}
|
|
|
|
switch (Key.UnicodeChar) {
|
|
case CHAR_CARRIAGE_RETURN:
|
|
ScreenOperation = UiSelect;
|
|
gDirection = 0;
|
|
break;
|
|
|
|
//
|
|
// We will push the adjustment of these numeric values directly to the input handler
|
|
// NOTE: we won't handle manual input numeric
|
|
//
|
|
case '+':
|
|
case '-':
|
|
Statement = MenuOption->ThisTag;
|
|
if ((Statement->Operand == EFI_IFR_DATE_OP)
|
|
|| (Statement->Operand == EFI_IFR_TIME_OP)
|
|
|| ((Statement->Operand == EFI_IFR_NUMERIC_OP) && (Statement->Step != 0))
|
|
){
|
|
if (Key.UnicodeChar == '+') {
|
|
gDirection = SCAN_RIGHT;
|
|
} else {
|
|
gDirection = SCAN_LEFT;
|
|
}
|
|
Status = ProcessOptions (Selection, MenuOption, TRUE, &OptionString);
|
|
SafeFreePool (OptionString);
|
|
}
|
|
break;
|
|
|
|
case '^':
|
|
ScreenOperation = UiUp;
|
|
break;
|
|
|
|
case 'V':
|
|
case 'v':
|
|
ScreenOperation = UiDown;
|
|
break;
|
|
|
|
case ' ':
|
|
if (gClassOfVfr != EFI_FRONT_PAGE_SUBCLASS) {
|
|
if (MenuOption->ThisTag->Operand == EFI_IFR_CHECKBOX_OP && !MenuOption->GrayOut) {
|
|
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) {
|
|
//
|
|
// Reset to standard default
|
|
//
|
|
DefaultId = EFI_HII_DEFAULT_CLASS_STANDARD;
|
|
}
|
|
ScreenOperation = gScanCodeToOperation[Index].ScreenOperation;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case CfScreenOperation:
|
|
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 = MENU_OPTION_FROM_LINK (Link);
|
|
if (IsSelectable (NextMenuOption)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Link == &Menu) {
|
|
ControlFlag = CfPrepareToReadKey;
|
|
break;
|
|
}
|
|
} else if (ScreenOperation == UiReset) {
|
|
//
|
|
// Press ESC to exit FormSet
|
|
//
|
|
Selection->Action = UI_ACTION_EXIT;
|
|
Selection->Statement = NULL;
|
|
}
|
|
|
|
for (Index = 0;
|
|
Index < sizeof (gScreenOperationToControlFlag) / sizeof (gScreenOperationToControlFlag[0]);
|
|
Index++
|
|
) {
|
|
if (ScreenOperation == gScreenOperationToControlFlag[Index].ScreenOperation) {
|
|
ControlFlag = gScreenOperationToControlFlag[Index].ControlFlag;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case CfUiPrevious:
|
|
ControlFlag = CfCheckSelection;
|
|
|
|
if (IsListEmpty (&gMenuList)) {
|
|
Selection->Action = UI_ACTION_NONE;
|
|
if (IsListEmpty (&Menu)) {
|
|
ControlFlag = CfReadKey;
|
|
}
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Remove the Cached page entry
|
|
//
|
|
UiRemoveMenuListEntry (Selection);
|
|
|
|
Selection->Action = UI_ACTION_REFRESH_FORM;
|
|
Selection->Statement = NULL;
|
|
break;
|
|
|
|
case CfUiSelect:
|
|
ControlFlag = CfCheckSelection;
|
|
|
|
Statement = MenuOption->ThisTag;
|
|
if ((Statement->Operand == EFI_IFR_TEXT_OP) ||
|
|
(Statement->Operand == EFI_IFR_DATE_OP) ||
|
|
(Statement->Operand == EFI_IFR_TIME_OP) ||
|
|
(Statement->Operand == EFI_IFR_NUMERIC_OP && Statement->Step != 0)) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Keep highlight on current MenuOption
|
|
//
|
|
Selection->QuestionId = Statement->QuestionId;
|
|
|
|
switch (Statement->Operand) {
|
|
case EFI_IFR_REF_OP:
|
|
if (Statement->RefDevicePath != 0) {
|
|
//
|
|
// Goto another Hii Package list
|
|
//
|
|
ControlFlag = CfUiReset;
|
|
Selection->Action = UI_ACTION_REFRESH_FORMSET;
|
|
|
|
StringPtr = GetToken (Statement->RefDevicePath, Selection->FormSet->HiiHandle);
|
|
if (StringPtr == NULL) {
|
|
//
|
|
// No device path string not found, exit
|
|
//
|
|
Selection->Action = UI_ACTION_EXIT;
|
|
Selection->Statement = NULL;
|
|
break;
|
|
}
|
|
BufferSize = StrLen (StringPtr) / 2;
|
|
DevicePath = AllocatePool (BufferSize);
|
|
|
|
HexStringToBuffer ((UINT8 *) DevicePath, &BufferSize, StringPtr);
|
|
Selection->Handle = HiiLibDevicePathToHiiHandle (DevicePath);
|
|
if (Selection->Handle == NULL) {
|
|
//
|
|
// If target Hii Handle not found, exit
|
|
//
|
|
Selection->Action = UI_ACTION_EXIT;
|
|
Selection->Statement = NULL;
|
|
break;
|
|
}
|
|
|
|
gBS->FreePool (StringPtr);
|
|
gBS->FreePool (DevicePath);
|
|
|
|
CopyMem (&Selection->FormSetGuid, &Statement->RefFormSetId, sizeof (EFI_GUID));
|
|
Selection->FormId = Statement->RefFormId;
|
|
Selection->QuestionId = Statement->RefQuestionId;
|
|
} else if (!CompareGuid (&Statement->RefFormSetId, &gZeroGuid)) {
|
|
//
|
|
// Goto another Formset, check for uncommitted data
|
|
//
|
|
ControlFlag = CfUiReset;
|
|
Selection->Action = UI_ACTION_REFRESH_FORMSET;
|
|
|
|
CopyMem (&Selection->FormSetGuid, &Statement->RefFormSetId, sizeof (EFI_GUID));
|
|
Selection->FormId = Statement->RefFormId;
|
|
Selection->QuestionId = Statement->RefQuestionId;
|
|
} else if (Statement->RefFormId != 0) {
|
|
//
|
|
// Goto another form inside this formset,
|
|
//
|
|
Selection->Action = UI_ACTION_REFRESH_FORM;
|
|
|
|
//
|
|
// Link current form so that we can always go back when someone hits the UiPrevious
|
|
//
|
|
UiAddMenuListEntry (Selection);
|
|
|
|
Selection->FormId = Statement->RefFormId;
|
|
Selection->QuestionId = Statement->RefQuestionId;
|
|
} else if (Statement->RefQuestionId != 0) {
|
|
//
|
|
// Goto another Question
|
|
//
|
|
Selection->QuestionId = Statement->RefQuestionId;
|
|
|
|
if ((Statement->QuestionFlags & EFI_IFR_FLAG_CALLBACK)) {
|
|
Selection->Action = UI_ACTION_REFRESH_FORM;
|
|
} else {
|
|
Repaint = TRUE;
|
|
NewLine = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case EFI_IFR_ACTION_OP:
|
|
//
|
|
// Process the Config string <ConfigResp>
|
|
//
|
|
Status = ProcessQuestionConfig (Selection, Statement);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// The action button may change some Question value, so refresh the form
|
|
//
|
|
Selection->Action = UI_ACTION_REFRESH_FORM;
|
|
break;
|
|
|
|
case EFI_IFR_RESET_BUTTON_OP:
|
|
//
|
|
// Reset Question to default value specified by DefaultId
|
|
//
|
|
ControlFlag = CfUiDefault;
|
|
DefaultId = Statement->DefaultId;
|
|
break;
|
|
|
|
default:
|
|
//
|
|
// Editable Questions: oneof, ordered list, checkbox, numeric, string, password
|
|
//
|
|
UpdateKeyHelp (MenuOption, TRUE);
|
|
Status = ProcessOptions (Selection, MenuOption, TRUE, &OptionString);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
Repaint = TRUE;
|
|
NewLine = TRUE;
|
|
break;
|
|
}
|
|
|
|
if (OptionString != NULL) {
|
|
PrintStringAt (LocalScreen.LeftColumn + gPromptBlockWidth + 1, MenuOption->Row, OptionString);
|
|
gBS->FreePool (OptionString);
|
|
}
|
|
|
|
Selection->Action = UI_ACTION_REFRESH_FORM;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case CfUiReset:
|
|
//
|
|
// We are going to leave current FormSet, so check uncommited data in this FormSet
|
|
//
|
|
ControlFlag = CfCheckSelection;
|
|
|
|
if (gClassOfVfr == EFI_FRONT_PAGE_SUBCLASS) {
|
|
//
|
|
// There is no parent menu for FrontPage
|
|
//
|
|
Selection->Action = UI_ACTION_NONE;
|
|
Selection->Statement = MenuOption->ThisTag;
|
|
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;
|
|
|
|
Selection->Action = UI_ACTION_NONE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
|
|
gST->ConOut->EnableCursor (gST->ConOut, TRUE);
|
|
|
|
UiFreeMenuList ();
|
|
gST->ConOut->ClearScreen (gST->ConOut);
|
|
return EFI_SUCCESS;
|
|
|
|
case CfUiLeft:
|
|
ControlFlag = CfCheckSelection;
|
|
if ((MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) || (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)) {
|
|
if (MenuOption->Sequence != 0) {
|
|
//
|
|
// In the middle or tail of the Date/Time op-code set, go left.
|
|
//
|
|
NewPos = NewPos->BackLink;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case CfUiRight:
|
|
ControlFlag = CfCheckSelection;
|
|
if ((MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) || (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)) {
|
|
if (MenuOption->Sequence != 2) {
|
|
//
|
|
// In the middle or tail of the Date/Time op-code set, go left.
|
|
//
|
|
NewPos = NewPos->ForwardLink;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case CfUiUp:
|
|
ControlFlag = CfCheckSelection;
|
|
|
|
SavedListEntry = TopOfScreen;
|
|
|
|
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 = MENU_OPTION_FROM_LINK (NewPos);
|
|
DistanceValue = PreviousMenuOption->Skip;
|
|
|
|
//
|
|
// 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.
|
|
//
|
|
DistanceValue += AdjustDateAndTimePosition (TRUE, &NewPos);
|
|
|
|
//
|
|
// Check the previous menu entry to see if it was a zero-length advance. If it was,
|
|
// don't worry about a redraw.
|
|
//
|
|
if ((INTN) MenuOption->Row - (INTN) DistanceValue < (INTN) TopRow) {
|
|
Repaint = TRUE;
|
|
TopOfScreen = NewPos;
|
|
}
|
|
|
|
Difference = MoveToNextStatement (TRUE, &NewPos);
|
|
if ((INTN) MenuOption->Row - (INTN) DistanceValue < (INTN) TopRow) {
|
|
if (Difference > 0) {
|
|
//
|
|
// Previous focus MenuOption is above the TopOfScreen, so we need to scroll
|
|
//
|
|
TopOfScreen = NewPos;
|
|
Repaint = TRUE;
|
|
}
|
|
}
|
|
if (Difference < 0) {
|
|
//
|
|
// We want to goto previous MenuOption, but finally we go down.
|
|
// it means that we hit the begining MenuOption that can be focused
|
|
// so we simply scroll to the top
|
|
//
|
|
if (SavedListEntry != Menu.ForwardLink) {
|
|
TopOfScreen = Menu.ForwardLink;
|
|
Repaint = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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->QuestionFlags, FALSE);
|
|
} else {
|
|
SavedMenuOption = MenuOption;
|
|
MenuOption = MENU_OPTION_FROM_LINK (NewPos);
|
|
if (!IsSelectable (MenuOption)) {
|
|
//
|
|
// 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;
|
|
|
|
if (NewPos->BackLink == &Menu) {
|
|
NewLine = FALSE;
|
|
Repaint = FALSE;
|
|
break;
|
|
}
|
|
|
|
NewLine = TRUE;
|
|
Repaint = TRUE;
|
|
Link = TopOfScreen;
|
|
PreviousMenuOption = MENU_OPTION_FROM_LINK (Link);
|
|
Index = BottomRow;
|
|
while ((Index >= TopRow) && (Link->BackLink != &Menu)) {
|
|
Index = Index - PreviousMenuOption->Skip;
|
|
Link = Link->BackLink;
|
|
PreviousMenuOption = MENU_OPTION_FROM_LINK (Link);
|
|
}
|
|
|
|
TopOfScreen = Link;
|
|
Difference = MoveToNextStatement (TRUE, &Link);
|
|
if (Difference > 0) {
|
|
//
|
|
// The focus MenuOption is above the TopOfScreen
|
|
//
|
|
TopOfScreen = Link;
|
|
} else if (Difference < 0) {
|
|
//
|
|
// This happens when there is no MenuOption can be focused from
|
|
// Current MenuOption to the first MenuOption
|
|
//
|
|
TopOfScreen = Menu.ForwardLink;
|
|
}
|
|
Index += Difference;
|
|
if (Index < TopRow) {
|
|
MenuOption = NULL;
|
|
}
|
|
|
|
if (NewPos == Link) {
|
|
Repaint = FALSE;
|
|
NewLine = FALSE;
|
|
} else {
|
|
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.
|
|
//
|
|
AdjustDateAndTimePosition (TRUE, &TopOfScreen);
|
|
AdjustDateAndTimePosition (TRUE, &NewPos);
|
|
break;
|
|
|
|
case CfUiPageDown:
|
|
ControlFlag = CfCheckSelection;
|
|
|
|
if (NewPos->ForwardLink == &Menu) {
|
|
NewLine = FALSE;
|
|
Repaint = FALSE;
|
|
break;
|
|
}
|
|
|
|
NewLine = TRUE;
|
|
Repaint = TRUE;
|
|
Link = TopOfScreen;
|
|
NextMenuOption = MENU_OPTION_FROM_LINK (Link);
|
|
Index = TopRow;
|
|
while ((Index <= BottomRow) && (Link->ForwardLink != &Menu)) {
|
|
Index = Index + NextMenuOption->Skip;
|
|
Link = Link->ForwardLink;
|
|
NextMenuOption = MENU_OPTION_FROM_LINK (Link);
|
|
}
|
|
|
|
Index += MoveToNextStatement (FALSE, &Link);
|
|
if (Index > BottomRow) {
|
|
//
|
|
// There are more MenuOption needing scrolling
|
|
//
|
|
TopOfScreen = Link;
|
|
MenuOption = NULL;
|
|
}
|
|
if (NewPos == Link && Index <= BottomRow) {
|
|
//
|
|
// Finally we know that NewPos is the last MenuOption can be focused.
|
|
//
|
|
NewLine = FALSE;
|
|
Repaint = FALSE;
|
|
} else {
|
|
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 last page.
|
|
//
|
|
AdjustDateAndTimePosition (TRUE, &TopOfScreen);
|
|
AdjustDateAndTimePosition (TRUE, &NewPos);
|
|
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.
|
|
//
|
|
SavedListEntry = NewPos;
|
|
DistanceValue = AdjustDateAndTimePosition (FALSE, &NewPos);
|
|
|
|
if (NewPos->ForwardLink != &Menu) {
|
|
MenuOption = MENU_OPTION_FROM_LINK (NewPos);
|
|
NewLine = TRUE;
|
|
NewPos = NewPos->ForwardLink;
|
|
NextMenuOption = MENU_OPTION_FROM_LINK (NewPos);
|
|
|
|
DistanceValue += NextMenuOption->Skip;
|
|
DistanceValue += MoveToNextStatement (FALSE, &NewPos);
|
|
//
|
|
// An option might be multi-line, so we need to reflect that data in the overall skip value
|
|
//
|
|
UpdateOptionSkipLines (Selection, NextMenuOption, &OptionString, SkipValue);
|
|
|
|
Temp = MenuOption->Row + MenuOption->Skip + DistanceValue - 1;
|
|
if ((MenuOption->Row + MenuOption->Skip == BottomRow + 1) &&
|
|
(NextMenuOption->ThisTag->Operand == EFI_IFR_DATE_OP ||
|
|
NextMenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)
|
|
) {
|
|
Temp ++;
|
|
}
|
|
|
|
//
|
|
// If we are going to scroll, update TopOfScreen
|
|
//
|
|
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 = MENU_OPTION_FROM_LINK (TopOfScreen);
|
|
|
|
//
|
|
// If bottom op-code is more than one line or top op-code is more than one line
|
|
//
|
|
if ((DistanceValue > 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 = MENU_OPTION_FROM_LINK (TopOfScreen);
|
|
|
|
//
|
|
// 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 = MENU_OPTION_FROM_LINK (TopOfScreen);
|
|
if (Difference < (INTN) SavedMenuOption->Skip) {
|
|
Difference = SavedMenuOption->Skip - Difference - 1;
|
|
break;
|
|
} else {
|
|
if (Difference == (INTN) SavedMenuOption->Skip) {
|
|
TopOfScreen = TopOfScreen->ForwardLink;
|
|
SavedMenuOption = MENU_OPTION_FROM_LINK (TopOfScreen);
|
|
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;
|
|
}
|
|
|
|
MenuOption = MENU_OPTION_FROM_LINK (SavedListEntry);
|
|
|
|
UpdateStatusBar (INPUT_ERROR, MenuOption->ThisTag->QuestionFlags, FALSE);
|
|
|
|
} else {
|
|
SavedMenuOption = MenuOption;
|
|
MenuOption = MENU_OPTION_FROM_LINK (NewPos);
|
|
if (!IsSelectable (MenuOption)) {
|
|
//
|
|
// 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;
|
|
|
|
//
|
|
// Submit the form
|
|
//
|
|
Status = SubmitForm (Selection->FormSet, Selection->Form);
|
|
|
|
if (!EFI_ERROR (Status)) {
|
|
UpdateStatusBar (INPUT_ERROR, MenuOption->ThisTag->QuestionFlags, FALSE);
|
|
UpdateStatusBar (NV_UPDATE_REQUIRED, MenuOption->ThisTag->QuestionFlags, FALSE);
|
|
} else {
|
|
do {
|
|
CreateDialog (4, TRUE, 0, NULL, &Key, gEmptyString, gSaveFailed, gPressEnter, gEmptyString);
|
|
} while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
|
|
|
|
Repaint = TRUE;
|
|
NewLine = TRUE;
|
|
}
|
|
break;
|
|
|
|
case CfUiDefault:
|
|
ControlFlag = CfCheckSelection;
|
|
|
|
Status = ExtractFormDefault (Selection->FormSet, Selection->Form, DefaultId);
|
|
|
|
if (!EFI_ERROR (Status)) {
|
|
Selection->Action = UI_ACTION_REFRESH_FORM;
|
|
|
|
//
|
|
// Show NV update flag on status bar
|
|
//
|
|
gNvUpdateRequired = TRUE;
|
|
}
|
|
break;
|
|
|
|
case CfUiNoOperation:
|
|
ControlFlag = CfCheckSelection;
|
|
break;
|
|
|
|
case CfExit:
|
|
UiFreeRefreshList ();
|
|
|
|
gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
|
|
gST->ConOut->SetCursorPosition (gST->ConOut, 0, Row + 4);
|
|
gST->ConOut->EnableCursor (gST->ConOut, TRUE);
|
|
gST->ConOut->OutputString (gST->ConOut, L"\n");
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|