Example #1
0
/**
  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;
    UINT8                   ValueType;
    EFI_HII_VALUE           HiiValue;
    EFI_HII_VALUE           *HiiValueArray;
    UINTN                   OptionCount;
    QUESTION_OPTION         *OneOfOption;
    QUESTION_OPTION         *CurrentOption;
    FORM_BROWSER_STATEMENT  *Question;
    INTN                    Result;

    DimensionsWidth   = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn;

    ValueArray        = NULL;
    ValueType         = 0;
    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;
        ValueType = Question->ValueType;
        OrderedList = TRUE;
    } else {
        OrderedList = FALSE;
    }

    //
    // Calculate Option count
    //
    if (OrderedList) {
        for (Index = 0; Index < Question->MaxContainers; Index++) {
            if (GetArrayData (ValueArray, ValueType, 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 = ValueType;
            HiiValueArray[Index].Value.u64 = GetArrayData (ValueArray, ValueType, 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) &&
                EvaluateExpressionList(OneOfOption->SuppressExpression, FALSE, NULL, NULL) != ExpressFalse) {
            //
            // 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);
        }
        FreePool (StringPtr);

        if (!OrderedList && (CompareHiiValue (&Question->HiiValue, &OneOfOption->Value, &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, 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 - gFooterHeight - 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);
            ASSERT (StringPtr != NULL);
            //
            // If the string occupies multiple lines, truncate it to fit in one line,
            // and append a "..." for indication.
            //
            if (StrLen (StringPtr) > (PopUpWidth - 1)) {
                TempStringPtr = AllocateZeroPool (sizeof (CHAR16) * (PopUpWidth - 1));
                ASSERT ( TempStringPtr != NULL );
                CopyMem (TempStringPtr, StringPtr, (sizeof (CHAR16) * (PopUpWidth - 5)));
                FreePool (StringPtr);
                StringPtr = TempStringPtr;
                StrCat (StringPtr, L"...");
            }

            if (Index == HighlightOptionIndex) {
                //
                // Highlight the selected one
                //
                CurrentOption = OneOfOption;

                gST->ConOut->SetAttribute (gST->ConOut, 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++;
            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--;

                    ASSERT (CurrentOption != NULL);
                    SwapListEntries (CurrentOption->Link.BackLink, &CurrentOption->Link);
                }
            }
            break;

        case '-':
            //
            // If an ordered list op-code, we will allow for a popup of +/- keys
            // to create an ordered list of items
            //
            if (OrderedList) {
                if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) &&
                        (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1))) {
                    //
                    // Highlight reaches the bottom of the popup window, scroll one menu item.
                    //
                    TopOptionIndex++;
                    ShowUpArrow = TRUE;
                }

                if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) {
                    ShowDownArrow = FALSE;
                }

                if (HighlightOptionIndex < (PopUpMenuLines - 1)) {
                    HighlightOptionIndex++;

                    ASSERT (CurrentOption != NULL);
                    SwapListEntries (&CurrentOption->Link, CurrentOption->Link.ForwardLink);
                }
            }
            break;

        case CHAR_NULL:
            switch (Key.ScanCode) {
            case SCAN_UP:
            case SCAN_DOWN:
                if (Key.ScanCode == SCAN_UP) {
                    if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) {
                        //
                        // Highlight reaches the top of the popup window, scroll one menu item.
                        //
                        TopOptionIndex--;
                        ShowDownArrow = TRUE;
                    }

                    if (TopOptionIndex == 0) {
                        ShowUpArrow = FALSE;
                    }

                    if (HighlightOptionIndex > 0) {
                        HighlightOptionIndex--;
                    }
                } else {
                    if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) &&
                            (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1))) {
                        //
                        // Highlight reaches the bottom of the popup window, scroll one menu item.
                        //
                        TopOptionIndex++;
                        ShowUpArrow = TRUE;
                    }

                    if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) {
                        ShowDownArrow = FALSE;
                    }

                    if (HighlightOptionIndex < (PopUpMenuLines - 1)) {
                        HighlightOptionIndex++;
                    }
                }
                break;

            case SCAN_ESC:
                gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);

                //
                // Restore link list order for orderedlist
                //
                if (OrderedList) {
                    HiiValue.Type = ValueType;
                    HiiValue.Value.u64 = 0;
                    for (Index = 0; Index < Question->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);
                    }
                }

                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);

                    SetArrayData (ValueArray, ValueType, Index, OneOfOption->Value.Value.u64);

                    Index++;
                    if (Index > Question->MaxContainers) {
                        break;
                    }

                    Link = GetNextNode (&Question->OptionListHead, Link);
                }
            } else {
                ASSERT (CurrentOption != NULL);
                CopyMem (&Question->HiiValue, &CurrentOption->Value, sizeof (EFI_HII_VALUE));
            }

            gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);
            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 (Selection, NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE);
            }

            return Status;

        default:
            break;
        }
    } while (TRUE);

}
/**
  Process a Question's Option (whether selected or un-selected).

  @param  Selection              Pointer to UI_MENU_SELECTION.
  @param  MenuOption             The MenuOption for this Question.
  @param  Selected               TRUE: if Question is selected.
  @param  OptionString           Pointer of the Option String to be displayed.

  @retval EFI_SUCCESS            Question Option process success.
  @retval Other                  Question Option process fail.

**/
EFI_STATUS
ProcessOptions (
  IN  UI_MENU_SELECTION           *Selection,
  IN  UI_MENU_OPTION              *MenuOption,
  IN  BOOLEAN                     Selected,
  OUT CHAR16                      **OptionString
  )
{
  EFI_STATUS                      Status;
  CHAR16                          *StringPtr;
  CHAR16                          *TempString;
  UINTN                           Index;
  FORM_BROWSER_STATEMENT          *Question;
  CHAR16                          FormattedNumber[21];
  UINT16                          Number;
  CHAR16                          Character[2];
  EFI_INPUT_KEY                   Key;
  UINTN                           BufferSize;
  QUESTION_OPTION                 *OneOfOption;
  LIST_ENTRY                      *Link;
  EFI_HII_VALUE                   HiiValue;
  EFI_HII_VALUE                   *QuestionValue;
  BOOLEAN                         Suppress;
  UINT16                          Maximum;
  QUESTION_OPTION                 *Option;
  UINTN                           Index2;
  UINT8                           *ValueArray;
  UINT8                           ValueType;
  EFI_STRING_ID                   StringId;

  Status        = EFI_SUCCESS;

  StringPtr     = NULL;
  Character[1]  = L'\0';
  *OptionString = NULL;
  StringId      = 0;

  ZeroMem (FormattedNumber, 21 * sizeof (CHAR16));
  BufferSize = (gOptionBlockWidth + 1) * 2 * gScreenDimensions.BottomRow;

  Question = MenuOption->ThisTag;
  QuestionValue = &Question->HiiValue;
  Maximum = (UINT16) Question->Maximum;

  ValueArray = Question->BufferValue;
  ValueType = Question->ValueType;

  switch (Question->Operand) {
  case EFI_IFR_ORDERED_LIST_OP:
    //
    // Check whether there are Options of this OrderedList
    //
    if (IsListEmpty (&Question->OptionListHead)) {
      break;
    }
    //
    // Initialize Option value array
    //
    if (GetArrayData (ValueArray, ValueType, 0) == 0) {
      GetQuestionDefault (Selection->FormSet, Selection->Form, Question, 0);
    }

    if (Selected) {
      //
      // Go ask for input
      //
      Status = GetSelectionInputPopUp (Selection, MenuOption);
    } else {
      //
      // We now know how many strings we will have, so we can allocate the
      // space required for the array or strings.
      //
      *OptionString = AllocateZeroPool (Question->MaxContainers * BufferSize);
      ASSERT (*OptionString);

      HiiValue.Type = ValueType;
      HiiValue.Value.u64 = 0;
      for (Index = 0; Index < Question->MaxContainers; Index++) {
        HiiValue.Value.u64 = GetArrayData (ValueArray, ValueType, Index);
        if (HiiValue.Value.u64 == 0) {
          //
          // Values for the options in ordered lists should never be a 0
          //
          break;
        }

        OneOfOption = ValueToOption (Question, &HiiValue);
        if (OneOfOption == NULL) {
          //
          // Show error message
          //
          do {
            CreateDialog (4, TRUE, 0, NULL, &Key, gEmptyString, gOptionMismatch, gPressEnter, gEmptyString);
          } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);

          //
          // The initial value of the orderedlist is invalid, force to be valid value
          //
          Link = GetFirstNode (&Question->OptionListHead);
          Index2 = 0;
          while (!IsNull (&Question->OptionListHead, Link) && Index2 < Question->MaxContainers) {
            Option = QUESTION_OPTION_FROM_LINK (Link);
            SetArrayData (ValueArray, ValueType, Index2, Option->Value.Value.u64);
            Index2++;
            Link = GetNextNode (&Question->OptionListHead, Link);
          }
          SetArrayData (ValueArray, ValueType, Index2, 0);

          Status = SetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE);
          UpdateStatusBar (Selection, NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE);

          FreePool (*OptionString);
          *OptionString = NULL;
          return EFI_NOT_FOUND;
        }

        Suppress = FALSE;
        if ((OneOfOption->SuppressExpression != NULL) &&
            (EvaluateExpressionList(OneOfOption->SuppressExpression, FALSE, NULL, NULL) == ExpressSuppress)) {
          //
          // This option is suppressed
          //
          Suppress = TRUE;
        }

        if (!Suppress) {
          Character[0] = LEFT_ONEOF_DELIMITER;
          NewStrCat (OptionString[0], Character);
          StringPtr = GetToken (OneOfOption->Text, Selection->Handle);
          ASSERT (StringPtr != NULL);
          NewStrCat (OptionString[0], StringPtr);
          Character[0] = RIGHT_ONEOF_DELIMITER;
          NewStrCat (OptionString[0], Character);
          Character[0] = CHAR_CARRIAGE_RETURN;
          NewStrCat (OptionString[0], Character);

          FreePool (StringPtr);
        }
      }
    }
    break;

  case EFI_IFR_ONE_OF_OP:
    //
    // Check whether there are Options of this OneOf
    //
    if (IsListEmpty (&Question->OptionListHead)) {
      break;
    }
    if (Selected) {
      //
      // Go ask for input
      //
      Status = GetSelectionInputPopUp (Selection, MenuOption);
    } else {
      *OptionString = AllocateZeroPool (BufferSize);
      ASSERT (*OptionString);

      OneOfOption = ValueToOption (Question, QuestionValue);
      if (OneOfOption == NULL) {
        //
        // Show error message
        //
        do {
          CreateDialog (4, TRUE, 0, NULL, &Key, gEmptyString, gOptionMismatch, gPressEnter, gEmptyString);
        } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);

        //
        // Force the Question value to be valid
        //
        Link = GetFirstNode (&Question->OptionListHead);
        while (!IsNull (&Question->OptionListHead, Link)) {
          Option = QUESTION_OPTION_FROM_LINK (Link);

          if ((Option->SuppressExpression == NULL) ||
              (EvaluateExpressionList(Option->SuppressExpression, FALSE, NULL, NULL) == ExpressFalse)) {
            CopyMem (QuestionValue, &Option->Value, sizeof (EFI_HII_VALUE));
            SetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE);
            UpdateStatusBar (Selection, NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE);
            break;
          }

          Link = GetNextNode (&Question->OptionListHead, Link);
        }

        FreePool (*OptionString);
        *OptionString = NULL;
        return EFI_NOT_FOUND;
      }

      if ((OneOfOption->SuppressExpression != NULL) &&
          ((EvaluateExpressionList(OneOfOption->SuppressExpression, FALSE, NULL, NULL) == ExpressSuppress))) {
        //
        // This option is suppressed
        //
        Suppress = TRUE;
      } else {
        Suppress = FALSE;
      }

      if (Suppress) {
        //
        // Current selected option happen to be suppressed,
        // enforce to select on a non-suppressed option
        //
        Link = GetFirstNode (&Question->OptionListHead);
        while (!IsNull (&Question->OptionListHead, Link)) {
          OneOfOption = QUESTION_OPTION_FROM_LINK (Link);

          if ((OneOfOption->SuppressExpression == NULL) ||
              (EvaluateExpressionList(OneOfOption->SuppressExpression, FALSE, NULL, NULL) == ExpressFalse)) {
            Suppress = FALSE;
            CopyMem (QuestionValue, &OneOfOption->Value, sizeof (EFI_HII_VALUE));
            SetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE);
            UpdateStatusBar (Selection, NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE);
            gST->ConOut->SetAttribute (gST->ConOut, PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND);
            break;
          }

          Link = GetNextNode (&Question->OptionListHead, Link);
        }
      }

      if (!Suppress) {
        Character[0] = LEFT_ONEOF_DELIMITER;
        NewStrCat (OptionString[0], Character);
        StringPtr = GetToken (OneOfOption->Text, Selection->Handle);
        ASSERT (StringPtr != NULL);
        NewStrCat (OptionString[0], StringPtr);
        Character[0] = RIGHT_ONEOF_DELIMITER;
        NewStrCat (OptionString[0], Character);

        FreePool (StringPtr);
      }
    }
    break;

  case EFI_IFR_CHECKBOX_OP:
    *OptionString = AllocateZeroPool (BufferSize);
    ASSERT (*OptionString);

    *OptionString[0] = LEFT_CHECKBOX_DELIMITER;

    if (Selected) {
      //
      // Since this is a BOOLEAN operation, flip it upon selection
      //
      QuestionValue->Value.b = (BOOLEAN) (QuestionValue->Value.b ? FALSE : TRUE);

      //
      // Perform inconsistent check
      //
      Status = ValidateQuestion (Selection->FormSet, Selection->Form, Question, EFI_HII_EXPRESSION_INCONSISTENT_IF);
      if (EFI_ERROR (Status)) {
        //
        // Inconsistent check fail, restore Question Value
        //
        QuestionValue->Value.b = (BOOLEAN) (QuestionValue->Value.b ? FALSE : TRUE);
        FreePool (*OptionString);
        *OptionString = NULL;
        return Status;
      }

      //
      // Save Question value
      //
      Status = SetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE);
      UpdateStatusBar (Selection, NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE);
    }

    if (QuestionValue->Value.b) {
      *(OptionString[0] + 1) = CHECK_ON;
    } else {
      *(OptionString[0] + 1) = CHECK_OFF;
    }
    *(OptionString[0] + 2) = RIGHT_CHECKBOX_DELIMITER;
    break;

  case EFI_IFR_NUMERIC_OP:
    if (Selected) {
      //
      // Go ask for input
      //
      Status = GetNumericInput (Selection, MenuOption);
    } else {
      *OptionString = AllocateZeroPool (BufferSize);
      ASSERT (*OptionString);

      *OptionString[0] = LEFT_NUMERIC_DELIMITER;

      //
      // Formatted print
      //
      PrintFormattedNumber (Question, FormattedNumber, 21 * sizeof (CHAR16));
      Number = (UINT16) GetStringWidth (FormattedNumber);
      CopyMem (OptionString[0] + 1, FormattedNumber, Number);

      *(OptionString[0] + Number / 2) = RIGHT_NUMERIC_DELIMITER;
    }
    break;

  case EFI_IFR_DATE_OP:
    if (Selected) {
      //
      // This is similar to numerics
      //
      Status = GetNumericInput (Selection, MenuOption);
    } else {
      *OptionString = AllocateZeroPool (BufferSize);
      ASSERT (*OptionString);

      switch (MenuOption->Sequence) {
      case 0:
        *OptionString[0] = LEFT_NUMERIC_DELIMITER;
        UnicodeSPrint (OptionString[0] + 1, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.date.Month);
        *(OptionString[0] + 3) = DATE_SEPARATOR;
        break;

      case 1:
        SetUnicodeMem (OptionString[0], 4, L' ');
        UnicodeSPrint (OptionString[0] + 4, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.date.Day);
        *(OptionString[0] + 6) = DATE_SEPARATOR;
        break;

      case 2:
        SetUnicodeMem (OptionString[0], 7, L' ');
        UnicodeSPrint (OptionString[0] + 7, 21 * sizeof (CHAR16), L"%04d", QuestionValue->Value.date.Year);
        *(OptionString[0] + 11) = RIGHT_NUMERIC_DELIMITER;
        break;
      }
    }
    break;

  case EFI_IFR_TIME_OP:
    if (Selected) {
      //
      // This is similar to numerics
      //
      Status = GetNumericInput (Selection, MenuOption);
    } else {
      *OptionString = AllocateZeroPool (BufferSize);
      ASSERT (*OptionString);

      switch (MenuOption->Sequence) {
      case 0:
        *OptionString[0] = LEFT_NUMERIC_DELIMITER;
        UnicodeSPrint (OptionString[0] + 1, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.time.Hour);
        *(OptionString[0] + 3) = TIME_SEPARATOR;
        break;

      case 1:
        SetUnicodeMem (OptionString[0], 4, L' ');
        UnicodeSPrint (OptionString[0] + 4, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.time.Minute);
        *(OptionString[0] + 6) = TIME_SEPARATOR;
        break;

      case 2:
        SetUnicodeMem (OptionString[0], 7, L' ');
        UnicodeSPrint (OptionString[0] + 7, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.time.Second);
        *(OptionString[0] + 9) = RIGHT_NUMERIC_DELIMITER;
        break;
      }
    }
    break;

  case EFI_IFR_STRING_OP:
    if (Selected) {
      StringPtr = AllocateZeroPool ((Maximum + 1) * sizeof (CHAR16));
      ASSERT (StringPtr);
      CopyMem(StringPtr, Question->BufferValue, Maximum * sizeof (CHAR16));

      Status = ReadString (MenuOption, gPromptForData, StringPtr);
      if (!EFI_ERROR (Status)) {
        HiiSetString(Selection->FormSet->HiiHandle, Question->HiiValue.Value.string, StringPtr, NULL);
        Status = ValidateQuestion(Selection->FormSet, Selection->Form, Question, EFI_HII_EXPRESSION_INCONSISTENT_IF);
        if (EFI_ERROR (Status)) {
          HiiSetString(Selection->FormSet->HiiHandle, Question->HiiValue.Value.string, (CHAR16*)Question->BufferValue, NULL);
        } else {
          CopyMem (Question->BufferValue, StringPtr, Maximum * sizeof (CHAR16));
          SetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE);

          UpdateStatusBar (Selection, NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE);
        }
      }

      FreePool (StringPtr);
    } else {
      *OptionString = AllocateZeroPool (BufferSize);
      ASSERT (*OptionString);

      if (((CHAR16 *) Question->BufferValue)[0] == 0x0000) {
        *(OptionString[0]) = '_';
      } else {
        if ((Maximum * sizeof (CHAR16)) < BufferSize) {
          BufferSize = Maximum * sizeof (CHAR16);
        }
        CopyMem (OptionString[0], (CHAR16 *) Question->BufferValue, BufferSize);
      }
    }
    break;

  case EFI_IFR_PASSWORD_OP:
    if (Selected) {
      StringPtr = AllocateZeroPool ((Maximum + 1) * sizeof (CHAR16));
      ASSERT (StringPtr);

      //
      // For interactive passwords, old password is validated by callback
      //
      if ((Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK)  != 0) {
        //
        // Use a NULL password to test whether old password is required
        //
        *StringPtr = 0;
        Status = PasswordCallback (Selection, MenuOption, StringPtr);
        if (Status == EFI_NOT_AVAILABLE_YET || Status == EFI_UNSUPPORTED) {
          //
          // Callback is not supported, or
          // Callback request to terminate password input
          //
          FreePool (StringPtr);
          return EFI_SUCCESS;
        }

        if (EFI_ERROR (Status)) {
          //
          // Old password exist, ask user for the old password
          //
          Status = ReadString (MenuOption, gPromptForPassword, StringPtr);
          if (EFI_ERROR (Status)) {
            FreePool (StringPtr);
            return Status;
          }

          //
          // Check user input old password
          //
          Status = PasswordCallback (Selection, MenuOption, StringPtr);
          if (EFI_ERROR (Status)) {
            if (Status == EFI_NOT_READY) {
              //
              // Typed in old password incorrect
              //
              PasswordInvalid ();
            } else {
              Status = EFI_SUCCESS;
            }

            FreePool (StringPtr);
            return Status;
          }
        }
      } else {
        //
        // For non-interactive password, validate old password in local
        //
        if (*((CHAR16 *) Question->BufferValue) != 0) {
          //
          // There is something there!  Prompt for password
          //
          Status = ReadString (MenuOption, gPromptForPassword, StringPtr);
          if (EFI_ERROR (Status)) {
            FreePool (StringPtr);
            return Status;
          }

          TempString = AllocateCopyPool ((Maximum + 1) * sizeof (CHAR16), Question->BufferValue);
          ASSERT (TempString != NULL);

          TempString[Maximum] = L'\0';

          if (StrCmp (StringPtr, TempString) != 0) {
            //
            // Typed in old password incorrect
            //
            PasswordInvalid ();

            FreePool (StringPtr);
            FreePool (TempString);
            return Status;
          }

          FreePool (TempString);
        }
      }

      //
      // Ask for new password
      //
      ZeroMem (StringPtr, (Maximum + 1) * sizeof (CHAR16));
      Status = ReadString (MenuOption, gPromptForNewPassword, StringPtr);
      if (EFI_ERROR (Status)) {
        //
        // Reset state machine for interactive password
        //
        if ((Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK) != 0) {
          PasswordCallback (Selection, MenuOption, NULL);
        }

        FreePool (StringPtr);
        return Status;
      }

      //
      // Confirm new password
      //
      TempString = AllocateZeroPool ((Maximum + 1) * sizeof (CHAR16));
      ASSERT (TempString);
      Status = ReadString (MenuOption, gConfirmPassword, TempString);
      if (EFI_ERROR (Status)) {
        //
        // Reset state machine for interactive password
        //
        if ((Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK) != 0) {
          PasswordCallback (Selection, MenuOption, NULL);
        }

        FreePool (StringPtr);
        FreePool (TempString);
        return Status;
      }

      //
      // Compare two typed-in new passwords
      //
      if (StrCmp (StringPtr, TempString) == 0) {
        //
        // Prepare the  Question->HiiValue.Value.string for ValidateQuestion use.
        //
        if((Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK) != 0) {
          StringId = Question->HiiValue.Value.string;
          Question->HiiValue.Value.string = NewString (StringPtr, Selection->FormSet->HiiHandle);
        } else {
          HiiSetString(Selection->FormSet->HiiHandle, Question->HiiValue.Value.string, StringPtr, NULL);
        }
        
        Status = ValidateQuestion(Selection->FormSet, Selection->Form, Question, EFI_HII_EXPRESSION_INCONSISTENT_IF);

        //
        //  Researve the Question->HiiValue.Value.string.
        //
        if((Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK) != 0) {
          DeleteString(Question->HiiValue.Value.string, Selection->FormSet->HiiHandle);
          Question->HiiValue.Value.string = StringId;
        }   
        
        if (EFI_ERROR (Status)) {
          //
          // Reset state machine for interactive password
          //
          if ((Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK) != 0) {
            PasswordCallback (Selection, MenuOption, NULL);
          } else {
            //
            // Researve the Question->HiiValue.Value.string.
            //
            HiiSetString(Selection->FormSet->HiiHandle, Question->HiiValue.Value.string, (CHAR16*)Question->BufferValue, NULL);            
          }
        } else {
          //
          // Two password match, send it to Configuration Driver
          //
          if ((Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK) != 0) {
            PasswordCallback (Selection, MenuOption, StringPtr);
          } else {
            CopyMem (Question->BufferValue, StringPtr, Maximum * sizeof (CHAR16));
            SetQuestionValue (Selection->FormSet, Selection->Form, Question, FALSE);
          }
        }
      } else {
        //
        // Reset state machine for interactive password
        //
        if ((Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK) != 0) {
          PasswordCallback (Selection, MenuOption, NULL);
        }

        //
        // Two password mismatch, prompt error message
        //
        do {
          CreateDialog (4, TRUE, 0, NULL, &Key, gEmptyString, gConfirmError, gPressEnter, gEmptyString);
        } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
      }

      FreePool (TempString);
      FreePool (StringPtr);
    }
    break;

  default:
    break;
  }

  return Status;
}
Example #3
0
/**
  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_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;
  UINT8                   *ReturnValue;
  UINT8                   ValueType;
  EFI_HII_VALUE           HiiValue;
  DISPLAY_QUESTION_OPTION         *OneOfOption;
  DISPLAY_QUESTION_OPTION         *CurrentOption;
  FORM_DISPLAY_ENGINE_STATEMENT  *Question;
  INTN                    Result;
  EFI_IFR_ORDERED_LIST    *OrderList;

  DimensionsWidth   = gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn;

  ValueArray        = NULL;
  ValueType         = 0;
  CurrentOption     = NULL;
  ShowDownArrow     = FALSE;
  ShowUpArrow       = FALSE;

  StringPtr = AllocateZeroPool ((gOptionBlockWidth + 1) * 2);
  ASSERT (StringPtr);

  ZeroMem (&HiiValue, sizeof (EFI_HII_VALUE));

  Question = MenuOption->ThisTag;
  if (Question->OpCode->OpCode == EFI_IFR_ORDERED_LIST_OP) {
    Link = GetFirstNode (&Question->OptionListHead);
    OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
    ValueArray = Question->CurrentValue.Buffer;
    ValueType =  OneOfOption->OptionOpCode->Type;
    OrderedList = TRUE;
    OrderList = (EFI_IFR_ORDERED_LIST *) Question->OpCode;
  } else {
    OrderedList = FALSE;
    OrderList = NULL;
  }

  //
  // Calculate Option count
  //
  PopUpMenuLines = 0;
  if (OrderedList) {
    AdjustOptionOrder(Question, &PopUpMenuLines);
  } else {
    Link = GetFirstNode (&Question->OptionListHead);
    while (!IsNull (&Question->OptionListHead, Link)) {
      OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
      PopUpMenuLines++;
      Link = GetNextNode (&Question->OptionListHead, Link);
    }
  }

  //
  // Get the number of one of options present and its size
  //
  PopUpWidth = 0;
  HighlightOptionIndex = 0;
  Link = GetFirstNode (&Question->OptionListHead);
  for (Index = 0; Index < PopUpMenuLines; Index++) {
    OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);

    StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle);
    if (StrLen (StringPtr) > PopUpWidth) {
      PopUpWidth = StrLen (StringPtr);
    }
    FreePool (StringPtr);
    HiiValue.Type = OneOfOption->OptionOpCode->Type;
    SetValuesByType (&HiiValue.Value, &OneOfOption->OptionOpCode->Value, HiiValue.Type);
    if (!OrderedList && (CompareHiiValue (&Question->CurrentValue, &HiiValue, &Result, NULL) == EFI_SUCCESS) && (Result == 0)) {
      //
      // Find current selected Option for OneOf
      //
      HighlightOptionIndex = Index;
    }

    Link = GetNextNode (&Question->OptionListHead, Link);
  }

  //
  // Perform popup menu initialization.
  //
  PopUpWidth = PopUpWidth + POPUP_PAD_SPACE_COUNT;

  SavedAttribute = gST->ConOut->Mode->Attribute;
  gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ());

  if ((PopUpWidth + POPUP_FRAME_WIDTH) > DimensionsWidth) {
    PopUpWidth = DimensionsWidth - POPUP_FRAME_WIDTH;
  }

  Start  = (DimensionsWidth - PopUpWidth - POPUP_FRAME_WIDTH) / 2 + gStatementDimensions.LeftColumn;
  End    = Start + PopUpWidth + POPUP_FRAME_WIDTH;
  Top    = gStatementDimensions.TopRow;
  Bottom = gStatementDimensions.BottomRow - 1;

  MenuLinesInView = Bottom - Top - 1;
  if (MenuLinesInView >= PopUpMenuLines) {
    Top     = Top + (MenuLinesInView - PopUpMenuLines) / 2;
    Bottom  = Top + PopUpMenuLines + 1;
  } else {
    ShowDownArrow = TRUE;
  }

  if (HighlightOptionIndex > (MenuLinesInView - 1)) {
    TopOptionIndex = HighlightOptionIndex - MenuLinesInView + 1;
  } else {
    TopOptionIndex = 0;
  }

  do {
    //
    // Clear that portion of the screen
    //
    ClearLines (Start, End, Top, Bottom, GetPopupColor ());

    //
    // Draw "One of" pop-up menu
    //
    Character = BOXDRAW_DOWN_RIGHT;
    PrintCharAt (Start, Top, Character);
    for (Index = Start; Index + 2 < End; Index++) {
      if ((ShowUpArrow) && ((Index + 1) == (Start + End) / 2)) {
        Character = GEOMETRICSHAPE_UP_TRIANGLE;
      } else {
        Character = BOXDRAW_HORIZONTAL;
      }

      PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
    }

    Character = BOXDRAW_DOWN_LEFT;
    PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
    Character = BOXDRAW_VERTICAL;
    for (Index = Top + 1; Index < Bottom; Index++) {
      PrintCharAt (Start, Index, Character);
      PrintCharAt (End - 1, Index, Character);
    }

    //
    // Move to top Option
    //
    Link = GetFirstNode (&Question->OptionListHead);
    for (Index = 0; Index < TopOptionIndex; Index++) {
      Link = GetNextNode (&Question->OptionListHead, Link);
    }

    //
    // Display the One of options
    //
    Index2 = Top + 1;
    for (Index = TopOptionIndex; (Index < PopUpMenuLines) && (Index2 < Bottom); Index++) {
      OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
      Link = GetNextNode (&Question->OptionListHead, Link);

      StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle);
      ASSERT (StringPtr != NULL);
      //
      // If the string occupies multiple lines, truncate it to fit in one line,
      // and append a "..." for indication.
      //
      if (StrLen (StringPtr) > (PopUpWidth - 1)) {
        TempStringPtr = AllocateZeroPool (sizeof (CHAR16) * (PopUpWidth - 1));
        ASSERT ( TempStringPtr != NULL );
        CopyMem (TempStringPtr, StringPtr, (sizeof (CHAR16) * (PopUpWidth - 5)));
        FreePool (StringPtr);
        StringPtr = TempStringPtr;
        StrCat (StringPtr, L"...");
      }

      if (Index == HighlightOptionIndex) {
          //
          // Highlight the selected one
          //
          CurrentOption = OneOfOption;

          gST->ConOut->SetAttribute (gST->ConOut, GetPickListColor ());
          PrintStringAt (Start + 2, Index2, StringPtr);
          gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ());
        } else {
          gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ());
          PrintStringAt (Start + 2, Index2, StringPtr);
        }

      Index2++;
      FreePool (StringPtr);
    }

    Character = BOXDRAW_UP_RIGHT;
    PrintCharAt (Start, Bottom, Character);
    for (Index = Start; Index + 2 < End; Index++) {
      if ((ShowDownArrow) && ((Index + 1) == (Start + End) / 2)) {
        Character = GEOMETRICSHAPE_DOWN_TRIANGLE;
      } else {
        Character = BOXDRAW_HORIZONTAL;
      }

      PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
    }

    Character = BOXDRAW_UP_LEFT;
    PrintCharAt ((UINTN)-1, (UINTN)-1, Character);

    //
    // Get User selection
    //
    Key.UnicodeChar = CHAR_NULL;
    if ((gDirection == SCAN_UP) || (gDirection == SCAN_DOWN)) {
      Key.ScanCode  = gDirection;
      gDirection    = 0;
      goto TheKey;
    }

    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--;

          ASSERT (CurrentOption != NULL);
          SwapListEntries (CurrentOption->Link.BackLink, &CurrentOption->Link);
        }
      }
      break;

    case '-':
      //
      // If an ordered list op-code, we will allow for a popup of +/- keys
      // to create an ordered list of items
      //
      if (OrderedList) {
        if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) &&
            (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1))) {
          //
          // Highlight reaches the bottom of the popup window, scroll one menu item.
          //
          TopOptionIndex++;
          ShowUpArrow = TRUE;
        }

        if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) {
          ShowDownArrow = FALSE;
        }

        if (HighlightOptionIndex < (PopUpMenuLines - 1)) {
          HighlightOptionIndex++;

          ASSERT (CurrentOption != NULL);
          SwapListEntries (&CurrentOption->Link, CurrentOption->Link.ForwardLink);
        }
      }
      break;

    case CHAR_NULL:
      switch (Key.ScanCode) {
      case SCAN_UP:
      case SCAN_DOWN:
        if (Key.ScanCode == SCAN_UP) {
          if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) {
            //
            // Highlight reaches the top of the popup window, scroll one menu item.
            //
            TopOptionIndex--;
            ShowDownArrow = TRUE;
          }

          if (TopOptionIndex == 0) {
            ShowUpArrow = FALSE;
          }

          if (HighlightOptionIndex > 0) {
            HighlightOptionIndex--;
          }
        } else {
          if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) &&
              (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1))) {
            //
            // Highlight reaches the bottom of the popup window, scroll one menu item.
            //
            TopOptionIndex++;
            ShowUpArrow = TRUE;
          }

          if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) {
            ShowDownArrow = FALSE;
          }

          if (HighlightOptionIndex < (PopUpMenuLines - 1)) {
            HighlightOptionIndex++;
          }
        }
        break;

      case SCAN_ESC:
        gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);

        //
        // Restore link list order for orderedlist
        //
        if (OrderedList) {
          HiiValue.Type = ValueType;
          HiiValue.Value.u64 = 0;
          for (Index = 0; Index < OrderList->MaxContainers; Index++) {
            HiiValue.Value.u64 = GetArrayData (ValueArray, ValueType, Index);
            if (HiiValue.Value.u64 == 0) {
              break;
            }

            OneOfOption = ValueToOption (Question, &HiiValue);
            if (OneOfOption == NULL) {
              return EFI_NOT_FOUND;
            }

            RemoveEntryList (&OneOfOption->Link);
            InsertTailList (&Question->OptionListHead, &OneOfOption->Link);
          }
        }

        return EFI_DEVICE_ERROR;

      default:
        break;
      }

      break;

    case CHAR_CARRIAGE_RETURN:
      //
      // return the current selection
      //
      if (OrderedList) {
        ReturnValue = AllocateZeroPool (Question->CurrentValue.BufferLen);
        ASSERT (ReturnValue != NULL);
        Index = 0;
        Link = GetFirstNode (&Question->OptionListHead);
        while (!IsNull (&Question->OptionListHead, Link)) {
          OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
          Link = GetNextNode (&Question->OptionListHead, Link);

          SetArrayData (ReturnValue, ValueType, Index, OneOfOption->OptionOpCode->Value.u64);

          Index++;
          if (Index > OrderList->MaxContainers) {
            break;
          }
        }
        if (CompareMem (ReturnValue, ValueArray, Question->CurrentValue.BufferLen) == 0) {
          FreePool (ReturnValue);
          return EFI_DEVICE_ERROR;
        } else {
          gUserInput->InputValue.Buffer = ReturnValue;
          gUserInput->InputValue.BufferLen = Question->CurrentValue.BufferLen;
          Status = EFI_SUCCESS;
        }
      } 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);
          Status = EFI_SUCCESS;
        }
      }

      gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);

      return EFI_SUCCESS;
      
    default:
      break;
    }
  } while (TRUE);

}