| /** @file | |
| Implementation for handling user input from the User Interfaces. | |
| Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR> | |
| Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved. | |
| 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 '^': | |
| 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--; | |
| } | |
| break; | |
| case 'V': | |
| case 'v': | |
| 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 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); | |
| } |