/** @file | |
Implementation for handling user input from the User Interfaces. | |
Copyright (c) 2004 - 2012, Intel Corporation. All rights reserved.<BR> | |
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. | |
**/ | |
#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; | |
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; | |
} | |
TempString = AllocateZeroPool ((Maximum + 1)* 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; | |
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; | |
} | |
break; | |
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 | |
// | |
StrCpy (StringPtr, 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)) { | |
StrnCpy (StringPtr, &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; | |
StrCat (TempString, KeyPad); | |
StrCat (TempString, StringPtr + CurrentCursor); | |
StrCpy (StringPtr, TempString); | |
} else { | |
StrCat (StringPtr, 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 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, | |
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: | |
*Minimum = NumericOp->data.u8.MinValue; | |
*Maximum = NumericOp->data.u8.MaxValue; | |
*Step = NumericOp->data.u8.Step; | |
*StorageWidth = (UINT16) sizeof (UINT8); | |
break; | |
case EFI_IFR_NUMERIC_SIZE_2: | |
*Minimum = NumericOp->data.u16.MinValue; | |
*Maximum = NumericOp->data.u16.MaxValue; | |
*Step = NumericOp->data.u16.Step; | |
*StorageWidth = (UINT16) sizeof (UINT16); | |
break; | |
case EFI_IFR_NUMERIC_SIZE_4: | |
*Minimum = NumericOp->data.u32.MinValue; | |
*Maximum = NumericOp->data.u32.MaxValue; | |
*Step = NumericOp->data.u32.Step; | |
*StorageWidth = (UINT16) sizeof (UINT32); | |
break; | |
case EFI_IFR_NUMERIC_SIZE_8: | |
*Minimum = NumericOp->data.u64.MinValue; | |
*Maximum = NumericOp->data.u64.MaxValue; | |
*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 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; | |
Question = MenuOption->ThisTag; | |
QuestionValue = &Question->CurrentValue; | |
// | |
// 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, &Minimum, &Maximum, &Step, &StorageWidth); | |
EditValue = QuestionValue->Value.u64; | |
EraseLen = gOptionBlockWidth; | |
} | |
if ((Question->OpCode->OpCode == EFI_IFR_NUMERIC_OP) && (NumericOp != NULL) && | |
((NumericOp->Flags & EFI_IFR_DISPLAY) == EFI_IFR_DISPLAY_UINT_HEX)) { | |
HexInput = TRUE; | |
} else { | |
HexInput = FALSE; | |
} | |
// | |
// 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; | |
} | |
} | |
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' '); | |
} else { | |
SetUnicodeMem (InputText, InputWidth, L' '); | |
} | |
if (MenuOption->Sequence == 2) { | |
InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER; | |
} else { | |
InputText[InputWidth + 1] = DATE_SEPARATOR; | |
} | |
InputText[InputWidth + 2] = 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' '); | |
} else { | |
SetUnicodeMem (InputText, InputWidth, L' '); | |
} | |
if (MenuOption->Sequence == 2) { | |
InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER; | |
} else { | |
InputText[InputWidth + 1] = TIME_SEPARATOR; | |
} | |
InputText[InputWidth + 2] = 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 (Key.UnicodeChar == '+') { | |
Key.ScanCode = SCAN_RIGHT; | |
} else { | |
Key.ScanCode = SCAN_LEFT; | |
} | |
Key.UnicodeChar = CHAR_NULL; | |
goto TheKey2; | |
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 (EditValue >= Minimum + Step) { | |
EditValue = EditValue - Step; | |
} else if (EditValue > Minimum){ | |
EditValue = Minimum; | |
} else { | |
EditValue = Maximum; | |
} | |
} else if (Key.ScanCode == SCAN_RIGHT) { | |
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; | |
break; | |
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. | |
// | |
if (EditValue < Minimum) { | |
UpdateStatusBar (INPUT_ERROR, TRUE); | |
break; | |
} else { | |
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; | |
break; | |
case CHAR_BACKSPACE: | |
if (ManualInput) { | |
if (Count == 0) { | |
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 { | |
EditValue = MultU64x32 (EditValue, 10) + (Key.UnicodeChar - L'0'); | |
} | |
} else { | |
if (HexInput) { | |
EditValue = Digital; | |
} else { | |
EditValue = Key.UnicodeChar - L'0'; | |
} | |
} | |
if (EditValue > Maximum) { | |
UpdateStatusBar (INPUT_ERROR, TRUE); | |
ASSERT (Count < sizeof (PreviousNumber) / sizeof (PreviousNumber[0])); | |
EditValue = PreviousNumber[Count]; | |
break; | |
} else { | |
UpdateStatusBar (INPUT_ERROR, FALSE); | |
} | |
Count++; | |
ASSERT (Count < (sizeof (PreviousNumber) / sizeof (PreviousNumber[0]))); | |
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; | |
StringPtr = AllocateZeroPool ((gOptionBlockWidth + 1) * 2); | |
ASSERT (StringPtr); | |
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; | |
StrCat (StringPtr, 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); | |
} | |