/**
  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 #2
0
/**
  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[MAX_NUMERIC_INPUT_WIDTH];
    CHAR16                  FormattedNumber[MAX_NUMERIC_INPUT_WIDTH - 1];
    UINT64                  PreviousNumber[MAX_NUMERIC_INPUT_WIDTH - 3];
    UINTN                   Count;
    UINTN                   Loop;
    BOOLEAN                 ManualInput;
    BOOLEAN                 HexInput;
    BOOLEAN                 DateOrTime;
    UINTN                   InputWidth;
    UINT64                  EditValue;
    UINT64                  Step;
    UINT64                  Minimum;
    UINT64                  Maximum;
    UINTN                   EraseLen;
    UINT8                   Digital;
    EFI_INPUT_KEY           Key;
    EFI_HII_VALUE           *QuestionValue;
    FORM_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;

    //
    // Only two case, user can enter to this function: Enter and +/- case.
    // In Enter case, gDirection = 0; in +/- case, gDirection = SCAN_LEFT/SCAN_WRIGHT
    //
    ManualInput        = (BOOLEAN)(gDirection == 0 ? TRUE : FALSE);

    if ((Question->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:
            switch (QuestionValue->Value.date.Month) {
            case 2:
                if ((QuestionValue->Value.date.Year % 4) == 0  &&
                        ((QuestionValue->Value.date.Year % 100) != 0 ||
                         (QuestionValue->Value.date.Year % 400) == 0)) {
                    Maximum = 29;
                } else {
                    Maximum = 28;
                }
                break;
            case 4:
            case 6:
            case 9:
            case 11:
                Maximum = 30;
                break;
            default:
                Maximum = 31;
                break;
            }

            EraseLen = 3;
            EditValue = QuestionValue->Value.date.Day;
            break;

        case 2:
            Maximum = 0xffff;
            EraseLen = 5;
            EditValue = QuestionValue->Value.date.Year;
            break;

        default:
            break;
        }
    } else if (Question->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 ((Question->Operand == EFI_IFR_NUMERIC_OP) &&
            ((Question->Flags & EFI_IFR_DISPLAY) == EFI_IFR_DISPLAY_UINT_HEX)) {
        HexInput = TRUE;
    } else {
        HexInput = FALSE;
    }

    //
    // Enter from "Enter" input, clear the old word showing.
    //
    if (ManualInput) {
        if (Question->Operand == EFI_IFR_NUMERIC_OP) {
            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' ');
            ASSERT (InputWidth + 2 < MAX_NUMERIC_INPUT_WIDTH);
            InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER;
            InputText[InputWidth + 2] = L'\0';

            PrintAt (Column, Row, InputText);
            Column++;
        }

        if (Question->Operand == EFI_IFR_DATE_OP) {
            if (MenuOption->Sequence == 2) {
                InputWidth = 4;
            } else {
                InputWidth = 2;
            }

            if (MenuOption->Sequence == 0) {
                InputText[0] = LEFT_NUMERIC_DELIMITER;
                SetUnicodeMem (InputText + 1, InputWidth, L' ');
            } else {
                SetUnicodeMem (InputText, InputWidth, L' ');
            }

            if (MenuOption->Sequence == 2) {
                InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER;
            } else {
                InputText[InputWidth + 1] = DATE_SEPARATOR;
            }
            InputText[InputWidth + 2] = L'\0';

            PrintAt (Column, Row, InputText);
            if (MenuOption->Sequence == 0) {
                Column++;
            }
        }

        if (Question->Operand == EFI_IFR_TIME_OP) {
            InputWidth = 2;

            if (MenuOption->Sequence == 0) {
                InputText[0] = LEFT_NUMERIC_DELIMITER;
                SetUnicodeMem (InputText + 1, InputWidth, L' ');
            } else {
                SetUnicodeMem (InputText, InputWidth, L' ');
            }

            if (MenuOption->Sequence == 2) {
                InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER;
            } else {
                InputText[InputWidth + 1] = TIME_SEPARATOR;
            }
            InputText[InputWidth + 2] = L'\0';

            PrintAt (Column, Row, InputText);
            if (MenuOption->Sequence == 0) {
                Column++;
            }
        }
    }

    //
    // First time we enter this handler, we need to check to see if
    // we were passed an increment or decrement directive
    //
    do {
        Key.UnicodeChar = CHAR_NULL;
        if (gDirection != 0) {
            Key.ScanCode  = gDirection;
            gDirection    = 0;
            goto TheKey2;
        }

        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 && !ManualInput) {
                    //
                    // By setting this value, we will return back to the caller.
                    // We need to do this since an auto-refresh will destroy the adjustment
                    // based on what the real-time-clock is showing.  So we always commit
                    // upon changing the value.
                    //
                    gDirection = SCAN_DOWN;
                }

                if ((Step != 0) && !ManualInput) {
                    if (Key.ScanCode == SCAN_LEFT) {
                        if (EditValue >= Minimum + Step) {
                            EditValue = EditValue - Step;
                        } else if (EditValue > Minimum) {
                            EditValue = Minimum;
                        } else {
                            EditValue = Maximum;
                        }
                    } else if (Key.ScanCode == SCAN_RIGHT) {
                        if (EditValue + Step <= Maximum) {
                            EditValue = EditValue + Step;
                        } else if (EditValue < Maximum) {
                            EditValue = Maximum;
                        } else {
                            EditValue = Minimum;
                        }
                    }

                    ZeroMem (FormattedNumber, 21 * sizeof (CHAR16));
                    if (Question->Operand == EFI_IFR_DATE_OP) {
                        if (MenuOption->Sequence == 2) {
                            //
                            // Year
                            //
                            UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%04d", (UINT16) EditValue);
                        } else {
                            //
                            // Month/Day
                            //
                            UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", (UINT8) EditValue);
                        }

                        if (MenuOption->Sequence == 0) {
                            ASSERT (EraseLen >= 2);
                            FormattedNumber[EraseLen - 2] = DATE_SEPARATOR;
                        } else if (MenuOption->Sequence == 1) {
                            ASSERT (EraseLen >= 1);
                            FormattedNumber[EraseLen - 1] = DATE_SEPARATOR;
                        }
                    } else if (Question->Operand == EFI_IFR_TIME_OP) {
                        UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", (UINT8) EditValue);

                        if (MenuOption->Sequence == 0) {
                            ASSERT (EraseLen >= 2);
                            FormattedNumber[EraseLen - 2] = TIME_SEPARATOR;
                        } else if (MenuOption->Sequence == 1) {
                            ASSERT (EraseLen >= 1);
                            FormattedNumber[EraseLen - 1] = TIME_SEPARATOR;
                        }
                    } else {
                        QuestionValue->Value.u64 = EditValue;
                        PrintFormattedNumber (Question, FormattedNumber, 21 * sizeof (CHAR16));
                    }

                    gST->ConOut->SetAttribute (gST->ConOut, PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND);
                    for (Loop = 0; Loop < EraseLen; Loop++) {
                        PrintAt (MenuOption->OptCol + Loop, MenuOption->Row, L" ");
                    }
                    gST->ConOut->SetAttribute (gST->ConOut, PcdGet8 (PcdBrowserFieldTextHighlightColor) | PcdGet8 (PcdBrowserFieldBackgroundHighlightColor));

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

                goto EnterCarriageReturn;
                break;

            case SCAN_UP:
            case SCAN_DOWN:
                goto EnterCarriageReturn;

            case SCAN_ESC:
                return EFI_DEVICE_ERROR;

            default:
                break;
            }

            break;

EnterCarriageReturn:

        case CHAR_CARRIAGE_RETURN:
            //
            // Validate input value with Minimum value.
            //
            if (EditValue < Minimum) {
                UpdateStatusBar (Selection, INPUT_ERROR, Question->QuestionFlags, TRUE);
                break;
            } else {
                UpdateStatusBar (Selection, INPUT_ERROR, Question->QuestionFlags, FALSE);
            }

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

            //
            // Adjust the value to the correct one.
            // Sample like: 2012.02.29 -> 2013.02.29 -> 2013.02.01
            //              2013.03.29 -> 2013.02.29 -> 2013.02.28
            //
            if (Question->Operand == EFI_IFR_DATE_OP &&
                    (MenuOption->Sequence == 0 || MenuOption->Sequence == 2)) {
                AdjustQuestionValue (Question, (UINT8)MenuOption->Sequence);
            }

            //
            // 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 (Selection, 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 (Selection, INPUT_ERROR, Question->QuestionFlags, FALSE);
                Count--;
                Column--;
                PrintAt (Column, Row, L" ");
            }
            break;

        default:
            if (ManualInput) {
                if (HexInput) {
                    if ((Key.UnicodeChar >= L'0') && (Key.UnicodeChar <= L'9')) {
                        Digital = (UINT8) (Key.UnicodeChar - L'0');
                    } else if ((Key.UnicodeChar >= L'A') && (Key.UnicodeChar <= L'F')) {
                        Digital = (UINT8) (Key.UnicodeChar - L'A' + 0x0A);
                    } else if ((Key.UnicodeChar >= L'a') && (Key.UnicodeChar <= L'f')) {
                        Digital = (UINT8) (Key.UnicodeChar - L'a' + 0x0A);
                    } else {
                        UpdateStatusBar (Selection, INPUT_ERROR, Question->QuestionFlags, TRUE);
                        break;
                    }
                } else {
                    if (Key.UnicodeChar > L'9' || Key.UnicodeChar < L'0') {
                        UpdateStatusBar (Selection, 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 (Selection, INPUT_ERROR, Question->QuestionFlags, TRUE);
                    ASSERT (Count < sizeof (PreviousNumber) / sizeof (PreviousNumber[0]));
                    EditValue = PreviousNumber[Count];
                    break;
                } else {
                    UpdateStatusBar (Selection, INPUT_ERROR, Question->QuestionFlags, FALSE);
                }

                Count++;
                ASSERT (Count < (sizeof (PreviousNumber) / sizeof (PreviousNumber[0])));
                PreviousNumber[Count] = EditValue;

                PrintCharAt (Column, Row, Key.UnicodeChar);
                Column++;
            }
            break;
        }
    } while (TRUE);

}
Example #3
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);

}
Example #4
0
EFI_STATUS
GetNumericInput (
  IN  UI_MENU_SELECTION           *Selection,
  IN  UI_MENU_OPTION              *MenuOption
  )
/*++

Routine Description:
  This routine reads a numeric value from the user input.

Arguments:
  Selection        -  Pointer to current selection.
  MenuOption       -  Pointer to the current input menu.

Returns:
  EFI_SUCCESS       - If numerical input is read successfully
  EFI_DEVICE_ERROR  - If operation fails

--*/
{
  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;
            }
          }

          EfiZeroMem (FormattedNumber, 21 * sizeof (CHAR16));
          if (Question->Operand == EFI_IFR_DATE_OP) {
            if (MenuOption->Sequence == 2) {
              //
              // Year
              //
              SPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%04d", (UINTN) EditValue);
            } else {
              //
              // Month/Day
              //
              SPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", (UINTN) 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) {
            SPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", (UINTN) 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);
          }

  		  goto EnterCarriageReturn;
        }
        break;

      case SCAN_UP:
      case SCAN_DOWN:
        goto EnterCarriageReturn;

      case SCAN_ESC:
        return EFI_DEVICE_ERROR;

      default:
        break;
      }

      break;

EnterCarriageReturn:

    case CHAR_CARRIAGE_RETURN:
      //
      // 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 = EditValue * 10 + (Key.UnicodeChar - L'0');
            //
            EditValue = LShiftU64 (EditValue, 3) + LShiftU64 (EditValue, 1) + (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);

  return EFI_SUCCESS;
}