diff options
Diffstat (limited to 'MdeModulePkg/Universal/DisplayEngineDxe/InputHandler.c')
| -rw-r--r-- | MdeModulePkg/Universal/DisplayEngineDxe/InputHandler.c | 3384 |
1 files changed, 1692 insertions, 1692 deletions
diff --git a/MdeModulePkg/Universal/DisplayEngineDxe/InputHandler.c b/MdeModulePkg/Universal/DisplayEngineDxe/InputHandler.c index f70feeb55f..8f35c9b4ed 100644 --- a/MdeModulePkg/Universal/DisplayEngineDxe/InputHandler.c +++ b/MdeModulePkg/Universal/DisplayEngineDxe/InputHandler.c @@ -1,1692 +1,1692 @@ -/** @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);
-}
+/** @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); +} |
