blob: 48c8a71131009dad49ce4d6fb76d9532da99d163 [file] [log] [blame]
/** @file
Implementation for handling the User Interface option processing.
Copyright (c) 2004 - 2014, Intel Corporation. All rights reserved.<BR>
This program and the accompanying materials
are licensed and made available under the terms and conditions of the BSD License
which accompanies this distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#include "FormDisplay.h"
#define MAX_TIME_OUT_LEN 0x10
/**
Concatenate a narrow string to another string.
@param Destination The destination string.
@param Source The source string. The string to be concatenated.
to the end of Destination.
**/
VOID
NewStrCat (
IN OUT CHAR16 *Destination,
IN CHAR16 *Source
)
{
UINTN Length;
for (Length = 0; Destination[Length] != 0; Length++)
;
//
// We now have the length of the original string
// We can safely assume for now that we are concatenating a narrow value to this string.
// For instance, the string is "XYZ" and cat'ing ">"
// If this assumption changes, we need to make this routine a bit more complex
//
Destination[Length] = NARROW_CHAR;
Length++;
StrCpy (Destination + Length, Source);
}
/**
Get UINT64 type value.
@param Value Input Hii value.
@retval UINT64 Return the UINT64 type value.
**/
UINT64
HiiValueToUINT64 (
IN EFI_HII_VALUE *Value
)
{
UINT64 RetVal;
RetVal = 0;
switch (Value->Type) {
case EFI_IFR_TYPE_NUM_SIZE_8:
RetVal = Value->Value.u8;
break;
case EFI_IFR_TYPE_NUM_SIZE_16:
RetVal = Value->Value.u16;
break;
case EFI_IFR_TYPE_NUM_SIZE_32:
RetVal = Value->Value.u32;
break;
case EFI_IFR_TYPE_BOOLEAN:
RetVal = Value->Value.b;
break;
case EFI_IFR_TYPE_DATE:
RetVal = *(UINT64*) &Value->Value.date;
break;
case EFI_IFR_TYPE_TIME:
RetVal = (*(UINT64*) &Value->Value.time) & 0xffffff;
break;
default:
RetVal = Value->Value.u64;
break;
}
return RetVal;
}
/**
Check whether this value type can be transfer to EFI_IFR_TYPE_BUFFER type.
EFI_IFR_TYPE_REF, EFI_IFR_TYPE_DATE and EFI_IFR_TYPE_TIME are converted to
EFI_IFR_TYPE_BUFFER when do the value compare.
@param Value Expression value to compare on.
@retval TRUE This value type can be transter to EFI_IFR_TYPE_BUFFER type.
@retval FALSE This value type can't be transter to EFI_IFR_TYPE_BUFFER type.
**/
BOOLEAN
IsTypeInBuffer (
IN EFI_HII_VALUE *Value
)
{
switch (Value->Type) {
case EFI_IFR_TYPE_BUFFER:
case EFI_IFR_TYPE_DATE:
case EFI_IFR_TYPE_TIME:
case EFI_IFR_TYPE_REF:
return TRUE;
default:
return FALSE;
}
}
/**
Check whether this value type can be transfer to EFI_IFR_TYPE_UINT64
@param Value Expression value to compare on.
@retval TRUE This value type can be transter to EFI_IFR_TYPE_BUFFER type.
@retval FALSE This value type can't be transter to EFI_IFR_TYPE_BUFFER type.
**/
BOOLEAN
IsTypeInUINT64 (
IN EFI_HII_VALUE *Value
)
{
switch (Value->Type) {
case EFI_IFR_TYPE_NUM_SIZE_8:
case EFI_IFR_TYPE_NUM_SIZE_16:
case EFI_IFR_TYPE_NUM_SIZE_32:
case EFI_IFR_TYPE_NUM_SIZE_64:
case EFI_IFR_TYPE_BOOLEAN:
return TRUE;
default:
return FALSE;
}
}
/**
Return the buffer length and buffer pointer for this value.
EFI_IFR_TYPE_REF, EFI_IFR_TYPE_DATE and EFI_IFR_TYPE_TIME are converted to
EFI_IFR_TYPE_BUFFER when do the value compare.
@param Value Expression value to compare on.
@param Buf Return the buffer pointer.
@param BufLen Return the buffer length.
**/
VOID
GetBufAndLenForValue (
IN EFI_HII_VALUE *Value,
OUT UINT8 **Buf,
OUT UINT16 *BufLen
)
{
switch (Value->Type) {
case EFI_IFR_TYPE_BUFFER:
*Buf = Value->Buffer;
*BufLen = Value->BufferLen;
break;
case EFI_IFR_TYPE_DATE:
*Buf = (UINT8 *) (&Value->Value.date);
*BufLen = (UINT16) sizeof (EFI_HII_DATE);
break;
case EFI_IFR_TYPE_TIME:
*Buf = (UINT8 *) (&Value->Value.time);
*BufLen = (UINT16) sizeof (EFI_HII_TIME);
break;
case EFI_IFR_TYPE_REF:
*Buf = (UINT8 *) (&Value->Value.ref);
*BufLen = (UINT16) sizeof (EFI_HII_REF);
break;
default:
*Buf = NULL;
*BufLen = 0;
}
}
/**
Compare two Hii value.
@param Value1 Expression value to compare on left-hand.
@param Value2 Expression value to compare on right-hand.
@param Result Return value after compare.
retval 0 Two operators equal.
return Positive value if Value1 is greater than Value2.
retval Negative value if Value1 is less than Value2.
@param HiiHandle Only required for string compare.
@retval other Could not perform compare on two values.
@retval EFI_SUCCESS Compare the value success.
**/
EFI_STATUS
CompareHiiValue (
IN EFI_HII_VALUE *Value1,
IN EFI_HII_VALUE *Value2,
OUT INTN *Result,
IN EFI_HII_HANDLE HiiHandle OPTIONAL
)
{
INT64 Temp64;
CHAR16 *Str1;
CHAR16 *Str2;
UINTN Len;
UINT8 *Buf1;
UINT16 Buf1Len;
UINT8 *Buf2;
UINT16 Buf2Len;
if (Value1->Type == EFI_IFR_TYPE_STRING && Value2->Type == EFI_IFR_TYPE_STRING) {
if (Value1->Value.string == 0 || Value2->Value.string == 0) {
//
// StringId 0 is reserved
//
return EFI_INVALID_PARAMETER;
}
if (Value1->Value.string == Value2->Value.string) {
*Result = 0;
return EFI_SUCCESS;
}
Str1 = GetToken (Value1->Value.string, HiiHandle);
if (Str1 == NULL) {
//
// String not found
//
return EFI_NOT_FOUND;
}
Str2 = GetToken (Value2->Value.string, HiiHandle);
if (Str2 == NULL) {
FreePool (Str1);
return EFI_NOT_FOUND;
}
*Result = StrCmp (Str1, Str2);
FreePool (Str1);
FreePool (Str2);
return EFI_SUCCESS;
}
//
// Take types(date, time, ref, buffer) as buffer
//
if (IsTypeInBuffer(Value1) && IsTypeInBuffer(Value2)) {
GetBufAndLenForValue(Value1, &Buf1, &Buf1Len);
GetBufAndLenForValue(Value2, &Buf2, &Buf2Len);
Len = Buf1Len > Buf2Len ? Buf2Len : Buf1Len;
*Result = CompareMem (Buf1, Buf2, Len);
if ((*Result == 0) && (Buf1Len != Buf2Len)) {
//
// In this case, means base on samll number buffer, the data is same
// So which value has more data, which value is bigger.
//
*Result = Buf1Len > Buf2Len ? 1 : -1;
}
return EFI_SUCCESS;
}
//
// Take remain types(integer, boolean, date/time) as integer
//
if (IsTypeInUINT64(Value1) && IsTypeInUINT64(Value2)) {
Temp64 = HiiValueToUINT64(Value1) - HiiValueToUINT64(Value2);
if (Temp64 > 0) {
*Result = 1;
} else if (Temp64 < 0) {
*Result = -1;
} else {
*Result = 0;
}
return EFI_SUCCESS;
}
return EFI_UNSUPPORTED;
}
/**
Search an Option of a Question by its value.
@param Question The Question
@param OptionValue Value for Option to be searched.
@retval Pointer Pointer to the found Option.
@retval NULL Option not found.
**/
DISPLAY_QUESTION_OPTION *
ValueToOption (
IN FORM_DISPLAY_ENGINE_STATEMENT *Question,
IN EFI_HII_VALUE *OptionValue
)
{
LIST_ENTRY *Link;
DISPLAY_QUESTION_OPTION *Option;
INTN Result;
EFI_HII_VALUE Value;
Link = GetFirstNode (&Question->OptionListHead);
while (!IsNull (&Question->OptionListHead, Link)) {
Option = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
ZeroMem (&Value, sizeof (EFI_HII_VALUE));
Value.Type = Option->OptionOpCode->Type;
CopyMem (&Value.Value, &Option->OptionOpCode->Value, Option->OptionOpCode->Header.Length - OFFSET_OF (EFI_IFR_ONE_OF_OPTION, Value));
if ((CompareHiiValue (&Value, OptionValue, &Result, NULL) == EFI_SUCCESS) && (Result == 0)) {
return Option;
}
Link = GetNextNode (&Question->OptionListHead, Link);
}
return NULL;
}
/**
Return data element in an Array by its Index.
@param Array The data array.
@param Type Type of the data in this array.
@param Index Zero based index for data in this array.
@retval Value The data to be returned
**/
UINT64
GetArrayData (
IN VOID *Array,
IN UINT8 Type,
IN UINTN Index
)
{
UINT64 Data;
ASSERT (Array != NULL);
Data = 0;
switch (Type) {
case EFI_IFR_TYPE_NUM_SIZE_8:
Data = (UINT64) *(((UINT8 *) Array) + Index);
break;
case EFI_IFR_TYPE_NUM_SIZE_16:
Data = (UINT64) *(((UINT16 *) Array) + Index);
break;
case EFI_IFR_TYPE_NUM_SIZE_32:
Data = (UINT64) *(((UINT32 *) Array) + Index);
break;
case EFI_IFR_TYPE_NUM_SIZE_64:
Data = (UINT64) *(((UINT64 *) Array) + Index);
break;
default:
break;
}
return Data;
}
/**
Set value of a data element in an Array by its Index.
@param Array The data array.
@param Type Type of the data in this array.
@param Index Zero based index for data in this array.
@param Value The value to be set.
**/
VOID
SetArrayData (
IN VOID *Array,
IN UINT8 Type,
IN UINTN Index,
IN UINT64 Value
)
{
ASSERT (Array != NULL);
switch (Type) {
case EFI_IFR_TYPE_NUM_SIZE_8:
*(((UINT8 *) Array) + Index) = (UINT8) Value;
break;
case EFI_IFR_TYPE_NUM_SIZE_16:
*(((UINT16 *) Array) + Index) = (UINT16) Value;
break;
case EFI_IFR_TYPE_NUM_SIZE_32:
*(((UINT32 *) Array) + Index) = (UINT32) Value;
break;
case EFI_IFR_TYPE_NUM_SIZE_64:
*(((UINT64 *) Array) + Index) = (UINT64) Value;
break;
default:
break;
}
}
/**
Check whether this value already in the array, if yes, return the index.
@param Array The data array.
@param Type Type of the data in this array.
@param Value The value to be find.
@param Index The index in the array which has same value with Value.
@retval TRUE Found the value in the array.
@retval FALSE Not found the value.
**/
BOOLEAN
FindArrayData (
IN VOID *Array,
IN UINT8 Type,
IN UINT64 Value,
OUT UINTN *Index OPTIONAL
)
{
UINTN Count;
UINT64 TmpValue;
UINT64 ValueComp;
ASSERT (Array != NULL);
Count = 0;
TmpValue = 0;
switch (Type) {
case EFI_IFR_TYPE_NUM_SIZE_8:
ValueComp = (UINT8) Value;
break;
case EFI_IFR_TYPE_NUM_SIZE_16:
ValueComp = (UINT16) Value;
break;
case EFI_IFR_TYPE_NUM_SIZE_32:
ValueComp = (UINT32) Value;
break;
case EFI_IFR_TYPE_NUM_SIZE_64:
ValueComp = (UINT64) Value;
break;
default:
ValueComp = 0;
break;
}
while ((TmpValue = GetArrayData (Array, Type, Count)) != 0) {
if (ValueComp == TmpValue) {
if (Index != NULL) {
*Index = Count;
}
return TRUE;
}
Count ++;
}
return FALSE;
}
/**
Print Question Value according to it's storage width and display attributes.
@param Question The Question to be printed.
@param FormattedNumber Buffer for output string.
@param BufferSize The FormattedNumber buffer size in bytes.
@retval EFI_SUCCESS Print success.
@retval EFI_BUFFER_TOO_SMALL Buffer size is not enough for formatted number.
**/
EFI_STATUS
PrintFormattedNumber (
IN FORM_DISPLAY_ENGINE_STATEMENT *Question,
IN OUT CHAR16 *FormattedNumber,
IN UINTN BufferSize
)
{
INT64 Value;
CHAR16 *Format;
EFI_HII_VALUE *QuestionValue;
EFI_IFR_NUMERIC *NumericOp;
if (BufferSize < (21 * sizeof (CHAR16))) {
return EFI_BUFFER_TOO_SMALL;
}
QuestionValue = &Question->CurrentValue;
NumericOp = (EFI_IFR_NUMERIC *) Question->OpCode;
Value = (INT64) QuestionValue->Value.u64;
switch (NumericOp->Flags & EFI_IFR_DISPLAY) {
case EFI_IFR_DISPLAY_INT_DEC:
switch (QuestionValue->Type) {
case EFI_IFR_NUMERIC_SIZE_1:
Value = (INT64) ((INT8) QuestionValue->Value.u8);
break;
case EFI_IFR_NUMERIC_SIZE_2:
Value = (INT64) ((INT16) QuestionValue->Value.u16);
break;
case EFI_IFR_NUMERIC_SIZE_4:
Value = (INT64) ((INT32) QuestionValue->Value.u32);
break;
case EFI_IFR_NUMERIC_SIZE_8:
default:
break;
}
if (Value < 0) {
Value = -Value;
Format = L"-%ld";
} else {
Format = L"%ld";
}
break;
case EFI_IFR_DISPLAY_UINT_DEC:
Format = L"%ld";
break;
case EFI_IFR_DISPLAY_UINT_HEX:
Format = L"%lx";
break;
default:
return EFI_UNSUPPORTED;
break;
}
UnicodeSPrint (FormattedNumber, BufferSize, Format, Value);
return EFI_SUCCESS;
}
/**
Draw a pop up windows based on the dimension, number of lines and
strings specified.
@param RequestedWidth The width of the pop-up.
@param NumberOfLines The number of lines.
@param Marker The variable argument list for the list of string to be printed.
**/
VOID
CreateSharedPopUp (
IN UINTN RequestedWidth,
IN UINTN NumberOfLines,
IN VA_LIST Marker
)
{
UINTN Index;
UINTN Count;
CHAR16 Character;
UINTN Start;
UINTN End;
UINTN Top;
UINTN Bottom;
CHAR16 *String;
UINTN DimensionsWidth;
UINTN DimensionsHeight;
DimensionsWidth = gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn;
DimensionsHeight = gStatementDimensions.BottomRow - gStatementDimensions.TopRow;
gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ());
if ((RequestedWidth + 2) > DimensionsWidth) {
RequestedWidth = DimensionsWidth - 2;
}
//
// Subtract the PopUp width from total Columns, allow for one space extra on
// each end plus a border.
//
Start = (DimensionsWidth - RequestedWidth - 2) / 2 + gStatementDimensions.LeftColumn + 1;
End = Start + RequestedWidth + 1;
Top = ((DimensionsHeight - NumberOfLines - 2) / 2) + gStatementDimensions.TopRow - 1;
Bottom = Top + NumberOfLines + 2;
Character = BOXDRAW_DOWN_RIGHT;
PrintCharAt (Start, Top, Character);
Character = BOXDRAW_HORIZONTAL;
for (Index = Start; Index + 2 < End; Index++) {
PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
}
Character = BOXDRAW_DOWN_LEFT;
PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
Character = BOXDRAW_VERTICAL;
Count = 0;
for (Index = Top; Index + 2 < Bottom; Index++, Count++) {
String = VA_ARG (Marker, CHAR16*);
//
// This will clear the background of the line - we never know who might have been
// here before us. This differs from the next clear in that it used the non-reverse
// video for normal printing.
//
if (GetStringWidth (String) / 2 > 1) {
ClearLines (Start, End, Index + 1, Index + 1, GetPopupColor ());
}
//
// Passing in a space results in the assumption that this is where typing will occur
//
if (String[0] == L' ') {
ClearLines (Start + 1, End - 1, Index + 1, Index + 1, GetPopupInverseColor ());
}
//
// Passing in a NULL results in a blank space
//
if (String[0] == CHAR_NULL) {
ClearLines (Start, End, Index + 1, Index + 1, GetPopupColor ());
}
PrintStringAt (
((DimensionsWidth - GetStringWidth (String) / 2) / 2) + gStatementDimensions.LeftColumn + 1,
Index + 1,
String
);
gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ());
PrintCharAt (Start, Index + 1, Character);
PrintCharAt (End - 1, Index + 1, Character);
}
Character = BOXDRAW_UP_RIGHT;
PrintCharAt (Start, Bottom - 1, Character);
Character = BOXDRAW_HORIZONTAL;
for (Index = Start; Index + 2 < End; Index++) {
PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
}
Character = BOXDRAW_UP_LEFT;
PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
}
/**
Draw a pop up windows based on the dimension, number of lines and
strings specified.
@param RequestedWidth The width of the pop-up.
@param NumberOfLines The number of lines.
@param ... A series of text strings that displayed in the pop-up.
**/
VOID
EFIAPI
CreateMultiStringPopUp (
IN UINTN RequestedWidth,
IN UINTN NumberOfLines,
...
)
{
VA_LIST Marker;
VA_START (Marker, NumberOfLines);
CreateSharedPopUp (RequestedWidth, NumberOfLines, Marker);
VA_END (Marker);
}
/**
Process nothing.
@param Event The Event need to be process
@param Context The context of the event.
**/
VOID
EFIAPI
EmptyEventProcess (
IN EFI_EVENT Event,
IN VOID *Context
)
{
}
/**
Process for the refresh interval statement.
@param Event The Event need to be process
@param Context The context of the event.
**/
VOID
EFIAPI
RefreshTimeOutProcess (
IN EFI_EVENT Event,
IN VOID *Context
)
{
WARNING_IF_CONTEXT *EventInfo;
CHAR16 TimeOutString[MAX_TIME_OUT_LEN];
EventInfo = (WARNING_IF_CONTEXT *) Context;
if (*(EventInfo->TimeOut) == 0) {
gBS->CloseEvent (Event);
gBS->SignalEvent (EventInfo->SyncEvent);
return;
}
UnicodeSPrint(TimeOutString, MAX_TIME_OUT_LEN, L"%d", *(EventInfo->TimeOut));
CreateDialog (NULL, gEmptyString, EventInfo->ErrorInfo, gPressEnter, gEmptyString, TimeOutString, NULL);
*(EventInfo->TimeOut) -= 1;
}
/**
Display error message for invalid password.
**/
VOID
PasswordInvalid (
VOID
)
{
EFI_INPUT_KEY Key;
//
// Invalid password, prompt error message
//
do {
CreateDialog (&Key, gEmptyString, gPassowordInvalid, gPressEnter, gEmptyString, NULL);
} while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
}
/**
Process password op code.
@param MenuOption The menu for current password op code.
@retval EFI_SUCCESS Question Option process success.
@retval Other Question Option process fail.
**/
EFI_STATUS
PasswordProcess (
IN UI_MENU_OPTION *MenuOption
)
{
CHAR16 *StringPtr;
CHAR16 *TempString;
UINTN Maximum;
EFI_STATUS Status;
EFI_IFR_PASSWORD *PasswordInfo;
FORM_DISPLAY_ENGINE_STATEMENT *Question;
EFI_INPUT_KEY Key;
Question = MenuOption->ThisTag;
PasswordInfo = (EFI_IFR_PASSWORD *) Question->OpCode;
Maximum = PasswordInfo->MaxSize;
Status = EFI_SUCCESS;
StringPtr = AllocateZeroPool ((Maximum + 1) * sizeof (CHAR16));
ASSERT (StringPtr);
//
// Use a NULL password to test whether old password is required
//
*StringPtr = 0;
Status = Question->PasswordCheck (gFormData, Question, StringPtr);
if (Status == EFI_NOT_AVAILABLE_YET || Status == EFI_UNSUPPORTED) {
//
// Password can't be set now.
//
if (Status == EFI_UNSUPPORTED) {
do {
CreateDialog (&Key, gEmptyString, gPasswordUnsupported, gPressEnter, gEmptyString, NULL);
} while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
}
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 = Question->PasswordCheck (gFormData, Question, StringPtr);
if (EFI_ERROR (Status)) {
if (Status == EFI_NOT_READY) {
//
// Typed in old password incorrect
//
PasswordInvalid ();
} else {
Status = EFI_SUCCESS;
}
FreePool (StringPtr);
return Status;
}
}
//
// Ask for new password
//
ZeroMem (StringPtr, (Maximum + 1) * sizeof (CHAR16));
Status = ReadString (MenuOption, gPromptForNewPassword, StringPtr);
if (EFI_ERROR (Status)) {
//
// Reset state machine for password
//
Question->PasswordCheck (gFormData, Question, 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 password
//
Question->PasswordCheck (gFormData, Question, NULL);
FreePool (StringPtr);
FreePool (TempString);
return Status;
}
//
// Compare two typed-in new passwords
//
if (StrCmp (StringPtr, TempString) == 0) {
gUserInput->InputValue.Buffer = AllocateCopyPool (Question->CurrentValue.BufferLen, StringPtr);
gUserInput->InputValue.BufferLen = Question->CurrentValue.BufferLen;
gUserInput->InputValue.Type = Question->CurrentValue.Type;
gUserInput->InputValue.Value.string = HiiSetString(gFormData->HiiHandle, gUserInput->InputValue.Value.string, StringPtr, NULL);
FreePool (StringPtr);
Status = EFI_SUCCESS;
if (EFI_ERROR (Status)) {
//
// Reset state machine for password
//
Question->PasswordCheck (gFormData, Question, NULL);
}
return Status;
} else {
//
// Reset state machine for password
//
Question->PasswordCheck (gFormData, Question, NULL);
//
// Two password mismatch, prompt error message
//
do {
CreateDialog (&Key, gEmptyString, gConfirmError, gPressEnter, gEmptyString, NULL);
} while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
Status = EFI_INVALID_PARAMETER;
}
ZeroMem (TempString, (Maximum + 1) * sizeof (CHAR16));
ZeroMem (StringPtr, (Maximum + 1) * sizeof (CHAR16));
FreePool (TempString);
FreePool (StringPtr);
return Status;
}
/**
Process a Question's Option (whether selected or un-selected).
@param MenuOption The MenuOption for this Question.
@param Selected TRUE: if Question is selected.
@param OptionString Pointer of the Option String to be displayed.
@param SkipErrorValue Whether need to return when value without option for it.
@retval EFI_SUCCESS Question Option process success.
@retval Other Question Option process fail.
**/
EFI_STATUS
ProcessOptions (
IN UI_MENU_OPTION *MenuOption,
IN BOOLEAN Selected,
OUT CHAR16 **OptionString,
IN BOOLEAN SkipErrorValue
)
{
EFI_STATUS Status;
CHAR16 *StringPtr;
UINTN Index;
FORM_DISPLAY_ENGINE_STATEMENT *Question;
CHAR16 FormattedNumber[21];
UINT16 Number;
CHAR16 Character[2];
EFI_INPUT_KEY Key;
UINTN BufferSize;
DISPLAY_QUESTION_OPTION *OneOfOption;
LIST_ENTRY *Link;
EFI_HII_VALUE HiiValue;
EFI_HII_VALUE *QuestionValue;
DISPLAY_QUESTION_OPTION *Option;
UINTN Index2;
UINT8 *ValueArray;
UINT8 ValueType;
EFI_STRING_ID StringId;
EFI_IFR_ORDERED_LIST *OrderList;
BOOLEAN ValueInvalid;
Status = EFI_SUCCESS;
StringPtr = NULL;
Character[1] = L'\0';
*OptionString = NULL;
StringId = 0;
ValueInvalid = FALSE;
ZeroMem (FormattedNumber, 21 * sizeof (CHAR16));
BufferSize = (gOptionBlockWidth + 1) * 2 * gStatementDimensions.BottomRow;
Question = MenuOption->ThisTag;
QuestionValue = &Question->CurrentValue;
switch (Question->OpCode->OpCode) {
case EFI_IFR_ORDERED_LIST_OP:
//
// Check whether there are Options of this OrderedList
//
if (IsListEmpty (&Question->OptionListHead)) {
break;
}
OrderList = (EFI_IFR_ORDERED_LIST *) Question->OpCode;
Link = GetFirstNode (&Question->OptionListHead);
OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
ValueType = OneOfOption->OptionOpCode->Type;
ValueArray = Question->CurrentValue.Buffer;
if (Selected) {
//
// Go ask for input
//
Status = GetSelectionInputPopUp (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 (OrderList->MaxContainers * BufferSize);
ASSERT (*OptionString);
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) {
//
// Values for the options in ordered lists should never be a 0
//
break;
}
OneOfOption = ValueToOption (Question, &HiiValue);
if (OneOfOption == NULL) {
if (SkipErrorValue) {
//
// Just try to get the option string, skip the value which not has option.
//
continue;
}
//
// Show error message
//
do {
CreateDialog (&Key, gEmptyString, gOptionMismatch, gPressEnter, gEmptyString, NULL);
} while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
//
// The initial value of the orderedlist is invalid, force to be valid value
// Exit current DisplayForm with new value.
//
gUserInput->SelectedStatement = Question;
gMisMatch = TRUE;
ValueArray = AllocateZeroPool (Question->CurrentValue.BufferLen);
ASSERT (ValueArray != NULL);
gUserInput->InputValue.Buffer = ValueArray;
gUserInput->InputValue.BufferLen = Question->CurrentValue.BufferLen;
gUserInput->InputValue.Type = Question->CurrentValue.Type;
Link = GetFirstNode (&Question->OptionListHead);
Index2 = 0;
while (!IsNull (&Question->OptionListHead, Link) && Index2 < OrderList->MaxContainers) {
Option = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
Link = GetNextNode (&Question->OptionListHead, Link);
SetArrayData (ValueArray, ValueType, Index2, Option->OptionOpCode->Value.u64);
Index2++;
}
SetArrayData (ValueArray, ValueType, Index2, 0);
FreePool (*OptionString);
*OptionString = NULL;
return EFI_NOT_FOUND;
}
Character[0] = LEFT_ONEOF_DELIMITER;
NewStrCat (OptionString[0], Character);
StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle);
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);
}
//
// If valid option more than the max container, skip these options.
//
if (Index >= OrderList->MaxContainers) {
break;
}
//
// Search the other options, try to find the one not in the container.
//
Link = GetFirstNode (&Question->OptionListHead);
while (!IsNull (&Question->OptionListHead, Link)) {
OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
Link = GetNextNode (&Question->OptionListHead, Link);
if (FindArrayData (ValueArray, ValueType, OneOfOption->OptionOpCode->Value.u64, NULL)) {
continue;
}
if (SkipErrorValue) {
//
// Not report error, just get the correct option string info.
//
Character[0] = LEFT_ONEOF_DELIMITER;
NewStrCat (OptionString[0], Character);
StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle);
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);
continue;
}
if (!ValueInvalid) {
ValueInvalid = TRUE;
//
// Show error message
//
do {
CreateDialog (&Key, gEmptyString, gOptionMismatch, gPressEnter, gEmptyString, NULL);
} while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
//
// The initial value of the orderedlist is invalid, force to be valid value
// Exit current DisplayForm with new value.
//
gUserInput->SelectedStatement = Question;
gMisMatch = TRUE;
ValueArray = AllocateCopyPool (Question->CurrentValue.BufferLen, Question->CurrentValue.Buffer);
ASSERT (ValueArray != NULL);
gUserInput->InputValue.Buffer = ValueArray;
gUserInput->InputValue.BufferLen = Question->CurrentValue.BufferLen;
gUserInput->InputValue.Type = Question->CurrentValue.Type;
}
SetArrayData (ValueArray, ValueType, Index++, OneOfOption->OptionOpCode->Value.u64);
}
if (ValueInvalid) {
FreePool (*OptionString);
*OptionString = NULL;
return EFI_NOT_FOUND;
}
}
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 (MenuOption);
} else {
*OptionString = AllocateZeroPool (BufferSize);
ASSERT (*OptionString);
OneOfOption = ValueToOption (Question, QuestionValue);
if (OneOfOption == NULL) {
if (SkipErrorValue) {
//
// Not report error, just get the correct option string info.
//
Link = GetFirstNode (&Question->OptionListHead);
OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
} else {
//
// Show error message
//
do {
CreateDialog (&Key, gEmptyString, gOptionMismatch, gPressEnter, gEmptyString, NULL);
} while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
//
// Force the Question value to be valid
// Exit current DisplayForm with new value.
//
Link = GetFirstNode (&Question->OptionListHead);
Option = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
gUserInput->InputValue.Type = Option->OptionOpCode->Type;
switch (gUserInput->InputValue.Type) {
case EFI_IFR_TYPE_NUM_SIZE_8:
gUserInput->InputValue.Value.u8 = Option->OptionOpCode->Value.u8;
break;
case EFI_IFR_TYPE_NUM_SIZE_16:
CopyMem (&gUserInput->InputValue.Value.u16, &Option->OptionOpCode->Value.u16, sizeof (UINT16));
break;
case EFI_IFR_TYPE_NUM_SIZE_32:
CopyMem (&gUserInput->InputValue.Value.u32, &Option->OptionOpCode->Value.u32, sizeof (UINT32));
break;
case EFI_IFR_TYPE_NUM_SIZE_64:
CopyMem (&gUserInput->InputValue.Value.u64, &Option->OptionOpCode->Value.u64, sizeof (UINT64));
break;
default:
ASSERT (FALSE);
break;
}
gUserInput->SelectedStatement = Question;
gMisMatch = TRUE;
FreePool (*OptionString);
*OptionString = NULL;
return EFI_NOT_FOUND;
}
}
Character[0] = LEFT_ONEOF_DELIMITER;
NewStrCat (OptionString[0], Character);
StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle);
ASSERT (StringPtr != NULL);
NewStrCat (OptionString[0], StringPtr);
Character[0] = RIGHT_ONEOF_DELIMITER;
NewStrCat (OptionString[0], Character);
FreePool (StringPtr);
}
break;
case EFI_IFR_CHECKBOX_OP:
if (Selected) {
//
// Since this is a BOOLEAN operation, flip it upon selection
//
gUserInput->InputValue.Type = QuestionValue->Type;
gUserInput->InputValue.Value.b = (BOOLEAN) (QuestionValue->Value.b ? FALSE : TRUE);
//
// Perform inconsistent check
//
return EFI_SUCCESS;
} else {
*OptionString = AllocateZeroPool (BufferSize);
ASSERT (*OptionString);
*OptionString[0] = LEFT_CHECKBOX_DELIMITER;
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 (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 (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 (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 (Question->CurrentValue.BufferLen + sizeof (CHAR16));
ASSERT (StringPtr);
CopyMem(StringPtr, Question->CurrentValue.Buffer, Question->CurrentValue.BufferLen);
Status = ReadString (MenuOption, gPromptForData, StringPtr);
if (EFI_ERROR (Status)) {
FreePool (StringPtr);
return Status;
}
gUserInput->InputValue.Buffer = AllocateCopyPool (Question->CurrentValue.BufferLen, StringPtr);
gUserInput->InputValue.BufferLen = Question->CurrentValue.BufferLen;
gUserInput->InputValue.Type = Question->CurrentValue.Type;
gUserInput->InputValue.Value.string = HiiSetString(gFormData->HiiHandle, gUserInput->InputValue.Value.string, StringPtr, NULL);
FreePool (StringPtr);
return EFI_SUCCESS;
} else {
*OptionString = AllocateZeroPool (BufferSize);
ASSERT (*OptionString);
if (((CHAR16 *) Question->CurrentValue.Buffer)[0] == 0x0000) {
*(OptionString[0]) = '_';
} else {
if (Question->CurrentValue.BufferLen < BufferSize) {
BufferSize = Question->CurrentValue.BufferLen;
}
CopyMem (OptionString[0], (CHAR16 *) Question->CurrentValue.Buffer, BufferSize);
}
}
break;
case EFI_IFR_PASSWORD_OP:
if (Selected) {
Status = PasswordProcess (MenuOption);
}
break;
default:
break;
}
return Status;
}
/**
Process the help string: Split StringPtr to several lines of strings stored in
FormattedString and the glyph width of each line cannot exceed gHelpBlockWidth.
@param StringPtr The entire help string.
@param FormattedString The oupput formatted string.
@param EachLineWidth The max string length of each line in the formatted string.
@param RowCount TRUE: if Question is selected.
**/
UINTN
ProcessHelpString (
IN CHAR16 *StringPtr,
OUT CHAR16 **FormattedString,
OUT UINT16 *EachLineWidth,
IN UINTN RowCount
)
{
UINTN Index;
CHAR16 *OutputString;
UINTN TotalRowNum;
UINTN CheckedNum;
UINT16 GlyphWidth;
UINT16 LineWidth;
UINT16 MaxStringLen;
UINT16 StringLen;
TotalRowNum = 0;
CheckedNum = 0;
GlyphWidth = 1;
Index = 0;
MaxStringLen = 0;
StringLen = 0;
//
// Set default help string width.
//
LineWidth = (UINT16) (gHelpBlockWidth - 1);
//
// Get row number of the String.
//
while ((StringLen = GetLineByWidth (StringPtr, LineWidth, &GlyphWidth, &Index, &OutputString)) != 0) {
if (StringLen > MaxStringLen) {
MaxStringLen = StringLen;
}
TotalRowNum ++;
FreePool (OutputString);
}
*EachLineWidth = MaxStringLen;
*FormattedString = AllocateZeroPool (TotalRowNum * MaxStringLen * sizeof (CHAR16));
ASSERT (*FormattedString != NULL);
//
// Generate formatted help string array.
//
GlyphWidth = 1;
Index = 0;
while((StringLen = GetLineByWidth (StringPtr, LineWidth, &GlyphWidth, &Index, &OutputString)) != 0) {
CopyMem (*FormattedString + CheckedNum * MaxStringLen, OutputString, StringLen * sizeof (CHAR16));
CheckedNum ++;
FreePool (OutputString);
}
return TotalRowNum;
}