-/** @file
-
-Copyright (c) 2004 - 2007, Intel Corporation
-All rights reserved. This program and the accompanying materials
-are licensed and made available under the terms and conditions of the BSD License
-which accompanies this distribution. The full text of the license may be found at
-http://opensource.org/licenses/bsd-license.php
-
-THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
-WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
-
-Module Name:
-
- InputHandler.c
-
-Abstract:
-
- Implementation for handling user input from the User Interface
-
-Revision History
-
-
-**/
-
-#include "Ui.h"
-#include "Setup.h"
-
-
-/**
- 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 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,
- 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 Count;
- UINTN Start;
- UINTN Top;
- UINTN DimensionsWidth;
- UINTN DimensionsHeight;
- BOOLEAN CursorVisible;
- UINTN Minimum;
- UINTN Maximum;
- FORM_BROWSER_STATEMENT *Question;
- BOOLEAN IsPassword;
-
- DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn;
- DimensionsHeight = gScreenDimensions.BottomRow - gScreenDimensions.TopRow;
-
- NullCharacter = CHAR_NULL;
- ScreenSize = GetStringWidth (Prompt) / sizeof (CHAR16);
- Space[0] = L' ';
- Space[1] = CHAR_NULL;
-
- Question = MenuOption->ThisTag;
- Minimum = (UINTN) Question->Minimum;
- Maximum = (UINTN) Question->Maximum;
-
- if (Question->Operand == EFI_IFR_PASSWORD_OP) {
- IsPassword = TRUE;
- } else {
- IsPassword = FALSE;
- }
-
- TempString = AllocateZeroPool ((Maximum + 1)* sizeof (CHAR16));
- ASSERT (TempString);
-
- if (ScreenSize < (Maximum + 1)) {
- ScreenSize = Maximum + 1;
- }
-
- if ((ScreenSize + 2) > DimensionsWidth) {
- ScreenSize = DimensionsWidth - 2;
- }
-
- BufferedString = AllocateZeroPool (ScreenSize * 2);
- ASSERT (BufferedString);
-
- Start = (DimensionsWidth - ScreenSize - 2) / 2 + gScreenDimensions.LeftColumn + 1;
- Top = ((DimensionsHeight - 6) / 2) + gScreenDimensions.TopRow - 1;
-
- //
- // Display prompt for string
- //
- CreatePopUp (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);
-
- 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:
- break;
-
- case SCAN_RIGHT:
- break;
-
- case SCAN_ESC:
- gBS->FreePool (TempString);
- gBS->FreePool (BufferedString);
- gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
- gST->ConOut->EnableCursor (gST->ConOut, CursorVisible);
- return EFI_DEVICE_ERROR;
-
- default:
- break;
- }
-
- break;
-
- case CHAR_CARRIAGE_RETURN:
- if (GetStringWidth (StringPtr) >= ((Minimum + 1) * sizeof (CHAR16))) {
-
- gBS->FreePool (TempString);
- gBS->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 (4, TRUE, 0, NULL, &Key, &NullCharacter, gMiniString, gPressEnter, &NullCharacter);
- } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
-
- gBS->FreePool (TempString);
- gBS->FreePool (BufferedString);
- gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
- gST->ConOut->EnableCursor (gST->ConOut, CursorVisible);
- return EFI_DEVICE_ERROR;
- }
-
- break;
-
- case CHAR_BACKSPACE:
- if (StringPtr[0] != CHAR_NULL) {
- for (Index = 0; StringPtr[Index] != CHAR_NULL; Index++) {
- TempString[Index] = StringPtr[Index];
- }
- //
- // Effectively truncate string by 1 character
- //
- TempString[Index - 1] = CHAR_NULL;
- StrCpy (StringPtr, TempString);
- }
-
- default:
- //
- // If it is the beginning of the string, don't worry about checking maximum limits
- //
- if ((StringPtr[0] == CHAR_NULL) && (Key.UnicodeChar != CHAR_BACKSPACE)) {
- StrnCpy (StringPtr, &Key.UnicodeChar, 1);
- StrnCpy (TempString, &Key.UnicodeChar, 1);
- } else if ((GetStringWidth (StringPtr) < ((Maximum + 1) * sizeof (CHAR16))) && (Key.UnicodeChar != CHAR_BACKSPACE)) {
- KeyPad[0] = Key.UnicodeChar;
- KeyPad[1] = CHAR_NULL;
- StrCat (StringPtr, KeyPad);
- StrCat (TempString, KeyPad);
- }
-
- //
- // 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) {
- PrintChar (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 + GetStringWidth (StringPtr) / 2, Top + 3);
- } while (TRUE);
-
-}
-
-
-/**
- This routine reads a numeric value from the user input.
-
- @param Selection Pointer to current selection.
- @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_SELECTION *Selection,
- IN UI_MENU_OPTION *MenuOption
- )
-{
- EFI_STATUS Status;
- UINTN Column;
- UINTN Row;
- CHAR16 InputText[23];
- CHAR16 FormattedNumber[22];
- UINT64 PreviousNumber[20];
- UINTN Count;
- UINTN Loop;
- BOOLEAN ManualInput;
- BOOLEAN HexInput;
- BOOLEAN DateOrTime;
- UINTN InputWidth;
- UINT64 EditValue;
- UINT64 Step;
- UINT64 Minimum;
- UINT64 Maximum;
- UINTN EraseLen;
- UINT8 Digital;
- EFI_INPUT_KEY Key;
- EFI_HII_VALUE *QuestionValue;
- FORM_BROWSER_FORM *Form;
- FORM_BROWSER_FORMSET *FormSet;
- FORM_BROWSER_STATEMENT *Question;
-
- Column = MenuOption->OptCol;
- Row = MenuOption->Row;
- PreviousNumber[0] = 0;
- Count = 0;
- InputWidth = 0;
- Digital = 0;
-
- FormSet = Selection->FormSet;
- Form = Selection->Form;
- Question = MenuOption->ThisTag;
- QuestionValue = &Question->HiiValue;
- Step = Question->Step;
- Minimum = Question->Minimum;
- Maximum = Question->Maximum;
-
- if ((Question->Operand == EFI_IFR_DATE_OP) || (Question->Operand == EFI_IFR_TIME_OP)) {
- DateOrTime = TRUE;
- } else {
- DateOrTime = FALSE;
- }
-
- //
- // Prepare Value to be edit
- //
- EraseLen = 0;
- EditValue = 0;
- if (Question->Operand == 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:
- Maximum = 31;
- 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->Operand == 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 {
- //
- // Numeric
- //
- EraseLen = gOptionBlockWidth;
- EditValue = QuestionValue->Value.u64;
- if (Maximum == 0) {
- Maximum = (UINT64) -1;
- }
- }
-
- if (Step == 0) {
- ManualInput = TRUE;
- } else {
- ManualInput = FALSE;
- }
-
- if ((Question->Operand == EFI_IFR_NUMERIC_OP) &&
- ((Question->Flags & EFI_IFR_DISPLAY) == EFI_IFR_DISPLAY_UINT_HEX)) {
- HexInput = TRUE;
- } else {
- HexInput = FALSE;
- }
-
- if (ManualInput) {
- if (HexInput) {
- InputWidth = Question->StorageWidth * 2;
- } else {
- switch (Question->StorageWidth) {
- case 1:
- InputWidth = 3;
- break;
-
- case 2:
- InputWidth = 5;
- break;
-
- case 4:
- InputWidth = 10;
- break;
-
- case 8:
- InputWidth = 20;
- break;
-
- default:
- InputWidth = 0;
- break;
- }
- }
-
- InputText[0] = LEFT_NUMERIC_DELIMITER;
- SetUnicodeMem (InputText + 1, InputWidth, L' ');
- InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER;
- InputText[InputWidth + 2] = L'\0';
-
- PrintAt (Column, Row, InputText);
- 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;
- }
-
- Status = WaitForKeyStroke (&Key);
-
-TheKey2:
- switch (Key.UnicodeChar) {
-
- case '+':
- case '-':
- if (Key.UnicodeChar == '+') {
- Key.ScanCode = SCAN_RIGHT;
- } else {
- Key.ScanCode = SCAN_LEFT;
- }
- Key.UnicodeChar = CHAR_NULL;
- goto TheKey2;
-
- case CHAR_NULL:
- switch (Key.ScanCode) {
- case SCAN_LEFT:
- case SCAN_RIGHT:
- if (DateOrTime) {
- //
- // 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 (!ManualInput) {
- if (Key.ScanCode == SCAN_LEFT) {
- if (EditValue > Step) {
- EditValue = EditValue - Step;
- } else {
- EditValue = Minimum;
- }
- } else if (Key.ScanCode == SCAN_RIGHT) {
- EditValue = EditValue + Step;
- if (EditValue > Maximum) {
- EditValue = Maximum;
- }
- }
-
- ZeroMem (FormattedNumber, 21 * sizeof (CHAR16));
- if (Question->Operand == EFI_IFR_DATE_OP) {
- if (MenuOption->Sequence == 2) {
- //
- // Year
- //
- UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%04d", EditValue);
- } else {
- //
- // Month/Day
- //
- UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", EditValue);
- }
-
- if (MenuOption->Sequence == 0) {
- FormattedNumber[EraseLen - 2] = DATE_SEPARATOR;
- } else if (MenuOption->Sequence == 1) {
- FormattedNumber[EraseLen - 1] = DATE_SEPARATOR;
- }
- } else if (Question->Operand == EFI_IFR_TIME_OP) {
- UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", EditValue);
-
- if (MenuOption->Sequence == 0) {
- FormattedNumber[EraseLen - 2] = TIME_SEPARATOR;
- } else if (MenuOption->Sequence == 1) {
- FormattedNumber[EraseLen - 1] = TIME_SEPARATOR;
- }
- } else {
- QuestionValue->Value.u64 = EditValue;
- PrintFormattedNumber (Question, FormattedNumber, 21 * sizeof (CHAR16));
- }
-
- gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
- for (Loop = 0; Loop < EraseLen; Loop++) {
- PrintAt (MenuOption->OptCol + Loop, MenuOption->Row, L" ");
- }
- gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_HIGHLIGHT | FIELD_BACKGROUND_HIGHLIGHT);
-
- if (MenuOption->Sequence == 0) {
- PrintCharAt (MenuOption->OptCol, Row, LEFT_NUMERIC_DELIMITER);
- Column = MenuOption->OptCol + 1;
- }
-
- PrintStringAt (Column, Row, FormattedNumber);
-
- if (!DateOrTime || MenuOption->Sequence == 2) {
- PrintChar (RIGHT_NUMERIC_DELIMITER);
- }
- }
- break;
-
- case SCAN_UP:
- case SCAN_DOWN:
- goto EnterCarriageReturn;
-
- case SCAN_ESC:
- return EFI_DEVICE_ERROR;
-
- default:
- break;
- }
-
- break;
-
-EnterCarriageReturn:
-
- case CHAR_CARRIAGE_RETURN:
- //
- // Store Edit value back to Question
- //
- if (Question->Operand == 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->Operand == 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;
- }
-
- //
- // Check to see if the Value is something reasonable against consistency limitations.
- // If not, let's kick the error specified.
- //
- Status = ValidateQuestion (FormSet, Form, Question, EFI_HII_EXPRESSION_INCONSISTENT_IF);
- if (EFI_ERROR (Status)) {
- //
- // Input value is not valid, restore Question Value
- //
- GetQuestionValue (FormSet, Form, Question, TRUE);
- } else {
- SetQuestionValue (FormSet, Form, Question, TRUE);
- if (!DateOrTime || (Question->Storage != NULL)) {
- //
- // NV flag is unnecessary for RTC type of Date/Time
- //
- UpdateStatusBar (NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE);
- }
- }
-
- return Status;
- break;
-
- case CHAR_BACKSPACE:
- if (ManualInput) {
- if (Count == 0) {
- break;
- }
- //
- // Remove a character
- //
- EditValue = PreviousNumber[Count - 1];
- UpdateStatusBar (INPUT_ERROR, Question->QuestionFlags, FALSE);
- Count--;
- Column--;
- PrintAt (Column, Row, L" ");
- }
- break;
-
- default:
- if (ManualInput) {
- if (HexInput) {
- if (!IsHexDigit (&Digital, Key.UnicodeChar)) {
- UpdateStatusBar (INPUT_ERROR, Question->QuestionFlags, TRUE);
- break;
- }
- } else {
- if (Key.UnicodeChar > L'9' || Key.UnicodeChar < L'0') {
- UpdateStatusBar (INPUT_ERROR, Question->QuestionFlags, TRUE);
- break;
- }
- }
-
- //
- // If Count exceed input width, there is no way more is valid
- //
- if (Count >= InputWidth) {
- break;
- }
- //
- // Someone typed something valid!
- //
- if (Count != 0) {
- if (HexInput) {
- EditValue = LShiftU64 (EditValue, 4) + Digital;
- } else {
- EditValue = MultU64x32 (EditValue, 10) + (Key.UnicodeChar - L'0');
- }
- } else {
- if (HexInput) {
- EditValue = Digital;
- } else {
- EditValue = Key.UnicodeChar - L'0';
- }
- }
-
- if (EditValue > Maximum) {
- UpdateStatusBar (INPUT_ERROR, Question->QuestionFlags, TRUE);
- EditValue = PreviousNumber[Count];
- break;
- } else {
- UpdateStatusBar (INPUT_ERROR, Question->QuestionFlags, FALSE);
- }
-
- Count++;
- PreviousNumber[Count] = EditValue;
-
- PrintCharAt (Column, Row, Key.UnicodeChar);
- Column++;
- }
- break;
- }
- } while (TRUE);
-
-}
-
-
-/**
- Get selection for OneOf and OrderedList (Left/Right will be ignored).
-
- @param Selection Pointer to current selection.
- @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_SELECTION *Selection,
- IN UI_MENU_OPTION *MenuOption
- )
-{
- EFI_STATUS Status;
- 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;
- EFI_HII_VALUE HiiValue;
- EFI_HII_VALUE *HiiValueArray;
- UINTN OptionCount;
- QUESTION_OPTION *OneOfOption;
- QUESTION_OPTION *CurrentOption;
- FORM_BROWSER_STATEMENT *Question;
-
- DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn;
-
- ValueArray = NULL;
- CurrentOption = NULL;
- ShowDownArrow = FALSE;
- ShowUpArrow = FALSE;
-
- StringPtr = AllocateZeroPool ((gOptionBlockWidth + 1) * 2);
- ASSERT (StringPtr);
-
- Question = MenuOption->ThisTag;
- if (Question->Operand == EFI_IFR_ORDERED_LIST_OP) {
- ValueArray = Question->BufferValue;
- OrderedList = TRUE;
- } else {
- OrderedList = FALSE;
- }
-
- //
- // Calculate Option count
- //
- if (OrderedList) {
- for (Index = 0; Index < Question->MaxContainers; Index++) {
- if (ValueArray[Index] == 0) {
- break;
- }
- }
-
- OptionCount = Index;
- } else {
- OptionCount = 0;
- Link = GetFirstNode (&Question->OptionListHead);
- while (!IsNull (&Question->OptionListHead, Link)) {
- OneOfOption = QUESTION_OPTION_FROM_LINK (Link);
-
- OptionCount++;
-
- Link = GetNextNode (&Question->OptionListHead, Link);
- }
- }
-
- //
- // Prepare HiiValue array
- //
- HiiValueArray = AllocateZeroPool (OptionCount * sizeof (EFI_HII_VALUE));
- ASSERT (HiiValueArray != NULL);
- Link = GetFirstNode (&Question->OptionListHead);
- for (Index = 0; Index < OptionCount; Index++) {
- if (OrderedList) {
- HiiValueArray[Index].Type = EFI_IFR_TYPE_NUM_SIZE_8;
- HiiValueArray[Index].Value.u8 = ValueArray[Index];
- } else {
- OneOfOption = QUESTION_OPTION_FROM_LINK (Link);
- CopyMem (&HiiValueArray[Index], &OneOfOption->Value, sizeof (EFI_HII_VALUE));
- Link = GetNextNode (&Question->OptionListHead, Link);
- }
- }
-
- //
- // Move Suppressed Option to list tail
- //
- PopUpMenuLines = 0;
- for (Index = 0; Index < OptionCount; Index++) {
- OneOfOption = ValueToOption (Question, &HiiValueArray[OptionCount - Index - 1]);
- if (OneOfOption == NULL) {
- return EFI_NOT_FOUND;
- }
-
- RemoveEntryList (&OneOfOption->Link);
-
- if ((OneOfOption->SuppressExpression != NULL) &&
- (OneOfOption->SuppressExpression->Result.Value.b)) {
- //
- // This option is suppressed, insert to tail
- //
- InsertTailList (&Question->OptionListHead, &OneOfOption->Link);
- } else {
- //
- // Insert to head
- //
- InsertHeadList (&Question->OptionListHead, &OneOfOption->Link);
-
- PopUpMenuLines++;
- }
- }
-
- //
- // 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 = QUESTION_OPTION_FROM_LINK (Link);
-
- StringPtr = GetToken (OneOfOption->Text, MenuOption->Handle);
- if (StrLen (StringPtr) > PopUpWidth) {
- PopUpWidth = StrLen (StringPtr);
- }
- gBS->FreePool (StringPtr);
-
- if (!OrderedList && CompareHiiValue (&Question->HiiValue, &OneOfOption->Value, NULL) == 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, POPUP_TEXT | POPUP_BACKGROUND);
-
- if ((PopUpWidth + POPUP_FRAME_WIDTH) > DimensionsWidth) {
- PopUpWidth = DimensionsWidth - POPUP_FRAME_WIDTH;
- }
-
- Start = (DimensionsWidth - PopUpWidth - POPUP_FRAME_WIDTH) / 2 + gScreenDimensions.LeftColumn;
- End = Start + PopUpWidth + POPUP_FRAME_WIDTH;
- Top = gScreenDimensions.TopRow + NONE_FRONT_PAGE_HEADER_HEIGHT;
- Bottom = gScreenDimensions.BottomRow - STATUS_BAR_HEIGHT - FOOTER_HEIGHT - 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, POPUP_TEXT | POPUP_BACKGROUND);
-
- //
- // 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;
- }
-
- PrintChar (Character);
- }
-
- Character = BOXDRAW_DOWN_LEFT;
- PrintChar (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 = QUESTION_OPTION_FROM_LINK (Link);
- Link = GetNextNode (&Question->OptionListHead, Link);
-
- StringPtr = GetToken (OneOfOption->Text, MenuOption->Handle);
- //
- // 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)));
- gBS->FreePool (StringPtr);
- StringPtr = TempStringPtr;
- StrCat (StringPtr, L"...");
- }
-
- if (Index == HighlightOptionIndex) {
- //
- // Highlight the selected one
- //
- CurrentOption = OneOfOption;
-
- gST->ConOut->SetAttribute (gST->ConOut, PICKLIST_HIGHLIGHT_TEXT | PICKLIST_HIGHLIGHT_BACKGROUND);
- PrintStringAt (Start + 2, Index2, StringPtr);
- gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);
- } else {
- gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);
- PrintStringAt (Start + 2, Index2, StringPtr);
- }
-
- Index2++;
- gBS->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;
- }
-
- PrintChar (Character);
- }
-
- Character = BOXDRAW_UP_LEFT;
- PrintChar (Character);
-
- //
- // Get User selection
- //
- Key.UnicodeChar = CHAR_NULL;
- if ((gDirection == SCAN_UP) || (gDirection == SCAN_DOWN)) {
- Key.ScanCode = gDirection;
- gDirection = 0;
- goto TheKey;
- }
-
- Status = 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--;
-
- 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++;
-
- 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 = EFI_IFR_TYPE_NUM_SIZE_8;
- HiiValue.Value.u64 = 0;
- for (Index = 0; Index < Question->MaxContainers; Index++) {
- HiiValue.Value.u8 = ValueArray[Index];
- if (HiiValue.Value.u8) {
- break;
- }
-
- OneOfOption = ValueToOption (Question, &HiiValue);
- if (OneOfOption == NULL) {
- return EFI_NOT_FOUND;
- }
-
- RemoveEntryList (&OneOfOption->Link);
- InsertTailList (&Question->OptionListHead, &OneOfOption->Link);
- }
- }
-
- gBS->FreePool (HiiValueArray);
- return EFI_DEVICE_ERROR;
-
- default:
- break;
- }
-
- break;
-
- case CHAR_CARRIAGE_RETURN:
- //
- // return the current selection
- //
- if (OrderedList) {
- Index = 0;
- Link = GetFirstNode (&Question->OptionListHead);
- while (!IsNull (&Question->OptionListHead, Link)) {
- OneOfOption = QUESTION_OPTION_FROM_LINK (Link);
-
- Question->BufferValue[Index] = OneOfOption->Value.Value.u8;
-
- Index++;
- if (Index > Question->MaxContainers) {
- break;
- }
-
- Link = GetNextNode (&Question->OptionListHead, Link);
- }
- } else {
- CopyMem (&Question->HiiValue, &CurrentOption->Value, sizeof (EFI_HII_VALUE));
- }
-
- gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);
- gBS->FreePool (HiiValueArray);
-
- Status = ValidateQuestion (Selection->FormSet, Selection->Form, Question, EFI_HII_EXPRESSION_INCONSISTENT_IF);
- if (EFI_ERROR (Status)) {
- //
- // Input value is not valid, restore Question Value
- //
- GetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE);
- } else {
- SetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE);
- UpdateStatusBar (NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE);
- }
-
- return Status;
-
- default:
- break;
- }
- } while (TRUE);
-
-}
-
-EFI_STATUS
-WaitForKeyStroke (
- OUT EFI_INPUT_KEY *Key
- )
-{
- EFI_STATUS Status;
-
- do {
- UiWaitForSingleEvent (gST->ConIn->WaitForKey, 0, 0);
- Status = gST->ConIn->ReadKeyStroke (gST->ConIn, Key);
- } while (EFI_ERROR(Status));
-
- return Status;
-}
+/** @file\r
+Implementation for handling user input from the User Interfaces.\r
+\r
+Copyright (c) 2004 - 2012, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution. The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "Setup.h"\r
+\r
+\r
+/**\r
+ Get string or password input from user.\r
+\r
+ @param MenuOption Pointer to the current input menu.\r
+ @param Prompt The prompt string shown on popup window.\r
+ @param StringPtr Old user input and destination for use input string.\r
+\r
+ @retval EFI_SUCCESS If string input is read successfully\r
+ @retval EFI_DEVICE_ERROR If operation fails\r
+\r
+**/\r
+EFI_STATUS\r
+ReadString (\r
+ IN UI_MENU_OPTION *MenuOption,\r
+ IN CHAR16 *Prompt,\r
+ IN OUT CHAR16 *StringPtr\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_INPUT_KEY Key;\r
+ CHAR16 NullCharacter;\r
+ UINTN ScreenSize;\r
+ CHAR16 Space[2];\r
+ CHAR16 KeyPad[2];\r
+ CHAR16 *TempString;\r
+ CHAR16 *BufferedString;\r
+ UINTN Index;\r
+ UINTN Index2;\r
+ UINTN Count;\r
+ UINTN Start;\r
+ UINTN Top;\r
+ UINTN DimensionsWidth;\r
+ UINTN DimensionsHeight;\r
+ UINTN CurrentCursor;\r
+ BOOLEAN CursorVisible;\r
+ UINTN Minimum;\r
+ UINTN Maximum;\r
+ FORM_BROWSER_STATEMENT *Question;\r
+ BOOLEAN IsPassword;\r
+\r
+ DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn;\r
+ DimensionsHeight = gScreenDimensions.BottomRow - gScreenDimensions.TopRow;\r
+\r
+ NullCharacter = CHAR_NULL;\r
+ ScreenSize = GetStringWidth (Prompt) / sizeof (CHAR16);\r
+ Space[0] = L' ';\r
+ Space[1] = CHAR_NULL;\r
+\r
+ Question = MenuOption->ThisTag;\r
+ Minimum = (UINTN) Question->Minimum;\r
+ Maximum = (UINTN) Question->Maximum;\r
+\r
+ if (Question->Operand == EFI_IFR_PASSWORD_OP) {\r
+ IsPassword = TRUE;\r
+ } else {\r
+ IsPassword = FALSE;\r
+ }\r
+\r
+ TempString = AllocateZeroPool ((Maximum + 1)* sizeof (CHAR16));\r
+ ASSERT (TempString);\r
+\r
+ if (ScreenSize < (Maximum + 1)) {\r
+ ScreenSize = Maximum + 1;\r
+ }\r
+\r
+ if ((ScreenSize + 2) > DimensionsWidth) {\r
+ ScreenSize = DimensionsWidth - 2;\r
+ }\r
+\r
+ BufferedString = AllocateZeroPool (ScreenSize * 2);\r
+ ASSERT (BufferedString);\r
+\r
+ Start = (DimensionsWidth - ScreenSize - 2) / 2 + gScreenDimensions.LeftColumn + 1;\r
+ Top = ((DimensionsHeight - 6) / 2) + gScreenDimensions.TopRow - 1;\r
+\r
+ //\r
+ // Display prompt for string\r
+ //\r
+ CreateMultiStringPopUp (ScreenSize, 4, &NullCharacter, Prompt, Space, &NullCharacter);\r
+\r
+ gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY));\r
+\r
+ CursorVisible = gST->ConOut->Mode->CursorVisible;\r
+ gST->ConOut->EnableCursor (gST->ConOut, TRUE);\r
+\r
+ CurrentCursor = GetStringWidth (StringPtr) / 2 - 1;\r
+ if (CurrentCursor != 0) {\r
+ //\r
+ // Show the string which has beed saved before.\r
+ //\r
+ SetUnicodeMem (BufferedString, ScreenSize - 1, L' ');\r
+ PrintStringAt (Start + 1, Top + 3, BufferedString);\r
+\r
+ if ((GetStringWidth (StringPtr) / 2) > (DimensionsWidth - 2)) {\r
+ Index = (GetStringWidth (StringPtr) / 2) - DimensionsWidth + 2;\r
+ } else {\r
+ Index = 0;\r
+ }\r
+\r
+ if (IsPassword) {\r
+ gST->ConOut->SetCursorPosition (gST->ConOut, Start + 1, Top + 3);\r
+ }\r
+\r
+ for (Count = 0; Index + 1 < GetStringWidth (StringPtr) / 2; Index++, Count++) {\r
+ BufferedString[Count] = StringPtr[Index];\r
+\r
+ if (IsPassword) {\r
+ PrintChar (L'*');\r
+ }\r
+ }\r
+\r
+ if (!IsPassword) {\r
+ PrintStringAt (Start + 1, Top + 3, BufferedString);\r
+ }\r
+ \r
+ gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));\r
+ gST->ConOut->SetCursorPosition (gST->ConOut, Start + GetStringWidth (StringPtr) / 2, Top + 3);\r
+ }\r
+ \r
+ do {\r
+ Status = WaitForKeyStroke (&Key);\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY));\r
+ switch (Key.UnicodeChar) {\r
+ case CHAR_NULL:\r
+ switch (Key.ScanCode) {\r
+ case SCAN_LEFT:\r
+ if (CurrentCursor > 0) {\r
+ CurrentCursor--;\r
+ }\r
+ break;\r
+\r
+ case SCAN_RIGHT:\r
+ if (CurrentCursor < (GetStringWidth (StringPtr) / 2 - 1)) {\r
+ CurrentCursor++;\r
+ }\r
+ break;\r
+\r
+ case SCAN_ESC:\r
+ FreePool (TempString);\r
+ FreePool (BufferedString);\r
+ gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));\r
+ gST->ConOut->EnableCursor (gST->ConOut, CursorVisible);\r
+ return EFI_DEVICE_ERROR;\r
+\r
+ default:\r
+ break;\r
+ }\r
+\r
+ break;\r
+\r
+ case CHAR_CARRIAGE_RETURN:\r
+ if (GetStringWidth (StringPtr) >= ((Minimum + 1) * sizeof (CHAR16))) {\r
+\r
+ FreePool (TempString);\r
+ FreePool (BufferedString);\r
+ gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));\r
+ gST->ConOut->EnableCursor (gST->ConOut, CursorVisible);\r
+ return EFI_SUCCESS;\r
+ } else {\r
+ //\r
+ // Simply create a popup to tell the user that they had typed in too few characters.\r
+ // To save code space, we can then treat this as an error and return back to the menu.\r
+ //\r
+ do {\r
+ CreateDialog (4, TRUE, 0, NULL, &Key, &NullCharacter, gMiniString, gPressEnter, &NullCharacter);\r
+ } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);\r
+\r
+ FreePool (TempString);\r
+ FreePool (BufferedString);\r
+ gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));\r
+ gST->ConOut->EnableCursor (gST->ConOut, CursorVisible);\r
+ return EFI_DEVICE_ERROR;\r
+ }\r
+\r
+ break;\r
+\r
+ case CHAR_BACKSPACE:\r
+ if (StringPtr[0] != CHAR_NULL && CurrentCursor != 0) {\r
+ for (Index = 0; Index < CurrentCursor - 1; Index++) {\r
+ TempString[Index] = StringPtr[Index];\r
+ }\r
+ Count = GetStringWidth (StringPtr) / 2 - 1;\r
+ if (Count >= CurrentCursor) {\r
+ for (Index = CurrentCursor - 1, Index2 = CurrentCursor; Index2 < Count; Index++, Index2++) {\r
+ TempString[Index] = StringPtr[Index2];\r
+ }\r
+ TempString[Index] = CHAR_NULL;\r
+ }\r
+ //\r
+ // Effectively truncate string by 1 character\r
+ //\r
+ StrCpy (StringPtr, TempString);\r
+ CurrentCursor --;\r
+ }\r
+\r
+ default:\r
+ //\r
+ // If it is the beginning of the string, don't worry about checking maximum limits\r
+ //\r
+ if ((StringPtr[0] == CHAR_NULL) && (Key.UnicodeChar != CHAR_BACKSPACE)) {\r
+ StrnCpy (StringPtr, &Key.UnicodeChar, 1);\r
+ CurrentCursor++;\r
+ } else if ((GetStringWidth (StringPtr) < ((Maximum + 1) * sizeof (CHAR16))) && (Key.UnicodeChar != CHAR_BACKSPACE)) {\r
+ KeyPad[0] = Key.UnicodeChar;\r
+ KeyPad[1] = CHAR_NULL;\r
+ Count = GetStringWidth (StringPtr) / 2 - 1;\r
+ if (CurrentCursor < Count) {\r
+ for (Index = 0; Index < CurrentCursor; Index++) {\r
+ TempString[Index] = StringPtr[Index];\r
+ }\r
+ TempString[Index] = CHAR_NULL;\r
+ StrCat (TempString, KeyPad);\r
+ StrCat (TempString, StringPtr + CurrentCursor);\r
+ StrCpy (StringPtr, TempString);\r
+ } else {\r
+ StrCat (StringPtr, KeyPad);\r
+ }\r
+ CurrentCursor++;\r
+ }\r
+\r
+ //\r
+ // If the width of the input string is now larger than the screen, we nee to\r
+ // adjust the index to start printing portions of the string\r
+ //\r
+ SetUnicodeMem (BufferedString, ScreenSize - 1, L' ');\r
+ PrintStringAt (Start + 1, Top + 3, BufferedString);\r
+\r
+ if ((GetStringWidth (StringPtr) / 2) > (DimensionsWidth - 2)) {\r
+ Index = (GetStringWidth (StringPtr) / 2) - DimensionsWidth + 2;\r
+ } else {\r
+ Index = 0;\r
+ }\r
+\r
+ if (IsPassword) {\r
+ gST->ConOut->SetCursorPosition (gST->ConOut, Start + 1, Top + 3);\r
+ }\r
+\r
+ for (Count = 0; Index + 1 < GetStringWidth (StringPtr) / 2; Index++, Count++) {\r
+ BufferedString[Count] = StringPtr[Index];\r
+\r
+ if (IsPassword) {\r
+ PrintChar (L'*');\r
+ }\r
+ }\r
+\r
+ if (!IsPassword) {\r
+ PrintStringAt (Start + 1, Top + 3, BufferedString);\r
+ }\r
+ break;\r
+ }\r
+\r
+ gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));\r
+ gST->ConOut->SetCursorPosition (gST->ConOut, Start + CurrentCursor + 1, Top + 3);\r
+ } while (TRUE);\r
+\r
+}\r
+\r
+/**\r
+ Adjust the value to the correct one. Rules follow the sample:\r
+ like: Year change: 2012.02.29 -> 2013.02.29 -> 2013.02.01\r
+ Month change: 2013.03.29 -> 2013.02.29 -> 2013.02.28\r
+\r
+ @param Question Pointer to current question.\r
+ @param Sequence The sequence of the field in the question.\r
+**/\r
+VOID\r
+AdjustQuestionValue (\r
+ IN FORM_BROWSER_STATEMENT *Question,\r
+ IN UINT8 Sequence\r
+ )\r
+{\r
+ UINT8 Month;\r
+ UINT16 Year;\r
+ UINT8 Maximum;\r
+ UINT8 Minimum;\r
+\r
+ if (Question->Operand != EFI_IFR_DATE_OP) {\r
+ return;\r
+ }\r
+\r
+ Month = Question->HiiValue.Value.date.Month;\r
+ Year = Question->HiiValue.Value.date.Year;\r
+ Minimum = 1;\r
+\r
+ switch (Month) {\r
+ case 2:\r
+ if ((Year % 4) == 0 && ((Year % 100) != 0 || (Year % 400) == 0)) {\r
+ Maximum = 29;\r
+ } else {\r
+ Maximum = 28;\r
+ }\r
+ break;\r
+ case 4:\r
+ case 6:\r
+ case 9:\r
+ case 11:\r
+ Maximum = 30;\r
+ break;\r
+ default:\r
+ Maximum = 31;\r
+ break;\r
+ }\r
+\r
+ //\r
+ // Change the month area.\r
+ //\r
+ if (Sequence == 0) {\r
+ if (Question->HiiValue.Value.date.Day > Maximum) {\r
+ Question->HiiValue.Value.date.Day = Maximum;\r
+ }\r
+ }\r
+ \r
+ //\r
+ // Change the Year area.\r
+ //\r
+ if (Sequence == 2) {\r
+ if (Question->HiiValue.Value.date.Day > Maximum) {\r
+ Question->HiiValue.Value.date.Day = Minimum;\r
+ }\r
+ }\r
+}\r
+\r
+/**\r
+ This routine reads a numeric value from the user input.\r
+\r
+ @param Selection Pointer to current selection.\r
+ @param MenuOption Pointer to the current input menu.\r
+\r
+ @retval EFI_SUCCESS If numerical input is read successfully\r
+ @retval EFI_DEVICE_ERROR If operation fails\r
+\r
+**/\r
+EFI_STATUS\r
+GetNumericInput (\r
+ IN UI_MENU_SELECTION *Selection,\r
+ IN UI_MENU_OPTION *MenuOption\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ UINTN Column;\r
+ UINTN Row;\r
+ CHAR16 InputText[MAX_NUMERIC_INPUT_WIDTH];\r
+ CHAR16 FormattedNumber[MAX_NUMERIC_INPUT_WIDTH - 1];\r
+ UINT64 PreviousNumber[MAX_NUMERIC_INPUT_WIDTH - 3];\r
+ UINTN Count;\r
+ UINTN Loop;\r
+ BOOLEAN ManualInput;\r
+ BOOLEAN HexInput;\r
+ BOOLEAN DateOrTime;\r
+ UINTN InputWidth;\r
+ UINT64 EditValue;\r
+ UINT64 Step;\r
+ UINT64 Minimum;\r
+ UINT64 Maximum;\r
+ UINTN EraseLen;\r
+ UINT8 Digital;\r
+ EFI_INPUT_KEY Key;\r
+ EFI_HII_VALUE *QuestionValue;\r
+ FORM_BROWSER_FORM *Form;\r
+ FORM_BROWSER_FORMSET *FormSet;\r
+ FORM_BROWSER_STATEMENT *Question;\r
+\r
+ Column = MenuOption->OptCol;\r
+ Row = MenuOption->Row;\r
+ PreviousNumber[0] = 0;\r
+ Count = 0;\r
+ InputWidth = 0;\r
+ Digital = 0;\r
+\r
+ FormSet = Selection->FormSet;\r
+ Form = Selection->Form;\r
+ Question = MenuOption->ThisTag;\r
+ QuestionValue = &Question->HiiValue;\r
+ Step = Question->Step;\r
+ Minimum = Question->Minimum;\r
+ Maximum = Question->Maximum;\r
+\r
+ //\r
+ // Only two case, user can enter to this function: Enter and +/- case.\r
+ // In Enter case, gDirection = 0; in +/- case, gDirection = SCAN_LEFT/SCAN_WRIGHT\r
+ //\r
+ ManualInput = (BOOLEAN)(gDirection == 0 ? TRUE : FALSE);\r
+\r
+ if ((Question->Operand == EFI_IFR_DATE_OP) || (Question->Operand == EFI_IFR_TIME_OP)) {\r
+ DateOrTime = TRUE;\r
+ } else {\r
+ DateOrTime = FALSE;\r
+ }\r
+\r
+ //\r
+ // Prepare Value to be edit\r
+ //\r
+ EraseLen = 0;\r
+ EditValue = 0;\r
+ if (Question->Operand == EFI_IFR_DATE_OP) {\r
+ Step = 1;\r
+ Minimum = 1;\r
+\r
+ switch (MenuOption->Sequence) {\r
+ case 0:\r
+ Maximum = 12;\r
+ EraseLen = 4;\r
+ EditValue = QuestionValue->Value.date.Month;\r
+ break;\r
+\r
+ case 1:\r
+ switch (QuestionValue->Value.date.Month) {\r
+ case 2:\r
+ if ((QuestionValue->Value.date.Year % 4) == 0 && \r
+ ((QuestionValue->Value.date.Year % 100) != 0 || \r
+ (QuestionValue->Value.date.Year % 400) == 0)) {\r
+ Maximum = 29;\r
+ } else {\r
+ Maximum = 28;\r
+ }\r
+ break;\r
+ case 4:\r
+ case 6:\r
+ case 9:\r
+ case 11:\r
+ Maximum = 30;\r
+ break;\r
+ default:\r
+ Maximum = 31;\r
+ break;\r
+ } \r
+\r
+ EraseLen = 3;\r
+ EditValue = QuestionValue->Value.date.Day;\r
+ break;\r
+\r
+ case 2:\r
+ Maximum = 0xffff;\r
+ EraseLen = 5;\r
+ EditValue = QuestionValue->Value.date.Year;\r
+ break;\r
+\r
+ default:\r
+ break;\r
+ }\r
+ } else if (Question->Operand == EFI_IFR_TIME_OP) {\r
+ Step = 1;\r
+ Minimum = 0;\r
+\r
+ switch (MenuOption->Sequence) {\r
+ case 0:\r
+ Maximum = 23;\r
+ EraseLen = 4;\r
+ EditValue = QuestionValue->Value.time.Hour;\r
+ break;\r
+\r
+ case 1:\r
+ Maximum = 59;\r
+ EraseLen = 3;\r
+ EditValue = QuestionValue->Value.time.Minute;\r
+ break;\r
+\r
+ case 2:\r
+ Maximum = 59;\r
+ EraseLen = 3;\r
+ EditValue = QuestionValue->Value.time.Second;\r
+ break;\r
+\r
+ default:\r
+ break;\r
+ }\r
+ } else {\r
+ //\r
+ // Numeric\r
+ //\r
+ EraseLen = gOptionBlockWidth;\r
+ EditValue = QuestionValue->Value.u64;\r
+ if (Maximum == 0) {\r
+ Maximum = (UINT64) -1;\r
+ }\r
+ }\r
+\r
+ if ((Question->Operand == EFI_IFR_NUMERIC_OP) &&\r
+ ((Question->Flags & EFI_IFR_DISPLAY) == EFI_IFR_DISPLAY_UINT_HEX)) {\r
+ HexInput = TRUE;\r
+ } else {\r
+ HexInput = FALSE;\r
+ }\r
+\r
+ //\r
+ // Enter from "Enter" input, clear the old word showing.\r
+ //\r
+ if (ManualInput) {\r
+ if (Question->Operand == EFI_IFR_NUMERIC_OP) {\r
+ if (HexInput) {\r
+ InputWidth = Question->StorageWidth * 2;\r
+ } else {\r
+ switch (Question->StorageWidth) {\r
+ case 1:\r
+ InputWidth = 3;\r
+ break;\r
+\r
+ case 2:\r
+ InputWidth = 5;\r
+ break;\r
+\r
+ case 4:\r
+ InputWidth = 10;\r
+ break;\r
+\r
+ case 8:\r
+ InputWidth = 20;\r
+ break;\r
+\r
+ default:\r
+ InputWidth = 0;\r
+ break;\r
+ }\r
+ }\r
+\r
+ InputText[0] = LEFT_NUMERIC_DELIMITER;\r
+ SetUnicodeMem (InputText + 1, InputWidth, L' ');\r
+ ASSERT (InputWidth + 2 < MAX_NUMERIC_INPUT_WIDTH);\r
+ InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER;\r
+ InputText[InputWidth + 2] = L'\0';\r
+\r
+ PrintAt (Column, Row, InputText);\r
+ Column++;\r
+ }\r
+\r
+ if (Question->Operand == EFI_IFR_DATE_OP) {\r
+ if (MenuOption->Sequence == 2) {\r
+ InputWidth = 4;\r
+ } else {\r
+ InputWidth = 2;\r
+ }\r
+\r
+ if (MenuOption->Sequence == 0) {\r
+ InputText[0] = LEFT_NUMERIC_DELIMITER;\r
+ SetUnicodeMem (InputText + 1, InputWidth, L' ');\r
+ } else {\r
+ SetUnicodeMem (InputText, InputWidth, L' ');\r
+ }\r
+\r
+ if (MenuOption->Sequence == 2) {\r
+ InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER;\r
+ } else {\r
+ InputText[InputWidth + 1] = DATE_SEPARATOR;\r
+ }\r
+ InputText[InputWidth + 2] = L'\0';\r
+\r
+ PrintAt (Column, Row, InputText);\r
+ if (MenuOption->Sequence == 0) {\r
+ Column++;\r
+ }\r
+ }\r
+\r
+ if (Question->Operand == EFI_IFR_TIME_OP) {\r
+ InputWidth = 2;\r
+\r
+ if (MenuOption->Sequence == 0) {\r
+ InputText[0] = LEFT_NUMERIC_DELIMITER;\r
+ SetUnicodeMem (InputText + 1, InputWidth, L' ');\r
+ } else {\r
+ SetUnicodeMem (InputText, InputWidth, L' ');\r
+ }\r
+\r
+ if (MenuOption->Sequence == 2) {\r
+ InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER;\r
+ } else {\r
+ InputText[InputWidth + 1] = TIME_SEPARATOR;\r
+ }\r
+ InputText[InputWidth + 2] = L'\0';\r
+\r
+ PrintAt (Column, Row, InputText);\r
+ if (MenuOption->Sequence == 0) {\r
+ Column++;\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ // First time we enter this handler, we need to check to see if\r
+ // we were passed an increment or decrement directive\r
+ //\r
+ do {\r
+ Key.UnicodeChar = CHAR_NULL;\r
+ if (gDirection != 0) {\r
+ Key.ScanCode = gDirection;\r
+ gDirection = 0;\r
+ goto TheKey2;\r
+ }\r
+\r
+ Status = WaitForKeyStroke (&Key);\r
+\r
+TheKey2:\r
+ switch (Key.UnicodeChar) {\r
+\r
+ case '+':\r
+ case '-':\r
+ if (Key.UnicodeChar == '+') {\r
+ Key.ScanCode = SCAN_RIGHT;\r
+ } else {\r
+ Key.ScanCode = SCAN_LEFT;\r
+ }\r
+ Key.UnicodeChar = CHAR_NULL;\r
+ goto TheKey2;\r
+\r
+ case CHAR_NULL:\r
+ switch (Key.ScanCode) {\r
+ case SCAN_LEFT:\r
+ case SCAN_RIGHT:\r
+ if (DateOrTime && !ManualInput) {\r
+ //\r
+ // By setting this value, we will return back to the caller.\r
+ // We need to do this since an auto-refresh will destroy the adjustment\r
+ // based on what the real-time-clock is showing. So we always commit\r
+ // upon changing the value.\r
+ //\r
+ gDirection = SCAN_DOWN;\r
+ }\r
+\r
+ if ((Step != 0) && !ManualInput) {\r
+ if (Key.ScanCode == SCAN_LEFT) {\r
+ if (EditValue >= Minimum + Step) {\r
+ EditValue = EditValue - Step;\r
+ } else if (EditValue > Minimum){\r
+ EditValue = Minimum;\r
+ } else {\r
+ EditValue = Maximum;\r
+ }\r
+ } else if (Key.ScanCode == SCAN_RIGHT) {\r
+ if (EditValue + Step <= Maximum) {\r
+ EditValue = EditValue + Step;\r
+ } else if (EditValue < Maximum) {\r
+ EditValue = Maximum;\r
+ } else {\r
+ EditValue = Minimum;\r
+ }\r
+ }\r
+\r
+ ZeroMem (FormattedNumber, 21 * sizeof (CHAR16));\r
+ if (Question->Operand == EFI_IFR_DATE_OP) {\r
+ if (MenuOption->Sequence == 2) {\r
+ //\r
+ // Year\r
+ //\r
+ UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%04d", (UINT16) EditValue);\r
+ } else {\r
+ //\r
+ // Month/Day\r
+ //\r
+ UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", (UINT8) EditValue);\r
+ }\r
+\r
+ if (MenuOption->Sequence == 0) {\r
+ ASSERT (EraseLen >= 2);\r
+ FormattedNumber[EraseLen - 2] = DATE_SEPARATOR;\r
+ } else if (MenuOption->Sequence == 1) {\r
+ ASSERT (EraseLen >= 1);\r
+ FormattedNumber[EraseLen - 1] = DATE_SEPARATOR;\r
+ }\r
+ } else if (Question->Operand == EFI_IFR_TIME_OP) {\r
+ UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", (UINT8) EditValue);\r
+\r
+ if (MenuOption->Sequence == 0) {\r
+ ASSERT (EraseLen >= 2);\r
+ FormattedNumber[EraseLen - 2] = TIME_SEPARATOR;\r
+ } else if (MenuOption->Sequence == 1) {\r
+ ASSERT (EraseLen >= 1);\r
+ FormattedNumber[EraseLen - 1] = TIME_SEPARATOR;\r
+ }\r
+ } else {\r
+ QuestionValue->Value.u64 = EditValue;\r
+ PrintFormattedNumber (Question, FormattedNumber, 21 * sizeof (CHAR16));\r
+ }\r
+\r
+ gST->ConOut->SetAttribute (gST->ConOut, PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND);\r
+ for (Loop = 0; Loop < EraseLen; Loop++) {\r
+ PrintAt (MenuOption->OptCol + Loop, MenuOption->Row, L" ");\r
+ }\r
+ gST->ConOut->SetAttribute (gST->ConOut, PcdGet8 (PcdBrowserFieldTextHighlightColor) | PcdGet8 (PcdBrowserFieldBackgroundHighlightColor));\r
+\r
+ if (MenuOption->Sequence == 0) {\r
+ PrintCharAt (MenuOption->OptCol, Row, LEFT_NUMERIC_DELIMITER);\r
+ Column = MenuOption->OptCol + 1;\r
+ }\r
+\r
+ PrintStringAt (Column, Row, FormattedNumber);\r
+\r
+ if (!DateOrTime || MenuOption->Sequence == 2) {\r
+ PrintChar (RIGHT_NUMERIC_DELIMITER);\r
+ }\r
+ }\r
+\r
+ goto EnterCarriageReturn;\r
+ break;\r
+\r
+ case SCAN_UP:\r
+ case SCAN_DOWN:\r
+ goto EnterCarriageReturn;\r
+\r
+ case SCAN_ESC:\r
+ return EFI_DEVICE_ERROR;\r
+\r
+ default:\r
+ break;\r
+ }\r
+\r
+ break;\r
+\r
+EnterCarriageReturn:\r
+\r
+ case CHAR_CARRIAGE_RETURN:\r
+ //\r
+ // Validate input value with Minimum value.\r
+ //\r
+ if (EditValue < Minimum) {\r
+ UpdateStatusBar (Selection, INPUT_ERROR, Question->QuestionFlags, TRUE);\r
+ break;\r
+ } else {\r
+ UpdateStatusBar (Selection, INPUT_ERROR, Question->QuestionFlags, FALSE);\r
+ }\r
+\r
+ //\r
+ // Store Edit value back to Question\r
+ //\r
+ if (Question->Operand == EFI_IFR_DATE_OP) {\r
+ switch (MenuOption->Sequence) {\r
+ case 0:\r
+ QuestionValue->Value.date.Month = (UINT8) EditValue;\r
+ break;\r
+\r
+ case 1:\r
+ QuestionValue->Value.date.Day = (UINT8) EditValue;\r
+ break;\r
+\r
+ case 2:\r
+ QuestionValue->Value.date.Year = (UINT16) EditValue;\r
+ break;\r
+\r
+ default:\r
+ break;\r
+ }\r
+ } else if (Question->Operand == EFI_IFR_TIME_OP) {\r
+ switch (MenuOption->Sequence) {\r
+ case 0:\r
+ QuestionValue->Value.time.Hour = (UINT8) EditValue;\r
+ break;\r
+\r
+ case 1:\r
+ QuestionValue->Value.time.Minute = (UINT8) EditValue;\r
+ break;\r
+\r
+ case 2:\r
+ QuestionValue->Value.time.Second = (UINT8) EditValue;\r
+ break;\r
+\r
+ default:\r
+ break;\r
+ }\r
+ } else {\r
+ //\r
+ // Numeric\r
+ //\r
+ QuestionValue->Value.u64 = EditValue;\r
+ }\r
+\r
+ //\r
+ // Adjust the value to the correct one.\r
+ // Sample like: 2012.02.29 -> 2013.02.29 -> 2013.02.01\r
+ // 2013.03.29 -> 2013.02.29 -> 2013.02.28\r
+ //\r
+ if (Question->Operand == EFI_IFR_DATE_OP && \r
+ (MenuOption->Sequence == 0 || MenuOption->Sequence == 2)) {\r
+ AdjustQuestionValue (Question, (UINT8)MenuOption->Sequence);\r
+ }\r
+\r
+ //\r
+ // Check to see if the Value is something reasonable against consistency limitations.\r
+ // If not, let's kick the error specified.\r
+ //\r
+ Status = ValidateQuestion (FormSet, Form, Question, EFI_HII_EXPRESSION_INCONSISTENT_IF);\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // Input value is not valid, restore Question Value\r
+ //\r
+ GetQuestionValue (FormSet, Form, Question, GetSetValueWithEditBuffer);\r
+ } else {\r
+ SetQuestionValue (FormSet, Form, Question, GetSetValueWithEditBuffer);\r
+ if (!DateOrTime || (Question->Storage != NULL)) {\r
+ //\r
+ // NV flag is unnecessary for RTC type of Date/Time\r
+ //\r
+ UpdateStatusBar (Selection, NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE);\r
+ }\r
+ }\r
+\r
+ return Status;\r
+ break;\r
+\r
+ case CHAR_BACKSPACE:\r
+ if (ManualInput) {\r
+ if (Count == 0) {\r
+ break;\r
+ }\r
+ //\r
+ // Remove a character\r
+ //\r
+ EditValue = PreviousNumber[Count - 1];\r
+ UpdateStatusBar (Selection, INPUT_ERROR, Question->QuestionFlags, FALSE);\r
+ Count--;\r
+ Column--;\r
+ PrintAt (Column, Row, L" ");\r
+ }\r
+ break;\r
+\r
+ default:\r
+ if (ManualInput) {\r
+ if (HexInput) {\r
+ if ((Key.UnicodeChar >= L'0') && (Key.UnicodeChar <= L'9')) {\r
+ Digital = (UINT8) (Key.UnicodeChar - L'0');\r
+ } else if ((Key.UnicodeChar >= L'A') && (Key.UnicodeChar <= L'F')) {\r
+ Digital = (UINT8) (Key.UnicodeChar - L'A' + 0x0A);\r
+ } else if ((Key.UnicodeChar >= L'a') && (Key.UnicodeChar <= L'f')) {\r
+ Digital = (UINT8) (Key.UnicodeChar - L'a' + 0x0A);\r
+ } else {\r
+ UpdateStatusBar (Selection, INPUT_ERROR, Question->QuestionFlags, TRUE);\r
+ break;\r
+ }\r
+ } else {\r
+ if (Key.UnicodeChar > L'9' || Key.UnicodeChar < L'0') {\r
+ UpdateStatusBar (Selection, INPUT_ERROR, Question->QuestionFlags, TRUE);\r
+ break;\r
+ }\r
+ }\r
+\r
+ //\r
+ // If Count exceed input width, there is no way more is valid\r
+ //\r
+ if (Count >= InputWidth) {\r
+ break;\r
+ }\r
+ //\r
+ // Someone typed something valid!\r
+ //\r
+ if (Count != 0) {\r
+ if (HexInput) {\r
+ EditValue = LShiftU64 (EditValue, 4) + Digital;\r
+ } else {\r
+ EditValue = MultU64x32 (EditValue, 10) + (Key.UnicodeChar - L'0');\r
+ }\r
+ } else {\r
+ if (HexInput) {\r
+ EditValue = Digital;\r
+ } else {\r
+ EditValue = Key.UnicodeChar - L'0';\r
+ }\r
+ }\r
+\r
+ if (EditValue > Maximum) {\r
+ UpdateStatusBar (Selection, INPUT_ERROR, Question->QuestionFlags, TRUE);\r
+ ASSERT (Count < sizeof (PreviousNumber) / sizeof (PreviousNumber[0]));\r
+ EditValue = PreviousNumber[Count];\r
+ break;\r
+ } else {\r
+ UpdateStatusBar (Selection, INPUT_ERROR, Question->QuestionFlags, FALSE);\r
+ }\r
+\r
+ Count++;\r
+ ASSERT (Count < (sizeof (PreviousNumber) / sizeof (PreviousNumber[0])));\r
+ PreviousNumber[Count] = EditValue;\r
+\r
+ PrintCharAt (Column, Row, Key.UnicodeChar);\r
+ Column++;\r
+ }\r
+ break;\r
+ }\r
+ } while (TRUE);\r
+\r
+}\r
+\r
+\r
+/**\r
+ Get selection for OneOf and OrderedList (Left/Right will be ignored).\r
+\r
+ @param Selection Pointer to current selection.\r
+ @param MenuOption Pointer to the current input menu.\r
+\r
+ @retval EFI_SUCCESS If Option input is processed successfully\r
+ @retval EFI_DEVICE_ERROR If operation fails\r
+\r
+**/\r
+EFI_STATUS\r
+GetSelectionInputPopUp (\r
+ IN UI_MENU_SELECTION *Selection,\r
+ IN UI_MENU_OPTION *MenuOption\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_INPUT_KEY Key;\r
+ UINTN Index;\r
+ CHAR16 *StringPtr;\r
+ CHAR16 *TempStringPtr;\r
+ UINTN Index2;\r
+ UINTN TopOptionIndex;\r
+ UINTN HighlightOptionIndex;\r
+ UINTN Start;\r
+ UINTN End;\r
+ UINTN Top;\r
+ UINTN Bottom;\r
+ UINTN PopUpMenuLines;\r
+ UINTN MenuLinesInView;\r
+ UINTN PopUpWidth;\r
+ CHAR16 Character;\r
+ INT32 SavedAttribute;\r
+ BOOLEAN ShowDownArrow;\r
+ BOOLEAN ShowUpArrow;\r
+ UINTN DimensionsWidth;\r
+ LIST_ENTRY *Link;\r
+ BOOLEAN OrderedList;\r
+ UINT8 *ValueArray;\r
+ UINT8 ValueType;\r
+ EFI_HII_VALUE HiiValue;\r
+ EFI_HII_VALUE *HiiValueArray;\r
+ UINTN OptionCount;\r
+ QUESTION_OPTION *OneOfOption;\r
+ QUESTION_OPTION *CurrentOption;\r
+ FORM_BROWSER_STATEMENT *Question;\r
+ INTN Result;\r
+\r
+ DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn;\r
+\r
+ ValueArray = NULL;\r
+ ValueType = 0;\r
+ CurrentOption = NULL;\r
+ ShowDownArrow = FALSE;\r
+ ShowUpArrow = FALSE;\r
+\r
+ StringPtr = AllocateZeroPool ((gOptionBlockWidth + 1) * 2);\r
+ ASSERT (StringPtr);\r
+\r
+ Question = MenuOption->ThisTag;\r
+ if (Question->Operand == EFI_IFR_ORDERED_LIST_OP) {\r
+ ValueArray = Question->BufferValue;\r
+ ValueType = Question->ValueType;\r
+ OrderedList = TRUE;\r
+ } else {\r
+ OrderedList = FALSE;\r
+ }\r
+\r
+ //\r
+ // Calculate Option count\r
+ //\r
+ if (OrderedList) {\r
+ for (Index = 0; Index < Question->MaxContainers; Index++) {\r
+ if (GetArrayData (ValueArray, ValueType, Index) == 0) {\r
+ break;\r
+ }\r
+ }\r
+\r
+ OptionCount = Index;\r
+ } else {\r
+ OptionCount = 0;\r
+ Link = GetFirstNode (&Question->OptionListHead);\r
+ while (!IsNull (&Question->OptionListHead, Link)) {\r
+ OneOfOption = QUESTION_OPTION_FROM_LINK (Link);\r
+\r
+ OptionCount++;\r
+\r
+ Link = GetNextNode (&Question->OptionListHead, Link);\r
+ }\r
+ }\r
+\r
+ //\r
+ // Move valid Option to list head.\r
+ //\r
+ PopUpMenuLines = 0;\r
+ if (OrderedList) {\r
+ //\r
+ // Prepare HiiValue array\r
+ // \r
+ HiiValueArray = AllocateZeroPool (OptionCount * sizeof (EFI_HII_VALUE));\r
+ ASSERT (HiiValueArray != NULL);\r
+ for (Index = 0; Index < OptionCount; Index++) {\r
+ HiiValueArray[Index].Type = ValueType;\r
+ HiiValueArray[Index].Value.u64 = GetArrayData (ValueArray, ValueType, Index);\r
+ }\r
+\r
+ for (Index = 0; Index < OptionCount; Index++) {\r
+ OneOfOption = ValueToOption (Question, &HiiValueArray[OptionCount - Index - 1]);\r
+ if (OneOfOption == NULL) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ RemoveEntryList (&OneOfOption->Link);\r
+\r
+ //\r
+ // Insert to head.\r
+ //\r
+ InsertHeadList (&Question->OptionListHead, &OneOfOption->Link);\r
+\r
+ PopUpMenuLines++;\r
+ }\r
+\r
+ FreePool (HiiValueArray);\r
+ } else {\r
+ Link = GetFirstNode (&Question->OptionListHead);\r
+ for (Index = 0; Index < OptionCount; Index++) {\r
+ OneOfOption = QUESTION_OPTION_FROM_LINK (Link);\r
+ Link = GetNextNode (&Question->OptionListHead, Link);\r
+ if ((OneOfOption->SuppressExpression == NULL) ||\r
+ EvaluateExpressionList(OneOfOption->SuppressExpression, FALSE, NULL, NULL) == ExpressFalse) {\r
+ RemoveEntryList (&OneOfOption->Link);\r
+ InsertHeadList (&Question->OptionListHead, &OneOfOption->Link);\r
+ PopUpMenuLines++;\r
+ }\r
+ }\r
+ }\r
+\r
+ //\r
+ // Get the number of one of options present and its size\r
+ //\r
+ PopUpWidth = 0;\r
+ HighlightOptionIndex = 0;\r
+ Link = GetFirstNode (&Question->OptionListHead);\r
+ for (Index = 0; Index < PopUpMenuLines; Index++) {\r
+ OneOfOption = QUESTION_OPTION_FROM_LINK (Link);\r
+\r
+ StringPtr = GetToken (OneOfOption->Text, MenuOption->Handle);\r
+ if (StrLen (StringPtr) > PopUpWidth) {\r
+ PopUpWidth = StrLen (StringPtr);\r
+ }\r
+ FreePool (StringPtr);\r
+\r
+ if (!OrderedList && (CompareHiiValue (&Question->HiiValue, &OneOfOption->Value, &Result, NULL) == EFI_SUCCESS) && (Result == 0)) {\r
+ //\r
+ // Find current selected Option for OneOf\r
+ //\r
+ HighlightOptionIndex = Index;\r
+ }\r
+\r
+ Link = GetNextNode (&Question->OptionListHead, Link);\r
+ }\r
+\r
+ //\r
+ // Perform popup menu initialization.\r
+ //\r
+ PopUpWidth = PopUpWidth + POPUP_PAD_SPACE_COUNT;\r
+\r
+ SavedAttribute = gST->ConOut->Mode->Attribute;\r
+ gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);\r
+\r
+ if ((PopUpWidth + POPUP_FRAME_WIDTH) > DimensionsWidth) {\r
+ PopUpWidth = DimensionsWidth - POPUP_FRAME_WIDTH;\r
+ }\r
+\r
+ Start = (DimensionsWidth - PopUpWidth - POPUP_FRAME_WIDTH) / 2 + gScreenDimensions.LeftColumn;\r
+ End = Start + PopUpWidth + POPUP_FRAME_WIDTH;\r
+ Top = gScreenDimensions.TopRow + NONE_FRONT_PAGE_HEADER_HEIGHT;\r
+ Bottom = gScreenDimensions.BottomRow - STATUS_BAR_HEIGHT - gFooterHeight - 1;\r
+\r
+ MenuLinesInView = Bottom - Top - 1;\r
+ if (MenuLinesInView >= PopUpMenuLines) {\r
+ Top = Top + (MenuLinesInView - PopUpMenuLines) / 2;\r
+ Bottom = Top + PopUpMenuLines + 1;\r
+ } else {\r
+ ShowDownArrow = TRUE;\r
+ }\r
+\r
+ if (HighlightOptionIndex > (MenuLinesInView - 1)) {\r
+ TopOptionIndex = HighlightOptionIndex - MenuLinesInView + 1;\r
+ } else {\r
+ TopOptionIndex = 0;\r
+ }\r
+\r
+ do {\r
+ //\r
+ // Clear that portion of the screen\r
+ //\r
+ ClearLines (Start, End, Top, Bottom, POPUP_TEXT | POPUP_BACKGROUND);\r
+\r
+ //\r
+ // Draw "One of" pop-up menu\r
+ //\r
+ Character = BOXDRAW_DOWN_RIGHT;\r
+ PrintCharAt (Start, Top, Character);\r
+ for (Index = Start; Index + 2 < End; Index++) {\r
+ if ((ShowUpArrow) && ((Index + 1) == (Start + End) / 2)) {\r
+ Character = GEOMETRICSHAPE_UP_TRIANGLE;\r
+ } else {\r
+ Character = BOXDRAW_HORIZONTAL;\r
+ }\r
+\r
+ PrintChar (Character);\r
+ }\r
+\r
+ Character = BOXDRAW_DOWN_LEFT;\r
+ PrintChar (Character);\r
+ Character = BOXDRAW_VERTICAL;\r
+ for (Index = Top + 1; Index < Bottom; Index++) {\r
+ PrintCharAt (Start, Index, Character);\r
+ PrintCharAt (End - 1, Index, Character);\r
+ }\r
+\r
+ //\r
+ // Move to top Option\r
+ //\r
+ Link = GetFirstNode (&Question->OptionListHead);\r
+ for (Index = 0; Index < TopOptionIndex; Index++) {\r
+ Link = GetNextNode (&Question->OptionListHead, Link);\r
+ }\r
+\r
+ //\r
+ // Display the One of options\r
+ //\r
+ Index2 = Top + 1;\r
+ for (Index = TopOptionIndex; (Index < PopUpMenuLines) && (Index2 < Bottom); Index++) {\r
+ OneOfOption = QUESTION_OPTION_FROM_LINK (Link);\r
+ Link = GetNextNode (&Question->OptionListHead, Link);\r
+\r
+ StringPtr = GetToken (OneOfOption->Text, MenuOption->Handle);\r
+ ASSERT (StringPtr != NULL);\r
+ //\r
+ // If the string occupies multiple lines, truncate it to fit in one line,\r
+ // and append a "..." for indication.\r
+ //\r
+ if (StrLen (StringPtr) > (PopUpWidth - 1)) {\r
+ TempStringPtr = AllocateZeroPool (sizeof (CHAR16) * (PopUpWidth - 1));\r
+ ASSERT ( TempStringPtr != NULL );\r
+ CopyMem (TempStringPtr, StringPtr, (sizeof (CHAR16) * (PopUpWidth - 5)));\r
+ FreePool (StringPtr);\r
+ StringPtr = TempStringPtr;\r
+ StrCat (StringPtr, L"...");\r
+ }\r
+\r
+ if (Index == HighlightOptionIndex) {\r
+ //\r
+ // Highlight the selected one\r
+ //\r
+ CurrentOption = OneOfOption;\r
+\r
+ gST->ConOut->SetAttribute (gST->ConOut, PICKLIST_HIGHLIGHT_TEXT | PICKLIST_HIGHLIGHT_BACKGROUND);\r
+ PrintStringAt (Start + 2, Index2, StringPtr);\r
+ gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);\r
+ } else {\r
+ gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);\r
+ PrintStringAt (Start + 2, Index2, StringPtr);\r
+ }\r
+\r
+ Index2++;\r
+ FreePool (StringPtr);\r
+ }\r
+\r
+ Character = BOXDRAW_UP_RIGHT;\r
+ PrintCharAt (Start, Bottom, Character);\r
+ for (Index = Start; Index + 2 < End; Index++) {\r
+ if ((ShowDownArrow) && ((Index + 1) == (Start + End) / 2)) {\r
+ Character = GEOMETRICSHAPE_DOWN_TRIANGLE;\r
+ } else {\r
+ Character = BOXDRAW_HORIZONTAL;\r
+ }\r
+\r
+ PrintChar (Character);\r
+ }\r
+\r
+ Character = BOXDRAW_UP_LEFT;\r
+ PrintChar (Character);\r
+\r
+ //\r
+ // Get User selection\r
+ //\r
+ Key.UnicodeChar = CHAR_NULL;\r
+ if ((gDirection == SCAN_UP) || (gDirection == SCAN_DOWN)) {\r
+ Key.ScanCode = gDirection;\r
+ gDirection = 0;\r
+ goto TheKey;\r
+ }\r
+\r
+ Status = WaitForKeyStroke (&Key);\r
+\r
+TheKey:\r
+ switch (Key.UnicodeChar) {\r
+ case '+':\r
+ if (OrderedList) {\r
+ if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) {\r
+ //\r
+ // Highlight reaches the top of the popup window, scroll one menu item.\r
+ //\r
+ TopOptionIndex--;\r
+ ShowDownArrow = TRUE;\r
+ }\r
+\r
+ if (TopOptionIndex == 0) {\r
+ ShowUpArrow = FALSE;\r
+ }\r
+\r
+ if (HighlightOptionIndex > 0) {\r
+ HighlightOptionIndex--;\r
+\r
+ ASSERT (CurrentOption != NULL);\r
+ SwapListEntries (CurrentOption->Link.BackLink, &CurrentOption->Link);\r
+ }\r
+ }\r
+ break;\r
+\r
+ case '-':\r
+ //\r
+ // If an ordered list op-code, we will allow for a popup of +/- keys\r
+ // to create an ordered list of items\r
+ //\r
+ if (OrderedList) {\r
+ if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) &&\r
+ (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1))) {\r
+ //\r
+ // Highlight reaches the bottom of the popup window, scroll one menu item.\r
+ //\r
+ TopOptionIndex++;\r
+ ShowUpArrow = TRUE;\r
+ }\r
+\r
+ if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) {\r
+ ShowDownArrow = FALSE;\r
+ }\r
+\r
+ if (HighlightOptionIndex < (PopUpMenuLines - 1)) {\r
+ HighlightOptionIndex++;\r
+\r
+ ASSERT (CurrentOption != NULL);\r
+ SwapListEntries (&CurrentOption->Link, CurrentOption->Link.ForwardLink);\r
+ }\r
+ }\r
+ break;\r
+\r
+ case CHAR_NULL:\r
+ switch (Key.ScanCode) {\r
+ case SCAN_UP:\r
+ case SCAN_DOWN:\r
+ if (Key.ScanCode == SCAN_UP) {\r
+ if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) {\r
+ //\r
+ // Highlight reaches the top of the popup window, scroll one menu item.\r
+ //\r
+ TopOptionIndex--;\r
+ ShowDownArrow = TRUE;\r
+ }\r
+\r
+ if (TopOptionIndex == 0) {\r
+ ShowUpArrow = FALSE;\r
+ }\r
+\r
+ if (HighlightOptionIndex > 0) {\r
+ HighlightOptionIndex--;\r
+ }\r
+ } else {\r
+ if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) &&\r
+ (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1))) {\r
+ //\r
+ // Highlight reaches the bottom of the popup window, scroll one menu item.\r
+ //\r
+ TopOptionIndex++;\r
+ ShowUpArrow = TRUE;\r
+ }\r
+\r
+ if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) {\r
+ ShowDownArrow = FALSE;\r
+ }\r
+\r
+ if (HighlightOptionIndex < (PopUpMenuLines - 1)) {\r
+ HighlightOptionIndex++;\r
+ }\r
+ }\r
+ break;\r
+\r
+ case SCAN_ESC:\r
+ gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);\r
+\r
+ //\r
+ // Restore link list order for orderedlist\r
+ //\r
+ if (OrderedList) {\r
+ HiiValue.Type = ValueType;\r
+ HiiValue.Value.u64 = 0;\r
+ for (Index = 0; Index < Question->MaxContainers; Index++) {\r
+ HiiValue.Value.u64 = GetArrayData (ValueArray, ValueType, Index);\r
+ if (HiiValue.Value.u64 == 0) {\r
+ break;\r
+ }\r
+\r
+ OneOfOption = ValueToOption (Question, &HiiValue);\r
+ if (OneOfOption == NULL) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ RemoveEntryList (&OneOfOption->Link);\r
+ InsertTailList (&Question->OptionListHead, &OneOfOption->Link);\r
+ }\r
+ }\r
+\r
+ return EFI_DEVICE_ERROR;\r
+\r
+ default:\r
+ break;\r
+ }\r
+\r
+ break;\r
+\r
+ case CHAR_CARRIAGE_RETURN:\r
+ //\r
+ // return the current selection\r
+ //\r
+ if (OrderedList) {\r
+ Index = 0;\r
+ Link = GetFirstNode (&Question->OptionListHead);\r
+ while (!IsNull (&Question->OptionListHead, Link)) {\r
+ OneOfOption = QUESTION_OPTION_FROM_LINK (Link);\r
+ Link = GetNextNode (&Question->OptionListHead, Link);\r
+\r
+ if ((OneOfOption->SuppressExpression != NULL) &&\r
+ EvaluateExpressionList(OneOfOption->SuppressExpression, FALSE, NULL, NULL) != ExpressFalse) {\r
+ continue;\r
+ }\r
+\r
+ SetArrayData (ValueArray, ValueType, Index, OneOfOption->Value.Value.u64);\r
+\r
+ Index++;\r
+ if (Index > Question->MaxContainers) {\r
+ break;\r
+ }\r
+ }\r
+ } else {\r
+ ASSERT (CurrentOption != NULL);\r
+ CopyMem (&Question->HiiValue, &CurrentOption->Value, sizeof (EFI_HII_VALUE));\r
+ }\r
+\r
+ gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);\r
+\r
+ Status = ValidateQuestion (Selection->FormSet, Selection->Form, Question, EFI_HII_EXPRESSION_INCONSISTENT_IF);\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // Input value is not valid, restore Question Value\r
+ //\r
+ GetQuestionValue (Selection->FormSet, Selection->Form, Question, GetSetValueWithEditBuffer);\r
+ } else {\r
+ SetQuestionValue (Selection->FormSet, Selection->Form, Question, GetSetValueWithEditBuffer);\r
+ UpdateStatusBar (Selection, NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE);\r
+ }\r
+\r
+ return Status;\r
+\r
+ default:\r
+ break;\r
+ }\r
+ } while (TRUE);\r
+\r
+}\r
+\r
+/**\r
+ Wait for a key to be pressed by user.\r
+\r
+ @param Key The key which is pressed by user.\r
+\r
+ @retval EFI_SUCCESS The function always completed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+WaitForKeyStroke (\r
+ OUT EFI_INPUT_KEY *Key\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+\r
+ do {\r
+ UiWaitForSingleEvent (gST->ConIn->WaitForKey, 0, 0);\r
+ Status = gST->ConIn->ReadKeyStroke (gST->ConIn, Key);\r
+ } while (EFI_ERROR(Status));\r
+\r
+ return Status;\r
+}\r