blob: f70feeb55f0e80aab6c40669c7b1aaeb08b465ca [file] [log] [blame]
/** @file
Implementation for handling user input from the User Interfaces.
Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include "FormDisplay.h"
/**
Get maximum and minimum info from this opcode.
@param OpCode Pointer to the current input opcode.
@param Minimum The minimum size info for this opcode.
@param Maximum The maximum size info for this opcode.
**/
VOID
GetFieldFromOp (
IN EFI_IFR_OP_HEADER *OpCode,
OUT UINTN *Minimum,
OUT UINTN *Maximum
)
{
EFI_IFR_STRING *StringOp;
EFI_IFR_PASSWORD *PasswordOp;
if (OpCode->OpCode == EFI_IFR_STRING_OP) {
StringOp = (EFI_IFR_STRING *)OpCode;
*Minimum = StringOp->MinSize;
*Maximum = StringOp->MaxSize;
} else if (OpCode->OpCode == EFI_IFR_PASSWORD_OP) {
PasswordOp = (EFI_IFR_PASSWORD *)OpCode;
*Minimum = PasswordOp->MinSize;
*Maximum = PasswordOp->MaxSize;
} else {
*Minimum = 0;
*Maximum = 0;
}
}
/**
Get string or password input from user.
@param MenuOption Pointer to the current input menu.
@param Prompt The prompt string shown on popup window.
@param StringPtr Old user input and destination for use input string.
@retval EFI_SUCCESS If string input is read successfully
@retval EFI_DEVICE_ERROR If operation fails
**/
EFI_STATUS
ReadString (
IN UI_MENU_OPTION *MenuOption,
IN CHAR16 *Prompt,
IN OUT CHAR16 *StringPtr
)
{
EFI_STATUS Status;
EFI_INPUT_KEY Key;
CHAR16 NullCharacter;
UINTN ScreenSize;
CHAR16 Space[2];
CHAR16 KeyPad[2];
CHAR16 *TempString;
CHAR16 *BufferedString;
UINTN Index;
UINTN Index2;
UINTN Count;
UINTN Start;
UINTN Top;
UINTN DimensionsWidth;
UINTN DimensionsHeight;
UINTN CurrentCursor;
BOOLEAN CursorVisible;
UINTN Minimum;
UINTN Maximum;
FORM_DISPLAY_ENGINE_STATEMENT *Question;
BOOLEAN IsPassword;
UINTN MaxLen;
DimensionsWidth = gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn;
DimensionsHeight = gStatementDimensions.BottomRow - gStatementDimensions.TopRow;
NullCharacter = CHAR_NULL;
ScreenSize = GetStringWidth (Prompt) / sizeof (CHAR16);
Space[0] = L' ';
Space[1] = CHAR_NULL;
Question = MenuOption->ThisTag;
GetFieldFromOp (Question->OpCode, &Minimum, &Maximum);
if (Question->OpCode->OpCode == EFI_IFR_PASSWORD_OP) {
IsPassword = TRUE;
} else {
IsPassword = FALSE;
}
MaxLen = Maximum + 1;
TempString = AllocateZeroPool (MaxLen * sizeof (CHAR16));
ASSERT (TempString);
if (ScreenSize < (Maximum + 1)) {
ScreenSize = Maximum + 1;
}
if ((ScreenSize + 2) > DimensionsWidth) {
ScreenSize = DimensionsWidth - 2;
}
BufferedString = AllocateZeroPool (ScreenSize * 2);
ASSERT (BufferedString);
Start = (DimensionsWidth - ScreenSize - 2) / 2 + gStatementDimensions.LeftColumn + 1;
Top = ((DimensionsHeight - 6) / 2) + gStatementDimensions.TopRow - 1;
//
// Display prompt for string
//
// CreateDialog (NULL, "", Prompt, Space, "", NULL);
CreateMultiStringPopUp (ScreenSize, 4, &NullCharacter, Prompt, Space, &NullCharacter);
gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY));
CursorVisible = gST->ConOut->Mode->CursorVisible;
gST->ConOut->EnableCursor (gST->ConOut, TRUE);
CurrentCursor = GetStringWidth (StringPtr) / 2 - 1;
if (CurrentCursor != 0) {
//
// Show the string which has beed saved before.
//
SetUnicodeMem (BufferedString, ScreenSize - 1, L' ');
PrintStringAt (Start + 1, Top + 3, BufferedString);
if ((GetStringWidth (StringPtr) / 2) > (DimensionsWidth - 2)) {
Index = (GetStringWidth (StringPtr) / 2) - DimensionsWidth + 2;
} else {
Index = 0;
}
if (IsPassword) {
gST->ConOut->SetCursorPosition (gST->ConOut, Start + 1, Top + 3);
}
for (Count = 0; Index + 1 < GetStringWidth (StringPtr) / 2; Index++, Count++) {
BufferedString[Count] = StringPtr[Index];
if (IsPassword) {
PrintCharAt ((UINTN)-1, (UINTN)-1, L'*');
}
}
if (!IsPassword) {
PrintStringAt (Start + 1, Top + 3, BufferedString);
}
gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
gST->ConOut->SetCursorPosition (gST->ConOut, Start + GetStringWidth (StringPtr) / 2, Top + 3);
}
do {
Status = WaitForKeyStroke (&Key);
ASSERT_EFI_ERROR (Status);
gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY));
switch (Key.UnicodeChar) {
case CHAR_NULL:
switch (Key.ScanCode) {
case SCAN_LEFT:
if (CurrentCursor > 0) {
CurrentCursor--;
}
break;
case SCAN_RIGHT:
if (CurrentCursor < (GetStringWidth (StringPtr) / 2 - 1)) {
CurrentCursor++;
}
break;
case SCAN_ESC:
FreePool (TempString);
FreePool (BufferedString);
gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
gST->ConOut->EnableCursor (gST->ConOut, CursorVisible);
return EFI_DEVICE_ERROR;
case SCAN_DELETE:
for (Index = CurrentCursor; StringPtr[Index] != CHAR_NULL; Index++) {
StringPtr[Index] = StringPtr[Index + 1];
PrintCharAt (Start + Index + 1, Top + 3, IsPassword && StringPtr[Index] != CHAR_NULL ? L'*' : StringPtr[Index]);
}
break;
default:
break;
}
break;
case CHAR_CARRIAGE_RETURN:
if (GetStringWidth (StringPtr) >= ((Minimum + 1) * sizeof (CHAR16))) {
FreePool (TempString);
FreePool (BufferedString);
gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
gST->ConOut->EnableCursor (gST->ConOut, CursorVisible);
return EFI_SUCCESS;
} else {
//
// Simply create a popup to tell the user that they had typed in too few characters.
// To save code space, we can then treat this as an error and return back to the menu.
//
do {
CreateDialog (&Key, &NullCharacter, gMiniString, gPressEnter, &NullCharacter, NULL);
} while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
FreePool (TempString);
FreePool (BufferedString);
gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
gST->ConOut->EnableCursor (gST->ConOut, CursorVisible);
return EFI_DEVICE_ERROR;
}
case CHAR_BACKSPACE:
if ((StringPtr[0] != CHAR_NULL) && (CurrentCursor != 0)) {
for (Index = 0; Index < CurrentCursor - 1; Index++) {
TempString[Index] = StringPtr[Index];
}
Count = GetStringWidth (StringPtr) / 2 - 1;
if (Count >= CurrentCursor) {
for (Index = CurrentCursor - 1, Index2 = CurrentCursor; Index2 < Count; Index++, Index2++) {
TempString[Index] = StringPtr[Index2];
}
TempString[Index] = CHAR_NULL;
}
//
// Effectively truncate string by 1 character
//
StrCpyS (StringPtr, MaxLen, TempString);
CurrentCursor--;
}
default:
//
// If it is the beginning of the string, don't worry about checking maximum limits
//
if ((StringPtr[0] == CHAR_NULL) && (Key.UnicodeChar != CHAR_BACKSPACE)) {
StrnCpyS (StringPtr, MaxLen, &Key.UnicodeChar, 1);
CurrentCursor++;
} else if ((GetStringWidth (StringPtr) < ((Maximum + 1) * sizeof (CHAR16))) && (Key.UnicodeChar != CHAR_BACKSPACE)) {
KeyPad[0] = Key.UnicodeChar;
KeyPad[1] = CHAR_NULL;
Count = GetStringWidth (StringPtr) / 2 - 1;
if (CurrentCursor < Count) {
for (Index = 0; Index < CurrentCursor; Index++) {
TempString[Index] = StringPtr[Index];
}
TempString[Index] = CHAR_NULL;
StrCatS (TempString, MaxLen, KeyPad);
StrCatS (TempString, MaxLen, StringPtr + CurrentCursor);
StrCpyS (StringPtr, MaxLen, TempString);
} else {
StrCatS (StringPtr, MaxLen, KeyPad);
}
CurrentCursor++;
}
//
// 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, ScreenSize - 1, L' ');
PrintStringAt (Start + 1, Top + 3, BufferedString);
if ((GetStringWidth (StringPtr) / 2) > (DimensionsWidth - 2)) {
Index = (GetStringWidth (StringPtr) / 2) - DimensionsWidth + 2;
} else {
Index = 0;
}
if (IsPassword) {
gST->ConOut->SetCursorPosition (gST->ConOut, Start + 1, Top + 3);
}
for (Count = 0; Index + 1 < GetStringWidth (StringPtr) / 2; Index++, Count++) {
BufferedString[Count] = StringPtr[Index];
if (IsPassword) {
PrintCharAt ((UINTN)-1, (UINTN)-1, L'*');
}
}
if (!IsPassword) {
PrintStringAt (Start + 1, Top + 3, BufferedString);
}
break;
}
gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
gST->ConOut->SetCursorPosition (gST->ConOut, Start + CurrentCursor + 1, Top + 3);
} while (TRUE);
}
/**
Adjust the value to the correct one. Rules follow the sample:
like: Year change: 2012.02.29 -> 2013.02.29 -> 2013.02.01
Month change: 2013.03.29 -> 2013.02.29 -> 2013.02.28
@param QuestionValue Pointer to current question.
@param Sequence The sequence of the field in the question.
**/
VOID
AdjustQuestionValue (
IN EFI_HII_VALUE *QuestionValue,
IN UINT8 Sequence
)
{
UINT8 Month;
UINT16 Year;
UINT8 Maximum;
UINT8 Minimum;
Month = QuestionValue->Value.date.Month;
Year = QuestionValue->Value.date.Year;
Minimum = 1;
switch (Month) {
case 2:
if (((Year % 4) == 0) && (((Year % 100) != 0) || ((Year % 400) == 0))) {
Maximum = 29;
} else {
Maximum = 28;
}
break;
case 4:
case 6:
case 9:
case 11:
Maximum = 30;
break;
default:
Maximum = 31;
break;
}
//
// Change the month area.
//
if (Sequence == 0) {
if (QuestionValue->Value.date.Day > Maximum) {
QuestionValue->Value.date.Day = Maximum;
}
}
//
// Change the Year area.
//
if (Sequence == 2) {
if (QuestionValue->Value.date.Day > Maximum) {
QuestionValue->Value.date.Day = Minimum;
}
}
}
/**
Get field info from numeric opcode.
@param OpCode Pointer to the current input opcode.
@param IntInput Whether question shows with EFI_IFR_DISPLAY_INT_DEC type.
@param QuestionValue Input question value, with EFI_HII_VALUE type.
@param Value Return question value, always return UINT64 type.
@param Minimum The minimum size info for this opcode.
@param Maximum The maximum size info for this opcode.
@param Step The step size info for this opcode.
@param StorageWidth The storage width info for this opcode.
**/
VOID
GetValueFromNum (
IN EFI_IFR_OP_HEADER *OpCode,
IN BOOLEAN IntInput,
IN EFI_HII_VALUE *QuestionValue,
OUT UINT64 *Value,
OUT UINT64 *Minimum,
OUT UINT64 *Maximum,
OUT UINT64 *Step,
OUT UINT16 *StorageWidth
)
{
EFI_IFR_NUMERIC *NumericOp;
NumericOp = (EFI_IFR_NUMERIC *)OpCode;
switch (NumericOp->Flags & EFI_IFR_NUMERIC_SIZE) {
case EFI_IFR_NUMERIC_SIZE_1:
if (IntInput) {
*Minimum = (INT64)(INT8)NumericOp->data.u8.MinValue;
*Maximum = (INT64)(INT8)NumericOp->data.u8.MaxValue;
*Value = (INT64)(INT8)QuestionValue->Value.u8;
} else {
*Minimum = NumericOp->data.u8.MinValue;
*Maximum = NumericOp->data.u8.MaxValue;
*Value = QuestionValue->Value.u8;
}
*Step = NumericOp->data.u8.Step;
*StorageWidth = (UINT16)sizeof (UINT8);
break;
case EFI_IFR_NUMERIC_SIZE_2:
if (IntInput) {
*Minimum = (INT64)(INT16)NumericOp->data.u16.MinValue;
*Maximum = (INT64)(INT16)NumericOp->data.u16.MaxValue;
*Value = (INT64)(INT16)QuestionValue->Value.u16;
} else {
*Minimum = NumericOp->data.u16.MinValue;
*Maximum = NumericOp->data.u16.MaxValue;
*Value = QuestionValue->Value.u16;
}
*Step = NumericOp->data.u16.Step;
*StorageWidth = (UINT16)sizeof (UINT16);
break;
case EFI_IFR_NUMERIC_SIZE_4:
if (IntInput) {
*Minimum = (INT64)(INT32)NumericOp->data.u32.MinValue;
*Maximum = (INT64)(INT32)NumericOp->data.u32.MaxValue;
*Value = (INT64)(INT32)QuestionValue->Value.u32;
} else {
*Minimum = NumericOp->data.u32.MinValue;
*Maximum = NumericOp->data.u32.MaxValue;
*Value = QuestionValue->Value.u32;
}
*Step = NumericOp->data.u32.Step;
*StorageWidth = (UINT16)sizeof (UINT32);
break;
case EFI_IFR_NUMERIC_SIZE_8:
if (IntInput) {
*Minimum = (INT64)NumericOp->data.u64.MinValue;
*Maximum = (INT64)NumericOp->data.u64.MaxValue;
*Value = (INT64)QuestionValue->Value.u64;
} else {
*Minimum = NumericOp->data.u64.MinValue;
*Maximum = NumericOp->data.u64.MaxValue;
*Value = QuestionValue->Value.u64;
}
*Step = NumericOp->data.u64.Step;
*StorageWidth = (UINT16)sizeof (UINT64);
break;
default:
break;
}
if (*Maximum == 0) {
*Maximum = (UINT64)-1;
}
}
/**
This routine reads a numeric value from the user input.
@param MenuOption Pointer to the current input menu.
@retval EFI_SUCCESS If numerical input is read successfully
@retval EFI_DEVICE_ERROR If operation fails
**/
EFI_STATUS
GetNumericInput (
IN UI_MENU_OPTION *MenuOption
)
{
UINTN Column;
UINTN Row;
CHAR16 InputText[MAX_NUMERIC_INPUT_WIDTH];
CHAR16 FormattedNumber[MAX_NUMERIC_INPUT_WIDTH - 1];
UINT64 PreviousNumber[MAX_NUMERIC_INPUT_WIDTH - 3];
UINTN Count;
UINTN Loop;
BOOLEAN ManualInput;
BOOLEAN HexInput;
BOOLEAN IntInput;
BOOLEAN Negative;
BOOLEAN ValidateFail;
BOOLEAN DateOrTime;
UINTN InputWidth;
UINT64 EditValue;
UINT64 Step;
UINT64 Minimum;
UINT64 Maximum;
UINTN EraseLen;
UINT8 Digital;
EFI_INPUT_KEY Key;
EFI_HII_VALUE *QuestionValue;
FORM_DISPLAY_ENGINE_STATEMENT *Question;
EFI_IFR_NUMERIC *NumericOp;
UINT16 StorageWidth;
Column = MenuOption->OptCol;
Row = MenuOption->Row;
PreviousNumber[0] = 0;
Count = 0;
InputWidth = 0;
Digital = 0;
StorageWidth = 0;
Minimum = 0;
Maximum = 0;
NumericOp = NULL;
IntInput = FALSE;
HexInput = FALSE;
Negative = FALSE;
ValidateFail = FALSE;
Question = MenuOption->ThisTag;
QuestionValue = &Question->CurrentValue;
ZeroMem (InputText, MAX_NUMERIC_INPUT_WIDTH * sizeof (CHAR16));
//
// Only two case, user can enter to this function: Enter and +/- case.
// In Enter case, gDirection = 0; in +/- case, gDirection = SCAN_LEFT/SCAN_WRIGHT
//
ManualInput = (BOOLEAN)(gDirection == 0 ? TRUE : FALSE);
if ((Question->OpCode->OpCode == EFI_IFR_DATE_OP) || (Question->OpCode->OpCode == EFI_IFR_TIME_OP)) {
DateOrTime = TRUE;
} else {
DateOrTime = FALSE;
}
//
// Prepare Value to be edit
//
EraseLen = 0;
EditValue = 0;
if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) {
Step = 1;
Minimum = 1;
switch (MenuOption->Sequence) {
case 0:
Maximum = 12;
EraseLen = 4;
EditValue = QuestionValue->Value.date.Month;
break;
case 1:
switch (QuestionValue->Value.date.Month) {
case 2:
if (((QuestionValue->Value.date.Year % 4) == 0) &&
(((QuestionValue->Value.date.Year % 100) != 0) ||
((QuestionValue->Value.date.Year % 400) == 0)))
{
Maximum = 29;
} else {
Maximum = 28;
}
break;
case 4:
case 6:
case 9:
case 11:
Maximum = 30;
break;
default:
Maximum = 31;
break;
}
EraseLen = 3;
EditValue = QuestionValue->Value.date.Day;
break;
case 2:
Maximum = 0xffff;
EraseLen = 5;
EditValue = QuestionValue->Value.date.Year;
break;
default:
break;
}
} else if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) {
Step = 1;
Minimum = 0;
switch (MenuOption->Sequence) {
case 0:
Maximum = 23;
EraseLen = 4;
EditValue = QuestionValue->Value.time.Hour;
break;
case 1:
Maximum = 59;
EraseLen = 3;
EditValue = QuestionValue->Value.time.Minute;
break;
case 2:
Maximum = 59;
EraseLen = 3;
EditValue = QuestionValue->Value.time.Second;
break;
default:
break;
}
} else {
ASSERT (Question->OpCode->OpCode == EFI_IFR_NUMERIC_OP);
NumericOp = (EFI_IFR_NUMERIC *)Question->OpCode;
GetValueFromNum (Question->OpCode, (NumericOp->Flags & EFI_IFR_DISPLAY) == 0, QuestionValue, &EditValue, &Minimum, &Maximum, &Step, &StorageWidth);
EraseLen = gOptionBlockWidth;
}
if ((Question->OpCode->OpCode == EFI_IFR_NUMERIC_OP) && (NumericOp != NULL)) {
if ((NumericOp->Flags & EFI_IFR_DISPLAY) == EFI_IFR_DISPLAY_UINT_HEX) {
HexInput = TRUE;
} else if ((NumericOp->Flags & EFI_IFR_DISPLAY) == 0) {
//
// Display with EFI_IFR_DISPLAY_INT_DEC type. Support negative number.
//
IntInput = TRUE;
}
}
//
// Enter from "Enter" input, clear the old word showing.
//
if (ManualInput) {
if (Question->OpCode->OpCode == EFI_IFR_NUMERIC_OP) {
if (HexInput) {
InputWidth = StorageWidth * 2;
} else {
switch (StorageWidth) {
case 1:
InputWidth = 3;
break;
case 2:
InputWidth = 5;
break;
case 4:
InputWidth = 10;
break;
case 8:
InputWidth = 20;
break;
default:
InputWidth = 0;
break;
}
if (IntInput) {
//
// Support an extra '-' for negative number.
//
InputWidth += 1;
}
}
InputText[0] = LEFT_NUMERIC_DELIMITER;
SetUnicodeMem (InputText + 1, InputWidth, L' ');
ASSERT (InputWidth + 2 < MAX_NUMERIC_INPUT_WIDTH);
InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER;
InputText[InputWidth + 2] = L'\0';
PrintStringAt (Column, Row, InputText);
Column++;
}
if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) {
if (MenuOption->Sequence == 2) {
InputWidth = 4;
} else {
InputWidth = 2;
}
if (MenuOption->Sequence == 0) {
InputText[0] = LEFT_NUMERIC_DELIMITER;
SetUnicodeMem (InputText + 1, InputWidth, L' ');
InputText[InputWidth + 1] = DATE_SEPARATOR;
InputText[InputWidth + 2] = L'\0';
} else if (MenuOption->Sequence == 1) {
SetUnicodeMem (InputText, InputWidth, L' ');
InputText[InputWidth] = DATE_SEPARATOR;
InputText[InputWidth + 1] = L'\0';
} else {
SetUnicodeMem (InputText, InputWidth, L' ');
InputText[InputWidth] = RIGHT_NUMERIC_DELIMITER;
InputText[InputWidth + 1] = L'\0';
}
PrintStringAt (Column, Row, InputText);
if (MenuOption->Sequence == 0) {
Column++;
}
}
if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) {
InputWidth = 2;
if (MenuOption->Sequence == 0) {
InputText[0] = LEFT_NUMERIC_DELIMITER;
SetUnicodeMem (InputText + 1, InputWidth, L' ');
InputText[InputWidth + 1] = TIME_SEPARATOR;
InputText[InputWidth + 2] = L'\0';
} else if (MenuOption->Sequence == 1) {
SetUnicodeMem (InputText, InputWidth, L' ');
InputText[InputWidth] = TIME_SEPARATOR;
InputText[InputWidth + 1] = L'\0';
} else {
SetUnicodeMem (InputText, InputWidth, L' ');
InputText[InputWidth] = RIGHT_NUMERIC_DELIMITER;
InputText[InputWidth + 1] = L'\0';
}
PrintStringAt (Column, Row, InputText);
if (MenuOption->Sequence == 0) {
Column++;
}
}
}
//
// First time we enter this handler, we need to check to see if
// we were passed an increment or decrement directive
//
do {
Key.UnicodeChar = CHAR_NULL;
if (gDirection != 0) {
Key.ScanCode = gDirection;
gDirection = 0;
goto TheKey2;
}
WaitForKeyStroke (&Key);
TheKey2:
switch (Key.UnicodeChar) {
case '+':
case '-':
if (ManualInput && IntInput) {
//
// In Manual input mode, check whether input the negative flag.
//
if (Key.UnicodeChar == '-') {
if (Negative) {
break;
}
Negative = TRUE;
PrintCharAt (Column++, Row, Key.UnicodeChar);
}
} else {
if (Key.UnicodeChar == '+') {
Key.ScanCode = SCAN_RIGHT;
} else {
Key.ScanCode = SCAN_LEFT;
}
Key.UnicodeChar = CHAR_NULL;
goto TheKey2;
}
break;
case CHAR_NULL:
switch (Key.ScanCode) {
case SCAN_LEFT:
case SCAN_RIGHT:
if (DateOrTime && !ManualInput) {
//
// By setting this value, we will return back to the caller.
// We need to do this since an auto-refresh will destroy the adjustment
// based on what the real-time-clock is showing. So we always commit
// upon changing the value.
//
gDirection = SCAN_DOWN;
}
if ((Step != 0) && !ManualInput) {
if (Key.ScanCode == SCAN_LEFT) {
if (IntInput) {
if ((INT64)EditValue >= (INT64)Minimum + (INT64)Step) {
EditValue = EditValue - Step;
} else if ((INT64)EditValue > (INT64)Minimum) {
EditValue = Minimum;
} else {
EditValue = Maximum;
}
} else {
if (EditValue >= Minimum + Step) {
EditValue = EditValue - Step;
} else if (EditValue > Minimum) {
EditValue = Minimum;
} else {
EditValue = Maximum;
}
}
} else if (Key.ScanCode == SCAN_RIGHT) {
if (IntInput) {
if ((INT64)EditValue + (INT64)Step <= (INT64)Maximum) {
EditValue = EditValue + Step;
} else if ((INT64)EditValue < (INT64)Maximum) {
EditValue = Maximum;
} else {
EditValue = Minimum;
}
} else {
if (EditValue + Step <= Maximum) {
EditValue = EditValue + Step;
} else if (EditValue < Maximum) {
EditValue = Maximum;
} else {
EditValue = Minimum;
}
}
}
ZeroMem (FormattedNumber, 21 * sizeof (CHAR16));
if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) {
if (MenuOption->Sequence == 2) {
//
// Year
//
UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%04d", (UINT16)EditValue);
} else {
//
// Month/Day
//
UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", (UINT8)EditValue);
}
if (MenuOption->Sequence == 0) {
ASSERT (EraseLen >= 2);
FormattedNumber[EraseLen - 2] = DATE_SEPARATOR;
} else if (MenuOption->Sequence == 1) {
ASSERT (EraseLen >= 1);
FormattedNumber[EraseLen - 1] = DATE_SEPARATOR;
}
} else if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) {
UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", (UINT8)EditValue);
if (MenuOption->Sequence == 0) {
ASSERT (EraseLen >= 2);
FormattedNumber[EraseLen - 2] = TIME_SEPARATOR;
} else if (MenuOption->Sequence == 1) {
ASSERT (EraseLen >= 1);
FormattedNumber[EraseLen - 1] = TIME_SEPARATOR;
}
} else {
QuestionValue->Value.u64 = EditValue;
PrintFormattedNumber (Question, FormattedNumber, 21 * sizeof (CHAR16));
}
gST->ConOut->SetAttribute (gST->ConOut, GetFieldTextColor ());
for (Loop = 0; Loop < EraseLen; Loop++) {
PrintStringAt (MenuOption->OptCol + Loop, MenuOption->Row, L" ");
}
gST->ConOut->SetAttribute (gST->ConOut, GetHighlightTextColor ());
if (MenuOption->Sequence == 0) {
PrintCharAt (MenuOption->OptCol, Row, LEFT_NUMERIC_DELIMITER);
Column = MenuOption->OptCol + 1;
}
PrintStringAt (Column, Row, FormattedNumber);
if (!DateOrTime || (MenuOption->Sequence == 2)) {
PrintCharAt ((UINTN)-1, (UINTN)-1, RIGHT_NUMERIC_DELIMITER);
}
}
goto EnterCarriageReturn;
case SCAN_UP:
case SCAN_DOWN:
goto EnterCarriageReturn;
case SCAN_ESC:
return EFI_DEVICE_ERROR;
default:
break;
}
break;
EnterCarriageReturn:
case CHAR_CARRIAGE_RETURN:
//
// Validate input value with Minimum value.
//
ValidateFail = FALSE;
if (IntInput) {
//
// After user input Enter, need to check whether the input value.
// If input a negative value, should compare with maximum value.
// else compare with the minimum value.
//
if (Negative) {
ValidateFail = (INT64)EditValue > (INT64)Maximum ? TRUE : FALSE;
} else {
ValidateFail = (INT64)EditValue < (INT64)Minimum ? TRUE : FALSE;
}
if (ValidateFail) {
UpdateStatusBar (INPUT_ERROR, TRUE);
break;
}
} else if (EditValue < Minimum) {
UpdateStatusBar (INPUT_ERROR, TRUE);
break;
}
UpdateStatusBar (INPUT_ERROR, FALSE);
CopyMem (&gUserInput->InputValue, &Question->CurrentValue, sizeof (EFI_HII_VALUE));
QuestionValue = &gUserInput->InputValue;
//
// Store Edit value back to Question
//
if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) {
switch (MenuOption->Sequence) {
case 0:
QuestionValue->Value.date.Month = (UINT8)EditValue;
break;
case 1:
QuestionValue->Value.date.Day = (UINT8)EditValue;
break;
case 2:
QuestionValue->Value.date.Year = (UINT16)EditValue;
break;
default:
break;
}
} else if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) {
switch (MenuOption->Sequence) {
case 0:
QuestionValue->Value.time.Hour = (UINT8)EditValue;
break;
case 1:
QuestionValue->Value.time.Minute = (UINT8)EditValue;
break;
case 2:
QuestionValue->Value.time.Second = (UINT8)EditValue;
break;
default:
break;
}
} else {
//
// Numeric
//
QuestionValue->Value.u64 = EditValue;
}
//
// Adjust the value to the correct one.
// Sample like: 2012.02.29 -> 2013.02.29 -> 2013.02.01
// 2013.03.29 -> 2013.02.29 -> 2013.02.28
//
if ((Question->OpCode->OpCode == EFI_IFR_DATE_OP) &&
((MenuOption->Sequence == 0) || (MenuOption->Sequence == 2)))
{
AdjustQuestionValue (QuestionValue, (UINT8)MenuOption->Sequence);
}
return EFI_SUCCESS;
case CHAR_BACKSPACE:
if (ManualInput) {
if (Count == 0) {
if (Negative) {
Negative = FALSE;
Column--;
PrintStringAt (Column, Row, L" ");
}
break;
}
//
// Remove a character
//
EditValue = PreviousNumber[Count - 1];
UpdateStatusBar (INPUT_ERROR, FALSE);
Count--;
Column--;
PrintStringAt (Column, Row, L" ");
}
break;
default:
if (ManualInput) {
if (HexInput) {
if ((Key.UnicodeChar >= L'0') && (Key.UnicodeChar <= L'9')) {
Digital = (UINT8)(Key.UnicodeChar - L'0');
} else if ((Key.UnicodeChar >= L'A') && (Key.UnicodeChar <= L'F')) {
Digital = (UINT8)(Key.UnicodeChar - L'A' + 0x0A);
} else if ((Key.UnicodeChar >= L'a') && (Key.UnicodeChar <= L'f')) {
Digital = (UINT8)(Key.UnicodeChar - L'a' + 0x0A);
} else {
UpdateStatusBar (INPUT_ERROR, TRUE);
break;
}
} else {
if ((Key.UnicodeChar > L'9') || (Key.UnicodeChar < L'0')) {
UpdateStatusBar (INPUT_ERROR, TRUE);
break;
}
}
//
// If Count exceed input width, there is no way more is valid
//
if (Count >= InputWidth) {
break;
}
//
// Someone typed something valid!
//
if (Count != 0) {
if (HexInput) {
EditValue = LShiftU64 (EditValue, 4) + Digital;
} else if (IntInput && Negative) {
//
// Save the negative number.
//
EditValue = ~(MultU64x32 (~(EditValue - 1), 10) + (Key.UnicodeChar - L'0')) + 1;
} else {
EditValue = MultU64x32 (EditValue, 10) + (Key.UnicodeChar - L'0');
}
} else {
if (HexInput) {
EditValue = Digital;
} else if (IntInput && Negative) {
//
// Save the negative number.
//
EditValue = ~(Key.UnicodeChar - L'0') + 1;
} else {
EditValue = Key.UnicodeChar - L'0';
}
}
if (IntInput) {
ValidateFail = FALSE;
//
// When user input a new value, should check the current value.
// If user input a negative value, should compare it with minimum
// value, else compare it with maximum value.
//
if (Negative) {
ValidateFail = (INT64)EditValue < (INT64)Minimum ? TRUE : FALSE;
} else {
ValidateFail = (INT64)EditValue > (INT64)Maximum ? TRUE : FALSE;
}
if (ValidateFail) {
UpdateStatusBar (INPUT_ERROR, TRUE);
ASSERT (Count < ARRAY_SIZE (PreviousNumber));
EditValue = PreviousNumber[Count];
break;
}
} else {
if (EditValue > Maximum) {
UpdateStatusBar (INPUT_ERROR, TRUE);
ASSERT (Count < ARRAY_SIZE (PreviousNumber));
EditValue = PreviousNumber[Count];
break;
}
}
UpdateStatusBar (INPUT_ERROR, FALSE);
Count++;
ASSERT (Count < (ARRAY_SIZE (PreviousNumber)));
PreviousNumber[Count] = EditValue;
gST->ConOut->SetAttribute (gST->ConOut, GetHighlightTextColor ());
PrintCharAt (Column, Row, Key.UnicodeChar);
Column++;
}
break;
}
} while (TRUE);
}
/**
Adjust option order base on the question value.
@param Question Pointer to current question.
@param PopUpMenuLines The line number of the pop up menu.
@retval EFI_SUCCESS If Option input is processed successfully
@retval EFI_DEVICE_ERROR If operation fails
**/
EFI_STATUS
AdjustOptionOrder (
IN FORM_DISPLAY_ENGINE_STATEMENT *Question,
OUT UINTN *PopUpMenuLines
)
{
UINTN Index;
EFI_IFR_ORDERED_LIST *OrderList;
UINT8 *ValueArray;
UINT8 ValueType;
LIST_ENTRY *Link;
DISPLAY_QUESTION_OPTION *OneOfOption;
EFI_HII_VALUE *HiiValueArray;
Link = GetFirstNode (&Question->OptionListHead);
OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
ValueArray = Question->CurrentValue.Buffer;
ValueType = OneOfOption->OptionOpCode->Type;
OrderList = (EFI_IFR_ORDERED_LIST *)Question->OpCode;
for (Index = 0; Index < OrderList->MaxContainers; Index++) {
if (GetArrayData (ValueArray, ValueType, Index) == 0) {
break;
}
}
*PopUpMenuLines = Index;
//
// Prepare HiiValue array
//
HiiValueArray = AllocateZeroPool (*PopUpMenuLines * sizeof (EFI_HII_VALUE));
ASSERT (HiiValueArray != NULL);
for (Index = 0; Index < *PopUpMenuLines; Index++) {
HiiValueArray[Index].Type = ValueType;
HiiValueArray[Index].Value.u64 = GetArrayData (ValueArray, ValueType, Index);
}
for (Index = 0; Index < *PopUpMenuLines; Index++) {
OneOfOption = ValueToOption (Question, &HiiValueArray[*PopUpMenuLines - Index - 1]);
if (OneOfOption == NULL) {
return EFI_NOT_FOUND;
}
RemoveEntryList (&OneOfOption->Link);
//
// Insert to head.
//
InsertHeadList (&Question->OptionListHead, &OneOfOption->Link);
}
FreePool (HiiValueArray);
return EFI_SUCCESS;
}
/**
Base on the type to compare the value.
@param Value1 The first value need to compare.
@param Value2 The second value need to compare.
@param Type The value type for above two values.
@retval TRUE The two value are same.
@retval FALSE The two value are different.
**/
BOOLEAN
IsValuesEqual (
IN EFI_IFR_TYPE_VALUE *Value1,
IN EFI_IFR_TYPE_VALUE *Value2,
IN UINT8 Type
)
{
switch (Type) {
case EFI_IFR_TYPE_BOOLEAN:
case EFI_IFR_TYPE_NUM_SIZE_8:
return (BOOLEAN)(Value1->u8 == Value2->u8);
case EFI_IFR_TYPE_NUM_SIZE_16:
return (BOOLEAN)(Value1->u16 == Value2->u16);
case EFI_IFR_TYPE_NUM_SIZE_32:
return (BOOLEAN)(Value1->u32 == Value2->u32);
case EFI_IFR_TYPE_NUM_SIZE_64:
return (BOOLEAN)(Value1->u64 == Value2->u64);
default:
ASSERT (FALSE);
return FALSE;
}
}
/**
Base on the type to set the value.
@param Dest The dest value.
@param Source The source value.
@param Type The value type for above two values.
**/
VOID
SetValuesByType (
OUT EFI_IFR_TYPE_VALUE *Dest,
IN EFI_IFR_TYPE_VALUE *Source,
IN UINT8 Type
)
{
switch (Type) {
case EFI_IFR_TYPE_BOOLEAN:
Dest->b = Source->b;
break;
case EFI_IFR_TYPE_NUM_SIZE_8:
Dest->u8 = Source->u8;
break;
case EFI_IFR_TYPE_NUM_SIZE_16:
Dest->u16 = Source->u16;
break;
case EFI_IFR_TYPE_NUM_SIZE_32:
Dest->u32 = Source->u32;
break;
case EFI_IFR_TYPE_NUM_SIZE_64:
Dest->u64 = Source->u64;
break;
default:
ASSERT (FALSE);
break;
}
}
/**
Get selection for OneOf and OrderedList (Left/Right will be ignored).
@param MenuOption Pointer to the current input menu.
@retval EFI_SUCCESS If Option input is processed successfully
@retval EFI_DEVICE_ERROR If operation fails
**/
EFI_STATUS
GetSelectionInputPopUp (
IN UI_MENU_OPTION *MenuOption
)
{
EFI_INPUT_KEY Key;
UINTN Index;
CHAR16 *StringPtr;
CHAR16 *TempStringPtr;
UINTN Index2;
UINTN TopOptionIndex;
UINTN HighlightOptionIndex;
UINTN Start;
UINTN End;
UINTN Top;
UINTN Bottom;
UINTN PopUpMenuLines;
UINTN MenuLinesInView;
UINTN PopUpWidth;
CHAR16 Character;
INT32 SavedAttribute;
BOOLEAN ShowDownArrow;
BOOLEAN ShowUpArrow;
UINTN DimensionsWidth;
LIST_ENTRY *Link;
BOOLEAN OrderedList;
UINT8 *ValueArray;
UINT8 *ReturnValue;
UINT8 ValueType;
EFI_HII_VALUE HiiValue;
DISPLAY_QUESTION_OPTION *OneOfOption;
DISPLAY_QUESTION_OPTION *CurrentOption;
FORM_DISPLAY_ENGINE_STATEMENT *Question;
INTN Result;
EFI_IFR_ORDERED_LIST *OrderList;
DimensionsWidth = gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn;
ValueArray = NULL;
ValueType = 0;
CurrentOption = NULL;
ShowDownArrow = FALSE;
ShowUpArrow = FALSE;
ZeroMem (&HiiValue, sizeof (EFI_HII_VALUE));
Question = MenuOption->ThisTag;
if (Question->OpCode->OpCode == EFI_IFR_ORDERED_LIST_OP) {
Link = GetFirstNode (&Question->OptionListHead);
OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
ValueArray = Question->CurrentValue.Buffer;
ValueType = OneOfOption->OptionOpCode->Type;
OrderedList = TRUE;
OrderList = (EFI_IFR_ORDERED_LIST *)Question->OpCode;
} else {
OrderedList = FALSE;
OrderList = NULL;
}
//
// Calculate Option count
//
PopUpMenuLines = 0;
if (OrderedList) {
AdjustOptionOrder (Question, &PopUpMenuLines);
} else {
Link = GetFirstNode (&Question->OptionListHead);
while (!IsNull (&Question->OptionListHead, Link)) {
OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
PopUpMenuLines++;
Link = GetNextNode (&Question->OptionListHead, Link);
}
}
//
// Get the number of one of options present and its size
//
PopUpWidth = 0;
HighlightOptionIndex = 0;
Link = GetFirstNode (&Question->OptionListHead);
for (Index = 0; Index < PopUpMenuLines; Index++) {
OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle);
if (StrLen (StringPtr) > PopUpWidth) {
PopUpWidth = StrLen (StringPtr);
}
FreePool (StringPtr);
HiiValue.Type = OneOfOption->OptionOpCode->Type;
SetValuesByType (&HiiValue.Value, &OneOfOption->OptionOpCode->Value, HiiValue.Type);
if (!OrderedList && (CompareHiiValue (&Question->CurrentValue, &HiiValue, &Result, NULL) == EFI_SUCCESS) && (Result == 0)) {
//
// Find current selected Option for OneOf
//
HighlightOptionIndex = Index;
}
Link = GetNextNode (&Question->OptionListHead, Link);
}
//
// Perform popup menu initialization.
//
PopUpWidth = PopUpWidth + POPUP_PAD_SPACE_COUNT;
SavedAttribute = gST->ConOut->Mode->Attribute;
gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ());
if ((PopUpWidth + POPUP_FRAME_WIDTH) > DimensionsWidth) {
PopUpWidth = DimensionsWidth - POPUP_FRAME_WIDTH;
}
Start = (DimensionsWidth - PopUpWidth - POPUP_FRAME_WIDTH) / 2 + gStatementDimensions.LeftColumn;
End = Start + PopUpWidth + POPUP_FRAME_WIDTH;
Top = gStatementDimensions.TopRow;
Bottom = gStatementDimensions.BottomRow - 1;
MenuLinesInView = Bottom - Top - 1;
if (MenuLinesInView >= PopUpMenuLines) {
Top = Top + (MenuLinesInView - PopUpMenuLines) / 2;
Bottom = Top + PopUpMenuLines + 1;
} else {
ShowDownArrow = TRUE;
}
if (HighlightOptionIndex > (MenuLinesInView - 1)) {
TopOptionIndex = HighlightOptionIndex - MenuLinesInView + 1;
} else {
TopOptionIndex = 0;
}
do {
//
// Clear that portion of the screen
//
ClearLines (Start, End, Top, Bottom, GetPopupColor ());
//
// Draw "One of" pop-up menu
//
Character = BOXDRAW_DOWN_RIGHT;
PrintCharAt (Start, Top, Character);
for (Index = Start; Index + 2 < End; Index++) {
if ((ShowUpArrow) && ((Index + 1) == (Start + End) / 2)) {
Character = GEOMETRICSHAPE_UP_TRIANGLE;
} else {
Character = BOXDRAW_HORIZONTAL;
}
PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
}
Character = BOXDRAW_DOWN_LEFT;
PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
Character = BOXDRAW_VERTICAL;
for (Index = Top + 1; Index < Bottom; Index++) {
PrintCharAt (Start, Index, Character);
PrintCharAt (End - 1, Index, Character);
}
//
// Move to top Option
//
Link = GetFirstNode (&Question->OptionListHead);
for (Index = 0; Index < TopOptionIndex; Index++) {
Link = GetNextNode (&Question->OptionListHead, Link);
}
//
// Display the One of options
//
Index2 = Top + 1;
for (Index = TopOptionIndex; (Index < PopUpMenuLines) && (Index2 < Bottom); Index++) {
OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
Link = GetNextNode (&Question->OptionListHead, Link);
StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle);
ASSERT (StringPtr != NULL);
//
// If the string occupies multiple lines, truncate it to fit in one line,
// and append a "..." for indication.
//
if (StrLen (StringPtr) > (PopUpWidth - 1)) {
TempStringPtr = AllocateZeroPool (sizeof (CHAR16) * (PopUpWidth - 1));
ASSERT (TempStringPtr != NULL);
CopyMem (TempStringPtr, StringPtr, (sizeof (CHAR16) * (PopUpWidth - 5)));
FreePool (StringPtr);
StringPtr = TempStringPtr;
StrCatS (StringPtr, PopUpWidth - 1, L"...");
}
if (Index == HighlightOptionIndex) {
//
// Highlight the selected one
//
CurrentOption = OneOfOption;
gST->ConOut->SetAttribute (gST->ConOut, GetPickListColor ());
PrintStringAt (Start + 2, Index2, StringPtr);
gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ());
} else {
gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ());
PrintStringAt (Start + 2, Index2, StringPtr);
}
Index2++;
FreePool (StringPtr);
}
Character = BOXDRAW_UP_RIGHT;
PrintCharAt (Start, Bottom, Character);
for (Index = Start; Index + 2 < End; Index++) {
if ((ShowDownArrow) && ((Index + 1) == (Start + End) / 2)) {
Character = GEOMETRICSHAPE_DOWN_TRIANGLE;
} else {
Character = BOXDRAW_HORIZONTAL;
}
PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
}
Character = BOXDRAW_UP_LEFT;
PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
//
// Get User selection
//
Key.UnicodeChar = CHAR_NULL;
if ((gDirection == SCAN_UP) || (gDirection == SCAN_DOWN)) {
Key.ScanCode = gDirection;
gDirection = 0;
goto TheKey;
}
WaitForKeyStroke (&Key);
TheKey:
switch (Key.UnicodeChar) {
case '+':
if (OrderedList) {
if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) {
//
// Highlight reaches the top of the popup window, scroll one menu item.
//
TopOptionIndex--;
ShowDownArrow = TRUE;
}
if (TopOptionIndex == 0) {
ShowUpArrow = FALSE;
}
if (HighlightOptionIndex > 0) {
HighlightOptionIndex--;
ASSERT (CurrentOption != NULL);
SwapListEntries (CurrentOption->Link.BackLink, &CurrentOption->Link);
}
}
break;
case '-':
//
// If an ordered list op-code, we will allow for a popup of +/- keys
// to create an ordered list of items
//
if (OrderedList) {
if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) &&
(HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1)))
{
//
// Highlight reaches the bottom of the popup window, scroll one menu item.
//
TopOptionIndex++;
ShowUpArrow = TRUE;
}
if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) {
ShowDownArrow = FALSE;
}
if (HighlightOptionIndex < (PopUpMenuLines - 1)) {
HighlightOptionIndex++;
ASSERT (CurrentOption != NULL);
SwapListEntries (&CurrentOption->Link, CurrentOption->Link.ForwardLink);
}
}
break;
case CHAR_NULL:
switch (Key.ScanCode) {
case SCAN_UP:
case SCAN_DOWN:
if (Key.ScanCode == SCAN_UP) {
if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) {
//
// Highlight reaches the top of the popup window, scroll one menu item.
//
TopOptionIndex--;
ShowDownArrow = TRUE;
}
if (TopOptionIndex == 0) {
ShowUpArrow = FALSE;
}
if (HighlightOptionIndex > 0) {
HighlightOptionIndex--;
}
} else {
if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) &&
(HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1)))
{
//
// Highlight reaches the bottom of the popup window, scroll one menu item.
//
TopOptionIndex++;
ShowUpArrow = TRUE;
}
if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) {
ShowDownArrow = FALSE;
}
if (HighlightOptionIndex < (PopUpMenuLines - 1)) {
HighlightOptionIndex++;
}
}
break;
case SCAN_ESC:
gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);
//
// Restore link list order for orderedlist
//
if (OrderedList) {
HiiValue.Type = ValueType;
HiiValue.Value.u64 = 0;
for (Index = 0; Index < OrderList->MaxContainers; Index++) {
HiiValue.Value.u64 = GetArrayData (ValueArray, ValueType, Index);
if (HiiValue.Value.u64 == 0) {
break;
}
OneOfOption = ValueToOption (Question, &HiiValue);
if (OneOfOption == NULL) {
return EFI_NOT_FOUND;
}
RemoveEntryList (&OneOfOption->Link);
InsertTailList (&Question->OptionListHead, &OneOfOption->Link);
}
}
return EFI_DEVICE_ERROR;
default:
break;
}
break;
case CHAR_CARRIAGE_RETURN:
//
// return the current selection
//
if (OrderedList) {
ReturnValue = AllocateZeroPool (Question->CurrentValue.BufferLen);
ASSERT (ReturnValue != NULL);
Index = 0;
Link = GetFirstNode (&Question->OptionListHead);
while (!IsNull (&Question->OptionListHead, Link)) {
OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
Link = GetNextNode (&Question->OptionListHead, Link);
SetArrayData (ReturnValue, ValueType, Index, OneOfOption->OptionOpCode->Value.u64);
Index++;
if (Index > OrderList->MaxContainers) {
break;
}
}
if (CompareMem (ReturnValue, ValueArray, Question->CurrentValue.BufferLen) == 0) {
FreePool (ReturnValue);
return EFI_DEVICE_ERROR;
} else {
gUserInput->InputValue.Buffer = ReturnValue;
gUserInput->InputValue.BufferLen = Question->CurrentValue.BufferLen;
}
} else {
ASSERT (CurrentOption != NULL);
gUserInput->InputValue.Type = CurrentOption->OptionOpCode->Type;
if (IsValuesEqual (&Question->CurrentValue.Value, &CurrentOption->OptionOpCode->Value, gUserInput->InputValue.Type)) {
return EFI_DEVICE_ERROR;
} else {
SetValuesByType (&gUserInput->InputValue.Value, &CurrentOption->OptionOpCode->Value, gUserInput->InputValue.Type);
}
}
gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);
return EFI_SUCCESS;
default:
break;
}
} while (TRUE);
}