mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-12-27 16:58:09 +01:00
01f33f7552
stylish change: remove space between func and brace Signed-off-by: SergeySlice <sergey.slice@gmail.com>
1665 lines
50 KiB
C
1665 lines
50 KiB
C
/** @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);
|
|
|
|
}
|
|
|